@vxil/feature-configs 0.3.0 → 0.3.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/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # @vxil/feature-configs
2
+
3
+ The typed config schemas for every Vxil feature — the shapes a `vxil.config.ts`
4
+ is built on, published so your editor and your CI can read the same contract the
5
+ platform validates against.
6
+
7
+ ```bash
8
+ npm i -D @vxil/feature-configs
9
+ ```
10
+
11
+ Most people never import this directly: writing
12
+ [`defineConfig`](https://www.npmjs.com/package/@vxil/config) gives you these
13
+ types automatically. Reach for it when you want to validate a manifest yourself,
14
+ generate config, or type a helper.
15
+
16
+ ## What's in it
17
+
18
+ - **One schema per feature**, all 19 of them — `NotificationsConfigSchema`,
19
+ `AuthConfigSchema`, `CmsConfigSchema`, `PaymentsConfigSchema`,
20
+ `FunctionsConfigSchema`, … — plus the matching `Static<>` types
21
+ (`NotificationsConfig`, `AuthConfig`, …).
22
+ - **`FEATURE_KEYS`** — the canonical feature list, and **`FEATURE_SCHEMAS`** —
23
+ the key → schema map.
24
+ - **`validateFeatureConfig(feature, raw)`** — the exact validation the control
25
+ plane runs, returning `{ ok, errors }` with JSON-pointer-style messages. Use it
26
+ in a pre-commit hook or a test to fail on a bad config before you push it.
27
+ - **`hooks`** (`@vxil/feature-configs/hooks`) — the CMS lifecycle-hook expression
28
+ grammar: the same closed, sandboxed parser/validator the server uses, so a
29
+ malformed `validate` / `derive` expression is caught in your editor.
30
+
31
+ ```ts
32
+ import { validateFeatureConfig, FEATURE_KEYS } from '@vxil/feature-configs';
33
+
34
+ const result = validateFeatureConfig('notifications', {
35
+ enabled: true,
36
+ fromEmail: 'noreply@acme.com',
37
+ });
38
+ if (!result.ok) throw new Error(result.errors.join('\n'));
39
+ ```
40
+
41
+ ## Why the schemas are small on purpose
42
+
43
+ Every feature's config is capped at a fixed number of top-level leaves. A knob
44
+ earns its slot by having a reader; one that nothing reads gets deleted rather
45
+ than left to rot as a promise the product does not keep. That is why the schemas
46
+ here are the whole truth about what a feature can be configured to do — and why
47
+ a version bump may *remove* a knob that was never wired. Removals are listed in
48
+ the feature reference at [vxil.com](https://vxil.com).
49
+
50
+ ## Related
51
+
52
+ - [`@vxil/config`](https://www.npmjs.com/package/@vxil/config) — `defineConfig()`.
53
+ - [`@vxil/cli`](https://www.npmjs.com/package/@vxil/cli) — the `vxil` command (`plan` / `push` / `gen`).
54
+ - [`@vxil/sdk`](https://www.npmjs.com/package/@vxil/sdk) — the typed runtime client.
55
+
56
+ Docs: [vxil.com](https://vxil.com) · Dashboard: [vxil.com/dashboard](https://vxil.com/dashboard)
57
+
58
+ MIT © techmaker.io
package/dist/hooks.d.ts CHANGED
@@ -10,7 +10,7 @@ export declare const HOOK_LIMITS: {
10
10
  readonly maxReadHooksPerCollection: 10;
11
11
  };
12
12
  /** The ONLY root variables an expression may reference. `caller` is the VERIFIED
13
- * end-user principal (docs/end-user-principals-design.md §5.6) — read-only and
13
+ * end-user principal (https://vxil.com/docs/guide/09-security-and-multitenancy) — read-only and
14
14
  * populated ONLY on the READ path (runReadHooks); on the write path and in
15
15
  * server-caller mode it is null, exactly like `before` on a create. It carries
16
16
  * `caller.endUserId` (the verified session sub, or null) and `caller.principal`
package/dist/hooks.js CHANGED
@@ -1,4 +1,4 @@
1
- // CMS lifecycle hooks — the SAFE expression engine (docs/cms-lifecycle-hooks-rung2-design.md, Lane A).
1
+ // CMS lifecycle hooks — the SAFE expression engine (https://vxil.com/docs/guide/07-validation-and-hooks).
2
2
  //
3
3
  // A "code-based" lifecycle hook is a small EXPRESSION the tenant authors. Three kinds:
4
4
  // - validate: the expression must evaluate truthy or the write is REJECTED (422)
@@ -17,8 +17,8 @@
17
17
  // CROSS-ROW AGGREGATION STAYS OUT — BY DESIGN. Read hooks are a pure function
18
18
  // of ONE row (+ `now`): there are no new root variables, no array/aggregate
19
19
  // functions, no access to sibling rows or other collections. Counts/sums/
20
- // group-bys across rows are the §5 identity-fork anti-item (docs/features/
21
- // cms.md §6.7) and must never enter this engine.
20
+ // group-bys across rows are the identity-fork anti-item (function territory:
21
+ // vxil.com/docs/guide/07-validation-and-hooks) and must never enter this engine.
22
22
  //
23
23
  // SAFETY (the "not malicious" guarantee) is structural, not heuristic:
24
24
  // * No JS is executed. `eval`/`Function`/the host runtime are never touched. The
@@ -61,7 +61,7 @@ export const HOOK_LIMITS = {
61
61
  };
62
62
  // ── allow-lists ─────────────────────────────────────────────────────────────
63
63
  /** The ONLY root variables an expression may reference. `caller` is the VERIFIED
64
- * end-user principal (docs/end-user-principals-design.md §5.6) — read-only and
64
+ * end-user principal (https://vxil.com/docs/guide/09-security-and-multitenancy) — read-only and
65
65
  * populated ONLY on the READ path (runReadHooks); on the write path and in
66
66
  * server-caller mode it is null, exactly like `before` on a create. It carries
67
67
  * `caller.endUserId` (the verified session sub, or null) and `caller.principal`
package/dist/index.js CHANGED
@@ -452,7 +452,7 @@ export const FilesConfigSchema = Type.Object({
452
452
  // object storage is cheap but the SQL-database-resident metadata + abuse aren't (pricing re-audit
453
453
  // 2026-07-10). 5 GiB/object, 1 TiB/tenant absolute; true PER-TIER clamps
454
454
  // (Free/Dev 10 GB · Team 50 GB · Business 256 GB) are a follow-up needing the
455
- // tenant tier threaded to files-v1 (docs/pricing-model-analysis.md §6).
455
+ // tenant tier threaded to files-v1 (plan tiers).
456
456
  maxObjectBytes: Type.Integer({ default: 100 * 1024 * 1024, minimum: 1, maximum: 5 * 1024 * 1024 * 1024 }),
457
457
  maxTotalBytes: Type.Integer({ default: 10 * 1024 * 1024 * 1024, minimum: 1, maximum: 1024 * 1024 * 1024 * 1024 }),
458
458
  maxObjectCount: Type.Integer({ default: 100000, minimum: 1, maximum: 100_000_000 }),
@@ -536,7 +536,7 @@ export const CmsConfigSchema = Type.Object({
536
536
  maxExpandFields: Type.Integer({ default: 5, minimum: 1, maximum: 25 }),
537
537
  maxComputedFieldsPerCollection: Type.Integer({ default: 10, minimum: 1, maximum: 50 }),
538
538
  }, { default: {} }),
539
- // Code-based lifecycle hooks (docs/cms-lifecycle-hooks-rung2-design.md, Lane A):
539
+ // Code-based lifecycle hooks (https://vxil.com/docs/guide/07-validation-and-hooks):
540
540
  // a tenant-authored SAFE expression. Write events (beforeCreate/beforeUpdate/
541
541
  // beforeWrite) run in-transaction — `validate` rejects the write, `derive`
542
542
  // computes a persisted field. Read events shape the RESPONSE only: `validate`
@@ -1044,9 +1044,9 @@ export const FunctionsConfigSchema = Type.Object({
1044
1044
  collection: Type.Optional(Type.String()), // cmsHook: the CMS collection slug
1045
1045
  event: Type.Optional(Type.String()), // cmsHook: beforeCreate|beforeUpdate|beforeWrite · authHook: 'user.created'
1046
1046
  }), { maxItems: 8 })),
1047
- scriptRef: Type.String(), // content-hashed WfP script name: fn-<tenant>-<name>-<sha>
1047
+ scriptRef: Type.String(), // content-hashed hosted-script name: fn-<tenant>-<name>-<sha>
1048
1048
  scopes: Type.Optional(Type.Array(Type.String())), // clamped via DENY_FUNCTION_SCOPES at deploy (admin/*/features:write/functions:write/secrets:write)
1049
- secrets: Type.Optional(Type.Array(Type.String())), // tenant_secrets refs (KEK_FUNCTIONS)
1049
+ secrets: Type.Optional(Type.Array(Type.String())), // names of tenant secrets injected at invoke time
1050
1050
  egressAllow: Type.Optional(Type.Array(Type.String())), // Outbound Worker allowlist hosts
1051
1051
  limits: Type.Optional(Type.Object({
1052
1052
  cpuMs: Type.Integer(),
@@ -1064,7 +1064,7 @@ export const FunctionsConfigSchema = Type.Object({
1064
1064
  })),
1065
1065
  }))),
1066
1066
  });
1067
- // copilot feature (docs/copilot-agents-sku-design.md §4). Re-declared to match
1067
+ // copilot feature (https://vxil.com/docs/guide/11-copilot-agents). Re-declared to match
1068
1068
  // workers/copilot-v1/src/config.ts's exported CopilotConfigSchema (one shape,
1069
1069
  // two consumers — keep the two definitions byte-identical). A thin COMPOSITION
1070
1070
  // layer over ai + vector-search + rag + mcp: it owns the AGENT layer (persona,
@@ -1511,7 +1511,7 @@ export function validateFeatureConfig(feature, raw) {
1511
1511
  if (errs.length)
1512
1512
  return { ok: false, errors: errs.slice(0, 10) };
1513
1513
  }
1514
- // Cross-field rules: copilot (docs/copilot-agents-sku-design.md §4). Pure
1514
+ // Cross-field rules: copilot (https://vxil.com/docs/guide/11-copilot-agents). Pure
1515
1515
  // rules only — cross-feature resolutions (collection existence, directiveRef
1516
1516
  // → ai.templates) are deferred to runtime, which fails soft with the
1517
1517
  // capability-surface envelope. Tool-NAME existence runs only when the mcp
@@ -1557,7 +1557,7 @@ export function validateFeatureConfig(feature, raw) {
1557
1557
  return { ok: true, errors: [], value: withDefaults };
1558
1558
  }
1559
1559
  // ─────────────────────────────────────────────────────────────────────────────
1560
- // PLANNER GROUNDING (roadmap §4.6.E; docs/ai-planner-design.md §2).
1560
+ // PLANNER GROUNDING (https://vxil.com/docs/guide/10-agents-and-mcp).
1561
1561
  // FEATURE_KEYS is the literal feature list the planner catalog is projected
1562
1562
  // from; the planner-catalog CI gate asserts set-equality with
1563
1563
  // Object.keys(FEATURE_SCHEMAS), so adding a feature without extending this
@@ -1,4 +1,4 @@
1
- // cms-rel B5/E config-write gates (docs/cms-relational-depth-options.md §3/§4)
1
+ // cms-rel B5/E config-write gates (read models + transactions — https://vxil.com/docs/guide/04-data-with-cms)
2
2
  // — the hooks.ts sibling: PURE structural validation of the `readModels` and
3
3
  // `cdc` bags at config-write time (the anti-malice-gate pattern). The grammar
4
4
  // (fn/rank allow-lists, arity caps, window bounds, dotted-term shape) is
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vxil/feature-configs",
3
- "version": "0.3.0",
4
- "description": "TypeBox config schemas for every vxil feature — the typed manifests vxil.config.ts is built on. INTERNAL workspace package: bundled into the published `vxil` package (via @vxil/config), not published separately.",
3
+ "version": "0.3.1",
4
+ "description": "The per-feature configuration schemas and validators behind vxil.config.ts (published for @vxil/cli and @vxil/config).",
5
5
  "license": "MIT",
6
6
  "homepage": "https://vxil.com",
7
7
  "repository": {
package/src/hooks.ts CHANGED
@@ -1,4 +1,4 @@
1
- // CMS lifecycle hooks — the SAFE expression engine (docs/cms-lifecycle-hooks-rung2-design.md, Lane A).
1
+ // CMS lifecycle hooks — the SAFE expression engine (https://vxil.com/docs/guide/07-validation-and-hooks).
2
2
  //
3
3
  // A "code-based" lifecycle hook is a small EXPRESSION the tenant authors. Three kinds:
4
4
  // - validate: the expression must evaluate truthy or the write is REJECTED (422)
@@ -17,8 +17,8 @@
17
17
  // CROSS-ROW AGGREGATION STAYS OUT — BY DESIGN. Read hooks are a pure function
18
18
  // of ONE row (+ `now`): there are no new root variables, no array/aggregate
19
19
  // functions, no access to sibling rows or other collections. Counts/sums/
20
- // group-bys across rows are the §5 identity-fork anti-item (docs/features/
21
- // cms.md §6.7) and must never enter this engine.
20
+ // group-bys across rows are the identity-fork anti-item (function territory:
21
+ // vxil.com/docs/guide/07-validation-and-hooks) and must never enter this engine.
22
22
  //
23
23
  // SAFETY (the "not malicious" guarantee) is structural, not heuristic:
24
24
  // * No JS is executed. `eval`/`Function`/the host runtime are never touched. The
@@ -63,7 +63,7 @@ export const HOOK_LIMITS = {
63
63
 
64
64
  // ── allow-lists ─────────────────────────────────────────────────────────────
65
65
  /** The ONLY root variables an expression may reference. `caller` is the VERIFIED
66
- * end-user principal (docs/end-user-principals-design.md §5.6) — read-only and
66
+ * end-user principal (https://vxil.com/docs/guide/09-security-and-multitenancy) — read-only and
67
67
  * populated ONLY on the READ path (runReadHooks); on the write path and in
68
68
  * server-caller mode it is null, exactly like `before` on a create. It carries
69
69
  * `caller.endUserId` (the verified session sub, or null) and `caller.principal`
package/src/index.ts CHANGED
@@ -564,7 +564,7 @@ export const FilesConfigSchema = Type.Object({
564
564
  // object storage is cheap but the SQL-database-resident metadata + abuse aren't (pricing re-audit
565
565
  // 2026-07-10). 5 GiB/object, 1 TiB/tenant absolute; true PER-TIER clamps
566
566
  // (Free/Dev 10 GB · Team 50 GB · Business 256 GB) are a follow-up needing the
567
- // tenant tier threaded to files-v1 (docs/pricing-model-analysis.md §6).
567
+ // tenant tier threaded to files-v1 (plan tiers).
568
568
  maxObjectBytes: Type.Integer({ default: 100 * 1024 * 1024, minimum: 1, maximum: 5 * 1024 * 1024 * 1024 }),
569
569
  maxTotalBytes: Type.Integer({ default: 10 * 1024 * 1024 * 1024, minimum: 1, maximum: 1024 * 1024 * 1024 * 1024 }),
570
570
  maxObjectCount: Type.Integer({ default: 100000, minimum: 1, maximum: 100_000_000 }),
@@ -678,7 +678,7 @@ export const CmsConfigSchema = Type.Object({
678
678
  },
679
679
  { default: {} },
680
680
  ),
681
- // Code-based lifecycle hooks (docs/cms-lifecycle-hooks-rung2-design.md, Lane A):
681
+ // Code-based lifecycle hooks (https://vxil.com/docs/guide/07-validation-and-hooks):
682
682
  // a tenant-authored SAFE expression. Write events (beforeCreate/beforeUpdate/
683
683
  // beforeWrite) run in-transaction — `validate` rejects the write, `derive`
684
684
  // computes a persisted field. Read events shape the RESPONSE only: `validate`
@@ -1373,9 +1373,9 @@ export const FunctionsConfigSchema = Type.Object({
1373
1373
  { maxItems: 8 },
1374
1374
  ),
1375
1375
  ),
1376
- scriptRef: Type.String(), // content-hashed WfP script name: fn-<tenant>-<name>-<sha>
1376
+ scriptRef: Type.String(), // content-hashed hosted-script name: fn-<tenant>-<name>-<sha>
1377
1377
  scopes: Type.Optional(Type.Array(Type.String())), // clamped via DENY_FUNCTION_SCOPES at deploy (admin/*/features:write/functions:write/secrets:write)
1378
- secrets: Type.Optional(Type.Array(Type.String())), // tenant_secrets refs (KEK_FUNCTIONS)
1378
+ secrets: Type.Optional(Type.Array(Type.String())), // names of tenant secrets injected at invoke time
1379
1379
  egressAllow: Type.Optional(Type.Array(Type.String())), // Outbound Worker allowlist hosts
1380
1380
  limits: Type.Optional(
1381
1381
  Type.Object({
@@ -1405,7 +1405,7 @@ export const FunctionsConfigSchema = Type.Object({
1405
1405
  // defaultLimits.memoryMb leaves.)
1406
1406
  export type FunctionsConfig = Static<typeof FunctionsConfigSchema>;
1407
1407
 
1408
- // copilot feature (docs/copilot-agents-sku-design.md §4). Re-declared to match
1408
+ // copilot feature (https://vxil.com/docs/guide/11-copilot-agents). Re-declared to match
1409
1409
  // workers/copilot-v1/src/config.ts's exported CopilotConfigSchema (one shape,
1410
1410
  // two consumers — keep the two definitions byte-identical). A thin COMPOSITION
1411
1411
  // layer over ai + vector-search + rag + mcp: it owns the AGENT layer (persona,
@@ -1922,7 +1922,7 @@ export function validateFeatureConfig(feature: string, raw: unknown): ConfigVali
1922
1922
  }
1923
1923
  if (errs.length) return { ok: false, errors: errs.slice(0, 10) };
1924
1924
  }
1925
- // Cross-field rules: copilot (docs/copilot-agents-sku-design.md §4). Pure
1925
+ // Cross-field rules: copilot (https://vxil.com/docs/guide/11-copilot-agents). Pure
1926
1926
  // rules only — cross-feature resolutions (collection existence, directiveRef
1927
1927
  // → ai.templates) are deferred to runtime, which fails soft with the
1928
1928
  // capability-surface envelope. Tool-NAME existence runs only when the mcp
@@ -1973,7 +1973,7 @@ export function validateFeatureConfig(feature: string, raw: unknown): ConfigVali
1973
1973
  }
1974
1974
 
1975
1975
  // ─────────────────────────────────────────────────────────────────────────────
1976
- // PLANNER GROUNDING (roadmap §4.6.E; docs/ai-planner-design.md §2).
1976
+ // PLANNER GROUNDING (https://vxil.com/docs/guide/10-agents-and-mcp).
1977
1977
  // FEATURE_KEYS is the literal feature list the planner catalog is projected
1978
1978
  // from; the planner-catalog CI gate asserts set-equality with
1979
1979
  // Object.keys(FEATURE_SCHEMAS), so adding a feature without extending this
package/src/readmodels.ts CHANGED
@@ -1,4 +1,4 @@
1
- // cms-rel B5/E config-write gates (docs/cms-relational-depth-options.md §3/§4)
1
+ // cms-rel B5/E config-write gates (read models + transactions — https://vxil.com/docs/guide/04-data-with-cms)
2
2
  // — the hooks.ts sibling: PURE structural validation of the `readModels` and
3
3
  // `cdc` bags at config-write time (the anti-malice-gate pattern). The grammar
4
4
  // (fn/rank allow-lists, arity caps, window bounds, dotted-term shape) is