toiljs 0.0.115 → 0.0.117

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 (326) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +16 -7
  3. package/TYPESCRIPT_LAW.md +1946 -2073
  4. package/build/backend/.tsbuildinfo +1 -1
  5. package/build/cli/.tsbuildinfo +1 -1
  6. package/build/cli/index.js +2375 -258
  7. package/build/client/.tsbuildinfo +1 -1
  8. package/build/client/dev/devtools.js +1 -1
  9. package/build/client/dev/error-overlay.js +1 -1
  10. package/build/client/routing/mount.js +1 -1
  11. package/build/compiler/.tsbuildinfo +1 -1
  12. package/build/compiler/auth-emails.js +7 -3
  13. package/build/compiler/emails.js +1 -1
  14. package/build/compiler/index.d.ts +1 -0
  15. package/build/compiler/index.js +25 -4
  16. package/build/compiler/pages.js +2 -3
  17. package/build/compiler/prerender.d.ts +3 -8
  18. package/build/compiler/prerender.js +88 -127
  19. package/build/compiler/ssg.js +3 -4
  20. package/build/compiler/template-build.js +3 -4
  21. package/build/compiler/toil-docs.generated.js +6 -5
  22. package/build/compiler/vite.js +3 -6
  23. package/build/devserver/.tsbuildinfo +1 -1
  24. package/build/devserver/db/database.js +7 -3
  25. package/build/devserver/db/types.js +1 -1
  26. package/build/devserver/production.js +4 -3
  27. package/build/devserver/runtime/crypto.d.ts +1 -1
  28. package/build/io/.tsbuildinfo +1 -1
  29. package/build/logger/.tsbuildinfo +1 -1
  30. package/build/shared/.tsbuildinfo +1 -1
  31. package/examples/basic/client/routes/analytics.tsx +7 -5
  32. package/examples/basic/server/tsconfig.json +1 -3
  33. package/package.json +45 -44
  34. package/presets/no-uint8array-tostring.js +120 -153
  35. package/presets/oxlint.d.ts +5 -0
  36. package/presets/oxlint.js +224 -0
  37. package/server/runtime/tsconfig.json +1 -3
  38. package/tsconfig.json +2 -11
  39. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
  40. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -90
  41. package/.github/ISSUE_TEMPLATE/config.yml +0 -8
  42. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
  43. package/.github/PULL_REQUEST_TEMPLATE.md +0 -43
  44. package/.github/changelog-config.json +0 -45
  45. package/.github/dependabot.yml +0 -27
  46. package/.github/workflows/ci.yml +0 -222
  47. package/.vscode/settings.json +0 -10
  48. package/docs/README.md +0 -107
  49. package/docs/auth/README.md +0 -104
  50. package/docs/auth/configuration.md +0 -105
  51. package/docs/auth/emails.md +0 -92
  52. package/docs/auth/extending.md +0 -202
  53. package/docs/auth/how-it-works.md +0 -138
  54. package/docs/auth/usage.md +0 -188
  55. package/docs/backend/README.md +0 -143
  56. package/docs/backend/data.md +0 -351
  57. package/docs/backend/rest.md +0 -402
  58. package/docs/backend/rpc.md +0 -226
  59. package/docs/background/README.md +0 -114
  60. package/docs/background/daemons.md +0 -276
  61. package/docs/background/derive.md +0 -179
  62. package/docs/cli/README.md +0 -379
  63. package/docs/concepts/ai-guide.md +0 -132
  64. package/docs/concepts/config.md +0 -416
  65. package/docs/concepts/decorators.md +0 -127
  66. package/docs/concepts/security.md +0 -108
  67. package/docs/concepts/tiers.md +0 -166
  68. package/docs/concepts/types.md +0 -216
  69. package/docs/database/README.md +0 -143
  70. package/docs/database/capacity.md +0 -350
  71. package/docs/database/counters.md +0 -174
  72. package/docs/database/documents.md +0 -277
  73. package/docs/database/events.md +0 -307
  74. package/docs/database/membership.md +0 -246
  75. package/docs/database/setup.md +0 -155
  76. package/docs/database/unique.md +0 -216
  77. package/docs/database/views.md +0 -246
  78. package/docs/frontend/README.md +0 -104
  79. package/docs/frontend/components.md +0 -286
  80. package/docs/frontend/data-fetching.md +0 -260
  81. package/docs/frontend/images.md +0 -148
  82. package/docs/frontend/metadata.md +0 -344
  83. package/docs/frontend/navigation.md +0 -296
  84. package/docs/frontend/rendering.md +0 -236
  85. package/docs/frontend/routing.md +0 -346
  86. package/docs/frontend/scripts.md +0 -118
  87. package/docs/frontend/search.md +0 -191
  88. package/docs/frontend/styling.md +0 -147
  89. package/docs/frontend/toil-global.md +0 -216
  90. package/docs/getting-started/README.md +0 -81
  91. package/docs/getting-started/create-project.md +0 -131
  92. package/docs/getting-started/deploy.md +0 -101
  93. package/docs/getting-started/first-app.md +0 -215
  94. package/docs/getting-started/installation.md +0 -124
  95. package/docs/getting-started/migrating.md +0 -125
  96. package/docs/getting-started/project-structure.md +0 -163
  97. package/docs/introduction/README.md +0 -66
  98. package/docs/introduction/design-principles.md +0 -81
  99. package/docs/introduction/distributed.md +0 -91
  100. package/docs/introduction/how-it-works.md +0 -121
  101. package/docs/introduction/hyperscale.md +0 -70
  102. package/docs/introduction/modern-stack.md +0 -85
  103. package/docs/introduction/vs-other-frameworks.md +0 -64
  104. package/docs/introduction/why-toil.md +0 -99
  105. package/docs/llms.txt +0 -95
  106. package/docs/realtime/README.md +0 -102
  107. package/docs/realtime/channels.md +0 -211
  108. package/docs/realtime/streams.md +0 -369
  109. package/docs/services/README.md +0 -60
  110. package/docs/services/analytics.md +0 -279
  111. package/docs/services/caching.md +0 -175
  112. package/docs/services/cookies.md +0 -235
  113. package/docs/services/crypto.md +0 -209
  114. package/docs/services/email.md +0 -289
  115. package/docs/services/environment.md +0 -141
  116. package/docs/services/ratelimit.md +0 -174
  117. package/docs/services/time.md +0 -85
  118. package/eslint.config.js +0 -57
  119. package/examples/basic/client/routes/docs/[...slug].tsx +0 -12
  120. package/presets/eslint.js +0 -88
  121. package/src/backend/index.ts +0 -204
  122. package/src/cli/configure.ts +0 -363
  123. package/src/cli/create.ts +0 -924
  124. package/src/cli/db.ts +0 -158
  125. package/src/cli/diagnostics.ts +0 -665
  126. package/src/cli/doctor.ts +0 -982
  127. package/src/cli/features.ts +0 -170
  128. package/src/cli/index.ts +0 -326
  129. package/src/cli/notify.ts +0 -120
  130. package/src/cli/proc.ts +0 -56
  131. package/src/cli/ui.ts +0 -169
  132. package/src/cli/update.ts +0 -248
  133. package/src/cli/updates.ts +0 -92
  134. package/src/cli/validate.ts +0 -31
  135. package/src/cli/version-check.ts +0 -125
  136. package/src/client/auth.ts +0 -869
  137. package/src/client/channel/channel.ts +0 -146
  138. package/src/client/components/Form.tsx +0 -65
  139. package/src/client/components/Image.tsx +0 -160
  140. package/src/client/components/Script.tsx +0 -113
  141. package/src/client/components/Slot.tsx +0 -21
  142. package/src/client/dev/devtools.tsx +0 -1089
  143. package/src/client/dev/error-overlay.tsx +0 -225
  144. package/src/client/errors.ts +0 -11
  145. package/src/client/head/head.ts +0 -182
  146. package/src/client/head/metadata.ts +0 -110
  147. package/src/client/index.ts +0 -130
  148. package/src/client/navigation/Link.tsx +0 -108
  149. package/src/client/navigation/NavLink.tsx +0 -86
  150. package/src/client/navigation/navigation.ts +0 -235
  151. package/src/client/navigation/prefetch.ts +0 -169
  152. package/src/client/navigation/scroll.ts +0 -53
  153. package/src/client/routing/Router.tsx +0 -216
  154. package/src/client/routing/action.ts +0 -122
  155. package/src/client/routing/error-boundary.tsx +0 -43
  156. package/src/client/routing/hooks.ts +0 -140
  157. package/src/client/routing/lazy.ts +0 -77
  158. package/src/client/routing/loader.ts +0 -381
  159. package/src/client/routing/match.ts +0 -47
  160. package/src/client/routing/mount.tsx +0 -90
  161. package/src/client/routing/params-context.ts +0 -10
  162. package/src/client/routing/slot-context.ts +0 -7
  163. package/src/client/rpc.ts +0 -97
  164. package/src/client/search/search.ts +0 -189
  165. package/src/client/search/use-page-search.ts +0 -73
  166. package/src/client/ssr/markers.tsx +0 -172
  167. package/src/client/stream/client.ts +0 -237
  168. package/src/client/types.ts +0 -73
  169. package/src/compiler/auth-emails.ts +0 -312
  170. package/src/compiler/config.ts +0 -369
  171. package/src/compiler/docs.ts +0 -127
  172. package/src/compiler/email-preview.ts +0 -314
  173. package/src/compiler/emails.ts +0 -404
  174. package/src/compiler/fonts.ts +0 -89
  175. package/src/compiler/generate.ts +0 -471
  176. package/src/compiler/image-blur.ts +0 -42
  177. package/src/compiler/image-report.ts +0 -88
  178. package/src/compiler/index.ts +0 -1362
  179. package/src/compiler/pages.ts +0 -49
  180. package/src/compiler/plugin.ts +0 -391
  181. package/src/compiler/prerender.ts +0 -263
  182. package/src/compiler/routes.ts +0 -170
  183. package/src/compiler/seo.ts +0 -415
  184. package/src/compiler/sri.ts +0 -144
  185. package/src/compiler/ssg.ts +0 -177
  186. package/src/compiler/ssr-codegen.ts +0 -85
  187. package/src/compiler/template-build.ts +0 -676
  188. package/src/compiler/template.ts +0 -262
  189. package/src/compiler/toil-docs.generated.ts +0 -77
  190. package/src/compiler/vite.ts +0 -245
  191. package/src/devserver/analytics/index.ts +0 -317
  192. package/src/devserver/config/dotenv.ts +0 -102
  193. package/src/devserver/config/env.ts +0 -30
  194. package/src/devserver/config/ratelimit.ts +0 -123
  195. package/src/devserver/daemon/catalog.ts +0 -120
  196. package/src/devserver/daemon/cron.ts +0 -87
  197. package/src/devserver/daemon/host.ts +0 -134
  198. package/src/devserver/daemon/index.ts +0 -369
  199. package/src/devserver/daemon/runtime.ts +0 -48
  200. package/src/devserver/db/catalog.ts +0 -109
  201. package/src/devserver/db/database.ts +0 -1834
  202. package/src/devserver/db/derives.ts +0 -121
  203. package/src/devserver/db/index.ts +0 -20
  204. package/src/devserver/db/routeKinds.ts +0 -191
  205. package/src/devserver/db/types.ts +0 -168
  206. package/src/devserver/email/caps.ts +0 -0
  207. package/src/devserver/email/config.ts +0 -141
  208. package/src/devserver/email/console.ts +0 -348
  209. package/src/devserver/email/index.ts +0 -155
  210. package/src/devserver/email/providers.ts +0 -131
  211. package/src/devserver/email/status.ts +0 -23
  212. package/src/devserver/email/validate.ts +0 -37
  213. package/src/devserver/email/wire.ts +0 -55
  214. package/src/devserver/http/cache.ts +0 -0
  215. package/src/devserver/http/envelope.ts +0 -157
  216. package/src/devserver/http/proxy.ts +0 -172
  217. package/src/devserver/http/runtime.ts +0 -387
  218. package/src/devserver/index.ts +0 -43
  219. package/src/devserver/production-ipc.ts +0 -63
  220. package/src/devserver/production-worker.ts +0 -83
  221. package/src/devserver/production.ts +0 -718
  222. package/src/devserver/runtime/crypto.ts +0 -587
  223. package/src/devserver/runtime/host.ts +0 -427
  224. package/src/devserver/runtime/module.ts +0 -442
  225. package/src/devserver/server.ts +0 -251
  226. package/src/devserver/ssr.ts +0 -208
  227. package/src/devserver/stream/catalog.ts +0 -106
  228. package/src/devserver/stream/host.ts +0 -42
  229. package/src/devserver/stream/index.ts +0 -301
  230. package/src/devserver/stream/manager.ts +0 -167
  231. package/src/devserver/stream/router.ts +0 -101
  232. package/src/devserver/stream/wire.ts +0 -58
  233. package/src/devserver/stream/ws.ts +0 -78
  234. package/src/devserver/wasm/sections.ts +0 -59
  235. package/src/devserver/wasm/surface.ts +0 -115
  236. package/src/io/FastMap.ts +0 -151
  237. package/src/io/FastSet.ts +0 -113
  238. package/src/io/codec.ts +0 -362
  239. package/src/io/index.ts +0 -10
  240. package/src/io/lengths.ts +0 -14
  241. package/src/io/types.ts +0 -19
  242. package/src/logger/index.ts +0 -22
  243. package/src/shared/index.ts +0 -46
  244. package/test/analytics-dev.test.ts +0 -113
  245. package/test/assembly/aspect-shim.ts +0 -33
  246. package/test/assembly/cookie.spec.ts +0 -303
  247. package/test/assembly/example.spec.ts +0 -22
  248. package/test/assembly/ssr.spec.ts +0 -95
  249. package/test/assembly.test.ts +0 -59
  250. package/test/auth-emails.test.ts +0 -118
  251. package/test/built-ssr.test.ts +0 -148
  252. package/test/channel.test.ts +0 -21
  253. package/test/config-host-port.test.ts +0 -59
  254. package/test/configure.test.ts +0 -94
  255. package/test/daemon-build.test.ts +0 -278
  256. package/test/daemon-catalog.test.ts +0 -349
  257. package/test/daemon-emulation.test.ts +0 -256
  258. package/test/db.test.ts +0 -0
  259. package/test/devserver-database.test.ts +0 -1102
  260. package/test/devserver-email.test.ts +0 -306
  261. package/test/devserver-pqauth.test.ts +0 -93
  262. package/test/devserver-secrets.test.ts +0 -63
  263. package/test/devserver.test.ts +0 -259
  264. package/test/doctor.test.ts +0 -298
  265. package/test/dom/Image.test.tsx +0 -144
  266. package/test/dom/Link.test.tsx +0 -67
  267. package/test/dom/NavLink.test.tsx +0 -37
  268. package/test/dom/Script.test.tsx +0 -48
  269. package/test/dom/action.test.tsx +0 -146
  270. package/test/dom/error-overlay.test.tsx +0 -44
  271. package/test/dom/loader.test.tsx +0 -121
  272. package/test/dom/navigation.test.ts +0 -59
  273. package/test/dom/revalidate.test.tsx +0 -38
  274. package/test/dom/route-head.test.tsx +0 -56
  275. package/test/dom/router-loading.test.tsx +0 -44
  276. package/test/dom/scroll.test.ts +0 -56
  277. package/test/dom/slot.test.tsx +0 -131
  278. package/test/dom/use-metadata.test.tsx +0 -58
  279. package/test/dom/view-transitions.test.tsx +0 -53
  280. package/test/dynamic-routing.test.ts +0 -132
  281. package/test/email-console.test.ts +0 -86
  282. package/test/email-preview.test.ts +0 -73
  283. package/test/emails.test.ts +0 -58
  284. package/test/errors.test.ts +0 -21
  285. package/test/example-guestbook.test.ts +0 -123
  286. package/test/features.test.ts +0 -149
  287. package/test/fixtures/bignum-wire/spec.ts +0 -25
  288. package/test/fixtures/daemon-app.ts +0 -65
  289. package/test/fixtures/stream-echo.ts +0 -26
  290. package/test/fixtures/stream-gate.ts +0 -28
  291. package/test/fixtures/stream-trap.ts +0 -18
  292. package/test/fixtures/stream-typed.ts +0 -41
  293. package/test/fonts.test.ts +0 -28
  294. package/test/global-setup.ts +0 -17
  295. package/test/head.test.ts +0 -42
  296. package/test/image-blur.test.ts +0 -50
  297. package/test/io.test.ts +0 -117
  298. package/test/metadata.test.ts +0 -40
  299. package/test/navlink.test.ts +0 -28
  300. package/test/pages.test.ts +0 -105
  301. package/test/placeholder.test.ts +0 -9
  302. package/test/pqauth-e2e.test.ts +0 -216
  303. package/test/pqauth-email.test.ts +0 -801
  304. package/test/prerender.test.ts +0 -116
  305. package/test/prettier-plugin.test.ts +0 -46
  306. package/test/routes.test.ts +0 -76
  307. package/test/rpc-bignum-wire.test.ts +0 -164
  308. package/test/rpc-dispatch.test.ts +0 -132
  309. package/test/rpc-kinds.test.ts +0 -18
  310. package/test/rpc.test.ts +0 -66
  311. package/test/search.test.ts +0 -114
  312. package/test/seo.test.ts +0 -175
  313. package/test/slot-layouts.test.ts +0 -69
  314. package/test/sri.test.ts +0 -129
  315. package/test/ssg.test.ts +0 -36
  316. package/test/ssr-hydration.test.tsx +0 -122
  317. package/test/ssr-render.test.ts +0 -197
  318. package/test/ssr-template.test.tsx +0 -482
  319. package/test/stream-emulation.test.ts +0 -479
  320. package/test/ui.test.ts +0 -40
  321. package/test/update.test.ts +0 -58
  322. package/test/validate.test.ts +0 -42
  323. package/test/version-check.test.ts +0 -130
  324. package/tests/data-parity/generated-parity.ts +0 -99
  325. package/tests/data-parity/parity.ts +0 -80
  326. package/tests/data-parity/spec.ts +0 -46
@@ -1,479 +0,0 @@
1
- /**
2
- * Dev STREAM emulation end-to-end (Phase 4). Compiles real `@stream` fixtures with the LOCAL toilscript
3
- * (`--targetMode hot`), then drives `DevStreamBox` + `StreamDevHost` and asserts the dev runtime mirrors
4
- * the production edge (`toil-backend` `src/wasm/stream`) BYTE-FOR-BYTE: the `@message` ring bridge
5
- * (echo / reject / empty), the `@connect` info-block bridge (path-based accept/reject + egress clear),
6
- * and the session driver (accept / dispatch / trap-close / lifecycle). The dev mirror of toil-backend's
7
- * message_bridge + @connect + hostile-isolation tests.
8
- */
9
-
10
- import { spawnSync } from 'node:child_process';
11
- import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
12
- import { tmpdir } from 'node:os';
13
- import { dirname, join } from 'node:path';
14
- import { fileURLToPath } from 'node:url';
15
-
16
- import { Server } from '@dacely/hyper-express';
17
- import { afterAll, beforeAll, describe, expect, it } from 'vitest';
18
-
19
- import { makeStreamClient } from '../src/client/stream/client.js';
20
- import { matchStreamRoute, parseStreamCatalog } from '../src/devserver/stream/catalog.js';
21
- import { buildStreamImports, freshStreamBoxState } from '../src/devserver/stream/host.js';
22
- import { wireStreams } from '../src/devserver/stream/wire.js';
23
- import { DevStreamBox } from '../src/devserver/stream/index.js';
24
- import { StreamDevHost } from '../src/devserver/stream/manager.js';
25
- import {
26
- StreamRouter,
27
- type StreamUpgradeContext,
28
- type StreamWs,
29
- } from '../src/devserver/stream/router.js';
30
- import { StreamWsSession, type StreamWsTransport } from '../src/devserver/stream/ws.js';
31
-
32
- const here = dirname(fileURLToPath(import.meta.url));
33
- // The toilscript build with the @message + @connect bridge codegen (it ships in the dep now).
34
- // Resolved from node_modules so this works both locally (the symlinked repo) and in CI (the npm
35
- // dep), not from a sibling checkout that CI does not have.
36
- const LOCAL_TOILSCRIPT_BIN = join(here, '..', 'node_modules', 'toilscript', 'bin', 'toilscript.js');
37
-
38
- let tmp: string;
39
- let ECHO_PATH: string;
40
- let GATE_PATH: string;
41
- let TRAP_PATH: string;
42
- let ECHO: Buffer;
43
- let GATE: Buffer;
44
- let TYPED: Buffer;
45
-
46
- function compile(srcName: string): { path: string; wasm: Buffer } {
47
- const src = join(here, 'fixtures', srcName);
48
- const out = join(tmp, srcName.replace(/\.ts$/, '.wasm'));
49
- const r = spawnSync(
50
- 'node',
51
- [LOCAL_TOILSCRIPT_BIN, src, '-o', out, '--targetMode', 'hot', '--runtime', 'stub'],
52
- { encoding: 'utf8' },
53
- );
54
- if (r.status !== 0) {
55
- throw new Error(
56
- `toilscript compile ${srcName} failed (${String(r.status)}):\n${r.stderr}${r.stdout}`,
57
- );
58
- }
59
- return { path: out, wasm: readFileSync(out) };
60
- }
61
-
62
- beforeAll(() => {
63
- tmp = mkdtempSync(join(tmpdir(), 'toiljs-stream-'));
64
- const echo = compile('stream-echo.ts');
65
- ECHO_PATH = echo.path;
66
- ECHO = echo.wasm;
67
- const gate = compile('stream-gate.ts');
68
- GATE_PATH = gate.path;
69
- GATE = gate.wasm;
70
- TRAP_PATH = compile('stream-trap.ts').path;
71
- TYPED = compile('stream-typed.ts').wasm;
72
- });
73
-
74
- afterAll(() => {
75
- if (tmp) rmSync(tmp, { recursive: true, force: true });
76
- });
77
-
78
- describe('dev stream box: typed @data @message decode (doc 03 2.5)', () => {
79
- it('decodes a guest-seeded @data payload at runtime and replies with the decoded field', () => {
80
- // Seed a real ChatMsg.encode() from a raw instance (deterministic), so the host feeds VALID
81
- // @data bytes without hand-crafting the wire format.
82
- const ref: { memory: WebAssembly.Memory | null } = { memory: null };
83
- const state = freshStreamBoxState();
84
- const seedInst = new WebAssembly.Instance(
85
- new WebAssembly.Module(new Uint8Array(TYPED)),
86
- buildStreamImports(ref, state),
87
- );
88
- const sx = seedInst.exports as unknown as {
89
- memory: WebAssembly.Memory;
90
- seedHi: () => void;
91
- seedOffset: () => number;
92
- seedLength: () => number;
93
- };
94
- ref.memory = sx.memory;
95
- sx.seedHi();
96
- const off = sx.seedOffset() >>> 0;
97
- const len = sx.seedLength() >>> 0;
98
- expect(len).toBeGreaterThan(0);
99
- const payload = Buffer.from(new Uint8Array(sx.memory.buffer, off, len));
100
-
101
- // Drive the typed @message: the guest DECODES ChatMsg{text:"hi"} and replies with text.length.
102
- const box = DevStreamBox.load(TYPED);
103
- const tid = 0x0000_0000_0000_0011n;
104
- expect(box.onConnect(tid, 'localhost', '/').kind).toBe('accept');
105
- const out = box.onMessage(tid, payload);
106
- expect(out.kind).toBe('reply');
107
- if (out.kind === 'reply') {
108
- expect(out.frames.length).toBe(1);
109
- expect(out.frames[0][0]).toBe(2); // decoded "hi" -> text.length 2
110
- }
111
- });
112
- });
113
-
114
- describe('dev stream box: the @message ring bridge', () => {
115
- const id = 0x0000_0007_0000_0005n;
116
-
117
- it('loads a hot stream artifact with the ring runtime', () => {
118
- expect(DevStreamBox.load(ECHO).hasRings).toBe(true);
119
- });
120
-
121
- it('echoes / rejects / empties through the ring, persisting state across events', () => {
122
- const box = DevStreamBox.load(ECHO);
123
- expect(box.onConnect(id, 'localhost', '/').kind).toBe('accept'); // echo declares no @connect
124
- expect(box.onMessage(id, Buffer.from('hello'))).toEqual({
125
- kind: 'reply',
126
- frames: [Buffer.from('hello')],
127
- });
128
- expect(box.onMessage(id, Buffer.from('second frame'))).toEqual({
129
- kind: 'reply',
130
- frames: [Buffer.from('second frame')],
131
- });
132
- expect(box.onMessage(id, Buffer.from('Xdrop'))).toEqual({ kind: 'reject', code: 0x0210 });
133
- expect(box.onMessage(id, Buffer.from(''))).toEqual({ kind: 'reply', frames: [] });
134
- });
135
-
136
- it('rejects a non-stream artifact (fails closed)', () => {
137
- expect(() => DevStreamBox.load(Buffer.from('\0asm\x01\0\0\0', 'binary'))).toThrow();
138
- });
139
- });
140
-
141
- describe('dev stream box: the @connect info-block bridge', () => {
142
- const id = 0x11n;
143
-
144
- it('reads the connect path and rejects /blocked while accepting others', () => {
145
- const box = DevStreamBox.load(GATE);
146
- expect(box.hasConnectBridge).toBe(true);
147
- expect(box.onConnect(id, 'acme.toil', '/blocked')).toEqual({
148
- kind: 'reject',
149
- code: 0x0211,
150
- initialEgress: [],
151
- });
152
-
153
- const ok = DevStreamBox.load(GATE);
154
- expect(ok.onConnect(id, 'acme.toil', '/room/42')).toEqual({
155
- kind: 'accept',
156
- initialEgress: [],
157
- });
158
- // An accepted connection is usable: its @message echoes.
159
- expect(ok.onMessage(id, Buffer.from('hi'))).toEqual({
160
- kind: 'reply',
161
- frames: [Buffer.from('hi')],
162
- });
163
- });
164
-
165
- it('returns @connect-staged egress and keeps the first @message reply clean', () => {
166
- const box = DevStreamBox.load(GATE);
167
- // /greet stages "GHI" during @connect; the host returns it as initial egress.
168
- expect(box.onConnect(id, 'acme.toil', '/greet')).toEqual({
169
- kind: 'accept',
170
- initialEgress: [Buffer.from('GHI')],
171
- });
172
- // The first @message must see ONLY its own reply, never the drained initial "GHI".
173
- expect(box.onMessage(id, Buffer.from('hi'))).toEqual({
174
- kind: 'reply',
175
- frames: [Buffer.from('hi')],
176
- });
177
- });
178
- });
179
-
180
- describe('dev stream session driver (StreamDevHost)', () => {
181
- it('accepts, dispatches, and closes - mirroring StreamWorker', () => {
182
- const host = new StreamDevHost(ECHO_PATH);
183
- expect(host.acceptUpgrade('c1', 'acme.toil', '/').kind).toBe('accepted');
184
- expect(host.activeConnections).toBe(1);
185
- expect(host.dispatch('c1', Buffer.from('one'))).toEqual({
186
- kind: 'reply',
187
- frames: [Buffer.from('one')],
188
- });
189
- // A guest reject -> close with the 0x02xx code.
190
- expect(host.dispatch('c1', Buffer.from('Xstop'))).toEqual({ kind: 'close', code: 0x0210 });
191
- // A frame for an unknown connection.
192
- expect(host.dispatch('ghost', Buffer.from('x'))).toEqual({ kind: 'noConnection' });
193
- host.close('c1');
194
- expect(host.activeConnections).toBe(0);
195
- });
196
-
197
- it('honors a @connect reject at the upgrade (no box registered)', () => {
198
- const host = new StreamDevHost(GATE_PATH);
199
- expect(host.acceptUpgrade('c1', 'acme.toil', '/blocked')).toEqual({
200
- kind: 'rejected',
201
- code: 0x0211,
202
- initialEgress: [],
203
- });
204
- expect(host.activeConnections).toBe(0);
205
- expect(host.acceptUpgrade('c2', 'acme.toil', '/ok').kind).toBe('accepted');
206
- expect(host.dispatch('c2', Buffer.from('hi'))).toEqual({
207
- kind: 'reply',
208
- frames: [Buffer.from('hi')],
209
- });
210
- });
211
-
212
- it('trap-closes a hostile @message and discards only its box', () => {
213
- const host = new StreamDevHost(TRAP_PATH);
214
- host.acceptUpgrade('h1', 'acme.toil', '/');
215
- host.acceptUpgrade('h2', 'acme.toil', '/');
216
- expect(host.activeConnections).toBe(2);
217
- // h1's @message TRAPS -> STREAM_HOOK_TRAPPED close, its box discarded; h2 is untouched.
218
- expect(host.dispatch('h1', Buffer.from('boom'))).toEqual({ kind: 'close', code: 0x0200 });
219
- expect(host.has('h1')).toBe(false);
220
- expect(host.has('h2')).toBe(true);
221
- expect(host.activeConnections).toBe(1);
222
- });
223
-
224
- it('throws on a duplicate open', () => {
225
- const host = new StreamDevHost(ECHO_PATH);
226
- host.acceptUpgrade('c1', 'acme.toil', '/');
227
- expect(() => host.acceptUpgrade('c1', 'acme.toil', '/')).toThrow();
228
- });
229
-
230
- it('returns initial egress from @connect while keeping dispatch clean', () => {
231
- const host = new StreamDevHost(GATE_PATH);
232
- expect(host.acceptUpgrade('c1', 'acme.toil', '/greet')).toMatchObject({
233
- kind: 'accepted',
234
- initialEgress: [Buffer.from('GHI')],
235
- });
236
- expect(host.dispatch('c1', Buffer.from('hi'))).toEqual({
237
- kind: 'reply',
238
- frames: [Buffer.from('hi')],
239
- });
240
- });
241
- });
242
-
243
- describe('dev stream WS session adapter (StreamWsSession)', () => {
244
- function makeTransport(): { sent: Buffer[]; closed: number[]; t: StreamWsTransport } {
245
- const sent: Buffer[] = [];
246
- const closed: number[] = [];
247
- return {
248
- sent,
249
- closed,
250
- t: { send: (f: Buffer) => sent.push(f), close: (c: number) => closed.push(c) },
251
- };
252
- }
253
-
254
- it('accepts, echoes a frame back, and tears down', () => {
255
- const host = new StreamDevHost(ECHO_PATH);
256
- const { sent, closed, t } = makeTransport();
257
- const s = new StreamWsSession(host, 'ws1', 'acme.toil', '/', t);
258
- expect(s.onOpen()).toBe(true);
259
- expect(s.isOpen).toBe(true);
260
- s.onMessage(Buffer.from('hi'));
261
- expect(sent).toEqual([Buffer.from('hi')]);
262
- expect(closed).toEqual([]);
263
- s.onClose();
264
- expect(host.activeConnections).toBe(0);
265
- });
266
-
267
- it('sends @connect initial egress on open', () => {
268
- const host = new StreamDevHost(GATE_PATH);
269
- const { sent, closed, t } = makeTransport();
270
- const s = new StreamWsSession(host, 'ws1', 'acme.toil', '/greet', t);
271
- expect(s.onOpen()).toBe(true);
272
- expect(s.isOpen).toBe(true);
273
- expect(sent).toEqual([Buffer.from('GHI')]);
274
- expect(closed).toEqual([]);
275
- s.onMessage(Buffer.from('hi'));
276
- expect(sent).toEqual([Buffer.from('GHI'), Buffer.from('hi')]);
277
- s.onClose();
278
- expect(host.activeConnections).toBe(0);
279
- });
280
-
281
- it('closes the socket with the code on a guest reject, then fires @close on socket close', () => {
282
- const host = new StreamDevHost(ECHO_PATH);
283
- const { closed, t } = makeTransport();
284
- const s = new StreamWsSession(host, 'ws1', 'acme.toil', '/', t);
285
- s.onOpen();
286
- s.onMessage(Buffer.from('Xstop')); // guest reject -> close 0x0210
287
- expect(closed).toEqual([0x0210]);
288
- expect(s.isOpen).toBe(false);
289
- s.onClose();
290
- expect(host.activeConnections).toBe(0);
291
- });
292
-
293
- it('closes a @connect-rejected upgrade without holding a box', () => {
294
- const host = new StreamDevHost(GATE_PATH);
295
- const { closed, t } = makeTransport();
296
- const s = new StreamWsSession(host, 'ws1', 'acme.toil', '/blocked', t);
297
- expect(s.onOpen()).toBe(false);
298
- expect(closed).toEqual([0x0211]);
299
- expect(host.activeConnections).toBe(0);
300
- });
301
- });
302
-
303
- describe('dev stream catalog (toilstream.catalog route table, doc 08 3.1/4.2)', () => {
304
- it('parses the @stream route table and matches routes (query stripped)', () => {
305
- const cat = parseStreamCatalog(ECHO);
306
- expect(cat.size).toBe(1);
307
- const def = [...cat.values()][0];
308
- expect(def.route.length).toBeGreaterThan(0);
309
- expect(def.hooks.message).toBe(true);
310
- expect(def.scope).toBe('regional'); // declared_scope default
311
- expect(def.messageMode).toBe('raw'); // the raw @message bridge
312
- // matchRoute (4.2): exact match, query stripped; a non-route misses (-> proxied to Vite).
313
- expect(matchStreamRoute(cat, def.route)).toBe(def);
314
- expect(matchStreamRoute(cat, `${def.route}?x=1`)).toBe(def);
315
- expect(matchStreamRoute(cat, '/definitely-not-a-stream')).toBeNull();
316
- });
317
- });
318
-
319
- /** A minimal hyper-express `Websocket` mock for the router (records send/close, replays events). */
320
- class MockWs implements StreamWs {
321
- readonly sent: Buffer[] = [];
322
- readonly closed: number[] = [];
323
- private msgCb?: (m: Buffer, b: boolean) => void;
324
- private closeCb?: () => void;
325
- send(d: Buffer, _isBinary: boolean): void {
326
- this.sent.push(d);
327
- }
328
- close(c: number): void {
329
- this.closed.push(c);
330
- }
331
- on(event: 'message', cb: (m: Buffer, b: boolean) => void): void;
332
- on(event: 'close', cb: () => void): void;
333
- on(event: 'message' | 'close', cb: ((m: Buffer, b: boolean) => void) | (() => void)): void {
334
- if (event === 'message') this.msgCb = cb as (m: Buffer, b: boolean) => void;
335
- else this.closeCb = cb as () => void;
336
- }
337
- emitMessage(m: Buffer): void {
338
- this.msgCb?.(m, true);
339
- }
340
- emitClose(): void {
341
- this.closeCb?.();
342
- }
343
- }
344
-
345
- describe('dev stream router (doc 08 4.1/4.2)', () => {
346
- it('matches @stream routes and bridges a socket to a resident box', () => {
347
- const router = new StreamRouter(ECHO_PATH);
348
- const route = [...parseStreamCatalog(ECHO).keys()][0];
349
- expect(router.matchRoute(route)).not.toBeNull();
350
- expect(router.matchRoute(`${route}?x=1`)).not.toBeNull(); // query stripped
351
- expect(router.matchRoute('/not-a-stream')).toBeNull(); // -> proxied to Vite
352
-
353
- const ws = new MockWs();
354
- const ctx: StreamUpgradeContext = {
355
- kind: 'stream',
356
- route,
357
- url: route,
358
- authority: 'acme.toil',
359
- };
360
- router.onUpgrade(ws, ctx);
361
- expect(router.activeConnections).toBe(1);
362
- ws.emitMessage(Buffer.from('hi'));
363
- expect(ws.sent).toEqual([Buffer.from('hi')]); // @message echoed back over the socket
364
- ws.emitClose();
365
- expect(router.activeConnections).toBe(0); // @close fired + box dropped
366
- });
367
-
368
- it('closes the socket with the code on a @connect reject', () => {
369
- const router = new StreamRouter(GATE_PATH);
370
- const route = [...parseStreamCatalog(GATE).keys()][0];
371
- const ws = new MockWs();
372
- // The gate rejects path "/blocked"; the upgrade's url carries the connect path.
373
- router.onUpgrade(ws, { kind: 'stream', route, url: '/blocked', authority: 'acme.toil' });
374
- expect(ws.closed).toEqual([0x0211]);
375
- expect(router.activeConnections).toBe(0); // rejected -> no resident box
376
- });
377
- });
378
-
379
- describe('dev stream LIVE round-trip (wireStreams over a real WebSocket)', () => {
380
- it('echoes a binary frame end-to-end through app.upgrade + app.ws', async () => {
381
- const router = new StreamRouter(ECHO_PATH);
382
- const route = [...parseStreamCatalog(ECHO).keys()][0];
383
- const app = new Server();
384
- // A dummy Vite target: a @stream-route upgrade never touches it (it goes to the StreamRouter).
385
- wireStreams(app, { host: '127.0.0.1', port: 65535 }, router);
386
-
387
- const PORT = 49317;
388
- await app.listen(PORT);
389
- try {
390
- const echoed = await new Promise<Buffer>((resolve, reject) => {
391
- const ws = new WebSocket(`ws://127.0.0.1:${String(PORT)}${route}`);
392
- ws.binaryType = 'arraybuffer';
393
- const timer = setTimeout(() => {
394
- reject(new Error('no echo within 3s'));
395
- }, 3000);
396
- ws.onopen = (): void => {
397
- ws.send(new Uint8Array([0x68, 0x69])); // "hi"
398
- };
399
- ws.onmessage = (ev: MessageEvent): void => {
400
- clearTimeout(timer);
401
- resolve(Buffer.from(ev.data as ArrayBuffer));
402
- ws.close();
403
- };
404
- ws.onerror = (): void => {
405
- clearTimeout(timer);
406
- reject(new Error('websocket error'));
407
- };
408
- });
409
- expect(echoed).toEqual(Buffer.from('hi'));
410
- } finally {
411
- app.close();
412
- }
413
- });
414
- });
415
-
416
- describe('Server.Stream client (doc 08 8.2 makeStreamClient)', () => {
417
- it('connects + echoes through a real WebSocket end to end', async () => {
418
- const router = new StreamRouter(ECHO_PATH);
419
- const route = [...parseStreamCatalog(ECHO).keys()][0];
420
- const app = new Server();
421
- wireStreams(app, { host: '127.0.0.1', port: 65535 }, router);
422
-
423
- const PORT = 49318;
424
- await app.listen(PORT);
425
- try {
426
- // The generated client would call makeStreamClient({ Echo: route }) -> globalThis.__toilStream.
427
- const stream = makeStreamClient({ Echo: route }, `ws://127.0.0.1:${String(PORT)}`);
428
- const channel = await stream.Echo.connect();
429
- const echoed = await new Promise<Uint8Array>((resolve, reject) => {
430
- const timer = setTimeout(() => {
431
- reject(new Error('no echo within 3s'));
432
- }, 3000);
433
- channel.onMessage((d) => {
434
- clearTimeout(timer);
435
- resolve(d);
436
- });
437
- channel.send(new Uint8Array([0x68, 0x69])); // "hi"
438
- });
439
- expect(Buffer.from(echoed)).toEqual(Buffer.from('hi'));
440
- channel.close();
441
- } finally {
442
- app.close();
443
- }
444
- });
445
-
446
- it('encodes a typed @data message on send (doc 03 2.5)', async () => {
447
- const router = new StreamRouter(ECHO_PATH);
448
- const route = [...parseStreamCatalog(ECHO).keys()][0];
449
- const app = new Server();
450
- wireStreams(app, { host: '127.0.0.1', port: 65535 }, router);
451
-
452
- const PORT = 49319;
453
- await app.listen(PORT);
454
- try {
455
- // A typed class: the encoder serializes { text } -> bytes (stands in for ChatMsg.encode()).
456
- const encode = (m: { text: string }): Uint8Array => new TextEncoder().encode(m.text);
457
- const stream = makeStreamClient({ Echo: route }, `ws://127.0.0.1:${String(PORT)}`, {
458
- Echo: encode,
459
- });
460
- const channel = await stream.Echo.connect();
461
- const echoed = await new Promise<Uint8Array>((resolve, reject) => {
462
- const timer = setTimeout(() => {
463
- reject(new Error('no echo within 3s'));
464
- }, 3000);
465
- channel.onMessage((d) => {
466
- clearTimeout(timer);
467
- resolve(d);
468
- });
469
- // Send a TYPED message; the channel must encode it before the raw WS send.
470
- (channel.send as unknown as (m: { text: string }) => void)({ text: 'typed' });
471
- });
472
- // The echo server returns the raw bytes it received -> proves the encoder ran on send.
473
- expect(Buffer.from(echoed)).toEqual(Buffer.from('typed'));
474
- channel.close();
475
- } finally {
476
- app.close();
477
- }
478
- });
479
- });
package/test/ui.test.ts DELETED
@@ -1,40 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
-
3
- import { box, tagline } from '../src/cli/ui';
4
-
5
- describe('tagline', () => {
6
- it('always yields a non-empty line, whichever variant is drawn', () => {
7
- for (let i = 0; i < 100; i++) {
8
- const t = tagline();
9
- expect(t.length).toBeGreaterThan(0);
10
- expect(t).not.toContain('undefined');
11
- }
12
- });
13
- });
14
-
15
- describe('box', () => {
16
- it('frames lines in a rounded box sized to the widest line', () => {
17
- expect(box(['hello', 'hi'])).toBe(
18
- [
19
- ' ╭─────────╮',
20
- ' │ hello │',
21
- ' │ hi │',
22
- ' ╰─────────╯',
23
- ].join('\n'),
24
- );
25
- });
26
-
27
- it('pads on visible width, ignoring ANSI color codes', () => {
28
- const colored = '\x1b[1mhello\x1b[22m';
29
- const lines = box([colored, 'hi']).split('\n');
30
- // Both content rows must end at the same column once colors are stripped.
31
- const stripped = lines.map((l) => l.replace(/\x1b\[[0-9;]*m/g, ''));
32
- expect(new Set(stripped.map((l) => l.length)).size).toBe(1);
33
- });
34
-
35
- it('paints only the border', () => {
36
- const out = box(['hi'], (s) => `<${s}>`);
37
- expect(out).toContain('<│> hi <│>');
38
- expect(out).toContain('<╭──────╮>');
39
- });
40
- });
@@ -1,58 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
-
3
- import { buildRows, classifyBump, parseNcuJson, withheldUpgrades } from '../src/cli/updates';
4
-
5
- describe('classifyBump', () => {
6
- it('classifies major / minor / patch from ranges', () => {
7
- expect(classifyBump('^19.2.6', '^20.0.0')).toBe('major');
8
- expect(classifyBump('^19.2.6', '^19.3.0')).toBe('minor');
9
- expect(classifyBump('^19.2.6', '^19.2.7')).toBe('patch');
10
- expect(classifyBump('1.2.3', '1.2.3')).toBe('other');
11
- });
12
- });
13
-
14
- describe('parseNcuJson', () => {
15
- it('parses the jsonUpgraded object, tolerating surrounding noise', () => {
16
- expect(parseNcuJson('{"react":"^19.3.0","eslint":"^10.4.1"}')).toEqual({
17
- react: '^19.3.0',
18
- eslint: '^10.4.1',
19
- });
20
- expect(parseNcuJson('npx noise\n{"a":"1.0.0"}\nbye')).toEqual({ a: '1.0.0' });
21
- expect(parseNcuJson('{}')).toEqual({});
22
- expect(parseNcuJson('not json')).toEqual({});
23
- });
24
- });
25
-
26
- describe('buildRows', () => {
27
- it('joins ncu output with current ranges and sorts major-first then by name', () => {
28
- const rows = buildRows(
29
- { react: '^20.0.0', eslint: '^10.4.1', vite: '^8.0.15' },
30
- { react: '^19.2.6', eslint: '^10.2.0', vite: '^8.0.14' },
31
- );
32
- expect(rows.map((r) => `${r.name}:${r.bump}`)).toEqual([
33
- 'react:major',
34
- 'eslint:minor',
35
- 'vite:patch',
36
- ]);
37
- expect(rows[0]).toMatchObject({ name: 'react', from: '^19.2.6', to: '^20.0.0' });
38
- });
39
-
40
- it('marks a package missing from current deps with a "?" source', () => {
41
- const rows = buildRows({ foo: '^2.0.0' }, {});
42
- expect(rows[0]).toMatchObject({ name: 'foo', from: '?', to: '^2.0.0', bump: 'major' });
43
- });
44
- });
45
-
46
- describe('withheldUpgrades', () => {
47
- it('withholds an upgrade into typescript 7 (the native port has no compiler API)', () => {
48
- expect(withheldUpgrades({ typescript: '^7.0.2', react: '^20.0.0' })).toEqual(['typescript']);
49
- });
50
-
51
- it('still offers typescript bumps inside the supported major', () => {
52
- expect(withheldUpgrades({ typescript: '^6.1.0' })).toEqual([]);
53
- });
54
-
55
- it('ignores packages with no declared ceiling', () => {
56
- expect(withheldUpgrades({ vite: '^9.0.0', eslint: '^11.0.0' })).toEqual([]);
57
- });
58
- });
@@ -1,42 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
-
3
- import { isPackageManager, isValidName, resolveProjectDir } from '../src/cli/validate';
4
-
5
- describe('isValidName', () => {
6
- it('accepts simple and nested names', () => {
7
- expect(isValidName('my-app')).toBe(true);
8
- expect(isValidName('apps/web')).toBe(true);
9
- expect(isValidName('@scope/app')).toBe(true);
10
- });
11
-
12
- it('rejects empty and illegal characters', () => {
13
- expect(typeof isValidName('')).toBe('string');
14
- expect(typeof isValidName(' ')).toBe('string');
15
- expect(typeof isValidName('bad name!')).toBe('string');
16
- });
17
- });
18
-
19
- describe('resolveProjectDir', () => {
20
- it('resolves names inside cwd', () => {
21
- expect(resolveProjectDir('/work', 'app')).toBe('/work/app');
22
- expect(resolveProjectDir('/work', 'a/b')).toBe('/work/a/b');
23
- expect(resolveProjectDir('/work', '.')).toBe('/work');
24
- });
25
-
26
- it('refuses to escape cwd (traversal / absolute)', () => {
27
- expect(resolveProjectDir('/work', '../evil')).toBeNull();
28
- expect(resolveProjectDir('/work', '../../etc')).toBeNull();
29
- expect(resolveProjectDir('/work', '/etc/passwd')).toBeNull();
30
- });
31
- });
32
-
33
- describe('isPackageManager', () => {
34
- it('allowlists known package managers only', () => {
35
- expect(isPackageManager('npm')).toBe(true);
36
- expect(isPackageManager('pnpm')).toBe(true);
37
- expect(isPackageManager('yarn')).toBe(true);
38
- expect(isPackageManager('bun')).toBe(true);
39
- expect(isPackageManager('npm && calc')).toBe(false);
40
- expect(isPackageManager('rm -rf /')).toBe(false);
41
- });
42
- });