toiljs 0.0.65 → 0.0.67

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.
@@ -362,7 +362,14 @@ export async function startDevServer(options: DevServerOptions): Promise<Running
362
362
  const route = ssrRoutes.find((r) => r.test(pathnameOf(request.url)));
363
363
  if (route) {
364
364
  try {
365
- const out = assembleSsr(route, module.dispatchRender(envelopeReq));
365
+ // A static route (its template has no holes -> no slots) needs no guest
366
+ // render: serve the prerendered template directly so it paints instantly
367
+ // instead of falling through to a (blank-until-JS) client render. Dynamic
368
+ // routes run the guest `render` and splice its values in.
369
+ const out: SsrResult | null =
370
+ route.entries.length === 0
371
+ ? { status: 200, headers: [], html: route.tmpl }
372
+ : assembleSsr(route, module.dispatchRender(envelopeReq));
366
373
  if (out !== null) {
367
374
  sendSsr(response, out, request.method === 'HEAD');
368
375
  return;
@@ -18,9 +18,8 @@
18
18
  * u32 data_coherence_hash
19
19
  * u32 pair_coherence_hash (exactly THREE u32 after build_id, not four)
20
20
  *
21
- * Fail-closed per Part 5's host rule: an ABSENT section is "legacy single
22
- * artifact, load as hot" (NOT a hard reject); a PRESENT-but-unparseable section is
23
- * a corrupt artifact -> do not start that artifact's emulator.
21
+ * Fail-closed per Part 5's host rule: an absent or unparseable section is a
22
+ * corrupt Toil artifact -> do not start that artifact's emulator.
24
23
  */
25
24
 
26
25
  import { DataReader } from 'toiljs/io';
@@ -47,16 +46,15 @@ export interface Surface {
47
46
  readonly pairCoherenceHash: number;
48
47
  }
49
48
 
50
- /** `'absent'` => legacy single artifact (load as hot, no emulators).
51
- * `'invalid'` => present but corrupt (fail closed). Otherwise the parsed surface. */
52
- export function parseSurface(wasm: Buffer): Surface | 'absent' | 'invalid' {
49
+ /** `'invalid'` => absent or corrupt (fail closed). Otherwise the parsed surface. */
50
+ export function parseSurface(wasm: Buffer): Surface | 'invalid' {
53
51
  let sec: Buffer | null;
54
52
  try {
55
53
  sec = customSection(wasm, 'toil.surface');
56
54
  } catch {
57
55
  return 'invalid'; // garbage section table
58
56
  }
59
- if (sec === null) return 'absent';
57
+ if (sec === null) return 'invalid';
60
58
 
61
59
  const r = new DataReader(sec);
62
60
  r.readU16(); // format_version
@@ -9,7 +9,7 @@
9
9
  * passes (one `--targetMode cold`, one `--targetMode hot`) and produces BOTH
10
10
  * `release-hot.wasm` and `release-cold.wasm`; the cold artifact decodes to a
11
11
  * daemon catalog and its `toil.surface` is target_mode = cold.
12
- * - a project with only the legacy request surface keeps the single-artifact
12
+ * - a project with only the default request surface keeps the single-artifact
13
13
  * path (no cold pass, no cold artifact).
14
14
  *
15
15
  * The build invokes the LOCAL toilscript (branch feat/streams-phase0-compiler),
@@ -17,7 +17,15 @@
17
17
  * `node_modules` the same way the dev build resolves it (`require.resolve`).
18
18
  */
19
19
 
20
- import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
20
+ import {
21
+ existsSync,
22
+ mkdirSync,
23
+ mkdtempSync,
24
+ readFileSync,
25
+ rmSync,
26
+ symlinkSync,
27
+ writeFileSync,
28
+ } from 'node:fs';
21
29
  import { tmpdir } from 'node:os';
22
30
  import { dirname, join } from 'node:path';
23
31
  import { fileURLToPath } from 'node:url';
@@ -190,8 +198,8 @@ describe.skipIf(!existsSync(LOCAL_TOILSCRIPT))('buildServer two-pass (daemon pro
190
198
  // The cold artifact carries the daemon surface + catalog (decoded byte-for-byte).
191
199
  const coldBytes = readFileSync(cold);
192
200
  const surface = parseSurface(coldBytes);
193
- expect(surface !== 'absent' && surface !== 'invalid' && surface.targetMode).toBe('cold');
194
- expect(surface !== 'absent' && surface !== 'invalid' && surface.flags.daemon).toBe(true);
201
+ expect(surface !== 'invalid' && surface.targetMode).toBe('cold');
202
+ expect(surface !== 'invalid' && surface.flags.daemon).toBe(true);
195
203
 
196
204
  const catalog = parseDaemonCatalog(coldBytes);
197
205
  expect(catalog).not.toBeNull();
@@ -201,12 +209,12 @@ describe.skipIf(!existsSync(LOCAL_TOILSCRIPT))('buildServer two-pass (daemon pro
201
209
  expect(catalog!.tasks[1].schedule.kind).toBe('cron');
202
210
 
203
211
  // A daemon-only project (no request/stream surface) has no hot files, so the hot pass is
204
- // skipped (toilscript would HARD-ERROR a @daemon class under --targetMode hot). The legacy
205
- // single-artifact `release.wasm` is therefore not produced for a pure background worker.
212
+ // skipped (toilscript would HARD-ERROR a @daemon class under --targetMode hot). The default
213
+ // request artifact `release.wasm` is therefore not produced for a pure background worker.
206
214
  expect(existsSync(join(tmp, 'build/server/release.wasm'))).toBe(false);
207
215
  }, 60_000);
208
216
 
209
- it('keeps the single-artifact path for a legacy (no-daemon) project', async () => {
217
+ it('keeps the single-artifact path for a request-only project', async () => {
210
218
  scaffold(LEGACY_SRC, BASE_TOILCONFIG);
211
219
  await buildServer(tmp);
212
220
 
@@ -117,7 +117,13 @@ describe('parseDaemonCatalog (Part 5)', () => {
117
117
  name: 'lateMinute',
118
118
  taskIndex: 0,
119
119
  kind: 1,
120
- cron: { minute: minuteMask, hour: 0xffffff, dom: 0xfffffffe, month: 0x1ffe, dow: 0x7f },
120
+ cron: {
121
+ minute: minuteMask,
122
+ hour: 0xffffff,
123
+ dom: 0xfffffffe,
124
+ month: 0x1ffe,
125
+ dow: 0x7f,
126
+ },
121
127
  },
122
128
  ]);
123
129
  const cat = parseDaemonCatalog(wasmWithSection('toildaemon.catalog', payload));
@@ -173,10 +179,11 @@ function buildSurfaceBytes(opts: {
173
179
  describe('parseSurface (Part 5)', () => {
174
180
  it('decodes a cold daemon surface with exactly three trailing u32 hashes', () => {
175
181
  const flags = 0b000100 | 0b001000; // daemon (bit2) + scheduled (bit3)
176
- const s = parseSurface(wasmWithSection('toil.surface', buildSurfaceBytes({ mode: 1, flags })));
177
- expect(s).not.toBe('absent');
182
+ const s = parseSurface(
183
+ wasmWithSection('toil.surface', buildSurfaceBytes({ mode: 1, flags })),
184
+ );
178
185
  expect(s).not.toBe('invalid');
179
- if (s !== 'absent' && s !== 'invalid') {
186
+ if (s !== 'invalid') {
180
187
  expect(s.targetMode).toBe('cold');
181
188
  expect(s.flags.daemon).toBe(true);
182
189
  expect(s.flags.scheduled).toBe(true);
@@ -188,13 +195,15 @@ describe('parseSurface (Part 5)', () => {
188
195
  });
189
196
 
190
197
  it('decodes a hot surface (target_mode 0)', () => {
191
- const s = parseSurface(wasmWithSection('toil.surface', buildSurfaceBytes({ mode: 0, flags: 1 })));
192
- expect(s !== 'absent' && s !== 'invalid' && s.targetMode).toBe('hot');
198
+ const s = parseSurface(
199
+ wasmWithSection('toil.surface', buildSurfaceBytes({ mode: 0, flags: 1 })),
200
+ );
201
+ expect(s !== 'invalid' && s.targetMode).toBe('hot');
193
202
  });
194
203
 
195
- it("treats an ABSENT section as 'absent' (legacy single artifact, load as hot)", () => {
204
+ it('fails closed when toil.surface is absent', () => {
196
205
  const wasm = wasmWithSection('toildb.catalog', Buffer.from([0x01, 0x00]));
197
- expect(parseSurface(wasm)).toBe('absent');
206
+ expect(parseSurface(wasm)).toBe('invalid');
198
207
  });
199
208
 
200
209
  it("fails closed: a PRESENT but truncated section is 'invalid'", () => {
@@ -92,7 +92,7 @@ function routeKindsSection(routes: readonly (readonly [number, number, string])[
92
92
  return Buffer.concat(chunks);
93
93
  }
94
94
 
95
- function catalogSectionV2(fillMaxWaitMs: number, fillAllowStale: number, replication = 5): Buffer {
95
+ function catalogSectionV1(fillMaxWaitMs: number, fillAllowStale: number, replication = 5): Buffer {
96
96
  const chunks: Buffer[] = [];
97
97
  const u8 = (v: number) => chunks.push(Buffer.from([v & 0xff]));
98
98
  const u16 = (v: number) => {
@@ -111,7 +111,7 @@ function catalogSectionV2(fillMaxWaitMs: number, fillAllowStale: number, replica
111
111
  chunks.push(b);
112
112
  };
113
113
 
114
- u16(2); // catalog version
114
+ u16(1); // catalog version
115
115
  u16(1); // databases
116
116
  str('App');
117
117
  u16(1); // collections
@@ -215,8 +215,8 @@ describe('toildb dev emulator (record family)', () => {
215
215
  expect(imports['data.resolve_collection'](p, l, 16)).toBe(-1070);
216
216
  });
217
217
 
218
- it('parses catalog v2 fill policy and backend replication bytes', () => {
219
- setDbCatalog(wasmWithSection('toildb.catalog', catalogSectionV2(7, 0)));
218
+ it('parses catalog v1 fill policy and backend replication bytes', () => {
219
+ setDbCatalog(wasmWithSection('toildb.catalog', catalogSectionV1(7, 0)));
220
220
  const { imports, buf, db } = setupRaw();
221
221
  const h = resolve(imports, buf, 'App/users');
222
222
 
@@ -231,9 +231,9 @@ describe('toildb dev emulator (record family)', () => {
231
231
  });
232
232
  });
233
233
 
234
- it('rejects replication policies that require catalog v3 metadata', () => {
234
+ it('rejects replication policies that require explicit policy metadata', () => {
235
235
  for (const replication of [3, 4]) {
236
- setDbCatalog(wasmWithSection('toildb.catalog', catalogSectionV2(7, 0, replication)));
236
+ setDbCatalog(wasmWithSection('toildb.catalog', catalogSectionV1(7, 0, replication)));
237
237
  const { imports, buf } = setupRaw();
238
238
  const [p, l] = put(buf, 0, 'App/users');
239
239
 
@@ -241,8 +241,8 @@ describe('toildb dev emulator (record family)', () => {
241
241
  }
242
242
  });
243
243
 
244
- it('rejects malformed catalog v2 fill policy', () => {
245
- setDbCatalog(wasmWithSection('toildb.catalog', catalogSectionV2(7, 2)));
244
+ it('rejects malformed catalog v1 fill policy', () => {
245
+ setDbCatalog(wasmWithSection('toildb.catalog', catalogSectionV1(7, 2)));
246
246
  const { imports, buf } = setupRaw();
247
247
  const [p, l] = put(buf, 0, 'App/users');
248
248
 
@@ -7,7 +7,7 @@
7
7
  * Compiles test/fixtures/bignum-wire/spec.ts with the installed toilscript (so it
8
8
  * exercises the published compiler + generated client, not a hand-written stub), then
9
9
  * imports the generated TS client and asserts the wire shape both directions, including
10
- * values far above 2^53 and the legacy limb-array shape older servers emitted.
10
+ * values far above 2^53.
11
11
  */
12
12
  import { spawnSync } from 'node:child_process';
13
13
  import fs from 'node:fs';
@@ -24,6 +24,9 @@ const codec = path.join(here, '..', 'src', 'io', 'codec.ts');
24
24
 
25
25
  /** Resolves the installed toilscript CLI entry (no PATH / .bin assumptions). */
26
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;
27
30
  const pkgPath = require.resolve('toilscript/package.json');
28
31
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')) as { bin?: Record<string, string> };
29
32
  const binRel = pkg.bin?.toilscript;
@@ -83,6 +86,10 @@ beforeAll(async () => {
83
86
  { encoding: 'utf8' },
84
87
  );
85
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);
86
93
  const gen = (await import(pathToFileURL(mod).href)) as {
87
94
  Wallet: WalletStatic;
88
95
  Account: AccountStatic;
@@ -142,13 +149,6 @@ describe('generated client bignum JSON wire format', () => {
142
149
  expect(back.d).toBe(BigInt('-' + huge));
143
150
  });
144
151
 
145
- it('still revives the legacy little-endian limb-array shape (back-compat)', () => {
146
- // u256 [5,0,4,0] little-endian = 5 + 4*2^128.
147
- const w = Wallet.fromJSONValue({ c: [5, 0, 4, 0], a: [9, 1] });
148
- expect(w.c).toBe(2n ** 130n + 5n);
149
- expect(w.a).toBe(2n ** 64n + 9n);
150
- });
151
-
152
152
  it('recurses into nested @data and arrays of bignums', () => {
153
153
  const a = new Account();
154
154
  a.main.c = BigInt(huge);