@zweer/mage-space-client 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +182 -0
- package/dist/index.cjs +1284 -0
- package/dist/index.d.cts +800 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +800 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1267 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +86 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1284 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let jose = require("jose");
|
|
3
|
+
//#region lib/errors.ts
|
|
4
|
+
/** Typed error hierarchy for the mage.space client. */
|
|
5
|
+
/** Base class for every error thrown by this library. */
|
|
6
|
+
var MageSpaceError = class extends Error {
|
|
7
|
+
constructor(message, options) {
|
|
8
|
+
super(message, options);
|
|
9
|
+
this.name = "MageSpaceError";
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
/** Authentication failures (token refresh, session creation). */
|
|
13
|
+
var AuthError = class extends MageSpaceError {
|
|
14
|
+
constructor(message, options) {
|
|
15
|
+
super(message, options);
|
|
16
|
+
this.name = "AuthError";
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
/** Action-hash discovery failures (page/chunk fetch, empty registry). */
|
|
20
|
+
var DiscoveryError = class extends MageSpaceError {
|
|
21
|
+
constructor(message, options) {
|
|
22
|
+
super(message, options);
|
|
23
|
+
this.name = "DiscoveryError";
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
/** A Server Action returned 404, indicating a stale (post-deploy) hash. */
|
|
27
|
+
var StaleActionError = class extends MageSpaceError {
|
|
28
|
+
action;
|
|
29
|
+
constructor(action, options) {
|
|
30
|
+
super(`Action "${action}" returned 404 (stale hash)`, options);
|
|
31
|
+
this.action = action;
|
|
32
|
+
this.name = "StaleActionError";
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
/** Failures while submitting or polling a generation job. */
|
|
36
|
+
var GenerationError = class extends MageSpaceError {
|
|
37
|
+
code;
|
|
38
|
+
constructor(message, code, options) {
|
|
39
|
+
super(message, options);
|
|
40
|
+
this.code = code;
|
|
41
|
+
this.name = "GenerationError";
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
/** Failure to parse a React Server Component (RSC) wire response. */
|
|
45
|
+
var RscParseError = class extends MageSpaceError {
|
|
46
|
+
constructor(message, options) {
|
|
47
|
+
super(message, options);
|
|
48
|
+
this.name = "RscParseError";
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
/** A wait/poll operation exceeded its deadline or was aborted. */
|
|
52
|
+
var TimeoutError = class extends MageSpaceError {
|
|
53
|
+
constructor(message, options) {
|
|
54
|
+
super(message, options);
|
|
55
|
+
this.name = "TimeoutError";
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Guard for irreversible operations: throws unless `{ confirm: true }` is passed.
|
|
60
|
+
*
|
|
61
|
+
* @throws {MageSpaceError} when confirmation is missing.
|
|
62
|
+
*/
|
|
63
|
+
function requireConfirm(action, opts) {
|
|
64
|
+
if (opts?.confirm !== true) throw new MageSpaceError(`${action} is irreversible and requires explicit confirmation: pass { confirm: true }`);
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region lib/actions.ts
|
|
68
|
+
/**
|
|
69
|
+
* Action-hash discovery and caching.
|
|
70
|
+
*
|
|
71
|
+
* mage.space's Server Action hashes change on every Vercel deploy, so the client
|
|
72
|
+
* discovers them from the page's JS bundles and caches the result. The registry
|
|
73
|
+
* serves hashes from a warm seed / cache and only re-discovers when a hash goes
|
|
74
|
+
* stale (a request 404s) or when explicitly refreshed.
|
|
75
|
+
*/
|
|
76
|
+
/** `createServerReference)("<hash>", callServer, void 0, findSourceMapURL, "<fn>")` */
|
|
77
|
+
const CREATE_SERVER_REF_RE = /createServerReference\)\("([0-9a-f]+)"[^)]*?"([^"]+)"\)/g;
|
|
78
|
+
/** `/_next/static/chunks/....js` references in the page HTML. */
|
|
79
|
+
const CHUNK_RE = /\/_next\/static\/chunks\/[^"'`()\s]+?\.js/g;
|
|
80
|
+
/**
|
|
81
|
+
* Default pages scraped during discovery. Server Actions are split per route, so
|
|
82
|
+
* a single page misses route-specific actions (e.g. `deleteReference` lives only
|
|
83
|
+
* in the `/references` bundle) — scrape the main routes and union their chunks.
|
|
84
|
+
*/
|
|
85
|
+
const DISCOVERY_PAGES = [
|
|
86
|
+
"/explore",
|
|
87
|
+
"/creations",
|
|
88
|
+
"/characters",
|
|
89
|
+
"/references"
|
|
90
|
+
];
|
|
91
|
+
/** Default in-process snapshot cache. */
|
|
92
|
+
var InMemoryActionCache = class {
|
|
93
|
+
snapshot = null;
|
|
94
|
+
async load() {
|
|
95
|
+
return this.snapshot;
|
|
96
|
+
}
|
|
97
|
+
async save(snapshot) {
|
|
98
|
+
this.snapshot = snapshot;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Discover the current Server Action hashes by scraping the page HTML for JS
|
|
103
|
+
* chunk URLs and extracting `createServerReference(...)` calls from each chunk.
|
|
104
|
+
*
|
|
105
|
+
* @throws {DiscoveryError} on network failure or when nothing is discovered.
|
|
106
|
+
*/
|
|
107
|
+
async function discoverActions(opts) {
|
|
108
|
+
const { fetch, baseUrl } = opts;
|
|
109
|
+
const pages = opts.discoveryPath !== void 0 ? [opts.discoveryPath] : opts.discoveryPaths ?? DISCOVERY_PAGES;
|
|
110
|
+
let deploymentId = "";
|
|
111
|
+
const chunkPaths = /* @__PURE__ */ new Set();
|
|
112
|
+
for (const page of pages) {
|
|
113
|
+
let res;
|
|
114
|
+
try {
|
|
115
|
+
res = await fetch(`${baseUrl}${page}`, { headers: { accept: "text/html" } });
|
|
116
|
+
} catch {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (!res.ok) continue;
|
|
120
|
+
if (deploymentId === "") deploymentId = res.headers.get("x-deployment-id") ?? "";
|
|
121
|
+
const html = await res.text();
|
|
122
|
+
for (const match of html.matchAll(CHUNK_RE)) {
|
|
123
|
+
const chunkPath = match[0];
|
|
124
|
+
if (chunkPath !== void 0) chunkPaths.add(chunkPath);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (chunkPaths.size === 0) throw new DiscoveryError("No JS chunk references found in discovery pages");
|
|
128
|
+
const hashes = {};
|
|
129
|
+
await Promise.all([...chunkPaths].map(async (path) => {
|
|
130
|
+
try {
|
|
131
|
+
const chunkRes = await fetch(`${baseUrl}${path}`, { headers: { accept: "application/javascript" } });
|
|
132
|
+
if (!chunkRes.ok) return;
|
|
133
|
+
const js = await chunkRes.text();
|
|
134
|
+
for (const match of js.matchAll(CREATE_SERVER_REF_RE)) {
|
|
135
|
+
const hash = match[1];
|
|
136
|
+
const fn = match[2];
|
|
137
|
+
if (hash !== void 0 && fn !== void 0 && !(fn in hashes)) hashes[fn] = hash;
|
|
138
|
+
}
|
|
139
|
+
} catch {}
|
|
140
|
+
}));
|
|
141
|
+
if (Object.keys(hashes).length === 0) throw new DiscoveryError("No server action hashes discovered in JS chunks");
|
|
142
|
+
return {
|
|
143
|
+
deploymentId,
|
|
144
|
+
hashes,
|
|
145
|
+
discoveredAt: Date.now()
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Serves action hashes from a warm seed / pluggable cache, discovering lazily and
|
|
150
|
+
* refreshing on staleness. Concurrent refreshes are de-duplicated (single-flight).
|
|
151
|
+
*/
|
|
152
|
+
var ActionRegistry = class {
|
|
153
|
+
fetch;
|
|
154
|
+
baseUrl;
|
|
155
|
+
cache;
|
|
156
|
+
seed;
|
|
157
|
+
discoveryPath;
|
|
158
|
+
discoveryPaths;
|
|
159
|
+
maxAgeMs;
|
|
160
|
+
snapshot = null;
|
|
161
|
+
inflight = null;
|
|
162
|
+
constructor(opts) {
|
|
163
|
+
this.fetch = opts.fetch;
|
|
164
|
+
this.baseUrl = opts.baseUrl;
|
|
165
|
+
this.cache = opts.cache ?? new InMemoryActionCache();
|
|
166
|
+
this.seed = opts.seed;
|
|
167
|
+
this.discoveryPath = opts.discoveryPath;
|
|
168
|
+
this.discoveryPaths = opts.discoveryPaths;
|
|
169
|
+
this.maxAgeMs = opts.maxAgeMs ?? 0;
|
|
170
|
+
}
|
|
171
|
+
isStale(snapshot) {
|
|
172
|
+
return this.maxAgeMs > 0 && Date.now() - snapshot.discoveredAt > this.maxAgeMs;
|
|
173
|
+
}
|
|
174
|
+
/** Return the current snapshot, loading from cache/seed or discovering as needed. */
|
|
175
|
+
async getSnapshot() {
|
|
176
|
+
if (this.snapshot !== null && !this.isStale(this.snapshot)) return this.snapshot;
|
|
177
|
+
const cached = await this.cache.load();
|
|
178
|
+
if (cached !== null && !this.isStale(cached)) {
|
|
179
|
+
this.snapshot = cached;
|
|
180
|
+
return cached;
|
|
181
|
+
}
|
|
182
|
+
if (this.seed !== void 0) {
|
|
183
|
+
this.snapshot = this.seed;
|
|
184
|
+
await this.cache.save(this.seed);
|
|
185
|
+
return this.seed;
|
|
186
|
+
}
|
|
187
|
+
return this.refresh();
|
|
188
|
+
}
|
|
189
|
+
/** Force re-discovery. Concurrent callers share one in-flight discovery. */
|
|
190
|
+
async refresh() {
|
|
191
|
+
if (this.inflight !== null) return this.inflight;
|
|
192
|
+
this.inflight = (async () => {
|
|
193
|
+
try {
|
|
194
|
+
const fresh = await discoverActions({
|
|
195
|
+
fetch: this.fetch,
|
|
196
|
+
baseUrl: this.baseUrl,
|
|
197
|
+
discoveryPath: this.discoveryPath,
|
|
198
|
+
discoveryPaths: this.discoveryPaths
|
|
199
|
+
});
|
|
200
|
+
this.snapshot = fresh;
|
|
201
|
+
await this.cache.save(fresh);
|
|
202
|
+
return fresh;
|
|
203
|
+
} finally {
|
|
204
|
+
this.inflight = null;
|
|
205
|
+
}
|
|
206
|
+
})();
|
|
207
|
+
return this.inflight;
|
|
208
|
+
}
|
|
209
|
+
/** Resolve a function name to its current hash + deployment id. */
|
|
210
|
+
async getHash(action) {
|
|
211
|
+
let snapshot = await this.getSnapshot();
|
|
212
|
+
let hash = snapshot.hashes[action];
|
|
213
|
+
if (hash === void 0) {
|
|
214
|
+
snapshot = await this.refresh();
|
|
215
|
+
hash = snapshot.hashes[action];
|
|
216
|
+
}
|
|
217
|
+
if (hash === void 0) throw new DiscoveryError(`Action "${action}" not found after discovery`);
|
|
218
|
+
return {
|
|
219
|
+
hash,
|
|
220
|
+
deploymentId: snapshot.deploymentId
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/** Drop the cached snapshot and re-discover (called on a stale 404). */
|
|
224
|
+
async invalidate() {
|
|
225
|
+
this.snapshot = null;
|
|
226
|
+
return this.refresh();
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
//#endregion
|
|
230
|
+
//#region lib/rsc.ts
|
|
231
|
+
/**
|
|
232
|
+
* Minimal parser for the React Server Component (RSC) "Flight" wire format used
|
|
233
|
+
* by Next.js Server Action responses.
|
|
234
|
+
*
|
|
235
|
+
* The wire format is a sequence of newline-delimited rows:
|
|
236
|
+
*
|
|
237
|
+
* ```text
|
|
238
|
+
* 0:{"a":"$@1","f":"","b":"buildId"}
|
|
239
|
+
* 1:{"history_id":"e3357fb8-...","architecture_config":"$T0:0:architectureConfig"}
|
|
240
|
+
* ```
|
|
241
|
+
*
|
|
242
|
+
* Each row is `<hexId>:<jsonPayload>`. Values may contain sentinels:
|
|
243
|
+
* - `$undefined` → `undefined`
|
|
244
|
+
* - `$@<id>` → reference to another row (a resolved promise)
|
|
245
|
+
* - `$$...` → an escaped literal string starting with `$`
|
|
246
|
+
* - `$<other>` → a typed marker (kept verbatim, best-effort)
|
|
247
|
+
*
|
|
248
|
+
* @remarks
|
|
249
|
+
* The exact envelope for some read actions (e.g. `getHistoryById`) should be
|
|
250
|
+
* confirmed against live traffic via the rev-eng Playwright flow; this parser is
|
|
251
|
+
* intentionally tolerant and falls back to the last JSON row when the row-0
|
|
252
|
+
* `{ a: ... }` envelope is absent.
|
|
253
|
+
*/
|
|
254
|
+
const REF_RE = /^\$@([0-9a-f]+)$/i;
|
|
255
|
+
/** A plain row reference like `$5` (not a promise `$@5`, nor a `$D`/`$S`/... marker). */
|
|
256
|
+
const HEX_REF_RE = /^\$([0-9a-f]+)$/i;
|
|
257
|
+
const ROW_ID_RE = /^[0-9a-f]+$/i;
|
|
258
|
+
/** Return the char index after consuming exactly `byteLen` UTF-8 bytes from `start`. */
|
|
259
|
+
function endOfUtf8Slice(text, start, byteLen) {
|
|
260
|
+
let bytes = 0;
|
|
261
|
+
let i = start;
|
|
262
|
+
while (i < text.length && bytes < byteLen) {
|
|
263
|
+
const cp = text.codePointAt(i);
|
|
264
|
+
bytes += cp <= 127 ? 1 : cp <= 2047 ? 2 : cp <= 65535 ? 3 : 4;
|
|
265
|
+
i += cp > 65535 ? 2 : 1;
|
|
266
|
+
}
|
|
267
|
+
return i;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Parse the raw Flight text into a map of `rowId → value`.
|
|
271
|
+
*
|
|
272
|
+
* @remarks
|
|
273
|
+
* Most rows are `id:<json>\n`, but text rows are length-delimited:
|
|
274
|
+
* `id:T<hexlen>,<hexlen UTF-8 bytes>` and are NOT newline-terminated — the next
|
|
275
|
+
* row begins immediately after the counted bytes. A naive line split therefore
|
|
276
|
+
* glues the following data row onto a multi-line prompt (which is exactly why
|
|
277
|
+
* large `getHistoryPaginated` responses failed to parse). This parser is
|
|
278
|
+
* position/length aware so length-delimited rows are consumed exactly and every
|
|
279
|
+
* subsequent row stays isolated.
|
|
280
|
+
*/
|
|
281
|
+
function parseFlightRows(text) {
|
|
282
|
+
const rows = /* @__PURE__ */ new Map();
|
|
283
|
+
const n = text.length;
|
|
284
|
+
let pos = 0;
|
|
285
|
+
while (pos < n) {
|
|
286
|
+
if (text[pos] === "\n") {
|
|
287
|
+
pos += 1;
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
const colon = text.indexOf(":", pos);
|
|
291
|
+
if (colon === -1) break;
|
|
292
|
+
const id = text.slice(pos, colon);
|
|
293
|
+
if (!ROW_ID_RE.test(id)) {
|
|
294
|
+
const nl = text.indexOf("\n", pos);
|
|
295
|
+
if (nl === -1) break;
|
|
296
|
+
pos = nl + 1;
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
if (text[colon + 1] === "T") {
|
|
300
|
+
const comma = text.indexOf(",", colon + 2);
|
|
301
|
+
if (comma === -1) break;
|
|
302
|
+
const byteLen = Number.parseInt(text.slice(colon + 2, comma), 16);
|
|
303
|
+
const start = comma + 1;
|
|
304
|
+
const end = endOfUtf8Slice(text, start, byteLen);
|
|
305
|
+
rows.set(id, text.slice(start, end));
|
|
306
|
+
pos = end;
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
const nl = text.indexOf("\n", colon + 1);
|
|
310
|
+
const payload = nl === -1 ? text.slice(colon + 1) : text.slice(colon + 1, nl);
|
|
311
|
+
pos = nl === -1 ? n : nl + 1;
|
|
312
|
+
const first = payload[0];
|
|
313
|
+
if (first === void 0) continue;
|
|
314
|
+
if (!(first === "{" || first === "[" || first === "\"" || first === "-" || first >= "0" && first <= "9" || payload === "true" || payload === "false" || payload === "null")) continue;
|
|
315
|
+
try {
|
|
316
|
+
rows.set(id, JSON.parse(payload));
|
|
317
|
+
} catch {}
|
|
318
|
+
}
|
|
319
|
+
return rows;
|
|
320
|
+
}
|
|
321
|
+
/** Recursively resolve Flight sentinels/references within a parsed value. */
|
|
322
|
+
function resolveFlightValue(value, rows, seen = /* @__PURE__ */ new Set()) {
|
|
323
|
+
if (typeof value === "string") {
|
|
324
|
+
if (value === "$undefined") return;
|
|
325
|
+
const ref = REF_RE.exec(value) ?? HEX_REF_RE.exec(value);
|
|
326
|
+
if (ref) {
|
|
327
|
+
const refId = ref[1];
|
|
328
|
+
if (refId === void 0 || seen.has(refId)) return;
|
|
329
|
+
seen.add(refId);
|
|
330
|
+
const target = rows.get(refId);
|
|
331
|
+
return target === void 0 ? void 0 : resolveFlightValue(target, rows, seen);
|
|
332
|
+
}
|
|
333
|
+
if (value.startsWith("$$")) return value.slice(1);
|
|
334
|
+
return value;
|
|
335
|
+
}
|
|
336
|
+
if (Array.isArray(value)) return value.map((entry) => resolveFlightValue(entry, rows, seen));
|
|
337
|
+
if (value !== null && typeof value === "object") {
|
|
338
|
+
const out = {};
|
|
339
|
+
for (const [key, entry] of Object.entries(value)) out[key] = resolveFlightValue(entry, rows, seen);
|
|
340
|
+
return out;
|
|
341
|
+
}
|
|
342
|
+
return value;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Parse a Server Action response and return the action's resolved return value.
|
|
346
|
+
*
|
|
347
|
+
* @throws {RscParseError} when the response contains no parseable rows.
|
|
348
|
+
*/
|
|
349
|
+
function parseServerActionResponse(text) {
|
|
350
|
+
const rows = parseFlightRows(text);
|
|
351
|
+
if (rows.size === 0) throw new RscParseError("Empty or unparseable RSC response");
|
|
352
|
+
const root = rows.get("0");
|
|
353
|
+
if (root !== null && typeof root === "object" && !Array.isArray(root) && "a" in root) return resolveFlightValue(root.a, rows);
|
|
354
|
+
let lastId = null;
|
|
355
|
+
for (const id of rows.keys()) if (lastId === null || Number.parseInt(id, 16) > Number.parseInt(lastId, 16)) lastId = id;
|
|
356
|
+
return resolveFlightValue(lastId === null ? void 0 : rows.get(lastId), rows);
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Unwrap a single-object result that some Server Actions (notably the
|
|
360
|
+
* `*Parallel` family) wrap in a one-element array.
|
|
361
|
+
*
|
|
362
|
+
* @remarks
|
|
363
|
+
* `getCreationsPaginatedParallel` and `getMentionSuggestionsParallel` return their
|
|
364
|
+
* payload as `[value]` on the RSC wire (`1:["$@2"]`), whereas most actions return
|
|
365
|
+
* the value directly. This normalizes object-returning actions to the value; it is
|
|
366
|
+
* a no-op when the response is already the object. Do NOT use it for actions whose
|
|
367
|
+
* genuine return type is an array (e.g. `getReferences`).
|
|
368
|
+
*/
|
|
369
|
+
function unwrapResult(value) {
|
|
370
|
+
return Array.isArray(value) && value.length === 1 ? value[0] : value;
|
|
371
|
+
}
|
|
372
|
+
//#endregion
|
|
373
|
+
//#region lib/characters.ts
|
|
374
|
+
/** Character actions are served from the `/explore` page. */
|
|
375
|
+
const CHARACTERS_PATH = "/explore";
|
|
376
|
+
const DEFAULT_SEARCH_LIMIT = 10;
|
|
377
|
+
var CharactersService = class {
|
|
378
|
+
http;
|
|
379
|
+
auth;
|
|
380
|
+
constructor(http, auth) {
|
|
381
|
+
this.http = http;
|
|
382
|
+
this.auth = auth;
|
|
383
|
+
}
|
|
384
|
+
/** Search the caller's characters (and optionally public references) by name. */
|
|
385
|
+
async search(opts) {
|
|
386
|
+
const [uid, session] = await Promise.all([this.auth.getUserId(), this.auth.getSession()]);
|
|
387
|
+
const body = {
|
|
388
|
+
query: opts.query,
|
|
389
|
+
uid,
|
|
390
|
+
limit: opts.limit ?? DEFAULT_SEARCH_LIMIT,
|
|
391
|
+
includePublicReferences: opts.includePublicReferences ?? false,
|
|
392
|
+
referenceModalities: opts.referenceModalities ?? ["image", "audio"]
|
|
393
|
+
};
|
|
394
|
+
const { data } = await this.http.callAction({
|
|
395
|
+
action: "getMentionSuggestionsParallel",
|
|
396
|
+
path: CHARACTERS_PATH,
|
|
397
|
+
args: [body],
|
|
398
|
+
session
|
|
399
|
+
});
|
|
400
|
+
return unwrapResult(data);
|
|
401
|
+
}
|
|
402
|
+
/** Create a new character from an already-uploaded image URL. */
|
|
403
|
+
async create(input) {
|
|
404
|
+
const session = await this.auth.getSession();
|
|
405
|
+
const body = {
|
|
406
|
+
name: input.name,
|
|
407
|
+
username: input.username,
|
|
408
|
+
image_url: input.image_url,
|
|
409
|
+
audio_url: input.audio_url ?? null,
|
|
410
|
+
description: input.description ?? null,
|
|
411
|
+
tags: input.tags ?? [],
|
|
412
|
+
visibility: input.visibility ?? "private",
|
|
413
|
+
moderation: input.moderation ?? []
|
|
414
|
+
};
|
|
415
|
+
const { data } = await this.http.callAction({
|
|
416
|
+
action: "createCharacter",
|
|
417
|
+
path: CHARACTERS_PATH,
|
|
418
|
+
args: [body],
|
|
419
|
+
session
|
|
420
|
+
});
|
|
421
|
+
return data;
|
|
422
|
+
}
|
|
423
|
+
/** Update a character's mutable fields (name, description, tags, visibility, voice). */
|
|
424
|
+
async update(id, patch) {
|
|
425
|
+
const session = await this.auth.getSession();
|
|
426
|
+
const { data } = await this.http.callAction({
|
|
427
|
+
action: "updateCharacter",
|
|
428
|
+
path: CHARACTERS_PATH,
|
|
429
|
+
args: [id, patch],
|
|
430
|
+
session
|
|
431
|
+
});
|
|
432
|
+
return data;
|
|
433
|
+
}
|
|
434
|
+
/** Soft-delete a character. */
|
|
435
|
+
async delete(id) {
|
|
436
|
+
const session = await this.auth.getSession();
|
|
437
|
+
await this.http.callAction({
|
|
438
|
+
action: "deleteCharacter",
|
|
439
|
+
path: CHARACTERS_PATH,
|
|
440
|
+
args: [id],
|
|
441
|
+
session
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
/** Fetch a single character by id; resolves to `null` when it does not exist. */
|
|
445
|
+
async get(id) {
|
|
446
|
+
const session = await this.auth.getSession();
|
|
447
|
+
const { data } = await this.http.callAction({
|
|
448
|
+
action: "getCharacter",
|
|
449
|
+
path: CHARACTERS_PATH,
|
|
450
|
+
args: [id],
|
|
451
|
+
session
|
|
452
|
+
});
|
|
453
|
+
return data ?? null;
|
|
454
|
+
}
|
|
455
|
+
/** Fetch multiple characters by id in a single call. */
|
|
456
|
+
async getMany(ids) {
|
|
457
|
+
const session = await this.auth.getSession();
|
|
458
|
+
const { data } = await this.http.callAction({
|
|
459
|
+
action: "getCharacters",
|
|
460
|
+
path: CHARACTERS_PATH,
|
|
461
|
+
args: [ids],
|
|
462
|
+
session
|
|
463
|
+
});
|
|
464
|
+
return data ?? [];
|
|
465
|
+
}
|
|
466
|
+
/** Make a character public. */
|
|
467
|
+
async publish(id) {
|
|
468
|
+
const session = await this.auth.getSession();
|
|
469
|
+
await this.http.callAction({
|
|
470
|
+
action: "publishCharacter",
|
|
471
|
+
path: CHARACTERS_PATH,
|
|
472
|
+
args: [id],
|
|
473
|
+
session
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
/** Make a character private. */
|
|
477
|
+
async unpublish(id) {
|
|
478
|
+
const session = await this.auth.getSession();
|
|
479
|
+
await this.http.callAction({
|
|
480
|
+
action: "unpublishCharacter",
|
|
481
|
+
path: CHARACTERS_PATH,
|
|
482
|
+
args: [id],
|
|
483
|
+
session
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Upload a character reference image (base64 data URL) and return its CDN URL.
|
|
488
|
+
*
|
|
489
|
+
* @param dataUrl - `data:image/jpeg;base64,...` (JPEG preferred; PNG/WebP ok).
|
|
490
|
+
*/
|
|
491
|
+
async uploadImage(dataUrl) {
|
|
492
|
+
const [uid, session] = await Promise.all([this.auth.getUserId(), this.auth.getSession()]);
|
|
493
|
+
const { data } = await this.http.callAction({
|
|
494
|
+
action: "uploadCharacterImage",
|
|
495
|
+
path: CHARACTERS_PATH,
|
|
496
|
+
args: [dataUrl, uid],
|
|
497
|
+
session
|
|
498
|
+
});
|
|
499
|
+
return data;
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Browse characters with pagination.
|
|
503
|
+
*
|
|
504
|
+
* @remarks
|
|
505
|
+
* Omit `uid` to browse the public/featured feed; pass it to page through a
|
|
506
|
+
* single user's characters.
|
|
507
|
+
*/
|
|
508
|
+
async listPaginated(opts = {}) {
|
|
509
|
+
const session = await this.auth.getSession();
|
|
510
|
+
const limit = opts.limit ?? 100;
|
|
511
|
+
const offset = opts.offset ?? 0;
|
|
512
|
+
const filters = {};
|
|
513
|
+
if (opts.uid !== void 0) filters.uid = opts.uid;
|
|
514
|
+
if (opts.visibility !== void 0) filters.visibility = opts.visibility;
|
|
515
|
+
if (opts.modality !== void 0) filters.modality = opts.modality;
|
|
516
|
+
if (opts.featured !== void 0) filters.featured = opts.featured;
|
|
517
|
+
const { data } = await this.http.callAction({
|
|
518
|
+
action: "getCharactersPaginated",
|
|
519
|
+
path: CHARACTERS_PATH,
|
|
520
|
+
args: [
|
|
521
|
+
limit,
|
|
522
|
+
offset,
|
|
523
|
+
filters
|
|
524
|
+
],
|
|
525
|
+
session
|
|
526
|
+
});
|
|
527
|
+
return data;
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Generate a voice for a character from its reference image.
|
|
531
|
+
*
|
|
532
|
+
* @remarks Costs gems (not covered by the unlimited tier).
|
|
533
|
+
* @experimental Request body inferred from JS bundle; not yet verified live.
|
|
534
|
+
*/
|
|
535
|
+
async generateVoice(opts) {
|
|
536
|
+
const [authToken, session] = await Promise.all([this.auth.getIdToken(), this.auth.getSession()]);
|
|
537
|
+
const { data } = await this.http.callAction({
|
|
538
|
+
action: "generateCharacterVoice",
|
|
539
|
+
path: CHARACTERS_PATH,
|
|
540
|
+
args: [{
|
|
541
|
+
imageUrl: opts.imageUrl,
|
|
542
|
+
guidancePrompt: opts.guidancePrompt ?? ""
|
|
543
|
+
}, authToken],
|
|
544
|
+
session
|
|
545
|
+
});
|
|
546
|
+
return data;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Follow a (public) character.
|
|
550
|
+
*
|
|
551
|
+
* @experimental Request body inferred from JS bundle; not yet verified live.
|
|
552
|
+
*/
|
|
553
|
+
async follow(id) {
|
|
554
|
+
const session = await this.auth.getSession();
|
|
555
|
+
await this.http.callAction({
|
|
556
|
+
action: "followCharacter",
|
|
557
|
+
path: CHARACTERS_PATH,
|
|
558
|
+
args: [id],
|
|
559
|
+
session
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Unfollow a character.
|
|
564
|
+
*
|
|
565
|
+
* @experimental Request body inferred from JS bundle; not yet verified live.
|
|
566
|
+
*/
|
|
567
|
+
async unfollow(id) {
|
|
568
|
+
const session = await this.auth.getSession();
|
|
569
|
+
await this.http.callAction({
|
|
570
|
+
action: "unfollowCharacter",
|
|
571
|
+
path: CHARACTERS_PATH,
|
|
572
|
+
args: [id],
|
|
573
|
+
session
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
//#endregion
|
|
578
|
+
//#region lib/auth.ts
|
|
579
|
+
/**
|
|
580
|
+
* Firebase authentication: refresh token → ID token → session cookie.
|
|
581
|
+
*
|
|
582
|
+
* The refresh token is the only long-lived credential the caller supplies.
|
|
583
|
+
* From it we mint a Firebase ID token (~1h) via the Secure Token REST API, and
|
|
584
|
+
* exchange that for a `__session` cookie (~24h) via the `createUserSession`
|
|
585
|
+
* Server Action. Both are cached and refreshed 5 minutes before expiry.
|
|
586
|
+
*/
|
|
587
|
+
const DEFAULT_FIREBASE_API_KEY = "AIzaSyAzUV2NNUOlLTL04jwmUw9oLhjteuv6Qr4";
|
|
588
|
+
const SECURE_TOKEN_URL = "https://securetoken.googleapis.com/v1/token";
|
|
589
|
+
const EXPIRY_BUFFER_MS = 3e5;
|
|
590
|
+
const SESSION_DEFAULT_TTL_MS = 864e5;
|
|
591
|
+
const ID_TOKEN_DEFAULT_TTL_S = 3600;
|
|
592
|
+
var AuthManager = class {
|
|
593
|
+
refreshToken;
|
|
594
|
+
http;
|
|
595
|
+
fetchImpl;
|
|
596
|
+
apiKey;
|
|
597
|
+
sessionPath;
|
|
598
|
+
tokens = null;
|
|
599
|
+
session = null;
|
|
600
|
+
constructor(opts) {
|
|
601
|
+
this.refreshToken = opts.refreshToken;
|
|
602
|
+
this.http = opts.http;
|
|
603
|
+
this.fetchImpl = opts.fetch;
|
|
604
|
+
this.apiKey = opts.firebaseApiKey ?? DEFAULT_FIREBASE_API_KEY;
|
|
605
|
+
this.sessionPath = opts.sessionPath ?? "/creations";
|
|
606
|
+
}
|
|
607
|
+
/** Return a valid Firebase ID token, refreshing via the refresh token if needed. */
|
|
608
|
+
async getIdToken() {
|
|
609
|
+
if (this.tokens !== null && Date.now() < this.tokens.expiresAt - EXPIRY_BUFFER_MS) return this.tokens.idToken;
|
|
610
|
+
return (await this.refreshTokens()).idToken;
|
|
611
|
+
}
|
|
612
|
+
/** Return the Firebase uid (fetching an ID token first if necessary). */
|
|
613
|
+
async getUserId() {
|
|
614
|
+
if (this.tokens !== null) return this.tokens.userId;
|
|
615
|
+
return (await this.refreshTokens()).userId;
|
|
616
|
+
}
|
|
617
|
+
/** Return a valid `__session` cookie, creating one via `createUserSession` if needed. */
|
|
618
|
+
async getSession() {
|
|
619
|
+
if (this.session !== null && Date.now() < this.session.expiresAt - EXPIRY_BUFFER_MS) return this.session.cookie;
|
|
620
|
+
const idToken = await this.getIdToken();
|
|
621
|
+
const cookie = extractSessionCookie((await this.http.callAction({
|
|
622
|
+
action: "createUserSession",
|
|
623
|
+
path: this.sessionPath,
|
|
624
|
+
args: [idToken]
|
|
625
|
+
})).headers);
|
|
626
|
+
if (cookie === null) throw new AuthError("createUserSession did not return a __session cookie");
|
|
627
|
+
const expiresAt = jwtExpiryMs(cookie) ?? Date.now() + SESSION_DEFAULT_TTL_MS;
|
|
628
|
+
this.session = {
|
|
629
|
+
cookie,
|
|
630
|
+
expiresAt
|
|
631
|
+
};
|
|
632
|
+
return cookie;
|
|
633
|
+
}
|
|
634
|
+
async refreshTokens() {
|
|
635
|
+
const body = new URLSearchParams({
|
|
636
|
+
grant_type: "refresh_token",
|
|
637
|
+
refresh_token: this.refreshToken
|
|
638
|
+
});
|
|
639
|
+
const res = await this.fetchImpl(`${SECURE_TOKEN_URL}?key=${this.apiKey}`, {
|
|
640
|
+
method: "POST",
|
|
641
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
642
|
+
body: body.toString()
|
|
643
|
+
});
|
|
644
|
+
if (!res.ok) throw new AuthError(`Token refresh failed: HTTP ${res.status} (refresh token may be invalid or revoked)`);
|
|
645
|
+
const json = await res.json();
|
|
646
|
+
if (json.id_token === void 0 || json.user_id === void 0) throw new AuthError("Token refresh response missing id_token/user_id");
|
|
647
|
+
if (json.refresh_token !== void 0) this.refreshToken = json.refresh_token;
|
|
648
|
+
const ttlS = json.expires_in === void 0 ? ID_TOKEN_DEFAULT_TTL_S : Number(json.expires_in);
|
|
649
|
+
const expiresAt = jwtExpiryMs(json.id_token) ?? Date.now() + ttlS * 1e3;
|
|
650
|
+
this.tokens = {
|
|
651
|
+
idToken: json.id_token,
|
|
652
|
+
refreshToken: this.refreshToken,
|
|
653
|
+
userId: json.user_id,
|
|
654
|
+
expiresAt
|
|
655
|
+
};
|
|
656
|
+
return this.tokens;
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
/** Read the `exp` claim (ms) from a JWT, or `null` if it cannot be decoded. */
|
|
660
|
+
function jwtExpiryMs(jwt) {
|
|
661
|
+
try {
|
|
662
|
+
const { exp } = (0, jose.decodeJwt)(jwt);
|
|
663
|
+
return typeof exp === "number" ? exp * 1e3 : null;
|
|
664
|
+
} catch {
|
|
665
|
+
return null;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
/** Extract the `__session` value from a `Set-Cookie` response header. */
|
|
669
|
+
function extractSessionCookie(headers) {
|
|
670
|
+
const withGetSetCookie = headers;
|
|
671
|
+
const list = typeof withGetSetCookie.getSetCookie === "function" ? withGetSetCookie.getSetCookie() : headers.get("set-cookie") !== null ? [headers.get("set-cookie")] : [];
|
|
672
|
+
for (const cookie of list) {
|
|
673
|
+
const match = /(?:^|;\s*)__session=([^;]+)/.exec(cookie);
|
|
674
|
+
if (match !== null && match[1] !== void 0) return match[1];
|
|
675
|
+
}
|
|
676
|
+
return null;
|
|
677
|
+
}
|
|
678
|
+
//#endregion
|
|
679
|
+
//#region lib/generation.ts
|
|
680
|
+
/** Generation actions are served from the `/explore` page. */
|
|
681
|
+
const GENERATION_PATH = "/explore";
|
|
682
|
+
/** RSC sentinel for an absent value. */
|
|
683
|
+
const UNDEFINED$1 = "$undefined";
|
|
684
|
+
/** Default concept id for mango-v3-pro (from docs — verify per model via rev-eng). */
|
|
685
|
+
const DEFAULT_CONCEPT_ID = "0cd8c7ed2e554d0f98f20c8cf8c0f7c";
|
|
686
|
+
/** Default concept id for berry-2 video (from docs — verify per model via rev-eng). */
|
|
687
|
+
const DEFAULT_BERRY_CONCEPT_ID = "0b1fa74f7347406392422d24f2885684";
|
|
688
|
+
const DEFAULT_POLL_INTERVAL_MS = 4e3;
|
|
689
|
+
const DEFAULT_TIMEOUT_MS = 3e5;
|
|
690
|
+
var GenerationService = class {
|
|
691
|
+
http;
|
|
692
|
+
auth;
|
|
693
|
+
constructor(http, auth) {
|
|
694
|
+
this.http = http;
|
|
695
|
+
this.auth = auth;
|
|
696
|
+
}
|
|
697
|
+
/** Submit an image generation job and return its `historyId`. */
|
|
698
|
+
async submit(opts) {
|
|
699
|
+
const config = {
|
|
700
|
+
seed: opts.seed ?? null,
|
|
701
|
+
prompt: opts.prompt,
|
|
702
|
+
model_id: opts.model ?? "mango-v3-pro",
|
|
703
|
+
fast_mode: opts.fastMode ?? false,
|
|
704
|
+
resolution: opts.resolution ?? "1K",
|
|
705
|
+
architecture: opts.architecture ?? "mango",
|
|
706
|
+
aspect_ratio: opts.aspectRatio ?? "portrait",
|
|
707
|
+
image: opts.image ?? UNDEFINED$1,
|
|
708
|
+
additional_images: opts.additionalImages ?? [],
|
|
709
|
+
characters: mapCharacters(opts.characters),
|
|
710
|
+
references: mapCharacters(opts.references),
|
|
711
|
+
audio_references: [],
|
|
712
|
+
moodboard: UNDEFINED$1
|
|
713
|
+
};
|
|
714
|
+
return this.runArchitecture(config, opts.conceptId ?? DEFAULT_CONCEPT_ID, opts.generationMode);
|
|
715
|
+
}
|
|
716
|
+
/** Submit a video generation job (Berry-2 family) and return its `historyId`. */
|
|
717
|
+
async submitVideo(opts) {
|
|
718
|
+
const config = {
|
|
719
|
+
seed: opts.seed ?? null,
|
|
720
|
+
prompt: opts.prompt,
|
|
721
|
+
model_id: opts.model ?? "berry-2",
|
|
722
|
+
fast_mode: opts.fastMode ?? false,
|
|
723
|
+
resolution: opts.resolution ?? "480p",
|
|
724
|
+
architecture: opts.architecture ?? "berry",
|
|
725
|
+
berry_aspect_ratio: opts.aspectRatio ?? "9:16",
|
|
726
|
+
duration: opts.duration ?? "3",
|
|
727
|
+
image: opts.image ?? UNDEFINED$1,
|
|
728
|
+
additional_images: opts.additionalImages ?? [],
|
|
729
|
+
characters: mapCharacters(opts.characters),
|
|
730
|
+
references: mapCharacters(opts.references),
|
|
731
|
+
audio_references: []
|
|
732
|
+
};
|
|
733
|
+
return this.runArchitecture(config, opts.conceptId ?? DEFAULT_BERRY_CONCEPT_ID, opts.generationMode);
|
|
734
|
+
}
|
|
735
|
+
/** Shared `runArchitecture` call for image and video configs. */
|
|
736
|
+
async runArchitecture(config, conceptId, generationMode = "unlimited") {
|
|
737
|
+
const [authToken, session] = await Promise.all([this.auth.getIdToken(), this.auth.getSession()]);
|
|
738
|
+
const args = [{
|
|
739
|
+
architectureConfig: config,
|
|
740
|
+
architectureConfigToSave: "$0:0:architectureConfig",
|
|
741
|
+
authToken,
|
|
742
|
+
conceptId,
|
|
743
|
+
activePowerPack: null,
|
|
744
|
+
generationMode
|
|
745
|
+
}];
|
|
746
|
+
const { data } = await this.http.callAction({
|
|
747
|
+
action: "runArchitecture",
|
|
748
|
+
path: GENERATION_PATH,
|
|
749
|
+
args,
|
|
750
|
+
session
|
|
751
|
+
});
|
|
752
|
+
if (data !== null && typeof data === "object" && "error_code" in data) {
|
|
753
|
+
const code = Number(data.error_code);
|
|
754
|
+
throw new GenerationError(errorMessageForCode(code), code);
|
|
755
|
+
}
|
|
756
|
+
const historyId = data !== null && typeof data === "object" ? data.history_id : void 0;
|
|
757
|
+
if (typeof historyId !== "string") throw new GenerationError("runArchitecture did not return a history_id");
|
|
758
|
+
return { historyId };
|
|
759
|
+
}
|
|
760
|
+
/** Fetch a single history record by id. */
|
|
761
|
+
async getHistory(historyId) {
|
|
762
|
+
const session = await this.auth.getSession();
|
|
763
|
+
const { data } = await this.http.callAction({
|
|
764
|
+
action: "getHistoryById",
|
|
765
|
+
path: GENERATION_PATH,
|
|
766
|
+
args: [historyId],
|
|
767
|
+
session
|
|
768
|
+
});
|
|
769
|
+
return data;
|
|
770
|
+
}
|
|
771
|
+
/** Cancel a running generation job. */
|
|
772
|
+
async cancelJob(historyId) {
|
|
773
|
+
const session = await this.auth.getSession();
|
|
774
|
+
await this.http.callAction({
|
|
775
|
+
action: "cancelArchitectureJob",
|
|
776
|
+
path: GENERATION_PATH,
|
|
777
|
+
args: [historyId],
|
|
778
|
+
session
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
/** Poll until the job leaves the `running` state, returning the final record. */
|
|
782
|
+
async waitForResult(historyId, opts = {}) {
|
|
783
|
+
const intervalMs = opts.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
784
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
785
|
+
const deadline = Date.now() + timeoutMs;
|
|
786
|
+
for (;;) {
|
|
787
|
+
if (opts.signal?.aborted === true) throw new TimeoutError("waitForResult was aborted");
|
|
788
|
+
const item = await this.getHistory(historyId);
|
|
789
|
+
if (item.status === "success") return item;
|
|
790
|
+
if (item.status === "error") throw new GenerationError(`Generation failed: ${JSON.stringify(item.error ?? null)}`);
|
|
791
|
+
if (Date.now() > deadline) throw new TimeoutError(`Generation timed out after ${timeoutMs}ms (last status: ${item.status})`);
|
|
792
|
+
await delay(intervalMs, opts.signal);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
function delay(ms, signal) {
|
|
797
|
+
return new Promise((resolve, reject) => {
|
|
798
|
+
const timer = setTimeout(resolve, ms);
|
|
799
|
+
signal?.addEventListener("abort", () => {
|
|
800
|
+
clearTimeout(timer);
|
|
801
|
+
reject(new TimeoutError("waitForResult was aborted"));
|
|
802
|
+
}, { once: true });
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
/** Normalize characters for an `architectureConfig`, defaulting absent audio to the RSC sentinel. */
|
|
806
|
+
function mapCharacters(characters = []) {
|
|
807
|
+
return characters.map((c) => ({
|
|
808
|
+
id: c.id,
|
|
809
|
+
name: c.name,
|
|
810
|
+
username: c.username,
|
|
811
|
+
image_url: c.image_url,
|
|
812
|
+
audio_url: c.audio_url ?? UNDEFINED$1
|
|
813
|
+
}));
|
|
814
|
+
}
|
|
815
|
+
function errorMessageForCode(code) {
|
|
816
|
+
switch (code) {
|
|
817
|
+
case 400: return "Bad request (wrong generationMode for your subscription tier?)";
|
|
818
|
+
case 401: return "Authentication failed (token expired or invalid)";
|
|
819
|
+
case 403: return "User is banned";
|
|
820
|
+
case 4031: return "Prompt blocked by content moderation";
|
|
821
|
+
default: return `Generation error (code ${code})`;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
//#endregion
|
|
825
|
+
//#region lib/history.ts
|
|
826
|
+
/** History actions are served from the `/creations` page. */
|
|
827
|
+
const HISTORY_PATH = "/creations";
|
|
828
|
+
const DEFAULT_LIMIT = 10;
|
|
829
|
+
/** RSC sentinel for an absent value. */
|
|
830
|
+
const UNDEFINED = "$undefined";
|
|
831
|
+
var HistoryService = class {
|
|
832
|
+
http;
|
|
833
|
+
auth;
|
|
834
|
+
constructor(http, auth) {
|
|
835
|
+
this.http = http;
|
|
836
|
+
this.auth = auth;
|
|
837
|
+
}
|
|
838
|
+
/** List generation history for the authenticated user. */
|
|
839
|
+
async list(opts = {}) {
|
|
840
|
+
const [uid, session] = await Promise.all([this.auth.getUserId(), this.auth.getSession()]);
|
|
841
|
+
const limit = opts.limit ?? DEFAULT_LIMIT;
|
|
842
|
+
const offset = opts.offset ?? 0;
|
|
843
|
+
const filters = {
|
|
844
|
+
status: opts.status === "all" ? UNDEFINED : opts.status ?? "success",
|
|
845
|
+
type: UNDEFINED
|
|
846
|
+
};
|
|
847
|
+
const { data } = await this.http.callAction({
|
|
848
|
+
action: "getHistoryPaginated",
|
|
849
|
+
path: HISTORY_PATH,
|
|
850
|
+
args: [
|
|
851
|
+
uid,
|
|
852
|
+
limit,
|
|
853
|
+
offset,
|
|
854
|
+
filters
|
|
855
|
+
],
|
|
856
|
+
session
|
|
857
|
+
});
|
|
858
|
+
return unwrapResult(data);
|
|
859
|
+
}
|
|
860
|
+
/** List the user's permanently saved creations. */
|
|
861
|
+
async listCreations(opts = {}) {
|
|
862
|
+
const [uid, session] = await Promise.all([this.auth.getUserId(), this.auth.getSession()]);
|
|
863
|
+
const limit = opts.limit ?? DEFAULT_LIMIT;
|
|
864
|
+
const offset = opts.offset ?? 0;
|
|
865
|
+
const { data } = await this.http.callAction({
|
|
866
|
+
action: "getCreationsPaginatedParallel",
|
|
867
|
+
path: HISTORY_PATH,
|
|
868
|
+
args: [
|
|
869
|
+
uid,
|
|
870
|
+
limit,
|
|
871
|
+
offset
|
|
872
|
+
],
|
|
873
|
+
session
|
|
874
|
+
});
|
|
875
|
+
return unwrapResult(data);
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Save a completed generation result as a permanent creation.
|
|
879
|
+
*
|
|
880
|
+
* @param result - the `result` object of a completed history item.
|
|
881
|
+
* @remarks The API returns no body; call {@link listCreations} to retrieve the
|
|
882
|
+
* saved creation.
|
|
883
|
+
*/
|
|
884
|
+
async saveCreation(result) {
|
|
885
|
+
const session = await this.auth.getSession();
|
|
886
|
+
await this.http.callAction({
|
|
887
|
+
action: "saveCreation",
|
|
888
|
+
path: HISTORY_PATH,
|
|
889
|
+
args: [result],
|
|
890
|
+
session
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Permanently delete a history item. Irreversible — requires `{ confirm: true }`.
|
|
895
|
+
*/
|
|
896
|
+
async deleteHistory(historyId, opts) {
|
|
897
|
+
requireConfirm("deleteHistory", opts);
|
|
898
|
+
const session = await this.auth.getSession();
|
|
899
|
+
await this.http.callAction({
|
|
900
|
+
action: "deleteHistory",
|
|
901
|
+
path: HISTORY_PATH,
|
|
902
|
+
args: [historyId],
|
|
903
|
+
session
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* Permanently delete many history items. Irreversible — requires `{ confirm: true }`.
|
|
908
|
+
*
|
|
909
|
+
* @experimental Request body inferred from JS bundle; not yet verified live.
|
|
910
|
+
*/
|
|
911
|
+
async deleteManyHistories(historyIds, opts) {
|
|
912
|
+
requireConfirm("deleteManyHistories", opts);
|
|
913
|
+
const session = await this.auth.getSession();
|
|
914
|
+
await this.http.callAction({
|
|
915
|
+
action: "deleteManyHistories",
|
|
916
|
+
path: HISTORY_PATH,
|
|
917
|
+
args: [historyIds],
|
|
918
|
+
session
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Permanently delete a saved creation. Irreversible — requires `{ confirm: true }`.
|
|
923
|
+
*/
|
|
924
|
+
async deleteCreation(creationId, opts) {
|
|
925
|
+
requireConfirm("deleteCreation", opts);
|
|
926
|
+
const session = await this.auth.getSession();
|
|
927
|
+
await this.http.callAction({
|
|
928
|
+
action: "deleteCreation",
|
|
929
|
+
path: HISTORY_PATH,
|
|
930
|
+
args: [creationId],
|
|
931
|
+
session
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Permanently delete many creations. Irreversible — requires `{ confirm: true }`.
|
|
936
|
+
*
|
|
937
|
+
* @experimental Request body inferred from JS bundle; not yet verified live.
|
|
938
|
+
*/
|
|
939
|
+
async deleteManyCreations(creationIds, opts) {
|
|
940
|
+
requireConfirm("deleteManyCreations", opts);
|
|
941
|
+
const session = await this.auth.getSession();
|
|
942
|
+
await this.http.callAction({
|
|
943
|
+
action: "deleteManyCreations",
|
|
944
|
+
path: HISTORY_PATH,
|
|
945
|
+
args: [creationIds],
|
|
946
|
+
session
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
};
|
|
950
|
+
//#endregion
|
|
951
|
+
//#region lib/http.ts
|
|
952
|
+
const DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36";
|
|
953
|
+
var HttpClient = class {
|
|
954
|
+
registry;
|
|
955
|
+
fetchImpl;
|
|
956
|
+
baseUrl;
|
|
957
|
+
userAgent;
|
|
958
|
+
constructor(registry, fetchImpl, baseUrl, userAgent = DEFAULT_USER_AGENT) {
|
|
959
|
+
this.registry = registry;
|
|
960
|
+
this.fetchImpl = fetchImpl;
|
|
961
|
+
this.baseUrl = baseUrl;
|
|
962
|
+
this.userAgent = userAgent;
|
|
963
|
+
}
|
|
964
|
+
async callAction(params, retried = false) {
|
|
965
|
+
const { hash, deploymentId } = await this.registry.getHash(params.action);
|
|
966
|
+
const headers = {
|
|
967
|
+
accept: "text/x-component",
|
|
968
|
+
"content-type": "text/plain;charset=UTF-8",
|
|
969
|
+
"next-action": hash,
|
|
970
|
+
"x-deployment-id": deploymentId,
|
|
971
|
+
origin: this.baseUrl,
|
|
972
|
+
referer: `${this.baseUrl}${params.path}`,
|
|
973
|
+
"user-agent": this.userAgent
|
|
974
|
+
};
|
|
975
|
+
if (params.session !== void 0) headers.cookie = `__session=${params.session}`;
|
|
976
|
+
const res = await this.fetchImpl(`${this.baseUrl}${params.path}`, {
|
|
977
|
+
method: "POST",
|
|
978
|
+
headers,
|
|
979
|
+
body: JSON.stringify(params.args)
|
|
980
|
+
});
|
|
981
|
+
if (res.status === 404 && !retried) {
|
|
982
|
+
await this.registry.invalidate();
|
|
983
|
+
return this.callAction(params, true);
|
|
984
|
+
}
|
|
985
|
+
if (!res.ok) {
|
|
986
|
+
const body = await safeText(res);
|
|
987
|
+
const suffix = body.length > 0 ? ` — ${body.slice(0, 200)}` : "";
|
|
988
|
+
throw new MageSpaceError(`Action "${params.action}" failed: HTTP ${res.status}${suffix}`);
|
|
989
|
+
}
|
|
990
|
+
return {
|
|
991
|
+
data: parseServerActionResponse(await res.text()),
|
|
992
|
+
headers: res.headers,
|
|
993
|
+
deploymentId: res.headers.get("x-deployment-id") ?? deploymentId
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
};
|
|
997
|
+
async function safeText(res) {
|
|
998
|
+
try {
|
|
999
|
+
return await res.text();
|
|
1000
|
+
} catch {
|
|
1001
|
+
return "";
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
//#endregion
|
|
1005
|
+
//#region lib/references.ts
|
|
1006
|
+
/** Reference actions are served from the `/explore` page. */
|
|
1007
|
+
const REFERENCES_PATH = "/explore";
|
|
1008
|
+
const DEFAULT_PAGE_LIMIT = 20;
|
|
1009
|
+
var ReferencesService = class {
|
|
1010
|
+
http;
|
|
1011
|
+
auth;
|
|
1012
|
+
constructor(http, auth) {
|
|
1013
|
+
this.http = http;
|
|
1014
|
+
this.auth = auth;
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* Upload a reference image (base64 data URL) and return its CDN URL.
|
|
1018
|
+
*
|
|
1019
|
+
* @param dataUrl - `data:image/jpeg;base64,...` (JPEG preferred; PNG/WebP ok).
|
|
1020
|
+
*/
|
|
1021
|
+
async uploadImage(dataUrl) {
|
|
1022
|
+
const [uid, session] = await Promise.all([this.auth.getUserId(), this.auth.getSession()]);
|
|
1023
|
+
const { data } = await this.http.callAction({
|
|
1024
|
+
action: "uploadReferenceImage",
|
|
1025
|
+
path: REFERENCES_PATH,
|
|
1026
|
+
args: [dataUrl, uid],
|
|
1027
|
+
session
|
|
1028
|
+
});
|
|
1029
|
+
return data;
|
|
1030
|
+
}
|
|
1031
|
+
/** Create a reusable reference from an uploaded image/audio URL. */
|
|
1032
|
+
async create(input) {
|
|
1033
|
+
const session = await this.auth.getSession();
|
|
1034
|
+
const body = {
|
|
1035
|
+
name: input.name,
|
|
1036
|
+
username: input.username,
|
|
1037
|
+
image_url: input.image_url,
|
|
1038
|
+
audio_url: input.audio_url ?? null,
|
|
1039
|
+
modality: input.modality ?? "image",
|
|
1040
|
+
variant: input.variant ?? "reference",
|
|
1041
|
+
description: input.description ?? null,
|
|
1042
|
+
tags: input.tags ?? [],
|
|
1043
|
+
visibility: input.visibility ?? "private",
|
|
1044
|
+
moderation: input.moderation ?? []
|
|
1045
|
+
};
|
|
1046
|
+
const { data } = await this.http.callAction({
|
|
1047
|
+
action: "createReference",
|
|
1048
|
+
path: REFERENCES_PATH,
|
|
1049
|
+
args: [body],
|
|
1050
|
+
session
|
|
1051
|
+
});
|
|
1052
|
+
return data;
|
|
1053
|
+
}
|
|
1054
|
+
/** Update a reference's mutable fields (name, description, tags, visibility). */
|
|
1055
|
+
async update(id, patch) {
|
|
1056
|
+
const session = await this.auth.getSession();
|
|
1057
|
+
const { data } = await this.http.callAction({
|
|
1058
|
+
action: "updateReference",
|
|
1059
|
+
path: REFERENCES_PATH,
|
|
1060
|
+
args: [id, patch],
|
|
1061
|
+
session
|
|
1062
|
+
});
|
|
1063
|
+
return data;
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* Permanently delete a reference (soft-delete). Irreversible — requires
|
|
1067
|
+
* `{ confirm: true }`.
|
|
1068
|
+
*/
|
|
1069
|
+
async delete(id, opts) {
|
|
1070
|
+
requireConfirm("deleteReference", opts);
|
|
1071
|
+
const session = await this.auth.getSession();
|
|
1072
|
+
await this.http.callAction({
|
|
1073
|
+
action: "deleteReference",
|
|
1074
|
+
path: REFERENCES_PATH,
|
|
1075
|
+
args: [id],
|
|
1076
|
+
session
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
/** List the caller's own references for a given modality (default `image`). */
|
|
1080
|
+
async list(modality = "image") {
|
|
1081
|
+
const [uid, session] = await Promise.all([this.auth.getUserId(), this.auth.getSession()]);
|
|
1082
|
+
const { data } = await this.http.callAction({
|
|
1083
|
+
action: "getReferences",
|
|
1084
|
+
path: REFERENCES_PATH,
|
|
1085
|
+
args: [uid, modality],
|
|
1086
|
+
session
|
|
1087
|
+
});
|
|
1088
|
+
return data ?? [];
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Browse references with pagination.
|
|
1092
|
+
*
|
|
1093
|
+
* @remarks
|
|
1094
|
+
* Omit `uid` to browse the public/featured feed; pass it to page through a
|
|
1095
|
+
* single user's references.
|
|
1096
|
+
*/
|
|
1097
|
+
async listPaginated(opts = {}) {
|
|
1098
|
+
const session = await this.auth.getSession();
|
|
1099
|
+
const limit = opts.limit ?? DEFAULT_PAGE_LIMIT;
|
|
1100
|
+
const offset = opts.offset ?? 0;
|
|
1101
|
+
const filters = {
|
|
1102
|
+
visibility: opts.visibility ?? "public",
|
|
1103
|
+
orderBy: opts.orderBy ?? "top",
|
|
1104
|
+
featuredOnly: opts.featuredOnly ?? false,
|
|
1105
|
+
modality: opts.modality ?? "image"
|
|
1106
|
+
};
|
|
1107
|
+
if (opts.uid !== void 0) filters.uid = opts.uid;
|
|
1108
|
+
const { data } = await this.http.callAction({
|
|
1109
|
+
action: "getReferencesPaginated",
|
|
1110
|
+
path: REFERENCES_PATH,
|
|
1111
|
+
args: [
|
|
1112
|
+
limit,
|
|
1113
|
+
offset,
|
|
1114
|
+
filters
|
|
1115
|
+
],
|
|
1116
|
+
session
|
|
1117
|
+
});
|
|
1118
|
+
return data;
|
|
1119
|
+
}
|
|
1120
|
+
};
|
|
1121
|
+
//#endregion
|
|
1122
|
+
//#region lib/seed.ts
|
|
1123
|
+
/**
|
|
1124
|
+
* Bundled action-hash snapshot from the mage.space Vercel deploy discovered on
|
|
1125
|
+
* 2026-08-06 (`docs/api/actions.md`).
|
|
1126
|
+
*
|
|
1127
|
+
* @remarks
|
|
1128
|
+
* Used as a warm seed so the very first call does not pay the discovery cost.
|
|
1129
|
+
* Hashes change on every deploy; when one goes stale the client re-discovers
|
|
1130
|
+
* automatically (404 → invalidate → refresh).
|
|
1131
|
+
*/
|
|
1132
|
+
const SEED_SNAPSHOT = {
|
|
1133
|
+
deploymentId: "dpl_GLUqzVNVADQR5wogKksDvu1m3BdH",
|
|
1134
|
+
discoveredAt: Date.parse("2026-08-06T00:00:00.000Z"),
|
|
1135
|
+
hashes: {
|
|
1136
|
+
createUserSession: "60fa7da54d8662813645f7077455339d23096f391c",
|
|
1137
|
+
deleteUserSession: "0077c71a228d26498d446a89e60ec6d17199443f9e",
|
|
1138
|
+
getUserHydrationData: "40314a83b96c226af04a264d1e076a06dbf3cc73b2",
|
|
1139
|
+
runArchitecture: "402c09c1b4694e5bb273a949a41a2f36ca82eff394",
|
|
1140
|
+
getHistoryById: "40b894a9a39b33b79d4eb046e4beeed7b30b21a887",
|
|
1141
|
+
cancelArchitectureJob: "40e4b91be4f6698821f96021adaf305a74b32af2a1",
|
|
1142
|
+
getHistoryPaginated: "787d245f6320977164c554a189a4d5b80c18ca3347",
|
|
1143
|
+
getMentionSuggestionsParallel: "7f1553e5f697d614a2982a5400edae73c4ef672ece",
|
|
1144
|
+
createCharacter: "4012fee65f53db4fbe9d83808da2869751fe503a87",
|
|
1145
|
+
updateCharacter: "60dc4b95918a5fea7b2752bedebb38e0950e19a17e",
|
|
1146
|
+
deleteCharacter: "40656ebd70a3804b5c18740913c8b769d4c72e1298",
|
|
1147
|
+
getCharacter: "40b1e8e090a726985d8868f145865a3b5bde0803b1",
|
|
1148
|
+
getCharacters: "402ea21b03340cd20f55ea8c15f56dbbc1ce957f51",
|
|
1149
|
+
publishCharacter: "409a73d2187553e2e069493147969c46d7329a7c44",
|
|
1150
|
+
unpublishCharacter: "40f009e2c220d4efc54b9f8968ab37e74cb66de982",
|
|
1151
|
+
uploadCharacterImage: "60869f48fe83aa95969a6549f84023c9e115ee274f",
|
|
1152
|
+
uploadReferenceImage: "606ba2b0eb79bd8f28c609706a89b52e0d9f517fa6",
|
|
1153
|
+
createReference: "406d91cfd5589042bd72defa1071bc7c3fb84810c4",
|
|
1154
|
+
updateReference: "602d6bb3ba2609661f07330224f6fd075e7af6bc9e",
|
|
1155
|
+
deleteReference: "409dd73e6daafe43b500b0c9f012a33fc5350af196",
|
|
1156
|
+
getReferences: "60a0357a926bc3e4031c00e62ca815e3ea21b3eb35",
|
|
1157
|
+
getReferencesPaginated: "7069d599ab890e353b790d46df56160d5c0a85348a",
|
|
1158
|
+
getCharactersPaginated: "7085cf87746975cbec2868405a026e22b97a20cfb4",
|
|
1159
|
+
getCreationsPaginatedParallel: "7f7f533eb9fc5a3fca55cb5e19d45532a27eb75f38",
|
|
1160
|
+
deleteHistory: "40e1f88b6813c101c03e136e4d5f5fadeb9909ee52",
|
|
1161
|
+
deleteManyHistories: "402a5bddb2a41165f2f64782b85b4915e63ec2a849",
|
|
1162
|
+
deleteCreation: "407e9ce3d04c7274e3341bf8450633dc8e3935ec9c",
|
|
1163
|
+
deleteManyCreations: "403d071e9dc36382b3efff80470e6d76b3edd299b8",
|
|
1164
|
+
saveCreation: "6087f655eaef4de56a01fb6c6a84df58cea0632e5f",
|
|
1165
|
+
generateCharacterVoice: "6052462e345ee9878d5c36c2a160ea1873919c2b41",
|
|
1166
|
+
followCharacter: "402d9d8cf924262612f29b5ef3d1a6a97d3a0a9d7a",
|
|
1167
|
+
unfollowCharacter: "40842140a2e9467f854870c6ad20e3dcf17bece652"
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
//#endregion
|
|
1171
|
+
//#region lib/client.ts
|
|
1172
|
+
/**
|
|
1173
|
+
* {@link MageSpaceClient} — the public facade that wires together action
|
|
1174
|
+
* discovery, authentication, generation, and history.
|
|
1175
|
+
*/
|
|
1176
|
+
const DEFAULT_BASE_URL = "https://www.mage.space";
|
|
1177
|
+
var MageSpaceClient = class {
|
|
1178
|
+
registry;
|
|
1179
|
+
auth;
|
|
1180
|
+
generationService;
|
|
1181
|
+
historyService;
|
|
1182
|
+
/** Character management (search, CRUD, pagination, uploads). */
|
|
1183
|
+
characters;
|
|
1184
|
+
/** Reference asset management (upload, create, update, list, feed). */
|
|
1185
|
+
references;
|
|
1186
|
+
constructor(options) {
|
|
1187
|
+
if (!options.refreshToken) throw new Error("MageSpaceClient requires a refreshToken");
|
|
1188
|
+
const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;
|
|
1189
|
+
const fetchImpl = options.fetch ?? ((input, init) => fetch(input, init));
|
|
1190
|
+
this.registry = new ActionRegistry({
|
|
1191
|
+
fetch: fetchImpl,
|
|
1192
|
+
baseUrl,
|
|
1193
|
+
cache: options.cache,
|
|
1194
|
+
seed: options.seed ?? SEED_SNAPSHOT
|
|
1195
|
+
});
|
|
1196
|
+
const http = new HttpClient(this.registry, fetchImpl, baseUrl, options.userAgent);
|
|
1197
|
+
this.auth = new AuthManager({
|
|
1198
|
+
refreshToken: options.refreshToken,
|
|
1199
|
+
http,
|
|
1200
|
+
fetch: fetchImpl,
|
|
1201
|
+
firebaseApiKey: options.firebaseApiKey
|
|
1202
|
+
});
|
|
1203
|
+
this.generationService = new GenerationService(http, this.auth);
|
|
1204
|
+
this.historyService = new HistoryService(http, this.auth);
|
|
1205
|
+
this.characters = new CharactersService(http, this.auth);
|
|
1206
|
+
this.references = new ReferencesService(http, this.auth);
|
|
1207
|
+
}
|
|
1208
|
+
/** Submit a generation job; returns its `historyId`. */
|
|
1209
|
+
generate(options) {
|
|
1210
|
+
return this.generationService.submit(options);
|
|
1211
|
+
}
|
|
1212
|
+
/** Submit a video generation job (Berry-2 family); returns its `historyId`. */
|
|
1213
|
+
generateVideo(options) {
|
|
1214
|
+
return this.generationService.submitVideo(options);
|
|
1215
|
+
}
|
|
1216
|
+
/** Fetch the current state of a generation by id. */
|
|
1217
|
+
getResult(historyId) {
|
|
1218
|
+
return this.generationService.getHistory(historyId);
|
|
1219
|
+
}
|
|
1220
|
+
/** Poll until a generation finishes (or fails / times out). */
|
|
1221
|
+
waitForResult(historyId, options) {
|
|
1222
|
+
return this.generationService.waitForResult(historyId, options);
|
|
1223
|
+
}
|
|
1224
|
+
/** Cancel a running generation job. */
|
|
1225
|
+
cancelJob(historyId) {
|
|
1226
|
+
return this.generationService.cancelJob(historyId);
|
|
1227
|
+
}
|
|
1228
|
+
/** List past generations for the authenticated user. */
|
|
1229
|
+
listHistory(options) {
|
|
1230
|
+
return this.historyService.list(options);
|
|
1231
|
+
}
|
|
1232
|
+
/** List the user's permanently saved creations. */
|
|
1233
|
+
listCreations(options) {
|
|
1234
|
+
return this.historyService.listCreations(options);
|
|
1235
|
+
}
|
|
1236
|
+
/** Save a completed generation result as a permanent creation. */
|
|
1237
|
+
saveCreation(result) {
|
|
1238
|
+
return this.historyService.saveCreation(result);
|
|
1239
|
+
}
|
|
1240
|
+
/** Permanently delete a history item. Irreversible — requires `{ confirm: true }`. */
|
|
1241
|
+
deleteHistory(historyId, options) {
|
|
1242
|
+
return this.historyService.deleteHistory(historyId, options);
|
|
1243
|
+
}
|
|
1244
|
+
/** Permanently delete many history items. Irreversible — requires `{ confirm: true }`. */
|
|
1245
|
+
deleteManyHistories(historyIds, options) {
|
|
1246
|
+
return this.historyService.deleteManyHistories(historyIds, options);
|
|
1247
|
+
}
|
|
1248
|
+
/** Permanently delete a saved creation. Irreversible — requires `{ confirm: true }`. */
|
|
1249
|
+
deleteCreation(creationId, options) {
|
|
1250
|
+
return this.historyService.deleteCreation(creationId, options);
|
|
1251
|
+
}
|
|
1252
|
+
/** Permanently delete many creations. Irreversible — requires `{ confirm: true }`. */
|
|
1253
|
+
deleteManyCreations(creationIds, options) {
|
|
1254
|
+
return this.historyService.deleteManyCreations(creationIds, options);
|
|
1255
|
+
}
|
|
1256
|
+
/** Force re-discovery of the Server Action hashes. */
|
|
1257
|
+
refreshActions() {
|
|
1258
|
+
return this.registry.refresh();
|
|
1259
|
+
}
|
|
1260
|
+
};
|
|
1261
|
+
//#endregion
|
|
1262
|
+
//#region lib/index.ts
|
|
1263
|
+
/** Public entry point for `@zweer/mage-space-client`. */
|
|
1264
|
+
const VERSION = "0.0.0";
|
|
1265
|
+
//#endregion
|
|
1266
|
+
exports.ActionRegistry = ActionRegistry;
|
|
1267
|
+
exports.AuthError = AuthError;
|
|
1268
|
+
exports.CharactersService = CharactersService;
|
|
1269
|
+
exports.DiscoveryError = DiscoveryError;
|
|
1270
|
+
exports.GenerationError = GenerationError;
|
|
1271
|
+
exports.InMemoryActionCache = InMemoryActionCache;
|
|
1272
|
+
exports.MageSpaceClient = MageSpaceClient;
|
|
1273
|
+
exports.MageSpaceError = MageSpaceError;
|
|
1274
|
+
exports.ReferencesService = ReferencesService;
|
|
1275
|
+
exports.RscParseError = RscParseError;
|
|
1276
|
+
exports.SEED_SNAPSHOT = SEED_SNAPSHOT;
|
|
1277
|
+
exports.StaleActionError = StaleActionError;
|
|
1278
|
+
exports.TimeoutError = TimeoutError;
|
|
1279
|
+
exports.VERSION = VERSION;
|
|
1280
|
+
exports.discoverActions = discoverActions;
|
|
1281
|
+
exports.parseFlightRows = parseFlightRows;
|
|
1282
|
+
exports.parseServerActionResponse = parseServerActionResponse;
|
|
1283
|
+
exports.resolveFlightValue = resolveFlightValue;
|
|
1284
|
+
exports.unwrapResult = unwrapResult;
|