@ateam-ai-mobile/expo 0.1.19 → 0.1.21
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/dist/aiLive/provider.d.mts +20 -0
- package/dist/aiLive/provider.d.ts +20 -0
- package/dist/aiLive/provider.js +400 -0
- package/dist/aiLive/provider.js.map +1 -0
- package/dist/aiLive/provider.mjs +15 -0
- package/dist/aiLive/provider.mjs.map +1 -0
- package/dist/bootstrap.js +350 -3
- package/dist/bootstrap.js.map +1 -1
- package/dist/bootstrap.mjs +17 -12
- package/dist/bootstrap.mjs.map +1 -1
- package/dist/capabilities/index.d.mts +40 -1
- package/dist/capabilities/index.d.ts +40 -1
- package/dist/capabilities/index.js +50 -14
- package/dist/capabilities/index.js.map +1 -1
- package/dist/capabilities/index.mjs +49 -32
- package/dist/capabilities/index.mjs.map +1 -1
- package/dist/{chunk-42FP5DTO.mjs → chunk-2ZQEQJQ6.mjs} +1 -1
- package/dist/chunk-2ZQEQJQ6.mjs.map +1 -0
- package/dist/chunk-AE55BKYV.mjs +24 -0
- package/dist/chunk-AE55BKYV.mjs.map +1 -0
- package/dist/chunk-BAZFMTCC.mjs +815 -0
- package/dist/chunk-BAZFMTCC.mjs.map +1 -0
- package/dist/{chunk-Z5AI2SXX.mjs → chunk-C62NQA4F.mjs} +203 -1004
- package/dist/chunk-C62NQA4F.mjs.map +1 -0
- package/dist/{chunk-DE6Y4VRI.mjs → chunk-GA7DS3PA.mjs} +1 -1
- package/dist/{chunk-DE6Y4VRI.mjs.map → chunk-GA7DS3PA.mjs.map} +1 -1
- package/dist/chunk-GW7YGXOW.mjs +47 -0
- package/dist/chunk-GW7YGXOW.mjs.map +1 -0
- package/dist/chunk-JUBLG3PN.mjs +351 -0
- package/dist/chunk-JUBLG3PN.mjs.map +1 -0
- package/dist/{chunk-Z3P7RUCC.mjs → chunk-OEX4OKYQ.mjs} +13 -13
- package/dist/{chunk-6JX222YX.mjs → chunk-WQK6PUBN.mjs} +9 -2
- package/dist/chunk-WQK6PUBN.mjs.map +1 -0
- package/dist/device/createDeviceProvider.js.map +1 -1
- package/dist/device/createDeviceProvider.mjs +7 -6
- package/dist/device-bridge/index.mjs +8 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/push/index.d.mts +24 -0
- package/dist/push/index.d.ts +24 -0
- package/dist/push/index.js +224 -0
- package/dist/push/index.js.map +1 -0
- package/dist/push/index.mjs +174 -0
- package/dist/push/index.mjs.map +1 -0
- package/dist/push/silentSync.d.mts +8 -0
- package/dist/push/silentSync.d.ts +8 -0
- package/dist/push/silentSync.js +1075 -0
- package/dist/push/silentSync.js.map +1 -0
- package/dist/push/silentSync.mjs +67 -0
- package/dist/push/silentSync.mjs.map +1 -0
- package/dist/vision/index.d.mts +7 -1
- package/dist/vision/index.d.ts +7 -1
- package/dist/vision/index.js +8 -0
- package/dist/vision/index.js.map +1 -1
- package/dist/vision/index.mjs +3 -1
- package/ios/AdaVisionModule.swift +44 -1
- package/package.json +28 -6
- package/src/aiLive/provider.ts +242 -0
- package/src/aiLive/streamProtocol.ts +90 -0
- package/src/bootstrap.ts +7 -1
- package/src/capabilities/choices.ts +65 -0
- package/src/capabilities/config.ts +5 -4
- package/src/capabilities/index.ts +2 -0
- package/src/capabilities/picturesToAi.ts +40 -0
- package/src/capabilities/store.ts +38 -37
- package/src/index.ts +3 -0
- package/src/push/index.ts +18 -0
- package/src/push/messaging.ts +49 -0
- package/src/push/registration.ts +31 -0
- package/src/push/silentSync.ts +83 -0
- package/src/push/usePushNotifications.ts +224 -0
- package/src/vision/index.ts +14 -0
- package/dist/chunk-42FP5DTO.mjs.map +0 -1
- package/dist/chunk-6JX222YX.mjs.map +0 -1
- package/dist/chunk-Z5AI2SXX.mjs.map +0 -1
- /package/dist/{chunk-Z3P7RUCC.mjs.map → chunk-OEX4OKYQ.mjs.map} +0 -0
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getToday
|
|
3
|
-
} from "./chunk-KOXF2FZM.mjs";
|
|
4
1
|
import {
|
|
5
2
|
collectHistory,
|
|
6
3
|
collectMoodExtras,
|
|
@@ -8,6 +5,9 @@ import {
|
|
|
8
5
|
hasPermission,
|
|
9
6
|
requestPermission
|
|
10
7
|
} from "./chunk-XQSY6MHP.mjs";
|
|
8
|
+
import {
|
|
9
|
+
getToday
|
|
10
|
+
} from "./chunk-KOXF2FZM.mjs";
|
|
11
11
|
import {
|
|
12
12
|
reverseGeocode
|
|
13
13
|
} from "./chunk-7FWYEHN5.mjs";
|
|
@@ -189,438 +189,176 @@ var RelayClient = class {
|
|
|
189
189
|
}
|
|
190
190
|
};
|
|
191
191
|
|
|
192
|
-
// src/device-bridge/
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
192
|
+
// src/device-bridge/collectors/location.ts
|
|
193
|
+
var location_exports = {};
|
|
194
|
+
__export(location_exports, {
|
|
195
|
+
collectCurrent: () => collectCurrent,
|
|
196
|
+
getPosition: () => getPosition,
|
|
197
|
+
hasPermission: () => hasPermission2,
|
|
198
|
+
requestPermission: () => requestPermission2,
|
|
199
|
+
watchPosition: () => watchPosition
|
|
200
|
+
});
|
|
201
|
+
import * as ExpoLocation from "expo-location";
|
|
202
|
+
async function requestPermission2() {
|
|
203
|
+
const { status: fg } = await ExpoLocation.requestForegroundPermissionsAsync();
|
|
204
|
+
if (fg !== "granted") return false;
|
|
205
205
|
try {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
return {
|
|
209
|
-
id: actionId,
|
|
210
|
-
ok: false,
|
|
211
|
-
error: "SMS not available on this device",
|
|
212
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
const { result } = await SMS.sendSMSAsync([phone], message);
|
|
216
|
-
return {
|
|
217
|
-
id: actionId,
|
|
218
|
-
ok: result === "sent" || result === "unknown",
|
|
219
|
-
// 'unknown' on Android means it was sent
|
|
220
|
-
result: {
|
|
221
|
-
to: params.contact_name || phone,
|
|
222
|
-
phone,
|
|
223
|
-
message,
|
|
224
|
-
status: result,
|
|
225
|
-
sent_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
226
|
-
},
|
|
227
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
228
|
-
};
|
|
229
|
-
} catch (error) {
|
|
230
|
-
return {
|
|
231
|
-
id: actionId,
|
|
232
|
-
ok: false,
|
|
233
|
-
error: `SMS send failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
234
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
235
|
-
};
|
|
206
|
+
await ExpoLocation.requestBackgroundPermissionsAsync();
|
|
207
|
+
} catch {
|
|
236
208
|
}
|
|
209
|
+
return fg === "granted";
|
|
237
210
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
async function getDefaultCalendarId() {
|
|
242
|
-
const calendars = await ExpoCalendar.getCalendarsAsync(ExpoCalendar.EntityTypes.EVENT);
|
|
243
|
-
const defaultCal = calendars.find((c) => c.isPrimary) || calendars[0];
|
|
244
|
-
return defaultCal?.id || null;
|
|
211
|
+
async function hasPermission2() {
|
|
212
|
+
const { status } = await ExpoLocation.getForegroundPermissionsAsync();
|
|
213
|
+
return status === "granted";
|
|
245
214
|
}
|
|
246
|
-
async function
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
id: actionId,
|
|
254
|
-
ok: false,
|
|
255
|
-
error: "Missing title, date, or start_time",
|
|
256
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
257
|
-
};
|
|
258
|
-
}
|
|
215
|
+
async function collectCurrent() {
|
|
216
|
+
const location = await ExpoLocation.getCurrentPositionAsync({
|
|
217
|
+
accuracy: ExpoLocation.Accuracy.Balanced
|
|
218
|
+
});
|
|
219
|
+
let place = "Current Location";
|
|
220
|
+
let address = `${location.coords.latitude.toFixed(4)}, ${location.coords.longitude.toFixed(4)}`;
|
|
221
|
+
let locationType = "unknown";
|
|
259
222
|
try {
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
id: actionId,
|
|
264
|
-
ok: false,
|
|
265
|
-
error: "No calendar found on device",
|
|
266
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
const [year, month, day] = date.split("-").map(Number);
|
|
270
|
-
const [startH, startM] = startTime.split(":").map(Number);
|
|
271
|
-
const startDate = new Date(year, month - 1, day, startH, startM);
|
|
272
|
-
let endDate;
|
|
273
|
-
if (endTime) {
|
|
274
|
-
const [endH, endM] = endTime.split(":").map(Number);
|
|
275
|
-
endDate = new Date(year, month - 1, day, endH, endM);
|
|
276
|
-
} else {
|
|
277
|
-
endDate = new Date(startDate.getTime() + 60 * 60 * 1e3);
|
|
278
|
-
}
|
|
279
|
-
const eventId = await ExpoCalendar.createEventAsync(calendarId, {
|
|
280
|
-
title,
|
|
281
|
-
startDate,
|
|
282
|
-
endDate,
|
|
283
|
-
location: params.location || void 0,
|
|
284
|
-
notes: params.notes || void 0,
|
|
285
|
-
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
223
|
+
const geocoded = await reverseGeocode({
|
|
224
|
+
latitude: location.coords.latitude,
|
|
225
|
+
longitude: location.coords.longitude
|
|
286
226
|
});
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
message: `Event "${title}" created successfully`
|
|
297
|
-
},
|
|
298
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
299
|
-
};
|
|
300
|
-
} catch (error) {
|
|
301
|
-
return {
|
|
302
|
-
id: actionId,
|
|
303
|
-
ok: false,
|
|
304
|
-
error: `Calendar create failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
305
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
async function executeReschedule(actionId, params) {
|
|
310
|
-
const eventId = params.event_id;
|
|
311
|
-
if (!eventId) {
|
|
312
|
-
return {
|
|
313
|
-
id: actionId,
|
|
314
|
-
ok: false,
|
|
315
|
-
error: "Missing event_id",
|
|
316
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
try {
|
|
320
|
-
const updates = {};
|
|
321
|
-
if (params.new_date && params.new_start_time) {
|
|
322
|
-
const [year, month, day] = params.new_date.split("-").map(Number);
|
|
323
|
-
const [h, m] = params.new_start_time.split(":").map(Number);
|
|
324
|
-
updates.startDate = new Date(year, month - 1, day, h, m);
|
|
325
|
-
if (params.new_end_time) {
|
|
326
|
-
const [eh, em] = params.new_end_time.split(":").map(Number);
|
|
327
|
-
updates.endDate = new Date(year, month - 1, day, eh, em);
|
|
328
|
-
} else {
|
|
329
|
-
updates.endDate = new Date(updates.startDate.getTime() + 60 * 60 * 1e3);
|
|
330
|
-
}
|
|
227
|
+
if (geocoded) {
|
|
228
|
+
place = geocoded.name || geocoded.street || place;
|
|
229
|
+
address = [
|
|
230
|
+
geocoded.streetNumber,
|
|
231
|
+
geocoded.street,
|
|
232
|
+
geocoded.city,
|
|
233
|
+
geocoded.region
|
|
234
|
+
].filter(Boolean).join(", ") || address;
|
|
235
|
+
locationType = classifyLocation(geocoded);
|
|
331
236
|
}
|
|
332
|
-
|
|
333
|
-
return {
|
|
334
|
-
id: actionId,
|
|
335
|
-
ok: true,
|
|
336
|
-
result: { event_id: eventId, message: "Event rescheduled successfully" },
|
|
337
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
338
|
-
};
|
|
339
|
-
} catch (error) {
|
|
340
|
-
return {
|
|
341
|
-
id: actionId,
|
|
342
|
-
ok: false,
|
|
343
|
-
error: `Calendar reschedule failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
344
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
345
|
-
};
|
|
237
|
+
} catch {
|
|
346
238
|
}
|
|
239
|
+
return {
|
|
240
|
+
ok: true,
|
|
241
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
242
|
+
location: {
|
|
243
|
+
place,
|
|
244
|
+
address,
|
|
245
|
+
lat: location.coords.latitude,
|
|
246
|
+
lng: location.coords.longitude,
|
|
247
|
+
type: locationType
|
|
248
|
+
}
|
|
249
|
+
};
|
|
347
250
|
}
|
|
348
|
-
async function
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
try {
|
|
359
|
-
await ExpoCalendar.deleteEventAsync(eventId);
|
|
360
|
-
return {
|
|
361
|
-
id: actionId,
|
|
362
|
-
ok: true,
|
|
363
|
-
result: { event_id: eventId, message: "Event cancelled successfully" },
|
|
364
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
365
|
-
};
|
|
366
|
-
} catch (error) {
|
|
367
|
-
return {
|
|
368
|
-
id: actionId,
|
|
369
|
-
ok: false,
|
|
370
|
-
error: `Calendar cancel failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
371
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
372
|
-
};
|
|
251
|
+
async function ensurePermission() {
|
|
252
|
+
if (await hasPermission2()) return;
|
|
253
|
+
const granted = await requestPermission2();
|
|
254
|
+
if (!granted) {
|
|
255
|
+
throw new Error(
|
|
256
|
+
"Location permission was not granted. Enable it for this app in iOS Settings > Privacy > Location Services."
|
|
257
|
+
);
|
|
373
258
|
}
|
|
374
259
|
}
|
|
375
|
-
function
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
hour12: false
|
|
260
|
+
async function getPosition(opts = {}) {
|
|
261
|
+
await ensurePermission();
|
|
262
|
+
const fix = await ExpoLocation.getCurrentPositionAsync({
|
|
263
|
+
accuracy: opts.accuracy === "high" ? ExpoLocation.Accuracy.High : ExpoLocation.Accuracy.Balanced
|
|
380
264
|
});
|
|
265
|
+
return toPosition(fix);
|
|
381
266
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
currentDndState.enabled = params.enabled;
|
|
393
|
-
}
|
|
394
|
-
if (params.schedule_start) {
|
|
395
|
-
currentDndState.schedule.start = params.schedule_start;
|
|
396
|
-
}
|
|
397
|
-
if (params.schedule_end) {
|
|
398
|
-
currentDndState.schedule.end = params.schedule_end;
|
|
399
|
-
}
|
|
400
|
-
if (params.allowlist) {
|
|
401
|
-
currentDndState.allowlist = params.allowlist;
|
|
402
|
-
}
|
|
403
|
-
return {
|
|
404
|
-
id: actionId,
|
|
405
|
-
ok: true,
|
|
406
|
-
result: {
|
|
407
|
-
message: `DND ${currentDndState.enabled ? "enabled" : "disabled"}`,
|
|
408
|
-
dnd: currentDndState
|
|
409
|
-
},
|
|
410
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
411
|
-
};
|
|
412
|
-
} catch (error) {
|
|
413
|
-
return {
|
|
414
|
-
id: actionId,
|
|
415
|
-
ok: false,
|
|
416
|
-
error: `DND set failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
417
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
418
|
-
};
|
|
419
|
-
}
|
|
267
|
+
function toPosition(fix) {
|
|
268
|
+
return {
|
|
269
|
+
latitude: fix.coords.latitude,
|
|
270
|
+
longitude: fix.coords.longitude,
|
|
271
|
+
altitude: fix.coords.altitude ?? void 0,
|
|
272
|
+
accuracy: nonNegative(fix.coords.accuracy),
|
|
273
|
+
timestamp: fix.timestamp ?? Date.now(),
|
|
274
|
+
speed: nonNegative(fix.coords.speed),
|
|
275
|
+
course: nonNegative(fix.coords.heading)
|
|
276
|
+
};
|
|
420
277
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
import * as Linking from "expo-linking";
|
|
424
|
-
import { Platform } from "react-native";
|
|
425
|
-
async function execute3(actionId, params) {
|
|
426
|
-
const destination = params.destination;
|
|
427
|
-
const mode = params.mode || "driving";
|
|
428
|
-
if (!destination) {
|
|
429
|
-
return {
|
|
430
|
-
id: actionId,
|
|
431
|
-
ok: false,
|
|
432
|
-
error: "Missing destination parameter",
|
|
433
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
try {
|
|
437
|
-
const encodedDest = encodeURIComponent(destination);
|
|
438
|
-
let url;
|
|
439
|
-
if (Platform.OS === "ios") {
|
|
440
|
-
const modeMap = {
|
|
441
|
-
driving: "d",
|
|
442
|
-
transit: "r",
|
|
443
|
-
walking: "w"
|
|
444
|
-
};
|
|
445
|
-
url = `maps://maps.apple.com/?daddr=${encodedDest}&dirflg=${modeMap[mode] || "d"}`;
|
|
446
|
-
} else {
|
|
447
|
-
const modeMap = {
|
|
448
|
-
driving: "driving",
|
|
449
|
-
transit: "transit",
|
|
450
|
-
walking: "walking"
|
|
451
|
-
};
|
|
452
|
-
url = `google.navigation:q=${encodedDest}&mode=${modeMap[mode] || "driving"}`;
|
|
453
|
-
}
|
|
454
|
-
const canOpen = await Linking.canOpenURL(url);
|
|
455
|
-
if (!canOpen) {
|
|
456
|
-
const webModeMap = {
|
|
457
|
-
driving: "driving",
|
|
458
|
-
transit: "transit",
|
|
459
|
-
walking: "walking"
|
|
460
|
-
};
|
|
461
|
-
url = `https://www.google.com/maps/dir/?api=1&destination=${encodedDest}&travelmode=${webModeMap[mode] || "driving"}`;
|
|
462
|
-
}
|
|
463
|
-
await Linking.openURL(url);
|
|
464
|
-
return {
|
|
465
|
-
id: actionId,
|
|
466
|
-
ok: true,
|
|
467
|
-
result: {
|
|
468
|
-
message: `Navigation started to ${destination} via ${mode}`,
|
|
469
|
-
destination,
|
|
470
|
-
mode,
|
|
471
|
-
status: "navigating"
|
|
472
|
-
},
|
|
473
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
474
|
-
};
|
|
475
|
-
} catch (error) {
|
|
476
|
-
return {
|
|
477
|
-
id: actionId,
|
|
478
|
-
ok: false,
|
|
479
|
-
error: `Navigation failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
480
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
481
|
-
};
|
|
482
|
-
}
|
|
278
|
+
function nonNegative(v) {
|
|
279
|
+
return typeof v === "number" && v >= 0 ? v : void 0;
|
|
483
280
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
281
|
+
var WATCH_TIER = {
|
|
282
|
+
navigation: ExpoLocation.Accuracy.BestForNavigation,
|
|
283
|
+
high: ExpoLocation.Accuracy.High,
|
|
284
|
+
balanced: ExpoLocation.Accuracy.Balanced,
|
|
285
|
+
low: ExpoLocation.Accuracy.Low
|
|
286
|
+
};
|
|
287
|
+
function watchPosition(cb, opts) {
|
|
288
|
+
let subscription = null;
|
|
289
|
+
let cancelled = false;
|
|
290
|
+
(async () => {
|
|
291
|
+
try {
|
|
292
|
+
await ensurePermission();
|
|
293
|
+
if (cancelled) return;
|
|
294
|
+
subscription = await ExpoLocation.watchPositionAsync(
|
|
295
|
+
{ accuracy: WATCH_TIER[opts?.accuracy ?? "balanced"] },
|
|
296
|
+
(fix) => cb(toPosition(fix))
|
|
297
|
+
);
|
|
298
|
+
if (cancelled) {
|
|
299
|
+
subscription?.remove();
|
|
300
|
+
subscription = null;
|
|
301
|
+
}
|
|
302
|
+
} catch (err) {
|
|
303
|
+
console.warn("[location] watchPosition failed:", err?.message || String(err));
|
|
304
|
+
}
|
|
305
|
+
})();
|
|
306
|
+
return () => {
|
|
307
|
+
cancelled = true;
|
|
308
|
+
subscription?.remove();
|
|
309
|
+
subscription = null;
|
|
310
|
+
};
|
|
503
311
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
};
|
|
514
|
-
} catch (e) {
|
|
515
|
-
return {
|
|
516
|
-
id: actionId,
|
|
517
|
-
ok: false,
|
|
518
|
-
error: String(e?.message || e),
|
|
519
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
520
|
-
};
|
|
521
|
-
}
|
|
312
|
+
function classifyLocation(geocoded) {
|
|
313
|
+
const name = (geocoded.name || "").toLowerCase();
|
|
314
|
+
const street = (geocoded.street || "").toLowerCase();
|
|
315
|
+
if (name.includes("home") || name.includes("residence")) return "home";
|
|
316
|
+
if (name.includes("office") || name.includes("workplace") || name.includes("tech park")) return "work";
|
|
317
|
+
if (name.includes("school") || name.includes("elementary") || name.includes("university")) return "school";
|
|
318
|
+
if (name.includes("gym") || name.includes("fitness")) return "fitness";
|
|
319
|
+
if (name.includes("hospital") || name.includes("clinic") || name.includes("dental")) return "medical";
|
|
320
|
+
return "other";
|
|
522
321
|
}
|
|
523
322
|
|
|
524
|
-
// src/device-bridge/
|
|
525
|
-
var
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
start() {
|
|
538
|
-
if (this.running) return;
|
|
539
|
-
this.running = true;
|
|
540
|
-
this.poll();
|
|
541
|
-
this.timer = setInterval(() => this.poll(), this.pollInterval);
|
|
542
|
-
}
|
|
543
|
-
/**
|
|
544
|
-
* Stop polling.
|
|
545
|
-
*/
|
|
546
|
-
stop() {
|
|
547
|
-
this.running = false;
|
|
548
|
-
if (this.timer) {
|
|
549
|
-
clearInterval(this.timer);
|
|
550
|
-
this.timer = null;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
/**
|
|
554
|
-
* Check if poller is running.
|
|
555
|
-
*/
|
|
556
|
-
isRunning() {
|
|
557
|
-
return this.running;
|
|
558
|
-
}
|
|
559
|
-
/**
|
|
560
|
-
* Execute a single poll cycle.
|
|
561
|
-
*/
|
|
562
|
-
async poll() {
|
|
563
|
-
if (!this.running) return;
|
|
323
|
+
// src/device-bridge/collectors/connectivity.ts
|
|
324
|
+
var connectivity_exports = {};
|
|
325
|
+
__export(connectivity_exports, {
|
|
326
|
+
collectConnectivity: () => collectConnectivity
|
|
327
|
+
});
|
|
328
|
+
import * as ExpoNetwork from "expo-network";
|
|
329
|
+
async function collectConnectivity() {
|
|
330
|
+
const networkState = await ExpoNetwork.getNetworkStateAsync();
|
|
331
|
+
let type = "none";
|
|
332
|
+
let wifiName;
|
|
333
|
+
let cellularType;
|
|
334
|
+
if (networkState.type === ExpoNetwork.NetworkStateType.WIFI) {
|
|
335
|
+
type = "wifi";
|
|
564
336
|
try {
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
if (action.status && action.status !== "pending") continue;
|
|
569
|
-
const result = await this.executeAction(action);
|
|
570
|
-
await this.relayClient.completeAction(result);
|
|
571
|
-
this.onActionExecuted?.(result);
|
|
572
|
-
}
|
|
573
|
-
} catch (error) {
|
|
574
|
-
this.onError?.(
|
|
575
|
-
error instanceof Error ? error : new Error(String(error))
|
|
576
|
-
);
|
|
337
|
+
wifiName = await getWifiName();
|
|
338
|
+
} catch {
|
|
339
|
+
wifiName = void 0;
|
|
577
340
|
}
|
|
341
|
+
} else if (networkState.type === ExpoNetwork.NetworkStateType.CELLULAR) {
|
|
342
|
+
type = "cellular";
|
|
343
|
+
cellularType = getCellularGeneration();
|
|
578
344
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
return await execute(id, params);
|
|
588
|
-
case "calendar.create":
|
|
589
|
-
return await executeCreate(id, params);
|
|
590
|
-
case "calendar.reschedule":
|
|
591
|
-
return await executeReschedule(id, params);
|
|
592
|
-
case "calendar.cancel":
|
|
593
|
-
return await executeCancel(id, params);
|
|
594
|
-
case "dnd.set":
|
|
595
|
-
return await execute2(id, params);
|
|
596
|
-
case "navigation.start":
|
|
597
|
-
return await execute3(id, params);
|
|
598
|
-
case "health.today":
|
|
599
|
-
return await executeToday(id);
|
|
600
|
-
case "health.history":
|
|
601
|
-
return await executeHistory(id, params);
|
|
602
|
-
default:
|
|
603
|
-
return {
|
|
604
|
-
id,
|
|
605
|
-
ok: false,
|
|
606
|
-
error: `Unknown action type: ${type}`,
|
|
607
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
608
|
-
};
|
|
609
|
-
}
|
|
610
|
-
} catch (error) {
|
|
611
|
-
return {
|
|
612
|
-
id,
|
|
613
|
-
ok: false,
|
|
614
|
-
error: `Action execution failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
615
|
-
executed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
616
|
-
};
|
|
345
|
+
return {
|
|
346
|
+
ok: true,
|
|
347
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
348
|
+
connectivity: {
|
|
349
|
+
type,
|
|
350
|
+
is_connected: networkState.isConnected ?? false,
|
|
351
|
+
...wifiName && { wifi_name: wifiName },
|
|
352
|
+
...cellularType && { cellular_type: cellularType }
|
|
617
353
|
}
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
async function getWifiName() {
|
|
357
|
+
return void 0;
|
|
358
|
+
}
|
|
359
|
+
function getCellularGeneration() {
|
|
360
|
+
return "LTE";
|
|
361
|
+
}
|
|
624
362
|
|
|
625
363
|
// src/device-bridge/capabilities.ts
|
|
626
364
|
var capabilities_exports = {};
|
|
@@ -638,29 +376,29 @@ __export(capabilities_exports, {
|
|
|
638
376
|
});
|
|
639
377
|
|
|
640
378
|
// src/device-bridge/collectors/calendar.ts
|
|
641
|
-
import * as
|
|
642
|
-
async function
|
|
643
|
-
const { status } = await
|
|
379
|
+
import * as ExpoCalendar from "expo-calendar";
|
|
380
|
+
async function requestPermission3() {
|
|
381
|
+
const { status } = await ExpoCalendar.requestCalendarPermissionsAsync();
|
|
644
382
|
return status === "granted";
|
|
645
383
|
}
|
|
646
|
-
async function
|
|
647
|
-
const { status } = await
|
|
384
|
+
async function hasPermission3() {
|
|
385
|
+
const { status } = await ExpoCalendar.getCalendarPermissionsAsync();
|
|
648
386
|
return status === "granted";
|
|
649
387
|
}
|
|
650
388
|
async function collectToday2() {
|
|
651
389
|
const now = /* @__PURE__ */ new Date();
|
|
652
390
|
const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
653
391
|
const endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59);
|
|
654
|
-
const calendars = await
|
|
392
|
+
const calendars = await ExpoCalendar.getCalendarsAsync(ExpoCalendar.EntityTypes.EVENT);
|
|
655
393
|
const userCalendarIds = getUserCalendarIds(calendars);
|
|
656
|
-
const events = await
|
|
394
|
+
const events = await ExpoCalendar.getEventsAsync(userCalendarIds, startOfDay, endOfDay);
|
|
657
395
|
const filtered = filterHolidays(events);
|
|
658
396
|
const mapped = filtered.map((e) => ({
|
|
659
397
|
id: e.id,
|
|
660
398
|
title: e.title || "Untitled",
|
|
661
399
|
date: startOfDay.toISOString().slice(0, 10),
|
|
662
|
-
start:
|
|
663
|
-
end:
|
|
400
|
+
start: formatTime(new Date(e.startDate)),
|
|
401
|
+
end: formatTime(new Date(e.endDate)),
|
|
664
402
|
location: e.location || "",
|
|
665
403
|
attendees: (e.attendees || []).map((a) => a.name || a.email || "Unknown"),
|
|
666
404
|
notes: e.notes || ""
|
|
@@ -678,9 +416,9 @@ async function collectUpcoming(daysAhead = 3) {
|
|
|
678
416
|
const now = /* @__PURE__ */ new Date();
|
|
679
417
|
const tomorrow = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);
|
|
680
418
|
const endDate = new Date(now.getFullYear(), now.getMonth(), now.getDate() + daysAhead + 1);
|
|
681
|
-
const calendars = await
|
|
419
|
+
const calendars = await ExpoCalendar.getCalendarsAsync(ExpoCalendar.EntityTypes.EVENT);
|
|
682
420
|
const userCalendarIds = getUserCalendarIds(calendars);
|
|
683
|
-
const events = await
|
|
421
|
+
const events = await ExpoCalendar.getEventsAsync(userCalendarIds, tomorrow, endDate);
|
|
684
422
|
const filtered = filterHolidays(events);
|
|
685
423
|
const mapped = filtered.map((e) => {
|
|
686
424
|
const startDt = new Date(e.startDate);
|
|
@@ -688,8 +426,8 @@ async function collectUpcoming(daysAhead = 3) {
|
|
|
688
426
|
id: e.id,
|
|
689
427
|
title: e.title || "Untitled",
|
|
690
428
|
date: startDt.toISOString().slice(0, 10),
|
|
691
|
-
start:
|
|
692
|
-
end:
|
|
429
|
+
start: formatTime(startDt),
|
|
430
|
+
end: formatTime(new Date(e.endDate)),
|
|
693
431
|
location: e.location || "",
|
|
694
432
|
attendees: (e.attendees || []).map((a) => a.name || a.email || "Unknown"),
|
|
695
433
|
notes: e.notes || ""
|
|
@@ -732,7 +470,7 @@ function filterHolidays(events) {
|
|
|
732
470
|
return true;
|
|
733
471
|
});
|
|
734
472
|
}
|
|
735
|
-
function
|
|
473
|
+
function formatTime(date) {
|
|
736
474
|
return date.toLocaleTimeString("en-US", {
|
|
737
475
|
hour: "2-digit",
|
|
738
476
|
minute: "2-digit",
|
|
@@ -744,11 +482,11 @@ function formatTime2(date) {
|
|
|
744
482
|
import * as ExpoContacts from "expo-contacts";
|
|
745
483
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
746
484
|
var HASH_STORE_KEY = "device-bridge.contacts.hashes.v1";
|
|
747
|
-
async function
|
|
485
|
+
async function requestPermission4() {
|
|
748
486
|
const { status } = await ExpoContacts.requestPermissionsAsync();
|
|
749
487
|
return status === "granted";
|
|
750
488
|
}
|
|
751
|
-
async function
|
|
489
|
+
async function hasPermission4() {
|
|
752
490
|
const { status } = await ExpoContacts.getPermissionsAsync();
|
|
753
491
|
return status === "granted";
|
|
754
492
|
}
|
|
@@ -814,169 +552,38 @@ async function collectAll() {
|
|
|
814
552
|
if (!fullSync && added === 0 && updated === 0 && removed === 0) {
|
|
815
553
|
return null;
|
|
816
554
|
}
|
|
817
|
-
try {
|
|
818
|
-
await AsyncStorage.setItem(HASH_STORE_KEY, JSON.stringify(currentHashes));
|
|
819
|
-
} catch {
|
|
820
|
-
}
|
|
821
|
-
return contacts;
|
|
822
|
-
}
|
|
823
|
-
function fnv1a(str) {
|
|
824
|
-
let h = 2166136261;
|
|
825
|
-
for (let i = 0; i < str.length; i++) {
|
|
826
|
-
h ^= str.charCodeAt(i);
|
|
827
|
-
h = Math.imul(h, 16777619);
|
|
828
|
-
}
|
|
829
|
-
return (h >>> 0).toString(16).padStart(8, "0");
|
|
830
|
-
}
|
|
831
|
-
function hashContact(c) {
|
|
832
|
-
const parts = [
|
|
833
|
-
c.id,
|
|
834
|
-
(c.name || "").trim(),
|
|
835
|
-
c.phone || "\0",
|
|
836
|
-
(c.email || "\0").toLowerCase(),
|
|
837
|
-
c.birthday || "\0"
|
|
838
|
-
];
|
|
839
|
-
return fnv1a(parts.join(""));
|
|
840
|
-
}
|
|
841
|
-
function formatBirthday(birthday) {
|
|
842
|
-
if (!birthday) return null;
|
|
843
|
-
const { year, month, day } = birthday;
|
|
844
|
-
if (month === void 0 || day === void 0) return null;
|
|
845
|
-
const m = String(month + 1).padStart(2, "0");
|
|
846
|
-
const d = String(day).padStart(2, "0");
|
|
847
|
-
if (!year) return `--${m}-${d}`;
|
|
848
|
-
return `${year}-${m}-${d}`;
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
// src/device-bridge/collectors/location.ts
|
|
852
|
-
var location_exports = {};
|
|
853
|
-
__export(location_exports, {
|
|
854
|
-
collectCurrent: () => collectCurrent,
|
|
855
|
-
getPosition: () => getPosition,
|
|
856
|
-
hasPermission: () => hasPermission4,
|
|
857
|
-
requestPermission: () => requestPermission4,
|
|
858
|
-
watchPosition: () => watchPosition
|
|
859
|
-
});
|
|
860
|
-
import * as ExpoLocation from "expo-location";
|
|
861
|
-
async function requestPermission4() {
|
|
862
|
-
const { status: fg } = await ExpoLocation.requestForegroundPermissionsAsync();
|
|
863
|
-
if (fg !== "granted") return false;
|
|
864
|
-
try {
|
|
865
|
-
await ExpoLocation.requestBackgroundPermissionsAsync();
|
|
866
|
-
} catch {
|
|
867
|
-
}
|
|
868
|
-
return fg === "granted";
|
|
869
|
-
}
|
|
870
|
-
async function hasPermission4() {
|
|
871
|
-
const { status } = await ExpoLocation.getForegroundPermissionsAsync();
|
|
872
|
-
return status === "granted";
|
|
873
|
-
}
|
|
874
|
-
async function collectCurrent() {
|
|
875
|
-
const location = await ExpoLocation.getCurrentPositionAsync({
|
|
876
|
-
accuracy: ExpoLocation.Accuracy.Balanced
|
|
877
|
-
});
|
|
878
|
-
let place = "Current Location";
|
|
879
|
-
let address = `${location.coords.latitude.toFixed(4)}, ${location.coords.longitude.toFixed(4)}`;
|
|
880
|
-
let locationType = "unknown";
|
|
881
|
-
try {
|
|
882
|
-
const geocoded = await reverseGeocode({
|
|
883
|
-
latitude: location.coords.latitude,
|
|
884
|
-
longitude: location.coords.longitude
|
|
885
|
-
});
|
|
886
|
-
if (geocoded) {
|
|
887
|
-
place = geocoded.name || geocoded.street || place;
|
|
888
|
-
address = [
|
|
889
|
-
geocoded.streetNumber,
|
|
890
|
-
geocoded.street,
|
|
891
|
-
geocoded.city,
|
|
892
|
-
geocoded.region
|
|
893
|
-
].filter(Boolean).join(", ") || address;
|
|
894
|
-
locationType = classifyLocation(geocoded);
|
|
895
|
-
}
|
|
555
|
+
try {
|
|
556
|
+
await AsyncStorage.setItem(HASH_STORE_KEY, JSON.stringify(currentHashes));
|
|
896
557
|
} catch {
|
|
897
558
|
}
|
|
898
|
-
return
|
|
899
|
-
ok: true,
|
|
900
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
901
|
-
location: {
|
|
902
|
-
place,
|
|
903
|
-
address,
|
|
904
|
-
lat: location.coords.latitude,
|
|
905
|
-
lng: location.coords.longitude,
|
|
906
|
-
type: locationType
|
|
907
|
-
}
|
|
908
|
-
};
|
|
559
|
+
return contacts;
|
|
909
560
|
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
"Location permission was not granted. Enable it for this app in iOS Settings > Privacy > Location Services."
|
|
916
|
-
);
|
|
561
|
+
function fnv1a(str) {
|
|
562
|
+
let h = 2166136261;
|
|
563
|
+
for (let i = 0; i < str.length; i++) {
|
|
564
|
+
h ^= str.charCodeAt(i);
|
|
565
|
+
h = Math.imul(h, 16777619);
|
|
917
566
|
}
|
|
567
|
+
return (h >>> 0).toString(16).padStart(8, "0");
|
|
918
568
|
}
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
return
|
|
928
|
-
latitude: fix.coords.latitude,
|
|
929
|
-
longitude: fix.coords.longitude,
|
|
930
|
-
altitude: fix.coords.altitude ?? void 0,
|
|
931
|
-
accuracy: nonNegative(fix.coords.accuracy),
|
|
932
|
-
timestamp: fix.timestamp ?? Date.now(),
|
|
933
|
-
speed: nonNegative(fix.coords.speed),
|
|
934
|
-
course: nonNegative(fix.coords.heading)
|
|
935
|
-
};
|
|
936
|
-
}
|
|
937
|
-
function nonNegative(v) {
|
|
938
|
-
return typeof v === "number" && v >= 0 ? v : void 0;
|
|
939
|
-
}
|
|
940
|
-
var WATCH_TIER = {
|
|
941
|
-
navigation: ExpoLocation.Accuracy.BestForNavigation,
|
|
942
|
-
high: ExpoLocation.Accuracy.High,
|
|
943
|
-
balanced: ExpoLocation.Accuracy.Balanced,
|
|
944
|
-
low: ExpoLocation.Accuracy.Low
|
|
945
|
-
};
|
|
946
|
-
function watchPosition(cb, opts) {
|
|
947
|
-
let subscription = null;
|
|
948
|
-
let cancelled = false;
|
|
949
|
-
(async () => {
|
|
950
|
-
try {
|
|
951
|
-
await ensurePermission();
|
|
952
|
-
if (cancelled) return;
|
|
953
|
-
subscription = await ExpoLocation.watchPositionAsync(
|
|
954
|
-
{ accuracy: WATCH_TIER[opts?.accuracy ?? "balanced"] },
|
|
955
|
-
(fix) => cb(toPosition(fix))
|
|
956
|
-
);
|
|
957
|
-
if (cancelled) {
|
|
958
|
-
subscription?.remove();
|
|
959
|
-
subscription = null;
|
|
960
|
-
}
|
|
961
|
-
} catch (err) {
|
|
962
|
-
console.warn("[location] watchPosition failed:", err?.message || String(err));
|
|
963
|
-
}
|
|
964
|
-
})();
|
|
965
|
-
return () => {
|
|
966
|
-
cancelled = true;
|
|
967
|
-
subscription?.remove();
|
|
968
|
-
subscription = null;
|
|
969
|
-
};
|
|
569
|
+
function hashContact(c) {
|
|
570
|
+
const parts = [
|
|
571
|
+
c.id,
|
|
572
|
+
(c.name || "").trim(),
|
|
573
|
+
c.phone || "\0",
|
|
574
|
+
(c.email || "\0").toLowerCase(),
|
|
575
|
+
c.birthday || "\0"
|
|
576
|
+
];
|
|
577
|
+
return fnv1a(parts.join(""));
|
|
970
578
|
}
|
|
971
|
-
function
|
|
972
|
-
|
|
973
|
-
const
|
|
974
|
-
if (
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
if (
|
|
978
|
-
|
|
979
|
-
return "other";
|
|
579
|
+
function formatBirthday(birthday) {
|
|
580
|
+
if (!birthday) return null;
|
|
581
|
+
const { year, month, day } = birthday;
|
|
582
|
+
if (month === void 0 || day === void 0) return null;
|
|
583
|
+
const m = String(month + 1).padStart(2, "0");
|
|
584
|
+
const d = String(day).padStart(2, "0");
|
|
585
|
+
if (!year) return `--${m}-${d}`;
|
|
586
|
+
return `${year}-${m}-${d}`;
|
|
980
587
|
}
|
|
981
588
|
|
|
982
589
|
// src/device-bridge/collectors/battery.ts
|
|
@@ -1025,46 +632,6 @@ function mapBatteryState(state) {
|
|
|
1025
632
|
}
|
|
1026
633
|
}
|
|
1027
634
|
|
|
1028
|
-
// src/device-bridge/collectors/connectivity.ts
|
|
1029
|
-
var connectivity_exports = {};
|
|
1030
|
-
__export(connectivity_exports, {
|
|
1031
|
-
collectConnectivity: () => collectConnectivity
|
|
1032
|
-
});
|
|
1033
|
-
import * as ExpoNetwork from "expo-network";
|
|
1034
|
-
async function collectConnectivity() {
|
|
1035
|
-
const networkState = await ExpoNetwork.getNetworkStateAsync();
|
|
1036
|
-
let type = "none";
|
|
1037
|
-
let wifiName;
|
|
1038
|
-
let cellularType;
|
|
1039
|
-
if (networkState.type === ExpoNetwork.NetworkStateType.WIFI) {
|
|
1040
|
-
type = "wifi";
|
|
1041
|
-
try {
|
|
1042
|
-
wifiName = await getWifiName();
|
|
1043
|
-
} catch {
|
|
1044
|
-
wifiName = void 0;
|
|
1045
|
-
}
|
|
1046
|
-
} else if (networkState.type === ExpoNetwork.NetworkStateType.CELLULAR) {
|
|
1047
|
-
type = "cellular";
|
|
1048
|
-
cellularType = getCellularGeneration();
|
|
1049
|
-
}
|
|
1050
|
-
return {
|
|
1051
|
-
ok: true,
|
|
1052
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1053
|
-
connectivity: {
|
|
1054
|
-
type,
|
|
1055
|
-
is_connected: networkState.isConnected ?? false,
|
|
1056
|
-
...wifiName && { wifi_name: wifiName },
|
|
1057
|
-
...cellularType && { cellular_type: cellularType }
|
|
1058
|
-
}
|
|
1059
|
-
};
|
|
1060
|
-
}
|
|
1061
|
-
async function getWifiName() {
|
|
1062
|
-
return void 0;
|
|
1063
|
-
}
|
|
1064
|
-
function getCellularGeneration() {
|
|
1065
|
-
return "LTE";
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
635
|
// src/device-bridge/collectors/notifications.ts
|
|
1069
636
|
import * as ExpoNotifications from "expo-notifications";
|
|
1070
637
|
async function requestPermission5() {
|
|
@@ -1186,8 +753,8 @@ function calendarCapability() {
|
|
|
1186
753
|
icon: "calendar",
|
|
1187
754
|
requiresPermission: true,
|
|
1188
755
|
permissionState: "not_requested",
|
|
1189
|
-
requestPermission:
|
|
1190
|
-
checkPermission:
|
|
756
|
+
requestPermission: requestPermission3,
|
|
757
|
+
checkPermission: hasPermission3,
|
|
1191
758
|
collect: async () => {
|
|
1192
759
|
const [today, upcoming] = await Promise.all([
|
|
1193
760
|
collectToday2(),
|
|
@@ -1208,8 +775,8 @@ function contactsCapability() {
|
|
|
1208
775
|
icon: "people",
|
|
1209
776
|
requiresPermission: true,
|
|
1210
777
|
permissionState: "not_requested",
|
|
1211
|
-
requestPermission:
|
|
1212
|
-
checkPermission:
|
|
778
|
+
requestPermission: requestPermission4,
|
|
779
|
+
checkPermission: hasPermission4,
|
|
1213
780
|
collect: async () => {
|
|
1214
781
|
const list = await collectAll();
|
|
1215
782
|
if (list === null) return null;
|
|
@@ -1228,8 +795,8 @@ function locationCapability() {
|
|
|
1228
795
|
icon: "location",
|
|
1229
796
|
requiresPermission: true,
|
|
1230
797
|
permissionState: "not_requested",
|
|
1231
|
-
requestPermission:
|
|
1232
|
-
checkPermission:
|
|
798
|
+
requestPermission: requestPermission2,
|
|
799
|
+
checkPermission: hasPermission2,
|
|
1233
800
|
collect: collectCurrent,
|
|
1234
801
|
defaultInterval: 3e4
|
|
1235
802
|
// 30 sec
|
|
@@ -1361,6 +928,8 @@ function allBuiltIn() {
|
|
|
1361
928
|
}
|
|
1362
929
|
|
|
1363
930
|
// src/device-bridge/background.ts
|
|
931
|
+
import * as TaskManager from "expo-task-manager";
|
|
932
|
+
import * as BackgroundFetch from "expo-background-fetch";
|
|
1364
933
|
var BACKGROUND_TASK_NAME = "DEVICE_BRIDGE_BACKGROUND_SYNC";
|
|
1365
934
|
var CAPABILITY_FACTORIES = {
|
|
1366
935
|
calendar: calendarCapability,
|
|
@@ -1427,384 +996,14 @@ async function collectAndPush(relay, domain) {
|
|
|
1427
996
|
return result.ok;
|
|
1428
997
|
}
|
|
1429
998
|
|
|
1430
|
-
// src/device-bridge/biometrics.ts
|
|
1431
|
-
var biometrics_exports = {};
|
|
1432
|
-
__export(biometrics_exports, {
|
|
1433
|
-
authenticate: () => authenticate,
|
|
1434
|
-
capabilities: () => capabilities
|
|
1435
|
-
});
|
|
1436
|
-
import * as LocalAuthentication from "expo-local-authentication";
|
|
1437
|
-
async function capabilities() {
|
|
1438
|
-
const [hasHardware, isEnrolled, types] = await Promise.all([
|
|
1439
|
-
LocalAuthentication.hasHardwareAsync(),
|
|
1440
|
-
LocalAuthentication.isEnrolledAsync(),
|
|
1441
|
-
LocalAuthentication.supportedAuthenticationTypesAsync()
|
|
1442
|
-
]);
|
|
1443
|
-
const supportedTypes = types.map((t) => {
|
|
1444
|
-
if (t === LocalAuthentication.AuthenticationType.FINGERPRINT) return "fingerprint";
|
|
1445
|
-
if (t === LocalAuthentication.AuthenticationType.FACIAL_RECOGNITION) return "face";
|
|
1446
|
-
if (t === LocalAuthentication.AuthenticationType.IRIS) return "iris";
|
|
1447
|
-
return "none";
|
|
1448
|
-
}).filter((t) => t !== "none");
|
|
1449
|
-
return { hasHardware, isEnrolled, supportedTypes };
|
|
1450
|
-
}
|
|
1451
|
-
async function authenticate(opts) {
|
|
1452
|
-
try {
|
|
1453
|
-
const res = await LocalAuthentication.authenticateAsync({
|
|
1454
|
-
promptMessage: opts.reason,
|
|
1455
|
-
cancelLabel: opts.cancelLabel,
|
|
1456
|
-
disableDeviceFallback: opts.fallbackToPasscode === false
|
|
1457
|
-
});
|
|
1458
|
-
if (res.success) return { success: true };
|
|
1459
|
-
return { success: false, error: res.error || "unknown" };
|
|
1460
|
-
} catch (e) {
|
|
1461
|
-
return { success: false, error: String(e?.message || e) };
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
|
|
1465
|
-
// src/device-bridge/sharing.ts
|
|
1466
|
-
var sharing_exports = {};
|
|
1467
|
-
__export(sharing_exports, {
|
|
1468
|
-
isAvailable: () => isAvailable,
|
|
1469
|
-
shareFile: () => shareFile
|
|
1470
|
-
});
|
|
1471
|
-
import * as Sharing from "expo-sharing";
|
|
1472
|
-
async function shareFile(fileUri, opts = {}) {
|
|
1473
|
-
try {
|
|
1474
|
-
const available = await Sharing.isAvailableAsync();
|
|
1475
|
-
if (!available) return { ok: false, error: "sharing_unavailable" };
|
|
1476
|
-
await Sharing.shareAsync(fileUri, opts);
|
|
1477
|
-
return { ok: true };
|
|
1478
|
-
} catch (e) {
|
|
1479
|
-
return { ok: false, error: String(e?.message || e) };
|
|
1480
|
-
}
|
|
1481
|
-
}
|
|
1482
|
-
async function isAvailable() {
|
|
1483
|
-
return Sharing.isAvailableAsync();
|
|
1484
|
-
}
|
|
1485
|
-
|
|
1486
|
-
// src/device-bridge/scanner.ts
|
|
1487
|
-
var scanner_exports = {};
|
|
1488
|
-
__export(scanner_exports, {
|
|
1489
|
-
CameraView: () => CameraView,
|
|
1490
|
-
normalizeResult: () => normalizeResult,
|
|
1491
|
-
requestPermissions: () => requestPermissions,
|
|
1492
|
-
useCameraPermissions: () => useCameraPermissions
|
|
1493
|
-
});
|
|
1494
|
-
import { CameraView, useCameraPermissions } from "expo-camera";
|
|
1495
|
-
async function requestPermissions() {
|
|
1496
|
-
const { Camera: Camera2 } = await import("expo-camera");
|
|
1497
|
-
const { status } = await Camera2.requestCameraPermissionsAsync();
|
|
1498
|
-
return { granted: status === "granted", status };
|
|
1499
|
-
}
|
|
1500
|
-
function normalizeResult(r) {
|
|
1501
|
-
return { type: r.type, data: r.data };
|
|
1502
|
-
}
|
|
1503
|
-
|
|
1504
|
-
// src/device-bridge/index.ts
|
|
1505
|
-
var DeviceBridge = class {
|
|
1506
|
-
constructor(config) {
|
|
1507
|
-
// Capability registry
|
|
1508
|
-
this.caps = /* @__PURE__ */ new Map();
|
|
1509
|
-
this.syncTimers = /* @__PURE__ */ new Map();
|
|
1510
|
-
this.subscriptions = /* @__PURE__ */ new Map();
|
|
1511
|
-
this.domainStatus = /* @__PURE__ */ new Map();
|
|
1512
|
-
this.running = false;
|
|
1513
|
-
this.config = config;
|
|
1514
|
-
this.relay = new RelayClient({
|
|
1515
|
-
relayUrl: config.relayUrl,
|
|
1516
|
-
deviceId: config.deviceId,
|
|
1517
|
-
tenant: config.tenant,
|
|
1518
|
-
actorId: config.actorId,
|
|
1519
|
-
apiKey: config.apiKey
|
|
1520
|
-
});
|
|
1521
|
-
this.queuePoller = new QueuePoller({
|
|
1522
|
-
relayClient: this.relay,
|
|
1523
|
-
pollInterval: 5e3,
|
|
1524
|
-
onActionExecuted: () => this.notifyStatusChange(),
|
|
1525
|
-
onError: (error) => this.config.onError?.({
|
|
1526
|
-
domain: "actions",
|
|
1527
|
-
message: error.message,
|
|
1528
|
-
code: "ACTION_ERROR",
|
|
1529
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1530
|
-
})
|
|
1531
|
-
});
|
|
1532
|
-
}
|
|
1533
|
-
// ─── Capability Registration ───
|
|
1534
|
-
/**
|
|
1535
|
-
* Register a capability. Does NOT request permission — just makes it known.
|
|
1536
|
-
* Can be called before or after startSync().
|
|
1537
|
-
*/
|
|
1538
|
-
register(cap) {
|
|
1539
|
-
this.caps.set(cap.id, cap);
|
|
1540
|
-
this.domainStatus.set(cap.id, {
|
|
1541
|
-
domain: cap.id,
|
|
1542
|
-
lastSync: null,
|
|
1543
|
-
state: "idle"
|
|
1544
|
-
});
|
|
1545
|
-
}
|
|
1546
|
-
/**
|
|
1547
|
-
* Register multiple capabilities at once.
|
|
1548
|
-
*/
|
|
1549
|
-
registerAll(caps) {
|
|
1550
|
-
for (const cap of caps) {
|
|
1551
|
-
this.register(cap);
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
/**
|
|
1555
|
-
* Get a registered capability by ID.
|
|
1556
|
-
* Use this to read name/description/reason for your permission UI.
|
|
1557
|
-
*/
|
|
1558
|
-
getCapability(id) {
|
|
1559
|
-
return this.caps.get(id);
|
|
1560
|
-
}
|
|
1561
|
-
/**
|
|
1562
|
-
* Get all registered capabilities with their current status.
|
|
1563
|
-
*/
|
|
1564
|
-
listCapabilities() {
|
|
1565
|
-
return Array.from(this.caps.values()).map((cap) => ({
|
|
1566
|
-
id: cap.id,
|
|
1567
|
-
name: cap.name,
|
|
1568
|
-
permission: cap.permissionState,
|
|
1569
|
-
sync: this.domainStatus.get(cap.id) || {
|
|
1570
|
-
domain: cap.id,
|
|
1571
|
-
lastSync: null,
|
|
1572
|
-
state: "idle"
|
|
1573
|
-
}
|
|
1574
|
-
}));
|
|
1575
|
-
}
|
|
1576
|
-
// ─── Permissions (host app controls the flow) ───
|
|
1577
|
-
/**
|
|
1578
|
-
* Request permission for ONE capability.
|
|
1579
|
-
* Call after showing your own explanation screen.
|
|
1580
|
-
* If bridge is already running and granted, syncing starts automatically.
|
|
1581
|
-
*/
|
|
1582
|
-
async requestPermission(capId) {
|
|
1583
|
-
const cap = this.caps.get(capId);
|
|
1584
|
-
if (!cap) throw new Error(`Capability "${capId}" not registered`);
|
|
1585
|
-
if (!cap.requiresPermission) {
|
|
1586
|
-
cap.permissionState = "not_needed";
|
|
1587
|
-
return true;
|
|
1588
|
-
}
|
|
1589
|
-
const granted = await cap.requestPermission();
|
|
1590
|
-
cap.permissionState = granted ? "granted" : "denied";
|
|
1591
|
-
if (granted && this.running) {
|
|
1592
|
-
this.startCapabilitySync(cap);
|
|
1593
|
-
}
|
|
1594
|
-
this.notifyStatusChange();
|
|
1595
|
-
return granted;
|
|
1596
|
-
}
|
|
1597
|
-
/**
|
|
1598
|
-
* Check permission WITHOUT prompting.
|
|
1599
|
-
*/
|
|
1600
|
-
async checkPermission(capId) {
|
|
1601
|
-
const cap = this.caps.get(capId);
|
|
1602
|
-
if (!cap) throw new Error(`Capability "${capId}" not registered`);
|
|
1603
|
-
if (!cap.requiresPermission) return "not_needed";
|
|
1604
|
-
const granted = await cap.checkPermission();
|
|
1605
|
-
cap.permissionState = granted ? "granted" : cap.permissionState;
|
|
1606
|
-
return cap.permissionState;
|
|
1607
|
-
}
|
|
1608
|
-
/**
|
|
1609
|
-
* Request ALL permissions sequentially (one at a time, not bombarded).
|
|
1610
|
-
*/
|
|
1611
|
-
async requestAllPermissions() {
|
|
1612
|
-
const results = /* @__PURE__ */ new Map();
|
|
1613
|
-
for (const [id, cap] of this.caps) {
|
|
1614
|
-
if (!cap.requiresPermission) {
|
|
1615
|
-
cap.permissionState = "not_needed";
|
|
1616
|
-
results.set(id, true);
|
|
1617
|
-
continue;
|
|
1618
|
-
}
|
|
1619
|
-
const granted = await cap.requestPermission();
|
|
1620
|
-
cap.permissionState = granted ? "granted" : "denied";
|
|
1621
|
-
results.set(id, granted);
|
|
1622
|
-
}
|
|
1623
|
-
this.notifyStatusChange();
|
|
1624
|
-
return results;
|
|
1625
|
-
}
|
|
1626
|
-
/**
|
|
1627
|
-
* Get capabilities that still need permission.
|
|
1628
|
-
* Use this to drive a "next permission" onboarding flow.
|
|
1629
|
-
*/
|
|
1630
|
-
getPendingPermissions() {
|
|
1631
|
-
return Array.from(this.caps.values()).filter(
|
|
1632
|
-
(cap) => cap.requiresPermission && cap.permissionState === "not_requested"
|
|
1633
|
-
);
|
|
1634
|
-
}
|
|
1635
|
-
// ─── Sync Control ───
|
|
1636
|
-
/**
|
|
1637
|
-
* Start syncing all permitted capabilities + action queue poller.
|
|
1638
|
-
*/
|
|
1639
|
-
async startSync() {
|
|
1640
|
-
if (this.running) return;
|
|
1641
|
-
this.running = true;
|
|
1642
|
-
for (const [, cap] of this.caps) {
|
|
1643
|
-
const permitted = !cap.requiresPermission || cap.permissionState === "granted" || cap.permissionState === "not_needed";
|
|
1644
|
-
if (permitted) {
|
|
1645
|
-
this.startCapabilitySync(cap);
|
|
1646
|
-
} else {
|
|
1647
|
-
this.updateDomainStatus(cap.id, "error", "Permission not granted");
|
|
1648
|
-
}
|
|
1649
|
-
}
|
|
1650
|
-
this.queuePoller.start();
|
|
1651
|
-
if (this.config.tenant && this.config.apiKey) {
|
|
1652
|
-
registerBackgroundSync({
|
|
1653
|
-
relayUrl: this.config.relayUrl,
|
|
1654
|
-
deviceId: this.config.deviceId,
|
|
1655
|
-
tenant: this.config.tenant,
|
|
1656
|
-
actorId: this.config.actorId,
|
|
1657
|
-
apiKey: this.config.apiKey
|
|
1658
|
-
}).catch((e) => console.warn("[adas/bridge] registerBackgroundSync failed:", e?.message || e));
|
|
1659
|
-
}
|
|
1660
|
-
this.notifyStatusChange();
|
|
1661
|
-
}
|
|
1662
|
-
/**
|
|
1663
|
-
* Stop all syncing.
|
|
1664
|
-
*/
|
|
1665
|
-
stopSync() {
|
|
1666
|
-
this.running = false;
|
|
1667
|
-
for (const [id, timer] of this.syncTimers) {
|
|
1668
|
-
clearInterval(timer);
|
|
1669
|
-
this.updateDomainStatus(id, "stopped");
|
|
1670
|
-
}
|
|
1671
|
-
this.syncTimers.clear();
|
|
1672
|
-
for (const unsub of this.subscriptions.values()) {
|
|
1673
|
-
try {
|
|
1674
|
-
unsub();
|
|
1675
|
-
} catch {
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1678
|
-
this.subscriptions.clear();
|
|
1679
|
-
this.queuePoller.stop();
|
|
1680
|
-
unregisterBackgroundSync().catch((e) => console.warn("[adas/bridge] unregisterBackgroundSync failed:", e?.message || e));
|
|
1681
|
-
this.notifyStatusChange();
|
|
1682
|
-
}
|
|
1683
|
-
/**
|
|
1684
|
-
* Force immediate sync of all permitted capabilities.
|
|
1685
|
-
*/
|
|
1686
|
-
async syncNow() {
|
|
1687
|
-
const promises = Array.from(this.caps.values()).filter((c) => c.permissionState === "granted" || c.permissionState === "not_needed").map((c) => this.syncCapability(c));
|
|
1688
|
-
await Promise.allSettled(promises);
|
|
1689
|
-
}
|
|
1690
|
-
/**
|
|
1691
|
-
* Force immediate sync of a single capability by id. Used by the host to
|
|
1692
|
-
* refresh fast-moving sources (battery) on app-foreground so the relay
|
|
1693
|
-
* gets a fresh reading the moment the user opens the app.
|
|
1694
|
-
*/
|
|
1695
|
-
async syncCapabilityNow(capId) {
|
|
1696
|
-
const cap = this.caps.get(capId);
|
|
1697
|
-
if (!cap) return;
|
|
1698
|
-
const permitted = !cap.requiresPermission || cap.permissionState === "granted" || cap.permissionState === "not_needed";
|
|
1699
|
-
if (!permitted) return;
|
|
1700
|
-
await this.syncCapability(cap);
|
|
1701
|
-
}
|
|
1702
|
-
/**
|
|
1703
|
-
* Get current bridge status.
|
|
1704
|
-
*/
|
|
1705
|
-
getStatus() {
|
|
1706
|
-
return {
|
|
1707
|
-
running: this.running,
|
|
1708
|
-
capabilities: this.listCapabilities(),
|
|
1709
|
-
lastFullSync: this.getLastFullSync(),
|
|
1710
|
-
pendingActions: this.relay.getOfflineQueueSize()
|
|
1711
|
-
};
|
|
1712
|
-
}
|
|
1713
|
-
/**
|
|
1714
|
-
* Check if the relay is reachable.
|
|
1715
|
-
*/
|
|
1716
|
-
async checkConnectivity() {
|
|
1717
|
-
return this.relay.ping();
|
|
1718
|
-
}
|
|
1719
|
-
// ─── Internal ───
|
|
1720
|
-
startCapabilitySync(cap) {
|
|
1721
|
-
if (this.syncTimers.has(cap.id)) return;
|
|
1722
|
-
const interval = this.config.syncIntervals?.[cap.id] ?? cap.defaultInterval;
|
|
1723
|
-
this.syncCapability(cap);
|
|
1724
|
-
const timer = setInterval(() => this.syncCapability(cap), interval);
|
|
1725
|
-
this.syncTimers.set(cap.id, timer);
|
|
1726
|
-
if (cap.subscribe && !this.subscriptions.has(cap.id)) {
|
|
1727
|
-
try {
|
|
1728
|
-
const unsub = cap.subscribe(() => {
|
|
1729
|
-
if (this.running) this.syncCapability(cap);
|
|
1730
|
-
});
|
|
1731
|
-
this.subscriptions.set(cap.id, unsub);
|
|
1732
|
-
} catch (err) {
|
|
1733
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1734
|
-
this.config.onError?.({
|
|
1735
|
-
domain: cap.id,
|
|
1736
|
-
message: `subscribe failed: ${message}`,
|
|
1737
|
-
code: "SUBSCRIBE_ERROR",
|
|
1738
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1739
|
-
});
|
|
1740
|
-
}
|
|
1741
|
-
}
|
|
1742
|
-
}
|
|
1743
|
-
async syncCapability(cap) {
|
|
1744
|
-
if (!this.running) return;
|
|
1745
|
-
this.updateDomainStatus(cap.id, "syncing");
|
|
1746
|
-
try {
|
|
1747
|
-
const data = await cap.collect();
|
|
1748
|
-
if (data === null) {
|
|
1749
|
-
this.updateDomainStatus(cap.id, "idle");
|
|
1750
|
-
return;
|
|
1751
|
-
}
|
|
1752
|
-
const result = await this.relay.pushDomain(cap.id, data);
|
|
1753
|
-
if (result.ok) {
|
|
1754
|
-
this.updateDomainStatus(cap.id, "idle");
|
|
1755
|
-
} else {
|
|
1756
|
-
this.updateDomainStatus(cap.id, "error", result.error);
|
|
1757
|
-
this.config.onError?.({
|
|
1758
|
-
domain: cap.id,
|
|
1759
|
-
message: result.error || "Push failed",
|
|
1760
|
-
code: "RELAY_ERROR",
|
|
1761
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1762
|
-
});
|
|
1763
|
-
}
|
|
1764
|
-
} catch (error) {
|
|
1765
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1766
|
-
this.updateDomainStatus(cap.id, "error", message);
|
|
1767
|
-
this.config.onError?.({
|
|
1768
|
-
domain: cap.id,
|
|
1769
|
-
message,
|
|
1770
|
-
code: "COLLECTOR_ERROR",
|
|
1771
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1772
|
-
});
|
|
1773
|
-
}
|
|
1774
|
-
}
|
|
1775
|
-
updateDomainStatus(id, state, error) {
|
|
1776
|
-
const current = this.domainStatus.get(id);
|
|
1777
|
-
this.domainStatus.set(id, {
|
|
1778
|
-
domain: id,
|
|
1779
|
-
lastSync: state === "idle" ? (/* @__PURE__ */ new Date()).toISOString() : current?.lastSync ?? null,
|
|
1780
|
-
state,
|
|
1781
|
-
error,
|
|
1782
|
-
itemCount: current?.itemCount
|
|
1783
|
-
});
|
|
1784
|
-
}
|
|
1785
|
-
getLastFullSync() {
|
|
1786
|
-
const syncs = Array.from(this.domainStatus.values()).map((d) => d.lastSync).filter((s) => s !== null);
|
|
1787
|
-
if (syncs.length === 0) return null;
|
|
1788
|
-
return syncs.sort()[0];
|
|
1789
|
-
}
|
|
1790
|
-
notifyStatusChange() {
|
|
1791
|
-
this.config.onStatusChange?.(this.getStatus());
|
|
1792
|
-
}
|
|
1793
|
-
};
|
|
1794
|
-
|
|
1795
999
|
export {
|
|
1796
1000
|
RelayClient,
|
|
1797
|
-
QueuePoller,
|
|
1798
1001
|
location_exports,
|
|
1799
1002
|
connectivity_exports,
|
|
1800
1003
|
capabilities_exports,
|
|
1801
1004
|
BACKGROUND_TASK_NAME,
|
|
1802
1005
|
registerBackgroundSync,
|
|
1803
1006
|
unregisterBackgroundSync,
|
|
1804
|
-
syncDomains
|
|
1805
|
-
biometrics_exports,
|
|
1806
|
-
sharing_exports,
|
|
1807
|
-
scanner_exports,
|
|
1808
|
-
DeviceBridge
|
|
1007
|
+
syncDomains
|
|
1809
1008
|
};
|
|
1810
|
-
//# sourceMappingURL=chunk-
|
|
1009
|
+
//# sourceMappingURL=chunk-C62NQA4F.mjs.map
|