@solidrt/flux-types 0.0.64 → 0.0.66
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/gui/gpu.d.ts +48 -5
- package/gui/rendertree.d.ts +1 -1
- package/gui/spatial.d.ts +3 -3
- package/gui/test.d.ts +78 -0
- package/index.d.ts +3 -1
- package/modules/fs.d.ts +22 -0
- package/modules/image.d.ts +96 -0
- package/modules/isolate.d.ts +1 -1
- package/modules/path.d.ts +62 -1
- package/modules/process.d.ts +14 -0
- package/modules/test.d.ts +158 -0
- package/package.json +1 -1
package/gui/gpu.d.ts
CHANGED
|
@@ -228,10 +228,41 @@ declare module "flux:gpu" {
|
|
|
228
228
|
* (integer textures never filter), and a float sampler over it (or a
|
|
229
229
|
* `usampler2D` over any other format) throws at bind.
|
|
230
230
|
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
231
|
+
* "etc2-rgba8", "etc2-rgba8-srgb", "bc7-rgba8" and "bc7-rgba8-srgb" are
|
|
232
|
+
* the block-compressed formats: 4x4 texel blocks of 16 bytes (1 byte per
|
|
233
|
+
* texel, a quarter of rgba8) that the sampler decodes for free, so a
|
|
234
|
+
* texture-heavy scene holds a quarter of the GPU memory. The payload is
|
|
235
|
+
* the raw blocks as a Uint8Array: ceil(width/4) * ceil(height/4) * 16
|
|
236
|
+
* bytes per level, and with `mipmap: true` the FULL chain concatenated
|
|
237
|
+
* level-major down to 1x1 (a compressed chain is uploaded, never
|
|
238
|
+
* generated - the runtime cannot build mips on compressed storage). The
|
|
239
|
+
* "-srgb" twins decode to linear light on sample exactly like
|
|
240
|
+
* "rgba8-srgb". Filterable like the byte formats (linear, mipmap,
|
|
241
|
+
* anisotropy all apply), and create-once and sample-only: createMutableTexture,
|
|
242
|
+
* uploadTexture, resizeTexture, readTexture, copyTexture and cube maps
|
|
243
|
+
* throw for them, and `<texture src>` display is out of contract.
|
|
244
|
+
* Availability differs by device, which is the point of the two codecs:
|
|
245
|
+
* the "etc2-*" formats are GLES 3.0 core and every device takes them
|
|
246
|
+
* (a desktop driver without the hardware format expands them to RGBA8,
|
|
247
|
+
* losing the memory cut but not correctness), so ETC2 is the only
|
|
248
|
+
* compressed format that is portable to SHIP raw; the "bc7-*" formats
|
|
249
|
+
* exist where `limits.bc7Textures` is true (every desktop: ANGLE over
|
|
250
|
+
* D3D11 and Metal, Mesa) and throw elsewhere, so BC7 is a load-time
|
|
251
|
+
* target - what `transcodeTexture` in flux:image picks on a desktop from
|
|
252
|
+
* one shipped KTX2 payload - not a format an app ships raw.
|
|
233
253
|
*/
|
|
234
|
-
export type TextureFormat =
|
|
254
|
+
export type TextureFormat =
|
|
255
|
+
| "rgba8"
|
|
256
|
+
| "rgba8-srgb"
|
|
257
|
+
| "r8"
|
|
258
|
+
| "r32f"
|
|
259
|
+
| "rgba32f"
|
|
260
|
+
| "rgba16f"
|
|
261
|
+
| "rgba32ui"
|
|
262
|
+
| "etc2-rgba8"
|
|
263
|
+
| "etc2-rgba8-srgb"
|
|
264
|
+
| "bc7-rgba8"
|
|
265
|
+
| "bc7-rgba8-srgb"
|
|
235
266
|
export type TextureFormatOption = { format?: TextureFormat }
|
|
236
267
|
/**
|
|
237
268
|
* This device's hard ceilings, queried once at startup: process constants.
|
|
@@ -277,13 +308,25 @@ declare module "flux:gpu" {
|
|
|
277
308
|
* "rgba16f" texture throws where this is false.
|
|
278
309
|
*/
|
|
279
310
|
halfFloatRenderable: boolean
|
|
311
|
+
/**
|
|
312
|
+
* Whether the "bc7-rgba8" and "bc7-rgba8-srgb" formats upload on this
|
|
313
|
+
* device (the EXT_texture_compression_bptc extension: every desktop,
|
|
314
|
+
* no mobile GPU). The "etc2-*" formats are GLES 3.0 core and need no
|
|
315
|
+
* flag. A loader with one shipped payload picks BC7 where this is
|
|
316
|
+
* true and ETC2 otherwise; `transcodeTexture` in flux:image applies
|
|
317
|
+
* that rule when given no target.
|
|
318
|
+
*/
|
|
319
|
+
bc7Textures: boolean
|
|
280
320
|
}
|
|
281
321
|
/**
|
|
282
322
|
* Create an immutable texture from a pixel buffer holding exactly one
|
|
283
323
|
* frame at the declared format's size. The view type must match the
|
|
284
324
|
* format: byte formats ("rgba8", "rgba8-srgb", "r8") take a Uint8Array,
|
|
285
|
-
* float formats ("r32f", "rgba32f", "rgba16f") a Float32Array.
|
|
286
|
-
*
|
|
325
|
+
* float formats ("r32f", "rgba32f", "rgba16f") a Float32Array. A
|
|
326
|
+
* compressed format ("etc2-*", "bc7-*") takes its raw blocks as a
|
|
327
|
+
* Uint8Array, the full mip chain when `mipmap: true` (see
|
|
328
|
+
* {@link TextureFormat}); the byte count in the error names what is
|
|
329
|
+
* expected. Returns the texture id.
|
|
287
330
|
*/
|
|
288
331
|
export function createTexture(data: Uint8Array | Float32Array | Uint32Array, width: number, height: number, opts?: SamplerOptions & TextureFormatOption & LabelOption): TextureId
|
|
289
332
|
/**
|
package/gui/rendertree.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The render-tree bridge (gui-enabled runtime only): the low-level surface the
|
|
2
2
|
// renderer drives to build and mutate the native tree - create/insert/delete
|
|
3
3
|
// nodes, write properties, query layout, measure text. Displaying the built
|
|
4
|
-
// tree is the runner's concern ("
|
|
4
|
+
// tree is the runner's concern ("sol:render" in lattice), not part of this
|
|
5
5
|
// module; requestFrame here only schedules a future frame.
|
|
6
6
|
|
|
7
7
|
declare module "flux:rendertree" {
|
package/gui/spatial.d.ts
CHANGED
|
@@ -155,7 +155,7 @@ declare module "flux:spatial" {
|
|
|
155
155
|
* left alone, so rewriting the whole array to move one component never
|
|
156
156
|
* restarts the others (a held write's target counts, so re-sending it
|
|
157
157
|
* does not restart its delay). Each settled component fires one
|
|
158
|
-
* "spatialTransitionEnd" engine event (
|
|
158
|
+
* "spatialTransitionEnd" engine event (sol:events), payload
|
|
159
159
|
* `{ node, component: NodeComponent }` - on a live node; a leaving
|
|
160
160
|
* node's settles feed its free instead (exitNode).
|
|
161
161
|
*/
|
|
@@ -646,7 +646,7 @@ declare module "flux:spatial" {
|
|
|
646
646
|
* positive fades in, negative out; past 0 the player is removed).
|
|
647
647
|
* When a player finishes or is removed without finishing (faded out,
|
|
648
648
|
* clip or target destroyed), the "spatialClipEnd" engine event
|
|
649
|
-
* (
|
|
649
|
+
* (sol:events) fires with payload { player, reason: "finished" |
|
|
650
650
|
* "dropped" }, before the same frame's handlers.
|
|
651
651
|
*/
|
|
652
652
|
export function createPlayer(clip: ClipId, targets: NodeId[], speed: number, loop: boolean, weight: number, fade: number): PlayerId
|
|
@@ -661,7 +661,7 @@ declare module "flux:spatial" {
|
|
|
661
661
|
* the player's time each advance, and its travel since the previous
|
|
662
662
|
* advance (continuous across a loop wrap, weighted by the player's
|
|
663
663
|
* weight) is reported as one "spatialRootMotion" engine event
|
|
664
|
-
* (
|
|
664
|
+
* (sol:events) per advance, payload { player, x, y, z, yaw }, before
|
|
665
665
|
* the frame's handlers. The translation is given in the root's CURRENT
|
|
666
666
|
* facing (the clip's own turn so far undone), i.e. the character's
|
|
667
667
|
* local frame. `rotation`, a rotation channel of the same clip (the
|
package/gui/test.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The node verbs of a gui test: finding and reading the nodes of the render
|
|
3
|
+
* tree. A GUI build of Flux with the test module has them, which is the
|
|
4
|
+
* SolidRT dev client; an app test reaches them through
|
|
5
|
+
* `@solidrt/test`, whose locators are built on this module.
|
|
6
|
+
*
|
|
7
|
+
* A node reads as the control API's `/tree` reports it: records cross as
|
|
8
|
+
* JSON text, shaped in one place for both readers.
|
|
9
|
+
*/
|
|
10
|
+
declare module "flux:test/gui" {
|
|
11
|
+
/**
|
|
12
|
+
* One field of a query: a string matches exactly (case-sensitive), `true`
|
|
13
|
+
* matches every node that has the field (for a pattern the caller checks
|
|
14
|
+
* itself), absent is no constraint.
|
|
15
|
+
*/
|
|
16
|
+
export type FieldQuery = string | true
|
|
17
|
+
|
|
18
|
+
export interface NodeQuery {
|
|
19
|
+
/** The element kind's name: "view", "text", ... */
|
|
20
|
+
kind?: string
|
|
21
|
+
/** The text a text or span node shows. */
|
|
22
|
+
text?: FieldQuery
|
|
23
|
+
/** The node's `label` prop. */
|
|
24
|
+
label?: FieldQuery
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The nodes under `root` (the tree root when null, `root` included) that
|
|
29
|
+
* the query matches, depth first, at most `limit`: a JSON array of node
|
|
30
|
+
* records without children, each with `path`, the ids from the search
|
|
31
|
+
* root down to it. A node whose text matches under an ancestor showing
|
|
32
|
+
* the very same text is left out. Null when `root` is no current node.
|
|
33
|
+
*/
|
|
34
|
+
export function find(root: number | null, query: NodeQuery, limit: number): string | null
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The record of node `id` with `depth` levels of children (0: the node
|
|
38
|
+
* alone), as JSON; null when `id` is no current node.
|
|
39
|
+
*/
|
|
40
|
+
export function node(id: number, depth: number): string | null
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The subtree of node `id` as an outline: one node per line indented by
|
|
44
|
+
* depth, with its kind, label, text and painted box (`view [save] 20,50
|
|
45
|
+
* 120x40`), and with `props` the off-default props as JSON after it. No
|
|
46
|
+
* ids, so two runs print the same text. A span that is all of its
|
|
47
|
+
* parent's text is folded into the parent's line, as `find` sees it.
|
|
48
|
+
* Null when `id` is no current node.
|
|
49
|
+
*/
|
|
50
|
+
export function outline(id: number, props: boolean): string | null
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Whether the node is painted where a user could see it: mounted and not
|
|
54
|
+
* exiting, its painted box still has area once every clipping ancestor
|
|
55
|
+
* and the window have cut it, and the opacity multiplied up its ancestor
|
|
56
|
+
* chain is above 0. It says nothing of what is painted over it; `hit`
|
|
57
|
+
* answers that.
|
|
58
|
+
*/
|
|
59
|
+
export function visible(id: number): boolean
|
|
60
|
+
|
|
61
|
+
/** The ids from the tree root down to `id`; null when `id` is no current node. */
|
|
62
|
+
export function path(id: number): number[] | null
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The ids of the nodes a pointer at the window point reaches, root first,
|
|
66
|
+
* the node it lands on last; empty when nothing is hit.
|
|
67
|
+
*/
|
|
68
|
+
export function hit(x: number, y: number): number[]
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The GPU resource inventory as JSON, the record the control API's `/gpu`
|
|
72
|
+
* answers with: textures, buffers, pipelines, programs and each draw
|
|
73
|
+
* target's entries. `label` keeps the resources created with exactly
|
|
74
|
+
* that label; `draw` is the draw entry id whose params are reported in
|
|
75
|
+
* full (matrix-valued ones are elided elsewhere).
|
|
76
|
+
*/
|
|
77
|
+
export function gpu(label: string | null, draw: number | null): string
|
|
78
|
+
}
|
package/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
/// <reference path="./modules/wasm.d.ts" />
|
|
14
14
|
/// <reference path="./modules/ffi.d.ts" />
|
|
15
15
|
/// <reference path="./modules/isolate.d.ts" />
|
|
16
|
+
/// <reference path="./modules/test.d.ts" />
|
|
16
17
|
|
|
17
18
|
// Web-standard globals. The runtime is QuickJS, not a browser or Node, so it
|
|
18
19
|
// ships no lib.dom / @types/bun: these declarations are the sole source for
|
|
@@ -31,7 +32,7 @@
|
|
|
31
32
|
// microphone/gpu are flux:* modules like the rest; requestAnimationFrame stays a
|
|
32
33
|
// global (web-standard name). flux:rendertree is the render-tree bridge the
|
|
33
34
|
// renderer drives; displaying the built tree (renderFrame) is the runner's
|
|
34
|
-
// concern (
|
|
35
|
+
// concern (sol:render in lattice), not part of flux.
|
|
35
36
|
/// <reference path="./gui/rendertree.d.ts" />
|
|
36
37
|
/// <reference path="./gui/camera.d.ts" />
|
|
37
38
|
/// <reference path="./gui/microphone.d.ts" />
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
/// <reference path="./gui/spatial.d.ts" />
|
|
41
42
|
/// <reference path="./gui/video.d.ts" />
|
|
42
43
|
/// <reference path="./gui/raf.d.ts" />
|
|
44
|
+
/// <reference path="./gui/test.d.ts" />
|
|
43
45
|
|
|
44
46
|
declare let Flux: {
|
|
45
47
|
/** The flux runtime version. */
|
package/modules/fs.d.ts
CHANGED
|
@@ -120,4 +120,26 @@ declare module "flux:fs" {
|
|
|
120
120
|
* @param to Path it should have afterwards.
|
|
121
121
|
*/
|
|
122
122
|
export function rename(from: string, to: string): Promise<void>
|
|
123
|
+
/**
|
|
124
|
+
* The files whose path matches the glob `pattern`, sorted: Node's
|
|
125
|
+
* `fs.glob`, as a promise of the whole list. The pattern language is
|
|
126
|
+
* `matchesGlob`'s (flux:path), and the two agree: a file is listed
|
|
127
|
+
* exactly when its path matches. Files only; a directory is walked and
|
|
128
|
+
* never listed, so an empty list means no file matches.
|
|
129
|
+
*
|
|
130
|
+
* Each path is given as seen from `cwd`. Without `cwd` the pattern stands
|
|
131
|
+
* against the process cwd like any relative path, and in a SolidRT app
|
|
132
|
+
* running an installed version a pattern that starts in `assets/` lists
|
|
133
|
+
* that version's assets. With `cwd` it is that directory as the OS has
|
|
134
|
+
* it.
|
|
135
|
+
*
|
|
136
|
+
* Throws on a malformed pattern. Rejects when a directory on the way
|
|
137
|
+
* cannot be read.
|
|
138
|
+
*
|
|
139
|
+
* @param pattern The glob pattern, written with "/".
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* let textures = await glob("assets/textures/*.png")
|
|
143
|
+
*/
|
|
144
|
+
export function glob(pattern: string, options?: { cwd?: string }): Promise<string[]>
|
|
123
145
|
}
|
package/modules/image.d.ts
CHANGED
|
@@ -44,4 +44,100 @@ declare module "flux:image" {
|
|
|
44
44
|
img: DecodedImage,
|
|
45
45
|
options?: { format?: "png" | "jpeg"; quality?: number; alpha?: AlphaMode },
|
|
46
46
|
): Uint8Array
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A transcoded compressed texture, shaped as the arguments of
|
|
50
|
+
* `createTexture` in flux:gpu: `createTexture(t.data, t.width, t.height,
|
|
51
|
+
* { format: t.format, mipmap: t.mipmap })`. `data` holds every level of
|
|
52
|
+
* the file, level 0 first; `mipmap` says it is the full chain down to
|
|
53
|
+
* 1x1 rather than the base level alone. `format` is the target with the
|
|
54
|
+
* "-srgb" qualifier when the file's texels are sRGB-encoded (the file
|
|
55
|
+
* says, not the caller).
|
|
56
|
+
*/
|
|
57
|
+
export type TranscodedTexture = {
|
|
58
|
+
data: Uint8Array
|
|
59
|
+
width: number
|
|
60
|
+
height: number
|
|
61
|
+
format: "etc2-rgba8" | "etc2-rgba8-srgb" | "bc7-rgba8" | "bc7-rgba8-srgb" | "rgba8" | "rgba8-srgb"
|
|
62
|
+
mipmap: boolean
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The Basis Universal codec inside a KTX2 file. "etc1s" is the small one:
|
|
67
|
+
* lossy at JPEG-class quality, around 1 bit per texel, for color maps
|
|
68
|
+
* (base color, emissive). "uastc" is the high quality one, for normal
|
|
69
|
+
* maps and data maps, where ETC1S artifacts show: 8 bits per texel
|
|
70
|
+
* before the file's zstd compression, which takes it to 2 to 6 on a
|
|
71
|
+
* real map at the default quality.
|
|
72
|
+
*/
|
|
73
|
+
export type TextureCodec = "etc1s" | "uastc"
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Transcodes a KTX2 file of Basis Universal texel data (ETC1S or UASTC, a
|
|
77
|
+
* 2D texture with one level or its full mip chain - what `encodeTexture`
|
|
78
|
+
* writes and what glTF's KHR_texture_basisu carries) into the block
|
|
79
|
+
* format a GPU samples. This is the load-time half of compressed
|
|
80
|
+
* textures: an app ships ONE file for every platform and each device
|
|
81
|
+
* turns it into its own native format, a quarter of the GPU memory of
|
|
82
|
+
* the same image as "rgba8".
|
|
83
|
+
*
|
|
84
|
+
* Without `target` the runtime picks this device's format: "bc7-rgba8"
|
|
85
|
+
* where the GPU reports it (`limits.bc7Textures` in flux:gpu, every
|
|
86
|
+
* desktop), "etc2-rgba8" everywhere else (GLES 3.0 core, every mobile
|
|
87
|
+
* GPU). A runtime with no GPU cannot pick and throws; name the target
|
|
88
|
+
* there. "rgba8" is the uncompressed form, for inspection and comparison;
|
|
89
|
+
* no device needs it as a fallback.
|
|
90
|
+
*
|
|
91
|
+
* The texels come through as the file holds them: a file baked here is
|
|
92
|
+
* premultiplied like every texture, a file from elsewhere (glTF stores
|
|
93
|
+
* straight alpha) samples as straight alpha.
|
|
94
|
+
*
|
|
95
|
+
* Asynchronous: the work runs off the JS thread, and a scene's worth of
|
|
96
|
+
* textures is seconds of it on a slow device. Start as many as there are
|
|
97
|
+
* textures: the runtime transcodes four at a time and queues the rest, so
|
|
98
|
+
* a `Promise.all` over a scene neither floods the device's cores nor
|
|
99
|
+
* multiplies the memory in flight. Rejects when the bytes are
|
|
100
|
+
* not such a file (another codec, a cube map, an array, a partial mip
|
|
101
|
+
* chain). Throws on a runtime built without compressed textures
|
|
102
|
+
* (`Flux.capabilities` lacks "ktx2").
|
|
103
|
+
*/
|
|
104
|
+
export function transcodeTexture(
|
|
105
|
+
ktx2: Uint8Array,
|
|
106
|
+
options?: { target?: "etc2-rgba8" | "bc7-rgba8" | "rgba8" },
|
|
107
|
+
): Promise<TranscodedTexture>
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Encodes RGBA8 pixels into a KTX2 file of Basis Universal texel data,
|
|
111
|
+
* the bake-time half of compressed textures. The file is device
|
|
112
|
+
* independent - the same bytes whatever machine encodes it and whatever
|
|
113
|
+
* device loads it - and `transcodeTexture` turns it into each device's
|
|
114
|
+
* format at load.
|
|
115
|
+
*
|
|
116
|
+
* `img` holds the pixels as the GPU will sample them, which means
|
|
117
|
+
* premultiplied: pass a `decodeImage` result as it is (blocks cannot be
|
|
118
|
+
* premultiplied after the fact, and mips only filter correctly on
|
|
119
|
+
* premultiplied color). `codec` is required. `srgb` (default false) says
|
|
120
|
+
* the pixels are sRGB-encoded color: set it for base color and emissive
|
|
121
|
+
* maps, never for normal or data maps; it flags the file, makes the
|
|
122
|
+
* error metric perceptual and filters the mips in linear light. `mipmap`
|
|
123
|
+
* (default false) generates and stores the full mip chain, filtered
|
|
124
|
+
* across the edges as `wrap` says ("clamp" default, or "repeat" for a
|
|
125
|
+
* texture sampled tiled). `quality` is 0..1 (default 0.5), higher is
|
|
126
|
+
* larger and closer to the source. For "uastc", 1 is the encoder's best
|
|
127
|
+
* blocks and anything below it trades block error for a file that
|
|
128
|
+
* compresses better.
|
|
129
|
+
*
|
|
130
|
+
* Asynchronous and slow by design (seconds for a large image):
|
|
131
|
+
* bake-time work, never something to do at load. The runtime encodes
|
|
132
|
+
* four at a time and queues the rest, so starting many neither floods
|
|
133
|
+
* the machine's cores nor multiplies the encoder's memory; the pixels of
|
|
134
|
+
* an encode that waits are held meanwhile, so with many images decode
|
|
135
|
+
* and start a few at a time. Rejects when `data.length` does not match
|
|
136
|
+
* `width * height * 4`. Throws on a runtime built without compressed
|
|
137
|
+
* textures.
|
|
138
|
+
*/
|
|
139
|
+
export function encodeTexture(
|
|
140
|
+
img: DecodedImage,
|
|
141
|
+
options: { codec: TextureCodec; srgb?: boolean; mipmap?: boolean; wrap?: "clamp" | "repeat"; quality?: number },
|
|
142
|
+
): Promise<Uint8Array>
|
|
47
143
|
}
|
package/modules/isolate.d.ts
CHANGED
|
@@ -70,7 +70,7 @@ declare module "flux:isolate" {
|
|
|
70
70
|
/**
|
|
71
71
|
* A handle on an isolate module: a `"use isolate"` module in a SolidRT
|
|
72
72
|
* project (id = its path relative to the source root, without extension),
|
|
73
|
-
* or `isolates/<id>.
|
|
73
|
+
* or `isolates/<id>.fluxbc`/`.js` next to the entry under standalone flux. Each property is
|
|
74
74
|
* an async function that runs the export of that name in a second runtime
|
|
75
75
|
* on its own thread (own heap, own event loop, the non-gui `flux:*`
|
|
76
76
|
* modules). Arguments and results are copied ({@link Sendable}).
|
package/modules/path.d.ts
CHANGED
|
@@ -23,4 +23,65 @@ declare module "flux:path" {
|
|
|
23
23
|
* guarantee; use `resolveWithin` when a segment is untrusted.
|
|
24
24
|
*/
|
|
25
25
|
export function join(...segments: string[]): string
|
|
26
|
-
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The last component of `path`, trailing separators ignored: Node's
|
|
29
|
+
* `path.basename`. Lexical. `basename("/a/model.gltf")` is "model.gltf",
|
|
30
|
+
* `basename("/a/b/")` is "b".
|
|
31
|
+
*
|
|
32
|
+
* @param ext A suffix to cut off the result, as in
|
|
33
|
+
* `basename("/a/model.gltf", ".gltf")`, which is "model". A
|
|
34
|
+
* name that is nothing but the suffix keeps it.
|
|
35
|
+
*/
|
|
36
|
+
export function basename(path: string, ext?: string): string
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* `path` without its last component: Node's `path.dirname`. Lexical.
|
|
40
|
+
* `dirname("/a/b.txt")` is "/a", and a bare name's is ".".
|
|
41
|
+
*/
|
|
42
|
+
export function dirname(path: string): string
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The extension of `path`'s last component, from its last dot on, or ""
|
|
46
|
+
* when it has none: Node's `path.extname`. `extname("a.tar.gz")` is ".gz";
|
|
47
|
+
* a leading dot names a hidden file, so `extname(".bashrc")` is "".
|
|
48
|
+
*/
|
|
49
|
+
export function extname(path: string): string
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The path that leads from `from` to `to`: Node's `path.relative`.
|
|
53
|
+
* Lexical. Each side stands against the process cwd when it is relative,
|
|
54
|
+
* so `relative(".", path)` is `path` as seen from the cwd.
|
|
55
|
+
* `relative("/a/b", "/a/c/d.txt")` is "../c/d.txt", and a place to itself
|
|
56
|
+
* is "". Two paths with nothing in common (another drive on Windows) give
|
|
57
|
+
* `to` as it resolved.
|
|
58
|
+
*/
|
|
59
|
+
export function relative(from: string, to: string): string
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Whether `path` matches the glob `pattern`, as a whole: Node's
|
|
63
|
+
* `path.matchesGlob`, with a smaller pattern language.
|
|
64
|
+
*
|
|
65
|
+
* - `*` is any run of characters inside one segment, none included
|
|
66
|
+
* - `**` as a whole segment is any number of segments, none included:
|
|
67
|
+
* "a/(**)/b" without the parentheses matches "a/b" and "a/x/y/b", and
|
|
68
|
+
* "assets/(**)" every path below "assets" (not "assets" itself)
|
|
69
|
+
* - `?` is one character of a segment
|
|
70
|
+
* - `[a-z]` is one character of the set, `[!a-z]` one outside it
|
|
71
|
+
*
|
|
72
|
+
* A character the language reads as syntax is written as a set of one:
|
|
73
|
+
* `[*]`, `[?]`, `[[]`. There are no `{a,b}` groups (test each pattern)
|
|
74
|
+
* and no escape character. Matching is on the text as given:
|
|
75
|
+
* case-sensitive, nothing normalized, a leading dot an ordinary
|
|
76
|
+
* character. On Windows "/" and "\\" match each other, so a pattern is
|
|
77
|
+
* written with "/" on every platform. Lexical: nothing is read from
|
|
78
|
+
* disk; `glob` in flux:fs lists the files a pattern matches.
|
|
79
|
+
*
|
|
80
|
+
* Throws on a malformed pattern (an unclosed set, a `**` that is not a
|
|
81
|
+
* whole segment), naming the character.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* matchesGlob("assets/textures/lion_head.png", "assets/textures/lion_*.png") // true
|
|
85
|
+
*/
|
|
86
|
+
export function matchesGlob(path: string, pattern: string): boolean
|
|
87
|
+
}
|
package/modules/process.d.ts
CHANGED
|
@@ -52,6 +52,20 @@ declare module "flux:process" {
|
|
|
52
52
|
* plain object, not Node's live and writable `process.env`.
|
|
53
53
|
*/
|
|
54
54
|
export let env: Record<string, string | undefined>
|
|
55
|
+
/**
|
|
56
|
+
* Ends the process with `code` as its exit status, Node's `process.exit`.
|
|
57
|
+
* For a command line tool that is done or has failed: the process ends
|
|
58
|
+
* inside the call, so nothing pending runs (timers, promises, requests in
|
|
59
|
+
* flight); what was printed is flushed first.
|
|
60
|
+
*
|
|
61
|
+
* For a script run by the flux binaries. It throws anywhere else: in a
|
|
62
|
+
* windowed app, which ends with `exit()` from `@solidrt/core` so its
|
|
63
|
+
* `onQuit` handlers run, and in an isolate, which is not the process.
|
|
64
|
+
*
|
|
65
|
+
* @param code An integer in 0..255, 0 (success) when omitted. Anything
|
|
66
|
+
* else throws.
|
|
67
|
+
*/
|
|
68
|
+
export function exit(code?: number): never
|
|
55
69
|
/**
|
|
56
70
|
* Listen for an OS signal. The callback receives the signal name. Returns an
|
|
57
71
|
* unsubscribe function. Unix only; a no-op elsewhere.
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for a flux program: `test` registers one and `expect` asserts
|
|
3
|
+
* inside it. A test file is run by a test host, `sol test <file>` (the
|
|
4
|
+
* `flux` binary's `--test` mode underneath); imported anywhere else the
|
|
5
|
+
* module throws, since nothing would run the tests.
|
|
6
|
+
*
|
|
7
|
+
* Every test runs in an engine of its own. The host evaluates the file
|
|
8
|
+
* once to list its tests and once more for each test, so a test starts
|
|
9
|
+
* from the file's freshly evaluated state: a variable, a listener or a
|
|
10
|
+
* timer of one test is not there for the next, a test's result does not
|
|
11
|
+
* depend on the tests before it, and an uncaught error (a throw in a timer
|
|
12
|
+
* callback, a rejection nobody handles) fails the test it happened in. The
|
|
13
|
+
* file's top level therefore runs once per test, plus once for the
|
|
14
|
+
* listing; keep it to registering tests and cheap setup.
|
|
15
|
+
*
|
|
16
|
+
* The names are the familiar ones and the semantics are simplified. Tests
|
|
17
|
+
* are flat: there is no `describe` and there are no hooks. The file is the
|
|
18
|
+
* group, the test name is a sentence that names its subject, and shared
|
|
19
|
+
* setup is a plain function a test calls.
|
|
20
|
+
*
|
|
21
|
+
* A test runs on real time: timers, `performance.now()` and `Date.now()`
|
|
22
|
+
* are the wall clock's, as they are for the program under test and for
|
|
23
|
+
* whatever is on the other side of its sockets. Logic with a timeout is
|
|
24
|
+
* tested by waiting for it, or takes its delay as a parameter.
|
|
25
|
+
*
|
|
26
|
+
* `Math.random()` is seeded in a test's engine, from before the file is
|
|
27
|
+
* evaluated: random inputs, and code under test that calls
|
|
28
|
+
* `Math.random()`, are the same on every run and do not depend on the
|
|
29
|
+
* tests that ran before. It is the engine's generator in kind and
|
|
30
|
+
* resolution, only its start is fixed. An isolate the test spawns is
|
|
31
|
+
* seeded too, with a seed derived from the test's. `sol test --seed <n>`
|
|
32
|
+
* runs the tests on another sequence.
|
|
33
|
+
*
|
|
34
|
+
* A test that does not finish within 5 seconds of real time fails as timed
|
|
35
|
+
* out, a synchronous loop included, and a test that waits on a promise
|
|
36
|
+
* nothing will settle fails at once. Either way its engine is dropped with
|
|
37
|
+
* whatever it left running. A safety cap, not a wait.
|
|
38
|
+
*
|
|
39
|
+
* Present on the `flux` binary only (capability `"test"`), not in a
|
|
40
|
+
* shipping runtime.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* import { test, expect } from "flux:test"
|
|
44
|
+
*
|
|
45
|
+
* test("matchPath: a param segment matches any value", () => {
|
|
46
|
+
* expect(matchPath("/users/$id", "/users/7")).toEqual({ id: "7" })
|
|
47
|
+
* })
|
|
48
|
+
*/
|
|
49
|
+
declare module "flux:test" {
|
|
50
|
+
/**
|
|
51
|
+
* Registers a test. `fn` passes when it returns, or when the promise it
|
|
52
|
+
* returns fulfills, and fails when it throws or the promise rejects.
|
|
53
|
+
* Tests run one after another, in the order they were registered. The
|
|
54
|
+
* file has to register the same tests every time it is evaluated.
|
|
55
|
+
*
|
|
56
|
+
* Throws when the name is empty or already taken in this file, and when
|
|
57
|
+
* called while a test runs: register at the top level of the file.
|
|
58
|
+
*/
|
|
59
|
+
export function test(name: string, fn: () => void | Promise<void>): void
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Starts an assertion on `received`. Each matcher throws when it does not
|
|
63
|
+
* hold, which fails the test; the message names the matcher and prints
|
|
64
|
+
* the expected and the received value.
|
|
65
|
+
*/
|
|
66
|
+
export function expect(received: unknown): Matchers
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Fulfills once nothing the test started is still in flight: work that
|
|
70
|
+
* completes by itself (a fetch, a body or file read, a query, a connect,
|
|
71
|
+
* a call into an isolate) has landed, and what it woke has run and
|
|
72
|
+
* started nothing more. For work a test set off without holding its
|
|
73
|
+
* promise; awaiting the promise itself is the direct way when there is
|
|
74
|
+
* one.
|
|
75
|
+
*
|
|
76
|
+
* What stands is not waited for: a listening server, an open socket or a
|
|
77
|
+
* read waiting on its peer, a running child process, an event listener,
|
|
78
|
+
* a timer. A test that wants a timer to fire waits for it itself.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* save(record) // writes in the background
|
|
82
|
+
* await settle()
|
|
83
|
+
* expect(await file(path).text()).toBe(expected)
|
|
84
|
+
*/
|
|
85
|
+
export function settle(): Promise<void>
|
|
86
|
+
|
|
87
|
+
export interface Matchers {
|
|
88
|
+
/** The same matchers, inverted: each throws when it does hold. */
|
|
89
|
+
not: Matchers
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* For a promise: the same matchers on its rejection reason, each
|
|
93
|
+
* awaiting the promise first and returning a promise to await in turn.
|
|
94
|
+
* A promise that fulfills fails the matcher, with or without `not`.
|
|
95
|
+
* `toThrow` sees the reason as the thrown value, so a rejection reads
|
|
96
|
+
* like a throw: `await expect(p).rejects.toThrow("no such file")`.
|
|
97
|
+
*/
|
|
98
|
+
rejects: RejectionMatchers
|
|
99
|
+
|
|
100
|
+
/** The same value, as `Object.is` sees it: `NaN` is `NaN`, `0` is not `-0`, and two equal objects are not the same. */
|
|
101
|
+
toBe(expected: unknown): void
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Equal in value, recursively. Objects are compared by their own
|
|
105
|
+
* enumerable properties, and a property whose value is `undefined`
|
|
106
|
+
* counts as absent. Arrays and typed arrays are compared element by
|
|
107
|
+
* element, and a typed array equals neither a plain array nor a typed
|
|
108
|
+
* array of another kind. `ArrayBuffer`s are compared byte by byte,
|
|
109
|
+
* `Map`s and `Set`s by their entries, `Date`s by their time, errors by
|
|
110
|
+
* name and message. Leaves are compared as `toBe` does.
|
|
111
|
+
*/
|
|
112
|
+
toEqual(expected: unknown): void
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* A number within half a unit of the last of `digits` decimal places
|
|
116
|
+
* (default 2): the difference is below `10 ** -digits / 2`. The way to
|
|
117
|
+
* compare floating point results.
|
|
118
|
+
*/
|
|
119
|
+
toBeCloseTo(expected: number, digits?: number): void
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* For a function: calling it throws. With a string, the thrown error's
|
|
123
|
+
* message has to contain it; with a pattern, to match it; with an error
|
|
124
|
+
* class, the thrown value has to be an instance of it.
|
|
125
|
+
*/
|
|
126
|
+
toThrow(expected?: string | RegExp | (new (...args: any[]) => unknown)): void
|
|
127
|
+
|
|
128
|
+
/** Exactly `null`; `undefined` is not. */
|
|
129
|
+
toBeNull(): void
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* A string that contains the part `item`, or an array (or any other
|
|
133
|
+
* iterable) with an entry that is `item` by `===`.
|
|
134
|
+
*/
|
|
135
|
+
toContain(item: unknown): void
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* An object that carries everything `pattern` names: each property of
|
|
139
|
+
* the pattern has to be present and, for a nested plain object, match
|
|
140
|
+
* in turn; anything else has to be equal as `toEqual` sees it.
|
|
141
|
+
* Properties the pattern does not name are ignored. An array in the
|
|
142
|
+
* pattern is matched entry by entry, at the same length.
|
|
143
|
+
*/
|
|
144
|
+
toMatchObject(pattern: object): void
|
|
145
|
+
|
|
146
|
+
toBeLessThan(expected: number | bigint): void
|
|
147
|
+
toBeLessThanOrEqual(expected: number | bigint): void
|
|
148
|
+
toBeGreaterThan(expected: number | bigint): void
|
|
149
|
+
toBeGreaterThanOrEqual(expected: number | bigint): void
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** The matchers of `expect(promise).rejects`: `Matchers`, each returning a promise. */
|
|
153
|
+
export type RejectionMatchers = {
|
|
154
|
+
[K in Exclude<keyof Matchers, "rejects">]: Matchers[K] extends (...args: infer A) => void
|
|
155
|
+
? (...args: A) => Promise<void>
|
|
156
|
+
: RejectionMatchers
|
|
157
|
+
}
|
|
158
|
+
}
|