@tur-ng/std 0.0.8 → 0.0.10

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.
Files changed (2) hide show
  1. package/package.json +4 -1
  2. package/src/index.d.ts +137 -34
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tur-ng/std",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "type": "module",
5
5
  "types": "src/index.d.ts",
6
6
  "files": [
@@ -14,5 +14,8 @@
14
14
  "type": "git",
15
15
  "url": "git+https://github.com/hpp2334/tur.git",
16
16
  "directory": "js/packages/tur-std"
17
+ },
18
+ "dependencies": {
19
+ "@tur-ng/core": "0.0.7"
17
20
  }
18
21
  }
package/src/index.d.ts CHANGED
@@ -18,13 +18,17 @@
18
18
  * substrate may import directly from `tur:core`.
19
19
  */
20
20
 
21
+ /// <reference types="@tur-ng/core" />
22
+
21
23
  declare module "tur:std" {
22
24
  // Re-export the reactive core (source/derive/mutate/get/set/view/mount,
23
25
  // Element/Source/Derived/Mutation/Readable/Val, ReadonlyStoreCtx/StoreCtx).
24
26
  export * from "tur:core";
25
27
 
26
- // Core meta-types used by the prop interfaces below.
27
- import type { Element, Mutation, Readable, Val } from "tur:core";
28
+ // Core meta-types used by the prop interfaces below. (`export *` alone
29
+ // re-exports but does not bind names locally every core type used in
30
+ // this module body must also appear here.)
31
+ import type { Derived, Element, Mutation, Readable, Val } from "tur:core";
28
32
 
29
33
  // ---------------------------------------------------------------------------
30
34
  // Value types — Color / LinearGradient / Brush / SpanData
@@ -584,43 +588,55 @@ declare module "tur:std" {
584
588
  }
585
589
 
586
590
  // ---------------------------------------------------------------------------
587
- // Async task primitives — `sleep` (a timer primitive) + `launch` (a
588
- // cancellable generator coroutine driver). These replace the old
589
- // `setTimeout` / `setInterval` globals.
591
+ // Async task primitives — the `Task<T>` handle, `sleep`, `CancelError`,
592
+ // and `isCancelError`. These replace the old `setTimeout` /
593
+ // `setInterval` globals (and the former `launch` generator driver):
594
+ // async composition is plain `async`/`await` + `.then`, cancellation is
595
+ // per-operation via the Task handle.
590
596
  // ---------------------------------------------------------------------------
591
597
 
592
- /** Resolve after `ms` milliseconds (engine time). The engine's frame loop
593
- * wakes precisely at the deadline. Use bare (`sleep(ms).then(...)`) or
594
- * inside a `launch` coroutine via `yield sleep(ms)`. */
595
- export function sleep(ms: number): Promise<void>;
596
-
597
- /** A cancellable coroutine task returned by `launch`. `cancel()` stops the
598
- * generator from resuming after its current `yield`. Any in-flight
599
- * `sleep` resolves harmlessly and is ignored. */
600
- export interface Task {
598
+ /** The handle every async engine API returns: `sleep`, `request`,
599
+ * `requestStream`, `clipboard.readText`/`writeText`,
600
+ * `filePicker.pick`/`saveFile`,
601
+ *
602
+ * - `promise` settles with the operation's result.
603
+ * - `cancel()` stops the operation where stoppable (a pending `sleep`
604
+ * timer is really cleared; an unpolled HTTP request is never sent;
605
+ * an in-flight one is discarded; a stream is wire-aborted) and
606
+ * **rejects `promise` with a `CancelError`**. Idempotent; a no-op
607
+ * for the promise once settled (op-specific abort still runs — e.g.
608
+ * cancelling a stream mid-consumption).
609
+ *
610
+ * Debounce idiom (the no-op rejection handler IS the cancelled
611
+ * branch):
612
+ * ```ts
613
+ * t?.cancel(); t = sleep(300);
614
+ * t.promise.then(show, () => {});
615
+ * ```
616
+ *
617
+ * Loop stop idiom: cancel the awaited sleep; the `await` throws
618
+ * `CancelError`; `catch (e) { if (isCancelError(e)) return; throw e; }`
619
+ * exits the loop. */
620
+ export interface Task<T> {
621
+ readonly promise: Promise<T>;
601
622
  cancel(): void;
602
623
  }
603
624
 
604
- /** Run a zero-arg generator function as a cancellable coroutine. The
605
- * generator must `yield` Promises (typically `sleep(ms)`); each resolved
606
- * promise resumes the generator, passing the resolved value back as the
607
- * `yield` result. Returns a `Task` whose `cancel()` halts further
608
- * resumption.
609
- *
610
- * Rejections: when a yielded promise rejects, the rejection reason is
611
- * thrown into the generator at the `yield` point so a `try/catch`
612
- * around `yield` catches it (the same ergonomics as `await`). An uncaught
613
- * rejection stops the coroutine. This makes `launch` safe to use with
614
- * fallible Promises (`clipboard.readText`, `http`, `fetch`), not just
615
- * `sleep`.
616
- *
617
- * Unlike `async`/`await`, generators can be externally stepped/abandoned,
618
- * which is what makes real cancellation possible. Use the debounce
619
- * pattern: `task?.cancel(); task = launch(function* () { yield sleep(ms);
620
- * ... });`. */
621
- export function launch<T>(
622
- gen: () => Generator<Promise<unknown>, T, unknown>,
623
- ): Task;
625
+ /** The rejection reason produced by `Task.cancel()` an `Error` whose
626
+ * `name` is `"CancelError"`. Test with `isCancelError` (or
627
+ * `e.name === "CancelError"`). */
628
+ export interface CancelError extends Error {
629
+ name: "CancelError";
630
+ }
631
+
632
+ /** `true` when `reason` is the rejection produced by `Task.cancel()`. */
633
+ export function isCancelError(reason: unknown): reason is CancelError;
634
+
635
+ /** Sleep for `ms` milliseconds (engine time) the engine's frame loop
636
+ * wakes precisely at the deadline. Returns a `Task<void>`: await
637
+ * `sleep(ms).promise`, and `cancel()` to clear the timer (the promise
638
+ * then rejects with a `CancelError`). */
639
+ export function sleep(ms: number): Task<void>;
624
640
 
625
641
  // ---------------------------------------------------------------------------
626
642
  // Element factories
@@ -801,4 +817,91 @@ declare module "tur:std" {
801
817
 
802
818
  /** Encode a string into a Uint8Array of UTF-8 bytes. */
803
819
  export function encodeUtf8(text: string): Uint8Array;
820
+
821
+ // ------------------------------------------------------------------
822
+ // Virtual apps — VirtualAppView hosts a complete nested engine
823
+ // instance (own worker, realm, store, tree) and draws the child's
824
+ // frames from its own paint. The controller is a lazy declaration:
825
+ // nothing runs until an element binds it (`app$` resolving to a
826
+ // controller); unbinding destroys the child unless `keepAlive`.
827
+ // ------------------------------------------------------------------
828
+
829
+ /**
830
+ * Opaque handle to a registered module source (`createModuleSource`).
831
+ * The source string never crosses the JS API again — only this handle
832
+ * does (the JS mirror of the Rust-side `ModuleSourceRegistry` /
833
+ * `load_module_source` flow).
834
+ */
835
+ export interface ModuleSourceHandle {
836
+ readonly __moduleSource: unique symbol;
837
+ }
838
+
839
+ /**
840
+ * Opaque handle to a worker pool registered on the runtime
841
+ * (`forWorkerPool`). The JS mirror of the Rust-side `WorkerPoolHandle`
842
+ * — resolved eagerly against the registry, so an unknown name throws
843
+ * at the call site.
844
+ */
845
+ export interface WorkerPoolHandle {
846
+ readonly __workerPool: unique symbol;
847
+ }
848
+
849
+ /** Lifecycle state of a hosted virtual app. */
850
+ export type VirtualAppStatus =
851
+ | "idle"
852
+ | "spawning"
853
+ | "running"
854
+ | "error"
855
+ | "destroyed";
856
+
857
+ /** A hosted virtual app's controller — a lazy declaration. */
858
+ export interface VirtualAppController {
859
+ /** Reactive status — read via `store.get(app.status$)`. */
860
+ readonly status$: Readable<VirtualAppStatus>;
861
+ /** Error detail (module load / start failures) — read via `store.get`. */
862
+ readonly errorMsg$: Readable<string>;
863
+ /**
864
+ * The ONLY lifecycle action — a control mutation (the `watch`
865
+ * `{ start$, stop$ }` convention: side effects ride the mutation
866
+ * rail). Dispatch via `store.set(app.destroy$)`. New code =
867
+ * destroy$ + a new controller with a new source.
868
+ */
869
+ readonly destroy$: Mutation;
870
+ }
871
+
872
+ /** Register a module source once; reference it by opaque handle. */
873
+ export function createModuleSource(source: string): ModuleSourceHandle;
874
+
875
+ /**
876
+ * Resolve a registered worker pool by name — eagerly (an unknown name
877
+ * throws right here). The only source of the handle
878
+ * `createVirtualAppController({ pool })` accepts.
879
+ */
880
+ export function forWorkerPool(name: string): WorkerPoolHandle;
881
+
882
+ /** Create a virtual-app controller (lazy — spawns on first bind). */
883
+ export function createVirtualAppController(opts: {
884
+ source: ModuleSourceHandle;
885
+ /**
886
+ * Target worker pool, from `forWorkerPool(name)` (handle-only — raw
887
+ * strings are rejected). Omit for the default `"virtual"` pool.
888
+ */
889
+ pool?: WorkerPoolHandle;
890
+ /** Survive element unbind (default `false`). */
891
+ keepAlive?: boolean;
892
+ }): VirtualAppController;
893
+
894
+ /** Element hosting a virtual app; draws the child's latest frame. */
895
+ export function VirtualAppView(props: {
896
+ /** Reactive controller binding — `null` unbinds (destroys). */
897
+ app$: Readable<VirtualAppController | null>;
898
+ background?: Val<Color>;
899
+ width?: Val<number>;
900
+ height?: Val<number>;
901
+ queryKey?: Array<string>;
902
+ /** Painted while the child isn't live. */
903
+ fallback?: Element;
904
+ /** Painted on error. */
905
+ errorView?: Element;
906
+ }): Element;
804
907
  }