@pushflodev/sdk 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js CHANGED
@@ -1,4 +1,4 @@
1
- /* @pushflo/sdk - https://pushflo.dev */
1
+ /* @pushflodev/sdk - https://pushflo.dev */
2
2
 
3
3
  // src/utils/constants.ts
4
4
  var DEFAULTS = {
@@ -447,13 +447,128 @@ var RestClient = class {
447
447
  return void 0;
448
448
  }
449
449
  try {
450
- return await response.json();
450
+ const json = await response.json();
451
+ if (json && typeof json === "object" && "success" in json && "data" in json) {
452
+ return json.data;
453
+ }
454
+ return json;
451
455
  } catch {
452
456
  throw new NetworkError("Failed to parse response", "PARSE_ERROR", { retryable: false });
453
457
  }
454
458
  }
455
459
  };
456
460
 
461
+ // src/errors/ValidationError.ts
462
+ var ValidationError = class _ValidationError extends PushFloError {
463
+ /** The field that failed validation */
464
+ field;
465
+ constructor(message, field) {
466
+ super(message, "VALIDATION_ERROR", { retryable: false });
467
+ this.name = "ValidationError";
468
+ this.field = field;
469
+ }
470
+ /**
471
+ * Create an error for an invalid channel slug
472
+ */
473
+ static invalidChannelSlug(slug) {
474
+ return new _ValidationError(
475
+ `Invalid channel slug '${slug}': must be 1-64 characters, lowercase alphanumeric with hyphens, cannot start or end with a hyphen`,
476
+ "slug"
477
+ );
478
+ }
479
+ /**
480
+ * Create an error for a required field
481
+ */
482
+ static required(field) {
483
+ return new _ValidationError(`${field} is required`, field);
484
+ }
485
+ toJSON() {
486
+ return {
487
+ ...super.toJSON(),
488
+ field: this.field
489
+ };
490
+ }
491
+ };
492
+
493
+ // src/utils/validation.ts
494
+ var MAX_SLUG_LENGTH = 64;
495
+ var MIN_SLUG_LENGTH = 1;
496
+ var SLUG_REGEX = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
497
+ function isValidChannelSlug(slug) {
498
+ if (!slug || typeof slug !== "string") {
499
+ return false;
500
+ }
501
+ if (slug.length < MIN_SLUG_LENGTH || slug.length > MAX_SLUG_LENGTH) {
502
+ return false;
503
+ }
504
+ if (slug.includes("--")) {
505
+ return false;
506
+ }
507
+ return SLUG_REGEX.test(slug);
508
+ }
509
+ function toChannelSlug(str) {
510
+ return str.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-").slice(0, MAX_SLUG_LENGTH);
511
+ }
512
+ function validateChannelSlug(slug) {
513
+ if (!slug || typeof slug !== "string") {
514
+ return {
515
+ valid: false,
516
+ error: "Channel slug is required"
517
+ };
518
+ }
519
+ if (slug.length < MIN_SLUG_LENGTH) {
520
+ return {
521
+ valid: false,
522
+ error: "Channel slug cannot be empty"
523
+ };
524
+ }
525
+ if (slug.length > MAX_SLUG_LENGTH) {
526
+ return {
527
+ valid: false,
528
+ error: `Channel slug cannot exceed ${MAX_SLUG_LENGTH} characters (got ${slug.length})`,
529
+ suggestion: toChannelSlug(slug)
530
+ };
531
+ }
532
+ if (slug !== slug.toLowerCase()) {
533
+ return {
534
+ valid: false,
535
+ error: "Channel slug must be lowercase",
536
+ suggestion: toChannelSlug(slug)
537
+ };
538
+ }
539
+ if (slug.startsWith("-")) {
540
+ return {
541
+ valid: false,
542
+ error: "Channel slug cannot start with a hyphen",
543
+ suggestion: toChannelSlug(slug)
544
+ };
545
+ }
546
+ if (slug.endsWith("-")) {
547
+ return {
548
+ valid: false,
549
+ error: "Channel slug cannot end with a hyphen",
550
+ suggestion: toChannelSlug(slug)
551
+ };
552
+ }
553
+ if (slug.includes("--")) {
554
+ return {
555
+ valid: false,
556
+ error: "Channel slug cannot contain consecutive hyphens",
557
+ suggestion: toChannelSlug(slug)
558
+ };
559
+ }
560
+ const invalidChars = slug.match(/[^a-z0-9-]/g);
561
+ if (invalidChars) {
562
+ const uniqueChars = [...new Set(invalidChars)].join(", ");
563
+ return {
564
+ valid: false,
565
+ error: `Channel slug contains invalid characters: ${uniqueChars}. Only lowercase letters, numbers, and hyphens are allowed.`,
566
+ suggestion: toChannelSlug(slug)
567
+ };
568
+ }
569
+ return { valid: true };
570
+ }
571
+
457
572
  // src/server/PushFloServer.ts
458
573
  var PushFloServer = class {
459
574
  client;
@@ -495,33 +610,55 @@ var PushFloServer = class {
495
610
  * Get a channel by slug
496
611
  */
497
612
  async getChannel(slug) {
613
+ this.validateSlug(slug);
498
614
  return this.client.get(API_PATHS.CHANNEL(slug));
499
615
  }
500
616
  /**
501
617
  * Create a new channel
618
+ *
619
+ * @throws {ValidationError} If the channel slug is invalid
502
620
  */
503
621
  async createChannel(input) {
622
+ this.validateSlug(input.slug);
504
623
  return this.client.post(API_PATHS.CHANNELS, input);
505
624
  }
506
625
  /**
507
626
  * Update an existing channel
508
627
  */
509
628
  async updateChannel(slug, input) {
629
+ this.validateSlug(slug);
510
630
  return this.client.patch(API_PATHS.CHANNEL(slug), input);
511
631
  }
512
632
  /**
513
633
  * Delete a channel
514
634
  */
515
635
  async deleteChannel(slug) {
636
+ this.validateSlug(slug);
516
637
  await this.client.delete(API_PATHS.CHANNEL(slug));
517
638
  }
639
+ /**
640
+ * Validate a channel slug and throw if invalid
641
+ */
642
+ validateSlug(slug) {
643
+ const result = validateChannelSlug(slug);
644
+ if (!result.valid) {
645
+ const error = ValidationError.invalidChannelSlug(slug);
646
+ if (result.suggestion) {
647
+ error.message += `. Suggested: '${result.suggestion}'`;
648
+ }
649
+ throw error;
650
+ }
651
+ }
518
652
  // ============================================
519
653
  // Message Publishing
520
654
  // ============================================
521
655
  /**
522
656
  * Publish a message to a channel
657
+ *
658
+ * @throws {ValidationError} If the channel slug is invalid
523
659
  */
524
660
  async publish(channel, content, options = {}) {
661
+ this.validateSlug(channel);
525
662
  return this.client.post(API_PATHS.CHANNEL_MESSAGES(channel), {
526
663
  content,
527
664
  eventType: options.eventType ?? "message"
@@ -529,8 +666,11 @@ var PushFloServer = class {
529
666
  }
530
667
  /**
531
668
  * Get message history for a channel
669
+ *
670
+ * @throws {ValidationError} If the channel slug is invalid
532
671
  */
533
672
  async getMessageHistory(channel, options = {}) {
673
+ this.validateSlug(channel);
534
674
  const response = await this.client.get(
535
675
  API_PATHS.CHANNEL_MESSAGES(channel),
536
676
  {
@@ -542,12 +682,12 @@ var PushFloServer = class {
542
682
  }
543
683
  );
544
684
  return {
545
- messages: response.messages,
685
+ messages: response.items,
546
686
  pagination: response.pagination
547
687
  };
548
688
  }
549
689
  };
550
690
 
551
- export { AuthenticationError, NetworkError, PushFloError, PushFloServer };
691
+ export { AuthenticationError, MAX_SLUG_LENGTH, MIN_SLUG_LENGTH, NetworkError, PushFloError, PushFloServer, ValidationError, isValidChannelSlug, toChannelSlug, validateChannelSlug };
552
692
  //# sourceMappingURL=server.js.map
553
693
  //# sourceMappingURL=server.js.map
@@ -1 +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":"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","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"]}
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/errors/ValidationError.ts","../src/utils/validation.ts","../src/server/PushFloServer.ts"],"names":[],"mappings":"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","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 const json = await response.json();\n // Handle wrapped responses: {success: true, data: ...}\n if (json && typeof json === 'object' && 'success' in json && 'data' in json) {\n return json.data as T;\n }\n return json as T;\n } catch {\n throw new NetworkError('Failed to parse response', 'PARSE_ERROR', { retryable: false });\n }\n }\n}\n","import { PushFloError } from './PushFloError.js';\n\n/**\n * Error thrown when input validation fails\n */\nexport class ValidationError extends PushFloError {\n /** The field that failed validation */\n readonly field?: string;\n\n constructor(message: string, field?: string) {\n super(message, 'VALIDATION_ERROR', { retryable: false });\n this.name = 'ValidationError';\n this.field = field;\n }\n\n /**\n * Create an error for an invalid channel slug\n */\n static invalidChannelSlug(slug: string): ValidationError {\n return new ValidationError(\n `Invalid channel slug '${slug}': must be 1-64 characters, lowercase alphanumeric with hyphens, cannot start or end with a hyphen`,\n 'slug'\n );\n }\n\n /**\n * Create an error for a required field\n */\n static required(field: string): ValidationError {\n return new ValidationError(`${field} is required`, field);\n }\n\n toJSON(): Record<string, unknown> {\n return {\n ...super.toJSON(),\n field: this.field,\n };\n }\n}\n","/**\n * Channel slug validation utilities\n *\n * Channel slugs must follow these rules:\n * - 1-64 characters long\n * - Lowercase letters (a-z), numbers (0-9), and hyphens (-) only\n * - Cannot start or end with a hyphen\n * - Cannot have consecutive hyphens\n */\n\n/** Maximum length for a channel slug */\nexport const MAX_SLUG_LENGTH = 64;\n\n/** Minimum length for a channel slug */\nexport const MIN_SLUG_LENGTH = 1;\n\n/**\n * Regular expression for validating channel slugs\n * Matches: lowercase alphanumeric, can contain hyphens but not at start/end\n */\nconst SLUG_REGEX = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;\n\n/**\n * Check if a channel slug is valid\n *\n * @param slug - The channel slug to validate\n * @returns true if the slug is valid, false otherwise\n *\n * @example\n * ```typescript\n * isValidChannelSlug('my-channel') // true\n * isValidChannelSlug('channel-123') // true\n * isValidChannelSlug('a') // true\n * isValidChannelSlug('My-Channel') // false (uppercase)\n * isValidChannelSlug('-channel') // false (starts with hyphen)\n * isValidChannelSlug('channel:name') // false (contains colon)\n * isValidChannelSlug('channel_name') // false (contains underscore)\n * ```\n */\nexport function isValidChannelSlug(slug: string): boolean {\n if (!slug || typeof slug !== 'string') {\n return false;\n }\n\n if (slug.length < MIN_SLUG_LENGTH || slug.length > MAX_SLUG_LENGTH) {\n return false;\n }\n\n // Check for consecutive hyphens\n if (slug.includes('--')) {\n return false;\n }\n\n return SLUG_REGEX.test(slug);\n}\n\n/**\n * Convert a string to a valid channel slug\n *\n * @param str - The string to convert\n * @returns A valid channel slug\n *\n * @example\n * ```typescript\n * toChannelSlug('My Channel') // 'my-channel'\n * toChannelSlug('Hello World!') // 'hello-world'\n * toChannelSlug('user:123:messages') // 'user-123-messages'\n * toChannelSlug('___test___') // 'test'\n * ```\n */\nexport function toChannelSlug(str: string): string {\n return str\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '-') // Replace non-alphanumeric with hyphens\n .replace(/^-+|-+$/g, '') // Remove leading/trailing hyphens\n .replace(/-{2,}/g, '-') // Replace consecutive hyphens with single\n .slice(0, MAX_SLUG_LENGTH); // Truncate to max length\n}\n\n/**\n * Validation result with detailed error information\n */\nexport interface SlugValidationResult {\n valid: boolean;\n error?: string;\n suggestion?: string;\n}\n\n/**\n * Validate a channel slug with detailed error messages\n *\n * @param slug - The channel slug to validate\n * @returns Validation result with error details and suggestions\n */\nexport function validateChannelSlug(slug: string): SlugValidationResult {\n if (!slug || typeof slug !== 'string') {\n return {\n valid: false,\n error: 'Channel slug is required',\n };\n }\n\n if (slug.length < MIN_SLUG_LENGTH) {\n return {\n valid: false,\n error: 'Channel slug cannot be empty',\n };\n }\n\n if (slug.length > MAX_SLUG_LENGTH) {\n return {\n valid: false,\n error: `Channel slug cannot exceed ${MAX_SLUG_LENGTH} characters (got ${slug.length})`,\n suggestion: toChannelSlug(slug),\n };\n }\n\n if (slug !== slug.toLowerCase()) {\n return {\n valid: false,\n error: 'Channel slug must be lowercase',\n suggestion: toChannelSlug(slug),\n };\n }\n\n if (slug.startsWith('-')) {\n return {\n valid: false,\n error: 'Channel slug cannot start with a hyphen',\n suggestion: toChannelSlug(slug),\n };\n }\n\n if (slug.endsWith('-')) {\n return {\n valid: false,\n error: 'Channel slug cannot end with a hyphen',\n suggestion: toChannelSlug(slug),\n };\n }\n\n if (slug.includes('--')) {\n return {\n valid: false,\n error: 'Channel slug cannot contain consecutive hyphens',\n suggestion: toChannelSlug(slug),\n };\n }\n\n const invalidChars = slug.match(/[^a-z0-9-]/g);\n if (invalidChars) {\n const uniqueChars = [...new Set(invalidChars)].join(', ');\n return {\n valid: false,\n error: `Channel slug contains invalid characters: ${uniqueChars}. Only lowercase letters, numbers, and hyphens are allowed.`,\n suggestion: toChannelSlug(slug),\n };\n }\n\n return { valid: true };\n}\n","import { RestClient } from './RestClient.js';\nimport { API_PATHS, DEFAULTS } from '../utils/constants.js';\nimport { AuthenticationError } from '../errors/AuthenticationError.js';\nimport { ValidationError } from '../errors/ValidationError.js';\nimport { validateChannelSlug } from '../utils/validation.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 items: 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 this.validateSlug(slug);\n return this.client.get<Channel>(API_PATHS.CHANNEL(slug));\n }\n\n /**\n * Create a new channel\n *\n * @throws {ValidationError} If the channel slug is invalid\n */\n async createChannel(input: ChannelInput): Promise<Channel> {\n this.validateSlug(input.slug);\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 this.validateSlug(slug);\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 this.validateSlug(slug);\n await this.client.delete<void>(API_PATHS.CHANNEL(slug));\n }\n\n /**\n * Validate a channel slug and throw if invalid\n */\n private validateSlug(slug: string): void {\n const result = validateChannelSlug(slug);\n if (!result.valid) {\n const error = ValidationError.invalidChannelSlug(slug);\n if (result.suggestion) {\n error.message += `. Suggested: '${result.suggestion}'`;\n }\n throw error;\n }\n }\n\n // ============================================\n // Message Publishing\n // ============================================\n\n /**\n * Publish a message to a channel\n *\n * @throws {ValidationError} If the channel slug is invalid\n */\n async publish(\n channel: string,\n content: Record<string, unknown>,\n options: PublishOptions = {}\n ): Promise<PublishResult> {\n this.validateSlug(channel);\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 * @throws {ValidationError} If the channel slug is invalid\n */\n async getMessageHistory(\n channel: string,\n options: MessageHistoryOptions = {}\n ): Promise<{ messages: Message[]; pagination: Pagination }> {\n this.validateSlug(channel);\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.items,\n pagination: response.pagination,\n };\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushflodev/sdk",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Official TypeScript SDK for PushFlo real-time messaging service",
5
5
  "author": "PushFlo",
6
6
  "license": "MIT",