@supacloud/lite 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.2](https://github.com/zuohuadong/supacloud/compare/supacloud-lite-v0.5.1...supacloud-lite-v0.5.2) (2026-07-30)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **lite:** release shadow backend after lock conflicts ([c44e7a3](https://github.com/zuohuadong/supacloud/commit/c44e7a3c17f3a2d1ae9f285f7f9ad5187ebd178b))
9
+
10
+ ## [0.5.1](https://github.com/zuohuadong/supacloud/compare/supacloud-lite-v0.5.0...supacloud-lite-v0.5.1) (2026-07-30)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * make Lite Windows shutdown verification reliable ([#650](https://github.com/zuohuadong/supacloud/issues/650)) ([c208006](https://github.com/zuohuadong/supacloud/commit/c208006aa49839562dda5b0fe1419f183ceb5b12))
16
+ * stabilize Lite shutdown and Studio interactions ([a4d6f19](https://github.com/zuohuadong/supacloud/commit/a4d6f19db6c4aa71702fccec83bfbdd2a0c8b2b1))
17
+
3
18
  ## [0.5.0](https://github.com/zuohuadong/supacloud/compare/supacloud-lite-v0.4.0...supacloud-lite-v0.5.0) (2026-07-29)
4
19
 
5
20
 
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.5.0",
11
+ version: "0.5.2",
12
12
  description: "Bun-native, single-project Supabase-compatible backend powered by PGlite",
13
13
  type: "module",
14
14
  license: "Apache-2.0",
@@ -3290,9 +3290,57 @@ function pickTag(text, base) {
3290
3290
  }
3291
3291
 
3292
3292
  // src/vendor/tinbase/db/pglite-engine.ts
3293
- import { mkdir, open, readFile, unlink } from "fs/promises";
3293
+ import { mkdir, open, unlink as unlink2 } from "fs/promises";
3294
3294
  import { dirname, resolve } from "path";
3295
3295
 
3296
+ // src/vendor/tinbase/db/data-dir-lock.ts
3297
+ import { readFile, unlink } from "fs/promises";
3298
+ async function recoverStaleDataDirLock(lockPath) {
3299
+ const lockState = await inspectDataDirLock(lockPath);
3300
+ if (lockState.kind !== "stale")
3301
+ return lockState;
3302
+ try {
3303
+ await unlink(lockPath);
3304
+ return { kind: "missing" };
3305
+ } catch (error) {
3306
+ if (error.code === "ENOENT")
3307
+ return { kind: "missing" };
3308
+ throw error;
3309
+ }
3310
+ }
3311
+ async function readDataDirLockOwner(lockPath) {
3312
+ const lockState = await inspectDataDirLock(lockPath);
3313
+ return lockState.kind === "active" || lockState.kind === "stale" ? lockState.owner : null;
3314
+ }
3315
+ async function inspectDataDirLock(lockPath) {
3316
+ let contents;
3317
+ try {
3318
+ contents = await readFile(lockPath, "utf8");
3319
+ } catch (error) {
3320
+ return error.code === "ENOENT" ? { kind: "missing" } : { kind: "unreadable" };
3321
+ }
3322
+ const owner = parseDataDirLockOwner(contents);
3323
+ if (!owner)
3324
+ return { kind: "unreadable" };
3325
+ return isProcessAlive(owner.pid) ? { kind: "active", owner } : { kind: "stale", owner };
3326
+ }
3327
+ function parseDataDirLockOwner(contents) {
3328
+ try {
3329
+ const value = JSON.parse(contents);
3330
+ return typeof value.pid === "number" && Number.isInteger(value.pid) && value.pid > 0 && typeof value.nonce === "string" ? { pid: value.pid, nonce: value.nonce } : null;
3331
+ } catch {
3332
+ return null;
3333
+ }
3334
+ }
3335
+ function isProcessAlive(pid) {
3336
+ try {
3337
+ process.kill(pid, 0);
3338
+ return true;
3339
+ } catch (error) {
3340
+ return error.code === "EPERM";
3341
+ }
3342
+ }
3343
+
3296
3344
  // src/standalone-assets-protocol.ts
3297
3345
  var STANDALONE_PGLITE_ASSETS = Symbol.for("supacloud-lite.pglite-standalone-assets");
3298
3346
 
@@ -3427,31 +3475,27 @@ async function acquireDataDirLock(dataDir) {
3427
3475
  return;
3428
3476
  released = true;
3429
3477
  await handle?.close();
3430
- const owner = await readLockOwner(lockPath);
3478
+ const owner = await readDataDirLockOwner(lockPath);
3431
3479
  if (owner?.nonce !== nonce)
3432
3480
  return;
3433
- await unlink(lockPath).catch((error) => {
3481
+ await unlink2(lockPath).catch((error) => {
3434
3482
  if (error.code !== "ENOENT")
3435
3483
  throw error;
3436
3484
  });
3437
3485
  };
3438
3486
  }
3439
3487
  async function createDataDirLock(absoluteDataDir, lockPath, nonce) {
3440
- for (let attempt = 0;attempt < 2; attempt++) {
3488
+ for (let attempt = 0;attempt < 3; attempt++) {
3441
3489
  try {
3442
3490
  return await writeDataDirLock(lockPath, nonce);
3443
3491
  } catch (error) {
3444
3492
  if (error.code !== "EEXIST")
3445
3493
  throw error;
3446
- const owner = await readLockOwner(lockPath);
3447
- if (owner && isProcessAlive(owner.pid))
3448
- throw lockInUseError(absoluteDataDir, owner.pid);
3449
- if (!owner || attempt > 0)
3494
+ const lockState = await recoverStaleDataDirLock(lockPath);
3495
+ if (lockState.kind === "active")
3496
+ throw lockInUseError(absoluteDataDir, lockState.owner.pid);
3497
+ if (lockState.kind === "unreadable")
3450
3498
  throw unreadableLockError(lockPath);
3451
- await unlink(lockPath).catch((unlinkError) => {
3452
- if (unlinkError.code !== "ENOENT")
3453
- throw unlinkError;
3454
- });
3455
3499
  }
3456
3500
  }
3457
3501
  throw unreadableLockError(lockPath);
@@ -3464,7 +3508,7 @@ async function writeDataDirLock(lockPath, nonce) {
3464
3508
  return handle;
3465
3509
  } catch (error) {
3466
3510
  await handle.close().catch(() => {});
3467
- await unlink(lockPath).catch(() => {});
3511
+ await unlink2(lockPath).catch(() => {});
3468
3512
  throw error;
3469
3513
  }
3470
3514
  }
@@ -3474,22 +3518,6 @@ function lockInUseError(dataDir, pid) {
3474
3518
  function unreadableLockError(lockPath) {
3475
3519
  return new Error(`PGlite data directory has an unreadable lock: ${lockPath}. Confirm no SupaCloud Lite process is using it, then remove the lock manually.`);
3476
3520
  }
3477
- async function readLockOwner(lockPath) {
3478
- try {
3479
- const value = JSON.parse(await readFile(lockPath, "utf8"));
3480
- return typeof value.pid === "number" && Number.isInteger(value.pid) && value.pid > 0 && typeof value.nonce === "string" ? { pid: value.pid, nonce: value.nonce } : null;
3481
- } catch {
3482
- return null;
3483
- }
3484
- }
3485
- function isProcessAlive(pid) {
3486
- try {
3487
- process.kill(pid, 0);
3488
- return true;
3489
- } catch (error) {
3490
- return error.code === "EPERM";
3491
- }
3492
- }
3493
3521
 
3494
3522
  // src/vendor/tinbase/db/database.ts
3495
3523
  var DEFAULT_SEARCH_PATH_SQL = `set search_path to "$user", public, extensions`;
@@ -8035,18 +8063,27 @@ async function computeDbDiff(opts) {
8035
8063
  engine: opts.makeShadowEngine ? await opts.makeShadowEngine() : undefined,
8036
8064
  migrations: opts.migrations
8037
8065
  });
8038
- const live = await createBackend({
8039
- engine: opts.liveEngine,
8040
- dataDir: opts.liveEngine ? undefined : opts.liveDataDir,
8041
- migrations: opts.migrations
8042
- });
8066
+ let live;
8067
+ let operationFailed = false;
8043
8068
  try {
8069
+ live = await createBackend({
8070
+ engine: opts.liveEngine,
8071
+ dataDir: opts.liveEngine ? undefined : opts.liveDataDir,
8072
+ migrations: opts.migrations
8073
+ });
8044
8074
  const shadowSnap = await snapshotSchema(shadow.db, schema);
8045
8075
  const liveSnap = await snapshotSchema(live.db, schema);
8046
8076
  return diffSchemas(shadowSnap, liveSnap, schema);
8077
+ } catch (error) {
8078
+ operationFailed = true;
8079
+ throw error;
8047
8080
  } finally {
8048
- await shadow.close();
8049
- await live.close();
8081
+ try {
8082
+ await closeBackends(live, shadow);
8083
+ } catch (error) {
8084
+ if (!operationFailed)
8085
+ throw error;
8086
+ }
8050
8087
  }
8051
8088
  }
8052
8089
  async function pullSchema(opts) {
@@ -8055,12 +8092,14 @@ async function pullSchema(opts) {
8055
8092
  engine: opts.makeShadowEngine ? await opts.makeShadowEngine() : undefined,
8056
8093
  migrations: opts.migrations
8057
8094
  });
8058
- const live = await createBackend({
8059
- engine: opts.liveEngine,
8060
- dataDir: opts.liveEngine ? undefined : opts.liveDataDir,
8061
- migrations: opts.migrations
8062
- });
8095
+ let live;
8096
+ let operationFailed = false;
8063
8097
  try {
8098
+ live = await createBackend({
8099
+ engine: opts.liveEngine,
8100
+ dataDir: opts.liveEngine ? undefined : opts.liveDataDir,
8101
+ migrations: opts.migrations
8102
+ });
8064
8103
  const ddl = diffSchemas(await snapshotSchema(shadow.db, schema), await snapshotSchema(live.db, schema), schema);
8065
8104
  if (ddl.length === 0)
8066
8105
  return { ddl, version: null, path: null };
@@ -8079,11 +8118,24 @@ async function pullSchema(opts) {
8079
8118
  await live.db.query(`insert into supabase_migrations.schema_migrations (version, name, statements)
8080
8119
  values ($1, $2, $3) on conflict (version) do nothing`, [stamp, `${stamp}_${name}`, [body]]);
8081
8120
  return { ddl, version: stamp, path };
8121
+ } catch (error) {
8122
+ operationFailed = true;
8123
+ throw error;
8082
8124
  } finally {
8083
- await shadow.close();
8084
- await live.close();
8125
+ try {
8126
+ await closeBackends(live, shadow);
8127
+ } catch (error) {
8128
+ if (!operationFailed)
8129
+ throw error;
8130
+ }
8085
8131
  }
8086
8132
  }
8133
+ async function closeBackends(...backends) {
8134
+ const results = await Promise.allSettled(backends.filter((backend) => backend !== undefined).map(async (backend) => await backend.close()));
8135
+ const failed = results.find((result) => result.status === "rejected");
8136
+ if (failed?.status === "rejected")
8137
+ throw failed.reason;
8138
+ }
8087
8139
 
8088
8140
  // src/vendor/tinbase/node/project.ts
8089
8141
  import { readdir, readFile as readFile2 } from "fs/promises";
@@ -8131,7 +8183,7 @@ function isNotFound(error) {
8131
8183
  }
8132
8184
 
8133
8185
  // src/project-runtime.ts
8134
- import { chmod, link, lstat, mkdir as mkdir5, readFile as readFile6, realpath as realpath2, unlink as unlink2, writeFile as writeFile4 } from "fs/promises";
8186
+ import { chmod, link, lstat, mkdir as mkdir5, readFile as readFile6, realpath as realpath2, unlink as unlink3, writeFile as writeFile4 } from "fs/promises";
8135
8187
  import { dirname as dirname3, isAbsolute, join as join7, parse, relative, resolve as resolve2 } from "path";
8136
8188
 
8137
8189
  // src/vendor/tinbase/node/bun-server.ts
@@ -8947,7 +8999,7 @@ async function ensureProjectSecrets(paths) {
8947
8999
  throw error2;
8948
9000
  stored = validateSecrets(JSON.parse(await readFile6(paths.secretsFile, "utf8")));
8949
9001
  } finally {
8950
- await unlink2(temporaryFile).catch((error2) => {
9002
+ await unlink3(temporaryFile).catch((error2) => {
8951
9003
  if (error2.code !== "ENOENT")
8952
9004
  throw error2;
8953
9005
  });
@@ -9322,13 +9374,12 @@ async function assertNoDataDirectoryLock(paths) {
9322
9374
  if (!paths.dataDir)
9323
9375
  return;
9324
9376
  const lockPath = `${paths.dataDir}.supacloud-lite.lock`;
9325
- try {
9326
- await lstat2(lockPath);
9327
- throw new Error(`data directory is in use or has a stale lock: ${lockPath}; stop Lite and remove the lock manually if it is stale`);
9328
- } catch (error) {
9329
- if (error.code !== "ENOENT")
9330
- throw error;
9331
- }
9377
+ const lockState = await recoverStaleDataDirLock(lockPath);
9378
+ if (lockState.kind === "missing")
9379
+ return;
9380
+ if (lockState.kind === "active")
9381
+ throw new Error(`data directory is already in use: ${paths.dataDir} (pid ${lockState.owner.pid})`);
9382
+ throw new Error(`data directory has an unreadable lock: ${lockPath}; confirm Lite is stopped, then remove the lock manually`);
9332
9383
  }
9333
9384
  async function stageDirectory(root, destination) {
9334
9385
  try {
@@ -9522,6 +9573,20 @@ function pathsOverlap(left, right) {
9522
9573
  return normalizedLeft === normalizedRight || isWithin(normalizedLeft, normalizedRight) || isWithin(normalizedRight, normalizedLeft);
9523
9574
  }
9524
9575
 
9576
+ // src/shutdown.ts
9577
+ async function waitForShutdown(closeProject, signalSource = process) {
9578
+ await new Promise((resolveShutdown) => {
9579
+ const resolveOnSignal = () => {
9580
+ signalSource.off("SIGINT", resolveOnSignal);
9581
+ signalSource.off("SIGTERM", resolveOnSignal);
9582
+ resolveShutdown();
9583
+ };
9584
+ signalSource.once("SIGINT", resolveOnSignal);
9585
+ signalSource.once("SIGTERM", resolveOnSignal);
9586
+ });
9587
+ await closeProject();
9588
+ }
9589
+
9525
9590
  // src/cli.ts
9526
9591
  function parseArgs(argv) {
9527
9592
  const args = [...argv];
@@ -9687,6 +9752,7 @@ Use --service-role to print the privileged key.`);
9687
9752
  Run "supacloud-lite keys --service-role" only when privileged access is required.
9688
9753
  `);
9689
9754
  await waitForShutdown(() => project.close());
9755
+ process.exitCode = 0;
9690
9756
  }
9691
9757
  async function runDbCommand(options) {
9692
9758
  const subcommand = options.positionals[0];
@@ -9815,17 +9881,6 @@ function timestamp() {
9815
9881
  return new Date().toISOString().replace(/[-:T]/g, "").slice(0, 14);
9816
9882
  }
9817
9883
  function quietLog() {}
9818
- function waitForShutdown(closeProject) {
9819
- return new Promise((resolveShutdown, rejectShutdown) => {
9820
- const closeOnSignal = () => {
9821
- process.off("SIGINT", closeOnSignal);
9822
- process.off("SIGTERM", closeOnSignal);
9823
- closeProject().then(resolveShutdown, rejectShutdown);
9824
- };
9825
- process.once("SIGINT", closeOnSignal);
9826
- process.once("SIGTERM", closeOnSignal);
9827
- });
9828
- }
9829
9884
  function printHelp() {
9830
9885
  console.log(`supacloud-lite - Bun-native Supabase-compatible backend on PGlite
9831
9886
 
package/dist/index.js CHANGED
@@ -71,7 +71,7 @@ function randomToken(bytes = 32) {
71
71
  // package.json
72
72
  var package_default = {
73
73
  name: "@supacloud/lite",
74
- version: "0.5.0",
74
+ version: "0.5.2",
75
75
  description: "Bun-native, single-project Supabase-compatible backend powered by PGlite",
76
76
  type: "module",
77
77
  license: "Apache-2.0",
@@ -3295,9 +3295,57 @@ function pickTag(text, base) {
3295
3295
  }
3296
3296
 
3297
3297
  // src/vendor/tinbase/db/pglite-engine.ts
3298
- import { mkdir, open, readFile, unlink } from "fs/promises";
3298
+ import { mkdir, open, unlink as unlink2 } from "fs/promises";
3299
3299
  import { dirname, resolve } from "path";
3300
3300
 
3301
+ // src/vendor/tinbase/db/data-dir-lock.ts
3302
+ import { readFile, unlink } from "fs/promises";
3303
+ async function recoverStaleDataDirLock(lockPath) {
3304
+ const lockState = await inspectDataDirLock(lockPath);
3305
+ if (lockState.kind !== "stale")
3306
+ return lockState;
3307
+ try {
3308
+ await unlink(lockPath);
3309
+ return { kind: "missing" };
3310
+ } catch (error) {
3311
+ if (error.code === "ENOENT")
3312
+ return { kind: "missing" };
3313
+ throw error;
3314
+ }
3315
+ }
3316
+ async function readDataDirLockOwner(lockPath) {
3317
+ const lockState = await inspectDataDirLock(lockPath);
3318
+ return lockState.kind === "active" || lockState.kind === "stale" ? lockState.owner : null;
3319
+ }
3320
+ async function inspectDataDirLock(lockPath) {
3321
+ let contents;
3322
+ try {
3323
+ contents = await readFile(lockPath, "utf8");
3324
+ } catch (error) {
3325
+ return error.code === "ENOENT" ? { kind: "missing" } : { kind: "unreadable" };
3326
+ }
3327
+ const owner = parseDataDirLockOwner(contents);
3328
+ if (!owner)
3329
+ return { kind: "unreadable" };
3330
+ return isProcessAlive(owner.pid) ? { kind: "active", owner } : { kind: "stale", owner };
3331
+ }
3332
+ function parseDataDirLockOwner(contents) {
3333
+ try {
3334
+ const value = JSON.parse(contents);
3335
+ return typeof value.pid === "number" && Number.isInteger(value.pid) && value.pid > 0 && typeof value.nonce === "string" ? { pid: value.pid, nonce: value.nonce } : null;
3336
+ } catch {
3337
+ return null;
3338
+ }
3339
+ }
3340
+ function isProcessAlive(pid) {
3341
+ try {
3342
+ process.kill(pid, 0);
3343
+ return true;
3344
+ } catch (error) {
3345
+ return error.code === "EPERM";
3346
+ }
3347
+ }
3348
+
3301
3349
  // src/standalone-assets-protocol.ts
3302
3350
  var STANDALONE_PGLITE_ASSETS = Symbol.for("supacloud-lite.pglite-standalone-assets");
3303
3351
 
@@ -3432,31 +3480,27 @@ async function acquireDataDirLock(dataDir) {
3432
3480
  return;
3433
3481
  released = true;
3434
3482
  await handle?.close();
3435
- const owner = await readLockOwner(lockPath);
3483
+ const owner = await readDataDirLockOwner(lockPath);
3436
3484
  if (owner?.nonce !== nonce)
3437
3485
  return;
3438
- await unlink(lockPath).catch((error) => {
3486
+ await unlink2(lockPath).catch((error) => {
3439
3487
  if (error.code !== "ENOENT")
3440
3488
  throw error;
3441
3489
  });
3442
3490
  };
3443
3491
  }
3444
3492
  async function createDataDirLock(absoluteDataDir, lockPath, nonce) {
3445
- for (let attempt = 0;attempt < 2; attempt++) {
3493
+ for (let attempt = 0;attempt < 3; attempt++) {
3446
3494
  try {
3447
3495
  return await writeDataDirLock(lockPath, nonce);
3448
3496
  } catch (error) {
3449
3497
  if (error.code !== "EEXIST")
3450
3498
  throw error;
3451
- const owner = await readLockOwner(lockPath);
3452
- if (owner && isProcessAlive(owner.pid))
3453
- throw lockInUseError(absoluteDataDir, owner.pid);
3454
- if (!owner || attempt > 0)
3499
+ const lockState = await recoverStaleDataDirLock(lockPath);
3500
+ if (lockState.kind === "active")
3501
+ throw lockInUseError(absoluteDataDir, lockState.owner.pid);
3502
+ if (lockState.kind === "unreadable")
3455
3503
  throw unreadableLockError(lockPath);
3456
- await unlink(lockPath).catch((unlinkError) => {
3457
- if (unlinkError.code !== "ENOENT")
3458
- throw unlinkError;
3459
- });
3460
3504
  }
3461
3505
  }
3462
3506
  throw unreadableLockError(lockPath);
@@ -3469,7 +3513,7 @@ async function writeDataDirLock(lockPath, nonce) {
3469
3513
  return handle;
3470
3514
  } catch (error) {
3471
3515
  await handle.close().catch(() => {});
3472
- await unlink(lockPath).catch(() => {});
3516
+ await unlink2(lockPath).catch(() => {});
3473
3517
  throw error;
3474
3518
  }
3475
3519
  }
@@ -3479,22 +3523,6 @@ function lockInUseError(dataDir, pid) {
3479
3523
  function unreadableLockError(lockPath) {
3480
3524
  return new Error(`PGlite data directory has an unreadable lock: ${lockPath}. Confirm no SupaCloud Lite process is using it, then remove the lock manually.`);
3481
3525
  }
3482
- async function readLockOwner(lockPath) {
3483
- try {
3484
- const value = JSON.parse(await readFile(lockPath, "utf8"));
3485
- return typeof value.pid === "number" && Number.isInteger(value.pid) && value.pid > 0 && typeof value.nonce === "string" ? { pid: value.pid, nonce: value.nonce } : null;
3486
- } catch {
3487
- return null;
3488
- }
3489
- }
3490
- function isProcessAlive(pid) {
3491
- try {
3492
- process.kill(pid, 0);
3493
- return true;
3494
- } catch (error) {
3495
- return error.code === "EPERM";
3496
- }
3497
- }
3498
3526
 
3499
3527
  // src/vendor/tinbase/db/database.ts
3500
3528
  var DEFAULT_SEARCH_PATH_SQL = `set search_path to "$user", public, extensions`;
@@ -8039,7 +8067,7 @@ async function serveBun(backend, opts = {}) {
8039
8067
  };
8040
8068
  }
8041
8069
  // src/project-runtime.ts
8042
- import { chmod, link, lstat, mkdir as mkdir4, readFile as readFile6, realpath as realpath2, unlink as unlink2, writeFile as writeFile3 } from "fs/promises";
8070
+ import { chmod, link, lstat, mkdir as mkdir4, readFile as readFile6, realpath as realpath2, unlink as unlink3, writeFile as writeFile3 } from "fs/promises";
8043
8071
  import { dirname as dirname3, isAbsolute, join as join6, parse, relative, resolve as resolve2 } from "path";
8044
8072
 
8045
8073
  // src/vendor/tinbase/node/config-toml.ts
@@ -8659,7 +8687,7 @@ async function ensureProjectSecrets(paths) {
8659
8687
  throw error2;
8660
8688
  stored = validateSecrets(JSON.parse(await readFile6(paths.secretsFile, "utf8")));
8661
8689
  } finally {
8662
- await unlink2(temporaryFile).catch((error2) => {
8690
+ await unlink3(temporaryFile).catch((error2) => {
8663
8691
  if (error2.code !== "ENOENT")
8664
8692
  throw error2;
8665
8693
  });
@@ -9033,13 +9061,12 @@ async function assertNoDataDirectoryLock(paths) {
9033
9061
  if (!paths.dataDir)
9034
9062
  return;
9035
9063
  const lockPath = `${paths.dataDir}.supacloud-lite.lock`;
9036
- try {
9037
- await lstat2(lockPath);
9038
- throw new Error(`data directory is in use or has a stale lock: ${lockPath}; stop Lite and remove the lock manually if it is stale`);
9039
- } catch (error) {
9040
- if (error.code !== "ENOENT")
9041
- throw error;
9042
- }
9064
+ const lockState = await recoverStaleDataDirLock(lockPath);
9065
+ if (lockState.kind === "missing")
9066
+ return;
9067
+ if (lockState.kind === "active")
9068
+ throw new Error(`data directory is already in use: ${paths.dataDir} (pid ${lockState.owner.pid})`);
9069
+ throw new Error(`data directory has an unreadable lock: ${lockPath}; confirm Lite is stopped, then remove the lock manually`);
9043
9070
  }
9044
9071
  async function stageDirectory(root, destination) {
9045
9072
  try {
package/dist/launcher.cjs CHANGED
@@ -6,6 +6,18 @@ const { join } = require('node:path')
6
6
 
7
7
  const cliPath = join(__dirname, 'cli.js')
8
8
  const bunProcess = spawn('bun', [cliPath, ...process.argv.slice(2)], { shell: false, stdio: 'inherit' })
9
+ let requestedShutdownSignal = null
10
+
11
+ function forwardShutdownSignal(signal) {
12
+ if (requestedShutdownSignal || bunProcess.exitCode !== null || bunProcess.signalCode !== null) return
13
+ requestedShutdownSignal = signal
14
+ bunProcess.kill(signal)
15
+ }
16
+
17
+ const forwardSigint = () => forwardShutdownSignal('SIGINT')
18
+ const forwardSigterm = () => forwardShutdownSignal('SIGTERM')
19
+ process.once('SIGINT', forwardSigint)
20
+ process.once('SIGTERM', forwardSigterm)
9
21
 
10
22
  bunProcess.once('error', (error) => {
11
23
  if (error.code === 'ENOENT') {
@@ -17,6 +29,7 @@ bunProcess.once('error', (error) => {
17
29
  })
18
30
 
19
31
  bunProcess.once('exit', (exitCode, signal) => {
20
- if (signal) process.kill(process.pid, signal)
21
- else process.exitCode = exitCode ?? 1
32
+ process.off('SIGINT', forwardSigint)
33
+ process.off('SIGTERM', forwardSigterm)
34
+ process.exitCode = exitCode ?? (requestedShutdownSignal === signal ? 0 : 1)
22
35
  })
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,wBAAwB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAElF,QAAA,MAAM,eAAe,4BAA4B,CAAA;AACjD,QAAA,MAAM,gBAAgB,IAAI,CAAA;AAE1B,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,eAAe,CAAA;IAC9B,OAAO,EAAE,OAAO,gBAAgB,CAAA;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,wBAAwB,CAAA;IACxC,gBAAgB,EAAE,OAAO,CAAA;IACzB,oBAAoB,EAAE,OAAO,CAAA;IAC7B,eAAe,EAAE,IAAI,CAAA;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,YAAY,CAAA;IACnB,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,wBAAwB,CAAA;IACxC,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,YAAY,CAAA;IACnB,cAAc,EAAE,wBAAwB,CAAA;IACxC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAA;CACxB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAqC9F;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA0FrG"}
1
+ {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,wBAAwB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAGlF,QAAA,MAAM,eAAe,4BAA4B,CAAA;AACjD,QAAA,MAAM,gBAAgB,IAAI,CAAA;AAE1B,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,eAAe,CAAA;IAC9B,OAAO,EAAE,OAAO,gBAAgB,CAAA;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,wBAAwB,CAAA;IACxC,gBAAgB,EAAE,OAAO,CAAA;IACzB,oBAAoB,EAAE,OAAO,CAAA;IAC7B,eAAe,EAAE,IAAI,CAAA;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,YAAY,CAAA;IACnB,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,wBAAwB,CAAA;IACxC,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,YAAY,CAAA;IACnB,cAAc,EAAE,wBAAwB,CAAA;IACxC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAA;CACxB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAqC9F;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA0FrG"}
@@ -0,0 +1,18 @@
1
+ export interface DataDirLockOwner {
2
+ pid: number;
3
+ nonce: string;
4
+ }
5
+ export type DataDirLockState = {
6
+ kind: 'missing';
7
+ } | {
8
+ kind: 'unreadable';
9
+ } | {
10
+ kind: 'active';
11
+ owner: DataDirLockOwner;
12
+ } | {
13
+ kind: 'stale';
14
+ owner: DataDirLockOwner;
15
+ };
16
+ export declare function recoverStaleDataDirLock(lockPath: string): Promise<DataDirLockState>;
17
+ export declare function readDataDirLockOwner(lockPath: string): Promise<DataDirLockOwner | null>;
18
+ //# sourceMappingURL=data-dir-lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-dir-lock.d.ts","sourceRoot":"","sources":["../../../../src/vendor/tinbase/db/data-dir-lock.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,gBAAgB,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,gBAAgB,CAAA;CAAE,CAAA;AAE9C,wBAAsB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAWzF;AAED,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAG7F"}
@@ -1 +1 @@
1
- {"version":3,"file":"pglite-engine.d.ts","sourceRoot":"","sources":["../../../../src/vendor/tinbase/db/pglite-engine.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAA2B,MAAM,aAAa,CAAA;AAMpE;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAmG5E"}
1
+ {"version":3,"file":"pglite-engine.d.ts","sourceRoot":"","sources":["../../../../src/vendor/tinbase/db/pglite-engine.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAA2B,MAAM,aAAa,CAAA;AAOpE;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAmG5E"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/lite",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Bun-native, single-project Supabase-compatible backend powered by PGlite",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",