@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 +38 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -2
- package/dist/index.d.ts +22 -2
- package/dist/index.js +38 -4
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +38 -4
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +23 -1
- package/dist/react/index.d.ts +23 -1
- package/dist/react/index.js +38 -4
- package/dist/react/index.js.map +1 -1
- package/package.json +7 -3
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 (
|
|
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
|
|
370
|
+
for (const h of this.wildcardHandlers.insert) h(table, keys, newValues);
|
|
340
371
|
} else if (event === "update") {
|
|
341
|
-
for (const h of
|
|
372
|
+
for (const h of this.wildcardHandlers.update) h(table, keys, newValues, oldValues);
|
|
342
373
|
} else {
|
|
343
|
-
for (const h of
|
|
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
|
};
|
package/dist/index.cjs.map
CHANGED
|
@@ -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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACCO,IAAM,WAAN,cAAuB,MAAM;AAAA,EAIlC,YAAY,SAAiB,QAAiB,KAAc;AAC1D,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,MAAM;AAAA,EACb;AACF;;;ACRO,SAAS,WAAW,KAAkB;AAC3C,QAAM,OAAY,EAAE,KAAK,IAAI,IAAI;AACjC,aAAW,MAAM,IAAI,QAAQ;AAC3B,SAAK,GAAG,MAAM,IAAI,GAAG;AAAA,EACvB;AACA,SAAO;AACT;AAMO,SAAS,WACd,UACA,aACqE;AACrE,MAAI,SAAS,WAAW,EAAG,QAAO,EAAE,SAAS,CAAC,GAAG,MAAM,CAAC,EAAE;AAC1D,QAAM,OAAO,eAAe,OAAO,KAAK,SAAS,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,MAAM,KAAK;AAC9E,QAAM,OAAO,SAAS,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,IAAI,CAAC;AAClE,SAAO,EAAE,SAAS,MAAM,KAAK;AAC/B;AAMO,SAAS,eACd,MAC+D;AAC/D,SAAO,OAAO,QAAQ,IAAI,EACvB,OAAO,CAAC,CAAC,CAAC,MAAM,MAAM,KAAK,EAC3B,IAAI,CAAC,CAAC,QAAQ,KAAK,OAAO,EAAE,QAAQ,MAAM,EAAE;AACjD;;;AC1BO,IAAM,YAAN,MAAgB;AAAA,EAKrB,YAAY,QAAmB,QAAgB,OAAe;AAC5D,SAAK,SAAS;AACd,SAAK,SAAS;AACd,SAAK,QAAQ;AAAA,EACf;AAAA;AAAA,EAGA,MAAM,OAAO,KAAa,QAA4B;AACpD,UAAM,QAAQ,eAAe,MAAM;AACnC,UAAM,KAAK,SAAS,SAAS,KAAK,MAAM,gBAAgB;AAAA,MACtD,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,KAAK,QAAQ,MAAM,CAAC;AAAA,IAC7C,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,MAAM,SAAS,GAAG,QAAQ,KAA2B;AACzD,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB,SAAS,KAAK,MAAM,gBAAgB,MAAM,UAAU,KAAK;AAAA,IAC3D;AACA,WAAO;AAAA,MACL,MAAM,KAAK,KAAK,IAAI,UAAU;AAAA,MAC9B,OAAO,KAAK;AAAA,IACd;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,SAAwB;AAC5B,UAAM,KAAK,SAAS,SAAS,KAAK,MAAM,WAAW,EAAE,QAAQ,OAAO,CAAC;AAAA,EACvE;AAAA;AAAA,EAGA,MAAM,WAA0B;AAC9B,UAAM,KAAK,SAAS,SAAS,KAAK,MAAM,aAAa,EAAE,QAAQ,OAAO,CAAC;AAAA,EACzE;AAAA;AAAA,EAIA,MAAc,SAAY,MAAc,MAAgC;AACtE,UAAM,MAAM,GAAG,KAAK,OAAO,GAAG,OAAO,KAAK,OAAO,EAAE,IAAI,IAAI;AAC3D,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,GAAG;AAAA,MACH,SAAS,EAAE,gBAAgB,oBAAoB,GAAG,MAAM,QAAQ;AAAA,IAClE,CAAC;AACD,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,YAAM,IAAI,MAAM,cAAc,MAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,MAAM,IAAI,IAAI,EAAE;AAAA,IACrF;AACA,QAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,WAAO,IAAI,KAAK;AAAA,EAClB;AACF;;;ACtBO,IAAM,YAAN,MAAgB;AAAA,EAYrB,YAAY,KAAa,IAAY,SAA4B;AAPjE,SAAQ,cAAkC;AAC1C,SAAQ,WAAW,oBAAI,IAA2B;AAClD,SAAQ,mBAAqC,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE;AAClF,SAAQ,cAAiC,CAAC;AAC1C,SAAQ,iBAAkC,CAAC;AAC3C,SAAQ,UAA4B;AAGlC,SAAK,MAAM,IAAI,QAAQ,OAAO,EAAE;AAChC,SAAK,KAAK;AACV,SAAK,kBAAkB,SAAS,gBAC1B,OAAO,eAAe,eAAe,WAAW,cAChD,WAAW,cACX;AAAA,EACR;AAAA;AAAA,EAIA,IAAY,KAAK;AACf,WAAO,GAAG,KAAK,GAAG,OAAO,KAAK,EAAE;AAAA,EAClC;AAAA,EAEA,IAAY,MAAM;AAChB,WAAO,GAAG,KAAK,GAAG,QAAQ,KAAK,EAAE;AAAA,EACnC;AAAA;AAAA,EAIA,MAAc,QAAW,KAAa,MAAgC;AACpE,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,GAAG;AAAA,MACH,SAAS,EAAE,gBAAgB,oBAAoB,GAAG,MAAM,QAAQ;AAAA,IAClE,CAAC;AACD,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,YAAM,IAAI;AAAA,QACR,OAAO,MAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,MAAM,IAAI,IAAI;AAAA,QAC1D,IAAI;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AACA,QAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,WAAO,IAAI,KAAK;AAAA,EAClB;AAAA;AAAA;AAAA,EAKA,MAAM,OAAyB;AAC7B,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,KAAK,GAAG,SAAS;AAC5C,aAAO,IAAI;AAAA,IACb,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAgC;AACpC,UAAM,KAAK,QAAQ,GAAG,KAAK,GAAG,UAAU;AAAA,MACtC,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,MAAM,KAAK,GAAG,CAAC;AAAA,IACxC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA,EAKA,MAAM,aAAgC;AACpC,UAAM,OAAO,MAAM,KAAK,QAA8B,GAAG,KAAK,EAAE,SAAS;AACzE,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,MAAM,SAAS,MAAkC;AAC/C,WAAO,KAAK,QAAmB,GAAG,KAAK,EAAE,WAAW,IAAI,EAAE;AAAA,EAC5D;AAAA;AAAA,EAGA,MAAM,YAAY,MAA6B;AAC7C,UAAM,KAAK,QAAQ,GAAG,KAAK,EAAE,WAAW;AAAA,MACtC,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,MAAM,SAAS,KAAK,CAAC;AAAA,IAC9C,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,UAAU,MAA6B;AAC3C,UAAM,KAAK,QAAQ,GAAG,KAAK,EAAE,WAAW,IAAI,IAAI,EAAE,QAAQ,SAAS,CAAC;AAAA,EACtE;AAAA;AAAA,EAGA,MAAM,UAAU,OAAe,MAAc,OAA8B;AACzE,UAAM,KAAK,QAAQ,GAAG,KAAK,EAAE,WAAW,KAAK,YAAY;AAAA,MACvD,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;AAAA,IACtC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA,EAKA,MAAM,OAAO,OAAe,MAA0C;AACpE,UAAM,MAAM,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI;AAC9C,UAAM,EAAE,SAAS,MAAM,QAAQ,IAAI,WAAW,GAAG;AACjD,WAAO,KAAK,QAAsB,GAAG,KAAK,EAAE,WAAW,KAAK,SAAS;AAAA,MACnE,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,SAAS,MAAM,QAAQ,CAAC;AAAA,IACjD,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,OAAO,OAAe,KAAa,QAA4B;AACnE,UAAM,QAAQ,eAAe,MAAM;AACnC,UAAM,KAAK,QAAQ,GAAG,KAAK,EAAE,UAAU,KAAK,gBAAgB;AAAA,MAC1D,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,KAAK,QAAQ,MAAM,CAAC;AAAA,IAC7C,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,OAAO,OAAe,MAAwC;AAClE,UAAM,MAAM,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI;AAC9C,UAAM,KAAK,QAAQ,GAAG,KAAK,EAAE,UAAU,KAAK,gBAAgB;AAAA,MAC1D,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,MAAM,IAAI,CAAC;AAAA,IACpC,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,MAAM,OAAe,MAA2C;AACpE,UAAM,SAAS,MAAM,UAAU;AAC/B,UAAM,QAAQ,MAAM,SAAS;AAC7B,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB,GAAG,KAAK,EAAE,UAAU,KAAK,gBAAgB,MAAM,UAAU,KAAK;AAAA,IAChE;AACA,WAAO;AAAA,MACL,MAAM,KAAK,KAAK,IAAI,UAAU;AAAA,MAC9B,OAAO,KAAK;AAAA,IACd;AAAA,EACF;AAAA;AAAA;AAAA,EAKA,MAAM,eACJ,OACA,SACA,MACuB;AACvB,WAAO,KAAK,QAAsB,GAAG,KAAK,EAAE,WAAW,KAAK,SAAS;AAAA,MACnE,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,SAAS,KAAK,CAAC;AAAA,IACxC,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,UACJ,OACA,KACA,QACe;AACf,UAAM,KAAK,QAAQ,GAAG,KAAK,EAAE,UAAU,KAAK,gBAAgB;AAAA,MAC1D,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,KAAK,OAAO,CAAC;AAAA,IACtC,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,SACJ,OACA,SAAS,GACT,QAAQ,KACoC;AAC5C,WAAO,KAAK,QAAQ,GAAG,KAAK,EAAE,UAAU,KAAK,gBAAgB,MAAM,UAAU,KAAK,EAAE;AAAA,EACtF;AAAA;AAAA;AAAA,EAKA,MAAM,OAAO,OAAmC;AAC9C,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB,GAAG,KAAK,EAAE,UAAU,KAAK;AAAA,MACzB,EAAE,QAAQ,OAAO;AAAA,IACnB;AACA,WAAO,IAAI,UAAU,MAAM,KAAK,QAAQ,KAAK;AAAA,EAC/C;AAAA;AAAA;AAAA,EAKA,IAAI,SAA2B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,UAAgB;AACd,QAAI,KAAK,YAAa;AACtB,QAAI,CAAC,KAAK,iBAAiB;AACzB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,SAAK,WAAW,YAAY;AAC5B,UAAM,MAAM,IAAI,KAAK,gBAAgB,GAAG,KAAK,GAAG,SAAS;AACzD,SAAK,cAAc;AAEnB,QAAI,iBAAiB,WAAW,MAAM;AACpC,WAAK,WAAW,WAAW;AAAA,IAC7B,CAAC;AAED,QAAI,iBAAiB,UAAU,CAAC,MAAoB;AAClD,YAAM,OAAO,KAAK,MAAM,EAAE,IAAI;AAC9B,YAAM,QAAQ,KAAK;AACnB,YAAM,OAAiB,KAAK,QAAQ,CAAC;AACrC,YAAM,YAAmB,KAAK,cAAc,CAAC;AAC7C,WAAK,UAAU,OAAO,UAAU,MAAM,WAAW,CAAC,CAAC;AAAA,IACrD,CAAC;AAED,QAAI,iBAAiB,UAAU,CAAC,MAAoB;AAClD,YAAM,OAAO,KAAK,MAAM,EAAE,IAAI;AAC9B,YAAM,QAAQ,KAAK;AACnB,YAAM,OAAiB,KAAK,QAAQ,CAAC;AACrC,YAAM,YAAmB,KAAK,cAAc,CAAC;AAC7C,YAAM,YAAmB,KAAK,cAAc,CAAC;AAC7C,WAAK,UAAU,OAAO,UAAU,MAAM,WAAW,SAAS;AAAA,IAC5D,CAAC;AAED,QAAI,iBAAiB,UAAU,CAAC,MAAoB;AAClD,YAAM,OAAO,KAAK,MAAM,EAAE,IAAI;AAC9B,YAAM,QAAQ,KAAK;AACnB,YAAM,OAAiB,KAAK,QAAQ,CAAC;AACrC,WAAK,UAAU,OAAO,UAAU,MAAM,CAAC,GAAG,CAAC,CAAC;AAAA,IAC9C,CAAC;AAED,QAAI,UAAU,MAAM;AAClB,WAAK,WAAW,OAAO;AAAA,IACzB;AAAA,EACF;AAAA;AAAA,EAGA,aAAmB;AACjB,SAAK,aAAa,MAAM;AACxB,SAAK,cAAc;AACnB,SAAK,WAAW,YAAY;AAAA,EAC9B;AAAA,EAeA,GACE,OACA,OACA,SAGY;AAEZ,QAAI,CAAC,KAAK,YAAa,MAAK,QAAQ;AAGpC,QAAI,UAAU,KAAK;AACjB,YAAMA,QAAO,KAAK,iBAAiB,KAAK;AACxC,MAAAA,MAAK,KAAK,OAAO;AACjB,aAAO,MAAM;AACX,cAAM,MAAMA,MAAK,QAAQ,OAAO;AAChC,YAAI,OAAO,EAAG,CAAAA,MAAK,OAAO,KAAK,CAAC;AAAA,MAClC;AAAA,IACF;AAGA,QAAI,gBAAgB,KAAK,SAAS,IAAI,KAAK;AAC3C,QAAI,CAAC,eAAe;AAClB,sBAAgB,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE;AACrD,WAAK,SAAS,IAAI,OAAO,aAAa;AAAA,IACxC;AAEA,UAAM,OAAO,cAAc,KAAK;AAChC,SAAK,KAAK,OAAO;AAEjB,WAAO,MAAM;AACX,YAAM,MAAM,KAAK,QAAQ,OAAO;AAChC,UAAI,OAAO,EAAG,MAAK,OAAO,KAAK,CAAC;AAAA,IAClC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,SAAsC;AAC1C,QAAI,CAAC,KAAK,YAAa,MAAK,QAAQ;AAEpC,SAAK,YAAY,KAAK,OAAO;AAC7B,WAAO,MAAM;AACX,YAAM,MAAM,KAAK,YAAY,QAAQ,OAAO;AAC5C,UAAI,OAAO,EAAG,MAAK,YAAY,OAAO,KAAK,CAAC;AAAA,IAC9C;AAAA,EACF;AAAA;AAAA,EAGA,SAAS,SAAoC;AAC3C,SAAK,eAAe,KAAK,OAAO;AAChC,WAAO,MAAM;AACX,YAAM,MAAM,KAAK,eAAe,QAAQ,OAAO;AAC/C,UAAI,OAAO,EAAG,MAAK,eAAe,OAAO,KAAK,CAAC;AAAA,IACjD;AAAA,EACF;AAAA;AAAA,EAIQ,WAAW,QAA0B;AAC3C,SAAK,UAAU;AACf,eAAW,WAAW,KAAK,gBAAgB;AACzC,cAAQ,MAAM;AAAA,IAChB;AAAA,EACF;AAAA,EAEQ,UACN,OACA,OACA,MACA,WACA,WACA;AAEA,UAAM,gBAAgB,KAAK,SAAS,IAAI,KAAK;AAC7C,QAAI,eAAe;AACjB,UAAI,UAAU,UAAU;AACtB,mBAAW,KAAK,cAAc,OAAQ,GAAE,MAAM,SAAS;AAAA,MACzD,WAAW,UAAU,UAAU;AAC7B,mBAAW,KAAK,cAAc,OAAQ,GAAE,MAAM,WAAW,SAAS;AAAA,MACpE,OAAO;AACL,mBAAW,KAAK,cAAc,OAAQ,GAAE,IAAI;AAAA,MAC9C;AAAA,IACF;AAGA,QAAI,UAAU,UAAU;AACtB,iBAAW,KAAK,KAAK,iBAAiB,OAAQ,GAAE,OAAO,MAAM,SAAS;AAAA,IACxE,WAAW,UAAU,UAAU;AAC7B,iBAAW,KAAK,KAAK,iBAAiB,OAAQ,GAAE,OAAO,MAAM,WAAW,SAAS;AAAA,IACnF,OAAO;AACL,iBAAW,KAAK,KAAK,iBAAiB,OAAQ,GAAE,OAAO,IAAI;AAAA,IAC7D;AAGA,eAAW,KAAK,KAAK,aAAa;AAChC,QAAE,OAAO,OAAO,MAAM,WAAW,SAAS;AAAA,IAC5C;AAAA,EACF;AACF;","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 (
|
|
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
|
|
339
|
+
for (const h of this.wildcardHandlers.insert) h(table, keys, newValues);
|
|
309
340
|
} else if (event === "update") {
|
|
310
|
-
for (const h of
|
|
341
|
+
for (const h of this.wildcardHandlers.update) h(table, keys, newValues, oldValues);
|
|
311
342
|
} else {
|
|
312
|
-
for (const h of
|
|
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"]}
|
package/dist/react/index.cjs
CHANGED
|
@@ -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 (
|
|
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
|
|
372
|
+
for (const h of this.wildcardHandlers.insert) h(table, keys, newValues);
|
|
342
373
|
} else if (event === "update") {
|
|
343
|
-
for (const h of
|
|
374
|
+
for (const h of this.wildcardHandlers.update) h(table, keys, newValues, oldValues);
|
|
344
375
|
} else {
|
|
345
|
-
for (const h of
|
|
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
|
};
|
package/dist/react/index.cjs.map
CHANGED
|
@@ -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"]}
|
package/dist/react/index.d.cts
CHANGED
|
@@ -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;
|
package/dist/react/index.d.ts
CHANGED
|
@@ -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;
|
package/dist/react/index.js
CHANGED
|
@@ -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 (
|
|
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
|
|
342
|
+
for (const h of this.wildcardHandlers.insert) h(table, keys, newValues);
|
|
312
343
|
} else if (event === "update") {
|
|
313
|
-
for (const h of
|
|
344
|
+
for (const h of this.wildcardHandlers.update) h(table, keys, newValues, oldValues);
|
|
314
345
|
} else {
|
|
315
|
-
for (const h of
|
|
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
|
};
|
package/dist/react/index.js.map
CHANGED
|
@@ -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.
|
|
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": [
|
|
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": {
|
|
32
|
+
"react": {
|
|
33
|
+
"optional": true
|
|
34
|
+
}
|
|
31
35
|
},
|
|
32
36
|
"devDependencies": {
|
|
33
37
|
"react": "^18.2.0",
|