toiljs 0.0.116 → 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 (325) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +16 -7
  3. package/build/backend/.tsbuildinfo +1 -1
  4. package/build/cli/.tsbuildinfo +1 -1
  5. package/build/cli/index.js +2375 -258
  6. package/build/client/.tsbuildinfo +1 -1
  7. package/build/client/dev/devtools.js +1 -1
  8. package/build/client/dev/error-overlay.js +1 -1
  9. package/build/client/routing/mount.js +1 -1
  10. package/build/compiler/.tsbuildinfo +1 -1
  11. package/build/compiler/auth-emails.js +7 -3
  12. package/build/compiler/emails.js +1 -1
  13. package/build/compiler/index.d.ts +1 -0
  14. package/build/compiler/index.js +25 -4
  15. package/build/compiler/pages.js +2 -3
  16. package/build/compiler/prerender.d.ts +3 -8
  17. package/build/compiler/prerender.js +88 -127
  18. package/build/compiler/ssg.js +3 -4
  19. package/build/compiler/template-build.js +3 -4
  20. package/build/compiler/toil-docs.generated.js +6 -5
  21. package/build/compiler/vite.js +3 -6
  22. package/build/devserver/.tsbuildinfo +1 -1
  23. package/build/devserver/db/database.js +7 -3
  24. package/build/devserver/db/types.js +1 -1
  25. package/build/devserver/production.js +4 -3
  26. package/build/devserver/runtime/crypto.d.ts +1 -1
  27. package/build/io/.tsbuildinfo +1 -1
  28. package/build/logger/.tsbuildinfo +1 -1
  29. package/build/shared/.tsbuildinfo +1 -1
  30. package/examples/basic/client/routes/analytics.tsx +7 -5
  31. package/examples/basic/server/tsconfig.json +1 -3
  32. package/package.json +45 -44
  33. package/presets/no-uint8array-tostring.js +120 -153
  34. package/presets/oxlint.d.ts +5 -0
  35. package/presets/oxlint.js +224 -0
  36. package/server/runtime/tsconfig.json +1 -3
  37. package/tsconfig.json +2 -11
  38. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
  39. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -90
  40. package/.github/ISSUE_TEMPLATE/config.yml +0 -8
  41. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
  42. package/.github/PULL_REQUEST_TEMPLATE.md +0 -43
  43. package/.github/changelog-config.json +0 -45
  44. package/.github/dependabot.yml +0 -27
  45. package/.github/workflows/ci.yml +0 -222
  46. package/.vscode/settings.json +0 -10
  47. package/docs/README.md +0 -107
  48. package/docs/auth/README.md +0 -104
  49. package/docs/auth/configuration.md +0 -105
  50. package/docs/auth/emails.md +0 -92
  51. package/docs/auth/extending.md +0 -202
  52. package/docs/auth/how-it-works.md +0 -138
  53. package/docs/auth/usage.md +0 -188
  54. package/docs/backend/README.md +0 -143
  55. package/docs/backend/data.md +0 -351
  56. package/docs/backend/rest.md +0 -402
  57. package/docs/backend/rpc.md +0 -226
  58. package/docs/background/README.md +0 -114
  59. package/docs/background/daemons.md +0 -276
  60. package/docs/background/derive.md +0 -179
  61. package/docs/cli/README.md +0 -379
  62. package/docs/concepts/ai-guide.md +0 -132
  63. package/docs/concepts/config.md +0 -416
  64. package/docs/concepts/decorators.md +0 -127
  65. package/docs/concepts/security.md +0 -108
  66. package/docs/concepts/tiers.md +0 -166
  67. package/docs/concepts/types.md +0 -216
  68. package/docs/database/README.md +0 -143
  69. package/docs/database/capacity.md +0 -350
  70. package/docs/database/counters.md +0 -174
  71. package/docs/database/documents.md +0 -277
  72. package/docs/database/events.md +0 -307
  73. package/docs/database/membership.md +0 -246
  74. package/docs/database/setup.md +0 -155
  75. package/docs/database/unique.md +0 -216
  76. package/docs/database/views.md +0 -246
  77. package/docs/frontend/README.md +0 -104
  78. package/docs/frontend/components.md +0 -286
  79. package/docs/frontend/data-fetching.md +0 -260
  80. package/docs/frontend/images.md +0 -148
  81. package/docs/frontend/metadata.md +0 -344
  82. package/docs/frontend/navigation.md +0 -296
  83. package/docs/frontend/rendering.md +0 -236
  84. package/docs/frontend/routing.md +0 -346
  85. package/docs/frontend/scripts.md +0 -118
  86. package/docs/frontend/search.md +0 -191
  87. package/docs/frontend/styling.md +0 -147
  88. package/docs/frontend/toil-global.md +0 -216
  89. package/docs/getting-started/README.md +0 -81
  90. package/docs/getting-started/create-project.md +0 -131
  91. package/docs/getting-started/deploy.md +0 -101
  92. package/docs/getting-started/first-app.md +0 -215
  93. package/docs/getting-started/installation.md +0 -124
  94. package/docs/getting-started/migrating.md +0 -125
  95. package/docs/getting-started/project-structure.md +0 -163
  96. package/docs/introduction/README.md +0 -66
  97. package/docs/introduction/design-principles.md +0 -81
  98. package/docs/introduction/distributed.md +0 -91
  99. package/docs/introduction/how-it-works.md +0 -121
  100. package/docs/introduction/hyperscale.md +0 -70
  101. package/docs/introduction/modern-stack.md +0 -85
  102. package/docs/introduction/vs-other-frameworks.md +0 -64
  103. package/docs/introduction/why-toil.md +0 -99
  104. package/docs/llms.txt +0 -95
  105. package/docs/realtime/README.md +0 -102
  106. package/docs/realtime/channels.md +0 -211
  107. package/docs/realtime/streams.md +0 -369
  108. package/docs/services/README.md +0 -60
  109. package/docs/services/analytics.md +0 -279
  110. package/docs/services/caching.md +0 -175
  111. package/docs/services/cookies.md +0 -235
  112. package/docs/services/crypto.md +0 -209
  113. package/docs/services/email.md +0 -289
  114. package/docs/services/environment.md +0 -141
  115. package/docs/services/ratelimit.md +0 -174
  116. package/docs/services/time.md +0 -85
  117. package/eslint.config.js +0 -57
  118. package/examples/basic/client/routes/docs/[...slug].tsx +0 -12
  119. package/presets/eslint.js +0 -88
  120. package/src/backend/index.ts +0 -204
  121. package/src/cli/configure.ts +0 -363
  122. package/src/cli/create.ts +0 -924
  123. package/src/cli/db.ts +0 -158
  124. package/src/cli/diagnostics.ts +0 -665
  125. package/src/cli/doctor.ts +0 -982
  126. package/src/cli/features.ts +0 -170
  127. package/src/cli/index.ts +0 -326
  128. package/src/cli/notify.ts +0 -120
  129. package/src/cli/proc.ts +0 -56
  130. package/src/cli/ui.ts +0 -169
  131. package/src/cli/update.ts +0 -248
  132. package/src/cli/updates.ts +0 -92
  133. package/src/cli/validate.ts +0 -31
  134. package/src/cli/version-check.ts +0 -125
  135. package/src/client/auth.ts +0 -869
  136. package/src/client/channel/channel.ts +0 -146
  137. package/src/client/components/Form.tsx +0 -65
  138. package/src/client/components/Image.tsx +0 -160
  139. package/src/client/components/Script.tsx +0 -113
  140. package/src/client/components/Slot.tsx +0 -21
  141. package/src/client/dev/devtools.tsx +0 -1089
  142. package/src/client/dev/error-overlay.tsx +0 -225
  143. package/src/client/errors.ts +0 -11
  144. package/src/client/head/head.ts +0 -182
  145. package/src/client/head/metadata.ts +0 -110
  146. package/src/client/index.ts +0 -130
  147. package/src/client/navigation/Link.tsx +0 -108
  148. package/src/client/navigation/NavLink.tsx +0 -86
  149. package/src/client/navigation/navigation.ts +0 -235
  150. package/src/client/navigation/prefetch.ts +0 -169
  151. package/src/client/navigation/scroll.ts +0 -53
  152. package/src/client/routing/Router.tsx +0 -216
  153. package/src/client/routing/action.ts +0 -122
  154. package/src/client/routing/error-boundary.tsx +0 -43
  155. package/src/client/routing/hooks.ts +0 -140
  156. package/src/client/routing/lazy.ts +0 -77
  157. package/src/client/routing/loader.ts +0 -381
  158. package/src/client/routing/match.ts +0 -47
  159. package/src/client/routing/mount.tsx +0 -90
  160. package/src/client/routing/params-context.ts +0 -10
  161. package/src/client/routing/slot-context.ts +0 -7
  162. package/src/client/rpc.ts +0 -97
  163. package/src/client/search/search.ts +0 -189
  164. package/src/client/search/use-page-search.ts +0 -73
  165. package/src/client/ssr/markers.tsx +0 -172
  166. package/src/client/stream/client.ts +0 -237
  167. package/src/client/types.ts +0 -73
  168. package/src/compiler/auth-emails.ts +0 -312
  169. package/src/compiler/config.ts +0 -369
  170. package/src/compiler/docs.ts +0 -127
  171. package/src/compiler/email-preview.ts +0 -314
  172. package/src/compiler/emails.ts +0 -404
  173. package/src/compiler/fonts.ts +0 -89
  174. package/src/compiler/generate.ts +0 -471
  175. package/src/compiler/image-blur.ts +0 -42
  176. package/src/compiler/image-report.ts +0 -88
  177. package/src/compiler/index.ts +0 -1362
  178. package/src/compiler/pages.ts +0 -49
  179. package/src/compiler/plugin.ts +0 -391
  180. package/src/compiler/prerender.ts +0 -263
  181. package/src/compiler/routes.ts +0 -170
  182. package/src/compiler/seo.ts +0 -415
  183. package/src/compiler/sri.ts +0 -144
  184. package/src/compiler/ssg.ts +0 -177
  185. package/src/compiler/ssr-codegen.ts +0 -85
  186. package/src/compiler/template-build.ts +0 -676
  187. package/src/compiler/template.ts +0 -262
  188. package/src/compiler/toil-docs.generated.ts +0 -77
  189. package/src/compiler/vite.ts +0 -245
  190. package/src/devserver/analytics/index.ts +0 -317
  191. package/src/devserver/config/dotenv.ts +0 -102
  192. package/src/devserver/config/env.ts +0 -30
  193. package/src/devserver/config/ratelimit.ts +0 -123
  194. package/src/devserver/daemon/catalog.ts +0 -120
  195. package/src/devserver/daemon/cron.ts +0 -87
  196. package/src/devserver/daemon/host.ts +0 -134
  197. package/src/devserver/daemon/index.ts +0 -369
  198. package/src/devserver/daemon/runtime.ts +0 -48
  199. package/src/devserver/db/catalog.ts +0 -109
  200. package/src/devserver/db/database.ts +0 -1834
  201. package/src/devserver/db/derives.ts +0 -121
  202. package/src/devserver/db/index.ts +0 -20
  203. package/src/devserver/db/routeKinds.ts +0 -191
  204. package/src/devserver/db/types.ts +0 -168
  205. package/src/devserver/email/caps.ts +0 -0
  206. package/src/devserver/email/config.ts +0 -141
  207. package/src/devserver/email/console.ts +0 -348
  208. package/src/devserver/email/index.ts +0 -155
  209. package/src/devserver/email/providers.ts +0 -131
  210. package/src/devserver/email/status.ts +0 -23
  211. package/src/devserver/email/validate.ts +0 -37
  212. package/src/devserver/email/wire.ts +0 -55
  213. package/src/devserver/http/cache.ts +0 -0
  214. package/src/devserver/http/envelope.ts +0 -157
  215. package/src/devserver/http/proxy.ts +0 -172
  216. package/src/devserver/http/runtime.ts +0 -387
  217. package/src/devserver/index.ts +0 -43
  218. package/src/devserver/production-ipc.ts +0 -63
  219. package/src/devserver/production-worker.ts +0 -83
  220. package/src/devserver/production.ts +0 -718
  221. package/src/devserver/runtime/crypto.ts +0 -587
  222. package/src/devserver/runtime/host.ts +0 -427
  223. package/src/devserver/runtime/module.ts +0 -442
  224. package/src/devserver/server.ts +0 -251
  225. package/src/devserver/ssr.ts +0 -208
  226. package/src/devserver/stream/catalog.ts +0 -106
  227. package/src/devserver/stream/host.ts +0 -42
  228. package/src/devserver/stream/index.ts +0 -301
  229. package/src/devserver/stream/manager.ts +0 -167
  230. package/src/devserver/stream/router.ts +0 -101
  231. package/src/devserver/stream/wire.ts +0 -58
  232. package/src/devserver/stream/ws.ts +0 -78
  233. package/src/devserver/wasm/sections.ts +0 -59
  234. package/src/devserver/wasm/surface.ts +0 -115
  235. package/src/io/FastMap.ts +0 -151
  236. package/src/io/FastSet.ts +0 -113
  237. package/src/io/codec.ts +0 -362
  238. package/src/io/index.ts +0 -10
  239. package/src/io/lengths.ts +0 -14
  240. package/src/io/types.ts +0 -19
  241. package/src/logger/index.ts +0 -22
  242. package/src/shared/index.ts +0 -46
  243. package/test/analytics-dev.test.ts +0 -113
  244. package/test/assembly/aspect-shim.ts +0 -33
  245. package/test/assembly/cookie.spec.ts +0 -303
  246. package/test/assembly/example.spec.ts +0 -22
  247. package/test/assembly/ssr.spec.ts +0 -95
  248. package/test/assembly.test.ts +0 -59
  249. package/test/auth-emails.test.ts +0 -118
  250. package/test/built-ssr.test.ts +0 -148
  251. package/test/channel.test.ts +0 -21
  252. package/test/config-host-port.test.ts +0 -59
  253. package/test/configure.test.ts +0 -94
  254. package/test/daemon-build.test.ts +0 -278
  255. package/test/daemon-catalog.test.ts +0 -349
  256. package/test/daemon-emulation.test.ts +0 -256
  257. package/test/db.test.ts +0 -0
  258. package/test/devserver-database.test.ts +0 -1102
  259. package/test/devserver-email.test.ts +0 -306
  260. package/test/devserver-pqauth.test.ts +0 -93
  261. package/test/devserver-secrets.test.ts +0 -63
  262. package/test/devserver.test.ts +0 -259
  263. package/test/doctor.test.ts +0 -298
  264. package/test/dom/Image.test.tsx +0 -144
  265. package/test/dom/Link.test.tsx +0 -67
  266. package/test/dom/NavLink.test.tsx +0 -37
  267. package/test/dom/Script.test.tsx +0 -48
  268. package/test/dom/action.test.tsx +0 -146
  269. package/test/dom/error-overlay.test.tsx +0 -44
  270. package/test/dom/loader.test.tsx +0 -121
  271. package/test/dom/navigation.test.ts +0 -59
  272. package/test/dom/revalidate.test.tsx +0 -38
  273. package/test/dom/route-head.test.tsx +0 -56
  274. package/test/dom/router-loading.test.tsx +0 -44
  275. package/test/dom/scroll.test.ts +0 -56
  276. package/test/dom/slot.test.tsx +0 -131
  277. package/test/dom/use-metadata.test.tsx +0 -58
  278. package/test/dom/view-transitions.test.tsx +0 -53
  279. package/test/dynamic-routing.test.ts +0 -132
  280. package/test/email-console.test.ts +0 -86
  281. package/test/email-preview.test.ts +0 -73
  282. package/test/emails.test.ts +0 -58
  283. package/test/errors.test.ts +0 -21
  284. package/test/example-guestbook.test.ts +0 -123
  285. package/test/features.test.ts +0 -149
  286. package/test/fixtures/bignum-wire/spec.ts +0 -25
  287. package/test/fixtures/daemon-app.ts +0 -65
  288. package/test/fixtures/stream-echo.ts +0 -26
  289. package/test/fixtures/stream-gate.ts +0 -28
  290. package/test/fixtures/stream-trap.ts +0 -18
  291. package/test/fixtures/stream-typed.ts +0 -41
  292. package/test/fonts.test.ts +0 -28
  293. package/test/global-setup.ts +0 -17
  294. package/test/head.test.ts +0 -42
  295. package/test/image-blur.test.ts +0 -50
  296. package/test/io.test.ts +0 -117
  297. package/test/metadata.test.ts +0 -40
  298. package/test/navlink.test.ts +0 -28
  299. package/test/pages.test.ts +0 -105
  300. package/test/placeholder.test.ts +0 -9
  301. package/test/pqauth-e2e.test.ts +0 -216
  302. package/test/pqauth-email.test.ts +0 -801
  303. package/test/prerender.test.ts +0 -116
  304. package/test/prettier-plugin.test.ts +0 -46
  305. package/test/routes.test.ts +0 -76
  306. package/test/rpc-bignum-wire.test.ts +0 -164
  307. package/test/rpc-dispatch.test.ts +0 -132
  308. package/test/rpc-kinds.test.ts +0 -18
  309. package/test/rpc.test.ts +0 -66
  310. package/test/search.test.ts +0 -114
  311. package/test/seo.test.ts +0 -175
  312. package/test/slot-layouts.test.ts +0 -69
  313. package/test/sri.test.ts +0 -129
  314. package/test/ssg.test.ts +0 -36
  315. package/test/ssr-hydration.test.tsx +0 -122
  316. package/test/ssr-render.test.ts +0 -197
  317. package/test/ssr-template.test.tsx +0 -482
  318. package/test/stream-emulation.test.ts +0 -479
  319. package/test/ui.test.ts +0 -40
  320. package/test/update.test.ts +0 -58
  321. package/test/validate.test.ts +0 -42
  322. package/test/version-check.test.ts +0 -130
  323. package/tests/data-parity/generated-parity.ts +0 -99
  324. package/tests/data-parity/parity.ts +0 -80
  325. 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
- });