@palbase/db 0.6.0 → 0.8.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.
- package/dist/admin.cjs +36 -23
- package/dist/admin.cjs.map +1 -1
- package/dist/admin.d.cts +21 -7
- package/dist/admin.d.ts +21 -7
- package/dist/admin.js +36 -23
- package/dist/admin.js.map +1 -1
- package/package.json +2 -2
package/dist/admin.cjs
CHANGED
|
@@ -40,11 +40,13 @@ function validateIdentifier(value, label) {
|
|
|
40
40
|
// src/admin-columns.ts
|
|
41
41
|
var ColumnsClient = class {
|
|
42
42
|
httpClient;
|
|
43
|
-
|
|
43
|
+
basePath;
|
|
44
|
+
constructor(httpClient, basePath = "/v1/meta") {
|
|
44
45
|
this.httpClient = httpClient;
|
|
46
|
+
this.basePath = basePath;
|
|
45
47
|
}
|
|
46
48
|
/**
|
|
47
|
-
* Create a new column on the given table. Sends `POST
|
|
49
|
+
* Create a new column on the given table. Sends `POST <basePath>/columns`.
|
|
48
50
|
*
|
|
49
51
|
* The column name is validated against the standard SQL identifier regex.
|
|
50
52
|
* The `type` field is forwarded as-is to postgres-meta — callers SHOULD
|
|
@@ -52,7 +54,7 @@ var ColumnsClient = class {
|
|
|
52
54
|
*/
|
|
53
55
|
async create(def) {
|
|
54
56
|
validateIdentifier(def.name, "column name");
|
|
55
|
-
const response = await this.httpClient.request("POST",
|
|
57
|
+
const response = await this.httpClient.request("POST", `${this.basePath}/columns`, {
|
|
56
58
|
body: def
|
|
57
59
|
});
|
|
58
60
|
if (response.error) {
|
|
@@ -61,7 +63,7 @@ var ColumnsClient = class {
|
|
|
61
63
|
return response.data;
|
|
62
64
|
}
|
|
63
65
|
/**
|
|
64
|
-
* Drop a column. Sends `DELETE
|
|
66
|
+
* Drop a column. Sends `DELETE <basePath>/columns/:tableId.:ordinalPosition`.
|
|
65
67
|
*
|
|
66
68
|
* postgres-meta identifies columns by `{tableId}.{ordinalPosition}` (the
|
|
67
69
|
* column's 1-based attnum within its table). Use `TablesClient.get(id)` to
|
|
@@ -78,7 +80,7 @@ var ColumnsClient = class {
|
|
|
78
80
|
params.set("cascade", "true");
|
|
79
81
|
}
|
|
80
82
|
const queryString = params.toString();
|
|
81
|
-
const path = queryString ?
|
|
83
|
+
const path = queryString ? `${this.basePath}/columns/${columnId}?${queryString}` : `${this.basePath}/columns/${columnId}`;
|
|
82
84
|
const response = await this.httpClient.request("DELETE", path);
|
|
83
85
|
if (response.error) {
|
|
84
86
|
throw response.error;
|
|
@@ -90,11 +92,13 @@ var ColumnsClient = class {
|
|
|
90
92
|
// src/admin-schemas.ts
|
|
91
93
|
var SchemasClient = class {
|
|
92
94
|
httpClient;
|
|
93
|
-
|
|
95
|
+
basePath;
|
|
96
|
+
constructor(httpClient, basePath = "/v1/meta") {
|
|
94
97
|
this.httpClient = httpClient;
|
|
98
|
+
this.basePath = basePath;
|
|
95
99
|
}
|
|
96
100
|
async list() {
|
|
97
|
-
const response = await this.httpClient.request("GET",
|
|
101
|
+
const response = await this.httpClient.request("GET", `${this.basePath}/schemas`);
|
|
98
102
|
if (response.error) {
|
|
99
103
|
throw response.error;
|
|
100
104
|
}
|
|
@@ -102,7 +106,7 @@ var SchemasClient = class {
|
|
|
102
106
|
}
|
|
103
107
|
async create(name) {
|
|
104
108
|
validateIdentifier(name, "schema name");
|
|
105
|
-
const response = await this.httpClient.request("POST",
|
|
109
|
+
const response = await this.httpClient.request("POST", `${this.basePath}/schemas`, {
|
|
106
110
|
body: { name }
|
|
107
111
|
});
|
|
108
112
|
if (response.error) {
|
|
@@ -122,7 +126,7 @@ var SchemasClient = class {
|
|
|
122
126
|
params.set("cascade", "true");
|
|
123
127
|
}
|
|
124
128
|
const queryString = params.toString();
|
|
125
|
-
const path = queryString ?
|
|
129
|
+
const path = queryString ? `${this.basePath}/schemas/${schema.id}?${queryString}` : `${this.basePath}/schemas/${schema.id}`;
|
|
126
130
|
const response = await this.httpClient.request("DELETE", path);
|
|
127
131
|
if (response.error) {
|
|
128
132
|
throw response.error;
|
|
@@ -133,8 +137,10 @@ var SchemasClient = class {
|
|
|
133
137
|
// src/admin-tables.ts
|
|
134
138
|
var TablesClient = class {
|
|
135
139
|
httpClient;
|
|
136
|
-
|
|
140
|
+
basePath;
|
|
141
|
+
constructor(httpClient, basePath = "/v1/meta") {
|
|
137
142
|
this.httpClient = httpClient;
|
|
143
|
+
this.basePath = basePath;
|
|
138
144
|
}
|
|
139
145
|
async list(options) {
|
|
140
146
|
const params = new URLSearchParams();
|
|
@@ -143,7 +149,7 @@ var TablesClient = class {
|
|
|
143
149
|
params.set("included_schemas", options.schema);
|
|
144
150
|
}
|
|
145
151
|
const queryString = params.toString();
|
|
146
|
-
const path = queryString ?
|
|
152
|
+
const path = queryString ? `${this.basePath}/tables?${queryString}` : `${this.basePath}/tables`;
|
|
147
153
|
const response = await this.httpClient.request("GET", path);
|
|
148
154
|
if (response.error) {
|
|
149
155
|
throw response.error;
|
|
@@ -151,7 +157,10 @@ var TablesClient = class {
|
|
|
151
157
|
return response.data ?? [];
|
|
152
158
|
}
|
|
153
159
|
async get(id) {
|
|
154
|
-
const response = await this.httpClient.request(
|
|
160
|
+
const response = await this.httpClient.request(
|
|
161
|
+
"GET",
|
|
162
|
+
`${this.basePath}/tables/${id}`
|
|
163
|
+
);
|
|
155
164
|
if (response.error) {
|
|
156
165
|
throw response.error;
|
|
157
166
|
}
|
|
@@ -162,7 +171,7 @@ var TablesClient = class {
|
|
|
162
171
|
if (def.schema) {
|
|
163
172
|
validateIdentifier(def.schema, "schema name");
|
|
164
173
|
}
|
|
165
|
-
const response = await this.httpClient.request("POST",
|
|
174
|
+
const response = await this.httpClient.request("POST", `${this.basePath}/tables`, {
|
|
166
175
|
body: def
|
|
167
176
|
});
|
|
168
177
|
if (response.error) {
|
|
@@ -172,7 +181,7 @@ var TablesClient = class {
|
|
|
172
181
|
}
|
|
173
182
|
/**
|
|
174
183
|
* Apply table-level updates (rename, schema move, RLS toggle, comment, etc).
|
|
175
|
-
* Sends `PATCH
|
|
184
|
+
* Sends `PATCH <basePath>/tables/:id`.
|
|
176
185
|
*
|
|
177
186
|
* Note: this does NOT add or drop columns. For column changes, use
|
|
178
187
|
* `ColumnsClient.create()` / `ColumnsClient.drop()`.
|
|
@@ -189,9 +198,11 @@ var TablesClient = class {
|
|
|
189
198
|
validateIdentifier(pk.name, "primary key column name");
|
|
190
199
|
}
|
|
191
200
|
}
|
|
192
|
-
const response = await this.httpClient.request(
|
|
193
|
-
|
|
194
|
-
|
|
201
|
+
const response = await this.httpClient.request(
|
|
202
|
+
"PATCH",
|
|
203
|
+
`${this.basePath}/tables/${id}`,
|
|
204
|
+
{ body: patch }
|
|
205
|
+
);
|
|
195
206
|
if (response.error) {
|
|
196
207
|
throw response.error;
|
|
197
208
|
}
|
|
@@ -203,7 +214,7 @@ var TablesClient = class {
|
|
|
203
214
|
params.set("cascade", "true");
|
|
204
215
|
}
|
|
205
216
|
const queryString = params.toString();
|
|
206
|
-
const path = queryString ?
|
|
217
|
+
const path = queryString ? `${this.basePath}/tables/${id}?${queryString}` : `${this.basePath}/tables/${id}`;
|
|
207
218
|
const response = await this.httpClient.request("DELETE", path);
|
|
208
219
|
if (response.error) {
|
|
209
220
|
throw response.error;
|
|
@@ -217,11 +228,13 @@ var AdminClient = class {
|
|
|
217
228
|
tables;
|
|
218
229
|
columns;
|
|
219
230
|
httpClient;
|
|
220
|
-
|
|
231
|
+
basePath;
|
|
232
|
+
constructor(httpClient, options) {
|
|
221
233
|
this.httpClient = httpClient;
|
|
222
|
-
this.
|
|
223
|
-
this.
|
|
224
|
-
this.
|
|
234
|
+
this.basePath = options?.basePath ?? "/v1/meta";
|
|
235
|
+
this.schemas = new SchemasClient(httpClient, this.basePath);
|
|
236
|
+
this.tables = new TablesClient(httpClient, this.basePath);
|
|
237
|
+
this.columns = new ColumnsClient(httpClient, this.basePath);
|
|
225
238
|
}
|
|
226
239
|
/**
|
|
227
240
|
* Execute a raw SQL query with full privileges (service role).
|
|
@@ -248,7 +261,7 @@ var AdminClient = class {
|
|
|
248
261
|
*/
|
|
249
262
|
async query(sql, params, options) {
|
|
250
263
|
const wrappedQuery = options?.readOnly ? `BEGIN; SET TRANSACTION READ ONLY; ${stripTrailingSemicolon(sql)}; COMMIT;` : sql;
|
|
251
|
-
const response = await this.httpClient.request("POST",
|
|
264
|
+
const response = await this.httpClient.request("POST", `${this.basePath}/query`, {
|
|
252
265
|
body: { query: wrappedQuery, params }
|
|
253
266
|
});
|
|
254
267
|
if (response.error) {
|
package/dist/admin.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/admin.ts","../src/admin-validation.ts","../src/admin-columns.ts","../src/admin-schemas.ts","../src/admin-tables.ts","../src/admin-client.ts"],"sourcesContent":["export { AdminClient } from './admin-client.js';\nexport type { QueryOptions } from './admin-client.js';\nexport { ColumnsClient } from './admin-columns.js';\nexport { SchemasClient } from './admin-schemas.js';\nexport { TablesClient } from './admin-tables.js';\nexport type {\n Column,\n ColumnDef,\n CreateColumnDef,\n CreateTableDef,\n Schema,\n Table,\n TablePrimaryKey,\n TableRelationship,\n UpdateTableDef,\n} from './admin-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 * Pass `{ readOnly: true }` to wrap the query in\n * `BEGIN; SET TRANSACTION READ ONLY; <query>; COMMIT;` so Postgres\n * rejects any write/DDL statement at the server — safe mode for\n * Studio's SQL editor and other consumer-facing query surfaces.\n *\n * @example\n * ```ts\n * // GOOD — parameterized\n * await admin.query('SELECT * FROM users WHERE id = $1', [userId]);\n *\n * // Read-only — Postgres enforces the denial (errcode 25006).\n * await admin.query('SELECT count(*) FROM users', [], { readOnly: true });\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>>(\n sql: string,\n params?: unknown[],\n options?: QueryOptions,\n ): Promise<T[]> {\n const wrappedQuery = options?.readOnly\n ? `BEGIN; SET TRANSACTION READ ONLY; ${stripTrailingSemicolon(sql)}; COMMIT;`\n : sql;\n const response = await this.httpClient.request<T[]>('POST', '/v1/meta/query', {\n body: { query: wrappedQuery, params },\n });\n if (response.error) {\n throw response.error;\n }\n return response.data ?? [];\n }\n}\n\nexport interface QueryOptions {\n /**\n * Run the query in a read-only Postgres transaction. Any INSERT /\n * UPDATE / DELETE / DDL statement inside the wrapped block is\n * rejected by the server with SQLSTATE 25006 — the SDK isn't the\n * guard, Postgres is. Useful for UI surfaces (SQL editor, data\n * explorer) that must not mutate.\n */\n readOnly?: boolean;\n}\n\n/** Trim the final `;` so our BEGIN/COMMIT wrapper doesn't close twice. */\nfunction stripTrailingSemicolon(sql: string): string {\n return sql.replace(/;\\s*$/, '');\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/admin.ts","../src/admin-validation.ts","../src/admin-columns.ts","../src/admin-schemas.ts","../src/admin-tables.ts","../src/admin-client.ts"],"sourcesContent":["export { AdminClient } from './admin-client.js';\nexport type { QueryOptions } from './admin-client.js';\nexport { ColumnsClient } from './admin-columns.js';\nexport { SchemasClient } from './admin-schemas.js';\nexport { TablesClient } from './admin-tables.js';\nexport type {\n Column,\n ColumnDef,\n CreateColumnDef,\n CreateTableDef,\n Schema,\n Table,\n TablePrimaryKey,\n TableRelationship,\n UpdateTableDef,\n} from './admin-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 private readonly basePath: string;\n\n constructor(httpClient: HttpClient, basePath = '/v1/meta') {\n this.httpClient = httpClient;\n this.basePath = basePath;\n }\n\n /**\n * Create a new column on the given table. Sends `POST <basePath>/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', `${this.basePath}/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 <basePath>/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 ? `${this.basePath}/columns/${columnId}?${queryString}`\n : `${this.basePath}/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 private readonly basePath: string;\n\n constructor(httpClient: HttpClient, basePath = '/v1/meta') {\n this.httpClient = httpClient;\n this.basePath = basePath;\n }\n\n async list(): Promise<Schema[]> {\n const response = await this.httpClient.request<Schema[]>('GET', `${this.basePath}/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', `${this.basePath}/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 ? `${this.basePath}/schemas/${schema.id}?${queryString}`\n : `${this.basePath}/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 private readonly basePath: string;\n\n constructor(httpClient: HttpClient, basePath = '/v1/meta') {\n this.httpClient = httpClient;\n this.basePath = basePath;\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\n ? `${this.basePath}/tables?${queryString}`\n : `${this.basePath}/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>(\n 'GET',\n `${this.basePath}/tables/${id}`,\n );\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', `${this.basePath}/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 <basePath>/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>(\n 'PATCH',\n `${this.basePath}/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 ? `${this.basePath}/tables/${id}?${queryString}`\n : `${this.basePath}/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 interface AdminClientOptions {\n /**\n * Override the default postgres-meta path prefix (`/v1/meta`). When\n * the gateway in front of postgres-meta requires a project-scoped\n * prefix (e.g. Kong's `/v1/pg-meta/admin/projects/<ref>`), pass it\n * here — every schema / table / column request then rides that\n * prefix unchanged.\n */\n basePath?: string;\n}\n\nexport class AdminClient {\n readonly schemas: SchemasClient;\n readonly tables: TablesClient;\n readonly columns: ColumnsClient;\n\n private readonly httpClient: HttpClient;\n private readonly basePath: string;\n\n constructor(httpClient: HttpClient, options?: AdminClientOptions) {\n this.httpClient = httpClient;\n this.basePath = options?.basePath ?? '/v1/meta';\n this.schemas = new SchemasClient(httpClient, this.basePath);\n this.tables = new TablesClient(httpClient, this.basePath);\n this.columns = new ColumnsClient(httpClient, this.basePath);\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 * Pass `{ readOnly: true }` to wrap the query in\n * `BEGIN; SET TRANSACTION READ ONLY; <query>; COMMIT;` so Postgres\n * rejects any write/DDL statement at the server — safe mode for\n * Studio's SQL editor and other consumer-facing query surfaces.\n *\n * @example\n * ```ts\n * // GOOD — parameterized\n * await admin.query('SELECT * FROM users WHERE id = $1', [userId]);\n *\n * // Read-only — Postgres enforces the denial (errcode 25006).\n * await admin.query('SELECT count(*) FROM users', [], { readOnly: true });\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>>(\n sql: string,\n params?: unknown[],\n options?: QueryOptions,\n ): Promise<T[]> {\n const wrappedQuery = options?.readOnly\n ? `BEGIN; SET TRANSACTION READ ONLY; ${stripTrailingSemicolon(sql)}; COMMIT;`\n : sql;\n const response = await this.httpClient.request<T[]>('POST', `${this.basePath}/query`, {\n body: { query: wrappedQuery, params },\n });\n if (response.error) {\n throw response.error;\n }\n return response.data ?? [];\n }\n}\n\nexport interface QueryOptions {\n /**\n * Run the query in a read-only Postgres transaction. Any INSERT /\n * UPDATE / DELETE / DDL statement inside the wrapped block is\n * rejected by the server with SQLSTATE 25006 — the SDK isn't the\n * guard, Postgres is. Useful for UI surfaces (SQL editor, data\n * explorer) that must not mutate.\n */\n readOnly?: boolean;\n}\n\n/** Trim the final `;` so our BEGIN/COMMIT wrapper doesn't close twice. */\nfunction stripTrailingSemicolon(sql: string): string {\n return sql.replace(/;\\s*$/, '');\n}\n"],"mappings":"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","names":[]}
|
package/dist/admin.d.cts
CHANGED
|
@@ -129,9 +129,10 @@ interface CreateColumnDef {
|
|
|
129
129
|
*/
|
|
130
130
|
declare class ColumnsClient {
|
|
131
131
|
private readonly httpClient;
|
|
132
|
-
|
|
132
|
+
private readonly basePath;
|
|
133
|
+
constructor(httpClient: HttpClient, basePath?: string);
|
|
133
134
|
/**
|
|
134
|
-
* Create a new column on the given table. Sends `POST
|
|
135
|
+
* Create a new column on the given table. Sends `POST <basePath>/columns`.
|
|
135
136
|
*
|
|
136
137
|
* The column name is validated against the standard SQL identifier regex.
|
|
137
138
|
* The `type` field is forwarded as-is to postgres-meta — callers SHOULD
|
|
@@ -139,7 +140,7 @@ declare class ColumnsClient {
|
|
|
139
140
|
*/
|
|
140
141
|
create(def: CreateColumnDef): Promise<Column>;
|
|
141
142
|
/**
|
|
142
|
-
* Drop a column. Sends `DELETE
|
|
143
|
+
* Drop a column. Sends `DELETE <basePath>/columns/:tableId.:ordinalPosition`.
|
|
143
144
|
*
|
|
144
145
|
* postgres-meta identifies columns by `{tableId}.{ordinalPosition}` (the
|
|
145
146
|
* column's 1-based attnum within its table). Use `TablesClient.get(id)` to
|
|
@@ -152,7 +153,8 @@ declare class ColumnsClient {
|
|
|
152
153
|
|
|
153
154
|
declare class SchemasClient {
|
|
154
155
|
private readonly httpClient;
|
|
155
|
-
|
|
156
|
+
private readonly basePath;
|
|
157
|
+
constructor(httpClient: HttpClient, basePath?: string);
|
|
156
158
|
list(): Promise<Schema[]>;
|
|
157
159
|
create(name: string): Promise<Schema>;
|
|
158
160
|
drop(name: string, options?: {
|
|
@@ -162,7 +164,8 @@ declare class SchemasClient {
|
|
|
162
164
|
|
|
163
165
|
declare class TablesClient {
|
|
164
166
|
private readonly httpClient;
|
|
165
|
-
|
|
167
|
+
private readonly basePath;
|
|
168
|
+
constructor(httpClient: HttpClient, basePath?: string);
|
|
166
169
|
list(options?: {
|
|
167
170
|
schema?: string;
|
|
168
171
|
}): Promise<Table[]>;
|
|
@@ -170,7 +173,7 @@ declare class TablesClient {
|
|
|
170
173
|
create(def: CreateTableDef): Promise<Table>;
|
|
171
174
|
/**
|
|
172
175
|
* Apply table-level updates (rename, schema move, RLS toggle, comment, etc).
|
|
173
|
-
* Sends `PATCH
|
|
176
|
+
* Sends `PATCH <basePath>/tables/:id`.
|
|
174
177
|
*
|
|
175
178
|
* Note: this does NOT add or drop columns. For column changes, use
|
|
176
179
|
* `ColumnsClient.create()` / `ColumnsClient.drop()`.
|
|
@@ -181,12 +184,23 @@ declare class TablesClient {
|
|
|
181
184
|
}): Promise<void>;
|
|
182
185
|
}
|
|
183
186
|
|
|
187
|
+
interface AdminClientOptions {
|
|
188
|
+
/**
|
|
189
|
+
* Override the default postgres-meta path prefix (`/v1/meta`). When
|
|
190
|
+
* the gateway in front of postgres-meta requires a project-scoped
|
|
191
|
+
* prefix (e.g. Kong's `/v1/pg-meta/admin/projects/<ref>`), pass it
|
|
192
|
+
* here — every schema / table / column request then rides that
|
|
193
|
+
* prefix unchanged.
|
|
194
|
+
*/
|
|
195
|
+
basePath?: string;
|
|
196
|
+
}
|
|
184
197
|
declare class AdminClient {
|
|
185
198
|
readonly schemas: SchemasClient;
|
|
186
199
|
readonly tables: TablesClient;
|
|
187
200
|
readonly columns: ColumnsClient;
|
|
188
201
|
private readonly httpClient;
|
|
189
|
-
|
|
202
|
+
private readonly basePath;
|
|
203
|
+
constructor(httpClient: HttpClient, options?: AdminClientOptions);
|
|
190
204
|
/**
|
|
191
205
|
* Execute a raw SQL query with full privileges (service role).
|
|
192
206
|
*
|
package/dist/admin.d.ts
CHANGED
|
@@ -129,9 +129,10 @@ interface CreateColumnDef {
|
|
|
129
129
|
*/
|
|
130
130
|
declare class ColumnsClient {
|
|
131
131
|
private readonly httpClient;
|
|
132
|
-
|
|
132
|
+
private readonly basePath;
|
|
133
|
+
constructor(httpClient: HttpClient, basePath?: string);
|
|
133
134
|
/**
|
|
134
|
-
* Create a new column on the given table. Sends `POST
|
|
135
|
+
* Create a new column on the given table. Sends `POST <basePath>/columns`.
|
|
135
136
|
*
|
|
136
137
|
* The column name is validated against the standard SQL identifier regex.
|
|
137
138
|
* The `type` field is forwarded as-is to postgres-meta — callers SHOULD
|
|
@@ -139,7 +140,7 @@ declare class ColumnsClient {
|
|
|
139
140
|
*/
|
|
140
141
|
create(def: CreateColumnDef): Promise<Column>;
|
|
141
142
|
/**
|
|
142
|
-
* Drop a column. Sends `DELETE
|
|
143
|
+
* Drop a column. Sends `DELETE <basePath>/columns/:tableId.:ordinalPosition`.
|
|
143
144
|
*
|
|
144
145
|
* postgres-meta identifies columns by `{tableId}.{ordinalPosition}` (the
|
|
145
146
|
* column's 1-based attnum within its table). Use `TablesClient.get(id)` to
|
|
@@ -152,7 +153,8 @@ declare class ColumnsClient {
|
|
|
152
153
|
|
|
153
154
|
declare class SchemasClient {
|
|
154
155
|
private readonly httpClient;
|
|
155
|
-
|
|
156
|
+
private readonly basePath;
|
|
157
|
+
constructor(httpClient: HttpClient, basePath?: string);
|
|
156
158
|
list(): Promise<Schema[]>;
|
|
157
159
|
create(name: string): Promise<Schema>;
|
|
158
160
|
drop(name: string, options?: {
|
|
@@ -162,7 +164,8 @@ declare class SchemasClient {
|
|
|
162
164
|
|
|
163
165
|
declare class TablesClient {
|
|
164
166
|
private readonly httpClient;
|
|
165
|
-
|
|
167
|
+
private readonly basePath;
|
|
168
|
+
constructor(httpClient: HttpClient, basePath?: string);
|
|
166
169
|
list(options?: {
|
|
167
170
|
schema?: string;
|
|
168
171
|
}): Promise<Table[]>;
|
|
@@ -170,7 +173,7 @@ declare class TablesClient {
|
|
|
170
173
|
create(def: CreateTableDef): Promise<Table>;
|
|
171
174
|
/**
|
|
172
175
|
* Apply table-level updates (rename, schema move, RLS toggle, comment, etc).
|
|
173
|
-
* Sends `PATCH
|
|
176
|
+
* Sends `PATCH <basePath>/tables/:id`.
|
|
174
177
|
*
|
|
175
178
|
* Note: this does NOT add or drop columns. For column changes, use
|
|
176
179
|
* `ColumnsClient.create()` / `ColumnsClient.drop()`.
|
|
@@ -181,12 +184,23 @@ declare class TablesClient {
|
|
|
181
184
|
}): Promise<void>;
|
|
182
185
|
}
|
|
183
186
|
|
|
187
|
+
interface AdminClientOptions {
|
|
188
|
+
/**
|
|
189
|
+
* Override the default postgres-meta path prefix (`/v1/meta`). When
|
|
190
|
+
* the gateway in front of postgres-meta requires a project-scoped
|
|
191
|
+
* prefix (e.g. Kong's `/v1/pg-meta/admin/projects/<ref>`), pass it
|
|
192
|
+
* here — every schema / table / column request then rides that
|
|
193
|
+
* prefix unchanged.
|
|
194
|
+
*/
|
|
195
|
+
basePath?: string;
|
|
196
|
+
}
|
|
184
197
|
declare class AdminClient {
|
|
185
198
|
readonly schemas: SchemasClient;
|
|
186
199
|
readonly tables: TablesClient;
|
|
187
200
|
readonly columns: ColumnsClient;
|
|
188
201
|
private readonly httpClient;
|
|
189
|
-
|
|
202
|
+
private readonly basePath;
|
|
203
|
+
constructor(httpClient: HttpClient, options?: AdminClientOptions);
|
|
190
204
|
/**
|
|
191
205
|
* Execute a raw SQL query with full privileges (service role).
|
|
192
206
|
*
|
package/dist/admin.js
CHANGED
|
@@ -11,11 +11,13 @@ function validateIdentifier(value, label) {
|
|
|
11
11
|
// src/admin-columns.ts
|
|
12
12
|
var ColumnsClient = class {
|
|
13
13
|
httpClient;
|
|
14
|
-
|
|
14
|
+
basePath;
|
|
15
|
+
constructor(httpClient, basePath = "/v1/meta") {
|
|
15
16
|
this.httpClient = httpClient;
|
|
17
|
+
this.basePath = basePath;
|
|
16
18
|
}
|
|
17
19
|
/**
|
|
18
|
-
* Create a new column on the given table. Sends `POST
|
|
20
|
+
* Create a new column on the given table. Sends `POST <basePath>/columns`.
|
|
19
21
|
*
|
|
20
22
|
* The column name is validated against the standard SQL identifier regex.
|
|
21
23
|
* The `type` field is forwarded as-is to postgres-meta — callers SHOULD
|
|
@@ -23,7 +25,7 @@ var ColumnsClient = class {
|
|
|
23
25
|
*/
|
|
24
26
|
async create(def) {
|
|
25
27
|
validateIdentifier(def.name, "column name");
|
|
26
|
-
const response = await this.httpClient.request("POST",
|
|
28
|
+
const response = await this.httpClient.request("POST", `${this.basePath}/columns`, {
|
|
27
29
|
body: def
|
|
28
30
|
});
|
|
29
31
|
if (response.error) {
|
|
@@ -32,7 +34,7 @@ var ColumnsClient = class {
|
|
|
32
34
|
return response.data;
|
|
33
35
|
}
|
|
34
36
|
/**
|
|
35
|
-
* Drop a column. Sends `DELETE
|
|
37
|
+
* Drop a column. Sends `DELETE <basePath>/columns/:tableId.:ordinalPosition`.
|
|
36
38
|
*
|
|
37
39
|
* postgres-meta identifies columns by `{tableId}.{ordinalPosition}` (the
|
|
38
40
|
* column's 1-based attnum within its table). Use `TablesClient.get(id)` to
|
|
@@ -49,7 +51,7 @@ var ColumnsClient = class {
|
|
|
49
51
|
params.set("cascade", "true");
|
|
50
52
|
}
|
|
51
53
|
const queryString = params.toString();
|
|
52
|
-
const path = queryString ?
|
|
54
|
+
const path = queryString ? `${this.basePath}/columns/${columnId}?${queryString}` : `${this.basePath}/columns/${columnId}`;
|
|
53
55
|
const response = await this.httpClient.request("DELETE", path);
|
|
54
56
|
if (response.error) {
|
|
55
57
|
throw response.error;
|
|
@@ -61,11 +63,13 @@ var ColumnsClient = class {
|
|
|
61
63
|
// src/admin-schemas.ts
|
|
62
64
|
var SchemasClient = class {
|
|
63
65
|
httpClient;
|
|
64
|
-
|
|
66
|
+
basePath;
|
|
67
|
+
constructor(httpClient, basePath = "/v1/meta") {
|
|
65
68
|
this.httpClient = httpClient;
|
|
69
|
+
this.basePath = basePath;
|
|
66
70
|
}
|
|
67
71
|
async list() {
|
|
68
|
-
const response = await this.httpClient.request("GET",
|
|
72
|
+
const response = await this.httpClient.request("GET", `${this.basePath}/schemas`);
|
|
69
73
|
if (response.error) {
|
|
70
74
|
throw response.error;
|
|
71
75
|
}
|
|
@@ -73,7 +77,7 @@ var SchemasClient = class {
|
|
|
73
77
|
}
|
|
74
78
|
async create(name) {
|
|
75
79
|
validateIdentifier(name, "schema name");
|
|
76
|
-
const response = await this.httpClient.request("POST",
|
|
80
|
+
const response = await this.httpClient.request("POST", `${this.basePath}/schemas`, {
|
|
77
81
|
body: { name }
|
|
78
82
|
});
|
|
79
83
|
if (response.error) {
|
|
@@ -93,7 +97,7 @@ var SchemasClient = class {
|
|
|
93
97
|
params.set("cascade", "true");
|
|
94
98
|
}
|
|
95
99
|
const queryString = params.toString();
|
|
96
|
-
const path = queryString ?
|
|
100
|
+
const path = queryString ? `${this.basePath}/schemas/${schema.id}?${queryString}` : `${this.basePath}/schemas/${schema.id}`;
|
|
97
101
|
const response = await this.httpClient.request("DELETE", path);
|
|
98
102
|
if (response.error) {
|
|
99
103
|
throw response.error;
|
|
@@ -104,8 +108,10 @@ var SchemasClient = class {
|
|
|
104
108
|
// src/admin-tables.ts
|
|
105
109
|
var TablesClient = class {
|
|
106
110
|
httpClient;
|
|
107
|
-
|
|
111
|
+
basePath;
|
|
112
|
+
constructor(httpClient, basePath = "/v1/meta") {
|
|
108
113
|
this.httpClient = httpClient;
|
|
114
|
+
this.basePath = basePath;
|
|
109
115
|
}
|
|
110
116
|
async list(options) {
|
|
111
117
|
const params = new URLSearchParams();
|
|
@@ -114,7 +120,7 @@ var TablesClient = class {
|
|
|
114
120
|
params.set("included_schemas", options.schema);
|
|
115
121
|
}
|
|
116
122
|
const queryString = params.toString();
|
|
117
|
-
const path = queryString ?
|
|
123
|
+
const path = queryString ? `${this.basePath}/tables?${queryString}` : `${this.basePath}/tables`;
|
|
118
124
|
const response = await this.httpClient.request("GET", path);
|
|
119
125
|
if (response.error) {
|
|
120
126
|
throw response.error;
|
|
@@ -122,7 +128,10 @@ var TablesClient = class {
|
|
|
122
128
|
return response.data ?? [];
|
|
123
129
|
}
|
|
124
130
|
async get(id) {
|
|
125
|
-
const response = await this.httpClient.request(
|
|
131
|
+
const response = await this.httpClient.request(
|
|
132
|
+
"GET",
|
|
133
|
+
`${this.basePath}/tables/${id}`
|
|
134
|
+
);
|
|
126
135
|
if (response.error) {
|
|
127
136
|
throw response.error;
|
|
128
137
|
}
|
|
@@ -133,7 +142,7 @@ var TablesClient = class {
|
|
|
133
142
|
if (def.schema) {
|
|
134
143
|
validateIdentifier(def.schema, "schema name");
|
|
135
144
|
}
|
|
136
|
-
const response = await this.httpClient.request("POST",
|
|
145
|
+
const response = await this.httpClient.request("POST", `${this.basePath}/tables`, {
|
|
137
146
|
body: def
|
|
138
147
|
});
|
|
139
148
|
if (response.error) {
|
|
@@ -143,7 +152,7 @@ var TablesClient = class {
|
|
|
143
152
|
}
|
|
144
153
|
/**
|
|
145
154
|
* Apply table-level updates (rename, schema move, RLS toggle, comment, etc).
|
|
146
|
-
* Sends `PATCH
|
|
155
|
+
* Sends `PATCH <basePath>/tables/:id`.
|
|
147
156
|
*
|
|
148
157
|
* Note: this does NOT add or drop columns. For column changes, use
|
|
149
158
|
* `ColumnsClient.create()` / `ColumnsClient.drop()`.
|
|
@@ -160,9 +169,11 @@ var TablesClient = class {
|
|
|
160
169
|
validateIdentifier(pk.name, "primary key column name");
|
|
161
170
|
}
|
|
162
171
|
}
|
|
163
|
-
const response = await this.httpClient.request(
|
|
164
|
-
|
|
165
|
-
|
|
172
|
+
const response = await this.httpClient.request(
|
|
173
|
+
"PATCH",
|
|
174
|
+
`${this.basePath}/tables/${id}`,
|
|
175
|
+
{ body: patch }
|
|
176
|
+
);
|
|
166
177
|
if (response.error) {
|
|
167
178
|
throw response.error;
|
|
168
179
|
}
|
|
@@ -174,7 +185,7 @@ var TablesClient = class {
|
|
|
174
185
|
params.set("cascade", "true");
|
|
175
186
|
}
|
|
176
187
|
const queryString = params.toString();
|
|
177
|
-
const path = queryString ?
|
|
188
|
+
const path = queryString ? `${this.basePath}/tables/${id}?${queryString}` : `${this.basePath}/tables/${id}`;
|
|
178
189
|
const response = await this.httpClient.request("DELETE", path);
|
|
179
190
|
if (response.error) {
|
|
180
191
|
throw response.error;
|
|
@@ -188,11 +199,13 @@ var AdminClient = class {
|
|
|
188
199
|
tables;
|
|
189
200
|
columns;
|
|
190
201
|
httpClient;
|
|
191
|
-
|
|
202
|
+
basePath;
|
|
203
|
+
constructor(httpClient, options) {
|
|
192
204
|
this.httpClient = httpClient;
|
|
193
|
-
this.
|
|
194
|
-
this.
|
|
195
|
-
this.
|
|
205
|
+
this.basePath = options?.basePath ?? "/v1/meta";
|
|
206
|
+
this.schemas = new SchemasClient(httpClient, this.basePath);
|
|
207
|
+
this.tables = new TablesClient(httpClient, this.basePath);
|
|
208
|
+
this.columns = new ColumnsClient(httpClient, this.basePath);
|
|
196
209
|
}
|
|
197
210
|
/**
|
|
198
211
|
* Execute a raw SQL query with full privileges (service role).
|
|
@@ -219,7 +232,7 @@ var AdminClient = class {
|
|
|
219
232
|
*/
|
|
220
233
|
async query(sql, params, options) {
|
|
221
234
|
const wrappedQuery = options?.readOnly ? `BEGIN; SET TRANSACTION READ ONLY; ${stripTrailingSemicolon(sql)}; COMMIT;` : sql;
|
|
222
|
-
const response = await this.httpClient.request("POST",
|
|
235
|
+
const response = await this.httpClient.request("POST", `${this.basePath}/query`, {
|
|
223
236
|
body: { query: wrappedQuery, params }
|
|
224
237
|
});
|
|
225
238
|
if (response.error) {
|
package/dist/admin.js.map
CHANGED
|
@@ -1 +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 * Pass `{ readOnly: true }` to wrap the query in\n * `BEGIN; SET TRANSACTION READ ONLY; <query>; COMMIT;` so Postgres\n * rejects any write/DDL statement at the server — safe mode for\n * Studio's SQL editor and other consumer-facing query surfaces.\n *\n * @example\n * ```ts\n * // GOOD — parameterized\n * await admin.query('SELECT * FROM users WHERE id = $1', [userId]);\n *\n * // Read-only — Postgres enforces the denial (errcode 25006).\n * await admin.query('SELECT count(*) FROM users', [], { readOnly: true });\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>>(\n sql: string,\n params?: unknown[],\n options?: QueryOptions,\n ): Promise<T[]> {\n const wrappedQuery = options?.readOnly\n ? `BEGIN; SET TRANSACTION READ ONLY; ${stripTrailingSemicolon(sql)}; COMMIT;`\n : sql;\n const response = await this.httpClient.request<T[]>('POST', '/v1/meta/query', {\n body: { query: wrappedQuery, params },\n });\n if (response.error) {\n throw response.error;\n }\n return response.data ?? [];\n }\n}\n\nexport interface QueryOptions {\n /**\n * Run the query in a read-only Postgres transaction. Any INSERT /\n * UPDATE / DELETE / DDL statement inside the wrapped block is\n * rejected by the server with SQLSTATE 25006 — the SDK isn't the\n * guard, Postgres is. Useful for UI surfaces (SQL editor, data\n * explorer) that must not mutate.\n */\n readOnly?: boolean;\n}\n\n/** Trim the final `;` so our BEGIN/COMMIT wrapper doesn't close twice. */\nfunction stripTrailingSemicolon(sql: string): string {\n return sql.replace(/;\\s*$/, '');\n}\n"],"mappings":"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","names":[]}
|
|
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 private readonly basePath: string;\n\n constructor(httpClient: HttpClient, basePath = '/v1/meta') {\n this.httpClient = httpClient;\n this.basePath = basePath;\n }\n\n /**\n * Create a new column on the given table. Sends `POST <basePath>/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', `${this.basePath}/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 <basePath>/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 ? `${this.basePath}/columns/${columnId}?${queryString}`\n : `${this.basePath}/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 private readonly basePath: string;\n\n constructor(httpClient: HttpClient, basePath = '/v1/meta') {\n this.httpClient = httpClient;\n this.basePath = basePath;\n }\n\n async list(): Promise<Schema[]> {\n const response = await this.httpClient.request<Schema[]>('GET', `${this.basePath}/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', `${this.basePath}/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 ? `${this.basePath}/schemas/${schema.id}?${queryString}`\n : `${this.basePath}/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 private readonly basePath: string;\n\n constructor(httpClient: HttpClient, basePath = '/v1/meta') {\n this.httpClient = httpClient;\n this.basePath = basePath;\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\n ? `${this.basePath}/tables?${queryString}`\n : `${this.basePath}/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>(\n 'GET',\n `${this.basePath}/tables/${id}`,\n );\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', `${this.basePath}/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 <basePath>/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>(\n 'PATCH',\n `${this.basePath}/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 ? `${this.basePath}/tables/${id}?${queryString}`\n : `${this.basePath}/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 interface AdminClientOptions {\n /**\n * Override the default postgres-meta path prefix (`/v1/meta`). When\n * the gateway in front of postgres-meta requires a project-scoped\n * prefix (e.g. Kong's `/v1/pg-meta/admin/projects/<ref>`), pass it\n * here — every schema / table / column request then rides that\n * prefix unchanged.\n */\n basePath?: string;\n}\n\nexport class AdminClient {\n readonly schemas: SchemasClient;\n readonly tables: TablesClient;\n readonly columns: ColumnsClient;\n\n private readonly httpClient: HttpClient;\n private readonly basePath: string;\n\n constructor(httpClient: HttpClient, options?: AdminClientOptions) {\n this.httpClient = httpClient;\n this.basePath = options?.basePath ?? '/v1/meta';\n this.schemas = new SchemasClient(httpClient, this.basePath);\n this.tables = new TablesClient(httpClient, this.basePath);\n this.columns = new ColumnsClient(httpClient, this.basePath);\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 * Pass `{ readOnly: true }` to wrap the query in\n * `BEGIN; SET TRANSACTION READ ONLY; <query>; COMMIT;` so Postgres\n * rejects any write/DDL statement at the server — safe mode for\n * Studio's SQL editor and other consumer-facing query surfaces.\n *\n * @example\n * ```ts\n * // GOOD — parameterized\n * await admin.query('SELECT * FROM users WHERE id = $1', [userId]);\n *\n * // Read-only — Postgres enforces the denial (errcode 25006).\n * await admin.query('SELECT count(*) FROM users', [], { readOnly: true });\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>>(\n sql: string,\n params?: unknown[],\n options?: QueryOptions,\n ): Promise<T[]> {\n const wrappedQuery = options?.readOnly\n ? `BEGIN; SET TRANSACTION READ ONLY; ${stripTrailingSemicolon(sql)}; COMMIT;`\n : sql;\n const response = await this.httpClient.request<T[]>('POST', `${this.basePath}/query`, {\n body: { query: wrappedQuery, params },\n });\n if (response.error) {\n throw response.error;\n }\n return response.data ?? [];\n }\n}\n\nexport interface QueryOptions {\n /**\n * Run the query in a read-only Postgres transaction. Any INSERT /\n * UPDATE / DELETE / DDL statement inside the wrapped block is\n * rejected by the server with SQLSTATE 25006 — the SDK isn't the\n * guard, Postgres is. Useful for UI surfaces (SQL editor, data\n * explorer) that must not mutate.\n */\n readOnly?: boolean;\n}\n\n/** Trim the final `;` so our BEGIN/COMMIT wrapper doesn't close twice. */\nfunction stripTrailingSemicolon(sql: string): string {\n return sql.replace(/;\\s*$/, '');\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@palbase/db",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"dist"
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@palbase/core": "0.
|
|
35
|
+
"@palbase/core": "^0.7.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@biomejs/biome": "^2.0.0",
|