@vxil/config 0.3.0 → 0.4.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/README.md +72 -0
- package/dist/index.d.ts +38 -7
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/src/index.test.ts +1 -1
- package/src/index.ts +42 -8
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# @vxil/config
|
|
2
|
+
|
|
3
|
+
`defineConfig()` — one typed source of truth for a whole Vxil backend.
|
|
4
|
+
|
|
5
|
+
A `vxil.config.ts` in your repo declares every feature you have enabled, your
|
|
6
|
+
CMS content model, your deployed functions, your secret *references* and your
|
|
7
|
+
seed data. `vxil push` compiles it to a manifest and applies it. Nothing in this
|
|
8
|
+
file is ever executed by Vxil — config is data.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm i -D @vxil/config
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
> The CLI that reads this file is **`@vxil/cli`** (it installs the `vxil`
|
|
15
|
+
> command). The bare `vxil` package name is blocked on npm — never `npm i vxil`.
|
|
16
|
+
|
|
17
|
+
## Use it
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
// vxil.config.ts
|
|
21
|
+
import { defineConfig } from '@vxil/config';
|
|
22
|
+
|
|
23
|
+
export default defineConfig({
|
|
24
|
+
features: {
|
|
25
|
+
cms: { enabled: true },
|
|
26
|
+
files: { enabled: true },
|
|
27
|
+
notifications: { enabled: true, fromEmail: 'noreply@acme.com' },
|
|
28
|
+
},
|
|
29
|
+
cms: {
|
|
30
|
+
collections: {
|
|
31
|
+
posts: {
|
|
32
|
+
fields: {
|
|
33
|
+
title: { type: 'string', required: true, indexSlot: 's1' },
|
|
34
|
+
body: { type: 'text' },
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npx @vxil/cli plan # dry-run diff against the live tenant
|
|
44
|
+
npx @vxil/cli push # apply, idempotent
|
|
45
|
+
npx @vxil/cli gen # emit the per-tenant typed client
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## What it gives you
|
|
49
|
+
|
|
50
|
+
- **Real autocomplete.** `defineConfig` is a typed identity function: it returns
|
|
51
|
+
its argument verbatim, and the types come from
|
|
52
|
+
[`@vxil/feature-configs`](https://www.npmjs.com/package/@vxil/feature-configs),
|
|
53
|
+
the same schemas the platform validates against. A knob that does not exist is
|
|
54
|
+
a compile error in your editor, not a 422 at push time.
|
|
55
|
+
- **The whole backend in one file.** Features, the CMS content model (collections,
|
|
56
|
+
field types, index slots, uniqueness, lifecycle hook expressions), functions,
|
|
57
|
+
secret references and seed data — one declarative object.
|
|
58
|
+
- **Secrets by reference, never by value.** A config carries `secret:<name>`; the
|
|
59
|
+
value is set once with `vxil secrets set` and never lands in your repo.
|
|
60
|
+
- **Versioned, concurrent-safe applies.** Every push is an If-Match write against
|
|
61
|
+
an immutable config version, so two people (or a person and CI) cannot silently
|
|
62
|
+
clobber each other — and `vxil versions` / `vxil rollback` undo one.
|
|
63
|
+
|
|
64
|
+
## Related
|
|
65
|
+
|
|
66
|
+
- [`@vxil/cli`](https://www.npmjs.com/package/@vxil/cli) — the `vxil` command.
|
|
67
|
+
- [`@vxil/feature-configs`](https://www.npmjs.com/package/@vxil/feature-configs) — the feature schemas and their `Static<>` types.
|
|
68
|
+
- [`@vxil/sdk`](https://www.npmjs.com/package/@vxil/sdk) — the typed runtime client.
|
|
69
|
+
|
|
70
|
+
Docs: [vxil.com](https://vxil.com) · Dashboard: [vxil.com/dashboard](https://vxil.com/dashboard)
|
|
71
|
+
|
|
72
|
+
MIT © techmaker.io
|
package/dist/index.d.ts
CHANGED
|
@@ -76,7 +76,7 @@ export type CoversFieldAttrs<Covered extends FieldAttrName> = [
|
|
|
76
76
|
export interface CollectionDef {
|
|
77
77
|
singular?: string;
|
|
78
78
|
fields: Record<string, FieldDef>;
|
|
79
|
-
/** End-user owner-scoping (docs/
|
|
79
|
+
/** End-user owner-scoping (https://vxil.com/docs/guide/09-security-and-multitenancy): names an
|
|
80
80
|
* existing `string` field on this collection that holds the owner (end-user)
|
|
81
81
|
* id. When set, the cms worker auto-scopes owned reads/writes to the VERIFIED
|
|
82
82
|
* end-user principal in end-user mode (default-deny) — and is a no-op in
|
|
@@ -118,7 +118,14 @@ export type FunctionTrigger = {
|
|
|
118
118
|
} | {
|
|
119
119
|
kind: 'queue';
|
|
120
120
|
source: string;
|
|
121
|
-
}
|
|
121
|
+
}
|
|
122
|
+
/** `webhook`: the function is woken by a webhook_subscription fan-out whose
|
|
123
|
+
* target_url is `<edge>/v1/internal/fn/trigger/<name>?tenant=<id>`. `source` is
|
|
124
|
+
* an event-name PREFIX filter (e.g. 'payments.'), enforced at delivery — a
|
|
125
|
+
* non-matching event is ACK-200 skipped. Declaring the binding is what opts the
|
|
126
|
+
* function into event delivery; a receiver that declares only `cron` keeps the
|
|
127
|
+
* older cron+{} wake. */
|
|
128
|
+
| {
|
|
122
129
|
kind: 'webhook';
|
|
123
130
|
source: string;
|
|
124
131
|
} | {
|
|
@@ -138,14 +145,38 @@ export interface FunctionDef {
|
|
|
138
145
|
* functions:write, secrets:write rejected; payments:write / notifications:send /
|
|
139
146
|
* users:* allowed as owner-granted business scopes). */
|
|
140
147
|
scopes?: string[];
|
|
141
|
-
/**
|
|
148
|
+
/** Names of tenant secrets injected into the function at invoke time. */
|
|
142
149
|
secrets?: string[];
|
|
143
|
-
/** Outbound
|
|
150
|
+
/** Outbound host allowlist (deny-by-default egress guard). */
|
|
144
151
|
egressAllow?: string[];
|
|
152
|
+
/** Per-function resource declarations. Both optional; both clamped server-side.
|
|
153
|
+
* `memoryMb` was REMOVED (2026-09-18): memory is fixed by the managed runtime
|
|
154
|
+
* and is not per-dispatch selectable. A config that still declares it pushes
|
|
155
|
+
* fine — the key is stripped, and `vxil plan --explain` marks it DROPPED. */
|
|
145
156
|
limits?: {
|
|
157
|
+
/** per-dispatch isolate CPU ceiling, ms (5..300000). Only ever TIGHTENS the
|
|
158
|
+
* platform cap.
|
|
159
|
+
*
|
|
160
|
+
* IT ALSO RAISES YOUR BILL on a metered tenant, and that is worth reading
|
|
161
|
+
* twice: it raises (never lowers) the per-invoke credit reserve, whose floor
|
|
162
|
+
* is the manifest default — and the reserve is the CEILING the pro-rated
|
|
163
|
+
* settle clamps into, because the settle can never commit more than the hold
|
|
164
|
+
* it releases from. So an invoke whose wall time exceeds `defaultLimits.cpuMs`
|
|
165
|
+
* (50) settles at its MEASURED wall-ms instead of being capped at 50:
|
|
166
|
+
* declaring `cpuMs: 1000` makes an 800 ms invoke cost 800 instead of 50.
|
|
167
|
+
* Declaring only `timeoutMs` leaves metering byte-identical.
|
|
168
|
+
*
|
|
169
|
+
* It does NOT affect anyone else's availability: the account-wide daily
|
|
170
|
+
* capacity budget counts the platform's own per-invoke estimate, never this
|
|
171
|
+
* value (workers/functions-v1/src/capacity.ts `capacityReserveCpuMs`). */
|
|
146
172
|
cpuMs?: number;
|
|
173
|
+
/** wall budget for ONE outbound fetch this function makes, ms (1000..120000,
|
|
174
|
+
* default 30000). Not a whole-invocation budget: three 25 s fetches still
|
|
175
|
+
* take 75 s. The invocation is bounded separately by the platform's own
|
|
176
|
+
* deadline (`FN_MAX_INVOKE_MS`, default ≥ 5 minutes), which raising this
|
|
177
|
+
* value widens with you. The vxil API callback leg keeps the platform's
|
|
178
|
+
* own 30 s bound. */
|
|
147
179
|
timeoutMs?: number;
|
|
148
|
-
memoryMb?: number;
|
|
149
180
|
};
|
|
150
181
|
enabled?: boolean;
|
|
151
182
|
/** optional typed contract surfaced by `vxil gen` (Level 1, §4.5). */
|
|
@@ -171,7 +202,7 @@ export interface SeedSpec {
|
|
|
171
202
|
email?: string;
|
|
172
203
|
}[];
|
|
173
204
|
}
|
|
174
|
-
/** The released API majors (docs/
|
|
205
|
+
/** The released API majors (see https://vxil.com/docs/guide/12-going-to-production), a CLOSED union —
|
|
175
206
|
* `'v1'` is the only released major, so `apiVersion: 'v2'` is a COMPILE error
|
|
176
207
|
* until a v2 GAs. `defineConfig` also validates it at runtime against
|
|
177
208
|
* `RELEASED_API_VERSIONS`. */
|
|
@@ -183,7 +214,7 @@ export declare const RELEASED_API_VERSIONS: readonly ApiVersion[];
|
|
|
183
214
|
export interface VxilConfig {
|
|
184
215
|
/** target environment label; resolved to a baseUrl by `vxil link` / `--env`. */
|
|
185
216
|
env?: string;
|
|
186
|
-
/** Pin the API major the CLI targets (default `'v1'
|
|
217
|
+
/** Pin the API major the CLI targets (default `'v1'`; see https://vxil.com/docs/guide/12-going-to-production).
|
|
187
218
|
* Path-major, per-platform: a breaking change ships as a new major on a new
|
|
188
219
|
* path with a ≥6-month deprecation window. `VXIL_API_VERSION` overrides this
|
|
189
220
|
* for a one-off invocation. */
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ export function defineConfig(c) {
|
|
|
9
9
|
if (c.apiVersion !== undefined && !RELEASED_API_VERSIONS.includes(c.apiVersion)) {
|
|
10
10
|
throw new Error(`vxil.config: apiVersion '${String(c.apiVersion)}' is not a released API major ` +
|
|
11
11
|
`(released: ${RELEASED_API_VERSIONS.map((v) => `'${v}'`).join(', ')}). ` +
|
|
12
|
-
'A new major ships as a new path with its own deprecation window — see docs/
|
|
12
|
+
'A new major ships as a new path with its own deprecation window — see https://vxil.com/docs/guide/12-going-to-production.');
|
|
13
13
|
}
|
|
14
14
|
return c;
|
|
15
15
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vxil/config",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Typed vxil.config.ts authoring for the vxil backend platform — defineConfig, feature and cms field definitions (published for @vxil/cli).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://vxil.com",
|
|
7
7
|
"repository": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"src"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@vxil/feature-configs": "0.
|
|
25
|
+
"@vxil/feature-configs": "0.4.0"
|
|
26
26
|
},
|
|
27
27
|
"publishConfig": {
|
|
28
28
|
"access": "public"
|
package/src/index.test.ts
CHANGED
|
@@ -23,6 +23,6 @@ describe('defineConfig', () => {
|
|
|
23
23
|
expect(() => defineConfig({ apiVersion: 'v2' as 'v1', features: {} }))
|
|
24
24
|
.toThrow(/apiVersion 'v2' is not a released API major/);
|
|
25
25
|
expect(() => defineConfig({ apiVersion: 'latest' as 'v1' }))
|
|
26
|
-
.toThrow(/
|
|
26
|
+
.toThrow(/docs\/guide\/12-going-to-production/);
|
|
27
27
|
});
|
|
28
28
|
});
|
package/src/index.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// §7.3-consistent — config is DATA; the only tenant CODE that crosses into vxil
|
|
8
8
|
// is (a) the Lane-A hook expressions (a closed sandbox) and (b) the functions/
|
|
9
9
|
// sources (the paid, opt-in, egress-guarded crossing). Everything here is
|
|
10
|
-
// declarative. See docs/
|
|
10
|
+
// declarative. See https://vxil.com/docs/guide/05-typed-sdk-and-cli.
|
|
11
11
|
import type {
|
|
12
12
|
NotificationsConfig, JobsConfig, AuthConfig, RateLimitsConfig, FilesConfig,
|
|
13
13
|
WebhooksConfig, CommentsConfig, CmsConfig, McpConfig, RealtimeConfig,
|
|
@@ -98,7 +98,7 @@ export type CoversFieldAttrs<Covered extends FieldAttrName> =
|
|
|
98
98
|
export interface CollectionDef {
|
|
99
99
|
singular?: string;
|
|
100
100
|
fields: Record<string, FieldDef>;
|
|
101
|
-
/** End-user owner-scoping (docs/
|
|
101
|
+
/** End-user owner-scoping (https://vxil.com/docs/guide/09-security-and-multitenancy): names an
|
|
102
102
|
* existing `string` field on this collection that holds the owner (end-user)
|
|
103
103
|
* id. When set, the cms worker auto-scopes owned reads/writes to the VERIFIED
|
|
104
104
|
* end-user principal in end-user mode (default-deny) — and is a no-op in
|
|
@@ -137,6 +137,12 @@ export type FunctionTrigger =
|
|
|
137
137
|
| { kind: 'http'; path?: string }
|
|
138
138
|
| { kind: 'cron'; schedule: string }
|
|
139
139
|
| { kind: 'queue'; source: string }
|
|
140
|
+
/** `webhook`: the function is woken by a webhook_subscription fan-out whose
|
|
141
|
+
* target_url is `<edge>/v1/internal/fn/trigger/<name>?tenant=<id>`. `source` is
|
|
142
|
+
* an event-name PREFIX filter (e.g. 'payments.'), enforced at delivery — a
|
|
143
|
+
* non-matching event is ACK-200 skipped. Declaring the binding is what opts the
|
|
144
|
+
* function into event delivery; a receiver that declares only `cron` keeps the
|
|
145
|
+
* older cron+{} wake. */
|
|
140
146
|
| { kind: 'webhook'; source: string }
|
|
141
147
|
| { kind: 'cmsHook'; collection: string; event: 'beforeCreate' | 'beforeUpdate' | 'beforeWrite' }
|
|
142
148
|
| { kind: 'authHook'; event?: AuthHookEvent };
|
|
@@ -150,11 +156,39 @@ export interface FunctionDef {
|
|
|
150
156
|
* functions:write, secrets:write rejected; payments:write / notifications:send /
|
|
151
157
|
* users:* allowed as owner-granted business scopes). */
|
|
152
158
|
scopes?: string[];
|
|
153
|
-
/**
|
|
159
|
+
/** Names of tenant secrets injected into the function at invoke time. */
|
|
154
160
|
secrets?: string[];
|
|
155
|
-
/** Outbound
|
|
161
|
+
/** Outbound host allowlist (deny-by-default egress guard). */
|
|
156
162
|
egressAllow?: string[];
|
|
157
|
-
|
|
163
|
+
/** Per-function resource declarations. Both optional; both clamped server-side.
|
|
164
|
+
* `memoryMb` was REMOVED (2026-09-18): memory is fixed by the managed runtime
|
|
165
|
+
* and is not per-dispatch selectable. A config that still declares it pushes
|
|
166
|
+
* fine — the key is stripped, and `vxil plan --explain` marks it DROPPED. */
|
|
167
|
+
limits?: {
|
|
168
|
+
/** per-dispatch isolate CPU ceiling, ms (5..300000). Only ever TIGHTENS the
|
|
169
|
+
* platform cap.
|
|
170
|
+
*
|
|
171
|
+
* IT ALSO RAISES YOUR BILL on a metered tenant, and that is worth reading
|
|
172
|
+
* twice: it raises (never lowers) the per-invoke credit reserve, whose floor
|
|
173
|
+
* is the manifest default — and the reserve is the CEILING the pro-rated
|
|
174
|
+
* settle clamps into, because the settle can never commit more than the hold
|
|
175
|
+
* it releases from. So an invoke whose wall time exceeds `defaultLimits.cpuMs`
|
|
176
|
+
* (50) settles at its MEASURED wall-ms instead of being capped at 50:
|
|
177
|
+
* declaring `cpuMs: 1000` makes an 800 ms invoke cost 800 instead of 50.
|
|
178
|
+
* Declaring only `timeoutMs` leaves metering byte-identical.
|
|
179
|
+
*
|
|
180
|
+
* It does NOT affect anyone else's availability: the account-wide daily
|
|
181
|
+
* capacity budget counts the platform's own per-invoke estimate, never this
|
|
182
|
+
* value (workers/functions-v1/src/capacity.ts `capacityReserveCpuMs`). */
|
|
183
|
+
cpuMs?: number;
|
|
184
|
+
/** wall budget for ONE outbound fetch this function makes, ms (1000..120000,
|
|
185
|
+
* default 30000). Not a whole-invocation budget: three 25 s fetches still
|
|
186
|
+
* take 75 s. The invocation is bounded separately by the platform's own
|
|
187
|
+
* deadline (`FN_MAX_INVOKE_MS`, default ≥ 5 minutes), which raising this
|
|
188
|
+
* value widens with you. The vxil API callback leg keeps the platform's
|
|
189
|
+
* own 30 s bound. */
|
|
190
|
+
timeoutMs?: number;
|
|
191
|
+
};
|
|
158
192
|
enabled?: boolean;
|
|
159
193
|
/** optional typed contract surfaced by `vxil gen` (Level 1, §4.5). */
|
|
160
194
|
signature?: { input?: unknown; output?: unknown };
|
|
@@ -173,7 +207,7 @@ export interface SeedSpec {
|
|
|
173
207
|
users?: { id: string; email?: string }[];
|
|
174
208
|
}
|
|
175
209
|
|
|
176
|
-
/** The released API majors (docs/
|
|
210
|
+
/** The released API majors (see https://vxil.com/docs/guide/12-going-to-production), a CLOSED union —
|
|
177
211
|
* `'v1'` is the only released major, so `apiVersion: 'v2'` is a COMPILE error
|
|
178
212
|
* until a v2 GAs. `defineConfig` also validates it at runtime against
|
|
179
213
|
* `RELEASED_API_VERSIONS`. */
|
|
@@ -188,7 +222,7 @@ export interface VxilConfig {
|
|
|
188
222
|
/** target environment label; resolved to a baseUrl by `vxil link` / `--env`. */
|
|
189
223
|
env?: string;
|
|
190
224
|
|
|
191
|
-
/** Pin the API major the CLI targets (default `'v1'
|
|
225
|
+
/** Pin the API major the CLI targets (default `'v1'`; see https://vxil.com/docs/guide/12-going-to-production).
|
|
192
226
|
* Path-major, per-platform: a breaking change ships as a new major on a new
|
|
193
227
|
* path with a ≥6-month deprecation window. `VXIL_API_VERSION` overrides this
|
|
194
228
|
* for a one-off invocation. */
|
|
@@ -245,7 +279,7 @@ export function defineConfig(c: VxilConfig): VxilConfig {
|
|
|
245
279
|
throw new Error(
|
|
246
280
|
`vxil.config: apiVersion '${String(c.apiVersion)}' is not a released API major ` +
|
|
247
281
|
`(released: ${RELEASED_API_VERSIONS.map((v) => `'${v}'`).join(', ')}). ` +
|
|
248
|
-
'A new major ships as a new path with its own deprecation window — see docs/
|
|
282
|
+
'A new major ships as a new path with its own deprecation window — see https://vxil.com/docs/guide/12-going-to-production.',
|
|
249
283
|
);
|
|
250
284
|
}
|
|
251
285
|
return c;
|