@myna-sh/cli 0.4.1 → 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 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":";AAyNO,IAAM,oBAAoB;;;ACvLjC,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;;;ACzQO,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;AAKA,QAAM,WAAW,MAAM,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;AAElE,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,GAAG;AAAA,IACH;AAAA,EACF;AACF;","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, preview) => this.mutate("DELETE", `/projects/${enc(project)}/previews/${enc(preview)}`)
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 preview = await ctx.management().previews.create(project, { changeSetId: args[0] });
2244
- emit(preview, () => {
2245
- process.stdout.write(`${preview.url}
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: ${preview.fallbackUrl}`);
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 preview = program.command("preview").description("Create and manage preview links");
2383
- preview.command("create").description("Create a preview for a change set or entry").argument("<change-set-or-entry>", "chs_ or ent_ id").action(
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
- preview.command("open").description("Create a preview and open it in the browser").argument("<change-set-or-entry>", "chs_ or ent_ id").action(
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
- preview.command("list").description("List preview tokens for the project").action(
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
- preview.command("revoke").description("Revoke a preview token").argument("<id>", "preview id (prv_)").action(
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.4.1" : "0.0.0-dev";
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";