@sproutboat/toolchain 0.4.11 → 0.4.13

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.
package/CHANGELOG.md CHANGED
@@ -1,10 +1,26 @@
1
1
  # @sproutboat/toolchain
2
2
 
3
+ ## 0.4.13
4
+
5
+ ### Patch Changes
6
+
7
+ - bb1b6a2: Parse ISO date timezone offsets correctly in native Porffor builds, including zero fields and fractional seconds.
8
+
9
+ ## 0.4.12
10
+
11
+ ### Patch Changes
12
+
13
+ - ed9640a: Point Porffor patch diagnostics, runtime comments, and historical notes to the
14
+ shared upstream notes in the Sproutboat platform repository.
15
+ - b6cc9e6: Pin Porffor alpha 9, including alpha 8's compiler and GC fixes and alpha 9's ESM module support. Adapt the native-fetch link and compile-flag patches to Porffor's split C-unit build. Preserve the published 0.4.11 `TypedArray.from` workaround, which alpha 9 still needs. Re-vendor the verified alpha 9 source archive. The uWebSockets commit is unchanged.
16
+
3
17
  ## 0.4.11
4
18
 
5
19
  ### Patch Changes
6
20
 
7
- - 6d72af7: Fix `TypedArray.from` with array-like inputs in native binaries, and regenerate Porffor's builtins table when its compiler is unpacked through a symlinked temporary path. This preserves the key bytes in repeated HMAC signing with `Uint8Array.from({ length: 16 }, mapFn)`.
21
+ - 6d72af7: Fix `TypedArray.from` with array-like inputs in native binaries and
22
+ regenerate Porffor's builtin table after the source patch, including when
23
+ the compiler is unpacked through a symlinked temporary path.
8
24
 
9
25
  ## 0.4.10
10
26
 
@@ -87,7 +103,7 @@
87
103
  prototype and stop once the "next" pointer stops advancing, reading a
88
104
  fixed-point chain as "no `.then` found" instead of spinning. Not specific to
89
105
  `Date.prototype.toISOString()` — an earlier note wrongly correlated the bug
90
- with that one call; see `sproutboat-cli/patches/UPSTREAM.md`.
106
+ with that one call; see `https://github.com/baronunread/sproutboat/tree/main/patches/upstream`.
91
107
 
92
108
  ## 0.4.0
93
109
 
@@ -0,0 +1,70 @@
1
+ # Porffor alpha 9 upgrade audit
2
+
3
+ Pin: `de4eb588264885b3a1596f75010e371a2052033f`, tagged `alpha-9` on
4
+ 2026-09-24. The vendored codeload archive has SHA-256
5
+ `1a62187a93356b36cb6ac703ce9770c8917e4fa65545fd7c33ee8a180ae5a4c9`.
6
+
7
+ ## Alpha 8 changes
8
+
9
+ The [alpha 8 release](https://github.com/CanadaHonk/porffor/releases/tag/alpha-8)
10
+ contains 27 commits. Every commit falls into one of these groups:
11
+
12
+ | Area | Commits | Effect on Sproutboat |
13
+ | --- | --- | --- |
14
+ | Language and compiler correctness | `6063154a`, `8436b386`, `d3376a2d`, `8833624c`, `b92571c4`, `dfab4235`, `65a6bb3f`, `cb26d320`, `6a47f02c`, `acf6dda0`, `c54557ba`, `50855902`, `5e5db5a4`, `6ec00849`, `61d42690` | Better default parameters, spread calls, object values, closure parameters, uninitialized and `let` bindings, optional computed keys, `finally` and exception exits; the remaining edits remove redundant IR and code generation. These improve handler correctness without replacing a local patch. |
15
+ | GC and memory | `83cd4902`, `b5eb0b98`, `9e283dd2`, `f687ac7c`, `08bf855d`, `135d2761`, `ae4002b1`, `a110867b` | Smaller memory commitment and less GC work. `a110867b` supplies a C macro used by the renderer. No Sproutboat GC patch exists to remove. |
16
+ | String built-ins | `f483ba7e` | Better `substring` and `substr`; no corresponding local shim. |
17
+ | Compiler progress | `b77750b0` | Shows compile progress on a TTY; no runtime contract change. |
18
+ | Self-hosted tools and benchmarks | `370f1f75`, `7b12abd2` | Adds `stat.isFile` and a TypeScript compiler benchmark; neither is used by Sproutboat. |
19
+
20
+ ## Alpha 9 and cleanup decision
21
+
22
+ The [alpha 9 release](https://github.com/CanadaHonk/porffor/releases/tag/alpha-9)
23
+ contains `de4eb588`, adding ESM imports and exports. A direct native-fetch
24
+ fixture importing a sibling module compiled and returned its imported value.
25
+ Porffor now compiles modules as separate cached C units. The local `SB_EXTRA_LINK`
26
+ and `SB_EXTRA_CFLAGS` patches had to move to the new link list and shared
27
+ compile arguments. `UWS_COMMIT` remains `360c276d609d59af56ae6932adb95154ace9f15f`.
28
+
29
+ No local patch is superseded by the alpha 8 or 9 changes. The patches provide
30
+ Sproutboat-specific native-fetch behavior: `$PORT`, log routing, bytestring
31
+ encoding, raw asset bodies, status and header handling, request size, R2
32
+ transfers, and external link and include flags. The CLI still bundles before
33
+ Porffor to validate the full dependency graph, apply its Node shims, and pass
34
+ one controlled handler export to the runtime wrapper. Native ESM makes a future
35
+ module-based wrapper possible, but removing Bun.build or export normalization
36
+ now would also change capability validation and binding behavior. Porffor's
37
+ old external esbuild requirement is gone in alpha 9, so the CLI can drop its
38
+ esbuild dependency and bundled platform executable.
39
+
40
+ A module-wrapper prototype compiled a separate handler module. Its normal
41
+ `env.GREETING` reference returned HTTP 500; using `globalThis.env.GREETING`
42
+ returned HTTP 200. The current export normalization keeps the existing
43
+ unqualified `env` contract. The direct ESM import is now part of the CI native
44
+ crypto vector, so a future pin cannot silently lose module support.
45
+
46
+ Alpha 9 caches compiled C units by the absolute entry path. In one local
47
+ `hello` handler-edit comparison, a stable path compiled in 549 ms versus
48
+ 1,505 ms with changing paths. The CLI's `dev` loop now uses a stable entry
49
+ path per process while keeping candidate artifacts isolated. These are local
50
+ single-run timings, not a published performance claim.
51
+
52
+ The published `@sproutboat/toolchain@0.4.11` includes a `TypedArray.from`
53
+ array-like input patch absent from this checkout's 0.4.10 base. Clean alpha 9
54
+ still returns an empty array for `Uint8Array.from({ length: 4 }, mapFn)`. The
55
+ 0.4.11 patch was carried forward, its precompiled builtin table is regenerated,
56
+ and a native crypto vector now exercises that array-like input.
57
+
58
+ Regression evidence for this pin: toolchain tests (11), CLI tests (92), both
59
+ kitchen-sink harnesses (27 broker checks, 29 standalone checks), and all 12 CLI
60
+ examples passed. The direct ESM fixture returned HTTP 200 with the imported
61
+ value. The package typecheck and lint passed.
62
+
63
+ ## Release order
64
+
65
+ The registry's latest toolchain is 0.4.11 and still pins alpha 7. This checkout
66
+ has been reconciled with that release's `TypedArray.from` patch; its pending
67
+ changeset targets 0.4.12. Publish the alpha 9 toolchain first. Then update
68
+ `sproutboat-cli`, the main `sproutboat` app, and `sproutboat-site/web` to require
69
+ 0.4.12 and refresh their lockfiles. The CLI can then ship without an esbuild
70
+ executable. Until 0.4.12 is published, existing npm consumers still run alpha 7.
@@ -0,0 +1,35 @@
1
+ # Porffor pin scorecard
2
+
3
+ Pin: alpha-9 (de4eb588264885b3a1596f75010e371a2052033f). Compatibility report: 2026-09-26T00:32:42.739Z.
4
+
5
+ | Measure | Result |
6
+ | --- | ---: |
7
+ | Fixtures compiled | 32/32 |
8
+ | Behavior matches | 30/32 |
9
+ | Median / p95 compile, ms | 3139 / 3472 |
10
+ | Median / p95 binary, bytes | 983392 / 1016464 |
11
+ | Patch markers | 27 |
12
+ | Upstream files written | 6 |
13
+ | Fresh patch application | pass |
14
+
15
+ This is the first recorded pin. Compare the next pin against this snapshot.
16
+
17
+ ## Patch inventory
18
+
19
+ - `compiler/render.js`: PORT_MARKER, CONSOLE_MARKER, BYTESTRING_MARKER, READ_RAW_MARKER
20
+ - `compiler/index.js`: LINK_MARKER, CFLAGS_MARKER
21
+ - `compiler/builtins/typedarray.js`: TYPED_ARRAY_FROM_MARKER
22
+ - `compiler/builtins/date.ts`: DATE_PARSER_MARKER
23
+ - `compiler/uwebsockets.js`: BODY_MARKER, STATUS_SIG_MARKER, STATUS_303_MARKER, STATUS_DEFAULT_MARKER, HDR_SIG_MARKER, HDR_CALL_MARKER, HDR_CAP_MARKER, HDR_SKIP_MARKER, HDR_APPEND_MARKER, READ_RAW_DECL_MARKER, FORBIDDEN_HDR_MARKER, WRITE_RESPONSE_MARKER, R2_TRANSFER_MARKER, R2_TRANSFER_CALL_MARKER, R2_DOWNLOAD_CACHE_MARKER, R2_RANGE_CACHE_MARKER, R2_CONDITIONAL_CACHE_MARKER, R2_STREAMING_CACHE_MARKER, R2_RANGE_HEADER_CACHE_MARKER
24
+ - `compiler/builtins_precompiled.js`: regenerated builtin table
25
+
26
+ Patch source SHA-256: `69cf5d0bcf3f4d4c05b81df0ff04b83479af622539fa19566155531bf3ee469c`.
27
+
28
+ ## Known behavior gaps
29
+
30
+ - 15-date-iso.js: request 3 mismatch: expected {"status":200,"body":"2008-03-04T23:00:00.000Z","content-type":null}, got {"status":200,"body":"0003-05-08T00:00:00.000Z","content-type":null}
31
+ - 16-date-parts.js: request 3 mismatch: expected {"status":200,"body":"{\"year\":2008,\"month\":3,\"day\":4}","content-type":"application/json;charset=utf-8"}, got {"status":200,"body":"{\"year\":3,\"month\":5,\"day\":8}","content-type":"application/json;charset=utf-8"}
32
+
33
+ ## Review
34
+
35
+ Use this scorecard with the full compatibility report and issue #35's build telemetry. If behavior regresses or patch work grows, review the individual patches and measure an alternate backend with the same fixtures.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sproutboat/toolchain",
3
- "version": "0.4.11",
3
+ "version": "0.4.13",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {
@@ -0,0 +1,58 @@
1
+ {
2
+ "pin": "alpha-9 (de4eb588264885b3a1596f75010e371a2052033f)",
3
+ "reportGeneratedAt": "2026-09-26T00:32:42.739Z",
4
+ "fixtures": 32,
5
+ "compiled": 32,
6
+ "matched": 30,
7
+ "medianCompileMs": 3139,
8
+ "p95CompileMs": 3472,
9
+ "medianBinaryBytes": 983392,
10
+ "p95BinaryBytes": 1016464,
11
+ "patchSourceSha256": "69cf5d0bcf3f4d4c05b81df0ff04b83479af622539fa19566155531bf3ee469c",
12
+ "patchMarkerCount": 27,
13
+ "patchTargets": {
14
+ "compiler/render.js": [
15
+ "PORT_MARKER",
16
+ "CONSOLE_MARKER",
17
+ "BYTESTRING_MARKER",
18
+ "READ_RAW_MARKER"
19
+ ],
20
+ "compiler/index.js": [
21
+ "LINK_MARKER",
22
+ "CFLAGS_MARKER"
23
+ ],
24
+ "compiler/builtins/typedarray.js": [
25
+ "TYPED_ARRAY_FROM_MARKER"
26
+ ],
27
+ "compiler/builtins/date.ts": [
28
+ "DATE_PARSER_MARKER"
29
+ ],
30
+ "compiler/uwebsockets.js": [
31
+ "BODY_MARKER",
32
+ "STATUS_SIG_MARKER",
33
+ "STATUS_303_MARKER",
34
+ "STATUS_DEFAULT_MARKER",
35
+ "HDR_SIG_MARKER",
36
+ "HDR_CALL_MARKER",
37
+ "HDR_CAP_MARKER",
38
+ "HDR_SKIP_MARKER",
39
+ "HDR_APPEND_MARKER",
40
+ "READ_RAW_DECL_MARKER",
41
+ "FORBIDDEN_HDR_MARKER",
42
+ "WRITE_RESPONSE_MARKER",
43
+ "R2_TRANSFER_MARKER",
44
+ "R2_TRANSFER_CALL_MARKER",
45
+ "R2_DOWNLOAD_CACHE_MARKER",
46
+ "R2_RANGE_CACHE_MARKER",
47
+ "R2_CONDITIONAL_CACHE_MARKER",
48
+ "R2_STREAMING_CACHE_MARKER",
49
+ "R2_RANGE_HEADER_CACHE_MARKER"
50
+ ],
51
+ "compiler/builtins_precompiled.js": []
52
+ },
53
+ "patchApplication": "pass",
54
+ "mismatches": [
55
+ "15-date-iso.js: request 3 mismatch: expected {\"status\":200,\"body\":\"2008-03-04T23:00:00.000Z\",\"content-type\":null}, got {\"status\":200,\"body\":\"0003-05-08T00:00:00.000Z\",\"content-type\":null}",
56
+ "16-date-parts.js: request 3 mismatch: expected {\"status\":200,\"body\":\"{\\\"year\\\":2008,\\\"month\\\":3,\\\"day\\\":4}\",\"content-type\":\"application/json;charset=utf-8\"}, got {\"status\":200,\"body\":\"{\\\"year\\\":3,\\\"month\\\":5,\\\"day\\\":8}\",\"content-type\":\"application/json;charset=utf-8\"}"
57
+ ]
58
+ }
@@ -28,12 +28,13 @@ async function fixture(): Promise<{ archive: string; sha256: string }> {
28
28
  " }\n" +
29
29
  " return -1;\n" +
30
30
  "}\n",
31
- "compiler/index.js": " '-xc', '-', '-c',\n uSocketsArchive,\n '-lm'\n",
31
+ "compiler/index.js": " ...darwinReleaseCompileArgs,\n uSocketsArchive,\n '-lm'\n",
32
32
  "compiler/precompile.js":
33
33
  "const fs = require('node:fs');\n" +
34
34
  "const path = require('node:path');\n" +
35
35
  "fs.writeFileSync(path.join(__dirname, 'builtins_precompiled.js'), fs.readFileSync(path.join(__dirname, 'builtins/typedarray.js')));\n",
36
36
  "compiler/builtins_precompiled.js": "fixture:precompiled\n",
37
+ "compiler/builtins/date.ts": "export const __ecma262_ParseDTSF = (string: string) => {\n let n: number = 0;\n let nInd: number = 0;\n\n const len: i32 = string.length;\n const endPtr: i32 = Porffor.IR.ptr(string) + len;\n let ptr: i32 = Porffor.IR.ptr(string);\n\n while (ptr <= endPtr) { // <= to include extra null byte to set last n\n const chr: i32 = Porffor.IR.loadU8(ptr++, 4);\n if (Porffor.fastAnd(chr >= 48, chr <= 57)) { // 0-9\n n *= 10;\n n += chr - 48;\n continue;\n }\n\n if (chr == 45) { // -\n if (Porffor.fastOr(ptr == Porffor.IR.ptr(string), nInd == 7)) n = -n;\n }\n\n if (n > 0) {\n if (nInd == 0) y = n;\n else if (nInd == 1) m = n - 1;\n else if (nInd == 2) dt = n;\n else if (nInd == 3) h = n;\n else if (nInd == 4) min = n;\n else if (nInd == 5) s = n;\n else if (nInd == 6) milli = n;\n else if (nInd == 7) tzHour = n;\n else if (nInd == 8) tzMin = n;\n\n n = 0;\n nInd++;\n }\n }\n\n h += tzHour;\n min += tzMin;\n};\n// RFC 7231 or Date.prototype.toString() parser\n",
37
38
  "compiler/builtins/typedarray.js":
38
39
  "export const __${name}_from = (arg: any, mapFn: any): ${name} => {\n" +
39
40
  " const arr: any[] = Porffor.array.new(4);\n" +
package/src/acquire.ts CHANGED
@@ -5,7 +5,7 @@ import { homedir } from "node:os";
5
5
  import { resolve } from "node:path";
6
6
  // @ts-expect-error Bun's file loader supplies a path; see the pinned-commit
7
7
  // check below for why this is safe to trust unconditionally.
8
- import vendoredPorfforArchive from "../vendor/porffor-8f01541.tar.gz" with { type: "file" };
8
+ import vendoredPorfforArchive from "../vendor/porffor-de4eb58.tar.gz" with { type: "file" };
9
9
  import { ensurePorfforPatched } from "./patch";
10
10
  import { PORFFOR_ARCHIVE_SHA256, PORFFOR_ARCHIVE_URL, PORFFOR_COMMIT_FULL } from "./pin";
11
11
  // Folded into the cache manifest below so a toolchain release that changes
@@ -38,6 +38,7 @@ const required = [
38
38
  "compiler/index.js",
39
39
  "compiler/uwebsockets.js",
40
40
  "compiler/precompile.js",
41
+ "compiler/builtins/date.ts",
41
42
  "compiler/builtins/typedarray.js",
42
43
  "compiler/builtins_precompiled.js",
43
44
  ];
@@ -0,0 +1,37 @@
1
+ import { expect, test } from "bun:test";
2
+ import { mkdtemp, rm, writeFile } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { resolve } from "node:path";
5
+ import { ensurePorffor } from "./acquire";
6
+
7
+ test("native ISO dates honor signed offsets, zero fields, and fractional seconds", async () => {
8
+ const root = await mkdtemp(resolve(tmpdir(), "sb-date-native-"));
9
+ const samples = [
10
+ "2024-01-02T03:04:05Z",
11
+ "2024-01-02T03:04:05+02:00",
12
+ "2024-01-02T03:04:05-05:00",
13
+ "2024-01-02T03:04:05.250+01:00",
14
+ "2024-01-02T00:00:00Z",
15
+ "2024-01-02T03:04:05.2+02:30",
16
+ "2024-01-02T03:04:05.25-00:30",
17
+ ];
18
+ try {
19
+ const porffor = await ensurePorffor({ cacheRoot: resolve(root, "cache") });
20
+ const source = resolve(root, "date.js");
21
+ const binary = resolve(root, "date-native");
22
+ await writeFile(source, `for (const value of ${JSON.stringify(samples)}) console.log(new Date(value).toISOString());\n`);
23
+ const compile = Bun.spawnSync(["bun", resolve(porffor, "runtime/index.js"), "native", source, "-o", binary, "-s"], {
24
+ cwd: root,
25
+ stdout: "pipe",
26
+ stderr: "pipe",
27
+ });
28
+ expect(compile.exitCode, Buffer.from(compile.stderr).toString()).toBe(0);
29
+ const run = Bun.spawnSync([binary], { stdout: "pipe", stderr: "pipe" });
30
+ expect(run.exitCode, Buffer.from(run.stderr).toString()).toBe(0);
31
+ expect(Buffer.from(run.stdout).toString().trim().split("\n")).toEqual(
32
+ samples.map((value) => new Date(value).toISOString()),
33
+ );
34
+ } finally {
35
+ await rm(root, { recursive: true, force: true });
36
+ }
37
+ }, 30_000);
package/src/patch.ts CHANGED
@@ -1,14 +1,16 @@
1
1
  /**
2
2
  * Idempotent, marker-guarded in-place edits to Porffor's generated C
3
3
  * (`compiler/render.js`, `compiler/index.js`, `compiler/uwebsockets.js`,
4
- * `compiler/builtins/typedarray.js`). Run
4
+ * `compiler/builtins/typedarray.js`, `compiler/builtins/date.ts`). Run
5
5
  * from the build path, not a `postinstall` hook: package managers block
6
6
  * dependency lifecycle scripts by default, so a published `postinstall` would
7
7
  * silently not run.
8
8
  *
9
9
  * Each edit is independent and re-applied on every build; a file patched by an
10
10
  * older version of this module still receives the newer edits. All of them are
11
- * tracked in patches/UPSTREAM.md and go away as Porffor closes the gaps.
11
+ * tracked in the platform's upstream notes:
12
+ * https://github.com/baronunread/sproutboat/tree/main/patches/upstream
13
+ * They go away as Porffor closes the gaps.
12
14
  */
13
15
  import { readFile, realpath, writeFile } from "node:fs/promises";
14
16
  import { resolve } from "node:path";
@@ -152,20 +154,21 @@ const RENDER_REPLACEMENTS = [
152
154
  * a musl (deploy) build overrides it outright. This splices one spread of
153
155
  * `SB_EXTRA_LINK` before `-lm`, inert unless the variable is set.
154
156
  */
155
- const LINK_ANCHOR = " uSocketsArchive,\n '-lm'\n";
157
+ const LINK_ANCHOR = " uSocketsArchive,\n '-lm'\n";
156
158
  const LINK_INJECT =
157
- " ...(process.env.SB_EXTRA_LINK ? process.env.SB_EXTRA_LINK.split(' ').filter(Boolean) : []),\n";
159
+ " ...(process.env.SB_EXTRA_LINK ? process.env.SB_EXTRA_LINK.split(' ').filter(Boolean) : []),\n";
158
160
  const LINK_MARKER = "SB_EXTRA_LINK";
159
161
 
160
162
  /**
161
163
  * #15 — and the same for the compile step, so the prelude's inline C can
162
- * `#include <bearssl.h>`. The link patch alone is not enough: Porffor compiles
163
- * the generated C from stdin with a fixed argument list, so there is otherwise
164
- * no way to add an include path.
164
+ * `#include <bearssl.h>`. The link patch alone is not enough: Porffor's
165
+ * module builds compile several C units with a fixed argument list, so there
166
+ * is otherwise no way to add an include path. Add the flag before the cache
167
+ * stamp is computed so a change invalidates compiled units.
165
168
  */
166
- const CFLAGS_ANCHOR = " '-xc', '-', '-c',\n";
169
+ const CFLAGS_ANCHOR = " ...darwinReleaseCompileArgs,\n";
167
170
  const CFLAGS_INJECT =
168
- " ...(process.env.SB_EXTRA_CFLAGS ? process.env.SB_EXTRA_CFLAGS.split(' ').filter(Boolean) : []),\n";
171
+ " ...(process.env.SB_EXTRA_CFLAGS ? process.env.SB_EXTRA_CFLAGS.split(' ').filter(Boolean) : []),\n";
169
172
  const CFLAGS_MARKER = "SB_EXTRA_CFLAGS";
170
173
 
171
174
  // Porffor's TypedArray.from only handles iterables. An array-like input such
@@ -190,6 +193,93 @@ const TYPED_ARRAY_FROM_INJECT =
190
193
  " }\n\n arr.length = len;\n\n return new ${name}(arr);\n};";
191
194
  const TYPED_ARRAY_FROM_MARKER = "sproutboat: TypedArray.from accepts array-like objects";
192
195
 
196
+ // Porffor alpha 9 treats +02:00 as two milliseconds and adds offset hours
197
+ // instead of subtracting them. Count fields even when they contain zero, and
198
+ // apply the signed offset after parsing the local wall-clock fields.
199
+ const DATE_PARSER_MARKER = "sproutboat: ISO timezone offset";
200
+ const DATE_PARSER_ANCHOR = ` let n: number = 0;
201
+ let nInd: number = 0;
202
+
203
+ const len: i32 = string.length;
204
+ const endPtr: i32 = Porffor.IR.ptr(string) + len;
205
+ let ptr: i32 = Porffor.IR.ptr(string);
206
+
207
+ while (ptr <= endPtr) { // <= to include extra null byte to set last n
208
+ const chr: i32 = Porffor.IR.loadU8(ptr++, 4);
209
+ if (Porffor.fastAnd(chr >= 48, chr <= 57)) { // 0-9
210
+ n *= 10;
211
+ n += chr - 48;
212
+ continue;
213
+ }
214
+
215
+ if (chr == 45) { // -
216
+ if (Porffor.fastOr(ptr == Porffor.IR.ptr(string), nInd == 7)) n = -n;
217
+ }
218
+
219
+ if (n > 0) {
220
+ if (nInd == 0) y = n;
221
+ else if (nInd == 1) m = n - 1;
222
+ else if (nInd == 2) dt = n;
223
+ else if (nInd == 3) h = n;
224
+ else if (nInd == 4) min = n;
225
+ else if (nInd == 5) s = n;
226
+ else if (nInd == 6) milli = n;
227
+ else if (nInd == 7) tzHour = n;
228
+ else if (nInd == 8) tzMin = n;
229
+
230
+ n = 0;
231
+ nInd++;
232
+ }
233
+ }
234
+
235
+ h += tzHour;
236
+ min += tzMin;`;
237
+ const DATE_PARSER_INJECT = ` // sproutboat: ISO timezone offset
238
+ let n: number = 0;
239
+ let nInd: number = 0;
240
+ let digits: i32 = 0;
241
+ let offsetSign: i32 = 0;
242
+
243
+ const len: i32 = string.length;
244
+ const endPtr: i32 = Porffor.IR.ptr(string) + len;
245
+ let ptr: i32 = Porffor.IR.ptr(string);
246
+
247
+ while (ptr <= endPtr) {
248
+ const chr: i32 = Porffor.IR.loadU8(ptr++, 4);
249
+ if (Porffor.fastAnd(chr >= 48, chr <= 57)) {
250
+ n = n * 10 + chr - 48;
251
+ digits++;
252
+ continue;
253
+ }
254
+
255
+ if (digits > 0) {
256
+ if (nInd == 0) y = n;
257
+ else if (nInd == 1) m = n - 1;
258
+ else if (nInd == 2) dt = n;
259
+ else if (nInd == 3) h = n;
260
+ else if (nInd == 4) min = n;
261
+ else if (nInd == 5) s = n;
262
+ else if (nInd == 6) {
263
+ while (digits < 3) { n *= 10; digits++; }
264
+ while (digits > 3) { n = Math.trunc(n / 10); digits--; }
265
+ milli = n;
266
+ }
267
+ else if (nInd == 7) tzHour = n;
268
+ else if (nInd == 8) tzMin = n;
269
+ n = 0;
270
+ nInd++;
271
+ digits = 0;
272
+ }
273
+
274
+ if (Porffor.fastOr(chr == 43, chr == 45) && nInd >= 6) {
275
+ offsetSign = chr == 43 ? 1 : -1;
276
+ nInd = 7;
277
+ }
278
+ }
279
+
280
+ h -= offsetSign * tzHour;
281
+ min -= offsetSign * tzMin;`;
282
+
193
283
  /**
194
284
  * #56 — make the inbound request-body limit configurable.
195
285
  *
@@ -817,7 +907,7 @@ export async function patchUwebsockets(root: string): Promise<void> {
817
907
  if (!src.includes(anchor)) {
818
908
  throw new Error(
819
909
  `could not patch Porffor's ${what}: anchor not found in ${file}. ` +
820
- "Porffor's uWebSockets shim changed — check patches/UPSTREAM.md.",
910
+ "Porffor's uWebSockets shim changed. See https://github.com/baronunread/sproutboat/tree/main/patches/upstream.",
821
911
  );
822
912
  }
823
913
  src = src.replace(anchor, inject);
@@ -839,11 +929,11 @@ async function patchCompilerArgs(root: string): Promise<void> {
839
929
  if (at === -1) {
840
930
  throw new Error(
841
931
  `could not patch Porffor for ${what}: anchor not found in ${file}. ` +
842
- "Porffor's native-fetch build changed — check patches/UPSTREAM.md.",
932
+ "Porffor's native-fetch build changed. See https://github.com/baronunread/sproutboat/tree/main/patches/upstream.",
843
933
  );
844
934
  }
845
- // After the anchor for cflags (the args follow it), before it for the link
846
- // line (the object list ends with it).
935
+ // Add flags to compileOnlyArgs so every generated C unit sees them and
936
+ // Porffor's module-build cache includes them in its compiler command.
847
937
  src =
848
938
  marker === CFLAGS_MARKER
849
939
  ? src.slice(0, at + anchor.length) + inject + src.slice(at + anchor.length)
@@ -853,6 +943,19 @@ async function patchCompilerArgs(root: string): Promise<void> {
853
943
  if (changed) await writeFile(file, src);
854
944
  }
855
945
 
946
+ export async function patchDateParser(root: string): Promise<void> {
947
+ const file = resolve(root, "compiler/builtins/date.ts");
948
+ const src = await readFile(file, "utf8");
949
+ if (src.includes(DATE_PARSER_MARKER)) return;
950
+ const start = src.indexOf("export const __ecma262_ParseDTSF");
951
+ const end = src.indexOf("// RFC 7231 or Date.prototype.toString() parser", start);
952
+ if (start < 0 || end < 0) throw new Error(`could not find Porffor's ISO date parser in ${file}`);
953
+ const parser = src.slice(start, end);
954
+ if (!parser.includes(DATE_PARSER_ANCHOR))
955
+ throw new Error(`could not patch Porffor's ISO date parser: anchor not found in ${file}`);
956
+ await writeFile(file, src.slice(0, start) + parser.replace(DATE_PARSER_ANCHOR, DATE_PARSER_INJECT) + src.slice(end));
957
+ }
958
+
856
959
  export async function patchTypedArrayFrom(root: string): Promise<void> {
857
960
  const file = resolve(root, "compiler/builtins/typedarray.js");
858
961
  const src = await readFile(file, "utf8");
@@ -892,7 +995,7 @@ export async function patchRenderJs(root: string): Promise<void> {
892
995
  if (at === -1) {
893
996
  throw new Error(
894
997
  `could not patch Porffor for ${what}: anchor not found in ${file}. ` +
895
- "Porffor's native-fetch renderer changed — check patches/UPSTREAM.md.",
998
+ "Porffor's native-fetch renderer changed. See https://github.com/baronunread/sproutboat/tree/main/patches/upstream.",
896
999
  );
897
1000
  }
898
1001
  src = src.slice(0, at + anchor.length) + inject + src.slice(at + anchor.length);
@@ -906,7 +1009,7 @@ export async function patchRenderJs(root: string): Promise<void> {
906
1009
  if (!src.includes(anchor)) {
907
1010
  throw new Error(
908
1011
  `could not patch Porffor for ${what}: anchor not found in ${file}. ` +
909
- "Porffor's native-fetch renderer changed — check patches/UPSTREAM.md.",
1012
+ "Porffor's native-fetch renderer changed. See https://github.com/baronunread/sproutboat/tree/main/patches/upstream.",
910
1013
  );
911
1014
  }
912
1015
  src = src.replace(anchor, inject);
@@ -927,6 +1030,7 @@ export async function ensurePorfforPatched(root: string): Promise<void> {
927
1030
  await patchCompilerArgs(root);
928
1031
  await patchUwebsockets(root);
929
1032
  await patchRenderJs(root);
1033
+ await patchDateParser(root);
930
1034
  await patchTypedArrayFrom(root);
931
1035
  done.add(root);
932
1036
  }
package/src/pin.ts CHANGED
@@ -11,14 +11,14 @@
11
11
  * uWebSockets archive in `sproutboat-cli/vendor/` — then run both repos'
12
12
  * test + conformance suites. See sproutboat-cli/MIGRATION.md.
13
13
  */
14
- export const PORFFOR_CHANNEL = "alpha-7";
15
- export const PORFFOR_COMMIT_FULL = "8f01541498d6d61c0cbbf8a71be152330888be7e";
14
+ export const PORFFOR_CHANNEL = "alpha-9";
15
+ export const PORFFOR_COMMIT_FULL = "de4eb588264885b3a1596f75010e371a2052033f";
16
16
  export const PORFFOR_COMMIT = PORFFOR_COMMIT_FULL.slice(0, 7);
17
17
  export const PORFFOR_ARCHIVE_SHA256 =
18
- "fae9cfb00c0a21e20e3aa79a9a8093bd24820062039985c6bde2b23e3e8bdb8c";
18
+ "1a62187a93356b36cb6ac703ce9770c8917e4fa65545fd7c33ee8a180ae5a4c9";
19
19
  export const PORFFOR_ARCHIVE_URL = `https://codeload.github.com/CanadaHonk/porffor/tar.gz/${PORFFOR_COMMIT_FULL}`;
20
20
 
21
- /** A compact identity string for a manifest / report (`alpha-7 (8f01541)`). */
21
+ /** A compact identity string for a manifest / report (`alpha-9 (de4eb58)`). */
22
22
  export function porfforVersion(): string {
23
23
  return process.env.PORFFOR_VERSION || `${PORFFOR_CHANNEL} (${PORFFOR_COMMIT})`;
24
24
  }
Binary file
Binary file