@solidrt/flux-types 0.0.63 → 0.0.65
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 +93 -10
- package/gui/spatial.d.ts +10 -0
- package/gui/test.d.ts +78 -0
- package/index.d.ts +2 -0
- package/modules/fs.d.ts +22 -0
- package/modules/image.d.ts +96 -0
- package/modules/path.d.ts +62 -1
- package/modules/process.d.ts +14 -0
- package/modules/test.d.ts +142 -0
- package/package.json +1 -1
package/gui/gpu.d.ts
CHANGED
|
@@ -219,10 +219,50 @@ declare module "flux:gpu" {
|
|
|
219
219
|
* via `<texture src>` is out of contract; also a draw target format (the
|
|
220
220
|
* target encodes what a pass writes).
|
|
221
221
|
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
222
|
+
* "rgba32ui" is the integer data-texture format: four u32 per texel
|
|
223
|
+
* (a Uint32Array of width*height*4), fetched through a `usampler2D` as a
|
|
224
|
+
* uvec4 and unpacked in the shader (`uintBitsToFloat`, `unpackHalf2x16`,
|
|
225
|
+
* `unpackUnorm4x8`) - bit-exact packed records where a float texel would
|
|
226
|
+
* canonicalize the bits (a splat cloud's records and SH bands, any
|
|
227
|
+
* packed table). Nearest-only and sample-only exactly like "rgba32f"
|
|
228
|
+
* (integer textures never filter), and a float sampler over it (or a
|
|
229
|
+
* `usampler2D` over any other format) throws at bind.
|
|
230
|
+
*
|
|
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.
|
|
224
253
|
*/
|
|
225
|
-
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"
|
|
226
266
|
export type TextureFormatOption = { format?: TextureFormat }
|
|
227
267
|
/**
|
|
228
268
|
* This device's hard ceilings, queried once at startup: process constants.
|
|
@@ -268,15 +308,27 @@ declare module "flux:gpu" {
|
|
|
268
308
|
* "rgba16f" texture throws where this is false.
|
|
269
309
|
*/
|
|
270
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
|
|
271
320
|
}
|
|
272
321
|
/**
|
|
273
322
|
* Create an immutable texture from a pixel buffer holding exactly one
|
|
274
323
|
* frame at the declared format's size. The view type must match the
|
|
275
324
|
* format: byte formats ("rgba8", "rgba8-srgb", "r8") take a Uint8Array,
|
|
276
|
-
* float formats ("r32f", "rgba32f", "rgba16f") a Float32Array.
|
|
277
|
-
*
|
|
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.
|
|
278
330
|
*/
|
|
279
|
-
export function createTexture(data: Uint8Array | Float32Array, width: number, height: number, opts?: SamplerOptions & TextureFormatOption & LabelOption): TextureId
|
|
331
|
+
export function createTexture(data: Uint8Array | Float32Array | Uint32Array, width: number, height: number, opts?: SamplerOptions & TextureFormatOption & LabelOption): TextureId
|
|
280
332
|
/**
|
|
281
333
|
* Create a cube map from six square faces in GL order (+X, -X, +Y, -Y,
|
|
282
334
|
* +Z, -Z), each exactly one `size` x `size` frame at the declared format
|
|
@@ -297,7 +349,7 @@ declare module "flux:gpu" {
|
|
|
297
349
|
* returns (the cube seen from outside), so a face set authored as seen
|
|
298
350
|
* from inside (Three's) is mirrored per image before upload.
|
|
299
351
|
*/
|
|
300
|
-
export function createCubeTexture(faces: (Uint8Array | Float32Array)[] | (Uint8Array | Float32Array)[][], size: number, opts?: SamplerOptions & TextureFormatOption & LabelOption): TextureId
|
|
352
|
+
export function createCubeTexture(faces: (Uint8Array | Float32Array | Uint32Array)[] | (Uint8Array | Float32Array | Uint32Array)[][], size: number, opts?: SamplerOptions & TextureFormatOption & LabelOption): TextureId
|
|
301
353
|
/**
|
|
302
354
|
* Create a texture intended to be updated later via {@link uploadTexture}. The
|
|
303
355
|
* seed buffer must hold at least one frame at the declared format's size
|
|
@@ -305,14 +357,14 @@ declare module "flux:gpu" {
|
|
|
305
357
|
* {@link createTexture}, the view type must match the format (Uint8Array
|
|
306
358
|
* for byte formats, Float32Array for float formats).
|
|
307
359
|
*/
|
|
308
|
-
export function createMutableTexture(data: Uint8Array | Float32Array, width: number, height: number, opts?: SamplerOptions & TextureFormatOption & LabelOption): TextureId
|
|
360
|
+
export function createMutableTexture(data: Uint8Array | Float32Array | Uint32Array, width: number, height: number, opts?: SamplerOptions & TextureFormatOption & LabelOption): TextureId
|
|
309
361
|
/**
|
|
310
362
|
* Replace a mutable texture's pixels; the frame size and required view
|
|
311
363
|
* type (Uint8Array for byte formats, Float32Array for float formats)
|
|
312
364
|
* follow the format the id was created with. `data` may hold several
|
|
313
365
|
* frames; `offset` (default 0) selects which frame to upload.
|
|
314
366
|
*/
|
|
315
|
-
export function uploadTexture(id: TextureId, data: Uint8Array | Float32Array, offset?: number): void
|
|
367
|
+
export function uploadTexture(id: TextureId, data: Uint8Array | Float32Array | Uint32Array, offset?: number): void
|
|
316
368
|
/**
|
|
317
369
|
* Replace a texture's storage with a new size at the same id (an id-stable
|
|
318
370
|
* resize): `<texture src>` references and shader sampler bindings keep
|
|
@@ -322,7 +374,7 @@ declare module "flux:gpu" {
|
|
|
322
374
|
* state). Render target ids are rejected - resize those with
|
|
323
375
|
* {@link setTargetSize}.
|
|
324
376
|
*/
|
|
325
|
-
export function resizeTexture(id: TextureId, data: Uint8Array | Float32Array, width: number, height: number): void
|
|
377
|
+
export function resizeTexture(id: TextureId, data: Uint8Array | Float32Array | Uint32Array, width: number, height: number): void
|
|
326
378
|
/**
|
|
327
379
|
* Destroy a texture (immutable, mutable, or shader). Frame-safe: the id is
|
|
328
380
|
* reclaimed by the runtime once the render tree no longer references it, so
|
|
@@ -775,6 +827,21 @@ declare module "flux:gpu" {
|
|
|
775
827
|
* nothing there and throws.
|
|
776
828
|
*/
|
|
777
829
|
retain?: boolean
|
|
830
|
+
/**
|
|
831
|
+
* The INDEX materialization, for a population whose records live in a
|
|
832
|
+
* data texture the shader fetches by record index: the records are
|
|
833
|
+
* handed off with {@link transferRecords} at `stride` bytes each (the
|
|
834
|
+
* key offsets address them), and the ordered instance buffer holds
|
|
835
|
+
* the sorted record indices as u32 - `arrayStride / 4` ids per
|
|
836
|
+
* instance record, so one drawn instance covers K records - written
|
|
837
|
+
* whole on every re-sort (the sorted ids, then 0xFFFFFFFF to the end
|
|
838
|
+
* of the buffer, which the shader culls before fetching). A re-sort
|
|
839
|
+
* then uploads 4 bytes per record instead of the record. Retains by
|
|
840
|
+
* nature; takes exactly one instance-step buffer, over handed-off
|
|
841
|
+
* records only (publishing that buffer through the lease throws), and
|
|
842
|
+
* `instanceCount` picks the first count x K records of the mirror.
|
|
843
|
+
*/
|
|
844
|
+
indices?: { stride: number }
|
|
778
845
|
}
|
|
779
846
|
/**
|
|
780
847
|
* The order half of a draw-entry update: `orderDirection` replaces the
|
|
@@ -887,6 +954,22 @@ declare module "flux:gpu" {
|
|
|
887
954
|
* record sets through {@link beginBufferWrite}/{@link endBufferWrite}.
|
|
888
955
|
*/
|
|
889
956
|
export function writeBuffer(id: BufferId, data: Uint8Array, byteOffset?: number): void
|
|
957
|
+
/**
|
|
958
|
+
* Hand a buffer's full record set to the engine - the write-once form of
|
|
959
|
+
* an ordered instance buffer ({@link InstanceOrder}): the engine's order
|
|
960
|
+
* mirror takes ownership of a copy, so the app keeps none, and the
|
|
961
|
+
* ordered entry sorts and republishes from the mirror on its own - the
|
|
962
|
+
* first publish when the entry attaches (nothing draws before that),
|
|
963
|
+
* re-sorts on `orderDirection` changes, and prefix republishes when the
|
|
964
|
+
* entry's `instanceCount` changes (the first n records are the sorted
|
|
965
|
+
* population). Hand off before the entry attaches; a second call
|
|
966
|
+
* replaces the records wholesale. By the time an entry attaches over
|
|
967
|
+
* the buffer the records must be whole records of the order's stride
|
|
968
|
+
* and, for gathered records, fit the buffer (an indexed order's buffer
|
|
969
|
+
* holds their ids instead, see {@link InstanceOrder}). The mirror is
|
|
970
|
+
* freed with the buffer.
|
|
971
|
+
*/
|
|
972
|
+
export function transferRecords(id: BufferId, records: Uint8Array): void
|
|
890
973
|
/**
|
|
891
974
|
* Destroy a vertex buffer. Pipeline textures drawing from it hold their own
|
|
892
975
|
* reference, so destruction order does not matter; further writes to the id
|
package/gui/spatial.d.ts
CHANGED
|
@@ -178,6 +178,16 @@ declare module "flux:spatial" {
|
|
|
178
178
|
queue?: number
|
|
179
179
|
/** Ascending above depth inside the queue; default 0. */
|
|
180
180
|
renderOrder?: number
|
|
181
|
+
/** Feed the entry's instance-order projected-key direction (the
|
|
182
|
+
* `orderDirection` update of a position-key `instanceOrder`) from the
|
|
183
|
+
* target's view: each flush whose view forward, mapped into the
|
|
184
|
+
* node's model frame, turned past a small core-side epsilon writes
|
|
185
|
+
* the new direction - on a retained order that re-sorts and
|
|
186
|
+
* republishes the records core-side, so a whole population follows
|
|
187
|
+
* the camera with no per-frame work here. For entries that declared
|
|
188
|
+
* a position-key instanceOrder; on any other entry the first feed
|
|
189
|
+
* fails (with a warning) and the feed stops, the sink stays. */
|
|
190
|
+
orderFeed?: boolean
|
|
181
191
|
}
|
|
182
192
|
/**
|
|
183
193
|
* Route the node's world matrix to one draw entry's `uModel` (and
|
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
|
|
@@ -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/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,142 @@
|
|
|
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, `srt 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. `srt 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
|
+
/** The same value, as `Object.is` sees it: `NaN` is `NaN`, `0` is not `-0`, and two equal objects are not the same. */
|
|
92
|
+
toBe(expected: unknown): void
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Equal in value, recursively. Objects are compared by their own
|
|
96
|
+
* enumerable properties, and a property whose value is `undefined`
|
|
97
|
+
* counts as absent. Arrays and typed arrays are compared element by
|
|
98
|
+
* element, and a typed array equals neither a plain array nor a typed
|
|
99
|
+
* array of another kind. `ArrayBuffer`s are compared byte by byte,
|
|
100
|
+
* `Map`s and `Set`s by their entries, `Date`s by their time, errors by
|
|
101
|
+
* name and message. Leaves are compared as `toBe` does.
|
|
102
|
+
*/
|
|
103
|
+
toEqual(expected: unknown): void
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* A number within half a unit of the last of `digits` decimal places
|
|
107
|
+
* (default 2): the difference is below `10 ** -digits / 2`. The way to
|
|
108
|
+
* compare floating point results.
|
|
109
|
+
*/
|
|
110
|
+
toBeCloseTo(expected: number, digits?: number): void
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* For a function: calling it throws. With a string, the thrown error's
|
|
114
|
+
* message has to contain it; with a pattern, to match it; with an error
|
|
115
|
+
* class, the thrown value has to be an instance of it.
|
|
116
|
+
*/
|
|
117
|
+
toThrow(expected?: string | RegExp | (new (...args: any[]) => unknown)): void
|
|
118
|
+
|
|
119
|
+
/** Exactly `null`; `undefined` is not. */
|
|
120
|
+
toBeNull(): void
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* A string that contains the part `item`, or an array (or any other
|
|
124
|
+
* iterable) with an entry that is `item` by `===`.
|
|
125
|
+
*/
|
|
126
|
+
toContain(item: unknown): void
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* An object that carries everything `pattern` names: each property of
|
|
130
|
+
* the pattern has to be present and, for a nested plain object, match
|
|
131
|
+
* in turn; anything else has to be equal as `toEqual` sees it.
|
|
132
|
+
* Properties the pattern does not name are ignored. An array in the
|
|
133
|
+
* pattern is matched entry by entry, at the same length.
|
|
134
|
+
*/
|
|
135
|
+
toMatchObject(pattern: object): void
|
|
136
|
+
|
|
137
|
+
toBeLessThan(expected: number | bigint): void
|
|
138
|
+
toBeLessThanOrEqual(expected: number | bigint): void
|
|
139
|
+
toBeGreaterThan(expected: number | bigint): void
|
|
140
|
+
toBeGreaterThanOrEqual(expected: number | bigint): void
|
|
141
|
+
}
|
|
142
|
+
}
|