@supacloud/lite 0.28.0 → 0.28.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.
- package/CHANGELOG.md +14 -0
- package/RELEASING.md +9 -0
- package/dist/cli.js +37 -23
- package/dist/index.js +37 -23
- package/dist/runtime/rest/errors.d.ts.map +1 -1
- package/dist/runtime/rest/handler.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.28.2](https://github.com/vibeunion/supacloud/compare/supacloud-lite-v0.28.1...supacloud-lite-v0.28.2) (2026-10-15)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **lite:** enforce RPC transaction access modes ([#1936](https://github.com/vibeunion/supacloud/issues/1936)) ([fb90dcc](https://github.com/vibeunion/supacloud/commit/fb90dcc43ed49a45656e1877493eaa641ef494da))
|
|
9
|
+
|
|
10
|
+
## [0.28.1](https://github.com/vibeunion/supacloud/compare/supacloud-lite-v0.28.0...supacloud-lite-v0.28.1) (2026-10-15)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **lite:** roll back rejected singular REST writes ([#1925](https://github.com/vibeunion/supacloud/issues/1925)) ([0af235d](https://github.com/vibeunion/supacloud/commit/0af235d55a3619ffd150e4b16751a27a32d1f2e0))
|
|
16
|
+
|
|
3
17
|
## [0.28.0](https://github.com/vibeunion/supacloud/compare/supacloud-lite-v0.27.1...supacloud-lite-v0.28.0) (2026-10-14)
|
|
4
18
|
|
|
5
19
|
|
package/RELEASING.md
CHANGED
|
@@ -30,6 +30,15 @@ bun run build:standalone:host
|
|
|
30
30
|
bun run test:standalone
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
针对本地 Auth 的聚焦回归,只构建 host 产物并运行单个脚本的 Auth 入口:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
bun run build:standalone:host
|
|
37
|
+
bun run scripts/standalone-smoke.ts --auth-only
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
该入口在不含 Bun/Node/npm 的子进程 `PATH` 下启动二进制,通过真实 HTTP 和 `supabase-js` 验证注册、密码登录、刷新轮换、退出撤销,以及进程重启后的会话恢复、刷新与撤销保留。成功标记为 `standalone-auth-smoke-ok`。可用 `SUPACLOUD_LITE_STANDALONE_BINARY` 指定待验收产物。它不执行 Functions、扩展、升级、快照或恢复,不能替代完整发布验收;默认完整 smoke 也会在升级和快照恢复阶段验证同一 Auth 状态。
|
|
41
|
+
|
|
33
42
|
Release workflow 为 Linux x64 baseline/arm64、macOS x64/arm64 和 Windows x64 baseline 构建独立产物。Linux x64 产物必须在发布 runner 上完成真实黑盒测试;其他交叉编译产物在具备对应原生 runner 前只视为已构建,不能替代平台验收。
|
|
34
43
|
|
|
35
44
|
Lite release 会同时上传 `SHA256SUMS`、项目自身的 `SUPACLOUD-LITE-APACHE-2.0.txt`、`NOTICE`、`THIRD_PARTY_NOTICES.md` 和 `LICENSES/*.txt`,并为发布资产生成 provenance。正式对外分发 macOS 或 Windows 产物前,还应按项目发布策略完成代码签名;构建成功不代表已经签名或公证。
|
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.2",
|
|
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",
|
|
@@ -8368,6 +8368,8 @@ function statusForSqlState(code) {
|
|
|
8368
8368
|
switch (code) {
|
|
8369
8369
|
case "42501":
|
|
8370
8370
|
return 403;
|
|
8371
|
+
case "25006":
|
|
8372
|
+
return 405;
|
|
8371
8373
|
case "23503":
|
|
8372
8374
|
case "23505":
|
|
8373
8375
|
return 409;
|
|
@@ -8620,7 +8622,10 @@ class RestHandler {
|
|
|
8620
8622
|
const { rows, affected } = await this.db.withContext(ctx, async (query) => {
|
|
8621
8623
|
const res = await query(built.sql, built.params);
|
|
8622
8624
|
if (opts.queryReturning) {
|
|
8623
|
-
|
|
8625
|
+
const rows = res.rows[0].body;
|
|
8626
|
+
if (opts.wantsObject && !opts.wantsCsv)
|
|
8627
|
+
assertObjectCardinality(rows);
|
|
8628
|
+
return { rows, affected: null };
|
|
8624
8629
|
}
|
|
8625
8630
|
return { rows: null, affected: res.affectedRows ?? 0 };
|
|
8626
8631
|
});
|
|
@@ -8652,14 +8657,7 @@ class RestHandler {
|
|
|
8652
8657
|
return new Response(opts.head ? null : rowsToCsv(rows), { status: opts.status, headers });
|
|
8653
8658
|
}
|
|
8654
8659
|
if (opts.wantsObject) {
|
|
8655
|
-
|
|
8656
|
-
return jsonResponse(406, {
|
|
8657
|
-
code: "PGRST116",
|
|
8658
|
-
message: "JSON object requested, multiple (or no) rows returned",
|
|
8659
|
-
details: `The result contains ${rows.length} rows`,
|
|
8660
|
-
hint: null
|
|
8661
|
-
});
|
|
8662
|
-
}
|
|
8660
|
+
assertObjectCardinality(rows);
|
|
8663
8661
|
headers["content-type"] = `${OBJECT_MEDIA}; charset=utf-8`;
|
|
8664
8662
|
return new Response(opts.head ? null : JSON.stringify(rows[0]), {
|
|
8665
8663
|
status: opts.status,
|
|
@@ -8723,17 +8721,22 @@ class RestHandler {
|
|
|
8723
8721
|
return await this.runRpc(ctx, fn, call, q, prefer, wantsObject, wantsCsv, schema, method);
|
|
8724
8722
|
}
|
|
8725
8723
|
async runRpc(ctx, fn, call, q, prefer, wantsObject, wantsCsv, schema, method) {
|
|
8724
|
+
const withRpcContext = (run) => this.db.withContext(ctx, async (query) => {
|
|
8725
|
+
if (method !== "POST" || fn.volatility !== "v")
|
|
8726
|
+
await query("set transaction read only");
|
|
8727
|
+
return run(query);
|
|
8728
|
+
});
|
|
8726
8729
|
if (fn.returnType === "void" && !fn.returnsSet) {
|
|
8727
|
-
await
|
|
8730
|
+
await withRpcContext((query) => query(`select ${call}`, []));
|
|
8728
8731
|
return new Response(null, { status: 204 });
|
|
8729
8732
|
}
|
|
8730
8733
|
if (!fn.returnsSet && ["b", "d", "e"].includes(fn.returnTypType)) {
|
|
8731
|
-
const res = await
|
|
8734
|
+
const res = await withRpcContext((query) => query(`select to_json(${call}) as result`, []));
|
|
8732
8735
|
const value = res.rows[0].result;
|
|
8733
8736
|
return jsonResponse(200, value);
|
|
8734
8737
|
}
|
|
8735
8738
|
if (fn.returnsSet && ["b", "d", "e"].includes(fn.returnTypType)) {
|
|
8736
|
-
const res = await
|
|
8739
|
+
const res = await withRpcContext((query) => query(`select coalesce(json_agg(_v), '[]'::json) as body from ${call} as _t(_v)`, []));
|
|
8737
8740
|
return jsonResponse(200, res.rows[0].body);
|
|
8738
8741
|
}
|
|
8739
8742
|
const alias = "_f";
|
|
@@ -8756,17 +8759,18 @@ class RestHandler {
|
|
|
8756
8759
|
const where = conds.length > 0 ? ` where ${conds.join(" and ")}` : "";
|
|
8757
8760
|
const order = builder.renderOrder([], alias);
|
|
8758
8761
|
const limitOffset = builder.renderLimitOffset("");
|
|
8759
|
-
const
|
|
8760
|
-
const
|
|
8761
|
-
const
|
|
8762
|
-
const
|
|
8762
|
+
const wantsCount = prefer.count !== undefined;
|
|
8763
|
+
const resultSource = wantsCount ? quoteIdent("_rpc_result") : call;
|
|
8764
|
+
const core = `select ${exprs.join(", ")} from ${resultSource} as ${quoteIdent(alias)}${where}${order}${limitOffset}`;
|
|
8765
|
+
const bodySql = `select coalesce(json_agg(row_to_json(_r)), '[]'::json) as body from (${core}) _r`;
|
|
8766
|
+
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;
|
|
8767
|
+
const { rows, count } = await withRpcContext(async (query) => {
|
|
8763
8768
|
const res = await query(sql, []);
|
|
8764
|
-
|
|
8765
|
-
if (
|
|
8766
|
-
|
|
8767
|
-
|
|
8768
|
-
}
|
|
8769
|
-
return { rows: res.rows[0].body, count };
|
|
8769
|
+
const rows = res.rows[0].body;
|
|
8770
|
+
if (wantsObject && !wantsCsv)
|
|
8771
|
+
assertObjectCardinality(rows);
|
|
8772
|
+
const count = wantsCount ? res.rows[0].count : null;
|
|
8773
|
+
return { rows, count };
|
|
8770
8774
|
});
|
|
8771
8775
|
return this.dataResponse(rows, {
|
|
8772
8776
|
status: 200,
|
|
@@ -8778,6 +8782,16 @@ class RestHandler {
|
|
|
8778
8782
|
});
|
|
8779
8783
|
}
|
|
8780
8784
|
}
|
|
8785
|
+
function assertObjectCardinality(rows) {
|
|
8786
|
+
if (rows.length !== 1) {
|
|
8787
|
+
throw new ApiError(406, {
|
|
8788
|
+
code: "PGRST116",
|
|
8789
|
+
message: "JSON object requested, multiple (or no) rows returned",
|
|
8790
|
+
details: `The result contains ${rows.length} rows`,
|
|
8791
|
+
hint: null
|
|
8792
|
+
});
|
|
8793
|
+
}
|
|
8794
|
+
}
|
|
8781
8795
|
function pickOverload(fns, providedNames) {
|
|
8782
8796
|
const provided = new Set(providedNames);
|
|
8783
8797
|
let best = null;
|
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.2",
|
|
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",
|
|
@@ -8362,6 +8362,8 @@ function statusForSqlState(code) {
|
|
|
8362
8362
|
switch (code) {
|
|
8363
8363
|
case "42501":
|
|
8364
8364
|
return 403;
|
|
8365
|
+
case "25006":
|
|
8366
|
+
return 405;
|
|
8365
8367
|
case "23503":
|
|
8366
8368
|
case "23505":
|
|
8367
8369
|
return 409;
|
|
@@ -8614,7 +8616,10 @@ class RestHandler {
|
|
|
8614
8616
|
const { rows, affected } = await this.db.withContext(ctx, async (query) => {
|
|
8615
8617
|
const res = await query(built.sql, built.params);
|
|
8616
8618
|
if (opts.queryReturning) {
|
|
8617
|
-
|
|
8619
|
+
const rows = res.rows[0].body;
|
|
8620
|
+
if (opts.wantsObject && !opts.wantsCsv)
|
|
8621
|
+
assertObjectCardinality(rows);
|
|
8622
|
+
return { rows, affected: null };
|
|
8618
8623
|
}
|
|
8619
8624
|
return { rows: null, affected: res.affectedRows ?? 0 };
|
|
8620
8625
|
});
|
|
@@ -8646,14 +8651,7 @@ class RestHandler {
|
|
|
8646
8651
|
return new Response(opts.head ? null : rowsToCsv(rows), { status: opts.status, headers });
|
|
8647
8652
|
}
|
|
8648
8653
|
if (opts.wantsObject) {
|
|
8649
|
-
|
|
8650
|
-
return jsonResponse(406, {
|
|
8651
|
-
code: "PGRST116",
|
|
8652
|
-
message: "JSON object requested, multiple (or no) rows returned",
|
|
8653
|
-
details: `The result contains ${rows.length} rows`,
|
|
8654
|
-
hint: null
|
|
8655
|
-
});
|
|
8656
|
-
}
|
|
8654
|
+
assertObjectCardinality(rows);
|
|
8657
8655
|
headers["content-type"] = `${OBJECT_MEDIA}; charset=utf-8`;
|
|
8658
8656
|
return new Response(opts.head ? null : JSON.stringify(rows[0]), {
|
|
8659
8657
|
status: opts.status,
|
|
@@ -8717,17 +8715,22 @@ class RestHandler {
|
|
|
8717
8715
|
return await this.runRpc(ctx, fn, call, q, prefer, wantsObject, wantsCsv, schema, method);
|
|
8718
8716
|
}
|
|
8719
8717
|
async runRpc(ctx, fn, call, q, prefer, wantsObject, wantsCsv, schema, method) {
|
|
8718
|
+
const withRpcContext = (run) => this.db.withContext(ctx, async (query) => {
|
|
8719
|
+
if (method !== "POST" || fn.volatility !== "v")
|
|
8720
|
+
await query("set transaction read only");
|
|
8721
|
+
return run(query);
|
|
8722
|
+
});
|
|
8720
8723
|
if (fn.returnType === "void" && !fn.returnsSet) {
|
|
8721
|
-
await
|
|
8724
|
+
await withRpcContext((query) => query(`select ${call}`, []));
|
|
8722
8725
|
return new Response(null, { status: 204 });
|
|
8723
8726
|
}
|
|
8724
8727
|
if (!fn.returnsSet && ["b", "d", "e"].includes(fn.returnTypType)) {
|
|
8725
|
-
const res = await
|
|
8728
|
+
const res = await withRpcContext((query) => query(`select to_json(${call}) as result`, []));
|
|
8726
8729
|
const value = res.rows[0].result;
|
|
8727
8730
|
return jsonResponse(200, value);
|
|
8728
8731
|
}
|
|
8729
8732
|
if (fn.returnsSet && ["b", "d", "e"].includes(fn.returnTypType)) {
|
|
8730
|
-
const res = await
|
|
8733
|
+
const res = await withRpcContext((query) => query(`select coalesce(json_agg(_v), '[]'::json) as body from ${call} as _t(_v)`, []));
|
|
8731
8734
|
return jsonResponse(200, res.rows[0].body);
|
|
8732
8735
|
}
|
|
8733
8736
|
const alias = "_f";
|
|
@@ -8750,17 +8753,18 @@ class RestHandler {
|
|
|
8750
8753
|
const where = conds.length > 0 ? ` where ${conds.join(" and ")}` : "";
|
|
8751
8754
|
const order = builder.renderOrder([], alias);
|
|
8752
8755
|
const limitOffset = builder.renderLimitOffset("");
|
|
8753
|
-
const
|
|
8754
|
-
const
|
|
8755
|
-
const
|
|
8756
|
-
const
|
|
8756
|
+
const wantsCount = prefer.count !== undefined;
|
|
8757
|
+
const resultSource = wantsCount ? quoteIdent("_rpc_result") : call;
|
|
8758
|
+
const core = `select ${exprs.join(", ")} from ${resultSource} as ${quoteIdent(alias)}${where}${order}${limitOffset}`;
|
|
8759
|
+
const bodySql = `select coalesce(json_agg(row_to_json(_r)), '[]'::json) as body from (${core}) _r`;
|
|
8760
|
+
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;
|
|
8761
|
+
const { rows, count } = await withRpcContext(async (query) => {
|
|
8757
8762
|
const res = await query(sql, []);
|
|
8758
|
-
|
|
8759
|
-
if (
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
}
|
|
8763
|
-
return { rows: res.rows[0].body, count };
|
|
8763
|
+
const rows = res.rows[0].body;
|
|
8764
|
+
if (wantsObject && !wantsCsv)
|
|
8765
|
+
assertObjectCardinality(rows);
|
|
8766
|
+
const count = wantsCount ? res.rows[0].count : null;
|
|
8767
|
+
return { rows, count };
|
|
8764
8768
|
});
|
|
8765
8769
|
return this.dataResponse(rows, {
|
|
8766
8770
|
status: 200,
|
|
@@ -8772,6 +8776,16 @@ class RestHandler {
|
|
|
8772
8776
|
});
|
|
8773
8777
|
}
|
|
8774
8778
|
}
|
|
8779
|
+
function assertObjectCardinality(rows) {
|
|
8780
|
+
if (rows.length !== 1) {
|
|
8781
|
+
throw new ApiError(406, {
|
|
8782
|
+
code: "PGRST116",
|
|
8783
|
+
message: "JSON object requested, multiple (or no) rows returned",
|
|
8784
|
+
details: `The result contains ${rows.length} rows`,
|
|
8785
|
+
hint: null
|
|
8786
|
+
});
|
|
8787
|
+
}
|
|
8788
|
+
}
|
|
8775
8789
|
function pickOverload(fns, providedNames) {
|
|
8776
8790
|
const provided = new Set(providedNames);
|
|
8777
8791
|
let best = null;
|
|
@@ -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"}
|