@uengage.io/platform-sdk 0.14.0 → 1.0.0

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,450 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.connect = connect;
4
+ exports.jitteredBackoff = jitteredBackoff;
5
+ const envelope_1 = require("./envelope");
6
+ const endpoints_1 = require("./endpoints");
7
+ const protocol_1 = require("./protocol");
8
+ const WS_OPEN = 1;
9
+ const BASE_RECONNECT_DELAY_MS = 500;
10
+ const DEFAULT_MAX_RECONNECT_DELAY_MS = 30_000;
11
+ const DEFAULT_KEEPALIVE_TIMEOUT_MS = 60_000;
12
+ /**
13
+ * Grace on top of the server's advertised keepalive interval before we
14
+ * call the connection dead. A `ka` is due every `connectionTimeoutMs`;
15
+ * waiting 1.5× absorbs normal jitter without letting a black-holed
16
+ * socket sit there looking healthy.
17
+ */
18
+ const KEEPALIVE_GRACE = 1.5;
19
+ /**
20
+ * How long a socket may sit between "we started opening it" and
21
+ * `connection_ack` before we give up on it.
22
+ *
23
+ * The keepalive watchdog can only start once a frame has arrived, so it
24
+ * cannot cover the handshake itself. A socket whose TCP/WS open succeeds
25
+ * but which then goes silent - black-holed connection, NAT mapping
26
+ * dropped right after open, radio dozing - would otherwise never fire
27
+ * `onclose`, never arm the keepalive, and leave `connecting` true so
28
+ * `ensureSocket()` short-circuits forever. Every subscription would be
29
+ * silently dead with nothing surfaced.
30
+ *
31
+ * It covers the token step too, which is why it is armed before
32
+ * `getToken()` is awaited rather than after the socket exists: an
33
+ * app-supplied token refresh is usually a `fetch`, and a `fetch` that never
34
+ * settles produces the same permanent wedge without ever creating a socket.
35
+ */
36
+ const CONNECT_DEADLINE_MS = 15_000;
37
+ function connect(options) {
38
+ const endpoints = resolveEndpoints(options);
39
+ const WebSocketImpl = options.webSocketImpl ?? globalWebSocket();
40
+ const fetchFn = options.fetchFn ?? globalThis.fetch;
41
+ const maxReconnectDelayMs = options.maxReconnectDelayMs ?? DEFAULT_MAX_RECONNECT_DELAY_MS;
42
+ const subscriptions = new Map();
43
+ let socket;
44
+ let acked = false;
45
+ let connecting = false;
46
+ let closedByCaller = false;
47
+ let reconnectAttempt = 0;
48
+ let reconnectTimer;
49
+ let keepaliveTimer;
50
+ let connectTimer;
51
+ let keepaliveTimeoutMs = DEFAULT_KEEPALIVE_TIMEOUT_MS;
52
+ /**
53
+ * Bumped for every connect attempt, and again when the connect deadline
54
+ * abandons one. `ensureSocket`'s async body checks it after every `await`
55
+ * so a slow `getToken()` that settles late cannot adopt its socket over a
56
+ * newer attempt's.
57
+ */
58
+ let connectGeneration = 0;
59
+ // ─── Socket lifecycle ────────────────────────────────────────────────
60
+ function ensureSocket() {
61
+ if (closedByCaller || connecting || socket)
62
+ return;
63
+ connecting = true;
64
+ const generation = ++connectGeneration;
65
+ // Armed BEFORE the token await, not after the socket exists. `getToken()`
66
+ // is app-supplied and the normal implementation is a `fetch`, which can
67
+ // hang indefinitely across a network transition. If it never settles and
68
+ // the deadline were armed later, `connecting` would stay true with no
69
+ // socket and no timer, `scheduleReconnect()` would never run, and every
70
+ // subsequent `subscribe()` would short-circuit on `connecting` - the
71
+ // client dead for the page's lifetime with nothing on `onError`.
72
+ armConnectDeadline(generation);
73
+ void (async () => {
74
+ let ws;
75
+ try {
76
+ const token = await options.getToken();
77
+ // The deadline may have abandoned this attempt while the token was
78
+ // pending. Bail before building a socket nobody will own.
79
+ if (closedByCaller || generation !== connectGeneration)
80
+ return;
81
+ const header = (0, protocol_1.authHeader)(endpoints.httpDomain, token);
82
+ ws = new WebSocketImpl(`wss://${endpoints.realtimeDomain}/event/realtime`, [
83
+ protocol_1.EVENT_WS_SUBPROTOCOL,
84
+ (0, protocol_1.authSubprotocol)(header),
85
+ ]);
86
+ }
87
+ catch (err) {
88
+ if (generation !== connectGeneration)
89
+ return;
90
+ connecting = false;
91
+ clearConnectDeadline();
92
+ broadcastError(toError(err, 'realtime: failed to open connection'));
93
+ scheduleReconnect();
94
+ return;
95
+ }
96
+ if (closedByCaller || generation !== connectGeneration) {
97
+ // Superseded between the token resolving and the socket opening.
98
+ // Close the orphan rather than leaking it.
99
+ try {
100
+ ws.close(1000, 'superseded');
101
+ }
102
+ catch {
103
+ // Nothing useful to do; it was never adopted.
104
+ }
105
+ return;
106
+ }
107
+ socket = ws;
108
+ ws.onopen = () => {
109
+ ws.send((0, protocol_1.connectionInitFrame)());
110
+ };
111
+ ws.onmessage = (ev) => {
112
+ armKeepalive();
113
+ handleFrame(typeof ev.data === 'string' ? ev.data : String(ev.data));
114
+ };
115
+ ws.onerror = () => {
116
+ // `onclose` always follows; reconnect is driven from there so the
117
+ // two paths can't both schedule a retry.
118
+ };
119
+ ws.onclose = () => {
120
+ teardownSocket();
121
+ if (!closedByCaller)
122
+ scheduleReconnect();
123
+ };
124
+ })();
125
+ }
126
+ function teardownSocket() {
127
+ if (socket) {
128
+ socket.onopen = null;
129
+ socket.onmessage = null;
130
+ socket.onerror = null;
131
+ socket.onclose = null;
132
+ }
133
+ socket = undefined;
134
+ connecting = false;
135
+ acked = false;
136
+ clearKeepalive();
137
+ clearConnectDeadline();
138
+ for (const record of subscriptions.values())
139
+ record.sent = false;
140
+ }
141
+ /**
142
+ * Fail a handshake that never completes. Cleared by `connection_ack`,
143
+ * after which the keepalive watchdog takes over.
144
+ */
145
+ function armConnectDeadline(generation) {
146
+ clearConnectDeadline();
147
+ connectTimer = setTimeout(() => {
148
+ connectTimer = undefined;
149
+ if (generation !== connectGeneration)
150
+ return;
151
+ // Abandon this attempt by retiring its generation. A `getToken()` still
152
+ // pending when this fires can then no longer install its socket, which
153
+ // would otherwise land un-deadlined and un-acked on top of the fresh
154
+ // attempt scheduled below.
155
+ connectGeneration++;
156
+ const stalled = socket;
157
+ teardownSocket();
158
+ broadcastError(new Error('realtime: connection timed out before connection_ack'));
159
+ try {
160
+ stalled?.close(4001, 'connect timeout');
161
+ }
162
+ catch {
163
+ // Already gone; the reconnect below is what matters.
164
+ }
165
+ if (!closedByCaller)
166
+ scheduleReconnect();
167
+ }, CONNECT_DEADLINE_MS);
168
+ unref(connectTimer);
169
+ }
170
+ function clearConnectDeadline() {
171
+ if (connectTimer)
172
+ clearTimeout(connectTimer);
173
+ connectTimer = undefined;
174
+ }
175
+ function scheduleReconnect() {
176
+ if (closedByCaller || reconnectTimer)
177
+ return;
178
+ if (subscriptions.size === 0)
179
+ return;
180
+ const delay = jitteredBackoff(reconnectAttempt, maxReconnectDelayMs);
181
+ reconnectAttempt++;
182
+ reconnectTimer = setTimeout(() => {
183
+ reconnectTimer = undefined;
184
+ ensureSocket();
185
+ }, delay);
186
+ unref(reconnectTimer);
187
+ }
188
+ function armKeepalive() {
189
+ clearKeepalive();
190
+ keepaliveTimer = setTimeout(() => {
191
+ // No frame — not even a `ka` — within the grace window. The socket
192
+ // may look open while delivering nothing (backgrounded tab, dozing
193
+ // radio, silently dropped NAT mapping); force it closed so the
194
+ // normal reconnect path runs.
195
+ const dead = socket;
196
+ teardownSocket();
197
+ try {
198
+ dead?.close(4000, 'keepalive timeout');
199
+ }
200
+ catch {
201
+ // Already gone; the reconnect below is what matters.
202
+ }
203
+ if (!closedByCaller)
204
+ scheduleReconnect();
205
+ }, keepaliveTimeoutMs * KEEPALIVE_GRACE);
206
+ unref(keepaliveTimer);
207
+ }
208
+ function clearKeepalive() {
209
+ if (keepaliveTimer)
210
+ clearTimeout(keepaliveTimer);
211
+ keepaliveTimer = undefined;
212
+ }
213
+ // ─── Frame handling ──────────────────────────────────────────────────
214
+ function handleFrame(raw) {
215
+ const frame = (0, protocol_1.parseServerFrame)(raw);
216
+ if (!frame)
217
+ return;
218
+ switch (frame.type) {
219
+ case 'connection_ack': {
220
+ acked = true;
221
+ connecting = false;
222
+ reconnectAttempt = 0;
223
+ clearConnectDeadline();
224
+ const advertised = frame.connectionTimeoutMs;
225
+ if (typeof advertised === 'number' && advertised > 0)
226
+ keepaliveTimeoutMs = advertised;
227
+ armKeepalive();
228
+ for (const record of subscriptions.values())
229
+ void openSubscription(record);
230
+ return;
231
+ }
232
+ case 'ka':
233
+ return;
234
+ case 'subscribe_success': {
235
+ return;
236
+ }
237
+ case 'data': {
238
+ const dataFrame = frame;
239
+ const record = subscriptions.get(dataFrame.id);
240
+ if (!record || record.closed)
241
+ return;
242
+ const payload = (0, protocol_1.parseDataFrameEvent)(dataFrame);
243
+ const event = (0, envelope_1.asRealtimeEvent)(payload);
244
+ if (!event) {
245
+ reportError(record, new Error(`realtime: dropped malformed event on ${record.channel}`));
246
+ return;
247
+ }
248
+ deliver(record, event, 'live');
249
+ return;
250
+ }
251
+ case 'subscribe_error':
252
+ case 'connection_error':
253
+ case 'publish_error': {
254
+ const errorFrame = frame;
255
+ const message = `realtime: ${(0, protocol_1.errorFrameMessage)(errorFrame)}`;
256
+ if (errorFrame.id) {
257
+ const record = subscriptions.get(errorFrame.id);
258
+ if (record) {
259
+ reportError(record, new Error(message));
260
+ return;
261
+ }
262
+ }
263
+ broadcastError(new Error(message));
264
+ return;
265
+ }
266
+ default:
267
+ return;
268
+ }
269
+ }
270
+ function deliver(record, event, origin) {
271
+ if (record.closed)
272
+ return;
273
+ if (!(0, envelope_1.isNewer)(event.id, record.lastEventId))
274
+ return;
275
+ record.lastEventId = event.id;
276
+ try {
277
+ record.options.onUpdate(event, { channel: record.channel, origin });
278
+ }
279
+ catch (err) {
280
+ reportError(record, toError(err, 'realtime: onUpdate threw'));
281
+ }
282
+ }
283
+ function reportError(record, error) {
284
+ if (!record.options.onError)
285
+ return;
286
+ try {
287
+ record.options.onError(error);
288
+ }
289
+ catch {
290
+ // A throwing error handler must not take down the socket.
291
+ }
292
+ }
293
+ function broadcastError(error) {
294
+ for (const record of subscriptions.values())
295
+ reportError(record, error);
296
+ }
297
+ // ─── Subscriptions ───────────────────────────────────────────────────
298
+ /**
299
+ * Open (or re-open) one subscription: send the subscribe frame first,
300
+ * then fetch the channel's last value.
301
+ *
302
+ * Order matters. AppSync Events has no retention, so anything published
303
+ * between "we asked for the snapshot" and "the stream is live" would be
304
+ * lost forever. Subscribing first means the worst case is a duplicate,
305
+ * which the ULID guard in `deliver` absorbs.
306
+ */
307
+ async function openSubscription(record) {
308
+ if (record.closed || !socket || !acked || record.sent)
309
+ return;
310
+ let token;
311
+ try {
312
+ token = await options.getToken();
313
+ }
314
+ catch (err) {
315
+ reportError(record, toError(err, 'realtime: getToken failed'));
316
+ return;
317
+ }
318
+ if (record.closed || !socket || socket.readyState !== WS_OPEN)
319
+ return;
320
+ record.sent = true;
321
+ try {
322
+ socket.send((0, protocol_1.subscribeFrame)(record.id, record.channel, (0, protocol_1.authHeader)(endpoints.httpDomain, token)));
323
+ }
324
+ catch (err) {
325
+ record.sent = false;
326
+ reportError(record, toError(err, 'realtime: subscribe frame failed to send'));
327
+ return;
328
+ }
329
+ await hydrate(record, token);
330
+ }
331
+ async function hydrate(record, token) {
332
+ try {
333
+ const snapshot = record.options.getSnapshot
334
+ ? await record.options.getSnapshot(record.channel)
335
+ : await fetchLatest(record.channel, token);
336
+ if (snapshot)
337
+ deliver(record, snapshot, 'snapshot');
338
+ }
339
+ catch (err) {
340
+ reportError(record, toError(err, `realtime: snapshot fetch failed for ${record.channel}`));
341
+ }
342
+ }
343
+ async function fetchLatest(channel, token) {
344
+ const url = `${endpoints.apiBase}/v1/realtime/latest?channel=${encodeURIComponent(channel)}`;
345
+ const res = await fetchFn(url, { headers: { authorization: `Bearer ${token}` } });
346
+ if (res.status === 404)
347
+ return null;
348
+ if (!res.ok)
349
+ throw new Error(`snapshot fetch returned HTTP ${res.status}`);
350
+ const body = (await res.json());
351
+ return (0, envelope_1.asRealtimeEvent)(body?.event);
352
+ }
353
+ return {
354
+ subscribe(channel, subscribeOptions) {
355
+ const record = {
356
+ id: randomId(),
357
+ channel,
358
+ options: subscribeOptions,
359
+ sent: false,
360
+ closed: false,
361
+ };
362
+ subscriptions.set(record.id, record);
363
+ if (acked && socket) {
364
+ void openSubscription(record);
365
+ }
366
+ else {
367
+ ensureSocket();
368
+ }
369
+ return {
370
+ channel,
371
+ unsubscribe() {
372
+ if (record.closed)
373
+ return;
374
+ record.closed = true;
375
+ subscriptions.delete(record.id);
376
+ if (socket && socket.readyState === WS_OPEN && record.sent) {
377
+ try {
378
+ socket.send((0, protocol_1.unsubscribeFrame)(record.id));
379
+ }
380
+ catch {
381
+ // Socket is going away anyway; the server drops the
382
+ // subscription when the connection closes.
383
+ }
384
+ }
385
+ },
386
+ };
387
+ },
388
+ close() {
389
+ closedByCaller = true;
390
+ if (reconnectTimer)
391
+ clearTimeout(reconnectTimer);
392
+ reconnectTimer = undefined;
393
+ for (const record of subscriptions.values())
394
+ record.closed = true;
395
+ subscriptions.clear();
396
+ const open = socket;
397
+ teardownSocket();
398
+ try {
399
+ open?.close(1000, 'client closed');
400
+ }
401
+ catch {
402
+ // Nothing useful to do if the socket is already torn down.
403
+ }
404
+ },
405
+ };
406
+ }
407
+ // ─── Helpers ───────────────────────────────────────────────────────────
408
+ function resolveEndpoints(options) {
409
+ if (options.endpoints)
410
+ return options.endpoints;
411
+ if (!options.env) {
412
+ throw new Error('realtime.connect: pass either {env} or {endpoints}');
413
+ }
414
+ const resolved = (0, endpoints_1.endpointsFor)(options.env);
415
+ return { ...resolved, apiBase: resolved.apiBase || endpoints_1.API_BASE[options.env] };
416
+ }
417
+ function globalWebSocket() {
418
+ const ctor = globalThis.WebSocket;
419
+ if (!ctor) {
420
+ throw new Error('realtime.connect: no global WebSocket. Pass {webSocketImpl} (Node < 22, or a test double).');
421
+ }
422
+ return ctor;
423
+ }
424
+ /**
425
+ * Full-jitter exponential backoff. Full jitter (rather than
426
+ * delay ± jitter) matters here because every tab watching an order
427
+ * reconnects on the same server-side event; without it they retry in
428
+ * lockstep.
429
+ */
430
+ function jitteredBackoff(attempt, maxDelayMs) {
431
+ const ceiling = Math.min(maxDelayMs, BASE_RECONNECT_DELAY_MS * 2 ** attempt);
432
+ return Math.floor(Math.random() * ceiling);
433
+ }
434
+ function randomId() {
435
+ const c = globalThis.crypto;
436
+ if (c?.randomUUID)
437
+ return c.randomUUID();
438
+ return `sub-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
439
+ }
440
+ function toError(err, fallbackMessage) {
441
+ if (err instanceof Error)
442
+ return err;
443
+ return new Error(`${fallbackMessage}: ${String(err)}`);
444
+ }
445
+ function unref(timer) {
446
+ const t = timer;
447
+ if (typeof t.unref === 'function')
448
+ t.unref();
449
+ }
450
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29ubmVjdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9yZWFsdGltZS9jb25uZWN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBNEpBLDBCQXVXQztBQTZCRCwwQ0FHQztBQW5pQkQseUNBQTBFO0FBQzFFLDJDQUErRjtBQUMvRix5Q0FZb0I7QUFxQnBCLE1BQU0sT0FBTyxHQUFHLENBQUMsQ0FBQztBQTRGbEIsTUFBTSx1QkFBdUIsR0FBRyxHQUFHLENBQUM7QUFDcEMsTUFBTSw4QkFBOEIsR0FBRyxNQUFNLENBQUM7QUFDOUMsTUFBTSw0QkFBNEIsR0FBRyxNQUFNLENBQUM7QUFDNUM7Ozs7O0dBS0c7QUFDSCxNQUFNLGVBQWUsR0FBRyxHQUFHLENBQUM7QUFDNUI7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQkc7QUFDSCxNQUFNLG1CQUFtQixHQUFHLE1BQU0sQ0FBQztBQUVuQyxTQUFnQixPQUFPLENBQUMsT0FBK0I7SUFDckQsTUFBTSxTQUFTLEdBQUcsZ0JBQWdCLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDNUMsTUFBTSxhQUFhLEdBQUcsT0FBTyxDQUFDLGFBQWEsSUFBSSxlQUFlLEVBQUUsQ0FBQztJQUNqRSxNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsT0FBTyxJQUFLLFVBQVUsQ0FBQyxLQUFzQixDQUFDO0lBQ3RFLE1BQU0sbUJBQW1CLEdBQUcsT0FBTyxDQUFDLG1CQUFtQixJQUFJLDhCQUE4QixDQUFDO0lBRTFGLE1BQU0sYUFBYSxHQUFHLElBQUksR0FBRyxFQUE4QixDQUFDO0lBRTVELElBQUksTUFBb0MsQ0FBQztJQUN6QyxJQUFJLEtBQUssR0FBRyxLQUFLLENBQUM7SUFDbEIsSUFBSSxVQUFVLEdBQUcsS0FBSyxDQUFDO0lBQ3ZCLElBQUksY0FBYyxHQUFHLEtBQUssQ0FBQztJQUMzQixJQUFJLGdCQUFnQixHQUFHLENBQUMsQ0FBQztJQUN6QixJQUFJLGNBQXlELENBQUM7SUFDOUQsSUFBSSxjQUF5RCxD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LE1BQU0sRUFBRSxHQUFHLE9BQU8sQ0FBQyxDQUFDO0FBQzdDLENBQUM7QUFFRCxTQUFTLFFBQVE7SUFDZixNQUFNLENBQUMsR0FBSSxVQUF5RCxDQUFDLE1BQU0sQ0FBQztJQUM1RSxJQUFJLENBQUMsRUFBRSxVQUFVO1FBQUUsT0FBTyxDQUFDLENBQUMsVUFBVSxFQUFFLENBQUM7SUFDekMsT0FBTyxPQUFPLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLElBQUksSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUM7QUFDckYsQ0FBQztBQUVELFNBQVMsT0FBTyxDQUFDLEdBQVksRUFBRSxlQUF1QjtJQUNwRCxJQUFJLEdBQUcsWUFBWSxLQUFLO1FBQUUsT0FBTyxHQUFHLENBQUM7SUFDckMsT0FBTyxJQUFJLEtBQUssQ0FBQyxHQUFHLGVBQWUsS0FBSyxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0FBQ3pELENBQUM7QUFFRCxTQUFTLEtBQUssQ0FBQyxLQUFvQztJQUNqRCxNQUFNLENBQUMsR0FBRyxLQUErQixDQUFDO0lBQzFDLElBQUksT0FBTyxDQUFDLENBQUMsS0FBSyxLQUFLLFVBQVU7UUFBRSxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUM7QUFDL0MsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGFzUmVhbHRpbWVFdmVudCwgaXNOZXdlciwgdHlwZSBSZWFsdGltZUV2ZW50IH0gZnJvbSAnLi9lbnZlbG9wZSc7XG5pbXBvcnQgeyBBUElfQkFTRSwgZW5kcG9pbnRzRm9yLCB0eXBlIFJlYWx0aW1lRW5kcG9pbnRzLCB0eXBlIFJlYWx0aW1lRW52IH0gZnJvbSAnLi9lbmRwb2ludHMnO1xuaW1wb3J0IHtcbiAgRVZFTlRfV1NfU1VCUFJPVE9DT0wsXG4gIGF1dGhIZWFkZXIsXG4gIGF1dGhTdWJwcm90b2NvbCxcbiAgY29ubmVjdGlvbkluaXRGcmFtZSxcbiAgZXJyb3JGcmFtZU1lc3NhZ2UsXG4gIHBhcnNlRGF0YUZyYW1lRXZlbnQsXG4gIHBhcnNlU2VydmVyRnJhbWUsXG4gIHN1YnNjcmliZUZyYW1lLFxuICB1bnN1YnNjcmliZUZyYW1lLFxuICB0eXBlIERhdGFGcmFtZSxcbiAgdHlwZSBFcnJvckZyYW1lLFxufSBmcm9tICcuL3Byb3RvY29sJztcblxuLyoqXG4gKiBUaGUgc2xpY2Ugb2YgdGhlIFdlYlNvY2tldCBBUEkgdGhpcyBjbGllbnQgdXNlcy5cbiAqXG4gKiBEZWNsYXJlZCBzdHJ1Y3R1cmFsbHkgcmF0aGVyIHRoYW4gcmVseWluZyBvbiBhIERPTSBsaWI6IHRoZSBTREtcbiAqIGNvbXBpbGVzIGFnYWluc3QgYGxpYjogRVMyMDIyYCBvbmx5LCBhbmQgdGhpcyBrZWVwcyB0aGUgY2xpZW50XG4gKiBpbmplY3RhYmxlIHNvIHRlc3RzIGNhbiBkcml2ZSB0aGUgcHJvdG9jb2wgd2l0aCBhIGZha2Ugc29ja2V0LlxuICovXG5leHBvcnQgaW50ZXJmYWNlIE1pbmltYWxXZWJTb2NrZXQge1xuICByZWFkeVN0YXRlOiBudW1iZXI7XG4gIHNlbmQoZGF0YTogc3RyaW5nKTogdm9pZDtcbiAgY2xvc2UoY29kZT86IG51bWJlciwgcmVhc29uPzogc3RyaW5nKTogdm9pZDtcbiAgb25vcGVuOiAoKGV2OiB1bmtub3duKSA9PiB2b2lkKSB8IG51bGw7XG4gIG9ubWVzc2FnZTogKChldjogeyBkYXRhOiB1bmtub3duIH0pID0+IHZvaWQpIHwgbnVsbDtcbiAgb25lcnJvcjogKChldjogdW5rbm93bikgPT4gdm9pZCkgfCBudWxsO1xuICBvbmNsb3NlOiAoKGV2OiB7IGNvZGU/OiBudW1iZXI7IHJlYXNvbj86IHN0cmluZyB9KSA9PiB2b2lkKSB8IG51bGw7XG59XG5cbmV4cG9ydCB0eXBlIFdlYlNvY2tldEN0b3IgPSBuZXcgKHVybDogc3RyaW5nLCBwcm90b2NvbHM/OiBzdHJpbmcgfCBzdHJpbmdbXSkgPT4gTWluaW1hbFdlYlNvY2tldDtcblxuY29uc3QgV1NfT1BFTiA9IDE7XG5cbi8qKiBXaGVyZSBhbiBldmVudCBoYW5kZWQgdG8gYG9uVXBkYXRlYCBjYW1lIGZyb20uICovXG5leHBvcnQgaW50ZXJmYWNlIFVwZGF0ZU1ldGEge1xuICBjaGFubmVsOiBzdHJpbmc7XG4gIC8qKlxuICAgKiBgJ3NuYXBzaG90J2Ag4oCUIHRoZSBjaGFubmVsJ3MgbGFzdCB2YWx1ZSwgZmV0Y2hlZCBmcm9tIHRoZSBsYXN0LXZhbHVlXG4gICAqIGNhY2hlIG9uIHN1YnNjcmliZSBvciBhZnRlciBhIHJlY29ubmVjdC4gYCdsaXZlJ2Ag4oCUIGRlbGl2ZXJlZCBvdmVyXG4gICAqIHRoZSBzb2NrZXQuXG4gICAqXG4gICAqIEFwcHMgZ2VuZXJhbGx5IGRvbid0IG5lZWQgdG8gYnJhbmNoIG9uIHRoaXM7IHRoZSBTREsgaGFzIGFscmVhZHlcbiAgICogb3JkZXJlZCBhbmQgZGUtZHVwbGljYXRlZCBib3RoIHNvdXJjZXMgYnkgVUxJRC4gSXQncyB0aGVyZSBmb3JcbiAgICogdGVsZW1ldHJ5IGFuZCBmb3IgVUlzIHRoYXQgd2FudCB0byBkaXN0aW5ndWlzaCBcInJlc3RvcmVkXCIgZnJvbVxuICAgKiBcImp1c3QgaGFwcGVuZWRcIi5cbiAgICovXG4gIG9yaWdpbjogJ3NuYXBzaG90JyB8ICdsaXZlJztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTdWJzY3JpYmVPcHRpb25zIHtcbiAgLyoqXG4gICAqIEZpcmVzIG9uY2Ugd2l0aCB0aGUgY2hhbm5lbCdzIGxhdGVzdCBrbm93biB2YWx1ZSAoaWYgYW55KSwgdGhlbiBvblxuICAgKiBldmVyeSBsaXZlIGV2ZW50LiBOZXZlciBmaXJlcyB3aXRoIGFuIGV2ZW50IG9sZGVyIHRoYW4gb25lIGFscmVhZHlcbiAgICogZGVsaXZlcmVkIG9uIHRoaXMgc3Vic2NyaXB0aW9uLlxuICAgKi9cbiAgb25VcGRhdGUoZXZlbnQ6IFJlYWx0aW1lRXZlbnQsIG1ldGE6IFVwZGF0ZU1ldGEpOiB2b2lkO1xuICAvKipcbiAgICogU3Vic2NyaXB0aW9uLWxldmVsIGZhaWx1cmVzOiBhIHJlamVjdGVkIHN1YnNjcmliZSwgYSBtYWxmb3JtZWRcbiAgICogZnJhbWUsIGEgZmFpbGVkIHNuYXBzaG90IGZldGNoLiBUcmFuc2llbnQgc29ja2V0IGRyb3BzIGFyZSBoYW5kbGVkXG4gICAqIGludGVybmFsbHkgYnkgdGhlIHJlY29ubmVjdCBsb29wIGFuZCBhcmUgbm90IHN1cmZhY2VkIGhlcmUuXG4gICAqL1xuICBvbkVycm9yPyhlcnJvcjogRXJyb3IpOiB2b2lkO1xuICAvKipcbiAgICogUmVwbGFjZSB0aGUgYnVpbHQtaW4gbGFzdC12YWx1ZSBmZXRjaCBmb3IgdGhpcyBjaGFubmVsLiBSZXR1cm4gbnVsbFxuICAgKiB3aGVuIHRoZXJlIGlzIG5vIHNuYXBzaG90LiBVc2VmdWwgd2hlbiBhIGRvbWFpbiBoYXMgYSBiZXR0ZXIgc291cmNlXG4gICAqIG9mIHRydXRoIHRoYW4gdGhlIGNhY2hlIOKAlCBlLmcuIGZhbGxpbmcgYmFjayB0byB0aGUgb3JkZXItZGV0YWlscyBBUElcbiAgICogZm9yIGFuIG9yZGVyIHdob3NlIHRyYW5zaXRpb25zIHByZWRhdGUgYnVzIGluc3RydW1lbnRhdGlvbi5cbiAgICovXG4gIGdldFNuYXBzaG90PyhjaGFubmVsOiBzdHJpbmcpOiBQcm9taXNlPFJlYWx0aW1lRXZlbnQgfCBudWxsPjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTdWJzY3JpcHRpb24ge1xuICByZWFkb25seSBjaGFubmVsOiBzdHJpbmc7XG4gIHVuc3Vic2NyaWJlKCk6IHZvaWQ7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUmVhbHRpbWVDbGllbnQge1xuICAvKipcbiAgICogU3Vic2NyaWJlIHRvIGEgY2hhbm5lbC4gQ2hhbm5lbHMgYXJlIHRoZSBvbmx5IGNvbmNlcHQgaGVyZSDigJQgdGhlXG4gICAqIHJlYWx0aW1lIGxheWVyIGtub3dzIG5vdGhpbmcgYWJvdXQgb3JkZXJzOyBgL29yZGVycy97aWR9YCBpcyBhXG4gICAqIG5hbWluZyBjb252ZW50aW9uIGVuZm9yY2VkIGJ5IHRoZSBzZXJ2aWNlJ3MgbmFtZXNwYWNlIHBvbGljeSwgbm90IGJ5XG4gICAqIHRoaXMgY2xpZW50LlxuICAgKlxuICAgKiBPbiB0aGlzIGNhbGwsIGFuZCBhZ2FpbiBhZnRlciBldmVyeSByZWNvbm5lY3QsIHRoZSBTREsgb3BlbnMgdGhlXG4gICAqIHN1YnNjcmlwdGlvbiAqZmlyc3QqIGFuZCBvbmx5IHRoZW4gZmV0Y2hlcyB0aGUgY2hhbm5lbCdzIGxhc3QgdmFsdWUsXG4gICAqIHNvIG5vIGV2ZW50IGNhbiBmYWxsIGJldHdlZW4gdGhlIHNuYXBzaG90IGFuZCB0aGUgc3RyZWFtLlxuICAgKi9cbiAgc3Vic2NyaWJlKGNoYW5uZWw6IHN0cmluZywgb3B0aW9uczogU3Vic2NyaWJlT3B0aW9ucyk6IFN1YnNjcmlwdGlvbjtcbiAgLyoqIENsb3NlIHRoZSBzaGFyZWQgc29ja2V0IGFuZCBzdG9wIHJlY29ubmVjdGluZy4gKi9cbiAgY2xvc2UoKTogdm9pZDtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBSZWFsdGltZUNvbm5lY3RPcHRpb25zIHtcbiAgLyoqIFJlc29sdmVzIGVuZHBvaW50cyBmcm9tIHRoZSBidWlsdC1pbiB0YWJsZS4gTXV0dWFsbHkgZXhjbHVzaXZlIHdpdGggYGVuZHBvaW50c2AuICovXG4gIGVudj86IFJlYWx0aW1lRW52O1xuICAvKiogRXhwbGljaXQgZW5kcG9pbnRzLCBmb3IgZW52aXJvbm1lbnRzIG5vdCBpbiB0aGUgdGFibGUuICovXG4gIGVuZHBvaW50cz86IFJlYWx0aW1lRW5kcG9pbnRzO1xuICAvKipcbiAgICogU3VwcGxpZXMgdGhlIHBsYXRmb3JtIEpXVC4gQ2FsbGVkIG9uIGNvbm5lY3QsIG9uIGV2ZXJ5IChyZSlzdWJzY3JpYmUsXG4gICAqIGFuZCBhZ2FpbiBhZnRlciBhIHRva2VuLWV4cGlyeSByZWNvbm5lY3Qg4oCUIHNvIHJldHVybmluZyBhIGZyZXNoIHRva2VuXG4gICAqIGZyb20geW91ciBzZXNzaW9uIHN0b3JlIGlzIGVub3VnaCB0byBrZWVwIGEgbG9uZy1saXZlZCBjb25uZWN0aW9uXG4gICAqIGF1dGhlbnRpY2F0ZWQuXG4gICAqL1xuICBnZXRUb2tlbigpOiBzdHJpbmcgfCBQcm9taXNlPHN0cmluZz47XG4gIC8qKiBJbmplY3QgYSBXZWJTb2NrZXQgaW1wbGVtZW50YXRpb24uIERlZmF1bHRzIHRvIGBnbG9iYWxUaGlzLldlYlNvY2tldGAuICovXG4gIHdlYlNvY2tldEltcGw/OiBXZWJTb2NrZXRDdG9yO1xuICAvKiogSW5qZWN0IGEgZmV0Y2ggaW1wbGVtZW50YXRpb24gZm9yIHNuYXBzaG90IGZldGNoZXMuIERlZmF1bHRzIHRvIGBnbG9iYWxUaGlzLmZldGNoYC4gKi9cbiAgZmV0Y2hGbj86IHR5cGVvZiBmZXRjaDtcbiAgLyoqIEJhY2tvZmYgY2VpbGluZyBmb3IgcmVjb25uZWN0cy4gRGVmYXVsdCAzMF8wMDAuICovXG4gIG1heFJlY29ubmVjdERlbGF5TXM/OiBudW1iZXI7XG59XG5cbmludGVyZmFjZSBTdWJzY3JpcHRpb25SZWNvcmQge1xuICBpZDogc3RyaW5nO1xuICBjaGFubmVsOiBzdHJpbmc7XG4gIG9wdGlvbnM6IFN1YnNjcmliZU9wdGlvbnM7XG4gIC8qKiBIaWdoZXN0IGVudmVsb3BlIGlkIGRlbGl2ZXJlZCBzbyBmYXIg4oCUIHRoZSBvcmRlcmluZy9kZWR1cGUgZ3VhcmQuICovXG4gIGxhc3RFdmVudElkPzogc3RyaW5nO1xuICAvKiogQ2xlYXJlZCBvbiBzb2NrZXQgbG9zcywgc2V0IG9uY2UgdGhlIHNlcnZlciBhY2tzIHRoZSBzdWJzY3JpYmUuICovXG4gIHNlbnQ6IGJvb2xlYW47XG4gIGNsb3NlZDogYm9vbGVhbjtcbn1cblxuY29uc3QgQkFTRV9SRUNPTk5FQ1RfREVMQVlfTVMgPSA1MDA7XG5jb25zdCBERUZBVUxUX01BWF9SRUNPTk5FQ1RfREVMQVlfTVMgPSAzMF8wMDA7XG5jb25zdCBERUZBVUxUX0tFRVBBTElWRV9USU1FT1VUX01TID0gNjBfMDAwO1xuLyoqXG4gKiBHcmFjZSBvbiB0b3Agb2YgdGhlIHNlcnZlcidzIGFkdmVydGlzZWQga2VlcGFsaXZlIGludGVydmFsIGJlZm9yZSB3ZVxuICogY2FsbCB0aGUgY29ubmVjdGlvbiBkZWFkLiBBIGBrYWAgaXMgZHVlIGV2ZXJ5IGBjb25uZWN0aW9uVGltZW91dE1zYDtcbiAqIHdhaXRpbmcgMS41w5cgYWJzb3JicyBub3JtYWwgaml0dGVyIHdpdGhvdXQgbGV0dGluZyBhIGJsYWNrLWhvbGVkXG4gKiBzb2NrZXQgc2l0IHRoZXJlIGxvb2tpbmcgaGVhbHRoeS5cbiAqL1xuY29uc3QgS0VFUEFMSVZFX0dSQUNFID0gMS41O1xuLyoqXG4gKiBIb3cgbG9uZyBhIHNvY2tldCBtYXkgc2l0IGJldHdlZW4gXCJ3ZSBzdGFydGVkIG9wZW5pbmcgaXRcIiBhbmRcbiAqIGBjb25uZWN0aW9uX2Fja2AgYmVmb3JlIHdlIGdpdmUgdXAgb24gaXQuXG4gKlxuICogVGhlIGtlZXBhbGl2ZSB3YXRjaGRvZyBjYW4gb25seSBzdGFydCBvbmNlIGEgZnJhbWUgaGFzIGFycml2ZWQsIHNvIGl0XG4gKiBjYW5ub3QgY292ZXIgdGhlIGhhbmRzaGFrZSBpdHNlbGYuIEEgc29ja2V0IHdob3NlIFRDUC9XUyBvcGVuIHN1Y2NlZWRzXG4gKiBidXQgd2hpY2ggdGhlbiBnb2VzIHNpbGVudCAtIGJsYWNrLWhvbGVkIGNvbm5lY3Rpb24sIE5BVCBtYXBwaW5nXG4gKiBkcm9wcGVkIHJpZ2h0IGFmdGVyIG9wZW4sIHJhZGlvIGRvemluZyAtIHdvdWxkIG90aGVyd2lzZSBuZXZlciBmaXJlXG4gKiBgb25jbG9zZWAsIG5ldmVyIGFybSB0aGUga2VlcGFsaXZlLCBhbmQgbGVhdmUgYGNvbm5lY3RpbmdgIHRydWUgc29cbiAqIGBlbnN1cmVTb2NrZXQoKWAgc2hvcnQtY2lyY3VpdHMgZm9yZXZlci4gRXZlcnkgc3Vic2NyaXB0aW9uIHdvdWxkIGJlXG4gKiBzaWxlbnRseSBkZWFkIHdpdGggbm90aGluZyBzdXJmYWNlZC5cbiAqXG4gKiBJdCBjb3ZlcnMgdGhlIHRva2VuIHN0ZXAgdG9vLCB3aGljaCBpcyB3aHkgaXQgaXMgYXJtZWQgYmVmb3JlXG4gKiBgZ2V0VG9rZW4oKWAgaXMgYXdhaXRlZCByYXRoZXIgdGhhbiBhZnRlciB0aGUgc29ja2V0IGV4aXN0czogYW5cbiAqIGFwcC1zdXBwbGllZCB0b2tlbiByZWZyZXNoIGlzIHVzdWFsbHkgYSBgZmV0Y2hgLCBhbmQgYSBgZmV0Y2hgIHRoYXQgbmV2ZXJcbiAqIHNldHRsZXMgcHJvZHVjZXMgdGhlIHNhbWUgcGVybWFuZW50IHdlZGdlIHdpdGhvdXQgZXZlciBjcmVhdGluZyBhIHNvY2tldC5cbiAqL1xuY29uc3QgQ09OTkVDVF9ERUFETElORV9NUyA9IDE1XzAwMDtcblxuZXhwb3J0IGZ1bmN0aW9uIGNvbm5lY3Qob3B0aW9uczogUmVhbHRpbWVDb25uZWN0T3B0aW9ucyk6IFJlYWx0aW1lQ2xpZW50IHtcbiAgY29uc3QgZW5kcG9pbnRzID0gcmVzb2x2ZUVuZHBvaW50cyhvcHRpb25zKTtcbiAgY29uc3QgV2ViU29ja2V0SW1wbCA9IG9wdGlvbnMud2ViU29ja2V0SW1wbCA/PyBnbG9iYWxXZWJTb2NrZXQoKTtcbiAgY29uc3QgZmV0Y2hGbiA9IG9wdGlvbnMuZmV0Y2hGbiA/PyAoZ2xvYmFsVGhpcy5mZXRjaCBhcyB0eXBlb2YgZmV0Y2gpO1xuICBjb25zdCBtYXhSZWNvbm5lY3REZWxheU1zID0gb3B0aW9ucy5tYXhSZWNvbm5lY3REZWxheU1zID8/IERFRkFVTFRfTUFYX1JFQ09OTkVDVF9ERUxBWV9NUztcblxuICBjb25zdCBzdWJzY3JpcHRpb25zID0gbmV3IE1hcDxzdHJpbmcsIFN1YnNjcmlwdGlvblJlY29yZD4oKTtcblxuICBsZXQgc29ja2V0OiBNaW5pbWFsV2ViU29ja2V0IHwgdW5kZWZpbmVkO1xuICBsZXQgYWNrZWQgPSBmYWxzZTtcbiAgbGV0IGNvbm5lY3RpbmcgPSBmYWxzZTtcbiAgbGV0IGNsb3NlZEJ5Q2FsbGVyID0gZmFsc2U7XG4gIGxldCByZWNvbm5lY3RBdHRlbXB0ID0gMDtcbiAgbGV0IHJlY29ubmVjdFRpbWVyOiBSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0PiB8IHVuZGVmaW5lZDtcbiAgbGV0IGtlZXBhbGl2ZVRpbWVyOiBSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0PiB8IHVuZGVmaW5lZDtcbiAgbGV0IGNvbm5lY3RUaW1lcjogUmV0dXJuVHlwZTx0eXBlb2Ygc2V0VGltZW91dD4gfCB1bmRlZmluZWQ7XG4gIGxldCBrZWVwYWxpdmVUaW1lb3V0TXMgPSBERUZBVUxUX0tFRVBBTElWRV9USU1FT1VUX01TO1xuICAvKipcbiAgICogQnVtcGVkIGZvciBldmVyeSBjb25uZWN0IGF0dGVtcHQsIGFuZCBhZ2FpbiB3aGVuIHRoZSBjb25uZWN0IGRlYWRsaW5lXG4gICAqIGFiYW5kb25zIG9uZS4gYGVuc3VyZVNvY2tldGAncyBhc3luYyBib2R5IGNoZWNrcyBpdCBhZnRlciBldmVyeSBgYXdhaXRgXG4gICAqIHNvIGEgc2xvdyBgZ2V0VG9rZW4oKWAgdGhhdCBzZXR0bGVzIGxhdGUgY2Fubm90IGFkb3B0IGl0cyBzb2NrZXQgb3ZlciBhXG4gICAqIG5ld2VyIGF0dGVtcHQncy5cbiAgICovXG4gIGxldCBjb25uZWN0R2VuZXJhdGlvbiA9IDA7XG5cbiAgLy8g4pSA4pSA4pSAIFNvY2tldCBsaWZlY3ljbGUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgZnVuY3Rpb24gZW5zdXJlU29ja2V0KCk6IHZvaWQge1xuICAgIGlmIChjbG9zZWRCeUNhbGxlciB8fCBjb25uZWN0aW5nIHx8IHNvY2tldCkgcmV0dXJuO1xuICAgIGNvbm5lY3RpbmcgPSB0cnVlO1xuICAgIGNvbnN0IGdlbmVyYXRpb24gPSArK2Nvbm5lY3RHZW5lcmF0aW9uO1xuXG4gICAgLy8gQXJtZWQgQkVGT1JFIHRoZSB0b2tlbiBhd2FpdCwgbm90IGFmdGVyIHRoZSBzb2NrZXQgZXhpc3RzLiBgZ2V0VG9rZW4oKWBcbiAgICAvLyBpcyBhcHAtc3VwcGxpZWQgYW5kIHRoZSBub3JtYWwgaW1wbGVtZW50YXRpb24gaXMgYSBgZmV0Y2hgLCB3aGljaCBjYW5cbiAgICAvLyBoYW5nIGluZGVmaW5pdGVseSBhY3Jvc3MgYSBuZXR3b3JrIHRyYW5zaXRpb24uIElmIGl0IG5ldmVyIHNldHRsZXMgYW5kXG4gICAgLy8gdGhlIGRlYWRsaW5lIHdlcmUgYXJtZWQgbGF0ZXIsIGBjb25uZWN0aW5nYCB3b3VsZCBzdGF5IHRydWUgd2l0aCBub1xuICAgIC8vIHNvY2tldCBhbmQgbm8gdGltZXIsIGBzY2hlZHVsZVJlY29ubmVjdCgpYCB3b3VsZCBuZXZlciBydW4sIGFuZCBldmVyeVxuICAgIC8vIHN1YnNlcXVlbnQgYHN1YnNjcmliZSgpYCB3b3VsZCBzaG9ydC1jaXJjdWl0IG9uIGBjb25uZWN0aW5nYCAtIHRoZVxuICAgIC8vIGNsaWVudCBkZWFkIGZvciB0aGUgcGFnZSdzIGxpZmV0aW1lIHdpdGggbm90aGluZyBvbiBgb25FcnJvcmAuXG4gICAgYXJtQ29ubmVjdERlYWRsaW5lKGdlbmVyYXRpb24pO1xuXG4gICAgdm9pZCAoYXN5bmMgKCkgPT4ge1xuICAgICAgbGV0IHdzOiBNaW5pbWFsV2ViU29ja2V0O1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgdG9rZW4gPSBhd2FpdCBvcHRpb25zLmdldFRva2VuKCk7XG4gICAgICAgIC8vIFRoZSBkZWFkbGluZSBtYXkgaGF2ZSBhYmFuZG9uZWQgdGhpcyBhdHRlbXB0IHdoaWxlIHRoZSB0b2tlbiB3YXNcbiAgICAgICAgLy8gcGVuZGluZy4gQmFpbCBiZWZvcmUgYnVpbGRpbmcgYSBzb2NrZXQgbm9ib2R5IHdpbGwgb3duLlxuICAgICAgICBpZiAoY2xvc2VkQnlDYWxsZXIgfHwgZ2VuZXJhdGlvbiAhPT0gY29ubmVjdEdlbmVyYXRpb24pIHJldHVybjtcbiAgICAgICAgY29uc3QgaGVhZGVyID0gYXV0aEhlYWRlcihlbmRwb2ludHMuaHR0cERvbWFpbiwgdG9rZW4pO1xuICAgICAgICB3cyA9IG5ldyBXZWJTb2NrZXRJbXBsKGB3c3M6Ly8ke2VuZHBvaW50cy5yZWFsdGltZURvbWFpbn0vZXZlbnQvcmVhbHRpbWVgLCBbXG4gICAgICAgICAgRVZFTlRfV1NfU1VCUFJPVE9DT0wsXG4gICAgICAgICAgYXV0aFN1YnByb3RvY29sKGhlYWRlciksXG4gICAgICAgIF0pO1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGlmIChnZW5lcmF0aW9uICE9PSBjb25uZWN0R2VuZXJhdGlvbikgcmV0dXJuO1xuICAgICAgICBjb25uZWN0aW5nID0gZmFsc2U7XG4gICAgICAgIGNsZWFyQ29ubmVjdERlYWRsaW5lKCk7XG4gICAgICAgIGJyb2FkY2FzdEVycm9yKHRvRXJyb3IoZXJyLCAncmVhbHRpbWU6IGZhaWxlZCB0byBvcGVuIGNvbm5lY3Rpb24nKSk7XG4gICAgICAgIHNjaGVkdWxlUmVjb25uZWN0KCk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgaWYgKGNsb3NlZEJ5Q2FsbGVyIHx8IGdlbmVyYXRpb24gIT09IGNvbm5lY3RHZW5lcmF0aW9uKSB7XG4gICAgICAgIC8vIFN1cGVyc2VkZWQgYmV0d2VlbiB0aGUgdG9rZW4gcmVzb2x2aW5nIGFuZCB0aGUgc29ja2V0IG9wZW5pbmcuXG4gICAgICAgIC8vIENsb3NlIHRoZSBvcnBoYW4gcmF0aGVyIHRoYW4gbGVha2luZyBpdC5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICB3cy5jbG9zZSgxMDAwLCAnc3VwZXJzZWRlZCcpO1xuICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAvLyBOb3RoaW5nIHVzZWZ1bCB0byBkbzsgaXQgd2FzIG5ldmVyIGFkb3B0ZWQuXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBzb2NrZXQgPSB3cztcbiAgICAgIHdzLm9ub3BlbiA9ICgpID0+IHtcbiAgICAgICAgd3Muc2VuZChjb25uZWN0aW9uSW5pdEZyYW1lKCkpO1xuICAgICAgfTtcbiAgICAgIHdzLm9ubWVzc2FnZSA9IChldikgPT4ge1xuICAgICAgICBhcm1LZWVwYWxpdmUoKTtcbiAgICAgICAgaGFuZGxlRnJhbWUodHlwZW9mIGV2LmRhdGEgPT09ICdzdHJpbmcnID8gZXYuZGF0YSA6IFN0cmluZyhldi5kYXRhKSk7XG4gICAgICB9O1xuICAgICAgd3Mub25lcnJvciA9ICgpID0+IHtcbiAgICAgICAgLy8gYG9uY2xvc2VgIGFsd2F5cyBmb2xsb3dzOyByZWNvbm5lY3QgaXMgZHJpdmVuIGZyb20gdGhlcmUgc28gdGhlXG4gICAgICAgIC8vIHR3byBwYXRocyBjYW4ndCBib3RoIHNjaGVkdWxlIGEgcmV0cnkuXG4gICAgICB9O1xuICAgICAgd3Mub25jbG9zZSA9ICgpID0+IHtcbiAgICAgICAgdGVhcmRvd25Tb2NrZXQoKTtcbiAgICAgICAgaWYgKCFjbG9zZWRCeUNhbGxlcikgc2NoZWR1bGVSZWNvbm5lY3QoKTtcbiAgICAgIH07XG4gICAgfSkoKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHRlYXJkb3duU29ja2V0KCk6IHZvaWQge1xuICAgIGlmIChzb2NrZXQpIHtcbiAgICAgIHNvY2tldC5vbm9wZW4gPSBudWxsO1xuICAgICAgc29ja2V0Lm9ubWVzc2FnZSA9IG51bGw7XG4gICAgICBzb2NrZXQub25lcnJvciA9IG51bGw7XG4gICAgICBzb2NrZXQub25jbG9zZSA9IG51bGw7XG4gICAgfVxuICAgIHNvY2tldCA9IHVuZGVmaW5lZDtcbiAgICBjb25uZWN0aW5nID0gZmFsc2U7XG4gICAgYWNrZWQgPSBmYWxzZTtcbiAgICBjbGVhcktlZXBhbGl2ZSgpO1xuICAgIGNsZWFyQ29ubmVjdERlYWRsaW5lKCk7XG4gICAgZm9yIChjb25zdCByZWNvcmQgb2Ygc3Vic2NyaXB0aW9ucy52YWx1ZXMoKSkgcmVjb3JkLnNlbnQgPSBmYWxzZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGYWlsIGEgaGFuZHNoYWtlIHRoYXQgbmV2ZXIgY29tcGxldGVzLiBDbGVhcmVkIGJ5IGBjb25uZWN0aW9uX2Fja2AsXG4gICAqIGFmdGVyIHdoaWNoIHRoZSBrZWVwYWxpdmUgd2F0Y2hkb2cgdGFrZXMgb3Zlci5cbiAgICovXG4gIGZ1bmN0aW9uIGFybUNvbm5lY3REZWFkbGluZShnZW5lcmF0aW9uOiBudW1iZXIpOiB2b2lkIHtcbiAgICBjbGVhckNvbm5lY3REZWFkbGluZSgpO1xuICAgIGNvbm5lY3RUaW1lciA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgY29ubmVjdFRpbWVyID0gdW5kZWZpbmVkO1xuICAgICAgaWYgKGdlbmVyYXRpb24gIT09IGNvbm5lY3RHZW5lcmF0aW9uKSByZXR1cm47XG4gICAgICAvLyBBYmFuZG9uIHRoaXMgYXR0ZW1wdCBieSByZXRpcmluZyBpdHMgZ2VuZXJhdGlvbi4gQSBgZ2V0VG9rZW4oKWAgc3RpbGxcbiAgICAgIC8vIHBlbmRpbmcgd2hlbiB0aGlzIGZpcmVzIGNhbiB0aGVuIG5vIGxvbmdlciBpbnN0YWxsIGl0cyBzb2NrZXQsIHdoaWNoXG4gICAgICAvLyB3b3VsZCBvdGhlcndpc2UgbGFuZCB1bi1kZWFkbGluZWQgYW5kIHVuLWFja2VkIG9uIHRvcCBvZiB0aGUgZnJlc2hcbiAgICAgIC8vIGF0dGVtcHQgc2NoZWR1bGVkIGJlbG93LlxuICAgICAgY29ubmVjdEdlbmVyYXRpb24rKztcbiAgICAgIGNvbnN0IHN0YWxsZWQgPSBzb2NrZXQ7XG4gICAgICB0ZWFyZG93blNvY2tldCgpO1xuICAgICAgYnJvYWRjYXN0RXJyb3IobmV3IEVycm9yKCdyZWFsdGltZTogY29ubmVjdGlvbiB0aW1lZCBvdXQgYmVmb3JlIGNvbm5lY3Rpb25fYWNrJykpO1xuICAgICAgdHJ5IHtcbiAgICAgICAgc3RhbGxlZD8uY2xvc2UoNDAwMSwgJ2Nvbm5lY3QgdGltZW91dCcpO1xuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIC8vIEFscmVhZHkgZ29uZTsgdGhlIHJlY29ubmVjdCBiZWxvdyBpcyB3aGF0IG1hdHRlcnMuXG4gICAgICB9XG4gICAgICBpZiAoIWNsb3NlZEJ5Q2FsbGVyKSBzY2hlZHVsZVJlY29ubmVjdCgpO1xuICAgIH0sIENPTk5FQ1RfREVBRExJTkVfTVMpO1xuICAgIHVucmVmKGNvbm5lY3RUaW1lcik7XG4gIH1cblxuICBmdW5jdGlvbiBjbGVhckNvbm5lY3REZWFkbGluZSgpOiB2b2lkIHtcbiAgICBpZiAoY29ubmVjdFRpbWVyKSBjbGVhclRpbWVvdXQoY29ubmVjdFRpbWVyKTtcbiAgICBjb25uZWN0VGltZXIgPSB1bmRlZmluZWQ7XG4gIH1cblxuICBmdW5jdGlvbiBzY2hlZHVsZVJlY29ubmVjdCgpOiB2b2lkIHtcbiAgICBpZiAoY2xvc2VkQnlDYWxsZXIgfHwgcmVjb25uZWN0VGltZXIpIHJldHVybjtcbiAgICBpZiAoc3Vic2NyaXB0aW9ucy5zaXplID09PSAwKSByZXR1cm47XG4gICAgY29uc3QgZGVsYXkgPSBqaXR0ZXJlZEJhY2tvZmYocmVjb25uZWN0QXR0ZW1wdCwgbWF4UmVjb25uZWN0RGVsYXlNcyk7XG4gICAgcmVjb25uZWN0QXR0ZW1wdCsrO1xuICAgIHJlY29ubmVjdFRpbWVyID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICByZWNvbm5lY3RUaW1lciA9IHVuZGVmaW5lZDtcbiAgICAgIGVuc3VyZVNvY2tldCgpO1xuICAgIH0sIGRlbGF5KTtcbiAgICB1bnJlZihyZWNvbm5lY3RUaW1lcik7XG4gIH1cblxuICBmdW5jdGlvbiBhcm1LZWVwYWxpdmUoKTogdm9pZCB7XG4gICAgY2xlYXJLZWVwYWxpdmUoKTtcbiAgICBrZWVwYWxpdmVUaW1lciA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgLy8gTm8gZnJhbWUg4oCUIG5vdCBldmVuIGEgYGthYCDigJQgd2l0aGluIHRoZSBncmFjZSB3aW5kb3cuIFRoZSBzb2NrZXRcbiAgICAgIC8vIG1heSBsb29rIG9wZW4gd2hpbGUgZGVsaXZlcmluZyBub3RoaW5nIChiYWNrZ3JvdW5kZWQgdGFiLCBkb3ppbmdcbiAgICAgIC8vIHJhZGlvLCBzaWxlbnRseSBkcm9wcGVkIE5BVCBtYXBwaW5nKTsgZm9yY2UgaXQgY2xvc2VkIHNvIHRoZVxuICAgICAgLy8gbm9ybWFsIHJlY29ubmVjdCBwYXRoIHJ1bnMuXG4gICAgICBjb25zdCBkZWFkID0gc29ja2V0O1xuICAgICAgdGVhcmRvd25Tb2NrZXQoKTtcbiAgICAgIHRyeSB7XG4gICAgICAgIGRlYWQ/LmNsb3NlKDQwMDAsICdrZWVwYWxpdmUgdGltZW91dCcpO1xuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIC8vIEFscmVhZHkgZ29uZTsgdGhlIHJlY29ubmVjdCBiZWxvdyBpcyB3aGF0IG1hdHRlcnMuXG4gICAgICB9XG4gICAgICBpZiAoIWNsb3NlZEJ5Q2FsbGVyKSBzY2hlZHVsZVJlY29ubmVjdCgpO1xuICAgIH0sIGtlZXBhbGl2ZVRpbWVvdXRNcyAqIEtFRVBBTElWRV9HUkFDRSk7XG4gICAgdW5yZWYoa2VlcGFsaXZlVGltZXIpO1xuICB9XG5cbiAgZnVuY3Rpb24gY2xlYXJLZWVwYWxpdmUoKTogdm9pZCB7XG4gICAgaWYgKGtlZXBhbGl2ZVRpbWVyKSBjbGVhclRpbWVvdXQoa2VlcGFsaXZlVGltZXIpO1xuICAgIGtlZXBhbGl2ZVRpbWVyID0gdW5kZWZpbmVkO1xuICB9XG5cbiAgLy8g4pSA4pSA4pSAIEZyYW1lIGhhbmRsaW5nIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIGZ1bmN0aW9uIGhhbmRsZUZyYW1lKHJhdzogc3RyaW5nKTogdm9pZCB7XG4gICAgY29uc3QgZnJhbWUgPSBwYXJzZVNlcnZlckZyYW1lKHJhdyk7XG4gICAgaWYgKCFmcmFtZSkgcmV0dXJuO1xuXG4gICAgc3dpdGNoIChmcmFtZS50eXBlKSB7XG4gICAgICBjYXNlICdjb25uZWN0aW9uX2Fjayc6IHtcbiAgICAgICAgYWNrZWQgPSB0cnVlO1xuICAgICAgICBjb25uZWN0aW5nID0gZmFsc2U7XG4gICAgICAgIHJlY29ubmVjdEF0dGVtcHQgPSAwO1xuICAgICAgICBjbGVhckNvbm5lY3REZWFkbGluZSgpO1xuICAgICAgICBjb25zdCBhZHZlcnRpc2VkID0gKGZyYW1lIGFzIHsgY29ubmVjdGlvblRpbWVvdXRNcz86IG51bWJlciB9KS5jb25uZWN0aW9uVGltZW91dE1zO1xuICAgICAgICBpZiAodHlwZW9mIGFkdmVydGlzZWQgPT09ICdudW1iZXInICYmIGFkdmVydGlzZWQgPiAwKSBrZWVwYWxpdmVUaW1lb3V0TXMgPSBhZHZlcnRpc2VkO1xuICAgICAgICBhcm1LZWVwYWxpdmUoKTtcbiAgICAgICAgZm9yIChjb25zdCByZWNvcmQgb2Ygc3Vic2NyaXB0aW9ucy52YWx1ZXMoKSkgdm9pZCBvcGVuU3Vic2NyaXB0aW9uKHJlY29yZCk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGNhc2UgJ2thJzpcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnc3Vic2NyaWJlX3N1Y2Nlc3MnOiB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGNhc2UgJ2RhdGEnOiB7XG4gICAgICAgIGNvbnN0IGRhdGFGcmFtZSA9IGZyYW1lIGFzIERhdGFGcmFtZTtcbiAgICAgICAgY29uc3QgcmVjb3JkID0gc3Vic2NyaXB0aW9ucy5nZXQoZGF0YUZyYW1lLmlkKTtcbiAgICAgICAgaWYgKCFyZWNvcmQgfHwgcmVjb3JkLmNsb3NlZCkgcmV0dXJuO1xuICAgICAgICBjb25zdCBwYXlsb2FkID0gcGFyc2VEYXRhRnJhbWVFdmVudChkYXRhRnJhbWUpO1xuICAgICAgICBjb25zdCBldmVudCA9IGFzUmVhbHRpbWVFdmVudChwYXlsb2FkKTtcbiAgICAgICAgaWYgKCFldmVudCkge1xuICAgICAgICAgIHJlcG9ydEVycm9yKHJlY29yZCwgbmV3IEVycm9yKGByZWFsdGltZTogZHJvcHBlZCBtYWxmb3JtZWQgZXZlbnQgb24gJHtyZWNvcmQuY2hhbm5lbH1gKSk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGRlbGl2ZXIocmVjb3JkLCBldmVudCwgJ2xpdmUnKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgY2FzZSAnc3Vic2NyaWJlX2Vycm9yJzpcbiAgICAgIGNhc2UgJ2Nvbm5lY3Rpb25fZXJyb3InOlxuICAgICAgY2FzZSAncHVibGlzaF9lcnJvcic6IHtcbiAgICAgICAgY29uc3QgZXJyb3JGcmFtZSA9IGZyYW1lIGFzIEVycm9yRnJhbWU7XG4gICAgICAgIGNvbnN0IG1lc3NhZ2UgPSBgcmVhbHRpbWU6ICR7ZXJyb3JGcmFtZU1lc3NhZ2UoZXJyb3JGcmFtZSl9YDtcbiAgICAgICAgaWYgKGVycm9yRnJhbWUuaWQpIHtcbiAgICAgICAgICBjb25zdCByZWNvcmQgPSBzdWJzY3JpcHRpb25zLmdldChlcnJvckZyYW1lLmlkKTtcbiAgICAgICAgICBpZiAocmVjb3JkKSB7XG4gICAgICAgICAgICByZXBvcnRFcnJvcihyZWNvcmQsIG5ldyBFcnJvcihtZXNzYWdlKSk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGJyb2FkY2FzdEVycm9yKG5ldyBFcnJvcihtZXNzYWdlKSk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBkZWxpdmVyKHJlY29yZDogU3Vic2NyaXB0aW9uUmVjb3JkLCBldmVudDogUmVhbHRpbWVFdmVudCwgb3JpZ2luOiBVcGRhdGVNZXRhWydvcmlnaW4nXSkge1xuICAgIGlmIChyZWNvcmQuY2xvc2VkKSByZXR1cm47XG4gICAgaWYgKCFpc05ld2VyKGV2ZW50LmlkLCByZWNvcmQubGFzdEV2ZW50SWQpKSByZXR1cm47XG4gICAgcmVjb3JkLmxhc3RFdmVudElkID0gZXZlbnQuaWQ7XG4gICAgdHJ5IHtcbiAgICAgIHJlY29yZC5vcHRpb25zLm9uVXBkYXRlKGV2ZW50LCB7IGNoYW5uZWw6IHJlY29yZC5jaGFubmVsLCBvcmlnaW4gfSk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICByZXBvcnRFcnJvcihyZWNvcmQsIHRvRXJyb3IoZXJyLCAncmVhbHRpbWU6IG9uVXBkYXRlIHRocmV3JykpO1xuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIHJlcG9ydEVycm9yKHJlY29yZDogU3Vic2NyaXB0aW9uUmVjb3JkLCBlcnJvcjogRXJyb3IpOiB2b2lkIHtcbiAgICBpZiAoIXJlY29yZC5vcHRpb25zLm9uRXJyb3IpIHJldHVybjtcbiAgICB0cnkge1xuICAgICAgcmVjb3JkLm9wdGlvbnMub25FcnJvcihlcnJvcik7XG4gICAgfSBjYXRjaCB7XG4gICAgICAvLyBBIHRocm93aW5nIGVycm9yIGhhbmRsZXIgbXVzdCBub3QgdGFrZSBkb3duIHRoZSBzb2NrZXQuXG4gICAgfVxuICB9XG5cbiAgZnVuY3Rpb24gYnJvYWRjYXN0RXJyb3IoZXJyb3I6IEVycm9yKTogdm9pZCB7XG4gICAgZm9yIChjb25zdCByZWNvcmQgb2Ygc3Vic2NyaXB0aW9ucy52YWx1ZXMoKSkgcmVwb3J0RXJyb3IocmVjb3JkLCBlcnJvcik7XG4gIH1cblxuICAvLyDilIDilIDilIAgU3Vic2NyaXB0aW9ucyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICAvKipcbiAgICogT3BlbiAob3IgcmUtb3Blbikgb25lIHN1YnNjcmlwdGlvbjogc2VuZCB0aGUgc3Vic2NyaWJlIGZyYW1lIGZpcnN0LFxuICAgKiB0aGVuIGZldGNoIHRoZSBjaGFubmVsJ3MgbGFzdCB2YWx1ZS5cbiAgICpcbiAgICogT3JkZXIgbWF0dGVycy4gQXBwU3luYyBFdmVudHMgaGFzIG5vIHJldGVudGlvbiwgc28gYW55dGhpbmcgcHVibGlzaGVkXG4gICAqIGJldHdlZW4gXCJ3ZSBhc2tlZCBmb3IgdGhlIHNuYXBzaG90XCIgYW5kIFwidGhlIHN0cmVhbSBpcyBsaXZlXCIgd291bGQgYmVcbiAgICogbG9zdCBmb3JldmVyLiBTdWJzY3JpYmluZyBmaXJzdCBtZWFucyB0aGUgd29yc3QgY2FzZSBpcyBhIGR1cGxpY2F0ZSxcbiAgICogd2hpY2ggdGhlIFVMSUQgZ3VhcmQgaW4gYGRlbGl2ZXJgIGFic29yYnMuXG4gICAqL1xuICBhc3luYyBmdW5jdGlvbiBvcGVuU3Vic2NyaXB0aW9uKHJlY29yZDogU3Vic2NyaXB0aW9uUmVjb3JkKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKHJlY29yZC5jbG9zZWQgfHwgIXNvY2tldCB8fCAhYWNrZWQgfHwgcmVjb3JkLnNlbnQpIHJldHVybjtcbiAgICBsZXQgdG9rZW46IHN0cmluZztcbiAgICB0cnkge1xuICAgICAgdG9rZW4gPSBhd2FpdCBvcHRpb25zLmdldFRva2VuKCk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICByZXBvcnRFcnJvcihyZWNvcmQsIHRvRXJyb3IoZXJyLCAncmVhbHRpbWU6IGdldFRva2VuIGZhaWxlZCcpKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKHJlY29yZC5jbG9zZWQgfHwgIXNvY2tldCB8fCBzb2NrZXQucmVhZHlTdGF0ZSAhPT0gV1NfT1BFTikgcmV0dXJuO1xuICAgIHJlY29yZC5zZW50ID0gdHJ1ZTtcbiAgICB0cnkge1xuICAgICAgc29ja2V0LnNlbmQoXG4gICAgICAgIHN1YnNjcmliZUZyYW1lKHJlY29yZC5pZCwgcmVjb3JkLmNoYW5uZWwsIGF1dGhIZWFkZXIoZW5kcG9pbnRzLmh0dHBEb21haW4sIHRva2VuKSksXG4gICAgICApO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgcmVjb3JkLnNlbnQgPSBmYWxzZTtcbiAgICAgIHJlcG9ydEVycm9yKHJlY29yZCwgdG9FcnJvcihlcnIsICdyZWFsdGltZTogc3Vic2NyaWJlIGZyYW1lIGZhaWxlZCB0byBzZW5kJykpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBhd2FpdCBoeWRyYXRlKHJlY29yZCwgdG9rZW4pO1xuICB9XG5cbiAgYXN5bmMgZnVuY3Rpb24gaHlkcmF0ZShyZWNvcmQ6IFN1YnNjcmlwdGlvblJlY29yZCwgdG9rZW46IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBzbmFwc2hvdCA9IHJlY29yZC5vcHRpb25zLmdldFNuYXBzaG90XG4gICAgICAgID8gYXdhaXQgcmVjb3JkLm9wdGlvbnMuZ2V0U25hcHNob3QocmVjb3JkLmNoYW5uZWwpXG4gICAgICAgIDogYXdhaXQgZmV0Y2hMYXRlc3QocmVjb3JkLmNoYW5uZWwsIHRva2VuKTtcbiAgICAgIGlmIChzbmFwc2hvdCkgZGVsaXZlcihyZWNvcmQsIHNuYXBzaG90LCAnc25hcHNob3QnKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIHJlcG9ydEVycm9yKHJlY29yZCwgdG9FcnJvcihlcnIsIGByZWFsdGltZTogc25hcHNob3QgZmV0Y2ggZmFpbGVkIGZvciAke3JlY29yZC5jaGFubmVsfWApKTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBmdW5jdGlvbiBmZXRjaExhdGVzdChjaGFubmVsOiBzdHJpbmcsIHRva2VuOiBzdHJpbmcpOiBQcm9taXNlPFJlYWx0aW1lRXZlbnQgfCBudWxsPiB7XG4gICAgY29uc3QgdXJsID0gYCR7ZW5kcG9pbnRzLmFwaUJhc2V9L3YxL3JlYWx0aW1lL2xhdGVzdD9jaGFubmVsPSR7ZW5jb2RlVVJJQ29tcG9uZW50KGNoYW5uZWwpfWA7XG4gICAgY29uc3QgcmVzID0gYXdhaXQgZmV0Y2hGbih1cmwsIHsgaGVhZGVyczogeyBhdXRob3JpemF0aW9uOiBgQmVhcmVyICR7dG9rZW59YCB9IH0pO1xuICAgIGlmIChyZXMuc3RhdHVzID09PSA0MDQpIHJldHVybiBudWxsO1xuICAgIGlmICghcmVzLm9rKSB0aHJvdyBuZXcgRXJyb3IoYHNuYXBzaG90IGZldGNoIHJldHVybmVkIEhUVFAgJHtyZXMuc3RhdHVzfWApO1xuICAgIGNvbnN0IGJvZHkgPSAoYXdhaXQgcmVzLmpzb24oKSkgYXMgeyBldmVudD86IHVua25vd24gfTtcbiAgICByZXR1cm4gYXNSZWFsdGltZUV2ZW50KGJvZHk/LmV2ZW50KTtcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgc3Vic2NyaWJlKGNoYW5uZWwsIHN1YnNjcmliZU9wdGlvbnMpIHtcbiAgICAgIGNvbnN0IHJlY29yZDogU3Vic2NyaXB0aW9uUmVjb3JkID0ge1xuICAgICAgICBpZDogcmFuZG9tSWQoKSxcbiAgICAgICAgY2hhbm5lbCxcbiAgICAgICAgb3B0aW9uczogc3Vic2NyaWJlT3B0aW9ucyxcbiAgICAgICAgc2VudDogZmFsc2UsXG4gICAgICAgIGNsb3NlZDogZmFsc2UsXG4gICAgICB9O1xuICAgICAgc3Vic2NyaXB0aW9ucy5zZXQocmVjb3JkLmlkLCByZWNvcmQpO1xuXG4gICAgICBpZiAoYWNrZWQgJiYgc29ja2V0KSB7XG4gICAgICAgIHZvaWQgb3BlblN1YnNjcmlwdGlvbihyZWNvcmQpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZW5zdXJlU29ja2V0KCk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIGNoYW5uZWwsXG4gICAgICAgIHVuc3Vic2NyaWJlKCkge1xuICAgICAgICAgIGlmIChyZWNvcmQuY2xvc2VkKSByZXR1cm47XG4gICAgICAgICAgcmVjb3JkLmNsb3NlZCA9IHRydWU7XG4gICAgICAgICAgc3Vic2NyaXB0aW9ucy5kZWxldGUocmVjb3JkLmlkKTtcbiAgICAgICAgICBpZiAoc29ja2V0ICYmIHNvY2tldC5yZWFkeVN0YXRlID09PSBXU19PUEVOICYmIHJlY29yZC5zZW50KSB7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICBzb2NrZXQuc2VuZCh1bnN1YnNjcmliZUZyYW1lKHJlY29yZC5pZCkpO1xuICAgICAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgICAgIC8vIFNvY2tldCBpcyBnb2luZyBhd2F5IGFueXdheTsgdGhlIHNlcnZlciBkcm9wcyB0aGVcbiAgICAgICAgICAgICAgLy8gc3Vic2NyaXB0aW9uIHdoZW4gdGhlIGNvbm5lY3Rpb24gY2xvc2VzLlxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgfSxcblxuICAgIGNsb3NlKCkge1xuICAgICAgY2xvc2VkQnlDYWxsZXIgPSB0cnVlO1xuICAgICAgaWYgKHJlY29ubmVjdFRpbWVyKSBjbGVhclRpbWVvdXQocmVjb25uZWN0VGltZXIpO1xuICAgICAgcmVjb25uZWN0VGltZXIgPSB1bmRlZmluZWQ7XG4gICAgICBmb3IgKGNvbnN0IHJlY29yZCBvZiBzdWJzY3JpcHRpb25zLnZhbHVlcygpKSByZWNvcmQuY2xvc2VkID0gdHJ1ZTtcbiAgICAgIHN1YnNjcmlwdGlvbnMuY2xlYXIoKTtcbiAgICAgIGNvbnN0IG9wZW4gPSBzb2NrZXQ7XG4gICAgICB0ZWFyZG93blNvY2tldCgpO1xuICAgICAgdHJ5IHtcbiAgICAgICAgb3Blbj8uY2xvc2UoMTAwMCwgJ2NsaWVudCBjbG9zZWQnKTtcbiAgICAgIH0gY2F0Y2gge1xuICAgICAgICAvLyBOb3RoaW5nIHVzZWZ1bCB0byBkbyBpZiB0aGUgc29ja2V0IGlzIGFscmVhZHkgdG9ybiBkb3duLlxuICAgICAgfVxuICAgIH0sXG4gIH07XG59XG5cbi8vIOKUgOKUgOKUgCBIZWxwZXJzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG5mdW5jdGlvbiByZXNvbHZlRW5kcG9pbnRzKG9wdGlvbnM6IFJlYWx0aW1lQ29ubmVjdE9wdGlvbnMpOiBSZWFsdGltZUVuZHBvaW50cyB7XG4gIGlmIChvcHRpb25zLmVuZHBvaW50cykgcmV0dXJuIG9wdGlvbnMuZW5kcG9pbnRzO1xuICBpZiAoIW9wdGlvbnMuZW52KSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdyZWFsdGltZS5jb25uZWN0OiBwYXNzIGVpdGhlciB7ZW52fSBvciB7ZW5kcG9pbnRzfScpO1xuICB9XG4gIGNvbnN0IHJlc29sdmVkID0gZW5kcG9pbnRzRm9yKG9wdGlvbnMuZW52KTtcbiAgcmV0dXJuIHsgLi4ucmVzb2x2ZWQsIGFwaUJhc2U6IHJlc29sdmVkLmFwaUJhc2UgfHwgQVBJX0JBU0Vbb3B0aW9ucy5lbnZdIH07XG59XG5cbmZ1bmN0aW9uIGdsb2JhbFdlYlNvY2tldCgpOiBXZWJTb2NrZXRDdG9yIHtcbiAgY29uc3QgY3RvciA9IChnbG9iYWxUaGlzIGFzIHsgV2ViU29ja2V0PzogV2ViU29ja2V0Q3RvciB9KS5XZWJTb2NrZXQ7XG4gIGlmICghY3Rvcikge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICdyZWFsdGltZS5jb25uZWN0OiBubyBnbG9iYWwgV2ViU29ja2V0LiBQYXNzIHt3ZWJTb2NrZXRJbXBsfSAoTm9kZSA8IDIyLCBvciBhIHRlc3QgZG91YmxlKS4nLFxuICAgICk7XG4gIH1cbiAgcmV0dXJuIGN0b3I7XG59XG5cbi8qKlxuICogRnVsbC1qaXR0ZXIgZXhwb25lbnRpYWwgYmFja29mZi4gRnVsbCBqaXR0ZXIgKHJhdGhlciB0aGFuXG4gKiBkZWxheSDCsSBqaXR0ZXIpIG1hdHRlcnMgaGVyZSBiZWNhdXNlIGV2ZXJ5IHRhYiB3YXRjaGluZyBhbiBvcmRlclxuICogcmVjb25uZWN0cyBvbiB0aGUgc2FtZSBzZXJ2ZXItc2lkZSBldmVudDsgd2l0aG91dCBpdCB0aGV5IHJldHJ5IGluXG4gKiBsb2Nrc3RlcC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGppdHRlcmVkQmFja29mZihhdHRlbXB0OiBudW1iZXIsIG1heERlbGF5TXM6IG51bWJlcik6IG51bWJlciB7XG4gIGNvbnN0IGNlaWxpbmcgPSBNYXRoLm1pbihtYXhEZWxheU1zLCBCQVNFX1JFQ09OTkVDVF9ERUxBWV9NUyAqIDIgKiogYXR0ZW1wdCk7XG4gIHJldHVybiBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiBjZWlsaW5nKTtcbn1cblxuZnVuY3Rpb24gcmFuZG9tSWQoKTogc3RyaW5nIHtcbiAgY29uc3QgYyA9IChnbG9iYWxUaGlzIGFzIHsgY3J5cHRvPzogeyByYW5kb21VVUlEPzogKCkgPT4gc3RyaW5nIH0gfSkuY3J5cHRvO1xuICBpZiAoYz8ucmFuZG9tVVVJRCkgcmV0dXJuIGMucmFuZG9tVVVJRCgpO1xuICByZXR1cm4gYHN1Yi0ke0RhdGUubm93KCkudG9TdHJpbmcoMzYpfS0ke01hdGgucmFuZG9tKCkudG9TdHJpbmcoMzYpLnNsaWNlKDIsIDEwKX1gO1xufVxuXG5mdW5jdGlvbiB0b0Vycm9yKGVycjogdW5rbm93biwgZmFsbGJhY2tNZXNzYWdlOiBzdHJpbmcpOiBFcnJvciB7XG4gIGlmIChlcnIgaW5zdGFuY2VvZiBFcnJvcikgcmV0dXJuIGVycjtcbiAgcmV0dXJuIG5ldyBFcnJvcihgJHtmYWxsYmFja01lc3NhZ2V9OiAke1N0cmluZyhlcnIpfWApO1xufVxuXG5mdW5jdGlvbiB1bnJlZih0aW1lcjogUmV0dXJuVHlwZTx0eXBlb2Ygc2V0VGltZW91dD4pOiB2b2lkIHtcbiAgY29uc3QgdCA9IHRpbWVyIGFzIHsgdW5yZWY/OiAoKSA9PiB2b2lkIH07XG4gIGlmICh0eXBlb2YgdC51bnJlZiA9PT0gJ2Z1bmN0aW9uJykgdC51bnJlZigpO1xufVxuIl19
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Per-environment realtime endpoints.
3
+ *
4
+ * Frontends pass `connect({ env: 'prod' })` and get URLs from here — no
5
+ * hostnames or ARNs in app code.
6
+ *
7
+ * AppSync assigns the Event API's DNS names at create time
8
+ * (`<apiId>.appsync-api.<region>.amazonaws.com` and the matching
9
+ * `appsync-realtime-api` host), so they cannot be known before the stack
10
+ * is deployed. Until `services/realtime` has been deployed to an
11
+ * environment its entry stays null and `connect({ env })` throws with
12
+ * instructions; `connect({ endpoints })` always works.
13
+ */
14
+ export interface RealtimeEndpoints {
15
+ /** AppSync Event API HTTP domain. Also the `host` in the auth header. */
16
+ httpDomain: string;
17
+ /** AppSync Event API realtime (WebSocket) domain. */
18
+ realtimeDomain: string;
19
+ /** Platform API base URL, used for the last-value snapshot fetch. */
20
+ apiBase: string;
21
+ }
22
+ export type RealtimeEnv = 'dev' | 'uat' | 'prod';
23
+ /** Platform API base per environment — known ahead of any deploy. */
24
+ export declare const API_BASE: Record<RealtimeEnv, string>;
25
+ export declare function endpointsFor(env: RealtimeEnv): RealtimeEndpoints;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.API_BASE = void 0;
4
+ exports.endpointsFor = endpointsFor;
5
+ /**
6
+ * TODO(event-bus): populate `httpDomain` / `realtimeDomain` per
7
+ * environment from the `RealtimeEventApiHttpDns-{env}` and
8
+ * `RealtimeEventApiRealtimeDns-{env}` outputs of `UengageRealtimeService-{env}`
9
+ * once the stack has been deployed there.
10
+ */
11
+ const ENDPOINTS = {
12
+ dev: null,
13
+ uat: null,
14
+ prod: null,
15
+ };
16
+ /** Platform API base per environment — known ahead of any deploy. */
17
+ exports.API_BASE = {
18
+ dev: 'https://api.dev.platform.uengage.io',
19
+ uat: 'https://api.uat.platform.uengage.io',
20
+ prod: 'https://api.platform.uengage.io',
21
+ };
22
+ function endpointsFor(env) {
23
+ const found = ENDPOINTS[env];
24
+ if (!found) {
25
+ throw new Error(`realtime.connect: no endpoints registered for env "${env}". ` +
26
+ 'Pass {endpoints: {httpDomain, realtimeDomain, apiBase}} explicitly, or fill in ' +
27
+ "src/realtime/endpoints.ts from the realtime stack's RealtimeEventApi*Dns outputs.");
28
+ }
29
+ return found;
30
+ }
31
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW5kcG9pbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3JlYWx0aW1lL2VuZHBvaW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUEyQ0Esb0NBVUM7QUE3QkQ7Ozs7O0dBS0c7QUFDSCxNQUFNLFNBQVMsR0FBa0Q7SUFDL0QsR0FBRyxFQUFFLElBQUk7SUFDVCxHQUFHLEVBQUUsSUFBSTtJQUNULElBQUksRUFBRSxJQUFJO0NBQ1gsQ0FBQztBQUVGLHFFQUFxRTtBQUN4RCxRQUFBLFFBQVEsR0FBZ0M7SUFDbkQsR0FBRyxFQUFFLHFDQUFxQztJQUMxQyxHQUFHLEVBQUUscUNBQXFDO0lBQzFDLElBQUksRUFBRSxpQ0FBaUM7Q0FDeEMsQ0FBQztBQUVGLFNBQWdCLFlBQVksQ0FBQyxHQUFnQjtJQUMzQyxNQUFNLEtBQUssR0FBRyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDN0IsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ1gsTUFBTSxJQUFJLEtBQUssQ0FDYixzREFBc0QsR0FBRyxLQUFLO1lBQzVELGlGQUFpRjtZQUNqRixtRkFBbUYsQ0FDdEYsQ0FBQztJQUNKLENBQUM7SUFDRCxPQUFPLEtBQUssQ0FBQztBQUNmLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFBlci1lbnZpcm9ubWVudCByZWFsdGltZSBlbmRwb2ludHMuXG4gKlxuICogRnJvbnRlbmRzIHBhc3MgYGNvbm5lY3QoeyBlbnY6ICdwcm9kJyB9KWAgYW5kIGdldCBVUkxzIGZyb20gaGVyZSDigJQgbm9cbiAqIGhvc3RuYW1lcyBvciBBUk5zIGluIGFwcCBjb2RlLlxuICpcbiAqIEFwcFN5bmMgYXNzaWducyB0aGUgRXZlbnQgQVBJJ3MgRE5TIG5hbWVzIGF0IGNyZWF0ZSB0aW1lXG4gKiAoYDxhcGlJZD4uYXBwc3luYy1hcGkuPHJlZ2lvbj4uYW1hem9uYXdzLmNvbWAgYW5kIHRoZSBtYXRjaGluZ1xuICogYGFwcHN5bmMtcmVhbHRpbWUtYXBpYCBob3N0KSwgc28gdGhleSBjYW5ub3QgYmUga25vd24gYmVmb3JlIHRoZSBzdGFja1xuICogaXMgZGVwbG95ZWQuIFVudGlsIGBzZXJ2aWNlcy9yZWFsdGltZWAgaGFzIGJlZW4gZGVwbG95ZWQgdG8gYW5cbiAqIGVudmlyb25tZW50IGl0cyBlbnRyeSBzdGF5cyBudWxsIGFuZCBgY29ubmVjdCh7IGVudiB9KWAgdGhyb3dzIHdpdGhcbiAqIGluc3RydWN0aW9uczsgYGNvbm5lY3QoeyBlbmRwb2ludHMgfSlgIGFsd2F5cyB3b3Jrcy5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBSZWFsdGltZUVuZHBvaW50cyB7XG4gIC8qKiBBcHBTeW5jIEV2ZW50IEFQSSBIVFRQIGRvbWFpbi4gQWxzbyB0aGUgYGhvc3RgIGluIHRoZSBhdXRoIGhlYWRlci4gKi9cbiAgaHR0cERvbWFpbjogc3RyaW5nO1xuICAvKiogQXBwU3luYyBFdmVudCBBUEkgcmVhbHRpbWUgKFdlYlNvY2tldCkgZG9tYWluLiAqL1xuICByZWFsdGltZURvbWFpbjogc3RyaW5nO1xuICAvKiogUGxhdGZvcm0gQVBJIGJhc2UgVVJMLCB1c2VkIGZvciB0aGUgbGFzdC12YWx1ZSBzbmFwc2hvdCBmZXRjaC4gKi9cbiAgYXBpQmFzZTogc3RyaW5nO1xufVxuXG5leHBvcnQgdHlwZSBSZWFsdGltZUVudiA9ICdkZXYnIHwgJ3VhdCcgfCAncHJvZCc7XG5cbi8qKlxuICogVE9ETyhldmVudC1idXMpOiBwb3B1bGF0ZSBgaHR0cERvbWFpbmAgLyBgcmVhbHRpbWVEb21haW5gIHBlclxuICogZW52aXJvbm1lbnQgZnJvbSB0aGUgYFJlYWx0aW1lRXZlbnRBcGlIdHRwRG5zLXtlbnZ9YCBhbmRcbiAqIGBSZWFsdGltZUV2ZW50QXBpUmVhbHRpbWVEbnMte2Vudn1gIG91dHB1dHMgb2YgYFVlbmdhZ2VSZWFsdGltZVNlcnZpY2Ute2Vudn1gXG4gKiBvbmNlIHRoZSBzdGFjayBoYXMgYmVlbiBkZXBsb3llZCB0aGVyZS5cbiAqL1xuY29uc3QgRU5EUE9JTlRTOiBSZWNvcmQ8UmVhbHRpbWVFbnYsIFJlYWx0aW1lRW5kcG9pbnRzIHwgbnVsbD4gPSB7XG4gIGRldjogbnVsbCxcbiAgdWF0OiBudWxsLFxuICBwcm9kOiBudWxsLFxufTtcblxuLyoqIFBsYXRmb3JtIEFQSSBiYXNlIHBlciBlbnZpcm9ubWVudCDigJQga25vd24gYWhlYWQgb2YgYW55IGRlcGxveS4gKi9cbmV4cG9ydCBjb25zdCBBUElfQkFTRTogUmVjb3JkPFJlYWx0aW1lRW52LCBzdHJpbmc+ID0ge1xuICBkZXY6ICdodHRwczovL2FwaS5kZXYucGxhdGZvcm0udWVuZ2FnZS5pbycsXG4gIHVhdDogJ2h0dHBzOi8vYXBpLnVhdC5wbGF0Zm9ybS51ZW5nYWdlLmlvJyxcbiAgcHJvZDogJ2h0dHBzOi8vYXBpLnBsYXRmb3JtLnVlbmdhZ2UuaW8nLFxufTtcblxuZXhwb3J0IGZ1bmN0aW9uIGVuZHBvaW50c0ZvcihlbnY6IFJlYWx0aW1lRW52KTogUmVhbHRpbWVFbmRwb2ludHMge1xuICBjb25zdCBmb3VuZCA9IEVORFBPSU5UU1tlbnZdO1xuICBpZiAoIWZvdW5kKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYHJlYWx0aW1lLmNvbm5lY3Q6IG5vIGVuZHBvaW50cyByZWdpc3RlcmVkIGZvciBlbnYgXCIke2Vudn1cIi4gYCArXG4gICAgICAgICdQYXNzIHtlbmRwb2ludHM6IHtodHRwRG9tYWluLCByZWFsdGltZURvbWFpbiwgYXBpQmFzZX19IGV4cGxpY2l0bHksIG9yIGZpbGwgaW4gJyArXG4gICAgICAgIFwic3JjL3JlYWx0aW1lL2VuZHBvaW50cy50cyBmcm9tIHRoZSByZWFsdGltZSBzdGFjaydzIFJlYWx0aW1lRXZlbnRBcGkqRG5zIG91dHB1dHMuXCIsXG4gICAgKTtcbiAgfVxuICByZXR1cm4gZm91bmQ7XG59XG4iXX0=
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Dependency-free envelope guard for the browser realtime client.
3
+ *
4
+ * This mirrors `@uengage/common`'s `platformEventSchema` and the SDK's own
5
+ * `events/schema.ts`, but hand-rolled: the `/realtime` subpath ships with
6
+ * zero runtime dependencies (no zod, no Node built-ins) so it stays a few
7
+ * KB in a frontend bundle. Change one, change all three.
8
+ *
9
+ * NO DOMAIN CROSS-CHECK, deliberately — this is the one mirror that does
10
+ * not carry the resource→domain map, and it is not an oversight:
11
+ *
12
+ * - It would be checking the wrong thing. The producing SDK derives
13
+ * `domain` from `type` and the consuming service re-validates the pair
14
+ * before it ever publishes to a channel. By the time an envelope
15
+ * reaches a browser the pair has been checked twice server-side; a
16
+ * third check can only reject, never repair.
17
+ * - It would rot dangerously. A browser bundle sits in someone's cache
18
+ * for weeks. Baking today's map into it means that the day a new
19
+ * domain ships, every un-refreshed tab starts silently dropping live
20
+ * events for it — a stale client rejecting the future, which is the
21
+ * failure mode `validatePlatformEvent` is careful to avoid even
22
+ * server-side.
23
+ *
24
+ * So the guard is structural only: shapes and types, not membership.
25
+ * `packages/platform-sdk/test/events/domain-map.test.ts` asserts that
26
+ * every canonical domain is accepted, and that an unlisted one is too.
27
+ */
28
+ /** The platform event-bus envelope, as it arrives at a browser. */
29
+ export interface RealtimeEvent {
30
+ id: string;
31
+ type: string;
32
+ domain: string;
33
+ source: string;
34
+ version: number;
35
+ occurredAt: string;
36
+ tenantId: string;
37
+ data: Record<string, unknown>;
38
+ }
39
+ /**
40
+ * Structural check on an untrusted payload. Returns the event or null —
41
+ * never throws, because a single malformed frame must not tear down a
42
+ * live subscription.
43
+ */
44
+ export declare function asRealtimeEvent(value: unknown): RealtimeEvent | null;
45
+ /**
46
+ * Ordering/dedupe guard, keyed on the envelope's monotonic ULID.
47
+ *
48
+ * ULIDs are lexicographically sortable by mint time, so a plain string
49
+ * compare orders events without parsing timestamps or knowing anything
50
+ * about the domain. This is what makes at-least-once delivery and the
51
+ * snapshot/stream race both harmless: whichever arrives second with an
52
+ * id <= the one already rendered is dropped.
53
+ */
54
+ export declare function isNewer(candidateId: string, seenId: string | undefined): boolean;