handoff-app 1.2.2-7 → 2.0.0-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Changelog.md +37 -15
- package/README.md +372 -118
- package/dist/app-builder/build.d.ts +41 -2
- package/dist/app-builder/build.js +455 -18
- package/dist/app-builder/client-config.d.ts +15 -2
- package/dist/app-builder/client-config.js +43 -4
- package/dist/app-builder/config-diff/snapshot.d.ts +1 -6
- package/dist/app-builder/config-diff/snapshot.js +2 -9
- package/dist/app-builder/index.d.ts +1 -1
- package/dist/app-builder/index.js +3 -1
- package/dist/app-builder/nav-shell.d.ts +23 -0
- package/dist/app-builder/nav-shell.js +80 -0
- package/dist/app-builder/paths.js +25 -1
- package/dist/app-builder/static-export.d.ts +17 -0
- package/dist/app-builder/static-export.js +206 -0
- package/dist/app-builder/vercel-output.d.ts +39 -0
- package/dist/app-builder/vercel-output.js +259 -0
- package/dist/app-builder/watchers/utils.d.ts +2 -0
- package/dist/app-builder/watchers.d.ts +14 -4
- package/dist/app-builder/watchers.js +123 -28
- package/dist/app-builder/websocket.js +1 -1
- package/dist/artifacts/index.d.ts +2 -0
- package/dist/artifacts/index.js +16 -0
- package/dist/artifacts/types.d.ts +136 -0
- package/dist/artifacts/types.js +24 -0
- package/dist/artifacts/url.d.ts +67 -0
- package/dist/artifacts/url.js +116 -0
- package/dist/cache/build-cache.js +48 -4
- package/dist/cli.js +0 -0
- package/dist/commands/build/app.js +1 -2
- package/dist/commands/build/index.d.ts +17 -0
- package/dist/commands/build/index.js +66 -0
- package/dist/commands/checkout/index.d.ts +24 -0
- package/dist/commands/checkout/index.js +71 -0
- package/dist/commands/db/migrate.d.ts +11 -0
- package/dist/commands/db/migrate.js +33 -0
- package/dist/commands/index.js +8 -0
- package/dist/commands/publish/index.d.ts +21 -0
- package/dist/commands/publish/index.js +68 -0
- package/dist/config/defaults.js +15 -1
- package/dist/config/loader.js +6 -1
- package/dist/config/normalizers/declaration.js +4 -1
- package/dist/config/normalizers/page.d.ts +18 -0
- package/dist/config/normalizers/page.js +34 -0
- package/dist/config/normalizers/pattern.js +7 -1
- package/dist/config/runtime.d.ts +4 -0
- package/dist/config/runtime.js +53 -5
- package/dist/index.d.ts +65 -1
- package/dist/index.js +123 -3
- package/dist/nav/index.d.ts +83 -0
- package/dist/nav/index.js +9 -0
- package/dist/nav/registry.d.ts +15 -0
- package/dist/nav/registry.js +42 -0
- package/dist/nav/resolver.d.ts +8 -0
- package/dist/nav/resolver.js +192 -0
- package/dist/nav/workspace.d.ts +18 -0
- package/dist/nav/workspace.js +103 -0
- package/dist/pipeline/styles.js +7 -35
- package/dist/pipeline/token-transformers.d.ts +45 -0
- package/dist/pipeline/token-transformers.js +71 -0
- package/dist/registry/asset-storage/adapters/vercel-blob.d.ts +17 -0
- package/dist/registry/asset-storage/adapters/vercel-blob.js +87 -0
- package/dist/registry/asset-storage/define.d.ts +10 -0
- package/dist/registry/asset-storage/define.js +13 -0
- package/dist/registry/asset-storage/resolve.d.ts +45 -0
- package/dist/registry/asset-storage/resolve.js +36 -0
- package/dist/registry/asset-storage/types.d.ts +51 -0
- package/dist/registry/asset-storage/types.js +9 -0
- package/dist/registry/assets/layout.d.ts +22 -0
- package/dist/registry/assets/layout.js +30 -0
- package/dist/registry/assets/sets.d.ts +18 -0
- package/dist/registry/assets/sets.js +44 -0
- package/dist/registry/assets/transfer.d.ts +45 -0
- package/dist/registry/assets/transfer.js +11 -0
- package/dist/registry/checkout/assets.d.ts +16 -0
- package/dist/registry/checkout/assets.js +150 -0
- package/dist/registry/checkout/index.d.ts +33 -0
- package/dist/registry/checkout/index.js +610 -0
- package/dist/registry/checkout/tokens.d.ts +15 -0
- package/dist/registry/checkout/tokens.js +171 -0
- package/dist/registry/client.d.ts +61 -0
- package/dist/registry/client.js +211 -0
- package/dist/registry/connection.d.ts +30 -0
- package/dist/registry/connection.js +36 -0
- package/dist/registry/db/adapter.d.ts +43 -0
- package/dist/registry/db/adapter.js +59 -0
- package/dist/registry/db/client.d.ts +35 -0
- package/dist/registry/db/client.js +94 -0
- package/dist/registry/db/index.d.ts +5 -0
- package/dist/registry/db/index.js +24 -0
- package/dist/registry/db/migrate.d.ts +23 -0
- package/dist/registry/db/migrate.js +64 -0
- package/dist/registry/db/schema.d.ts +4869 -0
- package/dist/registry/db/schema.js +347 -0
- package/dist/registry/path.d.ts +4 -0
- package/dist/registry/path.js +31 -0
- package/dist/registry/publish/artifacts.d.ts +16 -0
- package/dist/registry/publish/artifacts.js +132 -0
- package/dist/registry/publish/assets.d.ts +16 -0
- package/dist/registry/publish/assets.js +129 -0
- package/dist/registry/publish/errors.d.ts +5 -0
- package/dist/registry/publish/errors.js +26 -0
- package/dist/registry/publish/index.d.ts +27 -0
- package/dist/registry/publish/index.js +126 -0
- package/dist/registry/publish/package.d.ts +27 -0
- package/dist/registry/publish/package.js +257 -0
- package/dist/registry/publish/publish-build.d.ts +12 -0
- package/dist/registry/publish/publish-build.js +43 -0
- package/dist/registry/publish/tokens.d.ts +18 -0
- package/dist/registry/publish/tokens.js +112 -0
- package/dist/registry/tokens/sets.d.ts +59 -0
- package/dist/registry/tokens/sets.js +109 -0
- package/dist/registry/tokens/transfer.d.ts +48 -0
- package/dist/registry/tokens/transfer.js +10 -0
- package/dist/registry/transfer.d.ts +75 -0
- package/dist/registry/transfer.js +11 -0
- package/dist/store/filesystem-assets.d.ts +17 -0
- package/dist/store/filesystem-assets.js +147 -0
- package/dist/store/filesystem.d.ts +76 -0
- package/dist/store/filesystem.js +208 -0
- package/dist/store/index.d.ts +20 -0
- package/dist/store/index.js +38 -0
- package/dist/store/registry.d.ts +77 -0
- package/dist/store/registry.js +350 -0
- package/dist/store/source-files.d.ts +24 -0
- package/dist/store/source-files.js +147 -0
- package/dist/store/types.d.ts +185 -0
- package/dist/store/types.js +12 -0
- package/dist/transformers/plugins/csf-render.js +17 -8
- package/dist/transformers/plugins/handlebars-previews.js +10 -3
- package/dist/transformers/plugins/ssr-render.js +116 -49
- package/dist/transformers/preview/component/api.js +3 -1
- package/dist/transformers/preview/component/artifacts.js +8 -1
- package/dist/transformers/preview/component/builder.js +13 -5
- package/dist/transformers/preview/component/css.js +9 -1
- package/dist/transformers/preview/component/javascript.d.ts +9 -3
- package/dist/transformers/preview/component/javascript.js +45 -17
- package/dist/transformers/preview/component/shared-artifacts.d.ts +83 -0
- package/dist/transformers/preview/component/shared-artifacts.js +92 -0
- package/dist/transformers/preview/component.js +5 -2
- package/dist/transformers/preview/pattern/builder.js +28 -9
- package/dist/transformers/preview/pattern/html.d.ts +13 -10
- package/dist/transformers/preview/pattern/html.js +62 -27
- package/dist/transformers/preview/types.d.ts +38 -0
- package/dist/transformers/utils/handlebars.d.ts +3 -0
- package/dist/transformers/utils/handlebars.js +19 -5
- package/dist/types/config.d.ts +124 -31
- package/dist/utils/menu-shell.d.ts +60 -0
- package/dist/utils/menu-shell.js +221 -0
- package/dist/utils/stable-stringify.d.ts +2 -0
- package/dist/utils/stable-stringify.js +8 -0
- package/docs/cli.md +4 -2
- package/drizzle/0000_init_registry_storage.sql +95 -0
- package/drizzle/0001_add_review_metadata.sql +2 -0
- package/drizzle/0002_add_pages.sql +28 -0
- package/drizzle/0003_add_tokens.sql +29 -0
- package/drizzle/0004_add_assets.sql +37 -0
- package/drizzle/meta/0000_snapshot.json +642 -0
- package/drizzle/meta/0001_snapshot.json +654 -0
- package/drizzle/meta/0002_snapshot.json +835 -0
- package/drizzle/meta/0003_snapshot.json +1023 -0
- package/drizzle/meta/0004_snapshot.json +1252 -0
- package/drizzle/meta/_journal.json +41 -0
- package/drizzle.config.ts +16 -0
- package/package.json +10 -1
- package/src/app/components/Component/Preview.tsx +3 -2
- package/src/app/components/Component/RegistryActions.tsx +150 -0
- package/src/app/components/Foundations/DisplayLogo.tsx +11 -3
- package/src/app/components/Layout/Header.tsx +21 -3
- package/src/app/components/Layout/Main.tsx +11 -3
- package/src/app/components/Navigation/MainNav.tsx +4 -2
- package/src/app/components/Navigation/MobileNav.tsx +4 -2
- package/src/app/components/Navigation/SideNav.tsx +46 -11
- package/src/app/components/NotFound.tsx +24 -0
- package/src/app/components/context/NavProvider.tsx +71 -0
- package/src/app/components/old/Header.tsx +3 -3
- package/src/app/components/util/index.ts +203 -380
- package/src/app/components/util/useCollectionAssets.ts +125 -0
- package/src/app/components/util/useFoundationTokens.ts +67 -0
- package/src/app/generated/default-pages.json +1 -0
- package/src/app/generated/nav-shell.json +1 -0
- package/src/app/lib/api/query.ts +6 -0
- package/src/app/lib/api/redact.ts +46 -0
- package/src/app/lib/asset-storage.ts +81 -0
- package/src/app/lib/docs-api/artifacts.ts +116 -0
- package/src/app/lib/docs-api/backend.ts +151 -0
- package/src/app/lib/docs-api/errors.ts +52 -0
- package/src/app/lib/docs-api/handler.ts +34 -0
- package/src/app/lib/docs-api/index.ts +7 -0
- package/src/app/lib/docs-api/records.ts +189 -0
- package/src/app/lib/docs-api/registry.ts +130 -0
- package/src/app/lib/docs-api/runtime-config.ts +173 -0
- package/src/app/lib/docs-api/serve.ts +37 -0
- package/src/app/lib/docs-api/token-detail.ts +19 -0
- package/src/app/lib/registry-api/allowlist.ts +163 -0
- package/src/app/lib/registry-api/asset-transfer.ts +363 -0
- package/src/app/lib/registry-api/auth.ts +68 -0
- package/src/app/lib/registry-api/entity-routes.ts +197 -0
- package/src/app/lib/registry-api/errors.ts +51 -0
- package/src/app/lib/registry-api/files.ts +135 -0
- package/src/app/lib/registry-api/handler.ts +96 -0
- package/src/app/lib/registry-api/index.ts +4 -0
- package/src/app/lib/registry-api/meta.ts +87 -0
- package/src/app/lib/registry-api/revalidate.ts +36 -0
- package/src/app/lib/registry-api/store.ts +348 -0
- package/src/app/lib/registry-api/token-transfer.ts +231 -0
- package/src/app/lib/registry-api/transfer.ts +513 -0
- package/src/app/lib/registry-api/validation.ts +74 -0
- package/src/app/lib/registry-connection.ts +58 -0
- package/src/app/lib/utils.ts +14 -0
- package/src/app/next.config.mjs +72 -13
- package/src/app/pages/[...slug]/index.tsx +74 -20
- package/src/app/pages/_app.tsx +5 -2
- package/src/app/pages/api/docs/artifacts/[...artifactPath].ts +16 -0
- package/src/app/pages/api/docs/assets/[collection]/[...path].ts +50 -0
- package/src/app/pages/api/docs/assets/[collection]/index.ts +22 -0
- package/src/app/pages/api/docs/components/[id]/assets/[asset].ts +33 -0
- package/src/app/pages/api/docs/components/[id]/inspect/[previewId].ts +20 -0
- package/src/app/pages/api/docs/components/[id]/previews/[previewId].ts +21 -0
- package/src/app/pages/api/docs/components/[id].ts +24 -0
- package/src/app/pages/api/docs/components.json.ts +12 -0
- package/src/app/pages/api/docs/nav.json.ts +48 -0
- package/src/app/pages/api/docs/patterns/[id]/preview.ts +19 -0
- package/src/app/pages/api/docs/patterns/[id].ts +24 -0
- package/src/app/pages/api/docs/patterns.json.ts +12 -0
- package/src/app/pages/api/docs/tokens/components/[id].ts +30 -0
- package/src/app/pages/api/docs/tokens/foundations/[type].ts +32 -0
- package/src/app/pages/api/registry/components/[id]/files/[...filePath].ts +19 -0
- package/src/app/pages/api/registry/components/[id]/files/index.ts +19 -0
- package/src/app/pages/api/registry/components/[id].ts +20 -0
- package/src/app/pages/api/registry/components/index.ts +19 -0
- package/src/app/pages/api/registry/health.ts +20 -0
- package/src/app/pages/api/registry/patterns/[id]/files/[...filePath].ts +19 -0
- package/src/app/pages/api/registry/patterns/[id]/files/index.ts +19 -0
- package/src/app/pages/api/registry/patterns/[id].ts +20 -0
- package/src/app/pages/api/registry/patterns/index.ts +19 -0
- package/src/app/pages/api/registry/transfer/[kind]/[id].ts +39 -0
- package/src/app/pages/api/registry/transfer/assets/[collection].ts +24 -0
- package/src/app/pages/api/registry/transfer/assets/blobs/[hash].ts +23 -0
- package/src/app/pages/api/registry/transfer/assets/blobs/have.ts +11 -0
- package/src/app/pages/api/registry/transfer/assets/index.ts +10 -0
- package/src/app/pages/api/registry/transfer/tokens/[...setId].ts +26 -0
- package/src/app/pages/api/registry/transfer/tokens/index.ts +11 -0
- package/src/app/pages/assets/fonts.tsx +41 -17
- package/src/app/pages/assets/index.tsx +2 -2
- package/src/app/pages/assets/logos.tsx +6 -3
- package/src/app/pages/foundations/colors.tsx +18 -8
- package/src/app/pages/foundations/effects.tsx +18 -8
- package/src/app/pages/foundations/icons/[name]/index.tsx +28 -16
- package/src/app/pages/foundations/icons/index.tsx +18 -12
- package/src/app/pages/foundations/index.tsx +1 -1
- package/src/app/pages/foundations/logo.tsx +12 -8
- package/src/app/pages/foundations/typography.tsx +19 -9
- package/src/app/pages/index.tsx +1 -1
- package/src/app/pages/system/component/[component]/index.tsx +87 -17
- package/src/app/pages/system/index.tsx +13 -4
- package/src/app/pages/system/pattern/[pattern]/index.tsx +70 -14
- package/src/app/pages/system/tokens/components/[component]/index.tsx +42 -14
- package/src/app/pages/system/tokens/foundations/colors.tsx +7 -5
- package/src/app/pages/system/tokens/foundations/effects.tsx +7 -5
- package/src/app/pages/system/tokens/foundations/typography.tsx +8 -4
- package/src/app/tsconfig.json +2 -3
- package/src/app-builder/build.ts +555 -11
- package/src/app-builder/client-config.ts +61 -4
- package/src/app-builder/config-diff/snapshot.ts +1 -11
- package/src/app-builder/index.ts +1 -1
- package/src/app-builder/nav-shell.ts +74 -0
- package/src/app-builder/paths.ts +27 -1
- package/src/app-builder/static-export.ts +242 -0
- package/src/app-builder/vercel-output.ts +297 -0
- package/src/app-builder/watchers/utils.ts +2 -0
- package/src/app-builder/watchers.ts +149 -25
- package/src/app-builder/websocket.ts +2 -2
- package/src/artifacts/README.md +14 -0
- package/src/artifacts/index.ts +24 -0
- package/src/artifacts/types.ts +146 -0
- package/src/artifacts/url.ts +122 -0
- package/src/cache/build-cache.ts +47 -2
- package/src/commands/build/app.ts +1 -2
- package/src/commands/build/index.ts +61 -0
- package/src/commands/checkout/index.ts +66 -0
- package/src/commands/db/migrate.ts +25 -0
- package/src/commands/index.ts +8 -0
- package/src/commands/publish/index.ts +63 -0
- package/src/config/defaults.ts +15 -0
- package/src/config/loader.ts +10 -1
- package/src/config/normalizers/declaration.ts +3 -0
- package/src/config/normalizers/page.ts +45 -0
- package/src/config/normalizers/pattern.ts +4 -1
- package/src/config/runtime.ts +48 -2
- package/src/index.ts +126 -3
- package/src/nav/index.ts +94 -0
- package/src/nav/registry.ts +54 -0
- package/src/nav/resolver.ts +217 -0
- package/src/nav/workspace.ts +87 -0
- package/src/pipeline/styles.ts +4 -35
- package/src/pipeline/token-transformers.ts +97 -0
- package/src/registry/asset-storage/adapters/vercel-blob.ts +51 -0
- package/src/registry/asset-storage/define.ts +15 -0
- package/src/registry/asset-storage/resolve.ts +72 -0
- package/src/registry/asset-storage/types.ts +46 -0
- package/src/registry/assets/layout.ts +38 -0
- package/src/registry/assets/sets.ts +48 -0
- package/src/registry/assets/transfer.ts +50 -0
- package/src/registry/checkout/assets.ts +126 -0
- package/src/registry/checkout/index.ts +596 -0
- package/src/registry/checkout/tokens.ts +152 -0
- package/src/registry/client.ts +234 -0
- package/src/registry/connection.ts +50 -0
- package/src/registry/db/adapter.ts +74 -0
- package/src/registry/db/client.ts +88 -0
- package/src/registry/db/index.ts +35 -0
- package/src/registry/db/migrate.ts +56 -0
- package/src/registry/db/schema.ts +444 -0
- package/src/registry/path.ts +26 -0
- package/src/registry/publish/artifacts.ts +147 -0
- package/src/registry/publish/assets.ts +129 -0
- package/src/registry/publish/errors.ts +34 -0
- package/src/registry/publish/index.ts +128 -0
- package/src/registry/publish/package.ts +272 -0
- package/src/registry/publish/publish-build.ts +51 -0
- package/src/registry/publish/tokens.ts +119 -0
- package/src/registry/tokens/sets.ts +132 -0
- package/src/registry/tokens/transfer.ts +53 -0
- package/src/registry/transfer.ts +82 -0
- package/src/store/filesystem-assets.ts +152 -0
- package/src/store/filesystem.ts +237 -0
- package/src/store/index.ts +62 -0
- package/src/store/registry.ts +334 -0
- package/src/store/source-files.ts +155 -0
- package/src/store/types.ts +210 -0
- package/src/transformers/plugins/csf-render.ts +31 -7
- package/src/transformers/plugins/handlebars-previews.ts +22 -3
- package/src/transformers/plugins/ssr-render.ts +145 -57
- package/src/transformers/preview/component/api.ts +2 -1
- package/src/transformers/preview/component/artifacts.ts +10 -1
- package/src/transformers/preview/component/builder.ts +9 -2
- package/src/transformers/preview/component/css.ts +10 -1
- package/src/transformers/preview/component/javascript.ts +55 -20
- package/src/transformers/preview/component/shared-artifacts.ts +146 -0
- package/src/transformers/preview/component.ts +5 -2
- package/src/transformers/preview/pattern/builder.ts +33 -7
- package/src/transformers/preview/pattern/html.ts +71 -27
- package/src/transformers/preview/types.ts +42 -0
- package/src/transformers/utils/handlebars.ts +31 -5
- package/src/types/config.ts +129 -31
- package/src/utils/menu-shell.ts +241 -0
- package/src/utils/stable-stringify.ts +7 -0
- package/templates/default/package.json.tpl +1 -1
- package/handoff.config.js +0 -90
- package/src/app/components/ComponentSearch.tsx +0 -93
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import Handoff from '..';
|
|
2
|
+
import type { RuntimeMode } from '../types/config';
|
|
2
3
|
/**
|
|
3
4
|
* Publishes the tokens API files to the public directory.
|
|
4
5
|
*/
|
|
5
6
|
export declare const generateTokensApi: (handoff: Handoff) => Promise<void>;
|
|
7
|
+
/** Options for {@link persistClientConfig}. */
|
|
8
|
+
export interface PersistClientConfigOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Force the runtime mode persisted to both `client.config.json` and `runtime.server.json`,
|
|
11
|
+
* irrespective of the source project's `runtime.mode`. The registry build sets this to `registry`
|
|
12
|
+
* so the packaged artifact reports registry mode (badge + DB-backed reads) even when built from a
|
|
13
|
+
* workspace-mode project.
|
|
14
|
+
*/
|
|
15
|
+
runtimeModeOverride?: RuntimeMode;
|
|
16
|
+
}
|
|
6
17
|
/**
|
|
7
|
-
* Persists the
|
|
18
|
+
* Persists the app's resolved config to disk: the browser-facing `client.config.json` and the
|
|
19
|
+
* server-only `runtime.server.json` the docs read API resolves the active mode + registry
|
|
20
|
+
* connection inputs from. Both are always written together so they never drift.
|
|
8
21
|
*/
|
|
9
|
-
export declare const persistClientConfig: (handoff: Handoff) => Promise<void>;
|
|
22
|
+
export declare const persistClientConfig: (handoff: Handoff, options?: PersistClientConfigOptions) => Promise<void>;
|
|
@@ -16,6 +16,8 @@ exports.persistClientConfig = exports.generateTokensApi = void 0;
|
|
|
16
16
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
17
17
|
const path_1 = __importDefault(require("path"));
|
|
18
18
|
const config_1 = require("../config");
|
|
19
|
+
const resolve_1 = require("../registry/asset-storage/resolve");
|
|
20
|
+
const adapter_1 = require("../registry/db/adapter");
|
|
19
21
|
const paths_1 = require("./paths");
|
|
20
22
|
/**
|
|
21
23
|
* Publishes the tokens API files to the public directory.
|
|
@@ -50,13 +52,50 @@ const generateTokensApi = (handoff) => __awaiter(void 0, void 0, void 0, functio
|
|
|
50
52
|
});
|
|
51
53
|
exports.generateTokensApi = generateTokensApi;
|
|
52
54
|
/**
|
|
53
|
-
*
|
|
55
|
+
* Build the server-only runtime config consumed by the docs read API.
|
|
56
|
+
*
|
|
57
|
+
* The browser-facing `client.config.json` carries only the resolved mode; the server additionally
|
|
58
|
+
* needs the registry connection *inputs* (selected adapter + the *name* of the database-URL env
|
|
59
|
+
* var) to back the registry-mode docs read API. These are non-secret — the connection-string value
|
|
60
|
+
* itself is never persisted, only resolved from the env var at request time.
|
|
54
61
|
*/
|
|
55
|
-
const
|
|
62
|
+
const buildServerRuntimeConfig = (config, modeOverride) => {
|
|
63
|
+
var _a, _b;
|
|
64
|
+
const assetStorage = (0, resolve_1.resolveAssetStorageFromConfig)(config);
|
|
65
|
+
return {
|
|
66
|
+
mode: (_b = modeOverride !== null && modeOverride !== void 0 ? modeOverride : (_a = config === null || config === void 0 ? void 0 : config.runtime) === null || _a === void 0 ? void 0 : _a.mode) !== null && _b !== void 0 ? _b : 'workspace',
|
|
67
|
+
registry: {
|
|
68
|
+
adapter: (0, adapter_1.resolveRegistryAdapter)(config),
|
|
69
|
+
databaseUrlEnv: (0, adapter_1.resolveDatabaseUrlEnv)(config),
|
|
70
|
+
apiTokenEnv: (0, adapter_1.resolveApiTokenEnv)(config),
|
|
71
|
+
},
|
|
72
|
+
assetStorage: {
|
|
73
|
+
adapter: assetStorage.adapterKind,
|
|
74
|
+
module: assetStorage.module,
|
|
75
|
+
tokenEnv: assetStorage.tokenEnv,
|
|
76
|
+
maxInlineBytes: assetStorage.maxInlineBytes,
|
|
77
|
+
options: assetStorage.options,
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Persists the app's resolved config to disk: the browser-facing `client.config.json` and the
|
|
83
|
+
* server-only `runtime.server.json` the docs read API resolves the active mode + registry
|
|
84
|
+
* connection inputs from. Both are always written together so they never drift.
|
|
85
|
+
*/
|
|
86
|
+
const persistClientConfig = (handoff_1, ...args_1) => __awaiter(void 0, [handoff_1, ...args_1], void 0, function* (handoff, options = {}) {
|
|
56
87
|
const appPath = (0, paths_1.getAppPath)(handoff);
|
|
57
|
-
const destination = path_1.default.resolve(appPath, 'client.config.json');
|
|
58
88
|
// Ensure directory exists
|
|
59
89
|
yield fs_extra_1.default.ensureDir(appPath);
|
|
60
|
-
|
|
90
|
+
const clientConfig = (0, config_1.getClientConfig)(handoff.config);
|
|
91
|
+
if (options.runtimeModeOverride) {
|
|
92
|
+
// A forced registry mode (the registry build target) is never a connected workspace, so the
|
|
93
|
+
// baked client config must report `connected: false` even when packaged from a connected
|
|
94
|
+
// workspace project.
|
|
95
|
+
const connected = options.runtimeModeOverride === 'registry' ? false : clientConfig.runtime.connected;
|
|
96
|
+
clientConfig.runtime = Object.assign(Object.assign({}, clientConfig.runtime), { mode: options.runtimeModeOverride, connected });
|
|
97
|
+
}
|
|
98
|
+
yield fs_extra_1.default.writeJson(path_1.default.resolve(appPath, 'client.config.json'), { config: clientConfig }, { spaces: 2 });
|
|
99
|
+
yield fs_extra_1.default.writeJson(path_1.default.resolve(appPath, 'runtime.server.json'), buildServerRuntimeConfig(handoff.config, options.runtimeModeOverride), { spaces: 2 });
|
|
61
100
|
});
|
|
62
101
|
exports.persistClientConfig = persistClientConfig;
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
/** outerKey -> Map<innerKey, serializedValue> */
|
|
2
2
|
export type MapSnapshot = Map<string, Map<string, string>>;
|
|
3
|
-
|
|
4
|
-
* Deterministic JSON serializer that sorts object keys at every nesting level.
|
|
5
|
-
* Prevents false-positive diffs when identical values are constructed with
|
|
6
|
-
* different key insertion order across reloads.
|
|
7
|
-
*/
|
|
8
|
-
export declare const stableStringify: (value: unknown) => string;
|
|
3
|
+
export { stableStringify } from '../../utils/stable-stringify';
|
|
9
4
|
/**
|
|
10
5
|
* Compares two MapSnapshots and returns the outer keys where the inner maps
|
|
11
6
|
* differ (added, removed, or values changed).
|
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.diffMapSnapshots = exports.stableStringify = void 0;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* Prevents false-positive diffs when identical values are constructed with
|
|
7
|
-
* different key insertion order across reloads.
|
|
8
|
-
*/
|
|
9
|
-
const stableStringify = (value) => JSON.stringify(value, (_, v) => v && typeof v === 'object' && !Array.isArray(v)
|
|
10
|
-
? Object.fromEntries(Object.entries(v).sort(([a], [b]) => a.localeCompare(b)))
|
|
11
|
-
: v);
|
|
12
|
-
exports.stableStringify = stableStringify;
|
|
4
|
+
var stable_stringify_1 = require("../../utils/stable-stringify");
|
|
5
|
+
Object.defineProperty(exports, "stableStringify", { enumerable: true, get: function () { return stable_stringify_1.stableStringify; } });
|
|
13
6
|
/**
|
|
14
7
|
* Compares two MapSnapshots and returns the outer keys where the inner maps
|
|
15
8
|
* differ (added, removed, or values changed).
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { buildComponents } from '../pipeline/components';
|
|
2
|
-
export { default, devApp, watchApp } from './build';
|
|
2
|
+
export { default, devApp, watchApp, HandoffBuildError, DEFAULT_BUILD_TARGET, type BuildPackage, type BuildTarget } from './build';
|
|
@@ -3,10 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.watchApp = exports.devApp = exports.default = exports.buildComponents = void 0;
|
|
6
|
+
exports.DEFAULT_BUILD_TARGET = exports.HandoffBuildError = exports.watchApp = exports.devApp = exports.default = exports.buildComponents = void 0;
|
|
7
7
|
var components_1 = require("../pipeline/components");
|
|
8
8
|
Object.defineProperty(exports, "buildComponents", { enumerable: true, get: function () { return components_1.buildComponents; } });
|
|
9
9
|
var build_1 = require("./build");
|
|
10
10
|
Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(build_1).default; } });
|
|
11
11
|
Object.defineProperty(exports, "devApp", { enumerable: true, get: function () { return build_1.devApp; } });
|
|
12
12
|
Object.defineProperty(exports, "watchApp", { enumerable: true, get: function () { return build_1.watchApp; } });
|
|
13
|
+
Object.defineProperty(exports, "HandoffBuildError", { enumerable: true, get: function () { return build_1.HandoffBuildError; } });
|
|
14
|
+
Object.defineProperty(exports, "DEFAULT_BUILD_TARGET", { enumerable: true, get: function () { return build_1.DEFAULT_BUILD_TARGET; } });
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import Handoff from '..';
|
|
2
|
+
/**
|
|
3
|
+
* Relative path (within the staged Next app) of the baked navigation shell. The docs read API
|
|
4
|
+
* (`pages/api/docs/nav.json.ts`) imports this statically so it is bundled into the route chunk and
|
|
5
|
+
* available at runtime without any filesystem read — see `src/utils/menu-shell.ts` for why the
|
|
6
|
+
* markdown-driven shell must be frozen at build time for the Vercel registry function.
|
|
7
|
+
*/
|
|
8
|
+
export declare const NAV_SHELL_RELATIVE_PATH: string;
|
|
9
|
+
/** Baked package-default markdown used as the registry catch-all's runtime fallback. */
|
|
10
|
+
export declare const DEFAULT_PAGES_RELATIVE_PATH: string;
|
|
11
|
+
/**
|
|
12
|
+
* Bake package-default docs into the staged app so registry fallback renders do not depend on the
|
|
13
|
+
* build machine's absolute module path. The static JSON import is traced into every deployment.
|
|
14
|
+
*/
|
|
15
|
+
export declare const generateDefaultPages: (handoff: Handoff, appPath: string) => Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Bake the markdown-driven navigation shell into the staged app as `generated/nav-shell.json`.
|
|
18
|
+
*
|
|
19
|
+
* Must run BEFORE `next build` so Next inlines the JSON import into the API route chunk. Generated
|
|
20
|
+
* for every build target (workspace dev/start, static export, registry) so the static import never
|
|
21
|
+
* fails to resolve; only registry-mode clients actually fetch and use it.
|
|
22
|
+
*/
|
|
23
|
+
export declare const generateNavShell: (handoff: Handoff, appPath: string, includeWorkspacePages?: boolean) => Promise<void>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.generateNavShell = exports.generateDefaultPages = exports.DEFAULT_PAGES_RELATIVE_PATH = exports.NAV_SHELL_RELATIVE_PATH = void 0;
|
|
16
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
17
|
+
const gray_matter_1 = __importDefault(require("gray-matter"));
|
|
18
|
+
const path_1 = __importDefault(require("path"));
|
|
19
|
+
const menu_shell_1 = require("../utils/menu-shell");
|
|
20
|
+
const logger_1 = require("../utils/logger");
|
|
21
|
+
/**
|
|
22
|
+
* Relative path (within the staged Next app) of the baked navigation shell. The docs read API
|
|
23
|
+
* (`pages/api/docs/nav.json.ts`) imports this statically so it is bundled into the route chunk and
|
|
24
|
+
* available at runtime without any filesystem read — see `src/utils/menu-shell.ts` for why the
|
|
25
|
+
* markdown-driven shell must be frozen at build time for the Vercel registry function.
|
|
26
|
+
*/
|
|
27
|
+
exports.NAV_SHELL_RELATIVE_PATH = path_1.default.join('generated', 'nav-shell.json');
|
|
28
|
+
/** Baked package-default markdown used as the registry catch-all's runtime fallback. */
|
|
29
|
+
exports.DEFAULT_PAGES_RELATIVE_PATH = path_1.default.join('generated', 'default-pages.json');
|
|
30
|
+
/** Recursively collect package docs (except dedicated index routes) by their catch-all id. */
|
|
31
|
+
const collectDefaultPages = (root, relativeParts = []) => {
|
|
32
|
+
if (!fs_extra_1.default.existsSync(root))
|
|
33
|
+
return {};
|
|
34
|
+
const pages = {};
|
|
35
|
+
for (const entry of fs_extra_1.default.readdirSync(root)) {
|
|
36
|
+
const absolutePath = path_1.default.join(root, entry);
|
|
37
|
+
if (fs_extra_1.default.lstatSync(absolutePath).isDirectory()) {
|
|
38
|
+
Object.assign(pages, collectDefaultPages(absolutePath, [...relativeParts, entry]));
|
|
39
|
+
}
|
|
40
|
+
else if (entry.endsWith('.md') && entry !== 'index.md') {
|
|
41
|
+
const id = [...relativeParts, entry.replace(/\.md$/, '')].join('/');
|
|
42
|
+
const { data, content } = (0, gray_matter_1.default)(fs_extra_1.default.readFileSync(absolutePath, 'utf8'));
|
|
43
|
+
pages[id] = { metadata: data, content };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return pages;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Bake package-default docs into the staged app so registry fallback renders do not depend on the
|
|
50
|
+
* build machine's absolute module path. The static JSON import is traced into every deployment.
|
|
51
|
+
*/
|
|
52
|
+
const generateDefaultPages = (handoff, appPath) => __awaiter(void 0, void 0, void 0, function* () {
|
|
53
|
+
const pages = collectDefaultPages(path_1.default.join(handoff.modulePath, 'config', 'docs'));
|
|
54
|
+
const target = path_1.default.resolve(appPath, exports.DEFAULT_PAGES_RELATIVE_PATH);
|
|
55
|
+
yield fs_extra_1.default.ensureDir(path_1.default.dirname(target));
|
|
56
|
+
yield fs_extra_1.default.writeJson(target, pages);
|
|
57
|
+
});
|
|
58
|
+
exports.generateDefaultPages = generateDefaultPages;
|
|
59
|
+
/**
|
|
60
|
+
* Bake the markdown-driven navigation shell into the staged app as `generated/nav-shell.json`.
|
|
61
|
+
*
|
|
62
|
+
* Must run BEFORE `next build` so Next inlines the JSON import into the API route chunk. Generated
|
|
63
|
+
* for every build target (workspace dev/start, static export, registry) so the static import never
|
|
64
|
+
* fails to resolve; only registry-mode clients actually fetch and use it.
|
|
65
|
+
*/
|
|
66
|
+
const generateNavShell = (handoff_1, appPath_1, ...args_1) => __awaiter(void 0, [handoff_1, appPath_1, ...args_1], void 0, function* (handoff, appPath, includeWorkspacePages = true) {
|
|
67
|
+
var _a, _b, _c;
|
|
68
|
+
const docRoot = path_1.default.join(handoff.modulePath, 'config', 'docs');
|
|
69
|
+
// A registry serves only what is in its database, so workspace `pages/` present at build time must
|
|
70
|
+
// NOT be frozen into the shell (same rule as components/patterns). Published pages are merged into
|
|
71
|
+
// the shell at request time by `/api/docs/nav.json`; the package's own `config/docs` stays baked.
|
|
72
|
+
const workingPagesDir = includeWorkspacePages ? path_1.default.resolve(handoff.workingPath, 'pages') : undefined;
|
|
73
|
+
const basePath = (_c = (_b = (_a = handoff.config) === null || _a === void 0 ? void 0 : _a.app) === null || _b === void 0 ? void 0 : _b.base_path) !== null && _c !== void 0 ? _c : '';
|
|
74
|
+
const shell = (0, menu_shell_1.buildMenuShell)({ docRoot, workingPagesDir, basePath });
|
|
75
|
+
const target = path_1.default.resolve(appPath, exports.NAV_SHELL_RELATIVE_PATH);
|
|
76
|
+
yield fs_extra_1.default.ensureDir(path_1.default.dirname(target));
|
|
77
|
+
yield fs_extra_1.default.writeJson(target, shell, { spaces: 2 });
|
|
78
|
+
logger_1.Logger.info(`Baked navigation shell (${shell.length} sections) to ${exports.NAV_SHELL_RELATIVE_PATH}.`);
|
|
79
|
+
});
|
|
80
|
+
exports.generateNavShell = generateNavShell;
|
|
@@ -50,14 +50,35 @@ const mirrorDirectory = (sourcePath, destinationPath) => __awaiter(void 0, void
|
|
|
50
50
|
yield fs_extra_1.default.ensureDir(path_1.default.dirname(destinationPath));
|
|
51
51
|
yield fs_extra_1.default.copy(sourcePath, destinationPath, { overwrite: true });
|
|
52
52
|
});
|
|
53
|
+
/**
|
|
54
|
+
* Stage the fetch-produced asset download bundles (`exported/<id>/{collection}.zip`) into the app
|
|
55
|
+
* public root, where the docs read API's workspace asset store serves them at
|
|
56
|
+
* `/api/docs/assets/{collection}/{collection}.zip` (the canonical download URL). `fetch` writes these
|
|
57
|
+
* bundles, but a plain `build`/`start` cleans and re-stages `.handoff` without carrying them, so the
|
|
58
|
+
* download links would 404 until a fetch happened to run. Copying the committed export bundles here
|
|
59
|
+
* makes the route resolve deterministically in workspace dev and static builds. Absent bundles (a
|
|
60
|
+
* project with no logos/icons) are skipped.
|
|
61
|
+
*/
|
|
62
|
+
const stageAssetDownloadBundles = (handoff, destinationPublicPath) => __awaiter(void 0, void 0, void 0, function* () {
|
|
63
|
+
const bundles = [
|
|
64
|
+
{ src: handoff.getLogosZipFilePath(), name: 'logos.zip' },
|
|
65
|
+
{ src: handoff.getIconsZipFilePath(), name: 'icons.zip' },
|
|
66
|
+
];
|
|
67
|
+
for (const { src, name } of bundles) {
|
|
68
|
+
if (fs_extra_1.default.existsSync(src) && fs_extra_1.default.statSync(src).isFile()) {
|
|
69
|
+
yield fs_extra_1.default.ensureDir(destinationPublicPath);
|
|
70
|
+
yield fs_extra_1.default.copy(src, path_1.default.resolve(destinationPublicPath, name), { overwrite: true });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
53
74
|
/**
|
|
54
75
|
* Copy the public dir from the working dir to the module dir.
|
|
55
76
|
*/
|
|
56
77
|
const syncPublicFiles = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
|
|
57
78
|
const appPath = (0, exports.getAppPath)(handoff);
|
|
79
|
+
const destinationPublicPath = path_1.default.resolve(appPath, 'public');
|
|
58
80
|
const workingPublicPath = (0, exports.getWorkingPublicPath)(handoff);
|
|
59
81
|
if (workingPublicPath) {
|
|
60
|
-
const destinationPublicPath = path_1.default.resolve(appPath, 'public');
|
|
61
82
|
const sourceApiPath = path_1.default.resolve(workingPublicPath, 'api');
|
|
62
83
|
const destinationApiPath = path_1.default.resolve(destinationPublicPath, 'api');
|
|
63
84
|
yield fs_extra_1.default.copy(workingPublicPath, destinationPublicPath, {
|
|
@@ -69,5 +90,8 @@ const syncPublicFiles = (handoff) => __awaiter(void 0, void 0, void 0, function*
|
|
|
69
90
|
});
|
|
70
91
|
yield mirrorDirectory(sourceApiPath, destinationApiPath);
|
|
71
92
|
}
|
|
93
|
+
// Stage the asset download bundles regardless of whether the workspace has a `public/` dir — they
|
|
94
|
+
// come from the fetch output, not the workspace public tree.
|
|
95
|
+
yield stageAssetDownloadBundles(handoff, destinationPublicPath);
|
|
72
96
|
});
|
|
73
97
|
exports.syncPublicFiles = syncPublicFiles;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import Handoff from '..';
|
|
2
|
+
/**
|
|
3
|
+
* Validate that every artifact referenced by generated HTML can be materialized — i.e. the
|
|
4
|
+
* referenced file exists under the artifact root. Required references (e.g. a React preview's
|
|
5
|
+
* `component/<id>.client.js`) are always emitted, so a missing one fails the static build *before*
|
|
6
|
+
* export rather than producing a site with broken previews.
|
|
7
|
+
*
|
|
8
|
+
* @throws When any generated HTML references an artifact that is not present on disk.
|
|
9
|
+
*/
|
|
10
|
+
export declare const validateReferencedArtifacts: (handoff: Handoff) => void;
|
|
11
|
+
/**
|
|
12
|
+
* Materialize the docs read model into route-shaped static files under `outDir` (the Next export
|
|
13
|
+
* output). Produces the canonical artifact tree plus the list/detail metadata reads, matching the
|
|
14
|
+
* shapes the live docs read API returns so the exported site is byte-for-byte interchangeable with
|
|
15
|
+
* the dev server's responses.
|
|
16
|
+
*/
|
|
17
|
+
export declare const materializeDocsReadModel: (handoff: Handoff, outDir: string) => Promise<void>;
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.materializeDocsReadModel = exports.validateReferencedArtifacts = void 0;
|
|
16
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
17
|
+
const path_1 = __importDefault(require("path"));
|
|
18
|
+
const url_1 = require("../artifacts/url");
|
|
19
|
+
const logger_1 = require("../utils/logger");
|
|
20
|
+
const paths_1 = require("./paths");
|
|
21
|
+
/**
|
|
22
|
+
* Absolute path of the generated artifact root for the running app — the same
|
|
23
|
+
* `.handoff/{projectId}/public/api` directory the docs read API resolves artifacts from.
|
|
24
|
+
*/
|
|
25
|
+
const getStaticArtifactRoot = (handoff) => path_1.default.resolve((0, paths_1.getAppPath)(handoff), 'public', 'api');
|
|
26
|
+
const readJsonArray = (absolutePath) => {
|
|
27
|
+
try {
|
|
28
|
+
if (!fs_extra_1.default.existsSync(absolutePath) || !fs_extra_1.default.statSync(absolutePath).isFile()) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
const parsed = JSON.parse(fs_extra_1.default.readFileSync(absolutePath, 'utf8'));
|
|
32
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
33
|
+
}
|
|
34
|
+
catch (_a) {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
/** Whether the entity's primary detail artifact was generated (drives `current` vs `missing`). */
|
|
39
|
+
const detailArtifactExists = (artifactRoot, relativePath) => {
|
|
40
|
+
const absolutePath = path_1.default.resolve(artifactRoot, relativePath);
|
|
41
|
+
return fs_extra_1.default.existsSync(absolutePath) && fs_extra_1.default.statSync(absolutePath).isFile();
|
|
42
|
+
};
|
|
43
|
+
const deriveBuildStatus = (artifactRoot, detailRelativePath) => detailArtifactExists(artifactRoot, detailRelativePath) ? 'current' : 'missing';
|
|
44
|
+
/**
|
|
45
|
+
* Suffix of a component-owned React client/hydration bundle (`<id>.client.js`). Interactive React
|
|
46
|
+
* previews treat this artifact as a *required* reference: the
|
|
47
|
+
* preview cannot hydrate without it, so a missing one is a genuine build failure.
|
|
48
|
+
*/
|
|
49
|
+
const CLIENT_ARTIFACT_SUFFIX = '.client.js';
|
|
50
|
+
/**
|
|
51
|
+
* Whether a referenced artifact is *required* — i.e. the referencing HTML cannot render correctly
|
|
52
|
+
* without it. Only the React client bundle qualifies. Component-owned styles/scripts
|
|
53
|
+
* (`<id>.css`/`<id>.js`) and shared/global artifacts (`main.css`/`main.js`/`shared.css`) are
|
|
54
|
+
* optional: the renderers emit `<id>.css`/`<id>.js` optimistically and a component that produces no
|
|
55
|
+
* stylesheet/script simply has none, so an absent optional artifact must not fail the build.
|
|
56
|
+
*/
|
|
57
|
+
const isRequiredReference = (artifactPath) => artifactPath.endsWith(CLIENT_ARTIFACT_SUFFIX);
|
|
58
|
+
/**
|
|
59
|
+
* Extract the logical artifact paths a generated HTML document references through the canonical
|
|
60
|
+
* artifact route. References are read from the emitted `href`/`src` URLs (the build's structured
|
|
61
|
+
* references are realized as these tags); this is a build-time integrity check, not the runtime
|
|
62
|
+
* dependency-resolution path.
|
|
63
|
+
*/
|
|
64
|
+
const extractReferencedArtifactPaths = (html) => {
|
|
65
|
+
// Only `href`/`src` attribute values are treated as references, so artifact-shaped strings that
|
|
66
|
+
// happen to appear in embedded preview content are not mistaken for real dependencies.
|
|
67
|
+
const pattern = new RegExp(`(?:href|src)=["'][^"']*?${url_1.ARTIFACTS_ROUTE_SEGMENT}/([^"']+)["']`, 'g');
|
|
68
|
+
const paths = new Set();
|
|
69
|
+
let match;
|
|
70
|
+
while ((match = pattern.exec(html)) !== null) {
|
|
71
|
+
const encoded = match[1].split(/[?#]/)[0];
|
|
72
|
+
const segments = encoded.split('/').filter(Boolean);
|
|
73
|
+
try {
|
|
74
|
+
const decoded = segments.map((segment) => decodeURIComponent(segment));
|
|
75
|
+
if (decoded.length > 0) {
|
|
76
|
+
paths.add(decoded.join('/'));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (_a) {
|
|
80
|
+
// A reference that cannot be decoded is treated as missing below by failing to resolve.
|
|
81
|
+
paths.add(encoded);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return Array.from(paths);
|
|
85
|
+
};
|
|
86
|
+
/** Recursively collect every file under a directory as paths relative to it. */
|
|
87
|
+
const collectFilesRelative = (root) => {
|
|
88
|
+
const results = [];
|
|
89
|
+
const walk = (current) => {
|
|
90
|
+
for (const entry of fs_extra_1.default.readdirSync(current, { withFileTypes: true })) {
|
|
91
|
+
const absolute = path_1.default.join(current, entry.name);
|
|
92
|
+
if (entry.isDirectory()) {
|
|
93
|
+
walk(absolute);
|
|
94
|
+
}
|
|
95
|
+
else if (entry.isFile()) {
|
|
96
|
+
results.push(path_1.default.relative(root, absolute));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
if (fs_extra_1.default.existsSync(root)) {
|
|
101
|
+
walk(root);
|
|
102
|
+
}
|
|
103
|
+
return results;
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Validate that every artifact referenced by generated HTML can be materialized — i.e. the
|
|
107
|
+
* referenced file exists under the artifact root. Required references (e.g. a React preview's
|
|
108
|
+
* `component/<id>.client.js`) are always emitted, so a missing one fails the static build *before*
|
|
109
|
+
* export rather than producing a site with broken previews.
|
|
110
|
+
*
|
|
111
|
+
* @throws When any generated HTML references an artifact that is not present on disk.
|
|
112
|
+
*/
|
|
113
|
+
const validateReferencedArtifacts = (handoff) => {
|
|
114
|
+
const artifactRoot = getStaticArtifactRoot(handoff);
|
|
115
|
+
if (!fs_extra_1.default.existsSync(artifactRoot)) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const htmlFiles = collectFilesRelative(artifactRoot).filter((relative) => relative.toLowerCase().endsWith('.html'));
|
|
119
|
+
const missing = new Map();
|
|
120
|
+
for (const htmlRelative of htmlFiles) {
|
|
121
|
+
const html = fs_extra_1.default.readFileSync(path_1.default.resolve(artifactRoot, htmlRelative), 'utf8');
|
|
122
|
+
for (const referenced of extractReferencedArtifactPaths(html)) {
|
|
123
|
+
// Only required references gate the build; absent optional artifacts (component-owned
|
|
124
|
+
// styles/scripts, shared/global artifacts) are tolerated.
|
|
125
|
+
if (!isRequiredReference(referenced)) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const target = path_1.default.resolve(artifactRoot, ...referenced.split('/'));
|
|
129
|
+
const contained = !path_1.default.relative(artifactRoot, target).startsWith('..');
|
|
130
|
+
if (!contained || !fs_extra_1.default.existsSync(target) || !fs_extra_1.default.statSync(target).isFile()) {
|
|
131
|
+
if (!missing.has(referenced)) {
|
|
132
|
+
missing.set(referenced, new Set());
|
|
133
|
+
}
|
|
134
|
+
missing.get(referenced).add(htmlRelative);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (missing.size > 0) {
|
|
139
|
+
const details = Array.from(missing.entries())
|
|
140
|
+
.map(([artifactPath, referrers]) => ` - ${artifactPath} (referenced by ${Array.from(referrers).join(', ')})`)
|
|
141
|
+
.join('\n');
|
|
142
|
+
throw new Error(`Static build aborted: ${missing.size} required artifact(s) referenced by generated HTML could not be materialized:\n${details}`);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
exports.validateReferencedArtifacts = validateReferencedArtifacts;
|
|
146
|
+
/**
|
|
147
|
+
* Materialize the docs read model into route-shaped static files under `outDir` (the Next export
|
|
148
|
+
* output). Produces the canonical artifact tree plus the list/detail metadata reads, matching the
|
|
149
|
+
* shapes the live docs read API returns so the exported site is byte-for-byte interchangeable with
|
|
150
|
+
* the dev server's responses.
|
|
151
|
+
*/
|
|
152
|
+
const materializeDocsReadModel = (handoff, outDir) => __awaiter(void 0, void 0, void 0, function* () {
|
|
153
|
+
const artifactRoot = getStaticArtifactRoot(handoff);
|
|
154
|
+
if (!fs_extra_1.default.existsSync(artifactRoot)) {
|
|
155
|
+
logger_1.Logger.warn('No generated artifacts found to materialize for the static build.');
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const docsRoot = path_1.default.join(outDir, 'api', 'docs');
|
|
159
|
+
const artifactsOut = path_1.default.join(docsRoot, 'artifacts');
|
|
160
|
+
// Canonical artifact route: every generated artifact, addressable at `/api/docs/artifacts/{path}`.
|
|
161
|
+
yield fs_extra_1.default.ensureDir(artifactsOut);
|
|
162
|
+
yield fs_extra_1.default.copy(artifactRoot, artifactsOut, { overwrite: true });
|
|
163
|
+
const components = readJsonArray(path_1.default.resolve(artifactRoot, 'components.json'));
|
|
164
|
+
const patterns = readJsonArray(path_1.default.resolve(artifactRoot, 'patterns.json'));
|
|
165
|
+
yield fs_extra_1.default.ensureDir(docsRoot);
|
|
166
|
+
yield fs_extra_1.default.writeJson(path_1.default.join(docsRoot, 'components.json'), components);
|
|
167
|
+
yield fs_extra_1.default.writeJson(path_1.default.join(docsRoot, 'patterns.json'), patterns);
|
|
168
|
+
// Detail metadata reads, each carrying the artifact-derived build state.
|
|
169
|
+
yield fs_extra_1.default.ensureDir(path_1.default.join(docsRoot, 'components'));
|
|
170
|
+
for (const component of components) {
|
|
171
|
+
const detail = Object.assign(Object.assign({}, component), { build: { status: deriveBuildStatus(artifactRoot, path_1.default.join('component', `${component.id}.json`)) } });
|
|
172
|
+
yield fs_extra_1.default.writeJson(path_1.default.join(docsRoot, 'components', `${component.id}.json`), detail);
|
|
173
|
+
}
|
|
174
|
+
yield fs_extra_1.default.ensureDir(path_1.default.join(docsRoot, 'patterns'));
|
|
175
|
+
for (const pattern of patterns) {
|
|
176
|
+
const detail = Object.assign(Object.assign({}, pattern), { build: { status: deriveBuildStatus(artifactRoot, path_1.default.join('pattern', `${pattern.id}.json`)) } });
|
|
177
|
+
yield fs_extra_1.default.writeJson(path_1.default.join(docsRoot, 'patterns', `${pattern.id}.json`), detail);
|
|
178
|
+
}
|
|
179
|
+
yield materializeAssetDownloads(handoff, docsRoot);
|
|
180
|
+
logger_1.Logger.success(`Materialized docs read model (${components.length} component(s), ${patterns.length} pattern(s)) for static export.`);
|
|
181
|
+
});
|
|
182
|
+
exports.materializeDocsReadModel = materializeDocsReadModel;
|
|
183
|
+
/**
|
|
184
|
+
* Materialize the whole-collection asset download bundles at the canonical asset route
|
|
185
|
+
* (`api/docs/assets/{collection}/{collection}.zip`) — the same URL the docs read API serves in
|
|
186
|
+
* registry mode, so the "Download" links resolve identically on a static host. The bundles come from
|
|
187
|
+
* the fetch output (`exported/<id>/{collection}.zip`); `next build` never re-creates them, and the
|
|
188
|
+
* fetch-time copy into the app `public/` is wiped by the build's clean+restage, so they are copied
|
|
189
|
+
* here. Absent bundles (a project with no logos/icons) are skipped, not an error.
|
|
190
|
+
*/
|
|
191
|
+
const materializeAssetDownloads = (handoff, docsRoot) => __awaiter(void 0, void 0, void 0, function* () {
|
|
192
|
+
const bundles = [
|
|
193
|
+
{ collection: 'logos', src: handoff.getLogosZipFilePath() },
|
|
194
|
+
{ collection: 'icons', src: handoff.getIconsZipFilePath() },
|
|
195
|
+
];
|
|
196
|
+
let copied = 0;
|
|
197
|
+
for (const { collection, src } of bundles) {
|
|
198
|
+
if (fs_extra_1.default.existsSync(src) && fs_extra_1.default.statSync(src).isFile()) {
|
|
199
|
+
yield fs_extra_1.default.copy(src, path_1.default.join(docsRoot, 'assets', collection, `${collection}.zip`), { overwrite: true });
|
|
200
|
+
copied += 1;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (copied > 0) {
|
|
204
|
+
logger_1.Logger.info(`Materialized ${copied} asset download bundle(s) for static export.`);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import Handoff from '..';
|
|
2
|
+
/** Absolute path of the `.vercel/output` directory at the repo root. */
|
|
3
|
+
export declare const getVercelOutputPath: (handoff: Handoff) => string;
|
|
4
|
+
/**
|
|
5
|
+
* Lay a materialized static export under `.vercel/output/static/` and write a minimal Build Output
|
|
6
|
+
* API v3 `config.json` carrying the app's `trailingSlash` and resolved `basePath`. Pure CDN files —
|
|
7
|
+
* no functions, no database.
|
|
8
|
+
*
|
|
9
|
+
* @param exportDir Absolute path of the materialized static export (the same content the static
|
|
10
|
+
* target writes to `out/<projectId>`).
|
|
11
|
+
*/
|
|
12
|
+
export declare const writeStaticVercelOutput: (handoff: Handoff, exportDir: string) => Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Options for {@link writeRegistryVercelOutput}. The build module owns the standalone assembly (entry
|
|
15
|
+
* flattening + static/public/config layout) and passes it in, keeping this seam free of any
|
|
16
|
+
* standalone-layout knowledge and avoiding a circular import.
|
|
17
|
+
*/
|
|
18
|
+
export interface RegistryVercelOutputOptions {
|
|
19
|
+
/** Absolute path of the staged Next app (source of `.next/static` + `public`). */
|
|
20
|
+
appPath: string;
|
|
21
|
+
/** Absolute path of the Next `standalone` build root (`<appPath>/.next/standalone`). */
|
|
22
|
+
standaloneRoot: string;
|
|
23
|
+
/**
|
|
24
|
+
* Lay the standalone bundle out under the given entry dir (flatten the server entry, copy static
|
|
25
|
+
* assets, public tree, and runtime config beside it) and return the absolute server-entry path.
|
|
26
|
+
* Shared with the standalone deliverable so both layouts stay identical.
|
|
27
|
+
*/
|
|
28
|
+
assembleStandalone: (handoff: Handoff, appPath: string, standaloneRoot: string, entryDir: string) => Promise<string>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Package the dynamic registry app as the Vercel Build Output API directory. Lays the traced
|
|
32
|
+
* standalone bundle into a single Node function (`functions/index.func/`)
|
|
33
|
+
* wrapping the Next standalone server, copies the immutable static assets + public tree under
|
|
34
|
+
* `static/` for the Edge CDN, and writes a route table that serves static files first and routes
|
|
35
|
+
* everything else (SSR pages, the docs read API, the registry API) to the function.
|
|
36
|
+
*
|
|
37
|
+
* Hard constraint: `.vercel/output` lives at the repo root with that exact name (see module header).
|
|
38
|
+
*/
|
|
39
|
+
export declare const writeRegistryVercelOutput: (handoff: Handoff, options: RegistryVercelOutputOptions) => Promise<void>;
|