@mpgd/adapter-devvit 0.8.2 → 0.8.4

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.
Files changed (2) hide show
  1. package/dist/index.js +25 -173
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -1,8 +1,7 @@
1
- import { createBridgeError, } from '@mpgd/bridge';
1
+ import { bridgeStorageLoadProtocol, createBridgeError, decodeBridgeStorageLoadData, } from '@mpgd/bridge';
2
2
  import { createBridgeOrpcClient, defaultBridgeRpcEndpoint, } from '@mpgd/bridge/orpc';
3
3
  export * from './payments.js';
4
4
  export const defaultDevvitRpcEndpoint = defaultBridgeRpcEndpoint;
5
- const devvitFallbackStoragePrefix = 'mpgd:devvit:fallback:';
6
5
  export class DevvitBridgeError extends Error {
7
6
  code;
8
7
  requestId;
@@ -16,7 +15,6 @@ export class DevvitBridgeError extends Error {
16
15
  }
17
16
  }
18
17
  export function createDevvitPlatformGateway(input) {
19
- let lastResolvedStorageFallbackNamespace;
20
18
  async function request(method, payload) {
21
19
  const bridge = input.bridge ??
22
20
  getBridge() ??
@@ -40,18 +38,12 @@ export function createDevvitPlatformGateway(input) {
40
38
  }
41
39
  return {
42
40
  target: 'reddit',
43
- getCapabilities: () => request('runtime.getCapabilities', {}),
41
+ async getCapabilities() {
42
+ return { ...await request('runtime.getCapabilities', {}) };
43
+ },
44
44
  identity: {
45
- getPlayer: async () => {
46
- const player = await request('identity.getPlayer', {});
47
- rememberStorageFallbackNamespace(player);
48
- return player;
49
- },
50
- getSession: async () => {
51
- const session = await request('identity.getSession', {});
52
- rememberStorageFallbackNamespace(session.playerId === undefined ? null : { playerId: session.playerId });
53
- return session;
54
- },
45
+ getPlayer: () => request('identity.getPlayer', {}),
46
+ getSession: () => request('identity.getSession', {}),
55
47
  requestUpgrade: (payload) => request('identity.requestUpgrade', payload),
56
48
  },
57
49
  presentation: {
@@ -91,100 +83,16 @@ export function createDevvitPlatformGateway(input) {
91
83
  },
92
84
  storage: {
93
85
  async load(payload) {
94
- let value;
95
- try {
96
- value = await request('storage.load', payload);
97
- }
98
- catch (error) {
99
- const fallback = await loadStorageFallback(payload.key, {
100
- useCachedNamespaceOnFailure: true,
101
- });
102
- if (fallback !== null) {
103
- return fallback;
104
- }
105
- throw error;
106
- }
107
- const fallback = await loadStorageFallback(payload.key, {
108
- useCachedNamespaceOnFailure: value === null,
109
- });
110
- return fallback ?? (value === null ? null : { value });
86
+ return decodeBridgeStorageLoadData(await request('storage.load', payload));
111
87
  },
112
88
  async save(payload) {
113
- let result;
114
- try {
115
- result = await request('storage.save', payload);
116
- }
117
- catch (error) {
118
- if (await saveStorageFallback(payload, undefined, {
119
- useCachedNamespaceOnFailure: true,
120
- })) {
121
- return;
122
- }
123
- throw error;
124
- }
89
+ const result = await request('storage.save', payload);
125
90
  if (result.saved !== true) {
126
- if (await saveStorageFallback(payload, result.playerId, {
127
- useCachedNamespaceOnFailure: false,
128
- })) {
129
- return;
130
- }
131
- throw new Error('Devvit storage save was not persisted and local fallback storage is unavailable.');
91
+ throw new Error('Devvit storage save was not persisted.');
132
92
  }
133
- await removeStorageFallback(payload.key, result.playerId);
134
93
  },
135
94
  },
136
95
  };
137
- async function loadStorageFallback(key, options) {
138
- if (!hasDevvitStorageFallbackCandidate(key, input.appVersion)) {
139
- return null;
140
- }
141
- const namespace = await resolveNamespace(undefined, options);
142
- return namespace === undefined ? null : loadDevvitStorageFallback(key, namespace);
143
- }
144
- async function saveStorageFallback(payload, playerId, options) {
145
- const namespace = await resolveNamespace(playerId, options);
146
- return namespace === undefined ? false : saveDevvitStorageFallback(payload, namespace);
147
- }
148
- async function removeStorageFallback(key, playerId) {
149
- const namespace = await resolveNamespace(playerId, {
150
- useCachedNamespaceOnFailure: true,
151
- });
152
- if (namespace === undefined) {
153
- console.warn(`devvit storage fallback cleanup skipped due to unresolved namespace for key: ${key}`);
154
- }
155
- removeDevvitStorageFallback(key, namespace);
156
- }
157
- async function resolveNamespace(playerId, options) {
158
- const namespace = storageFallbackNamespaceFromPlayerId(playerId);
159
- if (namespace !== undefined) {
160
- lastResolvedStorageFallbackNamespace = namespace;
161
- return namespace;
162
- }
163
- try {
164
- return await resolveStorageFallbackNamespace();
165
- }
166
- catch (error) {
167
- console.warn(`devvit storage fallback namespace resolution failed: ${errorMessage(error)}`);
168
- return options.useCachedNamespaceOnFailure ? lastResolvedStorageFallbackNamespace : undefined;
169
- }
170
- }
171
- async function resolveStorageFallbackNamespace() {
172
- const player = await request('identity.getPlayer', {});
173
- return rememberStorageFallbackNamespace(player);
174
- }
175
- function rememberStorageFallbackNamespace(player) {
176
- const playerId = player !== null && typeof player.playerId === 'string' && player.playerId.length > 0
177
- ? player.playerId
178
- : 'anonymous';
179
- const namespace = devvitStorageFallbackNamespace(input.appVersion, playerId);
180
- lastResolvedStorageFallbackNamespace = namespace;
181
- return namespace;
182
- }
183
- function storageFallbackNamespaceFromPlayerId(playerId) {
184
- return playerId !== undefined && playerId.length > 0
185
- ? devvitStorageFallbackNamespace(input.appVersion, playerId)
186
- : undefined;
187
- }
188
96
  }
189
97
  export function createDevvitOrpcBridge(input = {}) {
190
98
  const client = input.client ?? createBridgeOrpcClient({
@@ -305,13 +213,20 @@ export function createDevvitSandboxBridge() {
305
213
  case 'storage.load': {
306
214
  const payload = optionalObjectPayload(input.payload);
307
215
  const key = typeof payload.key === 'string' ? payload.key : undefined;
308
- return ok(input, key === undefined ? null : (storage.get(key) ?? null));
216
+ const value = key === undefined ? undefined : storage.get(key);
217
+ return ok(input, value === undefined
218
+ ? { __mpgdBridgeProtocol: bridgeStorageLoadProtocol, found: false }
219
+ : {
220
+ __mpgdBridgeProtocol: bridgeStorageLoadProtocol,
221
+ found: true,
222
+ value: cloneJsonValue(value),
223
+ });
309
224
  }
310
225
  case 'storage.save': {
311
226
  const payload = optionalObjectPayload(input.payload);
312
227
  const key = typeof payload.key === 'string' ? payload.key : undefined;
313
228
  if (key !== undefined) {
314
- storage.set(key, payload.value);
229
+ storage.set(key, cloneJsonValue(payload.value));
315
230
  }
316
231
  return ok(input, {
317
232
  saved: key !== undefined,
@@ -323,6 +238,13 @@ export function createDevvitSandboxBridge() {
323
238
  },
324
239
  };
325
240
  }
241
+ function cloneJsonValue(value) {
242
+ const serialized = JSON.stringify(value);
243
+ if (serialized === undefined) {
244
+ throw new TypeError('Storage values must be JSON-serializable.');
245
+ }
246
+ return JSON.parse(serialized);
247
+ }
326
248
  function optionalObjectPayload(payload) {
327
249
  if (typeof payload !== 'object' || payload === null) {
328
250
  return {};
@@ -336,73 +258,3 @@ function ok(input, data) {
336
258
  data,
337
259
  };
338
260
  }
339
- function loadDevvitStorageFallback(key, namespace) {
340
- const storage = browserLocalStorage();
341
- if (storage === undefined) {
342
- return null;
343
- }
344
- const fallbackKey = devvitStorageFallbackKey(key, namespace);
345
- const stored = storage.getItem(fallbackKey);
346
- if (stored === null) {
347
- return null;
348
- }
349
- try {
350
- return {
351
- value: JSON.parse(stored),
352
- };
353
- }
354
- catch {
355
- storage.removeItem(fallbackKey);
356
- return null;
357
- }
358
- }
359
- function saveDevvitStorageFallback(input, namespace) {
360
- const storage = browserLocalStorage();
361
- if (storage === undefined) {
362
- return false;
363
- }
364
- try {
365
- storage.setItem(devvitStorageFallbackKey(input.key, namespace), JSON.stringify(input.value));
366
- return true;
367
- }
368
- catch {
369
- return false;
370
- }
371
- }
372
- function removeDevvitStorageFallback(key, namespace) {
373
- const storage = browserLocalStorage();
374
- if (storage !== undefined) {
375
- if (namespace !== undefined) {
376
- storage.removeItem(devvitStorageFallbackKey(key, namespace));
377
- }
378
- }
379
- }
380
- function hasDevvitStorageFallbackCandidate(key, appVersion) {
381
- const storage = browserLocalStorage();
382
- if (storage === undefined) {
383
- return false;
384
- }
385
- const prefix = `${devvitFallbackStoragePrefix}${encodeURIComponent(appVersion)}:`;
386
- const suffix = `:${encodeURIComponent(key)}`;
387
- for (let index = 0; index < storage.length; index += 1) {
388
- const candidate = storage.key(index);
389
- if (candidate?.startsWith(prefix) === true && candidate.endsWith(suffix)) {
390
- return true;
391
- }
392
- }
393
- return false;
394
- }
395
- function devvitStorageFallbackNamespace(appVersion, playerId) {
396
- return `${encodeURIComponent(appVersion)}:${encodeURIComponent(playerId)}`;
397
- }
398
- function devvitStorageFallbackKey(key, namespace) {
399
- return `${devvitFallbackStoragePrefix}${namespace}:${encodeURIComponent(key)}`;
400
- }
401
- function browserLocalStorage() {
402
- try {
403
- return globalThis.localStorage;
404
- }
405
- catch {
406
- return undefined;
407
- }
408
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpgd/adapter-devvit",
3
- "version": "0.8.2",
3
+ "version": "0.8.4",
4
4
  "description": "Reddit Devvit Web platform adapter for mpgd games.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -55,9 +55,9 @@
55
55
  ],
56
56
  "dependencies": {
57
57
  "@devvit/web": "0.13.8",
58
- "@mpgd/bridge": "0.6.0",
59
- "@mpgd/game-services": "0.8.2",
60
- "@mpgd/platform": "0.6.0"
58
+ "@mpgd/game-services": "0.10.0",
59
+ "@mpgd/platform": "0.7.0",
60
+ "@mpgd/bridge": "0.6.1"
61
61
  },
62
62
  "devDependencies": {
63
63
  "ttsc": "0.18.4",