@swifttui/web 0.4.0 → 0.4.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.
Files changed (41) hide show
  1. package/dist/index.d.ts +3 -3
  2. package/dist/index.js +2 -2
  3. package/dist/src/AccessibilityTree.js +10 -2
  4. package/dist/src/AccessibilityTree.js.map +1 -1
  5. package/dist/src/CanvasSurfacePainter.d.ts +16 -0
  6. package/dist/src/CanvasSurfacePainter.js +162 -16
  7. package/dist/src/CanvasSurfacePainter.js.map +1 -1
  8. package/dist/src/DomSurfacePainter.d.ts +17 -3
  9. package/dist/src/DomSurfacePainter.js +63 -10
  10. package/dist/src/DomSurfacePainter.js.map +1 -1
  11. package/dist/src/SurfacePainterConformanceControl.js +13 -0
  12. package/dist/src/SurfacePainterConformanceControl.js.map +1 -0
  13. package/dist/src/SurfaceRenderer.d.ts +3 -2
  14. package/dist/src/SurfaceRenderer.js.map +1 -1
  15. package/dist/src/WebHostSceneRuntime.d.ts +3 -1
  16. package/dist/src/WebHostSceneRuntime.js +16 -7
  17. package/dist/src/WebHostSceneRuntime.js.map +1 -1
  18. package/dist/src/WebHostSurfaceTransport.d.ts +63 -6
  19. package/dist/src/WebHostSurfaceTransport.js +186 -9
  20. package/dist/src/WebHostSurfaceTransport.js.map +1 -1
  21. package/dist/src/WebSocketSceneBridge.d.ts +2 -0
  22. package/dist/src/WebSocketSceneBridge.js +30 -5
  23. package/dist/src/WebSocketSceneBridge.js.map +1 -1
  24. package/dist/src/normalizeWireTokens.d.ts +5 -0
  25. package/dist/src/normalizeWireTokens.js +41 -0
  26. package/dist/src/normalizeWireTokens.js.map +1 -0
  27. package/dist/src/wasi/BrowserWASIBridge.d.ts +8 -0
  28. package/dist/src/wasi/BrowserWASIBridge.js +31 -4
  29. package/dist/src/wasi/BrowserWASIBridge.js.map +1 -1
  30. package/dist/src/wasi/SharedInputQueue.d.ts +2 -0
  31. package/dist/src/wasi/SharedInputQueue.js +22 -0
  32. package/dist/src/wasi/SharedInputQueue.js.map +1 -1
  33. package/dist/src/wasi/StdIOPipe.d.ts +2 -2
  34. package/dist/src/wasi/StdIOPipe.js +5 -3
  35. package/dist/src/wasi/StdIOPipe.js.map +1 -1
  36. package/dist/src/wasi/WasmSceneRuntime.js +24 -4
  37. package/dist/src/wasi/WasmSceneRuntime.js.map +1 -1
  38. package/dist/src/wasi/WasmSceneWorker.js +1 -1
  39. package/dist/testing.d.ts +2 -1
  40. package/dist/wasi.js +1 -1
  41. package/package.json +2 -1
@@ -16,7 +16,7 @@ type WebHostSurfaceCell = [x: number, text: string, span: number, styleIndex: nu
16
16
  type WebHostSurfaceRect = [x: number, y: number, width: number, height: number];
17
17
  type WebHostSurfaceSize = [width: number, height: number];
18
18
  type WebHostAccessibilityPoint = [x: number, y: number];
19
- type WebHostAccessibilityLiveRegion = "off" | "polite" | "assertive";
19
+ type WebHostAccessibilityLiveRegion = string;
20
20
  interface WebHostAccessibilityNode {
21
21
  id: string;
22
22
  parentId?: string;
@@ -37,7 +37,7 @@ interface WebHostAccessibilityNode {
37
37
  type WebHostSurfaceLinkRun = [x: number, span: number, targetIndex: number];
38
38
  /** Hyperlink runs for one row: [rowIndex, runs]. */
39
39
  type WebHostSurfaceLinkRow = [row: number, runs: WebHostSurfaceLinkRun[]];
40
- type WebHostFocusSemantics = "none" | "automatic" | "activate" | "edit";
40
+ type WebHostFocusSemantics = string;
41
41
  /**
42
42
  * The settled focus presentation for a committed frame — the same derivation
43
43
  * the Android host consumes (`prefersTextInput` gates its IME).
@@ -52,13 +52,20 @@ interface WebHostAccessibilityAnnouncement {
52
52
  message: string;
53
53
  politeness: WebHostAccessibilityLiveRegion;
54
54
  }
55
- type WebHostSurfaceImageFormat = "png" | "jpeg" | "gif";
55
+ type WebHostSurfaceImageFormat = string;
56
+ /**
57
+ * Reports locally unresolved image IDs to a recovery-capable host. The first
58
+ * branch lets bounded hosts return the admitted subset; the explicit legacy
59
+ * void branch preserves contextual typing for concise callbacks that return
60
+ * incidental values such as `array.push(...)`.
61
+ */
62
+ type WebHostImagePayloadRequestHandler = ((ids: readonly string[]) => readonly string[]) | ((ids: readonly string[]) => void);
56
63
  interface WebHostSurfaceImage {
57
64
  id: string;
58
65
  format: WebHostSurfaceImageFormat;
59
66
  bounds: WebHostSurfaceRect;
60
67
  visibleBounds: WebHostSurfaceRect;
61
- scalingMode: "stretch" | "fit" | "fill";
68
+ scalingMode: string;
62
69
  pixelSize?: WebHostSurfaceSize;
63
70
  dataBase64?: string;
64
71
  }
@@ -89,6 +96,8 @@ interface WebHostScrollRegion {
89
96
  }
90
97
  interface WebHostSurfaceFrame {
91
98
  version: 1 | 2;
99
+ epoch?: number;
100
+ gen?: number;
92
101
  sequence?: number;
93
102
  width: number;
94
103
  height: number;
@@ -109,6 +118,9 @@ type WebHostSurfaceDeltaRow = [row: number, cells: WebHostSurfaceCell[]];
109
118
  interface WebHostSurfaceDeltaFrame {
110
119
  version: 3;
111
120
  encoding: "delta";
121
+ epoch?: number;
122
+ gen?: number;
123
+ baselineGen?: number;
112
124
  sequence?: number;
113
125
  width: number;
114
126
  height: number;
@@ -150,12 +162,21 @@ type WebHostOutputRecord = {
150
162
  } | {
151
163
  type: "frameDiagnostic";
152
164
  diagnostic: WebHostFrameDiagnosticRecord;
165
+ } | {
166
+ type: "surfaceDropped";
167
+ reason: "noBaseline" | "staleBaseline";
153
168
  } | {
154
169
  type: "text";
155
170
  text: string;
156
171
  };
172
+ type WebHostResyncRequest = {
173
+ scope: "keyframe";
174
+ } | {
175
+ scope: "images";
176
+ ids?: string[];
177
+ };
157
178
  interface WebHostOutputSink {
158
- presentSurface(frame: WebHostSurfaceFrame): void;
179
+ presentSurface(frame: WebHostSurfaceFrame, recoveredImagePayloadIds?: readonly string[]): void;
159
180
  writeClipboard?(text: string): void | Promise<void>;
160
181
  notifyRuntimeIssue?(issue: WebHostRuntimeIssue): void;
161
182
  recordFrameDiagnostic?(diagnostic: WebHostFrameDiagnosticRecord): void;
@@ -176,6 +197,10 @@ interface WebHostMouseInput {
176
197
  deltaY?: number;
177
198
  modifiers?: number;
178
199
  }
200
+ /** Limits wire-derived recovery state and keeps every single-ID WASI request bounded. */
201
+ declare const MAX_IMAGE_RECOVERY_ID_BYTES = 1024;
202
+ declare const MAX_OUTSTANDING_IMAGE_RECOVERY_IDS = 1024;
203
+ declare function isWebHostImageRecoveryId(id: string): boolean;
179
204
  /**
180
205
  * The newest `surface` record version this runtime understands. Unknown
181
206
  * additive object keys are ignored by design (older runtimes render newer
@@ -189,17 +214,49 @@ declare class WebHostOutputDecoder {
189
214
  private readonly textDecoder;
190
215
  private bufferedText;
191
216
  private lastSurfaceFrame?;
217
+ private lastEpoch?;
218
+ private lastGen?;
219
+ private lastPresentedEpoch?;
220
+ private keyframeResyncOutstanding;
221
+ private keyframeResyncPending;
222
+ private readonly imageResyncOutstandingIds;
223
+ private readonly imageResyncPendingIds;
192
224
  feed(chunk: Uint8Array): WebHostOutputRecord[];
193
225
  flush(): WebHostOutputRecord[];
226
+ takeResyncRequest(maximumEncodedBytes?: number): WebHostResyncRequest | undefined;
227
+ /**
228
+ * Adds locally unresolved image IDs to this epoch's recovery set. IDs remain
229
+ * outstanding after delivery, so repeat painter misses cannot create storms;
230
+ * a payload-bearing record or epoch re-anchor releases them. Returns the IDs
231
+ * now tracked (new or already outstanding); callers should suppress repeats
232
+ * only for this admitted subset.
233
+ */
234
+ requestImagePayloads(ids: Iterable<string>): readonly string[];
235
+ /**
236
+ * Advances image recovery immediately before one decoded surface reaches its
237
+ * presenter. Payload arrival and epoch reset therefore follow delivery order
238
+ * rather than `feed`'s parse-ahead order across a multi-record chunk. The
239
+ * return value identifies payloads that answer an outstanding image request,
240
+ * so presenters can open exactly one fresh local decode generation even when
241
+ * content-addressed retransmission uses identical bytes.
242
+ */
243
+ prepareToPresentSurface(frame: WebHostSurfaceFrame): readonly string[];
244
+ resyncRequestDeliveryFailed(request: WebHostResyncRequest): void;
194
245
  private decodeLine;
246
+ private requestKeyframeResync;
247
+ private resetImageResyncForEpoch;
248
+ private clearArrivedImagePayloads;
249
+ private recoveredImagePayloadIds;
250
+ private sweepImageResyncForPresentedImages;
195
251
  private materializeDeltaFrame;
196
252
  }
197
253
  declare function encodeResizeControlMessage(columns: number, rows: number, cellWidth?: number, cellHeight?: number): Uint8Array;
198
254
  declare function encodeRenderStyleControlMessage(style: WebHostTerminalStyle): Uint8Array;
199
255
  declare function encodeCapabilitiesControlMessage(): Uint8Array;
256
+ declare function encodeResyncControlMessage(request: WebHostResyncRequest): Uint8Array;
200
257
  declare function encodeKeyInputMessage(input: WebHostKeyInput): Uint8Array;
201
258
  declare function encodePasteInputMessage(text: string): Uint8Array;
202
259
  declare function encodeMouseInputMessage(input: WebHostMouseInput): Uint8Array;
203
260
  //#endregion
204
- export { SUPPORTED_SURFACE_VERSION, WebHostAccessibilityAnnouncement, WebHostAccessibilityLiveRegion, WebHostAccessibilityNode, WebHostAccessibilityPoint, WebHostFocusPresentation, WebHostFocusSemantics, WebHostFrameDiagnosticRecord, WebHostKeyInput, WebHostMouseInput, WebHostOutputDecoder, WebHostOutputRecord, WebHostOutputSink, WebHostRuntimeIssue, WebHostScrollRegion, WebHostSurfaceCell, WebHostSurfaceDamage, WebHostSurfaceDamageRange, WebHostSurfaceDamageTextRow, WebHostSurfaceDeltaFrame, WebHostSurfaceDeltaRow, WebHostSurfaceFrame, WebHostSurfaceImage, WebHostSurfaceImageFormat, WebHostSurfaceLineStyle, WebHostSurfaceLinkRow, WebHostSurfaceLinkRun, WebHostSurfaceRect, WebHostSurfaceSize, WebHostSurfaceStyle, encodeCapabilitiesControlMessage, encodeKeyInputMessage, encodeMouseInputMessage, encodePasteInputMessage, encodeRenderStyleControlMessage, encodeResizeControlMessage };
261
+ export { MAX_IMAGE_RECOVERY_ID_BYTES, MAX_OUTSTANDING_IMAGE_RECOVERY_IDS, SUPPORTED_SURFACE_VERSION, WebHostAccessibilityAnnouncement, WebHostAccessibilityLiveRegion, WebHostAccessibilityNode, WebHostAccessibilityPoint, WebHostFocusPresentation, WebHostFocusSemantics, WebHostFrameDiagnosticRecord, WebHostImagePayloadRequestHandler, WebHostKeyInput, WebHostMouseInput, WebHostOutputDecoder, WebHostOutputRecord, WebHostOutputSink, WebHostResyncRequest, WebHostRuntimeIssue, WebHostScrollRegion, WebHostSurfaceCell, WebHostSurfaceDamage, WebHostSurfaceDamageRange, WebHostSurfaceDamageTextRow, WebHostSurfaceDeltaFrame, WebHostSurfaceDeltaRow, WebHostSurfaceFrame, WebHostSurfaceImage, WebHostSurfaceImageFormat, WebHostSurfaceLineStyle, WebHostSurfaceLinkRow, WebHostSurfaceLinkRun, WebHostSurfaceRect, WebHostSurfaceSize, WebHostSurfaceStyle, encodeCapabilitiesControlMessage, encodeKeyInputMessage, encodeMouseInputMessage, encodePasteInputMessage, encodeRenderStyleControlMessage, encodeResizeControlMessage, encodeResyncControlMessage, isWebHostImageRecoveryId };
205
262
  //# sourceMappingURL=WebHostSurfaceTransport.d.ts.map
@@ -1,7 +1,18 @@
1
1
  import { encodeWebHostTerminalRenderStyleBase64 } from "./WebHostTerminalStyle.js";
2
2
  //#region src/WebHostSurfaceTransport.ts
3
+ /**
4
+ * Browser consumer for SwiftTUI's host wire. The normative cross-host
5
+ * contract lives upstream:
6
+ * https://github.com/SwiftTUI/swift-tui/blob/main/docs/HOST-WIRE-CONTRACT.md
7
+ */
3
8
  const recordPrefix = "";
4
9
  const textEncoder = new TextEncoder();
10
+ /** Limits wire-derived recovery state and keeps every single-ID WASI request bounded. */
11
+ const MAX_IMAGE_RECOVERY_ID_BYTES = 1024;
12
+ const MAX_OUTSTANDING_IMAGE_RECOVERY_IDS = 1024;
13
+ function isWebHostImageRecoveryId(id) {
14
+ return id.length > 0 && id.length <= 1024 && textEncoder.encode(id).byteLength <= 1024;
15
+ }
5
16
  /**
6
17
  * The newest `surface` record version this runtime understands. Unknown
7
18
  * additive object keys are ignored by design (older runtimes render newer
@@ -15,6 +26,13 @@ var WebHostOutputDecoder = class {
15
26
  textDecoder = new TextDecoder();
16
27
  bufferedText = "";
17
28
  lastSurfaceFrame;
29
+ lastEpoch;
30
+ lastGen;
31
+ lastPresentedEpoch;
32
+ keyframeResyncOutstanding = false;
33
+ keyframeResyncPending = false;
34
+ imageResyncOutstandingIds = /* @__PURE__ */ new Set();
35
+ imageResyncPendingIds = /* @__PURE__ */ new Set();
18
36
  feed(chunk) {
19
37
  this.bufferedText += this.textDecoder.decode(chunk, { stream: true });
20
38
  const records = [];
@@ -40,6 +58,69 @@ var WebHostOutputDecoder = class {
40
58
  this.bufferedText = "";
41
59
  return [this.decodeLine(text)];
42
60
  }
61
+ takeResyncRequest(maximumEncodedBytes) {
62
+ if (this.keyframeResyncPending) {
63
+ this.keyframeResyncPending = false;
64
+ return { scope: "keyframe" };
65
+ }
66
+ if (this.imageResyncPendingIds.size === 0) return;
67
+ const { ids, rejectedIds } = boundedImageResyncIds([...this.imageResyncPendingIds].sort(), maximumEncodedBytes);
68
+ for (const id of rejectedIds) {
69
+ this.imageResyncPendingIds.delete(id);
70
+ this.imageResyncOutstandingIds.delete(id);
71
+ }
72
+ for (const id of ids) this.imageResyncPendingIds.delete(id);
73
+ if (ids.length === 0) return;
74
+ return {
75
+ scope: "images",
76
+ ids
77
+ };
78
+ }
79
+ /**
80
+ * Adds locally unresolved image IDs to this epoch's recovery set. IDs remain
81
+ * outstanding after delivery, so repeat painter misses cannot create storms;
82
+ * a payload-bearing record or epoch re-anchor releases them. Returns the IDs
83
+ * now tracked (new or already outstanding); callers should suppress repeats
84
+ * only for this admitted subset.
85
+ */
86
+ requestImagePayloads(ids) {
87
+ const acceptedIds = /* @__PURE__ */ new Set();
88
+ for (const id of ids) {
89
+ if (!isWebHostImageRecoveryId(id)) continue;
90
+ if (this.imageResyncOutstandingIds.has(id)) {
91
+ acceptedIds.add(id);
92
+ continue;
93
+ }
94
+ if (this.imageResyncOutstandingIds.size >= 1024) continue;
95
+ this.imageResyncOutstandingIds.add(id);
96
+ this.imageResyncPendingIds.add(id);
97
+ acceptedIds.add(id);
98
+ }
99
+ return [...acceptedIds];
100
+ }
101
+ /**
102
+ * Advances image recovery immediately before one decoded surface reaches its
103
+ * presenter. Payload arrival and epoch reset therefore follow delivery order
104
+ * rather than `feed`'s parse-ahead order across a multi-record chunk. The
105
+ * return value identifies payloads that answer an outstanding image request,
106
+ * so presenters can open exactly one fresh local decode generation even when
107
+ * content-addressed retransmission uses identical bytes.
108
+ */
109
+ prepareToPresentSurface(frame) {
110
+ const recoveredImagePayloadIds = this.recoveredImagePayloadIds(frame.images);
111
+ this.resetImageResyncForEpoch(frame.epoch);
112
+ this.sweepImageResyncForPresentedImages(frame.images);
113
+ this.clearArrivedImagePayloads(frame.images);
114
+ if (frame.epoch !== void 0) this.lastPresentedEpoch = frame.epoch;
115
+ return recoveredImagePayloadIds;
116
+ }
117
+ resyncRequestDeliveryFailed(request) {
118
+ if (request.scope === "keyframe") {
119
+ if (this.keyframeResyncOutstanding) this.keyframeResyncPending = true;
120
+ return;
121
+ }
122
+ for (const id of request.ids ?? []) if (this.imageResyncOutstandingIds.has(id)) this.imageResyncPendingIds.add(id);
123
+ }
43
124
  decodeLine(line) {
44
125
  if (line.startsWith(`${recordPrefix}clipboard:`)) {
45
126
  try {
@@ -97,15 +178,36 @@ var WebHostOutputDecoder = class {
97
178
  };
98
179
  if (isWebHostSurfaceFrame(frame)) {
99
180
  this.lastSurfaceFrame = frame;
181
+ this.lastEpoch = frame.epoch;
182
+ this.lastGen = frame.gen;
183
+ this.keyframeResyncOutstanding = false;
184
+ this.keyframeResyncPending = false;
100
185
  return {
101
186
  type: "surface",
102
187
  frame
103
188
  };
104
189
  }
105
190
  if (isWebHostSurfaceDeltaFrame(frame)) {
191
+ const carriesDeliveryStamps = frame.epoch !== void 0 || frame.gen !== void 0 || frame.baselineGen !== void 0;
192
+ if (!this.lastSurfaceFrame || this.lastSurfaceFrame.width !== frame.width || this.lastSurfaceFrame.height !== frame.height) {
193
+ if (carriesDeliveryStamps) this.requestKeyframeResync();
194
+ return {
195
+ type: "surfaceDropped",
196
+ reason: "noBaseline"
197
+ };
198
+ }
199
+ if (carriesDeliveryStamps && (frame.epoch === void 0 || frame.gen === void 0 || frame.baselineGen === void 0 || frame.epoch !== this.lastEpoch || frame.baselineGen !== this.lastGen)) {
200
+ this.requestKeyframeResync();
201
+ return {
202
+ type: "surfaceDropped",
203
+ reason: "staleBaseline"
204
+ };
205
+ }
106
206
  const materialized = this.materializeDeltaFrame(frame);
107
207
  if (materialized) {
108
208
  this.lastSurfaceFrame = materialized;
209
+ this.lastEpoch = frame.epoch;
210
+ this.lastGen = frame.gen;
109
211
  return {
110
212
  type: "surface",
111
213
  frame: materialized
@@ -118,9 +220,40 @@ var WebHostOutputDecoder = class {
118
220
  text: `${line}\n`
119
221
  };
120
222
  }
223
+ requestKeyframeResync() {
224
+ if (this.keyframeResyncOutstanding) return;
225
+ this.keyframeResyncOutstanding = true;
226
+ this.keyframeResyncPending = true;
227
+ }
228
+ resetImageResyncForEpoch(epoch) {
229
+ if (epoch === void 0 || epoch === this.lastPresentedEpoch) return;
230
+ this.imageResyncOutstandingIds.clear();
231
+ this.imageResyncPendingIds.clear();
232
+ }
233
+ clearArrivedImagePayloads(images) {
234
+ for (const image of images ?? []) {
235
+ if (image.dataBase64 === void 0) continue;
236
+ this.imageResyncOutstandingIds.delete(image.id);
237
+ this.imageResyncPendingIds.delete(image.id);
238
+ }
239
+ }
240
+ recoveredImagePayloadIds(images) {
241
+ const recoveredIds = /* @__PURE__ */ new Set();
242
+ for (const image of images ?? []) if (image.dataBase64 !== void 0 && this.imageResyncOutstandingIds.has(image.id)) recoveredIds.add(image.id);
243
+ return [...recoveredIds].sort();
244
+ }
245
+ sweepImageResyncForPresentedImages(images) {
246
+ const presentedIds = /* @__PURE__ */ new Set();
247
+ for (const image of images ?? []) if (this.imageResyncOutstandingIds.has(image.id)) presentedIds.add(image.id);
248
+ for (const id of this.imageResyncOutstandingIds) {
249
+ if (presentedIds.has(id)) continue;
250
+ this.imageResyncOutstandingIds.delete(id);
251
+ this.imageResyncPendingIds.delete(id);
252
+ }
253
+ }
121
254
  materializeDeltaFrame(frame) {
122
255
  const baseline = this.lastSurfaceFrame;
123
- if (!baseline || baseline.width !== frame.width || baseline.height !== frame.height) return;
256
+ if (!baseline) return;
124
257
  const rows = baseline.rows.slice();
125
258
  for (const [row, cells] of frame.deltaRows) {
126
259
  if (!Number.isSafeInteger(row) || row < 0 || row >= frame.height) return;
@@ -128,6 +261,8 @@ var WebHostOutputDecoder = class {
128
261
  }
129
262
  return {
130
263
  version: baseline.version,
264
+ epoch: frame.epoch,
265
+ gen: frame.gen,
131
266
  sequence: frame.sequence,
132
267
  width: frame.width,
133
268
  height: frame.height,
@@ -146,6 +281,38 @@ var WebHostOutputDecoder = class {
146
281
  };
147
282
  }
148
283
  };
284
+ function boundedImageResyncIds(sortedIds, maximumEncodedBytes) {
285
+ if (maximumEncodedBytes === void 0 || !Number.isFinite(maximumEncodedBytes)) return {
286
+ ids: sortedIds,
287
+ rejectedIds: []
288
+ };
289
+ const emptyRequestBytes = encodeResyncControlMessage({
290
+ scope: "images",
291
+ ids: []
292
+ }).byteLength;
293
+ const normalizedMaximum = Math.max(0, Math.floor(maximumEncodedBytes));
294
+ const ids = [];
295
+ const rejectedIds = [];
296
+ let encodedBytes = emptyRequestBytes;
297
+ for (const id of sortedIds) {
298
+ const separatorBytes = ids.length === 0 ? 0 : 1;
299
+ const idBytes = textEncoder.encode(JSON.stringify(id)).byteLength;
300
+ if (encodedBytes + separatorBytes + idBytes > normalizedMaximum) {
301
+ if (ids.length === 0) {
302
+ rejectedIds.push(id);
303
+ continue;
304
+ }
305
+ break;
306
+ }
307
+ ids.push(id);
308
+ encodedBytes += separatorBytes + idBytes;
309
+ if (encodedBytes >= normalizedMaximum) break;
310
+ }
311
+ return {
312
+ ids,
313
+ rejectedIds
314
+ };
315
+ }
149
316
  function declaresNewerSurfaceVersion(value) {
150
317
  if (!value || typeof value !== "object") return false;
151
318
  const version = value.version;
@@ -177,6 +344,13 @@ function encodeRenderStyleControlMessage(style) {
177
344
  function encodeCapabilitiesControlMessage() {
178
345
  return textEncoder.encode(`${recordPrefix}caps:{"acceptsDeltaFrames":true}\n`);
179
346
  }
347
+ function encodeResyncControlMessage(request) {
348
+ const payload = request.scope === "keyframe" ? { scope: "keyframe" } : {
349
+ scope: "images",
350
+ ...request.ids === void 0 ? {} : { ids: request.ids }
351
+ };
352
+ return textEncoder.encode(`${recordPrefix}resync:${JSON.stringify(payload)}\n`);
353
+ }
180
354
  function encodeKeyInputMessage(input) {
181
355
  const modifiers = Math.max(0, Math.round(input.modifiers ?? 0));
182
356
  if (input.key === "character") return textEncoder.encode(`${recordPrefix}key:character:${encodeURIComponent(input.character ?? "")}:${modifiers}\n`);
@@ -208,14 +382,17 @@ function isWebHostSurfaceFrame(value) {
208
382
  function isWebHostSurfaceDeltaFrame(value) {
209
383
  if (!value || typeof value !== "object") return false;
210
384
  const frame = value;
211
- return frame.version === 3 && frame.encoding === "delta" && (frame.sequence === void 0 || Number.isSafeInteger(frame.sequence) && frame.sequence >= 0) && typeof frame.width === "number" && typeof frame.height === "number" && Array.isArray(frame.styles) && Array.isArray(frame.deltaRows) && frame.deltaRows.every(isWebHostSurfaceDeltaRow) && (frame.images === void 0 || isWebHostSurfaceImages(frame.images)) && (frame.damage === void 0 || isWebHostSurfaceDamage(frame.damage)) && (frame.accessibilityTree === void 0 || isWebHostAccessibilityNodes(frame.accessibilityTree)) && (frame.accessibilityAnnouncements === void 0 || isWebHostAccessibilityAnnouncements(frame.accessibilityAnnouncements)) && (frame.scrollRegions === void 0 || isWebHostScrollRegions(frame.scrollRegions)) && hasValidAdditiveFrameFields(frame);
385
+ return frame.version === 3 && frame.encoding === "delta" && (frame.sequence === void 0 || Number.isSafeInteger(frame.sequence) && frame.sequence >= 0) && typeof frame.width === "number" && typeof frame.height === "number" && Array.isArray(frame.styles) && Array.isArray(frame.deltaRows) && frame.deltaRows.every(isWebHostSurfaceDeltaRow) && isOptionalSafeInteger(frame.baselineGen) && (frame.images === void 0 || isWebHostSurfaceImages(frame.images)) && (frame.damage === void 0 || isWebHostSurfaceDamage(frame.damage)) && (frame.accessibilityTree === void 0 || isWebHostAccessibilityNodes(frame.accessibilityTree)) && (frame.accessibilityAnnouncements === void 0 || isWebHostAccessibilityAnnouncements(frame.accessibilityAnnouncements)) && (frame.scrollRegions === void 0 || isWebHostScrollRegions(frame.scrollRegions)) && hasValidAdditiveFrameFields(frame);
212
386
  }
213
387
  /**
214
388
  * The F19 additive fields shared by the full and delta record shapes. Absent
215
389
  * means "feature not present" — servers older than the field omit it.
216
390
  */
217
391
  function hasValidAdditiveFrameFields(frame) {
218
- return (frame.links === void 0 || isWebHostSurfaceLinks(frame.links)) && (frame.linkTargets === void 0 || isWebHostSurfaceLinkTargets(frame.linkTargets)) && (frame.focusPresentation === void 0 || isWebHostFocusPresentation(frame.focusPresentation)) && (frame.preferredGridWidth === void 0 || Number.isSafeInteger(frame.preferredGridWidth) && frame.preferredGridWidth >= 0) && (frame.preferredGridHeight === void 0 || Number.isSafeInteger(frame.preferredGridHeight) && frame.preferredGridHeight >= 0);
392
+ return isOptionalSafeInteger(frame.epoch) && isOptionalSafeInteger(frame.gen) && (frame.links === void 0 || isWebHostSurfaceLinks(frame.links)) && (frame.linkTargets === void 0 || isWebHostSurfaceLinkTargets(frame.linkTargets)) && (frame.focusPresentation === void 0 || isWebHostFocusPresentation(frame.focusPresentation)) && (frame.preferredGridWidth === void 0 || Number.isSafeInteger(frame.preferredGridWidth) && frame.preferredGridWidth >= 0) && (frame.preferredGridHeight === void 0 || Number.isSafeInteger(frame.preferredGridHeight) && frame.preferredGridHeight >= 0);
393
+ }
394
+ function isOptionalSafeInteger(value) {
395
+ return value === void 0 || Number.isSafeInteger(value);
219
396
  }
220
397
  function isWebHostSurfaceLinks(value) {
221
398
  return Array.isArray(value) && value.every(isWebHostSurfaceLinkRow);
@@ -234,7 +411,7 @@ function isWebHostSurfaceLinkTargets(value) {
234
411
  function isWebHostFocusPresentation(value) {
235
412
  if (!value || typeof value !== "object") return false;
236
413
  const presentation = value;
237
- return (presentation.focusedIdentity === void 0 || typeof presentation.focusedIdentity === "string") && (presentation.semantics === "none" || presentation.semantics === "automatic" || presentation.semantics === "activate" || presentation.semantics === "edit") && typeof presentation.prefersTextInput === "boolean" && typeof presentation.hasFocusedRegion === "boolean";
414
+ return (presentation.focusedIdentity === void 0 || typeof presentation.focusedIdentity === "string") && typeof presentation.semantics === "string" && typeof presentation.prefersTextInput === "boolean" && typeof presentation.hasFocusedRegion === "boolean";
238
415
  }
239
416
  function isWebHostSurfaceDeltaRow(value) {
240
417
  return Array.isArray(value) && value.length === 2 && Number.isSafeInteger(value[0]) && value[0] >= 0 && isWebHostSurfaceRow(value[1]);
@@ -251,7 +428,7 @@ function isWebHostAccessibilityNodes(value) {
251
428
  function isWebHostAccessibilityNode(value) {
252
429
  if (!value || typeof value !== "object") return false;
253
430
  const node = value;
254
- return typeof node.id === "string" && (node.parentId === void 0 || typeof node.parentId === "string") && isWebHostSurfaceRect(node.rect) && typeof node.role === "string" && (node.label === void 0 || typeof node.label === "string") && (node.hint === void 0 || typeof node.hint === "string") && (node.hidden === void 0 || typeof node.hidden === "boolean") && (node.liveRegion === void 0 || node.liveRegion === "off" || node.liveRegion === "polite" || node.liveRegion === "assertive") && (node.cursorAnchor === void 0 || isWebHostAccessibilityPoint(node.cursorAnchor)) && (node.isFocused === void 0 || typeof node.isFocused === "boolean");
431
+ return typeof node.id === "string" && (node.parentId === void 0 || typeof node.parentId === "string") && isWebHostSurfaceRect(node.rect) && typeof node.role === "string" && (node.label === void 0 || typeof node.label === "string") && (node.hint === void 0 || typeof node.hint === "string") && (node.hidden === void 0 || typeof node.hidden === "boolean") && (node.liveRegion === void 0 || typeof node.liveRegion === "string") && (node.cursorAnchor === void 0 || isWebHostAccessibilityPoint(node.cursorAnchor)) && (node.isFocused === void 0 || typeof node.isFocused === "boolean");
255
432
  }
256
433
  function isWebHostAccessibilityPoint(value) {
257
434
  return Array.isArray(value) && value.length === 2 && value.every((entry) => typeof entry === "number");
@@ -262,7 +439,7 @@ function isWebHostAccessibilityAnnouncements(value) {
262
439
  function isWebHostAccessibilityAnnouncement(value) {
263
440
  if (!value || typeof value !== "object") return false;
264
441
  const announcement = value;
265
- return typeof announcement.message === "string" && (announcement.politeness === "off" || announcement.politeness === "polite" || announcement.politeness === "assertive");
442
+ return typeof announcement.message === "string" && typeof announcement.politeness === "string";
266
443
  }
267
444
  function isWebHostSurfaceImages(value) {
268
445
  return Array.isArray(value) && value.every(isWebHostSurfaceImage);
@@ -284,7 +461,7 @@ function isWebHostSurfaceDamageRange(value) {
284
461
  return Array.isArray(value) && value.length === 2 && typeof value[0] === "number" && typeof value[1] === "number";
285
462
  }
286
463
  function isWebHostSurfaceImageFormat(value) {
287
- return value === "png" || value === "jpeg" || value === "gif";
464
+ return typeof value === "string";
288
465
  }
289
466
  function isWebHostScrollRegions(value) {
290
467
  return Array.isArray(value) && value.every(isWebHostScrollRegion);
@@ -301,9 +478,9 @@ function isWebHostSurfaceSize(value) {
301
478
  return Array.isArray(value) && value.length === 2 && value.every((entry) => typeof entry === "number");
302
479
  }
303
480
  function isWebHostSurfaceScalingMode(value) {
304
- return value === "stretch" || value === "fit" || value === "fill";
481
+ return typeof value === "string";
305
482
  }
306
483
  //#endregion
307
- export { SUPPORTED_SURFACE_VERSION, WebHostOutputDecoder, encodeCapabilitiesControlMessage, encodeKeyInputMessage, encodeMouseInputMessage, encodePasteInputMessage, encodeRenderStyleControlMessage, encodeResizeControlMessage };
484
+ export { MAX_IMAGE_RECOVERY_ID_BYTES, MAX_OUTSTANDING_IMAGE_RECOVERY_IDS, SUPPORTED_SURFACE_VERSION, WebHostOutputDecoder, encodeCapabilitiesControlMessage, encodeKeyInputMessage, encodeMouseInputMessage, encodePasteInputMessage, encodeRenderStyleControlMessage, encodeResizeControlMessage, encodeResyncControlMessage, isWebHostImageRecoveryId };
308
485
 
309
486
  //# sourceMappingURL=WebHostSurfaceTransport.js.map