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.
- package/LICENSE +15 -0
- package/dist/client/wsWire.d.ts +49 -0
- package/dist/client/wsWire.js +57 -0
- package/dist/config/appConfig.d.ts +361 -0
- package/dist/config/appConfig.js +308 -0
- package/dist/core/activity/index.d.ts +8 -0
- package/dist/core/activity/index.js +7 -0
- package/dist/core/activity/model.d.ts +115 -0
- package/dist/core/activity/model.js +63 -0
- package/dist/core/analytics/index.d.ts +18 -0
- package/dist/core/analytics/index.js +18 -0
- package/dist/core/analytics/range.d.ts +186 -0
- package/dist/core/analytics/range.js +271 -0
- package/dist/core/analytics/schedule.d.ts +301 -0
- package/dist/core/analytics/schedule.js +372 -0
- package/dist/core/category/index.d.ts +3 -0
- package/dist/core/category/index.js +6 -0
- package/dist/core/category/memoryStore.d.ts +21 -0
- package/dist/core/category/memoryStore.js +97 -0
- package/dist/core/category/tree.d.ts +26 -0
- package/dist/core/category/tree.js +113 -0
- package/dist/core/category/types.d.ts +48 -0
- package/dist/core/category/types.js +8 -0
- package/dist/core/color.d.ts +14 -0
- package/dist/core/color.js +32 -0
- package/dist/core/colorScheme/colorSchemeStore.d.ts +70 -0
- package/dist/core/colorScheme/colorSchemeStore.js +123 -0
- package/dist/core/colorScheme/index.d.ts +7 -0
- package/dist/core/colorScheme/index.js +7 -0
- package/dist/core/domainOwners.d.ts +19 -0
- package/dist/core/domainOwners.js +15 -0
- package/dist/core/download.d.ts +14 -0
- package/dist/core/download.js +37 -0
- package/dist/core/events/ccEventBus.d.ts +44 -0
- package/dist/core/events/ccEventBus.js +70 -0
- package/dist/core/file-tree/fileTreeModel.d.ts +276 -0
- package/dist/core/file-tree/fileTreeModel.js +312 -0
- package/dist/core/folder-tree/actions.d.ts +84 -0
- package/dist/core/folder-tree/actions.js +119 -0
- package/dist/core/folder-tree/index.d.ts +3 -0
- package/dist/core/folder-tree/index.js +20 -0
- package/dist/core/folder-tree/path.d.ts +76 -0
- package/dist/core/folder-tree/path.js +146 -0
- package/dist/core/folder-tree/tree.d.ts +86 -0
- package/dist/core/folder-tree/tree.js +184 -0
- package/dist/core/layout/index.d.ts +1 -0
- package/dist/core/layout/index.js +7 -0
- package/dist/core/master-lock/index.d.ts +14 -0
- package/dist/core/master-lock/index.js +14 -0
- package/dist/core/master-lock/kdf.d.ts +63 -0
- package/dist/core/master-lock/kdf.js +117 -0
- package/dist/core/master-lock/policy.d.ts +32 -0
- package/dist/core/master-lock/policy.js +52 -0
- package/dist/core/master-lock/presence.d.ts +52 -0
- package/dist/core/master-lock/presence.js +61 -0
- package/dist/core/master-lock/wire.d.ts +139 -0
- package/dist/core/master-lock/wire.js +81 -0
- package/dist/core/media/bgRemoval.d.ts +27 -0
- package/dist/core/media/bgRemoval.js +101 -0
- package/dist/core/media/ffmpeg.d.ts +57 -0
- package/dist/core/media/ffmpeg.js +126 -0
- package/dist/core/media/format.d.ts +20 -0
- package/dist/core/media/format.js +54 -0
- package/dist/core/media/index.d.ts +12 -0
- package/dist/core/media/index.js +18 -0
- package/dist/core/media/mediaItem.d.ts +15 -0
- package/dist/core/media/mediaItem.js +34 -0
- package/dist/core/media/renderPlan.d.ts +63 -0
- package/dist/core/media/renderPlan.js +232 -0
- package/dist/core/media/segment.d.ts +79 -0
- package/dist/core/media/segment.js +247 -0
- package/dist/core/media/size.d.ts +1 -0
- package/dist/core/media/size.js +3 -0
- package/dist/core/media/trim.d.ts +21 -0
- package/dist/core/media/trim.js +40 -0
- package/dist/core/media/types.d.ts +148 -0
- package/dist/core/media/types.js +11 -0
- package/dist/core/media/uploadLimits.d.ts +7 -0
- package/dist/core/media/uploadLimits.js +16 -0
- package/dist/core/media/validate.d.ts +9 -0
- package/dist/core/media/validate.js +121 -0
- package/dist/core/media/videoCuts.d.ts +152 -0
- package/dist/core/media/videoCuts.js +284 -0
- package/dist/core/navigation/breadcrumbCompat.d.ts +24 -0
- package/dist/core/navigation/breadcrumbCompat.js +21 -0
- package/dist/core/navigation/coverageTest.d.ts +24 -0
- package/dist/core/navigation/coverageTest.js +48 -0
- package/dist/core/navigation/index.d.ts +6 -0
- package/dist/core/navigation/index.js +10 -0
- package/dist/core/navigation/migrateNavPreferences.d.ts +55 -0
- package/dist/core/navigation/migrateNavPreferences.js +63 -0
- package/dist/core/navigation/navigationModel.d.ts +165 -0
- package/dist/core/navigation/navigationModel.js +14 -0
- package/dist/core/navigation/navigationSelectors.d.ts +63 -0
- package/dist/core/navigation/navigationSelectors.js +167 -0
- package/dist/core/navigation/searchCatalogue.d.ts +23 -0
- package/dist/core/navigation/searchCatalogue.js +76 -0
- package/dist/core/net/reconnectSchedule.d.ts +46 -0
- package/dist/core/net/reconnectSchedule.js +38 -0
- package/dist/core/palette/ShortcutRegistry.d.ts +33 -0
- package/dist/core/palette/ShortcutRegistry.js +52 -0
- package/dist/core/palette/fuzzyRank.d.ts +18 -0
- package/dist/core/palette/fuzzyRank.js +73 -0
- package/dist/core/palette/hotkeys.d.ts +28 -0
- package/dist/core/palette/hotkeys.js +118 -0
- package/dist/core/palette/index.d.ts +3 -0
- package/dist/core/palette/index.js +8 -0
- package/dist/core/schema/projection.d.ts +84 -0
- package/dist/core/schema/projection.js +184 -0
- package/dist/core/session/claims.d.ts +145 -0
- package/dist/core/session/claims.js +157 -0
- package/dist/core/sharing/index.d.ts +5 -0
- package/dist/core/sharing/index.js +4 -0
- package/dist/core/sharing/model.d.ts +195 -0
- package/dist/core/sharing/model.js +150 -0
- package/dist/core/site/siteCopy.d.ts +92 -0
- package/dist/core/site/siteCopy.js +91 -0
- package/dist/core/slots.d.ts +22 -0
- package/dist/core/slots.js +31 -0
- package/dist/core/styles/constructableSheet.d.ts +51 -0
- package/dist/core/styles/constructableSheet.js +80 -0
- package/dist/core/tokens/registry.d.ts +119 -0
- package/dist/core/tokens/registry.js +447 -0
- package/dist/core/uiScale/index.d.ts +8 -0
- package/dist/core/uiScale/index.js +8 -0
- package/dist/core/uiScale/scale.d.ts +36 -0
- package/dist/core/uiScale/scale.js +47 -0
- package/dist/core/uiScale/uiScaleStore.d.ts +47 -0
- package/dist/core/uiScale/uiScaleStore.js +63 -0
- package/dist/core/wire/__fixtures__/rowsMarshalWorker.d.ts +1 -0
- package/dist/core/wire/__fixtures__/rowsMarshalWorker.js +23 -0
- package/dist/core/wire/index.d.ts +17 -0
- package/dist/core/wire/index.js +17 -0
- package/dist/core/wire/ingress.d.ts +44 -0
- package/dist/core/wire/ingress.js +134 -0
- package/dist/core/wire/marshalWorker.d.ts +70 -0
- package/dist/core/wire/marshalWorker.js +273 -0
- package/dist/core/wire/marshalling.d.ts +122 -0
- package/dist/core/wire/marshalling.js +126 -0
- package/dist/core/wire/wireHub.d.ts +80 -0
- package/dist/core/wire/wireHub.js +116 -0
- package/dist/ctgr/base64url.d.ts +8 -0
- package/dist/ctgr/base64url.js +39 -0
- package/dist/ctgr/chunkAccumulator.d.ts +214 -0
- package/dist/ctgr/chunkAccumulator.js +307 -0
- package/dist/ctgr/chunkStore.d.ts +24 -0
- package/dist/ctgr/chunkStore.js +125 -0
- package/dist/ctgr/codec.d.ts +44 -0
- package/dist/ctgr/codec.js +153 -0
- package/dist/ctgr/compress.d.ts +2 -0
- package/dist/ctgr/compress.js +20 -0
- package/dist/ctgr/crc32.d.ts +2 -0
- package/dist/ctgr/crc32.js +23 -0
- package/dist/ctgr/digest.d.ts +1 -0
- package/dist/ctgr/digest.js +15 -0
- package/dist/ctgr/index.d.ts +9 -0
- package/dist/ctgr/index.js +17 -0
- package/dist/ctgr/types.d.ts +64 -0
- package/dist/ctgr/types.js +21 -0
- package/dist/ctgr/v0compat.d.ts +27 -0
- package/dist/ctgr/v0compat.js +128 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +17 -0
- package/dist/shared/errors.d.ts +101 -0
- package/dist/shared/errors.js +99 -0
- package/dist/testing/bytes.d.ts +32 -0
- package/dist/testing/bytes.js +33 -0
- package/dist/testing/domInspect.d.ts +59 -0
- package/dist/testing/domInspect.js +115 -0
- package/dist/testing/fakeFetch.d.ts +23 -0
- package/dist/testing/fakeFetch.js +21 -0
- package/dist/testing/index.d.ts +3 -0
- package/dist/testing/index.js +11 -0
- package/package.json +373 -0
- package/src/client/wsWire.ts +83 -0
- package/src/config/appConfig.spec.ts +158 -0
- package/src/config/appConfig.ts +372 -0
- package/src/core/activity/index.ts +21 -0
- package/src/core/activity/model.ts +160 -0
- package/src/core/analytics/index.ts +18 -0
- package/src/core/analytics/range.spec.ts +280 -0
- package/src/core/analytics/range.ts +383 -0
- package/src/core/analytics/schedule.spec.ts +311 -0
- package/src/core/analytics/schedule.ts +624 -0
- package/src/core/category/category.spec.ts +144 -0
- package/src/core/category/index.ts +19 -0
- package/src/core/category/memoryStore.ts +104 -0
- package/src/core/category/tree.ts +118 -0
- package/src/core/category/types.ts +59 -0
- package/src/core/color.spec.ts +35 -0
- package/src/core/color.ts +35 -0
- package/src/core/colorScheme/colorScheme.spec.ts +127 -0
- package/src/core/colorScheme/colorSchemeStore.ts +184 -0
- package/src/core/colorScheme/index.ts +23 -0
- package/src/core/domainOwners.ts +32 -0
- package/src/core/download.spec.ts +38 -0
- package/src/core/download.ts +35 -0
- package/src/core/events/ccEventBus.spec.ts +90 -0
- package/src/core/events/ccEventBus.ts +120 -0
- package/src/core/file-tree/fileTreeModel.spec.ts +462 -0
- package/src/core/file-tree/fileTreeModel.ts +511 -0
- package/src/core/folder-tree/actions.spec.ts +103 -0
- package/src/core/folder-tree/actions.ts +153 -0
- package/src/core/folder-tree/index.ts +46 -0
- package/src/core/folder-tree/path.spec.ts +113 -0
- package/src/core/folder-tree/path.ts +148 -0
- package/src/core/folder-tree/tree.spec.ts +157 -0
- package/src/core/folder-tree/tree.ts +244 -0
- package/src/core/layout/index.ts +7 -0
- package/src/core/master-lock/index.ts +50 -0
- package/src/core/master-lock/kdf.spec.ts +95 -0
- package/src/core/master-lock/kdf.ts +145 -0
- package/src/core/master-lock/policy.ts +55 -0
- package/src/core/master-lock/presence.spec.ts +73 -0
- package/src/core/master-lock/presence.ts +71 -0
- package/src/core/master-lock/wire.ts +155 -0
- package/src/core/media/bgRemoval.spec.ts +120 -0
- package/src/core/media/bgRemoval.ts +123 -0
- package/src/core/media/ffmpeg.spec.ts +135 -0
- package/src/core/media/ffmpeg.ts +162 -0
- package/src/core/media/format.spec.ts +41 -0
- package/src/core/media/format.ts +58 -0
- package/src/core/media/index.ts +105 -0
- package/src/core/media/mediaItem.spec.ts +46 -0
- package/src/core/media/mediaItem.ts +40 -0
- package/src/core/media/renderPlan.spec.ts +140 -0
- package/src/core/media/renderPlan.ts +318 -0
- package/src/core/media/segment.spec.ts +159 -0
- package/src/core/media/segment.ts +352 -0
- package/src/core/media/size.spec.ts +19 -0
- package/src/core/media/size.ts +3 -0
- package/src/core/media/trim.spec.ts +75 -0
- package/src/core/media/trim.ts +60 -0
- package/src/core/media/types.ts +147 -0
- package/src/core/media/uploadLimits.ts +18 -0
- package/src/core/media/validate.spec.ts +106 -0
- package/src/core/media/validate.ts +131 -0
- package/src/core/media/videoCuts.spec.ts +327 -0
- package/src/core/media/videoCuts.ts +355 -0
- package/src/core/navigation/breadcrumbCompat.ts +44 -0
- package/src/core/navigation/coverageTest.ts +54 -0
- package/src/core/navigation/index.ts +50 -0
- package/src/core/navigation/migrateNavPreferences.ts +119 -0
- package/src/core/navigation/navigation.spec.ts +388 -0
- package/src/core/navigation/navigationModel.ts +185 -0
- package/src/core/navigation/navigationSelectors.ts +206 -0
- package/src/core/navigation/searchCatalogue.ts +89 -0
- package/src/core/net/reconnectSchedule.spec.ts +49 -0
- package/src/core/net/reconnectSchedule.ts +78 -0
- package/src/core/palette/ShortcutRegistry.ts +69 -0
- package/src/core/palette/fuzzyRank.ts +78 -0
- package/src/core/palette/hotkeys.ts +132 -0
- package/src/core/palette/index.ts +22 -0
- package/src/core/palette/palette.spec.ts +160 -0
- package/src/core/schema/projection.spec.ts +122 -0
- package/src/core/schema/projection.ts +225 -0
- package/src/core/session/claims.ts +181 -0
- package/src/core/sharing/index.ts +33 -0
- package/src/core/sharing/model.spec.ts +104 -0
- package/src/core/sharing/model.ts +296 -0
- package/src/core/site/siteCopy.ts +115 -0
- package/src/core/slots.ts +38 -0
- package/src/core/styles/constructableSheet.ts +86 -0
- package/src/core/tokens/registry.spec.ts +160 -0
- package/src/core/tokens/registry.ts +536 -0
- package/src/core/uiScale/index.ts +32 -0
- package/src/core/uiScale/scale.ts +63 -0
- package/src/core/uiScale/uiScale.spec.ts +77 -0
- package/src/core/uiScale/uiScaleStore.ts +100 -0
- package/src/core/wire/__fixtures__/rowsMarshalWorker.ts +24 -0
- package/src/core/wire/index.ts +56 -0
- package/src/core/wire/ingress.spec.ts +276 -0
- package/src/core/wire/ingress.ts +182 -0
- package/src/core/wire/marshalWorker.spec.ts +196 -0
- package/src/core/wire/marshalWorker.ts +361 -0
- package/src/core/wire/marshalling.spec.ts +139 -0
- package/src/core/wire/marshalling.ts +238 -0
- package/src/core/wire/wireHub.spec.ts +137 -0
- package/src/core/wire/wireHub.ts +213 -0
- package/src/ctgr/base64url.ts +44 -0
- package/src/ctgr/chunkAccumulator.ts +403 -0
- package/src/ctgr/chunkStore.spec.ts +101 -0
- package/src/ctgr/chunkStore.ts +168 -0
- package/src/ctgr/codec.spec.ts +208 -0
- package/src/ctgr/codec.ts +202 -0
- package/src/ctgr/compress.ts +21 -0
- package/src/ctgr/crc32.ts +23 -0
- package/src/ctgr/digest.ts +14 -0
- package/src/ctgr/index.ts +39 -0
- package/src/ctgr/types.ts +80 -0
- package/src/ctgr/v0compat.spec.ts +73 -0
- package/src/ctgr/v0compat.ts +156 -0
- package/src/declaredDepsAreImported.spec.ts +87 -0
- package/src/index.ts +25 -0
- package/src/leafSubpathsImportNothing.spec.ts +287 -0
- package/src/lockfileIsTracked.spec.ts +50 -0
- package/src/namedSubpathsResolve.spec.ts +122 -0
- package/src/noPathDeps.spec.ts +24 -0
- package/src/publishShape.spec.ts +89 -0
- package/src/shared/errors.ts +153 -0
- package/src/skippedTestsAreAnswered.spec.ts +221 -0
- package/src/testTmpRoot.spec.ts +102 -0
- package/src/testing/bytes.ts +38 -0
- package/src/testing/domInspect.spec.ts +79 -0
- package/src/testing/domInspect.ts +127 -0
- package/src/testing/fakeFetch.ts +26 -0
- 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";
|