@myna-sh/cli 0.4.0 → 0.5.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/README.md +2 -0
- package/dist/dsl.d.ts +16 -0
- package/dist/dsl.js +2 -0
- package/dist/dsl.js.map +1 -1
- package/dist/main.js +269 -16
- package/dist/main.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -58,6 +58,8 @@ myna organizations list
|
|
|
58
58
|
myna projects list --organization my-organization
|
|
59
59
|
myna link --organization my-organization --project my-project
|
|
60
60
|
myna entries list posts
|
|
61
|
+
myna entries search "webhook signatures"
|
|
62
|
+
myna changes diff chs_...
|
|
61
63
|
```
|
|
62
64
|
|
|
63
65
|
Run `myna --help` or `myna <command> --help` for the complete command reference. Global flags include `--json`, `--organization`, `--project`, `--token`, and `--api-url`.
|
package/dist/dsl.d.ts
CHANGED
|
@@ -166,6 +166,17 @@ interface CollectionSchema {
|
|
|
166
166
|
titleField: string | null;
|
|
167
167
|
slug: SlugConfig | null;
|
|
168
168
|
path: string | null;
|
|
169
|
+
/**
|
|
170
|
+
* How this collection is meant to be written: voice, length, conventions,
|
|
171
|
+
* what belongs here and what does not.
|
|
172
|
+
*
|
|
173
|
+
* A field `description` says what a field is for. This says what *good*
|
|
174
|
+
* looks like, which is the thing an agent has no way to infer from types and
|
|
175
|
+
* the thing a new human contributor is told verbally and then forgets. It
|
|
176
|
+
* travels with the schema, so it is versioned, reviewed, and deployed like
|
|
177
|
+
* the rest of it.
|
|
178
|
+
*/
|
|
179
|
+
guidance?: string;
|
|
169
180
|
fields: FieldDef[];
|
|
170
181
|
}
|
|
171
182
|
/** Maximum object/list nesting depth (SPEC 6.2). */
|
|
@@ -308,6 +319,11 @@ interface CollectionInput {
|
|
|
308
319
|
titleField?: string;
|
|
309
320
|
/** Path template such as `/blog/{slug}`. */
|
|
310
321
|
path?: string;
|
|
322
|
+
/**
|
|
323
|
+
* House style for this collection, read by anyone writing into it — the
|
|
324
|
+
* dashboard editor, the MCP schema tool, an agent planning a draft.
|
|
325
|
+
*/
|
|
326
|
+
guidance?: string;
|
|
311
327
|
fields: Record<string, FieldInput>;
|
|
312
328
|
}
|
|
313
329
|
/**
|
package/dist/dsl.js
CHANGED
|
@@ -187,6 +187,7 @@ function collection(input) {
|
|
|
187
187
|
required: slugField.required
|
|
188
188
|
};
|
|
189
189
|
}
|
|
190
|
+
const guidance = input.guidance ? { guidance: input.guidance } : {};
|
|
190
191
|
return {
|
|
191
192
|
name: input.name,
|
|
192
193
|
label: input.label ?? input.name,
|
|
@@ -195,6 +196,7 @@ function collection(input) {
|
|
|
195
196
|
titleField: input.titleField ?? null,
|
|
196
197
|
slug,
|
|
197
198
|
path: input.path ?? null,
|
|
199
|
+
...guidance,
|
|
198
200
|
fields
|
|
199
201
|
};
|
|
200
202
|
}
|
package/dist/dsl.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../schema/src/types.ts","../../schema/src/fields.ts","../../schema/src/collection.ts"],"sourcesContent":["/**\n * Canonical schema representation (SPEC 6.2 / 7).\n *\n * These are the normalized, serializable shapes stored as `schema_json`. Field\n * order is significant and preserved as an array; object *keys* are sorted only\n * during canonical stringification for a stable hash.\n */\n\nexport type FieldType =\n | \"text\"\n | \"number\"\n | \"boolean\"\n | \"date\"\n | \"datetime\"\n | \"markdown\"\n | \"richText\"\n | \"json\"\n | \"asset\"\n | \"reference\"\n | \"list\"\n | \"object\"\n | \"blocks\"\n | \"slug\";\n\nexport type JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue };\n\n/** UI metadata carried on every field (non-semantic, does not affect validation). */\nexport interface FieldUi {\n widget?: string;\n placeholder?: string;\n helpText?: string;\n group?: string;\n hidden?: boolean;\n}\n\ninterface FieldBase {\n key: string;\n type: FieldType;\n label: string;\n description?: string;\n required: boolean;\n /** Localized fields store `{ [locale]: value }` keyed by project locale.\n * Only top-level, non-slug fields may be localized. */\n localized?: boolean;\n ui?: FieldUi;\n}\n\n/** Locale context for entry validation and locale resolution. */\nexport interface EntryLocaleOptions {\n locales: string[];\n defaultLocale: string;\n}\n\nexport interface TextField extends FieldBase {\n type: \"text\";\n multiline: boolean;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n enum?: string[];\n default?: string;\n}\n\nexport interface NumberField extends FieldBase {\n type: \"number\";\n integer: boolean;\n min?: number;\n max?: number;\n default?: number;\n}\n\nexport interface BooleanField extends FieldBase {\n type: \"boolean\";\n default?: boolean;\n}\n\nexport interface DateField extends FieldBase {\n type: \"date\" | \"datetime\";\n min?: string;\n max?: string;\n default?: string;\n}\n\nexport interface MarkdownField extends FieldBase {\n type: \"markdown\";\n minLength?: number;\n maxLength?: number;\n default?: string;\n}\n\nexport interface JsonField extends FieldBase {\n type: \"json\";\n default?: JsonValue;\n}\n\nexport interface AssetField extends FieldBase {\n type: \"asset\";\n /** Allowed MIME families, e.g. `image/*`, `application/pdf`. */\n allowed: string[];\n multiple: boolean;\n}\n\nexport interface ReferenceField extends FieldBase {\n type: \"reference\";\n /** Target collection key. */\n target: string;\n multiple: boolean;\n}\n\nexport interface SlugField extends FieldBase {\n type: \"slug\";\n /** Source field key to derive the slug from. */\n from?: string;\n}\n\nexport type PrimitiveItem =\n | { kind: \"text\"; minLength?: number; maxLength?: number; pattern?: string; enum?: string[] }\n | { kind: \"number\"; integer?: boolean; min?: number; max?: number }\n | { kind: \"boolean\" }\n | { kind: \"date\" | \"datetime\"; min?: string; max?: string }\n | { kind: \"markdown\"; minLength?: number; maxLength?: number }\n | { kind: \"json\" };\n\nexport type ListItem =\n | PrimitiveItem\n | { kind: \"reference\"; target: string }\n | { kind: \"asset\"; allowed: string[] }\n | { kind: \"object\"; fields: FieldDef[] };\n\nexport interface ListField extends FieldBase {\n type: \"list\";\n item: ListItem;\n minItems?: number;\n maxItems?: number;\n}\n\nexport interface ObjectField extends FieldBase {\n type: \"object\";\n fields: FieldDef[];\n}\n\n/** Semantic rich text stored as a portable JSON document tree — never HTML.\n * The root is `{ \"type\": \"doc\", \"content\": [...] }`; nodes may embed entry\n * references (`ent_` ids) and assets (`ast_` ids). */\nexport interface RichTextField extends FieldBase {\n type: \"richText\";\n}\n\n/** One schema-defined component usable inside a `blocks` field. */\nexport interface BlockDef {\n key: string;\n label: string;\n fields: FieldDef[];\n}\n\n/** Heterogeneous, schema-defined component list. Stored as portable JSON:\n * `[{ \"type\": \"<blockKey>\", \"fields\": { ... } }, ...]`. */\nexport interface BlocksField extends FieldBase {\n type: \"blocks\";\n blocks: BlockDef[];\n minItems?: number;\n maxItems?: number;\n}\n\nexport type FieldDef =\n | TextField\n | NumberField\n | BooleanField\n | DateField\n | MarkdownField\n | RichTextField\n | JsonField\n | AssetField\n | ReferenceField\n | SlugField\n | ListField\n | ObjectField\n | BlocksField;\n\nexport type CollectionKind = \"collection\" | \"singleton\";\nexport type Visibility = \"public\" | \"private\";\n\nexport interface SlugConfig {\n from: string;\n required: boolean;\n}\n\n/** Canonical collection schema (one `collection_versions.schema_json`). */\nexport interface CollectionSchema {\n name: string;\n label: string;\n kind: CollectionKind;\n visibility: Visibility;\n titleField: string | null;\n slug: SlugConfig | null;\n path: string | null;\n fields: FieldDef[];\n}\n\n/** Maximum object/list nesting depth (SPEC 6.2). */\nexport const MAX_NESTING_DEPTH = 4;\n","import type {\n AssetField,\n BlockDef,\n BlocksField,\n BooleanField,\n DateField,\n FieldDef,\n FieldUi,\n JsonField,\n JsonValue,\n ListField,\n ListItem,\n MarkdownField,\n NumberField,\n ObjectField,\n ReferenceField,\n RichTextField,\n SlugField,\n TextField,\n} from \"./types.js\";\n\n/** A field definition before its `key` is assigned by `collection()`. */\nexport type FieldInput = DistributiveOmit<FieldDef, \"key\">;\n\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;\n\ninterface CommonOptions {\n label?: string;\n description?: string;\n required?: boolean;\n localized?: boolean;\n ui?: FieldUi;\n}\n\nfunction common(o: CommonOptions): {\n label?: string;\n description?: string;\n required: boolean;\n localized?: boolean;\n ui?: FieldUi;\n} {\n const base: { label?: string; description?: string; required: boolean; localized?: boolean; ui?: FieldUi } = {\n required: o.required ?? false,\n };\n if (o.label !== undefined) base.label = o.label;\n if (o.description !== undefined) base.description = o.description;\n if (o.localized !== undefined) base.localized = o.localized;\n if (o.ui !== undefined) base.ui = o.ui;\n return base;\n}\n\nexport interface TextOptions extends CommonOptions {\n multiline?: boolean;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n enum?: string[];\n default?: string;\n}\nexport interface NumberOptions extends CommonOptions {\n integer?: boolean;\n min?: number;\n max?: number;\n default?: number;\n}\nexport interface BooleanOptions extends CommonOptions {\n default?: boolean;\n}\nexport interface DateOptions extends CommonOptions {\n min?: string;\n max?: string;\n default?: string;\n}\nexport interface MarkdownOptions extends CommonOptions {\n minLength?: number;\n maxLength?: number;\n default?: string;\n}\nexport interface JsonOptions extends CommonOptions {\n default?: JsonValue;\n}\nexport interface AssetOptions extends CommonOptions {\n allowed?: string[];\n multiple?: boolean;\n}\nexport interface ReferenceOptions extends CommonOptions {\n to: string;\n multiple?: boolean;\n}\nexport interface SlugOptions extends CommonOptions {\n from?: string;\n}\nexport interface ListOptions extends CommonOptions {\n of: ListItem;\n minItems?: number;\n maxItems?: number;\n}\nexport interface ObjectOptions extends CommonOptions {\n fields: Record<string, FieldInput>;\n}\nexport type RichTextOptions = CommonOptions;\nexport interface BlocksOptions extends CommonOptions {\n /** The block components entries may compose, built with `block()`. */\n allowed: BlockDef[];\n minItems?: number;\n maxItems?: number;\n}\n\n/** Define a reusable block component for `field.blocks({ allowed: [...] })`. */\nexport function block(key: string, o: { label?: string; fields: Record<string, FieldInput> }): BlockDef {\n return { key, label: o.label ?? key, fields: assignKeys(o.fields) };\n}\n\nfunction defined<T extends Record<string, unknown>>(obj: T): T {\n for (const key of Object.keys(obj)) {\n if (obj[key] === undefined) delete obj[key];\n }\n return obj;\n}\n\nexport const field = {\n text(o: TextOptions = {}): Omit<TextField, \"key\"> {\n return defined({\n type: \"text\",\n ...common(o),\n multiline: o.multiline ?? false,\n minLength: o.minLength,\n maxLength: o.maxLength,\n pattern: o.pattern,\n enum: o.enum,\n default: o.default,\n }) as Omit<TextField, \"key\">;\n },\n\n number(o: NumberOptions = {}): Omit<NumberField, \"key\"> {\n return defined({\n type: \"number\",\n ...common(o),\n integer: o.integer ?? false,\n min: o.min,\n max: o.max,\n default: o.default,\n }) as Omit<NumberField, \"key\">;\n },\n\n boolean(o: BooleanOptions = {}): Omit<BooleanField, \"key\"> {\n return defined({\n type: \"boolean\",\n ...common(o),\n default: o.default,\n }) as Omit<BooleanField, \"key\">;\n },\n\n date(o: DateOptions = {}): Omit<DateField, \"key\"> {\n return defined({\n type: \"date\",\n ...common(o),\n min: o.min,\n max: o.max,\n default: o.default,\n }) as Omit<DateField, \"key\">;\n },\n\n datetime(o: DateOptions = {}): Omit<DateField, \"key\"> {\n return defined({\n type: \"datetime\",\n ...common(o),\n min: o.min,\n max: o.max,\n default: o.default,\n }) as Omit<DateField, \"key\">;\n },\n\n markdown(o: MarkdownOptions = {}): Omit<MarkdownField, \"key\"> {\n return defined({\n type: \"markdown\",\n ...common(o),\n minLength: o.minLength,\n maxLength: o.maxLength,\n default: o.default,\n }) as Omit<MarkdownField, \"key\">;\n },\n\n json(o: JsonOptions = {}): Omit<JsonField, \"key\"> {\n return defined({\n type: \"json\",\n ...common(o),\n default: o.default,\n }) as Omit<JsonField, \"key\">;\n },\n\n asset(o: AssetOptions = {}): Omit<AssetField, \"key\"> {\n return defined({\n type: \"asset\",\n ...common(o),\n allowed: o.allowed ?? [\"*/*\"],\n multiple: o.multiple ?? false,\n }) as Omit<AssetField, \"key\">;\n },\n\n reference(o: ReferenceOptions): Omit<ReferenceField, \"key\"> {\n return defined({\n type: \"reference\",\n ...common(o),\n target: o.to,\n multiple: o.multiple ?? false,\n }) as Omit<ReferenceField, \"key\">;\n },\n\n slug(o: SlugOptions = {}): Omit<SlugField, \"key\"> {\n return defined({\n type: \"slug\",\n ...common(o),\n from: o.from,\n }) as Omit<SlugField, \"key\">;\n },\n\n list(o: ListOptions): Omit<ListField, \"key\"> {\n return defined({\n type: \"list\",\n ...common(o),\n item: o.of,\n minItems: o.minItems,\n maxItems: o.maxItems,\n }) as Omit<ListField, \"key\">;\n },\n\n object(o: ObjectOptions): Omit<ObjectField, \"key\"> {\n return defined({\n type: \"object\",\n ...common(o),\n fields: assignKeys(o.fields),\n }) as Omit<ObjectField, \"key\">;\n },\n\n richText(o: RichTextOptions = {}): Omit<RichTextField, \"key\"> {\n return defined({\n type: \"richText\",\n ...common(o),\n }) as Omit<RichTextField, \"key\">;\n },\n\n blocks(o: BlocksOptions): Omit<BlocksField, \"key\"> {\n return defined({\n type: \"blocks\",\n ...common(o),\n blocks: o.allowed,\n minItems: o.minItems,\n maxItems: o.maxItems,\n }) as Omit<BlocksField, \"key\">;\n },\n} as const;\n\n/** Item builders for `field.list({ of: item.<type>() })`. */\nexport const item = {\n text(o: { minLength?: number; maxLength?: number; pattern?: string; enum?: string[] } = {}): ListItem {\n return defined({ kind: \"text\", ...o }) as ListItem;\n },\n number(o: { integer?: boolean; min?: number; max?: number } = {}): ListItem {\n return defined({ kind: \"number\", ...o }) as ListItem;\n },\n boolean(): ListItem {\n return { kind: \"boolean\" };\n },\n date(o: { min?: string; max?: string } = {}): ListItem {\n return defined({ kind: \"date\", ...o }) as ListItem;\n },\n datetime(o: { min?: string; max?: string } = {}): ListItem {\n return defined({ kind: \"datetime\", ...o }) as ListItem;\n },\n markdown(o: { minLength?: number; maxLength?: number } = {}): ListItem {\n return defined({ kind: \"markdown\", ...o }) as ListItem;\n },\n json(): ListItem {\n return { kind: \"json\" };\n },\n reference(o: { to: string }): ListItem {\n return { kind: \"reference\", target: o.to };\n },\n asset(o: { allowed?: string[] } = {}): ListItem {\n return { kind: \"asset\", allowed: o.allowed ?? [\"*/*\"] };\n },\n object(o: { fields: Record<string, FieldInput> }): ListItem {\n return { kind: \"object\", fields: assignKeys(o.fields) };\n },\n} as const;\n\n/** Turn a `{ key: FieldInput }` map into an ordered array of `FieldDef`. */\nexport function assignKeys(fields: Record<string, FieldInput>): FieldDef[] {\n return Object.entries(fields).map(([key, def]) => ({ key, ...def }) as FieldDef);\n}\n","import { assignKeys, type FieldInput } from \"./fields.js\";\nimport type { CollectionKind, CollectionSchema, SlugConfig, Visibility } from \"./types.js\";\n\nexport interface CollectionInput {\n /** Immutable collection key, e.g. `posts`. */\n name: string;\n label?: string;\n kind?: CollectionKind;\n /** Visibility defaults to `private` and must be explicitly declared `public`. */\n visibility?: Visibility;\n titleField?: string;\n /** Path template such as `/blog/{slug}`. */\n path?: string;\n fields: Record<string, FieldInput>;\n}\n\n/**\n * Author a collection schema. Returns the canonical `CollectionSchema` with an\n * ordered fields array. Defaults: `kind=collection`, `visibility=private`.\n */\nexport function collection(input: CollectionInput): CollectionSchema {\n const fields = assignKeys(input.fields);\n const slugField = fields.find((f) => f.type === \"slug\");\n\n let slug: SlugConfig | null = null;\n if (slugField && slugField.type === \"slug\") {\n slug = {\n from: slugField.from ?? input.titleField ?? slugField.key,\n required: slugField.required,\n };\n }\n\n return {\n name: input.name,\n label: input.label ?? input.name,\n kind: input.kind ?? \"collection\",\n visibility: input.visibility ?? \"private\",\n titleField: input.titleField ?? null,\n slug,\n path: input.path ?? null,\n fields,\n };\n}\n"],"mappings":";AA8MO,IAAM,oBAAoB;;;AC5KjC,SAAS,OAAO,GAMd;AACA,QAAM,OAAuG;AAAA,IAC3G,UAAU,EAAE,YAAY;AAAA,EAC1B;AACA,MAAI,EAAE,UAAU,OAAW,MAAK,QAAQ,EAAE;AAC1C,MAAI,EAAE,gBAAgB,OAAW,MAAK,cAAc,EAAE;AACtD,MAAI,EAAE,cAAc,OAAW,MAAK,YAAY,EAAE;AAClD,MAAI,EAAE,OAAO,OAAW,MAAK,KAAK,EAAE;AACpC,SAAO;AACT;AA4DO,SAAS,MAAM,KAAa,GAAqE;AACtG,SAAO,EAAE,KAAK,OAAO,EAAE,SAAS,KAAK,QAAQ,WAAW,EAAE,MAAM,EAAE;AACpE;AAEA,SAAS,QAA2C,KAAW;AAC7D,aAAW,OAAO,OAAO,KAAK,GAAG,GAAG;AAClC,QAAI,IAAI,GAAG,MAAM,OAAW,QAAO,IAAI,GAAG;AAAA,EAC5C;AACA,SAAO;AACT;AAEO,IAAM,QAAQ;AAAA,EACnB,KAAK,IAAiB,CAAC,GAA2B;AAChD,WAAO,QAAQ;AAAA,MACb,MAAM;AAAA,MACN,GAAG,OAAO,CAAC;AAAA,MACX,WAAW,EAAE,aAAa;AAAA,MAC1B,WAAW,EAAE;AAAA,MACb,WAAW,EAAE;AAAA,MACb,SAAS,EAAE;AAAA,MACX,MAAM,EAAE;AAAA,MACR,SAAS,EAAE;AAAA,IACb,CAAC;AAAA,EACH;AAAA,EAEA,OAAO,IAAmB,CAAC,GAA6B;AACtD,WAAO,QAAQ;AAAA,MACb,MAAM;AAAA,MACN,GAAG,OAAO,CAAC;AAAA,MACX,SAAS,EAAE,WAAW;AAAA,MACtB,KAAK,EAAE;AAAA,MACP,KAAK,EAAE;AAAA,MACP,SAAS,EAAE;AAAA,IACb,CAAC;AAAA,EACH;AAAA,EAEA,QAAQ,IAAoB,CAAC,GAA8B;AACzD,WAAO,QAAQ;AAAA,MACb,MAAM;AAAA,MACN,GAAG,OAAO,CAAC;AAAA,MACX,SAAS,EAAE;AAAA,IACb,CAAC;AAAA,EACH;AAAA,EAEA,KAAK,IAAiB,CAAC,GAA2B;AAChD,WAAO,QAAQ;AAAA,MACb,MAAM;AAAA,MACN,GAAG,OAAO,CAAC;AAAA,MACX,KAAK,EAAE;AAAA,MACP,KAAK,EAAE;AAAA,MACP,SAAS,EAAE;AAAA,IACb,CAAC;AAAA,EACH;AAAA,EAEA,SAAS,IAAiB,CAAC,GAA2B;AACpD,WAAO,QAAQ;AAAA,MACb,MAAM;AAAA,MACN,GAAG,OAAO,CAAC;AAAA,MACX,KAAK,EAAE;AAAA,MACP,KAAK,EAAE;AAAA,MACP,SAAS,EAAE;AAAA,IACb,CAAC;AAAA,EACH;AAAA,EAEA,SAAS,IAAqB,CAAC,GAA+B;AAC5D,WAAO,QAAQ;AAAA,MACb,MAAM;AAAA,MACN,GAAG,OAAO,CAAC;AAAA,MACX,WAAW,EAAE;AAAA,MACb,WAAW,EAAE;AAAA,MACb,SAAS,EAAE;AAAA,IACb,CAAC;AAAA,EACH;AAAA,EAEA,KAAK,IAAiB,CAAC,GAA2B;AAChD,WAAO,QAAQ;AAAA,MACb,MAAM;AAAA,MACN,GAAG,OAAO,CAAC;AAAA,MACX,SAAS,EAAE;AAAA,IACb,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,IAAkB,CAAC,GAA4B;AACnD,WAAO,QAAQ;AAAA,MACb,MAAM;AAAA,MACN,GAAG,OAAO,CAAC;AAAA,MACX,SAAS,EAAE,WAAW,CAAC,KAAK;AAAA,MAC5B,UAAU,EAAE,YAAY;AAAA,IAC1B,CAAC;AAAA,EACH;AAAA,EAEA,UAAU,GAAkD;AAC1D,WAAO,QAAQ;AAAA,MACb,MAAM;AAAA,MACN,GAAG,OAAO,CAAC;AAAA,MACX,QAAQ,EAAE;AAAA,MACV,UAAU,EAAE,YAAY;AAAA,IAC1B,CAAC;AAAA,EACH;AAAA,EAEA,KAAK,IAAiB,CAAC,GAA2B;AAChD,WAAO,QAAQ;AAAA,MACb,MAAM;AAAA,MACN,GAAG,OAAO,CAAC;AAAA,MACX,MAAM,EAAE;AAAA,IACV,CAAC;AAAA,EACH;AAAA,EAEA,KAAK,GAAwC;AAC3C,WAAO,QAAQ;AAAA,MACb,MAAM;AAAA,MACN,GAAG,OAAO,CAAC;AAAA,MACX,MAAM,EAAE;AAAA,MACR,UAAU,EAAE;AAAA,MACZ,UAAU,EAAE;AAAA,IACd,CAAC;AAAA,EACH;AAAA,EAEA,OAAO,GAA4C;AACjD,WAAO,QAAQ;AAAA,MACb,MAAM;AAAA,MACN,GAAG,OAAO,CAAC;AAAA,MACX,QAAQ,WAAW,EAAE,MAAM;AAAA,IAC7B,CAAC;AAAA,EACH;AAAA,EAEA,SAAS,IAAqB,CAAC,GAA+B;AAC5D,WAAO,QAAQ;AAAA,MACb,MAAM;AAAA,MACN,GAAG,OAAO,CAAC;AAAA,IACb,CAAC;AAAA,EACH;AAAA,EAEA,OAAO,GAA4C;AACjD,WAAO,QAAQ;AAAA,MACb,MAAM;AAAA,MACN,GAAG,OAAO,CAAC;AAAA,MACX,QAAQ,EAAE;AAAA,MACV,UAAU,EAAE;AAAA,MACZ,UAAU,EAAE;AAAA,IACd,CAAC;AAAA,EACH;AACF;AAGO,IAAM,OAAO;AAAA,EAClB,KAAK,IAAmF,CAAC,GAAa;AACpG,WAAO,QAAQ,EAAE,MAAM,QAAQ,GAAG,EAAE,CAAC;AAAA,EACvC;AAAA,EACA,OAAO,IAAuD,CAAC,GAAa;AAC1E,WAAO,QAAQ,EAAE,MAAM,UAAU,GAAG,EAAE,CAAC;AAAA,EACzC;AAAA,EACA,UAAoB;AAClB,WAAO,EAAE,MAAM,UAAU;AAAA,EAC3B;AAAA,EACA,KAAK,IAAoC,CAAC,GAAa;AACrD,WAAO,QAAQ,EAAE,MAAM,QAAQ,GAAG,EAAE,CAAC;AAAA,EACvC;AAAA,EACA,SAAS,IAAoC,CAAC,GAAa;AACzD,WAAO,QAAQ,EAAE,MAAM,YAAY,GAAG,EAAE,CAAC;AAAA,EAC3C;AAAA,EACA,SAAS,IAAgD,CAAC,GAAa;AACrE,WAAO,QAAQ,EAAE,MAAM,YAAY,GAAG,EAAE,CAAC;AAAA,EAC3C;AAAA,EACA,OAAiB;AACf,WAAO,EAAE,MAAM,OAAO;AAAA,EACxB;AAAA,EACA,UAAU,GAA6B;AACrC,WAAO,EAAE,MAAM,aAAa,QAAQ,EAAE,GAAG;AAAA,EAC3C;AAAA,EACA,MAAM,IAA4B,CAAC,GAAa;AAC9C,WAAO,EAAE,MAAM,SAAS,SAAS,EAAE,WAAW,CAAC,KAAK,EAAE;AAAA,EACxD;AAAA,EACA,OAAO,GAAqD;AAC1D,WAAO,EAAE,MAAM,UAAU,QAAQ,WAAW,EAAE,MAAM,EAAE;AAAA,EACxD;AACF;AAGO,SAAS,WAAW,QAAgD;AACzE,SAAO,OAAO,QAAQ,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,GAAG,OAAO,EAAE,KAAK,GAAG,IAAI,EAAc;AACjF;;;AC9QO,SAAS,WAAW,OAA0C;AACnE,QAAM,SAAS,WAAW,MAAM,MAAM;AACtC,QAAM,YAAY,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,MAAM;AAEtD,MAAI,OAA0B;AAC9B,MAAI,aAAa,UAAU,SAAS,QAAQ;AAC1C,WAAO;AAAA,MACL,MAAM,UAAU,QAAQ,MAAM,cAAc,UAAU;AAAA,MACtD,UAAU,UAAU;AAAA,IACtB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,MAAM;AAAA,IACZ,OAAO,MAAM,SAAS,MAAM;AAAA,IAC5B,MAAM,MAAM,QAAQ;AAAA,IACpB,YAAY,MAAM,cAAc;AAAA,IAChC,YAAY,MAAM,cAAc;AAAA,IAChC;AAAA,IACA,MAAM,MAAM,QAAQ;AAAA,IACpB;AAAA,EACF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../schema/src/types.ts","../../schema/src/fields.ts","../../schema/src/collection.ts"],"sourcesContent":["/**\n * Canonical schema representation (SPEC 6.2 / 7).\n *\n * These are the normalized, serializable shapes stored as `schema_json`. Field\n * order is significant and preserved as an array; object *keys* are sorted only\n * during canonical stringification for a stable hash.\n */\n\nexport type FieldType =\n | \"text\"\n | \"number\"\n | \"boolean\"\n | \"date\"\n | \"datetime\"\n | \"markdown\"\n | \"richText\"\n | \"json\"\n | \"asset\"\n | \"reference\"\n | \"list\"\n | \"object\"\n | \"blocks\"\n | \"slug\";\n\nexport type JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue };\n\n/** UI metadata carried on every field (non-semantic, does not affect validation). */\nexport interface FieldUi {\n widget?: string;\n placeholder?: string;\n helpText?: string;\n group?: string;\n hidden?: boolean;\n}\n\ninterface FieldBase {\n key: string;\n type: FieldType;\n label: string;\n description?: string;\n required: boolean;\n /** Localized fields store `{ [locale]: value }` keyed by project locale.\n * Only top-level, non-slug fields may be localized. */\n localized?: boolean;\n ui?: FieldUi;\n}\n\n/** Locale context for entry validation and locale resolution. */\nexport interface EntryLocaleOptions {\n locales: string[];\n defaultLocale: string;\n}\n\nexport interface TextField extends FieldBase {\n type: \"text\";\n multiline: boolean;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n enum?: string[];\n default?: string;\n}\n\nexport interface NumberField extends FieldBase {\n type: \"number\";\n integer: boolean;\n min?: number;\n max?: number;\n default?: number;\n}\n\nexport interface BooleanField extends FieldBase {\n type: \"boolean\";\n default?: boolean;\n}\n\nexport interface DateField extends FieldBase {\n type: \"date\" | \"datetime\";\n min?: string;\n max?: string;\n default?: string;\n}\n\nexport interface MarkdownField extends FieldBase {\n type: \"markdown\";\n minLength?: number;\n maxLength?: number;\n default?: string;\n}\n\nexport interface JsonField extends FieldBase {\n type: \"json\";\n default?: JsonValue;\n}\n\nexport interface AssetField extends FieldBase {\n type: \"asset\";\n /** Allowed MIME families, e.g. `image/*`, `application/pdf`. */\n allowed: string[];\n multiple: boolean;\n}\n\nexport interface ReferenceField extends FieldBase {\n type: \"reference\";\n /** Target collection key. */\n target: string;\n multiple: boolean;\n}\n\nexport interface SlugField extends FieldBase {\n type: \"slug\";\n /** Source field key to derive the slug from. */\n from?: string;\n}\n\nexport type PrimitiveItem =\n | { kind: \"text\"; minLength?: number; maxLength?: number; pattern?: string; enum?: string[] }\n | { kind: \"number\"; integer?: boolean; min?: number; max?: number }\n | { kind: \"boolean\" }\n | { kind: \"date\" | \"datetime\"; min?: string; max?: string }\n | { kind: \"markdown\"; minLength?: number; maxLength?: number }\n | { kind: \"json\" };\n\nexport type ListItem =\n | PrimitiveItem\n | { kind: \"reference\"; target: string }\n | { kind: \"asset\"; allowed: string[] }\n | { kind: \"object\"; fields: FieldDef[] };\n\nexport interface ListField extends FieldBase {\n type: \"list\";\n item: ListItem;\n minItems?: number;\n maxItems?: number;\n}\n\nexport interface ObjectField extends FieldBase {\n type: \"object\";\n fields: FieldDef[];\n}\n\n/** Semantic rich text stored as a portable JSON document tree — never HTML.\n * The root is `{ \"type\": \"doc\", \"content\": [...] }`; nodes may embed entry\n * references (`ent_` ids) and assets (`ast_` ids). */\nexport interface RichTextField extends FieldBase {\n type: \"richText\";\n}\n\n/** One schema-defined component usable inside a `blocks` field. */\nexport interface BlockDef {\n key: string;\n label: string;\n fields: FieldDef[];\n}\n\n/** Heterogeneous, schema-defined component list. Stored as portable JSON:\n * `[{ \"type\": \"<blockKey>\", \"fields\": { ... } }, ...]`. */\nexport interface BlocksField extends FieldBase {\n type: \"blocks\";\n blocks: BlockDef[];\n minItems?: number;\n maxItems?: number;\n}\n\nexport type FieldDef =\n | TextField\n | NumberField\n | BooleanField\n | DateField\n | MarkdownField\n | RichTextField\n | JsonField\n | AssetField\n | ReferenceField\n | SlugField\n | ListField\n | ObjectField\n | BlocksField;\n\nexport type CollectionKind = \"collection\" | \"singleton\";\nexport type Visibility = \"public\" | \"private\";\n\nexport interface SlugConfig {\n from: string;\n required: boolean;\n}\n\n/** Canonical collection schema (one `collection_versions.schema_json`). */\nexport interface CollectionSchema {\n name: string;\n label: string;\n kind: CollectionKind;\n visibility: Visibility;\n titleField: string | null;\n slug: SlugConfig | null;\n path: string | null;\n /**\n * How this collection is meant to be written: voice, length, conventions,\n * what belongs here and what does not.\n *\n * A field `description` says what a field is for. This says what *good*\n * looks like, which is the thing an agent has no way to infer from types and\n * the thing a new human contributor is told verbally and then forgets. It\n * travels with the schema, so it is versioned, reviewed, and deployed like\n * the rest of it.\n */\n guidance?: string;\n fields: FieldDef[];\n}\n\n/** Maximum object/list nesting depth (SPEC 6.2). */\nexport const MAX_NESTING_DEPTH = 4;\n","import type {\n AssetField,\n BlockDef,\n BlocksField,\n BooleanField,\n DateField,\n FieldDef,\n FieldUi,\n JsonField,\n JsonValue,\n ListField,\n ListItem,\n MarkdownField,\n NumberField,\n ObjectField,\n ReferenceField,\n RichTextField,\n SlugField,\n TextField,\n} from \"./types.js\";\n\n/** A field definition before its `key` is assigned by `collection()`. */\nexport type FieldInput = DistributiveOmit<FieldDef, \"key\">;\n\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;\n\ninterface CommonOptions {\n label?: string;\n description?: string;\n required?: boolean;\n localized?: boolean;\n ui?: FieldUi;\n}\n\nfunction common(o: CommonOptions): {\n label?: string;\n description?: string;\n required: boolean;\n localized?: boolean;\n ui?: FieldUi;\n} {\n const base: { label?: string; description?: string; required: boolean; localized?: boolean; ui?: FieldUi } = {\n required: o.required ?? false,\n };\n if (o.label !== undefined) base.label = o.label;\n if (o.description !== undefined) base.description = o.description;\n if (o.localized !== undefined) base.localized = o.localized;\n if (o.ui !== undefined) base.ui = o.ui;\n return base;\n}\n\nexport interface TextOptions extends CommonOptions {\n multiline?: boolean;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n enum?: string[];\n default?: string;\n}\nexport interface NumberOptions extends CommonOptions {\n integer?: boolean;\n min?: number;\n max?: number;\n default?: number;\n}\nexport interface BooleanOptions extends CommonOptions {\n default?: boolean;\n}\nexport interface DateOptions extends CommonOptions {\n min?: string;\n max?: string;\n default?: string;\n}\nexport interface MarkdownOptions extends CommonOptions {\n minLength?: number;\n maxLength?: number;\n default?: string;\n}\nexport interface JsonOptions extends CommonOptions {\n default?: JsonValue;\n}\nexport interface AssetOptions extends CommonOptions {\n allowed?: string[];\n multiple?: boolean;\n}\nexport interface ReferenceOptions extends CommonOptions {\n to: string;\n multiple?: boolean;\n}\nexport interface SlugOptions extends CommonOptions {\n from?: string;\n}\nexport interface ListOptions extends CommonOptions {\n of: ListItem;\n minItems?: number;\n maxItems?: number;\n}\nexport interface ObjectOptions extends CommonOptions {\n fields: Record<string, FieldInput>;\n}\nexport type RichTextOptions = CommonOptions;\nexport interface BlocksOptions extends CommonOptions {\n /** The block components entries may compose, built with `block()`. */\n allowed: BlockDef[];\n minItems?: number;\n maxItems?: number;\n}\n\n/** Define a reusable block component for `field.blocks({ allowed: [...] })`. */\nexport function block(key: string, o: { label?: string; fields: Record<string, FieldInput> }): BlockDef {\n return { key, label: o.label ?? key, fields: assignKeys(o.fields) };\n}\n\nfunction defined<T extends Record<string, unknown>>(obj: T): T {\n for (const key of Object.keys(obj)) {\n if (obj[key] === undefined) delete obj[key];\n }\n return obj;\n}\n\nexport const field = {\n text(o: TextOptions = {}): Omit<TextField, \"key\"> {\n return defined({\n type: \"text\",\n ...common(o),\n multiline: o.multiline ?? false,\n minLength: o.minLength,\n maxLength: o.maxLength,\n pattern: o.pattern,\n enum: o.enum,\n default: o.default,\n }) as Omit<TextField, \"key\">;\n },\n\n number(o: NumberOptions = {}): Omit<NumberField, \"key\"> {\n return defined({\n type: \"number\",\n ...common(o),\n integer: o.integer ?? false,\n min: o.min,\n max: o.max,\n default: o.default,\n }) as Omit<NumberField, \"key\">;\n },\n\n boolean(o: BooleanOptions = {}): Omit<BooleanField, \"key\"> {\n return defined({\n type: \"boolean\",\n ...common(o),\n default: o.default,\n }) as Omit<BooleanField, \"key\">;\n },\n\n date(o: DateOptions = {}): Omit<DateField, \"key\"> {\n return defined({\n type: \"date\",\n ...common(o),\n min: o.min,\n max: o.max,\n default: o.default,\n }) as Omit<DateField, \"key\">;\n },\n\n datetime(o: DateOptions = {}): Omit<DateField, \"key\"> {\n return defined({\n type: \"datetime\",\n ...common(o),\n min: o.min,\n max: o.max,\n default: o.default,\n }) as Omit<DateField, \"key\">;\n },\n\n markdown(o: MarkdownOptions = {}): Omit<MarkdownField, \"key\"> {\n return defined({\n type: \"markdown\",\n ...common(o),\n minLength: o.minLength,\n maxLength: o.maxLength,\n default: o.default,\n }) as Omit<MarkdownField, \"key\">;\n },\n\n json(o: JsonOptions = {}): Omit<JsonField, \"key\"> {\n return defined({\n type: \"json\",\n ...common(o),\n default: o.default,\n }) as Omit<JsonField, \"key\">;\n },\n\n asset(o: AssetOptions = {}): Omit<AssetField, \"key\"> {\n return defined({\n type: \"asset\",\n ...common(o),\n allowed: o.allowed ?? [\"*/*\"],\n multiple: o.multiple ?? false,\n }) as Omit<AssetField, \"key\">;\n },\n\n reference(o: ReferenceOptions): Omit<ReferenceField, \"key\"> {\n return defined({\n type: \"reference\",\n ...common(o),\n target: o.to,\n multiple: o.multiple ?? false,\n }) as Omit<ReferenceField, \"key\">;\n },\n\n slug(o: SlugOptions = {}): Omit<SlugField, \"key\"> {\n return defined({\n type: \"slug\",\n ...common(o),\n from: o.from,\n }) as Omit<SlugField, \"key\">;\n },\n\n list(o: ListOptions): Omit<ListField, \"key\"> {\n return defined({\n type: \"list\",\n ...common(o),\n item: o.of,\n minItems: o.minItems,\n maxItems: o.maxItems,\n }) as Omit<ListField, \"key\">;\n },\n\n object(o: ObjectOptions): Omit<ObjectField, \"key\"> {\n return defined({\n type: \"object\",\n ...common(o),\n fields: assignKeys(o.fields),\n }) as Omit<ObjectField, \"key\">;\n },\n\n richText(o: RichTextOptions = {}): Omit<RichTextField, \"key\"> {\n return defined({\n type: \"richText\",\n ...common(o),\n }) as Omit<RichTextField, \"key\">;\n },\n\n blocks(o: BlocksOptions): Omit<BlocksField, \"key\"> {\n return defined({\n type: \"blocks\",\n ...common(o),\n blocks: o.allowed,\n minItems: o.minItems,\n maxItems: o.maxItems,\n }) as Omit<BlocksField, \"key\">;\n },\n} as const;\n\n/** Item builders for `field.list({ of: item.<type>() })`. */\nexport const item = {\n text(o: { minLength?: number; maxLength?: number; pattern?: string; enum?: string[] } = {}): ListItem {\n return defined({ kind: \"text\", ...o }) as ListItem;\n },\n number(o: { integer?: boolean; min?: number; max?: number } = {}): ListItem {\n return defined({ kind: \"number\", ...o }) as ListItem;\n },\n boolean(): ListItem {\n return { kind: \"boolean\" };\n },\n date(o: { min?: string; max?: string } = {}): ListItem {\n return defined({ kind: \"date\", ...o }) as ListItem;\n },\n datetime(o: { min?: string; max?: string } = {}): ListItem {\n return defined({ kind: \"datetime\", ...o }) as ListItem;\n },\n markdown(o: { minLength?: number; maxLength?: number } = {}): ListItem {\n return defined({ kind: \"markdown\", ...o }) as ListItem;\n },\n json(): ListItem {\n return { kind: \"json\" };\n },\n reference(o: { to: string }): ListItem {\n return { kind: \"reference\", target: o.to };\n },\n asset(o: { allowed?: string[] } = {}): ListItem {\n return { kind: \"asset\", allowed: o.allowed ?? [\"*/*\"] };\n },\n object(o: { fields: Record<string, FieldInput> }): ListItem {\n return { kind: \"object\", fields: assignKeys(o.fields) };\n },\n} as const;\n\n/** Turn a `{ key: FieldInput }` map into an ordered array of `FieldDef`. */\nexport function assignKeys(fields: Record<string, FieldInput>): FieldDef[] {\n return Object.entries(fields).map(([key, def]) => ({ key, ...def }) as FieldDef);\n}\n","import { assignKeys, type FieldInput } from \"./fields.js\";\nimport type { CollectionKind, CollectionSchema, SlugConfig, Visibility } from \"./types.js\";\n\nexport interface CollectionInput {\n /** Immutable collection key, e.g. `posts`. */\n name: string;\n label?: string;\n kind?: CollectionKind;\n /** Visibility defaults to `private` and must be explicitly declared `public`. */\n visibility?: Visibility;\n titleField?: string;\n /** Path template such as `/blog/{slug}`. */\n path?: string;\n /**\n * House style for this collection, read by anyone writing into it — the\n * dashboard editor, the MCP schema tool, an agent planning a draft.\n */\n guidance?: string;\n fields: Record<string, FieldInput>;\n}\n\n/**\n * Author a collection schema. Returns the canonical `CollectionSchema` with an\n * ordered fields array. Defaults: `kind=collection`, `visibility=private`.\n */\nexport function collection(input: CollectionInput): CollectionSchema {\n const fields = assignKeys(input.fields);\n const slugField = fields.find((f) => f.type === \"slug\");\n\n let slug: SlugConfig | null = null;\n if (slugField && slugField.type === \"slug\") {\n slug = {\n from: slugField.from ?? input.titleField ?? slugField.key,\n required: slugField.required,\n };\n }\n\n // `guidance` is omitted rather than set to null when absent: it is part of the\n // canonical form the schema hash is computed over, so emitting a placeholder\n // would re-version every collection in every project that upgrades.\n const guidance = input.guidance ? { guidance: input.guidance } : {};\n\n return {\n name: input.name,\n label: input.label ?? input.name,\n kind: input.kind ?? \"collection\",\n visibility: input.visibility ?? \"private\",\n titleField: input.titleField ?? null,\n slug,\n path: input.path ?? null,\n ...guidance,\n fields,\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/main.js
CHANGED
|
@@ -477,6 +477,12 @@ var ManagementClient = class {
|
|
|
477
477
|
collections: (project, signal) => this.get(`/projects/${enc(project)}/collections`, void 0, signal),
|
|
478
478
|
collection: (project, key, signal) => this.get(`/projects/${enc(project)}/collections/${enc(key)}`, void 0, signal),
|
|
479
479
|
versions: (project, key, signal) => this.get(`/projects/${enc(project)}/collections/${enc(key)}/versions`, void 0, signal),
|
|
480
|
+
/** What is still untranslated in a collection, per project locale. */
|
|
481
|
+
translations: (project, key, signal) => this.get(
|
|
482
|
+
`/projects/${enc(project)}/collections/${enc(key)}/translations`,
|
|
483
|
+
void 0,
|
|
484
|
+
signal
|
|
485
|
+
),
|
|
480
486
|
diff: (project, collections) => this.mutate("POST", `/projects/${enc(project)}/schema/diff`, { collections }),
|
|
481
487
|
push: (project, collections, opts = {}) => this.mutate("POST", `/projects/${enc(project)}/schema/push`, {
|
|
482
488
|
collections,
|
|
@@ -495,7 +501,8 @@ var ManagementClient = class {
|
|
|
495
501
|
entries = {
|
|
496
502
|
list: (project, opts = {}) => this.page(`/projects/${enc(project)}/entries`, opts, {
|
|
497
503
|
collection: opts.collection,
|
|
498
|
-
status: opts.status
|
|
504
|
+
status: opts.status,
|
|
505
|
+
q: opts.q
|
|
499
506
|
}),
|
|
500
507
|
create: (project, body) => this.mutate("POST", `/projects/${enc(project)}/entries`, body),
|
|
501
508
|
get: (project, entry, signal) => this.get(`/projects/${enc(project)}/entries/${enc(entry)}`, void 0, signal),
|
|
@@ -575,13 +582,31 @@ var ManagementClient = class {
|
|
|
575
582
|
void 0,
|
|
576
583
|
signal
|
|
577
584
|
),
|
|
578
|
-
runChecks: (project, changeSet) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/checks/run`)
|
|
585
|
+
runChecks: (project, changeSet) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/checks/run`),
|
|
586
|
+
/** Report a verdict Myna cannot compute, against the current contents. */
|
|
587
|
+
reportCheck: (project, changeSet, name, body) => this.mutate(
|
|
588
|
+
"POST",
|
|
589
|
+
`/projects/${enc(project)}/change-sets/${enc(changeSet)}/checks/${enc(name)}`,
|
|
590
|
+
body
|
|
591
|
+
),
|
|
592
|
+
/** Field-level before/after for everything this change set would change. */
|
|
593
|
+
diff: (project, changeSet, signal) => this.get(
|
|
594
|
+
`/projects/${enc(project)}/change-sets/${enc(changeSet)}/diff`,
|
|
595
|
+
void 0,
|
|
596
|
+
signal
|
|
597
|
+
)
|
|
598
|
+
};
|
|
599
|
+
// --- Declared external checks ---------------------------------------------
|
|
600
|
+
checks = {
|
|
601
|
+
list: (project, signal) => this.get(`/projects/${enc(project)}/checks`, void 0, signal),
|
|
602
|
+
declare: (project, body) => this.mutate("POST", `/projects/${enc(project)}/checks`, body),
|
|
603
|
+
remove: (project, name) => this.mutate("DELETE", `/projects/${enc(project)}/checks/${enc(name)}`)
|
|
579
604
|
};
|
|
580
605
|
// --- Previews -------------------------------------------------------------
|
|
581
606
|
previews = {
|
|
582
607
|
create: (project, body) => this.mutate("POST", `/projects/${enc(project)}/previews`, body),
|
|
583
608
|
list: (project, signal) => this.get(`/projects/${enc(project)}/previews`, void 0, signal),
|
|
584
|
-
revoke: (project,
|
|
609
|
+
revoke: (project, preview2) => this.mutate("DELETE", `/projects/${enc(project)}/previews/${enc(preview2)}`)
|
|
585
610
|
};
|
|
586
611
|
// --- Assets ---------------------------------------------------------------
|
|
587
612
|
assets = {
|
|
@@ -589,7 +614,11 @@ var ManagementClient = class {
|
|
|
589
614
|
completeUpload: (project, upload) => this.mutate("POST", `/projects/${enc(project)}/assets/uploads/${enc(upload)}/complete`).then(
|
|
590
615
|
(r) => r.asset
|
|
591
616
|
),
|
|
592
|
-
list: (project, opts = {}) => this.page(`/projects/${enc(project)}/assets`, opts
|
|
617
|
+
list: (project, opts = {}) => this.page(`/projects/${enc(project)}/assets`, opts, {
|
|
618
|
+
tag: opts.tag,
|
|
619
|
+
checksum: opts.checksum,
|
|
620
|
+
orphaned: opts.orphaned ? "true" : void 0
|
|
621
|
+
}),
|
|
593
622
|
get: (project, asset, signal) => this.get(`/projects/${enc(project)}/assets/${enc(asset)}`, void 0, signal),
|
|
594
623
|
usage: (project, asset, signal) => this.get(`/projects/${enc(project)}/assets/${enc(asset)}`, { usage: "true" }, signal),
|
|
595
624
|
update: (project, asset, body) => this.mutate("PATCH", `/projects/${enc(project)}/assets/${enc(asset)}`, body),
|
|
@@ -1555,6 +1584,7 @@ function schemaToDsl(schema) {
|
|
|
1555
1584
|
opts.push(`visibility: ${str(schema.visibility)}`);
|
|
1556
1585
|
if (schema.titleField) opts.push(`titleField: ${str(schema.titleField)}`);
|
|
1557
1586
|
if (schema.path) opts.push(`path: ${str(schema.path)}`);
|
|
1587
|
+
if (schema.guidance) opts.push(`guidance: ${str(schema.guidance)}`);
|
|
1558
1588
|
const fieldLines = schema.fields.map((f) => ` ${ident(f.key)}: ${emitField(f)},`).join("\n");
|
|
1559
1589
|
return [
|
|
1560
1590
|
`import { collection, field, item } from "@myna-sh/sdk/schema";`,
|
|
@@ -1790,6 +1820,29 @@ function registerSchema(program) {
|
|
|
1790
1820
|
);
|
|
1791
1821
|
})
|
|
1792
1822
|
);
|
|
1823
|
+
schema.command("translations").description("Report what is still untranslated in a collection").argument("<collection>", "collection key").action(
|
|
1824
|
+
handle(async (ctx, args) => {
|
|
1825
|
+
const project = ctx.requireProject();
|
|
1826
|
+
const report = await ctx.management().schema.translations(project, args[0]);
|
|
1827
|
+
emit(report, () => {
|
|
1828
|
+
if (report.locales.length === 0) {
|
|
1829
|
+
diag("Localization is not configured for this project.");
|
|
1830
|
+
return;
|
|
1831
|
+
}
|
|
1832
|
+
if (report.fields.length === 0) {
|
|
1833
|
+
diag(`No localized fields in "${report.collection}".`);
|
|
1834
|
+
return;
|
|
1835
|
+
}
|
|
1836
|
+
diag(`${report.totalEntries} entr(y/ies), localized fields: ${report.fields.join(", ")}`);
|
|
1837
|
+
table(report.coverage, [
|
|
1838
|
+
{ header: "LOCALE", value: (c) => c.locale },
|
|
1839
|
+
{ header: "COMPLETE", value: (c) => `${c.completeEntries}/${c.totalEntries}` },
|
|
1840
|
+
{ header: "PERCENT", value: (c) => `${c.percent}%` },
|
|
1841
|
+
{ header: "MISSING FIELDS", value: (c) => String(c.missingFields) }
|
|
1842
|
+
]);
|
|
1843
|
+
});
|
|
1844
|
+
})
|
|
1845
|
+
);
|
|
1793
1846
|
const types = program.command("types").description("Generate content types");
|
|
1794
1847
|
types.command("generate").description("Generate deterministic TypeScript types from schemas").option("--schema-dir <dir>", "schema directory").option("--deployed", "use deployed schemas instead of local files", false).option("--out <file>", "write to a file instead of stdout").action(
|
|
1795
1848
|
handle(async (ctx, _args, opts) => {
|
|
@@ -1942,12 +1995,13 @@ function parseFormat(value) {
|
|
|
1942
1995
|
}
|
|
1943
1996
|
function registerEntries(program) {
|
|
1944
1997
|
const entries = program.command("entries").description("Create, read, update, and manage entries");
|
|
1945
|
-
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(
|
|
1998
|
+
entries.command("list").description("List entries in a collection").argument("<collection>", "collection key").option("--status <status>", "filter by status").option("--search <query>", "full-text query across the entry's text").option("--limit <n>", "page size", "25").option("--cursor <cursor>", "pagination cursor").action(
|
|
1946
1999
|
handle(async (ctx, args, opts) => {
|
|
1947
2000
|
const project = ctx.requireProject();
|
|
1948
2001
|
const page = await ctx.management().entries.list(project, {
|
|
1949
2002
|
collection: args[0],
|
|
1950
2003
|
status: opts.status,
|
|
2004
|
+
q: opts.search,
|
|
1951
2005
|
limit: Number(opts.limit),
|
|
1952
2006
|
cursor: opts.cursor
|
|
1953
2007
|
});
|
|
@@ -2170,6 +2224,25 @@ function registerEntries(program) {
|
|
|
2170
2224
|
emit(result, () => diag(`Restored revision ${opts.revision} into ${result.revision.id}.`));
|
|
2171
2225
|
})
|
|
2172
2226
|
);
|
|
2227
|
+
entries.command("search").description("Full-text search entries across a project").argument("<query>", "words to search for").option("--collection <key>", "restrict to one collection").option("--limit <n>", "page size", "25").action(
|
|
2228
|
+
handle(async (ctx, args, opts) => {
|
|
2229
|
+
const project = ctx.requireProject();
|
|
2230
|
+
const page = await ctx.management().entries.list(project, {
|
|
2231
|
+
q: args[0],
|
|
2232
|
+
collection: opts.collection,
|
|
2233
|
+
limit: Number(opts.limit)
|
|
2234
|
+
});
|
|
2235
|
+
emit(
|
|
2236
|
+
{ data: page.data, nextCursor: page.nextCursor },
|
|
2237
|
+
() => table(page.data, [
|
|
2238
|
+
{ header: "ID", value: (e) => e.id },
|
|
2239
|
+
{ header: "COLLECTION", value: (e) => e.collection },
|
|
2240
|
+
{ header: "SLUG", value: (e) => e.slug ?? "" },
|
|
2241
|
+
{ header: "STATUS", value: (e) => e.status }
|
|
2242
|
+
])
|
|
2243
|
+
);
|
|
2244
|
+
})
|
|
2245
|
+
);
|
|
2173
2246
|
}
|
|
2174
2247
|
function collect(value, previous) {
|
|
2175
2248
|
return [...previous, value];
|
|
@@ -2240,11 +2313,11 @@ function registerChanges(program) {
|
|
|
2240
2313
|
changes.command("preview").description("Create a preview for a change set").argument("<id>", "change set id").action(
|
|
2241
2314
|
handle(async (ctx, args) => {
|
|
2242
2315
|
const project = ctx.requireProject();
|
|
2243
|
-
const
|
|
2244
|
-
emit(
|
|
2245
|
-
process.stdout.write(`${
|
|
2316
|
+
const preview2 = await ctx.management().previews.create(project, { changeSetId: args[0] });
|
|
2317
|
+
emit(preview2, () => {
|
|
2318
|
+
process.stdout.write(`${preview2.url}
|
|
2246
2319
|
`);
|
|
2247
|
-
diag(`fallback: ${
|
|
2320
|
+
diag(`fallback: ${preview2.fallbackUrl}`);
|
|
2248
2321
|
});
|
|
2249
2322
|
})
|
|
2250
2323
|
);
|
|
@@ -2367,6 +2440,74 @@ function registerChanges(program) {
|
|
|
2367
2440
|
if (checks.some((c) => c.status === "failed")) process.exitCode = 1;
|
|
2368
2441
|
})
|
|
2369
2442
|
);
|
|
2443
|
+
changes.command("diff").description("Show what a change set would change, field by field").argument("<id>", "change set id").action(
|
|
2444
|
+
handle(async (ctx, args) => {
|
|
2445
|
+
const project = ctx.requireProject();
|
|
2446
|
+
const diff = await ctx.management().changeSets.diff(project, args[0]);
|
|
2447
|
+
emit(diff, () => {
|
|
2448
|
+
for (const item of diff.items) {
|
|
2449
|
+
const label = item.collection && item.slug ? `${item.collection}/${item.slug}` : item.resourceId;
|
|
2450
|
+
diag(`${item.operation} ${label}${item.changeSummary ? ` \u2014 ${item.changeSummary}` : ""}`);
|
|
2451
|
+
table(item.changes, [
|
|
2452
|
+
{ header: "FIELD", value: (c) => c.path },
|
|
2453
|
+
{ header: "KIND", value: (c) => c.kind },
|
|
2454
|
+
{ header: "BEFORE", value: (c) => preview(c.before) },
|
|
2455
|
+
{ header: "AFTER", value: (c) => preview(c.after) }
|
|
2456
|
+
]);
|
|
2457
|
+
}
|
|
2458
|
+
if (diff.items.length === 0) diag("No items.");
|
|
2459
|
+
});
|
|
2460
|
+
})
|
|
2461
|
+
);
|
|
2462
|
+
changes.command("update").description("Retitle a change set or mark it ready for review").argument("<id>", "change set id").option("--title <title>", "new title").option("--description <text>", "new description").option("--ready", "mark ready for review", false).action(
|
|
2463
|
+
handle(async (ctx, args, opts) => {
|
|
2464
|
+
const project = ctx.requireProject();
|
|
2465
|
+
if (!opts.title && !opts.description && !opts.ready) {
|
|
2466
|
+
throw new UsageError("Provide --title, --description, or --ready.");
|
|
2467
|
+
}
|
|
2468
|
+
const cs = await ctx.management().changeSets.update(project, args[0], {
|
|
2469
|
+
...opts.title ? { title: opts.title } : {},
|
|
2470
|
+
...opts.description ? { description: opts.description } : {},
|
|
2471
|
+
...opts.ready ? { status: "ready" } : {}
|
|
2472
|
+
});
|
|
2473
|
+
emit(cs, () => diag(`${cs.id} is ${cs.status}.`));
|
|
2474
|
+
})
|
|
2475
|
+
);
|
|
2476
|
+
changes.command("remove-reviewer").description("Unassign a reviewer from a change set").argument("<id>", "change set id").argument("<reviewer>", "reviewer user id").action(
|
|
2477
|
+
handle(async (ctx, args) => {
|
|
2478
|
+
const project = ctx.requireProject();
|
|
2479
|
+
const reviews = await ctx.management().changeSets.removeReviewer(project, args[0], args[1]);
|
|
2480
|
+
emit(reviews, () => diag(`${reviews.length} reviewer(s) remain.`));
|
|
2481
|
+
})
|
|
2482
|
+
);
|
|
2483
|
+
changes.command("resolve-comment").description("Resolve or reopen a comment on a change set").argument("<id>", "change set id").argument("<comment>", "comment id").option("--reopen", "reopen instead of resolving", false).action(
|
|
2484
|
+
handle(async (ctx, args, opts) => {
|
|
2485
|
+
const project = ctx.requireProject();
|
|
2486
|
+
const comment = await ctx.management().changeSets.resolveComment(project, args[0], args[1], !opts.reopen);
|
|
2487
|
+
emit(comment, () => diag(comment.resolvedAt ? "Resolved." : "Reopened."));
|
|
2488
|
+
})
|
|
2489
|
+
);
|
|
2490
|
+
changes.command("report-check").description("Report the result of an external check on a change set").argument("<id>", "change set id").argument("<name>", "declared check name").requiredOption("--status <status>", "passed, failed, or skipped").option("--message <text>", "detail shown to the reviewer").action(
|
|
2491
|
+
handle(async (ctx, args, opts) => {
|
|
2492
|
+
const project = ctx.requireProject();
|
|
2493
|
+
const status = opts.status;
|
|
2494
|
+
if (status !== "passed" && status !== "failed" && status !== "skipped") {
|
|
2495
|
+
throw new UsageError("--status must be passed, failed, or skipped.");
|
|
2496
|
+
}
|
|
2497
|
+
const check2 = await ctx.management().changeSets.reportCheck(project, args[0], args[1], {
|
|
2498
|
+
status,
|
|
2499
|
+
...opts.message ? { details: [{ message: opts.message }] } : {}
|
|
2500
|
+
});
|
|
2501
|
+
emit(check2, () => diag(`Reported ${check2.name}: ${check2.status}.`));
|
|
2502
|
+
if (check2.status === "failed") process.exitCode = 1;
|
|
2503
|
+
})
|
|
2504
|
+
);
|
|
2505
|
+
}
|
|
2506
|
+
function preview(value) {
|
|
2507
|
+
if (value === void 0 || value === null) return "\u2014";
|
|
2508
|
+
const text = typeof value === "string" ? value : JSON.stringify(value);
|
|
2509
|
+
const flat = text.replaceAll(/\s+/g, " ");
|
|
2510
|
+
return flat.length > 48 ? `${flat.slice(0, 47)}\u2026` : flat;
|
|
2370
2511
|
}
|
|
2371
2512
|
|
|
2372
2513
|
// src/commands/previews.ts
|
|
@@ -2379,8 +2520,8 @@ async function create(ctx, ref) {
|
|
|
2379
2520
|
return ctx.management().previews.create(ctx.requireProject(), previewTarget(ref));
|
|
2380
2521
|
}
|
|
2381
2522
|
function registerPreviews(program) {
|
|
2382
|
-
const
|
|
2383
|
-
|
|
2523
|
+
const preview2 = program.command("preview").description("Create and manage preview links");
|
|
2524
|
+
preview2.command("create").description("Create a preview for a change set or entry").argument("<change-set-or-entry>", "chs_ or ent_ id").action(
|
|
2384
2525
|
handle(async (ctx, args) => {
|
|
2385
2526
|
const p = await create(ctx, args[0]);
|
|
2386
2527
|
emit(p, () => {
|
|
@@ -2390,7 +2531,7 @@ function registerPreviews(program) {
|
|
|
2390
2531
|
});
|
|
2391
2532
|
})
|
|
2392
2533
|
);
|
|
2393
|
-
|
|
2534
|
+
preview2.command("open").description("Create a preview and open it in the browser").argument("<change-set-or-entry>", "chs_ or ent_ id").action(
|
|
2394
2535
|
handle(async (ctx, args) => {
|
|
2395
2536
|
const p = await create(ctx, args[0]);
|
|
2396
2537
|
if (ctx.interactive) openBrowser(p.url);
|
|
@@ -2398,7 +2539,7 @@ function registerPreviews(program) {
|
|
|
2398
2539
|
`));
|
|
2399
2540
|
})
|
|
2400
2541
|
);
|
|
2401
|
-
|
|
2542
|
+
preview2.command("list").description("List preview tokens for the project").action(
|
|
2402
2543
|
handle(async (ctx) => {
|
|
2403
2544
|
const previews = await ctx.management().previews.list(ctx.requireProject());
|
|
2404
2545
|
emit(
|
|
@@ -2412,7 +2553,7 @@ function registerPreviews(program) {
|
|
|
2412
2553
|
);
|
|
2413
2554
|
})
|
|
2414
2555
|
);
|
|
2415
|
-
|
|
2556
|
+
preview2.command("revoke").description("Revoke a preview token").argument("<id>", "preview id (prv_)").action(
|
|
2416
2557
|
handle(async (ctx, args) => {
|
|
2417
2558
|
await ctx.management().previews.revoke(ctx.requireProject(), args[0]);
|
|
2418
2559
|
emit({ ok: true }, () => diag(`Revoked ${args[0]}.`));
|
|
@@ -2488,6 +2629,31 @@ function registerAssets(program) {
|
|
|
2488
2629
|
emit({ ok: true }, () => diag(`Deleted ${args[0]}.`));
|
|
2489
2630
|
})
|
|
2490
2631
|
);
|
|
2632
|
+
assets.command("update").description("Set an asset's alt text, caption, filename, or tags").argument("<id>", "asset id (ast_)").option("--alt <text>", "default alt text").option("--caption <text>", "caption").option("--filename <name>", "display filename").option("--tag <tag...>", "replace the asset's tags").action(
|
|
2633
|
+
handle(async (ctx, args, opts) => {
|
|
2634
|
+
const project = ctx.requireProject();
|
|
2635
|
+
if (!opts.alt && !opts.caption && !opts.filename && !opts.tag) {
|
|
2636
|
+
throw new UsageError("Provide --alt, --caption, --filename, or --tag.");
|
|
2637
|
+
}
|
|
2638
|
+
const asset = await ctx.management().assets.update(project, args[0], {
|
|
2639
|
+
...opts.alt !== void 0 ? { defaultAlt: opts.alt } : {},
|
|
2640
|
+
...opts.caption !== void 0 ? { caption: opts.caption } : {},
|
|
2641
|
+
...opts.filename !== void 0 ? { displayFilename: opts.filename } : {},
|
|
2642
|
+
...opts.tag !== void 0 ? { tags: opts.tag } : {}
|
|
2643
|
+
});
|
|
2644
|
+
emit(asset, () => diag(`Updated ${asset.id}.`));
|
|
2645
|
+
})
|
|
2646
|
+
);
|
|
2647
|
+
assets.command("replace").description("Replace an asset's binary, keeping its id and references").argument("<id>", "asset id (ast_)").argument("<file>", "path to the replacement file").option("--confirm-replace", "required consequence flag", false).action(
|
|
2648
|
+
handle(async (ctx, args, opts) => {
|
|
2649
|
+
if (!opts.confirmReplace) throw new UsageError("Replacing an asset requires --confirm-replace.");
|
|
2650
|
+
const project = ctx.requireProject();
|
|
2651
|
+
const client = ctx.management();
|
|
2652
|
+
const uploaded = await client.assets.upload(project, args[1]);
|
|
2653
|
+
const asset = await client.assets.replace(project, args[0], { uploadId: uploaded.id });
|
|
2654
|
+
emit(asset, () => diag(`Replaced ${asset.id} (${asset.byteSize} bytes).`));
|
|
2655
|
+
})
|
|
2656
|
+
);
|
|
2491
2657
|
}
|
|
2492
2658
|
|
|
2493
2659
|
// ../shared/dist/ids.js
|
|
@@ -2511,6 +2677,7 @@ var ID_PREFIXES = {
|
|
|
2511
2677
|
changeSetReview: "csr",
|
|
2512
2678
|
changeSetComment: "csc",
|
|
2513
2679
|
changeSetCheck: "chk",
|
|
2680
|
+
projectCheck: "pck",
|
|
2514
2681
|
asset: "ast",
|
|
2515
2682
|
assetUpload: "upl",
|
|
2516
2683
|
savedView: "viw",
|
|
@@ -2631,6 +2798,8 @@ function registerAdmin(program) {
|
|
|
2631
2798
|
registerCors(program);
|
|
2632
2799
|
registerKeys(program);
|
|
2633
2800
|
registerMembers(program);
|
|
2801
|
+
registerViews(program);
|
|
2802
|
+
registerChecks(program);
|
|
2634
2803
|
registerWebhooks(program);
|
|
2635
2804
|
registerActivity(program);
|
|
2636
2805
|
registerBilling(program);
|
|
@@ -2848,17 +3017,22 @@ function registerKeys(program) {
|
|
|
2848
3017
|
{ header: "ID", value: (k) => k.id },
|
|
2849
3018
|
{ header: "NAME", value: (k) => k.name },
|
|
2850
3019
|
{ header: "SCOPES", value: (k) => k.scopes.join(",") },
|
|
3020
|
+
{ header: "COLLECTIONS", value: (k) => k.collectionKeys?.join(",") ?? "all" },
|
|
2851
3021
|
{ header: "REVOKED", value: (k) => k.revokedAt ? "yes" : "" }
|
|
2852
3022
|
])
|
|
2853
3023
|
);
|
|
2854
3024
|
})
|
|
2855
3025
|
);
|
|
2856
|
-
keys.command("create").description("Create an API key (project-scoped by default)").requiredOption("--name <name>", "key name").requiredOption("--scopes <scopes>", "comma-separated scopes").option("--label <label>", "agent/integration label").option("--org-scope", "create an organization-scoped key", false).option("--expires-at <iso>", "expiry (ISO 8601)").action(
|
|
3026
|
+
keys.command("create").description("Create an API key (project-scoped by default)").requiredOption("--name <name>", "key name").requiredOption("--scopes <scopes>", "comma-separated scopes").option("--label <label>", "agent/integration label").option("--org-scope", "create an organization-scoped key", false).option("--collections <keys>", "confine the key to these collections (comma-separated)").option("--expires-at <iso>", "expiry (ISO 8601)").action(
|
|
2857
3027
|
handle(async (ctx, _args, opts) => {
|
|
3028
|
+
if (opts.collections && opts.orgScope) {
|
|
3029
|
+
throw new UsageError("--collections requires a project-scoped key; drop --org-scope.");
|
|
3030
|
+
}
|
|
2858
3031
|
const body = {
|
|
2859
3032
|
name: opts.name,
|
|
2860
3033
|
scopes: csv(opts.scopes),
|
|
2861
3034
|
label: opts.label,
|
|
3035
|
+
collectionKeys: opts.collections ? csv(opts.collections) : void 0,
|
|
2862
3036
|
expiresAt: opts.expiresAt
|
|
2863
3037
|
};
|
|
2864
3038
|
const result = opts.orgScope ? await ctx.management().apiKeys.createForOrganization(ctx.requireOrganization(), body) : await ctx.management().apiKeys.createForProject(ctx.requireProject(), body);
|
|
@@ -2867,6 +3041,7 @@ function registerKeys(program) {
|
|
|
2867
3041
|
keyValues([
|
|
2868
3042
|
["ID", result.key.id],
|
|
2869
3043
|
["Name", result.key.name],
|
|
3044
|
+
["Collections", result.key.collectionKeys?.join(", ") ?? "(all)"],
|
|
2870
3045
|
["Secret", result.secret]
|
|
2871
3046
|
]);
|
|
2872
3047
|
});
|
|
@@ -2937,6 +3112,84 @@ function registerMembers(program) {
|
|
|
2937
3112
|
emit({ ok: true }, () => diag(`Removed ${args[0]}.`));
|
|
2938
3113
|
})
|
|
2939
3114
|
);
|
|
3115
|
+
members.command("revoke-invite").description("Revoke a pending invitation").argument("<invitation>", "invitation id (inv_)").action(
|
|
3116
|
+
handle(async (ctx, args) => {
|
|
3117
|
+
await ctx.management().invitations.revoke(ctx.requireOrganization(), args[0]);
|
|
3118
|
+
emit({ ok: true }, () => diag(`Revoked ${args[0]}.`));
|
|
3119
|
+
})
|
|
3120
|
+
);
|
|
3121
|
+
}
|
|
3122
|
+
function registerViews(program) {
|
|
3123
|
+
const views = program.command("views").description("Manage saved entry-table views");
|
|
3124
|
+
views.command("list").description("List saved views").action(
|
|
3125
|
+
handle(async (ctx) => {
|
|
3126
|
+
const list = await ctx.management().projects.views(ctx.requireProject());
|
|
3127
|
+
emit(
|
|
3128
|
+
list,
|
|
3129
|
+
() => table(list, [
|
|
3130
|
+
{ header: "ID", value: (v) => v.id },
|
|
3131
|
+
{ header: "NAME", value: (v) => v.name },
|
|
3132
|
+
{ header: "FILTERS", value: (v) => JSON.stringify(v.filters) }
|
|
3133
|
+
])
|
|
3134
|
+
);
|
|
3135
|
+
})
|
|
3136
|
+
);
|
|
3137
|
+
views.command("create").description("Create a saved view from a JSON filter object").requiredOption("--name <name>", "view name").requiredOption("--filters <json>", `filter object, e.g. '{"status":"draft"}'`).action(
|
|
3138
|
+
handle(async (ctx, _args, opts) => {
|
|
3139
|
+
let filters;
|
|
3140
|
+
try {
|
|
3141
|
+
filters = JSON.parse(opts.filters);
|
|
3142
|
+
} catch {
|
|
3143
|
+
throw new UsageError("--filters must be valid JSON.");
|
|
3144
|
+
}
|
|
3145
|
+
const view = await ctx.management().projects.createView(ctx.requireProject(), {
|
|
3146
|
+
name: opts.name,
|
|
3147
|
+
filters
|
|
3148
|
+
});
|
|
3149
|
+
emit(view, () => diag(`Created view ${view.id}.`));
|
|
3150
|
+
})
|
|
3151
|
+
);
|
|
3152
|
+
views.command("delete").description("Delete a saved view").argument("<id>", "view id (viw_)").action(
|
|
3153
|
+
handle(async (ctx, args) => {
|
|
3154
|
+
await ctx.management().projects.deleteView(ctx.requireProject(), args[0]);
|
|
3155
|
+
emit({ ok: true }, () => diag(`Deleted ${args[0]}.`));
|
|
3156
|
+
})
|
|
3157
|
+
);
|
|
3158
|
+
}
|
|
3159
|
+
function registerChecks(program) {
|
|
3160
|
+
const checks = program.command("checks").description("Declare external publish checks");
|
|
3161
|
+
checks.command("list").description("List declared external checks").action(
|
|
3162
|
+
handle(async (ctx) => {
|
|
3163
|
+
const list = await ctx.management().checks.list(ctx.requireProject());
|
|
3164
|
+
emit(
|
|
3165
|
+
list,
|
|
3166
|
+
() => table(list, [
|
|
3167
|
+
{ header: "NAME", value: (c) => c.name },
|
|
3168
|
+
{ header: "REQUIRED", value: (c) => c.required ? "yes" : "no" },
|
|
3169
|
+
{ header: "DESCRIPTION", value: (c) => c.description ?? "" }
|
|
3170
|
+
])
|
|
3171
|
+
);
|
|
3172
|
+
})
|
|
3173
|
+
);
|
|
3174
|
+
checks.command("declare").description("Declare (or update) an external check").argument("<name>", "check name, e.g. links").option("--description <text>", "what the check verifies").option("--required", "block publish until it passes", false).action(
|
|
3175
|
+
handle(async (ctx, args, opts) => {
|
|
3176
|
+
const check2 = await ctx.management().checks.declare(ctx.requireProject(), {
|
|
3177
|
+
name: args[0],
|
|
3178
|
+
...opts.description ? { description: opts.description } : {},
|
|
3179
|
+
required: Boolean(opts.required)
|
|
3180
|
+
});
|
|
3181
|
+
emit(
|
|
3182
|
+
check2,
|
|
3183
|
+
() => diag(`Declared ${check2.name}${check2.required ? " (required before publish)" : ""}.`)
|
|
3184
|
+
);
|
|
3185
|
+
})
|
|
3186
|
+
);
|
|
3187
|
+
checks.command("remove").description("Remove a declared external check").argument("<name>", "check name").action(
|
|
3188
|
+
handle(async (ctx, args) => {
|
|
3189
|
+
await ctx.management().checks.remove(ctx.requireProject(), args[0]);
|
|
3190
|
+
emit({ ok: true }, () => diag(`Removed ${args[0]}.`));
|
|
3191
|
+
})
|
|
3192
|
+
);
|
|
2940
3193
|
}
|
|
2941
3194
|
function registerWebhooks(program) {
|
|
2942
3195
|
const webhooks = program.command("webhooks").description("Manage webhook endpoints");
|
|
@@ -3080,7 +3333,7 @@ import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync a
|
|
|
3080
3333
|
import { join as join6 } from "path";
|
|
3081
3334
|
|
|
3082
3335
|
// src/version.ts
|
|
3083
|
-
var VERSION = true ? "0.
|
|
3336
|
+
var VERSION = true ? "0.5.0" : "0.0.0-dev";
|
|
3084
3337
|
|
|
3085
3338
|
// src/commands/doctor.ts
|
|
3086
3339
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|