cursedbelt-server 4.19.0 โ 4.19.1
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/README.md +61 -0
- package/dist/subpathReach.d.ts +32 -0
- package/dist/subpathReach.js +58 -0
- package/package.json +5 -3
- package/src/barrelsReachNoOptionalPeer.spec.ts +1 -49
- package/src/readmeInstallTable.spec.ts +48 -0
- package/src/server/serveBunOverload.spec.ts +52 -0
- package/src/subpathReach.ts +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# cursedbelt-server
|
|
2
|
+
|
|
3
|
+
The server tier of the cursedbelt split: Hono on Bun, the D1 seam a Worker crosses, the
|
|
4
|
+
binary-server client, the guard, and the fleet standards (retention, activity, notifications,
|
|
5
|
+
engagement). `cursedbelt-core` is below it, the React design system `cursedbelt` above it.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
bun add cursedbelt-server
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## ๐ด The root export is a BARREL โ import the leaf
|
|
12
|
+
|
|
13
|
+
`import โฆ from "cursedbelt-server"` re-exports everything, so it reaches every optional peer and
|
|
14
|
+
`bun:sqlite` at once (the table below). An app that wants one function pays for all of them, and
|
|
15
|
+
on a Worker `wrangler deploy` fails to bundle it. Import the subpath that owns the symbol โ
|
|
16
|
+
`cursedbelt-server/login-throttle`, `cursedbelt-server/binary-store`, `cursedbelt-server/d1` โ
|
|
17
|
+
never the root. `package.json` `exports` is the list of subpaths.
|
|
18
|
+
|
|
19
|
+
Why this stays prose: which symbol an app WANTS is decided in the app, and a repo's gate proves
|
|
20
|
+
that repo โ so the barrel-importer grep belongs to the generation's tools, not to this package.
|
|
21
|
+
|
|
22
|
+
## What you install
|
|
23
|
+
|
|
24
|
+
Most subpaths need nothing beyond `hono` (and `zod` where they validate). These are the only
|
|
25
|
+
ones that statically reach an OPTIONAL peer, or a bun builtin a Worker does not have. The table
|
|
26
|
+
is checked: `src/readmeInstallTable.spec.ts` fails when it disagrees with `src/subpathReach.ts`,
|
|
27
|
+
and `src/barrelsReachNoOptionalPeer.spec.ts` fails when that disagrees with what a bundler
|
|
28
|
+
actually keeps.
|
|
29
|
+
|
|
30
|
+
| subpath | optional peers it imports | bun builtins it needs |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| `.` | `kysely`, `kysely-bun-sqlite`, `otplib`, `plainjob` | `bun:sqlite` |
|
|
33
|
+
| `./jobs` | `plainjob` | โ |
|
|
34
|
+
| `./d1/backup-local` | โ | `bun:sqlite` |
|
|
35
|
+
| `./guard` | โ | `bun:sqlite` |
|
|
36
|
+
| `./guard/revocations` | โ | `bun:sqlite` |
|
|
37
|
+
| `./sqlite` | โ | `bun:sqlite` |
|
|
38
|
+
| `./engagement` | โ | `bun:sqlite` |
|
|
39
|
+
|
|
40
|
+
`sharp` and `@node-rs/argon2` are loaded lazily (`await import()`), so a missing one breaks only
|
|
41
|
+
the feature that asks for it, not the build.
|
|
42
|
+
|
|
43
|
+
## ๐ด `Bun.serve`'s `websocket` selects an OVERLOAD
|
|
44
|
+
|
|
45
|
+
It is not an optional field. An options object whose `websocket` may be `undefined` โ a
|
|
46
|
+
conditional value or a conditional spread โ matches no overload, and TypeScript reports an error
|
|
47
|
+
about the whole options object rather than the one field. Use `serveBun(app, { websocket })`,
|
|
48
|
+
which takes it as a genuinely optional field and makes the two concrete calls itself.
|
|
49
|
+
`src/server/serveBunOverload.spec.ts` runs `tsc` on the trap and goes red when bun's types change.
|
|
50
|
+
|
|
51
|
+
## Standards
|
|
52
|
+
|
|
53
|
+
`docs/retention.md`, `docs/activity.md`, `docs/notifications.md`, `docs/engagement.md` โ the
|
|
54
|
+
contracts every app that uses those modules is agreeing to. Cite them from an app with the
|
|
55
|
+
package prefix (`cursedbelt-server/docs/retention.md`).
|
|
56
|
+
|
|
57
|
+
## Verifying
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
cd "$FORGE/libs/cursedbelt-server" && bun run verify
|
|
61
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What each public subpath of this package REACHES that a consumer may not have โ the optional
|
|
3
|
+
* peers it imports statically, and the bun builtins it needs.
|
|
4
|
+
*
|
|
5
|
+
* Two specs read these tables and neither may drift from the other:
|
|
6
|
+
* ยท `barrelsReachNoOptionalPeer.spec.ts` bundles every subpath and fails when one reaches
|
|
7
|
+
* something its row does not list, or a row lists something it no longer reaches;
|
|
8
|
+
* ยท `readmeInstallTable.spec.ts` fails when README.md's "What you install" table disagrees
|
|
9
|
+
* with these rows โ the table an importing app actually reads (task 104).
|
|
10
|
+
* Not exported from package.json: this is the check's data, not an API.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* The subpaths whose whole PURPOSE is the optional peer they pull, mapped to what they are
|
|
14
|
+
* allowed to pull and why. An entry here is a promise that the peer is the point of the
|
|
15
|
+
* subpath โ not a place to park a new drag.
|
|
16
|
+
*
|
|
17
|
+
* ๐ด Measured 2026-09-18: these three are the ONLY subpaths in the map that reach an
|
|
18
|
+
* optional peer. Everything else is clean, which is what makes this list an allowlist
|
|
19
|
+
* rather than a baseline โ it can only shrink.
|
|
20
|
+
*/
|
|
21
|
+
export declare const MAY_DRAG: Record<string, readonly string[]>;
|
|
22
|
+
/**
|
|
23
|
+
* The subpaths whose whole PURPOSE is a bun-specific runtime, mapped to the builtins they
|
|
24
|
+
* are allowed to reach. Everything NOT listed here must be importable on `workerd`, which
|
|
25
|
+
* is the property the D1 seam exists to deliver.
|
|
26
|
+
*
|
|
27
|
+
* ๐ด Measured 2026-09-18 after the `./d1/backup-local` split: these five are the ONLY
|
|
28
|
+
* subpaths in the map that reach a bun builtin at all. Like {@link MAY_DRAG} it is an
|
|
29
|
+
* allowlist, not a baseline โ it can only shrink, and an entry that stops being true is a
|
|
30
|
+
* failure rather than a tidy-up.
|
|
31
|
+
*/
|
|
32
|
+
export declare const MAY_NEED_BUN: Record<string, readonly string[]>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What each public subpath of this package REACHES that a consumer may not have โ the optional
|
|
3
|
+
* peers it imports statically, and the bun builtins it needs.
|
|
4
|
+
*
|
|
5
|
+
* Two specs read these tables and neither may drift from the other:
|
|
6
|
+
* ยท `barrelsReachNoOptionalPeer.spec.ts` bundles every subpath and fails when one reaches
|
|
7
|
+
* something its row does not list, or a row lists something it no longer reaches;
|
|
8
|
+
* ยท `readmeInstallTable.spec.ts` fails when README.md's "What you install" table disagrees
|
|
9
|
+
* with these rows โ the table an importing app actually reads (task 104).
|
|
10
|
+
* Not exported from package.json: this is the check's data, not an API.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* The subpaths whose whole PURPOSE is the optional peer they pull, mapped to what they are
|
|
14
|
+
* allowed to pull and why. An entry here is a promise that the peer is the point of the
|
|
15
|
+
* subpath โ not a place to park a new drag.
|
|
16
|
+
*
|
|
17
|
+
* ๐ด Measured 2026-09-18: these three are the ONLY subpaths in the map that reach an
|
|
18
|
+
* optional peer. Everything else is clean, which is what makes this list an allowlist
|
|
19
|
+
* rather than a baseline โ it can only shrink.
|
|
20
|
+
*/
|
|
21
|
+
export const MAY_DRAG = {
|
|
22
|
+
// The whole-package barrel. It exists to re-export everything, so it necessarily reaches
|
|
23
|
+
// what the specific subpaths reach. An app importing `cursedbelt-server` whole is asking
|
|
24
|
+
// for that; an app importing `cursedbelt-server/d1` is not, and that is the distinction
|
|
25
|
+
// this spec protects.
|
|
26
|
+
'.': ['kysely', 'kysely-bun-sqlite', 'otplib', 'plainjob'],
|
|
27
|
+
// `./d1/kysely` (`createD1Kysely`) was here until 4.19.0, when it was removed: no file in
|
|
28
|
+
// the generation had ever imported it (task 472's census), so the allowance went with it.
|
|
29
|
+
// The job queue is plainjob. Nothing else here is.
|
|
30
|
+
'./jobs': ['plainjob'],
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* The subpaths whose whole PURPOSE is a bun-specific runtime, mapped to the builtins they
|
|
34
|
+
* are allowed to reach. Everything NOT listed here must be importable on `workerd`, which
|
|
35
|
+
* is the property the D1 seam exists to deliver.
|
|
36
|
+
*
|
|
37
|
+
* ๐ด Measured 2026-09-18 after the `./d1/backup-local` split: these five are the ONLY
|
|
38
|
+
* subpaths in the map that reach a bun builtin at all. Like {@link MAY_DRAG} it is an
|
|
39
|
+
* allowlist, not a baseline โ it can only shrink, and an entry that stops being true is a
|
|
40
|
+
* failure rather than a tidy-up.
|
|
41
|
+
*/
|
|
42
|
+
export const MAY_NEED_BUN = {
|
|
43
|
+
// The whole-package barrel โ it re-exports everything, including the Mac-hosted halves.
|
|
44
|
+
// An app importing `cursedbelt-server` whole is a Bun server by construction; an app
|
|
45
|
+
// importing `cursedbelt-server/d1` is not, and that is the distinction this protects.
|
|
46
|
+
'.': ['bun:sqlite'],
|
|
47
|
+
// The local backup IS `VACUUM INTO` against a live `bun:sqlite` handle. Split out of
|
|
48
|
+
// `./d1` on 2026-09-18 precisely so the seam itself stops paying for it โ see the header.
|
|
49
|
+
'./d1/backup-local': ['bun:sqlite'],
|
|
50
|
+
// The guard's revocation store is a `bun:sqlite` table, and `./guard` mounts it.
|
|
51
|
+
'./guard': ['bun:sqlite'],
|
|
52
|
+
'./guard/revocations': ['bun:sqlite'],
|
|
53
|
+
// `./sqlite` is the bun:sqlite tier. Naming it is the point of the subpath.
|
|
54
|
+
'./sqlite': ['bun:sqlite'],
|
|
55
|
+
// The engagement store IS `engagement.sqlite` on the Mac (4.19.0, task 280) โ the recorder
|
|
56
|
+
// runs where the app's own database is. A Worker app records nothing until it has a D1 store.
|
|
57
|
+
'./engagement': ['bun:sqlite'],
|
|
58
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursedbelt-server",
|
|
3
|
-
"version": "4.19.
|
|
3
|
+
"version": "4.19.1",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The app-facing Bun/Hono server tier of the cursedbelt split โ storage, sharing, activity, guard, sync. React-free; cursedbelt-core below it.",
|
|
@@ -29,7 +29,9 @@
|
|
|
29
29
|
"files": [
|
|
30
30
|
"dist",
|
|
31
31
|
"src",
|
|
32
|
-
"docs"
|
|
32
|
+
"docs",
|
|
33
|
+
"README.md",
|
|
34
|
+
"!src/server/typetests"
|
|
33
35
|
],
|
|
34
36
|
"exports": {
|
|
35
37
|
".": {
|
|
@@ -287,7 +289,7 @@
|
|
|
287
289
|
},
|
|
288
290
|
"dependencies": {
|
|
289
291
|
"cursedbelt-core": "^2.1.1",
|
|
290
|
-
"cursedops": "^0.
|
|
292
|
+
"cursedops": "^0.5.2",
|
|
291
293
|
"cwip": "^4.6.0",
|
|
292
294
|
"jose": "^6.2.3"
|
|
293
295
|
},
|
|
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'bun:test';
|
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import pkg from '../package.json';
|
|
5
|
+
import { MAY_DRAG, MAY_NEED_BUN } from './subpathReach.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* A public subpath must not STATICALLY drag anything its consumers cannot have โ an
|
|
@@ -99,55 +100,6 @@ import pkg from '../package.json';
|
|
|
99
100
|
|
|
100
101
|
const REPO = fileURLToPath(new URL('..', import.meta.url));
|
|
101
102
|
|
|
102
|
-
/**
|
|
103
|
-
* The subpaths whose whole PURPOSE is the optional peer they pull, mapped to what they are
|
|
104
|
-
* allowed to pull and why. An entry here is a promise that the peer is the point of the
|
|
105
|
-
* subpath โ not a place to park a new drag.
|
|
106
|
-
*
|
|
107
|
-
* ๐ด Measured 2026-09-18: these three are the ONLY subpaths in the map that reach an
|
|
108
|
-
* optional peer. Everything else is clean, which is what makes this list an allowlist
|
|
109
|
-
* rather than a baseline โ it can only shrink.
|
|
110
|
-
*/
|
|
111
|
-
const MAY_DRAG: Record<string, readonly string[]> = {
|
|
112
|
-
// The whole-package barrel. It exists to re-export everything, so it necessarily reaches
|
|
113
|
-
// what the specific subpaths reach. An app importing `cursedbelt-server` whole is asking
|
|
114
|
-
// for that; an app importing `cursedbelt-server/d1` is not, and that is the distinction
|
|
115
|
-
// this spec protects.
|
|
116
|
-
'.': ['kysely', 'kysely-bun-sqlite', 'otplib', 'plainjob'],
|
|
117
|
-
// `./d1/kysely` (`createD1Kysely`) was here until 4.19.0, when it was removed: no file in
|
|
118
|
-
// the generation had ever imported it (task 472's census), so the allowance went with it.
|
|
119
|
-
// The job queue is plainjob. Nothing else here is.
|
|
120
|
-
'./jobs': ['plainjob'],
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* The subpaths whose whole PURPOSE is a bun-specific runtime, mapped to the builtins they
|
|
125
|
-
* are allowed to reach. Everything NOT listed here must be importable on `workerd`, which
|
|
126
|
-
* is the property the D1 seam exists to deliver.
|
|
127
|
-
*
|
|
128
|
-
* ๐ด Measured 2026-09-18 after the `./d1/backup-local` split: these five are the ONLY
|
|
129
|
-
* subpaths in the map that reach a bun builtin at all. Like {@link MAY_DRAG} it is an
|
|
130
|
-
* allowlist, not a baseline โ it can only shrink, and an entry that stops being true is a
|
|
131
|
-
* failure rather than a tidy-up.
|
|
132
|
-
*/
|
|
133
|
-
const MAY_NEED_BUN: Record<string, readonly string[]> = {
|
|
134
|
-
// The whole-package barrel โ it re-exports everything, including the Mac-hosted halves.
|
|
135
|
-
// An app importing `cursedbelt-server` whole is a Bun server by construction; an app
|
|
136
|
-
// importing `cursedbelt-server/d1` is not, and that is the distinction this protects.
|
|
137
|
-
'.': ['bun:sqlite'],
|
|
138
|
-
// The local backup IS `VACUUM INTO` against a live `bun:sqlite` handle. Split out of
|
|
139
|
-
// `./d1` on 2026-09-18 precisely so the seam itself stops paying for it โ see the header.
|
|
140
|
-
'./d1/backup-local': ['bun:sqlite'],
|
|
141
|
-
// The guard's revocation store is a `bun:sqlite` table, and `./guard` mounts it.
|
|
142
|
-
'./guard': ['bun:sqlite'],
|
|
143
|
-
'./guard/revocations': ['bun:sqlite'],
|
|
144
|
-
// `./sqlite` is the bun:sqlite tier. Naming it is the point of the subpath.
|
|
145
|
-
'./sqlite': ['bun:sqlite'],
|
|
146
|
-
// The engagement store IS `engagement.sqlite` on the Mac (4.19.0, task 280) โ the recorder
|
|
147
|
-
// runs where the app's own database is. A Worker app records nothing until it has a D1 store.
|
|
148
|
-
'./engagement': ['bun:sqlite'],
|
|
149
|
-
};
|
|
150
|
-
|
|
151
103
|
const OPTIONAL_PEERS = new Set(
|
|
152
104
|
Object.entries(
|
|
153
105
|
(pkg as { peerDependenciesMeta?: Record<string, { optional?: boolean }> }).peerDependenciesMeta ?? {},
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* README.md's "What you install" table is what an importing app reads (task 104) โ so it is held
|
|
3
|
+
* to the same data the bundler check proves (`subpathReach.ts`), row for row, in both directions.
|
|
4
|
+
*/
|
|
5
|
+
import { describe, expect, it } from 'bun:test';
|
|
6
|
+
import { readFileSync } from 'node:fs';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { MAY_DRAG, MAY_NEED_BUN } from './subpathReach.js';
|
|
9
|
+
|
|
10
|
+
const README = readFileSync(fileURLToPath(new URL('../README.md', import.meta.url)), 'utf8');
|
|
11
|
+
|
|
12
|
+
/** `| \`./x\` | \`a\`, \`b\` | โ |` โ { './x': { peers: [a, b], bun: [] } } */
|
|
13
|
+
function tableRows(): Map<string, { peers: string[]; bun: string[] }> {
|
|
14
|
+
const section = README.split('## What you install')[1]?.split('\n## ')[0] ?? '';
|
|
15
|
+
const rows = new Map<string, { peers: string[]; bun: string[] }>();
|
|
16
|
+
for (const line of section.split('\n')) {
|
|
17
|
+
const cells = line.split('|').map((c) => c.trim());
|
|
18
|
+
const subpath = /^`(\.[^`]*)`$/.exec(cells[1] ?? '')?.[1];
|
|
19
|
+
if (!subpath) continue;
|
|
20
|
+
const named = (cell: string | undefined) => [...(cell ?? '').matchAll(/`([^`]+)`/g)].map((m) => m[1] as string).sort();
|
|
21
|
+
rows.set(subpath, { peers: named(cells[2]), bun: named(cells[3]) });
|
|
22
|
+
}
|
|
23
|
+
return rows;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe('README install table', () => {
|
|
27
|
+
const rows = tableRows();
|
|
28
|
+
const expected = new Set([...Object.keys(MAY_DRAG), ...Object.keys(MAY_NEED_BUN)]);
|
|
29
|
+
|
|
30
|
+
it('has a row for every subpath that reaches an optional peer or a bun builtin โ and no other', () => {
|
|
31
|
+
expect(rows.size).toBeGreaterThan(0);
|
|
32
|
+
expect([...rows.keys()].sort()).toEqual([...expected].sort());
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('names exactly what each subpath reaches', () => {
|
|
36
|
+
for (const subpath of expected) {
|
|
37
|
+
expect({ subpath, ...rows.get(subpath) }).toEqual({
|
|
38
|
+
subpath,
|
|
39
|
+
peers: [...(MAY_DRAG[subpath] ?? [])].sort(),
|
|
40
|
+
bun: [...(MAY_NEED_BUN[subpath] ?? [])].sort(),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('warns that the root export is a barrel', () => {
|
|
46
|
+
expect(README).toContain('The root export is a BARREL');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task 104, trap 3 โ `Bun.serve`'s `websocket` selects an OVERLOAD; it is not an optional field.
|
|
3
|
+
*
|
|
4
|
+
* A compiler fact, so its honest carrier is the compiler: `typetests/serveBunOverload.ts` holds the
|
|
5
|
+
* trap under `@ts-expect-error` and the seam (`serveBun`) without one, and this spec runs `tsc` on
|
|
6
|
+
* it. Two ways it goes red, both meaningful:
|
|
7
|
+
*
|
|
8
|
+
* ยท bun's types stop rejecting a maybe-undefined `websocket` โ the `@ts-expect-error` is UNUSED
|
|
9
|
+
* and tsc fails: the trap is gone, so the note in README.md and the fixture can go too;
|
|
10
|
+
* ยท `serveBun` stops accepting an optional `websocket` โ the seam line fails: the one thing an
|
|
11
|
+
* app is told to use instead has itself grown the trap.
|
|
12
|
+
*
|
|
13
|
+
* The second half proves the first can fire: the same fixture with the directives stripped must
|
|
14
|
+
* fail with TS2345 on exactly the two trap lines.
|
|
15
|
+
*/
|
|
16
|
+
import { describe, expect, it } from 'bun:test';
|
|
17
|
+
import { readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
18
|
+
import { fileURLToPath } from 'node:url';
|
|
19
|
+
|
|
20
|
+
const DIR = fileURLToPath(new URL('./typetests/', import.meta.url));
|
|
21
|
+
const TSC = fileURLToPath(new URL('../../node_modules/typescript/bin/tsc', import.meta.url));
|
|
22
|
+
|
|
23
|
+
const tsc = (project: string) => {
|
|
24
|
+
const run = Bun.spawnSync(['bun', TSC, '-p', project], { stdout: 'pipe', stderr: 'pipe' });
|
|
25
|
+
return { code: run.exitCode, out: `${run.stdout.toString()}${run.stderr.toString()}` };
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
describe("Bun.serve's websocket overload", () => {
|
|
29
|
+
it('the trap is still live in bunโs types, and serveBun is the seam that never meets it', () => {
|
|
30
|
+
const run = tsc(`${DIR}tsconfig.json`);
|
|
31
|
+
expect(run.out).toBe('');
|
|
32
|
+
expect(run.code).toBe(0);
|
|
33
|
+
}, 60_000);
|
|
34
|
+
|
|
35
|
+
it('๐ด without the directives the SAME lines fail โ so the check above can go red', () => {
|
|
36
|
+
const source = readFileSync(`${DIR}serveBunOverload.ts`, 'utf8').replace(/^\s*\/\/ @ts-expect-error.*$/gm, '');
|
|
37
|
+
const file = `${DIR}serveBunOverload.stripped.ts`;
|
|
38
|
+
const project = `${DIR}tsconfig.stripped.json`;
|
|
39
|
+
writeFileSync(file, source);
|
|
40
|
+
writeFileSync(project, JSON.stringify({ extends: './tsconfig.json', include: ['serveBunOverload.stripped.ts'] }));
|
|
41
|
+
try {
|
|
42
|
+
const run = tsc(project);
|
|
43
|
+
expect(run.code).not.toBe(0);
|
|
44
|
+
const errors = run.out.split('\n').filter((line) => line.includes('error TS'));
|
|
45
|
+
expect(errors).toHaveLength(2);
|
|
46
|
+
for (const line of errors) expect(line).toContain('TS2345');
|
|
47
|
+
} finally {
|
|
48
|
+
rmSync(file, { force: true });
|
|
49
|
+
rmSync(project, { force: true });
|
|
50
|
+
}
|
|
51
|
+
}, 60_000);
|
|
52
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What each public subpath of this package REACHES that a consumer may not have โ the optional
|
|
3
|
+
* peers it imports statically, and the bun builtins it needs.
|
|
4
|
+
*
|
|
5
|
+
* Two specs read these tables and neither may drift from the other:
|
|
6
|
+
* ยท `barrelsReachNoOptionalPeer.spec.ts` bundles every subpath and fails when one reaches
|
|
7
|
+
* something its row does not list, or a row lists something it no longer reaches;
|
|
8
|
+
* ยท `readmeInstallTable.spec.ts` fails when README.md's "What you install" table disagrees
|
|
9
|
+
* with these rows โ the table an importing app actually reads (task 104).
|
|
10
|
+
* Not exported from package.json: this is the check's data, not an API.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The subpaths whose whole PURPOSE is the optional peer they pull, mapped to what they are
|
|
15
|
+
* allowed to pull and why. An entry here is a promise that the peer is the point of the
|
|
16
|
+
* subpath โ not a place to park a new drag.
|
|
17
|
+
*
|
|
18
|
+
* ๐ด Measured 2026-09-18: these three are the ONLY subpaths in the map that reach an
|
|
19
|
+
* optional peer. Everything else is clean, which is what makes this list an allowlist
|
|
20
|
+
* rather than a baseline โ it can only shrink.
|
|
21
|
+
*/
|
|
22
|
+
export const MAY_DRAG: Record<string, readonly string[]> = {
|
|
23
|
+
// The whole-package barrel. It exists to re-export everything, so it necessarily reaches
|
|
24
|
+
// what the specific subpaths reach. An app importing `cursedbelt-server` whole is asking
|
|
25
|
+
// for that; an app importing `cursedbelt-server/d1` is not, and that is the distinction
|
|
26
|
+
// this spec protects.
|
|
27
|
+
'.': ['kysely', 'kysely-bun-sqlite', 'otplib', 'plainjob'],
|
|
28
|
+
// `./d1/kysely` (`createD1Kysely`) was here until 4.19.0, when it was removed: no file in
|
|
29
|
+
// the generation had ever imported it (task 472's census), so the allowance went with it.
|
|
30
|
+
// The job queue is plainjob. Nothing else here is.
|
|
31
|
+
'./jobs': ['plainjob'],
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The subpaths whose whole PURPOSE is a bun-specific runtime, mapped to the builtins they
|
|
36
|
+
* are allowed to reach. Everything NOT listed here must be importable on `workerd`, which
|
|
37
|
+
* is the property the D1 seam exists to deliver.
|
|
38
|
+
*
|
|
39
|
+
* ๐ด Measured 2026-09-18 after the `./d1/backup-local` split: these five are the ONLY
|
|
40
|
+
* subpaths in the map that reach a bun builtin at all. Like {@link MAY_DRAG} it is an
|
|
41
|
+
* allowlist, not a baseline โ it can only shrink, and an entry that stops being true is a
|
|
42
|
+
* failure rather than a tidy-up.
|
|
43
|
+
*/
|
|
44
|
+
export const MAY_NEED_BUN: Record<string, readonly string[]> = {
|
|
45
|
+
// The whole-package barrel โ it re-exports everything, including the Mac-hosted halves.
|
|
46
|
+
// An app importing `cursedbelt-server` whole is a Bun server by construction; an app
|
|
47
|
+
// importing `cursedbelt-server/d1` is not, and that is the distinction this protects.
|
|
48
|
+
'.': ['bun:sqlite'],
|
|
49
|
+
// The local backup IS `VACUUM INTO` against a live `bun:sqlite` handle. Split out of
|
|
50
|
+
// `./d1` on 2026-09-18 precisely so the seam itself stops paying for it โ see the header.
|
|
51
|
+
'./d1/backup-local': ['bun:sqlite'],
|
|
52
|
+
// The guard's revocation store is a `bun:sqlite` table, and `./guard` mounts it.
|
|
53
|
+
'./guard': ['bun:sqlite'],
|
|
54
|
+
'./guard/revocations': ['bun:sqlite'],
|
|
55
|
+
// `./sqlite` is the bun:sqlite tier. Naming it is the point of the subpath.
|
|
56
|
+
'./sqlite': ['bun:sqlite'],
|
|
57
|
+
// The engagement store IS `engagement.sqlite` on the Mac (4.19.0, task 280) โ the recorder
|
|
58
|
+
// runs where the app's own database is. A Worker app records nothing until it has a D1 store.
|
|
59
|
+
'./engagement': ['bun:sqlite'],
|
|
60
|
+
};
|
|
61
|
+
|