@vielzeug/codex 26.10.2 → 26.10.3
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/data/catalog.json +49 -44
- package/data/llms-full.txt +148 -86
- package/data/llms.txt +1 -1
- package/data/manifest.json +1 -1
- package/data/packages/prism.json +51 -48
- package/data/packages/tandem.json +6 -4
- package/data/packages/tavern.json +15 -12
- package/data/search.json +14 -14
- package/package.json +2 -2
package/data/llms-full.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Vielzeug: Full Documentation
|
|
2
2
|
|
|
3
|
-
> Complete documentation for 41 packages. Version: 26.10.
|
|
3
|
+
> Complete documentation for 41 packages. Version: 26.10.2
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -16531,7 +16531,7 @@ chart.dispose();
|
|
|
16531
16531
|
- **Keyboard navigation**: arrow keys, `Enter`, and `Escape` on every labelled chart except sparklines
|
|
16532
16532
|
- **CSS custom properties**: full theme control via `--prism-*` tokens, with dark mode built in
|
|
16533
16533
|
- **Responsive**: auto-resizes via `ResizeObserver`
|
|
16534
|
-
-
|
|
16534
|
+
- **`ChartHandle.tap()`**: observe resize and dispose events outside the render path, with zero cost when untapped
|
|
16535
16535
|
- **`Symbol.dispose`**: explicit resource management following the TC39 proposal
|
|
16536
16536
|
|
|
16537
16537
|
## Documentation
|
|
@@ -16555,7 +16555,7 @@ chart.dispose();
|
|
|
16555
16555
|
| `createLineChart()` | Render a line chart | Sync | Line keys must be numbers or dates |
|
|
16556
16556
|
| `createAreaChart()` | Render an area chart | Sync | Line keys must be numbers or dates |
|
|
16557
16557
|
| `createBarChart()` | Render grouped or stacked bars | Sync | Stacked negative values are clamped to zero |
|
|
16558
|
-
| `createPieChart()` | Render pie, donut, or semi-circle slices | Sync |
|
|
16558
|
+
| `createPieChart()` | Render pie, donut, or semi-circle slices | Sync | `onHover` receives `null` when the pointer leaves |
|
|
16559
16559
|
| `createRadarChart()` | Compare values across 3+ axes, such as hero stats | Sync | Events report a whole axis, not one series |
|
|
16560
16560
|
| `createSparkline()` | Render an inline line, area, bar, or stack | Sync | Omitted `a11y` makes the chart decorative |
|
|
16561
16561
|
| `linearScale()` | Create a numeric scale | Sync | `nice` defaults to `true` |
|
|
@@ -16564,7 +16564,6 @@ chart.dispose();
|
|
|
16564
16564
|
| `setTheme()` | Set Prism CSS custom properties | Sync | Values apply to `document.documentElement` |
|
|
16565
16565
|
| `resetTheme()` | Remove Prism theme overrides | Sync | Removes only properties managed by `setTheme()` |
|
|
16566
16566
|
| `seriesColor()` | Resolve a series palette color | Sync | Palette indexes wrap after eight colors |
|
|
16567
|
-
| `debugChart()` | Log chart lifecycle events | Sync | Import from `@vielzeug/prism/devtools` |
|
|
16568
16567
|
|
|
16569
16568
|
## Package Entry Point
|
|
16570
16569
|
|
|
@@ -16572,7 +16571,6 @@ chart.dispose();
|
|
|
16572
16571
|
| --- | --- |
|
|
16573
16572
|
| `@vielzeug/prism` | Chart factories, scales, theme helpers, errors, and public types |
|
|
16574
16573
|
| `@vielzeug/prism/theme` | Default CSS custom properties and dark-mode values |
|
|
16575
|
-
| `@vielzeug/prism/devtools` | Optional `debugChart()` lifecycle logging |
|
|
16576
16574
|
|
|
16577
16575
|
## Chart Factories
|
|
16578
16576
|
|
|
@@ -16876,11 +16874,16 @@ interface ChartHandle {
|
|
|
16876
16874
|
readonly disposalSignal: AbortSignal;
|
|
16877
16875
|
readonly disposed: boolean;
|
|
16878
16876
|
readonly el: SVGSVGElement;
|
|
16877
|
+
tap(handler: (event: PrismEvent) => void, options?: { readonly signal?: AbortSignal }): () => void;
|
|
16879
16878
|
update(data: TData): void;
|
|
16880
16879
|
dispose(): void;
|
|
16881
16880
|
[Symbol.dispose](): void;
|
|
16882
16881
|
}
|
|
16883
16882
|
|
|
16883
|
+
type PrismEvent =
|
|
16884
|
+
| { readonly height: number; readonly type: 'resize'; readonly width: number }
|
|
16885
|
+
| { readonly type: 'dispose' };
|
|
16886
|
+
|
|
16884
16887
|
interface ChartMargin {
|
|
16885
16888
|
top: number;
|
|
16886
16889
|
right: number;
|
|
@@ -16901,6 +16904,8 @@ type ChartA11y =
|
|
|
16901
16904
|
|
|
16902
16905
|
`update()` renders synchronously and throws `PrismRenderError` after disposal. Omitting `a11y` makes the SVG decorative with `aria-hidden="true"`.
|
|
16903
16906
|
|
|
16907
|
+
`tap()` observes runtime behavior outside the render path: it receives `resize` and `dispose` events (see `PrismEvent`). It returns an unsubscribe function; pass `options.signal` to detach automatically. Handler errors are swallowed, and tapping a disposed handle returns a no-op unsubscribe.
|
|
16908
|
+
|
|
16904
16909
|
---
|
|
16905
16910
|
|
|
16906
16911
|
### Data types
|
|
@@ -16995,8 +17000,8 @@ interface PieChartConfig
|
|
|
16995
17000
|
innerRadius?: number;
|
|
16996
17001
|
cornerRadius?: number;
|
|
16997
17002
|
padPixels?: number;
|
|
16998
|
-
onClick?: (
|
|
16999
|
-
onHover?: (
|
|
17003
|
+
onClick?: (event: PieEvent) => void;
|
|
17004
|
+
onHover?: (event: PieEvent | null) => void;
|
|
17000
17005
|
}
|
|
17001
17006
|
|
|
17002
17007
|
interface RadarChartConfig
|
|
@@ -17022,8 +17027,8 @@ interface SparklineConfig {
|
|
|
17022
17027
|
cornerRadius?: number;
|
|
17023
17028
|
curve?: 'linear' | 'monotone' | 'step';
|
|
17024
17029
|
fillOpacity?: number; // overrides --prism-spark-fill-opacity (area variant)
|
|
17025
|
-
onClick?: (
|
|
17026
|
-
onHover?: (
|
|
17030
|
+
onClick?: (event: SparklineEvent) => void;
|
|
17031
|
+
onHover?: (event: SparklineEvent | null) => void;
|
|
17027
17032
|
padPixels?: number;
|
|
17028
17033
|
showEndPoint?: boolean; // default true: dot on the latest value (line, area)
|
|
17029
17034
|
strokeWidth?: number;
|
|
@@ -17061,6 +17066,12 @@ interface PieSliceConfig {
|
|
|
17061
17066
|
label?: string;
|
|
17062
17067
|
}
|
|
17063
17068
|
|
|
17069
|
+
interface PieEvent {
|
|
17070
|
+
index: number;
|
|
17071
|
+
originalEvent: Event;
|
|
17072
|
+
slice: PieSliceConfig;
|
|
17073
|
+
}
|
|
17074
|
+
|
|
17064
17075
|
interface RadarAxisConfig {
|
|
17065
17076
|
key: string;
|
|
17066
17077
|
label: string;
|
|
@@ -17095,6 +17106,12 @@ interface StackSegment {
|
|
|
17095
17106
|
color?: string;
|
|
17096
17107
|
label?: string;
|
|
17097
17108
|
}
|
|
17109
|
+
|
|
17110
|
+
interface SparklineEvent {
|
|
17111
|
+
index: number;
|
|
17112
|
+
originalEvent: Event;
|
|
17113
|
+
value: number;
|
|
17114
|
+
}
|
|
17098
17115
|
```
|
|
17099
17116
|
|
|
17100
17117
|
A custom radar `tooltip.render` receives `datum.meta` with `axis`, `values`, and the default `text`.
|
|
@@ -17213,20 +17230,6 @@ interface PrismTheme {
|
|
|
17213
17230
|
}
|
|
17214
17231
|
```
|
|
17215
17232
|
|
|
17216
|
-
## Devtools
|
|
17217
|
-
|
|
17218
|
-
### `debugChart()`
|
|
17219
|
-
|
|
17220
|
-
```ts
|
|
17221
|
-
function debugChart(handle: T, options?: DebugChartOptions): T;
|
|
17222
|
-
|
|
17223
|
-
interface DebugChartOptions {
|
|
17224
|
-
label?: string; // log prefix; defaults to 'chart', producing [prism:chart]
|
|
17225
|
-
}
|
|
17226
|
-
```
|
|
17227
|
-
|
|
17228
|
-
Logs mount, resize, and disposal events to `console.debug` and returns the same handle. Import it and `DebugChartOptions` from `@vielzeug/prism/devtools`.
|
|
17229
|
-
|
|
17230
17233
|
## Errors
|
|
17231
17234
|
|
|
17232
17235
|
### `PrismError`
|
|
@@ -17610,16 +17613,18 @@ chart.update([
|
|
|
17610
17613
|
```ts
|
|
17611
17614
|
createPieChart(container, {
|
|
17612
17615
|
data,
|
|
17613
|
-
onHover: (
|
|
17614
|
-
//
|
|
17615
|
-
if (
|
|
17616
|
+
onHover: (event) => {
|
|
17617
|
+
// event is PieEvent | null (null on mouseleave)
|
|
17618
|
+
if (event) console.log(event.index, event.slice.label, event.slice.value);
|
|
17616
17619
|
},
|
|
17617
|
-
onClick: (
|
|
17618
|
-
console.log('clicked', slice.label);
|
|
17620
|
+
onClick: (event) => {
|
|
17621
|
+
console.log('clicked', event.index, event.slice.label);
|
|
17619
17622
|
},
|
|
17620
17623
|
});
|
|
17621
17624
|
```
|
|
17622
17625
|
|
|
17626
|
+
`PieEvent` provides `index`, the hovered/clicked `slice` (`PieSliceConfig`), and the `originalEvent` (`MouseEvent` or `KeyboardEvent`).
|
|
17627
|
+
|
|
17623
17628
|
## Radar Charts
|
|
17624
17629
|
|
|
17625
17630
|
`createRadarChart` compares several values on one shape, such as a hero's stats. Each series supplies values keyed by `axis.key`:
|
|
@@ -17725,21 +17730,23 @@ spark.update([12, 18, 14, 22, 30]);
|
|
|
17725
17730
|
|
|
17726
17731
|
### Event Hooks
|
|
17727
17732
|
|
|
17728
|
-
Sparklines use simplified hooks:
|
|
17733
|
+
Sparklines use simplified hooks: `SparklineEvent` rather than full `ChartEvent`. With a hook set, a marker follows the hovered value:
|
|
17729
17734
|
|
|
17730
17735
|
```ts
|
|
17731
17736
|
const spark = createSparkline(container, {
|
|
17732
17737
|
data: [10, 20, 30],
|
|
17733
|
-
onHover: (
|
|
17734
|
-
//
|
|
17735
|
-
if (
|
|
17738
|
+
onHover: (event) => {
|
|
17739
|
+
// event is SparklineEvent | null (null on mouseleave)
|
|
17740
|
+
if (event) console.log(`Hovering point ${event.index}: ${event.value}`);
|
|
17736
17741
|
},
|
|
17737
|
-
onClick: (
|
|
17738
|
-
console.log(`Clicked point ${index}: ${value}`);
|
|
17742
|
+
onClick: (event) => {
|
|
17743
|
+
console.log(`Clicked point ${event.index}: ${event.value}`);
|
|
17739
17744
|
},
|
|
17740
17745
|
});
|
|
17741
17746
|
```
|
|
17742
17747
|
|
|
17748
|
+
`SparklineEvent` provides `index`, the hovered/clicked `value`, and the `originalEvent`.
|
|
17749
|
+
|
|
17743
17750
|
> **Note:** Sparklines have no keyboard navigation. Label one with `a11y.ariaLabel` only when the trend carries meaning, and state the key value in surrounding text.
|
|
17744
17751
|
|
|
17745
17752
|
## Axes and Grid
|
|
@@ -17842,7 +17849,7 @@ const chart = createLineChart(container, {
|
|
|
17842
17849
|
- `values`: every series at the same key (line, area, and bar charts)
|
|
17843
17850
|
- `originalEvent`: the raw `MouseEvent` or, for keyboard navigation, `KeyboardEvent`
|
|
17844
17851
|
|
|
17845
|
-
> **Pie and
|
|
17852
|
+
> **Pie, radar, and sparkline events differ**: pie hooks receive a `PieEvent` (`index`, `slice`, `originalEvent`), sparkline hooks a `SparklineEvent` (`index`, `value`, `originalEvent`), and radar hooks a `RadarEvent` describing an axis. See [`PieChartConfig`](./api.md#chart-configurations) and [`createRadarChart()`](./api.md#createradarchart).
|
|
17846
17853
|
|
|
17847
17854
|
## Animations
|
|
17848
17855
|
|
|
@@ -18017,22 +18024,27 @@ Call `update()` only while the handle is active. Updating a disposed chart throw
|
|
|
18017
18024
|
|
|
18018
18025
|
Charts resize automatically when the container dimensions change. Prism uses `ResizeObserver` internally: no manual `resize()` call is needed.
|
|
18019
18026
|
|
|
18020
|
-
##
|
|
18027
|
+
## Runtime Observation
|
|
18021
18028
|
|
|
18022
|
-
|
|
18029
|
+
Every handle exposes `tap()` to observe runtime behavior — resize and dispose — outside the render path. It reuses the chart's own `ResizeObserver`, so observing costs nothing extra, and handler errors are swallowed: observation never affects chart behavior.
|
|
18023
18030
|
|
|
18024
18031
|
```ts
|
|
18025
18032
|
import { createLineChart } from '@vielzeug/prism';
|
|
18026
|
-
import { debugChart } from '@vielzeug/prism/devtools';
|
|
18027
18033
|
|
|
18028
|
-
const chart =
|
|
18029
|
-
|
|
18030
|
-
|
|
18031
|
-
|
|
18032
|
-
|
|
18034
|
+
const chart = createLineChart(container, {
|
|
18035
|
+
series: [{ data: [{ key: 1, value: 10 }], name: 'Revenue' }],
|
|
18036
|
+
});
|
|
18037
|
+
const controller = new AbortController();
|
|
18038
|
+
const unsubscribe = chart.tap(
|
|
18039
|
+
(event) => {
|
|
18040
|
+
if (event.type === 'resize') console.log('resized', event.width, event.height);
|
|
18041
|
+
if (event.type === 'dispose') console.log('disposed');
|
|
18042
|
+
},
|
|
18043
|
+
{ signal: controller.signal },
|
|
18044
|
+
);
|
|
18033
18045
|
```
|
|
18034
18046
|
|
|
18035
|
-
|
|
18047
|
+
`tap()` returns an unsubscribe function; pass `options.signal` to detach automatically instead. The `resize` event carries the new `width`/`height` and fires after the chart re-laid-out; `dispose` fires once, before teardown completes. Tapping a disposed handle returns a no-op unsubscribe. This is separate from prism's internal validation warnings, which run automatically in development and are never part of the public API.
|
|
18036
18048
|
|
|
18037
18049
|
## Accessibility
|
|
18038
18050
|
|
|
@@ -18145,7 +18157,7 @@ void source.reload().catch(() => undefined);
|
|
|
18145
18157
|
- Call `chart.dispose()` in your framework's unmount/cleanup phase to cancel transitions, disconnect resize observation, and remove DOM nodes.
|
|
18146
18158
|
- Call `chart.update(data)` from your application state boundary; Prism does not require or own a state library.
|
|
18147
18159
|
- Set `a11y: { ariaLabel: '…' }` on every chart that conveys meaningful data: unlabelled charts are hidden from assistive technology and receive no keyboard navigation.
|
|
18148
|
-
-
|
|
18160
|
+
- Observe lifecycle with `chart.tap(handler)` rather than polling `chart.disposed` or wrapping the container: it costs nothing when untapped and detaches via `signal`.
|
|
18149
18161
|
- For SSR, skip chart creation server-side: Prism depends on DOM APIs and `ResizeObserver`. Render charts only after hydration in a `onMounted`/`useEffect` callback.
|
|
18150
18162
|
|
|
18151
18163
|
### Examples
|
|
@@ -28810,7 +28822,7 @@ sync.dispose();
|
|
|
28810
28822
|
- **`flush()`**: a full cycle on demand: pull remote changes, then push dirty records; rejects on failure.
|
|
28811
28823
|
- **Hide-flows**: flushes with `keepalive` on `pagehide`/`hidden`, runs a full cycle on return to the foreground.
|
|
28812
28824
|
- **`tap()`**: typed `TandemEvent`s for pushes, pulls, invalid records, and warnings.
|
|
28813
|
-
- **Lifecycle-owned**: `dispose()`, `disposed`, `disposalSignal`, and `[Symbol.dispose]`.
|
|
28825
|
+
- **Lifecycle-owned**: `dispose()`, `disposed`, `disposalSignal`, and `[Symbol.dispose]`; post-dispose `changed()`/`flush()` fail with `TandemDisposedError`.
|
|
28814
28826
|
|
|
28815
28827
|
## Documentation
|
|
28816
28828
|
|
|
@@ -28837,7 +28849,7 @@ sync.dispose();
|
|
|
28837
28849
|
|
|
28838
28850
|
| Import | Purpose |
|
|
28839
28851
|
| --- | --- |
|
|
28840
|
-
| `@vielzeug/tandem` | `createSync`, port/gateway contracts, events, and types |
|
|
28852
|
+
| `@vielzeug/tandem` | `createSync`, `TandemError`, `TandemDisposedError`, port/gateway contracts, events, and types |
|
|
28841
28853
|
|
|
28842
28854
|
## Factories
|
|
28843
28855
|
|
|
@@ -28874,11 +28886,11 @@ sync.dispose();
|
|
|
28874
28886
|
|
|
28875
28887
|
| Member | Description |
|
|
28876
28888
|
| --- | --- |
|
|
28877
|
-
| `changed()` | Notes a local change and (re)starts the idle flush timer |
|
|
28889
|
+
| `changed()` | Notes a local change and (re)starts the idle flush timer; throws `TandemDisposedError` after `dispose` |
|
|
28878
28890
|
| `dispose()` | Stops the scheduler, clears the timer, removes document/window listeners |
|
|
28879
28891
|
| `disposed` | `true` once disposed |
|
|
28880
28892
|
| `disposalSignal` | `AbortSignal` aborted on dispose |
|
|
28881
|
-
| `flush()` | Runs a full cycle: pull, then push; resolves when it completes, rejects when it fails, and
|
|
28893
|
+
| `flush()` | Runs a full cycle: pull, then push; resolves when it completes, rejects when it fails, and rejects with `TandemDisposedError` after `dispose` |
|
|
28882
28894
|
| `tap(handler, options?)` | Observes `TandemEvent`s; returns an unsubscribe function |
|
|
28883
28895
|
| `[Symbol.dispose]()` | Calls `dispose()` for `using` scopes |
|
|
28884
28896
|
|
|
@@ -28894,7 +28906,7 @@ The storage seam. The engine owns every rev comparison: it calls your gateway wi
|
|
|
28894
28906
|
|
|
28895
28907
|
| Member | Contract |
|
|
28896
28908
|
| --- | --- |
|
|
28897
|
-
| `records()` | This device's own records, remote mirrors excluded |
|
|
28909
|
+
| `records()` | This device's own records, remote mirrors excluded; sync or async, so a disk-backed gateway can read from storage instead of holding a mirror |
|
|
28898
28910
|
| `pendingDeletions()` | Tombstones the server has not acknowledged |
|
|
28899
28911
|
| `applyRecords(records)` | Upsert the given records: already filtered to what the server is ahead on; return the ids that failed validation |
|
|
28900
28912
|
| `applyDeletions(deletions)` | Remove records deleted remotely |
|
|
@@ -28911,11 +28923,11 @@ type TandemEvent =
|
|
|
28911
28923
|
| { readonly records: number; readonly type: 'pull' }
|
|
28912
28924
|
| { readonly records: number; readonly type: 'push' }
|
|
28913
28925
|
| { readonly message: string; readonly skipped: readonly string[]; readonly type: 'invalid' }
|
|
28914
|
-
| { readonly message: string; readonly type: 'warning' }
|
|
28926
|
+
| { readonly error: unknown; readonly message: string; readonly type: 'warning' }
|
|
28915
28927
|
| { readonly type: 'dispose' };
|
|
28916
28928
|
```
|
|
28917
28929
|
|
|
28918
|
-
`invalid` reports record ids `applyRecords` refused; `warning` reports a rejected push or transport failure
|
|
28930
|
+
`invalid` reports record ids `applyRecords` refused; `warning` reports a rejected push or transport failure and carries the original cause in `error` beside its `message`; `pull` and `push` report the record counts applied and pushed. `dispose` fires once, before tappers are cleared.
|
|
28919
28931
|
|
|
28920
28932
|
## Types
|
|
28921
28933
|
|
|
@@ -28945,13 +28957,13 @@ A record paired with the entity (table or kind) it belongs to.
|
|
|
28945
28957
|
|
|
28946
28958
|
```ts
|
|
28947
28959
|
interface SyncDeletion {
|
|
28948
|
-
readonly deletedAt
|
|
28960
|
+
readonly deletedAt?: string;
|
|
28949
28961
|
readonly entity: string;
|
|
28950
28962
|
readonly id: string;
|
|
28951
28963
|
}
|
|
28952
28964
|
```
|
|
28953
28965
|
|
|
28954
|
-
A tombstone: a deletion that travels to the server on the next push.
|
|
28966
|
+
A tombstone: a deletion that travels to the server on the next push. `deletedAt` is set by whoever recorded the deletion and the engine never reads it: include it when your server wants the timestamp, omit it otherwise.
|
|
28955
28967
|
|
|
28956
28968
|
### `SyncState`
|
|
28957
28969
|
|
|
@@ -28986,7 +28998,23 @@ interface SyncOptions {
|
|
|
28986
28998
|
|
|
28987
28999
|
## Errors
|
|
28988
29000
|
|
|
28989
|
-
|
|
29001
|
+
### `TandemError`
|
|
29002
|
+
|
|
29003
|
+
```ts
|
|
29004
|
+
class TandemError extends Error {}
|
|
29005
|
+
```
|
|
29006
|
+
|
|
29007
|
+
Base class for every error Tandem throws; `instanceof TandemError` catches them all in one branch.
|
|
29008
|
+
|
|
29009
|
+
### `TandemDisposedError`
|
|
29010
|
+
|
|
29011
|
+
```ts
|
|
29012
|
+
class TandemDisposedError extends TandemError {}
|
|
29013
|
+
```
|
|
29014
|
+
|
|
29015
|
+
Thrown by `changed()` and rejected by `flush()` after `dispose()`. A scheduler that stops silently is a sync engine's worst failure: the device keeps editing while nothing uploads, so a leaked handle fails loudly instead.
|
|
29016
|
+
|
|
29017
|
+
Beyond disposal, Tandem defines no error classes. A rejected `push` is expected input, not a failure: the engine pulls to reconcile and reports it as a `warning` event. `flush()` additionally rejects with the underlying port or gateway error; dirty records survive the failure and retry on the next cycle.
|
|
28990
29018
|
|
|
28991
29019
|
### Usage Guide
|
|
28992
29020
|
|
|
@@ -29084,7 +29112,7 @@ try {
|
|
|
29084
29112
|
}
|
|
29085
29113
|
```
|
|
29086
29114
|
|
|
29087
|
-
`flush()` pulls remote changes and then pushes dirty records, so it is also the wiring point for a returning network connection. It rejects when either half fails; the records stay dirty and the next cycle retries them.
|
|
29115
|
+
`flush()` pulls remote changes and then pushes dirty records, so it is also the wiring point for a returning network connection. It rejects when either half fails; the records stay dirty and the next cycle retries them. After `dispose()`, `flush()` rejects with `TandemDisposedError` and `changed()` throws it: a leaked scheduler fails loudly instead of silently stopping. If your reactive wiring can fire after disposal, unsubscribe from your store before disposing the handle, or guard with `sync.disposed`.
|
|
29088
29116
|
|
|
29089
29117
|
Tandem also flushes on its own: it pushes with `keepalive: true` when the tab hides or closes, and runs a full cycle when the tab returns to the foreground. Serve the keepalive push with `navigator.sendBeacon` or a `keepalive` fetch so it survives the tab closing.
|
|
29090
29118
|
|
|
@@ -29114,7 +29142,7 @@ const unsubscribe = sync.tap((event) => {
|
|
|
29114
29142
|
console.warn(event.message, event.skipped);
|
|
29115
29143
|
break;
|
|
29116
29144
|
case 'warning':
|
|
29117
|
-
console.warn(event.message); // push rejected, network failure, etc.
|
|
29145
|
+
console.warn(event.message, event.error); // push rejected, network failure, etc.
|
|
29118
29146
|
break;
|
|
29119
29147
|
}
|
|
29120
29148
|
});
|
|
@@ -29190,7 +29218,7 @@ useEffect(() => {
|
|
|
29190
29218
|
|
|
29191
29219
|
## Working with Other Vielzeug Libraries
|
|
29192
29220
|
|
|
29193
|
-
- **Vault**: the usual `gateway` backend. Its IndexedDB adapter stores records and the sync-state row; `records()` reads
|
|
29221
|
+
- **Vault**: the usual `gateway` backend. Its IndexedDB adapter stores records and the sync-state row; `records()` reads them from storage (it may be async), `applyRecords()` writes through the codec.
|
|
29194
29222
|
- **Postmaster**: complementary, not overlapping. Postmaster guarantees at-least-once delivery of discrete mutations; Tandem keeps whole-record state in step. Apps that need both run them side by side.
|
|
29195
29223
|
- **Sentinel**: subscribe to `createNetwork()` and call `sync.flush()` when the connection returns; the cycle pulls and pushes, so remote changes land without waiting for the next foreground return.
|
|
29196
29224
|
- **Ripple**: `store.subscribe(() => sync.changed())` is the idiomatic wiring for a signal-backed store.
|
|
@@ -29350,13 +29378,16 @@ guest.dispose();
|
|
|
29350
29378
|
| --- | --- | --- | --- |
|
|
29351
29379
|
| `hostTavern()` | Host a subject: pairing, command validation, snapshot broadcast | Sync factory | Disposing ends hosting for every guest |
|
|
29352
29380
|
| `joinTavern()` | Join a session: consume invitation, produce answer | Async | The session lives only after the host accepts and the first snapshot mounts |
|
|
29353
|
-
| `TavernHost` | Host handle: invitation, answer acceptance, kick, notice relay | N/A | `dispose()` is idempotent |
|
|
29354
|
-
| `TavernGuest` | Guest handle: command forwarding, disposal | N/A | `dispose()` fires `onEnded` exactly once |
|
|
29381
|
+
| `TavernHost` | Host handle: invitation, answer acceptance, kick, notice relay, presence tap | N/A | `dispose()` is idempotent; control-plane methods throw `TavernDisposedError` after |
|
|
29382
|
+
| `TavernGuest` | Guest handle: command forwarding, rejection tap, disposal | N/A | `dispose()` fires `onEnded` exactly once |
|
|
29383
|
+
| `TavernHostEvent` | Host observability union for `tap()`: presence and warnings | N/A | Lifecycle transitions are callbacks, not events |
|
|
29384
|
+
| `TavernGuestEvent` | Guest observability union for `tap()`: rejections and warnings | N/A | `onJoined`/`onFailed` fire before the handle exists, so they stay callbacks |
|
|
29355
29385
|
| `TavernCommands` | The host's command table | N/A | Unknown names and foreign subject ids reject without applying |
|
|
29356
29386
|
| `TavernSubjects` | Snapshot reading, change and removal subscriptions | N/A | `snapshot` returning `null` skips the broadcast |
|
|
29357
29387
|
| `TavernNotices` | Wire serialization of local notices | N/A | `toWire` returning `null` skips that notice |
|
|
29358
29388
|
| `TavernError` | Base error for every Tavern failure | N/A | N/A |
|
|
29359
29389
|
| `TavernPairingError` | The consumer pasted the wrong kind of pairing code | N/A | N/A |
|
|
29390
|
+
| `TavernDisposedError` | A stopped host or ended guest was used again | N/A | N/A |
|
|
29360
29391
|
|
|
29361
29392
|
## Package Entry Point
|
|
29362
29393
|
|
|
@@ -29366,7 +29397,7 @@ guest.dispose();
|
|
|
29366
29397
|
|
|
29367
29398
|
## `hostTavern(options)`
|
|
29368
29399
|
|
|
29369
|
-
Creates a mesh host for one subject. Guest commands are validated against the wire shape (Tavern's own protocol), checked against `commands.has` and the `subjectId`, then applied through `commands.apply`: throwing rejects the guest with the error's message. Local changes (via `subjects.onChanged`) re-broadcast `subjects.snapshot()`: coalesced on a microtask; `subjects.onRemoved` ends hosting.
|
|
29400
|
+
Creates a mesh host for one subject. Guest commands are validated against the wire shape (Tavern's own protocol), checked against `commands.has` and the `subjectId`, then applied through `commands.apply`: throwing rejects the guest with the error's message. Local changes (via `subjects.onChanged`) re-broadcast `subjects.snapshot()`: coalesced on a microtask; `subjects.onRemoved` ends hosting. Presence and transport warnings are reported through `tap()` as `TavernHostEvent`s, and the live peer list is the `peers` getter; hosting ending: from removal or `dispose()`: fires `onEnded` exactly once. `relayNotice` serializes through `notices.toWire` and broadcasts to every guest.
|
|
29370
29401
|
|
|
29371
29402
|
**Returns:** `TavernHost`: the host handle.
|
|
29372
29403
|
|
|
@@ -29375,10 +29406,6 @@ Creates a mesh host for one subject. Guest commands are validated against the wi
|
|
|
29375
29406
|
| `commands` | `TavernCommands` | The host's command table: the same object the host's own UI calls |
|
|
29376
29407
|
| `notices?` | `TavernNotices` | Notice relay; omit to disable notice broadcasting |
|
|
29377
29408
|
| `onEnded?` | `() => void` | Hosting ended: the subject was removed or `dispose()` ran. Fires exactly once |
|
|
29378
|
-
| `onPeersChanged?` | `(peers: MeshPeer[]) => void` | The full peer list, whenever it changes |
|
|
29379
|
-
| `onPeerJoined?` | `(peer: MeshPeer) => void` | A peer joined |
|
|
29380
|
-
| `onPeerLeft?` | `(peer: MeshPeer) => void` | A peer left |
|
|
29381
|
-
| `onWarning?` | `(message: string) => void` | A transport-level warning |
|
|
29382
29409
|
| `rtc?` | `MeshRtcFactory` | WebRTC factory: the injection point for tests and non-browser runtimes |
|
|
29383
29410
|
| `subjectId` | `string` | The id of the hosted subject: guest commands targeting anything else reject |
|
|
29384
29411
|
| `subjects` | `TavernSubjects` | Snapshot reading, change and removal subscriptions |
|
|
@@ -29406,20 +29433,22 @@ const host = hostTavern({
|
|
|
29406
29433
|
|
|
29407
29434
|
| Member | Returns | Description |
|
|
29408
29435
|
| --- | --- | --- |
|
|
29409
|
-
| `acceptAnswerText(text)` | `Promise` | Consumes a guest's answer code; resolves with the peer once the channel opens. Throws `TavernPairingError` for any unusable code: wrong kind, malformed, expired: with the underlying error as `cause` |
|
|
29410
|
-
| `createInvitationText()` | `Promise` | Produces a single-use invitation code, QR-compact when the environment allows |
|
|
29436
|
+
| `acceptAnswerText(text)` | `Promise` | Consumes a guest's answer code; resolves with the peer once the channel opens. Throws `TavernPairingError` for any unusable code: wrong kind, malformed, expired: with the underlying error as `cause`. Throws `TavernDisposedError` after `dispose()` |
|
|
29437
|
+
| `createInvitationText()` | `Promise` | Produces a single-use invitation code, QR-compact when the environment allows. Throws `TavernDisposedError` after `dispose()` |
|
|
29411
29438
|
| `disposalSignal` | `AbortSignal` | Aborted when hosting ends |
|
|
29412
29439
|
| `dispose()` | `void` | Stops hosting: closes every channel and detaches all subscriptions. Idempotent; fires `onEnded` exactly once |
|
|
29413
29440
|
| `disposed` | `boolean` | Whether hosting has ended |
|
|
29414
|
-
| `kick(peerId)` | `void` | Disconnects a peer; the guest observes a disconnect |
|
|
29415
|
-
| `
|
|
29441
|
+
| `kick(peerId)` | `void` | Disconnects a peer; the guest observes a disconnect. Throws `TavernDisposedError` after `dispose()` |
|
|
29442
|
+
| `peers` | `readonly MeshPeer[]` | The guests currently connected; current inside a `peer-joined`/`peer-left` tap event |
|
|
29443
|
+
| `relayNotice(notice)` | `void` | Relays one local notice to every guest; the serializer decides what crosses. A notice relayed after `dispose()` is dropped: ephemeral UI state with no audience left |
|
|
29444
|
+
| `tap(handler, options?)` | `() => void` | Observes `TavernHostEvent`s; returns an unsubscribe function |
|
|
29416
29445
|
| `[Symbol.dispose]()` | `void` | Delegates to `dispose()`. Enables `using` declarations |
|
|
29417
29446
|
|
|
29418
29447
|
---
|
|
29419
29448
|
|
|
29420
29449
|
## `joinTavern(options)`
|
|
29421
29450
|
|
|
29422
|
-
Consumes an invitation and returns `{ answerText, guest }`. The guest mounts snapshots through `mount(snapshot)`: the first successful mount fires `onJoined`; wire notices are relayed through `notices.fromWire`; rejections through `
|
|
29451
|
+
Consumes an invitation and returns `{ answerText, guest }`. The guest mounts snapshots through `mount(snapshot)`: the first successful mount fires `onJoined`; wire notices are relayed through `notices.fromWire`; host rejections and transport warnings arrive through `tap()` as `TavernGuestEvent`s. Channel failures before the first mount fire `onFailed` (once); afterwards, a disconnect or `guest.dispose()` fires `onEnded` with the mounted subject exactly once. `guest.sendCommand` forwards a command to the host with the subject id the consumer routes by. An unusable invitation: wrong kind, malformed, expired: rejects with `TavernPairingError` and leaves no node or subscriptions behind.
|
|
29423
29452
|
|
|
29424
29453
|
**Returns:** `Promise`: the answer code to show back and the guest handle.
|
|
29425
29454
|
|
|
@@ -29432,8 +29461,6 @@ Consumes an invitation and returns `{ answerText, guest }`. The guest mounts sna
|
|
|
29432
29461
|
| `onEnded?` | `(subject: Mounted) => void` | The channel ended after joining; the mounted value is passed for unmounting |
|
|
29433
29462
|
| `onFailed?` | `(reason: string) => void` | The host never accepted the answer in time |
|
|
29434
29463
|
| `onJoined?` | `(subject: Mounted) => void` | The mounted value once the first snapshot arrives |
|
|
29435
|
-
| `onRejected?` | `(message: string) => void` | The host rejected a forwarded command |
|
|
29436
|
-
| `onWarning?` | `(message: string) => void` | A transport-level warning |
|
|
29437
29464
|
| `rtc?` | `MeshRtcFactory` | WebRTC factory: the injection point for tests and non-browser runtimes |
|
|
29438
29465
|
|
|
29439
29466
|
**Example**
|
|
@@ -29448,6 +29475,9 @@ const { answerText, guest } = await joinTavern({
|
|
|
29448
29475
|
onEnded: (subject) => unmountMirror(subject.id),
|
|
29449
29476
|
onJoined: (subject) => mountMirror(subject),
|
|
29450
29477
|
});
|
|
29478
|
+
guest.tap((event) => {
|
|
29479
|
+
if (event.type === 'rejected') console.warn(event.commandId, event.message);
|
|
29480
|
+
});
|
|
29451
29481
|
|
|
29452
29482
|
guest.sendCommand('doc-1', 'rename', ['New name']);
|
|
29453
29483
|
guest.dispose();
|
|
@@ -29460,7 +29490,8 @@ guest.dispose();
|
|
|
29460
29490
|
| `disposalSignal` | `AbortSignal` | Aborted when the session ends |
|
|
29461
29491
|
| `dispose()` | `void` | Leaves the session and drops the channel. Fires `onEnded` exactly once |
|
|
29462
29492
|
| `disposed` | `boolean` | Whether the session has ended: the channel dropped or `dispose()` ran |
|
|
29463
|
-
| `sendCommand(subjectId, name, args)` | `void` | Forwards a command to the host. Throws `
|
|
29493
|
+
| `sendCommand(subjectId, name, args)` | `void` | Forwards a command to the host. Throws `TavernDisposedError` if the session has ended |
|
|
29494
|
+
| `tap(handler, options?)` | `() => void` | Observes `TavernGuestEvent`s; returns an unsubscribe function |
|
|
29464
29495
|
| `[Symbol.dispose]()` | `void` | Delegates to `dispose()`. Enables `using` declarations |
|
|
29465
29496
|
|
|
29466
29497
|
---
|
|
@@ -29501,16 +29532,23 @@ interface TavernNotices {
|
|
|
29501
29532
|
fromWire(wire: unknown): void;
|
|
29502
29533
|
}
|
|
29503
29534
|
|
|
29535
|
+
/** Notable host moments reported through `TavernHost.tap`. */
|
|
29536
|
+
type TavernHostEvent =
|
|
29537
|
+
| { readonly peer: MeshPeer; readonly type: 'peer-joined' }
|
|
29538
|
+
| { readonly peer: MeshPeer; readonly reason?: string; readonly type: 'peer-left' }
|
|
29539
|
+
| { readonly error: unknown; readonly message: string; readonly type: 'warning' };
|
|
29540
|
+
|
|
29541
|
+
/** Notable guest moments reported through `TavernGuest.tap`. */
|
|
29542
|
+
type TavernGuestEvent =
|
|
29543
|
+
| { readonly commandId: string; readonly message: string; readonly type: 'rejected' }
|
|
29544
|
+
| { readonly error: unknown; readonly message: string; readonly type: 'warning' };
|
|
29545
|
+
|
|
29504
29546
|
/** Options for `hostTavern`. */
|
|
29505
29547
|
interface TavernHostOptions {
|
|
29506
29548
|
commands: TavernCommands;
|
|
29507
29549
|
notices?: TavernNotices;
|
|
29508
29550
|
/** Hosting ended: the subject was removed or `dispose()` ran. Fires exactly once. */
|
|
29509
29551
|
onEnded?(): void;
|
|
29510
|
-
onPeersChanged?(peers: MeshPeer[]): void;
|
|
29511
|
-
onPeerJoined?(peer: MeshPeer): void;
|
|
29512
|
-
onPeerLeft?(peer: MeshPeer): void;
|
|
29513
|
-
onWarning?(message: string): void;
|
|
29514
29552
|
rtc?: MeshRtcFactory;
|
|
29515
29553
|
subjectId: string;
|
|
29516
29554
|
subjects: TavernSubjects;
|
|
@@ -29526,8 +29564,6 @@ interface TavernGuestOptions {
|
|
|
29526
29564
|
onEnded?(subject: Mounted): void;
|
|
29527
29565
|
onFailed?(reason: string): void;
|
|
29528
29566
|
onJoined?(subject: Mounted): void;
|
|
29529
|
-
onRejected?(message: string): void;
|
|
29530
|
-
onWarning?(message: string): void;
|
|
29531
29567
|
rtc?: MeshRtcFactory;
|
|
29532
29568
|
}
|
|
29533
29569
|
|
|
@@ -29539,7 +29575,9 @@ interface TavernHost {
|
|
|
29539
29575
|
dispose(): void;
|
|
29540
29576
|
readonly disposed: boolean;
|
|
29541
29577
|
kick(peerId: string): void;
|
|
29578
|
+
readonly peers: readonly MeshPeer[];
|
|
29542
29579
|
relayNotice(notice: unknown): void;
|
|
29580
|
+
tap(handler: (event: TavernHostEvent) => void, options?: { readonly signal?: AbortSignal }): () => void;
|
|
29543
29581
|
[Symbol.dispose](): void;
|
|
29544
29582
|
}
|
|
29545
29583
|
|
|
@@ -29549,6 +29587,7 @@ interface TavernGuest {
|
|
|
29549
29587
|
dispose(): void;
|
|
29550
29588
|
readonly disposed: boolean;
|
|
29551
29589
|
sendCommand(subjectId: string, name: string, args: readonly unknown[]): void;
|
|
29590
|
+
tap(handler: (event: TavernGuestEvent) => void, options?: { readonly signal?: AbortSignal }): () => void;
|
|
29552
29591
|
[Symbol.dispose](): void;
|
|
29553
29592
|
}
|
|
29554
29593
|
```
|
|
@@ -29559,8 +29598,9 @@ interface TavernGuest {
|
|
|
29559
29598
|
| --- | --- | --- |
|
|
29560
29599
|
| `TavernError` | Base class for every Tavern failure: catch this to handle all Tavern errors in one branch | N/A |
|
|
29561
29600
|
| `TavernPairingError` | A pairing code the consumer pasted could not be used: wrong kind, malformed, expired, or refused by the host. The mesh-level failure is chained as `cause` | `cause` |
|
|
29601
|
+
| `TavernDisposedError` | A stopped host (`kick`, `createInvitationText`, `acceptAnswerText`) or an ended guest (`sendCommand`) was used again | N/A |
|
|
29562
29602
|
|
|
29563
|
-
`TavernPairingError`
|
|
29603
|
+
`TavernPairingError` and `TavernDisposedError` extend `TavernError`. Every user-input pairing mistake: a garbage code, the wrong code kind, an expired invitation, a refused answer: surfaces as `TavernPairingError` from `acceptAnswerText` and `joinTavern`; transport-level failures (timeouts, connection errors) propagate unchanged. Post-dispose use fails loudly with `TavernDisposedError`, mirroring mesh inside the Tavern error hierarchy.
|
|
29564
29604
|
|
|
29565
29605
|
### Usage Guide
|
|
29566
29606
|
|
|
@@ -29605,10 +29645,6 @@ const host = hostTavern({
|
|
|
29605
29645
|
has: (name) => name in myStore,
|
|
29606
29646
|
},
|
|
29607
29647
|
onEnded: () => updateSessionUi(), // hosting ended: subject removed or disposed
|
|
29608
|
-
onPeersChanged: (peers) => updatePeerList(peers),
|
|
29609
|
-
onPeerJoined: (peer) => toast(`${peer.name ?? 'A guest'} joined`),
|
|
29610
|
-
onPeerLeft: (peer) => toast(`${peer.name ?? 'A guest'} left`),
|
|
29611
|
-
onWarning: (message) => log.warn(message),
|
|
29612
29648
|
subjectId: 'doc-1',
|
|
29613
29649
|
subjects: {
|
|
29614
29650
|
onChanged: (listener) => myStore.onUpdated('doc-1', listener),
|
|
@@ -29617,6 +29653,14 @@ const host = hostTavern({
|
|
|
29617
29653
|
},
|
|
29618
29654
|
});
|
|
29619
29655
|
|
|
29656
|
+
// Presence and transport warnings arrive through tap(); the peer list is a getter.
|
|
29657
|
+
host.tap((event) => {
|
|
29658
|
+
if (event.type === 'peer-joined') toast(`${event.peer.name ?? 'A guest'} joined`);
|
|
29659
|
+
if (event.type === 'peer-left') toast(`${event.peer.name ?? 'A guest'} left`);
|
|
29660
|
+
if (event.type === 'warning') log.warn(event.message, event.error);
|
|
29661
|
+
updatePeerList(host.peers); // always current inside a presence event
|
|
29662
|
+
});
|
|
29663
|
+
|
|
29620
29664
|
const invitation = await host.createInvitationText();
|
|
29621
29665
|
// Show the invitation (QR or copy/paste); the guest returns an answer code.
|
|
29622
29666
|
await host.acceptAnswerText(answerCode);
|
|
@@ -29641,7 +29685,10 @@ const { answerText, guest } = await joinTavern({
|
|
|
29641
29685
|
onEnded: (subject) => unmountMirror(subject.id),
|
|
29642
29686
|
onFailed: (reason) => showError(reason),
|
|
29643
29687
|
onJoined: (subject) => mountMirror(subject),
|
|
29644
|
-
|
|
29688
|
+
});
|
|
29689
|
+
guest.tap((event) => {
|
|
29690
|
+
if (event.type === 'rejected') showToast(event.message); // event.commandId names the command it answers
|
|
29691
|
+
if (event.type === 'warning') log.warn(event.message, event.error);
|
|
29645
29692
|
});
|
|
29646
29693
|
|
|
29647
29694
|
guest.sendCommand('doc-1', 'rename', ['New name']);
|
|
@@ -29668,6 +29715,21 @@ host.relayNotice(localNotice);
|
|
|
29668
29715
|
|
|
29669
29716
|
**Echo guard**: a tab that both hosts and guests must prevent re-broadcasting a wire-originated notice back to its own guests. One boolean set during `fromWire` and checked in `toWire` is sufficient: the consumer owns this because only they know their local event system.
|
|
29670
29717
|
|
|
29718
|
+
## Observe with tap()
|
|
29719
|
+
|
|
29720
|
+
Both handles expose `tap()` for observability: presence and warnings on the host, rejections and warnings on the guest. Handler errors are swallowed, and the subscription detaches when its signal aborts or the session ends. Lifecycle transitions stay callbacks (`onEnded`, `onJoined`, `onFailed`) because they fire while `joinTavern` is still resolving, before you can hold the handle.
|
|
29721
|
+
|
|
29722
|
+
```ts
|
|
29723
|
+
import type { TavernGuestEvent, TavernHostEvent } from '@vielzeug/tavern';
|
|
29724
|
+
|
|
29725
|
+
host.tap((event: TavernHostEvent) => {
|
|
29726
|
+
if (event.type === 'warning') log.warn(event.message, event.error); // the original error, not just text
|
|
29727
|
+
});
|
|
29728
|
+
guest.tap((event: TavernGuestEvent) => {
|
|
29729
|
+
if (event.type === 'rejected') log.info(`command ${event.commandId} rejected: ${event.message}`);
|
|
29730
|
+
});
|
|
29731
|
+
```
|
|
29732
|
+
|
|
29671
29733
|
## Handle Pairing Mistakes
|
|
29672
29734
|
|
|
29673
29735
|
Every user-input pairing failure surfaces as `TavernPairingError` from both `acceptAnswerText` and `joinTavern`: a garbage code, the wrong code kind, an expired invitation, or an answer the host refuses. Catch it to show a helpful message instead of a raw error; the underlying mesh failure is chained as `cause` when you need it.
|
|
@@ -29688,7 +29750,7 @@ try {
|
|
|
29688
29750
|
|
|
29689
29751
|
## Working with Other Vielzeug Libraries
|
|
29690
29752
|
|
|
29691
|
-
Tavern builds on `@vielzeug/mesh` for the WebRTC transport, pairing codes, and QR-optimized codecs. You do not interact with mesh directly: tavern owns the protocol, but the `rtc` option accepts a `MeshRtcFactory` for testing or non-browser runtimes, and the
|
|
29753
|
+
Tavern builds on `@vielzeug/mesh` for the WebRTC transport, pairing codes, and QR-optimized codecs. You do not interact with mesh directly: tavern owns the protocol, but the `rtc` option accepts a `MeshRtcFactory` for testing or non-browser runtimes, and the peers carried by tap events and the {@link TavernHost.peers} getter are mesh types.
|
|
29692
29754
|
|
|
29693
29755
|
```ts
|
|
29694
29756
|
import type { MeshRtcFactory } from '@vielzeug/mesh';
|
package/data/llms.txt
CHANGED
package/data/manifest.json
CHANGED