@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,346 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.applyOverrides = applyOverrides;
7
- exports.clearAllOverrides = clearAllOverrides;
8
- exports.clearOverride = clearOverride;
9
- exports.flashAllMounted = flashAllMounted;
10
- exports.flashRecord = flashRecord;
11
- exports.getGlobalModes = getGlobalModes;
12
- exports.getOverride = getOverride;
13
- exports.getRenderVersion = getRenderVersion;
14
- exports.patchSourceCache = patchSourceCache;
15
- exports.setBlankImages = setBlankImages;
16
- exports.setNetworkMode = setNetworkMode;
17
- exports.setOverride = setOverride;
18
- exports.setOverrideForAllMounted = setOverrideForAllMounted;
19
- exports.touchGlobal = touchGlobal;
20
- exports.touchRecord = touchRecord;
21
- var _remote = require("../remote");
22
- var _state = require("../state");
23
- var _imageEventStore = require("../store/imageEventStore");
24
- /**
25
- * Simulation overrides — the "we own the props" tier of actions.
26
- *
27
- * Because the RN decorator and the expo-image render patch control every
28
- * mounted image's props, anything the component accepts is a simulation:
29
- * forced errors (instant, offline-safe file:// source), forced loading
30
- * (blackhole-IP source that never resolves), URL swaps, blanking, style
31
- * merges (locate-flash border), and app-wide modes (offline images /
32
- * cold-start / blank images).
33
- *
34
- * Re-render plumbing: every mutation bumps a per-record or global version.
35
- * RN wrappers subscribe via useSyncExternalStore with a per-record snapshot
36
- * (only the touched image re-renders); expo class instances are re-rendered
37
- * directly via forceUpdate() from the per-record instance registry.
38
- */
39
-
40
- // ---------------------------------------------------------------------------
41
- // Override model
42
- // ---------------------------------------------------------------------------
43
-
44
- const FORCED_ERROR_URI = "file:///buoy-images-forced-error.png";
45
- const HANG_URI = "https://10.255.255.1/buoy-images-hang.png";
46
- /** Offline mode replaces network sources with this (fails immediately). */
47
- const OFFLINE_URI = "file:///buoy-images-offline-no-network.png";
48
- const FLASH_STYLE = {
49
- borderWidth: 3,
50
- borderColor: "#FF453A"
51
- };
52
- /**
53
- * RN core has NO safe "empty source": undefined resolves to the app bundle
54
- * path and [] does too on RN 0.85 — both fire a bogus onError ("The file …
55
- * couldn't be opened", observed live). So RN images blank via opacity
56
- * instead (visual-only; the decoded image stays resident). expo-image
57
- * supports a true `source={null}` and keeps showing its placeholder.
58
- */
59
- const RN_BLANK_STYLE = {
60
- opacity: 0
61
- };
62
- // ---------------------------------------------------------------------------
63
- // Version bus (re-render triggers)
64
- // ---------------------------------------------------------------------------
65
-
66
- function emit() {
67
- for (const l of (0, _state.imagesState)().actionListeners) l();
68
- }
69
- function forceUpdateExpo(recordId) {
70
- const s = (0, _state.imagesState)();
71
- if (recordId != null) {
72
- const instance = s.expoInstances.get(recordId);
73
- instance?.forceUpdate?.();
74
- return;
75
- }
76
- for (const instance of s.expoInstances.values()) {
77
- instance?.forceUpdate?.();
78
- }
79
- }
80
-
81
- /** Bump one record's render version (re-renders just that image). */
82
- function touchRecord(recordId) {
83
- const s = (0, _state.imagesState)();
84
- s.recordVersions.set(recordId, ++s.versionCounter);
85
- emit();
86
- forceUpdateExpo(recordId);
87
- }
88
-
89
- /** Bump the global render version (re-renders every wrapped image). */
90
- function touchGlobal() {
91
- const s = (0, _state.imagesState)();
92
- s.globalVersion = ++s.versionCounter;
93
- emit();
94
- forceUpdateExpo();
95
- }
96
-
97
- /** Snapshot for the RN wrapper's useSyncExternalStore. */
98
- function getRenderVersion(recordId) {
99
- const s = (0, _state.imagesState)();
100
- const record = recordId != null ? s.recordVersions.get(recordId) ?? 0 : 0;
101
- return Math.max(record, s.globalVersion);
102
- }
103
-
104
- // ---------------------------------------------------------------------------
105
- // Per-record overrides
106
- // ---------------------------------------------------------------------------
107
-
108
- /** Remote param shape for the adapter's `setOverride` action. */
109
- function overrideParams(recordId, source) {
110
- return source.type === "url" ? {
111
- id: recordId,
112
- kind: "url",
113
- uri: source.uri
114
- } : {
115
- id: recordId,
116
- kind: source.type
117
- };
118
- }
119
-
120
- /**
121
- * Local bookkeeping only (no remote forward) — mirrors keep this as an
122
- * optimistic UI update; the next device snapshot is the source of truth.
123
- */
124
- function setOverrideLocal(recordId, override) {
125
- (0, _state.imagesState)().overrides.set(recordId, override);
126
- (0, _imageEventStore.updateRecord)(recordId, {
127
- overrideLabel: override.label
128
- });
129
- touchRecord(recordId);
130
- }
131
- function setOverride(recordId, override) {
132
- if (override.source) {
133
- void (0, _remote.remoteImagesDispatch)()?.("setOverride", overrideParams(recordId, override.source));
134
- }
135
- setOverrideLocal(recordId, override);
136
- }
137
- function clearOverrideLocal(recordId) {
138
- (0, _state.imagesState)().overrides.delete(recordId);
139
- (0, _imageEventStore.updateRecord)(recordId, {
140
- overrideLabel: undefined
141
- });
142
- touchRecord(recordId);
143
- }
144
- function clearOverride(recordId) {
145
- void (0, _remote.remoteImagesDispatch)()?.("clearOverride", {
146
- id: recordId
147
- });
148
- clearOverrideLocal(recordId);
149
- }
150
- function getOverride(recordId) {
151
- if (recordId == null) return undefined;
152
- return (0, _state.imagesState)().overrides.get(recordId);
153
- }
154
-
155
- // ---------------------------------------------------------------------------
156
- // Mass actions (every mounted image at once)
157
- // ---------------------------------------------------------------------------
158
-
159
- /** Adapter `massAction` kind for a mass override (per-record 'hang' is mass 'loading'). */
160
- function massKind(source) {
161
- return source.type === "hang" ? "loading" : source.type;
162
- }
163
-
164
- /** Apply one override to every mounted record. Returns how many were hit. */
165
- function setOverrideForAllMounted(source, label) {
166
- // One mass action on the device, not N per-record forwards.
167
- void (0, _remote.remoteImagesDispatch)()?.("massAction", {
168
- kind: massKind(source)
169
- });
170
- let count = 0;
171
- for (const record of (0, _imageEventStore.getSnapshot)()) {
172
- if (!record.mounted) continue;
173
- setOverrideLocal(record.id, {
174
- source,
175
- label
176
- });
177
- count++;
178
- }
179
- return count;
180
- }
181
-
182
- /** Remove every active override. Returns how many were cleared. */
183
- function clearAllOverrides() {
184
- void (0, _remote.remoteImagesDispatch)()?.("massAction", {
185
- kind: "restore"
186
- });
187
- // Mirrors track labels on records, not the overrides map — clear both.
188
- const ids = new Set((0, _state.imagesState)().overrides.keys());
189
- for (const record of (0, _imageEventStore.getSnapshot)()) {
190
- if (record.overrideLabel != null) ids.add(record.id);
191
- }
192
- for (const id of ids) clearOverrideLocal(id);
193
- return ids.size;
194
- }
195
-
196
- /** Flash every mounted image (locate everything the tool tracks). */
197
- function flashAllMounted(durationMs = 2500) {
198
- void (0, _remote.remoteImagesDispatch)()?.("massAction", {
199
- kind: "flash"
200
- });
201
- let count = 0;
202
- for (const record of (0, _imageEventStore.getSnapshot)()) {
203
- if (!record.mounted) continue;
204
- flashRecordLocal(record.id, durationMs);
205
- count++;
206
- }
207
- return count;
208
- }
209
- function flashRecordLocal(recordId, durationMs = 2500) {
210
- const s = (0, _state.imagesState)();
211
- s.flashUntil.set(recordId, Date.now() + durationMs);
212
- touchRecord(recordId);
213
- setTimeout(() => {
214
- (0, _state.imagesState)().flashUntil.delete(recordId);
215
- touchRecord(recordId);
216
- }, durationMs + 50);
217
- }
218
-
219
- /** Flash a border on the on-screen image so it can be located visually. */
220
- function flashRecord(recordId, durationMs = 2500) {
221
- void (0, _remote.remoteImagesDispatch)()?.("flash", {
222
- id: recordId
223
- });
224
- flashRecordLocal(recordId, durationMs);
225
- }
226
-
227
- // ---------------------------------------------------------------------------
228
- // Global simulation modes
229
- // ---------------------------------------------------------------------------
230
-
231
- /**
232
- * offline — every NETWORK image load fails immediately (both libs, cache or
233
- * not); bundled/local assets still load, like a real offline device showing
234
- * shipped images. Blunt on purpose: toggle + reload = images visibly gone.
235
- * (The subtler `only-if-cached` variant was correct-but-confusing, and its
236
- * uncached requests HANG on iOS instead of erroring — bad for QA.)
237
- * cold — every image bypasses caches (RN `cache:'reload'`, expo
238
- * `cachePolicy:'none'`): first-launch behavior without clearing anything.
239
- */
240
- function setNetworkMode(mode) {
241
- void (0, _remote.remoteImagesDispatch)()?.("setNetworkMode", {
242
- mode
243
- });
244
- (0, _state.imagesState)().globalModes.network = mode;
245
- touchGlobal();
246
- }
247
-
248
- /** Chrome-style "disable images": render every image without a source. */
249
- function setBlankImages(enabled) {
250
- void (0, _remote.remoteImagesDispatch)()?.("setBlankImages", {
251
- enabled
252
- });
253
- (0, _state.imagesState)().globalModes.blank = enabled;
254
- touchGlobal();
255
- }
256
- function getGlobalModes() {
257
- return (0, _state.imagesState)().globalModes;
258
- }
259
-
260
- // ---------------------------------------------------------------------------
261
- // Application (called from the RN wrapper + expo render patch)
262
- // ---------------------------------------------------------------------------
263
-
264
- /** Inject a `cache` policy into an RN source (object or array shape). */
265
- function patchSourceCache(source, cache) {
266
- if (Array.isArray(source)) {
267
- return source.map(entry => entry && typeof entry === "object" && typeof entry.uri === "string" ? {
268
- ...entry,
269
- cache
270
- } : entry);
271
- }
272
- if (source && typeof source === "object" && typeof source.uri === "string") {
273
- return {
274
- ...source,
275
- cache
276
- };
277
- }
278
- return source; // numeric assets etc. — nothing to patch
279
- }
280
- function resolveOverrideSource(source, lib) {
281
- switch (source.type) {
282
- case "url":
283
- return {
284
- uri: source.uri
285
- };
286
- case "error":
287
- return {
288
- uri: FORCED_ERROR_URI
289
- };
290
- case "hang":
291
- return {
292
- uri: HANG_URI
293
- };
294
- case "blank":
295
- // Only reached for expo — RN blanking is style-based (see RN_BLANK_STYLE).
296
- return null;
297
- }
298
- }
299
-
300
- /**
301
- * Merge global modes + per-record override + locate-flash into an
302
- * already-copied props object. Mutates and returns `out`.
303
- */
304
- function applyOverrides(out, recordId, lib) {
305
- const s = (0, _state.imagesState)();
306
- const modes = s.globalModes;
307
- if (modes.network === "offline") {
308
- // Hard offline: network sources fail outright (both libs); bundled/local
309
- // assets keep loading like a real offline device.
310
- const record = recordId != null ? (0, _imageEventStore.getRecord)(recordId) : undefined;
311
- if (record?.sourceKind === "network") {
312
- out.source = {
313
- uri: OFFLINE_URI
314
- };
315
- }
316
- } else if (modes.network === "cold") {
317
- if (lib === "rn") {
318
- out.source = patchSourceCache(out.source, "reload");
319
- } else {
320
- out.cachePolicy = "none";
321
- }
322
- }
323
- if (modes.blank) {
324
- if (lib === "expo") out.source = null;else out.style = [out.style, RN_BLANK_STYLE];
325
- }
326
- const override = getOverride(recordId);
327
- if (override) {
328
- if (override.source) {
329
- if (override.source.type === "blank" && lib === "rn") {
330
- out.style = [out.style, RN_BLANK_STYLE];
331
- } else {
332
- out.source = resolveOverrideSource(override.source, lib);
333
- }
334
- }
335
- if (override.extraProps) Object.assign(out, override.extraProps);
336
- if (override.style) out.style = [out.style, override.style];
337
- }
338
- if (recordId != null) {
339
- const until = s.flashUntil.get(recordId);
340
- if (until != null && until > Date.now()) {
341
- out.style = [out.style, FLASH_STYLE];
342
- }
343
- }
344
- return out;
345
- }
346
- //# sourceMappingURL=overrides.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.applyOverrides=applyOverrides;exports.clearAllOverrides=clearAllOverrides;exports.clearOverride=clearOverride;exports.flashAllMounted=flashAllMounted;exports.flashRecord=flashRecord;exports.getGlobalModes=getGlobalModes;exports.getOverride=getOverride;exports.getRenderVersion=getRenderVersion;exports.patchSourceCache=patchSourceCache;exports.setBlankImages=setBlankImages;exports.setNetworkMode=setNetworkMode;exports.setOverride=setOverride;exports.setOverrideForAllMounted=setOverrideForAllMounted;exports.touchGlobal=touchGlobal;exports.touchRecord=touchRecord;var _remote=require("../remote");var _state=require("../state");var _imageEventStore=require("../store/imageEventStore");const FORCED_ERROR_URI="file:///buoy-images-forced-error.png";const HANG_URI="https://10.255.255.1/buoy-images-hang.png";const OFFLINE_URI="file:///buoy-images-offline-no-network.png";const FLASH_STYLE={borderWidth:3,borderColor:"#FF453A"};const RN_BLANK_STYLE={opacity:0};function emit(){for(const e of(0,_state.imagesState)().actionListeners)e()}function forceUpdateExpo(e){const t=(0,_state.imagesState)();if(e!=null){const s=t.expoInstances.get(e);s?.forceUpdate?.();return}for(const s of t.expoInstances.values()){s?.forceUpdate?.()}}function touchRecord(e){const t=(0,_state.imagesState)();t.recordVersions.set(e,++t.versionCounter);emit();forceUpdateExpo(e)}function touchGlobal(){const e=(0,_state.imagesState)();e.globalVersion=++e.versionCounter;emit();forceUpdateExpo()}function getRenderVersion(e){const t=(0,_state.imagesState)();const s=e!=null?t.recordVersions.get(e)??0:0;return Math.max(s,t.globalVersion)}function overrideParams(e,t){return t.type==="url"?{id:e,kind:"url",uri:t.uri}:{id:e,kind:t.type}}function setOverrideLocal(e,t){(0,_state.imagesState)().overrides.set(e,t);(0,_imageEventStore.updateRecord)(e,{overrideLabel:t.label});touchRecord(e)}function setOverride(e,t){if(t.source){void(0,_remote.remoteImagesDispatch)()?.("setOverride",overrideParams(e,t.source))}setOverrideLocal(e,t)}function clearOverrideLocal(e){(0,_state.imagesState)().overrides.delete(e);(0,_imageEventStore.updateRecord)(e,{overrideLabel:void 0});touchRecord(e)}function clearOverride(e){void(0,_remote.remoteImagesDispatch)()?.("clearOverride",{id:e});clearOverrideLocal(e)}function getOverride(e){if(e==null)return void 0;return(0,_state.imagesState)().overrides.get(e)}function massKind(e){return e.type==="hang"?"loading":e.type}function setOverrideForAllMounted(e,t){void(0,_remote.remoteImagesDispatch)()?.("massAction",{kind:massKind(e)});let s=0;for(const r of(0,_imageEventStore.getSnapshot)()){if(!r.mounted)continue;setOverrideLocal(r.id,{source:e,label:t});s++}return s}function clearAllOverrides(){void(0,_remote.remoteImagesDispatch)()?.("massAction",{kind:"restore"});const e=new Set((0,_state.imagesState)().overrides.keys());for(const t of(0,_imageEventStore.getSnapshot)()){if(t.overrideLabel!=null)e.add(t.id)}for(const t of e)clearOverrideLocal(t);return e.size}function flashAllMounted(e=2500){void(0,_remote.remoteImagesDispatch)()?.("massAction",{kind:"flash"});let t=0;for(const s of(0,_imageEventStore.getSnapshot)()){if(!s.mounted)continue;flashRecordLocal(s.id,e);t++}return t}function flashRecordLocal(e,t=2500){const s=(0,_state.imagesState)();s.flashUntil.set(e,Date.now()+t);touchRecord(e);setTimeout(()=>{(0,_state.imagesState)().flashUntil.delete(e);touchRecord(e)},t+50)}function flashRecord(e,t=2500){void(0,_remote.remoteImagesDispatch)()?.("flash",{id:e});flashRecordLocal(e,t)}function setNetworkMode(e){void(0,_remote.remoteImagesDispatch)()?.("setNetworkMode",{mode:e});(0,_state.imagesState)().globalModes.network=e;touchGlobal()}function setBlankImages(e){void(0,_remote.remoteImagesDispatch)()?.("setBlankImages",{enabled:e});(0,_state.imagesState)().globalModes.blank=e;touchGlobal()}function getGlobalModes(){return(0,_state.imagesState)().globalModes}function patchSourceCache(e,t){if(Array.isArray(e)){return e.map(s=>s&&typeof s==="object"&&typeof s.uri==="string"?{...s,cache:t}:s)}if(e&&typeof e==="object"&&typeof e.uri==="string"){return{...e,cache:t}}return e}function resolveOverrideSource(e,t){switch(e.type){case"url":return{uri:e.uri};case"error":return{uri:FORCED_ERROR_URI};case"hang":return{uri:HANG_URI};case"blank":return null}}function applyOverrides(e,t,s){const r=(0,_state.imagesState)();const i=r.globalModes;if(i.network==="offline"){const n=t!=null?(0,_imageEventStore.getRecord)(t):void 0;if(n?.sourceKind==="network"){e.source={uri:OFFLINE_URI}}}else if(i.network==="cold"){if(s==="rn"){e.source=patchSourceCache(e.source,"reload")}else{e.cachePolicy="none"}}if(i.blank){if(s==="expo")e.source=null;else e.style=[e.style,RN_BLANK_STYLE]}const o=getOverride(t);if(o){if(o.source){if(o.source.type==="blank"&&s==="rn"){e.style=[e.style,RN_BLANK_STYLE]}else{e.source=resolveOverrideSource(o.source,s)}}if(o.extraProps)Object.assign(e,o.extraProps);if(o.style)e.style=[e.style,o.style]}if(t!=null){const n=r.flashUntil.get(t);if(n!=null&&n>Date.now()){e.style=[e.style,FLASH_STYLE]}}return e}
@@ -1,55 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.installRnImageDecoratorEarly = installRnImageDecoratorEarly;
7
- var React = _interopRequireWildcard(require("react"));
8
- var _state = require("../state");
9
- var _jsxRuntime = require("react/jsx-runtime");
10
- 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); }
11
- /**
12
- * Minimal RN <Image> decorator install, safe to run before React Native's
13
- * InitializeCore.
14
- *
15
- * RN latches the decorator once, when `Image.ios.js` / `Image.android.js` is
16
- * first evaluated. In development builds that happens during InitializeCore:
17
- * AppRegistry pulls in the LogBox inspector, which imports Image. So even the
18
- * first import of the app's entry file (`@buoy-gg/images/register`) is too
19
- * late there, and the tool reports "missed the module-load race".
20
- *
21
- * `register` itself can't run before InitializeCore, because its imports touch
22
- * globals InitializeCore installs (FormData, for one). This module imports only
23
- * React, the ImageInjection hook and the globalThis state, and loads the
24
- * capture hook lazily on the first render, by which time everything exists.
25
- * Run it as a Metro pre-main module ahead of InitializeCore (see README).
26
- */
27
-
28
- function installRnImageDecoratorEarly() {
29
- const s = (0, _state.imagesState)();
30
- if (s.decoratorRegistered) return;
31
- try {
32
- const injection = require("react-native/Libraries/Image/ImageInjection");
33
- const setDecorator = injection.unstable_setImageComponentDecorator;
34
- if (typeof setDecorator !== "function") return;
35
- setDecorator(BaseImage => {
36
- (0, _state.imagesState)().decoratorInvoked = true;
37
- return /*#__PURE__*/React.forwardRef(function BuoyCapturedImage(props, ref) {
38
- // Same module every render, so the hook order stays stable.
39
- const useImageCapture = require("./installRnImage").useImageCapture;
40
- const {
41
- props: instrumented,
42
- reloadKey
43
- } = useImageCapture(props);
44
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(BaseImage, {
45
- ...instrumented,
46
- ref: ref
47
- }, `buoy-r${reloadKey}`);
48
- });
49
- });
50
- s.decoratorRegistered = true;
51
- } catch {
52
- // Internal RN path moved: the regular install reports the decorator inactive.
53
- }
54
- }
55
- //# sourceMappingURL=rnDecoratorEarly.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.installRnImageDecoratorEarly=installRnImageDecoratorEarly;var React=_interopRequireWildcard(require("react"));var _state=require("../state");var _jsxRuntime=require("react/jsx-runtime");function _interopRequireWildcard(o,s){if("function"==typeof WeakMap)var i=new WeakMap,c=new WeakMap;return(_interopRequireWildcard=function(e,u){if(!u&&e&&e.__esModule)return e;var t,a,n={__proto__:null,default:e};if(null===e||"object"!=typeof e&&"function"!=typeof e)return n;if(t=u?c:i){if(t.has(e))return t.get(e);t.set(e,n)}for(const r in e)"default"!==r&&{}.hasOwnProperty.call(e,r)&&((a=(t=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,r))&&(a.get||a.set)?t(n,r,a):n[r]=e[r]);return n})(o,s)}function installRnImageDecoratorEarly(){const o=(0,_state.imagesState)();if(o.decoratorRegistered)return;try{const s=require("react-native/Libraries/Image/ImageInjection");const i=s.unstable_setImageComponentDecorator;if(typeof i!=="function")return;i(c=>{(0,_state.imagesState)().decoratorInvoked=true;return React.forwardRef(function e(u,t){const a=require("./installRnImage").useImageCapture;const{props:n,reloadKey:r}=a(u);return(0,_jsxRuntime.jsx)(c,{...n,ref:t},`buoy-r${r}`)})});o.decoratorRegistered=true}catch{}}
@@ -1,209 +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
- /**
10
- * Savings prover — Squoosh-style, on device.
11
- *
12
- * Instead of estimating what an oversized source wastes, PROVE it: re-encode
13
- * the image at the displayed size (× pixel ratio) as WebP via
14
- * expo-image-manipulator (verified: chainable resize → saveAsync WebP on BOTH
15
- * platforms, available in Expo Go), stat the output file, and report real
16
- * bytes vs the original. The optimized file:// URI can then be A/B'd in place
17
- * through the existing swap-url override.
18
- *
19
- * Original size is resolved in tiers: observed download bytes (onProgress
20
- * total) → expo disk-cache file size → HEAD Content-Length.
21
- */
22
-
23
- function loadLegacyFs() {
24
- try {
25
- const mod = require("expo-file-system/legacy");
26
- const fs = mod?.default || mod;
27
- if (fs?.getInfoAsync) return fs;
28
- } catch {
29
- // try main import
30
- }
31
- try {
32
- const mod = require("expo-file-system");
33
- const fs = mod?.default || mod;
34
- if (fs?.getInfoAsync) return fs;
35
- } catch {
36
- // not installed
37
- }
38
- return null;
39
- }
40
- async function fileSize(uri) {
41
- const fs = loadLegacyFs();
42
- if (!fs?.getInfoAsync) return null;
43
- try {
44
- const normalized = uri.startsWith("file://") ? uri : `file://${uri}`;
45
- const info = await fs.getInfoAsync(normalized, {
46
- size: true
47
- });
48
- return info.exists && typeof info.size === "number" ? info.size : null;
49
- } catch {
50
- return null;
51
- }
52
- }
53
- async function resolveOriginalBytes(record) {
54
- if (record.bytesTotal && record.bytesTotal > 0) {
55
- return {
56
- bytes: record.bytesTotal,
57
- source: "download"
58
- };
59
- }
60
- // expo disk cache file
61
- if (record.lib === "expo") {
62
- try {
63
- const expo = require("expo-image");
64
- const path = await expo.Image.getCachePathAsync(record.uri);
65
- if (path) {
66
- const size = await fileSize(path);
67
- if (size != null) return {
68
- bytes: size,
69
- source: "cacheFile"
70
- };
71
- }
72
- } catch {
73
- // fall through
74
- }
75
- }
76
- // HEAD request (network sources only)
77
- if (record.sourceKind === "network") {
78
- try {
79
- const response = await fetch(record.uri, {
80
- method: "HEAD"
81
- });
82
- const length = Number(response.headers.get("content-length"));
83
- if (Number.isFinite(length) && length > 0) return {
84
- bytes: length,
85
- source: "head"
86
- };
87
- } catch {
88
- // unknown original size is fine
89
- }
90
- }
91
- return null;
92
- }
93
-
94
- /**
95
- * Re-encode `record`'s source at its displayed size as WebP and measure it.
96
- * Requires expo-image-manipulator (graceful message when absent).
97
- */
98
- async function proveSavings(record) {
99
- // Mirror mode: the re-encode must happen ON DEVICE (that's the whole point —
100
- // real bytes from the real encoder); forward and relay the result.
101
- const remote = (0, _remote.remoteImagesDispatch)();
102
- if (remote) {
103
- try {
104
- return await remote("proveSavings", {
105
- id: record.id
106
- });
107
- } catch (error) {
108
- return {
109
- ok: false,
110
- message: String(error?.message ?? error)
111
- };
112
- }
113
- }
114
- if (!record.uri || record.sourceKind !== "network" && record.sourceKind !== "asset" && record.sourceKind !== "file") {
115
- return {
116
- ok: false,
117
- message: "No loadable source URI on this record"
118
- };
119
- }
120
- const needed = (0, _imageEventStore.neededPixels)(record);
121
- if (!needed) {
122
- return {
123
- ok: false,
124
- message: "No layout yet — the image must render once to know its displayed size"
125
- };
126
- }
127
- let manipulator;
128
- try {
129
- manipulator = require("expo-image-manipulator");
130
- } catch {
131
- return {
132
- ok: false,
133
- message: "expo-image-manipulator not installed — add it to enable real savings measurement"
134
- };
135
- }
136
- const webp = manipulator.SaveFormat?.WEBP ?? "webp";
137
- const targetWidth = Math.min(needed.width, record.intrinsic?.width ?? needed.width);
138
- let original = null;
139
- let optimized = null;
140
- try {
141
- [original, optimized] = await Promise.all([resolveOriginalBytes(record), (async () => {
142
- // Preferred: context API (current). Fallback: deprecated manipulateAsync.
143
- if (manipulator.ImageManipulator?.manipulate) {
144
- const context = manipulator.ImageManipulator.manipulate(record.uri);
145
- try {
146
- const ref = await context.resize({
147
- width: targetWidth
148
- }).renderAsync();
149
- try {
150
- return await ref.saveAsync({
151
- format: webp,
152
- compress: 0.8
153
- });
154
- } finally {
155
- ref.release?.();
156
- }
157
- } finally {
158
- context.release?.();
159
- }
160
- }
161
- if (manipulator.manipulateAsync) {
162
- return manipulator.manipulateAsync(record.uri, [{
163
- resize: {
164
- width: targetWidth
165
- }
166
- }], {
167
- compress: 0.8,
168
- format: webp
169
- });
170
- }
171
- return null;
172
- })()]);
173
- } catch (error) {
174
- return {
175
- ok: false,
176
- message: `Re-encode failed: ${String(error?.message ?? error)}`
177
- };
178
- }
179
- if (!optimized?.uri) {
180
- return {
181
- ok: false,
182
- message: "Re-encode produced no output (unsupported source?)"
183
- };
184
- }
185
- const optimizedBytes = await fileSize(optimized.uri);
186
- const result = {
187
- ok: true,
188
- message: "",
189
- optimizedUri: optimized.uri,
190
- optimizedBytes: optimizedBytes ?? undefined,
191
- optimizedDims: {
192
- width: optimized.width,
193
- height: optimized.height
194
- },
195
- originalBytes: original?.bytes,
196
- originalBytesSource: original?.source
197
- };
198
- if (original?.bytes != null && optimizedBytes != null) {
199
- result.savedBytes = Math.max(0, original.bytes - optimizedBytes);
200
- result.savedPct = original.bytes > 0 ? Math.round(result.savedBytes / original.bytes * 100) : 0;
201
- result.message = `Right-sized WebP is ${result.savedPct}% smaller (real bytes, encoded on device)`;
202
- } else if (optimizedBytes != null) {
203
- result.message = "Optimized variant encoded — original size unknown (no download bytes, cache entry, or Content-Length)";
204
- } else {
205
- result.message = "Optimized variant encoded — could not stat the output file";
206
- }
207
- return result;
208
- }
209
- //# sourceMappingURL=savings.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.proveSavings=proveSavings;var _remote=require("../remote");var _imageEventStore=require("../store/imageEventStore");function loadLegacyFs(){try{const e=require("expo-file-system/legacy");const t=e?.default||e;if(t?.getInfoAsync)return t}catch{}try{const e=require("expo-file-system");const t=e?.default||e;if(t?.getInfoAsync)return t}catch{}return null}async function fileSize(e){const t=loadLegacyFs();if(!t?.getInfoAsync)return null;try{const n=e.startsWith("file://")?e:`file://${e}`;const s=await t.getInfoAsync(n,{size:true});return s.exists&&typeof s.size==="number"?s.size:null}catch{return null}}async function resolveOriginalBytes(e){if(e.bytesTotal&&e.bytesTotal>0){return{bytes:e.bytesTotal,source:"download"}}if(e.lib==="expo"){try{const t=require("expo-image");const n=await t.Image.getCachePathAsync(e.uri);if(n){const s=await fileSize(n);if(s!=null)return{bytes:s,source:"cacheFile"}}}catch{}}if(e.sourceKind==="network"){try{const t=await fetch(e.uri,{method:"HEAD"});const n=Number(t.headers.get("content-length"));if(Number.isFinite(n)&&n>0)return{bytes:n,source:"head"}}catch{}}return null}async function proveSavings(e){const t=(0,_remote.remoteImagesDispatch)();if(t){try{return await t("proveSavings",{id:e.id})}catch(a){return{ok:false,message:String(a?.message??a)}}}if(!e.uri||e.sourceKind!=="network"&&e.sourceKind!=="asset"&&e.sourceKind!=="file"){return{ok:false,message:"No loadable source URI on this record"}}const n=(0,_imageEventStore.neededPixels)(e);if(!n){return{ok:false,message:"No layout yet \u2014 the image must render once to know its displayed size"}}let s;try{s=require("expo-image-manipulator")}catch{return{ok:false,message:"expo-image-manipulator not installed \u2014 add it to enable real savings measurement"}}const u=s.SaveFormat?.WEBP??"webp";const c=Math.min(n.width,e.intrinsic?.width??n.width);let r=null;let o=null;try{[r,o]=await Promise.all([resolveOriginalBytes(e),(async()=>{if(s.ImageManipulator?.manipulate){const a=s.ImageManipulator.manipulate(e.uri);try{const m=await a.resize({width:c}).renderAsync();try{return await m.saveAsync({format:u,compress:.8})}finally{m.release?.()}}finally{a.release?.()}}if(s.manipulateAsync){return s.manipulateAsync(e.uri,[{resize:{width:c}}],{compress:.8,format:u})}return null})()])}catch(a){return{ok:false,message:`Re-encode failed: ${String(a?.message??a)}`}}if(!o?.uri){return{ok:false,message:"Re-encode produced no output (unsupported source?)"}}const l=await fileSize(o.uri);const i={ok:true,message:"",optimizedUri:o.uri,optimizedBytes:l??void 0,optimizedDims:{width:o.width,height:o.height},originalBytes:r?.bytes,originalBytesSource:r?.source};if(r?.bytes!=null&&l!=null){i.savedBytes=Math.max(0,r.bytes-l);i.savedPct=r.bytes>0?Math.round(i.savedBytes/r.bytes*100):0;i.message=`Right-sized WebP is ${i.savedPct}% smaller (real bytes, encoded on device)`}else if(l!=null){i.message="Optimized variant encoded \u2014 original size unknown (no download bytes, cache entry, or Content-Length)"}else{i.message="Optimized variant encoded \u2014 could not stat the output file"}return i}