@myna-sh/cli 0.1.3 → 0.2.0
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/dsl.d.ts +46 -3
- package/dist/dsl.js +20 -0
- package/dist/dsl.js.map +1 -1
- package/dist/main.js +333 -5
- package/dist/main.js.map +1 -1
- package/package.json +22 -15
package/dist/main.js
CHANGED
|
@@ -223,7 +223,9 @@ function renderField(field, depth) {
|
|
|
223
223
|
const optional = field.required ? "" : "?";
|
|
224
224
|
const doc = field.description ? `${indent}/** ${escapeComment(field.description)} */
|
|
225
225
|
` : "";
|
|
226
|
-
|
|
226
|
+
const base = fieldType(field, depth);
|
|
227
|
+
const type = field.localized ? `{ [locale: string]: ${base} }` : base;
|
|
228
|
+
return `${doc}${indent}${safeKey(field.key)}${optional}: ${type};`;
|
|
227
229
|
}
|
|
228
230
|
function fieldType(field, depth) {
|
|
229
231
|
switch (field.type) {
|
|
@@ -250,6 +252,21 @@ function fieldType(field, depth) {
|
|
|
250
252
|
return renderObject(field, depth);
|
|
251
253
|
case "list":
|
|
252
254
|
return `${listItemType(field.item, depth)}[]`;
|
|
255
|
+
case "richText":
|
|
256
|
+
return "RichTextDoc";
|
|
257
|
+
case "blocks": {
|
|
258
|
+
const inner = " ".repeat(depth + 1);
|
|
259
|
+
const options = field.blocks.map((b) => {
|
|
260
|
+
const lines = b.fields.map((f) => renderField(f, depth + 2));
|
|
261
|
+
return `{ type: ${JSON.stringify(b.key)}; fields: {
|
|
262
|
+
${lines.join("\n")}
|
|
263
|
+
${inner}} }`;
|
|
264
|
+
});
|
|
265
|
+
return `Array<
|
|
266
|
+
${inner}${options.join(`
|
|
267
|
+
${inner}| `)}
|
|
268
|
+
${" ".repeat(depth)}>`;
|
|
269
|
+
}
|
|
253
270
|
default:
|
|
254
271
|
return "unknown";
|
|
255
272
|
}
|
|
@@ -291,7 +308,12 @@ ${closingIndent}}`;
|
|
|
291
308
|
return "unknown";
|
|
292
309
|
}
|
|
293
310
|
}
|
|
294
|
-
var JSON_VALUE =
|
|
311
|
+
var JSON_VALUE = [
|
|
312
|
+
"export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };",
|
|
313
|
+
"",
|
|
314
|
+
"/** Portable rich-text document tree (never rendered HTML). */",
|
|
315
|
+
'export interface RichTextDoc { type: "doc"; content: Array<{ type: string; [key: string]: unknown }>; }'
|
|
316
|
+
].join("\n");
|
|
295
317
|
function generateTypesModule(collections) {
|
|
296
318
|
return `${JSON_VALUE}
|
|
297
319
|
|
|
@@ -368,7 +390,12 @@ var ManagementClient = class {
|
|
|
368
390
|
create: (organization, body) => this.mutate("POST", `/organizations/${enc(organization)}/projects`, body),
|
|
369
391
|
get: (project, signal) => this.get(`/projects/${enc(project)}`, void 0, signal),
|
|
370
392
|
update: (project, body) => this.mutate("PATCH", `/projects/${enc(project)}`, body),
|
|
371
|
-
archive: (project) => this.mutate("POST", `/projects/${enc(project)}/archive`)
|
|
393
|
+
archive: (project) => this.mutate("POST", `/projects/${enc(project)}/archive`),
|
|
394
|
+
/** Full project export: schemas, entries (draft + published), asset metadata. */
|
|
395
|
+
export: (project, signal) => this.get(`/projects/${enc(project)}/export`, void 0, signal),
|
|
396
|
+
views: (project, signal) => this.get(`/projects/${enc(project)}/views`, void 0, signal),
|
|
397
|
+
createView: (project, body) => this.mutate("POST", `/projects/${enc(project)}/views`, body),
|
|
398
|
+
deleteView: (project, view) => this.mutate("DELETE", `/projects/${enc(project)}/views/${enc(view)}`)
|
|
372
399
|
};
|
|
373
400
|
// --- Schema ---------------------------------------------------------------
|
|
374
401
|
schema = {
|
|
@@ -380,6 +407,13 @@ var ManagementClient = class {
|
|
|
380
407
|
collections,
|
|
381
408
|
allowDestructive: opts.allowDestructive ?? false,
|
|
382
409
|
changeSummary: opts.changeSummary
|
|
410
|
+
}),
|
|
411
|
+
// Environments as projects
|
|
412
|
+
drift: (project, against, signal) => this.get(`/projects/${enc(project)}/schema/drift`, { against }, signal),
|
|
413
|
+
promote: (project, fromProject, opts = {}) => this.mutate("POST", `/projects/${enc(project)}/schema/promote`, {
|
|
414
|
+
fromProject,
|
|
415
|
+
allowDestructive: opts.allowDestructive ?? false,
|
|
416
|
+
confirm: true
|
|
383
417
|
})
|
|
384
418
|
};
|
|
385
419
|
// --- Entries & revisions --------------------------------------------------
|
|
@@ -394,6 +428,12 @@ var ManagementClient = class {
|
|
|
394
428
|
delete: (project, entry, changeSetId) => this.mutate("DELETE", `/projects/${enc(project)}/entries/${enc(entry)}`, void 0, { changeSetId }),
|
|
395
429
|
unpublish: (project, entry, changeSetId) => this.mutate("POST", `/projects/${enc(project)}/entries/${enc(entry)}/unpublish`, void 0, { changeSetId }),
|
|
396
430
|
restore: (project, entry) => this.mutate("POST", `/projects/${enc(project)}/entries/${enc(entry)}/restore`),
|
|
431
|
+
duplicate: (project, entry, changeSetId) => this.mutate("POST", `/projects/${enc(project)}/entries/${enc(entry)}/duplicate`, {
|
|
432
|
+
changeSetId
|
|
433
|
+
}),
|
|
434
|
+
bulk: (project, body) => this.mutate("POST", `/projects/${enc(project)}/entries/bulk`, body),
|
|
435
|
+
import: (project, body) => this.mutate("POST", `/projects/${enc(project)}/entries/import`, body),
|
|
436
|
+
references: (project, entry, signal) => this.get(`/projects/${enc(project)}/entries/${enc(entry)}/references`, void 0, signal),
|
|
397
437
|
revisions: (project, entry, signal) => this.get(`/projects/${enc(project)}/entries/${enc(entry)}/revisions`, void 0, signal),
|
|
398
438
|
revision: (project, entry, revision, signal) => this.get(`/projects/${enc(project)}/entries/${enc(entry)}/revisions/${enc(revision)}`, void 0, signal),
|
|
399
439
|
restoreRevision: (project, entry, revision) => this.mutate(
|
|
@@ -409,7 +449,48 @@ var ManagementClient = class {
|
|
|
409
449
|
update: (project, changeSet, body) => this.mutate("PATCH", `/projects/${enc(project)}/change-sets/${enc(changeSet)}`, body),
|
|
410
450
|
validate: (project, changeSet) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/validate`),
|
|
411
451
|
publish: (project, changeSet) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/publish`, { confirm: true }),
|
|
412
|
-
close: (project, changeSet) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/close`)
|
|
452
|
+
close: (project, changeSet) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/close`),
|
|
453
|
+
// Scheduled publishing
|
|
454
|
+
schedule: (project, changeSet, publishAt) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/schedule`, {
|
|
455
|
+
publishAt
|
|
456
|
+
}),
|
|
457
|
+
cancelSchedule: (project, changeSet) => this.mutate("DELETE", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/schedule`),
|
|
458
|
+
// Reviews & approvals
|
|
459
|
+
reviews: (project, changeSet, signal) => this.get(
|
|
460
|
+
`/projects/${enc(project)}/change-sets/${enc(changeSet)}/reviews`,
|
|
461
|
+
void 0,
|
|
462
|
+
signal
|
|
463
|
+
),
|
|
464
|
+
requestReview: (project, changeSet, body) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/reviews`, body),
|
|
465
|
+
removeReviewer: (project, changeSet, reviewer) => this.mutate(
|
|
466
|
+
"DELETE",
|
|
467
|
+
`/projects/${enc(project)}/change-sets/${enc(changeSet)}/reviews/${enc(reviewer)}`
|
|
468
|
+
),
|
|
469
|
+
approve: (project, changeSet, body = {}) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/approve`, body),
|
|
470
|
+
requestChanges: (project, changeSet, body = {}) => this.mutate(
|
|
471
|
+
"POST",
|
|
472
|
+
`/projects/${enc(project)}/change-sets/${enc(changeSet)}/request-changes`,
|
|
473
|
+
body
|
|
474
|
+
),
|
|
475
|
+
// Comments
|
|
476
|
+
comments: (project, changeSet, signal) => this.get(
|
|
477
|
+
`/projects/${enc(project)}/change-sets/${enc(changeSet)}/comments`,
|
|
478
|
+
void 0,
|
|
479
|
+
signal
|
|
480
|
+
),
|
|
481
|
+
comment: (project, changeSet, body) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/comments`, body),
|
|
482
|
+
resolveComment: (project, changeSet, comment, resolved = true) => this.mutate(
|
|
483
|
+
"PATCH",
|
|
484
|
+
`/projects/${enc(project)}/change-sets/${enc(changeSet)}/comments/${enc(comment)}`,
|
|
485
|
+
{ resolved }
|
|
486
|
+
),
|
|
487
|
+
// Automated checks
|
|
488
|
+
checks: (project, changeSet, signal) => this.get(
|
|
489
|
+
`/projects/${enc(project)}/change-sets/${enc(changeSet)}/checks`,
|
|
490
|
+
void 0,
|
|
491
|
+
signal
|
|
492
|
+
),
|
|
493
|
+
runChecks: (project, changeSet) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/checks/run`)
|
|
413
494
|
};
|
|
414
495
|
// --- Previews -------------------------------------------------------------
|
|
415
496
|
previews = {
|
|
@@ -427,6 +508,7 @@ var ManagementClient = class {
|
|
|
427
508
|
get: (project, asset, signal) => this.get(`/projects/${enc(project)}/assets/${enc(asset)}`, void 0, signal),
|
|
428
509
|
usage: (project, asset, signal) => this.get(`/projects/${enc(project)}/assets/${enc(asset)}`, { usage: "true" }, signal),
|
|
429
510
|
update: (project, asset, body) => this.mutate("PATCH", `/projects/${enc(project)}/assets/${enc(asset)}`, body),
|
|
511
|
+
replace: (project, asset, body) => this.mutate("POST", `/projects/${enc(project)}/assets/${enc(asset)}/replace`, body),
|
|
430
512
|
delete: (project, asset) => this.mutate("DELETE", `/projects/${enc(project)}/assets/${enc(asset)}`),
|
|
431
513
|
/** Full presigned upload flow: create → PUT bytes → complete. */
|
|
432
514
|
upload: (project, input, meta = {}) => this.uploadAsset(project, input, meta)
|
|
@@ -1407,6 +1489,35 @@ function registerSchema(program) {
|
|
|
1407
1489
|
emit(result, () => renderDiff(result));
|
|
1408
1490
|
})
|
|
1409
1491
|
);
|
|
1492
|
+
schema.command("drift").description("Show schema drift between this project and another (e.g. staging)").requiredOption("--against <project>", "project id or slug to compare with").action(
|
|
1493
|
+
handle(async (ctx, _args, opts) => {
|
|
1494
|
+
const project = ctx.requireProject();
|
|
1495
|
+
const result = await ctx.management().schema.drift(project, opts.against);
|
|
1496
|
+
emit(result, () => {
|
|
1497
|
+
if (result.inSync) diag("Schemas are in sync.");
|
|
1498
|
+
else {
|
|
1499
|
+
diag(`${result.ops.length} op(s) of drift (${result.classification}):`);
|
|
1500
|
+
for (const op of result.ops) {
|
|
1501
|
+
process.stderr.write(` ${op.kind} ${op.collection}${op.field ? `.${op.field}` : ""} \u2014 ${op.detail}
|
|
1502
|
+
`);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
});
|
|
1506
|
+
if (!result.inSync) process.exitCode = 1;
|
|
1507
|
+
})
|
|
1508
|
+
);
|
|
1509
|
+
schema.command("promote").description("Promote another project's deployed schemas into this project").requiredOption("--from <project>", "source project id or slug").option("--allow-destructive", "permit destructive schema changes", false).action(
|
|
1510
|
+
handle(async (ctx, _args, opts) => {
|
|
1511
|
+
const project = ctx.requireProject();
|
|
1512
|
+
const result = await ctx.management().schema.promote(project, opts.from, {
|
|
1513
|
+
allowDestructive: Boolean(opts.allowDestructive)
|
|
1514
|
+
});
|
|
1515
|
+
emit(result, () => {
|
|
1516
|
+
renderDiff(result.diff);
|
|
1517
|
+
diag(result.applied ? `Promoted ${result.versions.length} version(s).` : "Nothing to promote.");
|
|
1518
|
+
});
|
|
1519
|
+
})
|
|
1520
|
+
);
|
|
1410
1521
|
schema.command("push").description("Apply local schemas, creating immutable versions").option("--schema-dir <dir>", "schema directory").option("--allow-destructive", "permit destructive schema changes", false).option("--summary <text>", "change summary").action(
|
|
1411
1522
|
handle(async (ctx, _args, opts) => {
|
|
1412
1523
|
const project = ctx.requireProject();
|
|
@@ -1480,6 +1591,7 @@ function renderDiff(diff) {
|
|
|
1480
1591
|
}
|
|
1481
1592
|
|
|
1482
1593
|
// src/commands/entries.ts
|
|
1594
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
1483
1595
|
function registerEntries(program) {
|
|
1484
1596
|
const entries = program.command("entries").description("Create, read, update, and manage entries");
|
|
1485
1597
|
entries.command("list").description("List entries in a collection").argument("<collection>", "collection key").option("--status <status>", "filter by status").option("--limit <n>", "page size", "25").option("--cursor <cursor>", "pagination cursor").action(
|
|
@@ -1578,6 +1690,81 @@ function registerEntries(program) {
|
|
|
1578
1690
|
);
|
|
1579
1691
|
})
|
|
1580
1692
|
);
|
|
1693
|
+
entries.command("duplicate").description("Duplicate an entry into a new draft").argument("<ref>", "collection/slug or entry id").option("--change-set <id>", "stage on an explicit change set").action(
|
|
1694
|
+
handle(async (ctx, args, opts) => {
|
|
1695
|
+
const project = ctx.requireProject();
|
|
1696
|
+
const id = await resolveEntryId(ctx.management(), project, args[0]);
|
|
1697
|
+
const entry = await ctx.management().entries.duplicate(project, id, opts.changeSet);
|
|
1698
|
+
emit(entry, () => diag(`Duplicated into ${entry.id} (${entry.slug ?? "no slug"}).`));
|
|
1699
|
+
})
|
|
1700
|
+
);
|
|
1701
|
+
entries.command("bulk").description("Stage a delete or unpublish for many entries at once").requiredOption("--action <action>", "delete or unpublish").requiredOption("--ids <ids>", "comma-separated entry ids").option("--change-set <id>", "stage on an explicit change set").action(
|
|
1702
|
+
handle(async (ctx, _args, opts) => {
|
|
1703
|
+
const action = opts.action;
|
|
1704
|
+
if (action !== "delete" && action !== "unpublish") {
|
|
1705
|
+
throw new UsageError("--action must be delete or unpublish.");
|
|
1706
|
+
}
|
|
1707
|
+
const project = ctx.requireProject();
|
|
1708
|
+
const result = await ctx.management().entries.bulk(project, {
|
|
1709
|
+
action,
|
|
1710
|
+
entryIds: opts.ids.split(",").map((s) => s.trim()).filter(Boolean),
|
|
1711
|
+
changeSetId: opts.changeSet
|
|
1712
|
+
});
|
|
1713
|
+
emit(
|
|
1714
|
+
result,
|
|
1715
|
+
() => table(result.results, [
|
|
1716
|
+
{ header: "ENTRY", value: (r) => r.entryId },
|
|
1717
|
+
{ header: "OK", value: (r) => r.ok ? "yes" : "no" },
|
|
1718
|
+
{ header: "CHANGE SET", value: (r) => r.changeSetId ?? "\u2014" },
|
|
1719
|
+
{ header: "ERROR", value: (r) => r.error ?? "" }
|
|
1720
|
+
])
|
|
1721
|
+
);
|
|
1722
|
+
if (result.results.some((r) => !r.ok)) process.exitCode = 1;
|
|
1723
|
+
})
|
|
1724
|
+
);
|
|
1725
|
+
entries.command("import").description("Import entries from a JSON file, with dry-run validation").argument("<file>", 'JSON file: { "collection": "posts", "entries": [{ "slug"?, "data" }] }').option("--dry-run", "validate without creating anything", false).action(
|
|
1726
|
+
handle(async (ctx, args, opts) => {
|
|
1727
|
+
const project = ctx.requireProject();
|
|
1728
|
+
const raw = JSON.parse(await readFile2(args[0], "utf8"));
|
|
1729
|
+
const result = await ctx.management().entries.import(project, {
|
|
1730
|
+
collection: raw.collection,
|
|
1731
|
+
entries: raw.entries,
|
|
1732
|
+
dryRun: Boolean(opts.dryRun)
|
|
1733
|
+
});
|
|
1734
|
+
emit(result, () => {
|
|
1735
|
+
diag(
|
|
1736
|
+
result.dryRun ? `Dry run: ${result.valid ? "all rows valid" : "validation failed"}.` : `Imported ${result.results.filter((r) => r.ok).length}/${result.results.length} into change set ${result.changeSetId}.`
|
|
1737
|
+
);
|
|
1738
|
+
for (const row of result.results.filter((r) => !r.ok)) {
|
|
1739
|
+
for (const e of row.errors ?? []) {
|
|
1740
|
+
process.stderr.write(` row ${row.index}${row.slug ? ` (${row.slug})` : ""}: ${e.path} ${e.message}
|
|
1741
|
+
`);
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
});
|
|
1745
|
+
if (!result.valid) process.exitCode = 1;
|
|
1746
|
+
})
|
|
1747
|
+
);
|
|
1748
|
+
entries.command("references").description("Show which entries reference this one and what it references").argument("<ref>", "collection/slug or entry id").action(
|
|
1749
|
+
handle(async (ctx, args) => {
|
|
1750
|
+
const project = ctx.requireProject();
|
|
1751
|
+
const id = await resolveEntryId(ctx.management(), project, args[0]);
|
|
1752
|
+
const refs = await ctx.management().entries.references(project, id);
|
|
1753
|
+
emit(refs, () => {
|
|
1754
|
+
diag(`Referenced by ${refs.referencedBy.length} entr(y/ies):`);
|
|
1755
|
+
table(refs.referencedBy, [
|
|
1756
|
+
{ header: "ENTRY", value: (r) => `${r.collectionKey}/${r.slug ?? r.id}` },
|
|
1757
|
+
{ header: "IN DRAFT", value: (r) => r.inDraft ? "yes" : "no" },
|
|
1758
|
+
{ header: "IN PUBLISHED", value: (r) => r.inPublished ? "yes" : "no" }
|
|
1759
|
+
]);
|
|
1760
|
+
diag(`References ${refs.references.length} entr(y/ies):`);
|
|
1761
|
+
table(refs.references, [
|
|
1762
|
+
{ header: "ENTRY", value: (r) => r.collectionKey ? `${r.collectionKey}/${r.slug ?? r.id}` : r.id },
|
|
1763
|
+
{ header: "STATE", value: (r) => !r.exists ? "missing" : r.published ? "published" : "unpublished" }
|
|
1764
|
+
]);
|
|
1765
|
+
});
|
|
1766
|
+
})
|
|
1767
|
+
);
|
|
1581
1768
|
entries.command("restore").description("Restore a historical revision into a new draft").argument("<ref>", "collection/slug or entry id").requiredOption("--revision <id>", "revision id to restore").action(
|
|
1582
1769
|
handle(async (ctx, args, opts) => {
|
|
1583
1770
|
const project = ctx.requireProject();
|
|
@@ -1682,6 +1869,107 @@ function registerChanges(program) {
|
|
|
1682
1869
|
emit(cs, () => diag(`Closed ${cs.id}.`));
|
|
1683
1870
|
})
|
|
1684
1871
|
);
|
|
1872
|
+
changes.command("schedule").description("Schedule, reschedule, or cancel a change set's publish").argument("<id>", "change set id").option("--at <datetime>", "ISO 8601 datetime to publish at").option("--cancel", "cancel the scheduled publish", false).action(
|
|
1873
|
+
handle(async (ctx, args, opts) => {
|
|
1874
|
+
const project = ctx.requireProject();
|
|
1875
|
+
if (Boolean(opts.at) === Boolean(opts.cancel)) {
|
|
1876
|
+
throw new UsageError("Provide exactly one of --at <datetime> or --cancel.");
|
|
1877
|
+
}
|
|
1878
|
+
const client = ctx.management();
|
|
1879
|
+
const cs = opts.cancel ? await client.changeSets.cancelSchedule(project, args[0]) : await client.changeSets.schedule(project, args[0], new Date(opts.at).toISOString());
|
|
1880
|
+
emit(
|
|
1881
|
+
cs,
|
|
1882
|
+
() => diag(cs.scheduledAt ? `Scheduled to publish at ${cs.scheduledAt}.` : "Schedule cancelled.")
|
|
1883
|
+
);
|
|
1884
|
+
})
|
|
1885
|
+
);
|
|
1886
|
+
changes.command("reviews").description("List reviews and approvals for a change set").argument("<id>", "change set id").action(
|
|
1887
|
+
handle(async (ctx, args) => {
|
|
1888
|
+
const project = ctx.requireProject();
|
|
1889
|
+
const reviews = await ctx.management().changeSets.reviews(project, args[0]);
|
|
1890
|
+
emit(
|
|
1891
|
+
reviews,
|
|
1892
|
+
() => table(reviews, [
|
|
1893
|
+
{ header: "REVIEWER", value: (r) => `${r.reviewerType}:${r.reviewerId}` },
|
|
1894
|
+
{ header: "STATUS", value: (r) => r.status },
|
|
1895
|
+
{ header: "STALE", value: (r) => r.stale ? "yes" : "no" },
|
|
1896
|
+
{ header: "DECIDED", value: (r) => r.decidedAt ?? "\u2014" }
|
|
1897
|
+
])
|
|
1898
|
+
);
|
|
1899
|
+
})
|
|
1900
|
+
);
|
|
1901
|
+
changes.command("request-review").description("Assign a reviewer to a change set").argument("<id>", "change set id").requiredOption("--reviewer <id>", "reviewer id (user, api key, or agent id)").option("--reviewer-type <type>", "reviewer actor type", "user").action(
|
|
1902
|
+
handle(async (ctx, args, opts) => {
|
|
1903
|
+
const project = ctx.requireProject();
|
|
1904
|
+
const reviews = await ctx.management().changeSets.requestReview(project, args[0], {
|
|
1905
|
+
reviewerType: opts.reviewerType,
|
|
1906
|
+
reviewerId: opts.reviewer
|
|
1907
|
+
});
|
|
1908
|
+
emit(reviews, () => diag(`Requested review from ${opts.reviewer}.`));
|
|
1909
|
+
})
|
|
1910
|
+
);
|
|
1911
|
+
changes.command("approve").description("Approve a change set").argument("<id>", "change set id").option("--comment <text>", "optional review comment").action(
|
|
1912
|
+
handle(async (ctx, args, opts) => {
|
|
1913
|
+
const project = ctx.requireProject();
|
|
1914
|
+
const reviews = await ctx.management().changeSets.approve(project, args[0], {
|
|
1915
|
+
comment: opts.comment
|
|
1916
|
+
});
|
|
1917
|
+
emit(reviews, () => diag("Approved."));
|
|
1918
|
+
})
|
|
1919
|
+
);
|
|
1920
|
+
changes.command("request-changes").description("Request changes on a change set").argument("<id>", "change set id").option("--comment <text>", "optional review comment").action(
|
|
1921
|
+
handle(async (ctx, args, opts) => {
|
|
1922
|
+
const project = ctx.requireProject();
|
|
1923
|
+
const reviews = await ctx.management().changeSets.requestChanges(project, args[0], {
|
|
1924
|
+
comment: opts.comment
|
|
1925
|
+
});
|
|
1926
|
+
emit(reviews, () => diag("Requested changes."));
|
|
1927
|
+
})
|
|
1928
|
+
);
|
|
1929
|
+
changes.command("comments").description("List comments on a change set").argument("<id>", "change set id").action(
|
|
1930
|
+
handle(async (ctx, args) => {
|
|
1931
|
+
const project = ctx.requireProject();
|
|
1932
|
+
const comments = await ctx.management().changeSets.comments(project, args[0]);
|
|
1933
|
+
emit(
|
|
1934
|
+
comments,
|
|
1935
|
+
() => table(comments, [
|
|
1936
|
+
{ header: "AUTHOR", value: (c) => `${c.authorType}:${c.authorId ?? "\u2014"}` },
|
|
1937
|
+
{ header: "ANCHOR", value: (c) => c.resourceId ? `${c.resourceId}${c.fieldPath ? `#${c.fieldPath}` : ""}` : "\u2014" },
|
|
1938
|
+
{ header: "RESOLVED", value: (c) => c.resolvedAt ? "yes" : "no" },
|
|
1939
|
+
{ header: "BODY", value: (c) => c.body.length > 60 ? `${c.body.slice(0, 57)}...` : c.body }
|
|
1940
|
+
])
|
|
1941
|
+
);
|
|
1942
|
+
})
|
|
1943
|
+
);
|
|
1944
|
+
changes.command("comment").description("Comment on a change set (optionally anchored to a field)").argument("<id>", "change set id").argument("<body>", "comment body").option("--entry <id>", "anchor to an entry in the change set").option("--field <path>", "anchor to a field path, e.g. fields.title").action(
|
|
1945
|
+
handle(async (ctx, args, opts) => {
|
|
1946
|
+
const project = ctx.requireProject();
|
|
1947
|
+
const comment = await ctx.management().changeSets.comment(project, args[0], {
|
|
1948
|
+
body: args[1],
|
|
1949
|
+
resourceType: opts.entry ? "entry" : void 0,
|
|
1950
|
+
resourceId: opts.entry,
|
|
1951
|
+
fieldPath: opts.field
|
|
1952
|
+
});
|
|
1953
|
+
emit(comment, () => diag(`Commented ${comment.id}.`));
|
|
1954
|
+
})
|
|
1955
|
+
);
|
|
1956
|
+
changes.command("checks").description("Show the latest check run for a change set").argument("<id>", "change set id").option("--run", "run checks before showing results", false).action(
|
|
1957
|
+
handle(async (ctx, args, opts) => {
|
|
1958
|
+
const project = ctx.requireProject();
|
|
1959
|
+
const client = ctx.management();
|
|
1960
|
+
const checks = opts.run ? (await client.changeSets.runChecks(project, args[0])).checks : await client.changeSets.checks(project, args[0]);
|
|
1961
|
+
emit(
|
|
1962
|
+
checks,
|
|
1963
|
+
() => table(checks, [
|
|
1964
|
+
{ header: "CHECK", value: (c) => c.name },
|
|
1965
|
+
{ header: "STATUS", value: (c) => c.status },
|
|
1966
|
+
{ header: "STALE", value: (c) => c.stale ? "yes" : "no" },
|
|
1967
|
+
{ header: "ISSUES", value: (c) => String(c.details?.length ?? 0) }
|
|
1968
|
+
])
|
|
1969
|
+
);
|
|
1970
|
+
if (checks.some((c) => c.status === "failed")) process.exitCode = 1;
|
|
1971
|
+
})
|
|
1972
|
+
);
|
|
1685
1973
|
}
|
|
1686
1974
|
|
|
1687
1975
|
// src/commands/previews.ts
|
|
@@ -1893,6 +2181,46 @@ function registerProjects(program) {
|
|
|
1893
2181
|
);
|
|
1894
2182
|
})
|
|
1895
2183
|
);
|
|
2184
|
+
projects.command("export").description("Export the full project (schemas, entries, asset metadata) as JSON").argument("[project]", "project id or slug").action(
|
|
2185
|
+
handle(async (ctx, args) => {
|
|
2186
|
+
const ref = args[0] ?? ctx.requireProject();
|
|
2187
|
+
const data = await ctx.management().projects.export(ref);
|
|
2188
|
+
process.stdout.write(`${JSON.stringify(data, null, 2)}
|
|
2189
|
+
`);
|
|
2190
|
+
})
|
|
2191
|
+
);
|
|
2192
|
+
projects.command("update").description("Update project settings").argument("[project]", "project id or slug").option("--name <name>", "project name").option("--timezone <timezone>", "IANA timezone").option("--public-api <state>", "public API state (on|off)").option("--default-preview-template <template>", "default preview URL template").option("--clear-default-preview-template", "remove the default preview URL template").option("--origins <urls>", "comma-separated allowed origins").option("--clear-origins", "remove all allowed origins").action(
|
|
2193
|
+
handle(async (ctx, args, opts) => {
|
|
2194
|
+
const ref = args[0] ?? ctx.requireProject();
|
|
2195
|
+
const patch = {};
|
|
2196
|
+
if (opts.name !== void 0) patch.name = opts.name;
|
|
2197
|
+
if (opts.timezone !== void 0) patch.timezone = opts.timezone;
|
|
2198
|
+
if (opts.publicApi !== void 0) {
|
|
2199
|
+
const state = String(opts.publicApi).toLowerCase();
|
|
2200
|
+
if (!["on", "off", "true", "false"].includes(state)) {
|
|
2201
|
+
throw new UsageError("--public-api must be on or off.");
|
|
2202
|
+
}
|
|
2203
|
+
patch.publicApiEnabled = state === "on" || state === "true";
|
|
2204
|
+
}
|
|
2205
|
+
if (opts.defaultPreviewTemplate !== void 0 && opts.clearDefaultPreviewTemplate) {
|
|
2206
|
+
throw new UsageError("Use either --default-preview-template or --clear-default-preview-template, not both.");
|
|
2207
|
+
}
|
|
2208
|
+
if (opts.defaultPreviewTemplate !== void 0) {
|
|
2209
|
+
patch.defaultPreviewTemplate = opts.defaultPreviewTemplate;
|
|
2210
|
+
}
|
|
2211
|
+
if (opts.clearDefaultPreviewTemplate) patch.defaultPreviewTemplate = null;
|
|
2212
|
+
if (opts.origins !== void 0 && opts.clearOrigins) {
|
|
2213
|
+
throw new UsageError("Use either --origins or --clear-origins, not both.");
|
|
2214
|
+
}
|
|
2215
|
+
if (opts.origins !== void 0) patch.origins = csv(opts.origins);
|
|
2216
|
+
if (opts.clearOrigins) patch.origins = [];
|
|
2217
|
+
if (Object.keys(patch).length === 0) {
|
|
2218
|
+
throw new UsageError("Specify at least one project setting to update.");
|
|
2219
|
+
}
|
|
2220
|
+
const project = await ctx.management().projects.update(ref, patch);
|
|
2221
|
+
emit(project, () => diag(`Updated ${project.slug}.`));
|
|
2222
|
+
})
|
|
2223
|
+
);
|
|
1896
2224
|
projects.command("archive").description("Archive a project").argument("[project]", "project id or slug").action(
|
|
1897
2225
|
handle(async (ctx, args) => {
|
|
1898
2226
|
const ref = args[0] ?? ctx.requireProject();
|
|
@@ -2135,7 +2463,7 @@ function registerBilling(program) {
|
|
|
2135
2463
|
}
|
|
2136
2464
|
|
|
2137
2465
|
// src/main.ts
|
|
2138
|
-
var VERSION = "0.
|
|
2466
|
+
var VERSION = "0.2.0";
|
|
2139
2467
|
var GLOBAL_VALUE_FLAGS = /* @__PURE__ */ new Set(["--project", "--organization", "--token", "--api-url"]);
|
|
2140
2468
|
var GLOBAL_BOOL_FLAGS = /* @__PURE__ */ new Set(["--json", "--no-interactive", "--interactive"]);
|
|
2141
2469
|
function normalizeGlobals(argv) {
|