@palbase/db 0.1.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,229 @@
1
+ // src/admin-validation.ts
2
+ var IDENTIFIER_RE = /^[a-zA-Z_][a-zA-Z0-9_.]*$/;
3
+ function validateIdentifier(value, label) {
4
+ if (!IDENTIFIER_RE.test(value)) {
5
+ throw new Error(
6
+ `Invalid ${label}: "${value}". Identifiers must match ${IDENTIFIER_RE.source}`
7
+ );
8
+ }
9
+ }
10
+
11
+ // src/admin-columns.ts
12
+ var ColumnsClient = class {
13
+ httpClient;
14
+ constructor(httpClient) {
15
+ this.httpClient = httpClient;
16
+ }
17
+ /**
18
+ * Create a new column on the given table. Sends `POST /v1/meta/columns`.
19
+ *
20
+ * The column name is validated against the standard SQL identifier regex.
21
+ * The `type` field is forwarded as-is to postgres-meta — callers SHOULD
22
+ * restrict types to a known allowlist before calling this.
23
+ */
24
+ async create(def) {
25
+ validateIdentifier(def.name, "column name");
26
+ const response = await this.httpClient.request("POST", "/v1/meta/columns", {
27
+ body: def
28
+ });
29
+ if (response.error) {
30
+ throw response.error;
31
+ }
32
+ return response.data;
33
+ }
34
+ /**
35
+ * Drop a column. Sends `DELETE /v1/meta/columns/:tableId.:ordinalPosition`.
36
+ *
37
+ * postgres-meta identifies columns by `{tableId}.{ordinalPosition}` (the
38
+ * column's 1-based attnum within its table). Use `TablesClient.get(id)` to
39
+ * find a column id from a column name if needed.
40
+ */
41
+ async drop(columnId, options) {
42
+ if (!/^\d+\.\d+$/.test(columnId)) {
43
+ throw new Error(
44
+ `Invalid column id: "${columnId}". Expected format "{tableId}.{ordinalPosition}"`
45
+ );
46
+ }
47
+ const params = new URLSearchParams();
48
+ if (options?.cascade) {
49
+ params.set("cascade", "true");
50
+ }
51
+ const queryString = params.toString();
52
+ const path = queryString ? `/v1/meta/columns/${columnId}?${queryString}` : `/v1/meta/columns/${columnId}`;
53
+ const response = await this.httpClient.request("DELETE", path);
54
+ if (response.error) {
55
+ throw response.error;
56
+ }
57
+ return response.data;
58
+ }
59
+ };
60
+
61
+ // src/admin-schemas.ts
62
+ var SchemasClient = class {
63
+ httpClient;
64
+ constructor(httpClient) {
65
+ this.httpClient = httpClient;
66
+ }
67
+ async list() {
68
+ const response = await this.httpClient.request("GET", "/v1/meta/schemas");
69
+ if (response.error) {
70
+ throw response.error;
71
+ }
72
+ return response.data ?? [];
73
+ }
74
+ async create(name) {
75
+ validateIdentifier(name, "schema name");
76
+ const response = await this.httpClient.request("POST", "/v1/meta/schemas", {
77
+ body: { name }
78
+ });
79
+ if (response.error) {
80
+ throw response.error;
81
+ }
82
+ return response.data;
83
+ }
84
+ async drop(name, options) {
85
+ validateIdentifier(name, "schema name");
86
+ const schemas = await this.list();
87
+ const schema = schemas.find((s) => s.name === name);
88
+ if (!schema) {
89
+ throw new Error(`Schema "${name}" not found`);
90
+ }
91
+ const params = new URLSearchParams();
92
+ if (options?.cascade) {
93
+ params.set("cascade", "true");
94
+ }
95
+ const queryString = params.toString();
96
+ const path = queryString ? `/v1/meta/schemas/${schema.id}?${queryString}` : `/v1/meta/schemas/${schema.id}`;
97
+ const response = await this.httpClient.request("DELETE", path);
98
+ if (response.error) {
99
+ throw response.error;
100
+ }
101
+ }
102
+ };
103
+
104
+ // src/admin-tables.ts
105
+ var TablesClient = class {
106
+ httpClient;
107
+ constructor(httpClient) {
108
+ this.httpClient = httpClient;
109
+ }
110
+ async list(options) {
111
+ const params = new URLSearchParams();
112
+ if (options?.schema) {
113
+ validateIdentifier(options.schema, "schema name");
114
+ params.set("included_schemas", options.schema);
115
+ }
116
+ const queryString = params.toString();
117
+ const path = queryString ? `/v1/meta/tables?${queryString}` : "/v1/meta/tables";
118
+ const response = await this.httpClient.request("GET", path);
119
+ if (response.error) {
120
+ throw response.error;
121
+ }
122
+ return response.data ?? [];
123
+ }
124
+ async get(id) {
125
+ const response = await this.httpClient.request("GET", `/v1/meta/tables/${id}`);
126
+ if (response.error) {
127
+ throw response.error;
128
+ }
129
+ return response.data;
130
+ }
131
+ async create(def) {
132
+ validateIdentifier(def.name, "table name");
133
+ if (def.schema) {
134
+ validateIdentifier(def.schema, "schema name");
135
+ }
136
+ const response = await this.httpClient.request("POST", "/v1/meta/tables", {
137
+ body: def
138
+ });
139
+ if (response.error) {
140
+ throw response.error;
141
+ }
142
+ return response.data;
143
+ }
144
+ /**
145
+ * Apply table-level updates (rename, schema move, RLS toggle, comment, etc).
146
+ * Sends `PATCH /v1/meta/tables/:id`.
147
+ *
148
+ * Note: this does NOT add or drop columns. For column changes, use
149
+ * `ColumnsClient.create()` / `ColumnsClient.drop()`.
150
+ */
151
+ async update(id, patch) {
152
+ if (patch.name !== void 0) {
153
+ validateIdentifier(patch.name, "table name");
154
+ }
155
+ if (patch.schema !== void 0) {
156
+ validateIdentifier(patch.schema, "schema name");
157
+ }
158
+ if (patch.primary_keys) {
159
+ for (const pk of patch.primary_keys) {
160
+ validateIdentifier(pk.name, "primary key column name");
161
+ }
162
+ }
163
+ const response = await this.httpClient.request("PATCH", `/v1/meta/tables/${id}`, {
164
+ body: patch
165
+ });
166
+ if (response.error) {
167
+ throw response.error;
168
+ }
169
+ return response.data;
170
+ }
171
+ async drop(id, options) {
172
+ const params = new URLSearchParams();
173
+ if (options?.cascade) {
174
+ params.set("cascade", "true");
175
+ }
176
+ const queryString = params.toString();
177
+ const path = queryString ? `/v1/meta/tables/${id}?${queryString}` : `/v1/meta/tables/${id}`;
178
+ const response = await this.httpClient.request("DELETE", path);
179
+ if (response.error) {
180
+ throw response.error;
181
+ }
182
+ }
183
+ };
184
+
185
+ // src/admin-client.ts
186
+ var AdminClient = class {
187
+ schemas;
188
+ tables;
189
+ columns;
190
+ httpClient;
191
+ constructor(httpClient) {
192
+ this.httpClient = httpClient;
193
+ this.schemas = new SchemasClient(httpClient);
194
+ this.tables = new TablesClient(httpClient);
195
+ this.columns = new ColumnsClient(httpClient);
196
+ }
197
+ /**
198
+ * Execute a raw SQL query with full privileges (service role).
199
+ *
200
+ * Always use parameterized queries to prevent SQL injection.
201
+ * Never interpolate user input directly into the SQL string.
202
+ *
203
+ * @example
204
+ * ```ts
205
+ * // GOOD — parameterized
206
+ * await admin.query('SELECT * FROM users WHERE id = $1', [userId]);
207
+ *
208
+ * // BAD — string interpolation (SQL injection risk)
209
+ * await admin.query(`SELECT * FROM users WHERE id = '${userId}'`);
210
+ * ```
211
+ */
212
+ async query(sql, params) {
213
+ const response = await this.httpClient.request("POST", "/v1/meta/query", {
214
+ body: { query: sql, params }
215
+ });
216
+ if (response.error) {
217
+ throw response.error;
218
+ }
219
+ return response.data ?? [];
220
+ }
221
+ };
222
+
223
+ export {
224
+ ColumnsClient,
225
+ SchemasClient,
226
+ TablesClient,
227
+ AdminClient
228
+ };
229
+ //# sourceMappingURL=chunk-AQ6ZQNXC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/admin-validation.ts","../src/admin-columns.ts","../src/admin-schemas.ts","../src/admin-tables.ts","../src/admin-client.ts"],"sourcesContent":["const IDENTIFIER_RE = /^[a-zA-Z_][a-zA-Z0-9_.]*$/;\n\nexport function validateIdentifier(value: string, label: string): void {\n if (!IDENTIFIER_RE.test(value)) {\n throw new Error(\n `Invalid ${label}: \"${value}\". Identifiers must match ${IDENTIFIER_RE.source}`,\n );\n }\n}\n","import type { HttpClient } from '@palbase/core';\nimport type { Column, CreateColumnDef } from './admin-types.js';\nimport { validateIdentifier } from './admin-validation.js';\n\n/**\n * Postgres-meta column admin client.\n *\n * Wraps the `/v1/meta/columns` endpoints exposed by postgres-meta.\n * Use this to add or drop columns on an existing table — `TablesClient.update()`\n * does not handle column structure changes.\n */\nexport class ColumnsClient {\n private readonly httpClient: HttpClient;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n }\n\n /**\n * Create a new column on the given table. Sends `POST /v1/meta/columns`.\n *\n * The column name is validated against the standard SQL identifier regex.\n * The `type` field is forwarded as-is to postgres-meta — callers SHOULD\n * restrict types to a known allowlist before calling this.\n */\n async create(def: CreateColumnDef): Promise<Column> {\n validateIdentifier(def.name, 'column name');\n\n const response = await this.httpClient.request<Column>('POST', '/v1/meta/columns', {\n body: def,\n });\n if (response.error) {\n throw response.error;\n }\n return response.data as Column;\n }\n\n /**\n * Drop a column. Sends `DELETE /v1/meta/columns/:tableId.:ordinalPosition`.\n *\n * postgres-meta identifies columns by `{tableId}.{ordinalPosition}` (the\n * column's 1-based attnum within its table). Use `TablesClient.get(id)` to\n * find a column id from a column name if needed.\n */\n async drop(columnId: string, options?: { cascade?: boolean }): Promise<Column> {\n if (!/^\\d+\\.\\d+$/.test(columnId)) {\n throw new Error(\n `Invalid column id: \"${columnId}\". Expected format \"{tableId}.{ordinalPosition}\"`,\n );\n }\n\n const params = new URLSearchParams();\n if (options?.cascade) {\n params.set('cascade', 'true');\n }\n\n const queryString = params.toString();\n const path = queryString\n ? `/v1/meta/columns/${columnId}?${queryString}`\n : `/v1/meta/columns/${columnId}`;\n\n const response = await this.httpClient.request<Column>('DELETE', path);\n if (response.error) {\n throw response.error;\n }\n return response.data as Column;\n }\n}\n","import type { HttpClient } from '@palbase/core';\nimport type { Schema } from './admin-types.js';\nimport { validateIdentifier } from './admin-validation.js';\n\nexport class SchemasClient {\n private readonly httpClient: HttpClient;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n }\n\n async list(): Promise<Schema[]> {\n const response = await this.httpClient.request<Schema[]>('GET', '/v1/meta/schemas');\n if (response.error) {\n throw response.error;\n }\n return response.data ?? [];\n }\n\n async create(name: string): Promise<Schema> {\n validateIdentifier(name, 'schema name');\n\n const response = await this.httpClient.request<Schema>('POST', '/v1/meta/schemas', {\n body: { name },\n });\n if (response.error) {\n throw response.error;\n }\n return response.data as Schema;\n }\n\n async drop(name: string, options?: { cascade?: boolean }): Promise<void> {\n validateIdentifier(name, 'schema name');\n\n // Resolve name to id first\n const schemas = await this.list();\n const schema = schemas.find((s) => s.name === name);\n if (!schema) {\n throw new Error(`Schema \"${name}\" not found`);\n }\n\n const params = new URLSearchParams();\n if (options?.cascade) {\n params.set('cascade', 'true');\n }\n\n const queryString = params.toString();\n const path = queryString\n ? `/v1/meta/schemas/${schema.id}?${queryString}`\n : `/v1/meta/schemas/${schema.id}`;\n\n const response = await this.httpClient.request<void>('DELETE', path);\n if (response.error) {\n throw response.error;\n }\n }\n}\n","import type { HttpClient } from '@palbase/core';\nimport type { CreateTableDef, Table, UpdateTableDef } from './admin-types.js';\nimport { validateIdentifier } from './admin-validation.js';\n\nexport class TablesClient {\n private readonly httpClient: HttpClient;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n }\n\n async list(options?: { schema?: string }): Promise<Table[]> {\n const params = new URLSearchParams();\n if (options?.schema) {\n validateIdentifier(options.schema, 'schema name');\n params.set('included_schemas', options.schema);\n }\n\n const queryString = params.toString();\n const path = queryString ? `/v1/meta/tables?${queryString}` : '/v1/meta/tables';\n\n const response = await this.httpClient.request<Table[]>('GET', path);\n if (response.error) {\n throw response.error;\n }\n return response.data ?? [];\n }\n\n async get(id: number): Promise<Table> {\n const response = await this.httpClient.request<Table>('GET', `/v1/meta/tables/${id}`);\n if (response.error) {\n throw response.error;\n }\n return response.data as Table;\n }\n\n async create(def: CreateTableDef): Promise<Table> {\n validateIdentifier(def.name, 'table name');\n if (def.schema) {\n validateIdentifier(def.schema, 'schema name');\n }\n\n const response = await this.httpClient.request<Table>('POST', '/v1/meta/tables', {\n body: def,\n });\n if (response.error) {\n throw response.error;\n }\n return response.data as Table;\n }\n\n /**\n * Apply table-level updates (rename, schema move, RLS toggle, comment, etc).\n * Sends `PATCH /v1/meta/tables/:id`.\n *\n * Note: this does NOT add or drop columns. For column changes, use\n * `ColumnsClient.create()` / `ColumnsClient.drop()`.\n */\n async update(id: number, patch: UpdateTableDef): Promise<Table> {\n if (patch.name !== undefined) {\n validateIdentifier(patch.name, 'table name');\n }\n if (patch.schema !== undefined) {\n validateIdentifier(patch.schema, 'schema name');\n }\n if (patch.primary_keys) {\n for (const pk of patch.primary_keys) {\n validateIdentifier(pk.name, 'primary key column name');\n }\n }\n\n const response = await this.httpClient.request<Table>('PATCH', `/v1/meta/tables/${id}`, {\n body: patch,\n });\n if (response.error) {\n throw response.error;\n }\n return response.data as Table;\n }\n\n async drop(id: number, options?: { cascade?: boolean }): Promise<void> {\n const params = new URLSearchParams();\n if (options?.cascade) {\n params.set('cascade', 'true');\n }\n\n const queryString = params.toString();\n const path = queryString\n ? `/v1/meta/tables/${id}?${queryString}`\n : `/v1/meta/tables/${id}`;\n\n const response = await this.httpClient.request<void>('DELETE', path);\n if (response.error) {\n throw response.error;\n }\n }\n}\n","import type { HttpClient } from '@palbase/core';\nimport { ColumnsClient } from './admin-columns.js';\nimport { SchemasClient } from './admin-schemas.js';\nimport { TablesClient } from './admin-tables.js';\n\nexport class AdminClient {\n readonly schemas: SchemasClient;\n readonly tables: TablesClient;\n readonly columns: ColumnsClient;\n\n private readonly httpClient: HttpClient;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n this.schemas = new SchemasClient(httpClient);\n this.tables = new TablesClient(httpClient);\n this.columns = new ColumnsClient(httpClient);\n }\n\n /**\n * Execute a raw SQL query with full privileges (service role).\n *\n * Always use parameterized queries to prevent SQL injection.\n * Never interpolate user input directly into the SQL string.\n *\n * @example\n * ```ts\n * // GOOD — parameterized\n * await admin.query('SELECT * FROM users WHERE id = $1', [userId]);\n *\n * // BAD — string interpolation (SQL injection risk)\n * await admin.query(`SELECT * FROM users WHERE id = '${userId}'`);\n * ```\n */\n async query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]> {\n const response = await this.httpClient.request<T[]>('POST', '/v1/meta/query', {\n body: { query: sql, params },\n });\n if (response.error) {\n throw response.error;\n }\n return response.data ?? [];\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.cjs CHANGED
@@ -21,12 +21,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  DatabaseClient: () => DatabaseClient,
24
+ MaybeSingleQueryBuilder: () => MaybeSingleQueryBuilder,
24
25
  QueryBuilder: () => QueryBuilder,
25
- TransactionClient: () => TransactionClient
26
+ SingleQueryBuilder: () => SingleQueryBuilder,
27
+ TransactionClient: () => TransactionClient,
28
+ executeTransaction: () => executeTransaction
26
29
  });
27
30
  module.exports = __toCommonJS(index_exports);
28
31
 
29
- // ../../../modules/db/dist/chunk-AQ6ZQNXC.js
32
+ // src/admin-validation.ts
30
33
  var IDENTIFIER_RE = /^[a-zA-Z_][a-zA-Z0-9_.]*$/;
31
34
  function validateIdentifier(value, label) {
32
35
  if (!IDENTIFIER_RE.test(value)) {
@@ -35,6 +38,8 @@ function validateIdentifier(value, label) {
35
38
  );
36
39
  }
37
40
  }
41
+
42
+ // src/admin-columns.ts
38
43
  var ColumnsClient = class {
39
44
  httpClient;
40
45
  constructor(httpClient) {
@@ -83,6 +88,8 @@ var ColumnsClient = class {
83
88
  return response.data;
84
89
  }
85
90
  };
91
+
92
+ // src/admin-schemas.ts
86
93
  var SchemasClient = class {
87
94
  httpClient;
88
95
  constructor(httpClient) {
@@ -124,6 +131,8 @@ var SchemasClient = class {
124
131
  }
125
132
  }
126
133
  };
134
+
135
+ // src/admin-tables.ts
127
136
  var TablesClient = class {
128
137
  httpClient;
129
138
  constructor(httpClient) {
@@ -203,6 +212,8 @@ var TablesClient = class {
203
212
  }
204
213
  }
205
214
  };
215
+
216
+ // src/admin-client.ts
206
217
  var AdminClient = class {
207
218
  schemas;
208
219
  tables;
@@ -240,8 +251,7 @@ var AdminClient = class {
240
251
  }
241
252
  };
242
253
 
243
- // ../../../modules/db/dist/index.js
244
- var import_core = require("@palbase/core");
254
+ // src/query-builder.ts
245
255
  var TABLE_NAME_RE = /^[a-zA-Z_][a-zA-Z0-9_.]*$/;
246
256
  var COLUMN_NAME_RE = /^[a-zA-Z_][a-zA-Z0-9_.:\->#]*$/;
247
257
  function validateTableName(table) {
@@ -513,6 +523,9 @@ var QueryBuilder = class {
513
523
  return response;
514
524
  }
515
525
  };
526
+
527
+ // src/transaction.ts
528
+ var import_core = require("@palbase/core");
516
529
  var DEFAULT_TIMEOUT_MS = 3e4;
517
530
  var TX_ID_RE = /^[a-zA-Z0-9_\-]+$/;
518
531
  var TransactionClient = class {
@@ -609,6 +622,8 @@ async function executeTransaction(httpClient, fn, options) {
609
622
  };
610
623
  }
611
624
  }
625
+
626
+ // src/database-client.ts
612
627
  var FN_NAME_RE = /^[a-zA-Z_][a-zA-Z0-9_.]*$/;
613
628
  var DatabaseClient = class {
614
629
  httpClient;
@@ -641,7 +656,10 @@ var DatabaseClient = class {
641
656
  // Annotate the CommonJS export names for ESM import in node:
642
657
  0 && (module.exports = {
643
658
  DatabaseClient,
659
+ MaybeSingleQueryBuilder,
644
660
  QueryBuilder,
645
- TransactionClient
661
+ SingleQueryBuilder,
662
+ TransactionClient,
663
+ executeTransaction
646
664
  });
647
665
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../../../../modules/db/src/admin-validation.ts","../../../../modules/db/src/admin-columns.ts","../../../../modules/db/src/admin-schemas.ts","../../../../modules/db/src/admin-tables.ts","../../../../modules/db/src/admin-client.ts","../../../../modules/db/src/query-builder.ts","../../../../modules/db/src/transaction.ts","../../../../modules/db/src/database-client.ts"],"sourcesContent":["export { DatabaseClient, QueryBuilder, TransactionClient } from '@palbase/modules-db';\nexport type { QueryBuilderOptions, TransactionOptions, OrderOptions, GenericSchema, GenericTable } from '@palbase/modules-db';\n","const IDENTIFIER_RE = /^[a-zA-Z_][a-zA-Z0-9_.]*$/;\n\nexport function validateIdentifier(value: string, label: string): void {\n if (!IDENTIFIER_RE.test(value)) {\n throw new Error(\n `Invalid ${label}: \"${value}\". Identifiers must match ${IDENTIFIER_RE.source}`,\n );\n }\n}\n","import type { HttpClient } from '@palbase/core';\nimport type { Column, CreateColumnDef } from './admin-types.js';\nimport { validateIdentifier } from './admin-validation.js';\n\n/**\n * Postgres-meta column admin client.\n *\n * Wraps the `/v1/meta/columns` endpoints exposed by postgres-meta.\n * Use this to add or drop columns on an existing table — `TablesClient.update()`\n * does not handle column structure changes.\n */\nexport class ColumnsClient {\n private readonly httpClient: HttpClient;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n }\n\n /**\n * Create a new column on the given table. Sends `POST /v1/meta/columns`.\n *\n * The column name is validated against the standard SQL identifier regex.\n * The `type` field is forwarded as-is to postgres-meta — callers SHOULD\n * restrict types to a known allowlist before calling this.\n */\n async create(def: CreateColumnDef): Promise<Column> {\n validateIdentifier(def.name, 'column name');\n\n const response = await this.httpClient.request<Column>('POST', '/v1/meta/columns', {\n body: def,\n });\n if (response.error) {\n throw response.error;\n }\n return response.data as Column;\n }\n\n /**\n * Drop a column. Sends `DELETE /v1/meta/columns/:tableId.:ordinalPosition`.\n *\n * postgres-meta identifies columns by `{tableId}.{ordinalPosition}` (the\n * column's 1-based attnum within its table). Use `TablesClient.get(id)` to\n * find a column id from a column name if needed.\n */\n async drop(columnId: string, options?: { cascade?: boolean }): Promise<Column> {\n if (!/^\\d+\\.\\d+$/.test(columnId)) {\n throw new Error(\n `Invalid column id: \"${columnId}\". Expected format \"{tableId}.{ordinalPosition}\"`,\n );\n }\n\n const params = new URLSearchParams();\n if (options?.cascade) {\n params.set('cascade', 'true');\n }\n\n const queryString = params.toString();\n const path = queryString\n ? `/v1/meta/columns/${columnId}?${queryString}`\n : `/v1/meta/columns/${columnId}`;\n\n const response = await this.httpClient.request<Column>('DELETE', path);\n if (response.error) {\n throw response.error;\n }\n return response.data as Column;\n }\n}\n","import type { HttpClient } from '@palbase/core';\nimport type { Schema } from './admin-types.js';\nimport { validateIdentifier } from './admin-validation.js';\n\nexport class SchemasClient {\n private readonly httpClient: HttpClient;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n }\n\n async list(): Promise<Schema[]> {\n const response = await this.httpClient.request<Schema[]>('GET', '/v1/meta/schemas');\n if (response.error) {\n throw response.error;\n }\n return response.data ?? [];\n }\n\n async create(name: string): Promise<Schema> {\n validateIdentifier(name, 'schema name');\n\n const response = await this.httpClient.request<Schema>('POST', '/v1/meta/schemas', {\n body: { name },\n });\n if (response.error) {\n throw response.error;\n }\n return response.data as Schema;\n }\n\n async drop(name: string, options?: { cascade?: boolean }): Promise<void> {\n validateIdentifier(name, 'schema name');\n\n // Resolve name to id first\n const schemas = await this.list();\n const schema = schemas.find((s) => s.name === name);\n if (!schema) {\n throw new Error(`Schema \"${name}\" not found`);\n }\n\n const params = new URLSearchParams();\n if (options?.cascade) {\n params.set('cascade', 'true');\n }\n\n const queryString = params.toString();\n const path = queryString\n ? `/v1/meta/schemas/${schema.id}?${queryString}`\n : `/v1/meta/schemas/${schema.id}`;\n\n const response = await this.httpClient.request<void>('DELETE', path);\n if (response.error) {\n throw response.error;\n }\n }\n}\n","import type { HttpClient } from '@palbase/core';\nimport type { CreateTableDef, Table, UpdateTableDef } from './admin-types.js';\nimport { validateIdentifier } from './admin-validation.js';\n\nexport class TablesClient {\n private readonly httpClient: HttpClient;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n }\n\n async list(options?: { schema?: string }): Promise<Table[]> {\n const params = new URLSearchParams();\n if (options?.schema) {\n validateIdentifier(options.schema, 'schema name');\n params.set('included_schemas', options.schema);\n }\n\n const queryString = params.toString();\n const path = queryString ? `/v1/meta/tables?${queryString}` : '/v1/meta/tables';\n\n const response = await this.httpClient.request<Table[]>('GET', path);\n if (response.error) {\n throw response.error;\n }\n return response.data ?? [];\n }\n\n async get(id: number): Promise<Table> {\n const response = await this.httpClient.request<Table>('GET', `/v1/meta/tables/${id}`);\n if (response.error) {\n throw response.error;\n }\n return response.data as Table;\n }\n\n async create(def: CreateTableDef): Promise<Table> {\n validateIdentifier(def.name, 'table name');\n if (def.schema) {\n validateIdentifier(def.schema, 'schema name');\n }\n\n const response = await this.httpClient.request<Table>('POST', '/v1/meta/tables', {\n body: def,\n });\n if (response.error) {\n throw response.error;\n }\n return response.data as Table;\n }\n\n /**\n * Apply table-level updates (rename, schema move, RLS toggle, comment, etc).\n * Sends `PATCH /v1/meta/tables/:id`.\n *\n * Note: this does NOT add or drop columns. For column changes, use\n * `ColumnsClient.create()` / `ColumnsClient.drop()`.\n */\n async update(id: number, patch: UpdateTableDef): Promise<Table> {\n if (patch.name !== undefined) {\n validateIdentifier(patch.name, 'table name');\n }\n if (patch.schema !== undefined) {\n validateIdentifier(patch.schema, 'schema name');\n }\n if (patch.primary_keys) {\n for (const pk of patch.primary_keys) {\n validateIdentifier(pk.name, 'primary key column name');\n }\n }\n\n const response = await this.httpClient.request<Table>('PATCH', `/v1/meta/tables/${id}`, {\n body: patch,\n });\n if (response.error) {\n throw response.error;\n }\n return response.data as Table;\n }\n\n async drop(id: number, options?: { cascade?: boolean }): Promise<void> {\n const params = new URLSearchParams();\n if (options?.cascade) {\n params.set('cascade', 'true');\n }\n\n const queryString = params.toString();\n const path = queryString\n ? `/v1/meta/tables/${id}?${queryString}`\n : `/v1/meta/tables/${id}`;\n\n const response = await this.httpClient.request<void>('DELETE', path);\n if (response.error) {\n throw response.error;\n }\n }\n}\n","import type { HttpClient } from '@palbase/core';\nimport { ColumnsClient } from './admin-columns.js';\nimport { SchemasClient } from './admin-schemas.js';\nimport { TablesClient } from './admin-tables.js';\n\nexport class AdminClient {\n readonly schemas: SchemasClient;\n readonly tables: TablesClient;\n readonly columns: ColumnsClient;\n\n private readonly httpClient: HttpClient;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n this.schemas = new SchemasClient(httpClient);\n this.tables = new TablesClient(httpClient);\n this.columns = new ColumnsClient(httpClient);\n }\n\n /**\n * Execute a raw SQL query with full privileges (service role).\n *\n * Always use parameterized queries to prevent SQL injection.\n * Never interpolate user input directly into the SQL string.\n *\n * @example\n * ```ts\n * // GOOD — parameterized\n * await admin.query('SELECT * FROM users WHERE id = $1', [userId]);\n *\n * // BAD — string interpolation (SQL injection risk)\n * await admin.query(`SELECT * FROM users WHERE id = '${userId}'`);\n * ```\n */\n async query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]> {\n const response = await this.httpClient.request<T[]>('POST', '/v1/meta/query', {\n body: { query: sql, params },\n });\n if (response.error) {\n throw response.error;\n }\n return response.data ?? [];\n }\n}\n","import type { HttpClient, PalbaseResponse } from '@palbase/core';\nimport type { OrderOptions } from './types.js';\n\ntype HttpMethod = 'GET' | 'POST' | 'PATCH' | 'DELETE';\n\nconst TABLE_NAME_RE = /^[a-zA-Z_][a-zA-Z0-9_.]*$/;\nconst COLUMN_NAME_RE = /^[a-zA-Z_][a-zA-Z0-9_.:\\->#]*$/;\n\nfunction validateTableName(table: string): void {\n if (!TABLE_NAME_RE.test(table)) {\n throw new Error(`Invalid table name: \"${table}\". Table names must match ${TABLE_NAME_RE.source}`);\n }\n}\n\nfunction validateColumnName(column: string): void {\n if (!COLUMN_NAME_RE.test(column)) {\n throw new Error(`Invalid column name: \"${column}\". Column names must match ${COLUMN_NAME_RE.source}`);\n }\n}\n\nfunction encodeFilterValue(value: unknown): string {\n return String(value).replace(/[&#]/g, (c) => encodeURIComponent(c));\n}\n\ninterface QueryState<T> {\n readonly method: HttpMethod;\n readonly body?: Partial<T> | Partial<T>[] | Record<string, unknown> | Record<string, unknown>[];\n readonly headers: Record<string, string>;\n readonly filters: string[];\n readonly params: Record<string, string>;\n readonly isSingle: boolean;\n readonly isMaybeSingle: boolean;\n}\n\nexport interface QueryBuilderOptions {\n basePath?: string;\n signal?: AbortSignal;\n}\n\n/**\n * Wrapper returned by `.single()` — resolves to `PalbaseResponse<T>` (single row).\n */\nexport class SingleQueryBuilder<T> implements PromiseLike<PalbaseResponse<T>> {\n constructor(private readonly builder: QueryBuilder<T>) {}\n\n then<TResult1 = PalbaseResponse<T>, TResult2 = never>(\n onfulfilled?: ((value: PalbaseResponse<T>) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n return this.builder['execute']().then(\n (res) => onfulfilled ? onfulfilled(res as unknown as PalbaseResponse<T>) : res as unknown as TResult1,\n onrejected ?? undefined,\n );\n }\n}\n\n/**\n * Wrapper returned by `.maybeSingle()` — resolves to `PalbaseResponse<T | null>` (single row or null).\n */\nexport class MaybeSingleQueryBuilder<T> implements PromiseLike<PalbaseResponse<T | null>> {\n constructor(private readonly builder: QueryBuilder<T>) {}\n\n then<TResult1 = PalbaseResponse<T | null>, TResult2 = never>(\n onfulfilled?: ((value: PalbaseResponse<T | null>) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n return this.builder['execute']().then(\n (res) => onfulfilled ? onfulfilled(res as unknown as PalbaseResponse<T | null>) : res as unknown as TResult1,\n onrejected ?? undefined,\n );\n }\n}\n\nexport class QueryBuilder<T = Record<string, unknown>> implements PromiseLike<PalbaseResponse<T[]>> {\n private readonly httpClient: HttpClient;\n private readonly basePath: string;\n private readonly signal?: AbortSignal;\n private state: QueryState<T>;\n\n constructor(httpClient: HttpClient, table: string, options?: QueryBuilderOptions) {\n validateTableName(table);\n this.httpClient = httpClient;\n this.basePath = options?.basePath ?? `/v1/db/${table}`;\n this.signal = options?.signal;\n this.state = {\n method: 'GET',\n headers: {},\n filters: [],\n params: {},\n isSingle: false,\n isMaybeSingle: false,\n };\n }\n\n // --- Operation methods ---\n\n select(columns?: string): this {\n this.state = {\n ...this.state,\n // Only set GET if no mutation method (POST/PATCH/DELETE) is already set\n // insert().select() should stay POST with Prefer: return=representation\n method: this.state.body !== undefined ? this.state.method : 'GET',\n params: { ...this.state.params, select: columns ?? '*' },\n };\n return this;\n }\n\n insert(data: Partial<T> | Partial<T>[] | Record<string, unknown> | Record<string, unknown>[]): this {\n this.state = {\n ...this.state,\n method: 'POST',\n body: data,\n headers: {\n ...this.state.headers,\n Prefer: 'return=representation',\n },\n };\n return this;\n }\n\n update(data: Partial<T> | Record<string, unknown>): this {\n this.state = {\n ...this.state,\n method: 'PATCH',\n body: data,\n headers: {\n ...this.state.headers,\n Prefer: 'return=representation',\n },\n };\n return this;\n }\n\n delete(): this {\n this.state = {\n ...this.state,\n method: 'DELETE',\n };\n return this;\n }\n\n upsert(data: Partial<T> | Partial<T>[]): this {\n this.state = {\n ...this.state,\n method: 'POST',\n body: data,\n headers: {\n ...this.state.headers,\n Prefer: 'resolution=merge-duplicates,return=representation',\n },\n };\n return this;\n }\n\n // --- Filter methods ---\n\n eq(column: string & keyof T | (string & Record<never, never>), value: unknown): this {\n validateColumnName(column);\n this.state = {\n ...this.state,\n filters: [...this.state.filters, `${column}=eq.${encodeFilterValue(value)}`],\n };\n return this;\n }\n\n neq(column: string & keyof T | (string & Record<never, never>), value: unknown): this {\n validateColumnName(column);\n this.state = {\n ...this.state,\n filters: [...this.state.filters, `${column}=neq.${encodeFilterValue(value)}`],\n };\n return this;\n }\n\n gt(column: string & keyof T | (string & Record<never, never>), value: unknown): this {\n validateColumnName(column);\n this.state = {\n ...this.state,\n filters: [...this.state.filters, `${column}=gt.${encodeFilterValue(value)}`],\n };\n return this;\n }\n\n gte(column: string & keyof T | (string & Record<never, never>), value: unknown): this {\n validateColumnName(column);\n this.state = {\n ...this.state,\n filters: [...this.state.filters, `${column}=gte.${encodeFilterValue(value)}`],\n };\n return this;\n }\n\n lt(column: string & keyof T | (string & Record<never, never>), value: unknown): this {\n validateColumnName(column);\n this.state = {\n ...this.state,\n filters: [...this.state.filters, `${column}=lt.${encodeFilterValue(value)}`],\n };\n return this;\n }\n\n lte(column: string & keyof T | (string & Record<never, never>), value: unknown): this {\n validateColumnName(column);\n this.state = {\n ...this.state,\n filters: [...this.state.filters, `${column}=lte.${encodeFilterValue(value)}`],\n };\n return this;\n }\n\n like(column: string & keyof T | (string & Record<never, never>), pattern: string): this {\n validateColumnName(column);\n this.state = {\n ...this.state,\n filters: [...this.state.filters, `${column}=like.${encodeFilterValue(pattern)}`],\n };\n return this;\n }\n\n ilike(column: string & keyof T | (string & Record<never, never>), pattern: string): this {\n validateColumnName(column);\n this.state = {\n ...this.state,\n filters: [...this.state.filters, `${column}=ilike.${encodeFilterValue(pattern)}`],\n };\n return this;\n }\n\n in(column: string & keyof T | (string & Record<never, never>), values: unknown[]): this {\n validateColumnName(column);\n const encoded = values.map((v) => encodeFilterValue(v).replace(/[),]/g, (c) => encodeURIComponent(c)));\n this.state = {\n ...this.state,\n filters: [...this.state.filters, `${column}=in.(${encoded.join(',')})`],\n };\n return this;\n }\n\n is(column: string & keyof T | (string & Record<never, never>), value: unknown): this {\n validateColumnName(column);\n this.state = {\n ...this.state,\n filters: [...this.state.filters, `${column}=is.${encodeFilterValue(value)}`],\n };\n return this;\n }\n\n // --- Modifier methods ---\n\n order(column: string & keyof T | (string & Record<never, never>), options?: OrderOptions): this {\n validateColumnName(column);\n const direction = options?.ascending === false ? 'desc' : 'asc';\n this.state = {\n ...this.state,\n params: { ...this.state.params, order: `${column}.${direction}` },\n };\n return this;\n }\n\n limit(count: number): this {\n this.state = {\n ...this.state,\n params: { ...this.state.params, limit: String(count) },\n };\n return this;\n }\n\n range(from: number, to: number): this {\n this.state = {\n ...this.state,\n headers: {\n ...this.state.headers,\n Range: `${from}-${to}`,\n },\n };\n return this;\n }\n\n single(): SingleQueryBuilder<T> {\n this.state = {\n ...this.state,\n isSingle: true,\n headers: {\n ...this.state.headers,\n Accept: 'application/vnd.pgrst.object+json',\n },\n };\n return new SingleQueryBuilder(this);\n }\n\n maybeSingle(): MaybeSingleQueryBuilder<T> {\n this.state = {\n ...this.state,\n isMaybeSingle: true,\n headers: {\n ...this.state.headers,\n Accept: 'application/vnd.pgrst.object+json',\n },\n };\n return new MaybeSingleQueryBuilder(this);\n }\n\n // --- Execution ---\n\n then<TResult1 = PalbaseResponse<T[]>, TResult2 = never>(\n onfulfilled?: ((value: PalbaseResponse<T[]>) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n return this.execute().then(onfulfilled as unknown as ((value: PalbaseResponse<T>) => TResult1 | PromiseLike<TResult1>) | null, onrejected);\n }\n\n private buildPath(): string {\n // Build query string manually — URLSearchParams percent-encodes\n // parentheses and commas which breaks PostgREST filter syntax\n const parts: string[] = [];\n\n for (const [key, value] of Object.entries(this.state.params)) {\n parts.push(`${key}=${value}`);\n }\n\n for (const filter of this.state.filters) {\n parts.push(filter);\n }\n\n return parts.length > 0 ? `${this.basePath}?${parts.join('&')}` : this.basePath;\n }\n\n private async execute(): Promise<PalbaseResponse<T>> {\n const path = this.buildPath();\n const { method, body, headers } = this.state;\n\n const response = await this.httpClient.request<T>(method, path, {\n body: body,\n headers: Object.keys(headers).length > 0 ? headers : undefined,\n signal: this.signal,\n });\n\n // For maybeSingle, convert 406 (no rows) to { data: null, error: null }\n if (this.state.isMaybeSingle && response.error && response.status === 406) {\n return { data: null, error: null, status: 200 };\n }\n\n return response;\n }\n}\n","import type { HttpClient, PalbaseResponse } from '@palbase/core';\nimport { PalbaseError } from '@palbase/core';\nimport { QueryBuilder } from './query-builder.js';\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst TX_ID_RE = /^[a-zA-Z0-9_\\-]+$/;\n\nexport interface TransactionOptions {\n timeoutMs?: number;\n}\n\nexport class TransactionClient {\n private readonly httpClient: HttpClient;\n private readonly txId: string;\n private readonly signal?: AbortSignal;\n\n constructor(httpClient: HttpClient, txId: string, signal?: AbortSignal) {\n this.httpClient = httpClient;\n this.txId = txId;\n this.signal = signal;\n }\n\n from<T = Record<string, unknown>>(table: string): QueryBuilder<T> {\n return new QueryBuilder<T>(this.httpClient, table, {\n basePath: `/v1/db/transaction/${this.txId}/query/${table}`,\n signal: this.signal,\n });\n }\n}\n\nexport async function executeTransaction<T>(\n httpClient: HttpClient,\n fn: (tx: TransactionClient) => Promise<T>,\n options?: TransactionOptions,\n): Promise<PalbaseResponse<T>> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n let txId: string | undefined;\n\n try {\n // Begin transaction\n const beginResponse = await httpClient.request<{ txId: string }>(\n 'POST',\n '/v1/db/transaction/begin',\n { signal: controller.signal },\n );\n\n if (beginResponse.error || !beginResponse.data) {\n clearTimeout(timer);\n return {\n data: null,\n error: beginResponse.error ?? new PalbaseError(\n 'transaction_error',\n 'Failed to begin transaction',\n beginResponse.status,\n ),\n status: beginResponse.status,\n };\n }\n\n txId = beginResponse.data.txId;\n\n // Validate txId format to prevent path traversal from a compromised server response\n if (!TX_ID_RE.test(txId)) {\n clearTimeout(timer);\n return {\n data: null,\n error: new PalbaseError(\n 'transaction_error',\n `Invalid transaction ID format: \"${txId}\"`,\n 0,\n ),\n status: 0,\n };\n }\n\n const tx = new TransactionClient(httpClient, txId, controller.signal);\n\n // Execute user function\n const result = await fn(tx);\n\n // Commit\n const commitResponse = await httpClient.request<unknown>(\n 'POST',\n `/v1/db/transaction/${txId}/commit`,\n { signal: controller.signal },\n );\n\n clearTimeout(timer);\n\n if (commitResponse.error) {\n return {\n data: null,\n error: commitResponse.error,\n status: commitResponse.status,\n };\n }\n\n return { data: result, error: null, status: 200 };\n } catch (error) {\n clearTimeout(timer);\n\n // Rollback if we have a transaction ID.\n // Intentionally no signal — rollback must complete even if the timeout controller aborted.\n if (txId) {\n try {\n await httpClient.request<unknown>(\n 'POST',\n `/v1/db/transaction/${txId}/rollback`,\n { signal: undefined },\n );\n } catch {\n // Rollback failed — nothing we can do, return the original error\n }\n }\n\n if (error instanceof PalbaseError) {\n return { data: null, error, status: error.status };\n }\n\n const isAbort = error instanceof DOMException && error.name === 'AbortError';\n const code = isAbort ? 'transaction_timeout' : 'transaction_error';\n const message = isAbort\n ? `Transaction timed out after ${timeoutMs}ms`\n : error instanceof Error ? error.message : 'Transaction failed';\n const status = isAbort ? 408 : 0;\n\n return {\n data: null,\n error: new PalbaseError(code, message, status),\n status,\n };\n }\n}\n","import type { HttpClient, PalbaseResponse } from '@palbase/core';\nimport { AdminClient } from './admin-client.js';\nimport { QueryBuilder } from './query-builder.js';\nimport { type TransactionOptions, executeTransaction } from './transaction.js';\nimport type { TransactionClient } from './transaction.js';\nconst FN_NAME_RE = /^[a-zA-Z_][a-zA-Z0-9_.]*$/;\n\nexport interface DatabaseClientOptions {\n enableAdmin?: boolean;\n /** Override the default `/v1/db` path prefix. Set to empty string for direct PostgREST access. */\n pathPrefix?: string;\n}\n\nexport class DatabaseClient {\n private readonly httpClient: HttpClient;\n private readonly pathPrefix: string;\n readonly admin?: AdminClient;\n\n constructor(httpClient: HttpClient, options?: DatabaseClientOptions) {\n this.httpClient = httpClient;\n this.pathPrefix = options?.pathPrefix ?? '/v1/db';\n if (options?.enableAdmin) {\n this.admin = new AdminClient(httpClient);\n }\n }\n\n from<T = Record<string, unknown>>(table: string): QueryBuilder<T> {\n return new QueryBuilder<T>(this.httpClient, table, {\n basePath: `${this.pathPrefix}/${table}`,\n });\n }\n\n async rpc<T = unknown>(fnName: string, params?: Record<string, unknown>): Promise<PalbaseResponse<T>> {\n if (!FN_NAME_RE.test(fnName)) {\n throw new Error(`Invalid function name: \"${fnName}\". Function names must match ${FN_NAME_RE.source}`);\n }\n\n return this.httpClient.request<T>('POST', `${this.pathPrefix}/rpc/${fnName}`, {\n body: params,\n });\n }\n\n async transaction<T>(\n fn: (tx: TransactionClient) => Promise<T>,\n options?: TransactionOptions,\n ): Promise<PalbaseResponse<T>> {\n return executeTransaction(this.httpClient, fn, options);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAM,gBAAgB;AAEf,SAAS,mBAAmB,OAAe,OAAqB;AACrE,MAAI,CAAC,cAAc,KAAK,KAAK,GAAG;AAC9B,UAAM,IAAI;MACR,WAAW,KAAK,MAAM,KAAK,6BAA6B,cAAc,MAAM;IAC9E;EACF;AACF;ACGO,IAAM,gBAAN,MAAoB;EACR;EAEjB,YAAY,YAAwB;AAClC,SAAK,aAAa;EACpB;;;;;;;;EASA,MAAM,OAAO,KAAuC;AAClD,uBAAmB,IAAI,MAAM,aAAa;AAE1C,UAAM,WAAW,MAAM,KAAK,WAAW,QAAgB,QAAQ,oBAAoB;MACjF,MAAM;IACR,CAAC;AACD,QAAI,SAAS,OAAO;AAClB,YAAM,SAAS;IACjB;AACA,WAAO,SAAS;EAClB;;;;;;;;EASA,MAAM,KAAK,UAAkB,SAAkD;AAC7E,QAAI,CAAC,aAAa,KAAK,QAAQ,GAAG;AAChC,YAAM,IAAI;QACR,uBAAuB,QAAQ;MACjC;IACF;AAEA,UAAM,SAAS,IAAI,gBAAgB;AACnC,QAAI,SAAS,SAAS;AACpB,aAAO,IAAI,WAAW,MAAM;IAC9B;AAEA,UAAM,cAAc,OAAO,SAAS;AACpC,UAAM,OAAO,cACT,oBAAoB,QAAQ,IAAI,WAAW,KAC3C,oBAAoB,QAAQ;AAEhC,UAAM,WAAW,MAAM,KAAK,WAAW,QAAgB,UAAU,IAAI;AACrE,QAAI,SAAS,OAAO;AAClB,YAAM,SAAS;IACjB;AACA,WAAO,SAAS;EAClB;AACF;AC/DO,IAAM,gBAAN,MAAoB;EACR;EAEjB,YAAY,YAAwB;AAClC,SAAK,aAAa;EACpB;EAEA,MAAM,OAA0B;AAC9B,UAAM,WAAW,MAAM,KAAK,WAAW,QAAkB,OAAO,kBAAkB;AAClF,QAAI,SAAS,OAAO;AAClB,YAAM,SAAS;IACjB;AACA,WAAO,SAAS,QAAQ,CAAC;EAC3B;EAEA,MAAM,OAAO,MAA+B;AAC1C,uBAAmB,MAAM,aAAa;AAEtC,UAAM,WAAW,MAAM,KAAK,WAAW,QAAgB,QAAQ,oBAAoB;MACjF,MAAM,EAAE,KAAK;IACf,CAAC;AACD,QAAI,SAAS,OAAO;AAClB,YAAM,SAAS;IACjB;AACA,WAAO,SAAS;EAClB;EAEA,MAAM,KAAK,MAAc,SAAgD;AACvE,uBAAmB,MAAM,aAAa;AAGtC,UAAM,UAAU,MAAM,KAAK,KAAK;AAChC,UAAM,SAAS,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;AAClD,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,WAAW,IAAI,aAAa;IAC9C;AAEA,UAAM,SAAS,IAAI,gBAAgB;AACnC,QAAI,SAAS,SAAS;AACpB,aAAO,IAAI,WAAW,MAAM;IAC9B;AAEA,UAAM,cAAc,OAAO,SAAS;AACpC,UAAM,OAAO,cACT,oBAAoB,OAAO,EAAE,IAAI,WAAW,KAC5C,oBAAoB,OAAO,EAAE;AAEjC,UAAM,WAAW,MAAM,KAAK,WAAW,QAAc,UAAU,IAAI;AACnE,QAAI,SAAS,OAAO;AAClB,YAAM,SAAS;IACjB;EACF;AACF;ACpDO,IAAM,eAAN,MAAmB;EACP;EAEjB,YAAY,YAAwB;AAClC,SAAK,aAAa;EACpB;EAEA,MAAM,KAAK,SAAiD;AAC1D,UAAM,SAAS,IAAI,gBAAgB;AACnC,QAAI,SAAS,QAAQ;AACnB,yBAAmB,QAAQ,QAAQ,aAAa;AAChD,aAAO,IAAI,oBAAoB,QAAQ,MAAM;IAC/C;AAEA,UAAM,cAAc,OAAO,SAAS;AACpC,UAAM,OAAO,cAAc,mBAAmB,WAAW,KAAK;AAE9D,UAAM,WAAW,MAAM,KAAK,WAAW,QAAiB,OAAO,IAAI;AACnE,QAAI,SAAS,OAAO;AAClB,YAAM,SAAS;IACjB;AACA,WAAO,SAAS,QAAQ,CAAC;EAC3B;EAEA,MAAM,IAAI,IAA4B;AACpC,UAAM,WAAW,MAAM,KAAK,WAAW,QAAe,OAAO,mBAAmB,EAAE,EAAE;AACpF,QAAI,SAAS,OAAO;AAClB,YAAM,SAAS;IACjB;AACA,WAAO,SAAS;EAClB;EAEA,MAAM,OAAO,KAAqC;AAChD,uBAAmB,IAAI,MAAM,YAAY;AACzC,QAAI,IAAI,QAAQ;AACd,yBAAmB,IAAI,QAAQ,aAAa;IAC9C;AAEA,UAAM,WAAW,MAAM,KAAK,WAAW,QAAe,QAAQ,mBAAmB;MAC/E,MAAM;IACR,CAAC;AACD,QAAI,SAAS,OAAO;AAClB,YAAM,SAAS;IACjB;AACA,WAAO,SAAS;EAClB;;;;;;;;EASA,MAAM,OAAO,IAAY,OAAuC;AAC9D,QAAI,MAAM,SAAS,QAAW;AAC5B,yBAAmB,MAAM,MAAM,YAAY;IAC7C;AACA,QAAI,MAAM,WAAW,QAAW;AAC9B,yBAAmB,MAAM,QAAQ,aAAa;IAChD;AACA,QAAI,MAAM,cAAc;AACtB,iBAAW,MAAM,MAAM,cAAc;AACnC,2BAAmB,GAAG,MAAM,yBAAyB;MACvD;IACF;AAEA,UAAM,WAAW,MAAM,KAAK,WAAW,QAAe,SAAS,mBAAmB,EAAE,IAAI;MACtF,MAAM;IACR,CAAC;AACD,QAAI,SAAS,OAAO;AAClB,YAAM,SAAS;IACjB;AACA,WAAO,SAAS;EAClB;EAEA,MAAM,KAAK,IAAY,SAAgD;AACrE,UAAM,SAAS,IAAI,gBAAgB;AACnC,QAAI,SAAS,SAAS;AACpB,aAAO,IAAI,WAAW,MAAM;IAC9B;AAEA,UAAM,cAAc,OAAO,SAAS;AACpC,UAAM,OAAO,cACT,mBAAmB,EAAE,IAAI,WAAW,KACpC,mBAAmB,EAAE;AAEzB,UAAM,WAAW,MAAM,KAAK,WAAW,QAAc,UAAU,IAAI;AACnE,QAAI,SAAS,OAAO;AAClB,YAAM,SAAS;IACjB;EACF;AACF;AC3FO,IAAM,cAAN,MAAkB;EACd;EACA;EACA;EAEQ;EAEjB,YAAY,YAAwB;AAClC,SAAK,aAAa;AAClB,SAAK,UAAU,IAAI,cAAc,UAAU;AAC3C,SAAK,SAAS,IAAI,aAAa,UAAU;AACzC,SAAK,UAAU,IAAI,cAAc,UAAU;EAC7C;;;;;;;;;;;;;;;;EAiBA,MAAM,MAAmC,KAAa,QAAkC;AACtF,UAAM,WAAW,MAAM,KAAK,WAAW,QAAa,QAAQ,kBAAkB;MAC5E,MAAM,EAAE,OAAO,KAAK,OAAO;IAC7B,CAAC;AACD,QAAI,SAAS,OAAO;AAClB,YAAM,SAAS;IACjB;AACA,WAAO,SAAS,QAAQ,CAAC;EAC3B;AACF;;;AE1CA,kBAA6B;ADI7B,IAAM,gBAAgB;AACtB,IAAM,iBAAiB;AAEvB,SAAS,kBAAkB,OAAqB;AAC9C,MAAI,CAAC,cAAc,KAAK,KAAK,GAAG;AAC9B,UAAM,IAAI,MAAM,wBAAwB,KAAK,6BAA6B,cAAc,MAAM,EAAE;EAClG;AACF;AAEA,SAAS,mBAAmB,QAAsB;AAChD,MAAI,CAAC,eAAe,KAAK,MAAM,GAAG;AAChC,UAAM,IAAI,MAAM,yBAAyB,MAAM,8BAA8B,eAAe,MAAM,EAAE;EACtG;AACF;AAEA,SAAS,kBAAkB,OAAwB;AACjD,SAAO,OAAO,KAAK,EAAE,QAAQ,SAAS,CAAC,MAAM,mBAAmB,CAAC,CAAC;AACpE;AAoBO,IAAM,qBAAN,MAAuE;EAC5E,YAA6B,SAA0B;AAA1B,SAAA,UAAA;EAA2B;EAA3B;EAE7B,KACE,aACA,YAC8B;AAC9B,WAAO,KAAK,QAAQ,SAAS,EAAE,EAAE;MAC/B,CAAC,QAAQ,cAAc,YAAY,GAAoC,IAAI;MAC3E,cAAc;IAChB;EACF;AACF;AAKO,IAAM,0BAAN,MAAmF;EACxF,YAA6B,SAA0B;AAA1B,SAAA,UAAA;EAA2B;EAA3B;EAE7B,KACE,aACA,YAC8B;AAC9B,WAAO,KAAK,QAAQ,SAAS,EAAE,EAAE;MAC/B,CAAC,QAAQ,cAAc,YAAY,GAA2C,IAAI;MAClF,cAAc;IAChB;EACF;AACF;AAEO,IAAM,eAAN,MAA6F;EACjF;EACA;EACA;EACT;EAER,YAAY,YAAwB,OAAe,SAA+B;AAChF,sBAAkB,KAAK;AACvB,SAAK,aAAa;AAClB,SAAK,WAAW,SAAS,YAAY,UAAU,KAAK;AACpD,SAAK,SAAS,SAAS;AACvB,SAAK,QAAQ;MACX,QAAQ;MACR,SAAS,CAAC;MACV,SAAS,CAAC;MACV,QAAQ,CAAC;MACT,UAAU;MACV,eAAe;IACjB;EACF;;EAIA,OAAO,SAAwB;AAC7B,SAAK,QAAQ;MACX,GAAG,KAAK;;;MAGR,QAAQ,KAAK,MAAM,SAAS,SAAY,KAAK,MAAM,SAAS;MAC5D,QAAQ,EAAE,GAAG,KAAK,MAAM,QAAQ,QAAQ,WAAW,IAAI;IACzD;AACA,WAAO;EACT;EAEA,OAAO,MAA6F;AAClG,SAAK,QAAQ;MACX,GAAG,KAAK;MACR,QAAQ;MACR,MAAM;MACN,SAAS;QACP,GAAG,KAAK,MAAM;QACd,QAAQ;MACV;IACF;AACA,WAAO;EACT;EAEA,OAAO,MAAkD;AACvD,SAAK,QAAQ;MACX,GAAG,KAAK;MACR,QAAQ;MACR,MAAM;MACN,SAAS;QACP,GAAG,KAAK,MAAM;QACd,QAAQ;MACV;IACF;AACA,WAAO;EACT;EAEA,SAAe;AACb,SAAK,QAAQ;MACX,GAAG,KAAK;MACR,QAAQ;IACV;AACA,WAAO;EACT;EAEA,OAAO,MAAuC;AAC5C,SAAK,QAAQ;MACX,GAAG,KAAK;MACR,QAAQ;MACR,MAAM;MACN,SAAS;QACP,GAAG,KAAK,MAAM;QACd,QAAQ;MACV;IACF;AACA,WAAO;EACT;;EAIA,GAAG,QAA4D,OAAsB;AACnF,uBAAmB,MAAM;AACzB,SAAK,QAAQ;MACX,GAAG,KAAK;MACR,SAAS,CAAC,GAAG,KAAK,MAAM,SAAS,GAAG,MAAM,OAAO,kBAAkB,KAAK,CAAC,EAAE;IAC7E;AACA,WAAO;EACT;EAEA,IAAI,QAA4D,OAAsB;AACpF,uBAAmB,MAAM;AACzB,SAAK,QAAQ;MACX,GAAG,KAAK;MACR,SAAS,CAAC,GAAG,KAAK,MAAM,SAAS,GAAG,MAAM,QAAQ,kBAAkB,KAAK,CAAC,EAAE;IAC9E;AACA,WAAO;EACT;EAEA,GAAG,QAA4D,OAAsB;AACnF,uBAAmB,MAAM;AACzB,SAAK,QAAQ;MACX,GAAG,KAAK;MACR,SAAS,CAAC,GAAG,KAAK,MAAM,SAAS,GAAG,MAAM,OAAO,kBAAkB,KAAK,CAAC,EAAE;IAC7E;AACA,WAAO;EACT;EAEA,IAAI,QAA4D,OAAsB;AACpF,uBAAmB,MAAM;AACzB,SAAK,QAAQ;MACX,GAAG,KAAK;MACR,SAAS,CAAC,GAAG,KAAK,MAAM,SAAS,GAAG,MAAM,QAAQ,kBAAkB,KAAK,CAAC,EAAE;IAC9E;AACA,WAAO;EACT;EAEA,GAAG,QAA4D,OAAsB;AACnF,uBAAmB,MAAM;AACzB,SAAK,QAAQ;MACX,GAAG,KAAK;MACR,SAAS,CAAC,GAAG,KAAK,MAAM,SAAS,GAAG,MAAM,OAAO,kBAAkB,KAAK,CAAC,EAAE;IAC7E;AACA,WAAO;EACT;EAEA,IAAI,QAA4D,OAAsB;AACpF,uBAAmB,MAAM;AACzB,SAAK,QAAQ;MACX,GAAG,KAAK;MACR,SAAS,CAAC,GAAG,KAAK,MAAM,SAAS,GAAG,MAAM,QAAQ,kBAAkB,KAAK,CAAC,EAAE;IAC9E;AACA,WAAO;EACT;EAEA,KAAK,QAA4D,SAAuB;AACtF,uBAAmB,MAAM;AACzB,SAAK,QAAQ;MACX,GAAG,KAAK;MACR,SAAS,CAAC,GAAG,KAAK,MAAM,SAAS,GAAG,MAAM,SAAS,kBAAkB,OAAO,CAAC,EAAE;IACjF;AACA,WAAO;EACT;EAEA,MAAM,QAA4D,SAAuB;AACvF,uBAAmB,MAAM;AACzB,SAAK,QAAQ;MACX,GAAG,KAAK;MACR,SAAS,CAAC,GAAG,KAAK,MAAM,SAAS,GAAG,MAAM,UAAU,kBAAkB,OAAO,CAAC,EAAE;IAClF;AACA,WAAO;EACT;EAEA,GAAG,QAA4D,QAAyB;AACtF,uBAAmB,MAAM;AACzB,UAAM,UAAU,OAAO,IAAI,CAAC,MAAM,kBAAkB,CAAC,EAAE,QAAQ,SAAS,CAAC,MAAM,mBAAmB,CAAC,CAAC,CAAC;AACrG,SAAK,QAAQ;MACX,GAAG,KAAK;MACR,SAAS,CAAC,GAAG,KAAK,MAAM,SAAS,GAAG,MAAM,QAAQ,QAAQ,KAAK,GAAG,CAAC,GAAG;IACxE;AACA,WAAO;EACT;EAEA,GAAG,QAA4D,OAAsB;AACnF,uBAAmB,MAAM;AACzB,SAAK,QAAQ;MACX,GAAG,KAAK;MACR,SAAS,CAAC,GAAG,KAAK,MAAM,SAAS,GAAG,MAAM,OAAO,kBAAkB,KAAK,CAAC,EAAE;IAC7E;AACA,WAAO;EACT;;EAIA,MAAM,QAA4D,SAA8B;AAC9F,uBAAmB,MAAM;AACzB,UAAM,YAAY,SAAS,cAAc,QAAQ,SAAS;AAC1D,SAAK,QAAQ;MACX,GAAG,KAAK;MACR,QAAQ,EAAE,GAAG,KAAK,MAAM,QAAQ,OAAO,GAAG,MAAM,IAAI,SAAS,GAAG;IAClE;AACA,WAAO;EACT;EAEA,MAAM,OAAqB;AACzB,SAAK,QAAQ;MACX,GAAG,KAAK;MACR,QAAQ,EAAE,GAAG,KAAK,MAAM,QAAQ,OAAO,OAAO,KAAK,EAAE;IACvD;AACA,WAAO;EACT;EAEA,MAAM,MAAc,IAAkB;AACpC,SAAK,QAAQ;MACX,GAAG,KAAK;MACR,SAAS;QACP,GAAG,KAAK,MAAM;QACd,OAAO,GAAG,IAAI,IAAI,EAAE;MACtB;IACF;AACA,WAAO;EACT;EAEA,SAAgC;AAC9B,SAAK,QAAQ;MACX,GAAG,KAAK;MACR,UAAU;MACV,SAAS;QACP,GAAG,KAAK,MAAM;QACd,QAAQ;MACV;IACF;AACA,WAAO,IAAI,mBAAmB,IAAI;EACpC;EAEA,cAA0C;AACxC,SAAK,QAAQ;MACX,GAAG,KAAK;MACR,eAAe;MACf,SAAS;QACP,GAAG,KAAK,MAAM;QACd,QAAQ;MACV;IACF;AACA,WAAO,IAAI,wBAAwB,IAAI;EACzC;;EAIA,KACE,aACA,YAC8B;AAC9B,WAAO,KAAK,QAAQ,EAAE,KAAK,aAAoG,UAAU;EAC3I;EAEQ,YAAoB;AAG1B,UAAM,QAAkB,CAAC;AAEzB,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,MAAM,MAAM,GAAG;AAC5D,YAAM,KAAK,GAAG,GAAG,IAAI,KAAK,EAAE;IAC9B;AAEA,eAAW,UAAU,KAAK,MAAM,SAAS;AACvC,YAAM,KAAK,MAAM;IACnB;AAEA,WAAO,MAAM,SAAS,IAAI,GAAG,KAAK,QAAQ,IAAI,MAAM,KAAK,GAAG,CAAC,KAAK,KAAK;EACzE;EAEA,MAAc,UAAuC;AACnD,UAAM,OAAO,KAAK,UAAU;AAC5B,UAAM,EAAE,QAAQ,MAAM,QAAQ,IAAI,KAAK;AAEvC,UAAM,WAAW,MAAM,KAAK,WAAW,QAAW,QAAQ,MAAM;MAC9D;MACA,SAAS,OAAO,KAAK,OAAO,EAAE,SAAS,IAAI,UAAU;MACrD,QAAQ,KAAK;IACf,CAAC;AAGD,QAAI,KAAK,MAAM,iBAAiB,SAAS,SAAS,SAAS,WAAW,KAAK;AACzE,aAAO,EAAE,MAAM,MAAM,OAAO,MAAM,QAAQ,IAAI;IAChD;AAEA,WAAO;EACT;AACF;ACpVA,IAAM,qBAAqB;AAC3B,IAAM,WAAW;AAMV,IAAM,oBAAN,MAAwB;EACZ;EACA;EACA;EAEjB,YAAY,YAAwB,MAAc,QAAsB;AACtE,SAAK,aAAa;AAClB,SAAK,OAAO;AACZ,SAAK,SAAS;EAChB;EAEA,KAAkC,OAAgC;AAChE,WAAO,IAAI,aAAgB,KAAK,YAAY,OAAO;MACjD,UAAU,sBAAsB,KAAK,IAAI,UAAU,KAAK;MACxD,QAAQ,KAAK;IACf,CAAC;EACH;AACF;AAEA,eAAsB,mBACpB,YACA,IACA,SAC6B;AAC7B,QAAM,YAAY,SAAS,aAAa;AACxC,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,SAAS;AAE5D,MAAI;AAEJ,MAAI;AAEF,UAAM,gBAAgB,MAAM,WAAW;MACrC;MACA;MACA,EAAE,QAAQ,WAAW,OAAO;IAC9B;AAEA,QAAI,cAAc,SAAS,CAAC,cAAc,MAAM;AAC9C,mBAAa,KAAK;AAClB,aAAO;QACL,MAAM;QACN,OAAO,cAAc,SAAS,IAAI;UAChC;UACA;UACA,cAAc;QAChB;QACA,QAAQ,cAAc;MACxB;IACF;AAEA,WAAO,cAAc,KAAK;AAG1B,QAAI,CAAC,SAAS,KAAK,IAAI,GAAG;AACxB,mBAAa,KAAK;AAClB,aAAO;QACL,MAAM;QACN,OAAO,IAAI;UACT;UACA,mCAAmC,IAAI;UACvC;QACF;QACA,QAAQ;MACV;IACF;AAEA,UAAM,KAAK,IAAI,kBAAkB,YAAY,MAAM,WAAW,MAAM;AAGpE,UAAM,SAAS,MAAM,GAAG,EAAE;AAG1B,UAAM,iBAAiB,MAAM,WAAW;MACtC;MACA,sBAAsB,IAAI;MAC1B,EAAE,QAAQ,WAAW,OAAO;IAC9B;AAEA,iBAAa,KAAK;AAElB,QAAI,eAAe,OAAO;AACxB,aAAO;QACL,MAAM;QACN,OAAO,eAAe;QACtB,QAAQ,eAAe;MACzB;IACF;AAEA,WAAO,EAAE,MAAM,QAAQ,OAAO,MAAM,QAAQ,IAAI;EAClD,SAAS,OAAO;AACd,iBAAa,KAAK;AAIlB,QAAI,MAAM;AACR,UAAI;AACF,cAAM,WAAW;UACf;UACA,sBAAsB,IAAI;UAC1B,EAAE,QAAQ,OAAU;QACtB;MACF,QAAQ;MAER;IACF;AAEA,QAAI,iBAAiB,0BAAc;AACjC,aAAO,EAAE,MAAM,MAAM,OAAO,QAAQ,MAAM,OAAO;IACnD;AAEA,UAAM,UAAU,iBAAiB,gBAAgB,MAAM,SAAS;AAChE,UAAM,OAAO,UAAU,wBAAwB;AAC/C,UAAM,UAAU,UACZ,+BAA+B,SAAS,OACxC,iBAAiB,QAAQ,MAAM,UAAU;AAC7C,UAAM,SAAS,UAAU,MAAM;AAE/B,WAAO;MACL,MAAM;MACN,OAAO,IAAI,yBAAa,MAAM,SAAS,MAAM;MAC7C;IACF;EACF;AACF;AClIA,IAAM,aAAa;AAQZ,IAAM,iBAAN,MAAqB;EACT;EACA;EACR;EAET,YAAY,YAAwB,SAAiC;AACnE,SAAK,aAAa;AAClB,SAAK,aAAa,SAAS,cAAc;AACzC,QAAI,SAAS,aAAa;AACxB,WAAK,QAAQ,IAAI,YAAY,UAAU;IACzC;EACF;EAEA,KAAkC,OAAgC;AAChE,WAAO,IAAI,aAAgB,KAAK,YAAY,OAAO;MACjD,UAAU,GAAG,KAAK,UAAU,IAAI,KAAK;IACvC,CAAC;EACH;EAEA,MAAM,IAAiB,QAAgB,QAA+D;AACpG,QAAI,CAAC,WAAW,KAAK,MAAM,GAAG;AAC5B,YAAM,IAAI,MAAM,2BAA2B,MAAM,gCAAgC,WAAW,MAAM,EAAE;IACtG;AAEA,WAAO,KAAK,WAAW,QAAW,QAAQ,GAAG,KAAK,UAAU,QAAQ,MAAM,IAAI;MAC5E,MAAM;IACR,CAAC;EACH;EAEA,MAAM,YACJ,IACA,SAC6B;AAC7B,WAAO,mBAAmB,KAAK,YAAY,IAAI,OAAO;EACxD;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/admin-validation.ts","../src/admin-columns.ts","../src/admin-schemas.ts","../src/admin-tables.ts","../src/admin-client.ts","../src/query-builder.ts","../src/transaction.ts","../src/database-client.ts"],"sourcesContent":["export { DatabaseClient } from './database-client.js';\nexport type { DatabaseClientOptions } from './database-client.js';\nexport { QueryBuilder, SingleQueryBuilder, MaybeSingleQueryBuilder } from './query-builder.js';\nexport type { QueryBuilderOptions } from './query-builder.js';\nexport { TransactionClient, executeTransaction } from './transaction.js';\nexport type { TransactionOptions } from './transaction.js';\nexport type { OrderOptions, GenericSchema, GenericTable } from './types.js';\n","const IDENTIFIER_RE = /^[a-zA-Z_][a-zA-Z0-9_.]*$/;\n\nexport function validateIdentifier(value: string, label: string): void {\n if (!IDENTIFIER_RE.test(value)) {\n throw new Error(\n `Invalid ${label}: \"${value}\". Identifiers must match ${IDENTIFIER_RE.source}`,\n );\n }\n}\n","import type { HttpClient } from '@palbase/core';\nimport type { Column, CreateColumnDef } from './admin-types.js';\nimport { validateIdentifier } from './admin-validation.js';\n\n/**\n * Postgres-meta column admin client.\n *\n * Wraps the `/v1/meta/columns` endpoints exposed by postgres-meta.\n * Use this to add or drop columns on an existing table — `TablesClient.update()`\n * does not handle column structure changes.\n */\nexport class ColumnsClient {\n private readonly httpClient: HttpClient;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n }\n\n /**\n * Create a new column on the given table. Sends `POST /v1/meta/columns`.\n *\n * The column name is validated against the standard SQL identifier regex.\n * The `type` field is forwarded as-is to postgres-meta — callers SHOULD\n * restrict types to a known allowlist before calling this.\n */\n async create(def: CreateColumnDef): Promise<Column> {\n validateIdentifier(def.name, 'column name');\n\n const response = await this.httpClient.request<Column>('POST', '/v1/meta/columns', {\n body: def,\n });\n if (response.error) {\n throw response.error;\n }\n return response.data as Column;\n }\n\n /**\n * Drop a column. Sends `DELETE /v1/meta/columns/:tableId.:ordinalPosition`.\n *\n * postgres-meta identifies columns by `{tableId}.{ordinalPosition}` (the\n * column's 1-based attnum within its table). Use `TablesClient.get(id)` to\n * find a column id from a column name if needed.\n */\n async drop(columnId: string, options?: { cascade?: boolean }): Promise<Column> {\n if (!/^\\d+\\.\\d+$/.test(columnId)) {\n throw new Error(\n `Invalid column id: \"${columnId}\". Expected format \"{tableId}.{ordinalPosition}\"`,\n );\n }\n\n const params = new URLSearchParams();\n if (options?.cascade) {\n params.set('cascade', 'true');\n }\n\n const queryString = params.toString();\n const path = queryString\n ? `/v1/meta/columns/${columnId}?${queryString}`\n : `/v1/meta/columns/${columnId}`;\n\n const response = await this.httpClient.request<Column>('DELETE', path);\n if (response.error) {\n throw response.error;\n }\n return response.data as Column;\n }\n}\n","import type { HttpClient } from '@palbase/core';\nimport type { Schema } from './admin-types.js';\nimport { validateIdentifier } from './admin-validation.js';\n\nexport class SchemasClient {\n private readonly httpClient: HttpClient;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n }\n\n async list(): Promise<Schema[]> {\n const response = await this.httpClient.request<Schema[]>('GET', '/v1/meta/schemas');\n if (response.error) {\n throw response.error;\n }\n return response.data ?? [];\n }\n\n async create(name: string): Promise<Schema> {\n validateIdentifier(name, 'schema name');\n\n const response = await this.httpClient.request<Schema>('POST', '/v1/meta/schemas', {\n body: { name },\n });\n if (response.error) {\n throw response.error;\n }\n return response.data as Schema;\n }\n\n async drop(name: string, options?: { cascade?: boolean }): Promise<void> {\n validateIdentifier(name, 'schema name');\n\n // Resolve name to id first\n const schemas = await this.list();\n const schema = schemas.find((s) => s.name === name);\n if (!schema) {\n throw new Error(`Schema \"${name}\" not found`);\n }\n\n const params = new URLSearchParams();\n if (options?.cascade) {\n params.set('cascade', 'true');\n }\n\n const queryString = params.toString();\n const path = queryString\n ? `/v1/meta/schemas/${schema.id}?${queryString}`\n : `/v1/meta/schemas/${schema.id}`;\n\n const response = await this.httpClient.request<void>('DELETE', path);\n if (response.error) {\n throw response.error;\n }\n }\n}\n","import type { HttpClient } from '@palbase/core';\nimport type { CreateTableDef, Table, UpdateTableDef } from './admin-types.js';\nimport { validateIdentifier } from './admin-validation.js';\n\nexport class TablesClient {\n private readonly httpClient: HttpClient;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n }\n\n async list(options?: { schema?: string }): Promise<Table[]> {\n const params = new URLSearchParams();\n if (options?.schema) {\n validateIdentifier(options.schema, 'schema name');\n params.set('included_schemas', options.schema);\n }\n\n const queryString = params.toString();\n const path = queryString ? `/v1/meta/tables?${queryString}` : '/v1/meta/tables';\n\n const response = await this.httpClient.request<Table[]>('GET', path);\n if (response.error) {\n throw response.error;\n }\n return response.data ?? [];\n }\n\n async get(id: number): Promise<Table> {\n const response = await this.httpClient.request<Table>('GET', `/v1/meta/tables/${id}`);\n if (response.error) {\n throw response.error;\n }\n return response.data as Table;\n }\n\n async create(def: CreateTableDef): Promise<Table> {\n validateIdentifier(def.name, 'table name');\n if (def.schema) {\n validateIdentifier(def.schema, 'schema name');\n }\n\n const response = await this.httpClient.request<Table>('POST', '/v1/meta/tables', {\n body: def,\n });\n if (response.error) {\n throw response.error;\n }\n return response.data as Table;\n }\n\n /**\n * Apply table-level updates (rename, schema move, RLS toggle, comment, etc).\n * Sends `PATCH /v1/meta/tables/:id`.\n *\n * Note: this does NOT add or drop columns. For column changes, use\n * `ColumnsClient.create()` / `ColumnsClient.drop()`.\n */\n async update(id: number, patch: UpdateTableDef): Promise<Table> {\n if (patch.name !== undefined) {\n validateIdentifier(patch.name, 'table name');\n }\n if (patch.schema !== undefined) {\n validateIdentifier(patch.schema, 'schema name');\n }\n if (patch.primary_keys) {\n for (const pk of patch.primary_keys) {\n validateIdentifier(pk.name, 'primary key column name');\n }\n }\n\n const response = await this.httpClient.request<Table>('PATCH', `/v1/meta/tables/${id}`, {\n body: patch,\n });\n if (response.error) {\n throw response.error;\n }\n return response.data as Table;\n }\n\n async drop(id: number, options?: { cascade?: boolean }): Promise<void> {\n const params = new URLSearchParams();\n if (options?.cascade) {\n params.set('cascade', 'true');\n }\n\n const queryString = params.toString();\n const path = queryString\n ? `/v1/meta/tables/${id}?${queryString}`\n : `/v1/meta/tables/${id}`;\n\n const response = await this.httpClient.request<void>('DELETE', path);\n if (response.error) {\n throw response.error;\n }\n }\n}\n","import type { HttpClient } from '@palbase/core';\nimport { ColumnsClient } from './admin-columns.js';\nimport { SchemasClient } from './admin-schemas.js';\nimport { TablesClient } from './admin-tables.js';\n\nexport class AdminClient {\n readonly schemas: SchemasClient;\n readonly tables: TablesClient;\n readonly columns: ColumnsClient;\n\n private readonly httpClient: HttpClient;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n this.schemas = new SchemasClient(httpClient);\n this.tables = new TablesClient(httpClient);\n this.columns = new ColumnsClient(httpClient);\n }\n\n /**\n * Execute a raw SQL query with full privileges (service role).\n *\n * Always use parameterized queries to prevent SQL injection.\n * Never interpolate user input directly into the SQL string.\n *\n * @example\n * ```ts\n * // GOOD — parameterized\n * await admin.query('SELECT * FROM users WHERE id = $1', [userId]);\n *\n * // BAD — string interpolation (SQL injection risk)\n * await admin.query(`SELECT * FROM users WHERE id = '${userId}'`);\n * ```\n */\n async query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]> {\n const response = await this.httpClient.request<T[]>('POST', '/v1/meta/query', {\n body: { query: sql, params },\n });\n if (response.error) {\n throw response.error;\n }\n return response.data ?? [];\n }\n}\n","import type { HttpClient, PalbaseResponse } from '@palbase/core';\nimport type { OrderOptions } from './types.js';\n\ntype HttpMethod = 'GET' | 'POST' | 'PATCH' | 'DELETE';\n\nconst TABLE_NAME_RE = /^[a-zA-Z_][a-zA-Z0-9_.]*$/;\nconst COLUMN_NAME_RE = /^[a-zA-Z_][a-zA-Z0-9_.:\\->#]*$/;\n\nfunction validateTableName(table: string): void {\n if (!TABLE_NAME_RE.test(table)) {\n throw new Error(`Invalid table name: \"${table}\". Table names must match ${TABLE_NAME_RE.source}`);\n }\n}\n\nfunction validateColumnName(column: string): void {\n if (!COLUMN_NAME_RE.test(column)) {\n throw new Error(`Invalid column name: \"${column}\". Column names must match ${COLUMN_NAME_RE.source}`);\n }\n}\n\nfunction encodeFilterValue(value: unknown): string {\n return String(value).replace(/[&#]/g, (c) => encodeURIComponent(c));\n}\n\ninterface QueryState<T> {\n readonly method: HttpMethod;\n readonly body?: Partial<T> | Partial<T>[] | Record<string, unknown> | Record<string, unknown>[];\n readonly headers: Record<string, string>;\n readonly filters: string[];\n readonly params: Record<string, string>;\n readonly isSingle: boolean;\n readonly isMaybeSingle: boolean;\n}\n\nexport interface QueryBuilderOptions {\n basePath?: string;\n signal?: AbortSignal;\n}\n\n/**\n * Wrapper returned by `.single()` — resolves to `PalbaseResponse<T>` (single row).\n */\nexport class SingleQueryBuilder<T> implements PromiseLike<PalbaseResponse<T>> {\n constructor(private readonly builder: QueryBuilder<T>) {}\n\n then<TResult1 = PalbaseResponse<T>, TResult2 = never>(\n onfulfilled?: ((value: PalbaseResponse<T>) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n return this.builder['execute']().then(\n (res) => onfulfilled ? onfulfilled(res as unknown as PalbaseResponse<T>) : res as unknown as TResult1,\n onrejected ?? undefined,\n );\n }\n}\n\n/**\n * Wrapper returned by `.maybeSingle()` — resolves to `PalbaseResponse<T | null>` (single row or null).\n */\nexport class MaybeSingleQueryBuilder<T> implements PromiseLike<PalbaseResponse<T | null>> {\n constructor(private readonly builder: QueryBuilder<T>) {}\n\n then<TResult1 = PalbaseResponse<T | null>, TResult2 = never>(\n onfulfilled?: ((value: PalbaseResponse<T | null>) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n return this.builder['execute']().then(\n (res) => onfulfilled ? onfulfilled(res as unknown as PalbaseResponse<T | null>) : res as unknown as TResult1,\n onrejected ?? undefined,\n );\n }\n}\n\nexport class QueryBuilder<T = Record<string, unknown>> implements PromiseLike<PalbaseResponse<T[]>> {\n private readonly httpClient: HttpClient;\n private readonly basePath: string;\n private readonly signal?: AbortSignal;\n private state: QueryState<T>;\n\n constructor(httpClient: HttpClient, table: string, options?: QueryBuilderOptions) {\n validateTableName(table);\n this.httpClient = httpClient;\n this.basePath = options?.basePath ?? `/v1/db/${table}`;\n this.signal = options?.signal;\n this.state = {\n method: 'GET',\n headers: {},\n filters: [],\n params: {},\n isSingle: false,\n isMaybeSingle: false,\n };\n }\n\n // --- Operation methods ---\n\n select(columns?: string): this {\n this.state = {\n ...this.state,\n // Only set GET if no mutation method (POST/PATCH/DELETE) is already set\n // insert().select() should stay POST with Prefer: return=representation\n method: this.state.body !== undefined ? this.state.method : 'GET',\n params: { ...this.state.params, select: columns ?? '*' },\n };\n return this;\n }\n\n insert(data: Partial<T> | Partial<T>[] | Record<string, unknown> | Record<string, unknown>[]): this {\n this.state = {\n ...this.state,\n method: 'POST',\n body: data,\n headers: {\n ...this.state.headers,\n Prefer: 'return=representation',\n },\n };\n return this;\n }\n\n update(data: Partial<T> | Record<string, unknown>): this {\n this.state = {\n ...this.state,\n method: 'PATCH',\n body: data,\n headers: {\n ...this.state.headers,\n Prefer: 'return=representation',\n },\n };\n return this;\n }\n\n delete(): this {\n this.state = {\n ...this.state,\n method: 'DELETE',\n };\n return this;\n }\n\n upsert(data: Partial<T> | Partial<T>[]): this {\n this.state = {\n ...this.state,\n method: 'POST',\n body: data,\n headers: {\n ...this.state.headers,\n Prefer: 'resolution=merge-duplicates,return=representation',\n },\n };\n return this;\n }\n\n // --- Filter methods ---\n\n eq(column: string & keyof T | (string & Record<never, never>), value: unknown): this {\n validateColumnName(column);\n this.state = {\n ...this.state,\n filters: [...this.state.filters, `${column}=eq.${encodeFilterValue(value)}`],\n };\n return this;\n }\n\n neq(column: string & keyof T | (string & Record<never, never>), value: unknown): this {\n validateColumnName(column);\n this.state = {\n ...this.state,\n filters: [...this.state.filters, `${column}=neq.${encodeFilterValue(value)}`],\n };\n return this;\n }\n\n gt(column: string & keyof T | (string & Record<never, never>), value: unknown): this {\n validateColumnName(column);\n this.state = {\n ...this.state,\n filters: [...this.state.filters, `${column}=gt.${encodeFilterValue(value)}`],\n };\n return this;\n }\n\n gte(column: string & keyof T | (string & Record<never, never>), value: unknown): this {\n validateColumnName(column);\n this.state = {\n ...this.state,\n filters: [...this.state.filters, `${column}=gte.${encodeFilterValue(value)}`],\n };\n return this;\n }\n\n lt(column: string & keyof T | (string & Record<never, never>), value: unknown): this {\n validateColumnName(column);\n this.state = {\n ...this.state,\n filters: [...this.state.filters, `${column}=lt.${encodeFilterValue(value)}`],\n };\n return this;\n }\n\n lte(column: string & keyof T | (string & Record<never, never>), value: unknown): this {\n validateColumnName(column);\n this.state = {\n ...this.state,\n filters: [...this.state.filters, `${column}=lte.${encodeFilterValue(value)}`],\n };\n return this;\n }\n\n like(column: string & keyof T | (string & Record<never, never>), pattern: string): this {\n validateColumnName(column);\n this.state = {\n ...this.state,\n filters: [...this.state.filters, `${column}=like.${encodeFilterValue(pattern)}`],\n };\n return this;\n }\n\n ilike(column: string & keyof T | (string & Record<never, never>), pattern: string): this {\n validateColumnName(column);\n this.state = {\n ...this.state,\n filters: [...this.state.filters, `${column}=ilike.${encodeFilterValue(pattern)}`],\n };\n return this;\n }\n\n in(column: string & keyof T | (string & Record<never, never>), values: unknown[]): this {\n validateColumnName(column);\n const encoded = values.map((v) => encodeFilterValue(v).replace(/[),]/g, (c) => encodeURIComponent(c)));\n this.state = {\n ...this.state,\n filters: [...this.state.filters, `${column}=in.(${encoded.join(',')})`],\n };\n return this;\n }\n\n is(column: string & keyof T | (string & Record<never, never>), value: unknown): this {\n validateColumnName(column);\n this.state = {\n ...this.state,\n filters: [...this.state.filters, `${column}=is.${encodeFilterValue(value)}`],\n };\n return this;\n }\n\n // --- Modifier methods ---\n\n order(column: string & keyof T | (string & Record<never, never>), options?: OrderOptions): this {\n validateColumnName(column);\n const direction = options?.ascending === false ? 'desc' : 'asc';\n this.state = {\n ...this.state,\n params: { ...this.state.params, order: `${column}.${direction}` },\n };\n return this;\n }\n\n limit(count: number): this {\n this.state = {\n ...this.state,\n params: { ...this.state.params, limit: String(count) },\n };\n return this;\n }\n\n range(from: number, to: number): this {\n this.state = {\n ...this.state,\n headers: {\n ...this.state.headers,\n Range: `${from}-${to}`,\n },\n };\n return this;\n }\n\n single(): SingleQueryBuilder<T> {\n this.state = {\n ...this.state,\n isSingle: true,\n headers: {\n ...this.state.headers,\n Accept: 'application/vnd.pgrst.object+json',\n },\n };\n return new SingleQueryBuilder(this);\n }\n\n maybeSingle(): MaybeSingleQueryBuilder<T> {\n this.state = {\n ...this.state,\n isMaybeSingle: true,\n headers: {\n ...this.state.headers,\n Accept: 'application/vnd.pgrst.object+json',\n },\n };\n return new MaybeSingleQueryBuilder(this);\n }\n\n // --- Execution ---\n\n then<TResult1 = PalbaseResponse<T[]>, TResult2 = never>(\n onfulfilled?: ((value: PalbaseResponse<T[]>) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n return this.execute().then(onfulfilled as unknown as ((value: PalbaseResponse<T>) => TResult1 | PromiseLike<TResult1>) | null, onrejected);\n }\n\n private buildPath(): string {\n // Build query string manually — URLSearchParams percent-encodes\n // parentheses and commas which breaks PostgREST filter syntax\n const parts: string[] = [];\n\n for (const [key, value] of Object.entries(this.state.params)) {\n parts.push(`${key}=${value}`);\n }\n\n for (const filter of this.state.filters) {\n parts.push(filter);\n }\n\n return parts.length > 0 ? `${this.basePath}?${parts.join('&')}` : this.basePath;\n }\n\n private async execute(): Promise<PalbaseResponse<T>> {\n const path = this.buildPath();\n const { method, body, headers } = this.state;\n\n const response = await this.httpClient.request<T>(method, path, {\n body: body,\n headers: Object.keys(headers).length > 0 ? headers : undefined,\n signal: this.signal,\n });\n\n // For maybeSingle, convert 406 (no rows) to { data: null, error: null }\n if (this.state.isMaybeSingle && response.error && response.status === 406) {\n return { data: null, error: null, status: 200 };\n }\n\n return response;\n }\n}\n","import type { HttpClient, PalbaseResponse } from '@palbase/core';\nimport { PalbaseError } from '@palbase/core';\nimport { QueryBuilder } from './query-builder.js';\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst TX_ID_RE = /^[a-zA-Z0-9_\\-]+$/;\n\nexport interface TransactionOptions {\n timeoutMs?: number;\n}\n\nexport class TransactionClient {\n private readonly httpClient: HttpClient;\n private readonly txId: string;\n private readonly signal?: AbortSignal;\n\n constructor(httpClient: HttpClient, txId: string, signal?: AbortSignal) {\n this.httpClient = httpClient;\n this.txId = txId;\n this.signal = signal;\n }\n\n from<T = Record<string, unknown>>(table: string): QueryBuilder<T> {\n return new QueryBuilder<T>(this.httpClient, table, {\n basePath: `/v1/db/transaction/${this.txId}/query/${table}`,\n signal: this.signal,\n });\n }\n}\n\nexport async function executeTransaction<T>(\n httpClient: HttpClient,\n fn: (tx: TransactionClient) => Promise<T>,\n options?: TransactionOptions,\n): Promise<PalbaseResponse<T>> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n let txId: string | undefined;\n\n try {\n // Begin transaction\n const beginResponse = await httpClient.request<{ txId: string }>(\n 'POST',\n '/v1/db/transaction/begin',\n { signal: controller.signal },\n );\n\n if (beginResponse.error || !beginResponse.data) {\n clearTimeout(timer);\n return {\n data: null,\n error: beginResponse.error ?? new PalbaseError(\n 'transaction_error',\n 'Failed to begin transaction',\n beginResponse.status,\n ),\n status: beginResponse.status,\n };\n }\n\n txId = beginResponse.data.txId;\n\n // Validate txId format to prevent path traversal from a compromised server response\n if (!TX_ID_RE.test(txId)) {\n clearTimeout(timer);\n return {\n data: null,\n error: new PalbaseError(\n 'transaction_error',\n `Invalid transaction ID format: \"${txId}\"`,\n 0,\n ),\n status: 0,\n };\n }\n\n const tx = new TransactionClient(httpClient, txId, controller.signal);\n\n // Execute user function\n const result = await fn(tx);\n\n // Commit\n const commitResponse = await httpClient.request<unknown>(\n 'POST',\n `/v1/db/transaction/${txId}/commit`,\n { signal: controller.signal },\n );\n\n clearTimeout(timer);\n\n if (commitResponse.error) {\n return {\n data: null,\n error: commitResponse.error,\n status: commitResponse.status,\n };\n }\n\n return { data: result, error: null, status: 200 };\n } catch (error) {\n clearTimeout(timer);\n\n // Rollback if we have a transaction ID.\n // Intentionally no signal — rollback must complete even if the timeout controller aborted.\n if (txId) {\n try {\n await httpClient.request<unknown>(\n 'POST',\n `/v1/db/transaction/${txId}/rollback`,\n { signal: undefined },\n );\n } catch {\n // Rollback failed — nothing we can do, return the original error\n }\n }\n\n if (error instanceof PalbaseError) {\n return { data: null, error, status: error.status };\n }\n\n const isAbort = error instanceof DOMException && error.name === 'AbortError';\n const code = isAbort ? 'transaction_timeout' : 'transaction_error';\n const message = isAbort\n ? `Transaction timed out after ${timeoutMs}ms`\n : error instanceof Error ? error.message : 'Transaction failed';\n const status = isAbort ? 408 : 0;\n\n return {\n data: null,\n error: new PalbaseError(code, message, status),\n status,\n };\n }\n}\n","import type { HttpClient, PalbaseResponse } from '@palbase/core';\nimport { AdminClient } from './admin-client.js';\nimport { QueryBuilder } from './query-builder.js';\nimport { type TransactionOptions, executeTransaction } from './transaction.js';\nimport type { TransactionClient } from './transaction.js';\nconst FN_NAME_RE = /^[a-zA-Z_][a-zA-Z0-9_.]*$/;\n\nexport interface DatabaseClientOptions {\n enableAdmin?: boolean;\n /** Override the default `/v1/db` path prefix. Set to empty string for direct PostgREST access. */\n pathPrefix?: string;\n}\n\nexport class DatabaseClient {\n private readonly httpClient: HttpClient;\n private readonly pathPrefix: string;\n readonly admin?: AdminClient;\n\n constructor(httpClient: HttpClient, options?: DatabaseClientOptions) {\n this.httpClient = httpClient;\n this.pathPrefix = options?.pathPrefix ?? '/v1/db';\n if (options?.enableAdmin) {\n this.admin = new AdminClient(httpClient);\n }\n }\n\n from<T = Record<string, unknown>>(table: string): QueryBuilder<T> {\n return new QueryBuilder<T>(this.httpClient, table, {\n basePath: `${this.pathPrefix}/${table}`,\n });\n }\n\n async rpc<T = unknown>(fnName: string, params?: Record<string, unknown>): Promise<PalbaseResponse<T>> {\n if (!FN_NAME_RE.test(fnName)) {\n throw new Error(`Invalid function name: \"${fnName}\". Function names must match ${FN_NAME_RE.source}`);\n }\n\n return this.httpClient.request<T>('POST', `${this.pathPrefix}/rpc/${fnName}`, {\n body: params,\n });\n }\n\n async transaction<T>(\n fn: (tx: TransactionClient) => Promise<T>,\n options?: TransactionOptions,\n ): Promise<PalbaseResponse<T>> {\n return executeTransaction(this.httpClient, fn, options);\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -1 +1,96 @@
1
- export { DatabaseClient, GenericSchema, GenericTable, OrderOptions, QueryBuilder, QueryBuilderOptions, TransactionClient, TransactionOptions } from '@palbase/modules-db';
1
+ import { PalbaseResponse, HttpClient } from '@palbase/core';
2
+ import { AdminClient } from './admin.cjs';
3
+
4
+ interface OrderOptions {
5
+ ascending?: boolean;
6
+ }
7
+ interface GenericTable {
8
+ Row: Record<string, unknown>;
9
+ }
10
+ interface GenericSchema {
11
+ [schema: string]: {
12
+ Tables: {
13
+ [table: string]: GenericTable;
14
+ };
15
+ };
16
+ }
17
+
18
+ interface QueryBuilderOptions {
19
+ basePath?: string;
20
+ signal?: AbortSignal;
21
+ }
22
+ /**
23
+ * Wrapper returned by `.single()` — resolves to `PalbaseResponse<T>` (single row).
24
+ */
25
+ declare class SingleQueryBuilder<T> implements PromiseLike<PalbaseResponse<T>> {
26
+ private readonly builder;
27
+ constructor(builder: QueryBuilder<T>);
28
+ then<TResult1 = PalbaseResponse<T>, TResult2 = never>(onfulfilled?: ((value: PalbaseResponse<T>) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
29
+ }
30
+ /**
31
+ * Wrapper returned by `.maybeSingle()` — resolves to `PalbaseResponse<T | null>` (single row or null).
32
+ */
33
+ declare class MaybeSingleQueryBuilder<T> implements PromiseLike<PalbaseResponse<T | null>> {
34
+ private readonly builder;
35
+ constructor(builder: QueryBuilder<T>);
36
+ then<TResult1 = PalbaseResponse<T | null>, TResult2 = never>(onfulfilled?: ((value: PalbaseResponse<T | null>) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
37
+ }
38
+ declare class QueryBuilder<T = Record<string, unknown>> implements PromiseLike<PalbaseResponse<T[]>> {
39
+ private readonly httpClient;
40
+ private readonly basePath;
41
+ private readonly signal?;
42
+ private state;
43
+ constructor(httpClient: HttpClient, table: string, options?: QueryBuilderOptions);
44
+ select(columns?: string): this;
45
+ insert(data: Partial<T> | Partial<T>[] | Record<string, unknown> | Record<string, unknown>[]): this;
46
+ update(data: Partial<T> | Record<string, unknown>): this;
47
+ delete(): this;
48
+ upsert(data: Partial<T> | Partial<T>[]): this;
49
+ eq(column: string & keyof T | (string & Record<never, never>), value: unknown): this;
50
+ neq(column: string & keyof T | (string & Record<never, never>), value: unknown): this;
51
+ gt(column: string & keyof T | (string & Record<never, never>), value: unknown): this;
52
+ gte(column: string & keyof T | (string & Record<never, never>), value: unknown): this;
53
+ lt(column: string & keyof T | (string & Record<never, never>), value: unknown): this;
54
+ lte(column: string & keyof T | (string & Record<never, never>), value: unknown): this;
55
+ like(column: string & keyof T | (string & Record<never, never>), pattern: string): this;
56
+ ilike(column: string & keyof T | (string & Record<never, never>), pattern: string): this;
57
+ in(column: string & keyof T | (string & Record<never, never>), values: unknown[]): this;
58
+ is(column: string & keyof T | (string & Record<never, never>), value: unknown): this;
59
+ order(column: string & keyof T | (string & Record<never, never>), options?: OrderOptions): this;
60
+ limit(count: number): this;
61
+ range(from: number, to: number): this;
62
+ single(): SingleQueryBuilder<T>;
63
+ maybeSingle(): MaybeSingleQueryBuilder<T>;
64
+ then<TResult1 = PalbaseResponse<T[]>, TResult2 = never>(onfulfilled?: ((value: PalbaseResponse<T[]>) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
65
+ private buildPath;
66
+ private execute;
67
+ }
68
+
69
+ interface TransactionOptions {
70
+ timeoutMs?: number;
71
+ }
72
+ declare class TransactionClient {
73
+ private readonly httpClient;
74
+ private readonly txId;
75
+ private readonly signal?;
76
+ constructor(httpClient: HttpClient, txId: string, signal?: AbortSignal);
77
+ from<T = Record<string, unknown>>(table: string): QueryBuilder<T>;
78
+ }
79
+ declare function executeTransaction<T>(httpClient: HttpClient, fn: (tx: TransactionClient) => Promise<T>, options?: TransactionOptions): Promise<PalbaseResponse<T>>;
80
+
81
+ interface DatabaseClientOptions {
82
+ enableAdmin?: boolean;
83
+ /** Override the default `/v1/db` path prefix. Set to empty string for direct PostgREST access. */
84
+ pathPrefix?: string;
85
+ }
86
+ declare class DatabaseClient {
87
+ private readonly httpClient;
88
+ private readonly pathPrefix;
89
+ readonly admin?: AdminClient;
90
+ constructor(httpClient: HttpClient, options?: DatabaseClientOptions);
91
+ from<T = Record<string, unknown>>(table: string): QueryBuilder<T>;
92
+ rpc<T = unknown>(fnName: string, params?: Record<string, unknown>): Promise<PalbaseResponse<T>>;
93
+ transaction<T>(fn: (tx: TransactionClient) => Promise<T>, options?: TransactionOptions): Promise<PalbaseResponse<T>>;
94
+ }
95
+
96
+ export { DatabaseClient, type DatabaseClientOptions, type GenericSchema, type GenericTable, MaybeSingleQueryBuilder, type OrderOptions, QueryBuilder, type QueryBuilderOptions, SingleQueryBuilder, TransactionClient, type TransactionOptions, executeTransaction };