@vxil/sdk 0.5.0 → 0.5.2

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
@@ -399,6 +399,12 @@ export interface PaymentsWebhookEvent {
399
399
  event_id: string;
400
400
  provider: string;
401
401
  provider_evt_id: string;
402
+ /** the NORMALISED charge id the receiver stamped at delivery time — the same
403
+ * string `payments.charge.*` events carry as `provider_charge_id`. `null`
404
+ * when the delivery carries no charge (subscription events, failures) or
405
+ * was received before the column existed. Filter on it with
406
+ * `webhookEvents.list({ provider_charge_id })`. */
407
+ provider_charge_id?: string | null;
402
408
  event_type: string;
403
409
  /** received | processed | error | sig_failed | parse_failed | reprocessed |
404
410
  * ignored (a well-formed provider type we deliberately do not fold) |
@@ -640,6 +646,8 @@ export interface AiChatMessage {
640
646
  /** A synchronous generation result (POST /v1/ai/generate without stream). */
641
647
  export interface AiGeneration {
642
648
  generation_id: string;
649
+ /** your own `correlation_id` echoed back (null when you sent none). */
650
+ correlation_id?: string | null;
643
651
  text: string;
644
652
  usage: AiUsage;
645
653
  finish: string;
@@ -655,6 +663,8 @@ export interface AiGeneration {
655
663
  /** A streamed generation handle: open the realtime channel for token frames. */
656
664
  export interface AiStreamHandle {
657
665
  generation_id: string;
666
+ /** your own `correlation_id` echoed back (null when you sent none). */
667
+ correlation_id?: string | null;
658
668
  channel: string;
659
669
  token?: string;
660
670
  ttl_seconds?: number;
@@ -696,6 +706,10 @@ export interface AiTokenExpiringEvent {
696
706
  export interface AiJobHandle {
697
707
  generation_id: string;
698
708
  run_id: string;
709
+ /** your own `correlation_id` echoed back (null when you sent none). The
710
+ * `job.generation.completed|failed` events carry BOTH `generation_id` and
711
+ * `correlation_id` next to `run_id`, so no run→record link is needed. */
712
+ correlation_id?: string | null;
699
713
  status: string;
700
714
  resume_path: string;
701
715
  }
@@ -2110,20 +2124,38 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
2110
2124
  * `otp.testRecipients` (store-review / CI accounts): no mail is sent and
2111
2125
  * the full sign-in link comes back instead. `captcha_token` is required
2112
2126
  * when the tenant configured `security.captchaSecretRef`; `locale`
2113
- * chooses the mail's language. */
2127
+ * chooses the mail's language. `redirect_url` is an https page in your
2128
+ * app — or `http://localhost[:port]` / `http://127.0.0.1[:port]` for local
2129
+ * development (plain http on any other host is refused).
2130
+ *
2131
+ * GUEST CLAIM: pass the guest's session bearer as `anonymous_token` and
2132
+ * the link claims `email` FOR THAT GUEST — `verify` (which must present
2133
+ * the same `anonymous_token`) then keeps the guest's `user_id` (every
2134
+ * owner-scoped row survives) or, when the address already has an
2135
+ * account, merges the guest into it (`merged: true`, `user_id` = the
2136
+ * existing account; swap tokens). Needs `anonymous.enabled`; a non-guest
2137
+ * bearer is `409 not_anonymous`. */
2114
2138
  request: (input: {
2115
2139
  email: string;
2116
2140
  redirect_url: string;
2117
2141
  locale?: string;
2118
2142
  captcha_token?: string;
2143
+ anonymous_token?: string;
2119
2144
  }) => Promise<{
2120
2145
  sent: true;
2121
2146
  test_link?: string;
2122
2147
  }>;
2123
- verify: (token: string) => Promise<{
2148
+ /** `opts.anonymous_token` is REQUIRED for a link that was requested with
2149
+ * one (the guest claim above): the same guest session must present it,
2150
+ * else `401 invalid_session`. `merged` is true only when the guest was
2151
+ * folded into an existing account (then `user_id` is that account). */
2152
+ verify: (token: string, opts?: {
2153
+ anonymous_token?: string;
2154
+ }) => Promise<{
2124
2155
  user_id: string;
2125
2156
  session: AuthSession;
2126
2157
  verified: boolean;
2158
+ merged?: boolean;
2127
2159
  }>;
2128
2160
  };
2129
2161
  /** Email OTP sign-in: a 6-digit single-use code (distinct from magic-link).
@@ -2189,6 +2221,37 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
2189
2221
  }>;
2190
2222
  };
2191
2223
  };
2224
+ /** E-mail CHANGE for a signed-in (non-anonymous) user: a 6-digit code is
2225
+ * mailed to the NEW address; verify swaps the account's address (both the
2226
+ * auth row and the shared identity row, in one transaction), marks it
2227
+ * verified, and revokes every OTHER session of the user (reason
2228
+ * `email_changed`) — the presenting `token` keeps working. `request` is
2229
+ * always 202 whether or not the address is taken (no enumeration);
2230
+ * a taken address surfaces at `verify` as 409 `email_in_use`. A guest
2231
+ * session is 409 `anonymous_user` — guests use `anonymous.link`. Shares
2232
+ * the OTP knobs (cooldown → 429 `otp_rate_limited`, attempts, TTL,
2233
+ * `test_code` for `otp.testRecipients`) but does NOT need `otp.enabled`. */
2234
+ email: {
2235
+ change: {
2236
+ request: (input: {
2237
+ token: string;
2238
+ new_email: string;
2239
+ locale?: string;
2240
+ }) => Promise<{
2241
+ sent: true;
2242
+ test_code?: string;
2243
+ }>;
2244
+ verify: (input: {
2245
+ token: string;
2246
+ new_email: string;
2247
+ code: string;
2248
+ }) => Promise<{
2249
+ user_id: string;
2250
+ email: string;
2251
+ verified: true;
2252
+ }>;
2253
+ };
2254
+ };
2192
2255
  /** Step-up re-auth: an OTP challenge on the CURRENT session. On verify the
2193
2256
  * bearer token ROTATES (the returned session.token replaces the old one —
2194
2257
  * swap it client-side) and the JWT gains an `elv` claim. */
@@ -2668,6 +2731,21 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
2668
2731
  }>;
2669
2732
  scanned: number;
2670
2733
  }>;
2734
+ /** SERVER-ONLY (403 server_only in end-user mode). Account merge: move
2735
+ * every row `from_user_id` owns — across every collection that declares
2736
+ * an `owner_field`, soft-deleted rows included, the stored owner value
2737
+ * AND its indexed projection — onto `into_user_id`. ONE bounded page (≤500
2738
+ * rows) per call: loop while `done` is false. Idempotent (a second pass
2739
+ * moves 0). Bumps each moved row's `version`. The cms consumer of the
2740
+ * `auth.user.merged { from, into }` event; auth-v1 calls it after a
2741
+ * merge, the event is the backstop. Emits one `cms.items.rekeyed`. */
2742
+ reKey: (input: {
2743
+ from_user_id: string;
2744
+ into_user_id: string;
2745
+ }) => Promise<{
2746
+ moved: number;
2747
+ done: boolean;
2748
+ }>;
2671
2749
  };
2672
2750
  /** cms-rel B4 (cms.md §13): atomic multi-collection transaction — 1–5
2673
2751
  * steps over ≤3 collections, per-step `$where` CAS preconditions (the
@@ -3505,6 +3583,22 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
3505
3583
  object_id: string;
3506
3584
  expires_at: string | null;
3507
3585
  }>;
3586
+ /** SERVER-ONLY (403 server_only in end-user mode). Account merge: move
3587
+ * every object `from_user_id` owns (every status, tombstones included) and
3588
+ * the shared links minted on them onto `into_user_id`, which must be a
3589
+ * live tenant user (404 user_not_found otherwise). ONE bounded page (≤500
3590
+ * objects) per call: loop while `done` is false. Idempotent. An erased
3591
+ * `from` user's objects are held back for the GDPR byte sweep (moved 0).
3592
+ * The files consumer of the `auth.user.merged { from, into }` event;
3593
+ * auth-v1 calls it after a merge, the event is the backstop. Emits one
3594
+ * `files.objects.rekeyed`. */
3595
+ reKey: (input: {
3596
+ from_user_id: string;
3597
+ into_user_id: string;
3598
+ }) => Promise<{
3599
+ moved: number;
3600
+ done: boolean;
3601
+ }>;
3508
3602
  sharedLinks: {
3509
3603
  /** Public (unauthenticated) URL for the object; revocable. Optional
3510
3604
  * `max_downloads` (1 = one-time link) and an absolute `expires_at`; the
@@ -3614,7 +3708,8 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
3614
3708
  * openrouter) route via BYO keys in tenant_secrets. `images` on the generate
3615
3709
  * inputs takes up to 8 vision refs: a public https:// URL, a
3616
3710
  * data:image/...;base64 URL, or file:<object_id> (a files-feature object) —
3617
- * fetched images are capped at 4 MiB each / 16 MiB total.
3711
+ * fetched images are capped at 4 MiB each; `documents` (pdf/text, ≤10 MiB
3712
+ * each) share one 20 MiB inline ceiling per request with them.
3618
3713
  */
3619
3714
  readonly ai: {
3620
3715
  templates: {
@@ -3694,6 +3789,10 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
3694
3789
  ttl?: "5m" | "1h";
3695
3790
  };
3696
3791
  user_id?: string;
3792
+ /** Your own opaque handle (1..128 chars) for this generation — stored on
3793
+ * the row, echoed on the answer and the replay read, and (job mode)
3794
+ * carried on the `job.generation.*` events with `generation_id`. */
3795
+ correlation_id?: string;
3697
3796
  }) => Promise<AiGeneration>;
3698
3797
  /** Streamed generation: returns the channel + connect token immediately; open
3699
3798
  * the realtime channel for AiStreamFrame frames (token/title, then a
@@ -3723,6 +3822,10 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
3723
3822
  * {{prompt}}); overrides the built-in instruction (stream-only). */
3724
3823
  title_template?: string;
3725
3824
  user_id?: string;
3825
+ /** Your own opaque handle (1..128 chars) for this generation — stored on
3826
+ * the row, echoed on the answer and the replay read, and (job mode)
3827
+ * carried on the `job.generation.*` events with `generation_id`. */
3828
+ correlation_id?: string;
3726
3829
  }) => Promise<AiStreamHandle>;
3727
3830
  /** Job-routed async generation (long/vision/batch): 202 + a jobs run drives
3728
3831
  * the provider call; the settled answer lands in the replay buffer at
@@ -3771,6 +3874,10 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
3771
3874
  ttl?: "5m" | "1h";
3772
3875
  };
3773
3876
  user_id?: string;
3877
+ /** Your own opaque handle (1..128 chars) for this generation — stored on
3878
+ * the row, echoed on the answer and the replay read, and (job mode)
3879
+ * carried on the `job.generation.*` events with `generation_id`. */
3880
+ correlation_id?: string;
3774
3881
  }) => Promise<AiJobHandle>;
3775
3882
  /** Re-mint a fresh connect token for a LIVE stream (a generation that
3776
3883
  * outlives the ≤300s realtime token TTL); reconnect with `?since=<seq>`. */
@@ -4336,6 +4443,13 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
4336
4443
  outcome?: "received" | "processed" | "error" | "sig_failed" | "parse_failed" | "reprocessed" | "ignored" | "unowned" | "rejected_environment";
4337
4444
  /** the provider-reported environment axis (not the API key's label) */
4338
4445
  environment?: "production" | "sandbox";
4446
+ /** exact match on the normalised charge id (= `payments.charge.*`'s
4447
+ * `provider_charge_id`) — the delivery that recorded a charge, and any
4448
+ * refund/dispute delivery against it, in ≤ a few rows; then ONE
4449
+ * detail `get` reads the verified payload. */
4450
+ provider_charge_id?: string;
4451
+ /** exact match on the provider's own event id. */
4452
+ provider_evt_id?: string;
4339
4453
  since?: string;
4340
4454
  cursor?: string;
4341
4455
  limit?: number;
package/dist/index.js CHANGED
@@ -635,9 +635,23 @@ export class Vxil {
635
635
  * `otp.testRecipients` (store-review / CI accounts): no mail is sent and
636
636
  * the full sign-in link comes back instead. `captcha_token` is required
637
637
  * when the tenant configured `security.captchaSecretRef`; `locale`
638
- * chooses the mail's language. */
638
+ * chooses the mail's language. `redirect_url` is an https page in your
639
+ * app — or `http://localhost[:port]` / `http://127.0.0.1[:port]` for local
640
+ * development (plain http on any other host is refused).
641
+ *
642
+ * GUEST CLAIM: pass the guest's session bearer as `anonymous_token` and
643
+ * the link claims `email` FOR THAT GUEST — `verify` (which must present
644
+ * the same `anonymous_token`) then keeps the guest's `user_id` (every
645
+ * owner-scoped row survives) or, when the address already has an
646
+ * account, merges the guest into it (`merged: true`, `user_id` = the
647
+ * existing account; swap tokens). Needs `anonymous.enabled`; a non-guest
648
+ * bearer is `409 not_anonymous`. */
639
649
  request: async (input) => (await this.call('POST', '/v1/auth/magic-link/request', input)).data,
640
- verify: async (token) => (await this.call('POST', '/v1/auth/magic-link/verify', { token })).data,
650
+ /** `opts.anonymous_token` is REQUIRED for a link that was requested with
651
+ * one (the guest claim above): the same guest session must present it,
652
+ * else `401 invalid_session`. `merged` is true only when the guest was
653
+ * folded into an existing account (then `user_id` is that account). */
654
+ verify: async (token, opts = {}) => (await this.call('POST', '/v1/auth/magic-link/verify', { token, ...(opts.anonymous_token !== undefined ? { anonymous_token: opts.anonymous_token } : {}) })).data,
641
655
  },
642
656
  /** Email OTP sign-in: a 6-digit single-use code (distinct from magic-link).
643
657
  * Server-side guessing budget (config otp.maxAttempts), single active code,
@@ -664,6 +678,22 @@ export class Vxil {
664
678
  verify: async (input) => (await this.call('POST', '/v1/auth/anonymous/link/verify', input)).data,
665
679
  },
666
680
  },
681
+ /** E-mail CHANGE for a signed-in (non-anonymous) user: a 6-digit code is
682
+ * mailed to the NEW address; verify swaps the account's address (both the
683
+ * auth row and the shared identity row, in one transaction), marks it
684
+ * verified, and revokes every OTHER session of the user (reason
685
+ * `email_changed`) — the presenting `token` keeps working. `request` is
686
+ * always 202 whether or not the address is taken (no enumeration);
687
+ * a taken address surfaces at `verify` as 409 `email_in_use`. A guest
688
+ * session is 409 `anonymous_user` — guests use `anonymous.link`. Shares
689
+ * the OTP knobs (cooldown → 429 `otp_rate_limited`, attempts, TTL,
690
+ * `test_code` for `otp.testRecipients`) but does NOT need `otp.enabled`. */
691
+ email: {
692
+ change: {
693
+ request: async (input) => (await this.call('POST', '/v1/auth/email/change/request', input)).data,
694
+ verify: async (input) => (await this.call('POST', '/v1/auth/email/change/verify', input)).data,
695
+ },
696
+ },
667
697
  /** Step-up re-auth: an OTP challenge on the CURRENT session. On verify the
668
698
  * bearer token ROTATES (the returned session.token replaces the old one —
669
699
  * swap it client-side) and the JWT gains an `elv` claim. */
@@ -983,6 +1013,15 @@ export class Vxil {
983
1013
  * row_number|rank|percent_rank, computed over ≤500 aggregated groups
984
1014
  * (never raw rows), optional partitionBy. Same scan cap as aggregate. */
985
1015
  rank: async (collection, body) => (await this.call('POST', `/v1/cms/items/${encodeURIComponent(collection)}/rank`, body)).data,
1016
+ /** SERVER-ONLY (403 server_only in end-user mode). Account merge: move
1017
+ * every row `from_user_id` owns — across every collection that declares
1018
+ * an `owner_field`, soft-deleted rows included, the stored owner value
1019
+ * AND its indexed projection — onto `into_user_id`. ONE bounded page (≤500
1020
+ * rows) per call: loop while `done` is false. Idempotent (a second pass
1021
+ * moves 0). Bumps each moved row's `version`. The cms consumer of the
1022
+ * `auth.user.merged { from, into }` event; auth-v1 calls it after a
1023
+ * merge, the event is the backstop. Emits one `cms.items.rekeyed`. */
1024
+ reKey: async (input) => (await this.call('POST', '/v1/cms/items/re-key', input)).data,
986
1025
  },
987
1026
  /** cms-rel B4 (cms.md §13): atomic multi-collection transaction — 1–5
988
1027
  * steps over ≤3 collections, per-step `$where` CAS preconditions (the
@@ -1365,6 +1404,16 @@ export class Vxil {
1365
1404
  /** Set / extend / clear an object's TTL. `expiresInSeconds: null` clears it
1366
1405
  * (else a future auto-delete after the given seconds; minimum 60). */
1367
1406
  setTtl: async (objectId, expiresInSeconds) => (await this.call('PUT', `/v1/files/${encodeURIComponent(objectId)}/ttl`, { expiresInSeconds })).data,
1407
+ /** SERVER-ONLY (403 server_only in end-user mode). Account merge: move
1408
+ * every object `from_user_id` owns (every status, tombstones included) and
1409
+ * the shared links minted on them onto `into_user_id`, which must be a
1410
+ * live tenant user (404 user_not_found otherwise). ONE bounded page (≤500
1411
+ * objects) per call: loop while `done` is false. Idempotent. An erased
1412
+ * `from` user's objects are held back for the GDPR byte sweep (moved 0).
1413
+ * The files consumer of the `auth.user.merged { from, into }` event;
1414
+ * auth-v1 calls it after a merge, the event is the backstop. Emits one
1415
+ * `files.objects.rekeyed`. */
1416
+ reKey: async (input) => (await this.call('POST', '/v1/files/objects/re-key', input)).data,
1368
1417
  sharedLinks: {
1369
1418
  /** Public (unauthenticated) URL for the object; revocable. Optional
1370
1419
  * `max_downloads` (1 = one-time link) and an absolute `expires_at`; the
@@ -1432,7 +1481,8 @@ export class Vxil {
1432
1481
  * openrouter) route via BYO keys in tenant_secrets. `images` on the generate
1433
1482
  * inputs takes up to 8 vision refs: a public https:// URL, a
1434
1483
  * data:image/...;base64 URL, or file:<object_id> (a files-feature object) —
1435
- * fetched images are capped at 4 MiB each / 16 MiB total.
1484
+ * fetched images are capped at 4 MiB each; `documents` (pdf/text, ≤10 MiB
1485
+ * each) share one 20 MiB inline ceiling per request with them.
1436
1486
  */
1437
1487
  ai = {
1438
1488
  templates: {
@@ -1785,6 +1835,8 @@ export class Vxil {
1785
1835
  event_type: q?.event_type || undefined,
1786
1836
  outcome: q?.outcome || undefined,
1787
1837
  environment: q?.environment || undefined,
1838
+ provider_charge_id: q?.provider_charge_id || undefined,
1839
+ provider_evt_id: q?.provider_evt_id || undefined,
1788
1840
  since: q?.since || undefined,
1789
1841
  cursor: q?.cursor || undefined,
1790
1842
  limit: q?.limit || undefined,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vxil/sdk",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Typed client for the Vxil REST API (notifications, auth, jobs, files, cms, comments, webhooks, realtime, orgs, rate-limits).",