xbintsc 0.5.20 → 0.5.22
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/AGENTS.md +5 -3
- package/dist/src/driver/compiler.js +1 -1
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/runtime-objects.d.ts +1 -1
- package/dist/src/driver/runtime-objects.js +1 -0
- package/dist/src/driver/runtime-objects.js.map +1 -1
- package/dist/src/driver/version.d.ts +1 -1
- package/dist/src/driver/version.js +1 -1
- package/dist/src/extensions/node/fs/index.d.ts +4 -3
- package/dist/src/extensions/node/fs/index.js +12 -3
- package/dist/src/extensions/node/fs/index.js.map +1 -1
- package/dist/src/extensions/node/fs-promises/index.d.ts +5 -5
- package/dist/src/extensions/node/fs-promises/index.js +15 -6
- package/dist/src/extensions/node/fs-promises/index.js.map +1 -1
- package/dist/tests/e2e/async-await.test.js +1 -1
- package/dist/tests/e2e/async-fs.test.d.ts +9 -0
- package/dist/tests/e2e/async-fs.test.js +119 -0
- package/dist/tests/e2e/async-fs.test.js.map +1 -0
- package/dist/tests/e2e/fetch.test.js +24 -0
- package/dist/tests/e2e/fetch.test.js.map +1 -1
- package/dist/tests/e2e/gui-canvas.test.js +159 -0
- package/dist/tests/e2e/gui-canvas.test.js.map +1 -1
- package/dist/tests/e2e/gui-example-canvas.test.d.ts +11 -0
- package/dist/tests/e2e/gui-example-canvas.test.js +70 -0
- package/dist/tests/e2e/gui-example-canvas.test.js.map +1 -0
- package/dist/tests/e2e/gui-inline-svg.test.d.ts +11 -0
- package/dist/tests/e2e/gui-inline-svg.test.js +160 -0
- package/dist/tests/e2e/gui-inline-svg.test.js.map +1 -0
- package/doc/ai/contributing.md +2 -1
- package/doc/ai/extensions.md +8 -6
- package/doc/ai/language-support.md +2 -1
- package/doc/ai/zh-CN/contributing.md +1 -1
- package/doc/ai/zh-CN/extensions.md +6 -4
- package/doc/ai/zh-CN/language-support.md +2 -1
- package/doc/gui/api.md +8 -0
- package/doc/gui/canvas.md +47 -21
- package/doc/gui/known-issues.md +10 -9
- package/doc/gui/media.md +32 -3
- package/doc/gui.md +31 -3
- package/doc/implemented/async.md +16 -3
- package/doc/implemented/fetch.md +38 -6
- package/doc/implemented/node/fs-promises.md +19 -8
- package/doc/implemented/node.md +1 -1
- package/doc/implemented.md +1 -1
- package/doc/unimplemented/deviations.md +1 -1
- package/doc/unimplemented/node/async.md +6 -3
- package/doc/unimplemented/node/fs-promises.md +10 -6
- package/doc/unimplemented/node/fs.md +7 -8
- package/doc/unimplemented/node.md +2 -2
- package/doc/unimplemented/toolchain.md +1 -1
- package/doc/zh-CN/gui/api.md +7 -0
- package/doc/zh-CN/gui/canvas.md +36 -17
- package/doc/zh-CN/gui/known-issues.md +8 -7
- package/doc/zh-CN/gui/media.md +26 -2
- package/doc/zh-CN/gui.md +24 -3
- package/doc/zh-CN/implemented/async.md +13 -2
- package/doc/zh-CN/implemented/fetch.md +23 -4
- package/doc/zh-CN/implemented/node/fs-promises.md +6 -3
- package/doc/zh-CN/implemented/node.md +1 -1
- package/doc/zh-CN/implemented.md +1 -1
- package/doc/zh-CN/unimplemented/deviations.md +1 -1
- package/doc/zh-CN/unimplemented/node/async.md +6 -3
- package/doc/zh-CN/unimplemented/node/fs-promises.md +4 -4
- package/doc/zh-CN/unimplemented/node/fs.md +2 -2
- package/doc/zh-CN/unimplemented/node.md +2 -2
- package/doc/zh-CN/unimplemented/toolchain.md +1 -1
- package/package.json +1 -1
- package/runtime/ext_gui/canvas.cpp +114 -199
- package/runtime/ext_gui/canvas.h +101 -2
- package/runtime/ext_gui/canvas_gradient.cpp +112 -0
- package/runtime/ext_gui/canvas_internal.h +6 -6
- package/runtime/ext_gui/canvas_raster.cpp +27 -5
- package/runtime/ext_gui/canvas_stroke.cpp +252 -0
- package/runtime/ext_gui/canvas_style.cpp +240 -0
- package/runtime/ext_gui/dom_api.cpp +1 -0
- package/runtime/ext_gui/dom_api_canvas.cpp +38 -4
- package/runtime/ext_gui/dom_api_canvas_extra.cpp +232 -0
- package/runtime/ext_gui/dom_api_internal.h +31 -0
- package/runtime/ext_gui/dom_api_proto.cpp +24 -0
- package/runtime/ext_gui/inline_svg.cpp +219 -0
- package/runtime/ext_gui/inline_svg.h +41 -0
- package/runtime/ext_gui/layout.cpp +18 -0
- package/runtime/ext_gui/layout.h +7 -0
- package/runtime/ext_gui/layout_flow.cpp +4 -3
- package/runtime/ext_gui/paint.cpp +14 -0
- package/runtime/ext_gui/paint.h +5 -1
- package/runtime/ext_gui/renderer_image.cpp +53 -25
- package/runtime/ext_gui/style_ua.cpp +3 -0
- package/runtime/ext_gui/svg.cpp +122 -39
- package/runtime/ext_gui/svg.h +14 -3
- package/runtime/ext_gui/window.cpp +28 -0
- package/runtime/ext_node/fs/async_api.c +314 -0
- package/runtime/ext_node/fs/async_complete.c +99 -0
- package/runtime/ext_node/fs/async_fh.c +206 -0
- package/runtime/ext_node/fs/async_job.c +81 -0
- package/runtime/ext_node/fs/async_job.h +132 -0
- package/runtime/ext_node/fs/async_worker.c +534 -0
- package/runtime/ext_node/fs/async_worker_dir.c +439 -0
- package/runtime/ext_node/fs/async_worker_dir.h +33 -0
- package/runtime/ext_node/fs/async_worker_fd.c +74 -0
- package/runtime/ext_node/fs/dir.c +60 -15
- package/runtime/ext_node/fs/fs_common.h +9 -0
- package/runtime/ext_node/fs/fs_ops.c +18 -29
- package/runtime/ext_node/fs/parts/builtins.inc +34 -0
- package/runtime/ext_node/fs/parts/platform.inc +66 -0
- package/runtime/ext_node/fs/promises.c +44 -234
- package/runtime/rt_internal.h +2 -0
- package/runtime/xt_async_io.c +333 -0
- package/runtime/xt_async_io.h +47 -0
- package/runtime/xt_fetch/async.inc +295 -0
- package/runtime/xt_fetch/request.inc +6 -183
- package/runtime/xt_fetch/socket.inc +5 -4
- package/runtime/xt_fetch.c +6 -4
- package/runtime/xt_loop.c +44 -3
- package/src/driver/compiler.ts +1 -1
- package/src/driver/runtime-objects.ts +1 -0
- package/src/driver/version.ts +1 -1
- package/src/extensions/node/fs/index.ts +12 -3
- package/src/extensions/node/fs-promises/index.ts +15 -6
package/doc/gui/canvas.md
CHANGED
|
@@ -13,8 +13,11 @@ video frames (`renderer_image.cpp`), so the GPU scales it into the CSS box.
|
|
|
13
13
|
|
|
14
14
|
The engine-side object is `xtgui::Canvas2D`
|
|
15
15
|
(`runtime/ext_gui/canvas.{h,cpp}`) — one RGBA8 bitmap plus the 2D drawing state.
|
|
16
|
-
The rasteriser lives in `canvas_raster.cpp
|
|
17
|
-
`
|
|
16
|
+
The rasteriser lives in `canvas_raster.cpp`, stroke geometry (caps, joins,
|
|
17
|
+
dashes) in `canvas_stroke.cpp`, gradients in `canvas_gradient.cpp`, and drawing
|
|
18
|
+
state parsing in `canvas_style.cpp`; the DOM bindings live in
|
|
19
|
+
`dom_api_canvas.cpp` / `dom_api_canvas_extra.cpp`, and the per-window lifetime in
|
|
20
|
+
`gui_canvas.cpp`.
|
|
18
21
|
|
|
19
22
|
## Getting a context
|
|
20
23
|
|
|
@@ -37,6 +40,14 @@ horizontal coverage** pass, and fills paths with the **non-zero winding rule**
|
|
|
37
40
|
(`b == 0 && c == 0` in the transform) takes a direct coverage fast path.
|
|
38
41
|
Blending is non-premultiplied source-over, scaled by `globalAlpha`.
|
|
39
42
|
|
|
43
|
+
A fill or stroke paints with a solid colour **or a `CanvasGradient`**. Gradient
|
|
44
|
+
coordinates are user-space coordinates, so the rasteriser maps each device pixel
|
|
45
|
+
back through the inverse of the current transform before sampling; this keeps a
|
|
46
|
+
gradient attached to the shape when the shape is translated, scaled or rotated.
|
|
47
|
+
Strokes are flattened into segment quads plus the join/cap shapes selected by
|
|
48
|
+
`lineJoin`/`lineCap`; a dash pattern first splits the polyline into its "on"
|
|
49
|
+
pieces, then those pieces are stroked with the same caps.
|
|
50
|
+
|
|
40
51
|
Canvas pixels are **not** touched by the garbage collector: a canvas is owned by
|
|
41
52
|
the window, keyed by its DOM node (the same shape as the `<video>`/`<audio>`
|
|
42
53
|
decode table). It is released when the node is dropped (`loadHTML`, window
|
|
@@ -48,24 +59,39 @@ matching GPU texture.
|
|
|
48
59
|
State and transforms: `save` / `restore` (which restore the transform, line
|
|
49
60
|
width, alpha, font/text settings **and** the fill/stroke styles),
|
|
50
61
|
`translate`, `rotate`, `scale`, `transform(a,b,c,d,e,f)`, `setTransform`,
|
|
51
|
-
`resetTransform
|
|
62
|
+
`resetTransform`, `getTransform` (returns an object with `a`…`f` and `is2D`).
|
|
63
|
+
Transforms compose in canvas order: `translate(x, y); rotate(a)` rotates around
|
|
64
|
+
the translated origin.
|
|
52
65
|
|
|
53
66
|
Styles and settings: `fillStyle` / `strokeStyle` (CSS colors understood by the
|
|
54
|
-
cascade's color parser), `lineWidth`, `globalAlpha`,
|
|
55
|
-
|
|
56
|
-
`
|
|
57
|
-
|
|
58
|
-
`
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
cascade's color parser, or a `CanvasGradient`), `lineWidth`, `globalAlpha`,
|
|
68
|
+
`lineCap` (`butt`/`round`/`square`), `lineJoin` (`miter`/`round`/`bevel`),
|
|
69
|
+
`miterLimit`, `lineDashOffset`, `font` (a CSS font shorthand resolving to
|
|
70
|
+
family, size and bold/italic — default `10px sans-serif`), `textAlign`
|
|
71
|
+
(`start`/`left`, `center`, `right`/`end`) and `textBaseline` (`alphabetic`,
|
|
72
|
+
`top`, `middle`, `bottom`, `hanging`, `ideographic`). Reading a property returns
|
|
73
|
+
the value last assigned; assigning an unparseable color keeps the previous one.
|
|
74
|
+
|
|
75
|
+
Gradients: `createLinearGradient(x0, y0, x1, y1)` and
|
|
76
|
+
`createRadialGradient(x0, y0, r0, x1, y1, r1)` return a `CanvasGradient` with
|
|
77
|
+
`addColorStop(offset, color)`. Stops stay live, so a stop added after
|
|
78
|
+
`fillStyle = gradient` affects later fills, and the `fillStyle`/`strokeStyle`
|
|
79
|
+
getter returns the same gradient handle. Radial gradients interpolate by
|
|
80
|
+
distance from the **outer** circle's centre (`x1, y1`), so `x0`/`y0` are
|
|
81
|
+
currently ignored: use concentric circles (`x0 == x1`, `y0 == y1`).
|
|
82
|
+
|
|
83
|
+
Paths: `beginPath`, `closePath`, `moveTo`, `lineTo`, `rect`, `roundRect`,
|
|
84
|
+
`arc`, `ellipse`, `quadraticCurveTo`, `bezierCurveTo`, then `fill` (multi-loop,
|
|
85
|
+
non-zero winding) or `stroke`. Bezier and quadratic segments are flattened into
|
|
86
|
+
line segments; arcs and ellipses are flattened too. `roundRect` accepts one
|
|
87
|
+
radius number (the array form is not implemented).
|
|
66
88
|
|
|
67
89
|
Rectangles: `clearRect`, `fillRect`, `strokeRect`.
|
|
68
90
|
|
|
91
|
+
Dashes: `setLineDash(segments)` / `getLineDash()` / `lineDashOffset` implement
|
|
92
|
+
the standard pattern rules (an odd-length list is duplicated, negative or
|
|
93
|
+
non-finite entries are ignored, an all-zero list means a solid line).
|
|
94
|
+
|
|
69
95
|
Text: `measureText(text)` (returns `{ width }`), `fillText(text, x, y)` and
|
|
70
96
|
`strokeText(text, x, y)`. Text is shaped with HarfBuzz and rasterised with
|
|
71
97
|
FreeType (the same stack as HTML text) at the transform's approximate scale.
|
|
@@ -87,12 +113,12 @@ Out-of-bounds pixels are transparent.
|
|
|
87
113
|
|
|
88
114
|
## Not implemented
|
|
89
115
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
`
|
|
93
|
-
|
|
94
|
-
`
|
|
95
|
-
|
|
116
|
+
Patterns (`createPattern`), shadows, `clip()`, `globalCompositeOperation`,
|
|
117
|
+
`putImageData`, `createImageData`, `isPointInPath`, `setTransform` with a
|
|
118
|
+
`DOMMatrix`, `drawImage` with an image smoothing toggle, `toDataURL`/`toBlob`,
|
|
119
|
+
`<canvas>` as a CSS `background-image`, and offscreen canvases
|
|
120
|
+
(`OffscreenCanvas`, `transferControlToOffscreen`). `roundRect` takes a single
|
|
121
|
+
radius, and radial gradients ignore the inner circle's offset.
|
|
96
122
|
|
|
97
123
|
See also [Media](./media.md) for the image/video/audio decode stack that
|
|
98
124
|
`drawImage` and the texture upload share.
|
package/doc/gui/known-issues.md
CHANGED
|
@@ -34,20 +34,21 @@
|
|
|
34
34
|
*class change* on a flex container has also been observed to crash the same
|
|
35
35
|
way; `examples/gui/pelican-bike` reveals its game-over overlay with `opacity`
|
|
36
36
|
instead.
|
|
37
|
+
- **SVG is NanoSVG's subset.** SVG *files* (`<img src="logo.svg">`) and inline
|
|
38
|
+
`<svg>` trees share the vendored NanoSVG parser/rasteriser
|
|
39
|
+
(`runtime/ext_gui/svg.cpp`, `inline_svg.cpp`), which renders
|
|
40
|
+
`path`/`rect`/`circle`/`ellipse`/`line`/`polyline`/`polygon`, groups, `defs`
|
|
41
|
+
gradients and class-based `<style>` rules. `<text>`, `<use>`, filters, masks
|
|
42
|
+
and clip paths are not rendered, and CSS applied by the document cascade does
|
|
43
|
+
not reach inside the artwork (fill/stroke are SVG presentation attributes,
|
|
44
|
+
not CSS properties here). Vector art that needs those has to be rasterised
|
|
45
|
+
beforehand, drawn from boxes, or restricted to the supported syntax. See
|
|
46
|
+
[Media](./media.md#images-m6a).
|
|
37
47
|
|
|
38
48
|
## Open questions
|
|
39
49
|
|
|
40
50
|
- Whether Linux ships X11, Wayland, or both in the first cut. (Decision:
|
|
41
51
|
X11 first, Wayland later.)
|
|
42
|
-
- **SVG:** SVG *files* are supported through the image loader
|
|
43
|
-
(`<img src="logo.svg">`): `runtime/ext_gui/svg.cpp` wraps the vendored
|
|
44
|
-
**NanoSVG** parser/rasteriser and `image.{h,cpp}` treats an SVG as a vector
|
|
45
|
-
image, rasterised at the drawn pixel size. Inline `<svg>` **trees** in a
|
|
46
|
-
document are still not supported: the HTML parser knows elements and
|
|
47
|
-
attributes, the layout engine knows boxes, and paint knows
|
|
48
|
-
rectangles/text/images, so `<svg>` subtrees would need a whole second
|
|
49
|
-
geometry+paint path. Vector art therefore has to arrive as an SVG/PNG file or
|
|
50
|
-
be drawn from boxes, as `examples/gui/pelican-bike` does.
|
|
51
52
|
- **Windows: the C++ runtime must not be named on the link line.** `gui.lib` is
|
|
52
53
|
built with the *static* CRT (`CMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded`) and
|
|
53
54
|
every engine object records `libcpmt` as a dependent library, which is also
|
package/doc/gui/media.md
CHANGED
|
@@ -12,7 +12,9 @@ size cache (`stbi_info`). Paths accept a `file://` prefix and percent-encoding.
|
|
|
12
12
|
library (parse + anti-aliased scanline rasteriser), detects an SVG by extension
|
|
13
13
|
or by its contents, and produces the same RGBA8 `Image` the pipeline already
|
|
14
14
|
consumes. `stb_image` cannot decode SVG, so this is what makes
|
|
15
|
-
`<img src="logo.svg">` work.
|
|
15
|
+
`<img src="logo.svg">` work. The same parser is used for an inline `<svg>`
|
|
16
|
+
subtree, whose DOM nodes are serialized into a document first
|
|
17
|
+
(`inline_svg.{h,cpp}`).
|
|
16
18
|
|
|
17
19
|
- `<img>` is a replaced element (`display: inline-block`): layout gives it the
|
|
18
20
|
CSS size when set, otherwise the intrinsic pixel size, and preserves the
|
|
@@ -27,12 +29,39 @@ consumes. `stb_image` cannot decode SVG, so this is what makes
|
|
|
27
29
|
pixels (logical size × the window's pixel density), preserving the viewBox
|
|
28
30
|
aspect ratio, and cached by that size, so scaling or a HiDPI display produces
|
|
29
31
|
a crisp render instead of a magnified bitmap.
|
|
32
|
+
- An **inline** `<svg>` element is a replaced element like `<img>`, but its
|
|
33
|
+
source is the DOM subtree itself. `runtime/ext_gui/inline_svg.{h,cpp}`
|
|
34
|
+
serializes that subtree back into a standalone SVG document, restoring the
|
|
35
|
+
case-sensitive spellings NanoSVG matches (`viewBox`, `preserveAspectRatio`,
|
|
36
|
+
`linearGradient`, `gradientUnits`, …) that the HTML parser lowercased. Layout
|
|
37
|
+
sizes the box from the `width`/`height` attributes, else the `viewBox`, else
|
|
38
|
+
300x150; paint carries the markup on its image op and the renderer parses and
|
|
39
|
+
rasterises it at the drawn device pixel size through the same NanoSVG cache as
|
|
40
|
+
an SVG file. A DOM mutation re-serializes the subtree on the next layout (so
|
|
41
|
+
`setAttribute` and `innerHTML` both repaint the artwork), and the markup cache
|
|
42
|
+
is bounded (least-recently-used) so an animated document cannot grow it
|
|
43
|
+
without limit.
|
|
30
44
|
- The parse is cached per path; the parse and rasteriser state are released in
|
|
31
45
|
`xt_svg_shutdown` (`xt_image_shutdown`).
|
|
32
46
|
|
|
47
|
+
Document CSS styles the `<svg>` **box** — size, margins, background, borders,
|
|
48
|
+
`transform`, opacity, `display` — exactly as it styles any other element. The
|
|
49
|
+
artwork inside it keeps SVG semantics: presentation attributes and the
|
|
50
|
+
`class`-based rules of an SVG `<style>` block are read by NanoSVG, not by the
|
|
51
|
+
HTML cascade, so `fill`/`stroke` are not CSS properties here. For layout, paint,
|
|
52
|
+
hit testing and events the whole `<svg>` is one replaced box: its children get no
|
|
53
|
+
DOM boxes, so a click's `e.target` is the `<svg>` element, not a `<rect>`. The
|
|
54
|
+
renderer caches one texture per markup-and-size pair, so a program that rewrites
|
|
55
|
+
its artwork every frame keeps a texture per variant; prefer animating the box
|
|
56
|
+
(`transform`, `opacity`) or swapping between a small set of documents.
|
|
57
|
+
|
|
33
58
|
Still to do for images: CSS `background-image: url(...)`, `data:` URIs,
|
|
34
|
-
`object-fit
|
|
35
|
-
|
|
59
|
+
`object-fit` and 9-slice borders.
|
|
60
|
+
|
|
61
|
+
Both kinds of SVG share NanoSVG's subset: `path`, `rect`, `circle`, `ellipse`,
|
|
62
|
+
`line`, `polyline`, `polygon`, groups (`g`), `defs` with linear/radial gradients
|
|
63
|
+
and class-based `<style>` rules rasterise; `<text>`, `<use>`, filters, masks and
|
|
64
|
+
clip paths are not rendered (see [Known issues](./known-issues.md)).
|
|
36
65
|
|
|
37
66
|
## Video & audio (M13)
|
|
38
67
|
|
package/doc/gui.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# xbintsc GUI extension (self-hosted HTML/CSS renderer)
|
|
2
2
|
|
|
3
|
-
Status: **
|
|
3
|
+
Status: **M16** — features (M1–M15) are complete: HTML parsing, CSS selector
|
|
4
4
|
matching, the cascade, computed styles and layout (block, inline, Flexbox and
|
|
5
5
|
CSS positioning) are
|
|
6
6
|
in place, and the engine *paints*: it builds a display list of rectangles, images
|
|
@@ -9,7 +9,9 @@ tested and delivered to native TS handlers, `:hover`/`:focus` are matched
|
|
|
9
9
|
dynamically, `<img>` is sized from its intrinsic dimensions and drawn from a
|
|
10
10
|
texture — PNG/JPEG/… bitmaps and SVG files alike, the latter rasterised at the
|
|
11
11
|
drawn size so vector art stays crisp — and CSS transitions animate paint
|
|
12
|
-
properties. **
|
|
12
|
+
properties. **M16** gives inline `<svg>` trees the same treatment: the subtree is
|
|
13
|
+
serialized back into an SVG document and rasterised at the drawn device size, so
|
|
14
|
+
an icon needs no separate file. **M8** adds an interactive
|
|
13
15
|
DOM: element handles with stable identity, mutation (`appendChild`, `textContent`,
|
|
14
16
|
`classList`, `style`, …) and element-level events with capture/bubbling.
|
|
15
17
|
**M9** compiles `<script>` bodies ahead of time (inline and `<script src>`) — see
|
|
@@ -257,6 +259,18 @@ engine were Rust.
|
|
|
257
259
|
[Layout](./gui/layout.md#transforms-m15) and
|
|
258
260
|
[Animation](./gui/animation.md#css-transforms-m15); e2e in
|
|
259
261
|
`tests/e2e/gui-transform.test.ts` and `tests/e2e/gui-example-svg.test.ts`.
|
|
262
|
+
16. **M16 — inline `<svg>`** ✅
|
|
263
|
+
- `<svg>` is a replaced element (`layout.*`): its intrinsic size comes from
|
|
264
|
+
the `width`/`height` attributes, else the `viewBox`, else 300x150, and the
|
|
265
|
+
normal box walk does not descend into its children.
|
|
266
|
+
- `inline_svg.{h,cpp}` serializes the subtree into a standalone SVG document,
|
|
267
|
+
restoring the case-sensitive names the HTML parser lowercased (`viewBox`,
|
|
268
|
+
`preserveAspectRatio`, `linearGradient`, `gradientUnits`, …).
|
|
269
|
+
- Paint carries the markup on its image op (`PaintImage::svg`) and the
|
|
270
|
+
renderer parses/rasterises it at the drawn device size through the same
|
|
271
|
+
NanoSVG cache as an SVG file (`svg.{h,cpp}`); the markup cache is bounded
|
|
272
|
+
and a DOM mutation re-serializes on the next layout. e2e in
|
|
273
|
+
`tests/e2e/gui-inline-svg.test.ts`.
|
|
260
274
|
|
|
261
275
|
## Progress log
|
|
262
276
|
|
|
@@ -340,7 +354,13 @@ engine were Rust.
|
|
|
340
354
|
`getContext("2d")` handle and its API are bound in `dom_api_canvas.cpp`, and
|
|
341
355
|
`gui_canvas.cpp` owns the per-window lifetime. The bitmap uploads through the
|
|
342
356
|
image texture path (`renderer_image.cpp`) and the display list carries a
|
|
343
|
-
`canvas_id` (`paint.*`).
|
|
357
|
+
`canvas_id` (`paint.*`). The drawing surface was later widened with linear /
|
|
358
|
+
radial gradients (`canvas_gradient.cpp`), `ellipse`/`roundRect`, line
|
|
359
|
+
caps/joins/miter limits and dash patterns (`canvas_style.cpp`,
|
|
360
|
+
`canvas_stroke.cpp`), and `getTransform`; `transform` now post-multiplies, so
|
|
361
|
+
`translate(x, y); rotate(a)` rotates around the translated origin, and
|
|
362
|
+
`examples/gui/pelican-canvas` draws an animated pelican on a bicycle entirely
|
|
363
|
+
with these APIs. See [Canvas](./gui/canvas.md); e2e in
|
|
344
364
|
`tests/e2e/gui-canvas.test.ts`.
|
|
345
365
|
- **M15** ✅ CSS `transform` / `transform-origin`. A new translation unit
|
|
346
366
|
(`transform.{h,cpp}`) parses `translate`/`translateX`/`translateY`/`scale`/
|
|
@@ -355,4 +375,12 @@ engine were Rust.
|
|
|
355
375
|
through the inverse, and `getBoundingClientRect` returns the transformed AABB.
|
|
356
376
|
`examples/gui/pelican-svg` animates its artwork with it; e2e in
|
|
357
377
|
`tests/e2e/gui-transform.test.ts` and `tests/e2e/gui-example-svg.test.ts`.
|
|
378
|
+
- **M16** ✅ inline `<svg>`. `buildBoxTree` treats an `<svg>` element as a
|
|
379
|
+
replaced box (intrinsic size from `width`/`height`, then `viewBox`, then
|
|
380
|
+
300x150) and stops descending, `inline_svg.{h,cpp}` serializes the subtree with
|
|
381
|
+
canonical SVG casing, paint carries the markup on `PaintImage::svg`, and
|
|
382
|
+
`renderer_image.cpp` rasterises it at the drawn device pixel size through the
|
|
383
|
+
NanoSVG cache (`svg.{h,cpp}`, bounded LRU for markup). The `win.inlineSvg`
|
|
384
|
+
diagnostic exposes the serialized document, and `tests/e2e/gui-inline-svg.test.ts`
|
|
385
|
+
pins sizing, hit testing, gradients, CSS box styling and mutation.
|
|
358
386
|
|
package/doc/implemented/async.md
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
> Part of [Implemented syntax and features](../implemented.md) · [简体中文](../zh-CN/implemented/async.md)
|
|
4
4
|
|
|
5
5
|
Location: `runtime/xt_async.c`, `runtime/xt_coroutine.c`, `runtime/xt_promise.c`,
|
|
6
|
-
`runtime/xt_generator.c`, `runtime/xt_loop.c`, `runtime/
|
|
6
|
+
`runtime/xt_generator.c`, `runtime/xt_loop.c`, `runtime/xt_async_io.c`,
|
|
7
|
+
`runtime/xt_performance.c`.
|
|
7
8
|
|
|
8
9
|
## async / await
|
|
9
10
|
|
|
@@ -11,7 +12,7 @@ Location: `runtime/xt_async.c`, `runtime/xt_coroutine.c`, `runtime/xt_promise.c`
|
|
|
11
12
|
- The body runs synchronously until the first `await` of a pending promise, then suspends; settlement queues a microtask that resumes the coroutine.
|
|
12
13
|
- An exception that escapes the body rejects that promise instead of throwing synchronously.
|
|
13
14
|
- `await` on a non-promise or settled promise keeps the synchronous fast path and raises on rejection.
|
|
14
|
-
- Each `async` call owns a private stack and swaps its `try` frame stack and GC stack region, so `await` survives `try`/`finally`, loops and closures. A promise settled from a timer, a socket callback or another coroutine resumes the suspended body on the microtask queue.
|
|
15
|
+
- Each `async` call owns a private stack and swaps its `try` frame stack and GC stack region, so `await` survives `try`/`finally`, loops and closures. A promise settled from a timer, a socket callback, an offloaded file operation or another coroutine resumes the suspended body on the microtask queue.
|
|
15
16
|
|
|
16
17
|
## async generators
|
|
17
18
|
|
|
@@ -35,4 +36,16 @@ Location: `runtime/xt_async.c`, `runtime/xt_coroutine.c`, `runtime/xt_promise.c`
|
|
|
35
36
|
- `setTimeout` / `clearTimeout` / `setInterval` / `clearInterval` / `setImmediate` / `clearImmediate` / `queueMicrotask` and `performance.now` (monotonic fractional milliseconds).
|
|
36
37
|
- Timers use numeric ids; `Timeout` / `Immediate` objects are not implemented.
|
|
37
38
|
- A promise settled from a timer can be awaited: the async body suspends on the pending promise and resumes when the timer fires. Sockets and servers (`net`, `http`, `dgram`) deliver their callbacks on the same loop.
|
|
38
|
-
- `fetch`
|
|
39
|
+
- `fetch` and `fs/promises` are reactor-driven rather than blocking — see the next section.
|
|
40
|
+
|
|
41
|
+
## Asynchronous I/O offload
|
|
42
|
+
|
|
43
|
+
- `runtime/xt_async_io.c` owns a small pool of worker threads (four by default, created lazily). Jobs are split in two halves: a `run` function on a worker that may only touch plain C memory, and a `complete` function on the main thread that builds values and settles the promise. The split is what keeps the collector and the value model single-threaded.
|
|
44
|
+
- A completed job wakes `select(2)` through a self-pipe (POSIX) or a loopback UDP socket (Windows); the reactor also polls the pool, so a failed wakeup degrades to a short wait instead of a hang.
|
|
45
|
+
- While a job is queued or running, up to four values per job (its promise, and for `fetch` the headers object) are kept in the job's root slots and scanned by the collector.
|
|
46
|
+
- `fetch` runs each request/redirect hop as a job; `fs/promises` runs every file and descriptor operation as a job. Neither blocks timers, sockets or other `await`s.
|
|
47
|
+
- The event loop stays alive while jobs are outstanding (`xt_loop_has_work`), and exits once the pool is idle again.
|
|
48
|
+
|
|
49
|
+
Known limits of the asynchronous surface (no TLS/streaming `fetch`, inert
|
|
50
|
+
`fs.watch`, four-worker pool) are listed in
|
|
51
|
+
[Runtime deviations](../unimplemented/deviations.md) and the Node pages.
|
package/doc/implemented/fetch.md
CHANGED
|
@@ -2,11 +2,43 @@
|
|
|
2
2
|
|
|
3
3
|
> Part of [Implemented syntax and features](../implemented.md) · [简体中文](../zh-CN/implemented/fetch.md)
|
|
4
4
|
|
|
5
|
-
Location: `runtime/xt_fetch.c`.
|
|
5
|
+
Location: `runtime/xt_fetch.c`, `runtime/xt_fetch/async.inc`, `runtime/xt_async_io.c`.
|
|
6
6
|
|
|
7
|
-
- Global `fetch(input, init)` is
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
- Global `fetch(input, init)` is an HTTP/1.1 client. The blocking socket exchange
|
|
8
|
+
(`getaddrinfo`, connect, send, receive) runs on the asynchronous I/O worker
|
|
9
|
+
pool, so a request in flight does **not** stall the event loop: timers, sockets
|
|
10
|
+
and other `await`s keep running and the promise settles when the reactor
|
|
11
|
+
dispatches the completed exchange.
|
|
12
|
+
- Redirects are followed on the main thread: each hop is one offloaded
|
|
13
|
+
exchange, up to 20 redirects, and a 301/302/303 switches POST to GET.
|
|
14
|
+
- It accepts `http://` URLs only — `https://` rejects with a `TypeError` because
|
|
15
|
+
no TLS backend is linked.
|
|
16
|
+
- Options: `method`, `headers` (plain object or `Headers`), `body` and `redirect`
|
|
17
|
+
(`follow` / `manual` / `error`).
|
|
18
|
+
- The resolved `Response`-shaped object exposes `ok` / `status` / `statusText` /
|
|
19
|
+
`url` / `headers` plus `text()` / `json()` / `arrayBuffer()` / `bytes()` /
|
|
20
|
+
`clone()`. The body is buffered before the promise settles; streaming bodies
|
|
21
|
+
are not implemented.
|
|
22
|
+
- `Headers` is case-insensitive with `get` / `has` / `set` / `append` /
|
|
23
|
+
`delete` / `keys` / `values` / `entries` / `forEach` / `getSetCookie`.
|
|
12
24
|
- Invalid URLs and network failures reject with a `TypeError`.
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
async function main(): Promise<void> {
|
|
28
|
+
const response = await fetch("http://127.0.0.1:8080/status");
|
|
29
|
+
console.log(response.status, await response.text());
|
|
30
|
+
}
|
|
31
|
+
main();
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
A timer scheduled before the request completes fires while the exchange is still
|
|
35
|
+
in flight:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
const pending = fetch("http://127.0.0.1:8080/slow");
|
|
39
|
+
setTimeout(() => console.log("timer"), 10); // runs before the 150ms response
|
|
40
|
+
await pending;
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Known limits (only `http://`, no TLS, buffered body) are listed in
|
|
44
|
+
[Runtime deviations](../unimplemented/deviations.md).
|
|
@@ -2,14 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
> Part of [Node extension: implemented](../node.md) · [简体中文](../../zh-CN/implemented/node/fs-promises.md)
|
|
4
4
|
|
|
5
|
-
Location: `src/extensions/node/fs-promises/index.ts`, `runtime/ext_node/fs/
|
|
5
|
+
Location: `src/extensions/node/fs-promises/index.ts`, `runtime/ext_node/fs/async_*.c`,
|
|
6
|
+
`runtime/ext_node/fs/promises.c`.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
Every function submits a job to the runtime's asynchronous I/O pool
|
|
9
|
+
(`runtime/xt_async_io.c`): the blocking syscall runs on a worker thread and the
|
|
10
|
+
promise settles on the event loop. `await` therefore yields while a large read,
|
|
11
|
+
a slow network filesystem or a descriptor operation is in progress, and `then` /
|
|
12
|
+
`catch` keep the usual Promise semantics. Failures **reject** with the same
|
|
13
|
+
Node-shaped `Error` (with `code` / `errno` / `syscall` / `path`) that the
|
|
14
|
+
synchronous form throws.
|
|
15
|
+
|
|
16
|
+
`fs.promises` is also reachable from the `fs` module
|
|
17
|
+
(`import { promises as fsp } from "fs"`), and both namespaces expose a
|
|
18
|
+
`constants` object.
|
|
13
19
|
|
|
14
20
|
Promise exports: `readFile`, `writeFile`, `appendFile`, `mkdir`, `readdir`,
|
|
15
21
|
`rm`, `unlink`, `rmdir`, `rename`, `copyFile`, `cp`, `realpath`, `stat`,
|
|
@@ -19,7 +25,9 @@ Promise exports: `readFile`, `writeFile`, `appendFile`, `mkdir`, `readdir`,
|
|
|
19
25
|
|
|
20
26
|
`open(...)` resolves to a **`FileHandle`** with:
|
|
21
27
|
`read`, `write`, `readFile`, `writeFile`, `appendFile`, `close`, `stat`,
|
|
22
|
-
`truncate`, `chmod`, `chown`, `utimes`, `sync`, `datasync`.
|
|
28
|
+
`truncate`, `chmod`, `chown`, `utimes`, `sync`, `datasync`. Those methods are
|
|
29
|
+
offloaded too; `read(buffer, …)` fills the caller's `Buffer` when the worker
|
|
30
|
+
completes.
|
|
23
31
|
|
|
24
32
|
```ts
|
|
25
33
|
import { readFile, writeFile, open } from "fs/promises";
|
|
@@ -34,5 +42,8 @@ async function main(): Promise<void> {
|
|
|
34
42
|
main();
|
|
35
43
|
```
|
|
36
44
|
|
|
45
|
+
Work runs on a small fixed pool (four workers) and completions are dispatched in
|
|
46
|
+
submission order per worker; independent operations can overlap. A job never
|
|
47
|
+
blocks another `await`, a timer or a socket callback.
|
|
37
48
|
|
|
38
49
|
Known deviations are listed in [Unimplemented: `fs/promises`](../../unimplemented/node/fs-promises.md).
|
package/doc/implemented/node.md
CHANGED
|
@@ -19,7 +19,7 @@ work on another. Missing or different behaviour is recorded under
|
|
|
19
19
|
| Module | Page | Notes |
|
|
20
20
|
| --- | --- | --- |
|
|
21
21
|
| `fs` | [fs](./node/fs.md) | synchronous API only |
|
|
22
|
-
| `fs/promises` | [fs/promises](./node/fs-promises.md) |
|
|
22
|
+
| `fs/promises` | [fs/promises](./node/fs-promises.md) | reactor-driven file and `FileHandle` operations on the async I/O pool |
|
|
23
23
|
| `path` | [path](./node/path.md) | `path.<name>(...)` namespace calls |
|
|
24
24
|
| `os` | [os](./node/os.md) | `os.<name>(...)` namespace calls |
|
|
25
25
|
| `process` | [process](./node/process.md) | object: methods and properties |
|
package/doc/implemented.md
CHANGED
|
@@ -37,7 +37,7 @@ small file instead of a single monolithic document.
|
|
|
37
37
|
- [Collections](./implemented/collections.md) — Map, Set, WeakMap, WeakSet, WeakRef, iteration
|
|
38
38
|
- [Typed arrays and binary data](./implemented/typed-arrays.md) — typed arrays, ArrayBuffer, DataView
|
|
39
39
|
- [Async, promises and generators](./implemented/async.md) — async/await, async generators, promises, timers
|
|
40
|
-
- [fetch](./implemented/fetch.md) —
|
|
40
|
+
- [fetch](./implemented/fetch.md) — reactor-driven HTTP/1.1 client and Headers
|
|
41
41
|
|
|
42
42
|
### Modules and tooling
|
|
43
43
|
|
|
@@ -14,7 +14,7 @@ These features **compile and run**, but the result does not fully match ECMAScri
|
|
|
14
14
|
| Array out-of-bounds / sparse | Out-of-bounds access returns `undefined`; assigning `arr.length` truncates / extends, but sparse holes are not tracked distinctly |
|
|
15
15
|
| Memory management | Non-moving mark-sweep collector behind `xt_alloc`: explicit roots, subsystem root providers and a conservative C-stack scan. Single-threaded and stop-the-world; no moving/compaction or generational mode |
|
|
16
16
|
| Function `arity` / argument count | No argument count validation; `fn.length` reports the declared arity but calls are never checked against it |
|
|
17
|
-
| `async` / `await` | Real suspension on a stackful coroutine; `await` always queues a microtask (non-promises and already-settled promises included), so interleaving with pending `.then` callbacks matches the specification. Top-level `await` blocks on the event loop rather than yielding the module body. `fs/promises`
|
|
17
|
+
| `async` / `await` | Real suspension on a stackful coroutine; `await` always queues a microtask (non-promises and already-settled promises included), so interleaving with pending `.then` callbacks matches the specification. Top-level `await` blocks on the event loop rather than yielding the module body. `fs/promises` and `fetch` offload their blocking work to a four-worker pool and settle on the reactor. Async generators suspend at both `await` and `yield`; `for await...of` awaits one step per iteration |
|
|
18
18
|
| Async generator cleanup | `for await...of` does not call the iterator's `return()` when the loop exits early. `yield*` inside an async generator delegates to synchronous iterables only. `.return()` at a suspended `yield` completes without running `finally` (the same limitation as synchronous generators) |
|
|
19
19
|
| `super` | `super.x` / `super(...)` takes the prototype of `this`'s prototype; single-level inheritance is correct, but depth > 1 may be inaccurate |
|
|
20
20
|
| `Error.stack` | Function-level frames only: each frame is `at name (file:line)` with the function's declaration line (updated to the construction line for the innermost frame); no column numbers and no frames for generator / `async` bodies (they switch stacks) |
|
|
@@ -5,13 +5,16 @@
|
|
|
5
5
|
Implemented:
|
|
6
6
|
|
|
7
7
|
- Core event loop `runtime/xt_loop.c` (a `select(2)` reactor); `net` / `dgram` / `http` are all built on it.
|
|
8
|
-
-
|
|
8
|
+
- Async I/O offload `runtime/xt_async_io.c`: blocking file and HTTP-client work runs on four worker threads and completes on the reactor, so `fs/promises` and `fetch` do not stall timers or sockets.
|
|
9
|
+
- `Promise`, `async` / `await` are available (see the core documents); `fs/promises` returns Promises that settle from the reactor.
|
|
9
10
|
- Timers are available as globals: `setTimeout` / `clearTimeout` / `setInterval` / `clearInterval` run on the core reactor and keep the process alive until no timer remains.
|
|
10
11
|
- A standalone `EventEmitter` is provided by the `events` module (also a global constructor); streams and sockets additionally carry internal emitter methods (`on` / `addListener` / `once` / `off` / `removeListener` / `emit`).
|
|
11
12
|
|
|
12
13
|
Still unimplemented:
|
|
13
14
|
|
|
14
15
|
- Node's `Timeout` object (`setTimeout` returns a numeric id; `setImmediate` and `queueMicrotask` exist as globals).
|
|
15
|
-
-
|
|
16
|
+
- File watching: `fs.watch` / `fs/promises.watch` return inert objects; there is no inotify/kqueue/ReadDirectoryChangesW backend.
|
|
17
|
+
- `Dir.read` / `Dir.close` invoke their callbacks immediately, and callback-style `fs.readFile` / `fs.writeFile` / … are still unsupported (use `fs/promises` or the `*Sync` forms).
|
|
16
18
|
- On streams and sockets the built-in `once` is equivalent to `on` (no "remove after firing once" semantics); the `events` module's `EventEmitter` does implement real `once`.
|
|
17
|
-
- `net`'s `connect` is blocking; HTTP responses require `Connection: close`, with no keep-alive / chunked transfer / pipelining.
|
|
19
|
+
- `net`'s `connect` is still a blocking `connect(2)` performed before the socket joins the reactor; HTTP responses require `Connection: close`, with no keep-alive / chunked transfer / pipelining. `fetch` has no TLS and buffers the whole body before resolving.
|
|
20
|
+
- The offload pool is fixed at four workers; there is no thread-pool sizing option and no `AbortSignal` cancellation for queued jobs.
|
|
@@ -2,15 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
> Part of [Node extension: unimplemented](../node.md) · [简体中文](../../zh-CN/unimplemented/node/fs-promises.md)
|
|
4
4
|
|
|
5
|
-
`fs/promises`
|
|
6
|
-
|
|
7
|
-
[implemented: `fs/promises`](../../implemented/node/fs-promises.md))
|
|
5
|
+
`fs/promises` now offloads its blocking I/O to the async I/O pool (the
|
|
6
|
+
implemented surface is in
|
|
7
|
+
[implemented: `fs/promises`](../../implemented/node/fs-promises.md)); the
|
|
8
|
+
remaining differences are behavioural:
|
|
8
9
|
|
|
9
|
-
- I/O is synchronous: the promise is settled before it is awaited, so awaiting
|
|
10
|
-
it does not yield to the event loop.
|
|
11
10
|
- `FileHandle.appendFile` behaves like `writeFile` (it writes at the current
|
|
12
11
|
file position instead of appending).
|
|
13
12
|
- `FileHandle.readFile()` reads from the current file-descriptor offset.
|
|
14
|
-
- `Dir.read` / `Dir.close` (sync and promise forms) complete
|
|
13
|
+
- `Dir.read` / `Dir.close` (sync and promise forms) complete their callbacks
|
|
14
|
+
immediately; `Dir.readSync` / `closeSync` remain synchronous.
|
|
15
15
|
- `watch` resolves to the same never-emitting watcher as `fs.watch`; there is no
|
|
16
16
|
real file-watching backend.
|
|
17
|
+
- Reads and writes are whole-file/one-shot operations; there are no streaming
|
|
18
|
+
`FileHandle` variants and no `AbortSignal` cancellation.
|
|
19
|
+
- The pool has four workers; very many simultaneous operations queue behind
|
|
20
|
+
them (they still do not block the event loop).
|
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
> Part of [Node extension: unimplemented](../node.md) · [简体中文](../../zh-CN/unimplemented/node/fs.md)
|
|
4
4
|
|
|
5
|
-
`fs`
|
|
6
|
-
descriptors, metadata, links, `cp`, `glob`, `Dir`, `constants`) and
|
|
7
|
-
`fs/promises`
|
|
5
|
+
`fs` implements the **synchronous** API surface (files, directories,
|
|
6
|
+
descriptors, metadata, links, `cp`, `glob`, `Dir`, `constants`), and
|
|
7
|
+
`fs/promises` runs every file/descriptor operation on the asynchronous I/O pool
|
|
8
8
|
(see [implemented: `fs`](../../implemented/node/fs.md) and
|
|
9
|
-
[implemented: `fs/promises`](../../implemented/node/fs-promises.md)).
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
provide timers and sockets):
|
|
9
|
+
[implemented: `fs/promises`](../../implemented/node/fs-promises.md)). What is
|
|
10
|
+
still missing is mostly about APIs that need a different backend or callback
|
|
11
|
+
plumbing:
|
|
13
12
|
|
|
14
13
|
| Unimplemented API | Category |
|
|
15
14
|
| --- | --- |
|
|
@@ -24,7 +23,7 @@ provide timers and sockets):
|
|
|
24
23
|
- `readFileSync` supports encoding options (default/`utf8`/`ascii`/`latin1`/`binary`/ `hex`/`base64`/`base64url`), but **does not return a `Buffer`** by default: even though `Buffer` exists (simulated as a plain object), `readFileSync` still returns a string. Pass a `Buffer`-producing path or read raw bytes explicitly.
|
|
25
24
|
- `utf16le` / `ucs2` decoding is not implemented (the bytes are returned as text).
|
|
26
25
|
- `watch` / `watchFile` / `unwatchFile` return API-shaped emitter objects whose `.close()` / `.on()` methods exist but which **never fire** events.
|
|
27
|
-
- `Dir.read(cb)` / `Dir.close(cb)`
|
|
26
|
+
- `Dir.read(cb)` / `Dir.close(cb)` complete **synchronously** (the callback is invoked immediately); `FileHandle` methods do return real promises now.
|
|
28
27
|
- `mkdtempSync` always appends 6 random characters to the prefix (it does not require a trailing `XXXXXX`).
|
|
29
28
|
- `globSync` supports `*`, `?`, `[...]` and `**` but not the `exclude` callback or `follow`; `**` does not descend through symlinks (matching Node's default).
|
|
30
29
|
- `cpSync` symlink handling follows Node for the common cases but `verbatimSymlinks` is not supported.
|
|
@@ -10,10 +10,10 @@ is missing or differs from Node.
|
|
|
10
10
|
## Contents
|
|
11
11
|
|
|
12
12
|
- [`fs`](./node/fs.md) — callback-style async operations, real file watching, option callbacks
|
|
13
|
-
- [`fs/promises`](./node/fs-promises.md) —
|
|
13
|
+
- [`fs/promises`](./node/fs-promises.md) — `FileHandle` append/read offsets, immediate `Dir` callbacks, inert `watch`, no cancellation
|
|
14
14
|
- [Fully unimplemented modules](./node/modules.md) — `https`, `readline`, `tls`, `cluster`, `vm`, ...
|
|
15
15
|
- [Global objects and namespaces](./node/globals.md) — `global`, `__dirname`, `require` outside `node_modules`
|
|
16
|
-
- [Async and the event-loop model](./node/async.md) —
|
|
16
|
+
- [Async and the event-loop model](./node/async.md) — blocked file watching, blocking `net.connect`, no keep-alive, fixed-size offload pool
|
|
17
17
|
- [The Bun extension](./node/bun.md) — design example only
|
|
18
18
|
|
|
19
19
|
A module's implemented page links to its limitation page; when a limitation is
|
|
@@ -9,6 +9,6 @@
|
|
|
9
9
|
| Type checker | ✗ only diagnostic codes are defined; no checker |
|
|
10
10
|
| Full standard library | partial: Math / JSON / Date / Map / Set / RegExp / `Error` / `BigInt` / `Symbol` / `structuredClone` implemented; String.normalize missing |
|
|
11
11
|
| Multi-file module bundling | partial: relative-path `.ts` bundling, ESM `node_modules` packages, namespace imports and bare-specifier extension module imports implemented; CommonJS is handled only inside `node_modules`; circular dependencies / live bindings are not implemented |
|
|
12
|
-
| Real async runtime | ✓ `async`/`await` suspends on pending promises on the `select(2)` reactor: timers, TCP sockets and promise callbacks resume the coroutine. Still missing:
|
|
12
|
+
| Real async runtime | ✓ `async`/`await` suspends on pending promises on the `select(2)` reactor: timers, TCP sockets, offloaded file/HTTP work and promise callbacks resume the coroutine. Still missing: real OS-level file watching, non-blocking `net.connect`, TLS for `fetch`, and user-visible worker threads (`worker_threads` uses child processes) |
|
|
13
13
|
| Windows binary artifact verification | adapted at the build layer (`.exe` suffix, link flag branch), verified in CI |
|
|
14
14
|
| Precise ECMAScript number / string / comparison semantics | partial, see [Runtime deviations](./deviations.md) |
|
package/doc/zh-CN/gui/api.md
CHANGED
|
@@ -64,6 +64,7 @@ win.documentTree() // 序列化的 DOM(调试用)
|
|
|
64
64
|
win.layoutTree() // 序列化的布局盒(调试用)
|
|
65
65
|
win.paintCount() // 显示列表中图形的数量
|
|
66
66
|
win.paintList() // 序列化的显示列表(调试用)
|
|
67
|
+
win.inlineSvg(selector) // 序列化的内联 <svg>:{ markup, width, height, shapes }
|
|
67
68
|
win.measureText(text, fontSize?, family?) // 整形后的推进宽度(像素)
|
|
68
69
|
win.fontMetrics(fontSize?, family?) // { ascent, descent, lineHeight, ready }
|
|
69
70
|
win.hitTest(x, y) // 最深的元素描述符,或 ""
|
|
@@ -72,6 +73,12 @@ win.advance(ms) // 推进 CSS transition 时钟(测试
|
|
|
72
73
|
win.stats() // 引擎/运行时计数器(内存排查用)
|
|
73
74
|
```
|
|
74
75
|
|
|
76
|
+
`win.inlineSvg(selector)` 返回引擎为第一个匹配 `selector` 的 `<svg>` 元素构建的
|
|
77
|
+
独立 SVG 文档(`markup`),以及 NanoSVG 从它解析出的固有尺寸与图形数量;没有
|
|
78
|
+
匹配或元素不是 `<svg>` 时返回 `undefined`。它让序列化器的规范大小写
|
|
79
|
+
(`viewBox`、`linearGradient`、`gradientUnits` 等)无需 GPU 即可测试——HTML
|
|
80
|
+
解析器会把它们全部小写化。
|
|
81
|
+
|
|
75
82
|
`win.stats()` 返回 `{ heapBytes, heapAllocations, treeNodes, detachedRoots,`
|
|
76
83
|
`detachedNodes, handles, handleSlots, listeners }`:运行时存活堆、DOM 树规模、
|
|
77
84
|
分离节点池(detached pool)以及元素句柄表。稳定状态下它们都应保持常量——若
|