@buoy-gg/assets 7.0.36 → 7.0.38

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 (165) hide show
  1. package/README.md +41 -12
  2. package/lib/commonjs/capture/expo.js +1 -46
  3. package/lib/commonjs/capture/install.js +1 -78
  4. package/lib/commonjs/capture/isBuoyAsset.js +1 -0
  5. package/lib/commonjs/capture/measure.js +1 -186
  6. package/lib/commonjs/capture/metroGraph.js +1 -148
  7. package/lib/commonjs/capture/registry.js +1 -127
  8. package/lib/commonjs/components/AssetDetail.js +1 -214
  9. package/lib/commonjs/components/AssetImage.js +1 -0
  10. package/lib/commonjs/components/AssetRow.js +1 -233
  11. package/lib/commonjs/components/AssetsFilterBar.js +1 -96
  12. package/lib/commonjs/components/AssetsHeader.js +1 -72
  13. package/lib/commonjs/components/AssetsIcon.js +1 -19
  14. package/lib/commonjs/components/AssetsListControls.js +1 -96
  15. package/lib/commonjs/components/AssetsModal.js +1 -347
  16. package/lib/commonjs/components/ScanNotice.js +1 -0
  17. package/lib/commonjs/components/optionalExpoImage.js +1 -0
  18. package/lib/commonjs/components/optionalExpoImage.web.js +1 -0
  19. package/lib/commonjs/export.js +1 -62
  20. package/lib/commonjs/format.js +1 -59
  21. package/lib/commonjs/hooks/useAssetRecords.js +1 -22
  22. package/lib/commonjs/hooks/useAssetScanStatus.js +1 -0
  23. package/lib/commonjs/index.js +1 -313
  24. package/lib/commonjs/preset.js +1 -39
  25. package/lib/commonjs/remote.js +1 -65
  26. package/lib/commonjs/state.js +1 -51
  27. package/lib/commonjs/store/assetStore.js +1 -241
  28. package/lib/commonjs/store/baseline.js +1 -173
  29. package/lib/commonjs/store/insights.js +1 -120
  30. package/lib/commonjs/sync/assetsSyncAdapter.js +1 -157
  31. package/lib/commonjs/types.js +1 -2
  32. package/lib/commonjs/utils/deviceScale.js +1 -50
  33. package/lib/commonjs/utils/testIDs.js +1 -114
  34. package/lib/module/capture/expo.js +1 -41
  35. package/lib/module/capture/install.js +1 -72
  36. package/lib/module/capture/isBuoyAsset.js +1 -0
  37. package/lib/module/capture/measure.js +1 -179
  38. package/lib/module/capture/metroGraph.js +1 -141
  39. package/lib/module/capture/registry.js +1 -120
  40. package/lib/module/components/AssetDetail.js +1 -210
  41. package/lib/module/components/AssetImage.js +1 -0
  42. package/lib/module/components/AssetRow.js +1 -229
  43. package/lib/module/components/AssetsFilterBar.js +1 -92
  44. package/lib/module/components/AssetsHeader.js +1 -68
  45. package/lib/module/components/AssetsIcon.js +1 -12
  46. package/lib/module/components/AssetsListControls.js +1 -92
  47. package/lib/module/components/AssetsModal.js +1 -342
  48. package/lib/module/components/ScanNotice.js +1 -0
  49. package/lib/module/components/optionalExpoImage.js +1 -0
  50. package/lib/module/components/optionalExpoImage.web.js +1 -0
  51. package/lib/module/export.js +1 -57
  52. package/lib/module/format.js +1 -51
  53. package/lib/module/hooks/useAssetRecords.js +1 -18
  54. package/lib/module/hooks/useAssetScanStatus.js +1 -0
  55. package/lib/module/index.js +1 -63
  56. package/lib/module/preset.js +1 -35
  57. package/lib/module/remote.js +1 -58
  58. package/lib/module/state.js +1 -47
  59. package/lib/module/store/assetStore.js +1 -226
  60. package/lib/module/store/baseline.js +1 -165
  61. package/lib/module/store/insights.js +1 -116
  62. package/lib/module/sync/assetsSyncAdapter.js +1 -153
  63. package/lib/module/types.js +1 -2
  64. package/lib/module/utils/deviceScale.js +1 -46
  65. package/lib/module/utils/testIDs.js +1 -103
  66. package/lib/typescript/capture/isBuoyAsset.d.ts +9 -0
  67. package/lib/typescript/capture/registry.d.ts +1 -1
  68. package/lib/typescript/components/AssetDetail.d.ts +2 -2
  69. package/lib/typescript/components/AssetImage.d.ts +9 -0
  70. package/lib/typescript/components/AssetsFilterBar.d.ts +3 -2
  71. package/lib/typescript/components/AssetsHeader.d.ts +2 -1
  72. package/lib/typescript/components/AssetsListControls.d.ts +2 -9
  73. package/lib/typescript/components/ScanNotice.d.ts +7 -0
  74. package/lib/typescript/components/optionalExpoImage.d.ts +13 -0
  75. package/lib/typescript/components/optionalExpoImage.web.d.ts +3 -0
  76. package/lib/typescript/hooks/useAssetScanStatus.d.ts +6 -0
  77. package/lib/typescript/state.d.ts +2 -0
  78. package/lib/typescript/store/assetStore.d.ts +3 -1
  79. package/lib/typescript/types.d.ts +3 -0
  80. package/lib/typescript/utils/testIDs.d.ts +2 -5
  81. package/package.json +7 -3
  82. package/lib/commonjs/capture/expo.js.map +0 -1
  83. package/lib/commonjs/capture/install.js.map +0 -1
  84. package/lib/commonjs/capture/measure.js.map +0 -1
  85. package/lib/commonjs/capture/metroGraph.js.map +0 -1
  86. package/lib/commonjs/capture/registry.js.map +0 -1
  87. package/lib/commonjs/components/AssetDetail.js.map +0 -1
  88. package/lib/commonjs/components/AssetRow.js.map +0 -1
  89. package/lib/commonjs/components/AssetsFilterBar.js.map +0 -1
  90. package/lib/commonjs/components/AssetsHeader.js.map +0 -1
  91. package/lib/commonjs/components/AssetsIcon.js.map +0 -1
  92. package/lib/commonjs/components/AssetsListControls.js.map +0 -1
  93. package/lib/commonjs/components/AssetsModal.js.map +0 -1
  94. package/lib/commonjs/components/BaselineBar.js +0 -115
  95. package/lib/commonjs/components/BaselineBar.js.map +0 -1
  96. package/lib/commonjs/export.js.map +0 -1
  97. package/lib/commonjs/format.js.map +0 -1
  98. package/lib/commonjs/hooks/useAssetRecords.js.map +0 -1
  99. package/lib/commonjs/index.js.map +0 -1
  100. package/lib/commonjs/preset.js.map +0 -1
  101. package/lib/commonjs/remote.js.map +0 -1
  102. package/lib/commonjs/state.js.map +0 -1
  103. package/lib/commonjs/store/assetStore.js.map +0 -1
  104. package/lib/commonjs/store/baseline.js.map +0 -1
  105. package/lib/commonjs/store/insights.js.map +0 -1
  106. package/lib/commonjs/sync/assetsSyncAdapter.js.map +0 -1
  107. package/lib/commonjs/types.js.map +0 -1
  108. package/lib/commonjs/utils/deviceScale.js.map +0 -1
  109. package/lib/commonjs/utils/testIDs.js.map +0 -1
  110. package/lib/module/capture/expo.js.map +0 -1
  111. package/lib/module/capture/install.js.map +0 -1
  112. package/lib/module/capture/measure.js.map +0 -1
  113. package/lib/module/capture/metroGraph.js.map +0 -1
  114. package/lib/module/capture/registry.js.map +0 -1
  115. package/lib/module/components/AssetDetail.js.map +0 -1
  116. package/lib/module/components/AssetRow.js.map +0 -1
  117. package/lib/module/components/AssetsFilterBar.js.map +0 -1
  118. package/lib/module/components/AssetsHeader.js.map +0 -1
  119. package/lib/module/components/AssetsIcon.js.map +0 -1
  120. package/lib/module/components/AssetsListControls.js.map +0 -1
  121. package/lib/module/components/AssetsModal.js.map +0 -1
  122. package/lib/module/components/BaselineBar.js +0 -111
  123. package/lib/module/components/BaselineBar.js.map +0 -1
  124. package/lib/module/export.js.map +0 -1
  125. package/lib/module/format.js.map +0 -1
  126. package/lib/module/hooks/useAssetRecords.js.map +0 -1
  127. package/lib/module/index.js.map +0 -1
  128. package/lib/module/preset.js.map +0 -1
  129. package/lib/module/remote.js.map +0 -1
  130. package/lib/module/state.js.map +0 -1
  131. package/lib/module/store/assetStore.js.map +0 -1
  132. package/lib/module/store/baseline.js.map +0 -1
  133. package/lib/module/store/insights.js.map +0 -1
  134. package/lib/module/sync/assetsSyncAdapter.js.map +0 -1
  135. package/lib/module/types.js.map +0 -1
  136. package/lib/module/utils/deviceScale.js.map +0 -1
  137. package/lib/module/utils/testIDs.js.map +0 -1
  138. package/lib/typescript/capture/expo.d.ts.map +0 -1
  139. package/lib/typescript/capture/install.d.ts.map +0 -1
  140. package/lib/typescript/capture/measure.d.ts.map +0 -1
  141. package/lib/typescript/capture/metroGraph.d.ts.map +0 -1
  142. package/lib/typescript/capture/registry.d.ts.map +0 -1
  143. package/lib/typescript/components/AssetDetail.d.ts.map +0 -1
  144. package/lib/typescript/components/AssetRow.d.ts.map +0 -1
  145. package/lib/typescript/components/AssetsFilterBar.d.ts.map +0 -1
  146. package/lib/typescript/components/AssetsHeader.d.ts.map +0 -1
  147. package/lib/typescript/components/AssetsIcon.d.ts.map +0 -1
  148. package/lib/typescript/components/AssetsListControls.d.ts.map +0 -1
  149. package/lib/typescript/components/AssetsModal.d.ts.map +0 -1
  150. package/lib/typescript/components/BaselineBar.d.ts +0 -13
  151. package/lib/typescript/components/BaselineBar.d.ts.map +0 -1
  152. package/lib/typescript/export.d.ts.map +0 -1
  153. package/lib/typescript/format.d.ts.map +0 -1
  154. package/lib/typescript/hooks/useAssetRecords.d.ts.map +0 -1
  155. package/lib/typescript/index.d.ts.map +0 -1
  156. package/lib/typescript/preset.d.ts.map +0 -1
  157. package/lib/typescript/remote.d.ts.map +0 -1
  158. package/lib/typescript/state.d.ts.map +0 -1
  159. package/lib/typescript/store/assetStore.d.ts.map +0 -1
  160. package/lib/typescript/store/baseline.d.ts.map +0 -1
  161. package/lib/typescript/store/insights.d.ts.map +0 -1
  162. package/lib/typescript/sync/assetsSyncAdapter.d.ts.map +0 -1
  163. package/lib/typescript/types.d.ts.map +0 -1
  164. package/lib/typescript/utils/deviceScale.d.ts.map +0 -1
  165. package/lib/typescript/utils/testIDs.d.ts.map +0 -1
@@ -1,157 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.assetsSyncAdapter = void 0;
7
- var _install = require("../capture/install");
8
- var _measure = require("../capture/measure");
9
- var _assetStore = require("../store/assetStore");
10
- var _baseline = require("../store/baseline");
11
- var _insights = require("../store/insights");
12
- /**
13
- * Sync adapter for the Assets tool, consumed by @buoy-gg/external-sync's
14
- * `useExternalSync` (structurally matches its ToolSyncAdapter interface so
15
- * this package doesn't need a dependency on it — same pattern as images).
16
- * Exposes the asset inventory to the desktop dashboard and the MCP server.
17
- */
18
-
19
- const KINDS = ["image", "font", "video", "audio", "data", "other"];
20
- function toWire(record) {
21
- return {
22
- id: record.id,
23
- key: record.key,
24
- registryId: record.registryId ?? null,
25
- name: record.name,
26
- type: record.type,
27
- kind: record.kind,
28
- hash: record.hash ?? null,
29
- location: record.httpServerLocation ?? null,
30
- width: record.width ?? null,
31
- height: record.height ?? null,
32
- scales: record.scales,
33
- uri: record.resolvedUri ?? null,
34
- loaded: record.loaded,
35
- sizeBytes: record.sizeSource === "measured" ? record.sizeBytes ?? null : (0, _assetStore.estDecodedBytes)(record) || null,
36
- sizeSource: record.sizeSource ?? null
37
- };
38
- }
39
- function requireRecord(params) {
40
- const id = params?.id;
41
- if (typeof id !== "number") throw new Error("Missing numeric `id` param");
42
- const record = (0, _assetStore.getRecord)(id);
43
- if (!record) {
44
- throw new Error(`Asset record ${id} not found — re-run the list action for current ids.`);
45
- }
46
- return record;
47
- }
48
-
49
- /** Largest-first is the useful default for an inventory. */
50
- function bySizeDesc(a, b) {
51
- return (b.sizeBytes ?? (0, _assetStore.estDecodedBytes)(b)) - (a.sizeBytes ?? (0, _assetStore.estDecodedBytes)(a));
52
- }
53
- function overview() {
54
- const snapshot = (0, _assetStore.getSnapshot)();
55
- return {
56
- stats: (0, _assetStore.computeStats)(snapshot),
57
- scanStatus: (0, _install.getScanStatus)(),
58
- insights: (0, _insights.computeInsights)(snapshot),
59
- diff: (0, _baseline.getDiff)(),
60
- records: [...snapshot].sort(bySizeDesc)
61
- };
62
- }
63
- const assetsSyncAdapter = exports.assetsSyncAdapter = {
64
- version: 1,
65
- getSnapshot: () => {
66
- const {
67
- records,
68
- ...rest
69
- } = overview();
70
- return {
71
- ...rest,
72
- records: records.slice(0, 200).map(toWire)
73
- };
74
- },
75
- subscribe: onChange => (0, _assetStore.subscribe)(onChange),
76
- actions: {
77
- /** List assets, largest first. Params: { limit?, kind?, loadedOnly? }. */
78
- list: params => {
79
- const p = params ?? {};
80
- const limit = Math.min(Math.max(p.limit ?? 50, 1), 500);
81
- const {
82
- records,
83
- ...rest
84
- } = overview();
85
- let filtered = records;
86
- if (p.kind) {
87
- if (!KINDS.includes(p.kind)) {
88
- throw new Error(`kind must be one of: ${KINDS.join(", ")}`);
89
- }
90
- filtered = filtered.filter(r => r.kind === p.kind);
91
- }
92
- if (p.loadedOnly) filtered = filtered.filter(r => r.loaded);
93
- return {
94
- ...rest,
95
- total: filtered.length,
96
- records: filtered.slice(0, limit).map(toWire)
97
- };
98
- },
99
- /** Full detail for one asset. Params: { id }. */
100
- getDetail: params => {
101
- const record = requireRecord(params);
102
- return {
103
- ...toWire(record),
104
- origin: record.origin,
105
- fileSystemLocation: record.fileSystemLocation ?? null,
106
- files: record.files ?? null,
107
- sizesByScale: record.sizesByScale ?? null,
108
- estDecodedBytes: (0, _assetStore.estDecodedBytes)(record) || null
109
- };
110
- },
111
- /** Re-walk the registry, merge the Metro graph, refresh fonts, measure sizes. */
112
- rescan: async () => {
113
- const status = await (0, _install.rescan)();
114
- return {
115
- ok: true,
116
- scanStatus: status
117
- };
118
- },
119
- /** (Re-)measure byte sizes from the dev server. */
120
- measureSizes: async () => {
121
- const result = await (0, _measure.measureSizes)();
122
- return {
123
- ok: result.measured > 0,
124
- ...result
125
- };
126
- },
127
- /** Persist the current inventory as the comparison baseline. */
128
- saveBaseline: async () => {
129
- const baseline = await (0, _baseline.saveBaseline)();
130
- return {
131
- ok: true,
132
- savedAt: baseline.savedAt,
133
- assets: Object.keys(baseline.entries).length
134
- };
135
- },
136
- /** Drop the saved baseline. */
137
- clearBaseline: async () => {
138
- await (0, _baseline.clearBaseline)();
139
- return {
140
- ok: true,
141
- message: "Baseline cleared"
142
- };
143
- },
144
- /** Change vs the saved baseline (null when no baseline exists). */
145
- getDiff: () => (0, _baseline.getDiff)(),
146
- /** Wipe the store (next rescan rebuilds it). */
147
- clearRecords: () => {
148
- (0, _assetStore.clearRecords)();
149
- return {
150
- ok: true,
151
- message: "Inventory cleared — rescan to rebuild"
152
- };
153
- },
154
- getScanStatus: () => (0, _install.getScanStatus)()
155
- }
156
- };
157
- //# sourceMappingURL=assetsSyncAdapter.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.assetsSyncAdapter=void 0;var _install=require("../capture/install"),_measure=require("../capture/measure"),_assetStore=require("../store/assetStore"),_baseline=require("../store/baseline"),_insights=require("../store/insights");const KINDS=["image","font","video","audio","data","other"];function toWire(e){return{id:e.id,key:e.key,registryId:e.registryId??null,name:e.name,type:e.type,kind:e.kind,hash:e.hash??null,location:e.httpServerLocation??null,width:e.width??null,height:e.height??null,scales:e.scales,uri:e.resolvedUri??null,loaded:e.loaded,sizeBytes:"measured"===e.sizeSource?e.sizeBytes??null:(0,_assetStore.estDecodedBytes)(e)||null,sizeSource:e.sizeSource??null}}function requireRecord(e){const s=e?.id;if("number"!=typeof s)throw new Error("Missing numeric `id` param");const t=(0,_assetStore.getRecord)(s);if(!t)throw new Error(`Asset record ${s} not found — re-run the list action for current ids.`);return t}function bySizeDesc(e,s){return(s.sizeBytes??(0,_assetStore.estDecodedBytes)(s))-(e.sizeBytes??(0,_assetStore.estDecodedBytes)(e))}function overview(){const e=(0,_assetStore.getSnapshot)();return{stats:(0,_assetStore.computeStats)(e),scanStatus:(0,_install.getScanStatus)(),insights:(0,_insights.computeInsights)(e),diff:(0,_baseline.getDiff)(),records:[...e].sort(bySizeDesc)}}const assetsSyncAdapter=exports.assetsSyncAdapter={version:1,getSnapshot:()=>{const{records:e,...s}=overview();return{...s,records:e.slice(0,200).map(toWire)}},subscribe:e=>(0,_assetStore.subscribe)(e),actions:{list:e=>{const s=e??{},t=Math.min(Math.max(s.limit??50,1),500),{records:r,...i}=overview();let n=r;if(s.kind){if(!KINDS.includes(s.kind))throw new Error(`kind must be one of: ${KINDS.join(", ")}`);n=n.filter(e=>e.kind===s.kind)}return s.loadedOnly&&(n=n.filter(e=>e.loaded)),{...i,total:n.length,records:n.slice(0,t).map(toWire)}},getDetail:e=>{const s=requireRecord(e);return{...toWire(s),origin:s.origin,fileSystemLocation:s.fileSystemLocation??null,files:s.files??null,sizesByScale:s.sizesByScale??null,estDecodedBytes:(0,_assetStore.estDecodedBytes)(s)||null}},rescan:async()=>({ok:!0,scanStatus:await(0,_install.rescan)()}),measureSizes:async()=>{const e=await(0,_measure.measureSizes)();return{ok:e.measured>0,...e}},saveBaseline:async()=>{const e=await(0,_baseline.saveBaseline)();return{ok:!0,savedAt:e.savedAt,assets:Object.keys(e.entries).length}},clearBaseline:async()=>(await(0,_baseline.clearBaseline)(),{ok:!0,message:"Baseline cleared"}),getDiff:()=>(0,_baseline.getDiff)(),clearRecords:()=>((0,_assetStore.clearRecords)(),{ok:!0,message:"Inventory cleared — rescan to rebuild"}),getScanStatus:()=>(0,_install.getScanStatus)()}};
@@ -1,2 +1 @@
1
- "use strict";
2
- //# sourceMappingURL=types.js.map
1
+ "use strict";
@@ -1,50 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.effectivePixelRatio = effectivePixelRatio;
7
- exports.pickScale = pickScale;
8
- var _state = require("../state");
9
- /**
10
- * Which scale variant this device would actually use.
11
- *
12
- * Shared by the decoded-memory estimate and the scale-gap insight so the two
13
- * can't disagree about the same asset — they were written apart and did.
14
- */
15
-
16
- /**
17
- * PixelRatio, or the DEVICE's when this store is a dashboard mirror.
18
- *
19
- * A desktop dashboard renders another machine's inventory, and its own
20
- * PixelRatio has nothing to do with which variant the phone will pick.
21
- */
22
- function effectivePixelRatio() {
23
- const mirror = (0, _state.assetsState)().mirrorPixelRatio;
24
- if (mirror != null) return mirror;
25
- try {
26
- return require("react-native").PixelRatio.get();
27
- } catch {
28
- return 2;
29
- }
30
- }
31
-
32
- /**
33
- * The variant React Native would resolve, by AssetSourceResolver's rule: the
34
- * smallest scale that covers the device, or the largest available when none
35
- * does.
36
- *
37
- * Not simply "the biggest one shipped". An asset with @1x…@4x variants on a
38
- * @3x phone decodes at 3, and calling it 4 overstates its memory by 78% —
39
- * on exactly the never-loaded assets this tool exists to point at, since a
40
- * loaded one carries the scale RN already picked.
41
- */
42
- function pickScale(scales, deviceScale) {
43
- if (!scales.length) return 1;
44
- const ordered = [...scales].sort((a, b) => a - b);
45
- for (const scale of ordered) {
46
- if (scale >= deviceScale) return scale;
47
- }
48
- return ordered[ordered.length - 1];
49
- }
50
- //# sourceMappingURL=deviceScale.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.effectivePixelRatio=effectivePixelRatio,exports.pickScale=pickScale;var _state=require("../state");function effectivePixelRatio(){const e=(0,_state.assetsState)().mirrorPixelRatio;if(null!=e)return e;try{return require("react-native").PixelRatio.get()}catch{return 2}}function pickScale(e,t){if(!e.length)return 1;const r=[...e].sort((e,t)=>e-t);for(const e of r)if(e>=t)return e;return r[r.length-1]}
@@ -1,114 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.ASSETS_TEST_IDS = void 0;
7
- exports.assetBadgeTestID = assetBadgeTestID;
8
- exports.assetDetailRowTestID = assetDetailRowTestID;
9
- exports.assetFilterTestID = assetFilterTestID;
10
- exports.assetFindingTestID = assetFindingTestID;
11
- exports.assetFontChipTestID = assetFontChipTestID;
12
- exports.assetRowTestID = assetRowTestID;
13
- exports.assetSlug = assetSlug;
14
- /**
15
- * Automation handles for the Assets tool.
16
- *
17
- * Every handle the tool renders is built here rather than inlined at the call
18
- * site, for the same reason the highlight tool does it: the ids for rows,
19
- * badges, filter chips and detail fields are DERIVED from data, and a suite
20
- * that reimplements the derivation is a suite that can drift from the tool
21
- * without either side failing. `detox-e2e/assets/fixture.js` mirrors these
22
- * rules in CommonJS (the tests can't import TypeScript) and asserts the two
23
- * still agree before any flow trusts a handle.
24
- *
25
- * Naming: `buoy-assets-<area>[-<detail>]`, one prefix for the whole tool.
26
- */
27
-
28
- /**
29
- * Lowercase, hyphen-joined, never empty.
30
- *
31
- * Shared by rows, badges, filter chips and detail fields so one asset's row
32
- * handle and its detail handles are slugged the same way — `back-icon.png`
33
- * becomes `back-icon-png` everywhere it appears.
34
- */
35
- function assetSlug(value) {
36
- return String(value).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
37
- }
38
-
39
- /**
40
- * One inventory row, addressed by filename rather than by store id.
41
- *
42
- * The store's numeric ids are assigned in first-sighting order and are NOT
43
- * stable: clearing the inventory and rescanning renumbers everything, and the
44
- * order the registry walk and the Metro graph merge in depends on what the app
45
- * has evaluated so far. A handle built from those makes a test that passes
46
- * only on the run that recorded it.
47
- *
48
- * The tradeoff is that two assets with the same filename in different
49
- * directories collide. That's a real ambiguity rather than a hidden one —
50
- * Detox reports it as "multiple elements matched", which names the problem —
51
- * and it's the same tradeoff the row itself makes, since the row shows the
52
- * filename and not the path.
53
- */
54
- function assetRowTestID(name, type) {
55
- return `buoy-assets-row-${assetSlug(`${name}.${type}`)}`;
56
- }
57
-
58
- /** A finding badge inside a row: `…-badge-low-res`, `…-badge-dup`. */
59
- function assetBadgeTestID(name, type, label) {
60
- return `${assetRowTestID(name, type)}-badge-${assetSlug(label)}`;
61
- }
62
-
63
- /** A kind/status filter chip: `buoy-assets-filter-font`, `…-filter-unused`. */
64
- function assetFilterTestID(filter) {
65
- return `buoy-assets-filter-${assetSlug(filter)}`;
66
- }
67
-
68
- /** One label/value pair in the detail view: `buoy-assets-detail-row-hash`. */
69
- function assetDetailRowTestID(label) {
70
- return `buoy-assets-detail-row-${assetSlug(label)}`;
71
- }
72
-
73
- /** A loaded font-family chip: `buoy-assets-font-ionicons`. */
74
- function assetFontChipTestID(family) {
75
- return `buoy-assets-font-${assetSlug(family)}`;
76
- }
77
-
78
- /** Fixed handles — everything that isn't derived from a record. */
79
- const ASSETS_TEST_IDS = exports.ASSETS_TEST_IDS = {
80
- // header
81
- title: "buoy-assets-title",
82
- copy: "buoy-assets-copy",
83
- clear: "buoy-assets-clear",
84
- // list chrome
85
- list: "buoy-assets-list",
86
- search: "buoy-assets-search",
87
- sort: "buoy-assets-sort",
88
- saveBaseline: "buoy-assets-save-baseline",
89
- filterBar: "buoy-assets-filter-bar",
90
- notice: "buoy-assets-notice",
91
- noticeText: "buoy-assets-notice-text",
92
- fonts: "buoy-assets-fonts",
93
- fontsTitle: "buoy-assets-fonts-title",
94
- empty: "buoy-assets-empty",
95
- emptyTitle: "buoy-assets-empty-title",
96
- emptyText: "buoy-assets-empty-text",
97
- // baseline bar
98
- baseline: "buoy-assets-baseline",
99
- baselineSummary: "buoy-assets-baseline-summary",
100
- baselineUpdate: "buoy-assets-baseline-update",
101
- baselineClear: "buoy-assets-baseline-clear",
102
- // detail
103
- detail: "buoy-assets-detail",
104
- detailScroll: "buoy-assets-detail-scroll",
105
- detailPreview: "buoy-assets-detail-preview",
106
- detailFontPreview: "buoy-assets-detail-font-preview",
107
- detailFindings: "buoy-assets-detail-findings"
108
- };
109
-
110
- /** One finding sentence in the detail view, by position. */
111
- function assetFindingTestID(index) {
112
- return `buoy-assets-detail-finding-${index}`;
113
- }
114
- //# sourceMappingURL=testIDs.js.map
1
+ "use strict";function assetSlug(s){return String(s).toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"unknown"}function assetRowTestID(s,e){return`buoy-assets-row-${assetSlug(`${s}.${e}`)}`}function assetBadgeTestID(s,e,t){return`${assetRowTestID(s,e)}-badge-${assetSlug(t)}`}function assetFilterTestID(s){return`buoy-assets-filter-${assetSlug(s)}`}function assetDetailRowTestID(s){return`buoy-assets-detail-row-${assetSlug(s)}`}function assetFontChipTestID(s){return`buoy-assets-font-${assetSlug(s)}`}Object.defineProperty(exports,"__esModule",{value:!0}),exports.ASSETS_TEST_IDS=void 0,exports.assetBadgeTestID=assetBadgeTestID,exports.assetDetailRowTestID=assetDetailRowTestID,exports.assetFilterTestID=assetFilterTestID,exports.assetFindingTestID=assetFindingTestID,exports.assetFontChipTestID=assetFontChipTestID,exports.assetRowTestID=assetRowTestID,exports.assetSlug=assetSlug;const ASSETS_TEST_IDS=exports.ASSETS_TEST_IDS={title:"buoy-assets-title",copy:"buoy-assets-copy",clear:"buoy-assets-clear",list:"buoy-assets-list",search:"buoy-assets-search",sort:"buoy-assets-sort",filterBar:"buoy-assets-filter-bar",notice:"buoy-assets-notice",noticeText:"buoy-assets-notice-text",discoverySpinner:"buoy-assets-discovery-spinner",retry:"buoy-assets-retry",fonts:"buoy-assets-fonts",fontsTitle:"buoy-assets-fonts-title",empty:"buoy-assets-empty",emptyTitle:"buoy-assets-empty-title",emptyText:"buoy-assets-empty-text",detail:"buoy-assets-detail",detailScroll:"buoy-assets-detail-scroll",detailPreview:"buoy-assets-detail-preview",detailFontPreview:"buoy-assets-detail-font-preview",detailFindings:"buoy-assets-detail-findings"};function assetFindingTestID(s){return`buoy-assets-detail-finding-${s}`}
@@ -1,41 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * Expo enrichment — all optional, all read through globalThis.expo guards so
5
- * bare RN apps (no expo-modules) silently get none of it and no imports are
6
- * added for anyone.
7
- */
8
-
9
- function expoModules() {
10
- return globalThis.expo?.modules;
11
- }
12
-
13
- /**
14
- * Every loaded font family — build-time embedded (config plugin / UIAppFonts /
15
- * android assets/fonts) AND runtime Font.loadAsync, straight from the native
16
- * module Expo documents as a stable global. [] when unavailable (bare RN).
17
- */
18
- export function getLoadedFontFamilies() {
19
- try {
20
- const fonts = expoModules()?.ExpoFontLoader?.getLoadedFonts?.();
21
- return Array.isArray(fonts) ? [...fonts].sort() : [];
22
- } catch {
23
- return [];
24
- }
25
- }
26
-
27
- /**
28
- * expo-updates' per-file-hash → local URI map: the COMPLETE shipped asset set
29
- * in Expo Go / OTA environments, independent of what JS has evaluated.
30
- * null when not running in an updates-managed environment.
31
- */
32
- export function getLocalAssets() {
33
- try {
34
- const map = expoModules()?.ExpoUpdates?.localAssets;
35
- if (map && typeof map === "object" && Object.keys(map).length > 0) return map;
36
- return null;
37
- } catch {
38
- return null;
39
- }
40
- }
41
- //# sourceMappingURL=expo.js.map
1
+ "use strict";function expoModules(){return globalThis.expo?.modules}export function getLoadedFontFamilies(){try{const e=expoModules()?.ExpoFontLoader?.getLoadedFonts?.();return Array.isArray(e)?[...new Set(e)].sort():[]}catch{return[]}}export function getLocalAssets(){try{const e=expoModules()?.ExpoUpdates?.localAssets;return e&&"object"==typeof e&&Object.keys(e).length>0?e:null}catch{return null}}
@@ -1,72 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * Capture install + scan orchestration.
5
- *
6
- * installAssetCapture() is cheap and synchronous (registry walk + patch) and
7
- * runs at package import. The expensive passes — Metro graph fetch and byte
8
- * measurement — run in rescan(), triggered when the modal opens or via the
9
- * `rescan` sync action, so merely installing Buoy never hits the network.
10
- */
11
-
12
- import { assetsState } from "../state";
13
- import { invalidate } from "../store/assetStore";
14
- import { getLoadedFontFamilies, getLocalAssets } from "./expo";
15
- import { measureSizes } from "./measure";
16
- import { fetchAssetGraph, getDevServerOrigin } from "./metroGraph";
17
- import { getRegistryModule, patchRegisterAsset, walkRegistry } from "./registry";
18
- export function installAssetCapture() {
19
- const s = assetsState();
20
- if (s.captureDisabled || s.installed) return;
21
- s.installed = true;
22
- s.registryAvailable = getRegistryModule() != null;
23
- s.patched = patchRegisterAsset();
24
- walkRegistry();
25
- refreshEnrichment();
26
- s.lastScanAt = Date.now();
27
- invalidate();
28
- }
29
- function refreshEnrichment() {
30
- const s = assetsState();
31
- s.fontFamilies = getLoadedFontFamilies();
32
- const localAssets = getLocalAssets();
33
- s.localAssetCount = localAssets ? Object.keys(localAssets).length : null;
34
- }
35
-
36
- /**
37
- * Full scan: registry re-walk (catches anything the patch missed), Metro
38
- * graph merge (dev), Expo enrichment refresh, then size measurement.
39
- */
40
- export async function rescan() {
41
- const s = assetsState();
42
- if (s.captureDisabled) return getScanStatus();
43
- installAssetCapture();
44
- walkRegistry();
45
- refreshEnrichment();
46
- await fetchAssetGraph();
47
- s.lastScanAt = Date.now();
48
- invalidate();
49
- void measureSizes();
50
- return getScanStatus();
51
- }
52
- export function getScanStatus() {
53
- const s = assetsState();
54
- // Mirrors report the synced DEVICE's status, not the dashboard's.
55
- if (s.mirrorScanStatus) return s.mirrorScanStatus;
56
- let registryCount = 0;
57
- for (const record of s.records) if (record.registryId != null) registryCount++;
58
- return {
59
- registryAvailable: s.registryAvailable,
60
- patched: s.patched,
61
- registryCount,
62
- graphSupported: s.graphSupported || getDevServerOrigin() != null,
63
- graphCount: s.graphCount,
64
- graphError: s.graphError,
65
- lastScanAt: s.lastScanAt,
66
- measuring: s.measuring,
67
- measuredCount: s.measuredCount,
68
- fontFamilies: s.fontFamilies,
69
- localAssetCount: s.localAssetCount
70
- };
71
- }
72
- //# sourceMappingURL=install.js.map
1
+ "use strict";import{assetsState}from"../state";import{invalidate,removeBuoyRecords}from"../store/assetStore";import{getLoadedFontFamilies,getLocalAssets}from"./expo";import{measureSizes}from"./measure";import{fetchAssetGraph,getDevServerOrigin}from"./metroGraph";import{getRegistryModule,patchRegisterAsset,walkRegistry}from"./registry";export function installAssetCapture(){const t=assetsState();t.captureDisabled||t.installed||(t.installed=!0,t.registryAvailable=null!=getRegistryModule(),t.patched=patchRegisterAsset(),walkRegistry(),refreshEnrichment(),t.lastScanAt=Date.now(),invalidate())}function refreshEnrichment(){const t=assetsState();t.fontFamilies=getLoadedFontFamilies();const e=getLocalAssets();t.localAssetCount=e?Object.keys(e).length:null}export async function rescan(){const t=assetsState();if(t.captureDisabled)return getScanStatus();if(t.scanPromise)return t.scanPromise;t.scanPromise=runScan();try{return await t.scanPromise}finally{t.scanPromise=void 0}}async function runScan(){const t=assetsState();return removeBuoyRecords(),installAssetCapture(),walkRegistry(),refreshEnrichment(),await fetchAssetGraph(),t.lastScanAt=Date.now(),invalidate(),measureSizes(),getScanStatus()}export function getScanStatus(){const t=assetsState();if(t.mirrorScanStatus)return t.mirrorScanStatus;let e=0;for(const r of t.records)null!=r.registryId&&e++;return{registryAvailable:t.registryAvailable,patched:t.patched,registryCount:e,graphSupported:t.graphSupported||null!=getDevServerOrigin(),graphCount:t.graphCount,graphError:t.graphError,graphFetchInFlight:t.graphFetchInFlight,graphFetchStartedAt:t.graphFetchStartedAt??null,lastScanAt:t.lastScanAt,measuring:t.measuring,measuredCount:t.measuredCount,fontFamilies:t.fontFamilies,localAssetCount:t.localAssetCount}}
@@ -0,0 +1 @@
1
+ "use strict";export function isBuoyAssetPath(e){if(!e)return!1;let t=e;try{t=decodeURIComponent(t)}catch{}return t=t.replace(/\\/g,"/"),/(?:^|\/)node_modules\/@buoy-gg\/[^/]+(?:\/|$)/.test(t)||/(?:^|\/)buoy-packages\/packages\/[^/]+(?:\/|$)/.test(t)||/(?:^|\/)packages\/floating-tools-core\/(?:src|lib\/(?:module|commonjs))\/icons\/assets(?:\/|$)/.test(t)}export function isBuoyAsset(e){return[e.key,e.httpServerLocation,e.fileSystemLocation,...e.files??[]].some(isBuoyAssetPath)}
@@ -1,179 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * Byte-size measurement.
5
- *
6
- * Dev only in practice: each scale variant is fetched from the Metro dev
7
- * server (HEAD first for Content-Length, GET-blob fallback since Metro
8
- * doesn't reliably answer HEAD) and the per-variant sizes are summed onto
9
- * the record. Android release resource ids and iOS file:// URIs are not
10
- * measurable from JS — those records keep `estDecodedBytes` as their only
11
- * size signal and the UI marks them as estimates.
12
- */
13
-
14
- import { assetsState } from "../state";
15
- import { getSnapshot, invalidate, updateRecord } from "../store/assetStore";
16
- import { getDevServerOrigin } from "./metroGraph";
17
- const CONCURRENCY = 4;
18
-
19
- /**
20
- * Best displayable URL for a record: the resolved runtime URI, or (dev) the
21
- * same dev-server URL the measurement pass uses — this is what gives graph-
22
- * only (never-loaded) images real thumbnails.
23
- */
24
- export function previewURL(record) {
25
- if (record.resolvedUri) return record.resolvedUri;
26
- const variants = variantURLs(record);
27
- return variants.length ? variants[0].url : null;
28
- }
29
-
30
- /**
31
- * Image source for thumbnails/previews. On device the NUMERIC registry id is
32
- * preferred (resolves natively — works for Android release resource ids too).
33
- * On dashboard mirrors that id belongs to the DEVICE's registry and rn-web
34
- * throws "asset with ID N could not be found" — use the device's http(s)
35
- * URL instead (the Metro dev server is reachable from the desktop).
36
- */
37
- export function displaySource(record) {
38
- const mirror = assetsState().captureDisabled;
39
- if (!mirror && record.registryId != null) return record.registryId;
40
- const uri = previewURL(record);
41
- if (!uri) return null;
42
- if (mirror && !/^https?:\/\//.test(uri)) return null;
43
- return {
44
- uri
45
- };
46
- }
47
-
48
- /** All scale-variant URLs for a record, derived without re-encoding pitfalls. */
49
- export function variantURLs(record) {
50
- const scales = record.scales.length ? record.scales : [1];
51
-
52
- // Preferred: string surgery on the URL resolveAssetSource already proved
53
- // works (handles Expo's ?unstable_path= form and monorepo rewrites).
54
- const resolved = record.resolvedUri;
55
- if (resolved && /^https?:\/\//.test(resolved) && record.resolvedScale != null) {
56
- const pickedSuffix = record.resolvedScale === 1 ? "" : `@${record.resolvedScale}x`;
57
- const marker = `${pickedSuffix}.${record.type}`;
58
- if (resolved.includes(marker) || pickedSuffix === "") {
59
- return scales.map(scale => {
60
- const suffix = scale === 1 ? "" : `@${scale}x`;
61
- const url = (pickedSuffix === "" ? resolved.replace(`.${record.type}`, `${suffix}.${record.type}`) : resolved.replace(marker, `${suffix}.${record.type}`)).replace(/ /g, "%20"); // RN's asset path builder doesn't encode spaces
62
- return {
63
- scale,
64
- url
65
- };
66
- });
67
- }
68
- }
69
-
70
- // Graph-only records: rebuild the dev-server URL the way Metro's
71
- // AssetSourceResolver does (plain concatenation), encoding only spaces —
72
- // full encodeURI would double-encode Expo's pre-escaped unstable_path.
73
- const origin = getDevServerOrigin();
74
- if (!origin || !record.httpServerLocation) return [];
75
- return scales.map(scale => {
76
- const suffix = scale === 1 ? "" : `@${scale}x`;
77
- const path = `${record.httpServerLocation}/${record.name}${suffix}.${record.type}`;
78
- const sep = path.includes("?") ? "&" : "?";
79
- const url = `${origin}${path}${sep}platform=${platformName()}${record.hash ? `&hash=${record.hash}` : ""}`.replace(/ /g, "%20");
80
- return {
81
- scale,
82
- url
83
- };
84
- });
85
- }
86
- function platformName() {
87
- try {
88
- return require("react-native").Platform.OS;
89
- } catch {
90
- return "ios";
91
- }
92
- }
93
- async function fetchSize(url) {
94
- try {
95
- const head = await fetch(url, {
96
- method: "HEAD"
97
- });
98
- if (head.ok) {
99
- const length = Number(head.headers.get("content-length"));
100
- if (Number.isFinite(length) && length > 0) return length;
101
- }
102
- } catch {
103
- // fall through to GET
104
- }
105
- try {
106
- const response = await fetch(url);
107
- if (!response.ok) return null;
108
- const blob = await response.blob();
109
- return blob.size > 0 ? blob.size : null;
110
- } catch {
111
- return null;
112
- }
113
- }
114
- async function measureRecord(record) {
115
- const variants = variantURLs(record);
116
- if (!variants.length) {
117
- updateRecord(record.id, {
118
- sizeSource: "estimate"
119
- });
120
- return;
121
- }
122
- const sizesByScale = {};
123
- let total = 0;
124
- for (const {
125
- scale,
126
- url
127
- } of variants) {
128
- const size = await fetchSize(url);
129
- if (size != null) {
130
- sizesByScale[String(scale)] = size;
131
- total += size;
132
- }
133
- }
134
- if (total > 0) {
135
- updateRecord(record.id, {
136
- sizesByScale,
137
- sizeBytes: total,
138
- sizeSource: "measured"
139
- });
140
- } else {
141
- updateRecord(record.id, {
142
- sizeSource: "estimate"
143
- });
144
- }
145
- }
146
-
147
- /** Measure every unmeasured record; safe to re-run (skips measured ones). */
148
- export async function measureSizes() {
149
- const s = assetsState();
150
- if (s.measuring) return {
151
- measured: s.measuredCount,
152
- total: getSnapshot().length
153
- };
154
- const pending = getSnapshot().filter(r => r.sizeSource !== "measured");
155
- s.measuring = true;
156
- invalidate();
157
- try {
158
- let index = 0;
159
- const worker = async () => {
160
- for (;;) {
161
- const next = pending[index++];
162
- if (!next) return;
163
- await measureRecord(next);
164
- s.measuredCount = getSnapshot().filter(r => r.sizeSource === "measured").length;
165
- }
166
- };
167
- await Promise.all(Array.from({
168
- length: Math.min(CONCURRENCY, pending.length)
169
- }, worker));
170
- } finally {
171
- s.measuring = false;
172
- invalidate();
173
- }
174
- return {
175
- measured: s.measuredCount,
176
- total: getSnapshot().length
177
- };
178
- }
179
- //# sourceMappingURL=measure.js.map
1
+ "use strict";import{assetsState}from"../state";import{getSnapshot,invalidate,updateRecord}from"../store/assetStore";import{getDevServerOrigin}from"./metroGraph";const CONCURRENCY=4;export function previewURL(e){if(e.resolvedUri)return e.resolvedUri;const t=variantURLs(e);return t.length?t[0].url:null}export function displaySource(e){const t=assetsState().captureDisabled;if(!t&&null!=e.registryId)return e.registryId;const r=previewURL(e);return r?t&&!/^https?:\/\//.test(r)?null:{uri:r}:null}export function variantURLs(e){const t=e.scales.length?e.scales:[1],r=e.resolvedUri;if(r&&/^https?:\/\//.test(r)&&null!=e.resolvedScale){const n=1===e.resolvedScale?"":`@${e.resolvedScale}x`,s=`${n}.${e.type}`;if(r.includes(s)||""===n)return t.map(t=>{const a=1===t?"":`@${t}x`;return{scale:t,url:(""===n?r.replace(`.${e.type}`,`${a}.${e.type}`):r.replace(s,`${a}.${e.type}`)).replace(/ /g,"%20")}})}const n=getDevServerOrigin();return n&&e.httpServerLocation?t.map(t=>{const r=1===t?"":`@${t}x`,s=`${e.httpServerLocation}/${e.name}${r}.${e.type}`,a=s.includes("?")?"&":"?";return{scale:t,url:`${n}${s}${a}platform=${platformName()}${e.hash?`&hash=${e.hash}`:""}`.replace(/ /g,"%20")}}):[]}function platformName(){try{return require("react-native").Platform.OS}catch{return"ios"}}async function fetchSize(e){try{const t=await fetch(e,{method:"HEAD"});if(t.ok){const e=Number(t.headers.get("content-length"));if(Number.isFinite(e)&&e>0)return e}}catch{}try{const t=await fetch(e);if(!t.ok)return null;const r=await t.blob();return r.size>0?r.size:null}catch{return null}}async function measureRecord(e){const t=variantURLs(e);if(!t.length)return void updateRecord(e.id,{sizeSource:"estimate"});const r={};let n=0;for(const{scale:e,url:s}of t){const t=await fetchSize(s);null!=t&&(r[String(e)]=t,n+=t)}updateRecord(e.id,n>0?{sizesByScale:r,sizeBytes:n,sizeSource:"measured"}:{sizeSource:"estimate"})}export async function measureSizes(){const e=assetsState();if(e.measuring)return{measured:e.measuredCount,total:getSnapshot().length};const t=getSnapshot().filter(e=>"measured"!==e.sizeSource);e.measuring=!0,invalidate();try{let r=0;const n=async()=>{for(;;){const n=t[r++];if(!n)return;await measureRecord(n),e.measuredCount=getSnapshot().filter(e=>"measured"===e.sizeSource).length}};await Promise.all(Array.from({length:Math.min(4,t.length)},n))}finally{e.measuring=!1,invalidate()}return{measured:e.measuredCount,total:getSnapshot().length}}