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,587 +0,0 @@
1
- /**
2
- * Dev-server mock of the `env.crypto.*` host functions, mirroring the
3
- * production edge (`toil-backend/src/wasm/host/import_functions/crypto`) and the
4
- * toilscript std ABI (`std/assembly/crypto/algorithms.ts`). Backed by Node's
5
- * `crypto`. The dev server intentionally skips the edge's metering, so these
6
- * charge nothing; results must still be byte-identical to the edge for the
7
- * common algorithms.
8
- *
9
- * Variable-length results use the same two-step pull as the edge: the op
10
- * returns the length and stashes the bytes; the guest then calls `take_result`.
11
- *
12
- * Dev-only limitations: raw-format import of asymmetric keys (EC/Ed25519/
13
- * X25519) returns the "unsupported" code (-3) here because Node can't import a
14
- * bare key without DER; use pkcs8/spki in the dev server. The production edge
15
- * supports raw too. These are catchable guest-side errors, never crashes.
16
- */
17
-
18
- import * as nodeCrypto from 'node:crypto';
19
-
20
- import { ml_dsa44 } from '@dacely/noble-post-quantum/ml-dsa.js';
21
- import { ml_kem768 } from '@dacely/noble-post-quantum/ml-kem.js';
22
- import { ristretto255_oprf } from '@noble/curves/ed25519.js';
23
-
24
- import type { MemoryRef } from './host.js';
25
-
26
- // --- ABI id tables (must match the std + Rust backend) ----------------------
27
- const ALG = {
28
- SHA1: 1,
29
- SHA256: 2,
30
- SHA384: 3,
31
- SHA512: 4,
32
- SHA3_256: 5,
33
- SHA3_384: 6,
34
- SHA3_512: 7,
35
- AES_GCM: 10,
36
- AES_CBC: 11,
37
- AES_CTR: 12,
38
- AES_KW: 13,
39
- HMAC: 20,
40
- ECDSA: 32,
41
- ED25519: 33,
42
- ECDH: 50,
43
- X25519: 51,
44
- HKDF: 52,
45
- PBKDF2: 53,
46
- } as const;
47
- const FMT = { RAW: 0, PKCS8: 1, SPKI: 2, JWK: 3 } as const;
48
-
49
- // Recoverable error codes (negative returns).
50
- const ERR_GENERIC = -1;
51
- const ERR_UNSUPPORTED = -3;
52
- const ERR_INVALID_PARAMS = -4;
53
- const ERR_OPERATION_FAILED = -5;
54
- const ERR_NOT_EXTRACTABLE = -7;
55
-
56
- const MAX_OUTPUT = 1024 * 1024;
57
-
58
- interface KeyEntry {
59
- /** Raw bytes for symmetric/MAC/KDF keys. */
60
- raw: Buffer | null;
61
- /** Node KeyObject for asymmetric keys. */
62
- keyObject: nodeCrypto.KeyObject | null;
63
- alg: number;
64
- hash: number;
65
- extractable: boolean;
66
- isPrivate: boolean;
67
- }
68
-
69
- export interface CryptoState {
70
- keys: Map<number, KeyEntry>;
71
- nextHandle: number;
72
- lastResult: Buffer | null;
73
- }
74
-
75
- export function freshCryptoState(): CryptoState {
76
- return { keys: new Map(), nextHandle: 1, lastResult: null };
77
- }
78
-
79
- function memBuf(ref: MemoryRef): Buffer {
80
- if (!ref.memory) throw new Error('crypto host import called before memory was bound');
81
- return Buffer.from(ref.memory.buffer);
82
- }
83
-
84
- function readBytes(ref: MemoryRef, ptr: number, len: number): Buffer {
85
- const m = memBuf(ref);
86
- if (ptr < 0 || len < 0 || ptr + len > m.length)
87
- throw new Error(`crypto read out of bounds: ptr=${String(ptr)} len=${String(len)}`);
88
- // Copy out so later writes/grows can't alias the input.
89
- return Buffer.from(m.subarray(ptr, ptr + len));
90
- }
91
-
92
- function writeBytes(ref: MemoryRef, ptr: number, bytes: Buffer | Uint8Array): void {
93
- const m = memBuf(ref);
94
- if (ptr < 0 || ptr + bytes.length > m.length)
95
- throw new Error(
96
- `crypto write out of bounds: ptr=${String(ptr)} len=${String(bytes.length)}`,
97
- );
98
- m.set(bytes, ptr);
99
- }
100
-
101
- /** Little-endian reader over a packed params buffer (mirrors the Rust ParamReader). */
102
- class ParamReader {
103
- private pos = 0;
104
- constructor(private readonly buf: Buffer) {}
105
-
106
- readI32(): number {
107
- this.need(4);
108
- const v = this.buf.readInt32LE(this.pos);
109
- this.pos += 4;
110
- return v;
111
- }
112
-
113
- readU32(): number {
114
- this.need(4);
115
- const v = this.buf.readUInt32LE(this.pos);
116
- this.pos += 4;
117
- return v;
118
- }
119
-
120
- readBlob(): Buffer {
121
- const n = this.readU32();
122
- this.need(n);
123
- const s = Buffer.from(this.buf.subarray(this.pos, this.pos + n));
124
- this.pos += n;
125
- return s;
126
- }
127
-
128
- /** Bounds-check before a read so a malformed buffer throws a controlled
129
- * error (trap-equivalent, caught by the dispatcher) rather than a raw
130
- * Node RangeError. */
131
- private need(n: number): void {
132
- if (n < 0 || this.pos + n > this.buf.length)
133
- throw new Error('crypto: malformed params buffer (truncated)');
134
- }
135
- }
136
-
137
- function hashName(id: number): string {
138
- switch (id) {
139
- case ALG.SHA1:
140
- return 'sha1';
141
- case ALG.SHA256:
142
- return 'sha256';
143
- case ALG.SHA384:
144
- return 'sha384';
145
- case ALG.SHA512:
146
- return 'sha512';
147
- case ALG.SHA3_256:
148
- return 'sha3-256';
149
- case ALG.SHA3_384:
150
- return 'sha3-384';
151
- case ALG.SHA3_512:
152
- return 'sha3-512';
153
- default:
154
- throw new Error(`crypto: bad hash id ${String(id)}`);
155
- }
156
- }
157
-
158
- function stash(state: CryptoState, bytes: Buffer): number {
159
- state.lastResult = bytes;
160
- return bytes.length;
161
- }
162
-
163
- /**
164
- * Build the `env.crypto.*` import functions. `state.crypto` holds the per-
165
- * dispatch keystore + result scratch.
166
- */
167
- export function buildCryptoImports(
168
- ref: MemoryRef,
169
- cs: CryptoState,
170
- ): Record<string, (...args: number[]) => number | void> {
171
- return {
172
- 'crypto.fill_random': (outPtr: number, len: number): void => {
173
- if (len < 0 || len > MAX_OUTPUT) throw new Error('crypto.fill_random: bad length');
174
- writeBytes(ref, outPtr, nodeCrypto.randomBytes(len));
175
- },
176
-
177
- 'crypto.random_uuid': (outPtr: number): void => {
178
- writeBytes(ref, outPtr, nodeCrypto.randomBytes(16));
179
- },
180
-
181
- 'crypto.take_result': (outPtr: number, outLen: number): number => {
182
- const r = cs.lastResult;
183
- if (!r || r.length !== outLen) throw new Error('crypto.take_result: length mismatch');
184
- writeBytes(ref, outPtr, r);
185
- cs.lastResult = null;
186
- return r.length;
187
- },
188
-
189
- 'crypto.digest': (alg: number, dataPtr: number, dataLen: number): number => {
190
- const data = readBytes(ref, dataPtr, dataLen);
191
- try {
192
- return stash(cs, nodeCrypto.createHash(hashName(alg)).update(data).digest());
193
- } catch {
194
- return ERR_UNSUPPORTED;
195
- }
196
- },
197
-
198
- 'crypto.import_key': (
199
- format: number,
200
- keyPtr: number,
201
- keyLen: number,
202
- paramsPtr: number,
203
- paramsLen: number,
204
- extractable: number,
205
- _usages: number,
206
- ): number => {
207
- const key = readBytes(ref, keyPtr, keyLen);
208
- const pr = new ParamReader(readBytes(ref, paramsPtr, paramsLen));
209
- const alg = pr.readI32();
210
- const hash = pr.readI32();
211
- return importKey(cs, format, alg, hash, pr, key, extractable !== 0);
212
- },
213
-
214
- 'crypto.export_key': (format: number, handle: number): number => {
215
- const e = cs.keys.get(handle);
216
- if (!e) throw new Error('crypto.export_key: invalid handle');
217
- if (!e.extractable) return ERR_NOT_EXTRACTABLE;
218
- try {
219
- if (e.raw && format === FMT.RAW) return stash(cs, e.raw);
220
- if (e.keyObject) {
221
- if (format === FMT.PKCS8 && e.isPrivate)
222
- return stash(
223
- cs,
224
- e.keyObject.export({ format: 'der', type: 'pkcs8' }) as Buffer,
225
- );
226
- if (format === FMT.SPKI && !e.isPrivate)
227
- return stash(
228
- cs,
229
- e.keyObject.export({ format: 'der', type: 'spki' }) as Buffer,
230
- );
231
- }
232
- return ERR_UNSUPPORTED;
233
- } catch {
234
- return ERR_OPERATION_FAILED;
235
- }
236
- },
237
-
238
- 'crypto.encrypt': (h: number, pp: number, pl: number, dp: number, dl: number): number =>
239
- aesOp(cs, ref, true, h, pp, pl, dp, dl),
240
- 'crypto.decrypt': (h: number, pp: number, pl: number, dp: number, dl: number): number =>
241
- aesOp(cs, ref, false, h, pp, pl, dp, dl),
242
-
243
- 'crypto.sign': (h: number, pp: number, pl: number, dp: number, dl: number): number =>
244
- signOp(cs, ref, h, pp, pl, dp, dl),
245
-
246
- 'crypto.verify': (
247
- h: number,
248
- pp: number,
249
- pl: number,
250
- sp: number,
251
- sl: number,
252
- dp: number,
253
- dl: number,
254
- ): number => verifyOp(cs, ref, h, pp, pl, sp, sl, dp, dl),
255
-
256
- 'crypto.derive_bits': (h: number, pp: number, pl: number, lengthBits: number): number =>
257
- deriveBitsOp(cs, ref, h, pp, pl, lengthBits),
258
-
259
- // ML-DSA-44 (FIPS 204) verify for the auth primitive. Mirrors the edge
260
- // host (`mldsa_verify_import.rs`): same size asserts, 1/0/neg result.
261
- // Backed by the same noble lib the client signs with, so dev == prod.
262
- 'crypto.mldsa_verify': (
263
- pkPtr: number,
264
- pkLen: number,
265
- msgPtr: number,
266
- msgLen: number,
267
- sigPtr: number,
268
- sigLen: number,
269
- ctxPtr: number,
270
- ctxLen: number,
271
- ): number => {
272
- if (pkLen !== 1312 || sigLen !== 2420 || ctxLen > 255) return -4;
273
- try {
274
- const pk = new Uint8Array(readBytes(ref, pkPtr, pkLen));
275
- const msg = new Uint8Array(readBytes(ref, msgPtr, msgLen));
276
- const sig = new Uint8Array(readBytes(ref, sigPtr, sigLen));
277
- const ctx = new Uint8Array(readBytes(ref, ctxPtr, ctxLen));
278
- return ml_dsa44.verify(sig, msg, pk, { context: ctx }) ? 1 : 0;
279
- } catch {
280
- return -1;
281
- }
282
- },
283
-
284
- // ML-KEM-768 (FIPS 203) decapsulation for the mutual-auth + session-key
285
- // layer. Mirrors the edge host (`mlkem_decapsulate_import.rs` / fips203):
286
- // recover the 32-byte shared secret from the client's ciphertext using
287
- // the server's static secret key, write it to `outPtr`, return 0 / neg.
288
- // Backed by the same noble lib the client encapsulates with (dev == prod).
289
- 'crypto.mlkem_decapsulate': (
290
- ctPtr: number,
291
- ctLen: number,
292
- skPtr: number,
293
- skLen: number,
294
- outPtr: number,
295
- ): number => {
296
- if (ctLen !== 1088 || skLen !== 2400) return -4;
297
- try {
298
- const ct = new Uint8Array(readBytes(ref, ctPtr, ctLen));
299
- const sk = new Uint8Array(readBytes(ref, skPtr, skLen));
300
- const ss = ml_kem768.decapsulate(ct, sk); // 32 bytes; implicit rejection on bad ct
301
- writeBytes(ref, outPtr, Buffer.from(ss));
302
- return 0;
303
- } catch {
304
- return -5;
305
- }
306
- },
307
-
308
- // RFC 9497 OPRF (mode 0x00, ristretto255-SHA512) server evaluation for
309
- // the keyed-salt OPRF. Mirrors the edge host
310
- // (`voprf_evaluate_import.rs` / the `voprf` crate): derive the per-user
311
- // key from (seed, info=username) and blind-evaluate the client's blinded
312
- // element, writing the 32-byte evaluated element to `outPtr`. Backed by
313
- // `@noble/curves` ristretto255_oprf, which matches the edge byte-for-byte
314
- // (both RFC 9497), so dev == prod.
315
- 'crypto.voprf_evaluate': (
316
- seedPtr: number,
317
- seedLen: number,
318
- infoPtr: number,
319
- infoLen: number,
320
- blindedPtr: number,
321
- blindedLen: number,
322
- outPtr: number,
323
- ): number => {
324
- // seedLen MUST be exactly 32 (RFC 9497 Ns; noble deriveKeyPair rejects
325
- // other lengths) -- matching the edge so dev and prod never diverge.
326
- if (blindedLen !== 32 || seedLen !== 32 || infoLen > 512) return -4;
327
- try {
328
- const seed = new Uint8Array(readBytes(ref, seedPtr, seedLen));
329
- const info = new Uint8Array(readBytes(ref, infoPtr, infoLen));
330
- const blinded = new Uint8Array(readBytes(ref, blindedPtr, blindedLen));
331
- const oprf = ristretto255_oprf.oprf;
332
- const kp = oprf.deriveKeyPair(seed, info);
333
- const evaluated = oprf.blindEvaluate(kp.secretKey, blinded); // 32-byte element
334
- writeBytes(ref, outPtr, Buffer.from(evaluated));
335
- return 0;
336
- } catch {
337
- return -5;
338
- }
339
- },
340
- };
341
- }
342
-
343
- function importKey(
344
- cs: CryptoState,
345
- format: number,
346
- alg: number,
347
- hash: number,
348
- pr: ParamReader,
349
- key: Buffer,
350
- extractable: boolean,
351
- ): number {
352
- const newEntry = (e: Omit<KeyEntry, 'extractable'>): number => {
353
- const handle = cs.nextHandle++;
354
- cs.keys.set(handle, { ...e, extractable });
355
- return handle;
356
- };
357
-
358
- try {
359
- // Symmetric / MAC / KDF: raw bytes.
360
- if (
361
- alg === ALG.AES_GCM ||
362
- alg === ALG.AES_CBC ||
363
- alg === ALG.AES_CTR ||
364
- alg === ALG.AES_KW ||
365
- alg === ALG.HMAC ||
366
- alg === ALG.PBKDF2 ||
367
- alg === ALG.HKDF
368
- ) {
369
- if (format !== FMT.RAW) return ERR_UNSUPPORTED;
370
- return newEntry({ raw: key, keyObject: null, alg, hash, isPrivate: false });
371
- }
372
-
373
- // Asymmetric: pkcs8 (private) / spki (public). raw not supported in dev.
374
- const isPrivate = format === FMT.PKCS8;
375
- if (format === FMT.PKCS8) {
376
- const ko = nodeCrypto.createPrivateKey({ key, format: 'der', type: 'pkcs8' });
377
- return newEntry({ raw: null, keyObject: ko, alg, hash, isPrivate });
378
- }
379
- if (format === FMT.SPKI) {
380
- const ko = nodeCrypto.createPublicKey({ key, format: 'der', type: 'spki' });
381
- return newEntry({ raw: null, keyObject: ko, alg, hash, isPrivate });
382
- }
383
- return ERR_UNSUPPORTED;
384
- } catch {
385
- return ERR_INVALID_PARAMS;
386
- }
387
- }
388
-
389
- function aesAlgName(keyLen: number, mode: 'gcm' | 'cbc' | 'ctr'): string {
390
- const bits = keyLen === 16 ? 128 : keyLen === 32 ? 256 : 0;
391
- if (!bits) throw new Error('crypto: bad AES key length');
392
- return `aes-${String(bits)}-${mode}`;
393
- }
394
-
395
- function aesOp(
396
- cs: CryptoState,
397
- ref: MemoryRef,
398
- encrypt: boolean,
399
- handle: number,
400
- pp: number,
401
- pl: number,
402
- dp: number,
403
- dl: number,
404
- ): number {
405
- const e = cs.keys.get(handle);
406
- if (!e || !e.raw) throw new Error('crypto: invalid AES key handle');
407
- const pr = new ParamReader(readBytes(ref, pp, pl));
408
- const alg = pr.readI32();
409
- pr.readI32(); // hash (unused)
410
- const data = readBytes(ref, dp, dl);
411
- try {
412
- if (alg === ALG.AES_GCM) {
413
- const iv = pr.readBlob();
414
- const tagBits = pr.readI32();
415
- const aad = pr.readBlob();
416
- if (tagBits !== 0 && tagBits !== 128) return ERR_INVALID_PARAMS;
417
- if (encrypt) {
418
- const c = nodeCrypto.createCipheriv(
419
- aesAlgName(e.raw.length, 'gcm'),
420
- e.raw,
421
- iv,
422
- ) as nodeCrypto.CipherGCM;
423
- if (aad.length) c.setAAD(aad);
424
- const ct = Buffer.concat([c.update(data), c.final()]);
425
- return stash(cs, Buffer.concat([ct, c.getAuthTag()]));
426
- }
427
- // Ciphertext must be at least the 16-byte tag; shorter input can
428
- // never authenticate.
429
- if (data.length < 16) return ERR_OPERATION_FAILED;
430
- const d = nodeCrypto.createDecipheriv(
431
- aesAlgName(e.raw.length, 'gcm'),
432
- e.raw,
433
- iv,
434
- ) as nodeCrypto.DecipherGCM;
435
- if (aad.length) d.setAAD(aad);
436
- const tag = data.subarray(data.length - 16);
437
- const ct = data.subarray(0, data.length - 16);
438
- d.setAuthTag(tag);
439
- return stash(cs, Buffer.concat([d.update(ct), d.final()]));
440
- }
441
- if (alg === ALG.AES_CBC) {
442
- const iv = pr.readBlob();
443
- const c = encrypt
444
- ? nodeCrypto.createCipheriv(aesAlgName(e.raw.length, 'cbc'), e.raw, iv)
445
- : nodeCrypto.createDecipheriv(aesAlgName(e.raw.length, 'cbc'), e.raw, iv);
446
- return stash(cs, Buffer.concat([c.update(data), c.final()]));
447
- }
448
- if (alg === ALG.AES_CTR) {
449
- const counter = pr.readBlob();
450
- const c = encrypt
451
- ? nodeCrypto.createCipheriv(aesAlgName(e.raw.length, 'ctr'), e.raw, counter)
452
- : nodeCrypto.createDecipheriv(aesAlgName(e.raw.length, 'ctr'), e.raw, counter);
453
- return stash(cs, Buffer.concat([c.update(data), c.final()]));
454
- }
455
- return ERR_UNSUPPORTED;
456
- } catch {
457
- return ERR_OPERATION_FAILED;
458
- }
459
- }
460
-
461
- function signOp(
462
- cs: CryptoState,
463
- ref: MemoryRef,
464
- handle: number,
465
- pp: number,
466
- pl: number,
467
- dp: number,
468
- dl: number,
469
- ): number {
470
- const e = cs.keys.get(handle);
471
- if (!e) throw new Error('crypto.sign: invalid handle');
472
- const pr = new ParamReader(readBytes(ref, pp, pl));
473
- const alg = pr.readI32();
474
- const hash = pr.readI32();
475
- const data = readBytes(ref, dp, dl);
476
- try {
477
- if (e.alg === ALG.HMAC && e.raw) {
478
- return stash(cs, nodeCrypto.createHmac(hashName(e.hash), e.raw).update(data).digest());
479
- }
480
- if (e.alg === ALG.ECDSA && e.keyObject) {
481
- const sig = nodeCrypto.sign(hashName(hash), data, {
482
- key: e.keyObject,
483
- dsaEncoding: 'ieee-p1363',
484
- });
485
- return stash(cs, sig);
486
- }
487
- if (e.alg === ALG.ED25519 && e.keyObject) {
488
- return stash(cs, nodeCrypto.sign(null, data, e.keyObject));
489
- }
490
- void alg;
491
- return ERR_UNSUPPORTED;
492
- } catch {
493
- return ERR_OPERATION_FAILED;
494
- }
495
- }
496
-
497
- function verifyOp(
498
- cs: CryptoState,
499
- ref: MemoryRef,
500
- handle: number,
501
- pp: number,
502
- pl: number,
503
- sp: number,
504
- sl: number,
505
- dp: number,
506
- dl: number,
507
- ): number {
508
- const e = cs.keys.get(handle);
509
- if (!e) throw new Error('crypto.verify: invalid handle');
510
- const pr = new ParamReader(readBytes(ref, pp, pl));
511
- pr.readI32(); // alg
512
- const hash = pr.readI32();
513
- const sig = readBytes(ref, sp, sl);
514
- const data = readBytes(ref, dp, dl);
515
- try {
516
- if (e.alg === ALG.HMAC && e.raw) {
517
- const mac = nodeCrypto.createHmac(hashName(e.hash), e.raw).update(data).digest();
518
- return mac.length === sig.length && nodeCrypto.timingSafeEqual(mac, sig) ? 1 : 0;
519
- }
520
- if (e.alg === ALG.ECDSA && e.keyObject) {
521
- const ok = nodeCrypto.verify(
522
- hashName(hash),
523
- data,
524
- {
525
- key: e.keyObject,
526
- dsaEncoding: 'ieee-p1363',
527
- },
528
- sig,
529
- );
530
- return ok ? 1 : 0;
531
- }
532
- if (e.alg === ALG.ED25519 && e.keyObject) {
533
- return nodeCrypto.verify(null, data, e.keyObject, sig) ? 1 : 0;
534
- }
535
- return ERR_UNSUPPORTED;
536
- } catch {
537
- return ERR_GENERIC;
538
- }
539
- }
540
-
541
- function deriveBitsOp(
542
- cs: CryptoState,
543
- ref: MemoryRef,
544
- handle: number,
545
- pp: number,
546
- pl: number,
547
- lengthBits: number,
548
- ): number {
549
- if (lengthBits < 0 || lengthBits % 8 !== 0) return ERR_INVALID_PARAMS;
550
- const outLen = lengthBits / 8;
551
- if (outLen > MAX_OUTPUT) return ERR_INVALID_PARAMS;
552
- const e = cs.keys.get(handle);
553
- if (!e) throw new Error('crypto.derive_bits: invalid handle');
554
- const pr = new ParamReader(readBytes(ref, pp, pl));
555
- const alg = pr.readI32();
556
- const hash = pr.readI32();
557
- try {
558
- if (alg === ALG.PBKDF2 && e.raw) {
559
- const iterations = pr.readU32();
560
- const salt = pr.readBlob();
561
- return stash(
562
- cs,
563
- nodeCrypto.pbkdf2Sync(e.raw, salt, iterations, outLen, hashName(hash)),
564
- );
565
- }
566
- if (alg === ALG.HKDF && e.raw) {
567
- const salt = pr.readBlob();
568
- const info = pr.readBlob();
569
- const okm = nodeCrypto.hkdfSync(hashName(hash), e.raw, salt, info, outLen);
570
- return stash(cs, Buffer.from(okm));
571
- }
572
- if ((alg === ALG.ECDH || alg === ALG.X25519) && e.keyObject) {
573
- const peerHandle = pr.readI32();
574
- const peer = cs.keys.get(peerHandle);
575
- if (!peer || !peer.keyObject)
576
- throw new Error('crypto.derive_bits: invalid peer handle');
577
- const shared = nodeCrypto.diffieHellman({
578
- privateKey: e.keyObject,
579
- publicKey: peer.keyObject,
580
- });
581
- return stash(cs, Buffer.from(shared.subarray(0, Math.min(outLen, shared.length))));
582
- }
583
- return ERR_UNSUPPORTED;
584
- } catch {
585
- return ERR_OPERATION_FAILED;
586
- }
587
- }