@buoy-gg/images 7.0.41 → 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.
Files changed (194) hide show
  1. package/README.md +11 -70
  2. package/lib/commonjs/autoCapture.js +1 -4
  3. package/lib/commonjs/autoCapture.web.js +1 -6
  4. package/lib/commonjs/capture/actions.js +1 -243
  5. package/lib/commonjs/capture/install.js +1 -45
  6. package/lib/commonjs/capture/install.web.js +1 -250
  7. package/lib/commonjs/capture/installExpoImage.js +1 -99
  8. package/lib/commonjs/capture/installRnImage.js +1 -132
  9. package/lib/commonjs/capture/nativeImageModules.js +1 -30
  10. package/lib/commonjs/capture/nativeImageModules.web.js +1 -18
  11. package/lib/commonjs/capture/overrides.js +1 -346
  12. package/lib/commonjs/capture/rnDecoratorEarly.js +1 -55
  13. package/lib/commonjs/capture/savings.js +1 -209
  14. package/lib/commonjs/capture/savings.web.js +1 -71
  15. package/lib/commonjs/capture/shared.js +1 -378
  16. package/lib/commonjs/components/ImageDetail.js +1 -455
  17. package/lib/commonjs/components/ImageDetailFooter.js +1 -158
  18. package/lib/commonjs/components/ImageRow.js +1 -156
  19. package/lib/commonjs/components/ImagesHeader.js +1 -63
  20. package/lib/commonjs/components/ImagesIcon.js +1 -19
  21. package/lib/commonjs/components/ImagesListFooter.js +1 -126
  22. package/lib/commonjs/components/ImagesModal.js +1 -240
  23. package/lib/commonjs/components/ImagesRoot.js +1 -24
  24. package/lib/commonjs/components/ImagesRoot.web.js +1 -13
  25. package/lib/commonjs/components/InsightsBar.js +1 -59
  26. package/lib/commonjs/export.js +1 -81
  27. package/lib/commonjs/format.js +1 -117
  28. package/lib/commonjs/hooks/useImageRecords.js +1 -20
  29. package/lib/commonjs/index.js +1 -261
  30. package/lib/commonjs/preset.js +1 -39
  31. package/lib/commonjs/register.js +1 -19
  32. package/lib/commonjs/registerEarly.js +1 -14
  33. package/lib/commonjs/remote.js +1 -42
  34. package/lib/commonjs/state.js +1 -58
  35. package/lib/commonjs/store/imageEventStore.js +1 -238
  36. package/lib/commonjs/store/insights.js +1 -76
  37. package/lib/commonjs/sync/imagesSyncAdapter.js +1 -361
  38. package/lib/commonjs/types.js +0 -1
  39. package/lib/module/autoCapture.js +1 -4
  40. package/lib/module/autoCapture.web.js +1 -5
  41. package/lib/module/capture/actions.js +1 -230
  42. package/lib/module/capture/install.js +1 -40
  43. package/lib/module/capture/install.web.js +1 -243
  44. package/lib/module/capture/installExpoImage.js +1 -94
  45. package/lib/module/capture/installRnImage.js +1 -125
  46. package/lib/module/capture/nativeImageModules.js +1 -24
  47. package/lib/module/capture/nativeImageModules.web.js +1 -12
  48. package/lib/module/capture/overrides.js +1 -329
  49. package/lib/module/capture/rnDecoratorEarly.js +1 -50
  50. package/lib/module/capture/savings.js +1 -205
  51. package/lib/module/capture/savings.web.js +1 -67
  52. package/lib/module/capture/shared.js +1 -369
  53. package/lib/module/components/ImageDetail.js +1 -451
  54. package/lib/module/components/ImageDetailFooter.js +1 -154
  55. package/lib/module/components/ImageRow.js +1 -152
  56. package/lib/module/components/ImagesHeader.js +1 -59
  57. package/lib/module/components/ImagesIcon.js +1 -12
  58. package/lib/module/components/ImagesListFooter.js +1 -122
  59. package/lib/module/components/ImagesModal.js +1 -235
  60. package/lib/module/components/ImagesRoot.js +1 -20
  61. package/lib/module/components/ImagesRoot.web.js +1 -9
  62. package/lib/module/components/InsightsBar.js +1 -55
  63. package/lib/module/export.js +1 -76
  64. package/lib/module/format.js +1 -108
  65. package/lib/module/hooks/useImageRecords.js +1 -16
  66. package/lib/module/index.js +1 -79
  67. package/lib/module/preset.js +1 -35
  68. package/lib/module/register.js +1 -19
  69. package/lib/module/registerEarly.js +1 -14
  70. package/lib/module/remote.js +1 -36
  71. package/lib/module/state.js +1 -54
  72. package/lib/module/store/imageEventStore.js +1 -221
  73. package/lib/module/store/insights.js +1 -71
  74. package/lib/module/sync/imagesSyncAdapter.js +1 -356
  75. package/lib/module/types.js +0 -1
  76. package/lib/web/chunks/chunk-TTS5EH4R.mjs +1 -2
  77. package/lib/web/index.mjs +3 -4
  78. package/lib/web/register.mjs +1 -2
  79. package/package.json +2 -2
  80. package/register.js +1 -15
  81. package/lib/commonjs/autoCapture.js.map +0 -1
  82. package/lib/commonjs/autoCapture.web.js.map +0 -1
  83. package/lib/commonjs/capture/actions.js.map +0 -1
  84. package/lib/commonjs/capture/install.js.map +0 -1
  85. package/lib/commonjs/capture/install.web.js.map +0 -1
  86. package/lib/commonjs/capture/installExpoImage.js.map +0 -1
  87. package/lib/commonjs/capture/installRnImage.js.map +0 -1
  88. package/lib/commonjs/capture/nativeImageModules.js.map +0 -1
  89. package/lib/commonjs/capture/nativeImageModules.web.js.map +0 -1
  90. package/lib/commonjs/capture/overrides.js.map +0 -1
  91. package/lib/commonjs/capture/rnDecoratorEarly.js.map +0 -1
  92. package/lib/commonjs/capture/savings.js.map +0 -1
  93. package/lib/commonjs/capture/savings.web.js.map +0 -1
  94. package/lib/commonjs/capture/shared.js.map +0 -1
  95. package/lib/commonjs/components/ImageDetail.js.map +0 -1
  96. package/lib/commonjs/components/ImageDetailFooter.js.map +0 -1
  97. package/lib/commonjs/components/ImageRow.js.map +0 -1
  98. package/lib/commonjs/components/ImagesHeader.js.map +0 -1
  99. package/lib/commonjs/components/ImagesIcon.js.map +0 -1
  100. package/lib/commonjs/components/ImagesListFooter.js.map +0 -1
  101. package/lib/commonjs/components/ImagesModal.js.map +0 -1
  102. package/lib/commonjs/components/ImagesRoot.js.map +0 -1
  103. package/lib/commonjs/components/ImagesRoot.web.js.map +0 -1
  104. package/lib/commonjs/components/InsightsBar.js.map +0 -1
  105. package/lib/commonjs/export.js.map +0 -1
  106. package/lib/commonjs/format.js.map +0 -1
  107. package/lib/commonjs/hooks/useImageRecords.js.map +0 -1
  108. package/lib/commonjs/index.js.map +0 -1
  109. package/lib/commonjs/preset.js.map +0 -1
  110. package/lib/commonjs/register.js.map +0 -1
  111. package/lib/commonjs/registerEarly.js.map +0 -1
  112. package/lib/commonjs/remote.js.map +0 -1
  113. package/lib/commonjs/state.js.map +0 -1
  114. package/lib/commonjs/store/imageEventStore.js.map +0 -1
  115. package/lib/commonjs/store/insights.js.map +0 -1
  116. package/lib/commonjs/sync/imagesSyncAdapter.js.map +0 -1
  117. package/lib/commonjs/types.js.map +0 -1
  118. package/lib/module/autoCapture.js.map +0 -1
  119. package/lib/module/autoCapture.web.js.map +0 -1
  120. package/lib/module/capture/actions.js.map +0 -1
  121. package/lib/module/capture/install.js.map +0 -1
  122. package/lib/module/capture/install.web.js.map +0 -1
  123. package/lib/module/capture/installExpoImage.js.map +0 -1
  124. package/lib/module/capture/installRnImage.js.map +0 -1
  125. package/lib/module/capture/nativeImageModules.js.map +0 -1
  126. package/lib/module/capture/nativeImageModules.web.js.map +0 -1
  127. package/lib/module/capture/overrides.js.map +0 -1
  128. package/lib/module/capture/rnDecoratorEarly.js.map +0 -1
  129. package/lib/module/capture/savings.js.map +0 -1
  130. package/lib/module/capture/savings.web.js.map +0 -1
  131. package/lib/module/capture/shared.js.map +0 -1
  132. package/lib/module/components/ImageDetail.js.map +0 -1
  133. package/lib/module/components/ImageDetailFooter.js.map +0 -1
  134. package/lib/module/components/ImageRow.js.map +0 -1
  135. package/lib/module/components/ImagesHeader.js.map +0 -1
  136. package/lib/module/components/ImagesIcon.js.map +0 -1
  137. package/lib/module/components/ImagesListFooter.js.map +0 -1
  138. package/lib/module/components/ImagesModal.js.map +0 -1
  139. package/lib/module/components/ImagesRoot.js.map +0 -1
  140. package/lib/module/components/ImagesRoot.web.js.map +0 -1
  141. package/lib/module/components/InsightsBar.js.map +0 -1
  142. package/lib/module/export.js.map +0 -1
  143. package/lib/module/format.js.map +0 -1
  144. package/lib/module/hooks/useImageRecords.js.map +0 -1
  145. package/lib/module/index.js.map +0 -1
  146. package/lib/module/preset.js.map +0 -1
  147. package/lib/module/register.js.map +0 -1
  148. package/lib/module/registerEarly.js.map +0 -1
  149. package/lib/module/remote.js.map +0 -1
  150. package/lib/module/state.js.map +0 -1
  151. package/lib/module/store/imageEventStore.js.map +0 -1
  152. package/lib/module/store/insights.js.map +0 -1
  153. package/lib/module/sync/imagesSyncAdapter.js.map +0 -1
  154. package/lib/module/types.js.map +0 -1
  155. package/lib/typescript/autoCapture.d.ts.map +0 -1
  156. package/lib/typescript/autoCapture.web.d.ts.map +0 -1
  157. package/lib/typescript/capture/actions.d.ts.map +0 -1
  158. package/lib/typescript/capture/install.d.ts.map +0 -1
  159. package/lib/typescript/capture/install.web.d.ts.map +0 -1
  160. package/lib/typescript/capture/installExpoImage.d.ts.map +0 -1
  161. package/lib/typescript/capture/installRnImage.d.ts.map +0 -1
  162. package/lib/typescript/capture/nativeImageModules.d.ts.map +0 -1
  163. package/lib/typescript/capture/nativeImageModules.web.d.ts.map +0 -1
  164. package/lib/typescript/capture/overrides.d.ts.map +0 -1
  165. package/lib/typescript/capture/rnDecoratorEarly.d.ts.map +0 -1
  166. package/lib/typescript/capture/savings.d.ts.map +0 -1
  167. package/lib/typescript/capture/savings.web.d.ts.map +0 -1
  168. package/lib/typescript/capture/shared.d.ts.map +0 -1
  169. package/lib/typescript/components/ImageDetail.d.ts.map +0 -1
  170. package/lib/typescript/components/ImageDetailFooter.d.ts.map +0 -1
  171. package/lib/typescript/components/ImageRow.d.ts.map +0 -1
  172. package/lib/typescript/components/ImagesHeader.d.ts.map +0 -1
  173. package/lib/typescript/components/ImagesIcon.d.ts.map +0 -1
  174. package/lib/typescript/components/ImagesListFooter.d.ts.map +0 -1
  175. package/lib/typescript/components/ImagesModal.d.ts.map +0 -1
  176. package/lib/typescript/components/ImagesRoot.d.ts.map +0 -1
  177. package/lib/typescript/components/ImagesRoot.web.d.ts.map +0 -1
  178. package/lib/typescript/components/InsightsBar.d.ts.map +0 -1
  179. package/lib/typescript/export.d.ts.map +0 -1
  180. package/lib/typescript/format.d.ts.map +0 -1
  181. package/lib/typescript/hooks/useImageRecords.d.ts.map +0 -1
  182. package/lib/typescript/index.d.ts.map +0 -1
  183. package/lib/typescript/preset.d.ts.map +0 -1
  184. package/lib/typescript/register.d.ts.map +0 -1
  185. package/lib/typescript/registerEarly.d.ts.map +0 -1
  186. package/lib/typescript/remote.d.ts.map +0 -1
  187. package/lib/typescript/state.d.ts.map +0 -1
  188. package/lib/typescript/store/imageEventStore.d.ts.map +0 -1
  189. package/lib/typescript/store/insights.d.ts.map +0 -1
  190. package/lib/typescript/sync/imagesSyncAdapter.d.ts.map +0 -1
  191. package/lib/typescript/types.d.ts.map +0 -1
  192. package/lib/web/chunks/chunk-7CWLRQ2L.mjs +0 -2
  193. package/lib/web/chunks/chunk-B5A6CYSS.mjs +0 -2
  194. package/lib/web/chunks/chunk-LKTR6X2V.mjs +0 -2
@@ -1,71 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.proveSavings = proveSavings;
7
- var _remote = require("../remote");
8
- var _imageEventStore = require("../store/imageEventStore");
9
- const urls = new Map();
10
- async function proveSavings(record) {
11
- const remote = (0, _remote.remoteImagesDispatch)();
12
- if (remote) return await remote('proveSavings', {
13
- id: record.id
14
- });
15
- const dimensions = (0, _imageEventStore.neededPixels)(record);
16
- if (!dimensions) return {
17
- ok: false,
18
- message: 'Measure the displayed image before optimizing it.'
19
- };
20
- try {
21
- const response = await fetch(record.uri);
22
- if (!response.ok) throw new Error(`Image download returned HTTP ${response.status}.`);
23
- const original = await response.blob();
24
- const bitmap = await createImageBitmap(original);
25
- const canvas = document.createElement('canvas');
26
- canvas.width = Math.max(1, Math.min(bitmap.width, Math.round(dimensions.width)));
27
- canvas.height = Math.max(1, Math.min(bitmap.height, Math.round(dimensions.height)));
28
- const context = canvas.getContext('2d');
29
- if (!context) {
30
- bitmap.close();
31
- throw new Error('Canvas is unavailable.');
32
- }
33
- context.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
34
- bitmap.close();
35
- const optimized = await new Promise((resolve, reject) => canvas.toBlob(blob => blob ? resolve(blob) : reject(new Error('Image encoding failed.')), 'image/webp', 0.8));
36
- if (optimized.type !== 'image/webp') return {
37
- ok: false,
38
- message: 'This browser cannot encode WebP images.'
39
- };
40
- const previous = urls.get(record.id);
41
- if (previous) URL.revokeObjectURL(previous);
42
- const uri = URL.createObjectURL(optimized);
43
- urls.set(record.id, uri);
44
- while (urls.size > 30) {
45
- const [id, value] = urls.entries().next().value;
46
- URL.revokeObjectURL(value);
47
- urls.delete(id);
48
- }
49
- const savedBytes = original.size - optimized.size;
50
- return {
51
- ok: true,
52
- message: 'WebP preview created.',
53
- originalBytes: original.size,
54
- originalBytesSource: 'download',
55
- optimizedBytes: optimized.size,
56
- optimizedUri: uri,
57
- optimizedDims: {
58
- width: canvas.width,
59
- height: canvas.height
60
- },
61
- savedBytes,
62
- savedPct: original.size ? savedBytes / original.size * 100 : 0
63
- };
64
- } catch (error) {
65
- return {
66
- ok: false,
67
- message: `Image optimization failed: ${error instanceof Error ? error.message : String(error)}`
68
- };
69
- }
70
- }
71
- //# sourceMappingURL=savings.web.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.proveSavings=proveSavings;var _remote=require("../remote");var _imageEventStore=require("../store/imageEventStore");const urls=new Map;async function proveSavings(i){const c=(0,_remote.remoteImagesDispatch)();if(c)return await c("proveSavings",{id:i.id});const r=(0,_imageEventStore.neededPixels)(i);if(!r)return{ok:false,message:"Measure the displayed image before optimizing it."};try{const t=await fetch(i.uri);if(!t.ok)throw new Error(`Image download returned HTTP ${t.status}.`);const a=await t.blob();const s=await createImageBitmap(a);const e=document.createElement("canvas");e.width=Math.max(1,Math.min(s.width,Math.round(r.width)));e.height=Math.max(1,Math.min(s.height,Math.round(r.height)));const d=e.getContext("2d");if(!d){s.close();throw new Error("Canvas is unavailable.")}d.drawImage(s,0,0,e.width,e.height);s.close();const o=await new Promise((n,m)=>e.toBlob(w=>w?n(w):m(new Error("Image encoding failed.")),"image/webp",.8));if(o.type!=="image/webp")return{ok:false,message:"This browser cannot encode WebP images."};const g=urls.get(i.id);if(g)URL.revokeObjectURL(g);const h=URL.createObjectURL(o);urls.set(i.id,h);while(urls.size>30){const[n,m]=urls.entries().next().value;URL.revokeObjectURL(m);urls.delete(n)}const u=a.size-o.size;return{ok:true,message:"WebP preview created.",originalBytes:a.size,originalBytesSource:"download",optimizedBytes:o.size,optimizedUri:h,optimizedDims:{width:e.width,height:e.height},savedBytes:u,savedPct:a.size?u/a.size*100:0}}catch(t){return{ok:false,message:`Image optimization failed: ${t instanceof Error?t.message:String(t)}`}}}
@@ -1,378 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.ImageInstanceTracker = exports.INTERNAL_TEST_ID_PREFIX = void 0;
7
- exports.classifyUri = classifyUri;
8
- exports.isInternalImage = isInternalImage;
9
- exports.isNoiseUri = isNoiseUri;
10
- exports.payloadOf = payloadOf;
11
- exports.resolveSourceUri = resolveSourceUri;
12
- var _state = require("../state");
13
- var _actions = require("./actions");
14
- var _imageEventStore = require("../store/imageEventStore");
15
- /**
16
- * Capture core shared by the RN <Image> decorator and the expo-image patch.
17
- *
18
- * ImageInstanceTracker: one per live image component instance. It rolls a new
19
- * ImageRecord whenever the instance's source URI changes, and exposes wrapped
20
- * event handlers that feed the store while still calling through to whatever
21
- * handlers the app passed.
22
- */
23
-
24
- /**
25
- * Images rendered by Buoy's own UI (e.g. thumbnails inside the Images modal)
26
- * carry this testID prefix and are excluded from capture, so the tool doesn't
27
- * pollute its own registry.
28
- */
29
- const INTERNAL_TEST_ID_PREFIX = exports.INTERNAL_TEST_ID_PREFIX = "buoy-internal";
30
- function isInternalImage(testID) {
31
- return typeof testID === "string" && testID.startsWith(INTERNAL_TEST_ID_PREFIX);
32
- }
33
-
34
- /**
35
- * React Native's own devtools UI loads images too (LogBox icons resolve to
36
- * Metro asset URLs under Libraries/LogBox). They're noise in an app-focused
37
- * registry — skip them.
38
- */
39
- function isNoiseUri(uri) {
40
- return uri.includes("Libraries%2FLogBox") || uri.includes("Libraries/LogBox") || uri.includes("LogBoxImages");
41
- }
42
- function classifyUri(uri) {
43
- if (uri.startsWith("http://") || uri.startsWith("https://")) return "network";
44
- if (uri.startsWith("file://") || uri.startsWith("asset://")) return "file";
45
- if (uri.startsWith("data:")) return "data";
46
- return "other";
47
- }
48
-
49
- /**
50
- * Best-effort URI extraction from any source prop shape (RN or expo-image):
51
- * string, number (Metro asset), {uri}, arrays of those, ImageRef.
52
- */
53
- function resolveSourceUri(source) {
54
- if (source == null) return {
55
- uri: "",
56
- kind: "other"
57
- };
58
- if (Array.isArray(source)) {
59
- return resolveSourceUri(source.length > 0 ? source[0] : null);
60
- }
61
- if (typeof source === "string") {
62
- return {
63
- uri: source,
64
- kind: classifyUri(source)
65
- };
66
- }
67
- if (typeof source === "number") {
68
- // Metro asset id. Image is guaranteed evaluated by the time an instance
69
- // renders, so requiring it lazily here can never latch the decorator late.
70
- try {
71
- const rn = require("react-native");
72
- const resolved = rn.Image?.resolveAssetSource?.(source);
73
- if (resolved?.uri) return {
74
- uri: resolved.uri,
75
- kind: "asset"
76
- };
77
- } catch {
78
- // fall through
79
- }
80
- return {
81
- uri: `asset:#${source}`,
82
- kind: "asset"
83
- };
84
- }
85
- if (typeof source === "object") {
86
- const uri = source.uri;
87
- if (typeof uri === "string") {
88
- const kind = classifyUri(uri);
89
- // require()'d assets resolve to Metro dev-server URLs in dev — still
90
- // report them as assets when they carry the packager marker.
91
- if (source.__packager_asset) {
92
- return {
93
- uri,
94
- kind: "asset"
95
- };
96
- }
97
- return {
98
- uri,
99
- kind
100
- };
101
- }
102
- // expo-image ImageRef (native shared object) has width/height but no uri.
103
- const width = source.width;
104
- if (typeof width === "number") return {
105
- uri: "(ImageRef)",
106
- kind: "other"
107
- };
108
- }
109
- return {
110
- uri: "",
111
- kind: "other"
112
- };
113
- }
114
-
115
- /** The event-handler subset we intercept, normalized across both libraries. */
116
-
117
- /**
118
- * RN events arrive as {nativeEvent}; expo-image passes the payload directly.
119
- *
120
- * The shape is told apart by the property DESCRIPTOR, never by reading the
121
- * property. expo-image (SDK 56+) installs a deprecation GETTER called
122
- * `nativeEvent` on the payload it hands us — so the obvious
123
- * `event.nativeEvent` sniff logged
124
- * '[expo-image]: Accessing event payload through "nativeEvent" is deprecated'
125
- * once per load event, for every image in the app, purely because Buoy
126
- * looked. A real RN synthetic event carries it as a plain data property;
127
- * `getOwnPropertyDescriptor` distinguishes the two and invokes nothing.
128
- */
129
- function payloadOf(event) {
130
- if (!event || typeof event !== "object") return {};
131
- const descriptor = Object.getOwnPropertyDescriptor(event, "nativeEvent");
132
- const native = descriptor && !descriptor.get ? descriptor.value : undefined;
133
- if (native && typeof native === "object") {
134
- return native;
135
- }
136
- return event;
137
- }
138
-
139
- /**
140
- * After an RN-core load, classify the cache via Image.queryCache.
141
- *
142
- * Delayed + retried: NSURLCache commits fresh downloads asynchronously, so a
143
- * query at the instant of onLoad reports a miss for images that ARE being
144
- * cached (observed live). First probe after 600ms, one re-probe at 3s to
145
- * catch slow cache writes.
146
- */
147
- function queryRnCache(recordId, uri) {
148
- const probe = () => {
149
- try {
150
- const rn = require("react-native");
151
- const queryCache = rn.Image?.queryCache;
152
- if (typeof queryCache !== "function" || !uri) return;
153
- queryCache([uri]).then(result => {
154
- const verdict = result?.[uri];
155
- if (verdict) {
156
- (0, _imageEventStore.updateRecord)(recordId, {
157
- cacheVerdict: verdict,
158
- cacheVerdictSource: "queryCache"
159
- });
160
- }
161
- }).catch(() => {});
162
- } catch {
163
- // queryCache unavailable (web) — leave verdict unknown.
164
- }
165
- };
166
- setTimeout(probe, 600);
167
- setTimeout(() => {
168
- const record = (0, _imageEventStore.getRecord)(recordId);
169
- if (record && record.cacheVerdictSource !== "queryCache") probe();
170
- }, 3000);
171
- }
172
- class ImageInstanceTracker {
173
- recordId = null;
174
- uri = null;
175
- handlerCache = null;
176
- constructor(lib) {
177
- this.lib = lib;
178
- }
179
- getRecordId() {
180
- return this.recordId;
181
- }
182
-
183
- /** Record accessibility presence (called per render; writes only on change). */
184
- noteA11y(hasAltText) {
185
- if (this.recordId == null) return;
186
- const record = (0, _imageEventStore.getRecord)(this.recordId);
187
- if (record && record.hasAltText !== hasAltText) {
188
- (0, _imageEventStore.updateRecord)(this.recordId, {
189
- hasAltText
190
- });
191
- }
192
- }
193
-
194
- /** Call on every render with the current source prop. Rolls records on change. */
195
- syncSource(source) {
196
- const {
197
- uri,
198
- kind
199
- } = resolveSourceUri(source);
200
- if (uri === this.uri) return;
201
- this.uri = uri;
202
- this.handlerCache = null; // handlers close over recordId via getter, but roll anyway
203
- if (this.recordId != null) (0, _actions.dropActionState)(this.recordId);
204
- if (!uri || isNoiseUri(uri)) {
205
- this.recordId = null;
206
- return;
207
- }
208
- const record = (0, _imageEventStore.createRecord)({
209
- lib: this.lib,
210
- uri,
211
- sourceKind: kind
212
- });
213
- this.recordId = record.id;
214
- }
215
- unmount() {
216
- if (this.recordId != null) {
217
- (0, _imageEventStore.updateRecord)(this.recordId, {
218
- mounted: false
219
- });
220
- (0, _actions.dropActionState)(this.recordId);
221
- }
222
- }
223
-
224
- /**
225
- * Wrapped handlers for the current originals. Cached by handler identity so
226
- * re-renders with unchanged props reuse the same functions.
227
- */
228
- handlers(orig) {
229
- const key = [orig.onLoadStart, orig.onProgress, orig.onLoad, orig.onError, orig.onLoadEnd, orig.onDisplay, orig.onLayout, this.recordId];
230
- const cached = this.handlerCache;
231
- if (cached && cached.key.length === key.length && cached.key.every((k, i) => k === key[i])) {
232
- return cached.out;
233
- }
234
- const recordId = this.recordId;
235
- const lib = this.lib;
236
- const uri = this.uri ?? "";
237
- const out = {
238
- onLoadStart: event => {
239
- if (recordId != null) {
240
- const record = (0, _imageEventStore.getRecord)(recordId);
241
- const now = Date.now();
242
- // Out-of-order defense (seen live on iOS remounts/cache hits AND
243
- // instant file:// failures): the native onLoadStart of a burst can
244
- // arrive AFTER its onLoad/onError. A "start" right after a settled
245
- // outcome belongs to that same burst — don't regress the record.
246
- const settledAt = Math.max(record?.loadedAt ?? 0, record?.erroredAt ?? 0);
247
- const settledJustNow = settledAt > 0 && now - settledAt < 300;
248
- (0, _imageEventStore.updateRecord)(recordId, {
249
- ...(settledJustNow ? {} : {
250
- status: "loading",
251
- error: undefined,
252
- errorCode: undefined,
253
- errorHeaders: undefined
254
- }),
255
- startedAt: now,
256
- loadCount: (record?.loadCount ?? 0) + 1
257
- });
258
- }
259
- orig.onLoadStart?.(event);
260
- },
261
- onProgress: event => {
262
- if (recordId != null) {
263
- const p = payloadOf(event);
264
- (0, _imageEventStore.updateRecord)(recordId, {
265
- progressSeen: true,
266
- bytesLoaded: typeof p.loaded === "number" ? p.loaded : undefined,
267
- bytesTotal: typeof p.total === "number" ? p.total : undefined
268
- });
269
- }
270
- orig.onProgress?.(event);
271
- },
272
- onLoad: event => {
273
- if (recordId != null) {
274
- const p = payloadOf(event);
275
- const source = p.source ?? {};
276
- const now = Date.now();
277
- const record = (0, _imageEventStore.getRecord)(recordId);
278
- // Duration only counts from a start belonging to THIS load cycle: a
279
- // start newer than the previous completion. Without one (instant
280
- // cache hit whose onLoadStart arrives late/never), report 0ms.
281
- const start = record?.startedAt != null && record.startedAt <= now && record.startedAt > (record.loadedAt ?? record.createdAt - 1) ? record.startedAt : record?.loadedAt == null ? record?.startedAt ?? record?.createdAt ?? now : now;
282
- const width = typeof source.width === "number" ? source.width : 0;
283
- const height = typeof source.height === "number" ? source.height : 0;
284
- (0, _imageEventStore.updateRecord)(recordId, {
285
- status: "loaded",
286
- loadedAt: now,
287
- durationMs: Math.max(0, now - start),
288
- error: undefined,
289
- errorCode: undefined,
290
- errorHeaders: undefined,
291
- intrinsic: width > 0 && height > 0 ? {
292
- width,
293
- height
294
- } : record?.intrinsic,
295
- cacheVerdict: typeof p.cacheType === "string" ? p.cacheType : record?.cacheVerdict,
296
- cacheVerdictSource: typeof p.cacheType === "string" ? "event" : record?.cacheVerdictSource,
297
- mediaType: typeof source.mediaType === "string" || source.mediaType === null ? source.mediaType : record?.mediaType,
298
- isAnimated: typeof source.isAnimated === "boolean" ? source.isAnimated : record?.isAnimated
299
- });
300
- (0, _actions.settleReloadRequest)(recordId);
301
- if (lib === "rn") queryRnCache(recordId, uri);
302
- }
303
- orig.onLoad?.(event);
304
- },
305
- onError: event => {
306
- if (recordId != null) {
307
- const p = payloadOf(event);
308
- let message = typeof p.error === "string" ? p.error : String(p.error?.message ?? "unknown error");
309
- // Simulation sources produce raw file-not-found noise — translate to
310
- // an honest label (state-based, so it works for both libs' error
311
- // texts), in our record AND in the event the app receives.
312
- const record0 = (0, _imageEventStore.getRecord)(recordId);
313
- const s = (0, _state.imagesState)();
314
- const activeOverride = s.overrides.get(recordId);
315
- if (activeOverride?.source?.type === "error") {
316
- message = "Forced error (Buoy Images simulation)";
317
- if (typeof p.error === "string") p.error = message;
318
- } else if (s.globalModes.network === "offline" && record0?.sourceKind === "network") {
319
- message = "Offline simulation (Buoy Images): network image blocked";
320
- if (typeof p.error === "string") p.error = message;
321
- }
322
- (0, _imageEventStore.updateRecord)(recordId, {
323
- status: "error",
324
- erroredAt: Date.now(),
325
- error: message,
326
- errorCode: typeof p.responseCode === "number" ? p.responseCode : undefined,
327
- errorHeaders: p.httpResponseHeaders && typeof p.httpResponseHeaders === "object" ? p.httpResponseHeaders : undefined
328
- });
329
- (0, _actions.settleReloadRequest)(recordId);
330
- }
331
- orig.onError?.(event);
332
- },
333
- onLayout: event => {
334
- if (recordId != null) {
335
- const layout = payloadOf(event).layout ?? {};
336
- const width = typeof layout.width === "number" ? layout.width : 0;
337
- const height = typeof layout.height === "number" ? layout.height : 0;
338
- if (width > 0 && height > 0) {
339
- const record = (0, _imageEventStore.getRecord)(recordId);
340
- const prev = record?.layout;
341
- // Size change AFTER the image loaded = layout-shift suspect (the
342
- // image arriving resized its box — reserve explicit dimensions).
343
- const shifted = prev != null && record?.loadedAt != null && (Math.abs(prev.width - width) > 1 || Math.abs(prev.height - height) > 1);
344
- (0, _imageEventStore.updateRecord)(recordId, {
345
- layout: {
346
- width,
347
- height
348
- },
349
- ...(shifted ? {
350
- layoutShifts: (record?.layoutShifts ?? 0) + 1
351
- } : {})
352
- });
353
- }
354
- }
355
- orig.onLayout?.(event);
356
- }
357
- };
358
-
359
- // Only forward-wrap the optional ones so we don't attach handlers a
360
- // library doesn't support (onDisplay is expo-only; onLoadEnd is RN-only).
361
- out.onLoadEnd = orig.onLoadEnd ? event => orig.onLoadEnd?.(event) : undefined;
362
- if (this.lib === "expo") {
363
- out.onDisplay = event => {
364
- if (recordId != null) (0, _imageEventStore.updateRecord)(recordId, {
365
- displayedAt: Date.now()
366
- });
367
- orig.onDisplay?.(event);
368
- };
369
- }
370
- this.handlerCache = {
371
- key,
372
- out
373
- };
374
- return out;
375
- }
376
- }
377
- exports.ImageInstanceTracker = ImageInstanceTracker;
378
- //# sourceMappingURL=shared.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.ImageInstanceTracker=exports.INTERNAL_TEST_ID_PREFIX=void 0;exports.classifyUri=classifyUri;exports.isInternalImage=isInternalImage;exports.isNoiseUri=isNoiseUri;exports.payloadOf=payloadOf;exports.resolveSourceUri=resolveSourceUri;var _state=require("../state");var _actions=require("./actions");var _imageEventStore=require("../store/imageEventStore");const INTERNAL_TEST_ID_PREFIX=exports.INTERNAL_TEST_ID_PREFIX="buoy-internal";function isInternalImage(t){return typeof t==="string"&&t.startsWith(INTERNAL_TEST_ID_PREFIX)}function isNoiseUri(t){return t.includes("Libraries%2FLogBox")||t.includes("Libraries/LogBox")||t.includes("LogBoxImages")}function classifyUri(t){if(t.startsWith("http://")||t.startsWith("https://"))return"network";if(t.startsWith("file://")||t.startsWith("asset://"))return"file";if(t.startsWith("data:"))return"data";return"other"}function resolveSourceUri(t){if(t==null)return{uri:"",kind:"other"};if(Array.isArray(t)){return resolveSourceUri(t.length>0?t[0]:null)}if(typeof t==="string"){return{uri:t,kind:classifyUri(t)}}if(typeof t==="number"){try{const e=require("react-native");const n=e.Image?.resolveAssetSource?.(t);if(n?.uri)return{uri:n.uri,kind:"asset"}}catch{}return{uri:`asset:#${t}`,kind:"asset"}}if(typeof t==="object"){const e=t.uri;if(typeof e==="string"){const c=classifyUri(e);if(t.__packager_asset){return{uri:e,kind:"asset"}}return{uri:e,kind:c}}const n=t.width;if(typeof n==="number")return{uri:"(ImageRef)",kind:"other"}}return{uri:"",kind:"other"}}function payloadOf(t){if(!t||typeof t!=="object")return{};const e=Object.getOwnPropertyDescriptor(t,"nativeEvent");const n=e&&!e.get?e.value:void 0;if(n&&typeof n==="object"){return n}return t}function queryRnCache(t,e){const n=()=>{try{const c=require("react-native");const o=c.Image?.queryCache;if(typeof o!=="function"||!e)return;o([e]).then(p=>{const l=p?.[e];if(l){(0,_imageEventStore.updateRecord)(t,{cacheVerdict:l,cacheVerdictSource:"queryCache"})}}).catch(()=>{})}catch{}};setTimeout(n,600);setTimeout(()=>{const c=(0,_imageEventStore.getRecord)(t);if(c&&c.cacheVerdictSource!=="queryCache")n()},3e3)}class ImageInstanceTracker{recordId=null;uri=null;handlerCache=null;constructor(e){this.lib=e}getRecordId(){return this.recordId}noteA11y(e){if(this.recordId==null)return;const n=(0,_imageEventStore.getRecord)(this.recordId);if(n&&n.hasAltText!==e){(0,_imageEventStore.updateRecord)(this.recordId,{hasAltText:e})}}syncSource(e){const{uri:n,kind:c}=resolveSourceUri(e);if(n===this.uri)return;this.uri=n;this.handlerCache=null;if(this.recordId!=null)(0,_actions.dropActionState)(this.recordId);if(!n||isNoiseUri(n)){this.recordId=null;return}const o=(0,_imageEventStore.createRecord)({lib:this.lib,uri:n,sourceKind:c});this.recordId=o.id}unmount(){if(this.recordId!=null){(0,_imageEventStore.updateRecord)(this.recordId,{mounted:false});(0,_actions.dropActionState)(this.recordId)}}handlers(e){const n=[e.onLoadStart,e.onProgress,e.onLoad,e.onError,e.onLoadEnd,e.onDisplay,e.onLayout,this.recordId];const c=this.handlerCache;if(c&&c.key.length===n.length&&c.key.every((d,r)=>d===n[r])){return c.out}const o=this.recordId;const p=this.lib;const l=this.uri??"";const h={onLoadStart:d=>{if(o!=null){const r=(0,_imageEventStore.getRecord)(o);const i=Date.now();const a=Math.max(r?.loadedAt??0,r?.erroredAt??0);const s=a>0&&i-a<300;(0,_imageEventStore.updateRecord)(o,{...s?{}:{status:"loading",error:void 0,errorCode:void 0,errorHeaders:void 0},startedAt:i,loadCount:(r?.loadCount??0)+1})}e.onLoadStart?.(d)},onProgress:d=>{if(o!=null){const r=payloadOf(d);(0,_imageEventStore.updateRecord)(o,{progressSeen:true,bytesLoaded:typeof r.loaded==="number"?r.loaded:void 0,bytesTotal:typeof r.total==="number"?r.total:void 0})}e.onProgress?.(d)},onLoad:d=>{if(o!=null){const r=payloadOf(d);const i=r.source??{};const a=Date.now();const s=(0,_imageEventStore.getRecord)(o);const u=s?.startedAt!=null&&s.startedAt<=a&&s.startedAt>(s.loadedAt??s.createdAt-1)?s.startedAt:s?.loadedAt==null?s?.startedAt??s?.createdAt??a:a;const f=typeof i.width==="number"?i.width:0;const y=typeof i.height==="number"?i.height:0;(0,_imageEventStore.updateRecord)(o,{status:"loaded",loadedAt:a,durationMs:Math.max(0,a-u),error:void 0,errorCode:void 0,errorHeaders:void 0,intrinsic:f>0&&y>0?{width:f,height:y}:s?.intrinsic,cacheVerdict:typeof r.cacheType==="string"?r.cacheType:s?.cacheVerdict,cacheVerdictSource:typeof r.cacheType==="string"?"event":s?.cacheVerdictSource,mediaType:typeof i.mediaType==="string"||i.mediaType===null?i.mediaType:s?.mediaType,isAnimated:typeof i.isAnimated==="boolean"?i.isAnimated:s?.isAnimated});(0,_actions.settleReloadRequest)(o);if(p==="rn")queryRnCache(o,l)}e.onLoad?.(d)},onError:d=>{if(o!=null){const r=payloadOf(d);let i=typeof r.error==="string"?r.error:String(r.error?.message??"unknown error");const a=(0,_imageEventStore.getRecord)(o);const s=(0,_state.imagesState)();const u=s.overrides.get(o);if(u?.source?.type==="error"){i="Forced error (Buoy Images simulation)";if(typeof r.error==="string")r.error=i}else if(s.globalModes.network==="offline"&&a?.sourceKind==="network"){i="Offline simulation (Buoy Images): network image blocked";if(typeof r.error==="string")r.error=i}(0,_imageEventStore.updateRecord)(o,{status:"error",erroredAt:Date.now(),error:i,errorCode:typeof r.responseCode==="number"?r.responseCode:void 0,errorHeaders:r.httpResponseHeaders&&typeof r.httpResponseHeaders==="object"?r.httpResponseHeaders:void 0});(0,_actions.settleReloadRequest)(o)}e.onError?.(d)},onLayout:d=>{if(o!=null){const r=payloadOf(d).layout??{};const i=typeof r.width==="number"?r.width:0;const a=typeof r.height==="number"?r.height:0;if(i>0&&a>0){const s=(0,_imageEventStore.getRecord)(o);const u=s?.layout;const f=u!=null&&s?.loadedAt!=null&&(Math.abs(u.width-i)>1||Math.abs(u.height-a)>1);(0,_imageEventStore.updateRecord)(o,{layout:{width:i,height:a},...f?{layoutShifts:(s?.layoutShifts??0)+1}:{}})}}e.onLayout?.(d)}};h.onLoadEnd=e.onLoadEnd?d=>e.onLoadEnd?.(d):void 0;if(this.lib==="expo"){h.onDisplay=d=>{if(o!=null)(0,_imageEventStore.updateRecord)(o,{displayedAt:Date.now()});e.onDisplay?.(d)}}this.handlerCache={key:n,out:h};return h}}exports.ImageInstanceTracker=ImageInstanceTracker;