@absolutejs/absolute 0.19.0-beta.124 → 0.19.0-beta.126

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.
@@ -243,7 +243,11 @@
243
243
  "WebFetch(domain:developer.chrome.com)",
244
244
  "Bash(~/alex/bun-combined-patch/build/release/bun h2-benchmark.ts 3000)",
245
245
  "Bash(~/alex/bun-combined-patch/build/release/bun h2-benchmark.ts 3000 https)",
246
- "Bash(fuser -k 3000/tcp)"
246
+ "Bash(fuser -k 3000/tcp)",
247
+ "Bash(ALPN_BUN=~/alex/bun-alpn-patch/build/release/bun)",
248
+ "Bash(__NEW_LINE_54a38eed241da675__ echo:*)",
249
+ "Bash(__NEW_LINE_54a38eed241da675__ sleep:*)",
250
+ "Bash(timeout 5 $ALPN_BUN -e \":*)"
247
251
  ]
248
252
  }
249
253
  }
package/HTTP2_STATUS.md CHANGED
@@ -1,81 +1,44 @@
1
1
  # HTTP/2 Dev Server — Status & Roadmap
2
2
 
3
3
  ## Goal
4
- Serve dev module fetches (/@src/, vendor, etc.) over HTTP/2 multiplexed connections
5
- to eliminate the HTTP/1.1 6-connection bottleneck on import-heavy pages.
4
+ Serve dev module fetches over HTTP/2 multiplexed connections when configured to eliminate the
5
+ HTTP/1.1 6-connection bottleneck on import-heavy pages.
6
6
 
7
- ## What's Built & Working
7
+ ## What's Built & Ready
8
8
 
9
- ### HTTPS / TLS (shipping now)
9
+ ### HTTPS / TLS (shipping)
10
10
  - `dev: { https: true }` config option
11
11
  - `src/dev/devCert.ts` — mkcert + self-signed cert generation
12
12
  - `src/plugins/networking.ts` — Bun.serve with TLS when HTTPS enabled
13
13
  - `src/cli/scripts/dev.ts` — passes `ABSOLUTE_HTTPS=true` to server process
14
14
 
15
- ### HTTP/2 Plumbing (ready, waiting on Bun)
15
+ ### HTTP/2 Plumbing (waiting on Bun)
16
16
  - `src/core/prepare.ts` — exposes `globalThis.__http2Config` when `dev.https` is enabled
17
- - `src/plugins/hmr.ts` — skips Elysia `.ws('/hmr')` when `__http2Config` is set (h2 mode handles WS differently)
17
+ - `src/plugins/hmr.ts` — skips Elysia `.ws('/hmr')` when `__http2Config` is set (h2 mode uses RFC 8441 WebSocket instead)
18
18
  - `types/globals.d.ts` — `__http2Config` type declaration
19
19
  - `types/build.ts` — `dev.https` config type
20
20
 
21
- ### RFC 8441 WebSocket over HTTP/2 (tested, not shipping)
22
- We built and tested WebSocket over HTTP/2 via Extended CONNECT (RFC 8441).
23
- This runs WebSocket as a multiplexed h2 stream — no separate HTTP/1.1 connection.
24
- - Browser sends `:method: CONNECT` + `:protocol: websocket` on an h2 stream
25
- - Server responds `:status: 200`, stream becomes bidirectional WebSocket
26
- - Minimal WebSocket frame parser/writer handles text messages for HMR
27
- - Confirmed working with Chrome via Playwright
28
-
29
- ### Bun Patch: enableConnectProtocol (PR-ready)
30
- **Repo:** `~/alex/bun-http2-patch` (branch: `feat/http2-enable-connect-protocol`)
31
-
32
- **The bug:** `session.settings({ enableConnectProtocol: true })` is validated in JS
33
- and the Zig struct has the field, but `loadSettingsFromJSValue()` in
34
- `src/bun.js/api/bun/h2_frame_parser.zig` silently ignores it. The setting is never
35
- sent in the SETTINGS frame, so browsers never try Extended CONNECT.
36
-
37
- **The fix:** 7 lines in `loadSettingsFromJSValue()` following the exact `enablePush` pattern.
38
-
39
- **Status:** Built, tested, confirmed working. Ready for PR to oven-sh/bun.
40
-
41
21
  ## What's Blocking
42
22
 
43
23
  ### Bun Issue #14672 — HTTP/2 for Bun.serve()
44
- **This is the critical blocker.** Currently `Bun.serve()` only speaks HTTP/1.1.
45
- Our JS-level bridge (`node:http2` `app.fetch()` `arrayBuffer()` `stream.end()`)
46
- works but adds per-request overhead that negates the multiplexing gain at scale:
47
-
48
- | Metric | HTTP/1.1 (Bun.serve) | HTTP/2 (JS bridge) |
49
- |---|---|---|
50
- | Resource fetch sum (250 resources) | 21,626ms | **4,310ms** (5x better) |
51
- | Page load wall time | **~2,800ms** | ~22,000ms (8x worse) |
52
-
53
- The h2 multiplexing works (5x faster aggregate fetch), but the JS bridge overhead
54
- per-request makes overall load 8x slower than Bun.serve's native Zig path.
24
+ **Critical blocker.** `Bun.serve()` only speaks HTTP/1.1. Using `node:http2`
25
+ as a JS-level bridge works but the per-request overhead through `app.fetch()`
26
+ negates the multiplexing gain at scale. HTTP/2 needs to happen at the native
27
+ Bun.serve level.
55
28
 
56
29
  **Track:** https://github.com/oven-sh/bun/issues/14672
57
30
 
58
- ### Bun Issue #26721allowHTTP1 broken on node:http2
59
- `allowHTTP1: true` doesn't advertise `http/1.1` in ALPN, so HTTP/1.1 fallback
60
- for WebSocket upgrade never works. Our RFC 8441 approach bypasses this entirely,
61
- but it's relevant if someone wants the traditional `ws` upgrade path.
62
-
63
- **Track:** https://github.com/oven-sh/bun/issues/26721
31
+ ### Bun PR #28581enableConnectProtocol SETTINGS
32
+ `session.settings({ enableConnectProtocol: true })` is silently ignored
33
+ the setting never reaches the SETTINGS frame. Browsers require
34
+ `SETTINGS_ENABLE_CONNECT_PROTOCOL=1` before using RFC 8441 Extended CONNECT
35
+ for WebSocket over HTTP/2.
64
36
 
65
- ### Bun Quirk — listen(port, hostname) breaks ALPN
66
- Passing a hostname to `server.listen(port, hostname)` on a `node:http2` server
67
- causes ALPN negotiation to fail. `server.listen(port)` works fine.
68
- Not filed as an issue yet.
37
+ **Track:** https://github.com/oven-sh/bun/pull/28581
69
38
 
70
39
  ## When Bun.serve Gets HTTP/2
71
40
 
72
- Once #14672 lands, the path to enable HTTP/2 is:
73
-
74
41
  1. **networking.ts** — pass h2 option to `app.listen()` / `Bun.serve()` config
75
- 2. **hmr.ts** — the `__http2Config` check already skips `.ws()` in h2 mode
76
- 3. **WebSocket** — use RFC 8441 Extended CONNECT (requires enableConnectProtocol patch or Bun fixing it natively)
77
- 4. Remove the `__http2Config` bridge pattern if Bun.serve handles h2 + WS natively
78
-
79
- ## Combined Patch Build
80
- **Repo:** `~/alex/bun-combined-patch` (both reactFastRefresh + enableConnectProtocol)
81
- **Script:** `scripts/use-combined-bun.sh`
42
+ 2. **hmr.ts** — `__http2Config` check already skips `.ws()` in h2 mode
43
+ 3. **WebSocket** — use RFC 8441 Extended CONNECT (requires #28581 or Bun adding it natively)
44
+ 4. Remove `__http2Config` pattern if Bun.serve handles h2 + WS natively
package/dist/build.js CHANGED
@@ -171170,7 +171170,7 @@ import {
171170
171170
  rmSync,
171171
171171
  writeFileSync as writeFileSync3
171172
171172
  } from "fs";
171173
- import { basename as basename5, join as join13, relative as relative7, resolve as resolve10 } from "path";
171173
+ import { basename as basename5, dirname as dirname6, join as join13, relative as relative7, resolve as resolve10 } from "path";
171174
171174
  import { cwd, env as env2, exit } from "process";
171175
171175
  var {build: bunBuild6, Glob: Glob5 } = globalThis.Bun;
171176
171176
  var isDev, extractBuildError = (logs, pass, label, frameworkNames, isIncremental, throwOnError) => {
@@ -171220,6 +171220,28 @@ var isDev, extractBuildError = (logs, pass, label, frameworkNames, isIncremental
171220
171220
  globalThis.__absoluteVersion = pkg.version;
171221
171221
  return;
171222
171222
  }
171223
+ }, scanWorkerReferences = async (dirs) => {
171224
+ const urlPattern = /new\s+URL\(\s*["'](\.\.?\/[^"']+)["']\s*,\s*import\.meta\.url\s*\)/g;
171225
+ const resolvePattern = /import\.meta\.resolve\(\s*["'](\.\.?\/[^"']+)["']\s*\)/g;
171226
+ const workerPaths = new Set;
171227
+ for (const dir of dirs) {
171228
+ const glob = new Glob5("**/*.{ts,tsx,js,jsx}");
171229
+ for await (const file3 of glob.scan({ absolute: true, cwd: dir })) {
171230
+ const content = readFileSync5(file3, "utf-8");
171231
+ for (const pattern of [urlPattern, resolvePattern]) {
171232
+ pattern.lastIndex = 0;
171233
+ let match;
171234
+ while ((match = pattern.exec(content)) !== null) {
171235
+ const relPath = match[1];
171236
+ if (!relPath)
171237
+ continue;
171238
+ const absPath = resolve10(file3, "..", relPath);
171239
+ workerPaths.add(absPath);
171240
+ }
171241
+ }
171242
+ }
171243
+ }
171244
+ return [...workerPaths];
171223
171245
  }, vueFeatureFlags, build = async ({
171224
171246
  buildDirectory = "build",
171225
171247
  assetsDirectory,
@@ -171632,6 +171654,68 @@ var isDev, extractBuildError = (logs, pass, label, frameworkNames, isIncremental
171632
171654
  if (vueCssResult && !vueCssResult.success && vueCssResult.logs.length > 0) {
171633
171655
  extractBuildError(vueCssResult.logs, "vue-css", "Vue CSS", frameworkNames, isIncremental, throwOnError);
171634
171656
  }
171657
+ const frameworkDirs = [
171658
+ reactDir,
171659
+ svelteDir,
171660
+ vueDir,
171661
+ angularDir,
171662
+ htmlDir,
171663
+ htmxDir
171664
+ ].filter((d) => Boolean(d));
171665
+ const workerEntryPoints = await scanWorkerReferences(frameworkDirs);
171666
+ let workerOutputs = [];
171667
+ if (workerEntryPoints.length > 0) {
171668
+ const workerResult = await bunBuild6({
171669
+ entrypoints: workerEntryPoints,
171670
+ format: "esm",
171671
+ minify: !isDev,
171672
+ naming: "[dir]/[name].[hash].[ext]",
171673
+ outdir: buildPath,
171674
+ root: commonAncestor(workerEntryPoints.map((p) => dirname6(p))),
171675
+ target: "browser",
171676
+ throw: false
171677
+ });
171678
+ if (workerResult.success) {
171679
+ workerOutputs = workerResult.outputs;
171680
+ const workerUrlMap = new Map;
171681
+ for (const artifact of workerOutputs) {
171682
+ for (const ep of workerEntryPoints) {
171683
+ const epBase = basename5(ep).replace(/\.[^.]+$/, "");
171684
+ if (basename5(artifact.path).startsWith(epBase)) {
171685
+ workerUrlMap.set(ep, "/" + relative7(buildPath, artifact.path));
171686
+ }
171687
+ }
171688
+ }
171689
+ const allClientOutputPaths = [
171690
+ ...reactClientOutputPaths,
171691
+ ...nonReactClientOutputs.map((a) => a.path)
171692
+ ];
171693
+ for (const outputPath of allClientOutputPaths) {
171694
+ let content = readFileSync5(outputPath, "utf-8");
171695
+ let changed = false;
171696
+ for (const [srcPath, hashedPath] of workerUrlMap) {
171697
+ const srcRelPatterns = [
171698
+ "./" + relative7(buildPath, srcPath),
171699
+ "../" + relative7(buildPath, srcPath),
171700
+ relative7(buildPath, srcPath)
171701
+ ];
171702
+ for (const pattern of srcRelPatterns) {
171703
+ if (content.includes(pattern)) {
171704
+ content = content.replaceAll(pattern, hashedPath);
171705
+ changed = true;
171706
+ }
171707
+ }
171708
+ }
171709
+ if (changed)
171710
+ writeFileSync3(outputPath, content);
171711
+ }
171712
+ } else {
171713
+ const workerLogs = workerResult.logs;
171714
+ if (workerLogs.length > 0) {
171715
+ extractBuildError(workerLogs, "worker", "Worker", frameworkNames, isIncremental, throwOnError);
171716
+ }
171717
+ }
171718
+ }
171635
171719
  const allLogs = [
171636
171720
  ...serverLogs,
171637
171721
  ...reactClientLogs,
@@ -171645,7 +171729,8 @@ var isDev, extractBuildError = (logs, pass, label, frameworkNames, isIncremental
171645
171729
  ...serverOutputs,
171646
171730
  ...reactClientOutputs,
171647
171731
  ...nonReactClientOutputs,
171648
- ...cssOutputs
171732
+ ...cssOutputs,
171733
+ ...workerOutputs
171649
171734
  ], buildPath)
171650
171735
  };
171651
171736
  for (const artifact of serverOutputs) {
@@ -172793,7 +172878,7 @@ var init_registerClientScript = __esm(() => {
172793
172878
 
172794
172879
  // src/angular/injectorPatch.ts
172795
172880
  import { readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
172796
- import { dirname as dirname6, join as join15 } from "path";
172881
+ import { dirname as dirname7, join as join15 } from "path";
172797
172882
  var applyInjectorPatch = (chunkPath, content) => {
172798
172883
  if (content.includes('Symbol.for("angular.currentInjector")')) {
172799
172884
  return;
@@ -172830,7 +172915,7 @@ var applyInjectorPatch = (chunkPath, content) => {
172830
172915
  writeFileSync4(chunkPath, patched, "utf-8");
172831
172916
  }, patchAngularInjectorSingleton = () => {
172832
172917
  try {
172833
- const coreDir = dirname6(__require.resolve("@angular/core/package.json"));
172918
+ const coreDir = dirname7(__require.resolve("@angular/core/package.json"));
172834
172919
  const chunkPath = join15(coreDir, "fesm2022", "_not_found-chunk.mjs");
172835
172920
  const content = readFileSync8(chunkPath, "utf-8");
172836
172921
  applyInjectorPatch(chunkPath, content);
@@ -202424,7 +202509,7 @@ __export(exports_moduleServer, {
202424
202509
  SRC_URL_PREFIX: () => SRC_URL_PREFIX
202425
202510
  });
202426
202511
  import { existsSync as existsSync13, readFileSync as readFileSync9, statSync } from "fs";
202427
- import { basename as basename7, dirname as dirname7, extname as extname3, resolve as resolve17, relative as relative8 } from "path";
202512
+ import { basename as basename7, dirname as dirname8, extname as extname3, resolve as resolve17, relative as relative8 } from "path";
202428
202513
  var SRC_PREFIX = "/@src/", jsTranspiler2, tsTranspiler2, tsxTranspiler, TRANSPILABLE, ALL_EXPORTS_RE, STRING_CONTENTS_RE, preserveTypeExports = (originalSource, transpiled, valueExports) => {
202429
202514
  const codeOnly = originalSource.replace(STRING_CONTENTS_RE, '""');
202430
202515
  const allExports = [];
@@ -202484,7 +202569,7 @@ ${stubs}
202484
202569
  };
202485
202570
  result = result.replace(/^((?:import\s+.+?\s+from|export\s+.+?\s+from|import)\s*["'])([^"'./][^"']*)(["'])/gm, stubReplace);
202486
202571
  result = result.replace(/(import\s*\(\s*["'])([^"'./][^"']*)(["']\s*\))/g, stubReplace);
202487
- const fileDir = dirname7(filePath);
202572
+ const fileDir = dirname8(filePath);
202488
202573
  result = result.replace(/(from\s*["'])(\.\.?\/[^"']+)(["'])/g, (_match, prefix, relPath, suffix) => {
202489
202574
  const absPath = resolve17(fileDir, relPath);
202490
202575
  const rel = relative8(projectRoot, absPath);
@@ -202553,6 +202638,16 @@ ${stubs}
202553
202638
  }
202554
202639
  return _match;
202555
202640
  });
202641
+ result = result.replace(/new\s+URL\(\s*["'](\.\.?\/[^"']+)["']\s*,\s*import\.meta\.url\s*\)/g, (_match, relPath) => {
202642
+ const absPath = resolve17(fileDir, relPath);
202643
+ const rel = relative8(projectRoot, absPath);
202644
+ return `new URL('/${srcUrl(rel, projectRoot)}', import.meta.url)`;
202645
+ });
202646
+ result = result.replace(/import\.meta\.resolve\(\s*["'](\.\.?\/[^"']+)["']\s*\)/g, (_match, relPath) => {
202647
+ const absPath = resolve17(fileDir, relPath);
202648
+ const rel = relative8(projectRoot, absPath);
202649
+ return `'${srcUrl(rel, projectRoot)}'`;
202650
+ });
202556
202651
  return result;
202557
202652
  }, HOOK_NAMES, REFRESH_PREAMBLE, JSX_AUTO_RE, JSXS_AUTO_RE, JSX_PROD_RE, FRAGMENT_RE, addJsxImport = (code) => {
202558
202653
  const imports = [];
@@ -204938,5 +205033,5 @@ export {
204938
205033
  build
204939
205034
  };
204940
205035
 
204941
- //# debugId=64632B059519A68864756E2164756E21
205036
+ //# debugId=3A7A7808BFA71C3264756E2164756E21
204942
205037
  //# sourceMappingURL=build.js.map