@supacloud/lite 0.28.1 → 0.28.3
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/CHANGELOG.md +14 -0
- package/README.md +2 -2
- package/THIRD_PARTY_NOTICES.md +4 -4
- package/dist/cli.js +277 -40
- package/dist/index.js +231 -30
- package/dist/runtime/rest/errors.d.ts.map +1 -1
- package/dist/runtime/rest/handler.d.ts.map +1 -1
- package/package.json +13 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.28.3](https://github.com/vibeunion/supacloud/compare/supacloud-lite-v0.28.2...supacloud-lite-v0.28.3) (2026-10-15)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Miscellaneous Chores
|
|
7
|
+
|
|
8
|
+
* upgrade workspace dependencies ([#1940](https://github.com/vibeunion/supacloud/issues/1940)) ([77ad418](https://github.com/vibeunion/supacloud/commit/77ad418e0568ceeeea0b59a10a3bbc451a68a95f))
|
|
9
|
+
|
|
10
|
+
## [0.28.2](https://github.com/vibeunion/supacloud/compare/supacloud-lite-v0.28.1...supacloud-lite-v0.28.2) (2026-10-15)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **lite:** enforce RPC transaction access modes ([#1936](https://github.com/vibeunion/supacloud/issues/1936)) ([fb90dcc](https://github.com/vibeunion/supacloud/commit/fb90dcc43ed49a45656e1877493eaa641ef494da))
|
|
16
|
+
|
|
3
17
|
## [0.28.1](https://github.com/vibeunion/supacloud/compare/supacloud-lite-v0.28.0...supacloud-lite-v0.28.1) (2026-10-15)
|
|
4
18
|
|
|
5
19
|
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ V1 的目标不是复刻完整 Supabase 平台控制面,而是让现有应用
|
|
|
13
13
|
### 状态
|
|
14
14
|
|
|
15
15
|
- 运行时:npm 包需要 Bun 1.4.3+;单二进制发行版已内嵌 Bun 和 PGlite 资源
|
|
16
|
-
- 数据库:PGlite 0.5.
|
|
16
|
+
- 数据库:PGlite 0.5.10,或 native PostgreSQL
|
|
17
17
|
- 项目模型:单进程、单项目,内部 project ref 固定为 `local`
|
|
18
18
|
- 客户端:直接使用官方 `@supabase/supabase-js`
|
|
19
19
|
- 数据目录:`.supacloud-lite/db`
|
|
@@ -530,7 +530,7 @@ The goal of V1 is not to replicate the full Supabase platform control plane, but
|
|
|
530
530
|
### Status
|
|
531
531
|
|
|
532
532
|
- Runtime: the npm package requires Bun 1.4.3+; the single-binary release embeds Bun and PGlite assets
|
|
533
|
-
- Database: PGlite 0.5.
|
|
533
|
+
- Database: PGlite 0.5.10, or native PostgreSQL
|
|
534
534
|
- Project model: single process, single project, with an internal project ref fixed as `local`
|
|
535
535
|
- Client: uses the official `@supabase/supabase-js` directly
|
|
536
536
|
- Data directory: `.supacloud-lite/db`
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -20,10 +20,10 @@ SupaCloud Lite 使用 ElectricSQL PGlite 作为嵌入式 PostgreSQL 引擎。
|
|
|
20
20
|
|
|
21
21
|
Lite also bundles the official PGlite extension packages:
|
|
22
22
|
|
|
23
|
-
- `@electric-sql/pglite-pgvector` 0.0.
|
|
24
|
-
- `@electric-sql/pglite-postgis` 0.2.
|
|
25
|
-
- `@electric-sql/pglite-pg_textsearch` 0.0.
|
|
26
|
-
- `@electric-sql/pglite-pgmq` 0.0.
|
|
23
|
+
- `@electric-sql/pglite-pgvector` 0.0.11
|
|
24
|
+
- `@electric-sql/pglite-postgis` 0.2.10
|
|
25
|
+
- `@electric-sql/pglite-pg_textsearch` 0.0.12
|
|
26
|
+
- `@electric-sql/pglite-pgmq` 0.0.8
|
|
27
27
|
|
|
28
28
|
These packages are published by ElectricSQL under Apache-2.0 and their
|
|
29
29
|
extension archives are embedded in the standalone binary.
|
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import { dirname as dirname8, join as join10, resolve as resolve6 } from "path";
|
|
|
8
8
|
// package.json
|
|
9
9
|
var package_default = {
|
|
10
10
|
name: "@supacloud/lite",
|
|
11
|
-
version: "0.28.
|
|
11
|
+
version: "0.28.3",
|
|
12
12
|
description: "Bun-native, single-project Supabase-compatible backend powered by PGlite or native PostgreSQL",
|
|
13
13
|
type: "module",
|
|
14
14
|
license: "Apache-2.0",
|
|
@@ -62,36 +62,37 @@ var package_default = {
|
|
|
62
62
|
typecheck: "bun x tsc --noEmit -p tsconfig.json"
|
|
63
63
|
},
|
|
64
64
|
dependencies: {
|
|
65
|
-
"@electric-sql/pglite": "0.5.
|
|
66
|
-
"@electric-sql/pglite-pg_textsearch": "0.0.
|
|
67
|
-
"@electric-sql/pglite-pgmq": "0.0.
|
|
68
|
-
"@electric-sql/pglite-pgvector": "0.0.
|
|
69
|
-
"@electric-sql/pglite-postgis": "0.2.
|
|
70
|
-
"@supacloud/db": "^0.
|
|
65
|
+
"@electric-sql/pglite": "0.5.10",
|
|
66
|
+
"@electric-sql/pglite-pg_textsearch": "0.0.12",
|
|
67
|
+
"@electric-sql/pglite-pgmq": "0.0.8",
|
|
68
|
+
"@electric-sql/pglite-pgvector": "0.0.11",
|
|
69
|
+
"@electric-sql/pglite-postgis": "0.2.10",
|
|
70
|
+
"@supacloud/db": "^0.17.0",
|
|
71
71
|
"exact-mirror": "1.2.6",
|
|
72
72
|
tar: "^7.5.22",
|
|
73
|
-
typebox: "1.3.
|
|
73
|
+
typebox: "1.3.37"
|
|
74
74
|
},
|
|
75
75
|
devDependencies: {
|
|
76
76
|
"@supacloud/app": "file:../app",
|
|
77
|
+
"@supacloud/contracts": "file:../contracts",
|
|
77
78
|
"@supacloud/compiler": "file:../compiler",
|
|
78
79
|
"@supacloud/elysia": "file:../elysia",
|
|
79
80
|
"@supabase/supabase-js": "^2.117.3",
|
|
80
81
|
"@types/bun": "^1.4.3",
|
|
81
|
-
effect: "4.0.
|
|
82
|
-
elysia: "2.0.0-beta.
|
|
82
|
+
effect: "4.0.5",
|
|
83
|
+
elysia: "2.0.0-beta.32",
|
|
83
84
|
jose: "^6.2.12",
|
|
84
85
|
typescript: "^7.0.2"
|
|
85
86
|
},
|
|
86
87
|
overrides: {
|
|
87
|
-
"@supacloud/db": "^0.
|
|
88
|
+
"@supacloud/db": "^0.17.0",
|
|
88
89
|
"@supacloud/app": "file:../app",
|
|
89
90
|
"@supacloud/compiler": "file:../compiler",
|
|
90
91
|
"@supacloud/elysia": "file:../elysia",
|
|
91
92
|
"@supacloud/contracts": "file:../contracts",
|
|
92
93
|
"@supacloud/commands": "file:../commands",
|
|
93
94
|
"@supacloud/delivery": "file:../delivery",
|
|
94
|
-
"@graphql-tools/utils": "^12.0.
|
|
95
|
+
"@graphql-tools/utils": "^12.0.4"
|
|
95
96
|
},
|
|
96
97
|
engines: {
|
|
97
98
|
bun: ">=1.4.3"
|
|
@@ -8368,6 +8369,8 @@ function statusForSqlState(code) {
|
|
|
8368
8369
|
switch (code) {
|
|
8369
8370
|
case "42501":
|
|
8370
8371
|
return 403;
|
|
8372
|
+
case "25006":
|
|
8373
|
+
return 405;
|
|
8371
8374
|
case "23503":
|
|
8372
8375
|
case "23505":
|
|
8373
8376
|
return 409;
|
|
@@ -8719,17 +8722,22 @@ class RestHandler {
|
|
|
8719
8722
|
return await this.runRpc(ctx, fn, call, q, prefer, wantsObject, wantsCsv, schema, method);
|
|
8720
8723
|
}
|
|
8721
8724
|
async runRpc(ctx, fn, call, q, prefer, wantsObject, wantsCsv, schema, method) {
|
|
8725
|
+
const withRpcContext = (run) => this.db.withContext(ctx, async (query) => {
|
|
8726
|
+
if (method !== "POST" || fn.volatility !== "v")
|
|
8727
|
+
await query("set transaction read only");
|
|
8728
|
+
return run(query);
|
|
8729
|
+
});
|
|
8722
8730
|
if (fn.returnType === "void" && !fn.returnsSet) {
|
|
8723
|
-
await
|
|
8731
|
+
await withRpcContext((query) => query(`select ${call}`, []));
|
|
8724
8732
|
return new Response(null, { status: 204 });
|
|
8725
8733
|
}
|
|
8726
8734
|
if (!fn.returnsSet && ["b", "d", "e"].includes(fn.returnTypType)) {
|
|
8727
|
-
const res = await
|
|
8735
|
+
const res = await withRpcContext((query) => query(`select to_json(${call}) as result`, []));
|
|
8728
8736
|
const value = res.rows[0].result;
|
|
8729
8737
|
return jsonResponse(200, value);
|
|
8730
8738
|
}
|
|
8731
8739
|
if (fn.returnsSet && ["b", "d", "e"].includes(fn.returnTypType)) {
|
|
8732
|
-
const res = await
|
|
8740
|
+
const res = await withRpcContext((query) => query(`select coalesce(json_agg(_v), '[]'::json) as body from ${call} as _t(_v)`, []));
|
|
8733
8741
|
return jsonResponse(200, res.rows[0].body);
|
|
8734
8742
|
}
|
|
8735
8743
|
const alias = "_f";
|
|
@@ -8757,7 +8765,7 @@ class RestHandler {
|
|
|
8757
8765
|
const core = `select ${exprs.join(", ")} from ${resultSource} as ${quoteIdent(alias)}${where}${order}${limitOffset}`;
|
|
8758
8766
|
const bodySql = `select coalesce(json_agg(row_to_json(_r)), '[]'::json) as body from (${core}) _r`;
|
|
8759
8767
|
const sql = wantsCount ? `with ${quoteIdent("_rpc_result")} as (select * from ${call}) select (${bodySql}) as body, (select count(*)::int from ${resultSource} as ${quoteIdent(alias)}${where}) as count` : bodySql;
|
|
8760
|
-
const { rows, count } = await
|
|
8768
|
+
const { rows, count } = await withRpcContext(async (query) => {
|
|
8761
8769
|
const res = await query(sql, []);
|
|
8762
8770
|
const rows = res.rows[0].body;
|
|
8763
8771
|
if (wantsObject && !wantsCsv)
|
|
@@ -13208,6 +13216,7 @@ ORDER BY n.nspname, c.relname, p.polname
|
|
|
13208
13216
|
var FUNCTIONS_SQL = `
|
|
13209
13217
|
SELECT n.nspname AS schema,
|
|
13210
13218
|
p.proname AS name,
|
|
13219
|
+
pg_catalog.oidvectortypes(p.proargtypes) AS identity_arguments,
|
|
13211
13220
|
p.prosecdef AS security_definer,
|
|
13212
13221
|
p.proconfig AS config,
|
|
13213
13222
|
l.lanname AS language
|
|
@@ -13281,12 +13290,13 @@ async function readCatalog(executor, schemas = ["public"]) {
|
|
|
13281
13290
|
name: row.name,
|
|
13282
13291
|
command: POLCMD_MAP[row.command] ?? "all",
|
|
13283
13292
|
roles: row.roles ?? [],
|
|
13284
|
-
usingExpr: row.using_expr
|
|
13285
|
-
checkExpr: row.check_expr
|
|
13293
|
+
...row.using_expr == null ? {} : { usingExpr: row.using_expr },
|
|
13294
|
+
...row.check_expr == null ? {} : { checkExpr: row.check_expr }
|
|
13286
13295
|
})),
|
|
13287
13296
|
functions: functionRows.map((row) => ({
|
|
13288
13297
|
schema: row.schema,
|
|
13289
13298
|
name: row.name,
|
|
13299
|
+
identityArguments: row.identity_arguments,
|
|
13290
13300
|
security: row.security_definer ? "definer" : "invoker",
|
|
13291
13301
|
searchPath: extractSearchPath(row.config),
|
|
13292
13302
|
language: row.language
|
|
@@ -13314,8 +13324,10 @@ function splitQualifiedName(name) {
|
|
|
13314
13324
|
function isFixedSearchPath(searchPath) {
|
|
13315
13325
|
if (searchPath === null)
|
|
13316
13326
|
return false;
|
|
13327
|
+
if (searchPath.trim() === "" || searchPath.trim() === '""')
|
|
13328
|
+
return true;
|
|
13317
13329
|
const parts = searchPath.split(",").map((part) => part.trim().replace(/^"|"$/g, ""));
|
|
13318
|
-
return parts.every((part) => part !== "" && part.toLowerCase() !== "pg_temp");
|
|
13330
|
+
return parts.every((part) => part !== "" && part.toLowerCase() !== "pg_temp" && part.toLowerCase() !== "$user");
|
|
13319
13331
|
}
|
|
13320
13332
|
function reconcileModule(module, catalog) {
|
|
13321
13333
|
const issues = [];
|
|
@@ -13323,9 +13335,22 @@ function reconcileModule(module, catalog) {
|
|
|
13323
13335
|
const push = (severity, code, object, message) => issues.push({ severity, code, object, message });
|
|
13324
13336
|
for (const policy of module.policies) {
|
|
13325
13337
|
const [schema, table] = splitQualifiedName(policy.table);
|
|
13326
|
-
const found = catalog.policies.
|
|
13338
|
+
const found = catalog.policies.find((cp) => cp.schema === schema && cp.table === table && cp.name === policy.name);
|
|
13327
13339
|
if (!found) {
|
|
13328
13340
|
push("error", "missing-policy", `${policy.table}.${policy.name}`, `\u58F0\u660E\u7684\u7B56\u7565 ${policy.name} \u5728\u8868 ${policy.table} \u7684 catalog \u4E2D\u4E0D\u5B58\u5728`);
|
|
13341
|
+
} else {
|
|
13342
|
+
if (found.command !== policy.operation) {
|
|
13343
|
+
push("error", "policy-command-mismatch", `${policy.table}.${policy.name}`, `\u7B56\u7565 ${policy.name} \u58F0\u660E\u4E3A ${policy.operation}\uFF0Ccatalog \u5B9E\u9645\u4E3A ${found.command}`);
|
|
13344
|
+
}
|
|
13345
|
+
if (!sameRoles(policy.roles, found.roles)) {
|
|
13346
|
+
push("error", "policy-roles-mismatch", `${policy.table}.${policy.name}`, `\u7B56\u7565 ${policy.name} \u58F0\u660E\u89D2\u8272\u4E0E catalog \u4E0D\u4E00\u81F4`);
|
|
13347
|
+
}
|
|
13348
|
+
if (policy.usingExpr !== undefined && policy.usingExpr !== (found.usingExpr ?? null)) {
|
|
13349
|
+
push("error", "policy-using-mismatch", `${policy.table}.${policy.name}`, `\u7B56\u7565 ${policy.name} \u7684 USING \u8868\u8FBE\u5F0F\u4E0E catalog \u4E0D\u4E00\u81F4`);
|
|
13350
|
+
}
|
|
13351
|
+
if (policy.checkExpr !== undefined && policy.checkExpr !== (found.checkExpr ?? null)) {
|
|
13352
|
+
push("error", "policy-check-mismatch", `${policy.table}.${policy.name}`, `\u7B56\u7565 ${policy.name} \u7684 WITH CHECK \u8868\u8FBE\u5F0F\u4E0E catalog \u4E0D\u4E00\u81F4`);
|
|
13353
|
+
}
|
|
13329
13354
|
}
|
|
13330
13355
|
}
|
|
13331
13356
|
const declaredPolicyKeys = new Set(module.policies.map((p) => `${p.table}::${p.name}`));
|
|
@@ -13337,11 +13362,16 @@ function reconcileModule(module, catalog) {
|
|
|
13337
13362
|
}
|
|
13338
13363
|
for (const fn of module.functions) {
|
|
13339
13364
|
const [schema, name] = splitQualifiedName(fn.name);
|
|
13340
|
-
const
|
|
13341
|
-
if (
|
|
13365
|
+
const candidates = catalog.functions.filter((f) => f.schema === schema && f.name === name);
|
|
13366
|
+
if (candidates.length === 0) {
|
|
13342
13367
|
push("error", "missing-function", fn.name, `\u58F0\u660E\u7684\u51FD\u6570 ${fn.name} \u5728 catalog \u4E2D\u4E0D\u5B58\u5728`);
|
|
13343
13368
|
continue;
|
|
13344
13369
|
}
|
|
13370
|
+
const cf = fn.signature !== undefined ? candidates.find((candidate) => functionSignature(candidate) === fn.signature) : candidates.length === 1 ? candidates[0] : undefined;
|
|
13371
|
+
if (!cf) {
|
|
13372
|
+
push("error", fn.signature !== undefined ? "missing-function" : "ambiguous-function-overload", fn.name, fn.signature !== undefined ? `\u58F0\u660E\u7684\u51FD\u6570\u7B7E\u540D ${fn.signature} \u5728 catalog \u4E2D\u4E0D\u5B58\u5728\u6216\u7F3A\u5C11\u7B7E\u540D\u5143\u6570\u636E` : `\u51FD\u6570 ${fn.name} \u5B58\u5728\u591A\u4E2A\u91CD\u8F7D\uFF0C\u58F0\u660E\u5FC5\u987B\u5305\u542B\u5B8C\u6574 signature`);
|
|
13373
|
+
continue;
|
|
13374
|
+
}
|
|
13345
13375
|
if (cf.security !== fn.security) {
|
|
13346
13376
|
push("warn", "security-mismatch", fn.name, `\u51FD\u6570 ${fn.name} \u58F0\u660E\u4E3A security ${fn.security}\uFF0Ccatalog \u5B9E\u9645\u4E3A ${cf.security}`);
|
|
13347
13377
|
}
|
|
@@ -13352,9 +13382,11 @@ function reconcileModule(module, catalog) {
|
|
|
13352
13382
|
}
|
|
13353
13383
|
for (const trigger of module.triggers) {
|
|
13354
13384
|
const [schema, table] = splitQualifiedName(trigger.table);
|
|
13355
|
-
const found = catalog.triggers.
|
|
13385
|
+
const found = catalog.triggers.find((ct) => ct.schema === schema && ct.table === table && ct.name === trigger.name);
|
|
13356
13386
|
if (!found) {
|
|
13357
13387
|
push("error", "missing-trigger", `${trigger.table}.${trigger.name}`, `\u58F0\u660E\u7684\u89E6\u53D1\u5668 ${trigger.name} \u5728\u8868 ${trigger.table} \u7684 catalog \u4E2D\u4E0D\u5B58\u5728`);
|
|
13388
|
+
} else if (found.enabled !== (trigger.enabled ?? true)) {
|
|
13389
|
+
push("error", found.enabled ? "trigger-enabled-mismatch" : "disabled-trigger", `${trigger.table}.${trigger.name}`, `\u58F0\u660E\u7684\u89E6\u53D1\u5668 ${trigger.name} \u7684\u542F\u7528\u72B6\u6001\u4E0E catalog \u4E0D\u4E00\u81F4`);
|
|
13358
13390
|
}
|
|
13359
13391
|
}
|
|
13360
13392
|
const declaredTriggerKeys = new Set(module.triggers.map((t) => `${t.table}::${t.name}`));
|
|
@@ -13367,7 +13399,9 @@ function reconcileModule(module, catalog) {
|
|
|
13367
13399
|
for (const table of module.tables) {
|
|
13368
13400
|
const [schema, name] = splitQualifiedName(table);
|
|
13369
13401
|
const ct = catalog.tables.find((t) => t.schema === schema && t.name === name);
|
|
13370
|
-
if (
|
|
13402
|
+
if (!ct) {
|
|
13403
|
+
push("error", "missing-table", table, `\u58F0\u660E\u7684\u8868 ${table} \u5728 catalog \u4E2D\u4E0D\u5B58\u5728`);
|
|
13404
|
+
} else if (!ct.rlsEnabled) {
|
|
13371
13405
|
push("error", "rls-disabled", table, `\u5F52\u5C5E\u8868 ${table} \u672A\u5F00\u542F\u884C\u7EA7\u5B89\u5168\uFF08relrowsecurity = false\uFF09`);
|
|
13372
13406
|
}
|
|
13373
13407
|
}
|
|
@@ -13390,6 +13424,15 @@ function reconcileModule(module, catalog) {
|
|
|
13390
13424
|
ok: !issues.some((issue) => issue.severity === "error")
|
|
13391
13425
|
};
|
|
13392
13426
|
}
|
|
13427
|
+
function sameRoles(expected, actual) {
|
|
13428
|
+
const normalize = (roles) => [...new Set(roles)].sort();
|
|
13429
|
+
return JSON.stringify(normalize(expected)) === JSON.stringify(normalize(actual));
|
|
13430
|
+
}
|
|
13431
|
+
function functionSignature(functionEntry) {
|
|
13432
|
+
if (functionEntry.identityArguments === undefined)
|
|
13433
|
+
return;
|
|
13434
|
+
return `${functionEntry.schema}.${functionEntry.name}(${functionEntry.identityArguments})`;
|
|
13435
|
+
}
|
|
13393
13436
|
var SECURITY_DEFINER_RE = /\bsecurity\s+definer\b/i;
|
|
13394
13437
|
var SET_SEARCH_PATH_RE = /\bset\s+search_path\b/i;
|
|
13395
13438
|
var GRANT_TO_PUBLIC_RE = /\bgrant\b[^;]*\bto\s+public\b/i;
|
|
@@ -13470,14 +13513,15 @@ async function lintModule(module, readFile) {
|
|
|
13470
13513
|
for (const [file, sql] of contents) {
|
|
13471
13514
|
issues.push(...lintSql(sql, file));
|
|
13472
13515
|
}
|
|
13473
|
-
|
|
13516
|
+
const firstPolicy = module.policies[0];
|
|
13517
|
+
if (firstPolicy) {
|
|
13474
13518
|
const anyEnable = module.policies.some((policy) => ENABLE_RLS_RE.test(contents.get(policy.source) ?? ""));
|
|
13475
13519
|
if (!anyEnable) {
|
|
13476
13520
|
issues.push({
|
|
13477
13521
|
severity: "warn",
|
|
13478
13522
|
code: "missing-rls-enable",
|
|
13479
13523
|
message: `\u6A21\u5757 ${module.name} \u58F0\u660E\u4E86 ${module.policies.length} \u6761\u7B56\u7565\uFF0C\u4F46\u6240\u6709\u7B56\u7565\u6E90\u6587\u4EF6\u90FD\u6CA1\u6709 enable row level security`,
|
|
13480
|
-
file:
|
|
13524
|
+
file: firstPolicy.source
|
|
13481
13525
|
});
|
|
13482
13526
|
}
|
|
13483
13527
|
}
|
|
@@ -13523,10 +13567,10 @@ function keys(row, expected) {
|
|
|
13523
13567
|
function validTarget(value) {
|
|
13524
13568
|
const target = record(value);
|
|
13525
13569
|
keys(target, ["environment", "projectRef"]);
|
|
13526
|
-
if (typeof target
|
|
13570
|
+
if (typeof target["environment"] !== "string" || !/^[a-z][a-z0-9-]{0,62}$/.test(target["environment"]) || typeof target["projectRef"] !== "string" || !/^[a-z0-9][a-z0-9_-]{0,62}$/.test(target["projectRef"])) {
|
|
13527
13571
|
throw new Error("Invalid migration binding target");
|
|
13528
13572
|
}
|
|
13529
|
-
return target;
|
|
13573
|
+
return { environment: target["environment"], projectRef: target["projectRef"] };
|
|
13530
13574
|
}
|
|
13531
13575
|
function validFile(file) {
|
|
13532
13576
|
return typeof file === "string" && /^[a-zA-Z0-9_./-]+\.sql$/.test(file) && !file.startsWith("/") && file.split("/").every((part) => part && part !== "." && part !== "..");
|
|
@@ -13534,32 +13578,37 @@ function validFile(file) {
|
|
|
13534
13578
|
function parseMigrationBindingManifest(value) {
|
|
13535
13579
|
const manifest = record(value);
|
|
13536
13580
|
keys(manifest, ["schema", "targets", "templates"]);
|
|
13537
|
-
if (manifest
|
|
13581
|
+
if (manifest["schema"] !== "supacloud.migration-bindings.v1" || !Array.isArray(manifest["targets"]) || !manifest["targets"].length || !Array.isArray(manifest["templates"])) {
|
|
13538
13582
|
throw new Error("Invalid migration binding manifest");
|
|
13539
13583
|
}
|
|
13540
|
-
const targets = manifest
|
|
13584
|
+
const targets = manifest["targets"].map(validTarget);
|
|
13541
13585
|
if (new Set(targets.map((target) => target.environment)).size !== targets.length) {
|
|
13542
13586
|
throw new Error("Duplicate migration binding environment");
|
|
13543
13587
|
}
|
|
13544
13588
|
const files = new Set;
|
|
13545
|
-
const templates = manifest
|
|
13546
|
-
const template = record(
|
|
13589
|
+
const templates = manifest["templates"].map((value) => {
|
|
13590
|
+
const template = record(value);
|
|
13547
13591
|
keys(template, ["file", "templateSha256", "parameters"]);
|
|
13548
|
-
if (!validFile(template
|
|
13592
|
+
if (!validFile(template["file"]) || files.has(template["file"]) || typeof template["templateSha256"] !== "string" || !HASH.test(template["templateSha256"]) || !Array.isArray(template["parameters"]) || !template["parameters"].length) {
|
|
13549
13593
|
throw new Error("Invalid or duplicate migration binding template");
|
|
13550
13594
|
}
|
|
13551
|
-
files.add(template
|
|
13595
|
+
files.add(template["file"]);
|
|
13552
13596
|
const placeholders = new Set;
|
|
13553
|
-
const parameters = template
|
|
13554
|
-
const parameter = record(
|
|
13597
|
+
const parameters = template["parameters"].map((value) => {
|
|
13598
|
+
const parameter = record(value);
|
|
13555
13599
|
keys(parameter, ["placeholder", "variable", "type", "occurrences"]);
|
|
13556
|
-
if (typeof parameter
|
|
13600
|
+
if (typeof parameter["placeholder"] !== "string" || !/^__[A-Z][A-Z0-9_]*__$/.test(parameter["placeholder"]) || placeholders.has(parameter["placeholder"]) || typeof parameter["variable"] !== "string" || !/^[A-Z][A-Z0-9_]*$/.test(parameter["variable"]) || parameter["type"] !== "uuid" && parameter["type"] !== "https-url" && parameter["type"] !== "resource-name" || typeof parameter["occurrences"] !== "number" || !Number.isSafeInteger(parameter["occurrences"]) || parameter["occurrences"] < 1) {
|
|
13557
13601
|
throw new Error("Invalid or duplicate migration binding parameter");
|
|
13558
13602
|
}
|
|
13559
|
-
placeholders.add(parameter
|
|
13560
|
-
return
|
|
13603
|
+
placeholders.add(parameter["placeholder"]);
|
|
13604
|
+
return {
|
|
13605
|
+
placeholder: parameter["placeholder"],
|
|
13606
|
+
variable: parameter["variable"],
|
|
13607
|
+
type: parameter["type"],
|
|
13608
|
+
occurrences: parameter["occurrences"]
|
|
13609
|
+
};
|
|
13561
13610
|
});
|
|
13562
|
-
return { file: template
|
|
13611
|
+
return { file: template["file"], templateSha256: template["templateSha256"], parameters };
|
|
13563
13612
|
});
|
|
13564
13613
|
return { schema: "supacloud.migration-bindings.v1", targets, templates };
|
|
13565
13614
|
}
|
|
@@ -13647,6 +13696,194 @@ function renderMigrationBindings(options) {
|
|
|
13647
13696
|
}
|
|
13648
13697
|
};
|
|
13649
13698
|
}
|
|
13699
|
+
var COMMAND_PERSISTENCE_SQL = `
|
|
13700
|
+
DO $runtime$
|
|
13701
|
+
BEGIN
|
|
13702
|
+
IF to_regprocedure('supacloud_workflows.start_run(uuid,text,text,text,jsonb,integer)') IS NULL
|
|
13703
|
+
OR to_regprocedure('supacloud_commands.status(jsonb)') IS NULL THEN
|
|
13704
|
+
RAISE EXCEPTION 'Install the SupaCloud Commands/Workflow/PGMQ runtime before command persistence';
|
|
13705
|
+
END IF;
|
|
13706
|
+
END
|
|
13707
|
+
$runtime$;
|
|
13708
|
+
CREATE SCHEMA IF NOT EXISTS supacloud_commands;
|
|
13709
|
+
REVOKE ALL ON SCHEMA supacloud_commands FROM PUBLIC;
|
|
13710
|
+
CREATE TABLE IF NOT EXISTS supacloud_commands.execution_receipts (
|
|
13711
|
+
tenant_id text NOT NULL,
|
|
13712
|
+
actor_id text NOT NULL,
|
|
13713
|
+
command text NOT NULL,
|
|
13714
|
+
operation_key text NOT NULL,
|
|
13715
|
+
kind text NOT NULL CHECK (kind IN ('transactional', 'external')),
|
|
13716
|
+
input_fingerprint text NOT NULL CHECK (input_fingerprint ~ '^[a-f0-9]{64}$'),
|
|
13717
|
+
input_payload text,
|
|
13718
|
+
dispatch_key uuid NOT NULL DEFAULT gen_random_uuid() UNIQUE,
|
|
13719
|
+
status text NOT NULL CHECK (status IN ('pending', 'unknown', 'confirmed', 'rejected')),
|
|
13720
|
+
audit_state text NOT NULL CHECK (audit_state IN ('pending', 'complete')),
|
|
13721
|
+
result jsonb,
|
|
13722
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
13723
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
13724
|
+
PRIMARY KEY (tenant_id, actor_id, command, operation_key),
|
|
13725
|
+
CHECK ((status = 'confirmed') = (result IS NOT NULL)),
|
|
13726
|
+
CONSTRAINT execution_receipts_audit_terminal_check CHECK (
|
|
13727
|
+
(audit_state <> 'complete' OR status IN ('confirmed', 'rejected'))
|
|
13728
|
+
AND (status <> 'rejected' OR audit_state = 'complete')),
|
|
13729
|
+
CONSTRAINT execution_receipts_transactional_check CHECK (
|
|
13730
|
+
kind <> 'transactional' OR (status = 'confirmed' AND audit_state = 'complete'))
|
|
13731
|
+
);
|
|
13732
|
+
CREATE TABLE IF NOT EXISTS supacloud_commands.execution_audit (
|
|
13733
|
+
tenant_id text NOT NULL,
|
|
13734
|
+
actor_id text NOT NULL,
|
|
13735
|
+
command text NOT NULL,
|
|
13736
|
+
operation_key text NOT NULL,
|
|
13737
|
+
event text NOT NULL,
|
|
13738
|
+
details jsonb NOT NULL,
|
|
13739
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
13740
|
+
PRIMARY KEY (tenant_id, actor_id, command, operation_key),
|
|
13741
|
+
FOREIGN KEY (tenant_id, actor_id, command, operation_key)
|
|
13742
|
+
REFERENCES supacloud_commands.execution_receipts (tenant_id, actor_id, command, operation_key)
|
|
13743
|
+
);
|
|
13744
|
+
REVOKE ALL ON supacloud_commands.execution_receipts, supacloud_commands.execution_audit FROM PUBLIC;
|
|
13745
|
+
CREATE OR REPLACE FUNCTION supacloud_commands.protect_terminal_execution()
|
|
13746
|
+
RETURNS trigger LANGUAGE plpgsql SET search_path = '' AS $terminal$
|
|
13747
|
+
BEGIN
|
|
13748
|
+
IF OLD.status IN ('confirmed','rejected') AND (
|
|
13749
|
+
NEW.status IS DISTINCT FROM OLD.status OR NEW.result IS DISTINCT FROM OLD.result
|
|
13750
|
+
OR (OLD.audit_state = 'complete' AND NEW.audit_state IS DISTINCT FROM OLD.audit_state)) THEN
|
|
13751
|
+
RAISE EXCEPTION 'SUPACLOUD_COMMAND_TERMINAL_RECEIPT';
|
|
13752
|
+
END IF;
|
|
13753
|
+
RETURN NEW;
|
|
13754
|
+
END
|
|
13755
|
+
$terminal$;
|
|
13756
|
+
CREATE OR REPLACE TRIGGER execution_terminal_guard
|
|
13757
|
+
BEFORE UPDATE ON supacloud_commands.execution_receipts
|
|
13758
|
+
FOR EACH ROW EXECUTE FUNCTION supacloud_commands.protect_terminal_execution();
|
|
13759
|
+
|
|
13760
|
+
CREATE OR REPLACE FUNCTION supacloud_commands.require_rejection_audit()
|
|
13761
|
+
RETURNS trigger LANGUAGE plpgsql SET search_path = '' AS $rejection$
|
|
13762
|
+
BEGIN
|
|
13763
|
+
IF NEW.status = 'rejected' AND NOT EXISTS (
|
|
13764
|
+
SELECT FROM supacloud_commands.execution_audit a
|
|
13765
|
+
WHERE a.tenant_id=NEW.tenant_id AND a.actor_id=NEW.actor_id
|
|
13766
|
+
AND a.command=NEW.command AND a.operation_key=NEW.operation_key
|
|
13767
|
+
) THEN
|
|
13768
|
+
RAISE EXCEPTION 'SUPACLOUD_COMMAND_REJECTION_AUDIT_REQUIRED';
|
|
13769
|
+
END IF;
|
|
13770
|
+
RETURN NEW;
|
|
13771
|
+
END
|
|
13772
|
+
$rejection$;
|
|
13773
|
+
DROP TRIGGER IF EXISTS execution_rejection_audit ON supacloud_commands.execution_receipts;
|
|
13774
|
+
CREATE CONSTRAINT TRIGGER execution_rejection_audit
|
|
13775
|
+
AFTER INSERT OR UPDATE ON supacloud_commands.execution_receipts
|
|
13776
|
+
DEFERRABLE INITIALLY DEFERRED
|
|
13777
|
+
FOR EACH ROW WHEN (NEW.status = 'rejected')
|
|
13778
|
+
EXECUTE FUNCTION supacloud_commands.require_rejection_audit();
|
|
13779
|
+
CREATE OR REPLACE FUNCTION supacloud_commands.enqueue_execution_recovery()
|
|
13780
|
+
RETURNS trigger LANGUAGE plpgsql SET search_path = '' AS $recovery$
|
|
13781
|
+
DECLARE submission jsonb;
|
|
13782
|
+
BEGIN
|
|
13783
|
+
PERFORM pg_advisory_xact_lock(hashtextextended(NEW.dispatch_key::text,0));
|
|
13784
|
+
SELECT supacloud_commands.snapshot(NEW.dispatch_key,false) INTO submission;
|
|
13785
|
+
IF submission IS NOT NULL THEN
|
|
13786
|
+
IF submission->>'commandType' IS DISTINCT FROM NEW.command
|
|
13787
|
+
OR submission->>'actorId' IS DISTINCT FROM NEW.actor_id
|
|
13788
|
+
OR submission->>'tenantId' IS DISTINCT FROM NEW.tenant_id
|
|
13789
|
+
OR NEW.operation_key <> NEW.dispatch_key::text THEN
|
|
13790
|
+
RAISE EXCEPTION 'SUPACLOUD_COMMAND_IDENTITY_CONFLICT';
|
|
13791
|
+
END IF;
|
|
13792
|
+
RETURN NEW;
|
|
13793
|
+
END IF;
|
|
13794
|
+
IF NEW.kind = 'external' AND NEW.status <> 'rejected'
|
|
13795
|
+
AND (NEW.status <> 'confirmed' OR NEW.audit_state <> 'complete') THEN
|
|
13796
|
+
PERFORM supacloud_workflows.start_run(
|
|
13797
|
+
NEW.dispatch_key, 'supacloud.command.reconcile', '1', 'reconcile',
|
|
13798
|
+
jsonb_build_object('commandId', NEW.dispatch_key, 'tenantId', NEW.tenant_id,
|
|
13799
|
+
'actorId', NEW.actor_id, 'command', NEW.command, 'operationId', NEW.operation_key),
|
|
13800
|
+
20
|
|
13801
|
+
);
|
|
13802
|
+
END IF;
|
|
13803
|
+
RETURN NEW;
|
|
13804
|
+
END
|
|
13805
|
+
$recovery$;
|
|
13806
|
+
CREATE OR REPLACE TRIGGER execution_recovery_enqueue
|
|
13807
|
+
AFTER INSERT ON supacloud_commands.execution_receipts
|
|
13808
|
+
FOR EACH ROW EXECUTE FUNCTION supacloud_commands.enqueue_execution_recovery();
|
|
13809
|
+
|
|
13810
|
+
CREATE OR REPLACE FUNCTION supacloud_commands.execution_status(request jsonb)
|
|
13811
|
+
RETURNS jsonb LANGUAGE sql STABLE SECURITY DEFINER SET search_path = '' AS $status$
|
|
13812
|
+
SELECT jsonb_build_object('kind','execution','commandId',r.dispatch_key,
|
|
13813
|
+
'execution',jsonb_build_object('tenantId',r.tenant_id,'actorId',r.actor_id,
|
|
13814
|
+
'command',r.command,'operationId',r.operation_key,'dispatchKey',r.dispatch_key,
|
|
13815
|
+
'status',r.status,'audit',r.audit_state)
|
|
13816
|
+
|| CASE WHEN r.status='confirmed' THEN jsonb_build_object('result',r.result) ELSE '{}'::jsonb END,
|
|
13817
|
+
'workflow',CASE WHEN w.id IS NULL THEN NULL ELSE jsonb_build_object('runId',w.id,'status',w.status) END)
|
|
13818
|
+
FROM supacloud_commands.execution_receipts r
|
|
13819
|
+
LEFT JOIN supacloud_workflows.runs w ON w.id=r.dispatch_key
|
|
13820
|
+
WHERE CASE WHEN request ? 'commandId' THEN r.dispatch_key::text=request->>'commandId'
|
|
13821
|
+
ELSE r.tenant_id=request->>'tenantId' AND r.actor_id=request->>'actorId'
|
|
13822
|
+
AND r.command=request->>'command' AND r.operation_key=request->>'operationId' END
|
|
13823
|
+
$status$;
|
|
13824
|
+
REVOKE ALL ON FUNCTION supacloud_commands.execution_status(jsonb) FROM PUBLIC;
|
|
13825
|
+
`;
|
|
13826
|
+
var COMMAND_PERSISTENCE_UPGRADE_SQL = `
|
|
13827
|
+
ALTER TABLE supacloud_commands.execution_receipts
|
|
13828
|
+
ADD COLUMN IF NOT EXISTS input_fingerprint text,
|
|
13829
|
+
ADD COLUMN IF NOT EXISTS input_payload text;
|
|
13830
|
+
DO $migration$
|
|
13831
|
+
BEGIN
|
|
13832
|
+
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='supacloud_commands'
|
|
13833
|
+
AND table_name='execution_receipts' AND column_name='input_key') THEN
|
|
13834
|
+
EXECUTE 'UPDATE supacloud_commands.execution_receipts
|
|
13835
|
+
SET input_fingerprint=encode(sha256(convert_to(input_key,''UTF8'')),''hex''),input_payload=input_key
|
|
13836
|
+
WHERE input_fingerprint IS NULL';
|
|
13837
|
+
ALTER TABLE supacloud_commands.execution_receipts DROP COLUMN input_key;
|
|
13838
|
+
END IF;
|
|
13839
|
+
END
|
|
13840
|
+
$migration$;
|
|
13841
|
+
ALTER TABLE supacloud_commands.execution_receipts ALTER COLUMN input_fingerprint SET NOT NULL;
|
|
13842
|
+
ALTER TABLE supacloud_commands.execution_receipts
|
|
13843
|
+
DROP CONSTRAINT IF EXISTS execution_receipts_status_check,
|
|
13844
|
+
DROP CONSTRAINT IF EXISTS execution_receipts_check1,
|
|
13845
|
+
DROP CONSTRAINT IF EXISTS execution_receipts_transactional_check,
|
|
13846
|
+
DROP CONSTRAINT IF EXISTS execution_receipts_audit_terminal_check;
|
|
13847
|
+
ALTER TABLE supacloud_commands.execution_receipts
|
|
13848
|
+
ADD CONSTRAINT execution_receipts_status_check CHECK (status IN ('pending','unknown','confirmed','rejected')),
|
|
13849
|
+
ADD CONSTRAINT execution_receipts_transactional_check CHECK (
|
|
13850
|
+
kind <> 'transactional' OR (status = 'confirmed' AND audit_state = 'complete')),
|
|
13851
|
+
ADD CONSTRAINT execution_receipts_audit_terminal_check CHECK (
|
|
13852
|
+
(audit_state <> 'complete' OR status IN ('confirmed','rejected'))
|
|
13853
|
+
AND (status <> 'rejected' OR audit_state = 'complete'));
|
|
13854
|
+
DO $constraint$
|
|
13855
|
+
BEGIN
|
|
13856
|
+
IF NOT EXISTS (SELECT 1 FROM pg_constraint
|
|
13857
|
+
WHERE conrelid='supacloud_commands.execution_receipts'::regclass
|
|
13858
|
+
AND conname='execution_receipts_input_fingerprint_check') THEN
|
|
13859
|
+
ALTER TABLE supacloud_commands.execution_receipts
|
|
13860
|
+
ADD CONSTRAINT execution_receipts_input_fingerprint_check CHECK (input_fingerprint ~ '^[a-f0-9]{64}$');
|
|
13861
|
+
END IF;
|
|
13862
|
+
END
|
|
13863
|
+
$constraint$;
|
|
13864
|
+
${COMMAND_PERSISTENCE_SQL}
|
|
13865
|
+
DO $backfill$
|
|
13866
|
+
DECLARE r record;
|
|
13867
|
+
BEGIN
|
|
13868
|
+
FOR r IN SELECT * FROM supacloud_commands.execution_receipts
|
|
13869
|
+
WHERE kind='external' AND status<>'rejected' AND (status<>'confirmed' OR audit_state<>'complete')
|
|
13870
|
+
AND NOT EXISTS(SELECT FROM supacloud_workflows.runs w WHERE w.id=dispatch_key)
|
|
13871
|
+
LOOP
|
|
13872
|
+
PERFORM supacloud_workflows.start_run(
|
|
13873
|
+
r.dispatch_key,'supacloud.command.reconcile','1','reconcile',
|
|
13874
|
+
jsonb_build_object('commandId',r.dispatch_key,'tenantId',r.tenant_id,
|
|
13875
|
+
'actorId',r.actor_id,'command',r.command,'operationId',r.operation_key),20
|
|
13876
|
+
);
|
|
13877
|
+
END LOOP;
|
|
13878
|
+
END
|
|
13879
|
+
$backfill$;
|
|
13880
|
+
DROP INDEX IF EXISTS supacloud_commands.execution_pending_recovery;
|
|
13881
|
+
ALTER TABLE supacloud_commands.execution_receipts
|
|
13882
|
+
DROP COLUMN IF EXISTS lease_id,
|
|
13883
|
+
DROP COLUMN IF EXISTS lease_until,
|
|
13884
|
+
DROP COLUMN IF EXISTS next_attempt_at,
|
|
13885
|
+
DROP COLUMN IF EXISTS recovery_attempts;
|
|
13886
|
+
`;
|
|
13650
13887
|
|
|
13651
13888
|
// src/runtime/node/db-check.ts
|
|
13652
13889
|
async function loadDatabaseModules(moduleFile) {
|
package/dist/index.js
CHANGED
|
@@ -88,7 +88,7 @@ function randomToken(bytes = 32) {
|
|
|
88
88
|
// package.json
|
|
89
89
|
var package_default = {
|
|
90
90
|
name: "@supacloud/lite",
|
|
91
|
-
version: "0.28.
|
|
91
|
+
version: "0.28.3",
|
|
92
92
|
description: "Bun-native, single-project Supabase-compatible backend powered by PGlite or native PostgreSQL",
|
|
93
93
|
type: "module",
|
|
94
94
|
license: "Apache-2.0",
|
|
@@ -142,36 +142,37 @@ var package_default = {
|
|
|
142
142
|
typecheck: "bun x tsc --noEmit -p tsconfig.json"
|
|
143
143
|
},
|
|
144
144
|
dependencies: {
|
|
145
|
-
"@electric-sql/pglite": "0.5.
|
|
146
|
-
"@electric-sql/pglite-pg_textsearch": "0.0.
|
|
147
|
-
"@electric-sql/pglite-pgmq": "0.0.
|
|
148
|
-
"@electric-sql/pglite-pgvector": "0.0.
|
|
149
|
-
"@electric-sql/pglite-postgis": "0.2.
|
|
150
|
-
"@supacloud/db": "^0.
|
|
145
|
+
"@electric-sql/pglite": "0.5.10",
|
|
146
|
+
"@electric-sql/pglite-pg_textsearch": "0.0.12",
|
|
147
|
+
"@electric-sql/pglite-pgmq": "0.0.8",
|
|
148
|
+
"@electric-sql/pglite-pgvector": "0.0.11",
|
|
149
|
+
"@electric-sql/pglite-postgis": "0.2.10",
|
|
150
|
+
"@supacloud/db": "^0.17.0",
|
|
151
151
|
"exact-mirror": "1.2.6",
|
|
152
152
|
tar: "^7.5.22",
|
|
153
|
-
typebox: "1.3.
|
|
153
|
+
typebox: "1.3.37"
|
|
154
154
|
},
|
|
155
155
|
devDependencies: {
|
|
156
156
|
"@supacloud/app": "file:../app",
|
|
157
|
+
"@supacloud/contracts": "file:../contracts",
|
|
157
158
|
"@supacloud/compiler": "file:../compiler",
|
|
158
159
|
"@supacloud/elysia": "file:../elysia",
|
|
159
160
|
"@supabase/supabase-js": "^2.117.3",
|
|
160
161
|
"@types/bun": "^1.4.3",
|
|
161
|
-
effect: "4.0.
|
|
162
|
-
elysia: "2.0.0-beta.
|
|
162
|
+
effect: "4.0.5",
|
|
163
|
+
elysia: "2.0.0-beta.32",
|
|
163
164
|
jose: "^6.2.12",
|
|
164
165
|
typescript: "^7.0.2"
|
|
165
166
|
},
|
|
166
167
|
overrides: {
|
|
167
|
-
"@supacloud/db": "^0.
|
|
168
|
+
"@supacloud/db": "^0.17.0",
|
|
168
169
|
"@supacloud/app": "file:../app",
|
|
169
170
|
"@supacloud/compiler": "file:../compiler",
|
|
170
171
|
"@supacloud/elysia": "file:../elysia",
|
|
171
172
|
"@supacloud/contracts": "file:../contracts",
|
|
172
173
|
"@supacloud/commands": "file:../commands",
|
|
173
174
|
"@supacloud/delivery": "file:../delivery",
|
|
174
|
-
"@graphql-tools/utils": "^12.0.
|
|
175
|
+
"@graphql-tools/utils": "^12.0.4"
|
|
175
176
|
},
|
|
176
177
|
engines: {
|
|
177
178
|
bun: ">=1.4.3"
|
|
@@ -8362,6 +8363,8 @@ function statusForSqlState(code) {
|
|
|
8362
8363
|
switch (code) {
|
|
8363
8364
|
case "42501":
|
|
8364
8365
|
return 403;
|
|
8366
|
+
case "25006":
|
|
8367
|
+
return 405;
|
|
8365
8368
|
case "23503":
|
|
8366
8369
|
case "23505":
|
|
8367
8370
|
return 409;
|
|
@@ -8713,17 +8716,22 @@ class RestHandler {
|
|
|
8713
8716
|
return await this.runRpc(ctx, fn, call, q, prefer, wantsObject, wantsCsv, schema, method);
|
|
8714
8717
|
}
|
|
8715
8718
|
async runRpc(ctx, fn, call, q, prefer, wantsObject, wantsCsv, schema, method) {
|
|
8719
|
+
const withRpcContext = (run) => this.db.withContext(ctx, async (query) => {
|
|
8720
|
+
if (method !== "POST" || fn.volatility !== "v")
|
|
8721
|
+
await query("set transaction read only");
|
|
8722
|
+
return run(query);
|
|
8723
|
+
});
|
|
8716
8724
|
if (fn.returnType === "void" && !fn.returnsSet) {
|
|
8717
|
-
await
|
|
8725
|
+
await withRpcContext((query) => query(`select ${call}`, []));
|
|
8718
8726
|
return new Response(null, { status: 204 });
|
|
8719
8727
|
}
|
|
8720
8728
|
if (!fn.returnsSet && ["b", "d", "e"].includes(fn.returnTypType)) {
|
|
8721
|
-
const res = await
|
|
8729
|
+
const res = await withRpcContext((query) => query(`select to_json(${call}) as result`, []));
|
|
8722
8730
|
const value = res.rows[0].result;
|
|
8723
8731
|
return jsonResponse(200, value);
|
|
8724
8732
|
}
|
|
8725
8733
|
if (fn.returnsSet && ["b", "d", "e"].includes(fn.returnTypType)) {
|
|
8726
|
-
const res = await
|
|
8734
|
+
const res = await withRpcContext((query) => query(`select coalesce(json_agg(_v), '[]'::json) as body from ${call} as _t(_v)`, []));
|
|
8727
8735
|
return jsonResponse(200, res.rows[0].body);
|
|
8728
8736
|
}
|
|
8729
8737
|
const alias = "_f";
|
|
@@ -8751,7 +8759,7 @@ class RestHandler {
|
|
|
8751
8759
|
const core = `select ${exprs.join(", ")} from ${resultSource} as ${quoteIdent(alias)}${where}${order}${limitOffset}`;
|
|
8752
8760
|
const bodySql = `select coalesce(json_agg(row_to_json(_r)), '[]'::json) as body from (${core}) _r`;
|
|
8753
8761
|
const sql = wantsCount ? `with ${quoteIdent("_rpc_result")} as (select * from ${call}) select (${bodySql}) as body, (select count(*)::int from ${resultSource} as ${quoteIdent(alias)}${where}) as count` : bodySql;
|
|
8754
|
-
const { rows, count } = await
|
|
8762
|
+
const { rows, count } = await withRpcContext(async (query) => {
|
|
8755
8763
|
const res = await query(sql, []);
|
|
8756
8764
|
const rows = res.rows[0].body;
|
|
8757
8765
|
if (wantsObject && !wantsCsv)
|
|
@@ -13964,10 +13972,10 @@ function keys(row, expected) {
|
|
|
13964
13972
|
function validTarget(value) {
|
|
13965
13973
|
const target = record(value);
|
|
13966
13974
|
keys(target, ["environment", "projectRef"]);
|
|
13967
|
-
if (typeof target
|
|
13975
|
+
if (typeof target["environment"] !== "string" || !/^[a-z][a-z0-9-]{0,62}$/.test(target["environment"]) || typeof target["projectRef"] !== "string" || !/^[a-z0-9][a-z0-9_-]{0,62}$/.test(target["projectRef"])) {
|
|
13968
13976
|
throw new Error("Invalid migration binding target");
|
|
13969
13977
|
}
|
|
13970
|
-
return target;
|
|
13978
|
+
return { environment: target["environment"], projectRef: target["projectRef"] };
|
|
13971
13979
|
}
|
|
13972
13980
|
function validFile(file) {
|
|
13973
13981
|
return typeof file === "string" && /^[a-zA-Z0-9_./-]+\.sql$/.test(file) && !file.startsWith("/") && file.split("/").every((part) => part && part !== "." && part !== "..");
|
|
@@ -13975,32 +13983,37 @@ function validFile(file) {
|
|
|
13975
13983
|
function parseMigrationBindingManifest(value) {
|
|
13976
13984
|
const manifest = record(value);
|
|
13977
13985
|
keys(manifest, ["schema", "targets", "templates"]);
|
|
13978
|
-
if (manifest
|
|
13986
|
+
if (manifest["schema"] !== "supacloud.migration-bindings.v1" || !Array.isArray(manifest["targets"]) || !manifest["targets"].length || !Array.isArray(manifest["templates"])) {
|
|
13979
13987
|
throw new Error("Invalid migration binding manifest");
|
|
13980
13988
|
}
|
|
13981
|
-
const targets = manifest
|
|
13989
|
+
const targets = manifest["targets"].map(validTarget);
|
|
13982
13990
|
if (new Set(targets.map((target) => target.environment)).size !== targets.length) {
|
|
13983
13991
|
throw new Error("Duplicate migration binding environment");
|
|
13984
13992
|
}
|
|
13985
13993
|
const files = new Set;
|
|
13986
|
-
const templates = manifest
|
|
13987
|
-
const template = record(
|
|
13994
|
+
const templates = manifest["templates"].map((value) => {
|
|
13995
|
+
const template = record(value);
|
|
13988
13996
|
keys(template, ["file", "templateSha256", "parameters"]);
|
|
13989
|
-
if (!validFile(template
|
|
13997
|
+
if (!validFile(template["file"]) || files.has(template["file"]) || typeof template["templateSha256"] !== "string" || !HASH.test(template["templateSha256"]) || !Array.isArray(template["parameters"]) || !template["parameters"].length) {
|
|
13990
13998
|
throw new Error("Invalid or duplicate migration binding template");
|
|
13991
13999
|
}
|
|
13992
|
-
files.add(template
|
|
14000
|
+
files.add(template["file"]);
|
|
13993
14001
|
const placeholders = new Set;
|
|
13994
|
-
const parameters = template
|
|
13995
|
-
const parameter = record(
|
|
14002
|
+
const parameters = template["parameters"].map((value) => {
|
|
14003
|
+
const parameter = record(value);
|
|
13996
14004
|
keys(parameter, ["placeholder", "variable", "type", "occurrences"]);
|
|
13997
|
-
if (typeof parameter
|
|
14005
|
+
if (typeof parameter["placeholder"] !== "string" || !/^__[A-Z][A-Z0-9_]*__$/.test(parameter["placeholder"]) || placeholders.has(parameter["placeholder"]) || typeof parameter["variable"] !== "string" || !/^[A-Z][A-Z0-9_]*$/.test(parameter["variable"]) || parameter["type"] !== "uuid" && parameter["type"] !== "https-url" && parameter["type"] !== "resource-name" || typeof parameter["occurrences"] !== "number" || !Number.isSafeInteger(parameter["occurrences"]) || parameter["occurrences"] < 1) {
|
|
13998
14006
|
throw new Error("Invalid or duplicate migration binding parameter");
|
|
13999
14007
|
}
|
|
14000
|
-
placeholders.add(parameter
|
|
14001
|
-
return
|
|
14008
|
+
placeholders.add(parameter["placeholder"]);
|
|
14009
|
+
return {
|
|
14010
|
+
placeholder: parameter["placeholder"],
|
|
14011
|
+
variable: parameter["variable"],
|
|
14012
|
+
type: parameter["type"],
|
|
14013
|
+
occurrences: parameter["occurrences"]
|
|
14014
|
+
};
|
|
14002
14015
|
});
|
|
14003
|
-
return { file: template
|
|
14016
|
+
return { file: template["file"], templateSha256: template["templateSha256"], parameters };
|
|
14004
14017
|
});
|
|
14005
14018
|
return { schema: "supacloud.migration-bindings.v1", targets, templates };
|
|
14006
14019
|
}
|
|
@@ -14088,6 +14101,194 @@ function renderMigrationBindings(options) {
|
|
|
14088
14101
|
}
|
|
14089
14102
|
};
|
|
14090
14103
|
}
|
|
14104
|
+
var COMMAND_PERSISTENCE_SQL = `
|
|
14105
|
+
DO $runtime$
|
|
14106
|
+
BEGIN
|
|
14107
|
+
IF to_regprocedure('supacloud_workflows.start_run(uuid,text,text,text,jsonb,integer)') IS NULL
|
|
14108
|
+
OR to_regprocedure('supacloud_commands.status(jsonb)') IS NULL THEN
|
|
14109
|
+
RAISE EXCEPTION 'Install the SupaCloud Commands/Workflow/PGMQ runtime before command persistence';
|
|
14110
|
+
END IF;
|
|
14111
|
+
END
|
|
14112
|
+
$runtime$;
|
|
14113
|
+
CREATE SCHEMA IF NOT EXISTS supacloud_commands;
|
|
14114
|
+
REVOKE ALL ON SCHEMA supacloud_commands FROM PUBLIC;
|
|
14115
|
+
CREATE TABLE IF NOT EXISTS supacloud_commands.execution_receipts (
|
|
14116
|
+
tenant_id text NOT NULL,
|
|
14117
|
+
actor_id text NOT NULL,
|
|
14118
|
+
command text NOT NULL,
|
|
14119
|
+
operation_key text NOT NULL,
|
|
14120
|
+
kind text NOT NULL CHECK (kind IN ('transactional', 'external')),
|
|
14121
|
+
input_fingerprint text NOT NULL CHECK (input_fingerprint ~ '^[a-f0-9]{64}$'),
|
|
14122
|
+
input_payload text,
|
|
14123
|
+
dispatch_key uuid NOT NULL DEFAULT gen_random_uuid() UNIQUE,
|
|
14124
|
+
status text NOT NULL CHECK (status IN ('pending', 'unknown', 'confirmed', 'rejected')),
|
|
14125
|
+
audit_state text NOT NULL CHECK (audit_state IN ('pending', 'complete')),
|
|
14126
|
+
result jsonb,
|
|
14127
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
14128
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
14129
|
+
PRIMARY KEY (tenant_id, actor_id, command, operation_key),
|
|
14130
|
+
CHECK ((status = 'confirmed') = (result IS NOT NULL)),
|
|
14131
|
+
CONSTRAINT execution_receipts_audit_terminal_check CHECK (
|
|
14132
|
+
(audit_state <> 'complete' OR status IN ('confirmed', 'rejected'))
|
|
14133
|
+
AND (status <> 'rejected' OR audit_state = 'complete')),
|
|
14134
|
+
CONSTRAINT execution_receipts_transactional_check CHECK (
|
|
14135
|
+
kind <> 'transactional' OR (status = 'confirmed' AND audit_state = 'complete'))
|
|
14136
|
+
);
|
|
14137
|
+
CREATE TABLE IF NOT EXISTS supacloud_commands.execution_audit (
|
|
14138
|
+
tenant_id text NOT NULL,
|
|
14139
|
+
actor_id text NOT NULL,
|
|
14140
|
+
command text NOT NULL,
|
|
14141
|
+
operation_key text NOT NULL,
|
|
14142
|
+
event text NOT NULL,
|
|
14143
|
+
details jsonb NOT NULL,
|
|
14144
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
14145
|
+
PRIMARY KEY (tenant_id, actor_id, command, operation_key),
|
|
14146
|
+
FOREIGN KEY (tenant_id, actor_id, command, operation_key)
|
|
14147
|
+
REFERENCES supacloud_commands.execution_receipts (tenant_id, actor_id, command, operation_key)
|
|
14148
|
+
);
|
|
14149
|
+
REVOKE ALL ON supacloud_commands.execution_receipts, supacloud_commands.execution_audit FROM PUBLIC;
|
|
14150
|
+
CREATE OR REPLACE FUNCTION supacloud_commands.protect_terminal_execution()
|
|
14151
|
+
RETURNS trigger LANGUAGE plpgsql SET search_path = '' AS $terminal$
|
|
14152
|
+
BEGIN
|
|
14153
|
+
IF OLD.status IN ('confirmed','rejected') AND (
|
|
14154
|
+
NEW.status IS DISTINCT FROM OLD.status OR NEW.result IS DISTINCT FROM OLD.result
|
|
14155
|
+
OR (OLD.audit_state = 'complete' AND NEW.audit_state IS DISTINCT FROM OLD.audit_state)) THEN
|
|
14156
|
+
RAISE EXCEPTION 'SUPACLOUD_COMMAND_TERMINAL_RECEIPT';
|
|
14157
|
+
END IF;
|
|
14158
|
+
RETURN NEW;
|
|
14159
|
+
END
|
|
14160
|
+
$terminal$;
|
|
14161
|
+
CREATE OR REPLACE TRIGGER execution_terminal_guard
|
|
14162
|
+
BEFORE UPDATE ON supacloud_commands.execution_receipts
|
|
14163
|
+
FOR EACH ROW EXECUTE FUNCTION supacloud_commands.protect_terminal_execution();
|
|
14164
|
+
|
|
14165
|
+
CREATE OR REPLACE FUNCTION supacloud_commands.require_rejection_audit()
|
|
14166
|
+
RETURNS trigger LANGUAGE plpgsql SET search_path = '' AS $rejection$
|
|
14167
|
+
BEGIN
|
|
14168
|
+
IF NEW.status = 'rejected' AND NOT EXISTS (
|
|
14169
|
+
SELECT FROM supacloud_commands.execution_audit a
|
|
14170
|
+
WHERE a.tenant_id=NEW.tenant_id AND a.actor_id=NEW.actor_id
|
|
14171
|
+
AND a.command=NEW.command AND a.operation_key=NEW.operation_key
|
|
14172
|
+
) THEN
|
|
14173
|
+
RAISE EXCEPTION 'SUPACLOUD_COMMAND_REJECTION_AUDIT_REQUIRED';
|
|
14174
|
+
END IF;
|
|
14175
|
+
RETURN NEW;
|
|
14176
|
+
END
|
|
14177
|
+
$rejection$;
|
|
14178
|
+
DROP TRIGGER IF EXISTS execution_rejection_audit ON supacloud_commands.execution_receipts;
|
|
14179
|
+
CREATE CONSTRAINT TRIGGER execution_rejection_audit
|
|
14180
|
+
AFTER INSERT OR UPDATE ON supacloud_commands.execution_receipts
|
|
14181
|
+
DEFERRABLE INITIALLY DEFERRED
|
|
14182
|
+
FOR EACH ROW WHEN (NEW.status = 'rejected')
|
|
14183
|
+
EXECUTE FUNCTION supacloud_commands.require_rejection_audit();
|
|
14184
|
+
CREATE OR REPLACE FUNCTION supacloud_commands.enqueue_execution_recovery()
|
|
14185
|
+
RETURNS trigger LANGUAGE plpgsql SET search_path = '' AS $recovery$
|
|
14186
|
+
DECLARE submission jsonb;
|
|
14187
|
+
BEGIN
|
|
14188
|
+
PERFORM pg_advisory_xact_lock(hashtextextended(NEW.dispatch_key::text,0));
|
|
14189
|
+
SELECT supacloud_commands.snapshot(NEW.dispatch_key,false) INTO submission;
|
|
14190
|
+
IF submission IS NOT NULL THEN
|
|
14191
|
+
IF submission->>'commandType' IS DISTINCT FROM NEW.command
|
|
14192
|
+
OR submission->>'actorId' IS DISTINCT FROM NEW.actor_id
|
|
14193
|
+
OR submission->>'tenantId' IS DISTINCT FROM NEW.tenant_id
|
|
14194
|
+
OR NEW.operation_key <> NEW.dispatch_key::text THEN
|
|
14195
|
+
RAISE EXCEPTION 'SUPACLOUD_COMMAND_IDENTITY_CONFLICT';
|
|
14196
|
+
END IF;
|
|
14197
|
+
RETURN NEW;
|
|
14198
|
+
END IF;
|
|
14199
|
+
IF NEW.kind = 'external' AND NEW.status <> 'rejected'
|
|
14200
|
+
AND (NEW.status <> 'confirmed' OR NEW.audit_state <> 'complete') THEN
|
|
14201
|
+
PERFORM supacloud_workflows.start_run(
|
|
14202
|
+
NEW.dispatch_key, 'supacloud.command.reconcile', '1', 'reconcile',
|
|
14203
|
+
jsonb_build_object('commandId', NEW.dispatch_key, 'tenantId', NEW.tenant_id,
|
|
14204
|
+
'actorId', NEW.actor_id, 'command', NEW.command, 'operationId', NEW.operation_key),
|
|
14205
|
+
20
|
|
14206
|
+
);
|
|
14207
|
+
END IF;
|
|
14208
|
+
RETURN NEW;
|
|
14209
|
+
END
|
|
14210
|
+
$recovery$;
|
|
14211
|
+
CREATE OR REPLACE TRIGGER execution_recovery_enqueue
|
|
14212
|
+
AFTER INSERT ON supacloud_commands.execution_receipts
|
|
14213
|
+
FOR EACH ROW EXECUTE FUNCTION supacloud_commands.enqueue_execution_recovery();
|
|
14214
|
+
|
|
14215
|
+
CREATE OR REPLACE FUNCTION supacloud_commands.execution_status(request jsonb)
|
|
14216
|
+
RETURNS jsonb LANGUAGE sql STABLE SECURITY DEFINER SET search_path = '' AS $status$
|
|
14217
|
+
SELECT jsonb_build_object('kind','execution','commandId',r.dispatch_key,
|
|
14218
|
+
'execution',jsonb_build_object('tenantId',r.tenant_id,'actorId',r.actor_id,
|
|
14219
|
+
'command',r.command,'operationId',r.operation_key,'dispatchKey',r.dispatch_key,
|
|
14220
|
+
'status',r.status,'audit',r.audit_state)
|
|
14221
|
+
|| CASE WHEN r.status='confirmed' THEN jsonb_build_object('result',r.result) ELSE '{}'::jsonb END,
|
|
14222
|
+
'workflow',CASE WHEN w.id IS NULL THEN NULL ELSE jsonb_build_object('runId',w.id,'status',w.status) END)
|
|
14223
|
+
FROM supacloud_commands.execution_receipts r
|
|
14224
|
+
LEFT JOIN supacloud_workflows.runs w ON w.id=r.dispatch_key
|
|
14225
|
+
WHERE CASE WHEN request ? 'commandId' THEN r.dispatch_key::text=request->>'commandId'
|
|
14226
|
+
ELSE r.tenant_id=request->>'tenantId' AND r.actor_id=request->>'actorId'
|
|
14227
|
+
AND r.command=request->>'command' AND r.operation_key=request->>'operationId' END
|
|
14228
|
+
$status$;
|
|
14229
|
+
REVOKE ALL ON FUNCTION supacloud_commands.execution_status(jsonb) FROM PUBLIC;
|
|
14230
|
+
`;
|
|
14231
|
+
var COMMAND_PERSISTENCE_UPGRADE_SQL = `
|
|
14232
|
+
ALTER TABLE supacloud_commands.execution_receipts
|
|
14233
|
+
ADD COLUMN IF NOT EXISTS input_fingerprint text,
|
|
14234
|
+
ADD COLUMN IF NOT EXISTS input_payload text;
|
|
14235
|
+
DO $migration$
|
|
14236
|
+
BEGIN
|
|
14237
|
+
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='supacloud_commands'
|
|
14238
|
+
AND table_name='execution_receipts' AND column_name='input_key') THEN
|
|
14239
|
+
EXECUTE 'UPDATE supacloud_commands.execution_receipts
|
|
14240
|
+
SET input_fingerprint=encode(sha256(convert_to(input_key,''UTF8'')),''hex''),input_payload=input_key
|
|
14241
|
+
WHERE input_fingerprint IS NULL';
|
|
14242
|
+
ALTER TABLE supacloud_commands.execution_receipts DROP COLUMN input_key;
|
|
14243
|
+
END IF;
|
|
14244
|
+
END
|
|
14245
|
+
$migration$;
|
|
14246
|
+
ALTER TABLE supacloud_commands.execution_receipts ALTER COLUMN input_fingerprint SET NOT NULL;
|
|
14247
|
+
ALTER TABLE supacloud_commands.execution_receipts
|
|
14248
|
+
DROP CONSTRAINT IF EXISTS execution_receipts_status_check,
|
|
14249
|
+
DROP CONSTRAINT IF EXISTS execution_receipts_check1,
|
|
14250
|
+
DROP CONSTRAINT IF EXISTS execution_receipts_transactional_check,
|
|
14251
|
+
DROP CONSTRAINT IF EXISTS execution_receipts_audit_terminal_check;
|
|
14252
|
+
ALTER TABLE supacloud_commands.execution_receipts
|
|
14253
|
+
ADD CONSTRAINT execution_receipts_status_check CHECK (status IN ('pending','unknown','confirmed','rejected')),
|
|
14254
|
+
ADD CONSTRAINT execution_receipts_transactional_check CHECK (
|
|
14255
|
+
kind <> 'transactional' OR (status = 'confirmed' AND audit_state = 'complete')),
|
|
14256
|
+
ADD CONSTRAINT execution_receipts_audit_terminal_check CHECK (
|
|
14257
|
+
(audit_state <> 'complete' OR status IN ('confirmed','rejected'))
|
|
14258
|
+
AND (status <> 'rejected' OR audit_state = 'complete'));
|
|
14259
|
+
DO $constraint$
|
|
14260
|
+
BEGIN
|
|
14261
|
+
IF NOT EXISTS (SELECT 1 FROM pg_constraint
|
|
14262
|
+
WHERE conrelid='supacloud_commands.execution_receipts'::regclass
|
|
14263
|
+
AND conname='execution_receipts_input_fingerprint_check') THEN
|
|
14264
|
+
ALTER TABLE supacloud_commands.execution_receipts
|
|
14265
|
+
ADD CONSTRAINT execution_receipts_input_fingerprint_check CHECK (input_fingerprint ~ '^[a-f0-9]{64}$');
|
|
14266
|
+
END IF;
|
|
14267
|
+
END
|
|
14268
|
+
$constraint$;
|
|
14269
|
+
${COMMAND_PERSISTENCE_SQL}
|
|
14270
|
+
DO $backfill$
|
|
14271
|
+
DECLARE r record;
|
|
14272
|
+
BEGIN
|
|
14273
|
+
FOR r IN SELECT * FROM supacloud_commands.execution_receipts
|
|
14274
|
+
WHERE kind='external' AND status<>'rejected' AND (status<>'confirmed' OR audit_state<>'complete')
|
|
14275
|
+
AND NOT EXISTS(SELECT FROM supacloud_workflows.runs w WHERE w.id=dispatch_key)
|
|
14276
|
+
LOOP
|
|
14277
|
+
PERFORM supacloud_workflows.start_run(
|
|
14278
|
+
r.dispatch_key,'supacloud.command.reconcile','1','reconcile',
|
|
14279
|
+
jsonb_build_object('commandId',r.dispatch_key,'tenantId',r.tenant_id,
|
|
14280
|
+
'actorId',r.actor_id,'command',r.command,'operationId',r.operation_key),20
|
|
14281
|
+
);
|
|
14282
|
+
END LOOP;
|
|
14283
|
+
END
|
|
14284
|
+
$backfill$;
|
|
14285
|
+
DROP INDEX IF EXISTS supacloud_commands.execution_pending_recovery;
|
|
14286
|
+
ALTER TABLE supacloud_commands.execution_receipts
|
|
14287
|
+
DROP COLUMN IF EXISTS lease_id,
|
|
14288
|
+
DROP COLUMN IF EXISTS lease_until,
|
|
14289
|
+
DROP COLUMN IF EXISTS next_attempt_at,
|
|
14290
|
+
DROP COLUMN IF EXISTS recovery_attempts;
|
|
14291
|
+
`;
|
|
14091
14292
|
|
|
14092
14293
|
// src/runtime/node/project.ts
|
|
14093
14294
|
async function loadSupabaseProject(projectDir, seed = {}) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/runtime/rest/errors.ts"],"names":[],"mappings":"AAQA,0EAA0E;AAC1E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/runtime/rest/errors.ts"],"names":[],"mappings":"AAQA,0EAA0E;AAC1E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoBtD;AAUD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,GAAG,QAAQ,CAkBpD;AAED,sDAAsD;AACtD,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,EACb,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACnC,QAAQ,CAKV"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/runtime/rest/handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAA4B,KAAK,QAAQ,
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/runtime/rest/handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAA4B,KAAK,QAAQ,EAAmC,MAAM,mBAAmB,CAAA;AAC5G,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,aAAa,CAAA;AAyF3D,oFAAoF;AACpF,qBAAa,WAAW;IAOpB,OAAO,CAAC,EAAE;IANZ,8FAA8F;IAC9F,OAAO,CAAC,cAAc,CAAU;IAChC,8EAA8E;IAC9E,OAAO,CAAC,OAAO,CAAC,CAAQ;gBAGd,EAAE,EAAE,QAAQ,EACpB,IAAI,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE;IAMxD,4FAA4F;IACtF,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC;YAmC9D,WAAW;YAsHX,WAAW;IAgDzB,OAAO,CAAC,YAAY;YAqCN,SAAS;YAqET,MAAM;CA4FrB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supacloud/lite",
|
|
3
|
-
"version": "0.28.
|
|
3
|
+
"version": "0.28.3",
|
|
4
4
|
"description": "Bun-native, single-project Supabase-compatible backend powered by PGlite or native PostgreSQL",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -54,36 +54,37 @@
|
|
|
54
54
|
"typecheck": "bun x tsc --noEmit -p tsconfig.json"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@electric-sql/pglite": "0.5.
|
|
58
|
-
"@electric-sql/pglite-pg_textsearch": "0.0.
|
|
59
|
-
"@electric-sql/pglite-pgmq": "0.0.
|
|
60
|
-
"@electric-sql/pglite-pgvector": "0.0.
|
|
61
|
-
"@electric-sql/pglite-postgis": "0.2.
|
|
62
|
-
"@supacloud/db": "^0.
|
|
57
|
+
"@electric-sql/pglite": "0.5.10",
|
|
58
|
+
"@electric-sql/pglite-pg_textsearch": "0.0.12",
|
|
59
|
+
"@electric-sql/pglite-pgmq": "0.0.8",
|
|
60
|
+
"@electric-sql/pglite-pgvector": "0.0.11",
|
|
61
|
+
"@electric-sql/pglite-postgis": "0.2.10",
|
|
62
|
+
"@supacloud/db": "^0.17.0",
|
|
63
63
|
"exact-mirror": "1.2.6",
|
|
64
64
|
"tar": "^7.5.22",
|
|
65
|
-
"typebox": "1.3.
|
|
65
|
+
"typebox": "1.3.37"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@supacloud/app": "file:../app",
|
|
69
|
+
"@supacloud/contracts": "file:../contracts",
|
|
69
70
|
"@supacloud/compiler": "file:../compiler",
|
|
70
71
|
"@supacloud/elysia": "file:../elysia",
|
|
71
72
|
"@supabase/supabase-js": "^2.117.3",
|
|
72
73
|
"@types/bun": "^1.4.3",
|
|
73
|
-
"effect": "4.0.
|
|
74
|
-
"elysia": "2.0.0-beta.
|
|
74
|
+
"effect": "4.0.5",
|
|
75
|
+
"elysia": "2.0.0-beta.32",
|
|
75
76
|
"jose": "^6.2.12",
|
|
76
77
|
"typescript": "^7.0.2"
|
|
77
78
|
},
|
|
78
79
|
"overrides": {
|
|
79
|
-
"@supacloud/db": "^0.
|
|
80
|
+
"@supacloud/db": "^0.17.0",
|
|
80
81
|
"@supacloud/app": "file:../app",
|
|
81
82
|
"@supacloud/compiler": "file:../compiler",
|
|
82
83
|
"@supacloud/elysia": "file:../elysia",
|
|
83
84
|
"@supacloud/contracts": "file:../contracts",
|
|
84
85
|
"@supacloud/commands": "file:../commands",
|
|
85
86
|
"@supacloud/delivery": "file:../delivery",
|
|
86
|
-
"@graphql-tools/utils": "^12.0.
|
|
87
|
+
"@graphql-tools/utils": "^12.0.4"
|
|
87
88
|
},
|
|
88
89
|
"engines": {
|
|
89
90
|
"bun": ">=1.4.3"
|