@vxil/sdk 0.18.0 → 0.19.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 +267 -18
- package/dist/index.js +140 -14
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1646,9 +1646,11 @@ export type VxilEventPayload<E extends string> = E extends keyof VxilEventPayloa
|
|
|
1646
1646
|
export type FunctionEnvelopeTrigger = 'http' | 'cms-hook' | 'auth-hook' | 'queue' | 'cron' | 'webhook';
|
|
1647
1647
|
/** The audiences a function's scoped callback tokens are minted for — one per
|
|
1648
1648
|
* feature its declared scopes reach. `control-plane` carries `users:*` /
|
|
1649
|
-
* `usage:read`
|
|
1650
|
-
*
|
|
1651
|
-
|
|
1649
|
+
* `usage:read` / `audit:read` only; `webhooks` carries `webhooks:read` only (read the inbound
|
|
1650
|
+
* sources and received events — e.g. `GET /v1/webhooks/events?after=`). There
|
|
1651
|
+
* is deliberately no `functions` audience: a function cannot call another
|
|
1652
|
+
* function. */
|
|
1653
|
+
export type FunctionCallbackAudience = 'cms' | 'payments' | 'notifications' | 'comments' | 'files' | 'ai' | 'rag' | 'vector-search' | 'activity-feed' | 'orgs' | 'auth' | 'jobs' | 'realtime' | 'rate-limits' | 'control-plane' | 'webhooks';
|
|
1652
1654
|
/** One short-lived scoped token per audience your scopes imply
|
|
1653
1655
|
* (`env.scoped_jwts.cms`, `env.scoped_jwts['control-plane']`); an audience your
|
|
1654
1656
|
* scopes do not reach is absent. Send it as `Authorization: Bearer …` to
|
|
@@ -1710,6 +1712,43 @@ export interface WebhookTriggerPayload<D = Record<string, unknown>> {
|
|
|
1710
1712
|
truncated: true;
|
|
1711
1713
|
} | null;
|
|
1712
1714
|
}
|
|
1715
|
+
/** The verification parameters of an inbound `hmac` source (`webhooks.sources.create`). */
|
|
1716
|
+
export interface WebhookHmacVerify {
|
|
1717
|
+
/** the header carrying the signature (case-insensitive) */
|
|
1718
|
+
header: string;
|
|
1719
|
+
algorithm?: 'sha256' | 'sha1';
|
|
1720
|
+
encoding?: 'hex' | 'base64';
|
|
1721
|
+
/** stripped before comparing (e.g. `sha256=`); required on the request when set */
|
|
1722
|
+
prefix?: string;
|
|
1723
|
+
/** a header carrying the signing timestamp (unix seconds or ISO 8601): the
|
|
1724
|
+
* signed message becomes `<timestamp><timestamp_separator><body>` */
|
|
1725
|
+
timestamp_header?: string;
|
|
1726
|
+
/** default '.'; '' to concatenate */
|
|
1727
|
+
timestamp_separator?: string;
|
|
1728
|
+
/** default 300, 1..3600 */
|
|
1729
|
+
tolerance_seconds?: number;
|
|
1730
|
+
event_id_header?: string;
|
|
1731
|
+
event_type_header?: string;
|
|
1732
|
+
}
|
|
1733
|
+
/** `payload.data` of a `webhook` invocation fired by an inbound source whose
|
|
1734
|
+
* `target_function` is this function (`payload.event === 'inbound_webhook.received'`). */
|
|
1735
|
+
export interface InboundWebhookTriggerData {
|
|
1736
|
+
vxil_event_id: string;
|
|
1737
|
+
source_id: string;
|
|
1738
|
+
provider: string;
|
|
1739
|
+
provider_event_id: string | null;
|
|
1740
|
+
event_type: string | null;
|
|
1741
|
+
sig_verified: boolean;
|
|
1742
|
+
/** the provider's JSON body; null when it was larger than 48 000 bytes
|
|
1743
|
+
* (`payload_omitted: true`) — read it with
|
|
1744
|
+
* `GET /v1/webhooks/events?event_id=<vxil_event_id>` (scope webhooks:read) */
|
|
1745
|
+
payload: Record<string, unknown> | unknown[] | null;
|
|
1746
|
+
payload_omitted?: true;
|
|
1747
|
+
/** true on a `POST /v1/webhooks/sources/:id/test` probe */
|
|
1748
|
+
test?: true;
|
|
1749
|
+
/** true on a `POST /v1/webhooks/events/:id/replay` */
|
|
1750
|
+
replayed?: true;
|
|
1751
|
+
}
|
|
1713
1752
|
/** `POST /v1/fn/:name`: `payload` is the JSON request body (the query
|
|
1714
1753
|
* parameters on a GET). */
|
|
1715
1754
|
export interface HttpFunctionEnvelope<P = unknown> extends FunctionEnvelopeBase {
|
|
@@ -2847,6 +2886,11 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
2847
2886
|
readonly fn: { [K in keyof S["functions"] & string]: FnInvoker<S["functions"][K]["Input"], S["functions"][K]["Output"]>; };
|
|
2848
2887
|
private call;
|
|
2849
2888
|
readonly users: {
|
|
2889
|
+
/** Create or replace a tenant user by id. Upserting a deleted id brings it
|
|
2890
|
+
* back; upserting an ERASED id re-creates it as a new, empty user
|
|
2891
|
+
* (`erased_at` cleared — the erase already removed the old data). While
|
|
2892
|
+
* the erased user's files are still being erased the call fails with 409
|
|
2893
|
+
* `user_erase_pending`; retry later. `bulk` is all-or-nothing. */
|
|
2850
2894
|
upsert: (user: {
|
|
2851
2895
|
id: string;
|
|
2852
2896
|
email?: string;
|
|
@@ -3215,11 +3259,23 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
3215
3259
|
brief?: boolean;
|
|
3216
3260
|
}) => Promise<VxilPlan>;
|
|
3217
3261
|
};
|
|
3262
|
+
/** The project audit log. Needs `audit:read` (the narrow read-only scope —
|
|
3263
|
+
* declare it on a function to read the log from its callback token) or
|
|
3264
|
+
* `features:read`. Server-only: a public / thin-client key cannot read it. */
|
|
3218
3265
|
readonly audit: {
|
|
3266
|
+
/**
|
|
3267
|
+
* The audit trail, newest first. `subject` narrows to the events about ONE
|
|
3268
|
+
* thing: a user id (`payload.user_id` / `payload.id`), a cms record
|
|
3269
|
+
* (`payload.item_id` — the record's history), a verified end user
|
|
3270
|
+
* (`payload.end_user_id` — what that person changed through a thin-client
|
|
3271
|
+
* key or a function acting for them, and their payments events), or a
|
|
3272
|
+
* principal (`actor`). Server keys only.
|
|
3273
|
+
*/
|
|
3219
3274
|
list: (q?: {
|
|
3220
3275
|
since?: string;
|
|
3221
3276
|
cursor?: string;
|
|
3222
3277
|
limit?: number;
|
|
3278
|
+
subject?: string;
|
|
3223
3279
|
}) => Promise<Array<{
|
|
3224
3280
|
id: string;
|
|
3225
3281
|
event: string;
|
|
@@ -3237,6 +3293,8 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
3237
3293
|
until?: string;
|
|
3238
3294
|
after_id?: string;
|
|
3239
3295
|
limit?: number;
|
|
3296
|
+
/** the same subject filter as `list` */
|
|
3297
|
+
subject?: string;
|
|
3240
3298
|
}) => Promise<{
|
|
3241
3299
|
events: Array<{
|
|
3242
3300
|
id: string;
|
|
@@ -3521,7 +3579,8 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
3521
3579
|
state: string;
|
|
3522
3580
|
}>;
|
|
3523
3581
|
/** Clone a terminal run into a fresh queued run. A generation run answers
|
|
3524
|
-
* `409 not_replayable` — submit the generation again instead
|
|
3582
|
+
* `409 not_replayable` — submit the generation again instead; a redacted
|
|
3583
|
+
* or erased run answers `409 run_redacted` (its payload is gone). */
|
|
3525
3584
|
replay: (runId: string) => Promise<{
|
|
3526
3585
|
run_id: string;
|
|
3527
3586
|
replayed_from: string;
|
|
@@ -3544,6 +3603,30 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
3544
3603
|
moved: number;
|
|
3545
3604
|
done: boolean;
|
|
3546
3605
|
}>;
|
|
3606
|
+
/** SERVER-ONLY (403 server_only in end-user mode). Redact FINISHED runs
|
|
3607
|
+
* that hold personal data: the payload becomes `{ erased: true }`, the
|
|
3608
|
+
* result, progress, callback body and error text are cleared, a
|
|
3609
|
+
* generation's provider request body/headers are dropped and its credit
|
|
3610
|
+
* hold's user id is pseudonymized (unless its settle is still owed). The
|
|
3611
|
+
* row itself stays (state, timings, job_name). The same fields a GDPR
|
|
3612
|
+
* erase clears on the runs it can link to a user — use this for runs
|
|
3613
|
+
* nothing links to one (a plain queue run, an async function call).
|
|
3614
|
+
* `{ run_ids }` (≤500, one call; open runs are left alone and counted in
|
|
3615
|
+
* `skipped_open`) or `{ user_id }` (the generation runs whose credit hold
|
|
3616
|
+
* names that user — ONE page of ≤500 per call, call again until `done`).
|
|
3617
|
+
* Idempotent. Audited as `jobs.runs.redacted` (counts only; none when a
|
|
3618
|
+
* call redacted nothing). NOT cleared: concurrency_key, debounce_key,
|
|
3619
|
+
* idempotency_key and target_url — keep user ids out of those (hash them).
|
|
3620
|
+
* A redacted run does not replay. Scope jobs:write. */
|
|
3621
|
+
redact: (input: {
|
|
3622
|
+
run_ids: string[];
|
|
3623
|
+
} | {
|
|
3624
|
+
user_id: string;
|
|
3625
|
+
}) => Promise<{
|
|
3626
|
+
redacted: number;
|
|
3627
|
+
skipped_open: number;
|
|
3628
|
+
done: boolean;
|
|
3629
|
+
}>;
|
|
3547
3630
|
/**
|
|
3548
3631
|
* Suspend the RUNNING run until an event (call from the executing
|
|
3549
3632
|
* handler, then return 200 — the suspension wins). `state: 'resumed'` =
|
|
@@ -3734,8 +3817,10 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
3734
3817
|
/** On success the guest keeps its user id (`user_id` unchanged).
|
|
3735
3818
|
* If the claimed email ALREADY has an account, the guest is MERGED
|
|
3736
3819
|
* into it: `user_id` is the existing account, `merged: true`, and a
|
|
3737
|
-
* fresh `session.token` (same session
|
|
3738
|
-
* identity — swap it client-side
|
|
3820
|
+
* fresh `session.token` (the same session carried over to the merged
|
|
3821
|
+
* identity under a new session id — swap it client-side: the old
|
|
3822
|
+
* guest token is revoked, the refresh token keeps working; other
|
|
3823
|
+
* guest sessions are revoked).
|
|
3739
3824
|
* `rekeyed` (present on a merge): true ⇒ the guest's payments / cms /
|
|
3740
3825
|
* files rows were already moved onto `user_id` when this returned, so
|
|
3741
3826
|
* the merged user's next request finds them; false ⇒ the move ran
|
|
@@ -3967,15 +4052,34 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
3967
4052
|
verified: true;
|
|
3968
4053
|
}>;
|
|
3969
4054
|
};
|
|
3970
|
-
/** Social sign-in. The web `
|
|
3971
|
-
* (
|
|
3972
|
-
* `GET /v1/auth/oauth/{provider}/start
|
|
3973
|
-
*
|
|
4055
|
+
/** Social sign-in. The web flow: `startUrl` fetches the provider authorize
|
|
4056
|
+
* URL with your key (a browser cannot follow the header-carrying 302 of
|
|
4057
|
+
* `GET /v1/auth/oauth/{provider}/start` itself), you navigate the browser
|
|
4058
|
+
* there, and hand the returned `code`+`state` to `…/callback`; `native`
|
|
3974
4059
|
* is the mobile / broker token-exchange the SDK wraps. `oidc` is the
|
|
3975
4060
|
* tenant's generic OIDC / SSO issuer (auth config `providers.oidc` —
|
|
3976
4061
|
* Okta / Entra / Auth0 / any OpenID Connect IdP, or a SAML broker that
|
|
3977
4062
|
* speaks OIDC); it rides the same three routes. */
|
|
3978
4063
|
oauth: {
|
|
4064
|
+
/** Start the web sign-in from a browser SPA: answers the provider
|
|
4065
|
+
* `authorize_url` (PKCE challenge, one-shot `state`, and for `oidc` a
|
|
4066
|
+
* nonce bound to the flow — all held server-side) for you to navigate
|
|
4067
|
+
* to: `location.assign((await vx.auth.oauth.startUrl('oidc',
|
|
4068
|
+
* { redirect_uri })).authorize_url)`. The provider returns to
|
|
4069
|
+
* `redirect_uri` with `?code&state`, which your page sends to
|
|
4070
|
+
* `GET /v1/auth/oauth/{provider}/callback` for the session. The same
|
|
4071
|
+
* rules as the redirect form apply: `redirect_uri` must match the auth
|
|
4072
|
+
* config `security.allowedRedirectOrigins` when set (else
|
|
4073
|
+
* `422 redirect_not_allowed`), and the state expires after
|
|
4074
|
+
* `expires_in` seconds (600). `anonymous_token` (a guest bearer)
|
|
4075
|
+
* promotes or merges that guest at the callback. */
|
|
4076
|
+
startUrl: (provider: "google" | "apple" | "github" | "facebook" | "mock" | "oidc", input: {
|
|
4077
|
+
redirect_uri: string;
|
|
4078
|
+
anonymous_token?: string;
|
|
4079
|
+
}) => Promise<{
|
|
4080
|
+
authorize_url: string;
|
|
4081
|
+
expires_in: number;
|
|
4082
|
+
}>;
|
|
3979
4083
|
/** Native social sign-in: exchange a provider `id_token`/`access_token`
|
|
3980
4084
|
* for a vxil session (`linked` marks whether the user was created or
|
|
3981
4085
|
* matched to an existing identity). */
|
|
@@ -4126,6 +4230,20 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
4126
4230
|
* (end-user reads and writes are `403 server_only`, even with an
|
|
4127
4231
|
* owner_field). Server keys are never affected. */
|
|
4128
4232
|
end_user_access?: CmsEndUserAccess;
|
|
4233
|
+
/** Role slugs one of which a VERIFIED end user must hold to WRITE this
|
|
4234
|
+
* collection (create / update / $inc / delete / publish / transaction
|
|
4235
|
+
* step / batch / filtered delete, and an end-user delete's cascade into
|
|
4236
|
+
* it) — otherwise `403 role_required`. Reads are unaffected; server keys
|
|
4237
|
+
* are never affected. Omitted/`[]` = ungated. ≤16 entries, each
|
|
4238
|
+
* `^[a-z0-9][a-z0-9_-]{0,31}$` (the `orgs` role alphabet). */
|
|
4239
|
+
write_roles?: string[];
|
|
4240
|
+
/** Retention (guide ch. 4): live items created more than this many days
|
|
4241
|
+
* ago (1..3650) are soft-deleted by the nightly platform sweep — like a
|
|
4242
|
+
* normal delete (no restore; purged at least 30 days later, unique values freed), with ONE
|
|
4243
|
+
* `cms.collection.retention_swept` audit event per collection per night
|
|
4244
|
+
* instead of per-item delete events. A collection another collection
|
|
4245
|
+
* references with an `on_delete` rule is skipped. Omit/null = keep. */
|
|
4246
|
+
retain_days?: number | null;
|
|
4129
4247
|
fields?: Array<{
|
|
4130
4248
|
field: string;
|
|
4131
4249
|
type: "string" | "text" | "int" | "float" | "bool" | "datetime" | "json" | "relation" | "file";
|
|
@@ -4175,6 +4293,10 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
4175
4293
|
actions?: CmsActionDef[];
|
|
4176
4294
|
/** present when not the default 'readwrite' */
|
|
4177
4295
|
end_user_access?: CmsEndUserAccess;
|
|
4296
|
+
/** present when the collection is role-gated for end-user writes */
|
|
4297
|
+
write_roles?: string[];
|
|
4298
|
+
/** present when retention is set */
|
|
4299
|
+
retain_days?: number;
|
|
4178
4300
|
}>;
|
|
4179
4301
|
list: () => Promise<Array<{
|
|
4180
4302
|
collection: string;
|
|
@@ -4183,6 +4305,10 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
4183
4305
|
owner_field?: string | null;
|
|
4184
4306
|
/** the collection's end-user access mode (absent from an older server = 'readwrite') */
|
|
4185
4307
|
end_user_access?: CmsEndUserAccess;
|
|
4308
|
+
/** the roles an end user needs to write it (null / absent = ungated) */
|
|
4309
|
+
write_roles?: string[] | null;
|
|
4310
|
+
/** retention in days (null = keep until deleted; absent from an older server) */
|
|
4311
|
+
retain_days?: number | null;
|
|
4186
4312
|
}>>;
|
|
4187
4313
|
addField: (collection: string, field: Record<string, unknown>) => Promise<void>;
|
|
4188
4314
|
/** Set (or clear, with `null`/`[]`) a field's READ-ROLE gate (guide ch. 4) —
|
|
@@ -4225,6 +4351,25 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
4225
4351
|
collection: string;
|
|
4226
4352
|
end_user_access: CmsEndUserAccess;
|
|
4227
4353
|
}>;
|
|
4354
|
+
/** Set (or clear, with `[]` / `null`) the collection's END-USER WRITE
|
|
4355
|
+
* ROLES (guide ch. 9): a verified end user must hold one of `roles` (the
|
|
4356
|
+
* session's verified role claims — orgs roles) to write the collection,
|
|
4357
|
+
* otherwise every write door answers `403 role_required`. Reads are
|
|
4358
|
+
* unaffected; server keys are never affected. Audited; takes effect on
|
|
4359
|
+
* the next write. */
|
|
4360
|
+
setWriteRoles: (collection: string, roles: string[] | null) => Promise<{
|
|
4361
|
+
collection: string;
|
|
4362
|
+
write_roles: string[];
|
|
4363
|
+
}>;
|
|
4364
|
+
/** Set (1..3650 days) or clear (`null`) the collection's RETENTION (guide
|
|
4365
|
+
* ch. 4): the nightly platform sweep soft-deletes live items created more
|
|
4366
|
+
* than `days` ago — bounded per project per night, no restore, purged at
|
|
4367
|
+
* least 30 days later like any delete, one `cms.collection.retention_swept` audit event
|
|
4368
|
+
* per collection per sweep. Audited (`cms.collection.retain_days.set`). */
|
|
4369
|
+
setRetention: (collection: string, days: number | null) => Promise<{
|
|
4370
|
+
collection: string;
|
|
4371
|
+
retain_days: number | null;
|
|
4372
|
+
}>;
|
|
4228
4373
|
/** Re-project the collection's index slots after an `index_slot` move
|
|
4229
4374
|
* (guide ch. 4). Slots are projected on WRITE only, so until this runs,
|
|
4230
4375
|
* stored rows keep their OLD projection: the new slot is NULL and the
|
|
@@ -4276,6 +4421,29 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
4276
4421
|
version: number;
|
|
4277
4422
|
data: Record<string, unknown>;
|
|
4278
4423
|
}>;
|
|
4424
|
+
/** Upsert by a declared `unique` field (guide ch. 4): creates the item, or
|
|
4425
|
+
* — when a live item already holds `data[field]` — merges `patch` into
|
|
4426
|
+
* THAT item instead (one transaction; update hooks, guards, version bump
|
|
4427
|
+
* and a `cms.item.updated` audit, exactly like `patch`; null clears a
|
|
4428
|
+
* field). `created` tells which happened (201 vs 200). Concurrent upserts
|
|
4429
|
+
* of one key serialize, so they never create a duplicate. */
|
|
4430
|
+
upsert: (collection: string, input: {
|
|
4431
|
+
data: Record<string, unknown>;
|
|
4432
|
+
onConflict: {
|
|
4433
|
+
field: string;
|
|
4434
|
+
patch: Record<string, unknown>;
|
|
4435
|
+
};
|
|
4436
|
+
status?: "draft" | "published";
|
|
4437
|
+
lock?: string;
|
|
4438
|
+
guard?: CmsGuard;
|
|
4439
|
+
guards?: CmsGuardTerm[];
|
|
4440
|
+
}) => Promise<{
|
|
4441
|
+
item_id: string;
|
|
4442
|
+
status: string;
|
|
4443
|
+
version: number;
|
|
4444
|
+
data: Record<string, unknown>;
|
|
4445
|
+
created: boolean;
|
|
4446
|
+
}>;
|
|
4279
4447
|
/** `expand` inlines relation/file fields into `data` (guide ch. 4): the
|
|
4280
4448
|
* full item envelope, a `{ object_id, $ref: 'files' }` stub for a file,
|
|
4281
4449
|
* the bare id on a cycle/depth cut, `null` for an invisible target. */
|
|
@@ -4284,7 +4452,9 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
4284
4452
|
}) => Promise<Record<string, unknown>>;
|
|
4285
4453
|
/**
|
|
4286
4454
|
* The bounded query DSL. filter ops: $eq $ne $gt $gte $lt $lte $in
|
|
4287
|
-
* $
|
|
4455
|
+
* ($ne is NULL-safe: a row whose field is absent or null counts as not
|
|
4456
|
+
* equal; on a dotted join term the row still needs a readable target,
|
|
4457
|
+
* so a null or dangling relation is excluded) $contains $startsWith (LIKE-escaped; substring-searchable on s*-slotted
|
|
4288
4458
|
* fields) $arrayContains/$anyOf (json/relation array containment, indexed);
|
|
4289
4459
|
* range/sort needs slot-indexed fields. This is the
|
|
4290
4460
|
* DELIBERATELY-UNTYPED escape hatch: it accepts everything the server
|
|
@@ -4674,6 +4844,32 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
4674
4844
|
};
|
|
4675
4845
|
warnings?: string[];
|
|
4676
4846
|
}>;
|
|
4847
|
+
/**
|
|
4848
|
+
* Log records of ONE function, one per invocation (outcome, wall/cpu ms,
|
|
4849
|
+
* console lines, exceptions), oldest first. Pass the returned `next_since`
|
|
4850
|
+
* back as `since` to read only newer records (a tail); without it, the most
|
|
4851
|
+
* recent records of the last 24 hours. Kept 14 days. Needs
|
|
4852
|
+
* `functions:read`. (GET /v1/functions/:name/logs)
|
|
4853
|
+
*/
|
|
4854
|
+
logs: (name: string, opts?: {
|
|
4855
|
+
since?: string;
|
|
4856
|
+
}) => Promise<{
|
|
4857
|
+
lines: Array<{
|
|
4858
|
+
ts: string;
|
|
4859
|
+
outcome: string;
|
|
4860
|
+
wall_ms: number | null;
|
|
4861
|
+
cpu_ms: number | null;
|
|
4862
|
+
logs: Array<{
|
|
4863
|
+
level: string;
|
|
4864
|
+
message: string;
|
|
4865
|
+
}>;
|
|
4866
|
+
exceptions: Array<{
|
|
4867
|
+
name: string;
|
|
4868
|
+
message: string;
|
|
4869
|
+
}>;
|
|
4870
|
+
}>;
|
|
4871
|
+
next_since: string | null;
|
|
4872
|
+
}>;
|
|
4677
4873
|
};
|
|
4678
4874
|
/** The MCP aggregation surface (the `mcp` feature). */
|
|
4679
4875
|
readonly mcp: {
|
|
@@ -4969,20 +5165,38 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
4969
5165
|
last_error_msg?: string | null;
|
|
4970
5166
|
}>;
|
|
4971
5167
|
sources: {
|
|
4972
|
-
/** Register an inbound source; the receiver URL is returned ONCE.
|
|
5168
|
+
/** Register an inbound source; the receiver URL is returned ONCE.
|
|
5169
|
+
*
|
|
5170
|
+
* Destination: `forward_url` (your endpoint) OR `target_function` (a
|
|
5171
|
+
* deployed function that declares a bare `trigger: { kind: 'webhook' }`
|
|
5172
|
+
* binding — it receives `payload.event === 'inbound_webhook.received'`
|
|
5173
|
+
* with the inbound envelope as `payload.data`), never both; neither =
|
|
5174
|
+
* store only (read with `events.list`).
|
|
5175
|
+
*
|
|
5176
|
+
* `provider: 'hmac'` is the parameterized preset for any provider that
|
|
5177
|
+
* signs an HMAC of the raw body (Shopify, Intercom, Linear, Zendesk, …):
|
|
5178
|
+
* `verify` says which header, sha256|sha1, hex|base64, an optional prefix
|
|
5179
|
+
* and an optional timestamp header + tolerance. Like stripe/paddle/slack it
|
|
5180
|
+
* fails closed (401) until the source's signing secret is set. */
|
|
4973
5181
|
create: (input: {
|
|
4974
|
-
provider: "stripe" | "paddle" | "github" | "slack" | "revenuecat" | "generic";
|
|
5182
|
+
provider: "stripe" | "paddle" | "github" | "slack" | "revenuecat" | "generic" | "hmac";
|
|
4975
5183
|
name: string;
|
|
4976
5184
|
forward_url?: string;
|
|
5185
|
+
target_function?: string;
|
|
5186
|
+
verify?: WebhookHmacVerify;
|
|
4977
5187
|
}) => Promise<{
|
|
4978
5188
|
source_id: string;
|
|
4979
5189
|
receiver_url_path: string;
|
|
4980
5190
|
provider: string;
|
|
5191
|
+
target_function?: string | null;
|
|
4981
5192
|
}>;
|
|
4982
5193
|
list: () => Promise<Array<{
|
|
4983
5194
|
source_id: string;
|
|
4984
5195
|
provider: string;
|
|
4985
5196
|
name: string;
|
|
5197
|
+
forward_url?: string | null;
|
|
5198
|
+
target_function?: string | null;
|
|
5199
|
+
verify?: WebhookHmacVerify | null;
|
|
4986
5200
|
}>>;
|
|
4987
5201
|
delete: (sourceId: string) => Promise<void>;
|
|
4988
5202
|
/** Send a sample envelope to the source's forward_url through the real
|
|
@@ -4997,20 +5211,38 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
4997
5211
|
}>;
|
|
4998
5212
|
};
|
|
4999
5213
|
events: {
|
|
5214
|
+
/** Received events. Newest first, paged with `cursor`; or — with `after`
|
|
5215
|
+
* (your watermark, an event id) — OLDEST first from just after it, the
|
|
5216
|
+
* drain shape: store `next_after` and pass it back, `has_more` says
|
|
5217
|
+
* another page waits. The `after` drain answers only events received
|
|
5218
|
+
* at least 10 s ago (ids are not strictly arrival-ordered, so a fresher
|
|
5219
|
+
* event could otherwise commit below a stored watermark and be
|
|
5220
|
+
* skipped); `cursor` and `event_id` reads are not lagged. `event_id`
|
|
5221
|
+
* reads exactly one event. `cursor` and `after` are exclusive. */
|
|
5000
5222
|
list: (q?: {
|
|
5001
5223
|
source_id?: string;
|
|
5002
5224
|
status?: string;
|
|
5003
5225
|
cursor?: string;
|
|
5004
5226
|
limit?: number;
|
|
5227
|
+
after?: string;
|
|
5228
|
+
event_id?: string;
|
|
5005
5229
|
}) => Promise<{
|
|
5006
5230
|
events: Array<{
|
|
5007
5231
|
event_id: string;
|
|
5232
|
+
source_id: string;
|
|
5233
|
+
provider: string;
|
|
5234
|
+
provider_event_id: string | null;
|
|
5008
5235
|
event_type: string | null;
|
|
5009
5236
|
payload: Record<string, unknown>;
|
|
5010
5237
|
sig_verified: boolean;
|
|
5011
5238
|
status: string;
|
|
5239
|
+
received_at: string;
|
|
5012
5240
|
}>;
|
|
5013
5241
|
next_cursor: string | null;
|
|
5242
|
+
/** `after` pages only */
|
|
5243
|
+
next_after?: string | null;
|
|
5244
|
+
/** `after` pages only */
|
|
5245
|
+
has_more?: boolean;
|
|
5014
5246
|
}>;
|
|
5015
5247
|
replay: (eventId: string) => Promise<void>;
|
|
5016
5248
|
/** The Svix message-attempt view: delivery state/attempts/DLQ flag for
|
|
@@ -5231,8 +5463,14 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
5231
5463
|
* matching its TokenProvider type *structurally* (no import — a static
|
|
5232
5464
|
* import would break the single-file served sdk.mjs) that re-mints via
|
|
5233
5465
|
* POST /v1/realtime/tokens with `defaults` merged over the channel the
|
|
5234
|
-
* client asks for.
|
|
5235
|
-
*
|
|
5466
|
+
* client asks for. Two safe ways to use it:
|
|
5467
|
+
* - on a server, with a server key (`user_id` names the subject); or
|
|
5468
|
+
* - in a browser / mobile app, with a PUBLIC `end_user_required` key and
|
|
5469
|
+
* the signed-in user's session (`endUserToken` on the client): the mint
|
|
5470
|
+
* runs in end-user mode and the token's subject is FORCED to the
|
|
5471
|
+
* verified user (any `user_id` you pass is overridden), so no
|
|
5472
|
+
* backend of your own is needed.
|
|
5473
|
+
* Never ship a server key to a browser. */
|
|
5236
5474
|
tokenProvider: (defaults: {
|
|
5237
5475
|
user_id: string;
|
|
5238
5476
|
ttl_seconds?: number;
|
|
@@ -5808,6 +6046,9 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
5808
6046
|
model?: string;
|
|
5809
6047
|
user_id?: string;
|
|
5810
6048
|
input?: Record<string, unknown>;
|
|
6049
|
+
/** Drop chunks whose cosine `similarity` to the query is below this
|
|
6050
|
+
* (-1..1) before grounding; overrides rag config `retrieval.minSimilarity`. */
|
|
6051
|
+
min_similarity?: number;
|
|
5811
6052
|
}) => Promise<RagAnswer>;
|
|
5812
6053
|
/** Streamed grounded answer: citations + the channel handle up front, tokens
|
|
5813
6054
|
* over the realtime channel. */
|
|
@@ -5822,13 +6063,18 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
5822
6063
|
model?: string;
|
|
5823
6064
|
user_id?: string;
|
|
5824
6065
|
input?: Record<string, unknown>;
|
|
6066
|
+
/** Drop chunks whose cosine `similarity` to the query is below this
|
|
6067
|
+
* (-1..1) before grounding; overrides rag config `retrieval.minSimilarity`. */
|
|
6068
|
+
min_similarity?: number;
|
|
5825
6069
|
}) => Promise<RagStreamHandle>;
|
|
5826
6070
|
/** Retrieval-only grounding preview (guide ch. 6, rag): the exact chunks `answer`
|
|
5827
6071
|
* would ground on, with rerank + metadata boosts applied — no generation,
|
|
5828
6072
|
* no token spend. `boosts`/`rerank`/`min_score` override the rag config.
|
|
5829
6073
|
* `min_score` floors the EFFECTIVE `score`, which is a RANK value (about
|
|
5830
|
-
* 0.016–0.033 at the top): a floor above ~0.033 drops every hit.
|
|
5831
|
-
* relevance
|
|
6074
|
+
* 0.016–0.033 at the top): a floor above ~0.033 drops every hit. For a
|
|
6075
|
+
* relevance threshold use `min_similarity`, which floors each hit's cosine
|
|
6076
|
+
* `similarity` (-1..1; overrides config `retrieval.minSimilarity`; a hit
|
|
6077
|
+
* with no measured similarity, e.g. keyword mode, is kept). */
|
|
5832
6078
|
search: (input: {
|
|
5833
6079
|
query: string;
|
|
5834
6080
|
collection?: string;
|
|
@@ -5838,6 +6084,7 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
5838
6084
|
rerank?: boolean;
|
|
5839
6085
|
boosts?: Record<string, unknown>;
|
|
5840
6086
|
min_score?: number;
|
|
6087
|
+
min_similarity?: number;
|
|
5841
6088
|
}) => Promise<RagSearchResult>;
|
|
5842
6089
|
/** Ingest into the backing vector-search collection (chunk → embed → index). */
|
|
5843
6090
|
ingest: (collection: string, input: SearchIngestInput) => Promise<SearchIngestResult>;
|
|
@@ -5999,7 +6246,9 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
5999
6246
|
* (no oversell). Pass `job_id` to make the debit PROVISIONAL (held, not yet
|
|
6000
6247
|
* committed): a linked jobs run that terminally fails auto-refunds the hold,
|
|
6001
6248
|
* a success settles it. Throws VxilError(402, 'INSUFFICIENT_CREDITS') when the
|
|
6002
|
-
* available balance can't cover `amount`.
|
|
6249
|
+
* available balance can't cover `amount`. Throws VxilError(409,
|
|
6250
|
+
* 'job_released') when `job_id` names a generation run that already ended
|
|
6251
|
+
* (cancelled or failed) before this hold landed: nothing is held.
|
|
6003
6252
|
*
|
|
6004
6253
|
* Ordered credits: pass `credit_types` (1–8, in spend order — e.g.
|
|
6005
6254
|
* `['free', 'subscription', 'topup']`) instead of `credit_type`; the WHOLE
|
package/dist/index.js
CHANGED
|
@@ -429,6 +429,11 @@ export class Vxil {
|
|
|
429
429
|
return { data: parsed.data, meta: parsed.meta ?? { request_id: '' }, response: res };
|
|
430
430
|
}
|
|
431
431
|
users = {
|
|
432
|
+
/** Create or replace a tenant user by id. Upserting a deleted id brings it
|
|
433
|
+
* back; upserting an ERASED id re-creates it as a new, empty user
|
|
434
|
+
* (`erased_at` cleared — the erase already removed the old data). While
|
|
435
|
+
* the erased user's files are still being erased the call fails with 409
|
|
436
|
+
* `user_erase_pending`; retry later. `bulk` is all-or-nothing. */
|
|
432
437
|
upsert: async (user) => (await this.call('POST', '/v1/users', user)).data,
|
|
433
438
|
bulk: async (users) => (await this.call('POST', '/v1/users/bulk', { users })).data.upserted,
|
|
434
439
|
get: async (id, opts) => (await this.call('GET', `/v1/users/${encodeURIComponent(id)}${opts?.includeDeleted ? '?include_deleted=true' : ''}`)).data,
|
|
@@ -638,12 +643,24 @@ export class Vxil {
|
|
|
638
643
|
...(opts?.brief === false ? { brief: false } : {}),
|
|
639
644
|
})).data,
|
|
640
645
|
};
|
|
646
|
+
/** The project audit log. Needs `audit:read` (the narrow read-only scope —
|
|
647
|
+
* declare it on a function to read the log from its callback token) or
|
|
648
|
+
* `features:read`. Server-only: a public / thin-client key cannot read it. */
|
|
641
649
|
audit = {
|
|
650
|
+
/**
|
|
651
|
+
* The audit trail, newest first. `subject` narrows to the events about ONE
|
|
652
|
+
* thing: a user id (`payload.user_id` / `payload.id`), a cms record
|
|
653
|
+
* (`payload.item_id` — the record's history), a verified end user
|
|
654
|
+
* (`payload.end_user_id` — what that person changed through a thin-client
|
|
655
|
+
* key or a function acting for them, and their payments events), or a
|
|
656
|
+
* principal (`actor`). Server keys only.
|
|
657
|
+
*/
|
|
642
658
|
list: async (q) => {
|
|
643
659
|
const s = qs({
|
|
644
660
|
since: q?.since || undefined,
|
|
645
661
|
cursor: q?.cursor || undefined,
|
|
646
662
|
limit: q?.limit || undefined,
|
|
663
|
+
subject: q?.subject || undefined,
|
|
647
664
|
});
|
|
648
665
|
return (await this.call('GET', `/v1/audit${s}`)).data.events;
|
|
649
666
|
},
|
|
@@ -657,6 +674,7 @@ export class Vxil {
|
|
|
657
674
|
until: q?.until || undefined,
|
|
658
675
|
after_id: q?.after_id || undefined,
|
|
659
676
|
limit: q?.limit || undefined,
|
|
677
|
+
subject: q?.subject || undefined,
|
|
660
678
|
});
|
|
661
679
|
const { response: res, text } = await this.transport.send(`${this.base}${this.path(`/v1/audit/export${s}`)}`, {
|
|
662
680
|
method: 'GET',
|
|
@@ -864,7 +882,8 @@ export class Vxil {
|
|
|
864
882
|
* terminal run answers `409 not_cancellable`. */
|
|
865
883
|
cancel: async (runId) => (await this.call('POST', `/v1/jobs/runs/${encodeURIComponent(runId)}/cancel`)).data,
|
|
866
884
|
/** Clone a terminal run into a fresh queued run. A generation run answers
|
|
867
|
-
* `409 not_replayable` — submit the generation again instead
|
|
885
|
+
* `409 not_replayable` — submit the generation again instead; a redacted
|
|
886
|
+
* or erased run answers `409 run_redacted` (its payload is gone). */
|
|
868
887
|
replay: async (runId) => (await this.call('POST', `/v1/jobs/runs/${encodeURIComponent(runId)}/replay`)).data,
|
|
869
888
|
/** SERVER-ONLY (403 server_only in end-user mode). Account merge: move the
|
|
870
889
|
* credit-hold owner (`reserve_credits.user_id`) of every run of
|
|
@@ -878,6 +897,22 @@ export class Vxil {
|
|
|
878
897
|
* files re-key it does not check that `into_user_id` exists.
|
|
879
898
|
* Scope jobs:write. */
|
|
880
899
|
reKey: async (input) => (await this.call('POST', '/v1/jobs/runs/re-key', input)).data,
|
|
900
|
+
/** SERVER-ONLY (403 server_only in end-user mode). Redact FINISHED runs
|
|
901
|
+
* that hold personal data: the payload becomes `{ erased: true }`, the
|
|
902
|
+
* result, progress, callback body and error text are cleared, a
|
|
903
|
+
* generation's provider request body/headers are dropped and its credit
|
|
904
|
+
* hold's user id is pseudonymized (unless its settle is still owed). The
|
|
905
|
+
* row itself stays (state, timings, job_name). The same fields a GDPR
|
|
906
|
+
* erase clears on the runs it can link to a user — use this for runs
|
|
907
|
+
* nothing links to one (a plain queue run, an async function call).
|
|
908
|
+
* `{ run_ids }` (≤500, one call; open runs are left alone and counted in
|
|
909
|
+
* `skipped_open`) or `{ user_id }` (the generation runs whose credit hold
|
|
910
|
+
* names that user — ONE page of ≤500 per call, call again until `done`).
|
|
911
|
+
* Idempotent. Audited as `jobs.runs.redacted` (counts only; none when a
|
|
912
|
+
* call redacted nothing). NOT cleared: concurrency_key, debounce_key,
|
|
913
|
+
* idempotency_key and target_url — keep user ids out of those (hash them).
|
|
914
|
+
* A redacted run does not replay. Scope jobs:write. */
|
|
915
|
+
redact: async (input) => (await this.call('POST', '/v1/jobs/runs/redact', input)).data,
|
|
881
916
|
/**
|
|
882
917
|
* Suspend the RUNNING run until an event (call from the executing
|
|
883
918
|
* handler, then return 200 — the suspension wins). `state: 'resumed'` =
|
|
@@ -995,8 +1030,10 @@ export class Vxil {
|
|
|
995
1030
|
/** On success the guest keeps its user id (`user_id` unchanged).
|
|
996
1031
|
* If the claimed email ALREADY has an account, the guest is MERGED
|
|
997
1032
|
* into it: `user_id` is the existing account, `merged: true`, and a
|
|
998
|
-
* fresh `session.token` (same session
|
|
999
|
-
* identity — swap it client-side
|
|
1033
|
+
* fresh `session.token` (the same session carried over to the merged
|
|
1034
|
+
* identity under a new session id — swap it client-side: the old
|
|
1035
|
+
* guest token is revoked, the refresh token keeps working; other
|
|
1036
|
+
* guest sessions are revoked).
|
|
1000
1037
|
* `rekeyed` (present on a merge): true ⇒ the guest's payments / cms /
|
|
1001
1038
|
* files rows were already moved onto `user_id` when this returned, so
|
|
1002
1039
|
* the merged user's next request finds them; false ⇒ the move ran
|
|
@@ -1182,15 +1219,33 @@ export class Vxil {
|
|
|
1182
1219
|
},
|
|
1183
1220
|
confirm: async (token) => (await this.call('POST', '/v1/auth/email/verify/confirm', { token })).data,
|
|
1184
1221
|
},
|
|
1185
|
-
/** Social sign-in. The web `
|
|
1186
|
-
* (
|
|
1187
|
-
* `GET /v1/auth/oauth/{provider}/start
|
|
1188
|
-
*
|
|
1222
|
+
/** Social sign-in. The web flow: `startUrl` fetches the provider authorize
|
|
1223
|
+
* URL with your key (a browser cannot follow the header-carrying 302 of
|
|
1224
|
+
* `GET /v1/auth/oauth/{provider}/start` itself), you navigate the browser
|
|
1225
|
+
* there, and hand the returned `code`+`state` to `…/callback`; `native`
|
|
1189
1226
|
* is the mobile / broker token-exchange the SDK wraps. `oidc` is the
|
|
1190
1227
|
* tenant's generic OIDC / SSO issuer (auth config `providers.oidc` —
|
|
1191
1228
|
* Okta / Entra / Auth0 / any OpenID Connect IdP, or a SAML broker that
|
|
1192
1229
|
* speaks OIDC); it rides the same three routes. */
|
|
1193
1230
|
oauth: {
|
|
1231
|
+
/** Start the web sign-in from a browser SPA: answers the provider
|
|
1232
|
+
* `authorize_url` (PKCE challenge, one-shot `state`, and for `oidc` a
|
|
1233
|
+
* nonce bound to the flow — all held server-side) for you to navigate
|
|
1234
|
+
* to: `location.assign((await vx.auth.oauth.startUrl('oidc',
|
|
1235
|
+
* { redirect_uri })).authorize_url)`. The provider returns to
|
|
1236
|
+
* `redirect_uri` with `?code&state`, which your page sends to
|
|
1237
|
+
* `GET /v1/auth/oauth/{provider}/callback` for the session. The same
|
|
1238
|
+
* rules as the redirect form apply: `redirect_uri` must match the auth
|
|
1239
|
+
* config `security.allowedRedirectOrigins` when set (else
|
|
1240
|
+
* `422 redirect_not_allowed`), and the state expires after
|
|
1241
|
+
* `expires_in` seconds (600). `anonymous_token` (a guest bearer)
|
|
1242
|
+
* promotes or merges that guest at the callback. */
|
|
1243
|
+
startUrl: async (provider, input) => {
|
|
1244
|
+
// plain encodeURIComponent (no URLSearchParams — React Native ≤ 0.79)
|
|
1245
|
+
const q = `redirect_uri=${encodeURIComponent(input.redirect_uri)}`
|
|
1246
|
+
+ (input.anonymous_token ? `&anonymous_token=${encodeURIComponent(input.anonymous_token)}` : '');
|
|
1247
|
+
return (await this.call('GET', `/v1/auth/oauth/${encodeURIComponent(provider)}/start?${q}`, undefined, { accept: 'application/json' })).data;
|
|
1248
|
+
},
|
|
1194
1249
|
/** Native social sign-in: exchange a provider `id_token`/`access_token`
|
|
1195
1250
|
* for a vxil session (`linked` marks whether the user was created or
|
|
1196
1251
|
* matched to an existing identity). */
|
|
@@ -1201,6 +1256,9 @@ export class Vxil {
|
|
|
1201
1256
|
* (`linked: 'merged'`, `user_id` = that account, `rekeyed` says whether
|
|
1202
1257
|
* the guest's owner-scoped rows already moved — see
|
|
1203
1258
|
* `anonymous.link.verify`). */
|
|
1259
|
+
/** `nonce`: the value your app put on the IdP authorize request. For
|
|
1260
|
+
* When sent, the id_token's `nonce` claim must equal it (401
|
|
1261
|
+
* `oauth_token_invalid` otherwise); omitted, it is not checked. */
|
|
1204
1262
|
input) => (await this.call('POST', `/v1/auth/oauth/${encodeURIComponent(provider)}/native`, input)).data,
|
|
1205
1263
|
},
|
|
1206
1264
|
};
|
|
@@ -1311,6 +1369,19 @@ export class Vxil {
|
|
|
1311
1369
|
* (default) restores today's behaviour. Server keys are never affected.
|
|
1312
1370
|
* Audited; takes effect on the next request. */
|
|
1313
1371
|
setEndUserAccess: async (collection, access) => (await this.call('POST', `/v1/cms/collections/${encodeURIComponent(collection)}/fields`, { end_user_access: access })).data,
|
|
1372
|
+
/** Set (or clear, with `[]` / `null`) the collection's END-USER WRITE
|
|
1373
|
+
* ROLES (guide ch. 9): a verified end user must hold one of `roles` (the
|
|
1374
|
+
* session's verified role claims — orgs roles) to write the collection,
|
|
1375
|
+
* otherwise every write door answers `403 role_required`. Reads are
|
|
1376
|
+
* unaffected; server keys are never affected. Audited; takes effect on
|
|
1377
|
+
* the next write. */
|
|
1378
|
+
setWriteRoles: async (collection, roles) => (await this.call('POST', `/v1/cms/collections/${encodeURIComponent(collection)}/fields`, { write_roles: roles ?? [] })).data,
|
|
1379
|
+
/** Set (1..3650 days) or clear (`null`) the collection's RETENTION (guide
|
|
1380
|
+
* ch. 4): the nightly platform sweep soft-deletes live items created more
|
|
1381
|
+
* than `days` ago — bounded per project per night, no restore, purged at
|
|
1382
|
+
* least 30 days later like any delete, one `cms.collection.retention_swept` audit event
|
|
1383
|
+
* per collection per sweep. Audited (`cms.collection.retain_days.set`). */
|
|
1384
|
+
setRetention: async (collection, days) => (await this.call('POST', `/v1/cms/collections/${encodeURIComponent(collection)}/fields`, { retain_days: days })).data,
|
|
1314
1385
|
/** Re-project the collection's index slots after an `index_slot` move
|
|
1315
1386
|
* (guide ch. 4). Slots are projected on WRITE only, so until this runs,
|
|
1316
1387
|
* stored rows keep their OLD projection: the new slot is NULL and the
|
|
@@ -1379,6 +1450,16 @@ export class Vxil {
|
|
|
1379
1450
|
/** `lock` serializes same-key writers (a per-key lock); `guard`
|
|
1380
1451
|
* is the declarative capacity/overlap invariant (requires lock) — guide ch. 4. */
|
|
1381
1452
|
create: async (collection, input) => (await this.call('POST', `/v1/cms/items/${encodeURIComponent(collection)}`, input)).data,
|
|
1453
|
+
/** Upsert by a declared `unique` field (guide ch. 4): creates the item, or
|
|
1454
|
+
* — when a live item already holds `data[field]` — merges `patch` into
|
|
1455
|
+
* THAT item instead (one transaction; update hooks, guards, version bump
|
|
1456
|
+
* and a `cms.item.updated` audit, exactly like `patch`; null clears a
|
|
1457
|
+
* field). `created` tells which happened (201 vs 200). Concurrent upserts
|
|
1458
|
+
* of one key serialize, so they never create a duplicate. */
|
|
1459
|
+
upsert: async (collection, input) => {
|
|
1460
|
+
const r = await this.call('POST', `/v1/cms/items/${encodeURIComponent(collection)}`, input);
|
|
1461
|
+
return { ...r.data, created: r.response.status === 201 };
|
|
1462
|
+
},
|
|
1382
1463
|
/** `expand` inlines relation/file fields into `data` (guide ch. 4): the
|
|
1383
1464
|
* full item envelope, a `{ object_id, $ref: 'files' }` stub for a file,
|
|
1384
1465
|
* the bare id on a cycle/depth cut, `null` for an invisible target. */
|
|
@@ -1388,7 +1469,9 @@ export class Vxil {
|
|
|
1388
1469
|
},
|
|
1389
1470
|
/**
|
|
1390
1471
|
* The bounded query DSL. filter ops: $eq $ne $gt $gte $lt $lte $in
|
|
1391
|
-
* $
|
|
1472
|
+
* ($ne is NULL-safe: a row whose field is absent or null counts as not
|
|
1473
|
+
* equal; on a dotted join term the row still needs a readable target,
|
|
1474
|
+
* so a null or dangling relation is excluded) $contains $startsWith (LIKE-escaped; substring-searchable on s*-slotted
|
|
1392
1475
|
* fields) $arrayContains/$anyOf (json/relation array containment, indexed);
|
|
1393
1476
|
* range/sort needs slot-indexed fields. This is the
|
|
1394
1477
|
* DELIBERATELY-UNTYPED escape hatch: it accepts everything the server
|
|
@@ -1619,6 +1702,17 @@ export class Vxil {
|
|
|
1619
1702
|
* `features:write`. (DELETE /v1/functions/:name)
|
|
1620
1703
|
*/
|
|
1621
1704
|
delete: async (name, opts) => (await this.call('DELETE', `/v1/functions/${encodeURIComponent(name)}`, undefined, opts?.ifMatch !== undefined ? { 'if-match': String(opts.ifMatch) } : {})).data,
|
|
1705
|
+
/**
|
|
1706
|
+
* Log records of ONE function, one per invocation (outcome, wall/cpu ms,
|
|
1707
|
+
* console lines, exceptions), oldest first. Pass the returned `next_since`
|
|
1708
|
+
* back as `since` to read only newer records (a tail); without it, the most
|
|
1709
|
+
* recent records of the last 24 hours. Kept 14 days. Needs
|
|
1710
|
+
* `functions:read`. (GET /v1/functions/:name/logs)
|
|
1711
|
+
*/
|
|
1712
|
+
logs: async (name, opts) => {
|
|
1713
|
+
const s = qs({ since: opts?.since || undefined });
|
|
1714
|
+
return (await this.call('GET', `/v1/functions/${encodeURIComponent(name)}/logs${s}`)).data;
|
|
1715
|
+
},
|
|
1622
1716
|
};
|
|
1623
1717
|
/** The MCP aggregation surface (the `mcp` feature). */
|
|
1624
1718
|
mcp = {
|
|
@@ -1757,7 +1851,19 @@ export class Vxil {
|
|
|
1757
1851
|
*/
|
|
1758
1852
|
test: async (subId) => (await this.call('POST', `/v1/webhooks/subscriptions/${encodeURIComponent(subId)}/test`)).data,
|
|
1759
1853
|
sources: {
|
|
1760
|
-
/** Register an inbound source; the receiver URL is returned ONCE.
|
|
1854
|
+
/** Register an inbound source; the receiver URL is returned ONCE.
|
|
1855
|
+
*
|
|
1856
|
+
* Destination: `forward_url` (your endpoint) OR `target_function` (a
|
|
1857
|
+
* deployed function that declares a bare `trigger: { kind: 'webhook' }`
|
|
1858
|
+
* binding — it receives `payload.event === 'inbound_webhook.received'`
|
|
1859
|
+
* with the inbound envelope as `payload.data`), never both; neither =
|
|
1860
|
+
* store only (read with `events.list`).
|
|
1861
|
+
*
|
|
1862
|
+
* `provider: 'hmac'` is the parameterized preset for any provider that
|
|
1863
|
+
* signs an HMAC of the raw body (Shopify, Intercom, Linear, Zendesk, …):
|
|
1864
|
+
* `verify` says which header, sha256|sha1, hex|base64, an optional prefix
|
|
1865
|
+
* and an optional timestamp header + tolerance. Like stripe/paddle/slack it
|
|
1866
|
+
* fails closed (401) until the source's signing secret is set. */
|
|
1761
1867
|
create: async (input) => (await this.call('POST', '/v1/webhooks/sources', input)).data,
|
|
1762
1868
|
list: async () => (await this.call('GET', '/v1/webhooks/sources')).data.sources,
|
|
1763
1869
|
delete: async (sourceId) => {
|
|
@@ -1768,11 +1874,21 @@ export class Vxil {
|
|
|
1768
1874
|
test: async (sourceId) => (await this.call('POST', `/v1/webhooks/sources/${encodeURIComponent(sourceId)}/test`)).data,
|
|
1769
1875
|
},
|
|
1770
1876
|
events: {
|
|
1877
|
+
/** Received events. Newest first, paged with `cursor`; or — with `after`
|
|
1878
|
+
* (your watermark, an event id) — OLDEST first from just after it, the
|
|
1879
|
+
* drain shape: store `next_after` and pass it back, `has_more` says
|
|
1880
|
+
* another page waits. The `after` drain answers only events received
|
|
1881
|
+
* at least 10 s ago (ids are not strictly arrival-ordered, so a fresher
|
|
1882
|
+
* event could otherwise commit below a stored watermark and be
|
|
1883
|
+
* skipped); `cursor` and `event_id` reads are not lagged. `event_id`
|
|
1884
|
+
* reads exactly one event. `cursor` and `after` are exclusive. */
|
|
1771
1885
|
list: async (q) => {
|
|
1772
1886
|
const s = qs({
|
|
1773
1887
|
source_id: q?.source_id || undefined,
|
|
1774
1888
|
status: q?.status || undefined,
|
|
1775
1889
|
cursor: q?.cursor || undefined,
|
|
1890
|
+
after: q?.after || undefined,
|
|
1891
|
+
event_id: q?.event_id || undefined,
|
|
1776
1892
|
limit: q?.limit || undefined,
|
|
1777
1893
|
});
|
|
1778
1894
|
return (await this.call('GET', `/v1/webhooks/events${s}`)).data;
|
|
@@ -1875,8 +1991,14 @@ export class Vxil {
|
|
|
1875
1991
|
* matching its TokenProvider type *structurally* (no import — a static
|
|
1876
1992
|
* import would break the single-file served sdk.mjs) that re-mints via
|
|
1877
1993
|
* POST /v1/realtime/tokens with `defaults` merged over the channel the
|
|
1878
|
-
* client asks for.
|
|
1879
|
-
*
|
|
1994
|
+
* client asks for. Two safe ways to use it:
|
|
1995
|
+
* - on a server, with a server key (`user_id` names the subject); or
|
|
1996
|
+
* - in a browser / mobile app, with a PUBLIC `end_user_required` key and
|
|
1997
|
+
* the signed-in user's session (`endUserToken` on the client): the mint
|
|
1998
|
+
* runs in end-user mode and the token's subject is FORCED to the
|
|
1999
|
+
* verified user (any `user_id` you pass is overridden), so no
|
|
2000
|
+
* backend of your own is needed.
|
|
2001
|
+
* Never ship a server key to a browser. */
|
|
1880
2002
|
tokenProvider: (defaults) => (ctx) => this.realtime.mintToken({ channel: ctx.channel, ...defaults }),
|
|
1881
2003
|
/** One-shot: mint a token AND open the WebSocket. Uses the global
|
|
1882
2004
|
* `WebSocket` (browser / Node ≥22). On older Node, pass a constructor —
|
|
@@ -2170,8 +2292,10 @@ export class Vxil {
|
|
|
2170
2292
|
* would ground on, with rerank + metadata boosts applied — no generation,
|
|
2171
2293
|
* no token spend. `boosts`/`rerank`/`min_score` override the rag config.
|
|
2172
2294
|
* `min_score` floors the EFFECTIVE `score`, which is a RANK value (about
|
|
2173
|
-
* 0.016–0.033 at the top): a floor above ~0.033 drops every hit.
|
|
2174
|
-
* relevance
|
|
2295
|
+
* 0.016–0.033 at the top): a floor above ~0.033 drops every hit. For a
|
|
2296
|
+
* relevance threshold use `min_similarity`, which floors each hit's cosine
|
|
2297
|
+
* `similarity` (-1..1; overrides config `retrieval.minSimilarity`; a hit
|
|
2298
|
+
* with no measured similarity, e.g. keyword mode, is kept). */
|
|
2175
2299
|
search: async (input) => (await this.call('POST', '/v1/rag/search', input)).data,
|
|
2176
2300
|
/** Ingest into the backing vector-search collection (chunk → embed → index). */
|
|
2177
2301
|
ingest: async (collection, input) => (await this.call('POST', `/v1/rag/ingest/${encodeURIComponent(collection)}`, input)).data,
|
|
@@ -2309,7 +2433,9 @@ export class Vxil {
|
|
|
2309
2433
|
* (no oversell). Pass `job_id` to make the debit PROVISIONAL (held, not yet
|
|
2310
2434
|
* committed): a linked jobs run that terminally fails auto-refunds the hold,
|
|
2311
2435
|
* a success settles it. Throws VxilError(402, 'INSUFFICIENT_CREDITS') when the
|
|
2312
|
-
* available balance can't cover `amount`.
|
|
2436
|
+
* available balance can't cover `amount`. Throws VxilError(409,
|
|
2437
|
+
* 'job_released') when `job_id` names a generation run that already ended
|
|
2438
|
+
* (cancelled or failed) before this hold landed: nothing is held.
|
|
2313
2439
|
*
|
|
2314
2440
|
* Ordered credits: pass `credit_types` (1–8, in spend order — e.g.
|
|
2315
2441
|
* `['free', 'subscription', 'topup']`) instead of `credit_type`; the WHOLE
|
package/package.json
CHANGED