@vxil/config 0.3.1 → 0.4.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/index.d.ts +72 -22
- package/package.json +2 -2
- package/src/deepPartial.test.ts +24 -0
- package/src/index.ts +75 -20
package/dist/index.d.ts
CHANGED
|
@@ -118,10 +118,27 @@ 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>`. That
|
|
124
|
+
* subscription is AUTO-WIRED on deploy (2026-09-19): the platform reconciles
|
|
125
|
+
* one per function declaring this binding, with `event_prefixes` = the
|
|
126
|
+
* function's `source` values — a hand-made one equal in target + prefixes is
|
|
127
|
+
* adopted. `source` is an event-name PREFIX (e.g. 'payments.') or a full event
|
|
128
|
+
* name — lowercase dotted segments, ≤64 chars, validated at deploy — and is
|
|
129
|
+
* also enforced at delivery: a non-matching event is ACK-200 skipped.
|
|
130
|
+
* Declaring the binding is what opts the function into event delivery; a
|
|
131
|
+
* receiver that declares only `cron` keeps the older cron+{} wake. */
|
|
132
|
+
| {
|
|
122
133
|
kind: 'webhook';
|
|
123
134
|
source: string;
|
|
124
|
-
}
|
|
135
|
+
}
|
|
136
|
+
/** `cmsHook`: fires after a CMS write in `collection`. The platform filters
|
|
137
|
+
* deliveries on the binding (2026-09-19): `beforeCreate` → created only,
|
|
138
|
+
* `beforeUpdate` → updated only, `beforeWrite` → created|updated; a write to
|
|
139
|
+
* another collection never wakes the function. Deleted/bulk events are not
|
|
140
|
+
* delivered to a cmsHook binding. */
|
|
141
|
+
| {
|
|
125
142
|
kind: 'cmsHook';
|
|
126
143
|
collection: string;
|
|
127
144
|
event: 'beforeCreate' | 'beforeUpdate' | 'beforeWrite';
|
|
@@ -142,10 +159,34 @@ export interface FunctionDef {
|
|
|
142
159
|
secrets?: string[];
|
|
143
160
|
/** Outbound host allowlist (deny-by-default egress guard). */
|
|
144
161
|
egressAllow?: string[];
|
|
162
|
+
/** Per-function resource declarations. Both optional; both clamped server-side.
|
|
163
|
+
* `memoryMb` was REMOVED (2026-09-18): memory is fixed by the managed runtime
|
|
164
|
+
* and is not per-dispatch selectable. A config that still declares it pushes
|
|
165
|
+
* fine — the key is stripped, and `vxil plan --explain` marks it DROPPED. */
|
|
145
166
|
limits?: {
|
|
167
|
+
/** per-dispatch isolate CPU ceiling, ms (5..300000). Only ever TIGHTENS the
|
|
168
|
+
* platform cap.
|
|
169
|
+
*
|
|
170
|
+
* IT ALSO RAISES YOUR BILL on a metered tenant, and that is worth reading
|
|
171
|
+
* twice: it raises (never lowers) the per-invoke credit reserve, whose floor
|
|
172
|
+
* is the manifest default — and the reserve is the CEILING the pro-rated
|
|
173
|
+
* settle clamps into, because the settle can never commit more than the hold
|
|
174
|
+
* it releases from. So an invoke whose wall time exceeds `defaultLimits.cpuMs`
|
|
175
|
+
* (50) settles at its MEASURED wall-ms instead of being capped at 50:
|
|
176
|
+
* declaring `cpuMs: 1000` makes an 800 ms invoke cost 800 instead of 50.
|
|
177
|
+
* Declaring only `timeoutMs` leaves metering byte-identical.
|
|
178
|
+
*
|
|
179
|
+
* It does NOT affect anyone else's availability: the account-wide daily
|
|
180
|
+
* capacity budget counts the platform's own per-invoke estimate, never this
|
|
181
|
+
* value (workers/functions-v1/src/capacity.ts `capacityReserveCpuMs`). */
|
|
146
182
|
cpuMs?: number;
|
|
183
|
+
/** wall budget for ONE outbound fetch this function makes, ms (1000..120000,
|
|
184
|
+
* default 30000). Not a whole-invocation budget: three 25 s fetches still
|
|
185
|
+
* take 75 s. The invocation is bounded separately by the platform's own
|
|
186
|
+
* deadline (`FN_MAX_INVOKE_MS`, default ≥ 5 minutes), which raising this
|
|
187
|
+
* value widens with you. The vxil API callback leg keeps the platform's
|
|
188
|
+
* own 30 s bound. */
|
|
147
189
|
timeoutMs?: number;
|
|
148
|
-
memoryMb?: number;
|
|
149
190
|
};
|
|
150
191
|
enabled?: boolean;
|
|
151
192
|
/** optional typed contract surfaced by `vxil gen` (Level 1, §4.5). */
|
|
@@ -180,6 +221,15 @@ export type ApiVersion = 'v1';
|
|
|
180
221
|
* only by a real v2. */
|
|
181
222
|
export declare const RELEASED_API_VERSIONS: readonly ApiVersion[];
|
|
182
223
|
/** The whole vxil backend, declared in one typed, version-controlled file. */
|
|
224
|
+
/** Recursive partial: a `vxil.config.ts` may write any SUBSET of a nested bag
|
|
225
|
+
* (`auth: { otp: { enabled: true } }`) — the server applies the defaults for
|
|
226
|
+
* the rest (Value.Default). A shallow `Partial` made `tsc` reject every
|
|
227
|
+
* partially-written nested bag, including the guide's own skeleton
|
|
228
|
+
* (found by the 2026-09-19 cvskit dry-run). Arrays and primitives are kept
|
|
229
|
+
* as-is; only plain object bags recurse. */
|
|
230
|
+
export type DeepPartial<T> = T extends readonly unknown[] ? T : T extends object ? {
|
|
231
|
+
[K in keyof T]?: DeepPartial<T[K]>;
|
|
232
|
+
} : T;
|
|
183
233
|
export interface VxilConfig {
|
|
184
234
|
/** target environment label; resolved to a baseUrl by `vxil link` / `--env`. */
|
|
185
235
|
env?: string;
|
|
@@ -192,25 +242,25 @@ export interface VxilConfig {
|
|
|
192
242
|
* same Value.Default/Clean pipeline the server uses. Keys match FEATURE_SCHEMAS
|
|
193
243
|
* exactly (note the hyphenated ids). */
|
|
194
244
|
features?: {
|
|
195
|
-
notifications?:
|
|
196
|
-
jobs?:
|
|
197
|
-
auth?:
|
|
198
|
-
'rate-limits'?:
|
|
199
|
-
files?:
|
|
200
|
-
webhooks?:
|
|
201
|
-
comments?:
|
|
202
|
-
cms?:
|
|
203
|
-
mcp?:
|
|
204
|
-
realtime?:
|
|
205
|
-
presence?:
|
|
206
|
-
orgs?:
|
|
207
|
-
'activity-feed'?:
|
|
208
|
-
'vector-search'?:
|
|
209
|
-
ai?:
|
|
210
|
-
rag?:
|
|
211
|
-
payments?:
|
|
212
|
-
functions?:
|
|
213
|
-
copilot?:
|
|
245
|
+
notifications?: DeepPartial<NotificationsConfig>;
|
|
246
|
+
jobs?: DeepPartial<JobsConfig>;
|
|
247
|
+
auth?: DeepPartial<AuthConfig>;
|
|
248
|
+
'rate-limits'?: DeepPartial<RateLimitsConfig>;
|
|
249
|
+
files?: DeepPartial<FilesConfig>;
|
|
250
|
+
webhooks?: DeepPartial<WebhooksConfig>;
|
|
251
|
+
comments?: DeepPartial<CommentsConfig>;
|
|
252
|
+
cms?: DeepPartial<CmsConfig>;
|
|
253
|
+
mcp?: DeepPartial<McpConfig>;
|
|
254
|
+
realtime?: DeepPartial<RealtimeConfig>;
|
|
255
|
+
presence?: DeepPartial<PresenceConfig>;
|
|
256
|
+
orgs?: DeepPartial<OrgsConfig>;
|
|
257
|
+
'activity-feed'?: DeepPartial<ActivityFeedConfig>;
|
|
258
|
+
'vector-search'?: DeepPartial<VectorSearchConfig>;
|
|
259
|
+
ai?: DeepPartial<AiConfig>;
|
|
260
|
+
rag?: DeepPartial<RagConfig>;
|
|
261
|
+
payments?: DeepPartial<PaymentsConfig>;
|
|
262
|
+
functions?: DeepPartial<FunctionsConfig>;
|
|
263
|
+
copilot?: DeepPartial<CopilotConfig>;
|
|
214
264
|
};
|
|
215
265
|
/** (b) CMS SCHEMA-AS-CODE — collections + fields. `push` reconciles them
|
|
216
266
|
* additively against GET /v1/cms/collections (no destructive drops without
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vxil/config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
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",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"src"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@vxil/feature-configs": "0.
|
|
25
|
+
"@vxil/feature-configs": "0.4.1"
|
|
26
26
|
},
|
|
27
27
|
"publishConfig": {
|
|
28
28
|
"access": "public"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// A `vxil.config.ts` may write any SUBSET of a nested feature bag; the server
|
|
2
|
+
// defaults the rest. This file is COMPILED by the package typecheck, so a
|
|
3
|
+
// regression to a shallow Partial fails `tsc`, not just a runtime assertion
|
|
4
|
+
// (the 2026-09-19 cvskit dry-run found the guide's own skeleton red under tsc).
|
|
5
|
+
import { describe, expect, it } from 'vitest';
|
|
6
|
+
import type { DeepPartial, VxilConfig } from './index.js';
|
|
7
|
+
|
|
8
|
+
const partial: VxilConfig = {
|
|
9
|
+
features: {
|
|
10
|
+
auth: { otp: { enabled: true }, anonymous: { enabled: true } },
|
|
11
|
+
files: { ttl: { enabled: true, defaultExpiresInSeconds: 2592000 } },
|
|
12
|
+
ai: { defaults: { model: 'x' } },
|
|
13
|
+
cms: { limits: { maxItemsPerCollection: 5 } },
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
describe('DeepPartial', () => {
|
|
18
|
+
it('accepts partially-written nested bags (compile-time) and leaves arrays alone', () => {
|
|
19
|
+
type T = DeepPartial<{ a: { b: number; c: string }; list: string[] }>;
|
|
20
|
+
const v: T = { a: { b: 1 }, list: ['x'] };
|
|
21
|
+
expect(v.a?.b).toBe(1);
|
|
22
|
+
expect(partial.features?.auth?.otp?.enabled).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -137,7 +137,22 @@ 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>`. That
|
|
142
|
+
* subscription is AUTO-WIRED on deploy (2026-09-19): the platform reconciles
|
|
143
|
+
* one per function declaring this binding, with `event_prefixes` = the
|
|
144
|
+
* function's `source` values — a hand-made one equal in target + prefixes is
|
|
145
|
+
* adopted. `source` is an event-name PREFIX (e.g. 'payments.') or a full event
|
|
146
|
+
* name — lowercase dotted segments, ≤64 chars, validated at deploy — and is
|
|
147
|
+
* also enforced at delivery: a non-matching event is ACK-200 skipped.
|
|
148
|
+
* Declaring the binding is what opts the function into event delivery; a
|
|
149
|
+
* receiver that declares only `cron` keeps the older cron+{} wake. */
|
|
140
150
|
| { kind: 'webhook'; source: string }
|
|
151
|
+
/** `cmsHook`: fires after a CMS write in `collection`. The platform filters
|
|
152
|
+
* deliveries on the binding (2026-09-19): `beforeCreate` → created only,
|
|
153
|
+
* `beforeUpdate` → updated only, `beforeWrite` → created|updated; a write to
|
|
154
|
+
* another collection never wakes the function. Deleted/bulk events are not
|
|
155
|
+
* delivered to a cmsHook binding. */
|
|
141
156
|
| { kind: 'cmsHook'; collection: string; event: 'beforeCreate' | 'beforeUpdate' | 'beforeWrite' }
|
|
142
157
|
| { kind: 'authHook'; event?: AuthHookEvent };
|
|
143
158
|
|
|
@@ -154,7 +169,35 @@ export interface FunctionDef {
|
|
|
154
169
|
secrets?: string[];
|
|
155
170
|
/** Outbound host allowlist (deny-by-default egress guard). */
|
|
156
171
|
egressAllow?: string[];
|
|
157
|
-
|
|
172
|
+
/** Per-function resource declarations. Both optional; both clamped server-side.
|
|
173
|
+
* `memoryMb` was REMOVED (2026-09-18): memory is fixed by the managed runtime
|
|
174
|
+
* and is not per-dispatch selectable. A config that still declares it pushes
|
|
175
|
+
* fine — the key is stripped, and `vxil plan --explain` marks it DROPPED. */
|
|
176
|
+
limits?: {
|
|
177
|
+
/** per-dispatch isolate CPU ceiling, ms (5..300000). Only ever TIGHTENS the
|
|
178
|
+
* platform cap.
|
|
179
|
+
*
|
|
180
|
+
* IT ALSO RAISES YOUR BILL on a metered tenant, and that is worth reading
|
|
181
|
+
* twice: it raises (never lowers) the per-invoke credit reserve, whose floor
|
|
182
|
+
* is the manifest default — and the reserve is the CEILING the pro-rated
|
|
183
|
+
* settle clamps into, because the settle can never commit more than the hold
|
|
184
|
+
* it releases from. So an invoke whose wall time exceeds `defaultLimits.cpuMs`
|
|
185
|
+
* (50) settles at its MEASURED wall-ms instead of being capped at 50:
|
|
186
|
+
* declaring `cpuMs: 1000` makes an 800 ms invoke cost 800 instead of 50.
|
|
187
|
+
* Declaring only `timeoutMs` leaves metering byte-identical.
|
|
188
|
+
*
|
|
189
|
+
* It does NOT affect anyone else's availability: the account-wide daily
|
|
190
|
+
* capacity budget counts the platform's own per-invoke estimate, never this
|
|
191
|
+
* value (workers/functions-v1/src/capacity.ts `capacityReserveCpuMs`). */
|
|
192
|
+
cpuMs?: number;
|
|
193
|
+
/** wall budget for ONE outbound fetch this function makes, ms (1000..120000,
|
|
194
|
+
* default 30000). Not a whole-invocation budget: three 25 s fetches still
|
|
195
|
+
* take 75 s. The invocation is bounded separately by the platform's own
|
|
196
|
+
* deadline (`FN_MAX_INVOKE_MS`, default ≥ 5 minutes), which raising this
|
|
197
|
+
* value widens with you. The vxil API callback leg keeps the platform's
|
|
198
|
+
* own 30 s bound. */
|
|
199
|
+
timeoutMs?: number;
|
|
200
|
+
};
|
|
158
201
|
enabled?: boolean;
|
|
159
202
|
/** optional typed contract surfaced by `vxil gen` (Level 1, §4.5). */
|
|
160
203
|
signature?: { input?: unknown; output?: unknown };
|
|
@@ -184,6 +227,18 @@ export type ApiVersion = 'v1';
|
|
|
184
227
|
export const RELEASED_API_VERSIONS: readonly ApiVersion[] = ['v1'];
|
|
185
228
|
|
|
186
229
|
/** The whole vxil backend, declared in one typed, version-controlled file. */
|
|
230
|
+
/** Recursive partial: a `vxil.config.ts` may write any SUBSET of a nested bag
|
|
231
|
+
* (`auth: { otp: { enabled: true } }`) — the server applies the defaults for
|
|
232
|
+
* the rest (Value.Default). A shallow `Partial` made `tsc` reject every
|
|
233
|
+
* partially-written nested bag, including the guide's own skeleton
|
|
234
|
+
* (found by the 2026-09-19 cvskit dry-run). Arrays and primitives are kept
|
|
235
|
+
* as-is; only plain object bags recurse. */
|
|
236
|
+
export type DeepPartial<T> = T extends readonly unknown[]
|
|
237
|
+
? T
|
|
238
|
+
: T extends object
|
|
239
|
+
? { [K in keyof T]?: DeepPartial<T[K]> }
|
|
240
|
+
: T;
|
|
241
|
+
|
|
187
242
|
export interface VxilConfig {
|
|
188
243
|
/** target environment label; resolved to a baseUrl by `vxil link` / `--env`. */
|
|
189
244
|
env?: string;
|
|
@@ -198,25 +253,25 @@ export interface VxilConfig {
|
|
|
198
253
|
* same Value.Default/Clean pipeline the server uses. Keys match FEATURE_SCHEMAS
|
|
199
254
|
* exactly (note the hyphenated ids). */
|
|
200
255
|
features?: {
|
|
201
|
-
notifications?:
|
|
202
|
-
jobs?:
|
|
203
|
-
auth?:
|
|
204
|
-
'rate-limits'?:
|
|
205
|
-
files?:
|
|
206
|
-
webhooks?:
|
|
207
|
-
comments?:
|
|
208
|
-
cms?:
|
|
209
|
-
mcp?:
|
|
210
|
-
realtime?:
|
|
211
|
-
presence?:
|
|
212
|
-
orgs?:
|
|
213
|
-
'activity-feed'?:
|
|
214
|
-
'vector-search'?:
|
|
215
|
-
ai?:
|
|
216
|
-
rag?:
|
|
217
|
-
payments?:
|
|
218
|
-
functions?:
|
|
219
|
-
copilot?:
|
|
256
|
+
notifications?: DeepPartial<NotificationsConfig>;
|
|
257
|
+
jobs?: DeepPartial<JobsConfig>;
|
|
258
|
+
auth?: DeepPartial<AuthConfig>;
|
|
259
|
+
'rate-limits'?: DeepPartial<RateLimitsConfig>;
|
|
260
|
+
files?: DeepPartial<FilesConfig>;
|
|
261
|
+
webhooks?: DeepPartial<WebhooksConfig>;
|
|
262
|
+
comments?: DeepPartial<CommentsConfig>;
|
|
263
|
+
cms?: DeepPartial<CmsConfig>;
|
|
264
|
+
mcp?: DeepPartial<McpConfig>;
|
|
265
|
+
realtime?: DeepPartial<RealtimeConfig>;
|
|
266
|
+
presence?: DeepPartial<PresenceConfig>;
|
|
267
|
+
orgs?: DeepPartial<OrgsConfig>;
|
|
268
|
+
'activity-feed'?: DeepPartial<ActivityFeedConfig>;
|
|
269
|
+
'vector-search'?: DeepPartial<VectorSearchConfig>;
|
|
270
|
+
ai?: DeepPartial<AiConfig>;
|
|
271
|
+
rag?: DeepPartial<RagConfig>;
|
|
272
|
+
payments?: DeepPartial<PaymentsConfig>;
|
|
273
|
+
functions?: DeepPartial<FunctionsConfig>;
|
|
274
|
+
copilot?: DeepPartial<CopilotConfig>;
|
|
220
275
|
};
|
|
221
276
|
|
|
222
277
|
/** (b) CMS SCHEMA-AS-CODE — collections + fields. `push` reconciles them
|