@vxil/sdk 0.13.1 → 0.14.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/dist/index.d.ts CHANGED
@@ -382,6 +382,82 @@ export interface JobRun {
382
382
  * the fire); `started_at − scheduled_for` is how late it started. null for a
383
383
  * run you enqueued (and for runs fired before 2026-10-01). */
384
384
  scheduled_for?: string | null;
385
+ /** the single-run read only: what the run completed with — the `result` of
386
+ * its signed callback's `completed` body, or the value a generation run
387
+ * settled with (the fields its status mirror received; an over-64 KiB value
388
+ * reads `{ truncated: true, bytes, max_bytes }`). null when nothing reported
389
+ * one — a handler's own 2xx response body is never stored. */
390
+ result?: unknown;
391
+ /** the single-run read only: the latest progress report of the run — a
392
+ * `processing` ping on its signed callback (plain run) or its provider's
393
+ * webhook (generation run). null until one arrives. */
394
+ progress?: JobRunProgress | null;
395
+ /** the single-run read, generation runs only: the LAST `status_mirror`
396
+ * write the target refused, or null. A full write refused as a body
397
+ * problem (400 / 413 / 422 — e.g. a callback key the collection does not
398
+ * declare) is retried with only the status column (a progress update tries
399
+ * the declared `progress_fields` first), so an undeclared key no longer
400
+ * strands the row; this says what was dropped. Never flips the run; not cleared by a later
401
+ * successful mirror (compare `generation_status` / `at`). */
402
+ mirror_error?: JobRunMirrorError | null;
403
+ }
404
+ /** A refused status-mirror write (`JobRun.mirror_error`). */
405
+ export interface JobRunMirrorError {
406
+ /** the status word the refused write carried */
407
+ generation_status: 'pending' | 'processing' | 'completed' | 'failed';
408
+ /** the target's HTTP status on the full write; 0 = no answer (network / timeout) */
409
+ status: number;
410
+ /** the target's error code (e.g. `validation_failed`), `network_error` / `timeout`, or null */
411
+ code: string | null;
412
+ /** the target's message (≤ 200 chars), e.g. "unknown field 'video_url'" */
413
+ message: string | null;
414
+ /** when it was refused (ISO) */
415
+ at: string;
416
+ /** present when a narrower retry LANDED: the keys that were not written */
417
+ fields_dropped?: string[];
418
+ /** present when every narrower retry was refused too (the last one's HTTP
419
+ * status, 0 = no answer): the status word itself was refused and the
420
+ * record still holds the previous status */
421
+ retry_status?: number;
422
+ }
423
+ /** A run's latest progress report (`JobRun.progress`): only these keys pass. */
424
+ export interface JobRunProgress {
425
+ /** 0..100 */
426
+ progress?: number;
427
+ /** ≤ 64 chars */
428
+ stage?: string;
429
+ /** ≤ 200 chars */
430
+ message?: string;
431
+ /** when the report was stored (ISO) */
432
+ at: string;
433
+ }
434
+ /** The body an external worker POSTs to a run's signed `callback_url`
435
+ * (`postRunCallback`). `completed` ends the run `succeeded` with `result`
436
+ * stored (≤ 64 KiB of JSON); `failed` dead-letters it with `error`;
437
+ * `processing` only records progress (repeatable). On a run suspended in
438
+ * `jobs.wait(…)`, completed / failed WAKE the handler instead, with
439
+ * `payload.wakeup = { via: 'callback', status, result?, error? }`. */
440
+ export type RunCallbackBody = {
441
+ status: 'completed';
442
+ result?: unknown;
443
+ } | {
444
+ status: 'failed';
445
+ error?: string;
446
+ } | {
447
+ status: 'processing';
448
+ progress?: number;
449
+ stage?: string;
450
+ message?: string;
451
+ };
452
+ /** What a run callback answers: the run's state after it (`deduplicated: true`
453
+ * when the URL was already used or the run was already terminal — nothing
454
+ * changed). */
455
+ export interface RunCallbackAnswer {
456
+ run_id: string;
457
+ state: JobRun['state'];
458
+ deduplicated?: boolean;
459
+ woken?: boolean;
460
+ event?: string;
385
461
  }
386
462
  /** The run states no later write can move — what `waitForRun` and an
387
463
  * async+wait invoke resolve `done: true` on. */
@@ -661,7 +737,25 @@ export interface FileObject {
661
737
  size_bytes: number;
662
738
  status: 'pending' | 'available' | 'deleted';
663
739
  created_at: string;
740
+ /** The object's public URL when it is published to the public asset host
741
+ * (`files.publish`), else null. */
742
+ public_url?: string | null;
743
+ }
744
+ /** A published object (guide ch. 6, files, "Public asset delivery"): a stable,
745
+ * content-addressed URL on vxil's public asset host, served with
746
+ * `Cache-Control: public, max-age=31536000, immutable`. `variants` maps each
747
+ * declared image preset (`publicAssets.variants`) to its URL — images only. */
748
+ export interface PublishedFile {
749
+ object_id: string;
750
+ url: string;
751
+ sha256: string;
752
+ content_type: string;
753
+ size_bytes: number;
754
+ published_at: string | null;
755
+ variants: Record<string, string>;
664
756
  }
757
+ /** Why one id of a bulk publish was not published. */
758
+ export type PublishErrorCode = 'not_found' | 'upload_incomplete' | 'content_type_not_publishable' | 'object_too_large' | 'quota_exceeded' | 'batch_budget_exceeded' | 'asset_taken_down' | 'publish_failed';
665
759
  /** A live shared link as listed by GET /v1/files/{id}/shared-links (guide ch. 6,
666
760
  * files). `downloads` is the burn-on-read counter; `max_downloads` null =
667
761
  * unlimited (1 = a one-time link). A link that hit its cap, expired, or was
@@ -971,10 +1065,13 @@ export interface JobRunEventPayload {
971
1065
  level?: 'error';
972
1066
  state?: 'broken';
973
1067
  /** `job.dead_lettered` only, and only when something other than the
974
- * executor killed the run: `reaped` (the stuck-run reaper) or
975
- * `queue_backstop` (the queue's own retries ran out). Absent when the run
1068
+ * executor killed the run: `reaped` (the stuck-run reaper),
1069
+ * `queue_backstop` (the queue's own retries ran out), `callback_failed`
1070
+ * (the run's signed callback reported `status: 'failed'`) or
1071
+ * `callback_timeout` (the handler handed the run off with a 202 and no
1072
+ * callback completed it within its lifetime). Absent when the run
976
1073
  * exhausted its attempts normally. */
977
- reason?: 'reaped' | 'queue_backstop';
1074
+ reason?: 'reaped' | 'queue_backstop' | 'callback_failed' | 'callback_timeout';
978
1075
  }
979
1076
  /** The provider-reported environment of the money (`production` | `sandbox`). */
980
1077
  export type PaymentsEventEnvironment = 'production' | 'sandbox';
@@ -1366,6 +1463,13 @@ export interface JobDelivery<P = unknown> {
1366
1463
  payload: P;
1367
1464
  /** a schedule-fired run only: the slot it was due for (ISO, UTC) */
1368
1465
  scheduled_for?: string;
1466
+ /** a run enqueued with `callback` only: its CURRENT single-use signed
1467
+ * callback URL. Hand it to the worker that finishes the job (no API key
1468
+ * needed) and answer 202 — the run waits until that worker POSTs
1469
+ * `{ status: 'completed', result }` / `{ status: 'failed', error }` /
1470
+ * `{ status: 'processing', progress, stage, message }` to it (see
1471
+ * `postRunCallback`). */
1472
+ callback_url?: string;
1369
1473
  }
1370
1474
  /** The `?since=` replay read (GET /v1/ai/generations/{id}/stream). */
1371
1475
  export interface AiResumePage {
@@ -1798,6 +1902,16 @@ export declare function listCmsPublic(tenantId: string, collection: string, quer
1798
1902
  items: CmsPublicRow[];
1799
1903
  next_cursor: string | null;
1800
1904
  }>;
1905
+ /** POST a run's single-use signed `callback_url` (guide ch. 6, "Hand a run to
1906
+ * an external worker") — KEYLESS: no API key, no `Vxil` client; the URL is the
1907
+ * credential, so call this from the worker that finishes the job (a render
1908
+ * farm, a GPU box, another queue's task). Idempotent: a repeat of a used URL
1909
+ * answers `deduplicated: true`. A non-2xx throws `VxilError` (401 a forged or
1910
+ * altered URL, 410 `callback_expired`, 413 `result_too_large`, 404 a run that
1911
+ * did not opt in). */
1912
+ export declare function postRunCallback(callbackUrl: string, body: RunCallbackBody, opts?: {
1913
+ fetch?: typeof fetch;
1914
+ }): Promise<RunCallbackAnswer>;
1801
1915
  /** The structural shape a `vxil gen`-generated `VxilSchema` satisfies. The base
1802
1916
  * `Vxil` class is generic over it (`new Vxil<VxilSchema>(...)`), exactly the
1803
1917
  * `createClient<Database>()` move — types are layered on; the runtime is
@@ -2831,7 +2945,14 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
2831
2945
  readonly jobs: {
2832
2946
  /** Enqueue a one-off job; Vxil POSTs a signed callback to target_url with
2833
2947
  * retries. deliver_after (ISO) / delay_seconds (≤ 30 d, at most one of the
2834
- * two) defer the first delivery; > 12 h returns state 'delayed'. */
2948
+ * two) defer the first delivery; > 12 h returns state 'delayed'.
2949
+ *
2950
+ * `callback: true` (or `{ ttl_seconds }`, 60 s..31 d; default 24 h) opts
2951
+ * the run in to a KEYLESS signed `callback_url` (in the answer and on every
2952
+ * delivery): an external worker POSTs it to complete / fail the run, report
2953
+ * progress, or wake a `wait`. A handler that answers 202 HANDS the run off
2954
+ * — it waits for that callback (dead-lettered as `CallbackTimeout` when the
2955
+ * lifetime passes). See `postRunCallback`. */
2835
2956
  enqueue: (input: {
2836
2957
  job_name: string;
2837
2958
  target_url: string;
@@ -2840,11 +2961,15 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
2840
2961
  max_attempts?: number;
2841
2962
  deliver_after?: string;
2842
2963
  delay_seconds?: number;
2964
+ callback?: boolean | {
2965
+ ttl_seconds?: number;
2966
+ };
2843
2967
  }) => Promise<{
2844
2968
  run_id: string;
2845
2969
  state: string;
2846
2970
  deduplicated?: boolean;
2847
2971
  deliver_after?: string;
2972
+ callback_url?: string;
2848
2973
  }>;
2849
2974
  /** Atomic multi-enqueue (≤100 items; any invalid item rejects the whole
2850
2975
  * batch). Each item = the enqueue input, incl. per-item idempotency_key
@@ -2889,7 +3014,12 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
2889
3014
  * sends `provider.body` exactly; `completion.status_map` maps up to 8
2890
3015
  * provider words to completed | failed | processing (`{ OK: 'completed',
2891
3016
  * ERROR: 'failed' }`); `completion.result_path` names the value the run
2892
- * settles with — the status mirror receives it as `result`. */
3017
+ * settles with — the status mirror receives it as `result`.
3018
+ *
3019
+ * A mirror write the record refuses never fails the run: a body refusal
3020
+ * (e.g. a callback key the collection does not declare) is retried with
3021
+ * only the status column, so the row still reaches `completed`; the run then reports `mirror_error`
3022
+ * (`JobRun.mirror_error` on `vx.jobs.run(run_id)`). */
2893
3023
  generation: (input: {
2894
3024
  job_name: string;
2895
3025
  provider: {
@@ -2916,11 +3046,15 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
2916
3046
  /** dotted path of the value a completed run settles with (mirrored as `result`) */
2917
3047
  result_path?: string;
2918
3048
  };
3049
+ /** `progress_fields`: the progress keys (`progress` / `stage` /
3050
+ * `message`) a webhook-mode `processing` callback also writes onto the
3051
+ * mirrored record — realtime clients of that record see them live */
2919
3052
  status_mirror?: {
2920
3053
  feature: string;
2921
3054
  collection: string;
2922
3055
  record_id: string;
2923
3056
  column?: string;
3057
+ progress_fields?: Array<"progress" | "stage" | "message">;
2924
3058
  };
2925
3059
  timeout?: {
2926
3060
  after_ms: number;
@@ -2988,12 +3122,21 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
2988
3122
  }>;
2989
3123
  /**
2990
3124
  * Suspend the RUNNING run until an event (call from the executing
2991
- * handler, then return 200 — the suspension wins).
3125
+ * handler, then return 200 — the suspension wins). `state: 'resumed'` =
3126
+ * the event already fired (continue; `wakeup` is its payload). A run
3127
+ * enqueued with `callback` also gets its `callback_url`: an external worker
3128
+ * POSTing it wakes this wait with no API key.
2992
3129
  */
2993
3130
  wait: (runId: string, input: {
2994
3131
  event: string;
2995
3132
  timeout_seconds?: number;
2996
- }) => Promise<void>;
3133
+ }) => Promise<{
3134
+ run_id: string;
3135
+ state: "waiting" | "resumed";
3136
+ event: string;
3137
+ wakeup?: unknown;
3138
+ callback_url?: string;
3139
+ }>;
2997
3140
  /** Wake every run waiting on the event. */
2998
3141
  emitEvent: (event: string, payload?: Record<string, unknown>) => Promise<number>;
2999
3142
  /** Secret for verifying X-Vxil-Jobs-Signature on your callback endpoints. */
@@ -4708,7 +4851,9 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
4708
4851
  }>;
4709
4852
  /** Soft delete; bytes are hard-deleted 30 days later. */
4710
4853
  delete: (objectId: string) => Promise<void>;
4711
- /** Aggregate storage usage vs quotas (the FilesManager Storage panel). */
4854
+ /** Aggregate storage usage vs quotas (the FilesManager Storage panel).
4855
+ * `public_assets` = published copies vs the plan's published-bytes ceiling
4856
+ * (identical bytes count once). */
4712
4857
  usage: () => Promise<{
4713
4858
  usage: {
4714
4859
  object_count: number;
@@ -4720,7 +4865,46 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
4720
4865
  maxTotalBytes?: number;
4721
4866
  };
4722
4867
  available: Record<string, number | null>;
4868
+ public_assets?: {
4869
+ enabled: boolean;
4870
+ published_objects: number;
4871
+ published_bytes: number;
4872
+ max_published_bytes: number;
4873
+ bytes_remaining: number;
4874
+ };
4875
+ }>;
4876
+ /** SERVER-ONLY (403 server_only in end-user mode). Publish an available
4877
+ * object to vxil's public asset host: a stable, content-addressed URL
4878
+ * (`https://cdn.vxil.app/<tenant>/<sha256>.<ext>`) served from a global
4879
+ * edge cache with `Cache-Control: public, max-age=31536000, immutable`,
4880
+ * playable video/audio and CORS from `publicAssets.corsOrigins`. Needs
4881
+ * `files.publicAssets.enabled`. Publishable: png/jpeg/webp/avif/gif,
4882
+ * mp4/webm, mp3/m4a/ogg, woff2/woff, json — never HTML or SVG (422
4883
+ * `content_type_not_publishable`). Counts against the plan's
4884
+ * published-bytes ceiling (422 `quota_exceeded`). Idempotent: an already
4885
+ * published object answers its existing URL. Emits
4886
+ * `files.object.published`. */
4887
+ publish: (objectId: string) => Promise<PublishedFile>;
4888
+ /** SERVER-ONLY. Publish up to 100 objects (about 2 GiB of bytes) in one
4889
+ * call; a bad id lands in `errors[]` without failing the rest (ids past
4890
+ * the byte budget as `batch_budget_exceeded` — send them again),
4891
+ * `published[]` keeps your order. */
4892
+ publishMany: (objectIds: string[]) => Promise<{
4893
+ published: PublishedFile[];
4894
+ errors: Array<{
4895
+ object_id: string;
4896
+ code: PublishErrorCode;
4897
+ message: string;
4898
+ }>;
4723
4899
  }>;
4900
+ /** SERVER-ONLY. Take an object off the public asset host. Its public copy
4901
+ * is deleted once no other object of yours has the same bytes, and the
4902
+ * edge cache stops serving it within about a minute and a half (a
4903
+ * browser that already downloaded it keeps its copy). Deleting the object
4904
+ * unpublishes it too. 404 when it is not published; 503
4905
+ * `unpublish_failed` when the public copy could not be removed right
4906
+ * then (it stays published — retry). Emits `files.object.unpublished`. */
4907
+ unpublish: (objectId: string) => Promise<void>;
4724
4908
  /** OCR / text extraction (guide ch. 6, files, BYO-key add-on). Small/mock inputs
4725
4909
  * extract inline (status `available`); large inputs (or `async:true`) return
4726
4910
  * 202 with a `job_id` — poll `getText()`. */
package/dist/index.js CHANGED
@@ -182,6 +182,36 @@ export async function listCmsPublic(tenantId, collection, query, opts) {
182
182
  }
183
183
  return parsed.data ?? { items: [], next_cursor: null };
184
184
  }
185
+ /** POST a run's single-use signed `callback_url` (guide ch. 6, "Hand a run to
186
+ * an external worker") — KEYLESS: no API key, no `Vxil` client; the URL is the
187
+ * credential, so call this from the worker that finishes the job (a render
188
+ * farm, a GPU box, another queue's task). Idempotent: a repeat of a used URL
189
+ * answers `deduplicated: true`. A non-2xx throws `VxilError` (401 a forged or
190
+ * altered URL, 410 `callback_expired`, 413 `result_too_large`, 404 a run that
191
+ * did not opt in). */
192
+ export async function postRunCallback(callbackUrl, body, opts) {
193
+ const fetchImpl = opts?.fetch ?? fetch;
194
+ const res = await fetchImpl(callbackUrl, {
195
+ method: 'POST',
196
+ headers: { 'content-type': 'application/json' },
197
+ body: JSON.stringify(body),
198
+ });
199
+ const text = await res.text();
200
+ let parsed = {};
201
+ if (text.length > 0) {
202
+ try {
203
+ parsed = JSON.parse(text);
204
+ }
205
+ catch {
206
+ throw new VxilError(res.status, `http_${res.status}`, text.slice(0, 200), undefined, undefined, undefined, parseRetryAfter(res.headers.get('retry-after')));
207
+ }
208
+ }
209
+ if (!res.ok || parsed.error || !parsed.data) {
210
+ const e = parsed.error ?? { code: `http_${res.status}`, message: text.slice(0, 200) };
211
+ throw new VxilError(res.status, e.code, e.message, e.hint, e.fixUrl, parsed.meta?.request_id, parseRetryAfter(res.headers.get('retry-after')));
212
+ }
213
+ return parsed.data;
214
+ }
185
215
  export class Vxil {
186
216
  base;
187
217
  key;
@@ -651,7 +681,14 @@ export class Vxil {
651
681
  jobs = {
652
682
  /** Enqueue a one-off job; Vxil POSTs a signed callback to target_url with
653
683
  * retries. deliver_after (ISO) / delay_seconds (≤ 30 d, at most one of the
654
- * two) defer the first delivery; > 12 h returns state 'delayed'. */
684
+ * two) defer the first delivery; > 12 h returns state 'delayed'.
685
+ *
686
+ * `callback: true` (or `{ ttl_seconds }`, 60 s..31 d; default 24 h) opts
687
+ * the run in to a KEYLESS signed `callback_url` (in the answer and on every
688
+ * delivery): an external worker POSTs it to complete / fail the run, report
689
+ * progress, or wake a `wait`. A handler that answers 202 HANDS the run off
690
+ * — it waits for that callback (dead-lettered as `CallbackTimeout` when the
691
+ * lifetime passes). See `postRunCallback`. */
655
692
  enqueue: async (input) => (await this.call('POST', '/v1/jobs/enqueue', input)).data,
656
693
  /** Atomic multi-enqueue (≤100 items; any invalid item rejects the whole
657
694
  * batch). Each item = the enqueue input, incl. per-item idempotency_key
@@ -681,7 +718,12 @@ export class Vxil {
681
718
  * sends `provider.body` exactly; `completion.status_map` maps up to 8
682
719
  * provider words to completed | failed | processing (`{ OK: 'completed',
683
720
  * ERROR: 'failed' }`); `completion.result_path` names the value the run
684
- * settles with — the status mirror receives it as `result`. */
721
+ * settles with — the status mirror receives it as `result`.
722
+ *
723
+ * A mirror write the record refuses never fails the run: a body refusal
724
+ * (e.g. a callback key the collection does not declare) is retried with
725
+ * only the status column, so the row still reaches `completed`; the run then reports `mirror_error`
726
+ * (`JobRun.mirror_error` on `vx.jobs.run(run_id)`). */
685
727
  generation: async (input) => (await this.call('POST', '/v1/jobs/generation', input)).data,
686
728
  runs: async (q) => {
687
729
  const s = qs({
@@ -717,11 +759,12 @@ export class Vxil {
717
759
  replay: async (runId) => (await this.call('POST', `/v1/jobs/runs/${encodeURIComponent(runId)}/replay`)).data,
718
760
  /**
719
761
  * Suspend the RUNNING run until an event (call from the executing
720
- * handler, then return 200 — the suspension wins).
762
+ * handler, then return 200 — the suspension wins). `state: 'resumed'` =
763
+ * the event already fired (continue; `wakeup` is its payload). A run
764
+ * enqueued with `callback` also gets its `callback_url`: an external worker
765
+ * POSTing it wakes this wait with no API key.
721
766
  */
722
- wait: async (runId, input) => {
723
- await this.call('POST', `/v1/jobs/runs/${encodeURIComponent(runId)}/wait`, input);
724
- },
767
+ wait: async (runId, input) => (await this.call('POST', `/v1/jobs/runs/${encodeURIComponent(runId)}/wait`, input)).data,
725
768
  /** Wake every run waiting on the event. */
726
769
  emitEvent: async (event, payload) => (await this.call('POST', '/v1/jobs/events', { event, ...(payload ? { payload } : {}) })).data.woken,
727
770
  /** Secret for verifying X-Vxil-Jobs-Signature on your callback endpoints. */
@@ -1733,8 +1776,37 @@ export class Vxil {
1733
1776
  delete: async (objectId) => {
1734
1777
  await this.call('DELETE', `/v1/files/${encodeURIComponent(objectId)}`);
1735
1778
  },
1736
- /** Aggregate storage usage vs quotas (the FilesManager Storage panel). */
1779
+ /** Aggregate storage usage vs quotas (the FilesManager Storage panel).
1780
+ * `public_assets` = published copies vs the plan's published-bytes ceiling
1781
+ * (identical bytes count once). */
1737
1782
  usage: async () => (await this.call('GET', '/v1/files/usage')).data,
1783
+ /** SERVER-ONLY (403 server_only in end-user mode). Publish an available
1784
+ * object to vxil's public asset host: a stable, content-addressed URL
1785
+ * (`https://cdn.vxil.app/<tenant>/<sha256>.<ext>`) served from a global
1786
+ * edge cache with `Cache-Control: public, max-age=31536000, immutable`,
1787
+ * playable video/audio and CORS from `publicAssets.corsOrigins`. Needs
1788
+ * `files.publicAssets.enabled`. Publishable: png/jpeg/webp/avif/gif,
1789
+ * mp4/webm, mp3/m4a/ogg, woff2/woff, json — never HTML or SVG (422
1790
+ * `content_type_not_publishable`). Counts against the plan's
1791
+ * published-bytes ceiling (422 `quota_exceeded`). Idempotent: an already
1792
+ * published object answers its existing URL. Emits
1793
+ * `files.object.published`. */
1794
+ publish: async (objectId) => (await this.call('POST', `/v1/files/${encodeURIComponent(objectId)}/publish`)).data,
1795
+ /** SERVER-ONLY. Publish up to 100 objects (about 2 GiB of bytes) in one
1796
+ * call; a bad id lands in `errors[]` without failing the rest (ids past
1797
+ * the byte budget as `batch_budget_exceeded` — send them again),
1798
+ * `published[]` keeps your order. */
1799
+ publishMany: async (objectIds) => (await this.call('POST', '/v1/files/publish', { object_ids: objectIds })).data,
1800
+ /** SERVER-ONLY. Take an object off the public asset host. Its public copy
1801
+ * is deleted once no other object of yours has the same bytes, and the
1802
+ * edge cache stops serving it within about a minute and a half (a
1803
+ * browser that already downloaded it keeps its copy). Deleting the object
1804
+ * unpublishes it too. 404 when it is not published; 503
1805
+ * `unpublish_failed` when the public copy could not be removed right
1806
+ * then (it stays published — retry). Emits `files.object.unpublished`. */
1807
+ unpublish: async (objectId) => {
1808
+ await this.call('DELETE', `/v1/files/${encodeURIComponent(objectId)}/publish`);
1809
+ },
1738
1810
  /** OCR / text extraction (guide ch. 6, files, BYO-key add-on). Small/mock inputs
1739
1811
  * extract inline (status `available`); large inputs (or `async:true`) return
1740
1812
  * 202 with a `job_id` — poll `getText()`. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vxil/sdk",
3
- "version": "0.13.1",
3
+ "version": "0.14.1",
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).",