This is an automated email from the ASF dual-hosted git repository.
jason810496 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new be4a1e671e2 Fix unstyled TypeScript SDK API reference on the published
site (#72105)
be4a1e671e2 is described below
commit be4a1e671e20cd278696a6deb033e6dd8d1d185c
Author: Jason(Zhe-You) Liu <[email protected]>
AuthorDate: Thu Aug 27 00:03:03 2026 +0800
Fix unstyled TypeScript SDK API reference on the published site (#72105)
---
.github/workflows/publish-docs-to-s3.yml | 2 +-
.../airflow_breeze/commands/developer_commands.py | 9 +-
ts-sdk/DEVELOPMENT.md | 21 ++-
ts-sdk/README.md | 4 +-
ts-sdk/docs/package.json | 6 +-
ts-sdk/docs/scripts/deployment-checks.mjs | 102 +++++++++++++
ts-sdk/docs/scripts/strip-remote-fonts.mjs | 43 ++++++
ts-sdk/docs/scripts/verify-deployment-paths.mjs | 71 +++++++++
ts-sdk/docs/tests/deployment-checks.test.mjs | 161 +++++++++++++++++++++
ts-sdk/docs/typedoc.config.mjs | 67 +++++++++
ts-sdk/docs/typedoc.json | 24 ---
11 files changed, 474 insertions(+), 36 deletions(-)
diff --git a/.github/workflows/publish-docs-to-s3.yml
b/.github/workflows/publish-docs-to-s3.yml
index ebd0b0bfd88..982751db9b3 100644
--- a/.github/workflows/publish-docs-to-s3.yml
+++ b/.github/workflows/publish-docs-to-s3.yml
@@ -526,7 +526,7 @@ jobs:
if [[ "${INCLUDE_DOCS}" != "all" && " ${INCLUDE_DOCS} " != *" ts-sdk
"* ]]; then
echo "exists=false" >> "${GITHUB_OUTPUT}"
echo "include-docs='${INCLUDE_DOCS}' is a specific package list
without 'ts-sdk' — skipping TypeDoc build."
- elif [[ -f "ts-sdk/docs/typedoc.json" ]]; then
+ elif [[ -f "ts-sdk/docs/typedoc.config.mjs" ]]; then
echo "exists=true" >> "${GITHUB_OUTPUT}"
else
echo "exists=false" >> "${GITHUB_OUTPUT}"
diff --git a/dev/breeze/src/airflow_breeze/commands/developer_commands.py
b/dev/breeze/src/airflow_breeze/commands/developer_commands.py
index 55e09fd40b5..617f06329fc 100644
--- a/dev/breeze/src/airflow_breeze/commands/developer_commands.py
+++ b/dev/breeze/src/airflow_breeze/commands/developer_commands.py
@@ -852,14 +852,15 @@ def _build_ts_sdk_docs(generated_path: Path) -> int:
f"node:{TYPESCRIPT_SDK_NODE_VERSION}-bookworm-slim",
"sh",
"-c",
- # `npm ci` keeps the lock file authoritative; `npm run build`
strips the ASF
- # header from the landing page and then runs TypeDoc.
- "npm ci --no-audit --no-fund && npm run build",
+ # `npm ci` keeps the lock file authoritative; `npm test` covers
the postbuild
+ # checks; `npm run build` strips the ASF header from the landing
page, runs
+ # TypeDoc, then checks the generated HTML is publishable.
+ "npm ci --no-audit --no-fund && npm test && npm run build",
],
check=False,
)
if result.returncode != 0:
- console_print("[error]TypeDoc build failed.")
+ console_print("[error]TypeScript SDK docs build failed.")
return result.returncode
_stage_sdk_docs(
diff --git a/ts-sdk/DEVELOPMENT.md b/ts-sdk/DEVELOPMENT.md
index 92a4a60d7f4..1966842c31b 100644
--- a/ts-sdk/DEVELOPMENT.md
+++ b/ts-sdk/DEVELOPMENT.md
@@ -75,6 +75,20 @@ a `stable.txt` holding the version from
`ts-sdk/package.json`. To iterate on the
docs directly instead, `npm ci && npm run build` inside `ts-sdk/docs/` writes
to
`ts-sdk/docs/_build/html/`, and `npm start` rebuilds on change.
+`typedoc.config.mjs` pins the theme's `basePath` to `/docs/ts-sdk/<version>`,
so
+the generated HTML expects to be served from that prefix and looks unstyled
when
+opened straight off disk. Override it for any local preview, `npm start`
+included:
+
+```bash
+TS_SDK_DOCS_BASE_PATH=/ npm run build && npx serve _build/html
+```
+
+`npm run build` also strips the theme's Google Fonts tags and then fails if the
+output still has root-relative asset URLs or remote font requests. `npm test`
+covers those checks; `npm start` skips them, so run a full build before
+publishing.
+
CI builds the reference on every change under `ts-sdk/src/` or `ts-sdk/docs/`,
so a broken docs build fails the PR rather than the release.
@@ -91,9 +105,10 @@ gh workflow run "Publish Docs to S3" --repo apache/airflow
--ref main \
-f destination=live
```
-Use `destination=staging` first to check the output, then `live`. Confirm that
-`https://airflow.apache.org/docs/ts-sdk/stable/` resolves (allow time for cache
-invalidation) and that `/docs/ts-sdk/` redirects to it.
+Use `destination=staging` first and check
+<https://airflow.staged.apache.org/docs/ts-sdk/stable/>, then publish to `live`
+and confirm that <https://airflow.apache.org/docs/ts-sdk/stable/> resolves
(allow
+time for cache invalidation) and that `/docs/ts-sdk/` redirects to it.
## Publishing
diff --git a/ts-sdk/README.md b/ts-sdk/README.md
index 439a79e522e..b89d846969b 100644
--- a/ts-sdk/README.md
+++ b/ts-sdk/README.md
@@ -270,9 +270,9 @@ Do not edit the table by hand — update the manifest and run
the
## Links
-- [TypeScript SDK guide (staged docs build from latest
main)](http://apache-airflow-docs.s3-website.eu-central-1.amazonaws.com/docs/apache-airflow/stable/authoring-and-scheduling/language-sdks/typescript.html)
+- [TypeScript SDK guide (staged
docs)](https://airflow.staged.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/language-sdks/typescript.html)
— how Airflow runs TypeScript task handlers
-- [API reference
(staged)](http://apache-airflow-docs.s3-website.eu-central-1.amazonaws.com/docs/ts-sdk/stable/)
+- [API reference
(staged)](https://airflow.staged.apache.org/docs/ts-sdk/stable/)
— generated from the TypeScript sources
- [Source](https://github.com/apache/airflow/tree/main/ts-sdk) — the `ts-sdk/`
directory of the Apache Airflow monorepo
diff --git a/ts-sdk/docs/package.json b/ts-sdk/docs/package.json
index fd0e1328f25..a0579cca8c0 100644
--- a/ts-sdk/docs/package.json
+++ b/ts-sdk/docs/package.json
@@ -5,10 +5,12 @@
"description": "Documentation toolchain for the Apache Airflow TypeScript
SDK (ts-sdk). TypeDoc renders the API reference straight to HTML with the
clean-jsdoc-theme; it is kept in its own package so the docs-only dependencies
stay out of the SDK's runtime and build toolchain.",
"type": "module",
"scripts": {
+ "test": "node --test \"tests/**/*.test.mjs\"",
"prebuild": "node scripts/prepare-readme.mjs",
- "build": "typedoc",
+ "build": "typedoc --options typedoc.config.mjs",
+ "postbuild": "node scripts/strip-remote-fonts.mjs && node
scripts/verify-deployment-paths.mjs",
"prestart": "node scripts/prepare-readme.mjs",
- "start": "typedoc --watch"
+ "start": "typedoc --options typedoc.config.mjs --watch"
},
"devDependencies": {
"@clean-jsdoc-theme/typedoc": "^5.1.1",
diff --git a/ts-sdk/docs/scripts/deployment-checks.mjs
b/ts-sdk/docs/scripts/deployment-checks.mjs
new file mode 100644
index 00000000000..9d88e8e3c7c
--- /dev/null
+++ b/ts-sdk/docs/scripts/deployment-checks.mjs
@@ -0,0 +1,102 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/** Shared helpers and paths behind the docs postbuild steps. See
tests/deployment-checks.test.mjs. */
+
+import { readdir } from "node:fs/promises";
+import { join } from "node:path";
+import { fileURLToPath } from "node:url";
+
+// One `<link>` shape covers the preconnect hints and the stylesheet alike:
the theme emits
+// all three with the font host in `href`.
+const REMOTE_FONT_TAG =
+
/<link\b[^>]*href="https:\/\/fonts\.(?:googleapis|gstatic)\.com[^"]*"[^>]*>/g;
+
+const ROOT_RELATIVE = /(?:href|src)="\/(?:_assets|_islands)\//;
+const REMOTE_FONTS = /fonts\.(?:googleapis|gstatic)\.com/;
+
+/** The rendered site, resolved from this file so every entry point agrees on
it. */
+export const HTML_ROOT = fileURLToPath(new URL("../_build/html",
import.meta.url));
+
+/** The SDK's package.json, not the docs toolchain's: its version is the
published prefix. */
+export const SDK_PACKAGE_JSON = new URL("../../package.json", import.meta.url);
+
+/** Empty string means "served from the root", where root-relative URLs are
correct. */
+export function normalizeBasePath(rawBasePath) {
+ return rawBasePath.replace(/\/+$/, "");
+}
+
+/**
+ * A blank ``TS_SDK_DOCS_BASE_PATH`` counts as unset rather than as the root,
so an empty
+ * env var cannot quietly publish a root-relative build past checks that then
skip themselves.
+ */
+export function resolveBasePath(version, override =
process.env.TS_SDK_DOCS_BASE_PATH) {
+ return normalizeBasePath(override?.trim() || `/docs/ts-sdk/${version}`);
+}
+
+export function stripRemoteFontTags(html) {
+ return html.replace(REMOTE_FONT_TAG, "");
+}
+
+export function hasRemoteFonts(text) {
+ return REMOTE_FONTS.test(text);
+}
+
+export function inspectPage(html, basePath) {
+ return {
+ rootRelative: ROOT_RELATIVE.test(html),
+ remoteFonts: hasRemoteFonts(html),
+ usesBasePath: basePath !== "" && html.includes(`${basePath}/_assets/`),
+ };
+}
+
+const preview = (files) => `${files.slice(0, 5).join(", ")}${files.length > 5
? ", ..." : ""}`;
+
+/** @returns {string[]} human-readable failures; empty means the build is
publishable. */
+export function collectErrors({ basePath, scanned, rootRelative, remoteFonts,
usingBasePath }) {
+ const errors = [];
+ const servedFromRoot = basePath === "";
+
+ if (scanned === 0) return ["no HTML found -- did the TypeDoc build run?"];
+
+ if (!servedFromRoot && rootRelative.length > 0) {
+ errors.push(
+ `${rootRelative.length} page(s) use root-relative /_assets or /_islands
URLs, ` +
+ `which resolve against the domain root once published:
${preview(rootRelative)}`,
+ );
+ }
+ if (remoteFonts.length > 0) {
+ errors.push(
+ `${remoteFonts.length} file(s) load fonts from Google, which ASF privacy
guidance ` +
+ `does not allow: ${preview(remoteFonts)}`,
+ );
+ }
+ if (!servedFromRoot && usingBasePath === 0) {
+ errors.push(`no page references "${basePath}/_assets/" -- basePath is not
the published path.`);
+ }
+ return errors;
+}
+
+export async function* filesUnder(dir, extensions) {
+ for (const entry of await readdir(dir, { withFileTypes: true })) {
+ const path = join(dir, entry.name);
+ if (entry.isDirectory()) yield* filesUnder(path, extensions);
+ else if (extensions.some((extension) => entry.name.endsWith(extension)))
yield path;
+ }
+}
diff --git a/ts-sdk/docs/scripts/strip-remote-fonts.mjs
b/ts-sdk/docs/scripts/strip-remote-fonts.mjs
new file mode 100644
index 00000000000..49fec8318fb
--- /dev/null
+++ b/ts-sdk/docs/scripts/strip-remote-fonts.mjs
@@ -0,0 +1,43 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Remove the theme's Google Fonts tags; ASF privacy guidance allows them only
+// when ASF-hosted. There is no theme option: it validates the font families
+// against the live Google Fonts API and restores its own defaults on a 400, so
+// overriding them regresses in CI, where the build has network. The theme's
CSS
+// already declares Georgia / system-ui fallbacks, so nothing is substituted.
+
+import { readFile, writeFile } from "node:fs/promises";
+
+import { HTML_ROOT, filesUnder, stripRemoteFontTags } from
"./deployment-checks.mjs";
+
+let scanned = 0;
+let stripped = 0;
+
+for await (const file of filesUnder(HTML_ROOT, [".html"])) {
+ scanned += 1;
+ const html = await readFile(file, "utf8");
+ const cleaned = stripRemoteFontTags(html);
+ if (cleaned !== html) {
+ await writeFile(file, cleaned, "utf8");
+ stripped += 1;
+ }
+}
+
+console.log(`strip-remote-fonts: removed remote font tags from
${stripped}/${scanned} page(s).`);
diff --git a/ts-sdk/docs/scripts/verify-deployment-paths.mjs
b/ts-sdk/docs/scripts/verify-deployment-paths.mjs
new file mode 100644
index 00000000000..c874719fca4
--- /dev/null
+++ b/ts-sdk/docs/scripts/verify-deployment-paths.mjs
@@ -0,0 +1,71 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Fail the build on HTML that renders locally but breaks once published.
+
+import { readFile } from "node:fs/promises";
+
+import {
+ HTML_ROOT,
+ SDK_PACKAGE_JSON,
+ collectErrors,
+ filesUnder,
+ hasRemoteFonts,
+ inspectPage,
+ resolveBasePath,
+} from "./deployment-checks.mjs";
+
+// The theme can inline a font host into a stylesheet or an island bundle as
well as into the
+// page head, and those the stripper cannot repair -- so scan them too rather
than promise a
+// font-free build that only holds for HTML.
+const ASSET_EXTENSIONS = [".css", ".js"];
+
+const { version } = JSON.parse(await readFile(SDK_PACKAGE_JSON, "utf8"));
+const basePath = resolveBasePath(version);
+
+const rootRelative = [];
+const remoteFonts = [];
+let scanned = 0;
+let usingBasePath = 0;
+
+for await (const file of filesUnder(HTML_ROOT, [".html",
...ASSET_EXTENSIONS])) {
+ const name = file.slice(HTML_ROOT.length + 1);
+ const text = await readFile(file, "utf8");
+ if (!file.endsWith(".html")) {
+ if (hasRemoteFonts(text)) remoteFonts.push(name);
+ continue;
+ }
+ scanned += 1;
+ const result = inspectPage(text, basePath);
+ if (result.rootRelative) rootRelative.push(name);
+ if (result.remoteFonts) remoteFonts.push(name);
+ if (result.usesBasePath) usingBasePath += 1;
+}
+
+const errors = collectErrors({ basePath, scanned, rootRelative, remoteFonts,
usingBasePath });
+
+if (errors.length > 0) {
+ console.error("verify-deployment-paths: FAILED");
+ for (const error of errors) console.error(` - ${error}`);
+ process.exitCode = 1;
+} else {
+ console.log(
+ `verify-deployment-paths: ${scanned} page(s) OK (basePath "${basePath ||
"/"}", no remote fonts).`,
+ );
+}
diff --git a/ts-sdk/docs/tests/deployment-checks.test.mjs
b/ts-sdk/docs/tests/deployment-checks.test.mjs
new file mode 100644
index 00000000000..df4bc9acd9d
--- /dev/null
+++ b/ts-sdk/docs/tests/deployment-checks.test.mjs
@@ -0,0 +1,161 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import assert from "node:assert/strict";
+import { describe, it } from "node:test";
+
+import {
+ collectErrors,
+ inspectPage,
+ normalizeBasePath,
+ resolveBasePath,
+ stripRemoteFontTags,
+} from "../scripts/deployment-checks.mjs";
+
+// Verbatim from a clean-jsdoc-theme build: three tags together, with the
query separators
+// HTML-escaped the way the theme escapes the href it builds.
+const FONT_TAGS =
+ '<link rel="preconnect" href="https://fonts.googleapis.com" />' +
+ '<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />' +
+ '<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Source+Serif+4:wght@400;500;600;700&family=Roboto:wght@400;500;600;700&display=swap"
/>';
+
+const BASE = "/docs/ts-sdk/0.1.0-beta1";
+const VERSION = "0.1.0-beta1";
+
+describe("normalizeBasePath", () => {
+ it("strips trailing slashes", () => {
+ assert.equal(normalizeBasePath(`${BASE}/`), BASE);
+ assert.equal(normalizeBasePath(BASE), BASE);
+ });
+
+ it("maps the root to an empty string", () => {
+ assert.equal(normalizeBasePath("/"), "");
+ assert.equal(normalizeBasePath("//"), "");
+ });
+});
+
+describe("resolveBasePath", () => {
+ it("defaults to the published prefix for the SDK version", () => {
+ assert.equal(resolveBasePath(VERSION, undefined), BASE);
+ });
+
+ it("honours an override, normalized", () => {
+ assert.equal(resolveBasePath(VERSION, "/preview/"), "/preview");
+ assert.equal(resolveBasePath(VERSION, "/"), "");
+ });
+
+ // A blank env var is how the override leaks in unintentionally, and
treating it as the
+ // root would both emit root-relative HTML and make the checks skip
themselves.
+ it("treats a blank override as unset", () => {
+ assert.equal(resolveBasePath(VERSION, ""), BASE);
+ assert.equal(resolveBasePath(VERSION, " "), BASE);
+ });
+});
+
+describe("stripRemoteFontTags", () => {
+ it("removes preconnect and stylesheet tags", () => {
+ assert.equal(stripRemoteFontTags(`<head>${FONT_TAGS}</head>`),
"<head></head>");
+ });
+
+ it("keeps local stylesheets", () => {
+ const local = `<link rel="stylesheet" href="${BASE}/_assets/styles.css"
/>`;
+ assert.equal(stripRemoteFontTags(`<head>${FONT_TAGS}${local}</head>`),
`<head>${local}</head>`);
+ });
+
+ it("is a no-op on already-clean HTML", () => {
+ const html = "<head><title>x</title></head>";
+ assert.equal(stripRemoteFontTags(html), html);
+ });
+});
+
+describe("inspectPage", () => {
+ it("flags root-relative asset and island URLs", () => {
+ assert.equal(inspectPage('<link href="/_assets/styles.css" />',
BASE).rootRelative, true);
+ assert.equal(inspectPage('<script src="/_islands/cmdk.js">',
BASE).rootRelative, true);
+ });
+
+ it("accepts prefixed URLs", () => {
+ const page = inspectPage(`<link href="${BASE}/_assets/styles.css" />`,
BASE);
+ assert.equal(page.rootRelative, false);
+ assert.equal(page.usesBasePath, true);
+ });
+
+ it("does not credit a basePath from another version", () => {
+ const page = inspectPage('<link
href="/docs/ts-sdk/9.9.9/_assets/styles.css" />', BASE);
+ assert.equal(page.rootRelative, false);
+ assert.equal(page.usesBasePath, false);
+ });
+
+ it("never credits a basePath when served from the root", () => {
+ assert.equal(inspectPage('<link href="/_assets/styles.css" />',
"").usesBasePath, false);
+ });
+
+ it("detects both font hosts", () => {
+ assert.equal(inspectPage(FONT_TAGS, BASE).remoteFonts, true);
+ assert.equal(inspectPage('<link href="https://fonts.gstatic.com/x" />',
BASE).remoteFonts, true);
+ assert.equal(inspectPage("<head></head>", BASE).remoteFonts, false);
+ });
+});
+
+describe("collectErrors", () => {
+ const clean = {
+ basePath: BASE,
+ scanned: 36,
+ rootRelative: [],
+ remoteFonts: [],
+ usingBasePath: 36,
+ };
+
+ it("passes a correct build", () => {
+ assert.deepEqual(collectErrors(clean), []);
+ });
+
+ it("reports an empty build before anything else", () => {
+ const errors = collectErrors({ ...clean, scanned: 0, usingBasePath: 0 });
+ assert.equal(errors.length, 1);
+ assert.match(errors[0], /did the TypeDoc build run/);
+ });
+
+ it("reports every failure of the unfixed build at once", () => {
+ const errors = collectErrors({
+ ...clean,
+ rootRelative: ["index.html"],
+ remoteFonts: ["index.html"],
+ usingBasePath: 0,
+ });
+ assert.equal(errors.length, 3);
+ assert.match(errors[0], /root-relative/);
+ assert.match(errors[1], /ASF privacy guidance/);
+ assert.match(errors[2], /basePath is not the published path/);
+ });
+
+ it("truncates long file lists", () => {
+ const files = Array.from({ length: 36 }, (_, i) => `page-${i}.html`);
+ const [error] = collectErrors({ ...clean, remoteFonts: files });
+ assert.match(error, /^36 file\(s\) load fonts/);
+ assert.match(error, /page-4\.html, \.\.\.$/);
+ assert.equal(error.includes("page-5.html"), false);
+ });
+
+ it("skips the prefix checks when served from the root", () => {
+ const rooted = { ...clean, basePath: "", rootRelative: ["index.html"],
usingBasePath: 0 };
+ assert.deepEqual(collectErrors(rooted), []);
+ assert.equal(collectErrors({ ...rooted, remoteFonts: ["index.html"]
}).length, 1);
+ });
+});
diff --git a/ts-sdk/docs/typedoc.config.mjs b/ts-sdk/docs/typedoc.config.mjs
new file mode 100644
index 00000000000..e9955586fb7
--- /dev/null
+++ b/ts-sdk/docs/typedoc.config.mjs
@@ -0,0 +1,67 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// clean-jsdoc-theme defaults basePath to "/", which emits every asset and link
+// root-relative and leaves the published site with no CSS.
+//
+// The prefix is the versioned one, not /docs/ts-sdk/stable: publish-docs-to-s3
+// syncs one build to both, so only one can own the assets. Pinning to the
+// version keeps archived releases self-contained, since a later release
+// replaces stable/ (sync --delete) without touching assets they still cite.
+
+import { readFileSync } from "node:fs";
+
+import { resolveBasePath, SDK_PACKAGE_JSON } from
"./scripts/deployment-checks.mjs";
+
+const { version } = JSON.parse(readFileSync(SDK_PACKAGE_JSON, "utf8"));
+
+export default {
+ plugin: ["@clean-jsdoc-theme/typedoc"],
+ outputs: [
+ {
+ name: "clean-jsdoc-theme",
+ path: "_build/html",
+ },
+ ],
+ entryPoints: ["../api-docs/*.ts"],
+ tsconfig: "./tsconfig.json",
+ name: "Apache Airflow TypeScript SDK",
+ readme: ".typedoc/readme.md",
+ excludeInternal: true,
+ excludePrivate: true,
+ excludeExternals: true,
+ cleanJsdocTheme: {
+ siteName: "Apache Airflow TypeScript SDK",
+ menu: [
+ {
+ title: "Airflow Docs",
+ link: "https://airflow.apache.org/docs/",
+ target: "_blank",
+ },
+ {
+ title: "GitHub",
+ link: "https://github.com/apache/airflow/tree/main/ts-sdk",
+ target: "_blank",
+ },
+ ],
+ footer:
+ "Apache Airflow, Apache, Airflow, the Apache feather logo, and the
Apache Airflow logo are either registered trademarks or trademarks of The
Apache Software Foundation.",
+ basePath: resolveBasePath(version),
+ },
+};
diff --git a/ts-sdk/docs/typedoc.json b/ts-sdk/docs/typedoc.json
deleted file mode 100644
index feed7d21ac0..00000000000
--- a/ts-sdk/docs/typedoc.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "$schema": "https://typedoc.org/schema.json",
- "plugin": ["@clean-jsdoc-theme/typedoc"],
- "outputs": [{ "name": "clean-jsdoc-theme", "path": "_build/html" }],
- "entryPoints": ["../api-docs/*.ts"],
- "tsconfig": "./tsconfig.json",
- "name": "Apache Airflow TypeScript SDK",
- "readme": ".typedoc/readme.md",
- "excludeInternal": true,
- "excludePrivate": true,
- "excludeExternals": true,
- "cleanJsdocTheme": {
- "siteName": "Apache Airflow TypeScript SDK",
- "menu": [
- { "title": "Airflow Docs", "link": "https://airflow.apache.org/docs/",
"target": "_blank" },
- {
- "title": "GitHub",
- "link": "https://github.com/apache/airflow/tree/main/ts-sdk",
- "target": "_blank"
- }
- ],
- "footer": "Apache Airflow, Apache, Airflow, the Apache feather logo, and
the Apache Airflow logo are either registered trademarks or trademarks of The
Apache Software Foundation."
- }
-}