handoff-app 2.0.0-11 → 2.0.0-13

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.
Files changed (141) hide show
  1. package/{Changelog.md → CHANGELOG.md} +84 -0
  2. package/{License.md → LICENSE.md} +1 -1
  3. package/README.md +760 -801
  4. package/RECIPES.md +975 -0
  5. package/components.json +20 -20
  6. package/config/docs/foundations/colors/guidelines.md +0 -1
  7. package/config/docs/foundations/colors.md +0 -1
  8. package/config/docs/foundations/effects/guidelines.md +0 -1
  9. package/config/docs/foundations/effects/resources.md +0 -1
  10. package/config/docs/foundations/effects.md +0 -1
  11. package/config/docs/foundations/fonts.md +0 -1
  12. package/config/docs/foundations/grid.md +0 -1
  13. package/config/docs/foundations/icon.md +0 -1
  14. package/config/docs/foundations/icons/guidelines.md +0 -1
  15. package/config/docs/foundations/icons.md +0 -1
  16. package/config/docs/foundations/images/guidelines.md +0 -1
  17. package/config/docs/foundations/images/resources.md +0 -1
  18. package/config/docs/foundations/images.md +0 -1
  19. package/config/docs/foundations/logo/resources.md +0 -1
  20. package/config/docs/foundations/logo.md +0 -1
  21. package/config/docs/foundations/typography/guidelines.md +0 -1
  22. package/config/docs/foundations/typography/resources.md +0 -1
  23. package/config/docs/foundations/typography.md +0 -1
  24. package/config/docs/foundations.md +0 -1
  25. package/config/docs/guidelines.md +0 -1
  26. package/config/docs/index.md +0 -1
  27. package/config/docs/system/tokens/foundations/colors.md +0 -4
  28. package/config/docs/system/tokens/foundations/effects.md +0 -4
  29. package/config/docs/system/tokens/foundations/typography.md +0 -2
  30. package/config/docs/system.md +0 -2
  31. package/config/templates/page.md +2 -1
  32. package/config/templates/page.mdx +2 -1
  33. package/dist/ai/define.d.ts +1 -1
  34. package/dist/ai/define.js +1 -1
  35. package/dist/ai/types.d.ts +1 -1
  36. package/dist/ai/types.js +1 -1
  37. package/dist/app-builder/build.js +16 -16
  38. package/dist/app-builder/client-config.js +1 -1
  39. package/dist/config/loader.js +1 -2
  40. package/dist/index.d.ts +2 -2
  41. package/dist/index.js +2 -2
  42. package/dist/nav/index.d.ts +2 -2
  43. package/dist/nav/registry.d.ts +3 -3
  44. package/dist/nav/registry.js +1 -1
  45. package/dist/nav/workspace.d.ts +3 -3
  46. package/dist/nav/workspace.js +1 -1
  47. package/dist/providers.d.ts +10 -0
  48. package/dist/{define.js → providers.js} +4 -4
  49. package/dist/registry/asset-storage/define.d.ts +5 -5
  50. package/dist/registry/asset-storage/define.js +4 -4
  51. package/dist/registry/asset-storage/resolve.d.ts +8 -11
  52. package/dist/registry/asset-storage/resolve.js +12 -13
  53. package/dist/registry/asset-storage/types.d.ts +12 -12
  54. package/dist/registry/asset-storage/types.js +2 -2
  55. package/dist/registry/db/schema.d.ts +2 -2
  56. package/dist/registry/db/schema.js +2 -2
  57. package/dist/registry/email/define.d.ts +1 -1
  58. package/dist/registry/email/define.js +1 -1
  59. package/dist/registry/email/resolve.d.ts +7 -8
  60. package/dist/registry/email/resolve.js +21 -23
  61. package/dist/registry/email/types.d.ts +1 -1
  62. package/dist/registry/email/types.js +1 -1
  63. package/dist/store/registry.d.ts +3 -3
  64. package/dist/store/registry.js +4 -4
  65. package/dist/types/config.d.ts +4 -13
  66. package/docs/cli.md +275 -54
  67. package/package.json +8 -9
  68. package/src/ai/define.ts +1 -1
  69. package/src/ai/types.ts +1 -1
  70. package/src/app/components/Auth/AccountLayout.tsx +1 -1
  71. package/src/app/components/Auth/AuthShell.tsx +10 -4
  72. package/src/app/components/Foundations/ColorGrid.tsx +13 -14
  73. package/src/app/components/Layout/Main.tsx +2 -2
  74. package/src/app/components/McpIntegration/McpConfigDialog.tsx +12 -51
  75. package/src/app/components/Navigation/MobileNav.tsx +0 -2
  76. package/src/app/components/util/index.ts +0 -11
  77. package/src/app/lib/asset-storage.ts +25 -25
  78. package/src/app/lib/auth/email.ts +4 -5
  79. package/src/app/lib/docs-api/page-rendering.ts +1 -1
  80. package/src/app/lib/docs-api/registry.ts +2 -3
  81. package/src/app/lib/docs-api/runtime-config.ts +8 -8
  82. package/src/app/lib/mcp/components.ts +33 -29
  83. package/src/app/lib/mcp/pages.ts +26 -19
  84. package/src/app/lib/mcp/server.ts +33 -9
  85. package/src/app/lib/mcp/tokens.ts +22 -16
  86. package/src/app/lib/mcp/tool.ts +19 -0
  87. package/src/app/lib/registry-api/asset-transfer.ts +25 -25
  88. package/src/app/lib/server-module.ts +1 -1
  89. package/src/app/lib/utils.ts +7 -0
  90. package/src/app/next.config.mjs +2 -2
  91. package/src/app/pages/[...slug]/index.tsx +2 -1
  92. package/src/app/pages/account/ai.tsx +1 -1
  93. package/src/app/pages/account/index.tsx +1 -1
  94. package/src/app/pages/account/tokens.tsx +1 -1
  95. package/src/app/pages/account/users.tsx +1 -1
  96. package/src/app/pages/api/docs/nav.json.ts +1 -1
  97. package/src/app/pages/api/mcp.ts +6 -0
  98. package/src/app/pages/api/registry/transfer/assets/blobs/[hash].ts +1 -1
  99. package/src/app/pages/cli/device.tsx +1 -1
  100. package/src/app/pages/foundations/colors.tsx +9 -8
  101. package/src/app/pages/foundations/index.tsx +3 -3
  102. package/src/app/pages/index.tsx +2 -2
  103. package/src/app/pages/login.tsx +10 -5
  104. package/src/app/pages/reset-password.tsx +7 -2
  105. package/src/app/pages/system/component/[component]/index.tsx +2 -5
  106. package/src/app/pages/system/index.tsx +11 -11
  107. package/src/app/pages/system/pattern/[pattern]/index.tsx +2 -2
  108. package/src/app/pages/system/tokens/components/[component]/index.tsx +0 -4
  109. package/src/app-builder/build.ts +12 -12
  110. package/src/app-builder/client-config.ts +1 -1
  111. package/src/config/loader.ts +1 -2
  112. package/src/index.ts +6 -6
  113. package/src/nav/index.ts +2 -2
  114. package/src/nav/registry.ts +3 -3
  115. package/src/nav/workspace.ts +3 -3
  116. package/src/{define.ts → providers.ts} +8 -8
  117. package/src/registry/asset-storage/define.ts +6 -6
  118. package/src/registry/asset-storage/resolve.ts +16 -20
  119. package/src/registry/asset-storage/types.ts +12 -12
  120. package/src/registry/db/schema.ts +3 -3
  121. package/src/registry/email/define.ts +1 -1
  122. package/src/registry/email/resolve.ts +23 -23
  123. package/src/registry/email/types.ts +1 -1
  124. package/src/store/registry.ts +7 -7
  125. package/src/types/config.ts +4 -14
  126. package/.editorconfig +0 -19
  127. package/.eslintrc.json +0 -4
  128. package/.prettierignore +0 -0
  129. package/.prettierrc.json +0 -8
  130. package/.vscode/settings.json +0 -17
  131. package/dist/define.d.ts +0 -10
  132. package/dist/registry/email/adapters/resend.d.ts +0 -7
  133. package/dist/registry/email/adapters/resend.js +0 -35
  134. package/src/app/components/old/Header.tsx +0 -89
  135. package/src/app/pages/assets/fonts.tsx +0 -129
  136. package/src/app/pages/assets/index.tsx +0 -91
  137. package/src/app/pages/assets/logos.tsx +0 -75
  138. package/src/registry/email/adapters/resend.ts +0 -22
  139. /package/dist/registry/email/{adapters → providers}/smtp.d.ts +0 -0
  140. /package/dist/registry/email/{adapters → providers}/smtp.js +0 -0
  141. /package/src/registry/email/{adapters → providers}/smtp.ts +0 -0
@@ -1,10 +1,10 @@
1
1
  /**
2
- * Typed identity helper for custom asset storage adapters (mirrors `defineCatalogItem`): a consumer
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 adapter object, or a factory `(context) => AssetStorage` when it needs its options/env.
6
+ * may be the provider object, or a factory `(context) => StorageProvider` when it needs its options/env.
7
7
  */
8
- import type { AssetStorage, AssetStorageFactory } from './types';
9
- export declare function defineAssetStorage(adapter: AssetStorage): AssetStorage;
10
- export declare function defineAssetStorage(factory: AssetStorageFactory): AssetStorageFactory;
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 adapters (mirrors `defineCatalogItem`): a consumer
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 adapter object, or a factory `(context) => AssetStorage` when it needs its options/env.
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.defineAssetStorage = defineAssetStorage;
11
- function defineAssetStorage(input) {
10
+ exports.defineStorageProvider = defineStorageProvider;
11
+ function defineStorageProvider(input) {
12
12
  return input;
13
13
  }
@@ -2,34 +2,31 @@
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 or a `custom` module. The build bakes the adapter, the module location,
5
+ * `database` inline default or a `custom` module. The build bakes the provider, the module location,
6
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 adapter factory runs.
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. Recorded on each blob row as its storage provider. */
12
- export type AssetStorageAdapterKind = AssetStorageProvider;
13
11
  /** The raw, authored settings block (a structural copy of the config type, resolvable from bake). */
14
12
  export interface AssetStorageSettings {
15
- adapter?: AssetStorageAdapterKind;
13
+ provider?: AssetStorageProvider;
16
14
  module?: string;
17
15
  maxInlineBytes?: number;
18
16
  options?: Record<string, unknown>;
19
17
  }
20
- /** Default adapter when none is configured. */
21
- export declare const DEFAULT_ASSET_STORAGE_ADAPTER: AssetStorageAdapterKind;
18
+ export declare const DEFAULT_ASSET_STORAGE_PROVIDER: AssetStorageProvider;
22
19
  /** Default max bytes kept inline in Postgres `bytea` (4 MB, under Vercel's ~4.5 MB function limit). */
23
20
  export declare const DEFAULT_MAX_INLINE_BYTES: number;
24
21
  /** Fully resolved asset storage settings. */
25
22
  export interface ResolvedAssetStorage {
26
- /** The active adapter implementation. */
27
- adapterKind: AssetStorageAdapterKind;
28
- /** For `custom`: the server-only adapter module path. */
23
+ /** Recorded on each new blob row as its storage provider. */
24
+ provider: AssetStorageProvider;
25
+ /** For `custom`: the server-only provider module path. */
29
26
  module?: string;
30
27
  /** For `database`: the inline-content size ceiling. */
31
28
  maxInlineBytes: number;
32
- /** Adapter options: literals and `{ $env }` references, never secret values. */
29
+ /** Provider options: literals and `{ $env }` references, never secret values. */
33
30
  options: Record<string, unknown>;
34
31
  }
35
32
  /** Resolve raw asset storage settings into a fully-defaulted shape. */
@@ -3,25 +3,24 @@
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 or a `custom` module. The build bakes the adapter, the module location,
6
+ * `database` inline default or a `custom` module. The build bakes the provider, the module location,
7
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 adapter factory runs.
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.DEFAULT_ASSET_STORAGE_ADAPTER = void 0;
11
+ exports.resolveAssetStorageFromConfig = exports.resolveAssetStorageSettings = exports.DEFAULT_MAX_INLINE_BYTES = exports.DEFAULT_ASSET_STORAGE_PROVIDER = void 0;
12
12
  const errors_1 = require("../../config/errors");
13
- const ADAPTERS = ['database', 'custom'];
14
- /** Default adapter when none is configured. */
15
- exports.DEFAULT_ASSET_STORAGE_ADAPTER = 'database';
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
17
  /** Resolve raw asset storage settings into a fully-defaulted shape. */
19
18
  const resolveAssetStorageSettings = (settings) => {
20
19
  var _a, _b, _c;
21
- const adapterKind = (_a = settings === null || settings === void 0 ? void 0 : settings.adapter) !== null && _a !== void 0 ? _a : exports.DEFAULT_ASSET_STORAGE_ADAPTER;
20
+ const provider = (_a = settings === null || settings === void 0 ? void 0 : settings.provider) !== null && _a !== void 0 ? _a : exports.DEFAULT_ASSET_STORAGE_PROVIDER;
22
21
  const options = (_b = settings === null || settings === void 0 ? void 0 : settings.options) !== null && _b !== void 0 ? _b : {};
23
22
  const maxInlineBytes = typeof (settings === null || settings === void 0 ? void 0 : settings.maxInlineBytes) === 'number' && settings.maxInlineBytes > 0 ? settings.maxInlineBytes : exports.DEFAULT_MAX_INLINE_BYTES;
24
- return { adapterKind, module: ((_c = settings === null || settings === void 0 ? void 0 : settings.module) === null || _c === void 0 ? void 0 : _c.trim()) || undefined, 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 };
25
24
  };
26
25
  exports.resolveAssetStorageSettings = resolveAssetStorageSettings;
27
26
  /** Resolve and validate the asset storage block from a loaded config (CLI/build side). An unusable block fails the build. */
@@ -31,17 +30,17 @@ const resolveAssetStorageFromConfig = (config) => {
31
30
  const fail = (problem) => {
32
31
  throw new errors_1.HandoffConfigError(`Config "runtime.registry.assetStorage": ${problem}`);
33
32
  };
34
- const adapter = (_c = settings === null || settings === void 0 ? void 0 : settings.adapter) !== null && _c !== void 0 ? _c : exports.DEFAULT_ASSET_STORAGE_ADAPTER;
35
- if (!ADAPTERS.includes(adapter)) {
36
- fail(`unknown adapter ${JSON.stringify(adapter)}. Use "database" or "custom".`);
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".`);
37
36
  }
38
37
  const options = settings === null || settings === void 0 ? void 0 : settings.options;
39
38
  if (options !== undefined && (options === null || typeof options !== 'object' || Array.isArray(options))) {
40
39
  fail('"options" must be an object.');
41
40
  }
42
41
  const module = settings === null || settings === void 0 ? void 0 : settings.module;
43
- if (adapter === 'custom' && (typeof module !== 'string' || !module.trim())) {
44
- fail('adapter "custom" needs a "module" that default-exports a defineAssetStorage() result.');
42
+ if (provider === 'custom' && (typeof module !== 'string' || !module.trim())) {
43
+ fail('provider "custom" needs a "module" that default-exports a defineStorageProvider() result.');
45
44
  }
46
45
  const maxInlineBytes = settings === null || settings === void 0 ? void 0 : settings.maxInlineBytes;
47
46
  if (maxInlineBytes !== undefined && (typeof maxInlineBytes !== 'number' || !Number.isInteger(maxInlineBytes) || maxInlineBytes <= 0)) {
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Server-only asset storage contract for custom {@link defineAssetStorage} object storage modules.
2
+ * Server-only asset storage contract for custom {@link defineStorageProvider} object storage modules.
3
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 adapter. Object providers
4
+ * bytea column, read/written directly by the registry, so it needs no module. Object providers
5
5
  * record a `storageRef` + provider id so reads resolve back through whichever provider stored the
6
6
  * blob.
7
7
  */
8
8
  /** Input for storing one content-addressed blob. */
9
- export interface AssetStorageInput {
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 AssetStorageReadResult = {
20
+ export type StorageReadResult = {
21
21
  kind: 'bytes';
22
22
  bytes: Buffer;
23
23
  contentType?: string;
@@ -43,10 +43,10 @@ export interface AssetUpload {
43
43
  /** The reference to persist on the blob's `asset_blobs` row after the upload. */
44
44
  storageRef: string;
45
45
  }
46
- /** The pluggable storage adapter contract for object-backed asset providers. */
47
- export interface AssetStorage {
46
+ /** The pluggable storage contract for object-backed asset providers. */
47
+ export interface StorageProvider {
48
48
  /** Store one blob and return the reference to persist on its `asset_blobs` row. */
49
- put(input: AssetStorageInput): Promise<{
49
+ put(input: StorageInput): Promise<{
50
50
  storageRef: string;
51
51
  }>;
52
52
  /**
@@ -56,16 +56,16 @@ export interface AssetStorage {
56
56
  */
57
57
  createUpload?(input: AssetUploadInput): Promise<AssetUpload>;
58
58
  /** Resolve stored content by its `storageRef`. */
59
- get(storageRef: string): Promise<AssetStorageReadResult>;
59
+ get(storageRef: string): Promise<StorageReadResult>;
60
60
  /** Delete stored content by its `storageRef`. Should be retryable/idempotent. */
61
61
  delete(storageRef: string): Promise<void>;
62
62
  }
63
- /** Context passed to a custom adapter factory: its config options and the process env. */
64
- export interface AssetStorageContext {
63
+ /** Context passed to a custom provider factory: its config options and the process env. */
64
+ export interface StorageProviderContext {
65
65
  /** `runtime.registry.assetStorage.options`, with each `fromEnv()` reference replaced by its value. */
66
66
  options: Record<string, unknown>;
67
67
  /** The process environment. */
68
68
  env: NodeJS.ProcessEnv;
69
69
  }
70
- /** A custom adapter may export the adapter directly or a factory that builds it from its context. */
71
- export type AssetStorageFactory = (context: AssetStorageContext) => AssetStorage | Promise<AssetStorage>;
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,8 +1,8 @@
1
1
  "use strict";
2
2
  /**
3
- * Server-only asset storage contract for custom {@link defineAssetStorage} object storage modules.
3
+ * Server-only asset storage contract for custom {@link defineStorageProvider} object storage modules.
4
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 adapter. Object providers
5
+ * bytea column, read/written directly by the registry, so it needs no module. Object providers
6
6
  * record a `storageRef` + provider id so reads resolve back through whichever provider stored the
7
7
  * blob.
8
8
  */
@@ -24,7 +24,7 @@
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 live: inline in the database (`database`) or in the custom adapter (`custom`). */
27
+ /** Where an asset blob's bytes live: inline in the database (`database`) or in a custom provider (`custom`). */
28
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. */
@@ -2439,7 +2439,7 @@ export declare const assets: import("drizzle-orm/pg-core").PgTableWithColumns<{
2439
2439
  /**
2440
2440
  * Content-addressed binary blob store, shared across assets/collections (the `hash` is the blob
2441
2441
  * identity, so an unchanged file re-references the same row). Exactly one location is active per row:
2442
- * inline `content` (bytea, default DB adapter) or an external `storageRef` resolved through the
2442
+ * inline `content` (bytea, default `database` provider) or an external `storageRef` resolved through the
2443
2443
  * recorded `storageProvider`. Both nullable so DB- and object-backed blobs coexist.
2444
2444
  */
2445
2445
  export declare const assetBlobs: import("drizzle-orm/pg-core").PgTableWithColumns<{
@@ -28,7 +28,7 @@ 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 adapter keeps small/ordinary asset
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)({
@@ -321,7 +321,7 @@ exports.assets = (0, pg_core_1.pgTable)('assets', {
321
321
  /**
322
322
  * Content-addressed binary blob store, shared across assets/collections (the `hash` is the blob
323
323
  * identity, so an unchanged file re-references the same row). Exactly one location is active per row:
324
- * inline `content` (bytea, default DB adapter) or an external `storageRef` resolved through the
324
+ * inline `content` (bytea, default `database` provider) or an external `storageRef` resolved through the
325
325
  * recorded `storageProvider`. Both nullable so DB- and object-backed blobs coexist.
326
326
  */
327
327
  exports.assetBlobs = (0, pg_core_1.pgTable)('asset_blobs', {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Typed identity helper for custom email providers (mirrors `defineAssetStorage`): a consumer
2
+ * Typed identity helper for custom email providers (mirrors `defineStorageProvider`): a consumer
3
3
  * default-exports its result from a **server-only** module and points `runtime.registry.email.module`
4
4
  * at it. It must be its own module (not an inline `defineConfig` function) so the build can trace it
5
5
  * into the deployed registry. The default export may be the provider object, or a factory
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * Typed identity helper for custom email providers (mirrors `defineAssetStorage`): a consumer
3
+ * Typed identity helper for custom email providers (mirrors `defineStorageProvider`): a consumer
4
4
  * default-exports its result from a **server-only** module and points `runtime.registry.email.module`
5
5
  * at it. It must be its own module (not an inline `defineConfig` function) so the build can trace it
6
6
  * into the deployed registry. The default export may be the provider object, or a factory
@@ -1,19 +1,18 @@
1
1
  /**
2
2
  * Registry email settings resolution.
3
3
  *
4
- * `runtime.registry.email.provider` selects the built-in `resend` or `smtp` provider, or a `custom`
5
- * module. The build bakes the provider, the module location, and the options. An option literal is
6
- * baked as it is. An environment reference stays `{ $env }` and is resolved at request time.
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.
7
8
  */
8
9
  import type { ResolvedConfig } from '../../types/config';
9
- export type EmailProviderKind = 'resend' | 'smtp' | 'custom';
10
- export declare const DEFAULT_EMAIL_PROVIDER: EmailProviderKind;
11
- /** Default env-var name holding the Resend API key. */
12
- export declare const DEFAULT_RESEND_API_KEY_ENV = "RESEND_API_KEY";
10
+ export type EmailProviderKind = 'smtp' | 'custom';
13
11
  /** Email settings as they are baked: literals and `{ $env }` references, never secret values. */
14
12
  export interface EmailSettings {
15
13
  from?: string;
16
- provider: EmailProviderKind;
14
+ /** Unset when the config has no email block. */
15
+ provider?: EmailProviderKind;
17
16
  /** For `custom`: the server-only provider module path. */
18
17
  module?: string;
19
18
  options: Record<string, unknown>;
@@ -2,45 +2,46 @@
2
2
  /**
3
3
  * Registry email settings resolution.
4
4
  *
5
- * `runtime.registry.email.provider` selects the built-in `resend` or `smtp` provider, or a `custom`
6
- * module. The build bakes the provider, the module location, and the options. An option literal is
7
- * baked as it is. An environment reference stays `{ $env }` and is resolved at request time.
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.
8
9
  */
9
10
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.parseEmailSettings = exports.resolveEmailFromConfig = exports.DEFAULT_RESEND_API_KEY_ENV = exports.DEFAULT_EMAIL_PROVIDER = void 0;
11
+ exports.parseEmailSettings = exports.resolveEmailFromConfig = void 0;
11
12
  const errors_1 = require("../../config/errors");
12
- exports.DEFAULT_EMAIL_PROVIDER = 'resend';
13
- /** Default env-var name holding the Resend API key. */
14
- exports.DEFAULT_RESEND_API_KEY_ENV = 'RESEND_API_KEY';
15
- const PROVIDERS = ['resend', 'smtp', 'custom'];
13
+ const PROVIDERS = ['smtp', 'custom'];
16
14
  /** The options each built-in provider accepts. A custom provider accepts any. */
17
15
  const BUILT_IN_OPTIONS = {
18
- resend: ['apiKey'],
19
16
  smtp: ['host', 'port', 'secure', 'user', 'password'],
20
17
  };
21
18
  const isPlainRecord = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
22
19
  /** Resolve and validate the email block from a loaded config (CLI/build side). An unusable block fails the build. */
23
20
  const resolveEmailFromConfig = (config) => {
24
- var _a, _b, _c, _d;
21
+ var _a, _b, _c;
25
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;
26
23
  const fail = (problem) => {
27
24
  throw new errors_1.HandoffConfigError(`Config "runtime.registry.email": ${problem}`);
28
25
  };
29
- const provider = ((_c = email === null || email === void 0 ? void 0 : email.provider) !== null && _c !== void 0 ? _c : exports.DEFAULT_EMAIL_PROVIDER);
26
+ if (email === undefined)
27
+ return { options: {} };
28
+ const provider = email.provider;
30
29
  if (!PROVIDERS.includes(provider)) {
31
- fail(`unknown provider ${JSON.stringify(provider)}. Use "resend", "smtp" or "custom".`);
30
+ fail(email.provider === undefined
31
+ ? '"provider" is required. Use "smtp" or "custom".'
32
+ : `unknown provider ${JSON.stringify(provider)}. Use "smtp" or "custom".`);
32
33
  }
33
- if ((email === null || email === void 0 ? void 0 : email.options) !== undefined && !isPlainRecord(email.options)) {
34
+ if (email.options !== undefined && !isPlainRecord(email.options)) {
34
35
  fail('"options" must be an object.');
35
36
  }
36
- const options = Object.assign({}, ((_d = email === null || email === void 0 ? void 0 : email.options) !== null && _d !== void 0 ? _d : {}));
37
- const module = typeof (email === null || email === void 0 ? void 0 : email.module) === 'string' ? email.module.trim() : undefined;
37
+ const options = Object.assign({}, ((_c = email.options) !== null && _c !== void 0 ? _c : {}));
38
+ const module = typeof email.module === 'string' ? email.module.trim() : undefined;
38
39
  if (provider === 'custom') {
39
40
  if (!module)
40
41
  fail('provider "custom" needs a "module" that default-exports a defineEmailProvider() result.');
41
42
  }
42
43
  else {
43
- if ((email === null || email === void 0 ? void 0 : email.module) !== undefined)
44
+ if (email.module !== undefined)
44
45
  fail(`"module" is used only by provider "custom", not "${provider}".`);
45
46
  const allowed = BUILT_IN_OPTIONS[provider];
46
47
  const unknown = Object.keys(options).filter((key) => !allowed.includes(key));
@@ -51,26 +52,23 @@ const resolveEmailFromConfig = (config) => {
51
52
  }
52
53
  if (provider === 'smtp' && options.host === undefined)
53
54
  fail('provider "smtp" needs "options.host".');
54
- if (provider === 'resend' && options.apiKey === undefined)
55
- options.apiKey = { $env: exports.DEFAULT_RESEND_API_KEY_ENV };
56
- const from = typeof (email === null || email === void 0 ? void 0 : email.from) === 'string' ? email.from.trim() || undefined : undefined;
55
+ const from = typeof email.from === 'string' ? email.from.trim() || undefined : undefined;
57
56
  return Object.assign(Object.assign({ from, provider }, (module ? { module } : {})), { options });
58
57
  };
59
58
  exports.resolveEmailFromConfig = resolveEmailFromConfig;
60
59
  /** Read baked settings back leniently (server side): a malformed value must not take the server down. */
61
60
  const parseEmailSettings = (raw) => {
62
- var _a;
63
61
  let options = raw.options;
64
62
  if (typeof options === 'string') {
65
63
  try {
66
64
  options = options.trim() ? JSON.parse(options) : {};
67
65
  }
68
- catch (_b) {
66
+ catch (_a) {
69
67
  options = {};
70
68
  }
71
69
  }
72
- const provider = (_a = PROVIDERS.find((kind) => kind === raw.provider)) !== null && _a !== void 0 ? _a : exports.DEFAULT_EMAIL_PROVIDER;
70
+ const provider = PROVIDERS.find((kind) => kind === raw.provider);
73
71
  const module = typeof raw.module === 'string' ? raw.module.trim() : '';
74
- return Object.assign(Object.assign({ from: typeof raw.from === 'string' ? raw.from.trim() || undefined : undefined, provider }, (module ? { module } : {})), { options: isPlainRecord(options) ? options : {} });
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 : {} });
75
73
  };
76
74
  exports.parseEmailSettings = parseEmailSettings;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Server-only email provider contract. Handoff renders every message, and a provider only delivers it.
3
- * Resend and SMTP are built in. Anything else is a {@link defineEmailProvider} module.
3
+ * SMTP is built in. Anything else is a {@link defineEmailProvider} module.
4
4
  */
5
5
  /** One rendered message. */
6
6
  export interface EmailMessage {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
3
  * Server-only email provider contract. Handoff renders every message, and a provider only delivers it.
4
- * Resend and SMTP are built in. Anything else is a {@link defineEmailProvider} module.
4
+ * SMTP is built in. Anything else is a {@link defineEmailProvider} module.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -13,7 +13,7 @@
13
13
  * - Reads never materialize source files or run the build pipeline — every record and file is
14
14
  * returned exactly as it was published.
15
15
  */
16
- import type { AssetStorage } from '../registry/asset-storage/types';
16
+ import type { StorageProvider } from '../registry/asset-storage/types';
17
17
  import type { RegistryDatabase } from '../registry/db/client';
18
18
  import { type AssetStorageProvider } from '../registry/db/schema';
19
19
  import type { ComponentListObject, PageListObject, PatternListObject } from '../transformers/preview/types';
@@ -22,11 +22,11 @@ import type { AssetContentResource, AssetMetadata, AssetStore, ComponentStore, H
22
22
  export interface RegistryStoreContext {
23
23
  db: RegistryDatabase;
24
24
  /**
25
- * Resolve the {@link AssetStorage} adapter for a blob's recorded provider (or `null` for the inline
25
+ * Resolve the {@link StorageProvider} for a blob's recorded provider (or `null` for the inline
26
26
  * `database` provider). Injected by the app runtime so `src/store` stays free of app-side imports;
27
27
  * when omitted, only inline DB-backed asset content is resolvable.
28
28
  */
29
- resolveAssetAdapter?: (provider: AssetStorageProvider) => Promise<AssetStorage | null>;
29
+ resolveStorageProvider?: (provider: AssetStorageProvider) => Promise<StorageProvider | null>;
30
30
  }
31
31
  export declare class RegistryComponentStore implements ComponentStore {
32
32
  private readonly context;
@@ -404,14 +404,14 @@ class RegistryAssetStore {
404
404
  return Object.assign(Object.assign({}, base), { body: Buffer.from(row.blobContent) });
405
405
  }
406
406
  // Object-backed - resolve through the provider that stored it.
407
- if (!row.storageRef || !this.context.resolveAssetAdapter) {
407
+ if (!row.storageRef || !this.context.resolveStorageProvider) {
408
408
  return null;
409
409
  }
410
- const adapter = yield this.context.resolveAssetAdapter(row.storageProvider);
411
- if (!adapter) {
410
+ const storage = yield this.context.resolveStorageProvider(row.storageProvider);
411
+ if (!storage) {
412
412
  return null;
413
413
  }
414
- const result = yield adapter.get(row.storageRef);
414
+ const result = yield storage.get(row.storageRef);
415
415
  if (result.kind === 'redirect') {
416
416
  return Object.assign(Object.assign({}, base), { redirectUrl: result.url });
417
417
  }
@@ -126,15 +126,6 @@ interface HandoffEmailBase {
126
126
  /** Sender address, such as `Handoff <no-reply@example.com>`. Use a profile for per-environment values. */
127
127
  from?: string;
128
128
  }
129
- /** Send through the Resend HTTP API. */
130
- export interface HandoffResendEmail extends HandoffEmailBase {
131
- /** @default "resend" */
132
- provider?: 'resend';
133
- options?: {
134
- /** Environment reference to the Resend API key. @default fromEnv('RESEND_API_KEY') */
135
- apiKey?: RuntimeEnvReference<string>;
136
- };
137
- }
138
129
  /** Send through an SMTP server, such as Amazon SES, SendGrid, Postmark, Mailgun or Microsoft 365. */
139
130
  export interface HandoffSmtpEmail extends HandoffEmailBase {
140
131
  provider: 'smtp';
@@ -164,7 +155,7 @@ export interface HandoffCustomEmail extends HandoffEmailBase {
164
155
  options?: Record<string, unknown>;
165
156
  }
166
157
  /** Registry email settings. `provider` selects the shape of `options`. */
167
- export type HandoffEmailConfig = HandoffResendEmail | HandoffSmtpEmail | HandoffCustomEmail;
158
+ export type HandoffEmailConfig = HandoffSmtpEmail | HandoffCustomEmail;
168
159
  /**
169
160
  * One AI connection the deployment declares. Connections are the whole surface: a reader can add a
170
161
  * key for a declared connection and nothing else, so the server never calls a URL a reader chose.
@@ -268,13 +259,13 @@ export interface HandoffRuntimeConfig {
268
259
  * `custom` module for object storage.
269
260
  * @default "database"
270
261
  */
271
- adapter?: 'database' | 'custom';
272
- /** For `adapter: "custom"` - server-only module path default-exporting a `defineAssetStorage` adapter. */
262
+ provider?: 'database' | 'custom';
263
+ /** For `provider: "custom"` - server-only module path default-exporting a `defineStorageProvider()` result. */
273
264
  module?: string;
274
265
  /** Max bytes kept inline in the database `bytea` column (larger uploads are rejected). @default 4194304 */
275
266
  maxInlineBytes?: number;
276
267
  /**
277
- * Options passed to the adapter factory. Literals are baked. Environment references are resolved
268
+ * Options passed to the provider factory. Literals are baked. Environment references are resolved
278
269
  * at request time, so secrets must be environment references.
279
270
  */
280
271
  options?: Record<string, unknown>;