@dp-bohrium/widget-sdk 0.0.1

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.
@@ -0,0 +1,1121 @@
1
+ import { r as MessagePortTransport, t as ProtocolEndpoint } from "./protocol-endpoint-CHWoXvKN.js";
2
+ //#region src/runtime/network.ts
3
+ const failureReasons = /* @__PURE__ */ new Set([
4
+ "unsupported",
5
+ "permission-denied",
6
+ "resource-limit",
7
+ "invalid-reference",
8
+ "timeout",
9
+ "internal-error",
10
+ "network-consent-denied",
11
+ "network-policy-denied",
12
+ "network-transport-unavailable",
13
+ "network-request-too-large",
14
+ "network-response-too-large",
15
+ "network-rate-limited",
16
+ "network-redirect-denied",
17
+ "network-credential-unavailable",
18
+ "network-scope-invalid",
19
+ "network-dns-denied",
20
+ "network-error"
21
+ ]);
22
+ function createWidgetNetworkClient(dependencies) {
23
+ return Object.freeze({ fetch(input, options) {
24
+ const state = dependencies.state();
25
+ if (state === "disposed") return Promise.reject(/* @__PURE__ */ new Error("client is disposed"));
26
+ if (state !== "ready") return Promise.reject(/* @__PURE__ */ new Error("Widget network is not ready"));
27
+ if (!dependencies.granted()) return Promise.reject(/* @__PURE__ */ new Error("Widget network capability is not granted"));
28
+ return dependencies.request({
29
+ operation: "network.fetch",
30
+ input
31
+ }, options).then(validateNetworkResult);
32
+ } });
33
+ }
34
+ function validateNetworkResult(result) {
35
+ if (!isRecord$2(result) || typeof result.ok !== "boolean") invalidResult();
36
+ if (result.ok === false) {
37
+ if (!hasExactKeys$2(result, [
38
+ "ok",
39
+ "reason",
40
+ "message",
41
+ "retryable"
42
+ ]) || !failureReasons.has(result.reason) || typeof result.message !== "string" || result.message.length === 0 || typeof result.retryable !== "boolean") invalidResult();
43
+ return result;
44
+ }
45
+ if (!hasExactKeys$2(result, ["ok", "data"]) || !isNetworkOutput(result.data)) invalidResult();
46
+ return result;
47
+ }
48
+ function isNetworkOutput(value) {
49
+ if (!hasExactKeys$2(value, [
50
+ "status",
51
+ "statusText",
52
+ "headers",
53
+ "body",
54
+ "bodyEncoding",
55
+ "finalUrl",
56
+ "redirected"
57
+ ]) || !Number.isInteger(value["status"]) || Number(value["status"]) < 100 || Number(value["status"]) > 599 || typeof value["statusText"] !== "string" || !isStringRecord(value["headers"]) || typeof value["body"] !== "string" || value["bodyEncoding"] !== "utf8" && value["bodyEncoding"] !== "base64" || typeof value["finalUrl"] !== "string" || typeof value["redirected"] !== "boolean") return false;
58
+ try {
59
+ const finalUrl = new URL(value["finalUrl"]);
60
+ return (finalUrl.protocol === "http:" || finalUrl.protocol === "https:") && !finalUrl.username && !finalUrl.password && !finalUrl.hash;
61
+ } catch {
62
+ return false;
63
+ }
64
+ }
65
+ function isRecord$2(value) {
66
+ return typeof value === "object" && value !== null && !Array.isArray(value);
67
+ }
68
+ function isStringRecord(value) {
69
+ return isRecord$2(value) && Object.values(value).every((entry) => typeof entry === "string");
70
+ }
71
+ function hasExactKeys$2(value, keys) {
72
+ return isRecord$2(value) && Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
73
+ }
74
+ function invalidResult() {
75
+ throw new Error("network protocol error: invalid result");
76
+ }
77
+ //#endregion
78
+ //#region src/runtime/client.ts
79
+ const PRESENTATION_CONFIRMATION_TIMEOUT_MS = 6e4;
80
+ const presentationSurfaces = /* @__PURE__ */ new Set([
81
+ "workspace",
82
+ "floating",
83
+ "overlay"
84
+ ]);
85
+ const capabilityFailureReasons = {
86
+ unsupported: true,
87
+ "permission-denied": true,
88
+ "resource-limit": true,
89
+ "invalid-reference": true,
90
+ timeout: true,
91
+ "internal-error": true,
92
+ "network-consent-denied": true,
93
+ "network-policy-denied": true,
94
+ "network-transport-unavailable": true,
95
+ "network-request-too-large": true,
96
+ "network-response-too-large": true,
97
+ "network-rate-limited": true,
98
+ "network-redirect-denied": true,
99
+ "network-credential-unavailable": true,
100
+ "network-scope-invalid": true,
101
+ "network-dns-denied": true,
102
+ "network-error": true
103
+ };
104
+ function isRecord$1(value) {
105
+ return typeof value === "object" && value !== null && !Array.isArray(value);
106
+ }
107
+ function property$1(value, key) {
108
+ return value[key];
109
+ }
110
+ function hasExactKeys$1(value, keys) {
111
+ const actualKeys = Object.keys(value);
112
+ return actualKeys.length === keys.length && keys.every((key) => actualKeys.includes(key));
113
+ }
114
+ function isPlainRecord(value) {
115
+ if (!isRecord$1(value)) return false;
116
+ const prototype = Object.getPrototypeOf(value);
117
+ return prototype === Object.prototype || prototype === null;
118
+ }
119
+ function hasExactDescriptorKeys(descriptors, keys) {
120
+ return Reflect.ownKeys(descriptors).length === keys.length && keys.every((key) => Object.hasOwn(descriptors, key));
121
+ }
122
+ function dataDescriptorValue(descriptor, enumerable) {
123
+ if (descriptor === void 0 || descriptor.enumerable !== enumerable || !Object.hasOwn(descriptor, "value")) return;
124
+ return { value: descriptor.value };
125
+ }
126
+ function isGrantedPresentationSurface(value) {
127
+ return value === "floating" || value === "overlay";
128
+ }
129
+ function capturePresentationSurfaces(value) {
130
+ if (!Array.isArray(value)) return void 0;
131
+ const descriptors = Object.getOwnPropertyDescriptors(value);
132
+ const length = dataDescriptorValue(descriptors["length"], false)?.value;
133
+ if (length !== 1 && length !== 2) return void 0;
134
+ if (!hasExactDescriptorKeys(descriptors, ["length", ...Array.from({ length }, (_, i) => `${i}`)])) return void 0;
135
+ const surfaces = [];
136
+ for (let index = 0; index < length; index += 1) {
137
+ const surface = dataDescriptorValue(descriptors[`${index}`], true)?.value;
138
+ if (!isGrantedPresentationSurface(surface)) return void 0;
139
+ surfaces.push(surface);
140
+ }
141
+ if (new Set(surfaces).size !== surfaces.length) return void 0;
142
+ return Object.freeze(surfaces);
143
+ }
144
+ function captureGrantedPresentationCapability(value) {
145
+ try {
146
+ if (!isPlainRecord(value)) return void 0;
147
+ const descriptors = Object.getOwnPropertyDescriptors(value);
148
+ if (!hasExactDescriptorKeys(descriptors, [
149
+ "name",
150
+ "surfaces",
151
+ "requiresHostConfirmation"
152
+ ])) return void 0;
153
+ const name = dataDescriptorValue(descriptors["name"], true)?.value;
154
+ const confirmation = dataDescriptorValue(descriptors["requiresHostConfirmation"], true)?.value;
155
+ const surfaceValue = dataDescriptorValue(descriptors["surfaces"], true)?.value;
156
+ if (name !== "ui.presentation" || confirmation !== true) return void 0;
157
+ const surfaces = capturePresentationSurfaces(surfaceValue);
158
+ if (!surfaces) return void 0;
159
+ return Object.freeze({
160
+ name,
161
+ surfaces,
162
+ requiresHostConfirmation: confirmation,
163
+ hasOverlay: surfaces.some((surface) => surface === "overlay")
164
+ });
165
+ } catch {
166
+ return;
167
+ }
168
+ }
169
+ function hasNetworkFetchGrant(capabilities) {
170
+ const limitKeys = [
171
+ "timeoutMs",
172
+ "maxRedirects",
173
+ "maxConcurrentRequests",
174
+ "callsPerMinute",
175
+ "requestHeaderBytes",
176
+ "responseHeaderBytes",
177
+ "requestBodyBytes",
178
+ "responseBodyBytes"
179
+ ];
180
+ for (const capability of capabilities) try {
181
+ if (!isPlainRecord(capability)) continue;
182
+ const descriptors = Object.getOwnPropertyDescriptors(capability);
183
+ if (!hasExactDescriptorKeys(descriptors, ["name", "limits"])) continue;
184
+ if (dataDescriptorValue(descriptors["name"], true)?.value !== "network.fetch") continue;
185
+ const limits = dataDescriptorValue(descriptors["limits"], true)?.value;
186
+ if (!isPlainRecord(limits)) continue;
187
+ const limitDescriptors = Object.getOwnPropertyDescriptors(limits);
188
+ if (!hasExactDescriptorKeys(limitDescriptors, limitKeys)) continue;
189
+ if (limitKeys.every((key) => {
190
+ const limit = dataDescriptorValue(limitDescriptors[key], true)?.value;
191
+ return Number.isInteger(limit) && Number(limit) > 0;
192
+ })) return true;
193
+ } catch {}
194
+ return false;
195
+ }
196
+ function hasOverlayPresentationGrant(capabilities) {
197
+ for (const capability of capabilities) if (captureGrantedPresentationCapability(capability)?.hasOverlay) return true;
198
+ return false;
199
+ }
200
+ function isPresentationSurface(value) {
201
+ return typeof value === "string" && presentationSurfaces.has(value);
202
+ }
203
+ function isCapabilityFailureReason(value) {
204
+ return typeof value === "string" && Object.hasOwn(capabilityFailureReasons, value);
205
+ }
206
+ function assertPresentationResult(input, result) {
207
+ const action = input.action;
208
+ if (!isRecord$1(result) || !Object.hasOwn(result, "ok") || typeof property$1(result, "ok") !== "boolean") throw new Error(`presentation protocol error: invalid result for ${action}`);
209
+ if (property$1(result, "ok") === false) {
210
+ const message = property$1(result, "message");
211
+ if (hasExactKeys$1(result, [
212
+ "ok",
213
+ "reason",
214
+ "message",
215
+ "retryable"
216
+ ]) && isCapabilityFailureReason(property$1(result, "reason")) && typeof message === "string" && message.length > 0 && typeof property$1(result, "retryable") === "boolean") return;
217
+ throw new Error(`presentation protocol error: invalid result for ${action}`);
218
+ }
219
+ if (!hasExactKeys$1(result, ["ok", "data"])) throw new Error(`presentation protocol error: invalid success result for ${action}`);
220
+ const data = property$1(result, "data");
221
+ let valid = false;
222
+ if (isRecord$1(data)) {
223
+ if (action === "close") valid = hasExactKeys$1(data, ["action"]) && property$1(data, "action") === action;
224
+ else if (action === "focus-boundary") valid = hasExactKeys$1(data, ["action", "direction"]) && property$1(data, "action") === action && property$1(data, "direction") === input.direction;
225
+ else valid = hasExactKeys$1(data, ["action", "surface"]) && property$1(data, "action") === action && isPresentationSurface(property$1(data, "surface"));
226
+ }
227
+ if (!valid) throw new Error(`presentation protocol error: invalid success result for ${action}`);
228
+ }
229
+ function requestPresentationCapability(request, capabilityRequest, options) {
230
+ return request(capabilityRequest, {
231
+ ...options,
232
+ timeoutMs: options?.timeoutMs ?? 6e4
233
+ }).then((result) => {
234
+ assertPresentationResult(capabilityRequest.input, result);
235
+ return result;
236
+ });
237
+ }
238
+ function toError$1(reason, fallback) {
239
+ return reason instanceof Error ? reason : new Error(fallback);
240
+ }
241
+ function createRuntimeError(code, phase, message, correlationId) {
242
+ return {
243
+ code,
244
+ phase,
245
+ message,
246
+ retryable: false,
247
+ correlationId
248
+ };
249
+ }
250
+ const hostMessageTypes = /* @__PURE__ */ new Set([
251
+ "init",
252
+ "render",
253
+ "update",
254
+ "capability-result",
255
+ "error",
256
+ "dispose"
257
+ ]);
258
+ function isHostToWidgetMessage(message) {
259
+ return hostMessageTypes.has(message.type);
260
+ }
261
+ var WidgetClient = class {
262
+ network;
263
+ endpoint;
264
+ widgetSdkVersion;
265
+ channelId;
266
+ handshakeTimeoutMs;
267
+ capabilityTimeoutMs;
268
+ createMessageId;
269
+ events;
270
+ state = "created";
271
+ readyPromise;
272
+ resolveReady;
273
+ rejectReady;
274
+ handshakeTimer;
275
+ instanceId;
276
+ renderRevision;
277
+ overlayPresentationGranted = false;
278
+ networkFetchGranted = false;
279
+ pending = /* @__PURE__ */ new Map();
280
+ hostApiVersion;
281
+ readySettled = false;
282
+ constructor(options, events = {}) {
283
+ this.widgetSdkVersion = options.widgetSdkVersion;
284
+ this.channelId = options.channelId;
285
+ this.hostApiVersion = options.hostApiVersion ?? 1;
286
+ this.handshakeTimeoutMs = options.handshakeTimeoutMs ?? 5e3;
287
+ this.capabilityTimeoutMs = options.capabilityTimeoutMs ?? 5e3;
288
+ this.createMessageId = options.createMessageId ?? (() => crypto.randomUUID());
289
+ this.events = events;
290
+ this.network = createWidgetNetworkClient({
291
+ state: () => this.state,
292
+ granted: () => this.networkFetchGranted,
293
+ request: (request, requestOptions) => requestOptions === void 0 ? this.requestCapability(request) : this.requestCapability(request, requestOptions)
294
+ });
295
+ this.readyPromise = new Promise((resolve, reject) => {
296
+ this.resolveReady = resolve;
297
+ this.rejectReady = reject;
298
+ });
299
+ this.endpoint = new ProtocolEndpoint({
300
+ transport: options.transport,
301
+ channelId: options.channelId,
302
+ createMessageId: this.createMessageId,
303
+ acceptsInbound: isHostToWidgetMessage,
304
+ onMessage: (message) => this.receive(message),
305
+ onError: (error) => this.handleTransportError(error)
306
+ });
307
+ this.readyPromise.catch(() => void 0);
308
+ this.handshakeTimer = setTimeout(() => this.failHandshake(), this.handshakeTimeoutMs);
309
+ if (options.initialPayload !== void 0) this.initialize(options.initialPayload);
310
+ }
311
+ get currentState() {
312
+ return this.state;
313
+ }
314
+ ready() {
315
+ return this.readyPromise;
316
+ }
317
+ resize(width, height) {
318
+ this.assertReady();
319
+ if (!Number.isFinite(width) || width < 0 || !Number.isFinite(height) || height < 0) throw new Error("resize dimensions must be finite and non-negative");
320
+ this.send({
321
+ type: "resize",
322
+ payload: {
323
+ width,
324
+ height
325
+ }
326
+ });
327
+ }
328
+ requestCapability(request, options = this.capabilityTimeoutMs) {
329
+ this.assertReady();
330
+ const timeoutMs = typeof options === "number" ? options : options.timeoutMs ?? this.capabilityTimeoutMs;
331
+ const signal = typeof options === "number" ? void 0 : options.signal;
332
+ const requestId = this.createMessageId();
333
+ if (signal?.aborted) return Promise.reject(/* @__PURE__ */ new Error(`capability request aborted: ${requestId}`));
334
+ return new Promise((resolve, reject) => {
335
+ const timer = setTimeout(() => {
336
+ const pending = this.pending.get(requestId);
337
+ if (!pending) return;
338
+ pending.removeAbortListener?.();
339
+ this.pending.delete(requestId);
340
+ reject(/* @__PURE__ */ new Error(`capability request timed out: ${requestId}`));
341
+ }, timeoutMs);
342
+ const abort = () => {
343
+ const pending = this.pending.get(requestId);
344
+ if (!pending) return;
345
+ clearTimeout(pending.timer);
346
+ pending.removeAbortListener?.();
347
+ this.pending.delete(requestId);
348
+ reject(/* @__PURE__ */ new Error(`capability request aborted: ${requestId}`));
349
+ };
350
+ const removeAbortListener = signal ? () => signal.removeEventListener("abort", abort) : void 0;
351
+ this.pending.set(requestId, {
352
+ resolve,
353
+ reject,
354
+ timer,
355
+ ...removeAbortListener ? { removeAbortListener } : {}
356
+ });
357
+ signal?.addEventListener("abort", abort, { once: true });
358
+ if (signal?.aborted) {
359
+ abort();
360
+ return;
361
+ }
362
+ this.send({
363
+ type: "capability-request",
364
+ payload: {
365
+ requestId,
366
+ request
367
+ }
368
+ });
369
+ });
370
+ }
371
+ present(surface, options) {
372
+ return requestPresentationCapability((request, requestOptions) => this.requestCapability(request, requestOptions), {
373
+ operation: "ui.presentation",
374
+ input: {
375
+ action: "present",
376
+ surface
377
+ }
378
+ }, options);
379
+ }
380
+ closePresentation(options) {
381
+ return requestPresentationCapability((request, requestOptions) => this.requestCapability(request, requestOptions), {
382
+ operation: "ui.presentation",
383
+ input: { action: "close" }
384
+ }, options);
385
+ }
386
+ changePresentationSurface(surface, options) {
387
+ return requestPresentationCapability((request, requestOptions) => this.requestCapability(request, requestOptions), {
388
+ operation: "ui.presentation",
389
+ input: {
390
+ action: "change-surface",
391
+ surface
392
+ }
393
+ }, options);
394
+ }
395
+ focusPresentationBoundary(direction, options) {
396
+ if (!this.overlayPresentationGranted) return Promise.reject(/* @__PURE__ */ new Error("Overlay presentation is not granted for focus cooperation"));
397
+ return requestPresentationCapability((request, requestOptions) => this.requestCapability(request, requestOptions), {
398
+ operation: "ui.presentation",
399
+ input: {
400
+ action: "focus-boundary",
401
+ direction
402
+ }
403
+ }, options);
404
+ }
405
+ dispose() {
406
+ this.finishDispose("client-disposed");
407
+ }
408
+ finishDispose(reason, deadlineMs) {
409
+ if (this.state === "disposed") return;
410
+ const disposedBeforeReady = this.state !== "ready";
411
+ this.state = "disposed";
412
+ if (this.handshakeTimer) clearTimeout(this.handshakeTimer);
413
+ if (disposedBeforeReady || !this.readySettled) {
414
+ this.readySettled = true;
415
+ this.rejectReady(/* @__PURE__ */ new Error(`client disposed before ready: ${reason}`));
416
+ }
417
+ this.rejectPendingCapabilities();
418
+ this.endpoint.close();
419
+ this.events.onDispose?.(reason, deadlineMs);
420
+ }
421
+ rejectPendingCapabilities() {
422
+ for (const [requestId, pending] of this.pending) {
423
+ clearTimeout(pending.timer);
424
+ pending.removeAbortListener?.();
425
+ pending.reject(/* @__PURE__ */ new Error(`client disposed: ${requestId}`));
426
+ }
427
+ this.pending.clear();
428
+ }
429
+ receive(message) {
430
+ if (this.state === "disposed") return;
431
+ if (message.type === "init") this.initialize(message.payload);
432
+ else if (message.type === "render" || message.type === "update") {
433
+ if (this.state !== "ready") return;
434
+ if (!this.acceptsRender(message.type, message.payload)) return;
435
+ const isUpdate = message.type === "update";
436
+ this.events.onRender?.(message.payload, isUpdate);
437
+ if (isUpdate) this.events.onUpdate?.(message.payload);
438
+ } else if (message.type === "capability-result") {
439
+ const pending = this.pending.get(message.payload.requestId);
440
+ if (!pending) return;
441
+ clearTimeout(pending.timer);
442
+ pending.removeAbortListener?.();
443
+ this.pending.delete(message.payload.requestId);
444
+ pending.resolve(message.payload.result);
445
+ } else if (message.type === "error") this.events.onError?.(message.payload);
446
+ else if (message.type === "dispose") this.finishDispose(message.payload.reason, message.payload.deadlineMs);
447
+ }
448
+ initialize(payload) {
449
+ if (this.state !== "created") return;
450
+ this.state = "waiting-ready";
451
+ this.instanceId = payload.instanceId;
452
+ this.overlayPresentationGranted = hasOverlayPresentationGrant(payload.capabilities);
453
+ this.networkFetchGranted = hasNetworkFetchGrant(payload.capabilities);
454
+ this.initializeAsync(payload);
455
+ }
456
+ async initializeAsync(payload) {
457
+ try {
458
+ await this.events.onInit?.(payload);
459
+ if (this.currentState !== "waiting-ready") return;
460
+ this.state = "ready";
461
+ if (this.handshakeTimer) clearTimeout(this.handshakeTimer);
462
+ this.send({
463
+ type: "ready",
464
+ payload: {
465
+ widgetSdkVersion: this.widgetSdkVersion,
466
+ hostApiVersion: this.hostApiVersion
467
+ }
468
+ });
469
+ if (!this.readySettled) {
470
+ this.readySettled = true;
471
+ this.resolveReady();
472
+ }
473
+ } catch (reason) {
474
+ if (this.state === "disposed") return;
475
+ this.failHandshake(toError$1(reason, "widget initialization failed"), "initialization-failed", "WIDGET_CRASHED");
476
+ }
477
+ }
478
+ send(message) {
479
+ this.endpoint.send(message);
480
+ }
481
+ assertUsable() {
482
+ if (this.state === "disposed") throw new Error("client is disposed");
483
+ }
484
+ acceptsRender(type, request) {
485
+ if (request.instanceId !== this.instanceId) return false;
486
+ if (type === "render") {
487
+ if (this.renderRevision !== void 0 || request.revision !== 1) return false;
488
+ } else if (this.renderRevision === void 0 || request.revision <= this.renderRevision) return false;
489
+ this.renderRevision = request.revision;
490
+ return true;
491
+ }
492
+ assertReady() {
493
+ this.assertUsable();
494
+ if (this.state !== "ready") throw new Error("client is not ready");
495
+ }
496
+ handleTransportError(_reason) {
497
+ if (this.state === "disposed") return;
498
+ const error = createRuntimeError("MESSAGE_INVALID", this.state === "ready" ? "render" : "handshake", "Host sent an invalid protocol message", this.instanceId ?? this.channelId);
499
+ this.failWithError(/* @__PURE__ */ new Error("invalid widget protocol message"), error, "invalid-message");
500
+ }
501
+ failHandshake(reason = /* @__PURE__ */ new Error("widget handshake timed out"), disposeReason = "handshake-timeout", code = "HANDSHAKE_TIMEOUT") {
502
+ if (this.readySettled || this.state === "disposed") return;
503
+ const error = createRuntimeError(code, "handshake", code === "WIDGET_CRASHED" ? "Widget initialization failed" : "Widget handshake timed out", this.instanceId ?? this.channelId);
504
+ this.failWithError(reason, error, disposeReason);
505
+ }
506
+ failWithError(reason, error, disposeReason) {
507
+ if (this.state === "disposed") return;
508
+ this.state = "disposed";
509
+ if (this.handshakeTimer) clearTimeout(this.handshakeTimer);
510
+ if (!this.readySettled) {
511
+ this.readySettled = true;
512
+ this.rejectReady(reason);
513
+ }
514
+ this.rejectPendingCapabilities();
515
+ this.endpoint.close();
516
+ try {
517
+ this.events.onError?.(error);
518
+ } catch {}
519
+ try {
520
+ this.events.onDispose?.(disposeReason);
521
+ } catch {}
522
+ }
523
+ };
524
+ //#endregion
525
+ //#region src/runtime/event-validation.ts
526
+ const NAME = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/;
527
+ const MAX_BATCH_BYTES = 256e3;
528
+ function record(value) {
529
+ return value !== null && typeof value === "object" && !Array.isArray(value);
530
+ }
531
+ function keys(value, required, optional = []) {
532
+ return required.every((key) => Object.hasOwn(value, key)) && Object.keys(value).every((key) => required.includes(key) || optional.includes(key));
533
+ }
534
+ function id(value) {
535
+ return typeof value === "string" && value.length > 0 && value.length <= 256;
536
+ }
537
+ function json(value, depth = 0, ancestors = /* @__PURE__ */ new Set()) {
538
+ if (depth > 32) return false;
539
+ if (value === null || typeof value === "string" || typeof value === "boolean") return true;
540
+ if (typeof value === "number") return Number.isFinite(value);
541
+ if (typeof value !== "object" || ancestors.has(value)) return false;
542
+ if (!Array.isArray(value) && Object.prototype.toString.call(value) !== "[object Object]") return false;
543
+ ancestors.add(value);
544
+ const ok = Object.values(value).every((item) => json(item, depth + 1, ancestors));
545
+ ancestors.delete(value);
546
+ return ok;
547
+ }
548
+ function parseWidgetEventBatch(value) {
549
+ if (!record(value) || !keys(value, [
550
+ "schemaVersion",
551
+ "topic",
552
+ "cursor",
553
+ "reset",
554
+ "hasMore",
555
+ "events"
556
+ ], ["snapshot"]) || value["schemaVersion"] !== 1 || typeof value["topic"] !== "string" || !NAME.test(value["topic"]) || !id(value["cursor"]) || typeof value["reset"] !== "boolean" || typeof value["hasMore"] !== "boolean" || !Array.isArray(value["events"]) || value["events"].length > 100 || !json(value)) throw new Error("Invalid Widget event batch");
557
+ for (const event of value["events"]) if (!record(event) || !keys(event, [
558
+ "id",
559
+ "type",
560
+ "payload"
561
+ ]) || !id(event["id"]) || typeof event["type"] !== "string" || !NAME.test(event["type"])) throw new Error("Invalid Widget event");
562
+ if (new TextEncoder().encode(JSON.stringify(value)).byteLength > MAX_BATCH_BYTES) throw new Error("Widget event batch exceeds budget");
563
+ return value;
564
+ }
565
+ function parseToolLifecycleEvent(value) {
566
+ if (!record(value) || !keys(value, [
567
+ "toolCallId",
568
+ "toolName",
569
+ "phase"
570
+ ]) || !id(value["toolCallId"]) || typeof value["toolName"] !== "string" || !/^[A-Za-z0-9_.:-]{1,128}$/.test(value["toolName"]) || ![
571
+ "started",
572
+ "completed",
573
+ "failed",
574
+ "cancelled"
575
+ ].includes(String(value["phase"]))) throw new Error("Invalid tool lifecycle event");
576
+ return value;
577
+ }
578
+ //#endregion
579
+ //#region src/runtime/events.ts
580
+ const MAX_SEEN_EVENTS = 256;
581
+ /** 游标仅在消费者处理成功后推进;取消和失败都释放本实例的读取与计时器。 */
582
+ function subscribeToWidgetEvents(options) {
583
+ const controller = new AbortController();
584
+ const seen = /* @__PURE__ */ new Set();
585
+ let cursor = options.cursor;
586
+ let timer;
587
+ let finish;
588
+ const closed = new Promise((resolve) => {
589
+ finish = resolve;
590
+ });
591
+ function unsubscribe() {
592
+ if (controller.signal.aborted) return;
593
+ controller.abort();
594
+ if (timer) clearTimeout(timer);
595
+ seen.clear();
596
+ finish();
597
+ }
598
+ function fail(reason) {
599
+ if (controller.signal.aborted) return;
600
+ unsubscribe();
601
+ try {
602
+ options.onError(reason instanceof Error ? reason : /* @__PURE__ */ new Error("Widget event subscription failed"));
603
+ } catch {}
604
+ }
605
+ async function read(grant) {
606
+ try {
607
+ const response = await options.request({
608
+ operation: "events.read",
609
+ input: {
610
+ topic: options.topic,
611
+ limit: grant.limits.pageSize,
612
+ ...cursor === void 0 ? {} : { cursor }
613
+ }
614
+ }, { signal: controller.signal });
615
+ if (controller.signal.aborted) return;
616
+ if (response !== null && typeof response === "object" && "ok" in response && response.ok === false && "reason" in response && "message" in response && typeof response.reason === "string" && typeof response.message === "string") throw new Error(`Widget event read failed (${response.reason}): ${response.message.slice(0, 256)}`);
617
+ if (response === null || typeof response !== "object" || !("ok" in response) || response.ok !== true || !("data" in response)) throw new Error("Host denied or failed the event read");
618
+ const batch = parseWidgetEventBatch(response.data);
619
+ if (batch.topic !== options.topic || batch.events.length > grant.limits.pageSize) throw new Error("Host event scope or page limit mismatch");
620
+ if (!batch.reset && batch.cursor === cursor && (batch.hasMore || batch.events.length > 0)) throw new Error("Host event cursor did not advance");
621
+ if (batch.reset) seen.clear();
622
+ const events = batch.events.filter((event) => {
623
+ if (seen.has(event.id)) return false;
624
+ seen.add(event.id);
625
+ return true;
626
+ });
627
+ await options.onBatch({
628
+ ...batch,
629
+ events
630
+ }, { signal: controller.signal });
631
+ if (controller.signal.aborted) return;
632
+ cursor = batch.cursor;
633
+ while (seen.size > MAX_SEEN_EVENTS) {
634
+ const first = seen.values().next().value;
635
+ if (first !== void 0) seen.delete(first);
636
+ }
637
+ timer = setTimeout(() => {
638
+ read(grant);
639
+ }, Math.max(grant.limits.minPollMs, Math.ceil(6e4 / grant.limits.callsPerMinute)));
640
+ } catch (error) {
641
+ fail(error);
642
+ }
643
+ }
644
+ options.grant().then((grant) => {
645
+ if (controller.signal.aborted) return;
646
+ if (!grant?.topics.includes(options.topic) || !Number.isSafeInteger(grant.limits.pageSize) || grant.limits.pageSize < 1 || grant.limits.pageSize > 100 || !Number.isSafeInteger(grant.limits.minPollMs) || grant.limits.minPollMs < 1e3 || grant.limits.minPollMs > 6e4 || !Number.isSafeInteger(grant.limits.callsPerMinute) || grant.limits.callsPerMinute < 1) throw new Error("Widget event topic is not granted");
647
+ return read(grant);
648
+ }).catch(fail);
649
+ return {
650
+ get cursor() {
651
+ return cursor;
652
+ },
653
+ closed,
654
+ unsubscribe
655
+ };
656
+ }
657
+ /** 工具 Hook 是执行事实的观察者;不得用浏览器回调替代服务端执行门禁。 */
658
+ function toolHookHandlers(hooks) {
659
+ return async (batch, context = { signal: new AbortController().signal }) => {
660
+ if (context?.signal.aborted) return;
661
+ if (batch.reset) await hooks.onReset?.(batch.snapshot, context);
662
+ for (const event of batch.events) {
663
+ if (context?.signal.aborted) return;
664
+ if (event.type !== "tool.lifecycle") continue;
665
+ const payload = parseToolLifecycleEvent(event.payload);
666
+ if (hooks.toolName && payload.toolName !== hooks.toolName || hooks.toolCallId && payload.toolCallId !== hooks.toolCallId) continue;
667
+ if (payload.phase === "started") await hooks.onToolCall?.(payload, context);
668
+ else if (payload.phase === "completed") await hooks.onToolResult?.(payload, context);
669
+ else if (payload.phase === "failed") await hooks.onToolError?.(payload, context);
670
+ else await hooks.onToolCancelled?.(payload, context);
671
+ }
672
+ };
673
+ }
674
+ /** 实例内所有订阅共用授权额度;FIFO 防止后来的订阅抢占先到的读取。 */
675
+ function createWidgetEventReadScheduler() {
676
+ const queue = [];
677
+ const lastPollByTopic = /* @__PURE__ */ new Map();
678
+ let nextReadAt = 0;
679
+ let timer;
680
+ function pump() {
681
+ if (timer !== void 0) clearTimeout(timer);
682
+ timer = void 0;
683
+ const first = queue[0];
684
+ if (!first) return;
685
+ const now = Date.now();
686
+ const previous = lastPollByTopic.get(first.topic);
687
+ const eligibleAt = Math.max(nextReadAt, previous === void 0 ? now : previous + first.grant.limits.minPollMs);
688
+ if (eligibleAt > now) {
689
+ timer = setTimeout(pump, eligibleAt - now);
690
+ return;
691
+ }
692
+ queue.shift();
693
+ nextReadAt = now + Math.ceil(6e4 / first.grant.limits.callsPerMinute);
694
+ lastPollByTopic.set(first.topic, now);
695
+ first.run();
696
+ pump();
697
+ }
698
+ return { schedule(topic, grant, signal, read) {
699
+ return new Promise((resolve, reject) => {
700
+ if (signal.aborted) {
701
+ reject(/* @__PURE__ */ new Error("Widget event read aborted"));
702
+ return;
703
+ }
704
+ if (!Number.isSafeInteger(grant.limits.callsPerMinute) || grant.limits.callsPerMinute < 1 || !Number.isSafeInteger(grant.limits.minPollMs) || grant.limits.minPollMs < 1e3 || grant.limits.minPollMs > 6e4 || !grant.topics.includes(topic)) {
705
+ reject(/* @__PURE__ */ new Error("Widget event topic is not granted"));
706
+ return;
707
+ }
708
+ const pending = {
709
+ topic,
710
+ grant,
711
+ run: () => {
712
+ signal.removeEventListener("abort", abort);
713
+ Promise.resolve().then(() => {
714
+ if (signal.aborted) throw new Error("Widget event read aborted");
715
+ return read();
716
+ }).then(resolve, reject);
717
+ }
718
+ };
719
+ const abort = () => {
720
+ const index = queue.indexOf(pending);
721
+ if (index < 0) return;
722
+ queue.splice(index, 1);
723
+ signal.removeEventListener("abort", abort);
724
+ reject(/* @__PURE__ */ new Error("Widget event read aborted"));
725
+ pump();
726
+ };
727
+ signal.addEventListener("abort", abort, { once: true });
728
+ queue.push(pending);
729
+ pump();
730
+ });
731
+ } };
732
+ }
733
+ //#endregion
734
+ //#region src/runtime/presentation-keyboard.ts
735
+ function isCrossDocumentDeparture(target, relatedTarget) {
736
+ if (relatedTarget === null) return true;
737
+ if (typeof relatedTarget !== "object") return false;
738
+ return Reflect.get(relatedTarget, "ownerDocument") !== target.document;
739
+ }
740
+ function createPresentationKeyboardAdapter(options) {
741
+ let disposed = false;
742
+ let pendingTab;
743
+ const requests = /* @__PURE__ */ new Set();
744
+ const invoke = (request) => {
745
+ if (disposed) return;
746
+ const controller = new AbortController();
747
+ requests.add(controller);
748
+ Promise.resolve().then(() => request({ signal: controller.signal })).catch(() => void 0).finally(() => requests.delete(controller));
749
+ };
750
+ const onKeyDown = (event) => {
751
+ pendingTab = void 0;
752
+ if (!event.isTrusted || event.isComposing || event.repeat) return;
753
+ if (event.key === "Escape") {
754
+ queueMicrotask(() => {
755
+ if (!event.defaultPrevented) invoke((requestOptions) => options.close(requestOptions));
756
+ });
757
+ return;
758
+ }
759
+ if (event.key === "Tab") {
760
+ const tab = {
761
+ event,
762
+ direction: event.shiftKey ? "backward" : "forward"
763
+ };
764
+ pendingTab = tab;
765
+ queueMicrotask(() => {
766
+ if (pendingTab === tab) pendingTab = void 0;
767
+ });
768
+ }
769
+ };
770
+ const onFocusOut = (event) => {
771
+ const tab = pendingTab;
772
+ pendingTab = void 0;
773
+ if (!tab || tab.event.defaultPrevented || !isCrossDocumentDeparture(options.target, event.relatedTarget)) return;
774
+ invoke((requestOptions) => options.focusBoundary(tab.direction, requestOptions));
775
+ };
776
+ options.target.addEventListener("keydown", onKeyDown);
777
+ options.target.addEventListener("focusout", onFocusOut);
778
+ return { dispose() {
779
+ if (disposed) return;
780
+ disposed = true;
781
+ pendingTab = void 0;
782
+ options.target.removeEventListener("keydown", onKeyDown);
783
+ options.target.removeEventListener("focusout", onFocusOut);
784
+ for (const controller of requests) controller.abort();
785
+ requests.clear();
786
+ } };
787
+ }
788
+ //#endregion
789
+ //#region src/runtime/bootstrap.ts
790
+ const WIDGET_BOOTSTRAP_READY = "widget-bootstrap-ready";
791
+ const WIDGET_HOST_CONNECT = "widget-host-connect";
792
+ function isRecord(value) {
793
+ return typeof value === "object" && value !== null && !Array.isArray(value);
794
+ }
795
+ function property(value, key) {
796
+ return value[key];
797
+ }
798
+ function hasExactKeys(value, keys) {
799
+ const actualKeys = Object.keys(value);
800
+ return actualKeys.length === keys.length && keys.every((key) => actualKeys.includes(key));
801
+ }
802
+ function isInitPayload(value) {
803
+ if (!isRecord(value)) return false;
804
+ const instanceId = property(value, "instanceId");
805
+ const locale = property(value, "locale");
806
+ const theme = property(value, "theme");
807
+ const capabilities = property(value, "capabilities");
808
+ const resourceBudget = property(value, "resourceBudget");
809
+ return hasExactKeys(value, [
810
+ "instanceId",
811
+ "locale",
812
+ "theme",
813
+ "capabilities",
814
+ "resourceBudget"
815
+ ]) && typeof instanceId === "string" && instanceId.length > 0 && typeof locale === "string" && locale.length > 0 && isRecord(theme) && Array.isArray(capabilities) && isRecord(resourceBudget);
816
+ }
817
+ function isWidgetHostConnectMessage(value) {
818
+ if (!isRecord(value)) return false;
819
+ const type = property(value, "type");
820
+ const protocolVersion = property(value, "protocolVersion");
821
+ const channelId = property(value, "channelId");
822
+ const initPayload = property(value, "initPayload");
823
+ return hasExactKeys(value, [
824
+ "type",
825
+ "protocolVersion",
826
+ "channelId",
827
+ "initPayload"
828
+ ]) && type === "widget-host-connect" && protocolVersion === 1 && typeof channelId === "string" && channelId.length >= 16 && isInitPayload(initPayload);
829
+ }
830
+ function defaultWindow() {
831
+ if (typeof window === "undefined") throw new Error("createWidgetClient requires a browser window");
832
+ return window;
833
+ }
834
+ function createEvents(options) {
835
+ const base = options.events;
836
+ const events = {};
837
+ if (base?.onInit || options.onInit) events.onInit = async (payload) => {
838
+ await base?.onInit?.(payload);
839
+ await options.onInit?.(payload);
840
+ };
841
+ if (base?.onRender || base?.onUpdate || options.onRender || options.onUpdate) events.onRender = (request, update) => {
842
+ base?.onRender?.(request, update);
843
+ if (update) {
844
+ options.onUpdate?.(request);
845
+ if (!options.onUpdate) options.onRender?.(request);
846
+ } else {
847
+ options.onRender?.(request);
848
+ if (!options.onRender) options.onUpdate?.(request);
849
+ }
850
+ };
851
+ if (base?.onUpdate) events.onUpdate = base.onUpdate;
852
+ if (base?.onError || options.onError) events.onError = (error) => {
853
+ base?.onError?.(error);
854
+ options.onError?.(error);
855
+ };
856
+ if (base?.onDispose || options.onDispose) events.onDispose = (reason, deadlineMs) => {
857
+ base?.onDispose?.(reason, deadlineMs);
858
+ options.onDispose?.({
859
+ reason,
860
+ ...deadlineMs === void 0 ? {} : { deadlineMs }
861
+ });
862
+ };
863
+ return events;
864
+ }
865
+ function cloneGrantedCapabilities(capabilities) {
866
+ const cloned = [];
867
+ for (const capability of capabilities) try {
868
+ cloned.push(structuredClone(capability));
869
+ } catch {}
870
+ return cloned;
871
+ }
872
+ /**
873
+ * High-level Widget-side runtime. It hides bootstrap postMessage and the
874
+ * transferred MessagePort from Widget application code.
875
+ */
876
+ var WidgetRuntimeClient = class {
877
+ network;
878
+ runtimeWindow;
879
+ options;
880
+ events;
881
+ messageListener;
882
+ readyPromise;
883
+ resolveReady;
884
+ rejectReady;
885
+ readySettled = false;
886
+ connectionTimer;
887
+ client;
888
+ state = "created";
889
+ disposed = false;
890
+ capabilities = [];
891
+ overlayPresentationGranted = false;
892
+ subscriptions = /* @__PURE__ */ new Set();
893
+ eventReads = createWidgetEventReadScheduler();
894
+ eventReadsLifetime = new AbortController();
895
+ presentationKeyboard;
896
+ constructor(options) {
897
+ this.options = options;
898
+ this.runtimeWindow = options.window ?? defaultWindow();
899
+ const userEvents = createEvents(options);
900
+ this.events = {
901
+ ...userEvents,
902
+ onInit: async (payload) => {
903
+ this.overlayPresentationGranted = hasOverlayPresentationGrant(payload.capabilities);
904
+ this.capabilities = cloneGrantedCapabilities(payload.capabilities);
905
+ await userEvents.onInit?.(payload);
906
+ },
907
+ onDispose: (reason, deadlineMs) => {
908
+ this.stopSubscriptions();
909
+ this.stopPresentationKeyboard();
910
+ userEvents.onDispose?.(reason, deadlineMs);
911
+ }
912
+ };
913
+ const network = { fetch: (input, requestOptions) => {
914
+ if (this.currentState === "disposed") return Promise.reject(/* @__PURE__ */ new Error("client is disposed"));
915
+ if (this.currentState !== "ready") return Promise.reject(/* @__PURE__ */ new Error("Widget network is not ready"));
916
+ return this.requireClient().network.fetch(input, requestOptions);
917
+ } };
918
+ this.network = Object.freeze(network);
919
+ this.readyPromise = new Promise((resolve, reject) => {
920
+ this.resolveReady = resolve;
921
+ this.rejectReady = reject;
922
+ });
923
+ this.messageListener = (event) => this.receiveConnection(event);
924
+ this.runtimeWindow.addEventListener("message", this.messageListener);
925
+ this.connectionTimer = setTimeout(() => this.failConnection(/* @__PURE__ */ new Error("widget connection timed out")), options.connectionTimeoutMs ?? options.handshakeTimeoutMs ?? 5e3);
926
+ try {
927
+ this.runtimeWindow.parent.postMessage({
928
+ type: WIDGET_BOOTSTRAP_READY,
929
+ protocolVersion: 1
930
+ }, "*");
931
+ } catch (reason) {
932
+ this.failConnection(reason instanceof Error ? reason : /* @__PURE__ */ new Error("failed to send widget bootstrap message"));
933
+ }
934
+ }
935
+ get currentState() {
936
+ return this.client?.currentState ?? this.state;
937
+ }
938
+ ready() {
939
+ return this.readyPromise;
940
+ }
941
+ resize(widthOrSize, height) {
942
+ const client = this.requireClient();
943
+ if (typeof widthOrSize === "number") {
944
+ if (height === void 0) throw new Error("height is required when resize uses numeric arguments");
945
+ client.resize(widthOrSize, height);
946
+ return;
947
+ }
948
+ client.resize(widthOrSize.width, widthOrSize.height);
949
+ }
950
+ requestCapability(request, timeoutMs) {
951
+ return this.readyPromise.then(() => {
952
+ const client = this.requireClient();
953
+ const invoke = () => timeoutMs === void 0 ? client.requestCapability(request) : client.requestCapability(request, timeoutMs);
954
+ if (request.operation !== "events.read") return invoke();
955
+ const grant = this.capabilities.find((capability) => capability.name === "events.read");
956
+ if (!grant) throw new Error("Widget event topic is not granted");
957
+ const signal = typeof timeoutMs === "object" ? timeoutMs.signal : void 0;
958
+ return this.eventReads.schedule(request.input.topic, grant, signal ? AbortSignal.any([signal, this.eventReadsLifetime.signal]) : this.eventReadsLifetime.signal, invoke);
959
+ });
960
+ }
961
+ present(surface, options) {
962
+ return requestPresentationCapability((request, requestOptions) => this.requestCapability(request, requestOptions), {
963
+ operation: "ui.presentation",
964
+ input: {
965
+ action: "present",
966
+ surface
967
+ }
968
+ }, options);
969
+ }
970
+ closePresentation(options) {
971
+ return requestPresentationCapability((request, requestOptions) => this.requestCapability(request, requestOptions), {
972
+ operation: "ui.presentation",
973
+ input: { action: "close" }
974
+ }, options);
975
+ }
976
+ changePresentationSurface(surface, options) {
977
+ return requestPresentationCapability((request, requestOptions) => this.requestCapability(request, requestOptions), {
978
+ operation: "ui.presentation",
979
+ input: {
980
+ action: "change-surface",
981
+ surface
982
+ }
983
+ }, options);
984
+ }
985
+ focusPresentationBoundary(direction, options) {
986
+ return this.readyPromise.then(() => this.requireClient().focusPresentationBoundary(direction, options));
987
+ }
988
+ subscribeEvents(options) {
989
+ const subscription = subscribeToWidgetEvents({
990
+ ...options,
991
+ grant: async () => {
992
+ await this.readyPromise;
993
+ this.requireClient();
994
+ return this.capabilities.find((grant) => grant.name === "events.read");
995
+ },
996
+ request: (request, requestOptions) => this.requestCapability(request, requestOptions)
997
+ });
998
+ this.subscriptions.add(subscription);
999
+ subscription.closed.then(() => this.subscriptions.delete(subscription));
1000
+ if (this.disposed || this.currentState === "disposed") subscription.unsubscribe();
1001
+ return subscription;
1002
+ }
1003
+ onTool(hooks, options) {
1004
+ return this.subscribeEvents({
1005
+ ...options,
1006
+ topic: "tools.lifecycle",
1007
+ onBatch: toolHookHandlers(hooks)
1008
+ });
1009
+ }
1010
+ async invokeAction(input, options) {
1011
+ await this.readyPromise;
1012
+ if (!this.capabilities.find((capability) => capability.name === "actions.invoke")?.actions.includes(input.action)) throw new Error("Widget action is not granted");
1013
+ return this.requestCapability({
1014
+ operation: "actions.invoke",
1015
+ input
1016
+ }, options);
1017
+ }
1018
+ stopSubscriptions() {
1019
+ this.eventReadsLifetime.abort();
1020
+ for (const subscription of this.subscriptions) subscription.unsubscribe();
1021
+ this.subscriptions.clear();
1022
+ }
1023
+ startPresentationKeyboard() {
1024
+ if (this.presentationKeyboard || !this.overlayPresentationGranted) return;
1025
+ const target = presentationKeyboardTarget(this.runtimeWindow);
1026
+ if (!target) return;
1027
+ this.presentationKeyboard = createPresentationKeyboardAdapter({
1028
+ target,
1029
+ close: (requestOptions) => this.closePresentation(requestOptions),
1030
+ focusBoundary: (direction, requestOptions) => this.focusPresentationBoundary(direction, requestOptions)
1031
+ });
1032
+ }
1033
+ stopPresentationKeyboard() {
1034
+ this.presentationKeyboard?.dispose();
1035
+ this.presentationKeyboard = void 0;
1036
+ }
1037
+ dispose() {
1038
+ if (this.disposed) return;
1039
+ this.disposed = true;
1040
+ this.stopSubscriptions();
1041
+ this.stopPresentationKeyboard();
1042
+ this.state = "disposed";
1043
+ this.removeConnectionListener();
1044
+ if (this.connectionTimer) clearTimeout(this.connectionTimer);
1045
+ if (this.client) {
1046
+ this.client.dispose();
1047
+ this.rejectBeforeReady(/* @__PURE__ */ new Error("client disposed"));
1048
+ } else {
1049
+ this.rejectBeforeReady(/* @__PURE__ */ new Error("client disposed"));
1050
+ this.events.onDispose?.("client-disposed");
1051
+ }
1052
+ }
1053
+ receiveConnection(event) {
1054
+ if (this.disposed || this.client) return;
1055
+ if (event.source !== this.runtimeWindow.parent) return;
1056
+ if (this.options.expectedHostOrigin !== void 0 && event.origin !== this.options.expectedHostOrigin) return;
1057
+ if (!isWidgetHostConnectMessage(event.data)) return;
1058
+ const port = event.ports?.[0];
1059
+ if (!port) return;
1060
+ this.removeConnectionListener();
1061
+ try {
1062
+ const clientOptions = {
1063
+ transport: new MessagePortTransport(port),
1064
+ channelId: event.data.channelId,
1065
+ widgetSdkVersion: this.options.widgetSdkVersion,
1066
+ initialPayload: event.data.initPayload,
1067
+ ...this.options.hostApiVersion === void 0 ? {} : { hostApiVersion: this.options.hostApiVersion },
1068
+ ...this.options.capabilityTimeoutMs === void 0 ? {} : { capabilityTimeoutMs: this.options.capabilityTimeoutMs },
1069
+ ...this.options.handshakeTimeoutMs === void 0 ? {} : { handshakeTimeoutMs: this.options.handshakeTimeoutMs },
1070
+ ...this.options.createMessageId === void 0 ? {} : { createMessageId: this.options.createMessageId }
1071
+ };
1072
+ this.client = new WidgetClient(clientOptions, this.events);
1073
+ this.state = this.client.currentState;
1074
+ this.client.ready().then(() => {
1075
+ if (this.readySettled) return;
1076
+ this.startPresentationKeyboard();
1077
+ this.readySettled = true;
1078
+ this.resolveReady();
1079
+ }).catch((reason) => this.failConnection(toError(reason)));
1080
+ } catch (reason) {
1081
+ this.failConnection(toError(reason));
1082
+ }
1083
+ }
1084
+ requireClient() {
1085
+ if (this.disposed) throw new Error("client is disposed");
1086
+ if (!this.client) throw new Error("widget host connection is not ready");
1087
+ return this.client;
1088
+ }
1089
+ removeConnectionListener() {
1090
+ this.runtimeWindow.removeEventListener("message", this.messageListener);
1091
+ if (this.connectionTimer) clearTimeout(this.connectionTimer);
1092
+ }
1093
+ failConnection(reason) {
1094
+ if (this.disposed || this.readySettled) return;
1095
+ this.disposed = true;
1096
+ this.stopSubscriptions();
1097
+ this.stopPresentationKeyboard();
1098
+ this.state = "disposed";
1099
+ this.removeConnectionListener();
1100
+ this.client?.dispose();
1101
+ this.rejectBeforeReady(reason);
1102
+ }
1103
+ rejectBeforeReady(reason) {
1104
+ if (this.readySettled) return;
1105
+ this.readySettled = true;
1106
+ this.rejectReady(reason);
1107
+ }
1108
+ };
1109
+ function toError(reason) {
1110
+ return reason instanceof Error ? reason : /* @__PURE__ */ new Error("widget runtime failed");
1111
+ }
1112
+ function presentationKeyboardTarget(runtimeWindow) {
1113
+ const candidate = runtimeWindow;
1114
+ if (candidate.document === void 0 || typeof candidate.addEventListener !== "function" || typeof candidate.removeEventListener !== "function") return void 0;
1115
+ return candidate;
1116
+ }
1117
+ function createWidgetClient(options) {
1118
+ return new WidgetRuntimeClient(options);
1119
+ }
1120
+ //#endregion
1121
+ export { isWidgetHostConnectMessage as a, createWidgetClient as i, WIDGET_HOST_CONNECT as n, PRESENTATION_CONFIRMATION_TIMEOUT_MS as o, WidgetRuntimeClient as r, WidgetClient as s, WIDGET_BOOTSTRAP_READY as t };