@oasiz/sdk 0.1.0 → 1.0.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
@@ -1,6 +1,6 @@
1
1
  # @oasiz/sdk
2
2
 
3
- Typed SDK for Oasiz game bridge APIs.
3
+ Typed SDK for integrating games with the Oasiz platform. Handles score submission, haptic feedback, cross-session state persistence, multiplayer room codes, and app lifecycle events.
4
4
 
5
5
  ## Install
6
6
 
@@ -8,35 +8,191 @@ Typed SDK for Oasiz game bridge APIs.
8
8
  npm install @oasiz/sdk
9
9
  ```
10
10
 
11
- ## Usage
11
+ ## Quick start
12
12
 
13
13
  ```ts
14
- import { oasis } from "@oasiz/sdk";
14
+ import { oasiz } from "@oasiz/sdk";
15
15
 
16
- oasis.emitScoreConfig({
17
- anchors: [
18
- { raw: 30, normalized: 100 },
19
- { raw: 60, normalized: 300 },
20
- { raw: 120, normalized: 600 },
21
- { raw: 300, normalized: 950 },
22
- ],
16
+ // 1. Load persisted state at the start of each session
17
+ const state = oasiz.loadGameState();
18
+ let level = typeof state.level === "number" ? state.level : 1;
19
+
20
+ // 2. Save state at checkpoints
21
+ oasiz.saveGameState({ level, coins: 42 });
22
+
23
+ // 3. Trigger haptics on key events
24
+ oasiz.triggerHaptic("medium");
25
+
26
+ // 4. Submit score when the game ends
27
+ oasiz.submitScore(score);
28
+ ```
29
+
30
+ ---
31
+
32
+ ## Score
33
+
34
+ ### `oasiz.submitScore(score: number)`
35
+
36
+ Submit the player's score. The platform handles leaderboard persistence — do not track high scores locally.
37
+
38
+ ```ts
39
+ oasiz.submitScore(this.score);
40
+ ```
41
+
42
+ - `score` must be a non-negative integer. Floats are floored automatically.
43
+ - When and how often you call this depends on your game type (once at game over, end of each level, or throttled for long sessions).
44
+
45
+ ---
46
+
47
+ ## Haptics
48
+
49
+ ### `oasiz.triggerHaptic(type: HapticType)`
50
+
51
+ Trigger native haptic feedback. Always guard with the user's haptics setting.
52
+
53
+ ```ts
54
+ type HapticType = "light" | "medium" | "heavy" | "success" | "error";
55
+ ```
56
+
57
+ | Type | When to use |
58
+ |---|---|
59
+ | `"light"` | UI button taps, menu navigation, D-pad press |
60
+ | `"medium"` | Collecting items, standard collisions, scoring |
61
+ | `"heavy"` | Explosions, major impacts, screen shake |
62
+ | `"success"` | Level complete, achievement unlocked |
63
+ | `"error"` | Damage taken, game over, invalid action |
64
+
65
+ ```ts
66
+ button.addEventListener("click", () => {
67
+ oasiz.triggerHaptic("light");
23
68
  });
24
69
 
25
- const saved = oasis.loadGameState();
26
- oasis.saveGameState({ ...saved, level: 3 });
27
- oasis.triggerHaptic("light");
28
- oasis.submitScore(42);
70
+ private onGameOver(): void {
71
+ oasiz.submitScore(this.score);
72
+ if (this.settings.haptics) {
73
+ oasiz.triggerHaptic("error");
74
+ }
75
+ }
29
76
  ```
30
77
 
31
- ## API
78
+ ---
79
+
80
+ ## Game state persistence
81
+
82
+ Persist cross-session data such as unlocked levels, inventory, or lifetime stats. Available across devices and app reinstalls.
83
+
84
+ ### `oasiz.loadGameState(): Record<string, unknown>`
85
+
86
+ Returns the player's saved state synchronously. Returns `{}` if no state has been saved yet.
87
+
88
+ ```ts
89
+ const state = oasiz.loadGameState();
90
+ this.level = typeof state.level === "number" ? state.level : 1;
91
+ ```
32
92
 
33
- - `submitScore(score)`
34
- - `emitScoreConfig(config)`
35
- - `triggerHaptic(type)`
36
- - `loadGameState()`
37
- - `saveGameState(state)`
38
- - `flushGameState()`
39
- - `shareRoomCode(code)`
40
- - `onPause(callback)`
41
- - `onResume(callback)`
42
- - `oasis.gameId`, `oasis.roomCode`, `oasis.playerName`, `oasis.playerAvatar`
93
+ ### `oasiz.saveGameState(state: Record<string, unknown>)`
94
+
95
+ Queues a debounced save. Call freely at checkpoints.
96
+
97
+ ```ts
98
+ oasiz.saveGameState({ level: this.level, unlockedSkins: this.unlockedSkins });
99
+ ```
100
+
101
+ - State must be a plain JSON object (not an array or primitive).
102
+ - Use `saveGameState` instead of `localStorage` for cross-session progress.
103
+ - Do not store scores here — use `submitScore`.
104
+
105
+ ### `oasiz.flushGameState()`
106
+
107
+ Forces an immediate write, bypassing the debounce. Use at game over or before page unload.
108
+
109
+ ```ts
110
+ oasiz.saveGameState({ level: this.level });
111
+ oasiz.flushGameState();
112
+ oasiz.submitScore(this.score);
113
+ ```
114
+
115
+ ---
116
+
117
+ ## Lifecycle
118
+
119
+ ### `oasiz.onPause(callback: () => void): Unsubscribe`
120
+ ### `oasiz.onResume(callback: () => void): Unsubscribe`
121
+
122
+ Both return an unsubscribe function.
123
+
124
+ ```ts
125
+ const offPause = oasiz.onPause(() => {
126
+ this.gameLoop.stop();
127
+ this.bgMusic.pause();
128
+ });
129
+
130
+ const offResume = oasiz.onResume(() => {
131
+ this.gameLoop.start();
132
+ this.bgMusic.play();
133
+ });
134
+
135
+ offPause();
136
+ offResume();
137
+ ```
138
+
139
+ ---
140
+
141
+ ## Multiplayer
142
+
143
+ ### `oasiz.shareRoomCode(code: string | null)`
144
+
145
+ Notify the platform of the active multiplayer room. Pass `null` when leaving.
146
+
147
+ ```ts
148
+ oasiz.shareRoomCode(getRoomCode());
149
+ oasiz.shareRoomCode(null);
150
+ ```
151
+
152
+ ### Read-only properties
153
+
154
+ | Property | Type | Description |
155
+ |---|---|---|
156
+ | `oasiz.gameId` | string \| undefined | The platform's game ID |
157
+ | `oasiz.roomCode` | string \| undefined | Pre-filled room code from invite |
158
+ | `oasiz.playerName` | string \| undefined | Player's display name |
159
+ | `oasiz.playerAvatar` | string \| undefined | Player's profile picture URL |
160
+
161
+ ---
162
+
163
+ ## Named exports
164
+
165
+ ```ts
166
+ import {
167
+ submitScore,
168
+ triggerHaptic,
169
+ loadGameState,
170
+ saveGameState,
171
+ flushGameState,
172
+ shareRoomCode,
173
+ onPause,
174
+ onResume,
175
+ getGameId,
176
+ getRoomCode,
177
+ getPlayerName,
178
+ getPlayerAvatar,
179
+ } from "@oasiz/sdk";
180
+ ```
181
+
182
+ ---
183
+
184
+ ## TypeScript types
185
+
186
+ ```ts
187
+ import type { HapticType, GameState } from "@oasiz/sdk";
188
+ ```
189
+
190
+ ---
191
+
192
+ ## Local development
193
+
194
+ All methods safely no-op when the platform bridges are not injected. In development mode a console warning is logged:
195
+
196
+ ```
197
+ [oasiz/sdk] submitScore bridge is unavailable. This is expected in local development.
198
+ ```
package/dist/index.cjs CHANGED
@@ -20,14 +20,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- emitScoreConfig: () => emitScoreConfig,
24
23
  flushGameState: () => flushGameState,
25
24
  getGameId: () => getGameId,
26
25
  getPlayerAvatar: () => getPlayerAvatar,
27
26
  getPlayerName: () => getPlayerName,
28
27
  getRoomCode: () => getRoomCode,
29
28
  loadGameState: () => loadGameState,
30
- oasis: () => oasis,
29
+ oasiz: () => oasiz,
31
30
  onPause: () => onPause,
32
31
  onResume: () => onResume,
33
32
  saveGameState: () => saveGameState,
@@ -134,14 +133,6 @@ function submitScore(score) {
134
133
  }
135
134
  warnMissingBridge("submitScore");
136
135
  }
137
- function emitScoreConfig(config) {
138
- const bridge = getBridgeWindow3();
139
- if (typeof bridge?.emitScoreConfig === "function") {
140
- bridge.emitScoreConfig(config);
141
- return;
142
- }
143
- warnMissingBridge("emitScoreConfig");
144
- }
145
136
 
146
137
  // src/state.ts
147
138
  function isDevelopment4() {
@@ -235,9 +226,8 @@ function onResume(callback) {
235
226
  }
236
227
 
237
228
  // src/index.ts
238
- var oasis = {
229
+ var oasiz = {
239
230
  submitScore,
240
- emitScoreConfig,
241
231
  triggerHaptic,
242
232
  loadGameState,
243
233
  saveGameState,
@@ -260,14 +250,13 @@ var oasis = {
260
250
  };
261
251
  // Annotate the CommonJS export names for ESM import in node:
262
252
  0 && (module.exports = {
263
- emitScoreConfig,
264
253
  flushGameState,
265
254
  getGameId,
266
255
  getPlayerAvatar,
267
256
  getPlayerName,
268
257
  getRoomCode,
269
258
  loadGameState,
270
- oasis,
259
+ oasiz,
271
260
  onPause,
272
261
  onResume,
273
262
  saveGameState,
package/dist/index.d.cts CHANGED
@@ -1,35 +1,245 @@
1
+ /**
2
+ * Intensity level for native haptic feedback.
3
+ *
4
+ * | Type | Use case |
5
+ * |-------------|-------------------------------------------------|
6
+ * | `"light"` | UI button taps, menu navigation, D-pad press |
7
+ * | `"medium"` | Collecting items, standard collisions, scoring |
8
+ * | `"heavy"` | Explosions, major impacts, screen shake |
9
+ * | `"success"` | Level complete, achievement unlocked |
10
+ * | `"error"` | Damage taken, game over, invalid action |
11
+ *
12
+ * @category Haptics
13
+ */
1
14
  type HapticType = "light" | "medium" | "heavy" | "success" | "error";
2
- interface ScoreAnchor {
3
- raw: number;
4
- normalized: 100 | 300 | 600 | 950;
5
- }
6
- interface ScoreConfig {
7
- anchors: [ScoreAnchor, ScoreAnchor, ScoreAnchor, ScoreAnchor];
8
- }
15
+ /**
16
+ * A plain JSON-serializable object used for cross-session game state
17
+ * persistence.
18
+ *
19
+ * @category State
20
+ */
9
21
  type GameState = Record<string, unknown>;
10
22
 
23
+ /**
24
+ * Trigger native haptic feedback on the player's device.
25
+ *
26
+ * Always guard calls with the user's haptics preference toggle.
27
+ * The bridge is a no-op in local development (a console warning is logged).
28
+ *
29
+ * @param type - The haptic intensity level.
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * // UI button tap
34
+ * oasiz.triggerHaptic("light");
35
+ *
36
+ * // Tiered hit feedback
37
+ * oasiz.triggerHaptic(isPerfectHit ? "success" : "medium");
38
+ *
39
+ * // Game over
40
+ * oasiz.triggerHaptic("error");
41
+ * ```
42
+ *
43
+ * @category Haptics
44
+ */
11
45
  declare function triggerHaptic(type: HapticType): void;
12
46
 
47
+ /**
48
+ * Notify the platform of the active multiplayer room so friends can join
49
+ * via the invite system. Pass `null` when leaving a room.
50
+ *
51
+ * @param roomCode - The room code string, or `null` to clear.
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * import { insertCoin, getRoomCode } from "playroomkit";
56
+ *
57
+ * await insertCoin({ skipLobby: true });
58
+ * oasiz.shareRoomCode(getRoomCode());
59
+ *
60
+ * // On disconnect
61
+ * oasiz.shareRoomCode(null);
62
+ * ```
63
+ *
64
+ * @category Multiplayer
65
+ */
13
66
  declare function shareRoomCode(roomCode: string | null): void;
67
+ /**
68
+ * Get the platform's internal game ID, injected before the game loads.
69
+ *
70
+ * @returns The game ID string, or `undefined` if not yet injected.
71
+ *
72
+ * @category Multiplayer
73
+ */
14
74
  declare function getGameId(): string | undefined;
75
+ /**
76
+ * Get the pre-filled room code for auto-joining a friend's multiplayer session.
77
+ *
78
+ * @returns The room code string, or `undefined` if not in a multiplayer context.
79
+ *
80
+ * @category Multiplayer
81
+ */
15
82
  declare function getRoomCode(): string | undefined;
83
+ /**
84
+ * Get the current player's display name, injected by the platform.
85
+ *
86
+ * @returns The player name, or `undefined` if not available.
87
+ *
88
+ * @category Multiplayer
89
+ */
16
90
  declare function getPlayerName(): string | undefined;
91
+ /**
92
+ * Get the current player's avatar URL, injected by the platform.
93
+ *
94
+ * @returns The avatar URL string, or `undefined` if not available.
95
+ *
96
+ * @category Multiplayer
97
+ */
17
98
  declare function getPlayerAvatar(): string | undefined;
18
99
 
100
+ /**
101
+ * Submit the player's score.
102
+ *
103
+ * The platform handles leaderboard persistence — do not track high scores locally.
104
+ *
105
+ * - `score` must be a finite number. Floats are floored, negatives are clamped to 0.
106
+ * - In development mode a console warning is logged when the bridge is unavailable.
107
+ *
108
+ * @param score - The player's score (non-negative integer).
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * oasiz.submitScore(Math.floor(this.score));
113
+ * ```
114
+ *
115
+ * @category Score
116
+ */
19
117
  declare function submitScore(score: number): void;
20
- declare function emitScoreConfig(config: ScoreConfig): void;
21
118
 
119
+ /**
120
+ * Load the player's persisted game state.
121
+ *
122
+ * Returns the saved state synchronously. Returns `{}` if no state has been
123
+ * saved yet or if the bridge is unavailable. Always validate the shape of
124
+ * the returned data — it may be empty on first play.
125
+ *
126
+ * Call once at the start of the game.
127
+ *
128
+ * @returns The player's saved {@link GameState}, or an empty object.
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * const state = oasiz.loadGameState();
133
+ * this.level = typeof state.level === "number" ? state.level : 1;
134
+ * ```
135
+ *
136
+ * @category State
137
+ */
22
138
  declare function loadGameState(): GameState;
139
+ /**
140
+ * Queue a debounced save of the player's game state.
141
+ *
142
+ * Saves are batched automatically — call freely at checkpoints without
143
+ * worrying about request spam. State must be a plain JSON-serializable object.
144
+ *
145
+ * @param state - A plain object to persist. Do not pass arrays or primitives.
146
+ *
147
+ * @example
148
+ * ```ts
149
+ * oasiz.saveGameState({
150
+ * level: this.level,
151
+ * lifetimeHits: this.lifetimeHits,
152
+ * unlockedSkins: this.unlockedSkins,
153
+ * });
154
+ * ```
155
+ *
156
+ * @category State
157
+ */
23
158
  declare function saveGameState(state: GameState): void;
159
+ /**
160
+ * Force an immediate write of the pending game state, bypassing the debounce.
161
+ *
162
+ * Use at critical checkpoints like game over or before the page unloads
163
+ * to ensure data is persisted.
164
+ *
165
+ * @example
166
+ * ```ts
167
+ * oasiz.saveGameState({ level: this.level });
168
+ * oasiz.flushGameState(); // ensure it lands before the page closes
169
+ * oasiz.submitScore(this.score);
170
+ * ```
171
+ *
172
+ * @category State
173
+ */
24
174
  declare function flushGameState(): void;
25
175
 
176
+ /**
177
+ * A function that removes an event listener when called.
178
+ * Returned by {@link onPause} and {@link onResume}.
179
+ *
180
+ * @category Lifecycle
181
+ */
26
182
  type Unsubscribe = () => void;
183
+ /**
184
+ * Register a callback that fires when the app goes to the background.
185
+ *
186
+ * Use this to pause game loops, audio, and animations so the game
187
+ * doesn't continue running while the player is away.
188
+ *
189
+ * @param callback - Invoked when the platform dispatches `oasiz:pause`.
190
+ * @returns An {@link Unsubscribe} function that removes the listener.
191
+ *
192
+ * @example
193
+ * ```ts
194
+ * const off = oasiz.onPause(() => {
195
+ * this.gameLoop.stop();
196
+ * this.bgMusic.pause();
197
+ * });
198
+ *
199
+ * // Later, clean up:
200
+ * off();
201
+ * ```
202
+ *
203
+ * @category Lifecycle
204
+ */
27
205
  declare function onPause(callback: () => void): Unsubscribe;
206
+ /**
207
+ * Register a callback that fires when the app returns to the foreground.
208
+ *
209
+ * Use this to resume game loops, audio, and animations after a pause.
210
+ *
211
+ * @param callback - Invoked when the platform dispatches `oasiz:resume`.
212
+ * @returns An {@link Unsubscribe} function that removes the listener.
213
+ *
214
+ * @example
215
+ * ```ts
216
+ * const off = oasiz.onResume(() => {
217
+ * this.gameLoop.start();
218
+ * this.bgMusic.play();
219
+ * });
220
+ *
221
+ * // Later, clean up:
222
+ * off();
223
+ * ```
224
+ *
225
+ * @category Lifecycle
226
+ */
28
227
  declare function onResume(callback: () => void): Unsubscribe;
29
228
 
30
- declare const oasis: {
229
+ /**
230
+ * The main SDK namespace object. Provides convenient access to all
231
+ * platform bridge APIs as a single import.
232
+ *
233
+ * @example
234
+ * ```ts
235
+ * import { oasiz } from "@oasiz/sdk";
236
+ *
237
+ * oasiz.triggerHaptic("medium");
238
+ * oasiz.submitScore(score);
239
+ * ```
240
+ */
241
+ declare const oasiz: {
31
242
  submitScore: typeof submitScore;
32
- emitScoreConfig: typeof emitScoreConfig;
33
243
  triggerHaptic: typeof triggerHaptic;
34
244
  loadGameState: typeof loadGameState;
35
245
  saveGameState: typeof saveGameState;
@@ -43,4 +253,4 @@ declare const oasis: {
43
253
  readonly playerAvatar: string | undefined;
44
254
  };
45
255
 
46
- export { type GameState, type HapticType, type ScoreAnchor, type ScoreConfig, type Unsubscribe, emitScoreConfig, flushGameState, getGameId, getPlayerAvatar, getPlayerName, getRoomCode, loadGameState, oasis, onPause, onResume, saveGameState, shareRoomCode, submitScore, triggerHaptic };
256
+ export { type GameState, type HapticType, type Unsubscribe, flushGameState, getGameId, getPlayerAvatar, getPlayerName, getRoomCode, loadGameState, oasiz, onPause, onResume, saveGameState, shareRoomCode, submitScore, triggerHaptic };
package/dist/index.d.ts CHANGED
@@ -1,35 +1,245 @@
1
+ /**
2
+ * Intensity level for native haptic feedback.
3
+ *
4
+ * | Type | Use case |
5
+ * |-------------|-------------------------------------------------|
6
+ * | `"light"` | UI button taps, menu navigation, D-pad press |
7
+ * | `"medium"` | Collecting items, standard collisions, scoring |
8
+ * | `"heavy"` | Explosions, major impacts, screen shake |
9
+ * | `"success"` | Level complete, achievement unlocked |
10
+ * | `"error"` | Damage taken, game over, invalid action |
11
+ *
12
+ * @category Haptics
13
+ */
1
14
  type HapticType = "light" | "medium" | "heavy" | "success" | "error";
2
- interface ScoreAnchor {
3
- raw: number;
4
- normalized: 100 | 300 | 600 | 950;
5
- }
6
- interface ScoreConfig {
7
- anchors: [ScoreAnchor, ScoreAnchor, ScoreAnchor, ScoreAnchor];
8
- }
15
+ /**
16
+ * A plain JSON-serializable object used for cross-session game state
17
+ * persistence.
18
+ *
19
+ * @category State
20
+ */
9
21
  type GameState = Record<string, unknown>;
10
22
 
23
+ /**
24
+ * Trigger native haptic feedback on the player's device.
25
+ *
26
+ * Always guard calls with the user's haptics preference toggle.
27
+ * The bridge is a no-op in local development (a console warning is logged).
28
+ *
29
+ * @param type - The haptic intensity level.
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * // UI button tap
34
+ * oasiz.triggerHaptic("light");
35
+ *
36
+ * // Tiered hit feedback
37
+ * oasiz.triggerHaptic(isPerfectHit ? "success" : "medium");
38
+ *
39
+ * // Game over
40
+ * oasiz.triggerHaptic("error");
41
+ * ```
42
+ *
43
+ * @category Haptics
44
+ */
11
45
  declare function triggerHaptic(type: HapticType): void;
12
46
 
47
+ /**
48
+ * Notify the platform of the active multiplayer room so friends can join
49
+ * via the invite system. Pass `null` when leaving a room.
50
+ *
51
+ * @param roomCode - The room code string, or `null` to clear.
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * import { insertCoin, getRoomCode } from "playroomkit";
56
+ *
57
+ * await insertCoin({ skipLobby: true });
58
+ * oasiz.shareRoomCode(getRoomCode());
59
+ *
60
+ * // On disconnect
61
+ * oasiz.shareRoomCode(null);
62
+ * ```
63
+ *
64
+ * @category Multiplayer
65
+ */
13
66
  declare function shareRoomCode(roomCode: string | null): void;
67
+ /**
68
+ * Get the platform's internal game ID, injected before the game loads.
69
+ *
70
+ * @returns The game ID string, or `undefined` if not yet injected.
71
+ *
72
+ * @category Multiplayer
73
+ */
14
74
  declare function getGameId(): string | undefined;
75
+ /**
76
+ * Get the pre-filled room code for auto-joining a friend's multiplayer session.
77
+ *
78
+ * @returns The room code string, or `undefined` if not in a multiplayer context.
79
+ *
80
+ * @category Multiplayer
81
+ */
15
82
  declare function getRoomCode(): string | undefined;
83
+ /**
84
+ * Get the current player's display name, injected by the platform.
85
+ *
86
+ * @returns The player name, or `undefined` if not available.
87
+ *
88
+ * @category Multiplayer
89
+ */
16
90
  declare function getPlayerName(): string | undefined;
91
+ /**
92
+ * Get the current player's avatar URL, injected by the platform.
93
+ *
94
+ * @returns The avatar URL string, or `undefined` if not available.
95
+ *
96
+ * @category Multiplayer
97
+ */
17
98
  declare function getPlayerAvatar(): string | undefined;
18
99
 
100
+ /**
101
+ * Submit the player's score.
102
+ *
103
+ * The platform handles leaderboard persistence — do not track high scores locally.
104
+ *
105
+ * - `score` must be a finite number. Floats are floored, negatives are clamped to 0.
106
+ * - In development mode a console warning is logged when the bridge is unavailable.
107
+ *
108
+ * @param score - The player's score (non-negative integer).
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * oasiz.submitScore(Math.floor(this.score));
113
+ * ```
114
+ *
115
+ * @category Score
116
+ */
19
117
  declare function submitScore(score: number): void;
20
- declare function emitScoreConfig(config: ScoreConfig): void;
21
118
 
119
+ /**
120
+ * Load the player's persisted game state.
121
+ *
122
+ * Returns the saved state synchronously. Returns `{}` if no state has been
123
+ * saved yet or if the bridge is unavailable. Always validate the shape of
124
+ * the returned data — it may be empty on first play.
125
+ *
126
+ * Call once at the start of the game.
127
+ *
128
+ * @returns The player's saved {@link GameState}, or an empty object.
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * const state = oasiz.loadGameState();
133
+ * this.level = typeof state.level === "number" ? state.level : 1;
134
+ * ```
135
+ *
136
+ * @category State
137
+ */
22
138
  declare function loadGameState(): GameState;
139
+ /**
140
+ * Queue a debounced save of the player's game state.
141
+ *
142
+ * Saves are batched automatically — call freely at checkpoints without
143
+ * worrying about request spam. State must be a plain JSON-serializable object.
144
+ *
145
+ * @param state - A plain object to persist. Do not pass arrays or primitives.
146
+ *
147
+ * @example
148
+ * ```ts
149
+ * oasiz.saveGameState({
150
+ * level: this.level,
151
+ * lifetimeHits: this.lifetimeHits,
152
+ * unlockedSkins: this.unlockedSkins,
153
+ * });
154
+ * ```
155
+ *
156
+ * @category State
157
+ */
23
158
  declare function saveGameState(state: GameState): void;
159
+ /**
160
+ * Force an immediate write of the pending game state, bypassing the debounce.
161
+ *
162
+ * Use at critical checkpoints like game over or before the page unloads
163
+ * to ensure data is persisted.
164
+ *
165
+ * @example
166
+ * ```ts
167
+ * oasiz.saveGameState({ level: this.level });
168
+ * oasiz.flushGameState(); // ensure it lands before the page closes
169
+ * oasiz.submitScore(this.score);
170
+ * ```
171
+ *
172
+ * @category State
173
+ */
24
174
  declare function flushGameState(): void;
25
175
 
176
+ /**
177
+ * A function that removes an event listener when called.
178
+ * Returned by {@link onPause} and {@link onResume}.
179
+ *
180
+ * @category Lifecycle
181
+ */
26
182
  type Unsubscribe = () => void;
183
+ /**
184
+ * Register a callback that fires when the app goes to the background.
185
+ *
186
+ * Use this to pause game loops, audio, and animations so the game
187
+ * doesn't continue running while the player is away.
188
+ *
189
+ * @param callback - Invoked when the platform dispatches `oasiz:pause`.
190
+ * @returns An {@link Unsubscribe} function that removes the listener.
191
+ *
192
+ * @example
193
+ * ```ts
194
+ * const off = oasiz.onPause(() => {
195
+ * this.gameLoop.stop();
196
+ * this.bgMusic.pause();
197
+ * });
198
+ *
199
+ * // Later, clean up:
200
+ * off();
201
+ * ```
202
+ *
203
+ * @category Lifecycle
204
+ */
27
205
  declare function onPause(callback: () => void): Unsubscribe;
206
+ /**
207
+ * Register a callback that fires when the app returns to the foreground.
208
+ *
209
+ * Use this to resume game loops, audio, and animations after a pause.
210
+ *
211
+ * @param callback - Invoked when the platform dispatches `oasiz:resume`.
212
+ * @returns An {@link Unsubscribe} function that removes the listener.
213
+ *
214
+ * @example
215
+ * ```ts
216
+ * const off = oasiz.onResume(() => {
217
+ * this.gameLoop.start();
218
+ * this.bgMusic.play();
219
+ * });
220
+ *
221
+ * // Later, clean up:
222
+ * off();
223
+ * ```
224
+ *
225
+ * @category Lifecycle
226
+ */
28
227
  declare function onResume(callback: () => void): Unsubscribe;
29
228
 
30
- declare const oasis: {
229
+ /**
230
+ * The main SDK namespace object. Provides convenient access to all
231
+ * platform bridge APIs as a single import.
232
+ *
233
+ * @example
234
+ * ```ts
235
+ * import { oasiz } from "@oasiz/sdk";
236
+ *
237
+ * oasiz.triggerHaptic("medium");
238
+ * oasiz.submitScore(score);
239
+ * ```
240
+ */
241
+ declare const oasiz: {
31
242
  submitScore: typeof submitScore;
32
- emitScoreConfig: typeof emitScoreConfig;
33
243
  triggerHaptic: typeof triggerHaptic;
34
244
  loadGameState: typeof loadGameState;
35
245
  saveGameState: typeof saveGameState;
@@ -43,4 +253,4 @@ declare const oasis: {
43
253
  readonly playerAvatar: string | undefined;
44
254
  };
45
255
 
46
- export { type GameState, type HapticType, type ScoreAnchor, type ScoreConfig, type Unsubscribe, emitScoreConfig, flushGameState, getGameId, getPlayerAvatar, getPlayerName, getRoomCode, loadGameState, oasis, onPause, onResume, saveGameState, shareRoomCode, submitScore, triggerHaptic };
256
+ export { type GameState, type HapticType, type Unsubscribe, flushGameState, getGameId, getPlayerAvatar, getPlayerName, getRoomCode, loadGameState, oasiz, onPause, onResume, saveGameState, shareRoomCode, submitScore, triggerHaptic };
@@ -0,0 +1,252 @@
1
+ "use strict";
2
+ var oasiz = (() => {
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ flushGameState: () => flushGameState,
25
+ getGameId: () => getGameId,
26
+ getPlayerAvatar: () => getPlayerAvatar,
27
+ getPlayerName: () => getPlayerName,
28
+ getRoomCode: () => getRoomCode,
29
+ loadGameState: () => loadGameState,
30
+ oasiz: () => oasiz,
31
+ onPause: () => onPause,
32
+ onResume: () => onResume,
33
+ saveGameState: () => saveGameState,
34
+ shareRoomCode: () => shareRoomCode,
35
+ submitScore: () => submitScore,
36
+ triggerHaptic: () => triggerHaptic
37
+ });
38
+
39
+ // src/haptics.ts
40
+ function isDevelopment() {
41
+ const nodeEnv = globalThis.process?.env?.NODE_ENV;
42
+ return nodeEnv !== "production";
43
+ }
44
+ function getBridgeWindow() {
45
+ if (typeof window === "undefined") {
46
+ return void 0;
47
+ }
48
+ return window;
49
+ }
50
+ function triggerHaptic(type) {
51
+ const bridge = getBridgeWindow();
52
+ if (typeof bridge?.triggerHaptic === "function") {
53
+ bridge.triggerHaptic(type);
54
+ return;
55
+ }
56
+ if (isDevelopment()) {
57
+ console.warn(
58
+ "[oasiz/sdk] triggerHaptic bridge is unavailable. This is expected in local development."
59
+ );
60
+ }
61
+ }
62
+
63
+ // src/multiplayer.ts
64
+ function isDevelopment2() {
65
+ const nodeEnv = globalThis.process?.env?.NODE_ENV;
66
+ return nodeEnv !== "production";
67
+ }
68
+ function getBridgeWindow2() {
69
+ if (typeof window === "undefined") {
70
+ return void 0;
71
+ }
72
+ return window;
73
+ }
74
+ function shareRoomCode(roomCode) {
75
+ const bridge = getBridgeWindow2();
76
+ if (typeof bridge?.shareRoomCode === "function") {
77
+ bridge.shareRoomCode(roomCode);
78
+ return;
79
+ }
80
+ if (isDevelopment2()) {
81
+ console.warn(
82
+ "[oasiz/sdk] shareRoomCode bridge is unavailable. This is expected in local development."
83
+ );
84
+ }
85
+ }
86
+ function getGameId() {
87
+ const bridge = getBridgeWindow2();
88
+ return bridge?.__GAME_ID__;
89
+ }
90
+ function getRoomCode() {
91
+ const bridge = getBridgeWindow2();
92
+ return bridge?.__ROOM_CODE__;
93
+ }
94
+ function getPlayerName() {
95
+ const bridge = getBridgeWindow2();
96
+ return bridge?.__PLAYER_NAME__;
97
+ }
98
+ function getPlayerAvatar() {
99
+ const bridge = getBridgeWindow2();
100
+ return bridge?.__PLAYER_AVATAR__;
101
+ }
102
+
103
+ // src/score.ts
104
+ function isDevelopment3() {
105
+ const nodeEnv = globalThis.process?.env?.NODE_ENV;
106
+ return nodeEnv !== "production";
107
+ }
108
+ function warnMissingBridge(methodName) {
109
+ if (isDevelopment3()) {
110
+ console.warn(
111
+ "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
112
+ );
113
+ }
114
+ }
115
+ function getBridgeWindow3() {
116
+ if (typeof window === "undefined") {
117
+ return void 0;
118
+ }
119
+ return window;
120
+ }
121
+ function submitScore(score) {
122
+ if (!Number.isFinite(score)) {
123
+ if (isDevelopment3()) {
124
+ console.warn("[oasiz/sdk] submitScore expected a finite number:", score);
125
+ }
126
+ return;
127
+ }
128
+ const bridge = getBridgeWindow3();
129
+ const normalizedScore = Math.max(0, Math.floor(score));
130
+ if (typeof bridge?.submitScore === "function") {
131
+ bridge.submitScore(normalizedScore);
132
+ return;
133
+ }
134
+ warnMissingBridge("submitScore");
135
+ }
136
+
137
+ // src/state.ts
138
+ function isDevelopment4() {
139
+ const nodeEnv = globalThis.process?.env?.NODE_ENV;
140
+ return nodeEnv !== "production";
141
+ }
142
+ function getBridgeWindow4() {
143
+ if (typeof window === "undefined") {
144
+ return void 0;
145
+ }
146
+ return window;
147
+ }
148
+ function isPlainObject(value) {
149
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
150
+ return false;
151
+ }
152
+ const proto = Object.getPrototypeOf(value);
153
+ return proto === Object.prototype || proto === null;
154
+ }
155
+ function warnMissingBridge2(methodName) {
156
+ if (isDevelopment4()) {
157
+ console.warn(
158
+ "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
159
+ );
160
+ }
161
+ }
162
+ function loadGameState() {
163
+ const bridge = getBridgeWindow4();
164
+ if (typeof bridge?.loadGameState !== "function") {
165
+ warnMissingBridge2("loadGameState");
166
+ return {};
167
+ }
168
+ const state = bridge.loadGameState();
169
+ if (!isPlainObject(state)) {
170
+ if (isDevelopment4()) {
171
+ console.warn(
172
+ "[oasiz/sdk] loadGameState returned invalid data. Falling back to empty object."
173
+ );
174
+ }
175
+ return {};
176
+ }
177
+ return state;
178
+ }
179
+ function saveGameState(state) {
180
+ if (!isPlainObject(state)) {
181
+ if (isDevelopment4()) {
182
+ console.warn("[oasiz/sdk] saveGameState expected a plain object:", state);
183
+ }
184
+ return;
185
+ }
186
+ const bridge = getBridgeWindow4();
187
+ if (typeof bridge?.saveGameState === "function") {
188
+ bridge.saveGameState(state);
189
+ return;
190
+ }
191
+ warnMissingBridge2("saveGameState");
192
+ }
193
+ function flushGameState() {
194
+ const bridge = getBridgeWindow4();
195
+ if (typeof bridge?.flushGameState === "function") {
196
+ bridge.flushGameState();
197
+ return;
198
+ }
199
+ warnMissingBridge2("flushGameState");
200
+ }
201
+
202
+ // src/lifecycle.ts
203
+ function isDevelopment5() {
204
+ const nodeEnv = globalThis.process?.env?.NODE_ENV;
205
+ return nodeEnv !== "production";
206
+ }
207
+ function addLifecycleListener(eventName, callback) {
208
+ if (typeof window === "undefined") {
209
+ if (isDevelopment5()) {
210
+ console.warn(
211
+ "[oasiz/sdk] " + eventName + " listener registered without a browser window. This is expected in local development."
212
+ );
213
+ }
214
+ return () => {
215
+ };
216
+ }
217
+ const handler = () => callback();
218
+ window.addEventListener(eventName, handler);
219
+ return () => window.removeEventListener(eventName, handler);
220
+ }
221
+ function onPause(callback) {
222
+ return addLifecycleListener("oasiz:pause", callback);
223
+ }
224
+ function onResume(callback) {
225
+ return addLifecycleListener("oasiz:resume", callback);
226
+ }
227
+
228
+ // src/index.ts
229
+ var oasiz = {
230
+ submitScore,
231
+ triggerHaptic,
232
+ loadGameState,
233
+ saveGameState,
234
+ flushGameState,
235
+ shareRoomCode,
236
+ onPause,
237
+ onResume,
238
+ get gameId() {
239
+ return getGameId();
240
+ },
241
+ get roomCode() {
242
+ return getRoomCode();
243
+ },
244
+ get playerName() {
245
+ return getPlayerName();
246
+ },
247
+ get playerAvatar() {
248
+ return getPlayerAvatar();
249
+ }
250
+ };
251
+ return __toCommonJS(index_exports);
252
+ })();
package/dist/index.js CHANGED
@@ -95,14 +95,6 @@ function submitScore(score) {
95
95
  }
96
96
  warnMissingBridge("submitScore");
97
97
  }
98
- function emitScoreConfig(config) {
99
- const bridge = getBridgeWindow3();
100
- if (typeof bridge?.emitScoreConfig === "function") {
101
- bridge.emitScoreConfig(config);
102
- return;
103
- }
104
- warnMissingBridge("emitScoreConfig");
105
- }
106
98
 
107
99
  // src/state.ts
108
100
  function isDevelopment4() {
@@ -196,9 +188,8 @@ function onResume(callback) {
196
188
  }
197
189
 
198
190
  // src/index.ts
199
- var oasis = {
191
+ var oasiz = {
200
192
  submitScore,
201
- emitScoreConfig,
202
193
  triggerHaptic,
203
194
  loadGameState,
204
195
  saveGameState,
@@ -220,14 +211,13 @@ var oasis = {
220
211
  }
221
212
  };
222
213
  export {
223
- emitScoreConfig,
224
214
  flushGameState,
225
215
  getGameId,
226
216
  getPlayerAvatar,
227
217
  getPlayerName,
228
218
  getRoomCode,
229
219
  loadGameState,
230
- oasis,
220
+ oasiz,
231
221
  onPause,
232
222
  onResume,
233
223
  saveGameState,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oasiz/sdk",
3
- "version": "0.1.0",
3
+ "version": "1.0.1",
4
4
  "description": "Typed SDK for Oasiz game platform bridge APIs.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -10,7 +10,8 @@
10
10
  ".": {
11
11
  "types": "./dist/index.d.ts",
12
12
  "import": "./dist/index.js",
13
- "require": "./dist/index.cjs"
13
+ "require": "./dist/index.cjs",
14
+ "script": "./dist/index.global.js"
14
15
  }
15
16
  },
16
17
  "files": [
@@ -18,9 +19,11 @@
18
19
  ],
19
20
  "sideEffects": false,
20
21
  "scripts": {
21
- "build": "tsup src/index.ts --format esm,cjs --dts",
22
+ "build": "tsup src/index.ts --format esm,cjs,iife --global-name oasiz --dts",
22
23
  "prepack": "bun run build",
23
- "test": "node --experimental-strip-types --test ./tests/**/*.test.ts"
24
+ "test": "node --experimental-strip-types --test ./tests/**/*.test.ts",
25
+ "docs": "typedoc",
26
+ "docs:watch": "typedoc --watch"
24
27
  },
25
28
  "publishConfig": {
26
29
  "access": "public"
@@ -28,12 +31,26 @@
28
31
  "release": {
29
32
  "branches": [
30
33
  "main"
34
+ ],
35
+ "plugins": [
36
+ "@semantic-release/commit-analyzer",
37
+ "@semantic-release/release-notes-generator",
38
+ "@semantic-release/github",
39
+ [
40
+ "@semantic-release/exec",
41
+ {
42
+ "prepareCmd": "npm version ${nextRelease.version} --no-git-tag-version --allow-same-version --no-workspaces",
43
+ "publishCmd": "npm publish --access public --no-workspaces"
44
+ }
45
+ ]
31
46
  ]
32
47
  },
33
48
  "devDependencies": {
49
+ "@semantic-release/exec": "^7.1.0",
34
50
  "@types/node": "^25.3.0",
35
51
  "semantic-release": "^25.0.3",
36
52
  "tsup": "^8.5.1",
53
+ "typedoc": "^0.28.17",
37
54
  "typescript": "^5.9.3"
38
55
  }
39
56
  }