@vxil/cli 0.12.0 → 0.13.1
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/_vxil-feature-configs-apiState.d.ts +2 -2
- package/dist/_vxil-feature-configs-hooks.d.ts +2 -2
- package/dist/_vxil-feature-configs-readmodels.d.ts +1 -1
- package/dist/_vxil-feature-configs.d.ts +16 -16
- package/dist/config.d.ts +31 -17
- package/dist/config.js +21 -0
- package/dist/vxil.js +276 -579
- package/package.json +4 -4
|
@@ -166,11 +166,11 @@ export interface ApiStateResponseField extends ApiStateSummary {
|
|
|
166
166
|
}
|
|
167
167
|
/** The note every executor reports for a DISABLED feature whose block still
|
|
168
168
|
* declares rows: the declaration is kept in the manifest and nothing is
|
|
169
|
-
* converged (a parked feature is not a propagation window
|
|
169
|
+
* converged (a parked feature is not a propagation window). */
|
|
170
170
|
export declare const API_STATE_DISABLED_NOTE = "feature disabled \u2014 declaration kept, not converged";
|
|
171
171
|
/** `GET /v1/rate-limits/policies` is a 100-row HARD cap with no cursor. A list
|
|
172
172
|
* AT the cap is not a complete live set, so planning creates against it would
|
|
173
|
-
* re-POST every declared name the cap hid
|
|
173
|
+
* re-POST every declared name the cap hid. The server's
|
|
174
174
|
* converge and the CLI's read-only plan refuse with this message instead. */
|
|
175
175
|
export declare function rlPolicyListCapError(liveCount: number): string | null;
|
|
176
176
|
export declare const RL_POLICY_LIST_CAP = 100;
|
|
@@ -69,7 +69,7 @@ export declare function parseExpr(src: string): Node;
|
|
|
69
69
|
/** Walk the AST and reject anything outside the allow-list, plus depth bounds.
|
|
70
70
|
* Returns an array of human-readable errors (empty = safe). Pure, no eval. */
|
|
71
71
|
export declare function validateAst(root: Node): string[];
|
|
72
|
-
/** The VERIFIED caller/session context for read hooks (
|
|
72
|
+
/** The VERIFIED caller/session context for read hooks (guide ch. 7). Read-only;
|
|
73
73
|
* present only on the read path in end-user mode. In server mode / write path
|
|
74
74
|
* the whole object is null. */
|
|
75
75
|
export interface HookCaller {
|
|
@@ -142,7 +142,7 @@ export interface ReadHookResult {
|
|
|
142
142
|
* HookEvalError (→ a clean 422, never a pinned isolate). Write events never
|
|
143
143
|
* fire here, and read events never fire in runWriteHooks. */
|
|
144
144
|
export declare function runReadHooks(hooks: Record<string, HookDef> | undefined, collection: string, rows: ReadonlyArray<Record<string, unknown>>, now: string,
|
|
145
|
-
/** verified caller/session context (
|
|
145
|
+
/** verified caller/session context (guide ch. 7) — read-only, exposed to
|
|
146
146
|
* expressions as `caller.endUserId` / `caller.principal`. Omitted ⇒ null
|
|
147
147
|
* (server-caller mode). */
|
|
148
148
|
caller?: HookCaller | null): ReadHookResult;
|
|
@@ -9,7 +9,7 @@ export declare const READ_MODEL_LIMITS: {
|
|
|
9
9
|
/**
|
|
10
10
|
* Structural validation of a declared read-model `spec` — shared by the
|
|
11
11
|
* config-write gate here and re-exported for anyone needing the pure check.
|
|
12
|
-
* `kind` selects the
|
|
12
|
+
* `kind` selects the aggregate or the rank grammar (guide ch. 4).
|
|
13
13
|
*/
|
|
14
14
|
export declare function validateReadModelSpecShape(where: string, kind: 'aggregate' | 'rank', spec: unknown): string[];
|
|
15
15
|
/** Shape of one declared read-model (mirrors the CmsConfigSchema Record). */
|
|
@@ -4,7 +4,7 @@ export * from './_vxil-feature-configs-readmodels.js';
|
|
|
4
4
|
export * from './_vxil-feature-configs-apiState.js';
|
|
5
5
|
export * from './_vxil-feature-configs-canonicalJson.js';
|
|
6
6
|
export declare const RESERVED_CREDIT_TYPES: ReadonlySet<string>;
|
|
7
|
-
/**
|
|
7
|
+
/** A function binding's `retry.maxAttempts` ceiling, and the
|
|
8
8
|
* binding kinds that may carry `retry` — the platform-delivered event lanes
|
|
9
9
|
* (an http invoke returns its own status; a cron tick's retry would overlap
|
|
10
10
|
* the next tick). Read by the schema, the deploy clamp and the CLI. */
|
|
@@ -20,14 +20,14 @@ export declare const NOTIFICATION_TEMPLATE_PLACEHOLDERS: Readonly<Record<string,
|
|
|
20
20
|
* exist and renders EMPTY. Rejected in `subject` for exactly the reason the
|
|
21
21
|
* whole allow-list exists: a silent empty render in a live email. */
|
|
22
22
|
export declare const NOTIFICATION_TEMPLATE_SLOT_PLACEHOLDERS: Readonly<Record<string, readonly string[]>>;
|
|
23
|
-
/** Per-override caps
|
|
23
|
+
/** Per-override caps. Bodies are emails, not documents. */
|
|
24
24
|
export declare const NOTIF_OVERRIDE_SUBJECT_MAX = 500;
|
|
25
25
|
export declare const NOTIF_OVERRIDE_BODY_MAX = 20000;
|
|
26
26
|
/** Whole-map caps: keeps one manifest (and the KV row every send reads) small. */
|
|
27
27
|
export declare const NOTIF_OVERRIDE_MAX_LOCALES_PER_TEMPLATE = 20;
|
|
28
28
|
export declare const NOTIF_OVERRIDE_MAX_BYTES: number;
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
30
|
+
* Validate `templates.overrides` at CONFIG-WRITE time. Returns [] when the
|
|
31
31
|
* bag is absent or clean. Every rule fails LOUD rather than shipping something
|
|
32
32
|
* that silently renders wrong in a customer's inbox:
|
|
33
33
|
* • overrides present with `allowOverride:false` → rejected (never inert);
|
|
@@ -91,10 +91,10 @@ export declare const NotificationsConfigSchema: import("@sinclair/typebox").TObj
|
|
|
91
91
|
}>>;
|
|
92
92
|
/** in-app inbox channel (send with channel: 'inbox' | 'both') */
|
|
93
93
|
inboxEnabled: import("@sinclair/typebox").TBoolean;
|
|
94
|
-
/**
|
|
94
|
+
/** Broadcast campaigns channel. Optional bag (= 1 leaf): absent means
|
|
95
95
|
* disabled; per-campaign quiet_hours / freq_cap overrides live on the
|
|
96
96
|
* notifications.campaigns ROW (tenant data), not here. Folded into the
|
|
97
|
-
* canonical schema 2026-07-18 (
|
|
97
|
+
* canonical schema 2026-07-18 (Value.Clean previously STRIPPED the
|
|
98
98
|
* worker-local extension, so campaigns 403'd via the real config path). */
|
|
99
99
|
broadcast: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
100
100
|
enabled: import("@sinclair/typebox").TBoolean;
|
|
@@ -114,9 +114,9 @@ export declare const NotificationsConfigSchema: import("@sinclair/typebox").TObj
|
|
|
114
114
|
}>>;
|
|
115
115
|
}>;
|
|
116
116
|
export type NotificationsConfig = Static<typeof NotificationsConfigSchema>;
|
|
117
|
-
/** The
|
|
117
|
+
/** The broadcast bag as persisted (present ⇒ leaf defaults applied). */
|
|
118
118
|
export type BroadcastConfig = NonNullable<NotificationsConfig['broadcast']>;
|
|
119
|
-
/** Generation-lifecycle config defaults (
|
|
119
|
+
/** Generation-lifecycle config defaults (guide ch. 6, jobs). */
|
|
120
120
|
export declare const GENERATION_DEFAULTS: {
|
|
121
121
|
/** per-tenant in-flight generation cap (separate budget from queue jobs) */
|
|
122
122
|
readonly maxConcurrent: 20;
|
|
@@ -189,14 +189,14 @@ export declare const JobsConfigSchema: import("@sinclair/typebox").TObject<{
|
|
|
189
189
|
maxTimeoutMs: import("@sinclair/typebox").TInteger;
|
|
190
190
|
/** poll-mode: how many poll cycles before giving up (→ terminal-fail) */
|
|
191
191
|
pollMaxAttempts: import("@sinclair/typebox").TInteger;
|
|
192
|
-
/** MANDATORY per-hold cap on a generation `reserve_credits.amount`
|
|
193
|
-
*
|
|
192
|
+
/** MANDATORY per-hold cap on a generation `reserve_credits.amount`
|
|
193
|
+
* (guide ch. 6, jobs). Every requested amount is CLAMPED to this (never rejected) — a
|
|
194
194
|
* conservative default so an untrusted deployed function that carries a
|
|
195
195
|
* reserve block can never hold more than a bounded amount per run without
|
|
196
196
|
* any tenant action. */
|
|
197
197
|
maxReserveCredits: import("@sinclair/typebox").TInteger;
|
|
198
198
|
/** MANDATORY per-tenant ceiling on the SUM of un-settled provisional
|
|
199
|
-
* reserve holds across all in-flight generation runs (
|
|
199
|
+
* reserve holds across all in-flight generation runs (guide ch. 6, jobs): a
|
|
200
200
|
* reserve whose amount would push the tenant's outstanding-holds total over
|
|
201
201
|
* this is rejected 429, so a runaway function cannot hold every user at
|
|
202
202
|
* once. Defaulted so no tenant action is required to be safe. */
|
|
@@ -318,7 +318,7 @@ export declare const AuthConfigSchema: import("@sinclair/typebox").TObject<{
|
|
|
318
318
|
}>;
|
|
319
319
|
/** The security bag as persisted (present ⇒ leaf defaults applied). */
|
|
320
320
|
export type AuthSecurityConfig = NonNullable<Static<typeof AuthConfigSchema>['security']>;
|
|
321
|
-
/** The
|
|
321
|
+
/** The generic OIDC / SSO issuer block (`providers.oidc`). */
|
|
322
322
|
export type AuthOidcConfig = Static<typeof OidcProviderSchema>;
|
|
323
323
|
export type AuthConfig = Static<typeof AuthConfigSchema>;
|
|
324
324
|
export declare const RL_POLICY_NAME_PATTERN = "^[a-zA-Z0-9_.:-]+$";
|
|
@@ -789,11 +789,11 @@ export declare const CopilotConfigSchema: import("@sinclair/typebox").TObject<{
|
|
|
789
789
|
export type CopilotConfig = Static<typeof CopilotConfigSchema>;
|
|
790
790
|
export declare const FEATURE_SCHEMAS: Record<string, TSchema>;
|
|
791
791
|
export declare const CONFIG_FLAG_CAP = 15;
|
|
792
|
-
/** Counts leaf flags in a TypeBox object schema
|
|
793
|
-
* Per
|
|
792
|
+
/** Counts leaf flags in a TypeBox object schema.
|
|
793
|
+
* Per guide ch. 6, auth: an OPTIONAL object (e.g. a provider credential
|
|
794
794
|
* block) counts as ONE flag — the tenant's decision is "configure it or
|
|
795
795
|
* not", not each inner ref. */
|
|
796
|
-
/** The auth lifecycle events an `authHook` binding may name
|
|
796
|
+
/** The auth lifecycle events an `authHook` binding may name — the
|
|
797
797
|
* closed union `packages/config` types as AuthHookEvent; the control-plane
|
|
798
798
|
* reconciler maps each to its `auth.<event>` audit-event prefix. */
|
|
799
799
|
export declare const AUTH_HOOK_EVENTS: readonly ["user.created", "session.created", "session.revoked", "signin.failure"];
|
|
@@ -805,13 +805,13 @@ export type AuthHookEvent = (typeof AUTH_HOOK_EVENTS)[number];
|
|
|
805
805
|
* else is refused at config write (E-CMSHOOK, 2026-10-01): an unknown name used
|
|
806
806
|
* to fall back to beforeWrite at delivery, so a typo like 'afterCreate' or
|
|
807
807
|
* 'beforeDelete' silently subscribed the function to creates AND updates.
|
|
808
|
-
*
|
|
808
|
+
* A CI gate pins every copy to this list. */
|
|
809
809
|
export declare const CMS_HOOK_EVENTS: readonly ["beforeCreate", "beforeUpdate", "beforeWrite"];
|
|
810
810
|
export type CmsHookEvent = (typeof CMS_HOOK_EVENTS)[number];
|
|
811
811
|
/** The ONE refusal text for an unknown cmsHook binding event (the config
|
|
812
812
|
* validator and the deploy route's early 422 both use it). */
|
|
813
813
|
export declare function cmsHookEventError(event: unknown): string | null;
|
|
814
|
-
/**
|
|
814
|
+
/** The OTP test-recipient entry grammar (shared by the validator and auth-v1's
|
|
815
815
|
* matcher): an exact email, a `*@domain` glob, or a +E.164 phone number. */
|
|
816
816
|
export declare const TEST_RECIPIENT_EMAIL_RE: RegExp;
|
|
817
817
|
export declare const TEST_RECIPIENT_GLOB_RE: RegExp;
|
package/dist/config.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export interface FieldDef {
|
|
|
14
14
|
relationTo?: string;
|
|
15
15
|
computed?: boolean;
|
|
16
16
|
compute?: unknown;
|
|
17
|
-
/** Claims-table-enforced value uniqueness across live items (
|
|
17
|
+
/** Claims-table-enforced value uniqueness across live items (guide ch. 4):
|
|
18
18
|
* a duplicate write is a clean 409 unique_violation. Scalar types only
|
|
19
19
|
* (string/int/float/datetime/relation/file). Carried on collection/field
|
|
20
20
|
* CREATE by `vxil push` and server apply; since 2026-07-17 `vxil push` also
|
|
@@ -22,12 +22,12 @@ export interface FieldDef {
|
|
|
22
22
|
* field re-add — packages/cli/src/cms.ts FIELD_ATTRS); attributes push
|
|
23
23
|
* cannot reconcile surface as a visible plan warning, never a silent no-op. */
|
|
24
24
|
unique?: boolean;
|
|
25
|
-
/** Per-relation-field delete behavior (
|
|
26
|
-
* when the referenced item is deleted — or `restrict
|
|
25
|
+
/** Per-relation-field delete behavior (guide ch. 4): bounded, atomic fan-out
|
|
26
|
+
* when the referenced item is deleted — or `restrict`, which REFUSES
|
|
27
27
|
* the delete with 409 `referenced` while a live reference exists. Same
|
|
28
28
|
* carriage + reconcile behavior as `unique`. */
|
|
29
29
|
onDelete?: 'cascade' | 'set_null' | 'restrict';
|
|
30
|
-
/** FIELD-LEVEL READ SECURITY (
|
|
30
|
+
/** FIELD-LEVEL READ SECURITY (guide ch. 4): the end-user ORG ROLE slugs allowed
|
|
31
31
|
* to READ this field. Omitted/`[]` = ungated (every caller sees it — today's
|
|
32
32
|
* behavior). A non-empty list is FAIL-SAFE: in VERIFIED end-user mode the
|
|
33
33
|
* field is OMITTED from every read (get / list / query / `$expand` at any
|
|
@@ -40,8 +40,23 @@ export interface FieldDef {
|
|
|
40
40
|
* `orgs` role alphabet). The gate
|
|
41
41
|
* never affects WRITES. Carried by BOTH push paths and alterable in place. */
|
|
42
42
|
readRoles?: string[];
|
|
43
|
+
/** EQUALITY INDEX for an UNSLOTTED field (guide ch. 4 "Indexed equality"):
|
|
44
|
+
* `=` / `$eq` / `$in` filters on it are index-served instead of a bounded
|
|
45
|
+
* scan — results are identical either way. At most 4 per collection (the
|
|
46
|
+
* equality-key budget, separate from the 8 index slots); not together with
|
|
47
|
+
* `indexSlot` (a slot already indexes equality) and not on a computed field
|
|
48
|
+
* (both checked here at define time and again by the server). Carried by
|
|
49
|
+
* BOTH push paths and alterable in place; on a collection over 2,000 live
|
|
50
|
+
* items `vxil push` also runs the re-index that arms it. */
|
|
51
|
+
indexed?: boolean;
|
|
43
52
|
}
|
|
44
|
-
/**
|
|
53
|
+
/** The per-collection budget of `indexed: true` fields (cms/0158: e1…e4). */
|
|
54
|
+
export declare const CMS_INDEXED_FIELD_BUDGET = 4;
|
|
55
|
+
/** Define-time checks of a collection's `indexed` fields — the same refusals
|
|
56
|
+
* the server makes (422), so a config fails at `defineConfig` / `collection()`
|
|
57
|
+
* instead of mid-push. Throws on the first violation. */
|
|
58
|
+
export declare function assertIndexedFields(name: string, def: CollectionDef): void;
|
|
59
|
+
/** One config-declared per-record ACTION button (guide ch. 4): exactly ONE
|
|
45
60
|
* human-initiated step — pressing it invokes the deployed tenant function `fn`
|
|
46
61
|
* with `{ collection, item_id, action, actor, item }` and returns its result.
|
|
47
62
|
* No conditions, no chaining, no scheduling. `key` matches
|
|
@@ -56,7 +71,7 @@ export interface CmsActionDef {
|
|
|
56
71
|
* `FieldDef` itself so it can never drift from the authoring type: it is every
|
|
57
72
|
* key of `FieldDef` except `type` (which is required, not an optional carried
|
|
58
73
|
* attribute). This is the compile-time source of truth for the config-carriage
|
|
59
|
-
* completeness ratchet
|
|
74
|
+
* completeness ratchet: the CLI carriage
|
|
60
75
|
* table (`packages/cli/src/cms.ts` FIELD_ATTRS) and the server one
|
|
61
76
|
* (`workers/control-plane/src/handlers/apply.ts` FIELD_ATTR_DELTA) each pin
|
|
62
77
|
* `keyof` coverage against a set that equals this — so a 9th attribute added to
|
|
@@ -83,7 +98,7 @@ export interface CollectionDef {
|
|
|
83
98
|
* server-caller mode. Not a hard-isolation predicate; a single declarative
|
|
84
99
|
* flag the typed worker consults. Omit for shared/reference collections. */
|
|
85
100
|
ownerField?: string;
|
|
86
|
-
/** Public delivery (
|
|
101
|
+
/** Public delivery (guide ch. 4): when true, this collection's PUBLISHED items
|
|
87
102
|
* are servable through the keyless, edge-cached GET /v1/cms/public/:tenantId/
|
|
88
103
|
* :collection lane (no API key). Owner-UNSCOPED (public content, not per-user);
|
|
89
104
|
* the owner_field is stripped from every served row. Default false. Carried by
|
|
@@ -91,22 +106,22 @@ export interface CollectionDef {
|
|
|
91
106
|
* with a regression test on each — an attribute read by no enforcing path ships
|
|
92
107
|
* inert (the validation.unique / owner_field precedent). */
|
|
93
108
|
public?: boolean;
|
|
94
|
-
/** Per-record action buttons (
|
|
109
|
+
/** Per-record action buttons (guide ch. 4): `[{ key, label, fn }]`, stored on
|
|
95
110
|
* the collection (model data, not a config leaf). The dashboard renders one
|
|
96
111
|
* button per action on each record row; `POST /v1/cms/items/:coll/:id/actions/
|
|
97
112
|
* :key` invokes `fn` once. Carried by BOTH push paths (create + reconcile on
|
|
98
113
|
* existing collections — config is the source of truth; `[]`/absent clears). */
|
|
99
114
|
actions?: CmsActionDef[];
|
|
100
115
|
}
|
|
101
|
-
/** The auth lifecycle events an `authHook` binding may subscribe to
|
|
102
|
-
*
|
|
116
|
+
/** The auth lifecycle events an `authHook` binding may subscribe to
|
|
117
|
+
* (2026-09-10) — a CLOSED union; ONE event per binding, one audit
|
|
103
118
|
* event → one function invocation, no branching inside vxil:
|
|
104
119
|
* user.created — auth.user.created { user_id, method, is_anonymous }
|
|
105
120
|
* session.created — auth.session.created { user_id, session_id }
|
|
106
121
|
* session.revoked — auth.session.revoked { user_id, session_id, reason }
|
|
107
122
|
* signin.failure — auth.signin.failure { email_hash | user_id, reason } */
|
|
108
123
|
export type AuthHookEvent = 'user.created' | 'session.created' | 'session.revoked' | 'signin.failure';
|
|
109
|
-
/** The opt-in re-delivery of a platform-delivered trigger (
|
|
124
|
+
/** The opt-in re-delivery of a platform-delivered trigger (guide ch. 8).
|
|
110
125
|
* DEFAULT OFF: the platform acknowledges every delivery with a 200 whatever
|
|
111
126
|
* the handler returned — an application error is observed (`functions.run.failed`),
|
|
112
127
|
* never re-delivered. With `retry` set on a `queue` / `webhook` / `cmsHook` /
|
|
@@ -126,7 +141,7 @@ export interface FunctionTriggerRetry {
|
|
|
126
141
|
/** 1–5 attempts in all (1 = a failed attempt dead-letters at once, no re-delivery) */
|
|
127
142
|
maxAttempts: number;
|
|
128
143
|
}
|
|
129
|
-
/** A function trigger (
|
|
144
|
+
/** A function trigger (guide ch. 8). cmsHook fires on a CMS write;
|
|
130
145
|
* authHook fires on ONE auth lifecycle event (default `user.created` — the
|
|
131
146
|
* post-signup hook; at-least-once, ~1min fanout latency). */
|
|
132
147
|
export type FunctionTrigger = {
|
|
@@ -228,7 +243,7 @@ export interface FunctionDef {
|
|
|
228
243
|
timeoutMs?: number;
|
|
229
244
|
};
|
|
230
245
|
enabled?: boolean;
|
|
231
|
-
/** optional typed contract surfaced by `vxil gen` (Level 1
|
|
246
|
+
/** optional typed contract surfaced by `vxil gen` (Level 1; guide ch. 8). */
|
|
232
247
|
signature?: {
|
|
233
248
|
input?: unknown;
|
|
234
249
|
output?: unknown;
|
|
@@ -263,8 +278,7 @@ export declare const RELEASED_API_VERSIONS: readonly ApiVersion[];
|
|
|
263
278
|
/** Recursive partial: a `vxil.config.ts` may write any SUBSET of a nested bag
|
|
264
279
|
* (`auth: { otp: { enabled: true } }`) — the server applies the defaults for
|
|
265
280
|
* the rest (Value.Default). A shallow `Partial` made `tsc` reject every
|
|
266
|
-
* partially-written nested bag, including the guide's own skeleton
|
|
267
|
-
* (found by the 2026-09-19 cvskit dry-run). Arrays and primitives are kept
|
|
281
|
+
* partially-written nested bag, including the guide's own skeleton. Arrays and primitives are kept
|
|
268
282
|
* as-is; only plain object bags recurse. */
|
|
269
283
|
export type DeepPartial<T> = T extends readonly unknown[] ? T : T extends object ? {
|
|
270
284
|
[K in keyof T]?: DeepPartial<T[K]>;
|
|
@@ -303,7 +317,7 @@ export interface VxilConfig {
|
|
|
303
317
|
};
|
|
304
318
|
/** (b) CMS SCHEMA-AS-CODE — collections + fields. `push` reconciles them
|
|
305
319
|
* additively against GET /v1/cms/collections (no destructive drops without
|
|
306
|
-
* --allow-destructive).
|
|
320
|
+
* --allow-destructive). Validation hooks stay in features.cms.hooks (config). */
|
|
307
321
|
cms?: {
|
|
308
322
|
collections?: Record<string, CollectionDef>;
|
|
309
323
|
};
|
|
@@ -319,7 +333,7 @@ export interface VxilConfig {
|
|
|
319
333
|
* from JS callers with no compile gate, or a stale generated value) fails fast
|
|
320
334
|
* here rather than 404-ing every request against a nonexistent major. */
|
|
321
335
|
export declare function defineConfig(c: VxilConfig): VxilConfig;
|
|
322
|
-
/** Authoring helper for split-out cms/<name>.ts collection modules (
|
|
336
|
+
/** Authoring helper for split-out cms/<name>.ts collection modules (guide ch. 5).
|
|
323
337
|
* `field('title', 'string', { required: true, indexSlot: 's1' })`. */
|
|
324
338
|
export declare function field(type: FieldType, opts?: Omit<FieldDef, 'type'>): FieldDef;
|
|
325
339
|
/** Authoring helper for a collection module: `export default collection({...})`. */
|
package/dist/config.js
CHANGED
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
// ../config/src/index.ts
|
|
2
|
+
var CMS_INDEXED_FIELD_BUDGET = 4;
|
|
3
|
+
function assertIndexedFields(name, def) {
|
|
4
|
+
let n = 0;
|
|
5
|
+
for (const [fname, f] of Object.entries(def.fields ?? {})) {
|
|
6
|
+
if (f.indexed !== true) continue;
|
|
7
|
+
n += 1;
|
|
8
|
+
if (f.indexSlot) {
|
|
9
|
+
throw new Error(`vxil.config: cms.${name}.${fname} sets both indexed and indexSlot '${f.indexSlot}' \u2014 a slot already indexes equality; use one`);
|
|
10
|
+
}
|
|
11
|
+
if (f.computed) {
|
|
12
|
+
throw new Error(`vxil.config: cms.${name}.${fname} is computed \u2014 a computed field cannot be indexed (bind it to an indexSlot instead)`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
if (n > CMS_INDEXED_FIELD_BUDGET) {
|
|
16
|
+
throw new Error(`vxil.config: cms.${name} has ${n} indexed fields \u2014 at most ${CMS_INDEXED_FIELD_BUDGET} per collection`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
2
19
|
var RELEASED_API_VERSIONS = ["v1"];
|
|
3
20
|
function defineConfig(c) {
|
|
4
21
|
if (c.apiVersion !== void 0 && !RELEASED_API_VERSIONS.includes(c.apiVersion)) {
|
|
@@ -6,16 +23,20 @@ function defineConfig(c) {
|
|
|
6
23
|
`vxil.config: apiVersion '${String(c.apiVersion)}' is not a released API major (released: ${RELEASED_API_VERSIONS.map((v) => `'${v}'`).join(", ")}). A new major ships as a new path with its own deprecation window \u2014 see https://vxil.com/docs/guide/12-going-to-production.`
|
|
7
24
|
);
|
|
8
25
|
}
|
|
26
|
+
for (const [name, def] of Object.entries(c.cms?.collections ?? {})) assertIndexedFields(name, def);
|
|
9
27
|
return c;
|
|
10
28
|
}
|
|
11
29
|
function field(type, opts = {}) {
|
|
12
30
|
return { type, ...opts };
|
|
13
31
|
}
|
|
14
32
|
function collection(def) {
|
|
33
|
+
assertIndexedFields(def.singular ?? "collection", def);
|
|
15
34
|
return def;
|
|
16
35
|
}
|
|
17
36
|
export {
|
|
37
|
+
CMS_INDEXED_FIELD_BUDGET,
|
|
18
38
|
RELEASED_API_VERSIONS,
|
|
39
|
+
assertIndexedFields,
|
|
19
40
|
collection,
|
|
20
41
|
defineConfig,
|
|
21
42
|
field
|