@supacloud/lite 0.24.0 → 0.25.0

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,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.25.0](https://github.com/vibeunion/supacloud/compare/supacloud-lite-v0.24.0...supacloud-lite-v0.25.0) (2026-10-10)
4
+
5
+
6
+ ### Features
7
+
8
+ * **governance:** enforce strict user and platform type contracts ([#1754](https://github.com/vibeunion/supacloud/issues/1754)) ([b22f889](https://github.com/vibeunion/supacloud/commit/b22f88919c349f088848028f13b15e44994e0d9c))
9
+
3
10
  ## [0.24.0](https://github.com/vibeunion/supacloud/compare/supacloud-lite-v0.23.0...supacloud-lite-v0.24.0) (2026-10-10)
4
11
 
5
12
 
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.24.0",
11
+ version: "0.25.0",
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",
@@ -160,6 +160,8 @@ async function verifyJwt(token, secret) {
160
160
  if (parts.length !== 3)
161
161
  return null;
162
162
  const [header, payload, signature] = parts;
163
+ if (!header || !payload || !signature)
164
+ return null;
163
165
  try {
164
166
  const head = JSON.parse(decoder.decode(base64UrlToBytes(header)));
165
167
  if (head.alg !== "HS256")
@@ -189,7 +191,7 @@ var SUPACLOUD_LITE_VERSION = package_default.version;
189
191
  function globToRegExp(pattern) {
190
192
  let out = "";
191
193
  for (let i = 0;i < pattern.length; i++) {
192
- const c = pattern[i];
194
+ const c = pattern.charAt(i);
193
195
  if (c === "*") {
194
196
  if (pattern[i + 1] === "*") {
195
197
  out += ".*";
@@ -242,11 +244,11 @@ var PRESETS = {
242
244
  userInfoUrl: "https://openidconnect.googleapis.com/v1/userinfo",
243
245
  scopes: "openid email profile",
244
246
  profileMap: (r) => ({
245
- id: r.sub,
246
- email: r.email,
247
- emailVerified: r.email_verified === true,
248
- name: r.name,
249
- metadata: { avatar_url: r.picture, full_name: r.name }
247
+ id: typeof r["sub"] === "string" ? r["sub"] : "",
248
+ ...typeof r["email"] === "string" ? { email: r["email"] } : {},
249
+ emailVerified: r["email_verified"] === true,
250
+ ...typeof r["name"] === "string" ? { name: r["name"] } : {},
251
+ metadata: { avatar_url: r["picture"], full_name: r["name"] }
250
252
  })
251
253
  },
252
254
  github: {
@@ -254,7 +256,12 @@ var PRESETS = {
254
256
  tokenUrl: "https://github.com/login/oauth/access_token",
255
257
  userInfoUrl: "https://api.github.com/user",
256
258
  scopes: "read:user user:email",
257
- profileMap: (r) => ({ id: String(r.id), email: r.email, name: r.name ?? r.login, metadata: { avatar_url: r.avatar_url, user_name: r.login } })
259
+ profileMap: (r) => ({
260
+ id: String(r["id"]),
261
+ ...typeof r["email"] === "string" ? { email: r["email"] } : {},
262
+ ...typeof r["name"] === "string" || typeof r["login"] === "string" ? { name: typeof r["name"] === "string" ? r["name"] : r["login"] } : {},
263
+ metadata: { avatar_url: r["avatar_url"], user_name: r["login"] }
264
+ })
258
265
  }
259
266
  };
260
267
  function resolveProvider(name, cfg) {
@@ -273,7 +280,11 @@ function resolveProvider(name, cfg) {
273
280
  tokenUrl,
274
281
  userInfoUrl,
275
282
  scopes: cfg.scopes ?? preset.scopes ?? "",
276
- profileMap: cfg.profileMap ?? preset.profileMap ?? ((r) => ({ id: String(r.id ?? r.sub), email: r.email, name: r.name }))
283
+ profileMap: cfg.profileMap ?? preset.profileMap ?? ((r) => ({
284
+ id: String(r["id"] ?? r["sub"]),
285
+ ...typeof r["email"] === "string" ? { email: r["email"] } : {},
286
+ ...typeof r["name"] === "string" ? { name: r["name"] } : {}
287
+ }))
277
288
  };
278
289
  }
279
290
 
@@ -969,41 +980,41 @@ var DEFAULT_AUTH_SETTINGS = {
969
980
  };
970
981
  function sanitize(raw) {
971
982
  const s = { ...DEFAULT_AUTH_SETTINGS };
972
- if (typeof raw.disableSignup === "boolean")
973
- s.disableSignup = raw.disableSignup;
974
- if (typeof raw.anonymousUsers === "boolean")
975
- s.anonymousUsers = raw.anonymousUsers;
976
- if (typeof raw.autoconfirm === "boolean")
977
- s.autoconfirm = raw.autoconfirm;
978
- if (typeof raw.minPasswordLength === "number" && Number.isFinite(raw.minPasswordLength)) {
979
- s.minPasswordLength = Math.max(4, Math.min(72, Math.floor(raw.minPasswordLength)));
980
- }
981
- if (Array.isArray(raw.disabledProviders)) {
982
- s.disabledProviders = raw.disabledProviders.filter((p) => typeof p === "string").slice(0, 50);
983
- }
984
- if (typeof raw.otpLength === "number" && Number.isFinite(raw.otpLength)) {
985
- s.otpLength = Math.max(6, Math.min(10, Math.floor(raw.otpLength)));
986
- }
987
- if (typeof raw.otpExpirySeconds === "number" && Number.isFinite(raw.otpExpirySeconds) && raw.otpExpirySeconds > 0) {
988
- s.otpExpirySeconds = Math.floor(raw.otpExpirySeconds);
989
- }
990
- if (typeof raw.smsEnabled === "boolean")
991
- s.smsEnabled = raw.smsEnabled;
992
- if (typeof raw.smsSignupEnabled === "boolean")
993
- s.smsSignupEnabled = raw.smsSignupEnabled;
994
- if (typeof raw.smsOtpCooldownSeconds === "number" && Number.isFinite(raw.smsOtpCooldownSeconds)) {
995
- s.smsOtpCooldownSeconds = Math.max(0, Math.min(86400, Math.floor(raw.smsOtpCooldownSeconds)));
996
- }
997
- if (typeof raw.smsTemplate === "string" && raw.smsTemplate.length > 0 && raw.smsTemplate.length <= 1000 && /\{\{\s*\.Code\s*\}\}/.test(raw.smsTemplate)) {
998
- s.smsTemplate = raw.smsTemplate;
999
- }
1000
- if (typeof raw.maxEnrolledFactors === "number" && Number.isFinite(raw.maxEnrolledFactors) && raw.maxEnrolledFactors > 0) {
1001
- s.maxEnrolledFactors = Math.floor(raw.maxEnrolledFactors);
1002
- }
1003
- if (typeof raw.totpEnrollEnabled === "boolean")
1004
- s.totpEnrollEnabled = raw.totpEnrollEnabled;
1005
- if (typeof raw.totpVerifyEnabled === "boolean")
1006
- s.totpVerifyEnabled = raw.totpVerifyEnabled;
983
+ if (typeof raw["disableSignup"] === "boolean")
984
+ s.disableSignup = raw["disableSignup"];
985
+ if (typeof raw["anonymousUsers"] === "boolean")
986
+ s.anonymousUsers = raw["anonymousUsers"];
987
+ if (typeof raw["autoconfirm"] === "boolean")
988
+ s.autoconfirm = raw["autoconfirm"];
989
+ if (typeof raw["minPasswordLength"] === "number" && Number.isFinite(raw["minPasswordLength"])) {
990
+ s.minPasswordLength = Math.max(4, Math.min(72, Math.floor(raw["minPasswordLength"])));
991
+ }
992
+ if (Array.isArray(raw["disabledProviders"])) {
993
+ s.disabledProviders = raw["disabledProviders"].filter((p) => typeof p === "string").slice(0, 50);
994
+ }
995
+ if (typeof raw["otpLength"] === "number" && Number.isFinite(raw["otpLength"])) {
996
+ s.otpLength = Math.max(6, Math.min(10, Math.floor(raw["otpLength"])));
997
+ }
998
+ if (typeof raw["otpExpirySeconds"] === "number" && Number.isFinite(raw["otpExpirySeconds"]) && raw["otpExpirySeconds"] > 0) {
999
+ s.otpExpirySeconds = Math.floor(raw["otpExpirySeconds"]);
1000
+ }
1001
+ if (typeof raw["smsEnabled"] === "boolean")
1002
+ s.smsEnabled = raw["smsEnabled"];
1003
+ if (typeof raw["smsSignupEnabled"] === "boolean")
1004
+ s.smsSignupEnabled = raw["smsSignupEnabled"];
1005
+ if (typeof raw["smsOtpCooldownSeconds"] === "number" && Number.isFinite(raw["smsOtpCooldownSeconds"])) {
1006
+ s.smsOtpCooldownSeconds = Math.max(0, Math.min(86400, Math.floor(raw["smsOtpCooldownSeconds"])));
1007
+ }
1008
+ if (typeof raw["smsTemplate"] === "string" && raw["smsTemplate"].length > 0 && raw["smsTemplate"].length <= 1000 && /\{\{\s*\.Code\s*\}\}/.test(raw["smsTemplate"])) {
1009
+ s.smsTemplate = raw["smsTemplate"];
1010
+ }
1011
+ if (typeof raw["maxEnrolledFactors"] === "number" && Number.isFinite(raw["maxEnrolledFactors"]) && raw["maxEnrolledFactors"] > 0) {
1012
+ s.maxEnrolledFactors = Math.floor(raw["maxEnrolledFactors"]);
1013
+ }
1014
+ if (typeof raw["totpEnrollEnabled"] === "boolean")
1015
+ s.totpEnrollEnabled = raw["totpEnrollEnabled"];
1016
+ if (typeof raw["totpVerifyEnabled"] === "boolean")
1017
+ s.totpVerifyEnabled = raw["totpVerifyEnabled"];
1007
1018
  return s;
1008
1019
  }
1009
1020
  async function loadAuthSettings(db, defaults = {}) {
@@ -1044,7 +1055,7 @@ class RateLimiter {
1044
1055
  const cutoff = now - rule.windowMs;
1045
1056
  const recent = (this.hits.get(bucketKey) ?? []).filter((t) => t > cutoff);
1046
1057
  if (recent.length >= rule.limit) {
1047
- const retryMs = recent[0] + rule.windowMs - now;
1058
+ const retryMs = (recent[0] ?? now) + rule.windowMs - now;
1048
1059
  return Math.max(1, Math.ceil(retryMs / 1000));
1049
1060
  }
1050
1061
  recent.push(now);
@@ -1876,13 +1887,14 @@ Or sign in with this link: ${link}`
1876
1887
  const body = await req.json().catch(() => ({}));
1877
1888
  if (!body.email)
1878
1889
  return authError(400, "validation_failed", "email is required");
1890
+ const email = body.email;
1879
1891
  const hashed = body.password ? await hashPassword(body.password) : null;
1880
1892
  const created = await this.db.transaction(async (query) => {
1881
1893
  const res = await query(`insert into auth.users
1882
1894
  (aud, role, email, encrypted_password, email_confirmed_at, raw_app_meta_data, raw_user_meta_data)
1883
1895
  values ('authenticated', 'authenticated', $1, $2, case when $3 then now() else null end, $4, $5)
1884
1896
  returning *`, [
1885
- body.email.toLowerCase().trim(),
1897
+ email.toLowerCase().trim(),
1886
1898
  hashed,
1887
1899
  body.email_confirm ?? true,
1888
1900
  JSON.stringify({ provider: "email", providers: ["email"], ...body.app_metadata ?? {} }),
@@ -2545,11 +2557,11 @@ function installDenoShim() {
2545
2557
  }
2546
2558
  function takeCapturedHandler() {
2547
2559
  const h = captured.handler;
2548
- captured.handler = undefined;
2560
+ delete captured.handler;
2549
2561
  return h;
2550
2562
  }
2551
2563
  function resetCapturedHandler() {
2552
- captured.handler = undefined;
2564
+ delete captured.handler;
2553
2565
  }
2554
2566
 
2555
2567
  // src/runtime/functions/edge-runtime-shim.ts
@@ -2573,7 +2585,7 @@ function installEdgeRuntimeShim() {
2573
2585
  }
2574
2586
  };
2575
2587
  installed = runtime;
2576
- globalThis.EdgeRuntime = runtime;
2588
+ globalThis["EdgeRuntime"] = runtime;
2577
2589
  }
2578
2590
  function runWithBackgroundTasks(options, fn) {
2579
2591
  installEdgeRuntimeShim();
@@ -2945,9 +2957,13 @@ class FunctionsHandler {
2945
2957
  const env = capabilities?.secrets ? filterSecretsEnv(this.env, capabilities.secrets) : this.env;
2946
2958
  const invoke = () => this.invoke(entry.handler, request, ctx, env);
2947
2959
  const inner = () => runWithDenoEnv(env, () => runWithPgredisCache(this.pgredis, invoke));
2948
- const run = capabilities?.outboundHosts ? () => runWithFetchPolicy(capabilities.outboundHosts, inner) : inner;
2949
- const invokeWithBackground = () => runWithBackgroundTasks({ allowed: capabilities?.background !== false, timeoutMs: limits?.waitUntilTimeoutMs }, run);
2950
- const res = timeoutMs !== undefined ? await this.withTimeout(name, timeoutMs, invokeWithBackground, abort) : await invokeWithBackground();
2960
+ const outboundHosts = capabilities?.outboundHosts;
2961
+ const run = outboundHosts ? () => runWithFetchPolicy(outboundHosts, inner) : inner;
2962
+ const invokeWithBackground = () => runWithBackgroundTasks({
2963
+ allowed: capabilities?.background !== false,
2964
+ ...limits?.waitUntilTimeoutMs === undefined ? {} : { timeoutMs: limits.waitUntilTimeoutMs }
2965
+ }, run);
2966
+ const res = timeoutMs !== undefined && abort !== undefined ? await this.withTimeout(name, timeoutMs, invokeWithBackground, abort) : await invokeWithBackground();
2951
2967
  if (!(res instanceof Response)) {
2952
2968
  return json2(500, { error: `function "${name}" did not return a Response` });
2953
2969
  }
@@ -3015,6 +3031,8 @@ function withCountedBody(req, limit) {
3015
3031
  controller.enqueue(chunk);
3016
3032
  }
3017
3033
  });
3034
+ if (!req.body)
3035
+ return new Request(req);
3018
3036
  return new Request(req, { body: req.body.pipeThrough(counter), duplex: "half" });
3019
3037
  }
3020
3038
  function withResponseLimit(name, res, limit) {
@@ -6362,7 +6380,7 @@ async function createPgliteEngine(dataDir, options = {}) {
6362
6380
  let pg;
6363
6381
  try {
6364
6382
  pg = new PGlite({
6365
- dataDir,
6383
+ ...dataDir === undefined ? {} : { dataDir },
6366
6384
  extensions,
6367
6385
  ...standaloneAssets ? {
6368
6386
  pgliteWasmModule: standaloneAssets.pgliteWasmModule,
@@ -6391,7 +6409,7 @@ async function createPgliteEngine(dataDir, options = {}) {
6391
6409
  return {
6392
6410
  async query(sql, params) {
6393
6411
  const res = await pg.query(sql, params);
6394
- return { rows: res.rows, affectedRows: res.affectedRows };
6412
+ return { rows: res.rows, ...res.affectedRows === undefined ? {} : { affectedRows: res.affectedRows } };
6395
6413
  },
6396
6414
  async exec(sql) {
6397
6415
  await pg.exec(sql);
@@ -6401,7 +6419,7 @@ async function createPgliteEngine(dataDir, options = {}) {
6401
6419
  return fn({
6402
6420
  async query(sql, params) {
6403
6421
  const res = await tx.query(sql, params);
6404
- return { rows: res.rows, affectedRows: res.affectedRows };
6422
+ return { rows: res.rows, ...res.affectedRows === undefined ? {} : { affectedRows: res.affectedRows } };
6405
6423
  },
6406
6424
  async exec(sql) {
6407
6425
  await tx.exec(sql);
@@ -6430,8 +6448,8 @@ function getStandaloneAssets() {
6430
6448
  function withEmbeddedBundle(extension, bundlePath) {
6431
6449
  return {
6432
6450
  ...extension,
6433
- setup: async (pg, emscriptenOpts, clientOnly) => ({
6434
- ...await extension.setup(pg, emscriptenOpts, clientOnly),
6451
+ setup: async (...args) => ({
6452
+ ...await extension.setup(...args),
6435
6453
  bundlePath
6436
6454
  })
6437
6455
  };
@@ -6655,12 +6673,17 @@ class Database {
6655
6673
  order by kcu.constraint_name, kcu.ordinal_position`, [schema]);
6656
6674
  const uniqByTable = new Map;
6657
6675
  for (const u of uniq.rows) {
6658
- if (!uniqByTable.has(u.table_name))
6659
- uniqByTable.set(u.table_name, new Map);
6660
- const byConstraint = uniqByTable.get(u.table_name);
6661
- if (!byConstraint.has(u.constraint_name))
6662
- byConstraint.set(u.constraint_name, []);
6663
- byConstraint.get(u.constraint_name).push(u.column_name);
6676
+ let byConstraint = uniqByTable.get(u.table_name);
6677
+ if (!byConstraint) {
6678
+ byConstraint = new Map;
6679
+ uniqByTable.set(u.table_name, byConstraint);
6680
+ }
6681
+ let columns = byConstraint.get(u.constraint_name);
6682
+ if (!columns) {
6683
+ columns = [];
6684
+ byConstraint.set(u.constraint_name, columns);
6685
+ }
6686
+ columns.push(u.column_name);
6664
6687
  }
6665
6688
  const fks = await this.engine.query(`select
6666
6689
  tc.constraint_name,
@@ -6681,22 +6704,27 @@ class Database {
6681
6704
  order by tc.constraint_name, kcu.ordinal_position`, [schema]);
6682
6705
  const pkSet = new Map;
6683
6706
  for (const pk of pks.rows) {
6684
- if (!pkSet.has(pk.table_name))
6685
- pkSet.set(pk.table_name, new Set);
6686
- pkSet.get(pk.table_name).add(pk.column_name);
6707
+ let columns = pkSet.get(pk.table_name);
6708
+ if (!columns) {
6709
+ columns = new Set;
6710
+ pkSet.set(pk.table_name, columns);
6711
+ }
6712
+ columns.add(pk.column_name);
6687
6713
  }
6688
6714
  const tables = new Map;
6689
6715
  for (const c of cols.rows) {
6690
- if (!tables.has(c.table_name)) {
6691
- tables.set(c.table_name, {
6716
+ let table = tables.get(c.table_name);
6717
+ if (!table) {
6718
+ table = {
6692
6719
  schema,
6693
6720
  name: c.table_name,
6694
6721
  columns: [],
6695
6722
  primaryKey: [...pkSet.get(c.table_name) ?? []],
6696
6723
  uniqueKeys: [...uniqByTable.get(c.table_name)?.values() ?? []]
6697
- });
6724
+ };
6725
+ tables.set(c.table_name, table);
6698
6726
  }
6699
- tables.get(c.table_name).columns.push({
6727
+ table.columns.push({
6700
6728
  name: c.column_name,
6701
6729
  udtName: c.udt_name,
6702
6730
  isNullable: c.is_nullable === "YES",
@@ -6868,12 +6896,15 @@ function parseIdentityArgs(identity) {
6868
6896
  parts.push(current);
6869
6897
  return parts.map((part) => {
6870
6898
  const tokens = part.trim().split(/\s+/);
6871
- while (tokens.length > 1 && ["IN", "OUT", "INOUT", "VARIADIC"].includes(tokens[0])) {
6899
+ while (tokens.length > 1 && ["IN", "OUT", "INOUT", "VARIADIC"].includes(tokens[0] ?? "")) {
6872
6900
  tokens.shift();
6873
6901
  }
6902
+ const first = tokens[0];
6903
+ if (first === undefined || first === "")
6904
+ throw new Error("invalid function identity argument");
6874
6905
  if (tokens.length === 1)
6875
- return { name: "", type: tokens[0] };
6876
- const name = tokens[0].replace(/^"|"$/g, "");
6906
+ return { name: "", type: first };
6907
+ const name = first.replace(/^"|"$/g, "");
6877
6908
  return { name, type: tokens.slice(1).join(" ") };
6878
6909
  });
6879
6910
  }
@@ -7185,6 +7216,8 @@ class RealtimeEngine {
7185
7216
  const state = this.presence.get(topic);
7186
7217
  if (state?.has(channel.presenceKey)) {
7187
7218
  const metas = state.get(channel.presenceKey);
7219
+ if (!metas)
7220
+ return;
7188
7221
  state.delete(channel.presenceKey);
7189
7222
  if (state.size === 0)
7190
7223
  this.presence.delete(topic);
@@ -7479,7 +7512,7 @@ class ApiError extends Error {
7479
7512
  status;
7480
7513
  body;
7481
7514
  constructor(status, body) {
7482
- super(typeof body.message === "string" ? body.message : JSON.stringify(body));
7515
+ super(typeof body["message"] === "string" ? body["message"] : JSON.stringify(body));
7483
7516
  this.status = status;
7484
7517
  this.body = body;
7485
7518
  }
@@ -7857,6 +7890,8 @@ class QueryBuilder {
7857
7890
  const cells = columns.map((c) => {
7858
7891
  if (!(c in row)) {
7859
7892
  const col = tinfo.columns.find((x) => x.name === c);
7893
+ if (!col)
7894
+ throw new ApiError(400, { code: "PGRST204", message: `Column ${c} does not exist` });
7860
7895
  return opts.missingDefault && col.hasDefault ? "default" : "null";
7861
7896
  }
7862
7897
  return this.pushParam(params, tinfo, c, row[c]);
@@ -7976,6 +8011,8 @@ class QueryBuilder {
7976
8011
  });
7977
8012
  } else {
7978
8013
  const j = rel.junction;
8014
+ if (!j)
8015
+ throw new Error("many-to-many relationship is missing its junction table");
7979
8016
  const jAlias = this.nextAlias();
7980
8017
  const joinConds = j.fkToTarget.srcColumns.map((src, i) => `${quoteIdent(jAlias)}.${quoteIdent(src)} = ${quoteIdent(childAlias)}.${quoteIdent(j.fkToTarget.tgtColumns[i])}`);
7981
8018
  fromClause = `${this.qualify(rel.targetTable)} as ${quoteIdent(childAlias)} join ${this.qualify(j.table)} as ${quoteIdent(jAlias)} on ${joinConds.join(" and ")}`;
@@ -8241,6 +8278,8 @@ function renderFilter(f, alias) {
8241
8278
  case "phfts":
8242
8279
  case "wfts": {
8243
8280
  const fn = { fts: "to_tsquery", plfts: "plainto_tsquery", phfts: "phraseto_tsquery", wfts: "websearch_to_tsquery" }[f.op];
8281
+ if (!fn)
8282
+ throw new Error(`Unsupported full-text operator: ${f.op}`);
8244
8283
  expr = f.ftsConfig ? `${colExpr} @@ ${fn}(${quoteLiteral(f.ftsConfig)}, ${quoteLiteral(f.value)})` : `${colExpr} @@ ${fn}(${quoteLiteral(f.value)})`;
8245
8284
  break;
8246
8285
  }
@@ -8535,9 +8574,10 @@ class RestHandler {
8535
8574
  if (!opts.returning) {
8536
8575
  return new Response(null, { status: opts.status === 201 ? 201 : 204, headers: countHeader });
8537
8576
  }
8538
- return this.dataResponse(rows, {
8577
+ const responseRows = rows ?? [];
8578
+ return this.dataResponse(responseRows, {
8539
8579
  status: opts.status,
8540
- count: opts.prefer.count !== undefined ? rows.length : null,
8580
+ count: opts.prefer.count !== undefined ? responseRows.length : null,
8541
8581
  offset: 0,
8542
8582
  wantsObject: opts.wantsObject,
8543
8583
  wantsCsv: opts.wantsCsv,
@@ -8648,7 +8688,7 @@ class RestHandler {
8648
8688
  if (item.name === "*") {
8649
8689
  exprs.push(`${quoteIdent(alias)}.*`);
8650
8690
  } else {
8651
- const out = item.alias ?? item.name.split(/->>|->/).pop().trim();
8691
+ const out = item.alias ?? (item.name.split(/->>|->/).pop() ?? item.name).trim();
8652
8692
  const cast = item.cast ? `::${sanitizeCast(item.cast)}` : "";
8653
8693
  exprs.push(`${renderColumnExpr(alias, item.name)}${cast} as ${quoteIdent(out)}`);
8654
8694
  }
@@ -8902,7 +8942,10 @@ function applyResize(image, metadata, options) {
8902
8942
  image.resize(options.width);
8903
8943
  return;
8904
8944
  }
8905
- const proportionalWidth = Math.max(1, Math.round(metadata.width * options.height / metadata.height));
8945
+ const height = options.height;
8946
+ if (height === undefined)
8947
+ return;
8948
+ const proportionalWidth = Math.max(1, Math.round(metadata.width * height / metadata.height));
8906
8949
  image.resize(proportionalWidth);
8907
8950
  }
8908
8951
  async function transformImage(source, options, knownSourceSize) {
@@ -9059,6 +9102,8 @@ class ImageTransformCache {
9059
9102
  if (oldestKey === undefined)
9060
9103
  break;
9061
9104
  const oldest = this.entries.get(oldestKey);
9105
+ if (!oldest)
9106
+ break;
9062
9107
  this.entries.delete(oldestKey);
9063
9108
  this.cachedBytes -= oldest.size;
9064
9109
  }
@@ -9908,8 +9953,9 @@ class StorageHandler {
9908
9953
  ...[...folders].map((name) => ({ name, row: null })),
9909
9954
  ...Object.entries(files).map(([name, row]) => ({ name, row }))
9910
9955
  ];
9911
- if (body.search)
9912
- entries = entries.filter((e) => e.name.includes(body.search));
9956
+ const search = body.search;
9957
+ if (search)
9958
+ entries = entries.filter((e) => e.name.includes(search));
9913
9959
  const column = body.sortBy?.column ?? "name";
9914
9960
  const asc = (body.sortBy?.order ?? "asc") === "asc";
9915
9961
  entries.sort((a, b) => {
@@ -10059,6 +10105,8 @@ function parseSizeLimit(v) {
10059
10105
  if (!m)
10060
10106
  throw new StorageValidationError(`invalid file_size_limit: ${v}`);
10061
10107
  const mult = { b: 1, kb: 1024, mb: 1024 ** 2, gb: 1024 ** 3 }[(m[2] ?? "b").toLowerCase()];
10108
+ if (mult === undefined)
10109
+ throw new StorageValidationError(`invalid file_size_limit: ${v}`);
10062
10110
  return Math.floor(parseFloat(m[1]) * mult);
10063
10111
  }
10064
10112
  function objectMetadata(size, contentType, cacheControl) {
@@ -10357,7 +10405,7 @@ class NetService {
10357
10405
  const res = await guardedFetch(this.fetchImpl, row.url, {
10358
10406
  method: row.method,
10359
10407
  headers,
10360
- body: hasBody ? row.body ?? undefined : undefined,
10408
+ ...hasBody && row.body !== null ? { body: row.body } : {},
10361
10409
  signal: controller.signal
10362
10410
  });
10363
10411
  status = res.status;
@@ -10376,9 +10424,9 @@ class NetService {
10376
10424
  id: row.id,
10377
10425
  method: row.method,
10378
10426
  url: row.url,
10379
- status: status ?? undefined,
10427
+ ...status === null ? {} : { status },
10380
10428
  timedOut,
10381
- error: errorMsg ?? undefined
10429
+ ...errorMsg === null ? {} : { error: errorMsg }
10382
10430
  });
10383
10431
  }
10384
10432
  async record(id, status, contentType, respHeaders, content, timedOut, errorMsg) {
@@ -10831,16 +10879,16 @@ class GraphqlHandler {
10831
10879
  throw new ApiError(415, { message: "GraphQL requests require application/json" });
10832
10880
  }
10833
10881
  const body = JSON.parse(await boundedBody(request, this.maxBytes));
10834
- if (!isObject(body) || typeof body.query !== "string" || !body.query.trim() || body.variables != null && !isObject(body.variables) || body.extensions != null && !isObject(body.extensions) || body.operationName != null && typeof body.operationName !== "string") {
10882
+ if (!isObject(body) || typeof body["query"] !== "string" || !body["query"].trim() || body["variables"] != null && !isObject(body["variables"]) || body["extensions"] != null && !isObject(body["extensions"]) || body["operationName"] != null && typeof body["operationName"] !== "string") {
10835
10883
  throw new ApiError(400, { message: "Invalid GraphQL request" });
10836
10884
  }
10837
- operationName = body.operationName ?? undefined;
10885
+ operationName = body["operationName"] ?? undefined;
10838
10886
  const result = await this.db.withContext(context, async (query) => {
10839
10887
  await query(`select set_config('statement_timeout', $1, true)`, [String(this.timeoutMs)]);
10840
10888
  await query(`select set_config('search_path', $1, true)`, [
10841
10889
  this.schemas.map((schema) => `"${schema.replaceAll('"', '""')}"`).join(",")
10842
10890
  ]);
10843
- return query("select graphql.resolve($1::text, $2::jsonb, $3::text, $4::jsonb) as result", [body.query, JSON.stringify(body.variables ?? {}), body.operationName ?? null, JSON.stringify(body.extensions ?? {})]);
10891
+ return query("select graphql.resolve($1::text, $2::jsonb, $3::text, $4::jsonb) as result", [body["query"], JSON.stringify(body["variables"] ?? {}), body["operationName"] ?? null, JSON.stringify(body["extensions"] ?? {})]);
10844
10892
  });
10845
10893
  const row = result.rows[0];
10846
10894
  if (result.rows.length !== 1 || !isRecord(row) || !isGraphqlEnvelope(row["result"])) {
@@ -10938,9 +10986,12 @@ async function generateTypes(db, schema = "public") {
10938
10986
  const views = new Map;
10939
10987
  for (const c of cols) {
10940
10988
  const target = c.table_kind === "r" ? tables : views;
10941
- if (!target.has(c.table_name))
10942
- target.set(c.table_name, []);
10943
- target.get(c.table_name).push(c);
10989
+ let columns = target.get(c.table_name);
10990
+ if (!columns) {
10991
+ columns = [];
10992
+ target.set(c.table_name, columns);
10993
+ }
10994
+ columns.push(c);
10944
10995
  }
10945
10996
  const tsType = (udt, nullable) => {
10946
10997
  const base = mapType(udt, enumNames, schema);
@@ -11182,9 +11233,12 @@ async function snapshotSchema(db, schema = "public") {
11182
11233
  const constraints = new Map;
11183
11234
  const constraintIndexOids = new Set;
11184
11235
  for (const c of cons.rows) {
11185
- if (!constraints.has(c.table))
11186
- constraints.set(c.table, new Map);
11187
- constraints.get(c.table).set(c.name, c.def);
11236
+ let tableConstraints = constraints.get(c.table);
11237
+ if (!tableConstraints) {
11238
+ tableConstraints = new Map;
11239
+ constraints.set(c.table, tableConstraints);
11240
+ }
11241
+ tableConstraints.set(c.name, c.def);
11188
11242
  if (c.conindid)
11189
11243
  constraintIndexOids.add(c.conindid);
11190
11244
  }
@@ -11198,9 +11252,12 @@ async function snapshotSchema(db, schema = "public") {
11198
11252
  for (const r of idx.rows) {
11199
11253
  if (constraintIndexOids.has(r.indexrelid))
11200
11254
  continue;
11201
- if (!indexes.has(r.table))
11202
- indexes.set(r.table, new Map);
11203
- indexes.get(r.table).set(r.name, r.def);
11255
+ let tableIndexes = indexes.get(r.table);
11256
+ if (!tableIndexes) {
11257
+ tableIndexes = new Map;
11258
+ indexes.set(r.table, tableIndexes);
11259
+ }
11260
+ tableIndexes.set(r.name, r.def);
11204
11261
  }
11205
11262
  const en = await db.query(`select t.typname as name, array_agg(e.enumlabel order by e.enumsortorder) as labels
11206
11263
  from pg_type t join pg_enum e on e.enumtypid = t.oid
@@ -11299,7 +11356,7 @@ function schemasEqual(left, right) {
11299
11356
  if (value instanceof Map)
11300
11357
  return [...value].sort(([a], [b]) => String(a).localeCompare(String(b))).map(([key, entry]) => [key, canonical(entry)]);
11301
11358
  if (Array.isArray(value))
11302
- return value.map(canonical);
11359
+ return value.map((entry) => canonical(entry));
11303
11360
  if (value !== null && typeof value === "object")
11304
11361
  return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, entry]) => [key, canonical(entry)]));
11305
11362
  return value;
@@ -11911,7 +11968,11 @@ async function revokeReplicationGrants(tx) {
11911
11968
  }
11912
11969
  }
11913
11970
  async function grantReplicationAllowlist(tx, tables) {
11914
- const schemas = uniqueSorted(tables.map((table) => table.schema));
11971
+ const schemas = uniqueSorted(tables.map((table) => {
11972
+ if (!table.schema)
11973
+ throw new Error(`publication table ${table.name} has no schema`);
11974
+ return table.schema;
11975
+ }));
11915
11976
  for (const schema of schemas) {
11916
11977
  await tx.exec(`GRANT USAGE ON SCHEMA ${quoteIdentifier(schema)} TO ${POWERSYNC_REPLICATION_ROLE}`);
11917
11978
  }
@@ -12015,6 +12076,8 @@ function normalizeCidr(cidr) {
12015
12076
  if (!match)
12016
12077
  throw new Error(`invalid PowerSync client CIDR: ${cidr}`);
12017
12078
  const address = match[1];
12079
+ if (!address)
12080
+ throw new Error(`invalid PowerSync client CIDR: ${cidr}`);
12018
12081
  const family = isIP(address);
12019
12082
  const prefix = Number(match[2]);
12020
12083
  if (family === 0 || prefix < 0 || prefix > (family === 4 ? 32 : 128)) {
@@ -12121,12 +12184,12 @@ class PgWireClient {
12121
12184
  if (code === 0) {} else if (code === 3) {
12122
12185
  if (!needPassword())
12123
12186
  return;
12124
- this.socket.write(message(112, cstring(opts.password)));
12187
+ this.socket.write(message(112, cstring(opts.password ?? "")));
12125
12188
  } else if (code === 5) {
12126
12189
  if (!needPassword())
12127
12190
  return;
12128
12191
  const salt = payload.subarray(4, 8);
12129
- const inner = md5Hex(Buffer.from(opts.password + opts.user, "utf8"));
12192
+ const inner = md5Hex(Buffer.from((opts.password ?? "") + opts.user, "utf8"));
12130
12193
  const token = "md5" + md5Hex(Buffer.concat([Buffer.from(inner, "utf8"), salt]));
12131
12194
  this.socket.write(message(112, cstring(token)));
12132
12195
  } else if (code === 10) {
@@ -12148,6 +12211,10 @@ class PgWireClient {
12148
12211
  reject(new Error("SCRAM: server nonce does not extend client nonce"));
12149
12212
  return;
12150
12213
  }
12214
+ if (!attrs.s || !attrs.i || !opts.password) {
12215
+ reject(new Error("SCRAM: incomplete server challenge"));
12216
+ return;
12217
+ }
12151
12218
  const salt = Buffer.from(attrs.s, "base64");
12152
12219
  const iterations = parseInt(attrs.i, 10);
12153
12220
  const saltedPassword = pbkdf2Sync(opts.password, salt, iterations, 32, "sha256");
@@ -12564,7 +12631,7 @@ function toPgArrayLiteral(array) {
12564
12631
  // src/runtime/node/native/engine.ts
12565
12632
  var DEFAULT_PG_VERSION = "17.7.0";
12566
12633
  var POSTGRES_MIRROR_URL_ERROR = "SUPACLOUD_LITE_POSTGRES_MIRROR must be an absolute HTTPS URL or a loopback HTTP URL";
12567
- var NATIVE_POSTGRES_MAJOR = DEFAULT_PG_VERSION.split(".")[0];
12634
+ var NATIVE_POSTGRES_MAJOR = DEFAULT_PG_VERSION.split(".")[0] ?? "17";
12568
12635
  function nativePostgresMajor(installDir = process.env.SUPACLOUD_LITE_POSTGRES_DIR) {
12569
12636
  if (!installDir)
12570
12637
  return NATIVE_POSTGRES_MAJOR;
@@ -12831,15 +12898,18 @@ ${detail}` : " (no output)"}
12831
12898
  connect,
12832
12899
  onClose: async () => {
12833
12900
  removeExitHandler?.();
12834
- await stopPostgres(postgres, () => postgresExited);
12835
- rmSync(socketDirectory, { recursive: true, force: true });
12901
+ if (postgres)
12902
+ await stopPostgres(postgres, () => postgresExited);
12903
+ if (socketDirectory)
12904
+ rmSync(socketDirectory, { recursive: true, force: true });
12836
12905
  await releaseLock();
12837
12906
  }
12838
12907
  });
12839
12908
  } catch (error) {
12840
12909
  removeExitHandler?.();
12841
- if (postgres)
12842
- await stopPostgres(postgres, () => postgres.exitCode !== null);
12910
+ const processToStop = postgres;
12911
+ if (processToStop)
12912
+ await stopPostgres(processToStop, () => processToStop.exitCode !== null);
12843
12913
  if (socketDirectory)
12844
12914
  rmSync(socketDirectory, { recursive: true, force: true });
12845
12915
  await releaseLock();
@@ -12923,6 +12993,8 @@ async function withDelta(opts, baseline, action) {
12923
12993
  const liveSnap = await snapshotSchema(tx, schema);
12924
12994
  const ddl = diffSchemas(shadowSnap, liveSnap, schema);
12925
12995
  try {
12996
+ if (!shadow)
12997
+ throw new Error("Shadow database was closed before schema replay");
12926
12998
  if (ddl.length)
12927
12999
  await shadow.db.exec(ddl.join(`
12928
13000
  `));
@@ -14526,7 +14598,10 @@ ${details}`, { cause: buildThrow });
14526
14598
  throw new Error(buildOutput.logs.map((item) => item.message).join(`
14527
14599
  `) || `failed to bundle ${entryPath}`);
14528
14600
  }
14529
- return buildOutput.outputs[0].path;
14601
+ const output = buildOutput.outputs[0];
14602
+ if (!output)
14603
+ throw new Error(`bundle produced no output for ${entryPath}`);
14604
+ return output.path;
14530
14605
  } catch (buildError) {
14531
14606
  try {
14532
14607
  await rm3(outDir, { recursive: true, force: true });
@@ -14612,7 +14687,8 @@ async function loadFunctionsUnlocked(projectDir, options, mode) {
14612
14687
  const dir = join7(root, name);
14613
14688
  if (!(await stat2(dir)).isDirectory())
14614
14689
  continue;
14615
- const candidates = options[name]?.entrypoint ? [join7(projectDir, options[name].entrypoint)] : ["index.ts", "index.tsx", "index.js", "index.mjs"].map((f) => join7(dir, f));
14690
+ const entrypoint = options[name]?.entrypoint;
14691
+ const candidates = entrypoint ? [join7(projectDir, entrypoint)] : ["index.ts", "index.tsx", "index.js", "index.mjs"].map((f) => join7(dir, f));
14616
14692
  for (const path of candidates) {
14617
14693
  try {
14618
14694
  await stat2(path);