@uengage.io/platform-sdk 0.15.0 → 2.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.
- package/README.md +90 -0
- package/dist/client.d.ts +13 -0
- package/dist/client.js +4 -1
- package/dist/config.d.ts +0 -5
- package/dist/config.js +14 -3
- package/dist/errors.d.ts +17 -0
- package/dist/errors.js +21 -2
- package/dist/events/client.d.ts +81 -0
- package/dist/events/client.js +216 -0
- package/dist/events/index.d.ts +15 -0
- package/dist/events/index.js +41 -0
- package/dist/events/schema.d.ts +58 -0
- package/dist/events/schema.js +67 -0
- package/dist/http.js +25 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -1
- package/dist/realtime/connect.d.ts +106 -0
- package/dist/realtime/connect.js +450 -0
- package/dist/realtime/endpoints.d.ts +25 -0
- package/dist/realtime/endpoints.js +31 -0
- package/dist/realtime/envelope.d.ts +54 -0
- package/dist/realtime/envelope.js +77 -0
- package/dist/realtime/index.d.ts +37 -0
- package/dist/realtime/index.js +49 -0
- package/dist/realtime/protocol.d.ts +73 -0
- package/dist/realtime/protocol.js +89 -0
- package/dist/upsell/client.d.ts +44 -0
- package/dist/upsell/client.js +136 -0
- package/dist/upsell/index.d.ts +2 -0
- package/dist/upsell/index.js +7 -0
- package/dist/upsell/schema.d.ts +203 -0
- package/dist/upsell/schema.js +12 -0
- package/dist/version.d.ts +7 -1
- package/dist/version.js +8 -2
- package/package.json +38 -2
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { type RealtimeEvent } from './envelope';
|
|
2
|
+
import { type RealtimeEndpoints, type RealtimeEnv } from './endpoints';
|
|
3
|
+
/**
|
|
4
|
+
* The slice of the WebSocket API this client uses.
|
|
5
|
+
*
|
|
6
|
+
* Declared structurally rather than relying on a DOM lib: the SDK
|
|
7
|
+
* compiles against `lib: ES2022` only, and this keeps the client
|
|
8
|
+
* injectable so tests can drive the protocol with a fake socket.
|
|
9
|
+
*/
|
|
10
|
+
export interface MinimalWebSocket {
|
|
11
|
+
readyState: number;
|
|
12
|
+
send(data: string): void;
|
|
13
|
+
close(code?: number, reason?: string): void;
|
|
14
|
+
onopen: ((ev: unknown) => void) | null;
|
|
15
|
+
onmessage: ((ev: {
|
|
16
|
+
data: unknown;
|
|
17
|
+
}) => void) | null;
|
|
18
|
+
onerror: ((ev: unknown) => void) | null;
|
|
19
|
+
onclose: ((ev: {
|
|
20
|
+
code?: number;
|
|
21
|
+
reason?: string;
|
|
22
|
+
}) => void) | null;
|
|
23
|
+
}
|
|
24
|
+
export type WebSocketCtor = new (url: string, protocols?: string | string[]) => MinimalWebSocket;
|
|
25
|
+
/** Where an event handed to `onUpdate` came from. */
|
|
26
|
+
export interface UpdateMeta {
|
|
27
|
+
channel: string;
|
|
28
|
+
/**
|
|
29
|
+
* `'snapshot'` — the channel's last value, fetched from the last-value
|
|
30
|
+
* cache on subscribe or after a reconnect. `'live'` — delivered over
|
|
31
|
+
* the socket.
|
|
32
|
+
*
|
|
33
|
+
* Apps generally don't need to branch on this; the SDK has already
|
|
34
|
+
* ordered and de-duplicated both sources by ULID. It's there for
|
|
35
|
+
* telemetry and for UIs that want to distinguish "restored" from
|
|
36
|
+
* "just happened".
|
|
37
|
+
*/
|
|
38
|
+
origin: 'snapshot' | 'live';
|
|
39
|
+
}
|
|
40
|
+
export interface SubscribeOptions {
|
|
41
|
+
/**
|
|
42
|
+
* Fires once with the channel's latest known value (if any), then on
|
|
43
|
+
* every live event. Never fires with an event older than one already
|
|
44
|
+
* delivered on this subscription.
|
|
45
|
+
*/
|
|
46
|
+
onUpdate(event: RealtimeEvent, meta: UpdateMeta): void;
|
|
47
|
+
/**
|
|
48
|
+
* Subscription-level failures: a rejected subscribe, a malformed
|
|
49
|
+
* frame, a failed snapshot fetch. Transient socket drops are handled
|
|
50
|
+
* internally by the reconnect loop and are not surfaced here.
|
|
51
|
+
*/
|
|
52
|
+
onError?(error: Error): void;
|
|
53
|
+
/**
|
|
54
|
+
* Replace the built-in last-value fetch for this channel. Return null
|
|
55
|
+
* when there is no snapshot. Useful when a domain has a better source
|
|
56
|
+
* of truth than the cache — e.g. falling back to the order-details API
|
|
57
|
+
* for an order whose transitions predate bus instrumentation.
|
|
58
|
+
*/
|
|
59
|
+
getSnapshot?(channel: string): Promise<RealtimeEvent | null>;
|
|
60
|
+
}
|
|
61
|
+
export interface Subscription {
|
|
62
|
+
readonly channel: string;
|
|
63
|
+
unsubscribe(): void;
|
|
64
|
+
}
|
|
65
|
+
export interface RealtimeClient {
|
|
66
|
+
/**
|
|
67
|
+
* Subscribe to a channel. Channels are the only concept here — the
|
|
68
|
+
* realtime layer knows nothing about orders; `/orders/{id}` is a
|
|
69
|
+
* naming convention enforced by the service's namespace policy, not by
|
|
70
|
+
* this client.
|
|
71
|
+
*
|
|
72
|
+
* On this call, and again after every reconnect, the SDK opens the
|
|
73
|
+
* subscription *first* and only then fetches the channel's last value,
|
|
74
|
+
* so no event can fall between the snapshot and the stream.
|
|
75
|
+
*/
|
|
76
|
+
subscribe(channel: string, options: SubscribeOptions): Subscription;
|
|
77
|
+
/** Close the shared socket and stop reconnecting. */
|
|
78
|
+
close(): void;
|
|
79
|
+
}
|
|
80
|
+
export interface RealtimeConnectOptions {
|
|
81
|
+
/** Resolves endpoints from the built-in table. Mutually exclusive with `endpoints`. */
|
|
82
|
+
env?: RealtimeEnv;
|
|
83
|
+
/** Explicit endpoints, for environments not in the table. */
|
|
84
|
+
endpoints?: RealtimeEndpoints;
|
|
85
|
+
/**
|
|
86
|
+
* Supplies the platform JWT. Called on connect, on every (re)subscribe,
|
|
87
|
+
* and again after a token-expiry reconnect — so returning a fresh token
|
|
88
|
+
* from your session store is enough to keep a long-lived connection
|
|
89
|
+
* authenticated.
|
|
90
|
+
*/
|
|
91
|
+
getToken(): string | Promise<string>;
|
|
92
|
+
/** Inject a WebSocket implementation. Defaults to `globalThis.WebSocket`. */
|
|
93
|
+
webSocketImpl?: WebSocketCtor;
|
|
94
|
+
/** Inject a fetch implementation for snapshot fetches. Defaults to `globalThis.fetch`. */
|
|
95
|
+
fetchFn?: typeof fetch;
|
|
96
|
+
/** Backoff ceiling for reconnects. Default 30_000. */
|
|
97
|
+
maxReconnectDelayMs?: number;
|
|
98
|
+
}
|
|
99
|
+
export declare function connect(options: RealtimeConnectOptions): RealtimeClient;
|
|
100
|
+
/**
|
|
101
|
+
* Full-jitter exponential backoff. Full jitter (rather than
|
|
102
|
+
* delay ± jitter) matters here because every tab watching an order
|
|
103
|
+
* reconnects on the same server-side event; without it they retry in
|
|
104
|
+
* lockstep.
|
|
105
|
+
*/
|
|
106
|
+
export declare function jitteredBackoff(attempt: number, maxDelayMs: number): number;
|
|
@@ -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;
|