@doow/track 0.1.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,477 @@
1
+ 'use strict';
2
+
3
+ var zlib = require('zlib');
4
+ var uuid = require('./uuid.js');
5
+
6
+ /**
7
+ * S78: Exporter — HTTP transport + compression + retry
8
+ *
9
+ * Handles the *how* of delivery:
10
+ * - POST to {endpoint}/telemetry/events with gzip
11
+ * - Exponential backoff with ±20% jitter, max retryCount retries
12
+ * - 429 respects Retry-After header
13
+ * - 413 adaptive batch halving
14
+ * - Serialized flushes (one in-flight at a time)
15
+ * - Custom transport for testing/mTLS
16
+ * - onError callback — SDK never throws
17
+ */
18
+ async function gzipBuffer(data) {
19
+ return new Promise((resolve, reject) => {
20
+ const chunks = [];
21
+ const gz = zlib.createGzip();
22
+ gz.on('data', (chunk) => chunks.push(chunk));
23
+ gz.on('end', () => resolve(Buffer.concat(chunks)));
24
+ gz.on('error', reject);
25
+ gz.end(data);
26
+ });
27
+ }
28
+ const SDK_VERSION = "0.1.0";
29
+ function toWireEvent(event) {
30
+ var _a;
31
+ const sourceSystem = ((_a = event.source_system) === null || _a === void 0 ? void 0 : _a.trim()) ? event.source_system : 'sdk';
32
+ const metricTupleHint = event.metric_tuple_hint;
33
+ return {
34
+ ...event,
35
+ occurred_at: event.timestamp,
36
+ source_system: sourceSystem,
37
+ ...(metricTupleHint ? { metric_tuple_hint: metricTupleHint } : {}),
38
+ measurements: [
39
+ {
40
+ metric_name: event.metric,
41
+ quantity: event.quantity,
42
+ ...(metricTupleHint ? { metric_tuple_hint: metricTupleHint } : {}),
43
+ },
44
+ ],
45
+ };
46
+ }
47
+ function toBatchPayload(batchId, events) {
48
+ return {
49
+ batch_id: batchId,
50
+ sdk_version: SDK_VERSION,
51
+ events: events.map(toWireEvent),
52
+ };
53
+ }
54
+ class Exporter {
55
+ constructor(config) {
56
+ this._pendingFlush = null;
57
+ this._adaptiveBatchSize = null;
58
+ this._stopped = false;
59
+ this._concurrentCount = 0;
60
+ this._concurrentQueue = [];
61
+ /** S79: Per-category rate limit state. Key = category name, value = expiry timestamp in ms */
62
+ this._rateLimits = new Map();
63
+ this.config = config;
64
+ }
65
+ /** Serialized flush: one HTTP request in-flight at a time */
66
+ async flush(events) {
67
+ var _a;
68
+ this._evictExpiredLimits();
69
+ // S79: If any rate limit is currently active, hold events (don't drop)
70
+ if (this._isRateLimited()) {
71
+ this.config.debug.log('Rate limited — holding events, skipping flush');
72
+ return;
73
+ }
74
+ // Chain onto any pending flush
75
+ const chain = (_a = this._pendingFlush) !== null && _a !== void 0 ? _a : Promise.resolve();
76
+ let resolve;
77
+ const next = new Promise((r) => {
78
+ resolve = r;
79
+ });
80
+ this._pendingFlush = next;
81
+ await chain;
82
+ try {
83
+ // S80: Drain offline store FIFO before new events
84
+ await this._drainOfflineStore();
85
+ await this._doFlush(events);
86
+ }
87
+ finally {
88
+ resolve();
89
+ if (this._pendingFlush === next) {
90
+ this._pendingFlush = null;
91
+ }
92
+ }
93
+ }
94
+ /** Mark stopped — 401 auth failure */
95
+ stop() {
96
+ this._stopped = true;
97
+ }
98
+ get stopped() {
99
+ return this._stopped;
100
+ }
101
+ /** S79: True if any active rate limit has not yet expired */
102
+ get rateLimited() {
103
+ return this._isRateLimited();
104
+ }
105
+ /** Wait for any in-flight flush to complete */
106
+ async drain() {
107
+ if (this._pendingFlush) {
108
+ await this._pendingFlush;
109
+ }
110
+ }
111
+ // ─── S79: Rate limit helpers ──────────────────────────────────────────────
112
+ _isRateLimited() {
113
+ const now = Date.now();
114
+ for (const [, entry] of this._rateLimits) {
115
+ if (now < entry.expiresAt)
116
+ return true;
117
+ }
118
+ return false;
119
+ }
120
+ _evictExpiredLimits() {
121
+ const now = Date.now();
122
+ for (const [category, entry] of this._rateLimits) {
123
+ if (now >= entry.expiresAt) {
124
+ this._rateLimits.delete(category);
125
+ }
126
+ }
127
+ }
128
+ /** Parse and apply X-Doow-Rate-Limits header from a 429 response */
129
+ _applyRateLimitHeader(headers) {
130
+ var _a;
131
+ const raw = (_a = headers['x-doow-rate-limits']) !== null && _a !== void 0 ? _a : headers['X-Doow-Rate-Limits'];
132
+ if (!raw)
133
+ return;
134
+ try {
135
+ const parsed = JSON.parse(raw);
136
+ const now = Date.now();
137
+ for (const [category, entry] of Object.entries(parsed)) {
138
+ if (typeof entry === 'object' &&
139
+ entry !== null &&
140
+ typeof entry.retry_after === 'number' &&
141
+ entry.retry_after > 0) {
142
+ const expiresAt = now + entry.retry_after * 1000;
143
+ this._rateLimits.set(category, { expiresAt });
144
+ this.config.debug.log(`Rate limit set: ${category} expires in ${entry.retry_after}s`);
145
+ }
146
+ }
147
+ }
148
+ catch {
149
+ // Malformed header — ignore gracefully
150
+ this.config.debug.warn('Malformed X-Doow-Rate-Limits header — ignored');
151
+ }
152
+ }
153
+ // ─── S80: Offline store helpers ───────────────────────────────────────────
154
+ /** Drain the offline store FIFO before sending new events */
155
+ async _drainOfflineStore() {
156
+ const store = this.config.offlineStore;
157
+ if (!store)
158
+ return;
159
+ try {
160
+ const len = await store.length();
161
+ if (len === 0)
162
+ return;
163
+ this.config.debug.log(`Draining ${len} batches from offline store`);
164
+ }
165
+ catch {
166
+ return;
167
+ }
168
+ // Drain FIFO — shift one batch at a time
169
+ let batch;
170
+ while ((batch = await store.shift()) !== undefined) {
171
+ try {
172
+ const payload = JSON.parse(batch.payload);
173
+ if (payload.events && payload.events.length > 0) {
174
+ await this._sendWithRetry(payload.events, this.config.retryCount, payload.batch_id);
175
+ }
176
+ }
177
+ catch {
178
+ this.config.debug.warn(`Failed to replay offline batch ${batch.batch_id}`);
179
+ }
180
+ }
181
+ }
182
+ async _doFlush(events) {
183
+ var _a;
184
+ if (this._stopped || events.length === 0)
185
+ return;
186
+ const maxBatch = (_a = this._adaptiveBatchSize) !== null && _a !== void 0 ? _a : 500;
187
+ // Split into ≤500 event chunks
188
+ const chunks = [];
189
+ for (let i = 0; i < events.length; i += maxBatch) {
190
+ chunks.push(events.slice(i, i + maxBatch));
191
+ }
192
+ for (const chunk of chunks) {
193
+ await this._sendWithRetry(chunk, this.config.retryCount);
194
+ }
195
+ }
196
+ async _sendWithRetry(events, retriesLeft, batchId) {
197
+ var _a, _b;
198
+ if (this._stopped)
199
+ return;
200
+ const resolvedBatchId = batchId !== null && batchId !== void 0 ? batchId : uuid.generateUUID();
201
+ try {
202
+ await this._withConcurrencyLimit(() => this._sendOnce(events, resolvedBatchId));
203
+ }
204
+ catch (err) {
205
+ const sdkErr = err;
206
+ if (sdkErr.statusCode === 401) {
207
+ // Auth failure — stop permanently
208
+ this._stopped = true;
209
+ this.config.onError({
210
+ kind: 'AUTH_FAILURE',
211
+ message: 'API key rejected (401). Stopping SDK.',
212
+ statusCode: 401,
213
+ });
214
+ return;
215
+ }
216
+ if (sdkErr.statusCode === 413) {
217
+ // Adaptive recovery — halve batch
218
+ const half = Math.max(1, Math.floor(events.length / 2));
219
+ this._adaptiveBatchSize = half;
220
+ this.config.debug.warn(`413 received — adaptive batch size reduced to ${half}`);
221
+ const first = events.slice(0, half);
222
+ const second = events.slice(half);
223
+ // Retry each half with full retry budget
224
+ await this._sendWithRetry(first, this.config.retryCount, resolvedBatchId);
225
+ if (second.length > 0) {
226
+ await this._sendWithRetry(second, this.config.retryCount);
227
+ }
228
+ return;
229
+ }
230
+ if (sdkErr.statusCode === 429) {
231
+ // S79: Apply rate limit header from the response if present
232
+ if (sdkErr.rateLimitHeaders) {
233
+ this._applyRateLimitHeader(sdkErr.rateLimitHeaders);
234
+ }
235
+ const retryAfterMs = (_a = sdkErr.retryAfterMs) !== null && _a !== void 0 ? _a : this._backoff(this.config.retryCount - retriesLeft);
236
+ this.config.debug.log(`429 rate limited — waiting ${retryAfterMs}ms`);
237
+ this.config.onError({
238
+ kind: 'RATE_LIMITED',
239
+ message: `Rate limited (429). Retry after ${retryAfterMs}ms.`,
240
+ statusCode: 429,
241
+ retryAfterMs,
242
+ });
243
+ if (retriesLeft <= 0)
244
+ return;
245
+ await this._sleep(retryAfterMs);
246
+ return await this._sendWithRetry(events, retriesLeft - 1, resolvedBatchId);
247
+ }
248
+ if (retriesLeft <= 0) {
249
+ const errPayload = {
250
+ kind: (_b = sdkErr.kind) !== null && _b !== void 0 ? _b : 'NETWORK_ERROR',
251
+ message: sdkErr.message,
252
+ rejectedEventIds: events.map((e) => e.event_id),
253
+ };
254
+ if (sdkErr.statusCode !== undefined)
255
+ errPayload.statusCode = sdkErr.statusCode;
256
+ if (sdkErr.cause instanceof Error)
257
+ errPayload.error = sdkErr.cause;
258
+ this.config.onError(errPayload);
259
+ // S80: Persist to offline store if configured
260
+ await this._persistToOfflineStore(resolvedBatchId, events);
261
+ return;
262
+ }
263
+ // Exponential backoff for non-429 retryable errors
264
+ const delay = this._backoff(this.config.retryCount - retriesLeft);
265
+ this.config.debug.log(`Retry in ${delay}ms, ${retriesLeft - 1} retries left`);
266
+ await this._sleep(delay);
267
+ await this._sendWithRetry(events, retriesLeft - 1, resolvedBatchId);
268
+ }
269
+ }
270
+ /** S80: Persist a failed batch to the offline store */
271
+ async _persistToOfflineStore(batchId, events) {
272
+ const store = this.config.offlineStore;
273
+ if (!store)
274
+ return;
275
+ try {
276
+ const payload = toBatchPayload(batchId, events);
277
+ const serialized = {
278
+ batch_id: batchId,
279
+ payload: JSON.stringify(payload),
280
+ timestamp: new Date().toISOString(),
281
+ };
282
+ await store.push(serialized);
283
+ this.config.debug.log(`Persisted failed batch ${batchId} to offline store`);
284
+ }
285
+ catch {
286
+ this.config.debug.warn(`Failed to persist batch ${batchId} to offline store`);
287
+ }
288
+ }
289
+ async _sendOnce(events, batchId) {
290
+ var _a, _b, _c;
291
+ const payload = toBatchPayload(batchId, events);
292
+ const jsonBuffer = Buffer.from(JSON.stringify(payload));
293
+ let body;
294
+ const headers = {
295
+ Authorization: `Bearer ${this.config.apiKey}`,
296
+ 'Content-Type': 'application/json',
297
+ 'X-Doow-SDK-Version': SDK_VERSION,
298
+ };
299
+ if (this.config.disableCompression) {
300
+ body = jsonBuffer;
301
+ }
302
+ else {
303
+ body = await gzipBuffer(jsonBuffer);
304
+ headers['Content-Encoding'] = 'gzip';
305
+ }
306
+ headers['Content-Length'] = String(body.length);
307
+ const url = `${this.config.endpoint}/telemetry/events`;
308
+ const transportPayload = { body, headers, url };
309
+ this.config.debug.log(`POST ${url} batch_id=${batchId} events=${events.length} bytes=${body.length}`);
310
+ let response;
311
+ try {
312
+ if (this.config.transport) {
313
+ response = await this._withTimeout(this.config.transport.send(transportPayload), this.config.timeout);
314
+ }
315
+ else {
316
+ response = await this._withTimeout(this._defaultTransport(transportPayload), this.config.timeout);
317
+ }
318
+ }
319
+ catch (err) {
320
+ const e = err;
321
+ if (e.name === 'AbortError' || ((_a = e.message) === null || _a === void 0 ? void 0 : _a.includes('timeout'))) {
322
+ throw new SdkHttpError('TIMEOUT', `Request timed out after ${this.config.timeout}ms`, undefined, undefined, e);
323
+ }
324
+ throw new SdkHttpError('NETWORK_ERROR', `Network error: ${e.message}`, undefined, undefined, e);
325
+ }
326
+ this.config.debug.log(`Response ${response.status} batch_id=${batchId}`);
327
+ if (response.status === 202) {
328
+ // Success — reset adaptive batch size
329
+ this._adaptiveBatchSize = null;
330
+ return;
331
+ }
332
+ if (response.status === 207) {
333
+ // Partial accept
334
+ try {
335
+ const parsed = JSON.parse(response.body);
336
+ if (((_b = parsed.rejected) === null || _b === void 0 ? void 0 : _b.length) > 0) {
337
+ this.config.onError({
338
+ kind: 'PARTIAL_ACCEPT',
339
+ message: `Batch partially accepted — ${parsed.rejected.length} events rejected`,
340
+ statusCode: 207,
341
+ rejectedEventIds: parsed.rejected.map((r) => r.event_id),
342
+ });
343
+ }
344
+ }
345
+ catch {
346
+ // Non-JSON 207 — treat as success
347
+ }
348
+ this._adaptiveBatchSize = null;
349
+ return;
350
+ }
351
+ if (response.status === 401) {
352
+ throw new SdkHttpError('AUTH_FAILURE', 'Unauthorized (401)', 401);
353
+ }
354
+ if (response.status === 413) {
355
+ throw new SdkHttpError('TRANSPORT_ERROR', 'Payload too large (413)', 413);
356
+ }
357
+ if (response.status === 429) {
358
+ const retryAfterMs = this._parseRetryAfter((_c = response.headers['retry-after']) !== null && _c !== void 0 ? _c : response.headers['Retry-After']);
359
+ throw new SdkHttpError('RATE_LIMITED', `Rate limited (429)`, 429, retryAfterMs, undefined, response.headers);
360
+ }
361
+ if (response.status >= 500) {
362
+ throw new SdkHttpError('TRANSPORT_ERROR', `Server error ${response.status}`, response.status);
363
+ }
364
+ // Other 4xx — don't retry
365
+ throw new SdkHttpError('TRANSPORT_ERROR', `HTTP ${response.status}`, response.status);
366
+ }
367
+ async _defaultTransport(payload) {
368
+ const { default: https } = await import('https');
369
+ const { default: http } = await import('http');
370
+ return new Promise((resolve, reject) => {
371
+ const url = new URL(payload.url);
372
+ const isHttps = url.protocol === 'https:';
373
+ const client = isHttps ? https : http;
374
+ const req = client.request({
375
+ hostname: url.hostname,
376
+ port: url.port || (isHttps ? 443 : 80),
377
+ path: url.pathname + url.search,
378
+ method: 'POST',
379
+ headers: payload.headers,
380
+ }, (res) => {
381
+ const chunks = [];
382
+ let totalBytes = 0;
383
+ const maxBodyBytes = 1024 * 1024; // 1 MB cap
384
+ res.on('data', (chunk) => {
385
+ totalBytes += chunk.length;
386
+ if (totalBytes <= maxBodyBytes)
387
+ chunks.push(chunk);
388
+ });
389
+ res.on('end', () => {
390
+ var _a, _b;
391
+ const body = Buffer.concat(chunks).toString('utf8');
392
+ const headers = {};
393
+ for (const [k, v] of Object.entries(res.headers)) {
394
+ if (typeof v === 'string')
395
+ headers[k] = v;
396
+ else if (Array.isArray(v))
397
+ headers[k] = (_a = v[0]) !== null && _a !== void 0 ? _a : '';
398
+ }
399
+ resolve({ status: (_b = res.statusCode) !== null && _b !== void 0 ? _b : 0, headers, body });
400
+ });
401
+ res.on('error', reject);
402
+ });
403
+ req.on('error', reject);
404
+ req.write(payload.body);
405
+ req.end();
406
+ });
407
+ }
408
+ async _withTimeout(promise, timeoutMs) {
409
+ return new Promise((resolve, reject) => {
410
+ const timer = setTimeout(() => {
411
+ const err = new Error(`Timeout after ${timeoutMs}ms`);
412
+ err.name = 'AbortError';
413
+ reject(err);
414
+ }, timeoutMs);
415
+ promise.then((v) => {
416
+ clearTimeout(timer);
417
+ resolve(v);
418
+ }, (e) => {
419
+ clearTimeout(timer);
420
+ reject(e);
421
+ });
422
+ });
423
+ }
424
+ async _withConcurrencyLimit(fn) {
425
+ const max = this.config.maxConcurrentFlushes;
426
+ if (this._concurrentCount >= max) {
427
+ // Queue and wait
428
+ await new Promise((resolve) => this._concurrentQueue.push(resolve));
429
+ }
430
+ this._concurrentCount++;
431
+ try {
432
+ return await fn();
433
+ }
434
+ finally {
435
+ this._concurrentCount--;
436
+ const next = this._concurrentQueue.shift();
437
+ if (next)
438
+ next();
439
+ }
440
+ }
441
+ /** Exponential backoff with ±20% jitter */
442
+ _backoff(attempt) {
443
+ const base = Math.min(1000 * Math.pow(2, attempt), 16000);
444
+ const jitter = base * 0.2 * (Math.random() * 2 - 1);
445
+ return Math.round(base + jitter);
446
+ }
447
+ _parseRetryAfter(header) {
448
+ if (!header)
449
+ return undefined;
450
+ const seconds = parseFloat(header);
451
+ if (!isNaN(seconds))
452
+ return Math.round(seconds * 1000);
453
+ const date = new Date(header).getTime();
454
+ if (!isNaN(date))
455
+ return Math.max(0, date - Date.now());
456
+ return undefined;
457
+ }
458
+ _sleep(ms) {
459
+ return new Promise((resolve) => setTimeout(resolve, ms));
460
+ }
461
+ }
462
+ class SdkHttpError extends Error {
463
+ constructor(kind, message, statusCode, retryAfterMs, cause,
464
+ /** S79: Response headers from the 429 response — used to parse X-Doow-Rate-Limits */
465
+ rateLimitHeaders) {
466
+ super(message);
467
+ this.kind = kind;
468
+ this.statusCode = statusCode;
469
+ this.retryAfterMs = retryAfterMs;
470
+ this.cause = cause;
471
+ this.rateLimitHeaders = rateLimitHeaders;
472
+ this.name = 'SdkHttpError';
473
+ }
474
+ }
475
+
476
+ exports.Exporter = Exporter;
477
+ //# sourceMappingURL=exporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exporter.js","sources":["../../../src/exporter.ts"],"sourcesContent":[null],"names":["createGzip","generateUUID"],"mappings":";;;;;AAAA;;;;;;;;;;;AAWG;AAiBH,eAAe,UAAU,CAAC,IAAY,EAAA;IACpC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,KAAI;QACrC,MAAM,MAAM,GAAa,EAAE;AAC3B,QAAA,MAAM,EAAE,GAAGA,eAAU,EAAE;AACvB,QAAA,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,KAAK,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACpD,QAAA,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;AAClD,QAAA,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;AACtB,QAAA,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC;AACd,IAAA,CAAC,CAAC;AACJ;AAIA,MAAM,WAAW,GAAG,OAAe;AAEnC,SAAS,WAAW,CAAC,KAAsB,EAAA;;IACzC,MAAM,YAAY,GAAG,CAAA,CAAA,EAAA,GAAA,KAAK,CAAC,aAAa,0CAAE,IAAI,EAAE,IAAG,KAAK,CAAC,aAAa,GAAG,KAAK;AAC9E,IAAA,MAAM,eAAe,GAAG,KAAK,CAAC,iBAAiB;IAE/C,OAAO;AACL,QAAA,GAAG,KAAK;QACR,WAAW,EAAE,KAAK,CAAC,SAAS;AAC5B,QAAA,aAAa,EAAE,YAAY;AAC3B,QAAA,IAAI,eAAe,GAAG,EAAE,iBAAiB,EAAE,eAAe,EAAE,GAAG,EAAE,CAAC;AAClE,QAAA,YAAY,EAAE;AACZ,YAAA;gBACE,WAAW,EAAE,KAAK,CAAC,MAAM;gBACzB,QAAQ,EAAE,KAAK,CAAC,QAAQ;AACxB,gBAAA,IAAI,eAAe,GAAG,EAAE,iBAAiB,EAAE,eAAe,EAAE,GAAG,EAAE,CAAC;AACnE,aAAA;AACF,SAAA;KACF;AACH;AAEA,SAAS,cAAc,CAAC,OAAe,EAAE,MAAyB,EAAA;IAChE,OAAO;AACL,QAAA,QAAQ,EAAE,OAAO;AACjB,QAAA,WAAW,EAAE,WAAW;AACxB,QAAA,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC;KAChC;AACH;MA2Ba,QAAQ,CAAA;AAUnB,IAAA,WAAA,CAAY,MAAsB,EAAA;QAR1B,IAAA,CAAA,aAAa,GAAyB,IAAI;QAC1C,IAAA,CAAA,kBAAkB,GAAkB,IAAI;QACxC,IAAA,CAAA,QAAQ,GAAG,KAAK;QAChB,IAAA,CAAA,gBAAgB,GAAG,CAAC;QACX,IAAA,CAAA,gBAAgB,GAAsB,EAAE;;AAExC,QAAA,IAAA,CAAA,WAAW,GAAuC,IAAI,GAAG,EAAE;AAG1E,QAAA,IAAI,CAAC,MAAM,GAAG,MAAM;IACtB;;IAGA,MAAM,KAAK,CAAC,MAAyB,EAAA;;QACnC,IAAI,CAAC,mBAAmB,EAAE;;AAG1B,QAAA,IAAI,IAAI,CAAC,cAAc,EAAE,EAAE;YACzB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,+CAA+C,CAAC;YACtE;QACF;;QAGA,MAAM,KAAK,GAAG,CAAA,EAAA,GAAA,IAAI,CAAC,aAAa,MAAA,IAAA,IAAA,EAAA,KAAA,MAAA,GAAA,EAAA,GAAI,OAAO,CAAC,OAAO,EAAE;AACrD,QAAA,IAAI,OAAoB;QACxB,MAAM,IAAI,GAAG,IAAI,OAAO,CAAO,CAAC,CAAC,KAAI;YACnC,OAAO,GAAG,CAAC;AACb,QAAA,CAAC,CAAC;AACF,QAAA,IAAI,CAAC,aAAa,GAAG,IAAI;AAEzB,QAAA,MAAM,KAAK;AACX,QAAA,IAAI;;AAEF,YAAA,MAAM,IAAI,CAAC,kBAAkB,EAAE;AAC/B,YAAA,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC7B;gBAAU;AACR,YAAA,OAAO,EAAE;AACT,YAAA,IAAI,IAAI,CAAC,aAAa,KAAK,IAAI,EAAE;AAC/B,gBAAA,IAAI,CAAC,aAAa,GAAG,IAAI;YAC3B;QACF;IACF;;IAGA,IAAI,GAAA;AACF,QAAA,IAAI,CAAC,QAAQ,GAAG,IAAI;IACtB;AAEA,IAAA,IAAI,OAAO,GAAA;QACT,OAAO,IAAI,CAAC,QAAQ;IACtB;;AAGA,IAAA,IAAI,WAAW,GAAA;AACb,QAAA,OAAO,IAAI,CAAC,cAAc,EAAE;IAC9B;;AAGA,IAAA,MAAM,KAAK,GAAA;AACT,QAAA,IAAI,IAAI,CAAC,aAAa,EAAE;YACtB,MAAM,IAAI,CAAC,aAAa;QAC1B;IACF;;IAIQ,cAAc,GAAA;AACpB,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;QACtB,KAAK,MAAM,GAAG,KAAK,CAAC,IAAI,IAAI,CAAC,WAAW,EAAE;AACxC,YAAA,IAAI,GAAG,GAAG,KAAK,CAAC,SAAS;AAAE,gBAAA,OAAO,IAAI;QACxC;AACA,QAAA,OAAO,KAAK;IACd;IAEQ,mBAAmB,GAAA;AACzB,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;QACtB,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,WAAW,EAAE;AAChD,YAAA,IAAI,GAAG,IAAI,KAAK,CAAC,SAAS,EAAE;AAC1B,gBAAA,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC;YACnC;QACF;IACF;;AAGQ,IAAA,qBAAqB,CAAC,OAA+B,EAAA;;AAC3D,QAAA,MAAM,GAAG,GAAG,CAAA,EAAA,GAAA,OAAO,CAAC,oBAAoB,CAAC,MAAA,IAAA,IAAA,EAAA,KAAA,MAAA,GAAA,EAAA,GAAI,OAAO,CAAC,oBAAoB,CAAC;AAC1E,QAAA,IAAI,CAAC,GAAG;YAAE;AACV,QAAA,IAAI;YACF,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAqB;AAClD,YAAA,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;AACtB,YAAA,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;gBACtD,IACE,OAAO,KAAK,KAAK,QAAQ;AACzB,oBAAA,KAAK,KAAK,IAAI;AACd,oBAAA,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ;AACrC,oBAAA,KAAK,CAAC,WAAW,GAAG,CAAC,EACrB;oBACA,MAAM,SAAS,GAAG,GAAG,GAAG,KAAK,CAAC,WAAW,GAAG,IAAI;oBAChD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,CAAC;AAC7C,oBAAA,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA,gBAAA,EAAmB,QAAQ,eAAe,KAAK,CAAC,WAAW,CAAA,CAAA,CAAG,CAAC;gBACvF;YACF;QACF;AAAE,QAAA,MAAM;;YAEN,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,+CAA+C,CAAC;QACzE;IACF;;;AAKQ,IAAA,MAAM,kBAAkB,GAAA;AAC9B,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY;AACtC,QAAA,IAAI,CAAC,KAAK;YAAE;AAEZ,QAAA,IAAI;AACF,YAAA,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE;YAChC,IAAI,GAAG,KAAK,CAAC;gBAAE;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA,SAAA,EAAY,GAAG,CAAA,2BAAA,CAA6B,CAAC;QACrE;AAAE,QAAA,MAAM;YACN;QACF;;AAGA,QAAA,IAAI,KAAkC;AACtC,QAAA,OAAO,CAAC,KAAK,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,MAAM,SAAS,EAAE;AAClD,YAAA,IAAI;gBACF,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAiB;AACzD,gBAAA,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;AAC/C,oBAAA,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC;gBACrF;YACF;AAAE,YAAA,MAAM;AACN,gBAAA,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA,+BAAA,EAAkC,KAAK,CAAC,QAAQ,CAAA,CAAE,CAAC;YAC5E;QACF;IACF;IAEQ,MAAM,QAAQ,CAAC,MAAyB,EAAA;;QAC9C,IAAI,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE;QAE1C,MAAM,QAAQ,GAAG,CAAA,EAAA,GAAA,IAAI,CAAC,kBAAkB,MAAA,IAAA,IAAA,EAAA,KAAA,MAAA,GAAA,EAAA,GAAI,GAAG;;QAG/C,MAAM,MAAM,GAAwB,EAAE;AACtC,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,QAAQ,EAAE;AAChD,YAAA,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC;QAC5C;AAEA,QAAA,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE;AAC1B,YAAA,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;QAC1D;IACF;AAEQ,IAAA,MAAM,cAAc,CAC1B,MAAyB,EACzB,WAAmB,EACnB,OAAgB,EAAA;;QAEhB,IAAI,IAAI,CAAC,QAAQ;YAAE;QAEnB,MAAM,eAAe,GAAG,OAAO,KAAA,IAAA,IAAP,OAAO,cAAP,OAAO,GAAIC,iBAAY,EAAE;AAEjD,QAAA,IAAI;AACF,YAAA,MAAM,IAAI,CAAC,qBAAqB,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;QACjF;QAAE,OAAO,GAAG,EAAE;YACZ,MAAM,MAAM,GAAG,GAAmB;AAElC,YAAA,IAAI,MAAM,CAAC,UAAU,KAAK,GAAG,EAAE;;AAE7B,gBAAA,IAAI,CAAC,QAAQ,GAAG,IAAI;AACpB,gBAAA,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;AAClB,oBAAA,IAAI,EAAE,cAAc;AACpB,oBAAA,OAAO,EAAE,uCAAuC;AAChD,oBAAA,UAAU,EAAE,GAAG;AAChB,iBAAA,CAAC;gBACF;YACF;AAEA,YAAA,IAAI,MAAM,CAAC,UAAU,KAAK,GAAG,EAAE;;AAE7B,gBAAA,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACvD,gBAAA,IAAI,CAAC,kBAAkB,GAAG,IAAI;gBAC9B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA,8CAAA,EAAiD,IAAI,CAAA,CAAE,CAAC;gBAE/E,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC;gBACnC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;;AAGjC,gBAAA,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,eAAe,CAAC;AACzE,gBAAA,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;AACrB,oBAAA,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;gBAC3D;gBACA;YACF;AAEA,YAAA,IAAI,MAAM,CAAC,UAAU,KAAK,GAAG,EAAE;;AAE7B,gBAAA,IAAI,MAAM,CAAC,gBAAgB,EAAE;AAC3B,oBAAA,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,gBAAgB,CAAC;gBACrD;AACA,gBAAA,MAAM,YAAY,GAChB,CAAA,EAAA,GAAA,MAAM,CAAC,YAAY,mCAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,GAAG,WAAW,CAAC;gBAC5E,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA,2BAAA,EAA8B,YAAY,CAAA,EAAA,CAAI,CAAC;AACrE,gBAAA,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;AAClB,oBAAA,IAAI,EAAE,cAAc;oBACpB,OAAO,EAAE,CAAA,gCAAA,EAAmC,YAAY,CAAA,GAAA,CAAK;AAC7D,oBAAA,UAAU,EAAE,GAAG;oBACf,YAAY;AACb,iBAAA,CAAC;gBACF,IAAI,WAAW,IAAI,CAAC;oBAAE;AACtB,gBAAA,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC;AAC/B,gBAAA,OAAO,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,CAAC,EAAE,eAAe,CAAC;YAC5E;AAEA,YAAA,IAAI,WAAW,IAAI,CAAC,EAAE;AACpB,gBAAA,MAAM,UAAU,GAAa;AAC3B,oBAAA,IAAI,EAAE,CAAA,EAAA,GAAA,MAAM,CAAC,IAAI,mCAAI,eAAe;oBACpC,OAAO,EAAE,MAAM,CAAC,OAAO;AACvB,oBAAA,gBAAgB,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC;iBAChD;AACD,gBAAA,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS;AAAE,oBAAA,UAAU,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU;AAC9E,gBAAA,IAAI,MAAM,CAAC,KAAK,YAAY,KAAK;AAAE,oBAAA,UAAU,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK;AAClE,gBAAA,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC;;gBAE/B,MAAM,IAAI,CAAC,sBAAsB,CAAC,eAAe,EAAE,MAAM,CAAC;gBAC1D;YACF;;AAGA,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,GAAG,WAAW,CAAC;AACjE,YAAA,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA,SAAA,EAAY,KAAK,OAAO,WAAW,GAAG,CAAC,CAAA,aAAA,CAAe,CAAC;AAC7E,YAAA,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;AACxB,YAAA,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,CAAC,EAAE,eAAe,CAAC;QACrE;IACF;;AAGQ,IAAA,MAAM,sBAAsB,CAAC,OAAe,EAAE,MAAyB,EAAA;AAC7E,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY;AACtC,QAAA,IAAI,CAAC,KAAK;YAAE;AACZ,QAAA,IAAI;YACF,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC;AAC/C,YAAA,MAAM,UAAU,GAAoB;AAClC,gBAAA,QAAQ,EAAE,OAAO;AACjB,gBAAA,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;AAChC,gBAAA,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACpC;AACD,YAAA,MAAM,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC;YAC5B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA,uBAAA,EAA0B,OAAO,CAAA,iBAAA,CAAmB,CAAC;QAC7E;AAAE,QAAA,MAAM;YACN,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA,wBAAA,EAA2B,OAAO,CAAA,iBAAA,CAAmB,CAAC;QAC/E;IACF;AAEQ,IAAA,MAAM,SAAS,CAAC,MAAyB,EAAE,OAAe,EAAA;;QAChE,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC;AAE/C,QAAA,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;AACvD,QAAA,IAAI,IAAY;AAChB,QAAA,MAAM,OAAO,GAA2B;AACtC,YAAA,aAAa,EAAE,CAAA,OAAA,EAAU,IAAI,CAAC,MAAM,CAAC,MAAM,CAAA,CAAE;AAC7C,YAAA,cAAc,EAAE,kBAAkB;AAClC,YAAA,oBAAoB,EAAE,WAAW;SAClC;AAED,QAAA,IAAI,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE;YAClC,IAAI,GAAG,UAAU;QACnB;aAAO;AACL,YAAA,IAAI,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC;AACnC,YAAA,OAAO,CAAC,kBAAkB,CAAC,GAAG,MAAM;QACtC;QAEA,OAAO,CAAC,gBAAgB,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;QAE/C,MAAM,GAAG,GAAG,CAAA,EAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAA,iBAAA,CAAmB;QACtD,MAAM,gBAAgB,GAAqB,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE;QAEjE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CACnB,QAAQ,GAAG,CAAA,UAAA,EAAa,OAAO,CAAA,QAAA,EAAW,MAAM,CAAC,MAAM,CAAA,OAAA,EAAU,IAAI,CAAC,MAAM,CAAA,CAAE,CAC/E;AAED,QAAA,IAAI,QAA2B;AAE/B,QAAA,IAAI;AACF,YAAA,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE;gBACzB,QAAQ,GAAG,MAAM,IAAI,CAAC,YAAY,CAChC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAC5C,IAAI,CAAC,MAAM,CAAC,OAAO,CACpB;YACH;iBAAO;AACL,gBAAA,QAAQ,GAAG,MAAM,IAAI,CAAC,YAAY,CAChC,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,EACxC,IAAI,CAAC,MAAM,CAAC,OAAO,CACpB;YACH;QACF;QAAE,OAAO,GAAG,EAAE;YACZ,MAAM,CAAC,GAAG,GAAY;AACtB,YAAA,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY,KAAI,CAAA,EAAA,GAAA,CAAC,CAAC,OAAO,0CAAE,QAAQ,CAAC,SAAS,CAAC,CAAA,EAAE;AAC7D,gBAAA,MAAM,IAAI,YAAY,CACpB,SAAS,EACT,CAAA,wBAAA,EAA2B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAA,EAAA,CAAI,EAClD,SAAS,EACT,SAAS,EACT,CAAC,CACF;YACH;AACA,YAAA,MAAM,IAAI,YAAY,CACpB,eAAe,EACf,kBAAkB,CAAC,CAAC,OAAO,CAAA,CAAE,EAC7B,SAAS,EACT,SAAS,EACT,CAAC,CACF;QACH;AAEA,QAAA,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA,SAAA,EAAY,QAAQ,CAAC,MAAM,CAAA,UAAA,EAAa,OAAO,CAAA,CAAE,CAAC;AAExE,QAAA,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE;;AAE3B,YAAA,IAAI,CAAC,kBAAkB,GAAG,IAAI;YAC9B;QACF;AAEA,QAAA,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE;;AAE3B,YAAA,IAAI;gBACF,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAA0B;gBACjE,IAAI,CAAA,CAAA,EAAA,GAAA,MAAM,CAAC,QAAQ,0CAAE,MAAM,IAAG,CAAC,EAAE;AAC/B,oBAAA,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;AAClB,wBAAA,IAAI,EAAE,gBAAgB;AACtB,wBAAA,OAAO,EAAE,CAAA,2BAAA,EAA8B,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAA,gBAAA,CAAkB;AAC/E,wBAAA,UAAU,EAAE,GAAG;AACf,wBAAA,gBAAgB,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC;AACzD,qBAAA,CAAC;gBACJ;YACF;AAAE,YAAA,MAAM;;YAER;AACA,YAAA,IAAI,CAAC,kBAAkB,GAAG,IAAI;YAC9B;QACF;AAEA,QAAA,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE;YAC3B,MAAM,IAAI,YAAY,CAAC,cAAc,EAAE,oBAAoB,EAAE,GAAG,CAAC;QACnE;AAEA,QAAA,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE;YAC3B,MAAM,IAAI,YAAY,CAAC,iBAAiB,EAAE,yBAAyB,EAAE,GAAG,CAAC;QAC3E;AAEA,QAAA,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE;YAC3B,MAAM,YAAY,GAAG,IAAI,CAAC,gBAAgB,CACxC,CAAA,EAAA,GAAA,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC,mCAAI,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC,CACnE;AACD,YAAA,MAAM,IAAI,YAAY,CACpB,cAAc,EACd,oBAAoB,EACpB,GAAG,EACH,YAAY,EACZ,SAAS,EACT,QAAQ,CAAC,OAAO,CACjB;QACH;AAEA,QAAA,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE;AAC1B,YAAA,MAAM,IAAI,YAAY,CAAC,iBAAiB,EAAE,CAAA,aAAA,EAAgB,QAAQ,CAAC,MAAM,EAAE,EAAE,QAAQ,CAAC,MAAM,CAAC;QAC/F;;AAGA,QAAA,MAAM,IAAI,YAAY,CAAC,iBAAiB,EAAE,CAAA,KAAA,EAAQ,QAAQ,CAAC,MAAM,EAAE,EAAE,QAAQ,CAAC,MAAM,CAAC;IACvF;IAEQ,MAAM,iBAAiB,CAAC,OAAyB,EAAA;QACvD,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,OAAO,OAAO,CAAC;QAChD,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,OAAO,MAAM,CAAC;QAE9C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,KAAI;YACrC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC;AAChC,YAAA,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,KAAK,QAAQ;YACzC,MAAM,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,IAAI;AAErC,YAAA,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CACxB;gBACE,QAAQ,EAAE,GAAG,CAAC,QAAQ;AACtB,gBAAA,IAAI,EAAE,GAAG,CAAC,IAAI,KAAK,OAAO,GAAG,GAAG,GAAG,EAAE,CAAC;AACtC,gBAAA,IAAI,EAAE,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM;AAC/B,gBAAA,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,OAAO,CAAC,OAAO;aACzB,EACD,CAAC,GAAG,KAAI;gBACN,MAAM,MAAM,GAAa,EAAE;gBAC3B,IAAI,UAAU,GAAG,CAAC;AAClB,gBAAA,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,CAAC;gBACjC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,KAAI;AAC/B,oBAAA,UAAU,IAAI,KAAK,CAAC,MAAM;oBAC1B,IAAI,UAAU,IAAI,YAAY;AAAE,wBAAA,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;AACpD,gBAAA,CAAC,CAAC;AACF,gBAAA,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,MAAK;;AACjB,oBAAA,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;oBACnD,MAAM,OAAO,GAA2B,EAAE;AAC1C,oBAAA,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE;wBAChD,IAAI,OAAO,CAAC,KAAK,QAAQ;AAAE,4BAAA,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;AACpC,6BAAA,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;4BAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAA,EAAA,GAAA,CAAC,CAAC,CAAC,CAAC,MAAA,IAAA,IAAA,EAAA,KAAA,MAAA,GAAA,EAAA,GAAI,EAAE;oBACpD;AACA,oBAAA,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA,EAAA,GAAA,GAAG,CAAC,UAAU,MAAA,IAAA,IAAA,EAAA,KAAA,MAAA,GAAA,EAAA,GAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACzD,gBAAA,CAAC,CAAC;AACF,gBAAA,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;AACzB,YAAA,CAAC,CACF;AAED,YAAA,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;AACvB,YAAA,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;YACvB,GAAG,CAAC,GAAG,EAAE;AACX,QAAA,CAAC,CAAC;IACJ;AAEQ,IAAA,MAAM,YAAY,CAAI,OAAmB,EAAE,SAAiB,EAAA;QAClE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,KAAI;AACrC,YAAA,MAAM,KAAK,GAAG,UAAU,CAAC,MAAK;gBAC5B,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,CAAA,cAAA,EAAiB,SAAS,CAAA,EAAA,CAAI,CAAC;AACrD,gBAAA,GAAG,CAAC,IAAI,GAAG,YAAY;gBACvB,MAAM,CAAC,GAAG,CAAC;YACb,CAAC,EAAE,SAAS,CAAC;AAEb,YAAA,OAAO,CAAC,IAAI,CACV,CAAC,CAAC,KAAI;gBACJ,YAAY,CAAC,KAAK,CAAC;gBACnB,OAAO,CAAC,CAAC,CAAC;AACZ,YAAA,CAAC,EACD,CAAC,CAAC,KAAI;gBACJ,YAAY,CAAC,KAAK,CAAC;gBACnB,MAAM,CAAC,CAAU,CAAC;AACpB,YAAA,CAAC,CACF;AACH,QAAA,CAAC,CAAC;IACJ;IAEQ,MAAM,qBAAqB,CAAI,EAAoB,EAAA;AACzD,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,oBAAoB;AAE5C,QAAA,IAAI,IAAI,CAAC,gBAAgB,IAAI,GAAG,EAAE;;AAEhC,YAAA,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,KAAK,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3E;QAEA,IAAI,CAAC,gBAAgB,EAAE;AACvB,QAAA,IAAI;YACF,OAAO,MAAM,EAAE,EAAE;QACnB;gBAAU;YACR,IAAI,CAAC,gBAAgB,EAAE;YACvB,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE;AAC1C,YAAA,IAAI,IAAI;AAAE,gBAAA,IAAI,EAAE;QAClB;IACF;;AAGQ,IAAA,QAAQ,CAAC,OAAe,EAAA;AAC9B,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC;AACzD,QAAA,MAAM,MAAM,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC;IAClC;AAEQ,IAAA,gBAAgB,CAAC,MAA0B,EAAA;AACjD,QAAA,IAAI,CAAC,MAAM;AAAE,YAAA,OAAO,SAAS;AAC7B,QAAA,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC;AAClC,QAAA,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;QACtD,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE;AACvC,QAAA,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;AAAE,YAAA,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;AACvD,QAAA,OAAO,SAAS;IAClB;AAEQ,IAAA,MAAM,CAAC,EAAU,EAAA;AACvB,QAAA,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC1D;AACD;AAED,MAAM,YAAa,SAAQ,KAAK,CAAA;IAC9B,WAAA,CACkB,IAAsB,EACtC,OAAe,EACC,UAAmB,EACnB,YAAqB,EACrB,KAAa;;IAEb,gBAAyC,EAAA;QAEzD,KAAK,CAAC,OAAO,CAAC;QARE,IAAA,CAAA,IAAI,GAAJ,IAAI;QAEJ,IAAA,CAAA,UAAU,GAAV,UAAU;QACV,IAAA,CAAA,YAAY,GAAZ,YAAY;QACZ,IAAA,CAAA,KAAK,GAAL,KAAK;QAEL,IAAA,CAAA,gBAAgB,GAAhB,gBAAgB;AAGhC,QAAA,IAAI,CAAC,IAAI,GAAG,cAAc;IAC5B;AACD;;;;"}
@@ -0,0 +1,101 @@
1
+ 'use strict';
2
+
3
+ var fs = require('fs');
4
+ var path = require('path');
5
+
6
+ /**
7
+ * S80: FileOfflineStore — built-in persistent store for failed batches.
8
+ *
9
+ * Writes each failed batch as a separate JSON file in a configurable directory.
10
+ * Uses atomic write (write to temp file, rename) to prevent corrupt reads.
11
+ * Implements FIFO ordering via filename sort (filenames include ISO timestamp prefix).
12
+ *
13
+ * Usage:
14
+ * const store = new FileOfflineStore('./doow-track-offline');
15
+ * const meter = new DoowTracker('dk_...', { offlineStore: store });
16
+ */
17
+ class FileOfflineStore {
18
+ constructor(dir = './doow-track-offline') {
19
+ this._dir = dir;
20
+ }
21
+ /** Ensure the storage directory exists */
22
+ async _ensureDir() {
23
+ await fs.promises.mkdir(this._dir, { recursive: true });
24
+ }
25
+ /** All batch files, sorted by name (ascending = oldest first) */
26
+ async _listFiles() {
27
+ await this._ensureDir();
28
+ const entries = await fs.promises.readdir(this._dir);
29
+ return entries
30
+ .filter((f) => f.endsWith('.json') && !f.endsWith('.tmp.json'))
31
+ .sort() // lexicographic sort: timestamp prefix ensures FIFO
32
+ .map((f) => path.join(this._dir, f));
33
+ }
34
+ /**
35
+ * Persist a batch.
36
+ * Filename: {ISO_timestamp}_{batch_id}.json — sortable, unique.
37
+ * Atomic: write to .tmp.json then rename.
38
+ */
39
+ async push(batch) {
40
+ await this._ensureDir();
41
+ // Use the batch timestamp for sortable filename; fall back to now
42
+ const ts = batch.timestamp.replace(/[:.]/g, '-');
43
+ const filename = `${ts}_${batch.batch_id}.json`;
44
+ const filePath = path.join(this._dir, filename);
45
+ const tmpPath = path.join(this._dir, `${filename}.tmp`);
46
+ const content = JSON.stringify(batch);
47
+ // Atomic: write to tmp, then rename into place
48
+ await fs.promises.writeFile(tmpPath, content, 'utf8');
49
+ await fs.promises.rename(tmpPath, filePath);
50
+ }
51
+ /**
52
+ * Retrieve and remove the oldest batch (FIFO).
53
+ * Returns undefined if store is empty.
54
+ */
55
+ async shift() {
56
+ const files = await this._listFiles();
57
+ if (files.length === 0)
58
+ return undefined;
59
+ const oldest = files[0];
60
+ let content;
61
+ try {
62
+ content = await fs.promises.readFile(oldest, 'utf8');
63
+ }
64
+ catch {
65
+ // File may have been removed concurrently — treat as empty
66
+ return undefined;
67
+ }
68
+ let batch;
69
+ try {
70
+ batch = JSON.parse(content);
71
+ }
72
+ catch {
73
+ try {
74
+ await fs.promises.unlink(oldest);
75
+ }
76
+ catch {
77
+ /* best effort */
78
+ }
79
+ return undefined;
80
+ }
81
+ try {
82
+ await fs.promises.unlink(oldest);
83
+ }
84
+ catch {
85
+ // Best effort — if delete fails, we may replay; acceptable for offline store
86
+ }
87
+ return batch;
88
+ }
89
+ /** Number of pending batches in the store */
90
+ async length() {
91
+ const files = await this._listFiles();
92
+ return files.length;
93
+ }
94
+ /** Directory this store writes to */
95
+ get directory() {
96
+ return this._dir;
97
+ }
98
+ }
99
+
100
+ exports.FileOfflineStore = FileOfflineStore;
101
+ //# sourceMappingURL=file-offline-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-offline-store.js","sources":["../../../src/file-offline-store.ts"],"sourcesContent":[null],"names":["fs","join"],"mappings":";;;;;AAAA;;;;;;;;;;AAUG;MAKU,gBAAgB,CAAA;IAG3B,WAAA,CAAY,GAAG,GAAG,sBAAsB,EAAA;AACtC,QAAA,IAAI,CAAC,IAAI,GAAG,GAAG;IACjB;;AAGQ,IAAA,MAAM,UAAU,GAAA;AACtB,QAAA,MAAMA,WAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAChD;;AAGQ,IAAA,MAAM,UAAU,GAAA;AACtB,QAAA,MAAM,IAAI,CAAC,UAAU,EAAE;QACvB,MAAM,OAAO,GAAG,MAAMA,WAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;AAC3C,QAAA,OAAO;aACJ,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;aAC7D,IAAI,EAAE;AACN,aAAA,GAAG,CAAC,CAAC,CAAC,KAAKC,SAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACnC;AAEA;;;;AAIG;IACH,MAAM,IAAI,CAAC,KAAsB,EAAA;AAC/B,QAAA,MAAM,IAAI,CAAC,UAAU,EAAE;;AAEvB,QAAA,MAAM,EAAE,GAAG,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;QAChD,MAAM,QAAQ,GAAG,CAAA,EAAG,EAAE,IAAI,KAAK,CAAC,QAAQ,CAAA,KAAA,CAAO;QAC/C,MAAM,QAAQ,GAAGA,SAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC;AAC1C,QAAA,MAAM,OAAO,GAAGA,SAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA,EAAG,QAAQ,CAAA,IAAA,CAAM,CAAC;QAElD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;;QAErC,MAAMD,WAAE,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC;QAC5C,MAAMA,WAAE,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC;IACpC;AAEA;;;AAGG;AACH,IAAA,MAAM,KAAK,GAAA;AACT,QAAA,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE;AACrC,QAAA,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;AAAE,YAAA,OAAO,SAAS;AAExC,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAE;AACxB,QAAA,IAAI,OAAe;AACnB,QAAA,IAAI;YACF,OAAO,GAAG,MAAMA,WAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAC7C;AAAE,QAAA,MAAM;;AAEN,YAAA,OAAO,SAAS;QAClB;AAEA,QAAA,IAAI,KAAsB;AAC1B,QAAA,IAAI;AACF,YAAA,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAoB;QAChD;AAAE,QAAA,MAAM;AACN,YAAA,IAAI;AACF,gBAAA,MAAMA,WAAE,CAAC,MAAM,CAAC,MAAM,CAAC;YACzB;AAAE,YAAA,MAAM;;YAER;AACA,YAAA,OAAO,SAAS;QAClB;AAEA,QAAA,IAAI;AACF,YAAA,MAAMA,WAAE,CAAC,MAAM,CAAC,MAAM,CAAC;QACzB;AAAE,QAAA,MAAM;;QAER;AAEA,QAAA,OAAO,KAAK;IACd;;AAGA,IAAA,MAAM,MAAM,GAAA;AACV,QAAA,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE;QACrC,OAAO,KAAK,CAAC,MAAM;IACrB;;AAGA,IAAA,IAAI,SAAS,GAAA;QACX,OAAO,IAAI,CAAC,IAAI;IAClB;AACD;;;;"}
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ var tracker = require('./tracker.js');
4
+ var fileOfflineStore = require('./file-offline-store.js');
5
+
6
+
7
+
8
+ exports.DoowTracker = tracker.DoowTracker;
9
+ exports.FileOfflineStore = fileOfflineStore.FileOfflineStore;
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}