@treeseed/core 0.6.31 → 0.6.33

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/dist/dev.d.ts CHANGED
@@ -105,6 +105,7 @@ type SpawnSyncLike = typeof spawnSync;
105
105
  type SignalRegistrar = (signal: NodeJS.Signals, handler: () => void) => () => void;
106
106
  type FetchLike = (url: string, init?: RequestInit) => Promise<Response>;
107
107
  type ProcessKiller = (pid: number, signal: NodeJS.Signals) => void;
108
+ type ProcessStatusChecker = (pid: number) => boolean;
108
109
  type WatchStarter = TreeseedDevWatchStarter;
109
110
  type TreeseedIntegratedDevDependencies = {
110
111
  spawn: SpawnLike;
@@ -113,6 +114,7 @@ type TreeseedIntegratedDevDependencies = {
113
114
  prepareEnvironment: (tenantRoot: string) => void;
114
115
  fetch: FetchLike;
115
116
  killProcess: ProcessKiller;
117
+ processIsAlive: ProcessStatusChecker;
116
118
  write: (line: string, stream: 'stdout' | 'stderr') => void;
117
119
  openBrowser: (url: string) => void | Promise<void>;
118
120
  startWatch: WatchStarter;
package/dist/dev.js CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { spawn, spawnSync } from "node:child_process";
3
3
  import { createRequire } from "node:module";
4
4
  import { dirname, resolve, sep } from "node:path";
@@ -32,6 +32,7 @@ const TREESEED_DEFAULT_LOCAL_SMTP_HOST = "127.0.0.1";
32
32
  const TREESEED_DEFAULT_LOCAL_SMTP_PORT = 1025;
33
33
  const TREESEED_DEFAULT_MAILPIT_UI_PORT = 8025;
34
34
  const DEV_RELOAD_FILE = "public/__treeseed/dev-reload.json";
35
+ const DEV_RUNTIME_FILE = ".treeseed/generated/dev/runtime.json";
35
36
  const DEFAULT_READINESS_TIMEOUT_MS = 9e4;
36
37
  const DEFAULT_PROCESS_READY_GRACE_MS = 1200;
37
38
  const DEFAULT_SHUTDOWN_GRACE_MS = 2500;
@@ -449,8 +450,12 @@ function createTreeseedIntegratedDevPlan(options = {}) {
449
450
  TREESEED_API_D1_LOCAL_PERSIST_TO: mergedEnv.TREESEED_API_D1_LOCAL_PERSIST_TO ?? (usesCloudflareWebRuntime ? resolve(tenantRoot, ".treeseed", "generated", "environments", "local", ".wrangler", "state", "v3", "d1") : resolve(tenantRoot, ".wrangler", "state", "v3", "d1")),
450
451
  TREESEED_FORM_TOKEN_SECRET: mergedEnv.TREESEED_FORM_TOKEN_SECRET ?? "treeseed-local-form-token-secret",
451
452
  TREESEED_BETTER_AUTH_SECRET: mergedEnv.TREESEED_BETTER_AUTH_SECRET ?? "treeseed-local-better-auth-secret-minimum-32-characters",
452
- TREESEED_SMTP_HOST: mergedEnv.TREESEED_SMTP_HOST ?? TREESEED_DEFAULT_LOCAL_SMTP_HOST,
453
- TREESEED_SMTP_PORT: mergedEnv.TREESEED_SMTP_PORT ?? String(TREESEED_DEFAULT_LOCAL_SMTP_PORT),
453
+ TREESEED_SMTP_HOST: TREESEED_DEFAULT_LOCAL_SMTP_HOST,
454
+ TREESEED_SMTP_PORT: String(TREESEED_DEFAULT_LOCAL_SMTP_PORT),
455
+ TREESEED_SMTP_USERNAME: "",
456
+ TREESEED_SMTP_PASSWORD: "",
457
+ TREESEED_MAILPIT_SMTP_HOST: TREESEED_DEFAULT_LOCAL_SMTP_HOST,
458
+ TREESEED_MAILPIT_SMTP_PORT: String(TREESEED_DEFAULT_LOCAL_SMTP_PORT),
454
459
  TREESEED_MAILPIT_UI_PORT: mergedEnv.TREESEED_MAILPIT_UI_PORT ?? String(TREESEED_DEFAULT_MAILPIT_UI_PORT),
455
460
  TREESEED_AUTH_EMAIL_FROM: mergedEnv.TREESEED_AUTH_EMAIL_FROM ?? "Treeseed Market <auth@treeseed.local>"
456
461
  };
@@ -583,6 +588,17 @@ function defaultPrepareEnvironment(tenantRoot) {
583
588
  function defaultKillProcess(pid, signal) {
584
589
  process.kill(pid, signal);
585
590
  }
591
+ function defaultProcessIsAlive(pid) {
592
+ if (!Number.isInteger(pid) || pid <= 0) {
593
+ return false;
594
+ }
595
+ try {
596
+ process.kill(pid, 0);
597
+ return true;
598
+ } catch {
599
+ return false;
600
+ }
601
+ }
586
602
  function defaultRemovePath(path) {
587
603
  rmSync(path, { recursive: true, force: true });
588
604
  }
@@ -662,6 +678,88 @@ function resolveLocalMachineEnv(tenantRoot) {
662
678
  return {};
663
679
  }
664
680
  }
681
+ function devRuntimeStatePath(tenantRoot) {
682
+ return resolve(tenantRoot, DEV_RUNTIME_FILE);
683
+ }
684
+ function readDevRuntimeState(tenantRoot) {
685
+ try {
686
+ const parsed = JSON.parse(readFileSync(devRuntimeStatePath(tenantRoot), "utf8"));
687
+ if (!Number.isInteger(parsed.pid) || typeof parsed.tenantRoot !== "string" || typeof parsed.startedAt !== "string") {
688
+ return null;
689
+ }
690
+ return {
691
+ pid: parsed.pid,
692
+ tenantRoot: parsed.tenantRoot,
693
+ startedAt: parsed.startedAt
694
+ };
695
+ } catch {
696
+ return null;
697
+ }
698
+ }
699
+ function writeCurrentDevRuntimeState(tenantRoot) {
700
+ const outputPath = devRuntimeStatePath(tenantRoot);
701
+ mkdirSync(dirname(outputPath), { recursive: true });
702
+ writeFileSync(
703
+ outputPath,
704
+ `${JSON.stringify({
705
+ pid: process.pid,
706
+ tenantRoot,
707
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
708
+ }, null, 2)}
709
+ `,
710
+ "utf8"
711
+ );
712
+ }
713
+ function removeCurrentDevRuntimeState(tenantRoot) {
714
+ const state = readDevRuntimeState(tenantRoot);
715
+ if (!state || state.pid !== process.pid) {
716
+ return;
717
+ }
718
+ rmSync(devRuntimeStatePath(tenantRoot), { force: true });
719
+ }
720
+ async function waitForProcessExit(pid, processIsAlive, timeoutMs) {
721
+ const startedAt = Date.now();
722
+ while (Date.now() - startedAt < timeoutMs) {
723
+ if (!processIsAlive(pid)) {
724
+ return true;
725
+ }
726
+ await delay(100);
727
+ }
728
+ return !processIsAlive(pid);
729
+ }
730
+ async function stopPreviousDevRuntime(tenantRoot, options, deps) {
731
+ const state = readDevRuntimeState(tenantRoot);
732
+ if (!state) {
733
+ return;
734
+ }
735
+ const statePath = devRuntimeStatePath(tenantRoot);
736
+ if (state.pid === process.pid) {
737
+ return;
738
+ }
739
+ if (!deps.processIsAlive(state.pid)) {
740
+ rmSync(statePath, { force: true });
741
+ return;
742
+ }
743
+ emitEvent(options, deps.write, {
744
+ type: "replace",
745
+ message: `Stopping previous Treeseed dev runtime (${state.pid}) before starting a new one.`,
746
+ detail: { pid: state.pid, startedAt: state.startedAt }
747
+ });
748
+ try {
749
+ deps.killProcess(state.pid, "SIGTERM");
750
+ } catch {
751
+ }
752
+ if (await waitForProcessExit(state.pid, deps.processIsAlive, options.shutdownGraceMs ?? DEFAULT_SHUTDOWN_GRACE_MS)) {
753
+ rmSync(statePath, { force: true });
754
+ return;
755
+ }
756
+ try {
757
+ deps.killProcess(state.pid, "SIGKILL");
758
+ } catch {
759
+ }
760
+ await waitForProcessExit(state.pid, deps.processIsAlive, DEFAULT_KILL_GRACE_MS);
761
+ rmSync(statePath, { force: true });
762
+ }
665
763
  function emitEvent(options, write, event, stream = event.type === "error" ? "stderr" : "stdout") {
666
764
  if (options.json) {
667
765
  write(`${JSON.stringify({ schemaVersion: 1, kind: "treeseed.dev.event", ...event })}
@@ -983,6 +1081,7 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
983
1081
  const onSignal = deps.onSignal ?? defaultSignalRegistrar;
984
1082
  const fetchFn = deps.fetch ?? globalThis.fetch.bind(globalThis);
985
1083
  const killProcess = deps.killProcess ?? defaultKillProcess;
1084
+ const processIsAlive = deps.processIsAlive ?? defaultProcessIsAlive;
986
1085
  const openBrowser = deps.openBrowser ?? defaultOpenBrowser;
987
1086
  const startWatch = deps.startWatch ?? startPollingWatch;
988
1087
  const prepareEnvironment = deps.prepareEnvironment ?? defaultPrepareEnvironment;
@@ -1001,6 +1100,9 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
1001
1100
  writePlan(plan, options, write);
1002
1101
  return 0;
1003
1102
  }
1103
+ if (readDevRuntimeState(tenantRoot)) {
1104
+ await stopPreviousDevRuntime(tenantRoot, options, { write, killProcess, processIsAlive });
1105
+ }
1004
1106
  const resetResults = runTreeseedIntegratedDevReset(plan.reset, options, {
1005
1107
  write,
1006
1108
  removePath,
@@ -1021,6 +1123,7 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
1021
1123
  emitEvent(options, write, { type: "error", message: failedSetupMessage(failedSetup), detail: failedSetup });
1022
1124
  return 1;
1023
1125
  }
1126
+ writeCurrentDevRuntimeState(tenantRoot);
1024
1127
  const children = /* @__PURE__ */ new Map();
1025
1128
  const commandsById = new Map(plan.commands.map((command) => [command.id, command]));
1026
1129
  const requiredSurfaceIds = new Set(plan.readyChecks.filter((check) => check.required).map((check) => check.id));
@@ -1058,6 +1161,7 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
1058
1161
  for (const dispose of disposers) {
1059
1162
  dispose();
1060
1163
  }
1164
+ removeCurrentDevRuntimeState(tenantRoot);
1061
1165
  emitEvent(
1062
1166
  options,
1063
1167
  write,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treeseed/core",
3
- "version": "0.6.31",
3
+ "version": "0.6.33",
4
4
  "description": "Treeseed integrated platform starter for Astro/Starlight web runtimes and Hono API runtimes.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {
@@ -76,7 +76,7 @@
76
76
  "@astrojs/sitemap": "3.7.0",
77
77
  "@astrojs/starlight": "0.37.6",
78
78
  "@tailwindcss/vite": "^4.1.4",
79
- "@treeseed/sdk": "0.6.30",
79
+ "@treeseed/sdk": "0.6.32",
80
80
  "astro": "^5.6.1",
81
81
  "esbuild": "^0.28.0",
82
82
  "hono": "^4.8.2",
@@ -176,9 +176,11 @@ jobs:
176
176
  set -euo pipefail
177
177
  npm run verify:local 2>&1 | tee verify.log
178
178
  if test -f ./packages/sdk/scripts/check-build-warnings.mjs; then
179
- node ./packages/sdk/scripts/check-build-warnings.mjs verify.log
179
+ node ./packages/sdk/scripts/check-build-warnings.mjs verify.log \
180
+ --allow 'Module "url" has been externalized for browser compatibility, imported by ".*libsodium-sumo.*"'
180
181
  elif test -f ./node_modules/@treeseed/sdk/dist/scripts/check-build-warnings.js; then
181
- node ./node_modules/@treeseed/sdk/dist/scripts/check-build-warnings.js verify.log
182
+ node ./node_modules/@treeseed/sdk/dist/scripts/check-build-warnings.js verify.log \
183
+ --allow 'Module "url" has been externalized for browser compatibility, imported by ".*libsodium-sumo.*"'
182
184
  else
183
185
  echo "Unable to resolve @treeseed/sdk warning scanner entrypoint."
184
186
  exit 1