@vxil/config 0.5.1 → 0.6.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
@@ -106,6 +106,26 @@ export interface CollectionDef {
106
106
  * session.revoked — auth.session.revoked { user_id, session_id, reason }
107
107
  * signin.failure — auth.signin.failure { email_hash | user_id, reason } */
108
108
  export type AuthHookEvent = 'user.created' | 'session.created' | 'session.revoked' | 'signin.failure';
109
+ /** The opt-in re-delivery of a platform-delivered trigger (F33, 2026-09-25).
110
+ * DEFAULT OFF: the platform acknowledges every delivery with a 200 whatever
111
+ * the handler returned — an application error is observed (`functions.run.failed`),
112
+ * never re-delivered. With `retry` set on a `queue` / `webhook` / `cmsHook` /
113
+ * `authHook` trigger, a non-2xx answer of the handler is handed back to the
114
+ * jobs retry ladder instead (60 s, 120 s, … backoff) and the last failed
115
+ * attempt dead-letters the run (`job.dead_lettered`, replayable from the
116
+ * Jobs page). Each attempt is a billed invocation and carries the SAME
117
+ * `idempotency_key` — dedupe on it. Effective attempts =
118
+ * min(maxAttempts, the project's `jobs.retry.defaultMaxAttempts`, default 5).
119
+ * A handler that partially writes and then fails re-fires its downstream
120
+ * events on EVERY attempt, so the chain is a tree (up to maxAttempts children
121
+ * per hop): the causal-depth guard bounds its length (32 hops), not its size —
122
+ * the breaker, the loop guard, the dead-letter quota and the daily share do.
123
+ * Write only once you will return 2xx, or dedupe on idempotency_key. Not accepted on `http` (it returns its
124
+ * real status to its caller) or `cron` (a retry would overlap the next tick). */
125
+ export interface FunctionTriggerRetry {
126
+ /** 1–5 attempts in all (1 = a failed attempt dead-letters at once, no re-delivery) */
127
+ maxAttempts: number;
128
+ }
109
129
  /** A function trigger (the §7.3 crossing). cmsHook fires on a CMS write;
110
130
  * authHook fires on ONE auth lifecycle event (default `user.created` — the
111
131
  * post-signup hook; at-least-once, ~1min fanout latency). */
@@ -118,6 +138,7 @@ export type FunctionTrigger = {
118
138
  } | {
119
139
  kind: 'queue';
120
140
  source: string;
141
+ retry?: FunctionTriggerRetry;
121
142
  }
122
143
  /** `webhook`: the function is woken by a webhook_subscription fan-out whose
123
144
  * target_url is `<edge>/v1/internal/fn/trigger/<name>?tenant=<id>`. That
@@ -132,6 +153,7 @@ export type FunctionTrigger = {
132
153
  | {
133
154
  kind: 'webhook';
134
155
  source: string;
156
+ retry?: FunctionTriggerRetry;
135
157
  }
136
158
  /** `cmsHook`: fires after a CMS write in `collection`. The platform filters
137
159
  * deliveries on the binding (2026-09-19): `beforeCreate` → created only,
@@ -142,15 +164,26 @@ export type FunctionTrigger = {
142
164
  kind: 'cmsHook';
143
165
  collection: string;
144
166
  event: 'beforeCreate' | 'beforeUpdate' | 'beforeWrite';
167
+ retry?: FunctionTriggerRetry;
145
168
  } | {
146
169
  kind: 'authHook';
147
170
  event?: AuthHookEvent;
171
+ retry?: FunctionTriggerRetry;
148
172
  };
149
173
  /** A deployed tenant function — source in functions/, deployed on `vxil push`. */
150
174
  export interface FunctionDef {
151
175
  /** path to the ES-module entrypoint, e.g. ./functions/<name>.ts */
152
176
  entry: string;
153
177
  trigger?: FunctionTrigger;
178
+ /** Further bindings beside `trigger` (2026-09-25): a function is code + a SET
179
+ * of trigger bindings, so one bundle can be reached by several front doors —
180
+ * a webhook AND a cron, an http door AND a cmsHook. `trigger` and `triggers`
181
+ * may both be present; the deploy carries them in that order as
182
+ * `bindings[]`, at most 8 in all (the CLI refuses a 9th before the wire).
183
+ * Every binding lands the same isolate; only the envelope's `trigger` /
184
+ * `payload` differ. Each platform-delivered binding is billed per
185
+ * invocation and bounded by the loop guard and the breaker. */
186
+ triggers?: FunctionTrigger[];
154
187
  /** clamped by DENY_FUNCTION_SCOPES at deploy (admin, wildcard, features:write,
155
188
  * functions:write, secrets:write rejected; payments:write / notifications:send /
156
189
  * users:* allowed as owner-granted business scopes). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vxil/config",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
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.5.1"
25
+ "@vxil/feature-configs": "0.6.0"
26
26
  },
27
27
  "publishConfig": {
28
28
  "access": "public"
package/src/index.ts CHANGED
@@ -130,13 +130,34 @@ export interface CollectionDef {
130
130
  * signin.failure — auth.signin.failure { email_hash | user_id, reason } */
131
131
  export type AuthHookEvent = 'user.created' | 'session.created' | 'session.revoked' | 'signin.failure';
132
132
 
133
+ /** The opt-in re-delivery of a platform-delivered trigger (F33, 2026-09-25).
134
+ * DEFAULT OFF: the platform acknowledges every delivery with a 200 whatever
135
+ * the handler returned — an application error is observed (`functions.run.failed`),
136
+ * never re-delivered. With `retry` set on a `queue` / `webhook` / `cmsHook` /
137
+ * `authHook` trigger, a non-2xx answer of the handler is handed back to the
138
+ * jobs retry ladder instead (60 s, 120 s, … backoff) and the last failed
139
+ * attempt dead-letters the run (`job.dead_lettered`, replayable from the
140
+ * Jobs page). Each attempt is a billed invocation and carries the SAME
141
+ * `idempotency_key` — dedupe on it. Effective attempts =
142
+ * min(maxAttempts, the project's `jobs.retry.defaultMaxAttempts`, default 5).
143
+ * A handler that partially writes and then fails re-fires its downstream
144
+ * events on EVERY attempt, so the chain is a tree (up to maxAttempts children
145
+ * per hop): the causal-depth guard bounds its length (32 hops), not its size —
146
+ * the breaker, the loop guard, the dead-letter quota and the daily share do.
147
+ * Write only once you will return 2xx, or dedupe on idempotency_key. Not accepted on `http` (it returns its
148
+ * real status to its caller) or `cron` (a retry would overlap the next tick). */
149
+ export interface FunctionTriggerRetry {
150
+ /** 1–5 attempts in all (1 = a failed attempt dead-letters at once, no re-delivery) */
151
+ maxAttempts: number;
152
+ }
153
+
133
154
  /** A function trigger (the §7.3 crossing). cmsHook fires on a CMS write;
134
155
  * authHook fires on ONE auth lifecycle event (default `user.created` — the
135
156
  * post-signup hook; at-least-once, ~1min fanout latency). */
136
157
  export type FunctionTrigger =
137
158
  | { kind: 'http'; path?: string }
138
159
  | { kind: 'cron'; schedule: string }
139
- | { kind: 'queue'; source: string }
160
+ | { kind: 'queue'; source: string; retry?: FunctionTriggerRetry }
140
161
  /** `webhook`: the function is woken by a webhook_subscription fan-out whose
141
162
  * target_url is `<edge>/v1/internal/fn/trigger/<name>?tenant=<id>`. That
142
163
  * subscription is AUTO-WIRED on deploy (2026-09-19): the platform reconciles
@@ -147,20 +168,29 @@ export type FunctionTrigger =
147
168
  * also enforced at delivery: a non-matching event is ACK-200 skipped.
148
169
  * Declaring the binding is what opts the function into event delivery; a
149
170
  * receiver that declares only `cron` keeps the older cron+{} wake. */
150
- | { kind: 'webhook'; source: string }
171
+ | { kind: 'webhook'; source: string; retry?: FunctionTriggerRetry }
151
172
  /** `cmsHook`: fires after a CMS write in `collection`. The platform filters
152
173
  * deliveries on the binding (2026-09-19): `beforeCreate` → created only,
153
174
  * `beforeUpdate` → updated only, `beforeWrite` → created|updated; a write to
154
175
  * another collection never wakes the function. Deleted/bulk events are not
155
176
  * delivered to a cmsHook binding. */
156
- | { kind: 'cmsHook'; collection: string; event: 'beforeCreate' | 'beforeUpdate' | 'beforeWrite' }
157
- | { kind: 'authHook'; event?: AuthHookEvent };
177
+ | { kind: 'cmsHook'; collection: string; event: 'beforeCreate' | 'beforeUpdate' | 'beforeWrite'; retry?: FunctionTriggerRetry }
178
+ | { kind: 'authHook'; event?: AuthHookEvent; retry?: FunctionTriggerRetry };
158
179
 
159
180
  /** A deployed tenant function — source in functions/, deployed on `vxil push`. */
160
181
  export interface FunctionDef {
161
182
  /** path to the ES-module entrypoint, e.g. ./functions/<name>.ts */
162
183
  entry: string;
163
184
  trigger?: FunctionTrigger;
185
+ /** Further bindings beside `trigger` (2026-09-25): a function is code + a SET
186
+ * of trigger bindings, so one bundle can be reached by several front doors —
187
+ * a webhook AND a cron, an http door AND a cmsHook. `trigger` and `triggers`
188
+ * may both be present; the deploy carries them in that order as
189
+ * `bindings[]`, at most 8 in all (the CLI refuses a 9th before the wire).
190
+ * Every binding lands the same isolate; only the envelope's `trigger` /
191
+ * `payload` differ. Each platform-delivered binding is billed per
192
+ * invocation and bounded by the loop guard and the breaker. */
193
+ triggers?: FunctionTrigger[];
164
194
  /** clamped by DENY_FUNCTION_SCOPES at deploy (admin, wildcard, features:write,
165
195
  * functions:write, secrets:write rejected; payments:write / notifications:send /
166
196
  * users:* allowed as owner-granted business scopes). */