@buoy-gg/images 7.0.40 → 7.0.42
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/README.md +12 -65
- package/lib/commonjs/autoCapture.js +1 -0
- package/lib/commonjs/autoCapture.web.js +1 -0
- package/lib/commonjs/capture/actions.js +1 -242
- package/lib/commonjs/capture/install.js +1 -45
- package/lib/commonjs/capture/install.web.js +1 -0
- package/lib/commonjs/capture/installExpoImage.js +1 -99
- package/lib/commonjs/capture/installRnImage.js +1 -128
- package/lib/commonjs/capture/nativeImageModules.js +1 -0
- package/lib/commonjs/capture/nativeImageModules.web.js +1 -0
- package/lib/commonjs/capture/overrides.js +1 -346
- package/lib/commonjs/capture/rnDecoratorEarly.js +1 -0
- package/lib/commonjs/capture/savings.js +1 -209
- package/lib/commonjs/capture/savings.web.js +1 -0
- package/lib/commonjs/capture/shared.js +1 -378
- package/lib/commonjs/components/ImageDetail.js +1 -454
- package/lib/commonjs/components/ImageDetailFooter.js +1 -158
- package/lib/commonjs/components/ImageRow.js +1 -156
- package/lib/commonjs/components/ImagesHeader.js +1 -63
- package/lib/commonjs/components/ImagesIcon.js +1 -19
- package/lib/commonjs/components/ImagesListFooter.js +1 -126
- package/lib/commonjs/components/ImagesModal.js +1 -240
- package/lib/commonjs/components/ImagesRoot.js +1 -24
- package/lib/commonjs/components/ImagesRoot.web.js +1 -0
- package/lib/commonjs/components/InsightsBar.js +1 -59
- package/lib/commonjs/export.js +1 -81
- package/lib/commonjs/format.js +1 -117
- package/lib/commonjs/hooks/useImageRecords.js +1 -20
- package/lib/commonjs/index.js +1 -261
- package/lib/commonjs/preset.js +1 -39
- package/lib/commonjs/register.js +1 -19
- package/lib/commonjs/registerEarly.js +1 -0
- package/lib/commonjs/remote.js +1 -42
- package/lib/commonjs/state.js +1 -58
- package/lib/commonjs/store/imageEventStore.js +1 -238
- package/lib/commonjs/store/insights.js +1 -76
- package/lib/commonjs/sync/imagesSyncAdapter.js +1 -360
- package/lib/commonjs/types.js +0 -1
- package/lib/module/autoCapture.js +1 -0
- package/lib/module/autoCapture.web.js +1 -0
- package/lib/module/capture/actions.js +1 -229
- package/lib/module/capture/install.js +1 -40
- package/lib/module/capture/install.web.js +1 -0
- package/lib/module/capture/installExpoImage.js +1 -94
- package/lib/module/capture/installRnImage.js +1 -122
- package/lib/module/capture/nativeImageModules.js +1 -0
- package/lib/module/capture/nativeImageModules.web.js +1 -0
- package/lib/module/capture/overrides.js +1 -329
- package/lib/module/capture/rnDecoratorEarly.js +1 -0
- package/lib/module/capture/savings.js +1 -205
- package/lib/module/capture/savings.web.js +1 -0
- package/lib/module/capture/shared.js +1 -369
- package/lib/module/components/ImageDetail.js +1 -450
- package/lib/module/components/ImageDetailFooter.js +1 -154
- package/lib/module/components/ImageRow.js +1 -152
- package/lib/module/components/ImagesHeader.js +1 -59
- package/lib/module/components/ImagesIcon.js +1 -12
- package/lib/module/components/ImagesListFooter.js +1 -122
- package/lib/module/components/ImagesModal.js +1 -235
- package/lib/module/components/ImagesRoot.js +1 -20
- package/lib/module/components/ImagesRoot.web.js +1 -0
- package/lib/module/components/InsightsBar.js +1 -55
- package/lib/module/export.js +1 -76
- package/lib/module/format.js +1 -108
- package/lib/module/hooks/useImageRecords.js +1 -16
- package/lib/module/index.js +1 -79
- package/lib/module/preset.js +1 -35
- package/lib/module/register.js +1 -19
- package/lib/module/registerEarly.js +1 -0
- package/lib/module/remote.js +1 -36
- package/lib/module/state.js +1 -54
- package/lib/module/store/imageEventStore.js +1 -221
- package/lib/module/store/insights.js +1 -71
- package/lib/module/sync/imagesSyncAdapter.js +1 -355
- package/lib/module/types.js +0 -1
- package/lib/typescript/autoCapture.d.ts +2 -0
- package/lib/typescript/autoCapture.web.d.ts +2 -0
- package/lib/typescript/capture/actions.d.ts +0 -17
- package/lib/typescript/capture/install.web.d.ts +6 -0
- package/lib/typescript/capture/installRnImage.d.ts +5 -0
- package/lib/typescript/capture/nativeImageModules.d.ts +4 -0
- package/lib/typescript/capture/nativeImageModules.web.d.ts +4 -0
- package/lib/typescript/capture/rnDecoratorEarly.d.ts +18 -0
- package/lib/typescript/capture/savings.web.d.ts +5 -0
- package/lib/typescript/components/ImageDetail.d.ts +0 -6
- package/lib/typescript/components/ImagesRoot.web.d.ts +2 -0
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/registerEarly.d.ts +10 -0
- package/lib/typescript/sync/imagesSyncAdapter.d.ts +0 -9
- package/lib/typescript/types.d.ts +2 -1
- package/lib/web/chunks/chunk-TTS5EH4R.mjs +1 -0
- package/lib/web/index.mjs +3 -0
- package/lib/web/register.mjs +1 -0
- package/package.json +52 -6
- package/register.js +1 -15
- package/lib/commonjs/capture/actions.js.map +0 -1
- package/lib/commonjs/capture/install.js.map +0 -1
- package/lib/commonjs/capture/installExpoImage.js.map +0 -1
- package/lib/commonjs/capture/installRnImage.js.map +0 -1
- package/lib/commonjs/capture/overrides.js.map +0 -1
- package/lib/commonjs/capture/savings.js.map +0 -1
- package/lib/commonjs/capture/shared.js.map +0 -1
- package/lib/commonjs/components/ImageDetail.js.map +0 -1
- package/lib/commonjs/components/ImageDetailFooter.js.map +0 -1
- package/lib/commonjs/components/ImageRow.js.map +0 -1
- package/lib/commonjs/components/ImagesHeader.js.map +0 -1
- package/lib/commonjs/components/ImagesIcon.js.map +0 -1
- package/lib/commonjs/components/ImagesListFooter.js.map +0 -1
- package/lib/commonjs/components/ImagesModal.js.map +0 -1
- package/lib/commonjs/components/ImagesRoot.js.map +0 -1
- package/lib/commonjs/components/InsightsBar.js.map +0 -1
- package/lib/commonjs/export.js.map +0 -1
- package/lib/commonjs/format.js.map +0 -1
- package/lib/commonjs/hooks/useImageRecords.js.map +0 -1
- package/lib/commonjs/index.js.map +0 -1
- package/lib/commonjs/preset.js.map +0 -1
- package/lib/commonjs/register.js.map +0 -1
- package/lib/commonjs/remote.js.map +0 -1
- package/lib/commonjs/state.js.map +0 -1
- package/lib/commonjs/store/imageEventStore.js.map +0 -1
- package/lib/commonjs/store/insights.js.map +0 -1
- package/lib/commonjs/sync/imagesSyncAdapter.js.map +0 -1
- package/lib/commonjs/types.js.map +0 -1
- package/lib/module/capture/actions.js.map +0 -1
- package/lib/module/capture/install.js.map +0 -1
- package/lib/module/capture/installExpoImage.js.map +0 -1
- package/lib/module/capture/installRnImage.js.map +0 -1
- package/lib/module/capture/overrides.js.map +0 -1
- package/lib/module/capture/savings.js.map +0 -1
- package/lib/module/capture/shared.js.map +0 -1
- package/lib/module/components/ImageDetail.js.map +0 -1
- package/lib/module/components/ImageDetailFooter.js.map +0 -1
- package/lib/module/components/ImageRow.js.map +0 -1
- package/lib/module/components/ImagesHeader.js.map +0 -1
- package/lib/module/components/ImagesIcon.js.map +0 -1
- package/lib/module/components/ImagesListFooter.js.map +0 -1
- package/lib/module/components/ImagesModal.js.map +0 -1
- package/lib/module/components/ImagesRoot.js.map +0 -1
- package/lib/module/components/InsightsBar.js.map +0 -1
- package/lib/module/export.js.map +0 -1
- package/lib/module/format.js.map +0 -1
- package/lib/module/hooks/useImageRecords.js.map +0 -1
- package/lib/module/index.js.map +0 -1
- package/lib/module/preset.js.map +0 -1
- package/lib/module/register.js.map +0 -1
- package/lib/module/remote.js.map +0 -1
- package/lib/module/state.js.map +0 -1
- package/lib/module/store/imageEventStore.js.map +0 -1
- package/lib/module/store/insights.js.map +0 -1
- package/lib/module/sync/imagesSyncAdapter.js.map +0 -1
- package/lib/module/types.js.map +0 -1
- package/lib/typescript/capture/actions.d.ts.map +0 -1
- package/lib/typescript/capture/install.d.ts.map +0 -1
- package/lib/typescript/capture/installExpoImage.d.ts.map +0 -1
- package/lib/typescript/capture/installRnImage.d.ts.map +0 -1
- package/lib/typescript/capture/overrides.d.ts.map +0 -1
- package/lib/typescript/capture/savings.d.ts.map +0 -1
- package/lib/typescript/capture/shared.d.ts.map +0 -1
- package/lib/typescript/components/ImageDetail.d.ts.map +0 -1
- package/lib/typescript/components/ImageDetailFooter.d.ts.map +0 -1
- package/lib/typescript/components/ImageRow.d.ts.map +0 -1
- package/lib/typescript/components/ImagesHeader.d.ts.map +0 -1
- package/lib/typescript/components/ImagesIcon.d.ts.map +0 -1
- package/lib/typescript/components/ImagesListFooter.d.ts.map +0 -1
- package/lib/typescript/components/ImagesModal.d.ts.map +0 -1
- package/lib/typescript/components/ImagesRoot.d.ts.map +0 -1
- package/lib/typescript/components/InsightsBar.d.ts.map +0 -1
- package/lib/typescript/export.d.ts.map +0 -1
- package/lib/typescript/format.d.ts.map +0 -1
- package/lib/typescript/hooks/useImageRecords.d.ts.map +0 -1
- package/lib/typescript/index.d.ts.map +0 -1
- package/lib/typescript/preset.d.ts.map +0 -1
- package/lib/typescript/register.d.ts.map +0 -1
- package/lib/typescript/remote.d.ts.map +0 -1
- package/lib/typescript/state.d.ts.map +0 -1
- package/lib/typescript/store/imageEventStore.d.ts.map +0 -1
- package/lib/typescript/store/insights.d.ts.map +0 -1
- package/lib/typescript/sync/imagesSyncAdapter.d.ts.map +0 -1
- package/lib/typescript/types.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
1
3
|
# @buoy-gg/images
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
Inspect image loads observed by Buoy's supported image hooks, including timing, dimensions and estimated decoded memory.
|
|
6
|
+
|
|
7
|
+
<a href="https://buoy.gg/buoy/latest/docs/tools/images"><b>Images guide</b></a> · <a href="https://buoy.gg">buoy.gg</a> · <a href="https://github.com/Buoy-gg/buoy">All tools</a>
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
[](https://www.npmjs.com/package/@buoy-gg/images) [](https://www.npmjs.com/package/@buoy-gg/images)
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
<a href="https://buoy.gg/buoy/latest/docs/tools/images"><img src="https://raw.githubusercontent.com/Buoy-gg/buoy/main/.github/readme/tools/images.png" alt="Watch the Images film on buoy.gg" width="640" /></a>
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
</div>
|
|
10
14
|
|
|
11
15
|
## Install
|
|
12
16
|
|
|
@@ -14,67 +18,10 @@ Native image loading can bypass JavaScript network capture. This package instrum
|
|
|
14
18
|
npm install @buoy-gg/core @buoy-gg/images
|
|
15
19
|
```
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
```ts
|
|
20
|
-
import "@buoy-gg/images/register";
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
> Why first? RN core's `<Image>` instrumentation uses React Native's official component-decorator hook, which must be installed before the Image module first evaluates. expo-image capture has no such constraint — it works no matter when the tool loads. If you skip the import, the tool tells you what it's missing instead of sitting silently empty.
|
|
24
|
-
|
|
25
|
-
## Before you start
|
|
26
|
-
|
|
27
|
-
Use a development build with `@buoy-gg/core` and a Free or Pro Buoy account key. From your app’s directory, sign in:
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
npx --package=@buoy-gg/core buoy login
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
For Expo, initialize Buoy before rendering the menu:
|
|
34
|
-
|
|
35
|
-
```tsx
|
|
36
|
-
import { Buoy } from "@buoy-gg/core";
|
|
37
|
-
|
|
38
|
-
Buoy.init({ licenseKey: process.env.EXPO_PUBLIC_BUOY_KEY });
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
The login command writes the Expo key to `.env.local`. For React Native CLI, pass the key from your app’s environment configuration; React Native does not load `.env.local` automatically. Mount `FloatingDevTools` inside the same providers as your screens and restart the development server after installation. The [Quick Start](https://buoy.gg/buoy/latest/docs/quick-start) shows the complete root component setup.
|
|
42
|
-
|
|
43
|
-
## Quick start
|
|
44
|
-
|
|
45
|
-
```tsx
|
|
46
|
-
import { FloatingDevTools } from "@buoy-gg/core";
|
|
47
|
-
|
|
48
|
-
export default function App() {
|
|
49
|
-
return (
|
|
50
|
-
<>
|
|
51
|
-
<YourApp />
|
|
52
|
-
<FloatingDevTools />
|
|
53
|
-
</>
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
The IMAGES tool appears in the menu. Open it and browse image loads observed during the session.
|
|
59
|
-
|
|
60
|
-
## Check the integration
|
|
61
|
-
|
|
62
|
-
Load a supported image after the capture hook is installed and inspect the entry. Treat cache results according to the image library’s available signals and decoded memory as an estimate. Native image paths outside those hooks may not appear.
|
|
63
|
-
|
|
64
|
-
## What you get
|
|
65
|
-
|
|
66
|
-
- **Live image registry** — observed RN `<Image>` and expo-image loads: thumbnail, source, status, load time, decoded dimensions
|
|
67
|
-
- **Cache verdicts** — memory / disk / network per load (expo-image reports it directly; RN core is classified via `Image.queryCache`)
|
|
68
|
-
- **Oversize auditing** — decoded size vs displayed size × device pixel ratio, Lighthouse-style: green within 10%, red beyond 50%, with estimated wasted decoded memory and the exact size you should serve
|
|
69
|
-
- **Memory accounting** — estimated decoded-bitmap bytes per image and totals across the session (catch ballooning before the OOM)
|
|
70
|
-
- **Failure log** — captured `onError` events with the message, and on iOS the HTTP status code + response headers
|
|
71
|
-
- **Cache actions** — clear expo-image memory/disk caches from the detail view
|
|
72
|
-
- **Upscale detection** — tiny sources stretched into big boxes (blurry images) get flagged too
|
|
73
|
-
|
|
74
|
-
## Desktop & AI
|
|
21
|
+
Images appears in the menu once installed. Open it to browse the image loads seen during the session.
|
|
75
22
|
|
|
76
|
-
|
|
23
|
+
The [Images guide](https://buoy.gg/buoy/latest/docs/tools/images) covers setup, what it captures and its limits. New to Buoy? Start with the [Quick start](https://buoy.gg/buoy/latest/docs/quick-start).
|
|
77
24
|
|
|
78
|
-
|
|
25
|
+
---
|
|
79
26
|
|
|
80
|
-
|
|
27
|
+
<sub>Part of <a href="https://buoy.gg">Buoy</a>, developer tools that live inside your app. Proprietary software. © Buoy LLC. <a href="https://buoy.gg/terms">Terms</a></sub>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";require("./register");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});
|
|
@@ -1,242 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.dropActionState = dropActionState;
|
|
7
|
-
exports.evictExpoDiskEntry = evictExpoDiskEntry;
|
|
8
|
-
exports.getReloadNonce = getReloadNonce;
|
|
9
|
-
exports.getReloadRequest = getReloadRequest;
|
|
10
|
-
exports.hardReloadAllMounted = hardReloadAllMounted;
|
|
11
|
-
exports.hardReloadRecord = hardReloadRecord;
|
|
12
|
-
exports.registerExpoInstance = registerExpoInstance;
|
|
13
|
-
exports.retryRecord = retryRecord;
|
|
14
|
-
exports.settleReloadRequest = settleReloadRequest;
|
|
15
|
-
exports.subscribeActions = subscribeActions;
|
|
16
|
-
var _remote = require("../remote");
|
|
17
|
-
var _state = require("../state");
|
|
18
|
-
var _overrides = require("./overrides");
|
|
19
|
-
/**
|
|
20
|
-
* Per-image actions: hard reload (bypass cache) and retry.
|
|
21
|
-
*
|
|
22
|
-
* RN core path: the decorator wrapper owns every mounted <Image>'s props, so a
|
|
23
|
-
* reload request re-renders the wrapper with a bumped `key` (native remount →
|
|
24
|
-
* fresh load cycle) and, for cache-busting, `source.cache: 'reload'` injected
|
|
25
|
-
* — VERIFIED supported on BOTH platforms (iOS NSURLRequest policy;
|
|
26
|
-
* Android ReactImageView.kt reads source."cache" → Fresco full-fetch). The
|
|
27
|
-
* override stays applied until the load settles, then reverts lazily on the
|
|
28
|
-
* app's next natural re-render (avoids an extra cache round-trip).
|
|
29
|
-
*
|
|
30
|
-
* expo-image path: the patched class instance exposes the public
|
|
31
|
-
* `reloadAsync()`. Cache-busting first evicts the disk entry (cache path +
|
|
32
|
-
* expo-file-system delete) and clears expo's memory cache (global — no
|
|
33
|
-
* per-entry memory eviction exists), so the reload is a true refetch that
|
|
34
|
-
* rewrites both cache tiers.
|
|
35
|
-
*/
|
|
36
|
-
|
|
37
|
-
// ---------------------------------------------------------------------------
|
|
38
|
-
// RN reload-request bus (consumed by the decorator wrapper)
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
|
-
|
|
41
|
-
function subscribeActions(listener) {
|
|
42
|
-
const s = (0, _state.imagesState)();
|
|
43
|
-
s.actionListeners.add(listener);
|
|
44
|
-
return () => (0, _state.imagesState)().actionListeners.delete(listener);
|
|
45
|
-
}
|
|
46
|
-
function getReloadRequest(recordId) {
|
|
47
|
-
if (recordId == null) return undefined;
|
|
48
|
-
return (0, _state.imagesState)().reloadRequests.get(recordId);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** Reload nonce for a record (0 = never requested). Snapshot for the wrapper. */
|
|
52
|
-
function getReloadNonce(recordId) {
|
|
53
|
-
return getReloadRequest(recordId)?.nonce ?? 0;
|
|
54
|
-
}
|
|
55
|
-
function requestRnReload(recordId, bust) {
|
|
56
|
-
const s = (0, _state.imagesState)();
|
|
57
|
-
const prev = s.reloadRequests.get(recordId);
|
|
58
|
-
s.reloadRequests.set(recordId, {
|
|
59
|
-
nonce: (prev?.nonce ?? 0) + 1,
|
|
60
|
-
bust
|
|
61
|
-
});
|
|
62
|
-
// touchRecord bumps the render version (the wrapper's snapshot) and emits.
|
|
63
|
-
(0, _overrides.touchRecord)(recordId);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Mark a request consumed once its load settles (called from the tracker on
|
|
68
|
-
* load/error). Silent — the wrapper reverts the prop override on the app's
|
|
69
|
-
* next natural re-render instead of forcing an immediate one.
|
|
70
|
-
*/
|
|
71
|
-
function settleReloadRequest(recordId) {
|
|
72
|
-
const s = (0, _state.imagesState)();
|
|
73
|
-
const req = s.reloadRequests.get(recordId);
|
|
74
|
-
if (req?.bust) {
|
|
75
|
-
s.reloadRequests.set(recordId, {
|
|
76
|
-
nonce: req.nonce,
|
|
77
|
-
bust: false
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
function dropActionState(recordId) {
|
|
82
|
-
const s = (0, _state.imagesState)();
|
|
83
|
-
s.reloadRequests.delete(recordId);
|
|
84
|
-
s.expoInstances.delete(recordId);
|
|
85
|
-
s.overrides.delete(recordId);
|
|
86
|
-
s.flashUntil.delete(recordId);
|
|
87
|
-
s.recordVersions.delete(recordId);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// ---------------------------------------------------------------------------
|
|
91
|
-
// expo instance registry (populated by the render patch)
|
|
92
|
-
// ---------------------------------------------------------------------------
|
|
93
|
-
|
|
94
|
-
function registerExpoInstance(recordId, instance) {
|
|
95
|
-
if (recordId == null) return;
|
|
96
|
-
(0, _state.imagesState)().expoInstances.set(recordId, instance);
|
|
97
|
-
}
|
|
98
|
-
function expoImageStatics() {
|
|
99
|
-
try {
|
|
100
|
-
return require("expo-image").Image ?? null;
|
|
101
|
-
} catch {
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* expo-file-system, legacy path first (v19+/SDK 54+ moved the function API
|
|
107
|
-
* there) then the main import — same pattern as shared-ui's persistentStorage.
|
|
108
|
-
*/
|
|
109
|
-
function loadFileSystem() {
|
|
110
|
-
try {
|
|
111
|
-
const mod = require("expo-file-system/legacy");
|
|
112
|
-
const fs = mod?.default || mod;
|
|
113
|
-
if (fs?.deleteAsync) return fs;
|
|
114
|
-
} catch {
|
|
115
|
-
// try main import
|
|
116
|
-
}
|
|
117
|
-
try {
|
|
118
|
-
const mod = require("expo-file-system");
|
|
119
|
-
const fs = mod?.default || mod;
|
|
120
|
-
if (fs?.deleteAsync) return fs;
|
|
121
|
-
} catch {
|
|
122
|
-
// not installed
|
|
123
|
-
}
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Delete an expo-image disk-cache entry (JS-only: cache path + file delete).
|
|
129
|
-
* Returns true if an entry existed and was deleted.
|
|
130
|
-
*/
|
|
131
|
-
async function evictExpoDiskEntry(record) {
|
|
132
|
-
const statics = expoImageStatics();
|
|
133
|
-
const fileSystem = loadFileSystem();
|
|
134
|
-
if (!statics?.getCachePathAsync || !fileSystem?.deleteAsync || !record.uri) return false;
|
|
135
|
-
try {
|
|
136
|
-
const path = await statics.getCachePathAsync(record.uri);
|
|
137
|
-
if (!path) return false;
|
|
138
|
-
const uri = path.startsWith("file://") ? path : `file://${path}`;
|
|
139
|
-
await fileSystem.deleteAsync(uri, {
|
|
140
|
-
idempotent: true
|
|
141
|
-
});
|
|
142
|
-
return true;
|
|
143
|
-
} catch {
|
|
144
|
-
return false;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// ---------------------------------------------------------------------------
|
|
149
|
-
// High-level dispatchers (used by the detail view)
|
|
150
|
-
// ---------------------------------------------------------------------------
|
|
151
|
-
|
|
152
|
-
/** Fresh load attempt for a mounted image (no cache bypass). */
|
|
153
|
-
async function retryRecord(record) {
|
|
154
|
-
const remote = (0, _remote.remoteImagesDispatch)();
|
|
155
|
-
if (remote) return (0, _remote.remoteActionResult)(remote("retry", {
|
|
156
|
-
id: record.id
|
|
157
|
-
}));
|
|
158
|
-
if (!record.mounted) {
|
|
159
|
-
return {
|
|
160
|
-
ok: false,
|
|
161
|
-
message: "Instance unmounted — cannot reload"
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
if (record.lib === "expo") {
|
|
165
|
-
const instance = (0, _state.imagesState)().expoInstances.get(record.id);
|
|
166
|
-
if (!instance?.reloadAsync) {
|
|
167
|
-
return {
|
|
168
|
-
ok: false,
|
|
169
|
-
message: "Live instance not reachable"
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
await instance.reloadAsync();
|
|
173
|
-
return {
|
|
174
|
-
ok: true,
|
|
175
|
-
message: "Reload requested"
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
requestRnReload(record.id, false);
|
|
179
|
-
return {
|
|
180
|
-
ok: true,
|
|
181
|
-
message: "Remounting image"
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/** Hard-reload every mounted image (mass cache-bust). Returns count started. */
|
|
186
|
-
async function hardReloadAllMounted() {
|
|
187
|
-
const remote = (0, _remote.remoteImagesDispatch)();
|
|
188
|
-
if (remote) {
|
|
189
|
-
return (0, _remote.remoteActionResult)(remote("massAction", {
|
|
190
|
-
kind: "reload"
|
|
191
|
-
}));
|
|
192
|
-
}
|
|
193
|
-
const {
|
|
194
|
-
getSnapshot
|
|
195
|
-
} = require("../store/imageEventStore");
|
|
196
|
-
let count = 0;
|
|
197
|
-
for (const record of getSnapshot()) {
|
|
198
|
-
if (!record.mounted) continue;
|
|
199
|
-
const result = await hardReloadRecord(record);
|
|
200
|
-
if (result.ok) count++;
|
|
201
|
-
}
|
|
202
|
-
return {
|
|
203
|
-
ok: count > 0,
|
|
204
|
-
message: `Hard-reloading ${count} mounted image${count === 1 ? "" : "s"}`
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/** Bypass-cache reload: refetches from network and rewrites the caches. */
|
|
209
|
-
async function hardReloadRecord(record) {
|
|
210
|
-
const remote = (0, _remote.remoteImagesDispatch)();
|
|
211
|
-
if (remote) return (0, _remote.remoteActionResult)(remote("hardReload", {
|
|
212
|
-
id: record.id
|
|
213
|
-
}));
|
|
214
|
-
if (!record.mounted) {
|
|
215
|
-
return {
|
|
216
|
-
ok: false,
|
|
217
|
-
message: "Instance unmounted — cannot reload"
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
if (record.sourceKind !== "network") {
|
|
221
|
-
// Local/asset sources have no HTTP cache to bust — plain remount instead.
|
|
222
|
-
return retryRecord(record);
|
|
223
|
-
}
|
|
224
|
-
if (record.lib === "expo") {
|
|
225
|
-
const evicted = await evictExpoDiskEntry(record);
|
|
226
|
-
const statics = expoImageStatics();
|
|
227
|
-
// No per-entry memory eviction exists — global clear is the honest option.
|
|
228
|
-
await statics?.clearMemoryCache?.().catch?.(() => false);
|
|
229
|
-
const result = await retryRecord(record);
|
|
230
|
-
if (!result.ok) return result;
|
|
231
|
-
return {
|
|
232
|
-
ok: true,
|
|
233
|
-
message: evicted ? "Disk entry evicted, memory cache cleared, refetching" : "Memory cache cleared, refetching (no disk entry found)"
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
requestRnReload(record.id, true);
|
|
237
|
-
return {
|
|
238
|
-
ok: true,
|
|
239
|
-
message: "Refetching with cache bypassed"
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
//# sourceMappingURL=actions.js.map
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.dropActionState=dropActionState;exports.evictExpoDiskEntry=evictExpoDiskEntry;exports.getReloadNonce=getReloadNonce;exports.getReloadRequest=getReloadRequest;exports.hardReloadAllMounted=hardReloadAllMounted;exports.hardReloadRecord=hardReloadRecord;exports.registerExpoInstance=registerExpoInstance;exports.retryRecord=retryRecord;exports.settleReloadRequest=settleReloadRequest;exports.subscribeActions=subscribeActions;var _nativeImageModules=require("./nativeImageModules");var _remote=require("../remote");var _state=require("../state");var _overrides=require("./overrides");function subscribeActions(e){const t=(0,_state.imagesState)();t.actionListeners.add(e);return()=>(0,_state.imagesState)().actionListeners.delete(e)}function getReloadRequest(e){if(e==null)return void 0;return(0,_state.imagesState)().reloadRequests.get(e)}function getReloadNonce(e){return getReloadRequest(e)?.nonce??0}function requestRnReload(e,t){const s=(0,_state.imagesState)();const n=s.reloadRequests.get(e);s.reloadRequests.set(e,{nonce:(n?.nonce??0)+1,bust:t});(0,_overrides.touchRecord)(e)}function settleReloadRequest(e){const t=(0,_state.imagesState)();const s=t.reloadRequests.get(e);if(s?.bust){t.reloadRequests.set(e,{nonce:s.nonce,bust:false})}}function dropActionState(e){const t=(0,_state.imagesState)();t.reloadRequests.delete(e);t.expoInstances.delete(e);t.overrides.delete(e);t.flashUntil.delete(e);t.recordVersions.delete(e)}function registerExpoInstance(e,t){if(e==null)return;(0,_state.imagesState)().expoInstances.set(e,t)}function expoImageStatics(){try{return(0,_nativeImageModules.loadExpoImage)().Image??null}catch{return null}}function loadFileSystem(){try{const e=(0,_nativeImageModules.loadLegacyFileSystem)();const t=e?.default||e;if(t?.deleteAsync)return t}catch{}try{const e=(0,_nativeImageModules.loadFileSystemModule)();const t=e?.default||e;if(t?.deleteAsync)return t}catch{}return null}async function evictExpoDiskEntry(e){const t=expoImageStatics();const s=loadFileSystem();if(!t?.getCachePathAsync||!s?.deleteAsync||!e.uri)return false;try{const n=await t.getCachePathAsync(e.uri);if(!n)return false;const a=n.startsWith("file://")?n:`file://${n}`;await s.deleteAsync(a,{idempotent:true});return true}catch{return false}}async function retryRecord(e){const t=(0,_remote.remoteImagesDispatch)();if(t)return(0,_remote.remoteActionResult)(t("retry",{id:e.id}));if(!e.mounted){return{ok:false,message:"Instance unmounted \u2014 cannot reload"}}if(e.lib==="expo"){const s=(0,_state.imagesState)().expoInstances.get(e.id);if(!s?.reloadAsync){return{ok:false,message:"Live instance not reachable"}}await s.reloadAsync();return{ok:true,message:"Reload requested"}}requestRnReload(e.id,false);return{ok:true,message:"Remounting image"}}async function hardReloadAllMounted(){const e=(0,_remote.remoteImagesDispatch)();if(e){return(0,_remote.remoteActionResult)(e("massAction",{kind:"reload"}))}const{getSnapshot:t}=require("../store/imageEventStore");let s=0;for(const n of t()){if(!n.mounted)continue;const a=await hardReloadRecord(n);if(a.ok)s++}return{ok:s>0,message:`Hard-reloading ${s} mounted image${s===1?"":"s"}`}}async function hardReloadRecord(e){const t=(0,_remote.remoteImagesDispatch)();if(t)return(0,_remote.remoteActionResult)(t("hardReload",{id:e.id}));if(!e.mounted){return{ok:false,message:"Instance unmounted \u2014 cannot reload"}}if(e.sourceKind!=="network"){return retryRecord(e)}if(e.lib==="expo"){const s=await evictExpoDiskEntry(e);const n=expoImageStatics();await n?.clearMemoryCache?.().catch?.(()=>false);const a=await retryRecord(e);if(!a.ok)return a;return{ok:true,message:s?"Disk entry evicted, memory cache cleared, refetching":"Memory cache cleared, refetching (no disk entry found)"}}requestRnReload(e.id,true);return{ok:true,message:"Refetching with cache bypassed"}}
|
|
@@ -1,45 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.getCaptureStatus = getCaptureStatus;
|
|
7
|
-
exports.installImageCapture = installImageCapture;
|
|
8
|
-
var _state = require("../state");
|
|
9
|
-
var _installExpoImage = require("./installExpoImage");
|
|
10
|
-
var _installRnImage = require("./installRnImage");
|
|
11
|
-
/**
|
|
12
|
-
* installImageCapture — idempotent entry point for the capture layer.
|
|
13
|
-
*
|
|
14
|
-
* IMPORTANT (timing): the RN-core decorator only takes effect if this runs
|
|
15
|
-
* before anything evaluates the `Image` module from 'react-native'. The
|
|
16
|
-
* recommended integration is `import "@buoy-gg/images/register"` as the FIRST
|
|
17
|
-
* import of the app entry file. The expo-image patch has no such constraint.
|
|
18
|
-
*
|
|
19
|
-
* Idempotency is keyed on the globalThis singleton, so even if this package
|
|
20
|
-
* gets evaluated as two module instances, capture installs exactly once.
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
function installImageCapture() {
|
|
24
|
-
const s = (0, _state.imagesState)();
|
|
25
|
-
// Mirror mode (desktop dashboard): never instrument the dashboard's own UI.
|
|
26
|
-
if (s.captureDisabled || s.captureInstalled) return;
|
|
27
|
-
s.captureInstalled = true;
|
|
28
|
-
(0, _installRnImage.installRnImageInterceptor)();
|
|
29
|
-
(0, _installExpoImage.installExpoImageInterceptor)();
|
|
30
|
-
}
|
|
31
|
-
function getCaptureStatus() {
|
|
32
|
-
// Mirror mode: report the synced DEVICE's capture status, not the local probe.
|
|
33
|
-
const mirrored = (0, _state.imagesState)().mirrorCaptureStatus;
|
|
34
|
-
if (mirrored) return mirrored;
|
|
35
|
-
const rn = (0, _installRnImage.rnCaptureStatus)();
|
|
36
|
-
const expo = (0, _installExpoImage.expoCaptureStatus)();
|
|
37
|
-
return {
|
|
38
|
-
installed: (0, _state.imagesState)().captureInstalled,
|
|
39
|
-
rnDecoratorActive: rn.decoratorActive,
|
|
40
|
-
rnDecoratorTooLate: rn.tooLate,
|
|
41
|
-
expoPatched: expo.patched,
|
|
42
|
-
expoAvailable: expo.available
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
//# sourceMappingURL=install.js.map
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.getCaptureStatus=getCaptureStatus;exports.installImageCapture=installImageCapture;var _state=require("../state");var _installExpoImage=require("./installExpoImage");var _installRnImage=require("./installRnImage");function installImageCapture(){const t=(0,_state.imagesState)();if(t.captureDisabled||t.captureInstalled)return;t.captureInstalled=true;(0,_installRnImage.installRnImageInterceptor)();(0,_installExpoImage.installExpoImageInterceptor)()}function getCaptureStatus(){const t=(0,_state.imagesState)().mirrorCaptureStatus;if(t)return t;const e=(0,_installRnImage.rnCaptureStatus)();const a=(0,_installExpoImage.expoCaptureStatus)();return{installed:(0,_state.imagesState)().captureInstalled,rnDecoratorActive:e.decoratorActive,rnDecoratorTooLate:e.tooLate,expoPatched:a.patched,expoAvailable:a.available}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.getCaptureStatus=getCaptureStatus;exports.installImageCapture=installImageCapture;exports.startBrowserImageCapture=startBrowserImageCapture;exports.stopBrowserImageCapture=stopBrowserImageCapture;var _state=require("../state");var _imageEventStore=require("../store/imageEventStore");var _overrides=require("./overrides");const tracked=new Map;let detach;let leases=0;const EMPTY_IMAGE="data:image/png;base64,";function apply(e){const l=(0,_imageEventStore.getRecord)(e.id);if(!l)return;const n=(0,_state.imagesState)();const d=(0,_overrides.getOverride)(e.id);const r=(0,_overrides.getGlobalModes)();let t=e.source;if(r.network==="offline"&&l.sourceKind==="network")t=EMPTY_IMAGE;if(d?.source){const c=d.source;if(c.type==="url")t=c.uri;else if(c.type==="error")t=EMPTY_IMAGE;else t=null;if(c.type==="hang")(0,_imageEventStore.updateRecord)(e.id,{status:"loading"})}const s=n.reloadRequests.get(e.id);const o=s&&s.nonce!==e.nonce;if(o){e.nonce=s.nonce;if(s.bust&&t&&/^https?:/.test(new URL(t,document.baseURI).href)){const c=new URL(t,document.baseURI);c.searchParams.set("__buoy_reload",String(Date.now()));t=c.href}e.image.removeAttribute("src");(0,_imageEventStore.updateRecord)(e.id,{status:"loading",startedAt:Date.now(),loadCount:l.loadCount+1})}if(r.network==="cold"&&t&&/^https?:/.test(new URL(t,document.baseURI).href)){if(e.coldSource!==t||!e.coldURL||o){e.coldSource=t;const c=new URL(t,document.baseURI);c.searchParams.set("__buoy_reload",`${Date.now()}-${e.nonce}`);e.coldURL=c.href}t=e.coldURL}else{e.coldSource=void 0;e.coldURL=void 0}e.appliedSource=t;e.appliedSrcset=t!==e.source?null:e.srcset;if(e.appliedSrcset===null)e.image.removeAttribute("srcset");else if(e.image.getAttribute("srcset")!==e.appliedSrcset)e.image.setAttribute("srcset",e.appliedSrcset);if(t===null)e.image.removeAttribute("src");else if(e.image.getAttribute("src")!==t)e.image.setAttribute("src",t);const i=document.createElement("span").style;i.cssText=e.style??"";const a=d?.extraProps?.contentFit??d?.extraProps?.resizeMode;if(typeof a==="string")i.objectFit={stretch:"fill",center:"none"}[a]??a;if(r.blank||d?.source?.type==="blank")i.opacity="0";for(const[c,u]of Object.entries(d?.style??{})){const p=c.replace(/[A-Z]/g,f=>`-${f.toLowerCase()}`);i.setProperty(p,typeof u==="number"&&!["opacity","zIndex","flex","flexGrow","flexShrink"].includes(c)?`${u}px`:String(u))}if((n.flashUntil.get(e.id)??0)>Date.now())i.outline="3px solid #ff453a";e.appliedStyle=i.cssText||null;if(e.image.getAttribute("style")!==e.appliedStyle){if(e.appliedStyle===null)e.image.removeAttribute("style");else e.image.setAttribute("style",e.appliedStyle)}}function track(e){if(tracked.has(e)||e.closest("[data-buoy-root]"))return;const l=e.currentSrc||e.src;if(!l)return;const n=(0,_imageEventStore.createRecord)({lib:"web",uri:l,sourceKind:l.startsWith("data:")?"data":l.startsWith("blob:")?"file":"network",hasAltText:e.hasAttribute("alt")||e.hasAttribute("aria-label")});const d={id:n.id,image:e,source:e.getAttribute("src"),srcset:e.getAttribute("srcset"),style:e.getAttribute("style"),appliedSource:e.getAttribute("src"),appliedSrcset:e.getAttribute("srcset"),appliedStyle:e.getAttribute("style"),nonce:0,dispose(){}};const r=()=>{const i=e.getBoundingClientRect();const a=n.layout;const c=n.status==="loaded"&&a&&(a.width!==i.width||a.height!==i.height);(0,_imageEventStore.updateRecord)(n.id,{layout:{width:i.width,height:i.height},layoutShifts:(n.layoutShifts??0)+(c?1:0)})};const t=()=>{const i=performance.getEntriesByName(e.currentSrc||e.src);const a=i[i.length-1];(0,_imageEventStore.updateRecord)(n.id,{status:"loaded",loadedAt:Date.now(),intrinsic:{width:e.naturalWidth,height:e.naturalHeight},...!(0,_overrides.getOverride)(n.id)?.source&&(0,_overrides.getGlobalModes)().network==="normal"?{uri:e.currentSrc||e.src}:{},durationMs:a?.duration,bytesTotal:a&&a.encodedBodySize>0?a.encodedBodySize:void 0,cacheVerdict:a&&a.transferSize>0?"none":void 0,cacheVerdictSource:a?"event":void 0});r()};const s=()=>(0,_imageEventStore.updateRecord)(n.id,{status:"error",erroredAt:Date.now(),error:"The browser could not load this image."});e.addEventListener("load",t);e.addEventListener("error",s);const o=typeof ResizeObserver==="undefined"?void 0:new ResizeObserver(r);o?.observe(e);d.dispose=()=>{o?.disconnect();e.removeEventListener("load",t);e.removeEventListener("error",s)};tracked.set(e,d);r();(0,_imageEventStore.updateRecord)(n.id,{status:"loading",startedAt:Date.now(),loadCount:1});if(e.complete){if(e.naturalWidth>0)t();else s()}apply(d)}function installImageCapture(){const e=(0,_state.imagesState)();if(typeof document==="undefined"||e.captureDisabled||detach)return;e.captureInstalled=true;const l=()=>{for(const[r,t]of tracked)if(!r.isConnected){t.dispose();tracked.delete(r);(0,_imageEventStore.updateRecord)(t.id,{mounted:false})}document.querySelectorAll("img").forEach(track)};const n=new MutationObserver(r=>{if((0,_state.imagesState)().captureDisabled){stopBrowserImageCapture();return}for(const t of r){const s=t.target;const o=tracked.get(s);if(!o||t.type!=="attributes")continue;if(t.attributeName==="src"&&s.getAttribute("src")!==o.appliedSource||t.attributeName==="srcset"&&s.getAttribute("srcset")!==o.appliedSrcset){o.dispose();tracked.delete(s);(0,_imageEventStore.updateRecord)(o.id,{mounted:false})}else if(t.attributeName==="style"&&s.getAttribute("style")!==o.appliedStyle){o.style=s.getAttribute("style");apply(o)}}l()});n.observe(document.documentElement,{childList:true,subtree:true,attributes:true,attributeFilter:["src","srcset","style","alt"]});const d=()=>{for(const r of tracked.values())apply(r)};e.actionListeners.add(d);detach=()=>{n.disconnect();e.actionListeners.delete(d);for(const r of tracked.values()){r.dispose();for(const[t,s,o]of[["src",r.source,r.appliedSource],["srcset",r.srcset,r.appliedSrcset],["style",r.style,r.appliedStyle]]){if(r.image.getAttribute(t)!==o)continue;if(s===null)r.image.removeAttribute(t);else r.image.setAttribute(t,s)}(0,_imageEventStore.updateRecord)(r.id,{mounted:false})}tracked.clear();e.captureInstalled=false};l()}function stopBrowserImageCapture(){detach?.();detach=void 0}function startBrowserImageCapture(){if(typeof document==="undefined")return()=>{};if(leases++===0)installImageCapture();let e=false;return()=>{if(e)return;e=true;if(--leases===0)stopBrowserImageCapture()}}function getCaptureStatus(){return(0,_state.imagesState)().mirrorCaptureStatus??{installed:!!detach,rnDecoratorActive:false,rnDecoratorTooLate:false,expoPatched:false,expoAvailable:false,browserObserverActive:!!detach}}
|
|
@@ -1,99 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.expoCaptureStatus = expoCaptureStatus;
|
|
7
|
-
exports.installExpoImageInterceptor = installExpoImageInterceptor;
|
|
8
|
-
var _state = require("../state");
|
|
9
|
-
var _actions = require("./actions");
|
|
10
|
-
var _overrides = require("./overrides");
|
|
11
|
-
var _shared = require("./shared");
|
|
12
|
-
/**
|
|
13
|
-
* expo-image interception via class-prototype patch.
|
|
14
|
-
*
|
|
15
|
-
* expo-image's <Image> is a class component (React.PureComponent) whose
|
|
16
|
-
* render() reads `this.props`, so wrapping the prototype's render and swapping
|
|
17
|
-
* `this.props` for an instrumented copy for the duration of the original
|
|
18
|
-
* render is timing-immune (prototype methods are looked up dynamically — no
|
|
19
|
-
* import-order race like the RN core decorator). Props are restored in
|
|
20
|
-
* `finally`, so PureComponent's shouldComponentUpdate still compares the
|
|
21
|
-
* app's real props.
|
|
22
|
-
*
|
|
23
|
-
* Double-patch safety: install flags live on the globalThis singleton AND a
|
|
24
|
-
* marker is stamped on the prototype itself, so even two module instances of
|
|
25
|
-
* this package can't wrap render() twice.
|
|
26
|
-
*
|
|
27
|
-
* expo-image is an optional peer: if it isn't installed, this is a no-op.
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
const PATCH_MARKER = "__buoyImagesPatched";
|
|
31
|
-
const trackers = new WeakMap();
|
|
32
|
-
function trackerFor(instance) {
|
|
33
|
-
let tracker = trackers.get(instance);
|
|
34
|
-
if (!tracker) {
|
|
35
|
-
tracker = new _shared.ImageInstanceTracker("expo");
|
|
36
|
-
trackers.set(instance, tracker);
|
|
37
|
-
}
|
|
38
|
-
return tracker;
|
|
39
|
-
}
|
|
40
|
-
function installExpoImageInterceptor() {
|
|
41
|
-
const s = (0, _state.imagesState)();
|
|
42
|
-
if (s.expoInstalled) return;
|
|
43
|
-
s.expoInstalled = true;
|
|
44
|
-
let ImageClass;
|
|
45
|
-
try {
|
|
46
|
-
ImageClass = require("expo-image").Image;
|
|
47
|
-
} catch {
|
|
48
|
-
s.expoAvailable = false;
|
|
49
|
-
return; // expo-image not installed in this app
|
|
50
|
-
}
|
|
51
|
-
if (!ImageClass?.prototype?.render) {
|
|
52
|
-
s.expoAvailable = false;
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
s.expoAvailable = true;
|
|
56
|
-
const proto = ImageClass.prototype;
|
|
57
|
-
if (proto[PATCH_MARKER]) {
|
|
58
|
-
s.expoPatched = true;
|
|
59
|
-
return; // already patched by another module instance of this package
|
|
60
|
-
}
|
|
61
|
-
proto[PATCH_MARKER] = true;
|
|
62
|
-
const originalRender = proto.render;
|
|
63
|
-
const originalUnmount = proto.componentWillUnmount;
|
|
64
|
-
proto.render = function buoyImagesRender() {
|
|
65
|
-
const originalProps = this.props;
|
|
66
|
-
if ((0, _shared.isInternalImage)(originalProps?.testID)) {
|
|
67
|
-
return originalRender.call(this);
|
|
68
|
-
}
|
|
69
|
-
const tracker = trackerFor(this);
|
|
70
|
-
tracker.syncSource(originalProps?.source);
|
|
71
|
-
tracker.noteA11y(Boolean(originalProps?.alt ?? originalProps?.accessibilityLabel));
|
|
72
|
-
// Keep the action layer pointed at the live instance (reloadAsync etc.).
|
|
73
|
-
(0, _actions.registerExpoInstance)(tracker.getRecordId(), this);
|
|
74
|
-
const wrapped = tracker.handlers(originalProps);
|
|
75
|
-
try {
|
|
76
|
-
// Simulation layer: global modes + per-record override + locate-flash.
|
|
77
|
-
this.props = (0, _overrides.applyOverrides)({
|
|
78
|
-
...originalProps,
|
|
79
|
-
...wrapped
|
|
80
|
-
}, tracker.getRecordId(), "expo");
|
|
81
|
-
return originalRender.call(this);
|
|
82
|
-
} finally {
|
|
83
|
-
this.props = originalProps;
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
proto.componentWillUnmount = function buoyImagesUnmount() {
|
|
87
|
-
trackers.get(this)?.unmount();
|
|
88
|
-
originalUnmount?.call(this);
|
|
89
|
-
};
|
|
90
|
-
s.expoPatched = true;
|
|
91
|
-
}
|
|
92
|
-
function expoCaptureStatus() {
|
|
93
|
-
const s = (0, _state.imagesState)();
|
|
94
|
-
return {
|
|
95
|
-
available: s.expoAvailable,
|
|
96
|
-
patched: s.expoPatched
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
//# sourceMappingURL=installExpoImage.js.map
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.expoCaptureStatus=expoCaptureStatus;exports.installExpoImageInterceptor=installExpoImageInterceptor;var _state=require("../state");var _actions=require("./actions");var _overrides=require("./overrides");var _shared=require("./shared");const PATCH_MARKER="__buoyImagesPatched";const trackers=new WeakMap;function trackerFor(e){let t=trackers.get(e);if(!t){t=new _shared.ImageInstanceTracker("expo");trackers.set(e,t)}return t}function installExpoImageInterceptor(){const e=(0,_state.imagesState)();if(e.expoInstalled)return;e.expoInstalled=true;let t;try{t=require("expo-image").Image}catch{e.expoAvailable=false;return}if(!t?.prototype?.render){e.expoAvailable=false;return}e.expoAvailable=true;const a=t.prototype;if(a[PATCH_MARKER]){e.expoPatched=true;return}a[PATCH_MARKER]=true;const n=a.render;const s=a.componentWillUnmount;a.render=function i(){const r=this.props;if((0,_shared.isInternalImage)(r?.testID)){return n.call(this)}const o=trackerFor(this);o.syncSource(r?.source);o.noteA11y(Boolean(r?.alt??r?.accessibilityLabel));(0,_actions.registerExpoInstance)(o.getRecordId(),this);const l=o.handlers(r);try{this.props=(0,_overrides.applyOverrides)({...r,...l},o.getRecordId(),"expo");return n.call(this)}finally{this.props=r}};a.componentWillUnmount=function i(){trackers.get(this)?.unmount();s?.call(this)};e.expoPatched=true}function expoCaptureStatus(){const e=(0,_state.imagesState)();return{available:e.expoAvailable,patched:e.expoPatched}}
|
|
@@ -1,128 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.installRnImageInterceptor = installRnImageInterceptor;
|
|
7
|
-
exports.rnCaptureStatus = rnCaptureStatus;
|
|
8
|
-
var React = _interopRequireWildcard(require("react"));
|
|
9
|
-
var _state = require("../state");
|
|
10
|
-
var _actions = require("./actions");
|
|
11
|
-
var _overrides = require("./overrides");
|
|
12
|
-
var _shared = require("./shared");
|
|
13
|
-
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
|
-
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
15
|
-
/**
|
|
16
|
-
* RN core <Image> interception via the official injection hook.
|
|
17
|
-
*
|
|
18
|
-
* `unstable_setImageComponentDecorator` wraps EVERY RN <Image> app-wide — but
|
|
19
|
-
* the decorator is latched ONCE, at Image module evaluation
|
|
20
|
-
* (`Image.ios.js` / `Image.android.js` read it at module scope). So install
|
|
21
|
-
* order matters: this must run before anything touches `Image` from
|
|
22
|
-
* 'react-native' — hence the `@buoy-gg/images/register` first-import.
|
|
23
|
-
*
|
|
24
|
-
* Detection strategy: the decorator sets `decoratorInvoked` when RN's Image
|
|
25
|
-
* module applies it. `unstable_registerImageAttachedCallback` is a live Set
|
|
26
|
-
* (never latched), so image mounts are always visible — if mounts happen but
|
|
27
|
-
* the decorator never ran, we were too late and CaptureStatus reports it.
|
|
28
|
-
*
|
|
29
|
-
* All flags live on the globalThis singleton (../state.ts) so a second module
|
|
30
|
-
* instance of this package neither re-installs nor reports a stale status.
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
/** Wraps one RN Image render with capture instrumentation + reload actions. */
|
|
34
|
-
function useImageCapture(props) {
|
|
35
|
-
const trackerRef = React.useRef(null);
|
|
36
|
-
if (trackerRef.current == null) {
|
|
37
|
-
trackerRef.current = new _shared.ImageInstanceTracker("rn");
|
|
38
|
-
}
|
|
39
|
-
const tracker = trackerRef.current;
|
|
40
|
-
const internal = (0, _shared.isInternalImage)(props.testID);
|
|
41
|
-
if (!internal) {
|
|
42
|
-
tracker.syncSource(props.source);
|
|
43
|
-
tracker.noteA11y(Boolean(props.alt ?? props.accessibilityLabel ?? props["aria-label"]));
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Action/override bus: version bumps re-render just this instance (snapshot
|
|
47
|
-
// is per-record with a global floor, so unrelated actions don't invalidate
|
|
48
|
-
// other images but global-mode toggles reach everyone).
|
|
49
|
-
React.useSyncExternalStore(_actions.subscribeActions, () => (0, _overrides.getRenderVersion)(internal ? null : tracker.getRecordId()));
|
|
50
|
-
React.useEffect(() => {
|
|
51
|
-
if (internal) return undefined;
|
|
52
|
-
return () => tracker.unmount();
|
|
53
|
-
}, [tracker, internal]);
|
|
54
|
-
if (internal) return {
|
|
55
|
-
props,
|
|
56
|
-
reloadKey: 0
|
|
57
|
-
};
|
|
58
|
-
const recordId = tracker.getRecordId();
|
|
59
|
-
const wrapped = tracker.handlers(props);
|
|
60
|
-
const out = {
|
|
61
|
-
...props,
|
|
62
|
-
...wrapped
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
// While a bust request is pending (cleared when the load settles), refetch
|
|
66
|
-
// bypassing caches — VERIFIED both platforms honor source.cache: 'reload'.
|
|
67
|
-
const request = (0, _actions.getReloadRequest)(recordId);
|
|
68
|
-
if (request?.bust) {
|
|
69
|
-
out.source = (0, _overrides.patchSourceCache)(out.source, "reload");
|
|
70
|
-
}
|
|
71
|
-
// Simulation layer: global modes + per-record override + locate-flash.
|
|
72
|
-
(0, _overrides.applyOverrides)(out, recordId, "rn");
|
|
73
|
-
return {
|
|
74
|
-
props: out,
|
|
75
|
-
reloadKey: (0, _actions.getReloadNonce)(recordId)
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
function installRnImageInterceptor() {
|
|
79
|
-
const s = (0, _state.imagesState)();
|
|
80
|
-
if (s.rnInstalled) return;
|
|
81
|
-
s.rnInstalled = true;
|
|
82
|
-
try {
|
|
83
|
-
// Deep import: evaluates ONLY the injection module, never Image itself.
|
|
84
|
-
const injection = require("react-native/Libraries/Image/ImageInjection");
|
|
85
|
-
const setDecorator = injection.unstable_setImageComponentDecorator;
|
|
86
|
-
const registerAttached = injection.unstable_registerImageAttachedCallback;
|
|
87
|
-
if (typeof setDecorator === "function") {
|
|
88
|
-
setDecorator(BaseImage => {
|
|
89
|
-
(0, _state.imagesState)().decoratorInvoked = true;
|
|
90
|
-
const CapturedImage = /*#__PURE__*/React.forwardRef(function BuoyCapturedImage(props, ref) {
|
|
91
|
-
const {
|
|
92
|
-
props: instrumented,
|
|
93
|
-
reloadKey
|
|
94
|
-
} = useImageCapture(props);
|
|
95
|
-
// key bump forces a native remount → fresh load cycle (retry).
|
|
96
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsx)(BaseImage, {
|
|
97
|
-
...instrumented,
|
|
98
|
-
ref: ref
|
|
99
|
-
}, `buoy-r${reloadKey}`);
|
|
100
|
-
});
|
|
101
|
-
return CapturedImage;
|
|
102
|
-
});
|
|
103
|
-
s.decoratorRegistered = true;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Live mount signal — works regardless of decorator timing. Used purely
|
|
107
|
-
// to detect the "decorator registered too late" condition.
|
|
108
|
-
if (typeof registerAttached === "function") {
|
|
109
|
-
registerAttached(() => {
|
|
110
|
-
(0, _state.imagesState)().attachSeen++;
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
} catch {
|
|
114
|
-
// Internal RN path moved (version drift) or non-RN platform — expo-image
|
|
115
|
-
// capture still works; status will report the decorator as inactive.
|
|
116
|
-
s.decoratorRegistered = false;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
function rnCaptureStatus() {
|
|
120
|
-
const s = (0, _state.imagesState)();
|
|
121
|
-
return {
|
|
122
|
-
decoratorRegistered: s.decoratorRegistered,
|
|
123
|
-
decoratorActive: s.decoratorInvoked,
|
|
124
|
-
// Mounts observed but our wrapper never rendered → Image latched first.
|
|
125
|
-
tooLate: s.decoratorRegistered && !s.decoratorInvoked && s.attachSeen > 0
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
//# sourceMappingURL=installRnImage.js.map
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.installRnImageInterceptor=installRnImageInterceptor;exports.rnCaptureStatus=rnCaptureStatus;exports.useImageCapture=useImageCapture;var React=_interopRequireWildcard(require("react"));var _state=require("../state");var _actions=require("./actions");var _overrides=require("./overrides");var _shared=require("./shared");var _jsxRuntime=require("react/jsx-runtime");function _interopRequireWildcard(e,o){if("function"==typeof WeakMap)var r=new WeakMap,a=new WeakMap;return(_interopRequireWildcard=function(t,i){if(!i&&t&&t.__esModule)return t;var n,c,u={__proto__:null,default:t};if(null===t||"object"!=typeof t&&"function"!=typeof t)return u;if(n=i?a:r){if(n.has(t))return n.get(t);n.set(t,u)}for(const s in t)"default"!==s&&{}.hasOwnProperty.call(t,s)&&((c=(n=Object.defineProperty)&&Object.getOwnPropertyDescriptor(t,s))&&(c.get||c.set)?n(u,s,c):u[s]=t[s]);return u})(e,o)}function useImageCapture(e){const o=React.useRef(null);if(o.current==null){o.current=new _shared.ImageInstanceTracker("rn")}const r=o.current;const a=(0,_shared.isInternalImage)(e.testID);if(!a){r.syncSource(e.source);r.noteA11y(Boolean(e.alt??e.accessibilityLabel??e["aria-label"]))}React.useSyncExternalStore(_actions.subscribeActions,()=>(0,_overrides.getRenderVersion)(a?null:r.getRecordId()));React.useEffect(()=>{if(a)return void 0;return()=>r.unmount()},[r,a]);if(a)return{props:e,reloadKey:0};const t=r.getRecordId();const i=r.handlers(e);const n={...e,...i};const c=(0,_actions.getReloadRequest)(t);if(c?.bust){n.source=(0,_overrides.patchSourceCache)(n.source,"reload")}(0,_overrides.applyOverrides)(n,t,"rn");return{props:n,reloadKey:(0,_actions.getReloadNonce)(t)}}function installRnImageInterceptor(){const e=(0,_state.imagesState)();if(e.rnInstalled)return;e.rnInstalled=true;try{const o=require("react-native/Libraries/Image/ImageInjection");const r=o.unstable_setImageComponentDecorator;const a=o.unstable_registerImageAttachedCallback;if(typeof r==="function"&&!e.decoratorRegistered){r(t=>{(0,_state.imagesState)().decoratorInvoked=true;const i=React.forwardRef(function n(c,u){const{props:s,reloadKey:d}=useImageCapture(c);return(0,_jsxRuntime.jsx)(t,{...s,ref:u},`buoy-r${d}`)});return i});e.decoratorRegistered=true}if(typeof a==="function"){a(()=>{(0,_state.imagesState)().attachSeen++})}}catch{e.decoratorRegistered=false}}function rnCaptureStatus(){const e=(0,_state.imagesState)();return{decoratorRegistered:e.decoratorRegistered,decoratorActive:e.decoratorInvoked,tooLate:e.decoratorRegistered&&!e.decoratorInvoked&&e.attachSeen>0}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.loadExpoImage=loadExpoImage;exports.loadFileSystemModule=loadFileSystemModule;exports.loadLegacyFileSystem=loadLegacyFileSystem;function loadExpoImage(){try{return require("expo-image")}catch{return null}}function loadLegacyFileSystem(){try{return require("expo-file-system/legacy")}catch{return null}}function loadFileSystemModule(){try{return require("expo-file-system")}catch{return null}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.loadExpoImage=loadExpoImage;exports.loadFileSystemModule=loadFileSystemModule;exports.loadLegacyFileSystem=loadLegacyFileSystem;function loadExpoImage(){return null}function loadLegacyFileSystem(){return null}function loadFileSystemModule(){return null}
|