toiljs 0.0.60 → 0.0.61

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 (119) hide show
  1. package/.github/workflows/ci.yml +31 -0
  2. package/CHANGELOG.md +5 -0
  3. package/build/cli/.tsbuildinfo +1 -1
  4. package/build/cli/index.js +2 -2
  5. package/build/client/.tsbuildinfo +1 -1
  6. package/build/client/index.d.ts +1 -1
  7. package/build/client/index.js +1 -1
  8. package/build/client/routing/mount.js +12 -1
  9. package/build/client/ssr/markers.d.ts +1 -0
  10. package/build/client/ssr/markers.js +3 -0
  11. package/build/compiler/.tsbuildinfo +1 -1
  12. package/build/compiler/config.d.ts +21 -0
  13. package/build/compiler/config.js +35 -0
  14. package/build/compiler/docs.d.ts +2 -1
  15. package/build/compiler/docs.js +33 -304
  16. package/build/compiler/index.d.ts +13 -0
  17. package/build/compiler/index.js +113 -21
  18. package/build/compiler/template-build.d.ts +21 -1
  19. package/build/compiler/template-build.js +110 -26
  20. package/build/compiler/toil-docs.generated.d.ts +1 -0
  21. package/build/compiler/toil-docs.generated.js +20 -0
  22. package/build/devserver/.tsbuildinfo +1 -1
  23. package/build/devserver/daemon/catalog.d.ts +26 -0
  24. package/build/devserver/daemon/catalog.js +48 -0
  25. package/build/devserver/daemon/cron.d.ts +4 -0
  26. package/build/devserver/daemon/cron.js +50 -0
  27. package/build/devserver/daemon/host.d.ts +37 -0
  28. package/build/devserver/daemon/host.js +94 -0
  29. package/build/devserver/daemon/index.d.ts +34 -0
  30. package/build/devserver/daemon/index.js +241 -0
  31. package/build/devserver/db/catalog.d.ts +2 -1
  32. package/build/devserver/db/catalog.js +44 -44
  33. package/build/devserver/db/database.d.ts +27 -11
  34. package/build/devserver/db/database.js +539 -169
  35. package/build/devserver/db/index.d.ts +1 -1
  36. package/build/devserver/db/index.js +1 -1
  37. package/build/devserver/db/routeKinds.d.ts +8 -0
  38. package/build/devserver/db/routeKinds.js +139 -0
  39. package/build/devserver/db/types.d.ts +64 -1
  40. package/build/devserver/db/types.js +33 -1
  41. package/build/devserver/index.d.ts +10 -0
  42. package/build/devserver/index.js +7 -0
  43. package/build/devserver/mstore/store.d.ts +18 -0
  44. package/build/devserver/mstore/store.js +82 -0
  45. package/build/devserver/runtime/host.d.ts +6 -0
  46. package/build/devserver/runtime/host.js +45 -1
  47. package/build/devserver/runtime/module.d.ts +1 -0
  48. package/build/devserver/runtime/module.js +27 -1
  49. package/build/devserver/server.d.ts +6 -0
  50. package/build/devserver/server.js +59 -0
  51. package/build/devserver/ssr.d.ts +25 -0
  52. package/build/devserver/ssr.js +114 -0
  53. package/build/devserver/wasm/sections.d.ts +2 -0
  54. package/build/devserver/wasm/sections.js +42 -0
  55. package/build/devserver/wasm/surface.d.ts +18 -0
  56. package/build/devserver/wasm/surface.js +41 -0
  57. package/docs/README.md +4 -4
  58. package/docs/auth-todo.md +6 -6
  59. package/docs/caching.md +5 -5
  60. package/docs/cli.md +15 -0
  61. package/docs/client.md +40 -0
  62. package/docs/crypto.md +4 -4
  63. package/docs/data.md +6 -6
  64. package/docs/email.md +28 -28
  65. package/docs/environment.md +10 -10
  66. package/docs/index.md +26 -0
  67. package/docs/ratelimit.md +10 -10
  68. package/docs/routing.md +2 -2
  69. package/docs/server.md +61 -0
  70. package/docs/ssr.md +561 -113
  71. package/docs/styling.md +22 -0
  72. package/docs/time.md +1 -1
  73. package/eslint.config.js +10 -1
  74. package/examples/basic/client/components/Header.tsx +3 -0
  75. package/examples/basic/client/routes/features/actions.tsx +0 -2
  76. package/examples/basic/client/routes/hello.tsx +89 -19
  77. package/examples/basic/client/styles/main.css +48 -0
  78. package/examples/basic/server/SsrHelloRender.ts +97 -0
  79. package/examples/basic/server/main.ts +5 -0
  80. package/examples/basic/server/streams/Echo.ts +49 -0
  81. package/package.json +12 -10
  82. package/scripts/gen-toil-docs.mjs +96 -0
  83. package/src/cli/create.ts +2 -2
  84. package/src/client/index.ts +1 -1
  85. package/src/client/routing/mount.tsx +18 -2
  86. package/src/client/ssr/markers.tsx +22 -0
  87. package/src/compiler/config.ts +88 -2
  88. package/src/compiler/docs.ts +47 -308
  89. package/src/compiler/index.ts +236 -32
  90. package/src/compiler/ssr-codegen.ts +1 -1
  91. package/src/compiler/template-build.ts +247 -46
  92. package/src/compiler/toil-docs.generated.ts +26 -0
  93. package/src/devserver/daemon/catalog.ts +120 -0
  94. package/src/devserver/daemon/cron.ts +87 -0
  95. package/src/devserver/daemon/host.ts +224 -0
  96. package/src/devserver/daemon/index.ts +349 -0
  97. package/src/devserver/db/catalog.ts +61 -53
  98. package/src/devserver/db/database.ts +613 -149
  99. package/src/devserver/db/index.ts +1 -1
  100. package/src/devserver/db/routeKinds.ts +147 -0
  101. package/src/devserver/db/types.ts +65 -2
  102. package/src/devserver/index.ts +12 -0
  103. package/src/devserver/mstore/store.ts +121 -0
  104. package/src/devserver/runtime/host.ts +92 -1
  105. package/src/devserver/runtime/module.ts +35 -1
  106. package/src/devserver/server.ts +101 -0
  107. package/src/devserver/ssr.ts +166 -0
  108. package/src/devserver/wasm/sections.ts +59 -0
  109. package/src/devserver/wasm/surface.ts +88 -0
  110. package/test/daemon-build.test.ts +198 -0
  111. package/test/daemon-catalog.test.ts +265 -0
  112. package/test/daemon-emulation.test.ts +216 -0
  113. package/test/devserver-database.test.ts +396 -5
  114. package/test/email-preview.test.ts +6 -1
  115. package/test/fixtures/daemon-app.ts +56 -0
  116. package/test/global-setup.ts +17 -0
  117. package/test/ssr-render.test.ts +94 -27
  118. package/test/ssr-template.test.tsx +44 -1
  119. package/vitest.config.ts +3 -0
@@ -1,90 +1,80 @@
1
1
  /**
2
- * Parse a compiled server wasm's `toildb.catalog` custom section into a map of
3
- * `"<Db>/<collection>"` (the resolve key the guest passes to
4
- * `data.resolve_collection`) -> the collection's CURRENT `schema_version`.
2
+ * Parse a compiled server wasm's `toildb.catalog` custom section into structured
3
+ * dev capability metadata keyed by `"<Db>/<collection>"` (the resolve key the
4
+ * guest passes to `data.resolve_collection`).
5
5
  *
6
6
  * The dev DB uses this to STAMP each write with the value type's current schema
7
7
  * version. When the developer evolves a `@data` type and rebuilds, the catalog
8
8
  * version changes; data already on disk keeps its OLD stamp, so a read surfaces
9
9
  * that old version and the guest's woven `decodeInto` runs the `@migrate` - the
10
10
  * dev-side equivalent of the edge binding the cap's schema_version into the row.
11
+ * The parsed family is also used to reject wrong-family imports locally.
11
12
  *
12
13
  * Wire format mirrors `toildb::catalog` / the backend `db_catalog` decoder and the
13
14
  * compiler's `buildToilDbCatalog` emitter (all little-endian).
14
15
  */
15
16
 
16
- /** Read a LEB128 from `buf` at `pos`; throws on overrun (the section is a
17
- * tenant-built, possibly mid-rebuild wasm, so it must never over-read). */
18
17
  import { DataReader } from 'toiljs/io';
18
+ import { customSection } from '../wasm/sections.js';
19
+ import {
20
+ CollectionFamily,
21
+ type DbCatalogState,
22
+ DEFAULT_FILL_WAIT_MS,
23
+ type DevCollectionHandle,
24
+ isCollectionFamily,
25
+ MAX_FILL_WAIT_MS,
26
+ } from './types.js';
19
27
 
20
- function leb(buf: Buffer, pos: number): [number, number] {
21
- let result = 0;
22
- let shift = 0;
23
- let p = pos;
24
- for (;;) {
25
- if (p >= buf.length) throw new RangeError('leb128 past end of buffer');
26
- const b = buf[p++];
27
- result |= (b & 0x7f) << shift;
28
- if ((b & 0x80) === 0) break;
29
- shift += 7;
30
- if (shift > 35) throw new RangeError('leb128 too long');
31
- }
32
- return [result >>> 0, p];
28
+ function validReplication(value: number): boolean {
29
+ return value >= 0 && value <= 5;
33
30
  }
34
31
 
35
- /** The bytes of the named wasm custom section, or null if absent. Bounds-checked
36
- * so a truncated/garbage module can never read past the buffer. */
37
- function customSection(wasm: Buffer, want: string): Buffer | null {
38
- let pos = 8; // skip the 8-byte magic + version header
39
- while (pos < wasm.length) {
40
- const id = wasm[pos++];
41
- let size: number;
42
- [size, pos] = leb(wasm, pos);
43
- const end = pos + size;
44
- if (end > wasm.length || end < pos) return null; // truncated section table
45
- if (id === 0) {
46
- let nameLen: number;
47
- let namePos: number;
48
- [nameLen, namePos] = leb(wasm, pos);
49
- if (namePos + nameLen <= end && wasm.toString('latin1', namePos, namePos + nameLen) === want)
50
- return wasm.subarray(namePos + nameLen, end);
51
- }
52
- pos = end;
53
- }
54
- return null;
32
+ function validPlacement(value: number): boolean {
33
+ return value === 0 || value === 1;
55
34
  }
56
35
 
57
- /** `"<Db>/<collection>"` -> current `schema_version` for every collection. Decoded
58
- * with the shared bounds-checked {@link DataReader} (it returns 0/empty + flips
59
- * `.ok` past the end), so a truncated/garbage section - e.g. a mid-rebuild wasm -
60
- * yields only the collections that decoded cleanly and never over-reads. Mirrors
61
- * the compiler's `buildToilDbCatalog` emitter + the backend `db_catalog` decoder. */
62
- export function parseCatalog(wasm: Buffer): Map<string, number> {
63
- const out = new Map<string, number>();
36
+ /** Decode the devserver's catalog state. A missing section stays distinct from a
37
+ * present-but-bad section so `resolve_collection` can match the edge's
38
+ * Present/Malformed/NoSection admission behavior. */
39
+ export function parseCatalog(wasm: Buffer): DbCatalogState {
40
+ const collections = new Map<string, DevCollectionHandle>();
64
41
  let sec: Buffer | null;
65
42
  try {
66
43
  sec = customSection(wasm, 'toildb.catalog');
67
44
  } catch {
68
- return out; // garbage section table (mid-rebuild) -> no catalog
45
+ return { kind: 'no-section' }; // garbage section table (mid-rebuild) -> no catalog
69
46
  }
70
- if (sec === null) return out;
47
+ if (sec === null) return { kind: 'no-section' };
71
48
 
72
49
  const r = new DataReader(sec);
73
- r.readU16(); // catalog format version
50
+ const version = r.readU16();
51
+ if (!r.ok || (version !== 1 && version !== 2)) return { kind: 'malformed' };
74
52
  const ndb = r.readU16();
75
53
  for (let d = 0; d < ndb && r.ok; d++) {
76
54
  const db = r.readString();
77
55
  const nc = r.readU16();
78
56
  for (let c = 0; c < nc && r.ok; c++) {
79
57
  const name = r.readString();
80
- r.readU8(); // family
58
+ const family = r.readU8();
81
59
  r.readString(); // keyType
82
60
  r.readString(); // valueType
83
61
  r.readU32(); // valueDataId
84
62
  const schemaVersion = r.readU32();
85
63
  r.readU32(); // generation
86
- r.readU8(); // replication (emitter order: replication then placement)
87
- r.readU8(); // placement
64
+ const replication = r.readU8(); // emitter order: replication then placement
65
+ const placement = r.readU8();
66
+ let fillMaxWaitMs = DEFAULT_FILL_WAIT_MS;
67
+ let fillAllowStale = true;
68
+ if (version >= 2) {
69
+ fillMaxWaitMs = r.readU32();
70
+ const fillAllowStaleByte = r.readU8();
71
+ if (
72
+ fillMaxWaitMs > MAX_FILL_WAIT_MS ||
73
+ (fillAllowStaleByte !== 0 && fillAllowStaleByte !== 1)
74
+ )
75
+ return { kind: 'malformed' };
76
+ fillAllowStale = fillAllowStaleByte === 1;
77
+ }
88
78
  const nFields = r.readU16();
89
79
  for (let f = 0; f < nFields; f++) {
90
80
  r.readString(); // field name
@@ -93,8 +83,26 @@ export function parseCatalog(wasm: Buffer): Map<string, number> {
93
83
  }
94
84
  const nMig = r.readU16();
95
85
  for (let m = 0; m < nMig; m++) r.readU32(); // migratableFrom versions
96
- if (r.ok) out.set(db + '/' + name, schemaVersion >>> 0);
86
+ if (
87
+ !isCollectionFamily(family) ||
88
+ !validReplication(replication) ||
89
+ !validPlacement(placement)
90
+ )
91
+ return { kind: 'malformed' };
92
+ const key = db + '/' + name;
93
+ if (collections.has(key)) return { kind: 'malformed' };
94
+ if (r.ok)
95
+ collections.set(key, {
96
+ name: key,
97
+ family: family as CollectionFamily,
98
+ schemaVersion: schemaVersion >>> 0,
99
+ replication,
100
+ placement,
101
+ fillMaxWaitMs,
102
+ fillAllowStale,
103
+ });
97
104
  }
98
105
  }
99
- return out;
106
+ if (!r.ok || r.remaining() !== 0) return { kind: 'malformed' };
107
+ return { kind: 'present', collections };
100
108
  }