@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.
- package/.absolutejs/tsconfig.tsbuildinfo +1 -1
- package/.claude/settings.local.json +5 -1
- package/HTTP2_STATUS.md +19 -56
- package/dist/build.js +102 -7
- package/dist/build.js.map +4 -4
- package/dist/index.js +102 -7
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
|
@@ -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
|
|
5
|
-
|
|
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 &
|
|
7
|
+
## What's Built & Ready
|
|
8
8
|
|
|
9
|
-
### HTTPS / TLS (shipping
|
|
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 (
|
|
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
|
|
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
|
-
**
|
|
45
|
-
|
|
46
|
-
|
|
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
|
|
59
|
-
`
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
**Track:** https://github.com/oven-sh/bun/issues/26721
|
|
31
|
+
### Bun PR #28581 — enableConnectProtocol 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
|
-
|
|
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** —
|
|
76
|
-
3. **WebSocket** — use RFC 8441 Extended CONNECT (requires
|
|
77
|
-
4. Remove
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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=
|
|
205036
|
+
//# debugId=3A7A7808BFA71C3264756E2164756E21
|
|
204942
205037
|
//# sourceMappingURL=build.js.map
|