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
package/dist/pipeline/figma.js
CHANGED
|
@@ -35,29 +35,66 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
35
35
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
36
|
};
|
|
37
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
-
exports.figmaExtract = exports.validateFigmaAuth = void 0;
|
|
38
|
+
exports.figmaExtract = exports.validateFigmaAuth = exports.resolveFigmaConnection = void 0;
|
|
39
39
|
const p = __importStar(require("@clack/prompts"));
|
|
40
40
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
41
41
|
const stream = __importStar(require("node:stream"));
|
|
42
42
|
const path_1 = __importDefault(require("path"));
|
|
43
|
+
const errors_1 = require("../config/errors");
|
|
44
|
+
const resolve_env_1 = require("../config/resolve-env");
|
|
43
45
|
const logger_1 = require("../utils/logger");
|
|
44
46
|
const archive_1 = require("./archive");
|
|
45
47
|
const documentation_1 = require("./documentation");
|
|
48
|
+
const resolveFigmaConnection = (config) => {
|
|
49
|
+
var _a, _b, _c;
|
|
50
|
+
const figma = (_a = config === null || config === void 0 ? void 0 : config.integrations) === null || _a === void 0 ? void 0 : _a.figma;
|
|
51
|
+
return {
|
|
52
|
+
projectId: ((_b = figma === null || figma === void 0 ? void 0 : figma.projectId) === null || _b === void 0 ? void 0 : _b.trim()) || '',
|
|
53
|
+
accessToken: ((_c = figma === null || figma === void 0 ? void 0 : figma.accessToken) === null || _c === void 0 ? void 0 : _c.trim()) || '',
|
|
54
|
+
projectIdEnv: (0, resolve_env_1.configEnvName)(config, 'integrations.figma.projectId'),
|
|
55
|
+
accessTokenEnv: (0, resolve_env_1.configEnvName)(config, 'integrations.figma.accessToken'),
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
exports.resolveFigmaConnection = resolveFigmaConnection;
|
|
59
|
+
const describeSource = (envName, configPath) => envName ? `${envName} (${configPath})` : configPath;
|
|
60
|
+
/** Replaces each variable that the file already sets and appends the others. */
|
|
61
|
+
const saveEnvValues = (filePath, values) => __awaiter(void 0, void 0, void 0, function* () {
|
|
62
|
+
const lines = fs_extra_1.default.existsSync(filePath) ? (yield fs_extra_1.default.readFile(filePath, 'utf8')).split(/\r?\n/) : [];
|
|
63
|
+
while (lines.length && !lines[lines.length - 1].trim())
|
|
64
|
+
lines.pop();
|
|
65
|
+
for (const [name, value] of Object.entries(values)) {
|
|
66
|
+
const line = `${name}="${value}"`;
|
|
67
|
+
const index = lines.findIndex((existing) => new RegExp(`^\\s*(export\\s+)?${name}\\s*=`).test(existing));
|
|
68
|
+
if (index === -1)
|
|
69
|
+
lines.push(line);
|
|
70
|
+
else
|
|
71
|
+
lines[index] = line;
|
|
72
|
+
}
|
|
73
|
+
yield fs_extra_1.default.writeFile(filePath, `${lines.join('\n')}\n`);
|
|
74
|
+
});
|
|
46
75
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
76
|
+
* Makes sure that `fetch` has a Figma file ID and access token. In a terminal, it asks for an empty
|
|
77
|
+
* value and can save it to the env file of the selected profile. Without a terminal, it throws
|
|
78
|
+
* `HandoffConfigError`.
|
|
49
79
|
*/
|
|
50
80
|
const validateFigmaAuth = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
81
|
+
var _a;
|
|
82
|
+
const connection = (0, exports.resolveFigmaConnection)(handoff.config);
|
|
83
|
+
let { projectId, accessToken } = connection;
|
|
84
|
+
if (projectId && accessToken) {
|
|
54
85
|
return;
|
|
55
86
|
}
|
|
56
|
-
|
|
57
|
-
if (!
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
87
|
+
const profile = handoff.getProfile();
|
|
88
|
+
if (!process.stdin.isTTY) {
|
|
89
|
+
const missing = [
|
|
90
|
+
!accessToken && describeSource(connection.accessTokenEnv, 'integrations.figma.accessToken'),
|
|
91
|
+
!projectId && describeSource(connection.projectIdEnv, 'integrations.figma.projectId'),
|
|
92
|
+
].filter(Boolean);
|
|
93
|
+
throw new errors_1.HandoffConfigError(`Figma is not configured (profile "${profile !== null && profile !== void 0 ? profile : 'default'}"). Set ${missing.join(' and ')}.`);
|
|
94
|
+
}
|
|
95
|
+
if (!accessToken) {
|
|
96
|
+
p.log.warn(`Figma access token not found. Set ${describeSource(connection.accessTokenEnv, 'integrations.figma.accessToken')}.\n` +
|
|
97
|
+
`Use these instructions to generate one: https://help.figma.com/hc/en-us/articles/8085703771159-Manage-personal-access-tokens`);
|
|
61
98
|
const token = yield p.password({
|
|
62
99
|
message: 'Figma Developer Key:',
|
|
63
100
|
});
|
|
@@ -65,61 +102,50 @@ const validateFigmaAuth = (handoff) => __awaiter(void 0, void 0, void 0, functio
|
|
|
65
102
|
p.cancel('Authentication cancelled.');
|
|
66
103
|
process.exit(0);
|
|
67
104
|
}
|
|
68
|
-
|
|
105
|
+
accessToken = token.trim();
|
|
69
106
|
}
|
|
70
|
-
if (!
|
|
71
|
-
|
|
72
|
-
p.log.warn(`Figma project ID not found. Provide HANDOFF_FIGMA_PROJECT_ID via environment variable or .env file.\n` +
|
|
107
|
+
if (!projectId) {
|
|
108
|
+
p.log.warn(`Figma project ID not found. Set ${describeSource(connection.projectIdEnv, 'integrations.figma.projectId')}.\n` +
|
|
73
109
|
`Find it in your Figma file URL (e.g., figma.com/file/{PROJECT_ID}/...).`);
|
|
74
|
-
const
|
|
110
|
+
const value = yield p.text({
|
|
75
111
|
message: 'Figma Project Id:',
|
|
76
112
|
validate: (value) => {
|
|
77
113
|
if (!value.trim())
|
|
78
114
|
return 'Project ID is required';
|
|
79
115
|
},
|
|
80
116
|
});
|
|
81
|
-
if (p.isCancel(
|
|
117
|
+
if (p.isCancel(value)) {
|
|
82
118
|
p.cancel('Authentication cancelled.');
|
|
83
119
|
process.exit(0);
|
|
84
120
|
}
|
|
85
|
-
|
|
121
|
+
projectId = value.trim();
|
|
86
122
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
123
|
+
const values = {};
|
|
124
|
+
if (!connection.accessToken && connection.accessTokenEnv)
|
|
125
|
+
values[connection.accessTokenEnv] = accessToken;
|
|
126
|
+
if (!connection.projectId && connection.projectIdEnv)
|
|
127
|
+
values[connection.projectIdEnv] = projectId;
|
|
128
|
+
const envFileName = profile ? `.env.${profile}` : '.env';
|
|
129
|
+
if (Object.keys(values).length) {
|
|
130
|
+
const save = yield p.confirm({
|
|
131
|
+
message: `Save ${Object.keys(values).join(' and ')} to ${envFileName} for future runs?`,
|
|
91
132
|
initialValue: true,
|
|
92
133
|
});
|
|
93
|
-
if (p.isCancel(
|
|
94
|
-
p.log.info(`Skipped .
|
|
134
|
+
if (p.isCancel(save) || save === false) {
|
|
135
|
+
p.log.info(`Skipped saving. Set these variables before the next run.`);
|
|
95
136
|
}
|
|
96
137
|
else {
|
|
97
|
-
const envFilePath = path_1.default.resolve(handoff.workingPath, '.env');
|
|
98
|
-
const envFileContent = `
|
|
99
|
-
HANDOFF_DEV_ACCESS_TOKEN="${DEV_ACCESS_TOKEN}"
|
|
100
|
-
HANDOFF_FIGMA_PROJECT_ID="${FIGMA_PROJECT_ID}"
|
|
101
|
-
`;
|
|
102
138
|
try {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
.catch(() => false);
|
|
107
|
-
if (fileExists) {
|
|
108
|
-
yield fs_extra_1.default.appendFile(envFilePath, envFileContent);
|
|
109
|
-
p.log.success(`The .env file was found and updated with new content. Since these are sensitive variables, please do not commit this file.`);
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
yield fs_extra_1.default.writeFile(envFilePath, envFileContent.replace(/^\s*[\r\n]/gm, ''));
|
|
113
|
-
p.log.success(`Created .env file. Do not commit sensitive variables.`);
|
|
114
|
-
}
|
|
139
|
+
yield saveEnvValues(path_1.default.resolve(handoff.workingPath, envFileName), values);
|
|
140
|
+
const secretNote = connection.accessTokenEnv && values[connection.accessTokenEnv] ? ' It contains a secret, so do not commit it.' : '';
|
|
141
|
+
p.log.success(`Saved to ${envFileName}.${secretNote}`);
|
|
115
142
|
}
|
|
116
143
|
catch (error) {
|
|
117
|
-
logger_1.Logger.error(
|
|
144
|
+
logger_1.Logger.error(`Could not save ${envFileName}:`, error);
|
|
118
145
|
}
|
|
119
146
|
}
|
|
120
147
|
}
|
|
121
|
-
handoff.config.
|
|
122
|
-
handoff.config.figma_project_id = FIGMA_PROJECT_ID;
|
|
148
|
+
handoff.config.integrations = Object.assign(Object.assign({}, handoff.config.integrations), { figma: Object.assign(Object.assign({}, (_a = handoff.config.integrations) === null || _a === void 0 ? void 0 : _a.figma), { projectId, accessToken }) });
|
|
123
149
|
});
|
|
124
150
|
exports.validateFigmaAuth = validateFigmaAuth;
|
|
125
151
|
/**
|
|
@@ -127,17 +153,31 @@ exports.validateFigmaAuth = validateFigmaAuth;
|
|
|
127
153
|
*/
|
|
128
154
|
const figmaExtract = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
|
|
129
155
|
logger_1.Logger.success(`Starting Figma data extraction.`);
|
|
130
|
-
|
|
156
|
+
// The export directory can hold other files, so remove only what fetch writes.
|
|
157
|
+
const outputPath = handoff.getOutputPath();
|
|
158
|
+
yield Promise.all([
|
|
159
|
+
handoff.getTokensFilePath(),
|
|
160
|
+
handoff.getVariablesFilePath(),
|
|
161
|
+
handoff.getIconsZipFilePath(),
|
|
162
|
+
handoff.getLogosZipFilePath(),
|
|
163
|
+
path_1.default.join(outputPath, 'tokens-map.json'),
|
|
164
|
+
path_1.default.join(outputPath, 'fonts'),
|
|
165
|
+
].map((entry) => fs_extra_1.default.remove(entry)));
|
|
166
|
+
yield fs_extra_1.default.ensureDir(outputPath);
|
|
131
167
|
const documentationObject = yield (0, documentation_1.createDocumentationObject)(handoff);
|
|
168
|
+
const createZipFiles = process.env.HANDOFF_CREATE_ASSETS_ZIP_FILES !== 'false';
|
|
132
169
|
yield Promise.all([
|
|
133
170
|
fs_extra_1.default.writeJSON(handoff.getTokensFilePath(), documentationObject, { spaces: 2 }),
|
|
134
|
-
...(
|
|
171
|
+
...(createZipFiles
|
|
135
172
|
? [
|
|
136
173
|
(0, archive_1.zipAssets)(documentationObject.assets.icons, fs_extra_1.default.createWriteStream(handoff.getIconsZipFilePath())).then((writeStream) => stream.promises.finished(writeStream)),
|
|
137
174
|
(0, archive_1.zipAssets)(documentationObject.assets.logos, fs_extra_1.default.createWriteStream(handoff.getLogosZipFilePath())).then((writeStream) => stream.promises.finished(writeStream)),
|
|
138
175
|
]
|
|
139
176
|
: []),
|
|
140
177
|
]);
|
|
178
|
+
if (!createZipFiles) {
|
|
179
|
+
return documentationObject;
|
|
180
|
+
}
|
|
141
181
|
// define the output folder
|
|
142
182
|
const outputFolder = path_1.default.resolve(handoff.modulePath, '.handoff', `${handoff.getProjectId()}`, 'public');
|
|
143
183
|
// ensure output folder exists
|
package/dist/pipeline/styles.js
CHANGED
|
@@ -37,7 +37,7 @@ const buildCustomFonts = (handoff, documentationObject) => __awaiter(void 0, voi
|
|
|
37
37
|
if (fs_extra_1.default.existsSync(fontDirName)) {
|
|
38
38
|
const stream = fs_extra_1.default.createWriteStream(path_1.default.join(fontLocation, `${name}.zip`));
|
|
39
39
|
yield (0, archive_1.zip)(fontDirName, stream);
|
|
40
|
-
const fontsFolder = path_1.default.
|
|
40
|
+
const fontsFolder = path_1.default.join(handoff.getOutputPath(), 'fonts');
|
|
41
41
|
if (!fs_extra_1.default.existsSync(fontsFolder)) {
|
|
42
42
|
fs_extra_1.default.mkdirSync(fontsFolder);
|
|
43
43
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Validates that the Node.js runtime meets the minimum version requirements.
|
|
3
3
|
*
|
|
4
|
-
* @throws {Error} If Node.js version is below
|
|
4
|
+
* @throws {Error} If Node.js version is below 22.
|
|
5
5
|
*/
|
|
6
6
|
export declare const validateHandoffRequirements: () => Promise<void>;
|
|
@@ -14,13 +14,13 @@ const logger_1 = require("../utils/logger");
|
|
|
14
14
|
/**
|
|
15
15
|
* Validates that the Node.js runtime meets the minimum version requirements.
|
|
16
16
|
*
|
|
17
|
-
* @throws {Error} If Node.js version is below
|
|
17
|
+
* @throws {Error} If Node.js version is below 22.
|
|
18
18
|
*/
|
|
19
19
|
const validateHandoffRequirements = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
20
20
|
let requirements = false;
|
|
21
21
|
const result = process.versions;
|
|
22
22
|
if (result && result.node) {
|
|
23
|
-
if (parseInt(result.node) >=
|
|
23
|
+
if (parseInt(result.node) >= 22) {
|
|
24
24
|
requirements = true;
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -29,7 +29,7 @@ const validateHandoffRequirements = () => __awaiter(void 0, void 0, void 0, func
|
|
|
29
29
|
}
|
|
30
30
|
if (!requirements) {
|
|
31
31
|
logger_1.Logger.error('Handoff installation failed.');
|
|
32
|
-
logger_1.Logger.warn('- Please update node to at least Node
|
|
32
|
+
logger_1.Logger.warn('- Please update node to at least Node 22 https://nodejs.org/en/download. \n- You can read more about installing handoff at https://www.handoff.com/docs/');
|
|
33
33
|
throw new Error('Could not run handoff');
|
|
34
34
|
}
|
|
35
35
|
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Define helpers for custom server modules: storage, email and AI providers. This entry point has no
|
|
3
|
+
* runtime dependencies, so a module that imports it does not pull the CLI into the registry bundle.
|
|
4
|
+
*/
|
|
5
|
+
export { defineAiProvider } from './ai/define';
|
|
6
|
+
export type { AiProvider, AiProviderContext, AiProviderFactory } from './ai/types';
|
|
7
|
+
export { defineStorageProvider } from './registry/asset-storage/define';
|
|
8
|
+
export type { StorageProvider, StorageProviderContext, StorageProviderFactory, StorageInput, StorageReadResult, AssetUpload, AssetUploadInput, } from './registry/asset-storage/types';
|
|
9
|
+
export { defineEmailProvider } from './registry/email/define';
|
|
10
|
+
export type { EmailMessage, EmailProvider, EmailProviderContext, EmailProviderFactory } from './registry/email/types';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defineEmailProvider = exports.defineStorageProvider = exports.defineAiProvider = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Define helpers for custom server modules: storage, email and AI providers. This entry point has no
|
|
6
|
+
* runtime dependencies, so a module that imports it does not pull the CLI into the registry bundle.
|
|
7
|
+
*/
|
|
8
|
+
var define_1 = require("./ai/define");
|
|
9
|
+
Object.defineProperty(exports, "defineAiProvider", { enumerable: true, get: function () { return define_1.defineAiProvider; } });
|
|
10
|
+
var define_2 = require("./registry/asset-storage/define");
|
|
11
|
+
Object.defineProperty(exports, "defineStorageProvider", { enumerable: true, get: function () { return define_2.defineStorageProvider; } });
|
|
12
|
+
var define_3 = require("./registry/email/define");
|
|
13
|
+
Object.defineProperty(exports, "defineEmailProvider", { enumerable: true, get: function () { return define_3.defineEmailProvider; } });
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Typed identity helper for custom asset storage
|
|
2
|
+
* Typed identity helper for custom asset storage providers (mirrors `defineCatalogItem`): a consumer
|
|
3
3
|
* default-exports its result from a **server-only** module and points
|
|
4
4
|
* `runtime.registry.assetStorage.module` at it. It must be its own module (not an inline
|
|
5
5
|
* `defineConfig` function) so the build can trace it into the deployed registry. The default export
|
|
6
|
-
* may be the
|
|
6
|
+
* may be the provider object, or a factory `(context) => StorageProvider` when it needs its options/env.
|
|
7
7
|
*/
|
|
8
|
-
import type {
|
|
9
|
-
export declare function
|
|
10
|
-
export declare function
|
|
8
|
+
import type { StorageProvider, StorageProviderFactory } from './types';
|
|
9
|
+
export declare function defineStorageProvider(provider: StorageProvider): StorageProvider;
|
|
10
|
+
export declare function defineStorageProvider(factory: StorageProviderFactory): StorageProviderFactory;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Typed identity helper for custom asset storage
|
|
3
|
+
* Typed identity helper for custom asset storage providers (mirrors `defineCatalogItem`): a consumer
|
|
4
4
|
* default-exports its result from a **server-only** module and points
|
|
5
5
|
* `runtime.registry.assetStorage.module` at it. It must be its own module (not an inline
|
|
6
6
|
* `defineConfig` function) so the build can trace it into the deployed registry. The default export
|
|
7
|
-
* may be the
|
|
7
|
+
* may be the provider object, or a factory `(context) => StorageProvider` when it needs its options/env.
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.
|
|
11
|
-
function
|
|
10
|
+
exports.defineStorageProvider = defineStorageProvider;
|
|
11
|
+
function defineStorageProvider(input) {
|
|
12
12
|
return input;
|
|
13
13
|
}
|
|
@@ -2,44 +2,34 @@
|
|
|
2
2
|
* Asset storage settings resolution.
|
|
3
3
|
*
|
|
4
4
|
* `runtime.registry.assetStorage` selects the active provider for **new** uploads: the built-in
|
|
5
|
-
* `database` inline default
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* `database` inline default or a `custom` module. The build bakes the provider, the module location,
|
|
6
|
+
* and the options. An option literal is baked as it is. An environment reference stays `{ $env }`
|
|
7
|
+
* and is resolved at request time, before the custom provider factory runs.
|
|
8
8
|
*/
|
|
9
9
|
import type { ResolvedConfig } from '../../types/config';
|
|
10
10
|
import type { AssetStorageProvider } from '../db/schema';
|
|
11
|
-
/** Which adapter implementation is active. */
|
|
12
|
-
export type AssetStorageAdapterKind = 'database' | 'vercel-blob' | 'custom';
|
|
13
11
|
/** The raw, authored settings block (a structural copy of the config type, resolvable from bake). */
|
|
14
12
|
export interface AssetStorageSettings {
|
|
15
|
-
|
|
13
|
+
provider?: AssetStorageProvider;
|
|
16
14
|
module?: string;
|
|
17
|
-
tokenEnv?: string;
|
|
18
15
|
maxInlineBytes?: number;
|
|
19
16
|
options?: Record<string, unknown>;
|
|
20
17
|
}
|
|
21
|
-
|
|
22
|
-
export declare const DEFAULT_ASSET_STORAGE_ADAPTER: AssetStorageAdapterKind;
|
|
23
|
-
/** Default env-var name holding the Vercel Blob read/write token. */
|
|
24
|
-
export declare const DEFAULT_BLOB_TOKEN_ENV = "BLOB_READ_WRITE_TOKEN";
|
|
18
|
+
export declare const DEFAULT_ASSET_STORAGE_PROVIDER: AssetStorageProvider;
|
|
25
19
|
/** Default max bytes kept inline in Postgres `bytea` (4 MB, under Vercel's ~4.5 MB function limit). */
|
|
26
20
|
export declare const DEFAULT_MAX_INLINE_BYTES: number;
|
|
27
21
|
/** Fully resolved asset storage settings. */
|
|
28
22
|
export interface ResolvedAssetStorage {
|
|
29
|
-
/**
|
|
30
|
-
adapterKind: AssetStorageAdapterKind;
|
|
31
|
-
/** Provider id recorded on blob rows written by the active adapter (drives read resolution). */
|
|
23
|
+
/** Recorded on each new blob row as its storage provider. */
|
|
32
24
|
provider: AssetStorageProvider;
|
|
33
|
-
/** For `custom`: the server-only
|
|
25
|
+
/** For `custom`: the server-only provider module path. */
|
|
34
26
|
module?: string;
|
|
35
|
-
/** For `vercel-blob`: the env-var name holding the token. */
|
|
36
|
-
tokenEnv: string;
|
|
37
27
|
/** For `database`: the inline-content size ceiling. */
|
|
38
28
|
maxInlineBytes: number;
|
|
39
|
-
/**
|
|
29
|
+
/** Provider options: literals and `{ $env }` references, never secret values. */
|
|
40
30
|
options: Record<string, unknown>;
|
|
41
31
|
}
|
|
42
32
|
/** Resolve raw asset storage settings into a fully-defaulted shape. */
|
|
43
33
|
export declare const resolveAssetStorageSettings: (settings: AssetStorageSettings | null | undefined) => ResolvedAssetStorage;
|
|
44
|
-
/** Resolve the
|
|
34
|
+
/** Resolve and validate the asset storage block from a loaded config (CLI/build side). An unusable block fails the build. */
|
|
45
35
|
export declare const resolveAssetStorageFromConfig: (config: ResolvedConfig | null | undefined) => ResolvedAssetStorage;
|
|
@@ -3,37 +3,49 @@
|
|
|
3
3
|
* Asset storage settings resolution.
|
|
4
4
|
*
|
|
5
5
|
* `runtime.registry.assetStorage` selects the active provider for **new** uploads: the built-in
|
|
6
|
-
* `database` inline default
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* `database` inline default or a `custom` module. The build bakes the provider, the module location,
|
|
7
|
+
* and the options. An option literal is baked as it is. An environment reference stays `{ $env }`
|
|
8
|
+
* and is resolved at request time, before the custom provider factory runs.
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
-
exports.resolveAssetStorageFromConfig = exports.resolveAssetStorageSettings = exports.DEFAULT_MAX_INLINE_BYTES = exports.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
exports.DEFAULT_BLOB_TOKEN_ENV = 'BLOB_READ_WRITE_TOKEN';
|
|
11
|
+
exports.resolveAssetStorageFromConfig = exports.resolveAssetStorageSettings = exports.DEFAULT_MAX_INLINE_BYTES = exports.DEFAULT_ASSET_STORAGE_PROVIDER = void 0;
|
|
12
|
+
const errors_1 = require("../../config/errors");
|
|
13
|
+
const PROVIDERS = ['database', 'custom'];
|
|
14
|
+
exports.DEFAULT_ASSET_STORAGE_PROVIDER = 'database';
|
|
16
15
|
/** Default max bytes kept inline in Postgres `bytea` (4 MB, under Vercel's ~4.5 MB function limit). */
|
|
17
16
|
exports.DEFAULT_MAX_INLINE_BYTES = 4 * 1024 * 1024;
|
|
18
|
-
/** The stable provider id recorded on blobs for a custom adapter (single custom provider at a time). */
|
|
19
|
-
const customProviderId = (options) => {
|
|
20
|
-
const id = typeof options.providerId === 'string' && options.providerId.trim() ? options.providerId.trim() : 'custom';
|
|
21
|
-
return id;
|
|
22
|
-
};
|
|
23
17
|
/** Resolve raw asset storage settings into a fully-defaulted shape. */
|
|
24
18
|
const resolveAssetStorageSettings = (settings) => {
|
|
25
|
-
var _a, _b, _c
|
|
26
|
-
const
|
|
19
|
+
var _a, _b, _c;
|
|
20
|
+
const provider = (_a = settings === null || settings === void 0 ? void 0 : settings.provider) !== null && _a !== void 0 ? _a : exports.DEFAULT_ASSET_STORAGE_PROVIDER;
|
|
27
21
|
const options = (_b = settings === null || settings === void 0 ? void 0 : settings.options) !== null && _b !== void 0 ? _b : {};
|
|
28
|
-
const tokenEnv = ((_c = settings === null || settings === void 0 ? void 0 : settings.tokenEnv) === null || _c === void 0 ? void 0 : _c.trim()) || exports.DEFAULT_BLOB_TOKEN_ENV;
|
|
29
22
|
const maxInlineBytes = typeof (settings === null || settings === void 0 ? void 0 : settings.maxInlineBytes) === 'number' && settings.maxInlineBytes > 0 ? settings.maxInlineBytes : exports.DEFAULT_MAX_INLINE_BYTES;
|
|
30
|
-
|
|
31
|
-
return { adapterKind, provider, module: ((_d = settings === null || settings === void 0 ? void 0 : settings.module) === null || _d === void 0 ? void 0 : _d.trim()) || undefined, tokenEnv, maxInlineBytes, options };
|
|
23
|
+
return { provider, module: ((_c = settings === null || settings === void 0 ? void 0 : settings.module) === null || _c === void 0 ? void 0 : _c.trim()) || undefined, maxInlineBytes, options };
|
|
32
24
|
};
|
|
33
25
|
exports.resolveAssetStorageSettings = resolveAssetStorageSettings;
|
|
34
|
-
/** Resolve the
|
|
26
|
+
/** Resolve and validate the asset storage block from a loaded config (CLI/build side). An unusable block fails the build. */
|
|
35
27
|
const resolveAssetStorageFromConfig = (config) => {
|
|
36
|
-
var _a, _b, _c
|
|
37
|
-
|
|
28
|
+
var _a, _b, _c;
|
|
29
|
+
const settings = (_b = (_a = config === null || config === void 0 ? void 0 : config.runtime) === null || _a === void 0 ? void 0 : _a.registry) === null || _b === void 0 ? void 0 : _b.assetStorage;
|
|
30
|
+
const fail = (problem) => {
|
|
31
|
+
throw new errors_1.HandoffConfigError(`Config "runtime.registry.assetStorage": ${problem}`);
|
|
32
|
+
};
|
|
33
|
+
const provider = (_c = settings === null || settings === void 0 ? void 0 : settings.provider) !== null && _c !== void 0 ? _c : exports.DEFAULT_ASSET_STORAGE_PROVIDER;
|
|
34
|
+
if (!PROVIDERS.includes(provider)) {
|
|
35
|
+
fail(`unknown provider ${JSON.stringify(provider)}. Use "database" or "custom".`);
|
|
36
|
+
}
|
|
37
|
+
const options = settings === null || settings === void 0 ? void 0 : settings.options;
|
|
38
|
+
if (options !== undefined && (options === null || typeof options !== 'object' || Array.isArray(options))) {
|
|
39
|
+
fail('"options" must be an object.');
|
|
40
|
+
}
|
|
41
|
+
const module = settings === null || settings === void 0 ? void 0 : settings.module;
|
|
42
|
+
if (provider === 'custom' && (typeof module !== 'string' || !module.trim())) {
|
|
43
|
+
fail('provider "custom" needs a "module" that default-exports a defineStorageProvider() result.');
|
|
44
|
+
}
|
|
45
|
+
const maxInlineBytes = settings === null || settings === void 0 ? void 0 : settings.maxInlineBytes;
|
|
46
|
+
if (maxInlineBytes !== undefined && (typeof maxInlineBytes !== 'number' || !Number.isInteger(maxInlineBytes) || maxInlineBytes <= 0)) {
|
|
47
|
+
fail('"maxInlineBytes" must be a positive whole number.');
|
|
48
|
+
}
|
|
49
|
+
return (0, exports.resolveAssetStorageSettings)(settings);
|
|
38
50
|
};
|
|
39
51
|
exports.resolveAssetStorageFromConfig = resolveAssetStorageFromConfig;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Server-only asset storage contract for
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* Server-only asset storage contract for custom {@link defineStorageProvider} object storage modules.
|
|
3
|
+
* The built-in `database` provider is the inline default: bytes live in the `asset_blobs.content`
|
|
4
|
+
* bytea column, read/written directly by the registry, so it needs no module. Object providers
|
|
5
|
+
* record a `storageRef` + provider id so reads resolve back through whichever provider stored the
|
|
6
|
+
* blob.
|
|
7
7
|
*/
|
|
8
8
|
/** Input for storing one content-addressed blob. */
|
|
9
|
-
export interface
|
|
9
|
+
export interface StorageInput {
|
|
10
10
|
/** SHA-256 of the bytes (hex), the content identity, usable as a stable object key. */
|
|
11
11
|
hash: string;
|
|
12
12
|
bytes: Buffer;
|
|
@@ -17,7 +17,7 @@ export interface AssetStorageInput {
|
|
|
17
17
|
* How a provider returns stored content: raw bytes, a readable stream, or a signed/redirect URL the
|
|
18
18
|
* client can fetch directly (used to avoid routing large payloads through the serverless function).
|
|
19
19
|
*/
|
|
20
|
-
export type
|
|
20
|
+
export type StorageReadResult = {
|
|
21
21
|
kind: 'bytes';
|
|
22
22
|
bytes: Buffer;
|
|
23
23
|
contentType?: string;
|
|
@@ -29,23 +29,43 @@ export type AssetStorageReadResult = {
|
|
|
29
29
|
kind: 'redirect';
|
|
30
30
|
url: string;
|
|
31
31
|
};
|
|
32
|
-
/**
|
|
33
|
-
export interface
|
|
32
|
+
/** Input for a direct upload of one blob from the CLI to storage. */
|
|
33
|
+
export interface AssetUploadInput {
|
|
34
|
+
/** SHA-256 of the bytes (hex). */
|
|
35
|
+
hash: string;
|
|
36
|
+
size: number;
|
|
37
|
+
contentType: string;
|
|
38
|
+
}
|
|
39
|
+
/** A signed URL the CLI sends the blob to with `PUT` and the given headers. */
|
|
40
|
+
export interface AssetUpload {
|
|
41
|
+
url: string;
|
|
42
|
+
headers?: Record<string, string>;
|
|
43
|
+
/** The reference to persist on the blob's `asset_blobs` row after the upload. */
|
|
44
|
+
storageRef: string;
|
|
45
|
+
}
|
|
46
|
+
/** The pluggable storage contract for object-backed asset providers. */
|
|
47
|
+
export interface StorageProvider {
|
|
34
48
|
/** Store one blob and return the reference to persist on its `asset_blobs` row. */
|
|
35
|
-
put(input:
|
|
49
|
+
put(input: StorageInput): Promise<{
|
|
36
50
|
storageRef: string;
|
|
37
51
|
}>;
|
|
52
|
+
/**
|
|
53
|
+
* Return a signed URL so the CLI uploads the blob directly to storage instead of through the
|
|
54
|
+
* registry, which avoids serverless request size limits. The URL must make storage reject bytes
|
|
55
|
+
* that do not match `hash`, for example with a signed SHA-256 checksum header.
|
|
56
|
+
*/
|
|
57
|
+
createUpload?(input: AssetUploadInput): Promise<AssetUpload>;
|
|
38
58
|
/** Resolve stored content by its `storageRef`. */
|
|
39
|
-
get(storageRef: string): Promise<
|
|
59
|
+
get(storageRef: string): Promise<StorageReadResult>;
|
|
40
60
|
/** Delete stored content by its `storageRef`. Should be retryable/idempotent. */
|
|
41
61
|
delete(storageRef: string): Promise<void>;
|
|
42
62
|
}
|
|
43
|
-
/** Context passed to a custom
|
|
44
|
-
export interface
|
|
45
|
-
/**
|
|
63
|
+
/** Context passed to a custom provider factory: its config options and the process env. */
|
|
64
|
+
export interface StorageProviderContext {
|
|
65
|
+
/** `runtime.registry.assetStorage.options`, with each `fromEnv()` reference replaced by its value. */
|
|
46
66
|
options: Record<string, unknown>;
|
|
47
|
-
/** The process environment.
|
|
67
|
+
/** The process environment. */
|
|
48
68
|
env: NodeJS.ProcessEnv;
|
|
49
69
|
}
|
|
50
|
-
/** A custom
|
|
51
|
-
export type
|
|
70
|
+
/** A custom provider module may export the provider directly or a factory that builds it from its context. */
|
|
71
|
+
export type StorageProviderFactory = (context: StorageProviderContext) => StorageProvider | Promise<StorageProvider>;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Server-only asset storage contract for
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
3
|
+
* Server-only asset storage contract for custom {@link defineStorageProvider} object storage modules.
|
|
4
|
+
* The built-in `database` provider is the inline default: bytes live in the `asset_blobs.content`
|
|
5
|
+
* bytea column, read/written directly by the registry, so it needs no module. Object providers
|
|
6
|
+
* record a `storageRef` + provider id so reads resolve back through whichever provider stored the
|
|
7
|
+
* blob.
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { RegistryDatabase } from '../db/client';
|
|
2
|
+
/**
|
|
3
|
+
* Reader-supplied keys for connections the config declares with `credential: 'user'`.
|
|
4
|
+
*
|
|
5
|
+
* The key is write-only across the API boundary: once saved, a read reports only that a key exists.
|
|
6
|
+
* Only the chat route ever decrypts one, and only to send it to the connection's own endpoint.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Ids of the connections this reader has a *usable* key for. A row that no longer decrypts — written
|
|
10
|
+
* under a rotated `HANDOFF_AI_KEY_SECRET` — is left out, so the account page and the assistant agree
|
|
11
|
+
* that no key is in place instead of offering a key the provider would never receive.
|
|
12
|
+
*/
|
|
13
|
+
export declare const listRegistryAiKeyConnections: (db: RegistryDatabase, userId: string) => Promise<string[]>;
|
|
14
|
+
/** Save or replace this reader's key for one connection. */
|
|
15
|
+
export declare const saveRegistryAiKey: (db: RegistryDatabase, input: {
|
|
16
|
+
userId: string;
|
|
17
|
+
connectionId: string;
|
|
18
|
+
key: string;
|
|
19
|
+
}) => Promise<void>;
|
|
20
|
+
/** Remove this reader's key for one connection. Returns whether a row was there to remove. */
|
|
21
|
+
export declare const deleteRegistryAiKey: (db: RegistryDatabase, input: {
|
|
22
|
+
userId: string;
|
|
23
|
+
connectionId: string;
|
|
24
|
+
}) => Promise<boolean>;
|
|
25
|
+
/**
|
|
26
|
+
* Read back one reader's key. A row that no longer decrypts reads as absent, so the reader is told
|
|
27
|
+
* to add a key rather than shown a provider error.
|
|
28
|
+
*/
|
|
29
|
+
export declare const readRegistryAiKey: (db: RegistryDatabase, input: {
|
|
30
|
+
userId: string;
|
|
31
|
+
connectionId: string;
|
|
32
|
+
}) => Promise<string | null>;
|