@genex-ai/embed-sdk 0.17.0 → 0.21.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 +243 -0
- package/dist/chunk-Q476GJHI.js +249 -0
- package/dist/{chunk-DNLXNEPL.js → chunk-WJ4KFQLC.js} +223 -250
- package/dist/development.d.ts +44 -0
- package/dist/development.js +99 -0
- package/dist/index.d.ts +73 -1
- package/dist/index.js +16 -1
- package/dist/sentry.js +2 -1
- package/package.json +8 -2
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NATIVE_CHANNEL,
|
|
3
|
+
NATIVE_PROTOCOL_VERSION,
|
|
4
|
+
NATIVE_RECEIVER,
|
|
5
|
+
encodeBridgeMessage,
|
|
6
|
+
hasNativeMarker,
|
|
7
|
+
isGenerationId,
|
|
8
|
+
isGenerationTerminal,
|
|
9
|
+
readBridgeNativeCommand,
|
|
10
|
+
readNativeEntry
|
|
11
|
+
} from "./chunk-Q476GJHI.js";
|
|
12
|
+
|
|
1
13
|
// src/commerce.ts
|
|
2
14
|
var cfg = null;
|
|
3
15
|
function _initCommerce(c) {
|
|
@@ -105,234 +117,179 @@ async function buy(opts) {
|
|
|
105
117
|
return awaitOutcome(view.id, opts.timeoutMs ?? 18e4);
|
|
106
118
|
}
|
|
107
119
|
|
|
108
|
-
//
|
|
109
|
-
var
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
// src/generation.ts
|
|
121
|
+
var config = null;
|
|
122
|
+
function _initGeneration(value) {
|
|
123
|
+
config = value;
|
|
124
|
+
}
|
|
125
|
+
function must2() {
|
|
126
|
+
if (!config) throw new Error("genex generation: call initEmbed() first");
|
|
127
|
+
return config;
|
|
128
|
+
}
|
|
129
|
+
async function request(path, body) {
|
|
130
|
+
const c = must2();
|
|
131
|
+
const token = c.getToken();
|
|
132
|
+
if (!token) throw new Error("no_player_session");
|
|
133
|
+
const response = await fetch(`${c.apiUrl}/api/runtime${path}`, {
|
|
134
|
+
method: body === void 0 ? "GET" : "POST",
|
|
135
|
+
credentials: "omit",
|
|
136
|
+
cache: "no-store",
|
|
137
|
+
headers: { Authorization: `Bearer ${token}`, ...body === void 0 ? {} : { "Content-Type": "application/json" } },
|
|
138
|
+
...body === void 0 ? {} : { body: JSON.stringify(body) }
|
|
139
|
+
});
|
|
140
|
+
const result = await response.json().catch(() => ({}));
|
|
141
|
+
if (!response.ok) throw new Error(typeof result.error === "string" ? result.error : "generation_unavailable");
|
|
142
|
+
return result;
|
|
125
143
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
var ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
129
|
-
var LOOKUP = (() => {
|
|
130
|
-
const table = new Array(128).fill(-1);
|
|
131
|
-
for (let i = 0; i < ALPHABET.length; i++) table[ALPHABET.charCodeAt(i)] = i;
|
|
132
|
-
return table;
|
|
133
|
-
})();
|
|
134
|
-
function utf8Bytes(text) {
|
|
135
|
-
const out = [];
|
|
136
|
-
for (let i = 0; i < text.length; i++) {
|
|
137
|
-
let code = text.charCodeAt(i);
|
|
138
|
-
if (code >= 55296 && code <= 56319) {
|
|
139
|
-
const next = i + 1 < text.length ? text.charCodeAt(i + 1) : 0;
|
|
140
|
-
if (next >= 56320 && next <= 57343) {
|
|
141
|
-
code = 65536 + (code - 55296 << 10) + (next - 56320);
|
|
142
|
-
i++;
|
|
143
|
-
} else {
|
|
144
|
-
code = 65533;
|
|
145
|
-
}
|
|
146
|
-
} else if (code >= 56320 && code <= 57343) {
|
|
147
|
-
code = 65533;
|
|
148
|
-
}
|
|
149
|
-
if (code < 128) out.push(code);
|
|
150
|
-
else if (code < 2048) out.push(192 | code >> 6, 128 | code & 63);
|
|
151
|
-
else if (code < 65536) {
|
|
152
|
-
out.push(224 | code >> 12, 128 | code >> 6 & 63, 128 | code & 63);
|
|
153
|
-
} else {
|
|
154
|
-
out.push(
|
|
155
|
-
240 | code >> 18,
|
|
156
|
-
128 | code >> 12 & 63,
|
|
157
|
-
128 | code >> 6 & 63,
|
|
158
|
-
128 | code & 63
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
return out;
|
|
144
|
+
function validEstimateCoins(value) {
|
|
145
|
+
return Number.isSafeInteger(value) && value >= 0 && value <= 1e6;
|
|
163
146
|
}
|
|
164
|
-
function
|
|
165
|
-
|
|
166
|
-
for (let i = 0; i < bytes.length; ) {
|
|
167
|
-
const b0 = bytes[i];
|
|
168
|
-
let code;
|
|
169
|
-
let size;
|
|
170
|
-
if (b0 < 128) {
|
|
171
|
-
code = b0;
|
|
172
|
-
size = 1;
|
|
173
|
-
} else if ((b0 & 224) === 192) {
|
|
174
|
-
code = b0 & 31;
|
|
175
|
-
size = 2;
|
|
176
|
-
} else if ((b0 & 240) === 224) {
|
|
177
|
-
code = b0 & 15;
|
|
178
|
-
size = 3;
|
|
179
|
-
} else if ((b0 & 248) === 240) {
|
|
180
|
-
code = b0 & 7;
|
|
181
|
-
size = 4;
|
|
182
|
-
} else return null;
|
|
183
|
-
if (i + size > bytes.length) return null;
|
|
184
|
-
for (let k = 1; k < size; k++) {
|
|
185
|
-
const b = bytes[i + k];
|
|
186
|
-
if ((b & 192) !== 128) return null;
|
|
187
|
-
code = code << 6 | b & 63;
|
|
188
|
-
}
|
|
189
|
-
if (size === 2 && code < 128) return null;
|
|
190
|
-
if (size === 3 && (code < 2048 || code >= 55296 && code <= 57343)) return null;
|
|
191
|
-
if (size === 4 && (code < 65536 || code > 1114111)) return null;
|
|
192
|
-
if (code > 65535) {
|
|
193
|
-
const v = code - 65536;
|
|
194
|
-
out += String.fromCharCode(55296 + (v >> 10), 56320 + (v & 1023));
|
|
195
|
-
} else {
|
|
196
|
-
out += String.fromCharCode(code);
|
|
197
|
-
}
|
|
198
|
-
i += size;
|
|
199
|
-
}
|
|
200
|
-
return out;
|
|
147
|
+
function getGenerationModels() {
|
|
148
|
+
return request("/models");
|
|
201
149
|
}
|
|
202
|
-
function
|
|
203
|
-
return
|
|
150
|
+
function isWorkflowIdentifier(value) {
|
|
151
|
+
return typeof value === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(value) && value !== "." && value !== "..";
|
|
204
152
|
}
|
|
205
|
-
function
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
for (let i = 0; i < bytes.length; i += 3) {
|
|
209
|
-
const b0 = bytes[i];
|
|
210
|
-
const b1 = bytes[i + 1];
|
|
211
|
-
const b2 = bytes[i + 2];
|
|
212
|
-
out += ALPHABET[b0 >> 2];
|
|
213
|
-
out += ALPHABET[(b0 & 3) << 4 | (b1 ?? 0) >> 4];
|
|
214
|
-
if (b1 === void 0) break;
|
|
215
|
-
out += ALPHABET[(b1 & 15) << 2 | (b2 ?? 0) >> 6];
|
|
216
|
-
if (b2 === void 0) break;
|
|
217
|
-
out += ALPHABET[b2 & 63];
|
|
218
|
-
}
|
|
219
|
-
return out;
|
|
153
|
+
function getWorkflowOfferings(workflowId) {
|
|
154
|
+
if (!isWorkflowIdentifier(workflowId)) return Promise.reject(new Error("invalid_workflow_id"));
|
|
155
|
+
return request(`/workflows/${encodeURIComponent(workflowId)}/offerings`);
|
|
220
156
|
}
|
|
221
|
-
function
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
-
let buffer = 0;
|
|
225
|
-
let bits = 0;
|
|
226
|
-
for (let i = 0; i < encoded.length; i++) {
|
|
227
|
-
const code = encoded.charCodeAt(i);
|
|
228
|
-
const value = code < 128 ? LOOKUP[code] : -1;
|
|
229
|
-
if (value < 0) return void 0;
|
|
230
|
-
buffer = buffer << 6 | value;
|
|
231
|
-
bits += 6;
|
|
232
|
-
if (bits >= 8) {
|
|
233
|
-
bits -= 8;
|
|
234
|
-
bytes.push(buffer >> bits & 255);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
if (bits > 0 && (buffer & (1 << bits) - 1) !== 0) return void 0;
|
|
238
|
-
const text = utf8Text(bytes);
|
|
239
|
-
if (text === null) return void 0;
|
|
240
|
-
try {
|
|
241
|
-
return JSON.parse(text);
|
|
242
|
-
} catch {
|
|
243
|
-
return void 0;
|
|
244
|
-
}
|
|
157
|
+
function getGeneration(generationId) {
|
|
158
|
+
if (!isGenerationId(generationId)) return Promise.reject(new Error("invalid_generation_id"));
|
|
159
|
+
return request(`/generations/${encodeURIComponent(generationId)}`);
|
|
245
160
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
return true;
|
|
261
|
-
}
|
|
262
|
-
var BASE_KEYS = ["channel", "v", "type", "slug", "attemptId", "nonce"];
|
|
263
|
-
function envelopeBaseIsValid(value) {
|
|
264
|
-
return value.channel === NATIVE_CHANNEL && value.v === NATIVE_PROTOCOL_VERSION && isBridgeSlug(value.slug) && isBridgeId(value.attemptId) && isBridgeId(value.nonce);
|
|
265
|
-
}
|
|
266
|
-
function parseBridgeNativeCommand(value) {
|
|
267
|
-
if (!isRecord(value) || !envelopeBaseIsValid(value)) return void 0;
|
|
268
|
-
switch (value.type) {
|
|
269
|
-
case "ticket":
|
|
270
|
-
if (!hasExactKeys(value, [...BASE_KEYS, "ticket", "expiresAt"])) return void 0;
|
|
271
|
-
if (typeof value.ticket !== "string" || value.ticket.length === 0 || value.ticket.length > MAX_TICKET_LENGTH) {
|
|
272
|
-
return void 0;
|
|
273
|
-
}
|
|
274
|
-
if (typeof value.expiresAt !== "string" || value.expiresAt.length > MAX_EXPIRES_AT_LENGTH || !ISO_INSTANT_RE.test(value.expiresAt)) {
|
|
275
|
-
return void 0;
|
|
276
|
-
}
|
|
277
|
-
return value;
|
|
278
|
-
case "guest":
|
|
279
|
-
case "cancel":
|
|
280
|
-
if (!hasExactKeys(value, BASE_KEYS)) return void 0;
|
|
281
|
-
return value;
|
|
282
|
-
case "foreground":
|
|
283
|
-
if (!hasExactKeys(value, [...BASE_KEYS, "active"])) return void 0;
|
|
284
|
-
if (typeof value.active !== "boolean") return void 0;
|
|
285
|
-
return value;
|
|
286
|
-
case "network":
|
|
287
|
-
if (!hasExactKeys(value, [...BASE_KEYS, "online"])) return void 0;
|
|
288
|
-
if (typeof value.online !== "boolean") return void 0;
|
|
289
|
-
return value;
|
|
290
|
-
default:
|
|
291
|
-
return void 0;
|
|
292
|
-
}
|
|
161
|
+
function outcome(view) {
|
|
162
|
+
if (!isGenerationTerminal(view.status)) return void 0;
|
|
163
|
+
return {
|
|
164
|
+
status: view.status,
|
|
165
|
+
generationId: view.id,
|
|
166
|
+
...billingReceipt(view),
|
|
167
|
+
...view.status === "succeeded" ? {
|
|
168
|
+
output: view.output,
|
|
169
|
+
source: view.source,
|
|
170
|
+
...view.modelProvenance ? { modelProvenance: view.modelProvenance } : {}
|
|
171
|
+
} : {},
|
|
172
|
+
...view.error ? { error: view.error } : {}
|
|
173
|
+
};
|
|
293
174
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
if (message === void 0) return { ok: false, reason: "invalid" };
|
|
303
|
-
return { ok: true, message };
|
|
304
|
-
}
|
|
305
|
-
function readBridgeNativeCommand(raw) {
|
|
306
|
-
return read(raw, parseBridgeNativeCommand);
|
|
307
|
-
}
|
|
308
|
-
function encodeBridgeMessage(message) {
|
|
309
|
-
const encoded = encodeBase64UrlJson(message);
|
|
310
|
-
if (encoded.length > MAX_BRIDGE_MESSAGE_BYTES) return void 0;
|
|
311
|
-
return encoded;
|
|
175
|
+
function billingReceipt(view) {
|
|
176
|
+
return {
|
|
177
|
+
...view.billingStatus !== void 0 ? { billingStatus: view.billingStatus } : {},
|
|
178
|
+
...view.reservedCoins !== void 0 ? { reservedCoins: view.reservedCoins } : {},
|
|
179
|
+
...view.reservedDisplayUsdCents !== void 0 ? { reservedDisplayUsdCents: view.reservedDisplayUsdCents } : {},
|
|
180
|
+
...view.chargedCoins !== void 0 ? { chargedCoins: view.chargedCoins } : {},
|
|
181
|
+
...view.chargedDisplayUsdCents !== void 0 ? { chargedDisplayUsdCents: view.chargedDisplayUsdCents } : {}
|
|
182
|
+
};
|
|
312
183
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const query = search.charAt(0) === "?" ? search.slice(1) : search;
|
|
317
|
-
if (query.length === 0) return null;
|
|
318
|
-
for (const pair of query.split("&")) {
|
|
319
|
-
const eq = pair.indexOf("=");
|
|
320
|
-
if (eq === -1) continue;
|
|
321
|
-
if (pair.slice(0, eq) === key) return pair.slice(eq + 1);
|
|
184
|
+
function authorizationOutcome(view) {
|
|
185
|
+
if (["queued", "dispatching", "awaiting_external", "succeeded"].includes(view.status)) {
|
|
186
|
+
return { status: "authorized", generationId: view.id, ...billingReceipt(view), ...view.funding ? { funding: view.funding } : {} };
|
|
322
187
|
}
|
|
323
|
-
return
|
|
188
|
+
if (!isGenerationTerminal(view.status)) return void 0;
|
|
189
|
+
return {
|
|
190
|
+
status: view.status === "canceled" || view.status === "expired" ? view.status : "failed",
|
|
191
|
+
generationId: view.id,
|
|
192
|
+
...billingReceipt(view),
|
|
193
|
+
...view.error ? { error: view.error } : view.status === "unknown" ? { error: "unknown_execution" } : {}
|
|
194
|
+
};
|
|
324
195
|
}
|
|
325
|
-
function
|
|
326
|
-
|
|
196
|
+
async function poll(generationId, timeoutMs, readOutcome, popup) {
|
|
197
|
+
const deadline = Date.now() + timeoutMs;
|
|
198
|
+
while (Date.now() < deadline) {
|
|
199
|
+
try {
|
|
200
|
+
const view = await getGeneration(generationId);
|
|
201
|
+
const result = readOutcome(view);
|
|
202
|
+
if (result) return result;
|
|
203
|
+
if (popup?.closed && view.status === "requires_confirmation") {
|
|
204
|
+
const canceled = await request(`/generations/${generationId}/cancel`, {});
|
|
205
|
+
const cancelResult = readOutcome(canceled);
|
|
206
|
+
if (cancelResult) return cancelResult;
|
|
207
|
+
}
|
|
208
|
+
} catch {
|
|
209
|
+
}
|
|
210
|
+
await new Promise((resolve) => setTimeout(resolve, 750));
|
|
211
|
+
}
|
|
212
|
+
return { status: "pending", generationId, error: "wait_timeout" };
|
|
213
|
+
}
|
|
214
|
+
function waitForGeneration(generationId, timeoutMs = 6e5) {
|
|
215
|
+
if (!isGenerationId(generationId)) return Promise.resolve({ status: "failed", error: "invalid_generation_id" });
|
|
216
|
+
return poll(generationId, timeoutMs, outcome);
|
|
217
|
+
}
|
|
218
|
+
async function generate(options) {
|
|
219
|
+
if (!validEstimateCoins(options.estimateCoins)) return { status: "failed", error: "invalid_estimate_coins" };
|
|
220
|
+
const { timeoutMs, idempotencyKey, ...input } = options;
|
|
221
|
+
return startGeneration(
|
|
222
|
+
"/generations",
|
|
223
|
+
{ ...input, idempotencyKey },
|
|
224
|
+
timeoutMs ?? 6e5,
|
|
225
|
+
outcome
|
|
226
|
+
);
|
|
327
227
|
}
|
|
328
|
-
function
|
|
329
|
-
if (!
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
228
|
+
async function requestWorkflow(options) {
|
|
229
|
+
if (!validEstimateCoins(options.estimateCoins)) return { status: "failed", error: "invalid_estimate_coins" };
|
|
230
|
+
if (!isWorkflowIdentifier(options.workflowId)) return { status: "failed", error: "invalid_workflow_id" };
|
|
231
|
+
if (!isWorkflowIdentifier(options.offeringId)) return { status: "failed", error: "invalid_offering_id" };
|
|
232
|
+
return startGeneration(`/workflows/${encodeURIComponent(options.workflowId)}/requests`, {
|
|
233
|
+
offeringId: options.offeringId,
|
|
234
|
+
input: options.input,
|
|
235
|
+
estimateCoins: options.estimateCoins,
|
|
236
|
+
...options.allowExternal !== void 0 ? { allowExternal: options.allowExternal } : {},
|
|
237
|
+
idempotencyKey: options.idempotencyKey
|
|
238
|
+
}, options.timeoutMs ?? 6e5, authorizationOutcome, options);
|
|
239
|
+
}
|
|
240
|
+
async function resumeWorkflow(generationId, options = {}) {
|
|
241
|
+
if (!isGenerationId(generationId)) return { status: "failed", error: "invalid_generation_id" };
|
|
242
|
+
return startGeneration("", {}, options.timeoutMs ?? 6e5, authorizationOutcome, void 0, generationId);
|
|
243
|
+
}
|
|
244
|
+
async function startGeneration(path, body, timeoutMs, readOutcome, workflow, resumeId) {
|
|
245
|
+
const c = must2();
|
|
246
|
+
if (c.isLocalTest()) return { status: "failed", error: "local_test_unsupported" };
|
|
247
|
+
if (c.isNative()) return { status: "failed", error: "native_unsupported" };
|
|
248
|
+
if (typeof window === "undefined") return { status: "failed", error: "no_window" };
|
|
249
|
+
if (!c.getToken()) return { status: "failed", error: "no_player_session" };
|
|
250
|
+
const parentOrigin2 = c.getParentOrigin();
|
|
251
|
+
if (c.isEmbedded() && (!parentOrigin2 || !c.dashboardOrigins.includes(parentOrigin2))) {
|
|
252
|
+
return { status: "failed", error: "untrusted_parent" };
|
|
253
|
+
}
|
|
254
|
+
let popup = null;
|
|
255
|
+
let dashboard;
|
|
256
|
+
if (!c.isEmbedded()) {
|
|
257
|
+
try {
|
|
258
|
+
dashboard = new URL(c.dashboardOrigins[0]);
|
|
259
|
+
if (dashboard.protocol !== "https:" && !(dashboard.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(dashboard.hostname))) throw new Error();
|
|
260
|
+
} catch {
|
|
261
|
+
return { status: "failed", error: "invalid_dashboard_origin" };
|
|
262
|
+
}
|
|
263
|
+
popup = window.open(`${dashboard.origin}/play/generate`, "_blank", "popup,width=480,height=760");
|
|
264
|
+
if (!popup) return { status: "failed", error: "popup_blocked" };
|
|
265
|
+
try {
|
|
266
|
+
popup.opener = null;
|
|
267
|
+
} catch {
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
try {
|
|
271
|
+
const view = resumeId ? await getGeneration(resumeId) : await request(path, { ...body, idempotencyKey: body.idempotencyKey ?? crypto.randomUUID() });
|
|
272
|
+
if (resumeId && (view.id !== resumeId || view.kind !== "workflow")) throw new Error("invalid_generation_response");
|
|
273
|
+
if (!isGenerationId(view.id) || workflow && (view.kind !== "workflow" || view.workflow?.id !== workflow.workflowId || view.workflow.offeringId !== workflow.offeringId)) {
|
|
274
|
+
throw new Error("invalid_generation_response");
|
|
275
|
+
}
|
|
276
|
+
const result = readOutcome(view);
|
|
277
|
+
const needsReceipt = view.billingStatus === "pending" || view.quote?.billingPolicy !== void 0 && (view.chargedCoins ?? 0) > 0;
|
|
278
|
+
if (result && result.status !== "authorized" && !needsReceipt) {
|
|
279
|
+
popup?.close();
|
|
280
|
+
return result;
|
|
281
|
+
}
|
|
282
|
+
if (popup && dashboard) {
|
|
283
|
+
if (!popup.closed) popup.location.href = `${dashboard.origin}/play/generate/${encodeURIComponent(view.id)}`;
|
|
284
|
+
} else if (parentOrigin2) {
|
|
285
|
+
window.parent.postMessage({ type: "genex:generation:confirm", v: 1, generationId: view.id }, parentOrigin2);
|
|
286
|
+
}
|
|
287
|
+
if (result) return result;
|
|
288
|
+
return poll(view.id, timeoutMs, readOutcome, popup);
|
|
289
|
+
} catch (error) {
|
|
290
|
+
popup?.close();
|
|
291
|
+
return { status: "failed", error: error instanceof Error ? error.message : "generation_unavailable" };
|
|
292
|
+
}
|
|
336
293
|
}
|
|
337
294
|
|
|
338
295
|
// src/index.ts
|
|
@@ -341,8 +298,8 @@ var RETRY_FLAG = "genex:embed:retry";
|
|
|
341
298
|
var POPOVER_DISMISSED_FLAG = "genex:guest:popover-dismissed";
|
|
342
299
|
var LOCAL_AUTH_FLAG = "genex:embed:local-auth";
|
|
343
300
|
var PLAYER_ID_KEY = "genex:player";
|
|
344
|
-
var SDK_VERSION = "0.
|
|
345
|
-
var
|
|
301
|
+
var SDK_VERSION = "0.21.0";
|
|
302
|
+
var config2 = null;
|
|
346
303
|
var state = "pending";
|
|
347
304
|
var user = null;
|
|
348
305
|
var localTestMode = false;
|
|
@@ -463,16 +420,25 @@ function initEmbed(cfg2) {
|
|
|
463
420
|
clearTimeout(unattendedWaiterTimer);
|
|
464
421
|
unattendedWaiterTimer = void 0;
|
|
465
422
|
}
|
|
466
|
-
|
|
423
|
+
config2 = {
|
|
467
424
|
slug: cfg2.slug,
|
|
468
425
|
apiUrl: cfg2.apiUrl.replace(/\/$/, ""),
|
|
469
426
|
dashboardOrigins: [...cfg2.dashboardOrigins]
|
|
470
427
|
};
|
|
471
428
|
state = "pending";
|
|
472
429
|
_initCommerce({
|
|
473
|
-
apiUrl:
|
|
474
|
-
dashboardOrigins:
|
|
430
|
+
apiUrl: config2.apiUrl,
|
|
431
|
+
dashboardOrigins: config2.dashboardOrigins,
|
|
432
|
+
getToken: () => embedToken,
|
|
433
|
+
isEmbedded,
|
|
434
|
+
isNative: inNativeMode,
|
|
435
|
+
isLocalTest: () => localTestMode
|
|
436
|
+
});
|
|
437
|
+
_initGeneration({
|
|
438
|
+
apiUrl: config2.apiUrl,
|
|
439
|
+
dashboardOrigins: config2.dashboardOrigins,
|
|
475
440
|
getToken: () => embedToken,
|
|
441
|
+
getParentOrigin: () => parentOrigin,
|
|
476
442
|
isEmbedded,
|
|
477
443
|
isNative: inNativeMode,
|
|
478
444
|
isLocalTest: () => localTestMode
|
|
@@ -566,7 +532,7 @@ function on(event, cb) {
|
|
|
566
532
|
}
|
|
567
533
|
var KEEPALIVE_MAX_BYTES = 32 * 1024;
|
|
568
534
|
async function ensurePlayer() {
|
|
569
|
-
if (!
|
|
535
|
+
if (!config2) throw new Error("genex embed: call initEmbed() first");
|
|
570
536
|
if (state === "pending") await waitForPlayer();
|
|
571
537
|
if (state === "blocked") throw new Error("genex embed auth: blocked");
|
|
572
538
|
}
|
|
@@ -575,7 +541,7 @@ function stateFetch(path, init = {}) {
|
|
|
575
541
|
const headers = { Authorization: `Bearer ${embedToken}` };
|
|
576
542
|
if (init.body !== void 0) headers["Content-Type"] = "application/json";
|
|
577
543
|
if (init.ifVersion !== void 0) headers["If-Match"] = String(init.ifVersion);
|
|
578
|
-
return doFetch(`${
|
|
544
|
+
return doFetch(`${config2.apiUrl}/api/projects/${encodeURIComponent(config2.slug)}${path}`, {
|
|
579
545
|
method: init.method ?? "GET",
|
|
580
546
|
headers,
|
|
581
547
|
body: init.body,
|
|
@@ -665,7 +631,7 @@ async function submitScore(score, opts) {
|
|
|
665
631
|
return { submitted: true, best: body.best, improved: body.improved };
|
|
666
632
|
}
|
|
667
633
|
async function getLeaderboard(opts) {
|
|
668
|
-
if (!
|
|
634
|
+
if (!config2) throw new Error("genex embed: call initEmbed() first");
|
|
669
635
|
if (localTestMode) return { items: [], me: null };
|
|
670
636
|
if (state === "pending") {
|
|
671
637
|
try {
|
|
@@ -681,7 +647,7 @@ async function getLeaderboard(opts) {
|
|
|
681
647
|
const headers = {};
|
|
682
648
|
if (embedToken) headers.Authorization = `Bearer ${embedToken}`;
|
|
683
649
|
const res = await doFetch(
|
|
684
|
-
`${
|
|
650
|
+
`${config2.apiUrl}/api/projects/${encodeURIComponent(config2.slug)}/leaderboard${qs ? `?${qs}` : ""}`,
|
|
685
651
|
{ headers }
|
|
686
652
|
);
|
|
687
653
|
if (!res.ok) throw new Error(`genex leaderboard failed (${res.status})`);
|
|
@@ -728,7 +694,7 @@ function acceptsTicket() {
|
|
|
728
694
|
return (state === "pending" || state === "guest") && !redeeming;
|
|
729
695
|
}
|
|
730
696
|
async function handleParentMessage(event) {
|
|
731
|
-
if (!
|
|
697
|
+
if (!config2) return;
|
|
732
698
|
const data = event.data;
|
|
733
699
|
if (!data || data.v !== PROTOCOL_VERSION) return;
|
|
734
700
|
const isTicket = data.type === "genex:embed:ticket";
|
|
@@ -736,7 +702,7 @@ async function handleParentMessage(event) {
|
|
|
736
702
|
if (!isTicket && !isGuest) return;
|
|
737
703
|
if (isTicket && (!acceptsTicket() || typeof data.ticket !== "string" || !data.ticket)) return;
|
|
738
704
|
if (isGuest && (state !== "pending" || redeeming || requestingGuest)) return;
|
|
739
|
-
if (!
|
|
705
|
+
if (!config2.dashboardOrigins.includes(event.origin)) {
|
|
740
706
|
const fresh = await fetchDashboardOrigins();
|
|
741
707
|
if (isTicket && !acceptsTicket()) return;
|
|
742
708
|
if (isGuest && (state !== "pending" || redeeming || requestingGuest)) return;
|
|
@@ -773,13 +739,13 @@ function inNativeMode() {
|
|
|
773
739
|
}
|
|
774
740
|
function postToNative(message) {
|
|
775
741
|
const w = win();
|
|
776
|
-
if (!w || !
|
|
742
|
+
if (!w || !config2 || !nativeEntry) return;
|
|
777
743
|
const bridge = nativeBridge(w);
|
|
778
744
|
if (!bridge) return;
|
|
779
745
|
const encoded = encodeBridgeMessage({
|
|
780
746
|
channel: NATIVE_CHANNEL,
|
|
781
747
|
v: NATIVE_PROTOCOL_VERSION,
|
|
782
|
-
slug:
|
|
748
|
+
slug: config2.slug,
|
|
783
749
|
attemptId: nativeEntry.attemptId,
|
|
784
750
|
nonce: nativeEntry.nonce,
|
|
785
751
|
...message
|
|
@@ -806,11 +772,11 @@ function startNativeHandshake(w, entry) {
|
|
|
806
772
|
}, handshakeTimeoutMs);
|
|
807
773
|
}
|
|
808
774
|
async function handleNativeCommand(payload) {
|
|
809
|
-
if (!
|
|
810
|
-
const
|
|
811
|
-
if (!
|
|
812
|
-
const command =
|
|
813
|
-
if (command.slug !==
|
|
775
|
+
if (!config2 || !nativeEntry) return;
|
|
776
|
+
const read = readBridgeNativeCommand(payload);
|
|
777
|
+
if (!read.ok) return;
|
|
778
|
+
const command = read.message;
|
|
779
|
+
if (command.slug !== config2.slug || command.attemptId !== nativeEntry.attemptId || command.nonce !== nativeEntry.nonce) {
|
|
814
780
|
return;
|
|
815
781
|
}
|
|
816
782
|
switch (command.type) {
|
|
@@ -939,33 +905,33 @@ function readFragment(w) {
|
|
|
939
905
|
return { ticket, guest };
|
|
940
906
|
}
|
|
941
907
|
async function redirectToAuthorize(w, opts) {
|
|
942
|
-
if (!
|
|
908
|
+
if (!config2 || localTestMode) return;
|
|
943
909
|
if (inNativeMode()) return;
|
|
944
|
-
const origin = opts?.guestOk ?
|
|
910
|
+
const origin = opts?.guestOk ? config2.dashboardOrigins[0] ?? (await fetchDashboardOrigins())[0] : (await fetchDashboardOrigins())[0] ?? config2.dashboardOrigins[0];
|
|
945
911
|
if (!origin) {
|
|
946
912
|
enterBlocked();
|
|
947
913
|
return;
|
|
948
914
|
}
|
|
949
915
|
const returnTo = w.location.origin + w.location.pathname;
|
|
950
916
|
const guestParam = opts?.guestOk ? "&guest=ok" : "";
|
|
951
|
-
const url = `${origin}/play/authorize?returnTo=${encodeURIComponent(returnTo)}&slug=${encodeURIComponent(
|
|
917
|
+
const url = `${origin}/play/authorize?returnTo=${encodeURIComponent(returnTo)}&slug=${encodeURIComponent(config2.slug)}${guestParam}`;
|
|
952
918
|
w.location.replace(url);
|
|
953
919
|
}
|
|
954
920
|
async function fetchDashboardOrigins() {
|
|
955
|
-
if (!
|
|
921
|
+
if (!config2) return [];
|
|
956
922
|
try {
|
|
957
|
-
const res = await doFetch(`${
|
|
958
|
-
if (!res.ok) return
|
|
923
|
+
const res = await doFetch(`${config2.apiUrl}/api/embed/dashboard-origins`);
|
|
924
|
+
if (!res.ok) return config2.dashboardOrigins;
|
|
959
925
|
const body = await res.json();
|
|
960
926
|
if (Array.isArray(body.origins) && body.origins.every((o) => typeof o === "string")) {
|
|
961
927
|
return body.origins;
|
|
962
928
|
}
|
|
963
929
|
} catch {
|
|
964
930
|
}
|
|
965
|
-
return
|
|
931
|
+
return config2.dashboardOrigins;
|
|
966
932
|
}
|
|
967
933
|
async function redeemTicket(ticket) {
|
|
968
|
-
if (!
|
|
934
|
+
if (!config2 || localTestMode || redeeming) return;
|
|
969
935
|
const reauthing = nativeReauthPending && state === "authenticated";
|
|
970
936
|
if (state !== "pending" && state !== "guest" && !reauthing) return;
|
|
971
937
|
const upgradingFromGuest = state === "guest";
|
|
@@ -973,7 +939,7 @@ async function redeemTicket(ticket) {
|
|
|
973
939
|
redeeming = true;
|
|
974
940
|
try {
|
|
975
941
|
const res = await doFetch(
|
|
976
|
-
`${
|
|
942
|
+
`${config2.apiUrl}/api/embed/session`,
|
|
977
943
|
{
|
|
978
944
|
method: "POST",
|
|
979
945
|
headers: { "Content-Type": "application/json" },
|
|
@@ -1021,18 +987,18 @@ async function redeemTicket(ticket) {
|
|
|
1021
987
|
}
|
|
1022
988
|
}
|
|
1023
989
|
async function requestGuestSession() {
|
|
1024
|
-
if (!
|
|
990
|
+
if (!config2 || localTestMode || requestingGuest || redeeming) return;
|
|
1025
991
|
if (state !== "pending" && state !== "guest") return;
|
|
1026
992
|
const epoch = ++identityEpoch;
|
|
1027
993
|
requestingGuest = true;
|
|
1028
994
|
try {
|
|
1029
|
-
const res = await doFetch(`${
|
|
995
|
+
const res = await doFetch(`${config2.apiUrl}/api/embed/guest-session`, {
|
|
1030
996
|
method: "POST",
|
|
1031
997
|
headers: { "Content-Type": "application/json" },
|
|
1032
998
|
// playerId is analytics-only: the SERVER still mints the session's own
|
|
1033
999
|
// guest id and the token is unchanged — this just lets the play event
|
|
1034
1000
|
// key on something that survives the tab.
|
|
1035
|
-
body: JSON.stringify({ slug:
|
|
1001
|
+
body: JSON.stringify({ slug: config2.slug, playerId: playerId() })
|
|
1036
1002
|
}, handshakeTimeoutMs);
|
|
1037
1003
|
if (epoch !== identityEpoch) return;
|
|
1038
1004
|
if (!res.ok) {
|
|
@@ -1148,12 +1114,12 @@ function scheduleRefresh() {
|
|
|
1148
1114
|
}, refreshDelayMs);
|
|
1149
1115
|
}
|
|
1150
1116
|
async function refreshToken() {
|
|
1151
|
-
if (!
|
|
1117
|
+
if (!config2 || state !== "authenticated" && state !== "guest" || !embedToken) return;
|
|
1152
1118
|
const epoch = identityEpoch;
|
|
1153
1119
|
const tokenAtStart = embedToken;
|
|
1154
1120
|
let status;
|
|
1155
1121
|
try {
|
|
1156
|
-
const res = await doFetch(`${
|
|
1122
|
+
const res = await doFetch(`${config2.apiUrl}/api/embed/session/refresh`, {
|
|
1157
1123
|
method: "POST",
|
|
1158
1124
|
headers: { Authorization: `Bearer ${tokenAtStart}` },
|
|
1159
1125
|
// Best-effort completion if the tab starts unloading mid-refresh; zero
|
|
@@ -1533,7 +1499,7 @@ function __resetForTests(overrides) {
|
|
|
1533
1499
|
removeOverlay(true);
|
|
1534
1500
|
removeGuestPopover();
|
|
1535
1501
|
overlayFontRequested = false;
|
|
1536
|
-
|
|
1502
|
+
config2 = null;
|
|
1537
1503
|
state = "pending";
|
|
1538
1504
|
user = null;
|
|
1539
1505
|
localTestMode = false;
|
|
@@ -1564,6 +1530,13 @@ export {
|
|
|
1564
1530
|
getEntitlements,
|
|
1565
1531
|
consumeEntitlement,
|
|
1566
1532
|
buy,
|
|
1533
|
+
getGenerationModels,
|
|
1534
|
+
getWorkflowOfferings,
|
|
1535
|
+
getGeneration,
|
|
1536
|
+
waitForGeneration,
|
|
1537
|
+
generate,
|
|
1538
|
+
requestWorkflow,
|
|
1539
|
+
resumeWorkflow,
|
|
1567
1540
|
initEmbed,
|
|
1568
1541
|
isEmbedded,
|
|
1569
1542
|
getAuthState,
|