@withmestra/sdk 0.0.0-stage → 0.1.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/errors.js ADDED
@@ -0,0 +1,80 @@
1
+ /** Anything the API answers with an error status. `code` and `field` are the API's own. */
2
+ export class MestraError extends Error {
3
+ status;
4
+ code;
5
+ field;
6
+ /** The rest of the error body (`limit`, `max`, `latest`, ...), as the API sent it. */
7
+ details;
8
+ constructor(message, init) {
9
+ super(message);
10
+ this.name = new.target.name;
11
+ this.status = init.status;
12
+ if (init.code !== undefined)
13
+ this.code = init.code;
14
+ if (init.field !== undefined)
15
+ this.field = init.field;
16
+ this.details = init.details ?? {};
17
+ }
18
+ }
19
+ /** 404 `session_gone`. The session helpers recover from it by themselves; you only see it if recovery fails. */
20
+ export class SessionGoneError extends MestraError {
21
+ }
22
+ /** The size changed while you were editing (412 `scene_changed`), or changed before a lost session could be replayed. */
23
+ export class SizeChangedError extends MestraError {
24
+ }
25
+ /** 412 `version_changed`: the master has a newer version than the one you opened. Open it again and redo the edits. */
26
+ export class VersionChangedError extends MestraError {
27
+ /** The master's newest version number. */
28
+ latest;
29
+ /** `mestra` for a save made here, else the app: `indesign`, `figma`, `canva` or `upload`. */
30
+ by;
31
+ /** Who saved it, for a save made in Mestra; empty for an app's. */
32
+ byName;
33
+ constructor(message, init) {
34
+ super(message, init);
35
+ const d = init.details ?? {};
36
+ this.latest = typeof d.latest === "number" ? d.latest : 0;
37
+ this.by = typeof d.by === "string" ? d.by : "";
38
+ this.byName = typeof d.byName === "string" ? d.byName : "";
39
+ }
40
+ }
41
+ /** A lost session was reopened and the log replayed, but an edit answered differently from the first time. */
42
+ export class ReplayDivergedError extends MestraError {
43
+ }
44
+ /** 409 `sizes_busy`: another across-sizes call by the same user is still running. */
45
+ export class SizesBusyError extends MestraError {
46
+ }
47
+ /** 404 `operation_gone`: the operation is unknown, has ended, or is another user's. */
48
+ export class OperationGoneError extends MestraError {
49
+ }
50
+ /** Waiting for an across-sizes operation took longer than `timeoutMs`. The operation itself carries on. */
51
+ export class WaitTimeoutError extends MestraError {
52
+ /** Pass it to `getSizesOperation` or `watchSizes` to keep waiting. */
53
+ operation;
54
+ constructor(message, operation) {
55
+ super(message, { status: 0, code: "wait_timeout" });
56
+ this.operation = operation;
57
+ }
58
+ }
59
+ export function errorFor(status, body) {
60
+ const b = body !== null && typeof body === "object" ? body : {};
61
+ const { error, code, field, ...details } = b;
62
+ const message = typeof error === "string" && error !== "" ? error : `The API answered ${status}.`;
63
+ const init = {
64
+ status,
65
+ ...(typeof code === "string" ? { code } : {}),
66
+ ...(typeof field === "string" ? { field } : {}),
67
+ details,
68
+ };
69
+ if (status === 404 && code === "session_gone")
70
+ return new SessionGoneError(message, init);
71
+ if (status === 404 && code === "operation_gone")
72
+ return new OperationGoneError(message, init);
73
+ if (status === 409 && code === "sizes_busy")
74
+ return new SizesBusyError(message, init);
75
+ if (status === 412 && code === "version_changed")
76
+ return new VersionChangedError(message, init);
77
+ if (status === 412 && code === "scene_changed")
78
+ return new SizeChangedError(message, init);
79
+ return new MestraError(message, init);
80
+ }
package/dist/http.d.ts ADDED
@@ -0,0 +1,36 @@
1
+ /** Timers come from here so tests never wait. */
2
+ export interface Clock {
3
+ now(): number;
4
+ sleep(ms: number): Promise<void>;
5
+ }
6
+ export declare const systemClock: Clock;
7
+ /** The shape of the standard `fetch`. */
8
+ export type FetchLike = (input: string | Request, init?: RequestInit) => Promise<Response>;
9
+ export interface TransportOptions {
10
+ token: string;
11
+ /** The origin, such as `https://app.withmestra.com`. A trailing `/api/v1` is accepted and dropped. */
12
+ baseUrl?: string;
13
+ fetch?: FetchLike;
14
+ clock?: Clock;
15
+ }
16
+ export declare const defaultBaseUrl = "https://app.withmestra.com";
17
+ export declare const sessionHeader = "X-Mestra-Edit-Session";
18
+ export interface Sent {
19
+ status: number;
20
+ body: any;
21
+ }
22
+ /** One place that sends a request and turns an error answer into a MestraError. */
23
+ export declare class Transport {
24
+ readonly clock: Clock;
25
+ readonly baseUrl: string;
26
+ private readonly token;
27
+ private readonly fetchFn;
28
+ constructor(o: TransportOptions);
29
+ fetch(input: string | Request, init?: RequestInit): Promise<Response>;
30
+ send(method: string, path: string, o?: {
31
+ headers?: Record<string, string>;
32
+ json?: unknown;
33
+ bytes?: Uint8Array;
34
+ contentType?: string;
35
+ }): Promise<Sent>;
36
+ }
package/dist/http.js ADDED
@@ -0,0 +1,54 @@
1
+ import { errorFor } from "./errors.js";
2
+ export const systemClock = {
3
+ now: () => Date.now(),
4
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
5
+ };
6
+ export const defaultBaseUrl = "https://app.withmestra.com";
7
+ export const sessionHeader = "X-Mestra-Edit-Session";
8
+ /** One place that sends a request and turns an error answer into a MestraError. */
9
+ export class Transport {
10
+ clock;
11
+ baseUrl;
12
+ token;
13
+ fetchFn;
14
+ constructor(o) {
15
+ this.token = o.token;
16
+ this.baseUrl = (o.baseUrl ?? defaultBaseUrl).replace(/\/+$/, "").replace(/\/api\/v1$/, "");
17
+ this.fetchFn = o.fetch;
18
+ this.clock = o.clock ?? systemClock;
19
+ }
20
+ fetch(input, init) {
21
+ return (this.fetchFn ?? globalThis.fetch.bind(globalThis))(input, init);
22
+ }
23
+ async send(method, path, o = {}) {
24
+ const headers = { Authorization: `Bearer ${this.token}`, Accept: "application/json", ...o.headers };
25
+ let body;
26
+ if (o.json !== undefined) {
27
+ headers["Content-Type"] = "application/json";
28
+ body = JSON.stringify(o.json);
29
+ }
30
+ else if (o.bytes !== undefined) {
31
+ // fetch sets Content-Length itself for a byte body.
32
+ if (o.contentType)
33
+ headers["Content-Type"] = o.contentType;
34
+ body = o.bytes;
35
+ }
36
+ const init = { method, headers };
37
+ if (body !== undefined)
38
+ init.body = body;
39
+ const res = await this.fetch(this.baseUrl + path, init);
40
+ const text = await res.text();
41
+ let parsed = {};
42
+ if (text !== "") {
43
+ try {
44
+ parsed = JSON.parse(text);
45
+ }
46
+ catch {
47
+ parsed = {};
48
+ }
49
+ }
50
+ if (res.status >= 400)
51
+ throw errorFor(res.status, parsed);
52
+ return { status: res.status, body: parsed };
53
+ }
54
+ }
@@ -0,0 +1,123 @@
1
+ import type { paths } from "./v1.js";
2
+ import { type TransportOptions } from "./http.js";
3
+ import { MasterSession, SizeSession } from "./edit.js";
4
+ import { type EditSizesRequest, type SaveSizesOptions } from "./sizes.js";
5
+ import type { EditSizeResult, SaveSizeResult, SizesProgress, WaitOptions } from "./types.js";
6
+ export * from "./errors.js";
7
+ export * from "./types.js";
8
+ export { EditSession, SizeSession, MasterSession } from "./edit.js";
9
+ export type { EditSizesRequest, EditSizesResult, SaveSizesOptions } from "./sizes.js";
10
+ export { defaultPollIntervalMs, defaultTimeoutMs } from "./sizes.js";
11
+ export { defaultBaseUrl, systemClock, type Clock, type FetchLike, type TransportOptions } from "./http.js";
12
+ export type { paths, components, operations } from "./v1.js";
13
+ export type MestraOptions = TransportOptions;
14
+ /**
15
+ * Makes a client. `baseUrl` is the origin (default `https://app.withmestra.com`); `fetch` and `clock` are for tests.
16
+ */
17
+ export declare function createMestra(options: MestraOptions): {
18
+ /** The whole API, typed from the OpenAPI file. */
19
+ api: import("openapi-fetch").Client<paths, `${string}/${string}`>;
20
+ /** Opens one size of a run (`runId` is the run's id, `size` the format's key). */
21
+ openSize: (runId: string, size: string) => Promise<SizeSession>;
22
+ /** Opens a master for editing: the latest version, or `{version}`. Its save sends the version as `If-Match`. */
23
+ openMaster: (masterId: string, o?: {
24
+ version?: number | "latest";
25
+ }) => Promise<MasterSession>;
26
+ /** Which sizes of a run can be opened, with the scene hash each was last saved at. */
27
+ editable: (runId: string) => Promise<any>;
28
+ /** One edit on many sizes of a campaign. Waits for the answer, polling if the server needs longer. */
29
+ editSizes: (req: EditSizesRequest, o?: WaitOptions<EditSizeResult>) => Promise<{
30
+ results: import("./sizes.js").EditSizesResult[];
31
+ }>;
32
+ /** Saves several size sessions, each on its own. Waits for the answer. Size sessions only. */
33
+ saveSizes: (sessions: SizeSession[], o?: SaveSizesOptions<SaveSizeResult>) => Promise<{
34
+ results: SaveSizeResult[];
35
+ }>;
36
+ /** The raw first answer of an edit across sizes, for callers who watch progress themselves. */
37
+ startEditSizes: (req: EditSizesRequest) => Promise<SizesProgress<{
38
+ run?: string;
39
+ centre: string;
40
+ size?: string;
41
+ outcome: "applied" | "no_change" | "refused" | "not_editable" | "busy" | "failed";
42
+ status?: number;
43
+ code?: string;
44
+ session?: string;
45
+ sceneHash?: string;
46
+ label?: string;
47
+ check?: import("./v1.js").components["schemas"]["EditCheck"];
48
+ refused?: string;
49
+ detail?: string;
50
+ limit?: Record<string, never>;
51
+ candidates?: import("./v1.js").components["schemas"]["EditCandidate"][];
52
+ elements?: import("./v1.js").components["schemas"]["EditElement"][];
53
+ }>>;
54
+ /** The raw first answer of a save across sizes. */
55
+ startSaveSizes: (sessions: SizeSession[], idempotencyKey?: string) => Promise<SizesProgress<{
56
+ session: string;
57
+ outcome: "saved" | "failed";
58
+ status: number;
59
+ code?: string;
60
+ sceneHash?: string;
61
+ round?: number | null;
62
+ pull?: "ready" | "quiet" | "not_sent" | "superseded" | "none";
63
+ fileId?: string | null;
64
+ version?: number;
65
+ }>>;
66
+ /** Reads a running operation once. */
67
+ getSizesOperation: <R = {
68
+ run?: string;
69
+ centre: string;
70
+ size?: string;
71
+ outcome: "applied" | "no_change" | "refused" | "not_editable" | "busy" | "failed";
72
+ status?: number;
73
+ code?: string;
74
+ session?: string;
75
+ sceneHash?: string;
76
+ label?: string;
77
+ check?: import("./v1.js").components["schemas"]["EditCheck"];
78
+ refused?: string;
79
+ detail?: string;
80
+ limit?: Record<string, never>;
81
+ candidates?: import("./v1.js").components["schemas"]["EditCandidate"][];
82
+ elements?: import("./v1.js").components["schemas"]["EditElement"][];
83
+ } | {
84
+ session: string;
85
+ outcome: "saved" | "failed";
86
+ status: number;
87
+ code?: string;
88
+ sceneHash?: string;
89
+ round?: number | null;
90
+ pull?: "ready" | "quiet" | "not_sent" | "superseded" | "none";
91
+ fileId?: string | null;
92
+ version?: number;
93
+ }>(id: string) => Promise<SizesProgress<R>>;
94
+ /** An async iterator over an operation's progress, from the first answer until `done`. */
95
+ watchSizes: <R = {
96
+ run?: string;
97
+ centre: string;
98
+ size?: string;
99
+ outcome: "applied" | "no_change" | "refused" | "not_editable" | "busy" | "failed";
100
+ status?: number;
101
+ code?: string;
102
+ session?: string;
103
+ sceneHash?: string;
104
+ label?: string;
105
+ check?: import("./v1.js").components["schemas"]["EditCheck"];
106
+ refused?: string;
107
+ detail?: string;
108
+ limit?: Record<string, never>;
109
+ candidates?: import("./v1.js").components["schemas"]["EditCandidate"][];
110
+ elements?: import("./v1.js").components["schemas"]["EditElement"][];
111
+ } | {
112
+ session: string;
113
+ outcome: "saved" | "failed";
114
+ status: number;
115
+ code?: string;
116
+ sceneHash?: string;
117
+ round?: number | null;
118
+ pull?: "ready" | "quiet" | "not_sent" | "superseded" | "none";
119
+ fileId?: string | null;
120
+ version?: number;
121
+ }>(first: SizesProgress<R>, o?: WaitOptions<R>) => AsyncGenerator<SizesProgress<R>, void, undefined>;
122
+ };
123
+ export type Mestra = ReturnType<typeof createMestra>;
package/dist/index.js ADDED
@@ -0,0 +1,47 @@
1
+ import createClient from "openapi-fetch";
2
+ import { Transport } from "./http.js";
3
+ import { openMaster as openMasterWith, openSize as openSizeWith } from "./edit.js";
4
+ import { editSizes as editSizesWith, getSizesOperation, saveSizes as saveSizesWith, startEditSizes, startSaveSizes, watchSizes, } from "./sizes.js";
5
+ export * from "./errors.js";
6
+ export * from "./types.js";
7
+ export { EditSession, SizeSession, MasterSession } from "./edit.js";
8
+ export { defaultPollIntervalMs, defaultTimeoutMs } from "./sizes.js";
9
+ export { defaultBaseUrl, systemClock } from "./http.js";
10
+ /**
11
+ * Makes a client. `baseUrl` is the origin (default `https://app.withmestra.com`); `fetch` and `clock` are for tests.
12
+ */
13
+ export function createMestra(options) {
14
+ const t = new Transport(options);
15
+ const api = createClient({
16
+ baseUrl: t.baseUrl,
17
+ fetch: (req) => t.fetch(req),
18
+ });
19
+ api.use({
20
+ onRequest({ request }) {
21
+ request.headers.set("Authorization", `Bearer ${options.token}`);
22
+ return request;
23
+ },
24
+ });
25
+ return {
26
+ /** The whole API, typed from the OpenAPI file. */
27
+ api,
28
+ /** Opens one size of a run (`runId` is the run's id, `size` the format's key). */
29
+ openSize: (runId, size) => openSizeWith(t, runId, size),
30
+ /** Opens a master for editing: the latest version, or `{version}`. Its save sends the version as `If-Match`. */
31
+ openMaster: (masterId, o) => openMasterWith(t, masterId, o),
32
+ /** Which sizes of a run can be opened, with the scene hash each was last saved at. */
33
+ editable: async (runId) => (await t.send("GET", `/api/v1/jobs/${encodeURIComponent(runId)}/editable`)).body,
34
+ /** One edit on many sizes of a campaign. Waits for the answer, polling if the server needs longer. */
35
+ editSizes: (req, o) => editSizesWith(t, req, o),
36
+ /** Saves several size sessions, each on its own. Waits for the answer. Size sessions only. */
37
+ saveSizes: (sessions, o) => saveSizesWith(t, sessions, o),
38
+ /** The raw first answer of an edit across sizes, for callers who watch progress themselves. */
39
+ startEditSizes: (req) => startEditSizes(t, req),
40
+ /** The raw first answer of a save across sizes. */
41
+ startSaveSizes: (sessions, idempotencyKey) => startSaveSizes(t, sessions, idempotencyKey),
42
+ /** Reads a running operation once. */
43
+ getSizesOperation: (id) => getSizesOperation(t, id),
44
+ /** An async iterator over an operation's progress, from the first answer until `done`. */
45
+ watchSizes: (first, o) => watchSizes(t, first, o),
46
+ };
47
+ }
@@ -0,0 +1,33 @@
1
+ import { SizeSession } from "./edit.js";
2
+ import type { Transport } from "./http.js";
3
+ import type { Edit, EditSizeResult, SaveSizeResult, SizesProgress, WaitOptions } from "./types.js";
4
+ export declare const defaultPollIntervalMs = 1000;
5
+ export declare const defaultTimeoutMs = 600000;
6
+ /** Reads an operation once. Throws OperationGoneError when it has ended or isn't yours. */
7
+ export declare function getSizesOperation<R>(t: Transport, id: string): Promise<SizesProgress<R>>;
8
+ /**
9
+ * Yields the first answer, then every read of the operation, until it is done. The timer is the transport's clock.
10
+ */
11
+ export declare function watchSizes<R>(t: Transport, first: SizesProgress<R>, o?: WaitOptions<R>): AsyncGenerator<SizesProgress<R>, void, undefined>;
12
+ export interface EditSizesRequest {
13
+ campaign: string;
14
+ centres?: string[];
15
+ sizes: string[] | "all";
16
+ edit: Edit;
17
+ idempotencyKey?: string;
18
+ }
19
+ /** A size's result, with the session object when it has one. */
20
+ export type EditSizesResult = EditSizeResult & {
21
+ editSession?: SizeSession;
22
+ };
23
+ export declare function startEditSizes(t: Transport, req: EditSizesRequest): Promise<SizesProgress<EditSizeResult>>;
24
+ export declare function editSizes(t: Transport, req: EditSizesRequest, o?: WaitOptions<EditSizeResult>): Promise<{
25
+ results: EditSizesResult[];
26
+ }>;
27
+ export interface SaveSizesOptions<R> extends WaitOptions<R> {
28
+ idempotencyKey?: string;
29
+ }
30
+ export declare function startSaveSizes(t: Transport, sessions: SizeSession[], idempotencyKey?: string): Promise<SizesProgress<SaveSizeResult>>;
31
+ export declare function saveSizes(t: Transport, sessions: SizeSession[], o?: SaveSizesOptions<SaveSizeResult>): Promise<{
32
+ results: SaveSizeResult[];
33
+ }>;
package/dist/sizes.js ADDED
@@ -0,0 +1,97 @@
1
+ import { SizeSession } from "./edit.js";
2
+ import { WaitTimeoutError } from "./errors.js";
3
+ export const defaultPollIntervalMs = 1000;
4
+ export const defaultTimeoutMs = 600_000;
5
+ function randomKey() {
6
+ return globalThis.crypto.randomUUID();
7
+ }
8
+ /** `200` is a finished call; `202` is an operation to read until its status is `done`. */
9
+ function progressOf(status, body) {
10
+ if (status === 202 || body.operation !== undefined) {
11
+ return {
12
+ operation: body.operation,
13
+ ...(body.kind ? { kind: body.kind } : {}),
14
+ status: body.status,
15
+ done: body.done,
16
+ total: body.total,
17
+ results: body.results ?? [],
18
+ };
19
+ }
20
+ const results = (body.results ?? []);
21
+ return { status: "done", done: results.length, total: results.length, results };
22
+ }
23
+ /** Reads an operation once. Throws OperationGoneError when it has ended or isn't yours. */
24
+ export async function getSizesOperation(t, id) {
25
+ const res = await t.send("GET", `/api/v1/edit/sizes/operations/${encodeURIComponent(id)}`);
26
+ return progressOf(res.status, res.body);
27
+ }
28
+ /**
29
+ * Yields the first answer, then every read of the operation, until it is done. The timer is the transport's clock.
30
+ */
31
+ export async function* watchSizes(t, first, o = {}) {
32
+ const interval = o.pollIntervalMs ?? defaultPollIntervalMs;
33
+ const timeout = o.timeoutMs ?? defaultTimeoutMs;
34
+ const started = t.clock.now();
35
+ let p = first;
36
+ o.onProgress?.(p);
37
+ yield p;
38
+ while (p.status !== "done") {
39
+ const id = p.operation;
40
+ if (id === undefined)
41
+ return;
42
+ if (t.clock.now() - started >= timeout) {
43
+ throw new WaitTimeoutError(`Waited ${timeout} ms and the operation was still running. It carries on.`, id);
44
+ }
45
+ await t.clock.sleep(interval);
46
+ p = await getSizesOperation(t, id);
47
+ o.onProgress?.(p);
48
+ yield p;
49
+ }
50
+ }
51
+ async function finish(t, first, o) {
52
+ let last = first;
53
+ for await (const p of watchSizes(t, first, o))
54
+ last = p;
55
+ return last;
56
+ }
57
+ export async function startEditSizes(t, req) {
58
+ const res = await t.send("POST", "/api/v1/edit/sizes", {
59
+ json: { ...req, edit: { name: req.edit.name, input: req.edit.input ?? {} }, idempotencyKey: req.idempotencyKey ?? randomKey() },
60
+ });
61
+ return progressOf(res.status, res.body);
62
+ }
63
+ export async function editSizes(t, req, o = {}) {
64
+ const last = await finish(t, await startEditSizes(t, req), o);
65
+ const results = last.results.map((r) => {
66
+ if (r.session && r.sceneHash && r.run && r.size) {
67
+ const { session: id, sceneHash, run, size, label, check, elements } = r;
68
+ return {
69
+ ...r,
70
+ editSession: SizeSession.fromBatch(t, { run, size, session: id, sceneHash, ...(label ? { label } : {}), ...(check ? { check } : {}), ...(elements ? { elements } : {}) }, req.edit),
71
+ };
72
+ }
73
+ return r;
74
+ });
75
+ return { results };
76
+ }
77
+ export async function startSaveSizes(t, sessions, idempotencyKey = randomKey()) {
78
+ for (const s of sessions) {
79
+ if (s.kind !== "size") {
80
+ throw new TypeError("saveSizes saves size sessions only. Save a master session with its own save().");
81
+ }
82
+ }
83
+ const res = await t.send("PUT", "/api/v1/edit/sizes", {
84
+ json: { sizes: sessions.map((s) => ({ session: s.id, ifMatch: s.sceneHash })), idempotencyKey },
85
+ });
86
+ return progressOf(res.status, res.body);
87
+ }
88
+ export async function saveSizes(t, sessions, o = {}) {
89
+ const sent = sessions.map((s) => s.id);
90
+ const last = await finish(t, await startSaveSizes(t, sessions, o.idempotencyKey), o);
91
+ for (const r of last.results) {
92
+ const s = sessions[sent.indexOf(r.session)];
93
+ if (s && r.outcome === "saved" && r.sceneHash)
94
+ s.markSaved(r.sceneHash);
95
+ }
96
+ return { results: last.results };
97
+ }
@@ -0,0 +1,115 @@
1
+ import type { components } from "./v1.js";
2
+ type S = components["schemas"];
3
+ export type EditElement = S["EditElement"];
4
+ export type EditCandidate = S["EditCandidate"];
5
+ export type EditCheck = S["EditCheck"];
6
+ export type EditSizeResult = S["EditSizeResult"];
7
+ export type SaveSizeResult = S["SaveSizeResult"];
8
+ /** One edit: `name` is `set_frame`, `set_text`, `set_copy`, `swap_picture`, ... and `input` names the element by `ref` or `role`. */
9
+ export interface Edit {
10
+ name: string;
11
+ input?: Record<string, unknown>;
12
+ }
13
+ /**
14
+ * What an edit or a restore answers. A refusal is data, not an exception: `refused` is a code such as
15
+ * `ambiguous_role` (then `candidates` lists the elements), and is the empty string when the edit was made.
16
+ */
17
+ export interface EditAnswer {
18
+ elements: EditElement[];
19
+ candidates?: EditCandidate[];
20
+ fixes: string[];
21
+ carried: Record<string, string>;
22
+ check: EditCheck;
23
+ refused: string;
24
+ detail: string;
25
+ limit: Record<string, unknown> | null;
26
+ noChange: boolean;
27
+ /** The checkpoint a committed edit made (`e1`, `e2`, ...), the label restored, or empty. */
28
+ label: string;
29
+ }
30
+ export interface CheckAnswer {
31
+ check: EditCheck;
32
+ /** Only with `render`. */
33
+ standIns?: string[];
34
+ lossy?: boolean;
35
+ hash?: string;
36
+ current?: boolean;
37
+ }
38
+ export interface Preview {
39
+ png: Uint8Array;
40
+ width: number;
41
+ height: number;
42
+ check: EditCheck;
43
+ /** False when the session moved on while the picture was drawn, so it shows an earlier state. */
44
+ current: boolean;
45
+ standIns: string[];
46
+ lossy: boolean;
47
+ hash: string;
48
+ }
49
+ export interface Picture {
50
+ /** Send it as the `swap_picture` edit's `sha256`. */
51
+ sha256: string;
52
+ mime: "image/png" | "image/jpeg";
53
+ width: number;
54
+ height: number;
55
+ /** True when the picture is new; false when the session or run already held it. */
56
+ created: boolean;
57
+ }
58
+ export interface SizeSaveAnswer {
59
+ sceneHash: string;
60
+ fileId: string | null;
61
+ round: number | null;
62
+ publications: number;
63
+ pull: "ready" | "quiet" | "not_sent" | "superseded";
64
+ check: EditCheck;
65
+ label: string;
66
+ }
67
+ export interface MasterSaveAnswer {
68
+ version: number;
69
+ /** The version number to send as `If-Match` on the next save. The session keeps it for you. */
70
+ ifMatch: number;
71
+ /** True, with no new version, when the edits left the master as it was. */
72
+ unchanged: boolean;
73
+ status: "checking" | "ready" | "not_ready";
74
+ pull: string;
75
+ runsKept: number;
76
+ ruleSets: unknown[];
77
+ newRuleFailures: string[];
78
+ check: EditCheck;
79
+ label: string;
80
+ }
81
+ export interface MasterInfo {
82
+ id: string;
83
+ version: number;
84
+ latest: number;
85
+ name: string;
86
+ sourceApp: string;
87
+ brand: string;
88
+ canPull: boolean;
89
+ }
90
+ export interface FormatInfo {
91
+ key: string;
92
+ name: string;
93
+ width: number;
94
+ height: number;
95
+ unit: "px" | "mm";
96
+ }
97
+ /** The progress of a call across sizes. A call that finished at once is `done` with no `operation`. */
98
+ export interface SizesProgress<R> {
99
+ operation?: string;
100
+ /** Which call the operation is (`edit` or `save`); absent on a call that finished at once. */
101
+ kind?: "edit" | "save";
102
+ status: "running" | "done";
103
+ done: number;
104
+ total: number;
105
+ results: R[];
106
+ }
107
+ export interface WaitOptions<R> {
108
+ /** Milliseconds between reads of a running operation. Default 1000. */
109
+ pollIntervalMs?: number;
110
+ /** Give up waiting (not working) after this many milliseconds. Default 600000 (ten minutes). */
111
+ timeoutMs?: number;
112
+ /** Called with every snapshot, the first included. */
113
+ onProgress?: (progress: SizesProgress<R>) => void;
114
+ }
115
+ export {};
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};