@buoy-gg/notifications 7.0.41 → 7.0.43

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 (71) hide show
  1. package/lib/commonjs/components/LocalNotificationTest.js +1 -185
  2. package/lib/commonjs/components/NotificationTestView.js +1 -329
  3. package/lib/commonjs/components/NotificationToolView.js +1 -673
  4. package/lib/commonjs/components/notificationStyles.js +1 -292
  5. package/lib/commonjs/expo.js +1 -247
  6. package/lib/commonjs/index.js +1 -148
  7. package/lib/commonjs/json.js +1 -58
  8. package/lib/commonjs/payload.js +1 -68
  9. package/lib/commonjs/preset.js +1 -56
  10. package/lib/commonjs/preview.js +1 -18
  11. package/lib/commonjs/recorder.js +1 -339
  12. package/lib/commonjs/runtime.js +1 -110
  13. package/lib/commonjs/storage.js +1 -61
  14. package/lib/commonjs/types.js +0 -1
  15. package/lib/module/components/LocalNotificationTest.js +1 -180
  16. package/lib/module/components/NotificationTestView.js +1 -324
  17. package/lib/module/components/NotificationToolView.js +1 -668
  18. package/lib/module/components/notificationStyles.js +1 -288
  19. package/lib/module/expo.js +1 -241
  20. package/lib/module/index.js +1 -13
  21. package/lib/module/json.js +1 -51
  22. package/lib/module/payload.js +1 -63
  23. package/lib/module/preset.js +1 -50
  24. package/lib/module/preview.js +1 -14
  25. package/lib/module/recorder.js +1 -332
  26. package/lib/module/runtime.js +1 -103
  27. package/lib/module/storage.js +1 -56
  28. package/lib/module/types.js +0 -1
  29. package/package.json +2 -2
  30. package/lib/commonjs/components/LocalNotificationTest.js.map +0 -1
  31. package/lib/commonjs/components/NotificationTestView.js.map +0 -1
  32. package/lib/commonjs/components/NotificationToolView.js.map +0 -1
  33. package/lib/commonjs/components/notificationStyles.js.map +0 -1
  34. package/lib/commonjs/expo.js.map +0 -1
  35. package/lib/commonjs/index.js.map +0 -1
  36. package/lib/commonjs/json.js.map +0 -1
  37. package/lib/commonjs/payload.js.map +0 -1
  38. package/lib/commonjs/preset.js.map +0 -1
  39. package/lib/commonjs/preview.js.map +0 -1
  40. package/lib/commonjs/recorder.js.map +0 -1
  41. package/lib/commonjs/runtime.js.map +0 -1
  42. package/lib/commonjs/storage.js.map +0 -1
  43. package/lib/commonjs/types.js.map +0 -1
  44. package/lib/module/components/LocalNotificationTest.js.map +0 -1
  45. package/lib/module/components/NotificationTestView.js.map +0 -1
  46. package/lib/module/components/NotificationToolView.js.map +0 -1
  47. package/lib/module/components/notificationStyles.js.map +0 -1
  48. package/lib/module/expo.js.map +0 -1
  49. package/lib/module/index.js.map +0 -1
  50. package/lib/module/json.js.map +0 -1
  51. package/lib/module/payload.js.map +0 -1
  52. package/lib/module/preset.js.map +0 -1
  53. package/lib/module/preview.js.map +0 -1
  54. package/lib/module/recorder.js.map +0 -1
  55. package/lib/module/runtime.js.map +0 -1
  56. package/lib/module/storage.js.map +0 -1
  57. package/lib/module/types.js.map +0 -1
  58. package/lib/typescript/components/LocalNotificationTest.d.ts.map +0 -1
  59. package/lib/typescript/components/NotificationTestView.d.ts.map +0 -1
  60. package/lib/typescript/components/NotificationToolView.d.ts.map +0 -1
  61. package/lib/typescript/components/notificationStyles.d.ts.map +0 -1
  62. package/lib/typescript/expo.d.ts.map +0 -1
  63. package/lib/typescript/index.d.ts.map +0 -1
  64. package/lib/typescript/json.d.ts.map +0 -1
  65. package/lib/typescript/payload.d.ts.map +0 -1
  66. package/lib/typescript/preset.d.ts.map +0 -1
  67. package/lib/typescript/preview.d.ts.map +0 -1
  68. package/lib/typescript/recorder.d.ts.map +0 -1
  69. package/lib/typescript/runtime.d.ts.map +0 -1
  70. package/lib/typescript/storage.d.ts.map +0 -1
  71. package/lib/typescript/types.d.ts.map +0 -1
@@ -1,288 +1 @@
1
- "use strict";
2
-
3
- import { StyleSheet } from "react-native";
4
- import { monoFont, night } from "@buoy-gg/shared-ui";
5
- export const styles = StyleSheet.create({
6
- root: {
7
- flex: 1,
8
- minHeight: 0,
9
- backgroundColor: "transparent"
10
- },
11
- fill: {
12
- flex: 1,
13
- minHeight: 0
14
- },
15
- inlineHeader: {
16
- paddingHorizontal: 12,
17
- paddingVertical: 8,
18
- borderBottomWidth: 1,
19
- borderBottomColor: night.color.separator
20
- },
21
- status: {
22
- flexDirection: "row",
23
- alignItems: "center",
24
- flexWrap: "wrap",
25
- gap: 12,
26
- paddingHorizontal: 16,
27
- paddingVertical: 9
28
- },
29
- statusLabel: {
30
- flexDirection: "row",
31
- alignItems: "center",
32
- gap: 6
33
- },
34
- dot: {
35
- width: 5,
36
- height: 5,
37
- borderRadius: 3,
38
- backgroundColor: night.color.textTertiary
39
- },
40
- liveDot: {
41
- backgroundColor: night.color.accent
42
- },
43
- caption: {
44
- color: night.color.textSecondary,
45
- fontSize: 12,
46
- lineHeight: 17
47
- },
48
- provider: {
49
- color: night.color.textSecondary,
50
- fontSize: 11,
51
- marginLeft: "auto",
52
- fontFamily: monoFont
53
- },
54
- content: {
55
- padding: 14,
56
- gap: 20
57
- },
58
- eventList: {
59
- paddingHorizontal: 12,
60
- paddingTop: 2
61
- },
62
- searchRow: {
63
- flexDirection: "row",
64
- gap: 8,
65
- paddingHorizontal: 12,
66
- paddingBottom: 10,
67
- alignItems: "center"
68
- },
69
- searchField: {
70
- flex: 1,
71
- minWidth: 0,
72
- height: 36,
73
- flexDirection: "row",
74
- alignItems: "center",
75
- gap: 8,
76
- paddingHorizontal: 10,
77
- backgroundColor: night.color.glass,
78
- borderWidth: 1,
79
- borderColor: night.color.glassBorder,
80
- borderRadius: night.radius.row
81
- },
82
- searchInput: {
83
- flex: 1,
84
- minWidth: 0,
85
- height: "100%",
86
- color: night.color.text,
87
- fontSize: 13,
88
- padding: 0
89
- },
90
- clearSearch: {
91
- padding: 6
92
- },
93
- iconButton: {
94
- width: 34,
95
- height: 34,
96
- alignItems: "center",
97
- justifyContent: "center",
98
- backgroundColor: night.color.buttonSurface,
99
- borderRadius: 8,
100
- borderWidth: 1,
101
- borderColor: night.color.border
102
- },
103
- event: {
104
- backgroundColor: night.color.glass,
105
- borderRadius: night.radius.input,
106
- borderWidth: 1,
107
- borderColor: night.color.glassBorder,
108
- paddingHorizontal: 12,
109
- paddingVertical: 10,
110
- marginBottom: 6,
111
- gap: 6
112
- },
113
- eventTop: {
114
- flexDirection: "row",
115
- alignItems: "center",
116
- gap: 10,
117
- justifyContent: "space-between"
118
- },
119
- eventKind: {
120
- flexDirection: "row",
121
- alignItems: "center",
122
- gap: 6,
123
- flex: 1,
124
- minWidth: 0
125
- },
126
- eventLabel: {
127
- flexShrink: 1,
128
- fontSize: 11,
129
- fontWeight: "600"
130
- },
131
- timestamp: {
132
- color: night.color.textSecondary,
133
- fontFamily: monoFont,
134
- fontSize: 10
135
- },
136
- eventSubject: {
137
- flexDirection: "row",
138
- alignItems: "center",
139
- gap: 8
140
- },
141
- eventTitle: {
142
- flex: 1,
143
- color: night.color.text,
144
- fontSize: 15,
145
- fontWeight: "500"
146
- },
147
- eventMeta: {
148
- color: night.color.textSecondary,
149
- fontFamily: monoFont,
150
- fontSize: 10,
151
- lineHeight: 15
152
- },
153
- resultCount: {
154
- color: night.color.textSecondary,
155
- fontSize: 12,
156
- marginBottom: 10,
157
- marginLeft: 4
158
- },
159
- actionRow: {
160
- flexDirection: "row",
161
- alignItems: "center",
162
- flexWrap: "wrap",
163
- gap: 8
164
- },
165
- button: {
166
- flexDirection: "row",
167
- alignItems: "center",
168
- justifyContent: "center",
169
- gap: 6,
170
- minHeight: 34,
171
- paddingVertical: 8,
172
- paddingHorizontal: 14,
173
- borderRadius: night.radius.pill,
174
- backgroundColor: night.color.fillSecondary,
175
- alignSelf: "flex-start"
176
- },
177
- buttonText: {
178
- color: night.color.accent,
179
- fontSize: 13,
180
- fontWeight: "600"
181
- },
182
- primaryButton: {
183
- backgroundColor: night.color.accent
184
- },
185
- primaryButtonText: {
186
- color: night.color.onAccent
187
- },
188
- pressed: {
189
- opacity: 0.7
190
- },
191
- disabled: {
192
- opacity: 0.4
193
- },
194
- detailTitle: {
195
- color: night.color.text,
196
- fontSize: 20,
197
- fontWeight: "600"
198
- },
199
- field: {
200
- flexDirection: "row",
201
- paddingHorizontal: 16,
202
- paddingVertical: 12,
203
- gap: 14
204
- },
205
- fieldLabel: {
206
- width: 110,
207
- flexShrink: 0
208
- },
209
- value: {
210
- flex: 1,
211
- minWidth: 0,
212
- color: night.color.text,
213
- fontFamily: monoFont,
214
- fontSize: 12,
215
- lineHeight: 18
216
- },
217
- disclosure: {
218
- flexDirection: "row",
219
- alignItems: "center",
220
- justifyContent: "space-between",
221
- paddingHorizontal: 16,
222
- paddingVertical: 14
223
- },
224
- disclosureTitle: {
225
- color: night.color.text,
226
- fontSize: 13,
227
- fontWeight: "500"
228
- },
229
- jsonBody: {
230
- padding: 10,
231
- borderTopWidth: 1,
232
- borderTopColor: night.color.separator
233
- },
234
- token: {
235
- color: night.color.text,
236
- fontFamily: monoFont,
237
- fontSize: 12,
238
- lineHeight: 20
239
- },
240
- bodyText: {
241
- color: night.color.text,
242
- fontSize: 14,
243
- lineHeight: 21
244
- },
245
- footnote: {
246
- color: night.color.textSecondary,
247
- fontSize: 12,
248
- lineHeight: 18,
249
- marginTop: 8,
250
- marginHorizontal: 4
251
- },
252
- emptyFootnote: {
253
- color: night.color.textSecondary,
254
- fontSize: 12,
255
- lineHeight: 18,
256
- textAlign: "center",
257
- marginHorizontal: 28
258
- },
259
- sectionActions: {
260
- flexDirection: "row",
261
- marginBottom: 10
262
- },
263
- warning: {
264
- color: night.color.warning,
265
- fontSize: 12,
266
- lineHeight: 18
267
- },
268
- notice: {
269
- color: night.color.accent,
270
- backgroundColor: night.color.glass,
271
- paddingHorizontal: 16,
272
- paddingVertical: 10,
273
- fontSize: 12,
274
- lineHeight: 18
275
- },
276
- errorNotice: {
277
- color: night.color.danger,
278
- backgroundColor: night.color.glass,
279
- paddingHorizontal: 16,
280
- paddingVertical: 10,
281
- fontSize: 12,
282
- lineHeight: 18
283
- },
284
- errorText: {
285
- color: night.color.danger
286
- }
287
- });
288
- //# sourceMappingURL=notificationStyles.js.map
1
+ "use strict";import{StyleSheet as t}from"react-native";import{monoFont as e,night as o}from"@buoy-gg/shared-ui";export const styles=t.create({root:{flex:1,minHeight:0,backgroundColor:"transparent"},fill:{flex:1,minHeight:0},inlineHeader:{paddingHorizontal:12,paddingVertical:8,borderBottomWidth:1,borderBottomColor:o.color.separator},status:{flexDirection:"row",alignItems:"center",flexWrap:"wrap",gap:12,paddingHorizontal:16,paddingVertical:9},statusLabel:{flexDirection:"row",alignItems:"center",gap:6},dot:{width:5,height:5,borderRadius:3,backgroundColor:o.color.textTertiary},liveDot:{backgroundColor:o.color.accent},caption:{color:o.color.textSecondary,fontSize:12,lineHeight:17},provider:{color:o.color.textSecondary,fontSize:11,marginLeft:"auto",fontFamily:e},content:{padding:14,gap:20},eventList:{paddingHorizontal:12,paddingTop:2},searchRow:{flexDirection:"row",gap:8,paddingHorizontal:12,paddingBottom:10,alignItems:"center"},searchField:{flex:1,minWidth:0,height:36,flexDirection:"row",alignItems:"center",gap:8,paddingHorizontal:10,backgroundColor:o.color.glass,borderWidth:1,borderColor:o.color.glassBorder,borderRadius:o.radius.row},searchInput:{flex:1,minWidth:0,height:"100%",color:o.color.text,fontSize:13,padding:0},clearSearch:{padding:6},iconButton:{width:34,height:34,alignItems:"center",justifyContent:"center",backgroundColor:o.color.buttonSurface,borderRadius:8,borderWidth:1,borderColor:o.color.border},event:{backgroundColor:o.color.glass,borderRadius:o.radius.input,borderWidth:1,borderColor:o.color.glassBorder,paddingHorizontal:12,paddingVertical:10,marginBottom:6,gap:6},eventTop:{flexDirection:"row",alignItems:"center",gap:10,justifyContent:"space-between"},eventKind:{flexDirection:"row",alignItems:"center",gap:6,flex:1,minWidth:0},eventLabel:{flexShrink:1,fontSize:11,fontWeight:"600"},timestamp:{color:o.color.textSecondary,fontFamily:e,fontSize:10},eventSubject:{flexDirection:"row",alignItems:"center",gap:8},eventTitle:{flex:1,color:o.color.text,fontSize:15,fontWeight:"500"},eventMeta:{color:o.color.textSecondary,fontFamily:e,fontSize:10,lineHeight:15},resultCount:{color:o.color.textSecondary,fontSize:12,marginBottom:10,marginLeft:4},actionRow:{flexDirection:"row",alignItems:"center",flexWrap:"wrap",gap:8},button:{flexDirection:"row",alignItems:"center",justifyContent:"center",gap:6,minHeight:34,paddingVertical:8,paddingHorizontal:14,borderRadius:o.radius.pill,backgroundColor:o.color.fillSecondary,alignSelf:"flex-start"},buttonText:{color:o.color.accent,fontSize:13,fontWeight:"600"},primaryButton:{backgroundColor:o.color.accent},primaryButtonText:{color:o.color.onAccent},pressed:{opacity:.7},disabled:{opacity:.4},detailTitle:{color:o.color.text,fontSize:20,fontWeight:"600"},field:{flexDirection:"row",paddingHorizontal:16,paddingVertical:12,gap:14},fieldLabel:{width:110,flexShrink:0},value:{flex:1,minWidth:0,color:o.color.text,fontFamily:e,fontSize:12,lineHeight:18},disclosure:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",paddingHorizontal:16,paddingVertical:14},disclosureTitle:{color:o.color.text,fontSize:13,fontWeight:"500"},jsonBody:{padding:10,borderTopWidth:1,borderTopColor:o.color.separator},token:{color:o.color.text,fontFamily:e,fontSize:12,lineHeight:20},bodyText:{color:o.color.text,fontSize:14,lineHeight:21},footnote:{color:o.color.textSecondary,fontSize:12,lineHeight:18,marginTop:8,marginHorizontal:4},emptyFootnote:{color:o.color.textSecondary,fontSize:12,lineHeight:18,textAlign:"center",marginHorizontal:28},sectionActions:{flexDirection:"row",marginBottom:10},warning:{color:o.color.warning,fontSize:12,lineHeight:18},notice:{color:o.color.accent,backgroundColor:o.color.glass,paddingHorizontal:16,paddingVertical:10,fontSize:12,lineHeight:18},errorNotice:{color:o.color.danger,backgroundColor:o.color.glass,paddingHorizontal:16,paddingVertical:10,fontSize:12,lineHeight:18},errorText:{color:o.color.danger}});
@@ -1,241 +1 @@
1
- "use strict";
2
-
3
- import { normalizePreviewAppearance } from "./preview";
4
- import { expoMetadata, responseKey } from "./recorder";
5
- import { object, string } from "./json";
6
-
7
- // Structural interfaces let the app pass its installed SDK without a native dependency.
8
-
9
- function timeout(promise, ms = 15000) {
10
- return new Promise((resolve, reject) => {
11
- const timer = setTimeout(() => reject(new Error("Notification SDK timed out. Check permission and network settings, then retry.")), ms);
12
- promise.then(value => {
13
- clearTimeout(timer);
14
- resolve(value);
15
- }, error => {
16
- clearTimeout(timer);
17
- reject(error);
18
- });
19
- });
20
- }
21
-
22
- /** Does not replace presentation, routing or background handlers. */
23
- export function attachExpoNotifications(recorder, sdk, options) {
24
- const capabilities = ["received", "responses", "permissions", "device-token"];
25
- if (sdk.getLastNotificationResponse || sdk.getLastNotificationResponseAsync) capabilities.push("startup-response");
26
- if (sdk.getPresentedNotificationsAsync) capabilities.push("presented");
27
- if (sdk.scheduleNotificationAsync) capabilities.push("local-scheduling");
28
- if (options.projectId && sdk.getExpoPushTokenAsync) capabilities.push("expo-token");
29
- recorder.configure({
30
- preview: normalizePreviewAppearance(options.preview),
31
- provider: "expo",
32
- platform: options.platform,
33
- appId: options.appId ?? null,
34
- providerVersion: options.providerVersion ?? null,
35
- capabilities
36
- });
37
- let disposed = false;
38
- const subscriptions = [];
39
- const response = (value, startup = false) => {
40
- if (disposed || !value) return;
41
- void recorder.record(startup ? "startup-response" : "response", value, {
42
- ...expoMetadata(value),
43
- dedupeKey: responseKey(value)
44
- });
45
- };
46
- try {
47
- subscriptions.push(sdk.addNotificationReceivedListener(value => {
48
- if (!disposed) void recorder.record("received", value, expoMetadata(value));
49
- }));
50
- subscriptions.push(sdk.addNotificationResponseReceivedListener(value => response(value)));
51
- subscriptions.push(sdk.addPushTokenListener(token => {
52
- // Token acquisition here can trigger this listener recursively.
53
- if (!disposed && (token.type === "ios" || token.type === "android")) void recorder.observeToken(token.type === "ios" ? "apns" : "fcm", token.data).catch(() => {});
54
- }));
55
- } catch (error) {
56
- for (const subscription of subscriptions) subscription.remove();
57
- throw error;
58
- }
59
- const initial = Promise.resolve().then(() => sdk.getLastNotificationResponse?.() ?? sdk.getLastNotificationResponseAsync?.()).then(value => response(value, true));
60
- void initial.catch(() => {});
61
- const actions = {
62
- async getPermissions() {
63
- const permissions = await timeout(sdk.getPermissionsAsync());
64
- recorder.setPermissions(permissions);
65
- return recorder.getSnapshot().permissions;
66
- },
67
- async listPresented() {
68
- if (!sdk.getPresentedNotificationsAsync) throw new Error("This notification SDK cannot list presented notifications.");
69
- const presented = await timeout(sdk.getPresentedNotificationsAsync());
70
- recorder.setPresented(presented);
71
- return recorder.getSnapshot().presented;
72
- },
73
- async refreshToken(params) {
74
- const {
75
- type
76
- } = object(params);
77
- if (type === "expo") {
78
- if (!sdk.getExpoPushTokenAsync || !options.projectId) throw new Error("Set projectId in installExpoNotificationCapture before requesting an Expo token.");
79
- const token = await timeout(sdk.getExpoPushTokenAsync({
80
- projectId: options.projectId
81
- }));
82
- await recorder.observeToken("expo", token.data, {
83
- projectId: options.projectId
84
- });
85
- return {
86
- type: "expo",
87
- value: token.data
88
- };
89
- }
90
- if (type !== "device") throw new Error('Token type must be "device" or "expo".');
91
- if (!sdk.getDevicePushTokenAsync) throw new Error("This notification SDK cannot register a device token.");
92
- const token = await timeout(sdk.getDevicePushTokenAsync());
93
- if (token.type !== "ios" && token.type !== "android") throw new Error("This platform does not provide an APNs or FCM token.");
94
- await recorder.observeToken(token.type === "ios" ? "apns" : "fcm", token.data);
95
- return {
96
- type: token.type === "ios" ? "apns" : "fcm",
97
- value: token.data
98
- };
99
- },
100
- async scheduleLocal(params) {
101
- if (!sdk.scheduleNotificationAsync) throw new Error("This notification SDK cannot schedule local notifications.");
102
- const {
103
- title,
104
- body,
105
- data,
106
- seconds
107
- } = object(params);
108
- if (typeof title !== "string" || typeof body !== "string" || typeof seconds !== "number" || !Number.isFinite(seconds) || seconds < 1 || seconds > 3600) throw new Error("Provide title, body and a delay from 1 to 3600 seconds.");
109
- if (data !== undefined && (data === null || typeof data !== "object" || Array.isArray(data))) throw new Error("Local notification data must be an object.");
110
- const id = await timeout(sdk.scheduleNotificationAsync({
111
- content: {
112
- title,
113
- body,
114
- data: data ?? {}
115
- },
116
- trigger: {
117
- type: "timeInterval",
118
- seconds,
119
- repeats: false
120
- }
121
- }));
122
- await recorder.record("local-scheduled", {
123
- title,
124
- body,
125
- data,
126
- seconds
127
- }, {
128
- notificationId: id,
129
- title,
130
- testId: typeof object(data).__buoyTestId === "string" ? object(data).__buoyTestId : null
131
- });
132
- return {
133
- notificationId: id,
134
- transport: "local"
135
- };
136
- }
137
- };
138
- void actions.getPermissions().catch(() => {});
139
- return {
140
- actions,
141
- ready: initial,
142
- dispose() {
143
- disposed = true;
144
- for (const subscription of subscriptions) subscription.remove();
145
- }
146
- };
147
- }
148
-
149
- /** Wrap the handler the app already passes to Notifications.setNotificationHandler. */
150
- export function instrumentExpoNotificationHandler(recorder, handler) {
151
- const metadataById = new Map();
152
- return {
153
- ...handler,
154
- handleNotification: async function (...args) {
155
- const metadata = expoMetadata(args[0]);
156
- if (metadata.notificationId) metadataById.set(metadata.notificationId, metadata);
157
- if (metadataById.size > 50) metadataById.delete(metadataById.keys().next().value);
158
- try {
159
- const behavior = await handler.handleNotification.apply(this, args);
160
- void recorder.record("presentation-decision", {
161
- notification: args[0],
162
- behavior
163
- }, expoMetadata(args[0]));
164
- return behavior;
165
- } catch (error) {
166
- void recorder.record("presentation-error", {
167
- notification: args[0],
168
- error
169
- }, expoMetadata(args[0]));
170
- throw error;
171
- }
172
- },
173
- handleError: function (...args) {
174
- const identifier = string(args[0]);
175
- const metadata = identifier ? metadataById.get(identifier) : undefined;
176
- if (identifier) metadataById.delete(identifier);
177
- void recorder.record("presentation-error", {
178
- source: "sdk",
179
- identifier,
180
- error: args[1]
181
- }, metadata ?? {
182
- notificationId: identifier
183
- });
184
- return handler.handleError?.apply(this, args);
185
- }
186
- };
187
- }
188
-
189
- /** Expo's background task payload differs from its foreground Notification object. */
190
- export function expoBackgroundMetadata(value) {
191
- const metadata = expoMetadata(value);
192
- const raw = object(value);
193
- const data = object(raw.data);
194
- const launchPayload = object(data.UIApplicationLaunchOptionsRemoteNotificationKey);
195
- let parsed = {};
196
- if (typeof data.dataString === "string") {
197
- try {
198
- parsed = object(JSON.parse(data.dataString));
199
- } catch {/* Keep malformed data visible in the raw record. */}
200
- }
201
- const custom = object(data.body ?? raw.body ?? launchPayload.body);
202
- return {
203
- ...metadata,
204
- testId: metadata.testId ?? string(parsed.__buoyTestId) ?? string(custom.__buoyTestId),
205
- title: metadata.title ?? string(object(raw.notification).title) ?? string(object(object(raw.aps ?? launchPayload.aps).alert).title)
206
- };
207
- }
208
-
209
- /** Await local capture for at most 250 ms without extending the task for a desktop connection. */
210
- async function persistBounded(promise) {
211
- let timer;
212
- await Promise.race([promise.catch(() => {}), new Promise(resolve => {
213
- timer = setTimeout(resolve, 250);
214
- })]);
215
- if (timer) clearTimeout(timer);
216
- }
217
- export function instrumentNotificationBackgroundTask(recorder, task) {
218
- recorder.configure({
219
- capabilities: [...new Set([...recorder.getSnapshot().capabilities, "background-task"])]
220
- });
221
- return async function (...args) {
222
- const value = object(args[0]).data ?? args[0];
223
- const metadata = expoBackgroundMetadata(value);
224
- await persistBounded(recorder.record("background-started", args[0], metadata));
225
- try {
226
- const result = await task.apply(this, args);
227
- await persistBounded(recorder.record("background-completed", {
228
- input: args[0],
229
- result
230
- }, metadata));
231
- return result;
232
- } catch (error) {
233
- await persistBounded(recorder.record("background-error", {
234
- input: args[0],
235
- error
236
- }, metadata));
237
- throw error;
238
- }
239
- };
240
- }
241
- //# sourceMappingURL=expo.js.map
1
+ "use strict";import{normalizePreviewAppearance as g}from"./preview";import{expoMetadata as u,responseKey as T}from"./recorder";import{object as r,string as l}from"./json";function y(o,t=15e3){return new Promise((e,n)=>{const s=setTimeout(()=>n(new Error("Notification SDK timed out. Check permission and network settings, then retry.")),t);o.then(a=>{clearTimeout(s);e(a)},a=>{clearTimeout(s);n(a)})})}export function attachExpoNotifications(o,t,e){const n=["received","responses","permissions","device-token"];if(t.getLastNotificationResponse||t.getLastNotificationResponseAsync)n.push("startup-response");if(t.getPresentedNotificationsAsync)n.push("presented");if(t.scheduleNotificationAsync)n.push("local-scheduling");if(e.projectId&&t.getExpoPushTokenAsync)n.push("expo-token");o.configure({preview:g(e.preview),provider:"expo",platform:e.platform,appId:e.appId??null,providerVersion:e.providerVersion??null,capabilities:n});let s=false;const a=[];const h=(i,c=false)=>{if(s||!i)return;void o.record(c?"startup-response":"response",i,{...u(i),dedupeKey:T(i)})};try{a.push(t.addNotificationReceivedListener(i=>{if(!s)void o.record("received",i,u(i))}));a.push(t.addNotificationResponseReceivedListener(i=>h(i)));a.push(t.addPushTokenListener(i=>{if(!s&&(i.type==="ios"||i.type==="android"))void o.observeToken(i.type==="ios"?"apns":"fcm",i.data).catch(()=>{})}))}catch(i){for(const c of a)c.remove();throw i}const w=Promise.resolve().then(()=>t.getLastNotificationResponse?.()??t.getLastNotificationResponseAsync?.()).then(i=>h(i,true));void w.catch(()=>{});const v={async getPermissions(){const i=await y(t.getPermissionsAsync());o.setPermissions(i);return o.getSnapshot().permissions},async listPresented(){if(!t.getPresentedNotificationsAsync)throw new Error("This notification SDK cannot list presented notifications.");const i=await y(t.getPresentedNotificationsAsync());o.setPresented(i);return o.getSnapshot().presented},async refreshToken(i){const{type:c}=r(i);if(c==="expo"){if(!t.getExpoPushTokenAsync||!e.projectId)throw new Error("Set projectId in installExpoNotificationCapture before requesting an Expo token.");const p=await y(t.getExpoPushTokenAsync({projectId:e.projectId}));await o.observeToken("expo",p.data,{projectId:e.projectId});return{type:"expo",value:p.data}}if(c!=="device")throw new Error('Token type must be "device" or "expo".');if(!t.getDevicePushTokenAsync)throw new Error("This notification SDK cannot register a device token.");const d=await y(t.getDevicePushTokenAsync());if(d.type!=="ios"&&d.type!=="android")throw new Error("This platform does not provide an APNs or FCM token.");await o.observeToken(d.type==="ios"?"apns":"fcm",d.data);return{type:d.type==="ios"?"apns":"fcm",value:d.data}},async scheduleLocal(i){if(!t.scheduleNotificationAsync)throw new Error("This notification SDK cannot schedule local notifications.");const{title:c,body:d,data:p,seconds:f}=r(i);if(typeof c!=="string"||typeof d!=="string"||typeof f!=="number"||!Number.isFinite(f)||f<1||f>3600)throw new Error("Provide title, body and a delay from 1 to 3600 seconds.");if(p!==void 0&&(p===null||typeof p!=="object"||Array.isArray(p)))throw new Error("Local notification data must be an object.");const b=await y(t.scheduleNotificationAsync({content:{title:c,body:d,data:p??{}},trigger:{type:"timeInterval",seconds:f,repeats:false}}));await o.record("local-scheduled",{title:c,body:d,data:p,seconds:f},{notificationId:b,title:c,testId:typeof r(p).__buoyTestId==="string"?r(p).__buoyTestId:null});return{notificationId:b,transport:"local"}}};void v.getPermissions().catch(()=>{});return{actions:v,ready:w,dispose(){s=true;for(const i of a)i.remove()}}}export function instrumentExpoNotificationHandler(o,t){const e=new Map;return{...t,handleNotification:async function(...n){const s=u(n[0]);if(s.notificationId)e.set(s.notificationId,s);if(e.size>50)e.delete(e.keys().next().value);try{const a=await t.handleNotification.apply(this,n);void o.record("presentation-decision",{notification:n[0],behavior:a},u(n[0]));return a}catch(a){void o.record("presentation-error",{notification:n[0],error:a},u(n[0]));throw a}},handleError:function(...n){const s=l(n[0]);const a=s?e.get(s):void 0;if(s)e.delete(s);void o.record("presentation-error",{source:"sdk",identifier:s,error:n[1]},a??{notificationId:s});return t.handleError?.apply(this,n)}}}export function expoBackgroundMetadata(o){const t=u(o);const e=r(o);const n=r(e.data);const s=r(n.UIApplicationLaunchOptionsRemoteNotificationKey);let a={};if(typeof n.dataString==="string"){try{a=r(JSON.parse(n.dataString))}catch{}}const h=r(n.body??e.body??s.body);return{...t,testId:t.testId??l(a.__buoyTestId)??l(h.__buoyTestId),title:t.title??l(r(e.notification).title)??l(r(r(e.aps??s.aps).alert).title)}}async function m(o){let t;await Promise.race([o.catch(()=>{}),new Promise(e=>{t=setTimeout(e,250)})]);if(t)clearTimeout(t)}export function instrumentNotificationBackgroundTask(o,t){o.configure({capabilities:[...new Set([...o.getSnapshot().capabilities,"background-task"])]});return async function(...e){const n=r(e[0]).data??e[0];const s=expoBackgroundMetadata(n);await m(o.record("background-started",e[0],s));try{const a=await t.apply(this,e);await m(o.record("background-completed",{input:e[0],result:a},s));return a}catch(a){await m(o.record("background-error",{input:e[0],error:a},s));throw a}}}
@@ -1,13 +1 @@
1
- "use strict";
2
-
3
- export * from "./types";
4
- export { NotificationRecorder, expoMetadata } from "./recorder";
5
- export { attachExpoNotifications, instrumentExpoNotificationHandler, instrumentNotificationBackgroundTask } from "./expo";
6
- export { installExpoNotificationCapture, getNotificationRecorder, notificationSyncAdapter } from "./runtime";
7
- export { NotificationToolView } from "./components/NotificationToolView";
8
- export { notificationToolPreset, createNotificationTool, NotificationsModal } from "./preset";
9
- export { NotificationsIcon } from "@buoy-gg/shared-ui";
10
- export { composeSimulatorPayload, validateSimulatorPayload } from "./payload";
11
- export { createExpoNotificationStorage } from "./storage";
12
- export { NotificationTestView } from "./components/NotificationTestView";
13
- //# sourceMappingURL=index.js.map
1
+ "use strict";export*from"./types";export{NotificationRecorder,expoMetadata}from"./recorder";export{attachExpoNotifications,instrumentExpoNotificationHandler,instrumentNotificationBackgroundTask}from"./expo";export{installExpoNotificationCapture,getNotificationRecorder,notificationSyncAdapter}from"./runtime";export{NotificationToolView}from"./components/NotificationToolView";export{notificationToolPreset,createNotificationTool,NotificationsModal}from"./preset";export{NotificationsIcon}from"@buoy-gg/shared-ui";export{composeSimulatorPayload,validateSimulatorPayload}from"./payload";export{createExpoNotificationStorage}from"./storage";export{NotificationTestView}from"./components/NotificationTestView";
@@ -1,51 +1 @@
1
- "use strict";
2
-
3
- export function object(value) {
4
- return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
5
- }
6
- export function string(value) {
7
- return typeof value === "string" ? value : null;
8
- }
9
- export function json(value) {
10
- const seen = new WeakSet();
11
- function visit(v, depth) {
12
- if (v == null) return null;
13
- if (typeof v === "string") return v.length > 12000 ? v.slice(0, 12000) + " [truncated]" : v;
14
- if (typeof v === "boolean") return v;
15
- if (typeof v === "number") return Number.isFinite(v) ? v : String(v);
16
- if (typeof v !== "object") return String(v);
17
- if (seen.has(v)) return "[Circular]";
18
- if (depth > 12) return "[Depth limit]";
19
- seen.add(v);
20
- if (v instanceof Error) return {
21
- name: v.name,
22
- message: v.message
23
- };
24
- if (v instanceof Date) return Number.isNaN(v.getTime()) ? "[Invalid date]" : v.toISOString();
25
- if (Array.isArray(v)) return v.slice(0, 200).map(item => visit(item, depth + 1));
26
- const out = {};
27
- for (const key of Object.keys(v).slice(0, 200)) {
28
- try {
29
- Object.defineProperty(out, key, {
30
- value: visit(v[key], depth + 1),
31
- enumerable: true
32
- });
33
- } catch {
34
- out[key] = "[Unreadable]";
35
- }
36
- }
37
- return out;
38
- }
39
- return visit(value, 0);
40
- }
41
-
42
- /** UTF-8 size without requiring Buffer or TextEncoder in Hermes. */
43
- export function byteLength(value) {
44
- let bytes = 0;
45
- for (const character of value) {
46
- const cp = character.codePointAt(0);
47
- bytes += cp <= 0x7f ? 1 : cp <= 0x7ff ? 2 : cp <= 0xffff ? 3 : 4;
48
- }
49
- return bytes;
50
- }
51
- //# sourceMappingURL=json.js.map
1
+ "use strict";export function object(e){return e!==null&&typeof e==="object"&&!Array.isArray(e)?e:{}}export function string(e){return typeof e==="string"?e:null}export function json(e){const r=new WeakSet;function n(t,o){if(t==null)return null;if(typeof t==="string")return t.length>12e3?t.slice(0,12e3)+" [truncated]":t;if(typeof t==="boolean")return t;if(typeof t==="number")return Number.isFinite(t)?t:String(t);if(typeof t!=="object")return String(t);if(r.has(t))return"[Circular]";if(o>12)return"[Depth limit]";r.add(t);if(t instanceof Error)return{name:t.name,message:t.message};if(t instanceof Date)return Number.isNaN(t.getTime())?"[Invalid date]":t.toISOString();if(Array.isArray(t))return t.slice(0,200).map(i=>n(i,o+1));const f={};for(const i of Object.keys(t).slice(0,200)){try{Object.defineProperty(f,i,{value:n(t[i],o+1),enumerable:true})}catch{f[i]="[Unreadable]"}}return f}return n(e,0)}export function byteLength(e){let r=0;for(const n of e){const t=n.codePointAt(0);r+=t<=127?1:t<=2047?2:t<=65535?3:4}return r}
@@ -1,63 +1 @@
1
- "use strict";
2
-
3
- import { byteLength, object } from "./json";
4
- export function composeSimulatorPayload(draft) {
5
- const data = {
6
- ...draft.data,
7
- __buoyTestId: draft.testId
8
- };
9
- const aps = draft.silent ? {
10
- "content-available": 1
11
- } : {
12
- alert: {
13
- title: draft.title,
14
- body: draft.body
15
- },
16
- sound: "default"
17
- };
18
- return JSON.stringify(draft.adapter === "expo" ? {
19
- aps,
20
- body: data
21
- } : {
22
- ...data,
23
- aps
24
- }, null, 2);
25
- }
26
- export function validateSimulatorPayload(source) {
27
- const errors = [];
28
- const warnings = [];
29
- if (typeof source !== "string") return {
30
- valid: false,
31
- bytes: 0,
32
- errors: ["Provide the APNs payload as JSON text."],
33
- warnings,
34
- testId: null
35
- };
36
- const bytes = byteLength(source);
37
- if (bytes > 4096) errors.push(`Payload is ${bytes} bytes. APNs payloads must fit in 4096 UTF-8 bytes.`);
38
- let parsed;
39
- try {
40
- parsed = JSON.parse(source);
41
- } catch {
42
- errors.push("Payload is not valid JSON.");
43
- }
44
- const value = object(parsed);
45
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) errors.push("The payload must be a JSON object.");
46
- if (!value.aps || typeof value.aps !== "object" || Array.isArray(value.aps)) errors.push('The payload needs an "aps" object.');
47
- const aps = object(value.aps);
48
- if (aps.alert !== undefined && typeof aps.alert !== "string" && (aps.alert === null || typeof aps.alert !== "object" || Array.isArray(aps.alert))) errors.push("aps.alert must be text or an object.");
49
- if (aps.badge !== undefined && (!Number.isInteger(aps.badge) || Number(aps.badge) < 0)) errors.push("aps.badge must be a non-negative integer.");
50
- for (const key of ["content-available", "mutable-content"]) if (aps[key] !== undefined && aps[key] !== 1) errors.push(`aps.${key} must be 1 when present.`);
51
- if (aps["content-available"] === 1) warnings.push("Background execution depends on app configuration and OS scheduling. A successful send does not prove the task ran.");
52
- if (aps["mutable-content"] === 1) warnings.push("Use real APNs delivery to verify notification service extensions.");
53
- if (value.data !== undefined && value.body === undefined) warnings.push("Expo on iOS reads custom data from a top-level body object. This raw payload will be sent unchanged.");
54
- const testId = object(value.body).__buoyTestId ?? value.__buoyTestId;
55
- return {
56
- valid: errors.length === 0,
57
- bytes,
58
- errors,
59
- warnings,
60
- testId: typeof testId === "string" ? testId : null
61
- };
62
- }
63
- //# sourceMappingURL=payload.js.map
1
+ "use strict";import{byteLength as u,object as d}from"./json";export function composeSimulatorPayload(a){const e={...a.data,__buoyTestId:a.testId};const s=a.silent?{"content-available":1}:{alert:{title:a.title,body:a.body},sound:"default"};return JSON.stringify(a.adapter==="expo"?{aps:s,body:e}:{...e,aps:s},null,2)}export function validateSimulatorPayload(a){const e=[];const s=[];if(typeof a!=="string")return{valid:false,bytes:0,errors:["Provide the APNs payload as JSON text."],warnings:s,testId:null};const i=u(a);if(i>4096)e.push(`Payload is ${i} bytes. APNs payloads must fit in 4096 UTF-8 bytes.`);let o;try{o=JSON.parse(a)}catch{e.push("Payload is not valid JSON.")}const n=d(o);if(!o||typeof o!=="object"||Array.isArray(o))e.push("The payload must be a JSON object.");if(!n.aps||typeof n.aps!=="object"||Array.isArray(n.aps))e.push('The payload needs an "aps" object.');const t=d(n.aps);if(t.alert!==void 0&&typeof t.alert!=="string"&&(t.alert===null||typeof t.alert!=="object"||Array.isArray(t.alert)))e.push("aps.alert must be text or an object.");if(t.badge!==void 0&&(!Number.isInteger(t.badge)||Number(t.badge)<0))e.push("aps.badge must be a non-negative integer.");for(const r of["content-available","mutable-content"])if(t[r]!==void 0&&t[r]!==1)e.push(`aps.${r} must be 1 when present.`);if(t["content-available"]===1)s.push("Background execution depends on app configuration and OS scheduling. A successful send does not prove the task ran.");if(t["mutable-content"]===1)s.push("Use real APNs delivery to verify notification service extensions.");if(n.data!==void 0&&n.body===void 0)s.push("Expo on iOS reads custom data from a top-level body object. This raw payload will be sent unchanged.");const l=d(n.body).__buoyTestId??n.__buoyTestId;return{valid:e.length===0,bytes:i,errors:e,warnings:s,testId:typeof l==="string"?l:null}}