handoff-app 2.0.0-3 → 2.0.0-5
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 +302 -378
- package/dist/app-builder/build.js +2 -2
- 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 +39 -7
- 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/index.d.ts +2 -1
- package/dist/config/index.js +2 -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/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 +1 -1
- package/src/app/components/util/index.ts +5 -23
- package/src/app/lib/docs-api/backend.ts +11 -0
- 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 +126 -0
- package/src/app/lib/docs-api/page-rendering.ts +17 -0
- package/src/app/lib/docs-api/page-search.ts +191 -0
- package/src/app/lib/docs-api/records.ts +11 -19
- package/src/app/lib/docs-api/registry.ts +90 -11
- package/src/app/lib/docs-api/search.ts +115 -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 +6 -0
- package/src/app/pages/[...slug]/index.tsx +37 -22
- 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/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 +30 -5
- package/src/app-builder/build.ts +2 -2
- 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 +31 -7
- 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/index.ts +2 -1
- 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/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/package.json.tpl +4 -0
- package/config/docs/foundations/logos.md +0 -7
- package/src/app/lib/api/redact.ts +0 -46
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a credential can be presented unambiguously in a request header. One rule shared by the
|
|
3
|
+
* authentication headers and by whatever a deployment configures as a secret, so a value is never
|
|
4
|
+
* accepted through one carrier and rejected by the other.
|
|
5
|
+
*
|
|
6
|
+
* Whitespace is out so nothing can be trimmed away or smuggled past a parser, and the comma with it:
|
|
7
|
+
* repeats of a header Node does not de-duplicate arrive joined with `, `, and no accepted credential
|
|
8
|
+
* may look like that. Non-ASCII bytes are out too, since their header encoding is ambiguous and a
|
|
9
|
+
* credential has to reproduce exactly.
|
|
10
|
+
*/
|
|
11
|
+
export declare const isTransmittableCredential: (value: string) => boolean;
|
|
1
12
|
export declare const validateRegistryEmail: (email: string) => string | null;
|
|
2
13
|
export declare const validateRegistryDisplayName: (name: string) => string | null;
|
|
3
14
|
export declare const validateRegistryImageUrl: (image: string | null | undefined) => string | null | false;
|
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.validateRegistryImageUrl = exports.validateRegistryDisplayName = exports.validateRegistryEmail = void 0;
|
|
3
|
+
exports.validateRegistryImageUrl = exports.validateRegistryDisplayName = exports.validateRegistryEmail = exports.isTransmittableCredential = void 0;
|
|
4
4
|
const crypto_1 = require("./crypto");
|
|
5
5
|
const SIMPLE_EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
6
|
+
/**
|
|
7
|
+
* Printable ASCII (`!` through `~`) except the comma: HTTP's `VCHAR`, minus the character Node joins
|
|
8
|
+
* duplicate header values with.
|
|
9
|
+
*/
|
|
10
|
+
const TRANSMITTABLE_CREDENTIAL_PATTERN = /^[\x21-\x2B\x2D-\x7E]+$/;
|
|
11
|
+
/**
|
|
12
|
+
* Whether a credential can be presented unambiguously in a request header. One rule shared by the
|
|
13
|
+
* authentication headers and by whatever a deployment configures as a secret, so a value is never
|
|
14
|
+
* accepted through one carrier and rejected by the other.
|
|
15
|
+
*
|
|
16
|
+
* Whitespace is out so nothing can be trimmed away or smuggled past a parser, and the comma with it:
|
|
17
|
+
* repeats of a header Node does not de-duplicate arrive joined with `, `, and no accepted credential
|
|
18
|
+
* may look like that. Non-ASCII bytes are out too, since their header encoding is ambiguous and a
|
|
19
|
+
* credential has to reproduce exactly.
|
|
20
|
+
*/
|
|
21
|
+
const isTransmittableCredential = (value) => TRANSMITTABLE_CREDENTIAL_PATTERN.test(value);
|
|
22
|
+
exports.isTransmittableCredential = isTransmittableCredential;
|
|
6
23
|
const validateRegistryEmail = (email) => {
|
|
7
24
|
const normalized = (0, crypto_1.normalizeEmail)(email);
|
|
8
25
|
return normalized.length <= 320 && SIMPLE_EMAIL_PATTERN.test(normalized) ? normalized : null;
|
|
@@ -13,4 +13,4 @@ import Handoff from '../../index';
|
|
|
13
13
|
* Checkout all published asset collections, or a single collection when `collection` is given.
|
|
14
14
|
* Recreates workspace asset files; prompts before overwriting changed local files unless `--force`.
|
|
15
15
|
*/
|
|
16
|
-
export declare const checkoutAssets: (handoff: Handoff,
|
|
16
|
+
export declare const checkoutAssets: (handoff: Handoff, selection?: string[]) => Promise<void>;
|
|
@@ -84,15 +84,19 @@ const wouldChange = (absolutePath, bytes) => {
|
|
|
84
84
|
* Checkout all published asset collections, or a single collection when `collection` is given.
|
|
85
85
|
* Recreates workspace asset files; prompts before overwriting changed local files unless `--force`.
|
|
86
86
|
*/
|
|
87
|
-
const checkoutAssets = (handoff,
|
|
87
|
+
const checkoutAssets = (handoff, selection) => __awaiter(void 0, void 0, void 0, function* () {
|
|
88
|
+
var _a;
|
|
88
89
|
const connection = yield (0, index_1.resolveConnectionOrThrow)(handoff);
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
const unsupported = (_a = selection === null || selection === void 0 ? void 0 : selection.filter((name) => !(0, sets_1.isAssetCollection)(name))) !== null && _a !== void 0 ? _a : [];
|
|
91
|
+
if (unsupported.length > 0) {
|
|
92
|
+
throw new index_1.CheckoutError(`Unknown asset collection ${unsupported.map((name) => `"${name}"`).join(', ')}. ` +
|
|
93
|
+
`Supported collections: ${sets_1.ASSET_COLLECTIONS.join(', ')}.`);
|
|
91
94
|
}
|
|
92
95
|
const client = (0, client_1.createRegistryClient)({ baseUrl: connection.url, accessToken: connection.accessToken });
|
|
96
|
+
// A named selection is trusted as-is; only a full checkout has to ask the registry what exists.
|
|
93
97
|
let collections;
|
|
94
|
-
if (
|
|
95
|
-
collections =
|
|
98
|
+
if (selection) {
|
|
99
|
+
collections = selection;
|
|
96
100
|
}
|
|
97
101
|
else {
|
|
98
102
|
const summaries = yield client.listAssetCollections();
|
|
@@ -123,16 +127,27 @@ const checkoutAssets = (handoff, collection) => __awaiter(void 0, void 0, void 0
|
|
|
123
127
|
// Download each referenced blob once (dedup by hash), then resolve every asset to a physical write.
|
|
124
128
|
const blobCache = new Map();
|
|
125
129
|
const writes = [];
|
|
130
|
+
const plannedPaths = [];
|
|
126
131
|
for (const payload of payloads) {
|
|
127
132
|
for (const asset of payload.assets) {
|
|
133
|
+
const absolutePath = (0, layout_1.resolveAssetPhysicalPath)(asset.path, roots);
|
|
134
|
+
plannedPaths.push(absolutePath);
|
|
135
|
+
// A dry run reports paths only, so there is no reason to pull the bytes down.
|
|
136
|
+
if (handoff.dryRun) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
128
139
|
let bytes = blobCache.get(asset.contentHash);
|
|
129
140
|
if (!bytes) {
|
|
130
141
|
bytes = yield client.downloadAssetBlob(asset.contentHash);
|
|
131
142
|
blobCache.set(asset.contentHash, bytes);
|
|
132
143
|
}
|
|
133
|
-
writes.push({ absolutePath
|
|
144
|
+
writes.push({ absolutePath, bytes });
|
|
134
145
|
}
|
|
135
146
|
}
|
|
147
|
+
if (handoff.dryRun) {
|
|
148
|
+
(0, index_1.reportPlannedWrites)(handoff, 'asset collections', plannedPaths, plannedPaths.filter((target) => fs_extra_1.default.existsSync(target)));
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
136
151
|
const existingChanges = writes.filter((write) => fs_extra_1.default.existsSync(write.absolutePath) && wouldChange(write.absolutePath, write.bytes));
|
|
137
152
|
if (existingChanges.length > 0 && !handoff.force) {
|
|
138
153
|
const proceed = yield p.confirm({
|
|
@@ -24,6 +24,13 @@ export declare class CheckoutError extends Error {
|
|
|
24
24
|
* actionable {@link CheckoutError} naming the exact misconfiguration.
|
|
25
25
|
*/
|
|
26
26
|
export declare const resolveConnectionOrThrow: (handoff: Handoff) => Promise<import("../connection").ResolvedRegistryConnection>;
|
|
27
|
+
/**
|
|
28
|
+
* Report the files a checkout would create or replace, and write nothing. Called once the real
|
|
29
|
+
* checkout has computed its targets but before it prompts or writes, so a dry run exercises the same
|
|
30
|
+
* path resolution and conflict detection as the real thing. Shared by the entity, page, token and
|
|
31
|
+
* asset checkout paths.
|
|
32
|
+
*/
|
|
33
|
+
export declare const reportPlannedWrites: (handoff: Handoff, label: string, targets: string[], conflicts: string[]) => void;
|
|
27
34
|
/**
|
|
28
35
|
* Checkout a single component, pattern, or page from the connected registry into this workspace.
|
|
29
36
|
* Overwriting existing local files requires `--force` or an interactive confirmation.
|
|
@@ -35,4 +42,4 @@ export declare const checkoutEntity: (handoff: Handoff, kind: TransferEntityKind
|
|
|
35
42
|
* is collected and never aborts the rest; the run throws at the end if any entity failed. Overwrite
|
|
36
43
|
* prompting is per entity (skipped under `--force`).
|
|
37
44
|
*/
|
|
38
|
-
export declare const checkoutEntities: (handoff: Handoff, kind: TransferEntityKind) => Promise<void>;
|
|
45
|
+
export declare const checkoutEntities: (handoff: Handoff, kind: TransferEntityKind, ids?: string[]) => Promise<void>;
|
|
@@ -60,7 +60,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
60
60
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
61
61
|
};
|
|
62
62
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
63
|
-
exports.checkoutEntities = exports.checkoutEntity = exports.resolveConnectionOrThrow = exports.CheckoutError = void 0;
|
|
63
|
+
exports.checkoutEntities = exports.checkoutEntity = exports.reportPlannedWrites = exports.resolveConnectionOrThrow = exports.CheckoutError = void 0;
|
|
64
64
|
const p = __importStar(require("@clack/prompts"));
|
|
65
65
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
66
66
|
const path_1 = __importDefault(require("path"));
|
|
@@ -70,6 +70,7 @@ const logger_1 = require("../../utils/logger");
|
|
|
70
70
|
const client_1 = require("../client");
|
|
71
71
|
const connection_1 = require("../connection");
|
|
72
72
|
const path_2 = require("../path");
|
|
73
|
+
const selection_1 = require("../selection");
|
|
73
74
|
/** A connected-workspace configuration or precondition failure surfaced to the CLI. */
|
|
74
75
|
class CheckoutError extends Error {
|
|
75
76
|
constructor(message) {
|
|
@@ -568,6 +569,21 @@ const writeSourceFile = (targetDir, file) => __awaiter(void 0, void 0, void 0, f
|
|
|
568
569
|
yield fs_extra_1.default.writeFile(absolutePath, file.content, 'utf8');
|
|
569
570
|
return absolutePath;
|
|
570
571
|
});
|
|
572
|
+
/**
|
|
573
|
+
* Report the files a checkout would create or replace, and write nothing. Called once the real
|
|
574
|
+
* checkout has computed its targets but before it prompts or writes, so a dry run exercises the same
|
|
575
|
+
* path resolution and conflict detection as the real thing. Shared by the entity, page, token and
|
|
576
|
+
* asset checkout paths.
|
|
577
|
+
*/
|
|
578
|
+
const reportPlannedWrites = (handoff, label, targets, conflicts) => {
|
|
579
|
+
const conflicting = new Set(conflicts);
|
|
580
|
+
logger_1.Logger.info(`Would checkout ${label} (${targets.length} file(s)):`);
|
|
581
|
+
for (const target of targets) {
|
|
582
|
+
const relative = path_1.default.relative(handoff.workingPath, target) || target;
|
|
583
|
+
logger_1.Logger.info(` ${conflicting.has(target) ? 'overwrite' : 'create'}: ${relative}`);
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
exports.reportPlannedWrites = reportPlannedWrites;
|
|
571
587
|
/**
|
|
572
588
|
* Checkout a page: write its single verbatim `.md` (transfer path `<id>.md`) back under
|
|
573
589
|
* `<workingPath>/pages/`. Unlike components/patterns there is no declaration to synthesize — the
|
|
@@ -583,6 +599,10 @@ const checkoutPage = (handoff, id, payload) => __awaiter(void 0, void 0, void 0,
|
|
|
583
599
|
return target;
|
|
584
600
|
});
|
|
585
601
|
const conflicts = targets.filter((file) => fs_extra_1.default.existsSync(file));
|
|
602
|
+
if (handoff.dryRun) {
|
|
603
|
+
(0, exports.reportPlannedWrites)(handoff, `page "${id}"`, targets, conflicts);
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
586
606
|
if (!(yield confirmOverwrite(handoff, conflicts))) {
|
|
587
607
|
logger_1.Logger.warn(`Checkout of page "${id}" cancelled; no files were written.`);
|
|
588
608
|
return;
|
|
@@ -633,7 +653,13 @@ const checkoutSingle = (handoff, kind, id, client, registryUrl) => __awaiter(voi
|
|
|
633
653
|
}
|
|
634
654
|
return { file, absolutePath };
|
|
635
655
|
});
|
|
636
|
-
const
|
|
656
|
+
const plannedWrites = [...sourceTargets.map((entry) => entry.absolutePath), declarationPath];
|
|
657
|
+
const conflicts = plannedWrites.filter((file) => fs_extra_1.default.existsSync(file));
|
|
658
|
+
if (handoff.dryRun) {
|
|
659
|
+
// Returning null also skips `registerCheckedOut`, so a dry run never edits handoff.config either.
|
|
660
|
+
(0, exports.reportPlannedWrites)(handoff, `${kind} "${id}"`, plannedWrites, conflicts);
|
|
661
|
+
return null;
|
|
662
|
+
}
|
|
637
663
|
if (!(yield confirmOverwrite(handoff, conflicts))) {
|
|
638
664
|
logger_1.Logger.warn(`Checkout of ${kind} "${id}" cancelled; no files were written.`);
|
|
639
665
|
return null;
|
|
@@ -694,7 +720,7 @@ exports.checkoutEntity = checkoutEntity;
|
|
|
694
720
|
* is collected and never aborts the rest; the run throws at the end if any entity failed. Overwrite
|
|
695
721
|
* prompting is per entity (skipped under `--force`).
|
|
696
722
|
*/
|
|
697
|
-
const checkoutEntities = (handoff, kind) => __awaiter(void 0, void 0, void 0, function* () {
|
|
723
|
+
const checkoutEntities = (handoff, kind, ids) => __awaiter(void 0, void 0, void 0, function* () {
|
|
698
724
|
const connection = yield (0, exports.resolveConnectionOrThrow)(handoff);
|
|
699
725
|
const client = (0, client_1.createRegistryClient)({ baseUrl: connection.url, accessToken: connection.accessToken });
|
|
700
726
|
let summaries;
|
|
@@ -707,14 +733,20 @@ const checkoutEntities = (handoff, kind) => __awaiter(void 0, void 0, void 0, fu
|
|
|
707
733
|
}
|
|
708
734
|
throw error;
|
|
709
735
|
}
|
|
710
|
-
|
|
736
|
+
const published = summaries.map((summary) => summary.id);
|
|
737
|
+
const { selected: targets, unknown } = (0, selection_1.selectIds)(published, ids);
|
|
738
|
+
if (unknown.length > 0) {
|
|
739
|
+
throw new CheckoutError(`No ${kind} named ${unknown.map((id) => `"${id}"`).join(', ')} is published in the registry. ` +
|
|
740
|
+
`Published ${kind}s: ${published.join(', ') || '(none)'}.`);
|
|
741
|
+
}
|
|
742
|
+
if (targets.length === 0) {
|
|
711
743
|
logger_1.Logger.success(`No ${kind}s are published in the registry; nothing to checkout.`);
|
|
712
744
|
return;
|
|
713
745
|
}
|
|
714
746
|
let checkedOut = 0;
|
|
715
747
|
const failed = [];
|
|
716
748
|
const targetDirs = [];
|
|
717
|
-
for (const
|
|
749
|
+
for (const id of targets) {
|
|
718
750
|
try {
|
|
719
751
|
const targetDir = yield checkoutSingle(handoff, kind, id, client, connection.url);
|
|
720
752
|
if (targetDir) {
|
|
@@ -728,7 +760,8 @@ const checkoutEntities = (handoff, kind) => __awaiter(void 0, void 0, void 0, fu
|
|
|
728
760
|
}
|
|
729
761
|
// Register everything checked out in one pass, so the config is edited (and confirmed) once.
|
|
730
762
|
yield registerCheckedOut(handoff, kind, targetDirs);
|
|
731
|
-
logger_1.Logger.success(`${kind[0].toUpperCase()}${kind.slice(1)}s checkout complete — ${checkedOut}
|
|
763
|
+
logger_1.Logger.success(`${kind[0].toUpperCase()}${kind.slice(1)}s checkout complete — ${checkedOut} ${handoff.dryRun ? 'would be checked out' : 'checked out'}` +
|
|
764
|
+
`${failed.length ? `, ${failed.length} failed` : ''}.`);
|
|
732
765
|
if (failed.length > 0) {
|
|
733
766
|
for (const failure of failed) {
|
|
734
767
|
logger_1.Logger.error(` - ${failure.id}: ${failure.message}`);
|
|
@@ -12,4 +12,4 @@ import Handoff from '../../index';
|
|
|
12
12
|
* Checkout all published token sets, or a single set when `setId` is given. Reconstructs `tokens.json`
|
|
13
13
|
* and restores generated files; prompts before overwriting changed local files unless `--force`.
|
|
14
14
|
*/
|
|
15
|
-
export declare const checkoutTokens: (handoff: Handoff,
|
|
15
|
+
export declare const checkoutTokens: (handoff: Handoff, selection?: string[]) => Promise<void>;
|
|
@@ -97,15 +97,18 @@ const wouldChange = (absolutePath, content) => {
|
|
|
97
97
|
* Checkout all published token sets, or a single set when `setId` is given. Reconstructs `tokens.json`
|
|
98
98
|
* and restores generated files; prompts before overwriting changed local files unless `--force`.
|
|
99
99
|
*/
|
|
100
|
-
const checkoutTokens = (handoff,
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
const checkoutTokens = (handoff, selection) => __awaiter(void 0, void 0, void 0, function* () {
|
|
101
|
+
var _a;
|
|
102
|
+
const unsupported = (_a = selection === null || selection === void 0 ? void 0 : selection.filter((id) => !(0, sets_1.isTokenSetId)(id))) !== null && _a !== void 0 ? _a : [];
|
|
103
|
+
if (unsupported.length > 0) {
|
|
104
|
+
throw new index_1.CheckoutError(`Unknown or unsafe token set id ${unsupported.map((id) => `"${id}"`).join(', ')}.`);
|
|
103
105
|
}
|
|
104
106
|
const connection = yield (0, index_1.resolveConnectionOrThrow)(handoff);
|
|
105
107
|
const client = (0, client_1.createRegistryClient)({ baseUrl: connection.url, accessToken: connection.accessToken });
|
|
108
|
+
// A named selection is trusted as-is; only a full checkout has to ask the registry what exists.
|
|
106
109
|
let ids;
|
|
107
|
-
if (
|
|
108
|
-
ids =
|
|
110
|
+
if (selection) {
|
|
111
|
+
ids = selection;
|
|
109
112
|
}
|
|
110
113
|
else {
|
|
111
114
|
const summaries = yield client.listTokenSets();
|
|
@@ -153,6 +156,10 @@ const checkoutTokens = (handoff, setId) => __awaiter(void 0, void 0, void 0, fun
|
|
|
153
156
|
}
|
|
154
157
|
}
|
|
155
158
|
const existingChanges = changed.filter((filePath) => fs_extra_1.default.existsSync(filePath));
|
|
159
|
+
if (handoff.dryRun) {
|
|
160
|
+
(0, index_1.reportPlannedWrites)(handoff, 'token sets', changed, existingChanges);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
156
163
|
if (existingChanges.length > 0 && !handoff.force) {
|
|
157
164
|
const proceed = yield p.confirm({
|
|
158
165
|
message: `Checkout will overwrite ${existingChanges.length} changed local token file(s). Continue?`,
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
* config + environment: the registry URL may be given inline or via an env-var *name*, and the
|
|
7
7
|
* access token is always read from an env-var name (never persisted as a value). This is the single
|
|
8
8
|
* source the publish (and later checkout) client reads its target + credentials from.
|
|
9
|
+
*
|
|
10
|
+
* The older `HANDOFF_CLOUD_*` names still work as deprecated fallbacks; see
|
|
11
|
+
* {@link DEPRECATED_ENV_ALIASES}.
|
|
9
12
|
*/
|
|
10
13
|
import type { Config } from '../types/config';
|
|
11
14
|
/** Default env-var name holding the remote registry base URL. */
|
|
@@ -26,6 +29,9 @@ export interface ResolvedRegistryConnection {
|
|
|
26
29
|
/**
|
|
27
30
|
* Resolve the connected-workspace registry connection from config + environment. Never throws —
|
|
28
31
|
* Callers inspect the resolved URL and access token to surface specific configuration errors.
|
|
32
|
+
*
|
|
33
|
+
* `urlEnv` and `accessTokenEnv` always report the canonical name, even when an alias supplied the
|
|
34
|
+
* value, so errors built from them point at the variable to adopt.
|
|
29
35
|
*/
|
|
30
36
|
export declare const resolveRegistryConnection: (config: Config | null | undefined) => ResolvedRegistryConnection;
|
|
31
37
|
/**
|
|
@@ -35,5 +41,9 @@ export declare const resolveRegistryConnection: (config: Config | null | undefin
|
|
|
35
41
|
* keep precedence for deterministic CI: `HANDOFF_REGISTRY_URL` (or inline config) wins over the
|
|
36
42
|
* saved URL, and `HANDOFF_REGISTRY_ACCESS_TOKEN` wins over the saved token. The saved token only
|
|
37
43
|
* applies to the URL it was issued for.
|
|
44
|
+
*
|
|
45
|
+
* A configured `HANDOFF_SYNC_SECRET` is the last resort. Being deployment-wide rather than personal,
|
|
46
|
+
* it fills in only where there is no explicit token and no login (CI, in practice), instead of quietly
|
|
47
|
+
* speaking for a developer who is signed in.
|
|
38
48
|
*/
|
|
39
49
|
export declare const resolveAuthenticatedRegistryConnection: (config: Config | null | undefined, workingPath?: string) => Promise<ResolvedRegistryConnection>;
|
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
* config + environment: the registry URL may be given inline or via an env-var *name*, and the
|
|
8
8
|
* access token is always read from an env-var name (never persisted as a value). This is the single
|
|
9
9
|
* source the publish (and later checkout) client reads its target + credentials from.
|
|
10
|
+
*
|
|
11
|
+
* The older `HANDOFF_CLOUD_*` names still work as deprecated fallbacks; see
|
|
12
|
+
* {@link DEPRECATED_ENV_ALIASES}.
|
|
10
13
|
*/
|
|
11
14
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
12
15
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
@@ -20,22 +23,60 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
20
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
24
|
exports.resolveAuthenticatedRegistryConnection = exports.resolveRegistryConnection = exports.DEFAULT_REGISTRY_ACCESS_TOKEN_ENV = exports.DEFAULT_REGISTRY_URL_ENV = void 0;
|
|
22
25
|
const store_1 = require("../cli/auth/store");
|
|
26
|
+
// Imported straight from the module, not the `auth` barrel, to keep the database schema off the CLI
|
|
27
|
+
// startup path; this file is resolved for every command.
|
|
28
|
+
const sync_secret_1 = require("./auth/sync-secret");
|
|
29
|
+
const logger_1 = require("../utils/logger");
|
|
23
30
|
/** Default env-var name holding the remote registry base URL. */
|
|
24
31
|
exports.DEFAULT_REGISTRY_URL_ENV = 'HANDOFF_REGISTRY_URL';
|
|
25
32
|
/** Default env-var name holding the connected-workspace access token. */
|
|
26
33
|
exports.DEFAULT_REGISTRY_ACCESS_TOKEN_ENV = 'HANDOFF_REGISTRY_ACCESS_TOKEN';
|
|
34
|
+
/**
|
|
35
|
+
* Old env-var names we still read, keyed by the name that replaced them. Keeps workspaces set up
|
|
36
|
+
* from the older docs working instead of silently reporting no registry.
|
|
37
|
+
*/
|
|
38
|
+
const DEPRECATED_ENV_ALIASES = {
|
|
39
|
+
[exports.DEFAULT_REGISTRY_URL_ENV]: 'HANDOFF_CLOUD_URL',
|
|
40
|
+
[exports.DEFAULT_REGISTRY_ACCESS_TOKEN_ENV]: 'HANDOFF_CLOUD_TOKEN',
|
|
41
|
+
};
|
|
42
|
+
/** Aliases we've already warned about, so each one warns once per run and not once per lookup. */
|
|
43
|
+
const warnedAliases = new Set();
|
|
44
|
+
/**
|
|
45
|
+
* Value of the named env var, falling back to its deprecated alias. Only the default names have
|
|
46
|
+
* aliases: a custom `urlEnv`/`accessTokenEnv` is a deliberate choice, so a stray `HANDOFF_CLOUD_*`
|
|
47
|
+
* must not stand in for it. The warning goes through {@link Logger.warn}, so
|
|
48
|
+
* `HANDOFF_LOG_LEVEL=error` silences it in CI.
|
|
49
|
+
*/
|
|
50
|
+
const readConnectionEnv = (name) => {
|
|
51
|
+
var _a, _b;
|
|
52
|
+
const configured = (_a = process.env[name]) === null || _a === void 0 ? void 0 : _a.trim();
|
|
53
|
+
if (configured)
|
|
54
|
+
return configured;
|
|
55
|
+
const alias = DEPRECATED_ENV_ALIASES[name];
|
|
56
|
+
const inherited = alias ? (_b = process.env[alias]) === null || _b === void 0 ? void 0 : _b.trim() : '';
|
|
57
|
+
if (!inherited)
|
|
58
|
+
return '';
|
|
59
|
+
if (!warnedAliases.has(alias)) {
|
|
60
|
+
warnedAliases.add(alias);
|
|
61
|
+
logger_1.Logger.warn(`${alias} is deprecated and will be removed in a future release; set ${name} instead.`);
|
|
62
|
+
}
|
|
63
|
+
return inherited;
|
|
64
|
+
};
|
|
27
65
|
/**
|
|
28
66
|
* Resolve the connected-workspace registry connection from config + environment. Never throws —
|
|
29
67
|
* Callers inspect the resolved URL and access token to surface specific configuration errors.
|
|
68
|
+
*
|
|
69
|
+
* `urlEnv` and `accessTokenEnv` always report the canonical name, even when an alias supplied the
|
|
70
|
+
* value, so errors built from them point at the variable to adopt.
|
|
30
71
|
*/
|
|
31
72
|
const resolveRegistryConnection = (config) => {
|
|
32
|
-
var _a, _b, _c, _d
|
|
73
|
+
var _a, _b, _c, _d;
|
|
33
74
|
const connection = (_a = config === null || config === void 0 ? void 0 : config.runtime) === null || _a === void 0 ? void 0 : _a.registryConnection;
|
|
34
75
|
const urlEnv = ((_b = connection === null || connection === void 0 ? void 0 : connection.urlEnv) === null || _b === void 0 ? void 0 : _b.trim()) || exports.DEFAULT_REGISTRY_URL_ENV;
|
|
35
76
|
const accessTokenEnv = ((_c = connection === null || connection === void 0 ? void 0 : connection.accessTokenEnv) === null || _c === void 0 ? void 0 : _c.trim()) || exports.DEFAULT_REGISTRY_ACCESS_TOKEN_ENV;
|
|
36
77
|
const inlineUrl = (_d = connection === null || connection === void 0 ? void 0 : connection.url) === null || _d === void 0 ? void 0 : _d.trim();
|
|
37
|
-
const url =
|
|
38
|
-
const accessToken = (
|
|
78
|
+
const url = inlineUrl || readConnectionEnv(urlEnv);
|
|
79
|
+
const accessToken = readConnectionEnv(accessTokenEnv);
|
|
39
80
|
return {
|
|
40
81
|
url,
|
|
41
82
|
accessToken,
|
|
@@ -51,14 +92,18 @@ exports.resolveRegistryConnection = resolveRegistryConnection;
|
|
|
51
92
|
* keep precedence for deterministic CI: `HANDOFF_REGISTRY_URL` (or inline config) wins over the
|
|
52
93
|
* saved URL, and `HANDOFF_REGISTRY_ACCESS_TOKEN` wins over the saved token. The saved token only
|
|
53
94
|
* applies to the URL it was issued for.
|
|
95
|
+
*
|
|
96
|
+
* A configured `HANDOFF_SYNC_SECRET` is the last resort. Being deployment-wide rather than personal,
|
|
97
|
+
* it fills in only where there is no explicit token and no login (CI, in practice), instead of quietly
|
|
98
|
+
* speaking for a developer who is signed in.
|
|
54
99
|
*/
|
|
55
100
|
const resolveAuthenticatedRegistryConnection = (config_1, ...args_1) => __awaiter(void 0, [config_1, ...args_1], void 0, function* (config, workingPath = process.cwd()) {
|
|
56
101
|
const connection = (0, exports.resolveRegistryConnection)(config);
|
|
57
102
|
const auth = yield (0, store_1.readCliAuth)(workingPath);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
const accessToken = connection.accessToken || (
|
|
103
|
+
const login = (0, store_1.cliAuthTokenIsValid)(auth) ? auth : null;
|
|
104
|
+
const url = connection.url || (login === null || login === void 0 ? void 0 : login.remoteUrl) || '';
|
|
105
|
+
const savedToken = login && (0, store_1.cliAuthMatchesRegistry)(login, url) ? login.accessToken : '';
|
|
106
|
+
const accessToken = connection.accessToken || savedToken || (0, sync_secret_1.resolveRegistrySyncSecret)();
|
|
62
107
|
return Object.assign(Object.assign({}, connection), { url, accessToken });
|
|
63
108
|
});
|
|
64
109
|
exports.resolveAuthenticatedRegistryConnection = resolveAuthenticatedRegistryConnection;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logical content-kind vocabulary: the dependency-light boundary shared by the CLI, the
|
|
3
|
+
* publish/checkout orchestration, and the compatibility layer.
|
|
4
|
+
*
|
|
5
|
+
* A workspace publishes and checks out five kinds of content. Components, patterns and pages are
|
|
6
|
+
* entities with a singular wire kind ({@link TransferEntityKind}); tokens and assets travel through
|
|
7
|
+
* their own transfer endpoints. This module owns only the stable plural vocabulary and its ordering,
|
|
8
|
+
* so it stays free of fs, DB and React dependencies.
|
|
9
|
+
*/
|
|
10
|
+
import type { TransferEntityKind } from './transfer';
|
|
11
|
+
/** Stable identity used to publish `pages/index.md` while serving it at the root route. */
|
|
12
|
+
export declare const HOME_PAGE_ID = "index";
|
|
13
|
+
export declare const HOME_PAGE_PATH = "/";
|
|
14
|
+
/** Plural content kinds accepted wherever a publish or checkout target is named. */
|
|
15
|
+
export declare const REGISTRY_ENTITY_KINDS: readonly ["components", "patterns", "pages", "tokens", "assets"];
|
|
16
|
+
export type RegistryEntityKind = (typeof REGISTRY_ENTITY_KINDS)[number];
|
|
17
|
+
/** Map the plural content kind to the singular wire kind used by entity publish/checkout. */
|
|
18
|
+
export declare const ENTITY_WIRE_KIND: Record<'components' | 'patterns' | 'pages', TransferEntityKind>;
|
|
19
|
+
/** The entity kinds, in the order a full run visits them: patterns compose components, pages reference both. */
|
|
20
|
+
export declare const ENTITY_KIND_ORDER: readonly ["components", "patterns", "pages"];
|
|
21
|
+
/**
|
|
22
|
+
* Every kind, in dependency order. Tokens and assets come first because rendered component
|
|
23
|
+
* artifacts reference the CSS variables and asset URLs they produce.
|
|
24
|
+
*/
|
|
25
|
+
export declare const ALL_KIND_ORDER: readonly RegistryEntityKind[];
|
|
26
|
+
/** Whether a string names a supported content kind. */
|
|
27
|
+
export declare const isRegistryEntityKind: (value: string) => value is RegistryEntityKind;
|
|
@@ -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`,
|