docs: add daily notification duplicate/fallback analysis and plugin handoff

This commit is contained in:
Jose Olarte III
2026-03-02 20:35:08 +08:00
parent 7e2b16ddad
commit 96ae89bcfa
2 changed files with 514 additions and 327 deletions

View File

@@ -0,0 +1,169 @@
# Daily Notification: Why Extra Notifications With Fallback / "Starred Projects" Still Fire
**Date:** 2026-03-02
**Context:** After previous fixes (see `DAILY_NOTIFICATION_BUG_DIAGNOSIS.md` and `plugin-feedback-android-rollover-double-fire-and-user-content.md`), duplicate notifications and fallback/"starred projects" text still occur. This doc explains root causes and where fixes must happen.
---
## Summary of Whats Happening
1. **Extra notification(s)** fire at a different time (e.g. ~3 min early) or at the same time as the user-set one.
2. **Wrong text** appears: either generic fallback ("Daily Update" / "Good morning! Ready to make today amazing?") or the apps placeholder ("TimeSafari Update" / "Check your starred projects for updates!").
3. The **correct** notification (user-set time and message) can still fire as well, so the user sees both correct and wrong notifications.
---
## Root Causes
### 1. Second alarm from prefetch (UUID / fallback)
**Mechanism**
- The plugin has two scheduling paths:
- **NotifyReceiver** (AlarmManager): used for the apps single daily reminder; uses `scheduleId` (e.g. `daily_timesafari_reminder`) and carries title/body in the Intent.
- **DailyNotificationScheduler** (legacy): used by **DailyNotificationFetchWorker** when prefetch runs and then calls `scheduleNotificationIfNeeded(fallbackContent)`. That creates a **second** alarm with `notification_id` = **UUID** (from `createEmergencyFallbackContent()` or from fetcher placeholder).
- **ScheduleHelper** correctly **does not** enqueue prefetch for static reminders (see comment in `DailyNotificationPlugin.kt` ~2686: "Do not enqueue prefetch for static reminders"). So **new** schedules from the app no longer create a prefetch job.
- However:
- **Existing** WorkManager prefetch jobs (tag `daily_notification_fetch`) that were enqueued **before** that fix (or by an older build) are still pending. When they run, fetch fails or returns placeholder → `useFallbackContent()``scheduleNotificationIfNeeded(fallbackContent)`**second alarm with UUID**.
- That UUID alarm is **not** stored in the Schedule table. So when the user later calls `scheduleDailyNotification`, **cleanupExistingNotificationSchedules** only cancels alarms for schedule IDs that exist in the DB (e.g. `daily_timesafari_reminder`, `daily_rollover_*`). The **UUID alarm is never cancelled**.
- **Result:** You can have two alarms: one for `daily_timesafari_reminder` (correct) and one for a UUID (fallback text). If the UUID alarm was set for a slightly different time (e.g. from an old rollover), you get two notifications at two times.
**Where the fallback text comes from (plugin)**
- **DailyNotificationFetchWorker** (in both apps `node_modules` plugin and the standalone repo):
- On failed fetch after max retries: `useFallbackContent(scheduledTime)``createEmergencyFallbackContent(scheduledTime)` → title "Daily Update", body "🌅 Good morning! Ready to make today amazing?".
- That content is saved and then **scheduled** via `scheduleNotificationIfNeeded(fallbackContent)`, which uses **DailyNotificationScheduler** (legacy) and assigns a **new UUID** to the content. So the second alarm fires with that UUID and shows that fallback text.
### 2. Prefetch WorkManager jobs not cancelled when user reschedules
- **scheduleDailyNotification** (plugin) calls:
- `ScheduleHelper.cleanupExistingNotificationSchedules(...)` → cancels **alarms** for all DB schedules (except current `scheduleId`).
- `ScheduleHelper.scheduleDailyNotification(...)` → cancels alarm for current `scheduleId`, schedules NotifyReceiver alarm, **does not** enqueue prefetch.
- It does **not** cancel **WorkManager** jobs. So any already-enqueued prefetch work (tag `daily_notification_fetch`) remains. When that work runs, it creates the second (UUID) alarm as above.
- **ScheduleHelper** has `cancelAllWorkManagerJobs(context)` (cancels tags `prefetch`, `daily_notification_fetch`, etc.), but **nothing calls it** in the schedule path. So pending prefetch jobs are left in place.
**Fix (plugin):** When the app calls `scheduleDailyNotification`, **cancel all fetch-related WorkManager work** (e.g. call `ScheduleHelper.cancelAllWorkManagerJobs(context)` or a helper that only cancels `daily_notification_fetch` and `prefetch`) **before** or **right after** `cleanupExistingNotificationSchedules`. That prevents any pending prefetch from running and creating a UUID alarm later.
### 3. "Starred projects" message from the apps native fetcher
- **TimeSafariNativeFetcher** (`android/app/src/main/java/app/timesafari/TimeSafariNativeFetcher.java`) is still a **placeholder**: it always returns:
- Title: `"TimeSafari Update"`
- Body: `"Check your starred projects for updates!"`
- That text is used whenever the plugin **fetches** content and then displays it:
- **DailyNotificationFetchWorker**: on “successful” fetch it saves and schedules the fetchers result; for your app that result is the placeholder, so any notification created from that path shows “starred projects”.
- **DailyNotificationWorker** (JIT path): when `is_static_reminder` is false and content is loaded from Room by `notification_id`, if the worker then does a JIT refresh (e.g. content stale), it calls `DailyNotificationFetcher.fetchContentImmediately()` which can use the apps native fetcher and **overwrite** title/body with the placeholder.
- So “starred projects” appears on any notification that goes through a **fetch** path (prefetch success or JIT) instead of the **static reminder** path (Intent title/body or Room by canonical `schedule_id`).
**Fix (app):** For a static-reminder-only flow, the plugin should not run prefetch (already done) and should not overwrite with fetcher in JIT for static reminders. Reducing duplicate/out-of-schedule alarms (fixes above) ensures the main run is the static one. Optionally, implement **TimeSafariNativeFetcher** to return real content if you ever want “fetch-based” notifications; until then, the only path that should show user text is the NotifyReceiver alarm with `daily_timesafari_reminder` and title/body from Intent or from Room by `schedule_id`.
### 4. Rollover / Room content keyed by run-specific id
- When an alarm fires with `notification_id` = **UUID** or **notify_<timestamp>** (and no or missing title/body in the Intent), the Worker treats it as **non-static**. It loads content from Room by that `notification_id`. The entity for `daily_timesafari_reminder` (user title/body) is stored under a **different** id, so the Worker either finds nothing or finds content written by prefetch/fallback for that run → wrong text.
- When the alarm is the **correct** one (`daily_timesafari_reminder`) and Intent has title/body (or `schedule_id`), the Worker uses static reminder or resolves by `schedule_id` and shows user text. So the main fix is to **avoid creating the UUID/notify_* run in the first place** (cancel prefetch work; no second alarm). Rollover for the static reminder already passes `scheduleId` and title/body in the Intent (NotifyReceiver puts them in the PendingIntent), so once theres only one alarm, rollover should keep user text.
---
## Where Fixes Must Happen
### Plugin (daily-notification-plugin)
**1. Cancel prefetch (and related) WorkManager jobs when scheduling**
- **File:** `DailyNotificationPlugin.kt` (or wherever `scheduleDailyNotification` is implemented).
- **Change:** When handling `scheduleDailyNotification`, after `cleanupExistingNotificationSchedules` and before (or after) `ScheduleHelper.scheduleDailyNotification`, call a method that cancels all WorkManager work that can create a second alarm. Prefer reusing **ScheduleHelper.cancelAllWorkManagerJobs(context)** or adding a small helper that cancels only fetch-related tags (e.g. `daily_notification_fetch`, `prefetch`) so you dont cancel display/dismiss work unnecessarily.
- **Effect:** Pending prefetch jobs from older builds or previous flows will not run, so no new UUID alarm is created and no extra notification with fallback text.
**2. (Already done) Do not enqueue prefetch for static reminders**
- **ScheduleHelper.scheduleDailyNotification** already does **not** enqueue FetchWorker for static reminders. No change needed here; just ensure no other code path enqueues prefetch for the apps single daily reminder.
**3. (Optional) DailyNotificationFetchWorker: skip scheduling second alarm for static-reminder schedules**
- If you ever enqueue prefetch with an explicit “static reminder” flag, in **DailyNotificationFetchWorker** inside `useFallbackContent` / `scheduleNotificationIfNeeded`, skip calling `scheduleNotificationIfNeeded` when that flag is set. For your current setup (no prefetch for static), this is redundant but makes the contract clear and future-proof.
**4. Receiver: no DB on main thread**
- Your **DailyNotificationReceiver** in the apps plugin only reads Intent extras and enqueues work; it does not read Room on the main thread. If you still see `db_fallback_failed` in logcat, the failing DB access is elsewhere (e.g. another receiver or an old build). Ensure no BroadcastReceiver does Room/DB access on the main thread; resolve title/body in the Worker from `schedule_id` if Intent lacks them.
### App (crowd-funder-for-time-pwa)
**Scope: static reminders only.** For fixing static reminders, **no app code changes are required.** Real fetch-based content can be added later.
**1. TimeSafariNativeFetcher**
- **File:** `android/app/src/main/java/app/timesafari/TimeSafariNativeFetcher.java`
- **Current behavior:** Placeholder that returns `"TimeSafari Update"` / `"Check your starred projects for updates!"` (expected).
- **For static reminders now:** Leave as-is. The plugin fix (cancel prefetch work when scheduling) ensures the only notification path is the static one; the fetcher is never used for display in that flow. No change needed.
- **Later (optional):** When you implement real-world content fetching, replace the placeholder here so any future fetch-driven notifications show real content.
**2. Build and dependency**
- After plugin changes, ensure the app uses the updated plugin (point `package.json` at the fixed repo or publish and bump version), then **clean build** Android (`./gradlew clean`, rebuild, reinstall). Confirming the APK contains the plugin version that cancels prefetch work and does not enqueue prefetch for static reminders avoids stale behavior from old builds.
---
## Verification After Fixes
1. **Single notification, user text**
- Set daily reminder with a **distinct** title/body and a time 23 minutes ahead. Wait until that time.
- **Expect:** Exactly **one** notification at that time with your text. No second notification (no UUID, no “Daily Update” or “starred projects”).
2. **No out-of-schedule notification**
- Change reminder time (e.g. from 21:53 to 21:56) and save. Wait past 21:53 and until 21:56.
- **Expect:** No notification at 21:53; one at 21:56 with your text.
3. **Rollover**
- Let the correct notification fire once so rollover runs. Next day (or next occurrence) you should see **one** notification with the same user text.
4. **Logcat**
- No `display=<uuid>` at the same time as `static_reminder id=daily_timesafari_reminder`.
- After scheduling (e.g. edit and save), you should see prefetch/fetch work being cancelled if you add a log in the cancel path.
---
## Short Summary
| Issue | Cause | Fix location |
|-------|--------|--------------|
| Extra notification at same or different time | Prefetch WorkManager job still runs and creates second (UUID) alarm via legacy scheduler; that alarm is never cancelled on reschedule | **Plugin:** Cancel fetch-related WorkManager jobs when `scheduleDailyNotification` is called |
| Fallback text ("Daily Update" / "Good morning!") | FetchWorkers `useFallbackContent``scheduleNotificationIfNeeded` creates alarm with that content | **Plugin:** Same as above (no prefetch run → no fallback alarm); optionally FetchWorker skips scheduling when static-reminder flag set |
| "Starred projects" text | TimeSafariNativeFetcher placeholder used when a fetch path runs | **Plugin:** Same as above (no prefetch → no fetch path). **App:** No change for static reminders; leave fetcher as placeholder until real fetch is implemented. |
| Wrong content on rollover | Rollover run keyed by UUID or notify_* and no title/body in Intent → Worker loads from Room by that id → wrong/empty content | **Plugin:** Avoid creating UUID/notify_* run (cancel prefetch). Static rollover already passes schedule_id and title/body. |
The critical missing step is **cancelling prefetch (and fetch) WorkManager work when the user schedules or reschedules** the daily notification. That prevents any pending prefetch from running and creating the second alarm with fallback or “starred projects” text.
---
## For Cursor (plugin repo) — actionable handoff
Use this section when applying the fix in the **daily-notification-plugin** repo (e.g. with Cursor). Paste or @-mention this doc as context.
**Goal:** For static reminders, only one notification at the user's chosen time with user-set title/body. No extra notification from pending prefetch (UUID alarm with fallback or "starred projects" text).
**Root cause:** `scheduleDailyNotification` cleans up DB schedules and alarms but **does not cancel WorkManager prefetch jobs**. Any previously enqueued job (tag `daily_notification_fetch`) still runs, then creates a second alarm via `DailyNotificationScheduler` (UUID). That alarm is never cancelled on reschedule. Fix: cancel fetch-related WorkManager work when the user schedules.
**Change (required):**
1. **Cancel fetch-related WorkManager jobs when handling `scheduleDailyNotification`**
- **File:** `android/src/main/java/com/timesafari/dailynotification/DailyNotificationPlugin.kt`
- **Where:** In `scheduleDailyNotification(call)`, inside the `CoroutineScope(Dispatchers.IO).launch { ... }` block, **after** `ScheduleHelper.cleanupExistingNotificationSchedules(...)` and **before** `ScheduleHelper.scheduleDailyNotification(...)`.
- **What:** Call a method that cancels WorkManager work that can create a second alarm. Reuse **ScheduleHelper.cancelAllWorkManagerJobs(context)** (it already cancels `prefetch`, `daily_notification_fetch`, etc.). If you prefer not to cancel display/dismiss work, add a helper that only cancels `daily_notification_fetch` and `prefetch` and call that instead.
- **Example (using existing helper):**
```kotlin
ScheduleHelper.cancelAllWorkManagerJobs(context)
```
(If `cancelAllWorkManagerJobs` is suspend, call it with `runBlocking { }` or from the same coroutine scope.)
**No other plugin changes needed for this fix:** ScheduleHelper already does not enqueue prefetch for static reminders; the only missing step is cancelling **pending** prefetch work when the user schedules or reschedules.
**Files to look at (plugin Android):**
- `DailyNotificationPlugin.kt` — `scheduleDailyNotification(call)` (add cancel call after cleanup, before ScheduleHelper.scheduleDailyNotification).
- `ScheduleHelper` (in same file or separate) — `cancelAllWorkManagerJobs(context)` (already exists; ensure it cancels at least `daily_notification_fetch` and `prefetch`).

672
package-lock.json generated
View File

@@ -5095,6 +5095,312 @@
"node-forge": "^1.3.3" "node-forge": "^1.3.3"
} }
}, },
"node_modules/@expo/config": {
"version": "55.0.8",
"resolved": "https://registry.npmjs.org/@expo/config/-/config-55.0.8.tgz",
"integrity": "sha512-D7RYYHfErCgEllGxNwdYdkgzLna7zkzUECBV3snbUpf7RvIpB5l1LpCgzuVoc5KVew5h7N1Tn4LnT/tBSUZsQg==",
"license": "MIT",
"optional": true,
"dependencies": {
"@expo/config-plugins": "~55.0.6",
"@expo/config-types": "^55.0.5",
"@expo/json-file": "^10.0.12",
"@expo/require-utils": "^55.0.2",
"deepmerge": "^4.3.1",
"getenv": "^2.0.0",
"glob": "^13.0.0",
"resolve-from": "^5.0.0",
"resolve-workspace-root": "^2.0.0",
"semver": "^7.6.0",
"slugify": "^1.3.4"
}
},
"node_modules/@expo/config-plugins": {
"version": "55.0.6",
"resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-55.0.6.tgz",
"integrity": "sha512-cIox6FjZlFaaX40rbQ3DvP9e87S5X85H9uw+BAxJE5timkMhuByy3GAlOsj1h96EyzSiol7Q6YIGgY1Jiz4M+A==",
"license": "MIT",
"optional": true,
"dependencies": {
"@expo/config-types": "^55.0.5",
"@expo/json-file": "~10.0.12",
"@expo/plist": "^0.5.2",
"@expo/sdk-runtime-versions": "^1.0.0",
"chalk": "^4.1.2",
"debug": "^4.3.5",
"getenv": "^2.0.0",
"glob": "^13.0.0",
"resolve-from": "^5.0.0",
"semver": "^7.5.4",
"slugify": "^1.6.6",
"xcode": "^3.0.1",
"xml2js": "0.6.0"
}
},
"node_modules/@expo/config-plugins/node_modules/balanced-match": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"license": "MIT",
"optional": true,
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/@expo/config-plugins/node_modules/brace-expansion": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
"integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
"license": "MIT",
"optional": true,
"dependencies": {
"balanced-match": "^4.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/@expo/config-plugins/node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"optional": true,
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/@expo/config-plugins/node_modules/glob": {
"version": "13.0.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
"integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
"license": "BlueOak-1.0.0",
"optional": true,
"dependencies": {
"minimatch": "^10.2.2",
"minipass": "^7.1.3",
"path-scurry": "^2.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@expo/config-plugins/node_modules/lru-cache": {
"version": "11.2.6",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
"integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==",
"license": "BlueOak-1.0.0",
"optional": true,
"engines": {
"node": "20 || >=22"
}
},
"node_modules/@expo/config-plugins/node_modules/minimatch": {
"version": "10.2.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"license": "BlueOak-1.0.0",
"optional": true,
"dependencies": {
"brace-expansion": "^5.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@expo/config-plugins/node_modules/minipass": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
"integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
"license": "BlueOak-1.0.0",
"optional": true,
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/@expo/config-plugins/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT",
"optional": true
},
"node_modules/@expo/config-plugins/node_modules/path-scurry": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
"integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
"license": "BlueOak-1.0.0",
"optional": true,
"dependencies": {
"lru-cache": "^11.0.0",
"minipass": "^7.1.2"
},
"engines": {
"node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@expo/config-plugins/node_modules/resolve-from": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=8"
}
},
"node_modules/@expo/config-plugins/node_modules/xml2js": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.0.tgz",
"integrity": "sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==",
"license": "MIT",
"optional": true,
"dependencies": {
"sax": ">=0.6.0",
"xmlbuilder": "~11.0.0"
},
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/@expo/config-plugins/node_modules/xmlbuilder": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=4.0"
}
},
"node_modules/@expo/config-types": {
"version": "55.0.5",
"resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-55.0.5.tgz",
"integrity": "sha512-sCmSUZG4mZ/ySXvfyyBdhjivz8Q539X1NondwDdYG7s3SBsk+wsgPJzYsqgAG/P9+l0xWjUD2F+kQ1cAJ6NNLg==",
"license": "MIT",
"optional": true
},
"node_modules/@expo/config/node_modules/balanced-match": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"license": "MIT",
"optional": true,
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/@expo/config/node_modules/brace-expansion": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
"integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
"license": "MIT",
"optional": true,
"dependencies": {
"balanced-match": "^4.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/@expo/config/node_modules/glob": {
"version": "13.0.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
"integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
"license": "BlueOak-1.0.0",
"optional": true,
"dependencies": {
"minimatch": "^10.2.2",
"minipass": "^7.1.3",
"path-scurry": "^2.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@expo/config/node_modules/lru-cache": {
"version": "11.2.6",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
"integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==",
"license": "BlueOak-1.0.0",
"optional": true,
"engines": {
"node": "20 || >=22"
}
},
"node_modules/@expo/config/node_modules/minimatch": {
"version": "10.2.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"license": "BlueOak-1.0.0",
"optional": true,
"dependencies": {
"brace-expansion": "^5.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@expo/config/node_modules/minipass": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
"integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
"license": "BlueOak-1.0.0",
"optional": true,
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/@expo/config/node_modules/path-scurry": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
"integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
"license": "BlueOak-1.0.0",
"optional": true,
"dependencies": {
"lru-cache": "^11.0.0",
"minipass": "^7.1.2"
},
"engines": {
"node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@expo/config/node_modules/resolve-from": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=8"
}
},
"node_modules/@expo/devcert": { "node_modules/@expo/devcert": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/@expo/devcert/-/devcert-1.2.1.tgz", "resolved": "https://registry.npmjs.org/@expo/devcert/-/devcert-1.2.1.tgz",
@@ -5150,6 +5456,21 @@
"react-native": "*" "react-native": "*"
} }
}, },
"node_modules/@expo/env": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@expo/env/-/env-2.1.1.tgz",
"integrity": "sha512-rVvHC4I6xlPcg+mAO09ydUi2Wjv1ZytpLmHOSzvXzBAz9mMrJggqCe4s4dubjJvi/Ino/xQCLhbaLCnTtLpikg==",
"license": "MIT",
"optional": true,
"dependencies": {
"chalk": "^4.0.0",
"debug": "^4.3.4",
"getenv": "^2.0.0"
},
"engines": {
"node": ">=20.12.0"
}
},
"node_modules/@expo/fingerprint": { "node_modules/@expo/fingerprint": {
"version": "0.16.5", "version": "0.16.5",
"resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.16.5.tgz", "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.16.5.tgz",
@@ -5173,21 +5494,6 @@
"fingerprint": "bin/cli.js" "fingerprint": "bin/cli.js"
} }
}, },
"node_modules/@expo/fingerprint/node_modules/@expo/env": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@expo/env/-/env-2.1.1.tgz",
"integrity": "sha512-rVvHC4I6xlPcg+mAO09ydUi2Wjv1ZytpLmHOSzvXzBAz9mMrJggqCe4s4dubjJvi/Ino/xQCLhbaLCnTtLpikg==",
"license": "MIT",
"optional": true,
"dependencies": {
"chalk": "^4.0.0",
"debug": "^4.3.4",
"getenv": "^2.0.0"
},
"engines": {
"node": ">=20.12.0"
}
},
"node_modules/@expo/fingerprint/node_modules/balanced-match": { "node_modules/@expo/fingerprint/node_modules/balanced-match": {
"version": "4.0.4", "version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
@@ -5340,230 +5646,6 @@
"chalk": "^4.1.2" "chalk": "^4.1.2"
} }
}, },
"node_modules/@expo/local-build-cache-provider/node_modules/@expo/config": {
"version": "55.0.8",
"resolved": "https://registry.npmjs.org/@expo/config/-/config-55.0.8.tgz",
"integrity": "sha512-D7RYYHfErCgEllGxNwdYdkgzLna7zkzUECBV3snbUpf7RvIpB5l1LpCgzuVoc5KVew5h7N1Tn4LnT/tBSUZsQg==",
"license": "MIT",
"optional": true,
"dependencies": {
"@expo/config-plugins": "~55.0.6",
"@expo/config-types": "^55.0.5",
"@expo/json-file": "^10.0.12",
"@expo/require-utils": "^55.0.2",
"deepmerge": "^4.3.1",
"getenv": "^2.0.0",
"glob": "^13.0.0",
"resolve-from": "^5.0.0",
"resolve-workspace-root": "^2.0.0",
"semver": "^7.6.0",
"slugify": "^1.3.4"
}
},
"node_modules/@expo/local-build-cache-provider/node_modules/@expo/config-plugins": {
"version": "55.0.6",
"resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-55.0.6.tgz",
"integrity": "sha512-cIox6FjZlFaaX40rbQ3DvP9e87S5X85H9uw+BAxJE5timkMhuByy3GAlOsj1h96EyzSiol7Q6YIGgY1Jiz4M+A==",
"license": "MIT",
"optional": true,
"dependencies": {
"@expo/config-types": "^55.0.5",
"@expo/json-file": "~10.0.12",
"@expo/plist": "^0.5.2",
"@expo/sdk-runtime-versions": "^1.0.0",
"chalk": "^4.1.2",
"debug": "^4.3.5",
"getenv": "^2.0.0",
"glob": "^13.0.0",
"resolve-from": "^5.0.0",
"semver": "^7.5.4",
"slugify": "^1.6.6",
"xcode": "^3.0.1",
"xml2js": "0.6.0"
}
},
"node_modules/@expo/local-build-cache-provider/node_modules/@expo/config-types": {
"version": "55.0.5",
"resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-55.0.5.tgz",
"integrity": "sha512-sCmSUZG4mZ/ySXvfyyBdhjivz8Q539X1NondwDdYG7s3SBsk+wsgPJzYsqgAG/P9+l0xWjUD2F+kQ1cAJ6NNLg==",
"license": "MIT",
"optional": true
},
"node_modules/@expo/local-build-cache-provider/node_modules/@expo/plist": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.5.2.tgz",
"integrity": "sha512-o4xdVdBpe4aTl3sPMZ2u3fJH4iG1I768EIRk1xRZP+GaFI93MaR3JvoFibYqxeTmLQ1p1kNEVqylfUjezxx45g==",
"license": "MIT",
"optional": true,
"dependencies": {
"@xmldom/xmldom": "^0.8.8",
"base64-js": "^1.5.1",
"xmlbuilder": "^15.1.1"
}
},
"node_modules/@expo/local-build-cache-provider/node_modules/@xmldom/xmldom": {
"version": "0.8.11",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz",
"integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/@expo/local-build-cache-provider/node_modules/balanced-match": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"license": "MIT",
"optional": true,
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/@expo/local-build-cache-provider/node_modules/brace-expansion": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz",
"integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==",
"license": "MIT",
"optional": true,
"dependencies": {
"balanced-match": "^4.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/@expo/local-build-cache-provider/node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"optional": true,
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/@expo/local-build-cache-provider/node_modules/glob": {
"version": "13.0.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
"integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
"license": "BlueOak-1.0.0",
"optional": true,
"dependencies": {
"minimatch": "^10.2.2",
"minipass": "^7.1.3",
"path-scurry": "^2.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@expo/local-build-cache-provider/node_modules/lru-cache": {
"version": "11.2.6",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
"integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==",
"license": "BlueOak-1.0.0",
"optional": true,
"engines": {
"node": "20 || >=22"
}
},
"node_modules/@expo/local-build-cache-provider/node_modules/minimatch": {
"version": "10.2.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"license": "BlueOak-1.0.0",
"optional": true,
"dependencies": {
"brace-expansion": "^5.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@expo/local-build-cache-provider/node_modules/minipass": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
"integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
"license": "BlueOak-1.0.0",
"optional": true,
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/@expo/local-build-cache-provider/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT",
"optional": true
},
"node_modules/@expo/local-build-cache-provider/node_modules/path-scurry": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
"integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
"license": "BlueOak-1.0.0",
"optional": true,
"dependencies": {
"lru-cache": "^11.0.0",
"minipass": "^7.1.2"
},
"engines": {
"node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@expo/local-build-cache-provider/node_modules/resolve-from": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=8"
}
},
"node_modules/@expo/local-build-cache-provider/node_modules/xml2js": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.0.tgz",
"integrity": "sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==",
"license": "MIT",
"optional": true,
"dependencies": {
"sax": ">=0.6.0",
"xmlbuilder": "~11.0.0"
},
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/@expo/local-build-cache-provider/node_modules/xml2js/node_modules/xmlbuilder": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=4.0"
}
},
"node_modules/@expo/log-box": { "node_modules/@expo/log-box": {
"version": "55.0.7", "version": "55.0.7",
"resolved": "https://registry.npmjs.org/@expo/log-box/-/log-box-55.0.7.tgz", "resolved": "https://registry.npmjs.org/@expo/log-box/-/log-box-55.0.7.tgz",
@@ -5830,6 +5912,28 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/@expo/plist": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.5.2.tgz",
"integrity": "sha512-o4xdVdBpe4aTl3sPMZ2u3fJH4iG1I768EIRk1xRZP+GaFI93MaR3JvoFibYqxeTmLQ1p1kNEVqylfUjezxx45g==",
"license": "MIT",
"optional": true,
"dependencies": {
"@xmldom/xmldom": "^0.8.8",
"base64-js": "^1.5.1",
"xmlbuilder": "^15.1.1"
}
},
"node_modules/@expo/plist/node_modules/@xmldom/xmldom": {
"version": "0.8.11",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz",
"integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/@expo/require-utils": { "node_modules/@expo/require-utils": {
"version": "55.0.2", "version": "55.0.2",
"resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-55.0.2.tgz", "resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-55.0.2.tgz",
@@ -8547,8 +8651,8 @@
} }
}, },
"node_modules/@timesafari/daily-notification-plugin": { "node_modules/@timesafari/daily-notification-plugin": {
"version": "1.2.0", "version": "1.2.1",
"resolved": "git+https://gitea.anomalistdesign.com/trent_larson/daily-notification-plugin.git#cff7b659dcc72099104b17028b31e4976289614b", "resolved": "git+https://gitea.anomalistdesign.com/trent_larson/daily-notification-plugin.git#aa0eaa5389f67709240b88ad5955b2c89a7abf9e",
"license": "MIT", "license": "MIT",
"workspaces": [ "workspaces": [
"packages/*" "packages/*"
@@ -16426,70 +16530,6 @@
} }
} }
}, },
"node_modules/expo/node_modules/@expo/config": {
"version": "55.0.8",
"resolved": "https://registry.npmjs.org/@expo/config/-/config-55.0.8.tgz",
"integrity": "sha512-D7RYYHfErCgEllGxNwdYdkgzLna7zkzUECBV3snbUpf7RvIpB5l1LpCgzuVoc5KVew5h7N1Tn4LnT/tBSUZsQg==",
"license": "MIT",
"optional": true,
"dependencies": {
"@expo/config-plugins": "~55.0.6",
"@expo/config-types": "^55.0.5",
"@expo/json-file": "^10.0.12",
"@expo/require-utils": "^55.0.2",
"deepmerge": "^4.3.1",
"getenv": "^2.0.0",
"glob": "^13.0.0",
"resolve-from": "^5.0.0",
"resolve-workspace-root": "^2.0.0",
"semver": "^7.6.0",
"slugify": "^1.3.4"
}
},
"node_modules/expo/node_modules/@expo/config-plugins": {
"version": "55.0.6",
"resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-55.0.6.tgz",
"integrity": "sha512-cIox6FjZlFaaX40rbQ3DvP9e87S5X85H9uw+BAxJE5timkMhuByy3GAlOsj1h96EyzSiol7Q6YIGgY1Jiz4M+A==",
"license": "MIT",
"optional": true,
"dependencies": {
"@expo/config-types": "^55.0.5",
"@expo/json-file": "~10.0.12",
"@expo/plist": "^0.5.2",
"@expo/sdk-runtime-versions": "^1.0.0",
"chalk": "^4.1.2",
"debug": "^4.3.5",
"getenv": "^2.0.0",
"glob": "^13.0.0",
"resolve-from": "^5.0.0",
"semver": "^7.5.4",
"slugify": "^1.6.6",
"xcode": "^3.0.1",
"xml2js": "0.6.0"
}
},
"node_modules/expo/node_modules/@expo/config-types": {
"version": "55.0.5",
"resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-55.0.5.tgz",
"integrity": "sha512-sCmSUZG4mZ/ySXvfyyBdhjivz8Q539X1NondwDdYG7s3SBsk+wsgPJzYsqgAG/P9+l0xWjUD2F+kQ1cAJ6NNLg==",
"license": "MIT",
"optional": true
},
"node_modules/expo/node_modules/@expo/env": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@expo/env/-/env-2.1.1.tgz",
"integrity": "sha512-rVvHC4I6xlPcg+mAO09ydUi2Wjv1ZytpLmHOSzvXzBAz9mMrJggqCe4s4dubjJvi/Ino/xQCLhbaLCnTtLpikg==",
"license": "MIT",
"optional": true,
"dependencies": {
"chalk": "^4.0.0",
"debug": "^4.3.4",
"getenv": "^2.0.0"
},
"engines": {
"node": ">=20.12.0"
}
},
"node_modules/expo/node_modules/@expo/metro-config": { "node_modules/expo/node_modules/@expo/metro-config": {
"version": "55.0.9", "version": "55.0.9",
"resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-55.0.9.tgz", "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-55.0.9.tgz",
@@ -16526,18 +16566,6 @@
} }
} }
}, },
"node_modules/expo/node_modules/@expo/plist": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.5.2.tgz",
"integrity": "sha512-o4xdVdBpe4aTl3sPMZ2u3fJH4iG1I768EIRk1xRZP+GaFI93MaR3JvoFibYqxeTmLQ1p1kNEVqylfUjezxx45g==",
"license": "MIT",
"optional": true,
"dependencies": {
"@xmldom/xmldom": "^0.8.8",
"base64-js": "^1.5.1",
"xmlbuilder": "^15.1.1"
}
},
"node_modules/expo/node_modules/@expo/prebuild-config": { "node_modules/expo/node_modules/@expo/prebuild-config": {
"version": "55.0.7", "version": "55.0.7",
"resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-55.0.7.tgz", "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-55.0.7.tgz",
@@ -16699,16 +16727,6 @@
"license": "MIT", "license": "MIT",
"optional": true "optional": true
}, },
"node_modules/expo/node_modules/@xmldom/xmldom": {
"version": "0.8.11",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz",
"integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/expo/node_modules/ansi-regex": { "node_modules/expo/node_modules/ansi-regex": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",