handoff-app 2.0.0-4 → 2.0.0-6
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/AGENTS.md +7 -0
- package/CLAUDE.md +7 -0
- package/README.md +346 -377
- package/dist/app-builder/build.js +11 -4
- package/dist/app-builder/client-config.js +1 -0
- package/dist/app-builder/nav-shell.js +8 -15
- package/dist/app-builder/vercel-output.js +4 -4
- package/dist/cli/make.js +2 -2
- package/dist/cli.js +38 -6
- package/dist/commands/build/app.js +1 -5
- package/dist/commands/build/components.js +1 -5
- package/dist/commands/build/index.js +1 -5
- package/dist/commands/checkout/index.d.ts +15 -12
- package/dist/commands/checkout/index.js +15 -54
- package/dist/commands/compat/deprecations.d.ts +46 -0
- package/dist/commands/compat/deprecations.js +62 -0
- package/dist/commands/compat/push-args.d.ts +48 -0
- package/dist/commands/compat/push-args.js +73 -0
- package/dist/commands/db/migrate.js +1 -5
- package/dist/commands/dev/index.js +1 -5
- package/dist/commands/eject/config.js +1 -5
- package/dist/commands/eject/pages.js +1 -5
- package/dist/commands/eject/theme.js +1 -5
- package/dist/commands/fetch/index.js +1 -5
- package/dist/commands/index.js +4 -0
- package/dist/commands/login/index.js +1 -5
- package/dist/commands/logout/index.js +1 -5
- package/dist/commands/make/component.js +1 -5
- package/dist/commands/make/page.js +3 -7
- package/dist/commands/make/template.js +1 -5
- package/dist/commands/publish/index.d.ts +18 -12
- package/dist/commands/publish/index.js +18 -54
- package/dist/commands/pull/index.d.ts +13 -0
- package/dist/commands/pull/index.js +20 -0
- package/dist/commands/push/index.d.ts +23 -0
- package/dist/commands/push/index.js +58 -0
- package/dist/commands/scaffold/index.js +1 -5
- package/dist/commands/start/index.js +1 -5
- package/dist/commands/types.d.ts +10 -0
- package/dist/commands/utils.d.ts +74 -6
- package/dist/commands/utils.js +89 -10
- package/dist/commands/validate/components.js +1 -5
- package/dist/config/defaults.d.ts +2 -0
- package/dist/config/defaults.js +4 -1
- package/dist/config/index.d.ts +3 -2
- package/dist/config/index.js +3 -1
- package/dist/config/loader.d.ts +19 -6
- package/dist/config/loader.js +43 -13
- package/dist/config/normalizers/page.d.ts +4 -4
- package/dist/config/normalizers/page.js +2 -2
- package/dist/config/runtime.js +9 -25
- package/dist/index.d.ts +69 -22
- package/dist/index.js +148 -35
- package/dist/nav/resolver.js +3 -1
- package/dist/nav/workspace.js +2 -2
- package/dist/registry/auth/access-tokens.d.ts +11 -3
- package/dist/registry/auth/access-tokens.js +29 -3
- package/dist/registry/auth/index.d.ts +4 -3
- package/dist/registry/auth/index.js +10 -2
- package/dist/registry/auth/sync-secret.d.ts +27 -0
- package/dist/registry/auth/sync-secret.js +65 -0
- package/dist/registry/auth/types.d.ts +20 -1
- package/dist/registry/auth/types.js +3 -1
- package/dist/registry/auth/validation.d.ts +11 -0
- package/dist/registry/auth/validation.js +18 -1
- package/dist/registry/checkout/assets.d.ts +1 -1
- package/dist/registry/checkout/assets.js +21 -6
- package/dist/registry/checkout/index.d.ts +8 -1
- package/dist/registry/checkout/index.js +39 -6
- package/dist/registry/checkout/tokens.d.ts +1 -1
- package/dist/registry/checkout/tokens.js +12 -5
- package/dist/registry/connection.d.ts +10 -0
- package/dist/registry/connection.js +52 -7
- package/dist/registry/content-kinds.d.ts +27 -0
- package/dist/registry/content-kinds.js +33 -0
- package/dist/registry/db/client.d.ts +6 -1
- package/dist/registry/db/client.js +16 -3
- package/dist/registry/db/schema.d.ts +37 -3
- package/dist/registry/db/schema.js +9 -7
- package/dist/registry/publish/assets.d.ts +2 -2
- package/dist/registry/publish/assets.js +46 -35
- package/dist/registry/publish/index.d.ts +29 -5
- package/dist/registry/publish/index.js +87 -34
- package/dist/registry/publish/tokens.d.ts +2 -2
- package/dist/registry/publish/tokens.js +30 -24
- package/dist/registry/selection.d.ts +12 -0
- package/dist/registry/selection.js +22 -0
- package/dist/store/registry.d.ts +48 -0
- package/dist/store/registry.js +75 -12
- package/dist/transformers/preview/types.d.ts +2 -2
- package/dist/types/config.d.ts +7 -0
- package/dist/utils/markdown.d.ts +10 -0
- package/dist/utils/markdown.js +17 -0
- package/dist/utils/menu-shell.js +14 -7
- package/dist/utils/pages.d.ts +22 -0
- package/dist/utils/pages.js +73 -0
- package/dist/utils/path.d.ts +5 -0
- package/dist/utils/path.js +9 -0
- package/docs/api.md +1 -3
- package/docs/cli.md +1 -1
- package/drizzle/0002_add_pages.sql +5 -3
- package/drizzle/meta/0002_snapshot.json +17 -8
- package/drizzle/meta/0003_snapshot.json +17 -8
- package/drizzle/meta/0004_snapshot.json +17 -8
- package/drizzle/meta/0005_snapshot.json +17 -8
- package/package.json +4 -2
- package/src/app/components/CopyToClipboard.tsx +27 -26
- package/src/app/components/Layout/Header.tsx +4 -12
- package/src/app/components/Layout/RuntimeModeBadge.tsx +18 -0
- package/src/app/components/McpIntegration/McpConfigDialog.tsx +349 -0
- package/src/app/components/McpIntegration/McpIcon.tsx +10 -0
- package/src/app/components/Navigation/MobileNav.tsx +3 -1
- package/src/app/components/ui/dialog.tsx +73 -0
- package/src/app/components/util/index.ts +5 -23
- package/src/app/lib/docs-api/backend.ts +16 -1
- package/src/app/lib/docs-api/errors.ts +8 -7
- package/src/app/lib/docs-api/handler.ts +2 -3
- package/src/app/lib/docs-api/index.ts +2 -0
- package/src/app/lib/docs-api/page-discovery.ts +121 -0
- package/src/app/lib/docs-api/page-rendering.ts +37 -0
- package/src/app/lib/docs-api/page-search.ts +191 -0
- package/src/app/lib/docs-api/records.ts +31 -24
- package/src/app/lib/docs-api/registry.ts +92 -11
- package/src/app/lib/docs-api/runtime-config.ts +5 -0
- package/src/app/lib/docs-api/search.ts +120 -0
- package/src/app/lib/mcp/auth.ts +64 -0
- package/src/app/lib/mcp/index.ts +2 -0
- package/src/app/lib/mcp/server.ts +343 -0
- package/src/app/lib/mcp/shape.ts +595 -0
- package/src/app/lib/registry-api/allowlist.ts +7 -5
- package/src/app/lib/registry-api/asset-transfer.ts +58 -31
- package/src/app/lib/registry-api/auth.ts +89 -9
- package/src/app/lib/registry-api/entity-routes.ts +17 -7
- package/src/app/lib/registry-api/errors.ts +10 -4
- package/src/app/lib/registry-api/handler.ts +13 -15
- package/src/app/lib/registry-api/meta.ts +4 -0
- package/src/app/lib/registry-api/revalidate.ts +31 -12
- package/src/app/lib/registry-api/store.ts +89 -26
- package/src/app/lib/registry-api/sync-compat.ts +166 -0
- package/src/app/lib/registry-api/token-transfer.ts +44 -12
- package/src/app/lib/registry-api/transfer-batch.ts +116 -0
- package/src/app/lib/registry-api/transfer.ts +62 -35
- package/src/app/lib/registry-api/validation.ts +33 -1
- package/src/app/lib/registry-connection.ts +1 -1
- package/src/app/next.config.mjs +9 -0
- package/src/app/pages/[...slug]/index.tsx +52 -51
- package/src/app/pages/api/account/tokens/index.ts +4 -13
- package/src/app/pages/api/docs/search/pages.json.ts +25 -0
- package/src/app/pages/api/mcp.ts +56 -0
- package/src/app/pages/api/oauth/revoke.ts +14 -7
- package/src/app/pages/api/registry/components/[id]/files/[...filePath].ts +1 -1
- package/src/app/pages/api/registry/components/[id].ts +2 -2
- package/src/app/pages/api/registry/components/index.ts +1 -1
- package/src/app/pages/api/registry/pages/[...id].ts +20 -0
- package/src/app/pages/api/registry/pages/index.ts +19 -0
- package/src/app/pages/api/registry/patterns/[id]/files/[...filePath].ts +1 -1
- package/src/app/pages/api/registry/patterns/[id].ts +2 -2
- package/src/app/pages/api/registry/patterns/index.ts +1 -1
- package/src/app/pages/api/registry/transfer/assets/blobs/have.ts +2 -2
- package/src/app/pages/api/sync/upload.ts +21 -0
- package/src/app/pages/index/index.tsx +14 -0
- package/src/app/pages/index.tsx +31 -5
- package/src/app-builder/build.ts +11 -4
- package/src/app-builder/client-config.ts +2 -1
- package/src/app-builder/nav-shell.ts +8 -13
- package/src/app-builder/vercel-output.ts +4 -4
- package/src/cli/make.ts +2 -2
- package/src/cli.ts +30 -6
- package/src/commands/build/app.ts +2 -3
- package/src/commands/build/components.ts +2 -3
- package/src/commands/build/index.ts +2 -3
- package/src/commands/checkout/index.ts +18 -44
- package/src/commands/compat/deprecations.ts +79 -0
- package/src/commands/compat/push-args.ts +106 -0
- package/src/commands/db/migrate.ts +2 -3
- package/src/commands/dev/index.ts +2 -3
- package/src/commands/eject/config.ts +2 -3
- package/src/commands/eject/pages.ts +2 -3
- package/src/commands/eject/theme.ts +2 -3
- package/src/commands/fetch/index.ts +2 -3
- package/src/commands/index.ts +4 -0
- package/src/commands/login/index.ts +2 -3
- package/src/commands/logout/index.ts +2 -3
- package/src/commands/make/component.ts +2 -3
- package/src/commands/make/page.ts +4 -5
- package/src/commands/make/template.ts +2 -3
- package/src/commands/publish/index.ts +21 -44
- package/src/commands/pull/index.ts +32 -0
- package/src/commands/push/index.ts +115 -0
- package/src/commands/scaffold/index.ts +2 -3
- package/src/commands/start/index.ts +2 -3
- package/src/commands/types.ts +11 -1
- package/src/commands/utils.ts +80 -11
- package/src/commands/validate/components.ts +2 -3
- package/src/config/defaults.ts +3 -0
- package/src/config/index.ts +3 -2
- package/src/config/loader.ts +57 -12
- package/src/config/normalizers/page.ts +4 -4
- package/src/config/runtime.ts +9 -24
- package/src/index.ts +183 -42
- package/src/nav/resolver.ts +3 -1
- package/src/nav/workspace.ts +2 -2
- package/src/registry/auth/access-tokens.ts +29 -5
- package/src/registry/auth/index.ts +7 -1
- package/src/registry/auth/sync-secret.ts +67 -0
- package/src/registry/auth/types.ts +24 -1
- package/src/registry/auth/validation.ts +18 -0
- package/src/registry/checkout/assets.ts +24 -7
- package/src/registry/checkout/index.ts +41 -5
- package/src/registry/checkout/tokens.ts +12 -6
- package/src/registry/connection.ts +53 -5
- package/src/registry/content-kinds.ts +39 -0
- package/src/registry/db/client.ts +22 -2
- package/src/registry/db/schema.ts +9 -7
- package/src/registry/publish/assets.ts +47 -37
- package/src/registry/publish/index.ts +102 -38
- package/src/registry/publish/tokens.ts +35 -26
- package/src/registry/selection.ts +19 -0
- package/src/store/registry.ts +118 -13
- package/src/transformers/preview/types.ts +2 -2
- package/src/types/config.ts +7 -0
- package/src/utils/README.md +1 -0
- package/src/utils/markdown.ts +11 -0
- package/src/utils/menu-shell.ts +14 -7
- package/src/utils/pages.ts +52 -0
- package/src/utils/path.ts +15 -6
- package/templates/default/README.md.tpl +10 -0
- package/templates/default/handoff.config.js.tpl +6 -3
- package/templates/default/handoff.config.ts.tpl +6 -3
- package/templates/default/handoff.config.with-examples.js.tpl +6 -3
- package/templates/default/handoff.config.with-examples.ts.tpl +6 -3
- package/templates/default/package.json.tpl +4 -0
- package/config/docs/foundations/logos.md +0 -7
- package/src/app/lib/api/redact.ts +0 -46
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Logical content-kind vocabulary: the dependency-light boundary shared by the CLI, the
|
|
4
|
+
* publish/checkout orchestration, and the compatibility layer.
|
|
5
|
+
*
|
|
6
|
+
* A workspace publishes and checks out five kinds of content. Components, patterns and pages are
|
|
7
|
+
* entities with a singular wire kind ({@link TransferEntityKind}); tokens and assets travel through
|
|
8
|
+
* their own transfer endpoints. This module owns only the stable plural vocabulary and its ordering,
|
|
9
|
+
* so it stays free of fs, DB and React dependencies.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.isRegistryEntityKind = exports.ALL_KIND_ORDER = exports.ENTITY_KIND_ORDER = exports.ENTITY_WIRE_KIND = exports.REGISTRY_ENTITY_KINDS = exports.HOME_PAGE_PATH = exports.HOME_PAGE_ID = void 0;
|
|
13
|
+
/** Stable identity used to publish `pages/index.md` while serving it at the root route. */
|
|
14
|
+
exports.HOME_PAGE_ID = 'index';
|
|
15
|
+
exports.HOME_PAGE_PATH = '/';
|
|
16
|
+
/** Plural content kinds accepted wherever a publish or checkout target is named. */
|
|
17
|
+
exports.REGISTRY_ENTITY_KINDS = ['components', 'patterns', 'pages', 'tokens', 'assets'];
|
|
18
|
+
/** Map the plural content kind to the singular wire kind used by entity publish/checkout. */
|
|
19
|
+
exports.ENTITY_WIRE_KIND = {
|
|
20
|
+
components: 'component',
|
|
21
|
+
patterns: 'pattern',
|
|
22
|
+
pages: 'page',
|
|
23
|
+
};
|
|
24
|
+
/** The entity kinds, in the order a full run visits them: patterns compose components, pages reference both. */
|
|
25
|
+
exports.ENTITY_KIND_ORDER = ['components', 'patterns', 'pages'];
|
|
26
|
+
/**
|
|
27
|
+
* Every kind, in dependency order. Tokens and assets come first because rendered component
|
|
28
|
+
* artifacts reference the CSS variables and asset URLs they produce.
|
|
29
|
+
*/
|
|
30
|
+
exports.ALL_KIND_ORDER = ['tokens', 'assets', ...exports.ENTITY_KIND_ORDER];
|
|
31
|
+
/** Whether a string names a supported content kind. */
|
|
32
|
+
const isRegistryEntityKind = (value) => exports.REGISTRY_ENTITY_KINDS.includes(value);
|
|
33
|
+
exports.isRegistryEntityKind = isRegistryEntityKind;
|
|
@@ -26,10 +26,15 @@ export interface RegistryDbConnection {
|
|
|
26
26
|
export interface CreateRegistryDbConnectionParams {
|
|
27
27
|
driver: RegistryDatabaseDriver;
|
|
28
28
|
connectionString: string;
|
|
29
|
+
/**
|
|
30
|
+
* Apply {@link SERVING_POOL_LIMITS}. `db:migrate` leaves this unset, because a migration statement can run for
|
|
31
|
+
* minutes.
|
|
32
|
+
*/
|
|
33
|
+
serving?: boolean;
|
|
29
34
|
}
|
|
30
35
|
/**
|
|
31
36
|
* Create a registry database connection for the resolved driver. Both drivers target the
|
|
32
37
|
* Postgres dialect and share the same schema, so the returned connection is interchangeable for
|
|
33
38
|
* migration and (later) the registry store.
|
|
34
39
|
*/
|
|
35
|
-
export declare const createRegistryDbConnection: ({ driver, connectionString, }: CreateRegistryDbConnectionParams) => Promise<RegistryDbConnection>;
|
|
40
|
+
export declare const createRegistryDbConnection: ({ driver, connectionString, serving, }: CreateRegistryDbConnectionParams) => Promise<RegistryDbConnection>;
|
|
@@ -42,13 +42,26 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
42
42
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
43
|
exports.createRegistryDbConnection = void 0;
|
|
44
44
|
const schema = __importStar(require("./schema"));
|
|
45
|
+
/**
|
|
46
|
+
* One pool backs the anonymous docs read API, the authenticated management API, and every auth query, so a request
|
|
47
|
+
* must not hold a connection, or wait for one, without a deadline.
|
|
48
|
+
*
|
|
49
|
+
* `statement_timeout` bounds one statement, not a transaction, so it does not cut a publish short — an ingest is many
|
|
50
|
+
* small row writes in one transaction. `connectionTimeoutMillis` makes a saturated pool fail a request instead of
|
|
51
|
+
* queueing it forever, which is the `pg` default.
|
|
52
|
+
*/
|
|
53
|
+
const SERVING_POOL_LIMITS = {
|
|
54
|
+
statement_timeout: 15000,
|
|
55
|
+
connectionTimeoutMillis: 5000,
|
|
56
|
+
};
|
|
45
57
|
/**
|
|
46
58
|
* Create a registry database connection for the resolved driver. Both drivers target the
|
|
47
59
|
* Postgres dialect and share the same schema, so the returned connection is interchangeable for
|
|
48
60
|
* migration and (later) the registry store.
|
|
49
61
|
*/
|
|
50
|
-
const createRegistryDbConnection = (_a) => __awaiter(void 0, [_a], void 0, function* ({ driver, connectionString, }) {
|
|
62
|
+
const createRegistryDbConnection = (_a) => __awaiter(void 0, [_a], void 0, function* ({ driver, connectionString, serving, }) {
|
|
51
63
|
var _b;
|
|
64
|
+
const limits = serving ? SERVING_POOL_LIMITS : {};
|
|
52
65
|
if (driver === 'neon') {
|
|
53
66
|
const { Pool, neonConfig } = yield Promise.resolve().then(() => __importStar(require('@neondatabase/serverless')));
|
|
54
67
|
// Neon's serverless driver needs a WebSocket constructor in Node; reuse the bundled `ws`.
|
|
@@ -56,7 +69,7 @@ const createRegistryDbConnection = (_a) => __awaiter(void 0, [_a], void 0, funct
|
|
|
56
69
|
neonConfig.webSocketConstructor = (_b = ws.default) !== null && _b !== void 0 ? _b : ws;
|
|
57
70
|
const { drizzle } = yield Promise.resolve().then(() => __importStar(require('drizzle-orm/neon-serverless')));
|
|
58
71
|
const { migrate } = yield Promise.resolve().then(() => __importStar(require('drizzle-orm/neon-serverless/migrator')));
|
|
59
|
-
const pool = new Pool({ connectionString });
|
|
72
|
+
const pool = new Pool(Object.assign({ connectionString }, limits));
|
|
60
73
|
try {
|
|
61
74
|
yield pool.query('select 1');
|
|
62
75
|
}
|
|
@@ -75,7 +88,7 @@ const createRegistryDbConnection = (_a) => __awaiter(void 0, [_a], void 0, funct
|
|
|
75
88
|
const { Pool } = yield Promise.resolve().then(() => __importStar(require('pg')));
|
|
76
89
|
const { drizzle } = yield Promise.resolve().then(() => __importStar(require('drizzle-orm/node-postgres')));
|
|
77
90
|
const { migrate } = yield Promise.resolve().then(() => __importStar(require('drizzle-orm/node-postgres/migrator')));
|
|
78
|
-
const pool = new Pool({ connectionString });
|
|
91
|
+
const pool = new Pool(Object.assign({ connectionString }, limits));
|
|
79
92
|
try {
|
|
80
93
|
yield pool.query('select 1');
|
|
81
94
|
}
|
|
@@ -998,9 +998,9 @@ export declare const pages: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
998
998
|
dialect: "pg";
|
|
999
999
|
}>;
|
|
1000
1000
|
/**
|
|
1001
|
-
*
|
|
1002
|
-
*
|
|
1003
|
-
*
|
|
1001
|
+
* Published pages store one verbatim `.md` file for byte-exact checkout and runtime body resolution.
|
|
1002
|
+
* The schema permits at most one row per page, and its path must be `${pageId}.md`. This invariant
|
|
1003
|
+
* lets readers select the body without ordering or deduplication.
|
|
1004
1004
|
*/
|
|
1005
1005
|
export declare const pageFiles: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
1006
1006
|
name: "page_files";
|
|
@@ -1076,6 +1076,23 @@ export declare const pageFiles: import("drizzle-orm/pg-core").PgTableWithColumns
|
|
|
1076
1076
|
identity: undefined;
|
|
1077
1077
|
generated: undefined;
|
|
1078
1078
|
}, {}, {}>;
|
|
1079
|
+
body: import("drizzle-orm/pg-core").PgColumn<{
|
|
1080
|
+
name: "body";
|
|
1081
|
+
tableName: "page_files";
|
|
1082
|
+
dataType: "string";
|
|
1083
|
+
columnType: "PgText";
|
|
1084
|
+
data: string;
|
|
1085
|
+
driverParam: string;
|
|
1086
|
+
notNull: true;
|
|
1087
|
+
hasDefault: false;
|
|
1088
|
+
isPrimaryKey: false;
|
|
1089
|
+
isAutoincrement: false;
|
|
1090
|
+
hasRuntimeDefault: false;
|
|
1091
|
+
enumValues: [string, ...string[]];
|
|
1092
|
+
baseColumn: never;
|
|
1093
|
+
identity: undefined;
|
|
1094
|
+
generated: undefined;
|
|
1095
|
+
}, {}, {}>;
|
|
1079
1096
|
storageRef: import("drizzle-orm/pg-core").PgColumn<{
|
|
1080
1097
|
name: "storage_ref";
|
|
1081
1098
|
tableName: "page_files";
|
|
@@ -4376,6 +4393,23 @@ export declare const registrySchema: {
|
|
|
4376
4393
|
identity: undefined;
|
|
4377
4394
|
generated: undefined;
|
|
4378
4395
|
}, {}, {}>;
|
|
4396
|
+
body: import("drizzle-orm/pg-core").PgColumn<{
|
|
4397
|
+
name: "body";
|
|
4398
|
+
tableName: "page_files";
|
|
4399
|
+
dataType: "string";
|
|
4400
|
+
columnType: "PgText";
|
|
4401
|
+
data: string;
|
|
4402
|
+
driverParam: string;
|
|
4403
|
+
notNull: true;
|
|
4404
|
+
hasDefault: false;
|
|
4405
|
+
isPrimaryKey: false;
|
|
4406
|
+
isAutoincrement: false;
|
|
4407
|
+
hasRuntimeDefault: false;
|
|
4408
|
+
enumValues: [string, ...string[]];
|
|
4409
|
+
baseColumn: never;
|
|
4410
|
+
identity: undefined;
|
|
4411
|
+
generated: undefined;
|
|
4412
|
+
}, {}, {}>;
|
|
4379
4413
|
storageRef: import("drizzle-orm/pg-core").PgColumn<{
|
|
4380
4414
|
name: "storage_ref";
|
|
4381
4415
|
tableName: "page_files";
|
|
@@ -129,9 +129,9 @@ exports.patternFiles = (0, pg_core_1.pgTable)('pattern_files', {
|
|
|
129
129
|
* the record — see {@link pageFiles}.
|
|
130
130
|
*/
|
|
131
131
|
exports.pages = (0, pg_core_1.pgTable)('pages', {
|
|
132
|
-
/** Stable page id (
|
|
132
|
+
/** Stable page id (e.g. `guides/setup`; `index` is the root page). Join key across stores. */
|
|
133
133
|
id: (0, pg_core_1.text)('id').primaryKey(),
|
|
134
|
-
/** Logical route the page is served at (e.g. `/guides/setup`). */
|
|
134
|
+
/** Logical route the page is served at (e.g. `/guides/setup`, or `/` for `index`). */
|
|
135
135
|
path: (0, pg_core_1.text)('path').notNull(),
|
|
136
136
|
title: (0, pg_core_1.text)('title'),
|
|
137
137
|
description: (0, pg_core_1.text)('description'),
|
|
@@ -146,9 +146,9 @@ exports.pages = (0, pg_core_1.pgTable)('pages', {
|
|
|
146
146
|
updatedAt: (0, pg_core_1.timestamp)('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
|
147
147
|
}, (table) => [(0, pg_core_1.index)('pages_group_idx').on(table.group)]);
|
|
148
148
|
/**
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
149
|
+
* Published pages store one verbatim `.md` file for byte-exact checkout and runtime body resolution.
|
|
150
|
+
* The schema permits at most one row per page, and its path must be `${pageId}.md`. This invariant
|
|
151
|
+
* lets readers select the body without ordering or deduplication.
|
|
152
152
|
*/
|
|
153
153
|
exports.pageFiles = (0, pg_core_1.pgTable)('page_files', {
|
|
154
154
|
pageId: (0, pg_core_1.text)('page_id')
|
|
@@ -158,13 +158,15 @@ exports.pageFiles = (0, pg_core_1.pgTable)('page_files', {
|
|
|
158
158
|
path: (0, pg_core_1.text)('path').notNull(),
|
|
159
159
|
kind: (0, pg_core_1.text)('kind').$type().notNull(),
|
|
160
160
|
content: (0, pg_core_1.text)('content'),
|
|
161
|
+
body: (0, pg_core_1.text)('body').notNull(),
|
|
161
162
|
storageRef: (0, pg_core_1.text)('storage_ref'),
|
|
162
163
|
contentType: (0, pg_core_1.text)('content_type').notNull(),
|
|
163
164
|
createdAt: (0, pg_core_1.timestamp)('created_at', { withTimezone: true }).notNull().defaultNow(),
|
|
164
165
|
updatedAt: (0, pg_core_1.timestamp)('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
|
165
166
|
}, (table) => [
|
|
166
|
-
(0, pg_core_1.primaryKey)({ columns: [table.pageId
|
|
167
|
-
(0, pg_core_1.check)('
|
|
167
|
+
(0, pg_core_1.primaryKey)({ columns: [table.pageId] }),
|
|
168
|
+
(0, pg_core_1.check)('page_files_kind_markdown', (0, drizzle_orm_1.sql) `${table.kind} = 'markdown'`),
|
|
169
|
+
(0, pg_core_1.check)('page_files_canonical_path', (0, drizzle_orm_1.sql) `${table.path} = ${table.pageId} || '.md'`),
|
|
168
170
|
]);
|
|
169
171
|
/**
|
|
170
172
|
* Docs read-model artifacts. Path-keyed so shared/global artifacts (`component/main.css`,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Connected-workspace asset publish orchestration.
|
|
3
3
|
*
|
|
4
|
-
* `publish assets [collection]` runs a fresh build (`handoff.fetch()`), discovers the collections, and
|
|
4
|
+
* `publish assets [collection...]` runs a fresh build (`handoff.fetch()`), discovers the collections, and
|
|
5
5
|
* uploads each changed one. A collection is skipped when its deterministic `sourceHash` is unchanged;
|
|
6
6
|
* content transfers blob-by-blob (only hashes the registry lacks, since content-addressed dedup is the
|
|
7
7
|
* per-blob skip-unchanged), and the manifest is finalized atomically only after its blobs exist.
|
|
@@ -13,4 +13,4 @@ import Handoff from '../../index';
|
|
|
13
13
|
* blobs, and finalizes each manifest atomically. Reports published/unchanged/uploaded/failed counts;
|
|
14
14
|
* throws at the end if any collection failed.
|
|
15
15
|
*/
|
|
16
|
-
export declare const publishAssets: (handoff: Handoff,
|
|
16
|
+
export declare const publishAssets: (handoff: Handoff, selection?: string[]) => Promise<void>;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Connected-workspace asset publish orchestration.
|
|
4
4
|
*
|
|
5
|
-
* `publish assets [collection]` runs a fresh build (`handoff.fetch()`), discovers the collections, and
|
|
5
|
+
* `publish assets [collection...]` runs a fresh build (`handoff.fetch()`), discovers the collections, and
|
|
6
6
|
* uploads each changed one. A collection is skipped when its deterministic `sourceHash` is unchanged;
|
|
7
7
|
* content transfers blob-by-blob (only hashes the registry lacks, since content-addressed dedup is the
|
|
8
8
|
* per-blob skip-unchanged), and the manifest is finalized atomically only after its blobs exist.
|
|
@@ -20,7 +20,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
20
20
|
exports.publishAssets = void 0;
|
|
21
21
|
const logger_1 = require("../../utils/logger");
|
|
22
22
|
const sets_1 = require("../assets/sets");
|
|
23
|
-
const
|
|
23
|
+
const selection_1 = require("../selection");
|
|
24
24
|
const errors_1 = require("./errors");
|
|
25
25
|
const index_1 = require("./index");
|
|
26
26
|
const publish_build_1 = require("./publish-build");
|
|
@@ -49,22 +49,29 @@ const toManifestEntry = (asset) => {
|
|
|
49
49
|
* blobs, and finalizes each manifest atomically. Reports published/unchanged/uploaded/failed counts;
|
|
50
50
|
* throws at the end if any collection failed.
|
|
51
51
|
*/
|
|
52
|
-
const publishAssets = (handoff,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
const publishAssets = (handoff, selection) => __awaiter(void 0, void 0, void 0, function* () {
|
|
53
|
+
var _a;
|
|
54
|
+
const { client, url } = yield (0, index_1.resolveTransport)(handoff);
|
|
55
|
+
const unsupported = (_a = selection === null || selection === void 0 ? void 0 : selection.filter((collection) => !(0, sets_1.isAssetCollection)(collection))) !== null && _a !== void 0 ? _a : [];
|
|
56
|
+
if (unsupported.length > 0) {
|
|
57
|
+
throw new index_1.PublishError(`Unknown asset collection ${unsupported.map((name) => `"${name}"`).join(', ')}. ` +
|
|
58
|
+
`Supported collections: ${sets_1.ASSET_COLLECTIONS.join(', ')}.`);
|
|
56
59
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const targets = collection && (0, sets_1.isAssetCollection)(collection) ? [collection] : available.filter(sets_1.isAssetCollection);
|
|
61
|
-
const client = (0, client_1.createRegistryClient)({ baseUrl: connection.url, accessToken: connection.accessToken });
|
|
62
|
-
let remote = [];
|
|
63
|
-
try {
|
|
64
|
-
remote = yield client.listAssetCollections();
|
|
60
|
+
if (!handoff.skipBuild) {
|
|
61
|
+
logger_1.Logger.info(selection ? `Building assets to publish ${selection.join(', ')}…` : 'Building assets for publish…');
|
|
62
|
+
yield handoff.fetch();
|
|
65
63
|
}
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
const available = (yield handoff.store.assets.listCollections()).filter(sets_1.isAssetCollection);
|
|
65
|
+
const targets = (0, selection_1.selectIds)(available, selection).selected;
|
|
66
|
+
// Without a registry there is nothing to compare against, so a dry run reports every collection.
|
|
67
|
+
let remote = [];
|
|
68
|
+
if (client) {
|
|
69
|
+
try {
|
|
70
|
+
remote = yield client.listAssetCollections();
|
|
71
|
+
}
|
|
72
|
+
catch (_b) {
|
|
73
|
+
logger_1.Logger.info('Could not read current registry asset collections; publishing all selected collections.');
|
|
74
|
+
}
|
|
68
75
|
}
|
|
69
76
|
const remoteHashByCollection = new Map(remote.map((entry) => [entry.collection, entry.sourceHash]));
|
|
70
77
|
let published = 0;
|
|
@@ -90,35 +97,39 @@ const publishAssets = (handoff, collection) => __awaiter(void 0, void 0, void 0,
|
|
|
90
97
|
if (!byHash.has(asset.contentHash))
|
|
91
98
|
byHash.set(asset.contentHash, asset);
|
|
92
99
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
// A dry run has no registry to ask, so every deduped blob counts as one that would upload.
|
|
101
|
+
const missing = client ? yield client.assetBlobHaveCheck(Array.from(byHash.keys())) : Array.from(byHash.keys());
|
|
102
|
+
if (client) {
|
|
103
|
+
for (const hash of missing) {
|
|
104
|
+
const asset = byHash.get(hash);
|
|
105
|
+
if (!asset)
|
|
106
|
+
continue;
|
|
107
|
+
const content = yield handoff.store.assets.getAssetContent(col, asset.path);
|
|
108
|
+
if (!(content === null || content === void 0 ? void 0 : content.body)) {
|
|
109
|
+
throw new Error(`Could not read bytes for asset "${asset.path}" in collection "${col}".`);
|
|
110
|
+
}
|
|
111
|
+
yield client.uploadAssetBlob(hash, content.contentType, content.body);
|
|
112
|
+
uploaded += 1;
|
|
101
113
|
}
|
|
102
|
-
|
|
103
|
-
|
|
114
|
+
const pkg = {
|
|
115
|
+
collection: col,
|
|
116
|
+
build: (0, publish_build_1.createCurrentBuild)({ sourceHash }),
|
|
117
|
+
assets: manifest.map(toManifestEntry),
|
|
118
|
+
};
|
|
119
|
+
yield client.publishAssetManifest(pkg);
|
|
104
120
|
}
|
|
105
|
-
const pkg = {
|
|
106
|
-
collection: col,
|
|
107
|
-
build: (0, publish_build_1.createCurrentBuild)({ sourceHash }),
|
|
108
|
-
assets: manifest.map(toManifestEntry),
|
|
109
|
-
};
|
|
110
|
-
yield client.publishAssetManifest(pkg);
|
|
111
121
|
published += 1;
|
|
112
|
-
logger_1.Logger.info(
|
|
122
|
+
logger_1.Logger.info(`${(0, index_1.publishedLabel)(handoff)}: ${col} (${manifest.length} asset(s), ${missing.length} new blob(s))`);
|
|
113
123
|
}
|
|
114
124
|
catch (error) {
|
|
115
125
|
failed.push({
|
|
116
126
|
collection: col,
|
|
117
|
-
message: (0, errors_1.describePublishError)(error,
|
|
127
|
+
message: (0, errors_1.describePublishError)(error, url, 'asset collection'),
|
|
118
128
|
});
|
|
119
129
|
}
|
|
120
130
|
}
|
|
121
|
-
logger_1.Logger.success(`Assets publish complete: ${published}
|
|
131
|
+
logger_1.Logger.success(`Assets publish complete: ${published} ${handoff.dryRun ? 'would be published' : 'published'}, ` +
|
|
132
|
+
`${unchanged} unchanged, ${uploaded} blob(s) uploaded${failed.length ? `, ${failed.length} failed` : ''}.`);
|
|
122
133
|
if (failed.length > 0) {
|
|
123
134
|
for (const failure of failed) {
|
|
124
135
|
logger_1.Logger.error(` - ${failure.collection}: ${failure.message}`);
|
|
@@ -9,26 +9,50 @@
|
|
|
9
9
|
* that still guarantees the entity's artifacts (and the shared artifacts they reference) are current.
|
|
10
10
|
*/
|
|
11
11
|
import Handoff from '../../index';
|
|
12
|
+
import { type RegistryClient } from '../client';
|
|
12
13
|
import type { TransferEntityKind } from '../transfer';
|
|
13
14
|
/** A connected-workspace configuration or precondition failure surfaced to the CLI. */
|
|
14
15
|
export declare class PublishError extends Error {
|
|
15
16
|
constructor(message: string);
|
|
16
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Ensure this project may publish at all: a registry host stores and serves what was published to it,
|
|
20
|
+
* so only a workspace runtime can be the source. Kept apart from the connection lookup because a dry
|
|
21
|
+
* run still has to be refused on a registry host while needing no URL or token itself.
|
|
22
|
+
*/
|
|
23
|
+
export declare const assertPublishableWorkspace: (handoff: Handoff) => void;
|
|
17
24
|
/**
|
|
18
25
|
* Ensure the workspace is a connected workspace able to publish: workspace runtime mode (registry
|
|
19
26
|
* hosts do not publish) and a resolved registry URL + access token. Throws an actionable
|
|
20
27
|
* {@link PublishError} naming the exact misconfiguration.
|
|
21
28
|
*/
|
|
22
29
|
export declare const resolveConnectionOrThrow: (handoff: Handoff) => Promise<import("../connection").ResolvedRegistryConnection>;
|
|
30
|
+
/**
|
|
31
|
+
* How a publish run reaches the registry. `client` is `null` on a dry run: the documented dry run
|
|
32
|
+
* needs no registry URL or token, so it stops at the workspace guard and contacts nothing.
|
|
33
|
+
*/
|
|
34
|
+
export interface PublishTransport {
|
|
35
|
+
client: RegistryClient | null;
|
|
36
|
+
/** The registry URL, or a placeholder on a dry run. Used only in log lines and error messages. */
|
|
37
|
+
url: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolve the transport for a publish run. Shared by the entity, token and asset paths so the dry-run
|
|
41
|
+
* short-circuit lives in one place instead of four call sites.
|
|
42
|
+
*/
|
|
43
|
+
export declare const resolveTransport: (handoff: Handoff) => Promise<PublishTransport>;
|
|
44
|
+
/** Prefix for a per-item progress line: `Published:` normally, `Would publish:` on a dry run. */
|
|
45
|
+
export declare const publishedLabel: (handoff: Handoff) => string;
|
|
23
46
|
/**
|
|
24
47
|
* Publish a single component, pattern, or page from a connected workspace. Components and patterns
|
|
25
48
|
* receive a targeted build, while pages are packaged directly from their source.
|
|
26
49
|
*/
|
|
27
50
|
export declare const publishEntity: (handoff: Handoff, kind: TransferEntityKind, id: string) => Promise<void>;
|
|
28
51
|
/**
|
|
29
|
-
* Publish
|
|
30
|
-
* once, assembles each entity's package, skips
|
|
31
|
-
* registry (unless `--force`), and reports
|
|
32
|
-
* collected and never aborts the rest; the
|
|
52
|
+
* Publish the components, patterns or pages declared in this connected workspace: every one of the
|
|
53
|
+
* kind, or only `ids` when given. Builds the kind once, assembles each entity's package, skips
|
|
54
|
+
* entities whose content hash already matches the registry (unless `--force`), and reports
|
|
55
|
+
* published/unchanged/failed counts. A per-entity failure is collected and never aborts the rest; the
|
|
56
|
+
* run throws at the end if any entity failed.
|
|
33
57
|
*/
|
|
34
|
-
export declare const publishEntities: (handoff: Handoff, kind: TransferEntityKind) => Promise<void>;
|
|
58
|
+
export declare const publishEntities: (handoff: Handoff, kind: TransferEntityKind, ids?: string[]) => Promise<void>;
|
|
@@ -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.publishEntities = exports.publishEntity = exports.resolveConnectionOrThrow = exports.PublishError = void 0;
|
|
25
|
+
exports.publishEntities = exports.publishEntity = exports.publishedLabel = exports.resolveTransport = exports.resolveConnectionOrThrow = exports.assertPublishableWorkspace = 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"));
|
|
@@ -30,6 +30,7 @@ const builder_2 = require("../../transformers/preview/pattern/builder");
|
|
|
30
30
|
const logger_1 = require("../../utils/logger");
|
|
31
31
|
const client_1 = require("../client");
|
|
32
32
|
const connection_1 = require("../connection");
|
|
33
|
+
const selection_1 = require("../selection");
|
|
33
34
|
const errors_1 = require("./errors");
|
|
34
35
|
const package_1 = require("./package");
|
|
35
36
|
/** A connected-workspace configuration or precondition failure surfaced to the CLI. */
|
|
@@ -41,17 +42,26 @@ class PublishError extends Error {
|
|
|
41
42
|
}
|
|
42
43
|
exports.PublishError = PublishError;
|
|
43
44
|
/**
|
|
44
|
-
* Ensure
|
|
45
|
-
*
|
|
46
|
-
*
|
|
45
|
+
* Ensure this project may publish at all: a registry host stores and serves what was published to it,
|
|
46
|
+
* so only a workspace runtime can be the source. Kept apart from the connection lookup because a dry
|
|
47
|
+
* run still has to be refused on a registry host while needing no URL or token itself.
|
|
47
48
|
*/
|
|
48
|
-
const
|
|
49
|
+
const assertPublishableWorkspace = (handoff) => {
|
|
49
50
|
var _a, _b, _c;
|
|
50
51
|
const mode = (_c = (_b = (_a = handoff.config) === null || _a === void 0 ? void 0 : _a.runtime) === null || _b === void 0 ? void 0 : _b.mode) !== null && _c !== void 0 ? _c : 'workspace';
|
|
51
52
|
if (mode !== 'workspace') {
|
|
52
53
|
throw new PublishError(`publish is only available from a connected workspace (runtime.mode: "workspace"); this project is "${mode}". ` +
|
|
53
54
|
'A registry host stores and serves what was published to it; it does not publish.');
|
|
54
55
|
}
|
|
56
|
+
};
|
|
57
|
+
exports.assertPublishableWorkspace = assertPublishableWorkspace;
|
|
58
|
+
/**
|
|
59
|
+
* Ensure the workspace is a connected workspace able to publish: workspace runtime mode (registry
|
|
60
|
+
* hosts do not publish) and a resolved registry URL + access token. Throws an actionable
|
|
61
|
+
* {@link PublishError} naming the exact misconfiguration.
|
|
62
|
+
*/
|
|
63
|
+
const resolveConnectionOrThrow = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
|
|
64
|
+
(0, exports.assertPublishableWorkspace)(handoff);
|
|
55
65
|
const connection = yield (0, connection_1.resolveAuthenticatedRegistryConnection)(handoff.config, handoff.workingPath);
|
|
56
66
|
if (!connection.url) {
|
|
57
67
|
throw new PublishError(`No registry is configured. Run \`handoff-app login --url <registry-url>\`, set runtime.registryConnection.url, ` +
|
|
@@ -64,6 +74,25 @@ const resolveConnectionOrThrow = (handoff) => __awaiter(void 0, void 0, void 0,
|
|
|
64
74
|
return connection;
|
|
65
75
|
});
|
|
66
76
|
exports.resolveConnectionOrThrow = resolveConnectionOrThrow;
|
|
77
|
+
/**
|
|
78
|
+
* Resolve the transport for a publish run. Shared by the entity, token and asset paths so the dry-run
|
|
79
|
+
* short-circuit lives in one place instead of four call sites.
|
|
80
|
+
*/
|
|
81
|
+
const resolveTransport = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
|
|
82
|
+
if (handoff.dryRun) {
|
|
83
|
+
(0, exports.assertPublishableWorkspace)(handoff);
|
|
84
|
+
return { client: null, url: '(dry run)' };
|
|
85
|
+
}
|
|
86
|
+
const connection = yield (0, exports.resolveConnectionOrThrow)(handoff);
|
|
87
|
+
return {
|
|
88
|
+
client: (0, client_1.createRegistryClient)({ baseUrl: connection.url, accessToken: connection.accessToken }),
|
|
89
|
+
url: connection.url,
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
exports.resolveTransport = resolveTransport;
|
|
93
|
+
/** Prefix for a per-item progress line: `Published:` normally, `Would publish:` on a dry run. */
|
|
94
|
+
const publishedLabel = (handoff) => (handoff.dryRun ? 'Would publish' : 'Published');
|
|
95
|
+
exports.publishedLabel = publishedLabel;
|
|
67
96
|
/** Run the smallest build that guarantees the selected entity's artifacts are current. */
|
|
68
97
|
const runTargetedBuild = (handoff, kind, id) => __awaiter(void 0, void 0, void 0, function* () {
|
|
69
98
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
@@ -105,19 +134,26 @@ const runTargetedBuild = (handoff, kind, id) => __awaiter(void 0, void 0, void 0
|
|
|
105
134
|
* receive a targeted build, while pages are packaged directly from their source.
|
|
106
135
|
*/
|
|
107
136
|
const publishEntity = (handoff, kind, id) => __awaiter(void 0, void 0, void 0, function* () {
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
137
|
+
const { client, url } = yield (0, exports.resolveTransport)(handoff);
|
|
138
|
+
if (!handoff.skipBuild) {
|
|
139
|
+
logger_1.Logger.info(kind === 'page' ? `Preparing page "${id}" for publish…` : `Building ${kind} "${id}" for publish…`);
|
|
140
|
+
yield runTargetedBuild(handoff, kind, id);
|
|
141
|
+
}
|
|
111
142
|
const pkg = yield (0, package_1.buildPublishPackage)(handoff, kind, id);
|
|
112
143
|
(0, package_1.assertRequiredArtifactsPresent)(pkg);
|
|
113
|
-
|
|
114
|
-
|
|
144
|
+
const contents = `${pkg.artifacts.length} artifact(s), ${pkg.files.length} source file(s)`;
|
|
145
|
+
if (!client) {
|
|
146
|
+
logger_1.Logger.info(`Would publish ${kind} "${id}" (${contents}).`);
|
|
147
|
+
logger_1.Logger.success(`Dry run: ${kind} "${id}" would be published.`);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
logger_1.Logger.info(`Uploading ${kind} "${id}" to ${url} (${contents})…`);
|
|
115
151
|
try {
|
|
116
152
|
yield client.publish(kind, id, pkg);
|
|
117
153
|
}
|
|
118
154
|
catch (error) {
|
|
119
155
|
if (error instanceof client_1.RegistryClientError) {
|
|
120
|
-
throw new PublishError((0, errors_1.describeUploadFailure)(error,
|
|
156
|
+
throw new PublishError((0, errors_1.describeUploadFailure)(error, url, 'package'));
|
|
121
157
|
}
|
|
122
158
|
throw error;
|
|
123
159
|
}
|
|
@@ -144,27 +180,46 @@ const runBulkBuild = (handoff, kind) => __awaiter(void 0, void 0, void 0, functi
|
|
|
144
180
|
});
|
|
145
181
|
/** List every declared entity id of the kind from the workspace store. */
|
|
146
182
|
const listWorkspaceEntityIds = (handoff, kind) => __awaiter(void 0, void 0, void 0, function* () {
|
|
147
|
-
const
|
|
183
|
+
const stores = { component: handoff.store.components, pattern: handoff.store.patterns, page: handoff.store.pages };
|
|
184
|
+
const store = stores[kind];
|
|
185
|
+
if (!store) {
|
|
186
|
+
throw new PublishError(`Unknown entity kind "${kind}".`);
|
|
187
|
+
}
|
|
148
188
|
return (yield store.list()).map((entity) => entity.id);
|
|
149
189
|
});
|
|
150
190
|
/**
|
|
151
|
-
*
|
|
152
|
-
*
|
|
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.
|
|
191
|
+
* Narrow the workspace's declared ids to the requested subset, naming any id that is not declared
|
|
192
|
+
* here instead of silently publishing fewer entities than asked for.
|
|
155
193
|
*/
|
|
156
|
-
const
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
194
|
+
const selectEntityIds = (available, requested, kind) => {
|
|
195
|
+
const { selected, unknown } = (0, selection_1.selectIds)(available, requested);
|
|
196
|
+
if (unknown.length > 0) {
|
|
197
|
+
throw new PublishError(`No ${kind} named ${unknown.map((id) => `"${id}"`).join(', ')} is declared in this workspace. ` +
|
|
198
|
+
`Declared ${kind}s: ${available.join(', ') || '(none)'}.`);
|
|
199
|
+
}
|
|
200
|
+
return selected;
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* Publish the components, patterns or pages declared in this connected workspace: every one of the
|
|
204
|
+
* kind, or only `ids` when given. Builds the kind once, assembles each entity's package, skips
|
|
205
|
+
* entities whose content hash already matches the registry (unless `--force`), and reports
|
|
206
|
+
* published/unchanged/failed counts. A per-entity failure is collected and never aborts the rest; the
|
|
207
|
+
* run throws at the end if any entity failed.
|
|
208
|
+
*/
|
|
209
|
+
const publishEntities = (handoff, kind, ids) => __awaiter(void 0, void 0, void 0, function* () {
|
|
210
|
+
const { client, url } = yield (0, exports.resolveTransport)(handoff);
|
|
211
|
+
if (!handoff.skipBuild) {
|
|
212
|
+
logger_1.Logger.info(`Building ${kind}s for publish…`);
|
|
213
|
+
yield runBulkBuild(handoff, kind);
|
|
214
|
+
}
|
|
215
|
+
const targets = selectEntityIds(yield listWorkspaceEntityIds(handoff, kind), ids, kind);
|
|
216
|
+
if (targets.length === 0) {
|
|
162
217
|
logger_1.Logger.success(`No ${kind}s are declared in this workspace; nothing to publish.`);
|
|
163
218
|
return;
|
|
164
219
|
}
|
|
165
|
-
|
|
220
|
+
// Without a registry there is nothing to compare against, so a dry run reports every target.
|
|
166
221
|
let remote = [];
|
|
167
|
-
if (!handoff.force) {
|
|
222
|
+
if (client && !handoff.force) {
|
|
168
223
|
try {
|
|
169
224
|
remote = yield client.listEntities(kind);
|
|
170
225
|
}
|
|
@@ -177,7 +232,7 @@ const publishEntities = (handoff, kind) => __awaiter(void 0, void 0, void 0, fun
|
|
|
177
232
|
let published = 0;
|
|
178
233
|
let unchanged = 0;
|
|
179
234
|
const failed = [];
|
|
180
|
-
for (const id of
|
|
235
|
+
for (const id of targets) {
|
|
181
236
|
try {
|
|
182
237
|
const pkg = yield (0, package_1.buildPublishPackage)(handoff, kind, id);
|
|
183
238
|
(0, package_1.assertRequiredArtifactsPresent)(pkg);
|
|
@@ -188,20 +243,18 @@ const publishEntities = (handoff, kind) => __awaiter(void 0, void 0, void 0, fun
|
|
|
188
243
|
logger_1.Logger.info(`Unchanged: ${id}`);
|
|
189
244
|
continue;
|
|
190
245
|
}
|
|
191
|
-
|
|
246
|
+
if (client) {
|
|
247
|
+
yield client.publish(kind, id, pkg);
|
|
248
|
+
}
|
|
192
249
|
published += 1;
|
|
193
|
-
logger_1.Logger.info(
|
|
250
|
+
logger_1.Logger.info(`${(0, exports.publishedLabel)(handoff)}: ${id} (${pkg.artifacts.length} artifact(s), ${pkg.files.length} source file(s))`);
|
|
194
251
|
}
|
|
195
252
|
catch (error) {
|
|
196
|
-
|
|
197
|
-
? (0, errors_1.describeUploadFailure)(error, connection.url, 'package')
|
|
198
|
-
: error instanceof Error
|
|
199
|
-
? error.message
|
|
200
|
-
: String(error);
|
|
201
|
-
failed.push({ id, message });
|
|
253
|
+
failed.push({ id, message: (0, errors_1.describePublishError)(error, url, 'package') });
|
|
202
254
|
}
|
|
203
255
|
}
|
|
204
|
-
logger_1.Logger.success(`${kind[0].toUpperCase()}${kind.slice(1)}s publish complete — ${published}
|
|
256
|
+
logger_1.Logger.success(`${kind[0].toUpperCase()}${kind.slice(1)}s publish complete — ${published} ${handoff.dryRun ? 'would be published' : 'published'}, ` +
|
|
257
|
+
`${unchanged} unchanged${failed.length ? `, ${failed.length} failed` : ''}.`);
|
|
205
258
|
if (failed.length > 0) {
|
|
206
259
|
for (const failure of failed) {
|
|
207
260
|
logger_1.Logger.error(` - ${failure.id}: ${failure.message}`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Connected-workspace token publish orchestration.
|
|
3
3
|
*
|
|
4
|
-
* `publish tokens [setId]` runs a **fresh token build** (`handoff.fetch()` — Figma extract +
|
|
4
|
+
* `publish tokens [setId...]` runs a **fresh token build** (`handoff.fetch()` — Figma extract +
|
|
5
5
|
* `buildStyles`) so the local `tokens.json` and generated token outputs are current, discovers the
|
|
6
6
|
* logical sets from that document, then uploads each selected set (its extracted record + generated
|
|
7
7
|
* artifacts) to the connected registry. Each set is one atomic upsert; a set whose deterministic
|
|
@@ -15,4 +15,4 @@ import Handoff from '../../index';
|
|
|
15
15
|
* counts. A per-set upload failure is collected and never leaves a partially written set (each set is
|
|
16
16
|
* committed atomically server-side); the run throws at the end if any set failed.
|
|
17
17
|
*/
|
|
18
|
-
export declare const publishTokens: (handoff: Handoff,
|
|
18
|
+
export declare const publishTokens: (handoff: Handoff, selection?: string[]) => Promise<void>;
|