@oasiz/sdk 1.4.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/README.md CHANGED
@@ -219,6 +219,8 @@ Registers a callback for platform back actions. While at least one back listener
219
219
 
220
220
  Use this for pause menus, in-game overlays, or custom back-stack behavior.
221
221
 
222
+ If your callback throws, Oasiz falls back to closing the game and returning the player to Oasiz home before rethrowing the error for debugging/reporting.
223
+
222
224
  ```ts
223
225
  const offBack = oasiz.onBackButton(() => {
224
226
  if (this.isPauseMenuOpen) {
@@ -242,6 +244,17 @@ quitButton.addEventListener("click", () => {
242
244
  });
243
245
  ```
244
246
 
247
+ ### `oasiz.share(options: { text?: string; score?: number; image?: string }): Promise<void>`
248
+
249
+ Ask the host to open the same share flow Oasiz already uses today. Use `text` to customize the share message, `score` to trigger a challenge-style share, and `image` to share an `http(s)` URL or `data:image/...` payload.
250
+
251
+ ```ts
252
+ await oasiz.share({
253
+ text: "I made it to level 9!",
254
+ score: 4200,
255
+ });
256
+ ```
257
+
245
258
  ### `oasiz.onLeaveGame(callback: () => void): Unsubscribe`
246
259
 
247
260
  Registers a callback fired when the host initiates closing the game (for example, close button, gesture, or host navigation). Use this for lightweight cleanup.
package/dist/index.cjs CHANGED
@@ -43,6 +43,7 @@ __export(index_exports, {
43
43
  onResume: () => onResume,
44
44
  purchase: () => purchase,
45
45
  saveGameState: () => saveGameState,
46
+ share: () => share,
46
47
  shareRoomCode: () => shareRoomCode,
47
48
  submitScore: () => submitScore,
48
49
  syncProducts: () => syncProducts,
@@ -156,7 +157,7 @@ function emitScoreConfig(config) {
156
157
  warnMissingBridge("emitScoreConfig");
157
158
  }
158
159
 
159
- // src/state.ts
160
+ // src/share.ts
160
161
  function isDevelopment4() {
161
162
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
162
163
  return nodeEnv !== "production";
@@ -167,6 +168,69 @@ function getBridgeWindow4() {
167
168
  }
168
169
  return window;
169
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
+ }
170
234
  function isPlainObject(value) {
171
235
  if (!value || typeof value !== "object" || Array.isArray(value)) {
172
236
  return false;
@@ -174,22 +238,22 @@ function isPlainObject(value) {
174
238
  const proto = Object.getPrototypeOf(value);
175
239
  return proto === Object.prototype || proto === null;
176
240
  }
177
- function warnMissingBridge2(methodName) {
178
- if (isDevelopment4()) {
241
+ function warnMissingBridge3(methodName) {
242
+ if (isDevelopment5()) {
179
243
  console.warn(
180
244
  "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
181
245
  );
182
246
  }
183
247
  }
184
248
  function loadGameState() {
185
- const bridge = getBridgeWindow4();
249
+ const bridge = getBridgeWindow5();
186
250
  if (typeof bridge?.loadGameState !== "function") {
187
- warnMissingBridge2("loadGameState");
251
+ warnMissingBridge3("loadGameState");
188
252
  return {};
189
253
  }
190
254
  const state = bridge.loadGameState();
191
255
  if (!isPlainObject(state)) {
192
- if (isDevelopment4()) {
256
+ if (isDevelopment5()) {
193
257
  console.warn(
194
258
  "[oasiz/sdk] loadGameState returned invalid data. Falling back to empty object."
195
259
  );
@@ -200,35 +264,35 @@ function loadGameState() {
200
264
  }
201
265
  function saveGameState(state) {
202
266
  if (!isPlainObject(state)) {
203
- if (isDevelopment4()) {
267
+ if (isDevelopment5()) {
204
268
  console.warn("[oasiz/sdk] saveGameState expected a plain object:", state);
205
269
  }
206
270
  return;
207
271
  }
208
- const bridge = getBridgeWindow4();
272
+ const bridge = getBridgeWindow5();
209
273
  if (typeof bridge?.saveGameState === "function") {
210
274
  bridge.saveGameState(state);
211
275
  return;
212
276
  }
213
- warnMissingBridge2("saveGameState");
277
+ warnMissingBridge3("saveGameState");
214
278
  }
215
279
  function flushGameState() {
216
- const bridge = getBridgeWindow4();
280
+ const bridge = getBridgeWindow5();
217
281
  if (typeof bridge?.flushGameState === "function") {
218
282
  bridge.flushGameState();
219
283
  return;
220
284
  }
221
- warnMissingBridge2("flushGameState");
285
+ warnMissingBridge3("flushGameState");
222
286
  }
223
287
 
224
288
  // src/lifecycle.ts
225
- function isDevelopment5() {
289
+ function isDevelopment6() {
226
290
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
227
291
  return nodeEnv !== "production";
228
292
  }
229
293
  function addLifecycleListener(eventName, callback) {
230
294
  if (typeof window === "undefined") {
231
- if (isDevelopment5()) {
295
+ if (isDevelopment6()) {
232
296
  console.warn(
233
297
  "[oasiz/sdk] " + eventName + " listener registered without a browser window. This is expected in local development."
234
298
  );
@@ -249,26 +313,34 @@ function onResume(callback) {
249
313
 
250
314
  // src/navigation.ts
251
315
  var activeBackListeners = 0;
252
- function isDevelopment6() {
316
+ function isDevelopment7() {
253
317
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
254
318
  return nodeEnv !== "production";
255
319
  }
256
- function getBridgeWindow5() {
320
+ function getBridgeWindow6() {
257
321
  if (typeof window === "undefined") {
258
322
  return void 0;
259
323
  }
260
324
  return window;
261
325
  }
262
- function warnMissingBridge3(methodName) {
263
- if (isDevelopment6()) {
326
+ function warnMissingBridge4(methodName) {
327
+ if (isDevelopment7()) {
264
328
  console.warn(
265
329
  "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
266
330
  );
267
331
  }
268
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
+ }
269
341
  function addNavigationListener(eventName, callback) {
270
342
  if (typeof window === "undefined") {
271
- if (isDevelopment6()) {
343
+ if (isDevelopment7()) {
272
344
  console.warn(
273
345
  "[oasiz/sdk] " + eventName + " listener registered without a browser window. This is expected in local development."
274
346
  );
@@ -281,25 +353,32 @@ function addNavigationListener(eventName, callback) {
281
353
  return () => window.removeEventListener(eventName, handler);
282
354
  }
283
355
  function onBackButton(callback) {
284
- const off = addNavigationListener("oasiz:back", callback);
285
- 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();
286
365
  activeBackListeners += 1;
287
366
  if (activeBackListeners === 1) {
288
367
  if (typeof bridge?.__oasizSetBackOverride === "function") {
289
368
  bridge.__oasizSetBackOverride(true);
290
369
  } else {
291
- warnMissingBridge3("__oasizSetBackOverride");
370
+ warnMissingBridge4("__oasizSetBackOverride");
292
371
  }
293
372
  }
294
373
  return () => {
295
374
  off();
296
375
  activeBackListeners = Math.max(0, activeBackListeners - 1);
297
376
  if (activeBackListeners === 0) {
298
- const currentBridge = getBridgeWindow5();
377
+ const currentBridge = getBridgeWindow6();
299
378
  if (typeof currentBridge?.__oasizSetBackOverride === "function") {
300
379
  currentBridge.__oasizSetBackOverride(false);
301
380
  } else {
302
- warnMissingBridge3("__oasizSetBackOverride");
381
+ warnMissingBridge4("__oasizSetBackOverride");
303
382
  }
304
383
  }
305
384
  };
@@ -308,36 +387,36 @@ function onLeaveGame(callback) {
308
387
  return addNavigationListener("oasiz:leave", callback);
309
388
  }
310
389
  function leaveGame() {
311
- const bridge = getBridgeWindow5();
390
+ const bridge = getBridgeWindow6();
312
391
  if (typeof bridge?.__oasizLeaveGame === "function") {
313
392
  bridge.__oasizLeaveGame();
314
393
  return;
315
394
  }
316
- warnMissingBridge3("__oasizLeaveGame");
395
+ warnMissingBridge4("__oasizLeaveGame");
317
396
  }
318
397
 
319
398
  // src/store.ts
320
- function isDevelopment7() {
399
+ function isDevelopment8() {
321
400
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
322
401
  return nodeEnv !== "production";
323
402
  }
324
- function getBridgeWindow6() {
403
+ function getBridgeWindow7() {
325
404
  if (typeof window === "undefined") {
326
405
  return void 0;
327
406
  }
328
407
  return window;
329
408
  }
330
- function warnMissingBridge4(methodName) {
331
- if (isDevelopment7()) {
409
+ function warnMissingBridge5(methodName) {
410
+ if (isDevelopment8()) {
332
411
  console.warn(
333
412
  "[oasiz/sdk] " + methodName + " store bridge is unavailable. This is expected in local development."
334
413
  );
335
414
  }
336
415
  }
337
416
  async function requestStoreBridge(action, payload) {
338
- const bridge = getBridgeWindow6();
417
+ const bridge = getBridgeWindow7();
339
418
  if (typeof bridge?.__oasizStoreBridgeRequest !== "function") {
340
- warnMissingBridge4(action);
419
+ warnMissingBridge5(action);
341
420
  throw new Error("Store bridge unavailable");
342
421
  }
343
422
  return await bridge.__oasizStoreBridgeRequest({
@@ -415,6 +494,7 @@ function onJemBalanceChanged(callback) {
415
494
  var oasiz = {
416
495
  submitScore,
417
496
  emitScoreConfig,
497
+ share,
418
498
  triggerHaptic,
419
499
  loadGameState,
420
500
  saveGameState,
@@ -475,6 +555,7 @@ var oasiz = {
475
555
  onResume,
476
556
  purchase,
477
557
  saveGameState,
558
+ share,
478
559
  shareRoomCode,
479
560
  submitScore,
480
561
  syncProducts,
package/dist/index.d.cts CHANGED
@@ -7,6 +7,11 @@ interface ScoreConfig {
7
7
  anchors: [ScoreAnchor, ScoreAnchor, ScoreAnchor, ScoreAnchor];
8
8
  }
9
9
  type GameState = Record<string, unknown>;
10
+ interface ShareRequest {
11
+ image?: string;
12
+ score?: number;
13
+ text?: string;
14
+ }
10
15
  type StoreProductType = "non-consumable" | "consumable";
11
16
  type StoreProductStatus = "active" | "disabled" | "archived";
12
17
  type StoreEntitlementStatus = "active" | "consumed" | "revoked" | "refunded";
@@ -90,6 +95,8 @@ declare function getPlayerAvatar(): string | undefined;
90
95
  declare function submitScore(score: number): void;
91
96
  declare function emitScoreConfig(config: ScoreConfig): void;
92
97
 
98
+ declare function share(options: ShareRequest): Promise<void>;
99
+
93
100
  declare function loadGameState(): GameState;
94
101
  declare function saveGameState(state: GameState): void;
95
102
  declare function flushGameState(): void;
@@ -116,6 +123,7 @@ declare function onJemBalanceChanged(callback: (jemBalance: number) => void): ()
116
123
  declare const oasiz: {
117
124
  submitScore: typeof submitScore;
118
125
  emitScoreConfig: typeof emitScoreConfig;
126
+ share: typeof share;
119
127
  triggerHaptic: typeof triggerHaptic;
120
128
  loadGameState: typeof loadGameState;
121
129
  saveGameState: typeof saveGameState;
@@ -144,4 +152,4 @@ declare const oasiz: {
144
152
  readonly playerAvatar: string | undefined;
145
153
  };
146
154
 
147
- export { type GameState, type HapticType, type ScoreAnchor, type ScoreConfig, type StoreConsumeFailure, type StoreConsumeResult, type StoreConsumeSuccess, type StoreEntitlement, type StoreEntitlementStatus, type StoreManifestProduct, type StoreProduct, type StoreProductStatus, type StoreProductType, type StorePurchaseFailure, type StorePurchaseResult, type StorePurchaseSuccess, type StoreStateSnapshot, type Unsubscribe, consume, emitScoreConfig, flushGameState, getEntitlements, getGameId, getJemBalance, getPlayerAvatar, getPlayerName, getProducts, getQuantity, getRoomCode, hasEntitlement, leaveGame, loadGameState, oasiz, onBackButton, onEntitlementsChanged, onJemBalanceChanged, onLeaveGame, onPause, onResume, purchase, saveGameState, shareRoomCode, submitScore, syncProducts, triggerHaptic };
155
+ export { type GameState, type HapticType, type ScoreAnchor, type ScoreConfig, type ShareRequest, type StoreConsumeFailure, type StoreConsumeResult, type StoreConsumeSuccess, type StoreEntitlement, type StoreEntitlementStatus, type StoreManifestProduct, type StoreProduct, type StoreProductStatus, type StoreProductType, type StorePurchaseFailure, type StorePurchaseResult, type StorePurchaseSuccess, type StoreStateSnapshot, type Unsubscribe, consume, emitScoreConfig, flushGameState, getEntitlements, getGameId, getJemBalance, getPlayerAvatar, getPlayerName, getProducts, getQuantity, getRoomCode, hasEntitlement, leaveGame, loadGameState, oasiz, onBackButton, onEntitlementsChanged, onJemBalanceChanged, onLeaveGame, onPause, onResume, purchase, saveGameState, share, shareRoomCode, submitScore, syncProducts, triggerHaptic };
package/dist/index.d.ts CHANGED
@@ -7,6 +7,11 @@ interface ScoreConfig {
7
7
  anchors: [ScoreAnchor, ScoreAnchor, ScoreAnchor, ScoreAnchor];
8
8
  }
9
9
  type GameState = Record<string, unknown>;
10
+ interface ShareRequest {
11
+ image?: string;
12
+ score?: number;
13
+ text?: string;
14
+ }
10
15
  type StoreProductType = "non-consumable" | "consumable";
11
16
  type StoreProductStatus = "active" | "disabled" | "archived";
12
17
  type StoreEntitlementStatus = "active" | "consumed" | "revoked" | "refunded";
@@ -90,6 +95,8 @@ declare function getPlayerAvatar(): string | undefined;
90
95
  declare function submitScore(score: number): void;
91
96
  declare function emitScoreConfig(config: ScoreConfig): void;
92
97
 
98
+ declare function share(options: ShareRequest): Promise<void>;
99
+
93
100
  declare function loadGameState(): GameState;
94
101
  declare function saveGameState(state: GameState): void;
95
102
  declare function flushGameState(): void;
@@ -116,6 +123,7 @@ declare function onJemBalanceChanged(callback: (jemBalance: number) => void): ()
116
123
  declare const oasiz: {
117
124
  submitScore: typeof submitScore;
118
125
  emitScoreConfig: typeof emitScoreConfig;
126
+ share: typeof share;
119
127
  triggerHaptic: typeof triggerHaptic;
120
128
  loadGameState: typeof loadGameState;
121
129
  saveGameState: typeof saveGameState;
@@ -144,4 +152,4 @@ declare const oasiz: {
144
152
  readonly playerAvatar: string | undefined;
145
153
  };
146
154
 
147
- export { type GameState, type HapticType, type ScoreAnchor, type ScoreConfig, type StoreConsumeFailure, type StoreConsumeResult, type StoreConsumeSuccess, type StoreEntitlement, type StoreEntitlementStatus, type StoreManifestProduct, type StoreProduct, type StoreProductStatus, type StoreProductType, type StorePurchaseFailure, type StorePurchaseResult, type StorePurchaseSuccess, type StoreStateSnapshot, type Unsubscribe, consume, emitScoreConfig, flushGameState, getEntitlements, getGameId, getJemBalance, getPlayerAvatar, getPlayerName, getProducts, getQuantity, getRoomCode, hasEntitlement, leaveGame, loadGameState, oasiz, onBackButton, onEntitlementsChanged, onJemBalanceChanged, onLeaveGame, onPause, onResume, purchase, saveGameState, shareRoomCode, submitScore, syncProducts, triggerHaptic };
155
+ export { type GameState, type HapticType, type ScoreAnchor, type ScoreConfig, type ShareRequest, type StoreConsumeFailure, type StoreConsumeResult, type StoreConsumeSuccess, type StoreEntitlement, type StoreEntitlementStatus, type StoreManifestProduct, type StoreProduct, type StoreProductStatus, type StoreProductType, type StorePurchaseFailure, type StorePurchaseResult, type StorePurchaseSuccess, type StoreStateSnapshot, type Unsubscribe, consume, emitScoreConfig, flushGameState, getEntitlements, getGameId, getJemBalance, getPlayerAvatar, getPlayerName, getProducts, getQuantity, getRoomCode, hasEntitlement, leaveGame, loadGameState, oasiz, onBackButton, onEntitlementsChanged, onJemBalanceChanged, onLeaveGame, onPause, onResume, purchase, saveGameState, share, shareRoomCode, submitScore, syncProducts, triggerHaptic };
package/dist/index.js CHANGED
@@ -104,7 +104,7 @@ function emitScoreConfig(config) {
104
104
  warnMissingBridge("emitScoreConfig");
105
105
  }
106
106
 
107
- // src/state.ts
107
+ // src/share.ts
108
108
  function isDevelopment4() {
109
109
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
110
110
  return nodeEnv !== "production";
@@ -115,6 +115,69 @@ function getBridgeWindow4() {
115
115
  }
116
116
  return window;
117
117
  }
118
+ function warnMissingBridge2(methodName) {
119
+ if (isDevelopment4()) {
120
+ console.warn(
121
+ "[oasiz/sdk] " + methodName + " share bridge is unavailable. This is expected in local development."
122
+ );
123
+ }
124
+ }
125
+ function isValidImageReference(image) {
126
+ if (/^data:image\/[a-zA-Z0-9.+-]+;base64,/.test(image)) {
127
+ return true;
128
+ }
129
+ try {
130
+ const parsed = new URL(image);
131
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
132
+ } catch {
133
+ return false;
134
+ }
135
+ }
136
+ function validateRequest(options) {
137
+ const text = typeof options.text === "string" ? options.text.trim() : "";
138
+ const hasText = text.length > 0;
139
+ const hasScore = options.score !== void 0;
140
+ const hasImage = typeof options.image === "string" && options.image.length > 0;
141
+ if (!hasText && !hasScore && !hasImage) {
142
+ throw new Error("Share request requires text, score, or image.");
143
+ }
144
+ if (hasScore) {
145
+ if (typeof options.score !== "number" || !Number.isInteger(options.score) || options.score < 0) {
146
+ throw new Error("Share score must be a non-negative integer.");
147
+ }
148
+ }
149
+ if (hasImage && !isValidImageReference(options.image)) {
150
+ throw new Error(
151
+ "Share image must be an http(s) URL or a data:image/... base64 string."
152
+ );
153
+ }
154
+ return {
155
+ ...hasText ? { text } : {},
156
+ ...hasScore ? { score: options.score } : {},
157
+ ...hasImage ? { image: options.image } : {}
158
+ };
159
+ }
160
+ async function share(options) {
161
+ const request = validateRequest(options);
162
+ const bridge = getBridgeWindow4();
163
+ if (typeof bridge?.__oasizShareRequest !== "function") {
164
+ warnMissingBridge2("__oasizShareRequest");
165
+ throw new Error("Share bridge unavailable");
166
+ }
167
+ await bridge.__oasizShareRequest(request);
168
+ }
169
+
170
+ // src/state.ts
171
+ function isDevelopment5() {
172
+ const nodeEnv = globalThis.process?.env?.NODE_ENV;
173
+ return nodeEnv !== "production";
174
+ }
175
+ function getBridgeWindow5() {
176
+ if (typeof window === "undefined") {
177
+ return void 0;
178
+ }
179
+ return window;
180
+ }
118
181
  function isPlainObject(value) {
119
182
  if (!value || typeof value !== "object" || Array.isArray(value)) {
120
183
  return false;
@@ -122,22 +185,22 @@ function isPlainObject(value) {
122
185
  const proto = Object.getPrototypeOf(value);
123
186
  return proto === Object.prototype || proto === null;
124
187
  }
125
- function warnMissingBridge2(methodName) {
126
- if (isDevelopment4()) {
188
+ function warnMissingBridge3(methodName) {
189
+ if (isDevelopment5()) {
127
190
  console.warn(
128
191
  "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
129
192
  );
130
193
  }
131
194
  }
132
195
  function loadGameState() {
133
- const bridge = getBridgeWindow4();
196
+ const bridge = getBridgeWindow5();
134
197
  if (typeof bridge?.loadGameState !== "function") {
135
- warnMissingBridge2("loadGameState");
198
+ warnMissingBridge3("loadGameState");
136
199
  return {};
137
200
  }
138
201
  const state = bridge.loadGameState();
139
202
  if (!isPlainObject(state)) {
140
- if (isDevelopment4()) {
203
+ if (isDevelopment5()) {
141
204
  console.warn(
142
205
  "[oasiz/sdk] loadGameState returned invalid data. Falling back to empty object."
143
206
  );
@@ -148,35 +211,35 @@ function loadGameState() {
148
211
  }
149
212
  function saveGameState(state) {
150
213
  if (!isPlainObject(state)) {
151
- if (isDevelopment4()) {
214
+ if (isDevelopment5()) {
152
215
  console.warn("[oasiz/sdk] saveGameState expected a plain object:", state);
153
216
  }
154
217
  return;
155
218
  }
156
- const bridge = getBridgeWindow4();
219
+ const bridge = getBridgeWindow5();
157
220
  if (typeof bridge?.saveGameState === "function") {
158
221
  bridge.saveGameState(state);
159
222
  return;
160
223
  }
161
- warnMissingBridge2("saveGameState");
224
+ warnMissingBridge3("saveGameState");
162
225
  }
163
226
  function flushGameState() {
164
- const bridge = getBridgeWindow4();
227
+ const bridge = getBridgeWindow5();
165
228
  if (typeof bridge?.flushGameState === "function") {
166
229
  bridge.flushGameState();
167
230
  return;
168
231
  }
169
- warnMissingBridge2("flushGameState");
232
+ warnMissingBridge3("flushGameState");
170
233
  }
171
234
 
172
235
  // src/lifecycle.ts
173
- function isDevelopment5() {
236
+ function isDevelopment6() {
174
237
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
175
238
  return nodeEnv !== "production";
176
239
  }
177
240
  function addLifecycleListener(eventName, callback) {
178
241
  if (typeof window === "undefined") {
179
- if (isDevelopment5()) {
242
+ if (isDevelopment6()) {
180
243
  console.warn(
181
244
  "[oasiz/sdk] " + eventName + " listener registered without a browser window. This is expected in local development."
182
245
  );
@@ -197,26 +260,34 @@ function onResume(callback) {
197
260
 
198
261
  // src/navigation.ts
199
262
  var activeBackListeners = 0;
200
- function isDevelopment6() {
263
+ function isDevelopment7() {
201
264
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
202
265
  return nodeEnv !== "production";
203
266
  }
204
- function getBridgeWindow5() {
267
+ function getBridgeWindow6() {
205
268
  if (typeof window === "undefined") {
206
269
  return void 0;
207
270
  }
208
271
  return window;
209
272
  }
210
- function warnMissingBridge3(methodName) {
211
- if (isDevelopment6()) {
273
+ function warnMissingBridge4(methodName) {
274
+ if (isDevelopment7()) {
212
275
  console.warn(
213
276
  "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
214
277
  );
215
278
  }
216
279
  }
280
+ function normalizeNavigationError(error) {
281
+ if (error instanceof Error) {
282
+ return error;
283
+ }
284
+ return new Error(
285
+ typeof error === "string" ? error : "Back button callback failed."
286
+ );
287
+ }
217
288
  function addNavigationListener(eventName, callback) {
218
289
  if (typeof window === "undefined") {
219
- if (isDevelopment6()) {
290
+ if (isDevelopment7()) {
220
291
  console.warn(
221
292
  "[oasiz/sdk] " + eventName + " listener registered without a browser window. This is expected in local development."
222
293
  );
@@ -229,25 +300,32 @@ function addNavigationListener(eventName, callback) {
229
300
  return () => window.removeEventListener(eventName, handler);
230
301
  }
231
302
  function onBackButton(callback) {
232
- const off = addNavigationListener("oasiz:back", callback);
233
- const bridge = getBridgeWindow5();
303
+ const off = addNavigationListener("oasiz:back", () => {
304
+ try {
305
+ callback();
306
+ } catch (error) {
307
+ leaveGame();
308
+ throw normalizeNavigationError(error);
309
+ }
310
+ });
311
+ const bridge = getBridgeWindow6();
234
312
  activeBackListeners += 1;
235
313
  if (activeBackListeners === 1) {
236
314
  if (typeof bridge?.__oasizSetBackOverride === "function") {
237
315
  bridge.__oasizSetBackOverride(true);
238
316
  } else {
239
- warnMissingBridge3("__oasizSetBackOverride");
317
+ warnMissingBridge4("__oasizSetBackOverride");
240
318
  }
241
319
  }
242
320
  return () => {
243
321
  off();
244
322
  activeBackListeners = Math.max(0, activeBackListeners - 1);
245
323
  if (activeBackListeners === 0) {
246
- const currentBridge = getBridgeWindow5();
324
+ const currentBridge = getBridgeWindow6();
247
325
  if (typeof currentBridge?.__oasizSetBackOverride === "function") {
248
326
  currentBridge.__oasizSetBackOverride(false);
249
327
  } else {
250
- warnMissingBridge3("__oasizSetBackOverride");
328
+ warnMissingBridge4("__oasizSetBackOverride");
251
329
  }
252
330
  }
253
331
  };
@@ -256,36 +334,36 @@ function onLeaveGame(callback) {
256
334
  return addNavigationListener("oasiz:leave", callback);
257
335
  }
258
336
  function leaveGame() {
259
- const bridge = getBridgeWindow5();
337
+ const bridge = getBridgeWindow6();
260
338
  if (typeof bridge?.__oasizLeaveGame === "function") {
261
339
  bridge.__oasizLeaveGame();
262
340
  return;
263
341
  }
264
- warnMissingBridge3("__oasizLeaveGame");
342
+ warnMissingBridge4("__oasizLeaveGame");
265
343
  }
266
344
 
267
345
  // src/store.ts
268
- function isDevelopment7() {
346
+ function isDevelopment8() {
269
347
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
270
348
  return nodeEnv !== "production";
271
349
  }
272
- function getBridgeWindow6() {
350
+ function getBridgeWindow7() {
273
351
  if (typeof window === "undefined") {
274
352
  return void 0;
275
353
  }
276
354
  return window;
277
355
  }
278
- function warnMissingBridge4(methodName) {
279
- if (isDevelopment7()) {
356
+ function warnMissingBridge5(methodName) {
357
+ if (isDevelopment8()) {
280
358
  console.warn(
281
359
  "[oasiz/sdk] " + methodName + " store bridge is unavailable. This is expected in local development."
282
360
  );
283
361
  }
284
362
  }
285
363
  async function requestStoreBridge(action, payload) {
286
- const bridge = getBridgeWindow6();
364
+ const bridge = getBridgeWindow7();
287
365
  if (typeof bridge?.__oasizStoreBridgeRequest !== "function") {
288
- warnMissingBridge4(action);
366
+ warnMissingBridge5(action);
289
367
  throw new Error("Store bridge unavailable");
290
368
  }
291
369
  return await bridge.__oasizStoreBridgeRequest({
@@ -363,6 +441,7 @@ function onJemBalanceChanged(callback) {
363
441
  var oasiz = {
364
442
  submitScore,
365
443
  emitScoreConfig,
444
+ share,
366
445
  triggerHaptic,
367
446
  loadGameState,
368
447
  saveGameState,
@@ -422,6 +501,7 @@ export {
422
501
  onResume,
423
502
  purchase,
424
503
  saveGameState,
504
+ share,
425
505
  shareRoomCode,
426
506
  submitScore,
427
507
  syncProducts,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oasiz/sdk",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "description": "Typed SDK for Oasiz game platform bridge APIs.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",