skillprint-js-sdk 1.1.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1236 @@
1
+ "use strict";
2
+ var SkillprintSDK = (() => {
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ ApiEnvironment: () => ApiEnvironment,
25
+ GenericCanvasSkillprintAdapter: () => GenericCanvasSkillprintAdapter,
26
+ LogLevel: () => LogLevel,
27
+ Mood: () => Mood,
28
+ ParameterDefinition: () => ParameterDefinition,
29
+ ParameterInfo: () => ParameterInfo,
30
+ ParameterType: () => ParameterType,
31
+ ParameterUpdateResult: () => ParameterUpdateResult,
32
+ PhaserSkillprintAdapter: () => PhaserSkillprintAdapter,
33
+ PixiSkillprintAdapter: () => PixiSkillprintAdapter,
34
+ PollResultsResponse: () => PollResultsResponse,
35
+ ScreenshotUtility: () => ScreenshotUtility,
36
+ SkillprintAPIClient: () => SkillprintAPIClient,
37
+ SkillprintConfig: () => SkillprintConfig,
38
+ SkillprintManager: () => SkillprintManager,
39
+ SkillprintSessionHelper: () => SkillprintSessionHelper,
40
+ StartSessionRequest: () => StartSessionRequest,
41
+ ThreeSkillprintAdapter: () => ThreeSkillprintAdapter,
42
+ WebGLUrlParameterExtractor: () => WebGLUrlParameterExtractor
43
+ });
44
+
45
+ // src/constants.ts
46
+ var ApiEnvironment = /* @__PURE__ */ ((ApiEnvironment2) => {
47
+ ApiEnvironment2["PRODUCTION"] = "production";
48
+ ApiEnvironment2["STAGING"] = "staging";
49
+ return ApiEnvironment2;
50
+ })(ApiEnvironment || {});
51
+ var ParameterType = /* @__PURE__ */ ((ParameterType2) => {
52
+ ParameterType2["FLOAT"] = "Float";
53
+ ParameterType2["INTEGER"] = "Integer";
54
+ ParameterType2["BOOLEAN"] = "Boolean";
55
+ return ParameterType2;
56
+ })(ParameterType || {});
57
+ var Mood = /* @__PURE__ */ ((Mood2) => {
58
+ Mood2["RELAX"] = "relax";
59
+ Mood2["FOCUS"] = "focus";
60
+ Mood2["CREATIVITY"] = "creativity";
61
+ Mood2["COLLABORATE"] = "collaborate";
62
+ Mood2["GRIT"] = "grit";
63
+ Mood2["JOY"] = "joy";
64
+ Mood2["CURIOSITY"] = "curiosity";
65
+ Mood2["EMPATHY"] = "empathy";
66
+ Mood2["AWE"] = "awe";
67
+ return Mood2;
68
+ })(Mood || {});
69
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
70
+ LogLevel2["INFO"] = "info";
71
+ LogLevel2["WARNING"] = "warning";
72
+ LogLevel2["ERROR"] = "error";
73
+ return LogLevel2;
74
+ })(LogLevel || {});
75
+
76
+ // src/config.ts
77
+ var SkillprintConfig = class {
78
+ constructor(options = {}) {
79
+ this.gameName = options.gameName || "GAME NAME SLUG REGISTERED IN SKILLPRINT API";
80
+ this.targetEnvironment = options.targetEnvironment || "production" /* PRODUCTION */;
81
+ this.productionPartnerApiKey = options.productionPartnerApiKey || "";
82
+ this.productionApiBaseUrl = options.productionApiBaseUrl || "https://api.skillprint.com/v1";
83
+ this.stagingPartnerApiKey = options.stagingPartnerApiKey || "";
84
+ this.stagingApiBaseUrl = options.stagingApiBaseUrl || "https://staging-api.skillprint.com/v1";
85
+ this.gameParameters = options.gameParameters || [];
86
+ this.screenshotIntervalSeconds = options.screenshotIntervalSeconds ?? 2;
87
+ this.screenshotPostIntervalSeconds = options.screenshotPostIntervalSeconds ?? 5;
88
+ this.pollResultsIntervalSeconds = options.pollResultsIntervalSeconds ?? 5;
89
+ this.enableDebugLogging = options.enableDebugLogging ?? false;
90
+ }
91
+ /**
92
+ * Resolves the active partner API key based on current targetEnvironment.
93
+ */
94
+ get activePartnerApiKey() {
95
+ switch (this.targetEnvironment) {
96
+ case "production" /* PRODUCTION */:
97
+ return this.productionPartnerApiKey;
98
+ case "staging" /* STAGING */:
99
+ return this.stagingPartnerApiKey || this.productionPartnerApiKey;
100
+ default:
101
+ return this.productionPartnerApiKey;
102
+ }
103
+ }
104
+ /**
105
+ * Resolves the active API base URL based on current targetEnvironment.
106
+ */
107
+ get activeApiBaseUrl() {
108
+ switch (this.targetEnvironment) {
109
+ case "production" /* PRODUCTION */:
110
+ return this.productionApiBaseUrl;
111
+ case "staging" /* STAGING */:
112
+ return this.stagingApiBaseUrl;
113
+ default:
114
+ return this.productionApiBaseUrl;
115
+ }
116
+ }
117
+ };
118
+
119
+ // src/parameter-definition.ts
120
+ var ParameterDefinition = class {
121
+ constructor(options = {}) {
122
+ this.updateAction = null;
123
+ this.parameterName = options.parameterName || "";
124
+ this.description = options.description || "";
125
+ this.howSDKChangesIt = options.howSDKChangesIt || "";
126
+ this.type = options.type || "Float" /* FLOAT */;
127
+ this.minValue = options.minValue ?? 0;
128
+ this.maxValue = options.maxValue ?? 100;
129
+ this.defaultValue = options.defaultValue ?? "";
130
+ this.updateAction = null;
131
+ }
132
+ /**
133
+ * Checks if a converted value is valid for this parameter type and range.
134
+ * @param value The value to validate
135
+ */
136
+ isValid(value) {
137
+ switch (this.type) {
138
+ case "Float" /* FLOAT */:
139
+ return typeof value === "number" && !isNaN(value) && value >= this.minValue && value <= this.maxValue;
140
+ case "Integer" /* INTEGER */:
141
+ return typeof value === "number" && Number.isInteger(value) && value >= this.minValue && value <= this.maxValue;
142
+ case "Boolean" /* BOOLEAN */:
143
+ return typeof value === "boolean";
144
+ default:
145
+ return false;
146
+ }
147
+ }
148
+ /**
149
+ * Converts a raw API value into the defined type for this parameter.
150
+ * @param rawValue Raw incoming value from API
151
+ */
152
+ convertValue(rawValue) {
153
+ if (rawValue === null || rawValue === void 0) {
154
+ return null;
155
+ }
156
+ try {
157
+ switch (this.type) {
158
+ case "Float" /* FLOAT */: {
159
+ const parsedFloat = parseFloat(String(rawValue));
160
+ return isNaN(parsedFloat) ? null : parsedFloat;
161
+ }
162
+ case "Integer" /* INTEGER */: {
163
+ const parsedInt = parseInt(String(rawValue), 10);
164
+ return isNaN(parsedInt) ? null : parsedInt;
165
+ }
166
+ case "Boolean" /* BOOLEAN */: {
167
+ if (typeof rawValue === "boolean") return rawValue;
168
+ if (typeof rawValue === "string") {
169
+ const lower = rawValue.trim().toLowerCase();
170
+ if (lower === "true" || lower === "1") return true;
171
+ if (lower === "false" || lower === "0") return false;
172
+ }
173
+ return Boolean(rawValue);
174
+ }
175
+ default:
176
+ return String(rawValue);
177
+ }
178
+ } catch (error) {
179
+ console.error(`Error converting value for parameter ${this.parameterName}:`, error);
180
+ return null;
181
+ }
182
+ }
183
+ };
184
+
185
+ // src/api-models.ts
186
+ var StartSessionRequest = class {
187
+ constructor(sessionId, game, targetMood) {
188
+ this.sessionId = sessionId;
189
+ this.game = game;
190
+ this.targetMood = targetMood;
191
+ }
192
+ };
193
+ var ParameterInfo = class {
194
+ constructor(name, type, description, minValue = null, maxValue = null) {
195
+ this.name = name;
196
+ this.type = type;
197
+ this.description = description;
198
+ this.minValue = minValue;
199
+ this.maxValue = maxValue;
200
+ }
201
+ };
202
+ var ParameterUpdateResult = class {
203
+ constructor(data = {}) {
204
+ this.parameterName = data.parameterName || "";
205
+ this.newValue = data.newValue;
206
+ this._rawJson = JSON.stringify(data);
207
+ Object.keys(data).forEach((key) => {
208
+ if (!(key in this)) {
209
+ this[key] = data[key];
210
+ }
211
+ });
212
+ }
213
+ /**
214
+ * Extracts parsed new value from response payload.
215
+ */
216
+ getParsedValue() {
217
+ if (this.newValue !== null && this.newValue !== void 0 && this.newValue !== "") {
218
+ return this.newValue;
219
+ }
220
+ if (this.parameterName && this[this.parameterName] !== void 0) {
221
+ return this[this.parameterName];
222
+ }
223
+ return null;
224
+ }
225
+ /**
226
+ * Converts value to target primitive type.
227
+ * @param targetType Target primitive type string ('number', 'integer', 'boolean', 'string')
228
+ */
229
+ convertToType(targetType) {
230
+ const value = this.getParsedValue();
231
+ if (value === null || value === void 0) {
232
+ return null;
233
+ }
234
+ try {
235
+ switch (targetType.toLowerCase()) {
236
+ case "number":
237
+ case "float": {
238
+ const val = Number(value);
239
+ return isNaN(val) ? null : val;
240
+ }
241
+ case "integer": {
242
+ const val = parseInt(String(value), 10);
243
+ return isNaN(val) ? null : val;
244
+ }
245
+ case "boolean": {
246
+ if (typeof value === "boolean") return value;
247
+ if (typeof value === "string") {
248
+ const lower = value.trim().toLowerCase();
249
+ if (lower === "true" || lower === "1") return true;
250
+ if (lower === "false" || lower === "0") return false;
251
+ }
252
+ return Boolean(value);
253
+ }
254
+ case "string":
255
+ return String(value);
256
+ default:
257
+ return value;
258
+ }
259
+ } catch (error) {
260
+ console.error(`Failed to convert value '${String(value)}' to type ${targetType}:`, error);
261
+ return null;
262
+ }
263
+ }
264
+ /**
265
+ * Converts value based on parameter definition type and applies range clamping if necessary.
266
+ * @param paramDef Parameter definition object
267
+ */
268
+ convertToParameterType(paramDef) {
269
+ const rawValue = this.getParsedValue();
270
+ if (rawValue === null || rawValue === void 0) {
271
+ return null;
272
+ }
273
+ try {
274
+ let convertedValue;
275
+ switch (paramDef.type) {
276
+ case "Float" /* FLOAT */:
277
+ convertedValue = parseFloat(String(rawValue));
278
+ if (isNaN(convertedValue)) return null;
279
+ break;
280
+ case "Integer" /* INTEGER */:
281
+ convertedValue = parseInt(String(rawValue), 10);
282
+ if (isNaN(convertedValue)) return null;
283
+ break;
284
+ case "Boolean" /* BOOLEAN */:
285
+ if (typeof rawValue === "boolean") {
286
+ convertedValue = rawValue;
287
+ } else if (typeof rawValue === "string") {
288
+ const lower = rawValue.trim().toLowerCase();
289
+ convertedValue = lower === "true" || lower === "1";
290
+ } else {
291
+ convertedValue = Boolean(rawValue);
292
+ }
293
+ break;
294
+ default:
295
+ convertedValue = String(rawValue);
296
+ break;
297
+ }
298
+ if (paramDef.isValid(convertedValue)) {
299
+ return convertedValue;
300
+ } else {
301
+ console.warn(
302
+ `Converted value ${String(convertedValue)} for parameter ${this.parameterName} is outside valid range [${paramDef.minValue}, ${paramDef.maxValue}]`
303
+ );
304
+ if (paramDef.type === "Float" /* FLOAT */ && typeof convertedValue === "number") {
305
+ return Math.max(paramDef.minValue, Math.min(paramDef.maxValue, convertedValue));
306
+ } else if (paramDef.type === "Integer" /* INTEGER */ && typeof convertedValue === "number") {
307
+ return Math.max(Math.floor(paramDef.minValue), Math.min(Math.floor(paramDef.maxValue), convertedValue));
308
+ }
309
+ return convertedValue;
310
+ }
311
+ } catch (error) {
312
+ console.error(`Error converting parameter ${this.parameterName}:`, error);
313
+ return null;
314
+ }
315
+ }
316
+ };
317
+ var PollResultsResponse = class {
318
+ constructor(data = {}) {
319
+ this.gameplayTips = data.gameplayTips || "";
320
+ this.state = data.state || "";
321
+ this.parameterUpdates = (data.parameterUpdates || []).map((update) => new ParameterUpdateResult(update));
322
+ }
323
+ };
324
+
325
+ // src/api-client.ts
326
+ var SkillprintAPIClient = class {
327
+ constructor(baseUrl, partnerApiKey, logger) {
328
+ // API Endpoints
329
+ this.START_SESSION_ENDPOINT = "/games/api/sessions/";
330
+ this.UPLOAD_SCREENSHOTS_ENDPOINT = "/games/api/record-session/{sessionId}/";
331
+ this.POLL_RESULTS_ENDPOINT = "/games/api/sessions/{sessionId}/";
332
+ this.CREATE_USER_ENDPOINT = "/partners/api/users/add/";
333
+ this.GET_USER_TOKEN_ENDPOINT = "/partners/api/users/auth/token/";
334
+ this.baseUrl = baseUrl.replace(/\/$/, "");
335
+ this.partnerApiKey = partnerApiKey;
336
+ this.logger = logger;
337
+ }
338
+ /**
339
+ * Starts a new Skillprint session.
340
+ */
341
+ async startSession(sessionId, targetMood, customPlayerId = null, gameName = "", _gameParameters = []) {
342
+ const url = `${this.baseUrl}${this.START_SESSION_ENDPOINT}`;
343
+ this.logger?.(`Starting session: POST ${url}`, "info" /* INFO */);
344
+ this.logger?.(`Starting session: MOOD ${targetMood}`, "warning" /* WARNING */);
345
+ const validMoods = Object.values(Mood);
346
+ if (!validMoods.includes(targetMood)) {
347
+ const errorMessage = `Invalid targetMood: '${targetMood}'. Valid moods are: ${validMoods.join(", ")}.`;
348
+ this.logger?.(errorMessage, "error" /* ERROR */);
349
+ throw new Error(errorMessage);
350
+ }
351
+ let userToken = null;
352
+ if (customPlayerId) {
353
+ try {
354
+ userToken = await this.createOrGetUserToken(customPlayerId);
355
+ this.logger?.(`User token obtained successfully for player: ${customPlayerId}`, "info" /* INFO */);
356
+ } catch (error) {
357
+ const message = error instanceof Error ? error.message : String(error);
358
+ this.logger?.(`Failed to obtain user token: ${message}`, "error" /* ERROR */);
359
+ this.logger?.("Proceeding with session creation without user token", "warning" /* WARNING */);
360
+ }
361
+ }
362
+ const requestData = new StartSessionRequest(sessionId, gameName, targetMood);
363
+ const headers = {
364
+ "Content-Type": "application/json",
365
+ "Authorization": `Api-Key ${this.partnerApiKey}`
366
+ };
367
+ if (userToken) {
368
+ headers["X-Auth-Token"] = `Token ${userToken}`;
369
+ }
370
+ try {
371
+ const response = await fetch(url, {
372
+ method: "POST",
373
+ headers,
374
+ body: JSON.stringify(requestData)
375
+ });
376
+ const responseText = await response.text();
377
+ if (response.ok) {
378
+ this.logger?.(`StartSession successful. Response: ${responseText}`, "info" /* INFO */);
379
+ return { success: true, data: responseText };
380
+ } else {
381
+ this.logger?.(`StartSession Error: ${response.status}. Response: ${responseText}`, "error" /* ERROR */);
382
+ throw new Error(`${response.status} | ${responseText}`);
383
+ }
384
+ } catch (error) {
385
+ const message = error instanceof Error ? error.message : String(error);
386
+ this.logger?.(`StartSession Error: ${message}`, "error" /* ERROR */);
387
+ throw error;
388
+ }
389
+ }
390
+ /**
391
+ * Uploads a batch of captured screenshots for an active session.
392
+ */
393
+ async postScreenshots(sessionId, screenshots, isLastChunk = false) {
394
+ const endpoint = this.UPLOAD_SCREENSHOTS_ENDPOINT.replace("{sessionId}", sessionId);
395
+ const url = `${this.baseUrl}${endpoint}`;
396
+ this.logger?.(`Posting ${screenshots.length} screenshots (isLastChunk: ${isLastChunk}): POST ${url}`, "info" /* INFO */);
397
+ if (screenshots.length === 0 && !isLastChunk) {
398
+ const errorMsg = "No screenshots provided, and 'is_last_chunk' is false. API likely requires files in this case.";
399
+ this.logger?.(errorMsg, "warning" /* WARNING */);
400
+ throw new Error(errorMsg);
401
+ }
402
+ const formData = new FormData();
403
+ formData.append("is_last_chunk", isLastChunk.toString().toLowerCase());
404
+ for (let i = 0; i < screenshots.length; i++) {
405
+ const screenshot = screenshots[i];
406
+ if (screenshot) {
407
+ const fieldName = `screenshot${i}`;
408
+ const filename = `screenshot_${i}.jpg`;
409
+ formData.append(fieldName, screenshot, filename);
410
+ } else {
411
+ this.logger?.(`Screenshot at index ${i} is null, skipping.`, "warning" /* WARNING */);
412
+ }
413
+ }
414
+ try {
415
+ const response = await fetch(url, {
416
+ method: "POST",
417
+ headers: {
418
+ "Authorization": `Api-Key ${this.partnerApiKey}`
419
+ },
420
+ body: formData
421
+ });
422
+ const responseText = await response.text();
423
+ if (response.ok) {
424
+ this.logger?.(`PostScreenshots successful. Response: ${responseText}`, "info" /* INFO */);
425
+ return { success: true, data: responseText };
426
+ } else {
427
+ this.logger?.(`PostScreenshots Error: ${response.status}. Response: ${responseText}`, "error" /* ERROR */);
428
+ throw new Error(`${response.status} | ${responseText}`);
429
+ }
430
+ } catch (error) {
431
+ const message = error instanceof Error ? error.message : String(error);
432
+ this.logger?.(`PostScreenshots Error: ${message}`, "error" /* ERROR */);
433
+ throw error;
434
+ }
435
+ }
436
+ /**
437
+ * Polls API for updated parameter values for the active session.
438
+ */
439
+ async pollParameterResults(sessionId) {
440
+ const endpoint = this.POLL_RESULTS_ENDPOINT.replace("{sessionId}", sessionId);
441
+ const url = `${this.baseUrl}${endpoint}`;
442
+ this.logger?.(`Polling results: GET ${url}`, "info" /* INFO */);
443
+ try {
444
+ const response = await fetch(url, {
445
+ method: "GET",
446
+ headers: {
447
+ "Authorization": `Api-Key ${this.partnerApiKey}`,
448
+ "Accept": "application/json"
449
+ }
450
+ });
451
+ const responseText = await response.text();
452
+ if (response.ok) {
453
+ this.logger?.(`PollResults successful. Response: ${responseText}`, "info" /* INFO */);
454
+ try {
455
+ const parsedResponse = JSON.parse(responseText);
456
+ const pollResults = new PollResultsResponse(parsedResponse);
457
+ if (pollResults.parameterUpdates && pollResults.parameterUpdates.length > 0) {
458
+ this.logger?.(`Successfully parsed ${pollResults.parameterUpdates.length} parameter updates`, "info" /* INFO */);
459
+ pollResults.parameterUpdates.forEach((update) => {
460
+ const parsedValue = update.getParsedValue();
461
+ this.logger?.(
462
+ `Parameter Update: ${update.parameterName} = ${String(parsedValue)} (Type: ${typeof parsedValue})`,
463
+ "info" /* INFO */
464
+ );
465
+ });
466
+ } else {
467
+ this.logger?.("No parameter updates found in response", "warning" /* WARNING */);
468
+ }
469
+ return { success: true, data: pollResults.parameterUpdates || [] };
470
+ } catch (parseError) {
471
+ const message = parseError instanceof Error ? parseError.message : String(parseError);
472
+ this.logger?.(`PollResults JSON parsing error: ${message}. Response: ${responseText}`, "error" /* ERROR */);
473
+ throw parseError;
474
+ }
475
+ } else {
476
+ this.logger?.(`PollResults Error: ${response.status}. Response: ${responseText}`, "error" /* ERROR */);
477
+ throw new Error(`${response.status} | ${responseText}`);
478
+ }
479
+ } catch (error) {
480
+ const message = error instanceof Error ? error.message : String(error);
481
+ this.logger?.(`PollResults Error: ${message}`, "error" /* ERROR */);
482
+ throw error;
483
+ }
484
+ }
485
+ /**
486
+ * Creates or fetches user auth token for custom player ID.
487
+ */
488
+ async createOrGetUserToken(customPlayerId) {
489
+ if (!customPlayerId) {
490
+ throw new Error("Custom player ID cannot be null or empty");
491
+ }
492
+ try {
493
+ return await this.getUserToken(customPlayerId);
494
+ } catch {
495
+ try {
496
+ await this.createUser(customPlayerId);
497
+ return await this.getUserToken(customPlayerId);
498
+ } catch (createError) {
499
+ const message = createError instanceof Error ? createError.message : String(createError);
500
+ throw new Error(`Failed to create user: ${message}`);
501
+ }
502
+ }
503
+ }
504
+ /**
505
+ * Registers a new user with Skillprint API.
506
+ */
507
+ async createUser(internalId) {
508
+ const url = `${this.baseUrl}${this.CREATE_USER_ENDPOINT}`;
509
+ this.logger?.(`Creating user: POST ${url} with internalId: ${internalId}`, "info" /* INFO */);
510
+ const response = await fetch(url, {
511
+ method: "POST",
512
+ headers: {
513
+ "Content-Type": "application/json",
514
+ "Authorization": `Api-Key ${this.partnerApiKey}`
515
+ },
516
+ body: JSON.stringify({ internalId })
517
+ });
518
+ const responseText = await response.text();
519
+ if (response.ok) {
520
+ this.logger?.(`CreateUser successful. Response: ${responseText}`, "info" /* INFO */);
521
+ return responseText;
522
+ } else {
523
+ this.logger?.(`CreateUser Error: ${response.status}. Response: ${responseText}`, "error" /* ERROR */);
524
+ throw new Error(`${response.status} | ${responseText}`);
525
+ }
526
+ }
527
+ /**
528
+ * Obtains auth token for existing user internalId.
529
+ */
530
+ async getUserToken(internalId) {
531
+ const url = `${this.baseUrl}${this.GET_USER_TOKEN_ENDPOINT}`;
532
+ this.logger?.(`Getting user token: POST ${url} with internalId: ${internalId}`, "info" /* INFO */);
533
+ const response = await fetch(url, {
534
+ method: "POST",
535
+ headers: {
536
+ "Content-Type": "application/json",
537
+ "Authorization": `Api-Key ${this.partnerApiKey}`
538
+ },
539
+ body: JSON.stringify({ internalId })
540
+ });
541
+ const responseText = await response.text();
542
+ if (response.ok) {
543
+ this.logger?.(`GetUserToken successful. Response: ${responseText}`, "info" /* INFO */);
544
+ try {
545
+ const tokenResponse = JSON.parse(responseText);
546
+ if (tokenResponse.token) {
547
+ return tokenResponse.token;
548
+ } else {
549
+ throw new Error("Token not found in response");
550
+ }
551
+ } catch (parseError) {
552
+ const message = parseError instanceof Error ? parseError.message : String(parseError);
553
+ this.logger?.(`Failed to parse token response: ${message}`, "error" /* ERROR */);
554
+ throw new Error("Failed to parse token response");
555
+ }
556
+ } else {
557
+ this.logger?.(`GetUserToken Error: ${response.status}. Response: ${responseText}`, "error" /* ERROR */);
558
+ throw new Error(`${response.status} | ${responseText}`);
559
+ }
560
+ }
561
+ };
562
+
563
+ // src/screenshot-utility.ts
564
+ var ScreenshotUtility = class {
565
+ constructor(logger) {
566
+ this.logger = logger;
567
+ }
568
+ /**
569
+ * Captures a screenshot from a standard HTML canvas element as a Blob.
570
+ * @param canvas Target HTML Canvas element
571
+ * @param options Screenshot options (format, quality, max dimensions)
572
+ */
573
+ async captureScreenshot(canvas, options = {}) {
574
+ try {
575
+ const {
576
+ format = "image/jpeg",
577
+ quality = 0.8,
578
+ maxWidth = null,
579
+ maxHeight = null
580
+ } = options;
581
+ if (!canvas) {
582
+ throw new Error("No canvas provided for screenshot");
583
+ }
584
+ const screenshotCanvas = document.createElement("canvas");
585
+ const ctx = screenshotCanvas.getContext("2d");
586
+ if (!ctx) {
587
+ throw new Error("Failed to obtain 2D rendering context for screenshot canvas");
588
+ }
589
+ let targetWidth = canvas.width;
590
+ let targetHeight = canvas.height;
591
+ if (targetWidth === 0 || targetHeight === 0) {
592
+ throw new Error("Canvas has 0 width or height");
593
+ }
594
+ if (maxWidth && targetWidth > maxWidth) {
595
+ const scale = maxWidth / targetWidth;
596
+ targetWidth = maxWidth;
597
+ targetHeight = Math.floor(targetHeight * scale);
598
+ }
599
+ if (maxHeight && targetHeight > maxHeight) {
600
+ const scale = maxHeight / targetHeight;
601
+ targetHeight = maxHeight;
602
+ targetWidth = Math.floor(targetWidth * scale);
603
+ }
604
+ screenshotCanvas.width = targetWidth;
605
+ screenshotCanvas.height = targetHeight;
606
+ ctx.drawImage(canvas, 0, 0, targetWidth, targetHeight);
607
+ return new Promise((resolve) => {
608
+ screenshotCanvas.toBlob(
609
+ (blob) => {
610
+ if (blob) {
611
+ this.logger?.("Screenshot captured successfully", "info" /* INFO */);
612
+ resolve(blob);
613
+ } else {
614
+ this.logger?.("Failed to create screenshot blob", "error" /* ERROR */);
615
+ resolve(null);
616
+ }
617
+ },
618
+ format,
619
+ quality
620
+ );
621
+ });
622
+ } catch (error) {
623
+ const message = error instanceof Error ? error.message : String(error);
624
+ this.logger?.(`Error capturing screenshot: ${message}`, "error" /* ERROR */);
625
+ return null;
626
+ }
627
+ }
628
+ /**
629
+ * Alternative method for WebGL contexts that don't preserve drawing buffer.
630
+ * Reads pixel data directly from WebGL rendering context and flips vertically.
631
+ * @param gl WebGL rendering context (WebGLRenderingContext | WebGL2RenderingContext)
632
+ * @param canvas Target HTML Canvas element
633
+ * @param options Screenshot options
634
+ */
635
+ async captureScreenshotWebGL(gl, canvas, options = {}) {
636
+ try {
637
+ const { format = "image/jpeg", quality = 0.8 } = options;
638
+ if (!gl || !canvas) {
639
+ throw new Error("Invalid WebGL context or canvas provided");
640
+ }
641
+ const pixels = new Uint8Array(canvas.width * canvas.height * 4);
642
+ gl.readPixels(0, 0, canvas.width, canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
643
+ const tempCanvas = document.createElement("canvas");
644
+ tempCanvas.width = canvas.width;
645
+ tempCanvas.height = canvas.height;
646
+ const ctx = tempCanvas.getContext("2d");
647
+ if (!ctx) {
648
+ throw new Error("Failed to get 2d context for WebGL screenshot processing");
649
+ }
650
+ const imageData = ctx.createImageData(canvas.width, canvas.height);
651
+ for (let y = 0; y < canvas.height; y++) {
652
+ for (let x = 0; x < canvas.width; x++) {
653
+ const srcIndex = (y * canvas.width + x) * 4;
654
+ const dstIndex = ((canvas.height - 1 - y) * canvas.width + x) * 4;
655
+ imageData.data[dstIndex] = pixels[srcIndex];
656
+ imageData.data[dstIndex + 1] = pixels[srcIndex + 1];
657
+ imageData.data[dstIndex + 2] = pixels[srcIndex + 2];
658
+ imageData.data[dstIndex + 3] = pixels[srcIndex + 3];
659
+ }
660
+ }
661
+ ctx.putImageData(imageData, 0, 0);
662
+ return new Promise((resolve) => {
663
+ tempCanvas.toBlob(
664
+ (blob) => {
665
+ if (blob) {
666
+ this.logger?.("WebGL screenshot captured successfully", "info" /* INFO */);
667
+ resolve(blob);
668
+ } else {
669
+ this.logger?.("Failed to create WebGL screenshot blob", "error" /* ERROR */);
670
+ resolve(null);
671
+ }
672
+ },
673
+ format,
674
+ quality
675
+ );
676
+ });
677
+ } catch (error) {
678
+ const message = error instanceof Error ? error.message : String(error);
679
+ this.logger?.(`Error capturing WebGL screenshot: ${message}`, "error" /* ERROR */);
680
+ return null;
681
+ }
682
+ }
683
+ };
684
+
685
+ // src/url-parameter-extractor.ts
686
+ var WebGLUrlParameterExtractor = class {
687
+ /**
688
+ * Retrieves a URL query parameter by key.
689
+ * @param parameterName Name of the URL query parameter
690
+ */
691
+ static getUrlParameter(parameterName) {
692
+ try {
693
+ if (!this.isUrlParameterSupported()) return null;
694
+ const url = new URL(window.location.href);
695
+ return url.searchParams.get(parameterName);
696
+ } catch (error) {
697
+ const message = error instanceof Error ? error.message : String(error);
698
+ console.warn(`Failed to get URL parameter '${parameterName}': ${message}`);
699
+ return null;
700
+ }
701
+ }
702
+ /**
703
+ * Gets current window URL string.
704
+ */
705
+ static getCurrentUrl() {
706
+ try {
707
+ if (!this.isUrlParameterSupported()) return null;
708
+ return window.location.href;
709
+ } catch (error) {
710
+ const message = error instanceof Error ? error.message : String(error);
711
+ console.warn(`Failed to get current URL: ${message}`);
712
+ return null;
713
+ }
714
+ }
715
+ /**
716
+ * Extracts targetMood and playerId from URL query parameters.
717
+ */
718
+ static getSkillprintUrlParameters() {
719
+ const targetMood = this.getUrlParameter("mood") || this.getUrlParameter("targetMood");
720
+ const playerId = this.getUrlParameter("playerId") || this.getUrlParameter("player_id") || this.getUrlParameter("userId");
721
+ if (typeof window !== "undefined" && window.localStorage) {
722
+ if (targetMood) {
723
+ try {
724
+ localStorage.setItem("SkillprintMood", targetMood);
725
+ } catch {
726
+ }
727
+ }
728
+ if (playerId) {
729
+ try {
730
+ localStorage.setItem("SkillprintPlayerId", playerId);
731
+ } catch {
732
+ }
733
+ }
734
+ }
735
+ return { targetMood, playerId };
736
+ }
737
+ /**
738
+ * Returns true if running in browser context with URL access.
739
+ */
740
+ static isUrlParameterSupported() {
741
+ return typeof window !== "undefined" && typeof window.location !== "undefined";
742
+ }
743
+ };
744
+ var SkillprintSessionHelper = class {
745
+ /**
746
+ * Starts a Skillprint session, attempting to auto-extract parameters from URL.
747
+ * @param manager SkillprintManager instance
748
+ * @param fallbackMood Mood to use if not found in URL
749
+ * @param fallbackPlayerId Player ID to use if not found in URL
750
+ * @param overrideMood Forced mood override
751
+ * @param overridePlayerId Forced player ID override
752
+ */
753
+ static startSessionWithUrlParams(manager, fallbackMood = "focus" /* FOCUS */, fallbackPlayerId = null, overrideMood = null, overridePlayerId = null) {
754
+ if (!manager) {
755
+ console.error("SkillprintManager is null. Cannot start session.");
756
+ return;
757
+ }
758
+ let targetMood = overrideMood;
759
+ let playerId = overridePlayerId;
760
+ if (!targetMood || !playerId) {
761
+ if (WebGLUrlParameterExtractor.isUrlParameterSupported()) {
762
+ const urlParams = WebGLUrlParameterExtractor.getSkillprintUrlParameters();
763
+ if (!targetMood) {
764
+ targetMood = urlParams.targetMood || fallbackMood;
765
+ }
766
+ if (!playerId) {
767
+ playerId = urlParams.playerId || fallbackPlayerId;
768
+ }
769
+ manager.log(`URL Parameters - Mood: '${targetMood}', Player ID: '${playerId}'`, "info" /* INFO */);
770
+ } else {
771
+ if (!targetMood) targetMood = fallbackMood;
772
+ if (!playerId) playerId = fallbackPlayerId;
773
+ manager.log(
774
+ `Non-browser Platform - Using fallback values. Mood: '${targetMood}', Player ID: '${playerId}'`,
775
+ "info" /* INFO */
776
+ );
777
+ }
778
+ }
779
+ if (!targetMood) {
780
+ manager.log("No target mood specified and no fallback provided. Using 'focus' as default.", "warning" /* WARNING */);
781
+ targetMood = "focus" /* FOCUS */;
782
+ }
783
+ const validMoods = Object.values(Mood);
784
+ if (!validMoods.includes(targetMood)) {
785
+ manager.log(
786
+ `Invalid mood '${targetMood}'. Valid moods: ${validMoods.join(", ")}. Using 'focus' as fallback.`,
787
+ "warning" /* WARNING */
788
+ );
789
+ targetMood = "focus" /* FOCUS */;
790
+ }
791
+ manager.log(
792
+ `Starting Skillprint session with Mood: '${targetMood}', Player ID: '${playerId || "none"}'`,
793
+ "info" /* INFO */
794
+ );
795
+ manager.startGameSession(targetMood, playerId);
796
+ }
797
+ };
798
+
799
+ // src/manager.ts
800
+ var _SkillprintManager = class _SkillprintManager {
801
+ constructor(config, canvasProvider) {
802
+ this.currentSessionId = null;
803
+ this.isSessionActive = false;
804
+ this.apiClient = null;
805
+ this.screenshotUtility = null;
806
+ this.screenshotQueue = [];
807
+ this.registeredParameters = /* @__PURE__ */ new Map();
808
+ // Timers
809
+ this.screenshotCaptureTimer = null;
810
+ this.screenshotPostTimer = null;
811
+ this.pollResultsTimer = null;
812
+ if (_SkillprintManager.instance) {
813
+ console.warn("Another instance of SkillprintManager already exists. Using existing instance.");
814
+ return _SkillprintManager.instance;
815
+ }
816
+ this.config = config;
817
+ this.canvasProvider = canvasProvider;
818
+ this.currentSessionId = null;
819
+ this.isSessionActive = false;
820
+ this.apiClient = null;
821
+ this.screenshotUtility = null;
822
+ this.screenshotQueue = [];
823
+ this.registeredParameters = /* @__PURE__ */ new Map();
824
+ this.initializeSDK();
825
+ _SkillprintManager.instance = this;
826
+ }
827
+ static getInstance() {
828
+ return _SkillprintManager.instance;
829
+ }
830
+ initializeSDK() {
831
+ if (!this.config) {
832
+ console.error("SkillprintConfig not provided to SkillprintManager. SDK will not function.");
833
+ return;
834
+ }
835
+ if (!this.config.activePartnerApiKey) {
836
+ this.log(
837
+ `Partner API Key for ${this.config.targetEnvironment} environment is not set. SDK will not function.`,
838
+ "error" /* ERROR */
839
+ );
840
+ return;
841
+ }
842
+ if (!this.config.activeApiBaseUrl) {
843
+ this.log(
844
+ `API Base URL for ${this.config.targetEnvironment} environment is not set. SDK will not function.`,
845
+ "error" /* ERROR */
846
+ );
847
+ return;
848
+ }
849
+ this.apiClient = new SkillprintAPIClient(
850
+ this.config.activeApiBaseUrl,
851
+ this.config.activePartnerApiKey,
852
+ this.log.bind(this)
853
+ );
854
+ this.screenshotUtility = new ScreenshotUtility(this.log.bind(this));
855
+ this.config.gameParameters.forEach((paramDefOptions) => {
856
+ const paramDef = new ParameterDefinition(paramDefOptions);
857
+ if (!this.registeredParameters.has(paramDef.parameterName)) {
858
+ this.registeredParameters.set(paramDef.parameterName, paramDef);
859
+ } else {
860
+ this.log(
861
+ `Duplicate parameter name found in config: ${paramDef.parameterName}. Using first occurrence.`,
862
+ "warning" /* WARNING */
863
+ );
864
+ }
865
+ });
866
+ this.log(
867
+ `Skillprint SDK Initialized for ${this.config.targetEnvironment} environment (URL: ${this.config.activeApiBaseUrl}).`
868
+ );
869
+ }
870
+ /**
871
+ * Registers a modifier callback action for a named parameter.
872
+ * @param parameterName Unique parameter identifier
873
+ * @param updateAction Callback invoked when Skillprint modifies this parameter
874
+ * @param expectedType Optional type check string ('number', 'integer', 'boolean')
875
+ */
876
+ registerParameterModifier(parameterName, updateAction, expectedType = null) {
877
+ const paramDef = this.registeredParameters.get(parameterName);
878
+ if (paramDef) {
879
+ if (expectedType) {
880
+ let typeMatch = false;
881
+ if (expectedType === "number" && paramDef.type === "Float" /* FLOAT */) typeMatch = true;
882
+ else if (expectedType === "integer" && paramDef.type === "Integer" /* INTEGER */) typeMatch = true;
883
+ else if (expectedType === "boolean" && paramDef.type === "Boolean" /* BOOLEAN */) typeMatch = true;
884
+ if (!typeMatch) {
885
+ this.log(
886
+ `Type mismatch for parameter '${parameterName}'. Expected ${paramDef.type}, but got ${expectedType}. Modifier not registered.`,
887
+ "error" /* ERROR */
888
+ );
889
+ return;
890
+ }
891
+ }
892
+ paramDef.updateAction = (value) => {
893
+ try {
894
+ updateAction(value);
895
+ } catch (error) {
896
+ const message = error instanceof Error ? error.message : String(error);
897
+ this.log(`Error in update action for parameter '${parameterName}': ${message}`, "error" /* ERROR */);
898
+ }
899
+ };
900
+ this.log(`Parameter '${parameterName}' modifier registered successfully.`);
901
+ } else {
902
+ this.log(
903
+ `Attempted to register modifier for undefined parameter: '${parameterName}'. Ensure it's in SkillprintConfig.`,
904
+ "warning" /* WARNING */
905
+ );
906
+ }
907
+ }
908
+ /**
909
+ * Starts a new Skillprint AI gameplay session.
910
+ * @param targetMood Target optimization mood category
911
+ * @param customPlayerId Optional external player identifier
912
+ */
913
+ async startGameSession(targetMood, customPlayerId = null) {
914
+ if (this.isSessionActive) {
915
+ this.log("Session already active. Call stopGameSession first.", "warning" /* WARNING */);
916
+ return;
917
+ }
918
+ if (!this.config || !this.config.activePartnerApiKey || !this.config.activeApiBaseUrl || !this.apiClient) {
919
+ this.log("SDK not configured properly (check environment settings). Cannot start session.", "error" /* ERROR */);
920
+ return;
921
+ }
922
+ this.currentSessionId = this.generateSessionId();
923
+ this.isSessionActive = true;
924
+ this.log(`Starting game session for ${this.config.targetEnvironment} environment.`, "info" /* INFO */);
925
+ const parameterInfos = Array.from(this.registeredParameters.values()).map(
926
+ (p) => new ParameterInfo(
927
+ p.parameterName,
928
+ String(p.type),
929
+ p.description,
930
+ p.type === "Float" /* FLOAT */ || p.type === "Integer" /* INTEGER */ ? p.minValue.toString() : null,
931
+ p.type === "Float" /* FLOAT */ || p.type === "Integer" /* INTEGER */ ? p.maxValue.toString() : null
932
+ )
933
+ );
934
+ try {
935
+ const result = await this.apiClient.startSession(
936
+ this.currentSessionId,
937
+ targetMood,
938
+ customPlayerId,
939
+ this.config.gameName,
940
+ parameterInfos
941
+ );
942
+ this.log(`Skillprint session started: ${this.currentSessionId}. Response: ${result.data}`);
943
+ this.startScreenshotCaptureLoop();
944
+ this.startScreenshotPostLoop();
945
+ this.startPollResultsLoop();
946
+ } catch (error) {
947
+ const message = error instanceof Error ? error.message : String(error);
948
+ this.log(`Failed to start Skillprint session: ${message}`, "error" /* ERROR */);
949
+ this.isSessionActive = false;
950
+ this.currentSessionId = null;
951
+ }
952
+ }
953
+ /**
954
+ * Stops the active Skillprint session and clears all loop timers.
955
+ */
956
+ stopGameSession() {
957
+ if (!this.isSessionActive) {
958
+ this.log("No active session to stop.", "warning" /* WARNING */);
959
+ return;
960
+ }
961
+ this.log(`Stopping Skillprint session: ${this.currentSessionId}`);
962
+ this.isSessionActive = false;
963
+ if (this.screenshotCaptureTimer) clearInterval(this.screenshotCaptureTimer);
964
+ if (this.screenshotPostTimer) clearInterval(this.screenshotPostTimer);
965
+ if (this.pollResultsTimer) clearInterval(this.pollResultsTimer);
966
+ this.screenshotCaptureTimer = null;
967
+ this.screenshotPostTimer = null;
968
+ this.pollResultsTimer = null;
969
+ this.screenshotQueue = [];
970
+ this.currentSessionId = null;
971
+ this.log("Skillprint session stopped.");
972
+ }
973
+ startScreenshotCaptureLoop() {
974
+ this.screenshotCaptureTimer = setInterval(async () => {
975
+ if (!this.isSessionActive || !this.screenshotUtility) return;
976
+ const canvas = this.canvasProvider?.();
977
+ if (!canvas) {
978
+ this.log("No canvas available for screenshot capture", "warning" /* WARNING */);
979
+ return;
980
+ }
981
+ const screenshot = await this.screenshotUtility.captureScreenshot(canvas);
982
+ if (screenshot) {
983
+ if (this.screenshotQueue.length < 50) {
984
+ this.screenshotQueue.push(screenshot);
985
+ this.log(`Screenshot captured. Queue size: ${this.screenshotQueue.length}`);
986
+ } else {
987
+ this.log("Screenshot queue full. Discarding new screenshot.", "warning" /* WARNING */);
988
+ }
989
+ }
990
+ }, this.config.screenshotIntervalSeconds * 1e3);
991
+ }
992
+ startScreenshotPostLoop() {
993
+ this.screenshotPostTimer = setInterval(async () => {
994
+ if (!this.isSessionActive || this.screenshotQueue.length === 0 || !this.apiClient || !this.currentSessionId) return;
995
+ const batchSize = Math.max(
996
+ 1,
997
+ Math.ceil(this.config.screenshotPostIntervalSeconds / this.config.screenshotIntervalSeconds) * 2
998
+ );
999
+ const batchToPost = this.screenshotQueue.splice(0, Math.min(this.screenshotQueue.length, batchSize));
1000
+ if (batchToPost.length > 0) {
1001
+ this.log(`Posting ${batchToPost.length} screenshots...`);
1002
+ try {
1003
+ const result = await this.apiClient.postScreenshots(this.currentSessionId, batchToPost, false);
1004
+ this.log(`Successfully posted ${batchToPost.length} screenshots. Response: ${result.data}`);
1005
+ } catch (error) {
1006
+ const message = error instanceof Error ? error.message : String(error);
1007
+ this.log(`Failed to post screenshots: ${message}`, "error" /* ERROR */);
1008
+ }
1009
+ }
1010
+ }, this.config.screenshotPostIntervalSeconds * 1e3);
1011
+ }
1012
+ startPollResultsLoop() {
1013
+ this.pollResultsTimer = setInterval(async () => {
1014
+ if (!this.isSessionActive || !this.apiClient || !this.currentSessionId) return;
1015
+ try {
1016
+ const result = await this.apiClient.pollParameterResults(this.currentSessionId);
1017
+ if (result.success && result.data && result.data.length > 0) {
1018
+ this.log(`Received ${result.data.length} parameter updates from API.`);
1019
+ this.applyParameterUpdates(result.data);
1020
+ }
1021
+ } catch (error) {
1022
+ const message = error instanceof Error ? error.message : String(error);
1023
+ this.log(`Failed to poll results: ${message}`, "warning" /* WARNING */);
1024
+ }
1025
+ }, this.config.pollResultsIntervalSeconds * 1e3);
1026
+ }
1027
+ applyParameterUpdates(updates) {
1028
+ updates.forEach((update) => {
1029
+ const parsed = update.getParsedValue();
1030
+ this.log(
1031
+ `[DEBUG] Received update - Name: ${update.parameterName}, Value: '${String(parsed)}', Type: ${typeof parsed}`,
1032
+ "info" /* INFO */
1033
+ );
1034
+ const paramDef = this.registeredParameters.get(update.parameterName);
1035
+ if (paramDef) {
1036
+ if (!paramDef.updateAction) {
1037
+ this.log(
1038
+ `Parameter '${update.parameterName}' received from API but has no registered modifier. Skipping.`,
1039
+ "warning" /* WARNING */
1040
+ );
1041
+ return;
1042
+ }
1043
+ const convertedValue = update.convertToParameterType(paramDef);
1044
+ if (convertedValue !== null && paramDef.isValid(convertedValue)) {
1045
+ try {
1046
+ this.log(`Applying update: ${paramDef.parameterName} = ${String(convertedValue)} (Type: ${String(paramDef.type)})`);
1047
+ paramDef.updateAction(convertedValue);
1048
+ } catch (error) {
1049
+ const message = error instanceof Error ? error.message : String(error);
1050
+ this.log(`Error applying update for ${paramDef.parameterName}: ${message}`, "error" /* ERROR */);
1051
+ }
1052
+ } else {
1053
+ this.log(
1054
+ `Invalid value or type for parameter ${paramDef.parameterName}: '${String(update.newValue)}'. Expected type: ${String(paramDef.type)}, Range: ${paramDef.minValue}-${paramDef.maxValue}. Skipping.`,
1055
+ "warning" /* WARNING */
1056
+ );
1057
+ }
1058
+ } else {
1059
+ this.log(`Received update for unknown parameter: ${update.parameterName}. Skipping.`, "warning" /* WARNING */);
1060
+ }
1061
+ });
1062
+ }
1063
+ generateSessionId() {
1064
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
1065
+ const r = Math.random() * 16 | 0;
1066
+ const v = c === "x" ? r : r & 3 | 8;
1067
+ return v.toString(16);
1068
+ });
1069
+ }
1070
+ log(message, level = "info" /* INFO */) {
1071
+ if (!this.config.enableDebugLogging && level === "info" /* INFO */) return;
1072
+ const prefix = "[SkillprintSDK]";
1073
+ switch (level) {
1074
+ case "info" /* INFO */:
1075
+ console.log(`${prefix} ${message}`);
1076
+ break;
1077
+ case "warning" /* WARNING */:
1078
+ console.warn(`${prefix} ${message}`);
1079
+ break;
1080
+ case "error" /* ERROR */:
1081
+ console.error(`${prefix} ${message}`);
1082
+ break;
1083
+ }
1084
+ }
1085
+ getCurrentSessionId() {
1086
+ return this.currentSessionId;
1087
+ }
1088
+ getConfig() {
1089
+ return this.config;
1090
+ }
1091
+ // WebGL convenience methods
1092
+ startGameSessionFromUrl(fallbackMood = "relax" /* RELAX */, fallbackPlayerId = null) {
1093
+ SkillprintSessionHelper.startSessionWithUrlParams(this, fallbackMood, fallbackPlayerId);
1094
+ }
1095
+ startGameSessionWithOverrides(fallbackMood = "relax" /* RELAX */, fallbackPlayerId = null, overrideMood = null, overridePlayerId = null) {
1096
+ SkillprintSessionHelper.startSessionWithUrlParams(
1097
+ this,
1098
+ fallbackMood,
1099
+ fallbackPlayerId,
1100
+ overrideMood,
1101
+ overridePlayerId
1102
+ );
1103
+ }
1104
+ getUrlParametersInfo() {
1105
+ if (!WebGLUrlParameterExtractor.isUrlParameterSupported()) {
1106
+ return "URL parameters not supported on this platform (not in browser)";
1107
+ }
1108
+ const currentUrl = WebGLUrlParameterExtractor.getCurrentUrl();
1109
+ const urlParams = WebGLUrlParameterExtractor.getSkillprintUrlParameters();
1110
+ return `Current URL: ${currentUrl}
1111
+ Mood Parameter: '${urlParams.targetMood || "not found"}'
1112
+ Player ID Parameter: '${urlParams.playerId || "not found"}'`;
1113
+ }
1114
+ };
1115
+ _SkillprintManager.instance = null;
1116
+ var SkillprintManager = _SkillprintManager;
1117
+
1118
+ // src/adapters/phaser.ts
1119
+ var PhaserSkillprintAdapter = class _PhaserSkillprintAdapter {
1120
+ constructor(scene, config) {
1121
+ this.scene = scene;
1122
+ this.config = config;
1123
+ this.manager = new SkillprintManager(config, () => this.scene.game.canvas);
1124
+ }
1125
+ static create(scene, config) {
1126
+ return new _PhaserSkillprintAdapter(scene, config);
1127
+ }
1128
+ startSession(targetMood, customPlayerId = null) {
1129
+ return this.manager.startGameSession(targetMood, customPlayerId);
1130
+ }
1131
+ stopSession() {
1132
+ this.manager.stopGameSession();
1133
+ }
1134
+ registerParameter(parameterName, updateAction, expectedType = null) {
1135
+ this.manager.registerParameterModifier(parameterName, updateAction, expectedType);
1136
+ }
1137
+ /**
1138
+ * Phaser-specific helper to register sprite property updates directly.
1139
+ */
1140
+ registerSpriteProperty(parameterName, sprite, propertyName) {
1141
+ this.manager.registerParameterModifier(parameterName, (value) => {
1142
+ sprite[propertyName] = value;
1143
+ });
1144
+ }
1145
+ };
1146
+
1147
+ // src/adapters/three.ts
1148
+ var ThreeSkillprintAdapter = class _ThreeSkillprintAdapter {
1149
+ constructor(renderer, config) {
1150
+ this.renderer = renderer;
1151
+ this.config = config;
1152
+ this.manager = new SkillprintManager(config, () => this.renderer.domElement);
1153
+ }
1154
+ static create(renderer, config) {
1155
+ return new _ThreeSkillprintAdapter(renderer, config);
1156
+ }
1157
+ startSession(targetMood, customPlayerId = null) {
1158
+ return this.manager.startGameSession(targetMood, customPlayerId);
1159
+ }
1160
+ stopSession() {
1161
+ this.manager.stopGameSession();
1162
+ }
1163
+ registerParameter(parameterName, updateAction, expectedType = null) {
1164
+ this.manager.registerParameterModifier(parameterName, updateAction, expectedType);
1165
+ }
1166
+ /**
1167
+ * Three.js-specific helper to register object or nested material property updates.
1168
+ */
1169
+ registerObjectProperty(parameterName, object, propertyName) {
1170
+ this.manager.registerParameterModifier(parameterName, (value) => {
1171
+ if (propertyName.includes(".")) {
1172
+ const props = propertyName.split(".");
1173
+ let target = object;
1174
+ for (let i = 0; i < props.length - 1; i++) {
1175
+ target = target[props[i]];
1176
+ }
1177
+ target[props[props.length - 1]] = value;
1178
+ } else {
1179
+ object[propertyName] = value;
1180
+ }
1181
+ });
1182
+ }
1183
+ };
1184
+
1185
+ // src/adapters/pixi.ts
1186
+ var PixiSkillprintAdapter = class _PixiSkillprintAdapter {
1187
+ constructor(app, config) {
1188
+ this.app = app;
1189
+ this.config = config;
1190
+ this.manager = new SkillprintManager(config, () => this.app.view || this.app.canvas || null);
1191
+ }
1192
+ static create(app, config) {
1193
+ return new _PixiSkillprintAdapter(app, config);
1194
+ }
1195
+ startSession(targetMood, customPlayerId = null) {
1196
+ return this.manager.startGameSession(targetMood, customPlayerId);
1197
+ }
1198
+ stopSession() {
1199
+ this.manager.stopGameSession();
1200
+ }
1201
+ registerParameter(parameterName, updateAction, expectedType = null) {
1202
+ this.manager.registerParameterModifier(parameterName, updateAction, expectedType);
1203
+ }
1204
+ /**
1205
+ * PixiJS-specific helper to register display object property updates.
1206
+ */
1207
+ registerDisplayObjectProperty(parameterName, displayObject, propertyName) {
1208
+ this.manager.registerParameterModifier(parameterName, (value) => {
1209
+ displayObject[propertyName] = value;
1210
+ });
1211
+ }
1212
+ };
1213
+
1214
+ // src/adapters/canvas.ts
1215
+ var GenericCanvasSkillprintAdapter = class _GenericCanvasSkillprintAdapter {
1216
+ constructor(canvas, config) {
1217
+ this.canvas = canvas;
1218
+ this.config = config;
1219
+ this.manager = new SkillprintManager(config, () => this.canvas);
1220
+ }
1221
+ static create(canvas, config) {
1222
+ return new _GenericCanvasSkillprintAdapter(canvas, config);
1223
+ }
1224
+ startSession(targetMood, customPlayerId = null) {
1225
+ return this.manager.startGameSession(targetMood, customPlayerId);
1226
+ }
1227
+ stopSession() {
1228
+ this.manager.stopGameSession();
1229
+ }
1230
+ registerParameter(parameterName, updateAction, expectedType = null) {
1231
+ this.manager.registerParameterModifier(parameterName, updateAction, expectedType);
1232
+ }
1233
+ };
1234
+ return __toCommonJS(index_exports);
1235
+ })();
1236
+ //# sourceMappingURL=index.global.js.map