@vxil/sdk 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -6
- package/dist/index.d.ts +759 -64
- package/dist/index.js +405 -270
- package/dist/qs.d.ts +15 -0
- package/dist/qs.js +48 -0
- package/dist/retry.d.ts +47 -0
- package/dist/retry.js +156 -0
- package/package.json +2 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,77 @@
|
|
|
1
|
-
/** The released API majors, as a CLOSED union — one per released major (docs/
|
|
2
|
-
* feature-versioning.md §3). `'v1'` is the only released major, so pinning
|
|
3
|
-
* anything else (`apiVersion: 'v2'`) is a COMPILE error until a v2 GAs and
|
|
4
|
-
* widens this union. Every path the SDK builds is major-versioned; the default
|
|
5
|
-
* is the compile-time constant `'v1'` (never a floating `latest` alias). */
|
|
6
1
|
export type ApiVersion = 'v1';
|
|
7
2
|
/** A per-feature API-version override key: the URL namespace that leads a path
|
|
8
3
|
* (`/v1/<namespace>/…`). Matches the segment `versionedPath` rewrites, so an
|
|
9
4
|
* override changes exactly that namespace's paths. Renamed client accessors map
|
|
10
5
|
* onto their real namespaces (`vx.search` → `search`, `vx.feeds` → `feeds`). */
|
|
11
6
|
export type FeatureKey = 'users' | 'notifications' | 'config' | 'features' | 'audit' | 'usage' | 'jobs' | 'auth' | 'rate-limits' | 'files' | 'cms' | 'comments' | 'dm' | 'feeds' | 'realtime' | 'orgs' | 'webhooks' | 'search' | 'ai' | 'rag' | 'payments' | 'fn' | 'copilot';
|
|
7
|
+
/** Retry policy for the client (DEFAULT OFF: `attempts` is `0`). Only
|
|
8
|
+
* idempotent requests are ever retried — GET/HEAD/PUT/DELETE, and a POST only
|
|
9
|
+
* when the call carries an `Idempotency-Key` header (`{ idempotencyKey }` on
|
|
10
|
+
* the money routes) — so a retry can never double-apply a write. */
|
|
11
|
+
export interface VxilRetryOptions {
|
|
12
|
+
/** How many RETRIES to make after the first attempt (`2` ⇒ up to 3 requests).
|
|
13
|
+
* Default `0` = retries off. */
|
|
14
|
+
attempts?: number;
|
|
15
|
+
/** Response statuses that trigger a retry. Default `[429, 502, 503, 504]`. */
|
|
16
|
+
retryOn?: number[];
|
|
17
|
+
/** Base delay before the first retry; doubles each retry (with jitter in
|
|
18
|
+
* `[½, 1]` of the computed delay). Default `250`. */
|
|
19
|
+
backoffMs?: number;
|
|
20
|
+
/** The longest the client will ever wait between attempts. Also the cap on
|
|
21
|
+
* an honoured `Retry-After`: when the server asks for MORE than this the
|
|
22
|
+
* client does not retry and surfaces the error (its `retryAfter` tells you
|
|
23
|
+
* how long the server asked for). Default `10_000`. */
|
|
24
|
+
maxBackoffMs?: number;
|
|
25
|
+
/** Use the response's `Retry-After` header (seconds or an HTTP-date) as the
|
|
26
|
+
* delay instead of the backoff schedule when present. Default `true`. */
|
|
27
|
+
respectRetryAfter?: boolean;
|
|
28
|
+
/** Also retry when `fetch` itself fails (DNS, connection reset, a
|
|
29
|
+
* `timeoutMs` timeout) — again only for idempotent requests. Default `true`
|
|
30
|
+
* (inert while `attempts` is `0`). */
|
|
31
|
+
retryOnNetworkError?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/** What a hook sees of a request: a FROZEN descriptor. Credential headers
|
|
34
|
+
* (`authorization`, `x-vxil-end-user`) are never included. */
|
|
35
|
+
export interface VxilRequestInfo {
|
|
36
|
+
readonly method: string;
|
|
37
|
+
readonly url: string;
|
|
38
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
39
|
+
/** 1 for the first attempt, 2 for the first retry, … */
|
|
40
|
+
readonly attempt: number;
|
|
41
|
+
}
|
|
42
|
+
/** Request lifecycle hooks. Each may be async; a thrown error propagates to
|
|
43
|
+
* the caller (the request is not sent / the response is not returned). */
|
|
44
|
+
export interface VxilHooks {
|
|
45
|
+
/** Runs before EVERY attempt (so a retry can carry a fresh trace id). May
|
|
46
|
+
* return extra headers to add for that attempt — a correlation id, a
|
|
47
|
+
* tracing header. The credential headers cannot be set from a hook (they
|
|
48
|
+
* are dropped); rotate an end-user session with `vx.asEndUser(token)`. */
|
|
49
|
+
beforeRequest?: (request: VxilRequestInfo) => void | Record<string, string> | Promise<void | Record<string, string>>;
|
|
50
|
+
/** Runs after every response that arrives (2xx or not, retried or final),
|
|
51
|
+
* before the retry decision. The body has already been read into the
|
|
52
|
+
* client — inspect `response.status` / `response.headers`, not the body. */
|
|
53
|
+
afterResponse?: (info: {
|
|
54
|
+
request: VxilRequestInfo;
|
|
55
|
+
response: Response;
|
|
56
|
+
durationMs: number;
|
|
57
|
+
}) => void | Promise<void>;
|
|
58
|
+
/** Runs right before the client sleeps for a retry. `status` is set for a
|
|
59
|
+
* retryable response, `error` for a network failure / timeout. */
|
|
60
|
+
onRetry?: (info: {
|
|
61
|
+
request: VxilRequestInfo;
|
|
62
|
+
delayMs: number;
|
|
63
|
+
status?: number;
|
|
64
|
+
error?: unknown;
|
|
65
|
+
}) => void | Promise<void>;
|
|
66
|
+
}
|
|
12
67
|
export interface VxilOptions {
|
|
68
|
+
/** The tenant API key (`vxil_live_…` / `vxil_test_…`).
|
|
69
|
+
*
|
|
70
|
+
* A key may optionally be minted with an **IP allowlist** (`allowed_cidrs`,
|
|
71
|
+
* set in the dashboard at mint time). When it is, a call from an address
|
|
72
|
+
* outside that list is refused at the edge with `403 ip_not_allowed` —
|
|
73
|
+
* before it reaches any feature — and the key is otherwise unchanged. Keys
|
|
74
|
+
* minted without one (the default) are not IP-restricted. */
|
|
13
75
|
apiKey: string;
|
|
14
76
|
/** defaults to the production edge */
|
|
15
77
|
baseUrl?: string;
|
|
@@ -22,17 +84,30 @@ export interface VxilOptions {
|
|
|
22
84
|
* a mobile/SPA holds a public/thin-client key + the signed-in user's session
|
|
23
85
|
* token and calls the edge directly, safely scoped to that user. Absent ⇒
|
|
24
86
|
* **no header** ⇒ server-caller mode, exactly as today (backward compatible).
|
|
25
|
-
* See docs/
|
|
87
|
+
* See https://vxil.com/docs/guide/09-security-and-multitenancy. For a per-call override on
|
|
26
88
|
* an otherwise server-mode client, use `vx.asEndUser(token)`. */
|
|
27
89
|
endUserToken?: string;
|
|
28
90
|
/** Global API major every request pins (default `'v1'`). Path-major, per
|
|
29
|
-
* docs/
|
|
91
|
+
* the versioning section of https://vxil.com/docs/guide/12-going-to-production — a breaking change ships as a new major on a
|
|
30
92
|
* new path, never in place. */
|
|
31
93
|
apiVersion?: ApiVersion;
|
|
32
94
|
/** Per-feature API-major overrides (`{ cms: 'v2' }`); each takes precedence
|
|
33
95
|
* over `apiVersion` for that namespace only. Optional; the compiled default
|
|
34
96
|
* is `'v1'` for every feature. */
|
|
35
97
|
apiVersions?: Partial<Record<FeatureKey, ApiVersion>>;
|
|
98
|
+
/** Retry policy — DEFAULT OFF (`attempts: 0`). Only idempotent requests are
|
|
99
|
+
* ever retried (GET/HEAD/PUT/DELETE, and a POST only when it carries an
|
|
100
|
+
* `Idempotency-Key`), so a retry can never double-apply a write. See
|
|
101
|
+
* `VxilRetryOptions` for the schedule and `Retry-After` handling. */
|
|
102
|
+
retry?: VxilRetryOptions;
|
|
103
|
+
/** Per-attempt timeout in milliseconds (default: none). On expiry the
|
|
104
|
+
* request — body read included — is aborted and a `VxilError`
|
|
105
|
+
* `{ status: 0, code: 'request_timeout' }` is thrown. A timeout counts as a
|
|
106
|
+
* network error for `retry`. */
|
|
107
|
+
timeoutMs?: number;
|
|
108
|
+
/** Request lifecycle hooks — correlation ids, logging, metrics, a retry
|
|
109
|
+
* audit. Nothing runs unless set. See `VxilHooks`. */
|
|
110
|
+
hooks?: VxilHooks;
|
|
36
111
|
}
|
|
37
112
|
/** Rewrite a built `/v1/<ns>/…` path onto the version configured for its
|
|
38
113
|
* namespace: the per-namespace override wins, else the global default. PURE and
|
|
@@ -44,13 +119,24 @@ export interface Meta {
|
|
|
44
119
|
request_id: string;
|
|
45
120
|
tenant_id?: string;
|
|
46
121
|
}
|
|
122
|
+
/** Every non-2xx answer is thrown as this class, carrying the structured error
|
|
123
|
+
* envelope. `status` is the HTTP status — or `0` when no response arrived
|
|
124
|
+
* (a `timeoutMs` timeout, `code: 'request_timeout'`). */
|
|
47
125
|
export declare class VxilError extends Error {
|
|
48
126
|
readonly status: number;
|
|
49
127
|
readonly code: string;
|
|
50
128
|
readonly hint?: string | undefined;
|
|
51
129
|
readonly fixUrl?: string | undefined;
|
|
52
130
|
readonly requestId?: string | undefined;
|
|
53
|
-
|
|
131
|
+
/** Seconds the server asked the caller to wait — parsed from `Retry-After`
|
|
132
|
+
* (delta-seconds or an HTTP-date) when the answer carried it (a rate
|
|
133
|
+
* limit, a busy upstream); otherwise undefined. */
|
|
134
|
+
readonly retryAfter?: number | undefined;
|
|
135
|
+
constructor(status: number, code: string, message: string, hint?: string | undefined, fixUrl?: string | undefined, requestId?: string | undefined,
|
|
136
|
+
/** Seconds the server asked the caller to wait — parsed from `Retry-After`
|
|
137
|
+
* (delta-seconds or an HTTP-date) when the answer carried it (a rate
|
|
138
|
+
* limit, a busy upstream); otherwise undefined. */
|
|
139
|
+
retryAfter?: number | undefined);
|
|
54
140
|
}
|
|
55
141
|
export interface VxilUser {
|
|
56
142
|
id: string;
|
|
@@ -78,6 +164,44 @@ export interface Delivery {
|
|
|
78
164
|
queued_at: string;
|
|
79
165
|
sent_at: string | null;
|
|
80
166
|
failed_at: string | null;
|
|
167
|
+
/** Engagement stamps from the signed provider webhook (first-wins). `null`
|
|
168
|
+
* means "no such event received", NOT "did not happen". */
|
|
169
|
+
delivered_at?: string | null;
|
|
170
|
+
opened_at?: string | null;
|
|
171
|
+
clicked_at?: string | null;
|
|
172
|
+
}
|
|
173
|
+
/** An ADDITIVE, non-fatal note on a 202 send — today only `mock_provider`
|
|
174
|
+
* (the send is recorded but no email leaves vxil). */
|
|
175
|
+
export interface NotificationWarning {
|
|
176
|
+
code: string;
|
|
177
|
+
message: string;
|
|
178
|
+
hint?: string;
|
|
179
|
+
}
|
|
180
|
+
export interface NotificationSendResult {
|
|
181
|
+
delivery_id?: string;
|
|
182
|
+
inbox_message_id?: string;
|
|
183
|
+
status: string;
|
|
184
|
+
/** present only for a scheduled send (`send_at` / `delay_seconds`) */
|
|
185
|
+
scheduled_for?: string;
|
|
186
|
+
warnings?: NotificationWarning[];
|
|
187
|
+
}
|
|
188
|
+
export interface Campaign {
|
|
189
|
+
campaign_id: string;
|
|
190
|
+
name: string;
|
|
191
|
+
channel: string;
|
|
192
|
+
template_id: string;
|
|
193
|
+
audience_ref: string;
|
|
194
|
+
schedule_cron: string | null;
|
|
195
|
+
status: 'draft' | 'scheduled' | 'running' | 'done' | 'paused' | 'cancelled';
|
|
196
|
+
created_at: string;
|
|
197
|
+
/** the jobs schedule row this campaign owns; null = no cron registered */
|
|
198
|
+
schedule_id?: string | null;
|
|
199
|
+
last_run_at?: string | null;
|
|
200
|
+
}
|
|
201
|
+
export interface CampaignState {
|
|
202
|
+
campaign_id: string;
|
|
203
|
+
status: string;
|
|
204
|
+
schedule_id: string | null;
|
|
81
205
|
}
|
|
82
206
|
export interface FeatureConfig {
|
|
83
207
|
feature: string;
|
|
@@ -230,8 +354,14 @@ export interface PaymentsWebhookEvent {
|
|
|
230
354
|
provider: string;
|
|
231
355
|
provider_evt_id: string;
|
|
232
356
|
event_type: string;
|
|
233
|
-
/** received | processed | error | sig_failed | parse_failed | reprocessed
|
|
357
|
+
/** received | processed | error | sig_failed | parse_failed | reprocessed |
|
|
358
|
+
* ignored (a well-formed provider type we deliberately do not fold) |
|
|
359
|
+
* unowned (no end user resolvable) | rejected_environment (a sandbox event
|
|
360
|
+
* the tenant did not opt into) — the last three are 200-acked, never folded. */
|
|
234
361
|
outcome: string | null;
|
|
362
|
+
/** the PROVIDER-reported environment of the delivery ('production' |
|
|
363
|
+
* 'sandbox') — a separate axis from the API key's live/test label. */
|
|
364
|
+
environment?: 'production' | 'sandbox';
|
|
235
365
|
signature_ok: boolean;
|
|
236
366
|
error: string | null;
|
|
237
367
|
received_at: string | null;
|
|
@@ -244,6 +374,70 @@ export interface PaymentsWebhookEvent {
|
|
|
244
374
|
raw_body?: string | null;
|
|
245
375
|
provider_event_ts?: string | null;
|
|
246
376
|
}
|
|
377
|
+
/** The outcome of a provider re-sync / Restore Purchases (payments.md §7d). */
|
|
378
|
+
export interface PaymentsSyncOutcome {
|
|
379
|
+
provider: string;
|
|
380
|
+
synced: number;
|
|
381
|
+
changed: number;
|
|
382
|
+
unchanged: number;
|
|
383
|
+
errors: string[];
|
|
384
|
+
changes: Array<{
|
|
385
|
+
provider_sub_id: string;
|
|
386
|
+
status: string;
|
|
387
|
+
tier: string | null;
|
|
388
|
+
until: string | null;
|
|
389
|
+
}>;
|
|
390
|
+
dry_run?: boolean;
|
|
391
|
+
}
|
|
392
|
+
/** A lifecycle simulation run (payments.md §7d; mock/dev tenants only). */
|
|
393
|
+
export interface PaymentsSimulationResult {
|
|
394
|
+
scenario: string;
|
|
395
|
+
run_id: string;
|
|
396
|
+
user_id: string;
|
|
397
|
+
tier: string;
|
|
398
|
+
tenant_kind: 'standard' | 'dev' | 'unknown';
|
|
399
|
+
note: string;
|
|
400
|
+
steps: Array<{
|
|
401
|
+
event_type: string;
|
|
402
|
+
status: number;
|
|
403
|
+
outcome: string;
|
|
404
|
+
}>;
|
|
405
|
+
oracle: {
|
|
406
|
+
expected: Record<string, 'premium' | 'free'>;
|
|
407
|
+
observed: Record<string, {
|
|
408
|
+
tier: string;
|
|
409
|
+
until: string | null;
|
|
410
|
+
}>;
|
|
411
|
+
pass: boolean;
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
/** The ONE complete entitlement read (GET /v1/payments/entitlements; payments.md
|
|
415
|
+
* §3a). `until` is the winning subscription's current_period_end (+ the
|
|
416
|
+
* configured grace window when it is past_due); null on the free baseline. */
|
|
417
|
+
export interface PaymentsEntitlementView {
|
|
418
|
+
user_id: string;
|
|
419
|
+
tier: string;
|
|
420
|
+
entitlements: string[];
|
|
421
|
+
quotas: Record<string, number>;
|
|
422
|
+
source_sub_id: string | null;
|
|
423
|
+
since: string | null;
|
|
424
|
+
until: string | null;
|
|
425
|
+
as_of: string;
|
|
426
|
+
environment: 'production' | 'sandbox';
|
|
427
|
+
/** present only when `?quota=` was asked for */
|
|
428
|
+
quota?: {
|
|
429
|
+
name: string;
|
|
430
|
+
value: number | null;
|
|
431
|
+
};
|
|
432
|
+
/** present only when `?credit_type=` was asked for */
|
|
433
|
+
credits?: {
|
|
434
|
+
credit_type: string;
|
|
435
|
+
balance: number;
|
|
436
|
+
held: number;
|
|
437
|
+
available: number;
|
|
438
|
+
period_end: string | null;
|
|
439
|
+
};
|
|
440
|
+
}
|
|
247
441
|
export interface FileObject {
|
|
248
442
|
object_id: string;
|
|
249
443
|
filename: string;
|
|
@@ -252,6 +446,26 @@ export interface FileObject {
|
|
|
252
446
|
status: 'pending' | 'available' | 'deleted';
|
|
253
447
|
created_at: string;
|
|
254
448
|
}
|
|
449
|
+
/** A live shared link as listed by GET /v1/files/{id}/shared-links (files.md
|
|
450
|
+
* §6). `downloads` is the burn-on-read counter; `max_downloads` null =
|
|
451
|
+
* unlimited (1 = a one-time link). A link that hit its cap, expired, or was
|
|
452
|
+
* revoked no longer lists. */
|
|
453
|
+
export interface FileSharedLink {
|
|
454
|
+
link_id: string;
|
|
455
|
+
created_at: string;
|
|
456
|
+
expires_at: string;
|
|
457
|
+
downloads: number;
|
|
458
|
+
max_downloads: number | null;
|
|
459
|
+
}
|
|
460
|
+
/** Options for POST /v1/files/{id}/shared-links. `expires_at` (ISO 8601) takes
|
|
461
|
+
* precedence over `ttl_seconds`; both are clamped to the tenant's
|
|
462
|
+
* `sharedLinks.maxTtl`. `max_downloads` ≥ 1 caps successful public
|
|
463
|
+
* resolutions (1 = one-time link); omit for unlimited. */
|
|
464
|
+
export interface CreateSharedLinkOptions {
|
|
465
|
+
ttl_seconds?: number;
|
|
466
|
+
expires_at?: string;
|
|
467
|
+
max_downloads?: number | null;
|
|
468
|
+
}
|
|
255
469
|
/** One recognized OCR text block (files.md §1.1). bbox is [x, y, w, h] in the
|
|
256
470
|
* provider's unit space; page is 1-based. confidence is 0..1 normalized per
|
|
257
471
|
* provider (Textract native 0..100 divided by 100; mock pins 0.95); absent
|
|
@@ -448,6 +662,57 @@ export interface AiEmbedResult {
|
|
|
448
662
|
usage: AiUsage;
|
|
449
663
|
generation_id: string;
|
|
450
664
|
}
|
|
665
|
+
/** The per-request knobs every verdict route shares (POST /v1/ai/classify and
|
|
666
|
+
* /v1/ai/judge): a stored template may author the whole prompt (rendered with
|
|
667
|
+
* the verdict vars — `{{input}}`, `{{labels}}`, `{{rubric}}`, `{{examples}}` /
|
|
668
|
+
* `{{candidate}}`, `{{candidates}}`, `{{criteria}}`, `{{scale_min}}`,
|
|
669
|
+
* `{{scale_max}}` — plus your own `vars`); provider/model override the tenant
|
|
670
|
+
* defaults; `max_tokens` defaults to min(config maxTokens, 512) and
|
|
671
|
+
* `temperature` to 0. Verdicts are sync-only (no stream / job mode). */
|
|
672
|
+
export interface AiVerdictOptions {
|
|
673
|
+
template?: string;
|
|
674
|
+
template_version?: number;
|
|
675
|
+
vars?: Record<string, unknown>;
|
|
676
|
+
provider?: string;
|
|
677
|
+
model?: string;
|
|
678
|
+
max_tokens?: number;
|
|
679
|
+
temperature?: number;
|
|
680
|
+
user_id?: string;
|
|
681
|
+
}
|
|
682
|
+
/** A classify verdict (POST /v1/ai/classify). `label` (single) or `labels`
|
|
683
|
+
* (multi:true — every applicable label, possibly empty) is guaranteed to be
|
|
684
|
+
* drawn from the request's `labels`: the allowed set is forced through the
|
|
685
|
+
* structured-output schema, so a 200 can never carry an off-list label. */
|
|
686
|
+
export interface AiClassifyResult {
|
|
687
|
+
generation_id: string;
|
|
688
|
+
label?: string;
|
|
689
|
+
labels?: string[];
|
|
690
|
+
/** 0..1 */
|
|
691
|
+
confidence: number;
|
|
692
|
+
rationale?: string;
|
|
693
|
+
usage: AiUsage;
|
|
694
|
+
/** true when the verdict only validated after the one repair pass. */
|
|
695
|
+
repaired?: boolean;
|
|
696
|
+
cached: boolean;
|
|
697
|
+
}
|
|
698
|
+
/** A judge verdict (POST /v1/ai/judge). One `candidate` → `score` (an integer
|
|
699
|
+
* inside `scale`, default 0..10) + `verdict: 'pass' | 'fail'`; several
|
|
700
|
+
* `candidates` → `scores` (one per candidate, in order) + `best` (the index of
|
|
701
|
+
* the top score) + `verdict: 'tie'` when the top score is shared. Sending
|
|
702
|
+
* `candidates` ALWAYS yields `scores` + `best` — a one-element list keeps the
|
|
703
|
+
* richer `score`/`pass|fail` fields too, so the shape never flips out from
|
|
704
|
+
* under a variable-length array. */
|
|
705
|
+
export interface AiJudgeResult {
|
|
706
|
+
generation_id: string;
|
|
707
|
+
score?: number;
|
|
708
|
+
scores?: number[];
|
|
709
|
+
verdict?: 'pass' | 'fail' | 'tie';
|
|
710
|
+
best?: number;
|
|
711
|
+
rationale?: string;
|
|
712
|
+
usage: AiUsage;
|
|
713
|
+
repaired?: boolean;
|
|
714
|
+
cached: boolean;
|
|
715
|
+
}
|
|
451
716
|
/** Per-user token rollups (GET /v1/ai/usage). */
|
|
452
717
|
export interface AiUsageReport {
|
|
453
718
|
input_tokens: number;
|
|
@@ -850,6 +1115,35 @@ type DisabledFeatures<S extends VxilSchemaShape> = {
|
|
|
850
1115
|
export type EnabledVxil<S extends VxilSchemaShape> = Omit<Vxil<S>, DisabledFeatures<S>> & {
|
|
851
1116
|
[P in DisabledFeatures<S>]: DisabledFeature<FeatureMap[P] & string>;
|
|
852
1117
|
};
|
|
1118
|
+
/** One config-declared per-record ACTION (cms.md §17): a button on a record row
|
|
1119
|
+
* that invokes the deployed tenant function `fn` ONCE with `{ collection,
|
|
1120
|
+
* item_id, action, actor, item }`. Exactly one human-initiated step — no
|
|
1121
|
+
* conditions, no chaining, no scheduling. ≤8 per collection. */
|
|
1122
|
+
export interface CmsActionDef {
|
|
1123
|
+
/** /^[a-z][a-z0-9_]{0,31}$/ — the URL key of `items.runAction` */
|
|
1124
|
+
key: string;
|
|
1125
|
+
/** the button label (1–64 chars) */
|
|
1126
|
+
label: string;
|
|
1127
|
+
/** the deployed function name (/^[a-z][a-z0-9-]{0,47}$/) */
|
|
1128
|
+
fn: string;
|
|
1129
|
+
}
|
|
1130
|
+
/** One live item referencing another through a relation field (cms.md §11.1). */
|
|
1131
|
+
export interface CmsBacklink {
|
|
1132
|
+
collection: string;
|
|
1133
|
+
field: string;
|
|
1134
|
+
item_id: string;
|
|
1135
|
+
status: string;
|
|
1136
|
+
}
|
|
1137
|
+
/** The bounded reverse read (`items.backlinks`): `count` = rows in THIS page
|
|
1138
|
+
* (≤ `limit`), never a total; `has_more` = the cap was hit. */
|
|
1139
|
+
export interface CmsBacklinksPage {
|
|
1140
|
+
collection: string;
|
|
1141
|
+
item_id: string;
|
|
1142
|
+
backlinks: CmsBacklink[];
|
|
1143
|
+
count: number;
|
|
1144
|
+
has_more: boolean;
|
|
1145
|
+
limit: number;
|
|
1146
|
+
}
|
|
853
1147
|
/** A write guard (cms.md §10): "after this write, at most `max` live items
|
|
854
1148
|
* match `filter`". Requires `lock` — an unlocked guard is racy by construction. */
|
|
855
1149
|
export interface CmsGuard {
|
|
@@ -1035,6 +1329,12 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1035
1329
|
/** The end-user session token threaded as `X-Vxil-End-User` when set (end-user
|
|
1036
1330
|
* mode). Undefined ⇒ no header ⇒ server-caller mode (unchanged). */
|
|
1037
1331
|
private readonly endUserToken;
|
|
1332
|
+
/** The ONE path every request takes: retry + timeout + hooks around
|
|
1333
|
+
* `fetchImpl`. With none of the three configured it is a single fetch. */
|
|
1334
|
+
private readonly transport;
|
|
1335
|
+
private readonly retryOpts;
|
|
1336
|
+
private readonly timeoutMs;
|
|
1337
|
+
private readonly hooks;
|
|
1038
1338
|
constructor(opts: VxilOptions);
|
|
1039
1339
|
/** The header bag every request layers on top of `authorization`: the
|
|
1040
1340
|
* `X-Vxil-End-User` session token in end-user mode, nothing in server mode.
|
|
@@ -1043,9 +1343,9 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1043
1343
|
/** Return a client that sends `X-Vxil-End-User: <token>` on every request —
|
|
1044
1344
|
* a per-call/scoped override of end-user mode over an otherwise server-mode
|
|
1045
1345
|
* client, mirroring how the edge threads the verified principal. The base
|
|
1046
|
-
* URL, api key, fetch impl
|
|
1047
|
-
* end-user token is (re)set. Pass a falsy token to get back a server-mode
|
|
1048
|
-
* client (drops the header). See docs/
|
|
1346
|
+
* URL, api key, fetch impl, version pins and the retry/timeout/hooks options
|
|
1347
|
+
* are inherited unchanged; only the end-user token is (re)set. Pass a falsy token to get back a server-mode
|
|
1348
|
+
* client (drops the header). See https://vxil.com/docs/guide/09-security-and-multitenancy. */
|
|
1049
1349
|
asEndUser(endUserToken: string | undefined): Vxil<S>;
|
|
1050
1350
|
/** Build the versioned request path — the ONE place a wire path is finalized.
|
|
1051
1351
|
* Every request (call(), audit.export, the fn proxy) routes through here, so
|
|
@@ -1117,15 +1417,72 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1117
1417
|
user_id: string;
|
|
1118
1418
|
template: "magic-link" | "welcome" | "transactional";
|
|
1119
1419
|
data: Record<string, unknown>;
|
|
1420
|
+
/** Explicit wins; otherwise the recipient's stored `locale` attribute
|
|
1421
|
+
* (set it via `users.upsert({ attributes: { locale } })`), then
|
|
1422
|
+
* `config.defaultLocale`. */
|
|
1120
1423
|
locale?: string;
|
|
1121
1424
|
/** 'inbox'/'both' require config inboxEnabled */
|
|
1122
1425
|
channel?: "email" | "inbox" | "both";
|
|
1426
|
+
/** hold the send until this ISO instant (≤ 30 d out) */
|
|
1427
|
+
send_at?: string;
|
|
1428
|
+
/** …or this many seconds from now (≤ 30 d). At most one of the two. */
|
|
1429
|
+
delay_seconds?: number;
|
|
1123
1430
|
}, opts?: {
|
|
1124
1431
|
idempotencyKey?: string;
|
|
1432
|
+
}) => Promise<NotificationSendResult>;
|
|
1433
|
+
/** Up to 100 sends in one call. Each item is INDEPENDENTLY atomic (recorded
|
|
1434
|
+
* + enqueued, or neither) and `results` is index-aligned with `items`. */
|
|
1435
|
+
sendBatch: (items: Array<{
|
|
1436
|
+
user_id: string;
|
|
1437
|
+
template: "magic-link" | "welcome" | "transactional";
|
|
1438
|
+
data: Record<string, unknown>;
|
|
1439
|
+
locale?: string;
|
|
1440
|
+
channel?: "email" | "inbox" | "both";
|
|
1441
|
+
send_at?: string;
|
|
1442
|
+
delay_seconds?: number;
|
|
1443
|
+
/** per-item 24 h idempotency key (the header equivalent for one item) */
|
|
1444
|
+
idempotency_key?: string;
|
|
1445
|
+
}>) => Promise<{
|
|
1446
|
+
results: Array<{
|
|
1447
|
+
index: number;
|
|
1448
|
+
delivery_id?: string;
|
|
1449
|
+
inbox_message_id?: string;
|
|
1450
|
+
status?: string;
|
|
1451
|
+
scheduled_for?: string;
|
|
1452
|
+
error?: {
|
|
1453
|
+
code: string;
|
|
1454
|
+
message: string;
|
|
1455
|
+
hint?: string;
|
|
1456
|
+
};
|
|
1457
|
+
}>;
|
|
1458
|
+
count: number;
|
|
1459
|
+
accepted: number;
|
|
1460
|
+
failed: number;
|
|
1461
|
+
warnings?: NotificationWarning[];
|
|
1462
|
+
}>;
|
|
1463
|
+
/** Render a template exactly as a send would, WITHOUT sending. `missing`
|
|
1464
|
+
* lists the required data keys you left out. Needs `notifications:read`. */
|
|
1465
|
+
preview: (templateId: string, input?: {
|
|
1466
|
+
data?: Record<string, unknown>;
|
|
1467
|
+
locale?: string;
|
|
1125
1468
|
}) => Promise<{
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1469
|
+
template_id: string;
|
|
1470
|
+
locale: string;
|
|
1471
|
+
overridden: boolean;
|
|
1472
|
+
subject: string;
|
|
1473
|
+
html: string;
|
|
1474
|
+
text: string;
|
|
1475
|
+
missing: string[];
|
|
1476
|
+
}>;
|
|
1477
|
+
/** Read-only deliverability probe: is `config.fromEmail`'s domain verified
|
|
1478
|
+
* at the provider? FAIL-SOFT — `verified: 'unknown'` whenever it cannot be
|
|
1479
|
+
* determined (mock provider, no key, provider unreachable). */
|
|
1480
|
+
senderDomain: () => Promise<{
|
|
1481
|
+
domain: string | null;
|
|
1482
|
+
verified: boolean | "unknown";
|
|
1483
|
+
provider: string;
|
|
1484
|
+
checked_at: string;
|
|
1485
|
+
reason?: string;
|
|
1129
1486
|
}>;
|
|
1130
1487
|
inbox: {
|
|
1131
1488
|
list: (q: {
|
|
@@ -1153,6 +1510,8 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1153
1510
|
user_id?: string;
|
|
1154
1511
|
status?: string;
|
|
1155
1512
|
limit?: number;
|
|
1513
|
+
/** A7: only deliveries that reached this engagement state */
|
|
1514
|
+
engagement?: "delivered" | "opened" | "clicked";
|
|
1156
1515
|
}) => Promise<Delivery[]>;
|
|
1157
1516
|
suppressions: {
|
|
1158
1517
|
list: () => Promise<Array<{
|
|
@@ -1194,17 +1553,36 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1194
1553
|
}) => Promise<{
|
|
1195
1554
|
campaign_id: string;
|
|
1196
1555
|
status: string;
|
|
1556
|
+
schedule_id: string | null;
|
|
1197
1557
|
}>;
|
|
1198
|
-
list: () => Promise<
|
|
1558
|
+
list: () => Promise<Campaign[]>;
|
|
1559
|
+
/** Stop a scheduled campaign from firing (the jobs cron is torn down). */
|
|
1560
|
+
pause: (campaignId: string) => Promise<CampaignState>;
|
|
1561
|
+
/** Re-register the cron (back to `scheduled`, or `draft` with no cron). */
|
|
1562
|
+
resume: (campaignId: string) => Promise<CampaignState>;
|
|
1563
|
+
/** Terminal stop — the cron is removed and the campaign cannot resume. */
|
|
1564
|
+
cancel: (campaignId: string) => Promise<CampaignState>;
|
|
1565
|
+
/** Soft-delete: hidden from the list, cron removed; the dedupe ledger
|
|
1566
|
+
* survives so a re-created campaign cannot re-send to a served user. */
|
|
1567
|
+
remove: (campaignId: string) => Promise<CampaignState & {
|
|
1568
|
+
deleted?: boolean;
|
|
1569
|
+
}>;
|
|
1570
|
+
/** Push ONE audience page (≤ 1000 rows) into a campaign run — the shape a
|
|
1571
|
+
* tenant audience callback uses. Omit `audience` for a campaign whose
|
|
1572
|
+
* `audience_ref` is the built-in `users:all` selector. */
|
|
1573
|
+
run: (campaignId: string, audience?: Array<{
|
|
1574
|
+
end_user_id: string;
|
|
1575
|
+
locale?: string | null;
|
|
1576
|
+
}>) => Promise<{
|
|
1199
1577
|
campaign_id: string;
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
}
|
|
1578
|
+
audience_source: "supplied" | "builtin" | "external";
|
|
1579
|
+
audience_size: number;
|
|
1580
|
+
sent: number;
|
|
1581
|
+
deferred: number;
|
|
1582
|
+
skipped_freq_cap: number;
|
|
1583
|
+
skipped_no_email: number;
|
|
1584
|
+
already_sent: number;
|
|
1585
|
+
}>;
|
|
1208
1586
|
};
|
|
1209
1587
|
};
|
|
1210
1588
|
readonly config: {
|
|
@@ -1242,7 +1620,7 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1242
1620
|
* ADVISORY vxil plan — which features to enable, a materialized `config_draft`,
|
|
1243
1621
|
* and which truly-unique logic needs a tenant function. Deterministic + pure;
|
|
1244
1622
|
* it PROPOSES a plan, it never applies anything (review it, then push the
|
|
1245
|
-
* config). Needs `features:read`. (POST /v1/plan; docs/
|
|
1623
|
+
* config). Needs `features:read`. (POST /v1/plan; https://vxil.com/docs/guide/10-agents-and-mcp) */
|
|
1246
1624
|
readonly planner: {
|
|
1247
1625
|
create: (description: string, name?: string) => Promise<VxilPlan>;
|
|
1248
1626
|
};
|
|
@@ -1454,6 +1832,16 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1454
1832
|
delete: (ruleId: string) => Promise<void>;
|
|
1455
1833
|
};
|
|
1456
1834
|
};
|
|
1835
|
+
/** vxil-auth. SCOPES (vxil.com/docs/guide/09-security-and-multitenancy): every flow here that
|
|
1836
|
+
* obtains, renews, verifies or ends the caller's OWN session (signUp/signIn,
|
|
1837
|
+
* magicLink, otp, anonymous, stepUp, oauth, sessions.refresh/revoke/verify,
|
|
1838
|
+
* password reset) accepts the narrow `auth:signin` — the ONLY auth scope a
|
|
1839
|
+
* key baked into a browser/mobile bundle should carry. `sessions.list` needs
|
|
1840
|
+
* `auth:read`; `sessions.revokeById` and `users.erase` need `auth:write`
|
|
1841
|
+
* (administrative — server keys only). In end-user mode (`endUserToken` /
|
|
1842
|
+
* `asEndUser`) the administrative calls bind to the signed-in user: own
|
|
1843
|
+
* sessions only, own devices only, erase self only. `auth:write` still
|
|
1844
|
+
* satisfies every call. */
|
|
1457
1845
|
readonly auth: {
|
|
1458
1846
|
signUp: (input: {
|
|
1459
1847
|
email: string;
|
|
@@ -1486,9 +1874,18 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1486
1874
|
* Server-side guessing budget (config otp.maxAttempts), single active code,
|
|
1487
1875
|
* resend cooldown (429 otp_rate_limited). */
|
|
1488
1876
|
otp: {
|
|
1877
|
+
/** `test_code` is present ONLY when the address matches auth config
|
|
1878
|
+
* `otp.testRecipients` (store-review / CI accounts): no mail is sent and
|
|
1879
|
+
* the code comes back instead. `captcha_token` is required when the
|
|
1880
|
+
* tenant configured `security.captchaSecretRef`. */
|
|
1489
1881
|
request: (input: {
|
|
1490
1882
|
email: string;
|
|
1491
|
-
|
|
1883
|
+
locale?: string;
|
|
1884
|
+
captcha_token?: string;
|
|
1885
|
+
}) => Promise<{
|
|
1886
|
+
sent: true;
|
|
1887
|
+
test_code?: string;
|
|
1888
|
+
}>;
|
|
1492
1889
|
verify: (input: {
|
|
1493
1890
|
email: string;
|
|
1494
1891
|
code: string;
|
|
@@ -1510,7 +1907,16 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1510
1907
|
request: (input: {
|
|
1511
1908
|
token: string;
|
|
1512
1909
|
email: string;
|
|
1513
|
-
|
|
1910
|
+
locale?: string;
|
|
1911
|
+
}) => Promise<{
|
|
1912
|
+
sent: true;
|
|
1913
|
+
test_code?: string;
|
|
1914
|
+
}>;
|
|
1915
|
+
/** On success the guest keeps its user id (`user_id` unchanged).
|
|
1916
|
+
* If the claimed email ALREADY has an account, the guest is MERGED
|
|
1917
|
+
* into it: `user_id` is the existing account, `merged: true`, and a
|
|
1918
|
+
* fresh `session.token` (same session, re-signed for the merged
|
|
1919
|
+
* identity — swap it client-side; other guest sessions are revoked). */
|
|
1514
1920
|
verify: (input: {
|
|
1515
1921
|
token: string;
|
|
1516
1922
|
email: string;
|
|
@@ -1519,6 +1925,11 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1519
1925
|
user_id: string;
|
|
1520
1926
|
email: string;
|
|
1521
1927
|
verified: boolean;
|
|
1928
|
+
merged?: true;
|
|
1929
|
+
session?: {
|
|
1930
|
+
token: string;
|
|
1931
|
+
expires_at: string;
|
|
1932
|
+
};
|
|
1522
1933
|
}>;
|
|
1523
1934
|
};
|
|
1524
1935
|
};
|
|
@@ -1528,7 +1939,11 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1528
1939
|
stepUp: {
|
|
1529
1940
|
request: (input: {
|
|
1530
1941
|
token: string;
|
|
1531
|
-
|
|
1942
|
+
locale?: string;
|
|
1943
|
+
}) => Promise<{
|
|
1944
|
+
sent: true;
|
|
1945
|
+
test_code?: string;
|
|
1946
|
+
}>;
|
|
1532
1947
|
verify: (input: {
|
|
1533
1948
|
token: string;
|
|
1534
1949
|
code: string;
|
|
@@ -1546,11 +1961,22 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1546
1961
|
* (email → tombstone, password/name/avatar cleared) and revoke every
|
|
1547
1962
|
* live session. Idempotent — a second call on an already-erased user is a
|
|
1548
1963
|
* no-op. Meant to be called from a "delete my account" server route or
|
|
1549
|
-
* vxil function (which declares `auth:write`).
|
|
1964
|
+
* vxil function (which declares `auth:write`). In END-USER mode the call
|
|
1965
|
+
* is SELF-only: `userId` must be the signed-in user, any other id throws
|
|
1966
|
+
* 403 `server_only` (a thin client can never erase someone else). */
|
|
1550
1967
|
erase: (userId: string) => Promise<{
|
|
1551
1968
|
user_id: string;
|
|
1552
1969
|
erased: boolean;
|
|
1553
1970
|
}>;
|
|
1971
|
+
/** END-USER MODE ONLY (`endUserToken` / `asEndUser`): the signed-in user
|
|
1972
|
+
* deep-merges a bounded `attributes` object into its OWN shared identity
|
|
1973
|
+
* row (null deletes a key; ≤8 KB, ≤64 top-level keys, depth ≤8). Profile
|
|
1974
|
+
* fields are not patchable here. In server mode the call throws 422
|
|
1975
|
+
* `end_user_mode_required` — use `users.patch(id, …)` instead. */
|
|
1976
|
+
patchMe: (attributes: Record<string, unknown>) => Promise<{
|
|
1977
|
+
user_id: string;
|
|
1978
|
+
attributes: Record<string, unknown>;
|
|
1979
|
+
}>;
|
|
1554
1980
|
};
|
|
1555
1981
|
sessions: {
|
|
1556
1982
|
verify: (token: string) => Promise<{
|
|
@@ -1568,11 +1994,26 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1568
1994
|
session: AuthSession;
|
|
1569
1995
|
}>;
|
|
1570
1996
|
revoke: (token: string) => Promise<void>;
|
|
1997
|
+
/** "Sign out everywhere": revoke EVERY live session of a user in one call
|
|
1998
|
+
* (one statement, one batched edge-cache write; each session's
|
|
1999
|
+
* revoked_reason = 'revoke_all'). SERVER mode names the user (`userId`,
|
|
2000
|
+
* scope `auth:write`); in END-USER mode the signed-in user's own sessions
|
|
2001
|
+
* go and `userId` is ignored (scope `auth:signin` suffices). Idempotent:
|
|
2002
|
+
* a user with no live sessions returns `revoked: 0`. */
|
|
2003
|
+
revokeAll: (userId?: string) => Promise<{
|
|
2004
|
+
user_id: string;
|
|
2005
|
+
revoked: number;
|
|
2006
|
+
session_ids: string[];
|
|
2007
|
+
}>;
|
|
1571
2008
|
/** Force-revoke a SPECIFIC session by its `session_id` (from `list`) — the
|
|
1572
2009
|
* server-forced device-lockout path beyond the client-cooperative
|
|
1573
|
-
* by-token `revoke`. Throws 404 when the id is unknown or already revoked
|
|
2010
|
+
* by-token `revoke`. Throws 404 when the id is unknown or already revoked
|
|
2011
|
+
* — or, in end-user mode, when it belongs to another user (a signed-in
|
|
2012
|
+
* user can kick only their OWN devices). Scope `auth:write`. */
|
|
1574
2013
|
revokeById: (sessionId: string) => Promise<void>;
|
|
1575
|
-
/** List a user's active sessions (the account "signed-in devices" surface).
|
|
2014
|
+
/** List a user's active sessions (the account "signed-in devices" surface).
|
|
2015
|
+
* Scope `auth:read`. In end-user mode the list is bound to the signed-in
|
|
2016
|
+
* user regardless of `userId` (the verified principal wins). */
|
|
1576
2017
|
list: (userId: string) => Promise<Array<{
|
|
1577
2018
|
session_id: string;
|
|
1578
2019
|
created_at: string;
|
|
@@ -1591,21 +2032,48 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1591
2032
|
password: string;
|
|
1592
2033
|
}) => Promise<void>;
|
|
1593
2034
|
};
|
|
2035
|
+
/** Email verification (the flow behind auth config `emailVerification.required`,
|
|
2036
|
+
* which makes password sign-in answer 403 `email_not_verified` until the
|
|
2037
|
+
* address is confirmed). `request` always resolves (anti-enumeration —
|
|
2038
|
+
* a link is mailed only to an existing, unverified user; `redirect_url`
|
|
2039
|
+
* is your page that reads `?token=` and calls `confirm`). `confirm` is
|
|
2040
|
+
* single-use and expires after 24h. */
|
|
2041
|
+
emailVerification: {
|
|
2042
|
+
request: (input: {
|
|
2043
|
+
email: string;
|
|
2044
|
+
redirect_url: string;
|
|
2045
|
+
locale?: string;
|
|
2046
|
+
captcha_token?: string;
|
|
2047
|
+
}) => Promise<void>;
|
|
2048
|
+
confirm: (token: string) => Promise<{
|
|
2049
|
+
user_id: string;
|
|
2050
|
+
verified: true;
|
|
2051
|
+
}>;
|
|
2052
|
+
};
|
|
1594
2053
|
/** Social sign-in. The web `start`/`callback` flows are browser redirects
|
|
1595
|
-
* (not JSON calls)
|
|
2054
|
+
* (not JSON calls): send the browser to
|
|
2055
|
+
* `GET /v1/auth/oauth/{provider}/start?redirect_uri=…` (server-side, with
|
|
2056
|
+
* your key) and hand the returned `code`+`state` to `…/callback`; `native`
|
|
2057
|
+
* is the mobile / broker token-exchange the SDK wraps. `oidc` is the
|
|
2058
|
+
* tenant's generic OIDC / SSO issuer (auth config `providers.oidc` —
|
|
2059
|
+
* Okta / Entra / Auth0 / any OpenID Connect IdP, or a SAML broker that
|
|
2060
|
+
* speaks OIDC); it rides the same three routes. */
|
|
1596
2061
|
oauth: {
|
|
1597
2062
|
/** Native social sign-in: exchange a provider `id_token`/`access_token`
|
|
1598
2063
|
* for a vxil session (`linked` marks whether the user was created or
|
|
1599
2064
|
* matched to an existing identity). */
|
|
1600
|
-
native: (provider: "google" | "apple" | "github" | "facebook" | "mock", input: {
|
|
2065
|
+
native: (provider: "google" | "apple" | "github" | "facebook" | "mock" | "oidc", input: {
|
|
1601
2066
|
id_token?: string;
|
|
1602
2067
|
access_token?: string;
|
|
1603
2068
|
nonce?: string;
|
|
2069
|
+
anonymous_token?: string;
|
|
1604
2070
|
}) => Promise<{
|
|
1605
2071
|
user_id: string;
|
|
1606
2072
|
session: AuthSession;
|
|
1607
2073
|
verified: boolean;
|
|
1608
|
-
linked: "created" | "existing";
|
|
2074
|
+
linked: "created" | "existing" | "promoted" | "merged";
|
|
2075
|
+
/** sessions taken over by auth config session.maxConcurrent (present once opted in) */
|
|
2076
|
+
took_over?: string[];
|
|
1609
2077
|
}>;
|
|
1610
2078
|
};
|
|
1611
2079
|
};
|
|
@@ -1613,8 +2081,10 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1613
2081
|
createPolicy: (input: {
|
|
1614
2082
|
name: string;
|
|
1615
2083
|
key_template: string;
|
|
1616
|
-
|
|
1617
|
-
|
|
2084
|
+
/** omitted ⇒ seeded from the tenant's rate-limits config `defaults` (F8-54) */
|
|
2085
|
+
limit?: number;
|
|
2086
|
+
/** omitted ⇒ seeded from the tenant's rate-limits config `defaults` (F8-54) */
|
|
2087
|
+
window_seconds?: number;
|
|
1618
2088
|
behavior?: "block" | "shape";
|
|
1619
2089
|
}) => Promise<RateLimitPolicy>;
|
|
1620
2090
|
listPolicies: () => Promise<RateLimitPolicy[]>;
|
|
@@ -1704,7 +2174,7 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1704
2174
|
create: (input: {
|
|
1705
2175
|
collection: string;
|
|
1706
2176
|
singular?: string;
|
|
1707
|
-
/** End-user owner-scoping (docs/
|
|
2177
|
+
/** End-user owner-scoping (https://vxil.com/docs/guide/09-security-and-multitenancy):
|
|
1708
2178
|
* names an existing `string` field that holds the owner id. When set,
|
|
1709
2179
|
* the cms worker auto-scopes owned reads/writes to the VERIFIED
|
|
1710
2180
|
* end-user principal (default-deny) in end-user mode; a no-op in
|
|
@@ -1732,14 +2202,31 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1732
2202
|
* scalar-valued types only. Concurrent duplicates → 409 unique_violation. */
|
|
1733
2203
|
unique?: boolean;
|
|
1734
2204
|
/** relation fields only: what a delete of the referenced item does to
|
|
1735
|
-
* this one (bounded fan-out, cms.md §11)
|
|
1736
|
-
|
|
2205
|
+
* this one — cascade / set_null (bounded fan-out, cms.md §11) or
|
|
2206
|
+
* `restrict` (§11.1: the delete is refused with 409 `referenced`
|
|
2207
|
+
* while a live reference exists). */
|
|
2208
|
+
on_delete?: "cascade" | "set_null" | "restrict";
|
|
2209
|
+
/** FIELD-LEVEL read security (cms.md §18): the end-user ORG ROLE slugs
|
|
2210
|
+
* allowed to READ this field. Omitted/`[]` = ungated. A non-empty list
|
|
2211
|
+
* is FAIL-SAFE — in verified end-user mode the field is OMITTED from
|
|
2212
|
+
* every read (get / list / query / `$expand` / the write-response echo)
|
|
2213
|
+
* unless the session's verified roles intersect it, it is UNQUERYABLE
|
|
2214
|
+
* (filter/sort/group-by on it is a 422), and it is NEVER served on the
|
|
2215
|
+
* anonymous public lane. A SERVER caller (an API key with no end-user
|
|
2216
|
+
* session) still sees every field. Writes are unaffected. ≤16 entries,
|
|
2217
|
+
* each `^[a-z0-9][a-z0-9_-]{0,31}$` (the `orgs` role alphabet). */
|
|
2218
|
+
read_roles?: string[];
|
|
1737
2219
|
}>;
|
|
2220
|
+
/** Per-record action buttons (cms.md §17): `[{ key, label, fn }]` —
|
|
2221
|
+
* exactly ONE human-initiated step each; `fn` names a deployed tenant
|
|
2222
|
+
* function invoked by `items.runAction`. ≤8 per collection. */
|
|
2223
|
+
actions?: CmsActionDef[];
|
|
1738
2224
|
}) => Promise<{
|
|
1739
2225
|
collection: string;
|
|
1740
2226
|
fields: number;
|
|
1741
2227
|
owner_field?: string;
|
|
1742
2228
|
public?: boolean;
|
|
2229
|
+
actions?: CmsActionDef[];
|
|
1743
2230
|
}>;
|
|
1744
2231
|
list: () => Promise<Array<{
|
|
1745
2232
|
collection: string;
|
|
@@ -1748,6 +2235,14 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1748
2235
|
owner_field?: string | null;
|
|
1749
2236
|
}>>;
|
|
1750
2237
|
addField: (collection: string, field: Record<string, unknown>) => Promise<void>;
|
|
2238
|
+
/** Set (or clear, with `null`/`[]`) a field's READ-ROLE gate (cms.md §18) —
|
|
2239
|
+
* the same-type in-place alter on the fields route. Re-sends the field's
|
|
2240
|
+
* `type` (required by the alter path); every OTHER attribute the field
|
|
2241
|
+
* carries is re-sent from `rest`, because the alter overwrites the whole
|
|
2242
|
+
* definition. In verified end-user mode a gated field is omitted from every
|
|
2243
|
+
* read unless the session's verified roles intersect `roles`; server-caller
|
|
2244
|
+
* reads and ALL writes are unaffected. */
|
|
2245
|
+
setFieldReadRoles: (collection: string, field: string, type: "string" | "text" | "int" | "float" | "bool" | "datetime" | "json" | "relation" | "file", roles: string[] | null, rest?: Record<string, unknown>) => Promise<void>;
|
|
1751
2246
|
/** Set (or clear, with `null`) the collection's end-user owner-scope flag
|
|
1752
2247
|
* (design §5.1). Names an existing `string` field that holds the owner id. */
|
|
1753
2248
|
setOwnerField: (collection: string, ownerField: string | null) => Promise<void>;
|
|
@@ -1757,6 +2252,11 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1757
2252
|
* the owner_field are never exposed. `false` closes the lane (and purges the
|
|
1758
2253
|
* edge cache). Read the public side with `cmsPublicUrl` / `listCmsPublic`. */
|
|
1759
2254
|
setPublic: (collection: string, isPublic: boolean) => Promise<void>;
|
|
2255
|
+
/** Replace the collection's per-record ACTION list (cms.md §17) — the
|
|
2256
|
+
* `{ actions }` fields-route meta-op; `[]` clears. Each action is ONE
|
|
2257
|
+
* human-initiated step: the dashboard renders it as a button per record,
|
|
2258
|
+
* and `items.runAction` invokes its deployed function. */
|
|
2259
|
+
setActions: (collection: string, actions: CmsActionDef[]) => Promise<void>;
|
|
1760
2260
|
};
|
|
1761
2261
|
items: {
|
|
1762
2262
|
/** `lock` serializes same-key writers (per-tenant advisory lock); `guard`
|
|
@@ -1814,6 +2314,27 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1814
2314
|
set_null: number;
|
|
1815
2315
|
}>;
|
|
1816
2316
|
publish: (collection: string, itemId: string) => Promise<Record<string, unknown>>;
|
|
2317
|
+
/** P1-12 (cms.md §11.1): the BOUNDED reverse read — which live items
|
|
2318
|
+
* reference this one, through which relation field. Owner-scoped in
|
|
2319
|
+
* end-user mode like `get`. `count` is the page returned (≤ limit, max
|
|
2320
|
+
* 100), never a total; `has_more` says the cap was hit. A DELETE refused
|
|
2321
|
+
* with 409 `referenced` (an `on_delete: 'restrict'` edge) names the same
|
|
2322
|
+
* refs in its `referencing` field. */
|
|
2323
|
+
backlinks: (collection: string, itemId: string, opts?: {
|
|
2324
|
+
limit?: number;
|
|
2325
|
+
}) => Promise<CmsBacklinksPage>;
|
|
2326
|
+
/** P1-7 (cms.md §17): run ONE declared per-record action — invokes the
|
|
2327
|
+
* action's deployed function with `{ collection, item_id, action, actor,
|
|
2328
|
+
* item }` and returns its result. 404 when the key is not declared;
|
|
2329
|
+
* 502 `action_failed` (with `upstream.code` = the function's error
|
|
2330
|
+
* class) when the function fails. Requires cms:write. */
|
|
2331
|
+
runAction: (collection: string, itemId: string, key: string) => Promise<{
|
|
2332
|
+
collection: string;
|
|
2333
|
+
item_id: string;
|
|
2334
|
+
action: string;
|
|
2335
|
+
fn: string;
|
|
2336
|
+
result: unknown;
|
|
2337
|
+
}>;
|
|
1817
2338
|
/** cms-rel B2 (cms.md §12): bounded group-by aggregate. fns count|sum|
|
|
1818
2339
|
* min|max|avg over index-slot-bound fields; groupBy ≤2 slot-bound
|
|
1819
2340
|
* fields; filter = the full query DSL incl. ONE-hop dotted join terms
|
|
@@ -1953,7 +2474,7 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
1953
2474
|
* only the envelope: membership, read-cursors, mute, and a directed block
|
|
1954
2475
|
* list; the messages themselves are `comments` rows on the
|
|
1955
2476
|
* `dm:<conversation_id>` topic. Gated by its own `dm` config, independent of
|
|
1956
|
-
* comments. See docs/
|
|
2477
|
+
* comments. See vxil.com/docs/guide/06-feature-catalog.
|
|
1957
2478
|
*/
|
|
1958
2479
|
readonly dm: {
|
|
1959
2480
|
/** Read the resolved DM config (defaults merged with the stored partial). */
|
|
@@ -2375,6 +2896,24 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
2375
2896
|
}>;
|
|
2376
2897
|
}>;
|
|
2377
2898
|
};
|
|
2899
|
+
/** Every audit event the platform can emit, with the subscribable prefixes
|
|
2900
|
+
* and their counts — the discovery surface for `subscribe({ event_prefixes })`.
|
|
2901
|
+
* Static reference data (CI-generated from the emitters), identical for every
|
|
2902
|
+
* tenant. `level` is the event CLASS ('lifecycle' | 'failure'), not the
|
|
2903
|
+
* payload's 'info'|'warn'|'error' severity key. */
|
|
2904
|
+
eventCatalog: () => Promise<{
|
|
2905
|
+
count: number;
|
|
2906
|
+
events: Array<{
|
|
2907
|
+
name: string;
|
|
2908
|
+
feature: string;
|
|
2909
|
+
level: "lifecycle" | "failure";
|
|
2910
|
+
payload_keys?: string[];
|
|
2911
|
+
}>;
|
|
2912
|
+
prefixes: Array<{
|
|
2913
|
+
prefix: string;
|
|
2914
|
+
count: number;
|
|
2915
|
+
}>;
|
|
2916
|
+
}>;
|
|
2378
2917
|
};
|
|
2379
2918
|
readonly orgs: {
|
|
2380
2919
|
create: (input: {
|
|
@@ -2657,20 +3196,20 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
2657
3196
|
expires_at: string | null;
|
|
2658
3197
|
}>;
|
|
2659
3198
|
sharedLinks: {
|
|
2660
|
-
/** Public (unauthenticated) URL for the object; revocable.
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
3199
|
+
/** Public (unauthenticated) URL for the object; revocable. Optional
|
|
3200
|
+
* `max_downloads` (1 = one-time link) and an absolute `expires_at`; the
|
|
3201
|
+
* public resolver answers 410 `link_exhausted` / `link_expired` past
|
|
3202
|
+
* either bound. */
|
|
3203
|
+
create: (objectId: string, opts?: CreateSharedLinkOptions) => Promise<{
|
|
2664
3204
|
link_id: string;
|
|
2665
3205
|
url: string;
|
|
2666
|
-
|
|
3206
|
+
expires_in: number;
|
|
3207
|
+
expires_at: string;
|
|
3208
|
+
max_downloads: number | null;
|
|
3209
|
+
downloads: number;
|
|
2667
3210
|
}>;
|
|
2668
|
-
/** List an object's active (unrevoked, unexpired) shared links. */
|
|
2669
|
-
list: (objectId: string) => Promise<
|
|
2670
|
-
link_id: string;
|
|
2671
|
-
created_at: string;
|
|
2672
|
-
expires_at: string | null;
|
|
2673
|
-
}>>;
|
|
3211
|
+
/** List an object's active (unrevoked, unexpired, unexhausted) shared links. */
|
|
3212
|
+
list: (objectId: string) => Promise<FileSharedLink[]>;
|
|
2674
3213
|
revoke: (linkId: string) => Promise<void>;
|
|
2675
3214
|
};
|
|
2676
3215
|
};
|
|
@@ -2896,6 +3435,47 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
2896
3435
|
done: boolean;
|
|
2897
3436
|
max_seq: number;
|
|
2898
3437
|
}>;
|
|
3438
|
+
/** Classify an input into one of YOUR labels (or, with `multi:true`, every
|
|
3439
|
+
* label that applies) — a schema-forced verdict over the generate path:
|
|
3440
|
+
* the allowed labels ride the structured-output schema as an enum, so the
|
|
3441
|
+
* model cannot answer off-list; the reply is `{ label | labels,
|
|
3442
|
+
* confidence, rationale }`. Same metering/cache/provider rules as
|
|
3443
|
+
* `generate` (a still-invalid verdict is a billed 422
|
|
3444
|
+
* output_schema_mismatch). Caps: 2–64 labels (≤64 chars, unique), input
|
|
3445
|
+
* ≤32k chars (a string or ordered `{ text }` parts joined into ONE
|
|
3446
|
+
* input), rubric ≤4k, ≤16 examples. */
|
|
3447
|
+
classify: (input: AiVerdictOptions & {
|
|
3448
|
+
input: string | Array<{
|
|
3449
|
+
text: string;
|
|
3450
|
+
}>;
|
|
3451
|
+
labels: string[];
|
|
3452
|
+
multi?: boolean;
|
|
3453
|
+
rubric?: string;
|
|
3454
|
+
examples?: Array<{
|
|
3455
|
+
input: string;
|
|
3456
|
+
label: string;
|
|
3457
|
+
}>;
|
|
3458
|
+
}) => Promise<AiClassifyResult>;
|
|
3459
|
+
/** Grade a candidate response (or rank up to 8) against YOUR criteria —
|
|
3460
|
+
* a schema-forced integer score inside `scale` (default 0..10) plus
|
|
3461
|
+
* `pass|fail` for one candidate, or `scores[]` + `best` (+ `verdict:'tie'`)
|
|
3462
|
+
* for several — `candidates` always yields `scores`/`best`, even at
|
|
3463
|
+
* length 1. Same metering/cache/provider rules as `generate`. Caps:
|
|
3464
|
+
* input ≤16k chars, candidates ≤8 × 4k chars, criteria ≤16 items / 4k
|
|
3465
|
+
* chars, scale span ≤100. */
|
|
3466
|
+
judge: (input: AiVerdictOptions & {
|
|
3467
|
+
input: string;
|
|
3468
|
+
candidate?: string;
|
|
3469
|
+
candidates?: string[];
|
|
3470
|
+
criteria: string | Array<{
|
|
3471
|
+
name: string;
|
|
3472
|
+
weight?: number;
|
|
3473
|
+
}>;
|
|
3474
|
+
scale?: {
|
|
3475
|
+
min: number;
|
|
3476
|
+
max: number;
|
|
3477
|
+
};
|
|
3478
|
+
}) => Promise<AiJudgeResult>;
|
|
2899
3479
|
/** Embed a batch of strings (1–256). */
|
|
2900
3480
|
embed: (input: {
|
|
2901
3481
|
input: string[];
|
|
@@ -3045,27 +3625,43 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
3045
3625
|
*/
|
|
3046
3626
|
readonly payments: {
|
|
3047
3627
|
/** Provider + ledger capability surface (+ the `missing` set an agent can act
|
|
3048
|
-
* on to recommend a provider).
|
|
3628
|
+
* on to recommend a provider). `client` is the RevenueCat tenant's PUBLIC
|
|
3629
|
+
* SDK handout (project id + public SDK key — never the secret key); null
|
|
3630
|
+
* for every other provider. */
|
|
3049
3631
|
capabilities: () => Promise<{
|
|
3050
3632
|
provider: string;
|
|
3051
3633
|
capabilities: string[];
|
|
3052
3634
|
ledger_capabilities: string[];
|
|
3053
3635
|
missing: string[];
|
|
3636
|
+
client: {
|
|
3637
|
+
revenuecat: {
|
|
3638
|
+
project_id: string;
|
|
3639
|
+
public_sdk_key: string;
|
|
3640
|
+
};
|
|
3641
|
+
} | null;
|
|
3054
3642
|
}>;
|
|
3055
3643
|
/** The user's effective entitlement snapshot (the multi-sub tier fold):
|
|
3056
|
-
* tier + boolean entitlements + numeric quotas
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3644
|
+
* tier + boolean entitlements + numeric quotas — plus the access window
|
|
3645
|
+
* (`since` / `until` = the winning subscription's period end, or end +
|
|
3646
|
+
* grace when it is past_due), `as_of` (server time), the `environment`
|
|
3647
|
+
* the winning subscription was written from, and OPTIONAL inline reads:
|
|
3648
|
+
* `quota` inlines one quota, `creditType` inlines the same owner-bound
|
|
3649
|
+
* balance `getBalance` returns — ONE call for a thin client's paywall.
|
|
3650
|
+
* A non-2xx answer means UNKNOWN: render the last cached answer, never
|
|
3651
|
+
* free (payments.md §3a). */
|
|
3652
|
+
getEntitlements: (userId: string, opts?: {
|
|
3653
|
+
quota?: string;
|
|
3654
|
+
creditType?: string;
|
|
3655
|
+
}) => Promise<PaymentsEntitlementView>;
|
|
3063
3656
|
/** Boolean gate: does the user hold `entitlement`? Returns granted + its
|
|
3064
|
-
* source (subscription/tier) + the resolved tier
|
|
3657
|
+
* source (subscription/tier) + the resolved tier, plus `until` (how long
|
|
3658
|
+
* the answer is good for) and `as_of`. Non-2xx = unknown, never free. */
|
|
3065
3659
|
hasEntitlement: (userId: string, entitlement: string) => Promise<{
|
|
3066
3660
|
granted: boolean;
|
|
3067
3661
|
source: string | null;
|
|
3068
3662
|
tier: string;
|
|
3663
|
+
until: string | null;
|
|
3664
|
+
as_of: string;
|
|
3069
3665
|
}>;
|
|
3070
3666
|
/** A single numeric quota for the user (e.g. `seats`, `api_calls`); null when
|
|
3071
3667
|
* the resolved tier declares no such quota. Convenience over getEntitlements. */
|
|
@@ -3216,10 +3812,107 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
3216
3812
|
status: "pending" | "succeeded" | "failed";
|
|
3217
3813
|
amount_cents: number;
|
|
3218
3814
|
}>;
|
|
3219
|
-
/**
|
|
3220
|
-
*
|
|
3815
|
+
/** Where the user MANAGES their subscription, resolved by FUNDING SOURCE
|
|
3816
|
+
* (the provider of their live subscription, not the tenant's primary):
|
|
3817
|
+
* `kind: 'portal'` = a provider-minted portal session URL (Stripe, Paddle
|
|
3818
|
+
* via the captured customer id, mock); `'store'` = the App Store / Play
|
|
3819
|
+
* Store manage-subscriptions page for a RevenueCat row that recorded its
|
|
3820
|
+
* store; `'none'` (url null) = nothing to link (PayPal, a manual grant, no
|
|
3821
|
+
* live subscription on a portal-less primary). */
|
|
3221
3822
|
getCustomerPortalUrl: (userId: string) => Promise<{
|
|
3222
|
-
url: string;
|
|
3823
|
+
url: string | null;
|
|
3824
|
+
kind: "portal" | "store" | "none";
|
|
3825
|
+
provider: string | null;
|
|
3826
|
+
}>;
|
|
3827
|
+
/** SERVER-ONLY. A support / promotional / migration grant: a `manual`
|
|
3828
|
+
* subscription row (no provider behind it) folded like a webhook —
|
|
3829
|
+
* entitlements + the tier's recurring credits apply; `until` null =
|
|
3830
|
+
* open-ended, else access ends at `until`. Idempotent on
|
|
3831
|
+
* `idempotency_key` (a replay returns the first grant, `replayed: true`).
|
|
3832
|
+
* 422 unknown_tier when the tier is not in ledger.tierMap. */
|
|
3833
|
+
grantSubscription: (input: {
|
|
3834
|
+
user_id: string;
|
|
3835
|
+
tier: string;
|
|
3836
|
+
until?: string | null;
|
|
3837
|
+
reason: string;
|
|
3838
|
+
idempotency_key: string;
|
|
3839
|
+
}) => Promise<{
|
|
3840
|
+
subscription_id: string | null;
|
|
3841
|
+
user_id: string;
|
|
3842
|
+
tier: string;
|
|
3843
|
+
status: string;
|
|
3844
|
+
until: string | null;
|
|
3845
|
+
replayed: boolean;
|
|
3846
|
+
}>;
|
|
3847
|
+
/** SERVER-ONLY. Revoke a MANUAL grant now (409 not_manual for a provider
|
|
3848
|
+
* subscription — use `cancel`). Never touches a provider. */
|
|
3849
|
+
revokeSubscription: (subscriptionId: string) => Promise<{
|
|
3850
|
+
subscription_id: string;
|
|
3851
|
+
status: string;
|
|
3852
|
+
cancel_at: string | null;
|
|
3853
|
+
revoked_at: string | null;
|
|
3854
|
+
}>;
|
|
3855
|
+
/** SERVER-ONLY. Re-read a customer's subscriptions FROM the provider and
|
|
3856
|
+
* fold them through the same conditional upsert a webhook uses (never a
|
|
3857
|
+
* second write path). `dry_run` diffs without writing. Capped 30/min per
|
|
3858
|
+
* project (429 rate_limited). The migration backfill (`vxil migrate
|
|
3859
|
+
* payments --from-provider …`) drives this per customer. */
|
|
3860
|
+
syncSubscriptions: (input: {
|
|
3861
|
+
user_id?: string;
|
|
3862
|
+
customer_ref?: string;
|
|
3863
|
+
provider?: "mock" | "stripe" | "paddle" | "revenuecat" | "paypal";
|
|
3864
|
+
dry_run?: boolean;
|
|
3865
|
+
}) => Promise<PaymentsSyncOutcome>;
|
|
3866
|
+
/** The server leg of "Restore Purchases": in end-user mode the verified
|
|
3867
|
+
* principal's OWN provider state is re-read (no user id needed); in server
|
|
3868
|
+
* mode pass `user_id`. 3 per hour per user (429). Returns the sync outcome
|
|
3869
|
+
* plus the fresh entitlement view in one answer. */
|
|
3870
|
+
restoreSubscriptions: (input?: {
|
|
3871
|
+
user_id?: string;
|
|
3872
|
+
provider?: "mock" | "stripe" | "paddle" | "revenuecat" | "paypal";
|
|
3873
|
+
}) => Promise<PaymentsSyncOutcome & {
|
|
3874
|
+
entitlements: PaymentsEntitlementView;
|
|
3875
|
+
}>;
|
|
3876
|
+
/** SERVER-ONLY. Account merge: move EVERY provider's subscriptions,
|
|
3877
|
+
* customer links and available credit balances of `from_user_id` onto
|
|
3878
|
+
* `into_user_id` — at-most-once per pair. The consumer of the
|
|
3879
|
+
* `auth.user.merged { from, into }` audit event. */
|
|
3880
|
+
reKeySubscriptions: (input: {
|
|
3881
|
+
from_user_id: string;
|
|
3882
|
+
into_user_id: string;
|
|
3883
|
+
}) => Promise<{
|
|
3884
|
+
from_user_id: string;
|
|
3885
|
+
into_user_id: string;
|
|
3886
|
+
moved: number;
|
|
3887
|
+
credits_moved: number;
|
|
3888
|
+
}>;
|
|
3889
|
+
/** SERVER-ONLY, mock/dev tenants ONLY (403 simulation_not_allowed
|
|
3890
|
+
* elsewhere). Drive a scripted lifecycle (renewal, expiry, refund-pair,
|
|
3891
|
+
* past-due-grace, cross-platform-unlock, transfer) through the mock
|
|
3892
|
+
* webhook path and judge it with the conformance oracle. */
|
|
3893
|
+
simulate: (input: {
|
|
3894
|
+
scenario: "refund-pair" | "cross-platform-unlock" | "renewal" | "expiry" | "past-due-grace" | "transfer";
|
|
3895
|
+
user_id: string;
|
|
3896
|
+
tier?: string;
|
|
3897
|
+
}) => Promise<PaymentsSimulationResult>;
|
|
3898
|
+
/** The last report-only reconciliation sweep result for this project
|
|
3899
|
+
* (`run: null` before the first daily tick). */
|
|
3900
|
+
reconcile: () => Promise<{
|
|
3901
|
+
run: {
|
|
3902
|
+
run_id: string;
|
|
3903
|
+
ran_at: string | null;
|
|
3904
|
+
clean: boolean;
|
|
3905
|
+
discrepancy_count: number;
|
|
3906
|
+
lapsed_count: number;
|
|
3907
|
+
findings: Array<{
|
|
3908
|
+
kind: string;
|
|
3909
|
+
count: number;
|
|
3910
|
+
ids: string[];
|
|
3911
|
+
}>;
|
|
3912
|
+
} | null;
|
|
3913
|
+
enforce_period_end: {
|
|
3914
|
+
slack_hours: number;
|
|
3915
|
+
} | null;
|
|
3223
3916
|
}>;
|
|
3224
3917
|
/** List charges newest-first (the refund enabler — discover the charge_id).
|
|
3225
3918
|
* Filters: user_id, status, limit (clamped 1..100, default 50). */
|
|
@@ -3249,7 +3942,9 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
3249
3942
|
list: (q?: {
|
|
3250
3943
|
provider?: "mock" | "stripe" | "paddle" | "revenuecat" | "paypal";
|
|
3251
3944
|
event_type?: string;
|
|
3252
|
-
outcome?: "received" | "processed" | "error" | "sig_failed" | "parse_failed" | "reprocessed";
|
|
3945
|
+
outcome?: "received" | "processed" | "error" | "sig_failed" | "parse_failed" | "reprocessed" | "ignored" | "unowned" | "rejected_environment";
|
|
3946
|
+
/** the provider-reported environment axis (not the API key's label) */
|
|
3947
|
+
environment?: "production" | "sandbox";
|
|
3253
3948
|
since?: string;
|
|
3254
3949
|
cursor?: string;
|
|
3255
3950
|
limit?: number;
|