@kumwe/studio-preview 0.1.0-alpha.4 → 0.1.0-alpha.6

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
@@ -14,7 +14,8 @@ still owns authentication, authorization, CSP, sandboxing, and rendering.
14
14
  `PreviewClient` is the Studio half of the channel; `PreviewHost` is the preview-surface half. Both
15
15
  pin an exact target origin and drop any inbound message whose source window, origin, schema, channel
16
16
  ID, session generation, or sequence does not match; each direction stamps its own monotonic sequence
17
- counter.
17
+ counter. Outbound client messages cross the same canonical guard before posting, and the draft.2
18
+ message vocabulary is closed.
18
19
 
19
20
  The handshake and request flow is announce → ready → render → rendered:
20
21
 
@@ -22,21 +23,47 @@ The handshake and request flow is announce → ready → render → rendered:
22
23
  callback, then calls `announce()` to post `studio.preview/ready`.
23
24
  2. Studio awaits `client.ready()`, which resolves with the announced payload — immediately when the
24
25
  announcement already arrived — and rejects on timeout, abort, or disposal.
25
- 3. Studio requests `studio.preview/render` via `client.render()`. The host invokes its render
26
- callback and replies with `studio.preview/rendered` carrying the same draft digest as the
27
- request. A newer request supersedes an in-flight one on both sides, so a stale result is never
28
- posted or resolved.
26
+ 3. Studio requests `studio.preview/render` via `client.render()` with a session-unique `requestId`.
27
+ The host invokes its render callback with an `AbortSignal` and replies with
28
+ `studio.preview/rendered` carrying the same request ID and draft digest. A newer request aborts and
29
+ generation-invalidates in-flight work on both sides, so a stale result is never posted or resolved
30
+ even when a retry uses the same digest at another viewport.
29
31
  4. A failed render is answered with `studio.preview/error` (code `studio.preview/render-failed`, the
30
- draft digest as `correlationId`, `retryable: true`) and a generic message — renderer failure
32
+ request ID as `correlationId`, `retryable: true`) and a generic message — renderer failure
31
33
  details never cross the channel.
32
34
  5. `client.select()` forwards `studio.preview/select` to the host's `onSelect` listeners.
33
35
 
36
+ ## Portable draft identity and markers
37
+
38
+ `canonicalPreviewDraftBytes(draft)` returns exactly the canonical UTF-8 bytes of a complete Studio
39
+ artifact. `computePreviewDraftDigest(draft)` hashes those bytes as lowercase hexadecimal SHA-256; it
40
+ does not add an envelope, prefix, viewport, BOM, or newline. The host still validates and authorizes
41
+ the staged draft, recomputes its digest, and compares it before rendering — the digest is never a
42
+ credential.
43
+
44
+ `createPreviewMarker(digest, ordinal)` implements the canonical
45
+ `studio.preview/node/<digest>/<ordinal>` grammar. `createPreviewMarkerInventory(blueprint, digest)`
46
+ enumerates roots, each node before descendants, sorted slot names, then child arrays. Every rendered
47
+ response requires an exact one-to-one `markerMap`; the generic responder refuses a mismatched digest,
48
+ grammar, contiguous order, duplicate, or incomplete map. Because the channel does not hold the draft,
49
+ the authoritative renderer additionally compares node mapping with `createPreviewMarkerInventory`;
50
+ the standalone reference renderer does so. `announceActivation()` accepts only a marker from the
51
+ currently accepted inventory, and the client independently drops invented or revoked activation
52
+ traffic.
53
+
54
+ These semantics require wire protocol `0.1.0-draft.2`. A `.1` peer is incompatible rather than
55
+ silently accepted. Portable implementations replay `@kumwe/studio-testkit/vectors/preview/` against
56
+ their own canonicalizer and traversal.
57
+
34
58
  ## Marker geometry
35
59
 
36
- `client.measure()` posts `studio.preview/measure` with a bounded marker list and resolves with the
60
+ `client.measure()` posts `studio.preview/measure` with a non-empty bounded marker list and resolves with the
37
61
  `studio.preview/measurements` answer: per marker, one or more CSS-pixel rectangles relative to the
38
62
  preview viewport origin (inline content fragments across lines), plus viewport metrics. Markers the
39
- renderer cannot place are returned in a distinct `unknown` list, never thrown.
63
+ renderer cannot place are returned in a distinct `unknown` list, never thrown. A marker outside the
64
+ latest digest-bound inventory is rejected locally with `PreviewChannelError` code
65
+ `studio.preview/measure-stale-marker`; the host independently rejects raw stale traffic without
66
+ invoking the measurer.
40
67
 
41
68
  The host never reads the DOM. The embedding renderer passes a `measure` callback in
42
69
  `PreviewHostOptions`; without one, measure requests are answered with the qualified
@@ -44,10 +71,12 @@ The host never reads the DOM. The embedding renderer passes a `measure` callback
44
71
  `studio.preview/measure-failed` — measurer failure details never cross the channel.
45
72
 
46
73
  Geometry is volatile, not document state: each response is stamped with the digest of the render it
47
- was measured against. A response whose digest no longer matches the client's latest completed render
48
- resolves as a typed `{ status: 'stale' }` outcome instead of geometry, and a reload voids in-flight
49
- measurements exactly like it voids renders. Newer measure requests supersede older ones on both
50
- sides.
74
+ was measured against. The host captures that digest and a private generation before invoking the
75
+ callback. A same-digest rerender, newer measurement, disposal, reload or teardown aborts and
76
+ invalidates the prior measurement. A viewport instruction also rejects the client's pending
77
+ measurement with `PreviewChannelError` code `studio.preview/measure-viewport-changed` and aborts the
78
+ host callback before viewport listeners run, so pre-resize geometry cannot settle late. Measured and
79
+ unknown entries must exactly partition the requested markers.
51
80
 
52
81
  Version negotiation currently requires the exact draft wire version on both sides: schema filtering
53
82
  accepts only `STUDIO_WIRE_PROTOCOL_VERSION`, so a ready announcement from a host speaking any other
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
- export { PreviewClient, type PreviewClientOptions, type PreviewMeasureOptions, type PreviewMeasureOutcome, type PreviewMessageEvent, type PreviewMessageListener, type PreviewMessageSource, type PreviewMessageTarget, type PreviewProtocolListener, type PreviewReadyOptions, type PreviewRenderOptions, } from './preview-client.js';
1
+ export { PreviewClient, PreviewChannelError, type PreviewClientOptions, type PreviewMeasureOptions, type PreviewMeasureOutcome, type PreviewMessageEvent, type PreviewMessageListener, type PreviewMessageSource, type PreviewMessageTarget, type PreviewProtocolListener, type PreviewReadyOptions, type PreviewRenderOptions, } from './preview-client.js';
2
2
  export { PreviewHost, type PreviewHostOptions, type PreviewMeasureCallback, type PreviewMeasurement, type PreviewRenderCallback, type PreviewSelectListener, } from './preview-host.js';
3
+ export { canonicalPreviewDraftBytes, computePreviewDraftDigest, createPreviewMarker, createPreviewMarkerInventory, type PreviewDigestOptions, type PreviewMarkerInventory, } from './preview-identity.js';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,GAC1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,WAAW,EACX,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,GAC3B,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,GAC1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,WAAW,EACX,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,GAC3B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,mBAAmB,EACnB,4BAA4B,EAC5B,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,GAC5B,MAAM,uBAAuB,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
- export { PreviewClient, } from './preview-client.js';
1
+ export { PreviewClient, PreviewChannelError, } from './preview-client.js';
2
2
  export { PreviewHost, } from './preview-host.js';
3
+ export { canonicalPreviewDraftBytes, computePreviewDraftDigest, createPreviewMarker, createPreviewMarkerInventory, } from './preview-identity.js';
3
4
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,GAWd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,WAAW,GAMZ,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,mBAAmB,GAWpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,WAAW,GAMZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,mBAAmB,EACnB,4BAA4B,GAG7B,MAAM,uBAAuB,CAAC"}
@@ -1,4 +1,10 @@
1
- import { type PreviewMeasurePayload, type PreviewMeasurementsPayload, type PreviewMessage, type PreviewReadyPayload, type PreviewRenderedPayload, type PreviewRenderPayload, type PreviewSelectPayload, type PreviewTeardownPayload } from '@kumwe/studio-protocol';
1
+ import { type PreviewMeasurePayload, type PreviewMeasurementsPayload, type PreviewMessage, type PreviewReadyPayload, type PreviewRenderedPayload, type PreviewRenderPayload, type PreviewActivatedPayload, type PreviewDisposePayload, type PreviewSelectPayload, type PreviewViewportPayload, type PreviewTeardownPayload, type QualifiedName } from '@kumwe/studio-protocol';
2
+ /** Stable client-side and wire failure surfaced by the preview channel. */
3
+ export declare class PreviewChannelError extends Error {
4
+ readonly code: QualifiedName;
5
+ readonly retryable: boolean;
6
+ constructor(code: QualifiedName, message: string, retryable?: boolean);
7
+ }
2
8
  export interface PreviewMessageEvent {
3
9
  data: unknown;
4
10
  origin: string;
@@ -42,6 +48,7 @@ export type PreviewMeasureOutcome = {
42
48
  status: 'stale';
43
49
  };
44
50
  export type PreviewProtocolListener = (message: PreviewMessage) => void;
51
+ export type PreviewActivationListener = (payload: PreviewActivatedPayload) => void;
45
52
  export declare class PreviewClient {
46
53
  #private;
47
54
  constructor(options: PreviewClientOptions);
@@ -67,6 +74,20 @@ export declare class PreviewClient {
67
74
  * volatile measurement of a specific render digest, never document state.
68
75
  */
69
76
  measure(payload: PreviewMeasurePayload, options?: PreviewMeasureOptions): Promise<PreviewMeasureOutcome>;
77
+ /**
78
+ * Drive the preview surface to a semantic viewport role or to bounded
79
+ * explicit dimensions. The two are alternatives, so a payload carrying both
80
+ * is refused before it reaches the channel.
81
+ */
82
+ setViewport(payload: PreviewViewportPayload): void;
83
+ /**
84
+ * Instruct the renderer to revoke the resources it holds for a superseded
85
+ * draft while the channel stays open. This is not teardown: teardown ends
86
+ * the session, dispose frees a render's resources within it.
87
+ */
88
+ disposeDraft(payload: PreviewDisposePayload): void;
89
+ /** Observe trusted marker interactions the renderer reports. */
90
+ onActivated(listener: PreviewActivationListener): () => void;
70
91
  select(payload: PreviewSelectPayload): void;
71
92
  /** Announce channel closure to the host, then dispose this client. */
72
93
  teardown(reason: PreviewTeardownPayload['reason']): void;
@@ -1 +1 @@
1
- {"version":3,"file":"preview-client.d.ts","sourceRoot":"","sources":["../src/preview-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,qBAAqB,EAC1B,KAAK,0BAA0B,EAC/B,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC5B,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,MAAM,sBAAsB,GAAG,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;AAE1E,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC1E,mBAAmB,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,sBAAsB,GAAG,IAAI,CAAC;CAC9E;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3D;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,oBAAoB,CAAC;IAC7B,MAAM,EAAE,oBAAoB,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAC7B;IAAE,QAAQ,EAAE,0BAA0B,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GAC5D;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC;AAEhD,MAAM,MAAM,uBAAuB,GAAG,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;AAuBxE,qBAAa,aAAa;;gBAmBL,OAAO,EAAE,oBAAoB;IAazC,OAAO,IAAI,IAAI;IA2Bf,SAAS,CAAC,QAAQ,EAAE,uBAAuB,GAAG,MAAM,IAAI;IAO/D;;;;;;;;OAQG;IACI,KAAK,CAAC,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAqCtE,MAAM,CACX,OAAO,EAAE,oBAAoB,EAC7B,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,sBAAsB,CAAC;IAiElC;;;;;;;OAOG;IACI,OAAO,CACZ,OAAO,EAAE,qBAAqB,EAC9B,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,qBAAqB,CAAC;IA6D1B,MAAM,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI;IAqJlD,sEAAsE;IAC/D,QAAQ,CAAC,MAAM,EAAE,sBAAsB,CAAC,QAAQ,CAAC,GAAG,IAAI;CAahE;AAGD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUrD"}
1
+ {"version":3,"file":"preview-client.d.ts","sourceRoot":"","sources":["../src/preview-client.ts"],"names":[],"mappings":"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"}