@oasiz/sdk 1.3.0 → 1.4.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.
package/dist/index.cjs CHANGED
@@ -20,24 +20,33 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ consume: () => consume,
23
24
  emitScoreConfig: () => emitScoreConfig,
24
- enableLogOverlay: () => enableLogOverlay,
25
25
  flushGameState: () => flushGameState,
26
+ getEntitlements: () => getEntitlements,
26
27
  getGameId: () => getGameId,
28
+ getJemBalance: () => getJemBalance,
27
29
  getPlayerAvatar: () => getPlayerAvatar,
28
30
  getPlayerName: () => getPlayerName,
31
+ getProducts: () => getProducts,
32
+ getQuantity: () => getQuantity,
29
33
  getRoomCode: () => getRoomCode,
34
+ hasEntitlement: () => hasEntitlement,
30
35
  leaveGame: () => leaveGame,
31
36
  loadGameState: () => loadGameState,
32
37
  oasiz: () => oasiz,
33
38
  onBackButton: () => onBackButton,
39
+ onEntitlementsChanged: () => onEntitlementsChanged,
40
+ onJemBalanceChanged: () => onJemBalanceChanged,
34
41
  onLeaveGame: () => onLeaveGame,
35
42
  onPause: () => onPause,
36
43
  onResume: () => onResume,
37
- openInviteModal: () => openInviteModal,
44
+ purchase: () => purchase,
38
45
  saveGameState: () => saveGameState,
46
+ share: () => share,
39
47
  shareRoomCode: () => shareRoomCode,
40
48
  submitScore: () => submitScore,
49
+ syncProducts: () => syncProducts,
41
50
  triggerHaptic: () => triggerHaptic
42
51
  });
43
52
  module.exports = __toCommonJS(index_exports);
@@ -66,897 +75,6 @@ function triggerHaptic(type) {
66
75
  }
67
76
  }
68
77
 
69
- // src/log-overlay.ts
70
- var CONSOLE_METHODS = [
71
- "debug",
72
- "log",
73
- "info",
74
- "warn",
75
- "error"
76
- ];
77
- var DEFAULT_MAX_ENTRIES = 200;
78
- var DEFAULT_TITLE = "SDK Logs";
79
- var OVERLAY_MARGIN = 12;
80
- var DEFAULT_COLLAPSED_WIDTH = 156;
81
- var DEFAULT_COLLAPSED_HEIGHT = 52;
82
- var DEFAULT_EXPANDED_WIDTH = 565;
83
- var DEFAULT_EXPANDED_HEIGHT = 372;
84
- var DRAG_THRESHOLD_PX = 6;
85
- var MIN_EXPANDED_WIDTH = 160;
86
- var MIN_EXPANDED_HEIGHT = 110;
87
- var RESIZE_HOTSPOT_PX = 28;
88
- var TOP_DRAG_ZONE_PX = 44;
89
- var NOOP_HANDLE = {
90
- clear() {
91
- },
92
- destroy() {
93
- },
94
- hide() {
95
- },
96
- isVisible() {
97
- return false;
98
- },
99
- show() {
100
- }
101
- };
102
- function getBrowserWindow() {
103
- if (typeof window === "undefined") {
104
- return void 0;
105
- }
106
- return window;
107
- }
108
- function getDocument() {
109
- if (typeof document === "undefined") {
110
- return void 0;
111
- }
112
- return document;
113
- }
114
- function clampMaxEntries(value) {
115
- if (!Number.isFinite(value)) {
116
- return DEFAULT_MAX_ENTRIES;
117
- }
118
- return Math.max(10, Math.floor(value));
119
- }
120
- function createConsoleSnapshot() {
121
- const fallback = console.log.bind(console);
122
- return {
123
- debug: typeof console.debug === "function" ? console.debug.bind(console) : fallback,
124
- log: fallback,
125
- info: typeof console.info === "function" ? console.info.bind(console) : fallback,
126
- warn: typeof console.warn === "function" ? console.warn.bind(console) : fallback,
127
- error: typeof console.error === "function" ? console.error.bind(console) : fallback
128
- };
129
- }
130
- function formatTimestamp(timestamp) {
131
- const date = new Date(timestamp);
132
- const hours = String(date.getHours()).padStart(2, "0");
133
- const minutes = String(date.getMinutes()).padStart(2, "0");
134
- const seconds = String(date.getSeconds()).padStart(2, "0");
135
- const milliseconds = String(date.getMilliseconds()).padStart(3, "0");
136
- return "[" + hours + ":" + minutes + ":" + seconds + "." + milliseconds + "]";
137
- }
138
- function safeStringify(value) {
139
- const seen = /* @__PURE__ */ new WeakSet();
140
- try {
141
- return JSON.stringify(
142
- value,
143
- (_key, candidate) => {
144
- if (typeof candidate === "bigint") {
145
- return candidate.toString() + "n";
146
- }
147
- if (typeof candidate === "object" && candidate !== null) {
148
- if (seen.has(candidate)) {
149
- return "[Circular]";
150
- }
151
- seen.add(candidate);
152
- }
153
- return candidate;
154
- },
155
- 2
156
- ) ?? String(value);
157
- } catch {
158
- return String(value);
159
- }
160
- }
161
- function formatArg(value) {
162
- if (typeof value === "string") {
163
- return value;
164
- }
165
- if (value instanceof Error) {
166
- if (value.stack) {
167
- return value.stack;
168
- }
169
- return value.name + ": " + value.message;
170
- }
171
- if (typeof value === "undefined") {
172
- return "undefined";
173
- }
174
- if (typeof value === "function") {
175
- return "[Function " + (value.name || "anonymous") + "]";
176
- }
177
- return safeStringify(value);
178
- }
179
- function formatEntryMessage(args) {
180
- const message = args.map(formatArg).join(" ");
181
- if (message.length <= 4e3) {
182
- return message;
183
- }
184
- return message.slice(0, 3997) + "...";
185
- }
186
- function createEntry(level, args, id) {
187
- return {
188
- id,
189
- level,
190
- message: formatEntryMessage(args),
191
- timestamp: Date.now()
192
- };
193
- }
194
- function createButton(label) {
195
- const button = document.createElement("button");
196
- button.type = "button";
197
- button.textContent = label;
198
- button.style.cssText = [
199
- "appearance:none",
200
- "border:1px solid rgba(255,255,255,0.18)",
201
- "background:rgba(255,255,255,0.06)",
202
- "color:#f8fafc",
203
- "border-radius:999px",
204
- "padding:6px 10px",
205
- "font:600 12px/1.1 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace",
206
- "cursor:pointer"
207
- ].join(";");
208
- return button;
209
- }
210
- function getLevelAccent(level) {
211
- if (level === "error") {
212
- return {
213
- lineBackground: "rgba(255, 109, 122, 0.08)"
214
- };
215
- }
216
- if (level === "warn") {
217
- return {
218
- lineBackground: "rgba(255, 196, 94, 0.07)"
219
- };
220
- }
221
- if (level === "info") {
222
- return {
223
- lineBackground: "rgba(82, 187, 255, 0.07)"
224
- };
225
- }
226
- if (level === "debug") {
227
- return {
228
- lineBackground: "rgba(166, 137, 255, 0.07)"
229
- };
230
- }
231
- return {
232
- lineBackground: "rgba(117, 235, 191, 0.06)"
233
- };
234
- }
235
- function getViewportSize() {
236
- const browserWindow = getBrowserWindow();
237
- return {
238
- width: Math.max(320, browserWindow?.innerWidth ?? 1280),
239
- height: Math.max(240, browserWindow?.innerHeight ?? 720)
240
- };
241
- }
242
- function clampPanelSize(size) {
243
- const viewport = getViewportSize();
244
- const maxWidth = Math.max(MIN_EXPANDED_WIDTH, viewport.width - OVERLAY_MARGIN * 2);
245
- const maxHeight = Math.max(
246
- MIN_EXPANDED_HEIGHT,
247
- viewport.height - OVERLAY_MARGIN * 2
248
- );
249
- return {
250
- width: Math.min(maxWidth, Math.max(MIN_EXPANDED_WIDTH, size.width)),
251
- height: Math.min(maxHeight, Math.max(MIN_EXPANDED_HEIGHT, size.height))
252
- };
253
- }
254
- function getOverlaySize(state) {
255
- if (state.expanded && state.panelSize) {
256
- return state.panelSize;
257
- }
258
- const rect = state.ui?.root && typeof state.ui.root.getBoundingClientRect === "function" ? state.ui.root.getBoundingClientRect() : null;
259
- if (rect && Number.isFinite(rect.width) && Number.isFinite(rect.height)) {
260
- return {
261
- width: Math.max(1, rect.width),
262
- height: Math.max(1, rect.height)
263
- };
264
- }
265
- return state.expanded ? clampPanelSize({
266
- width: DEFAULT_EXPANDED_WIDTH,
267
- height: DEFAULT_EXPANDED_HEIGHT
268
- }) : { width: DEFAULT_COLLAPSED_WIDTH, height: DEFAULT_COLLAPSED_HEIGHT };
269
- }
270
- function applyPanelSize(state) {
271
- if (!state.ui) {
272
- return;
273
- }
274
- if (!state.expanded) {
275
- state.ui.root.style.width = "auto";
276
- state.ui.panel.style.width = "min(565px, calc(100vw - 24px))";
277
- state.ui.panel.style.height = "auto";
278
- state.ui.entries.style.maxHeight = "min(36vh, 280px)";
279
- return;
280
- }
281
- if (!state.panelSize) {
282
- state.ui.root.style.width = "min(565px, calc(100vw - 24px))";
283
- state.ui.panel.style.width = "min(565px, calc(100vw - 24px))";
284
- state.ui.panel.style.height = "auto";
285
- state.ui.entries.style.maxHeight = "min(36vh, 280px)";
286
- return;
287
- }
288
- const nextSize = clampPanelSize(
289
- state.panelSize
290
- );
291
- state.panelSize = nextSize;
292
- state.ui.root.style.width = nextSize.width + "px";
293
- state.ui.panel.style.width = "100%";
294
- state.ui.panel.style.height = nextSize.height + "px";
295
- state.ui.entries.style.maxHeight = Math.max(72, nextSize.height - 88) + "px";
296
- }
297
- function clampPosition(point, state) {
298
- const viewport = getViewportSize();
299
- const size = getOverlaySize(state);
300
- const maxX = Math.max(OVERLAY_MARGIN, viewport.width - size.width - OVERLAY_MARGIN);
301
- const maxY = Math.max(
302
- OVERLAY_MARGIN,
303
- viewport.height - size.height - OVERLAY_MARGIN
304
- );
305
- return {
306
- x: Math.min(maxX, Math.max(OVERLAY_MARGIN, point.x)),
307
- y: Math.min(maxY, Math.max(OVERLAY_MARGIN, point.y))
308
- };
309
- }
310
- function applyOverlayPosition(state) {
311
- if (!state.ui) {
312
- return;
313
- }
314
- if (!state.position) {
315
- const viewport = getViewportSize();
316
- const size = getOverlaySize(state);
317
- state.position = clampPosition(
318
- {
319
- x: viewport.width - size.width - OVERLAY_MARGIN,
320
- y: viewport.height - size.height - OVERLAY_MARGIN
321
- },
322
- state
323
- );
324
- } else {
325
- state.position = clampPosition(state.position, state);
326
- }
327
- state.ui.root.style.left = state.position.x + "px";
328
- state.ui.root.style.top = state.position.y + "px";
329
- }
330
- function getPointFromMouseEvent(event) {
331
- return { x: event.clientX, y: event.clientY };
332
- }
333
- function getPointFromTouchEvent(event) {
334
- const touch = event.touches[0] ?? event.changedTouches[0];
335
- if (!touch) {
336
- return null;
337
- }
338
- return { x: touch.clientX, y: touch.clientY };
339
- }
340
- function stopDragging(state) {
341
- if (state.isDragging || state.isResizing) {
342
- state.suppressToggleClickUntil = Date.now() + 180;
343
- }
344
- state.isDragging = false;
345
- state.dragStartPoint = null;
346
- state.lastDragPoint = null;
347
- state.removeDragListeners?.();
348
- state.removeDragListeners = null;
349
- if (state.ui) {
350
- state.ui.panel.style.cursor = "default";
351
- state.ui.dragZone.style.cursor = "grab";
352
- state.ui.toggleButton.style.cursor = "grab";
353
- }
354
- }
355
- function stopResizing(state) {
356
- if (state.isResizing) {
357
- state.suppressToggleClickUntil = Date.now() + 180;
358
- }
359
- state.isResizing = false;
360
- state.resizeStartPoint = null;
361
- state.resizeStartSize = null;
362
- state.removeResizeListeners?.();
363
- state.removeResizeListeners = null;
364
- }
365
- function beginDragTracking(state, startPoint) {
366
- const doc = getDocument();
367
- if (!doc) {
368
- return;
369
- }
370
- stopResizing(state);
371
- stopDragging(state);
372
- state.dragMoved = false;
373
- state.dragStartPoint = startPoint;
374
- state.lastDragPoint = startPoint;
375
- const handlePointerMove = (nextPoint) => {
376
- if (!state.dragStartPoint || !state.lastDragPoint || !nextPoint) {
377
- return;
378
- }
379
- if (!state.isDragging) {
380
- const deltaFromStartX = nextPoint.x - state.dragStartPoint.x;
381
- const deltaFromStartY = nextPoint.y - state.dragStartPoint.y;
382
- const distance = Math.sqrt(
383
- deltaFromStartX * deltaFromStartX + deltaFromStartY * deltaFromStartY
384
- );
385
- if (distance < DRAG_THRESHOLD_PX) {
386
- return;
387
- }
388
- state.isDragging = true;
389
- state.dragMoved = true;
390
- if (state.ui) {
391
- state.ui.panel.style.cursor = "grabbing";
392
- state.ui.dragZone.style.cursor = "grabbing";
393
- state.ui.toggleButton.style.cursor = "grabbing";
394
- }
395
- }
396
- const currentPosition = state.position ?? { x: OVERLAY_MARGIN, y: OVERLAY_MARGIN };
397
- state.position = clampPosition(
398
- {
399
- x: currentPosition.x + (nextPoint.x - state.lastDragPoint.x),
400
- y: currentPosition.y + (nextPoint.y - state.lastDragPoint.y)
401
- },
402
- state
403
- );
404
- state.lastDragPoint = nextPoint;
405
- applyOverlayPosition(state);
406
- };
407
- const handleMouseMove = (event) => {
408
- handlePointerMove(getPointFromMouseEvent(event));
409
- };
410
- const handleTouchMove = (event) => {
411
- handlePointerMove(getPointFromTouchEvent(event));
412
- };
413
- const handleMouseUp = () => {
414
- stopDragging(state);
415
- };
416
- const handleTouchEnd = () => {
417
- stopDragging(state);
418
- };
419
- doc.addEventListener("mousemove", handleMouseMove);
420
- doc.addEventListener("mouseup", handleMouseUp);
421
- doc.addEventListener("touchmove", handleTouchMove, { passive: true });
422
- doc.addEventListener("touchend", handleTouchEnd);
423
- doc.addEventListener("touchcancel", handleTouchEnd);
424
- state.removeDragListeners = () => {
425
- doc.removeEventListener("mousemove", handleMouseMove);
426
- doc.removeEventListener("mouseup", handleMouseUp);
427
- doc.removeEventListener("touchmove", handleTouchMove);
428
- doc.removeEventListener("touchend", handleTouchEnd);
429
- doc.removeEventListener("touchcancel", handleTouchEnd);
430
- };
431
- }
432
- function startResizing(state, startPoint) {
433
- const doc = getDocument();
434
- if (!doc) {
435
- return;
436
- }
437
- stopDragging(state);
438
- stopResizing(state);
439
- state.isResizing = true;
440
- state.resizeStartPoint = startPoint;
441
- state.resizeStartSize = state.panelSize ?? clampPanelSize({ width: DEFAULT_EXPANDED_WIDTH, height: DEFAULT_EXPANDED_HEIGHT });
442
- const handleResizeMove = (nextPoint) => {
443
- if (!state.isResizing || !state.resizeStartPoint || !state.resizeStartSize || !nextPoint) {
444
- return;
445
- }
446
- state.panelSize = clampPanelSize({
447
- width: state.resizeStartSize.width + (nextPoint.x - state.resizeStartPoint.x),
448
- height: state.resizeStartSize.height + (nextPoint.y - state.resizeStartPoint.y)
449
- });
450
- applyPanelSize(state);
451
- applyOverlayPosition(state);
452
- };
453
- const handleMouseMove = (event) => {
454
- handleResizeMove(getPointFromMouseEvent(event));
455
- };
456
- const handleTouchMove = (event) => {
457
- handleResizeMove(getPointFromTouchEvent(event));
458
- };
459
- const handleFinish = () => {
460
- stopResizing(state);
461
- };
462
- doc.addEventListener("mousemove", handleMouseMove);
463
- doc.addEventListener("mouseup", handleFinish);
464
- doc.addEventListener("touchmove", handleTouchMove, { passive: true });
465
- doc.addEventListener("touchend", handleFinish);
466
- doc.addEventListener("touchcancel", handleFinish);
467
- state.removeResizeListeners = () => {
468
- doc.removeEventListener("mousemove", handleMouseMove);
469
- doc.removeEventListener("mouseup", handleFinish);
470
- doc.removeEventListener("touchmove", handleTouchMove);
471
- doc.removeEventListener("touchend", handleFinish);
472
- doc.removeEventListener("touchcancel", handleFinish);
473
- };
474
- }
475
- function isInBottomRightResizeZone(element, point) {
476
- const rect = element.getBoundingClientRect();
477
- return point.x >= rect.right - RESIZE_HOTSPOT_PX && point.x <= rect.right && point.y >= rect.bottom - RESIZE_HOTSPOT_PX && point.y <= rect.bottom;
478
- }
479
- function canStartDragFromTarget(target) {
480
- if (!(target instanceof Element)) {
481
- return true;
482
- }
483
- if (target.closest("button") || target.closest("a") || target.closest("input") || target.closest("textarea") || target.closest("select")) {
484
- return false;
485
- }
486
- return true;
487
- }
488
- function isInTopDragZone(element, point, zoneHeight) {
489
- const rect = element.getBoundingClientRect();
490
- return point.x >= rect.left && point.x <= rect.right && point.y >= rect.top && point.y <= rect.top + zoneHeight;
491
- }
492
- function attachDragStartListeners(element, state, options = {}) {
493
- element.addEventListener("mousedown", (event) => {
494
- if (!options.allowInteractiveTarget && !canStartDragFromTarget(event.target)) {
495
- return;
496
- }
497
- const point = getPointFromMouseEvent(event);
498
- if (typeof options.topZoneHeight === "number" && !isInTopDragZone(element, point, options.topZoneHeight)) {
499
- return;
500
- }
501
- beginDragTracking(state, point);
502
- });
503
- element.addEventListener("touchstart", (event) => {
504
- if (!options.allowInteractiveTarget && !canStartDragFromTarget(event.target)) {
505
- return;
506
- }
507
- const point = getPointFromTouchEvent(event);
508
- if (!point) {
509
- return;
510
- }
511
- if (typeof options.topZoneHeight === "number" && !isInTopDragZone(element, point, options.topZoneHeight)) {
512
- return;
513
- }
514
- beginDragTracking(state, point);
515
- });
516
- }
517
- function attachCollapsedToggleListeners(element, state) {
518
- const startToggleInteraction = (startPoint) => {
519
- const doc = getDocument();
520
- if (!doc) {
521
- return;
522
- }
523
- beginDragTracking(state, startPoint);
524
- const finishInteraction = () => {
525
- releaseListeners();
526
- if (state.dragMoved || Date.now() < state.suppressToggleClickUntil) {
527
- return;
528
- }
529
- state.expanded = true;
530
- state.unreadCount = 0;
531
- renderOverlay(state);
532
- };
533
- const releaseListeners = () => {
534
- doc.removeEventListener("mouseup", finishInteraction);
535
- doc.removeEventListener("touchend", finishInteraction);
536
- doc.removeEventListener("touchcancel", finishInteraction);
537
- };
538
- doc.addEventListener("mouseup", finishInteraction);
539
- doc.addEventListener("touchend", finishInteraction);
540
- doc.addEventListener("touchcancel", finishInteraction);
541
- };
542
- element.addEventListener("mousedown", (event) => {
543
- event.preventDefault();
544
- startToggleInteraction(getPointFromMouseEvent(event));
545
- });
546
- element.addEventListener("touchstart", (event) => {
547
- const point = getPointFromTouchEvent(event);
548
- if (!point) {
549
- return;
550
- }
551
- event.preventDefault();
552
- startToggleInteraction(point);
553
- });
554
- }
555
- function attachPanelResizeListeners(element, state) {
556
- element.addEventListener("mousedown", (event) => {
557
- if (!state.expanded || !canStartDragFromTarget(event.target)) {
558
- return;
559
- }
560
- const point = getPointFromMouseEvent(event);
561
- if (!isInBottomRightResizeZone(element, point)) {
562
- return;
563
- }
564
- event.preventDefault();
565
- event.stopPropagation();
566
- startResizing(state, point);
567
- });
568
- element.addEventListener("touchstart", (event) => {
569
- if (!state.expanded || !canStartDragFromTarget(event.target)) {
570
- return;
571
- }
572
- const point = getPointFromTouchEvent(event);
573
- if (!point || !isInBottomRightResizeZone(element, point)) {
574
- return;
575
- }
576
- event.preventDefault();
577
- event.stopPropagation();
578
- startResizing(state, point);
579
- });
580
- }
581
- function createOverlayUi(state) {
582
- const root = document.createElement("div");
583
- root.style.cssText = [
584
- "position:fixed",
585
- "left:12px",
586
- "top:12px",
587
- "z-index:2147483647",
588
- "display:flex",
589
- "flex-direction:column",
590
- "align-items:stretch",
591
- "gap:8px",
592
- "width:min(565px, calc(100vw - 24px))",
593
- "pointer-events:none"
594
- ].join(";");
595
- const toggleButton = createButton("Logs");
596
- toggleButton.style.pointerEvents = "auto";
597
- toggleButton.style.alignSelf = "flex-end";
598
- toggleButton.style.display = "inline-flex";
599
- toggleButton.style.alignItems = "center";
600
- toggleButton.style.justifyContent = "center";
601
- toggleButton.style.minHeight = "40px";
602
- toggleButton.style.minWidth = "76px";
603
- toggleButton.style.padding = "8px 14px";
604
- toggleButton.style.textAlign = "center";
605
- toggleButton.style.border = "1px solid rgba(122, 212, 255, 0.22)";
606
- toggleButton.style.background = "linear-gradient(180deg, rgba(13,31,54,0.98), rgba(8,19,37,0.98))";
607
- toggleButton.style.boxShadow = "0 18px 40px rgba(4,12,24,0.34)";
608
- toggleButton.style.cursor = "grab";
609
- toggleButton.style.touchAction = "none";
610
- const panel = document.createElement("div");
611
- panel.style.cssText = [
612
- "position:relative",
613
- "display:flex",
614
- "flex-direction:column",
615
- "width:min(565px, calc(100vw - 24px))",
616
- "max-height:min(48vh, 372px)",
617
- "border-radius:18px",
618
- "border:1px solid rgba(116,167,255,0.16)",
619
- "background:linear-gradient(180deg, rgba(9,19,37,0.98), rgba(5,12,24,0.98))",
620
- "box-shadow:0 28px 64px rgba(2,8,18,0.46)",
621
- "backdrop-filter:blur(16px)",
622
- "overflow:hidden",
623
- "cursor:default",
624
- "pointer-events:auto"
625
- ].join(";");
626
- const dragZone = document.createElement("div");
627
- dragZone.style.cssText = [
628
- "position:absolute",
629
- "top:0",
630
- "left:0",
631
- "right:0",
632
- "height:" + String(TOP_DRAG_ZONE_PX) + "px",
633
- "z-index:1",
634
- "cursor:grab",
635
- "background:transparent",
636
- "pointer-events:auto"
637
- ].join(";");
638
- const controls = document.createElement("div");
639
- controls.style.cssText = [
640
- "position:absolute",
641
- "top:22px",
642
- "right:22px",
643
- "z-index:2",
644
- "display:flex",
645
- "align-items:center",
646
- "gap:8px",
647
- "pointer-events:auto"
648
- ].join(";");
649
- const clearButton = createButton("Clear");
650
- clearButton.style.background = "rgba(255,255,255,0.1)";
651
- clearButton.style.border = "1px solid rgba(255,255,255,0.16)";
652
- clearButton.style.color = "#eef6ff";
653
- clearButton.style.minHeight = "30px";
654
- clearButton.style.padding = "4px 9px";
655
- clearButton.style.fontSize = "11px";
656
- clearButton.style.backdropFilter = "blur(8px)";
657
- const collapseButton = createButton("Hide");
658
- collapseButton.style.background = "rgba(113, 171, 255, 0.12)";
659
- collapseButton.style.border = "1px solid rgba(113, 171, 255, 0.2)";
660
- collapseButton.style.color = "#d9ebff";
661
- collapseButton.style.minHeight = "30px";
662
- collapseButton.style.padding = "4px 9px";
663
- collapseButton.style.fontSize = "11px";
664
- collapseButton.style.backdropFilter = "blur(8px)";
665
- const body = document.createElement("div");
666
- body.style.cssText = [
667
- "position:relative",
668
- "display:flex",
669
- "flex-direction:column",
670
- "padding:12px",
671
- "background:linear-gradient(180deg, rgba(4,10,20,0.88), rgba(3,8,18,0.98))",
672
- "flex:1 1 auto",
673
- "min-height:0"
674
- ].join(";");
675
- const entries = document.createElement("div");
676
- entries.style.cssText = [
677
- "display:flex",
678
- "flex-direction:column",
679
- "gap:0",
680
- "overflow:auto",
681
- "flex:1 1 auto",
682
- "min-height:96px",
683
- "max-height:min(36vh, 280px)",
684
- "padding:0",
685
- "border:1px solid rgba(115,153,212,0.14)",
686
- "border-radius:12px",
687
- "background:rgba(4,10,20,0.82)"
688
- ].join(";");
689
- const emptyState = document.createElement("div");
690
- emptyState.style.cssText = [
691
- "display:flex",
692
- "align-items:center",
693
- "justify-content:center",
694
- "flex:1 1 auto",
695
- "min-height:96px",
696
- "color:rgba(204,222,250,0.6)",
697
- "font:500 12px/1.5 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace",
698
- "text-align:center",
699
- "padding:18px"
700
- ].join(";");
701
- emptyState.textContent = "Console output will appear here.";
702
- collapseButton.addEventListener("click", (event) => {
703
- event.stopPropagation();
704
- state.expanded = false;
705
- renderOverlay(state);
706
- });
707
- clearButton.addEventListener("click", (event) => {
708
- event.stopPropagation();
709
- state.entries = [];
710
- state.unreadCount = 0;
711
- renderOverlay(state);
712
- });
713
- controls.appendChild(clearButton);
714
- controls.appendChild(collapseButton);
715
- entries.appendChild(emptyState);
716
- body.appendChild(entries);
717
- body.appendChild(controls);
718
- panel.appendChild(dragZone);
719
- panel.appendChild(body);
720
- attachDragStartListeners(dragZone, state);
721
- attachPanelResizeListeners(panel, state);
722
- attachCollapsedToggleListeners(toggleButton, state);
723
- root.appendChild(panel);
724
- root.appendChild(toggleButton);
725
- return {
726
- body,
727
- clearButton,
728
- collapseButton,
729
- controls,
730
- dragZone,
731
- emptyState,
732
- entries,
733
- panel,
734
- root,
735
- toggleButton
736
- };
737
- }
738
- function renderOverlay(state) {
739
- if (!state.ui) {
740
- return;
741
- }
742
- state.ui.panel.style.display = state.expanded ? "flex" : "none";
743
- state.ui.toggleButton.style.display = state.expanded ? "none" : "inline-flex";
744
- state.ui.toggleButton.textContent = "Logs";
745
- if (state.entries.length === 0) {
746
- state.ui.entries.style.display = "flex";
747
- state.ui.emptyState.style.display = "flex";
748
- state.ui.entries.replaceChildren(state.ui.emptyState);
749
- applyPanelSize(state);
750
- applyOverlayPosition(state);
751
- return;
752
- }
753
- state.ui.entries.style.display = "flex";
754
- state.ui.emptyState.style.display = "none";
755
- const nextChildren = state.entries.map((entry) => {
756
- const accent = getLevelAccent(entry.level);
757
- const row = document.createElement("div");
758
- row.style.cssText = [
759
- "display:flex",
760
- "align-items:flex-start",
761
- "gap:0",
762
- "padding:4px 12px",
763
- "background:" + accent.lineBackground
764
- ].join(";");
765
- const line = document.createElement("div");
766
- line.textContent = formatTimestamp(entry.timestamp) + " " + entry.level.toUpperCase() + " " + entry.message;
767
- line.style.cssText = [
768
- "color:#ecf4ff",
769
- "font:500 12px/1.5 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace",
770
- "white-space:pre-wrap",
771
- "word-break:break-word",
772
- "flex:1 1 auto"
773
- ].join(";");
774
- row.appendChild(line);
775
- return row;
776
- });
777
- state.ui.entries.replaceChildren(...nextChildren);
778
- state.ui.entries.scrollTop = state.ui.entries.scrollHeight;
779
- applyPanelSize(state);
780
- applyOverlayPosition(state);
781
- }
782
- function mountOverlay(state) {
783
- const doc = getDocument();
784
- if (!doc?.body || state.ui) {
785
- return;
786
- }
787
- state.ui = createOverlayUi(state);
788
- doc.body.appendChild(state.ui.root);
789
- applyPanelSize(state);
790
- applyOverlayPosition(state);
791
- renderOverlay(state);
792
- }
793
- function enqueueEntry(state, level, args) {
794
- state.entries.push(createEntry(level, args, state.nextEntryId));
795
- state.nextEntryId += 1;
796
- if (state.entries.length > state.maxEntries) {
797
- state.entries.splice(0, state.entries.length - state.maxEntries);
798
- }
799
- if (!state.expanded) {
800
- state.unreadCount += 1;
801
- }
802
- renderOverlay(state);
803
- }
804
- function restoreConsole(snapshot) {
805
- for (const method of CONSOLE_METHODS) {
806
- console[method] = snapshot[method];
807
- }
808
- }
809
- function patchConsole(state) {
810
- for (const method of CONSOLE_METHODS) {
811
- const original = state.originalConsole[method];
812
- console[method] = (...args) => {
813
- enqueueEntry(state, method, args);
814
- original(...args);
815
- };
816
- }
817
- }
818
- function cleanupOverlay(browserWindow, state) {
819
- restoreConsole(state.originalConsole);
820
- stopResizing(state);
821
- stopDragging(state);
822
- if (state.domReadyHandler) {
823
- getDocument()?.removeEventListener("DOMContentLoaded", state.domReadyHandler);
824
- state.domReadyHandler = void 0;
825
- }
826
- if (state.resizeHandler && typeof browserWindow.removeEventListener === "function") {
827
- browserWindow.removeEventListener("resize", state.resizeHandler);
828
- state.resizeHandler = void 0;
829
- }
830
- state.ui?.root.remove();
831
- state.ui = null;
832
- delete browserWindow.__oasizLogOverlayController__;
833
- delete browserWindow.__oasizLogOverlayState__;
834
- }
835
- function createController(browserWindow, state) {
836
- return {
837
- retain() {
838
- state.refCount += 1;
839
- this.ensureMounted();
840
- },
841
- ensureMounted() {
842
- const doc = getDocument();
843
- if (!doc) {
844
- return;
845
- }
846
- if (doc.body) {
847
- mountOverlay(state);
848
- applyOverlayPosition(state);
849
- return;
850
- }
851
- if (!state.domReadyHandler) {
852
- state.domReadyHandler = () => {
853
- mountOverlay(state);
854
- state.domReadyHandler = void 0;
855
- };
856
- doc.addEventListener("DOMContentLoaded", state.domReadyHandler, {
857
- once: true
858
- });
859
- }
860
- },
861
- clear() {
862
- state.entries = [];
863
- state.unreadCount = 0;
864
- renderOverlay(state);
865
- },
866
- show() {
867
- state.expanded = true;
868
- state.unreadCount = 0;
869
- renderOverlay(state);
870
- },
871
- hide() {
872
- state.expanded = false;
873
- renderOverlay(state);
874
- },
875
- isVisible() {
876
- return state.expanded;
877
- },
878
- destroy() {
879
- state.refCount = Math.max(0, state.refCount - 1);
880
- if (state.refCount === 0) {
881
- cleanupOverlay(browserWindow, state);
882
- }
883
- }
884
- };
885
- }
886
- function enableLogOverlay(options = {}) {
887
- if (options.enabled === false) {
888
- return NOOP_HANDLE;
889
- }
890
- const browserWindow = getBrowserWindow();
891
- const doc = getDocument();
892
- if (!browserWindow || !doc) {
893
- return NOOP_HANDLE;
894
- }
895
- const existingController = browserWindow.__oasizLogOverlayController__;
896
- const existingState = browserWindow.__oasizLogOverlayState__;
897
- if (existingController && existingState) {
898
- existingController.retain();
899
- if (typeof options.maxEntries === "number") {
900
- existingState.maxEntries = clampMaxEntries(options.maxEntries);
901
- if (existingState.entries.length > existingState.maxEntries) {
902
- existingState.entries.splice(
903
- 0,
904
- existingState.entries.length - existingState.maxEntries
905
- );
906
- }
907
- }
908
- if (typeof options.collapsed === "boolean") {
909
- existingState.expanded = !options.collapsed;
910
- applyPanelSize(existingState);
911
- if (existingState.expanded) {
912
- existingState.unreadCount = 0;
913
- }
914
- }
915
- if (typeof options.title === "string" && options.title.trim().length > 0) {
916
- existingState.title = options.title.trim();
917
- }
918
- existingController.ensureMounted();
919
- renderOverlay(existingState);
920
- return existingController;
921
- }
922
- const state = {
923
- dragMoved: false,
924
- dragStartPoint: null,
925
- entries: [],
926
- expanded: options.collapsed !== true,
927
- isDragging: false,
928
- isResizing: false,
929
- lastDragPoint: null,
930
- maxEntries: clampMaxEntries(options.maxEntries),
931
- nextEntryId: 1,
932
- originalConsole: createConsoleSnapshot(),
933
- panelSize: null,
934
- position: null,
935
- refCount: 1,
936
- removeDragListeners: null,
937
- removeResizeListeners: null,
938
- resizeStartPoint: null,
939
- resizeStartSize: null,
940
- suppressToggleClickUntil: 0,
941
- title: typeof options.title === "string" && options.title.trim().length > 0 ? options.title.trim() : DEFAULT_TITLE,
942
- resizeHandler: void 0,
943
- ui: null,
944
- unreadCount: 0
945
- };
946
- const controller = createController(browserWindow, state);
947
- browserWindow.__oasizLogOverlayState__ = state;
948
- browserWindow.__oasizLogOverlayController__ = controller;
949
- patchConsole(state);
950
- if (typeof browserWindow.addEventListener === "function") {
951
- state.resizeHandler = () => {
952
- applyOverlayPosition(state);
953
- };
954
- browserWindow.addEventListener("resize", state.resizeHandler);
955
- }
956
- controller.ensureMounted();
957
- return controller;
958
- }
959
-
960
78
  // src/multiplayer.ts
961
79
  function isDevelopment2() {
962
80
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
@@ -968,10 +86,10 @@ function getBridgeWindow2() {
968
86
  }
969
87
  return window;
970
88
  }
971
- function shareRoomCode(roomCode, options) {
89
+ function shareRoomCode(roomCode) {
972
90
  const bridge = getBridgeWindow2();
973
91
  if (typeof bridge?.shareRoomCode === "function") {
974
- bridge.shareRoomCode(roomCode, options);
92
+ bridge.shareRoomCode(roomCode);
975
93
  return;
976
94
  }
977
95
  if (isDevelopment2()) {
@@ -980,18 +98,6 @@ function shareRoomCode(roomCode, options) {
980
98
  );
981
99
  }
982
100
  }
983
- function openInviteModal() {
984
- const bridge = getBridgeWindow2();
985
- if (typeof bridge?.openInviteModal === "function") {
986
- bridge.openInviteModal();
987
- return;
988
- }
989
- if (isDevelopment2()) {
990
- console.warn(
991
- "[oasiz/sdk] openInviteModal bridge is unavailable. This is expected in local development."
992
- );
993
- }
994
- }
995
101
  function getGameId() {
996
102
  const bridge = getBridgeWindow2();
997
103
  return bridge?.__GAME_ID__;
@@ -1051,7 +157,7 @@ function emitScoreConfig(config) {
1051
157
  warnMissingBridge("emitScoreConfig");
1052
158
  }
1053
159
 
1054
- // src/state.ts
160
+ // src/share.ts
1055
161
  function isDevelopment4() {
1056
162
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
1057
163
  return nodeEnv !== "production";
@@ -1062,6 +168,69 @@ function getBridgeWindow4() {
1062
168
  }
1063
169
  return window;
1064
170
  }
171
+ function warnMissingBridge2(methodName) {
172
+ if (isDevelopment4()) {
173
+ console.warn(
174
+ "[oasiz/sdk] " + methodName + " share bridge is unavailable. This is expected in local development."
175
+ );
176
+ }
177
+ }
178
+ function isValidImageReference(image) {
179
+ if (/^data:image\/[a-zA-Z0-9.+-]+;base64,/.test(image)) {
180
+ return true;
181
+ }
182
+ try {
183
+ const parsed = new URL(image);
184
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
185
+ } catch {
186
+ return false;
187
+ }
188
+ }
189
+ function validateRequest(options) {
190
+ const text = typeof options.text === "string" ? options.text.trim() : "";
191
+ const hasText = text.length > 0;
192
+ const hasScore = options.score !== void 0;
193
+ const hasImage = typeof options.image === "string" && options.image.length > 0;
194
+ if (!hasText && !hasScore && !hasImage) {
195
+ throw new Error("Share request requires text, score, or image.");
196
+ }
197
+ if (hasScore) {
198
+ if (typeof options.score !== "number" || !Number.isInteger(options.score) || options.score < 0) {
199
+ throw new Error("Share score must be a non-negative integer.");
200
+ }
201
+ }
202
+ if (hasImage && !isValidImageReference(options.image)) {
203
+ throw new Error(
204
+ "Share image must be an http(s) URL or a data:image/... base64 string."
205
+ );
206
+ }
207
+ return {
208
+ ...hasText ? { text } : {},
209
+ ...hasScore ? { score: options.score } : {},
210
+ ...hasImage ? { image: options.image } : {}
211
+ };
212
+ }
213
+ async function share(options) {
214
+ const request = validateRequest(options);
215
+ const bridge = getBridgeWindow4();
216
+ if (typeof bridge?.__oasizShareRequest !== "function") {
217
+ warnMissingBridge2("__oasizShareRequest");
218
+ throw new Error("Share bridge unavailable");
219
+ }
220
+ await bridge.__oasizShareRequest(request);
221
+ }
222
+
223
+ // src/state.ts
224
+ function isDevelopment5() {
225
+ const nodeEnv = globalThis.process?.env?.NODE_ENV;
226
+ return nodeEnv !== "production";
227
+ }
228
+ function getBridgeWindow5() {
229
+ if (typeof window === "undefined") {
230
+ return void 0;
231
+ }
232
+ return window;
233
+ }
1065
234
  function isPlainObject(value) {
1066
235
  if (!value || typeof value !== "object" || Array.isArray(value)) {
1067
236
  return false;
@@ -1069,22 +238,22 @@ function isPlainObject(value) {
1069
238
  const proto = Object.getPrototypeOf(value);
1070
239
  return proto === Object.prototype || proto === null;
1071
240
  }
1072
- function warnMissingBridge2(methodName) {
1073
- if (isDevelopment4()) {
241
+ function warnMissingBridge3(methodName) {
242
+ if (isDevelopment5()) {
1074
243
  console.warn(
1075
244
  "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
1076
245
  );
1077
246
  }
1078
247
  }
1079
248
  function loadGameState() {
1080
- const bridge = getBridgeWindow4();
249
+ const bridge = getBridgeWindow5();
1081
250
  if (typeof bridge?.loadGameState !== "function") {
1082
- warnMissingBridge2("loadGameState");
251
+ warnMissingBridge3("loadGameState");
1083
252
  return {};
1084
253
  }
1085
254
  const state = bridge.loadGameState();
1086
255
  if (!isPlainObject(state)) {
1087
- if (isDevelopment4()) {
256
+ if (isDevelopment5()) {
1088
257
  console.warn(
1089
258
  "[oasiz/sdk] loadGameState returned invalid data. Falling back to empty object."
1090
259
  );
@@ -1095,35 +264,35 @@ function loadGameState() {
1095
264
  }
1096
265
  function saveGameState(state) {
1097
266
  if (!isPlainObject(state)) {
1098
- if (isDevelopment4()) {
267
+ if (isDevelopment5()) {
1099
268
  console.warn("[oasiz/sdk] saveGameState expected a plain object:", state);
1100
269
  }
1101
270
  return;
1102
271
  }
1103
- const bridge = getBridgeWindow4();
272
+ const bridge = getBridgeWindow5();
1104
273
  if (typeof bridge?.saveGameState === "function") {
1105
274
  bridge.saveGameState(state);
1106
275
  return;
1107
276
  }
1108
- warnMissingBridge2("saveGameState");
277
+ warnMissingBridge3("saveGameState");
1109
278
  }
1110
279
  function flushGameState() {
1111
- const bridge = getBridgeWindow4();
280
+ const bridge = getBridgeWindow5();
1112
281
  if (typeof bridge?.flushGameState === "function") {
1113
282
  bridge.flushGameState();
1114
283
  return;
1115
284
  }
1116
- warnMissingBridge2("flushGameState");
285
+ warnMissingBridge3("flushGameState");
1117
286
  }
1118
287
 
1119
288
  // src/lifecycle.ts
1120
- function isDevelopment5() {
289
+ function isDevelopment6() {
1121
290
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
1122
291
  return nodeEnv !== "production";
1123
292
  }
1124
293
  function addLifecycleListener(eventName, callback) {
1125
294
  if (typeof window === "undefined") {
1126
- if (isDevelopment5()) {
295
+ if (isDevelopment6()) {
1127
296
  console.warn(
1128
297
  "[oasiz/sdk] " + eventName + " listener registered without a browser window. This is expected in local development."
1129
298
  );
@@ -1144,26 +313,34 @@ function onResume(callback) {
1144
313
 
1145
314
  // src/navigation.ts
1146
315
  var activeBackListeners = 0;
1147
- function isDevelopment6() {
316
+ function isDevelopment7() {
1148
317
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
1149
318
  return nodeEnv !== "production";
1150
319
  }
1151
- function getBridgeWindow5() {
320
+ function getBridgeWindow6() {
1152
321
  if (typeof window === "undefined") {
1153
322
  return void 0;
1154
323
  }
1155
324
  return window;
1156
325
  }
1157
- function warnMissingBridge3(methodName) {
1158
- if (isDevelopment6()) {
326
+ function warnMissingBridge4(methodName) {
327
+ if (isDevelopment7()) {
1159
328
  console.warn(
1160
329
  "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
1161
330
  );
1162
331
  }
1163
332
  }
333
+ function normalizeNavigationError(error) {
334
+ if (error instanceof Error) {
335
+ return error;
336
+ }
337
+ return new Error(
338
+ typeof error === "string" ? error : "Back button callback failed."
339
+ );
340
+ }
1164
341
  function addNavigationListener(eventName, callback) {
1165
342
  if (typeof window === "undefined") {
1166
- if (isDevelopment6()) {
343
+ if (isDevelopment7()) {
1167
344
  console.warn(
1168
345
  "[oasiz/sdk] " + eventName + " listener registered without a browser window. This is expected in local development."
1169
346
  );
@@ -1176,25 +353,32 @@ function addNavigationListener(eventName, callback) {
1176
353
  return () => window.removeEventListener(eventName, handler);
1177
354
  }
1178
355
  function onBackButton(callback) {
1179
- const off = addNavigationListener("oasiz:back", callback);
1180
- const bridge = getBridgeWindow5();
356
+ const off = addNavigationListener("oasiz:back", () => {
357
+ try {
358
+ callback();
359
+ } catch (error) {
360
+ leaveGame();
361
+ throw normalizeNavigationError(error);
362
+ }
363
+ });
364
+ const bridge = getBridgeWindow6();
1181
365
  activeBackListeners += 1;
1182
366
  if (activeBackListeners === 1) {
1183
367
  if (typeof bridge?.__oasizSetBackOverride === "function") {
1184
368
  bridge.__oasizSetBackOverride(true);
1185
369
  } else {
1186
- warnMissingBridge3("__oasizSetBackOverride");
370
+ warnMissingBridge4("__oasizSetBackOverride");
1187
371
  }
1188
372
  }
1189
373
  return () => {
1190
374
  off();
1191
375
  activeBackListeners = Math.max(0, activeBackListeners - 1);
1192
376
  if (activeBackListeners === 0) {
1193
- const currentBridge = getBridgeWindow5();
377
+ const currentBridge = getBridgeWindow6();
1194
378
  if (typeof currentBridge?.__oasizSetBackOverride === "function") {
1195
379
  currentBridge.__oasizSetBackOverride(false);
1196
380
  } else {
1197
- warnMissingBridge3("__oasizSetBackOverride");
381
+ warnMissingBridge4("__oasizSetBackOverride");
1198
382
  }
1199
383
  }
1200
384
  };
@@ -1203,30 +387,136 @@ function onLeaveGame(callback) {
1203
387
  return addNavigationListener("oasiz:leave", callback);
1204
388
  }
1205
389
  function leaveGame() {
1206
- const bridge = getBridgeWindow5();
390
+ const bridge = getBridgeWindow6();
1207
391
  if (typeof bridge?.__oasizLeaveGame === "function") {
1208
392
  bridge.__oasizLeaveGame();
1209
393
  return;
1210
394
  }
1211
- warnMissingBridge3("__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
+ });
1212
491
  }
1213
492
 
1214
493
  // src/index.ts
1215
494
  var oasiz = {
1216
495
  submitScore,
1217
496
  emitScoreConfig,
497
+ share,
1218
498
  triggerHaptic,
1219
- enableLogOverlay,
1220
499
  loadGameState,
1221
500
  saveGameState,
1222
501
  flushGameState,
1223
502
  shareRoomCode,
1224
- openInviteModal,
1225
503
  onPause,
1226
504
  onResume,
1227
505
  onBackButton,
1228
506
  onLeaveGame,
1229
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
+ },
1230
520
  get gameId() {
1231
521
  return getGameId();
1232
522
  },
@@ -1242,23 +532,32 @@ var oasiz = {
1242
532
  };
1243
533
  // Annotate the CommonJS export names for ESM import in node:
1244
534
  0 && (module.exports = {
535
+ consume,
1245
536
  emitScoreConfig,
1246
- enableLogOverlay,
1247
537
  flushGameState,
538
+ getEntitlements,
1248
539
  getGameId,
540
+ getJemBalance,
1249
541
  getPlayerAvatar,
1250
542
  getPlayerName,
543
+ getProducts,
544
+ getQuantity,
1251
545
  getRoomCode,
546
+ hasEntitlement,
1252
547
  leaveGame,
1253
548
  loadGameState,
1254
549
  oasiz,
1255
550
  onBackButton,
551
+ onEntitlementsChanged,
552
+ onJemBalanceChanged,
1256
553
  onLeaveGame,
1257
554
  onPause,
1258
555
  onResume,
1259
- openInviteModal,
556
+ purchase,
1260
557
  saveGameState,
558
+ share,
1261
559
  shareRoomCode,
1262
560
  submitScore,
561
+ syncProducts,
1263
562
  triggerHaptic
1264
563
  });