@mpgd/adapter-devvit 0.14.11 → 0.14.12

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.
@@ -1,4 +1,4 @@
1
- import type { BridgeNotificationsBackendMethod } from '@mpgd/bridge';
1
+ import { type BridgeNotificationsBackendMethod } from '@mpgd/bridge';
2
2
  import type { NotificationRuntimeBackend, NotificationRuntimeSession } from '@mpgd/platform/notifications-runtime';
3
3
  /**
4
4
  * The webview side of the notifications runtime's backend on Devvit
@@ -1,3 +1,4 @@
1
+ import { isThrownInstance, readThrownField, } from '@mpgd/bridge';
1
2
  import { assertBridgeNotificationsBackendPayload, assertBridgeNotificationsBackendResult, notificationsBackendBridgeHostCodes, readBridgeNotificationsHostCode, } from '@mpgd/bridge/notifications';
2
3
  import { isNotificationErrorCode, NotificationError, notificationsProtocol, } from '@mpgd/platform/notifications';
3
4
  /** The default `timeoutMs`: a request still unanswered after this is no longer awaited. */
@@ -279,12 +280,15 @@ export function createDevvitNotificationsBackendClient(input) {
279
280
  };
280
281
  }
281
282
  function toNotificationError(method, error) {
282
- if (error instanceof NotificationError) {
283
+ if (isThrownInstance(error, NotificationError)) {
283
284
  return error;
284
285
  }
285
- const failure = typeof error === 'object' && error !== null
286
- ? error
287
- : {};
286
+ // Each field read once; one whose getter or Proxy trap throws counts as missing.
287
+ const failure = {
288
+ code: readThrownField(error, 'code'),
289
+ message: readThrownField(error, 'message'),
290
+ retryable: readThrownField(error, 'retryable'),
291
+ };
288
292
  const message = typeof failure.message === 'string' ? failure.message : undefined;
289
293
  if (backendUnavailableCodes.has(failure.code)) {
290
294
  return new NotificationError('NOTIFICATIONS_CONFIGURATION_REQUIRED', {
@@ -1,4 +1,4 @@
1
- import type { BridgeNotificationsV2Method } from '@mpgd/bridge';
1
+ import { type BridgeNotificationsV2Method } from '@mpgd/bridge';
2
2
  import type { NotificationSubscriptionAdapter } from '@mpgd/platform';
3
3
  import { type NotificationProvider } from '@mpgd/platform/notifications';
4
4
  /**
@@ -1,3 +1,4 @@
1
+ import { isThrownInstance, readThrownField } from '@mpgd/bridge';
1
2
  import { assertBridgeNotificationsV2Result, readBridgeNotificationsHostCode, } from '@mpgd/bridge/notifications';
2
3
  import { isNotificationErrorCode, isValidNotificationTimeZone, NotificationError, notificationsProtocol, toNotificationSubscriptionAdapter, } from '@mpgd/platform/notifications';
3
4
  import { devvitNotificationProviderId } from './notifications-contract.js';
@@ -109,12 +110,15 @@ function defaultLocale() {
109
110
  return typeof navigatorLike?.language === 'string' ? navigatorLike.language : undefined;
110
111
  }
111
112
  function toNotificationError(error) {
112
- if (error instanceof NotificationError) {
113
+ if (isThrownInstance(error, NotificationError)) {
113
114
  return error;
114
115
  }
115
- const failure = typeof error === 'object' && error !== null
116
- ? error
117
- : {};
116
+ // Each field read once; one whose getter or Proxy trap throws counts as missing.
117
+ const failure = {
118
+ code: readThrownField(error, 'code'),
119
+ message: readThrownField(error, 'message'),
120
+ retryable: readThrownField(error, 'retryable'),
121
+ };
118
122
  const message = typeof failure.message === 'string' ? failure.message : undefined;
119
123
  const explicit = typeof failure.retryable === 'boolean' ? { retryable: failure.retryable } : {};
120
124
  if (isNotificationErrorCode(failure.code)) {
@@ -1,4 +1,4 @@
1
- import type { BridgeRequest, BridgeResponse } from '@mpgd/bridge';
1
+ import { type BridgeRequest, type BridgeResponse } from '@mpgd/bridge';
2
2
  import { type NotificationService } from '@mpgd/game-services/notifications';
3
3
  import { type DevvitNotificationSessionContext } from './notifications-delivery.js';
4
4
  /**
@@ -1,3 +1,4 @@
1
+ import { isThrownInstance } from '@mpgd/bridge';
1
2
  import { createNotificationsBackendBridgeHandler } from '@mpgd/bridge/notifications';
2
3
  import { NotificationError } from '@mpgd/platform/notifications';
3
4
  import { NotificationConsentRateLimitError, } from '@mpgd/game-services/notifications';
@@ -65,8 +66,8 @@ function warnBackendFailure(error, context) {
65
66
  // A throttled consent write is the player's own cap, and an input the
66
67
  // service refuses is the caller's: neither is a server failure, and a
67
68
  // webview could otherwise fill the log with them.
68
- if (error instanceof NotificationConsentRateLimitError
69
- || (error instanceof NotificationError && error.code === 'NOTIFICATIONS_INVALID_INPUT')) {
69
+ if (isThrownInstance(error, NotificationConsentRateLimitError)
70
+ || (isThrownInstance(error, NotificationError) && error.code === 'NOTIFICATIONS_INVALID_INPUT')) {
70
71
  return;
71
72
  }
72
73
  console.warn(`devvit notifications ${context.method} failed`, error);
@@ -1,4 +1,4 @@
1
- import type { BridgeRequest, BridgeResponse } from '@mpgd/bridge';
1
+ import { type BridgeRequest, type BridgeResponse } from '@mpgd/bridge';
2
2
  import { type NotificationProvider } from '@mpgd/platform/notifications';
3
3
  /**
4
4
  * The game-rendered consent of the Devvit profile (notifications v2 RFC,
@@ -1,3 +1,4 @@
1
+ import { isThrownInstance } from '@mpgd/bridge';
1
2
  import { createNotificationsV2BridgeHandler } from '@mpgd/bridge/notifications';
2
3
  import { NotificationError, } from '@mpgd/platform/notifications';
3
4
  import { createDevvitNotificationCapabilities, devvitNotificationProviderId, devvitUserIdPattern, } from '../notifications-contract.js';
@@ -177,7 +178,7 @@ export function createDevvitNotificationsV2BridgeHandler(input) {
177
178
  // An unexpected failure's text stays on the server.
178
179
  unexpectedFailureMessage: (method) => `${method} failed on the Devvit server.`,
179
180
  onError: input.onError ?? ((error, context) => {
180
- if (error instanceof NotificationError && error.code === 'NOTIFICATIONS_UNSUPPORTED') {
181
+ if (isThrownInstance(error, NotificationError) && error.code === 'NOTIFICATIONS_UNSUPPORTED') {
181
182
  return;
182
183
  }
183
184
  console.warn(`devvit notifications ${context.method} failed`, error);
@@ -1,3 +1,4 @@
1
+ import { thrownMessage } from '@mpgd/bridge';
1
2
  import { createNotificationProviderRegistry, createNotificationService, createNotificationServiceCatalog, } from '@mpgd/game-services/notifications';
2
3
  import { referenceNotificationProviders, } from '@mpgd/platform/notifications';
3
4
  import { devvitNotificationBodyMaxLength, devvitNotificationLinkPattern, devvitNotificationProviderId, devvitNotificationsPerAppPerDay, devvitNotificationsPerUserPerDay, devvitNotificationTarget, devvitNotificationTextLength, devvitNotificationTitleMaxLength, devvitUserIdPattern, } from '../notifications-contract.js';
@@ -183,7 +184,7 @@ export function createDevvitNotificationDeliveryProvider(input) {
183
184
  }
184
185
  catch (error) {
185
186
  // A failing classifier keeps the default answer; the report says so.
186
- note = ` (classifyEnqueueError threw: ${error instanceof Error ? error.message : String(error)})`;
187
+ note = ` (classifyEnqueueError threw: ${thrownMessage(error)})`;
187
188
  }
188
189
  // The provider code follows the final answer, after any override; a
189
190
  // thrown call keeps DEVVIT_ENQUEUE_FAILED, which names its source.
@@ -223,7 +224,7 @@ export function createDevvitNotificationDeliveryProvider(input) {
223
224
  });
224
225
  }
225
226
  catch (error) {
226
- return failure('thrown', error instanceof Error ? error.message : String(error), 'ambiguous');
227
+ return failure('thrown', thrownMessage(error), 'ambiguous');
227
228
  }
228
229
  if (typeof response?.successCount === 'number' && response.successCount > 0) {
229
230
  return { status: 'accepted' };
@@ -1,3 +1,4 @@
1
+ import { isThrownInstance, readThrownField } from '@mpgd/bridge';
1
2
  import { formatNotificationInstant, notificationSendDueAt, } from '@mpgd/game-services/notifications';
2
3
  import { createHash } from 'node:crypto';
3
4
  export const defaultDevvitNotificationNamespace = 'mpgd:notifications';
@@ -1084,7 +1085,9 @@ const permanentRedisError = /not allowed|allow-?list|unknown command|WRONGTYPE|u
1084
1085
  const connectionLoss = /\b(?:econnreset|econnrefused|socket hang up|temporarily unavailable|resource[_ ]exhausted|try again later)\b/iu;
1085
1086
  const transactionAbort = /\b(?:timed?\s*out|timeout|deadline exceeded)\b/iu;
1086
1087
  function redisThrowKind(error) {
1087
- if (error instanceof TypeError || error instanceof RangeError || error instanceof SyntaxError) {
1088
+ if (isThrownInstance(error, TypeError)
1089
+ || isThrownInstance(error, RangeError)
1090
+ || isThrownInstance(error, SyntaxError)) {
1088
1091
  return 'permanent';
1089
1092
  }
1090
1093
  const text = redisErrorText(error);
@@ -1110,8 +1113,8 @@ function redisErrorText(error) {
1110
1113
  if (typeof error !== 'object' || error === null) {
1111
1114
  return '';
1112
1115
  }
1113
- const record = error;
1114
- return [record.message, record.code, record.msg]
1116
+ // A field whose getter or Proxy trap throws counts as missing.
1117
+ return [readThrownField(error, 'message'), readThrownField(error, 'code'), readThrownField(error, 'msg')]
1115
1118
  .filter((part) => typeof part === 'string')
1116
1119
  .join(' ');
1117
1120
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpgd/adapter-devvit",
3
- "version": "0.14.11",
3
+ "version": "0.14.12",
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.15.0",
58
- "@mpgd/bridge": "0.18.1",
59
- "@mpgd/game-services": "0.33.1",
60
- "@mpgd/platform": "0.35.0"
58
+ "@mpgd/platform": "0.35.1",
59
+ "@mpgd/game-services": "0.34.0",
60
+ "@mpgd/bridge": "0.19.0"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@types/node": "^24.0.0",