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,116 +0,0 @@
1
- import fs from 'node:fs';
2
- import os from 'node:os';
3
- import path from 'node:path';
4
-
5
- import ts from 'typescript';
6
- import { afterEach, describe, expect, it, vi } from 'vitest';
7
-
8
- import {
9
- extractStaticMetadata,
10
- loadTypeScript,
11
- loadTypeScriptSync,
12
- } from '../src/compiler/prerender';
13
-
14
- const written: string[] = [];
15
- function tmp(source: string): string {
16
- const file = path.join(
17
- os.tmpdir(),
18
- `toil-prerender-${String(written.length)}-${process.pid}.tsx`,
19
- );
20
- fs.writeFileSync(file, source);
21
- written.push(file);
22
- return file;
23
- }
24
- afterEach(() => {
25
- for (const f of written.splice(0)) fs.rmSync(f, { force: true });
26
- });
27
-
28
- describe('extractStaticMetadata', () => {
29
- it('extracts a static metadata object literal (nested objects/arrays)', () => {
30
- const file = tmp(
31
- `export const metadata = { title: 'X', keywords: ['a', 'b'], openGraph: { type: 'website' } };\n` +
32
- `export default function P() { return null; }\n`,
33
- );
34
- expect(extractStaticMetadata(ts, file)).toEqual({
35
- title: 'X',
36
- keywords: ['a', 'b'],
37
- openGraph: { type: 'website' },
38
- });
39
- });
40
-
41
- it('returns null when there is no static metadata export', () => {
42
- expect(
43
- extractStaticMetadata(ts, tmp(`export default function P() { return null; }\n`)),
44
- ).toBeNull();
45
- // generateMetadata (a function) is not a static object literal → not extracted.
46
- expect(
47
- extractStaticMetadata(
48
- ts,
49
- tmp(`export const generateMetadata = () => ({ title: 'X' });\n`),
50
- ),
51
- ).toBeNull();
52
- });
53
-
54
- it('skips computed/non-literal properties but keeps the static ones', () => {
55
- const file = tmp(`const x = foo();\nexport const metadata = { title: 'X', dyn: x };\n`);
56
- expect(extractStaticMetadata(ts, file)).toEqual({ title: 'X' });
57
- });
58
- });
59
-
60
- /**
61
- * Builds a throwaway project root whose resolvable `typescript` is `main`-ed at a module exporting
62
- * only `version` — the shape TypeScript 7 ships, having moved the compiler API to
63
- * `typescript/unstable/*`. Resolution succeeds, so a truthy check can't tell it apart from a usable
64
- * compiler; only probing the API can.
65
- */
66
- function rootWithApiLessTypeScript(): string {
67
- const root = fs.mkdtempSync(path.join(os.tmpdir(), `toil-ts7-${process.pid}-`));
68
- dirs.push(root);
69
- const pkgDir = path.join(root, 'node_modules', 'typescript');
70
- fs.mkdirSync(pkgDir, { recursive: true });
71
- fs.writeFileSync(path.join(root, 'package.json'), '{"name":"fixture"}');
72
- fs.writeFileSync(
73
- path.join(pkgDir, 'package.json'),
74
- '{"name":"typescript","version":"7.0.2","main":"./version.cjs"}',
75
- );
76
- fs.writeFileSync(
77
- path.join(pkgDir, 'version.cjs'),
78
- `module.exports = { version: '7.0.2', versionMajorMinor: '7.0' };\n`,
79
- );
80
- return root;
81
- }
82
-
83
- const dirs: string[] = [];
84
- afterEach(() => {
85
- for (const d of dirs.splice(0)) fs.rmSync(d, { recursive: true, force: true });
86
- });
87
-
88
- describe('loadTypeScript', () => {
89
- it('returns null (not a half-built module) when typescript exposes no compiler API', async () => {
90
- const root = rootWithApiLessTypeScript();
91
- const warn = vi.spyOn(process.stderr, 'write').mockReturnValue(true);
92
- try {
93
- // Both must degrade rather than hand back `{ version }`, whose first `ts.ScriptTarget`
94
- // read would throw `Cannot read properties of undefined (reading 'Latest')` mid-build.
95
- expect(loadTypeScriptSync(root)).toBeNull();
96
- await expect(loadTypeScript(root)).resolves.toBeNull();
97
- } finally {
98
- warn.mockRestore();
99
- }
100
- });
101
-
102
- it('returns null when typescript is not installed at all', async () => {
103
- const root = fs.mkdtempSync(path.join(os.tmpdir(), `toil-nots-${process.pid}-`));
104
- dirs.push(root);
105
- fs.writeFileSync(path.join(root, 'package.json'), '{"name":"fixture"}');
106
- expect(loadTypeScriptSync(root)).toBeNull();
107
- await expect(loadTypeScript(root)).resolves.toBeNull();
108
- });
109
-
110
- it('returns the compiler API when a usable typescript is installed', async () => {
111
- // This repo's own root resolves its devDependency typescript, which has the classic API.
112
- const root = path.resolve(__dirname, '..');
113
- expect(loadTypeScriptSync(root)?.createSourceFile).toBeTypeOf('function');
114
- expect((await loadTypeScript(root))?.ScriptTarget.Latest).toBeTypeOf('number');
115
- });
116
- });
@@ -1,46 +0,0 @@
1
- import prettier from 'prettier';
2
- import { describe, expect, it } from 'vitest';
3
-
4
- import * as plugin from '../presets/prettier-plugin.js';
5
-
6
- function fmt(src: string): Promise<string> {
7
- return prettier.format(src, {
8
- parser: 'typescript',
9
- plugins: [plugin],
10
- tabWidth: 4,
11
- singleQuote: true,
12
- semi: true,
13
- });
14
- }
15
-
16
- // The plugin lets prettier format toilscript server code, whose native decorators on free
17
- // functions (@main, @remote function) are not valid JS/TS grammar (so stock prettier throws).
18
- describe('toiljs prettier-plugin', () => {
19
- it('formats a free @remote function and keeps the decorator', async () => {
20
- const out = await fmt('@remote\nfunction ping(n:i32):i32{return n+1}\n');
21
- expect(out).toContain('@remote');
22
- expect(out).toContain('function ping(n: i32): i32 {');
23
- expect(out).not.toContain('toil-decorator'); // marker fully restored
24
- });
25
-
26
- it('formats @main', async () => {
27
- const out = await fmt('@main\nfunction run():i32{return 42}\n');
28
- expect(out).toMatch(/@main\nfunction run\(\): i32 \{/);
29
- });
30
-
31
- it('handles @remote export function', async () => {
32
- const out = await fmt('@remote\nexport function pong():void{}\n');
33
- expect(out).toContain('@remote\nexport function pong(): void {}');
34
- });
35
-
36
- it('leaves class/method decorators untouched', async () => {
37
- const out = await fmt('@data\nclass A{ x:i32=0 }\n');
38
- expect(out).toContain('@data');
39
- expect(out).toContain('class A {');
40
- });
41
-
42
- it('is idempotent', async () => {
43
- const once = await fmt('@remote\nfunction f():void{}\n@main\nfunction g():void{}\n');
44
- expect(await fmt(once)).toBe(once);
45
- });
46
- });
@@ -1,76 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
-
3
- import { matchRoute } from '../src/client/routing/match';
4
- import { filePathToRoute, interceptTarget } from '../src/compiler/routes';
5
-
6
- describe('filePathToRoute', () => {
7
- it('maps index, static, nested, and dynamic files to patterns', () => {
8
- expect(filePathToRoute('index.tsx')).toBe('/');
9
- expect(filePathToRoute('about.tsx')).toBe('/about');
10
- expect(filePathToRoute('blog/index.tsx')).toBe('/blog');
11
- expect(filePathToRoute('blog/[id].tsx')).toBe('/blog/:id');
12
- expect(filePathToRoute('docs/guide/intro.jsx')).toBe('/docs/guide/intro');
13
- expect(filePathToRoute('docs/[...slug].tsx')).toBe('/docs/*slug');
14
- });
15
-
16
- it('maps optional catch-all and strips route groups', () => {
17
- expect(filePathToRoute('docs/[[...slug]].tsx')).toBe('/docs/**slug');
18
- expect(filePathToRoute('[[...slug]].tsx')).toBe('/**slug');
19
- expect(filePathToRoute('(marketing)/about.tsx')).toBe('/about');
20
- expect(filePathToRoute('(shop)/index.tsx')).toBe('/');
21
- expect(filePathToRoute('(a)/(b)/deep.tsx')).toBe('/deep');
22
- });
23
-
24
- it('strips parallel-slot (@slot) segments from the URL', () => {
25
- expect(filePathToRoute('@modal/photo/[id].tsx')).toBe('/photo/:id');
26
- expect(filePathToRoute('@sidebar/index.tsx')).toBe('/');
27
- expect(filePathToRoute('dashboard/@chart/views.tsx')).toBe('/dashboard/views');
28
- });
29
- });
30
-
31
- describe('interceptTarget', () => {
32
- it('resolves (.)/(..)/(...) marker targets', () => {
33
- expect(interceptTarget('@modal/(.)photo/[id].tsx')).toBe('/photo/:id');
34
- expect(interceptTarget('feed/@modal/(..)photo/[id].tsx')).toBe('/photo/:id');
35
- expect(interceptTarget('a/b/@m/(...)login.tsx')).toBe('/login');
36
- });
37
-
38
- it('returns null for routes with no interception marker', () => {
39
- expect(interceptTarget('photo/[id].tsx')).toBeNull();
40
- expect(interceptTarget('@modal/settings.tsx')).toBeNull();
41
- });
42
- });
43
-
44
- describe('matchRoute', () => {
45
- it('matches static routes', () => {
46
- expect(matchRoute('/', '/')).toEqual({});
47
- expect(matchRoute('/about', '/about')).toEqual({});
48
- });
49
-
50
- it('rejects non-matches', () => {
51
- expect(matchRoute('/about', '/x')).toBeNull();
52
- expect(matchRoute('/blog/:id', '/blog')).toBeNull();
53
- expect(matchRoute('/', '/about')).toBeNull();
54
- });
55
-
56
- it('extracts dynamic params', () => {
57
- expect(matchRoute('/blog/:id', '/blog/42')).toEqual({ id: '42' });
58
- expect(matchRoute('/u/:user/p/:post', '/u/ann/p/7')).toEqual({ user: 'ann', post: '7' });
59
- expect(matchRoute('/blog/:id', '/blog/a%20b')).toEqual({ id: 'a b' });
60
- });
61
-
62
- it('captures the tail with catch-all routes', () => {
63
- expect(matchRoute('/docs/*slug', '/docs/a/b/c')).toEqual({ slug: 'a/b/c' });
64
- expect(matchRoute('/docs/*slug', '/docs/intro')).toEqual({ slug: 'intro' });
65
- expect(matchRoute('/files/*path', '/files/a%20b/c')).toEqual({ path: 'a b/c' });
66
- // catch-all needs at least one trailing segment
67
- expect(matchRoute('/docs/*slug', '/docs')).toBeNull();
68
- });
69
-
70
- it('matches optional catch-all with zero or more segments', () => {
71
- expect(matchRoute('/docs/**slug', '/docs')).toEqual({ slug: '' });
72
- expect(matchRoute('/docs/**slug', '/docs/a/b')).toEqual({ slug: 'a/b' });
73
- expect(matchRoute('/**slug', '/')).toEqual({ slug: '' });
74
- expect(matchRoute('/**slug', '/x/y')).toEqual({ slug: 'x/y' });
75
- });
76
- });
@@ -1,164 +0,0 @@
1
- /**
2
- * Regression test for the bignum JSON wire format. 64-bit-and-up integers
3
- * (u64/i64/u128/i128/u256/i256) must cross JSON as DECIMAL STRINGS, not number tokens
4
- * or limb arrays: JSON numbers ride through a browser client's JSON.parse as IEEE
5
- * doubles, which silently corrupt any integer past 2^53.
6
- *
7
- * Compiles test/fixtures/bignum-wire/spec.ts with the installed toilscript (so it
8
- * exercises the published compiler + generated client, not a hand-written stub), then
9
- * imports the generated TS client and asserts the wire shape both directions, including
10
- * values far above 2^53.
11
- */
12
- import { spawnSync } from 'node:child_process';
13
- import fs from 'node:fs';
14
- import os from 'node:os';
15
- import path from 'node:path';
16
- import { fileURLToPath, pathToFileURL } from 'node:url';
17
-
18
- import { beforeAll, describe, expect, it } from 'vitest';
19
-
20
- const here = path.dirname(fileURLToPath(import.meta.url));
21
- const spec = path.join(here, 'fixtures', 'bignum-wire', 'spec.ts');
22
- // The generated module imports DataWriter/DataReader from this specifier.
23
- const codec = path.join(here, '..', 'src', 'io', 'codec.ts');
24
-
25
- /** Resolves the installed toilscript CLI entry (no PATH / .bin assumptions). */
26
- function toilscriptBin(): string {
27
- if (process.env.TOILSCRIPT_BIN) return process.env.TOILSCRIPT_BIN;
28
- const sibling = path.resolve(here, '..', '..', 'toilscript', 'bin', 'toilscript.js');
29
- if (fs.existsSync(sibling)) return sibling;
30
- const pkgPath = require.resolve('toilscript/package.json');
31
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')) as { bin?: Record<string, string> };
32
- const binRel = pkg.bin?.toilscript;
33
- if (!binRel) throw new Error('toilscript declares no bin');
34
- return path.join(path.dirname(pkgPath), binRel);
35
- }
36
-
37
- interface Wallet {
38
- u: bigint;
39
- i: bigint;
40
- a: bigint;
41
- b: bigint;
42
- c: bigint;
43
- d: bigint;
44
- label: string;
45
- toJSONValue(): Record<string, unknown>;
46
- }
47
- interface WalletStatic {
48
- new (): Wallet;
49
- fromJSONValue(v: unknown): Wallet;
50
- }
51
- interface AccountStatic {
52
- new (): { main: Wallet; ids: bigint[]; toJSONValue(): Record<string, unknown> };
53
- fromJSONValue(v: unknown): { main: Wallet; ids: bigint[] };
54
- }
55
-
56
- let Wallet: WalletStatic;
57
- let Account: AccountStatic;
58
- let tmp: string;
59
-
60
- beforeAll(async () => {
61
- tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'bignum-wire-'));
62
- fs.writeFileSync(path.join(tmp, 'package.json'), '{ "type": "module" }\n');
63
- // vitest transforms the imported .ts through Vite/oxc, which walks up for a tsconfig.
64
- fs.writeFileSync(
65
- path.join(tmp, 'tsconfig.json'),
66
- JSON.stringify({ compilerOptions: { target: 'esnext', module: 'esnext' } }),
67
- );
68
- const mod = path.join(tmp, 'server.ts');
69
- const wasm = path.join(tmp, 'spec.wasm');
70
- const res = spawnSync(
71
- process.execPath,
72
- [
73
- toilscriptBin(),
74
- spec,
75
- '-o',
76
- wasm,
77
- '--runtime',
78
- 'stub',
79
- '--initialMemory',
80
- '32',
81
- '--rpcModule',
82
- mod,
83
- '--rpcRuntime',
84
- codec,
85
- ],
86
- { encoding: 'utf8' },
87
- );
88
- if (res.status !== 0) throw new Error('toilscript compile failed:\n' + res.stderr);
89
- const generatedSource = fs.readFileSync(mod, 'utf8');
90
- const removedHelper = ['__toil', 'Unlimb'].join('');
91
- expect(generatedSource).toContain('__toilBigInt');
92
- expect(generatedSource).not.toContain(removedHelper);
93
- const gen = (await import(pathToFileURL(mod).href)) as {
94
- Wallet: WalletStatic;
95
- Account: AccountStatic;
96
- };
97
- Wallet = gen.Wallet;
98
- Account = gen.Account;
99
- });
100
-
101
- // A few representative bignum types; `huge` is well past Number.MAX_SAFE_INTEGER.
102
- const huge = '123456789012345678901234567890';
103
- const u128Max = '340282366920938463463374607431768211455';
104
- const i64Min = '-9223372036854775808';
105
-
106
- describe('generated client bignum JSON wire format', () => {
107
- it('serializes every 64-bit-and-up integer as a decimal string', () => {
108
- const w = new Wallet();
109
- w.u = BigInt(huge.slice(0, 19)); // fits u64
110
- w.i = BigInt(i64Min);
111
- w.a = BigInt(u128Max);
112
- w.b = -123n;
113
- w.c = BigInt(huge);
114
- w.d = -1n;
115
- w.label = 'x';
116
- const json = w.toJSONValue();
117
- // Each bignum field is a string, never a number or an array.
118
- for (const k of ['u', 'i', 'a', 'b', 'c', 'd'] as const) {
119
- expect(typeof json[k], `field ${k}`).toBe('string');
120
- }
121
- expect(json.a).toBe(u128Max);
122
- expect(json.c).toBe(huge);
123
- expect(json.i).toBe(i64Min);
124
- expect(json.label).toBe('x');
125
- });
126
-
127
- it('revives a decimal string past 2^53 into an exact bigint', () => {
128
- const w = Wallet.fromJSONValue({
129
- u: '0',
130
- i: i64Min,
131
- a: u128Max,
132
- b: '-123',
133
- c: huge,
134
- d: '-1',
135
- label: 'y',
136
- });
137
- expect(w.c).toBe(BigInt(huge));
138
- expect(w.a).toBe(BigInt(u128Max));
139
- expect(w.i).toBe(BigInt(i64Min));
140
- expect(w.d).toBe(-1n);
141
- });
142
-
143
- it('round-trips a value through send then revive without loss', () => {
144
- const w = new Wallet();
145
- w.c = BigInt(huge);
146
- w.d = BigInt('-' + huge);
147
- const back = Wallet.fromJSONValue(JSON.parse(JSON.stringify(w.toJSONValue())));
148
- expect(back.c).toBe(BigInt(huge));
149
- expect(back.d).toBe(BigInt('-' + huge));
150
- });
151
-
152
- it('recurses into nested @data and arrays of bignums', () => {
153
- const a = new Account();
154
- a.main.c = BigInt(huge);
155
- a.ids = [1n, BigInt(huge)];
156
- const json = a.toJSONValue();
157
- const main = json.main as Record<string, unknown>;
158
- expect(main.c).toBe(huge);
159
- expect(json.ids).toEqual(['1', huge]);
160
- const back = Account.fromJSONValue(JSON.parse(JSON.stringify(json)));
161
- expect(back.main.c).toBe(BigInt(huge));
162
- expect(back.ids).toEqual([1n, BigInt(huge)]);
163
- });
164
- });
@@ -1,132 +0,0 @@
1
- /**
2
- * @service / @remote RPC: a real round trip into the example project's toilscript-compiled server
3
- * wasm. A `POST /__toil_rpc` carrying the FNV method id in the `dacely-rpc` header is dispatched by the
4
- * runtime `Rpc` registry (compiler-injected `__rpcDispatch` + `Rpc.register`) to the @remote method,
5
- * and the @data/scalar result comes back encoded with the same DataWriter codec the client decodes.
6
- */
7
- import fs from 'node:fs';
8
- import path from 'node:path';
9
- import { fileURLToPath } from 'node:url';
10
-
11
- import { describe, expect, it } from 'vitest';
12
-
13
- import { WasmServerModule } from '../src/devserver/index.js';
14
-
15
- const EXAMPLE_WASM = path.resolve(
16
- path.dirname(fileURLToPath(import.meta.url)),
17
- '../examples/basic/build/server/release.wasm',
18
- );
19
-
20
- /** FNV-1a (the toilscript `dataTypeId`); the client + the parser injection must agree on this. */
21
- function fnv1a(s: string): number {
22
- let h = 0x811c9dc5;
23
- for (let i = 0; i < s.length; i++) h = Math.imul(h ^ s.charCodeAt(i), 0x01000193) >>> 0;
24
- return h >>> 0;
25
- }
26
-
27
- describe.skipIf(!fs.existsSync(EXAMPLE_WASM))('@service RPC dispatch', () => {
28
- const load = (): WasmServerModule => {
29
- const m = new WasmServerModule(EXAMPLE_WASM);
30
- m.refresh();
31
- return m;
32
- };
33
-
34
- it('dispatches Server.stats.playerCount() over /__toil_rpc', () => {
35
- const id = fnv1a('Stats.playerCount');
36
- expect(id).toBe(118912982); // matches the generated __toilRpc client
37
- const r = load().dispatch({
38
- method: 'POST',
39
- path: '/__toil_rpc',
40
- headers: [['dacely-rpc', String(id)]],
41
- body: new Uint8Array(0),
42
- });
43
- expect(r.unhandled).toBe(false);
44
- expect(r.status).toBe(200);
45
- // The result is a DataWriter-encoded i32 (the seeded player count = 3).
46
- expect(r.body.length).toBe(4);
47
- const dv = new DataView(r.body.buffer, r.body.byteOffset, r.body.length);
48
- expect(dv.getInt32(0, true)).toBe(3);
49
- });
50
-
51
- it('dispatches a free Server.ping(n) over /__toil_rpc', () => {
52
- const id = fnv1a('ping');
53
- const body = new Uint8Array(4);
54
- new DataView(body.buffer).setInt32(0, 41, true); // arg n = 41 (DataWriter writeI32, LE)
55
- const r = load().dispatch({
56
- method: 'POST',
57
- path: '/__toil_rpc',
58
- headers: [['dacely-rpc', String(id)]],
59
- body,
60
- });
61
- expect(r.status).toBe(200);
62
- expect(r.body.length).toBe(4);
63
- const dv = new DataView(r.body.buffer, r.body.byteOffset, r.body.length);
64
- expect(dv.getInt32(0, true)).toBe(42); // ping(41) = 42
65
- });
66
-
67
- it('round-trips a Uint8Array[] arg + result (writeBytes/readBytes loop, the #5 fix)', () => {
68
- const id = fnv1a('echoParts');
69
- const parts = [Uint8Array.from([1, 2]), Uint8Array.from([3, 4, 5])];
70
- const buf: number[] = [];
71
- const pushU32 = (v: number) => buf.push(v & 255, (v >> 8) & 255, (v >> 16) & 255, (v >>> 24) & 255);
72
- pushU32(parts.length); // u32 count, then per part: u32 len + bytes (DataWriter.writeBytes)
73
- for (const p of parts) {
74
- pushU32(p.length);
75
- for (const b of p) buf.push(b);
76
- }
77
- const r = load().dispatch({
78
- method: 'POST',
79
- path: '/__toil_rpc',
80
- headers: [['dacely-rpc', String(id)]],
81
- body: Uint8Array.from(buf),
82
- });
83
- expect(r.status).toBe(200);
84
- const dv = new DataView(r.body.buffer, r.body.byteOffset, r.body.length);
85
- let off = 0;
86
- const count = dv.getUint32(off, true);
87
- off += 4;
88
- const out: number[][] = [];
89
- for (let i = 0; i < count; i++) {
90
- const len = dv.getUint32(off, true);
91
- off += 4;
92
- out.push([...r.body.subarray(off, off + len)]);
93
- off += len;
94
- }
95
- expect(out).toEqual([
96
- [1, 2],
97
- [3, 4, 5],
98
- ]);
99
- });
100
-
101
- it('rejects an @auth @remote with 401 when there is no session', () => {
102
- const id = fnv1a('Stats.secretCount');
103
- const r = load().dispatch({
104
- method: 'POST',
105
- path: '/__toil_rpc',
106
- headers: [['dacely-rpc', String(id)]],
107
- body: new Uint8Array(0),
108
- });
109
- expect(r.status).toBe(401);
110
- });
111
-
112
- it('returns 400 for an unknown method id', () => {
113
- const r = load().dispatch({
114
- method: 'POST',
115
- path: '/__toil_rpc',
116
- headers: [['dacely-rpc', '999999']],
117
- body: new Uint8Array(0),
118
- });
119
- expect(r.status).toBe(400);
120
- });
121
-
122
- it('does not intercept a non-RPC request (no dacely-rpc header)', () => {
123
- const r = load().dispatch({
124
- method: 'GET',
125
- path: '/json',
126
- headers: [['host', 'localhost:3000']],
127
- body: new Uint8Array(0),
128
- });
129
- expect(r.status).toBe(200);
130
- expect(Buffer.from(r.body).toString()).toBe('{"hello":"toiljs"}\n');
131
- });
132
- });
@@ -1,18 +0,0 @@
1
- /**
2
- * The dev-server RPC DB-kind resolution (audit #2): an `@action` @remote may write; a plain/read-only
3
- * @remote (absent from `toildb.rpc_kinds`) defaults to read-only Query, matching the edge host gate.
4
- */
5
- import { describe, expect, it } from 'vitest';
6
-
7
- import { rpcKindForId, type RpcKindEntry } from '../src/devserver/db/routeKinds.js';
8
- import { DbFunctionKind } from '../src/devserver/db/types.js';
9
-
10
- describe('rpcKindForId', () => {
11
- it('returns the @action kind for a listed id, and read-only Query for an absent one', () => {
12
- const methods: RpcKindEntry[] = [{ methodId: 42, kind: DbFunctionKind.Action }];
13
- expect(rpcKindForId(methods, 42)).toBe(DbFunctionKind.Action);
14
- // An id NOT in rpc_kinds (a plain/read-only @remote) defaults to Query - the safe default.
15
- expect(rpcKindForId(methods, 999)).toBe(DbFunctionKind.Query);
16
- expect(rpcKindForId([], 42)).toBe(DbFunctionKind.Query);
17
- });
18
- });
package/test/rpc.test.ts DELETED
@@ -1,66 +0,0 @@
1
- import { afterEach, describe, expect, it } from 'vitest';
2
-
3
- import { Server } from '../src/client/rpc';
4
-
5
- // `Server` is the runtime behind the generated typed surface. The RPC branch surfaces
6
- // `globalThis.__toilRpc` (attached by the generated `shared/server.ts`); when that client has
7
- // not loaded, the proxy throws a helpful "client has not loaded" error naming the path.
8
- describe('Server RPC stub (client not loaded)', () => {
9
- afterEach(() => {
10
- delete (globalThis as { __toilRpc?: unknown }).__toilRpc;
11
- });
12
-
13
- it('throws on a direct call, naming the path', () => {
14
- const s = Server as { ping: () => unknown };
15
- expect(() => s.ping()).toThrow(/Server\.ping\(\)/);
16
- });
17
-
18
- it('throws on a nested service.method call', () => {
19
- const s = Server as { accounts: { getUser: () => unknown } };
20
- expect(() => s.accounts.getUser()).toThrow(/Server\.accounts\.getUser\(\)/);
21
- expect(() => s.accounts.getUser()).toThrow(/has not loaded/);
22
- });
23
-
24
- it('surfaces the attached RPC client (service method + free remote) once loaded', async () => {
25
- const fake = { stats: { playerCount: async () => 3 }, ping: async (n: number) => n + 1 };
26
- (globalThis as { __toilRpc?: unknown }).__toilRpc = fake;
27
- const s = Server as {
28
- stats: { playerCount: () => Promise<number> };
29
- ping: (n: number) => Promise<number>;
30
- };
31
- expect(await s.stats.playerCount()).toBe(3);
32
- expect(await s.ping(41)).toBe(42);
33
- });
34
-
35
- it('is not thenable (so it is not mistaken for a promise)', () => {
36
- const s = Server as Record<string, unknown>;
37
- expect(s.then).toBeUndefined();
38
- });
39
-
40
- it('ignores symbol probes without throwing', () => {
41
- const s = Server as Record<PropertyKey, unknown>;
42
- expect(s[Symbol.iterator]).toBeUndefined();
43
- });
44
- });
45
-
46
- // `Server.REST` surfaces the working fetch client that the generated `shared/server.ts`
47
- // attaches to `globalThis.__toilRest` on import.
48
- describe('Server.REST surface', () => {
49
- afterEach(() => {
50
- delete (globalThis as { __toilRest?: unknown }).__toilRest;
51
- });
52
-
53
- it('returns the attached REST client when shared/server has loaded', () => {
54
- const fake = { todos: { getTodo: async () => 'ok' } };
55
- (globalThis as { __toilRest?: unknown }).__toilRest = fake;
56
- const s = Server as { REST: typeof fake };
57
- expect(s.REST).toBe(fake);
58
- expect(s.REST.todos.getTodo).toBeTypeOf('function');
59
- });
60
-
61
- it('throws a helpful "not loaded" error when the REST client is absent', () => {
62
- const s = Server as { REST: { todos: { getTodo: () => unknown } } };
63
- expect(() => s.REST.todos.getTodo()).toThrow(/Server\.REST\.todos\.getTodo\(\)/);
64
- expect(() => s.REST.todos.getTodo()).toThrow(/has not loaded/);
65
- });
66
- });