@hs-x/cli 0.2.3 → 0.2.4
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/dist/commands/deploy.d.ts +45 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +151 -6
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +149 -14
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/status.js +49 -4
- package/dist/commands/status.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/package.json +7 -7
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { type WorkerManifest } from '@hs-x/codegen';
|
|
2
|
+
import type { AppEventDeclaration, AppObjectAssociationDeclaration, AppObjectDeclaration, CardDefinition } from '@hs-x/types';
|
|
3
|
+
import type { AppBillingDeclaration } from '@hs-x/types';
|
|
1
4
|
import * as Command from '@effect/cli/Command';
|
|
2
5
|
import { Option } from 'effect';
|
|
3
6
|
import { Cwd } from '../services/cwd.js';
|
|
@@ -7,6 +10,21 @@ export declare function deployCommand({ argv, root, json, }: {
|
|
|
7
10
|
readonly root: string;
|
|
8
11
|
readonly json: boolean;
|
|
9
12
|
}): Promise<CliResult>;
|
|
13
|
+
interface CloudflareWorkerDeployResult {
|
|
14
|
+
readonly workerName: string;
|
|
15
|
+
readonly workerSourcePath: string;
|
|
16
|
+
readonly entrypointPath: string;
|
|
17
|
+
readonly configPath?: string;
|
|
18
|
+
readonly url: string | undefined;
|
|
19
|
+
readonly command: readonly string[];
|
|
20
|
+
readonly dryRun: boolean;
|
|
21
|
+
readonly stdout: string;
|
|
22
|
+
readonly stderr: string;
|
|
23
|
+
}
|
|
24
|
+
interface CloudflareDeployResult {
|
|
25
|
+
readonly deployId: string;
|
|
26
|
+
readonly workers: readonly CloudflareWorkerDeployResult[];
|
|
27
|
+
}
|
|
10
28
|
export declare function renderWranglerConfig(input: {
|
|
11
29
|
readonly workerName: string;
|
|
12
30
|
readonly entrypointPath: string;
|
|
@@ -22,6 +40,32 @@ export declare function renderWranglerConfig(input: {
|
|
|
22
40
|
/** Enable Workers Logs ingestion (linked deploys; read by the dashboard tail). */
|
|
23
41
|
readonly observability?: boolean;
|
|
24
42
|
}): string;
|
|
43
|
+
export declare function ensureHubSpotRuntimeProjectArtifacts({ argv, root, app, workers, cloudflareDeployResult, needsRuntime, controlPlanePlan, }: {
|
|
44
|
+
readonly argv: readonly string[];
|
|
45
|
+
readonly root: string;
|
|
46
|
+
readonly app: HsxAppConfig;
|
|
47
|
+
readonly workers: readonly WorkerManifest[];
|
|
48
|
+
readonly cloudflareDeployResult: CloudflareDeployResult | undefined;
|
|
49
|
+
readonly needsRuntime: boolean;
|
|
50
|
+
readonly controlPlanePlan?: {
|
|
51
|
+
readonly runtimeBindings?: {
|
|
52
|
+
readonly oauthCallbackPath: string;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
}): Promise<void>;
|
|
56
|
+
interface HsxAppConfig {
|
|
57
|
+
readonly appName: string;
|
|
58
|
+
readonly description?: string;
|
|
59
|
+
readonly distribution: string;
|
|
60
|
+
readonly auth: string;
|
|
61
|
+
readonly platformVersion: string;
|
|
62
|
+
readonly scopes: readonly string[];
|
|
63
|
+
readonly appObjects: readonly AppObjectDeclaration[];
|
|
64
|
+
readonly appObjectAssociations: readonly AppObjectAssociationDeclaration[];
|
|
65
|
+
readonly appEvents: readonly AppEventDeclaration[];
|
|
66
|
+
readonly cards: readonly CardDefinition[];
|
|
67
|
+
readonly billing?: AppBillingDeclaration;
|
|
68
|
+
}
|
|
25
69
|
export declare const deployCmd: Command.Command<"deploy", Cwd, import("../cli-error.js").CliError, {
|
|
26
70
|
readonly json: boolean;
|
|
27
71
|
readonly cwd: Option.Option<string>;
|
|
@@ -76,4 +120,5 @@ export declare const deployCmd: Command.Command<"deploy", Cwd, import("../cli-er
|
|
|
76
120
|
readonly yes: boolean;
|
|
77
121
|
}>;
|
|
78
122
|
}>;
|
|
123
|
+
export {};
|
|
79
124
|
//# sourceMappingURL=deploy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/deploy.js
CHANGED
|
@@ -1367,6 +1367,37 @@ async function executeCloudflareWorkerDeploy({ argv, root, worker, index, source
|
|
|
1367
1367
|
tenantDataPlane?.flagsKvNamespaceName,
|
|
1368
1368
|
tenantDataPlane?.tenantD1DatabaseName,
|
|
1369
1369
|
].filter((name) => typeof name === 'string' && name.toLowerCase().startsWith(accountResourcePrefix));
|
|
1370
|
+
// wrangler does NOT auto-apply D1 migrations on deploy (found live:
|
|
1371
|
+
// tenant DBs had zero tables across every real deploy since ADR-014) —
|
|
1372
|
+
// apply them explicitly before the worker code that expects them lands.
|
|
1373
|
+
const migrationsDirExists = await stat(join(root, 'node_modules', '@hs-x', 'runtime', 'migrations'))
|
|
1374
|
+
.then((entry) => entry.isDirectory())
|
|
1375
|
+
.catch(() => false);
|
|
1376
|
+
if (tenantDataPlane?.tenantD1DatabaseName && !dryRun && !migrationsDirExists) {
|
|
1377
|
+
process.stderr.write('warning: @hs-x/runtime did not ship its tenant migrations (pre-0.2.4 package) — tenant D1 tables were NOT applied; upgrade @hs-x/runtime.\n');
|
|
1378
|
+
}
|
|
1379
|
+
if (tenantDataPlane?.tenantD1DatabaseName && !dryRun && migrationsDirExists) {
|
|
1380
|
+
try {
|
|
1381
|
+
await runCloudflareCommand([
|
|
1382
|
+
'bun',
|
|
1383
|
+
'x',
|
|
1384
|
+
'wrangler',
|
|
1385
|
+
'd1',
|
|
1386
|
+
'migrations',
|
|
1387
|
+
'apply',
|
|
1388
|
+
tenantDataPlane.tenantD1DatabaseName,
|
|
1389
|
+
'--remote',
|
|
1390
|
+
'--config',
|
|
1391
|
+
relative(root, configPath),
|
|
1392
|
+
], { cwd: root, env: cloudflareDeployEnv(argv) });
|
|
1393
|
+
}
|
|
1394
|
+
catch (error) {
|
|
1395
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1396
|
+
if (!/No migrations to apply/i.test(message)) {
|
|
1397
|
+
throw new Error(`Tenant D1 migrations failed: ${message.slice(-300)}`);
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1370
1401
|
const command = [
|
|
1371
1402
|
'bun',
|
|
1372
1403
|
'x',
|
|
@@ -1470,7 +1501,19 @@ async function executeCloudflareWorkerDeploy({ argv, root, worker, index, source
|
|
|
1470
1501
|
installKvNamespaceName: installRuntimeBinding.installKvNamespaceName,
|
|
1471
1502
|
tokenKeySecretName: installRuntimeBinding.tokenKeySecretName,
|
|
1472
1503
|
runtimeBaseUrl: resolvedRuntimeUrl,
|
|
1473
|
-
...(tenantDataPlane
|
|
1504
|
+
...(tenantDataPlane
|
|
1505
|
+
? {
|
|
1506
|
+
syncGrantSecretName: tenantDataPlane.syncGrantSecretName,
|
|
1507
|
+
// Read-through legs need these registered: exemplars read the
|
|
1508
|
+
// tenant D1; flag tooling reads the flags KV.
|
|
1509
|
+
tenantD1DatabaseId: tenantDataPlane.tenantD1DatabaseId,
|
|
1510
|
+
tenantD1DatabaseName: tenantDataPlane.tenantD1DatabaseName,
|
|
1511
|
+
flagsKvNamespaceId: tenantDataPlane.flagsKvNamespaceId,
|
|
1512
|
+
...(tenantDataPlane.flagsKvNamespaceName
|
|
1513
|
+
? { flagsKvNamespaceName: tenantDataPlane.flagsKvNamespaceName }
|
|
1514
|
+
: {}),
|
|
1515
|
+
}
|
|
1516
|
+
: {}),
|
|
1474
1517
|
});
|
|
1475
1518
|
}
|
|
1476
1519
|
catch (error) {
|
|
@@ -1655,6 +1698,10 @@ async function putDeployInstallRuntimeBinding(input) {
|
|
|
1655
1698
|
tokenKeySecretName: input.tokenKeySecretName,
|
|
1656
1699
|
...(input.runtimeBaseUrl ? { runtimeBaseUrl: input.runtimeBaseUrl } : {}),
|
|
1657
1700
|
...(input.syncGrantSecretName ? { syncGrantSecretName: input.syncGrantSecretName } : {}),
|
|
1701
|
+
...(input.tenantD1DatabaseId ? { tenantD1DatabaseId: input.tenantD1DatabaseId } : {}),
|
|
1702
|
+
...(input.tenantD1DatabaseName ? { tenantD1DatabaseName: input.tenantD1DatabaseName } : {}),
|
|
1703
|
+
...(input.flagsKvNamespaceId ? { flagsKvNamespaceId: input.flagsKvNamespaceId } : {}),
|
|
1704
|
+
...(input.flagsKvNamespaceName ? { flagsKvNamespaceName: input.flagsKvNamespaceName } : {}),
|
|
1658
1705
|
},
|
|
1659
1706
|
});
|
|
1660
1707
|
const body = await response.json();
|
|
@@ -1836,6 +1883,14 @@ export function renderWranglerConfig(input) {
|
|
|
1836
1883
|
`name = "${tomlString(input.workerName)}"`,
|
|
1837
1884
|
`main = "${tomlString(input.entrypointPath)}"`,
|
|
1838
1885
|
`compatibility_date = "${tomlString(input.compatibilityDate)}"`,
|
|
1886
|
+
// nodejs_compat: guide + scaffold code reads `process.env.X` at module
|
|
1887
|
+
// scope (e.g. defineSource hmac secrets); without it the Worker fails
|
|
1888
|
+
// validation with "process is not defined" (Cloudflare error 10021,
|
|
1889
|
+
// cold-stranger run #2 blocker A). With nodejs_compat and a
|
|
1890
|
+
// compatibility_date >= 2025-04-01, Workers auto-populate process.env
|
|
1891
|
+
// from bindings/secrets; the explicit populate flag keeps that true even
|
|
1892
|
+
// when --compatibility-date is set to an older date.
|
|
1893
|
+
'compatibility_flags = [ "nodejs_compat", "nodejs_compat_populate_process_env" ]',
|
|
1839
1894
|
];
|
|
1840
1895
|
if (input.installKvNamespaceId) {
|
|
1841
1896
|
lines.push('', '[[kv_namespaces]]', 'binding = "INSTALL_KV"', `id = "${tomlString(input.installKvNamespaceId)}"`);
|
|
@@ -1891,7 +1946,7 @@ async function readDeployHubSpotOAuthSecret(input) {
|
|
|
1891
1946
|
function extractWorkerUrl(output) {
|
|
1892
1947
|
return /https:\/\/[a-zA-Z0-9.-]+\.workers\.dev\b/.exec(output)?.[0];
|
|
1893
1948
|
}
|
|
1894
|
-
async function ensureHubSpotRuntimeProjectArtifacts({ argv, root, app, workers, cloudflareDeployResult, needsRuntime, controlPlanePlan, }) {
|
|
1949
|
+
export async function ensureHubSpotRuntimeProjectArtifacts({ argv, root, app, workers, cloudflareDeployResult, needsRuntime, controlPlanePlan, }) {
|
|
1895
1950
|
if (!needsRuntime) {
|
|
1896
1951
|
return;
|
|
1897
1952
|
}
|
|
@@ -1912,6 +1967,14 @@ async function ensureHubSpotRuntimeProjectArtifacts({ argv, root, app, workers,
|
|
|
1912
1967
|
});
|
|
1913
1968
|
for (const [file, contents] of Object.entries(generated.files)) {
|
|
1914
1969
|
const path = join(root, file);
|
|
1970
|
+
// src/app/cards/package.json is user-owned once it exists: HubSpot's own
|
|
1971
|
+
// build errors instruct users to add card dependencies (e.g. hs-uix) to
|
|
1972
|
+
// it, and regenerating the fixed-dep version here silently dropped those
|
|
1973
|
+
// deps so the card never built (cold-stranger run #2 blocker B). Keep the
|
|
1974
|
+
// user's file verbatim; only generate one when absent.
|
|
1975
|
+
if (file === 'src/app/cards/package.json' && (await stat(path).catch(() => undefined))) {
|
|
1976
|
+
continue;
|
|
1977
|
+
}
|
|
1915
1978
|
await mkdir(dirname(path), { recursive: true });
|
|
1916
1979
|
await writeFile(path, contents);
|
|
1917
1980
|
}
|
|
@@ -2716,6 +2779,7 @@ async function requestHostedControlPlaneDeployRecordAndMaybePromote({ request, p
|
|
|
2716
2779
|
signedManifest: request.manifest,
|
|
2717
2780
|
signature: `sig_${plan.deployId}`,
|
|
2718
2781
|
bundleKey: deployBundleKey({ projectId: request.projectId, deployId: plan.deployId }),
|
|
2782
|
+
...(await collectDeployProvenance(userId)),
|
|
2719
2783
|
},
|
|
2720
2784
|
});
|
|
2721
2785
|
const body = await response.json();
|
|
@@ -2854,14 +2918,59 @@ function stringProperty(source, propertyName) {
|
|
|
2854
2918
|
return match ? { [propertyName]: match } : {};
|
|
2855
2919
|
}
|
|
2856
2920
|
function schemaProperty(source) {
|
|
2857
|
-
|
|
2921
|
+
// Balanced-brace extraction. The previous non-greedy regex stopped at the
|
|
2922
|
+
// first `}` inside an object-form field (`score: { type: "number", … }`),
|
|
2923
|
+
// truncating the schema and mis-reading nested keys (`type`) as property
|
|
2924
|
+
// names — which made portal-schema plans wrong for any schema that mixes
|
|
2925
|
+
// shorthand strings with object declarations.
|
|
2926
|
+
const body = objectLiteralBody(source, 'schema');
|
|
2858
2927
|
if (!body) {
|
|
2859
2928
|
return {};
|
|
2860
2929
|
}
|
|
2861
2930
|
const schema = {};
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2931
|
+
let index = 0;
|
|
2932
|
+
while (index < body.length) {
|
|
2933
|
+
const entry = /^[\s,]*([A-Za-z_$][\w$]*)\s*:\s*/.exec(body.slice(index));
|
|
2934
|
+
if (!entry || !entry[1])
|
|
2935
|
+
break;
|
|
2936
|
+
const name = entry[1];
|
|
2937
|
+
const valueStart = index + entry[0].length;
|
|
2938
|
+
const char = body[valueStart];
|
|
2939
|
+
if (char === '"' || char === "'" || char === '`') {
|
|
2940
|
+
const close = body.indexOf(char, valueStart + 1);
|
|
2941
|
+
if (close === -1)
|
|
2942
|
+
break;
|
|
2943
|
+
schema[name] = body.slice(valueStart + 1, close);
|
|
2944
|
+
index = close + 1;
|
|
2945
|
+
}
|
|
2946
|
+
else if (char === '{') {
|
|
2947
|
+
let depth = 0;
|
|
2948
|
+
let end = -1;
|
|
2949
|
+
for (let i = valueStart; i < body.length; i += 1) {
|
|
2950
|
+
if (body[i] === '{')
|
|
2951
|
+
depth += 1;
|
|
2952
|
+
else if (body[i] === '}') {
|
|
2953
|
+
depth -= 1;
|
|
2954
|
+
if (depth === 0) {
|
|
2955
|
+
end = i;
|
|
2956
|
+
break;
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
}
|
|
2960
|
+
if (end === -1)
|
|
2961
|
+
break;
|
|
2962
|
+
const inner = body.slice(valueStart + 1, end);
|
|
2963
|
+
const type = /type\s*:\s*["'`]([^"'`]+)["'`]/.exec(inner)?.[1];
|
|
2964
|
+
const property = /property\s*:\s*["'`]([^"'`]+)["'`]/.exec(inner)?.[1];
|
|
2965
|
+
schema[name] = { ...(type ? { type } : {}), ...(property ? { property } : {}) };
|
|
2966
|
+
index = end + 1;
|
|
2967
|
+
}
|
|
2968
|
+
else {
|
|
2969
|
+
// Unsupported value shape (identifier, number); skip to the next entry.
|
|
2970
|
+
const nextComma = body.indexOf(',', valueStart);
|
|
2971
|
+
if (nextComma === -1)
|
|
2972
|
+
break;
|
|
2973
|
+
index = nextComma + 1;
|
|
2865
2974
|
}
|
|
2866
2975
|
}
|
|
2867
2976
|
return Object.keys(schema).length > 0 ? { schema } : {};
|
|
@@ -3169,4 +3278,40 @@ const deployPromoteSubCmd = Command.make('promote', {
|
|
|
3169
3278
|
yield* runQuarantined(opts.json, () => deployCommand({ argv, root, json: opts.json }));
|
|
3170
3279
|
})));
|
|
3171
3280
|
export const deployCmd = deployBaseCmd.pipe(Command.withSubcommands([deployPromoteSubCmd]));
|
|
3281
|
+
/**
|
|
3282
|
+
* Provenance for the active-deploy card, collected where it's cheapest: the
|
|
3283
|
+
* CLI knows its own version, the git state of the tree it deployed, and the
|
|
3284
|
+
* session identity. Everything best-effort — a non-git tree stamps nothing.
|
|
3285
|
+
*/
|
|
3286
|
+
async function collectDeployProvenance(userId) {
|
|
3287
|
+
const git = async (args) => {
|
|
3288
|
+
try {
|
|
3289
|
+
const proc = Bun.spawn({ cmd: ['git', ...args], stdout: 'pipe', stderr: 'ignore' });
|
|
3290
|
+
const [out, code] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
|
3291
|
+
const trimmed = out.trim();
|
|
3292
|
+
return code === 0 && trimmed ? trimmed : undefined;
|
|
3293
|
+
}
|
|
3294
|
+
catch {
|
|
3295
|
+
return undefined;
|
|
3296
|
+
}
|
|
3297
|
+
};
|
|
3298
|
+
const [gitCommit, gitBranch] = await Promise.all([
|
|
3299
|
+
git(['rev-parse', '--short', 'HEAD']),
|
|
3300
|
+
git(['rev-parse', '--abbrev-ref', 'HEAD']),
|
|
3301
|
+
]);
|
|
3302
|
+
let deployedBy;
|
|
3303
|
+
try {
|
|
3304
|
+
const { loadStore, activeSession } = await import('../account-store.js');
|
|
3305
|
+
deployedBy = activeSession(await loadStore())?.email ?? userId;
|
|
3306
|
+
}
|
|
3307
|
+
catch {
|
|
3308
|
+
deployedBy = userId;
|
|
3309
|
+
}
|
|
3310
|
+
return {
|
|
3311
|
+
cliVersion: `hs-x@${CLI_VERSION}`,
|
|
3312
|
+
...(gitCommit ? { gitCommit } : {}),
|
|
3313
|
+
...(gitBranch ? { gitBranch } : {}),
|
|
3314
|
+
...(deployedBy ? { deployedBy } : {}),
|
|
3315
|
+
};
|
|
3316
|
+
}
|
|
3172
3317
|
//# sourceMappingURL=deploy.js.map
|