@zweer/mage-space-client 1.1.2 → 1.3.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/README.md +11 -1
- package/dist/index.cjs +431 -116
- package/dist/index.d.cts +157 -20
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +157 -20
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +427 -117
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -18,12 +18,38 @@ var AuthError = class extends MageSpaceError {
|
|
|
18
18
|
};
|
|
19
19
|
/** Action-hash discovery failures (page/chunk fetch, empty registry). */
|
|
20
20
|
var DiscoveryError = class extends MageSpaceError {
|
|
21
|
+
/**
|
|
22
|
+
* `true` when the action name was absent even after a forced re-discovery
|
|
23
|
+
* (a fallback trigger); `false` for scrape failures (no chunks/no hashes).
|
|
24
|
+
*/
|
|
25
|
+
notFound;
|
|
21
26
|
constructor(message, options) {
|
|
22
27
|
super(message, options);
|
|
23
28
|
this.name = "DiscoveryError";
|
|
29
|
+
this.notFound = options?.notFound ?? false;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* A required action is unavailable on the active host (a persistent 404 / HTML
|
|
34
|
+
* fallback that survived one in-place re-discovery). A fallback trigger.
|
|
35
|
+
*/
|
|
36
|
+
var ActionUnavailableError = class extends MageSpaceError {
|
|
37
|
+
action;
|
|
38
|
+
targetKey;
|
|
39
|
+
constructor(action, targetKey, options) {
|
|
40
|
+
super(`Action "${action}" is unavailable on target "${targetKey}"`, options);
|
|
41
|
+
this.action = action;
|
|
42
|
+
this.targetKey = targetKey;
|
|
43
|
+
this.name = "ActionUnavailableError";
|
|
24
44
|
}
|
|
25
45
|
};
|
|
26
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* A Server Action returned 404, indicating a stale (post-deploy) hash.
|
|
48
|
+
*
|
|
49
|
+
* @deprecated Legacy export kept for backward compatibility; no longer thrown.
|
|
50
|
+
* The current signal for a definitively unavailable action is
|
|
51
|
+
* {@link ActionUnavailableError}.
|
|
52
|
+
*/
|
|
27
53
|
var StaleActionError = class extends MageSpaceError {
|
|
28
54
|
action;
|
|
29
55
|
constructor(action, options) {
|
|
@@ -215,7 +241,7 @@ var ActionRegistry = class {
|
|
|
215
241
|
snapshot = await this.refresh();
|
|
216
242
|
hash = snapshot.hashes[action];
|
|
217
243
|
}
|
|
218
|
-
if (hash === void 0) throw new DiscoveryError(`Action "${action}" not found after discovery
|
|
244
|
+
if (hash === void 0) throw new DiscoveryError(`Action "${action}" not found after discovery`, { notFound: true });
|
|
219
245
|
return {
|
|
220
246
|
hash,
|
|
221
247
|
deploymentId: snapshot.deploymentId
|
|
@@ -327,9 +353,11 @@ function resolveFlightValue(value, rows, seen = /* @__PURE__ */ new Set()) {
|
|
|
327
353
|
if (ref) {
|
|
328
354
|
const refId = ref[1];
|
|
329
355
|
if (refId === void 0 || seen.has(refId)) return;
|
|
330
|
-
seen.add(refId);
|
|
331
356
|
const target = rows.get(refId);
|
|
332
|
-
|
|
357
|
+
if (target === void 0) return;
|
|
358
|
+
const nextSeen = new Set(seen);
|
|
359
|
+
nextSeen.add(refId);
|
|
360
|
+
return resolveFlightValue(target, rows, nextSeen);
|
|
333
361
|
}
|
|
334
362
|
if (value.startsWith("$$")) return value.slice(1);
|
|
335
363
|
if (value.startsWith("$D")) return new Date(value.slice(2));
|
|
@@ -376,6 +404,26 @@ function unwrapResult(value) {
|
|
|
376
404
|
/** Character actions are served from the `/explore` page. */
|
|
377
405
|
const CHARACTERS_PATH = "/explore";
|
|
378
406
|
const DEFAULT_SEARCH_LIMIT = 10;
|
|
407
|
+
/** Server-enforced username constraints (from module 64428). */
|
|
408
|
+
const USERNAME_MIN = 1;
|
|
409
|
+
const USERNAME_MAX$1 = 15;
|
|
410
|
+
const USERNAME_RE$1 = /^[a-z][a-z0-9_-]*$/;
|
|
411
|
+
/**
|
|
412
|
+
* Generate a username from a display name, matching the web UI logic:
|
|
413
|
+
* lowercase, strip special chars, append a 4-char random suffix, truncate to 15.
|
|
414
|
+
*/
|
|
415
|
+
function generateUsername$1(name) {
|
|
416
|
+
const suffix = Math.random().toString(36).substring(2, 6);
|
|
417
|
+
const base = name.toLowerCase().trim().replace(/[^a-z0-9\s]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
418
|
+
const maxBase = USERNAME_MAX$1 - suffix.length - 1;
|
|
419
|
+
return `${base.slice(0, maxBase).replace(/[-_]+$/, "")}-${suffix}`;
|
|
420
|
+
}
|
|
421
|
+
/** Validate a username and throw early with a clear message instead of a server 500. */
|
|
422
|
+
function validateUsername$1(username) {
|
|
423
|
+
if (username.length < USERNAME_MIN) throw new MageSpaceError("Username is required (min 1 character)");
|
|
424
|
+
if (username.length > USERNAME_MAX$1) throw new MageSpaceError(`Username "${username}" is too long (${username.length} chars, max ${USERNAME_MAX$1})`);
|
|
425
|
+
if (!USERNAME_RE$1.test(username)) throw new MageSpaceError(`Username "${username}" is invalid: must start with a lowercase letter and contain only a-z, 0-9, _ or -`);
|
|
426
|
+
}
|
|
379
427
|
var CharactersService = class {
|
|
380
428
|
http;
|
|
381
429
|
auth;
|
|
@@ -403,10 +451,12 @@ var CharactersService = class {
|
|
|
403
451
|
}
|
|
404
452
|
/** Create a new character from an already-uploaded image URL. */
|
|
405
453
|
async create(input) {
|
|
454
|
+
const username = input.username ?? generateUsername$1(input.name);
|
|
455
|
+
validateUsername$1(username);
|
|
406
456
|
const session = await this.auth.getSession();
|
|
407
457
|
const body = {
|
|
408
458
|
name: input.name,
|
|
409
|
-
username
|
|
459
|
+
username,
|
|
410
460
|
image_url: input.image_url,
|
|
411
461
|
audio_url: input.audio_url ?? null,
|
|
412
462
|
description: input.description ?? null,
|
|
@@ -593,7 +643,7 @@ const SESSION_DEFAULT_TTL_MS = 864e5;
|
|
|
593
643
|
const ID_TOKEN_DEFAULT_TTL_S = 3600;
|
|
594
644
|
var AuthManager = class {
|
|
595
645
|
refreshToken;
|
|
596
|
-
|
|
646
|
+
getHttp;
|
|
597
647
|
fetchImpl;
|
|
598
648
|
apiKey;
|
|
599
649
|
sessionPath;
|
|
@@ -601,11 +651,19 @@ var AuthManager = class {
|
|
|
601
651
|
session = null;
|
|
602
652
|
constructor(opts) {
|
|
603
653
|
this.refreshToken = opts.refreshToken;
|
|
604
|
-
this.
|
|
654
|
+
this.getHttp = opts.getHttp;
|
|
605
655
|
this.fetchImpl = opts.fetch;
|
|
606
656
|
this.apiKey = opts.firebaseApiKey ?? DEFAULT_FIREBASE_API_KEY;
|
|
607
657
|
this.sessionPath = opts.sessionPath ?? "/creations";
|
|
608
658
|
}
|
|
659
|
+
/**
|
|
660
|
+
* Drop the cached `__session` cookie so the next {@link getSession} mints a
|
|
661
|
+
* fresh one against the (newly) active host. The Firebase token chain is kept
|
|
662
|
+
* because it is host-independent.
|
|
663
|
+
*/
|
|
664
|
+
resetSession() {
|
|
665
|
+
this.session = null;
|
|
666
|
+
}
|
|
609
667
|
/** Return a valid Firebase ID token, refreshing via the refresh token if needed. */
|
|
610
668
|
async getIdToken() {
|
|
611
669
|
if (this.tokens !== null && Date.now() < this.tokens.expiresAt - EXPIRY_BUFFER_MS) return this.tokens.idToken;
|
|
@@ -620,7 +678,7 @@ var AuthManager = class {
|
|
|
620
678
|
async getSession() {
|
|
621
679
|
if (this.session !== null && Date.now() < this.session.expiresAt - EXPIRY_BUFFER_MS) return this.session.cookie;
|
|
622
680
|
const idToken = await this.getIdToken();
|
|
623
|
-
const cookie = extractSessionCookie((await this.
|
|
681
|
+
const cookie = extractSessionCookie((await this.getHttp().callAction({
|
|
624
682
|
action: "createUserSession",
|
|
625
683
|
path: this.sessionPath,
|
|
626
684
|
args: [idToken]
|
|
@@ -678,9 +736,58 @@ function extractSessionCookie(headers) {
|
|
|
678
736
|
return null;
|
|
679
737
|
}
|
|
680
738
|
//#endregion
|
|
739
|
+
//#region lib/targets.ts
|
|
740
|
+
/** Resolve a logical action to its host-specific wire name (identity if unlisted). */
|
|
741
|
+
function resolveActionName(target, logical) {
|
|
742
|
+
return target.actionNames[logical] ?? logical;
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Production target.
|
|
746
|
+
*
|
|
747
|
+
* @remarks
|
|
748
|
+
* Maps each logical action to the wire name the current production deploy
|
|
749
|
+
* actually serves (all VERIFIED against `lib/seed.ts` / the existing MSW tests):
|
|
750
|
+
* `submitGeneration → runArchitecture`, `cancelJob → cancelArchitectureJob`,
|
|
751
|
+
* `listHistory → getHistoryPaginated`, `listCreations → getCreationsPaginatedParallel`,
|
|
752
|
+
* and `getHistoryById → getHistoryById` (identity). The table is explicit rather
|
|
753
|
+
* than empty so production behaviour is reproduced exactly regardless of the
|
|
754
|
+
* logical-name vocabulary.
|
|
755
|
+
*/
|
|
756
|
+
const PRODUCTION_TARGET = {
|
|
757
|
+
key: "production",
|
|
758
|
+
baseUrl: "https://www.mage.space",
|
|
759
|
+
actionNames: {
|
|
760
|
+
submitGeneration: "runArchitecture",
|
|
761
|
+
getHistoryById: "getHistoryById",
|
|
762
|
+
cancelJob: "cancelArchitectureJob",
|
|
763
|
+
listHistory: "getHistoryPaginated",
|
|
764
|
+
listCreations: "getCreationsPaginatedParallel"
|
|
765
|
+
},
|
|
766
|
+
pollStrategy: "byId"
|
|
767
|
+
};
|
|
768
|
+
/**
|
|
769
|
+
* Beta target. Beta facts are assumptions pending a reverse-engineering capture
|
|
770
|
+
* under `docs/api/beta` — treat them as unverified until that doc lands.
|
|
771
|
+
*/
|
|
772
|
+
const BETA_TARGET = {
|
|
773
|
+
key: "beta",
|
|
774
|
+
baseUrl: "https://beta.mage.space",
|
|
775
|
+
actionNames: {
|
|
776
|
+
submitGeneration: "runArchitecture",
|
|
777
|
+
cancelJob: "cancelArchitectureJob",
|
|
778
|
+
listHistory: "getHistoryPaginated",
|
|
779
|
+
listCreations: "getCreationsPaginated"
|
|
780
|
+
},
|
|
781
|
+
pollStrategy: "paginated"
|
|
782
|
+
};
|
|
783
|
+
/** Default ordered fallback chain: production first, then beta. */
|
|
784
|
+
const DEFAULT_TARGET_CHAIN = [PRODUCTION_TARGET, BETA_TARGET];
|
|
785
|
+
//#endregion
|
|
681
786
|
//#region lib/generation.ts
|
|
682
787
|
/** Generation actions are served from the `/explore` page. */
|
|
683
788
|
const GENERATION_PATH = "/explore";
|
|
789
|
+
/** Mirrors HistoryService.HISTORY_PATH ('/creations'); kept local to avoid coupling the two services. */
|
|
790
|
+
const HISTORY_PATH$1 = "/creations";
|
|
684
791
|
/** RSC sentinel for an absent value. */
|
|
685
792
|
const UNDEFINED$1 = "$undefined";
|
|
686
793
|
/** Default concept id for mango-v3-pro (from docs — verify per model via rev-eng). */
|
|
@@ -692,9 +799,14 @@ const DEFAULT_TIMEOUT_MS = 3e5;
|
|
|
692
799
|
var GenerationService = class {
|
|
693
800
|
http;
|
|
694
801
|
auth;
|
|
695
|
-
|
|
802
|
+
target;
|
|
803
|
+
/** Single-status-read primitive; overridable so the client can make it fallback-aware. */
|
|
804
|
+
pollOnce;
|
|
805
|
+
constructor(http, auth, target, pollOnce) {
|
|
696
806
|
this.http = http;
|
|
697
807
|
this.auth = auth;
|
|
808
|
+
this.target = target;
|
|
809
|
+
this.pollOnce = pollOnce ?? ((id) => this.getHistory(id));
|
|
698
810
|
}
|
|
699
811
|
/** Submit an image generation job and return its `historyId`. */
|
|
700
812
|
async submit(opts) {
|
|
@@ -746,7 +858,7 @@ var GenerationService = class {
|
|
|
746
858
|
generationMode
|
|
747
859
|
}];
|
|
748
860
|
const { data } = await this.http.callAction({
|
|
749
|
-
action: "
|
|
861
|
+
action: resolveActionName(this.target, "submitGeneration"),
|
|
750
862
|
path: GENERATION_PATH,
|
|
751
863
|
args,
|
|
752
864
|
session
|
|
@@ -759,22 +871,68 @@ var GenerationService = class {
|
|
|
759
871
|
if (typeof historyId !== "string") throw new GenerationError("runArchitecture did not return a history_id");
|
|
760
872
|
return { historyId };
|
|
761
873
|
}
|
|
762
|
-
/** Fetch a single history record by id. */
|
|
874
|
+
/** Fetch a single history record by id, dispatching on the target's poll strategy. */
|
|
763
875
|
async getHistory(historyId) {
|
|
876
|
+
if (this.target.pollStrategy === "paginated") return this.getHistoryPaginated(historyId);
|
|
764
877
|
const session = await this.auth.getSession();
|
|
765
878
|
const { data } = await this.http.callAction({
|
|
766
|
-
action: "getHistoryById",
|
|
879
|
+
action: resolveActionName(this.target, "getHistoryById"),
|
|
767
880
|
path: GENERATION_PATH,
|
|
768
881
|
args: [historyId],
|
|
769
882
|
session
|
|
770
883
|
});
|
|
771
884
|
return data;
|
|
772
885
|
}
|
|
886
|
+
/**
|
|
887
|
+
* Beta status read: `getHistoryById` is absent, so page `getHistoryPaginated`
|
|
888
|
+
* and find the item whose `id === historyId`.
|
|
889
|
+
*
|
|
890
|
+
* @remarks
|
|
891
|
+
* ASSUMED (pending docs/api/beta): each page window is swept in two phases —
|
|
892
|
+
* `running` first, then the raw `$undefined` sentinel (terminal items) — since
|
|
893
|
+
* which filter surfaces a running job on beta is not yet documented. Bounded by
|
|
894
|
+
* {@link PAGINATED_POLL_MAX_PAGES} pages of {@link PAGINATED_POLL_PAGE_SIZE}.
|
|
895
|
+
*/
|
|
896
|
+
async getHistoryPaginated(historyId) {
|
|
897
|
+
const uid = await this.auth.getUserId();
|
|
898
|
+
const limit = 20;
|
|
899
|
+
let offset = 0;
|
|
900
|
+
for (let pageIndex = 0; pageIndex < 10; pageIndex += 1) {
|
|
901
|
+
for (const status of ["running", UNDEFINED$1]) {
|
|
902
|
+
const page = await this.fetchHistoryPage(uid, status, limit, offset);
|
|
903
|
+
const found = page.histories.find((h) => h.id === historyId);
|
|
904
|
+
if (found !== void 0) return found;
|
|
905
|
+
if (status === UNDEFINED$1 && !page.hasMore) throw new GenerationError(`history ${historyId} not found on beta`);
|
|
906
|
+
}
|
|
907
|
+
offset += limit;
|
|
908
|
+
}
|
|
909
|
+
throw new GenerationError(`history ${historyId} not found on beta`);
|
|
910
|
+
}
|
|
911
|
+
/** Fetch one page of history via `listHistory` (resolved per target) on `/creations`. */
|
|
912
|
+
async fetchHistoryPage(uid, status, limit, offset) {
|
|
913
|
+
const session = await this.auth.getSession();
|
|
914
|
+
const filters = {
|
|
915
|
+
status,
|
|
916
|
+
type: UNDEFINED$1
|
|
917
|
+
};
|
|
918
|
+
const { data } = await this.http.callAction({
|
|
919
|
+
action: resolveActionName(this.target, "listHistory"),
|
|
920
|
+
path: HISTORY_PATH$1,
|
|
921
|
+
args: [
|
|
922
|
+
uid,
|
|
923
|
+
limit,
|
|
924
|
+
offset,
|
|
925
|
+
filters
|
|
926
|
+
],
|
|
927
|
+
session
|
|
928
|
+
});
|
|
929
|
+
return unwrapResult(data);
|
|
930
|
+
}
|
|
773
931
|
/** Cancel a running generation job. */
|
|
774
932
|
async cancelJob(historyId) {
|
|
775
933
|
const session = await this.auth.getSession();
|
|
776
934
|
await this.http.callAction({
|
|
777
|
-
action: "
|
|
935
|
+
action: resolveActionName(this.target, "cancelJob"),
|
|
778
936
|
path: GENERATION_PATH,
|
|
779
937
|
args: [historyId],
|
|
780
938
|
session
|
|
@@ -787,7 +945,7 @@ var GenerationService = class {
|
|
|
787
945
|
const deadline = Date.now() + timeoutMs;
|
|
788
946
|
for (;;) {
|
|
789
947
|
if (opts.signal?.aborted === true) throw new TimeoutError("waitForResult was aborted");
|
|
790
|
-
const item = await this.
|
|
948
|
+
const item = await this.pollOnce(historyId);
|
|
791
949
|
if (item.status === "success") return item;
|
|
792
950
|
if (item.status === "error") throw new GenerationError(`Generation failed: ${JSON.stringify(item.error ?? null)}`);
|
|
793
951
|
if (Date.now() > deadline) throw new TimeoutError(`Generation timed out after ${timeoutMs}ms (last status: ${item.status})`);
|
|
@@ -833,9 +991,11 @@ const UNDEFINED = "$undefined";
|
|
|
833
991
|
var HistoryService = class {
|
|
834
992
|
http;
|
|
835
993
|
auth;
|
|
836
|
-
|
|
994
|
+
target;
|
|
995
|
+
constructor(http, auth, target) {
|
|
837
996
|
this.http = http;
|
|
838
997
|
this.auth = auth;
|
|
998
|
+
this.target = target;
|
|
839
999
|
}
|
|
840
1000
|
/** List generation history for the authenticated user. */
|
|
841
1001
|
async list(opts = {}) {
|
|
@@ -847,7 +1007,7 @@ var HistoryService = class {
|
|
|
847
1007
|
type: UNDEFINED
|
|
848
1008
|
};
|
|
849
1009
|
const { data } = await this.http.callAction({
|
|
850
|
-
action: "
|
|
1010
|
+
action: resolveActionName(this.target, "listHistory"),
|
|
851
1011
|
path: HISTORY_PATH,
|
|
852
1012
|
args: [
|
|
853
1013
|
uid,
|
|
@@ -865,7 +1025,7 @@ var HistoryService = class {
|
|
|
865
1025
|
const limit = opts.limit ?? DEFAULT_LIMIT;
|
|
866
1026
|
const offset = opts.offset ?? 0;
|
|
867
1027
|
const { data } = await this.http.callAction({
|
|
868
|
-
action: "
|
|
1028
|
+
action: resolveActionName(this.target, "listCreations"),
|
|
869
1029
|
path: HISTORY_PATH,
|
|
870
1030
|
args: [
|
|
871
1031
|
uid,
|
|
@@ -950,75 +1110,21 @@ var HistoryService = class {
|
|
|
950
1110
|
}
|
|
951
1111
|
};
|
|
952
1112
|
//#endregion
|
|
953
|
-
//#region lib/http.ts
|
|
954
|
-
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";
|
|
955
|
-
/**
|
|
956
|
-
* Known RSC error digests mapped to human-readable messages.
|
|
957
|
-
* Discovered via reverse engineering; the server does not expose error details.
|
|
958
|
-
*/
|
|
959
|
-
const KNOWN_DIGESTS = {
|
|
960
|
-
"709776019": "Username already taken (duplicate character/reference username)",
|
|
961
|
-
"1853426377": "Username already taken (duplicate character/reference username)"
|
|
962
|
-
};
|
|
963
|
-
var HttpClient = class {
|
|
964
|
-
registry;
|
|
965
|
-
fetchImpl;
|
|
966
|
-
baseUrl;
|
|
967
|
-
userAgent;
|
|
968
|
-
constructor(registry, fetchImpl, baseUrl, userAgent = DEFAULT_USER_AGENT) {
|
|
969
|
-
this.registry = registry;
|
|
970
|
-
this.fetchImpl = fetchImpl;
|
|
971
|
-
this.baseUrl = baseUrl;
|
|
972
|
-
this.userAgent = userAgent;
|
|
973
|
-
}
|
|
974
|
-
async callAction(params, retried = false) {
|
|
975
|
-
const { hash, deploymentId } = await this.registry.getHash(params.action);
|
|
976
|
-
const headers = {
|
|
977
|
-
accept: "text/x-component",
|
|
978
|
-
"content-type": "text/plain;charset=UTF-8",
|
|
979
|
-
"next-action": hash,
|
|
980
|
-
"x-deployment-id": deploymentId,
|
|
981
|
-
origin: this.baseUrl,
|
|
982
|
-
referer: `${this.baseUrl}${params.path}`,
|
|
983
|
-
"user-agent": this.userAgent
|
|
984
|
-
};
|
|
985
|
-
if (params.session !== void 0) headers.cookie = `__session=${params.session}`;
|
|
986
|
-
const res = await this.fetchImpl(`${this.baseUrl}${params.path}`, {
|
|
987
|
-
method: "POST",
|
|
988
|
-
headers,
|
|
989
|
-
body: JSON.stringify(params.args)
|
|
990
|
-
});
|
|
991
|
-
const contentType = res.headers.get("content-type") ?? "";
|
|
992
|
-
const isHtmlFallback = res.ok && contentType.includes("text/html");
|
|
993
|
-
if ((res.status === 404 || isHtmlFallback) && !retried) {
|
|
994
|
-
await safeText(res);
|
|
995
|
-
await this.registry.invalidate();
|
|
996
|
-
return this.callAction(params, true);
|
|
997
|
-
}
|
|
998
|
-
if (!res.ok) {
|
|
999
|
-
const body = await safeText(res);
|
|
1000
|
-
const digest = body.match(/E\{"digest":"([^"]+)"\}/)?.[1];
|
|
1001
|
-
const knownDigest = digest !== void 0 ? KNOWN_DIGESTS[digest] : void 0;
|
|
1002
|
-
const hint = knownDigest ? ` — ${knownDigest}` : digest ? ` (server digest: ${digest})` : "";
|
|
1003
|
-
const raw = body.length > 0 ? ` — ${body.slice(0, 200)}` : "";
|
|
1004
|
-
throw new MageSpaceError(`Action "${params.action}" failed: HTTP ${res.status}${hint}${raw}`);
|
|
1005
|
-
}
|
|
1006
|
-
return {
|
|
1007
|
-
data: parseServerActionResponse(await res.text()),
|
|
1008
|
-
headers: res.headers,
|
|
1009
|
-
deploymentId: res.headers.get("x-deployment-id") ?? deploymentId
|
|
1010
|
-
};
|
|
1011
|
-
}
|
|
1012
|
-
};
|
|
1013
|
-
async function safeText(res) {
|
|
1014
|
-
try {
|
|
1015
|
-
return await res.text();
|
|
1016
|
-
} catch {
|
|
1017
|
-
return "";
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
//#endregion
|
|
1021
1113
|
//#region lib/references.ts
|
|
1114
|
+
/** Server-enforced username constraints (shared with characters, module 64428). */
|
|
1115
|
+
const USERNAME_MAX = 15;
|
|
1116
|
+
const USERNAME_RE = /^[a-z][a-z0-9_-]*$/;
|
|
1117
|
+
/** Generate a username from a display name (same logic as characters). */
|
|
1118
|
+
function generateUsername(name) {
|
|
1119
|
+
const suffix = Math.random().toString(36).substring(2, 6);
|
|
1120
|
+
const base = name.toLowerCase().trim().replace(/[^a-z0-9\s]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
1121
|
+
const maxBase = USERNAME_MAX - suffix.length - 1;
|
|
1122
|
+
return `${base.slice(0, maxBase).replace(/[-_]+$/, "")}-${suffix}`;
|
|
1123
|
+
}
|
|
1124
|
+
function validateUsername(username) {
|
|
1125
|
+
if (username.length > USERNAME_MAX) throw new MageSpaceError(`Username "${username}" is too long (${username.length} chars, max ${USERNAME_MAX})`);
|
|
1126
|
+
if (!USERNAME_RE.test(username)) throw new MageSpaceError(`Username "${username}" is invalid: must start with a lowercase letter and contain only a-z, 0-9, _ or -`);
|
|
1127
|
+
}
|
|
1022
1128
|
/** Reference actions are served from the `/explore` page. */
|
|
1023
1129
|
const REFERENCES_PATH = "/explore";
|
|
1024
1130
|
const DEFAULT_PAGE_LIMIT = 20;
|
|
@@ -1046,10 +1152,12 @@ var ReferencesService = class {
|
|
|
1046
1152
|
}
|
|
1047
1153
|
/** Create a reusable reference from an uploaded image/audio URL. */
|
|
1048
1154
|
async create(input) {
|
|
1155
|
+
const username = input.username ?? generateUsername(input.name);
|
|
1156
|
+
validateUsername(username);
|
|
1049
1157
|
const session = await this.auth.getSession();
|
|
1050
1158
|
const body = {
|
|
1051
1159
|
name: input.name,
|
|
1052
|
-
username
|
|
1160
|
+
username,
|
|
1053
1161
|
image_url: input.image_url,
|
|
1054
1162
|
audio_url: input.audio_url ?? null,
|
|
1055
1163
|
modality: input.modality ?? "image",
|
|
@@ -1242,99 +1350,301 @@ const SEED_SNAPSHOT = {
|
|
|
1242
1350
|
}
|
|
1243
1351
|
};
|
|
1244
1352
|
//#endregion
|
|
1353
|
+
//#region lib/http.ts
|
|
1354
|
+
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";
|
|
1355
|
+
/**
|
|
1356
|
+
* HTTP statuses that mean "this action is unavailable on this host". A stale
|
|
1357
|
+
* (post-deploy) action hash surfaces as a 404 on the production host and as a
|
|
1358
|
+
* 409 ("Server Action unavailable") on the beta host — both mean the same
|
|
1359
|
+
* thing: invalidate the registry, re-discover, and (if still unavailable) fall
|
|
1360
|
+
* back to the next host.
|
|
1361
|
+
*/
|
|
1362
|
+
const UNAVAILABLE_STATUSES = /* @__PURE__ */ new Set([404, 409]);
|
|
1363
|
+
/**
|
|
1364
|
+
* Known RSC error digests mapped to human-readable messages.
|
|
1365
|
+
* Discovered via reverse engineering; the server does not expose error details.
|
|
1366
|
+
*/
|
|
1367
|
+
const KNOWN_DIGESTS = {
|
|
1368
|
+
"709776019": "Username already taken (duplicate character/reference username)",
|
|
1369
|
+
"1853426377": "Invalid username (must be 1–15 lowercase chars, start with a letter, only a-z 0-9 _ -)"
|
|
1370
|
+
};
|
|
1371
|
+
var HttpClient = class {
|
|
1372
|
+
registry;
|
|
1373
|
+
fetchImpl;
|
|
1374
|
+
baseUrl;
|
|
1375
|
+
targetKey;
|
|
1376
|
+
userAgent;
|
|
1377
|
+
constructor(registry, fetchImpl, baseUrl, targetKey, userAgent = DEFAULT_USER_AGENT) {
|
|
1378
|
+
this.registry = registry;
|
|
1379
|
+
this.fetchImpl = fetchImpl;
|
|
1380
|
+
this.baseUrl = baseUrl;
|
|
1381
|
+
this.targetKey = targetKey;
|
|
1382
|
+
this.userAgent = userAgent;
|
|
1383
|
+
}
|
|
1384
|
+
/** A response that means the action is unavailable (stale hash or gone). */
|
|
1385
|
+
isUnavailable(res) {
|
|
1386
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
1387
|
+
const isHtmlFallback = res.ok && contentType.includes("text/html");
|
|
1388
|
+
return UNAVAILABLE_STATUSES.has(res.status) || isHtmlFallback;
|
|
1389
|
+
}
|
|
1390
|
+
async callAction(params, retried = false) {
|
|
1391
|
+
const { hash, deploymentId } = await this.registry.getHash(params.action);
|
|
1392
|
+
const headers = {
|
|
1393
|
+
accept: "text/x-component",
|
|
1394
|
+
"content-type": "text/plain;charset=UTF-8",
|
|
1395
|
+
"next-action": hash,
|
|
1396
|
+
"x-deployment-id": deploymentId,
|
|
1397
|
+
origin: this.baseUrl,
|
|
1398
|
+
referer: `${this.baseUrl}${params.path}`,
|
|
1399
|
+
"user-agent": this.userAgent
|
|
1400
|
+
};
|
|
1401
|
+
if (params.session !== void 0) headers.cookie = `__session=${params.session}`;
|
|
1402
|
+
const res = await this.fetchImpl(`${this.baseUrl}${params.path}`, {
|
|
1403
|
+
method: "POST",
|
|
1404
|
+
headers,
|
|
1405
|
+
body: JSON.stringify(params.args)
|
|
1406
|
+
});
|
|
1407
|
+
if (this.isUnavailable(res) && !retried) {
|
|
1408
|
+
await safeText(res);
|
|
1409
|
+
await this.registry.invalidate();
|
|
1410
|
+
return this.callAction(params, true);
|
|
1411
|
+
}
|
|
1412
|
+
if (this.isUnavailable(res)) {
|
|
1413
|
+
await safeText(res);
|
|
1414
|
+
throw new ActionUnavailableError(params.action, this.targetKey);
|
|
1415
|
+
}
|
|
1416
|
+
if (!res.ok) {
|
|
1417
|
+
const body = await safeText(res);
|
|
1418
|
+
const digest = body.match(/E\{"digest":"([^"]+)"\}/)?.[1];
|
|
1419
|
+
const knownDigest = digest !== void 0 ? KNOWN_DIGESTS[digest] : void 0;
|
|
1420
|
+
const hint = knownDigest ? ` — ${knownDigest}` : digest ? ` (server digest: ${digest})` : "";
|
|
1421
|
+
const raw = body.length > 0 ? ` — ${body.slice(0, 200)}` : "";
|
|
1422
|
+
throw new MageSpaceError(`Action "${params.action}" failed: HTTP ${res.status}${hint}${raw}`);
|
|
1423
|
+
}
|
|
1424
|
+
return {
|
|
1425
|
+
data: parseServerActionResponse(await res.text()),
|
|
1426
|
+
headers: res.headers,
|
|
1427
|
+
deploymentId: res.headers.get("x-deployment-id") ?? deploymentId
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1430
|
+
};
|
|
1431
|
+
async function safeText(res) {
|
|
1432
|
+
try {
|
|
1433
|
+
return await res.text();
|
|
1434
|
+
} catch {
|
|
1435
|
+
return "";
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
//#endregion
|
|
1439
|
+
//#region lib/target-context.ts
|
|
1440
|
+
/**
|
|
1441
|
+
* A {@link TargetContext} bundles one host's {@link ActionRegistry} and
|
|
1442
|
+
* {@link HttpClient} so the client can hold one pair per target and switch by
|
|
1443
|
+
* selecting the other pair (rather than mutating a shared `baseUrl`).
|
|
1444
|
+
*/
|
|
1445
|
+
/**
|
|
1446
|
+
* Build the registry + HTTP client for a single target.
|
|
1447
|
+
*
|
|
1448
|
+
* @remarks
|
|
1449
|
+
* The caller's `cache` and the production `seed` are production-only concerns:
|
|
1450
|
+
* the bundled seed holds production hashes and the single-slot cache cannot be
|
|
1451
|
+
* namespaced per host. Non-production targets always discover fresh with their
|
|
1452
|
+
* own {@link InMemoryActionCache} and no seed.
|
|
1453
|
+
*/
|
|
1454
|
+
function createTargetContext(target, deps) {
|
|
1455
|
+
const isProduction = target.key === "production";
|
|
1456
|
+
const registry = new ActionRegistry({
|
|
1457
|
+
fetch: deps.fetch,
|
|
1458
|
+
baseUrl: target.baseUrl,
|
|
1459
|
+
cache: isProduction ? deps.cache : new InMemoryActionCache(),
|
|
1460
|
+
seed: isProduction ? deps.seed : void 0
|
|
1461
|
+
});
|
|
1462
|
+
return {
|
|
1463
|
+
target,
|
|
1464
|
+
registry,
|
|
1465
|
+
http: new HttpClient(registry, deps.fetch, target.baseUrl, target.key, deps.userAgent)
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
//#endregion
|
|
1245
1469
|
//#region lib/client.ts
|
|
1246
1470
|
/**
|
|
1247
1471
|
* {@link MageSpaceClient} — the public facade that wires together action
|
|
1248
1472
|
* discovery, authentication, generation, and history.
|
|
1473
|
+
*
|
|
1474
|
+
* The client models each mage.space host as a {@link Target} and holds an
|
|
1475
|
+
* ordered chain (production first, then beta). When the active host can no
|
|
1476
|
+
* longer serve a required action, the client advances to the next target and
|
|
1477
|
+
* retries once. The switch is sticky for the lifetime of the instance.
|
|
1249
1478
|
*/
|
|
1250
|
-
const DEFAULT_BASE_URL = "https://www.mage.space";
|
|
1251
1479
|
var MageSpaceClient = class {
|
|
1252
|
-
registry;
|
|
1253
1480
|
auth;
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1481
|
+
chain;
|
|
1482
|
+
auto;
|
|
1483
|
+
contextDeps;
|
|
1484
|
+
contexts = /* @__PURE__ */ new Map();
|
|
1485
|
+
activeIndex;
|
|
1486
|
+
activeContext;
|
|
1487
|
+
activeBundle;
|
|
1488
|
+
/**
|
|
1489
|
+
* Character management (search, CRUD, pagination, uploads).
|
|
1490
|
+
*
|
|
1491
|
+
* @remarks
|
|
1492
|
+
* Bound to the initial host (production by default) and does not follow an
|
|
1493
|
+
* automatic beta fallback. Construct a client with `fallback: { start: 'beta' }`
|
|
1494
|
+
* to use these against beta.
|
|
1495
|
+
*/
|
|
1257
1496
|
characters;
|
|
1258
|
-
/**
|
|
1497
|
+
/**
|
|
1498
|
+
* Reference asset management (upload, create, update, list, feed).
|
|
1499
|
+
*
|
|
1500
|
+
* @remarks
|
|
1501
|
+
* Bound to the initial host (production by default) and does not follow an
|
|
1502
|
+
* automatic beta fallback. Construct a client with `fallback: { start: 'beta' }`
|
|
1503
|
+
* to use these against beta.
|
|
1504
|
+
*/
|
|
1259
1505
|
references;
|
|
1260
1506
|
constructor(options) {
|
|
1261
1507
|
if (!options.refreshToken) throw new Error("MageSpaceClient requires a refreshToken");
|
|
1262
|
-
const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;
|
|
1263
1508
|
const fetchImpl = options.fetch ?? ((input, init) => fetch(input, init));
|
|
1264
|
-
|
|
1509
|
+
const fallback = options.fallback;
|
|
1510
|
+
const baseUrlPinned = typeof options.baseUrl === "string" && options.baseUrl.length > 0;
|
|
1511
|
+
if (baseUrlPinned && fallback !== void 0 && (fallback.chain !== void 0 || fallback.start !== void 0 || fallback.auto !== void 0)) throw new Error("baseUrl pins a single host and cannot be combined with fallback options");
|
|
1512
|
+
if (baseUrlPinned) {
|
|
1513
|
+
this.chain = [{
|
|
1514
|
+
key: "production",
|
|
1515
|
+
baseUrl: options.baseUrl,
|
|
1516
|
+
actionNames: {},
|
|
1517
|
+
pollStrategy: "byId"
|
|
1518
|
+
}];
|
|
1519
|
+
this.auto = false;
|
|
1520
|
+
} else {
|
|
1521
|
+
const chain = fallback?.chain ?? DEFAULT_TARGET_CHAIN;
|
|
1522
|
+
if (chain.length === 0) throw new Error("fallback.chain must be a non-empty array");
|
|
1523
|
+
this.chain = chain;
|
|
1524
|
+
this.auto = fallback?.auto ?? true;
|
|
1525
|
+
}
|
|
1526
|
+
const startKey = baseUrlPinned ? "production" : fallback?.start ?? "production";
|
|
1527
|
+
const startIndex = this.chain.findIndex((t) => t.key === startKey);
|
|
1528
|
+
if (startIndex === -1) throw new Error(`fallback.start '${startKey}' is not in the target chain`);
|
|
1529
|
+
this.contextDeps = {
|
|
1265
1530
|
fetch: fetchImpl,
|
|
1266
|
-
|
|
1531
|
+
userAgent: options.userAgent,
|
|
1267
1532
|
cache: options.cache,
|
|
1268
1533
|
seed: options.seed ?? SEED_SNAPSHOT
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1534
|
+
};
|
|
1535
|
+
this.activeIndex = startIndex;
|
|
1536
|
+
this.activeContext = this.getContext(startIndex);
|
|
1271
1537
|
this.auth = new AuthManager({
|
|
1272
1538
|
refreshToken: options.refreshToken,
|
|
1273
|
-
http,
|
|
1539
|
+
getHttp: () => this.activeContext.http,
|
|
1274
1540
|
fetch: fetchImpl,
|
|
1275
1541
|
firebaseApiKey: options.firebaseApiKey
|
|
1276
1542
|
});
|
|
1277
|
-
this.
|
|
1278
|
-
this.
|
|
1279
|
-
this.
|
|
1280
|
-
|
|
1543
|
+
this.activeBundle = this.buildBundle(this.activeContext);
|
|
1544
|
+
this.characters = new CharactersService(this.activeContext.http, this.auth);
|
|
1545
|
+
this.references = new ReferencesService(this.activeContext.http, this.auth);
|
|
1546
|
+
}
|
|
1547
|
+
/** Lazily build (and memoise) the {@link TargetContext} for a chain index. */
|
|
1548
|
+
getContext(index) {
|
|
1549
|
+
const target = this.chain[index];
|
|
1550
|
+
const existing = this.contexts.get(target.key);
|
|
1551
|
+
if (existing !== void 0) return existing;
|
|
1552
|
+
const context = createTargetContext(target, this.contextDeps);
|
|
1553
|
+
this.contexts.set(target.key, context);
|
|
1554
|
+
return context;
|
|
1555
|
+
}
|
|
1556
|
+
/** Build the generation/history service bundle for a context. */
|
|
1557
|
+
buildBundle(context) {
|
|
1558
|
+
const pollOnce = (id) => this.withFallback((bundle) => bundle.generationService.getHistory(id));
|
|
1559
|
+
return {
|
|
1560
|
+
target: context.target,
|
|
1561
|
+
generationService: new GenerationService(context.http, this.auth, context.target, pollOnce),
|
|
1562
|
+
historyService: new HistoryService(context.http, this.auth, context.target)
|
|
1563
|
+
};
|
|
1564
|
+
}
|
|
1565
|
+
/** Advance to the next target (sticky). Returns `null` if none or `auto` is off. */
|
|
1566
|
+
advanceTarget() {
|
|
1567
|
+
if (this.auto === false || this.activeIndex >= this.chain.length - 1) return null;
|
|
1568
|
+
this.activeIndex += 1;
|
|
1569
|
+
this.activeContext = this.getContext(this.activeIndex);
|
|
1570
|
+
this.activeBundle = this.buildBundle(this.activeContext);
|
|
1571
|
+
return this.activeBundle;
|
|
1572
|
+
}
|
|
1573
|
+
/** Whether an error is a definitive action-unavailable signal (a fallback trigger). */
|
|
1574
|
+
shouldFallback(err) {
|
|
1575
|
+
return err instanceof ActionUnavailableError || err instanceof DiscoveryError && err.notFound === true;
|
|
1576
|
+
}
|
|
1577
|
+
/** Run `op` against the active bundle, switching to the next target once on a trigger. */
|
|
1578
|
+
async withFallback(op) {
|
|
1579
|
+
try {
|
|
1580
|
+
return await op(this.activeBundle);
|
|
1581
|
+
} catch (err) {
|
|
1582
|
+
if (!this.shouldFallback(err)) throw err;
|
|
1583
|
+
if (this.advanceTarget() === null) throw err;
|
|
1584
|
+
this.auth.resetSession();
|
|
1585
|
+
return op(this.activeBundle);
|
|
1586
|
+
}
|
|
1281
1587
|
}
|
|
1282
1588
|
/** Submit a generation job; returns its `historyId`. */
|
|
1283
1589
|
generate(options) {
|
|
1284
|
-
return this.generationService.submit(options);
|
|
1590
|
+
return this.withFallback((b) => b.generationService.submit(options));
|
|
1285
1591
|
}
|
|
1286
1592
|
/** Submit a video generation job (Berry-2 family); returns its `historyId`. */
|
|
1287
1593
|
generateVideo(options) {
|
|
1288
|
-
return this.generationService.submitVideo(options);
|
|
1594
|
+
return this.withFallback((b) => b.generationService.submitVideo(options));
|
|
1289
1595
|
}
|
|
1290
1596
|
/** Fetch the current state of a generation by id. */
|
|
1291
1597
|
getResult(historyId) {
|
|
1292
|
-
return this.generationService.getHistory(historyId);
|
|
1598
|
+
return this.withFallback((b) => b.generationService.getHistory(historyId));
|
|
1293
1599
|
}
|
|
1294
1600
|
/** Poll until a generation finishes (or fails / times out). */
|
|
1295
1601
|
waitForResult(historyId, options) {
|
|
1296
|
-
return this.generationService.waitForResult(historyId, options);
|
|
1602
|
+
return this.withFallback((b) => b.generationService.waitForResult(historyId, options));
|
|
1297
1603
|
}
|
|
1298
1604
|
/** Cancel a running generation job. */
|
|
1299
1605
|
cancelJob(historyId) {
|
|
1300
|
-
return this.generationService.cancelJob(historyId);
|
|
1606
|
+
return this.withFallback((b) => b.generationService.cancelJob(historyId));
|
|
1301
1607
|
}
|
|
1302
1608
|
/** List past generations for the authenticated user. */
|
|
1303
1609
|
listHistory(options) {
|
|
1304
|
-
return this.historyService.list(options);
|
|
1610
|
+
return this.withFallback((b) => b.historyService.list(options));
|
|
1305
1611
|
}
|
|
1306
1612
|
/** List the user's permanently saved creations. */
|
|
1307
1613
|
listCreations(options) {
|
|
1308
|
-
return this.historyService.listCreations(options);
|
|
1614
|
+
return this.withFallback((b) => b.historyService.listCreations(options));
|
|
1309
1615
|
}
|
|
1310
1616
|
/** Save a completed generation result as a permanent creation. */
|
|
1311
1617
|
saveCreation(result) {
|
|
1312
|
-
return this.historyService.saveCreation(result);
|
|
1618
|
+
return this.withFallback((b) => b.historyService.saveCreation(result));
|
|
1313
1619
|
}
|
|
1314
1620
|
/** Permanently delete a history item. Irreversible — requires `{ confirm: true }`. */
|
|
1315
1621
|
deleteHistory(historyId, options) {
|
|
1316
|
-
return this.historyService.deleteHistory(historyId, options);
|
|
1622
|
+
return this.withFallback((b) => b.historyService.deleteHistory(historyId, options));
|
|
1317
1623
|
}
|
|
1318
1624
|
/** Permanently delete many history items. Irreversible — requires `{ confirm: true }`. */
|
|
1319
1625
|
deleteManyHistories(historyIds, options) {
|
|
1320
|
-
return this.historyService.deleteManyHistories(historyIds, options);
|
|
1626
|
+
return this.withFallback((b) => b.historyService.deleteManyHistories(historyIds, options));
|
|
1321
1627
|
}
|
|
1322
1628
|
/** Permanently delete a saved creation. Irreversible — requires `{ confirm: true }`. */
|
|
1323
1629
|
deleteCreation(creationId, options) {
|
|
1324
|
-
return this.historyService.deleteCreation(creationId, options);
|
|
1630
|
+
return this.withFallback((b) => b.historyService.deleteCreation(creationId, options));
|
|
1325
1631
|
}
|
|
1326
1632
|
/** Permanently delete many creations. Irreversible — requires `{ confirm: true }`. */
|
|
1327
1633
|
deleteManyCreations(creationIds, options) {
|
|
1328
|
-
return this.historyService.deleteManyCreations(creationIds, options);
|
|
1634
|
+
return this.withFallback((b) => b.historyService.deleteManyCreations(creationIds, options));
|
|
1329
1635
|
}
|
|
1330
|
-
/** Force re-discovery of the Server Action hashes. */
|
|
1636
|
+
/** Force re-discovery of the Server Action hashes on the active host. */
|
|
1331
1637
|
refreshActions() {
|
|
1332
|
-
return this.registry.refresh();
|
|
1638
|
+
return this.activeContext.registry.refresh();
|
|
1333
1639
|
}
|
|
1334
1640
|
/** Return the authenticated user's Firebase uid. */
|
|
1335
1641
|
getUid() {
|
|
1336
1642
|
return this.auth.getUserId();
|
|
1337
1643
|
}
|
|
1644
|
+
/** Return the key of the currently active target (`'production'` or `'beta'`). */
|
|
1645
|
+
getActiveTarget() {
|
|
1646
|
+
return this.chain[this.activeIndex].key;
|
|
1647
|
+
}
|
|
1338
1648
|
};
|
|
1339
1649
|
//#endregion
|
|
1340
1650
|
//#region lib/index.ts
|
|
@@ -1342,13 +1652,17 @@ var MageSpaceClient = class {
|
|
|
1342
1652
|
const VERSION = "0.0.0";
|
|
1343
1653
|
//#endregion
|
|
1344
1654
|
exports.ActionRegistry = ActionRegistry;
|
|
1655
|
+
exports.ActionUnavailableError = ActionUnavailableError;
|
|
1345
1656
|
exports.AuthError = AuthError;
|
|
1657
|
+
exports.BETA_TARGET = BETA_TARGET;
|
|
1346
1658
|
exports.CharactersService = CharactersService;
|
|
1659
|
+
exports.DEFAULT_TARGET_CHAIN = DEFAULT_TARGET_CHAIN;
|
|
1347
1660
|
exports.DiscoveryError = DiscoveryError;
|
|
1348
1661
|
exports.GenerationError = GenerationError;
|
|
1349
1662
|
exports.InMemoryActionCache = InMemoryActionCache;
|
|
1350
1663
|
exports.MageSpaceClient = MageSpaceClient;
|
|
1351
1664
|
exports.MageSpaceError = MageSpaceError;
|
|
1665
|
+
exports.PRODUCTION_TARGET = PRODUCTION_TARGET;
|
|
1352
1666
|
exports.ReferencesService = ReferencesService;
|
|
1353
1667
|
exports.RscParseError = RscParseError;
|
|
1354
1668
|
exports.SEED_SNAPSHOT = SEED_SNAPSHOT;
|
|
@@ -1358,5 +1672,6 @@ exports.VERSION = VERSION;
|
|
|
1358
1672
|
exports.discoverActions = discoverActions;
|
|
1359
1673
|
exports.parseFlightRows = parseFlightRows;
|
|
1360
1674
|
exports.parseServerActionResponse = parseServerActionResponse;
|
|
1675
|
+
exports.resolveActionName = resolveActionName;
|
|
1361
1676
|
exports.resolveFlightValue = resolveFlightValue;
|
|
1362
1677
|
exports.unwrapResult = unwrapResult;
|