@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 +58 -0
- package/dist/hooks.d.ts +1 -1
- package/dist/hooks.js +4 -4
- package/dist/index.js +7 -7
- package/dist/readmodels.js +1 -1
- package/package.json +2 -2
- package/src/hooks.ts +4 -4
- package/src/index.ts +7 -7
- package/src/readmodels.ts +1 -1
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/
|
|
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/
|
|
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
|
|
21
|
-
//
|
|
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/
|
|
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 (
|
|
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/
|
|
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
|
|
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())), //
|
|
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
|
|
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
|
|
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 (
|
|
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
|
package/dist/readmodels.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// cms-rel B5/E config-write gates (docs/
|
|
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.
|
|
4
|
-
"description": "
|
|
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/
|
|
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
|
|
21
|
-
//
|
|
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/
|
|
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 (
|
|
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/
|
|
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
|
|
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())), //
|
|
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
|
|
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
|
|
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 (
|
|
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/
|
|
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
|