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
|
@@ -20,8 +20,8 @@ exports.DEFAULT_REGISTRY_API_TOKEN_ENV = 'HANDOFF_REGISTRY_API_TOKEN';
|
|
|
20
20
|
exports.DEFAULT_REGISTRY_DRIVER = 'pg';
|
|
21
21
|
/** Resolve the env-var name holding the database URL (defaults to `DATABASE_URL`). */
|
|
22
22
|
const resolveDatabaseUrlEnv = (config) => {
|
|
23
|
-
var _a, _b, _c, _d;
|
|
24
|
-
const configured = (_d = (_c = (_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.
|
|
23
|
+
var _a, _b, _c, _d, _e;
|
|
24
|
+
const configured = (_e = (_d = (_c = (_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.database) === null || _c === void 0 ? void 0 : _c.url) === null || _d === void 0 ? void 0 : _d.$env) === null || _e === void 0 ? void 0 : _e.trim();
|
|
25
25
|
return configured || exports.DEFAULT_DATABASE_URL_ENV;
|
|
26
26
|
};
|
|
27
27
|
exports.resolveDatabaseUrlEnv = resolveDatabaseUrlEnv;
|
|
@@ -43,7 +43,7 @@ const resolveRegistryDatabase = (config) => {
|
|
|
43
43
|
const connectionString = (_a = process.env[databaseUrlEnv]) === null || _a === void 0 ? void 0 : _a.trim();
|
|
44
44
|
if (!connectionString) {
|
|
45
45
|
throw new Error(`Registry database URL is not configured. Set the "${databaseUrlEnv}" environment variable to a ` +
|
|
46
|
-
`PostgreSQL connection string (or change "runtime.registry.
|
|
46
|
+
`PostgreSQL connection string (or change "runtime.registry.database.url" to point at the env var you use).`);
|
|
47
47
|
}
|
|
48
48
|
return { driver, databaseUrlEnv, connectionString };
|
|
49
49
|
};
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
import type { ArtifactBuildStatus, ArtifactKind, ArtifactOwnerKind, ArtifactReference } from '../../artifacts/types';
|
|
25
25
|
import type { RegistryTextFileKind } from '../../store/types';
|
|
26
26
|
import type { TokenSetKind } from '../tokens/sets';
|
|
27
|
-
/** Where an asset blob's bytes
|
|
28
|
-
export type AssetStorageProvider = 'database' | '
|
|
27
|
+
/** Where an asset blob's bytes live: inline in the database (`database`) or in a custom provider (`custom`). */
|
|
28
|
+
export type AssetStorageProvider = 'database' | 'custom';
|
|
29
29
|
import type { ComponentListObject, PageListObject, PatternComponentEntry, PatternListObject } from '../../transformers/preview/types';
|
|
30
30
|
/** Registry account role. Administrators may issue write-scoped credentials. */
|
|
31
31
|
export type RegistryUserRole = 'admin' | 'member';
|
|
@@ -248,6 +248,23 @@ export declare const components: import("drizzle-orm/pg-core").PgTableWithColumn
|
|
|
248
248
|
}, {}, {
|
|
249
249
|
$type: string[];
|
|
250
250
|
}>;
|
|
251
|
+
sourceDir: import("drizzle-orm/pg-core").PgColumn<{
|
|
252
|
+
name: "source_dir";
|
|
253
|
+
tableName: "components";
|
|
254
|
+
dataType: "string";
|
|
255
|
+
columnType: "PgText";
|
|
256
|
+
data: string;
|
|
257
|
+
driverParam: string;
|
|
258
|
+
notNull: false;
|
|
259
|
+
hasDefault: false;
|
|
260
|
+
isPrimaryKey: false;
|
|
261
|
+
isAutoincrement: false;
|
|
262
|
+
hasRuntimeDefault: false;
|
|
263
|
+
enumValues: [string, ...string[]];
|
|
264
|
+
baseColumn: never;
|
|
265
|
+
identity: undefined;
|
|
266
|
+
generated: undefined;
|
|
267
|
+
}, {}, {}>;
|
|
251
268
|
record: import("drizzle-orm/pg-core").PgColumn<{
|
|
252
269
|
name: "record";
|
|
253
270
|
tableName: "components";
|
|
@@ -603,6 +620,23 @@ export declare const patterns: import("drizzle-orm/pg-core").PgTableWithColumns<
|
|
|
603
620
|
}, {}, {
|
|
604
621
|
$type: PatternComponentEntry[];
|
|
605
622
|
}>;
|
|
623
|
+
sourceDir: import("drizzle-orm/pg-core").PgColumn<{
|
|
624
|
+
name: "source_dir";
|
|
625
|
+
tableName: "patterns";
|
|
626
|
+
dataType: "string";
|
|
627
|
+
columnType: "PgText";
|
|
628
|
+
data: string;
|
|
629
|
+
driverParam: string;
|
|
630
|
+
notNull: false;
|
|
631
|
+
hasDefault: false;
|
|
632
|
+
isPrimaryKey: false;
|
|
633
|
+
isAutoincrement: false;
|
|
634
|
+
hasRuntimeDefault: false;
|
|
635
|
+
enumValues: [string, ...string[]];
|
|
636
|
+
baseColumn: never;
|
|
637
|
+
identity: undefined;
|
|
638
|
+
generated: undefined;
|
|
639
|
+
}, {}, {}>;
|
|
606
640
|
record: import("drizzle-orm/pg-core").PgColumn<{
|
|
607
641
|
name: "record";
|
|
608
642
|
tableName: "patterns";
|
|
@@ -2405,7 +2439,7 @@ export declare const assets: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
2405
2439
|
/**
|
|
2406
2440
|
* Content-addressed binary blob store, shared across assets/collections (the `hash` is the blob
|
|
2407
2441
|
* identity, so an unchanged file re-references the same row). Exactly one location is active per row:
|
|
2408
|
-
* inline `content` (bytea, default
|
|
2442
|
+
* inline `content` (bytea, default `database` provider) or an external `storageRef` resolved through the
|
|
2409
2443
|
* recorded `storageProvider`. Both nullable so DB- and object-backed blobs coexist.
|
|
2410
2444
|
*/
|
|
2411
2445
|
export declare const assetBlobs: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
@@ -3310,6 +3344,104 @@ export declare const registryDeviceAuthorizations: import("drizzle-orm/pg-core")
|
|
|
3310
3344
|
};
|
|
3311
3345
|
dialect: "pg";
|
|
3312
3346
|
}>;
|
|
3347
|
+
/**
|
|
3348
|
+
* One reader's key for one declared AI connection. Unlike every other credential here the value
|
|
3349
|
+
* must be recoverable, because it is sent to the provider on that reader's behalf, so it is
|
|
3350
|
+
* encrypted rather than hashed (see `../auth/crypto`). A row whose `connectionId` no longer appears
|
|
3351
|
+
* in the config is ignored, not deleted: the connection may come back on the next deploy.
|
|
3352
|
+
*/
|
|
3353
|
+
export declare const registryAiKeys: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
3354
|
+
name: "registry_ai_keys";
|
|
3355
|
+
schema: undefined;
|
|
3356
|
+
columns: {
|
|
3357
|
+
userId: import("drizzle-orm/pg-core").PgColumn<{
|
|
3358
|
+
name: "user_id";
|
|
3359
|
+
tableName: "registry_ai_keys";
|
|
3360
|
+
dataType: "string";
|
|
3361
|
+
columnType: "PgText";
|
|
3362
|
+
data: string;
|
|
3363
|
+
driverParam: string;
|
|
3364
|
+
notNull: true;
|
|
3365
|
+
hasDefault: false;
|
|
3366
|
+
isPrimaryKey: false;
|
|
3367
|
+
isAutoincrement: false;
|
|
3368
|
+
hasRuntimeDefault: false;
|
|
3369
|
+
enumValues: [string, ...string[]];
|
|
3370
|
+
baseColumn: never;
|
|
3371
|
+
identity: undefined;
|
|
3372
|
+
generated: undefined;
|
|
3373
|
+
}, {}, {}>;
|
|
3374
|
+
connectionId: import("drizzle-orm/pg-core").PgColumn<{
|
|
3375
|
+
name: "connection_id";
|
|
3376
|
+
tableName: "registry_ai_keys";
|
|
3377
|
+
dataType: "string";
|
|
3378
|
+
columnType: "PgText";
|
|
3379
|
+
data: string;
|
|
3380
|
+
driverParam: string;
|
|
3381
|
+
notNull: true;
|
|
3382
|
+
hasDefault: false;
|
|
3383
|
+
isPrimaryKey: false;
|
|
3384
|
+
isAutoincrement: false;
|
|
3385
|
+
hasRuntimeDefault: false;
|
|
3386
|
+
enumValues: [string, ...string[]];
|
|
3387
|
+
baseColumn: never;
|
|
3388
|
+
identity: undefined;
|
|
3389
|
+
generated: undefined;
|
|
3390
|
+
}, {}, {}>;
|
|
3391
|
+
encryptedKey: import("drizzle-orm/pg-core").PgColumn<{
|
|
3392
|
+
name: "encrypted_key";
|
|
3393
|
+
tableName: "registry_ai_keys";
|
|
3394
|
+
dataType: "string";
|
|
3395
|
+
columnType: "PgText";
|
|
3396
|
+
data: string;
|
|
3397
|
+
driverParam: string;
|
|
3398
|
+
notNull: true;
|
|
3399
|
+
hasDefault: false;
|
|
3400
|
+
isPrimaryKey: false;
|
|
3401
|
+
isAutoincrement: false;
|
|
3402
|
+
hasRuntimeDefault: false;
|
|
3403
|
+
enumValues: [string, ...string[]];
|
|
3404
|
+
baseColumn: never;
|
|
3405
|
+
identity: undefined;
|
|
3406
|
+
generated: undefined;
|
|
3407
|
+
}, {}, {}>;
|
|
3408
|
+
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
3409
|
+
name: "created_at";
|
|
3410
|
+
tableName: "registry_ai_keys";
|
|
3411
|
+
dataType: "date";
|
|
3412
|
+
columnType: "PgTimestamp";
|
|
3413
|
+
data: Date;
|
|
3414
|
+
driverParam: string;
|
|
3415
|
+
notNull: true;
|
|
3416
|
+
hasDefault: true;
|
|
3417
|
+
isPrimaryKey: false;
|
|
3418
|
+
isAutoincrement: false;
|
|
3419
|
+
hasRuntimeDefault: false;
|
|
3420
|
+
enumValues: undefined;
|
|
3421
|
+
baseColumn: never;
|
|
3422
|
+
identity: undefined;
|
|
3423
|
+
generated: undefined;
|
|
3424
|
+
}, {}, {}>;
|
|
3425
|
+
updatedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
3426
|
+
name: "updated_at";
|
|
3427
|
+
tableName: "registry_ai_keys";
|
|
3428
|
+
dataType: "date";
|
|
3429
|
+
columnType: "PgTimestamp";
|
|
3430
|
+
data: Date;
|
|
3431
|
+
driverParam: string;
|
|
3432
|
+
notNull: true;
|
|
3433
|
+
hasDefault: true;
|
|
3434
|
+
isPrimaryKey: false;
|
|
3435
|
+
isAutoincrement: false;
|
|
3436
|
+
hasRuntimeDefault: false;
|
|
3437
|
+
enumValues: undefined;
|
|
3438
|
+
baseColumn: never;
|
|
3439
|
+
identity: undefined;
|
|
3440
|
+
generated: undefined;
|
|
3441
|
+
}, {}, {}>;
|
|
3442
|
+
};
|
|
3443
|
+
dialect: "pg";
|
|
3444
|
+
}>;
|
|
3313
3445
|
/**
|
|
3314
3446
|
* Fixed-window throttling counters. Callers pass a hash of the identifying value so raw emails and
|
|
3315
3447
|
* IP addresses are not retained.
|
|
@@ -3605,6 +3737,23 @@ export declare const registrySchema: {
|
|
|
3605
3737
|
}, {}, {
|
|
3606
3738
|
$type: string[];
|
|
3607
3739
|
}>;
|
|
3740
|
+
sourceDir: import("drizzle-orm/pg-core").PgColumn<{
|
|
3741
|
+
name: "source_dir";
|
|
3742
|
+
tableName: "components";
|
|
3743
|
+
dataType: "string";
|
|
3744
|
+
columnType: "PgText";
|
|
3745
|
+
data: string;
|
|
3746
|
+
driverParam: string;
|
|
3747
|
+
notNull: false;
|
|
3748
|
+
hasDefault: false;
|
|
3749
|
+
isPrimaryKey: false;
|
|
3750
|
+
isAutoincrement: false;
|
|
3751
|
+
hasRuntimeDefault: false;
|
|
3752
|
+
enumValues: [string, ...string[]];
|
|
3753
|
+
baseColumn: never;
|
|
3754
|
+
identity: undefined;
|
|
3755
|
+
generated: undefined;
|
|
3756
|
+
}, {}, {}>;
|
|
3608
3757
|
record: import("drizzle-orm/pg-core").PgColumn<{
|
|
3609
3758
|
name: "record";
|
|
3610
3759
|
tableName: "components";
|
|
@@ -3952,6 +4101,23 @@ export declare const registrySchema: {
|
|
|
3952
4101
|
}, {}, {
|
|
3953
4102
|
$type: PatternComponentEntry[];
|
|
3954
4103
|
}>;
|
|
4104
|
+
sourceDir: import("drizzle-orm/pg-core").PgColumn<{
|
|
4105
|
+
name: "source_dir";
|
|
4106
|
+
tableName: "patterns";
|
|
4107
|
+
dataType: "string";
|
|
4108
|
+
columnType: "PgText";
|
|
4109
|
+
data: string;
|
|
4110
|
+
driverParam: string;
|
|
4111
|
+
notNull: false;
|
|
4112
|
+
hasDefault: false;
|
|
4113
|
+
isPrimaryKey: false;
|
|
4114
|
+
isAutoincrement: false;
|
|
4115
|
+
hasRuntimeDefault: false;
|
|
4116
|
+
enumValues: [string, ...string[]];
|
|
4117
|
+
baseColumn: never;
|
|
4118
|
+
identity: undefined;
|
|
4119
|
+
generated: undefined;
|
|
4120
|
+
}, {}, {}>;
|
|
3955
4121
|
record: import("drizzle-orm/pg-core").PgColumn<{
|
|
3956
4122
|
name: "record";
|
|
3957
4123
|
tableName: "patterns";
|
|
@@ -6413,6 +6579,98 @@ export declare const registrySchema: {
|
|
|
6413
6579
|
};
|
|
6414
6580
|
dialect: "pg";
|
|
6415
6581
|
}>;
|
|
6582
|
+
registryAiKeys: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
6583
|
+
name: "registry_ai_keys";
|
|
6584
|
+
schema: undefined;
|
|
6585
|
+
columns: {
|
|
6586
|
+
userId: import("drizzle-orm/pg-core").PgColumn<{
|
|
6587
|
+
name: "user_id";
|
|
6588
|
+
tableName: "registry_ai_keys";
|
|
6589
|
+
dataType: "string";
|
|
6590
|
+
columnType: "PgText";
|
|
6591
|
+
data: string;
|
|
6592
|
+
driverParam: string;
|
|
6593
|
+
notNull: true;
|
|
6594
|
+
hasDefault: false;
|
|
6595
|
+
isPrimaryKey: false;
|
|
6596
|
+
isAutoincrement: false;
|
|
6597
|
+
hasRuntimeDefault: false;
|
|
6598
|
+
enumValues: [string, ...string[]];
|
|
6599
|
+
baseColumn: never;
|
|
6600
|
+
identity: undefined;
|
|
6601
|
+
generated: undefined;
|
|
6602
|
+
}, {}, {}>;
|
|
6603
|
+
connectionId: import("drizzle-orm/pg-core").PgColumn<{
|
|
6604
|
+
name: "connection_id";
|
|
6605
|
+
tableName: "registry_ai_keys";
|
|
6606
|
+
dataType: "string";
|
|
6607
|
+
columnType: "PgText";
|
|
6608
|
+
data: string;
|
|
6609
|
+
driverParam: string;
|
|
6610
|
+
notNull: true;
|
|
6611
|
+
hasDefault: false;
|
|
6612
|
+
isPrimaryKey: false;
|
|
6613
|
+
isAutoincrement: false;
|
|
6614
|
+
hasRuntimeDefault: false;
|
|
6615
|
+
enumValues: [string, ...string[]];
|
|
6616
|
+
baseColumn: never;
|
|
6617
|
+
identity: undefined;
|
|
6618
|
+
generated: undefined;
|
|
6619
|
+
}, {}, {}>;
|
|
6620
|
+
encryptedKey: import("drizzle-orm/pg-core").PgColumn<{
|
|
6621
|
+
name: "encrypted_key";
|
|
6622
|
+
tableName: "registry_ai_keys";
|
|
6623
|
+
dataType: "string";
|
|
6624
|
+
columnType: "PgText";
|
|
6625
|
+
data: string;
|
|
6626
|
+
driverParam: string;
|
|
6627
|
+
notNull: true;
|
|
6628
|
+
hasDefault: false;
|
|
6629
|
+
isPrimaryKey: false;
|
|
6630
|
+
isAutoincrement: false;
|
|
6631
|
+
hasRuntimeDefault: false;
|
|
6632
|
+
enumValues: [string, ...string[]];
|
|
6633
|
+
baseColumn: never;
|
|
6634
|
+
identity: undefined;
|
|
6635
|
+
generated: undefined;
|
|
6636
|
+
}, {}, {}>;
|
|
6637
|
+
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
6638
|
+
name: "created_at";
|
|
6639
|
+
tableName: "registry_ai_keys";
|
|
6640
|
+
dataType: "date";
|
|
6641
|
+
columnType: "PgTimestamp";
|
|
6642
|
+
data: Date;
|
|
6643
|
+
driverParam: string;
|
|
6644
|
+
notNull: true;
|
|
6645
|
+
hasDefault: true;
|
|
6646
|
+
isPrimaryKey: false;
|
|
6647
|
+
isAutoincrement: false;
|
|
6648
|
+
hasRuntimeDefault: false;
|
|
6649
|
+
enumValues: undefined;
|
|
6650
|
+
baseColumn: never;
|
|
6651
|
+
identity: undefined;
|
|
6652
|
+
generated: undefined;
|
|
6653
|
+
}, {}, {}>;
|
|
6654
|
+
updatedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
6655
|
+
name: "updated_at";
|
|
6656
|
+
tableName: "registry_ai_keys";
|
|
6657
|
+
dataType: "date";
|
|
6658
|
+
columnType: "PgTimestamp";
|
|
6659
|
+
data: Date;
|
|
6660
|
+
driverParam: string;
|
|
6661
|
+
notNull: true;
|
|
6662
|
+
hasDefault: true;
|
|
6663
|
+
isPrimaryKey: false;
|
|
6664
|
+
isAutoincrement: false;
|
|
6665
|
+
hasRuntimeDefault: false;
|
|
6666
|
+
enumValues: undefined;
|
|
6667
|
+
baseColumn: never;
|
|
6668
|
+
identity: undefined;
|
|
6669
|
+
generated: undefined;
|
|
6670
|
+
}, {}, {}>;
|
|
6671
|
+
};
|
|
6672
|
+
dialect: "pg";
|
|
6673
|
+
}>;
|
|
6416
6674
|
registryDeviceAuthorizations: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
6417
6675
|
name: "registry_device_authorizations";
|
|
6418
6676
|
schema: undefined;
|
|
@@ -23,12 +23,12 @@
|
|
|
23
23
|
* storage of large/binary assets (a content-reference field) rather than assuming inline content.
|
|
24
24
|
*/
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.registrySchema = exports.registryAuthRateLimits = exports.registryDeviceAuthorizations = exports.registryAccessTokens = exports.registryAuthActionTokens = exports.registryInstallations = exports.registryUsers = exports.assetBlobs = exports.assets = exports.assetCollections = exports.tokenArtifacts = exports.tokenSets = exports.buildMetadata = exports.docsArtifacts = exports.pageFiles = exports.pages = exports.patternFiles = exports.patterns = exports.componentFiles = exports.components = void 0;
|
|
26
|
+
exports.registrySchema = exports.registryAuthRateLimits = exports.registryAiKeys = exports.registryDeviceAuthorizations = exports.registryAccessTokens = exports.registryAuthActionTokens = exports.registryInstallations = exports.registryUsers = exports.assetBlobs = exports.assets = exports.assetCollections = exports.tokenArtifacts = exports.tokenSets = exports.buildMetadata = exports.docsArtifacts = exports.pageFiles = exports.pages = exports.patternFiles = exports.patterns = exports.componentFiles = exports.components = void 0;
|
|
27
27
|
const drizzle_orm_1 = require("drizzle-orm");
|
|
28
28
|
const pg_core_1 = require("drizzle-orm/pg-core");
|
|
29
29
|
/**
|
|
30
30
|
* PostgreSQL `bytea` column. Drizzle pg-core has no built-in binary type, so declare it once here.
|
|
31
|
-
* Reads/writes are Node `Buffer`s. The default asset storage
|
|
31
|
+
* Reads/writes are Node `Buffer`s. The default asset storage provider keeps small/ordinary asset
|
|
32
32
|
* bytes inline in this column; larger assets are offloaded to an object-storage `storageRef` instead.
|
|
33
33
|
*/
|
|
34
34
|
const bytea = (0, pg_core_1.customType)({
|
|
@@ -55,6 +55,8 @@ exports.components = (0, pg_core_1.pgTable)('components', {
|
|
|
55
55
|
sourceFormat: (0, pg_core_1.text)('source_format'),
|
|
56
56
|
tags: (0, pg_core_1.jsonb)('tags').$type(),
|
|
57
57
|
categories: (0, pg_core_1.jsonb)('categories').$type(),
|
|
58
|
+
/** Workspace-relative directory the component was published from. */
|
|
59
|
+
sourceDir: (0, pg_core_1.text)('source_dir'),
|
|
58
60
|
/** Full normalized component record. */
|
|
59
61
|
record: (0, pg_core_1.jsonb)('record').$type().notNull(),
|
|
60
62
|
/** Registry-only review/catalog metadata (management-API allowlist; never a render input). */
|
|
@@ -98,6 +100,8 @@ exports.patterns = (0, pg_core_1.pgTable)('patterns', {
|
|
|
98
100
|
tags: (0, pg_core_1.jsonb)('tags').$type(),
|
|
99
101
|
/** Pattern component references. */
|
|
100
102
|
components: (0, pg_core_1.jsonb)('components').$type(),
|
|
103
|
+
/** Workspace-relative directory the pattern was published from. */
|
|
104
|
+
sourceDir: (0, pg_core_1.text)('source_dir'),
|
|
101
105
|
/** Full normalized pattern record. */
|
|
102
106
|
record: (0, pg_core_1.jsonb)('record').$type().notNull(),
|
|
103
107
|
/** Registry-only review/catalog metadata (management-API allowlist; never a render input). */
|
|
@@ -317,13 +321,13 @@ exports.assets = (0, pg_core_1.pgTable)('assets', {
|
|
|
317
321
|
/**
|
|
318
322
|
* Content-addressed binary blob store, shared across assets/collections (the `hash` is the blob
|
|
319
323
|
* identity, so an unchanged file re-references the same row). Exactly one location is active per row:
|
|
320
|
-
* inline `content` (bytea, default
|
|
324
|
+
* inline `content` (bytea, default `database` provider) or an external `storageRef` resolved through the
|
|
321
325
|
* recorded `storageProvider`. Both nullable so DB- and object-backed blobs coexist.
|
|
322
326
|
*/
|
|
323
327
|
exports.assetBlobs = (0, pg_core_1.pgTable)('asset_blobs', {
|
|
324
328
|
/** SHA-256 of the blob bytes (hex). */
|
|
325
329
|
hash: (0, pg_core_1.text)('hash').primaryKey(),
|
|
326
|
-
/** Which storage backed this blob when it was written (`database
|
|
330
|
+
/** Which storage backed this blob when it was written (`database`|custom id). */
|
|
327
331
|
storageProvider: (0, pg_core_1.text)('storage_provider').$type().notNull(),
|
|
328
332
|
/** Inline bytes; null when stored externally via `storageRef`. */
|
|
329
333
|
content: bytea('content'),
|
|
@@ -435,6 +439,26 @@ exports.registryDeviceAuthorizations = (0, pg_core_1.pgTable)('registry_device_a
|
|
|
435
439
|
(0, pg_core_1.check)('registry_device_authorizations_status_valid', (0, drizzle_orm_1.sql) `${table.status} in ('pending', 'approved', 'denied', 'consumed')`),
|
|
436
440
|
(0, pg_core_1.check)('registry_device_authorizations_scopes_array', (0, drizzle_orm_1.sql) `jsonb_typeof(${table.scopes}) = 'array'`),
|
|
437
441
|
]);
|
|
442
|
+
/**
|
|
443
|
+
* One reader's key for one declared AI connection. Unlike every other credential here the value
|
|
444
|
+
* must be recoverable, because it is sent to the provider on that reader's behalf, so it is
|
|
445
|
+
* encrypted rather than hashed (see `../auth/crypto`). A row whose `connectionId` no longer appears
|
|
446
|
+
* in the config is ignored, not deleted: the connection may come back on the next deploy.
|
|
447
|
+
*/
|
|
448
|
+
exports.registryAiKeys = (0, pg_core_1.pgTable)('registry_ai_keys', {
|
|
449
|
+
userId: (0, pg_core_1.text)('user_id')
|
|
450
|
+
.notNull()
|
|
451
|
+
.references(() => exports.registryUsers.id, { onDelete: 'cascade' }),
|
|
452
|
+
/** Id of the connection the config declares. Never a reader-supplied endpoint. */
|
|
453
|
+
connectionId: (0, pg_core_1.text)('connection_id').notNull(),
|
|
454
|
+
/** Self-describing AES-256-GCM payload; the deployment secret it is keyed from is never stored. */
|
|
455
|
+
encryptedKey: (0, pg_core_1.text)('encrypted_key').notNull(),
|
|
456
|
+
createdAt: (0, pg_core_1.timestamp)('created_at', { withTimezone: true }).notNull().defaultNow(),
|
|
457
|
+
updatedAt: (0, pg_core_1.timestamp)('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
|
458
|
+
}, (table) => [
|
|
459
|
+
(0, pg_core_1.primaryKey)({ columns: [table.userId, table.connectionId] }),
|
|
460
|
+
(0, pg_core_1.check)('registry_ai_keys_connection_not_blank', (0, drizzle_orm_1.sql) `length(btrim(${table.connectionId})) > 0`),
|
|
461
|
+
]);
|
|
438
462
|
/**
|
|
439
463
|
* Fixed-window throttling counters. Callers pass a hash of the identifying value so raw emails and
|
|
440
464
|
* IP addresses are not retained.
|
|
@@ -471,6 +495,7 @@ exports.registrySchema = {
|
|
|
471
495
|
registryInstallations: exports.registryInstallations,
|
|
472
496
|
registryAuthActionTokens: exports.registryAuthActionTokens,
|
|
473
497
|
registryAccessTokens: exports.registryAccessTokens,
|
|
498
|
+
registryAiKeys: exports.registryAiKeys,
|
|
474
499
|
registryDeviceAuthorizations: exports.registryDeviceAuthorizations,
|
|
475
500
|
registryAuthRateLimits: exports.registryAuthRateLimits,
|
|
476
501
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed identity helper for custom email providers (mirrors `defineStorageProvider`): a consumer
|
|
3
|
+
* default-exports its result from a **server-only** module and points `runtime.registry.email.module`
|
|
4
|
+
* at it. It must be its own module (not an inline `defineConfig` function) so the build can trace it
|
|
5
|
+
* into the deployed registry. The default export may be the provider object, or a factory
|
|
6
|
+
* `(context) => EmailProvider` when it needs its options or env.
|
|
7
|
+
*/
|
|
8
|
+
import type { EmailProvider, EmailProviderFactory } from './types';
|
|
9
|
+
export declare function defineEmailProvider(provider: EmailProvider): EmailProvider;
|
|
10
|
+
export declare function defineEmailProvider(factory: EmailProviderFactory): EmailProviderFactory;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Typed identity helper for custom email providers (mirrors `defineStorageProvider`): a consumer
|
|
4
|
+
* default-exports its result from a **server-only** module and points `runtime.registry.email.module`
|
|
5
|
+
* at it. It must be its own module (not an inline `defineConfig` function) so the build can trace it
|
|
6
|
+
* into the deployed registry. The default export may be the provider object, or a factory
|
|
7
|
+
* `(context) => EmailProvider` when it needs its options or env.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.defineEmailProvider = defineEmailProvider;
|
|
11
|
+
function defineEmailProvider(input) {
|
|
12
|
+
return input;
|
|
13
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in SMTP provider, selected with `runtime.registry.email.provider: "smtp"`.
|
|
3
|
+
*
|
|
4
|
+
* `nodemailer` is loaded lazily so it is only pulled into memory (and traced into the registry
|
|
5
|
+
* bundle) when this provider is selected. Options may come from environment variables, so numbers
|
|
6
|
+
* and booleans can arrive as strings.
|
|
7
|
+
*/
|
|
8
|
+
import type { EmailProvider } from '../types';
|
|
9
|
+
/** Build an SMTP provider from options whose references are already resolved. */
|
|
10
|
+
export declare const createSmtpProvider: (options: Record<string, unknown>) => EmailProvider;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Built-in SMTP provider, selected with `runtime.registry.email.provider: "smtp"`.
|
|
4
|
+
*
|
|
5
|
+
* `nodemailer` is loaded lazily so it is only pulled into memory (and traced into the registry
|
|
6
|
+
* bundle) when this provider is selected. Options may come from environment variables, so numbers
|
|
7
|
+
* and booleans can arrive as strings.
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
26
|
+
if (mod && mod.__esModule) return mod;
|
|
27
|
+
var result = {};
|
|
28
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
29
|
+
__setModuleDefault(result, mod);
|
|
30
|
+
return result;
|
|
31
|
+
};
|
|
32
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
33
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
34
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
35
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
36
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
37
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
38
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.createSmtpProvider = void 0;
|
|
43
|
+
const toNumber = (value) => {
|
|
44
|
+
const number = typeof value === 'number' ? value : typeof value === 'string' && value.trim() ? Number(value) : NaN;
|
|
45
|
+
return Number.isFinite(number) ? number : undefined;
|
|
46
|
+
};
|
|
47
|
+
const toBoolean = (value) => typeof value === 'boolean' ? value : typeof value === 'string' && value.trim() ? value.trim() === 'true' : undefined;
|
|
48
|
+
/** Build an SMTP provider from options whose references are already resolved. */
|
|
49
|
+
const createSmtpProvider = (options) => {
|
|
50
|
+
var _a;
|
|
51
|
+
const host = typeof options.host === 'string' ? options.host : '';
|
|
52
|
+
const configuredPort = toNumber(options.port);
|
|
53
|
+
const secure = (_a = toBoolean(options.secure)) !== null && _a !== void 0 ? _a : (configuredPort === undefined || configuredPort === 465);
|
|
54
|
+
const port = configuredPort !== null && configuredPort !== void 0 ? configuredPort : (secure ? 465 : 587);
|
|
55
|
+
const user = typeof options.user === 'string' ? options.user : undefined;
|
|
56
|
+
const password = typeof options.password === 'string' ? options.password : undefined;
|
|
57
|
+
return {
|
|
58
|
+
send(_a) {
|
|
59
|
+
return __awaiter(this, arguments, void 0, function* ({ from, to, subject, html, text }) {
|
|
60
|
+
var _b;
|
|
61
|
+
if (!host)
|
|
62
|
+
throw new Error('The SMTP host is not set.');
|
|
63
|
+
const { createTransport } = yield Promise.resolve().then(() => __importStar(require('nodemailer')));
|
|
64
|
+
const transport = createTransport({ host, port, secure, auth: user ? { user, pass: password } : undefined });
|
|
65
|
+
const info = yield transport.sendMail({ from, to, subject, html, text });
|
|
66
|
+
if ((_b = info.rejected) === null || _b === void 0 ? void 0 : _b.length) {
|
|
67
|
+
throw new Error(`The SMTP server rejected the recipient: ${info.response}`);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
exports.createSmtpProvider = createSmtpProvider;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry email settings resolution.
|
|
3
|
+
*
|
|
4
|
+
* `runtime.registry.email.provider` selects the built-in `smtp` provider or a `custom` module. Without
|
|
5
|
+
* an email block, email delivery is off. The build bakes the provider, the module location, and the
|
|
6
|
+
* options. An option literal is baked as it is. An environment reference stays `{ $env }` and is
|
|
7
|
+
* resolved at request time.
|
|
8
|
+
*/
|
|
9
|
+
import type { ResolvedConfig } from '../../types/config';
|
|
10
|
+
export type EmailProviderKind = 'smtp' | 'custom';
|
|
11
|
+
/** Email settings as they are baked: literals and `{ $env }` references, never secret values. */
|
|
12
|
+
export interface EmailSettings {
|
|
13
|
+
from?: string;
|
|
14
|
+
/** Unset when the config has no email block. */
|
|
15
|
+
provider?: EmailProviderKind;
|
|
16
|
+
/** For `custom`: the server-only provider module path. */
|
|
17
|
+
module?: string;
|
|
18
|
+
options: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
/** Resolve and validate the email block from a loaded config (CLI/build side). An unusable block fails the build. */
|
|
21
|
+
export declare const resolveEmailFromConfig: (config: ResolvedConfig | null | undefined) => EmailSettings;
|
|
22
|
+
/** Read baked settings back leniently (server side): a malformed value must not take the server down. */
|
|
23
|
+
export declare const parseEmailSettings: (raw: {
|
|
24
|
+
from?: unknown;
|
|
25
|
+
provider?: unknown;
|
|
26
|
+
module?: unknown;
|
|
27
|
+
options?: unknown;
|
|
28
|
+
}) => EmailSettings;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Registry email settings resolution.
|
|
4
|
+
*
|
|
5
|
+
* `runtime.registry.email.provider` selects the built-in `smtp` provider or a `custom` module. Without
|
|
6
|
+
* an email block, email delivery is off. The build bakes the provider, the module location, and the
|
|
7
|
+
* options. An option literal is baked as it is. An environment reference stays `{ $env }` and is
|
|
8
|
+
* resolved at request time.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.parseEmailSettings = exports.resolveEmailFromConfig = void 0;
|
|
12
|
+
const errors_1 = require("../../config/errors");
|
|
13
|
+
const PROVIDERS = ['smtp', 'custom'];
|
|
14
|
+
/** The options each built-in provider accepts. A custom provider accepts any. */
|
|
15
|
+
const BUILT_IN_OPTIONS = {
|
|
16
|
+
smtp: ['host', 'port', 'secure', 'user', 'password'],
|
|
17
|
+
};
|
|
18
|
+
const isPlainRecord = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
19
|
+
/** Resolve and validate the email block from a loaded config (CLI/build side). An unusable block fails the build. */
|
|
20
|
+
const resolveEmailFromConfig = (config) => {
|
|
21
|
+
var _a, _b, _c;
|
|
22
|
+
const email = (_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.email;
|
|
23
|
+
const fail = (problem) => {
|
|
24
|
+
throw new errors_1.HandoffConfigError(`Config "runtime.registry.email": ${problem}`);
|
|
25
|
+
};
|
|
26
|
+
if (email === undefined)
|
|
27
|
+
return { options: {} };
|
|
28
|
+
const provider = email.provider;
|
|
29
|
+
if (!PROVIDERS.includes(provider)) {
|
|
30
|
+
fail(email.provider === undefined
|
|
31
|
+
? '"provider" is required. Use "smtp" or "custom".'
|
|
32
|
+
: `unknown provider ${JSON.stringify(provider)}. Use "smtp" or "custom".`);
|
|
33
|
+
}
|
|
34
|
+
if (email.options !== undefined && !isPlainRecord(email.options)) {
|
|
35
|
+
fail('"options" must be an object.');
|
|
36
|
+
}
|
|
37
|
+
const options = Object.assign({}, ((_c = email.options) !== null && _c !== void 0 ? _c : {}));
|
|
38
|
+
const module = typeof email.module === 'string' ? email.module.trim() : undefined;
|
|
39
|
+
if (provider === 'custom') {
|
|
40
|
+
if (!module)
|
|
41
|
+
fail('provider "custom" needs a "module" that default-exports a defineEmailProvider() result.');
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
if (email.module !== undefined)
|
|
45
|
+
fail(`"module" is used only by provider "custom", not "${provider}".`);
|
|
46
|
+
const allowed = BUILT_IN_OPTIONS[provider];
|
|
47
|
+
const unknown = Object.keys(options).filter((key) => !allowed.includes(key));
|
|
48
|
+
if (unknown.length > 0) {
|
|
49
|
+
fail(`provider "${provider}" does not support ${unknown.map((key) => `"options.${key}"`).join(', ')}. ` +
|
|
50
|
+
`Use ${allowed.map((key) => `"${key}"`).join(', ')}.`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (provider === 'smtp' && options.host === undefined)
|
|
54
|
+
fail('provider "smtp" needs "options.host".');
|
|
55
|
+
const from = typeof email.from === 'string' ? email.from.trim() || undefined : undefined;
|
|
56
|
+
return Object.assign(Object.assign({ from, provider }, (module ? { module } : {})), { options });
|
|
57
|
+
};
|
|
58
|
+
exports.resolveEmailFromConfig = resolveEmailFromConfig;
|
|
59
|
+
/** Read baked settings back leniently (server side): a malformed value must not take the server down. */
|
|
60
|
+
const parseEmailSettings = (raw) => {
|
|
61
|
+
let options = raw.options;
|
|
62
|
+
if (typeof options === 'string') {
|
|
63
|
+
try {
|
|
64
|
+
options = options.trim() ? JSON.parse(options) : {};
|
|
65
|
+
}
|
|
66
|
+
catch (_a) {
|
|
67
|
+
options = {};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const provider = PROVIDERS.find((kind) => kind === raw.provider);
|
|
71
|
+
const module = typeof raw.module === 'string' ? raw.module.trim() : '';
|
|
72
|
+
return Object.assign(Object.assign(Object.assign({ from: typeof raw.from === 'string' ? raw.from.trim() || undefined : undefined }, (provider ? { provider } : {})), (module ? { module } : {})), { options: isPlainRecord(options) ? options : {} });
|
|
73
|
+
};
|
|
74
|
+
exports.parseEmailSettings = parseEmailSettings;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-only email provider contract. Handoff renders every message, and a provider only delivers it.
|
|
3
|
+
* SMTP is built in. Anything else is a {@link defineEmailProvider} module.
|
|
4
|
+
*/
|
|
5
|
+
/** One rendered message. */
|
|
6
|
+
export interface EmailMessage {
|
|
7
|
+
from: string;
|
|
8
|
+
to: string;
|
|
9
|
+
subject: string;
|
|
10
|
+
html: string;
|
|
11
|
+
text: string;
|
|
12
|
+
}
|
|
13
|
+
/** The pluggable provider contract. `send` throws when the message is not accepted for delivery. */
|
|
14
|
+
export interface EmailProvider {
|
|
15
|
+
send(message: EmailMessage): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
/** Context passed to a provider factory. */
|
|
18
|
+
export interface EmailProviderContext {
|
|
19
|
+
/** `runtime.registry.email.options`, with environment references resolved to their values. */
|
|
20
|
+
options: Record<string, unknown>;
|
|
21
|
+
env: NodeJS.ProcessEnv;
|
|
22
|
+
}
|
|
23
|
+
/** A custom provider may export the provider directly or a factory that builds it from its context. */
|
|
24
|
+
export type EmailProviderFactory = (context: EmailProviderContext) => EmailProvider | Promise<EmailProvider>;
|