@supacloud/lite 0.27.1 → 0.28.1
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 +20 -0
- package/README.md +32 -0
- package/RELEASING.md +9 -0
- package/dist/cli.js +176 -42
- package/dist/index.js +50 -33
- package/dist/runtime/node/fs-driver.d.ts +2 -2
- package/dist/runtime/node/fs-driver.d.ts.map +1 -1
- package/dist/runtime/node/native/readiness.d.ts +6 -1
- package/dist/runtime/node/native/readiness.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,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.28.1](https://github.com/vibeunion/supacloud/compare/supacloud-lite-v0.28.0...supacloud-lite-v0.28.1) (2026-10-15)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **lite:** roll back rejected singular REST writes ([#1925](https://github.com/vibeunion/supacloud/issues/1925)) ([0af235d](https://github.com/vibeunion/supacloud/commit/0af235d55a3619ffd150e4b16751a27a32d1f2e0))
|
|
9
|
+
|
|
10
|
+
## [0.28.0](https://github.com/vibeunion/supacloud/compare/supacloud-lite-v0.27.1...supacloud-lite-v0.28.0) (2026-10-14)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* **lite:** add offline auth user provisioning ([#1911](https://github.com/vibeunion/supacloud/issues/1911)) ([e9c8931](https://github.com/vibeunion/supacloud/commit/e9c89319cf39d9fe2d71ceac9ab76e6856bb5d84))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* **lite:** normalize filesystem storage roots ([#1902](https://github.com/vibeunion/supacloud/issues/1902)) ([f1b5141](https://github.com/vibeunion/supacloud/commit/f1b5141c1e831e68aaf94b505909c9e4942a42df))
|
|
21
|
+
* **lite:** report effective local auth configuration in doctor ([#1907](https://github.com/vibeunion/supacloud/issues/1907)) ([c167617](https://github.com/vibeunion/supacloud/commit/c1676176bdd5a2c057114474186570b7e6d5d0dd))
|
|
22
|
+
|
|
3
23
|
## [0.27.1](https://github.com/vibeunion/supacloud/compare/supacloud-lite-v0.27.0...supacloud-lite-v0.27.1) (2026-10-14)
|
|
4
24
|
|
|
5
25
|
|
package/README.md
CHANGED
|
@@ -159,6 +159,7 @@ supacloud-lite start
|
|
|
159
159
|
supacloud-lite migrate
|
|
160
160
|
supacloud-lite status
|
|
161
161
|
supacloud-lite keys [--service-role]
|
|
162
|
+
supacloud-lite auth create-user --email <email> --password-stdin [--email-confirm] [--json]
|
|
162
163
|
supacloud-lite gen types [-o database.types.ts]
|
|
163
164
|
supacloud-lite db reset
|
|
164
165
|
supacloud-lite db diff [-f migration_name]
|
|
@@ -174,6 +175,21 @@ supacloud-lite version
|
|
|
174
175
|
|
|
175
176
|
首次初始化或 state 目录不存在/为空时,先运行 `supacloud-lite migrate`。`db reset` 只接受已经初始化且保留有效 `secrets.json` 标记的 state;它不会为 reset 创建或覆盖项目 secrets。
|
|
176
177
|
|
|
178
|
+
#### 创建本地 Auth 账号
|
|
179
|
+
|
|
180
|
+
先停止项目服务,再从受保护的文件或密码管理器管道提供密码:
|
|
181
|
+
|
|
182
|
+
```sh
|
|
183
|
+
supacloud-lite auth create-user --project-dir ./my-project \
|
|
184
|
+
--email operator@example.com --password-stdin --email-confirm --json < /secure/password.txt
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
不要将密码写进命令行参数或 shell 历史;文件应限制为当前用户可读,并按自己的凭据管理流程清理。命令只接受 stdin 的一行 UTF-8 密码(最多 4096 字节),移除末尾一个 LF/CRLF,保留空格。交互终端输入和 `--password` 参数会被拒绝。
|
|
188
|
+
|
|
189
|
+
这是离线、本地账号创建操作,不连接远程 Auth API,不执行待应用的业务迁移、seed、函数或 webhook;请先用 `migrate` 初始化应用 schema。即使关闭公开注册,也可以由本机管理员创建账号。邮箱默认未确认;只有管理员已核实邮箱时才加 `--email-confirm`。未确认账号能否登录仍受项目的邮箱确认配置控制,命令不会发送确认邮件。
|
|
190
|
+
|
|
191
|
+
命令遵守有效密码长度策略,拒绝重复邮箱(不覆盖密码)、`--memory`、禁用 Auth、外部身份模式及占用中的数据目录。输出仅含用户 ID、邮箱和确认状态,不含密码、项目密钥或登录 token。该账号使用普通 `authenticated` 角色;业务权限仍由应用自己的成员关系和 RLS 管理。
|
|
192
|
+
|
|
177
193
|
`db diff` / `db pull` 只读检查现有数据库,不会初始化 live backend 或自动应用待执行迁移;已应用历史必须与本地迁移精确一致。生成的 DDL 先在不含 seed 的临时影子数据库中重放并核对受支持的 Catalog。检测到不能完整重建的 view/function/trigger/policy、权限、RLS 或非追加 enum 变更时拒绝生成,改用官方 Supabase pg-delta;这不是完整 PostgreSQL Catalog 覆盖。
|
|
178
194
|
|
|
179
195
|
`db pull` 默认写入 `.supacloud-lite/schema-drafts`,不修改迁移账本。仅对已审核且已存在于数据库的变更使用 `--baseline`:它记录账本而不执行 DDL,提交确认后才发布到迁移目录,禁止覆盖已有文件。若失败后留下 `.sql.pending`,停止启动/迁移,保留文件并检查对应账本版本与 SQL;只有确认匹配提交后才能发布该文件。不要猜测提交结果或直接重放捕获的 DDL。库调用方在 `baseline: false` 时也应把 `migrationsDir` 指向草稿目录。恢复步骤及文件/数据库非原子边界见仓库 `docs/select-agent-backend.md`。
|
|
@@ -599,6 +615,7 @@ supacloud-lite start
|
|
|
599
615
|
supacloud-lite migrate
|
|
600
616
|
supacloud-lite status
|
|
601
617
|
supacloud-lite keys [--service-role]
|
|
618
|
+
supacloud-lite auth create-user --email <email> --password-stdin [--email-confirm] [--json]
|
|
602
619
|
supacloud-lite gen types [-o database.types.ts]
|
|
603
620
|
supacloud-lite db reset
|
|
604
621
|
supacloud-lite db diff [-f migration_name]
|
|
@@ -614,6 +631,21 @@ supacloud-lite version
|
|
|
614
631
|
|
|
615
632
|
Run `supacloud-lite migrate` first when initializing a project or when the state directory is missing or empty. `db reset` only accepts initialized state with a valid `secrets.json` marker; it never creates or replaces project secrets for a reset.
|
|
616
633
|
|
|
634
|
+
#### Create a Local Auth User
|
|
635
|
+
|
|
636
|
+
Stop the project server first, then supply the password through a protected file or password-manager pipe:
|
|
637
|
+
|
|
638
|
+
```sh
|
|
639
|
+
supacloud-lite auth create-user --project-dir ./my-project \
|
|
640
|
+
--email operator@example.com --password-stdin --email-confirm --json < /secure/password.txt
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
Keep passwords out of command arguments and shell history. Restrict the file to its owner and remove it according to your credential-handling policy. The command accepts one UTF-8 password line on stdin (at most 4096 bytes), removes one trailing LF/CRLF, and preserves spaces. Interactive terminal input and `--password` arguments are rejected.
|
|
644
|
+
|
|
645
|
+
This is an offline, local operation: it does not contact a remote Auth API or run pending application migrations, seed, functions, or webhooks. Initialize application schemas with `migrate` first. Local administrators can provision accounts even when public signup is disabled. Email is unconfirmed by default; use `--email-confirm` only after verifying the address. Login eligibility for unconfirmed accounts still follows the project's email-confirmation settings; this command does not send confirmation mail.
|
|
646
|
+
|
|
647
|
+
The command enforces the effective password-length policy and rejects duplicate emails without changing passwords, `--memory`, disabled Auth, external identity, and occupied data directories. Output contains only the user ID, email, and confirmation status, never passwords, project keys, or session tokens. Accounts have the ordinary `authenticated` role; application membership and RLS still govern business permissions.
|
|
648
|
+
|
|
617
649
|
Common flags:
|
|
618
650
|
|
|
619
651
|
- `--project-dir`: the project directory containing `supabase/`
|
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.
|
|
11
|
+
version: "0.28.1",
|
|
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",
|
|
@@ -8620,7 +8620,10 @@ class RestHandler {
|
|
|
8620
8620
|
const { rows, affected } = await this.db.withContext(ctx, async (query) => {
|
|
8621
8621
|
const res = await query(built.sql, built.params);
|
|
8622
8622
|
if (opts.queryReturning) {
|
|
8623
|
-
|
|
8623
|
+
const rows = res.rows[0].body;
|
|
8624
|
+
if (opts.wantsObject && !opts.wantsCsv)
|
|
8625
|
+
assertObjectCardinality(rows);
|
|
8626
|
+
return { rows, affected: null };
|
|
8624
8627
|
}
|
|
8625
8628
|
return { rows: null, affected: res.affectedRows ?? 0 };
|
|
8626
8629
|
});
|
|
@@ -8652,14 +8655,7 @@ class RestHandler {
|
|
|
8652
8655
|
return new Response(opts.head ? null : rowsToCsv(rows), { status: opts.status, headers });
|
|
8653
8656
|
}
|
|
8654
8657
|
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
|
-
}
|
|
8658
|
+
assertObjectCardinality(rows);
|
|
8663
8659
|
headers["content-type"] = `${OBJECT_MEDIA}; charset=utf-8`;
|
|
8664
8660
|
return new Response(opts.head ? null : JSON.stringify(rows[0]), {
|
|
8665
8661
|
status: opts.status,
|
|
@@ -8756,17 +8752,18 @@ class RestHandler {
|
|
|
8756
8752
|
const where = conds.length > 0 ? ` where ${conds.join(" and ")}` : "";
|
|
8757
8753
|
const order = builder.renderOrder([], alias);
|
|
8758
8754
|
const limitOffset = builder.renderLimitOffset("");
|
|
8759
|
-
const
|
|
8760
|
-
const
|
|
8761
|
-
const
|
|
8755
|
+
const wantsCount = prefer.count !== undefined;
|
|
8756
|
+
const resultSource = wantsCount ? quoteIdent("_rpc_result") : call;
|
|
8757
|
+
const core = `select ${exprs.join(", ")} from ${resultSource} as ${quoteIdent(alias)}${where}${order}${limitOffset}`;
|
|
8758
|
+
const bodySql = `select coalesce(json_agg(row_to_json(_r)), '[]'::json) as body from (${core}) _r`;
|
|
8759
|
+
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;
|
|
8762
8760
|
const { rows, count } = await this.db.withContext(ctx, async (query) => {
|
|
8763
8761
|
const res = await query(sql, []);
|
|
8764
|
-
|
|
8765
|
-
if (
|
|
8766
|
-
|
|
8767
|
-
|
|
8768
|
-
}
|
|
8769
|
-
return { rows: res.rows[0].body, count };
|
|
8762
|
+
const rows = res.rows[0].body;
|
|
8763
|
+
if (wantsObject && !wantsCsv)
|
|
8764
|
+
assertObjectCardinality(rows);
|
|
8765
|
+
const count = wantsCount ? res.rows[0].count : null;
|
|
8766
|
+
return { rows, count };
|
|
8770
8767
|
});
|
|
8771
8768
|
return this.dataResponse(rows, {
|
|
8772
8769
|
status: 200,
|
|
@@ -8778,6 +8775,16 @@ class RestHandler {
|
|
|
8778
8775
|
});
|
|
8779
8776
|
}
|
|
8780
8777
|
}
|
|
8778
|
+
function assertObjectCardinality(rows) {
|
|
8779
|
+
if (rows.length !== 1) {
|
|
8780
|
+
throw new ApiError(406, {
|
|
8781
|
+
code: "PGRST116",
|
|
8782
|
+
message: "JSON object requested, multiple (or no) rows returned",
|
|
8783
|
+
details: `The result contains ${rows.length} rows`,
|
|
8784
|
+
hint: null
|
|
8785
|
+
});
|
|
8786
|
+
}
|
|
8787
|
+
}
|
|
8781
8788
|
function pickOverload(fns, providedNames) {
|
|
8782
8789
|
const provided = new Set(providedNames);
|
|
8783
8790
|
let best = null;
|
|
@@ -13728,12 +13735,20 @@ async function inspectPostgresExtensions(engine, source) {
|
|
|
13728
13735
|
}
|
|
13729
13736
|
|
|
13730
13737
|
// src/runtime/node/native/readiness.ts
|
|
13731
|
-
function liteCapabilities(engine, replicationProfile) {
|
|
13738
|
+
function liteCapabilities(engine, replicationProfile, authEnabled = true) {
|
|
13732
13739
|
const extensionSource = engine === "native" ? "native-postgresql" : "pglite-bundled";
|
|
13733
13740
|
const common = {
|
|
13734
13741
|
graphql: { status: "unverified", extension: "pg_graphql", reason: "DATABASE_NOT_INSPECTED" },
|
|
13735
13742
|
runtime_mode: "development",
|
|
13736
|
-
identity: {
|
|
13743
|
+
identity: {
|
|
13744
|
+
mode: "local",
|
|
13745
|
+
verification: "built-in",
|
|
13746
|
+
local_auth: {
|
|
13747
|
+
status: authEnabled ? "enabled" : "disabled",
|
|
13748
|
+
reason: authEnabled ? "LOCAL_AUTH_ENABLED" : "AUTH_DISABLED",
|
|
13749
|
+
verification: "configuration-only"
|
|
13750
|
+
}
|
|
13751
|
+
},
|
|
13737
13752
|
migrations: { formats: ["flat-sql", "timestamp-folder"], content_check: "supported", target_bindings: "supported" },
|
|
13738
13753
|
project_release_manifests: "unsupported",
|
|
13739
13754
|
postgres_extensions: {
|
|
@@ -14521,7 +14536,7 @@ function isNotFound(error) {
|
|
|
14521
14536
|
|
|
14522
14537
|
// src/project-runtime.ts
|
|
14523
14538
|
import { chmod, link as link2, lstat as lstat2, mkdir as mkdir5, readFile as readFile7, realpath as realpath2, unlink as unlink2, writeFile as writeFile5 } from "fs/promises";
|
|
14524
|
-
import { dirname as dirname6, isAbsolute, join as join8, parse, relative, resolve as resolve4 } from "path";
|
|
14539
|
+
import { dirname as dirname6, isAbsolute as isAbsolute2, join as join8, parse, relative as relative2, resolve as resolve4 } from "path";
|
|
14525
14540
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
14526
14541
|
|
|
14527
14542
|
// src/runtime/node/bun-server.ts
|
|
@@ -14573,16 +14588,18 @@ async function serveBun(backend, opts = {}) {
|
|
|
14573
14588
|
|
|
14574
14589
|
// src/runtime/node/fs-driver.ts
|
|
14575
14590
|
import { mkdir as mkdir3, readFile as readFile4, rename, rm as rm2, writeFile as writeFile3 } from "fs/promises";
|
|
14576
|
-
import { dirname as dirname4, join as join5,
|
|
14591
|
+
import { dirname as dirname4, isAbsolute, join as join5, relative, resolve as resolvePath, sep } from "path";
|
|
14577
14592
|
|
|
14578
14593
|
class FsStorageDriver {
|
|
14579
14594
|
root;
|
|
14580
14595
|
constructor(root) {
|
|
14581
14596
|
this.root = root;
|
|
14597
|
+
this.root = resolvePath(root);
|
|
14582
14598
|
}
|
|
14583
14599
|
resolve(key) {
|
|
14584
|
-
const path =
|
|
14585
|
-
|
|
14600
|
+
const path = join5(this.root, key);
|
|
14601
|
+
const relativePath = relative(this.root, path);
|
|
14602
|
+
if (!relativePath || relativePath === ".." || relativePath.startsWith(`..${sep}`) || isAbsolute(relativePath)) {
|
|
14586
14603
|
throw new Error(`invalid storage key: ${key}`);
|
|
14587
14604
|
}
|
|
14588
14605
|
return path;
|
|
@@ -14964,10 +14981,10 @@ var RESET_INITIALIZATION_ERROR = 'db reset requires initialized state; run "supa
|
|
|
14964
14981
|
var RESET_INVALID_SECRETS_ERROR = "db reset requires a valid project secrets marker; restore the state before retrying";
|
|
14965
14982
|
function resolveProjectPaths(options = {}) {
|
|
14966
14983
|
const projectDir = resolve4(options.projectDir ?? process.cwd());
|
|
14967
|
-
const stateDir =
|
|
14984
|
+
const stateDir = resolvePath2(projectDir, options.stateDir ?? process.env.SUPACLOUD_LITE_STATE_DIR ?? ".supacloud-lite");
|
|
14968
14985
|
const databaseEngine = resolveDatabaseEngine(options.engine, options.memory);
|
|
14969
|
-
const dataDir = options.memory ? undefined :
|
|
14970
|
-
const storageDir =
|
|
14986
|
+
const dataDir = options.memory ? undefined : resolvePath2(projectDir, options.dataDir ?? process.env.SUPACLOUD_LITE_DATA_DIR ?? join8(stateDir, databaseEngine === "native" ? "pgdata" : "db"));
|
|
14987
|
+
const storageDir = resolvePath2(projectDir, options.storageDir ?? process.env.SUPACLOUD_LITE_STORAGE_DIR ?? join8(stateDir, "storage"));
|
|
14971
14988
|
return {
|
|
14972
14989
|
projectDir,
|
|
14973
14990
|
stateDir,
|
|
@@ -15001,8 +15018,8 @@ async function assertResetPathsSafe(paths) {
|
|
|
15001
15018
|
];
|
|
15002
15019
|
for (const [label, targetPath] of targets) {
|
|
15003
15020
|
const target = resolve4(targetPath);
|
|
15004
|
-
const relativePath =
|
|
15005
|
-
if (!relativePath || relativePath.startsWith("..") ||
|
|
15021
|
+
const relativePath = relative2(stateDir, target);
|
|
15022
|
+
if (!relativePath || relativePath.startsWith("..") || isAbsolute2(relativePath)) {
|
|
15006
15023
|
throw new Error(`refusing to reset ${label} path outside the state directory: ${target}`);
|
|
15007
15024
|
}
|
|
15008
15025
|
await assertResetTargetCanonical(stateDir, canonicalStateDir, target, label);
|
|
@@ -15049,9 +15066,9 @@ async function assertResetTargetCanonical(stateDir, canonicalStateDir, target, l
|
|
|
15049
15066
|
current = parent;
|
|
15050
15067
|
}
|
|
15051
15068
|
const existingAncestor = await nearestExistingAncestor(target);
|
|
15052
|
-
const canonicalTarget = resolve4(await realpath2(existingAncestor),
|
|
15053
|
-
const canonicalRelative =
|
|
15054
|
-
if (!canonicalRelative || canonicalRelative.startsWith("..") ||
|
|
15069
|
+
const canonicalTarget = resolve4(await realpath2(existingAncestor), relative2(existingAncestor, target));
|
|
15070
|
+
const canonicalRelative = relative2(canonicalStateDir, canonicalTarget);
|
|
15071
|
+
if (!canonicalRelative || canonicalRelative.startsWith("..") || isAbsolute2(canonicalRelative)) {
|
|
15055
15072
|
throw new Error(`refusing to reset ${label} path outside the canonical state directory: ${target}`);
|
|
15056
15073
|
}
|
|
15057
15074
|
}
|
|
@@ -15343,8 +15360,8 @@ async function loadWebhooks(projectDir) {
|
|
|
15343
15360
|
throw error;
|
|
15344
15361
|
}
|
|
15345
15362
|
}
|
|
15346
|
-
function
|
|
15347
|
-
return
|
|
15363
|
+
function resolvePath2(projectDir, path) {
|
|
15364
|
+
return isAbsolute2(path) ? path : resolve4(projectDir, path);
|
|
15348
15365
|
}
|
|
15349
15366
|
function randomHex(bytes) {
|
|
15350
15367
|
const value = crypto.getRandomValues(new Uint8Array(bytes));
|
|
@@ -15390,7 +15407,7 @@ async function findEphemeralPort(host = "127.0.0.1") {
|
|
|
15390
15407
|
|
|
15391
15408
|
// src/snapshot.ts
|
|
15392
15409
|
import { chmod as chmod2, copyFile, lstat as lstat3, mkdir as mkdir6, mkdtemp, readdir as readdir3, readFile as readFile8, rename as rename2, rm as rm5, writeFile as writeFile6 } from "fs/promises";
|
|
15393
|
-
import { dirname as dirname7, join as join9, parse as parse2, relative as
|
|
15410
|
+
import { dirname as dirname7, join as join9, parse as parse2, relative as relative3, resolve as resolve5, sep as sep2 } from "path";
|
|
15394
15411
|
import { create as createTar, extract as extractTar2 } from "tar";
|
|
15395
15412
|
var SNAPSHOT_FORMAT = "supacloud-lite-snapshot";
|
|
15396
15413
|
var SNAPSHOT_VERSION = 1;
|
|
@@ -15498,10 +15515,10 @@ async function restoreSnapshot(options) {
|
|
|
15498
15515
|
await mkdir6(stateStage, { recursive: true });
|
|
15499
15516
|
await copyEntry(join9(payloadRoot, "secrets.json"), join9(stateStage, "secrets.json"));
|
|
15500
15517
|
if (paths.dataDir && isWithin(paths.stateDir, paths.dataDir)) {
|
|
15501
|
-
await copyEntry(join9(payloadRoot, "database"), join9(stateStage,
|
|
15518
|
+
await copyEntry(join9(payloadRoot, "database"), join9(stateStage, relative3(paths.stateDir, paths.dataDir)));
|
|
15502
15519
|
}
|
|
15503
15520
|
if (options.storageBackend === "fs" && isWithin(paths.stateDir, paths.storageDir)) {
|
|
15504
|
-
await copyEntry(join9(payloadRoot, "storage"), join9(stateStage,
|
|
15521
|
+
await copyEntry(join9(payloadRoot, "storage"), join9(stateStage, relative3(paths.stateDir, paths.storageDir)));
|
|
15505
15522
|
}
|
|
15506
15523
|
const swaps = [];
|
|
15507
15524
|
try {
|
|
@@ -15816,6 +15833,89 @@ async function waitForShutdown(closeProject, signalSource = process) {
|
|
|
15816
15833
|
await closeProject();
|
|
15817
15834
|
}
|
|
15818
15835
|
|
|
15836
|
+
// src/auth-cli.ts
|
|
15837
|
+
async function createLocalAuthUser(options) {
|
|
15838
|
+
if (options.memory)
|
|
15839
|
+
throw new Error("auth create-user requires a persistent project; --memory is not supported");
|
|
15840
|
+
const email = options.email?.trim().toLowerCase();
|
|
15841
|
+
if (!email || email.length > 254 || !/^[^\s@\x00-\x1f\x7f]+@[^\s@\x00-\x1f\x7f]+\.[^\s@\x00-\x1f\x7f]+$/.test(email)) {
|
|
15842
|
+
throw new Error("auth create-user requires a valid --email");
|
|
15843
|
+
}
|
|
15844
|
+
if (!options.passwordStdin)
|
|
15845
|
+
throw new Error("auth create-user requires --password-stdin; passwords are not accepted as arguments");
|
|
15846
|
+
if (process.stdin.isTTY)
|
|
15847
|
+
throw new Error("--password-stdin requires piped or redirected input; terminal input is not accepted");
|
|
15848
|
+
const paths = resolveProjectPaths(options);
|
|
15849
|
+
const config = loadProjectConfig(paths.projectDir, process.env, options.runtimeMode);
|
|
15850
|
+
if (config.auth.enabled === false)
|
|
15851
|
+
throw new Error("cannot create a local user: Auth is disabled in config.toml");
|
|
15852
|
+
if (options.externalIdentity || options.identityModule || config.lite.identityModule) {
|
|
15853
|
+
throw new Error("cannot create a local user: the project uses external identity");
|
|
15854
|
+
}
|
|
15855
|
+
await assertDataDirUnlocked(paths.dataDir);
|
|
15856
|
+
const password = await readPassword();
|
|
15857
|
+
const project = await createProjectBackend({
|
|
15858
|
+
...options,
|
|
15859
|
+
applyMigrations: false,
|
|
15860
|
+
includeSeed: false,
|
|
15861
|
+
includeFunctions: false,
|
|
15862
|
+
includeWebhooks: false,
|
|
15863
|
+
includeIdentity: false,
|
|
15864
|
+
graphql: { enabled: false },
|
|
15865
|
+
storageBackend: "memory",
|
|
15866
|
+
startRuntimeServices: false,
|
|
15867
|
+
log: () => {}
|
|
15868
|
+
});
|
|
15869
|
+
try {
|
|
15870
|
+
const settings = await loadAuthSettings(project.backend.db, project.config.auth.settings);
|
|
15871
|
+
if (password.length < settings.minPasswordLength) {
|
|
15872
|
+
throw new Error(`password must be at least ${settings.minPasswordLength} characters`);
|
|
15873
|
+
}
|
|
15874
|
+
const existing = await project.backend.db.query("select id from auth.users where email = $1", [email]);
|
|
15875
|
+
if (existing.rows.length > 0)
|
|
15876
|
+
throw new Error("an Auth user with this email already exists; no changes made");
|
|
15877
|
+
const response = await project.backend.fetch("http://local/auth/v1/admin/users", {
|
|
15878
|
+
method: "POST",
|
|
15879
|
+
headers: {
|
|
15880
|
+
apikey: project.backend.serviceRoleKey,
|
|
15881
|
+
authorization: `Bearer ${project.backend.serviceRoleKey}`,
|
|
15882
|
+
"content-type": "application/json"
|
|
15883
|
+
},
|
|
15884
|
+
body: JSON.stringify({ email, password, email_confirm: options.emailConfirm === true })
|
|
15885
|
+
});
|
|
15886
|
+
if (!response.ok)
|
|
15887
|
+
throw new Error(`local Auth user creation failed (HTTP ${response.status})`);
|
|
15888
|
+
const user = await response.json();
|
|
15889
|
+
if (!isRecord(user) || typeof user.id !== "string" || typeof user.email !== "string") {
|
|
15890
|
+
throw new Error("local Auth returned an invalid user response");
|
|
15891
|
+
}
|
|
15892
|
+
return { id: user.id, email: user.email, email_confirmed: typeof user.email_confirmed_at === "string" };
|
|
15893
|
+
} finally {
|
|
15894
|
+
await project.backend.close();
|
|
15895
|
+
}
|
|
15896
|
+
}
|
|
15897
|
+
async function readPassword() {
|
|
15898
|
+
const chunks = [];
|
|
15899
|
+
let length = 0;
|
|
15900
|
+
for await (const chunk of Bun.stdin.stream()) {
|
|
15901
|
+
length += chunk.byteLength;
|
|
15902
|
+
if (length > 4098)
|
|
15903
|
+
throw new Error("password input exceeds 4096 bytes");
|
|
15904
|
+
chunks.push(chunk);
|
|
15905
|
+
}
|
|
15906
|
+
let password;
|
|
15907
|
+
try {
|
|
15908
|
+
password = new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(chunks)).replace(/\r?\n$/, "");
|
|
15909
|
+
} catch {
|
|
15910
|
+
throw new Error("password input must be valid UTF-8");
|
|
15911
|
+
}
|
|
15912
|
+
if (!password || /[\r\n\x00]/.test(password))
|
|
15913
|
+
throw new Error("password input must contain one non-empty line");
|
|
15914
|
+
if (Buffer.byteLength(password, "utf8") > 4096)
|
|
15915
|
+
throw new Error("password input exceeds 4096 bytes");
|
|
15916
|
+
return password;
|
|
15917
|
+
}
|
|
15918
|
+
|
|
15819
15919
|
// src/cli.ts
|
|
15820
15920
|
function parseArgs(argv) {
|
|
15821
15921
|
const args = [...argv];
|
|
@@ -15896,7 +15996,15 @@ function parseArgs(argv) {
|
|
|
15896
15996
|
options.force = true;
|
|
15897
15997
|
else if (argument === "--json")
|
|
15898
15998
|
options.json = true;
|
|
15899
|
-
else if (argument === "--
|
|
15999
|
+
else if (argument === "--email")
|
|
16000
|
+
options.email = next();
|
|
16001
|
+
else if (argument === "--password-stdin")
|
|
16002
|
+
options.passwordStdin = true;
|
|
16003
|
+
else if (argument === "--email-confirm")
|
|
16004
|
+
options.emailConfirm = true;
|
|
16005
|
+
else if (argument.startsWith("--password")) {
|
|
16006
|
+
throw new Error("password arguments are not supported; use --password-stdin");
|
|
16007
|
+
} else if (argument === "--version") {
|
|
15900
16008
|
console.log(package_default.version);
|
|
15901
16009
|
process.exit(0);
|
|
15902
16010
|
} else if (argument === "--help" || argument === "-h") {
|
|
@@ -15911,7 +16019,20 @@ function parseArgs(argv) {
|
|
|
15911
16019
|
}
|
|
15912
16020
|
async function main() {
|
|
15913
16021
|
const options = parseArgs(process.argv.slice(2));
|
|
16022
|
+
if (options.command !== "auth" && (options.email !== undefined || options.passwordStdin || options.emailConfirm)) {
|
|
16023
|
+
throw new Error("--email, --password-stdin and --email-confirm are only supported by auth create-user");
|
|
16024
|
+
}
|
|
15914
16025
|
const paths = resolveProjectPaths(options);
|
|
16026
|
+
if (options.command === "auth") {
|
|
16027
|
+
if (options.positionals.length !== 1 || options.positionals[0] !== "create-user") {
|
|
16028
|
+
throw new Error("usage: auth create-user --email <email> --password-stdin [--email-confirm] [--json]");
|
|
16029
|
+
}
|
|
16030
|
+
const user = await createLocalAuthUser(options);
|
|
16031
|
+
await writeStandardOutput(options.json ? `${JSON.stringify(user, null, 2)}
|
|
16032
|
+
` : `Created Auth user ${user.id} (${user.email}); email confirmed: ${user.email_confirmed ? "yes" : "no"}
|
|
16033
|
+
`);
|
|
16034
|
+
return;
|
|
16035
|
+
}
|
|
15915
16036
|
if (options.command === "version" || options.command === "--version") {
|
|
15916
16037
|
await writeStandardOutput(`${package_default.version}
|
|
15917
16038
|
`);
|
|
@@ -15991,10 +16112,18 @@ ${privilegedKey}
|
|
|
15991
16112
|
if (options.command === "doctor") {
|
|
15992
16113
|
const config = loadProjectConfig(paths.projectDir, process.env, options.runtimeMode);
|
|
15993
16114
|
const replication = resolveNativeReplicationOptions(options, paths.databaseEngine);
|
|
15994
|
-
const report = liteCapabilities(paths.databaseEngine, replication?.profile);
|
|
16115
|
+
const report = liteCapabilities(paths.databaseEngine, replication?.profile, config.auth.enabled !== false);
|
|
15995
16116
|
report.runtime_mode = config.lite.runtimeMode;
|
|
15996
16117
|
if (options.identityModule ?? config.lite.identityModule) {
|
|
15997
|
-
report.identity = {
|
|
16118
|
+
report.identity = {
|
|
16119
|
+
mode: "external",
|
|
16120
|
+
verification: "unverified",
|
|
16121
|
+
local_auth: {
|
|
16122
|
+
status: "disabled",
|
|
16123
|
+
reason: "EXTERNAL_IDENTITY_CONFIGURED",
|
|
16124
|
+
verification: "configuration-only"
|
|
16125
|
+
}
|
|
16126
|
+
};
|
|
15998
16127
|
}
|
|
15999
16128
|
if (paths.dataDir && existsSync5(join10(paths.dataDir, "PG_VERSION"))) {
|
|
16000
16129
|
const engine = paths.databaseEngine === "native" ? await createNativeEngine({ dataDir: paths.dataDir, installDir: options.postgresDir, log: quietLog, replication }) : await createPgliteEngine(paths.dataDir);
|
|
@@ -16294,6 +16423,7 @@ async function printDoctor(report) {
|
|
|
16294
16423
|
output.push(`graphql: ${report.graphql.status} (${report.graphql.version ?? report.graphql.reason ?? "pg_graphql"})`);
|
|
16295
16424
|
output.push(`postgres_extensions: ${report.postgres_extensions.status} ` + `(${report.postgres_extensions.source}, ${report.postgres_extensions.available.length} available, ` + `${report.postgres_extensions.installed.length} installed)`);
|
|
16296
16425
|
output.push(`identity: ${report.identity.mode} (${report.identity.verification})`);
|
|
16426
|
+
output.push(`local_auth: ${report.identity.local_auth.status} (${report.identity.local_auth.reason}, configuration-only)`);
|
|
16297
16427
|
output.push(`migration_formats: ${report.migrations.formats.join(", ")}`);
|
|
16298
16428
|
if (report.powersync_readiness) {
|
|
16299
16429
|
output.push(`powersync_ready: ${report.powersync_readiness.ready}`);
|
|
@@ -16330,6 +16460,7 @@ Commands:
|
|
|
16330
16460
|
status list applied migrations
|
|
16331
16461
|
keys print the anon key
|
|
16332
16462
|
keys --service-role also print the privileged service_role key
|
|
16463
|
+
auth create-user create a local Auth user while the project server is stopped
|
|
16333
16464
|
gen types emit Supabase-shaped TypeScript database types
|
|
16334
16465
|
db reset reset initialized database/storage and re-run migrations
|
|
16335
16466
|
db diff print schema changes outside migrations
|
|
@@ -16364,7 +16495,10 @@ Options:
|
|
|
16364
16495
|
--replication-tls-cert <p> PostgreSQL TLS certificate
|
|
16365
16496
|
--replication-tls-key <p> PostgreSQL TLS private key
|
|
16366
16497
|
--memory use an in-memory PGlite database
|
|
16367
|
-
--json emit machine-readable doctor/migrate/status output
|
|
16498
|
+
--json emit machine-readable doctor/migrate/status/auth output
|
|
16499
|
+
--email <email> auth create-user: account email
|
|
16500
|
+
--password-stdin auth create-user: read one password line from redirected stdin
|
|
16501
|
+
--email-confirm auth create-user: explicitly mark the email as confirmed
|
|
16368
16502
|
--runtime-mode development (default) or strict
|
|
16369
16503
|
--postgres-dir operator-managed PostgreSQL installation (native only)
|
|
16370
16504
|
--identity-module trusted external identity bridge module
|
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.
|
|
91
|
+
version: "0.28.1",
|
|
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",
|
|
@@ -8614,7 +8614,10 @@ class RestHandler {
|
|
|
8614
8614
|
const { rows, affected } = await this.db.withContext(ctx, async (query) => {
|
|
8615
8615
|
const res = await query(built.sql, built.params);
|
|
8616
8616
|
if (opts.queryReturning) {
|
|
8617
|
-
|
|
8617
|
+
const rows = res.rows[0].body;
|
|
8618
|
+
if (opts.wantsObject && !opts.wantsCsv)
|
|
8619
|
+
assertObjectCardinality(rows);
|
|
8620
|
+
return { rows, affected: null };
|
|
8618
8621
|
}
|
|
8619
8622
|
return { rows: null, affected: res.affectedRows ?? 0 };
|
|
8620
8623
|
});
|
|
@@ -8646,14 +8649,7 @@ class RestHandler {
|
|
|
8646
8649
|
return new Response(opts.head ? null : rowsToCsv(rows), { status: opts.status, headers });
|
|
8647
8650
|
}
|
|
8648
8651
|
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
|
-
}
|
|
8652
|
+
assertObjectCardinality(rows);
|
|
8657
8653
|
headers["content-type"] = `${OBJECT_MEDIA}; charset=utf-8`;
|
|
8658
8654
|
return new Response(opts.head ? null : JSON.stringify(rows[0]), {
|
|
8659
8655
|
status: opts.status,
|
|
@@ -8750,17 +8746,18 @@ class RestHandler {
|
|
|
8750
8746
|
const where = conds.length > 0 ? ` where ${conds.join(" and ")}` : "";
|
|
8751
8747
|
const order = builder.renderOrder([], alias);
|
|
8752
8748
|
const limitOffset = builder.renderLimitOffset("");
|
|
8753
|
-
const
|
|
8754
|
-
const
|
|
8755
|
-
const
|
|
8749
|
+
const wantsCount = prefer.count !== undefined;
|
|
8750
|
+
const resultSource = wantsCount ? quoteIdent("_rpc_result") : call;
|
|
8751
|
+
const core = `select ${exprs.join(", ")} from ${resultSource} as ${quoteIdent(alias)}${where}${order}${limitOffset}`;
|
|
8752
|
+
const bodySql = `select coalesce(json_agg(row_to_json(_r)), '[]'::json) as body from (${core}) _r`;
|
|
8753
|
+
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;
|
|
8756
8754
|
const { rows, count } = await this.db.withContext(ctx, async (query) => {
|
|
8757
8755
|
const res = await query(sql, []);
|
|
8758
|
-
|
|
8759
|
-
if (
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
}
|
|
8763
|
-
return { rows: res.rows[0].body, count };
|
|
8756
|
+
const rows = res.rows[0].body;
|
|
8757
|
+
if (wantsObject && !wantsCsv)
|
|
8758
|
+
assertObjectCardinality(rows);
|
|
8759
|
+
const count = wantsCount ? res.rows[0].count : null;
|
|
8760
|
+
return { rows, count };
|
|
8764
8761
|
});
|
|
8765
8762
|
return this.dataResponse(rows, {
|
|
8766
8763
|
status: 200,
|
|
@@ -8772,6 +8769,16 @@ class RestHandler {
|
|
|
8772
8769
|
});
|
|
8773
8770
|
}
|
|
8774
8771
|
}
|
|
8772
|
+
function assertObjectCardinality(rows) {
|
|
8773
|
+
if (rows.length !== 1) {
|
|
8774
|
+
throw new ApiError(406, {
|
|
8775
|
+
code: "PGRST116",
|
|
8776
|
+
message: "JSON object requested, multiple (or no) rows returned",
|
|
8777
|
+
details: `The result contains ${rows.length} rows`,
|
|
8778
|
+
hint: null
|
|
8779
|
+
});
|
|
8780
|
+
}
|
|
8781
|
+
}
|
|
8775
8782
|
function pickOverload(fns, providedNames) {
|
|
8776
8783
|
const provided = new Set(providedNames);
|
|
8777
8784
|
let best = null;
|
|
@@ -11736,16 +11743,18 @@ class S3StorageDriver {
|
|
|
11736
11743
|
}
|
|
11737
11744
|
// src/runtime/node/fs-driver.ts
|
|
11738
11745
|
import { mkdir as mkdir2, readFile as readFile2, rename, rm, writeFile } from "fs/promises";
|
|
11739
|
-
import { dirname as dirname2, join,
|
|
11746
|
+
import { dirname as dirname2, isAbsolute, join, relative, resolve as resolvePath, sep } from "path";
|
|
11740
11747
|
|
|
11741
11748
|
class FsStorageDriver {
|
|
11742
11749
|
root;
|
|
11743
11750
|
constructor(root) {
|
|
11744
11751
|
this.root = root;
|
|
11752
|
+
this.root = resolvePath(root);
|
|
11745
11753
|
}
|
|
11746
11754
|
resolve(key) {
|
|
11747
|
-
const path =
|
|
11748
|
-
|
|
11755
|
+
const path = join(this.root, key);
|
|
11756
|
+
const relativePath = relative(this.root, path);
|
|
11757
|
+
if (!relativePath || relativePath === ".." || relativePath.startsWith(`..${sep}`) || isAbsolute(relativePath)) {
|
|
11749
11758
|
throw new Error(`invalid storage key: ${key}`);
|
|
11750
11759
|
}
|
|
11751
11760
|
return path;
|
|
@@ -12937,12 +12946,20 @@ function removeStalePidFile(pidPath) {
|
|
|
12937
12946
|
} catch {}
|
|
12938
12947
|
}
|
|
12939
12948
|
// src/runtime/node/native/readiness.ts
|
|
12940
|
-
function liteCapabilities(engine, replicationProfile) {
|
|
12949
|
+
function liteCapabilities(engine, replicationProfile, authEnabled = true) {
|
|
12941
12950
|
const extensionSource = engine === "native" ? "native-postgresql" : "pglite-bundled";
|
|
12942
12951
|
const common = {
|
|
12943
12952
|
graphql: { status: "unverified", extension: "pg_graphql", reason: "DATABASE_NOT_INSPECTED" },
|
|
12944
12953
|
runtime_mode: "development",
|
|
12945
|
-
identity: {
|
|
12954
|
+
identity: {
|
|
12955
|
+
mode: "local",
|
|
12956
|
+
verification: "built-in",
|
|
12957
|
+
local_auth: {
|
|
12958
|
+
status: authEnabled ? "enabled" : "disabled",
|
|
12959
|
+
reason: authEnabled ? "LOCAL_AUTH_ENABLED" : "AUTH_DISABLED",
|
|
12960
|
+
verification: "configuration-only"
|
|
12961
|
+
}
|
|
12962
|
+
},
|
|
12946
12963
|
migrations: { formats: ["flat-sql", "timestamp-folder"], content_check: "supported", target_bindings: "supported" },
|
|
12947
12964
|
project_release_manifests: "unsupported",
|
|
12948
12965
|
postgres_extensions: {
|
|
@@ -13216,7 +13233,7 @@ async function inspectPostgresExtensions(engine, source) {
|
|
|
13216
13233
|
}
|
|
13217
13234
|
// src/project-runtime.ts
|
|
13218
13235
|
import { chmod, link, lstat as lstat2, mkdir as mkdir4, readFile as readFile6, realpath as realpath2, unlink as unlink2, writeFile as writeFile4 } from "fs/promises";
|
|
13219
|
-
import { dirname as dirname4, isAbsolute, join as join7, parse, relative, resolve as resolve3 } from "path";
|
|
13236
|
+
import { dirname as dirname4, isAbsolute as isAbsolute2, join as join7, parse, relative as relative2, resolve as resolve3 } from "path";
|
|
13220
13237
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
13221
13238
|
|
|
13222
13239
|
// src/runtime/node/config-toml.ts
|
|
@@ -14159,10 +14176,10 @@ function isNotFound(error) {
|
|
|
14159
14176
|
// src/project-runtime.ts
|
|
14160
14177
|
function resolveProjectPaths(options = {}) {
|
|
14161
14178
|
const projectDir = resolve3(options.projectDir ?? process.cwd());
|
|
14162
|
-
const stateDir =
|
|
14179
|
+
const stateDir = resolvePath2(projectDir, options.stateDir ?? process.env.SUPACLOUD_LITE_STATE_DIR ?? ".supacloud-lite");
|
|
14163
14180
|
const databaseEngine = resolveDatabaseEngine(options.engine, options.memory);
|
|
14164
|
-
const dataDir = options.memory ? undefined :
|
|
14165
|
-
const storageDir =
|
|
14181
|
+
const dataDir = options.memory ? undefined : resolvePath2(projectDir, options.dataDir ?? process.env.SUPACLOUD_LITE_DATA_DIR ?? join7(stateDir, databaseEngine === "native" ? "pgdata" : "db"));
|
|
14182
|
+
const storageDir = resolvePath2(projectDir, options.storageDir ?? process.env.SUPACLOUD_LITE_STORAGE_DIR ?? join7(stateDir, "storage"));
|
|
14166
14183
|
return {
|
|
14167
14184
|
projectDir,
|
|
14168
14185
|
stateDir,
|
|
@@ -14444,8 +14461,8 @@ async function loadWebhooks(projectDir) {
|
|
|
14444
14461
|
throw error;
|
|
14445
14462
|
}
|
|
14446
14463
|
}
|
|
14447
|
-
function
|
|
14448
|
-
return
|
|
14464
|
+
function resolvePath2(projectDir, path) {
|
|
14465
|
+
return isAbsolute2(path) ? path : resolve3(projectDir, path);
|
|
14449
14466
|
}
|
|
14450
14467
|
function randomHex(bytes) {
|
|
14451
14468
|
const value = crypto.getRandomValues(new Uint8Array(bytes));
|
|
@@ -14490,7 +14507,7 @@ async function findEphemeralPort(host = "127.0.0.1") {
|
|
|
14490
14507
|
}
|
|
14491
14508
|
// src/snapshot.ts
|
|
14492
14509
|
import { chmod as chmod2, copyFile, lstat as lstat3, mkdir as mkdir5, mkdtemp, readdir as readdir3, readFile as readFile7, rename as rename2, rm as rm4, writeFile as writeFile5 } from "fs/promises";
|
|
14493
|
-
import { dirname as dirname5, join as join8, parse as parse2, relative as
|
|
14510
|
+
import { dirname as dirname5, join as join8, parse as parse2, relative as relative3, resolve as resolve4, sep as sep2 } from "path";
|
|
14494
14511
|
import { create as createTar, extract as extractTar2 } from "tar";
|
|
14495
14512
|
var SNAPSHOT_FORMAT = "supacloud-lite-snapshot";
|
|
14496
14513
|
var SNAPSHOT_VERSION = 1;
|
|
@@ -14598,10 +14615,10 @@ async function restoreSnapshot(options) {
|
|
|
14598
14615
|
await mkdir5(stateStage, { recursive: true });
|
|
14599
14616
|
await copyEntry(join8(payloadRoot, "secrets.json"), join8(stateStage, "secrets.json"));
|
|
14600
14617
|
if (paths.dataDir && isWithin(paths.stateDir, paths.dataDir)) {
|
|
14601
|
-
await copyEntry(join8(payloadRoot, "database"), join8(stateStage,
|
|
14618
|
+
await copyEntry(join8(payloadRoot, "database"), join8(stateStage, relative3(paths.stateDir, paths.dataDir)));
|
|
14602
14619
|
}
|
|
14603
14620
|
if (options.storageBackend === "fs" && isWithin(paths.stateDir, paths.storageDir)) {
|
|
14604
|
-
await copyEntry(join8(payloadRoot, "storage"), join8(stateStage,
|
|
14621
|
+
await copyEntry(join8(payloadRoot, "storage"), join8(stateStage, relative3(paths.stateDir, paths.storageDir)));
|
|
14605
14622
|
}
|
|
14606
14623
|
const swaps = [];
|
|
14607
14624
|
try {
|
|
@@ -9,8 +9,8 @@ export declare class FsStorageDriver implements StorageDriver {
|
|
|
9
9
|
/**
|
|
10
10
|
* Resolve a storage key to an absolute path inside the root.
|
|
11
11
|
*
|
|
12
|
-
* SECURITY: rejects keys that escape the root (e.g. `../`)
|
|
13
|
-
*
|
|
12
|
+
* SECURITY: rejects keys that escape the root (e.g. `../`) or resolve to
|
|
13
|
+
* the root itself.
|
|
14
14
|
*
|
|
15
15
|
* @throws if the key would resolve outside the root directory.
|
|
16
16
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs-driver.d.ts","sourceRoot":"","sources":["../../../src/runtime/node/fs-driver.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD;;;GAGG;AACH,qBAAa,eAAgB,YAAW,aAAa;IACvC,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"fs-driver.d.ts","sourceRoot":"","sources":["../../../src/runtime/node/fs-driver.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD;;;GAGG;AACH,qBAAa,eAAgB,YAAW,aAAa;IACvC,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,MAAM;IAIhC;;;;;;;OAOG;IACH,OAAO,CAAC,OAAO;IASf,iEAAiE;IAC3D,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAavD,4DAA4D;IACtD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IASlD,iFAAiF;IAC3E,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAKhD,uDAAuD;IACjD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxC,0CAA0C;IACpC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAGhD"}
|
|
@@ -10,6 +10,11 @@ export interface LiteDoctorReport {
|
|
|
10
10
|
identity: {
|
|
11
11
|
mode: 'local' | 'external';
|
|
12
12
|
verification: 'built-in' | 'unverified';
|
|
13
|
+
local_auth: {
|
|
14
|
+
status: 'enabled' | 'disabled';
|
|
15
|
+
reason: 'LOCAL_AUTH_ENABLED' | 'AUTH_DISABLED' | 'EXTERNAL_IDENTITY_CONFIGURED';
|
|
16
|
+
verification: 'configuration-only';
|
|
17
|
+
};
|
|
13
18
|
};
|
|
14
19
|
migrations: {
|
|
15
20
|
formats: string[];
|
|
@@ -78,6 +83,6 @@ export interface PowerSyncReadiness {
|
|
|
78
83
|
invalidation_reason: string | null;
|
|
79
84
|
}>;
|
|
80
85
|
}
|
|
81
|
-
export declare function liteCapabilities(engine: 'pglite' | 'native', replicationProfile?: 'powersync'): LiteDoctorReport;
|
|
86
|
+
export declare function liteCapabilities(engine: 'pglite' | 'native', replicationProfile?: 'powersync', authEnabled?: boolean): LiteDoctorReport;
|
|
82
87
|
export declare function inspectPowerSyncReadiness(engine: DbEngine, options: PowerSyncReplicationOptions): Promise<PowerSyncReadiness>;
|
|
83
88
|
//# sourceMappingURL=readiness.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readiness.d.ts","sourceRoot":"","sources":["../../../../src/runtime/node/native/readiness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AAC7D,OAAO,EAGL,KAAK,2BAA2B,EACjC,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAA4B,0BAA0B,EAAE,MAAM,iBAAiB,CAAA;AAE3F,MAAM,MAAM,oBAAoB,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,GAAG,aAAa,CAAA;AAEtF,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,iBAAiB,CAAA;IAC1B,YAAY,EAAE,WAAW,CAAA;IACzB,QAAQ,EAAE;
|
|
1
|
+
{"version":3,"file":"readiness.d.ts","sourceRoot":"","sources":["../../../../src/runtime/node/native/readiness.ts"],"names":[],"mappings":"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"}
|
|
@@ -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,EAAqB,MAAM,mBAAmB,CAAA;AAC9F,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;
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/runtime/rest/handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAA4B,KAAK,QAAQ,EAAqB,MAAM,mBAAmB,CAAA;AAC9F,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;CAqFrB"}
|