Skip to content

Commit 9c4294e

Browse files
committed
better HTTP caching; dom shim anti-collision; other tweaks
--- `package.json`: Added the missing default entry point `./all.mjs` for the `.exports` directive which takes priority over `.main`. --- `http`: When using bundlers, this pseudo-module now resolves to `http.mjs` in browsers. This can be convenient for code shared between server and client. Revised and improved compression and file resolution tools. This necessitated some breakage. All changes below are breaking. Internal changes which shouldn't affect user code are not listed. `HttpCompressor`: - Caching support has been moved to `HttpFile`/`HttpDir`. - The mapping of FS paths to files is now done just once, at the level of `HttpDirs`, instead of two different mappings before. - Caching of compressed artifacts per algorithm is done at the level of `HttpFile`. - Constructor now accepts algo options, allowing to override them without subclassing. - Added a dedicated compression method for each algo. - Added a dedicated decompression method for each algo. - Added `.decompress`. - Compression methods no longer take algo option overrides; options are defined once, at the constructor level. - `.compress` now takes an options object instead of multiple args. `HttpDirs`: - Renamed `.resolveFile` → `.resolve`. - Fixed a bug where "not found" paths were cached, which can be a memory leak. Only "found" paths should be cached now. - Added `.cacheFiles` for virtual files. - Caching is now enabled via `.setOpt({caching: true})`. - When caching is enabled, resolved files are now deduplicated by `.fsPath`. When multiple URL paths resolve to the same FS path, only one `HttpFile` is created and stored, and its body is no longer duplicated in RAM. `HttpDir`: - Renamed `.resolveFile` → `.resolve`. - No longer supports resolving directories; only files. `HttpFile`: - Has a bit more focus on caching and deduplication. All "get"-style methods do their work only once, and reuse previous results whenever possible. The only exception is `.resOpt`. - Constructor takes an options object, which must contain `.fsPath` and `.urlPath`. Any file property can be provided in these options, including `.text` and `.bytes`. This makes it easy to construct "virtual" files; those can now be provided to `HttpDirs` and resolved just like "real" files. - Renamed `.opt` → `.resOpt`. - `HttpFile.resolve` now supports only files and returns nil for directory paths. - Now integrates compression caching. - Compressed file responses now always have `vary: accept-encoding`. Deno sets this automatically, but Bun doesn't. `ErrHttp` now takes `(msg: string, opt?: Record)`, exactly like `Error`. Status and/or response can be provided in the options. Removed `getStatus`. Renaming: - `arrHex` → `byteArrHex` - `uuidArr` → `uuidByteArr` Added `fileResponse` which conveniently combines a live client if any, a compressor if any, and an actual file response, in proper order. Added optional polyfills for Bun: - `CompressionStreamPolyfill` for `CompressionStream` - `DecompressionStreamPolyfill` for `DecompressionStream` Added `toByteArr`. --- `http_live.mjs`: - `LiveClient`: - Breaking: renamed `.withLiveScript` → `.liveResponse` - Added `.liveHtml`. - Breaking: dropped `withLiveScript`; use `fileResponse` instead. Now supports a "hot" mode. When enabled, `LiveClient` now migrates its set of known served files between hot reloads. At the time of writing, this only works in Bun with `--hot`. --- `io`: Added: - `mkdirTemp` - `mkdirTempSync` - `rename` - `renameSync` `io_bun.mjs`: passing `{recursive: true}` to `remove` and `removeSync` now causes directory removal as expected. Previously, it would try to remove a file instead. --- `dom_shim.mjs`: All internal methods are now namespaced with `domShim_` to avoid collisions with user-defined methods. Overrides are still possible. With very few exceptions, internal properties are accessed directly, bypassing getters and setters. This is more consistent with the standard DOM API and makes the behavior more predictable for user code which overrides any of the DOM getters and setters for its own needs. Breaking: removed: - `Element..attributes` - `NamedNodeMap` - `Attr` We never needed those APIs, and they just carry overhead. Fixed an edge case where the `Element..style` object could become inconsistent with a style attribute. Any code which uses only the renderer in `prax.mjs`, avoids using `Element..attributes`, and avoids accessing / overriding / colliding with any internals of the DOM shim, should be unaffected. Breaking: removed `.toJSON` from all DOM classes. There was never any particular reason for adding that. Reason for removing it: sometimes it's possible to have an edge case where external code overrides `.outerHTML` to include additional things into HTML, which is perfectly valid and sometimes useful, while accidentally and erroneously trying to JSON-encode some of our shimmed elements, which used to call `.outerHTML` by default, leading to infinite recursion and a stack overflow. --- `dom.mjs`: Breaking: the following functions now take only one argument, which must be an options object: - `eventDispatch` - `eventListen` - `ListenRef` Motivation for `ListenRef`: when a function has 5 arguments, it can be hard / error-prone / unmaintainable to remember what goes where. Positional arity should be limited to 1 or 2. Beyond that, functions should take an options dictionary where every argument is named. Motivation for the other functions: semantic association. Restored `ChildDiff`. Its main use case is updating `document.head` on pushstate transitions, which must be done without removing any "static" head elements, such as global page styles. Our renderer in `prax.mjs` is often unsuitable for this since it's order-sensitive. `document.head` is mostly order-insensitive; by ignoring the order, its diffing can be done in a very simple manner by this tool. --- `obs.mjs`: Added `toObs`. --- `iter.mjs`: `indexOf` now prefers native `Array.prototype.indexOf` when possible, which performs better than a custom loop in modern engines. When not possible, it falls back on a custom loop. --- `lang.mjs`: Breaking: `reset` now returns the target, not the value. We know that this is inconsistent with `clojure.core.reset!`, on which this was modeled. The previous behavior returned the provided value, not the value actually retained by the target reference. Since we implement `reset` as a property set rather than a method call, within the JS semantics we can't get the retained value for free; an assignment expression always returns the provided value, which is available to the caller in any case. Returning the target reference aligns more closely with `assign` and `patch`, which is useful because `ObsPh` implements `reset` as an equivalent of `assign`. Added `resetOpt`.
1 parent 069d181 commit 9c4294e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+2624
-1901
lines changed

.eslint.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export default [
102102
[`no-unused-vars`]: [`error`, {argsIgnorePattern: `^_`, varsIgnorePattern: `^_`}],
103103
[`no-useless-catch`]: `error`,
104104
[`no-useless-escape`]: `error`,
105-
[`no-warning-comments`]: [`error`, {location: `anywhere`, terms: [`FIXME`]}],
105+
[`no-warning-comments`]: [`error`, {location: `anywhere`, terms: [`F`+`I`+`X`+`M`+`E`]}],
106106
[`no-with`]: `error`,
107107
[`object-curly-spacing`]: [`error`, `never`],
108108
[`require-yield`]: `error`,

doc/cmd_doc.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const FEATS = [
3434
[`http`, `shortcuts for the fetch/Response APIs, URL routing, cookie decoding/encoding.`],
3535
[`http_bun`, `tools for HTTP servers running in Bun.`],
3636
[`http_deno`, `tools for HTTP servers running in Deno.`],
37-
[`http_shared`, `streaming and broadcasting tools for generic HTTP servers.`],
37+
[`http_srv`, `streaming and broadcasting tools for generic HTTP servers.`],
3838
[`http_live`, `tools for live-reloading in development.`],
3939
[`cli`, `essential tools for CLI apps.`],
4040
[`test`, `tools for testing and benchmarking.`],

doc/cmd_doc_async.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const FEATS = [
3939
[`http`, `shortcuts for the fetch/Response APIs, URL routing, cookie decoding/encoding.`],
4040
[`http_bun`, `tools for HTTP servers running in Bun.`],
4141
[`http_deno`, `tools for HTTP servers running in Deno.`],
42-
[`http_shared`, `streaming and broadcasting tools for generic HTTP servers.`],
42+
[`http_srv`, `streaming and broadcasting tools for generic HTTP servers.`],
4343
[`http_live`, `tools for live-reloading in development.`],
4444
[`cli`, `essential tools for CLI apps.`],
4545
[`test`, `tools for testing and benchmarking.`],

doc/http/_ErrHttp.md

Lines changed: 0 additions & 11 deletions
This file was deleted.

doc/http_bun_readme.md

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,56 @@ Also see {{featLink http}} for routing and cookies, {{featLink http_deno}} for D
1919
Simple example of a server that serves files from the current directory, automatically matching URL paths to HTML files:
2020

2121
```js
22-
import * as hb from '@mitranim/js/http_bun.mjs'
22+
import * as h from '@mitranim/js/http'
2323

24-
// Finds files in the current folder, with no filtering.
25-
const DIRS = hb.HttpDirs.of(new hb.HttpDir(`.`))
24+
// Resolves files relatively to the current folder.
25+
const DIRS = h.HttpDirs.of(new h.HttpDir({fsPath: `.`}))
2626

27-
async function respond(req) {
27+
h.serve({onRequest})
28+
29+
async function onRequest(req) {
30+
const path = new URL(req.url).pathname
31+
32+
return (
33+
(await serveFile(req, path)) ||
34+
new Response(`not found`, {status: 404})
35+
)
36+
}
37+
38+
async function serveFile(req, path) {
39+
const file = await DIRS.resolveSiteFileWithNotFound(path)
40+
return file?.response()
41+
}
42+
```
43+
44+
For production, enable compression and caching:
45+
46+
```js
47+
import * as h from '@mitranim/js/http'
48+
49+
const DIRS = h.HttpDirs.of(new h.HttpDir({fsPath: `.`}))
50+
const COMP = new h.HttpCompressor()
51+
52+
DIRS.setOpt({caching: true})
53+
54+
h.serve({onRequest})
55+
56+
async function onRequest(req) {
2857
const path = new URL(req.url).pathname
2958

3059
return (
31-
(await DIRS.resolveSiteFileWithNotFound(path))?.response() ||
60+
(await serveFile(req, path)) ||
3261
new Response(`not found`, {status: 404})
3362
)
3463
}
3564

36-
Bun.serve({fetch: respond})
65+
async function serveFile(req, path) {
66+
return h.fileResponse({
67+
req,
68+
file: await DIRS.resolveSiteFileWithNotFound(path),
69+
compressor: COMP,
70+
})
71+
}
3772
```
3873
3974
## API

doc/http_deno_readme.md

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,56 @@ Also see {{featLink http}} for routing and cookies, {{featLink http_bun}} for Bu
1919
Simple example of a server that serves files from the current directory, automatically matching URL paths to HTML files:
2020

2121
```js
22-
import * as hd from '{{featUrl http_deno}}'
22+
import * as h from '@mitranim/js/http'
2323

24-
// Finds files in the current folder, with no filtering.
25-
const DIRS = hd.HttpDirs.of(new hd.HttpDir(`.`))
24+
// Resolves files relatively to the current folder.
25+
const DIRS = h.HttpDirs.of(new h.HttpDir({fsPath: `.`}))
2626

27-
async function respond(req) {
27+
h.serve({onRequest})
28+
29+
async function onRequest(req) {
30+
const path = new URL(req.url).pathname
31+
32+
return (
33+
(await serveFile(req, path)) ||
34+
new Response(`not found`, {status: 404})
35+
)
36+
}
37+
38+
async function serveFile(req, path) {
39+
const file = await DIRS.resolveSiteFileWithNotFound(path)
40+
return file?.response()
41+
}
42+
```
43+
44+
For production, enable compression and caching:
45+
46+
```js
47+
import * as h from '@mitranim/js/http'
48+
49+
const DIRS = h.HttpDirs.of(new h.HttpDir({fsPath: `.`}))
50+
const COMP = new h.HttpCompressor()
51+
52+
DIRS.setOpt({caching: true})
53+
54+
h.serve({onRequest})
55+
56+
async function onRequest(req) {
2857
const path = new URL(req.url).pathname
2958

3059
return (
31-
(await DIRS.resolveSiteFileWithNotFound(path))?.response() ||
60+
(await serveFile(req, path)) ||
3261
new Response(`not found`, {status: 404})
3362
)
3463
}
3564

36-
Deno.serve({handler: respond})
65+
async function serveFile(req, path) {
66+
return h.fileResponse({
67+
req,
68+
file: await DIRS.resolveSiteFileWithNotFound(path),
69+
compressor: COMP,
70+
})
71+
}
3772
```
3873
3974
## API
File renamed without changes.

docs/cli_readme.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
CLI args:
2424

2525
```js
26-
import * as cl from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected].79/cli.mjs'
26+
import * as cl from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected].80/cli.mjs'
2727

2828
const cli = cl.Flag.os()
2929

@@ -34,15 +34,15 @@ console.log(...cli.args)
3434
Console clearing:
3535

3636
```js
37-
import * as cl from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected].79/cli.mjs'
37+
import * as cl from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected].80/cli.mjs'
3838

3939
cl.emptty()
4040
```
4141

4242
Clearing the console only once, before running your code:
4343

4444
```js
45-
import 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected].79/cli_emptty.mjs'
45+
import 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected].80/cli_emptty.mjs'
4646
```
4747

4848
## API

docs/coll_readme.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Port and rework of https://github.com/mitranim/jol.
2626
## Usage
2727

2828
```js
29-
import * as c from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected].79/coll.mjs'
29+
import * as c from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected].80/coll.mjs'
3030
```
3131

3232
## API
@@ -101,8 +101,8 @@ Links: [source](../coll.mjs#L100); [test/example](../test/coll_test.mjs#L218).
101101
Variant of [#`Bmap`](#class-bmap) with support for key and value checks. Subclasses must override methods `.reqKey` and `.reqVal`. These methods are automatically called by `.set`. Method `.reqKey` must validate and return the given key, and method `.reqVal` must validate and return the given value. Use type assertions provided by [`lang`](lang_readme.md).
102102

103103
```js
104-
import * as l from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected].79/lang.mjs'
105-
import * as c from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected].79/coll.mjs'
104+
import * as l from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected].80/lang.mjs'
105+
import * as c from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected].80/coll.mjs'
106106

107107
class StrNatMap extends c.TypedMap {
108108
reqKey(key) {return l.reqStr(key)}
@@ -242,7 +242,7 @@ Differences and advantages over `Array`:
242242
The overhead of the wrapper is insignificant.
243243

244244
```js
245-
import * as c from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected].79/coll.mjs'
245+
import * as c from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected].80/coll.mjs'
246246

247247
console.log(new c.Vec())
248248
// Vec{Symbol(val): []}

docs/dom_readme.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
## Usage
1212

1313
```js
14-
import * as d from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected].79/dom.mjs'
14+
import * as d from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected].80/dom.mjs'
1515
```
1616

1717
## API
@@ -72,3 +72,4 @@ The following APIs are exported but undocumented. Check [dom.mjs](../dom.mjs).
7272
* [`class MixinChild`](../dom.mjs#L236)
7373
* [`function MixChildCon`](../dom.mjs#L291)
7474
* [`class MixinChildCon`](../dom.mjs#L293)
75+
* [`class ChildDiff`](../dom.mjs#L304)

0 commit comments

Comments
 (0)