@hs-x/cli 0.2.7 → 0.3.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/LICENSE +202 -0
- package/README.md +25 -8
- package/dist/audit-interactive.d.ts +29 -0
- package/dist/audit-interactive.d.ts.map +1 -0
- package/dist/audit-interactive.js +156 -0
- package/dist/audit-interactive.js.map +1 -0
- package/dist/bin/hs-x.js.map +1 -1
- package/dist/checkpoint-interactive.d.ts +24 -0
- package/dist/checkpoint-interactive.d.ts.map +1 -0
- package/dist/checkpoint-interactive.js +65 -0
- package/dist/checkpoint-interactive.js.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +266 -39
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/kit.d.ts +1 -1
- package/dist/cli/kit.d.ts.map +1 -1
- package/dist/cli/kit.js +19 -3
- package/dist/cli/kit.js.map +1 -1
- package/dist/cloudflare-auth.d.ts +8 -0
- package/dist/cloudflare-auth.d.ts.map +1 -1
- package/dist/cloudflare-auth.js +12 -2
- package/dist/cloudflare-auth.js.map +1 -1
- package/dist/cloudflare-oauth-store.d.ts +9 -1
- package/dist/cloudflare-oauth-store.d.ts.map +1 -1
- package/dist/cloudflare-oauth-store.js +23 -4
- package/dist/cloudflare-oauth-store.js.map +1 -1
- package/dist/cloudflare-oauth.d.ts +1 -0
- package/dist/cloudflare-oauth.d.ts.map +1 -1
- package/dist/cloudflare-oauth.js +6 -2
- package/dist/cloudflare-oauth.js.map +1 -1
- package/dist/commands/account.d.ts +28 -13
- package/dist/commands/account.d.ts.map +1 -1
- package/dist/commands/account.js +219 -69
- package/dist/commands/account.js.map +1 -1
- package/dist/commands/api.d.ts.map +1 -1
- package/dist/commands/api.js +7 -11
- package/dist/commands/api.js.map +1 -1
- package/dist/commands/completion.d.ts.map +1 -1
- package/dist/commands/completion.js +13 -7
- package/dist/commands/completion.js.map +1 -1
- package/dist/commands/connect.d.ts.map +1 -1
- package/dist/commands/connect.js +239 -125
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/control-plane-read.d.ts +71 -10
- package/dist/commands/control-plane-read.d.ts.map +1 -1
- package/dist/commands/control-plane-read.js +1035 -58
- package/dist/commands/control-plane-read.js.map +1 -1
- package/dist/commands/deploy-promote.d.ts +25 -3
- package/dist/commands/deploy-promote.d.ts.map +1 -1
- package/dist/commands/deploy-promote.js +314 -38
- package/dist/commands/deploy-promote.js.map +1 -1
- package/dist/commands/deploy.d.ts +69 -4
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +1512 -276
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts +5 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +502 -107
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/doctor.d.ts +14 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +131 -20
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/flags.d.ts +108 -6
- package/dist/commands/flags.d.ts.map +1 -1
- package/dist/commands/flags.js +996 -75
- package/dist/commands/flags.js.map +1 -1
- package/dist/commands/help-command.d.ts +14 -0
- package/dist/commands/help-command.d.ts.map +1 -1
- package/dist/commands/help-command.js +144 -44
- package/dist/commands/help-command.js.map +1 -1
- package/dist/commands/history.js +14 -10
- package/dist/commands/history.js.map +1 -1
- package/dist/commands/init.d.ts +4 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +98 -17
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/link.d.ts +2 -3
- package/dist/commands/link.d.ts.map +1 -1
- package/dist/commands/link.js +201 -101
- package/dist/commands/link.js.map +1 -1
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +22 -5
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/migrate.d.ts +37 -17
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +500 -73
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/rollback.d.ts +42 -2
- package/dist/commands/rollback.d.ts.map +1 -1
- package/dist/commands/rollback.js +422 -58
- package/dist/commands/rollback.js.map +1 -1
- package/dist/commands/secrets.d.ts +76 -3
- package/dist/commands/secrets.d.ts.map +1 -1
- package/dist/commands/secrets.js +328 -41
- package/dist/commands/secrets.js.map +1 -1
- package/dist/commands/status.d.ts +13 -2
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +154 -17
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/unlink.d.ts +2 -2
- package/dist/commands/unlink.d.ts.map +1 -1
- package/dist/commands/unlink.js +2 -2
- package/dist/commands/unlink.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +4 -3
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/validate.d.ts +12 -3
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +70 -8
- package/dist/commands/validate.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/control-plane-fetch.d.ts.map +1 -1
- package/dist/control-plane-fetch.js +5 -4
- package/dist/control-plane-fetch.js.map +1 -1
- package/dist/deploy-revision-capture.d.ts +48 -0
- package/dist/deploy-revision-capture.d.ts.map +1 -0
- package/dist/deploy-revision-capture.js +125 -0
- package/dist/deploy-revision-capture.js.map +1 -0
- package/dist/dev/event-bus.d.ts +5 -2
- package/dist/dev/event-bus.d.ts.map +1 -1
- package/dist/dev/event-bus.js +4 -1
- package/dist/dev/event-bus.js.map +1 -1
- package/dist/dev/hubspot-logs-poller.d.ts +35 -0
- package/dist/dev/hubspot-logs-poller.d.ts.map +1 -0
- package/dist/dev/hubspot-logs-poller.js +94 -0
- package/dist/dev/hubspot-logs-poller.js.map +1 -0
- package/dist/dev/invoke.d.ts +9 -1
- package/dist/dev/invoke.d.ts.map +1 -1
- package/dist/dev/invoke.js +69 -20
- package/dist/dev/invoke.js.map +1 -1
- package/dist/dev/keyboard.d.ts +31 -0
- package/dist/dev/keyboard.d.ts.map +1 -0
- package/dist/dev/keyboard.js +143 -0
- package/dist/dev/keyboard.js.map +1 -0
- package/dist/dev/log-server.d.ts +7 -0
- package/dist/dev/log-server.d.ts.map +1 -1
- package/dist/dev/log-server.js +68 -31
- package/dist/dev/log-server.js.map +1 -1
- package/dist/dev/migrated-card.d.ts +17 -0
- package/dist/dev/migrated-card.d.ts.map +1 -0
- package/dist/dev/migrated-card.js +114 -0
- package/dist/dev/migrated-card.js.map +1 -0
- package/dist/dev/stream-filter.d.ts +40 -0
- package/dist/dev/stream-filter.d.ts.map +1 -0
- package/dist/dev/stream-filter.js +93 -0
- package/dist/dev/stream-filter.js.map +1 -0
- package/dist/dev/stream-renderer.d.ts +34 -0
- package/dist/dev/stream-renderer.d.ts.map +1 -1
- package/dist/dev/stream-renderer.js +222 -8
- package/dist/dev/stream-renderer.js.map +1 -1
- package/dist/errors-registry.d.ts.map +1 -1
- package/dist/errors-registry.js +280 -36
- package/dist/errors-registry.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +8 -0
- package/dist/help.js.map +1 -1
- package/dist/hubspot-app-logs.d.ts +76 -0
- package/dist/hubspot-app-logs.d.ts.map +1 -0
- package/dist/hubspot-app-logs.js +323 -0
- package/dist/hubspot-app-logs.js.map +1 -0
- package/dist/hubspot-auth.d.ts +8 -2
- package/dist/hubspot-auth.d.ts.map +1 -1
- package/dist/hubspot-auth.js +9 -9
- package/dist/hubspot-auth.js.map +1 -1
- package/dist/hubspot-developer-client.js +119 -11
- package/dist/hubspot-developer-client.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/init/templates.d.ts +11 -1
- package/dist/init/templates.d.ts.map +1 -1
- package/dist/init/templates.js +228 -15
- package/dist/init/templates.js.map +1 -1
- package/dist/link-migration-journal.d.ts +131 -0
- package/dist/link-migration-journal.d.ts.map +1 -0
- package/dist/link-migration-journal.js +395 -0
- package/dist/link-migration-journal.js.map +1 -0
- package/dist/link-preflight.d.ts +78 -0
- package/dist/link-preflight.d.ts.map +1 -0
- package/dist/link-preflight.js +231 -0
- package/dist/link-preflight.js.map +1 -0
- package/dist/local-project-picker.d.ts +34 -0
- package/dist/local-project-picker.d.ts.map +1 -0
- package/dist/local-project-picker.js +121 -0
- package/dist/local-project-picker.js.map +1 -0
- package/dist/logs-interactive.d.ts +37 -0
- package/dist/logs-interactive.d.ts.map +1 -0
- package/dist/logs-interactive.js +110 -0
- package/dist/logs-interactive.js.map +1 -0
- package/dist/output-context.d.ts +13 -0
- package/dist/output-context.d.ts.map +1 -0
- package/dist/output-context.js +46 -0
- package/dist/output-context.js.map +1 -0
- package/dist/project-context.d.ts +55 -0
- package/dist/project-context.d.ts.map +1 -0
- package/dist/project-context.js +115 -0
- package/dist/project-context.js.map +1 -0
- package/dist/project-picker.d.ts +65 -0
- package/dist/project-picker.d.ts.map +1 -0
- package/dist/project-picker.js +227 -0
- package/dist/project-picker.js.map +1 -0
- package/dist/prompt.d.ts +8 -0
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +81 -13
- package/dist/prompt.js.map +1 -1
- package/dist/reporter/human.d.ts +10 -2
- package/dist/reporter/human.d.ts.map +1 -1
- package/dist/reporter/human.js +82 -14
- package/dist/reporter/human.js.map +1 -1
- package/dist/reporter/index.d.ts.map +1 -1
- package/dist/reporter/index.js +7 -5
- package/dist/reporter/index.js.map +1 -1
- package/dist/reporter/json.d.ts +2 -1
- package/dist/reporter/json.d.ts.map +1 -1
- package/dist/reporter/json.js +10 -7
- package/dist/reporter/json.js.map +1 -1
- package/dist/reporter/style.d.ts +19 -0
- package/dist/reporter/style.d.ts.map +1 -1
- package/dist/reporter/style.js +126 -6
- package/dist/reporter/style.js.map +1 -1
- package/dist/reporter/types.d.ts +34 -4
- package/dist/reporter/types.d.ts.map +1 -1
- package/dist/reporter/types.js.map +1 -1
- package/dist/services/account-store.d.ts +3 -1
- package/dist/services/account-store.d.ts.map +1 -1
- package/dist/services/account-store.js +2 -1
- package/dist/services/account-store.js.map +1 -1
- package/dist/services/app-paths.d.ts.map +1 -1
- package/dist/services/app-paths.js +3 -1
- package/dist/services/app-paths.js.map +1 -1
- package/dist/services/cloudflare-auth.d.ts +1 -1
- package/dist/services/cloudflare-auth.d.ts.map +1 -1
- package/dist/services/cloudflare-auth.js +2 -2
- package/dist/services/cloudflare-auth.js.map +1 -1
- package/dist/services/cloudflare-kv.d.ts.map +1 -1
- package/dist/services/cloudflare-kv.js.map +1 -1
- package/dist/services/command-history.d.ts.map +1 -1
- package/dist/services/command-history.js.map +1 -1
- package/dist/services/config.d.ts.map +1 -1
- package/dist/services/config.js +1 -1
- package/dist/services/config.js.map +1 -1
- package/dist/services/control-plane.d.ts.map +1 -1
- package/dist/services/control-plane.js +3 -3
- package/dist/services/control-plane.js.map +1 -1
- package/dist/services/env-loader.d.ts.map +1 -1
- package/dist/services/env-loader.js +1 -3
- package/dist/services/env-loader.js.map +1 -1
- package/dist/services/http.d.ts.map +1 -1
- package/dist/services/http.js.map +1 -1
- package/dist/services/hubspot-dev-client.d.ts.map +1 -1
- package/dist/services/hubspot-dev-client.js +30 -0
- package/dist/services/hubspot-dev-client.js.map +1 -1
- package/dist/services/live.d.ts +2 -2
- package/dist/services/live.d.ts.map +1 -1
- package/dist/services/live.js +1 -1
- package/dist/services/live.js.map +1 -1
- package/dist/services/machine-id.d.ts.map +1 -1
- package/dist/services/machine-id.js.map +1 -1
- package/dist/services/reporter.d.ts +8 -5
- package/dist/services/reporter.d.ts.map +1 -1
- package/dist/services/reporter.js +6 -4
- package/dist/services/reporter.js.map +1 -1
- package/dist/services/tenant-state.d.ts.map +1 -1
- package/dist/services/tenant-state.js.map +1 -1
- package/dist/state-store.d.ts +4 -0
- package/dist/state-store.d.ts.map +1 -1
- package/dist/state-store.js +15 -0
- package/dist/state-store.js.map +1 -1
- package/dist/status-drilldown.d.ts +24 -0
- package/dist/status-drilldown.d.ts.map +1 -0
- package/dist/status-drilldown.js +50 -0
- package/dist/status-drilldown.js.map +1 -0
- package/dist/tenant-state.d.ts.map +1 -1
- package/dist/tenant-state.js +3 -2
- package/dist/tenant-state.js.map +1 -1
- package/dist/token-key-custody.d.ts +91 -0
- package/dist/token-key-custody.d.ts.map +1 -0
- package/dist/token-key-custody.js +375 -0
- package/dist/token-key-custody.js.map +1 -0
- package/package.json +14 -15
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type { LinkPreflightPlan } from './link-preflight.js';
|
|
2
|
+
import { type TokenKeyCustodyAdapter } from './token-key-custody.js';
|
|
3
|
+
/**
|
|
4
|
+
* Ordered step boundaries. Each is idempotent and resumable; the journal
|
|
5
|
+
* records which boundaries a run has crossed so a resumed `hs-x link` picks up
|
|
6
|
+
* exactly where a crash left off and converges to the same final state.
|
|
7
|
+
*/
|
|
8
|
+
export declare const LINK_MIGRATION_STEPS: readonly ["preflight-passed", "kv-owner-prefix-rewritten", "pointer-rewritten", "probe-passed", "cp-committed", "custody-rekeyed", "done"];
|
|
9
|
+
export type LinkMigrationStep = (typeof LINK_MIGRATION_STEPS)[number];
|
|
10
|
+
declare const LINK_MIGRATION_CODES: readonly ["HSX_E_LINK_PROBE_FAILED", "HSX_E_LINK_MIGRATION_INTERRUPTED"];
|
|
11
|
+
export type LinkMigrationErrorCode = (typeof LINK_MIGRATION_CODES)[number];
|
|
12
|
+
/** One binding's preserve-set — references only, never key bytes. */
|
|
13
|
+
export interface LinkMigrationBinding {
|
|
14
|
+
readonly projectId: string;
|
|
15
|
+
readonly environment: string;
|
|
16
|
+
readonly hubSpotAppId: number;
|
|
17
|
+
readonly installKvNamespaceId: string;
|
|
18
|
+
readonly installKvNamespaceName: string;
|
|
19
|
+
readonly tokenKeySecretName: string;
|
|
20
|
+
readonly custodyMechanism: string;
|
|
21
|
+
/** custodyId embedding the OLD owner (what the binding records pre-link). */
|
|
22
|
+
readonly oldCustodyId: string;
|
|
23
|
+
/** custodyId embedding the NEW owner (what the binding records post-link). */
|
|
24
|
+
readonly newCustodyId: string;
|
|
25
|
+
/** Authoritative Worker names the migration must never rename. */
|
|
26
|
+
readonly workerNames: readonly string[];
|
|
27
|
+
}
|
|
28
|
+
export interface LinkMigrationJournal {
|
|
29
|
+
readonly schemaVersion: 1;
|
|
30
|
+
readonly journalId: string;
|
|
31
|
+
readonly status: 'in-progress' | 'done' | 'aborted';
|
|
32
|
+
readonly cloudflareAccountId: string;
|
|
33
|
+
readonly stateKvNamespaceId: string;
|
|
34
|
+
readonly priorOwnerId: string;
|
|
35
|
+
readonly newOwnerId: string;
|
|
36
|
+
readonly bindings: readonly LinkMigrationBinding[];
|
|
37
|
+
/** Union of every binding's Worker names — authoritative, never renamed. */
|
|
38
|
+
readonly workerNames: readonly string[];
|
|
39
|
+
readonly completedSteps: readonly LinkMigrationStep[];
|
|
40
|
+
/** Whether the CP commit reported success (gates old-custody removal). */
|
|
41
|
+
readonly cpCommitOk?: boolean;
|
|
42
|
+
readonly createdAt: string;
|
|
43
|
+
readonly updatedAt: string;
|
|
44
|
+
}
|
|
45
|
+
export declare function defaultLinkMigrationJournalPath(env?: NodeJS.ProcessEnv): string;
|
|
46
|
+
/**
|
|
47
|
+
* Deterministic journal id from the STABLE identity of the claim: the
|
|
48
|
+
* Cloudflare account, the tenant-state namespace, and the TARGET owner. It does
|
|
49
|
+
* NOT depend on the prior owner, so a resumed run finds the journal even after
|
|
50
|
+
* the local pointer has already been rewritten to the new owner.
|
|
51
|
+
*/
|
|
52
|
+
export declare function linkMigrationJournalId(input: {
|
|
53
|
+
readonly cloudflareAccountId: string;
|
|
54
|
+
readonly stateKvNamespaceId: string;
|
|
55
|
+
readonly newOwnerId: string;
|
|
56
|
+
}): string;
|
|
57
|
+
export declare function loadLinkMigrationJournal(journalId: string, path?: string): Promise<LinkMigrationJournal | undefined>;
|
|
58
|
+
export declare function saveLinkMigrationJournal(journal: LinkMigrationJournal, path?: string): Promise<void>;
|
|
59
|
+
export declare function deleteLinkMigrationJournal(journalId: string, path?: string): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Build a fresh (unpersisted) journal from a passed link-preflight plan. The
|
|
62
|
+
* preserve-set — Worker names, INSTALL_KV namespace ids, custody references —
|
|
63
|
+
* comes straight from the plan's STRUCTURED fields (never the informational
|
|
64
|
+
* `checks` strings). Only escrowed bindings can reach here (the preflight fails
|
|
65
|
+
* closed on `worker-secret-only` custody), so each binding has a custody id.
|
|
66
|
+
*/
|
|
67
|
+
export declare function buildLinkMigrationJournal(input: {
|
|
68
|
+
readonly plan: LinkPreflightPlan;
|
|
69
|
+
readonly newOwnerId: string;
|
|
70
|
+
readonly now?: () => Date;
|
|
71
|
+
}): LinkMigrationJournal;
|
|
72
|
+
/** The read/write surface the migration needs over a KV namespace. */
|
|
73
|
+
export interface LinkMigrationKvClient {
|
|
74
|
+
get(key: string): Promise<string | undefined>;
|
|
75
|
+
put(key: string, value: string): Promise<void>;
|
|
76
|
+
list(prefix: string): Promise<readonly string[]>;
|
|
77
|
+
delete(key: string): Promise<void>;
|
|
78
|
+
}
|
|
79
|
+
export interface LinkProbeInput {
|
|
80
|
+
readonly binding: LinkMigrationBinding;
|
|
81
|
+
/** The escrowed token key, already proven to open the binding's canary. */
|
|
82
|
+
readonly resolvedTokenKey: string;
|
|
83
|
+
/** Read-only handle bound to the binding's EXACT INSTALL_KV namespace. */
|
|
84
|
+
readonly installKv: Pick<LinkMigrationKvClient, 'get' | 'list'>;
|
|
85
|
+
}
|
|
86
|
+
export type LinkProbe = (input: LinkProbeInput) => Promise<void>;
|
|
87
|
+
export interface LinkMigrationSeams {
|
|
88
|
+
readonly stateKv: LinkMigrationKvClient;
|
|
89
|
+
readonly makeInstallKv: (namespaceId: string) => Pick<LinkMigrationKvClient, 'get' | 'list'>;
|
|
90
|
+
readonly custodyAdapter: TokenKeyCustodyAdapter;
|
|
91
|
+
/** Rewrite the local pointer's ownerId (forward = new, compensate = old). */
|
|
92
|
+
readonly rewritePointer: (ownerId: string) => Promise<void>;
|
|
93
|
+
/** Commit the owner rewrite to the control plane; undefined = ok. */
|
|
94
|
+
readonly commit: () => Promise<string | undefined>;
|
|
95
|
+
readonly probe?: LinkProbe;
|
|
96
|
+
readonly now?: () => Date;
|
|
97
|
+
}
|
|
98
|
+
export interface LinkMigrationResult {
|
|
99
|
+
readonly journal: LinkMigrationJournal;
|
|
100
|
+
readonly rewrittenKeyCount: number;
|
|
101
|
+
readonly rekeyedBindingCount: number;
|
|
102
|
+
readonly notifyWarning?: string;
|
|
103
|
+
readonly resumed: boolean;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Default probe: a harmless signed proof that token resolution still works AND
|
|
107
|
+
* the exact INSTALL_KV namespace is reachable after the owner rewrite. The
|
|
108
|
+
* canary was already opened (the resolved key is passed in); this additionally
|
|
109
|
+
* performs a read against the binding's INSTALL_KV namespace (a real HubSpot
|
|
110
|
+
* call is T04/live and out of scope). Throws a typed error on failure.
|
|
111
|
+
*/
|
|
112
|
+
export declare const defaultLinkProbe: LinkProbe;
|
|
113
|
+
/**
|
|
114
|
+
* Run (or resume) the journaled link migration to convergence. Each step is
|
|
115
|
+
* skipped when the journal already records it, and every action is idempotent,
|
|
116
|
+
* so a resumed run reaches a final state identical to the unfailed path.
|
|
117
|
+
*
|
|
118
|
+
* `failAfter` injects a fatal interruption immediately AFTER a boundary is
|
|
119
|
+
* persisted — the seam the acceptance tests use to prove convergence. A probe
|
|
120
|
+
* failure (as opposed to an injected interruption after the probe boundary)
|
|
121
|
+
* compensates: the KV owner prefix and pointer are rolled back and the journal
|
|
122
|
+
* is cleared so the operation is re-runnable from a clean pre-link state.
|
|
123
|
+
*/
|
|
124
|
+
export declare function runLinkMigration(input: {
|
|
125
|
+
readonly journal: LinkMigrationJournal;
|
|
126
|
+
readonly seams: LinkMigrationSeams;
|
|
127
|
+
readonly journalPath?: string;
|
|
128
|
+
readonly failAfter?: string;
|
|
129
|
+
}): Promise<LinkMigrationResult>;
|
|
130
|
+
export {};
|
|
131
|
+
//# sourceMappingURL=link-migration-journal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-migration-journal.d.ts","sourceRoot":"","sources":["../src/link-migration-journal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
// ADR-022 T03 — journaled, resumable transactional link migration.
|
|
2
|
+
//
|
|
3
|
+
// Linking an installed project must PRESERVE Worker identity, the exact
|
|
4
|
+
// INSTALL_KV namespace, and the escrowed token key. The mutation sequence
|
|
5
|
+
// (KV owner-prefix rewrite → local pointer rewrite → token-resolution probe →
|
|
6
|
+
// control-plane owner commit → token-key custody re-key) is journaled with
|
|
7
|
+
// explicit step boundaries so an injected failure at any boundary re-runs to
|
|
8
|
+
// convergence WITHOUT replacing a key or a namespace. The probe (a harmless
|
|
9
|
+
// signed proof, BEFORE the CP commit) is the point of no return that is still
|
|
10
|
+
// reversible: if it fails, the KV owner prefix and the local pointer are rolled
|
|
11
|
+
// back to the pre-link owner and the operation aborts with a typed error.
|
|
12
|
+
//
|
|
13
|
+
// The custody re-key follows the T01 handoff: the token key is re-escrowed
|
|
14
|
+
// under the NEW owner's custody id while the OLD entry is kept, and the old
|
|
15
|
+
// entry is removed only AFTER the CP commit succeeds — so there is never a
|
|
16
|
+
// window with zero custody entries and the key bytes are never regenerated.
|
|
17
|
+
//
|
|
18
|
+
// The journal is a VERSIONED local record (CLI state dir, 0600) that carries
|
|
19
|
+
// ONLY references — owner ids, namespace ids, worker names, secret NAMES, and
|
|
20
|
+
// custody ids. It never contains key bytes or a sealed canary.
|
|
21
|
+
import { createHash } from 'node:crypto';
|
|
22
|
+
import { chmod, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
23
|
+
import { dirname } from 'node:path';
|
|
24
|
+
import { Schema, schemas } from '@hs-x/types';
|
|
25
|
+
import { CliError } from './cli-error.js';
|
|
26
|
+
import { statePath } from './paths.js';
|
|
27
|
+
import { escrowTokenKey, resolveEscrowedTokenKey, tokenKeyCustodyIdFor, } from './token-key-custody.js';
|
|
28
|
+
/**
|
|
29
|
+
* Ordered step boundaries. Each is idempotent and resumable; the journal
|
|
30
|
+
* records which boundaries a run has crossed so a resumed `hs-x link` picks up
|
|
31
|
+
* exactly where a crash left off and converges to the same final state.
|
|
32
|
+
*/
|
|
33
|
+
export const LINK_MIGRATION_STEPS = [
|
|
34
|
+
'preflight-passed',
|
|
35
|
+
'kv-owner-prefix-rewritten',
|
|
36
|
+
'pointer-rewritten',
|
|
37
|
+
'probe-passed',
|
|
38
|
+
'cp-committed',
|
|
39
|
+
'custody-rekeyed',
|
|
40
|
+
'done',
|
|
41
|
+
];
|
|
42
|
+
const LINK_MIGRATION_CODES = [
|
|
43
|
+
// The token-resolution probe failed AFTER the KV/pointer rewrite: the owner
|
|
44
|
+
// prefix and pointer were rolled back to the pre-link owner. Re-runnable.
|
|
45
|
+
'HSX_E_LINK_PROBE_FAILED',
|
|
46
|
+
// A journaled boundary was crossed and then the run was interrupted (the
|
|
47
|
+
// injected-failure seam, or a real crash). Re-running `hs-x link` resumes.
|
|
48
|
+
'HSX_E_LINK_MIGRATION_INTERRUPTED',
|
|
49
|
+
];
|
|
50
|
+
const EMPTY_STORE = { schemaVersion: 1, journals: {} };
|
|
51
|
+
export function defaultLinkMigrationJournalPath(env = process.env) {
|
|
52
|
+
const override = env.HSX_LINK_JOURNAL_PATH;
|
|
53
|
+
if (override && override.length > 0)
|
|
54
|
+
return override;
|
|
55
|
+
return statePath('link-migrations.json');
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Deterministic journal id from the STABLE identity of the claim: the
|
|
59
|
+
* Cloudflare account, the tenant-state namespace, and the TARGET owner. It does
|
|
60
|
+
* NOT depend on the prior owner, so a resumed run finds the journal even after
|
|
61
|
+
* the local pointer has already been rewritten to the new owner.
|
|
62
|
+
*/
|
|
63
|
+
export function linkMigrationJournalId(input) {
|
|
64
|
+
return createHash('sha256')
|
|
65
|
+
.update(`${input.cloudflareAccountId}\n${input.stateKvNamespaceId}\n${input.newOwnerId}`)
|
|
66
|
+
.digest('hex')
|
|
67
|
+
.slice(0, 24);
|
|
68
|
+
}
|
|
69
|
+
async function loadStore(path) {
|
|
70
|
+
let raw;
|
|
71
|
+
try {
|
|
72
|
+
raw = await readFile(path, 'utf8');
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return EMPTY_STORE;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const parsed = JSON.parse(raw);
|
|
79
|
+
if (parsed.schemaVersion !== 1 ||
|
|
80
|
+
typeof parsed.journals !== 'object' ||
|
|
81
|
+
parsed.journals === null) {
|
|
82
|
+
return EMPTY_STORE;
|
|
83
|
+
}
|
|
84
|
+
return { schemaVersion: 1, journals: parsed.journals };
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return EMPTY_STORE;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function writeStore(path, store) {
|
|
91
|
+
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
92
|
+
await writeFile(path, `${JSON.stringify(store, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
93
|
+
try {
|
|
94
|
+
await chmod(path, 0o600);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// best effort on platforms without POSIX perms
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
export async function loadLinkMigrationJournal(journalId, path = defaultLinkMigrationJournalPath()) {
|
|
101
|
+
const store = await loadStore(path);
|
|
102
|
+
return store.journals[journalId];
|
|
103
|
+
}
|
|
104
|
+
export async function saveLinkMigrationJournal(journal, path = defaultLinkMigrationJournalPath()) {
|
|
105
|
+
const store = await loadStore(path);
|
|
106
|
+
await writeStore(path, {
|
|
107
|
+
schemaVersion: 1,
|
|
108
|
+
journals: { ...store.journals, [journal.journalId]: journal },
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
export async function deleteLinkMigrationJournal(journalId, path = defaultLinkMigrationJournalPath()) {
|
|
112
|
+
const store = await loadStore(path);
|
|
113
|
+
if (!(journalId in store.journals))
|
|
114
|
+
return;
|
|
115
|
+
const { [journalId]: _removed, ...rest } = store.journals;
|
|
116
|
+
const remaining = rest;
|
|
117
|
+
if (Object.keys(remaining).length === 0) {
|
|
118
|
+
await rm(path, { force: true });
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
await writeStore(path, { schemaVersion: 1, journals: remaining });
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Build a fresh (unpersisted) journal from a passed link-preflight plan. The
|
|
125
|
+
* preserve-set — Worker names, INSTALL_KV namespace ids, custody references —
|
|
126
|
+
* comes straight from the plan's STRUCTURED fields (never the informational
|
|
127
|
+
* `checks` strings). Only escrowed bindings can reach here (the preflight fails
|
|
128
|
+
* closed on `worker-secret-only` custody), so each binding has a custody id.
|
|
129
|
+
*/
|
|
130
|
+
export function buildLinkMigrationJournal(input) {
|
|
131
|
+
const nowIso = (input.now ? input.now() : new Date()).toISOString();
|
|
132
|
+
const bindings = input.plan.bindings.map((binding) => ({
|
|
133
|
+
projectId: binding.projectId,
|
|
134
|
+
environment: binding.environment,
|
|
135
|
+
hubSpotAppId: binding.hubSpotAppId,
|
|
136
|
+
installKvNamespaceId: binding.installKvNamespaceId,
|
|
137
|
+
installKvNamespaceName: binding.installKvNamespaceName,
|
|
138
|
+
tokenKeySecretName: binding.tokenKeySecretName,
|
|
139
|
+
custodyMechanism: binding.tokenKeyCustody.mechanism,
|
|
140
|
+
oldCustodyId: binding.tokenKeyCustody.custodyId,
|
|
141
|
+
newCustodyId: tokenKeyCustodyIdFor({
|
|
142
|
+
ownerId: input.newOwnerId,
|
|
143
|
+
projectId: binding.projectId,
|
|
144
|
+
environment: binding.environment,
|
|
145
|
+
hubSpotAppId: binding.hubSpotAppId,
|
|
146
|
+
}),
|
|
147
|
+
workerNames: binding.workerNames,
|
|
148
|
+
}));
|
|
149
|
+
const workerNames = [...new Set(bindings.flatMap((binding) => binding.workerNames))].sort();
|
|
150
|
+
return {
|
|
151
|
+
schemaVersion: 1,
|
|
152
|
+
journalId: linkMigrationJournalId({
|
|
153
|
+
cloudflareAccountId: input.plan.cloudflareAccountId,
|
|
154
|
+
stateKvNamespaceId: input.plan.stateKvNamespaceId,
|
|
155
|
+
newOwnerId: input.newOwnerId,
|
|
156
|
+
}),
|
|
157
|
+
status: 'in-progress',
|
|
158
|
+
cloudflareAccountId: input.plan.cloudflareAccountId,
|
|
159
|
+
stateKvNamespaceId: input.plan.stateKvNamespaceId,
|
|
160
|
+
priorOwnerId: input.plan.priorOwnerId,
|
|
161
|
+
newOwnerId: input.newOwnerId,
|
|
162
|
+
bindings,
|
|
163
|
+
workerNames,
|
|
164
|
+
completedSteps: [],
|
|
165
|
+
createdAt: nowIso,
|
|
166
|
+
updatedAt: nowIso,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function migrationError(code, detail) {
|
|
170
|
+
return new CliError({ code, message: `${code}: ${detail}` });
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Default probe: a harmless signed proof that token resolution still works AND
|
|
174
|
+
* the exact INSTALL_KV namespace is reachable after the owner rewrite. The
|
|
175
|
+
* canary was already opened (the resolved key is passed in); this additionally
|
|
176
|
+
* performs a read against the binding's INSTALL_KV namespace (a real HubSpot
|
|
177
|
+
* call is T04/live and out of scope). Throws a typed error on failure.
|
|
178
|
+
*/
|
|
179
|
+
export const defaultLinkProbe = async (input) => {
|
|
180
|
+
if (input.resolvedTokenKey.length === 0) {
|
|
181
|
+
throw migrationError('HSX_E_LINK_PROBE_FAILED', `token resolution for ${input.binding.installKvNamespaceId} produced an empty key.`);
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
// Harmless token-state read: prove the preserved INSTALL_KV namespace still
|
|
185
|
+
// answers under the rewritten owner. We do not require any entries.
|
|
186
|
+
await input.installKv.list('token-state/');
|
|
187
|
+
}
|
|
188
|
+
catch (cause) {
|
|
189
|
+
throw migrationError('HSX_E_LINK_PROBE_FAILED', `INSTALL_KV namespace ${input.binding.installKvNamespaceId} did not respond to a token-state read: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
// Owner-prefix rewrite (idempotent copy-then-delete; used forward + reverse)
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
async function rewriteOwnerPrefix(kv, from, to) {
|
|
196
|
+
const fromPrefix = `owners/${from}/`;
|
|
197
|
+
const toPrefix = `owners/${to}/`;
|
|
198
|
+
const fromKeys = await kv.list(fromPrefix);
|
|
199
|
+
if (fromKeys.length === 0)
|
|
200
|
+
return [];
|
|
201
|
+
const rewritten = [];
|
|
202
|
+
for (const fromKey of fromKeys) {
|
|
203
|
+
const value = await kv.get(fromKey);
|
|
204
|
+
if (value === undefined)
|
|
205
|
+
continue;
|
|
206
|
+
const toKey = `${toPrefix}${fromKey.slice(fromPrefix.length)}`;
|
|
207
|
+
await kv.put(toKey, rewriteOwnerIdValue(value, from, to));
|
|
208
|
+
rewritten.push({ fromKey, toKey });
|
|
209
|
+
}
|
|
210
|
+
// Delete sources only after every write succeeded, so a mid-loop crash leaves
|
|
211
|
+
// both prefixes populated rather than losing data.
|
|
212
|
+
for (const { fromKey } of rewritten) {
|
|
213
|
+
await kv.delete(fromKey);
|
|
214
|
+
}
|
|
215
|
+
return rewritten;
|
|
216
|
+
}
|
|
217
|
+
function rewriteOwnerIdValue(value, from, to) {
|
|
218
|
+
try {
|
|
219
|
+
const parsed = JSON.parse(value);
|
|
220
|
+
if (typeof parsed === 'object' &&
|
|
221
|
+
parsed !== null &&
|
|
222
|
+
!Array.isArray(parsed) &&
|
|
223
|
+
parsed.ownerId === from) {
|
|
224
|
+
return JSON.stringify({ ...parsed, ownerId: to });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// Non-JSON values keep their bytes.
|
|
229
|
+
}
|
|
230
|
+
return value;
|
|
231
|
+
}
|
|
232
|
+
function bindingKvKey(ownerId, binding) {
|
|
233
|
+
return `owners/${ownerId}/projects/${binding.projectId}/bindings/${binding.environment}/${binding.hubSpotAppId}`;
|
|
234
|
+
}
|
|
235
|
+
async function readBindingRecord(kv, ownerId, binding) {
|
|
236
|
+
const key = bindingKvKey(ownerId, binding);
|
|
237
|
+
const raw = await kv.get(key);
|
|
238
|
+
if (raw === undefined) {
|
|
239
|
+
throw migrationError('HSX_E_LINK_MIGRATION_INTERRUPTED', `binding record ${key} is missing after the owner rewrite — cannot re-key custody.`);
|
|
240
|
+
}
|
|
241
|
+
return Schema.decodeUnknownSync(schemas.LocalAppRuntimeBinding)(JSON.parse(raw));
|
|
242
|
+
}
|
|
243
|
+
// ---------------------------------------------------------------------------
|
|
244
|
+
// The journaled runner
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
function withStep(journal, step, patch, nowIso) {
|
|
247
|
+
const completedSteps = journal.completedSteps.includes(step)
|
|
248
|
+
? journal.completedSteps
|
|
249
|
+
: [...journal.completedSteps, step];
|
|
250
|
+
return { ...journal, ...patch, completedSteps, updatedAt: nowIso };
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Run (or resume) the journaled link migration to convergence. Each step is
|
|
254
|
+
* skipped when the journal already records it, and every action is idempotent,
|
|
255
|
+
* so a resumed run reaches a final state identical to the unfailed path.
|
|
256
|
+
*
|
|
257
|
+
* `failAfter` injects a fatal interruption immediately AFTER a boundary is
|
|
258
|
+
* persisted — the seam the acceptance tests use to prove convergence. A probe
|
|
259
|
+
* failure (as opposed to an injected interruption after the probe boundary)
|
|
260
|
+
* compensates: the KV owner prefix and pointer are rolled back and the journal
|
|
261
|
+
* is cleared so the operation is re-runnable from a clean pre-link state.
|
|
262
|
+
*/
|
|
263
|
+
export async function runLinkMigration(input) {
|
|
264
|
+
const path = input.journalPath ?? defaultLinkMigrationJournalPath();
|
|
265
|
+
const now = input.seams.now ?? (() => new Date());
|
|
266
|
+
const probe = input.seams.probe ?? defaultLinkProbe;
|
|
267
|
+
const resumed = input.journal.completedSteps.length > 0;
|
|
268
|
+
let journal = input.journal;
|
|
269
|
+
let rewrittenKeyCount = 0;
|
|
270
|
+
let rekeyedBindingCount = 0;
|
|
271
|
+
let notifyWarning;
|
|
272
|
+
const persist = async (step, patch = {}) => {
|
|
273
|
+
journal = withStep(journal, step, patch, now().toISOString());
|
|
274
|
+
await saveLinkMigrationJournal(journal, path);
|
|
275
|
+
if (input.failAfter && input.failAfter === step) {
|
|
276
|
+
throw migrationError('HSX_E_LINK_MIGRATION_INTERRUPTED', `injected failure after boundary "${step}".`);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
const done = (step) => journal.completedSteps.includes(step);
|
|
280
|
+
// ── preflight-passed: recorded at journal creation. ──────────────────────
|
|
281
|
+
if (!done('preflight-passed')) {
|
|
282
|
+
await persist('preflight-passed');
|
|
283
|
+
}
|
|
284
|
+
// ── KV owner-prefix rewrite (idempotent copy-then-delete). ───────────────
|
|
285
|
+
if (!done('kv-owner-prefix-rewritten')) {
|
|
286
|
+
const rewritten = await rewriteOwnerPrefix(input.seams.stateKv, journal.priorOwnerId, journal.newOwnerId);
|
|
287
|
+
rewrittenKeyCount = rewritten.length;
|
|
288
|
+
await persist('kv-owner-prefix-rewritten');
|
|
289
|
+
}
|
|
290
|
+
// ── Local pointer rewrite (idempotent). ──────────────────────────────────
|
|
291
|
+
if (!done('pointer-rewritten')) {
|
|
292
|
+
await input.seams.rewritePointer(journal.newOwnerId);
|
|
293
|
+
await persist('pointer-rewritten');
|
|
294
|
+
}
|
|
295
|
+
// ── Probe: token resolution + INSTALL_KV read. Reversible on failure. ────
|
|
296
|
+
if (!done('probe-passed')) {
|
|
297
|
+
try {
|
|
298
|
+
for (const binding of journal.bindings) {
|
|
299
|
+
const record = await readBindingRecord(input.seams.stateKv, journal.newOwnerId, binding);
|
|
300
|
+
if (record.tokenKeyCustody.kind !== 'escrowed') {
|
|
301
|
+
throw migrationError('HSX_E_LINK_PROBE_FAILED', `binding ${bindingKvKey(journal.newOwnerId, binding)} no longer records escrowed custody.`);
|
|
302
|
+
}
|
|
303
|
+
const resolvedTokenKey = await resolveEscrowedTokenKey({
|
|
304
|
+
custody: record.tokenKeyCustody,
|
|
305
|
+
adapter: input.seams.custodyAdapter,
|
|
306
|
+
});
|
|
307
|
+
await probe({
|
|
308
|
+
binding,
|
|
309
|
+
resolvedTokenKey,
|
|
310
|
+
installKv: input.seams.makeInstallKv(binding.installKvNamespaceId),
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
catch (cause) {
|
|
315
|
+
await compensate(journal, input.seams);
|
|
316
|
+
await deleteLinkMigrationJournal(journal.journalId, path);
|
|
317
|
+
if (cause instanceof CliError)
|
|
318
|
+
throw cause;
|
|
319
|
+
throw migrationError('HSX_E_LINK_PROBE_FAILED', cause instanceof Error ? cause.message : String(cause));
|
|
320
|
+
}
|
|
321
|
+
await persist('probe-passed');
|
|
322
|
+
}
|
|
323
|
+
// ── Control-plane owner commit (best-effort; gates old-custody removal). ─
|
|
324
|
+
if (!done('cp-committed')) {
|
|
325
|
+
notifyWarning = await input.seams.commit();
|
|
326
|
+
await persist('cp-committed', { cpCommitOk: notifyWarning === undefined });
|
|
327
|
+
}
|
|
328
|
+
// ── Custody re-key: re-escrow under the new owner's id, then (only once ───
|
|
329
|
+
// the CP commit succeeded) remove the old entry. Never a zero-entry gap.
|
|
330
|
+
if (!done('custody-rekeyed')) {
|
|
331
|
+
const cpCommitOk = journal.cpCommitOk === true;
|
|
332
|
+
for (const binding of journal.bindings) {
|
|
333
|
+
const record = await readBindingRecord(input.seams.stateKv, journal.newOwnerId, binding);
|
|
334
|
+
if (record.tokenKeyCustody.kind !== 'escrowed')
|
|
335
|
+
continue;
|
|
336
|
+
if (record.tokenKeyCustody.custodyId === binding.newCustodyId) {
|
|
337
|
+
// Already re-keyed on a prior (interrupted) run — just ensure the old
|
|
338
|
+
// entry is gone (idempotent) when the CP commit has succeeded.
|
|
339
|
+
if (cpCommitOk)
|
|
340
|
+
await input.seams.custodyAdapter.remove(binding.oldCustodyId);
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
// Resolve the SAME key from the old custody, re-escrow it under the new
|
|
344
|
+
// custody id (a fresh canary is sealed under the identical key — no new
|
|
345
|
+
// key is minted), then repoint the binding record at the new custody.
|
|
346
|
+
const tokenKey = await resolveEscrowedTokenKey({
|
|
347
|
+
custody: record.tokenKeyCustody,
|
|
348
|
+
adapter: input.seams.custodyAdapter,
|
|
349
|
+
});
|
|
350
|
+
const newCustody = await escrowTokenKey({
|
|
351
|
+
adapter: input.seams.custodyAdapter,
|
|
352
|
+
custodyId: binding.newCustodyId,
|
|
353
|
+
tokenKeySecretValue: tokenKey,
|
|
354
|
+
...(input.seams.now ? { now: input.seams.now } : {}),
|
|
355
|
+
});
|
|
356
|
+
const updated = {
|
|
357
|
+
...record,
|
|
358
|
+
tokenKeyCustody: newCustody,
|
|
359
|
+
updatedAt: now().toISOString(),
|
|
360
|
+
};
|
|
361
|
+
await input.seams.stateKv.put(bindingKvKey(journal.newOwnerId, binding), JSON.stringify(Schema.encodeSync(schemas.LocalAppRuntimeBinding)(updated)));
|
|
362
|
+
// Old entry removed only after the CP commit succeeded (both entries
|
|
363
|
+
// coexist until here, so there is never a window with zero custody).
|
|
364
|
+
if (cpCommitOk)
|
|
365
|
+
await input.seams.custodyAdapter.remove(binding.oldCustodyId);
|
|
366
|
+
rekeyedBindingCount += 1;
|
|
367
|
+
}
|
|
368
|
+
await persist('custody-rekeyed');
|
|
369
|
+
}
|
|
370
|
+
// ── Done. ────────────────────────────────────────────────────────────────
|
|
371
|
+
if (!done('done')) {
|
|
372
|
+
await persist('done', { status: 'done' });
|
|
373
|
+
}
|
|
374
|
+
return {
|
|
375
|
+
journal,
|
|
376
|
+
rewrittenKeyCount,
|
|
377
|
+
rekeyedBindingCount,
|
|
378
|
+
resumed,
|
|
379
|
+
...(notifyWarning !== undefined ? { notifyWarning } : {}),
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Roll the reversible steps back to the pre-link owner: reverse the KV owner
|
|
384
|
+
* prefix (new → old) and the local pointer. Custody is untouched at probe time
|
|
385
|
+
* (re-key runs later), so there is nothing to undo there.
|
|
386
|
+
*/
|
|
387
|
+
async function compensate(journal, seams) {
|
|
388
|
+
if (journal.completedSteps.includes('kv-owner-prefix-rewritten')) {
|
|
389
|
+
await rewriteOwnerPrefix(seams.stateKv, journal.newOwnerId, journal.priorOwnerId);
|
|
390
|
+
}
|
|
391
|
+
if (journal.completedSteps.includes('pointer-rewritten')) {
|
|
392
|
+
await seams.rewritePointer(journal.priorOwnerId);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
//# sourceMappingURL=link-migration-journal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-migration-journal.js","sourceRoot":"","sources":["../src/link-migration-journal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Schema, schemas } from '@hs-x/types';
|
|
2
|
+
import { type TokenKeyCustodyAdapter } from './token-key-custody.js';
|
|
3
|
+
type CloudflarePointer = Schema.Schema.Type<typeof schemas.CloudflarePointer>;
|
|
4
|
+
declare const LINK_PREFLIGHT_CODES: readonly ["HSX_E_LINK_PREFLIGHT_NAMESPACE", "HSX_E_LINK_PREFLIGHT_STATE_CORRUPT", "HSX_E_LINK_PREFLIGHT_AMBIGUOUS", "HSX_E_LINK_PREFLIGHT_UNPROVEN"];
|
|
5
|
+
export type LinkPreflightErrorCode = (typeof LINK_PREFLIGHT_CODES)[number];
|
|
6
|
+
/** Read-only slice of the tenant-state KV surface the preflight is allowed. */
|
|
7
|
+
export interface LinkPreflightReadOnlyKv {
|
|
8
|
+
get(key: string): Promise<string | undefined>;
|
|
9
|
+
list(prefix: string): Promise<readonly string[]>;
|
|
10
|
+
}
|
|
11
|
+
export interface LinkPreflightNamespaceInfo {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly title: string;
|
|
14
|
+
}
|
|
15
|
+
/** One binding's redacted continuity plan — names and ids, never key bytes. */
|
|
16
|
+
export interface LinkPreflightBindingPlan {
|
|
17
|
+
readonly projectId: string;
|
|
18
|
+
readonly environment: string;
|
|
19
|
+
readonly hubSpotAppId: number;
|
|
20
|
+
/** Worker names recorded by the active deploy for this project/environment. */
|
|
21
|
+
readonly workerNames: readonly string[];
|
|
22
|
+
readonly installKvNamespaceId: string;
|
|
23
|
+
readonly installKvNamespaceName: string;
|
|
24
|
+
readonly tokenKeySecretName: string;
|
|
25
|
+
readonly tokenKeyCustody: {
|
|
26
|
+
readonly kind: 'escrowed';
|
|
27
|
+
readonly mechanism: string;
|
|
28
|
+
readonly custodyId: string;
|
|
29
|
+
/** The escrowed key was retrieved and opened the sealed canary. */
|
|
30
|
+
readonly canaryProven: true;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export interface LinkPreflightPlan {
|
|
34
|
+
readonly plan: 'link-preflight';
|
|
35
|
+
readonly schemaVersion: 1;
|
|
36
|
+
readonly priorOwnerId: string;
|
|
37
|
+
readonly newOwnerId?: string;
|
|
38
|
+
readonly cloudflareAccountId: string;
|
|
39
|
+
readonly stateKvNamespaceId: string;
|
|
40
|
+
readonly ownerMetaPresent: boolean;
|
|
41
|
+
readonly bindings: readonly LinkPreflightBindingPlan[];
|
|
42
|
+
/** Deterministic, ordered record of what was proven. */
|
|
43
|
+
readonly checks: readonly string[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Read-only Cloudflare KV namespace metadata lookup:
|
|
47
|
+
* `GET /client/v4/accounts/{account}/storage/kv/namespaces/{id}`.
|
|
48
|
+
* `undefined` = the namespace does not exist. Transport / auth failures throw
|
|
49
|
+
* `HSX_E_API_NETWORK` (they are not evidence of a missing namespace).
|
|
50
|
+
*/
|
|
51
|
+
export declare function fetchCloudflareKvNamespace(input: {
|
|
52
|
+
readonly accountId: string;
|
|
53
|
+
readonly apiToken: string;
|
|
54
|
+
readonly namespaceId: string;
|
|
55
|
+
readonly baseUrl?: string;
|
|
56
|
+
readonly fetchImpl?: typeof fetch;
|
|
57
|
+
}): Promise<LinkPreflightNamespaceInfo | undefined>;
|
|
58
|
+
/**
|
|
59
|
+
* Run the ADR-022 continuity preflight for one owner prefix. Read-only by
|
|
60
|
+
* construction: the KV surface exposes only get/list, the namespace lookup is
|
|
61
|
+
* a metadata GET, and custody resolution is the T01 read-only primitive.
|
|
62
|
+
* Throws typed `CliError`s (the link-preflight codes above, or the T01
|
|
63
|
+
* token-key-custody codes); returns the redacted plan on success.
|
|
64
|
+
*/
|
|
65
|
+
export declare function runLinkPreflight(input: {
|
|
66
|
+
readonly pointer: CloudflarePointer;
|
|
67
|
+
/** The `local_*` (or current) owner prefix link intends to rewrite. */
|
|
68
|
+
readonly priorOwnerId: string;
|
|
69
|
+
/** Known claim target; omitted in the dashboard flow (target chosen later). */
|
|
70
|
+
readonly newOwnerId?: string;
|
|
71
|
+
readonly apiToken?: string;
|
|
72
|
+
readonly baseUrl?: string;
|
|
73
|
+
readonly kv?: LinkPreflightReadOnlyKv;
|
|
74
|
+
readonly getNamespace?: (namespaceId: string) => Promise<LinkPreflightNamespaceInfo | undefined>;
|
|
75
|
+
readonly custodyAdapter?: TokenKeyCustodyAdapter;
|
|
76
|
+
}): Promise<LinkPreflightPlan>;
|
|
77
|
+
export {};
|
|
78
|
+
//# sourceMappingURL=link-preflight.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-preflight.d.ts","sourceRoot":"","sources":["../src/link-preflight.ts"],"names":[],"mappings":"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"}
|