@powerdata/pd4 0.1.0 → 0.1.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.
package/dist/index.cjs CHANGED
@@ -111,6 +111,8 @@ var PD4Client = class {
111
111
  constructor(url, db, options) {
112
112
  this.eventSource = null;
113
113
  this.handlers = /* @__PURE__ */ new Map();
114
+ this.wildcardHandlers = { insert: [], update: [], delete: [] };
115
+ this.anyHandlers = [];
114
116
  this.statusHandlers = [];
115
117
  this._status = "connecting";
116
118
  this.url = url.replace(/\/$/, "");
@@ -305,6 +307,14 @@ var PD4Client = class {
305
307
  }
306
308
  on(table, event, handler) {
307
309
  if (!this.eventSource) this.connect();
310
+ if (table === "*") {
311
+ const list2 = this.wildcardHandlers[event];
312
+ list2.push(handler);
313
+ return () => {
314
+ const idx = list2.indexOf(handler);
315
+ if (idx >= 0) list2.splice(idx, 1);
316
+ };
317
+ }
308
318
  let tableHandlers = this.handlers.get(table);
309
319
  if (!tableHandlers) {
310
320
  tableHandlers = { insert: [], update: [], delete: [] };
@@ -317,6 +327,19 @@ var PD4Client = class {
317
327
  if (idx >= 0) list.splice(idx, 1);
318
328
  };
319
329
  }
330
+ /**
331
+ * Subscribe to all SSE events on all tables.
332
+ * Handler receives (table, event, keys, newValues, oldValues).
333
+ * Auto-connects on first call. Returns an unsubscribe function.
334
+ */
335
+ onAny(handler) {
336
+ if (!this.eventSource) this.connect();
337
+ this.anyHandlers.push(handler);
338
+ return () => {
339
+ const idx = this.anyHandlers.indexOf(handler);
340
+ if (idx >= 0) this.anyHandlers.splice(idx, 1);
341
+ };
342
+ }
320
343
  /** Subscribe to connection status changes. Returns an unsubscribe function. */
321
344
  onStatus(handler) {
322
345
  this.statusHandlers.push(handler);
@@ -334,13 +357,24 @@ var PD4Client = class {
334
357
  }
335
358
  _dispatch(table, event, keys, newValues, oldValues) {
336
359
  const tableHandlers = this.handlers.get(table);
337
- if (!tableHandlers) return;
360
+ if (tableHandlers) {
361
+ if (event === "insert") {
362
+ for (const h of tableHandlers.insert) h(keys, newValues);
363
+ } else if (event === "update") {
364
+ for (const h of tableHandlers.update) h(keys, newValues, oldValues);
365
+ } else {
366
+ for (const h of tableHandlers.delete) h(keys);
367
+ }
368
+ }
338
369
  if (event === "insert") {
339
- for (const h of tableHandlers.insert) h(keys, newValues);
370
+ for (const h of this.wildcardHandlers.insert) h(table, keys, newValues);
340
371
  } else if (event === "update") {
341
- for (const h of tableHandlers.update) h(keys, newValues, oldValues);
372
+ for (const h of this.wildcardHandlers.update) h(table, keys, newValues, oldValues);
342
373
  } else {
343
- for (const h of tableHandlers.delete) h(keys);
374
+ for (const h of this.wildcardHandlers.delete) h(table, keys);
375
+ }
376
+ for (const h of this.anyHandlers) {
377
+ h(table, event, keys, newValues, oldValues);
344
378
  }
345
379
  }
346
380
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/utils.ts","../src/buffer.ts","../src/client.ts"],"sourcesContent":["export { PD4Client } from './client'\nexport { PD4Buffer } from './buffer'\nexport { PD4Error } from './errors'\nexport { flattenRow, toColumnar, toColumnValues } from './utils'\nexport type {\n ColumnInfo,\n TableInfo,\n ColumnValue,\n PD4Row,\n Row,\n SseEventType,\n ConnectionStatus,\n InsertHandler,\n UpdateHandler,\n DeleteHandler,\n StatusHandler,\n FetchOptions,\n FetchResult,\n InsertResult,\n BufferInfo,\n ViewInfo,\n PD4ClientOptions,\n} from './types'\n","/** Base error class for PD4 client errors */\nexport class PD4Error extends Error {\n readonly status: number | undefined\n readonly url: string | undefined\n\n constructor(message: string, status?: number, url?: string) {\n super(message)\n this.name = 'PD4Error'\n this.status = status\n this.url = url\n }\n}\n","import type { PD4Row, Row } from './types'\n\n/** Convert PD4 row format to flat record */\nexport function flattenRow(row: PD4Row): Row {\n const flat: Row = { _pk: row.key }\n for (const cv of row.values) {\n flat[cv.column] = cv.value\n }\n return flat\n}\n\n/**\n * Convert flat row objects to PD4's columnar insert format.\n * Returns { columns, rows } suitable for the insert endpoint.\n */\nexport function toColumnar(\n flatRows: Row[],\n columnNames?: string[],\n): { columns: string[]; rows: (string | number | boolean | null)[][] } {\n if (flatRows.length === 0) return { columns: [], rows: [] }\n const cols = columnNames ?? Object.keys(flatRows[0]).filter((k) => k !== '_pk')\n const rows = flatRows.map((row) => cols.map((c) => row[c] ?? null))\n return { columns: cols, rows }\n}\n\n/**\n * Convert a flat row object to PD4's column-value pairs for update.\n * Filters out the _pk field.\n */\nexport function toColumnValues(\n flat: Row,\n): { column: string; value: string | number | boolean | null }[] {\n return Object.entries(flat)\n .filter(([k]) => k !== '_pk')\n .map(([column, value]) => ({ column, value }))\n}\n","import type { PD4Client } from './client'\nimport { flattenRow, toColumnValues } from './utils'\nimport type { Row, FetchResult, PD4Row } from './types'\n\n/**\n * A buffered view wrapping a PD4 table.\n * Changes are accumulated locally and only become visible\n * to other clients after commit().\n */\nexport class PD4Buffer {\n private client: PD4Client\n readonly handle: string\n readonly table: string\n\n constructor(client: PD4Client, handle: string, table: string) {\n this.client = client\n this.handle = handle\n this.table = table\n }\n\n /** Update a row within the buffer (not visible to others until commit). */\n async update(key: number, values: Row): Promise<void> {\n const pairs = toColumnValues(values)\n await this._request(`views/${this.handle}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values: pairs }),\n })\n }\n\n /** Fetch rows from the buffer (sees uncommitted changes). */\n async fetch(offset = 0, limit = 100): Promise<FetchResult> {\n const data = await this._request<{ rows: PD4Row[]; total: number }>(\n `views/${this.handle}/rows?offset=${offset}&limit=${limit}`,\n )\n return {\n rows: data.rows.map(flattenRow),\n total: data.total,\n }\n }\n\n /** Commit all buffered writes atomically. Fires SSE events. */\n async commit(): Promise<void> {\n await this._request(`views/${this.handle}/commit`, { method: 'POST' })\n }\n\n /** Rollback all buffered writes (discard pending changes). */\n async rollback(): Promise<void> {\n await this._request(`views/${this.handle}/rollback`, { method: 'POST' })\n }\n\n // --- Internal ----------------------------------------------------------\n\n private async _request<T>(path: string, init?: RequestInit): Promise<T> {\n const url = `${this.client.url}/v1/${this.client.db}/${path}`\n const res = await fetch(url, {\n ...init,\n headers: { 'Content-Type': 'application/json', ...init?.headers },\n })\n if (!res.ok) {\n const text = await res.text()\n throw new Error(`PD4 Buffer ${init?.method ?? 'GET'} ${url}: ${res.status} ${text}`)\n }\n if (res.status === 204) return undefined as T\n return res.json()\n }\n}\n","import { PD4Error } from './errors'\nimport { flattenRow, toColumnar, toColumnValues } from './utils'\nimport { PD4Buffer } from './buffer'\nimport type {\n ColumnInfo,\n TableInfo,\n PD4Row,\n Row,\n FetchOptions,\n FetchResult,\n InsertResult,\n ConnectionStatus,\n InsertHandler,\n UpdateHandler,\n DeleteHandler,\n StatusHandler,\n PD4ClientOptions,\n} from './types'\n\n// ---------------------------------------------------------------------------\n// Internal types for SSE handler registry\n// ---------------------------------------------------------------------------\n\ninterface TableHandlers {\n insert: InsertHandler[]\n update: UpdateHandler[]\n delete: DeleteHandler[]\n}\n\n// ---------------------------------------------------------------------------\n// PD4Client\n// ---------------------------------------------------------------------------\n\nexport class PD4Client {\n readonly url: string\n readonly db: string\n\n private EventSourceCtor: (new (url: string) => EventSource) | null\n private eventSource: EventSource | null = null\n private handlers = new Map<string, TableHandlers>()\n private statusHandlers: StatusHandler[] = []\n private _status: ConnectionStatus = 'connecting'\n\n constructor(url: string, db: string, options?: PD4ClientOptions) {\n this.url = url.replace(/\\/$/, '')\n this.db = db\n this.EventSourceCtor = options?.EventSource\n ?? (typeof globalThis !== 'undefined' && globalThis.EventSource\n ? globalThis.EventSource\n : null)\n }\n\n // --- URL helpers -------------------------------------------------------\n\n private get v1() {\n return `${this.url}/v1/${this.db}`\n }\n\n private get api() {\n return `${this.url}/api/${this.db}`\n }\n\n // --- HTTP helper -------------------------------------------------------\n\n private async request<T>(url: string, init?: RequestInit): Promise<T> {\n const res = await fetch(url, {\n ...init,\n headers: { 'Content-Type': 'application/json', ...init?.headers },\n })\n if (!res.ok) {\n const text = await res.text()\n throw new PD4Error(\n `PD4 ${init?.method ?? 'GET'} ${url}: ${res.status} ${text}`,\n res.status,\n url,\n )\n }\n if (res.status === 204) return undefined as T\n return res.json()\n }\n\n // --- Health ------------------------------------------------------------\n\n /** Check if PD4 is reachable */\n async ping(): Promise<boolean> {\n try {\n const res = await fetch(`${this.url}/health`)\n return res.ok\n } catch {\n return false\n }\n }\n\n // --- Database ----------------------------------------------------------\n\n /** Ensure the database is open */\n async ensureDatabase(): Promise<void> {\n await this.request(`${this.url}/v1/db`, {\n method: 'POST',\n body: JSON.stringify({ name: this.db }),\n })\n }\n\n // --- Tables ------------------------------------------------------------\n\n /** List all table names */\n async listTables(): Promise<string[]> {\n const data = await this.request<{ tables: string[] }>(`${this.v1}/tables`)\n return data.tables\n }\n\n /** Get table info */\n async getTable(name: string): Promise<TableInfo> {\n return this.request<TableInfo>(`${this.v1}/tables/${name}`)\n }\n\n /** Create a table (with auto_pk) */\n async createTable(name: string): Promise<void> {\n await this.request(`${this.v1}/tables`, {\n method: 'POST',\n body: JSON.stringify({ name, auto_pk: true }),\n })\n }\n\n /** Drop a table */\n async dropTable(name: string): Promise<void> {\n await this.request(`${this.v1}/tables/${name}`, { method: 'DELETE' })\n }\n\n /** Add a column to a table */\n async addColumn(table: string, name: string, dtype: string): Promise<void> {\n await this.request(`${this.v1}/tables/${table}/columns`, {\n method: 'POST',\n body: JSON.stringify({ name, dtype }),\n })\n }\n\n // --- Rows (flat objects in, flat objects out) ---------------------------\n\n /** Insert one or more rows (flat objects). Returns keys and count. */\n async insert(table: string, rows: Row | Row[]): Promise<InsertResult> {\n const arr = Array.isArray(rows) ? rows : [rows]\n const { columns, rows: colRows } = toColumnar(arr)\n return this.request<InsertResult>(`${this.v1}/tables/${table}/rows`, {\n method: 'POST',\n body: JSON.stringify({ columns, rows: colRows }),\n })\n }\n\n /** Update a single row by key with a flat object of changed columns. */\n async update(table: string, key: number, values: Row): Promise<void> {\n const pairs = toColumnValues(values)\n await this.request(`${this.v1}/views/${table}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values: pairs }),\n })\n }\n\n /** Delete rows by keys. */\n async delete(table: string, keys: number | number[]): Promise<void> {\n const arr = Array.isArray(keys) ? keys : [keys]\n await this.request(`${this.v1}/views/${table}/rows/delete`, {\n method: 'POST',\n body: JSON.stringify({ keys: arr }),\n })\n }\n\n /** Fetch rows from a table/view. Returns flat Row objects. */\n async fetch(table: string, opts?: FetchOptions): Promise<FetchResult> {\n const offset = opts?.offset ?? 0\n const limit = opts?.limit ?? 1000\n const data = await this.request<{ rows: PD4Row[]; total: number }>(\n `${this.v1}/views/${table}/rows?offset=${offset}&limit=${limit}`,\n )\n return {\n rows: data.rows.map(flattenRow),\n total: data.total,\n }\n }\n\n // --- Raw row access (PD4 wire format) ----------------------------------\n\n /** Insert rows in columnar format (low-level). */\n async insertColumnar(\n table: string,\n columns: string[],\n rows: (string | number | boolean | null)[][],\n ): Promise<InsertResult> {\n return this.request<InsertResult>(`${this.v1}/tables/${table}/rows`, {\n method: 'POST',\n body: JSON.stringify({ columns, rows }),\n })\n }\n\n /** Update a row by key with column-value pairs (low-level). */\n async updateRaw(\n table: string,\n key: number,\n values: { column: string; value: string | number | boolean | null }[],\n ): Promise<void> {\n await this.request(`${this.v1}/views/${table}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values }),\n })\n }\n\n /** Fetch rows in PD4 wire format (low-level). */\n async fetchRaw(\n table: string,\n offset = 0,\n limit = 1000,\n ): Promise<{ rows: PD4Row[]; total: number }> {\n return this.request(`${this.v1}/views/${table}/rows?offset=${offset}&limit=${limit}`)\n }\n\n // --- Buffer ------------------------------------------------------------\n\n /** Create a buffered view wrapping a table. */\n async buffer(table: string): Promise<PD4Buffer> {\n const info = await this.request<{ handle: string; source: string; row_count: number }>(\n `${this.v1}/views/${table}/buffer`,\n { method: 'POST' },\n )\n return new PD4Buffer(this, info.handle, table)\n }\n\n // --- SSE ---------------------------------------------------------------\n\n /** Get current SSE connection status. */\n get status(): ConnectionStatus {\n return this._status\n }\n\n /** Connect to the SSE event stream. Called automatically on first on(). */\n connect(): void {\n if (this.eventSource) return\n if (!this.EventSourceCtor) {\n throw new Error(\n 'EventSource is not available. In Node.js, pass { EventSource } from the \"eventsource\" package to the PD4Client constructor.',\n )\n }\n\n this._setStatus('connecting')\n const sse = new this.EventSourceCtor(`${this.api}/events`)\n this.eventSource = sse\n\n sse.addEventListener('session', () => {\n this._setStatus('connected')\n })\n\n sse.addEventListener('insert', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n const newValues: Row[] = data.new_values ?? []\n this._dispatch(table, 'insert', keys, newValues, [])\n })\n\n sse.addEventListener('update', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n const newValues: Row[] = data.new_values ?? []\n const oldValues: Row[] = data.old_values ?? []\n this._dispatch(table, 'update', keys, newValues, oldValues)\n })\n\n sse.addEventListener('delete', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n this._dispatch(table, 'delete', keys, [], [])\n })\n\n sse.onerror = () => {\n this._setStatus('error')\n }\n }\n\n /** Disconnect from the SSE event stream. */\n disconnect(): void {\n this.eventSource?.close()\n this.eventSource = null\n this._setStatus('connecting')\n }\n\n /**\n * Subscribe to SSE events for a table.\n * Auto-connects on first call. Returns an unsubscribe function.\n */\n on(table: string, event: 'insert', handler: InsertHandler): () => void\n on(table: string, event: 'update', handler: UpdateHandler): () => void\n on(table: string, event: 'delete', handler: DeleteHandler): () => void\n on(\n table: string,\n event: 'insert' | 'update' | 'delete',\n handler: InsertHandler | UpdateHandler | DeleteHandler,\n ): () => void {\n // Auto-connect\n if (!this.eventSource) this.connect()\n\n let tableHandlers = this.handlers.get(table)\n if (!tableHandlers) {\n tableHandlers = { insert: [], update: [], delete: [] }\n this.handlers.set(table, tableHandlers)\n }\n\n const list = tableHandlers[event] as unknown[]\n list.push(handler)\n\n return () => {\n const idx = list.indexOf(handler)\n if (idx >= 0) list.splice(idx, 1)\n }\n }\n\n /** Subscribe to connection status changes. Returns an unsubscribe function. */\n onStatus(handler: StatusHandler): () => void {\n this.statusHandlers.push(handler)\n return () => {\n const idx = this.statusHandlers.indexOf(handler)\n if (idx >= 0) this.statusHandlers.splice(idx, 1)\n }\n }\n\n // --- SSE internals -----------------------------------------------------\n\n private _setStatus(status: ConnectionStatus) {\n this._status = status\n for (const handler of this.statusHandlers) {\n handler(status)\n }\n }\n\n private _dispatch(\n table: string,\n event: 'insert' | 'update' | 'delete',\n keys: number[],\n newValues: Row[],\n oldValues: Row[],\n ) {\n const tableHandlers = this.handlers.get(table)\n if (!tableHandlers) return\n\n if (event === 'insert') {\n for (const h of tableHandlers.insert) h(keys, newValues)\n } else if (event === 'update') {\n for (const h of tableHandlers.update) h(keys, newValues, oldValues)\n } else {\n for (const h of tableHandlers.delete) h(keys)\n }\n }\n}\n\n// Re-export for convenience\nexport type { ColumnInfo, TableInfo, PD4Row, Row, FetchOptions, FetchResult, InsertResult }\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/utils.ts","../src/buffer.ts","../src/client.ts"],"sourcesContent":["export { PD4Client } from './client'\nexport { PD4Buffer } from './buffer'\nexport { PD4Error } from './errors'\nexport { flattenRow, toColumnar, toColumnValues } from './utils'\nexport type {\n ColumnInfo,\n TableInfo,\n ColumnValue,\n PD4Row,\n Row,\n SseEventType,\n ConnectionStatus,\n InsertHandler,\n UpdateHandler,\n DeleteHandler,\n WildcardInsertHandler,\n WildcardUpdateHandler,\n WildcardDeleteHandler,\n AnyEventHandler,\n StatusHandler,\n FetchOptions,\n FetchResult,\n InsertResult,\n BufferInfo,\n ViewInfo,\n PD4ClientOptions,\n} from './types'\n","/** Base error class for PD4 client errors */\nexport class PD4Error extends Error {\n readonly status: number | undefined\n readonly url: string | undefined\n\n constructor(message: string, status?: number, url?: string) {\n super(message)\n this.name = 'PD4Error'\n this.status = status\n this.url = url\n }\n}\n","import type { PD4Row, Row } from './types'\n\n/** Convert PD4 row format to flat record */\nexport function flattenRow(row: PD4Row): Row {\n const flat: Row = { _pk: row.key }\n for (const cv of row.values) {\n flat[cv.column] = cv.value\n }\n return flat\n}\n\n/**\n * Convert flat row objects to PD4's columnar insert format.\n * Returns { columns, rows } suitable for the insert endpoint.\n */\nexport function toColumnar(\n flatRows: Row[],\n columnNames?: string[],\n): { columns: string[]; rows: (string | number | boolean | null)[][] } {\n if (flatRows.length === 0) return { columns: [], rows: [] }\n const cols = columnNames ?? Object.keys(flatRows[0]).filter((k) => k !== '_pk')\n const rows = flatRows.map((row) => cols.map((c) => row[c] ?? null))\n return { columns: cols, rows }\n}\n\n/**\n * Convert a flat row object to PD4's column-value pairs for update.\n * Filters out the _pk field.\n */\nexport function toColumnValues(\n flat: Row,\n): { column: string; value: string | number | boolean | null }[] {\n return Object.entries(flat)\n .filter(([k]) => k !== '_pk')\n .map(([column, value]) => ({ column, value }))\n}\n","import type { PD4Client } from './client'\nimport { flattenRow, toColumnValues } from './utils'\nimport type { Row, FetchResult, PD4Row } from './types'\n\n/**\n * A buffered view wrapping a PD4 table.\n * Changes are accumulated locally and only become visible\n * to other clients after commit().\n */\nexport class PD4Buffer {\n private client: PD4Client\n readonly handle: string\n readonly table: string\n\n constructor(client: PD4Client, handle: string, table: string) {\n this.client = client\n this.handle = handle\n this.table = table\n }\n\n /** Update a row within the buffer (not visible to others until commit). */\n async update(key: number, values: Row): Promise<void> {\n const pairs = toColumnValues(values)\n await this._request(`views/${this.handle}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values: pairs }),\n })\n }\n\n /** Fetch rows from the buffer (sees uncommitted changes). */\n async fetch(offset = 0, limit = 100): Promise<FetchResult> {\n const data = await this._request<{ rows: PD4Row[]; total: number }>(\n `views/${this.handle}/rows?offset=${offset}&limit=${limit}`,\n )\n return {\n rows: data.rows.map(flattenRow),\n total: data.total,\n }\n }\n\n /** Commit all buffered writes atomically. Fires SSE events. */\n async commit(): Promise<void> {\n await this._request(`views/${this.handle}/commit`, { method: 'POST' })\n }\n\n /** Rollback all buffered writes (discard pending changes). */\n async rollback(): Promise<void> {\n await this._request(`views/${this.handle}/rollback`, { method: 'POST' })\n }\n\n // --- Internal ----------------------------------------------------------\n\n private async _request<T>(path: string, init?: RequestInit): Promise<T> {\n const url = `${this.client.url}/v1/${this.client.db}/${path}`\n const res = await fetch(url, {\n ...init,\n headers: { 'Content-Type': 'application/json', ...init?.headers },\n })\n if (!res.ok) {\n const text = await res.text()\n throw new Error(`PD4 Buffer ${init?.method ?? 'GET'} ${url}: ${res.status} ${text}`)\n }\n if (res.status === 204) return undefined as T\n return res.json()\n }\n}\n","import { PD4Error } from './errors'\nimport { flattenRow, toColumnar, toColumnValues } from './utils'\nimport { PD4Buffer } from './buffer'\nimport type {\n ColumnInfo,\n TableInfo,\n PD4Row,\n Row,\n FetchOptions,\n FetchResult,\n InsertResult,\n ConnectionStatus,\n InsertHandler,\n UpdateHandler,\n DeleteHandler,\n WildcardInsertHandler,\n WildcardUpdateHandler,\n WildcardDeleteHandler,\n AnyEventHandler,\n StatusHandler,\n PD4ClientOptions,\n} from './types'\n\n// ---------------------------------------------------------------------------\n// Internal types for SSE handler registry\n// ---------------------------------------------------------------------------\n\ninterface TableHandlers {\n insert: InsertHandler[]\n update: UpdateHandler[]\n delete: DeleteHandler[]\n}\n\ninterface WildcardHandlers {\n insert: WildcardInsertHandler[]\n update: WildcardUpdateHandler[]\n delete: WildcardDeleteHandler[]\n}\n\n// ---------------------------------------------------------------------------\n// PD4Client\n// ---------------------------------------------------------------------------\n\nexport class PD4Client {\n readonly url: string\n readonly db: string\n\n private EventSourceCtor: (new (url: string) => EventSource) | null\n private eventSource: EventSource | null = null\n private handlers = new Map<string, TableHandlers>()\n private wildcardHandlers: WildcardHandlers = { insert: [], update: [], delete: [] }\n private anyHandlers: AnyEventHandler[] = []\n private statusHandlers: StatusHandler[] = []\n private _status: ConnectionStatus = 'connecting'\n\n constructor(url: string, db: string, options?: PD4ClientOptions) {\n this.url = url.replace(/\\/$/, '')\n this.db = db\n this.EventSourceCtor = options?.EventSource\n ?? (typeof globalThis !== 'undefined' && globalThis.EventSource\n ? globalThis.EventSource\n : null)\n }\n\n // --- URL helpers -------------------------------------------------------\n\n private get v1() {\n return `${this.url}/v1/${this.db}`\n }\n\n private get api() {\n return `${this.url}/api/${this.db}`\n }\n\n // --- HTTP helper -------------------------------------------------------\n\n private async request<T>(url: string, init?: RequestInit): Promise<T> {\n const res = await fetch(url, {\n ...init,\n headers: { 'Content-Type': 'application/json', ...init?.headers },\n })\n if (!res.ok) {\n const text = await res.text()\n throw new PD4Error(\n `PD4 ${init?.method ?? 'GET'} ${url}: ${res.status} ${text}`,\n res.status,\n url,\n )\n }\n if (res.status === 204) return undefined as T\n return res.json()\n }\n\n // --- Health ------------------------------------------------------------\n\n /** Check if PD4 is reachable */\n async ping(): Promise<boolean> {\n try {\n const res = await fetch(`${this.url}/health`)\n return res.ok\n } catch {\n return false\n }\n }\n\n // --- Database ----------------------------------------------------------\n\n /** Ensure the database is open */\n async ensureDatabase(): Promise<void> {\n await this.request(`${this.url}/v1/db`, {\n method: 'POST',\n body: JSON.stringify({ name: this.db }),\n })\n }\n\n // --- Tables ------------------------------------------------------------\n\n /** List all table names */\n async listTables(): Promise<string[]> {\n const data = await this.request<{ tables: string[] }>(`${this.v1}/tables`)\n return data.tables\n }\n\n /** Get table info */\n async getTable(name: string): Promise<TableInfo> {\n return this.request<TableInfo>(`${this.v1}/tables/${name}`)\n }\n\n /** Create a table (with auto_pk) */\n async createTable(name: string): Promise<void> {\n await this.request(`${this.v1}/tables`, {\n method: 'POST',\n body: JSON.stringify({ name, auto_pk: true }),\n })\n }\n\n /** Drop a table */\n async dropTable(name: string): Promise<void> {\n await this.request(`${this.v1}/tables/${name}`, { method: 'DELETE' })\n }\n\n /** Add a column to a table */\n async addColumn(table: string, name: string, dtype: string): Promise<void> {\n await this.request(`${this.v1}/tables/${table}/columns`, {\n method: 'POST',\n body: JSON.stringify({ name, dtype }),\n })\n }\n\n // --- Rows (flat objects in, flat objects out) ---------------------------\n\n /** Insert one or more rows (flat objects). Returns keys and count. */\n async insert(table: string, rows: Row | Row[]): Promise<InsertResult> {\n const arr = Array.isArray(rows) ? rows : [rows]\n const { columns, rows: colRows } = toColumnar(arr)\n return this.request<InsertResult>(`${this.v1}/tables/${table}/rows`, {\n method: 'POST',\n body: JSON.stringify({ columns, rows: colRows }),\n })\n }\n\n /** Update a single row by key with a flat object of changed columns. */\n async update(table: string, key: number, values: Row): Promise<void> {\n const pairs = toColumnValues(values)\n await this.request(`${this.v1}/views/${table}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values: pairs }),\n })\n }\n\n /** Delete rows by keys. */\n async delete(table: string, keys: number | number[]): Promise<void> {\n const arr = Array.isArray(keys) ? keys : [keys]\n await this.request(`${this.v1}/views/${table}/rows/delete`, {\n method: 'POST',\n body: JSON.stringify({ keys: arr }),\n })\n }\n\n /** Fetch rows from a table/view. Returns flat Row objects. */\n async fetch(table: string, opts?: FetchOptions): Promise<FetchResult> {\n const offset = opts?.offset ?? 0\n const limit = opts?.limit ?? 1000\n const data = await this.request<{ rows: PD4Row[]; total: number }>(\n `${this.v1}/views/${table}/rows?offset=${offset}&limit=${limit}`,\n )\n return {\n rows: data.rows.map(flattenRow),\n total: data.total,\n }\n }\n\n // --- Raw row access (PD4 wire format) ----------------------------------\n\n /** Insert rows in columnar format (low-level). */\n async insertColumnar(\n table: string,\n columns: string[],\n rows: (string | number | boolean | null)[][],\n ): Promise<InsertResult> {\n return this.request<InsertResult>(`${this.v1}/tables/${table}/rows`, {\n method: 'POST',\n body: JSON.stringify({ columns, rows }),\n })\n }\n\n /** Update a row by key with column-value pairs (low-level). */\n async updateRaw(\n table: string,\n key: number,\n values: { column: string; value: string | number | boolean | null }[],\n ): Promise<void> {\n await this.request(`${this.v1}/views/${table}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values }),\n })\n }\n\n /** Fetch rows in PD4 wire format (low-level). */\n async fetchRaw(\n table: string,\n offset = 0,\n limit = 1000,\n ): Promise<{ rows: PD4Row[]; total: number }> {\n return this.request(`${this.v1}/views/${table}/rows?offset=${offset}&limit=${limit}`)\n }\n\n // --- Buffer ------------------------------------------------------------\n\n /** Create a buffered view wrapping a table. */\n async buffer(table: string): Promise<PD4Buffer> {\n const info = await this.request<{ handle: string; source: string; row_count: number }>(\n `${this.v1}/views/${table}/buffer`,\n { method: 'POST' },\n )\n return new PD4Buffer(this, info.handle, table)\n }\n\n // --- SSE ---------------------------------------------------------------\n\n /** Get current SSE connection status. */\n get status(): ConnectionStatus {\n return this._status\n }\n\n /** Connect to the SSE event stream. Called automatically on first on(). */\n connect(): void {\n if (this.eventSource) return\n if (!this.EventSourceCtor) {\n throw new Error(\n 'EventSource is not available. In Node.js, pass { EventSource } from the \"eventsource\" package to the PD4Client constructor.',\n )\n }\n\n this._setStatus('connecting')\n const sse = new this.EventSourceCtor(`${this.api}/events`)\n this.eventSource = sse\n\n sse.addEventListener('session', () => {\n this._setStatus('connected')\n })\n\n sse.addEventListener('insert', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n const newValues: Row[] = data.new_values ?? []\n this._dispatch(table, 'insert', keys, newValues, [])\n })\n\n sse.addEventListener('update', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n const newValues: Row[] = data.new_values ?? []\n const oldValues: Row[] = data.old_values ?? []\n this._dispatch(table, 'update', keys, newValues, oldValues)\n })\n\n sse.addEventListener('delete', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n this._dispatch(table, 'delete', keys, [], [])\n })\n\n sse.onerror = () => {\n this._setStatus('error')\n }\n }\n\n /** Disconnect from the SSE event stream. */\n disconnect(): void {\n this.eventSource?.close()\n this.eventSource = null\n this._setStatus('connecting')\n }\n\n /**\n * Subscribe to SSE events for a specific table, or use '*' for all tables.\n * Auto-connects on first call. Returns an unsubscribe function.\n *\n * Table-specific: handler receives (keys, rows/newValues/oldValues)\n * Wildcard '*': handler receives (table, keys, rows/newValues/oldValues)\n */\n on(table: '*', event: 'insert', handler: WildcardInsertHandler): () => void\n on(table: '*', event: 'update', handler: WildcardUpdateHandler): () => void\n on(table: '*', event: 'delete', handler: WildcardDeleteHandler): () => void\n on(table: string, event: 'insert', handler: InsertHandler): () => void\n on(table: string, event: 'update', handler: UpdateHandler): () => void\n on(table: string, event: 'delete', handler: DeleteHandler): () => void\n on(\n table: string,\n event: 'insert' | 'update' | 'delete',\n handler:\n | InsertHandler | UpdateHandler | DeleteHandler\n | WildcardInsertHandler | WildcardUpdateHandler | WildcardDeleteHandler,\n ): () => void {\n // Auto-connect\n if (!this.eventSource) this.connect()\n\n // Wildcard: register in wildcardHandlers\n if (table === '*') {\n const list = this.wildcardHandlers[event] as unknown[]\n list.push(handler)\n return () => {\n const idx = list.indexOf(handler)\n if (idx >= 0) list.splice(idx, 1)\n }\n }\n\n // Table-specific\n let tableHandlers = this.handlers.get(table)\n if (!tableHandlers) {\n tableHandlers = { insert: [], update: [], delete: [] }\n this.handlers.set(table, tableHandlers)\n }\n\n const list = tableHandlers[event] as unknown[]\n list.push(handler)\n\n return () => {\n const idx = list.indexOf(handler)\n if (idx >= 0) list.splice(idx, 1)\n }\n }\n\n /**\n * Subscribe to all SSE events on all tables.\n * Handler receives (table, event, keys, newValues, oldValues).\n * Auto-connects on first call. Returns an unsubscribe function.\n */\n onAny(handler: AnyEventHandler): () => void {\n if (!this.eventSource) this.connect()\n\n this.anyHandlers.push(handler)\n return () => {\n const idx = this.anyHandlers.indexOf(handler)\n if (idx >= 0) this.anyHandlers.splice(idx, 1)\n }\n }\n\n /** Subscribe to connection status changes. Returns an unsubscribe function. */\n onStatus(handler: StatusHandler): () => void {\n this.statusHandlers.push(handler)\n return () => {\n const idx = this.statusHandlers.indexOf(handler)\n if (idx >= 0) this.statusHandlers.splice(idx, 1)\n }\n }\n\n // --- SSE internals -----------------------------------------------------\n\n private _setStatus(status: ConnectionStatus) {\n this._status = status\n for (const handler of this.statusHandlers) {\n handler(status)\n }\n }\n\n private _dispatch(\n table: string,\n event: 'insert' | 'update' | 'delete',\n keys: number[],\n newValues: Row[],\n oldValues: Row[],\n ) {\n // Table-specific handlers\n const tableHandlers = this.handlers.get(table)\n if (tableHandlers) {\n if (event === 'insert') {\n for (const h of tableHandlers.insert) h(keys, newValues)\n } else if (event === 'update') {\n for (const h of tableHandlers.update) h(keys, newValues, oldValues)\n } else {\n for (const h of tableHandlers.delete) h(keys)\n }\n }\n\n // Wildcard handlers (on('*', ...))\n if (event === 'insert') {\n for (const h of this.wildcardHandlers.insert) h(table, keys, newValues)\n } else if (event === 'update') {\n for (const h of this.wildcardHandlers.update) h(table, keys, newValues, oldValues)\n } else {\n for (const h of this.wildcardHandlers.delete) h(table, keys)\n }\n\n // onAny handlers\n for (const h of this.anyHandlers) {\n h(table, event, keys, newValues, oldValues)\n }\n }\n}\n\n// Re-export for convenience\nexport type {\n ColumnInfo, TableInfo, PD4Row, Row, FetchOptions, FetchResult, InsertResult,\n WildcardInsertHandler, WildcardUpdateHandler, WildcardDeleteHandler, AnyEventHandler,\n}\n"],"mappings":"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","names":["list"]}
package/dist/index.d.cts CHANGED
@@ -31,6 +31,12 @@ type InsertHandler = (keys: number[], rows: Row[]) => void;
31
31
  type UpdateHandler = (keys: number[], newValues: Row[], oldValues: Row[]) => void;
32
32
  /** Callback for delete events: (keys) */
33
33
  type DeleteHandler = (keys: number[]) => void;
34
+ /** Wildcard callbacks: same as above but with table name as first arg */
35
+ type WildcardInsertHandler = (table: string, keys: number[], rows: Row[]) => void;
36
+ type WildcardUpdateHandler = (table: string, keys: number[], newValues: Row[], oldValues: Row[]) => void;
37
+ type WildcardDeleteHandler = (table: string, keys: number[]) => void;
38
+ /** Callback for onAny: fires for every event on every table */
39
+ type AnyEventHandler = (table: string, event: SseEventType, keys: number[], newValues: Row[], oldValues: Row[]) => void;
34
40
  /** Callback for status changes */
35
41
  type StatusHandler = (status: ConnectionStatus) => void;
36
42
  /** Options for fetch */
@@ -95,6 +101,8 @@ declare class PD4Client {
95
101
  private EventSourceCtor;
96
102
  private eventSource;
97
103
  private handlers;
104
+ private wildcardHandlers;
105
+ private anyHandlers;
98
106
  private statusHandlers;
99
107
  private _status;
100
108
  constructor(url: string, db: string, options?: PD4ClientOptions);
@@ -144,12 +152,24 @@ declare class PD4Client {
144
152
  /** Disconnect from the SSE event stream. */
145
153
  disconnect(): void;
146
154
  /**
147
- * Subscribe to SSE events for a table.
155
+ * Subscribe to SSE events for a specific table, or use '*' for all tables.
148
156
  * Auto-connects on first call. Returns an unsubscribe function.
157
+ *
158
+ * Table-specific: handler receives (keys, rows/newValues/oldValues)
159
+ * Wildcard '*': handler receives (table, keys, rows/newValues/oldValues)
149
160
  */
161
+ on(table: '*', event: 'insert', handler: WildcardInsertHandler): () => void;
162
+ on(table: '*', event: 'update', handler: WildcardUpdateHandler): () => void;
163
+ on(table: '*', event: 'delete', handler: WildcardDeleteHandler): () => void;
150
164
  on(table: string, event: 'insert', handler: InsertHandler): () => void;
151
165
  on(table: string, event: 'update', handler: UpdateHandler): () => void;
152
166
  on(table: string, event: 'delete', handler: DeleteHandler): () => void;
167
+ /**
168
+ * Subscribe to all SSE events on all tables.
169
+ * Handler receives (table, event, keys, newValues, oldValues).
170
+ * Auto-connects on first call. Returns an unsubscribe function.
171
+ */
172
+ onAny(handler: AnyEventHandler): () => void;
153
173
  /** Subscribe to connection status changes. Returns an unsubscribe function. */
154
174
  onStatus(handler: StatusHandler): () => void;
155
175
  private _setStatus;
@@ -182,4 +202,4 @@ declare function toColumnValues(flat: Row): {
182
202
  value: string | number | boolean | null;
183
203
  }[];
184
204
 
185
- export { type BufferInfo, type ColumnInfo, type ColumnValue, type ConnectionStatus, type DeleteHandler, type FetchOptions, type FetchResult, type InsertHandler, type InsertResult, PD4Buffer, PD4Client, type PD4ClientOptions, PD4Error, type PD4Row, type Row, type SseEventType, type StatusHandler, type TableInfo, type UpdateHandler, type ViewInfo, flattenRow, toColumnValues, toColumnar };
205
+ export { type AnyEventHandler, type BufferInfo, type ColumnInfo, type ColumnValue, type ConnectionStatus, type DeleteHandler, type FetchOptions, type FetchResult, type InsertHandler, type InsertResult, PD4Buffer, PD4Client, type PD4ClientOptions, PD4Error, type PD4Row, type Row, type SseEventType, type StatusHandler, type TableInfo, type UpdateHandler, type ViewInfo, type WildcardDeleteHandler, type WildcardInsertHandler, type WildcardUpdateHandler, flattenRow, toColumnValues, toColumnar };
package/dist/index.d.ts CHANGED
@@ -31,6 +31,12 @@ type InsertHandler = (keys: number[], rows: Row[]) => void;
31
31
  type UpdateHandler = (keys: number[], newValues: Row[], oldValues: Row[]) => void;
32
32
  /** Callback for delete events: (keys) */
33
33
  type DeleteHandler = (keys: number[]) => void;
34
+ /** Wildcard callbacks: same as above but with table name as first arg */
35
+ type WildcardInsertHandler = (table: string, keys: number[], rows: Row[]) => void;
36
+ type WildcardUpdateHandler = (table: string, keys: number[], newValues: Row[], oldValues: Row[]) => void;
37
+ type WildcardDeleteHandler = (table: string, keys: number[]) => void;
38
+ /** Callback for onAny: fires for every event on every table */
39
+ type AnyEventHandler = (table: string, event: SseEventType, keys: number[], newValues: Row[], oldValues: Row[]) => void;
34
40
  /** Callback for status changes */
35
41
  type StatusHandler = (status: ConnectionStatus) => void;
36
42
  /** Options for fetch */
@@ -95,6 +101,8 @@ declare class PD4Client {
95
101
  private EventSourceCtor;
96
102
  private eventSource;
97
103
  private handlers;
104
+ private wildcardHandlers;
105
+ private anyHandlers;
98
106
  private statusHandlers;
99
107
  private _status;
100
108
  constructor(url: string, db: string, options?: PD4ClientOptions);
@@ -144,12 +152,24 @@ declare class PD4Client {
144
152
  /** Disconnect from the SSE event stream. */
145
153
  disconnect(): void;
146
154
  /**
147
- * Subscribe to SSE events for a table.
155
+ * Subscribe to SSE events for a specific table, or use '*' for all tables.
148
156
  * Auto-connects on first call. Returns an unsubscribe function.
157
+ *
158
+ * Table-specific: handler receives (keys, rows/newValues/oldValues)
159
+ * Wildcard '*': handler receives (table, keys, rows/newValues/oldValues)
149
160
  */
161
+ on(table: '*', event: 'insert', handler: WildcardInsertHandler): () => void;
162
+ on(table: '*', event: 'update', handler: WildcardUpdateHandler): () => void;
163
+ on(table: '*', event: 'delete', handler: WildcardDeleteHandler): () => void;
150
164
  on(table: string, event: 'insert', handler: InsertHandler): () => void;
151
165
  on(table: string, event: 'update', handler: UpdateHandler): () => void;
152
166
  on(table: string, event: 'delete', handler: DeleteHandler): () => void;
167
+ /**
168
+ * Subscribe to all SSE events on all tables.
169
+ * Handler receives (table, event, keys, newValues, oldValues).
170
+ * Auto-connects on first call. Returns an unsubscribe function.
171
+ */
172
+ onAny(handler: AnyEventHandler): () => void;
153
173
  /** Subscribe to connection status changes. Returns an unsubscribe function. */
154
174
  onStatus(handler: StatusHandler): () => void;
155
175
  private _setStatus;
@@ -182,4 +202,4 @@ declare function toColumnValues(flat: Row): {
182
202
  value: string | number | boolean | null;
183
203
  }[];
184
204
 
185
- export { type BufferInfo, type ColumnInfo, type ColumnValue, type ConnectionStatus, type DeleteHandler, type FetchOptions, type FetchResult, type InsertHandler, type InsertResult, PD4Buffer, PD4Client, type PD4ClientOptions, PD4Error, type PD4Row, type Row, type SseEventType, type StatusHandler, type TableInfo, type UpdateHandler, type ViewInfo, flattenRow, toColumnValues, toColumnar };
205
+ export { type AnyEventHandler, type BufferInfo, type ColumnInfo, type ColumnValue, type ConnectionStatus, type DeleteHandler, type FetchOptions, type FetchResult, type InsertHandler, type InsertResult, PD4Buffer, PD4Client, type PD4ClientOptions, PD4Error, type PD4Row, type Row, type SseEventType, type StatusHandler, type TableInfo, type UpdateHandler, type ViewInfo, type WildcardDeleteHandler, type WildcardInsertHandler, type WildcardUpdateHandler, flattenRow, toColumnValues, toColumnar };
package/dist/index.js CHANGED
@@ -80,6 +80,8 @@ var PD4Client = class {
80
80
  constructor(url, db, options) {
81
81
  this.eventSource = null;
82
82
  this.handlers = /* @__PURE__ */ new Map();
83
+ this.wildcardHandlers = { insert: [], update: [], delete: [] };
84
+ this.anyHandlers = [];
83
85
  this.statusHandlers = [];
84
86
  this._status = "connecting";
85
87
  this.url = url.replace(/\/$/, "");
@@ -274,6 +276,14 @@ var PD4Client = class {
274
276
  }
275
277
  on(table, event, handler) {
276
278
  if (!this.eventSource) this.connect();
279
+ if (table === "*") {
280
+ const list2 = this.wildcardHandlers[event];
281
+ list2.push(handler);
282
+ return () => {
283
+ const idx = list2.indexOf(handler);
284
+ if (idx >= 0) list2.splice(idx, 1);
285
+ };
286
+ }
277
287
  let tableHandlers = this.handlers.get(table);
278
288
  if (!tableHandlers) {
279
289
  tableHandlers = { insert: [], update: [], delete: [] };
@@ -286,6 +296,19 @@ var PD4Client = class {
286
296
  if (idx >= 0) list.splice(idx, 1);
287
297
  };
288
298
  }
299
+ /**
300
+ * Subscribe to all SSE events on all tables.
301
+ * Handler receives (table, event, keys, newValues, oldValues).
302
+ * Auto-connects on first call. Returns an unsubscribe function.
303
+ */
304
+ onAny(handler) {
305
+ if (!this.eventSource) this.connect();
306
+ this.anyHandlers.push(handler);
307
+ return () => {
308
+ const idx = this.anyHandlers.indexOf(handler);
309
+ if (idx >= 0) this.anyHandlers.splice(idx, 1);
310
+ };
311
+ }
289
312
  /** Subscribe to connection status changes. Returns an unsubscribe function. */
290
313
  onStatus(handler) {
291
314
  this.statusHandlers.push(handler);
@@ -303,13 +326,24 @@ var PD4Client = class {
303
326
  }
304
327
  _dispatch(table, event, keys, newValues, oldValues) {
305
328
  const tableHandlers = this.handlers.get(table);
306
- if (!tableHandlers) return;
329
+ if (tableHandlers) {
330
+ if (event === "insert") {
331
+ for (const h of tableHandlers.insert) h(keys, newValues);
332
+ } else if (event === "update") {
333
+ for (const h of tableHandlers.update) h(keys, newValues, oldValues);
334
+ } else {
335
+ for (const h of tableHandlers.delete) h(keys);
336
+ }
337
+ }
307
338
  if (event === "insert") {
308
- for (const h of tableHandlers.insert) h(keys, newValues);
339
+ for (const h of this.wildcardHandlers.insert) h(table, keys, newValues);
309
340
  } else if (event === "update") {
310
- for (const h of tableHandlers.update) h(keys, newValues, oldValues);
341
+ for (const h of this.wildcardHandlers.update) h(table, keys, newValues, oldValues);
311
342
  } else {
312
- for (const h of tableHandlers.delete) h(keys);
343
+ for (const h of this.wildcardHandlers.delete) h(table, keys);
344
+ }
345
+ for (const h of this.anyHandlers) {
346
+ h(table, event, keys, newValues, oldValues);
313
347
  }
314
348
  }
315
349
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/utils.ts","../src/buffer.ts","../src/client.ts"],"sourcesContent":["/** Base error class for PD4 client errors */\nexport class PD4Error extends Error {\n readonly status: number | undefined\n readonly url: string | undefined\n\n constructor(message: string, status?: number, url?: string) {\n super(message)\n this.name = 'PD4Error'\n this.status = status\n this.url = url\n }\n}\n","import type { PD4Row, Row } from './types'\n\n/** Convert PD4 row format to flat record */\nexport function flattenRow(row: PD4Row): Row {\n const flat: Row = { _pk: row.key }\n for (const cv of row.values) {\n flat[cv.column] = cv.value\n }\n return flat\n}\n\n/**\n * Convert flat row objects to PD4's columnar insert format.\n * Returns { columns, rows } suitable for the insert endpoint.\n */\nexport function toColumnar(\n flatRows: Row[],\n columnNames?: string[],\n): { columns: string[]; rows: (string | number | boolean | null)[][] } {\n if (flatRows.length === 0) return { columns: [], rows: [] }\n const cols = columnNames ?? Object.keys(flatRows[0]).filter((k) => k !== '_pk')\n const rows = flatRows.map((row) => cols.map((c) => row[c] ?? null))\n return { columns: cols, rows }\n}\n\n/**\n * Convert a flat row object to PD4's column-value pairs for update.\n * Filters out the _pk field.\n */\nexport function toColumnValues(\n flat: Row,\n): { column: string; value: string | number | boolean | null }[] {\n return Object.entries(flat)\n .filter(([k]) => k !== '_pk')\n .map(([column, value]) => ({ column, value }))\n}\n","import type { PD4Client } from './client'\nimport { flattenRow, toColumnValues } from './utils'\nimport type { Row, FetchResult, PD4Row } from './types'\n\n/**\n * A buffered view wrapping a PD4 table.\n * Changes are accumulated locally and only become visible\n * to other clients after commit().\n */\nexport class PD4Buffer {\n private client: PD4Client\n readonly handle: string\n readonly table: string\n\n constructor(client: PD4Client, handle: string, table: string) {\n this.client = client\n this.handle = handle\n this.table = table\n }\n\n /** Update a row within the buffer (not visible to others until commit). */\n async update(key: number, values: Row): Promise<void> {\n const pairs = toColumnValues(values)\n await this._request(`views/${this.handle}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values: pairs }),\n })\n }\n\n /** Fetch rows from the buffer (sees uncommitted changes). */\n async fetch(offset = 0, limit = 100): Promise<FetchResult> {\n const data = await this._request<{ rows: PD4Row[]; total: number }>(\n `views/${this.handle}/rows?offset=${offset}&limit=${limit}`,\n )\n return {\n rows: data.rows.map(flattenRow),\n total: data.total,\n }\n }\n\n /** Commit all buffered writes atomically. Fires SSE events. */\n async commit(): Promise<void> {\n await this._request(`views/${this.handle}/commit`, { method: 'POST' })\n }\n\n /** Rollback all buffered writes (discard pending changes). */\n async rollback(): Promise<void> {\n await this._request(`views/${this.handle}/rollback`, { method: 'POST' })\n }\n\n // --- Internal ----------------------------------------------------------\n\n private async _request<T>(path: string, init?: RequestInit): Promise<T> {\n const url = `${this.client.url}/v1/${this.client.db}/${path}`\n const res = await fetch(url, {\n ...init,\n headers: { 'Content-Type': 'application/json', ...init?.headers },\n })\n if (!res.ok) {\n const text = await res.text()\n throw new Error(`PD4 Buffer ${init?.method ?? 'GET'} ${url}: ${res.status} ${text}`)\n }\n if (res.status === 204) return undefined as T\n return res.json()\n }\n}\n","import { PD4Error } from './errors'\nimport { flattenRow, toColumnar, toColumnValues } from './utils'\nimport { PD4Buffer } from './buffer'\nimport type {\n ColumnInfo,\n TableInfo,\n PD4Row,\n Row,\n FetchOptions,\n FetchResult,\n InsertResult,\n ConnectionStatus,\n InsertHandler,\n UpdateHandler,\n DeleteHandler,\n StatusHandler,\n PD4ClientOptions,\n} from './types'\n\n// ---------------------------------------------------------------------------\n// Internal types for SSE handler registry\n// ---------------------------------------------------------------------------\n\ninterface TableHandlers {\n insert: InsertHandler[]\n update: UpdateHandler[]\n delete: DeleteHandler[]\n}\n\n// ---------------------------------------------------------------------------\n// PD4Client\n// ---------------------------------------------------------------------------\n\nexport class PD4Client {\n readonly url: string\n readonly db: string\n\n private EventSourceCtor: (new (url: string) => EventSource) | null\n private eventSource: EventSource | null = null\n private handlers = new Map<string, TableHandlers>()\n private statusHandlers: StatusHandler[] = []\n private _status: ConnectionStatus = 'connecting'\n\n constructor(url: string, db: string, options?: PD4ClientOptions) {\n this.url = url.replace(/\\/$/, '')\n this.db = db\n this.EventSourceCtor = options?.EventSource\n ?? (typeof globalThis !== 'undefined' && globalThis.EventSource\n ? globalThis.EventSource\n : null)\n }\n\n // --- URL helpers -------------------------------------------------------\n\n private get v1() {\n return `${this.url}/v1/${this.db}`\n }\n\n private get api() {\n return `${this.url}/api/${this.db}`\n }\n\n // --- HTTP helper -------------------------------------------------------\n\n private async request<T>(url: string, init?: RequestInit): Promise<T> {\n const res = await fetch(url, {\n ...init,\n headers: { 'Content-Type': 'application/json', ...init?.headers },\n })\n if (!res.ok) {\n const text = await res.text()\n throw new PD4Error(\n `PD4 ${init?.method ?? 'GET'} ${url}: ${res.status} ${text}`,\n res.status,\n url,\n )\n }\n if (res.status === 204) return undefined as T\n return res.json()\n }\n\n // --- Health ------------------------------------------------------------\n\n /** Check if PD4 is reachable */\n async ping(): Promise<boolean> {\n try {\n const res = await fetch(`${this.url}/health`)\n return res.ok\n } catch {\n return false\n }\n }\n\n // --- Database ----------------------------------------------------------\n\n /** Ensure the database is open */\n async ensureDatabase(): Promise<void> {\n await this.request(`${this.url}/v1/db`, {\n method: 'POST',\n body: JSON.stringify({ name: this.db }),\n })\n }\n\n // --- Tables ------------------------------------------------------------\n\n /** List all table names */\n async listTables(): Promise<string[]> {\n const data = await this.request<{ tables: string[] }>(`${this.v1}/tables`)\n return data.tables\n }\n\n /** Get table info */\n async getTable(name: string): Promise<TableInfo> {\n return this.request<TableInfo>(`${this.v1}/tables/${name}`)\n }\n\n /** Create a table (with auto_pk) */\n async createTable(name: string): Promise<void> {\n await this.request(`${this.v1}/tables`, {\n method: 'POST',\n body: JSON.stringify({ name, auto_pk: true }),\n })\n }\n\n /** Drop a table */\n async dropTable(name: string): Promise<void> {\n await this.request(`${this.v1}/tables/${name}`, { method: 'DELETE' })\n }\n\n /** Add a column to a table */\n async addColumn(table: string, name: string, dtype: string): Promise<void> {\n await this.request(`${this.v1}/tables/${table}/columns`, {\n method: 'POST',\n body: JSON.stringify({ name, dtype }),\n })\n }\n\n // --- Rows (flat objects in, flat objects out) ---------------------------\n\n /** Insert one or more rows (flat objects). Returns keys and count. */\n async insert(table: string, rows: Row | Row[]): Promise<InsertResult> {\n const arr = Array.isArray(rows) ? rows : [rows]\n const { columns, rows: colRows } = toColumnar(arr)\n return this.request<InsertResult>(`${this.v1}/tables/${table}/rows`, {\n method: 'POST',\n body: JSON.stringify({ columns, rows: colRows }),\n })\n }\n\n /** Update a single row by key with a flat object of changed columns. */\n async update(table: string, key: number, values: Row): Promise<void> {\n const pairs = toColumnValues(values)\n await this.request(`${this.v1}/views/${table}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values: pairs }),\n })\n }\n\n /** Delete rows by keys. */\n async delete(table: string, keys: number | number[]): Promise<void> {\n const arr = Array.isArray(keys) ? keys : [keys]\n await this.request(`${this.v1}/views/${table}/rows/delete`, {\n method: 'POST',\n body: JSON.stringify({ keys: arr }),\n })\n }\n\n /** Fetch rows from a table/view. Returns flat Row objects. */\n async fetch(table: string, opts?: FetchOptions): Promise<FetchResult> {\n const offset = opts?.offset ?? 0\n const limit = opts?.limit ?? 1000\n const data = await this.request<{ rows: PD4Row[]; total: number }>(\n `${this.v1}/views/${table}/rows?offset=${offset}&limit=${limit}`,\n )\n return {\n rows: data.rows.map(flattenRow),\n total: data.total,\n }\n }\n\n // --- Raw row access (PD4 wire format) ----------------------------------\n\n /** Insert rows in columnar format (low-level). */\n async insertColumnar(\n table: string,\n columns: string[],\n rows: (string | number | boolean | null)[][],\n ): Promise<InsertResult> {\n return this.request<InsertResult>(`${this.v1}/tables/${table}/rows`, {\n method: 'POST',\n body: JSON.stringify({ columns, rows }),\n })\n }\n\n /** Update a row by key with column-value pairs (low-level). */\n async updateRaw(\n table: string,\n key: number,\n values: { column: string; value: string | number | boolean | null }[],\n ): Promise<void> {\n await this.request(`${this.v1}/views/${table}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values }),\n })\n }\n\n /** Fetch rows in PD4 wire format (low-level). */\n async fetchRaw(\n table: string,\n offset = 0,\n limit = 1000,\n ): Promise<{ rows: PD4Row[]; total: number }> {\n return this.request(`${this.v1}/views/${table}/rows?offset=${offset}&limit=${limit}`)\n }\n\n // --- Buffer ------------------------------------------------------------\n\n /** Create a buffered view wrapping a table. */\n async buffer(table: string): Promise<PD4Buffer> {\n const info = await this.request<{ handle: string; source: string; row_count: number }>(\n `${this.v1}/views/${table}/buffer`,\n { method: 'POST' },\n )\n return new PD4Buffer(this, info.handle, table)\n }\n\n // --- SSE ---------------------------------------------------------------\n\n /** Get current SSE connection status. */\n get status(): ConnectionStatus {\n return this._status\n }\n\n /** Connect to the SSE event stream. Called automatically on first on(). */\n connect(): void {\n if (this.eventSource) return\n if (!this.EventSourceCtor) {\n throw new Error(\n 'EventSource is not available. In Node.js, pass { EventSource } from the \"eventsource\" package to the PD4Client constructor.',\n )\n }\n\n this._setStatus('connecting')\n const sse = new this.EventSourceCtor(`${this.api}/events`)\n this.eventSource = sse\n\n sse.addEventListener('session', () => {\n this._setStatus('connected')\n })\n\n sse.addEventListener('insert', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n const newValues: Row[] = data.new_values ?? []\n this._dispatch(table, 'insert', keys, newValues, [])\n })\n\n sse.addEventListener('update', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n const newValues: Row[] = data.new_values ?? []\n const oldValues: Row[] = data.old_values ?? []\n this._dispatch(table, 'update', keys, newValues, oldValues)\n })\n\n sse.addEventListener('delete', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n this._dispatch(table, 'delete', keys, [], [])\n })\n\n sse.onerror = () => {\n this._setStatus('error')\n }\n }\n\n /** Disconnect from the SSE event stream. */\n disconnect(): void {\n this.eventSource?.close()\n this.eventSource = null\n this._setStatus('connecting')\n }\n\n /**\n * Subscribe to SSE events for a table.\n * Auto-connects on first call. Returns an unsubscribe function.\n */\n on(table: string, event: 'insert', handler: InsertHandler): () => void\n on(table: string, event: 'update', handler: UpdateHandler): () => void\n on(table: string, event: 'delete', handler: DeleteHandler): () => void\n on(\n table: string,\n event: 'insert' | 'update' | 'delete',\n handler: InsertHandler | UpdateHandler | DeleteHandler,\n ): () => void {\n // Auto-connect\n if (!this.eventSource) this.connect()\n\n let tableHandlers = this.handlers.get(table)\n if (!tableHandlers) {\n tableHandlers = { insert: [], update: [], delete: [] }\n this.handlers.set(table, tableHandlers)\n }\n\n const list = tableHandlers[event] as unknown[]\n list.push(handler)\n\n return () => {\n const idx = list.indexOf(handler)\n if (idx >= 0) list.splice(idx, 1)\n }\n }\n\n /** Subscribe to connection status changes. Returns an unsubscribe function. */\n onStatus(handler: StatusHandler): () => void {\n this.statusHandlers.push(handler)\n return () => {\n const idx = this.statusHandlers.indexOf(handler)\n if (idx >= 0) this.statusHandlers.splice(idx, 1)\n }\n }\n\n // --- SSE internals -----------------------------------------------------\n\n private _setStatus(status: ConnectionStatus) {\n this._status = status\n for (const handler of this.statusHandlers) {\n handler(status)\n }\n }\n\n private _dispatch(\n table: string,\n event: 'insert' | 'update' | 'delete',\n keys: number[],\n newValues: Row[],\n oldValues: Row[],\n ) {\n const tableHandlers = this.handlers.get(table)\n if (!tableHandlers) return\n\n if (event === 'insert') {\n for (const h of tableHandlers.insert) h(keys, newValues)\n } else if (event === 'update') {\n for (const h of tableHandlers.update) h(keys, newValues, oldValues)\n } else {\n for (const h of tableHandlers.delete) h(keys)\n }\n }\n}\n\n// Re-export for convenience\nexport type { ColumnInfo, TableInfo, PD4Row, Row, FetchOptions, FetchResult, InsertResult }\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/utils.ts","../src/buffer.ts","../src/client.ts"],"sourcesContent":["/** Base error class for PD4 client errors */\nexport class PD4Error extends Error {\n readonly status: number | undefined\n readonly url: string | undefined\n\n constructor(message: string, status?: number, url?: string) {\n super(message)\n this.name = 'PD4Error'\n this.status = status\n this.url = url\n }\n}\n","import type { PD4Row, Row } from './types'\n\n/** Convert PD4 row format to flat record */\nexport function flattenRow(row: PD4Row): Row {\n const flat: Row = { _pk: row.key }\n for (const cv of row.values) {\n flat[cv.column] = cv.value\n }\n return flat\n}\n\n/**\n * Convert flat row objects to PD4's columnar insert format.\n * Returns { columns, rows } suitable for the insert endpoint.\n */\nexport function toColumnar(\n flatRows: Row[],\n columnNames?: string[],\n): { columns: string[]; rows: (string | number | boolean | null)[][] } {\n if (flatRows.length === 0) return { columns: [], rows: [] }\n const cols = columnNames ?? Object.keys(flatRows[0]).filter((k) => k !== '_pk')\n const rows = flatRows.map((row) => cols.map((c) => row[c] ?? null))\n return { columns: cols, rows }\n}\n\n/**\n * Convert a flat row object to PD4's column-value pairs for update.\n * Filters out the _pk field.\n */\nexport function toColumnValues(\n flat: Row,\n): { column: string; value: string | number | boolean | null }[] {\n return Object.entries(flat)\n .filter(([k]) => k !== '_pk')\n .map(([column, value]) => ({ column, value }))\n}\n","import type { PD4Client } from './client'\nimport { flattenRow, toColumnValues } from './utils'\nimport type { Row, FetchResult, PD4Row } from './types'\n\n/**\n * A buffered view wrapping a PD4 table.\n * Changes are accumulated locally and only become visible\n * to other clients after commit().\n */\nexport class PD4Buffer {\n private client: PD4Client\n readonly handle: string\n readonly table: string\n\n constructor(client: PD4Client, handle: string, table: string) {\n this.client = client\n this.handle = handle\n this.table = table\n }\n\n /** Update a row within the buffer (not visible to others until commit). */\n async update(key: number, values: Row): Promise<void> {\n const pairs = toColumnValues(values)\n await this._request(`views/${this.handle}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values: pairs }),\n })\n }\n\n /** Fetch rows from the buffer (sees uncommitted changes). */\n async fetch(offset = 0, limit = 100): Promise<FetchResult> {\n const data = await this._request<{ rows: PD4Row[]; total: number }>(\n `views/${this.handle}/rows?offset=${offset}&limit=${limit}`,\n )\n return {\n rows: data.rows.map(flattenRow),\n total: data.total,\n }\n }\n\n /** Commit all buffered writes atomically. Fires SSE events. */\n async commit(): Promise<void> {\n await this._request(`views/${this.handle}/commit`, { method: 'POST' })\n }\n\n /** Rollback all buffered writes (discard pending changes). */\n async rollback(): Promise<void> {\n await this._request(`views/${this.handle}/rollback`, { method: 'POST' })\n }\n\n // --- Internal ----------------------------------------------------------\n\n private async _request<T>(path: string, init?: RequestInit): Promise<T> {\n const url = `${this.client.url}/v1/${this.client.db}/${path}`\n const res = await fetch(url, {\n ...init,\n headers: { 'Content-Type': 'application/json', ...init?.headers },\n })\n if (!res.ok) {\n const text = await res.text()\n throw new Error(`PD4 Buffer ${init?.method ?? 'GET'} ${url}: ${res.status} ${text}`)\n }\n if (res.status === 204) return undefined as T\n return res.json()\n }\n}\n","import { PD4Error } from './errors'\nimport { flattenRow, toColumnar, toColumnValues } from './utils'\nimport { PD4Buffer } from './buffer'\nimport type {\n ColumnInfo,\n TableInfo,\n PD4Row,\n Row,\n FetchOptions,\n FetchResult,\n InsertResult,\n ConnectionStatus,\n InsertHandler,\n UpdateHandler,\n DeleteHandler,\n WildcardInsertHandler,\n WildcardUpdateHandler,\n WildcardDeleteHandler,\n AnyEventHandler,\n StatusHandler,\n PD4ClientOptions,\n} from './types'\n\n// ---------------------------------------------------------------------------\n// Internal types for SSE handler registry\n// ---------------------------------------------------------------------------\n\ninterface TableHandlers {\n insert: InsertHandler[]\n update: UpdateHandler[]\n delete: DeleteHandler[]\n}\n\ninterface WildcardHandlers {\n insert: WildcardInsertHandler[]\n update: WildcardUpdateHandler[]\n delete: WildcardDeleteHandler[]\n}\n\n// ---------------------------------------------------------------------------\n// PD4Client\n// ---------------------------------------------------------------------------\n\nexport class PD4Client {\n readonly url: string\n readonly db: string\n\n private EventSourceCtor: (new (url: string) => EventSource) | null\n private eventSource: EventSource | null = null\n private handlers = new Map<string, TableHandlers>()\n private wildcardHandlers: WildcardHandlers = { insert: [], update: [], delete: [] }\n private anyHandlers: AnyEventHandler[] = []\n private statusHandlers: StatusHandler[] = []\n private _status: ConnectionStatus = 'connecting'\n\n constructor(url: string, db: string, options?: PD4ClientOptions) {\n this.url = url.replace(/\\/$/, '')\n this.db = db\n this.EventSourceCtor = options?.EventSource\n ?? (typeof globalThis !== 'undefined' && globalThis.EventSource\n ? globalThis.EventSource\n : null)\n }\n\n // --- URL helpers -------------------------------------------------------\n\n private get v1() {\n return `${this.url}/v1/${this.db}`\n }\n\n private get api() {\n return `${this.url}/api/${this.db}`\n }\n\n // --- HTTP helper -------------------------------------------------------\n\n private async request<T>(url: string, init?: RequestInit): Promise<T> {\n const res = await fetch(url, {\n ...init,\n headers: { 'Content-Type': 'application/json', ...init?.headers },\n })\n if (!res.ok) {\n const text = await res.text()\n throw new PD4Error(\n `PD4 ${init?.method ?? 'GET'} ${url}: ${res.status} ${text}`,\n res.status,\n url,\n )\n }\n if (res.status === 204) return undefined as T\n return res.json()\n }\n\n // --- Health ------------------------------------------------------------\n\n /** Check if PD4 is reachable */\n async ping(): Promise<boolean> {\n try {\n const res = await fetch(`${this.url}/health`)\n return res.ok\n } catch {\n return false\n }\n }\n\n // --- Database ----------------------------------------------------------\n\n /** Ensure the database is open */\n async ensureDatabase(): Promise<void> {\n await this.request(`${this.url}/v1/db`, {\n method: 'POST',\n body: JSON.stringify({ name: this.db }),\n })\n }\n\n // --- Tables ------------------------------------------------------------\n\n /** List all table names */\n async listTables(): Promise<string[]> {\n const data = await this.request<{ tables: string[] }>(`${this.v1}/tables`)\n return data.tables\n }\n\n /** Get table info */\n async getTable(name: string): Promise<TableInfo> {\n return this.request<TableInfo>(`${this.v1}/tables/${name}`)\n }\n\n /** Create a table (with auto_pk) */\n async createTable(name: string): Promise<void> {\n await this.request(`${this.v1}/tables`, {\n method: 'POST',\n body: JSON.stringify({ name, auto_pk: true }),\n })\n }\n\n /** Drop a table */\n async dropTable(name: string): Promise<void> {\n await this.request(`${this.v1}/tables/${name}`, { method: 'DELETE' })\n }\n\n /** Add a column to a table */\n async addColumn(table: string, name: string, dtype: string): Promise<void> {\n await this.request(`${this.v1}/tables/${table}/columns`, {\n method: 'POST',\n body: JSON.stringify({ name, dtype }),\n })\n }\n\n // --- Rows (flat objects in, flat objects out) ---------------------------\n\n /** Insert one or more rows (flat objects). Returns keys and count. */\n async insert(table: string, rows: Row | Row[]): Promise<InsertResult> {\n const arr = Array.isArray(rows) ? rows : [rows]\n const { columns, rows: colRows } = toColumnar(arr)\n return this.request<InsertResult>(`${this.v1}/tables/${table}/rows`, {\n method: 'POST',\n body: JSON.stringify({ columns, rows: colRows }),\n })\n }\n\n /** Update a single row by key with a flat object of changed columns. */\n async update(table: string, key: number, values: Row): Promise<void> {\n const pairs = toColumnValues(values)\n await this.request(`${this.v1}/views/${table}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values: pairs }),\n })\n }\n\n /** Delete rows by keys. */\n async delete(table: string, keys: number | number[]): Promise<void> {\n const arr = Array.isArray(keys) ? keys : [keys]\n await this.request(`${this.v1}/views/${table}/rows/delete`, {\n method: 'POST',\n body: JSON.stringify({ keys: arr }),\n })\n }\n\n /** Fetch rows from a table/view. Returns flat Row objects. */\n async fetch(table: string, opts?: FetchOptions): Promise<FetchResult> {\n const offset = opts?.offset ?? 0\n const limit = opts?.limit ?? 1000\n const data = await this.request<{ rows: PD4Row[]; total: number }>(\n `${this.v1}/views/${table}/rows?offset=${offset}&limit=${limit}`,\n )\n return {\n rows: data.rows.map(flattenRow),\n total: data.total,\n }\n }\n\n // --- Raw row access (PD4 wire format) ----------------------------------\n\n /** Insert rows in columnar format (low-level). */\n async insertColumnar(\n table: string,\n columns: string[],\n rows: (string | number | boolean | null)[][],\n ): Promise<InsertResult> {\n return this.request<InsertResult>(`${this.v1}/tables/${table}/rows`, {\n method: 'POST',\n body: JSON.stringify({ columns, rows }),\n })\n }\n\n /** Update a row by key with column-value pairs (low-level). */\n async updateRaw(\n table: string,\n key: number,\n values: { column: string; value: string | number | boolean | null }[],\n ): Promise<void> {\n await this.request(`${this.v1}/views/${table}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values }),\n })\n }\n\n /** Fetch rows in PD4 wire format (low-level). */\n async fetchRaw(\n table: string,\n offset = 0,\n limit = 1000,\n ): Promise<{ rows: PD4Row[]; total: number }> {\n return this.request(`${this.v1}/views/${table}/rows?offset=${offset}&limit=${limit}`)\n }\n\n // --- Buffer ------------------------------------------------------------\n\n /** Create a buffered view wrapping a table. */\n async buffer(table: string): Promise<PD4Buffer> {\n const info = await this.request<{ handle: string; source: string; row_count: number }>(\n `${this.v1}/views/${table}/buffer`,\n { method: 'POST' },\n )\n return new PD4Buffer(this, info.handle, table)\n }\n\n // --- SSE ---------------------------------------------------------------\n\n /** Get current SSE connection status. */\n get status(): ConnectionStatus {\n return this._status\n }\n\n /** Connect to the SSE event stream. Called automatically on first on(). */\n connect(): void {\n if (this.eventSource) return\n if (!this.EventSourceCtor) {\n throw new Error(\n 'EventSource is not available. In Node.js, pass { EventSource } from the \"eventsource\" package to the PD4Client constructor.',\n )\n }\n\n this._setStatus('connecting')\n const sse = new this.EventSourceCtor(`${this.api}/events`)\n this.eventSource = sse\n\n sse.addEventListener('session', () => {\n this._setStatus('connected')\n })\n\n sse.addEventListener('insert', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n const newValues: Row[] = data.new_values ?? []\n this._dispatch(table, 'insert', keys, newValues, [])\n })\n\n sse.addEventListener('update', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n const newValues: Row[] = data.new_values ?? []\n const oldValues: Row[] = data.old_values ?? []\n this._dispatch(table, 'update', keys, newValues, oldValues)\n })\n\n sse.addEventListener('delete', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n this._dispatch(table, 'delete', keys, [], [])\n })\n\n sse.onerror = () => {\n this._setStatus('error')\n }\n }\n\n /** Disconnect from the SSE event stream. */\n disconnect(): void {\n this.eventSource?.close()\n this.eventSource = null\n this._setStatus('connecting')\n }\n\n /**\n * Subscribe to SSE events for a specific table, or use '*' for all tables.\n * Auto-connects on first call. Returns an unsubscribe function.\n *\n * Table-specific: handler receives (keys, rows/newValues/oldValues)\n * Wildcard '*': handler receives (table, keys, rows/newValues/oldValues)\n */\n on(table: '*', event: 'insert', handler: WildcardInsertHandler): () => void\n on(table: '*', event: 'update', handler: WildcardUpdateHandler): () => void\n on(table: '*', event: 'delete', handler: WildcardDeleteHandler): () => void\n on(table: string, event: 'insert', handler: InsertHandler): () => void\n on(table: string, event: 'update', handler: UpdateHandler): () => void\n on(table: string, event: 'delete', handler: DeleteHandler): () => void\n on(\n table: string,\n event: 'insert' | 'update' | 'delete',\n handler:\n | InsertHandler | UpdateHandler | DeleteHandler\n | WildcardInsertHandler | WildcardUpdateHandler | WildcardDeleteHandler,\n ): () => void {\n // Auto-connect\n if (!this.eventSource) this.connect()\n\n // Wildcard: register in wildcardHandlers\n if (table === '*') {\n const list = this.wildcardHandlers[event] as unknown[]\n list.push(handler)\n return () => {\n const idx = list.indexOf(handler)\n if (idx >= 0) list.splice(idx, 1)\n }\n }\n\n // Table-specific\n let tableHandlers = this.handlers.get(table)\n if (!tableHandlers) {\n tableHandlers = { insert: [], update: [], delete: [] }\n this.handlers.set(table, tableHandlers)\n }\n\n const list = tableHandlers[event] as unknown[]\n list.push(handler)\n\n return () => {\n const idx = list.indexOf(handler)\n if (idx >= 0) list.splice(idx, 1)\n }\n }\n\n /**\n * Subscribe to all SSE events on all tables.\n * Handler receives (table, event, keys, newValues, oldValues).\n * Auto-connects on first call. Returns an unsubscribe function.\n */\n onAny(handler: AnyEventHandler): () => void {\n if (!this.eventSource) this.connect()\n\n this.anyHandlers.push(handler)\n return () => {\n const idx = this.anyHandlers.indexOf(handler)\n if (idx >= 0) this.anyHandlers.splice(idx, 1)\n }\n }\n\n /** Subscribe to connection status changes. Returns an unsubscribe function. */\n onStatus(handler: StatusHandler): () => void {\n this.statusHandlers.push(handler)\n return () => {\n const idx = this.statusHandlers.indexOf(handler)\n if (idx >= 0) this.statusHandlers.splice(idx, 1)\n }\n }\n\n // --- SSE internals -----------------------------------------------------\n\n private _setStatus(status: ConnectionStatus) {\n this._status = status\n for (const handler of this.statusHandlers) {\n handler(status)\n }\n }\n\n private _dispatch(\n table: string,\n event: 'insert' | 'update' | 'delete',\n keys: number[],\n newValues: Row[],\n oldValues: Row[],\n ) {\n // Table-specific handlers\n const tableHandlers = this.handlers.get(table)\n if (tableHandlers) {\n if (event === 'insert') {\n for (const h of tableHandlers.insert) h(keys, newValues)\n } else if (event === 'update') {\n for (const h of tableHandlers.update) h(keys, newValues, oldValues)\n } else {\n for (const h of tableHandlers.delete) h(keys)\n }\n }\n\n // Wildcard handlers (on('*', ...))\n if (event === 'insert') {\n for (const h of this.wildcardHandlers.insert) h(table, keys, newValues)\n } else if (event === 'update') {\n for (const h of this.wildcardHandlers.update) h(table, keys, newValues, oldValues)\n } else {\n for (const h of this.wildcardHandlers.delete) h(table, keys)\n }\n\n // onAny handlers\n for (const h of this.anyHandlers) {\n h(table, event, keys, newValues, oldValues)\n }\n }\n}\n\n// Re-export for convenience\nexport type {\n ColumnInfo, TableInfo, PD4Row, Row, FetchOptions, FetchResult, InsertResult,\n WildcardInsertHandler, WildcardUpdateHandler, WildcardDeleteHandler, AnyEventHandler,\n}\n"],"mappings":"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","names":["list"]}
@@ -113,6 +113,8 @@ var PD4Client = class {
113
113
  constructor(url, db, options) {
114
114
  this.eventSource = null;
115
115
  this.handlers = /* @__PURE__ */ new Map();
116
+ this.wildcardHandlers = { insert: [], update: [], delete: [] };
117
+ this.anyHandlers = [];
116
118
  this.statusHandlers = [];
117
119
  this._status = "connecting";
118
120
  this.url = url.replace(/\/$/, "");
@@ -307,6 +309,14 @@ var PD4Client = class {
307
309
  }
308
310
  on(table, event, handler) {
309
311
  if (!this.eventSource) this.connect();
312
+ if (table === "*") {
313
+ const list2 = this.wildcardHandlers[event];
314
+ list2.push(handler);
315
+ return () => {
316
+ const idx = list2.indexOf(handler);
317
+ if (idx >= 0) list2.splice(idx, 1);
318
+ };
319
+ }
310
320
  let tableHandlers = this.handlers.get(table);
311
321
  if (!tableHandlers) {
312
322
  tableHandlers = { insert: [], update: [], delete: [] };
@@ -319,6 +329,19 @@ var PD4Client = class {
319
329
  if (idx >= 0) list.splice(idx, 1);
320
330
  };
321
331
  }
332
+ /**
333
+ * Subscribe to all SSE events on all tables.
334
+ * Handler receives (table, event, keys, newValues, oldValues).
335
+ * Auto-connects on first call. Returns an unsubscribe function.
336
+ */
337
+ onAny(handler) {
338
+ if (!this.eventSource) this.connect();
339
+ this.anyHandlers.push(handler);
340
+ return () => {
341
+ const idx = this.anyHandlers.indexOf(handler);
342
+ if (idx >= 0) this.anyHandlers.splice(idx, 1);
343
+ };
344
+ }
322
345
  /** Subscribe to connection status changes. Returns an unsubscribe function. */
323
346
  onStatus(handler) {
324
347
  this.statusHandlers.push(handler);
@@ -336,13 +359,24 @@ var PD4Client = class {
336
359
  }
337
360
  _dispatch(table, event, keys, newValues, oldValues) {
338
361
  const tableHandlers = this.handlers.get(table);
339
- if (!tableHandlers) return;
362
+ if (tableHandlers) {
363
+ if (event === "insert") {
364
+ for (const h of tableHandlers.insert) h(keys, newValues);
365
+ } else if (event === "update") {
366
+ for (const h of tableHandlers.update) h(keys, newValues, oldValues);
367
+ } else {
368
+ for (const h of tableHandlers.delete) h(keys);
369
+ }
370
+ }
340
371
  if (event === "insert") {
341
- for (const h of tableHandlers.insert) h(keys, newValues);
372
+ for (const h of this.wildcardHandlers.insert) h(table, keys, newValues);
342
373
  } else if (event === "update") {
343
- for (const h of tableHandlers.update) h(keys, newValues, oldValues);
374
+ for (const h of this.wildcardHandlers.update) h(table, keys, newValues, oldValues);
344
375
  } else {
345
- for (const h of tableHandlers.delete) h(keys);
376
+ for (const h of this.wildcardHandlers.delete) h(table, keys);
377
+ }
378
+ for (const h of this.anyHandlers) {
379
+ h(table, event, keys, newValues, oldValues);
346
380
  }
347
381
  }
348
382
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/index.ts","../../src/react/context.tsx","../../src/errors.ts","../../src/utils.ts","../../src/buffer.ts","../../src/client.ts","../../src/react/usePD4Subscribe.ts","../../src/react/usePD4Table.ts","../../src/react/usePD4Query.ts"],"sourcesContent":["export { PD4Provider, usePD4 } from './context'\nexport type { PD4ProviderProps } from './context'\nexport { usePD4Subscribe } from './usePD4Subscribe'\nexport { usePD4Table } from './usePD4Table'\nexport type { UsePD4TableOptions, UsePD4TableResult } from './usePD4Table'\nexport { usePD4Query } from './usePD4Query'\nexport type { UsePD4QueryResult } from './usePD4Query'\n","import { createContext, useContext, useRef } from 'react'\nimport { PD4Client } from '../client'\n\nconst PD4Context = createContext<PD4Client | null>(null)\n\nexport interface PD4ProviderProps {\n url: string\n db: string\n children: React.ReactNode\n}\n\nexport function PD4Provider({ url, db, children }: PD4ProviderProps) {\n const clientRef = useRef<PD4Client | null>(null)\n if (!clientRef.current) {\n clientRef.current = new PD4Client(url, db)\n }\n return <PD4Context.Provider value={clientRef.current}>{children}</PD4Context.Provider>\n}\n\nexport function usePD4(): PD4Client {\n const client = useContext(PD4Context)\n if (!client) {\n throw new Error('usePD4() must be used within a <PD4Provider>')\n }\n return client\n}\n","/** Base error class for PD4 client errors */\nexport class PD4Error extends Error {\n readonly status: number | undefined\n readonly url: string | undefined\n\n constructor(message: string, status?: number, url?: string) {\n super(message)\n this.name = 'PD4Error'\n this.status = status\n this.url = url\n }\n}\n","import type { PD4Row, Row } from './types'\n\n/** Convert PD4 row format to flat record */\nexport function flattenRow(row: PD4Row): Row {\n const flat: Row = { _pk: row.key }\n for (const cv of row.values) {\n flat[cv.column] = cv.value\n }\n return flat\n}\n\n/**\n * Convert flat row objects to PD4's columnar insert format.\n * Returns { columns, rows } suitable for the insert endpoint.\n */\nexport function toColumnar(\n flatRows: Row[],\n columnNames?: string[],\n): { columns: string[]; rows: (string | number | boolean | null)[][] } {\n if (flatRows.length === 0) return { columns: [], rows: [] }\n const cols = columnNames ?? Object.keys(flatRows[0]).filter((k) => k !== '_pk')\n const rows = flatRows.map((row) => cols.map((c) => row[c] ?? null))\n return { columns: cols, rows }\n}\n\n/**\n * Convert a flat row object to PD4's column-value pairs for update.\n * Filters out the _pk field.\n */\nexport function toColumnValues(\n flat: Row,\n): { column: string; value: string | number | boolean | null }[] {\n return Object.entries(flat)\n .filter(([k]) => k !== '_pk')\n .map(([column, value]) => ({ column, value }))\n}\n","import type { PD4Client } from './client'\nimport { flattenRow, toColumnValues } from './utils'\nimport type { Row, FetchResult, PD4Row } from './types'\n\n/**\n * A buffered view wrapping a PD4 table.\n * Changes are accumulated locally and only become visible\n * to other clients after commit().\n */\nexport class PD4Buffer {\n private client: PD4Client\n readonly handle: string\n readonly table: string\n\n constructor(client: PD4Client, handle: string, table: string) {\n this.client = client\n this.handle = handle\n this.table = table\n }\n\n /** Update a row within the buffer (not visible to others until commit). */\n async update(key: number, values: Row): Promise<void> {\n const pairs = toColumnValues(values)\n await this._request(`views/${this.handle}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values: pairs }),\n })\n }\n\n /** Fetch rows from the buffer (sees uncommitted changes). */\n async fetch(offset = 0, limit = 100): Promise<FetchResult> {\n const data = await this._request<{ rows: PD4Row[]; total: number }>(\n `views/${this.handle}/rows?offset=${offset}&limit=${limit}`,\n )\n return {\n rows: data.rows.map(flattenRow),\n total: data.total,\n }\n }\n\n /** Commit all buffered writes atomically. Fires SSE events. */\n async commit(): Promise<void> {\n await this._request(`views/${this.handle}/commit`, { method: 'POST' })\n }\n\n /** Rollback all buffered writes (discard pending changes). */\n async rollback(): Promise<void> {\n await this._request(`views/${this.handle}/rollback`, { method: 'POST' })\n }\n\n // --- Internal ----------------------------------------------------------\n\n private async _request<T>(path: string, init?: RequestInit): Promise<T> {\n const url = `${this.client.url}/v1/${this.client.db}/${path}`\n const res = await fetch(url, {\n ...init,\n headers: { 'Content-Type': 'application/json', ...init?.headers },\n })\n if (!res.ok) {\n const text = await res.text()\n throw new Error(`PD4 Buffer ${init?.method ?? 'GET'} ${url}: ${res.status} ${text}`)\n }\n if (res.status === 204) return undefined as T\n return res.json()\n }\n}\n","import { PD4Error } from './errors'\nimport { flattenRow, toColumnar, toColumnValues } from './utils'\nimport { PD4Buffer } from './buffer'\nimport type {\n ColumnInfo,\n TableInfo,\n PD4Row,\n Row,\n FetchOptions,\n FetchResult,\n InsertResult,\n ConnectionStatus,\n InsertHandler,\n UpdateHandler,\n DeleteHandler,\n StatusHandler,\n PD4ClientOptions,\n} from './types'\n\n// ---------------------------------------------------------------------------\n// Internal types for SSE handler registry\n// ---------------------------------------------------------------------------\n\ninterface TableHandlers {\n insert: InsertHandler[]\n update: UpdateHandler[]\n delete: DeleteHandler[]\n}\n\n// ---------------------------------------------------------------------------\n// PD4Client\n// ---------------------------------------------------------------------------\n\nexport class PD4Client {\n readonly url: string\n readonly db: string\n\n private EventSourceCtor: (new (url: string) => EventSource) | null\n private eventSource: EventSource | null = null\n private handlers = new Map<string, TableHandlers>()\n private statusHandlers: StatusHandler[] = []\n private _status: ConnectionStatus = 'connecting'\n\n constructor(url: string, db: string, options?: PD4ClientOptions) {\n this.url = url.replace(/\\/$/, '')\n this.db = db\n this.EventSourceCtor = options?.EventSource\n ?? (typeof globalThis !== 'undefined' && globalThis.EventSource\n ? globalThis.EventSource\n : null)\n }\n\n // --- URL helpers -------------------------------------------------------\n\n private get v1() {\n return `${this.url}/v1/${this.db}`\n }\n\n private get api() {\n return `${this.url}/api/${this.db}`\n }\n\n // --- HTTP helper -------------------------------------------------------\n\n private async request<T>(url: string, init?: RequestInit): Promise<T> {\n const res = await fetch(url, {\n ...init,\n headers: { 'Content-Type': 'application/json', ...init?.headers },\n })\n if (!res.ok) {\n const text = await res.text()\n throw new PD4Error(\n `PD4 ${init?.method ?? 'GET'} ${url}: ${res.status} ${text}`,\n res.status,\n url,\n )\n }\n if (res.status === 204) return undefined as T\n return res.json()\n }\n\n // --- Health ------------------------------------------------------------\n\n /** Check if PD4 is reachable */\n async ping(): Promise<boolean> {\n try {\n const res = await fetch(`${this.url}/health`)\n return res.ok\n } catch {\n return false\n }\n }\n\n // --- Database ----------------------------------------------------------\n\n /** Ensure the database is open */\n async ensureDatabase(): Promise<void> {\n await this.request(`${this.url}/v1/db`, {\n method: 'POST',\n body: JSON.stringify({ name: this.db }),\n })\n }\n\n // --- Tables ------------------------------------------------------------\n\n /** List all table names */\n async listTables(): Promise<string[]> {\n const data = await this.request<{ tables: string[] }>(`${this.v1}/tables`)\n return data.tables\n }\n\n /** Get table info */\n async getTable(name: string): Promise<TableInfo> {\n return this.request<TableInfo>(`${this.v1}/tables/${name}`)\n }\n\n /** Create a table (with auto_pk) */\n async createTable(name: string): Promise<void> {\n await this.request(`${this.v1}/tables`, {\n method: 'POST',\n body: JSON.stringify({ name, auto_pk: true }),\n })\n }\n\n /** Drop a table */\n async dropTable(name: string): Promise<void> {\n await this.request(`${this.v1}/tables/${name}`, { method: 'DELETE' })\n }\n\n /** Add a column to a table */\n async addColumn(table: string, name: string, dtype: string): Promise<void> {\n await this.request(`${this.v1}/tables/${table}/columns`, {\n method: 'POST',\n body: JSON.stringify({ name, dtype }),\n })\n }\n\n // --- Rows (flat objects in, flat objects out) ---------------------------\n\n /** Insert one or more rows (flat objects). Returns keys and count. */\n async insert(table: string, rows: Row | Row[]): Promise<InsertResult> {\n const arr = Array.isArray(rows) ? rows : [rows]\n const { columns, rows: colRows } = toColumnar(arr)\n return this.request<InsertResult>(`${this.v1}/tables/${table}/rows`, {\n method: 'POST',\n body: JSON.stringify({ columns, rows: colRows }),\n })\n }\n\n /** Update a single row by key with a flat object of changed columns. */\n async update(table: string, key: number, values: Row): Promise<void> {\n const pairs = toColumnValues(values)\n await this.request(`${this.v1}/views/${table}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values: pairs }),\n })\n }\n\n /** Delete rows by keys. */\n async delete(table: string, keys: number | number[]): Promise<void> {\n const arr = Array.isArray(keys) ? keys : [keys]\n await this.request(`${this.v1}/views/${table}/rows/delete`, {\n method: 'POST',\n body: JSON.stringify({ keys: arr }),\n })\n }\n\n /** Fetch rows from a table/view. Returns flat Row objects. */\n async fetch(table: string, opts?: FetchOptions): Promise<FetchResult> {\n const offset = opts?.offset ?? 0\n const limit = opts?.limit ?? 1000\n const data = await this.request<{ rows: PD4Row[]; total: number }>(\n `${this.v1}/views/${table}/rows?offset=${offset}&limit=${limit}`,\n )\n return {\n rows: data.rows.map(flattenRow),\n total: data.total,\n }\n }\n\n // --- Raw row access (PD4 wire format) ----------------------------------\n\n /** Insert rows in columnar format (low-level). */\n async insertColumnar(\n table: string,\n columns: string[],\n rows: (string | number | boolean | null)[][],\n ): Promise<InsertResult> {\n return this.request<InsertResult>(`${this.v1}/tables/${table}/rows`, {\n method: 'POST',\n body: JSON.stringify({ columns, rows }),\n })\n }\n\n /** Update a row by key with column-value pairs (low-level). */\n async updateRaw(\n table: string,\n key: number,\n values: { column: string; value: string | number | boolean | null }[],\n ): Promise<void> {\n await this.request(`${this.v1}/views/${table}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values }),\n })\n }\n\n /** Fetch rows in PD4 wire format (low-level). */\n async fetchRaw(\n table: string,\n offset = 0,\n limit = 1000,\n ): Promise<{ rows: PD4Row[]; total: number }> {\n return this.request(`${this.v1}/views/${table}/rows?offset=${offset}&limit=${limit}`)\n }\n\n // --- Buffer ------------------------------------------------------------\n\n /** Create a buffered view wrapping a table. */\n async buffer(table: string): Promise<PD4Buffer> {\n const info = await this.request<{ handle: string; source: string; row_count: number }>(\n `${this.v1}/views/${table}/buffer`,\n { method: 'POST' },\n )\n return new PD4Buffer(this, info.handle, table)\n }\n\n // --- SSE ---------------------------------------------------------------\n\n /** Get current SSE connection status. */\n get status(): ConnectionStatus {\n return this._status\n }\n\n /** Connect to the SSE event stream. Called automatically on first on(). */\n connect(): void {\n if (this.eventSource) return\n if (!this.EventSourceCtor) {\n throw new Error(\n 'EventSource is not available. In Node.js, pass { EventSource } from the \"eventsource\" package to the PD4Client constructor.',\n )\n }\n\n this._setStatus('connecting')\n const sse = new this.EventSourceCtor(`${this.api}/events`)\n this.eventSource = sse\n\n sse.addEventListener('session', () => {\n this._setStatus('connected')\n })\n\n sse.addEventListener('insert', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n const newValues: Row[] = data.new_values ?? []\n this._dispatch(table, 'insert', keys, newValues, [])\n })\n\n sse.addEventListener('update', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n const newValues: Row[] = data.new_values ?? []\n const oldValues: Row[] = data.old_values ?? []\n this._dispatch(table, 'update', keys, newValues, oldValues)\n })\n\n sse.addEventListener('delete', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n this._dispatch(table, 'delete', keys, [], [])\n })\n\n sse.onerror = () => {\n this._setStatus('error')\n }\n }\n\n /** Disconnect from the SSE event stream. */\n disconnect(): void {\n this.eventSource?.close()\n this.eventSource = null\n this._setStatus('connecting')\n }\n\n /**\n * Subscribe to SSE events for a table.\n * Auto-connects on first call. Returns an unsubscribe function.\n */\n on(table: string, event: 'insert', handler: InsertHandler): () => void\n on(table: string, event: 'update', handler: UpdateHandler): () => void\n on(table: string, event: 'delete', handler: DeleteHandler): () => void\n on(\n table: string,\n event: 'insert' | 'update' | 'delete',\n handler: InsertHandler | UpdateHandler | DeleteHandler,\n ): () => void {\n // Auto-connect\n if (!this.eventSource) this.connect()\n\n let tableHandlers = this.handlers.get(table)\n if (!tableHandlers) {\n tableHandlers = { insert: [], update: [], delete: [] }\n this.handlers.set(table, tableHandlers)\n }\n\n const list = tableHandlers[event] as unknown[]\n list.push(handler)\n\n return () => {\n const idx = list.indexOf(handler)\n if (idx >= 0) list.splice(idx, 1)\n }\n }\n\n /** Subscribe to connection status changes. Returns an unsubscribe function. */\n onStatus(handler: StatusHandler): () => void {\n this.statusHandlers.push(handler)\n return () => {\n const idx = this.statusHandlers.indexOf(handler)\n if (idx >= 0) this.statusHandlers.splice(idx, 1)\n }\n }\n\n // --- SSE internals -----------------------------------------------------\n\n private _setStatus(status: ConnectionStatus) {\n this._status = status\n for (const handler of this.statusHandlers) {\n handler(status)\n }\n }\n\n private _dispatch(\n table: string,\n event: 'insert' | 'update' | 'delete',\n keys: number[],\n newValues: Row[],\n oldValues: Row[],\n ) {\n const tableHandlers = this.handlers.get(table)\n if (!tableHandlers) return\n\n if (event === 'insert') {\n for (const h of tableHandlers.insert) h(keys, newValues)\n } else if (event === 'update') {\n for (const h of tableHandlers.update) h(keys, newValues, oldValues)\n } else {\n for (const h of tableHandlers.delete) h(keys)\n }\n }\n}\n\n// Re-export for convenience\nexport type { ColumnInfo, TableInfo, PD4Row, Row, FetchOptions, FetchResult, InsertResult }\n","import { useEffect, useRef } from 'react'\nimport { usePD4 } from './context'\nimport type { InsertHandler, UpdateHandler, DeleteHandler } from '../types'\n\n/**\n * Subscribe to SSE events for a table. Auto-cleans up on unmount.\n *\n * Usage:\n * usePD4Subscribe('metrics', 'insert', (keys, rows) => { ... })\n * usePD4Subscribe('metrics', 'update', (keys, newVals, oldVals) => { ... })\n * usePD4Subscribe('metrics', 'delete', (keys) => { ... })\n */\nexport function usePD4Subscribe(table: string, event: 'insert', handler: InsertHandler): void\nexport function usePD4Subscribe(table: string, event: 'update', handler: UpdateHandler): void\nexport function usePD4Subscribe(table: string, event: 'delete', handler: DeleteHandler): void\nexport function usePD4Subscribe(\n table: string,\n event: 'insert' | 'update' | 'delete',\n handler: InsertHandler | UpdateHandler | DeleteHandler,\n): void {\n const db = usePD4()\n const handlerRef = useRef(handler)\n handlerRef.current = handler\n\n useEffect(() => {\n const wrapper = (...args: unknown[]) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ;(handlerRef.current as any)(...args)\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return db.on(table, event as any, wrapper as any)\n }, [db, table, event])\n}\n","import { useState, useEffect, useCallback, useRef } from 'react'\nimport { usePD4 } from './context'\nimport type { Row, ColumnInfo, ConnectionStatus } from '../types'\n\nconst HIGHLIGHT_MS = 1500\n\nexport interface UsePD4TableOptions {\n limit?: number\n deleteDelay?: number\n}\n\nexport interface UsePD4TableResult {\n rows: Row[]\n total: number\n columns: ColumnInfo[]\n status: ConnectionStatus\n highlights: Record<number, string>\n}\n\n/**\n * Full table state management hook.\n * Handles: initial fetch, SSE subscriptions for insert/update/delete,\n * row state management, highlight tracking, and buffer-commit edge cases.\n */\nexport function usePD4Table(\n table: string,\n opts?: UsePD4TableOptions,\n): UsePD4TableResult {\n const db = usePD4()\n const limit = opts?.limit ?? 1000\n const deleteDelay = opts?.deleteDelay ?? HIGHLIGHT_MS\n\n const [rows, setRows] = useState<Row[]>([])\n const [total, setTotal] = useState(0)\n const [columns, setColumns] = useState<ColumnInfo[]>([])\n const [status, setStatus] = useState<ConnectionStatus>('connecting')\n const [highlights, setHighlights] = useState<Record<number, string>>({})\n\n // Refs for stable callback access\n const tableRef = useRef(table)\n tableRef.current = table\n\n const highlightKeys = useCallback((keys: number[], type: string) => {\n if (keys.length === 0) return\n setHighlights((prev) => {\n const next = { ...prev }\n for (const k of keys) next[k] = type\n return next\n })\n setTimeout(() => {\n setHighlights((prev) => {\n const next = { ...prev }\n for (const k of keys) {\n if (next[k] === type) delete next[k]\n }\n return next\n })\n }, HIGHLIGHT_MS)\n }, [])\n\n const fetchRows = useCallback(async () => {\n try {\n const { rows: fetched, total: t } = await db.fetch(tableRef.current, { offset: 0, limit })\n setRows(fetched)\n setTotal(t)\n } catch {\n // Non-fatal\n }\n }, [db, limit])\n\n useEffect(() => {\n let cancelled = false\n const unsubs: (() => void)[] = []\n\n ;(async () => {\n try {\n // 1. Get column info\n const info = await db.getTable(table)\n if (cancelled) return\n setColumns(info.columns.filter((c) => c.name !== '_pk'))\n\n // 2. Fetch initial rows\n await fetchRows()\n if (cancelled) return\n setStatus('connected')\n\n // 3. SSE subscriptions\n unsubs.push(db.on(table, 'insert', (keys, newValues) => {\n if (cancelled) return\n const newRows = keys.map((k, i) => ({ _pk: k, ...newValues[i] }))\n setRows((prev) => [...prev, ...newRows])\n setTotal((prev) => prev + newRows.length)\n highlightKeys(keys, 'insert')\n }))\n\n unsubs.push(db.on(table, 'update', (keys, newValues) => {\n if (cancelled) return\n\n // Buffer commits send raw scalar values, not flat objects — fall back to refetch\n if (\n newValues.length === 0 ||\n typeof newValues[0] !== 'object' ||\n newValues[0] === null\n ) {\n fetchRows()\n highlightKeys(keys, 'update')\n return\n }\n\n const updateMap = new Map(keys.map((k, i) => [k, newValues[i]]))\n setRows((prev) =>\n prev.map((row) => {\n const changes = updateMap.get(row._pk as number)\n if (!changes) return row\n return { ...row, ...changes }\n }),\n )\n highlightKeys(keys, 'update')\n }))\n\n unsubs.push(db.on(table, 'delete', (keys) => {\n if (cancelled) return\n const keySet = new Set(keys)\n highlightKeys(keys, 'delete')\n setTimeout(() => {\n setRows((prev) => prev.filter((row) => !keySet.has(row._pk as number)))\n setTotal((prev) => Math.max(0, prev - keys.length))\n }, deleteDelay)\n }))\n } catch {\n if (!cancelled) {\n setStatus('error')\n }\n }\n })()\n\n return () => {\n cancelled = true\n for (const unsub of unsubs) unsub()\n }\n }, [db, table, limit, fetchRows, highlightKeys, deleteDelay])\n\n return { rows, total, columns, status, highlights }\n}\n","import { useState, useEffect } from 'react'\nimport { usePD4 } from './context'\nimport type { Row } from '../types'\n\nexport interface UsePD4QueryResult {\n rows: Row[]\n total: number\n loading: boolean\n error: string | null\n refetch: () => void\n}\n\n/**\n * One-shot query hook. Fetches rows from a table once (or on refetch).\n */\nexport function usePD4Query(\n table: string,\n offset = 0,\n limit = 1000,\n): UsePD4QueryResult {\n const db = usePD4()\n const [rows, setRows] = useState<Row[]>([])\n const [total, setTotal] = useState(0)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n const [attempt, setAttempt] = useState(0)\n\n useEffect(() => {\n let cancelled = false\n setLoading(true)\n setError(null)\n\n db.fetch(table, { offset, limit })\n .then((result) => {\n if (cancelled) return\n setRows(result.rows)\n setTotal(result.total)\n setLoading(false)\n })\n .catch((err) => {\n if (cancelled) return\n setError(err instanceof Error ? err.message : 'Query failed')\n setLoading(false)\n })\n\n return () => { cancelled = true }\n }, [db, table, offset, limit, attempt])\n\n return {\n rows,\n total,\n loading,\n error,\n refetch: () => setAttempt((a) => a + 1),\n }\n}\n"],"mappings":"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","names":["import_react","import_react","import_react"]}
1
+ {"version":3,"sources":["../../src/react/index.ts","../../src/react/context.tsx","../../src/errors.ts","../../src/utils.ts","../../src/buffer.ts","../../src/client.ts","../../src/react/usePD4Subscribe.ts","../../src/react/usePD4Table.ts","../../src/react/usePD4Query.ts"],"sourcesContent":["export { PD4Provider, usePD4 } from './context'\nexport type { PD4ProviderProps } from './context'\nexport { usePD4Subscribe } from './usePD4Subscribe'\nexport { usePD4Table } from './usePD4Table'\nexport type { UsePD4TableOptions, UsePD4TableResult } from './usePD4Table'\nexport { usePD4Query } from './usePD4Query'\nexport type { UsePD4QueryResult } from './usePD4Query'\n","import { createContext, useContext, useRef } from 'react'\nimport { PD4Client } from '../client'\n\nconst PD4Context = createContext<PD4Client | null>(null)\n\nexport interface PD4ProviderProps {\n url: string\n db: string\n children: React.ReactNode\n}\n\nexport function PD4Provider({ url, db, children }: PD4ProviderProps) {\n const clientRef = useRef<PD4Client | null>(null)\n if (!clientRef.current) {\n clientRef.current = new PD4Client(url, db)\n }\n return <PD4Context.Provider value={clientRef.current}>{children}</PD4Context.Provider>\n}\n\nexport function usePD4(): PD4Client {\n const client = useContext(PD4Context)\n if (!client) {\n throw new Error('usePD4() must be used within a <PD4Provider>')\n }\n return client\n}\n","/** Base error class for PD4 client errors */\nexport class PD4Error extends Error {\n readonly status: number | undefined\n readonly url: string | undefined\n\n constructor(message: string, status?: number, url?: string) {\n super(message)\n this.name = 'PD4Error'\n this.status = status\n this.url = url\n }\n}\n","import type { PD4Row, Row } from './types'\n\n/** Convert PD4 row format to flat record */\nexport function flattenRow(row: PD4Row): Row {\n const flat: Row = { _pk: row.key }\n for (const cv of row.values) {\n flat[cv.column] = cv.value\n }\n return flat\n}\n\n/**\n * Convert flat row objects to PD4's columnar insert format.\n * Returns { columns, rows } suitable for the insert endpoint.\n */\nexport function toColumnar(\n flatRows: Row[],\n columnNames?: string[],\n): { columns: string[]; rows: (string | number | boolean | null)[][] } {\n if (flatRows.length === 0) return { columns: [], rows: [] }\n const cols = columnNames ?? Object.keys(flatRows[0]).filter((k) => k !== '_pk')\n const rows = flatRows.map((row) => cols.map((c) => row[c] ?? null))\n return { columns: cols, rows }\n}\n\n/**\n * Convert a flat row object to PD4's column-value pairs for update.\n * Filters out the _pk field.\n */\nexport function toColumnValues(\n flat: Row,\n): { column: string; value: string | number | boolean | null }[] {\n return Object.entries(flat)\n .filter(([k]) => k !== '_pk')\n .map(([column, value]) => ({ column, value }))\n}\n","import type { PD4Client } from './client'\nimport { flattenRow, toColumnValues } from './utils'\nimport type { Row, FetchResult, PD4Row } from './types'\n\n/**\n * A buffered view wrapping a PD4 table.\n * Changes are accumulated locally and only become visible\n * to other clients after commit().\n */\nexport class PD4Buffer {\n private client: PD4Client\n readonly handle: string\n readonly table: string\n\n constructor(client: PD4Client, handle: string, table: string) {\n this.client = client\n this.handle = handle\n this.table = table\n }\n\n /** Update a row within the buffer (not visible to others until commit). */\n async update(key: number, values: Row): Promise<void> {\n const pairs = toColumnValues(values)\n await this._request(`views/${this.handle}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values: pairs }),\n })\n }\n\n /** Fetch rows from the buffer (sees uncommitted changes). */\n async fetch(offset = 0, limit = 100): Promise<FetchResult> {\n const data = await this._request<{ rows: PD4Row[]; total: number }>(\n `views/${this.handle}/rows?offset=${offset}&limit=${limit}`,\n )\n return {\n rows: data.rows.map(flattenRow),\n total: data.total,\n }\n }\n\n /** Commit all buffered writes atomically. Fires SSE events. */\n async commit(): Promise<void> {\n await this._request(`views/${this.handle}/commit`, { method: 'POST' })\n }\n\n /** Rollback all buffered writes (discard pending changes). */\n async rollback(): Promise<void> {\n await this._request(`views/${this.handle}/rollback`, { method: 'POST' })\n }\n\n // --- Internal ----------------------------------------------------------\n\n private async _request<T>(path: string, init?: RequestInit): Promise<T> {\n const url = `${this.client.url}/v1/${this.client.db}/${path}`\n const res = await fetch(url, {\n ...init,\n headers: { 'Content-Type': 'application/json', ...init?.headers },\n })\n if (!res.ok) {\n const text = await res.text()\n throw new Error(`PD4 Buffer ${init?.method ?? 'GET'} ${url}: ${res.status} ${text}`)\n }\n if (res.status === 204) return undefined as T\n return res.json()\n }\n}\n","import { PD4Error } from './errors'\nimport { flattenRow, toColumnar, toColumnValues } from './utils'\nimport { PD4Buffer } from './buffer'\nimport type {\n ColumnInfo,\n TableInfo,\n PD4Row,\n Row,\n FetchOptions,\n FetchResult,\n InsertResult,\n ConnectionStatus,\n InsertHandler,\n UpdateHandler,\n DeleteHandler,\n WildcardInsertHandler,\n WildcardUpdateHandler,\n WildcardDeleteHandler,\n AnyEventHandler,\n StatusHandler,\n PD4ClientOptions,\n} from './types'\n\n// ---------------------------------------------------------------------------\n// Internal types for SSE handler registry\n// ---------------------------------------------------------------------------\n\ninterface TableHandlers {\n insert: InsertHandler[]\n update: UpdateHandler[]\n delete: DeleteHandler[]\n}\n\ninterface WildcardHandlers {\n insert: WildcardInsertHandler[]\n update: WildcardUpdateHandler[]\n delete: WildcardDeleteHandler[]\n}\n\n// ---------------------------------------------------------------------------\n// PD4Client\n// ---------------------------------------------------------------------------\n\nexport class PD4Client {\n readonly url: string\n readonly db: string\n\n private EventSourceCtor: (new (url: string) => EventSource) | null\n private eventSource: EventSource | null = null\n private handlers = new Map<string, TableHandlers>()\n private wildcardHandlers: WildcardHandlers = { insert: [], update: [], delete: [] }\n private anyHandlers: AnyEventHandler[] = []\n private statusHandlers: StatusHandler[] = []\n private _status: ConnectionStatus = 'connecting'\n\n constructor(url: string, db: string, options?: PD4ClientOptions) {\n this.url = url.replace(/\\/$/, '')\n this.db = db\n this.EventSourceCtor = options?.EventSource\n ?? (typeof globalThis !== 'undefined' && globalThis.EventSource\n ? globalThis.EventSource\n : null)\n }\n\n // --- URL helpers -------------------------------------------------------\n\n private get v1() {\n return `${this.url}/v1/${this.db}`\n }\n\n private get api() {\n return `${this.url}/api/${this.db}`\n }\n\n // --- HTTP helper -------------------------------------------------------\n\n private async request<T>(url: string, init?: RequestInit): Promise<T> {\n const res = await fetch(url, {\n ...init,\n headers: { 'Content-Type': 'application/json', ...init?.headers },\n })\n if (!res.ok) {\n const text = await res.text()\n throw new PD4Error(\n `PD4 ${init?.method ?? 'GET'} ${url}: ${res.status} ${text}`,\n res.status,\n url,\n )\n }\n if (res.status === 204) return undefined as T\n return res.json()\n }\n\n // --- Health ------------------------------------------------------------\n\n /** Check if PD4 is reachable */\n async ping(): Promise<boolean> {\n try {\n const res = await fetch(`${this.url}/health`)\n return res.ok\n } catch {\n return false\n }\n }\n\n // --- Database ----------------------------------------------------------\n\n /** Ensure the database is open */\n async ensureDatabase(): Promise<void> {\n await this.request(`${this.url}/v1/db`, {\n method: 'POST',\n body: JSON.stringify({ name: this.db }),\n })\n }\n\n // --- Tables ------------------------------------------------------------\n\n /** List all table names */\n async listTables(): Promise<string[]> {\n const data = await this.request<{ tables: string[] }>(`${this.v1}/tables`)\n return data.tables\n }\n\n /** Get table info */\n async getTable(name: string): Promise<TableInfo> {\n return this.request<TableInfo>(`${this.v1}/tables/${name}`)\n }\n\n /** Create a table (with auto_pk) */\n async createTable(name: string): Promise<void> {\n await this.request(`${this.v1}/tables`, {\n method: 'POST',\n body: JSON.stringify({ name, auto_pk: true }),\n })\n }\n\n /** Drop a table */\n async dropTable(name: string): Promise<void> {\n await this.request(`${this.v1}/tables/${name}`, { method: 'DELETE' })\n }\n\n /** Add a column to a table */\n async addColumn(table: string, name: string, dtype: string): Promise<void> {\n await this.request(`${this.v1}/tables/${table}/columns`, {\n method: 'POST',\n body: JSON.stringify({ name, dtype }),\n })\n }\n\n // --- Rows (flat objects in, flat objects out) ---------------------------\n\n /** Insert one or more rows (flat objects). Returns keys and count. */\n async insert(table: string, rows: Row | Row[]): Promise<InsertResult> {\n const arr = Array.isArray(rows) ? rows : [rows]\n const { columns, rows: colRows } = toColumnar(arr)\n return this.request<InsertResult>(`${this.v1}/tables/${table}/rows`, {\n method: 'POST',\n body: JSON.stringify({ columns, rows: colRows }),\n })\n }\n\n /** Update a single row by key with a flat object of changed columns. */\n async update(table: string, key: number, values: Row): Promise<void> {\n const pairs = toColumnValues(values)\n await this.request(`${this.v1}/views/${table}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values: pairs }),\n })\n }\n\n /** Delete rows by keys. */\n async delete(table: string, keys: number | number[]): Promise<void> {\n const arr = Array.isArray(keys) ? keys : [keys]\n await this.request(`${this.v1}/views/${table}/rows/delete`, {\n method: 'POST',\n body: JSON.stringify({ keys: arr }),\n })\n }\n\n /** Fetch rows from a table/view. Returns flat Row objects. */\n async fetch(table: string, opts?: FetchOptions): Promise<FetchResult> {\n const offset = opts?.offset ?? 0\n const limit = opts?.limit ?? 1000\n const data = await this.request<{ rows: PD4Row[]; total: number }>(\n `${this.v1}/views/${table}/rows?offset=${offset}&limit=${limit}`,\n )\n return {\n rows: data.rows.map(flattenRow),\n total: data.total,\n }\n }\n\n // --- Raw row access (PD4 wire format) ----------------------------------\n\n /** Insert rows in columnar format (low-level). */\n async insertColumnar(\n table: string,\n columns: string[],\n rows: (string | number | boolean | null)[][],\n ): Promise<InsertResult> {\n return this.request<InsertResult>(`${this.v1}/tables/${table}/rows`, {\n method: 'POST',\n body: JSON.stringify({ columns, rows }),\n })\n }\n\n /** Update a row by key with column-value pairs (low-level). */\n async updateRaw(\n table: string,\n key: number,\n values: { column: string; value: string | number | boolean | null }[],\n ): Promise<void> {\n await this.request(`${this.v1}/views/${table}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values }),\n })\n }\n\n /** Fetch rows in PD4 wire format (low-level). */\n async fetchRaw(\n table: string,\n offset = 0,\n limit = 1000,\n ): Promise<{ rows: PD4Row[]; total: number }> {\n return this.request(`${this.v1}/views/${table}/rows?offset=${offset}&limit=${limit}`)\n }\n\n // --- Buffer ------------------------------------------------------------\n\n /** Create a buffered view wrapping a table. */\n async buffer(table: string): Promise<PD4Buffer> {\n const info = await this.request<{ handle: string; source: string; row_count: number }>(\n `${this.v1}/views/${table}/buffer`,\n { method: 'POST' },\n )\n return new PD4Buffer(this, info.handle, table)\n }\n\n // --- SSE ---------------------------------------------------------------\n\n /** Get current SSE connection status. */\n get status(): ConnectionStatus {\n return this._status\n }\n\n /** Connect to the SSE event stream. Called automatically on first on(). */\n connect(): void {\n if (this.eventSource) return\n if (!this.EventSourceCtor) {\n throw new Error(\n 'EventSource is not available. In Node.js, pass { EventSource } from the \"eventsource\" package to the PD4Client constructor.',\n )\n }\n\n this._setStatus('connecting')\n const sse = new this.EventSourceCtor(`${this.api}/events`)\n this.eventSource = sse\n\n sse.addEventListener('session', () => {\n this._setStatus('connected')\n })\n\n sse.addEventListener('insert', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n const newValues: Row[] = data.new_values ?? []\n this._dispatch(table, 'insert', keys, newValues, [])\n })\n\n sse.addEventListener('update', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n const newValues: Row[] = data.new_values ?? []\n const oldValues: Row[] = data.old_values ?? []\n this._dispatch(table, 'update', keys, newValues, oldValues)\n })\n\n sse.addEventListener('delete', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n this._dispatch(table, 'delete', keys, [], [])\n })\n\n sse.onerror = () => {\n this._setStatus('error')\n }\n }\n\n /** Disconnect from the SSE event stream. */\n disconnect(): void {\n this.eventSource?.close()\n this.eventSource = null\n this._setStatus('connecting')\n }\n\n /**\n * Subscribe to SSE events for a specific table, or use '*' for all tables.\n * Auto-connects on first call. Returns an unsubscribe function.\n *\n * Table-specific: handler receives (keys, rows/newValues/oldValues)\n * Wildcard '*': handler receives (table, keys, rows/newValues/oldValues)\n */\n on(table: '*', event: 'insert', handler: WildcardInsertHandler): () => void\n on(table: '*', event: 'update', handler: WildcardUpdateHandler): () => void\n on(table: '*', event: 'delete', handler: WildcardDeleteHandler): () => void\n on(table: string, event: 'insert', handler: InsertHandler): () => void\n on(table: string, event: 'update', handler: UpdateHandler): () => void\n on(table: string, event: 'delete', handler: DeleteHandler): () => void\n on(\n table: string,\n event: 'insert' | 'update' | 'delete',\n handler:\n | InsertHandler | UpdateHandler | DeleteHandler\n | WildcardInsertHandler | WildcardUpdateHandler | WildcardDeleteHandler,\n ): () => void {\n // Auto-connect\n if (!this.eventSource) this.connect()\n\n // Wildcard: register in wildcardHandlers\n if (table === '*') {\n const list = this.wildcardHandlers[event] as unknown[]\n list.push(handler)\n return () => {\n const idx = list.indexOf(handler)\n if (idx >= 0) list.splice(idx, 1)\n }\n }\n\n // Table-specific\n let tableHandlers = this.handlers.get(table)\n if (!tableHandlers) {\n tableHandlers = { insert: [], update: [], delete: [] }\n this.handlers.set(table, tableHandlers)\n }\n\n const list = tableHandlers[event] as unknown[]\n list.push(handler)\n\n return () => {\n const idx = list.indexOf(handler)\n if (idx >= 0) list.splice(idx, 1)\n }\n }\n\n /**\n * Subscribe to all SSE events on all tables.\n * Handler receives (table, event, keys, newValues, oldValues).\n * Auto-connects on first call. Returns an unsubscribe function.\n */\n onAny(handler: AnyEventHandler): () => void {\n if (!this.eventSource) this.connect()\n\n this.anyHandlers.push(handler)\n return () => {\n const idx = this.anyHandlers.indexOf(handler)\n if (idx >= 0) this.anyHandlers.splice(idx, 1)\n }\n }\n\n /** Subscribe to connection status changes. Returns an unsubscribe function. */\n onStatus(handler: StatusHandler): () => void {\n this.statusHandlers.push(handler)\n return () => {\n const idx = this.statusHandlers.indexOf(handler)\n if (idx >= 0) this.statusHandlers.splice(idx, 1)\n }\n }\n\n // --- SSE internals -----------------------------------------------------\n\n private _setStatus(status: ConnectionStatus) {\n this._status = status\n for (const handler of this.statusHandlers) {\n handler(status)\n }\n }\n\n private _dispatch(\n table: string,\n event: 'insert' | 'update' | 'delete',\n keys: number[],\n newValues: Row[],\n oldValues: Row[],\n ) {\n // Table-specific handlers\n const tableHandlers = this.handlers.get(table)\n if (tableHandlers) {\n if (event === 'insert') {\n for (const h of tableHandlers.insert) h(keys, newValues)\n } else if (event === 'update') {\n for (const h of tableHandlers.update) h(keys, newValues, oldValues)\n } else {\n for (const h of tableHandlers.delete) h(keys)\n }\n }\n\n // Wildcard handlers (on('*', ...))\n if (event === 'insert') {\n for (const h of this.wildcardHandlers.insert) h(table, keys, newValues)\n } else if (event === 'update') {\n for (const h of this.wildcardHandlers.update) h(table, keys, newValues, oldValues)\n } else {\n for (const h of this.wildcardHandlers.delete) h(table, keys)\n }\n\n // onAny handlers\n for (const h of this.anyHandlers) {\n h(table, event, keys, newValues, oldValues)\n }\n }\n}\n\n// Re-export for convenience\nexport type {\n ColumnInfo, TableInfo, PD4Row, Row, FetchOptions, FetchResult, InsertResult,\n WildcardInsertHandler, WildcardUpdateHandler, WildcardDeleteHandler, AnyEventHandler,\n}\n","import { useEffect, useRef } from 'react'\nimport { usePD4 } from './context'\nimport type { InsertHandler, UpdateHandler, DeleteHandler } from '../types'\n\n/**\n * Subscribe to SSE events for a table. Auto-cleans up on unmount.\n *\n * Usage:\n * usePD4Subscribe('metrics', 'insert', (keys, rows) => { ... })\n * usePD4Subscribe('metrics', 'update', (keys, newVals, oldVals) => { ... })\n * usePD4Subscribe('metrics', 'delete', (keys) => { ... })\n */\nexport function usePD4Subscribe(table: string, event: 'insert', handler: InsertHandler): void\nexport function usePD4Subscribe(table: string, event: 'update', handler: UpdateHandler): void\nexport function usePD4Subscribe(table: string, event: 'delete', handler: DeleteHandler): void\nexport function usePD4Subscribe(\n table: string,\n event: 'insert' | 'update' | 'delete',\n handler: InsertHandler | UpdateHandler | DeleteHandler,\n): void {\n const db = usePD4()\n const handlerRef = useRef(handler)\n handlerRef.current = handler\n\n useEffect(() => {\n const wrapper = (...args: unknown[]) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ;(handlerRef.current as any)(...args)\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return db.on(table, event as any, wrapper as any)\n }, [db, table, event])\n}\n","import { useState, useEffect, useCallback, useRef } from 'react'\nimport { usePD4 } from './context'\nimport type { Row, ColumnInfo, ConnectionStatus } from '../types'\n\nconst HIGHLIGHT_MS = 1500\n\nexport interface UsePD4TableOptions {\n limit?: number\n deleteDelay?: number\n}\n\nexport interface UsePD4TableResult {\n rows: Row[]\n total: number\n columns: ColumnInfo[]\n status: ConnectionStatus\n highlights: Record<number, string>\n}\n\n/**\n * Full table state management hook.\n * Handles: initial fetch, SSE subscriptions for insert/update/delete,\n * row state management, highlight tracking, and buffer-commit edge cases.\n */\nexport function usePD4Table(\n table: string,\n opts?: UsePD4TableOptions,\n): UsePD4TableResult {\n const db = usePD4()\n const limit = opts?.limit ?? 1000\n const deleteDelay = opts?.deleteDelay ?? HIGHLIGHT_MS\n\n const [rows, setRows] = useState<Row[]>([])\n const [total, setTotal] = useState(0)\n const [columns, setColumns] = useState<ColumnInfo[]>([])\n const [status, setStatus] = useState<ConnectionStatus>('connecting')\n const [highlights, setHighlights] = useState<Record<number, string>>({})\n\n // Refs for stable callback access\n const tableRef = useRef(table)\n tableRef.current = table\n\n const highlightKeys = useCallback((keys: number[], type: string) => {\n if (keys.length === 0) return\n setHighlights((prev) => {\n const next = { ...prev }\n for (const k of keys) next[k] = type\n return next\n })\n setTimeout(() => {\n setHighlights((prev) => {\n const next = { ...prev }\n for (const k of keys) {\n if (next[k] === type) delete next[k]\n }\n return next\n })\n }, HIGHLIGHT_MS)\n }, [])\n\n const fetchRows = useCallback(async () => {\n try {\n const { rows: fetched, total: t } = await db.fetch(tableRef.current, { offset: 0, limit })\n setRows(fetched)\n setTotal(t)\n } catch {\n // Non-fatal\n }\n }, [db, limit])\n\n useEffect(() => {\n let cancelled = false\n const unsubs: (() => void)[] = []\n\n ;(async () => {\n try {\n // 1. Get column info\n const info = await db.getTable(table)\n if (cancelled) return\n setColumns(info.columns.filter((c) => c.name !== '_pk'))\n\n // 2. Fetch initial rows\n await fetchRows()\n if (cancelled) return\n setStatus('connected')\n\n // 3. SSE subscriptions\n unsubs.push(db.on(table, 'insert', (keys, newValues) => {\n if (cancelled) return\n const newRows = keys.map((k, i) => ({ _pk: k, ...newValues[i] }))\n setRows((prev) => [...prev, ...newRows])\n setTotal((prev) => prev + newRows.length)\n highlightKeys(keys, 'insert')\n }))\n\n unsubs.push(db.on(table, 'update', (keys, newValues) => {\n if (cancelled) return\n\n // Buffer commits send raw scalar values, not flat objects — fall back to refetch\n if (\n newValues.length === 0 ||\n typeof newValues[0] !== 'object' ||\n newValues[0] === null\n ) {\n fetchRows()\n highlightKeys(keys, 'update')\n return\n }\n\n const updateMap = new Map(keys.map((k, i) => [k, newValues[i]]))\n setRows((prev) =>\n prev.map((row) => {\n const changes = updateMap.get(row._pk as number)\n if (!changes) return row\n return { ...row, ...changes }\n }),\n )\n highlightKeys(keys, 'update')\n }))\n\n unsubs.push(db.on(table, 'delete', (keys) => {\n if (cancelled) return\n const keySet = new Set(keys)\n highlightKeys(keys, 'delete')\n setTimeout(() => {\n setRows((prev) => prev.filter((row) => !keySet.has(row._pk as number)))\n setTotal((prev) => Math.max(0, prev - keys.length))\n }, deleteDelay)\n }))\n } catch {\n if (!cancelled) {\n setStatus('error')\n }\n }\n })()\n\n return () => {\n cancelled = true\n for (const unsub of unsubs) unsub()\n }\n }, [db, table, limit, fetchRows, highlightKeys, deleteDelay])\n\n return { rows, total, columns, status, highlights }\n}\n","import { useState, useEffect } from 'react'\nimport { usePD4 } from './context'\nimport type { Row } from '../types'\n\nexport interface UsePD4QueryResult {\n rows: Row[]\n total: number\n loading: boolean\n error: string | null\n refetch: () => void\n}\n\n/**\n * One-shot query hook. Fetches rows from a table once (or on refetch).\n */\nexport function usePD4Query(\n table: string,\n offset = 0,\n limit = 1000,\n): UsePD4QueryResult {\n const db = usePD4()\n const [rows, setRows] = useState<Row[]>([])\n const [total, setTotal] = useState(0)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n const [attempt, setAttempt] = useState(0)\n\n useEffect(() => {\n let cancelled = false\n setLoading(true)\n setError(null)\n\n db.fetch(table, { offset, limit })\n .then((result) => {\n if (cancelled) return\n setRows(result.rows)\n setTotal(result.total)\n setLoading(false)\n })\n .catch((err) => {\n if (cancelled) return\n setError(err instanceof Error ? err.message : 'Query failed')\n setLoading(false)\n })\n\n return () => { cancelled = true }\n }, [db, table, offset, limit, attempt])\n\n return {\n rows,\n total,\n loading,\n error,\n refetch: () => setAttempt((a) => a + 1),\n }\n}\n"],"mappings":"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","names":["list","import_react","import_react","import_react"]}
@@ -23,6 +23,8 @@ interface PD4Row {
23
23
  }
24
24
  /** Flat row for display: column name -> value, plus _pk */
25
25
  type Row = Record<string, string | number | boolean | null>;
26
+ /** SSE event types */
27
+ type SseEventType = 'insert' | 'update' | 'delete';
26
28
  /** SSE connection status */
27
29
  type ConnectionStatus = 'connecting' | 'connected' | 'error';
28
30
  /** Callback for insert events: (keys, rows) */
@@ -31,6 +33,12 @@ type InsertHandler = (keys: number[], rows: Row[]) => void;
31
33
  type UpdateHandler = (keys: number[], newValues: Row[], oldValues: Row[]) => void;
32
34
  /** Callback for delete events: (keys) */
33
35
  type DeleteHandler = (keys: number[]) => void;
36
+ /** Wildcard callbacks: same as above but with table name as first arg */
37
+ type WildcardInsertHandler = (table: string, keys: number[], rows: Row[]) => void;
38
+ type WildcardUpdateHandler = (table: string, keys: number[], newValues: Row[], oldValues: Row[]) => void;
39
+ type WildcardDeleteHandler = (table: string, keys: number[]) => void;
40
+ /** Callback for onAny: fires for every event on every table */
41
+ type AnyEventHandler = (table: string, event: SseEventType, keys: number[], newValues: Row[], oldValues: Row[]) => void;
34
42
  /** Callback for status changes */
35
43
  type StatusHandler = (status: ConnectionStatus) => void;
36
44
  /** Options for fetch */
@@ -84,6 +92,8 @@ declare class PD4Client {
84
92
  private EventSourceCtor;
85
93
  private eventSource;
86
94
  private handlers;
95
+ private wildcardHandlers;
96
+ private anyHandlers;
87
97
  private statusHandlers;
88
98
  private _status;
89
99
  constructor(url: string, db: string, options?: PD4ClientOptions);
@@ -133,12 +143,24 @@ declare class PD4Client {
133
143
  /** Disconnect from the SSE event stream. */
134
144
  disconnect(): void;
135
145
  /**
136
- * Subscribe to SSE events for a table.
146
+ * Subscribe to SSE events for a specific table, or use '*' for all tables.
137
147
  * Auto-connects on first call. Returns an unsubscribe function.
148
+ *
149
+ * Table-specific: handler receives (keys, rows/newValues/oldValues)
150
+ * Wildcard '*': handler receives (table, keys, rows/newValues/oldValues)
138
151
  */
152
+ on(table: '*', event: 'insert', handler: WildcardInsertHandler): () => void;
153
+ on(table: '*', event: 'update', handler: WildcardUpdateHandler): () => void;
154
+ on(table: '*', event: 'delete', handler: WildcardDeleteHandler): () => void;
139
155
  on(table: string, event: 'insert', handler: InsertHandler): () => void;
140
156
  on(table: string, event: 'update', handler: UpdateHandler): () => void;
141
157
  on(table: string, event: 'delete', handler: DeleteHandler): () => void;
158
+ /**
159
+ * Subscribe to all SSE events on all tables.
160
+ * Handler receives (table, event, keys, newValues, oldValues).
161
+ * Auto-connects on first call. Returns an unsubscribe function.
162
+ */
163
+ onAny(handler: AnyEventHandler): () => void;
142
164
  /** Subscribe to connection status changes. Returns an unsubscribe function. */
143
165
  onStatus(handler: StatusHandler): () => void;
144
166
  private _setStatus;
@@ -23,6 +23,8 @@ interface PD4Row {
23
23
  }
24
24
  /** Flat row for display: column name -> value, plus _pk */
25
25
  type Row = Record<string, string | number | boolean | null>;
26
+ /** SSE event types */
27
+ type SseEventType = 'insert' | 'update' | 'delete';
26
28
  /** SSE connection status */
27
29
  type ConnectionStatus = 'connecting' | 'connected' | 'error';
28
30
  /** Callback for insert events: (keys, rows) */
@@ -31,6 +33,12 @@ type InsertHandler = (keys: number[], rows: Row[]) => void;
31
33
  type UpdateHandler = (keys: number[], newValues: Row[], oldValues: Row[]) => void;
32
34
  /** Callback for delete events: (keys) */
33
35
  type DeleteHandler = (keys: number[]) => void;
36
+ /** Wildcard callbacks: same as above but with table name as first arg */
37
+ type WildcardInsertHandler = (table: string, keys: number[], rows: Row[]) => void;
38
+ type WildcardUpdateHandler = (table: string, keys: number[], newValues: Row[], oldValues: Row[]) => void;
39
+ type WildcardDeleteHandler = (table: string, keys: number[]) => void;
40
+ /** Callback for onAny: fires for every event on every table */
41
+ type AnyEventHandler = (table: string, event: SseEventType, keys: number[], newValues: Row[], oldValues: Row[]) => void;
34
42
  /** Callback for status changes */
35
43
  type StatusHandler = (status: ConnectionStatus) => void;
36
44
  /** Options for fetch */
@@ -84,6 +92,8 @@ declare class PD4Client {
84
92
  private EventSourceCtor;
85
93
  private eventSource;
86
94
  private handlers;
95
+ private wildcardHandlers;
96
+ private anyHandlers;
87
97
  private statusHandlers;
88
98
  private _status;
89
99
  constructor(url: string, db: string, options?: PD4ClientOptions);
@@ -133,12 +143,24 @@ declare class PD4Client {
133
143
  /** Disconnect from the SSE event stream. */
134
144
  disconnect(): void;
135
145
  /**
136
- * Subscribe to SSE events for a table.
146
+ * Subscribe to SSE events for a specific table, or use '*' for all tables.
137
147
  * Auto-connects on first call. Returns an unsubscribe function.
148
+ *
149
+ * Table-specific: handler receives (keys, rows/newValues/oldValues)
150
+ * Wildcard '*': handler receives (table, keys, rows/newValues/oldValues)
138
151
  */
152
+ on(table: '*', event: 'insert', handler: WildcardInsertHandler): () => void;
153
+ on(table: '*', event: 'update', handler: WildcardUpdateHandler): () => void;
154
+ on(table: '*', event: 'delete', handler: WildcardDeleteHandler): () => void;
139
155
  on(table: string, event: 'insert', handler: InsertHandler): () => void;
140
156
  on(table: string, event: 'update', handler: UpdateHandler): () => void;
141
157
  on(table: string, event: 'delete', handler: DeleteHandler): () => void;
158
+ /**
159
+ * Subscribe to all SSE events on all tables.
160
+ * Handler receives (table, event, keys, newValues, oldValues).
161
+ * Auto-connects on first call. Returns an unsubscribe function.
162
+ */
163
+ onAny(handler: AnyEventHandler): () => void;
142
164
  /** Subscribe to connection status changes. Returns an unsubscribe function. */
143
165
  onStatus(handler: StatusHandler): () => void;
144
166
  private _setStatus;
@@ -83,6 +83,8 @@ var PD4Client = class {
83
83
  constructor(url, db, options) {
84
84
  this.eventSource = null;
85
85
  this.handlers = /* @__PURE__ */ new Map();
86
+ this.wildcardHandlers = { insert: [], update: [], delete: [] };
87
+ this.anyHandlers = [];
86
88
  this.statusHandlers = [];
87
89
  this._status = "connecting";
88
90
  this.url = url.replace(/\/$/, "");
@@ -277,6 +279,14 @@ var PD4Client = class {
277
279
  }
278
280
  on(table, event, handler) {
279
281
  if (!this.eventSource) this.connect();
282
+ if (table === "*") {
283
+ const list2 = this.wildcardHandlers[event];
284
+ list2.push(handler);
285
+ return () => {
286
+ const idx = list2.indexOf(handler);
287
+ if (idx >= 0) list2.splice(idx, 1);
288
+ };
289
+ }
280
290
  let tableHandlers = this.handlers.get(table);
281
291
  if (!tableHandlers) {
282
292
  tableHandlers = { insert: [], update: [], delete: [] };
@@ -289,6 +299,19 @@ var PD4Client = class {
289
299
  if (idx >= 0) list.splice(idx, 1);
290
300
  };
291
301
  }
302
+ /**
303
+ * Subscribe to all SSE events on all tables.
304
+ * Handler receives (table, event, keys, newValues, oldValues).
305
+ * Auto-connects on first call. Returns an unsubscribe function.
306
+ */
307
+ onAny(handler) {
308
+ if (!this.eventSource) this.connect();
309
+ this.anyHandlers.push(handler);
310
+ return () => {
311
+ const idx = this.anyHandlers.indexOf(handler);
312
+ if (idx >= 0) this.anyHandlers.splice(idx, 1);
313
+ };
314
+ }
292
315
  /** Subscribe to connection status changes. Returns an unsubscribe function. */
293
316
  onStatus(handler) {
294
317
  this.statusHandlers.push(handler);
@@ -306,13 +329,24 @@ var PD4Client = class {
306
329
  }
307
330
  _dispatch(table, event, keys, newValues, oldValues) {
308
331
  const tableHandlers = this.handlers.get(table);
309
- if (!tableHandlers) return;
332
+ if (tableHandlers) {
333
+ if (event === "insert") {
334
+ for (const h of tableHandlers.insert) h(keys, newValues);
335
+ } else if (event === "update") {
336
+ for (const h of tableHandlers.update) h(keys, newValues, oldValues);
337
+ } else {
338
+ for (const h of tableHandlers.delete) h(keys);
339
+ }
340
+ }
310
341
  if (event === "insert") {
311
- for (const h of tableHandlers.insert) h(keys, newValues);
342
+ for (const h of this.wildcardHandlers.insert) h(table, keys, newValues);
312
343
  } else if (event === "update") {
313
- for (const h of tableHandlers.update) h(keys, newValues, oldValues);
344
+ for (const h of this.wildcardHandlers.update) h(table, keys, newValues, oldValues);
314
345
  } else {
315
- for (const h of tableHandlers.delete) h(keys);
346
+ for (const h of this.wildcardHandlers.delete) h(table, keys);
347
+ }
348
+ for (const h of this.anyHandlers) {
349
+ h(table, event, keys, newValues, oldValues);
316
350
  }
317
351
  }
318
352
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/context.tsx","../../src/errors.ts","../../src/utils.ts","../../src/buffer.ts","../../src/client.ts","../../src/react/usePD4Subscribe.ts","../../src/react/usePD4Table.ts","../../src/react/usePD4Query.ts"],"sourcesContent":["import { createContext, useContext, useRef } from 'react'\nimport { PD4Client } from '../client'\n\nconst PD4Context = createContext<PD4Client | null>(null)\n\nexport interface PD4ProviderProps {\n url: string\n db: string\n children: React.ReactNode\n}\n\nexport function PD4Provider({ url, db, children }: PD4ProviderProps) {\n const clientRef = useRef<PD4Client | null>(null)\n if (!clientRef.current) {\n clientRef.current = new PD4Client(url, db)\n }\n return <PD4Context.Provider value={clientRef.current}>{children}</PD4Context.Provider>\n}\n\nexport function usePD4(): PD4Client {\n const client = useContext(PD4Context)\n if (!client) {\n throw new Error('usePD4() must be used within a <PD4Provider>')\n }\n return client\n}\n","/** Base error class for PD4 client errors */\nexport class PD4Error extends Error {\n readonly status: number | undefined\n readonly url: string | undefined\n\n constructor(message: string, status?: number, url?: string) {\n super(message)\n this.name = 'PD4Error'\n this.status = status\n this.url = url\n }\n}\n","import type { PD4Row, Row } from './types'\n\n/** Convert PD4 row format to flat record */\nexport function flattenRow(row: PD4Row): Row {\n const flat: Row = { _pk: row.key }\n for (const cv of row.values) {\n flat[cv.column] = cv.value\n }\n return flat\n}\n\n/**\n * Convert flat row objects to PD4's columnar insert format.\n * Returns { columns, rows } suitable for the insert endpoint.\n */\nexport function toColumnar(\n flatRows: Row[],\n columnNames?: string[],\n): { columns: string[]; rows: (string | number | boolean | null)[][] } {\n if (flatRows.length === 0) return { columns: [], rows: [] }\n const cols = columnNames ?? Object.keys(flatRows[0]).filter((k) => k !== '_pk')\n const rows = flatRows.map((row) => cols.map((c) => row[c] ?? null))\n return { columns: cols, rows }\n}\n\n/**\n * Convert a flat row object to PD4's column-value pairs for update.\n * Filters out the _pk field.\n */\nexport function toColumnValues(\n flat: Row,\n): { column: string; value: string | number | boolean | null }[] {\n return Object.entries(flat)\n .filter(([k]) => k !== '_pk')\n .map(([column, value]) => ({ column, value }))\n}\n","import type { PD4Client } from './client'\nimport { flattenRow, toColumnValues } from './utils'\nimport type { Row, FetchResult, PD4Row } from './types'\n\n/**\n * A buffered view wrapping a PD4 table.\n * Changes are accumulated locally and only become visible\n * to other clients after commit().\n */\nexport class PD4Buffer {\n private client: PD4Client\n readonly handle: string\n readonly table: string\n\n constructor(client: PD4Client, handle: string, table: string) {\n this.client = client\n this.handle = handle\n this.table = table\n }\n\n /** Update a row within the buffer (not visible to others until commit). */\n async update(key: number, values: Row): Promise<void> {\n const pairs = toColumnValues(values)\n await this._request(`views/${this.handle}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values: pairs }),\n })\n }\n\n /** Fetch rows from the buffer (sees uncommitted changes). */\n async fetch(offset = 0, limit = 100): Promise<FetchResult> {\n const data = await this._request<{ rows: PD4Row[]; total: number }>(\n `views/${this.handle}/rows?offset=${offset}&limit=${limit}`,\n )\n return {\n rows: data.rows.map(flattenRow),\n total: data.total,\n }\n }\n\n /** Commit all buffered writes atomically. Fires SSE events. */\n async commit(): Promise<void> {\n await this._request(`views/${this.handle}/commit`, { method: 'POST' })\n }\n\n /** Rollback all buffered writes (discard pending changes). */\n async rollback(): Promise<void> {\n await this._request(`views/${this.handle}/rollback`, { method: 'POST' })\n }\n\n // --- Internal ----------------------------------------------------------\n\n private async _request<T>(path: string, init?: RequestInit): Promise<T> {\n const url = `${this.client.url}/v1/${this.client.db}/${path}`\n const res = await fetch(url, {\n ...init,\n headers: { 'Content-Type': 'application/json', ...init?.headers },\n })\n if (!res.ok) {\n const text = await res.text()\n throw new Error(`PD4 Buffer ${init?.method ?? 'GET'} ${url}: ${res.status} ${text}`)\n }\n if (res.status === 204) return undefined as T\n return res.json()\n }\n}\n","import { PD4Error } from './errors'\nimport { flattenRow, toColumnar, toColumnValues } from './utils'\nimport { PD4Buffer } from './buffer'\nimport type {\n ColumnInfo,\n TableInfo,\n PD4Row,\n Row,\n FetchOptions,\n FetchResult,\n InsertResult,\n ConnectionStatus,\n InsertHandler,\n UpdateHandler,\n DeleteHandler,\n StatusHandler,\n PD4ClientOptions,\n} from './types'\n\n// ---------------------------------------------------------------------------\n// Internal types for SSE handler registry\n// ---------------------------------------------------------------------------\n\ninterface TableHandlers {\n insert: InsertHandler[]\n update: UpdateHandler[]\n delete: DeleteHandler[]\n}\n\n// ---------------------------------------------------------------------------\n// PD4Client\n// ---------------------------------------------------------------------------\n\nexport class PD4Client {\n readonly url: string\n readonly db: string\n\n private EventSourceCtor: (new (url: string) => EventSource) | null\n private eventSource: EventSource | null = null\n private handlers = new Map<string, TableHandlers>()\n private statusHandlers: StatusHandler[] = []\n private _status: ConnectionStatus = 'connecting'\n\n constructor(url: string, db: string, options?: PD4ClientOptions) {\n this.url = url.replace(/\\/$/, '')\n this.db = db\n this.EventSourceCtor = options?.EventSource\n ?? (typeof globalThis !== 'undefined' && globalThis.EventSource\n ? globalThis.EventSource\n : null)\n }\n\n // --- URL helpers -------------------------------------------------------\n\n private get v1() {\n return `${this.url}/v1/${this.db}`\n }\n\n private get api() {\n return `${this.url}/api/${this.db}`\n }\n\n // --- HTTP helper -------------------------------------------------------\n\n private async request<T>(url: string, init?: RequestInit): Promise<T> {\n const res = await fetch(url, {\n ...init,\n headers: { 'Content-Type': 'application/json', ...init?.headers },\n })\n if (!res.ok) {\n const text = await res.text()\n throw new PD4Error(\n `PD4 ${init?.method ?? 'GET'} ${url}: ${res.status} ${text}`,\n res.status,\n url,\n )\n }\n if (res.status === 204) return undefined as T\n return res.json()\n }\n\n // --- Health ------------------------------------------------------------\n\n /** Check if PD4 is reachable */\n async ping(): Promise<boolean> {\n try {\n const res = await fetch(`${this.url}/health`)\n return res.ok\n } catch {\n return false\n }\n }\n\n // --- Database ----------------------------------------------------------\n\n /** Ensure the database is open */\n async ensureDatabase(): Promise<void> {\n await this.request(`${this.url}/v1/db`, {\n method: 'POST',\n body: JSON.stringify({ name: this.db }),\n })\n }\n\n // --- Tables ------------------------------------------------------------\n\n /** List all table names */\n async listTables(): Promise<string[]> {\n const data = await this.request<{ tables: string[] }>(`${this.v1}/tables`)\n return data.tables\n }\n\n /** Get table info */\n async getTable(name: string): Promise<TableInfo> {\n return this.request<TableInfo>(`${this.v1}/tables/${name}`)\n }\n\n /** Create a table (with auto_pk) */\n async createTable(name: string): Promise<void> {\n await this.request(`${this.v1}/tables`, {\n method: 'POST',\n body: JSON.stringify({ name, auto_pk: true }),\n })\n }\n\n /** Drop a table */\n async dropTable(name: string): Promise<void> {\n await this.request(`${this.v1}/tables/${name}`, { method: 'DELETE' })\n }\n\n /** Add a column to a table */\n async addColumn(table: string, name: string, dtype: string): Promise<void> {\n await this.request(`${this.v1}/tables/${table}/columns`, {\n method: 'POST',\n body: JSON.stringify({ name, dtype }),\n })\n }\n\n // --- Rows (flat objects in, flat objects out) ---------------------------\n\n /** Insert one or more rows (flat objects). Returns keys and count. */\n async insert(table: string, rows: Row | Row[]): Promise<InsertResult> {\n const arr = Array.isArray(rows) ? rows : [rows]\n const { columns, rows: colRows } = toColumnar(arr)\n return this.request<InsertResult>(`${this.v1}/tables/${table}/rows`, {\n method: 'POST',\n body: JSON.stringify({ columns, rows: colRows }),\n })\n }\n\n /** Update a single row by key with a flat object of changed columns. */\n async update(table: string, key: number, values: Row): Promise<void> {\n const pairs = toColumnValues(values)\n await this.request(`${this.v1}/views/${table}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values: pairs }),\n })\n }\n\n /** Delete rows by keys. */\n async delete(table: string, keys: number | number[]): Promise<void> {\n const arr = Array.isArray(keys) ? keys : [keys]\n await this.request(`${this.v1}/views/${table}/rows/delete`, {\n method: 'POST',\n body: JSON.stringify({ keys: arr }),\n })\n }\n\n /** Fetch rows from a table/view. Returns flat Row objects. */\n async fetch(table: string, opts?: FetchOptions): Promise<FetchResult> {\n const offset = opts?.offset ?? 0\n const limit = opts?.limit ?? 1000\n const data = await this.request<{ rows: PD4Row[]; total: number }>(\n `${this.v1}/views/${table}/rows?offset=${offset}&limit=${limit}`,\n )\n return {\n rows: data.rows.map(flattenRow),\n total: data.total,\n }\n }\n\n // --- Raw row access (PD4 wire format) ----------------------------------\n\n /** Insert rows in columnar format (low-level). */\n async insertColumnar(\n table: string,\n columns: string[],\n rows: (string | number | boolean | null)[][],\n ): Promise<InsertResult> {\n return this.request<InsertResult>(`${this.v1}/tables/${table}/rows`, {\n method: 'POST',\n body: JSON.stringify({ columns, rows }),\n })\n }\n\n /** Update a row by key with column-value pairs (low-level). */\n async updateRaw(\n table: string,\n key: number,\n values: { column: string; value: string | number | boolean | null }[],\n ): Promise<void> {\n await this.request(`${this.v1}/views/${table}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values }),\n })\n }\n\n /** Fetch rows in PD4 wire format (low-level). */\n async fetchRaw(\n table: string,\n offset = 0,\n limit = 1000,\n ): Promise<{ rows: PD4Row[]; total: number }> {\n return this.request(`${this.v1}/views/${table}/rows?offset=${offset}&limit=${limit}`)\n }\n\n // --- Buffer ------------------------------------------------------------\n\n /** Create a buffered view wrapping a table. */\n async buffer(table: string): Promise<PD4Buffer> {\n const info = await this.request<{ handle: string; source: string; row_count: number }>(\n `${this.v1}/views/${table}/buffer`,\n { method: 'POST' },\n )\n return new PD4Buffer(this, info.handle, table)\n }\n\n // --- SSE ---------------------------------------------------------------\n\n /** Get current SSE connection status. */\n get status(): ConnectionStatus {\n return this._status\n }\n\n /** Connect to the SSE event stream. Called automatically on first on(). */\n connect(): void {\n if (this.eventSource) return\n if (!this.EventSourceCtor) {\n throw new Error(\n 'EventSource is not available. In Node.js, pass { EventSource } from the \"eventsource\" package to the PD4Client constructor.',\n )\n }\n\n this._setStatus('connecting')\n const sse = new this.EventSourceCtor(`${this.api}/events`)\n this.eventSource = sse\n\n sse.addEventListener('session', () => {\n this._setStatus('connected')\n })\n\n sse.addEventListener('insert', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n const newValues: Row[] = data.new_values ?? []\n this._dispatch(table, 'insert', keys, newValues, [])\n })\n\n sse.addEventListener('update', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n const newValues: Row[] = data.new_values ?? []\n const oldValues: Row[] = data.old_values ?? []\n this._dispatch(table, 'update', keys, newValues, oldValues)\n })\n\n sse.addEventListener('delete', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n this._dispatch(table, 'delete', keys, [], [])\n })\n\n sse.onerror = () => {\n this._setStatus('error')\n }\n }\n\n /** Disconnect from the SSE event stream. */\n disconnect(): void {\n this.eventSource?.close()\n this.eventSource = null\n this._setStatus('connecting')\n }\n\n /**\n * Subscribe to SSE events for a table.\n * Auto-connects on first call. Returns an unsubscribe function.\n */\n on(table: string, event: 'insert', handler: InsertHandler): () => void\n on(table: string, event: 'update', handler: UpdateHandler): () => void\n on(table: string, event: 'delete', handler: DeleteHandler): () => void\n on(\n table: string,\n event: 'insert' | 'update' | 'delete',\n handler: InsertHandler | UpdateHandler | DeleteHandler,\n ): () => void {\n // Auto-connect\n if (!this.eventSource) this.connect()\n\n let tableHandlers = this.handlers.get(table)\n if (!tableHandlers) {\n tableHandlers = { insert: [], update: [], delete: [] }\n this.handlers.set(table, tableHandlers)\n }\n\n const list = tableHandlers[event] as unknown[]\n list.push(handler)\n\n return () => {\n const idx = list.indexOf(handler)\n if (idx >= 0) list.splice(idx, 1)\n }\n }\n\n /** Subscribe to connection status changes. Returns an unsubscribe function. */\n onStatus(handler: StatusHandler): () => void {\n this.statusHandlers.push(handler)\n return () => {\n const idx = this.statusHandlers.indexOf(handler)\n if (idx >= 0) this.statusHandlers.splice(idx, 1)\n }\n }\n\n // --- SSE internals -----------------------------------------------------\n\n private _setStatus(status: ConnectionStatus) {\n this._status = status\n for (const handler of this.statusHandlers) {\n handler(status)\n }\n }\n\n private _dispatch(\n table: string,\n event: 'insert' | 'update' | 'delete',\n keys: number[],\n newValues: Row[],\n oldValues: Row[],\n ) {\n const tableHandlers = this.handlers.get(table)\n if (!tableHandlers) return\n\n if (event === 'insert') {\n for (const h of tableHandlers.insert) h(keys, newValues)\n } else if (event === 'update') {\n for (const h of tableHandlers.update) h(keys, newValues, oldValues)\n } else {\n for (const h of tableHandlers.delete) h(keys)\n }\n }\n}\n\n// Re-export for convenience\nexport type { ColumnInfo, TableInfo, PD4Row, Row, FetchOptions, FetchResult, InsertResult }\n","import { useEffect, useRef } from 'react'\nimport { usePD4 } from './context'\nimport type { InsertHandler, UpdateHandler, DeleteHandler } from '../types'\n\n/**\n * Subscribe to SSE events for a table. Auto-cleans up on unmount.\n *\n * Usage:\n * usePD4Subscribe('metrics', 'insert', (keys, rows) => { ... })\n * usePD4Subscribe('metrics', 'update', (keys, newVals, oldVals) => { ... })\n * usePD4Subscribe('metrics', 'delete', (keys) => { ... })\n */\nexport function usePD4Subscribe(table: string, event: 'insert', handler: InsertHandler): void\nexport function usePD4Subscribe(table: string, event: 'update', handler: UpdateHandler): void\nexport function usePD4Subscribe(table: string, event: 'delete', handler: DeleteHandler): void\nexport function usePD4Subscribe(\n table: string,\n event: 'insert' | 'update' | 'delete',\n handler: InsertHandler | UpdateHandler | DeleteHandler,\n): void {\n const db = usePD4()\n const handlerRef = useRef(handler)\n handlerRef.current = handler\n\n useEffect(() => {\n const wrapper = (...args: unknown[]) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ;(handlerRef.current as any)(...args)\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return db.on(table, event as any, wrapper as any)\n }, [db, table, event])\n}\n","import { useState, useEffect, useCallback, useRef } from 'react'\nimport { usePD4 } from './context'\nimport type { Row, ColumnInfo, ConnectionStatus } from '../types'\n\nconst HIGHLIGHT_MS = 1500\n\nexport interface UsePD4TableOptions {\n limit?: number\n deleteDelay?: number\n}\n\nexport interface UsePD4TableResult {\n rows: Row[]\n total: number\n columns: ColumnInfo[]\n status: ConnectionStatus\n highlights: Record<number, string>\n}\n\n/**\n * Full table state management hook.\n * Handles: initial fetch, SSE subscriptions for insert/update/delete,\n * row state management, highlight tracking, and buffer-commit edge cases.\n */\nexport function usePD4Table(\n table: string,\n opts?: UsePD4TableOptions,\n): UsePD4TableResult {\n const db = usePD4()\n const limit = opts?.limit ?? 1000\n const deleteDelay = opts?.deleteDelay ?? HIGHLIGHT_MS\n\n const [rows, setRows] = useState<Row[]>([])\n const [total, setTotal] = useState(0)\n const [columns, setColumns] = useState<ColumnInfo[]>([])\n const [status, setStatus] = useState<ConnectionStatus>('connecting')\n const [highlights, setHighlights] = useState<Record<number, string>>({})\n\n // Refs for stable callback access\n const tableRef = useRef(table)\n tableRef.current = table\n\n const highlightKeys = useCallback((keys: number[], type: string) => {\n if (keys.length === 0) return\n setHighlights((prev) => {\n const next = { ...prev }\n for (const k of keys) next[k] = type\n return next\n })\n setTimeout(() => {\n setHighlights((prev) => {\n const next = { ...prev }\n for (const k of keys) {\n if (next[k] === type) delete next[k]\n }\n return next\n })\n }, HIGHLIGHT_MS)\n }, [])\n\n const fetchRows = useCallback(async () => {\n try {\n const { rows: fetched, total: t } = await db.fetch(tableRef.current, { offset: 0, limit })\n setRows(fetched)\n setTotal(t)\n } catch {\n // Non-fatal\n }\n }, [db, limit])\n\n useEffect(() => {\n let cancelled = false\n const unsubs: (() => void)[] = []\n\n ;(async () => {\n try {\n // 1. Get column info\n const info = await db.getTable(table)\n if (cancelled) return\n setColumns(info.columns.filter((c) => c.name !== '_pk'))\n\n // 2. Fetch initial rows\n await fetchRows()\n if (cancelled) return\n setStatus('connected')\n\n // 3. SSE subscriptions\n unsubs.push(db.on(table, 'insert', (keys, newValues) => {\n if (cancelled) return\n const newRows = keys.map((k, i) => ({ _pk: k, ...newValues[i] }))\n setRows((prev) => [...prev, ...newRows])\n setTotal((prev) => prev + newRows.length)\n highlightKeys(keys, 'insert')\n }))\n\n unsubs.push(db.on(table, 'update', (keys, newValues) => {\n if (cancelled) return\n\n // Buffer commits send raw scalar values, not flat objects — fall back to refetch\n if (\n newValues.length === 0 ||\n typeof newValues[0] !== 'object' ||\n newValues[0] === null\n ) {\n fetchRows()\n highlightKeys(keys, 'update')\n return\n }\n\n const updateMap = new Map(keys.map((k, i) => [k, newValues[i]]))\n setRows((prev) =>\n prev.map((row) => {\n const changes = updateMap.get(row._pk as number)\n if (!changes) return row\n return { ...row, ...changes }\n }),\n )\n highlightKeys(keys, 'update')\n }))\n\n unsubs.push(db.on(table, 'delete', (keys) => {\n if (cancelled) return\n const keySet = new Set(keys)\n highlightKeys(keys, 'delete')\n setTimeout(() => {\n setRows((prev) => prev.filter((row) => !keySet.has(row._pk as number)))\n setTotal((prev) => Math.max(0, prev - keys.length))\n }, deleteDelay)\n }))\n } catch {\n if (!cancelled) {\n setStatus('error')\n }\n }\n })()\n\n return () => {\n cancelled = true\n for (const unsub of unsubs) unsub()\n }\n }, [db, table, limit, fetchRows, highlightKeys, deleteDelay])\n\n return { rows, total, columns, status, highlights }\n}\n","import { useState, useEffect } from 'react'\nimport { usePD4 } from './context'\nimport type { Row } from '../types'\n\nexport interface UsePD4QueryResult {\n rows: Row[]\n total: number\n loading: boolean\n error: string | null\n refetch: () => void\n}\n\n/**\n * One-shot query hook. Fetches rows from a table once (or on refetch).\n */\nexport function usePD4Query(\n table: string,\n offset = 0,\n limit = 1000,\n): UsePD4QueryResult {\n const db = usePD4()\n const [rows, setRows] = useState<Row[]>([])\n const [total, setTotal] = useState(0)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n const [attempt, setAttempt] = useState(0)\n\n useEffect(() => {\n let cancelled = false\n setLoading(true)\n setError(null)\n\n db.fetch(table, { offset, limit })\n .then((result) => {\n if (cancelled) return\n setRows(result.rows)\n setTotal(result.total)\n setLoading(false)\n })\n .catch((err) => {\n if (cancelled) return\n setError(err instanceof Error ? err.message : 'Query failed')\n setLoading(false)\n })\n\n return () => { cancelled = true }\n }, [db, table, offset, limit, attempt])\n\n return {\n rows,\n total,\n loading,\n error,\n refetch: () => setAttempt((a) => a + 1),\n }\n}\n"],"mappings":"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","names":["useRef","useRef","useEffect","useRef","useRef","useEffect","useState","useEffect","useState","useEffect"]}
1
+ {"version":3,"sources":["../../src/react/context.tsx","../../src/errors.ts","../../src/utils.ts","../../src/buffer.ts","../../src/client.ts","../../src/react/usePD4Subscribe.ts","../../src/react/usePD4Table.ts","../../src/react/usePD4Query.ts"],"sourcesContent":["import { createContext, useContext, useRef } from 'react'\nimport { PD4Client } from '../client'\n\nconst PD4Context = createContext<PD4Client | null>(null)\n\nexport interface PD4ProviderProps {\n url: string\n db: string\n children: React.ReactNode\n}\n\nexport function PD4Provider({ url, db, children }: PD4ProviderProps) {\n const clientRef = useRef<PD4Client | null>(null)\n if (!clientRef.current) {\n clientRef.current = new PD4Client(url, db)\n }\n return <PD4Context.Provider value={clientRef.current}>{children}</PD4Context.Provider>\n}\n\nexport function usePD4(): PD4Client {\n const client = useContext(PD4Context)\n if (!client) {\n throw new Error('usePD4() must be used within a <PD4Provider>')\n }\n return client\n}\n","/** Base error class for PD4 client errors */\nexport class PD4Error extends Error {\n readonly status: number | undefined\n readonly url: string | undefined\n\n constructor(message: string, status?: number, url?: string) {\n super(message)\n this.name = 'PD4Error'\n this.status = status\n this.url = url\n }\n}\n","import type { PD4Row, Row } from './types'\n\n/** Convert PD4 row format to flat record */\nexport function flattenRow(row: PD4Row): Row {\n const flat: Row = { _pk: row.key }\n for (const cv of row.values) {\n flat[cv.column] = cv.value\n }\n return flat\n}\n\n/**\n * Convert flat row objects to PD4's columnar insert format.\n * Returns { columns, rows } suitable for the insert endpoint.\n */\nexport function toColumnar(\n flatRows: Row[],\n columnNames?: string[],\n): { columns: string[]; rows: (string | number | boolean | null)[][] } {\n if (flatRows.length === 0) return { columns: [], rows: [] }\n const cols = columnNames ?? Object.keys(flatRows[0]).filter((k) => k !== '_pk')\n const rows = flatRows.map((row) => cols.map((c) => row[c] ?? null))\n return { columns: cols, rows }\n}\n\n/**\n * Convert a flat row object to PD4's column-value pairs for update.\n * Filters out the _pk field.\n */\nexport function toColumnValues(\n flat: Row,\n): { column: string; value: string | number | boolean | null }[] {\n return Object.entries(flat)\n .filter(([k]) => k !== '_pk')\n .map(([column, value]) => ({ column, value }))\n}\n","import type { PD4Client } from './client'\nimport { flattenRow, toColumnValues } from './utils'\nimport type { Row, FetchResult, PD4Row } from './types'\n\n/**\n * A buffered view wrapping a PD4 table.\n * Changes are accumulated locally and only become visible\n * to other clients after commit().\n */\nexport class PD4Buffer {\n private client: PD4Client\n readonly handle: string\n readonly table: string\n\n constructor(client: PD4Client, handle: string, table: string) {\n this.client = client\n this.handle = handle\n this.table = table\n }\n\n /** Update a row within the buffer (not visible to others until commit). */\n async update(key: number, values: Row): Promise<void> {\n const pairs = toColumnValues(values)\n await this._request(`views/${this.handle}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values: pairs }),\n })\n }\n\n /** Fetch rows from the buffer (sees uncommitted changes). */\n async fetch(offset = 0, limit = 100): Promise<FetchResult> {\n const data = await this._request<{ rows: PD4Row[]; total: number }>(\n `views/${this.handle}/rows?offset=${offset}&limit=${limit}`,\n )\n return {\n rows: data.rows.map(flattenRow),\n total: data.total,\n }\n }\n\n /** Commit all buffered writes atomically. Fires SSE events. */\n async commit(): Promise<void> {\n await this._request(`views/${this.handle}/commit`, { method: 'POST' })\n }\n\n /** Rollback all buffered writes (discard pending changes). */\n async rollback(): Promise<void> {\n await this._request(`views/${this.handle}/rollback`, { method: 'POST' })\n }\n\n // --- Internal ----------------------------------------------------------\n\n private async _request<T>(path: string, init?: RequestInit): Promise<T> {\n const url = `${this.client.url}/v1/${this.client.db}/${path}`\n const res = await fetch(url, {\n ...init,\n headers: { 'Content-Type': 'application/json', ...init?.headers },\n })\n if (!res.ok) {\n const text = await res.text()\n throw new Error(`PD4 Buffer ${init?.method ?? 'GET'} ${url}: ${res.status} ${text}`)\n }\n if (res.status === 204) return undefined as T\n return res.json()\n }\n}\n","import { PD4Error } from './errors'\nimport { flattenRow, toColumnar, toColumnValues } from './utils'\nimport { PD4Buffer } from './buffer'\nimport type {\n ColumnInfo,\n TableInfo,\n PD4Row,\n Row,\n FetchOptions,\n FetchResult,\n InsertResult,\n ConnectionStatus,\n InsertHandler,\n UpdateHandler,\n DeleteHandler,\n WildcardInsertHandler,\n WildcardUpdateHandler,\n WildcardDeleteHandler,\n AnyEventHandler,\n StatusHandler,\n PD4ClientOptions,\n} from './types'\n\n// ---------------------------------------------------------------------------\n// Internal types for SSE handler registry\n// ---------------------------------------------------------------------------\n\ninterface TableHandlers {\n insert: InsertHandler[]\n update: UpdateHandler[]\n delete: DeleteHandler[]\n}\n\ninterface WildcardHandlers {\n insert: WildcardInsertHandler[]\n update: WildcardUpdateHandler[]\n delete: WildcardDeleteHandler[]\n}\n\n// ---------------------------------------------------------------------------\n// PD4Client\n// ---------------------------------------------------------------------------\n\nexport class PD4Client {\n readonly url: string\n readonly db: string\n\n private EventSourceCtor: (new (url: string) => EventSource) | null\n private eventSource: EventSource | null = null\n private handlers = new Map<string, TableHandlers>()\n private wildcardHandlers: WildcardHandlers = { insert: [], update: [], delete: [] }\n private anyHandlers: AnyEventHandler[] = []\n private statusHandlers: StatusHandler[] = []\n private _status: ConnectionStatus = 'connecting'\n\n constructor(url: string, db: string, options?: PD4ClientOptions) {\n this.url = url.replace(/\\/$/, '')\n this.db = db\n this.EventSourceCtor = options?.EventSource\n ?? (typeof globalThis !== 'undefined' && globalThis.EventSource\n ? globalThis.EventSource\n : null)\n }\n\n // --- URL helpers -------------------------------------------------------\n\n private get v1() {\n return `${this.url}/v1/${this.db}`\n }\n\n private get api() {\n return `${this.url}/api/${this.db}`\n }\n\n // --- HTTP helper -------------------------------------------------------\n\n private async request<T>(url: string, init?: RequestInit): Promise<T> {\n const res = await fetch(url, {\n ...init,\n headers: { 'Content-Type': 'application/json', ...init?.headers },\n })\n if (!res.ok) {\n const text = await res.text()\n throw new PD4Error(\n `PD4 ${init?.method ?? 'GET'} ${url}: ${res.status} ${text}`,\n res.status,\n url,\n )\n }\n if (res.status === 204) return undefined as T\n return res.json()\n }\n\n // --- Health ------------------------------------------------------------\n\n /** Check if PD4 is reachable */\n async ping(): Promise<boolean> {\n try {\n const res = await fetch(`${this.url}/health`)\n return res.ok\n } catch {\n return false\n }\n }\n\n // --- Database ----------------------------------------------------------\n\n /** Ensure the database is open */\n async ensureDatabase(): Promise<void> {\n await this.request(`${this.url}/v1/db`, {\n method: 'POST',\n body: JSON.stringify({ name: this.db }),\n })\n }\n\n // --- Tables ------------------------------------------------------------\n\n /** List all table names */\n async listTables(): Promise<string[]> {\n const data = await this.request<{ tables: string[] }>(`${this.v1}/tables`)\n return data.tables\n }\n\n /** Get table info */\n async getTable(name: string): Promise<TableInfo> {\n return this.request<TableInfo>(`${this.v1}/tables/${name}`)\n }\n\n /** Create a table (with auto_pk) */\n async createTable(name: string): Promise<void> {\n await this.request(`${this.v1}/tables`, {\n method: 'POST',\n body: JSON.stringify({ name, auto_pk: true }),\n })\n }\n\n /** Drop a table */\n async dropTable(name: string): Promise<void> {\n await this.request(`${this.v1}/tables/${name}`, { method: 'DELETE' })\n }\n\n /** Add a column to a table */\n async addColumn(table: string, name: string, dtype: string): Promise<void> {\n await this.request(`${this.v1}/tables/${table}/columns`, {\n method: 'POST',\n body: JSON.stringify({ name, dtype }),\n })\n }\n\n // --- Rows (flat objects in, flat objects out) ---------------------------\n\n /** Insert one or more rows (flat objects). Returns keys and count. */\n async insert(table: string, rows: Row | Row[]): Promise<InsertResult> {\n const arr = Array.isArray(rows) ? rows : [rows]\n const { columns, rows: colRows } = toColumnar(arr)\n return this.request<InsertResult>(`${this.v1}/tables/${table}/rows`, {\n method: 'POST',\n body: JSON.stringify({ columns, rows: colRows }),\n })\n }\n\n /** Update a single row by key with a flat object of changed columns. */\n async update(table: string, key: number, values: Row): Promise<void> {\n const pairs = toColumnValues(values)\n await this.request(`${this.v1}/views/${table}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values: pairs }),\n })\n }\n\n /** Delete rows by keys. */\n async delete(table: string, keys: number | number[]): Promise<void> {\n const arr = Array.isArray(keys) ? keys : [keys]\n await this.request(`${this.v1}/views/${table}/rows/delete`, {\n method: 'POST',\n body: JSON.stringify({ keys: arr }),\n })\n }\n\n /** Fetch rows from a table/view. Returns flat Row objects. */\n async fetch(table: string, opts?: FetchOptions): Promise<FetchResult> {\n const offset = opts?.offset ?? 0\n const limit = opts?.limit ?? 1000\n const data = await this.request<{ rows: PD4Row[]; total: number }>(\n `${this.v1}/views/${table}/rows?offset=${offset}&limit=${limit}`,\n )\n return {\n rows: data.rows.map(flattenRow),\n total: data.total,\n }\n }\n\n // --- Raw row access (PD4 wire format) ----------------------------------\n\n /** Insert rows in columnar format (low-level). */\n async insertColumnar(\n table: string,\n columns: string[],\n rows: (string | number | boolean | null)[][],\n ): Promise<InsertResult> {\n return this.request<InsertResult>(`${this.v1}/tables/${table}/rows`, {\n method: 'POST',\n body: JSON.stringify({ columns, rows }),\n })\n }\n\n /** Update a row by key with column-value pairs (low-level). */\n async updateRaw(\n table: string,\n key: number,\n values: { column: string; value: string | number | boolean | null }[],\n ): Promise<void> {\n await this.request(`${this.v1}/views/${table}/rows/update`, {\n method: 'POST',\n body: JSON.stringify({ key, values }),\n })\n }\n\n /** Fetch rows in PD4 wire format (low-level). */\n async fetchRaw(\n table: string,\n offset = 0,\n limit = 1000,\n ): Promise<{ rows: PD4Row[]; total: number }> {\n return this.request(`${this.v1}/views/${table}/rows?offset=${offset}&limit=${limit}`)\n }\n\n // --- Buffer ------------------------------------------------------------\n\n /** Create a buffered view wrapping a table. */\n async buffer(table: string): Promise<PD4Buffer> {\n const info = await this.request<{ handle: string; source: string; row_count: number }>(\n `${this.v1}/views/${table}/buffer`,\n { method: 'POST' },\n )\n return new PD4Buffer(this, info.handle, table)\n }\n\n // --- SSE ---------------------------------------------------------------\n\n /** Get current SSE connection status. */\n get status(): ConnectionStatus {\n return this._status\n }\n\n /** Connect to the SSE event stream. Called automatically on first on(). */\n connect(): void {\n if (this.eventSource) return\n if (!this.EventSourceCtor) {\n throw new Error(\n 'EventSource is not available. In Node.js, pass { EventSource } from the \"eventsource\" package to the PD4Client constructor.',\n )\n }\n\n this._setStatus('connecting')\n const sse = new this.EventSourceCtor(`${this.api}/events`)\n this.eventSource = sse\n\n sse.addEventListener('session', () => {\n this._setStatus('connected')\n })\n\n sse.addEventListener('insert', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n const newValues: Row[] = data.new_values ?? []\n this._dispatch(table, 'insert', keys, newValues, [])\n })\n\n sse.addEventListener('update', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n const newValues: Row[] = data.new_values ?? []\n const oldValues: Row[] = data.old_values ?? []\n this._dispatch(table, 'update', keys, newValues, oldValues)\n })\n\n sse.addEventListener('delete', (e: MessageEvent) => {\n const data = JSON.parse(e.data)\n const table = data.handle as string\n const keys: number[] = data.keys ?? []\n this._dispatch(table, 'delete', keys, [], [])\n })\n\n sse.onerror = () => {\n this._setStatus('error')\n }\n }\n\n /** Disconnect from the SSE event stream. */\n disconnect(): void {\n this.eventSource?.close()\n this.eventSource = null\n this._setStatus('connecting')\n }\n\n /**\n * Subscribe to SSE events for a specific table, or use '*' for all tables.\n * Auto-connects on first call. Returns an unsubscribe function.\n *\n * Table-specific: handler receives (keys, rows/newValues/oldValues)\n * Wildcard '*': handler receives (table, keys, rows/newValues/oldValues)\n */\n on(table: '*', event: 'insert', handler: WildcardInsertHandler): () => void\n on(table: '*', event: 'update', handler: WildcardUpdateHandler): () => void\n on(table: '*', event: 'delete', handler: WildcardDeleteHandler): () => void\n on(table: string, event: 'insert', handler: InsertHandler): () => void\n on(table: string, event: 'update', handler: UpdateHandler): () => void\n on(table: string, event: 'delete', handler: DeleteHandler): () => void\n on(\n table: string,\n event: 'insert' | 'update' | 'delete',\n handler:\n | InsertHandler | UpdateHandler | DeleteHandler\n | WildcardInsertHandler | WildcardUpdateHandler | WildcardDeleteHandler,\n ): () => void {\n // Auto-connect\n if (!this.eventSource) this.connect()\n\n // Wildcard: register in wildcardHandlers\n if (table === '*') {\n const list = this.wildcardHandlers[event] as unknown[]\n list.push(handler)\n return () => {\n const idx = list.indexOf(handler)\n if (idx >= 0) list.splice(idx, 1)\n }\n }\n\n // Table-specific\n let tableHandlers = this.handlers.get(table)\n if (!tableHandlers) {\n tableHandlers = { insert: [], update: [], delete: [] }\n this.handlers.set(table, tableHandlers)\n }\n\n const list = tableHandlers[event] as unknown[]\n list.push(handler)\n\n return () => {\n const idx = list.indexOf(handler)\n if (idx >= 0) list.splice(idx, 1)\n }\n }\n\n /**\n * Subscribe to all SSE events on all tables.\n * Handler receives (table, event, keys, newValues, oldValues).\n * Auto-connects on first call. Returns an unsubscribe function.\n */\n onAny(handler: AnyEventHandler): () => void {\n if (!this.eventSource) this.connect()\n\n this.anyHandlers.push(handler)\n return () => {\n const idx = this.anyHandlers.indexOf(handler)\n if (idx >= 0) this.anyHandlers.splice(idx, 1)\n }\n }\n\n /** Subscribe to connection status changes. Returns an unsubscribe function. */\n onStatus(handler: StatusHandler): () => void {\n this.statusHandlers.push(handler)\n return () => {\n const idx = this.statusHandlers.indexOf(handler)\n if (idx >= 0) this.statusHandlers.splice(idx, 1)\n }\n }\n\n // --- SSE internals -----------------------------------------------------\n\n private _setStatus(status: ConnectionStatus) {\n this._status = status\n for (const handler of this.statusHandlers) {\n handler(status)\n }\n }\n\n private _dispatch(\n table: string,\n event: 'insert' | 'update' | 'delete',\n keys: number[],\n newValues: Row[],\n oldValues: Row[],\n ) {\n // Table-specific handlers\n const tableHandlers = this.handlers.get(table)\n if (tableHandlers) {\n if (event === 'insert') {\n for (const h of tableHandlers.insert) h(keys, newValues)\n } else if (event === 'update') {\n for (const h of tableHandlers.update) h(keys, newValues, oldValues)\n } else {\n for (const h of tableHandlers.delete) h(keys)\n }\n }\n\n // Wildcard handlers (on('*', ...))\n if (event === 'insert') {\n for (const h of this.wildcardHandlers.insert) h(table, keys, newValues)\n } else if (event === 'update') {\n for (const h of this.wildcardHandlers.update) h(table, keys, newValues, oldValues)\n } else {\n for (const h of this.wildcardHandlers.delete) h(table, keys)\n }\n\n // onAny handlers\n for (const h of this.anyHandlers) {\n h(table, event, keys, newValues, oldValues)\n }\n }\n}\n\n// Re-export for convenience\nexport type {\n ColumnInfo, TableInfo, PD4Row, Row, FetchOptions, FetchResult, InsertResult,\n WildcardInsertHandler, WildcardUpdateHandler, WildcardDeleteHandler, AnyEventHandler,\n}\n","import { useEffect, useRef } from 'react'\nimport { usePD4 } from './context'\nimport type { InsertHandler, UpdateHandler, DeleteHandler } from '../types'\n\n/**\n * Subscribe to SSE events for a table. Auto-cleans up on unmount.\n *\n * Usage:\n * usePD4Subscribe('metrics', 'insert', (keys, rows) => { ... })\n * usePD4Subscribe('metrics', 'update', (keys, newVals, oldVals) => { ... })\n * usePD4Subscribe('metrics', 'delete', (keys) => { ... })\n */\nexport function usePD4Subscribe(table: string, event: 'insert', handler: InsertHandler): void\nexport function usePD4Subscribe(table: string, event: 'update', handler: UpdateHandler): void\nexport function usePD4Subscribe(table: string, event: 'delete', handler: DeleteHandler): void\nexport function usePD4Subscribe(\n table: string,\n event: 'insert' | 'update' | 'delete',\n handler: InsertHandler | UpdateHandler | DeleteHandler,\n): void {\n const db = usePD4()\n const handlerRef = useRef(handler)\n handlerRef.current = handler\n\n useEffect(() => {\n const wrapper = (...args: unknown[]) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ;(handlerRef.current as any)(...args)\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return db.on(table, event as any, wrapper as any)\n }, [db, table, event])\n}\n","import { useState, useEffect, useCallback, useRef } from 'react'\nimport { usePD4 } from './context'\nimport type { Row, ColumnInfo, ConnectionStatus } from '../types'\n\nconst HIGHLIGHT_MS = 1500\n\nexport interface UsePD4TableOptions {\n limit?: number\n deleteDelay?: number\n}\n\nexport interface UsePD4TableResult {\n rows: Row[]\n total: number\n columns: ColumnInfo[]\n status: ConnectionStatus\n highlights: Record<number, string>\n}\n\n/**\n * Full table state management hook.\n * Handles: initial fetch, SSE subscriptions for insert/update/delete,\n * row state management, highlight tracking, and buffer-commit edge cases.\n */\nexport function usePD4Table(\n table: string,\n opts?: UsePD4TableOptions,\n): UsePD4TableResult {\n const db = usePD4()\n const limit = opts?.limit ?? 1000\n const deleteDelay = opts?.deleteDelay ?? HIGHLIGHT_MS\n\n const [rows, setRows] = useState<Row[]>([])\n const [total, setTotal] = useState(0)\n const [columns, setColumns] = useState<ColumnInfo[]>([])\n const [status, setStatus] = useState<ConnectionStatus>('connecting')\n const [highlights, setHighlights] = useState<Record<number, string>>({})\n\n // Refs for stable callback access\n const tableRef = useRef(table)\n tableRef.current = table\n\n const highlightKeys = useCallback((keys: number[], type: string) => {\n if (keys.length === 0) return\n setHighlights((prev) => {\n const next = { ...prev }\n for (const k of keys) next[k] = type\n return next\n })\n setTimeout(() => {\n setHighlights((prev) => {\n const next = { ...prev }\n for (const k of keys) {\n if (next[k] === type) delete next[k]\n }\n return next\n })\n }, HIGHLIGHT_MS)\n }, [])\n\n const fetchRows = useCallback(async () => {\n try {\n const { rows: fetched, total: t } = await db.fetch(tableRef.current, { offset: 0, limit })\n setRows(fetched)\n setTotal(t)\n } catch {\n // Non-fatal\n }\n }, [db, limit])\n\n useEffect(() => {\n let cancelled = false\n const unsubs: (() => void)[] = []\n\n ;(async () => {\n try {\n // 1. Get column info\n const info = await db.getTable(table)\n if (cancelled) return\n setColumns(info.columns.filter((c) => c.name !== '_pk'))\n\n // 2. Fetch initial rows\n await fetchRows()\n if (cancelled) return\n setStatus('connected')\n\n // 3. SSE subscriptions\n unsubs.push(db.on(table, 'insert', (keys, newValues) => {\n if (cancelled) return\n const newRows = keys.map((k, i) => ({ _pk: k, ...newValues[i] }))\n setRows((prev) => [...prev, ...newRows])\n setTotal((prev) => prev + newRows.length)\n highlightKeys(keys, 'insert')\n }))\n\n unsubs.push(db.on(table, 'update', (keys, newValues) => {\n if (cancelled) return\n\n // Buffer commits send raw scalar values, not flat objects — fall back to refetch\n if (\n newValues.length === 0 ||\n typeof newValues[0] !== 'object' ||\n newValues[0] === null\n ) {\n fetchRows()\n highlightKeys(keys, 'update')\n return\n }\n\n const updateMap = new Map(keys.map((k, i) => [k, newValues[i]]))\n setRows((prev) =>\n prev.map((row) => {\n const changes = updateMap.get(row._pk as number)\n if (!changes) return row\n return { ...row, ...changes }\n }),\n )\n highlightKeys(keys, 'update')\n }))\n\n unsubs.push(db.on(table, 'delete', (keys) => {\n if (cancelled) return\n const keySet = new Set(keys)\n highlightKeys(keys, 'delete')\n setTimeout(() => {\n setRows((prev) => prev.filter((row) => !keySet.has(row._pk as number)))\n setTotal((prev) => Math.max(0, prev - keys.length))\n }, deleteDelay)\n }))\n } catch {\n if (!cancelled) {\n setStatus('error')\n }\n }\n })()\n\n return () => {\n cancelled = true\n for (const unsub of unsubs) unsub()\n }\n }, [db, table, limit, fetchRows, highlightKeys, deleteDelay])\n\n return { rows, total, columns, status, highlights }\n}\n","import { useState, useEffect } from 'react'\nimport { usePD4 } from './context'\nimport type { Row } from '../types'\n\nexport interface UsePD4QueryResult {\n rows: Row[]\n total: number\n loading: boolean\n error: string | null\n refetch: () => void\n}\n\n/**\n * One-shot query hook. Fetches rows from a table once (or on refetch).\n */\nexport function usePD4Query(\n table: string,\n offset = 0,\n limit = 1000,\n): UsePD4QueryResult {\n const db = usePD4()\n const [rows, setRows] = useState<Row[]>([])\n const [total, setTotal] = useState(0)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n const [attempt, setAttempt] = useState(0)\n\n useEffect(() => {\n let cancelled = false\n setLoading(true)\n setError(null)\n\n db.fetch(table, { offset, limit })\n .then((result) => {\n if (cancelled) return\n setRows(result.rows)\n setTotal(result.total)\n setLoading(false)\n })\n .catch((err) => {\n if (cancelled) return\n setError(err instanceof Error ? err.message : 'Query failed')\n setLoading(false)\n })\n\n return () => { cancelled = true }\n }, [db, table, offset, limit, attempt])\n\n return {\n rows,\n total,\n loading,\n error,\n refetch: () => setAttempt((a) => a + 1),\n }\n}\n"],"mappings":"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","names":["list","useRef","useRef","useEffect","useRef","useRef","useEffect","useState","useEffect","useState","useEffect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@powerdata/pd4",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "JavaScript client library for PD4 — REST, SSE, and React hooks",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -18,7 +18,9 @@
18
18
  "require": "./dist/react/index.cjs"
19
19
  }
20
20
  },
21
- "files": ["dist"],
21
+ "files": [
22
+ "dist"
23
+ ],
22
24
  "scripts": {
23
25
  "build": "tsup",
24
26
  "dev": "tsup --watch"
@@ -27,7 +29,9 @@
27
29
  "react": ">=18"
28
30
  },
29
31
  "peerDependenciesMeta": {
30
- "react": { "optional": true }
32
+ "react": {
33
+ "optional": true
34
+ }
31
35
  },
32
36
  "devDependencies": {
33
37
  "react": "^18.2.0",