handoff-app 2.0.0-8 → 2.0.0
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 → CHANGELOG.md} +85 -1
- package/{License.md → LICENSE.md} +1 -1
- package/README.md +816 -393
- package/RECIPES.md +975 -0
- package/UPGRADE.md +57 -9
- package/components.json +20 -20
- package/config/docs/foundations/colors/guidelines.md +0 -1
- package/config/docs/foundations/colors.md +0 -1
- package/config/docs/foundations/effects/guidelines.md +0 -1
- package/config/docs/foundations/effects/resources.md +0 -1
- package/config/docs/foundations/effects.md +0 -1
- package/config/docs/foundations/fonts.md +0 -1
- package/config/docs/foundations/grid.md +0 -1
- package/config/docs/foundations/icon.md +0 -1
- package/config/docs/foundations/icons/guidelines.md +0 -1
- package/config/docs/foundations/icons.md +0 -1
- package/config/docs/foundations/images/guidelines.md +0 -1
- package/config/docs/foundations/images/resources.md +0 -1
- package/config/docs/foundations/images.md +0 -1
- package/config/docs/foundations/logo/resources.md +0 -1
- package/config/docs/foundations/logo.md +0 -1
- package/config/docs/foundations/typography/guidelines.md +0 -1
- package/config/docs/foundations/typography/resources.md +0 -1
- package/config/docs/foundations/typography.md +0 -1
- package/config/docs/foundations.md +0 -1
- package/config/docs/guidelines.md +0 -1
- package/config/docs/index.md +0 -1
- package/config/docs/system/tokens/foundations/colors.md +0 -4
- package/config/docs/system/tokens/foundations/effects.md +0 -4
- package/config/docs/system/tokens/foundations/typography.md +0 -2
- package/config/docs/system.md +0 -2
- package/config/templates/page.md +2 -1
- package/config/templates/page.mdx +2 -1
- package/dist/ai/connections.d.ts +66 -0
- package/dist/ai/connections.js +149 -0
- package/dist/ai/define.d.ts +10 -0
- package/dist/ai/define.js +13 -0
- package/dist/ai/types.d.ts +26 -0
- package/dist/ai/types.js +10 -0
- package/dist/app-builder/build.d.ts +14 -7
- package/dist/app-builder/build.js +195 -66
- package/dist/app-builder/client-config.js +3 -2
- package/dist/app-builder/paths.d.ts +0 -12
- package/dist/app-builder/paths.js +3 -20
- package/dist/app-builder/static-export.js +1 -1
- package/dist/app-builder/vercel-output.d.ts +1 -1
- package/dist/app-builder/vercel-output.js +5 -4
- package/dist/cache/build-cache.d.ts +27 -4
- package/dist/cache/build-cache.js +114 -47
- package/dist/cache/build-inputs.d.ts +13 -0
- package/dist/cache/build-inputs.js +19 -0
- package/dist/cache/index.d.ts +2 -1
- package/dist/cache/index.js +5 -1
- package/dist/catalog/normalize.js +3 -0
- package/dist/catalog/previews.js +5 -4
- package/dist/catalog/types.d.ts +8 -0
- package/dist/cli/create.js +1 -2
- package/dist/commands/build/index.d.ts +1 -1
- package/dist/commands/build/index.js +1 -1
- package/dist/commands/utils.d.ts +3 -3
- package/dist/config/defaults.js +7 -4
- package/dist/config/from-env.d.ts +4 -0
- package/dist/config/from-env.js +38 -1
- package/dist/config/helpers.js +2 -2
- package/dist/config/index.d.ts +0 -1
- package/dist/config/index.js +1 -4
- package/dist/config/loader.js +28 -2
- package/dist/config/resolve-env.d.ts +2 -1
- package/dist/config/resolve-env.js +28 -12
- package/dist/config/runtime.js +9 -11
- package/dist/handlebars.d.ts +10 -2
- package/dist/index.d.ts +12 -7
- package/dist/index.js +18 -22
- package/dist/nav/index.d.ts +2 -2
- package/dist/nav/registry.d.ts +3 -3
- package/dist/nav/registry.js +1 -1
- package/dist/nav/workspace.d.ts +3 -3
- package/dist/nav/workspace.js +1 -1
- package/dist/pipeline/figma.d.ts +13 -2
- package/dist/pipeline/figma.js +86 -46
- package/dist/pipeline/styles.js +1 -1
- package/dist/pipeline/validation.d.ts +1 -1
- package/dist/pipeline/validation.js +3 -3
- package/dist/providers.d.ts +10 -0
- package/dist/providers.js +13 -0
- package/dist/registry/asset-storage/define.d.ts +5 -5
- package/dist/registry/asset-storage/define.js +4 -4
- package/dist/registry/asset-storage/resolve.d.ts +9 -19
- package/dist/registry/asset-storage/resolve.js +33 -21
- package/dist/registry/asset-storage/types.d.ts +37 -17
- package/dist/registry/asset-storage/types.js +5 -5
- package/dist/registry/auth/ai-keys.d.ts +32 -0
- package/dist/registry/auth/ai-keys.js +69 -0
- package/dist/registry/auth/crypto.d.ts +17 -0
- package/dist/registry/auth/crypto.js +61 -1
- package/dist/registry/auth/index.d.ts +2 -1
- package/dist/registry/auth/index.js +11 -2
- package/dist/registry/checkout/index.d.ts +2 -1
- package/dist/registry/checkout/index.js +57 -127
- package/dist/registry/client.js +53 -17
- package/dist/registry/db/driver.js +3 -3
- package/dist/registry/db/schema.d.ts +261 -3
- package/dist/registry/db/schema.js +29 -4
- package/dist/registry/email/define.d.ts +10 -0
- package/dist/registry/email/define.js +13 -0
- package/dist/registry/email/providers/smtp.d.ts +10 -0
- package/dist/registry/email/providers/smtp.js +73 -0
- package/dist/registry/email/resolve.d.ts +28 -0
- package/dist/registry/email/resolve.js +74 -0
- package/dist/registry/email/types.d.ts +24 -0
- package/dist/registry/email/types.js +6 -0
- package/dist/registry/path.d.ts +2 -0
- package/dist/registry/path.js +11 -1
- package/dist/registry/publish/package.js +8 -0
- package/dist/registry/transfer.d.ts +4 -0
- package/dist/store/registry.d.ts +3 -3
- package/dist/store/registry.js +4 -4
- package/dist/transformers/plugins/ssr-render.js +40 -15
- package/dist/transformers/preview/component/builder.d.ts +1 -9
- package/dist/transformers/preview/component/builder.js +17 -17
- package/dist/transformers/preview/component/css.d.ts +3 -1
- package/dist/transformers/preview/component/css.js +92 -51
- package/dist/transformers/preview/component/html.d.ts +3 -1
- package/dist/transformers/preview/component/html.js +6 -2
- package/dist/transformers/preview/component/javascript.d.ts +2 -1
- package/dist/transformers/preview/component/javascript.js +10 -4
- package/dist/transformers/preview/pattern/html.js +3 -1
- package/dist/transformers/preview/types.d.ts +13 -0
- package/dist/transformers/utils/build.d.ts +24 -0
- package/dist/transformers/utils/build.js +100 -1
- package/dist/transformers/utils/module.js +2 -1
- package/dist/types/config.d.ts +135 -18
- package/dist/utils/path.d.ts +1 -5
- package/dist/utils/path.js +6 -23
- package/docs/api.md +6 -4
- package/docs/cli.md +275 -54
- package/drizzle/0007_add_ai_keys.sql +11 -0
- package/drizzle/0008_add_source_dir.sql +2 -0
- package/drizzle/meta/0007_snapshot.json +2013 -0
- package/drizzle/meta/0008_snapshot.json +2025 -0
- package/drizzle/meta/_journal.json +14 -0
- package/package.json +19 -8
- package/src/ai/README.md +42 -0
- package/src/ai/connections.ts +189 -0
- package/src/ai/define.ts +15 -0
- package/src/ai/types.ts +30 -0
- package/src/app/components/AiAssistant/AiAssistantPanel.tsx +144 -0
- package/src/app/components/AiAssistant/AiAssistantProvider.tsx +215 -0
- package/src/app/components/AiAssistant/AiConversation.tsx +737 -0
- package/src/app/components/AiAssistant/AiToolResult.tsx +99 -0
- package/src/app/components/AiAssistant/dockState.ts +86 -0
- package/src/app/components/AiAssistant/toolLabel.ts +24 -0
- package/src/app/components/AiAssistant/useAiChat.ts +147 -0
- package/src/app/components/AiAssistant/useAiConnections.ts +86 -0
- package/src/app/components/AiAssistant/usePageContext.ts +63 -0
- package/src/app/components/Auth/AccountLayout.tsx +58 -19
- package/src/app/components/Auth/AuthShell.tsx +27 -33
- package/src/app/components/Auth/RegistryInstallGate.tsx +4 -8
- package/src/app/components/Auth/installStatus.ts +29 -0
- package/src/app/components/Component/PageSliceResolver.tsx +1 -0
- package/src/app/components/Component/Preview.tsx +383 -189
- package/src/app/components/Component/slices/ComponentDisplaySlice.tsx +4 -0
- package/src/app/components/Foundations/ColorGrid.tsx +13 -14
- package/src/app/components/Layout/Header.tsx +21 -15
- package/src/app/components/Layout/Main.tsx +2 -2
- package/src/app/components/Markdown/MarkdownComponents.tsx +30 -32
- package/src/app/components/McpIntegration/McpConfigDialog.tsx +12 -51
- package/src/app/components/Navigation/MobileNav.tsx +2 -2
- package/src/app/components/Navigation/SideNav.tsx +1 -15
- package/src/app/components/context/NavProvider.tsx +7 -3
- package/src/app/components/ui/attachment.tsx +50 -0
- package/src/app/components/ui/bubble.tsx +57 -0
- package/src/app/components/ui/button.tsx +3 -1
- package/src/app/components/ui/confirm-dialog.tsx +87 -0
- package/src/app/components/ui/dialog.tsx +7 -2
- package/src/app/components/ui/marker.tsx +56 -0
- package/src/app/components/ui/sidebar.tsx +3 -1
- package/src/app/components/util/index.ts +3 -12
- package/src/app/css/index.css +64 -0
- package/src/app/lib/ai/auth.ts +33 -0
- package/src/app/lib/ai/context.ts +99 -0
- package/src/app/lib/ai/model.ts +79 -0
- package/src/app/lib/ai/prompt.ts +36 -0
- package/src/app/lib/ai/resolve.ts +97 -0
- package/src/app/lib/ai/tools.ts +120 -0
- package/src/app/lib/api/methods.ts +13 -0
- package/src/app/lib/asset-storage.ts +35 -36
- package/src/app/lib/auth/api.ts +4 -12
- package/src/app/lib/auth/email.ts +96 -23
- package/src/app/lib/docs-api/page-rendering.ts +1 -1
- package/src/app/lib/docs-api/registry.ts +2 -3
- package/src/app/lib/docs-api/runtime-config.ts +47 -21
- package/src/app/lib/mcp/components.ts +523 -0
- package/src/app/lib/mcp/page-url.ts +24 -0
- package/src/app/lib/mcp/pages.ts +84 -0
- package/src/app/lib/mcp/result.ts +33 -0
- package/src/app/lib/mcp/server.ts +39 -318
- package/src/app/lib/mcp/tokens.ts +334 -0
- package/src/app/lib/mcp/tool.ts +19 -0
- package/src/app/lib/registry-api/asset-transfer.ts +119 -11
- package/src/app/lib/registry-api/errors.ts +2 -0
- package/src/app/lib/registry-api/handler.ts +7 -1
- package/src/app/lib/registry-api/transfer.ts +28 -6
- package/src/app/lib/server-module.ts +34 -0
- package/src/app/lib/utils.ts +20 -0
- package/src/app/next.config.mjs +26 -24
- package/src/app/pages/[...slug]/index.tsx +2 -1
- package/src/app/pages/_app.tsx +6 -2
- package/src/app/pages/_document.tsx +6 -0
- package/src/app/pages/account/ai.tsx +183 -0
- package/src/app/pages/account/index.tsx +68 -68
- package/src/app/pages/account/tokens.tsx +37 -7
- package/src/app/pages/account/users.tsx +75 -15
- package/src/app/pages/api/account/ai/keys/[connectionId].ts +60 -0
- package/src/app/pages/api/account/ai/keys/index.ts +20 -0
- package/src/app/pages/api/admin/users/[id]/[action].ts +80 -11
- package/src/app/pages/api/admin/users/index.ts +2 -2
- package/src/app/pages/api/ai/chat.ts +115 -0
- package/src/app/pages/api/ai/connections.ts +23 -0
- package/src/app/pages/api/auth/request-reset.ts +14 -16
- package/src/app/pages/api/docs/nav.json.ts +1 -1
- package/src/app/pages/api/install.ts +3 -3
- package/src/app/pages/api/mcp.ts +6 -0
- package/src/app/pages/api/registry/transfer/assets/blobs/[hash]/complete.ts +11 -0
- package/src/app/pages/api/registry/transfer/assets/blobs/[hash]/upload.ts +12 -0
- package/src/app/pages/api/registry/transfer/assets/blobs/[hash].ts +1 -1
- package/src/app/pages/cli/device.tsx +10 -5
- package/src/app/pages/foundations/colors.tsx +9 -8
- package/src/app/pages/foundations/index.tsx +3 -3
- package/src/app/pages/index.tsx +2 -2
- package/src/app/pages/install.tsx +7 -2
- package/src/app/pages/login.tsx +32 -27
- package/src/app/pages/reset-password.tsx +21 -20
- package/src/app/pages/system/component/[component]/index.tsx +2 -5
- package/src/app/pages/system/index.tsx +11 -11
- package/src/app/pages/system/pattern/[pattern]/index.tsx +2 -2
- package/src/app/pages/system/tokens/components/[component]/index.tsx +0 -4
- package/src/app-builder/README.md +2 -1
- package/src/app-builder/build.ts +218 -73
- package/src/app-builder/client-config.ts +3 -2
- package/src/app-builder/paths.ts +3 -22
- package/src/app-builder/static-export.ts +1 -1
- package/src/app-builder/vercel-output.ts +5 -4
- package/src/cache/README.md +4 -1
- package/src/cache/build-cache.ts +128 -43
- package/src/cache/build-inputs.ts +28 -0
- package/src/cache/index.ts +4 -0
- package/src/catalog/README.md +3 -0
- package/src/catalog/normalize.ts +2 -0
- package/src/catalog/previews.ts +6 -3
- package/src/catalog/types.ts +8 -0
- package/src/cli/create.ts +1 -2
- package/src/commands/build/index.ts +1 -1
- package/src/config/README.md +10 -9
- package/src/config/defaults.ts +7 -4
- package/src/config/from-env.ts +33 -0
- package/src/config/helpers.ts +1 -3
- package/src/config/index.ts +0 -3
- package/src/config/loader.ts +27 -3
- package/src/config/resolve-env.ts +28 -15
- package/src/config/runtime.ts +10 -5
- package/src/handlebars.ts +8 -2
- package/src/index.ts +33 -29
- package/src/nav/index.ts +2 -2
- package/src/nav/registry.ts +3 -3
- package/src/nav/workspace.ts +3 -3
- package/src/pipeline/README.md +1 -1
- package/src/pipeline/figma.ts +98 -49
- package/src/pipeline/styles.ts +1 -1
- package/src/pipeline/validation.ts +3 -3
- package/src/providers.ts +18 -0
- package/src/registry/asset-storage/define.ts +6 -6
- package/src/registry/asset-storage/resolve.ts +38 -37
- package/src/registry/asset-storage/types.ts +39 -17
- package/src/registry/auth/ai-keys.ts +62 -0
- package/src/registry/auth/crypto.ts +63 -1
- package/src/registry/auth/index.ts +11 -1
- package/src/registry/checkout/index.ts +60 -138
- package/src/registry/client.ts +60 -20
- package/src/registry/db/driver.ts +2 -2
- package/src/registry/db/schema.ts +49 -22
- package/src/registry/email/define.ts +15 -0
- package/src/registry/email/providers/smtp.ts +39 -0
- package/src/registry/email/resolve.ts +94 -0
- package/src/registry/email/types.ts +28 -0
- package/src/registry/path.ts +11 -0
- package/src/registry/publish/package.ts +9 -0
- package/src/registry/transfer.ts +4 -0
- package/src/store/registry.ts +7 -7
- package/src/transformers/plugins/ssr-render.ts +53 -16
- package/src/transformers/preview/component/builder.ts +19 -25
- package/src/transformers/preview/component/css.ts +101 -53
- package/src/transformers/preview/component/html.ts +6 -2
- package/src/transformers/preview/component/javascript.ts +14 -4
- package/src/transformers/preview/pattern/html.ts +3 -1
- package/src/transformers/preview/types.ts +14 -0
- package/src/transformers/utils/build.ts +93 -0
- package/src/transformers/utils/module.ts +4 -2
- package/src/types/config.ts +148 -20
- package/src/utils/README.md +1 -1
- package/src/utils/path.ts +6 -29
- package/templates/default/handoff.config.js.tpl +10 -2
- package/templates/default/handoff.config.ts.tpl +10 -2
- package/templates/default/handoff.config.with-examples.js.tpl +10 -2
- package/templates/default/handoff.config.with-examples.ts.tpl +10 -2
- package/templates/default/package.json.tpl +2 -2
- package/.editorconfig +0 -19
- package/.eslintrc.json +0 -4
- package/.prettierignore +0 -0
- package/.prettierrc.json +0 -8
- package/.vscode/settings.json +0 -17
- package/dist/config/validator.d.ts +0 -9
- package/dist/config/validator.js +0 -24
- package/dist/registry/asset-storage/adapters/vercel-blob.d.ts +0 -17
- package/dist/registry/asset-storage/adapters/vercel-blob.js +0 -87
- package/src/app/components/old/Header.tsx +0 -89
- package/src/app/lib/mcp/shape.ts +0 -596
- package/src/app/pages/assets/fonts.tsx +0 -129
- package/src/app/pages/assets/index.tsx +0 -91
- package/src/app/pages/assets/logos.tsx +0 -75
- package/src/config/validator.ts +0 -22
- package/src/registry/asset-storage/adapters/vercel-blob.ts +0 -51
|
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.buildPreviews = void 0;
|
|
16
16
|
const plugin_react_1 = __importDefault(require("@vitejs/plugin-react"));
|
|
17
17
|
const vite_1 = require("vite");
|
|
18
|
+
const build_inputs_1 = require("../../../cache/build-inputs");
|
|
18
19
|
const logger_1 = require("../../../utils/logger");
|
|
19
20
|
const plugins_1 = require("../../plugins");
|
|
20
21
|
const vite_config_1 = __importDefault(require("../../vite-config"));
|
|
@@ -37,6 +38,7 @@ const RENDERER_PLUGINS = {
|
|
|
37
38
|
* @param data - The result of transforming component tokens.
|
|
38
39
|
* @param handoff - The Handoff configuration object.
|
|
39
40
|
* @param components - Optional file components object.
|
|
41
|
+
* @param inputs - Collects the files that the build reads, for the build cache.
|
|
40
42
|
* @returns A promise that resolves to the transformed component tokens result.
|
|
41
43
|
* @throws Will throw an error if the Vite build process fails.
|
|
42
44
|
*
|
|
@@ -45,7 +47,7 @@ const RENDERER_PLUGINS = {
|
|
|
45
47
|
* const result = await buildPreviews(transformedData, handoffConfig, fileComponents);
|
|
46
48
|
* ```
|
|
47
49
|
*/
|
|
48
|
-
const buildPreviews = (data, handoff, components) => __awaiter(void 0, void 0, void 0, function* () {
|
|
50
|
+
const buildPreviews = (data, handoff, components, inputs) => __awaiter(void 0, void 0, void 0, function* () {
|
|
49
51
|
var _a, _b, _c;
|
|
50
52
|
if (!((_a = data.entries) === null || _a === void 0 ? void 0 : _a.template))
|
|
51
53
|
return data;
|
|
@@ -75,9 +77,11 @@ const buildPreviews = (data, handoff, components) => __awaiter(void 0, void 0, v
|
|
|
75
77
|
if ((_c = (_b = handoff === null || handoff === void 0 ? void 0 : handoff.config) === null || _b === void 0 ? void 0 : _b.hooks) === null || _c === void 0 ? void 0 : _c.htmlBuildConfig) {
|
|
76
78
|
viteConfig = handoff.config.hooks.htmlBuildConfig(viteConfig);
|
|
77
79
|
}
|
|
78
|
-
yield (0, vite_1.build)(viteConfig);
|
|
80
|
+
yield (0, vite_1.build)((0, build_inputs_1.withBuildInputs)(viteConfig, inputs));
|
|
79
81
|
}
|
|
80
82
|
catch (error) {
|
|
83
|
+
if (inputs)
|
|
84
|
+
inputs.failed = true;
|
|
81
85
|
logger_1.Logger.error(`Error building component previews: ${data.entries.template}`, error);
|
|
82
86
|
}
|
|
83
87
|
finally {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BuildInputs } from '../../../cache/build-inputs';
|
|
1
2
|
import Handoff from '../../../index';
|
|
2
3
|
import { TransformComponentTokensResult } from '../types';
|
|
3
4
|
export declare const MAIN_COMPONENT_JS_FILE = "main.js";
|
|
@@ -10,7 +11,7 @@ export declare const MAIN_COMPONENT_JS_FILE = "main.js";
|
|
|
10
11
|
* @param handoff - The Handoff configuration object
|
|
11
12
|
* @returns The updated component transformation result with JavaScript data
|
|
12
13
|
*/
|
|
13
|
-
export declare const buildComponentJs: (data: TransformComponentTokensResult, handoff: Handoff) => Promise<TransformComponentTokensResult>;
|
|
14
|
+
export declare const buildComponentJs: (data: TransformComponentTokensResult, handoff: Handoff, inputs?: BuildInputs) => Promise<TransformComponentTokensResult>;
|
|
14
15
|
/**
|
|
15
16
|
* Builds the global preview script artifact (`component/main.js`) from the configured workspace JS
|
|
16
17
|
* entry.
|
|
@@ -16,6 +16,7 @@ exports.buildMainJS = exports.buildComponentJs = exports.MAIN_COMPONENT_JS_FILE
|
|
|
16
16
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
17
17
|
const path_1 = __importDefault(require("path"));
|
|
18
18
|
const vite_1 = require("vite");
|
|
19
|
+
const build_inputs_1 = require("../../../cache/build-inputs");
|
|
19
20
|
const duration_1 = require("../../../utils/duration");
|
|
20
21
|
const logger_1 = require("../../../utils/logger");
|
|
21
22
|
const vite_config_1 = __importDefault(require("../../vite-config"));
|
|
@@ -28,8 +29,9 @@ exports.MAIN_COMPONENT_JS_FILE = 'main.js';
|
|
|
28
29
|
* @param options.entry - The entry file path for the bundle
|
|
29
30
|
* @param options.outputPath - The directory where the bundle will be output
|
|
30
31
|
* @param options.outputFilename - The name of the output file
|
|
32
|
+
* @param inputs - Collects the files that the build reads, for the build cache
|
|
31
33
|
*/
|
|
32
|
-
const buildJsBundle = (_a, handoff_1) => __awaiter(void 0, [_a, handoff_1], void 0, function* ({ entry, outputPath, outputFilename, format = 'cjs', }, handoff) {
|
|
34
|
+
const buildJsBundle = (_a, handoff_1, inputs_1) => __awaiter(void 0, [_a, handoff_1, inputs_1], void 0, function* ({ entry, outputPath, outputFilename, format = 'cjs', }, handoff, inputs) {
|
|
33
35
|
var _b, _c, _d;
|
|
34
36
|
const absEntryPath = path_1.default.resolve(entry);
|
|
35
37
|
// Store the current NODE_ENV value before vite build
|
|
@@ -51,7 +53,7 @@ const buildJsBundle = (_a, handoff_1) => __awaiter(void 0, [_a, handoff_1], void
|
|
|
51
53
|
if ((_d = (_c = handoff === null || handoff === void 0 ? void 0 : handoff.config) === null || _c === void 0 ? void 0 : _c.hooks) === null || _d === void 0 ? void 0 : _d.jsBuildConfig) {
|
|
52
54
|
viteConfig = handoff.config.hooks.jsBuildConfig(viteConfig);
|
|
53
55
|
}
|
|
54
|
-
yield (0, vite_1.build)(viteConfig);
|
|
56
|
+
yield (0, vite_1.build)((0, build_inputs_1.withBuildInputs)(viteConfig, inputs));
|
|
55
57
|
}
|
|
56
58
|
finally {
|
|
57
59
|
// Restore the original NODE_ENV value after vite build completes
|
|
@@ -74,7 +76,7 @@ const buildJsBundle = (_a, handoff_1) => __awaiter(void 0, [_a, handoff_1], void
|
|
|
74
76
|
* @param handoff - The Handoff configuration object
|
|
75
77
|
* @returns The updated component transformation result with JavaScript data
|
|
76
78
|
*/
|
|
77
|
-
const buildComponentJs = (data, handoff) => __awaiter(void 0, void 0, void 0, function* () {
|
|
79
|
+
const buildComponentJs = (data, handoff, inputs) => __awaiter(void 0, void 0, void 0, function* () {
|
|
78
80
|
var _a;
|
|
79
81
|
const id = data.id;
|
|
80
82
|
const outputPath = (0, component_1.getComponentOutputPath)(handoff);
|
|
@@ -97,9 +99,11 @@ const buildComponentJs = (data, handoff) => __awaiter(void 0, void 0, void 0, fu
|
|
|
97
99
|
entry,
|
|
98
100
|
outputPath,
|
|
99
101
|
outputFilename: `${id}.js`,
|
|
100
|
-
}, handoff);
|
|
102
|
+
}, handoff, inputs);
|
|
101
103
|
}
|
|
102
104
|
catch (e) {
|
|
105
|
+
if (inputs)
|
|
106
|
+
inputs.failed = true;
|
|
103
107
|
logger_1.Logger.error(`Failed to bundle JS for component "${id}" (${id}.js):`, e);
|
|
104
108
|
return data;
|
|
105
109
|
}
|
|
@@ -113,6 +117,8 @@ const buildComponentJs = (data, handoff) => __awaiter(void 0, void 0, void 0, fu
|
|
|
113
117
|
}
|
|
114
118
|
}
|
|
115
119
|
catch (e) {
|
|
120
|
+
if (inputs)
|
|
121
|
+
inputs.failed = true;
|
|
116
122
|
logger_1.Logger.error(`JS build failed for component "${id}":`, e);
|
|
117
123
|
}
|
|
118
124
|
return data;
|
|
@@ -48,7 +48,9 @@ const composePatternHtml = (_patternId, patternTitle, fragments, basePath, share
|
|
|
48
48
|
cssHrefs.add((0, url_1.buildArtifactUrl)(`component/${componentId}.css`, basePath));
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
-
|
|
51
|
+
// React SSR separates adjacent text children with `<!-- -->`. The parser drops comments by
|
|
52
|
+
// default, which would make every composed React fragment fail to hydrate.
|
|
53
|
+
const doc = (0, node_html_parser_1.parse)(html, { comment: true });
|
|
52
54
|
const suffix = `_p${i}`;
|
|
53
55
|
const namespacedRootId = `root${suffix}`;
|
|
54
56
|
const namespacedPropsId = `__APP_PROPS__${suffix}`;
|
|
@@ -14,6 +14,8 @@ export type PageSliceType = 'BEST_PRACTICES' | 'COMPONENT_DISPLAY' | 'VALIDATION
|
|
|
14
14
|
export interface BasePageSlice {
|
|
15
15
|
type: PageSliceType;
|
|
16
16
|
}
|
|
17
|
+
/** A fluid preview renders at the width the reader's stage has, so it is never scaled down. */
|
|
18
|
+
export type PreviewWidth = 'fluid' | number;
|
|
17
19
|
export interface BestPracticesPageSlice extends BasePageSlice {
|
|
18
20
|
type: 'BEST_PRACTICES';
|
|
19
21
|
}
|
|
@@ -22,6 +24,8 @@ export interface ComponentDisplayPageSlice extends BasePageSlice {
|
|
|
22
24
|
showPreview?: boolean;
|
|
23
25
|
showCodeHighlight?: boolean;
|
|
24
26
|
defaultHeight?: string;
|
|
27
|
+
/** Viewport width for the preview, in pixels. Defaults to 'fluid'. */
|
|
28
|
+
defaultWidth?: PreviewWidth;
|
|
25
29
|
filterBy?: Filter;
|
|
26
30
|
}
|
|
27
31
|
export interface ValidationResultsPageSlice extends BasePageSlice {
|
|
@@ -100,6 +104,8 @@ export type ComponentObject = {
|
|
|
100
104
|
story?: string;
|
|
101
105
|
/** Optional path to schema file (if available) */
|
|
102
106
|
schema?: string;
|
|
107
|
+
/** Declaration file path; a preview `render` is read from it at build and hydration time. */
|
|
108
|
+
declaration?: string;
|
|
103
109
|
/** Optional path to templates directory */
|
|
104
110
|
templates?: string;
|
|
105
111
|
};
|
|
@@ -187,6 +193,8 @@ export type TransformComponentTokensResult = {
|
|
|
187
193
|
story?: string;
|
|
188
194
|
schema?: string;
|
|
189
195
|
templates?: string;
|
|
196
|
+
/** See `ComponentObject.entries.declaration`. */
|
|
197
|
+
declaration?: string;
|
|
190
198
|
};
|
|
191
199
|
renderer?: RendererKind;
|
|
192
200
|
/** See `ComponentObject.sourceFormat`. Distinct from `format`, the preview's code language. */
|
|
@@ -208,6 +216,11 @@ export type OptionalPreviewRender = {
|
|
|
208
216
|
};
|
|
209
217
|
url: string;
|
|
210
218
|
usage?: string;
|
|
219
|
+
/**
|
|
220
|
+
* Build-time only: the server renders it and the client re-reads it from the declaration module.
|
|
221
|
+
* `JSON.stringify` drops it from published artifacts.
|
|
222
|
+
*/
|
|
223
|
+
render?: (args: Record<string, any>) => unknown;
|
|
211
224
|
/** Optional source preview/story key used to derive this preview */
|
|
212
225
|
sourcePreview?: string;
|
|
213
226
|
};
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import esbuild from 'esbuild';
|
|
2
|
+
/**
|
|
3
|
+
* Keeps co-located stylesheet imports out of the JavaScript graph; the CSS pipeline compiles them.
|
|
4
|
+
*
|
|
5
|
+
* esbuild has no `.scss` loader, and a `.css` import fails too because these builds run with
|
|
6
|
+
* `write: false` and no output path. `empty` rather than a plugin: `esbuild.buildSync` rejects
|
|
7
|
+
* plugins, and the declaration and config loaders are synchronous.
|
|
8
|
+
*
|
|
9
|
+
* CSS Modules are not covered — the empty module leaves the class map undefined.
|
|
10
|
+
*/
|
|
11
|
+
export declare const STYLE_IMPORT_LOADERS: Record<string, esbuild.Loader>;
|
|
2
12
|
/**
|
|
3
13
|
* Default esbuild configuration for SSR builds
|
|
4
14
|
*/
|
|
@@ -7,6 +17,20 @@ export declare const DEFAULT_SSR_BUILD_CONFIG: esbuild.BuildOptions;
|
|
|
7
17
|
* Default esbuild configuration for client builds
|
|
8
18
|
*/
|
|
9
19
|
export declare const DEFAULT_CLIENT_BUILD_CONFIG: esbuild.BuildOptions;
|
|
20
|
+
/** Apply after a consumer hook: a hook that replaces `loader` would otherwise drop these. */
|
|
21
|
+
export declare function withStyleLoaders<T extends esbuild.BuildOptions>(config: T): T;
|
|
22
|
+
/**
|
|
23
|
+
* Stylesheet paths imported anywhere in a component's module graph, in cascade order.
|
|
24
|
+
*
|
|
25
|
+
* An unbuildable graph yields an empty list rather than throwing, leaving the CSS step with the
|
|
26
|
+
* declared entry.
|
|
27
|
+
*/
|
|
28
|
+
export declare function collectStyleImports(entryPath: string, handoff: any): Promise<string[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Every file in the module graph of an entry, stylesheets included, or `null` when the graph does
|
|
31
|
+
* not build.
|
|
32
|
+
*/
|
|
33
|
+
export declare function collectModuleInputs(entryPath: string, handoff: any): Promise<string[] | null>;
|
|
10
34
|
/**
|
|
11
35
|
* Resolves a module from multiple search directories
|
|
12
36
|
* @param id - Module ID to resolve
|
|
@@ -1,12 +1,36 @@
|
|
|
1
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
|
+
};
|
|
2
11
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
13
|
};
|
|
5
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.DEFAULT_CLIENT_BUILD_CONFIG = exports.DEFAULT_SSR_BUILD_CONFIG = void 0;
|
|
15
|
+
exports.DEFAULT_CLIENT_BUILD_CONFIG = exports.DEFAULT_SSR_BUILD_CONFIG = exports.STYLE_IMPORT_LOADERS = void 0;
|
|
16
|
+
exports.withStyleLoaders = withStyleLoaders;
|
|
17
|
+
exports.collectStyleImports = collectStyleImports;
|
|
18
|
+
exports.collectModuleInputs = collectModuleInputs;
|
|
7
19
|
exports.resolveModule = resolveModule;
|
|
8
20
|
exports.createReactResolvePlugin = createReactResolvePlugin;
|
|
21
|
+
const esbuild_1 = __importDefault(require("esbuild"));
|
|
9
22
|
const path_1 = __importDefault(require("path"));
|
|
23
|
+
const STYLE_EXTENSIONS = ['.css', '.scss', '.sass'];
|
|
24
|
+
/**
|
|
25
|
+
* Keeps co-located stylesheet imports out of the JavaScript graph; the CSS pipeline compiles them.
|
|
26
|
+
*
|
|
27
|
+
* esbuild has no `.scss` loader, and a `.css` import fails too because these builds run with
|
|
28
|
+
* `write: false` and no output path. `empty` rather than a plugin: `esbuild.buildSync` rejects
|
|
29
|
+
* plugins, and the declaration and config loaders are synchronous.
|
|
30
|
+
*
|
|
31
|
+
* CSS Modules are not covered — the empty module leaves the class map undefined.
|
|
32
|
+
*/
|
|
33
|
+
exports.STYLE_IMPORT_LOADERS = Object.fromEntries(STYLE_EXTENSIONS.map((extension) => [extension, 'empty']));
|
|
10
34
|
/**
|
|
11
35
|
* Default esbuild configuration for SSR builds
|
|
12
36
|
*/
|
|
@@ -17,6 +41,7 @@ exports.DEFAULT_SSR_BUILD_CONFIG = {
|
|
|
17
41
|
platform: 'node',
|
|
18
42
|
jsx: 'automatic',
|
|
19
43
|
external: ['react', 'react-dom', '@opentelemetry/api'],
|
|
44
|
+
loader: exports.STYLE_IMPORT_LOADERS,
|
|
20
45
|
};
|
|
21
46
|
/**
|
|
22
47
|
* Default esbuild configuration for client builds
|
|
@@ -29,7 +54,81 @@ exports.DEFAULT_CLIENT_BUILD_CONFIG = {
|
|
|
29
54
|
jsx: 'automatic',
|
|
30
55
|
sourcemap: false,
|
|
31
56
|
minify: false,
|
|
57
|
+
loader: exports.STYLE_IMPORT_LOADERS,
|
|
32
58
|
};
|
|
59
|
+
/** Apply after a consumer hook: a hook that replaces `loader` would otherwise drop these. */
|
|
60
|
+
function withStyleLoaders(config) {
|
|
61
|
+
return Object.assign(Object.assign({}, config), { loader: Object.assign(Object.assign({}, config.loader), exports.STYLE_IMPORT_LOADERS) });
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Stylesheets of the module graph in cascade order: every import of a module before the module
|
|
65
|
+
* itself, siblings in source order.
|
|
66
|
+
*
|
|
67
|
+
* This is the order a bundler emits CSS in, and the cascade depends on it. A component that
|
|
68
|
+
* overrides a rule of a component it imports ties that rule on specificity, so the later of the
|
|
69
|
+
* two wins. esbuild's `onLoad` callbacks do not give this order: they fire roughly breadth-first,
|
|
70
|
+
* which puts a component's own stylesheet ahead of the ones it imports and reverses every such
|
|
71
|
+
* override.
|
|
72
|
+
*/
|
|
73
|
+
function orderStyleImports(metafile, entryPath) {
|
|
74
|
+
const entryKey = Object.keys(metafile.inputs).find((key) => path_1.default.resolve(key) === entryPath);
|
|
75
|
+
if (!entryKey)
|
|
76
|
+
return [];
|
|
77
|
+
const ordered = [];
|
|
78
|
+
const visited = new Set();
|
|
79
|
+
const visit = (key) => {
|
|
80
|
+
var _a, _b;
|
|
81
|
+
if (visited.has(key))
|
|
82
|
+
return;
|
|
83
|
+
visited.add(key);
|
|
84
|
+
for (const imported of (_b = (_a = metafile.inputs[key]) === null || _a === void 0 ? void 0 : _a.imports) !== null && _b !== void 0 ? _b : []) {
|
|
85
|
+
if (!imported.external && metafile.inputs[imported.path])
|
|
86
|
+
visit(imported.path);
|
|
87
|
+
}
|
|
88
|
+
if (STYLE_EXTENSIONS.includes(path_1.default.extname(key)))
|
|
89
|
+
ordered.push(path_1.default.resolve(key));
|
|
90
|
+
};
|
|
91
|
+
visit(entryKey);
|
|
92
|
+
return ordered;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Stylesheet paths imported anywhere in a component's module graph, in cascade order.
|
|
96
|
+
*
|
|
97
|
+
* An unbuildable graph yields an empty list rather than throwing, leaving the CSS step with the
|
|
98
|
+
* declared entry.
|
|
99
|
+
*/
|
|
100
|
+
function collectStyleImports(entryPath, handoff) {
|
|
101
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
102
|
+
const metafile = yield buildModuleGraph(entryPath, handoff);
|
|
103
|
+
return metafile ? orderStyleImports(metafile, entryPath) : [];
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Every file in the module graph of an entry, stylesheets included, or `null` when the graph does
|
|
108
|
+
* not build.
|
|
109
|
+
*/
|
|
110
|
+
function collectModuleInputs(entryPath, handoff) {
|
|
111
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
112
|
+
const metafile = yield buildModuleGraph(entryPath, handoff);
|
|
113
|
+
return metafile ? Object.keys(metafile.inputs).map((input) => path_1.default.resolve(input)) : null;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function buildModuleGraph(entryPath, handoff) {
|
|
117
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
118
|
+
var _a, _b, _c;
|
|
119
|
+
const baseConfig = Object.assign(Object.assign({}, exports.DEFAULT_SSR_BUILD_CONFIG), { entryPoints: [entryPath], logLevel: 'silent' });
|
|
120
|
+
const hookedConfig = ((_b = (_a = handoff === null || handoff === void 0 ? void 0 : handoff.config) === null || _a === void 0 ? void 0 : _a.hooks) === null || _b === void 0 ? void 0 : _b.ssrBuildConfig) ? handoff.config.hooks.ssrBuildConfig(baseConfig) : baseConfig;
|
|
121
|
+
try {
|
|
122
|
+
// The `empty` loader keeps stylesheets out of the JavaScript, and the metafile still records
|
|
123
|
+
// them as inputs, so the graph carries the order.
|
|
124
|
+
const result = yield esbuild_1.default.build(Object.assign(Object.assign({}, withStyleLoaders(hookedConfig)), { metafile: true }));
|
|
125
|
+
return (_c = result.metafile) !== null && _c !== void 0 ? _c : null;
|
|
126
|
+
}
|
|
127
|
+
catch (_d) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
33
132
|
/**
|
|
34
133
|
* Resolves a module from multiple search directories
|
|
35
134
|
* @param id - Module ID to resolve
|
|
@@ -19,9 +19,10 @@ const build_1 = require("./build");
|
|
|
19
19
|
function createModuleBuildConfig(entryPath, handoff) {
|
|
20
20
|
var _a, _b;
|
|
21
21
|
const defaultBuildConfig = Object.assign(Object.assign({}, build_1.DEFAULT_SSR_BUILD_CONFIG), { entryPoints: [entryPath] });
|
|
22
|
-
|
|
22
|
+
const hookedBuildConfig = ((_b = (_a = handoff.config) === null || _a === void 0 ? void 0 : _a.hooks) === null || _b === void 0 ? void 0 : _b.ssrBuildConfig)
|
|
23
23
|
? handoff.config.hooks.ssrBuildConfig(defaultBuildConfig)
|
|
24
24
|
: defaultBuildConfig;
|
|
25
|
+
return (0, build_1.withStyleLoaders)(hookedBuildConfig);
|
|
25
26
|
}
|
|
26
27
|
function evaluateBuiltModule(code) {
|
|
27
28
|
const mod = { exports: {} };
|
package/dist/types/config.d.ts
CHANGED
|
@@ -115,6 +115,100 @@ export interface ConfigCatalog {
|
|
|
115
115
|
export type RuntimeMode = 'workspace' | 'registry';
|
|
116
116
|
/** Format used when synthesizing local workspace declarations. */
|
|
117
117
|
export type DeclarationFormat = 'ts' | 'js' | 'cjs';
|
|
118
|
+
/** How a connection's credential is supplied. Declared, never inferred from a missing key. */
|
|
119
|
+
export type AiCredentialKind = 'service' | 'user' | 'none';
|
|
120
|
+
/**
|
|
121
|
+
* An email option: a literal, baked into the build, or an environment reference, read by the deployed
|
|
122
|
+
* app at request time.
|
|
123
|
+
*/
|
|
124
|
+
export type HandoffEmailOption<T> = T | RuntimeEnvReference<T>;
|
|
125
|
+
interface HandoffEmailBase {
|
|
126
|
+
/** Sender address, such as `Handoff <no-reply@example.com>`. Use a profile for per-environment values. */
|
|
127
|
+
from?: string;
|
|
128
|
+
}
|
|
129
|
+
/** Send through an SMTP server, such as Amazon SES, SendGrid, Postmark, Mailgun or Microsoft 365. */
|
|
130
|
+
export interface HandoffSmtpEmail extends HandoffEmailBase {
|
|
131
|
+
provider: 'smtp';
|
|
132
|
+
options: {
|
|
133
|
+
host: HandoffEmailOption<string>;
|
|
134
|
+
/** @default 465, or 587 when `secure` is false */
|
|
135
|
+
port?: HandoffEmailOption<number>;
|
|
136
|
+
/**
|
|
137
|
+
* Use TLS from the start of the connection. When false, the connection upgrades with STARTTLS if
|
|
138
|
+
* the server offers it. @default true, or false when `port` is set to a port other than 465
|
|
139
|
+
*/
|
|
140
|
+
secure?: HandoffEmailOption<boolean>;
|
|
141
|
+
user?: HandoffEmailOption<string>;
|
|
142
|
+
/** Environment reference to the SMTP password. */
|
|
143
|
+
password?: RuntimeEnvReference<string>;
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/** Send through a server-only module that default-exports a `defineEmailProvider()` result. */
|
|
147
|
+
export interface HandoffCustomEmail extends HandoffEmailBase {
|
|
148
|
+
provider: 'custom';
|
|
149
|
+
/** Server-only module path. It must be its own file so the build can trace it into the deployed registry. */
|
|
150
|
+
module: string;
|
|
151
|
+
/**
|
|
152
|
+
* Options passed to the provider factory. Literals are baked. Environment references are resolved
|
|
153
|
+
* at request time. `apiKey` and `password` must be environment references.
|
|
154
|
+
*/
|
|
155
|
+
options?: Record<string, unknown>;
|
|
156
|
+
}
|
|
157
|
+
/** Registry email settings. `provider` selects the shape of `options`. */
|
|
158
|
+
export type HandoffEmailConfig = HandoffSmtpEmail | HandoffCustomEmail;
|
|
159
|
+
/**
|
|
160
|
+
* One AI connection the deployment declares. Connections are the whole surface: a reader can add a
|
|
161
|
+
* key for a declared connection and nothing else, so the server never calls a URL a reader chose.
|
|
162
|
+
*
|
|
163
|
+
* Every connection speaks the OpenAI-compatible `/chat/completions` API. A provider that fits
|
|
164
|
+
* nothing else names a server-only `module` instead of a `baseUrl`.
|
|
165
|
+
*/
|
|
166
|
+
export interface HandoffAiConnection {
|
|
167
|
+
/** Stable id. A model is addressed as `<id>/<model>`. */
|
|
168
|
+
id: string;
|
|
169
|
+
/** Name shown to a reader in the model picker and on the account page. */
|
|
170
|
+
label: string;
|
|
171
|
+
/** OpenAI-compatible base URL, such as `https://api.openai.com/v1`. Omit only when `module` is set. */
|
|
172
|
+
baseUrl?: string;
|
|
173
|
+
/**
|
|
174
|
+
* Server-only module path default-exporting a `defineAiProvider()` result, for a provider that is
|
|
175
|
+
* not OpenAI-compatible. It must be its own file so the build can trace it into the deployed
|
|
176
|
+
* registry.
|
|
177
|
+
*/
|
|
178
|
+
module?: string;
|
|
179
|
+
/**
|
|
180
|
+
* Environment reference to the deployment's own key. Its presence makes the connection a service
|
|
181
|
+
* key: the deployment pays and every reader can use it. The name is baked; the value is read at
|
|
182
|
+
* request time and never leaves the server.
|
|
183
|
+
*/
|
|
184
|
+
apiKey?: RuntimeEnvReference<string>;
|
|
185
|
+
/**
|
|
186
|
+
* Set to `'user'` when each reader supplies their own key. Without it, a connection that needs a
|
|
187
|
+
* reader's key looks like one that needs none, and the reader gets a raw 401 instead of an
|
|
188
|
+
* instruction.
|
|
189
|
+
*/
|
|
190
|
+
credential?: 'user';
|
|
191
|
+
/** The models this connection offers. A reader cannot use a model that is not listed. */
|
|
192
|
+
models?: string[];
|
|
193
|
+
/**
|
|
194
|
+
* Options passed to a `module` provider factory. Literals are baked. Environment references are
|
|
195
|
+
* resolved at request time, so secrets must be environment references.
|
|
196
|
+
*/
|
|
197
|
+
options?: Record<string, unknown>;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* The AI assistant block. Presence of the block opts the build in, and `enabled: false` turns it
|
|
201
|
+
* off. Like `mcp`, `enabled` is config-only and baked at build time, so a build without it leaves
|
|
202
|
+
* out both the chat route and the header control rather than showing a control that fails.
|
|
203
|
+
*/
|
|
204
|
+
export interface HandoffAiConfig {
|
|
205
|
+
/** @default true when the `ai` block is present */
|
|
206
|
+
enabled?: boolean;
|
|
207
|
+
/** Declared connections. Merged by `id` with any supplied through `HANDOFF_AI_CONNECTIONS`. */
|
|
208
|
+
connections?: HandoffAiConnection[];
|
|
209
|
+
/** The `<connectionId>/<model>` used when a reader has not picked one. */
|
|
210
|
+
defaultModel?: string;
|
|
211
|
+
}
|
|
118
212
|
/**
|
|
119
213
|
* User-facing `runtime` configuration block. A single optional block that selects the runtime
|
|
120
214
|
* mode and carries mode-specific host/connection settings.
|
|
@@ -135,6 +229,8 @@ export interface HandoffRuntimeConfig {
|
|
|
135
229
|
* @default true
|
|
136
230
|
*/
|
|
137
231
|
mcp?: boolean;
|
|
232
|
+
/** The docs AI assistant. Omit the block to leave it out of the build. See {@link HandoffAiConfig}. */
|
|
233
|
+
ai?: HandoffAiConfig;
|
|
138
234
|
/** Workspace-mode settings. */
|
|
139
235
|
workspace?: {
|
|
140
236
|
entries?: ConfigEntries;
|
|
@@ -142,9 +238,9 @@ export interface HandoffRuntimeConfig {
|
|
|
142
238
|
};
|
|
143
239
|
/** Registry-mode host settings. Env-var values are stored as names, never as secrets. */
|
|
144
240
|
registry?: {
|
|
145
|
-
/** Environment reference to the database URL. @default fromEnv('DATABASE_URL') */
|
|
146
|
-
databaseUrl?: RuntimeEnvReference<string>;
|
|
147
241
|
database?: {
|
|
242
|
+
/** Environment reference to the database URL. @default fromEnv('DATABASE_URL') */
|
|
243
|
+
url?: RuntimeEnvReference<string>;
|
|
148
244
|
/**
|
|
149
245
|
* PostgreSQL is the supported database. `driver` selects the built-in connection driver — how
|
|
150
246
|
* to connect, not which engine — over the same package-owned Postgres schema and migrations.
|
|
@@ -159,20 +255,26 @@ export interface HandoffRuntimeConfig {
|
|
|
159
255
|
*/
|
|
160
256
|
assetStorage?: {
|
|
161
257
|
/**
|
|
162
|
-
* Active storage provider for new uploads. `database` keeps bytes inline in Postgres; use
|
|
163
|
-
*
|
|
258
|
+
* Active storage provider for new uploads. `database` keeps bytes inline in Postgres; use a
|
|
259
|
+
* `custom` module for object storage.
|
|
164
260
|
* @default "database"
|
|
165
261
|
*/
|
|
166
|
-
|
|
167
|
-
/** For `
|
|
262
|
+
provider?: 'database' | 'custom';
|
|
263
|
+
/** For `provider: "custom"` - server-only module path default-exporting a `defineStorageProvider()` result. */
|
|
168
264
|
module?: string;
|
|
169
|
-
/** For `adapter: "vercel-blob"` - environment reference to the Blob read/write token. @default fromEnv('BLOB_READ_WRITE_TOKEN') */
|
|
170
|
-
token?: RuntimeEnvReference<string>;
|
|
171
265
|
/** Max bytes kept inline in the database `bytea` column (larger uploads are rejected). @default 4194304 */
|
|
172
266
|
maxInlineBytes?: number;
|
|
173
|
-
/**
|
|
267
|
+
/**
|
|
268
|
+
* Options passed to the provider factory. Literals are baked. Environment references are resolved
|
|
269
|
+
* at request time, so secrets must be environment references.
|
|
270
|
+
*/
|
|
174
271
|
options?: Record<string, unknown>;
|
|
175
272
|
};
|
|
273
|
+
/**
|
|
274
|
+
* Invitation and password-reset email. Email stays off until `from` is set and the required
|
|
275
|
+
* options of the provider have values. Until then, invitation links are shown to an administrator.
|
|
276
|
+
*/
|
|
277
|
+
email?: HandoffEmailConfig;
|
|
176
278
|
};
|
|
177
279
|
/**
|
|
178
280
|
* Connected-workspace settings pointing at a remote registry. A connected workspace is
|
|
@@ -185,11 +287,18 @@ export interface HandoffRuntimeConfig {
|
|
|
185
287
|
accessToken?: EnvSecret<string>;
|
|
186
288
|
};
|
|
187
289
|
}
|
|
290
|
+
/** Connections to external services. */
|
|
291
|
+
export interface ConfigIntegrations {
|
|
292
|
+
/** The Figma file that `fetch` reads foundations from. In a terminal, `fetch` asks for an empty value. */
|
|
293
|
+
figma?: {
|
|
294
|
+
/** The Figma file ID. @default fromEnv('HANDOFF_FIGMA_PROJECT_ID', { default: null }) */
|
|
295
|
+
projectId?: EnvValue<string | null>;
|
|
296
|
+
/** A Figma personal access token. Accepts only an environment reference. @default fromEnv('HANDOFF_DEV_ACCESS_TOKEN', { default: null }) */
|
|
297
|
+
accessToken?: EnvSecret<string | null>;
|
|
298
|
+
};
|
|
299
|
+
}
|
|
188
300
|
export interface Config {
|
|
189
|
-
|
|
190
|
-
devAccessToken?: EnvSecret<string | null>;
|
|
191
|
-
figma_project_id?: EnvValue<string | null>;
|
|
192
|
-
figmaProjectId?: EnvValue<string | null>;
|
|
301
|
+
integrations?: ConfigIntegrations;
|
|
193
302
|
exportsOutputDirectory?: EnvValue<string>;
|
|
194
303
|
sitesOutputDirectory?: EnvValue<string>;
|
|
195
304
|
useVariables?: EnvValue<boolean>;
|
|
@@ -273,6 +382,9 @@ export interface Config {
|
|
|
273
382
|
validateComponent?: (component: TransformComponentTokensResult) => Promise<Record<string, ValidationResult>>;
|
|
274
383
|
/**
|
|
275
384
|
* Optional hook to override the SSR build configuration used in the ssrRenderPlugin
|
|
385
|
+
*
|
|
386
|
+
* Stylesheet loaders are re-applied after this hook, so a `.css`/`.scss`/`.sass` import stays out
|
|
387
|
+
* of the JavaScript graph whatever the hook returns.
|
|
276
388
|
* @param config - The default esbuild configuration
|
|
277
389
|
* @returns Modified esbuild configuration
|
|
278
390
|
* @example
|
|
@@ -286,6 +398,9 @@ export interface Config {
|
|
|
286
398
|
ssrBuildConfig?: (config: BuildOptions) => BuildOptions;
|
|
287
399
|
/**
|
|
288
400
|
* Optional hook to override the client-side build configuration used in the ssrRenderPlugin
|
|
401
|
+
*
|
|
402
|
+
* Stylesheet loaders are re-applied after this hook, so a `.css`/`.scss`/`.sass` import stays out
|
|
403
|
+
* of the JavaScript graph whatever the hook returns.
|
|
289
404
|
* @param config - The default esbuild configuration
|
|
290
405
|
* @returns Modified esbuild configuration
|
|
291
406
|
* @example
|
|
@@ -385,11 +500,13 @@ export interface ClientRuntimeConfig {
|
|
|
385
500
|
connected: boolean;
|
|
386
501
|
}
|
|
387
502
|
/** Values available after config loading. Deferred references retain their names. */
|
|
388
|
-
export type ResolvedConfig = Omit<Config, '
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
503
|
+
export type ResolvedConfig = Omit<Config, 'integrations' | 'exportsOutputDirectory' | 'sitesOutputDirectory' | 'useVariables' | 'app' | 'runtime'> & {
|
|
504
|
+
integrations?: {
|
|
505
|
+
figma?: {
|
|
506
|
+
projectId?: string | null;
|
|
507
|
+
accessToken?: string | null;
|
|
508
|
+
};
|
|
509
|
+
};
|
|
393
510
|
exportsOutputDirectory?: string;
|
|
394
511
|
sitesOutputDirectory?: string;
|
|
395
512
|
useVariables?: boolean;
|
package/dist/utils/path.d.ts
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generates a filesystem-safe directory name from an absolute path
|
|
3
|
-
* @param workingPath - The absolute working path to transform
|
|
4
|
-
* @returns A filesystem-safe string suitable for use as a directory name
|
|
5
|
-
*/
|
|
1
|
+
/** `<base name>-<path hash>`; the hash keeps directories with the same base name apart. */
|
|
6
2
|
export declare function generateFilesystemSafeId(workingPath: string): string;
|
|
7
3
|
/**
|
|
8
4
|
* Normalize a filesystem path for cross-platform string comparison.
|
package/dist/utils/path.js
CHANGED
|
@@ -8,31 +8,14 @@ exports.normalizePathForCompare = normalizePathForCompare;
|
|
|
8
8
|
exports.arePathsEqual = arePathsEqual;
|
|
9
9
|
exports.resolveWorkingPath = resolveWorkingPath;
|
|
10
10
|
exports.isPathInside = isPathInside;
|
|
11
|
+
const crypto_1 = require("crypto");
|
|
11
12
|
const path_1 = __importDefault(require("path"));
|
|
12
|
-
/**
|
|
13
|
-
* Generates a filesystem-safe directory name from an absolute path
|
|
14
|
-
* @param workingPath - The absolute working path to transform
|
|
15
|
-
* @returns A filesystem-safe string suitable for use as a directory name
|
|
16
|
-
*/
|
|
13
|
+
/** `<base name>-<path hash>`; the hash keeps directories with the same base name apart. */
|
|
17
14
|
function generateFilesystemSafeId(workingPath) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// Remove or replace invalid filesystem characters
|
|
23
|
-
safeId = safeId.replace(/[<>:"|?*\x00]/g, '-');
|
|
24
|
-
// Remove leading/trailing dashes and spaces
|
|
25
|
-
safeId = safeId.replace(/^[\s-]+|[\s-]+$/g, '');
|
|
26
|
-
// Handle Windows reserved names (case-insensitive)
|
|
27
|
-
const reservedNames = /^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(\.|$)/i;
|
|
28
|
-
if (reservedNames.test(safeId)) {
|
|
29
|
-
safeId = `project-${safeId}`;
|
|
30
|
-
}
|
|
31
|
-
// Ensure we have at least one character
|
|
32
|
-
if (!safeId) {
|
|
33
|
-
safeId = 'default-project';
|
|
34
|
-
}
|
|
35
|
-
return safeId;
|
|
15
|
+
const baseName = path_1.default.basename(path_1.default.resolve(workingPath));
|
|
16
|
+
const name = baseName.replace(/[^A-Za-z0-9._-]/g, '-').replace(/^[.-]+|[.-]+$/g, '') || 'project';
|
|
17
|
+
const hash = (0, crypto_1.createHash)('sha256').update(normalizePathForCompare(workingPath)).digest('hex').slice(0, 8);
|
|
18
|
+
return `${name}-${hash}`;
|
|
36
19
|
}
|
|
37
20
|
/**
|
|
38
21
|
* Normalize a filesystem path for cross-platform string comparison.
|
package/docs/api.md
CHANGED
|
@@ -80,10 +80,12 @@ class constructor
|
|
|
80
80
|
handoff.fetch();
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
Fetch
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
Fetch reads the Figma file ID and access token from `integrations.figma`. By
|
|
84
|
+
default, they come from `HANDOFF_FIGMA_PROJECT_ID` and
|
|
85
|
+
`HANDOFF_DEV_ACCESS_TOKEN`. In a terminal, fetch asks for an empty value.
|
|
86
|
+
Without a terminal, it throws `HandoffConfigError`. Then it exports all of the
|
|
87
|
+
tokens and generated data into an `exported` directory in the local working
|
|
88
|
+
root.
|
|
87
89
|
|
|
88
90
|
### build
|
|
89
91
|
|