@sigrun/shared 0.1.1 → 0.1.2

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/CHANGELOG.md CHANGED
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.2] - 2026-10-24
11
+ ### Added
12
+ - Extension-surface wire types, including block-instance and live-view
13
+ contracts shared by the server, webui, and TUI.
14
+
15
+ ## [0.1.1] - 2026-10-23
16
+ ### Added
17
+ - `./live` and `./blocks` export subpaths.
18
+
10
19
  ## [0.1.0] - 2026-10-21
11
20
  ### Added
12
21
  - First published release of the shared leaf (workspace package of
@@ -17,6 +17,7 @@ export { ICON_NAMES } from "./icons.js";
17
17
  export { lttb } from "./lttb.js";
18
18
  export { formatValue } from "./format.js";
19
19
  export { toText } from "./text.js";
20
+ export { canonicalJson, eventKeyWeb, instanceKey } from "./instance.js";
20
21
  export type { Action, ActionButton, Bindable, Binding, Block, BlockBase, BulkAction, CalloutBlock, Card, CardBadge, CardMetric, CardMeta, CardStatus, ChartAxisX, ChartAxisY, ChartBlock, ChartSeries, ChartThreshold, ChoiceField, ChoiceOption, CollectionBlock, CollectionColumn, CollectionGroup, CollectionSort, CollectionTime, ColumnChild, ColumnsBlock, Confirm, CoreTarget, DataOp, DataOps, DataUpdate, DateField, DatePreset, DaterangeField, DatetimeField, DetailsBlock, DiffBlock, Field, FieldBase, FieldsBlock, FieldsRow, FileField, Format, FormBlock, GridBlock, GridChild, ImageBlock, ListField, LogBlock, LogLine, LongtextField, MarkdownBlock, MeterBlock, MeterThreshold, NumberField, PageBlock, PageEmpty, Placement, SectionBlock, SecretField, Size, StackBlock, StatBlock, TextField, ToggleField, Tone, Tree, TreeBlock, TreeNode, WidthCondition, } from "./types.js";
21
22
  export type { IconName } from "./icons.js";
22
23
  export type { TextOptions } from "./text.js";
@@ -18,3 +18,4 @@ export { ICON_NAMES } from "./icons.js";
18
18
  export { lttb } from "./lttb.js";
19
19
  export { formatValue } from "./format.js";
20
20
  export { toText } from "./text.js";
21
+ export { canonicalJson, eventKeyWeb, instanceKey } from "./instance.js";
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The instance-key helpers the CHANNELS need (UI-blocks U3 Task 5, spec
3
+ * §7.3): `canonicalJson` (the default `instanceKey`) and
4
+ * `eventKeyWeb`/`eventKeyNode` (the wire `k` — sha256 hex of
5
+ * `slug\0id\0key`). The server's copy lives in
6
+ * `src/server/surfaces/instance.ts`; these are the DOM- and Node-safe
7
+ * twins (WebCrypto's `crypto.subtle.digest` vs `node:crypto`), kept
8
+ * byte-compatible by the shared canonical-JSON rule.
9
+ *
10
+ * The wire's purity rule holds: NO node: imports here — the Node flavor
11
+ * is a separate module (`instance-node.ts`) so the webui never sees it.
12
+ */
13
+ import type { JsonObject, JsonValue } from "../json.js";
14
+ /**
15
+ * Canonical JSON: every object serializes with keys SORTED (recursive);
16
+ * primitives and arrays serialize as usual. Two params objects equal up
17
+ * to key order produce byte-identical strings — the same rule as the
18
+ * server's `canonicalJson` (kept in sync by hand; both sides pin it by
19
+ * test).
20
+ */
21
+ export declare function canonicalJson(value: JsonValue): string;
22
+ /** The default instance key (spec §7.3): canonical JSON of the params. */
23
+ export declare function instanceKey(params: JsonObject): string;
24
+ /**
25
+ * The wire `k` for one surface instance (spec §7.3), WebCrypto flavor:
26
+ * `sha256(slug + "\0" + id + "\0" + key)` hex. Async (the DOM digest is
27
+ * promise-shaped); the Node twin (`instance-node.ts`) is sync.
28
+ */
29
+ export declare function eventKeyWeb(slug: string, id: string, key: string): Promise<string>;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The instance-key helpers the CHANNELS need (UI-blocks U3 Task 5, spec
3
+ * §7.3): `canonicalJson` (the default `instanceKey`) and
4
+ * `eventKeyWeb`/`eventKeyNode` (the wire `k` — sha256 hex of
5
+ * `slug\0id\0key`). The server's copy lives in
6
+ * `src/server/surfaces/instance.ts`; these are the DOM- and Node-safe
7
+ * twins (WebCrypto's `crypto.subtle.digest` vs `node:crypto`), kept
8
+ * byte-compatible by the shared canonical-JSON rule.
9
+ *
10
+ * The wire's purity rule holds: NO node: imports here — the Node flavor
11
+ * is a separate module (`instance-node.ts`) so the webui never sees it.
12
+ */
13
+ /**
14
+ * Canonical JSON: every object serializes with keys SORTED (recursive);
15
+ * primitives and arrays serialize as usual. Two params objects equal up
16
+ * to key order produce byte-identical strings — the same rule as the
17
+ * server's `canonicalJson` (kept in sync by hand; both sides pin it by
18
+ * test).
19
+ */
20
+ export function canonicalJson(value) {
21
+ if (Array.isArray(value)) {
22
+ return `[${value.map((entry) => canonicalJson(entry)).join(",")}]`;
23
+ }
24
+ if (typeof value === "object" && value !== null) {
25
+ const keys = Object.keys(value).sort();
26
+ const parts = keys.map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`);
27
+ return `{${parts.join(",")}}`;
28
+ }
29
+ return JSON.stringify(value);
30
+ }
31
+ /** The default instance key (spec §7.3): canonical JSON of the params. */
32
+ export function instanceKey(params) {
33
+ return canonicalJson(params);
34
+ }
35
+ /** The hex encoding of a sha256 digest buffer. */
36
+ function hex(buffer) {
37
+ const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
38
+ let out = "";
39
+ for (const byte of bytes)
40
+ out += byte.toString(16).padStart(2, "0");
41
+ return out;
42
+ }
43
+ /**
44
+ * The wire `k` for one surface instance (spec §7.3), WebCrypto flavor:
45
+ * `sha256(slug + "\0" + id + "\0" + key)` hex. Async (the DOM digest is
46
+ * promise-shaped); the Node twin (`instance-node.ts`) is sync.
47
+ */
48
+ export async function eventKeyWeb(slug, id, key) {
49
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode([slug, id, key].join("\u0000")));
50
+ return hex(digest);
51
+ }
package/dist/views.d.ts CHANGED
@@ -402,6 +402,14 @@ export interface MetaResponse {
402
402
  * worker's first run.
403
403
  */
404
404
  readonly workers?: readonly WorkerStatus[];
405
+ /**
406
+ * The declared surfaces (UI-blocks U3 Task 5): the flattened
407
+ * view + panel rows the channels register their rail/panel
408
+ * entries from. Absent when the extension declared none. The
409
+ * meta response carries the DECLARATION (id/title/icon/
410
+ * placement); access filtering is render-time (§6.5).
411
+ */
412
+ readonly surfaces?: readonly ExtensionSurfaceInfo[];
405
413
  }[];
406
414
  /**
407
415
  * §5 health: pure aggregation over boot-retained §3 producers (spec
@@ -589,6 +597,8 @@ export interface ExtensionInfo {
589
597
  readonly workers: readonly WorkerStatus[];
590
598
  /** The declared HTTP routes, one `{method, path, auth}` each (W5 Task 2). */
591
599
  routes?: readonly ExtensionRouteInfo[];
600
+ /** The declared surfaces, one `{id, title, icon, placement}` each (U3 Task 1); absent when none. */
601
+ surfaces?: readonly ExtensionSurfaceInfo[];
592
602
  /**
593
603
  * The channel ids this extension contributes (gateway C1, spec §7):
594
604
  * present only on rows of extensions whose factory result carried
@@ -622,6 +632,26 @@ export interface ExtensionRouteInfo {
622
632
  readonly path: string;
623
633
  readonly auth: string;
624
634
  }
635
+ /**
636
+ * One `GET /v1/extensions` surface row (UI-blocks U3 Task 1, spec
637
+ * §7.1): the extension-declared views and panels — what the channels'
638
+ * rail (views) and right panel (panels) register from. One row per
639
+ * declared surface, in declaration order (views before panels).
640
+ */
641
+ export interface ExtensionSurfaceInfo {
642
+ readonly id: string;
643
+ readonly title: string;
644
+ readonly icon: string;
645
+ /** The declared placement; `settings` sections (§13.3) are not in this workstream. */
646
+ readonly placement: "view" | "panel";
647
+ /** The declared refresh cadence (spec §7.1); channels refetch data on
648
+ * this interval while the surface is visible. Absent → no cadence. */
649
+ readonly refreshMs?: number;
650
+ /** §7.3 shared-data flag: a sharedData surface's `since` fetches may
651
+ * answer with `ops` and NO `data` — channels fall back to a full
652
+ * fetch (ops folding is U1). Absent → per-instance data. */
653
+ readonly sharedData?: boolean;
654
+ }
625
655
  /** The `GET /v1/extensions` payload (spec §7.7). */
626
656
  export interface ExtensionsResponse {
627
657
  readonly extensions: readonly ExtensionInfo[];
@@ -781,4 +811,12 @@ export type HostEventWire = {
781
811
  /** W4 Task 4 (§8.4): the changed job's summary, WITHOUT `access` — the events stream computes `accessOf` per principal and adds it before delivery (a hidden job's event is withheld entirely). */
782
812
  readonly type: "job.changed";
783
813
  readonly job: Omit<JobWire, "access">;
814
+ } | {
815
+ /** UI-blocks U3 Task 4 (spec §7.3): one surface instance changed. `k` is the hashed instance key (sha256(slug + "\0" + id + "\0" + key) hex) — the event NEVER carries params or data (render/data are principal-specific; the watcher refetches as itself). Delivered only to streams in the watcher registry whose stored access is not `"none"`. */
816
+ readonly type: "surface.changed";
817
+ readonly slug: string;
818
+ readonly id: string;
819
+ readonly k: string;
820
+ readonly rev: number;
821
+ readonly treeRev: number;
784
822
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sigrun/shared",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "sigrun shared leaf package \u2014 theme tokens, turn/conversation shaping, and the channel wire types (pure leaf, zero dependencies)",
5
5
  "type": "module",
6
6
  "exports": {