@topogram/cli 0.3.60 → 0.3.62
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/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -153,6 +153,17 @@ const KNOWN_CLI_CONSUMER_WORKFLOWS = {
|
|
|
153
153
|
"topogram-hello": "Topogram Package Verification",
|
|
154
154
|
"topograms": "Catalog Verification"
|
|
155
155
|
};
|
|
156
|
+
const KNOWN_CLI_CONSUMER_WORKFLOW_JOBS = {
|
|
157
|
+
"topograms": [
|
|
158
|
+
"Validate catalog",
|
|
159
|
+
"Smoke native starter",
|
|
160
|
+
"Smoke starter alias (hello-web)",
|
|
161
|
+
"Smoke starter alias (hello-api)",
|
|
162
|
+
"Smoke starter alias (hello-db)",
|
|
163
|
+
"Smoke starter alias (web-api)",
|
|
164
|
+
"Smoke starter alias (web-api-db)"
|
|
165
|
+
]
|
|
166
|
+
};
|
|
156
167
|
const PACKAGE_UPDATE_CLI_CHECK_SCRIPTS = [
|
|
157
168
|
"cli:surface",
|
|
158
169
|
"doctor",
|
|
@@ -199,7 +210,7 @@ function printUsage(options = {}) {
|
|
|
199
210
|
console.log(" or: topogram widget check [path] [--projection <id>] [--widget <id>] [--json]");
|
|
200
211
|
console.log(" or: topogram widget behavior [path] [--projection <id>] [--widget <id>] [--json]");
|
|
201
212
|
console.log(" or: topogram generate [path] [--out <path>]");
|
|
202
|
-
console.log(" or: topogram
|
|
213
|
+
console.log(" or: topogram emit <target> [path] [--json|--write --out-dir <path>]");
|
|
203
214
|
console.log(" or: topogram query list [--json]");
|
|
204
215
|
console.log(" or: topogram query show <name> [--json]");
|
|
205
216
|
console.log(" or: topogram trust template [path]");
|
|
@@ -259,6 +270,8 @@ function printUsage(options = {}) {
|
|
|
259
270
|
console.log(" topogram query list");
|
|
260
271
|
console.log(" topogram query show widget-behavior");
|
|
261
272
|
console.log(" topogram query widget-behavior ./topogram --projection proj_web_surface --json");
|
|
273
|
+
console.log(" topogram emit ui-widget-contract --widget widget_data_grid --json");
|
|
274
|
+
console.log(" topogram emit widget-conformance-report ./topogram --projection proj_web_surface --json");
|
|
262
275
|
console.log(" topogram generator list");
|
|
263
276
|
console.log(" topogram generator show @topogram/generator-react-web");
|
|
264
277
|
console.log(" topogram generator check ./generator-package");
|
|
@@ -327,7 +340,8 @@ function printUsage(options = {}) {
|
|
|
327
340
|
console.log(" or: topogram template show <id> [--json] [--catalog <path-or-source>]");
|
|
328
341
|
console.log(" or: topogram import app <path> [--from <track[,track]>] [--write]");
|
|
329
342
|
console.log(" or: topogram validate <path>");
|
|
330
|
-
console.log(" or: node ./src/cli.js <path> [--json] [--validate] [--resolve] [--
|
|
343
|
+
console.log(" or: node ./src/cli.js <path> [--json] [--validate] [--resolve] [--workflow <name>] [--mode <id>] [--from <track[,track]>] [--adopt <selector>] [--refresh-adopted] [--shape <id>] [--capability <id>] [--widget <id>] [--projection <id>] [--entity <id>] [--journey <id>] [--surface <id>] [--task <id>] [--profile <id>] [--from-snapshot <path>] [--from-topogram <path>] [--write] [--out-dir <path>]");
|
|
344
|
+
console.log(" or: node ./src/cli.js emit <target> [path] [--json] [--write] [--out-dir <path>]");
|
|
331
345
|
console.log(" or: node ./src/cli.js import app <path> [--from <track[,track]>] [--write]");
|
|
332
346
|
console.log(" or: node ./src/cli.js import docs <path> [--write]");
|
|
333
347
|
console.log(" or: node ./src/cli.js generate journeys <path> [--write]");
|
|
@@ -400,11 +414,23 @@ function printNewHelp() {
|
|
|
400
414
|
function printGenerateHelp() {
|
|
401
415
|
console.log("Usage: topogram generate [path] [--out <path>]");
|
|
402
416
|
console.log(" or: topogram generate app [path] [--out <path>]");
|
|
403
|
-
console.log(" or: topogram generate [path] --generate <target> [--json]");
|
|
404
|
-
console.log(" or: topogram generate [path] --generate <target> --write [--out-dir <path>]");
|
|
405
417
|
console.log("");
|
|
406
418
|
console.log("Defaults: path is ./topogram and app generation writes ./app.");
|
|
407
|
-
console.log("
|
|
419
|
+
console.log("Use `topogram emit <target>` for contracts, reports, snapshots, migration plans, and other artifacts.");
|
|
420
|
+
console.log("");
|
|
421
|
+
console.log("Examples:");
|
|
422
|
+
console.log(" topogram generate");
|
|
423
|
+
console.log(" topogram generate ./topogram --out ./app");
|
|
424
|
+
console.log(" topogram generate app ./topogram --out ./app");
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function printEmitHelp() {
|
|
428
|
+
console.log("Usage: topogram emit <target> [path] [--json]");
|
|
429
|
+
console.log(" or: topogram emit <target> [path] --write [--out-dir <path>]");
|
|
430
|
+
console.log("");
|
|
431
|
+
console.log("Emits named contracts, reports, snapshots, migration plans, and other artifacts.");
|
|
432
|
+
console.log("");
|
|
433
|
+
console.log("Defaults: path is ./topogram. Emit prints to stdout unless --write is passed. --write writes ./artifacts unless --out-dir is supplied.");
|
|
408
434
|
console.log("");
|
|
409
435
|
console.log("Common artifact targets:");
|
|
410
436
|
console.log(" ui-widget-contract");
|
|
@@ -422,13 +448,12 @@ function printGenerateHelp() {
|
|
|
422
448
|
console.log(" --journey <id>");
|
|
423
449
|
console.log("");
|
|
424
450
|
console.log("Examples:");
|
|
425
|
-
console.log(" topogram
|
|
426
|
-
console.log(" topogram
|
|
427
|
-
console.log(" topogram
|
|
428
|
-
console.log(" topogram
|
|
429
|
-
console.log(" topogram
|
|
430
|
-
console.log(" topogram
|
|
431
|
-
console.log(" topogram generate ./topogram --generate ui-widget-contract --write --out-dir ./contracts");
|
|
451
|
+
console.log(" topogram emit ui-widget-contract --widget widget_data_grid --json");
|
|
452
|
+
console.log(" topogram emit widget-conformance-report ./topogram --projection proj_web_surface --json");
|
|
453
|
+
console.log(" topogram emit widget-behavior-report ./topogram --projection proj_web_surface --json");
|
|
454
|
+
console.log(" topogram emit db-schema-snapshot ./topogram --projection proj_db_postgres --json");
|
|
455
|
+
console.log(" topogram emit sql-migration ./topogram --projection proj_db_postgres --from-snapshot ./state/current.json");
|
|
456
|
+
console.log(" topogram emit ui-widget-contract --write --out-dir ./contracts");
|
|
432
457
|
}
|
|
433
458
|
|
|
434
459
|
function printWidgetHelp() {
|
|
@@ -858,6 +883,10 @@ function printCommandHelp(command) {
|
|
|
858
883
|
printGenerateHelp();
|
|
859
884
|
return true;
|
|
860
885
|
}
|
|
886
|
+
if (command === "emit") {
|
|
887
|
+
printEmitHelp();
|
|
888
|
+
return true;
|
|
889
|
+
}
|
|
861
890
|
if (command === "widget") {
|
|
862
891
|
printWidgetHelp();
|
|
863
892
|
return true;
|
|
@@ -3211,6 +3240,14 @@ function expectedConsumerWorkflowName(name) {
|
|
|
3211
3240
|
return KNOWN_CLI_CONSUMER_WORKFLOWS[name] || null;
|
|
3212
3241
|
}
|
|
3213
3242
|
|
|
3243
|
+
/**
|
|
3244
|
+
* @param {string} name
|
|
3245
|
+
* @returns {string[]}
|
|
3246
|
+
*/
|
|
3247
|
+
function expectedConsumerWorkflowJobs(name) {
|
|
3248
|
+
return KNOWN_CLI_CONSUMER_WORKFLOW_JOBS[name] || [];
|
|
3249
|
+
}
|
|
3250
|
+
|
|
3214
3251
|
/**
|
|
3215
3252
|
* @param {string[]} args
|
|
3216
3253
|
* @param {string} cwd
|
|
@@ -3351,16 +3388,18 @@ function waitForConsumerCi(consumer, options = {}) {
|
|
|
3351
3388
|
/**
|
|
3352
3389
|
* @param {{ name: string, root?: string|null, workflow?: string|null }} consumer
|
|
3353
3390
|
* @param {{ strict?: boolean }} [options]
|
|
3354
|
-
* @returns {{ checked: boolean, ok: boolean|null, expectedWorkflow: string|null, headSha: string|null, run: { databaseId?: number, workflowName?: string, status?: string, conclusion?: string, headSha?: string, url?: string }|null, diagnostics: Array<Record<string, any>> }}
|
|
3391
|
+
* @returns {{ checked: boolean, ok: boolean|null, expectedWorkflow: string|null, expectedJobs: string[], headSha: string|null, run: { databaseId?: number, workflowName?: string, status?: string, conclusion?: string, headSha?: string, url?: string, jobs?: Array<Record<string, any>> }|null, diagnostics: Array<Record<string, any>> }}
|
|
3355
3392
|
*/
|
|
3356
3393
|
function inspectConsumerCi(consumer, options = {}) {
|
|
3357
3394
|
const diagnostics = [];
|
|
3358
3395
|
const expectedWorkflow = consumer.workflow || expectedConsumerWorkflowName(consumer.name);
|
|
3396
|
+
const expectedJobs = expectedConsumerWorkflowJobs(consumer.name);
|
|
3359
3397
|
if (!consumer.root || !fs.existsSync(consumer.root)) {
|
|
3360
3398
|
return {
|
|
3361
3399
|
checked: false,
|
|
3362
3400
|
ok: null,
|
|
3363
3401
|
expectedWorkflow,
|
|
3402
|
+
expectedJobs,
|
|
3364
3403
|
headSha: null,
|
|
3365
3404
|
run: null,
|
|
3366
3405
|
diagnostics: []
|
|
@@ -3388,6 +3427,7 @@ function inspectConsumerCi(consumer, options = {}) {
|
|
|
3388
3427
|
checked: true,
|
|
3389
3428
|
ok: false,
|
|
3390
3429
|
expectedWorkflow,
|
|
3430
|
+
expectedJobs,
|
|
3391
3431
|
headSha,
|
|
3392
3432
|
run: null,
|
|
3393
3433
|
diagnostics
|
|
@@ -3424,6 +3464,7 @@ function inspectConsumerCi(consumer, options = {}) {
|
|
|
3424
3464
|
checked: true,
|
|
3425
3465
|
ok: false,
|
|
3426
3466
|
expectedWorkflow,
|
|
3467
|
+
expectedJobs,
|
|
3427
3468
|
headSha,
|
|
3428
3469
|
run: null,
|
|
3429
3470
|
diagnostics
|
|
@@ -3454,6 +3495,7 @@ function inspectConsumerCi(consumer, options = {}) {
|
|
|
3454
3495
|
checked: true,
|
|
3455
3496
|
ok: false,
|
|
3456
3497
|
expectedWorkflow,
|
|
3498
|
+
expectedJobs,
|
|
3457
3499
|
headSha,
|
|
3458
3500
|
run: null,
|
|
3459
3501
|
diagnostics
|
|
@@ -3477,17 +3519,105 @@ function inspectConsumerCi(consumer, options = {}) {
|
|
|
3477
3519
|
suggestedFix: "Wait for or fix the consumer verification workflow, then rerun release status."
|
|
3478
3520
|
});
|
|
3479
3521
|
}
|
|
3522
|
+
if (expectedJobs.length > 0 && run.databaseId) {
|
|
3523
|
+
const jobResult = inspectConsumerWorkflowJobs(consumer, run.databaseId, expectedJobs, options);
|
|
3524
|
+
if (jobResult.jobs) {
|
|
3525
|
+
run.jobs = jobResult.jobs;
|
|
3526
|
+
}
|
|
3527
|
+
diagnostics.push(...jobResult.diagnostics);
|
|
3528
|
+
} else if (expectedJobs.length > 0) {
|
|
3529
|
+
diagnostics.push({
|
|
3530
|
+
code: "release_consumer_ci_jobs_unavailable",
|
|
3531
|
+
severity: options.strict ? "error" : "warning",
|
|
3532
|
+
message: `${consumer.name} ${expectedWorkflow} run did not include a database id, so expected jobs could not be inspected.`,
|
|
3533
|
+
path: run.url || `attebury/${consumer.name}`,
|
|
3534
|
+
suggestedFix: "Rerun release status after GitHub exposes the workflow run id."
|
|
3535
|
+
});
|
|
3536
|
+
}
|
|
3537
|
+
const errorCount = diagnostics.filter((diagnostic) => diagnostic.severity === "error").length;
|
|
3480
3538
|
return {
|
|
3481
3539
|
checked: true,
|
|
3482
|
-
ok:
|
|
3540
|
+
ok: errorCount === 0 &&
|
|
3483
3541
|
(!options.strict || (run.status === "completed" && run.conclusion === "success" && (!headSha || !run.headSha || run.headSha === headSha))),
|
|
3484
3542
|
expectedWorkflow,
|
|
3543
|
+
expectedJobs,
|
|
3485
3544
|
headSha,
|
|
3486
3545
|
run,
|
|
3487
3546
|
diagnostics
|
|
3488
3547
|
};
|
|
3489
3548
|
}
|
|
3490
3549
|
|
|
3550
|
+
/**
|
|
3551
|
+
* @param {{ name: string, root?: string|null }} consumer
|
|
3552
|
+
* @param {number|string} runId
|
|
3553
|
+
* @param {string[]} expectedJobs
|
|
3554
|
+
* @param {{ strict?: boolean }} [options]
|
|
3555
|
+
* @returns {{ jobs: Array<Record<string, any>>|null, diagnostics: Array<Record<string, any>> }}
|
|
3556
|
+
*/
|
|
3557
|
+
function inspectConsumerWorkflowJobs(consumer, runId, expectedJobs, options = {}) {
|
|
3558
|
+
const diagnostics = [];
|
|
3559
|
+
const result = childProcess.spawnSync("gh", [
|
|
3560
|
+
"run",
|
|
3561
|
+
"view",
|
|
3562
|
+
String(runId),
|
|
3563
|
+
"--repo",
|
|
3564
|
+
`attebury/${consumer.name}`,
|
|
3565
|
+
"--json",
|
|
3566
|
+
"jobs"
|
|
3567
|
+
], {
|
|
3568
|
+
cwd: consumer.root || process.cwd(),
|
|
3569
|
+
encoding: "utf8",
|
|
3570
|
+
env: { ...process.env, PATH: process.env.PATH || "" }
|
|
3571
|
+
});
|
|
3572
|
+
if (result.status !== 0) {
|
|
3573
|
+
diagnostics.push(commandDiagnostic({
|
|
3574
|
+
code: "release_consumer_ci_jobs_unavailable",
|
|
3575
|
+
severity: options.strict ? "error" : "warning",
|
|
3576
|
+
message: `Could not inspect expected jobs for ${consumer.name}.`,
|
|
3577
|
+
path: `attebury/${consumer.name}`,
|
|
3578
|
+
suggestedFix: "Check GitHub CLI auth/network access, then rerun release status.",
|
|
3579
|
+
result
|
|
3580
|
+
}));
|
|
3581
|
+
return { jobs: null, diagnostics };
|
|
3582
|
+
}
|
|
3583
|
+
let payload = {};
|
|
3584
|
+
try {
|
|
3585
|
+
payload = JSON.parse(String(result.stdout || "{}"));
|
|
3586
|
+
} catch (error) {
|
|
3587
|
+
diagnostics.push({
|
|
3588
|
+
code: "release_consumer_ci_jobs_unreadable",
|
|
3589
|
+
severity: options.strict ? "error" : "warning",
|
|
3590
|
+
message: `Could not parse ${consumer.name} workflow job status: ${messageFromError(error)}`,
|
|
3591
|
+
path: `attebury/${consumer.name}`,
|
|
3592
|
+
suggestedFix: "Rerun release status after GitHub CLI output is valid JSON."
|
|
3593
|
+
});
|
|
3594
|
+
}
|
|
3595
|
+
const jobs = Array.isArray(payload.jobs) ? payload.jobs : [];
|
|
3596
|
+
for (const expectedJob of expectedJobs) {
|
|
3597
|
+
const job = jobs.find((candidate) => candidate?.name === expectedJob);
|
|
3598
|
+
if (!job) {
|
|
3599
|
+
diagnostics.push({
|
|
3600
|
+
code: "release_consumer_ci_job_missing",
|
|
3601
|
+
severity: options.strict ? "error" : "warning",
|
|
3602
|
+
message: `${consumer.name} workflow is missing expected job '${expectedJob}'.`,
|
|
3603
|
+
path: `attebury/${consumer.name}`,
|
|
3604
|
+
suggestedFix: "Update the consumer workflow or the release-status expected job list, then rerun release status."
|
|
3605
|
+
});
|
|
3606
|
+
continue;
|
|
3607
|
+
}
|
|
3608
|
+
if (job.status !== "completed" || job.conclusion !== "success") {
|
|
3609
|
+
diagnostics.push({
|
|
3610
|
+
code: "release_consumer_ci_job_not_successful",
|
|
3611
|
+
severity: options.strict ? "error" : "warning",
|
|
3612
|
+
message: `${consumer.name} job '${expectedJob}' is ${job.status || "unknown"}/${job.conclusion || "unknown"}.`,
|
|
3613
|
+
path: job.url || `attebury/${consumer.name}`,
|
|
3614
|
+
suggestedFix: "Wait for or fix the expected workflow job, then rerun release status."
|
|
3615
|
+
});
|
|
3616
|
+
}
|
|
3617
|
+
}
|
|
3618
|
+
return { jobs, diagnostics };
|
|
3619
|
+
}
|
|
3620
|
+
|
|
3491
3621
|
/**
|
|
3492
3622
|
* @param {string} cwd
|
|
3493
3623
|
* @returns {Array<{ name: string, root: string|null, path: string, version: string|null, found: boolean }>}
|
|
@@ -8482,12 +8612,18 @@ if (args[0] === "version" || args[0] === "--version") {
|
|
|
8482
8612
|
} else if (args[0] === "generator") {
|
|
8483
8613
|
printGeneratorHelp();
|
|
8484
8614
|
process.exit(args[1] ? 1 : 0);
|
|
8615
|
+
} else if (args[0] === "emit") {
|
|
8616
|
+
if (!args[1] || args[1].startsWith("-")) {
|
|
8617
|
+
printEmitHelp();
|
|
8618
|
+
process.exit(1);
|
|
8619
|
+
}
|
|
8620
|
+
commandArgs = { generateTarget: args[1], inputPath: commandPath(2), emitArtifact: true };
|
|
8485
8621
|
} else if (args[0] === "validate") {
|
|
8486
8622
|
commandArgs = { validate: true, inputPath: args[1] };
|
|
8487
8623
|
} else if (args[0] === "generate" && args[1] === "app") {
|
|
8488
8624
|
commandArgs = { generateTarget: "app-bundle", write: true, inputPath: commandPath(2), defaultOutDir: "./app" };
|
|
8489
8625
|
} else if (args[0] === "generate" && args.indexOf("--generate") >= 0) {
|
|
8490
|
-
commandArgs = { inputPath: commandPath(1) };
|
|
8626
|
+
commandArgs = { inputPath: commandPath(1), deprecatedGenerateArtifact: true };
|
|
8491
8627
|
} else if (args[0] === "generate" && args[1] !== "journeys") {
|
|
8492
8628
|
commandArgs = { generateTarget: "app-bundle", write: true, inputPath: commandPath(1), defaultOutDir: "./app" };
|
|
8493
8629
|
} else if (args[0] === "trust" && args[1] === "template") {
|
|
@@ -8727,8 +8863,13 @@ const shouldValidate = Boolean(commandArgs?.validate) || args.includes("--valida
|
|
|
8727
8863
|
const shouldResolve = args.includes("--resolve");
|
|
8728
8864
|
const generateIndex = args.indexOf("--generate");
|
|
8729
8865
|
const generateTarget = commandArgs?.generateTarget || (generateIndex >= 0 ? args[generateIndex + 1] : null);
|
|
8866
|
+
if (commandArgs?.deprecatedGenerateArtifact && (!generateTarget || generateTarget.startsWith("-"))) {
|
|
8867
|
+
console.error("Missing required --generate <target>.");
|
|
8868
|
+
printUsage();
|
|
8869
|
+
process.exit(1);
|
|
8870
|
+
}
|
|
8730
8871
|
if (RENAMED_GENERATE_TARGETS.has(generateTarget)) {
|
|
8731
|
-
console.error(`
|
|
8872
|
+
console.error(`Artifact target '${generateTarget}' was renamed to '${RENAMED_GENERATE_TARGETS.get(generateTarget)}'.`);
|
|
8732
8873
|
process.exit(1);
|
|
8733
8874
|
}
|
|
8734
8875
|
const workflowIndex = args.indexOf("--workflow");
|
|
@@ -11461,6 +11602,9 @@ try {
|
|
|
11461
11602
|
const ast = parsePath(inputPath);
|
|
11462
11603
|
|
|
11463
11604
|
if (generateTarget) {
|
|
11605
|
+
if (commandArgs?.deprecatedGenerateArtifact) {
|
|
11606
|
+
console.error(`Deprecated: use \`topogram emit ${generateTarget} ${inputPath || "./topogram"}\` instead of \`topogram generate ${inputPath || "./topogram"} --generate ${generateTarget}\`.`);
|
|
11607
|
+
}
|
|
11464
11608
|
const projectRoot = normalizeProjectRoot(inputPath);
|
|
11465
11609
|
const explicitProjectConfig = loadProjectConfig(projectRoot) || loadProjectConfig(inputPath);
|
|
11466
11610
|
const implementationOptionalTargets = new Set(["app-bundle-plan", "app-bundle", "environment-plan", "environment-bundle", "compile-check-plan", "compile-check-bundle"]);
|
|
@@ -352,17 +352,17 @@ infer_current_snapshot_from_live_tables() {
|
|
|
352
352
|
}
|
|
353
353
|
|
|
354
354
|
generate_desired_snapshot() {
|
|
355
|
-
"$TOPOGRAM_BIN"
|
|
355
|
+
"$TOPOGRAM_BIN" emit db-schema-snapshot "$INPUT_PATH" --projection "$PROJECTION_ID" > "$DESIRED_SNAPSHOT"
|
|
356
356
|
}
|
|
357
357
|
|
|
358
358
|
generate_migration_plan() {
|
|
359
359
|
local from_snapshot="$1"
|
|
360
|
-
"$TOPOGRAM_BIN"
|
|
360
|
+
"$TOPOGRAM_BIN" emit db-migration-plan "$INPUT_PATH" --projection "$PROJECTION_ID" --from-snapshot "$from_snapshot" > "$PLAN_JSON"
|
|
361
361
|
}
|
|
362
362
|
|
|
363
363
|
generate_sql_migration() {
|
|
364
364
|
local from_snapshot="$1"
|
|
365
|
-
"$TOPOGRAM_BIN" "$INPUT_PATH" --
|
|
365
|
+
"$TOPOGRAM_BIN" emit sql-migration "$INPUT_PATH" --projection "$PROJECTION_ID" --from-snapshot "$from_snapshot" > "$MIGRATION_SQL"
|
|
366
366
|
}
|
|
367
367
|
|
|
368
368
|
ensure_supported_plan() {
|
|
@@ -22,5 +22,5 @@ Configure the API base URL and optional auth token via scheme environment variab
|
|
|
22
22
|
From `engine/`:
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
topogram
|
|
25
|
+
topogram emit swiftui-app ./topogram --projection proj_ios_surface__swiftui --write --out-dir ./app/ios-swiftui
|
|
26
26
|
```
|
package/src/new-project.js
CHANGED
|
@@ -2046,6 +2046,8 @@ Or run self-contained local runtime verification:
|
|
|
2046
2046
|
|
|
2047
2047
|
Useful inspection:
|
|
2048
2048
|
npm run check:json
|
|
2049
|
+
topogram emit ui-widget-contract ./topogram --json
|
|
2050
|
+
topogram emit widget-conformance-report ./topogram --json
|
|
2049
2051
|
npm run doctor
|
|
2050
2052
|
npm run source:status
|
|
2051
2053
|
npm run source:status:remote
|
|
@@ -2123,6 +2125,7 @@ ${workflowCommands.join("\n")}
|
|
|
2123
2125
|
|
|
2124
2126
|
Edit \`topogram/\` and \`topogram.project.json\`, then regenerate with \`npm run generate\`.
|
|
2125
2127
|
Generated app code is written to \`app/\`.
|
|
2128
|
+
Use \`topogram emit <target>\` to inspect contracts, reports, snapshots, and other artifacts without regenerating the app.
|
|
2126
2129
|
${template.includesExecutableImplementation ? "\nThis template copied `implementation/` code. `topogram new` did not execute it; review `implementation/`, `topogram.template-policy.json`, and `.topogram-template-trust.json` before regenerating after edits.\n" : ""}
|
|
2127
2130
|
`;
|
|
2128
2131
|
fs.writeFileSync(path.join(projectRoot, "README.md"), readme, "utf8");
|