@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,216 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createEventsClient = createEventsClient;
4
+ const client_sns_1 = require("@aws-sdk/client-sns");
5
+ const ulid_1 = require("ulid");
6
+ const errors_1 = require("../errors");
7
+ const schema_1 = require("./schema");
8
+ /** SNS caps PublishBatch at 10 entries per call. */
9
+ const SNS_BATCH_MAX = 10;
10
+ /**
11
+ * SNS caps a single message at 256 KB, and a whole PublishBatch request
12
+ * at the same 256 KB across all its entries.
13
+ */
14
+ const SNS_MAX_BYTES = 262_144;
15
+ /**
16
+ * Headroom left for the `type` / `domain` / `source` message attributes,
17
+ * which count toward the limit alongside the body. A few hundred bytes
18
+ * covers them; being conservative here costs nothing, since an envelope
19
+ * anywhere near the cap is a call-site mistake either way.
20
+ */
21
+ const ATTRIBUTE_ALLOWANCE_BYTES = 1_024;
22
+ const MAX_MESSAGE_BYTES = SNS_MAX_BYTES - ATTRIBUTE_ALLOWANCE_BYTES;
23
+ /**
24
+ * Byte length of a UTF-8 string. `TextEncoder` rather than
25
+ * `Buffer.byteLength` so this file carries no Node built-in.
26
+ */
27
+ const encoder = new TextEncoder();
28
+ function byteLength(value) {
29
+ return encoder.encode(value).length;
30
+ }
31
+ /**
32
+ * Monotonic within this process: two events minted in the same
33
+ * millisecond still sort in emission order. Downstream consumers use the
34
+ * envelope `id` as the ordering and dedupe key, so this is load-bearing,
35
+ * not a nicety.
36
+ */
37
+ const nextUlid = (0, ulid_1.monotonicFactory)();
38
+ /**
39
+ * Build an isolated events client. Reads `PLATFORM_EVENTS_TOPIC_ARN` and
40
+ * friends from the environment for anything not passed explicitly, so a
41
+ * Lambda whose deploy stack injected the ARN needs no arguments.
42
+ */
43
+ function createEventsClient(input = {}) {
44
+ let client = input.snsClient;
45
+ function topicArn() {
46
+ const arn = input.topicArn ?? env('PLATFORM_EVENTS_TOPIC_ARN');
47
+ if (!arn) {
48
+ throw new errors_1.ConfigError('events.publish: no topic ARN. Pass {topicArn} to createEventsClient or set PLATFORM_EVENTS_TOPIC_ARN.');
49
+ }
50
+ return arn;
51
+ }
52
+ function sns() {
53
+ if (!client) {
54
+ const region = input.region ?? regionFromArn(topicArn()) ?? env('AWS_REGION') ?? env('AWS_DEFAULT_REGION');
55
+ client = new client_sns_1.SNSClient(region ? { region } : {});
56
+ }
57
+ return client;
58
+ }
59
+ function source(override) {
60
+ return (override ??
61
+ input.source ??
62
+ env('PLATFORM_EVENTS_SOURCE') ??
63
+ env('SERVICE_NAME') ??
64
+ 'platform-sdk');
65
+ }
66
+ function buildEnvelope(type, data, opts) {
67
+ const domain = (0, schema_1.domainForEventType)(type);
68
+ if (!domain) {
69
+ throw new errors_1.ValidationError(`events.publish: cannot route event type "${type}". It must be a dotted ` +
70
+ '"<resource>.<operation>" name whose resource maps to a known bus domain ' +
71
+ '(order, customer, menu, offer, loyalty, usage). Adding a domain is a one-line ' +
72
+ 'edit in events/schema.ts and its two mirrors.');
73
+ }
74
+ const candidate = {
75
+ id: nextUlid(),
76
+ type,
77
+ domain,
78
+ source: source(opts.source),
79
+ version: schema_1.PLATFORM_EVENT_VERSION,
80
+ occurredAt: opts.occurredAt ?? isoNow(),
81
+ tenantId: opts.tenantId,
82
+ data,
83
+ };
84
+ const result = schema_1.platformEventSchema.safeParse(candidate);
85
+ if (!result.success) {
86
+ throw new errors_1.ValidationError(`events.publish: invalid envelope for "${type}"`, {
87
+ issues: result.error.issues,
88
+ });
89
+ }
90
+ return result.data;
91
+ }
92
+ /**
93
+ * Serialize and size-check one envelope.
94
+ *
95
+ * Checked before the publish rather than after the rejection because
96
+ * SNS's own error for an oversized message names neither the event nor
97
+ * the offending field, and on the PHP side the same failure disappears
98
+ * into `error_log`. Better to say which event and how big.
99
+ */
100
+ function serialize(event) {
101
+ const message = JSON.stringify(event);
102
+ const size = byteLength(message);
103
+ if (size > MAX_MESSAGE_BYTES) {
104
+ throw new errors_1.ValidationError(`events.publish: event ${event.id} ("${event.type}") serializes to ${size} bytes, ` +
105
+ `over the ${MAX_MESSAGE_BYTES}-byte ceiling (SNS caps a message at 256KB including ` +
106
+ 'attributes). Put the payload somewhere addressable and reference it from `data`.');
107
+ }
108
+ return message;
109
+ }
110
+ return {
111
+ async publish(type, data, opts) {
112
+ const event = buildEnvelope(type, data, opts);
113
+ const message = serialize(event);
114
+ const arn = topicArn();
115
+ try {
116
+ await sns().send(new client_sns_1.PublishCommand({
117
+ TopicArn: arn,
118
+ Message: message,
119
+ MessageAttributes: messageAttributes(event),
120
+ }));
121
+ }
122
+ catch (err) {
123
+ throw new errors_1.EventPublishError(`events.publish: SNS publish failed for "${type}"`, {
124
+ failedEventIds: [event.id],
125
+ cause: err,
126
+ });
127
+ }
128
+ return event;
129
+ },
130
+ async publishBatch(events) {
131
+ if (events.length === 0)
132
+ return [];
133
+ const envelopes = events.map((e) => buildEnvelope(e.type, e.data, e));
134
+ // Serialize (and size-check) everything up front, so an oversized
135
+ // event fails the call before any of its siblings are published.
136
+ const messages = new Map(envelopes.map((event) => [event.id, serialize(event)]));
137
+ const arn = topicArn();
138
+ const failed = [];
139
+ let firstError;
140
+ for (const group of chunkByBytes(envelopes, messages)) {
141
+ const entries = group.map((event) => ({
142
+ Id: event.id,
143
+ Message: messages.get(event.id),
144
+ MessageAttributes: messageAttributes(event),
145
+ }));
146
+ try {
147
+ const resp = await sns().send(new client_sns_1.PublishBatchCommand({ TopicArn: arn, PublishBatchRequestEntries: entries }));
148
+ for (const f of resp.Failed ?? []) {
149
+ if (f.Id)
150
+ failed.push(f.Id);
151
+ }
152
+ }
153
+ catch (err) {
154
+ firstError = firstError ?? err;
155
+ for (const event of group)
156
+ failed.push(event.id);
157
+ }
158
+ }
159
+ if (failed.length > 0) {
160
+ throw new errors_1.EventPublishError(`events.publishBatch: ${failed.length}/${envelopes.length} event(s) did not reach the bus`, { failedEventIds: failed, cause: firstError });
161
+ }
162
+ return envelopes;
163
+ },
164
+ };
165
+ }
166
+ function messageAttributes(event) {
167
+ return {
168
+ type: { DataType: 'String', StringValue: event.type },
169
+ domain: { DataType: 'String', StringValue: event.domain },
170
+ source: { DataType: 'String', StringValue: event.source },
171
+ };
172
+ }
173
+ /** `arn:aws:sns:<region>:<account>:<name>` → `<region>`. */
174
+ function regionFromArn(arn) {
175
+ const parts = arn.split(':');
176
+ return parts.length >= 4 && parts[3] ? parts[3] : undefined;
177
+ }
178
+ /** UTC ISO 8601, seconds precision — the format the envelope schema pins. */
179
+ function isoNow() {
180
+ return new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
181
+ }
182
+ function env(name) {
183
+ if (typeof process === 'undefined')
184
+ return undefined;
185
+ const value = process.env?.[name];
186
+ return value === '' ? undefined : value;
187
+ }
188
+ /**
189
+ * Group envelopes into PublishBatch calls bounded by BOTH limits: at most
190
+ * 10 entries, and at most 256 KB of combined payload.
191
+ *
192
+ * The byte bound matters as soon as payloads get rich — ten 30 KB
193
+ * envelopes are each individually legal and collectively over the
194
+ * request cap, and SNS rejects the whole call rather than the overflow.
195
+ */
196
+ function chunkByBytes(envelopes, messages) {
197
+ const out = [];
198
+ let current = [];
199
+ let currentBytes = 0;
200
+ for (const event of envelopes) {
201
+ const size = byteLength(messages.get(event.id)) + ATTRIBUTE_ALLOWANCE_BYTES;
202
+ const full = current.length >= SNS_BATCH_MAX;
203
+ const wouldOverflow = current.length > 0 && currentBytes + size > SNS_MAX_BYTES;
204
+ if (full || wouldOverflow) {
205
+ out.push(current);
206
+ current = [];
207
+ currentBytes = 0;
208
+ }
209
+ current.push(event);
210
+ currentBytes += size;
211
+ }
212
+ if (current.length > 0)
213
+ out.push(current);
214
+ return out;
215
+ }
216
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2V2ZW50cy9jbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUEySUEsZ0RBa0pDO0FBN1JELG9EQU02QjtBQUM3QiwrQkFBd0M7QUFDeEMsc0NBQTRFO0FBQzVFLHFDQUtrQjtBQUVsQixvREFBb0Q7QUFDcEQsTUFBTSxhQUFhLEdBQUcsRUFBRSxDQUFDO0FBRXpCOzs7R0FHRztBQUNILE1BQU0sYUFBYSxHQUFHLE9BQU8sQ0FBQztBQUU5Qjs7Ozs7R0FLRztBQUNILE1BQU0seUJBQXlCLEdBQUcsS0FBSyxDQUFDO0FBRXhDLE1BQU0saUJBQWlCLEdBQUcsYUFBYSxHQUFHLHlCQUF5QixDQUFDO0FBRXBFOzs7R0FHRztBQUNILE1BQU0sT0FBTyxHQUFHLElBQUksV0FBVyxFQUFFLENBQUM7QUFFbEMsU0FBUyxVQUFVLENBQUMsS0FBYTtJQUMvQixPQUFPLE9BQU8sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsTUFBTSxDQUFDO0FBQ3RDLENBQUM7QUFFRDs7Ozs7R0FLRztBQUNILE1BQU0sUUFBUSxHQUFHLElBQUEsdUJBQWdCLEdBQUUsQ0FBQztBQW1GcEM7Ozs7R0FJRztBQUNILFNBQWdCLGtCQUFrQixDQUFDLFFBQTJCLEVBQUU7SUFDOUQsSUFBSSxNQUFNLEdBQTBCLEtBQUssQ0FBQyxTQUFTLENBQUM7SUFFcEQsU0FBUyxRQUFRO1FBQ2YsTUFBTSxHQUFHLEdBQUcsS0FBSyxDQUFDLFFBQVEsSUFBSSxHQUFHLENBQUMsMkJBQTJCLENBQUMsQ0FBQztRQUMvRCxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDVCxNQUFNLElBQUksb0JBQVcsQ0FDbkIsdUdBQXVHLENBQ3hHLENBQUM7UUFDSixDQUFDO1FBQ0QsT0FBTyxHQUFHLENBQUM7SUFDYixDQUFDO0lBRUQsU0FBUyxHQUFHO1FBQ1YsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ1osTUFBTSxNQUFNLEdBQ1YsS0FBSyxDQUFDLE1BQU0sSUFBSSxhQUFhLENBQUMsUUFBUSxFQUFFLENBQUMsSUFBSSxHQUFHLENBQUMsWUFBWSxDQUFDLElBQUksR0FBRyxDQUFDLG9CQUFvQixDQUFDLENBQUM7WUFDOUYsTUFBTSxHQUFHLElBQUksc0JBQVMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ25ELENBQUM7UUFDRCxPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDO0lBRUQsU0FBUyxNQUFNLENBQUMsUUFBaUI7UUFDL0IsT0FBTyxDQUNMLFFBQVE7WUFDUixLQUFLLENBQUMsTUFBTTtZQUNaLEdBQUcsQ0FBQyx3QkFBd0IsQ0FBQztZQUM3QixHQUFHLENBQUMsY0FBYyxDQUFDO1lBQ25CLGNBQWMsQ0FDZixDQUFDO0lBQ0osQ0FBQztJQUVELFNBQVMsYUFBYSxDQUNwQixJQUFZLEVBQ1osSUFBNkIsRUFDN0IsSUFBb0I7UUFFcEIsTUFBTSxNQUFNLEdBQUcsSUFBQSwyQkFBa0IsRUFBQyxJQUFJLENBQUMsQ0FBQztRQUN4QyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDWixNQUFNLElBQUksd0JBQWUsQ0FDdkIsNENBQTRDLElBQUkseUJBQXlCO2dCQUN2RSwwRUFBMEU7Z0JBQzFFLGdGQUFnRjtnQkFDaEYsK0NBQStDLENBQ2xELENBQUM7UUFDSixDQUFDO1FBQ0QsTUFBTSxTQUFTLEdBQUc7WUFDaEIsRUFBRSxFQUFFLFFBQVEsRUFBRTtZQUNkLElBQUk7WUFDSixNQUFNO1lBQ04sTUFBTSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDO1lBQzNCLE9BQU8sRUFBRSwrQkFBc0I7WUFDL0IsVUFBVSxFQUFFLElBQUksQ0FBQyxVQUFVLElBQUksTUFBTSxFQUFFO1lBQ3ZDLFFBQVEsRUFBRSxJQUFJLENBQUMsUUFBUTtZQUN2QixJQUFJO1NBQ0wsQ0FBQztRQUNGLE1BQU0sTUFBTSxHQUFHLDRCQUFtQixDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUN4RCxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ3BCLE1BQU0sSUFBSSx3QkFBZSxDQUFDLHlDQUF5QyxJQUFJLEdBQUcsRUFBRTtnQkFDMUUsTUFBTSxFQUFFLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTTthQUM1QixDQUFDLENBQUM7UUFDTCxDQUFDO1FBQ0QsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDO0lBQ3JCLENBQUM7SUFFRDs7Ozs7OztPQU9HO0lBQ0gsU0FBUyxTQUFTLENBQUMsS0FBb0I7UUFDckMsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUN0QyxNQUFNLElBQUksR0FBRyxVQUFVLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDakMsSUFBSSxJQUFJLEdBQUcsaUJBQWlCLEVBQUUsQ0FBQztZQUM3QixNQUFNLElBQUksd0JBQWUsQ0FDdkIseUJBQXlCLEtBQUssQ0FBQyxFQUFFLE1BQU0sS0FBSyxDQUFDLElBQUksb0JBQW9CLElBQUksVUFBVTtnQkFDakYsWUFBWSxpQkFBaUIsdURBQXVEO2dCQUNwRixrRkFBa0YsQ0FDckYsQ0FBQztRQUNKLENBQUM7UUFDRCxPQUFPLE9BQU8sQ0FBQztJQUNqQixDQUFDO0lBRUQsT0FBTztRQUNMLEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJO1lBQzVCLE1BQU0sS0FBSyxHQUFHLGFBQWEsQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO1lBQzlDLE1BQU0sT0FBTyxHQUFHLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUNqQyxNQUFNLEdBQUcsR0FBRyxRQUFRLEVBQUUsQ0FBQztZQUN2QixJQUFJLENBQUM7Z0JBQ0gsTUFBTSxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQ2QsSUFBSSwyQkFBYyxDQUFDO29CQUNqQixRQUFRLEVBQUUsR0FBRztvQkFDYixPQUFPLEVBQUUsT0FBTztvQkFDaEIsaUJBQWlCLEVBQUUsaUJBQWlCLENBQUMsS0FBSyxDQUFDO2lCQUM1QyxDQUFDLENBQ0gsQ0FBQztZQUNKLENBQUM7WUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO2dCQUNiLE1BQU0sSUFBSSwwQkFBaUIsQ0FBQywyQ0FBMkMsSUFBSSxHQUFHLEVBQUU7b0JBQzlFLGNBQWMsRUFBRSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7b0JBQzFCLEtBQUssRUFBRSxHQUFHO2lCQUNYLENBQUMsQ0FBQztZQUNMLENBQUM7WUFDRCxPQUFPLEtBQUssQ0FBQztRQUNmLENBQUM7UUFFRCxLQUFLLENBQUMsWUFBWSxDQUFDLE1BQU07WUFDdkIsSUFBSSxNQUFNLENBQUMsTUFBTSxLQUFLLENBQUM7Z0JBQUUsT0FBTyxFQUFFLENBQUM7WUFDbkMsTUFBTSxTQUFTLEdBQUcsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ3RFLGtFQUFrRTtZQUNsRSxpRUFBaUU7WUFDakUsTUFBTSxRQUFRLEdBQUcsSUFBSSxHQUFHLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsRUFBRSxFQUFFLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUNqRixNQUFNLEdBQUcsR0FBRyxRQUFRLEVBQUUsQ0FBQztZQUV2QixNQUFNLE1BQU0sR0FBYSxFQUFFLENBQUM7WUFDNUIsSUFBSSxVQUFtQixDQUFDO1lBQ3hCLEtBQUssTUFBTSxLQUFLLElBQUksWUFBWSxDQUFDLFNBQVMsRUFBRSxRQUFRLENBQUMsRUFBRSxDQUFDO2dCQUN0RCxNQUFNLE9BQU8sR0FBK0IsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztvQkFDaEUsRUFBRSxFQUFFLEtBQUssQ0FBQyxFQUFFO29CQUNaLE9BQU8sRUFBRSxRQUFRLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQVc7b0JBQ3pDLGlCQUFpQixFQUFFLGlCQUFpQixDQUFDLEtBQUssQ0FBQztpQkFDNUMsQ0FBQyxDQUFDLENBQUM7Z0JBQ0osSUFBSSxDQUFDO29CQUNILE1BQU0sSUFBSSxHQUFHLE1BQU0sR0FBRyxFQUFFLENBQUMsSUFBSSxDQUMzQixJQUFJLGdDQUFtQixDQUFDLEVBQUUsUUFBUSxFQUFFLEdBQUcsRUFBRSwwQkFBMEIsRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUNoRixDQUFDO29CQUNGLEtBQUssTUFBTSxDQUFDLElBQUksSUFBSSxDQUFDLE1BQU0sSUFBSSxFQUFFLEVBQUUsQ0FBQzt3QkFDbEMsSUFBSSxDQUFDLENBQUMsRUFBRTs0QkFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQztvQkFDOUIsQ0FBQztnQkFDSCxDQUFDO2dCQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7b0JBQ2IsVUFBVSxHQUFHLFVBQVUsSUFBSSxHQUFHLENBQUM7b0JBQy9CLEtBQUssTUFBTSxLQUFLLElBQUksS0FBSzt3QkFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQztnQkFDbkQsQ0FBQztZQUNILENBQUM7WUFFRCxJQUFJLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7Z0JBQ3RCLE1BQU0sSUFBSSwwQkFBaUIsQ0FDekIsd0JBQXdCLE1BQU0sQ0FBQyxNQUFNLElBQUksU0FBUyxDQUFDLE1BQU0saUNBQWlDLEVBQzFGLEVBQUUsY0FBYyxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFFLENBQzlDLENBQUM7WUFDSixDQUFDO1lBQ0QsT0FBTyxTQUFTLENBQUM7UUFDbkIsQ0FBQztLQUNGLENBQUM7QUFDSixDQUFDO0FBRUQsU0FBUyxpQkFBaUIsQ0FBQyxLQUFvQjtJQUM3QyxPQUFPO1FBQ0wsSUFBSSxFQUFFLEVBQUUsUUFBUSxFQUFFLFFBQVEsRUFBRSxXQUFXLEVBQUUsS0FBSyxDQUFDLElBQUksRUFBRTtRQUNyRCxNQUFNLEVBQUUsRUFBRSxRQUFRLEVBQUUsUUFBUSxFQUFFLFdBQVcsRUFBRSxLQUFLLENBQUMsTUFBTSxFQUFFO1FBQ3pELE1BQU0sRUFBRSxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsV0FBVyxFQUFFLEtBQUssQ0FBQyxNQUFNLEVBQUU7S0FDMUQsQ0FBQztBQUNKLENBQUM7QUFFRCw0REFBNEQ7QUFDNUQsU0FBUyxhQUFhLENBQUMsR0FBVztJQUNoQyxNQUFNLEtBQUssR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQzdCLE9BQU8sS0FBSyxDQUFDLE1BQU0sSUFBSSxDQUFDLElBQUksS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztBQUM5RCxDQUFDO0FBRUQsNkVBQTZFO0FBQzdFLFNBQVMsTUFBTTtJQUNiLE9BQU8sSUFBSSxJQUFJLEVBQUUsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxPQUFPLENBQUMsV0FBVyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0FBQzVELENBQUM7QUFFRCxTQUFTLEdBQUcsQ0FBQyxJQUFZO0lBQ3ZCLElBQUksT0FBTyxPQUFPLEtBQUssV0FBVztRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQ3JELE1BQU0sS0FBSyxHQUFJLE9BQXdELENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDcEYsT0FBTyxLQUFLLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQztBQUMxQyxDQUFDO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILFNBQVMsWUFBWSxDQUNuQixTQUEwQixFQUMxQixRQUE2QjtJQUU3QixNQUFNLEdBQUcsR0FBc0IsRUFBRSxDQUFDO0lBQ2xDLElBQUksT0FBTyxHQUFvQixFQUFFLENBQUM7SUFDbEMsSUFBSSxZQUFZLEdBQUcsQ0FBQyxDQUFDO0lBRXJCLEtBQUssTUFBTSxLQUFLLElBQUksU0FBUyxFQUFFLENBQUM7UUFDOUIsTUFBTSxJQUFJLEdBQUcsVUFBVSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBVyxDQUFDLEdBQUcseUJBQXlCLENBQUM7UUFDdEYsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLE1BQU0sSUFBSSxhQUFhLENBQUM7UUFDN0MsTUFBTSxhQUFhLEdBQUcsT0FBTyxDQUFDLE1BQU0sR0FBRyxDQUFDLElBQUksWUFBWSxHQUFHLElBQUksR0FBRyxhQUFhLENBQUM7UUFDaEYsSUFBSSxJQUFJLElBQUksYUFBYSxFQUFFLENBQUM7WUFDMUIsR0FBRyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUNsQixPQUFPLEdBQUcsRUFBRSxDQUFDO1lBQ2IsWUFBWSxHQUFHLENBQUMsQ0FBQztRQUNuQixDQUFDO1FBQ0QsT0FBTyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNwQixZQUFZLElBQUksSUFBSSxDQUFDO0lBQ3ZCLENBQUM7SUFDRCxJQUFJLE9BQU8sQ0FBQyxNQUFNLEdBQUcsQ0FBQztRQUFFLEdBQUcsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDMUMsT0FBTyxHQUFHLENBQUM7QUFDYixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgU05TQ2xpZW50LFxuICBQdWJsaXNoQ29tbWFuZCxcbiAgUHVibGlzaEJhdGNoQ29tbWFuZCxcbiAgdHlwZSBQdWJsaXNoQmF0Y2hSZXF1ZXN0RW50cnksXG4gIHR5cGUgTWVzc2FnZUF0dHJpYnV0ZVZhbHVlLFxufSBmcm9tICdAYXdzLXNkay9jbGllbnQtc25zJztcbmltcG9ydCB7IG1vbm90b25pY0ZhY3RvcnkgfSBmcm9tICd1bGlkJztcbmltcG9ydCB7IENvbmZpZ0Vycm9yLCBFdmVudFB1Ymxpc2hFcnJvciwgVmFsaWRhdGlvbkVycm9yIH0gZnJvbSAnLi4vZXJyb3JzJztcbmltcG9ydCB7XG4gIGRvbWFpbkZvckV2ZW50VHlwZSxcbiAgcGxhdGZvcm1FdmVudFNjaGVtYSxcbiAgUExBVEZPUk1fRVZFTlRfVkVSU0lPTixcbiAgdHlwZSBQbGF0Zm9ybUV2ZW50LFxufSBmcm9tICcuL3NjaGVtYSc7XG5cbi8qKiBTTlMgY2FwcyBQdWJsaXNoQmF0Y2ggYXQgMTAgZW50cmllcyBwZXIgY2FsbC4gKi9cbmNvbnN0IFNOU19CQVRDSF9NQVggPSAxMDtcblxuLyoqXG4gKiBTTlMgY2FwcyBhIHNpbmdsZSBtZXNzYWdlIGF0IDI1NiBLQiwgYW5kIGEgd2hvbGUgUHVibGlzaEJhdGNoIHJlcXVlc3RcbiAqIGF0IHRoZSBzYW1lIDI1NiBLQiBhY3Jvc3MgYWxsIGl0cyBlbnRyaWVzLlxuICovXG5jb25zdCBTTlNfTUFYX0JZVEVTID0gMjYyXzE0NDtcblxuLyoqXG4gKiBIZWFkcm9vbSBsZWZ0IGZvciB0aGUgYHR5cGVgIC8gYGRvbWFpbmAgLyBgc291cmNlYCBtZXNzYWdlIGF0dHJpYnV0ZXMsXG4gKiB3aGljaCBjb3VudCB0b3dhcmQgdGhlIGxpbWl0IGFsb25nc2lkZSB0aGUgYm9keS4gQSBmZXcgaHVuZHJlZCBieXRlc1xuICogY292ZXJzIHRoZW07IGJlaW5nIGNvbnNlcnZhdGl2ZSBoZXJlIGNvc3RzIG5vdGhpbmcsIHNpbmNlIGFuIGVudmVsb3BlXG4gKiBhbnl3aGVyZSBuZWFyIHRoZSBjYXAgaXMgYSBjYWxsLXNpdGUgbWlzdGFrZSBlaXRoZXIgd2F5LlxuICovXG5jb25zdCBBVFRSSUJVVEVfQUxMT1dBTkNFX0JZVEVTID0gMV8wMjQ7XG5cbmNvbnN0IE1BWF9NRVNTQUdFX0JZVEVTID0gU05TX01BWF9CWVRFUyAtIEFUVFJJQlVURV9BTExPV0FOQ0VfQllURVM7XG5cbi8qKlxuICogQnl0ZSBsZW5ndGggb2YgYSBVVEYtOCBzdHJpbmcuIGBUZXh0RW5jb2RlcmAgcmF0aGVyIHRoYW5cbiAqIGBCdWZmZXIuYnl0ZUxlbmd0aGAgc28gdGhpcyBmaWxlIGNhcnJpZXMgbm8gTm9kZSBidWlsdC1pbi5cbiAqL1xuY29uc3QgZW5jb2RlciA9IG5ldyBUZXh0RW5jb2RlcigpO1xuXG5mdW5jdGlvbiBieXRlTGVuZ3RoKHZhbHVlOiBzdHJpbmcpOiBudW1iZXIge1xuICByZXR1cm4gZW5jb2Rlci5lbmNvZGUodmFsdWUpLmxlbmd0aDtcbn1cblxuLyoqXG4gKiBNb25vdG9uaWMgd2l0aGluIHRoaXMgcHJvY2VzczogdHdvIGV2ZW50cyBtaW50ZWQgaW4gdGhlIHNhbWVcbiAqIG1pbGxpc2Vjb25kIHN0aWxsIHNvcnQgaW4gZW1pc3Npb24gb3JkZXIuIERvd25zdHJlYW0gY29uc3VtZXJzIHVzZSB0aGVcbiAqIGVudmVsb3BlIGBpZGAgYXMgdGhlIG9yZGVyaW5nIGFuZCBkZWR1cGUga2V5LCBzbyB0aGlzIGlzIGxvYWQtYmVhcmluZyxcbiAqIG5vdCBhIG5pY2V0eS5cbiAqL1xuY29uc3QgbmV4dFVsaWQgPSBtb25vdG9uaWNGYWN0b3J5KCk7XG5cbi8qKiBXaGF0IGEgY2FsbCBzaXRlIHN1cHBsaWVzOyB0aGUgU0RLIGRlcml2ZXMgZXZlcnl0aGluZyBlbHNlLiAqL1xuZXhwb3J0IGludGVyZmFjZSBQdWJsaXNoT3B0aW9ucyB7XG4gIC8qKiB1RW5nYWdlIGJ1c2luZXNzIGlkIHRoZSBldmVudCBiZWxvbmdzIHRvLiAqL1xuICB0ZW5hbnRJZDogc3RyaW5nO1xuICAvKipcbiAgICogV2hlbiB0aGUgdGhpbmcgaGFwcGVuZWQsIGlmIGl0IHdhc24ndCBub3cg4oCUIGUuZy4gcmVwbGF5aW5nIGFcbiAgICogdHJhbnNpdGlvbiByZWFkIGJhY2sgb2ZmIGEgREIgcm93LiBVVEMgSVNPIDg2MDEuIERlZmF1bHRzIHRvIHRoZVxuICAgKiBtb21lbnQgYHB1Ymxpc2goKWAgd2FzIGNhbGxlZC5cbiAgICovXG4gIG9jY3VycmVkQXQ/OiBzdHJpbmc7XG4gIC8qKiBPdmVycmlkZXMgdGhlIGNsaWVudC1sZXZlbCBgc291cmNlYCBmb3IgdGhpcyBvbmUgZXZlbnQuICovXG4gIHNvdXJjZT86IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBQdWJsaXNoQmF0Y2hJbnB1dCBleHRlbmRzIFB1Ymxpc2hPcHRpb25zIHtcbiAgdHlwZTogc3RyaW5nO1xuICBkYXRhOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBFdmVudHNDbGllbnRJbnB1dCB7XG4gIC8qKlxuICAgKiBCdXMgdG9waWMgQVJOLiBEZWZhdWx0cyB0byBgUExBVEZPUk1fRVZFTlRTX1RPUElDX0FSTmAuXG4gICAqL1xuICB0b3BpY0Fybj86IHN0cmluZztcbiAgLyoqXG4gICAqIEFXUyByZWdpb24uIERlZmF1bHRzIHRvIHRoZSByZWdpb24gZW1iZWRkZWQgaW4gYHRvcGljQXJuYCwgdGhlblxuICAgKiBgQVdTX1JFR0lPTmAgLyBgQVdTX0RFRkFVTFRfUkVHSU9OYC4gUmFyZWx5IG5lZWRzIHNldHRpbmcuXG4gICAqL1xuICByZWdpb24/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBXaGljaCBzeXN0ZW0gaXMgZW1pdHRpbmcuIERlZmF1bHRzIHRvIGBQTEFURk9STV9FVkVOVFNfU09VUkNFYCwgdGhlblxuICAgKiBgU0VSVklDRV9OQU1FYCAoc2V0IG9uIGV2ZXJ5IHBsYXRmb3JtIExhbWJkYSBieSBgTGFtYmRhU2VydmljZWApLFxuICAgKiB0aGVuIGAncGxhdGZvcm0tc2RrJ2AuXG4gICAqL1xuICBzb3VyY2U/OiBzdHJpbmc7XG4gIC8qKiBJbmplY3QgYSBwcmUtYnVpbHQgU05TIGNsaWVudCAodGVzdHMsIGN1c3RvbSBjcmVkZW50aWFscy9yZXRyeSBjb25maWcpLiAqL1xuICBzbnNDbGllbnQ/OiBTTlNDbGllbnQ7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgRXZlbnRzQ2xpZW50IHtcbiAgLyoqXG4gICAqIFB1Ymxpc2ggb25lIGV2ZW50IG9udG8gdGhlIHBsYXRmb3JtIGV2ZW50IGJ1cy5cbiAgICpcbiAgICogQnVpbGRzIHRoZSBlbnZlbG9wZSAobW9ub3RvbmljIFVMSUQgYGlkYCwgYG9jY3VycmVkQXRgLCBgdmVyc2lvbmAsXG4gICAqIGBkb21haW5gIGRlcml2ZWQgZnJvbSB0aGUgYHR5cGVgIHByZWZpeCksIHZhbGlkYXRlcyBpdCwgYW5kIHB1Ymxpc2hlc1xuICAgKiB3aXRoIGB0eXBlYCAvIGBkb21haW5gIC8gYHNvdXJjZWAgc2V0IGFzIFNOUyBtZXNzYWdlIGF0dHJpYnV0ZXMgc29cbiAgICogY29uc3VtZXIgZmlsdGVyIHBvbGljaWVzIG1hdGNoIHdpdGhvdXQgZGVzZXJpYWxpc2luZyB0aGUgYm9keS5cbiAgICpcbiAgICogVEhST1dTIG9uIGZhaWx1cmUg4oCUIGl0IGRvZXMgbm90IGZhaWwgb3Blbi4gRmFpbC1vcGVuIGlzIHRoZSBjYWxsZXInc1xuICAgKiBjYWxsIGFuZCBpdCBkaWZmZXJzIGJ5IGNhbGwgc2l0ZTogYSBsZWdhY3kgb3JkZXItc3RhdHVzIHRyYW5zaXRpb25cbiAgICogbXVzdCBuZXZlciBiZSBibG9ja2VkIGJ5IGEgYnVzIG91dGFnZSAod3JhcCBpbiB0cnkvY2F0Y2ggYW5kIGxvZyksXG4gICAqIHdoZXJlYXMgYSBzZXJ2aWNlIHRoYXQgbmVlZHMgdGhlIGV2ZW50IGRlbGl2ZXJlZCBtYXkgd2FudCB0byByZXRyeS5cbiAgICogVGhlIFBIUCBTREsncyBidWZmZXJlZCBjbGllbnQgbWFrZXMgdGhlIGZhaWwtb3BlbiBjaG9pY2UgZXhwbGljaXRseVxuICAgKiBmb3IgdGhlIGxlZ2FjeSByZXF1ZXN0IHBhdGguXG4gICAqXG4gICAqIEB0aHJvd3Mge0NvbmZpZ0Vycm9yfSBubyB0b3BpYyBBUk4gY29uZmlndXJlZFxuICAgKiBAdGhyb3dzIHtWYWxpZGF0aW9uRXJyb3J9IHRoZSBlbnZlbG9wZSBmYWlsZWQgc2NoZW1hIHZhbGlkYXRpb25cbiAgICogQHRocm93cyB7RXZlbnRQdWJsaXNoRXJyb3J9IFNOUyByZWplY3RlZCBvciBlcnJvcmVkIG9uIHRoZSBwdWJsaXNoXG4gICAqL1xuICBwdWJsaXNoKFxuICAgIHR5cGU6IHN0cmluZyxcbiAgICBkYXRhOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbiAgICBvcHRzOiBQdWJsaXNoT3B0aW9ucyxcbiAgKTogUHJvbWlzZTxQbGF0Zm9ybUV2ZW50PjtcbiAgLyoqXG4gICAqIFB1Ymxpc2ggbWFueSBldmVudHMgaW4gb25lIHJvdW5kIHRyaXAsIGNodW5rZWQgYXQgU05TJ3MgMTAtcGVyLWNhbGxcbiAgICogYFB1Ymxpc2hCYXRjaGAgbGltaXQuXG4gICAqXG4gICAqIEFsbC1vci1ub3RoaW5nIGl0IGlzIE5PVDogY2h1bmtzIGFyZSBhdHRlbXB0ZWQgaW5kZXBlbmRlbnRseSwgYW5kIGFcbiAgICogcGFydGlhbCBmYWlsdXJlIHRocm93cyBgRXZlbnRQdWJsaXNoRXJyb3JgIGNhcnJ5aW5nIHRoZSBpZHMgdGhhdCBkaWRcbiAgICogbm90IGxhbmQgaW4gYGZhaWxlZEV2ZW50SWRzYC4gRW52ZWxvcGUgdmFsaWRhdGlvbiBydW5zIG92ZXIgdGhlIHdob2xlXG4gICAqIGlucHV0IGZpcnN0LCBzbyBhIG1hbGZvcm1lZCBldmVudCBmYWlscyB0aGUgY2FsbCBiZWZvcmUgYW55dGhpbmcgaXNcbiAgICogcHVibGlzaGVkLlxuICAgKlxuICAgKiBAdGhyb3dzIHtDb25maWdFcnJvcn0gbm8gdG9waWMgQVJOIGNvbmZpZ3VyZWRcbiAgICogQHRocm93cyB7VmFsaWRhdGlvbkVycm9yfSBhbnkgZW52ZWxvcGUgZmFpbGVkIHNjaGVtYSB2YWxpZGF0aW9uXG4gICAqIEB0aHJvd3Mge0V2ZW50UHVibGlzaEVycm9yfSBvbmUgb3IgbW9yZSBldmVudHMgZGlkIG5vdCByZWFjaCB0aGUgYnVzXG4gICAqL1xuICBwdWJsaXNoQmF0Y2goZXZlbnRzOiBQdWJsaXNoQmF0Y2hJbnB1dFtdKTogUHJvbWlzZTxQbGF0Zm9ybUV2ZW50W10+O1xufVxuXG4vKipcbiAqIEJ1aWxkIGFuIGlzb2xhdGVkIGV2ZW50cyBjbGllbnQuIFJlYWRzIGBQTEFURk9STV9FVkVOVFNfVE9QSUNfQVJOYCBhbmRcbiAqIGZyaWVuZHMgZnJvbSB0aGUgZW52aXJvbm1lbnQgZm9yIGFueXRoaW5nIG5vdCBwYXNzZWQgZXhwbGljaXRseSwgc28gYVxuICogTGFtYmRhIHdob3NlIGRlcGxveSBzdGFjayBpbmplY3RlZCB0aGUgQVJOIG5lZWRzIG5vIGFyZ3VtZW50cy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUV2ZW50c0NsaWVudChpbnB1dDogRXZlbnRzQ2xpZW50SW5wdXQgPSB7fSk6IEV2ZW50c0NsaWVudCB7XG4gIGxldCBjbGllbnQ6IFNOU0NsaWVudCB8IHVuZGVmaW5lZCA9IGlucHV0LnNuc0NsaWVudDtcblxuICBmdW5jdGlvbiB0b3BpY0FybigpOiBzdHJpbmcge1xuICAgIGNvbnN0IGFybiA9IGlucHV0LnRvcGljQXJuID8/IGVudignUExBVEZPUk1fRVZFTlRTX1RPUElDX0FSTicpO1xuICAgIGlmICghYXJuKSB7XG4gICAgICB0aHJvdyBuZXcgQ29uZmlnRXJyb3IoXG4gICAgICAgICdldmVudHMucHVibGlzaDogbm8gdG9waWMgQVJOLiBQYXNzIHt0b3BpY0Fybn0gdG8gY3JlYXRlRXZlbnRzQ2xpZW50IG9yIHNldCBQTEFURk9STV9FVkVOVFNfVE9QSUNfQVJOLicsXG4gICAgICApO1xuICAgIH1cbiAgICByZXR1cm4gYXJuO1xuICB9XG5cbiAgZnVuY3Rpb24gc25zKCk6IFNOU0NsaWVudCB7XG4gICAgaWYgKCFjbGllbnQpIHtcbiAgICAgIGNvbnN0IHJlZ2lvbiA9XG4gICAgICAgIGlucHV0LnJlZ2lvbiA/PyByZWdpb25Gcm9tQXJuKHRvcGljQXJuKCkpID8/IGVudignQVdTX1JFR0lPTicpID8/IGVudignQVdTX0RFRkFVTFRfUkVHSU9OJyk7XG4gICAgICBjbGllbnQgPSBuZXcgU05TQ2xpZW50KHJlZ2lvbiA/IHsgcmVnaW9uIH0gOiB7fSk7XG4gICAgfVxuICAgIHJldHVybiBjbGllbnQ7XG4gIH1cblxuICBmdW5jdGlvbiBzb3VyY2Uob3ZlcnJpZGU/OiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIHJldHVybiAoXG4gICAgICBvdmVycmlkZSA/P1xuICAgICAgaW5wdXQuc291cmNlID8/XG4gICAgICBlbnYoJ1BMQVRGT1JNX0VWRU5UU19TT1VSQ0UnKSA/P1xuICAgICAgZW52KCdTRVJWSUNFX05BTUUnKSA/P1xuICAgICAgJ3BsYXRmb3JtLXNkaydcbiAgICApO1xuICB9XG5cbiAgZnVuY3Rpb24gYnVpbGRFbnZlbG9wZShcbiAgICB0eXBlOiBzdHJpbmcsXG4gICAgZGF0YTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gICAgb3B0czogUHVibGlzaE9wdGlvbnMsXG4gICk6IFBsYXRmb3JtRXZlbnQge1xuICAgIGNvbnN0IGRvbWFpbiA9IGRvbWFpbkZvckV2ZW50VHlwZSh0eXBlKTtcbiAgICBpZiAoIWRvbWFpbikge1xuICAgICAgdGhyb3cgbmV3IFZhbGlkYXRpb25FcnJvcihcbiAgICAgICAgYGV2ZW50cy5wdWJsaXNoOiBjYW5ub3Qgcm91dGUgZXZlbnQgdHlwZSBcIiR7dHlwZX1cIi4gSXQgbXVzdCBiZSBhIGRvdHRlZCBgICtcbiAgICAgICAgICAnXCI8cmVzb3VyY2U+LjxvcGVyYXRpb24+XCIgbmFtZSB3aG9zZSByZXNvdXJjZSBtYXBzIHRvIGEga25vd24gYnVzIGRvbWFpbiAnICtcbiAgICAgICAgICAnKG9yZGVyLCBjdXN0b21lciwgbWVudSwgb2ZmZXIsIGxveWFsdHksIHVzYWdlKS4gQWRkaW5nIGEgZG9tYWluIGlzIGEgb25lLWxpbmUgJyArXG4gICAgICAgICAgJ2VkaXQgaW4gZXZlbnRzL3NjaGVtYS50cyBhbmQgaXRzIHR3byBtaXJyb3JzLicsXG4gICAgICApO1xuICAgIH1cbiAgICBjb25zdCBjYW5kaWRhdGUgPSB7XG4gICAgICBpZDogbmV4dFVsaWQoKSxcbiAgICAgIHR5cGUsXG4gICAgICBkb21haW4sXG4gICAgICBzb3VyY2U6IHNvdXJjZShvcHRzLnNvdXJjZSksXG4gICAgICB2ZXJzaW9uOiBQTEFURk9STV9FVkVOVF9WRVJTSU9OLFxuICAgICAgb2NjdXJyZWRBdDogb3B0cy5vY2N1cnJlZEF0ID8/IGlzb05vdygpLFxuICAgICAgdGVuYW50SWQ6IG9wdHMudGVuYW50SWQsXG4gICAgICBkYXRhLFxuICAgIH07XG4gICAgY29uc3QgcmVzdWx0ID0gcGxhdGZvcm1FdmVudFNjaGVtYS5zYWZlUGFyc2UoY2FuZGlkYXRlKTtcbiAgICBpZiAoIXJlc3VsdC5zdWNjZXNzKSB7XG4gICAgICB0aHJvdyBuZXcgVmFsaWRhdGlvbkVycm9yKGBldmVudHMucHVibGlzaDogaW52YWxpZCBlbnZlbG9wZSBmb3IgXCIke3R5cGV9XCJgLCB7XG4gICAgICAgIGlzc3VlczogcmVzdWx0LmVycm9yLmlzc3VlcyxcbiAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0LmRhdGE7XG4gIH1cblxuICAvKipcbiAgICogU2VyaWFsaXplIGFuZCBzaXplLWNoZWNrIG9uZSBlbnZlbG9wZS5cbiAgICpcbiAgICogQ2hlY2tlZCBiZWZvcmUgdGhlIHB1Ymxpc2ggcmF0aGVyIHRoYW4gYWZ0ZXIgdGhlIHJlamVjdGlvbiBiZWNhdXNlXG4gICAqIFNOUydzIG93biBlcnJvciBmb3IgYW4gb3ZlcnNpemVkIG1lc3NhZ2UgbmFtZXMgbmVpdGhlciB0aGUgZXZlbnQgbm9yXG4gICAqIHRoZSBvZmZlbmRpbmcgZmllbGQsIGFuZCBvbiB0aGUgUEhQIHNpZGUgdGhlIHNhbWUgZmFpbHVyZSBkaXNhcHBlYXJzXG4gICAqIGludG8gYGVycm9yX2xvZ2AuIEJldHRlciB0byBzYXkgd2hpY2ggZXZlbnQgYW5kIGhvdyBiaWcuXG4gICAqL1xuICBmdW5jdGlvbiBzZXJpYWxpemUoZXZlbnQ6IFBsYXRmb3JtRXZlbnQpOiBzdHJpbmcge1xuICAgIGNvbnN0IG1lc3NhZ2UgPSBKU09OLnN0cmluZ2lmeShldmVudCk7XG4gICAgY29uc3Qgc2l6ZSA9IGJ5dGVMZW5ndGgobWVzc2FnZSk7XG4gICAgaWYgKHNpemUgPiBNQVhfTUVTU0FHRV9CWVRFUykge1xuICAgICAgdGhyb3cgbmV3IFZhbGlkYXRpb25FcnJvcihcbiAgICAgICAgYGV2ZW50cy5wdWJsaXNoOiBldmVudCAke2V2ZW50LmlkfSAoXCIke2V2ZW50LnR5cGV9XCIpIHNlcmlhbGl6ZXMgdG8gJHtzaXplfSBieXRlcywgYCArXG4gICAgICAgICAgYG92ZXIgdGhlICR7TUFYX01FU1NBR0VfQllURVN9LWJ5dGUgY2VpbGluZyAoU05TIGNhcHMgYSBtZXNzYWdlIGF0IDI1NktCIGluY2x1ZGluZyBgICtcbiAgICAgICAgICAnYXR0cmlidXRlcykuIFB1dCB0aGUgcGF5bG9hZCBzb21ld2hlcmUgYWRkcmVzc2FibGUgYW5kIHJlZmVyZW5jZSBpdCBmcm9tIGBkYXRhYC4nLFxuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIG1lc3NhZ2U7XG4gIH1cblxuICByZXR1cm4ge1xuICAgIGFzeW5jIHB1Ymxpc2godHlwZSwgZGF0YSwgb3B0cykge1xuICAgICAgY29uc3QgZXZlbnQgPSBidWlsZEVudmVsb3BlKHR5cGUsIGRhdGEsIG9wdHMpO1xuICAgICAgY29uc3QgbWVzc2FnZSA9IHNlcmlhbGl6ZShldmVudCk7XG4gICAgICBjb25zdCBhcm4gPSB0b3BpY0FybigpO1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgc25zKCkuc2VuZChcbiAgICAgICAgICBuZXcgUHVibGlzaENvbW1hbmQoe1xuICAgICAgICAgICAgVG9waWNBcm46IGFybixcbiAgICAgICAgICAgIE1lc3NhZ2U6IG1lc3NhZ2UsXG4gICAgICAgICAgICBNZXNzYWdlQXR0cmlidXRlczogbWVzc2FnZUF0dHJpYnV0ZXMoZXZlbnQpLFxuICAgICAgICAgIH0pLFxuICAgICAgICApO1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIHRocm93IG5ldyBFdmVudFB1Ymxpc2hFcnJvcihgZXZlbnRzLnB1Ymxpc2g6IFNOUyBwdWJsaXNoIGZhaWxlZCBmb3IgXCIke3R5cGV9XCJgLCB7XG4gICAgICAgICAgZmFpbGVkRXZlbnRJZHM6IFtldmVudC5pZF0sXG4gICAgICAgICAgY2F1c2U6IGVycixcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICByZXR1cm4gZXZlbnQ7XG4gICAgfSxcblxuICAgIGFzeW5jIHB1Ymxpc2hCYXRjaChldmVudHMpIHtcbiAgICAgIGlmIChldmVudHMubGVuZ3RoID09PSAwKSByZXR1cm4gW107XG4gICAgICBjb25zdCBlbnZlbG9wZXMgPSBldmVudHMubWFwKChlKSA9PiBidWlsZEVudmVsb3BlKGUudHlwZSwgZS5kYXRhLCBlKSk7XG4gICAgICAvLyBTZXJpYWxpemUgKGFuZCBzaXplLWNoZWNrKSBldmVyeXRoaW5nIHVwIGZyb250LCBzbyBhbiBvdmVyc2l6ZWRcbiAgICAgIC8vIGV2ZW50IGZhaWxzIHRoZSBjYWxsIGJlZm9yZSBhbnkgb2YgaXRzIHNpYmxpbmdzIGFyZSBwdWJsaXNoZWQuXG4gICAgICBjb25zdCBtZXNzYWdlcyA9IG5ldyBNYXAoZW52ZWxvcGVzLm1hcCgoZXZlbnQpID0+IFtldmVudC5pZCwgc2VyaWFsaXplKGV2ZW50KV0pKTtcbiAgICAgIGNvbnN0IGFybiA9IHRvcGljQXJuKCk7XG5cbiAgICAgIGNvbnN0IGZhaWxlZDogc3RyaW5nW10gPSBbXTtcbiAgICAgIGxldCBmaXJzdEVycm9yOiB1bmtub3duO1xuICAgICAgZm9yIChjb25zdCBncm91cCBvZiBjaHVua0J5Qnl0ZXMoZW52ZWxvcGVzLCBtZXNzYWdlcykpIHtcbiAgICAgICAgY29uc3QgZW50cmllczogUHVibGlzaEJhdGNoUmVxdWVzdEVudHJ5W10gPSBncm91cC5tYXAoKGV2ZW50KSA9PiAoe1xuICAgICAgICAgIElkOiBldmVudC5pZCxcbiAgICAgICAgICBNZXNzYWdlOiBtZXNzYWdlcy5nZXQoZXZlbnQuaWQpIGFzIHN0cmluZyxcbiAgICAgICAgICBNZXNzYWdlQXR0cmlidXRlczogbWVzc2FnZUF0dHJpYnV0ZXMoZXZlbnQpLFxuICAgICAgICB9KSk7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgcmVzcCA9IGF3YWl0IHNucygpLnNlbmQoXG4gICAgICAgICAgICBuZXcgUHVibGlzaEJhdGNoQ29tbWFuZCh7IFRvcGljQXJuOiBhcm4sIFB1Ymxpc2hCYXRjaFJlcXVlc3RFbnRyaWVzOiBlbnRyaWVzIH0pLFxuICAgICAgICAgICk7XG4gICAgICAgICAgZm9yIChjb25zdCBmIG9mIHJlc3AuRmFpbGVkID8/IFtdKSB7XG4gICAgICAgICAgICBpZiAoZi5JZCkgZmFpbGVkLnB1c2goZi5JZCk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICBmaXJzdEVycm9yID0gZmlyc3RFcnJvciA/PyBlcnI7XG4gICAgICAgICAgZm9yIChjb25zdCBldmVudCBvZiBncm91cCkgZmFpbGVkLnB1c2goZXZlbnQuaWQpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmIChmYWlsZWQubGVuZ3RoID4gMCkge1xuICAgICAgICB0aHJvdyBuZXcgRXZlbnRQdWJsaXNoRXJyb3IoXG4gICAgICAgICAgYGV2ZW50cy5wdWJsaXNoQmF0Y2g6ICR7ZmFpbGVkLmxlbmd0aH0vJHtlbnZlbG9wZXMubGVuZ3RofSBldmVudChzKSBkaWQgbm90IHJlYWNoIHRoZSBidXNgLFxuICAgICAgICAgIHsgZmFpbGVkRXZlbnRJZHM6IGZhaWxlZCwgY2F1c2U6IGZpcnN0RXJyb3IgfSxcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBlbnZlbG9wZXM7XG4gICAgfSxcbiAgfTtcbn1cblxuZnVuY3Rpb24gbWVzc2FnZUF0dHJpYnV0ZXMoZXZlbnQ6IFBsYXRmb3JtRXZlbnQpOiBSZWNvcmQ8c3RyaW5nLCBNZXNzYWdlQXR0cmlidXRlVmFsdWU+IHtcbiAgcmV0dXJuIHtcbiAgICB0eXBlOiB7IERhdGFUeXBlOiAnU3RyaW5nJywgU3RyaW5nVmFsdWU6IGV2ZW50LnR5cGUgfSxcbiAgICBkb21haW46IHsgRGF0YVR5cGU6ICdTdHJpbmcnLCBTdHJpbmdWYWx1ZTogZXZlbnQuZG9tYWluIH0sXG4gICAgc291cmNlOiB7IERhdGFUeXBlOiAnU3RyaW5nJywgU3RyaW5nVmFsdWU6IGV2ZW50LnNvdXJjZSB9LFxuICB9O1xufVxuXG4vKiogYGFybjphd3M6c25zOjxyZWdpb24+OjxhY2NvdW50Pjo8bmFtZT5gIOKGkiBgPHJlZ2lvbj5gLiAqL1xuZnVuY3Rpb24gcmVnaW9uRnJvbUFybihhcm46IHN0cmluZyk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IHBhcnRzID0gYXJuLnNwbGl0KCc6Jyk7XG4gIHJldHVybiBwYXJ0cy5sZW5ndGggPj0gNCAmJiBwYXJ0c1szXSA/IHBhcnRzWzNdIDogdW5kZWZpbmVkO1xufVxuXG4vKiogVVRDIElTTyA4NjAxLCBzZWNvbmRzIHByZWNpc2lvbiDigJQgdGhlIGZvcm1hdCB0aGUgZW52ZWxvcGUgc2NoZW1hIHBpbnMuICovXG5mdW5jdGlvbiBpc29Ob3coKTogc3RyaW5nIHtcbiAgcmV0dXJuIG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKS5yZXBsYWNlKC9cXC5cXGR7M31aJC8sICdaJyk7XG59XG5cbmZ1bmN0aW9uIGVudihuYW1lOiBzdHJpbmcpOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICBpZiAodHlwZW9mIHByb2Nlc3MgPT09ICd1bmRlZmluZWQnKSByZXR1cm4gdW5kZWZpbmVkO1xuICBjb25zdCB2YWx1ZSA9IChwcm9jZXNzIGFzIHsgZW52PzogUmVjb3JkPHN0cmluZywgc3RyaW5nIHwgdW5kZWZpbmVkPiB9KS5lbnY/LltuYW1lXTtcbiAgcmV0dXJuIHZhbHVlID09PSAnJyA/IHVuZGVmaW5lZCA6IHZhbHVlO1xufVxuXG4vKipcbiAqIEdyb3VwIGVudmVsb3BlcyBpbnRvIFB1Ymxpc2hCYXRjaCBjYWxscyBib3VuZGVkIGJ5IEJPVEggbGltaXRzOiBhdCBtb3N0XG4gKiAxMCBlbnRyaWVzLCBhbmQgYXQgbW9zdCAyNTYgS0Igb2YgY29tYmluZWQgcGF5bG9hZC5cbiAqXG4gKiBUaGUgYnl0ZSBib3VuZCBtYXR0ZXJzIGFzIHNvb24gYXMgcGF5bG9hZHMgZ2V0IHJpY2gg4oCUIHRlbiAzMCBLQlxuICogZW52ZWxvcGVzIGFyZSBlYWNoIGluZGl2aWR1YWxseSBsZWdhbCBhbmQgY29sbGVjdGl2ZWx5IG92ZXIgdGhlXG4gKiByZXF1ZXN0IGNhcCwgYW5kIFNOUyByZWplY3RzIHRoZSB3aG9sZSBjYWxsIHJhdGhlciB0aGFuIHRoZSBvdmVyZmxvdy5cbiAqL1xuZnVuY3Rpb24gY2h1bmtCeUJ5dGVzKFxuICBlbnZlbG9wZXM6IFBsYXRmb3JtRXZlbnRbXSxcbiAgbWVzc2FnZXM6IE1hcDxzdHJpbmcsIHN0cmluZz4sXG4pOiBQbGF0Zm9ybUV2ZW50W11bXSB7XG4gIGNvbnN0IG91dDogUGxhdGZvcm1FdmVudFtdW10gPSBbXTtcbiAgbGV0IGN1cnJlbnQ6IFBsYXRmb3JtRXZlbnRbXSA9IFtdO1xuICBsZXQgY3VycmVudEJ5dGVzID0gMDtcblxuICBmb3IgKGNvbnN0IGV2ZW50IG9mIGVudmVsb3Blcykge1xuICAgIGNvbnN0IHNpemUgPSBieXRlTGVuZ3RoKG1lc3NhZ2VzLmdldChldmVudC5pZCkgYXMgc3RyaW5nKSArIEFUVFJJQlVURV9BTExPV0FOQ0VfQllURVM7XG4gICAgY29uc3QgZnVsbCA9IGN1cnJlbnQubGVuZ3RoID49IFNOU19CQVRDSF9NQVg7XG4gICAgY29uc3Qgd291bGRPdmVyZmxvdyA9IGN1cnJlbnQubGVuZ3RoID4gMCAmJiBjdXJyZW50Qnl0ZXMgKyBzaXplID4gU05TX01BWF9CWVRFUztcbiAgICBpZiAoZnVsbCB8fCB3b3VsZE92ZXJmbG93KSB7XG4gICAgICBvdXQucHVzaChjdXJyZW50KTtcbiAgICAgIGN1cnJlbnQgPSBbXTtcbiAgICAgIGN1cnJlbnRCeXRlcyA9IDA7XG4gICAgfVxuICAgIGN1cnJlbnQucHVzaChldmVudCk7XG4gICAgY3VycmVudEJ5dGVzICs9IHNpemU7XG4gIH1cbiAgaWYgKGN1cnJlbnQubGVuZ3RoID4gMCkgb3V0LnB1c2goY3VycmVudCk7XG4gIHJldHVybiBvdXQ7XG59XG4iXX0=
@@ -0,0 +1,15 @@
1
+ import { type EventsClient } from './client';
2
+ export { createEventsClient, type EventsClient, type EventsClientInput, type PublishOptions, type PublishBatchInput, } from './client';
3
+ export { platformEventSchema, domainForEventType, DOMAIN_BY_RESOURCE, PLATFORM_EVENT_TYPES, PLATFORM_EVENT_VERSION, type PlatformEvent, type PlatformEventType, } from './schema';
4
+ /**
5
+ * Lazily-built default events client, configured entirely from the
6
+ * environment (`PLATFORM_EVENTS_TOPIC_ARN`, `PLATFORM_EVENTS_SOURCE`).
7
+ *
8
+ * Lazy because constructing it eagerly would build an SNS client — and
9
+ * read config that may not be set — in every process that merely imports
10
+ * the SDK. Use `createEventsClient(...)` when you need an isolated client
11
+ * with its own topic, region, or injected SNS client.
12
+ */
13
+ export declare const events: EventsClient;
14
+ /** Test-only: drop the memoised default client so env changes take effect. */
15
+ export declare function _resetEventsSingleton(): void;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.events = exports.PLATFORM_EVENT_VERSION = exports.PLATFORM_EVENT_TYPES = exports.DOMAIN_BY_RESOURCE = exports.domainForEventType = exports.platformEventSchema = exports.createEventsClient = void 0;
4
+ exports._resetEventsSingleton = _resetEventsSingleton;
5
+ const client_1 = require("./client");
6
+ var client_2 = require("./client");
7
+ Object.defineProperty(exports, "createEventsClient", { enumerable: true, get: function () { return client_2.createEventsClient; } });
8
+ var schema_1 = require("./schema");
9
+ Object.defineProperty(exports, "platformEventSchema", { enumerable: true, get: function () { return schema_1.platformEventSchema; } });
10
+ Object.defineProperty(exports, "domainForEventType", { enumerable: true, get: function () { return schema_1.domainForEventType; } });
11
+ Object.defineProperty(exports, "DOMAIN_BY_RESOURCE", { enumerable: true, get: function () { return schema_1.DOMAIN_BY_RESOURCE; } });
12
+ Object.defineProperty(exports, "PLATFORM_EVENT_TYPES", { enumerable: true, get: function () { return schema_1.PLATFORM_EVENT_TYPES; } });
13
+ Object.defineProperty(exports, "PLATFORM_EVENT_VERSION", { enumerable: true, get: function () { return schema_1.PLATFORM_EVENT_VERSION; } });
14
+ let singleton;
15
+ /**
16
+ * Lazily-built default events client, configured entirely from the
17
+ * environment (`PLATFORM_EVENTS_TOPIC_ARN`, `PLATFORM_EVENTS_SOURCE`).
18
+ *
19
+ * Lazy because constructing it eagerly would build an SNS client — and
20
+ * read config that may not be set — in every process that merely imports
21
+ * the SDK. Use `createEventsClient(...)` when you need an isolated client
22
+ * with its own topic, region, or injected SNS client.
23
+ */
24
+ exports.events = {
25
+ publish(type, data, opts) {
26
+ return defaultClient().publish(type, data, opts);
27
+ },
28
+ publishBatch(batch) {
29
+ return defaultClient().publishBatch(batch);
30
+ },
31
+ };
32
+ function defaultClient() {
33
+ if (!singleton)
34
+ singleton = (0, client_1.createEventsClient)();
35
+ return singleton;
36
+ }
37
+ /** Test-only: drop the memoised default client so env changes take effect. */
38
+ function _resetEventsSingleton() {
39
+ singleton = undefined;
40
+ }
41
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZXZlbnRzL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQTZDQSxzREFFQztBQS9DRCxxQ0FBaUU7QUFFakUsbUNBTWtCO0FBTGhCLDRHQUFBLGtCQUFrQixPQUFBO0FBTXBCLG1DQVFrQjtBQVBoQiw2R0FBQSxtQkFBbUIsT0FBQTtBQUNuQiw0R0FBQSxrQkFBa0IsT0FBQTtBQUNsQiw0R0FBQSxrQkFBa0IsT0FBQTtBQUNsQiw4R0FBQSxvQkFBb0IsT0FBQTtBQUNwQixnSEFBQSxzQkFBc0IsT0FBQTtBQUt4QixJQUFJLFNBQW1DLENBQUM7QUFFeEM7Ozs7Ozs7O0dBUUc7QUFDVSxRQUFBLE1BQU0sR0FBaUI7SUFDbEMsT0FBTyxDQUFDLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSTtRQUN0QixPQUFPLGFBQWEsRUFBRSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ25ELENBQUM7SUFDRCxZQUFZLENBQUMsS0FBSztRQUNoQixPQUFPLGFBQWEsRUFBRSxDQUFDLFlBQVksQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUM3QyxDQUFDO0NBQ0YsQ0FBQztBQUVGLFNBQVMsYUFBYTtJQUNwQixJQUFJLENBQUMsU0FBUztRQUFFLFNBQVMsR0FBRyxJQUFBLDJCQUFrQixHQUFFLENBQUM7SUFDakQsT0FBTyxTQUFTLENBQUM7QUFDbkIsQ0FBQztBQUVELDhFQUE4RTtBQUM5RSxTQUFnQixxQkFBcUI7SUFDbkMsU0FBUyxHQUFHLFNBQVMsQ0FBQztBQUN4QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgY3JlYXRlRXZlbnRzQ2xpZW50LCB0eXBlIEV2ZW50c0NsaWVudCB9IGZyb20gJy4vY2xpZW50JztcblxuZXhwb3J0IHtcbiAgY3JlYXRlRXZlbnRzQ2xpZW50LFxuICB0eXBlIEV2ZW50c0NsaWVudCxcbiAgdHlwZSBFdmVudHNDbGllbnRJbnB1dCxcbiAgdHlwZSBQdWJsaXNoT3B0aW9ucyxcbiAgdHlwZSBQdWJsaXNoQmF0Y2hJbnB1dCxcbn0gZnJvbSAnLi9jbGllbnQnO1xuZXhwb3J0IHtcbiAgcGxhdGZvcm1FdmVudFNjaGVtYSxcbiAgZG9tYWluRm9yRXZlbnRUeXBlLFxuICBET01BSU5fQllfUkVTT1VSQ0UsXG4gIFBMQVRGT1JNX0VWRU5UX1RZUEVTLFxuICBQTEFURk9STV9FVkVOVF9WRVJTSU9OLFxuICB0eXBlIFBsYXRmb3JtRXZlbnQsXG4gIHR5cGUgUGxhdGZvcm1FdmVudFR5cGUsXG59IGZyb20gJy4vc2NoZW1hJztcblxubGV0IHNpbmdsZXRvbjogRXZlbnRzQ2xpZW50IHwgdW5kZWZpbmVkO1xuXG4vKipcbiAqIExhemlseS1idWlsdCBkZWZhdWx0IGV2ZW50cyBjbGllbnQsIGNvbmZpZ3VyZWQgZW50aXJlbHkgZnJvbSB0aGVcbiAqIGVudmlyb25tZW50IChgUExBVEZPUk1fRVZFTlRTX1RPUElDX0FSTmAsIGBQTEFURk9STV9FVkVOVFNfU09VUkNFYCkuXG4gKlxuICogTGF6eSBiZWNhdXNlIGNvbnN0cnVjdGluZyBpdCBlYWdlcmx5IHdvdWxkIGJ1aWxkIGFuIFNOUyBjbGllbnQg4oCUIGFuZFxuICogcmVhZCBjb25maWcgdGhhdCBtYXkgbm90IGJlIHNldCDigJQgaW4gZXZlcnkgcHJvY2VzcyB0aGF0IG1lcmVseSBpbXBvcnRzXG4gKiB0aGUgU0RLLiBVc2UgYGNyZWF0ZUV2ZW50c0NsaWVudCguLi4pYCB3aGVuIHlvdSBuZWVkIGFuIGlzb2xhdGVkIGNsaWVudFxuICogd2l0aCBpdHMgb3duIHRvcGljLCByZWdpb24sIG9yIGluamVjdGVkIFNOUyBjbGllbnQuXG4gKi9cbmV4cG9ydCBjb25zdCBldmVudHM6IEV2ZW50c0NsaWVudCA9IHtcbiAgcHVibGlzaCh0eXBlLCBkYXRhLCBvcHRzKSB7XG4gICAgcmV0dXJuIGRlZmF1bHRDbGllbnQoKS5wdWJsaXNoKHR5cGUsIGRhdGEsIG9wdHMpO1xuICB9LFxuICBwdWJsaXNoQmF0Y2goYmF0Y2gpIHtcbiAgICByZXR1cm4gZGVmYXVsdENsaWVudCgpLnB1Ymxpc2hCYXRjaChiYXRjaCk7XG4gIH0sXG59O1xuXG5mdW5jdGlvbiBkZWZhdWx0Q2xpZW50KCk6IEV2ZW50c0NsaWVudCB7XG4gIGlmICghc2luZ2xldG9uKSBzaW5nbGV0b24gPSBjcmVhdGVFdmVudHNDbGllbnQoKTtcbiAgcmV0dXJuIHNpbmdsZXRvbjtcbn1cblxuLyoqIFRlc3Qtb25seTogZHJvcCB0aGUgbWVtb2lzZWQgZGVmYXVsdCBjbGllbnQgc28gZW52IGNoYW5nZXMgdGFrZSBlZmZlY3QuICovXG5leHBvcnQgZnVuY3Rpb24gX3Jlc2V0RXZlbnRzU2luZ2xldG9uKCk6IHZvaWQge1xuICBzaW5nbGV0b24gPSB1bmRlZmluZWQ7XG59XG4iXX0=
@@ -0,0 +1,58 @@
1
+ import { z } from 'zod';
2
+ /** Dotted `resource.operation`, e.g. `order.status_changed`. */
3
+ export declare const EVENT_TYPE_RE: RegExp;
4
+ export declare const platformEventSchema: z.ZodObject<{
5
+ id: z.ZodString;
6
+ type: z.ZodString;
7
+ domain: z.ZodString;
8
+ source: z.ZodString;
9
+ version: z.ZodNumber;
10
+ occurredAt: z.ZodString;
11
+ tenantId: z.ZodString;
12
+ data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
13
+ }, "strip", z.ZodTypeAny, {
14
+ type: string;
15
+ id: string;
16
+ domain: string;
17
+ source: string;
18
+ version: number;
19
+ occurredAt: string;
20
+ tenantId: string;
21
+ data: Record<string, unknown>;
22
+ }, {
23
+ type: string;
24
+ id: string;
25
+ domain: string;
26
+ source: string;
27
+ version: number;
28
+ occurredAt: string;
29
+ tenantId: string;
30
+ data: Record<string, unknown>;
31
+ }>;
32
+ export type PlatformEvent = z.infer<typeof platformEventSchema>;
33
+ /** Event types the bus carries in phase 1. */
34
+ export declare const PLATFORM_EVENT_TYPES: readonly ["order.created", "order.status_changed"];
35
+ export type PlatformEventType = (typeof PLATFORM_EVENT_TYPES)[number];
36
+ /** Current envelope version stamped on everything this SDK publishes. */
37
+ export declare const PLATFORM_EVENT_VERSION = 1;
38
+ /**
39
+ * Resource prefix → bus domain.
40
+ *
41
+ * An explicit map, not a pluralisation rule: the platform's domains are
42
+ * not uniformly plural (`orders` and `customers` are, `menu`, `loyalty`
43
+ * and `usage` are not).
44
+ *
45
+ * Copied from `packages/common/src/events/domains.json`, which is the
46
+ * single source of truth, because this package is published to npm and
47
+ * cannot import from a private workspace package. Exported so
48
+ * `test/events/domain-map.test.ts` can assert it against that JSON — the
49
+ * suite fails the moment the two drift.
50
+ */
51
+ export declare const DOMAIN_BY_RESOURCE: Readonly<Record<string, string>>;
52
+ /**
53
+ * The domain an event type belongs to, or null for a malformed type or
54
+ * an unregistered resource prefix. `publish()` treats null as an error:
55
+ * emitting onto a domain that does not exist is a call-site mistake, and
56
+ * nothing downstream is subscribed to catch it.
57
+ */
58
+ export declare function domainForEventType(type: string): string | null;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DOMAIN_BY_RESOURCE = exports.PLATFORM_EVENT_VERSION = exports.PLATFORM_EVENT_TYPES = exports.platformEventSchema = exports.EVENT_TYPE_RE = void 0;
4
+ exports.domainForEventType = domainForEventType;
5
+ const zod_1 = require("zod");
6
+ /**
7
+ * Platform event-bus envelope, publisher side.
8
+ *
9
+ * This mirrors `@uengage/common`'s `platformEventSchema` field for field.
10
+ * It is duplicated rather than imported because `@uengage/common` is a
11
+ * private workspace package and this SDK is published to npm — the same
12
+ * reason `audit/schema.ts` lives here instead of in common. The two are
13
+ * kept honest by `services/realtime/test/envelope.test.ts`, which
14
+ * validates an SDK-built envelope against the common schema.
15
+ */
16
+ /** ULID: Crockford base32, 26 chars, excludes I/L/O/U. */
17
+ const ULID_RE = /^[0-9A-HJKMNP-TV-Z]{26}$/;
18
+ /** Dotted `resource.operation`, e.g. `order.status_changed`. */
19
+ exports.EVENT_TYPE_RE = /^[a-z][a-z0-9_]*\.[a-z][a-z0-9_]*$/;
20
+ exports.platformEventSchema = zod_1.z.object({
21
+ id: zod_1.z.string().regex(ULID_RE, 'must be a ULID'),
22
+ type: zod_1.z.string().regex(exports.EVENT_TYPE_RE, 'must be "<resource>.<operation>"'),
23
+ domain: zod_1.z.string().regex(/^[a-z][a-z0-9_]*$/, 'must be a lowercase domain token'),
24
+ source: zod_1.z.string().min(1),
25
+ version: zod_1.z.number().int().positive(),
26
+ occurredAt: zod_1.z.string().datetime({ offset: false }),
27
+ tenantId: zod_1.z.string().min(1),
28
+ data: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()),
29
+ });
30
+ /** Event types the bus carries in phase 1. */
31
+ exports.PLATFORM_EVENT_TYPES = ['order.created', 'order.status_changed'];
32
+ /** Current envelope version stamped on everything this SDK publishes. */
33
+ exports.PLATFORM_EVENT_VERSION = 1;
34
+ /**
35
+ * Resource prefix → bus domain.
36
+ *
37
+ * An explicit map, not a pluralisation rule: the platform's domains are
38
+ * not uniformly plural (`orders` and `customers` are, `menu`, `loyalty`
39
+ * and `usage` are not).
40
+ *
41
+ * Copied from `packages/common/src/events/domains.json`, which is the
42
+ * single source of truth, because this package is published to npm and
43
+ * cannot import from a private workspace package. Exported so
44
+ * `test/events/domain-map.test.ts` can assert it against that JSON — the
45
+ * suite fails the moment the two drift.
46
+ */
47
+ exports.DOMAIN_BY_RESOURCE = {
48
+ order: 'orders',
49
+ customer: 'customers',
50
+ menu: 'menu',
51
+ offer: 'offers',
52
+ loyalty: 'loyalty',
53
+ usage: 'usage',
54
+ };
55
+ /**
56
+ * The domain an event type belongs to, or null for a malformed type or
57
+ * an unregistered resource prefix. `publish()` treats null as an error:
58
+ * emitting onto a domain that does not exist is a call-site mistake, and
59
+ * nothing downstream is subscribed to catch it.
60
+ */
61
+ function domainForEventType(type) {
62
+ if (!exports.EVENT_TYPE_RE.test(type))
63
+ return null;
64
+ const resource = type.slice(0, type.indexOf('.'));
65
+ return exports.DOMAIN_BY_RESOURCE[resource] ?? null;
66
+ }
67
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NoZW1hLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2V2ZW50cy9zY2hlbWEudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBb0VBLGdEQUlDO0FBeEVELDZCQUF3QjtBQUV4Qjs7Ozs7Ozs7O0dBU0c7QUFFSCwwREFBMEQ7QUFDMUQsTUFBTSxPQUFPLEdBQUcsMEJBQTBCLENBQUM7QUFFM0MsZ0VBQWdFO0FBQ25ELFFBQUEsYUFBYSxHQUFHLG9DQUFvQyxDQUFDO0FBRXJELFFBQUEsbUJBQW1CLEdBQUcsT0FBQyxDQUFDLE1BQU0sQ0FBQztJQUMxQyxFQUFFLEVBQUUsT0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsZ0JBQWdCLENBQUM7SUFDL0MsSUFBSSxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxLQUFLLENBQUMscUJBQWEsRUFBRSxrQ0FBa0MsQ0FBQztJQUN6RSxNQUFNLEVBQUUsT0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLEtBQUssQ0FBQyxtQkFBbUIsRUFBRSxrQ0FBa0MsQ0FBQztJQUNqRixNQUFNLEVBQUUsT0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDekIsT0FBTyxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxRQUFRLEVBQUU7SUFDcEMsVUFBVSxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDbEQsUUFBUSxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQzNCLElBQUksRUFBRSxPQUFDLENBQUMsTUFBTSxDQUFDLE9BQUMsQ0FBQyxNQUFNLEVBQUUsRUFBRSxPQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7Q0FDeEMsQ0FBQyxDQUFDO0FBSUgsOENBQThDO0FBQ2pDLFFBQUEsb0JBQW9CLEdBQUcsQ0FBQyxlQUFlLEVBQUUsc0JBQXNCLENBQVUsQ0FBQztBQUl2Rix5RUFBeUU7QUFDNUQsUUFBQSxzQkFBc0IsR0FBRyxDQUFDLENBQUM7QUFFeEM7Ozs7Ozs7Ozs7OztHQVlHO0FBQ1UsUUFBQSxrQkFBa0IsR0FBcUM7SUFDbEUsS0FBSyxFQUFFLFFBQVE7SUFDZixRQUFRLEVBQUUsV0FBVztJQUNyQixJQUFJLEVBQUUsTUFBTTtJQUNaLEtBQUssRUFBRSxRQUFRO0lBQ2YsT0FBTyxFQUFFLFNBQVM7SUFDbEIsS0FBSyxFQUFFLE9BQU87Q0FDZixDQUFDO0FBRUY7Ozs7O0dBS0c7QUFDSCxTQUFnQixrQkFBa0IsQ0FBQyxJQUFZO0lBQzdDLElBQUksQ0FBQyxxQkFBYSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUM7UUFBRSxPQUFPLElBQUksQ0FBQztJQUMzQyxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDbEQsT0FBTywwQkFBa0IsQ0FBQyxRQUFRLENBQUMsSUFBSSxJQUFJLENBQUM7QUFDOUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHogfSBmcm9tICd6b2QnO1xuXG4vKipcbiAqIFBsYXRmb3JtIGV2ZW50LWJ1cyBlbnZlbG9wZSwgcHVibGlzaGVyIHNpZGUuXG4gKlxuICogVGhpcyBtaXJyb3JzIGBAdWVuZ2FnZS9jb21tb25gJ3MgYHBsYXRmb3JtRXZlbnRTY2hlbWFgIGZpZWxkIGZvciBmaWVsZC5cbiAqIEl0IGlzIGR1cGxpY2F0ZWQgcmF0aGVyIHRoYW4gaW1wb3J0ZWQgYmVjYXVzZSBgQHVlbmdhZ2UvY29tbW9uYCBpcyBhXG4gKiBwcml2YXRlIHdvcmtzcGFjZSBwYWNrYWdlIGFuZCB0aGlzIFNESyBpcyBwdWJsaXNoZWQgdG8gbnBtIOKAlCB0aGUgc2FtZVxuICogcmVhc29uIGBhdWRpdC9zY2hlbWEudHNgIGxpdmVzIGhlcmUgaW5zdGVhZCBvZiBpbiBjb21tb24uIFRoZSB0d28gYXJlXG4gKiBrZXB0IGhvbmVzdCBieSBgc2VydmljZXMvcmVhbHRpbWUvdGVzdC9lbnZlbG9wZS50ZXN0LnRzYCwgd2hpY2hcbiAqIHZhbGlkYXRlcyBhbiBTREstYnVpbHQgZW52ZWxvcGUgYWdhaW5zdCB0aGUgY29tbW9uIHNjaGVtYS5cbiAqL1xuXG4vKiogVUxJRDogQ3JvY2tmb3JkIGJhc2UzMiwgMjYgY2hhcnMsIGV4Y2x1ZGVzIEkvTC9PL1UuICovXG5jb25zdCBVTElEX1JFID0gL15bMC05QS1ISktNTlAtVFYtWl17MjZ9JC87XG5cbi8qKiBEb3R0ZWQgYHJlc291cmNlLm9wZXJhdGlvbmAsIGUuZy4gYG9yZGVyLnN0YXR1c19jaGFuZ2VkYC4gKi9cbmV4cG9ydCBjb25zdCBFVkVOVF9UWVBFX1JFID0gL15bYS16XVthLXowLTlfXSpcXC5bYS16XVthLXowLTlfXSokLztcblxuZXhwb3J0IGNvbnN0IHBsYXRmb3JtRXZlbnRTY2hlbWEgPSB6Lm9iamVjdCh7XG4gIGlkOiB6LnN0cmluZygpLnJlZ2V4KFVMSURfUkUsICdtdXN0IGJlIGEgVUxJRCcpLFxuICB0eXBlOiB6LnN0cmluZygpLnJlZ2V4KEVWRU5UX1RZUEVfUkUsICdtdXN0IGJlIFwiPHJlc291cmNlPi48b3BlcmF0aW9uPlwiJyksXG4gIGRvbWFpbjogei5zdHJpbmcoKS5yZWdleCgvXlthLXpdW2EtejAtOV9dKiQvLCAnbXVzdCBiZSBhIGxvd2VyY2FzZSBkb21haW4gdG9rZW4nKSxcbiAgc291cmNlOiB6LnN0cmluZygpLm1pbigxKSxcbiAgdmVyc2lvbjogei5udW1iZXIoKS5pbnQoKS5wb3NpdGl2ZSgpLFxuICBvY2N1cnJlZEF0OiB6LnN0cmluZygpLmRhdGV0aW1lKHsgb2Zmc2V0OiBmYWxzZSB9KSxcbiAgdGVuYW50SWQ6IHouc3RyaW5nKCkubWluKDEpLFxuICBkYXRhOiB6LnJlY29yZCh6LnN0cmluZygpLCB6LnVua25vd24oKSksXG59KTtcblxuZXhwb3J0IHR5cGUgUGxhdGZvcm1FdmVudCA9IHouaW5mZXI8dHlwZW9mIHBsYXRmb3JtRXZlbnRTY2hlbWE+O1xuXG4vKiogRXZlbnQgdHlwZXMgdGhlIGJ1cyBjYXJyaWVzIGluIHBoYXNlIDEuICovXG5leHBvcnQgY29uc3QgUExBVEZPUk1fRVZFTlRfVFlQRVMgPSBbJ29yZGVyLmNyZWF0ZWQnLCAnb3JkZXIuc3RhdHVzX2NoYW5nZWQnXSBhcyBjb25zdDtcblxuZXhwb3J0IHR5cGUgUGxhdGZvcm1FdmVudFR5cGUgPSAodHlwZW9mIFBMQVRGT1JNX0VWRU5UX1RZUEVTKVtudW1iZXJdO1xuXG4vKiogQ3VycmVudCBlbnZlbG9wZSB2ZXJzaW9uIHN0YW1wZWQgb24gZXZlcnl0aGluZyB0aGlzIFNESyBwdWJsaXNoZXMuICovXG5leHBvcnQgY29uc3QgUExBVEZPUk1fRVZFTlRfVkVSU0lPTiA9IDE7XG5cbi8qKlxuICogUmVzb3VyY2UgcHJlZml4IOKGkiBidXMgZG9tYWluLlxuICpcbiAqIEFuIGV4cGxpY2l0IG1hcCwgbm90IGEgcGx1cmFsaXNhdGlvbiBydWxlOiB0aGUgcGxhdGZvcm0ncyBkb21haW5zIGFyZVxuICogbm90IHVuaWZvcm1seSBwbHVyYWwgKGBvcmRlcnNgIGFuZCBgY3VzdG9tZXJzYCBhcmUsIGBtZW51YCwgYGxveWFsdHlgXG4gKiBhbmQgYHVzYWdlYCBhcmUgbm90KS5cbiAqXG4gKiBDb3BpZWQgZnJvbSBgcGFja2FnZXMvY29tbW9uL3NyYy9ldmVudHMvZG9tYWlucy5qc29uYCwgd2hpY2ggaXMgdGhlXG4gKiBzaW5nbGUgc291cmNlIG9mIHRydXRoLCBiZWNhdXNlIHRoaXMgcGFja2FnZSBpcyBwdWJsaXNoZWQgdG8gbnBtIGFuZFxuICogY2Fubm90IGltcG9ydCBmcm9tIGEgcHJpdmF0ZSB3b3Jrc3BhY2UgcGFja2FnZS4gRXhwb3J0ZWQgc29cbiAqIGB0ZXN0L2V2ZW50cy9kb21haW4tbWFwLnRlc3QudHNgIGNhbiBhc3NlcnQgaXQgYWdhaW5zdCB0aGF0IEpTT04g4oCUIHRoZVxuICogc3VpdGUgZmFpbHMgdGhlIG1vbWVudCB0aGUgdHdvIGRyaWZ0LlxuICovXG5leHBvcnQgY29uc3QgRE9NQUlOX0JZX1JFU09VUkNFOiBSZWFkb25seTxSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+PiA9IHtcbiAgb3JkZXI6ICdvcmRlcnMnLFxuICBjdXN0b21lcjogJ2N1c3RvbWVycycsXG4gIG1lbnU6ICdtZW51JyxcbiAgb2ZmZXI6ICdvZmZlcnMnLFxuICBsb3lhbHR5OiAnbG95YWx0eScsXG4gIHVzYWdlOiAndXNhZ2UnLFxufTtcblxuLyoqXG4gKiBUaGUgZG9tYWluIGFuIGV2ZW50IHR5cGUgYmVsb25ncyB0bywgb3IgbnVsbCBmb3IgYSBtYWxmb3JtZWQgdHlwZSBvclxuICogYW4gdW5yZWdpc3RlcmVkIHJlc291cmNlIHByZWZpeC4gYHB1Ymxpc2goKWAgdHJlYXRzIG51bGwgYXMgYW4gZXJyb3I6XG4gKiBlbWl0dGluZyBvbnRvIGEgZG9tYWluIHRoYXQgZG9lcyBub3QgZXhpc3QgaXMgYSBjYWxsLXNpdGUgbWlzdGFrZSwgYW5kXG4gKiBub3RoaW5nIGRvd25zdHJlYW0gaXMgc3Vic2NyaWJlZCB0byBjYXRjaCBpdC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRvbWFpbkZvckV2ZW50VHlwZSh0eXBlOiBzdHJpbmcpOiBzdHJpbmcgfCBudWxsIHtcbiAgaWYgKCFFVkVOVF9UWVBFX1JFLnRlc3QodHlwZSkpIHJldHVybiBudWxsO1xuICBjb25zdCByZXNvdXJjZSA9IHR5cGUuc2xpY2UoMCwgdHlwZS5pbmRleE9mKCcuJykpO1xuICByZXR1cm4gRE9NQUlOX0JZX1JFU09VUkNFW3Jlc291cmNlXSA/PyBudWxsO1xufVxuIl19
@@ -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;