handoff-app 2.0.0-1 → 2.0.0-2
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/README.md +39 -17
- package/config/docs/foundations/colors/guidelines.md +9 -0
- package/config/docs/foundations/effects/guidelines.md +9 -0
- package/config/docs/foundations/effects/resources.md +9 -0
- package/config/docs/foundations/grid.md +10 -0
- package/config/docs/foundations/icons/guidelines.md +9 -0
- package/config/docs/foundations/images/guidelines.md +9 -0
- package/config/docs/foundations/images/resources.md +9 -0
- package/config/docs/foundations/images.md +10 -0
- package/config/docs/foundations/logo/resources.md +9 -0
- package/config/docs/foundations/typography/guidelines.md +9 -0
- package/config/docs/foundations/typography/resources.md +9 -0
- package/config/docs/foundations.md +5 -3
- package/dist/app-builder/build.js +89 -46
- package/dist/app-builder/client-config.js +5 -5
- package/dist/app-builder/paths.js +7 -9
- package/dist/app-builder/static-export.js +6 -6
- package/dist/app-builder/vercel-output.js +2 -2
- package/dist/artifacts/url.d.ts +7 -7
- package/dist/artifacts/url.js +7 -7
- package/dist/cli/create.js +48 -3
- package/dist/commands/checkout/index.d.ts +6 -6
- package/dist/commands/checkout/index.js +8 -12
- package/dist/commands/publish/index.d.ts +6 -5
- package/dist/commands/publish/index.js +8 -11
- package/dist/commands/utils.d.ts +6 -0
- package/dist/commands/utils.js +9 -1
- package/dist/index.d.ts +12 -8
- package/dist/index.js +26 -10
- package/dist/registry/asset-storage/resolve.d.ts +1 -1
- package/dist/registry/asset-storage/resolve.js +1 -1
- package/dist/registry/checkout/index.d.ts +10 -5
- package/dist/registry/checkout/index.js +62 -11
- package/dist/registry/client.d.ts +2 -1
- package/dist/registry/client.js +8 -0
- package/dist/registry/db/client.d.ts +9 -9
- package/dist/registry/db/client.js +7 -7
- package/dist/registry/db/{adapter.d.ts → driver.d.ts} +16 -14
- package/dist/registry/db/{adapter.js → driver.js} +17 -15
- package/dist/registry/db/index.d.ts +1 -1
- package/dist/registry/db/index.js +9 -9
- package/dist/registry/db/migrate.d.ts +1 -1
- package/dist/registry/db/migrate.js +5 -5
- package/dist/registry/publish/index.d.ts +8 -1
- package/dist/registry/publish/index.js +88 -2
- package/dist/registry/transfer.d.ts +13 -0
- package/dist/types/config.d.ts +7 -3
- package/drizzle.config.ts +1 -1
- package/package.json +1 -1
- package/src/app/components/Foundations/DisplayLogo.tsx +3 -3
- package/src/app/lib/docs-api/runtime-config.ts +10 -10
- package/src/app/lib/registry-api/transfer.ts +29 -0
- package/src/app/lib/registry-connection.ts +2 -2
- package/src/app/next.config.mjs +1 -1
- package/src/app/pages/api/registry/transfer/[kind]/index.ts +21 -0
- package/src/app/pages/foundations/icons/index.tsx +2 -2
- package/src/app/pages/foundations/icons/{[name] → library/[name]}/index.tsx +6 -6
- package/src/app-builder/build.ts +75 -55
- package/src/app-builder/client-config.ts +3 -3
- package/src/app-builder/paths.ts +7 -9
- package/src/app-builder/static-export.ts +6 -6
- package/src/app-builder/vercel-output.ts +2 -2
- package/src/artifacts/url.ts +7 -7
- package/src/cli/create.ts +58 -3
- package/src/commands/checkout/index.ts +10 -15
- package/src/commands/publish/index.ts +10 -14
- package/src/commands/utils.ts +12 -0
- package/src/index.ts +26 -12
- package/src/registry/asset-storage/resolve.ts +1 -1
- package/src/registry/checkout/index.ts +73 -11
- package/src/registry/client.ts +7 -1
- package/src/registry/db/client.ts +12 -12
- package/src/registry/db/{adapter.ts → driver.ts} +19 -17
- package/src/registry/db/index.ts +4 -4
- package/src/registry/db/migrate.ts +5 -5
- package/src/registry/publish/index.ts +100 -2
- package/src/registry/transfer.ts +14 -0
- package/src/types/config.ts +7 -3
- package/templates/default/README.md.tpl +40 -1
- package/templates/default/gitignore.tpl +18 -3
- package/templates/default/handoff.config.js.tpl +14 -0
- package/templates/default/handoff.config.ts.tpl +14 -0
- package/templates/default/handoff.config.with-examples.js.tpl +14 -0
- package/templates/default/handoff.config.with-examples.ts.tpl +14 -0
- package/templates/default/package.json.tpl +2 -1
- package/templates/default/vercel.json.tpl +4 -0
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Bundled Drizzle client + migrator for the registry database.
|
|
4
4
|
*
|
|
5
|
-
* The package ships both built-in
|
|
5
|
+
* The package ships both built-in drivers (`pg` and `neon`) and the Drizzle client/migrator, so a
|
|
6
6
|
* consuming project needs no global Drizzle install or separate CLI. Driver modules are loaded
|
|
7
|
-
* lazily (dynamic import) for the selected
|
|
7
|
+
* lazily (dynamic import) for the selected driver only, so workspace dev/build never pulls the
|
|
8
8
|
* Postgres/Neon drivers into memory when the registry is not in use.
|
|
9
9
|
*/
|
|
10
10
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
@@ -43,13 +43,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
43
43
|
exports.createRegistryDbConnection = void 0;
|
|
44
44
|
const schema = __importStar(require("./schema"));
|
|
45
45
|
/**
|
|
46
|
-
* Create a registry database connection for the resolved
|
|
46
|
+
* Create a registry database connection for the resolved driver. Both drivers target the
|
|
47
47
|
* Postgres dialect and share the same schema, so the returned connection is interchangeable for
|
|
48
48
|
* migration and (later) the registry store.
|
|
49
49
|
*/
|
|
50
|
-
const createRegistryDbConnection = (_a) => __awaiter(void 0, [_a], void 0, function* ({
|
|
50
|
+
const createRegistryDbConnection = (_a) => __awaiter(void 0, [_a], void 0, function* ({ driver, connectionString, }) {
|
|
51
51
|
var _b;
|
|
52
|
-
if (
|
|
52
|
+
if (driver === 'neon') {
|
|
53
53
|
const { Pool, neonConfig } = yield Promise.resolve().then(() => __importStar(require('@neondatabase/serverless')));
|
|
54
54
|
// Neon's serverless driver needs a WebSocket constructor in Node; reuse the bundled `ws`.
|
|
55
55
|
const ws = yield Promise.resolve().then(() => __importStar(require('ws')));
|
|
@@ -66,7 +66,7 @@ const createRegistryDbConnection = (_a) => __awaiter(void 0, [_a], void 0, funct
|
|
|
66
66
|
}
|
|
67
67
|
const db = drizzle(pool, { schema });
|
|
68
68
|
return {
|
|
69
|
-
|
|
69
|
+
driver,
|
|
70
70
|
db,
|
|
71
71
|
migrate: (migrationsFolder) => migrate(db, { migrationsFolder }),
|
|
72
72
|
close: () => pool.end(),
|
|
@@ -85,7 +85,7 @@ const createRegistryDbConnection = (_a) => __awaiter(void 0, [_a], void 0, funct
|
|
|
85
85
|
}
|
|
86
86
|
const db = drizzle(pool, { schema });
|
|
87
87
|
return {
|
|
88
|
-
|
|
88
|
+
driver,
|
|
89
89
|
db,
|
|
90
90
|
migrate: (migrationsFolder) => migrate(db, { migrationsFolder }),
|
|
91
91
|
close: () => pool.end(),
|
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Registry database
|
|
2
|
+
* Registry database driver resolution.
|
|
3
3
|
*
|
|
4
|
-
* `runtime.registry.database.
|
|
5
|
-
* `handoff-app` package — `pg` (default) and `neon` —
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* PostgreSQL is the supported database. `runtime.registry.database.driver` selects one of two
|
|
5
|
+
* built-in connection drivers shipped with the `handoff-app` package — `pg` (default) and `neon` —
|
|
6
|
+
* both targeting the same Postgres dialect over one package-owned schema and migration set. The
|
|
7
|
+
* driver picks *how* to connect, not *which* database engine to use. `build --target registry` and
|
|
8
|
+
* `db:migrate` resolve the **same** driver so build and migration never diverge. Connection
|
|
9
|
+
* credentials are referenced by environment-variable name only and resolved to a value at runtime;
|
|
10
|
+
* the value is never stored in config.
|
|
9
11
|
*/
|
|
10
12
|
import type { Config } from '../../types/config';
|
|
11
|
-
/** Built-in registry database
|
|
12
|
-
export type
|
|
13
|
+
/** Built-in registry database connection driver. Both ship with the package and target Postgres. */
|
|
14
|
+
export type RegistryDatabaseDriver = 'pg' | 'neon';
|
|
13
15
|
/** Default env-var name holding the database connection string. */
|
|
14
16
|
export declare const DEFAULT_DATABASE_URL_ENV = "DATABASE_URL";
|
|
15
17
|
/** Default env-var name holding the registry management API bearer token. */
|
|
16
18
|
export declare const DEFAULT_REGISTRY_API_TOKEN_ENV = "HANDOFF_REGISTRY_API_TOKEN";
|
|
17
|
-
/** Default registry database
|
|
18
|
-
export declare const
|
|
19
|
+
/** Default registry database driver when none is configured. */
|
|
20
|
+
export declare const DEFAULT_REGISTRY_DRIVER: RegistryDatabaseDriver;
|
|
19
21
|
/** Fully resolved registry database connection inputs. */
|
|
20
22
|
export interface ResolvedRegistryDatabase {
|
|
21
|
-
/** Selected built-in
|
|
22
|
-
|
|
23
|
+
/** Selected built-in driver. */
|
|
24
|
+
driver: RegistryDatabaseDriver;
|
|
23
25
|
/** Name of the env var the connection string was read from. */
|
|
24
26
|
databaseUrlEnv: string;
|
|
25
27
|
/** Resolved connection string (never persisted in config). */
|
|
@@ -33,8 +35,8 @@ export declare const resolveDatabaseUrlEnv: (config: Config | null | undefined)
|
|
|
33
35
|
* value is resolved from the environment at request time and never written to config.
|
|
34
36
|
*/
|
|
35
37
|
export declare const resolveApiTokenEnv: (config: Config | null | undefined) => string;
|
|
36
|
-
/** Resolve the configured registry
|
|
37
|
-
export declare const
|
|
38
|
+
/** Resolve the configured registry database driver (defaults to `pg`). */
|
|
39
|
+
export declare const resolveRegistryDriver: (config: Config | null | undefined) => RegistryDatabaseDriver;
|
|
38
40
|
/**
|
|
39
41
|
* Resolve the registry database connection from config + environment. Throws an actionable error
|
|
40
42
|
* when the configured env var is unset so the CLI can surface a clear "missing database URL"
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Registry database
|
|
3
|
+
* Registry database driver resolution.
|
|
4
4
|
*
|
|
5
|
-
* `runtime.registry.database.
|
|
6
|
-
* `handoff-app` package — `pg` (default) and `neon` —
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* PostgreSQL is the supported database. `runtime.registry.database.driver` selects one of two
|
|
6
|
+
* built-in connection drivers shipped with the `handoff-app` package — `pg` (default) and `neon` —
|
|
7
|
+
* both targeting the same Postgres dialect over one package-owned schema and migration set. The
|
|
8
|
+
* driver picks *how* to connect, not *which* database engine to use. `build --target registry` and
|
|
9
|
+
* `db:migrate` resolve the **same** driver so build and migration never diverge. Connection
|
|
10
|
+
* credentials are referenced by environment-variable name only and resolved to a value at runtime;
|
|
11
|
+
* the value is never stored in config.
|
|
10
12
|
*/
|
|
11
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.resolveRegistryDatabase = exports.
|
|
14
|
+
exports.resolveRegistryDatabase = exports.resolveRegistryDriver = exports.resolveApiTokenEnv = exports.resolveDatabaseUrlEnv = exports.DEFAULT_REGISTRY_DRIVER = exports.DEFAULT_REGISTRY_API_TOKEN_ENV = exports.DEFAULT_DATABASE_URL_ENV = void 0;
|
|
13
15
|
/** Default env-var name holding the database connection string. */
|
|
14
16
|
exports.DEFAULT_DATABASE_URL_ENV = 'DATABASE_URL';
|
|
15
17
|
/** Default env-var name holding the registry management API bearer token. */
|
|
16
18
|
exports.DEFAULT_REGISTRY_API_TOKEN_ENV = 'HANDOFF_REGISTRY_API_TOKEN';
|
|
17
|
-
/** Default registry database
|
|
18
|
-
exports.
|
|
19
|
+
/** Default registry database driver when none is configured. */
|
|
20
|
+
exports.DEFAULT_REGISTRY_DRIVER = 'pg';
|
|
19
21
|
/** Resolve the env-var name holding the database URL (defaults to `DATABASE_URL`). */
|
|
20
22
|
const resolveDatabaseUrlEnv = (config) => {
|
|
21
23
|
var _a, _b, _c;
|
|
@@ -34,12 +36,12 @@ const resolveApiTokenEnv = (config) => {
|
|
|
34
36
|
return configured || exports.DEFAULT_REGISTRY_API_TOKEN_ENV;
|
|
35
37
|
};
|
|
36
38
|
exports.resolveApiTokenEnv = resolveApiTokenEnv;
|
|
37
|
-
/** Resolve the configured registry
|
|
38
|
-
const
|
|
39
|
+
/** Resolve the configured registry database driver (defaults to `pg`). */
|
|
40
|
+
const resolveRegistryDriver = (config) => {
|
|
39
41
|
var _a, _b, _c, _d;
|
|
40
|
-
return (_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.
|
|
42
|
+
return (_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.driver) !== null && _d !== void 0 ? _d : exports.DEFAULT_REGISTRY_DRIVER;
|
|
41
43
|
};
|
|
42
|
-
exports.
|
|
44
|
+
exports.resolveRegistryDriver = resolveRegistryDriver;
|
|
43
45
|
/**
|
|
44
46
|
* Resolve the registry database connection from config + environment. Throws an actionable error
|
|
45
47
|
* when the configured env var is unset so the CLI can surface a clear "missing database URL"
|
|
@@ -47,13 +49,13 @@ exports.resolveRegistryAdapter = resolveRegistryAdapter;
|
|
|
47
49
|
*/
|
|
48
50
|
const resolveRegistryDatabase = (config) => {
|
|
49
51
|
var _a;
|
|
50
|
-
const
|
|
52
|
+
const driver = (0, exports.resolveRegistryDriver)(config);
|
|
51
53
|
const databaseUrlEnv = (0, exports.resolveDatabaseUrlEnv)(config);
|
|
52
54
|
const connectionString = (_a = process.env[databaseUrlEnv]) === null || _a === void 0 ? void 0 : _a.trim();
|
|
53
55
|
if (!connectionString) {
|
|
54
56
|
throw new Error(`Registry database URL is not configured. Set the "${databaseUrlEnv}" environment variable to a ` +
|
|
55
57
|
`PostgreSQL connection string (or change "runtime.registry.databaseUrlEnv" to point at the env var you use).`);
|
|
56
58
|
}
|
|
57
|
-
return {
|
|
59
|
+
return { driver, databaseUrlEnv, connectionString };
|
|
58
60
|
};
|
|
59
61
|
exports.resolveRegistryDatabase = resolveRegistryDatabase;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { buildMetadata, componentFiles, components, docsArtifacts, patternFiles, patterns, registrySchema, type BuildMetadataEntityKind, type DocsArtifactEntityKind, type RegistryReviewMetadata, } from './schema';
|
|
2
2
|
export type { ArtifactBuildStatus as RegistryBuildStatus } from '../../artifacts/types';
|
|
3
|
-
export { DEFAULT_DATABASE_URL_ENV,
|
|
3
|
+
export { DEFAULT_DATABASE_URL_ENV, DEFAULT_REGISTRY_DRIVER, DEFAULT_REGISTRY_API_TOKEN_ENV, resolveApiTokenEnv, resolveDatabaseUrlEnv, resolveRegistryDriver, resolveRegistryDatabase, type RegistryDatabaseDriver, type ResolvedRegistryDatabase, } from './driver';
|
|
4
4
|
export { createRegistryDbConnection, type CreateRegistryDbConnectionParams, type RegistryDatabase, type RegistryDbConnection, } from './client';
|
|
5
5
|
export { getMigrationsFolder, runRegistryMigrations } from './migrate';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.runRegistryMigrations = exports.getMigrationsFolder = exports.createRegistryDbConnection = exports.resolveRegistryDatabase = exports.
|
|
3
|
+
exports.runRegistryMigrations = exports.getMigrationsFolder = exports.createRegistryDbConnection = exports.resolveRegistryDatabase = exports.resolveRegistryDriver = exports.resolveDatabaseUrlEnv = exports.resolveApiTokenEnv = exports.DEFAULT_REGISTRY_API_TOKEN_ENV = exports.DEFAULT_REGISTRY_DRIVER = exports.DEFAULT_DATABASE_URL_ENV = exports.registrySchema = exports.patterns = exports.patternFiles = exports.docsArtifacts = exports.components = exports.componentFiles = exports.buildMetadata = void 0;
|
|
4
4
|
var schema_1 = require("./schema");
|
|
5
5
|
Object.defineProperty(exports, "buildMetadata", { enumerable: true, get: function () { return schema_1.buildMetadata; } });
|
|
6
6
|
Object.defineProperty(exports, "componentFiles", { enumerable: true, get: function () { return schema_1.componentFiles; } });
|
|
@@ -9,14 +9,14 @@ Object.defineProperty(exports, "docsArtifacts", { enumerable: true, get: functio
|
|
|
9
9
|
Object.defineProperty(exports, "patternFiles", { enumerable: true, get: function () { return schema_1.patternFiles; } });
|
|
10
10
|
Object.defineProperty(exports, "patterns", { enumerable: true, get: function () { return schema_1.patterns; } });
|
|
11
11
|
Object.defineProperty(exports, "registrySchema", { enumerable: true, get: function () { return schema_1.registrySchema; } });
|
|
12
|
-
var
|
|
13
|
-
Object.defineProperty(exports, "DEFAULT_DATABASE_URL_ENV", { enumerable: true, get: function () { return
|
|
14
|
-
Object.defineProperty(exports, "
|
|
15
|
-
Object.defineProperty(exports, "DEFAULT_REGISTRY_API_TOKEN_ENV", { enumerable: true, get: function () { return
|
|
16
|
-
Object.defineProperty(exports, "resolveApiTokenEnv", { enumerable: true, get: function () { return
|
|
17
|
-
Object.defineProperty(exports, "resolveDatabaseUrlEnv", { enumerable: true, get: function () { return
|
|
18
|
-
Object.defineProperty(exports, "
|
|
19
|
-
Object.defineProperty(exports, "resolveRegistryDatabase", { enumerable: true, get: function () { return
|
|
12
|
+
var driver_1 = require("./driver");
|
|
13
|
+
Object.defineProperty(exports, "DEFAULT_DATABASE_URL_ENV", { enumerable: true, get: function () { return driver_1.DEFAULT_DATABASE_URL_ENV; } });
|
|
14
|
+
Object.defineProperty(exports, "DEFAULT_REGISTRY_DRIVER", { enumerable: true, get: function () { return driver_1.DEFAULT_REGISTRY_DRIVER; } });
|
|
15
|
+
Object.defineProperty(exports, "DEFAULT_REGISTRY_API_TOKEN_ENV", { enumerable: true, get: function () { return driver_1.DEFAULT_REGISTRY_API_TOKEN_ENV; } });
|
|
16
|
+
Object.defineProperty(exports, "resolveApiTokenEnv", { enumerable: true, get: function () { return driver_1.resolveApiTokenEnv; } });
|
|
17
|
+
Object.defineProperty(exports, "resolveDatabaseUrlEnv", { enumerable: true, get: function () { return driver_1.resolveDatabaseUrlEnv; } });
|
|
18
|
+
Object.defineProperty(exports, "resolveRegistryDriver", { enumerable: true, get: function () { return driver_1.resolveRegistryDriver; } });
|
|
19
|
+
Object.defineProperty(exports, "resolveRegistryDatabase", { enumerable: true, get: function () { return driver_1.resolveRegistryDatabase; } });
|
|
20
20
|
var client_1 = require("./client");
|
|
21
21
|
Object.defineProperty(exports, "createRegistryDbConnection", { enumerable: true, get: function () { return client_1.createRegistryDbConnection; } });
|
|
22
22
|
var migrate_1 = require("./migrate");
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Registry migration runner.
|
|
3
3
|
*
|
|
4
4
|
* `handoff-app db:migrate` reads the consuming project's config + DB env vars, resolves the same
|
|
5
|
-
* `runtime.registry.database.
|
|
5
|
+
* `runtime.registry.database.driver` used at runtime/build, and applies the package-owned
|
|
6
6
|
* migration set with the bundled Drizzle migrator. Migrations are resolved relative to the
|
|
7
7
|
* installed package (not the consuming project), so the schema is versioned with the tool. This
|
|
8
8
|
* runs independently of `build`.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Registry migration runner.
|
|
4
4
|
*
|
|
5
5
|
* `handoff-app db:migrate` reads the consuming project's config + DB env vars, resolves the same
|
|
6
|
-
* `runtime.registry.database.
|
|
6
|
+
* `runtime.registry.database.driver` used at runtime/build, and applies the package-owned
|
|
7
7
|
* migration set with the bundled Drizzle migrator. Migrations are resolved relative to the
|
|
8
8
|
* installed package (not the consuming project), so the schema is versioned with the tool. This
|
|
9
9
|
* runs independently of `build`.
|
|
@@ -25,7 +25,7 @@ exports.runRegistryMigrations = exports.getMigrationsFolder = void 0;
|
|
|
25
25
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
26
26
|
const path_1 = __importDefault(require("path"));
|
|
27
27
|
const logger_1 = require("../../utils/logger");
|
|
28
|
-
const
|
|
28
|
+
const driver_1 = require("./driver");
|
|
29
29
|
const client_1 = require("./client");
|
|
30
30
|
/**
|
|
31
31
|
* Absolute path to the package-owned migrations folder. Resolved relative to the installed
|
|
@@ -44,15 +44,15 @@ const runRegistryMigrations = (handoff) => __awaiter(void 0, void 0, void 0, fun
|
|
|
44
44
|
if (!handoff.config) {
|
|
45
45
|
throw new Error('Handoff is not initialized; cannot resolve registry database configuration.');
|
|
46
46
|
}
|
|
47
|
-
const {
|
|
47
|
+
const { driver, databaseUrlEnv, connectionString } = (0, driver_1.resolveRegistryDatabase)(handoff.config);
|
|
48
48
|
const migrationsFolder = (0, exports.getMigrationsFolder)(handoff);
|
|
49
49
|
const journalPath = path_1.default.join(migrationsFolder, 'meta', '_journal.json');
|
|
50
50
|
if (!fs_extra_1.default.existsSync(journalPath)) {
|
|
51
51
|
throw new Error(`Packaged registry migrations were not found at "${migrationsFolder}". This indicates a broken ` +
|
|
52
52
|
`handoff-app installation — reinstall the package to restore the bundled migration set.`);
|
|
53
53
|
}
|
|
54
|
-
logger_1.Logger.info(`Running registry migrations (
|
|
55
|
-
const connection = yield (0, client_1.createRegistryDbConnection)({
|
|
54
|
+
logger_1.Logger.info(`Running registry migrations (driver: "${driver}", database url from "${databaseUrlEnv}").`);
|
|
55
|
+
const connection = yield (0, client_1.createRegistryDbConnection)({ driver, connectionString });
|
|
56
56
|
try {
|
|
57
57
|
yield connection.migrate(migrationsFolder);
|
|
58
58
|
logger_1.Logger.success('Registry migrations applied successfully.');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Connected-workspace publish orchestration.
|
|
3
3
|
*
|
|
4
|
-
* `publish <
|
|
4
|
+
* `publish <components|patterns|pages> <id>` prepares one entity and uploads its record, source files,
|
|
5
5
|
* applicable rendered artifacts, and build metadata through the shared registry client.
|
|
6
6
|
*
|
|
7
7
|
* The build is targeted: a component builds the global artifacts + that component; a pattern builds
|
|
@@ -25,3 +25,10 @@ export declare const resolveConnectionOrThrow: (handoff: Handoff) => import("../
|
|
|
25
25
|
* receive a targeted build, while pages are packaged directly from their source.
|
|
26
26
|
*/
|
|
27
27
|
export declare const publishEntity: (handoff: Handoff, kind: TransferEntityKind, id: string) => Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Publish every component, pattern, or page declared in this connected workspace. Builds the kind
|
|
30
|
+
* once, assembles each entity's package, skips entities whose content hash already matches the
|
|
31
|
+
* registry (unless `--force`), and reports published/unchanged/failed counts. A per-entity failure is
|
|
32
|
+
* collected and never aborts the rest; the run throws at the end if any entity failed.
|
|
33
|
+
*/
|
|
34
|
+
export declare const publishEntities: (handoff: Handoff, kind: TransferEntityKind) => Promise<void>;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Connected-workspace publish orchestration.
|
|
4
4
|
*
|
|
5
|
-
* `publish <
|
|
5
|
+
* `publish <components|patterns|pages> <id>` prepares one entity and uploads its record, source files,
|
|
6
6
|
* applicable rendered artifacts, and build metadata through the shared registry client.
|
|
7
7
|
*
|
|
8
8
|
* The build is targeted: a component builds the global artifacts + that component; a pattern builds
|
|
@@ -22,7 +22,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
22
22
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
23
|
};
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.publishEntity = exports.resolveConnectionOrThrow = exports.PublishError = void 0;
|
|
25
|
+
exports.publishEntities = exports.publishEntity = exports.resolveConnectionOrThrow = exports.PublishError = void 0;
|
|
26
26
|
const css_1 = require("../../transformers/preview/component/css");
|
|
27
27
|
const javascript_1 = require("../../transformers/preview/component/javascript");
|
|
28
28
|
const builder_1 = __importDefault(require("../../transformers/preview/component/builder"));
|
|
@@ -124,3 +124,89 @@ const publishEntity = (handoff, kind, id) => __awaiter(void 0, void 0, void 0, f
|
|
|
124
124
|
logger_1.Logger.success(`Published ${kind} "${id}" to the registry.`);
|
|
125
125
|
});
|
|
126
126
|
exports.publishEntity = publishEntity;
|
|
127
|
+
/** The published-content hash a bulk publish compares for skip-unchanged (artifact for rendered kinds, source for pages). */
|
|
128
|
+
const entityHash = (value) => { var _a; return (_a = value.artifactHash) !== null && _a !== void 0 ? _a : value.sourceHash; };
|
|
129
|
+
/**
|
|
130
|
+
* Run one build covering every entity of the kind, so a bulk publish builds once instead of per id.
|
|
131
|
+
* Pages carry no rendered artifacts. Components need the global artifacts + all components; patterns
|
|
132
|
+
* additionally need all patterns composed (each references pre-built component HTML).
|
|
133
|
+
*/
|
|
134
|
+
const runBulkBuild = (handoff, kind) => __awaiter(void 0, void 0, void 0, function* () {
|
|
135
|
+
if (kind === 'page') {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
yield (0, javascript_1.buildMainJS)(handoff);
|
|
139
|
+
yield (0, css_1.buildMainCss)(handoff);
|
|
140
|
+
yield (0, builder_1.default)(handoff);
|
|
141
|
+
if (kind === 'pattern') {
|
|
142
|
+
yield (0, builder_2.processPatterns)(handoff);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
/** List every declared entity id of the kind from the workspace store. */
|
|
146
|
+
const listWorkspaceEntityIds = (handoff, kind) => __awaiter(void 0, void 0, void 0, function* () {
|
|
147
|
+
const store = kind === 'component' ? handoff.store.components : kind === 'pattern' ? handoff.store.patterns : handoff.store.pages;
|
|
148
|
+
return (yield store.list()).map((entity) => entity.id);
|
|
149
|
+
});
|
|
150
|
+
/**
|
|
151
|
+
* Publish every component, pattern, or page declared in this connected workspace. Builds the kind
|
|
152
|
+
* once, assembles each entity's package, skips entities whose content hash already matches the
|
|
153
|
+
* registry (unless `--force`), and reports published/unchanged/failed counts. A per-entity failure is
|
|
154
|
+
* collected and never aborts the rest; the run throws at the end if any entity failed.
|
|
155
|
+
*/
|
|
156
|
+
const publishEntities = (handoff, kind) => __awaiter(void 0, void 0, void 0, function* () {
|
|
157
|
+
const connection = (0, exports.resolveConnectionOrThrow)(handoff);
|
|
158
|
+
logger_1.Logger.info(`Building ${kind}s for publish…`);
|
|
159
|
+
yield runBulkBuild(handoff, kind);
|
|
160
|
+
const ids = yield listWorkspaceEntityIds(handoff, kind);
|
|
161
|
+
if (ids.length === 0) {
|
|
162
|
+
logger_1.Logger.success(`No ${kind}s are declared in this workspace; nothing to publish.`);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const client = (0, client_1.createRegistryClient)({ baseUrl: connection.url, accessToken: connection.accessToken });
|
|
166
|
+
let remote = [];
|
|
167
|
+
if (!handoff.force) {
|
|
168
|
+
try {
|
|
169
|
+
remote = yield client.listEntities(kind);
|
|
170
|
+
}
|
|
171
|
+
catch (_a) {
|
|
172
|
+
// If the summary listing fails we simply publish everything (the server still overwrites in place).
|
|
173
|
+
logger_1.Logger.info(`Could not read current registry ${kind}s; publishing all.`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const remoteHashById = new Map(remote.map((entry) => [entry.id, entityHash(entry)]));
|
|
177
|
+
let published = 0;
|
|
178
|
+
let unchanged = 0;
|
|
179
|
+
const failed = [];
|
|
180
|
+
for (const id of ids) {
|
|
181
|
+
try {
|
|
182
|
+
const pkg = yield (0, package_1.buildPublishPackage)(handoff, kind, id);
|
|
183
|
+
(0, package_1.assertRequiredArtifactsPresent)(pkg);
|
|
184
|
+
const remoteHash = remoteHashById.get(id);
|
|
185
|
+
const localHash = entityHash(pkg.build);
|
|
186
|
+
if (!handoff.force && remoteHash && localHash && remoteHash === localHash) {
|
|
187
|
+
unchanged += 1;
|
|
188
|
+
logger_1.Logger.info(`Unchanged: ${id}`);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
yield client.publish(kind, id, pkg);
|
|
192
|
+
published += 1;
|
|
193
|
+
logger_1.Logger.info(`Published: ${id} (${pkg.artifacts.length} artifact(s), ${pkg.files.length} source file(s))`);
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
const message = error instanceof client_1.RegistryClientError
|
|
197
|
+
? (0, errors_1.describeUploadFailure)(error, connection.url, 'package')
|
|
198
|
+
: error instanceof Error
|
|
199
|
+
? error.message
|
|
200
|
+
: String(error);
|
|
201
|
+
failed.push({ id, message });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
logger_1.Logger.success(`${kind[0].toUpperCase()}${kind.slice(1)}s publish complete — ${published} published, ${unchanged} unchanged${failed.length ? `, ${failed.length} failed` : ''}.`);
|
|
205
|
+
if (failed.length > 0) {
|
|
206
|
+
for (const failure of failed) {
|
|
207
|
+
logger_1.Logger.error(` - ${failure.id}: ${failure.message}`);
|
|
208
|
+
}
|
|
209
|
+
throw new PublishError(`${failed.length} ${kind}(s) failed to publish.`);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
exports.publishEntities = publishEntities;
|
|
@@ -73,3 +73,16 @@ export interface CheckoutPayload {
|
|
|
73
73
|
item: Record<string, unknown>;
|
|
74
74
|
files: TransferFile[];
|
|
75
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* A published entity's summary from `GET /api/registry/transfer/{component|pattern|page}`. Carries
|
|
78
|
+
* the build hashes so a connected workspace can skip unchanged entities on a bulk publish and
|
|
79
|
+
* enumerate published ids for a bulk checkout. Components/patterns key on `artifactHash`; pages key
|
|
80
|
+
* on `sourceHash`.
|
|
81
|
+
*/
|
|
82
|
+
export interface EntitySummary {
|
|
83
|
+
id: string;
|
|
84
|
+
kind: TransferEntityKind;
|
|
85
|
+
status?: string;
|
|
86
|
+
artifactHash?: string;
|
|
87
|
+
sourceHash?: string;
|
|
88
|
+
}
|
package/dist/types/config.d.ts
CHANGED
|
@@ -139,8 +139,12 @@ export interface HandoffRuntimeConfig {
|
|
|
139
139
|
/** Name of the env var holding the registry API token. @default "HANDOFF_REGISTRY_API_TOKEN" */
|
|
140
140
|
apiTokenEnv?: string;
|
|
141
141
|
database?: {
|
|
142
|
-
/**
|
|
143
|
-
|
|
142
|
+
/**
|
|
143
|
+
* PostgreSQL is the supported database. `driver` selects the built-in connection driver — how
|
|
144
|
+
* to connect, not which engine — over the same package-owned Postgres schema and migrations.
|
|
145
|
+
* @default "pg"
|
|
146
|
+
*/
|
|
147
|
+
driver?: 'pg' | 'neon';
|
|
144
148
|
};
|
|
145
149
|
/**
|
|
146
150
|
* Where published asset blobs (icons/logos/fonts/sprites/archives) are stored. Defaults to the
|
|
@@ -214,7 +218,7 @@ export interface Config {
|
|
|
214
218
|
*/
|
|
215
219
|
entries?: ConfigEntries;
|
|
216
220
|
/**
|
|
217
|
-
* Override URLs for the asset zip download links. When unset, each link defaults to the
|
|
221
|
+
* Override URLs for the asset zip download links. When unset, each link defaults to the
|
|
218
222
|
* basePath-aware asset route `{basePath}/api/docs/assets/{collection}/{collection}.zip`, served by
|
|
219
223
|
* the docs read API in every runtime mode (from the DB in registry mode, from statically
|
|
220
224
|
* materialized files in a static export).
|
package/drizzle.config.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { defineConfig } from 'drizzle-kit';
|
|
|
5
5
|
*
|
|
6
6
|
* Migrations are generated into `./drizzle` and shipped with the `handoff-app` package; the
|
|
7
7
|
* bundled runner (`handoff-app db:migrate`) applies them at install/deploy time. Both built-in
|
|
8
|
-
*
|
|
8
|
+
* drivers (`pg`, `neon`) target the Postgres dialect, so a single migration set serves both.
|
|
9
9
|
*
|
|
10
10
|
* Generate after schema changes with: `npx drizzle-kit generate`
|
|
11
11
|
*/
|
package/package.json
CHANGED
|
@@ -2,9 +2,9 @@ import React from 'react';
|
|
|
2
2
|
import type { DisplayableAsset } from '../util/useCollectionAssets';
|
|
3
3
|
|
|
4
4
|
export const DisplayLogo: React.FC<{ logo: DisplayableAsset; content?: string; correct?: boolean }> = ({ logo, content }) => {
|
|
5
|
-
// Workspace/static ships the SVG body inline in tokens.json (`data`)
|
|
6
|
-
//
|
|
7
|
-
// Registry has no inline body
|
|
5
|
+
// Workspace/static ships the SVG body inline in tokens.json (`data`), so render it directly and
|
|
6
|
+
// avoid depending on the fetch-only `/api/assets/logos/*` files that a static host won't have.
|
|
7
|
+
// Registry has no inline body, so it falls back to the individual asset content URL (`src`).
|
|
8
8
|
const src = logo.src ?? `${process.env.HANDOFF_APP_BASE_PATH ?? ''}/api/assets/logos/${logo.path}`;
|
|
9
9
|
return (
|
|
10
10
|
<div className="flex flex-col gap-5">
|
|
@@ -3,10 +3,10 @@ import path from 'path';
|
|
|
3
3
|
import type { RuntimeMode } from '@handoff/types/config';
|
|
4
4
|
import {
|
|
5
5
|
DEFAULT_DATABASE_URL_ENV,
|
|
6
|
-
|
|
6
|
+
DEFAULT_REGISTRY_DRIVER,
|
|
7
7
|
DEFAULT_REGISTRY_API_TOKEN_ENV,
|
|
8
|
-
type
|
|
9
|
-
} from '@handoff/registry/db/
|
|
8
|
+
type RegistryDatabaseDriver,
|
|
9
|
+
} from '@handoff/registry/db/driver';
|
|
10
10
|
import { DEFAULT_ASSET_STORAGE_ADAPTER, type AssetStorageSettings } from '@handoff/registry/asset-storage/resolve';
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -15,7 +15,7 @@ import { DEFAULT_ASSET_STORAGE_ADAPTER, type AssetStorageSettings } from '@hando
|
|
|
15
15
|
* The docs read API is mode-aware: workspace mode resolves artifacts from generated filesystem
|
|
16
16
|
* files, registry mode resolves them from the database. The browser-facing `client.config.json`
|
|
17
17
|
* carries only the resolved {@link RuntimeMode} (no connection details), so the server needs its
|
|
18
|
-
* own non-secret source for the registry connection *inputs* — the selected
|
|
18
|
+
* own non-secret source for the registry connection *inputs* — the selected driver and the *name*
|
|
19
19
|
* of the env var holding the database URL. The build persists these to `runtime.server.json`
|
|
20
20
|
* alongside `client.config.json`; this module reads them back, never crossing secrets to the
|
|
21
21
|
* browser (the connection-string value is resolved from the env var at request time, not here).
|
|
@@ -25,8 +25,8 @@ import { DEFAULT_ASSET_STORAGE_ADAPTER, type AssetStorageSettings } from '@hando
|
|
|
25
25
|
|
|
26
26
|
/** Non-secret registry connection inputs resolved on the server. */
|
|
27
27
|
export interface ServerRegistryRuntimeConfig {
|
|
28
|
-
/** Selected built-in database
|
|
29
|
-
|
|
28
|
+
/** Selected built-in database driver. */
|
|
29
|
+
driver: RegistryDatabaseDriver;
|
|
30
30
|
/** Name of the env var holding the database connection string (value resolved at request time). */
|
|
31
31
|
databaseUrlEnv: string;
|
|
32
32
|
/**
|
|
@@ -49,11 +49,11 @@ export interface ServerRuntimeConfig {
|
|
|
49
49
|
|
|
50
50
|
let cached: ServerRuntimeConfig | null = null;
|
|
51
51
|
|
|
52
|
-
/** Safe defaults: workspace mode with the default Postgres
|
|
52
|
+
/** Safe defaults: workspace mode with the default Postgres driver + `DATABASE_URL` env var. */
|
|
53
53
|
const defaults = (): ServerRuntimeConfig => ({
|
|
54
54
|
mode: 'workspace',
|
|
55
55
|
registry: {
|
|
56
|
-
|
|
56
|
+
driver: DEFAULT_REGISTRY_DRIVER,
|
|
57
57
|
databaseUrlEnv: DEFAULT_DATABASE_URL_ENV,
|
|
58
58
|
apiTokenEnv: DEFAULT_REGISTRY_API_TOKEN_ENV,
|
|
59
59
|
},
|
|
@@ -112,7 +112,7 @@ const fromEnv = (): ServerRuntimeConfig | null => {
|
|
|
112
112
|
return {
|
|
113
113
|
mode: mode === 'registry' ? 'registry' : 'workspace',
|
|
114
114
|
registry: {
|
|
115
|
-
|
|
115
|
+
driver: process.env.HANDOFF_REGISTRY_DRIVER?.trim() === 'neon' ? 'neon' : 'pg',
|
|
116
116
|
databaseUrlEnv,
|
|
117
117
|
apiTokenEnv,
|
|
118
118
|
},
|
|
@@ -156,7 +156,7 @@ export const getServerRuntimeConfig = (): ServerRuntimeConfig => {
|
|
|
156
156
|
cached = {
|
|
157
157
|
mode: parsed?.mode === 'registry' ? 'registry' : 'workspace',
|
|
158
158
|
registry: {
|
|
159
|
-
|
|
159
|
+
driver: parsed?.registry?.driver === 'neon' ? 'neon' : 'pg',
|
|
160
160
|
databaseUrlEnv,
|
|
161
161
|
apiTokenEnv,
|
|
162
162
|
},
|
|
@@ -432,6 +432,35 @@ const upsertBuildMetadata = async (db: RegistryDatabase, kind: TransferEntityKin
|
|
|
432
432
|
.onConflictDoUpdate({ target: [buildMetadata.entityKind, buildMetadata.entityId], set: values as any });
|
|
433
433
|
};
|
|
434
434
|
|
|
435
|
+
/**
|
|
436
|
+
* Handles `GET /api/registry/transfer/{component|pattern|page}`. Lists summaries for one kind of
|
|
437
|
+
* entity: each id plus its build hashes and status. A connected workspace uses these to skip
|
|
438
|
+
* unchanged entities on a bulk publish and to enumerate published ids for a bulk checkout. The read
|
|
439
|
+
* is unauthenticated; only the registry-runtime and method guards apply.
|
|
440
|
+
*/
|
|
441
|
+
export const handleEntitySummaryRoute = (req: NextApiRequest, res: NextApiResponse, kind: TransferEntityKind): Promise<void> =>
|
|
442
|
+
handleRegistryRoute(req, res, ['GET'], async ({ db }) => {
|
|
443
|
+
const table = ENTITY[kind].table;
|
|
444
|
+
const rows = await db
|
|
445
|
+
.select({
|
|
446
|
+
id: table.id,
|
|
447
|
+
status: buildMetadata.status,
|
|
448
|
+
artifactHash: buildMetadata.artifactHash,
|
|
449
|
+
sourceHash: buildMetadata.sourceHash,
|
|
450
|
+
})
|
|
451
|
+
.from(table)
|
|
452
|
+
.leftJoin(buildMetadata, and(eq(buildMetadata.entityKind, kind), eq(buildMetadata.entityId, table.id)));
|
|
453
|
+
|
|
454
|
+
const entities = rows.map((row) => ({
|
|
455
|
+
id: row.id,
|
|
456
|
+
kind,
|
|
457
|
+
status: row.status ?? undefined,
|
|
458
|
+
artifactHash: row.artifactHash ?? undefined,
|
|
459
|
+
sourceHash: row.sourceHash ?? undefined,
|
|
460
|
+
}));
|
|
461
|
+
sendRegistryData(res, 200, { entities }, buildMeta());
|
|
462
|
+
});
|
|
463
|
+
|
|
435
464
|
/**
|
|
436
465
|
* Handle `GET /api/registry/transfer/{component|pattern}/:id` — checkout read. Returns the
|
|
437
466
|
* normalized record plus its registry-safe source files so a connected workspace can reconstruct
|
|
@@ -7,7 +7,7 @@ import { getServerRuntimeConfig } from './docs-api/runtime-config';
|
|
|
7
7
|
* Both the registry-mode docs read API and the registry management API resolve their database
|
|
8
8
|
* through this single helper so one connection pool backs the whole registry runtime rather than
|
|
9
9
|
* one pool per consumer. The connection string is read from the configured env var *name* at
|
|
10
|
-
* request time — never persisted in config — and the selected
|
|
10
|
+
* request time — never persisted in config — and the selected driver comes from the server runtime
|
|
11
11
|
* config baked at build time.
|
|
12
12
|
*
|
|
13
13
|
* Server-only: imported exclusively by registry-mode API route helpers, which are themselves only
|
|
@@ -48,7 +48,7 @@ export const getRegistryConnection = (): Promise<RegistryDbConnection> => {
|
|
|
48
48
|
)
|
|
49
49
|
);
|
|
50
50
|
}
|
|
51
|
-
connectionPromise = createRegistryDbConnection({
|
|
51
|
+
connectionPromise = createRegistryDbConnection({ driver: registry.driver, connectionString }).catch((error) => {
|
|
52
52
|
connectionPromise = null;
|
|
53
53
|
// TODO: Route server errors through the shared logger.
|
|
54
54
|
console.error('Registry database connection failed.', error);
|
package/src/app/next.config.mjs
CHANGED
|
@@ -95,7 +95,7 @@ const nextConfig = {
|
|
|
95
95
|
// config-only — these are derived from `runtime.*`, not inferred from env at runtime. The DB
|
|
96
96
|
// connection string itself is read from the named env var at request time (deployment-supplied).
|
|
97
97
|
HANDOFF_RUNTIME_MODE: '%HANDOFF_RUNTIME_MODE%',
|
|
98
|
-
|
|
98
|
+
HANDOFF_REGISTRY_DRIVER: '%HANDOFF_REGISTRY_DRIVER%',
|
|
99
99
|
HANDOFF_REGISTRY_DATABASE_URL_ENV: '%HANDOFF_REGISTRY_DATABASE_URL_ENV%',
|
|
100
100
|
HANDOFF_REGISTRY_API_TOKEN_ENV: '%HANDOFF_REGISTRY_API_TOKEN_ENV%',
|
|
101
101
|
// Asset storage selection (provider + non-secret options + env-var names). Secret values (Blob
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
2
|
+
import { singleQueryValue } from '@/lib/api/query';
|
|
3
|
+
import { handleRegistryRoute, sendRegistryError } from '@/lib/registry-api';
|
|
4
|
+
import { handleEntitySummaryRoute } from '@/lib/registry-api/transfer';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `GET /api/registry/transfer/{component|pattern|page}`. Lists the registry's published entity
|
|
8
|
+
* summaries for one kind (id, build hashes, status) so a connected workspace can skip unchanged
|
|
9
|
+
* entities on a bulk publish and enumerate published ids for a bulk checkout. Registry-runtime only,
|
|
10
|
+
* enforced by the guard stack. The `tokens` and `assets` summaries have their own static routes,
|
|
11
|
+
* which Next resolves before this dynamic segment.
|
|
12
|
+
*/
|
|
13
|
+
export default function handler(req: NextApiRequest, res: NextApiResponse): Promise<void> {
|
|
14
|
+
const kind = singleQueryValue(req.query.kind);
|
|
15
|
+
if (kind !== 'component' && kind !== 'pattern' && kind !== 'page') {
|
|
16
|
+
return handleRegistryRoute(req, res, ['GET'], async () => {
|
|
17
|
+
sendRegistryError(res, 'not_found', `Unknown transfer entity kind "${kind ?? ''}".`);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return handleEntitySummaryRoute(req, res, kind);
|
|
21
|
+
}
|