@pushflodev/sdk 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,47 @@
1
+ import { f as ServerOptions, P as PublishOptions, d as PublishResult, c as MessageHistoryOptions, M as Message } from './message-CVgilLwz.js';
2
+ import { L as ListChannelsOptions, C as Channel, c as Pagination, a as ChannelInput, b as ChannelUpdateInput } from './api-DYrG_5uC.js';
3
+ export { A as AuthenticationError, N as NetworkError, P as PushFloError } from './api-DYrG_5uC.js';
4
+
5
+ /**
6
+ * Server-side PushFlo client for publishing messages and managing channels
7
+ */
8
+ declare class PushFloServer {
9
+ private readonly client;
10
+ constructor(options: ServerOptions);
11
+ /**
12
+ * List all channels
13
+ */
14
+ listChannels(options?: ListChannelsOptions): Promise<{
15
+ channels: Channel[];
16
+ pagination: Pagination;
17
+ }>;
18
+ /**
19
+ * Get a channel by slug
20
+ */
21
+ getChannel(slug: string): Promise<Channel>;
22
+ /**
23
+ * Create a new channel
24
+ */
25
+ createChannel(input: ChannelInput): Promise<Channel>;
26
+ /**
27
+ * Update an existing channel
28
+ */
29
+ updateChannel(slug: string, input: ChannelUpdateInput): Promise<Channel>;
30
+ /**
31
+ * Delete a channel
32
+ */
33
+ deleteChannel(slug: string): Promise<void>;
34
+ /**
35
+ * Publish a message to a channel
36
+ */
37
+ publish(channel: string, content: Record<string, unknown>, options?: PublishOptions): Promise<PublishResult>;
38
+ /**
39
+ * Get message history for a channel
40
+ */
41
+ getMessageHistory(channel: string, options?: MessageHistoryOptions): Promise<{
42
+ messages: Message[];
43
+ pagination: Pagination;
44
+ }>;
45
+ }
46
+
47
+ export { Channel, ChannelInput, ChannelUpdateInput, ListChannelsOptions, Message, MessageHistoryOptions, Pagination, PublishOptions, PublishResult, PushFloServer, ServerOptions };
package/dist/server.js ADDED
@@ -0,0 +1,553 @@
1
+ /* @pushflo/sdk - https://pushflo.dev */
2
+
3
+ // src/utils/constants.ts
4
+ var DEFAULTS = {
5
+ /** Default PushFlo API base URL */
6
+ BASE_URL: "https://api.pushflo.dev",
7
+ /** API version prefix */
8
+ API_VERSION: "/api/v1",
9
+ /** Connection timeout in milliseconds */
10
+ CONNECTION_TIMEOUT: 3e4,
11
+ /** Initial reconnection delay in milliseconds */
12
+ RECONNECT_DELAY: 1e3,
13
+ /** Maximum reconnection delay in milliseconds */
14
+ MAX_RECONNECT_DELAY: 3e4,
15
+ /** Reconnection delay multiplier for exponential backoff */
16
+ RECONNECT_MULTIPLIER: 1.5,
17
+ /** Default page size for list operations */
18
+ PAGE_SIZE: 25
19
+ };
20
+ var API_PATHS = {
21
+ AUTH_TOKEN: "/auth/token",
22
+ CHANNELS: "/channels",
23
+ CHANNEL: (slug) => `/channels/${encodeURIComponent(slug)}`,
24
+ CHANNEL_MESSAGES: (slug) => `/channels/${encodeURIComponent(slug)}/messages`
25
+ };
26
+ var ERROR_CODES = {
27
+ // Authentication errors
28
+ INVALID_API_KEY: "INVALID_API_KEY",
29
+ UNAUTHORIZED: "UNAUTHORIZED",
30
+ FORBIDDEN: "FORBIDDEN",
31
+ // Network errors
32
+ NETWORK_ERROR: "NETWORK_ERROR",
33
+ REQUEST_TIMEOUT: "REQUEST_TIMEOUT",
34
+ // API errors
35
+ NOT_FOUND: "NOT_FOUND",
36
+ VALIDATION_ERROR: "VALIDATION_ERROR",
37
+ RATE_LIMITED: "RATE_LIMITED",
38
+ SERVER_ERROR: "SERVER_ERROR"};
39
+
40
+ // src/utils/retry.ts
41
+ function calculateBackoff(attempt, options = {}) {
42
+ const {
43
+ initialDelay = DEFAULTS.RECONNECT_DELAY,
44
+ maxDelay = DEFAULTS.MAX_RECONNECT_DELAY,
45
+ multiplier = DEFAULTS.RECONNECT_MULTIPLIER
46
+ } = options;
47
+ const exponentialDelay = initialDelay * Math.pow(multiplier, attempt);
48
+ const cappedDelay = Math.min(exponentialDelay, maxDelay);
49
+ const jitter = cappedDelay * 0.25 * (Math.random() * 2 - 1);
50
+ return Math.floor(cappedDelay + jitter);
51
+ }
52
+ function sleep(ms, signal) {
53
+ return new Promise((resolve, reject) => {
54
+ if (signal?.aborted) {
55
+ reject(new DOMException("Aborted", "AbortError"));
56
+ return;
57
+ }
58
+ const timeoutId = setTimeout(resolve, ms);
59
+ signal?.addEventListener(
60
+ "abort",
61
+ () => {
62
+ clearTimeout(timeoutId);
63
+ reject(new DOMException("Aborted", "AbortError"));
64
+ },
65
+ { once: true }
66
+ );
67
+ });
68
+ }
69
+ async function retry(fn, options = {}) {
70
+ const {
71
+ maxAttempts = 3,
72
+ initialDelay = DEFAULTS.RECONNECT_DELAY,
73
+ maxDelay = DEFAULTS.MAX_RECONNECT_DELAY,
74
+ multiplier = DEFAULTS.RECONNECT_MULTIPLIER,
75
+ isRetryable = () => true,
76
+ onRetry,
77
+ signal
78
+ } = options;
79
+ let lastError;
80
+ let attempt = 0;
81
+ while (maxAttempts === 0 || attempt < maxAttempts) {
82
+ try {
83
+ if (signal?.aborted) {
84
+ throw new DOMException("Aborted", "AbortError");
85
+ }
86
+ return await fn();
87
+ } catch (error) {
88
+ lastError = error;
89
+ if (error instanceof DOMException && error.name === "AbortError") {
90
+ throw error;
91
+ }
92
+ if (!isRetryable(error)) {
93
+ throw error;
94
+ }
95
+ if (maxAttempts !== 0 && attempt >= maxAttempts - 1) {
96
+ throw error;
97
+ }
98
+ const delay = calculateBackoff(attempt, { initialDelay, maxDelay, multiplier });
99
+ onRetry?.(attempt + 1, delay, error);
100
+ await sleep(delay, signal);
101
+ attempt++;
102
+ }
103
+ }
104
+ throw lastError;
105
+ }
106
+
107
+ // src/utils/logger.ts
108
+ var LOG_LEVELS = {
109
+ debug: 0,
110
+ info: 1,
111
+ warn: 2,
112
+ error: 3
113
+ };
114
+ var Logger = class {
115
+ enabled;
116
+ prefix;
117
+ minLevel;
118
+ constructor(options = {}) {
119
+ this.enabled = options.debug ?? false;
120
+ this.prefix = options.prefix ?? "[PushFlo]";
121
+ this.minLevel = LOG_LEVELS[options.level ?? "debug"];
122
+ }
123
+ /**
124
+ * Enable or disable logging
125
+ */
126
+ setEnabled(enabled) {
127
+ this.enabled = enabled;
128
+ }
129
+ /**
130
+ * Set minimum log level
131
+ */
132
+ setLevel(level) {
133
+ this.minLevel = LOG_LEVELS[level];
134
+ }
135
+ /**
136
+ * Log a debug message
137
+ */
138
+ debug(message, ...args) {
139
+ this.log("debug", message, ...args);
140
+ }
141
+ /**
142
+ * Log an info message
143
+ */
144
+ info(message, ...args) {
145
+ this.log("info", message, ...args);
146
+ }
147
+ /**
148
+ * Log a warning message
149
+ */
150
+ warn(message, ...args) {
151
+ this.log("warn", message, ...args);
152
+ }
153
+ /**
154
+ * Log an error message
155
+ */
156
+ error(message, ...args) {
157
+ this.log("error", message, ...args);
158
+ }
159
+ log(level, message, ...args) {
160
+ if (!this.enabled && level !== "error") {
161
+ return;
162
+ }
163
+ if (LOG_LEVELS[level] < this.minLevel) {
164
+ return;
165
+ }
166
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
167
+ const formattedMessage = `${this.prefix} ${timestamp} [${level.toUpperCase()}] ${message}`;
168
+ switch (level) {
169
+ case "debug":
170
+ console.debug(formattedMessage, ...args);
171
+ break;
172
+ case "info":
173
+ console.info(formattedMessage, ...args);
174
+ break;
175
+ case "warn":
176
+ console.warn(formattedMessage, ...args);
177
+ break;
178
+ case "error":
179
+ console.error(formattedMessage, ...args);
180
+ break;
181
+ }
182
+ }
183
+ };
184
+ function createLogger(options = {}) {
185
+ return new Logger(options);
186
+ }
187
+
188
+ // src/errors/PushFloError.ts
189
+ var PushFloError = class extends Error {
190
+ /** Error code for programmatic handling */
191
+ code;
192
+ /** Whether this error is potentially recoverable through retry */
193
+ retryable;
194
+ /** Original error that caused this error, if any */
195
+ cause;
196
+ constructor(message, code, options = {}) {
197
+ super(message);
198
+ this.name = "PushFloError";
199
+ this.code = code;
200
+ this.retryable = options.retryable ?? false;
201
+ this.cause = options.cause;
202
+ if (Error.captureStackTrace) {
203
+ Error.captureStackTrace(this, this.constructor);
204
+ }
205
+ }
206
+ /**
207
+ * Convert error to JSON-serializable object
208
+ */
209
+ toJSON() {
210
+ return {
211
+ name: this.name,
212
+ message: this.message,
213
+ code: this.code,
214
+ retryable: this.retryable,
215
+ stack: this.stack,
216
+ cause: this.cause?.message
217
+ };
218
+ }
219
+ /**
220
+ * Create a string representation
221
+ */
222
+ toString() {
223
+ return `${this.name} [${this.code}]: ${this.message}`;
224
+ }
225
+ };
226
+
227
+ // src/errors/AuthenticationError.ts
228
+ var AuthenticationError = class _AuthenticationError extends PushFloError {
229
+ constructor(message, code = ERROR_CODES.UNAUTHORIZED, options = {}) {
230
+ super(message, code, { retryable: false, ...options });
231
+ this.name = "AuthenticationError";
232
+ }
233
+ /**
234
+ * Create an invalid API key error
235
+ */
236
+ static invalidKey(keyType) {
237
+ const message = keyType ? `Invalid ${keyType} API key` : "Invalid API key";
238
+ return new _AuthenticationError(message, ERROR_CODES.INVALID_API_KEY);
239
+ }
240
+ /**
241
+ * Create an unauthorized error
242
+ */
243
+ static unauthorized(reason) {
244
+ return new _AuthenticationError(
245
+ reason ?? "Unauthorized - check your API key",
246
+ ERROR_CODES.UNAUTHORIZED
247
+ );
248
+ }
249
+ /**
250
+ * Create a forbidden error
251
+ */
252
+ static forbidden(action) {
253
+ const message = action ? `Access forbidden: insufficient permissions for ${action}` : "Access forbidden: insufficient permissions";
254
+ return new _AuthenticationError(message, ERROR_CODES.FORBIDDEN);
255
+ }
256
+ };
257
+
258
+ // src/errors/NetworkError.ts
259
+ var NetworkError = class _NetworkError extends PushFloError {
260
+ /** HTTP status code, if applicable */
261
+ statusCode;
262
+ constructor(message, code = ERROR_CODES.NETWORK_ERROR, options = {}) {
263
+ super(message, code, { retryable: true, ...options });
264
+ this.name = "NetworkError";
265
+ this.statusCode = options.statusCode;
266
+ }
267
+ /**
268
+ * Create a network error from fetch failure
269
+ */
270
+ static fromFetch(cause) {
271
+ return new _NetworkError(
272
+ `Network request failed: ${cause.message}`,
273
+ ERROR_CODES.NETWORK_ERROR,
274
+ { retryable: true, cause }
275
+ );
276
+ }
277
+ /**
278
+ * Create a request timeout error
279
+ */
280
+ static timeout(timeoutMs) {
281
+ return new _NetworkError(
282
+ `Request timed out after ${timeoutMs}ms`,
283
+ ERROR_CODES.REQUEST_TIMEOUT,
284
+ { retryable: true }
285
+ );
286
+ }
287
+ /**
288
+ * Create an error from HTTP status code
289
+ */
290
+ static fromStatus(statusCode, message) {
291
+ const defaultMessage = _NetworkError.getStatusMessage(statusCode);
292
+ const retryable = statusCode >= 500 || statusCode === 429;
293
+ let code = ERROR_CODES.SERVER_ERROR;
294
+ if (statusCode === 404) {
295
+ code = ERROR_CODES.NOT_FOUND;
296
+ } else if (statusCode === 422 || statusCode === 400) {
297
+ code = ERROR_CODES.VALIDATION_ERROR;
298
+ } else if (statusCode === 429) {
299
+ code = ERROR_CODES.RATE_LIMITED;
300
+ }
301
+ return new _NetworkError(
302
+ message ?? defaultMessage,
303
+ code,
304
+ { retryable, statusCode }
305
+ );
306
+ }
307
+ static getStatusMessage(statusCode) {
308
+ const messages = {
309
+ 400: "Bad request",
310
+ 404: "Resource not found",
311
+ 422: "Validation error",
312
+ 429: "Rate limit exceeded",
313
+ 500: "Internal server error",
314
+ 502: "Bad gateway",
315
+ 503: "Service unavailable",
316
+ 504: "Gateway timeout"
317
+ };
318
+ return messages[statusCode] ?? `HTTP error ${statusCode}`;
319
+ }
320
+ toJSON() {
321
+ return {
322
+ ...super.toJSON(),
323
+ statusCode: this.statusCode
324
+ };
325
+ }
326
+ };
327
+
328
+ // src/server/RestClient.ts
329
+ var RestClient = class {
330
+ apiKey;
331
+ baseUrl;
332
+ timeout;
333
+ retryAttempts;
334
+ logger;
335
+ constructor(options) {
336
+ this.apiKey = options.apiKey;
337
+ this.baseUrl = (options.baseUrl ?? DEFAULTS.BASE_URL).replace(/\/$/, "");
338
+ this.timeout = options.timeout ?? DEFAULTS.CONNECTION_TIMEOUT;
339
+ this.retryAttempts = options.retryAttempts ?? 3;
340
+ this.logger = createLogger({ debug: options.debug, prefix: "[PushFlo REST]" });
341
+ }
342
+ /**
343
+ * Make an HTTP request to the API
344
+ */
345
+ async request(method, path, options = {}) {
346
+ const url = this.buildUrl(path, options.query);
347
+ this.logger.debug(`${method} ${url}`);
348
+ const retryOpts = {
349
+ maxAttempts: this.retryAttempts,
350
+ isRetryable: (error) => {
351
+ if (error instanceof NetworkError) {
352
+ return error.retryable;
353
+ }
354
+ return false;
355
+ },
356
+ onRetry: (attempt, delay, error) => {
357
+ this.logger.warn(`Request failed, retrying (${attempt}/${this.retryAttempts}) in ${delay}ms`, error);
358
+ },
359
+ ...options.retryOptions
360
+ };
361
+ return retry(async () => {
362
+ const controller = new AbortController();
363
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
364
+ try {
365
+ const response = await fetch(url, {
366
+ method,
367
+ headers: this.getHeaders(),
368
+ body: options.body ? JSON.stringify(options.body) : void 0,
369
+ signal: controller.signal
370
+ });
371
+ return await this.handleResponse(response);
372
+ } catch (error) {
373
+ if (error instanceof AuthenticationError || error instanceof NetworkError) {
374
+ throw error;
375
+ }
376
+ if (error instanceof DOMException && error.name === "AbortError") {
377
+ throw NetworkError.timeout(this.timeout);
378
+ }
379
+ if (error instanceof TypeError) {
380
+ throw NetworkError.fromFetch(error);
381
+ }
382
+ throw error;
383
+ } finally {
384
+ clearTimeout(timeoutId);
385
+ }
386
+ }, retryOpts);
387
+ }
388
+ /**
389
+ * Make a GET request
390
+ */
391
+ get(path, query) {
392
+ return this.request("GET", path, { query });
393
+ }
394
+ /**
395
+ * Make a POST request
396
+ */
397
+ post(path, body) {
398
+ return this.request("POST", path, { body });
399
+ }
400
+ /**
401
+ * Make a PATCH request
402
+ */
403
+ patch(path, body) {
404
+ return this.request("PATCH", path, { body });
405
+ }
406
+ /**
407
+ * Make a DELETE request
408
+ */
409
+ delete(path) {
410
+ return this.request("DELETE", path);
411
+ }
412
+ buildUrl(path, query) {
413
+ const fullPath = `${DEFAULTS.API_VERSION}${path}`;
414
+ const url = new URL(fullPath, this.baseUrl);
415
+ if (query) {
416
+ Object.entries(query).forEach(([key, value]) => {
417
+ if (value !== void 0) {
418
+ url.searchParams.set(key, String(value));
419
+ }
420
+ });
421
+ }
422
+ return url.toString();
423
+ }
424
+ getHeaders() {
425
+ return {
426
+ "Content-Type": "application/json",
427
+ Authorization: `Bearer ${this.apiKey}`
428
+ };
429
+ }
430
+ async handleResponse(response) {
431
+ if (response.status === 401) {
432
+ throw AuthenticationError.unauthorized();
433
+ }
434
+ if (response.status === 403) {
435
+ throw AuthenticationError.forbidden();
436
+ }
437
+ if (!response.ok) {
438
+ let errorMessage;
439
+ try {
440
+ const errorBody = await response.json();
441
+ errorMessage = errorBody.error;
442
+ } catch {
443
+ }
444
+ throw NetworkError.fromStatus(response.status, errorMessage);
445
+ }
446
+ if (response.status === 204) {
447
+ return void 0;
448
+ }
449
+ try {
450
+ return await response.json();
451
+ } catch {
452
+ throw new NetworkError("Failed to parse response", "PARSE_ERROR", { retryable: false });
453
+ }
454
+ }
455
+ };
456
+
457
+ // src/server/PushFloServer.ts
458
+ var PushFloServer = class {
459
+ client;
460
+ constructor(options) {
461
+ if (!options.secretKey) {
462
+ throw new AuthenticationError(
463
+ "Secret key is required",
464
+ "MISSING_SECRET_KEY"
465
+ );
466
+ }
467
+ if (!options.secretKey.startsWith("sec_") && !options.secretKey.startsWith("mgmt_")) {
468
+ throw AuthenticationError.invalidKey("secret");
469
+ }
470
+ this.client = new RestClient({
471
+ apiKey: options.secretKey,
472
+ baseUrl: options.baseUrl,
473
+ timeout: options.timeout,
474
+ retryAttempts: options.retryAttempts,
475
+ debug: options.debug
476
+ });
477
+ }
478
+ // ============================================
479
+ // Channel Management
480
+ // ============================================
481
+ /**
482
+ * List all channels
483
+ */
484
+ async listChannels(options = {}) {
485
+ const response = await this.client.get(API_PATHS.CHANNELS, {
486
+ page: options.page,
487
+ pageSize: options.pageSize ?? DEFAULTS.PAGE_SIZE
488
+ });
489
+ return {
490
+ channels: response.channels,
491
+ pagination: response.pagination
492
+ };
493
+ }
494
+ /**
495
+ * Get a channel by slug
496
+ */
497
+ async getChannel(slug) {
498
+ return this.client.get(API_PATHS.CHANNEL(slug));
499
+ }
500
+ /**
501
+ * Create a new channel
502
+ */
503
+ async createChannel(input) {
504
+ return this.client.post(API_PATHS.CHANNELS, input);
505
+ }
506
+ /**
507
+ * Update an existing channel
508
+ */
509
+ async updateChannel(slug, input) {
510
+ return this.client.patch(API_PATHS.CHANNEL(slug), input);
511
+ }
512
+ /**
513
+ * Delete a channel
514
+ */
515
+ async deleteChannel(slug) {
516
+ await this.client.delete(API_PATHS.CHANNEL(slug));
517
+ }
518
+ // ============================================
519
+ // Message Publishing
520
+ // ============================================
521
+ /**
522
+ * Publish a message to a channel
523
+ */
524
+ async publish(channel, content, options = {}) {
525
+ return this.client.post(API_PATHS.CHANNEL_MESSAGES(channel), {
526
+ content,
527
+ eventType: options.eventType ?? "message"
528
+ });
529
+ }
530
+ /**
531
+ * Get message history for a channel
532
+ */
533
+ async getMessageHistory(channel, options = {}) {
534
+ const response = await this.client.get(
535
+ API_PATHS.CHANNEL_MESSAGES(channel),
536
+ {
537
+ page: options.page,
538
+ pageSize: options.pageSize ?? DEFAULTS.PAGE_SIZE,
539
+ eventType: options.eventType,
540
+ after: options.after,
541
+ before: options.before
542
+ }
543
+ );
544
+ return {
545
+ messages: response.messages,
546
+ pagination: response.pagination
547
+ };
548
+ }
549
+ };
550
+
551
+ export { AuthenticationError, NetworkError, PushFloError, PushFloServer };
552
+ //# sourceMappingURL=server.js.map
553
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/constants.ts","../src/utils/retry.ts","../src/utils/logger.ts","../src/errors/PushFloError.ts","../src/errors/AuthenticationError.ts","../src/errors/NetworkError.ts","../src/server/RestClient.ts","../src/server/PushFloServer.ts"],"names":[],"mappings":";;;AAGO,IAAM,QAAA,GAAW;AAAA;AAAA,EAEtB,QAAA,EAAU,yBAAA;AAAA,EAGD;AAAA,EAGT,WAAA,EAAa,SAAA;AAAA;AAAA,EAGb,kBAAA,EAAoB,GAAA;AAAA,EAGA;AAAA,EAGpB,eAAA,EAAiB,GAAA;AAAA;AAAA,EAGjB,mBAAA,EAAqB,GAAA;AAAA;AAAA,EAGrB,oBAAA,EAAsB,GAAA;AAAA,EAGE;AAAA,EAGxB,SAAA,EAAW;AACb,CAAA;AAKO,IAAM,SAAA,GAAY;AAAA,EACvB,UAAA,EAAY,aAAA;AAAA,EACZ,QAAA,EAAU,WAAA;AAAA,EACV,SAAS,CAAC,IAAA,KAAiB,CAAA,UAAA,EAAa,kBAAA,CAAmB,IAAI,CAAC,CAAA,CAAA;AAAA,EAChE,kBAAkB,CAAC,IAAA,KAAiB,CAAA,UAAA,EAAa,kBAAA,CAAmB,IAAI,CAAC,CAAA,SAAA;AAC3E,CAAA;AA2BO,IAAM,WAAA,GAAc;AAAA,EAIN;AAAA,EAGnB,eAAA,EAAiB,iBAAA;AAAA,EACjB,YAAA,EAAc,cAAA;AAAA,EACd,SAAA,EAAW,WAAA;AAAA;AAAA,EAGX,aAAA,EAAe,eAAA;AAAA,EACf,eAAA,EAAiB,iBAAA;AAAA;AAAA,EAGjB,SAAA,EAAW,WAAA;AAAA,EACX,gBAAA,EAAkB,kBAAA;AAAA,EAClB,YAAA,EAAc,cAAA;AAAA,EACd,YAAA,EAAc,cAOhB,CAAA;;;AC1EO,SAAS,gBAAA,CACd,OAAA,EACA,OAAA,GAII,EAAC,EACG;AACR,EAAA,MAAM;AAAA,IACJ,eAAe,QAAA,CAAS,eAAA;AAAA,IACxB,WAAW,QAAA,CAAS,mBAAA;AAAA,IACpB,aAAa,QAAA,CAAS;AAAA,GACxB,GAAI,OAAA;AAGJ,EAAA,MAAM,gBAAA,GAAmB,YAAA,GAAe,IAAA,CAAK,GAAA,CAAI,YAAY,OAAO,CAAA;AAGpE,EAAA,MAAM,WAAA,GAAc,IAAA,CAAK,GAAA,CAAI,gBAAA,EAAkB,QAAQ,CAAA;AAGvD,EAAA,MAAM,SAAS,WAAA,GAAc,IAAA,IAAQ,IAAA,CAAK,MAAA,KAAW,CAAA,GAAI,CAAA,CAAA;AAEzD,EAAA,OAAO,IAAA,CAAK,KAAA,CAAM,WAAA,GAAc,MAAM,CAAA;AACxC;AAKO,SAAS,KAAA,CAAM,IAAY,MAAA,EAAqC;AACrE,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,IAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,MAAA,MAAA,CAAO,IAAI,YAAA,CAAa,SAAA,EAAW,YAAY,CAAC,CAAA;AAChD,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,SAAA,GAAY,UAAA,CAAW,OAAA,EAAS,EAAE,CAAA;AAExC,IAAA,MAAA,EAAQ,gBAAA;AAAA,MACN,OAAA;AAAA,MACA,MAAM;AACJ,QAAA,YAAA,CAAa,SAAS,CAAA;AACtB,QAAA,MAAA,CAAO,IAAI,YAAA,CAAa,SAAA,EAAW,YAAY,CAAC,CAAA;AAAA,MAClD,CAAA;AAAA,MACA,EAAE,MAAM,IAAA;AAAK,KACf;AAAA,EACF,CAAC,CAAA;AACH;AAKA,eAAsB,KAAA,CACpB,EAAA,EACA,OAAA,GAAwB,EAAC,EACb;AACZ,EAAA,MAAM;AAAA,IACJ,WAAA,GAAc,CAAA;AAAA,IACd,eAAe,QAAA,CAAS,eAAA;AAAA,IACxB,WAAW,QAAA,CAAS,mBAAA;AAAA,IACpB,aAAa,QAAA,CAAS,oBAAA;AAAA,IACtB,cAAc,MAAM,IAAA;AAAA,IACpB,OAAA;AAAA,IACA;AAAA,GACF,GAAI,OAAA;AAEJ,EAAA,IAAI,SAAA;AACJ,EAAA,IAAI,OAAA,GAAU,CAAA;AAEd,EAAA,OAAO,WAAA,KAAgB,CAAA,IAAK,OAAA,GAAU,WAAA,EAAa;AACjD,IAAA,IAAI;AACF,MAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,QAAA,MAAM,IAAI,YAAA,CAAa,SAAA,EAAW,YAAY,CAAA;AAAA,MAChD;AACA,MAAA,OAAO,MAAM,EAAA,EAAG;AAAA,IAClB,SAAS,KAAA,EAAO;AACd,MAAA,SAAA,GAAY,KAAA;AAGZ,MAAA,IAAI,KAAA,YAAiB,YAAA,IAAgB,KAAA,CAAM,IAAA,KAAS,YAAA,EAAc;AAChE,QAAA,MAAM,KAAA;AAAA,MACR;AAGA,MAAA,IAAI,CAAC,WAAA,CAAY,KAAK,CAAA,EAAG;AACvB,QAAA,MAAM,KAAA;AAAA,MACR;AAGA,MAAA,IAAI,WAAA,KAAgB,CAAA,IAAK,OAAA,IAAW,WAAA,GAAc,CAAA,EAAG;AACnD,QAAA,MAAM,KAAA;AAAA,MACR;AAGA,MAAA,MAAM,QAAQ,gBAAA,CAAiB,OAAA,EAAS,EAAE,YAAA,EAAc,QAAA,EAAU,YAAY,CAAA;AAG9E,MAAA,OAAA,GAAU,OAAA,GAAU,CAAA,EAAG,KAAA,EAAO,KAAK,CAAA;AAGnC,MAAA,MAAM,KAAA,CAAM,OAAO,MAAM,CAAA;AAEzB,MAAA,OAAA,EAAA;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,SAAA;AACR;;;ACtHA,IAAM,UAAA,GAAuC;AAAA,EAC3C,KAAA,EAAO,CAAA;AAAA,EACP,IAAA,EAAM,CAAA;AAAA,EACN,IAAA,EAAM,CAAA;AAAA,EACN,KAAA,EAAO;AACT,CAAA;AAKO,IAAM,SAAN,MAAa;AAAA,EACV,OAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EAER,WAAA,CAAY,OAAA,GAAyB,EAAC,EAAG;AACvC,IAAA,IAAA,CAAK,OAAA,GAAU,QAAQ,KAAA,IAAS,KAAA;AAChC,IAAA,IAAA,CAAK,MAAA,GAAS,QAAQ,MAAA,IAAU,WAAA;AAChC,IAAA,IAAA,CAAK,QAAA,GAAW,UAAA,CAAW,OAAA,CAAQ,KAAA,IAAS,OAAO,CAAA;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,OAAA,EAAwB;AACjC,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,KAAA,EAAuB;AAC9B,IAAA,IAAA,CAAK,QAAA,GAAW,WAAW,KAAK,CAAA;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,KAAA,CAAM,YAAoB,IAAA,EAAuB;AAC/C,IAAA,IAAA,CAAK,GAAA,CAAI,OAAA,EAAS,OAAA,EAAS,GAAG,IAAI,CAAA;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAA,CAAK,YAAoB,IAAA,EAAuB;AAC9C,IAAA,IAAA,CAAK,GAAA,CAAI,MAAA,EAAQ,OAAA,EAAS,GAAG,IAAI,CAAA;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAA,CAAK,YAAoB,IAAA,EAAuB;AAC9C,IAAA,IAAA,CAAK,GAAA,CAAI,MAAA,EAAQ,OAAA,EAAS,GAAG,IAAI,CAAA;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAKA,KAAA,CAAM,YAAoB,IAAA,EAAuB;AAC/C,IAAA,IAAA,CAAK,GAAA,CAAI,OAAA,EAAS,OAAA,EAAS,GAAG,IAAI,CAAA;AAAA,EACpC;AAAA,EAEQ,GAAA,CAAI,KAAA,EAAiB,OAAA,EAAA,GAAoB,IAAA,EAAuB;AACtE,IAAA,IAAI,CAAC,IAAA,CAAK,OAAA,IAAW,KAAA,KAAU,OAAA,EAAS;AACtC,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,UAAA,CAAW,KAAK,CAAA,GAAI,IAAA,CAAK,QAAA,EAAU;AACrC,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,SAAA,GAAA,iBAAY,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AACzC,IAAA,MAAM,gBAAA,GAAmB,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,CAAA,EAAI,SAAS,CAAA,EAAA,EAAK,KAAA,CAAM,WAAA,EAAa,CAAA,EAAA,EAAK,OAAO,CAAA,CAAA;AAExF,IAAA,QAAQ,KAAA;AAAO,MACb,KAAK,OAAA;AACH,QAAA,OAAA,CAAQ,KAAA,CAAM,gBAAA,EAAkB,GAAG,IAAI,CAAA;AACvC,QAAA;AAAA,MACF,KAAK,MAAA;AACH,QAAA,OAAA,CAAQ,IAAA,CAAK,gBAAA,EAAkB,GAAG,IAAI,CAAA;AACtC,QAAA;AAAA,MACF,KAAK,MAAA;AACH,QAAA,OAAA,CAAQ,IAAA,CAAK,gBAAA,EAAkB,GAAG,IAAI,CAAA;AACtC,QAAA;AAAA,MACF,KAAK,OAAA;AACH,QAAA,OAAA,CAAQ,KAAA,CAAM,gBAAA,EAAkB,GAAG,IAAI,CAAA;AACvC,QAAA;AAAA;AACJ,EACF;AACF,CAAA;AAKO,SAAS,YAAA,CAAa,OAAA,GAAyB,EAAC,EAAW;AAChE,EAAA,OAAO,IAAI,OAAO,OAAO,CAAA;AAC3B;;;ACzGO,IAAM,YAAA,GAAN,cAA2B,KAAA,CAAM;AAAA;AAAA,EAE7B,IAAA;AAAA;AAAA,EAGA,SAAA;AAAA;AAAA,EAGA,KAAA;AAAA,EAET,WAAA,CACE,OAAA,EACA,IAAA,EACA,OAAA,GAAkD,EAAC,EACnD;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,cAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,SAAA,GAAY,QAAQ,SAAA,IAAa,KAAA;AACtC,IAAA,IAAA,CAAK,QAAQ,OAAA,CAAQ,KAAA;AAGrB,IAAA,IAAI,MAAM,iBAAA,EAAmB;AAC3B,MAAA,KAAA,CAAM,iBAAA,CAAkB,IAAA,EAAM,IAAA,CAAK,WAAW,CAAA;AAAA,IAChD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAA,GAAkC;AAChC,IAAA,OAAO;AAAA,MACL,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,SAAS,IAAA,CAAK,OAAA;AAAA,MACd,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,WAAW,IAAA,CAAK,SAAA;AAAA,MAChB,OAAO,IAAA,CAAK,KAAA;AAAA,MACZ,KAAA,EAAO,KAAK,KAAA,EAAO;AAAA,KACrB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,QAAA,GAAmB;AACjB,IAAA,OAAO,CAAA,EAAG,KAAK,IAAI,CAAA,EAAA,EAAK,KAAK,IAAI,CAAA,GAAA,EAAM,KAAK,OAAO,CAAA,CAAA;AAAA,EACrD;AACF;;;AC5CO,IAAM,mBAAA,GAAN,MAAM,oBAAA,SAA4B,YAAA,CAAa;AAAA,EACpD,YACE,OAAA,EACA,IAAA,GAAe,YAAY,YAAA,EAC3B,OAAA,GAAkD,EAAC,EACnD;AAEA,IAAA,KAAA,CAAM,SAAS,IAAA,EAAM,EAAE,WAAW,KAAA,EAAO,GAAG,SAAS,CAAA;AACrD,IAAA,IAAA,CAAK,IAAA,GAAO,qBAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,WAAW,OAAA,EAAuC;AACvD,IAAA,MAAM,OAAA,GAAU,OAAA,GACZ,CAAA,QAAA,EAAW,OAAO,CAAA,QAAA,CAAA,GAClB,iBAAA;AACJ,IAAA,OAAO,IAAI,oBAAA,CAAoB,OAAA,EAAS,WAAA,CAAY,eAAe,CAAA;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,aAAa,MAAA,EAAsC;AACxD,IAAA,OAAO,IAAI,oBAAA;AAAA,MACT,MAAA,IAAU,mCAAA;AAAA,MACV,WAAA,CAAY;AAAA,KACd;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,UAAU,MAAA,EAAsC;AACrD,IAAA,MAAM,OAAA,GAAU,MAAA,GACZ,CAAA,+CAAA,EAAkD,MAAM,CAAA,CAAA,GACxD,4CAAA;AACJ,IAAA,OAAO,IAAI,oBAAA,CAAoB,OAAA,EAAS,WAAA,CAAY,SAAS,CAAA;AAAA,EAC/D;AACF;;;ACxCO,IAAM,YAAA,GAAN,MAAM,aAAA,SAAqB,YAAA,CAAa;AAAA;AAAA,EAEpC,UAAA;AAAA,EAET,YACE,OAAA,EACA,IAAA,GAAe,YAAY,aAAA,EAC3B,OAAA,GAAuE,EAAC,EACxE;AACA,IAAA,KAAA,CAAM,SAAS,IAAA,EAAM,EAAE,WAAW,IAAA,EAAM,GAAG,SAAS,CAAA;AACpD,IAAA,IAAA,CAAK,IAAA,GAAO,cAAA;AACZ,IAAA,IAAA,CAAK,aAAa,OAAA,CAAQ,UAAA;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,UAAU,KAAA,EAA4B;AAC3C,IAAA,OAAO,IAAI,aAAA;AAAA,MACT,CAAA,wBAAA,EAA2B,MAAM,OAAO,CAAA,CAAA;AAAA,MACxC,WAAA,CAAY,aAAA;AAAA,MACZ,EAAE,SAAA,EAAW,IAAA,EAAM,KAAA;AAAM,KAC3B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,QAAQ,SAAA,EAAiC;AAC9C,IAAA,OAAO,IAAI,aAAA;AAAA,MACT,2BAA2B,SAAS,CAAA,EAAA,CAAA;AAAA,MACpC,WAAA,CAAY,eAAA;AAAA,MACZ,EAAE,WAAW,IAAA;AAAK,KACpB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,UAAA,CAAW,UAAA,EAAoB,OAAA,EAAgC;AACpE,IAAA,MAAM,cAAA,GAAiB,aAAA,CAAa,gBAAA,CAAiB,UAAU,CAAA;AAG/D,IAAA,MAAM,SAAA,GAAY,UAAA,IAAc,GAAA,IAAO,UAAA,KAAe,GAAA;AAGtD,IAAA,IAAI,OAAe,WAAA,CAAY,YAAA;AAC/B,IAAA,IAAI,eAAe,GAAA,EAAK;AACtB,MAAA,IAAA,GAAO,WAAA,CAAY,SAAA;AAAA,IACrB,CAAA,MAAA,IAAW,UAAA,KAAe,GAAA,IAAO,UAAA,KAAe,GAAA,EAAK;AACnD,MAAA,IAAA,GAAO,WAAA,CAAY,gBAAA;AAAA,IACrB,CAAA,MAAA,IAAW,eAAe,GAAA,EAAK;AAC7B,MAAA,IAAA,GAAO,WAAA,CAAY,YAAA;AAAA,IACrB;AAEA,IAAA,OAAO,IAAI,aAAA;AAAA,MACT,OAAA,IAAW,cAAA;AAAA,MACX,IAAA;AAAA,MACA,EAAE,WAAW,UAAA;AAAW,KAC1B;AAAA,EACF;AAAA,EAEA,OAAe,iBAAiB,UAAA,EAA4B;AAC1D,IAAA,MAAM,QAAA,GAAmC;AAAA,MACvC,GAAA,EAAK,aAAA;AAAA,MACL,GAAA,EAAK,oBAAA;AAAA,MACL,GAAA,EAAK,kBAAA;AAAA,MACL,GAAA,EAAK,qBAAA;AAAA,MACL,GAAA,EAAK,uBAAA;AAAA,MACL,GAAA,EAAK,aAAA;AAAA,MACL,GAAA,EAAK,qBAAA;AAAA,MACL,GAAA,EAAK;AAAA,KACP;AACA,IAAA,OAAO,QAAA,CAAS,UAAU,CAAA,IAAK,CAAA,WAAA,EAAc,UAAU,CAAA,CAAA;AAAA,EACzD;AAAA,EAEA,MAAA,GAAkC;AAChC,IAAA,OAAO;AAAA,MACL,GAAG,MAAM,MAAA,EAAO;AAAA,MAChB,YAAY,IAAA,CAAK;AAAA,KACnB;AAAA,EACF;AACF;;;AC7DO,IAAM,aAAN,MAAiB;AAAA,EACL,MAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,aAAA;AAAA,EACA,MAAA;AAAA,EAEjB,YAAY,OAAA,EAA4B;AACtC,IAAA,IAAA,CAAK,SAAS,OAAA,CAAQ,MAAA;AACtB,IAAA,IAAA,CAAK,WAAW,OAAA,CAAQ,OAAA,IAAW,SAAS,QAAA,EAAU,OAAA,CAAQ,OAAO,EAAE,CAAA;AACvE,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA,CAAQ,OAAA,IAAW,QAAA,CAAS,kBAAA;AAC3C,IAAA,IAAA,CAAK,aAAA,GAAgB,QAAQ,aAAA,IAAiB,CAAA;AAC9C,IAAA,IAAA,CAAK,MAAA,GAAS,aAAa,EAAE,KAAA,EAAO,QAAQ,KAAA,EAAO,MAAA,EAAQ,kBAAkB,CAAA;AAAA,EAC/E;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAA,CACJ,MAAA,EACA,IAAA,EACA,OAAA,GAII,EAAC,EACO;AACZ,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,QAAA,CAAS,IAAA,EAAM,QAAQ,KAAK,CAAA;AAE7C,IAAA,IAAA,CAAK,OAAO,KAAA,CAAM,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,GAAG,CAAA,CAAE,CAAA;AAEpC,IAAA,MAAM,SAAA,GAA0B;AAAA,MAC9B,aAAa,IAAA,CAAK,aAAA;AAAA,MAClB,WAAA,EAAa,CAAC,KAAA,KAAU;AACtB,QAAA,IAAI,iBAAiB,YAAA,EAAc;AACjC,UAAA,OAAO,KAAA,CAAM,SAAA;AAAA,QACf;AACA,QAAA,OAAO,KAAA;AAAA,MACT,CAAA;AAAA,MACA,OAAA,EAAS,CAAC,OAAA,EAAS,KAAA,EAAO,KAAA,KAAU;AAClC,QAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,0BAAA,EAA6B,OAAO,CAAA,CAAA,EAAI,KAAK,aAAa,CAAA,KAAA,EAAQ,KAAK,CAAA,EAAA,CAAA,EAAM,KAAK,CAAA;AAAA,MACrG,CAAA;AAAA,MACA,GAAG,OAAA,CAAQ;AAAA,KACb;AAEA,IAAA,OAAO,MAAM,YAAY;AACvB,MAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,MAAA,MAAM,YAAY,UAAA,CAAW,MAAM,WAAW,KAAA,EAAM,EAAG,KAAK,OAAO,CAAA;AAEnE,MAAA,IAAI;AACF,QAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,UAChC,MAAA;AAAA,UACA,OAAA,EAAS,KAAK,UAAA,EAAW;AAAA,UACzB,MAAM,OAAA,CAAQ,IAAA,GAAO,KAAK,SAAA,CAAU,OAAA,CAAQ,IAAI,CAAA,GAAI,KAAA,CAAA;AAAA,UACpD,QAAQ,UAAA,CAAW;AAAA,SACpB,CAAA;AAED,QAAA,OAAO,MAAM,IAAA,CAAK,cAAA,CAAkB,QAAQ,CAAA;AAAA,MAC9C,SAAS,KAAA,EAAO;AACd,QAAA,IAAI,KAAA,YAAiB,mBAAA,IAAuB,KAAA,YAAiB,YAAA,EAAc;AACzE,UAAA,MAAM,KAAA;AAAA,QACR;AAEA,QAAA,IAAI,KAAA,YAAiB,YAAA,IAAgB,KAAA,CAAM,IAAA,KAAS,YAAA,EAAc;AAChE,UAAA,MAAM,YAAA,CAAa,OAAA,CAAQ,IAAA,CAAK,OAAO,CAAA;AAAA,QACzC;AAEA,QAAA,IAAI,iBAAiB,SAAA,EAAW;AAC9B,UAAA,MAAM,YAAA,CAAa,UAAU,KAAK,CAAA;AAAA,QACpC;AAEA,QAAA,MAAM,KAAA;AAAA,MACR,CAAA,SAAE;AACA,QAAA,YAAA,CAAa,SAAS,CAAA;AAAA,MACxB;AAAA,IACF,GAAG,SAAS,CAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,GAAA,CAAO,MAAc,KAAA,EAAiE;AACpF,IAAA,OAAO,KAAK,OAAA,CAAW,KAAA,EAAO,IAAA,EAAM,EAAE,OAAO,CAAA;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKA,IAAA,CAAQ,MAAc,IAAA,EAA4B;AAChD,IAAA,OAAO,KAAK,OAAA,CAAW,MAAA,EAAQ,IAAA,EAAM,EAAE,MAAM,CAAA;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKA,KAAA,CAAS,MAAc,IAAA,EAA4B;AACjD,IAAA,OAAO,KAAK,OAAA,CAAW,OAAA,EAAS,IAAA,EAAM,EAAE,MAAM,CAAA;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAKA,OAAU,IAAA,EAA0B;AAClC,IAAA,OAAO,IAAA,CAAK,OAAA,CAAW,QAAA,EAAU,IAAI,CAAA;AAAA,EACvC;AAAA,EAEQ,QAAA,CAAS,MAAc,KAAA,EAA6D;AAC1F,IAAA,MAAM,QAAA,GAAW,CAAA,EAAG,QAAA,CAAS,WAAW,GAAG,IAAI,CAAA,CAAA;AAC/C,IAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,QAAA,EAAU,KAAK,OAAO,CAAA;AAE1C,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAC,GAAA,EAAK,KAAK,CAAA,KAAM;AAC9C,QAAA,IAAI,UAAU,MAAA,EAAW;AACvB,UAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,GAAA,EAAK,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,QACzC;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAEA,IAAA,OAAO,IAAI,QAAA,EAAS;AAAA,EACtB;AAAA,EAEQ,UAAA,GAAqC;AAC3C,IAAA,OAAO;AAAA,MACL,cAAA,EAAgB,kBAAA;AAAA,MAChB,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,MAAM,CAAA;AAAA,KACtC;AAAA,EACF;AAAA,EAEA,MAAc,eAAkB,QAAA,EAAgC;AAE9D,IAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AAC3B,MAAA,MAAM,oBAAoB,YAAA,EAAa;AAAA,IACzC;AAEA,IAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AAC3B,MAAA,MAAM,oBAAoB,SAAA,EAAU;AAAA,IACtC;AAGA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,IAAI,YAAA;AAEJ,MAAA,IAAI;AACF,QAAA,MAAM,SAAA,GAAa,MAAM,QAAA,CAAS,IAAA,EAAK;AACvC,QAAA,YAAA,GAAe,SAAA,CAAU,KAAA;AAAA,MAC3B,CAAA,CAAA,MAAQ;AAAA,MAER;AAEA,MAAA,MAAM,YAAA,CAAa,UAAA,CAAW,QAAA,CAAS,MAAA,EAAQ,YAAY,CAAA;AAAA,IAC7D;AAGA,IAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AAC3B,MAAA,OAAO,MAAA;AAAA,IACT;AAGA,IAAA,IAAI;AACF,MAAA,OAAQ,MAAM,SAAS,IAAA,EAAK;AAAA,IAC9B,CAAA,CAAA,MAAQ;AACN,MAAA,MAAM,IAAI,YAAA,CAAa,0BAAA,EAA4B,eAAe,EAAE,SAAA,EAAW,OAAO,CAAA;AAAA,IACxF;AAAA,EACF;AACF,CAAA;;;AChKO,IAAM,gBAAN,MAAoB;AAAA,EACR,MAAA;AAAA,EAEjB,YAAY,OAAA,EAAwB;AAClC,IAAA,IAAI,CAAC,QAAQ,SAAA,EAAW;AACtB,MAAA,MAAM,IAAI,mBAAA;AAAA,QACR,wBAAA;AAAA,QACA;AAAA,OACF;AAAA,IACF;AAEA,IAAA,IAAI,CAAC,OAAA,CAAQ,SAAA,CAAU,UAAA,CAAW,MAAM,CAAA,IAAK,CAAC,OAAA,CAAQ,SAAA,CAAU,UAAA,CAAW,OAAO,CAAA,EAAG;AACnF,MAAA,MAAM,mBAAA,CAAoB,WAAW,QAAQ,CAAA;AAAA,IAC/C;AAEA,IAAA,IAAA,CAAK,MAAA,GAAS,IAAI,UAAA,CAAW;AAAA,MAC3B,QAAQ,OAAA,CAAQ,SAAA;AAAA,MAChB,SAAS,OAAA,CAAQ,OAAA;AAAA,MACjB,SAAS,OAAA,CAAQ,OAAA;AAAA,MACjB,eAAe,OAAA,CAAQ,aAAA;AAAA,MACvB,OAAO,OAAA,CAAQ;AAAA,KAChB,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,YAAA,CACJ,OAAA,GAA+B,EAAC,EAC0B;AAC1D,IAAA,MAAM,WAAW,MAAM,IAAA,CAAK,MAAA,CAAO,GAAA,CAAsB,UAAU,QAAA,EAAU;AAAA,MAC3E,MAAM,OAAA,CAAQ,IAAA;AAAA,MACd,QAAA,EAAU,OAAA,CAAQ,QAAA,IAAY,QAAA,CAAS;AAAA,KACxC,CAAA;AAED,IAAA,OAAO;AAAA,MACL,UAAU,QAAA,CAAS,QAAA;AAAA,MACnB,YAAY,QAAA,CAAS;AAAA,KACvB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAW,IAAA,EAAgC;AAC/C,IAAA,OAAO,KAAK,MAAA,CAAO,GAAA,CAAa,SAAA,CAAU,OAAA,CAAQ,IAAI,CAAC,CAAA;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,KAAA,EAAuC;AACzD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,IAAA,CAAc,SAAA,CAAU,UAAU,KAAK,CAAA;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAA,CAAc,IAAA,EAAc,KAAA,EAA6C;AAC7E,IAAA,OAAO,KAAK,MAAA,CAAO,KAAA,CAAe,UAAU,OAAA,CAAQ,IAAI,GAAG,KAAK,CAAA;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,IAAA,EAA6B;AAC/C,IAAA,MAAM,KAAK,MAAA,CAAO,MAAA,CAAa,SAAA,CAAU,OAAA,CAAQ,IAAI,CAAC,CAAA;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,OAAA,CACJ,OAAA,EACA,OAAA,EACA,OAAA,GAA0B,EAAC,EACH;AACxB,IAAA,OAAO,KAAK,MAAA,CAAO,IAAA,CAAoB,SAAA,CAAU,gBAAA,CAAiB,OAAO,CAAA,EAAG;AAAA,MAC1E,OAAA;AAAA,MACA,SAAA,EAAW,QAAQ,SAAA,IAAa;AAAA,KACjC,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAA,CACJ,OAAA,EACA,OAAA,GAAiC,EAAC,EACwB;AAC1D,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,MAAA,CAAO,GAAA;AAAA,MACjC,SAAA,CAAU,iBAAiB,OAAO,CAAA;AAAA,MAClC;AAAA,QACE,MAAM,OAAA,CAAQ,IAAA;AAAA,QACd,QAAA,EAAU,OAAA,CAAQ,QAAA,IAAY,QAAA,CAAS,SAAA;AAAA,QACvC,WAAW,OAAA,CAAQ,SAAA;AAAA,QACnB,OAAO,OAAA,CAAQ,KAAA;AAAA,QACf,QAAQ,OAAA,CAAQ;AAAA;AAClB,KACF;AAEA,IAAA,OAAO;AAAA,MACL,UAAU,QAAA,CAAS,QAAA;AAAA,MACnB,YAAY,QAAA,CAAS;AAAA,KACvB;AAAA,EACF;AACF","file":"server.js","sourcesContent":["/**\n * Default configuration values\n */\nexport const DEFAULTS = {\n /** Default PushFlo API base URL */\n BASE_URL: 'https://api.pushflo.dev',\n\n /** WebSocket endpoint path */\n WS_PATH: '/ws',\n\n /** API version prefix */\n API_VERSION: '/api/v1',\n\n /** Connection timeout in milliseconds */\n CONNECTION_TIMEOUT: 30000,\n\n /** Heartbeat interval in milliseconds */\n HEARTBEAT_INTERVAL: 25000,\n\n /** Initial reconnection delay in milliseconds */\n RECONNECT_DELAY: 1000,\n\n /** Maximum reconnection delay in milliseconds */\n MAX_RECONNECT_DELAY: 30000,\n\n /** Reconnection delay multiplier for exponential backoff */\n RECONNECT_MULTIPLIER: 1.5,\n\n /** Maximum number of reconnection attempts (0 = infinite) */\n MAX_RECONNECT_ATTEMPTS: 0,\n\n /** Default page size for list operations */\n PAGE_SIZE: 25,\n} as const;\n\n/**\n * API endpoint paths\n */\nexport const API_PATHS = {\n AUTH_TOKEN: '/auth/token',\n CHANNELS: '/channels',\n CHANNEL: (slug: string) => `/channels/${encodeURIComponent(slug)}`,\n CHANNEL_MESSAGES: (slug: string) => `/channels/${encodeURIComponent(slug)}/messages`,\n} as const;\n\n/**\n * WebSocket message types (client -> server)\n */\nexport const WS_CLIENT_MESSAGES = {\n SUBSCRIBE: 'subscribe',\n UNSUBSCRIBE: 'unsubscribe',\n PING: 'ping',\n ACK: 'ack',\n} as const;\n\n/**\n * WebSocket message types (server -> client)\n */\nexport const WS_SERVER_MESSAGES = {\n CONNECTED: 'connected',\n SUBSCRIBED: 'subscribed',\n UNSUBSCRIBED: 'unsubscribed',\n MESSAGE: 'message',\n ERROR: 'error',\n PONG: 'pong',\n} as const;\n\n/**\n * Error codes\n */\nexport const ERROR_CODES = {\n // Connection errors\n CONNECTION_FAILED: 'CONNECTION_FAILED',\n CONNECTION_TIMEOUT: 'CONNECTION_TIMEOUT',\n CONNECTION_CLOSED: 'CONNECTION_CLOSED',\n\n // Authentication errors\n INVALID_API_KEY: 'INVALID_API_KEY',\n UNAUTHORIZED: 'UNAUTHORIZED',\n FORBIDDEN: 'FORBIDDEN',\n\n // Network errors\n NETWORK_ERROR: 'NETWORK_ERROR',\n REQUEST_TIMEOUT: 'REQUEST_TIMEOUT',\n\n // API errors\n NOT_FOUND: 'NOT_FOUND',\n VALIDATION_ERROR: 'VALIDATION_ERROR',\n RATE_LIMITED: 'RATE_LIMITED',\n SERVER_ERROR: 'SERVER_ERROR',\n\n // Client errors\n INVALID_STATE: 'INVALID_STATE',\n ALREADY_CONNECTED: 'ALREADY_CONNECTED',\n NOT_CONNECTED: 'NOT_CONNECTED',\n CHANNEL_NOT_FOUND: 'CHANNEL_NOT_FOUND',\n} as const;\n","import { DEFAULTS } from './constants.js';\n\nexport interface RetryOptions {\n /** Maximum number of retry attempts (0 = infinite) */\n maxAttempts?: number;\n /** Initial delay in milliseconds */\n initialDelay?: number;\n /** Maximum delay in milliseconds */\n maxDelay?: number;\n /** Delay multiplier for exponential backoff */\n multiplier?: number;\n /** Function to determine if error is retryable */\n isRetryable?: (error: unknown) => boolean;\n /** Callback for each retry attempt */\n onRetry?: (attempt: number, delay: number, error: unknown) => void;\n /** Abort signal for cancellation */\n signal?: AbortSignal;\n}\n\n/**\n * Calculate delay with exponential backoff and jitter\n */\nexport function calculateBackoff(\n attempt: number,\n options: {\n initialDelay?: number;\n maxDelay?: number;\n multiplier?: number;\n } = {}\n): number {\n const {\n initialDelay = DEFAULTS.RECONNECT_DELAY,\n maxDelay = DEFAULTS.MAX_RECONNECT_DELAY,\n multiplier = DEFAULTS.RECONNECT_MULTIPLIER,\n } = options;\n\n // Exponential backoff\n const exponentialDelay = initialDelay * Math.pow(multiplier, attempt);\n\n // Cap at max delay\n const cappedDelay = Math.min(exponentialDelay, maxDelay);\n\n // Add jitter (±25%)\n const jitter = cappedDelay * 0.25 * (Math.random() * 2 - 1);\n\n return Math.floor(cappedDelay + jitter);\n}\n\n/**\n * Sleep for a given duration (cancellable)\n */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(new DOMException('Aborted', 'AbortError'));\n return;\n }\n\n const timeoutId = setTimeout(resolve, ms);\n\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timeoutId);\n reject(new DOMException('Aborted', 'AbortError'));\n },\n { once: true }\n );\n });\n}\n\n/**\n * Retry a function with exponential backoff\n */\nexport async function retry<T>(\n fn: () => Promise<T>,\n options: RetryOptions = {}\n): Promise<T> {\n const {\n maxAttempts = 3,\n initialDelay = DEFAULTS.RECONNECT_DELAY,\n maxDelay = DEFAULTS.MAX_RECONNECT_DELAY,\n multiplier = DEFAULTS.RECONNECT_MULTIPLIER,\n isRetryable = () => true,\n onRetry,\n signal,\n } = options;\n\n let lastError: unknown;\n let attempt = 0;\n\n while (maxAttempts === 0 || attempt < maxAttempts) {\n try {\n if (signal?.aborted) {\n throw new DOMException('Aborted', 'AbortError');\n }\n return await fn();\n } catch (error) {\n lastError = error;\n\n // Don't retry abort errors\n if (error instanceof DOMException && error.name === 'AbortError') {\n throw error;\n }\n\n // Check if we should retry\n if (!isRetryable(error)) {\n throw error;\n }\n\n // Check if we've exhausted attempts\n if (maxAttempts !== 0 && attempt >= maxAttempts - 1) {\n throw error;\n }\n\n // Calculate delay\n const delay = calculateBackoff(attempt, { initialDelay, maxDelay, multiplier });\n\n // Notify retry callback\n onRetry?.(attempt + 1, delay, error);\n\n // Wait before next attempt\n await sleep(delay, signal);\n\n attempt++;\n }\n }\n\n throw lastError;\n}\n\n/**\n * Create a retry function with preset options\n */\nexport function createRetry(defaultOptions: RetryOptions) {\n return <T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T> => {\n return retry(fn, { ...defaultOptions, ...options });\n };\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport interface LoggerOptions {\n /** Enable debug logging */\n debug?: boolean;\n /** Custom log prefix */\n prefix?: string;\n /** Minimum log level */\n level?: LogLevel;\n}\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\n/**\n * Simple logger with debug mode support\n */\nexport class Logger {\n private enabled: boolean;\n private prefix: string;\n private minLevel: number;\n\n constructor(options: LoggerOptions = {}) {\n this.enabled = options.debug ?? false;\n this.prefix = options.prefix ?? '[PushFlo]';\n this.minLevel = LOG_LEVELS[options.level ?? 'debug'];\n }\n\n /**\n * Enable or disable logging\n */\n setEnabled(enabled: boolean): void {\n this.enabled = enabled;\n }\n\n /**\n * Set minimum log level\n */\n setLevel(level: LogLevel): void {\n this.minLevel = LOG_LEVELS[level];\n }\n\n /**\n * Log a debug message\n */\n debug(message: string, ...args: unknown[]): void {\n this.log('debug', message, ...args);\n }\n\n /**\n * Log an info message\n */\n info(message: string, ...args: unknown[]): void {\n this.log('info', message, ...args);\n }\n\n /**\n * Log a warning message\n */\n warn(message: string, ...args: unknown[]): void {\n this.log('warn', message, ...args);\n }\n\n /**\n * Log an error message\n */\n error(message: string, ...args: unknown[]): void {\n this.log('error', message, ...args);\n }\n\n private log(level: LogLevel, message: string, ...args: unknown[]): void {\n if (!this.enabled && level !== 'error') {\n return;\n }\n\n if (LOG_LEVELS[level] < this.minLevel) {\n return;\n }\n\n const timestamp = new Date().toISOString();\n const formattedMessage = `${this.prefix} ${timestamp} [${level.toUpperCase()}] ${message}`;\n\n switch (level) {\n case 'debug':\n console.debug(formattedMessage, ...args);\n break;\n case 'info':\n console.info(formattedMessage, ...args);\n break;\n case 'warn':\n console.warn(formattedMessage, ...args);\n break;\n case 'error':\n console.error(formattedMessage, ...args);\n break;\n }\n }\n}\n\n/**\n * Create a child logger with a custom prefix\n */\nexport function createLogger(options: LoggerOptions = {}): Logger {\n return new Logger(options);\n}\n","/**\n * Base error class for all PushFlo SDK errors\n */\nexport class PushFloError extends Error {\n /** Error code for programmatic handling */\n readonly code: string;\n\n /** Whether this error is potentially recoverable through retry */\n readonly retryable: boolean;\n\n /** Original error that caused this error, if any */\n readonly cause?: Error;\n\n constructor(\n message: string,\n code: string,\n options: { retryable?: boolean; cause?: Error } = {}\n ) {\n super(message);\n this.name = 'PushFloError';\n this.code = code;\n this.retryable = options.retryable ?? false;\n this.cause = options.cause;\n\n // Maintain proper stack trace in V8 environments\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n\n /**\n * Convert error to JSON-serializable object\n */\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n retryable: this.retryable,\n stack: this.stack,\n cause: this.cause?.message,\n };\n }\n\n /**\n * Create a string representation\n */\n toString(): string {\n return `${this.name} [${this.code}]: ${this.message}`;\n }\n}\n","import { PushFloError } from './PushFloError.js';\nimport { ERROR_CODES } from '../utils/constants.js';\n\n/**\n * Error thrown when authentication fails\n */\nexport class AuthenticationError extends PushFloError {\n constructor(\n message: string,\n code: string = ERROR_CODES.UNAUTHORIZED,\n options: { retryable?: boolean; cause?: Error } = {}\n ) {\n // Authentication errors are generally not retryable\n super(message, code, { retryable: false, ...options });\n this.name = 'AuthenticationError';\n }\n\n /**\n * Create an invalid API key error\n */\n static invalidKey(keyType?: string): AuthenticationError {\n const message = keyType\n ? `Invalid ${keyType} API key`\n : 'Invalid API key';\n return new AuthenticationError(message, ERROR_CODES.INVALID_API_KEY);\n }\n\n /**\n * Create an unauthorized error\n */\n static unauthorized(reason?: string): AuthenticationError {\n return new AuthenticationError(\n reason ?? 'Unauthorized - check your API key',\n ERROR_CODES.UNAUTHORIZED\n );\n }\n\n /**\n * Create a forbidden error\n */\n static forbidden(action?: string): AuthenticationError {\n const message = action\n ? `Access forbidden: insufficient permissions for ${action}`\n : 'Access forbidden: insufficient permissions';\n return new AuthenticationError(message, ERROR_CODES.FORBIDDEN);\n }\n}\n","import { PushFloError } from './PushFloError.js';\nimport { ERROR_CODES } from '../utils/constants.js';\n\n/**\n * Error thrown for network-related failures\n */\nexport class NetworkError extends PushFloError {\n /** HTTP status code, if applicable */\n readonly statusCode?: number;\n\n constructor(\n message: string,\n code: string = ERROR_CODES.NETWORK_ERROR,\n options: { retryable?: boolean; cause?: Error; statusCode?: number } = {}\n ) {\n super(message, code, { retryable: true, ...options });\n this.name = 'NetworkError';\n this.statusCode = options.statusCode;\n }\n\n /**\n * Create a network error from fetch failure\n */\n static fromFetch(cause: Error): NetworkError {\n return new NetworkError(\n `Network request failed: ${cause.message}`,\n ERROR_CODES.NETWORK_ERROR,\n { retryable: true, cause }\n );\n }\n\n /**\n * Create a request timeout error\n */\n static timeout(timeoutMs: number): NetworkError {\n return new NetworkError(\n `Request timed out after ${timeoutMs}ms`,\n ERROR_CODES.REQUEST_TIMEOUT,\n { retryable: true }\n );\n }\n\n /**\n * Create an error from HTTP status code\n */\n static fromStatus(statusCode: number, message?: string): NetworkError {\n const defaultMessage = NetworkError.getStatusMessage(statusCode);\n\n // Determine if retryable based on status\n const retryable = statusCode >= 500 || statusCode === 429;\n\n // Map status to error code\n let code: string = ERROR_CODES.SERVER_ERROR;\n if (statusCode === 404) {\n code = ERROR_CODES.NOT_FOUND;\n } else if (statusCode === 422 || statusCode === 400) {\n code = ERROR_CODES.VALIDATION_ERROR;\n } else if (statusCode === 429) {\n code = ERROR_CODES.RATE_LIMITED;\n }\n\n return new NetworkError(\n message ?? defaultMessage,\n code,\n { retryable, statusCode }\n );\n }\n\n private static getStatusMessage(statusCode: number): string {\n const messages: Record<number, string> = {\n 400: 'Bad request',\n 404: 'Resource not found',\n 422: 'Validation error',\n 429: 'Rate limit exceeded',\n 500: 'Internal server error',\n 502: 'Bad gateway',\n 503: 'Service unavailable',\n 504: 'Gateway timeout',\n };\n return messages[statusCode] ?? `HTTP error ${statusCode}`;\n }\n\n toJSON(): Record<string, unknown> {\n return {\n ...super.toJSON(),\n statusCode: this.statusCode,\n };\n }\n}\n","import { DEFAULTS } from '../utils/constants.js';\nimport { retry, type RetryOptions } from '../utils/retry.js';\nimport { createLogger, type Logger } from '../utils/logger.js';\nimport { AuthenticationError } from '../errors/AuthenticationError.js';\nimport { NetworkError } from '../errors/NetworkError.js';\nimport type { ApiErrorResponse } from '../types/api.js';\n\nexport interface RestClientOptions {\n /** API key (secret or management key) */\n apiKey: string;\n\n /** Base URL for the API */\n baseUrl?: string;\n\n /** Request timeout in milliseconds */\n timeout?: number;\n\n /** Number of retry attempts */\n retryAttempts?: number;\n\n /** Enable debug logging */\n debug?: boolean;\n}\n\n/**\n * HTTP client for PushFlo REST API\n */\nexport class RestClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n private readonly retryAttempts: number;\n private readonly logger: Logger;\n\n constructor(options: RestClientOptions) {\n this.apiKey = options.apiKey;\n this.baseUrl = (options.baseUrl ?? DEFAULTS.BASE_URL).replace(/\\/$/, '');\n this.timeout = options.timeout ?? DEFAULTS.CONNECTION_TIMEOUT;\n this.retryAttempts = options.retryAttempts ?? 3;\n this.logger = createLogger({ debug: options.debug, prefix: '[PushFlo REST]' });\n }\n\n /**\n * Make an HTTP request to the API\n */\n async request<T>(\n method: string,\n path: string,\n options: {\n body?: unknown;\n query?: Record<string, string | number | undefined>;\n retryOptions?: Partial<RetryOptions>;\n } = {}\n ): Promise<T> {\n const url = this.buildUrl(path, options.query);\n\n this.logger.debug(`${method} ${url}`);\n\n const retryOpts: RetryOptions = {\n maxAttempts: this.retryAttempts,\n isRetryable: (error) => {\n if (error instanceof NetworkError) {\n return error.retryable;\n }\n return false;\n },\n onRetry: (attempt, delay, error) => {\n this.logger.warn(`Request failed, retrying (${attempt}/${this.retryAttempts}) in ${delay}ms`, error);\n },\n ...options.retryOptions,\n };\n\n return retry(async () => {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(url, {\n method,\n headers: this.getHeaders(),\n body: options.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n });\n\n return await this.handleResponse<T>(response);\n } catch (error) {\n if (error instanceof AuthenticationError || error instanceof NetworkError) {\n throw error;\n }\n\n if (error instanceof DOMException && error.name === 'AbortError') {\n throw NetworkError.timeout(this.timeout);\n }\n\n if (error instanceof TypeError) {\n throw NetworkError.fromFetch(error);\n }\n\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }, retryOpts);\n }\n\n /**\n * Make a GET request\n */\n get<T>(path: string, query?: Record<string, string | number | undefined>): Promise<T> {\n return this.request<T>('GET', path, { query });\n }\n\n /**\n * Make a POST request\n */\n post<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('POST', path, { body });\n }\n\n /**\n * Make a PATCH request\n */\n patch<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('PATCH', path, { body });\n }\n\n /**\n * Make a DELETE request\n */\n delete<T>(path: string): Promise<T> {\n return this.request<T>('DELETE', path);\n }\n\n private buildUrl(path: string, query?: Record<string, string | number | undefined>): string {\n const fullPath = `${DEFAULTS.API_VERSION}${path}`;\n const url = new URL(fullPath, this.baseUrl);\n\n if (query) {\n Object.entries(query).forEach(([key, value]) => {\n if (value !== undefined) {\n url.searchParams.set(key, String(value));\n }\n });\n }\n\n return url.toString();\n }\n\n private getHeaders(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n };\n }\n\n private async handleResponse<T>(response: Response): Promise<T> {\n // Handle authentication errors\n if (response.status === 401) {\n throw AuthenticationError.unauthorized();\n }\n\n if (response.status === 403) {\n throw AuthenticationError.forbidden();\n }\n\n // Handle other errors\n if (!response.ok) {\n let errorMessage: string | undefined;\n\n try {\n const errorBody = (await response.json()) as ApiErrorResponse;\n errorMessage = errorBody.error;\n } catch {\n // Ignore JSON parse errors\n }\n\n throw NetworkError.fromStatus(response.status, errorMessage);\n }\n\n // Handle empty responses\n if (response.status === 204) {\n return undefined as T;\n }\n\n // Parse JSON response\n try {\n return (await response.json()) as T;\n } catch {\n throw new NetworkError('Failed to parse response', 'PARSE_ERROR', { retryable: false });\n }\n }\n}\n","import { RestClient } from './RestClient.js';\nimport { API_PATHS, DEFAULTS } from '../utils/constants.js';\nimport { AuthenticationError } from '../errors/AuthenticationError.js';\nimport type { ServerOptions } from '../types/connection.js';\nimport type {\n Channel,\n ChannelInput,\n ChannelUpdateInput,\n ListChannelsOptions,\n} from '../types/channel.js';\nimport type {\n Message,\n PublishOptions,\n PublishResult,\n MessageHistoryOptions,\n} from '../types/message.js';\nimport type { Pagination } from '../types/api.js';\n\ninterface ChannelsResponse {\n channels: Channel[];\n pagination: Pagination;\n}\n\ninterface MessagesResponse {\n messages: Message[];\n pagination: Pagination;\n}\n\n/**\n * Server-side PushFlo client for publishing messages and managing channels\n */\nexport class PushFloServer {\n private readonly client: RestClient;\n\n constructor(options: ServerOptions) {\n if (!options.secretKey) {\n throw new AuthenticationError(\n 'Secret key is required',\n 'MISSING_SECRET_KEY'\n );\n }\n\n if (!options.secretKey.startsWith('sec_') && !options.secretKey.startsWith('mgmt_')) {\n throw AuthenticationError.invalidKey('secret');\n }\n\n this.client = new RestClient({\n apiKey: options.secretKey,\n baseUrl: options.baseUrl,\n timeout: options.timeout,\n retryAttempts: options.retryAttempts,\n debug: options.debug,\n });\n }\n\n // ============================================\n // Channel Management\n // ============================================\n\n /**\n * List all channels\n */\n async listChannels(\n options: ListChannelsOptions = {}\n ): Promise<{ channels: Channel[]; pagination: Pagination }> {\n const response = await this.client.get<ChannelsResponse>(API_PATHS.CHANNELS, {\n page: options.page,\n pageSize: options.pageSize ?? DEFAULTS.PAGE_SIZE,\n });\n\n return {\n channels: response.channels,\n pagination: response.pagination,\n };\n }\n\n /**\n * Get a channel by slug\n */\n async getChannel(slug: string): Promise<Channel> {\n return this.client.get<Channel>(API_PATHS.CHANNEL(slug));\n }\n\n /**\n * Create a new channel\n */\n async createChannel(input: ChannelInput): Promise<Channel> {\n return this.client.post<Channel>(API_PATHS.CHANNELS, input);\n }\n\n /**\n * Update an existing channel\n */\n async updateChannel(slug: string, input: ChannelUpdateInput): Promise<Channel> {\n return this.client.patch<Channel>(API_PATHS.CHANNEL(slug), input);\n }\n\n /**\n * Delete a channel\n */\n async deleteChannel(slug: string): Promise<void> {\n await this.client.delete<void>(API_PATHS.CHANNEL(slug));\n }\n\n // ============================================\n // Message Publishing\n // ============================================\n\n /**\n * Publish a message to a channel\n */\n async publish(\n channel: string,\n content: Record<string, unknown>,\n options: PublishOptions = {}\n ): Promise<PublishResult> {\n return this.client.post<PublishResult>(API_PATHS.CHANNEL_MESSAGES(channel), {\n content,\n eventType: options.eventType ?? 'message',\n });\n }\n\n /**\n * Get message history for a channel\n */\n async getMessageHistory(\n channel: string,\n options: MessageHistoryOptions = {}\n ): Promise<{ messages: Message[]; pagination: Pagination }> {\n const response = await this.client.get<MessagesResponse>(\n API_PATHS.CHANNEL_MESSAGES(channel),\n {\n page: options.page,\n pageSize: options.pageSize ?? DEFAULTS.PAGE_SIZE,\n eventType: options.eventType,\n after: options.after,\n before: options.before,\n }\n );\n\n return {\n messages: response.messages,\n pagination: response.pagination,\n };\n }\n}\n"]}