@oasiz/sdk 1.5.6 → 1.6.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/dist/index.cjs CHANGED
@@ -20,36 +20,38 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- addScore: () => addScore,
24
- enableLogOverlay: () => enableLogOverlay,
23
+ consume: () => consume,
24
+ emitScoreConfig: () => emitScoreConfig,
25
25
  flushGameState: () => flushGameState,
26
+ getEntitlements: () => getEntitlements,
26
27
  getGameId: () => getGameId,
28
+ getJemBalance: () => getJemBalance,
27
29
  getPlayerAvatar: () => getPlayerAvatar,
28
- getPlayerCharacter: () => getPlayerCharacter,
29
- getPlayerId: () => getPlayerId,
30
30
  getPlayerName: () => getPlayerName,
31
+ getProducts: () => getProducts,
32
+ getQuantity: () => getQuantity,
31
33
  getRoomCode: () => getRoomCode,
32
- getSafeAreaTop: () => getSafeAreaTop,
33
- getViewportInsets: () => getViewportInsets,
34
+ hasEntitlement: () => hasEntitlement,
34
35
  leaveGame: () => leaveGame,
35
36
  loadGameState: () => loadGameState,
36
37
  oasiz: () => oasiz,
37
38
  onBackButton: () => onBackButton,
39
+ onEntitlementsChanged: () => onEntitlementsChanged,
40
+ onJemBalanceChanged: () => onJemBalanceChanged,
38
41
  onLeaveGame: () => onLeaveGame,
39
42
  onPause: () => onPause,
40
43
  onResume: () => onResume,
41
- openInviteModal: () => openInviteModal,
44
+ purchase: () => purchase,
42
45
  saveGameState: () => saveGameState,
43
- setLeaderboardVisible: () => setLeaderboardVisible,
44
- setScore: () => setScore,
45
46
  share: () => share,
46
47
  shareRoomCode: () => shareRoomCode,
47
48
  submitScore: () => submitScore,
49
+ syncProducts: () => syncProducts,
48
50
  triggerHaptic: () => triggerHaptic
49
51
  });
50
52
  module.exports = __toCommonJS(index_exports);
51
53
 
52
- // src/character.ts
54
+ // src/haptics.ts
53
55
  function isDevelopment() {
54
56
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
55
57
  return nodeEnv !== "production";
@@ -60,1014 +62,72 @@ function getBridgeWindow() {
60
62
  }
61
63
  return window;
62
64
  }
63
- function warnMissingBridge(methodName) {
64
- if (isDevelopment()) {
65
- console.warn(
66
- "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
67
- );
68
- }
69
- }
70
- async function getPlayerCharacter() {
71
- const bridge = getBridgeWindow();
72
- if (typeof bridge?.__oasizGetPlayerCharacter !== "function") {
73
- warnMissingBridge("getPlayerCharacter");
74
- return null;
75
- }
76
- try {
77
- const result = await bridge.__oasizGetPlayerCharacter();
78
- return result ?? null;
79
- } catch (error) {
80
- if (isDevelopment()) {
81
- console.error("[oasiz/sdk] getPlayerCharacter failed:", error);
82
- }
83
- return null;
84
- }
85
- }
86
-
87
- // src/haptics.ts
88
- function isDevelopment2() {
89
- const nodeEnv = globalThis.process?.env?.NODE_ENV;
90
- return nodeEnv !== "production";
91
- }
92
- function getBridgeWindow2() {
93
- if (typeof window === "undefined") {
94
- return void 0;
95
- }
96
- return window;
97
- }
98
65
  function triggerHaptic(type) {
99
- const bridge = getBridgeWindow2();
66
+ const bridge = getBridgeWindow();
100
67
  if (typeof bridge?.triggerHaptic === "function") {
101
68
  bridge.triggerHaptic(type);
102
69
  return;
103
70
  }
104
- if (isDevelopment2()) {
71
+ if (isDevelopment()) {
105
72
  console.warn(
106
73
  "[oasiz/sdk] triggerHaptic bridge is unavailable. This is expected in local development."
107
74
  );
108
75
  }
109
76
  }
110
77
 
111
- // src/log-overlay.ts
112
- var CONSOLE_METHODS = [
113
- "debug",
114
- "log",
115
- "info",
116
- "warn",
117
- "error"
118
- ];
119
- var DEFAULT_MAX_ENTRIES = 200;
120
- var DEFAULT_TITLE = "SDK Logs";
121
- var OVERLAY_MARGIN = 12;
122
- var DEFAULT_COLLAPSED_WIDTH = 156;
123
- var DEFAULT_COLLAPSED_HEIGHT = 52;
124
- var DEFAULT_EXPANDED_WIDTH = 565;
125
- var DEFAULT_EXPANDED_HEIGHT = 372;
126
- var DRAG_THRESHOLD_PX = 6;
127
- var MIN_EXPANDED_WIDTH = 160;
128
- var MIN_EXPANDED_HEIGHT = 110;
129
- var RESIZE_HOTSPOT_PX = 28;
130
- var TOP_DRAG_ZONE_PX = 44;
131
- var NOOP_HANDLE = {
132
- clear() {
133
- },
134
- destroy() {
135
- },
136
- hide() {
137
- },
138
- isVisible() {
139
- return false;
140
- },
141
- show() {
142
- }
143
- };
144
- function getBrowserWindow() {
145
- if (typeof window === "undefined") {
146
- return void 0;
147
- }
148
- return window;
149
- }
150
- function getDocument() {
151
- if (typeof document === "undefined") {
152
- return void 0;
153
- }
154
- return document;
155
- }
156
- function clampMaxEntries(value) {
157
- if (!Number.isFinite(value)) {
158
- return DEFAULT_MAX_ENTRIES;
159
- }
160
- return Math.max(10, Math.floor(value));
161
- }
162
- function createConsoleSnapshot() {
163
- const fallback = console.log.bind(console);
164
- return {
165
- debug: typeof console.debug === "function" ? console.debug.bind(console) : fallback,
166
- log: fallback,
167
- info: typeof console.info === "function" ? console.info.bind(console) : fallback,
168
- warn: typeof console.warn === "function" ? console.warn.bind(console) : fallback,
169
- error: typeof console.error === "function" ? console.error.bind(console) : fallback
170
- };
171
- }
172
- function formatTimestamp(timestamp) {
173
- const date = new Date(timestamp);
174
- const hours = String(date.getHours()).padStart(2, "0");
175
- const minutes = String(date.getMinutes()).padStart(2, "0");
176
- const seconds = String(date.getSeconds()).padStart(2, "0");
177
- const milliseconds = String(date.getMilliseconds()).padStart(3, "0");
178
- return "[" + hours + ":" + minutes + ":" + seconds + "." + milliseconds + "]";
179
- }
180
- function safeStringify(value) {
181
- const seen = /* @__PURE__ */ new WeakSet();
182
- try {
183
- return JSON.stringify(
184
- value,
185
- (_key, candidate) => {
186
- if (typeof candidate === "bigint") {
187
- return candidate.toString() + "n";
188
- }
189
- if (typeof candidate === "object" && candidate !== null) {
190
- if (seen.has(candidate)) {
191
- return "[Circular]";
192
- }
193
- seen.add(candidate);
194
- }
195
- return candidate;
196
- },
197
- 2
198
- ) ?? String(value);
199
- } catch {
200
- return String(value);
201
- }
202
- }
203
- function formatArg(value) {
204
- if (typeof value === "string") {
205
- return value;
206
- }
207
- if (value instanceof Error) {
208
- if (value.stack) {
209
- return value.stack;
210
- }
211
- return value.name + ": " + value.message;
212
- }
213
- if (typeof value === "undefined") {
214
- return "undefined";
215
- }
216
- if (typeof value === "function") {
217
- return "[Function " + (value.name || "anonymous") + "]";
218
- }
219
- return safeStringify(value);
220
- }
221
- function formatEntryMessage(args) {
222
- const message = args.map(formatArg).join(" ");
223
- if (message.length <= 4e3) {
224
- return message;
225
- }
226
- return message.slice(0, 3997) + "...";
227
- }
228
- function createEntry(level, args, id) {
229
- return {
230
- id,
231
- level,
232
- message: formatEntryMessage(args),
233
- timestamp: Date.now()
234
- };
235
- }
236
- function createButton(label) {
237
- const button = document.createElement("button");
238
- button.type = "button";
239
- button.textContent = label;
240
- button.style.cssText = [
241
- "appearance:none",
242
- "border:1px solid rgba(255,255,255,0.18)",
243
- "background:rgba(255,255,255,0.06)",
244
- "color:#f8fafc",
245
- "border-radius:999px",
246
- "padding:6px 10px",
247
- "font:600 12px/1.1 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace",
248
- "cursor:pointer"
249
- ].join(";");
250
- return button;
251
- }
252
- function getLevelAccent(level) {
253
- if (level === "error") {
254
- return {
255
- lineBackground: "rgba(255, 109, 122, 0.08)"
256
- };
257
- }
258
- if (level === "warn") {
259
- return {
260
- lineBackground: "rgba(255, 196, 94, 0.07)"
261
- };
262
- }
263
- if (level === "info") {
264
- return {
265
- lineBackground: "rgba(82, 187, 255, 0.07)"
266
- };
267
- }
268
- if (level === "debug") {
269
- return {
270
- lineBackground: "rgba(166, 137, 255, 0.07)"
271
- };
272
- }
273
- return {
274
- lineBackground: "rgba(117, 235, 191, 0.06)"
275
- };
276
- }
277
- function getViewportSize() {
278
- const browserWindow = getBrowserWindow();
279
- return {
280
- width: Math.max(320, browserWindow?.innerWidth ?? 1280),
281
- height: Math.max(240, browserWindow?.innerHeight ?? 720)
282
- };
283
- }
284
- function clampPanelSize(size) {
285
- const viewport = getViewportSize();
286
- const maxWidth = Math.max(MIN_EXPANDED_WIDTH, viewport.width - OVERLAY_MARGIN * 2);
287
- const maxHeight = Math.max(
288
- MIN_EXPANDED_HEIGHT,
289
- viewport.height - OVERLAY_MARGIN * 2
290
- );
291
- return {
292
- width: Math.min(maxWidth, Math.max(MIN_EXPANDED_WIDTH, size.width)),
293
- height: Math.min(maxHeight, Math.max(MIN_EXPANDED_HEIGHT, size.height))
294
- };
295
- }
296
- function getOverlaySize(state) {
297
- if (state.expanded && state.panelSize) {
298
- return state.panelSize;
299
- }
300
- const rect = state.ui?.root && typeof state.ui.root.getBoundingClientRect === "function" ? state.ui.root.getBoundingClientRect() : null;
301
- if (rect && Number.isFinite(rect.width) && Number.isFinite(rect.height)) {
302
- return {
303
- width: Math.max(1, rect.width),
304
- height: Math.max(1, rect.height)
305
- };
306
- }
307
- return state.expanded ? clampPanelSize({
308
- width: DEFAULT_EXPANDED_WIDTH,
309
- height: DEFAULT_EXPANDED_HEIGHT
310
- }) : { width: DEFAULT_COLLAPSED_WIDTH, height: DEFAULT_COLLAPSED_HEIGHT };
311
- }
312
- function applyPanelSize(state) {
313
- if (!state.ui) {
314
- return;
315
- }
316
- if (!state.expanded) {
317
- state.ui.root.style.width = "auto";
318
- state.ui.panel.style.width = "min(565px, calc(100vw - 24px))";
319
- state.ui.panel.style.height = "auto";
320
- state.ui.entries.style.maxHeight = "min(36vh, 280px)";
321
- return;
322
- }
323
- if (!state.panelSize) {
324
- state.ui.root.style.width = "min(565px, calc(100vw - 24px))";
325
- state.ui.panel.style.width = "min(565px, calc(100vw - 24px))";
326
- state.ui.panel.style.height = "auto";
327
- state.ui.entries.style.maxHeight = "min(36vh, 280px)";
328
- return;
329
- }
330
- const nextSize = clampPanelSize(
331
- state.panelSize
332
- );
333
- state.panelSize = nextSize;
334
- state.ui.root.style.width = nextSize.width + "px";
335
- state.ui.panel.style.width = "100%";
336
- state.ui.panel.style.height = nextSize.height + "px";
337
- state.ui.entries.style.maxHeight = Math.max(72, nextSize.height - 88) + "px";
338
- }
339
- function clampPosition(point, state) {
340
- const viewport = getViewportSize();
341
- const size = getOverlaySize(state);
342
- const maxX = Math.max(OVERLAY_MARGIN, viewport.width - size.width - OVERLAY_MARGIN);
343
- const maxY = Math.max(
344
- OVERLAY_MARGIN,
345
- viewport.height - size.height - OVERLAY_MARGIN
346
- );
347
- return {
348
- x: Math.min(maxX, Math.max(OVERLAY_MARGIN, point.x)),
349
- y: Math.min(maxY, Math.max(OVERLAY_MARGIN, point.y))
350
- };
351
- }
352
- function applyOverlayPosition(state) {
353
- if (!state.ui) {
354
- return;
355
- }
356
- if (!state.position) {
357
- const viewport = getViewportSize();
358
- const size = getOverlaySize(state);
359
- state.position = clampPosition(
360
- {
361
- x: viewport.width - size.width - OVERLAY_MARGIN,
362
- y: viewport.height - size.height - OVERLAY_MARGIN
363
- },
364
- state
365
- );
366
- } else {
367
- state.position = clampPosition(state.position, state);
368
- }
369
- state.ui.root.style.left = state.position.x + "px";
370
- state.ui.root.style.top = state.position.y + "px";
371
- }
372
- function getPointFromMouseEvent(event) {
373
- return { x: event.clientX, y: event.clientY };
374
- }
375
- function getPointFromTouchEvent(event) {
376
- const touch = event.touches[0] ?? event.changedTouches[0];
377
- if (!touch) {
378
- return null;
379
- }
380
- return { x: touch.clientX, y: touch.clientY };
381
- }
382
- function stopDragging(state) {
383
- if (state.isDragging || state.isResizing) {
384
- state.suppressToggleClickUntil = Date.now() + 180;
385
- }
386
- state.isDragging = false;
387
- state.dragStartPoint = null;
388
- state.lastDragPoint = null;
389
- state.removeDragListeners?.();
390
- state.removeDragListeners = null;
391
- if (state.ui) {
392
- state.ui.panel.style.cursor = "default";
393
- state.ui.dragZone.style.cursor = "grab";
394
- state.ui.toggleButton.style.cursor = "grab";
395
- }
396
- }
397
- function stopResizing(state) {
398
- if (state.isResizing) {
399
- state.suppressToggleClickUntil = Date.now() + 180;
400
- }
401
- state.isResizing = false;
402
- state.resizeStartPoint = null;
403
- state.resizeStartSize = null;
404
- state.removeResizeListeners?.();
405
- state.removeResizeListeners = null;
406
- }
407
- function beginDragTracking(state, startPoint) {
408
- const doc = getDocument();
409
- if (!doc) {
410
- return;
411
- }
412
- stopResizing(state);
413
- stopDragging(state);
414
- state.dragMoved = false;
415
- state.dragStartPoint = startPoint;
416
- state.lastDragPoint = startPoint;
417
- const handlePointerMove = (nextPoint) => {
418
- if (!state.dragStartPoint || !state.lastDragPoint || !nextPoint) {
419
- return;
420
- }
421
- if (!state.isDragging) {
422
- const deltaFromStartX = nextPoint.x - state.dragStartPoint.x;
423
- const deltaFromStartY = nextPoint.y - state.dragStartPoint.y;
424
- const distance = Math.sqrt(
425
- deltaFromStartX * deltaFromStartX + deltaFromStartY * deltaFromStartY
426
- );
427
- if (distance < DRAG_THRESHOLD_PX) {
428
- return;
429
- }
430
- state.isDragging = true;
431
- state.dragMoved = true;
432
- if (state.ui) {
433
- state.ui.panel.style.cursor = "grabbing";
434
- state.ui.dragZone.style.cursor = "grabbing";
435
- state.ui.toggleButton.style.cursor = "grabbing";
436
- }
437
- }
438
- const currentPosition = state.position ?? { x: OVERLAY_MARGIN, y: OVERLAY_MARGIN };
439
- state.position = clampPosition(
440
- {
441
- x: currentPosition.x + (nextPoint.x - state.lastDragPoint.x),
442
- y: currentPosition.y + (nextPoint.y - state.lastDragPoint.y)
443
- },
444
- state
445
- );
446
- state.lastDragPoint = nextPoint;
447
- applyOverlayPosition(state);
448
- };
449
- const handleMouseMove = (event) => {
450
- handlePointerMove(getPointFromMouseEvent(event));
451
- };
452
- const handleTouchMove = (event) => {
453
- handlePointerMove(getPointFromTouchEvent(event));
454
- };
455
- const handleMouseUp = () => {
456
- stopDragging(state);
457
- };
458
- const handleTouchEnd = () => {
459
- stopDragging(state);
460
- };
461
- doc.addEventListener("mousemove", handleMouseMove);
462
- doc.addEventListener("mouseup", handleMouseUp);
463
- doc.addEventListener("touchmove", handleTouchMove, { passive: true });
464
- doc.addEventListener("touchend", handleTouchEnd);
465
- doc.addEventListener("touchcancel", handleTouchEnd);
466
- state.removeDragListeners = () => {
467
- doc.removeEventListener("mousemove", handleMouseMove);
468
- doc.removeEventListener("mouseup", handleMouseUp);
469
- doc.removeEventListener("touchmove", handleTouchMove);
470
- doc.removeEventListener("touchend", handleTouchEnd);
471
- doc.removeEventListener("touchcancel", handleTouchEnd);
472
- };
473
- }
474
- function startResizing(state, startPoint) {
475
- const doc = getDocument();
476
- if (!doc) {
477
- return;
478
- }
479
- stopDragging(state);
480
- stopResizing(state);
481
- state.isResizing = true;
482
- state.resizeStartPoint = startPoint;
483
- state.resizeStartSize = state.panelSize ?? clampPanelSize({ width: DEFAULT_EXPANDED_WIDTH, height: DEFAULT_EXPANDED_HEIGHT });
484
- const handleResizeMove = (nextPoint) => {
485
- if (!state.isResizing || !state.resizeStartPoint || !state.resizeStartSize || !nextPoint) {
486
- return;
487
- }
488
- state.panelSize = clampPanelSize({
489
- width: state.resizeStartSize.width + (nextPoint.x - state.resizeStartPoint.x),
490
- height: state.resizeStartSize.height + (nextPoint.y - state.resizeStartPoint.y)
491
- });
492
- applyPanelSize(state);
493
- applyOverlayPosition(state);
494
- };
495
- const handleMouseMove = (event) => {
496
- handleResizeMove(getPointFromMouseEvent(event));
497
- };
498
- const handleTouchMove = (event) => {
499
- handleResizeMove(getPointFromTouchEvent(event));
500
- };
501
- const handleFinish = () => {
502
- stopResizing(state);
503
- };
504
- doc.addEventListener("mousemove", handleMouseMove);
505
- doc.addEventListener("mouseup", handleFinish);
506
- doc.addEventListener("touchmove", handleTouchMove, { passive: true });
507
- doc.addEventListener("touchend", handleFinish);
508
- doc.addEventListener("touchcancel", handleFinish);
509
- state.removeResizeListeners = () => {
510
- doc.removeEventListener("mousemove", handleMouseMove);
511
- doc.removeEventListener("mouseup", handleFinish);
512
- doc.removeEventListener("touchmove", handleTouchMove);
513
- doc.removeEventListener("touchend", handleFinish);
514
- doc.removeEventListener("touchcancel", handleFinish);
515
- };
516
- }
517
- function isInBottomRightResizeZone(element, point) {
518
- const rect = element.getBoundingClientRect();
519
- return point.x >= rect.right - RESIZE_HOTSPOT_PX && point.x <= rect.right && point.y >= rect.bottom - RESIZE_HOTSPOT_PX && point.y <= rect.bottom;
520
- }
521
- function canStartDragFromTarget(target) {
522
- if (!(target instanceof Element)) {
523
- return true;
524
- }
525
- if (target.closest("button") || target.closest("a") || target.closest("input") || target.closest("textarea") || target.closest("select")) {
526
- return false;
527
- }
528
- return true;
529
- }
530
- function isInTopDragZone(element, point, zoneHeight) {
531
- const rect = element.getBoundingClientRect();
532
- return point.x >= rect.left && point.x <= rect.right && point.y >= rect.top && point.y <= rect.top + zoneHeight;
533
- }
534
- function attachDragStartListeners(element, state, options = {}) {
535
- element.addEventListener("mousedown", (event) => {
536
- if (!options.allowInteractiveTarget && !canStartDragFromTarget(event.target)) {
537
- return;
538
- }
539
- const point = getPointFromMouseEvent(event);
540
- if (typeof options.topZoneHeight === "number" && !isInTopDragZone(element, point, options.topZoneHeight)) {
541
- return;
542
- }
543
- beginDragTracking(state, point);
544
- });
545
- element.addEventListener("touchstart", (event) => {
546
- if (!options.allowInteractiveTarget && !canStartDragFromTarget(event.target)) {
547
- return;
548
- }
549
- const point = getPointFromTouchEvent(event);
550
- if (!point) {
551
- return;
552
- }
553
- if (typeof options.topZoneHeight === "number" && !isInTopDragZone(element, point, options.topZoneHeight)) {
554
- return;
555
- }
556
- beginDragTracking(state, point);
557
- });
558
- }
559
- function attachCollapsedToggleListeners(element, state) {
560
- const startToggleInteraction = (startPoint) => {
561
- const doc = getDocument();
562
- if (!doc) {
563
- return;
564
- }
565
- beginDragTracking(state, startPoint);
566
- const finishInteraction = () => {
567
- releaseListeners();
568
- if (state.dragMoved || Date.now() < state.suppressToggleClickUntil) {
569
- return;
570
- }
571
- state.expanded = true;
572
- state.unreadCount = 0;
573
- renderOverlay(state);
574
- };
575
- const releaseListeners = () => {
576
- doc.removeEventListener("mouseup", finishInteraction);
577
- doc.removeEventListener("touchend", finishInteraction);
578
- doc.removeEventListener("touchcancel", finishInteraction);
579
- };
580
- doc.addEventListener("mouseup", finishInteraction);
581
- doc.addEventListener("touchend", finishInteraction);
582
- doc.addEventListener("touchcancel", finishInteraction);
583
- };
584
- element.addEventListener("mousedown", (event) => {
585
- event.preventDefault();
586
- startToggleInteraction(getPointFromMouseEvent(event));
587
- });
588
- element.addEventListener("touchstart", (event) => {
589
- const point = getPointFromTouchEvent(event);
590
- if (!point) {
591
- return;
592
- }
593
- event.preventDefault();
594
- startToggleInteraction(point);
595
- });
596
- }
597
- function attachPanelResizeListeners(element, state) {
598
- element.addEventListener("mousedown", (event) => {
599
- if (!state.expanded || !canStartDragFromTarget(event.target)) {
600
- return;
601
- }
602
- const point = getPointFromMouseEvent(event);
603
- if (!isInBottomRightResizeZone(element, point)) {
604
- return;
605
- }
606
- event.preventDefault();
607
- event.stopPropagation();
608
- startResizing(state, point);
609
- });
610
- element.addEventListener("touchstart", (event) => {
611
- if (!state.expanded || !canStartDragFromTarget(event.target)) {
612
- return;
613
- }
614
- const point = getPointFromTouchEvent(event);
615
- if (!point || !isInBottomRightResizeZone(element, point)) {
616
- return;
617
- }
618
- event.preventDefault();
619
- event.stopPropagation();
620
- startResizing(state, point);
621
- });
622
- }
623
- function createOverlayUi(state) {
624
- const root = document.createElement("div");
625
- root.style.cssText = [
626
- "position:fixed",
627
- "left:12px",
628
- "top:12px",
629
- "z-index:2147483647",
630
- "display:flex",
631
- "flex-direction:column",
632
- "align-items:stretch",
633
- "gap:8px",
634
- "width:min(565px, calc(100vw - 24px))",
635
- "pointer-events:none"
636
- ].join(";");
637
- const toggleButton = createButton("Logs");
638
- toggleButton.style.pointerEvents = "auto";
639
- toggleButton.style.alignSelf = "flex-end";
640
- toggleButton.style.display = "inline-flex";
641
- toggleButton.style.alignItems = "center";
642
- toggleButton.style.justifyContent = "center";
643
- toggleButton.style.minHeight = "40px";
644
- toggleButton.style.minWidth = "76px";
645
- toggleButton.style.padding = "8px 14px";
646
- toggleButton.style.textAlign = "center";
647
- toggleButton.style.border = "1px solid rgba(122, 212, 255, 0.22)";
648
- toggleButton.style.background = "linear-gradient(180deg, rgba(13,31,54,0.98), rgba(8,19,37,0.98))";
649
- toggleButton.style.boxShadow = "0 18px 40px rgba(4,12,24,0.34)";
650
- toggleButton.style.cursor = "grab";
651
- toggleButton.style.touchAction = "none";
652
- const panel = document.createElement("div");
653
- panel.style.cssText = [
654
- "position:relative",
655
- "display:flex",
656
- "flex-direction:column",
657
- "width:min(565px, calc(100vw - 24px))",
658
- "max-height:min(48vh, 372px)",
659
- "border-radius:18px",
660
- "border:1px solid rgba(116,167,255,0.16)",
661
- "background:linear-gradient(180deg, rgba(9,19,37,0.98), rgba(5,12,24,0.98))",
662
- "box-shadow:0 28px 64px rgba(2,8,18,0.46)",
663
- "backdrop-filter:blur(16px)",
664
- "overflow:hidden",
665
- "cursor:default",
666
- "pointer-events:auto"
667
- ].join(";");
668
- const dragZone = document.createElement("div");
669
- dragZone.style.cssText = [
670
- "position:absolute",
671
- "top:0",
672
- "left:0",
673
- "right:0",
674
- "height:" + String(TOP_DRAG_ZONE_PX) + "px",
675
- "z-index:1",
676
- "cursor:grab",
677
- "background:transparent",
678
- "pointer-events:auto"
679
- ].join(";");
680
- const controls = document.createElement("div");
681
- controls.style.cssText = [
682
- "position:absolute",
683
- "top:22px",
684
- "right:22px",
685
- "z-index:2",
686
- "display:flex",
687
- "align-items:center",
688
- "gap:8px",
689
- "pointer-events:auto"
690
- ].join(";");
691
- const clearButton = createButton("Clear");
692
- clearButton.style.background = "rgba(255,255,255,0.1)";
693
- clearButton.style.border = "1px solid rgba(255,255,255,0.16)";
694
- clearButton.style.color = "#eef6ff";
695
- clearButton.style.minHeight = "30px";
696
- clearButton.style.padding = "4px 9px";
697
- clearButton.style.fontSize = "11px";
698
- clearButton.style.backdropFilter = "blur(8px)";
699
- const collapseButton = createButton("Hide");
700
- collapseButton.style.background = "rgba(113, 171, 255, 0.12)";
701
- collapseButton.style.border = "1px solid rgba(113, 171, 255, 0.2)";
702
- collapseButton.style.color = "#d9ebff";
703
- collapseButton.style.minHeight = "30px";
704
- collapseButton.style.padding = "4px 9px";
705
- collapseButton.style.fontSize = "11px";
706
- collapseButton.style.backdropFilter = "blur(8px)";
707
- const body = document.createElement("div");
708
- body.style.cssText = [
709
- "position:relative",
710
- "display:flex",
711
- "flex-direction:column",
712
- "padding:12px",
713
- "background:linear-gradient(180deg, rgba(4,10,20,0.88), rgba(3,8,18,0.98))",
714
- "flex:1 1 auto",
715
- "min-height:0"
716
- ].join(";");
717
- const entries = document.createElement("div");
718
- entries.style.cssText = [
719
- "display:flex",
720
- "flex-direction:column",
721
- "gap:0",
722
- "overflow:auto",
723
- "flex:1 1 auto",
724
- "min-height:96px",
725
- "max-height:min(36vh, 280px)",
726
- "padding:0",
727
- "border:1px solid rgba(115,153,212,0.14)",
728
- "border-radius:12px",
729
- "background:rgba(4,10,20,0.82)"
730
- ].join(";");
731
- const emptyState = document.createElement("div");
732
- emptyState.style.cssText = [
733
- "display:flex",
734
- "align-items:center",
735
- "justify-content:center",
736
- "flex:1 1 auto",
737
- "min-height:96px",
738
- "color:rgba(204,222,250,0.6)",
739
- "font:500 12px/1.5 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace",
740
- "text-align:center",
741
- "padding:18px"
742
- ].join(";");
743
- emptyState.textContent = "Console output will appear here.";
744
- collapseButton.addEventListener("click", (event) => {
745
- event.stopPropagation();
746
- state.expanded = false;
747
- renderOverlay(state);
748
- });
749
- clearButton.addEventListener("click", (event) => {
750
- event.stopPropagation();
751
- state.entries = [];
752
- state.unreadCount = 0;
753
- renderOverlay(state);
754
- });
755
- controls.appendChild(clearButton);
756
- controls.appendChild(collapseButton);
757
- entries.appendChild(emptyState);
758
- body.appendChild(entries);
759
- body.appendChild(controls);
760
- panel.appendChild(dragZone);
761
- panel.appendChild(body);
762
- attachDragStartListeners(dragZone, state);
763
- attachPanelResizeListeners(panel, state);
764
- attachCollapsedToggleListeners(toggleButton, state);
765
- root.appendChild(panel);
766
- root.appendChild(toggleButton);
767
- return {
768
- body,
769
- clearButton,
770
- collapseButton,
771
- controls,
772
- dragZone,
773
- emptyState,
774
- entries,
775
- panel,
776
- root,
777
- toggleButton
778
- };
779
- }
780
- function renderOverlay(state) {
781
- if (!state.ui) {
782
- return;
783
- }
784
- state.ui.panel.style.display = state.expanded ? "flex" : "none";
785
- state.ui.toggleButton.style.display = state.expanded ? "none" : "inline-flex";
786
- state.ui.toggleButton.textContent = "Logs";
787
- if (state.entries.length === 0) {
788
- state.ui.entries.style.display = "flex";
789
- state.ui.emptyState.style.display = "flex";
790
- state.ui.entries.replaceChildren(state.ui.emptyState);
791
- applyPanelSize(state);
792
- applyOverlayPosition(state);
793
- return;
794
- }
795
- state.ui.entries.style.display = "flex";
796
- state.ui.emptyState.style.display = "none";
797
- const nextChildren = state.entries.map((entry) => {
798
- const accent = getLevelAccent(entry.level);
799
- const row = document.createElement("div");
800
- row.style.cssText = [
801
- "display:flex",
802
- "align-items:flex-start",
803
- "gap:0",
804
- "padding:4px 12px",
805
- "background:" + accent.lineBackground
806
- ].join(";");
807
- const line = document.createElement("div");
808
- line.textContent = formatTimestamp(entry.timestamp) + " " + entry.level.toUpperCase() + " " + entry.message;
809
- line.style.cssText = [
810
- "color:#ecf4ff",
811
- "font:500 12px/1.5 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace",
812
- "white-space:pre-wrap",
813
- "word-break:break-word",
814
- "flex:1 1 auto"
815
- ].join(";");
816
- row.appendChild(line);
817
- return row;
818
- });
819
- state.ui.entries.replaceChildren(...nextChildren);
820
- state.ui.entries.scrollTop = state.ui.entries.scrollHeight;
821
- applyPanelSize(state);
822
- applyOverlayPosition(state);
823
- }
824
- function mountOverlay(state) {
825
- const doc = getDocument();
826
- if (!doc?.body || state.ui) {
827
- return;
828
- }
829
- state.ui = createOverlayUi(state);
830
- doc.body.appendChild(state.ui.root);
831
- applyPanelSize(state);
832
- applyOverlayPosition(state);
833
- renderOverlay(state);
834
- }
835
- function enqueueEntry(state, level, args) {
836
- state.entries.push(createEntry(level, args, state.nextEntryId));
837
- state.nextEntryId += 1;
838
- if (state.entries.length > state.maxEntries) {
839
- state.entries.splice(0, state.entries.length - state.maxEntries);
840
- }
841
- if (!state.expanded) {
842
- state.unreadCount += 1;
843
- }
844
- renderOverlay(state);
845
- }
846
- function restoreConsole(snapshot) {
847
- for (const method of CONSOLE_METHODS) {
848
- console[method] = snapshot[method];
849
- }
850
- }
851
- function patchConsole(state) {
852
- for (const method of CONSOLE_METHODS) {
853
- const original = state.originalConsole[method];
854
- console[method] = (...args) => {
855
- enqueueEntry(state, method, args);
856
- original(...args);
857
- };
858
- }
859
- }
860
- function cleanupOverlay(browserWindow, state) {
861
- restoreConsole(state.originalConsole);
862
- stopResizing(state);
863
- stopDragging(state);
864
- if (state.domReadyHandler) {
865
- getDocument()?.removeEventListener("DOMContentLoaded", state.domReadyHandler);
866
- state.domReadyHandler = void 0;
867
- }
868
- if (state.resizeHandler && typeof browserWindow.removeEventListener === "function") {
869
- browserWindow.removeEventListener("resize", state.resizeHandler);
870
- state.resizeHandler = void 0;
871
- }
872
- state.ui?.root.remove();
873
- state.ui = null;
874
- delete browserWindow.__oasizLogOverlayController__;
875
- delete browserWindow.__oasizLogOverlayState__;
876
- }
877
- function createController(browserWindow, state) {
878
- return {
879
- retain() {
880
- state.refCount += 1;
881
- this.ensureMounted();
882
- },
883
- ensureMounted() {
884
- const doc = getDocument();
885
- if (!doc) {
886
- return;
887
- }
888
- if (doc.body) {
889
- mountOverlay(state);
890
- applyOverlayPosition(state);
891
- return;
892
- }
893
- if (!state.domReadyHandler) {
894
- state.domReadyHandler = () => {
895
- mountOverlay(state);
896
- state.domReadyHandler = void 0;
897
- };
898
- doc.addEventListener("DOMContentLoaded", state.domReadyHandler, {
899
- once: true
900
- });
901
- }
902
- },
903
- clear() {
904
- state.entries = [];
905
- state.unreadCount = 0;
906
- renderOverlay(state);
907
- },
908
- show() {
909
- state.expanded = true;
910
- state.unreadCount = 0;
911
- renderOverlay(state);
912
- },
913
- hide() {
914
- state.expanded = false;
915
- renderOverlay(state);
916
- },
917
- isVisible() {
918
- return state.expanded;
919
- },
920
- destroy() {
921
- state.refCount = Math.max(0, state.refCount - 1);
922
- if (state.refCount === 0) {
923
- cleanupOverlay(browserWindow, state);
924
- }
925
- }
926
- };
927
- }
928
- function enableLogOverlay(options = {}) {
929
- if (options.enabled === false) {
930
- return NOOP_HANDLE;
931
- }
932
- const browserWindow = getBrowserWindow();
933
- const doc = getDocument();
934
- if (!browserWindow || !doc) {
935
- return NOOP_HANDLE;
936
- }
937
- const existingController = browserWindow.__oasizLogOverlayController__;
938
- const existingState = browserWindow.__oasizLogOverlayState__;
939
- if (existingController && existingState) {
940
- existingController.retain();
941
- if (typeof options.maxEntries === "number") {
942
- existingState.maxEntries = clampMaxEntries(options.maxEntries);
943
- if (existingState.entries.length > existingState.maxEntries) {
944
- existingState.entries.splice(
945
- 0,
946
- existingState.entries.length - existingState.maxEntries
947
- );
948
- }
949
- }
950
- if (typeof options.collapsed === "boolean") {
951
- existingState.expanded = !options.collapsed;
952
- applyPanelSize(existingState);
953
- if (existingState.expanded) {
954
- existingState.unreadCount = 0;
955
- }
956
- }
957
- if (typeof options.title === "string" && options.title.trim().length > 0) {
958
- existingState.title = options.title.trim();
959
- }
960
- existingController.ensureMounted();
961
- renderOverlay(existingState);
962
- return existingController;
963
- }
964
- const state = {
965
- dragMoved: false,
966
- dragStartPoint: null,
967
- entries: [],
968
- expanded: options.collapsed !== true,
969
- isDragging: false,
970
- isResizing: false,
971
- lastDragPoint: null,
972
- maxEntries: clampMaxEntries(options.maxEntries),
973
- nextEntryId: 1,
974
- originalConsole: createConsoleSnapshot(),
975
- panelSize: null,
976
- position: null,
977
- refCount: 1,
978
- removeDragListeners: null,
979
- removeResizeListeners: null,
980
- resizeStartPoint: null,
981
- resizeStartSize: null,
982
- suppressToggleClickUntil: 0,
983
- title: typeof options.title === "string" && options.title.trim().length > 0 ? options.title.trim() : DEFAULT_TITLE,
984
- resizeHandler: void 0,
985
- ui: null,
986
- unreadCount: 0
987
- };
988
- const controller = createController(browserWindow, state);
989
- browserWindow.__oasizLogOverlayState__ = state;
990
- browserWindow.__oasizLogOverlayController__ = controller;
991
- patchConsole(state);
992
- if (typeof browserWindow.addEventListener === "function") {
993
- state.resizeHandler = () => {
994
- applyOverlayPosition(state);
995
- };
996
- browserWindow.addEventListener("resize", state.resizeHandler);
997
- }
998
- controller.ensureMounted();
999
- return controller;
1000
- }
1001
-
1002
78
  // src/multiplayer.ts
1003
- function isDevelopment3() {
79
+ function isDevelopment2() {
1004
80
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
1005
81
  return nodeEnv !== "production";
1006
82
  }
1007
- function getBridgeWindow3() {
83
+ function getBridgeWindow2() {
1008
84
  if (typeof window === "undefined") {
1009
85
  return void 0;
1010
86
  }
1011
87
  return window;
1012
88
  }
1013
- function shareRoomCode(roomCode, options) {
1014
- const bridge = getBridgeWindow3();
89
+ function shareRoomCode(roomCode) {
90
+ const bridge = getBridgeWindow2();
1015
91
  if (typeof bridge?.shareRoomCode === "function") {
1016
- bridge.shareRoomCode(roomCode, options);
92
+ bridge.shareRoomCode(roomCode);
1017
93
  return;
1018
94
  }
1019
- if (isDevelopment3()) {
95
+ if (isDevelopment2()) {
1020
96
  console.warn(
1021
97
  "[oasiz/sdk] shareRoomCode bridge is unavailable. This is expected in local development."
1022
98
  );
1023
99
  }
1024
100
  }
1025
- function openInviteModal() {
1026
- const bridge = getBridgeWindow3();
1027
- if (typeof bridge?.openInviteModal === "function") {
1028
- bridge.openInviteModal();
1029
- return;
1030
- }
1031
- if (isDevelopment3()) {
1032
- console.warn(
1033
- "[oasiz/sdk] openInviteModal bridge is unavailable. This is expected in local development."
1034
- );
1035
- }
1036
- }
1037
101
  function getGameId() {
1038
- const bridge = getBridgeWindow3();
102
+ const bridge = getBridgeWindow2();
1039
103
  return bridge?.__GAME_ID__;
1040
104
  }
1041
105
  function getRoomCode() {
1042
- const bridge = getBridgeWindow3();
106
+ const bridge = getBridgeWindow2();
1043
107
  return bridge?.__ROOM_CODE__;
1044
108
  }
1045
- function getPlayerId() {
1046
- const bridge = getBridgeWindow3();
1047
- return bridge?.__PLAYER_ID__;
1048
- }
1049
109
  function getPlayerName() {
1050
- const bridge = getBridgeWindow3();
110
+ const bridge = getBridgeWindow2();
1051
111
  return bridge?.__PLAYER_NAME__;
1052
112
  }
1053
113
  function getPlayerAvatar() {
1054
- const bridge = getBridgeWindow3();
114
+ const bridge = getBridgeWindow2();
1055
115
  return bridge?.__PLAYER_AVATAR__;
1056
116
  }
1057
117
 
1058
118
  // src/score.ts
1059
- function isDevelopment4() {
119
+ function isDevelopment3() {
1060
120
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
1061
121
  return nodeEnv !== "production";
1062
122
  }
1063
- function warnMissingBridge2(methodName) {
1064
- if (isDevelopment4()) {
123
+ function warnMissingBridge(methodName) {
124
+ if (isDevelopment3()) {
1065
125
  console.warn(
1066
126
  "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
1067
127
  );
1068
128
  }
1069
129
  }
1070
- function getBridgeWindow4() {
130
+ function getBridgeWindow3() {
1071
131
  if (typeof window === "undefined") {
1072
132
  return void 0;
1073
133
  }
@@ -1075,92 +135,41 @@ function getBridgeWindow4() {
1075
135
  }
1076
136
  function submitScore(score) {
1077
137
  if (!Number.isFinite(score)) {
1078
- if (isDevelopment4()) {
138
+ if (isDevelopment3()) {
1079
139
  console.warn("[oasiz/sdk] submitScore expected a finite number:", score);
1080
140
  }
1081
141
  return;
1082
142
  }
1083
- const bridge = getBridgeWindow4();
143
+ const bridge = getBridgeWindow3();
1084
144
  const normalizedScore = Math.max(0, Math.floor(score));
1085
145
  if (typeof bridge?.submitScore === "function") {
1086
146
  bridge.submitScore(normalizedScore);
1087
147
  return;
1088
148
  }
1089
- warnMissingBridge2("submitScore");
1090
- }
1091
-
1092
- // src/score-edit.ts
1093
- function isDevelopment5() {
1094
- const nodeEnv = globalThis.process?.env?.NODE_ENV;
1095
- return nodeEnv !== "production";
1096
- }
1097
- function getBridgeWindow5() {
1098
- if (typeof window === "undefined") {
1099
- return void 0;
1100
- }
1101
- return window;
1102
- }
1103
- function warnMissingBridge3(methodName) {
1104
- if (isDevelopment5()) {
1105
- console.warn(
1106
- "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
1107
- );
1108
- }
1109
- }
1110
- async function editScore(payload, methodName) {
1111
- const bridge = getBridgeWindow5();
1112
- if (typeof bridge?.__oasizEditScore !== "function") {
1113
- warnMissingBridge3(methodName);
1114
- return null;
1115
- }
1116
- try {
1117
- const result = await bridge.__oasizEditScore(payload);
1118
- return result ?? null;
1119
- } catch (error) {
1120
- if (isDevelopment5()) {
1121
- console.error("[oasiz/sdk] " + methodName + " failed:", error);
1122
- }
1123
- return null;
1124
- }
149
+ warnMissingBridge("submitScore");
1125
150
  }
1126
- async function addScore(delta) {
1127
- if (!Number.isInteger(delta)) {
1128
- if (isDevelopment5()) {
1129
- console.warn("[oasiz/sdk] addScore expected an integer:", delta);
1130
- }
1131
- return null;
1132
- }
1133
- if (delta === 0) {
1134
- return null;
1135
- }
1136
- return editScore({ delta }, "addScore");
1137
- }
1138
- async function setScore(score) {
1139
- if (!Number.isInteger(score) || score < 0) {
1140
- if (isDevelopment5()) {
1141
- console.warn(
1142
- "[oasiz/sdk] setScore expected a non-negative integer:",
1143
- score
1144
- );
1145
- }
1146
- return null;
151
+ function emitScoreConfig(config) {
152
+ const bridge = getBridgeWindow3();
153
+ if (typeof bridge?.emitScoreConfig === "function") {
154
+ bridge.emitScoreConfig(config);
155
+ return;
1147
156
  }
1148
- return editScore({ score }, "setScore");
157
+ warnMissingBridge("emitScoreConfig");
1149
158
  }
1150
159
 
1151
160
  // src/share.ts
1152
- function isDevelopment6() {
161
+ function isDevelopment4() {
1153
162
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
1154
163
  return nodeEnv !== "production";
1155
164
  }
1156
- function getBridgeWindow6() {
165
+ function getBridgeWindow4() {
1157
166
  if (typeof window === "undefined") {
1158
167
  return void 0;
1159
168
  }
1160
169
  return window;
1161
170
  }
1162
- function warnMissingBridge4(methodName) {
1163
- if (isDevelopment6()) {
171
+ function warnMissingBridge2(methodName) {
172
+ if (isDevelopment4()) {
1164
173
  console.warn(
1165
174
  "[oasiz/sdk] " + methodName + " share bridge is unavailable. This is expected in local development."
1166
175
  );
@@ -1203,20 +212,20 @@ function validateRequest(options) {
1203
212
  }
1204
213
  async function share(options) {
1205
214
  const request = validateRequest(options);
1206
- const bridge = getBridgeWindow6();
215
+ const bridge = getBridgeWindow4();
1207
216
  if (typeof bridge?.__oasizShareRequest !== "function") {
1208
- warnMissingBridge4("__oasizShareRequest");
217
+ warnMissingBridge2("__oasizShareRequest");
1209
218
  throw new Error("Share bridge unavailable");
1210
219
  }
1211
220
  await bridge.__oasizShareRequest(request);
1212
221
  }
1213
222
 
1214
223
  // src/state.ts
1215
- function isDevelopment7() {
224
+ function isDevelopment5() {
1216
225
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
1217
226
  return nodeEnv !== "production";
1218
227
  }
1219
- function getBridgeWindow7() {
228
+ function getBridgeWindow5() {
1220
229
  if (typeof window === "undefined") {
1221
230
  return void 0;
1222
231
  }
@@ -1229,22 +238,22 @@ function isPlainObject(value) {
1229
238
  const proto = Object.getPrototypeOf(value);
1230
239
  return proto === Object.prototype || proto === null;
1231
240
  }
1232
- function warnMissingBridge5(methodName) {
1233
- if (isDevelopment7()) {
241
+ function warnMissingBridge3(methodName) {
242
+ if (isDevelopment5()) {
1234
243
  console.warn(
1235
244
  "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
1236
245
  );
1237
246
  }
1238
247
  }
1239
248
  function loadGameState() {
1240
- const bridge = getBridgeWindow7();
249
+ const bridge = getBridgeWindow5();
1241
250
  if (typeof bridge?.loadGameState !== "function") {
1242
- warnMissingBridge5("loadGameState");
251
+ warnMissingBridge3("loadGameState");
1243
252
  return {};
1244
253
  }
1245
254
  const state = bridge.loadGameState();
1246
255
  if (!isPlainObject(state)) {
1247
- if (isDevelopment7()) {
256
+ if (isDevelopment5()) {
1248
257
  console.warn(
1249
258
  "[oasiz/sdk] loadGameState returned invalid data. Falling back to empty object."
1250
259
  );
@@ -1255,35 +264,35 @@ function loadGameState() {
1255
264
  }
1256
265
  function saveGameState(state) {
1257
266
  if (!isPlainObject(state)) {
1258
- if (isDevelopment7()) {
267
+ if (isDevelopment5()) {
1259
268
  console.warn("[oasiz/sdk] saveGameState expected a plain object:", state);
1260
269
  }
1261
270
  return;
1262
271
  }
1263
- const bridge = getBridgeWindow7();
272
+ const bridge = getBridgeWindow5();
1264
273
  if (typeof bridge?.saveGameState === "function") {
1265
274
  bridge.saveGameState(state);
1266
275
  return;
1267
276
  }
1268
- warnMissingBridge5("saveGameState");
277
+ warnMissingBridge3("saveGameState");
1269
278
  }
1270
279
  function flushGameState() {
1271
- const bridge = getBridgeWindow7();
280
+ const bridge = getBridgeWindow5();
1272
281
  if (typeof bridge?.flushGameState === "function") {
1273
282
  bridge.flushGameState();
1274
283
  return;
1275
284
  }
1276
- warnMissingBridge5("flushGameState");
285
+ warnMissingBridge3("flushGameState");
1277
286
  }
1278
287
 
1279
288
  // src/lifecycle.ts
1280
- function isDevelopment8() {
289
+ function isDevelopment6() {
1281
290
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
1282
291
  return nodeEnv !== "production";
1283
292
  }
1284
293
  function addLifecycleListener(eventName, callback) {
1285
294
  if (typeof window === "undefined") {
1286
- if (isDevelopment8()) {
295
+ if (isDevelopment6()) {
1287
296
  console.warn(
1288
297
  "[oasiz/sdk] " + eventName + " listener registered without a browser window. This is expected in local development."
1289
298
  );
@@ -1302,345 +311,20 @@ function onResume(callback) {
1302
311
  return addLifecycleListener("oasiz:resume", callback);
1303
312
  }
1304
313
 
1305
- // src/layout.ts
1306
- var INSET_SIDES = ["top", "right", "bottom", "left"];
1307
- var SIDE_TO_AXIS = {
1308
- top: "vertical",
1309
- right: "horizontal",
1310
- bottom: "vertical",
1311
- left: "horizontal"
1312
- };
1313
- function createInsetEdges(value) {
1314
- return {
1315
- top: value,
1316
- right: value,
1317
- bottom: value,
1318
- left: value
1319
- };
1320
- }
1321
- function isDevelopment9() {
1322
- const nodeEnv = globalThis.process?.env?.NODE_ENV;
1323
- return nodeEnv !== "production";
1324
- }
1325
- function getBridgeWindow8() {
1326
- if (typeof window === "undefined") {
1327
- return void 0;
1328
- }
1329
- return window;
1330
- }
1331
- function warnMissingBridge6(methodName) {
1332
- if (isDevelopment9()) {
1333
- console.warn(
1334
- "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
1335
- );
1336
- }
1337
- }
1338
- function isRecord(value) {
1339
- return typeof value === "object" && value !== null;
1340
- }
1341
- function toFiniteNumber(value) {
1342
- if (typeof value !== "number" || !Number.isFinite(value)) {
1343
- if (typeof value !== "string") {
1344
- return void 0;
1345
- }
1346
- const parsed = Number.parseFloat(value.trim());
1347
- if (!Number.isFinite(parsed)) {
1348
- return void 0;
1349
- }
1350
- return parsed;
1351
- }
1352
- return value;
1353
- }
1354
- function clampInsetPixels(value) {
1355
- const numeric = toFiniteNumber(value);
1356
- if (typeof numeric === "undefined") {
1357
- return void 0;
1358
- }
1359
- return Math.max(0, numeric);
1360
- }
1361
- function normalizeInsetPercent(value) {
1362
- const numeric = toFiniteNumber(value);
1363
- if (typeof numeric === "undefined") {
1364
- return void 0;
1365
- }
1366
- return Math.min(100, Math.max(0, numeric));
1367
- }
1368
- function getViewportSize2(bridge, axis) {
1369
- const visualViewportSize = axis === "vertical" ? bridge.visualViewport?.height : bridge.visualViewport?.width;
1370
- if (typeof visualViewportSize === "number" && Number.isFinite(visualViewportSize) && visualViewportSize > 0) {
1371
- return visualViewportSize;
1372
- }
1373
- const innerSize = axis === "vertical" ? bridge.innerHeight : bridge.innerWidth;
1374
- if (typeof innerSize === "number" && Number.isFinite(innerSize) && innerSize > 0) {
1375
- return innerSize;
1376
- }
1377
- const documentSize = axis === "vertical" ? bridge.document?.documentElement?.clientHeight : bridge.document?.documentElement?.clientWidth;
1378
- if (typeof documentSize === "number" && Number.isFinite(documentSize) && documentSize > 0) {
1379
- return documentSize;
1380
- }
1381
- const bodySize = axis === "vertical" ? bridge.document?.body?.clientHeight : bridge.document?.body?.clientWidth;
1382
- if (typeof bodySize === "number" && Number.isFinite(bodySize) && bodySize > 0) {
1383
- return bodySize;
1384
- }
1385
- return 0;
1386
- }
1387
- function readCssSafeAreaValue(bridge, cssValue) {
1388
- const doc = bridge.document;
1389
- const root = doc?.body ?? doc?.documentElement;
1390
- if (!doc || !root || typeof doc.createElement !== "function" || typeof root.appendChild !== "function" || typeof bridge.getComputedStyle !== "function") {
1391
- return 0;
1392
- }
1393
- const probe = doc.createElement("div");
1394
- probe.style.position = "fixed";
1395
- probe.style.top = "0";
1396
- probe.style.left = "0";
1397
- probe.style.width = "0";
1398
- probe.style.height = "0";
1399
- probe.style.visibility = "hidden";
1400
- probe.style.pointerEvents = "none";
1401
- probe.style.paddingTop = cssValue;
1402
- root.appendChild(probe);
1403
- try {
1404
- return clampInsetPixels(bridge.getComputedStyle(probe).paddingTop) ?? 0;
1405
- } finally {
1406
- if (typeof probe.remove === "function") {
1407
- probe.remove();
1408
- } else {
1409
- probe.parentNode?.removeChild(probe);
1410
- }
1411
- }
1412
- }
1413
- function readCssSafeAreaPixels(bridge, side) {
1414
- const envPixels = readCssSafeAreaValue(
1415
- bridge,
1416
- "env(safe-area-inset-" + side + ")"
1417
- );
1418
- if (envPixels > 0) {
1419
- return envPixels;
1420
- }
1421
- return readCssSafeAreaValue(
1422
- bridge,
1423
- "constant(safe-area-inset-" + side + ")"
1424
- );
1425
- }
1426
- function getDevicePixelRatio(bridge) {
1427
- const dpr = bridge.devicePixelRatio;
1428
- if (typeof dpr !== "number" || !Number.isFinite(dpr) || dpr <= 0) {
1429
- return 1;
1430
- }
1431
- return dpr;
1432
- }
1433
- function roughlyEqualPixels(a, b) {
1434
- return Math.abs(a - b) <= 2;
1435
- }
1436
- function normalizeInsetPixels(value, bridge, side) {
1437
- const pixels = clampInsetPixels(value);
1438
- if (typeof pixels === "undefined") {
1439
- return void 0;
1440
- }
1441
- const cssEnvPixels = readCssSafeAreaPixels(bridge, side);
1442
- if (pixels <= 0) {
1443
- return cssEnvPixels;
1444
- }
1445
- const dpr = getDevicePixelRatio(bridge);
1446
- if (cssEnvPixels > 0 && dpr > 1 && roughlyEqualPixels(pixels / dpr, cssEnvPixels)) {
1447
- return cssEnvPixels;
1448
- }
1449
- return pixels;
1450
- }
1451
- function pixelsToPercentOfViewport(pixels, bridge, side) {
1452
- const size = getViewportSize2(bridge, SIDE_TO_AXIS[side]);
1453
- if (size <= 0) {
1454
- return 0;
1455
- }
1456
- return normalizeInsetPercent(pixels / size * 100) ?? 0;
1457
- }
1458
- function percentToPixelsOfViewport(percent, bridge, side) {
1459
- const size = getViewportSize2(bridge, SIDE_TO_AXIS[side]);
1460
- if (size <= 0) {
1461
- return 0;
1462
- }
1463
- return percent / 100 * size;
1464
- }
1465
- function cssSafeAreaPercent(bridge, side) {
1466
- return pixelsToPercentOfViewport(readCssSafeAreaPixels(bridge, side), bridge, side);
1467
- }
1468
- function resolvePercentValue(value, bridge, side) {
1469
- const percent = normalizeInsetPercent(value);
1470
- if (typeof percent === "undefined") {
1471
- return void 0;
1472
- }
1473
- return percent > 0 ? percent : cssSafeAreaPercent(bridge, side);
1474
- }
1475
- function resolvePixelValue(value, bridge, side) {
1476
- const numeric = toFiniteNumber(value);
1477
- if (typeof numeric === "undefined") {
1478
- return void 0;
1479
- }
1480
- return normalizeInsetPixels(numeric, bridge, side);
1481
- }
1482
- function sideSuffix(side) {
1483
- switch (side) {
1484
- case "top":
1485
- return "Top";
1486
- case "right":
1487
- return "Right";
1488
- case "bottom":
1489
- return "Bottom";
1490
- case "left":
1491
- return "Left";
1492
- }
1493
- }
1494
- function callBridgeFunction(bridge, name) {
1495
- const fn = bridge[name];
1496
- if (typeof fn !== "function") {
1497
- return void 0;
1498
- }
1499
- try {
1500
- return fn.call(bridge);
1501
- } catch (error) {
1502
- console.error("[oasiz/sdk] " + String(name) + " failed:", error);
1503
- return void 0;
1504
- }
1505
- }
1506
- function readInsetObjectValue(value, side, group) {
1507
- if (!isRecord(value)) {
1508
- return void 0;
1509
- }
1510
- if (group) {
1511
- return isRecord(value[group]) ? value[group][side] : void 0;
1512
- }
1513
- return value[side];
1514
- }
1515
- function firstDefined(...values) {
1516
- return values.find((value) => typeof value !== "undefined");
1517
- }
1518
- function readHostInsetSources(bridge) {
1519
- return {
1520
- globalPixels: bridge.__OASIZ_VIEWPORT_INSETS__,
1521
- globalPercent: bridge.__OASIZ_VIEWPORT_INSETS_PERCENT__,
1522
- methodPixels: callBridgeFunction(bridge, "getViewportInsets"),
1523
- methodPercent: callBridgeFunction(bridge, "getViewportInsetsPercent")
1524
- };
1525
- }
1526
- function readIndividualPercentValue(bridge, side) {
1527
- const suffix = sideSuffix(side);
1528
- return firstDefined(
1529
- callBridgeFunction(
1530
- bridge,
1531
- "getSafeArea" + suffix + "Percent"
1532
- ),
1533
- bridge["__OASIZ_SAFE_AREA_" + side.toUpperCase() + "_PERCENT__"]
1534
- );
1535
- }
1536
- function readIndividualPixelValue(bridge, side) {
1537
- const suffix = sideSuffix(side);
1538
- return firstDefined(
1539
- callBridgeFunction(
1540
- bridge,
1541
- "getSafeArea" + suffix
1542
- ),
1543
- bridge["__OASIZ_SAFE_AREA_" + side.toUpperCase() + "__"]
1544
- );
1545
- }
1546
- function resolveInsetSide(bridge, sources, side) {
1547
- const percentCandidate = firstDefined(
1548
- readInsetObjectValue(sources.methodPercent, side, "percent"),
1549
- readInsetObjectValue(sources.methodPercent, side),
1550
- readInsetObjectValue(sources.globalPercent, side, "percent"),
1551
- readInsetObjectValue(sources.globalPercent, side),
1552
- readInsetObjectValue(sources.methodPixels, side, "percent"),
1553
- readInsetObjectValue(sources.globalPixels, side, "percent"),
1554
- readIndividualPercentValue(bridge, side)
1555
- );
1556
- const percent = resolvePercentValue(percentCandidate, bridge, side);
1557
- if (typeof percent !== "undefined") {
1558
- return {
1559
- pixels: percentToPixelsOfViewport(percent, bridge, side),
1560
- percent
1561
- };
1562
- }
1563
- const pixelCandidate = firstDefined(
1564
- readInsetObjectValue(sources.methodPixels, side, "pixels"),
1565
- readInsetObjectValue(sources.methodPixels, side),
1566
- readInsetObjectValue(sources.globalPixels, side, "pixels"),
1567
- readInsetObjectValue(sources.globalPixels, side),
1568
- readIndividualPixelValue(bridge, side)
1569
- );
1570
- const pixels = resolvePixelValue(pixelCandidate, bridge, side);
1571
- if (typeof pixels !== "undefined") {
1572
- return {
1573
- pixels,
1574
- percent: pixelsToPercentOfViewport(pixels, bridge, side)
1575
- };
1576
- }
1577
- const cssPixels = readCssSafeAreaPixels(bridge, side);
1578
- return {
1579
- pixels: cssPixels,
1580
- percent: pixelsToPercentOfViewport(cssPixels, bridge, side)
1581
- };
1582
- }
1583
- function getViewportInsets() {
1584
- const bridge = getBridgeWindow8();
1585
- if (!bridge) {
1586
- return {
1587
- pixels: createInsetEdges(0),
1588
- percent: createInsetEdges(0)
1589
- };
1590
- }
1591
- const sources = readHostInsetSources(bridge);
1592
- const pixels = createInsetEdges(0);
1593
- const percent = createInsetEdges(0);
1594
- for (const side of INSET_SIDES) {
1595
- const resolved = resolveInsetSide(bridge, sources, side);
1596
- pixels[side] = resolved.pixels;
1597
- percent[side] = resolved.percent;
1598
- }
1599
- return { pixels, percent };
1600
- }
1601
- function getSafeAreaTop() {
1602
- const bridge = getBridgeWindow8();
1603
- if (!bridge) {
1604
- return 0;
1605
- }
1606
- const top = getViewportInsets().percent.top;
1607
- if (top <= 0) {
1608
- warnMissingBridge6("getSafeAreaTop");
1609
- }
1610
- return top;
1611
- }
1612
- function setLeaderboardVisible(visible) {
1613
- if (typeof visible !== "boolean") {
1614
- if (isDevelopment9()) {
1615
- console.warn(
1616
- "[oasiz/sdk] setLeaderboardVisible expected a boolean:",
1617
- visible
1618
- );
1619
- }
1620
- return;
1621
- }
1622
- const bridge = getBridgeWindow8();
1623
- if (typeof bridge?.__oasizSetLeaderboardVisible === "function") {
1624
- bridge.__oasizSetLeaderboardVisible(visible);
1625
- return;
1626
- }
1627
- warnMissingBridge6("__oasizSetLeaderboardVisible");
1628
- }
1629
-
1630
314
  // src/navigation.ts
1631
315
  var activeBackListeners = 0;
1632
- function isDevelopment10() {
316
+ function isDevelopment7() {
1633
317
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
1634
318
  return nodeEnv !== "production";
1635
319
  }
1636
- function getBridgeWindow9() {
320
+ function getBridgeWindow6() {
1637
321
  if (typeof window === "undefined") {
1638
322
  return void 0;
1639
323
  }
1640
324
  return window;
1641
325
  }
1642
- function warnMissingBridge7(methodName) {
1643
- if (isDevelopment10()) {
326
+ function warnMissingBridge4(methodName) {
327
+ if (isDevelopment7()) {
1644
328
  console.warn(
1645
329
  "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
1646
330
  );
@@ -1656,7 +340,7 @@ function normalizeNavigationError(error) {
1656
340
  }
1657
341
  function addNavigationListener(eventName, callback) {
1658
342
  if (typeof window === "undefined") {
1659
- if (isDevelopment10()) {
343
+ if (isDevelopment7()) {
1660
344
  console.warn(
1661
345
  "[oasiz/sdk] " + eventName + " listener registered without a browser window. This is expected in local development."
1662
346
  );
@@ -1677,24 +361,24 @@ function onBackButton(callback) {
1677
361
  throw normalizeNavigationError(error);
1678
362
  }
1679
363
  });
1680
- const bridge = getBridgeWindow9();
364
+ const bridge = getBridgeWindow6();
1681
365
  activeBackListeners += 1;
1682
366
  if (activeBackListeners === 1) {
1683
367
  if (typeof bridge?.__oasizSetBackOverride === "function") {
1684
368
  bridge.__oasizSetBackOverride(true);
1685
369
  } else {
1686
- warnMissingBridge7("__oasizSetBackOverride");
370
+ warnMissingBridge4("__oasizSetBackOverride");
1687
371
  }
1688
372
  }
1689
373
  return () => {
1690
374
  off();
1691
375
  activeBackListeners = Math.max(0, activeBackListeners - 1);
1692
376
  if (activeBackListeners === 0) {
1693
- const currentBridge = getBridgeWindow9();
377
+ const currentBridge = getBridgeWindow6();
1694
378
  if (typeof currentBridge?.__oasizSetBackOverride === "function") {
1695
379
  currentBridge.__oasizSetBackOverride(false);
1696
380
  } else {
1697
- warnMissingBridge7("__oasizSetBackOverride");
381
+ warnMissingBridge4("__oasizSetBackOverride");
1698
382
  }
1699
383
  }
1700
384
  };
@@ -1703,84 +387,177 @@ function onLeaveGame(callback) {
1703
387
  return addNavigationListener("oasiz:leave", callback);
1704
388
  }
1705
389
  function leaveGame() {
1706
- const bridge = getBridgeWindow9();
390
+ const bridge = getBridgeWindow6();
1707
391
  if (typeof bridge?.__oasizLeaveGame === "function") {
1708
392
  bridge.__oasizLeaveGame();
1709
393
  return;
1710
394
  }
1711
- warnMissingBridge7("__oasizLeaveGame");
395
+ warnMissingBridge4("__oasizLeaveGame");
396
+ }
397
+
398
+ // src/store.ts
399
+ function isDevelopment8() {
400
+ const nodeEnv = globalThis.process?.env?.NODE_ENV;
401
+ return nodeEnv !== "production";
402
+ }
403
+ function getBridgeWindow7() {
404
+ if (typeof window === "undefined") {
405
+ return void 0;
406
+ }
407
+ return window;
408
+ }
409
+ function warnMissingBridge5(methodName) {
410
+ if (isDevelopment8()) {
411
+ console.warn(
412
+ "[oasiz/sdk] " + methodName + " store bridge is unavailable. This is expected in local development."
413
+ );
414
+ }
415
+ }
416
+ async function requestStoreBridge(action, payload) {
417
+ const bridge = getBridgeWindow7();
418
+ if (typeof bridge?.__oasizStoreBridgeRequest !== "function") {
419
+ warnMissingBridge5(action);
420
+ throw new Error("Store bridge unavailable");
421
+ }
422
+ return await bridge.__oasizStoreBridgeRequest({
423
+ action,
424
+ payload
425
+ });
426
+ }
427
+ async function getFreshState() {
428
+ return await requestStoreBridge("getStoreState");
429
+ }
430
+ function subscribeToStoreEvent(eventName, callback) {
431
+ if (typeof window === "undefined") {
432
+ return () => {
433
+ };
434
+ }
435
+ const handler = (event) => {
436
+ const customEvent = event;
437
+ callback(customEvent.detail);
438
+ };
439
+ window.addEventListener(eventName, handler);
440
+ return () => {
441
+ window.removeEventListener(eventName, handler);
442
+ };
443
+ }
444
+ async function syncProducts(products, expectedVersion) {
445
+ return await requestStoreBridge("syncProducts", {
446
+ expectedVersion: expectedVersion ?? null,
447
+ products
448
+ });
449
+ }
450
+ async function getProducts() {
451
+ const state = await getFreshState();
452
+ return state.products;
453
+ }
454
+ async function getEntitlements() {
455
+ const state = await getFreshState();
456
+ return state.entitlements;
457
+ }
458
+ async function hasEntitlement(productId) {
459
+ const state = await getFreshState();
460
+ return state.entitlements.find((item) => item.productId === productId)?.owned === true;
461
+ }
462
+ async function getQuantity(productId) {
463
+ const state = await getFreshState();
464
+ return state.entitlements.find((item) => item.productId === productId)?.quantity ?? 0;
465
+ }
466
+ async function purchase(productId, quantity = 1) {
467
+ return await requestStoreBridge("purchaseProduct", {
468
+ productId,
469
+ quantity
470
+ });
471
+ }
472
+ async function consume(productId, quantity = 1) {
473
+ return await requestStoreBridge("consumeProduct", {
474
+ productId,
475
+ quantity
476
+ });
477
+ }
478
+ async function getJemBalance() {
479
+ const state = await getFreshState();
480
+ return state.jemBalance;
481
+ }
482
+ function onEntitlementsChanged(callback) {
483
+ return subscribeToStoreEvent("oasiz:entitlements-changed", (state) => {
484
+ callback(state.entitlements);
485
+ });
486
+ }
487
+ function onJemBalanceChanged(callback) {
488
+ return subscribeToStoreEvent("oasiz:jem-balance-changed", (state) => {
489
+ callback(state.jemBalance);
490
+ });
1712
491
  }
1713
492
 
1714
493
  // src/index.ts
1715
494
  var oasiz = {
1716
495
  submitScore,
1717
- addScore,
1718
- setScore,
1719
- getPlayerCharacter,
496
+ emitScoreConfig,
1720
497
  share,
1721
498
  triggerHaptic,
1722
- enableLogOverlay,
1723
499
  loadGameState,
1724
500
  saveGameState,
1725
501
  flushGameState,
1726
502
  shareRoomCode,
1727
- openInviteModal,
1728
503
  onPause,
1729
504
  onResume,
1730
- getSafeAreaTop,
1731
- getViewportInsets,
1732
- setLeaderboardVisible,
1733
505
  onBackButton,
1734
506
  onLeaveGame,
1735
507
  leaveGame,
508
+ store: {
509
+ syncProducts,
510
+ getProducts,
511
+ getEntitlements,
512
+ hasEntitlement,
513
+ getQuantity,
514
+ purchase,
515
+ consume,
516
+ getJemBalance,
517
+ onEntitlementsChanged,
518
+ onJemBalanceChanged
519
+ },
1736
520
  get gameId() {
1737
521
  return getGameId();
1738
522
  },
1739
523
  get roomCode() {
1740
524
  return getRoomCode();
1741
525
  },
1742
- get playerId() {
1743
- return getPlayerId();
1744
- },
1745
526
  get playerName() {
1746
527
  return getPlayerName();
1747
528
  },
1748
529
  get playerAvatar() {
1749
530
  return getPlayerAvatar();
1750
- },
1751
- get safeAreaTop() {
1752
- return getSafeAreaTop();
1753
- },
1754
- get viewportInsets() {
1755
- return getViewportInsets();
1756
531
  }
1757
532
  };
1758
533
  // Annotate the CommonJS export names for ESM import in node:
1759
534
  0 && (module.exports = {
1760
- addScore,
1761
- enableLogOverlay,
535
+ consume,
536
+ emitScoreConfig,
1762
537
  flushGameState,
538
+ getEntitlements,
1763
539
  getGameId,
540
+ getJemBalance,
1764
541
  getPlayerAvatar,
1765
- getPlayerCharacter,
1766
- getPlayerId,
1767
542
  getPlayerName,
543
+ getProducts,
544
+ getQuantity,
1768
545
  getRoomCode,
1769
- getSafeAreaTop,
1770
- getViewportInsets,
546
+ hasEntitlement,
1771
547
  leaveGame,
1772
548
  loadGameState,
1773
549
  oasiz,
1774
550
  onBackButton,
551
+ onEntitlementsChanged,
552
+ onJemBalanceChanged,
1775
553
  onLeaveGame,
1776
554
  onPause,
1777
555
  onResume,
1778
- openInviteModal,
556
+ purchase,
1779
557
  saveGameState,
1780
- setLeaderboardVisible,
1781
- setScore,
1782
558
  share,
1783
559
  shareRoomCode,
1784
560
  submitScore,
561
+ syncProducts,
1785
562
  triggerHaptic
1786
563
  });