@supacloud/lite 0.6.0 → 0.7.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 CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.7.1](https://github.com/zuohuadong/supacloud/compare/supacloud-lite-v0.7.0...supacloud-lite-v0.7.1) (2026-08-11)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **lite:** bound standalone subprocess collection ([#827](https://github.com/zuohuadong/supacloud/issues/827)) ([4c37c33](https://github.com/zuohuadong/supacloud/commit/4c37c3325391de11ff6224b0297eb211a3627f5a))
9
+ * **lite:** guard db reset on uninitialized state ([#826](https://github.com/zuohuadong/supacloud/issues/826)) ([1e45f5f](https://github.com/zuohuadong/supacloud/commit/1e45f5fdd0cda0dbe238043526eb8d5ab8ebf1e1))
10
+ * **lite:** keep project API keys stable ([#817](https://github.com/zuohuadong/supacloud/issues/817)) ([80c5818](https://github.com/zuohuadong/supacloud/commit/80c581846a91654268af2b745f9a7d3d01ab8d5e))
11
+
12
+ ## [0.7.0](https://github.com/zuohuadong/supacloud/compare/supacloud-lite-v0.6.0...supacloud-lite-v0.7.0) (2026-08-11)
13
+
14
+
15
+ ### Features
16
+
17
+ * **lite:** support admin magic link verification ([#788](https://github.com/zuohuadong/supacloud/issues/788)) ([97defa0](https://github.com/zuohuadong/supacloud/commit/97defa0e3ba875d1bd0363f9f64df02a089238ff))
18
+
3
19
  ## [0.6.0](https://github.com/zuohuadong/supacloud/compare/supacloud-lite-v0.5.10...supacloud-lite-v0.6.0) (2026-08-11)
4
20
 
5
21
 
package/README.md CHANGED
@@ -109,6 +109,8 @@ supacloud-lite inspect
109
109
  supacloud-lite version
110
110
  ```
111
111
 
112
+ 首次初始化或 state 目录不存在/为空时,先运行 `supacloud-lite migrate`。`db reset` 只接受已经初始化且保留有效 `secrets.json` 标记的 state;它不会为 reset 创建或覆盖项目 secrets。
113
+
112
114
  通用参数:
113
115
 
114
116
  - `--project-dir`:包含 `supabase/` 的项目目录
@@ -257,7 +259,7 @@ RLS 表的 Realtime DELETE 无法在行删除后安全重放 SELECT policy,因
257
259
  应用代码、SQL migrations、RLS policy、Storage 调用和 Realtime 订阅可以保持原来的 Supabase 形状。迁移时仍需验证以下边界:
258
260
 
259
261
  1. 把现有 `supabase/migrations`、`config.toml`、Functions 和 seed 文件带到 Lite 项目。
260
- 2. `supacloud-lite db reset` 在空库重放 schema,再导入业务数据。
262
+ 2. 先用 `supacloud-lite migrate` 初始化空 state 并重放 schema;只有在已初始化的可丢弃 state 上需要再次重放时才使用 `supacloud-lite db reset`,然后导入业务数据。
261
263
  3. 对使用 PGlite 未提供扩展或 PostgREST 高级语法的 SQL/API 做替代处理。
262
264
  4. 通过真实 `@supabase/supabase-js` 集成测试验证关键查询、RLS、Storage 和 Realtime。
263
265
 
@@ -441,6 +443,8 @@ supacloud-lite inspect
441
443
  supacloud-lite version
442
444
  ```
443
445
 
446
+ 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.
447
+
444
448
  Common flags:
445
449
 
446
450
  - `--project-dir`: the project directory containing `supabase/`
@@ -589,7 +593,7 @@ Realtime DELETE on RLS tables cannot safely replay SELECT policies after a row i
589
593
  Application code, SQL migrations, RLS policies, Storage calls, and Realtime subscriptions can keep their original Supabase shape. When migrating, you still need to validate the following boundaries:
590
594
 
591
595
  1. Bring the existing `supabase/migrations`, `config.toml`, Functions, and seed files into the Lite project.
592
- 2. Use `supacloud-lite db reset` to replay the schema on an empty database, then import business data.
596
+ 2. Use `supacloud-lite migrate` to initialize empty state and replay the schema. Use `supacloud-lite db reset` only when replaying an already initialized disposable state, then import business data.
593
597
  3. Provide alternatives for SQL/API that use extensions not offered by PGlite or advanced PostgREST syntax.
594
598
  4. Verify key queries, RLS, Storage, and Realtime through real `@supabase/supabase-js` integration tests.
595
599
 
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import { dirname as dirname5, join as join9, resolve as resolve4 } from "path";
8
8
  // package.json
9
9
  var package_default = {
10
10
  name: "@supacloud/lite",
11
- version: "0.6.0",
11
+ version: "0.7.1",
12
12
  description: "Bun-native, single-project Supabase-compatible backend powered by PGlite",
13
13
  type: "module",
14
14
  license: "Apache-2.0",
@@ -83,6 +83,8 @@ var package_default = {
83
83
  // src/runtime/jwt.ts
84
84
  var encoder = new TextEncoder;
85
85
  var decoder = new TextDecoder;
86
+ var PROJECT_API_KEY_ISSUED_AT = 1641769200;
87
+ var PROJECT_API_KEY_EXPIRES_AT = 4102444800;
86
88
  function bytesToBase64Url(bytes) {
87
89
  let binary = "";
88
90
  const chunk = 32768;
@@ -111,6 +113,18 @@ async function signJwt(claims, secret) {
111
113
  const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(data));
112
114
  return `${data}.${bytesToBase64Url(new Uint8Array(sig))}`;
113
115
  }
116
+ async function mintProjectApiKeys(jwtSecret) {
117
+ const commonClaims = {
118
+ iss: "supabase",
119
+ ref: "local",
120
+ iat: PROJECT_API_KEY_ISSUED_AT,
121
+ exp: PROJECT_API_KEY_EXPIRES_AT
122
+ };
123
+ return {
124
+ anonKey: await signJwt({ ...commonClaims, role: "anon" }, jwtSecret),
125
+ serviceRoleKey: await signJwt({ ...commonClaims, role: "service_role" }, jwtSecret)
126
+ };
127
+ }
114
128
  async function verifyJwt(token, secret) {
115
129
  const parts = token.split(".");
116
130
  if (parts.length !== 3)
@@ -1112,6 +1126,10 @@ function randomOtp(length) {
1112
1126
  }
1113
1127
  return code;
1114
1128
  }
1129
+ async function sha256Hex(value) {
1130
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
1131
+ return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
1132
+ }
1115
1133
  function normalizePhone(value) {
1116
1134
  if (typeof value !== "string")
1117
1135
  return null;
@@ -1650,33 +1668,58 @@ Or sign in with this link: ${link}`
1650
1668
  }
1651
1669
  static MAX_OTP_ATTEMPTS = 5;
1652
1670
  async redeem(token, types, email) {
1653
- const normalizedEmail = email?.toLowerCase().trim() ?? null;
1654
- const res = await this.db.query(`delete from auth.one_time_tokens
1655
- where token = $1 and token_type = any($2::text[])
1656
- and ($3::text is null or email = $3) and expires_at > now()
1657
- and attempts < $4
1658
- returning user_id, email`, [token, `{${types.join(",")}}`, normalizedEmail, AuthHandler.MAX_OTP_ATTEMPTS]);
1659
- const row = res.rows[0];
1660
- if (!row) {
1661
- if (normalizedEmail) {
1662
- await this.db.query(`update auth.one_time_tokens set attempts = attempts + 1
1663
- where email = $1 and token_type = any($2::text[]) and expires_at > now()`, [normalizedEmail, `{${types.join(",")}}`]);
1664
- await this.db.query(`delete from auth.one_time_tokens where email = $1 and attempts >= $2`, [normalizedEmail, AuthHandler.MAX_OTP_ATTEMPTS]);
1665
- }
1671
+ const redemption = {
1672
+ token,
1673
+ tokenTypes: `{${types.join(",")}}`,
1674
+ email: email?.toLowerCase().trim() ?? null
1675
+ };
1676
+ return this.db.transaction((query) => this.claimEmailToken(query, redemption));
1677
+ }
1678
+ async claimEmailToken(query, redemption) {
1679
+ const candidate = await this.findEmailTokenCandidate(query, redemption);
1680
+ if (!candidate) {
1681
+ await this.recordFailedEmailTokenAttempt(query, redemption);
1666
1682
  return null;
1667
1683
  }
1668
- await this.db.query(`delete from auth.one_time_tokens where email = $1`, [row.email]);
1669
- const ures = await this.db.query(`update auth.users set email_confirmed_at = coalesce(email_confirmed_at, now()), last_sign_in_at = now()
1670
- where id = $1 returning *`, [row.user_id]);
1671
- return ures.rows[0] ?? null;
1684
+ const claimedToken = await this.lockAndDeleteEmailToken(query, candidate, redemption);
1685
+ return claimedToken ? this.confirmEmailTokenUser(query, claimedToken) : null;
1686
+ }
1687
+ async lockAndDeleteEmailToken(query, candidate, redemption) {
1688
+ await query(`select id from auth.users where id = $1 for update`, [candidate.user_id]);
1689
+ const claimedTokens = await query(`delete from auth.one_time_tokens
1690
+ where id = $1 and token = $2 and token_type = any($3::text[])
1691
+ and expires_at > now() and attempts < $4
1692
+ returning id, user_id, email`, [candidate.id, redemption.token, redemption.tokenTypes, AuthHandler.MAX_OTP_ATTEMPTS]);
1693
+ return claimedTokens.rows[0] ?? null;
1694
+ }
1695
+ async confirmEmailTokenUser(query, claimedToken) {
1696
+ await query(`delete from auth.one_time_tokens where email = $1`, [claimedToken.email]);
1697
+ const users = await query(`update auth.users set email_confirmed_at = coalesce(email_confirmed_at, now()), last_sign_in_at = now()
1698
+ where id = $1 returning *`, [claimedToken.user_id]);
1699
+ return users.rows[0] ?? null;
1700
+ }
1701
+ async findEmailTokenCandidate(query, redemption) {
1702
+ const candidates = await query(`select id, user_id, email from auth.one_time_tokens
1703
+ where token = $1 and token_type = any($2::text[])
1704
+ and ($3::text is null or email = $3) and expires_at > now()
1705
+ and attempts < $4`, [redemption.token, redemption.tokenTypes, redemption.email, AuthHandler.MAX_OTP_ATTEMPTS]);
1706
+ return candidates.rows[0] ?? null;
1707
+ }
1708
+ async recordFailedEmailTokenAttempt(query, redemption) {
1709
+ if (!redemption.email)
1710
+ return;
1711
+ await query(`update auth.one_time_tokens set attempts = attempts + 1
1712
+ where email = $1 and token_type = any($2::text[]) and expires_at > now()`, [redemption.email, redemption.tokenTypes]);
1713
+ await query(`delete from auth.one_time_tokens where email = $1 and attempts >= $2`, [redemption.email, AuthHandler.MAX_OTP_ATTEMPTS]);
1672
1714
  }
1673
1715
  async verifyToken(req) {
1674
1716
  const body = await req.json().catch(() => ({}));
1675
- if (!body.token)
1676
- return authError(400, "validation_failed", "token is required");
1717
+ const token = body.token ?? body.token_hash;
1718
+ if (!token)
1719
+ return authError(400, "validation_failed", "token or token_hash is required");
1677
1720
  if (body.type === "sms" || body.phone !== undefined) {
1678
1721
  const phone = normalizePhone(body.phone);
1679
- if (body.type !== "sms" || !phone || !/^\d{6,10}$/.test(body.token)) {
1722
+ if (body.type !== "sms" || !phone || !body.token || !/^\d{6,10}$/.test(body.token)) {
1680
1723
  return authError(400, "validation_failed", "phone, token, and type=sms are required");
1681
1724
  }
1682
1725
  const fingerprint = await keyedDigest(this.config.jwtSecret, "supacloud-lite:phone-fingerprint:v1", phone);
@@ -1695,7 +1738,7 @@ Or sign in with this link: ${link}`
1695
1738
  return json(200, session);
1696
1739
  }
1697
1740
  const types = body.type === "recovery" ? ["recovery"] : body.type === "magiclink" ? ["magiclink"] : ["otp", "magiclink"];
1698
- const user = await this.redeem(body.token, types, body.email);
1741
+ const user = await this.redeem(token, types, body.email);
1699
1742
  if (!user)
1700
1743
  return authError(403, "otp_expired", "Token has expired or is invalid");
1701
1744
  return json(200, await this.sessionFor(user));
@@ -1753,6 +1796,9 @@ Or sign in with this link: ${link}`
1753
1796
  }
1754
1797
  const idMatch = path.match(/^admin\/users\/([0-9a-f-]{36})$/);
1755
1798
  const exportMatch = path.match(/^admin\/users\/([0-9a-f-]{36})\/export$/);
1799
+ if (path === "admin/generate_link" && method === "POST") {
1800
+ return await this.generateAdminMagicLink(req);
1801
+ }
1756
1802
  if (path === "admin/audit" && method === "GET") {
1757
1803
  const res = await this.db.query(`select id, payload, created_at, ip_address from auth.audit_log_entries
1758
1804
  order by created_at desc limit 200`);
@@ -1823,6 +1869,50 @@ Or sign in with this link: ${link}`
1823
1869
  }
1824
1870
  return authError(404, "not_found", `unknown admin endpoint`);
1825
1871
  }
1872
+ async generateAdminMagicLink(req) {
1873
+ const body = await req.json().catch(() => ({}));
1874
+ if (body.type !== "magiclink") {
1875
+ return authError(422, "unsupported_link_type", "Only magiclink generation is supported");
1876
+ }
1877
+ if (!body.email)
1878
+ return authError(400, "validation_failed", "email is required");
1879
+ const email = body.email.toLowerCase().trim();
1880
+ const emailOtp = randomOtp(this.settings.otpLength);
1881
+ const hashedToken = await sha256Hex(randomToken(32));
1882
+ const expiry = `${this.settings.otpExpirySeconds} seconds`;
1883
+ const requestUrl = new URL(req.url);
1884
+ const redirectTo = resolveRedirect(requestUrl.searchParams.get("redirect_to") ?? body.redirect_to, this.config.siteUrl, this.config.uriAllowList, this.config.enforceRedirectAllowList);
1885
+ const user = await this.db.transaction(async (query) => {
1886
+ const result = await query(`insert into auth.users
1887
+ (aud, role, email, raw_app_meta_data, raw_user_meta_data)
1888
+ values ('authenticated', 'authenticated', $1, '{"provider":"email","providers":["email"]}', $2::jsonb)
1889
+ on conflict (email) do update set email = excluded.email
1890
+ returning *`, [email, JSON.stringify(body.data ?? {})]);
1891
+ const linkedUser = result.rows[0];
1892
+ await query(`insert into auth.identities (user_id, provider, provider_id, identity_data)
1893
+ values ($1::uuid, 'email', $1::text, $2::jsonb)
1894
+ on conflict (provider, provider_id) do nothing`, [linkedUser.id, JSON.stringify({ sub: linkedUser.id, email })]);
1895
+ await query(`delete from auth.one_time_tokens
1896
+ where email = $1 and token_type = any($2::text[])`, [email, "{otp,magiclink}"]);
1897
+ await query(`insert into auth.one_time_tokens (user_id, email, token_type, token, expires_at)
1898
+ values ($1, $2, 'otp', $3, now() + $5::interval),
1899
+ ($1, $2, 'magiclink', $4, now() + $5::interval)`, [linkedUser.id, email, emailOtp, hashedToken, expiry]);
1900
+ return linkedUser;
1901
+ });
1902
+ const actionUrl = new URL(`${this.config.apiUrl}/auth/v1/verify`);
1903
+ actionUrl.searchParams.set("token", hashedToken);
1904
+ actionUrl.searchParams.set("type", "magiclink");
1905
+ actionUrl.searchParams.set("redirect_to", redirectTo);
1906
+ await this.audit("user_magiclink_requested", { actorId: user.id, actorEmail: email, type: "admin" });
1907
+ return json(200, {
1908
+ ...this.userJson(user, [], await this.getUserIdentities(user.id)),
1909
+ action_link: actionUrl.toString(),
1910
+ email_otp: emailOtp,
1911
+ hashed_token: hashedToken,
1912
+ redirect_to: redirectTo,
1913
+ verification_type: "magiclink"
1914
+ });
1915
+ }
1826
1916
  async audit(action, opts = {}) {
1827
1917
  try {
1828
1918
  const payload = {
@@ -4148,7 +4238,7 @@ class Database {
4148
4238
  }
4149
4239
  }
4150
4240
  if (seedSql) {
4151
- const hash = await sha256Hex(seedSql);
4241
+ const hash = await sha256Hex2(seedSql);
4152
4242
  const seen = await this.engine.query(`select 1 from supabase_migrations.seed_files where path = 'supabase/seed.sql' and hash = $1`, [hash]);
4153
4243
  if (seen.rows.length === 0) {
4154
4244
  await this.engine.transaction(async (tx) => {
@@ -4441,7 +4531,7 @@ function quoteIdent(name) {
4441
4531
  function quoteLiteral(value) {
4442
4532
  return `'${value.replaceAll("'", "''")}'`;
4443
4533
  }
4444
- async function sha256Hex(text) {
4534
+ async function sha256Hex2(text) {
4445
4535
  const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));
4446
4536
  return [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
4447
4537
  }
@@ -8514,10 +8604,7 @@ async function createBackend(config = {}) {
8514
8604
  await failStartup(e);
8515
8605
  }
8516
8606
  const pgredis = await PgredisCache.create(db).catch(failStartup);
8517
- const now = Math.floor(Date.now() / 1000);
8518
- const tenYears = 10 * 365 * 24 * 3600;
8519
- const anonKey = await signJwt({ iss: "supabase", ref: "local", role: "anon", iat: now, exp: now + tenYears }, jwtSecret);
8520
- const serviceRoleKey = await signJwt({ iss: "supabase", ref: "local", role: "service_role", iat: now, exp: now + tenYears }, jwtSecret);
8607
+ const { anonKey, serviceRoleKey } = await mintProjectApiKeys(jwtSecret);
8521
8608
  const exposedSchemas = [...new Set(config.dbSchemas ?? ["public", "pgmq_public"])];
8522
8609
  const rest = new RestHandler(db, { exposedSchemas, maxRows: config.maxRows });
8523
8610
  const exposed = isNetworkExposed(config.host);
@@ -9625,6 +9712,8 @@ class S3StorageDriver {
9625
9712
  }
9626
9713
 
9627
9714
  // src/project-runtime.ts
9715
+ var RESET_INITIALIZATION_ERROR = 'db reset requires initialized state; run "supacloud-lite migrate" first';
9716
+ var RESET_INVALID_SECRETS_ERROR = "db reset requires a valid project secrets marker; restore the state before retrying";
9628
9717
  function resolveProjectPaths(options = {}) {
9629
9718
  const projectDir = resolve2(options.projectDir ?? process.cwd());
9630
9719
  const stateDir = resolvePath(projectDir, options.stateDir ?? process.env.SUPACLOUD_LITE_STATE_DIR ?? ".supacloud-lite");
@@ -9642,7 +9731,7 @@ async function assertResetPathsSafe(paths) {
9642
9731
  const stateDir = resolve2(paths.stateDir);
9643
9732
  if (stateDir === parse(stateDir).root)
9644
9733
  throw new Error("refusing to use the filesystem root as the state directory");
9645
- const stateInfo = await lstat(stateDir);
9734
+ const stateInfo = await requiredResetEntry(stateDir);
9646
9735
  if (!stateInfo.isDirectory() || stateInfo.isSymbolicLink()) {
9647
9736
  throw new Error(`refusing to reset through an invalid state directory: ${stateDir}`);
9648
9737
  }
@@ -9651,10 +9740,11 @@ async function assertResetPathsSafe(paths) {
9651
9740
  if (secretsFile !== join7(stateDir, "secrets.json")) {
9652
9741
  throw new Error(`refusing to reset a state directory with an invalid secrets marker path: ${secretsFile}`);
9653
9742
  }
9654
- const markerInfo = await lstat(secretsFile);
9743
+ const markerInfo = await requiredResetEntry(secretsFile);
9655
9744
  if (!markerInfo.isFile() || markerInfo.isSymbolicLink()) {
9656
- throw new Error(`refusing to reset a state directory without a valid secrets marker: ${stateDir}`);
9745
+ throw new Error(RESET_INVALID_SECRETS_ERROR);
9657
9746
  }
9747
+ await assertResetSecretsValid(secretsFile);
9658
9748
  const targets = [
9659
9749
  ...paths.dataDir ? [["database", paths.dataDir]] : [],
9660
9750
  ["storage", paths.storageDir]
@@ -9668,6 +9758,30 @@ async function assertResetPathsSafe(paths) {
9668
9758
  await assertResetTargetCanonical(stateDir, canonicalStateDir, target, label);
9669
9759
  }
9670
9760
  }
9761
+ async function requiredResetEntry(path) {
9762
+ try {
9763
+ return await lstat(path);
9764
+ } catch (error) {
9765
+ if (error.code === "ENOENT")
9766
+ throw new Error(RESET_INITIALIZATION_ERROR);
9767
+ throw error;
9768
+ }
9769
+ }
9770
+ async function assertResetSecretsValid(path) {
9771
+ let serializedSecrets;
9772
+ try {
9773
+ serializedSecrets = await readFile6(path, "utf8");
9774
+ } catch (error) {
9775
+ if (error.code === "ENOENT")
9776
+ throw new Error(RESET_INITIALIZATION_ERROR);
9777
+ throw error;
9778
+ }
9779
+ try {
9780
+ validateSecrets(JSON.parse(serializedSecrets));
9781
+ } catch {
9782
+ throw new Error(RESET_INVALID_SECRETS_ERROR);
9783
+ }
9784
+ }
9671
9785
  async function assertResetTargetCanonical(stateDir, canonicalStateDir, target, label) {
9672
9786
  let current = target;
9673
9787
  while (current !== stateDir) {
@@ -9750,13 +9864,7 @@ async function ensureProjectSecrets(paths) {
9750
9864
  });
9751
9865
  }
9752
9866
  async function mintProjectKeys(jwtSecret) {
9753
- const now = Math.floor(Date.now() / 1000);
9754
- const exp = now + 10 * 365 * 24 * 3600;
9755
- const common = { iss: "supabase", ref: "local", iat: now, exp };
9756
- return {
9757
- anonKey: await signJwt({ ...common, role: "anon" }, jwtSecret),
9758
- serviceRoleKey: await signJwt({ ...common, role: "service_role" }, jwtSecret)
9759
- };
9867
+ return await mintProjectApiKeys(jwtSecret);
9760
9868
  }
9761
9869
  async function createProjectBackend(options = {}) {
9762
9870
  const paths = resolveProjectPaths(options);
@@ -9928,7 +10036,7 @@ async function findEphemeralPort(host = "127.0.0.1") {
9928
10036
  }
9929
10037
 
9930
10038
  // src/snapshot.ts
9931
- import { chmod as chmod2, copyFile, link as link2, lstat as lstat2, mkdir as mkdir6, mkdtemp, readdir as readdir3, readFile as readFile7, rename as rename2, rm as rm3, writeFile as writeFile5 } from "fs/promises";
10039
+ import { chmod as chmod2, copyFile, lstat as lstat2, mkdir as mkdir6, mkdtemp, readdir as readdir3, readFile as readFile7, rename as rename2, rm as rm3, writeFile as writeFile5 } from "fs/promises";
9932
10040
  import { dirname as dirname4, join as join8, parse as parse2, relative as relative2, resolve as resolve3, sep as sep2 } from "path";
9933
10041
  import { create as createTar, extract as extractTar } from "tar";
9934
10042
  var SNAPSHOT_FORMAT = "supacloud-lite-snapshot";
@@ -10153,14 +10261,7 @@ async function stageDirectory(root, destination) {
10153
10261
  }
10154
10262
  async function stageFile(source, target) {
10155
10263
  await mkdir6(dirname4(target), { recursive: true });
10156
- try {
10157
- await link2(source, target);
10158
- } catch (error) {
10159
- const code = error.code;
10160
- if (code !== "EXDEV" && code !== "EPERM" && code !== "EACCES")
10161
- throw error;
10162
- await copyFile(source, target);
10163
- }
10264
+ await copyFile(source, target);
10164
10265
  }
10165
10266
  async function readManifest(payloadRoot) {
10166
10267
  let parsed;
@@ -10677,7 +10778,7 @@ Commands:
10677
10778
  keys print the anon key
10678
10779
  keys --service-role also print the privileged service_role key
10679
10780
  gen types emit Supabase-shaped TypeScript database types
10680
- db reset wipe database and storage, then re-run migrations
10781
+ db reset reset initialized database/storage and re-run migrations
10681
10782
  db diff print schema changes outside migrations
10682
10783
  db pull [name] write live schema changes as an applied migration
10683
10784
  snapshot create create a compressed database/storage/secrets snapshot
@@ -10686,6 +10787,8 @@ Commands:
10686
10787
  inspect show table rows and sizes
10687
10788
  version print the package version
10688
10789
 
10790
+ Fresh projects must run "supacloud-lite migrate" before "db reset".
10791
+
10689
10792
  Options:
10690
10793
  -p, --port <n> port (default 54321)
10691
10794
  --host <host> bind host (default 127.0.0.1)
package/dist/index.js CHANGED
@@ -4,6 +4,8 @@ var __require = import.meta.require;
4
4
  // src/runtime/jwt.ts
5
5
  var encoder = new TextEncoder;
6
6
  var decoder = new TextDecoder;
7
+ var PROJECT_API_KEY_ISSUED_AT = 1641769200;
8
+ var PROJECT_API_KEY_EXPIRES_AT = 4102444800;
7
9
  function bytesToBase64Url(bytes) {
8
10
  let binary = "";
9
11
  const chunk = 32768;
@@ -32,6 +34,18 @@ async function signJwt(claims, secret) {
32
34
  const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(data));
33
35
  return `${data}.${bytesToBase64Url(new Uint8Array(sig))}`;
34
36
  }
37
+ async function mintProjectApiKeys(jwtSecret) {
38
+ const commonClaims = {
39
+ iss: "supabase",
40
+ ref: "local",
41
+ iat: PROJECT_API_KEY_ISSUED_AT,
42
+ exp: PROJECT_API_KEY_EXPIRES_AT
43
+ };
44
+ return {
45
+ anonKey: await signJwt({ ...commonClaims, role: "anon" }, jwtSecret),
46
+ serviceRoleKey: await signJwt({ ...commonClaims, role: "service_role" }, jwtSecret)
47
+ };
48
+ }
35
49
  async function verifyJwt(token, secret) {
36
50
  const parts = token.split(".");
37
51
  if (parts.length !== 3)
@@ -71,7 +85,7 @@ function randomToken(bytes = 32) {
71
85
  // package.json
72
86
  var package_default = {
73
87
  name: "@supacloud/lite",
74
- version: "0.6.0",
88
+ version: "0.7.1",
75
89
  description: "Bun-native, single-project Supabase-compatible backend powered by PGlite",
76
90
  type: "module",
77
91
  license: "Apache-2.0",
@@ -1117,6 +1131,10 @@ function randomOtp(length) {
1117
1131
  }
1118
1132
  return code;
1119
1133
  }
1134
+ async function sha256Hex(value) {
1135
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
1136
+ return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
1137
+ }
1120
1138
  function normalizePhone(value) {
1121
1139
  if (typeof value !== "string")
1122
1140
  return null;
@@ -1655,33 +1673,58 @@ Or sign in with this link: ${link}`
1655
1673
  }
1656
1674
  static MAX_OTP_ATTEMPTS = 5;
1657
1675
  async redeem(token, types, email) {
1658
- const normalizedEmail = email?.toLowerCase().trim() ?? null;
1659
- const res = await this.db.query(`delete from auth.one_time_tokens
1660
- where token = $1 and token_type = any($2::text[])
1661
- and ($3::text is null or email = $3) and expires_at > now()
1662
- and attempts < $4
1663
- returning user_id, email`, [token, `{${types.join(",")}}`, normalizedEmail, AuthHandler.MAX_OTP_ATTEMPTS]);
1664
- const row = res.rows[0];
1665
- if (!row) {
1666
- if (normalizedEmail) {
1667
- await this.db.query(`update auth.one_time_tokens set attempts = attempts + 1
1668
- where email = $1 and token_type = any($2::text[]) and expires_at > now()`, [normalizedEmail, `{${types.join(",")}}`]);
1669
- await this.db.query(`delete from auth.one_time_tokens where email = $1 and attempts >= $2`, [normalizedEmail, AuthHandler.MAX_OTP_ATTEMPTS]);
1670
- }
1676
+ const redemption = {
1677
+ token,
1678
+ tokenTypes: `{${types.join(",")}}`,
1679
+ email: email?.toLowerCase().trim() ?? null
1680
+ };
1681
+ return this.db.transaction((query) => this.claimEmailToken(query, redemption));
1682
+ }
1683
+ async claimEmailToken(query, redemption) {
1684
+ const candidate = await this.findEmailTokenCandidate(query, redemption);
1685
+ if (!candidate) {
1686
+ await this.recordFailedEmailTokenAttempt(query, redemption);
1671
1687
  return null;
1672
1688
  }
1673
- await this.db.query(`delete from auth.one_time_tokens where email = $1`, [row.email]);
1674
- const ures = await this.db.query(`update auth.users set email_confirmed_at = coalesce(email_confirmed_at, now()), last_sign_in_at = now()
1675
- where id = $1 returning *`, [row.user_id]);
1676
- return ures.rows[0] ?? null;
1689
+ const claimedToken = await this.lockAndDeleteEmailToken(query, candidate, redemption);
1690
+ return claimedToken ? this.confirmEmailTokenUser(query, claimedToken) : null;
1691
+ }
1692
+ async lockAndDeleteEmailToken(query, candidate, redemption) {
1693
+ await query(`select id from auth.users where id = $1 for update`, [candidate.user_id]);
1694
+ const claimedTokens = await query(`delete from auth.one_time_tokens
1695
+ where id = $1 and token = $2 and token_type = any($3::text[])
1696
+ and expires_at > now() and attempts < $4
1697
+ returning id, user_id, email`, [candidate.id, redemption.token, redemption.tokenTypes, AuthHandler.MAX_OTP_ATTEMPTS]);
1698
+ return claimedTokens.rows[0] ?? null;
1699
+ }
1700
+ async confirmEmailTokenUser(query, claimedToken) {
1701
+ await query(`delete from auth.one_time_tokens where email = $1`, [claimedToken.email]);
1702
+ const users = await query(`update auth.users set email_confirmed_at = coalesce(email_confirmed_at, now()), last_sign_in_at = now()
1703
+ where id = $1 returning *`, [claimedToken.user_id]);
1704
+ return users.rows[0] ?? null;
1705
+ }
1706
+ async findEmailTokenCandidate(query, redemption) {
1707
+ const candidates = await query(`select id, user_id, email from auth.one_time_tokens
1708
+ where token = $1 and token_type = any($2::text[])
1709
+ and ($3::text is null or email = $3) and expires_at > now()
1710
+ and attempts < $4`, [redemption.token, redemption.tokenTypes, redemption.email, AuthHandler.MAX_OTP_ATTEMPTS]);
1711
+ return candidates.rows[0] ?? null;
1712
+ }
1713
+ async recordFailedEmailTokenAttempt(query, redemption) {
1714
+ if (!redemption.email)
1715
+ return;
1716
+ await query(`update auth.one_time_tokens set attempts = attempts + 1
1717
+ where email = $1 and token_type = any($2::text[]) and expires_at > now()`, [redemption.email, redemption.tokenTypes]);
1718
+ await query(`delete from auth.one_time_tokens where email = $1 and attempts >= $2`, [redemption.email, AuthHandler.MAX_OTP_ATTEMPTS]);
1677
1719
  }
1678
1720
  async verifyToken(req) {
1679
1721
  const body = await req.json().catch(() => ({}));
1680
- if (!body.token)
1681
- return authError(400, "validation_failed", "token is required");
1722
+ const token = body.token ?? body.token_hash;
1723
+ if (!token)
1724
+ return authError(400, "validation_failed", "token or token_hash is required");
1682
1725
  if (body.type === "sms" || body.phone !== undefined) {
1683
1726
  const phone = normalizePhone(body.phone);
1684
- if (body.type !== "sms" || !phone || !/^\d{6,10}$/.test(body.token)) {
1727
+ if (body.type !== "sms" || !phone || !body.token || !/^\d{6,10}$/.test(body.token)) {
1685
1728
  return authError(400, "validation_failed", "phone, token, and type=sms are required");
1686
1729
  }
1687
1730
  const fingerprint = await keyedDigest(this.config.jwtSecret, "supacloud-lite:phone-fingerprint:v1", phone);
@@ -1700,7 +1743,7 @@ Or sign in with this link: ${link}`
1700
1743
  return json(200, session);
1701
1744
  }
1702
1745
  const types = body.type === "recovery" ? ["recovery"] : body.type === "magiclink" ? ["magiclink"] : ["otp", "magiclink"];
1703
- const user = await this.redeem(body.token, types, body.email);
1746
+ const user = await this.redeem(token, types, body.email);
1704
1747
  if (!user)
1705
1748
  return authError(403, "otp_expired", "Token has expired or is invalid");
1706
1749
  return json(200, await this.sessionFor(user));
@@ -1758,6 +1801,9 @@ Or sign in with this link: ${link}`
1758
1801
  }
1759
1802
  const idMatch = path.match(/^admin\/users\/([0-9a-f-]{36})$/);
1760
1803
  const exportMatch = path.match(/^admin\/users\/([0-9a-f-]{36})\/export$/);
1804
+ if (path === "admin/generate_link" && method === "POST") {
1805
+ return await this.generateAdminMagicLink(req);
1806
+ }
1761
1807
  if (path === "admin/audit" && method === "GET") {
1762
1808
  const res = await this.db.query(`select id, payload, created_at, ip_address from auth.audit_log_entries
1763
1809
  order by created_at desc limit 200`);
@@ -1828,6 +1874,50 @@ Or sign in with this link: ${link}`
1828
1874
  }
1829
1875
  return authError(404, "not_found", `unknown admin endpoint`);
1830
1876
  }
1877
+ async generateAdminMagicLink(req) {
1878
+ const body = await req.json().catch(() => ({}));
1879
+ if (body.type !== "magiclink") {
1880
+ return authError(422, "unsupported_link_type", "Only magiclink generation is supported");
1881
+ }
1882
+ if (!body.email)
1883
+ return authError(400, "validation_failed", "email is required");
1884
+ const email = body.email.toLowerCase().trim();
1885
+ const emailOtp = randomOtp(this.settings.otpLength);
1886
+ const hashedToken = await sha256Hex(randomToken(32));
1887
+ const expiry = `${this.settings.otpExpirySeconds} seconds`;
1888
+ const requestUrl = new URL(req.url);
1889
+ const redirectTo = resolveRedirect(requestUrl.searchParams.get("redirect_to") ?? body.redirect_to, this.config.siteUrl, this.config.uriAllowList, this.config.enforceRedirectAllowList);
1890
+ const user = await this.db.transaction(async (query) => {
1891
+ const result = await query(`insert into auth.users
1892
+ (aud, role, email, raw_app_meta_data, raw_user_meta_data)
1893
+ values ('authenticated', 'authenticated', $1, '{"provider":"email","providers":["email"]}', $2::jsonb)
1894
+ on conflict (email) do update set email = excluded.email
1895
+ returning *`, [email, JSON.stringify(body.data ?? {})]);
1896
+ const linkedUser = result.rows[0];
1897
+ await query(`insert into auth.identities (user_id, provider, provider_id, identity_data)
1898
+ values ($1::uuid, 'email', $1::text, $2::jsonb)
1899
+ on conflict (provider, provider_id) do nothing`, [linkedUser.id, JSON.stringify({ sub: linkedUser.id, email })]);
1900
+ await query(`delete from auth.one_time_tokens
1901
+ where email = $1 and token_type = any($2::text[])`, [email, "{otp,magiclink}"]);
1902
+ await query(`insert into auth.one_time_tokens (user_id, email, token_type, token, expires_at)
1903
+ values ($1, $2, 'otp', $3, now() + $5::interval),
1904
+ ($1, $2, 'magiclink', $4, now() + $5::interval)`, [linkedUser.id, email, emailOtp, hashedToken, expiry]);
1905
+ return linkedUser;
1906
+ });
1907
+ const actionUrl = new URL(`${this.config.apiUrl}/auth/v1/verify`);
1908
+ actionUrl.searchParams.set("token", hashedToken);
1909
+ actionUrl.searchParams.set("type", "magiclink");
1910
+ actionUrl.searchParams.set("redirect_to", redirectTo);
1911
+ await this.audit("user_magiclink_requested", { actorId: user.id, actorEmail: email, type: "admin" });
1912
+ return json(200, {
1913
+ ...this.userJson(user, [], await this.getUserIdentities(user.id)),
1914
+ action_link: actionUrl.toString(),
1915
+ email_otp: emailOtp,
1916
+ hashed_token: hashedToken,
1917
+ redirect_to: redirectTo,
1918
+ verification_type: "magiclink"
1919
+ });
1920
+ }
1831
1921
  async audit(action, opts = {}) {
1832
1922
  try {
1833
1923
  const payload = {
@@ -4153,7 +4243,7 @@ class Database {
4153
4243
  }
4154
4244
  }
4155
4245
  if (seedSql) {
4156
- const hash = await sha256Hex(seedSql);
4246
+ const hash = await sha256Hex2(seedSql);
4157
4247
  const seen = await this.engine.query(`select 1 from supabase_migrations.seed_files where path = 'supabase/seed.sql' and hash = $1`, [hash]);
4158
4248
  if (seen.rows.length === 0) {
4159
4249
  await this.engine.transaction(async (tx) => {
@@ -4446,7 +4536,7 @@ function quoteIdent(name) {
4446
4536
  function quoteLiteral(value) {
4447
4537
  return `'${value.replaceAll("'", "''")}'`;
4448
4538
  }
4449
- async function sha256Hex(text) {
4539
+ async function sha256Hex2(text) {
4450
4540
  const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));
4451
4541
  return [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
4452
4542
  }
@@ -8358,10 +8448,7 @@ async function createBackend(config = {}) {
8358
8448
  await failStartup(e);
8359
8449
  }
8360
8450
  const pgredis = await PgredisCache.create(db).catch(failStartup);
8361
- const now = Math.floor(Date.now() / 1000);
8362
- const tenYears = 10 * 365 * 24 * 3600;
8363
- const anonKey = await signJwt({ iss: "supabase", ref: "local", role: "anon", iat: now, exp: now + tenYears }, jwtSecret);
8364
- const serviceRoleKey = await signJwt({ iss: "supabase", ref: "local", role: "service_role", iat: now, exp: now + tenYears }, jwtSecret);
8451
+ const { anonKey, serviceRoleKey } = await mintProjectApiKeys(jwtSecret);
8365
8452
  const exposedSchemas = [...new Set(config.dbSchemas ?? ["public", "pgmq_public"])];
8366
8453
  const rest = new RestHandler(db, { exposedSchemas, maxRows: config.maxRows });
8367
8454
  const exposed = isNetworkExposed(config.host);
@@ -9434,13 +9521,7 @@ async function ensureProjectSecrets(paths) {
9434
9521
  });
9435
9522
  }
9436
9523
  async function mintProjectKeys(jwtSecret) {
9437
- const now = Math.floor(Date.now() / 1000);
9438
- const exp = now + 10 * 365 * 24 * 3600;
9439
- const common = { iss: "supabase", ref: "local", iat: now, exp };
9440
- return {
9441
- anonKey: await signJwt({ ...common, role: "anon" }, jwtSecret),
9442
- serviceRoleKey: await signJwt({ ...common, role: "service_role" }, jwtSecret)
9443
- };
9524
+ return await mintProjectApiKeys(jwtSecret);
9444
9525
  }
9445
9526
  async function createProjectBackend(options = {}) {
9446
9527
  const paths = resolveProjectPaths(options);
@@ -9611,7 +9692,7 @@ async function findEphemeralPort(host = "127.0.0.1") {
9611
9692
  return port;
9612
9693
  }
9613
9694
  // src/snapshot.ts
9614
- import { chmod as chmod2, copyFile, link as link2, lstat as lstat2, mkdir as mkdir5, mkdtemp, readdir as readdir3, readFile as readFile7, rename as rename2, rm as rm3, writeFile as writeFile4 } from "fs/promises";
9695
+ import { chmod as chmod2, copyFile, lstat as lstat2, mkdir as mkdir5, mkdtemp, readdir as readdir3, readFile as readFile7, rename as rename2, rm as rm3, writeFile as writeFile4 } from "fs/promises";
9615
9696
  import { dirname as dirname4, join as join7, parse as parse2, relative as relative2, resolve as resolve3, sep as sep2 } from "path";
9616
9697
  import { create as createTar, extract as extractTar } from "tar";
9617
9698
  var SNAPSHOT_FORMAT = "supacloud-lite-snapshot";
@@ -9836,14 +9917,7 @@ async function stageDirectory(root, destination) {
9836
9917
  }
9837
9918
  async function stageFile(source, target) {
9838
9919
  await mkdir5(dirname4(target), { recursive: true });
9839
- try {
9840
- await link2(source, target);
9841
- } catch (error) {
9842
- const code = error.code;
9843
- if (code !== "EXDEV" && code !== "EPERM" && code !== "EACCES")
9844
- throw error;
9845
- await copyFile(source, target);
9846
- }
9920
+ await copyFile(source, target);
9847
9921
  }
9848
9922
  async function readManifest(payloadRoot) {
9849
9923
  let parsed;
@@ -1 +1 @@
1
- {"version":3,"file":"project-runtime.d.ts","sourceRoot":"","sources":["../src/project-runtime.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0B,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAEtF,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAE3E,OAAO,EAAqB,KAAK,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAIrF,OAAO,EAAmB,KAAK,sBAAsB,EAAE,MAAM,gCAAgC,CAAA;AAC7F,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAElE,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,cAAc,CAAC,EAAE,wBAAwB,CAAA;IACzC,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,EAAE,CAAC,EAAE,sBAAsB,CAAA;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAChC;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,oBAAoB,CAAA;IAC7B,MAAM,EAAE,aAAa,CAAA;IACrB,KAAK,EAAE,YAAY,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,cAAc,EAAE,MAAM,CAAA;IACtB,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,cAAc,CAAA;CAC/B;AAED,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,QAAQ,GAAG,IAAI,GAAG,QAAQ,CAAA;AAC9D,MAAM,MAAM,wBAAwB,GAAG,OAAO,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAA;AAExE,MAAM,WAAW,oBAAqB,SAAQ,cAAc;IAC1D,MAAM,EAAE,aAAa,CAAA;IACrB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B;AAED,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,qBAA0B,GAAG,YAAY,CAiBrF;AAED,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA4B7E;AA4CD,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,CAqCvF;AAED,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAAC,CAQ7G;AAED,wBAAsB,oBAAoB,CAAC,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAAC,cAAc,CAAC,CAqEvG;AAED,wBAAgB,qBAAqB,CAAC,KAAK,CAAC,EAAE,wBAAwB,GAAG,wBAAwB,CAIhG;AAiBD,wBAAsB,kBAAkB,CAAC,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAiC3G"}
1
+ {"version":3,"file":"project-runtime.d.ts","sourceRoot":"","sources":["../src/project-runtime.ts"],"names":[],"mappings":"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"}
@@ -119,10 +119,16 @@ export declare class AuthHandler {
119
119
  /** Max wrong guesses for a one-time code before its tokens are invalidated. */
120
120
  private static readonly MAX_OTP_ATTEMPTS;
121
121
  private redeem;
122
+ private claimEmailToken;
123
+ private lockAndDeleteEmailToken;
124
+ private confirmEmailTokenUser;
125
+ private findEmailTokenCandidate;
126
+ private recordFailedEmailTokenAttempt;
122
127
  private verifyToken;
123
128
  private redeemPhoneOtp;
124
129
  private verifyLink;
125
130
  private admin;
131
+ private generateAdminMagicLink;
126
132
  /**
127
133
  * Append a security event to auth.audit_log_entries (GoTrue-compatible
128
134
  * payload). Best-effort: a logging failure never breaks the request.
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/runtime/auth/handler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/runtime/auth/handler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAW,MAAM,mBAAmB,CAAA;AAE1D,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAAc,SAAS,EAAE,MAAM,aAAa,CAAA;AAEhF,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAGnE,OAAO,EAAyB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAA;AAExE,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAG7C,wDAAwD;AACxD,MAAM,WAAW,UAAU;IACzB,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAA;IACjB,2FAA2F;IAC3F,MAAM,EAAE,MAAM,CAAA;IACd,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAA;IACf,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAA;IACjB,yGAAyG;IACzG,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,uEAAuE;IACvE,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAA;IACd,+DAA+D;IAC/D,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAA;IAC5B,oFAAoF;IACpF,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAA;IACpD,gFAAgF;IAChF,UAAU,CAAC,EAAE,OAAO,KAAK,CAAA;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB;;;;OAIG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAA;IAClC;;;OAGG;IACH,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB;;;OAGG;IACH,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;CACjC;AAED,UAAU,OAAO;IACf,EAAE,EAAE,MAAM,CAAA;IACV,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,kBAAkB,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAA;IACxC,eAAe,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAA;IACrC,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IACjD,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IAClD,UAAU,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAA;IAChC,UAAU,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAA;IAChC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,kBAAkB,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAA;IACxC,YAAY,EAAE,OAAO,GAAG,IAAI,CAAA;IAC5B,YAAY,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAA;IAClC,UAAU,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAA;CACjC;AAgID,wEAAwE;AACxE,qBAAa,WAAW;IAYpB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,MAAM;IAZhB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,yBAAyB,CAAS;IAC1D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAM;IACrD,OAAO,CAAC,KAAK,CAAc;IAC3B,8EAA8E;IAC9E,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,eAAe,CAA2B;IAClD,OAAO,CAAC,wBAAwB,CAA6B;IAC7D,OAAO,CAAC,QAAQ,CAAQ;IAExB,YACU,EAAE,EAAE,QAAQ,EACZ,MAAM,EAAE,UAAU,EAa3B;IAED;;;OAGG;IACH,OAAO,CAAC,KAAK;IAKb,OAAO,CAAC,QAAQ;IAUhB,4EAA4E;IACtE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAM1B;YAEa,oCAAoC;IAgBlD,8FAA8F;IACxF,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAuD3E;YAIa,MAAM;YAiEN,KAAK;YAwCL,OAAO;YAMP,UAAU;YAgEV,MAAM;YAwBN,UAAU;YA4CV,OAAO;YAqBP,eAAe;YAqDf,eAAe;YAmCf,SAAS;YAwBT,kBAAkB;IAShC,OAAO,CAAC,mBAAmB;YASb,eAAe;YAgBf,YAAY;YAaZ,qBAAqB;YAgBrB,qBAAqB;IAUnC,OAAO,CAAC,kBAAkB;IAM1B,OAAO,CAAC,kBAAkB;YAQZ,YAAY;IAM1B,+EAA+E;IAC/E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAI;YAE9B,MAAM;YASN,eAAe;YAWf,uBAAuB;YAkBvB,qBAAqB;YAUrB,uBAAuB;YAcvB,6BAA6B;YAa7B,WAAW;YAsCX,cAAc;YAyDd,UAAU;YAuBV,KAAK;YA6FL,sBAAsB;IAuEpC;;;OAGG;YACW,KAAK;IAqBnB;;;;;OAKG;YACW,UAAU;IA2CxB;;;;;;;;;OASG;YACW,SAAS;YAqBT,YAAY;YAyDZ,eAAe;YAqBf,YAAY;YAyCZ,cAAc;YAWd,cAAc;IAmB5B,oFAAoF;YACtE,iBAAiB;YAuBjB,kBAAkB;YAqBlB,iBAAiB;YAUjB,uBAAuB;IASrC,OAAO,CAAC,kBAAkB;YAoBZ,cAAc;YAOd,gBAAgB;IAM9B,wEAAwE;IACxE,QAAQ,CACN,CAAC,EAAE,OAAO,EACV,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAO,EACvC,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAO,GACzC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAmBzB;IAED,+EAA+E;YACjE,gBAAgB;YAUhB,UAAU;CAqDzB"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAE9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,gBAAgB,EAAqB,MAAM,wBAAwB,CAAA;AAG5E,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAE3C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAIrD,OAAO,EAAE,eAAe,EAAwB,MAAM,uBAAuB,CAAA;AAC7E,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAoB,MAAM,kBAAkB,CAAA;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACzD,OAAO,KAAK,EAAE,aAAa,EAAU,aAAa,EAA6B,MAAM,YAAY,CAAA;AAGjG,cAAc,YAAY,CAAA;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AACzD,OAAO,EAAE,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC9D,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EAAE,SAAS,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AACzE,OAAO,EAAE,cAAc,EAAE,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAC9E,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACxD,OAAO,EAAE,gBAAgB,EAAE,KAAK,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAClG,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,KAAK,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACjG,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC5D,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC/D,OAAO,EAAE,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAC/E,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACtF,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3D;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACnC,oGAAoG;IACpG,KAAK,EAAE,OAAO,KAAK,CAAA;IACnB,mFAAmF;IACnF,EAAE,EAAE,QAAQ,CAAA;IACZ,6EAA6E;IAC7E,QAAQ,EAAE,cAAc,CAAA;IACxB,6EAA6E;IAC7E,SAAS,EAAE,gBAAgB,CAAA;IAC3B,qEAAqE;IACrE,QAAQ,EAAE,eAAe,CAAA;IACzB,mCAAmC;IACnC,IAAI,EAAE,WAAW,CAAA;IACjB,wDAAwD;IACxD,GAAG,EAAE,UAAU,CAAA;IACf,6EAA6E;IAC7E,SAAS,EAAE,gBAAgB,CAAA;IAC3B,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAA;IACf,+CAA+C;IAC/C,cAAc,EAAE,MAAM,CAAA;IACtB,oFAAoF;IACpF,SAAS,EAAE,MAAM,CAAA;IACjB,kEAAkE;IAClE,IAAI,EAAE,SAAS,CAAA;IACf,4FAA4F;IAC5F,KAAK,EAAE,WAAW,GAAG,IAAI,CAAA;IACzB,gHAAgH;IAChH,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAA;IACzB,8CAA8C;IAC9C,OAAO,EAAE,CAAC,UAAU,EAAE,aAAa,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IAC7E,gGAAgG;IAChG,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B;AAWD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,aAAa,CAAC,MAAM,GAAE,aAAkB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA8W7F"}
@@ -2,6 +2,10 @@
2
2
  * Minimal HS256 JWT implementation on WebCrypto.
3
3
  * Works in Node (>=18) and browsers - no dependencies.
4
4
  */
5
+ export interface ProjectApiKeys {
6
+ anonKey: string;
7
+ serviceRoleKey: string;
8
+ }
5
9
  /** Standard + Supabase/GoTrue claims carried in an access token. Open-ended: any extra claims are preserved. */
6
10
  export interface JwtClaims {
7
11
  [key: string]: unknown;
@@ -22,6 +26,8 @@ export interface JwtClaims {
22
26
  }
23
27
  /** Sign claims as an HS256 JWT. */
24
28
  export declare function signJwt(claims: JwtClaims, secret: string): Promise<string>;
29
+ /** Keep project API keys canonical so CLI output and restarted runtimes remain byte-identical. */
30
+ export declare function mintProjectApiKeys(jwtSecret: string): Promise<ProjectApiKeys>;
25
31
  /** Verifies signature and expiry. Returns claims, or null when invalid. */
26
32
  export declare function verifyJwt(token: string, secret: string): Promise<JwtClaims | null>;
27
33
  /** Decode without verification (for introspection/debugging only). */
@@ -1 +1 @@
1
- {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../src/runtime/jwt.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAiCH,gHAAgH;AAChH,MAAM,WAAW,SAAS;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;IACtB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qFAAqF;IACrF,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,2FAA2F;IAC3F,GAAG,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC9C;AAED,mCAAmC;AACnC,wBAAsB,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAOhF;AAED,2EAA2E;AAC3E,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAuBxF;AAED,sEAAsE;AACtE,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAQzD;AAED,0GAA0G;AAC1G,wBAAgB,WAAW,CAAC,KAAK,SAAK,GAAG,MAAM,CAI9C"}
1
+ {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../src/runtime/jwt.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,cAAc,EAAE,MAAM,CAAA;CACvB;AA8BD,gHAAgH;AAChH,MAAM,WAAW,SAAS;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;IACtB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qFAAqF;IACrF,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,2FAA2F;IAC3F,GAAG,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC9C;AAED,mCAAmC;AACnC,wBAAsB,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAOhF;AAED,kGAAkG;AAClG,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAWnF;AAED,2EAA2E;AAC3E,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAuBxF;AAED,sEAAsE;AACtE,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAQzD;AAED,0GAA0G;AAC1G,wBAAgB,WAAW,CAAC,KAAK,SAAK,GAAG,MAAM,CAI9C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/lite",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "Bun-native, single-project Supabase-compatible backend powered by PGlite",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",