@myna-sh/cli 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dsl.d.ts +46 -3
- package/dist/dsl.js +20 -0
- package/dist/dsl.js.map +1 -1
- package/dist/main.js +301 -5
- package/dist/main.js.map +1 -1
- package/package.json +22 -15
package/dist/dsl.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* order is significant and preserved as an array; object *keys* are sorted only
|
|
6
6
|
* during canonical stringification for a stable hash.
|
|
7
7
|
*/
|
|
8
|
-
type FieldType = "text" | "number" | "boolean" | "date" | "datetime" | "markdown" | "json" | "asset" | "reference" | "list" | "object" | "slug";
|
|
8
|
+
type FieldType = "text" | "number" | "boolean" | "date" | "datetime" | "markdown" | "richText" | "json" | "asset" | "reference" | "list" | "object" | "blocks" | "slug";
|
|
9
9
|
type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
10
10
|
[key: string]: JsonValue;
|
|
11
11
|
};
|
|
@@ -23,8 +23,16 @@ interface FieldBase {
|
|
|
23
23
|
label: string;
|
|
24
24
|
description?: string;
|
|
25
25
|
required: boolean;
|
|
26
|
+
/** Localized fields store `{ [locale]: value }` keyed by project locale.
|
|
27
|
+
* Only top-level, non-slug fields may be localized. */
|
|
28
|
+
localized?: boolean;
|
|
26
29
|
ui?: FieldUi;
|
|
27
30
|
}
|
|
31
|
+
/** Locale context for entry validation and locale resolution. */
|
|
32
|
+
interface EntryLocaleOptions {
|
|
33
|
+
locales: string[];
|
|
34
|
+
defaultLocale: string;
|
|
35
|
+
}
|
|
28
36
|
interface TextField extends FieldBase {
|
|
29
37
|
type: "text";
|
|
30
38
|
multiline: boolean;
|
|
@@ -122,7 +130,27 @@ interface ObjectField extends FieldBase {
|
|
|
122
130
|
type: "object";
|
|
123
131
|
fields: FieldDef[];
|
|
124
132
|
}
|
|
125
|
-
|
|
133
|
+
/** Semantic rich text stored as a portable JSON document tree — never HTML.
|
|
134
|
+
* The root is `{ "type": "doc", "content": [...] }`; nodes may embed entry
|
|
135
|
+
* references (`ent_` ids) and assets (`ast_` ids). */
|
|
136
|
+
interface RichTextField extends FieldBase {
|
|
137
|
+
type: "richText";
|
|
138
|
+
}
|
|
139
|
+
/** One schema-defined component usable inside a `blocks` field. */
|
|
140
|
+
interface BlockDef {
|
|
141
|
+
key: string;
|
|
142
|
+
label: string;
|
|
143
|
+
fields: FieldDef[];
|
|
144
|
+
}
|
|
145
|
+
/** Heterogeneous, schema-defined component list. Stored as portable JSON:
|
|
146
|
+
* `[{ "type": "<blockKey>", "fields": { ... } }, ...]`. */
|
|
147
|
+
interface BlocksField extends FieldBase {
|
|
148
|
+
type: "blocks";
|
|
149
|
+
blocks: BlockDef[];
|
|
150
|
+
minItems?: number;
|
|
151
|
+
maxItems?: number;
|
|
152
|
+
}
|
|
153
|
+
type FieldDef = TextField | NumberField | BooleanField | DateField | MarkdownField | RichTextField | JsonField | AssetField | ReferenceField | SlugField | ListField | ObjectField | BlocksField;
|
|
126
154
|
type CollectionKind = "collection" | "singleton";
|
|
127
155
|
type Visibility = "public" | "private";
|
|
128
156
|
interface SlugConfig {
|
|
@@ -150,6 +178,7 @@ interface CommonOptions {
|
|
|
150
178
|
label?: string;
|
|
151
179
|
description?: string;
|
|
152
180
|
required?: boolean;
|
|
181
|
+
localized?: boolean;
|
|
153
182
|
ui?: FieldUi;
|
|
154
183
|
}
|
|
155
184
|
interface TextOptions extends CommonOptions {
|
|
@@ -201,6 +230,18 @@ interface ListOptions extends CommonOptions {
|
|
|
201
230
|
interface ObjectOptions extends CommonOptions {
|
|
202
231
|
fields: Record<string, FieldInput>;
|
|
203
232
|
}
|
|
233
|
+
type RichTextOptions = CommonOptions;
|
|
234
|
+
interface BlocksOptions extends CommonOptions {
|
|
235
|
+
/** The block components entries may compose, built with `block()`. */
|
|
236
|
+
allowed: BlockDef[];
|
|
237
|
+
minItems?: number;
|
|
238
|
+
maxItems?: number;
|
|
239
|
+
}
|
|
240
|
+
/** Define a reusable block component for `field.blocks({ allowed: [...] })`. */
|
|
241
|
+
declare function block(key: string, o: {
|
|
242
|
+
label?: string;
|
|
243
|
+
fields: Record<string, FieldInput>;
|
|
244
|
+
}): BlockDef;
|
|
204
245
|
declare const field: {
|
|
205
246
|
readonly text: (o?: TextOptions) => Omit<TextField, "key">;
|
|
206
247
|
readonly number: (o?: NumberOptions) => Omit<NumberField, "key">;
|
|
@@ -214,6 +255,8 @@ declare const field: {
|
|
|
214
255
|
readonly slug: (o?: SlugOptions) => Omit<SlugField, "key">;
|
|
215
256
|
readonly list: (o: ListOptions) => Omit<ListField, "key">;
|
|
216
257
|
readonly object: (o: ObjectOptions) => Omit<ObjectField, "key">;
|
|
258
|
+
readonly richText: (o?: RichTextOptions) => Omit<RichTextField, "key">;
|
|
259
|
+
readonly blocks: (o: BlocksOptions) => Omit<BlocksField, "key">;
|
|
217
260
|
};
|
|
218
261
|
/** Item builders for `field.list({ of: item.<type>() })`. */
|
|
219
262
|
declare const item: {
|
|
@@ -273,4 +316,4 @@ interface CollectionInput {
|
|
|
273
316
|
*/
|
|
274
317
|
declare function collection(input: CollectionInput): CollectionSchema;
|
|
275
318
|
|
|
276
|
-
export { type AssetField, type AssetOptions, type BooleanField, type BooleanOptions, type CollectionInput, type CollectionKind, type CollectionSchema, type DateField, type DateOptions, type FieldDef, type FieldInput, type FieldType, type FieldUi, type JsonField, type JsonOptions, type JsonValue, type ListField, type ListItem, type ListOptions, MAX_NESTING_DEPTH, type MarkdownField, type MarkdownOptions, type NumberField, type NumberOptions, type ObjectField, type ObjectOptions, type PrimitiveItem, type ReferenceField, type ReferenceOptions, type SlugConfig, type SlugField, type SlugOptions, type TextField, type TextOptions, type Visibility, assignKeys, collection, field, item };
|
|
319
|
+
export { type AssetField, type AssetOptions, type BlockDef, type BlocksField, type BlocksOptions, type BooleanField, type BooleanOptions, type CollectionInput, type CollectionKind, type CollectionSchema, type DateField, type DateOptions, type EntryLocaleOptions, type FieldDef, type FieldInput, type FieldType, type FieldUi, type JsonField, type JsonOptions, type JsonValue, type ListField, type ListItem, type ListOptions, MAX_NESTING_DEPTH, type MarkdownField, type MarkdownOptions, type NumberField, type NumberOptions, type ObjectField, type ObjectOptions, type PrimitiveItem, type ReferenceField, type ReferenceOptions, type RichTextField, type RichTextOptions, type SlugConfig, type SlugField, type SlugOptions, type TextField, type TextOptions, type Visibility, assignKeys, block, collection, field, item };
|
package/dist/dsl.js
CHANGED
|
@@ -8,9 +8,13 @@ function common(o) {
|
|
|
8
8
|
};
|
|
9
9
|
if (o.label !== void 0) base.label = o.label;
|
|
10
10
|
if (o.description !== void 0) base.description = o.description;
|
|
11
|
+
if (o.localized !== void 0) base.localized = o.localized;
|
|
11
12
|
if (o.ui !== void 0) base.ui = o.ui;
|
|
12
13
|
return base;
|
|
13
14
|
}
|
|
15
|
+
function block(key, o) {
|
|
16
|
+
return { key, label: o.label ?? key, fields: assignKeys(o.fields) };
|
|
17
|
+
}
|
|
14
18
|
function defined(obj) {
|
|
15
19
|
for (const key of Object.keys(obj)) {
|
|
16
20
|
if (obj[key] === void 0) delete obj[key];
|
|
@@ -119,6 +123,21 @@ var field = {
|
|
|
119
123
|
...common(o),
|
|
120
124
|
fields: assignKeys(o.fields)
|
|
121
125
|
});
|
|
126
|
+
},
|
|
127
|
+
richText(o = {}) {
|
|
128
|
+
return defined({
|
|
129
|
+
type: "richText",
|
|
130
|
+
...common(o)
|
|
131
|
+
});
|
|
132
|
+
},
|
|
133
|
+
blocks(o) {
|
|
134
|
+
return defined({
|
|
135
|
+
type: "blocks",
|
|
136
|
+
...common(o),
|
|
137
|
+
blocks: o.allowed,
|
|
138
|
+
minItems: o.minItems,
|
|
139
|
+
maxItems: o.maxItems
|
|
140
|
+
});
|
|
122
141
|
}
|
|
123
142
|
};
|
|
124
143
|
var item = {
|
|
@@ -182,6 +201,7 @@ function collection(input) {
|
|
|
182
201
|
export {
|
|
183
202
|
MAX_NESTING_DEPTH,
|
|
184
203
|
assignKeys,
|
|
204
|
+
block,
|
|
185
205
|
collection,
|
|
186
206
|
field,
|
|
187
207
|
item
|
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 | \"json\"\n | \"asset\"\n | \"reference\"\n | \"list\"\n | \"object\"\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 ui?: FieldUi;\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\nexport type FieldDef =\n | TextField\n | NumberField\n | BooleanField\n | DateField\n | MarkdownField\n | JsonField\n | AssetField\n | ReferenceField\n | SlugField\n | ListField\n | ObjectField;\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 BooleanField,\n DateField,\n FieldDef,\n FieldUi,\n JsonField,\n JsonValue,\n ListField,\n ListItem,\n MarkdownField,\n NumberField,\n ObjectField,\n ReferenceField,\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 ui?: FieldUi;\n}\n\nfunction common(o: CommonOptions): { label?: string; description?: string; required: boolean; ui?: FieldUi } {\n const base: { label?: string; description?: string; required: 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.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}\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} 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":"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","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 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":"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","names":[]}
|
package/dist/main.js
CHANGED
|
@@ -223,7 +223,9 @@ function renderField(field, depth) {
|
|
|
223
223
|
const optional = field.required ? "" : "?";
|
|
224
224
|
const doc = field.description ? `${indent}/** ${escapeComment(field.description)} */
|
|
225
225
|
` : "";
|
|
226
|
-
|
|
226
|
+
const base = fieldType(field, depth);
|
|
227
|
+
const type = field.localized ? `{ [locale: string]: ${base} }` : base;
|
|
228
|
+
return `${doc}${indent}${safeKey(field.key)}${optional}: ${type};`;
|
|
227
229
|
}
|
|
228
230
|
function fieldType(field, depth) {
|
|
229
231
|
switch (field.type) {
|
|
@@ -250,6 +252,21 @@ function fieldType(field, depth) {
|
|
|
250
252
|
return renderObject(field, depth);
|
|
251
253
|
case "list":
|
|
252
254
|
return `${listItemType(field.item, depth)}[]`;
|
|
255
|
+
case "richText":
|
|
256
|
+
return "RichTextDoc";
|
|
257
|
+
case "blocks": {
|
|
258
|
+
const inner = " ".repeat(depth + 1);
|
|
259
|
+
const options = field.blocks.map((b) => {
|
|
260
|
+
const lines = b.fields.map((f) => renderField(f, depth + 2));
|
|
261
|
+
return `{ type: ${JSON.stringify(b.key)}; fields: {
|
|
262
|
+
${lines.join("\n")}
|
|
263
|
+
${inner}} }`;
|
|
264
|
+
});
|
|
265
|
+
return `Array<
|
|
266
|
+
${inner}${options.join(`
|
|
267
|
+
${inner}| `)}
|
|
268
|
+
${" ".repeat(depth)}>`;
|
|
269
|
+
}
|
|
253
270
|
default:
|
|
254
271
|
return "unknown";
|
|
255
272
|
}
|
|
@@ -291,7 +308,12 @@ ${closingIndent}}`;
|
|
|
291
308
|
return "unknown";
|
|
292
309
|
}
|
|
293
310
|
}
|
|
294
|
-
var JSON_VALUE =
|
|
311
|
+
var JSON_VALUE = [
|
|
312
|
+
"export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };",
|
|
313
|
+
"",
|
|
314
|
+
"/** Portable rich-text document tree (never rendered HTML). */",
|
|
315
|
+
'export interface RichTextDoc { type: "doc"; content: Array<{ type: string; [key: string]: unknown }>; }'
|
|
316
|
+
].join("\n");
|
|
295
317
|
function generateTypesModule(collections) {
|
|
296
318
|
return `${JSON_VALUE}
|
|
297
319
|
|
|
@@ -368,7 +390,12 @@ var ManagementClient = class {
|
|
|
368
390
|
create: (organization, body) => this.mutate("POST", `/organizations/${enc(organization)}/projects`, body),
|
|
369
391
|
get: (project, signal) => this.get(`/projects/${enc(project)}`, void 0, signal),
|
|
370
392
|
update: (project, body) => this.mutate("PATCH", `/projects/${enc(project)}`, body),
|
|
371
|
-
archive: (project) => this.mutate("POST", `/projects/${enc(project)}/archive`)
|
|
393
|
+
archive: (project) => this.mutate("POST", `/projects/${enc(project)}/archive`),
|
|
394
|
+
/** Full project export: schemas, entries (draft + published), asset metadata. */
|
|
395
|
+
export: (project, signal) => this.get(`/projects/${enc(project)}/export`, void 0, signal),
|
|
396
|
+
views: (project, signal) => this.get(`/projects/${enc(project)}/views`, void 0, signal),
|
|
397
|
+
createView: (project, body) => this.mutate("POST", `/projects/${enc(project)}/views`, body),
|
|
398
|
+
deleteView: (project, view) => this.mutate("DELETE", `/projects/${enc(project)}/views/${enc(view)}`)
|
|
372
399
|
};
|
|
373
400
|
// --- Schema ---------------------------------------------------------------
|
|
374
401
|
schema = {
|
|
@@ -380,6 +407,13 @@ var ManagementClient = class {
|
|
|
380
407
|
collections,
|
|
381
408
|
allowDestructive: opts.allowDestructive ?? false,
|
|
382
409
|
changeSummary: opts.changeSummary
|
|
410
|
+
}),
|
|
411
|
+
// Environments as projects
|
|
412
|
+
drift: (project, against, signal) => this.get(`/projects/${enc(project)}/schema/drift`, { against }, signal),
|
|
413
|
+
promote: (project, fromProject, opts = {}) => this.mutate("POST", `/projects/${enc(project)}/schema/promote`, {
|
|
414
|
+
fromProject,
|
|
415
|
+
allowDestructive: opts.allowDestructive ?? false,
|
|
416
|
+
confirm: true
|
|
383
417
|
})
|
|
384
418
|
};
|
|
385
419
|
// --- Entries & revisions --------------------------------------------------
|
|
@@ -394,6 +428,12 @@ var ManagementClient = class {
|
|
|
394
428
|
delete: (project, entry, changeSetId) => this.mutate("DELETE", `/projects/${enc(project)}/entries/${enc(entry)}`, void 0, { changeSetId }),
|
|
395
429
|
unpublish: (project, entry, changeSetId) => this.mutate("POST", `/projects/${enc(project)}/entries/${enc(entry)}/unpublish`, void 0, { changeSetId }),
|
|
396
430
|
restore: (project, entry) => this.mutate("POST", `/projects/${enc(project)}/entries/${enc(entry)}/restore`),
|
|
431
|
+
duplicate: (project, entry, changeSetId) => this.mutate("POST", `/projects/${enc(project)}/entries/${enc(entry)}/duplicate`, {
|
|
432
|
+
changeSetId
|
|
433
|
+
}),
|
|
434
|
+
bulk: (project, body) => this.mutate("POST", `/projects/${enc(project)}/entries/bulk`, body),
|
|
435
|
+
import: (project, body) => this.mutate("POST", `/projects/${enc(project)}/entries/import`, body),
|
|
436
|
+
references: (project, entry, signal) => this.get(`/projects/${enc(project)}/entries/${enc(entry)}/references`, void 0, signal),
|
|
397
437
|
revisions: (project, entry, signal) => this.get(`/projects/${enc(project)}/entries/${enc(entry)}/revisions`, void 0, signal),
|
|
398
438
|
revision: (project, entry, revision, signal) => this.get(`/projects/${enc(project)}/entries/${enc(entry)}/revisions/${enc(revision)}`, void 0, signal),
|
|
399
439
|
restoreRevision: (project, entry, revision) => this.mutate(
|
|
@@ -409,7 +449,48 @@ var ManagementClient = class {
|
|
|
409
449
|
update: (project, changeSet, body) => this.mutate("PATCH", `/projects/${enc(project)}/change-sets/${enc(changeSet)}`, body),
|
|
410
450
|
validate: (project, changeSet) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/validate`),
|
|
411
451
|
publish: (project, changeSet) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/publish`, { confirm: true }),
|
|
412
|
-
close: (project, changeSet) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/close`)
|
|
452
|
+
close: (project, changeSet) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/close`),
|
|
453
|
+
// Scheduled publishing
|
|
454
|
+
schedule: (project, changeSet, publishAt) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/schedule`, {
|
|
455
|
+
publishAt
|
|
456
|
+
}),
|
|
457
|
+
cancelSchedule: (project, changeSet) => this.mutate("DELETE", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/schedule`),
|
|
458
|
+
// Reviews & approvals
|
|
459
|
+
reviews: (project, changeSet, signal) => this.get(
|
|
460
|
+
`/projects/${enc(project)}/change-sets/${enc(changeSet)}/reviews`,
|
|
461
|
+
void 0,
|
|
462
|
+
signal
|
|
463
|
+
),
|
|
464
|
+
requestReview: (project, changeSet, body) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/reviews`, body),
|
|
465
|
+
removeReviewer: (project, changeSet, reviewer) => this.mutate(
|
|
466
|
+
"DELETE",
|
|
467
|
+
`/projects/${enc(project)}/change-sets/${enc(changeSet)}/reviews/${enc(reviewer)}`
|
|
468
|
+
),
|
|
469
|
+
approve: (project, changeSet, body = {}) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/approve`, body),
|
|
470
|
+
requestChanges: (project, changeSet, body = {}) => this.mutate(
|
|
471
|
+
"POST",
|
|
472
|
+
`/projects/${enc(project)}/change-sets/${enc(changeSet)}/request-changes`,
|
|
473
|
+
body
|
|
474
|
+
),
|
|
475
|
+
// Comments
|
|
476
|
+
comments: (project, changeSet, signal) => this.get(
|
|
477
|
+
`/projects/${enc(project)}/change-sets/${enc(changeSet)}/comments`,
|
|
478
|
+
void 0,
|
|
479
|
+
signal
|
|
480
|
+
),
|
|
481
|
+
comment: (project, changeSet, body) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/comments`, body),
|
|
482
|
+
resolveComment: (project, changeSet, comment, resolved = true) => this.mutate(
|
|
483
|
+
"PATCH",
|
|
484
|
+
`/projects/${enc(project)}/change-sets/${enc(changeSet)}/comments/${enc(comment)}`,
|
|
485
|
+
{ resolved }
|
|
486
|
+
),
|
|
487
|
+
// Automated checks
|
|
488
|
+
checks: (project, changeSet, signal) => this.get(
|
|
489
|
+
`/projects/${enc(project)}/change-sets/${enc(changeSet)}/checks`,
|
|
490
|
+
void 0,
|
|
491
|
+
signal
|
|
492
|
+
),
|
|
493
|
+
runChecks: (project, changeSet) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/checks/run`)
|
|
413
494
|
};
|
|
414
495
|
// --- Previews -------------------------------------------------------------
|
|
415
496
|
previews = {
|
|
@@ -427,6 +508,7 @@ var ManagementClient = class {
|
|
|
427
508
|
get: (project, asset, signal) => this.get(`/projects/${enc(project)}/assets/${enc(asset)}`, void 0, signal),
|
|
428
509
|
usage: (project, asset, signal) => this.get(`/projects/${enc(project)}/assets/${enc(asset)}`, { usage: "true" }, signal),
|
|
429
510
|
update: (project, asset, body) => this.mutate("PATCH", `/projects/${enc(project)}/assets/${enc(asset)}`, body),
|
|
511
|
+
replace: (project, asset, body) => this.mutate("POST", `/projects/${enc(project)}/assets/${enc(asset)}/replace`, body),
|
|
430
512
|
delete: (project, asset) => this.mutate("DELETE", `/projects/${enc(project)}/assets/${enc(asset)}`),
|
|
431
513
|
/** Full presigned upload flow: create → PUT bytes → complete. */
|
|
432
514
|
upload: (project, input, meta = {}) => this.uploadAsset(project, input, meta)
|
|
@@ -1407,6 +1489,35 @@ function registerSchema(program) {
|
|
|
1407
1489
|
emit(result, () => renderDiff(result));
|
|
1408
1490
|
})
|
|
1409
1491
|
);
|
|
1492
|
+
schema.command("drift").description("Show schema drift between this project and another (e.g. staging)").requiredOption("--against <project>", "project id or slug to compare with").action(
|
|
1493
|
+
handle(async (ctx, _args, opts) => {
|
|
1494
|
+
const project = ctx.requireProject();
|
|
1495
|
+
const result = await ctx.management().schema.drift(project, opts.against);
|
|
1496
|
+
emit(result, () => {
|
|
1497
|
+
if (result.inSync) diag("Schemas are in sync.");
|
|
1498
|
+
else {
|
|
1499
|
+
diag(`${result.ops.length} op(s) of drift (${result.classification}):`);
|
|
1500
|
+
for (const op of result.ops) {
|
|
1501
|
+
process.stderr.write(` ${op.kind} ${op.collection}${op.field ? `.${op.field}` : ""} \u2014 ${op.detail}
|
|
1502
|
+
`);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
});
|
|
1506
|
+
if (!result.inSync) process.exitCode = 1;
|
|
1507
|
+
})
|
|
1508
|
+
);
|
|
1509
|
+
schema.command("promote").description("Promote another project's deployed schemas into this project").requiredOption("--from <project>", "source project id or slug").option("--allow-destructive", "permit destructive schema changes", false).action(
|
|
1510
|
+
handle(async (ctx, _args, opts) => {
|
|
1511
|
+
const project = ctx.requireProject();
|
|
1512
|
+
const result = await ctx.management().schema.promote(project, opts.from, {
|
|
1513
|
+
allowDestructive: Boolean(opts.allowDestructive)
|
|
1514
|
+
});
|
|
1515
|
+
emit(result, () => {
|
|
1516
|
+
renderDiff(result.diff);
|
|
1517
|
+
diag(result.applied ? `Promoted ${result.versions.length} version(s).` : "Nothing to promote.");
|
|
1518
|
+
});
|
|
1519
|
+
})
|
|
1520
|
+
);
|
|
1410
1521
|
schema.command("push").description("Apply local schemas, creating immutable versions").option("--schema-dir <dir>", "schema directory").option("--allow-destructive", "permit destructive schema changes", false).option("--summary <text>", "change summary").action(
|
|
1411
1522
|
handle(async (ctx, _args, opts) => {
|
|
1412
1523
|
const project = ctx.requireProject();
|
|
@@ -1480,6 +1591,7 @@ function renderDiff(diff) {
|
|
|
1480
1591
|
}
|
|
1481
1592
|
|
|
1482
1593
|
// src/commands/entries.ts
|
|
1594
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
1483
1595
|
function registerEntries(program) {
|
|
1484
1596
|
const entries = program.command("entries").description("Create, read, update, and manage entries");
|
|
1485
1597
|
entries.command("list").description("List entries in a collection").argument("<collection>", "collection key").option("--status <status>", "filter by status").option("--limit <n>", "page size", "25").option("--cursor <cursor>", "pagination cursor").action(
|
|
@@ -1578,6 +1690,81 @@ function registerEntries(program) {
|
|
|
1578
1690
|
);
|
|
1579
1691
|
})
|
|
1580
1692
|
);
|
|
1693
|
+
entries.command("duplicate").description("Duplicate an entry into a new draft").argument("<ref>", "collection/slug or entry id").option("--change-set <id>", "stage on an explicit change set").action(
|
|
1694
|
+
handle(async (ctx, args, opts) => {
|
|
1695
|
+
const project = ctx.requireProject();
|
|
1696
|
+
const id = await resolveEntryId(ctx.management(), project, args[0]);
|
|
1697
|
+
const entry = await ctx.management().entries.duplicate(project, id, opts.changeSet);
|
|
1698
|
+
emit(entry, () => diag(`Duplicated into ${entry.id} (${entry.slug ?? "no slug"}).`));
|
|
1699
|
+
})
|
|
1700
|
+
);
|
|
1701
|
+
entries.command("bulk").description("Stage a delete or unpublish for many entries at once").requiredOption("--action <action>", "delete or unpublish").requiredOption("--ids <ids>", "comma-separated entry ids").option("--change-set <id>", "stage on an explicit change set").action(
|
|
1702
|
+
handle(async (ctx, _args, opts) => {
|
|
1703
|
+
const action = opts.action;
|
|
1704
|
+
if (action !== "delete" && action !== "unpublish") {
|
|
1705
|
+
throw new UsageError("--action must be delete or unpublish.");
|
|
1706
|
+
}
|
|
1707
|
+
const project = ctx.requireProject();
|
|
1708
|
+
const result = await ctx.management().entries.bulk(project, {
|
|
1709
|
+
action,
|
|
1710
|
+
entryIds: opts.ids.split(",").map((s) => s.trim()).filter(Boolean),
|
|
1711
|
+
changeSetId: opts.changeSet
|
|
1712
|
+
});
|
|
1713
|
+
emit(
|
|
1714
|
+
result,
|
|
1715
|
+
() => table(result.results, [
|
|
1716
|
+
{ header: "ENTRY", value: (r) => r.entryId },
|
|
1717
|
+
{ header: "OK", value: (r) => r.ok ? "yes" : "no" },
|
|
1718
|
+
{ header: "CHANGE SET", value: (r) => r.changeSetId ?? "\u2014" },
|
|
1719
|
+
{ header: "ERROR", value: (r) => r.error ?? "" }
|
|
1720
|
+
])
|
|
1721
|
+
);
|
|
1722
|
+
if (result.results.some((r) => !r.ok)) process.exitCode = 1;
|
|
1723
|
+
})
|
|
1724
|
+
);
|
|
1725
|
+
entries.command("import").description("Import entries from a JSON file, with dry-run validation").argument("<file>", 'JSON file: { "collection": "posts", "entries": [{ "slug"?, "data" }] }').option("--dry-run", "validate without creating anything", false).action(
|
|
1726
|
+
handle(async (ctx, args, opts) => {
|
|
1727
|
+
const project = ctx.requireProject();
|
|
1728
|
+
const raw = JSON.parse(await readFile2(args[0], "utf8"));
|
|
1729
|
+
const result = await ctx.management().entries.import(project, {
|
|
1730
|
+
collection: raw.collection,
|
|
1731
|
+
entries: raw.entries,
|
|
1732
|
+
dryRun: Boolean(opts.dryRun)
|
|
1733
|
+
});
|
|
1734
|
+
emit(result, () => {
|
|
1735
|
+
diag(
|
|
1736
|
+
result.dryRun ? `Dry run: ${result.valid ? "all rows valid" : "validation failed"}.` : `Imported ${result.results.filter((r) => r.ok).length}/${result.results.length} into change set ${result.changeSetId}.`
|
|
1737
|
+
);
|
|
1738
|
+
for (const row of result.results.filter((r) => !r.ok)) {
|
|
1739
|
+
for (const e of row.errors ?? []) {
|
|
1740
|
+
process.stderr.write(` row ${row.index}${row.slug ? ` (${row.slug})` : ""}: ${e.path} ${e.message}
|
|
1741
|
+
`);
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
});
|
|
1745
|
+
if (!result.valid) process.exitCode = 1;
|
|
1746
|
+
})
|
|
1747
|
+
);
|
|
1748
|
+
entries.command("references").description("Show which entries reference this one and what it references").argument("<ref>", "collection/slug or entry id").action(
|
|
1749
|
+
handle(async (ctx, args) => {
|
|
1750
|
+
const project = ctx.requireProject();
|
|
1751
|
+
const id = await resolveEntryId(ctx.management(), project, args[0]);
|
|
1752
|
+
const refs = await ctx.management().entries.references(project, id);
|
|
1753
|
+
emit(refs, () => {
|
|
1754
|
+
diag(`Referenced by ${refs.referencedBy.length} entr(y/ies):`);
|
|
1755
|
+
table(refs.referencedBy, [
|
|
1756
|
+
{ header: "ENTRY", value: (r) => `${r.collectionKey}/${r.slug ?? r.id}` },
|
|
1757
|
+
{ header: "IN DRAFT", value: (r) => r.inDraft ? "yes" : "no" },
|
|
1758
|
+
{ header: "IN PUBLISHED", value: (r) => r.inPublished ? "yes" : "no" }
|
|
1759
|
+
]);
|
|
1760
|
+
diag(`References ${refs.references.length} entr(y/ies):`);
|
|
1761
|
+
table(refs.references, [
|
|
1762
|
+
{ header: "ENTRY", value: (r) => r.collectionKey ? `${r.collectionKey}/${r.slug ?? r.id}` : r.id },
|
|
1763
|
+
{ header: "STATE", value: (r) => !r.exists ? "missing" : r.published ? "published" : "unpublished" }
|
|
1764
|
+
]);
|
|
1765
|
+
});
|
|
1766
|
+
})
|
|
1767
|
+
);
|
|
1581
1768
|
entries.command("restore").description("Restore a historical revision into a new draft").argument("<ref>", "collection/slug or entry id").requiredOption("--revision <id>", "revision id to restore").action(
|
|
1582
1769
|
handle(async (ctx, args, opts) => {
|
|
1583
1770
|
const project = ctx.requireProject();
|
|
@@ -1682,6 +1869,107 @@ function registerChanges(program) {
|
|
|
1682
1869
|
emit(cs, () => diag(`Closed ${cs.id}.`));
|
|
1683
1870
|
})
|
|
1684
1871
|
);
|
|
1872
|
+
changes.command("schedule").description("Schedule, reschedule, or cancel a change set's publish").argument("<id>", "change set id").option("--at <datetime>", "ISO 8601 datetime to publish at").option("--cancel", "cancel the scheduled publish", false).action(
|
|
1873
|
+
handle(async (ctx, args, opts) => {
|
|
1874
|
+
const project = ctx.requireProject();
|
|
1875
|
+
if (Boolean(opts.at) === Boolean(opts.cancel)) {
|
|
1876
|
+
throw new UsageError("Provide exactly one of --at <datetime> or --cancel.");
|
|
1877
|
+
}
|
|
1878
|
+
const client = ctx.management();
|
|
1879
|
+
const cs = opts.cancel ? await client.changeSets.cancelSchedule(project, args[0]) : await client.changeSets.schedule(project, args[0], new Date(opts.at).toISOString());
|
|
1880
|
+
emit(
|
|
1881
|
+
cs,
|
|
1882
|
+
() => diag(cs.scheduledAt ? `Scheduled to publish at ${cs.scheduledAt}.` : "Schedule cancelled.")
|
|
1883
|
+
);
|
|
1884
|
+
})
|
|
1885
|
+
);
|
|
1886
|
+
changes.command("reviews").description("List reviews and approvals for a change set").argument("<id>", "change set id").action(
|
|
1887
|
+
handle(async (ctx, args) => {
|
|
1888
|
+
const project = ctx.requireProject();
|
|
1889
|
+
const reviews = await ctx.management().changeSets.reviews(project, args[0]);
|
|
1890
|
+
emit(
|
|
1891
|
+
reviews,
|
|
1892
|
+
() => table(reviews, [
|
|
1893
|
+
{ header: "REVIEWER", value: (r) => `${r.reviewerType}:${r.reviewerId}` },
|
|
1894
|
+
{ header: "STATUS", value: (r) => r.status },
|
|
1895
|
+
{ header: "STALE", value: (r) => r.stale ? "yes" : "no" },
|
|
1896
|
+
{ header: "DECIDED", value: (r) => r.decidedAt ?? "\u2014" }
|
|
1897
|
+
])
|
|
1898
|
+
);
|
|
1899
|
+
})
|
|
1900
|
+
);
|
|
1901
|
+
changes.command("request-review").description("Assign a reviewer to a change set").argument("<id>", "change set id").requiredOption("--reviewer <id>", "reviewer id (user, api key, or agent id)").option("--reviewer-type <type>", "reviewer actor type", "user").action(
|
|
1902
|
+
handle(async (ctx, args, opts) => {
|
|
1903
|
+
const project = ctx.requireProject();
|
|
1904
|
+
const reviews = await ctx.management().changeSets.requestReview(project, args[0], {
|
|
1905
|
+
reviewerType: opts.reviewerType,
|
|
1906
|
+
reviewerId: opts.reviewer
|
|
1907
|
+
});
|
|
1908
|
+
emit(reviews, () => diag(`Requested review from ${opts.reviewer}.`));
|
|
1909
|
+
})
|
|
1910
|
+
);
|
|
1911
|
+
changes.command("approve").description("Approve a change set").argument("<id>", "change set id").option("--comment <text>", "optional review comment").action(
|
|
1912
|
+
handle(async (ctx, args, opts) => {
|
|
1913
|
+
const project = ctx.requireProject();
|
|
1914
|
+
const reviews = await ctx.management().changeSets.approve(project, args[0], {
|
|
1915
|
+
comment: opts.comment
|
|
1916
|
+
});
|
|
1917
|
+
emit(reviews, () => diag("Approved."));
|
|
1918
|
+
})
|
|
1919
|
+
);
|
|
1920
|
+
changes.command("request-changes").description("Request changes on a change set").argument("<id>", "change set id").option("--comment <text>", "optional review comment").action(
|
|
1921
|
+
handle(async (ctx, args, opts) => {
|
|
1922
|
+
const project = ctx.requireProject();
|
|
1923
|
+
const reviews = await ctx.management().changeSets.requestChanges(project, args[0], {
|
|
1924
|
+
comment: opts.comment
|
|
1925
|
+
});
|
|
1926
|
+
emit(reviews, () => diag("Requested changes."));
|
|
1927
|
+
})
|
|
1928
|
+
);
|
|
1929
|
+
changes.command("comments").description("List comments on a change set").argument("<id>", "change set id").action(
|
|
1930
|
+
handle(async (ctx, args) => {
|
|
1931
|
+
const project = ctx.requireProject();
|
|
1932
|
+
const comments = await ctx.management().changeSets.comments(project, args[0]);
|
|
1933
|
+
emit(
|
|
1934
|
+
comments,
|
|
1935
|
+
() => table(comments, [
|
|
1936
|
+
{ header: "AUTHOR", value: (c) => `${c.authorType}:${c.authorId ?? "\u2014"}` },
|
|
1937
|
+
{ header: "ANCHOR", value: (c) => c.resourceId ? `${c.resourceId}${c.fieldPath ? `#${c.fieldPath}` : ""}` : "\u2014" },
|
|
1938
|
+
{ header: "RESOLVED", value: (c) => c.resolvedAt ? "yes" : "no" },
|
|
1939
|
+
{ header: "BODY", value: (c) => c.body.length > 60 ? `${c.body.slice(0, 57)}...` : c.body }
|
|
1940
|
+
])
|
|
1941
|
+
);
|
|
1942
|
+
})
|
|
1943
|
+
);
|
|
1944
|
+
changes.command("comment").description("Comment on a change set (optionally anchored to a field)").argument("<id>", "change set id").argument("<body>", "comment body").option("--entry <id>", "anchor to an entry in the change set").option("--field <path>", "anchor to a field path, e.g. fields.title").action(
|
|
1945
|
+
handle(async (ctx, args, opts) => {
|
|
1946
|
+
const project = ctx.requireProject();
|
|
1947
|
+
const comment = await ctx.management().changeSets.comment(project, args[0], {
|
|
1948
|
+
body: args[1],
|
|
1949
|
+
resourceType: opts.entry ? "entry" : void 0,
|
|
1950
|
+
resourceId: opts.entry,
|
|
1951
|
+
fieldPath: opts.field
|
|
1952
|
+
});
|
|
1953
|
+
emit(comment, () => diag(`Commented ${comment.id}.`));
|
|
1954
|
+
})
|
|
1955
|
+
);
|
|
1956
|
+
changes.command("checks").description("Show the latest check run for a change set").argument("<id>", "change set id").option("--run", "run checks before showing results", false).action(
|
|
1957
|
+
handle(async (ctx, args, opts) => {
|
|
1958
|
+
const project = ctx.requireProject();
|
|
1959
|
+
const client = ctx.management();
|
|
1960
|
+
const checks = opts.run ? (await client.changeSets.runChecks(project, args[0])).checks : await client.changeSets.checks(project, args[0]);
|
|
1961
|
+
emit(
|
|
1962
|
+
checks,
|
|
1963
|
+
() => table(checks, [
|
|
1964
|
+
{ header: "CHECK", value: (c) => c.name },
|
|
1965
|
+
{ header: "STATUS", value: (c) => c.status },
|
|
1966
|
+
{ header: "STALE", value: (c) => c.stale ? "yes" : "no" },
|
|
1967
|
+
{ header: "ISSUES", value: (c) => String(c.details?.length ?? 0) }
|
|
1968
|
+
])
|
|
1969
|
+
);
|
|
1970
|
+
if (checks.some((c) => c.status === "failed")) process.exitCode = 1;
|
|
1971
|
+
})
|
|
1972
|
+
);
|
|
1685
1973
|
}
|
|
1686
1974
|
|
|
1687
1975
|
// src/commands/previews.ts
|
|
@@ -1893,6 +2181,14 @@ function registerProjects(program) {
|
|
|
1893
2181
|
);
|
|
1894
2182
|
})
|
|
1895
2183
|
);
|
|
2184
|
+
projects.command("export").description("Export the full project (schemas, entries, asset metadata) as JSON").argument("[project]", "project id or slug").action(
|
|
2185
|
+
handle(async (ctx, args) => {
|
|
2186
|
+
const ref = args[0] ?? ctx.requireProject();
|
|
2187
|
+
const data = await ctx.management().projects.export(ref);
|
|
2188
|
+
process.stdout.write(`${JSON.stringify(data, null, 2)}
|
|
2189
|
+
`);
|
|
2190
|
+
})
|
|
2191
|
+
);
|
|
1896
2192
|
projects.command("update").description("Update project settings").argument("[project]", "project id or slug").option("--name <name>", "project name").option("--timezone <timezone>", "IANA timezone").option("--public-api <state>", "public API state (on|off)").option("--default-preview-template <template>", "default preview URL template").option("--clear-default-preview-template", "remove the default preview URL template").option("--origins <urls>", "comma-separated allowed origins").option("--clear-origins", "remove all allowed origins").action(
|
|
1897
2193
|
handle(async (ctx, args, opts) => {
|
|
1898
2194
|
const ref = args[0] ?? ctx.requireProject();
|
|
@@ -2167,7 +2463,7 @@ function registerBilling(program) {
|
|
|
2167
2463
|
}
|
|
2168
2464
|
|
|
2169
2465
|
// src/main.ts
|
|
2170
|
-
var VERSION = "0.
|
|
2466
|
+
var VERSION = "0.2.0";
|
|
2171
2467
|
var GLOBAL_VALUE_FLAGS = /* @__PURE__ */ new Set(["--project", "--organization", "--token", "--api-url"]);
|
|
2172
2468
|
var GLOBAL_BOOL_FLAGS = /* @__PURE__ */ new Set(["--json", "--no-interactive", "--interactive"]);
|
|
2173
2469
|
function normalizeGlobals(argv) {
|