@polardbx/pxbaas-client 0.0.0-stage → 0.1.0-beta.2

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.
Files changed (42) hide show
  1. package/README.md +51 -2
  2. package/dist/drivers/apps.driver.d.ts +121 -0
  3. package/dist/drivers/apps.driver.d.ts.map +1 -0
  4. package/dist/drivers/apps.driver.js +90 -0
  5. package/dist/drivers/apps.driver.js.map +1 -0
  6. package/dist/drivers/auth-admin.driver.d.ts +78 -0
  7. package/dist/drivers/auth-admin.driver.d.ts.map +1 -0
  8. package/dist/drivers/auth-admin.driver.js +145 -0
  9. package/dist/drivers/auth-admin.driver.js.map +1 -0
  10. package/dist/drivers/functions.driver.d.ts +75 -0
  11. package/dist/drivers/functions.driver.d.ts.map +1 -0
  12. package/dist/drivers/functions.driver.js +173 -0
  13. package/dist/drivers/functions.driver.js.map +1 -0
  14. package/dist/drivers/meta.driver.d.ts +77 -0
  15. package/dist/drivers/meta.driver.d.ts.map +1 -0
  16. package/dist/drivers/meta.driver.js +191 -0
  17. package/dist/drivers/meta.driver.js.map +1 -0
  18. package/dist/drivers/share-links.driver.d.ts +42 -0
  19. package/dist/drivers/share-links.driver.d.ts.map +1 -0
  20. package/dist/drivers/share-links.driver.js +53 -0
  21. package/dist/drivers/share-links.driver.js.map +1 -0
  22. package/dist/drivers/storage.driver.d.ts +72 -0
  23. package/dist/drivers/storage.driver.d.ts.map +1 -0
  24. package/dist/drivers/storage.driver.js +171 -0
  25. package/dist/drivers/storage.driver.js.map +1 -0
  26. package/dist/drivers/tenant-ddl.driver.d.ts +106 -0
  27. package/dist/drivers/tenant-ddl.driver.d.ts.map +1 -0
  28. package/dist/drivers/tenant-ddl.driver.js +131 -0
  29. package/dist/drivers/tenant-ddl.driver.js.map +1 -0
  30. package/dist/drivers/zero.driver.d.ts +78 -0
  31. package/dist/drivers/zero.driver.d.ts.map +1 -0
  32. package/dist/drivers/zero.driver.js +117 -0
  33. package/dist/drivers/zero.driver.js.map +1 -0
  34. package/dist/index.d.ts +25 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +34 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/types.d.ts +26 -0
  39. package/dist/types.d.ts.map +1 -0
  40. package/dist/types.js +5 -0
  41. package/dist/types.js.map +1 -0
  42. package/package.json +57 -3
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Storage Driver - Supabase Storage API client
3
+ *
4
+ * Endpoints:
5
+ * - GET /storage/v1/bucket - List buckets
6
+ * - GET /storage/v1/object/list/{bucket} - List objects
7
+ * - POST /storage/v1/object/{bucket}/{path} - Upload object
8
+ * - GET /storage/v1/object/{bucket}/{path} - Download object
9
+ * - DELETE /storage/v1/object/{bucket}/{path} - Delete object
10
+ * - POST /storage/v1/object/move - Move object
11
+ * - POST /storage/v1/object/copy - Copy object
12
+ */
13
+ import axios from 'axios';
14
+ import { writeFile, stat } from 'node:fs/promises';
15
+ import { basename } from 'node:path';
16
+ import { createReadStream } from 'node:fs';
17
+ export class StorageDriver {
18
+ client;
19
+ serviceRoleKey;
20
+ tenantId;
21
+ constructor(options) {
22
+ this.serviceRoleKey = options.serviceRoleKey;
23
+ this.tenantId = options.tenantId;
24
+ this.client = axios.create({
25
+ baseURL: `${options.endpoint}/storage/v1`,
26
+ timeout: options.timeout ?? 60000,
27
+ headers: {
28
+ 'Authorization': `Bearer ${this.serviceRoleKey}`,
29
+ 'apikey': this.serviceRoleKey,
30
+ },
31
+ });
32
+ }
33
+ getHeaders() {
34
+ const headers = {
35
+ 'Authorization': `Bearer ${this.serviceRoleKey}`,
36
+ 'apikey': this.serviceRoleKey,
37
+ };
38
+ if (this.tenantId) {
39
+ headers['X-Tenant-Id'] = this.tenantId;
40
+ }
41
+ return headers;
42
+ }
43
+ handleError(error) {
44
+ if (axios.isAxiosError(error)) {
45
+ const axiosError = error;
46
+ const status = axiosError.response?.status;
47
+ const data = axiosError.response?.data;
48
+ let message = axiosError.message;
49
+ if (status === 401) {
50
+ message = 'Authentication failed. Please check your service_role key and run "pxbaas login" again.';
51
+ }
52
+ else if (status === 403) {
53
+ message = 'Permission denied. Storage operations require service_role key.';
54
+ }
55
+ else if (status === 404) {
56
+ message = 'Object or bucket not found. Please check the path and try again.';
57
+ }
58
+ else if (data?.error) {
59
+ message = String(data.error);
60
+ }
61
+ else if (data?.message) {
62
+ message = String(data.message);
63
+ }
64
+ throw new Error(`Storage API error (${status ?? 'unknown'}): ${message}`);
65
+ }
66
+ throw error;
67
+ }
68
+ /**
69
+ * List all buckets
70
+ */
71
+ async listBuckets() {
72
+ try {
73
+ const response = await this.client.get('/bucket', { headers: this.getHeaders() });
74
+ return response.data ?? [];
75
+ }
76
+ catch (error) {
77
+ this.handleError(error);
78
+ }
79
+ }
80
+ /**
81
+ * List objects in a bucket
82
+ */
83
+ async listObjects(bucket, prefix) {
84
+ try {
85
+ const response = await this.client.post(`/object/list/${bucket}`, { prefix, limit: 1000 }, { headers: this.getHeaders() });
86
+ return response.data ?? [];
87
+ }
88
+ catch (error) {
89
+ this.handleError(error);
90
+ }
91
+ }
92
+ /**
93
+ * Upload a file to storage
94
+ */
95
+ async upload(bucket, remotePath, localPath) {
96
+ try {
97
+ const fileStream = createReadStream(localPath);
98
+ const stats = await stat(localPath);
99
+ const filename = basename(localPath);
100
+ await this.client.post(`/object/${bucket}/${remotePath}`, fileStream, {
101
+ headers: {
102
+ ...this.getHeaders(),
103
+ 'Content-Type': 'application/octet-stream',
104
+ 'Content-Length': stats.size,
105
+ },
106
+ });
107
+ }
108
+ catch (error) {
109
+ this.handleError(error);
110
+ }
111
+ }
112
+ /**
113
+ * Download a file from storage
114
+ */
115
+ async download(bucket, remotePath, localPath) {
116
+ try {
117
+ const response = await this.client.get(`/object/${bucket}/${remotePath}`, {
118
+ headers: this.getHeaders(),
119
+ responseType: 'arraybuffer',
120
+ });
121
+ await writeFile(localPath, response.data);
122
+ }
123
+ catch (error) {
124
+ this.handleError(error);
125
+ }
126
+ }
127
+ /**
128
+ * Delete an object from storage
129
+ */
130
+ async delete(bucket, remotePath) {
131
+ try {
132
+ await this.client.delete(`/object/${bucket}/${remotePath}`, { headers: this.getHeaders() });
133
+ }
134
+ catch (error) {
135
+ this.handleError(error);
136
+ }
137
+ }
138
+ /**
139
+ * Move/rename an object
140
+ */
141
+ async move(sourceBucket, sourcePath, destBucket, destPath) {
142
+ try {
143
+ await this.client.post('/object/move', {
144
+ bucketId: sourceBucket,
145
+ sourceKey: sourcePath,
146
+ destinationBucket: destBucket,
147
+ destinationKey: destPath,
148
+ }, { headers: this.getHeaders() });
149
+ }
150
+ catch (error) {
151
+ this.handleError(error);
152
+ }
153
+ }
154
+ /**
155
+ * Copy an object
156
+ */
157
+ async copy(sourceBucket, sourcePath, destBucket, destPath) {
158
+ try {
159
+ await this.client.post('/object/copy', {
160
+ bucketId: sourceBucket,
161
+ sourceKey: sourcePath,
162
+ destinationBucket: destBucket,
163
+ destinationKey: destPath,
164
+ }, { headers: this.getHeaders() });
165
+ }
166
+ catch (error) {
167
+ this.handleError(error);
168
+ }
169
+ }
170
+ }
171
+ //# sourceMappingURL=storage.driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.driver.js","sourceRoot":"","sources":["../../src/drivers/storage.driver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Tenant DDL Driver — 多租户「普通租户」自助 DDL / RLS 注册客户端。
3
+ *
4
+ * 对接 Studio BFF 的租户自助端点(Aone #87795993):
5
+ * - POST /api/tenant-ddl/query 在租户自有库执行 DDL/DML
6
+ * - POST /api/tenant-ddl/rls/tables 开/关表级 RLS
7
+ * - GET /api/tenant-ddl/rls/tables 列出本租户已注册 RLS 的表
8
+ * - DELETE /api/tenant-ddl/rls/tables 移除一张表的 RLS 注册(开关行)
9
+ * - POST /api/tenant-ddl/rls/policies 增/改一条 RLS 策略
10
+ * - GET /api/tenant-ddl/rls/policies 列出本租户策略(可按 table 过滤)
11
+ * - DELETE /api/tenant-ddl/rls/policies 删除一条策略
12
+ *
13
+ * 认证模型(务必遵守,见方案文档「三、认证与信任链」):
14
+ * 只发送 `Authorization: Bearer <per-tenant service_role key>`。**绝不**自行拼
15
+ * `X-Tenant-Id`——Kong 入口会剥离客户端伪造的该头、验签 per-tenant JWT 后注入
16
+ * 可信 X-Tenant-Id,并把下游 Authorization 替换为全局 key。租户身份完全由网关推导。
17
+ *
18
+ * 成功响应统一为 `{ data: ... }`,失败为 `{ code, message }`;本 driver 解包 data、
19
+ * 并把失败规范化为 TenantDdlError(携带 code/message/status)。
20
+ */
21
+ /** RLS 命令行枚举(与服务端 resolver 白名单一致)。 */
22
+ export type RlsCommand = "ALL" | "SELECT" | "INSERT" | "UPDATE" | "DELETE";
23
+ export interface TenantDdlDriverOptions {
24
+ endpoint: string;
25
+ /** per-tenant service_role key(带 tenant_id claim)。不可用全局 key。 */
26
+ serviceRoleKey: string;
27
+ timeout?: number;
28
+ }
29
+ export interface RlsTableInput {
30
+ table_name: string;
31
+ /** 默认 true(启用 RLS)。 */
32
+ rls_enabled?: boolean;
33
+ /** 默认 false。 */
34
+ rls_forced?: boolean;
35
+ }
36
+ export interface RlsPolicyInput {
37
+ table_name: string;
38
+ policy_name: string;
39
+ /** `*` 或 `[a-zA-Z0-9_]`;缺省服务端按 `*` 处理。 */
40
+ role_name?: string;
41
+ command?: RlsCommand | string;
42
+ using_expr?: string | null;
43
+ check_expr?: string | null;
44
+ permissive?: boolean;
45
+ }
46
+ /** GET /rls/tables 的一行(rls_* 以 tinyint 0/1 返回)。 */
47
+ export interface RlsTableRow {
48
+ table_schema: string;
49
+ table_name: string;
50
+ rls_enabled: number | boolean;
51
+ rls_forced: number | boolean;
52
+ }
53
+ /** GET /rls/policies 的一行。 */
54
+ export interface RlsPolicyRow {
55
+ table_schema: string;
56
+ table_name: string;
57
+ policy_name: string;
58
+ role_name: string;
59
+ command: string;
60
+ using_expr: string | null;
61
+ check_expr: string | null;
62
+ permissive: number | boolean;
63
+ }
64
+ /** 租户自助 DDL/RLS 调用的统一错误(携带机器码 + HTTP 状态)。 */
65
+ export declare class TenantDdlError extends Error {
66
+ readonly code: string;
67
+ readonly status?: number;
68
+ constructor(code: string, message: string, status?: number);
69
+ }
70
+ export declare class TenantDdlDriver {
71
+ private readonly client;
72
+ constructor(options: TenantDdlDriverOptions);
73
+ /**
74
+ * Execute a DDL/DML statement on the tenant's OWN database. The tenant MySQL
75
+ * account (GRANT ALL ON own_db.* only) is the hard isolation boundary; the
76
+ * server additionally pre-checks and rejects privilege/instance-level statements.
77
+ * Returns the raw pg-meta rows (`data`), or `[]` for statements with no result set.
78
+ */
79
+ query(sql: string): Promise<unknown[]>;
80
+ /** Enable/force RLS on one of the tenant's tables (central registry, schema server-bound). */
81
+ upsertRlsTable(input: RlsTableInput): Promise<void>;
82
+ /** List the tenant's own RLS-registered tables. */
83
+ listRlsTables(): Promise<RlsTableRow[]>;
84
+ /**
85
+ * Remove a table's RLS registration (the central enable/force toggle row),
86
+ * symmetric to {@link deleteRlsPolicy}. The server binds table_schema to the
87
+ * tenant db and does NOT require the table to still exist, so this is safe to
88
+ * call after a DROP (idempotent cleanup).
89
+ */
90
+ deleteRlsTable(tableName: string): Promise<void>;
91
+ /** Insert/update one RLS policy for the tenant (schema server-bound). */
92
+ upsertRlsPolicy(input: RlsPolicyInput): Promise<void>;
93
+ /** List the tenant's own policies, optionally filtered by table name. */
94
+ listRlsPolicies(tableName?: string): Promise<RlsPolicyRow[]>;
95
+ /** Delete one of the tenant's policies (scoped by table + policy name). */
96
+ deleteRlsPolicy(tableName: string, policyName: string): Promise<void>;
97
+ private request;
98
+ }
99
+ /**
100
+ * Map any failure to a TenantDdlError. Server errors carry `{ code, message }`
101
+ * (e.g. PERMISSION_DENIED / GLOBAL_KEY_NOT_ALLOWED / ROLE_NOT_ALLOWED /
102
+ * TENANT_NOT_ACTIVE / STATEMENT_NOT_ALLOWED); fall back to an HTTP-status code
103
+ * or a network code when the body is absent.
104
+ */
105
+ export declare function normalizeTenantDdlError(error: unknown): TenantDdlError;
106
+ //# sourceMappingURL=tenant-ddl.driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-ddl.driver.d.ts","sourceRoot":"","sources":["../../src/drivers/tenant-ddl.driver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,sCAAsC;AACtC,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE3E,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,uBAAuB;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,gBAAgB;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,mDAAmD;AACnD,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;CAC9B;AAED,6BAA6B;AAC7B,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;CAC9B;AAED,6CAA6C;AAC7C,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAEb,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;CAM3D;AAQD,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;gBAE3B,OAAO,EAAE,sBAAsB;IAW3C;;;;;OAKG;IACG,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAO5C,8FAA8F;IACxF,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAMzD,mDAAmD;IAC7C,aAAa,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAO7C;;;;;OAKG;IACG,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQtD,yEAAyE;IACnE,eAAe,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAM3D,yEAAyE;IACnE,eAAe,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IASlE,2EAA2E;IACrE,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAQ7D,OAAO;CAUtB;AAQD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAwBtE"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Tenant DDL Driver — 多租户「普通租户」自助 DDL / RLS 注册客户端。
3
+ *
4
+ * 对接 Studio BFF 的租户自助端点(Aone #87795993):
5
+ * - POST /api/tenant-ddl/query 在租户自有库执行 DDL/DML
6
+ * - POST /api/tenant-ddl/rls/tables 开/关表级 RLS
7
+ * - GET /api/tenant-ddl/rls/tables 列出本租户已注册 RLS 的表
8
+ * - DELETE /api/tenant-ddl/rls/tables 移除一张表的 RLS 注册(开关行)
9
+ * - POST /api/tenant-ddl/rls/policies 增/改一条 RLS 策略
10
+ * - GET /api/tenant-ddl/rls/policies 列出本租户策略(可按 table 过滤)
11
+ * - DELETE /api/tenant-ddl/rls/policies 删除一条策略
12
+ *
13
+ * 认证模型(务必遵守,见方案文档「三、认证与信任链」):
14
+ * 只发送 `Authorization: Bearer <per-tenant service_role key>`。**绝不**自行拼
15
+ * `X-Tenant-Id`——Kong 入口会剥离客户端伪造的该头、验签 per-tenant JWT 后注入
16
+ * 可信 X-Tenant-Id,并把下游 Authorization 替换为全局 key。租户身份完全由网关推导。
17
+ *
18
+ * 成功响应统一为 `{ data: ... }`,失败为 `{ code, message }`;本 driver 解包 data、
19
+ * 并把失败规范化为 TenantDdlError(携带 code/message/status)。
20
+ */
21
+ import axios from "axios";
22
+ /** 租户自助 DDL/RLS 调用的统一错误(携带机器码 + HTTP 状态)。 */
23
+ export class TenantDdlError extends Error {
24
+ code;
25
+ status;
26
+ constructor(code, message, status) {
27
+ super(message);
28
+ this.name = "TenantDdlError";
29
+ this.code = code;
30
+ this.status = status;
31
+ }
32
+ }
33
+ export class TenantDdlDriver {
34
+ client;
35
+ constructor(options) {
36
+ const endpoint = options.endpoint.replace(/\/$/, "");
37
+ this.client = axios.create({
38
+ baseURL: `${endpoint}/api/tenant-ddl`,
39
+ timeout: options.timeout ?? 60_000,
40
+ // Only the per-tenant service_role bearer. Never send X-Tenant-Id here —
41
+ // Kong strips any client-supplied value and injects the trusted one.
42
+ headers: { Authorization: `Bearer ${options.serviceRoleKey}` },
43
+ });
44
+ }
45
+ /**
46
+ * Execute a DDL/DML statement on the tenant's OWN database. The tenant MySQL
47
+ * account (GRANT ALL ON own_db.* only) is the hard isolation boundary; the
48
+ * server additionally pre-checks and rejects privilege/instance-level statements.
49
+ * Returns the raw pg-meta rows (`data`), or `[]` for statements with no result set.
50
+ */
51
+ async query(sql) {
52
+ const data = await this.request(() => this.client.post("/query", { sql }));
53
+ return data ?? [];
54
+ }
55
+ /** Enable/force RLS on one of the tenant's tables (central registry, schema server-bound). */
56
+ async upsertRlsTable(input) {
57
+ await this.request(() => this.client.post("/rls/tables", compact(input)));
58
+ }
59
+ /** List the tenant's own RLS-registered tables. */
60
+ async listRlsTables() {
61
+ const data = await this.request(() => this.client.get("/rls/tables"));
62
+ return data ?? [];
63
+ }
64
+ /**
65
+ * Remove a table's RLS registration (the central enable/force toggle row),
66
+ * symmetric to {@link deleteRlsPolicy}. The server binds table_schema to the
67
+ * tenant db and does NOT require the table to still exist, so this is safe to
68
+ * call after a DROP (idempotent cleanup).
69
+ */
70
+ async deleteRlsTable(tableName) {
71
+ await this.request(() => this.client.delete("/rls/tables", {
72
+ params: { table_name: tableName },
73
+ }));
74
+ }
75
+ /** Insert/update one RLS policy for the tenant (schema server-bound). */
76
+ async upsertRlsPolicy(input) {
77
+ await this.request(() => this.client.post("/rls/policies", compact(input)));
78
+ }
79
+ /** List the tenant's own policies, optionally filtered by table name. */
80
+ async listRlsPolicies(tableName) {
81
+ const data = await this.request(() => this.client.get("/rls/policies", {
82
+ params: tableName ? { table_name: tableName } : undefined,
83
+ }));
84
+ return data ?? [];
85
+ }
86
+ /** Delete one of the tenant's policies (scoped by table + policy name). */
87
+ async deleteRlsPolicy(tableName, policyName) {
88
+ await this.request(() => this.client.delete("/rls/policies", {
89
+ params: { table_name: tableName, policy_name: policyName },
90
+ }));
91
+ }
92
+ async request(operation) {
93
+ try {
94
+ const response = await operation();
95
+ return response.data?.data;
96
+ }
97
+ catch (error) {
98
+ throw normalizeTenantDdlError(error);
99
+ }
100
+ }
101
+ }
102
+ function compact(value) {
103
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
104
+ }
105
+ /**
106
+ * Map any failure to a TenantDdlError. Server errors carry `{ code, message }`
107
+ * (e.g. PERMISSION_DENIED / GLOBAL_KEY_NOT_ALLOWED / ROLE_NOT_ALLOWED /
108
+ * TENANT_NOT_ACTIVE / STATEMENT_NOT_ALLOWED); fall back to an HTTP-status code
109
+ * or a network code when the body is absent.
110
+ */
111
+ export function normalizeTenantDdlError(error) {
112
+ if (!axios.isAxiosError(error)) {
113
+ const message = error instanceof Error ? error.message : String(error);
114
+ return new TenantDdlError("TENANT_DDL_UNKNOWN_ERROR", message);
115
+ }
116
+ const axiosError = error;
117
+ const status = axiosError.response?.status;
118
+ const body = axiosError.response?.data;
119
+ const code = typeof body?.code === "string"
120
+ ? body.code
121
+ : status
122
+ ? `TENANT_DDL_HTTP_${status}`
123
+ : "TENANT_DDL_NETWORK_ERROR";
124
+ const message = typeof body?.message === "string"
125
+ ? body.message
126
+ : status
127
+ ? `Tenant DDL request failed (HTTP ${status})`
128
+ : axiosError.message || "Tenant DDL network request failed";
129
+ return new TenantDdlError(code, message, status);
130
+ }
131
+ //# sourceMappingURL=tenant-ddl.driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-ddl.driver.js","sourceRoot":"","sources":["../../src/drivers/tenant-ddl.driver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Zero Driver — PolarDB-X Zero PXBaas provisioning client.
3
+ *
4
+ * Wraps the unauthenticated Zero API that claims a disposable, Supabase-compatible
5
+ * BaaS instance from a pre-warmed pool (15-day TTL, no renewal).
6
+ *
7
+ * Contract source: https://zero.polardbx.com/BAAS-SKILL.md (polardbx-zero-pxbaas v0.1.0)
8
+ *
9
+ * Endpoints:
10
+ * - POST /api/v1/supabase/instances claim (body: { whitelistIp? })
11
+ * - GET /api/v1/supabase/instances list (needs pxz_token cookie)
12
+ * - DELETE /api/v1/supabase/instances/{id} release (needs pxz_token cookie)
13
+ *
14
+ * Ownership: the claim response returns a `token` (also set as the `pxz_token`
15
+ * cookie). list/release must send it back via `Cookie: pxz_token=<token>`.
16
+ */
17
+ import { AxiosError } from 'axios';
18
+ export interface ZeroInstance {
19
+ id: string;
20
+ status: string;
21
+ region?: string;
22
+ zoneId?: string;
23
+ aliyunInstanceId?: string;
24
+ /** host:8000 WITHOUT scheme, e.g. "pxsp-xxxhttp.polardbxaisupabase.rds.aliyuncs.com:8000" */
25
+ publicEndpoint: string;
26
+ dashboardUrl?: string;
27
+ /** Canonical App origin returned by the platform; clients must not derive it. */
28
+ appEndpoint?: string;
29
+ appDomain?: {
30
+ hostname: string;
31
+ status: string;
32
+ tlsStatus: string;
33
+ };
34
+ apiKey: string;
35
+ serviceRoleKey: string;
36
+ dbPassword?: string;
37
+ dashboardPassword?: string;
38
+ whitelistIp?: string;
39
+ assignedAt?: string;
40
+ expiresAt?: string;
41
+ createdAt?: string;
42
+ }
43
+ export interface ZeroClaimResponse {
44
+ instance: ZeroInstance;
45
+ token: string;
46
+ }
47
+ export interface ZeroDriverOptions {
48
+ /** Zero API base URL. Defaults to https://zero.polardbx.com (override via BAAS_ZERO_ENDPOINT). */
49
+ baseUrl?: string;
50
+ timeout?: number;
51
+ }
52
+ /** Error codes returned by Zero (`{ error: { code, message } }`). */
53
+ export type ZeroErrorCode = 'INVALID_WHITELIST_IP' | 'INSTANCE_NOT_FOUND' | 'RATE_LIMIT_EXCEEDED' | 'SUPABASE_LIMIT_REACHED' | 'SUPABASE_POOL_EXHAUSTED' | 'SUPABASE_DISABLED' | 'INTERNAL_ERROR' | 'NETWORK_ERROR' | 'UNKNOWN';
54
+ export declare class ZeroError extends Error {
55
+ code: ZeroErrorCode;
56
+ status?: number;
57
+ constructor(code: ZeroErrorCode, message: string, status?: number);
58
+ }
59
+ export declare class ZeroDriver {
60
+ private client;
61
+ constructor(options?: ZeroDriverOptions);
62
+ /** Claim a new instance. Unauthenticated. Optionally whitelist extra IPs. */
63
+ claim(whitelistIp?: string): Promise<ZeroClaimResponse>;
64
+ /** List the caller's non-released instances. Requires the ownership token. */
65
+ list(token: string): Promise<ZeroInstance[]>;
66
+ /** Release (destroy) an instance. Requires the ownership token. */
67
+ release(id: string, token: string): Promise<void>;
68
+ /**
69
+ * Normalize the publicEndpoint (host:8000, no scheme) into a base URL.
70
+ * Prefers dashboardUrl when it already carries a scheme.
71
+ */
72
+ static toEndpoint(instance: ZeroInstance, scheme?: string): string;
73
+ private toError;
74
+ private codeFromStatus;
75
+ }
76
+ /** Wrap a network-level axios failure into a ZeroError. */
77
+ export declare function isZeroNetworkError(e: unknown): e is AxiosError;
78
+ //# sourceMappingURL=zero.driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zero.driver.d.ts","sourceRoot":"","sources":["../../src/drivers/zero.driver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAc,EAAiB,UAAU,EAAE,MAAM,OAAO,CAAC;AAEzD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6FAA6F;IAC7F,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE;QACV,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,kGAAkG;IAClG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qEAAqE;AACrE,MAAM,MAAM,aAAa,GACrB,sBAAsB,GACtB,oBAAoB,GACpB,qBAAqB,GACrB,wBAAwB,GACxB,yBAAyB,GACzB,mBAAmB,GACnB,gBAAgB,GAChB,eAAe,GACf,SAAS,CAAC;AAEd,qBAAa,SAAU,SAAQ,KAAK;IAClC,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;gBACJ,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;CAMlE;AAID,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAgB;gBAElB,OAAO,GAAE,iBAAsB;IAa3C,6EAA6E;IACvE,KAAK,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAe7D,8EAA8E;IACxE,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAalD,mEAAmE;IAC7D,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASvD;;;OAGG;IACH,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,GAAG,MAAM;IAQlE,OAAO,CAAC,OAAO;IAOf,OAAO,CAAC,cAAc;CAUvB;AAED,2DAA2D;AAC3D,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,UAAU,CAE9D"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Zero Driver — PolarDB-X Zero PXBaas provisioning client.
3
+ *
4
+ * Wraps the unauthenticated Zero API that claims a disposable, Supabase-compatible
5
+ * BaaS instance from a pre-warmed pool (15-day TTL, no renewal).
6
+ *
7
+ * Contract source: https://zero.polardbx.com/BAAS-SKILL.md (polardbx-zero-pxbaas v0.1.0)
8
+ *
9
+ * Endpoints:
10
+ * - POST /api/v1/supabase/instances claim (body: { whitelistIp? })
11
+ * - GET /api/v1/supabase/instances list (needs pxz_token cookie)
12
+ * - DELETE /api/v1/supabase/instances/{id} release (needs pxz_token cookie)
13
+ *
14
+ * Ownership: the claim response returns a `token` (also set as the `pxz_token`
15
+ * cookie). list/release must send it back via `Cookie: pxz_token=<token>`.
16
+ */
17
+ import axios from 'axios';
18
+ export class ZeroError extends Error {
19
+ code;
20
+ status;
21
+ constructor(code, message, status) {
22
+ super(message);
23
+ this.name = 'ZeroError';
24
+ this.code = code;
25
+ this.status = status;
26
+ }
27
+ }
28
+ const DEFAULT_BASE_URL = 'https://zero.polardbx.com';
29
+ export class ZeroDriver {
30
+ client;
31
+ constructor(options = {}) {
32
+ const baseUrl = options.baseUrl ?? process.env.BAAS_ZERO_ENDPOINT ?? DEFAULT_BASE_URL;
33
+ this.client = axios.create({
34
+ baseURL: baseUrl.replace(/\/$/, ''),
35
+ timeout: options.timeout ?? 30000,
36
+ // NOTE: do NOT set a default Content-Type. axios adds application/json
37
+ // automatically for the claim() POST object body, but a DELETE/GET carrying
38
+ // Content-Type: application/json with no body makes Zero return HTTP 400.
39
+ // Do not throw on non-2xx; we map errors ourselves.
40
+ validateStatus: () => true,
41
+ });
42
+ }
43
+ /** Claim a new instance. Unauthenticated. Optionally whitelist extra IPs. */
44
+ async claim(whitelistIp) {
45
+ const body = {};
46
+ if (whitelistIp)
47
+ body.whitelistIp = whitelistIp;
48
+ const res = await this.client.post('/api/v1/supabase/instances', body);
49
+ if (res.status !== 200 && res.status !== 201) {
50
+ throw this.toError(res.status, res.data);
51
+ }
52
+ const data = res.data;
53
+ if (!data?.instance?.id || !data?.token) {
54
+ throw new ZeroError('UNKNOWN', 'Malformed claim response: missing instance.id or token', res.status);
55
+ }
56
+ return data;
57
+ }
58
+ /** List the caller's non-released instances. Requires the ownership token. */
59
+ async list(token) {
60
+ const res = await this.client.get('/api/v1/supabase/instances', {
61
+ headers: { Cookie: `pxz_token=${token}` },
62
+ });
63
+ if (res.status !== 200) {
64
+ throw this.toError(res.status, res.data);
65
+ }
66
+ const data = res.data;
67
+ if (Array.isArray(data))
68
+ return data;
69
+ const obj = data;
70
+ return obj?.instances ?? [];
71
+ }
72
+ /** Release (destroy) an instance. Requires the ownership token. */
73
+ async release(id, token) {
74
+ const res = await this.client.delete(`/api/v1/supabase/instances/${encodeURIComponent(id)}`, {
75
+ headers: { Cookie: `pxz_token=${token}` },
76
+ });
77
+ if (res.status !== 200 && res.status !== 204) {
78
+ throw this.toError(res.status, res.data);
79
+ }
80
+ }
81
+ /**
82
+ * Normalize the publicEndpoint (host:8000, no scheme) into a base URL.
83
+ * Prefers dashboardUrl when it already carries a scheme.
84
+ */
85
+ static toEndpoint(instance, scheme = 'http') {
86
+ if (instance.dashboardUrl && /^https?:\/\//.test(instance.dashboardUrl)) {
87
+ return instance.dashboardUrl.replace(/\/$/, '');
88
+ }
89
+ const host = instance.publicEndpoint.replace(/^https?:\/\//, '').replace(/\/$/, '');
90
+ return `${scheme}://${host}`;
91
+ }
92
+ toError(status, data) {
93
+ const err = data?.error;
94
+ const code = err?.code ?? this.codeFromStatus(status);
95
+ const message = err?.message ?? `Zero API error (HTTP ${status})`;
96
+ return new ZeroError(code, message, status);
97
+ }
98
+ codeFromStatus(status) {
99
+ if (status === 429)
100
+ return 'RATE_LIMIT_EXCEEDED';
101
+ if (status === 503)
102
+ return 'SUPABASE_POOL_EXHAUSTED';
103
+ if (status === 404)
104
+ return 'INSTANCE_NOT_FOUND';
105
+ if (status >= 500)
106
+ return 'INTERNAL_ERROR';
107
+ // 400 is not mapped to a specific code: INVALID_WHITELIST_IP is claim-only and
108
+ // comes from the response body's error.code (preferred in toError). A bare 400
109
+ // on other calls (e.g. release) must not be mislabeled as a whitelist error.
110
+ return 'UNKNOWN';
111
+ }
112
+ }
113
+ /** Wrap a network-level axios failure into a ZeroError. */
114
+ export function isZeroNetworkError(e) {
115
+ return axios.isAxiosError(e);
116
+ }
117
+ //# sourceMappingURL=zero.driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zero.driver.js","sourceRoot":"","sources":["../../src/drivers/zero.driver.ts"],"names":[],"mappings":"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"}