cursedbelt-core 1.0.0

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 (307) hide show
  1. package/LICENSE +15 -0
  2. package/dist/client/wsWire.d.ts +49 -0
  3. package/dist/client/wsWire.js +57 -0
  4. package/dist/config/appConfig.d.ts +361 -0
  5. package/dist/config/appConfig.js +308 -0
  6. package/dist/core/activity/index.d.ts +8 -0
  7. package/dist/core/activity/index.js +7 -0
  8. package/dist/core/activity/model.d.ts +115 -0
  9. package/dist/core/activity/model.js +63 -0
  10. package/dist/core/analytics/index.d.ts +18 -0
  11. package/dist/core/analytics/index.js +18 -0
  12. package/dist/core/analytics/range.d.ts +186 -0
  13. package/dist/core/analytics/range.js +271 -0
  14. package/dist/core/analytics/schedule.d.ts +301 -0
  15. package/dist/core/analytics/schedule.js +372 -0
  16. package/dist/core/category/index.d.ts +3 -0
  17. package/dist/core/category/index.js +6 -0
  18. package/dist/core/category/memoryStore.d.ts +21 -0
  19. package/dist/core/category/memoryStore.js +97 -0
  20. package/dist/core/category/tree.d.ts +26 -0
  21. package/dist/core/category/tree.js +113 -0
  22. package/dist/core/category/types.d.ts +48 -0
  23. package/dist/core/category/types.js +8 -0
  24. package/dist/core/color.d.ts +14 -0
  25. package/dist/core/color.js +32 -0
  26. package/dist/core/colorScheme/colorSchemeStore.d.ts +70 -0
  27. package/dist/core/colorScheme/colorSchemeStore.js +123 -0
  28. package/dist/core/colorScheme/index.d.ts +7 -0
  29. package/dist/core/colorScheme/index.js +7 -0
  30. package/dist/core/domainOwners.d.ts +19 -0
  31. package/dist/core/domainOwners.js +15 -0
  32. package/dist/core/download.d.ts +14 -0
  33. package/dist/core/download.js +37 -0
  34. package/dist/core/events/ccEventBus.d.ts +44 -0
  35. package/dist/core/events/ccEventBus.js +70 -0
  36. package/dist/core/file-tree/fileTreeModel.d.ts +276 -0
  37. package/dist/core/file-tree/fileTreeModel.js +312 -0
  38. package/dist/core/folder-tree/actions.d.ts +84 -0
  39. package/dist/core/folder-tree/actions.js +119 -0
  40. package/dist/core/folder-tree/index.d.ts +3 -0
  41. package/dist/core/folder-tree/index.js +20 -0
  42. package/dist/core/folder-tree/path.d.ts +76 -0
  43. package/dist/core/folder-tree/path.js +146 -0
  44. package/dist/core/folder-tree/tree.d.ts +86 -0
  45. package/dist/core/folder-tree/tree.js +184 -0
  46. package/dist/core/layout/index.d.ts +1 -0
  47. package/dist/core/layout/index.js +7 -0
  48. package/dist/core/master-lock/index.d.ts +14 -0
  49. package/dist/core/master-lock/index.js +14 -0
  50. package/dist/core/master-lock/kdf.d.ts +63 -0
  51. package/dist/core/master-lock/kdf.js +117 -0
  52. package/dist/core/master-lock/policy.d.ts +32 -0
  53. package/dist/core/master-lock/policy.js +52 -0
  54. package/dist/core/master-lock/presence.d.ts +52 -0
  55. package/dist/core/master-lock/presence.js +61 -0
  56. package/dist/core/master-lock/wire.d.ts +139 -0
  57. package/dist/core/master-lock/wire.js +81 -0
  58. package/dist/core/media/bgRemoval.d.ts +27 -0
  59. package/dist/core/media/bgRemoval.js +101 -0
  60. package/dist/core/media/ffmpeg.d.ts +57 -0
  61. package/dist/core/media/ffmpeg.js +126 -0
  62. package/dist/core/media/format.d.ts +20 -0
  63. package/dist/core/media/format.js +54 -0
  64. package/dist/core/media/index.d.ts +12 -0
  65. package/dist/core/media/index.js +18 -0
  66. package/dist/core/media/mediaItem.d.ts +15 -0
  67. package/dist/core/media/mediaItem.js +34 -0
  68. package/dist/core/media/renderPlan.d.ts +63 -0
  69. package/dist/core/media/renderPlan.js +232 -0
  70. package/dist/core/media/segment.d.ts +79 -0
  71. package/dist/core/media/segment.js +247 -0
  72. package/dist/core/media/size.d.ts +1 -0
  73. package/dist/core/media/size.js +3 -0
  74. package/dist/core/media/trim.d.ts +21 -0
  75. package/dist/core/media/trim.js +40 -0
  76. package/dist/core/media/types.d.ts +148 -0
  77. package/dist/core/media/types.js +11 -0
  78. package/dist/core/media/uploadLimits.d.ts +7 -0
  79. package/dist/core/media/uploadLimits.js +16 -0
  80. package/dist/core/media/validate.d.ts +9 -0
  81. package/dist/core/media/validate.js +121 -0
  82. package/dist/core/media/videoCuts.d.ts +152 -0
  83. package/dist/core/media/videoCuts.js +284 -0
  84. package/dist/core/navigation/breadcrumbCompat.d.ts +24 -0
  85. package/dist/core/navigation/breadcrumbCompat.js +21 -0
  86. package/dist/core/navigation/coverageTest.d.ts +24 -0
  87. package/dist/core/navigation/coverageTest.js +48 -0
  88. package/dist/core/navigation/index.d.ts +6 -0
  89. package/dist/core/navigation/index.js +10 -0
  90. package/dist/core/navigation/migrateNavPreferences.d.ts +55 -0
  91. package/dist/core/navigation/migrateNavPreferences.js +63 -0
  92. package/dist/core/navigation/navigationModel.d.ts +165 -0
  93. package/dist/core/navigation/navigationModel.js +14 -0
  94. package/dist/core/navigation/navigationSelectors.d.ts +63 -0
  95. package/dist/core/navigation/navigationSelectors.js +167 -0
  96. package/dist/core/navigation/searchCatalogue.d.ts +23 -0
  97. package/dist/core/navigation/searchCatalogue.js +76 -0
  98. package/dist/core/net/reconnectSchedule.d.ts +46 -0
  99. package/dist/core/net/reconnectSchedule.js +38 -0
  100. package/dist/core/palette/ShortcutRegistry.d.ts +33 -0
  101. package/dist/core/palette/ShortcutRegistry.js +52 -0
  102. package/dist/core/palette/fuzzyRank.d.ts +18 -0
  103. package/dist/core/palette/fuzzyRank.js +73 -0
  104. package/dist/core/palette/hotkeys.d.ts +28 -0
  105. package/dist/core/palette/hotkeys.js +118 -0
  106. package/dist/core/palette/index.d.ts +3 -0
  107. package/dist/core/palette/index.js +8 -0
  108. package/dist/core/schema/projection.d.ts +84 -0
  109. package/dist/core/schema/projection.js +184 -0
  110. package/dist/core/session/claims.d.ts +145 -0
  111. package/dist/core/session/claims.js +157 -0
  112. package/dist/core/sharing/index.d.ts +5 -0
  113. package/dist/core/sharing/index.js +4 -0
  114. package/dist/core/sharing/model.d.ts +195 -0
  115. package/dist/core/sharing/model.js +150 -0
  116. package/dist/core/site/siteCopy.d.ts +92 -0
  117. package/dist/core/site/siteCopy.js +91 -0
  118. package/dist/core/slots.d.ts +22 -0
  119. package/dist/core/slots.js +31 -0
  120. package/dist/core/styles/constructableSheet.d.ts +51 -0
  121. package/dist/core/styles/constructableSheet.js +80 -0
  122. package/dist/core/tokens/registry.d.ts +119 -0
  123. package/dist/core/tokens/registry.js +447 -0
  124. package/dist/core/uiScale/index.d.ts +8 -0
  125. package/dist/core/uiScale/index.js +8 -0
  126. package/dist/core/uiScale/scale.d.ts +36 -0
  127. package/dist/core/uiScale/scale.js +47 -0
  128. package/dist/core/uiScale/uiScaleStore.d.ts +47 -0
  129. package/dist/core/uiScale/uiScaleStore.js +63 -0
  130. package/dist/core/wire/__fixtures__/rowsMarshalWorker.d.ts +1 -0
  131. package/dist/core/wire/__fixtures__/rowsMarshalWorker.js +23 -0
  132. package/dist/core/wire/index.d.ts +17 -0
  133. package/dist/core/wire/index.js +17 -0
  134. package/dist/core/wire/ingress.d.ts +44 -0
  135. package/dist/core/wire/ingress.js +134 -0
  136. package/dist/core/wire/marshalWorker.d.ts +70 -0
  137. package/dist/core/wire/marshalWorker.js +273 -0
  138. package/dist/core/wire/marshalling.d.ts +122 -0
  139. package/dist/core/wire/marshalling.js +126 -0
  140. package/dist/core/wire/wireHub.d.ts +80 -0
  141. package/dist/core/wire/wireHub.js +116 -0
  142. package/dist/ctgr/base64url.d.ts +8 -0
  143. package/dist/ctgr/base64url.js +39 -0
  144. package/dist/ctgr/chunkAccumulator.d.ts +214 -0
  145. package/dist/ctgr/chunkAccumulator.js +307 -0
  146. package/dist/ctgr/chunkStore.d.ts +24 -0
  147. package/dist/ctgr/chunkStore.js +125 -0
  148. package/dist/ctgr/codec.d.ts +44 -0
  149. package/dist/ctgr/codec.js +153 -0
  150. package/dist/ctgr/compress.d.ts +2 -0
  151. package/dist/ctgr/compress.js +20 -0
  152. package/dist/ctgr/crc32.d.ts +2 -0
  153. package/dist/ctgr/crc32.js +23 -0
  154. package/dist/ctgr/digest.d.ts +1 -0
  155. package/dist/ctgr/digest.js +15 -0
  156. package/dist/ctgr/index.d.ts +9 -0
  157. package/dist/ctgr/index.js +17 -0
  158. package/dist/ctgr/types.d.ts +64 -0
  159. package/dist/ctgr/types.js +21 -0
  160. package/dist/ctgr/v0compat.d.ts +27 -0
  161. package/dist/ctgr/v0compat.js +128 -0
  162. package/dist/index.d.ts +15 -0
  163. package/dist/index.js +17 -0
  164. package/dist/shared/errors.d.ts +101 -0
  165. package/dist/shared/errors.js +99 -0
  166. package/dist/testing/bytes.d.ts +32 -0
  167. package/dist/testing/bytes.js +33 -0
  168. package/dist/testing/domInspect.d.ts +59 -0
  169. package/dist/testing/domInspect.js +115 -0
  170. package/dist/testing/fakeFetch.d.ts +23 -0
  171. package/dist/testing/fakeFetch.js +21 -0
  172. package/dist/testing/index.d.ts +3 -0
  173. package/dist/testing/index.js +11 -0
  174. package/package.json +373 -0
  175. package/src/client/wsWire.ts +83 -0
  176. package/src/config/appConfig.spec.ts +158 -0
  177. package/src/config/appConfig.ts +372 -0
  178. package/src/core/activity/index.ts +21 -0
  179. package/src/core/activity/model.ts +160 -0
  180. package/src/core/analytics/index.ts +18 -0
  181. package/src/core/analytics/range.spec.ts +280 -0
  182. package/src/core/analytics/range.ts +383 -0
  183. package/src/core/analytics/schedule.spec.ts +311 -0
  184. package/src/core/analytics/schedule.ts +624 -0
  185. package/src/core/category/category.spec.ts +144 -0
  186. package/src/core/category/index.ts +19 -0
  187. package/src/core/category/memoryStore.ts +104 -0
  188. package/src/core/category/tree.ts +118 -0
  189. package/src/core/category/types.ts +59 -0
  190. package/src/core/color.spec.ts +35 -0
  191. package/src/core/color.ts +35 -0
  192. package/src/core/colorScheme/colorScheme.spec.ts +127 -0
  193. package/src/core/colorScheme/colorSchemeStore.ts +184 -0
  194. package/src/core/colorScheme/index.ts +23 -0
  195. package/src/core/domainOwners.ts +32 -0
  196. package/src/core/download.spec.ts +38 -0
  197. package/src/core/download.ts +35 -0
  198. package/src/core/events/ccEventBus.spec.ts +90 -0
  199. package/src/core/events/ccEventBus.ts +120 -0
  200. package/src/core/file-tree/fileTreeModel.spec.ts +462 -0
  201. package/src/core/file-tree/fileTreeModel.ts +511 -0
  202. package/src/core/folder-tree/actions.spec.ts +103 -0
  203. package/src/core/folder-tree/actions.ts +153 -0
  204. package/src/core/folder-tree/index.ts +46 -0
  205. package/src/core/folder-tree/path.spec.ts +113 -0
  206. package/src/core/folder-tree/path.ts +148 -0
  207. package/src/core/folder-tree/tree.spec.ts +157 -0
  208. package/src/core/folder-tree/tree.ts +244 -0
  209. package/src/core/layout/index.ts +7 -0
  210. package/src/core/master-lock/index.ts +50 -0
  211. package/src/core/master-lock/kdf.spec.ts +95 -0
  212. package/src/core/master-lock/kdf.ts +145 -0
  213. package/src/core/master-lock/policy.ts +55 -0
  214. package/src/core/master-lock/presence.spec.ts +73 -0
  215. package/src/core/master-lock/presence.ts +71 -0
  216. package/src/core/master-lock/wire.ts +155 -0
  217. package/src/core/media/bgRemoval.spec.ts +120 -0
  218. package/src/core/media/bgRemoval.ts +123 -0
  219. package/src/core/media/ffmpeg.spec.ts +135 -0
  220. package/src/core/media/ffmpeg.ts +162 -0
  221. package/src/core/media/format.spec.ts +41 -0
  222. package/src/core/media/format.ts +58 -0
  223. package/src/core/media/index.ts +105 -0
  224. package/src/core/media/mediaItem.spec.ts +46 -0
  225. package/src/core/media/mediaItem.ts +40 -0
  226. package/src/core/media/renderPlan.spec.ts +140 -0
  227. package/src/core/media/renderPlan.ts +318 -0
  228. package/src/core/media/segment.spec.ts +159 -0
  229. package/src/core/media/segment.ts +352 -0
  230. package/src/core/media/size.spec.ts +19 -0
  231. package/src/core/media/size.ts +3 -0
  232. package/src/core/media/trim.spec.ts +75 -0
  233. package/src/core/media/trim.ts +60 -0
  234. package/src/core/media/types.ts +147 -0
  235. package/src/core/media/uploadLimits.ts +18 -0
  236. package/src/core/media/validate.spec.ts +106 -0
  237. package/src/core/media/validate.ts +131 -0
  238. package/src/core/media/videoCuts.spec.ts +327 -0
  239. package/src/core/media/videoCuts.ts +355 -0
  240. package/src/core/navigation/breadcrumbCompat.ts +44 -0
  241. package/src/core/navigation/coverageTest.ts +54 -0
  242. package/src/core/navigation/index.ts +50 -0
  243. package/src/core/navigation/migrateNavPreferences.ts +119 -0
  244. package/src/core/navigation/navigation.spec.ts +388 -0
  245. package/src/core/navigation/navigationModel.ts +185 -0
  246. package/src/core/navigation/navigationSelectors.ts +206 -0
  247. package/src/core/navigation/searchCatalogue.ts +89 -0
  248. package/src/core/net/reconnectSchedule.spec.ts +49 -0
  249. package/src/core/net/reconnectSchedule.ts +78 -0
  250. package/src/core/palette/ShortcutRegistry.ts +69 -0
  251. package/src/core/palette/fuzzyRank.ts +78 -0
  252. package/src/core/palette/hotkeys.ts +132 -0
  253. package/src/core/palette/index.ts +22 -0
  254. package/src/core/palette/palette.spec.ts +160 -0
  255. package/src/core/schema/projection.spec.ts +122 -0
  256. package/src/core/schema/projection.ts +225 -0
  257. package/src/core/session/claims.ts +181 -0
  258. package/src/core/sharing/index.ts +33 -0
  259. package/src/core/sharing/model.spec.ts +104 -0
  260. package/src/core/sharing/model.ts +296 -0
  261. package/src/core/site/siteCopy.ts +115 -0
  262. package/src/core/slots.ts +38 -0
  263. package/src/core/styles/constructableSheet.ts +86 -0
  264. package/src/core/tokens/registry.spec.ts +160 -0
  265. package/src/core/tokens/registry.ts +536 -0
  266. package/src/core/uiScale/index.ts +32 -0
  267. package/src/core/uiScale/scale.ts +63 -0
  268. package/src/core/uiScale/uiScale.spec.ts +77 -0
  269. package/src/core/uiScale/uiScaleStore.ts +100 -0
  270. package/src/core/wire/__fixtures__/rowsMarshalWorker.ts +24 -0
  271. package/src/core/wire/index.ts +56 -0
  272. package/src/core/wire/ingress.spec.ts +276 -0
  273. package/src/core/wire/ingress.ts +182 -0
  274. package/src/core/wire/marshalWorker.spec.ts +196 -0
  275. package/src/core/wire/marshalWorker.ts +361 -0
  276. package/src/core/wire/marshalling.spec.ts +139 -0
  277. package/src/core/wire/marshalling.ts +238 -0
  278. package/src/core/wire/wireHub.spec.ts +137 -0
  279. package/src/core/wire/wireHub.ts +213 -0
  280. package/src/ctgr/base64url.ts +44 -0
  281. package/src/ctgr/chunkAccumulator.ts +403 -0
  282. package/src/ctgr/chunkStore.spec.ts +101 -0
  283. package/src/ctgr/chunkStore.ts +168 -0
  284. package/src/ctgr/codec.spec.ts +208 -0
  285. package/src/ctgr/codec.ts +202 -0
  286. package/src/ctgr/compress.ts +21 -0
  287. package/src/ctgr/crc32.ts +23 -0
  288. package/src/ctgr/digest.ts +14 -0
  289. package/src/ctgr/index.ts +39 -0
  290. package/src/ctgr/types.ts +80 -0
  291. package/src/ctgr/v0compat.spec.ts +73 -0
  292. package/src/ctgr/v0compat.ts +156 -0
  293. package/src/declaredDepsAreImported.spec.ts +87 -0
  294. package/src/index.ts +25 -0
  295. package/src/leafSubpathsImportNothing.spec.ts +287 -0
  296. package/src/lockfileIsTracked.spec.ts +50 -0
  297. package/src/namedSubpathsResolve.spec.ts +122 -0
  298. package/src/noPathDeps.spec.ts +24 -0
  299. package/src/publishShape.spec.ts +89 -0
  300. package/src/shared/errors.ts +153 -0
  301. package/src/skippedTestsAreAnswered.spec.ts +221 -0
  302. package/src/testTmpRoot.spec.ts +102 -0
  303. package/src/testing/bytes.ts +38 -0
  304. package/src/testing/domInspect.spec.ts +79 -0
  305. package/src/testing/domInspect.ts +127 -0
  306. package/src/testing/fakeFetch.ts +26 -0
  307. package/src/testing/index.ts +11 -0
@@ -0,0 +1,102 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import { isAbsolute, resolve, sep } from 'node:path';
3
+ import { resolveTestTmpRoot } from '../scripts/testTmpRoot';
4
+
5
+ /**
6
+ * 🔴 THE CHECKER CANNOT SEE THIS ONE, WHICH IS WHY IT IS A TEST.
7
+ *
8
+ * `scripts/testTmpRoot.ts` is a bun test PRELOAD: it runs before the first spec in
9
+ * this repo, on every `bun test`, and it decides where every `mkdtemp` in the suite
10
+ * lands. Until 2026-09-13 it pointed `$TMPDIR` at the PREVIOUS generation's state
11
+ * root — 77 MB and 259 process directories had accumulated there — and
12
+ * `tools/check-paths.ts` reported nothing, because the path was interpolated from
13
+ * `$HOME` on one line and assembled from a variable on the next. Neither line
14
+ * carried a shape the trigger looks for.
15
+ *
16
+ * So the gate is here instead, and it asserts the RESOLVED root rather than the
17
+ * source text: a previous generation's directory cannot come back through a
18
+ * variable, a rename, or a constant without one of these failing.
19
+ *
20
+ * This spec lives in `src/` rather than beside the file it proves because
21
+ * `bun run test` is `bun test src` — a spec under `scripts/` would never run, and
22
+ * a gate that does not run is the exact failure mode above.
23
+ */
24
+
25
+ /** Every directory name this generation must never write into. */
26
+ const PREVIOUS_GENERATIONS = ['app-managed', 'code-retiring', 'code-active', 'Master-Docs'];
27
+
28
+ const REPO_ROOT = resolve(import.meta.dir, '..');
29
+
30
+ describe('the test scratch root', () => {
31
+ it('lives under $FORGE_STATE when the generation supplies one', () => {
32
+ const root = resolveTestTmpRoot({ FORGE_STATE: '/state/cursedforge' });
33
+ expect(root.startsWith(`/state/cursedforge${sep}`)).toBe(true);
34
+ });
35
+
36
+ it('falls back to this repo, never to anything derived from $HOME', () => {
37
+ const root = resolveTestTmpRoot({ HOME: '/home/somebody' });
38
+ expect(root.startsWith(`${REPO_ROOT}${sep}`)).toBe(true);
39
+ expect(root).not.toContain('/home/somebody');
40
+ // Gitignored here, so a fallback run leaves nothing for git to report.
41
+ expect(root).toContain('.agent.noindex');
42
+ });
43
+
44
+ it('treats a blank $FORGE_STATE as no $FORGE_STATE', () => {
45
+ expect(resolveTestTmpRoot({ FORGE_STATE: ' ', HOME: '/home/somebody' }).startsWith(`${REPO_ROOT}${sep}`)).toBe(
46
+ true,
47
+ );
48
+ });
49
+
50
+ /*
51
+ * 🔴 `.noindex` is not decoration and is not cosmetic: it is the only marker that
52
+ * keeps Spotlight out of files written LATER into a tree, and a test root is
53
+ * written to constantly (measured 43× on this machine). Both branches carry it.
54
+ */
55
+ it('keeps a .noindex segment on every branch', () => {
56
+ for (const env of [{ FORGE_STATE: '/state/cursedforge' }, { HOME: '/home/somebody' }]) {
57
+ expect(resolveTestTmpRoot(env).split(sep).some((segment) => segment.endsWith('.noindex'))).toBe(true);
58
+ }
59
+ });
60
+
61
+ it('resolves an absolute path, whatever the caller cwd is', () => {
62
+ for (const env of [{ FORGE_STATE: '/state/cursedforge' }, {}]) {
63
+ expect(isAbsolute(resolveTestTmpRoot(env))).toBe(true);
64
+ }
65
+ });
66
+
67
+ /*
68
+ * The assertion the whole file exists for. Note the envs that TRY to steer it
69
+ * back: the previous generation's own variable, and a home that already contains
70
+ * its folder. Neither may reach the answer.
71
+ */
72
+ it('never resolves a previous generation, whatever the environment says', () => {
73
+ const envs: Array<Record<string, string | undefined>> = [
74
+ {},
75
+ { HOME: '/home/somebody' },
76
+ { FORGE_STATE: '/state/cursedforge', HOME: '/home/somebody' },
77
+ { APP_MANAGED_HOME: '/nowhere/inherited', HOME: '/home/somebody' },
78
+ { HOME: `/home/somebody/${PREVIOUS_GENERATIONS[0]}` },
79
+ ];
80
+ for (const env of envs) {
81
+ const root = resolveTestTmpRoot(env);
82
+ for (const name of PREVIOUS_GENERATIONS) {
83
+ expect(root, `resolved "${root}" for env ${JSON.stringify(env)}`).not.toContain(name);
84
+ }
85
+ }
86
+ });
87
+
88
+ /*
89
+ * And the live one: whatever this very process ended up with. The preload has
90
+ * already run by the time any spec does, so `$TMPDIR` here is the real answer on
91
+ * the real machine — the measurement the source-text assertions above cannot make.
92
+ */
93
+ it('has already pointed THIS process somewhere legitimate', () => {
94
+ const live = process.env.TMPDIR ?? '';
95
+ if (process.env.TMPDIR_TEST_PASSTHROUGH) return; // a suite asked for the system root
96
+ for (const name of PREVIOUS_GENERATIONS) {
97
+ expect(live, `$TMPDIR is "${live}"`).not.toContain(name);
98
+ }
99
+ const expected = resolveTestTmpRoot();
100
+ expect(live.startsWith(`${expected}${sep}`)).toBe(true);
101
+ });
102
+ });
@@ -0,0 +1,38 @@
1
+ /**
2
+ * `Uint8Array` → a body the DOM types accept, in ONE place.
3
+ *
4
+ * ## The problem this exists to stop being solved five different ways
5
+ *
6
+ * TypeScript 5.7 made `Uint8Array` generic in its buffer: `Uint8Array<ArrayBufferLike>`.
7
+ * `BodyInit` accepts `ArrayBufferView<ArrayBuffer>` — an ArrayBuffer view, NOT a
8
+ * possibly-shared one — so `new Response(bytes)` and `fetch(url, { body: bytes })`
9
+ * stopped compiling for every plain `new Uint8Array([…])` in the repo. Nothing
10
+ * about the runtime changed; the types got more precise about `SharedArrayBuffer`.
11
+ *
12
+ * Two fixes suggest themselves and one of them is a bug:
13
+ *
14
+ * * `bytes.buffer as ArrayBuffer` — **wrong in general.** `.buffer` is the
15
+ * WHOLE underlying buffer, so for any view that does not span it (`subarray`,
16
+ * a slice of a decoder's output, a chunk read off a stream) this silently
17
+ * sends more bytes than the caller holds. It happens to be right for a
18
+ * literal `new Uint8Array([1,2,3,4])` and that is exactly what makes it a
19
+ * trap: it works everywhere it is first tried.
20
+ * * `bytes as Uint8Array<ArrayBuffer>` — correct, and what this does. It
21
+ * asserts the one thing the caller actually knows (this buffer is not
22
+ * shared), changes no lengths, and keeps the view's own offset.
23
+ *
24
+ * Test-only by intent: production code that needs to send bytes should hold a
25
+ * correctly-typed buffer rather than assert one.
26
+ */
27
+
28
+ /** A `Uint8Array` as a `BodyInit`, preserving byte offset and length. */
29
+ export const asBody = (bytes: Uint8Array): Uint8Array<ArrayBuffer> => bytes as Uint8Array<ArrayBuffer>;
30
+
31
+ /** `new Response(bytes)` with the assertion applied — the common case. */
32
+ export const bytesResponse = (bytes: Uint8Array, init?: ResponseInit): Response =>
33
+ new Response(asBody(bytes), init);
34
+
35
+ /** A readable stream of `bytes` — what the media catalogue's ingest paths take. */
36
+ export const bytesStream = (bytes: Uint8Array | string): ReadableStream<Uint8Array> =>
37
+ new Response(typeof bytes === 'string' ? asBody(new TextEncoder().encode(bytes)) : asBody(bytes))
38
+ .body as ReadableStream<Uint8Array>;
@@ -0,0 +1,79 @@
1
+ /**
2
+ * The standing gate on the cheap-DOM-inspect preload.
3
+ *
4
+ * This is a PERFORMANCE assertion, which is normally a bad idea — a wall-clock
5
+ * threshold on a shared CI runner is how you get a flaky suite. It earns its
6
+ * place because the failure it guards is three orders of magnitude wide, not a
7
+ * few percent: without the inspector a single failing `expect` on an attached
8
+ * 50-child element measured 1,576 ms, with it 0.1 ms. The threshold below sits
9
+ * at 150 ms — 10× the headroom a loaded runner could plausibly need, and still
10
+ * 10× under the broken behavior. Nothing between those two numbers is a state
11
+ * this code can actually be in.
12
+ *
13
+ * If this goes red, the preload stopped running or happy-dom moved the class:
14
+ * check `installCheapDomInspect()` is still called AFTER `GlobalRegistrator
15
+ * .register()` in both `happydom.ts` preloads.
16
+ */
17
+ import { describe, expect, test } from 'bun:test';
18
+
19
+ /** Comfortably above a slow runner, comfortably below the 1,576 ms failure. */
20
+ const BUDGET_MS = 150;
21
+
22
+ const attachedTree = (children: number): Element => {
23
+ const el = document.createElement('div');
24
+ el.className = 'subject';
25
+ el.innerHTML = '<span>x</span>'.repeat(children);
26
+ document.body.appendChild(el);
27
+ return el;
28
+ };
29
+
30
+ describe('the cheap DOM inspector is installed by the test preload', () => {
31
+ test('a failing expect on a deep ATTACHED element is fast', () => {
32
+ const el = attachedTree(50);
33
+ const started = performance.now();
34
+ // Deliberately fails: this is the exact shape `waitFor(() => expect(el)
35
+ // .toBeNull())` runs on every poll.
36
+ expect(() => expect(el).toBeNull()).toThrow();
37
+ const elapsed = performance.now() - started;
38
+ expect(elapsed).toBeLessThan(BUDGET_MS);
39
+ });
40
+
41
+ test('the message shows the MARKUP, not the node object graph', () => {
42
+ const el = attachedTree(1);
43
+ let message = '';
44
+ try {
45
+ expect(el).toBeNull();
46
+ } catch (error) {
47
+ message = (error as Error).message;
48
+ }
49
+ expect(message).toContain('<div class="subject">');
50
+ // The old serializer walked upward through parentNode into the document —
51
+ // that walk is both the cost and the reason the message was unreadable.
52
+ expect(message).not.toContain('Symbol(parentNode)');
53
+ expect(message).not.toContain('HTMLHtmlElement');
54
+ });
55
+
56
+ test('very large markup is elided rather than dumped whole', () => {
57
+ const el = attachedTree(400);
58
+ let message = '';
59
+ try {
60
+ expect(el).toBeNull();
61
+ } catch (error) {
62
+ message = (error as Error).message;
63
+ }
64
+ expect(message).toContain('more chars');
65
+ expect(message.length).toBeLessThan(2000);
66
+ });
67
+
68
+ test('a text node still describes itself', () => {
69
+ const text = document.createTextNode('hello');
70
+ let message = '';
71
+ try {
72
+ expect(text).toBeNull();
73
+ } catch (error) {
74
+ message = (error as Error).message;
75
+ }
76
+ expect(message).toContain('#text');
77
+ expect(message).toContain('hello');
78
+ });
79
+ });
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Make a happy-dom node CHEAP to serialize, for every `bun test` process in this
3
+ * repo. Imported by `packages/cursedbelt/happydom.ts` and
4
+ * `packages/cursedshell/happydom.ts` — the two test preloads — so it is the ONE
5
+ * place this behavior is defined.
6
+ *
7
+ * ## The bug this exists to not have
8
+ *
9
+ * `expect(received)` builds its failure message EAGERLY, before anything decides
10
+ * whether that message will ever be read. When `received` is a happy-dom node,
11
+ * Bun's inspector walks the node's own object graph — and a happy-dom node holds
12
+ * `[Symbol(parentNode)]`, so the walk goes UPWARD through the parent chain into
13
+ * `HTMLBodyElement` → `HTMLHtmlElement` → `HTMLDocument` → the window, and back
14
+ * down through every sibling subtree it meets on the way.
15
+ *
16
+ * Measured on this repo's own studio spec (2026-07-26):
17
+ *
18
+ * | received value | cost of ONE failing `expect` |
19
+ * |---------------------------------------|------------------------------|
20
+ * | `true` | 0.1 ms |
21
+ * | a plain object | 0.0 ms |
22
+ * | a detached empty `<div>` | 9.8 ms |
23
+ * | an ATTACHED empty `<div>` | 29.8 ms |
24
+ * | an attached `<div>` with 50 children | **1,576 ms** |
25
+ *
26
+ * That cost is paid on assertions that FAIL, which sounds rare until you notice
27
+ * that `waitFor(() => expect(el).toBeNull())` fails on purpose on every poll
28
+ * until the condition comes true. The studio spec's `settled()` helper spent
29
+ * ~790 ms per mount building error strings that `waitFor` then threw away — 12
30
+ * mounts, ~9.5 s of a 10.5 s file, and on the CI runner the same two assertions
31
+ * blew through Bun's 5,000 ms per-test timeout and turned `main` red.
32
+ *
33
+ * ## The fix
34
+ *
35
+ * Install `nodejs.util.inspect.custom` on happy-dom's `Node.prototype` (the
36
+ * common ancestor of every element, text node, fragment and document), returning
37
+ * TRUNCATED `outerHTML`. Bun's inspector honors that hook and stops walking.
38
+ * Same measurement, after: **1,576 ms → 0.1 ms.**
39
+ *
40
+ * The message gets BETTER as well as faster. What it replaces is not an HTML
41
+ * dump; it is a `[Symbol(listeners)] / [Symbol(parentNode)]` object graph that
42
+ * scrolls for thousands of lines and never shows the markup. `outerHTML` is what
43
+ * someone reading a failure actually wants.
44
+ *
45
+ * ## What this deliberately does NOT do
46
+ *
47
+ * It does not touch `prettyDOM`, `screen.debug()` or any Testing Library output
48
+ * — those have their own serializer and are called only when someone asks for
49
+ * them, so they were never the cost. Only the implicit `expect`/`console.log`
50
+ * path changes.
51
+ */
52
+
53
+ /** Longest markup we will put in a failure message before eliding the middle. */
54
+ const MAX_MARKUP = 800;
55
+
56
+ /** Node.nodeType constants, spelled out so this file needs no DOM lib types. */
57
+ const ELEMENT_NODE = 1;
58
+ const TEXT_NODE = 3;
59
+ const COMMENT_NODE = 8;
60
+ const DOCUMENT_NODE = 9;
61
+ const DOCUMENT_FRAGMENT_NODE = 11;
62
+
63
+ const INSPECT = Symbol.for('nodejs.util.inspect.custom');
64
+
65
+ interface MinimalNode {
66
+ nodeType: number;
67
+ nodeName?: string;
68
+ outerHTML?: string;
69
+ innerHTML?: string;
70
+ data?: string;
71
+ childNodes?: { length: number };
72
+ }
73
+
74
+ /** Keep the head and the tail: the opening tag says what it is, the closing tag
75
+ * says where it ended, and the middle of a 40 KB body is never the useful part. */
76
+ function elide(markup: string): string {
77
+ if (markup.length <= MAX_MARKUP) return markup;
78
+ const head = markup.slice(0, MAX_MARKUP - 120);
79
+ const tail = markup.slice(-100);
80
+ return `${head}\n … ${markup.length - MAX_MARKUP + 20} more chars …\n${tail}`;
81
+ }
82
+
83
+ function describe(node: MinimalNode): string {
84
+ switch (node.nodeType) {
85
+ case ELEMENT_NODE:
86
+ return elide(node.outerHTML ?? `<${(node.nodeName ?? 'element').toLowerCase()} />`);
87
+ case TEXT_NODE:
88
+ return `#text ${JSON.stringify(node.data ?? '')}`;
89
+ case COMMENT_NODE:
90
+ return `<!--${node.data ?? ''}-->`;
91
+ case DOCUMENT_NODE:
92
+ return '#document';
93
+ case DOCUMENT_FRAGMENT_NODE:
94
+ return elide(`#document-fragment ${node.innerHTML ?? `(${node.childNodes?.length ?? 0} children)`}`);
95
+ default:
96
+ return `${node.nodeName ?? 'Node'}(nodeType ${node.nodeType})`;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Install the cheap inspector. Idempotent, and a no-op when happy-dom has not
102
+ * registered its globals (a pure-logic preload has no `Node`).
103
+ *
104
+ * Call AFTER `GlobalRegistrator.register()` — before it, `globalThis.Node` is
105
+ * whatever Bun ships and patching it would miss the class the specs actually get.
106
+ */
107
+ export function installCheapDomInspect(): void {
108
+ const NodeCtor = (globalThis as { Node?: { prototype?: object } }).Node;
109
+ const proto = NodeCtor?.prototype;
110
+ if (!proto) return;
111
+ if (Object.hasOwn(proto, INSPECT)) return; // already installed
112
+
113
+ Object.defineProperty(proto, INSPECT, {
114
+ value(this: MinimalNode) {
115
+ // An inspector that throws turns a readable assertion failure into an
116
+ // unreadable one, so every branch here is best-effort.
117
+ try {
118
+ return describe(this);
119
+ } catch {
120
+ return '[unserializable DOM node]';
121
+ }
122
+ },
123
+ configurable: true,
124
+ enumerable: false,
125
+ writable: true,
126
+ });
127
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * A `fetch` stand-in that actually satisfies `typeof fetch`.
3
+ *
4
+ * `typeof fetch` is not just a call signature: the DOM lib declares `preconnect`
5
+ * on it, so a bare `async (input) => new Response(…)` is NOT assignable and
6
+ * `as typeof fetch` is refused outright ("neither type sufficiently overlaps").
7
+ * The reflexive workaround is `as unknown as typeof fetch`, and that is the
8
+ * pattern worth stopping: a double assertion silences THIS mismatch and every
9
+ * future one, including a genuine argument-shape error in the fake.
10
+ *
11
+ * This attaches the missing member instead. The result is a real value of the
12
+ * type, so the fake's own `(input, init)` signature stays checked — which is the
13
+ * whole reason to type a fake at all.
14
+ */
15
+
16
+ /** The part of `fetch` a test actually implements. */
17
+ export type FetchImpl = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
18
+
19
+ /**
20
+ * Wrap a fake so it is a complete `typeof fetch`.
21
+ *
22
+ * `preconnect` is a no-op: it is a resource HINT with no observable result, so a
23
+ * test that triggered one and got nothing has lost nothing.
24
+ */
25
+ export const fakeFetch = (impl: FetchImpl): typeof fetch =>
26
+ Object.assign(impl, { preconnect: () => undefined }) as typeof fetch;
@@ -0,0 +1,11 @@
1
+ // cursedbelt-core/testing — test-only helpers shared across every `bun test` process
2
+ // in the fleet. Its own subpath so nothing test-related is reachable from the
3
+ // runtime entrypoints.
4
+ //
5
+ // Exposed on 2026-08-02 so consumers (e.g. cursed-satellites' happydom preload)
6
+ // can `import { installCheapDomInspect } from "cursedbelt-core/testing"` instead of
7
+ // keeping a ~60-line hand-port of domInspect.ts — the file already lived here,
8
+ // it just had no export.
9
+ export { installCheapDomInspect } from "./domInspect";
10
+ export { asBody, bytesResponse, bytesStream } from "./bytes";
11
+ export { type FetchImpl, fakeFetch } from "./fakeFetch";