@solidrt/flux-types 0.0.51 → 0.0.53

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/README.md CHANGED
@@ -46,12 +46,12 @@ rely on must be named.
46
46
 
47
47
  - `Flux` global (`version`, `capabilities`).
48
48
  - `flux:*` modules: `flux:http`, `flux:fs`, `flux:sqlite`, `flux:subprocess`,
49
- `flux:p2p`, `flux:net`, `flux:mdns`, `flux:process`, `flux:path`, `flux:wasm`, `flux:ffi`,
49
+ `flux:p2p`, `flux:net`, `flux:mdns`, `flux:process`, `flux:tty`, `flux:path`, `flux:wasm`, `flux:ffi`,
50
50
  and (on a gui-enabled runtime)
51
- `flux:camera`, `flux:microphone`, `flux:audio`, `flux:gpu`.
51
+ `flux:camera`, `flux:microphone`, `flux:audio`, `flux:gpu`, `flux:spatial`.
52
52
  - Web-standard globals: `console`, `fetch` + `Headers`/`Request`/`Response`,
53
53
  `setTimeout`/`setInterval`/`queueMicrotask`, `performance`, `WebSocket`,
54
- `TextEncoder`/`TextDecoder`. These are deliberate subsets matching exactly what
54
+ `TextEncoder`/`TextDecoder`, `crypto.subtle.digest`. These are deliberate subsets matching exactly what
55
55
  the runtime implements.
56
56
  - GUI globals (gui-enabled runtime only): `requestAnimationFrame` /
57
57
  `cancelAnimationFrame` (web-standard names, so kept global).
package/gui/camera.d.ts CHANGED
@@ -65,9 +65,10 @@ declare module "flux:camera" {
65
65
  * if permission is denied, and resolves once the first frame is ready. On
66
66
  * Linux a session that delivers neither within 10 seconds rejects with a
67
67
  * timeout error and releases the device (a wedged capture backend would
68
- * otherwise hold it and never settle). Rejects with "camera subsystem is
69
- * starting" while the subsystem is still coming up - wait for listCameras
70
- * to report a device before opening.
68
+ * otherwise hold it and never settle). The first open also starts the
69
+ * camera subsystem and waits for it (on every platform, up to 10 seconds,
70
+ * then rejects with a timeout), so there is no need to poll listCameras
71
+ * before opening.
71
72
  */
72
73
  export function open(options?: CameraOpenOptions): Promise<CameraSession>
73
74
  /**
package/gui/gpu.d.ts CHANGED
@@ -17,11 +17,12 @@
17
17
  // they return.
18
18
  //
19
19
  // Sampling is a per-texture property declared at creation: every create path
20
- // accepts `{ filter?, wrap? }` ("linear"/"nearest", "clamp"/"repeat";
21
- // defaults linear + clamp for every origin). The state follows the id
22
- // everywhere it is sampled - shader passes and `<texture>` display alike -
23
- // and survives id-stable resizes. It cannot be changed after creation. No
24
- // mipmaps exist.
20
+ // accepts `{ filter?, wrap?, mipmap? }` ("linear"/"nearest", "clamp"/"repeat",
21
+ // boolean; defaults linear + clamp + no mips for every origin). The state
22
+ // follows the id everywhere it is sampled - shader passes and `<texture>`
23
+ // display alike - and survives id-stable resizes. It cannot be changed after
24
+ // creation. `mipmap: true` keeps a mip chain on the id, regenerated after
25
+ // every upload or render, so shader sampling minifies without aliasing.
25
26
  //
26
27
  // Compositing several targets is a render-tree job, not a shader one: stack
27
28
  // `<texture>` elements and set their `blendMode` (the full Skia set, e.g.
@@ -103,8 +104,9 @@ declare module "flux:gpu" {
103
104
  * call site: a name with no active uniform, a value whose length does not
104
105
  * fit the declared type, a `sampler2D` named here (samplers bind via
105
106
  * `textures`), or a value that is not a number / number array throws.
106
- * Reflection only sees active uniforms, so a uniform that is declared but
107
- * optimized out counts as unknown - remove the write (or use the uniform).
107
+ * A uniform that is declared but optimized out by the compiler is accepted
108
+ * with a warning and the write is skipped, so one param object can drive
109
+ * shader variants that do not all use every uniform.
108
110
  * An `undefined` value is skipped, so conditional spreads stay usable.
109
111
  */
110
112
  export type ShaderParams = Record<string, number | number[]>
@@ -118,8 +120,29 @@ declare module "flux:gpu" {
118
120
  * sampler2D inputs AND `<texture src>` display (a "nearest" texture
119
121
  * upscales with hard pixels on screen - the pixel-art path). `wrap` only
120
122
  * matters to shaders sampling outside 0..1; the display draw never tiles.
121
- */
122
- export type SamplerOptions = { filter?: FilterMode; wrap?: WrapMode }
123
+ *
124
+ * `mipmap` (default false) keeps a mip chain on the id: rebuilt by the
125
+ * runtime after every upload (data textures) and every render (targets,
126
+ * automatically - nothing to schedule), and used by shader sampling when
127
+ * the texture is minified (trilinear for "linear", per-level nearest for
128
+ * "nearest"). Without it minification skips texels and aliases. Only
129
+ * shader sampling minifies through the chain; the `<texture>` display
130
+ * draw samples the full-size level. Regeneration is one GPU pass per
131
+ * upload or render, so a per-frame texture pays it per frame.
132
+ */
133
+ export type SamplerOptions = { filter?: FilterMode; wrap?: WrapMode; mipmap?: boolean }
134
+ /**
135
+ * One `textures` binding value: a texture id, sampled with the texture's
136
+ * own declared state, or `{ id, filter?, wrap? }` to sample that id with a
137
+ * different filter and/or wrap in this binding only (blur a "nearest"
138
+ * atlas linearly; tile a clamped target in one consumer). The override is
139
+ * a per-pass deviation: the texture's own state stays what `<texture>`
140
+ * paints and what every binding without an override uses. `mipmap` is
141
+ * not overridable - the chain either exists on the id or it does not.
142
+ */
143
+ export type TextureBinding = TextureId | { id: TextureId; filter?: FilterMode; wrap?: WrapMode }
144
+ /** Sampler2D inputs by uniform name; see {@link TextureBinding}. */
145
+ export type TextureBindings = Record<string, TextureBinding>
123
146
  /**
124
147
  * A free-form debug name every create accepts (WebGPU's label): surfaced by
125
148
  * the dev server's GPU inventory (get_gpu_resources) and in engine log
@@ -243,7 +266,7 @@ declare module "flux:gpu" {
243
266
  width: number,
244
267
  height: number,
245
268
  params?: ShaderParams | null,
246
- opts?: { textures?: Record<string, TextureId> } & SamplerOptions & LabelOption,
269
+ opts?: { textures?: TextureBindings } & SamplerOptions & LabelOption,
247
270
  ): TextureId
248
271
  /**
249
272
  * Compile a single shader stage from raw GLSL ES: the primitive under
@@ -314,9 +337,12 @@ declare module "flux:gpu" {
314
337
  * entry's buffer, from record 0 (ES 3.0 has no base instance), so
315
338
  * several independently culled groups cannot share one buffer as
316
339
  * sub-ranges: give each group its own `instanceBuffer` and entry, and
317
- * cull it by `instanceCount`.
340
+ * cull it by `instanceCount`. Each attribute's `slot` (default 0)
341
+ * picks a buffer of the entry's `instanceBuffers` list (see
342
+ * {@link InstanceAttribute}); a pipeline using only slot 0 binds via
343
+ * the plain `instanceBuffer` key.
318
344
  */
319
- instanceAttributes?: VertexAttribute[]
345
+ instanceAttributes?: InstanceAttribute[]
320
346
  topology?: Topology
321
347
  blend?: BlendMode
322
348
  cull?: CullMode
@@ -368,6 +394,18 @@ declare module "flux:gpu" {
368
394
  * read-modify-write of its own pixels (decay, blur, simulation) still
369
395
  * ping-pongs across two manual targets - a pass can never sample the
370
396
  * texture it writes.
397
+ *
398
+ * `samples` (1, 2, 4 or 8; default 1) multisamples the target's storage:
399
+ * edges of filled geometry get coverage-weighted AA instead of binary
400
+ * jaggies. Storage only - the texture id still names a single-sample
401
+ * image, so display, sampling, `readTexture` and `copyTexture` are
402
+ * unaffected. The count is clamped to the device maximum and a
403
+ * configuration the driver refuses falls back to single-sample with a
404
+ * warning (read the effective value off the resource inventory). Cannot
405
+ * combine with `loadOp: "load"` (throws): multisampled storage cannot
406
+ * load the previous contents. Tiled mobile GPUs resolve in-tile
407
+ * (EXT_multisampled_render_to_texture); everywhere else a resolve blit
408
+ * follows each pass.
371
409
  */
372
410
  export function createShaderTarget(
373
411
  pipeline: RenderPipelineId,
@@ -375,12 +413,16 @@ declare module "flux:gpu" {
375
413
  height: number,
376
414
  params?: ShaderParams | null,
377
415
  opts?: {
378
- textures?: Record<string, TextureId>
416
+ textures?: TextureBindings
379
417
  buffer?: BufferId
380
418
  instanceBuffer?: BufferId
419
+ /** One buffer per instance slot of the pipeline (index = the
420
+ * attributes' `slot`); pass this OR `instanceBuffer`, not both. */
421
+ instanceBuffers?: BufferId[]
381
422
  clearColor?: [number, number, number, number]
382
423
  render?: "auto" | "manual"
383
424
  loadOp?: "clear" | "load"
425
+ samples?: 1 | 2 | 4 | 8
384
426
  } & (DrawRange | (IndexBinding & IndexRange)) &
385
427
  SamplerOptions &
386
428
  LabelOption,
@@ -392,6 +434,16 @@ declare module "flux:gpu" {
392
434
  * immediately.
393
435
  */
394
436
  export function destroyProgram(id: ProgramId): void
437
+ /**
438
+ * The vertex attributes a linked program actually reads (name and format),
439
+ * as the compiler left them: an `in` the vertex stage never uses is not
440
+ * listed, and a type no layout can feed (a matrix, an integer vector) is
441
+ * rejected at linkProgram. This is the list a pipeline over the program
442
+ * must cover between `attributes` and `instanceAttributes` - an uncovered
443
+ * or mis-formatted one throws at createRenderPipeline. Answered locally,
444
+ * no GPU round trip.
445
+ */
446
+ export function programAttributes(program: ProgramId): VertexAttribute[]
395
447
  export type Topology = "points" | "lines" | "line-strip" | "triangles" | "triangle-strip"
396
448
  /**
397
449
  * Blending for a pipeline's own draw. "none" (default) overwrites:
@@ -443,6 +495,17 @@ declare module "flux:gpu" {
443
495
  * shader's `in` declarations.
444
496
  */
445
497
  export type VertexAttribute = { name: string; format: "f32" | "vec2" | "vec3" | "vec4" }
498
+ /**
499
+ * One float attribute of a per-instance record. `slot` (default 0) picks
500
+ * which buffer of the entry's `instanceBuffers` list the attribute
501
+ * fetches from: attributes sharing a slot interleave into one record in
502
+ * list order, distinct slots are distinct buffers with their own strides
503
+ * - which is what lets two writers own instance data independently (a
504
+ * core-written pose buffer beside an app-written style buffer). Slots
505
+ * must be dense from 0, at most 4; a single-slot pipeline (every `slot`
506
+ * omitted) binds via the plain `instanceBuffer` key.
507
+ */
508
+ export type InstanceAttribute = VertexAttribute & { slot?: number }
446
509
  /**
447
510
  * A pipeline target's draw as data, WebGPU-style: `firstVertex` +
448
511
  * `vertexCount` pick the vertex range `[firstVertex, firstVertex +
@@ -492,6 +555,28 @@ declare module "flux:gpu" {
492
555
  * there is no base vertex (ES 3.0, like ES 3.0's missing base instance).
493
556
  */
494
557
  export type IndexRange = { firstIndex?: number; indexCount?: number; instanceCount?: number }
558
+ /**
559
+ * A draw entry's buffer swap: each key present re-points that role of the
560
+ * entry at another {@link createBuffer} buffer, absent keys keep their
561
+ * current buffer. Replace-only - the roles an entry fills are pipeline
562
+ * layout state (`attributes` needs a `buffer`, `instanceAttributes` an
563
+ * `instanceBuffer`) and indexing is its draw vocabulary, so naming a role
564
+ * the entry does not fill throws; `indexBuffer` travels with
565
+ * `indexFormat` as at create. The entry's current range is kept and
566
+ * rechecked against the new buffers' sizes: a swap to a buffer too small
567
+ * for the live range throws (shrink the range first); a larger buffer
568
+ * never does. This is the growth primitive: a population outgrowing its
569
+ * instance buffer creates a larger one, writes it, swaps, and destroys
570
+ * the old (the entry holds the old buffer alive until the swap lands, so
571
+ * either order is safe). `instanceBuffer` swaps slot 0;
572
+ * `instanceBuffers` swaps every slot at once and must fill exactly the
573
+ * slots the entry fills (pass one spelling or the other).
574
+ */
575
+ export type BufferUpdate = {
576
+ buffer?: BufferId
577
+ instanceBuffer?: BufferId
578
+ instanceBuffers?: BufferId[]
579
+ } & ({} | IndexBinding)
495
580
 
496
581
  /**
497
582
  * Compile a GLSL ES vertex+fragment pipeline into an offscreen texture of
@@ -531,12 +616,15 @@ declare module "flux:gpu" {
531
616
  height: number,
532
617
  params?: ShaderParams | null,
533
618
  opts?: {
534
- textures?: Record<string, TextureId>
619
+ textures?: TextureBindings
535
620
  attributes?: VertexAttribute[]
536
621
  buffer?: BufferId
537
622
  /** See {@link createRenderPipeline}'s `instanceAttributes`. */
538
- instanceAttributes?: VertexAttribute[]
623
+ instanceAttributes?: InstanceAttribute[]
539
624
  instanceBuffer?: BufferId
625
+ /** One buffer per instance slot (index = the attributes' `slot`);
626
+ * pass this OR `instanceBuffer`, not both. */
627
+ instanceBuffers?: BufferId[]
540
628
  topology?: Topology
541
629
  depth?: boolean
542
630
  depthWrite?: boolean
@@ -545,6 +633,7 @@ declare module "flux:gpu" {
545
633
  clearColor?: [number, number, number, number]
546
634
  render?: "auto" | "manual"
547
635
  loadOp?: "clear" | "load"
636
+ samples?: 1 | 2 | 4 | 8
548
637
  } & (DrawRange | (IndexBinding & IndexRange)) &
549
638
  SamplerOptions &
550
639
  LabelOption,
@@ -605,9 +694,13 @@ declare module "flux:gpu" {
605
694
  * (On a manual target nothing renders here; the range applies at its next
606
695
  * {@link renderTarget}.) An indexed target takes the {@link IndexRange}
607
696
  * spelling instead, bounds-checked against its index buffer; the pair
608
- * that does not match the target's mode throws.
697
+ * that does not match the target's mode throws. Buffer keys
698
+ * ({@link BufferUpdate}) swap the target's buffers in the same call: the
699
+ * merged range is checked against the swapped buffers, so one call grows a
700
+ * buffer and extends the range into it, and a call that throws changes
701
+ * nothing (range and buffers commit together or not at all).
609
702
  */
610
- export function setDraw(id: TextureId, draw: DrawRange | IndexRange): void
703
+ export function setDraw(id: TextureId, draw: (DrawRange | IndexRange) & BufferUpdate): void
611
704
  /**
612
705
  * Create a draw target: a render target whose contents are an ordered,
613
706
  * mutable LIST of draws - one render clears once, then executes every
@@ -623,6 +716,10 @@ declare module "flux:gpu" {
623
716
  * occlusion work. It is the storage half of the depth story; whether an
624
717
  * entry tests/writes depth is its pipeline's `depth`/`depthWrite` state,
625
718
  * and adding a depth-testing pipeline to a target without storage throws.
719
+ * `depth: "texture"` is the same storage as a SAMPLEABLE depth texture
720
+ * with an id of its own - {@link depthTexture} - for shadow maps,
721
+ * depth-of-field, SSAO; it cannot combine with `samples` (a multisampled
722
+ * depth texture is not sampleable; the create throws).
626
723
  *
627
724
  * `params` seeds the target's SHARED params - the target-level values
628
725
  * every entry reads, the same live channel {@link setTargetParams} drives
@@ -638,24 +735,64 @@ declare module "flux:gpu" {
638
735
  * "auto"` target re-renders exactly when its entries or their inputs
639
736
  * change - a static scene costs zero passes, however many entries it
640
737
  * holds, and one render is ONE pass however many entries it draws.
641
- * `render: "manual"` and `loadOp: "load"` compose exactly as on
642
- * {@link createShaderTarget}. With no entries a render is the clear alone.
738
+ * `render: "manual"`, `loadOp: "load"` and `samples` compose exactly as
739
+ * on {@link createShaderTarget}. With no entries a render is the clear
740
+ * alone.
643
741
  * Returns a texture id (display, resize, destroy like any target; entries
644
742
  * die with it).
743
+ *
744
+ * `into` makes a SUB-TARGET: a draw target that renders into the `width`
745
+ * x `height` rectangle at `x`/`y` (top-left origin, the texture leaf's
746
+ * `srcX`/`srcY` space; default 0) of draw target `into`'s storage instead
747
+ * of owning any. It is a draw target to every verb - entries, shared
748
+ * params and bindings, order, `setTargetSize` - with dirty state of its
749
+ * own, and the parent renders ALL its tiles in ONE pass: a changed tile
750
+ * redraws over its own rectangle (the rest keeps its pixels), a changed
751
+ * parent redraws everything. That is what makes N views or N shadow maps
752
+ * cost one pass instead of N. The returned id is not a texture: sample,
753
+ * display (`<d-texture src={parent} srcX srcY srcW srcH>`), read back and
754
+ * copy the PARENT; `depthTexture(parent)` is the tile's depth too. Depth,
755
+ * `samples`, `render` and `loadOp` are the parent's (passing them
756
+ * throws), `clearColor` is the tile's own. A rectangle partly outside the
757
+ * parent is clipped; {@link setTargetRect} moves it. Tiles do not nest.
758
+ * Destroying the parent destroys its tiles.
645
759
  */
646
760
  export function createDrawTarget(
647
761
  width: number,
648
762
  height: number,
649
763
  params?: ShaderParams | null,
650
764
  opts?: {
651
- depth?: boolean
652
- textures?: Record<string, TextureId>
765
+ depth?: boolean | "texture"
766
+ textures?: TextureBindings
653
767
  clearColor?: [number, number, number, number]
654
768
  render?: "auto" | "manual"
655
769
  loadOp?: "clear" | "load"
770
+ samples?: 1 | 2 | 4 | 8
771
+ into?: TextureId
772
+ x?: number
773
+ y?: number
656
774
  } & SamplerOptions &
657
775
  LabelOption,
658
776
  ): TextureId
777
+ /**
778
+ * The depth texture of a draw target created with `depth: "texture"`: a
779
+ * texture id of its own, stable for the target's life (a
780
+ * {@link setTargetSize} follows the color), holding the target's depth
781
+ * after every render - 24-bit window depth in 0..1, read as `.r` from a
782
+ * `sampler2D`. Bind it anywhere a texture binds ({@link setTargetTextures}
783
+ * for a whole scene target, an entry's `textures`, a fragment target's
784
+ * inputs); the dependency graph treats a binding to it as a binding to its
785
+ * target, so the depth pass renders first and a target sampling its own
786
+ * depth throws. SAMPLER-ONLY: it is not an upload texture and not a
787
+ * readback source (`readTexture`/`copyTexture` throw - render it through a
788
+ * pass to read it), its sampling is fixed at `nearest`/`clamp` (a depth
789
+ * texture is only complete at nearest without a comparison mode - filter
790
+ * in the shader, e.g. a PCF loop), and it dies with its target
791
+ * (`destroyTexture` on it throws). Displaying it via `<texture src>`
792
+ * shows the depth in the red channel. Throws for a target without texture
793
+ * depth.
794
+ */
795
+ export function depthTexture(target: TextureId): TextureId
659
796
  /**
660
797
  * Append a draw entry to a draw target: `pipeline` draws `opts.buffer`
661
798
  * (required when the pipeline declares attributes) with its own `params`
@@ -690,9 +827,12 @@ declare module "flux:gpu" {
690
827
  pipeline: RenderPipelineId,
691
828
  params?: ShaderParams | null,
692
829
  opts?: {
693
- textures?: Record<string, TextureId>
830
+ textures?: TextureBindings
694
831
  buffer?: BufferId
695
832
  instanceBuffer?: BufferId
833
+ /** One buffer per instance slot of the pipeline (index = the
834
+ * attributes' `slot`); pass this OR `instanceBuffer`, not both. */
835
+ instanceBuffers?: BufferId[]
696
836
  before?: DrawId
697
837
  } & (DrawRange | (IndexBinding & IndexRange)),
698
838
  ): DrawId
@@ -760,7 +900,7 @@ declare module "flux:gpu" {
760
900
  * everywhere it is declared, and each entry's effective inputs (its own
761
901
  * plus the applicable shared ones) must fit the device's texture units.
762
902
  */
763
- export function setTargetTextures(target: TextureId, textures: Record<string, TextureId>): void
903
+ export function setTargetTextures(target: TextureId, textures: TextureBindings): void
764
904
  /**
765
905
  * Resize a render target of any kind in place and re-render it: the id,
766
906
  * compiled programs, last-applied params, sampler bindings, and draw
@@ -769,19 +909,33 @@ declare module "flux:gpu" {
769
909
  * which carries seed pixels instead.)
770
910
  */
771
911
  export function setTargetSize(id: TextureId, width: number, height: number): void
912
+ /**
913
+ * Move and resize a sub-target's rectangle in its parent (top-left
914
+ * origin; every key required). The parent re-renders in full at the next
915
+ * flush. Throws for anything but a sub-target (see `into` on
916
+ * {@link createDrawTarget}); `setTargetSize` on a tile is this with the
917
+ * origin kept.
918
+ */
919
+ export function setTargetRect(id: TextureId, rect: { x: number; y: number; width: number; height: number }): void
772
920
  /**
773
921
  * Rebind one draw entry's sampler2D inputs by uniform name:
774
922
  * {@link setTargetTextures} addressed to a single entry, same merge,
775
923
  * validation, and cycle rules. Entries bind independently - two entries
776
924
  * may bind the same uniform name to different sources.
777
925
  */
778
- export function setDrawTextures(target: TextureId, draw: DrawId, textures: Record<string, TextureId>): void
926
+ export function setDrawTextures(target: TextureId, draw: DrawId, textures: TextureBindings): void
779
927
  /**
780
928
  * Update one draw entry's draw range: {@link setDraw} addressed to a
781
929
  * single entry, same partial merge, bounds validation, and vocabulary
782
930
  * rule (an indexed entry speaks {@link IndexRange}).
783
931
  */
784
932
  export function setDrawRange(target: TextureId, draw: DrawId, update: DrawRange | IndexRange): void
933
+ /**
934
+ * Swap one draw entry's buffers: the {@link BufferUpdate} half of
935
+ * {@link setDraw} addressed to a single entry, same replace-only rule and
936
+ * range recheck.
937
+ */
938
+ export function setDrawBuffers(target: TextureId, draw: DrawId, update: BufferUpdate): void
785
939
  /**
786
940
  * Reorder a draw target's list. `order` must name every current entry
787
941
  * exactly once - a full permutation of the live {@link DrawId}s; a
@@ -5,6 +5,7 @@
5
5
  // module; requestFrame here only schedules a future frame.
6
6
 
7
7
  declare module "flux:rendertree" {
8
+ import type { TextureId } from "flux:gpu"
8
9
  /** Font options for {@link measureText} and {@link prepareText}. */
9
10
  export interface MeasureTextOptions {
10
11
  fontFamily?: "sans" | "serif" | "mono" | (string & {})
@@ -77,9 +78,20 @@ declare module "flux:rendertree" {
77
78
 
78
79
  /** Create the window root node with the given id. */
79
80
  export function createRoot(id: number): void
81
+ /**
82
+ * Make `id`, an existing window node, the root again. Creating a window
83
+ * takes the root over, so this is the way back to an earlier window without
84
+ * recreating it (render()'s error boundary swapping the app's window back
85
+ * in on reset). No-op for the current root or an unknown id.
86
+ */
87
+ export function setRoot(id: number): void
80
88
  /** Create a node of `kind` (the primitive element name) with the given id. Throws an `Error` for a name that is not an element. */
81
89
  export function createNode(id: number, kind: string): void
82
- /** Insert `nodeId` under `parentId`, before `anchorId` if given (else appended). */
90
+ /**
91
+ * Insert `nodeId` under `parentId`, before `anchorId` if given (else appended).
92
+ * Throws an `Error` when a laid-out node would land under a detached (d-*)
93
+ * parent: a detached subtree is entirely detached. The tree is left untouched.
94
+ */
83
95
  export function insertNode(parentId: number, nodeId: number, anchorId?: number): void
84
96
  /**
85
97
  * Unlink `nodeId` from `parentId` but keep its subtree alive, so it can be
@@ -170,6 +182,18 @@ declare module "flux:rendertree" {
170
182
  * semantics), for comparing against pointer event coordinates.
171
183
  */
172
184
  export function getBoundingBoxViewport(id: number): { x: number, y: number, width: number, height: number } | null
185
+ /**
186
+ * The texture id of a snapshot repaint boundary's retained rasterization
187
+ * (its subtree's pixels at display scale, premultiplied, top-left origin,
188
+ * cropped to the layout box). Allocated on the first call and stable for
189
+ * the node's lifetime; it is re-pointed at the current pixels after every
190
+ * rasterization, so consumers never rebind. Before the first paint the id
191
+ * has no pixels yet (a `<texture>` measures 0x0, a shader pass skips the
192
+ * binding). Owned by the boundary: `destroyTexture` on it throws, and an
193
+ * unmounted boundary releases it through the deferred-destroy path. Throws
194
+ * if the node is not a snapshot boundary.
195
+ */
196
+ export function snapshotTexture(id: number): TextureId
173
197
  /**
174
198
  * Parses a CSS color string (hex, rgb()/rgba(), hsl()/hsla(), hwb(),
175
199
  * named colors) into packed 0xRRGGBBAA form (which the color property
@@ -0,0 +1,188 @@
1
+ // The spatial core (gui-enabled runtime only): a native transform hierarchy
2
+ // whose flush recomputes only the subtrees that changed and writes the
3
+ // results to draw sinks - a draw entry's `uModel` (+ `uNormal`) params and
4
+ // its instance count as the visibility switch. Generic on purpose: no camera,
5
+ // mesh or light concept. @solidrt/3d is the first consumer; any draw-list
6
+ // user with a tree of transforms (a 2D sprite scene, a skeleton) is the same
7
+ // shape. Node ids are plain numbers, generation-tagged and never reused, so
8
+ // a destroyed node's id throws everywhere.
9
+ //
10
+ // A transform argument is one Float32Array of 10: position xyz, unit
11
+ // quaternion xyzw, scale xyz. Writes queue the node; nothing reaches the
12
+ // GPU until flush(). worldMatrix() reads through pending writes.
13
+
14
+ declare module "flux:spatial" {
15
+ import type { BufferId, DrawId, TextureId } from "flux:gpu"
16
+
17
+ export type NodeId = number & { readonly __spatialNode: unique symbol }
18
+
19
+ /** A new root node. `visible: false` hides the node's whole subtree. */
20
+ export function createNode(transform: Float32Array, visible: boolean): NodeId
21
+ /** Free a node; its children become roots. A bound sink is dropped
22
+ * without a write (removing the entry is the caller's job). */
23
+ export function destroyNode(node: NodeId): void
24
+ /** Re-parent (null = make a root). Throws on a cycle. */
25
+ export function setParent(node: NodeId, parent: NodeId | null): void
26
+ /** Replace the local transform (compare before calling; an unchanged
27
+ * write still queues the node). Never consults or cancels transition
28
+ * tracks: a running track overwrites a raw write at the next frame
29
+ * (last write wins - the producer rule). */
30
+ export function setTransform(node: NodeId, transform: Float32Array): void
31
+ /**
32
+ * One node-transition spec, the element `transition` vocabulary minus
33
+ * the lifecycle conveniences: `{ duration }` / `{ duration, bounce }`
34
+ * is a spring (the default kind; retargets keep position and velocity,
35
+ * rotation springs keep angular velocity along the geodesic),
36
+ * `{ duration, curve }` a tween (rotation tweens slerp the geodesic;
37
+ * retargets restart from the current value), or the shorthand string
38
+ * `"<duration>ms [curve]"`. Durations in ms; no delay, from or exit.
39
+ */
40
+ export type NodeTransitionSpec =
41
+ | { duration: number; bounce?: number }
42
+ | { duration: number; curve: "linear" | "ease" | "ease-in" | "ease-out" | "ease-in-out" | [number, number, number, number] }
43
+ | string
44
+ /** The declaration setTransition takes: a spec per transform component
45
+ * plus `all` as a catch-all (per-component entries win). */
46
+ export interface NodeTransition {
47
+ position?: NodeTransitionSpec
48
+ rotation?: NodeTransitionSpec
49
+ scale?: NodeTransitionSpec
50
+ all?: NodeTransitionSpec
51
+ }
52
+ /**
53
+ * Declare (or with null clear) the node's transitions: with a config
54
+ * set, writeTransform animates instead of snapping. A bare string is
55
+ * the `all` catch-all. Clearing cancels the node's running tracks in
56
+ * place - it keeps its mid-flight transform, no settled events fire,
57
+ * and later writes snap. Replacing a config affects future writes only.
58
+ */
59
+ export function setTransition(node: NodeId, transition: NodeTransition | string | null): void
60
+ /**
61
+ * Replace the local transform THROUGH the transition declaration: a
62
+ * declared component animates toward the written value (the write is a
63
+ * target), an undeclared one snaps. Without a declaration this is
64
+ * setTransform. A component matching its running track's target is
65
+ * left alone, so rewriting the whole array to move one component never
66
+ * restarts the others. Each settled component fires one
67
+ * "spatialTransitionEnd" engine event (srt:events), payload
68
+ * `{ node, component: "position" | "rotation" | "scale" }`.
69
+ */
70
+ export function writeTransform(node: NodeId, transform: Float32Array): void
71
+ export function setVisible(node: NodeId, visible: boolean): void
72
+ /**
73
+ * Route the node's world matrix to one draw entry's `uModel` (and
74
+ * `uNormal`, the inverse-transpose, when `normal`). Validated like
75
+ * setDrawParams: the entry must exist and declare those uniforms. The
76
+ * entry is assumed switched off (instanceCount 0); the next flush turns it
77
+ * on with `count` when the node is shown, and off again when hidden.
78
+ * One draw sink PER TARGET: binding on a target the node already draws
79
+ * into replaces that sink, binding on another target adds one - a mesh
80
+ * drawn by a scene and by each of its views is one node with one flush.
81
+ */
82
+ export function bindDraw(node: NodeId, target: TextureId, draw: DrawId, normal: boolean, count: number): void
83
+ /** Remove the node's draw sink on `target`, or every draw sink without
84
+ * one. Issues no write: the entries are the caller's to remove. */
85
+ export function unbindDraw(node: NodeId, target?: TextureId): void
86
+ /** Change every bound entry's "on" count (an instanced mesh's record
87
+ * count); written at once to the entries currently on. */
88
+ export function setDrawCount(node: NodeId, count: number): void
89
+ /** Fill `out` (a Float32Array of 16, column-major) with the node's world
90
+ * matrix as the tree stands now, pending writes included. */
91
+ export function worldMatrix(node: NodeId, out: Float32Array): void
92
+ /** Effective visibility (every ancestor visible too) as of the last flush. */
93
+ export function shown(node: NodeId): boolean
94
+ /** Recompute every changed subtree and write the sinks; requests a frame
95
+ * when anything was written. */
96
+ export function flush(): void
97
+
98
+ export type ShapeId = number & { readonly __spatialShape: unique symbol }
99
+
100
+ /** One hit of raycast(), nearest first. `face`/`uv`/`normal` are present
101
+ * for nodes with a shape (uv only when the shape has UVs); a node with
102
+ * bounds but no shape reports its local box, distance and point only. */
103
+ export type Hit = {
104
+ node: NodeId
105
+ /** World units along the normalized ray. */
106
+ distance: number
107
+ point: [number, number, number]
108
+ /** World-space geometric normal, facing the ray. */
109
+ normal?: [number, number, number]
110
+ /** Triangle index into the shape's index list. */
111
+ face?: number
112
+ uv?: [number, number]
113
+ }
114
+
115
+ /** Set (null clears) the node's LOCAL tight box [minX, minY, minZ, maxX,
116
+ * maxY, maxZ]. With one the node is in the picking index: its world box
117
+ * follows the flush; hidden nodes stay in and are skipped at query time. */
118
+ export function setBounds(node: NodeId, bounds: Float32Array | null): void
119
+ /**
120
+ * Triangle data for the picking narrowphase, one copy shared by every
121
+ * node that references it: positions read from an interleaved vertex
122
+ * array (`stride` floats per vertex, xyz at `posOffset`, uv at
123
+ * `uvOffset`, -1 for none) and a Uint16Array/Uint32Array triangle list.
124
+ * Throws on out-of-range indices.
125
+ */
126
+ export function createShape(vertices: Float32Array, stride: number, posOffset: number, uvOffset: number, indices: Uint16Array | Uint32Array): ShapeId
127
+ /** Free a shape; nodes still referencing it fall back to their box. */
128
+ export function destroyShape(shape: ShapeId): void
129
+ export function setShape(node: NodeId, shape: ShapeId | null): void
130
+ /** Every shown node with bounds the ray strikes, nearest first. The
131
+ * direction need not be normalized; distances are world units. Reads
132
+ * the index as of the last flush. */
133
+ export function raycast(origin: Float32Array, direction: Float32Array): Hit[]
134
+ /**
135
+ * Every shown node with bounds whose local box, carried through its
136
+ * world transform, overlaps the world-axis box `bounds` (a Float32Array
137
+ * of 6: [minX, minY, minZ, maxX, maxY, maxZ]; touching counts, a point
138
+ * is min == max). Tested by separating axes, so a rotated flat rect -
139
+ * the 2d marquee case - tests exactly, never by its world AABB.
140
+ * Unordered; reads the index as of the last flush, like raycast.
141
+ */
142
+ export function overlap(bounds: Float32Array): NodeId[]
143
+
144
+ /**
145
+ * Route the world DIRECTION of the node's local `vector` (a
146
+ * Float32Array of 3) into
147
+ * vec3 slot `index` of the `len`-float shared array param `name` on a
148
+ * draw target: the flush writes `normalize(worldRotation * v)` there
149
+ * and re-sends the whole array when any slot changes; unbound slots are
150
+ * zeros. Every sink naming the same param shares one array (`len` must
151
+ * agree); what the slots mean - light directions, an emitter axis - is
152
+ * the caller's business, packed alongside its own non-spatial params.
153
+ * One slot sink per target, like bindDraw: rebinding on the same target
154
+ * replaces that sink (the abandoned slot zeroes), another target adds one.
155
+ */
156
+ export function bindDirectionSlot(node: NodeId, target: TextureId, name: string, len: number, index: number, vector: Float32Array): void
157
+ /** Remove the node's slot sink on `target`, or every slot sink without
158
+ * one (the abandoned slots zero at the next flush). */
159
+ export function unbindSlot(node: NodeId, target?: TextureId): void
160
+
161
+ /**
162
+ * Route the node's world pose to record slot `index` of vertex buffer
163
+ * `buffer` used as an instance buffer: the flush writes the 5 floats
164
+ * [x, y, angle, sx, sy] (world xy translation, rotation of the local x
165
+ * axis in the world xy plane, xy scale with sy negated when the matrix
166
+ * mirrors) at float offset index * 5. Writes batch: however many bound
167
+ * nodes moved, each flush issues at most one coalesced write per
168
+ * buffer, so producer-driven populations cost one buffer write per
169
+ * frame. A hidden node's slot zeroes (zero scale collapses the
170
+ * instance); so does an unbound or destroyed node's. Validated at bind
171
+ * time: the buffer must exist and the slot must fit its byte size.
172
+ * Rebinding replaces the node's record sink; the abandoned slot zeroes.
173
+ */
174
+ export function bindPoseRecord(node: NodeId, buffer: BufferId, index: number): void
175
+ /** Remove the node's record sink (its slot zeroes at the next flush). */
176
+ export function unbindRecord(node: NodeId): void
177
+ /**
178
+ * Move every record sink on buffer `old` to buffer `new`, slot indices
179
+ * untouched: the growth swap. The whole used range republishes into
180
+ * `new` at the next flush, so a population outgrowing its buffer swaps
181
+ * in a larger one with one call and one bulk write instead of a
182
+ * bindPoseRecord per node (pair it with the draw entry's own buffer
183
+ * swap, setDraw's `instanceBuffers`). Throws when nothing is bound to
184
+ * `old`, when `new` does not exist or cannot hold every bound slot, or
185
+ * when `new` already carries record sinks.
186
+ */
187
+ export function retargetRecords(old: BufferId, next: BufferId): void
188
+ }
package/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  /// <reference path="./modules/process.d.ts" />
2
+ /// <reference path="./modules/tty.d.ts" />
2
3
  /// <reference path="./modules/path.d.ts" />
3
4
  /// <reference path="./modules/http.d.ts" />
4
5
  /// <reference path="./modules/fs.d.ts" />
@@ -15,7 +16,7 @@
15
16
 
16
17
  // Web-standard globals. The runtime is QuickJS, not a browser or Node, so it
17
18
  // ships no lib.dom / @types/bun: these declarations are the sole source for
18
- // console, fetch, the Fetch types, timers, WebSocket, and the encoders.
19
+ // console, fetch, the Fetch types, timers, WebSocket, the encoders, and crypto.
19
20
  /// <reference path="./standards/console.d.ts" />
20
21
  /// <reference path="./standards/time.d.ts" />
21
22
  /// <reference path="./standards/text.d.ts" />
@@ -23,6 +24,7 @@
23
24
  /// <reference path="./standards/fetch.d.ts" />
24
25
  /// <reference path="./standards/websocket.d.ts" />
25
26
  /// <reference path="./standards/abort.d.ts" />
27
+ /// <reference path="./standards/crypto.d.ts" />
26
28
 
27
29
  // GUI capabilities (present only on a gui-enabled runtime). rendertree/camera/
28
30
  // microphone/gpu are flux:* modules like the rest; requestAnimationFrame stays a
@@ -34,6 +36,7 @@
34
36
  /// <reference path="./gui/microphone.d.ts" />
35
37
  /// <reference path="./gui/audio.d.ts" />
36
38
  /// <reference path="./gui/gpu.d.ts" />
39
+ /// <reference path="./gui/spatial.d.ts" />
37
40
  /// <reference path="./gui/video.d.ts" />
38
41
  /// <reference path="./gui/raf.d.ts" />
39
42
 
package/modules/fs.d.ts CHANGED
@@ -34,6 +34,24 @@ declare module "flux:fs" {
34
34
  write(data: string | Uint8Array): Promise<void>
35
35
  /** Append `data` to the end of the file, creating it if missing. */
36
36
  append(data: string | Uint8Array): Promise<void>
37
+ /** Remove the file. A missing file is not an error. */
38
+ remove(): Promise<void>
39
+ }
40
+
41
+ /**
42
+ * One change under a watched directory. `path` is the absolute path of the
43
+ * entry. `rename` names the path a file now has (the target of a rename:
44
+ * an editor's atomic save shows up as one); the old name of a rename
45
+ * arrives as `remove`.
46
+ */
47
+ type WatchEvent = {
48
+ kind: "create" | "modify" | "remove" | "rename"
49
+ path: string
50
+ }
51
+
52
+ type WatchOptions = {
53
+ /** Watch the whole tree below the directory too. Default false. */
54
+ recursive?: boolean
37
55
  }
38
56
 
39
57
  type FluxDir = {
@@ -47,6 +65,13 @@ declare module "flux:fs" {
47
65
  * already exists.
48
66
  */
49
67
  create(): Promise<void>
68
+ /**
69
+ * Watch the directory for changes, calling `callback` for each one.
70
+ * Events are raw and undebounced: one save usually arrives as several,
71
+ * so coalesce them yourself. The watch keeps the process alive until the
72
+ * returned function is called. Throws if the directory does not exist.
73
+ */
74
+ watch(callback: (event: WatchEvent) => void, options?: WatchOptions): () => void
50
75
  }
51
76
 
52
77
  /**
@@ -66,4 +91,12 @@ declare module "flux:fs" {
66
91
  * @param path Path to the directory.
67
92
  */
68
93
  export function dir(path: string): FluxDir
94
+ /**
95
+ * The canonical absolute path: symlinks resolved, `.`/`..` collapsed, the
96
+ * spelling the OS reports (no `\\?\` prefix on Windows). Rejects if the
97
+ * path does not exist.
98
+ *
99
+ * @param path Path to a file or directory.
100
+ */
101
+ export function realpath(path: string): Promise<string>
69
102
  }
package/modules/http.d.ts CHANGED
@@ -149,7 +149,7 @@ declare module "flux:http" {
149
149
  }
150
150
 
151
151
  type Server = {
152
- /** The bound port. */
152
+ /** The bound port (the OS-assigned one when `port` was 0 or omitted). */
153
153
  readonly port: number
154
154
  /** The bound host/interface. */
155
155
  readonly host: string
@@ -191,8 +191,8 @@ declare module "flux:http" {
191
191
  }
192
192
 
193
193
  type ServeOptions = {
194
- /** Port to listen on. */
195
- port: number
194
+ /** Port to listen on. 0 or omitted picks a free port; read it back from `server.port`. */
195
+ port?: number
196
196
  /** Hostname/interface to bind. Defaults to "0.0.0.0" (all interfaces). */
197
197
  host?: string
198
198
  /**
@@ -1,24 +1,47 @@
1
1
  declare module "flux:image" {
2
- /** Decoded pixels: tightly-packed RGBA8 plus the pixel dimensions. */
2
+ /**
3
+ * Decoded pixels: tightly-packed RGBA8 plus the pixel dimensions. Alpha is
4
+ * premultiplied unless the call that produced them said otherwise.
5
+ */
3
6
  export type DecodedImage = {
4
7
  data: Uint8Array
5
8
  width: number
6
9
  height: number
7
10
  }
8
11
 
12
+ /**
13
+ * Which alpha convention a pixel buffer follows. Image files store
14
+ * `"straight"` alpha; every texture and target on the GPU is
15
+ * `"premultiplied"` (color already multiplied by alpha), and so is what
16
+ * `readTexture` / `captureSnapshot` hand back.
17
+ */
18
+ export type AlphaMode = "premultiplied" | "straight"
19
+
9
20
  /**
10
21
  * Decodes encoded image bytes (png, jpeg, webp, gif, bmp, ico) into raw
11
22
  * RGBA8 pixels plus the decoded dimensions. Synchronous, pure CPU. Throws
12
23
  * when the bytes are not a decodable image.
24
+ *
25
+ * `alpha` selects what comes out: `"premultiplied"` (default) is ready for
26
+ * `createTexture` as-is; `"straight"` is the file's pixels verbatim, for CPU
27
+ * processing that wants color under transparent pixels preserved. Opaque
28
+ * pixels are identical either way.
13
29
  */
14
- export function decodeImage(bytes: Uint8Array): DecodedImage
30
+ export function decodeImage(bytes: Uint8Array, options?: { alpha?: AlphaMode }): DecodedImage
15
31
 
16
32
  /**
17
33
  * Encodes raw RGBA8 pixels into an image file, the reverse of `decodeImage`
18
- * (`encodeImage(decodeImage(bytes))` round-trips). `format` defaults to
19
- * `"png"` (lossless, keeps alpha); `"jpeg"` drops the alpha channel and
20
- * takes `quality` in 0..1 (default 0.9, ignored for png). Throws when
21
- * `data.length` does not match `width * height * 4`.
34
+ * (`encodeImage(decodeImage(bytes))` round-trips: exactly for opaque and
35
+ * fully transparent pixels, within rounding for translucent ones). `format`
36
+ * defaults to `"png"` (lossless, keeps alpha); `"jpeg"` drops the alpha
37
+ * channel and takes `quality` in 0..1 (default 0.9, ignored for png).
38
+ * `alpha` names what `img.data` holds: `"premultiplied"` (default, a decode
39
+ * or a readback) is converted to the straight alpha PNG stores;
40
+ * `"straight"` is written verbatim. Throws when `data.length` does not
41
+ * match `width * height * 4`.
22
42
  */
23
- export function encodeImage(img: DecodedImage, options?: { format?: "png" | "jpeg"; quality?: number }): Uint8Array
43
+ export function encodeImage(
44
+ img: DecodedImage,
45
+ options?: { format?: "png" | "jpeg"; quality?: number; alpha?: AlphaMode },
46
+ ): Uint8Array
24
47
  }
package/modules/net.d.ts CHANGED
@@ -22,6 +22,10 @@ declare module "flux:net" {
22
22
  /**
23
23
  * Outcome of a {@link probe}. `closed` (a refusal) still means the host is up —
24
24
  * something answered; only `filtered` (a timeout/unreachable) is no evidence.
25
+ *
26
+ * Windows reports a refusal only after ~2 s of SYN retries, so a `timeoutMs`
27
+ * under that reads a refused port as `filtered` there; `open` is instant
28
+ * everywhere.
25
29
  */
26
30
  type Liveness = "open" | "closed" | "filtered"
27
31
 
@@ -43,6 +47,8 @@ declare module "flux:net" {
43
47
  mac: string | null
44
48
  /** Whether the interface is up. */
45
49
  up: boolean
50
+ /** Whether it holds the default route (the interface other hosts reach). */
51
+ default: boolean
46
52
  /** Whether it is a loopback interface. */
47
53
  loopback: boolean
48
54
  /** Whether it supports multicast. */
@@ -6,6 +6,8 @@ declare module "flux:process" {
6
6
  * argument.
7
7
  */
8
8
  export let argv: string[]
9
+ /** The OS process id of this process (what a registry record or a `kill` names). */
10
+ export let pid: number
9
11
  /** The host OS: "darwin", "win32", "linux", "android", ... */
10
12
  export let platform: string
11
13
  /** The CPU architecture: "x64", "arm64", ... */
@@ -16,6 +18,40 @@ declare module "flux:process" {
16
18
  * provided for now.)
17
19
  */
18
20
  export function memoryUsage(): { rss: number }
21
+ /**
22
+ * The current user's home directory, or `null` when the environment does
23
+ * not name one (HOME on unix, USERPROFILE on Windows).
24
+ */
25
+ export function homedir(): string | null
26
+ /**
27
+ * The path of the running executable, or `null` when the OS cannot name it
28
+ * (Node's is always a string). What a dev tool spawns through
29
+ * `flux:subprocess` to start another instance of the runtime it runs in.
30
+ * In a packed app this is the app itself: spawning it launches that app,
31
+ * not a bare runtime.
32
+ */
33
+ export let execPath: string | null
34
+ /**
35
+ * Terminate another process. Portable (SIGKILL / TerminateProcess), so
36
+ * there is no signal argument, unlike Node's `process.kill(pid, signal)`.
37
+ *
38
+ * @param pid The OS process id.
39
+ * @returns `true` when the process was terminated; `false` when it does not
40
+ * exist or the OS refused.
41
+ */
42
+ export function kill(pid: number): boolean
43
+ /**
44
+ * Whether a process with `pid` exists. The `process.kill(pid, 0)` idiom
45
+ * under its own name. A zombie (exited, not yet reaped) counts as gone.
46
+ *
47
+ * @param pid The OS process id.
48
+ */
49
+ export function alive(pid: number): boolean
50
+ /**
51
+ * The process environment, snapshotted when the module is evaluated: a
52
+ * plain object, not Node's live and writable `process.env`.
53
+ */
54
+ export let env: Record<string, string | undefined>
19
55
  /**
20
56
  * Listen for an OS signal. The callback receives the signal name. Returns an
21
57
  * unsubscribe function. Unix only; a no-op elsewhere.
@@ -14,6 +14,15 @@ declare module "flux:subprocess" {
14
14
  * as UTF-8 strings.
15
15
  */
16
16
  encoding?: "buffer" | "utf8"
17
+ /**
18
+ * `spawn()` only: the child outlives this engine and this process. It is
19
+ * never killed on drop or reload, has no stdin/stdout/stderr pipes (all
20
+ * null: `stdout`/`stderr` iterate to nothing, `write` fails) and runs in
21
+ * its own process group, so a Ctrl+C to the parent does not reach it.
22
+ * `pid`, `kill()` and `status()` still work. Cannot combine with `stdin`.
23
+ * What a dev tool uses to launch another runtime instance.
24
+ */
25
+ detached?: boolean
17
26
  }
18
27
 
19
28
  /** The buffered result of a child run to completion via {@link Command.output}. */
@@ -0,0 +1,69 @@
1
+ declare module "flux:tty" {
2
+ /**
3
+ * Whether stdin is a terminal this process can use. False for a pipe, a
4
+ * file, or no stdin at all (a GUI launch), the cases where nobody is there
5
+ * to prompt; on unix also false for a job backgrounded from an interactive
6
+ * shell (`cmd &`), which still has the terminal as stdin but would be
7
+ * stopped by job control the moment it touched it.
8
+ */
9
+ export let isTTY: boolean
10
+ /** One key press in raw mode (see {@link setRawMode}). */
11
+ export interface Key {
12
+ /**
13
+ * Node's keypress names: "return", "backspace", "tab", "escape",
14
+ * "delete", "insert", "up", "down", "left", "right", "home", "end",
15
+ * "pageup", "pagedown", "space", "f1".."f12", or the lowercase letter
16
+ * or symbol typed.
17
+ */
18
+ name: string
19
+ /** The character typed, with its case, for a printable key; else undefined. */
20
+ char: string | undefined
21
+ ctrl: boolean
22
+ /** Alt (Option) held. */
23
+ meta: boolean
24
+ shift: boolean
25
+ }
26
+ /**
27
+ * Listen for input on stdin. `"line"` delivers one line per call as the
28
+ * terminal's own line discipline hands it over (cooked mode: the terminal
29
+ * does the editing), with the newline stripped; `"key"` delivers one key
30
+ * press per call while raw mode is on (and nothing arrives as a line
31
+ * then); `"close"` fires once when stdin reaches end of file (Ctrl-D in
32
+ * cooked mode, or the pipe closing). A listener holds the process alive
33
+ * until it unsubscribes; after `"close"` nothing can come, so every tty
34
+ * listener is dropped then, and a later `on` registers nothing. stdin is
35
+ * read once per process: a second engine in the same process (an isolate)
36
+ * gets no input.
37
+ *
38
+ * @param event `"line"`, `"key"` or `"close"`.
39
+ * @param callback Receives the line text or the {@link Key}; nothing for
40
+ * `"close"`.
41
+ * @returns An unsubscribe function.
42
+ */
43
+ export function on(event: "line", callback: (line: string) => void): () => void
44
+ export function on(event: "key", callback: (key: Key) => void): () => void
45
+ export function on(event: "close", callback: () => void): () => void
46
+ /**
47
+ * Like {@link on}, but the listener fires at most once and then unsubscribes.
48
+ */
49
+ export function once(event: "line", callback: (line: string) => void): () => void
50
+ export function once(event: "key", callback: (key: Key) => void): () => void
51
+ export function once(event: "close", callback: () => void): () => void
52
+ /**
53
+ * Switch the terminal's raw mode: no echo, no line editing, no signal keys
54
+ * (Ctrl-C arrives as a key), and stdin delivers `"key"` events instead of
55
+ * `"line"`s. The change applies from the next read: a line the terminal
56
+ * is already collecting is delivered as a line. Turn it off before
57
+ * exiting; the runtime also restores the terminal on exit and on a panic,
58
+ * but not on a kill. Throws when stdin is not a terminal.
59
+ *
60
+ * While raw, `console.log` output still breaks lines correctly (the
61
+ * runtime writes "\r\n"); your own {@link write} calls must use "\r\n".
62
+ */
63
+ export function setRawMode(on: boolean): void
64
+ /**
65
+ * Write `text` to stdout as is and flush: no newline appended, unlike
66
+ * `console.log`. What a prompt needs.
67
+ */
68
+ export function write(text: string): void
69
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidrt/flux-types",
3
- "version": "0.0.51",
3
+ "version": "0.0.53",
4
4
  "license": "MIT",
5
5
  "funding": "https://github.com/sponsors/wellawaretech",
6
6
  "author": "Antoine van Wel",
@@ -0,0 +1,18 @@
1
+ // crypto. The Web Crypto surface flux provides: `subtle.digest` only. No key
2
+ // material, no encryption, no random: an app hashes bytes (content
3
+ // addressing, integrity checks) and the rest of the standard waits for a need.
4
+
5
+ interface SubtleCrypto {
6
+ /**
7
+ * Hash `data` (a Uint8Array or ArrayBuffer) with `algorithm`, one of
8
+ * "SHA-256", "SHA-384", "SHA-512" (as a string or `{ name }`). Resolves to
9
+ * the digest as an ArrayBuffer. Other algorithms (SHA-1 included) reject.
10
+ */
11
+ digest(algorithm: string | { name: string }, data: Uint8Array | ArrayBuffer): Promise<ArrayBuffer>
12
+ }
13
+
14
+ interface Crypto {
15
+ readonly subtle: SubtleCrypto
16
+ }
17
+
18
+ declare var crypto: Crypto