@vxil/sdk 0.13.0 → 0.14.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 +255 -103
- package/dist/index.js +125 -58
- package/dist/qs.js +1 -1
- package/dist/reporting.js +1 -2
- package/dist/retry.js +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// (code/message/hint/fixUrl + request id) so callers — humans or agents —
|
|
4
4
|
// can self-correct.
|
|
5
5
|
/** The released API majors, as a CLOSED union — one per released major (docs/
|
|
6
|
-
*
|
|
6
|
+
* guide ch. 5). `'v1'` is the only released major, so pinning
|
|
7
7
|
* anything else (`apiVersion: 'v2'`) is a COMPILE error until a v2 GAs and
|
|
8
8
|
* widens this union. Every path the SDK builds is major-versioned; the default
|
|
9
9
|
* is the compile-time constant `'v1'` (never a floating `latest` alias). */
|
|
@@ -136,7 +136,7 @@ function resolveBase(explicit) {
|
|
|
136
136
|
return (explicit ?? envBase ?? DEFAULT_BASE).replace(/\/$/, '');
|
|
137
137
|
}
|
|
138
138
|
/** Build the anonymous, KEYLESS public-delivery URL for a collection's PUBLISHED
|
|
139
|
-
* items (
|
|
139
|
+
* items (guide ch. 4, "Draft and publish") — `{base}/v1/cms/public/:tenantId/:collection[?…]`. This is
|
|
140
140
|
* the reader path a public blog/storefront/docs site hits with NO api key: the
|
|
141
141
|
* edge mints a restricted read-only inner token bound to the URL tenant, forces
|
|
142
142
|
* `status='published'`, and strips the collection's owner_field. PURE (no
|
|
@@ -155,7 +155,7 @@ export function cmsPublicUrl(tenantId, collection, query, opts) {
|
|
|
155
155
|
return `${base}${path}${s}`;
|
|
156
156
|
}
|
|
157
157
|
/** Fetch a page of a collection's PUBLISHED items over the KEYLESS public lane
|
|
158
|
-
* (
|
|
158
|
+
* (guide ch. 4) — NO api key, no `Vxil` client, no auth of any kind. This is the
|
|
159
159
|
* anonymous reader path (a blog/storefront/docs front-end). Returns the same
|
|
160
160
|
* `{ items, next_cursor }` envelope the authed list does, but rows are stripped
|
|
161
161
|
* to the public surface (`CmsPublicRow`) — drafts and the owner_field are never
|
|
@@ -182,6 +182,36 @@ export async function listCmsPublic(tenantId, collection, query, opts) {
|
|
|
182
182
|
}
|
|
183
183
|
return parsed.data ?? { items: [], next_cursor: null };
|
|
184
184
|
}
|
|
185
|
+
/** POST a run's single-use signed `callback_url` (guide ch. 6, "Hand a run to
|
|
186
|
+
* an external worker") — KEYLESS: no API key, no `Vxil` client; the URL is the
|
|
187
|
+
* credential, so call this from the worker that finishes the job (a render
|
|
188
|
+
* farm, a GPU box, another queue's task). Idempotent: a repeat of a used URL
|
|
189
|
+
* answers `deduplicated: true`. A non-2xx throws `VxilError` (401 a forged or
|
|
190
|
+
* altered URL, 410 `callback_expired`, 413 `result_too_large`, 404 a run that
|
|
191
|
+
* did not opt in). */
|
|
192
|
+
export async function postRunCallback(callbackUrl, body, opts) {
|
|
193
|
+
const fetchImpl = opts?.fetch ?? fetch;
|
|
194
|
+
const res = await fetchImpl(callbackUrl, {
|
|
195
|
+
method: 'POST',
|
|
196
|
+
headers: { 'content-type': 'application/json' },
|
|
197
|
+
body: JSON.stringify(body),
|
|
198
|
+
});
|
|
199
|
+
const text = await res.text();
|
|
200
|
+
let parsed = {};
|
|
201
|
+
if (text.length > 0) {
|
|
202
|
+
try {
|
|
203
|
+
parsed = JSON.parse(text);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
throw new VxilError(res.status, `http_${res.status}`, text.slice(0, 200), undefined, undefined, undefined, parseRetryAfter(res.headers.get('retry-after')));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (!res.ok || parsed.error || !parsed.data) {
|
|
210
|
+
const e = parsed.error ?? { code: `http_${res.status}`, message: text.slice(0, 200) };
|
|
211
|
+
throw new VxilError(res.status, e.code, e.message, e.hint, e.fixUrl, parsed.meta?.request_id, parseRetryAfter(res.headers.get('retry-after')));
|
|
212
|
+
}
|
|
213
|
+
return parsed.data;
|
|
214
|
+
}
|
|
185
215
|
export class Vxil {
|
|
186
216
|
base;
|
|
187
217
|
key;
|
|
@@ -254,7 +284,7 @@ export class Vxil {
|
|
|
254
284
|
static connect(opts) {
|
|
255
285
|
return new Vxil(opts);
|
|
256
286
|
}
|
|
257
|
-
/** Typed per-collection CMS handle (
|
|
287
|
+
/** Typed per-collection CMS handle (guide ch. 5). A thin wrapper over the
|
|
258
288
|
* generic `cms.items.*` methods — the wire calls are identical; the generated
|
|
259
289
|
* `VxilSchema` supplies the field types. `vx.cms.items.*` stays as the
|
|
260
290
|
* always-available un-generic fallback. */
|
|
@@ -291,7 +321,7 @@ export class Vxil {
|
|
|
291
321
|
rank: (q) => this.cms.items.rank(c, q),
|
|
292
322
|
};
|
|
293
323
|
}
|
|
294
|
-
/** Typed function invoke (
|
|
324
|
+
/** Typed function invoke (guide ch. 8). `vx.fn.<name>(payload)` POSTs to
|
|
295
325
|
* /v1/fn/<name>; the generated `VxilSchema` types Input/Output (Level-0
|
|
296
326
|
* opaque until a function declares a signature). A Proxy gives the
|
|
297
327
|
* `vx.fn.<name>` accessor shape without enumerating names at runtime.
|
|
@@ -359,7 +389,7 @@ export class Vxil {
|
|
|
359
389
|
});
|
|
360
390
|
// An empty body on a 2xx is a valid "no content" success (e.g. 204 from
|
|
361
391
|
// DELETE/markRead) — never an error. Only parse when there are bytes; a
|
|
362
|
-
// void-returning caller ignores `data` anyway.
|
|
392
|
+
// void-returning caller ignores `data` anyway.
|
|
363
393
|
let parsed = {};
|
|
364
394
|
if (text.length > 0) {
|
|
365
395
|
try {
|
|
@@ -395,7 +425,7 @@ export class Vxil {
|
|
|
395
425
|
* (email/display_name/avatar_url/attributes) is scrubbed, while the id is
|
|
396
426
|
* kept so cross-feature references stay intact. NOTE: a full account-delete
|
|
397
427
|
* flow also calls the auth half — POST /v1/auth/users/:id/erase — to erase
|
|
398
|
-
* the credential/session identity (see
|
|
428
|
+
* the credential/session identity (see guide ch. 6, auth).
|
|
399
429
|
*/
|
|
400
430
|
delete: async (id, opts) => {
|
|
401
431
|
await this.call('DELETE', `/v1/users/${encodeURIComponent(id)}${opts?.erase ? '?erase=true' : ''}`);
|
|
@@ -497,7 +527,7 @@ export class Vxil {
|
|
|
497
527
|
* does not cover them, so an "unsubscribe from everything" click can never
|
|
498
528
|
* lock a user out of its own account. Muteable ids: `welcome`,
|
|
499
529
|
* `transactional`. (Password-reset mail rides `transactional`, which stays
|
|
500
|
-
* muteable — see
|
|
530
|
+
* muteable — see guide ch. 6, notifications.)
|
|
501
531
|
*
|
|
502
532
|
* SCOPES: with an end-user token both calls are confined to the verified
|
|
503
533
|
* principal and take `notifications:read` (a browser key that can mute its
|
|
@@ -524,7 +554,7 @@ export class Vxil {
|
|
|
524
554
|
},
|
|
525
555
|
/** A single delivery by id (the list is `deliveries()`). */
|
|
526
556
|
delivery: async (deliveryId) => (await this.call('GET', `/v1/notifications/deliveries/${encodeURIComponent(deliveryId)}`)).data,
|
|
527
|
-
/** Email broadcast campaigns (
|
|
557
|
+
/** Email broadcast campaigns (guide ch. 6, notifications): audience-ref fan-out
|
|
528
558
|
* with quiet-hours + frequency-cap policy. `schedule_cron` sets a recurring
|
|
529
559
|
* send (status `scheduled`); omit it for a `draft`. */
|
|
530
560
|
campaigns: {
|
|
@@ -557,12 +587,12 @@ export class Vxil {
|
|
|
557
587
|
/** The tenant's enabled-feature summary (GET /v1/features). Privilege-
|
|
558
588
|
* independent: any valid key may read it — it leaks no secrets, only which
|
|
559
589
|
* features the tenant has turned on. Mirrors what the MCP tool-list
|
|
560
|
-
* aggregation sees.
|
|
590
|
+
* aggregation sees. */
|
|
561
591
|
list: async () => (await this.call('GET', '/v1/features')).data.features,
|
|
562
592
|
/** The FULL enabled-feature summary: `features` + `api_versions` (released
|
|
563
593
|
* API majors per feature) + the additive `key` block — the CALLING key's
|
|
564
594
|
* identity and per-tool MCP permissions (allowed_tools/denied_tools
|
|
565
|
-
* patterns
|
|
595
|
+
* patterns; guide ch. 10). `key` is absent for non-key
|
|
566
596
|
* callers and for keys without explicit tool perms. `list()` stays the
|
|
567
597
|
* stable flat-array shorthand. */
|
|
568
598
|
summary: async () => (await this.call('GET', '/v1/features')).data,
|
|
@@ -610,7 +640,7 @@ export class Vxil {
|
|
|
610
640
|
if (!res.ok) {
|
|
611
641
|
// Preserve the structured error envelope on failure — the export path
|
|
612
642
|
// is hand-rolled (no this.call), so without this a 403/429 would lose
|
|
613
|
-
// its code/hint/requestId.
|
|
643
|
+
// its code/hint/requestId.
|
|
614
644
|
let env = {};
|
|
615
645
|
try {
|
|
616
646
|
env = JSON.parse(text);
|
|
@@ -621,7 +651,7 @@ export class Vxil {
|
|
|
621
651
|
}
|
|
622
652
|
// Skip blank lines AND tolerate a single malformed NDJSON line rather
|
|
623
653
|
// than aborting the whole batch (e.g. a truncated final line on a large
|
|
624
|
-
// page) — collect parse failures so the caller can decide.
|
|
654
|
+
// page) — collect parse failures so the caller can decide.
|
|
625
655
|
const events = [];
|
|
626
656
|
const parseErrors = [];
|
|
627
657
|
text.split('\n').forEach((l, i) => {
|
|
@@ -651,13 +681,20 @@ export class Vxil {
|
|
|
651
681
|
jobs = {
|
|
652
682
|
/** Enqueue a one-off job; Vxil POSTs a signed callback to target_url with
|
|
653
683
|
* retries. deliver_after (ISO) / delay_seconds (≤ 30 d, at most one of the
|
|
654
|
-
* two) defer the first delivery; > 12 h returns state 'delayed'.
|
|
684
|
+
* two) defer the first delivery; > 12 h returns state 'delayed'.
|
|
685
|
+
*
|
|
686
|
+
* `callback: true` (or `{ ttl_seconds }`, 60 s..31 d; default 24 h) opts
|
|
687
|
+
* the run in to a KEYLESS signed `callback_url` (in the answer and on every
|
|
688
|
+
* delivery): an external worker POSTs it to complete / fail the run, report
|
|
689
|
+
* progress, or wake a `wait`. A handler that answers 202 HANDS the run off
|
|
690
|
+
* — it waits for that callback (dead-lettered as `CallbackTimeout` when the
|
|
691
|
+
* lifetime passes). See `postRunCallback`. */
|
|
655
692
|
enqueue: async (input) => (await this.call('POST', '/v1/jobs/enqueue', input)).data,
|
|
656
693
|
/** Atomic multi-enqueue (≤100 items; any invalid item rejects the whole
|
|
657
694
|
* batch). Each item = the enqueue input, incl. per-item idempotency_key
|
|
658
695
|
* and deliver_after/delay_seconds. Results align with the input order. */
|
|
659
696
|
enqueueBatch: async (items) => (await this.call('POST', '/v1/jobs/enqueue-batch', { jobs: items })).data,
|
|
660
|
-
/** Enqueue a long-running EXTERNAL generation run (
|
|
697
|
+
/** Enqueue a long-running EXTERNAL generation run (guide ch. 6, jobs): Vxil calls
|
|
661
698
|
* the provider (BYO key), tracks completion via poll/webhook, mirrors a typed
|
|
662
699
|
* generation_status onto a tenant record, enforces a built-in timeout, and
|
|
663
700
|
* (on failure) fires the payments credit-reversal.
|
|
@@ -666,7 +703,7 @@ export class Vxil {
|
|
|
666
703
|
* of the same `idempotency_key` (`deduplicated: true`) carries the run's
|
|
667
704
|
* status NOW (`processing`, `completed` or `failed` too).
|
|
668
705
|
*
|
|
669
|
-
* `reserve_credits`
|
|
706
|
+
* `reserve_credits` takes a PROVISIONAL held credit debit at enqueue
|
|
670
707
|
* (linked to the run), committed on `completed` and reversed on
|
|
671
708
|
* failed/timeout/DLQ. `amount` is positive-only and CLAMPED to the platform
|
|
672
709
|
* `config.generation.maxReserveCredits` cap; in end-user mode `user_id` is
|
|
@@ -717,11 +754,12 @@ export class Vxil {
|
|
|
717
754
|
replay: async (runId) => (await this.call('POST', `/v1/jobs/runs/${encodeURIComponent(runId)}/replay`)).data,
|
|
718
755
|
/**
|
|
719
756
|
* Suspend the RUNNING run until an event (call from the executing
|
|
720
|
-
* handler, then return 200 — the suspension wins).
|
|
757
|
+
* handler, then return 200 — the suspension wins). `state: 'resumed'` =
|
|
758
|
+
* the event already fired (continue; `wakeup` is its payload). A run
|
|
759
|
+
* enqueued with `callback` also gets its `callback_url`: an external worker
|
|
760
|
+
* POSTing it wakes this wait with no API key.
|
|
721
761
|
*/
|
|
722
|
-
wait: async (runId, input) => {
|
|
723
|
-
await this.call('POST', `/v1/jobs/runs/${encodeURIComponent(runId)}/wait`, input);
|
|
724
|
-
},
|
|
762
|
+
wait: async (runId, input) => (await this.call('POST', `/v1/jobs/runs/${encodeURIComponent(runId)}/wait`, input)).data,
|
|
725
763
|
/** Wake every run waiting on the event. */
|
|
726
764
|
emitEvent: async (event, payload) => (await this.call('POST', '/v1/jobs/events', { event, ...(payload ? { payload } : {}) })).data.woken,
|
|
727
765
|
/** Secret for verifying X-Vxil-Jobs-Signature on your callback endpoints. */
|
|
@@ -1059,7 +1097,7 @@ export class Vxil {
|
|
|
1059
1097
|
check: async (input) => (await this.call('POST', '/v1/rate-limits/check', input)).data,
|
|
1060
1098
|
/** Per-identifier overrides layered over a policy: `pattern` matches the
|
|
1061
1099
|
* RENDERED key (exact, or a `*`-glob where the longest literal prefix
|
|
1062
|
-
* wins). Propagates to the check path within ≤30s (
|
|
1100
|
+
* wins). Propagates to the check path within ≤30s (cache TTL). */
|
|
1063
1101
|
overrides: {
|
|
1064
1102
|
list: async (policyId) => (await this.call('GET', `/v1/rate-limits/policies/${encodeURIComponent(policyId)}/overrides`)).data.overrides,
|
|
1065
1103
|
create: async (policyId, input) => (await this.call('POST', `/v1/rate-limits/policies/${encodeURIComponent(policyId)}/overrides`, input)).data,
|
|
@@ -1078,7 +1116,7 @@ export class Vxil {
|
|
|
1078
1116
|
addField: async (collection, field) => {
|
|
1079
1117
|
await this.call('POST', `/v1/cms/collections/${encodeURIComponent(collection)}/fields`, field);
|
|
1080
1118
|
},
|
|
1081
|
-
/** Set (or clear, with `null`/`[]`) a field's READ-ROLE gate (
|
|
1119
|
+
/** Set (or clear, with `null`/`[]`) a field's READ-ROLE gate (guide ch. 4) —
|
|
1082
1120
|
* the same-type in-place alter on the fields route. Re-sends the field's
|
|
1083
1121
|
* `type` (required by the alter path); every OTHER attribute the field
|
|
1084
1122
|
* carries is re-sent from `rest`, because the alter overwrites the whole
|
|
@@ -1088,7 +1126,7 @@ export class Vxil {
|
|
|
1088
1126
|
setFieldReadRoles: async (collection, field, type, roles, rest = {}) => {
|
|
1089
1127
|
await this.call('POST', `/v1/cms/collections/${encodeURIComponent(collection)}/fields`, { ...rest, field, type, read_roles: roles ?? [] });
|
|
1090
1128
|
},
|
|
1091
|
-
/** Turn a field's EQUALITY INDEX on or off (
|
|
1129
|
+
/** Turn a field's EQUALITY INDEX on or off (guide ch. 4 "Indexed equality")
|
|
1092
1130
|
* — the same-type in-place alter on the fields route; `indexed` is
|
|
1093
1131
|
* present-key, so no other attribute moves. Only for UNSLOTTED,
|
|
1094
1132
|
* non-computed fields, ≤4 per collection (422 `eq_index_budget`).
|
|
@@ -1112,11 +1150,11 @@ export class Vxil {
|
|
|
1112
1150
|
}
|
|
1113
1151
|
},
|
|
1114
1152
|
/** Set (or clear, with `null`) the collection's end-user owner-scope flag
|
|
1115
|
-
* (
|
|
1153
|
+
* (guide ch. 4, "`ownerField`"). Names an existing `string` field that holds the owner id. */
|
|
1116
1154
|
setOwnerField: async (collection, ownerField) => {
|
|
1117
1155
|
await this.call('POST', `/v1/cms/collections/${encodeURIComponent(collection)}/fields`, { owner_field: ownerField });
|
|
1118
1156
|
},
|
|
1119
|
-
/** Toggle the collection's PUBLIC-delivery flag (
|
|
1157
|
+
/** Toggle the collection's PUBLIC-delivery flag (guide ch. 4). When `true`,
|
|
1120
1158
|
* its PUBLISHED items become KEYLESS-readable via the anonymous public lane
|
|
1121
1159
|
* (`GET {base}/v1/cms/public/:tenantId/:collection` — no api key); drafts and
|
|
1122
1160
|
* the owner_field are never exposed. `false` closes the lane (and purges the
|
|
@@ -1125,7 +1163,7 @@ export class Vxil {
|
|
|
1125
1163
|
await this.call('POST', `/v1/cms/collections/${encodeURIComponent(collection)}/fields`, { public: isPublic });
|
|
1126
1164
|
},
|
|
1127
1165
|
/** Re-project the collection's index slots after an `index_slot` move
|
|
1128
|
-
* (
|
|
1166
|
+
* (guide ch. 4). Slots are projected on WRITE only, so until this runs,
|
|
1129
1167
|
* stored rows keep their OLD projection: the new slot is NULL and the
|
|
1130
1168
|
* VACATED slot still holds the old field's values — range/sort on the
|
|
1131
1169
|
* moved field returns the WRONG rows, not merely missing ones. ONE page
|
|
@@ -1180,7 +1218,7 @@ export class Vxil {
|
|
|
1180
1218
|
}
|
|
1181
1219
|
return { scanned, updated, skipped, pages, ...(indexCertify ? { index_certify: indexCertify } : {}) };
|
|
1182
1220
|
},
|
|
1183
|
-
/** Replace the collection's per-record ACTION list (
|
|
1221
|
+
/** Replace the collection's per-record ACTION list (guide ch. 4) — the
|
|
1184
1222
|
* `{ actions }` fields-route meta-op; `[]` clears. Each action is ONE
|
|
1185
1223
|
* human-initiated step: the dashboard renders it as a button per record,
|
|
1186
1224
|
* and `items.runAction` invokes its deployed function. */
|
|
@@ -1189,10 +1227,10 @@ export class Vxil {
|
|
|
1189
1227
|
},
|
|
1190
1228
|
},
|
|
1191
1229
|
items: {
|
|
1192
|
-
/** `lock` serializes same-key writers (per-
|
|
1193
|
-
* is the declarative capacity/overlap invariant (requires lock) —
|
|
1230
|
+
/** `lock` serializes same-key writers (a per-key lock); `guard`
|
|
1231
|
+
* is the declarative capacity/overlap invariant (requires lock) — guide ch. 4. */
|
|
1194
1232
|
create: async (collection, input) => (await this.call('POST', `/v1/cms/items/${encodeURIComponent(collection)}`, input)).data,
|
|
1195
|
-
/** `expand` inlines relation/file fields into `data` (
|
|
1233
|
+
/** `expand` inlines relation/file fields into `data` (guide ch. 4): the
|
|
1196
1234
|
* full item envelope, a `{ object_id, $ref: 'files' }` stub for a file,
|
|
1197
1235
|
* the bare id on a cycle/depth cut, `null` for an invisible target. */
|
|
1198
1236
|
get: async (collection, itemId, opts) => {
|
|
@@ -1224,9 +1262,9 @@ export class Vxil {
|
|
|
1224
1262
|
});
|
|
1225
1263
|
return (await this.call('GET', `/v1/cms/items/${encodeURIComponent(collection)}${s}`)).data;
|
|
1226
1264
|
},
|
|
1227
|
-
/** Merge-patch data keys; null clears a key. Concurrency opts (
|
|
1265
|
+
/** Merge-patch data keys; null clears a key. Concurrency opts (guide ch. 4):
|
|
1228
1266
|
* `ifVersion` → If-Match CAS; `if` → bounded field precondition against
|
|
1229
|
-
* the locked row; `lock`/`guard` → the
|
|
1267
|
+
* the locked row; `lock`/`guard` → the write-serialization primitives (guide ch. 4). */
|
|
1230
1268
|
patch: async (collection, itemId, data, opts) => (await this.call('PATCH', `/v1/cms/items/${encodeURIComponent(collection)}/${encodeURIComponent(itemId)}`, {
|
|
1231
1269
|
data,
|
|
1232
1270
|
...(opts?.if ? { if: opts.if } : {}),
|
|
@@ -1237,18 +1275,18 @@ export class Vxil {
|
|
|
1237
1275
|
/** Atomic in-database increment — PATCH `{ $inc: {field: delta} }`, ONE
|
|
1238
1276
|
* conditional UPDATE guarded by the field's validation min/max (the quota
|
|
1239
1277
|
* shape), the optional `if` precondition, and If-Match. 409
|
|
1240
|
-
* inc_out_of_bounds when the guard refuses (
|
|
1278
|
+
* inc_out_of_bounds when the guard refuses (guide ch. 4). */
|
|
1241
1279
|
inc: async (collection, itemId, incs, opts) => (await this.call('PATCH', `/v1/cms/items/${encodeURIComponent(collection)}/${encodeURIComponent(itemId)}`, { $inc: incs, ...(opts?.if ? { if: opts.if } : {}) }, opts?.ifVersion !== undefined ? { 'if-match': String(opts.ifVersion) } : {})).data,
|
|
1242
|
-
/** `{ count }` under the same bounded filter grammar (
|
|
1280
|
+
/** `{ count }` under the same bounded filter grammar (guide ch. 4). 422
|
|
1243
1281
|
* count_unavailable_with_read_hooks on beforeRead-hooked collections. */
|
|
1244
1282
|
count: async (collection, filter) => {
|
|
1245
1283
|
const s = qs({ count: 'true', filter: filter ? JSON.stringify(filter) : undefined });
|
|
1246
1284
|
return (await this.call('GET', `/v1/cms/items/${encodeURIComponent(collection)}${s}`)).data.count;
|
|
1247
1285
|
},
|
|
1248
|
-
/** Returns the cascade tally (
|
|
1286
|
+
/** Returns the cascade tally (guide ch. 4); `ifVersion` rides If-Match and
|
|
1249
1287
|
* a conflict aborts BEFORE any cascade side-effect. */
|
|
1250
1288
|
delete: async (collection, itemId, opts) => (await this.call('DELETE', `/v1/cms/items/${encodeURIComponent(collection)}/${encodeURIComponent(itemId)}`, undefined, opts?.ifVersion !== undefined ? { 'if-match': String(opts.ifVersion) } : {})).data,
|
|
1251
|
-
/**
|
|
1289
|
+
/** BOUNDED FILTERED delete — soft-delete up to `limit`
|
|
1252
1290
|
* items (1–100, default 25) matching `filter`, newest id first. A filter
|
|
1253
1291
|
* is REQUIRED (an unselected sweep is refused 422). Each matched row runs
|
|
1254
1292
|
* the SAME single-item delete path (restrict refusal, cascade budget,
|
|
@@ -1268,7 +1306,7 @@ export class Vxil {
|
|
|
1268
1306
|
...(q.dryRun !== undefined ? { dry_run: q.dryRun } : {}),
|
|
1269
1307
|
})).data,
|
|
1270
1308
|
publish: async (collection, itemId) => (await this.call('POST', `/v1/cms/items/${encodeURIComponent(collection)}/${encodeURIComponent(itemId)}/publish`)).data,
|
|
1271
|
-
/**
|
|
1309
|
+
/** The BOUNDED reverse read — which live items
|
|
1272
1310
|
* reference this one, through which relation field. Owner-scoped in
|
|
1273
1311
|
* end-user mode like `get`. `count` is the page returned (≤ limit, max
|
|
1274
1312
|
* 100), never a total; `has_more` says the cap was hit. A DELETE refused
|
|
@@ -1278,19 +1316,19 @@ export class Vxil {
|
|
|
1278
1316
|
const qs = opts?.limit !== undefined ? `?limit=${encodeURIComponent(String(opts.limit))}` : '';
|
|
1279
1317
|
return (await this.call('GET', `/v1/cms/items/${encodeURIComponent(collection)}/${encodeURIComponent(itemId)}/backlinks${qs}`)).data;
|
|
1280
1318
|
},
|
|
1281
|
-
/**
|
|
1319
|
+
/** Run ONE declared per-record action — invokes the
|
|
1282
1320
|
* action's deployed function with `{ collection, item_id, action, actor,
|
|
1283
1321
|
* item }` and returns its result. 404 when the key is not declared;
|
|
1284
1322
|
* 502 `action_failed` (with `upstream.code` = the function's error
|
|
1285
1323
|
* class) when the function fails. Requires cms:write. */
|
|
1286
1324
|
runAction: async (collection, itemId, key) => (await this.call('POST', `/v1/cms/items/${encodeURIComponent(collection)}/${encodeURIComponent(itemId)}/actions/${encodeURIComponent(key)}`)).data,
|
|
1287
|
-
/**
|
|
1325
|
+
/** Bounded group-by aggregate. fns count|sum|
|
|
1288
1326
|
* min|max|avg over index-slot-bound fields; groupBy ≤2 slot-bound
|
|
1289
1327
|
* fields; filter = the full query DSL incl. ONE-hop dotted join terms
|
|
1290
1328
|
* ({"channel.visibility":"public"}); scan capped at 50k rows → 422
|
|
1291
1329
|
* window_too_large (narrow the window or materialize a read-model). */
|
|
1292
1330
|
aggregate: async (collection, body) => (await this.call('POST', `/v1/cms/items/${encodeURIComponent(collection)}/aggregate`, body)).data,
|
|
1293
|
-
/**
|
|
1331
|
+
/** Window ranking over the aggregate — rank ∈
|
|
1294
1332
|
* row_number|rank|percent_rank, computed over ≤500 aggregated groups
|
|
1295
1333
|
* (never raw rows), optional partitionBy. Same scan cap as aggregate. */
|
|
1296
1334
|
rank: async (collection, body) => (await this.call('POST', `/v1/cms/items/${encodeURIComponent(collection)}/rank`, body)).data,
|
|
@@ -1304,13 +1342,13 @@ export class Vxil {
|
|
|
1304
1342
|
* merge, the event is the backstop. Emits one `cms.items.rekeyed`. */
|
|
1305
1343
|
reKey: async (input) => (await this.call('POST', '/v1/cms/items/re-key', input)).data,
|
|
1306
1344
|
},
|
|
1307
|
-
/**
|
|
1345
|
+
/** Atomic multi-collection transaction — 1–5
|
|
1308
1346
|
* steps over ≤3 collections, per-step `$where` CAS preconditions (the
|
|
1309
1347
|
* PATCH `if` grammar). All-or-nothing: any failed precondition/validation
|
|
1310
1348
|
* rolls the WHOLE transaction back (409 precondition_failed names the
|
|
1311
1349
|
* step). cms-internal only — no cross-feature effects inside the tx. */
|
|
1312
1350
|
transaction: async (steps) => (await this.call('POST', '/v1/cms/transactions', { steps })).data,
|
|
1313
|
-
/**
|
|
1351
|
+
/** Several get / query / create / patch / delete ops in
|
|
1314
1352
|
* ONE round trip — the function-chain shape ("read 3 rows → patch 2 →
|
|
1315
1353
|
* create 1" is one call, not six). ≤25 ops, each run through the SAME path
|
|
1316
1354
|
* its single route uses (scopes, owner-scoping, hooks, guards, `if`,
|
|
@@ -1325,7 +1363,7 @@ export class Vxil {
|
|
|
1325
1363
|
ops: ops.map((o) => ('expand' in o && o.expand !== undefined ? { ...o, expand: expandParam(o.expand) } : o)),
|
|
1326
1364
|
...(opts?.atomic !== undefined ? { atomic: opts.atomic } : {}),
|
|
1327
1365
|
})).data,
|
|
1328
|
-
/**
|
|
1366
|
+
/** Declared read-models (config `readModels`
|
|
1329
1367
|
* bag) — list with last-run status, and "run now" materialization into
|
|
1330
1368
|
* the rollup collection. */
|
|
1331
1369
|
readModels: {
|
|
@@ -1373,7 +1411,7 @@ export class Vxil {
|
|
|
1373
1411
|
/** Read the resolved DM config (defaults merged with the stored partial). */
|
|
1374
1412
|
getConfig: async () => (await this.call('GET', '/v1/dm/config')).data,
|
|
1375
1413
|
/** Write the DM master config — a partial merge (omitted leaves keep their
|
|
1376
|
-
* current value), version-bumped and republished to the gate's
|
|
1414
|
+
* current value), version-bumped and republished to the gate's cache. */
|
|
1377
1415
|
setConfig: async (patch) => (await this.call('PUT', '/v1/dm/config', patch)).data,
|
|
1378
1416
|
conversations: {
|
|
1379
1417
|
/** Open (or, for a direct pair, return the existing) conversation. A
|
|
@@ -1436,7 +1474,7 @@ export class Vxil {
|
|
|
1436
1474
|
/** The MCP aggregation surface (the `mcp` feature). */
|
|
1437
1475
|
mcp = {
|
|
1438
1476
|
/** Per-tenant secret for verifying the X-Vxil-Mcp-Signature header on custom
|
|
1439
|
-
* MCP tool calls (
|
|
1477
|
+
* MCP tool calls (guide ch. 10) — the mirror of jobs.signingSecret(). */
|
|
1440
1478
|
signingSecret: async () => (await this.call('GET', '/v1/mcp/signing-secret')).data.signing_secret,
|
|
1441
1479
|
};
|
|
1442
1480
|
/**
|
|
@@ -1521,7 +1559,7 @@ export class Vxil {
|
|
|
1521
1559
|
markAll: async (userId) => this.feedsMark(userId, 'read'),
|
|
1522
1560
|
/** Archive/dismiss groups (hidden from inbox + dropped from the badge). */
|
|
1523
1561
|
archive: async (userId, scope) => this.feedsMark(userId, 'archive', scope),
|
|
1524
|
-
/** The badge: { unseen, unread, total } (
|
|
1562
|
+
/** The badge: { unseen, unread, total } (cached, recomputed on a miss). */
|
|
1525
1563
|
unreadCount: async (userId) => (await this.call('GET', `/v1/feeds/notification/${encodeURIComponent(userId)}/count`)).data,
|
|
1526
1564
|
},
|
|
1527
1565
|
/**
|
|
@@ -1649,7 +1687,7 @@ export class Vxil {
|
|
|
1649
1687
|
/** List an org's pending invitations. */
|
|
1650
1688
|
list: async (orgId) => (await this.call('GET', `/v1/orgs/${encodeURIComponent(orgId)}/invitations`)).data.invitations,
|
|
1651
1689
|
},
|
|
1652
|
-
/** Custom tenant roles (permission-sets
|
|
1690
|
+
/** Custom tenant roles (permission-sets). A custom role
|
|
1653
1691
|
* is a named set of permissions assignable like any built-in; the four
|
|
1654
1692
|
* built-ins reproduce the fixed owner>admin>member>viewer lattice. */
|
|
1655
1693
|
roles: {
|
|
@@ -1662,7 +1700,7 @@ export class Vxil {
|
|
|
1662
1700
|
await this.call('DELETE', `/v1/orgs/roles/${encodeURIComponent(roleKey)}`);
|
|
1663
1701
|
},
|
|
1664
1702
|
},
|
|
1665
|
-
/** Per-org/user resource ACL grants
|
|
1703
|
+
/** Per-org/user resource ACL grants: grant a permission
|
|
1666
1704
|
* on a specific resource string; `check(...,{ resource })` consults them. */
|
|
1667
1705
|
acl: {
|
|
1668
1706
|
grant: async (orgId, input) => (await this.call('POST', `/v1/orgs/${encodeURIComponent(orgId)}/acl`, input)).data,
|
|
@@ -1733,9 +1771,38 @@ export class Vxil {
|
|
|
1733
1771
|
delete: async (objectId) => {
|
|
1734
1772
|
await this.call('DELETE', `/v1/files/${encodeURIComponent(objectId)}`);
|
|
1735
1773
|
},
|
|
1736
|
-
/** Aggregate storage usage vs quotas (the FilesManager Storage panel).
|
|
1774
|
+
/** Aggregate storage usage vs quotas (the FilesManager Storage panel).
|
|
1775
|
+
* `public_assets` = published copies vs the plan's published-bytes ceiling
|
|
1776
|
+
* (identical bytes count once). */
|
|
1737
1777
|
usage: async () => (await this.call('GET', '/v1/files/usage')).data,
|
|
1738
|
-
/**
|
|
1778
|
+
/** SERVER-ONLY (403 server_only in end-user mode). Publish an available
|
|
1779
|
+
* object to vxil's public asset host: a stable, content-addressed URL
|
|
1780
|
+
* (`https://cdn.vxil.app/<tenant>/<sha256>.<ext>`) served from a global
|
|
1781
|
+
* edge cache with `Cache-Control: public, max-age=31536000, immutable`,
|
|
1782
|
+
* playable video/audio and CORS from `publicAssets.corsOrigins`. Needs
|
|
1783
|
+
* `files.publicAssets.enabled`. Publishable: png/jpeg/webp/avif/gif,
|
|
1784
|
+
* mp4/webm, mp3/m4a/ogg, woff2/woff, json — never HTML or SVG (422
|
|
1785
|
+
* `content_type_not_publishable`). Counts against the plan's
|
|
1786
|
+
* published-bytes ceiling (422 `quota_exceeded`). Idempotent: an already
|
|
1787
|
+
* published object answers its existing URL. Emits
|
|
1788
|
+
* `files.object.published`. */
|
|
1789
|
+
publish: async (objectId) => (await this.call('POST', `/v1/files/${encodeURIComponent(objectId)}/publish`)).data,
|
|
1790
|
+
/** SERVER-ONLY. Publish up to 100 objects (about 2 GiB of bytes) in one
|
|
1791
|
+
* call; a bad id lands in `errors[]` without failing the rest (ids past
|
|
1792
|
+
* the byte budget as `batch_budget_exceeded` — send them again),
|
|
1793
|
+
* `published[]` keeps your order. */
|
|
1794
|
+
publishMany: async (objectIds) => (await this.call('POST', '/v1/files/publish', { object_ids: objectIds })).data,
|
|
1795
|
+
/** SERVER-ONLY. Take an object off the public asset host. Its public copy
|
|
1796
|
+
* is deleted once no other object of yours has the same bytes, and the
|
|
1797
|
+
* edge cache stops serving it within about a minute and a half (a
|
|
1798
|
+
* browser that already downloaded it keeps its copy). Deleting the object
|
|
1799
|
+
* unpublishes it too. 404 when it is not published; 503
|
|
1800
|
+
* `unpublish_failed` when the public copy could not be removed right
|
|
1801
|
+
* then (it stays published — retry). Emits `files.object.unpublished`. */
|
|
1802
|
+
unpublish: async (objectId) => {
|
|
1803
|
+
await this.call('DELETE', `/v1/files/${encodeURIComponent(objectId)}/publish`);
|
|
1804
|
+
},
|
|
1805
|
+
/** OCR / text extraction (guide ch. 6, files, BYO-key add-on). Small/mock inputs
|
|
1739
1806
|
* extract inline (status `available`); large inputs (or `async:true`) return
|
|
1740
1807
|
* 202 with a `job_id` — poll `getText()`. */
|
|
1741
1808
|
extractText: async (objectId, opts) => (await this.call('POST', `/v1/files/${encodeURIComponent(objectId)}/extract-text`, opts ?? {})).data,
|
|
@@ -1818,7 +1885,7 @@ export class Vxil {
|
|
|
1818
1885
|
* The AI substrate (the `ai` feature): store prompt TEMPLATES (config-as-code),
|
|
1819
1886
|
* then generate (sync or streamed) and embed across providers. 'mock' is the
|
|
1820
1887
|
* deterministic default; the real providers (openai/anthropic/gemini/azure/
|
|
1821
|
-
* openrouter) route via BYO keys in
|
|
1888
|
+
* openrouter) route via BYO keys in your project secrets. `images` on the generate
|
|
1822
1889
|
* inputs takes up to 8 vision refs: a public https:// URL, a
|
|
1823
1890
|
* data:image/...;base64 URL, or file:<object_id> (a files-feature object) —
|
|
1824
1891
|
* fetched images are capped at 4 MiB each; `documents` (pdf/text, ≤10 MiB
|
|
@@ -1878,7 +1945,7 @@ export class Vxil {
|
|
|
1878
1945
|
* outlives the ≤300s realtime token TTL); reconnect with `?since=<seq>`. */
|
|
1879
1946
|
remintToken: async (generationId) => (await this.call('GET', `/v1/ai/generations/${encodeURIComponent(generationId)}/token`)).data,
|
|
1880
1947
|
/** Resume a streamed generation after a dropped socket: every recorded frame
|
|
1881
|
-
* with seq > `since` plus `done` (the
|
|
1948
|
+
* with seq > `since` plus `done` (the replay buffer — plain JSON, not
|
|
1882
1949
|
* an SSE stream; the buffer lives 1 h). A settled JOB-lane generation is
|
|
1883
1950
|
* served from its stored answer instead (30 days). `status` says where the
|
|
1884
1951
|
* generation is; `expired: true` means it settled but its frames are gone
|
|
@@ -1929,7 +1996,7 @@ export class Vxil {
|
|
|
1929
1996
|
/** Streamed grounded answer: citations + the channel handle up front, tokens
|
|
1930
1997
|
* over the realtime channel. */
|
|
1931
1998
|
stream: async (input) => (await this.call('POST', '/v1/rag/answer', { ...input, stream: true })).data,
|
|
1932
|
-
/** Retrieval-only grounding preview (
|
|
1999
|
+
/** Retrieval-only grounding preview (guide ch. 6, rag): the exact chunks `answer`
|
|
1933
2000
|
* would ground on, with rerank + metadata boosts applied — no generation,
|
|
1934
2001
|
* no token spend. `boosts`/`rerank`/`min_score` override the rag config.
|
|
1935
2002
|
* `min_score` floors the EFFECTIVE `score`, which is a RANK value (about
|
|
@@ -2028,7 +2095,7 @@ export class Vxil {
|
|
|
2028
2095
|
* `quota` inlines one quota, `creditType` inlines the same owner-bound
|
|
2029
2096
|
* balance `getBalance` returns — ONE call for a thin client's paywall.
|
|
2030
2097
|
* A non-2xx answer means UNKNOWN: render the last cached answer, never
|
|
2031
|
-
* free (
|
|
2098
|
+
* free (guide ch. 6, payments).
|
|
2032
2099
|
*
|
|
2033
2100
|
* OVERLAPPING SUBSCRIPTIONS — what `until` means. The WINNER is the
|
|
2034
2101
|
* entitled subscription with the highest `tierMap[tier].rank` (default 0);
|
|
@@ -2114,14 +2181,14 @@ export class Vxil {
|
|
|
2114
2181
|
return (await this.call('GET', `/v1/payments/subscriptions${s}`)).data.subscriptions;
|
|
2115
2182
|
},
|
|
2116
2183
|
/**
|
|
2117
|
-
* Create a hosted-checkout session (
|
|
2184
|
+
* Create a hosted-checkout session (guide ch. 6, payments). Redirect the buyer to
|
|
2118
2185
|
* the returned `url`; completion lands server-side via the provider webhook
|
|
2119
2186
|
* (the matching session flips to completed, the charge/grant is folded).
|
|
2120
2187
|
* Idempotency-Key REQUIRED — a retry replays the SAME session verbatim.
|
|
2121
2188
|
*/
|
|
2122
2189
|
createCheckoutSession: async (input, opts) => (await this.call('POST', '/v1/payments/checkout-sessions', input, { 'idempotency-key': opts.idempotencyKey })).data,
|
|
2123
2190
|
/**
|
|
2124
|
-
* Refund a charge at-most-once (
|
|
2191
|
+
* Refund a charge at-most-once (guide ch. 6, payments). Omit `amount_cents` to
|
|
2125
2192
|
* refund the full un-refunded remainder. Idempotency-Key REQUIRED — a retry
|
|
2126
2193
|
* replays the recorded refund (never a second provider refund); a refund can
|
|
2127
2194
|
* NEVER exceed the charge (422 refund_exceeds_charge).
|
|
@@ -2241,7 +2308,7 @@ export class Vxil {
|
|
|
2241
2308
|
});
|
|
2242
2309
|
return (await this.call('GET', `/v1/payments/refunds${suffix}`)).data;
|
|
2243
2310
|
},
|
|
2244
|
-
/** Provider webhook event log (
|
|
2311
|
+
/** Provider webhook event log (guide ch. 6, payments "Event log & replay"):
|
|
2245
2312
|
* operator visibility over every delivery — incl. persisted signature
|
|
2246
2313
|
* failures — plus an idempotent reprocess verb. Needs payments:read
|
|
2247
2314
|
* (reprocess: payments:write).
|
|
@@ -2276,5 +2343,5 @@ export class Vxil {
|
|
|
2276
2343
|
};
|
|
2277
2344
|
}
|
|
2278
2345
|
// Failure reporting for tenant functions — the Sentry-envelope forwarder
|
|
2279
|
-
// (
|
|
2346
|
+
// (guide ch. 8). Zero dependencies; see reporting.ts.
|
|
2280
2347
|
export { withReporting, report, buildEnvelope, parseDsn, exceptionEvent, reportServerErrors, REPORT_TIMEOUT_MS, } from './reporting.js';
|
package/dist/qs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Query-string builder — the SDK's replacement for the WHATWG `URLSearchParams`
|
|
2
|
-
// at every call site (
|
|
2
|
+
// at every call site (the React-Native-clean SDK).
|
|
3
3
|
//
|
|
4
4
|
// WHY (implementation note; `//` comments never reach the shipped .d.ts):
|
|
5
5
|
// React Native supplies its own URLSearchParams polyfill, and up to RN 0.79
|
package/dist/reporting.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// withReporting — forward a tenant function's failure to the tenant's OWN
|
|
2
|
-
// error reporter over the Sentry envelope HTTP format
|
|
3
|
-
// techmaker evaluation P0-5's "SDK half", 2026-09-23).
|
|
2
|
+
// error reporter over the Sentry envelope HTTP format.
|
|
4
3
|
//
|
|
5
4
|
// NOT a Sentry SDK and NOT a dependency on one: the function sandbox is
|
|
6
5
|
// Web-standard fetch/crypto only, so this is the plain ingestion envelope
|
package/dist/retry.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// The request seam: retry + timeout + hooks around the ONE `fetch` every SDK
|
|
2
|
-
// request goes through
|
|
3
|
-
// `VxilError.retryAfter` shares
|
|
2
|
+
// request goes through, and the `Retry-After` parser
|
|
3
|
+
// `VxilError.retryAfter` shares. DEFAULT OFF — with no `retry`,
|
|
4
4
|
// `timeoutMs` or `hooks` option the transport is a single `fetch` + `text()`,
|
|
5
5
|
// exactly what `call()` did before.
|
|
6
6
|
//
|
package/package.json
CHANGED