@emmanuel-nike/ark-notify-js 0.1.8 → 0.2.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server-auth.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { createHmac } from 'node:crypto'\nimport type {\n ConnectionCapabilities,\n CreateAuthorizedServerAuthResponseOptions,\n HandleServerAuthOptions,\n ServerAuthApprovedResponse,\n ServerAuthDeniedResponse,\n ServerAuthRequest,\n ServerAuthResponse,\n ServerAuthDecision,\n} from './types'\n\nconst CLIENT_ID_PATTERN = /^[a-zA-Z0-9_\\-.@\\+]{1,128}$/\nconst DEFAULT_TTL_SECONDS = 3600\nconst MAX_TTL_SECONDS = 86400\n\nconst DEFAULT_CAPABILITIES: ConnectionCapabilities = {\n subscribe: true,\n publish: true,\n presence: true,\n}\n\nexport function isValidClientId(clientId: unknown): clientId is string {\n return typeof clientId === 'string' && CLIENT_ID_PATTERN.test(clientId)\n}\n\nfunction normalizeCapabilities(\n capabilities?: Partial<ConnectionCapabilities> | null\n): ConnectionCapabilities {\n if (!capabilities || typeof capabilities !== 'object') {\n return { ...DEFAULT_CAPABILITIES }\n }\n\n return {\n subscribe: capabilities.subscribe !== false,\n publish: capabilities.publish !== false,\n presence: capabilities.presence !== false,\n }\n}\n\nfunction resolveTokenTtl(ttl?: number | null): number {\n if (ttl === undefined || ttl === null) {\n return DEFAULT_TTL_SECONDS\n }\n\n if (!Number.isInteger(ttl) || ttl <= 0) {\n throw new Error('ttl must be a positive integer (seconds)')\n }\n\n return Math.min(ttl, MAX_TTL_SECONDS)\n}\n\nfunction encodePayload(payload: Record<string, unknown>): string {\n return Buffer.from(JSON.stringify(payload)).toString('base64url')\n}\n\nfunction computeConnectionTokenSignature(\n secret: string,\n appKey: string,\n payloadEncoded: string\n): string {\n return createHmac('sha256', secret).update(`${appKey}.${payloadEncoded}`).digest('hex')\n}\n\n/** Build a signed connection token in Ark Notify format. */\nexport function buildConnectionToken(\n appKey: string,\n secret: string,\n options: {\n clientId: string\n exp: number\n capabilities?: Partial<ConnectionCapabilities>\n }\n): string {\n if (!isValidClientId(options.clientId)) {\n throw new Error('clientId must be 1-128 alphanumeric characters')\n }\n\n const payloadEncoded = encodePayload({\n client_id: options.clientId,\n exp: options.exp,\n capabilities: normalizeCapabilities(options.capabilities),\n })\n\n const signature = computeConnectionTokenSignature(secret, appKey, payloadEncoded)\n return `${appKey}.${payloadEncoded}.${signature}`\n}\n\n/**\n * Parse the JSON body Ark Notify sends to your `serverAuthUrl`.\n * Returns null when the payload is invalid.\n */\nexport function parseServerAuthRequest(body: unknown): ServerAuthRequest | null {\n if (!body || typeof body !== 'object') {\n return null\n }\n\n const record = body as Record<string, unknown>\n const client_id = record.client_id ?? record.clientId\n\n if (!isValidClientId(client_id)) {\n return null\n }\n\n const user_data = record.user_data ?? record.userData\n const capabilities = record.capabilities\n const ttl = record.ttl\n\n return {\n client_id,\n user_data:\n user_data === undefined\n ? null\n : user_data && typeof user_data === 'object'\n ? (user_data as Record<string, unknown>)\n : null,\n capabilities:\n capabilities && typeof capabilities === 'object'\n ? (capabilities as Partial<ConnectionCapabilities>)\n : null,\n ttl: typeof ttl === 'number' ? ttl : null,\n }\n}\n\n/**\n * Build an approved response for your `serverAuthUrl` webhook.\n *\n * - Without `credentials`: returns `{ allowed: true, client_id, ... }` (option A).\n * - With `credentials`: returns `{ token }` with a pre-signed token (option B).\n */\nexport function createAuthorizedServerAuthResponse(\n options: CreateAuthorizedServerAuthResponseOptions\n): ServerAuthApprovedResponse {\n if (!isValidClientId(options.clientId)) {\n throw new Error('clientId must be 1-128 alphanumeric characters')\n }\n\n const capabilities = normalizeCapabilities(options.capabilities)\n const ttl = resolveTokenTtl(options.ttl)\n\n if (options.credentials) {\n const exp = Math.floor(Date.now() / 1000) + ttl\n return {\n token: buildConnectionToken(options.credentials.appKey, options.credentials.secret, {\n clientId: options.clientId,\n exp,\n capabilities,\n }),\n }\n }\n\n return {\n allowed: true,\n client_id: options.clientId,\n capabilities,\n ttl,\n }\n}\n\n/** Build a denied response for your `serverAuthUrl` webhook. */\nexport function createDeniedServerAuthResponse(reason?: string): ServerAuthDeniedResponse {\n return reason ? { allowed: false, reason } : { allowed: false }\n}\n\n/**\n * Authenticate an incoming `serverAuthUrl` request and return the webhook response\n * body Ark Notify expects.\n */\nexport async function handleServerAuth(\n options: HandleServerAuthOptions\n): Promise<ServerAuthResponse> {\n const decision: ServerAuthDecision = await options.isAuthorized(options.request)\n\n if (decision === false) {\n return createDeniedServerAuthResponse()\n }\n\n const clientId = decision.clientId ?? options.request.client_id\n if (!isValidClientId(clientId)) {\n return createDeniedServerAuthResponse('invalid_client_id')\n }\n\n try {\n return createAuthorizedServerAuthResponse({\n clientId,\n capabilities: decision.capabilities ?? options.request.capabilities ?? undefined,\n ttl: decision.ttl ?? options.request.ttl ?? undefined,\n credentials: options.credentials,\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : 'invalid_request'\n return createDeniedServerAuthResponse(message)\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/server-auth.ts","../../src/config.ts","../../src/utils.ts","../../src/server-stream.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { createHmac } from 'node:crypto'\nimport type {\n ConnectionCapabilities,\n CreateAuthorizedServerAuthResponseOptions,\n HandleServerAuthOptions,\n ServerAuthApprovedResponse,\n ServerAuthDeniedResponse,\n ServerAuthRequest,\n ServerAuthResponse,\n ServerAuthDecision,\n} from './types'\n\nconst CLIENT_ID_PATTERN = /^[a-zA-Z0-9_\\-.@\\+]{1,128}$/\nconst DEFAULT_TTL_SECONDS = 3600\nconst MAX_TTL_SECONDS = 86400\n\nconst DEFAULT_CAPABILITIES: ConnectionCapabilities = {\n subscribe: true,\n publish: true,\n presence: true,\n}\n\nexport function isValidClientId(clientId: unknown): clientId is string {\n return typeof clientId === 'string' && CLIENT_ID_PATTERN.test(clientId)\n}\n\nfunction normalizeCapabilities(\n capabilities?: Partial<ConnectionCapabilities> | null\n): ConnectionCapabilities {\n if (!capabilities || typeof capabilities !== 'object') {\n return { ...DEFAULT_CAPABILITIES }\n }\n\n return {\n subscribe: capabilities.subscribe !== false,\n publish: capabilities.publish !== false,\n presence: capabilities.presence !== false,\n }\n}\n\nfunction resolveTokenTtl(ttl?: number | null): number {\n if (ttl === undefined || ttl === null) {\n return DEFAULT_TTL_SECONDS\n }\n\n if (!Number.isInteger(ttl) || ttl <= 0) {\n throw new Error('ttl must be a positive integer (seconds)')\n }\n\n return Math.min(ttl, MAX_TTL_SECONDS)\n}\n\nfunction encodePayload(payload: Record<string, unknown>): string {\n return Buffer.from(JSON.stringify(payload)).toString('base64url')\n}\n\nfunction computeConnectionTokenSignature(\n secret: string,\n appKey: string,\n payloadEncoded: string\n): string {\n return createHmac('sha256', secret).update(`${appKey}.${payloadEncoded}`).digest('hex')\n}\n\n/** Build a signed connection token in Ark Notify format. */\nexport function buildConnectionToken(\n appKey: string,\n secret: string,\n options: {\n clientId: string\n exp: number\n capabilities?: Partial<ConnectionCapabilities>\n }\n): string {\n if (!isValidClientId(options.clientId)) {\n throw new Error('clientId must be 1-128 alphanumeric characters')\n }\n\n const payloadEncoded = encodePayload({\n client_id: options.clientId,\n exp: options.exp,\n capabilities: normalizeCapabilities(options.capabilities),\n })\n\n const signature = computeConnectionTokenSignature(secret, appKey, payloadEncoded)\n return `${appKey}.${payloadEncoded}.${signature}`\n}\n\n/**\n * Parse the JSON body Ark Notify sends to your `serverAuthUrl`.\n * Returns null when the payload is invalid.\n */\nexport function parseServerAuthRequest(body: unknown): ServerAuthRequest | null {\n if (!body || typeof body !== 'object') {\n return null\n }\n\n const record = body as Record<string, unknown>\n const client_id = record.client_id ?? record.clientId\n\n if (!isValidClientId(client_id)) {\n return null\n }\n\n const user_data = record.user_data ?? record.userData\n const capabilities = record.capabilities\n const ttl = record.ttl\n\n return {\n client_id,\n user_data:\n user_data === undefined\n ? null\n : user_data && typeof user_data === 'object'\n ? (user_data as Record<string, unknown>)\n : null,\n capabilities:\n capabilities && typeof capabilities === 'object'\n ? (capabilities as Partial<ConnectionCapabilities>)\n : null,\n ttl: typeof ttl === 'number' ? ttl : null,\n }\n}\n\n/**\n * Build an approved response for your `serverAuthUrl` webhook.\n *\n * - Without `credentials`: returns `{ allowed: true, client_id, ... }` (option A).\n * - With `credentials`: returns `{ token }` with a pre-signed token (option B).\n */\nexport function createAuthorizedServerAuthResponse(\n options: CreateAuthorizedServerAuthResponseOptions\n): ServerAuthApprovedResponse {\n if (!isValidClientId(options.clientId)) {\n throw new Error('clientId must be 1-128 alphanumeric characters')\n }\n\n const capabilities = normalizeCapabilities(options.capabilities)\n const ttl = resolveTokenTtl(options.ttl)\n\n if (options.credentials) {\n const exp = Math.floor(Date.now() / 1000) + ttl\n return {\n token: buildConnectionToken(options.credentials.appKey, options.credentials.secret, {\n clientId: options.clientId,\n exp,\n capabilities,\n }),\n }\n }\n\n return {\n allowed: true,\n client_id: options.clientId,\n capabilities,\n ttl,\n }\n}\n\n/** Build a denied response for your `serverAuthUrl` webhook. */\nexport function createDeniedServerAuthResponse(reason?: string): ServerAuthDeniedResponse {\n return reason ? { allowed: false, reason } : { allowed: false }\n}\n\n/**\n * Authenticate an incoming `serverAuthUrl` request and return the webhook response\n * body Ark Notify expects.\n */\nexport async function handleServerAuth(\n options: HandleServerAuthOptions\n): Promise<ServerAuthResponse> {\n const decision: ServerAuthDecision = await options.isAuthorized(options.request)\n\n if (decision === false) {\n return createDeniedServerAuthResponse()\n }\n\n const clientId = decision.clientId ?? options.request.client_id\n if (!isValidClientId(clientId)) {\n return createDeniedServerAuthResponse('invalid_client_id')\n }\n\n try {\n return createAuthorizedServerAuthResponse({\n clientId,\n capabilities: decision.capabilities ?? options.request.capabilities ?? undefined,\n ttl: decision.ttl ?? options.request.ttl ?? undefined,\n credentials: options.credentials,\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : 'invalid_request'\n return createDeniedServerAuthResponse(message)\n }\n}\n","export const DEFAULT_BASE_URL = 'https://ark-notify-933303906015.europe-north1.run.app'\n\nlet configuredBaseUrl: string | undefined\n\nexport interface ArkNotifyGlobalConfig {\n baseUrl?: string\n}\n\n/** Set the default base URL once at application startup. */\nexport function configureArkNotify(config: ArkNotifyGlobalConfig): void {\n if (config.baseUrl !== undefined) {\n configuredBaseUrl = config.baseUrl\n }\n}\n\nexport function resolveBaseUrl(baseUrl?: string): string {\n return (baseUrl ?? configuredBaseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '')\n}\n","import type { ApiError } from './types'\n\nexport class ArkNotifyError extends Error {\n readonly status: number\n readonly code: string\n readonly retryAfterSec?: number\n readonly reason?: string\n\n constructor(status: number, body: ApiError) {\n super(body.message)\n this.name = 'ArkNotifyError'\n this.status = status\n this.code = body.error\n this.retryAfterSec = body.retryAfterSec\n this.reason = body.reason\n }\n}\n\nexport function toWebSocketUrl(baseUrl: string, path: string): string {\n const normalizedBase = baseUrl.replace(/\\/$/, '')\n const normalizedPath = path.startsWith('/') ? path : `/${path}`\n const httpUrl = `${normalizedBase}${normalizedPath}`\n // Avoid URL.protocol — not supported in React Native and some runtimes.\n return httpUrl.replace(/^https:/i, 'wss:').replace(/^http:/i, 'ws:')\n}\n\n/** Append query params without URLSearchParams (unsupported in React Native). */\nexport function appendQueryParams(\n url: string,\n params: Record<string, string | undefined | null | false>\n): string {\n const query = Object.entries(params)\n .filter((entry): entry is [string, string] => {\n const value = entry[1]\n return value !== undefined && value !== null && value !== false && value !== ''\n })\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)\n .join('&')\n\n if (!query) return url\n\n const separator = url.includes('?') ? '&' : '?'\n return `${url}${separator}${query}`\n}\n\nexport function resolveValue<T>(value: T | (() => T)): T {\n return typeof value === 'function' ? (value as () => T)() : value\n}\n\nexport function isPrivateChannel(channel: string): boolean {\n return channel.startsWith('private-')\n}\n","import { resolveBaseUrl } from './config'\nimport type {\n AppCredentials,\n ArkNotifyServerStreamConfig,\n EventMessage,\n PresenceMessage,\n ServerErrorMessage,\n ServerStreamConnectedMessage,\n} from './types'\nimport { appendQueryParams, ArkNotifyError } from './utils'\n\ntype ServerStreamEventMap = {\n connected: (message: ServerStreamConnectedMessage) => void\n event: (message: EventMessage) => void\n presence: (message: PresenceMessage) => void\n message: (message: ServerStreamMessage) => void\n error: (error: Error) => void\n close: () => void\n}\n\ntype ServerStreamEventName = keyof ServerStreamEventMap\n\nfunction buildAppCredentialsHeaders(credentials: AppCredentials): Record<string, string> {\n return {\n 'X-App-Key': credentials.appKey,\n 'X-App-Secret': credentials.secret,\n }\n}\n\ntype ServerStreamMessage = ServerStreamConnectedMessage | EventMessage | PresenceMessage | ServerErrorMessage\n\nfunction dispatchSseEvent(\n data: string,\n handlers: {\n onConnected: (message: ServerStreamConnectedMessage) => void\n onEvent: (message: EventMessage) => void\n onPresence: (message: PresenceMessage) => void\n onMessage: (message: ServerStreamMessage) => void\n }\n): void {\n if (!data) return\n\n let parsed: ServerStreamMessage\n try {\n parsed = JSON.parse(data) as ServerStreamMessage\n } catch {\n return\n }\n\n if (parsed.type === 'connected') {\n handlers.onConnected(parsed)\n handlers.onMessage(parsed)\n return\n }\n\n if (parsed.type === 'event') {\n handlers.onEvent(parsed)\n handlers.onMessage(parsed)\n return\n }\n\n if (parsed.type === 'presence') {\n handlers.onPresence(parsed)\n handlers.onMessage(parsed)\n return\n }\n\n handlers.onMessage(parsed)\n}\n\nasync function readSseStream(\n body: ReadableStream<Uint8Array>,\n onSseData: (data: string) => void,\n signal: AbortSignal\n): Promise<void> {\n const reader = body.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n let currentEvent: string | null = null\n let dataLines: string[] = []\n\n const flushEvent = () => {\n if (dataLines.length === 0 && currentEvent === null) return\n onSseData(dataLines.join('\\n'))\n currentEvent = null\n dataLines = []\n }\n\n try {\n while (!signal.aborted) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n\n for (const rawLine of lines) {\n const line = rawLine.endsWith('\\r') ? rawLine.slice(0, -1) : rawLine\n\n if (line === '') {\n flushEvent()\n continue\n }\n\n if (line.startsWith(':')) continue\n\n if (line.startsWith('event:')) {\n currentEvent = line.slice(6).trimStart()\n continue\n }\n\n if (line.startsWith('data:')) {\n dataLines.push(line.slice(5).trimStart())\n }\n }\n }\n\n if (buffer.length > 0) {\n const line = buffer.endsWith('\\r') ? buffer.slice(0, -1) : buffer\n if (line.startsWith('data:')) {\n dataLines.push(line.slice(5).trimStart())\n }\n }\n\n flushEvent()\n } finally {\n reader.releaseLock()\n }\n}\n\nexport class ArkNotifyServerStream {\n private readonly config: ArkNotifyServerStreamConfig & { baseUrl: string }\n private readonly fetchFn: typeof fetch\n private readonly listeners = new Map<\n ServerStreamEventName,\n Set<ServerStreamEventMap[ServerStreamEventName]>\n >()\n private abortController: AbortController | null = null\n private connectionId: string | null = null\n private connecting = false\n\n constructor(config: ArkNotifyServerStreamConfig) {\n this.config = { ...config, baseUrl: resolveBaseUrl(config.baseUrl) }\n this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis)\n }\n\n getConnectionId(): string | null {\n return this.connectionId\n }\n\n on<E extends ServerStreamEventName>(event: E, handler: ServerStreamEventMap[E]): () => void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set())\n }\n this.listeners.get(event)!.add(handler)\n return () => this.off(event, handler)\n }\n\n off<E extends ServerStreamEventName>(event: E, handler: ServerStreamEventMap[E]): void {\n this.listeners.get(event)?.delete(handler)\n }\n\n private emit<E extends ServerStreamEventName>(\n event: E,\n ...args: Parameters<ServerStreamEventMap[E]>\n ): void {\n for (const handler of this.listeners.get(event) ?? []) {\n ;(handler as (...a: Parameters<ServerStreamEventMap[E]>) => void)(...args)\n }\n }\n\n async connect(): Promise<void> {\n if (this.abortController || this.connecting) return\n\n this.connecting = true\n const abortController = new AbortController()\n this.abortController = abortController\n\n const base = this.config.baseUrl.replace(/\\/$/, '')\n const streamUrl = appendQueryParams(\n `${base}/api/v1/apps/${this.config.appKey}/server-stream`,\n {\n channels: this.config.channels.join(','),\n history: this.config.history ? 'true' : undefined,\n }\n )\n\n try {\n const response = await this.fetchFn(streamUrl, {\n method: 'GET',\n headers: {\n Accept: 'text/event-stream',\n ...buildAppCredentialsHeaders(this.config.credentials),\n },\n signal: abortController.signal,\n })\n\n if (!response.ok) {\n let body: { error: string; message: string; reason?: string; retryAfterSec?: number }\n try {\n body = await response.json()\n } catch {\n body = { error: 'request_failed', message: response.statusText }\n }\n throw new ArkNotifyError(response.status, body)\n }\n\n if (!response.body) {\n throw new Error('Server stream response has no body')\n }\n\n void this.consumeStream(response.body, abortController)\n } catch (err) {\n if (abortController.signal.aborted) return\n this.abortController = null\n this.emit('error', err instanceof Error ? err : new Error(String(err)))\n this.emit('close')\n } finally {\n this.connecting = false\n }\n }\n\n private async consumeStream(body: ReadableStream<Uint8Array>, abortController: AbortController): Promise<void> {\n try {\n await readSseStream(\n body,\n (data) => {\n dispatchSseEvent(data, {\n onConnected: (message) => {\n this.connectionId = message.connection_id\n this.emit('connected', message)\n },\n onEvent: (message) => this.emit('event', message),\n onPresence: (message) => this.emit('presence', message),\n onMessage: (message) => this.emit('message', message),\n })\n },\n abortController.signal\n )\n } catch (err) {\n if (abortController.signal.aborted) return\n this.emit('error', err instanceof Error ? err : new Error(String(err)))\n } finally {\n if (this.abortController === abortController) {\n this.abortController = null\n this.connectionId = null\n this.emit('close')\n }\n }\n }\n\n disconnect(): void {\n if (!this.abortController) return\n this.abortController.abort()\n }\n\n bind(\n channel: string,\n event: string,\n handler: (data: unknown, message: EventMessage) => void\n ): () => void {\n const listener = (message: EventMessage) => {\n if (message.channel === channel && message.event === event) {\n handler(message.data, message)\n }\n }\n return this.on('event', listener)\n }\n\n bindAll(channel: string, handler: (data: unknown, message: EventMessage) => void): () => void {\n const listener = (message: EventMessage) => {\n if (message.channel === channel) {\n handler(message.data, message)\n }\n }\n return this.on('event', listener)\n }\n}\n"]}
@@ -0,0 +1,32 @@
1
+ import { g as ArkNotifyServerStreamConfig, Q as ServerStreamConnectedMessage, E as EventMessage, w as PresenceMessage, N as ServerErrorMessage } from './types-Aj_RUX12.cjs';
2
+
3
+ type ServerStreamEventMap = {
4
+ connected: (message: ServerStreamConnectedMessage) => void;
5
+ event: (message: EventMessage) => void;
6
+ presence: (message: PresenceMessage) => void;
7
+ message: (message: ServerStreamMessage) => void;
8
+ error: (error: Error) => void;
9
+ close: () => void;
10
+ };
11
+ type ServerStreamEventName = keyof ServerStreamEventMap;
12
+ type ServerStreamMessage = ServerStreamConnectedMessage | EventMessage | PresenceMessage | ServerErrorMessage;
13
+ declare class ArkNotifyServerStream {
14
+ private readonly config;
15
+ private readonly fetchFn;
16
+ private readonly listeners;
17
+ private abortController;
18
+ private connectionId;
19
+ private connecting;
20
+ constructor(config: ArkNotifyServerStreamConfig);
21
+ getConnectionId(): string | null;
22
+ on<E extends ServerStreamEventName>(event: E, handler: ServerStreamEventMap[E]): () => void;
23
+ off<E extends ServerStreamEventName>(event: E, handler: ServerStreamEventMap[E]): void;
24
+ private emit;
25
+ connect(): Promise<void>;
26
+ private consumeStream;
27
+ disconnect(): void;
28
+ bind(channel: string, event: string, handler: (data: unknown, message: EventMessage) => void): () => void;
29
+ bindAll(channel: string, handler: (data: unknown, message: EventMessage) => void): () => void;
30
+ }
31
+
32
+ export { ArkNotifyServerStream as A };
@@ -0,0 +1,32 @@
1
+ import { g as ArkNotifyServerStreamConfig, Q as ServerStreamConnectedMessage, E as EventMessage, w as PresenceMessage, N as ServerErrorMessage } from './types-Aj_RUX12.js';
2
+
3
+ type ServerStreamEventMap = {
4
+ connected: (message: ServerStreamConnectedMessage) => void;
5
+ event: (message: EventMessage) => void;
6
+ presence: (message: PresenceMessage) => void;
7
+ message: (message: ServerStreamMessage) => void;
8
+ error: (error: Error) => void;
9
+ close: () => void;
10
+ };
11
+ type ServerStreamEventName = keyof ServerStreamEventMap;
12
+ type ServerStreamMessage = ServerStreamConnectedMessage | EventMessage | PresenceMessage | ServerErrorMessage;
13
+ declare class ArkNotifyServerStream {
14
+ private readonly config;
15
+ private readonly fetchFn;
16
+ private readonly listeners;
17
+ private abortController;
18
+ private connectionId;
19
+ private connecting;
20
+ constructor(config: ArkNotifyServerStreamConfig);
21
+ getConnectionId(): string | null;
22
+ on<E extends ServerStreamEventName>(event: E, handler: ServerStreamEventMap[E]): () => void;
23
+ off<E extends ServerStreamEventName>(event: E, handler: ServerStreamEventMap[E]): void;
24
+ private emit;
25
+ connect(): Promise<void>;
26
+ private consumeStream;
27
+ disconnect(): void;
28
+ bind(channel: string, event: string, handler: (data: unknown, message: EventMessage) => void): () => void;
29
+ bindAll(channel: string, handler: (data: unknown, message: EventMessage) => void): () => void;
30
+ }
31
+
32
+ export { ArkNotifyServerStream as A };
@@ -138,6 +138,13 @@ interface ConnectedMessage {
138
138
  authenticated: boolean;
139
139
  channels?: string[];
140
140
  }
141
+ interface ServerStreamConnectedMessage {
142
+ type: 'connected';
143
+ connection_id: string;
144
+ app_key: string;
145
+ channels: string[];
146
+ transport: 'server-stream';
147
+ }
141
148
  interface EventMessage {
142
149
  type: 'event';
143
150
  channel: string;
@@ -243,6 +250,14 @@ interface ArkNotifySSEConfig {
243
250
  onPrivateChannelAuth?: PrivateChannelAuthHandler;
244
251
  EventSource?: typeof EventSource;
245
252
  }
253
+ interface ArkNotifyServerStreamConfig {
254
+ baseUrl?: string;
255
+ appKey: string;
256
+ credentials: AppCredentials;
257
+ channels: string[];
258
+ history?: boolean;
259
+ fetch?: typeof fetch;
260
+ }
246
261
  interface ChannelEventHandler<T = unknown> {
247
262
  (data: T, message: EventMessage): void;
248
263
  }
@@ -254,4 +269,4 @@ interface HealthResponse {
254
269
  uptime: number;
255
270
  }
256
271
 
257
- export type { AppCredentials as A, PublishedMessage as B, ConnectionTokenInput as C, ServerAuthApprovedResponse as D, EventMessage as E, ServerAuthDecision as F, ServerAuthDeniedResponse as G, HandleServerAuthOptions as H, ServerAuthRequest as I, ServerAuthResponse as J, ServerAuthTokenResponse as K, LoginInput as L, ServerErrorMessage as M, ServerMessage as N, SubscribeOptions as O, PingMessage as P, SubscribedMessage as Q, UpdateApplicationInput as R, ServerAuthAllowedResponse as S, User as T, UnsubscribedMessage as U, UserRole as V, ConnectionTokenResponse as a, ApiError as b, Application as c, ArkNotifyClientConfig as d, ArkNotifyConnectionConfig as e, ArkNotifySSEConfig as f, AuthResponse as g, ChannelAuthInput as h, ChannelAuthResponse as i, ChannelEventHandler as j, ChannelHandlers as k, ConnectedMessage as l, ConnectionCapabilities as m, ConnectionState as n, CreateApplicationInput as o, CreateAuthorizedServerAuthResponseOptions as p, HealthResponse as q, PongMessage as r, PresenceAction as s, PresenceLeftMessage as t, PresenceMemberInfo as u, PresenceMessage as v, PresenceUpdatedMessage as w, PrivateChannelAuthHandler as x, PublishEventInput as y, PublishEventResponse as z };
272
+ export type { AppCredentials as A, PublishEventResponse as B, ConnectionTokenInput as C, PublishedMessage as D, EventMessage as E, ServerAuthApprovedResponse as F, ServerAuthDecision as G, HandleServerAuthOptions as H, ServerAuthDeniedResponse as I, ServerAuthRequest as J, ServerAuthResponse as K, LoginInput as L, ServerAuthTokenResponse as M, ServerErrorMessage as N, ServerMessage as O, PingMessage as P, ServerStreamConnectedMessage as Q, SubscribeOptions as R, ServerAuthAllowedResponse as S, SubscribedMessage as T, UnsubscribedMessage as U, UpdateApplicationInput as V, User as W, UserRole as X, ConnectionTokenResponse as a, ApiError as b, Application as c, ArkNotifyClientConfig as d, ArkNotifyConnectionConfig as e, ArkNotifySSEConfig as f, ArkNotifyServerStreamConfig as g, AuthResponse as h, ChannelAuthInput as i, ChannelAuthResponse as j, ChannelEventHandler as k, ChannelHandlers as l, ConnectedMessage as m, ConnectionCapabilities as n, ConnectionState as o, CreateApplicationInput as p, CreateAuthorizedServerAuthResponseOptions as q, HealthResponse as r, PongMessage as s, PresenceAction as t, PresenceLeftMessage as u, PresenceMemberInfo as v, PresenceMessage as w, PresenceUpdatedMessage as x, PrivateChannelAuthHandler as y, PublishEventInput as z };
@@ -138,6 +138,13 @@ interface ConnectedMessage {
138
138
  authenticated: boolean;
139
139
  channels?: string[];
140
140
  }
141
+ interface ServerStreamConnectedMessage {
142
+ type: 'connected';
143
+ connection_id: string;
144
+ app_key: string;
145
+ channels: string[];
146
+ transport: 'server-stream';
147
+ }
141
148
  interface EventMessage {
142
149
  type: 'event';
143
150
  channel: string;
@@ -243,6 +250,14 @@ interface ArkNotifySSEConfig {
243
250
  onPrivateChannelAuth?: PrivateChannelAuthHandler;
244
251
  EventSource?: typeof EventSource;
245
252
  }
253
+ interface ArkNotifyServerStreamConfig {
254
+ baseUrl?: string;
255
+ appKey: string;
256
+ credentials: AppCredentials;
257
+ channels: string[];
258
+ history?: boolean;
259
+ fetch?: typeof fetch;
260
+ }
246
261
  interface ChannelEventHandler<T = unknown> {
247
262
  (data: T, message: EventMessage): void;
248
263
  }
@@ -254,4 +269,4 @@ interface HealthResponse {
254
269
  uptime: number;
255
270
  }
256
271
 
257
- export type { AppCredentials as A, PublishedMessage as B, ConnectionTokenInput as C, ServerAuthApprovedResponse as D, EventMessage as E, ServerAuthDecision as F, ServerAuthDeniedResponse as G, HandleServerAuthOptions as H, ServerAuthRequest as I, ServerAuthResponse as J, ServerAuthTokenResponse as K, LoginInput as L, ServerErrorMessage as M, ServerMessage as N, SubscribeOptions as O, PingMessage as P, SubscribedMessage as Q, UpdateApplicationInput as R, ServerAuthAllowedResponse as S, User as T, UnsubscribedMessage as U, UserRole as V, ConnectionTokenResponse as a, ApiError as b, Application as c, ArkNotifyClientConfig as d, ArkNotifyConnectionConfig as e, ArkNotifySSEConfig as f, AuthResponse as g, ChannelAuthInput as h, ChannelAuthResponse as i, ChannelEventHandler as j, ChannelHandlers as k, ConnectedMessage as l, ConnectionCapabilities as m, ConnectionState as n, CreateApplicationInput as o, CreateAuthorizedServerAuthResponseOptions as p, HealthResponse as q, PongMessage as r, PresenceAction as s, PresenceLeftMessage as t, PresenceMemberInfo as u, PresenceMessage as v, PresenceUpdatedMessage as w, PrivateChannelAuthHandler as x, PublishEventInput as y, PublishEventResponse as z };
272
+ export type { AppCredentials as A, PublishEventResponse as B, ConnectionTokenInput as C, PublishedMessage as D, EventMessage as E, ServerAuthApprovedResponse as F, ServerAuthDecision as G, HandleServerAuthOptions as H, ServerAuthDeniedResponse as I, ServerAuthRequest as J, ServerAuthResponse as K, LoginInput as L, ServerAuthTokenResponse as M, ServerErrorMessage as N, ServerMessage as O, PingMessage as P, ServerStreamConnectedMessage as Q, SubscribeOptions as R, ServerAuthAllowedResponse as S, SubscribedMessage as T, UnsubscribedMessage as U, UpdateApplicationInput as V, User as W, UserRole as X, ConnectionTokenResponse as a, ApiError as b, Application as c, ArkNotifyClientConfig as d, ArkNotifyConnectionConfig as e, ArkNotifySSEConfig as f, ArkNotifyServerStreamConfig as g, AuthResponse as h, ChannelAuthInput as i, ChannelAuthResponse as j, ChannelEventHandler as k, ChannelHandlers as l, ConnectedMessage as m, ConnectionCapabilities as n, ConnectionState as o, CreateApplicationInput as p, CreateAuthorizedServerAuthResponseOptions as q, HealthResponse as r, PongMessage as s, PresenceAction as t, PresenceLeftMessage as u, PresenceMemberInfo as v, PresenceMessage as w, PresenceUpdatedMessage as x, PrivateChannelAuthHandler as y, PublishEventInput as z };
@@ -1,4 +1,4 @@
1
- import { d as ArkNotifyClientConfig, q as HealthResponse, L as LoginInput, g as AuthResponse, T as User, c as Application, o as CreateApplicationInput, R as UpdateApplicationInput, A as AppCredentials, y as PublishEventInput, z as PublishEventResponse, h as ChannelAuthInput, i as ChannelAuthResponse, C as ConnectionTokenInput, a as ConnectionTokenResponse, e as ArkNotifyConnectionConfig, n as ConnectionState, l as ConnectedMessage, E as EventMessage, v as PresenceMessage, N as ServerMessage, O as SubscribeOptions, j as ChannelEventHandler, f as ArkNotifySSEConfig, b as ApiError } from './types-C3Ycb__r.js';
1
+ import { d as ArkNotifyClientConfig, r as HealthResponse, L as LoginInput, h as AuthResponse, W as User, c as Application, p as CreateApplicationInput, V as UpdateApplicationInput, A as AppCredentials, z as PublishEventInput, B as PublishEventResponse, i as ChannelAuthInput, j as ChannelAuthResponse, C as ConnectionTokenInput, a as ConnectionTokenResponse, e as ArkNotifyConnectionConfig, o as ConnectionState, m as ConnectedMessage, E as EventMessage, w as PresenceMessage, O as ServerMessage, R as SubscribeOptions, k as ChannelEventHandler, f as ArkNotifySSEConfig, b as ApiError } from './types-Aj_RUX12.js';
2
2
 
3
3
  declare const DEFAULT_BASE_URL = "https://ark-notify-933303906015.europe-north1.run.app";
4
4
  interface ArkNotifyGlobalConfig {
@@ -1,4 +1,4 @@
1
- import { d as ArkNotifyClientConfig, q as HealthResponse, L as LoginInput, g as AuthResponse, T as User, c as Application, o as CreateApplicationInput, R as UpdateApplicationInput, A as AppCredentials, y as PublishEventInput, z as PublishEventResponse, h as ChannelAuthInput, i as ChannelAuthResponse, C as ConnectionTokenInput, a as ConnectionTokenResponse, e as ArkNotifyConnectionConfig, n as ConnectionState, l as ConnectedMessage, E as EventMessage, v as PresenceMessage, N as ServerMessage, O as SubscribeOptions, j as ChannelEventHandler, f as ArkNotifySSEConfig, b as ApiError } from './types-C3Ycb__r.cjs';
1
+ import { d as ArkNotifyClientConfig, r as HealthResponse, L as LoginInput, h as AuthResponse, W as User, c as Application, p as CreateApplicationInput, V as UpdateApplicationInput, A as AppCredentials, z as PublishEventInput, B as PublishEventResponse, i as ChannelAuthInput, j as ChannelAuthResponse, C as ConnectionTokenInput, a as ConnectionTokenResponse, e as ArkNotifyConnectionConfig, o as ConnectionState, m as ConnectedMessage, E as EventMessage, w as PresenceMessage, O as ServerMessage, R as SubscribeOptions, k as ChannelEventHandler, f as ArkNotifySSEConfig, b as ApiError } from './types-Aj_RUX12.cjs';
2
2
 
3
3
  declare const DEFAULT_BASE_URL = "https://ark-notify-933303906015.europe-north1.run.app";
4
4
  interface ArkNotifyGlobalConfig {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emmanuel-nike/ark-notify-js",
3
- "version": "0.1.8",
3
+ "version": "0.2.1",
4
4
  "description": "JavaScript SDK for Ark Notify — real-time pub/sub, presence, and platform management",
5
5
  "license": "MIT",
6
6
  "type": "module",