@vxil/config 0.1.1 → 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/index.d.ts CHANGED
@@ -14,7 +14,39 @@ export interface FieldDef {
14
14
  relationTo?: string;
15
15
  computed?: boolean;
16
16
  compute?: unknown;
17
+ /** Claims-table-enforced value uniqueness across live items (cms.md §9.3):
18
+ * a duplicate write is a clean 409 unique_violation. Scalar types only
19
+ * (string/int/float/datetime/relation/file). Carried on collection/field
20
+ * CREATE by `vxil push` and server apply; since 2026-07-17 `vxil push` also
21
+ * RECONCILES it on an existing same-type field (in-place alter via the REST
22
+ * field re-add — packages/cli/src/cms.ts FIELD_ATTRS); attributes push
23
+ * cannot reconcile surface as a visible plan warning, never a silent no-op. */
24
+ unique?: boolean;
25
+ /** Per-relation-field delete behavior (cms.md §11): bounded, atomic fan-out
26
+ * when the referenced item is deleted. Same carriage + reconcile behavior
27
+ * as `unique`. */
28
+ onDelete?: 'cascade' | 'set_null';
17
29
  }
30
+ /** The CANONICAL field-attribute name set (config-side spelling), DERIVED from
31
+ * `FieldDef` itself so it can never drift from the authoring type: it is every
32
+ * key of `FieldDef` except `type` (which is required, not an optional carried
33
+ * attribute). This is the compile-time source of truth for the config-carriage
34
+ * completeness ratchet (audit 2026-07-15e #2, roadmap §4.5 P3): the CLI carriage
35
+ * table (`packages/cli/src/cms.ts` FIELD_ATTRS) and the server one
36
+ * (`workers/control-plane/src/handlers/apply.ts` FIELD_ATTR_DELTA) each pin
37
+ * `keyof` coverage against a set that equals this — so a 9th attribute added to
38
+ * `FieldDef` WITHOUT threading it through BOTH push paths is a tsc error naming
39
+ * the uncovered key, not a silent half-carriage. */
40
+ export type FieldAttrName = Exclude<keyof FieldDef, 'type'>;
41
+ /** Compile-time exact-key closure helper: resolves to `Set` iff `Covered`
42
+ * covers EVERY member of `Required` (i.e. `Required` is assignable to
43
+ * `Covered`), else to the specific uncovered key literal(s) — so a `satisfies`
44
+ * against the covered literal union produces a tsc error naming the field the
45
+ * developer forgot to thread through the carriage table. Used by both push
46
+ * paths' carriage tables to gate additions to `FieldAttrName`. */
47
+ export type CoversFieldAttrs<Covered extends FieldAttrName> = [
48
+ FieldAttrName
49
+ ] extends [Covered] ? Covered : Exclude<FieldAttrName, Covered>;
18
50
  /** A CMS collection-as-code: collection slug → fields. */
19
51
  export interface CollectionDef {
20
52
  singular?: string;
@@ -23,9 +55,17 @@ export interface CollectionDef {
23
55
  * existing `string` field on this collection that holds the owner (end-user)
24
56
  * id. When set, the cms worker auto-scopes owned reads/writes to the VERIFIED
25
57
  * end-user principal in end-user mode (default-deny) — and is a no-op in
26
- * server-caller mode. NOT an RLS predicate; a single declarative flag the
27
- * typed worker consults. Omit for shared/reference collections. */
58
+ * server-caller mode. Not a hard-isolation predicate; a single declarative
59
+ * flag the typed worker consults. Omit for shared/reference collections. */
28
60
  ownerField?: string;
61
+ /** Public delivery (roadmap §4.4): when true, this collection's PUBLISHED items
62
+ * are servable through the keyless, edge-cached GET /v1/cms/public/:tenantId/
63
+ * :collection lane (no API key). Owner-UNSCOPED (public content, not per-user);
64
+ * the owner_field is stripped from every served row. Default false. Carried by
65
+ * BOTH push paths (the `vxil push` cms reconciler AND control-plane /v1/apply)
66
+ * with a regression test on each — an attribute read by no enforcing path ships
67
+ * inert (the validation.unique / owner_field precedent). */
68
+ public?: boolean;
29
69
  }
30
70
  /** A function trigger (the §7.3 crossing). cmsHook fires on a CMS write;
31
71
  * authHook is the post-signup hook (fires on `auth.user.created`,
@@ -77,7 +117,7 @@ export interface FunctionDef {
77
117
  }
78
118
  /** A secret REFERENCE (never a value). `vxil secrets set <name>` writes the value. */
79
119
  export interface SecretRef {
80
- /** which feature's KEK the value is envelope-encrypted under. */
120
+ /** which feature the value is encrypted at rest under. */
81
121
  feature: string;
82
122
  description?: string;
83
123
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vxil/config",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "defineConfig() — one typed source of truth for a whole vxil backend (features + CMS schema + functions + secret refs + seed). INTERNAL workspace package: bundled into the published `vxil` package's `vxil/config` subpath, not published separately.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://vxil.com",
@@ -11,14 +11,18 @@
11
11
  },
12
12
  "type": "module",
13
13
  "exports": {
14
- ".": "./src/index.ts"
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js",
17
+ "default": "./dist/index.js"
18
+ }
15
19
  },
16
20
  "files": [
17
21
  "dist",
18
22
  "src"
19
23
  ],
20
24
  "dependencies": {
21
- "@vxil/feature-configs": "0.1.0"
25
+ "@vxil/feature-configs": "0.2.0"
22
26
  },
23
27
  "publishConfig": {
24
28
  "access": "public"
package/src/index.ts CHANGED
@@ -33,8 +33,41 @@ export interface FieldDef {
33
33
  relationTo?: string;
34
34
  computed?: boolean;
35
35
  compute?: unknown;
36
+ /** Claims-table-enforced value uniqueness across live items (cms.md §9.3):
37
+ * a duplicate write is a clean 409 unique_violation. Scalar types only
38
+ * (string/int/float/datetime/relation/file). Carried on collection/field
39
+ * CREATE by `vxil push` and server apply; since 2026-07-17 `vxil push` also
40
+ * RECONCILES it on an existing same-type field (in-place alter via the REST
41
+ * field re-add — packages/cli/src/cms.ts FIELD_ATTRS); attributes push
42
+ * cannot reconcile surface as a visible plan warning, never a silent no-op. */
43
+ unique?: boolean;
44
+ /** Per-relation-field delete behavior (cms.md §11): bounded, atomic fan-out
45
+ * when the referenced item is deleted. Same carriage + reconcile behavior
46
+ * as `unique`. */
47
+ onDelete?: 'cascade' | 'set_null';
36
48
  }
37
49
 
50
+ /** The CANONICAL field-attribute name set (config-side spelling), DERIVED from
51
+ * `FieldDef` itself so it can never drift from the authoring type: it is every
52
+ * key of `FieldDef` except `type` (which is required, not an optional carried
53
+ * attribute). This is the compile-time source of truth for the config-carriage
54
+ * completeness ratchet (audit 2026-07-15e #2, roadmap §4.5 P3): the CLI carriage
55
+ * table (`packages/cli/src/cms.ts` FIELD_ATTRS) and the server one
56
+ * (`workers/control-plane/src/handlers/apply.ts` FIELD_ATTR_DELTA) each pin
57
+ * `keyof` coverage against a set that equals this — so a 9th attribute added to
58
+ * `FieldDef` WITHOUT threading it through BOTH push paths is a tsc error naming
59
+ * the uncovered key, not a silent half-carriage. */
60
+ export type FieldAttrName = Exclude<keyof FieldDef, 'type'>;
61
+
62
+ /** Compile-time exact-key closure helper: resolves to `Set` iff `Covered`
63
+ * covers EVERY member of `Required` (i.e. `Required` is assignable to
64
+ * `Covered`), else to the specific uncovered key literal(s) — so a `satisfies`
65
+ * against the covered literal union produces a tsc error naming the field the
66
+ * developer forgot to thread through the carriage table. Used by both push
67
+ * paths' carriage tables to gate additions to `FieldAttrName`. */
68
+ export type CoversFieldAttrs<Covered extends FieldAttrName> =
69
+ [FieldAttrName] extends [Covered] ? Covered : Exclude<FieldAttrName, Covered>;
70
+
38
71
  /** A CMS collection-as-code: collection slug → fields. */
39
72
  export interface CollectionDef {
40
73
  singular?: string;
@@ -43,9 +76,17 @@ export interface CollectionDef {
43
76
  * existing `string` field on this collection that holds the owner (end-user)
44
77
  * id. When set, the cms worker auto-scopes owned reads/writes to the VERIFIED
45
78
  * end-user principal in end-user mode (default-deny) — and is a no-op in
46
- * server-caller mode. NOT an RLS predicate; a single declarative flag the
47
- * typed worker consults. Omit for shared/reference collections. */
79
+ * server-caller mode. Not a hard-isolation predicate; a single declarative
80
+ * flag the typed worker consults. Omit for shared/reference collections. */
48
81
  ownerField?: string;
82
+ /** Public delivery (roadmap §4.4): when true, this collection's PUBLISHED items
83
+ * are servable through the keyless, edge-cached GET /v1/cms/public/:tenantId/
84
+ * :collection lane (no API key). Owner-UNSCOPED (public content, not per-user);
85
+ * the owner_field is stripped from every served row. Default false. Carried by
86
+ * BOTH push paths (the `vxil push` cms reconciler AND control-plane /v1/apply)
87
+ * with a regression test on each — an attribute read by no enforcing path ships
88
+ * inert (the validation.unique / owner_field precedent). */
89
+ public?: boolean;
49
90
  }
50
91
 
51
92
  /** A function trigger (the §7.3 crossing). cmsHook fires on a CMS write;
@@ -80,7 +121,7 @@ export interface FunctionDef {
80
121
 
81
122
  /** A secret REFERENCE (never a value). `vxil secrets set <name>` writes the value. */
82
123
  export interface SecretRef {
83
- /** which feature's KEK the value is envelope-encrypted under. */
124
+ /** which feature the value is encrypted at rest under. */
84
125
  feature: string;
85
126
  description?: string;
86
127
  }