plotlink-ows 1.0.32 → 1.2.94

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.
Files changed (145) hide show
  1. package/README.md +4 -0
  2. package/app/lib/agent-command.ts +85 -0
  3. package/app/lib/agent-readiness.ts +133 -0
  4. package/app/lib/apply-schema.ts +55 -0
  5. package/app/lib/bubble-text.ts +160 -0
  6. package/app/lib/cartoon-coach.ts +198 -0
  7. package/app/lib/cartoon-markdown.ts +83 -0
  8. package/app/lib/cartoon-prompt.ts +122 -0
  9. package/app/lib/cartoon-readiness.ts +811 -0
  10. package/app/lib/clean-image-sync.ts +245 -0
  11. package/app/lib/codex-images.ts +152 -0
  12. package/app/lib/cut-asset-diagnostics.ts +120 -0
  13. package/app/lib/cuts.ts +302 -0
  14. package/app/lib/fonts.ts +109 -0
  15. package/app/lib/generate-claude-md.ts +10 -3
  16. package/app/lib/generate-story-instructions.ts +731 -0
  17. package/app/lib/image-asset-validate.ts +123 -0
  18. package/app/lib/lettering-status.ts +133 -0
  19. package/app/lib/overlays.ts +637 -0
  20. package/app/lib/paths.ts +10 -0
  21. package/app/lib/public-title.ts +65 -0
  22. package/app/lib/publish.ts +16 -2
  23. package/app/lib/story-progress.ts +243 -0
  24. package/app/lib/terminal-protocol.ts +16 -0
  25. package/app/lib/terminal-redact.ts +50 -0
  26. package/app/prisma/schema.sql +25 -0
  27. package/app/routes/agent.ts +42 -0
  28. package/app/routes/codex-images.ts +67 -0
  29. package/app/routes/publish.ts +209 -28
  30. package/app/routes/stories.ts +961 -5
  31. package/app/routes/terminal.ts +383 -31
  32. package/app/server.ts +47 -12
  33. package/app/vite.config.ts +6 -0
  34. package/app/web/components/CartoonPreview.tsx +267 -0
  35. package/app/web/components/CartoonPublishPage.tsx +407 -0
  36. package/app/web/components/CartoonPublishPreview.tsx +121 -0
  37. package/app/web/components/CartoonStepGuide.tsx +90 -0
  38. package/app/web/components/CartoonWorkflowNav.tsx +68 -0
  39. package/app/web/components/CodexImportPicker.tsx +230 -0
  40. package/app/web/components/CutListPanel.tsx +1299 -0
  41. package/app/web/components/EpisodesPage.tsx +80 -0
  42. package/app/web/components/FinishEpisodePanel.tsx +151 -0
  43. package/app/web/components/Layout.tsx +7 -4
  44. package/app/web/components/LetteringEditor.tsx +1141 -0
  45. package/app/web/components/PreviewPanel.tsx +1017 -144
  46. package/app/web/components/Settings.tsx +63 -0
  47. package/app/web/components/StoriesPage.tsx +710 -33
  48. package/app/web/components/StoryBrowser.tsx +22 -14
  49. package/app/web/components/StoryInfoPage.tsx +266 -0
  50. package/app/web/components/StoryProgressPanel.tsx +516 -0
  51. package/app/web/components/TerminalPanel.tsx +233 -11
  52. package/app/web/components/WorkflowCoach.tsx +128 -0
  53. package/app/web/components/asset-image.tsx +114 -0
  54. package/app/web/components/asset-test-utils.ts +44 -0
  55. package/app/web/components/export-cut.ts +320 -0
  56. package/app/web/dist/assets/export-cut-nKQ_n2-J.js +1 -0
  57. package/app/web/dist/assets/index-BAZGwVwj.js +143 -0
  58. package/app/web/dist/assets/index-DoXH2OlP.css +32 -0
  59. package/app/web/dist/index.html +2 -2
  60. package/app/web/lib/cartoon-publish-summary.ts +43 -0
  61. package/app/web/lib/codex-import.ts +94 -0
  62. package/app/web/lib/image-compress.ts +53 -0
  63. package/app/web/lib/import-image.ts +58 -0
  64. package/app/web/lib/publish-helpers.ts +385 -0
  65. package/app/web/lib/upload-retry.ts +130 -0
  66. package/app/web/lib/verify-public-title.ts +105 -0
  67. package/app/web/styles.css +9 -0
  68. package/bin/plotlink-ows.js +53 -16
  69. package/bin/startup-plan.cjs +58 -0
  70. package/lib/genres.ts +92 -0
  71. package/package.json +60 -20
  72. package/scripts/gen-schema-sql.mjs +49 -0
  73. package/scripts/package-hygiene.mjs +116 -0
  74. package/scripts/preflight.mjs +173 -0
  75. package/scripts/start-smoke.mjs +128 -0
  76. package/app/node_modules/.prisma/local-client/client.d.ts +0 -1
  77. package/app/node_modules/.prisma/local-client/client.js +0 -5
  78. package/app/node_modules/.prisma/local-client/default.d.ts +0 -1
  79. package/app/node_modules/.prisma/local-client/default.js +0 -5
  80. package/app/node_modules/.prisma/local-client/edge.d.ts +0 -1
  81. package/app/node_modules/.prisma/local-client/edge.js +0 -184
  82. package/app/node_modules/.prisma/local-client/index-browser.js +0 -173
  83. package/app/node_modules/.prisma/local-client/index.d.ts +0 -3304
  84. package/app/node_modules/.prisma/local-client/index.js +0 -207
  85. package/app/node_modules/.prisma/local-client/libquery_engine-darwin-arm64.dylib.node +0 -0
  86. package/app/node_modules/.prisma/local-client/package.json +0 -183
  87. package/app/node_modules/.prisma/local-client/query_engine_bg.js +0 -2
  88. package/app/node_modules/.prisma/local-client/query_engine_bg.wasm +0 -0
  89. package/app/node_modules/.prisma/local-client/runtime/edge-esm.js +0 -35
  90. package/app/node_modules/.prisma/local-client/runtime/edge.js +0 -35
  91. package/app/node_modules/.prisma/local-client/runtime/index-browser.d.ts +0 -370
  92. package/app/node_modules/.prisma/local-client/runtime/index-browser.js +0 -17
  93. package/app/node_modules/.prisma/local-client/runtime/library.d.ts +0 -3982
  94. package/app/node_modules/.prisma/local-client/runtime/library.js +0 -147
  95. package/app/node_modules/.prisma/local-client/runtime/react-native.js +0 -84
  96. package/app/node_modules/.prisma/local-client/runtime/wasm-compiler-edge.js +0 -85
  97. package/app/node_modules/.prisma/local-client/runtime/wasm-engine-edge.js +0 -38
  98. package/app/node_modules/.prisma/local-client/schema.prisma +0 -21
  99. package/app/node_modules/.prisma/local-client/wasm-edge-light-loader.mjs +0 -5
  100. package/app/node_modules/.prisma/local-client/wasm-worker-loader.mjs +0 -5
  101. package/app/node_modules/.prisma/local-client/wasm.d.ts +0 -1
  102. package/app/node_modules/.prisma/local-client/wasm.js +0 -191
  103. package/app/web/dist/assets/index-B-2Ft7Yv.css +0 -32
  104. package/app/web/dist/assets/index-BFw-v-OZ.js +0 -134
  105. package/packages/cli/node_modules/commander/LICENSE +0 -22
  106. package/packages/cli/node_modules/commander/Readme.md +0 -1149
  107. package/packages/cli/node_modules/commander/esm.mjs +0 -16
  108. package/packages/cli/node_modules/commander/index.js +0 -24
  109. package/packages/cli/node_modules/commander/lib/argument.js +0 -149
  110. package/packages/cli/node_modules/commander/lib/command.js +0 -2662
  111. package/packages/cli/node_modules/commander/lib/error.js +0 -39
  112. package/packages/cli/node_modules/commander/lib/help.js +0 -709
  113. package/packages/cli/node_modules/commander/lib/option.js +0 -367
  114. package/packages/cli/node_modules/commander/lib/suggestSimilar.js +0 -101
  115. package/packages/cli/node_modules/commander/package-support.json +0 -16
  116. package/packages/cli/node_modules/commander/package.json +0 -82
  117. package/packages/cli/node_modules/commander/typings/esm.d.mts +0 -3
  118. package/packages/cli/node_modules/commander/typings/index.d.ts +0 -1045
  119. package/packages/cli/node_modules/resolve-from/index.d.ts +0 -31
  120. package/packages/cli/node_modules/resolve-from/index.js +0 -47
  121. package/packages/cli/node_modules/resolve-from/license +0 -9
  122. package/packages/cli/node_modules/resolve-from/package.json +0 -36
  123. package/packages/cli/node_modules/resolve-from/readme.md +0 -72
  124. package/packages/cli/node_modules/tsup/LICENSE +0 -21
  125. package/packages/cli/node_modules/tsup/README.md +0 -75
  126. package/packages/cli/node_modules/tsup/assets/cjs_shims.js +0 -13
  127. package/packages/cli/node_modules/tsup/assets/esm_shims.js +0 -9
  128. package/packages/cli/node_modules/tsup/assets/package.json +0 -3
  129. package/packages/cli/node_modules/tsup/dist/chunk-DI5BO6XE.js +0 -153
  130. package/packages/cli/node_modules/tsup/dist/chunk-JZ25TPTY.js +0 -42
  131. package/packages/cli/node_modules/tsup/dist/chunk-PEEXUWMS.js +0 -6
  132. package/packages/cli/node_modules/tsup/dist/chunk-TWFEYLU4.js +0 -352
  133. package/packages/cli/node_modules/tsup/dist/chunk-VGC3FXLU.js +0 -203
  134. package/packages/cli/node_modules/tsup/dist/cli-default.js +0 -12
  135. package/packages/cli/node_modules/tsup/dist/cli-main.js +0 -8
  136. package/packages/cli/node_modules/tsup/dist/cli-node.js +0 -14
  137. package/packages/cli/node_modules/tsup/dist/index.d.ts +0 -511
  138. package/packages/cli/node_modules/tsup/dist/index.js +0 -1711
  139. package/packages/cli/node_modules/tsup/dist/rollup.js +0 -6949
  140. package/packages/cli/node_modules/tsup/package.json +0 -99
  141. package/packages/cli/node_modules/tsup/schema.json +0 -362
  142. package/public/screenshot-1.png +0 -0
  143. package/public/screenshot-2.png +0 -0
  144. package/public/screenshot-3.png +0 -0
  145. package/scripts/e2e-verify.ts +0 -1100
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env node
2
+ // PlotLink OWS — release preflight & package-hygiene check (#466, EPIC #465).
3
+ //
4
+ // Run before a manual `npm publish` (the operator gate, #472) with:
5
+ //
6
+ // npm run preflight
7
+ //
8
+ // It is READ-ONLY and safe: it never runs `npm audit fix`, never publishes,
9
+ // never needs `npm login`, and never prints secrets/passphrases/wallet data.
10
+ //
11
+ // It reports + checks four things, and exits non-zero on any blocking issue:
12
+ // 1. Expected Node/npm toolchain (from package.json engines / packageManager).
13
+ // 2. A production `npm audit --omit=dev` summary (reported; high/critical warn).
14
+ // 3. The packed-package contents (`npm pack --dry-run`), FAILING on any
15
+ // generated/local artifact that must never ship: bundled node_modules,
16
+ // *.test.* / *.spec.* files, stray *.tgz tarballs, build caches, or obvious
17
+ // secret files (.env/.pem/.key).
18
+ // 4. A packed-tarball install smoke test in a throwaway temp dir (no scripts),
19
+ // verifying the bin + runtime entrypoints land and the bin parses.
20
+ //
21
+ // Keep the SUSPICIOUS rules below in sync with the `files` exclusions in
22
+ // package.json — they are two sides of the same hygiene contract.
23
+
24
+ import { execFileSync } from "node:child_process";
25
+ import { readFileSync, mkdtempSync, rmSync, existsSync, writeFileSync } from "node:fs";
26
+ import { tmpdir } from "node:os";
27
+ import { join, dirname } from "node:path";
28
+ import { fileURLToPath } from "node:url";
29
+ import { findSuspicious, findMissingRequired, requiredInstalledFiles, findRuntimeDepLeaks } from "./package-hygiene.mjs";
30
+
31
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
32
+ const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
33
+
34
+ const failures = [];
35
+ const warnings = [];
36
+ const section = (t) => console.log(`\n=== ${t} ===`);
37
+ const fail = (m) => { failures.push(m); console.log(` ✗ ${m}`); };
38
+ const warn = (m) => { warnings.push(m); console.log(` ! ${m}`); };
39
+ const ok = (m) => console.log(` ✓ ${m}`);
40
+
41
+ /** Run a command, returning { code, stdout } without throwing (audit/pack exit non-zero on findings). */
42
+ function run(cmd, args, opts = {}) {
43
+ try {
44
+ const stdout = execFileSync(cmd, args, { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], maxBuffer: 64 * 1024 * 1024, ...opts });
45
+ return { code: 0, stdout };
46
+ } catch (e) {
47
+ return { code: e.status ?? 1, stdout: e.stdout?.toString() ?? "" };
48
+ }
49
+ }
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // 1) Expected toolchain
53
+ // ---------------------------------------------------------------------------
54
+ section("Expected toolchain");
55
+ console.log(` engines.node: ${pkg.engines?.node ?? "(unset)"}`);
56
+ console.log(` engines.npm: ${pkg.engines?.npm ?? "(unset)"}`);
57
+ console.log(` packageManager: ${pkg.packageManager ?? "(unset)"}`);
58
+ const haveNode = process.version;
59
+ const haveNpm = run("npm", ["-v"]).stdout.trim() || "?";
60
+ console.log(` running node ${haveNode} / npm ${haveNpm}`);
61
+ if (!/^v20\./.test(haveNode)) warn(`Node ${haveNode} is not 20.x — publish should run on Node 20.x (engines.node).`);
62
+ if (!/^10\./.test(haveNpm)) warn(`npm ${haveNpm} is not 10.x — publish should run on npm 10.x (engines.npm / packageManager).`);
63
+ if (warnings.length === 0) ok("running on the expected Node 20.x / npm 10.x toolchain");
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // 2) Production audit summary (reported; never auto-fixed)
67
+ // ---------------------------------------------------------------------------
68
+ section("Production audit (npm audit --omit=dev)");
69
+ const audit = run("npm", ["audit", "--omit=dev", "--json"]);
70
+ try {
71
+ const vulns = JSON.parse(audit.stdout).metadata?.vulnerabilities ?? {};
72
+ console.log(` ${JSON.stringify(vulns)}`);
73
+ const severe = (vulns.high ?? 0) + (vulns.critical ?? 0);
74
+ if (severe > 0) warn(`${severe} high/critical production vulnerabilit${severe === 1 ? "y" : "ies"} — review before publishing (do NOT run 'npm audit fix --force').`);
75
+ else ok("no high/critical production vulnerabilities");
76
+ } catch {
77
+ warn("could not parse `npm audit` output (offline, or registry unreachable) — re-run with network access before publishing.");
78
+ }
79
+
80
+ // ---------------------------------------------------------------------------
81
+ // 2b) Runtime dependency boundary (#471, EPIC #465)
82
+ // ---------------------------------------------------------------------------
83
+ section("Runtime dependency boundary (dependencies allowlist)");
84
+ const leaks = findRuntimeDepLeaks(pkg);
85
+ if (leaks.length) {
86
+ fail(`web-app/build/upload-only package(s) in runtime 'dependencies' (move to devDependencies, or add to ALLOWED_RUNTIME_DEPS if genuinely a runtime import): ${leaks.join(", ")}`);
87
+ } else {
88
+ ok(`runtime 'dependencies' match the OWS allowlist (${Object.keys(pkg.dependencies ?? {}).length} pkgs; no web-app/S3/build leaks)`);
89
+ }
90
+
91
+ // ---------------------------------------------------------------------------
92
+ // 3) Packed contents + suspicious-file detection
93
+ // ---------------------------------------------------------------------------
94
+ section("Packed package contents (npm pack --dry-run)");
95
+ const dry = run("npm", ["pack", "--dry-run", "--json"]);
96
+ let manifest = null;
97
+ try { manifest = JSON.parse(dry.stdout)[0]; } catch { fail("`npm pack --dry-run --json` did not return a parseable manifest."); }
98
+ if (manifest) {
99
+ console.log(` entries: ${manifest.entryCount} unpacked: ${(manifest.unpackedSize / 1024).toFixed(0)}KB tarball: ${(manifest.size / 1024).toFixed(0)}KB`);
100
+ const paths = manifest.files.map((f) => f.path);
101
+ const bad = findSuspicious(paths);
102
+ if (bad.length) {
103
+ fail(`${bad.length} suspicious file(s) in the packed package (fix the package.json 'files' exclusions):`);
104
+ bad.slice(0, 40).forEach((b) => console.log(` - ${b.label}: ${b.path}`));
105
+ if (bad.length > 40) console.log(` … and ${bad.length - 40} more`);
106
+ } else {
107
+ ok("clean: no node_modules / test / fixture / coverage / screenshot / tarball / cache / temp / secret files");
108
+ }
109
+ // Enforce that the required runtime contents are still present (#468) — a
110
+ // `files` exclusion that's too aggressive (dropping the bin, README, LICENSE,
111
+ // the Prisma schema, or the prebuilt web UI) fails here.
112
+ const missing = findMissingRequired(paths);
113
+ if (missing.length) fail(`packed package is missing required runtime file(s): ${missing.join(", ")}`);
114
+ else ok("required runtime contents present (bin, README, LICENSE, server, Prisma schema, web dist)");
115
+ }
116
+
117
+ // ---------------------------------------------------------------------------
118
+ // 4) Packed-tarball install smoke test (temp dir, no install scripts)
119
+ // ---------------------------------------------------------------------------
120
+ section("Tarball install smoke test");
121
+ let tmp;
122
+ try {
123
+ tmp = mkdtempSync(join(tmpdir(), "plotlink-preflight-"));
124
+ // Pack the real tarball INTO the temp dir, so no stray .tgz is left in the repo.
125
+ const packed = run("npm", ["pack", "--pack-destination", tmp, "--json"]);
126
+ const tgz = JSON.parse(packed.stdout)[0]?.filename;
127
+ if (!tgz) throw new Error("npm pack did not report a tarball filename");
128
+ const tgzPath = join(tmp, tgz);
129
+ // A throwaway consumer project that installs the tarball.
130
+ writeFileSync(join(tmp, "package.json"), JSON.stringify({ name: "preflight-smoke", private: true, version: "0.0.0" }) + "\n");
131
+ // --ignore-scripts: don't run the package's postinstall (prisma generate) — this
132
+ // is a packaging smoke test, not a full runtime bring-up (that's the operator gate).
133
+ const install = run("npm", ["install", tgzPath, "--ignore-scripts", "--no-audit", "--no-fund", "--no-save"], { cwd: tmp });
134
+ if (install.code !== 0) throw new Error("`npm install <tarball>` failed in the temp project");
135
+ const installed = join(tmp, "node_modules", pkg.name);
136
+ // Includes the postinstall prerequisite (the Prisma schema) derived from the
137
+ // actual postinstall command, so dropping it from `files` fails here even
138
+ // though the install ran with --ignore-scripts (#466, re1).
139
+ const required = requiredInstalledFiles(pkg);
140
+ const missing = required.filter((f) => !existsSync(join(installed, f)));
141
+ if (missing.length) fail(`installed tarball is missing required runtime/postinstall file(s): ${missing.join(", ")}`);
142
+ else ok(`tarball installs cleanly; bin + runtime + postinstall prerequisites present (${required.length} checked)`);
143
+ const check = run(process.execPath, ["--check", join(installed, "bin/plotlink-ows.js")]);
144
+ if (check.code !== 0) fail("bin/plotlink-ows.js failed `node --check` (syntax error)");
145
+ else ok("bin/plotlink-ows.js passes node --check");
146
+ } catch (e) {
147
+ fail(`tarball install smoke test failed: ${e.message || e}`);
148
+ } finally {
149
+ if (tmp) rmSync(tmp, { recursive: true, force: true });
150
+ }
151
+
152
+ // ---------------------------------------------------------------------------
153
+ // 5) Prod-only START smoke — packed tarball install + real server boot (#479)
154
+ // ---------------------------------------------------------------------------
155
+ section("Prod-only start smoke (packed tarball install + boot)");
156
+ if (process.env.PREFLIGHT_SKIP_START_SMOKE === "1") {
157
+ warn("start smoke SKIPPED via PREFLIGHT_SKIP_START_SMOKE=1 — a skipped run is NOT publish-safe; re-run without it before publishing.");
158
+ } else {
159
+ const startSmoke = run(process.execPath, [join(root, "scripts", "start-smoke.mjs")], { stdio: ["ignore", "inherit", "inherit"] });
160
+ if (startSmoke.code !== 0) fail("prod-only start smoke failed — the packed tarball did not install+boot+serve (see output above).");
161
+ else ok("packed tarball installs prod-only and the server serves /api/auth/status + / (the prebuilt web UI)");
162
+ }
163
+
164
+ // ---------------------------------------------------------------------------
165
+ // Summary
166
+ // ---------------------------------------------------------------------------
167
+ section("Preflight summary");
168
+ console.log(` warnings: ${warnings.length} failures: ${failures.length}`);
169
+ if (failures.length) {
170
+ console.log(`\n✗ Preflight FAILED — ${failures.length} blocking issue(s). Do not publish.`);
171
+ process.exit(1);
172
+ }
173
+ console.log(`\n✓ Preflight passed${warnings.length ? ` (with ${warnings.length} warning(s) to review)` : ""}.`);
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env node
2
+ // Prod-only packed-tarball START smoke (#479, EPIC #465).
3
+ //
4
+ // The pack/install smoke in preflight only checks FILE presence with
5
+ // `--ignore-scripts`; it cannot catch a *startup* regression. This smoke does a
6
+ // real bring-up:
7
+ // 1. `npm pack` the real tarball.
8
+ // 2. `npm install --omit=dev` it (scripts ON — postinstall `prisma generate`
9
+ // and native builds run, exactly like a user install).
10
+ // 3. Assert the web-app/build deps removed in #469/#471 are ABSENT.
11
+ // 4. Start the CLI via its real bin with a FRESH HOME + minimal config, then
12
+ // assert the HTTP server actually serves `/api/auth/status` and `/`.
13
+ //
14
+ // This is the check that catches the #479 failure (the server exiting during
15
+ // `prisma db push` in a packed prod-only install). Exits non-zero on any
16
+ // failure and prints the captured server output so a real failure is diagnosable.
17
+ //
18
+ // Heavier than the pack smoke (installs from the registry + boots the server +
19
+ // builds native deps), so it is a publish-gate check run by preflight. Set
20
+ // PREFLIGHT_SKIP_START_SMOKE=1 to skip during local iteration (preflight warns
21
+ // loudly when skipped — a skipped run is NOT publish-safe).
22
+
23
+ import { execFileSync, spawn } from "node:child_process";
24
+ import { mkdtempSync, rmSync, existsSync, writeFileSync, mkdirSync, readFileSync } from "node:fs";
25
+ import { tmpdir } from "node:os";
26
+ import { join, dirname } from "node:path";
27
+ import { fileURLToPath } from "node:url";
28
+
29
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
30
+ const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
31
+ const PORT = Number(process.env.SMOKE_PORT) || 7787;
32
+ const REMOVED_DEPS = ["@aws-sdk/client-s3", "react", "vite"]; // moved to devDeps in #469/#471
33
+ const BOOT_TIMEOUT_MS = 60_000;
34
+
35
+ let failures = 0;
36
+ const ok = (m) => console.log(` ✓ ${m}`);
37
+ const fail = (m) => { failures++; console.log(` ✗ ${m}`); };
38
+ const sh = (cmd, args, opts = {}) => execFileSync(cmd, args, { encoding: "utf8", ...opts });
39
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
40
+
41
+ const tmp = mkdtempSync(join(tmpdir(), "plotlink-start-smoke-"));
42
+ const home = join(tmp, "home");
43
+ mkdirSync(home, { recursive: true });
44
+ let child;
45
+ let serverOut = "";
46
+
47
+ try {
48
+ // 1) Pack the real tarball into the temp dir (no stray .tgz in the repo).
49
+ const tgz = JSON.parse(sh("npm", ["pack", "--pack-destination", tmp, "--json"], { cwd: root }))[0].filename;
50
+
51
+ // 2) Throwaway consumer; install prod-only WITH scripts (real user install).
52
+ writeFileSync(join(tmp, "package.json"), JSON.stringify({ name: "start-smoke", private: true, version: "0.0.0" }) + "\n");
53
+ sh("npm", ["install", join(tmp, tgz), "--omit=dev", "--no-audit", "--no-fund", "--no-save"], { cwd: tmp, stdio: "inherit" });
54
+ const installed = join(tmp, "node_modules", pkg.name);
55
+ if (!existsSync(installed)) throw new Error("package did not install into the consumer project");
56
+ ok("tarball installed prod-only (--omit=dev, scripts on)");
57
+
58
+ // 3) The web-app/build deps removed in #469/#471 must be absent (hoisted or nested).
59
+ const leaked = REMOVED_DEPS.filter(
60
+ (d) => existsSync(join(tmp, "node_modules", d)) || existsSync(join(installed, "node_modules", d)),
61
+ );
62
+ if (leaked.length) fail(`removed web-app/build deps present in prod install: ${leaked.join(", ")}`);
63
+ else ok(`removed deps absent from prod install (${REMOVED_DEPS.join(", ")})`);
64
+
65
+ // 4) Fresh HOME + minimal config so the bin starts the server (skips the wizard).
66
+ const cfgDir = join(home, ".plotlink-ows");
67
+ mkdirSync(cfgDir, { recursive: true });
68
+ writeFileSync(
69
+ join(cfgDir, "config.json"),
70
+ JSON.stringify({ port: PORT, passphrase_hash: "smoke", wallet_name: "plotlink-writer", created_at: "2026-01-01T00:00:00Z" }) + "\n",
71
+ );
72
+
73
+ // 5) Start via the real bin (exercises cmdStart → server → prisma db push).
74
+ const binPath = join(installed, "bin", "plotlink-ows.js");
75
+ child = spawn(process.execPath, [binPath], {
76
+ cwd: installed,
77
+ // PLOTLINK_OWS_NO_OPEN=1 stops the bin auto-opening a browser during this
78
+ // non-interactive release check (#481); normal `npx plotlink-ows` is unaffected.
79
+ env: { ...process.env, HOME: home, USERPROFILE: home, APP_PORT: String(PORT), PLOTLINK_OWS_NO_OPEN: "1" },
80
+ stdio: ["ignore", "pipe", "pipe"],
81
+ detached: true, // own process group, so we can kill the bin AND its server child
82
+ });
83
+ child.stdout.on("data", (d) => (serverOut += d));
84
+ child.stderr.on("data", (d) => (serverOut += d));
85
+
86
+ const deadline = Date.now() + BOOT_TIMEOUT_MS;
87
+ let statusOk = false;
88
+ let rootOk = false;
89
+ while (Date.now() < deadline) {
90
+ if (child.exitCode !== null) break; // bin exited (e.g. db push failed)
91
+ try {
92
+ const r = await fetch(`http://localhost:${PORT}/api/auth/status`);
93
+ if (r.ok) {
94
+ statusOk = true;
95
+ const rootRes = await fetch(`http://localhost:${PORT}/`);
96
+ rootOk = rootRes.ok && /<!doctype html|<html/i.test(await rootRes.text());
97
+ break;
98
+ }
99
+ } catch {
100
+ /* not listening yet */
101
+ }
102
+ await sleep(1000);
103
+ }
104
+
105
+ if (statusOk) ok("server serves GET /api/auth/status");
106
+ else fail(`server did not serve /api/auth/status within ${BOOT_TIMEOUT_MS / 1000}s (bin exitCode=${child.exitCode})`);
107
+ if (rootOk) ok("server serves GET / (prebuilt web UI)");
108
+ else fail("server did not serve the prebuilt web UI at /");
109
+
110
+ if (failures > 0 && serverOut.trim()) {
111
+ console.log("\n --- captured server output ---");
112
+ for (const line of serverOut.trimEnd().split("\n")) console.log(` | ${line}`);
113
+ }
114
+ } catch (e) {
115
+ fail(`start smoke errored: ${e?.message || e}`);
116
+ if (serverOut.trim()) {
117
+ console.log("\n --- captured server output ---");
118
+ for (const line of serverOut.trimEnd().split("\n")) console.log(` | ${line}`);
119
+ }
120
+ } finally {
121
+ if (child && child.pid && child.exitCode === null) {
122
+ try { process.kill(-child.pid, "SIGTERM"); } catch { /* group gone */ }
123
+ try { child.kill("SIGKILL"); } catch { /* already dead */ }
124
+ }
125
+ rmSync(tmp, { recursive: true, force: true });
126
+ }
127
+
128
+ process.exit(failures > 0 ? 1 : 0);
@@ -1 +0,0 @@
1
- export * from "./index"
@@ -1,5 +0,0 @@
1
-
2
- /* !!! This is code generated by Prisma. Do not edit directly. !!!
3
- /* eslint-disable */
4
- // biome-ignore-all lint: generated file
5
- module.exports = { ...require('.') }
@@ -1 +0,0 @@
1
- export * from "./index"
@@ -1,5 +0,0 @@
1
-
2
- /* !!! This is code generated by Prisma. Do not edit directly. !!!
3
- /* eslint-disable */
4
- // biome-ignore-all lint: generated file
5
- module.exports = { ...require('#main-entry-point') }
@@ -1 +0,0 @@
1
- export * from "./default"
@@ -1,184 +0,0 @@
1
-
2
- /* !!! This is code generated by Prisma. Do not edit directly. !!!
3
- /* eslint-disable */
4
- // biome-ignore-all lint: generated file
5
-
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
-
8
- const {
9
- PrismaClientKnownRequestError,
10
- PrismaClientUnknownRequestError,
11
- PrismaClientRustPanicError,
12
- PrismaClientInitializationError,
13
- PrismaClientValidationError,
14
- getPrismaClient,
15
- sqltag,
16
- empty,
17
- join,
18
- raw,
19
- skip,
20
- Decimal,
21
- Debug,
22
- objectEnumValues,
23
- makeStrictEnum,
24
- Extensions,
25
- warnOnce,
26
- defineDmmfProperty,
27
- Public,
28
- getRuntime,
29
- createParam,
30
- } = require('./runtime/edge.js')
31
-
32
-
33
- const Prisma = {}
34
-
35
- exports.Prisma = Prisma
36
- exports.$Enums = {}
37
-
38
- /**
39
- * Prisma Client JS version: 6.19.3
40
- * Query Engine version: c2990dca591cba766e3b7ef5d9e8a84796e47ab7
41
- */
42
- Prisma.prismaVersion = {
43
- client: "6.19.3",
44
- engine: "c2990dca591cba766e3b7ef5d9e8a84796e47ab7"
45
- }
46
-
47
- Prisma.PrismaClientKnownRequestError = PrismaClientKnownRequestError;
48
- Prisma.PrismaClientUnknownRequestError = PrismaClientUnknownRequestError
49
- Prisma.PrismaClientRustPanicError = PrismaClientRustPanicError
50
- Prisma.PrismaClientInitializationError = PrismaClientInitializationError
51
- Prisma.PrismaClientValidationError = PrismaClientValidationError
52
- Prisma.Decimal = Decimal
53
-
54
- /**
55
- * Re-export of sql-template-tag
56
- */
57
- Prisma.sql = sqltag
58
- Prisma.empty = empty
59
- Prisma.join = join
60
- Prisma.raw = raw
61
- Prisma.validator = Public.validator
62
-
63
- /**
64
- * Extensions
65
- */
66
- Prisma.getExtensionContext = Extensions.getExtensionContext
67
- Prisma.defineExtension = Extensions.defineExtension
68
-
69
- /**
70
- * Shorthand utilities for JSON filtering
71
- */
72
- Prisma.DbNull = objectEnumValues.instances.DbNull
73
- Prisma.JsonNull = objectEnumValues.instances.JsonNull
74
- Prisma.AnyNull = objectEnumValues.instances.AnyNull
75
-
76
- Prisma.NullTypes = {
77
- DbNull: objectEnumValues.classes.DbNull,
78
- JsonNull: objectEnumValues.classes.JsonNull,
79
- AnyNull: objectEnumValues.classes.AnyNull
80
- }
81
-
82
-
83
-
84
-
85
-
86
- /**
87
- * Enums
88
- */
89
- exports.Prisma.TransactionIsolationLevel = makeStrictEnum({
90
- Serializable: 'Serializable'
91
- });
92
-
93
- exports.Prisma.SessionScalarFieldEnum = {
94
- id: 'id',
95
- token: 'token',
96
- createdAt: 'createdAt',
97
- expiresAt: 'expiresAt'
98
- };
99
-
100
- exports.Prisma.SettingScalarFieldEnum = {
101
- key: 'key',
102
- value: 'value'
103
- };
104
-
105
- exports.Prisma.SortOrder = {
106
- asc: 'asc',
107
- desc: 'desc'
108
- };
109
-
110
-
111
- exports.Prisma.ModelName = {
112
- Session: 'Session',
113
- Setting: 'Setting'
114
- };
115
- /**
116
- * Create the Client
117
- */
118
- const config = {
119
- "generator": {
120
- "name": "client",
121
- "provider": {
122
- "fromEnvVar": null,
123
- "value": "prisma-client-js"
124
- },
125
- "output": {
126
- "value": "/Users/cho/Projects/plotlink-ows/app/node_modules/.prisma/local-client",
127
- "fromEnvVar": null
128
- },
129
- "config": {
130
- "engineType": "library"
131
- },
132
- "binaryTargets": [
133
- {
134
- "fromEnvVar": null,
135
- "value": "darwin-arm64",
136
- "native": true
137
- }
138
- ],
139
- "previewFeatures": [],
140
- "sourceFilePath": "/Users/cho/Projects/plotlink-ows/app/prisma/schema.prisma",
141
- "isCustomOutput": true
142
- },
143
- "relativeEnvPaths": {
144
- "rootEnvPath": null
145
- },
146
- "relativePath": "../../../prisma",
147
- "clientVersion": "6.19.3",
148
- "engineVersion": "c2990dca591cba766e3b7ef5d9e8a84796e47ab7",
149
- "datasourceNames": [
150
- "db"
151
- ],
152
- "activeProvider": "sqlite",
153
- "postinstall": false,
154
- "inlineDatasources": {
155
- "db": {
156
- "url": {
157
- "fromEnvVar": null,
158
- "value": "file:../../data/local.db"
159
- }
160
- }
161
- },
162
- "inlineSchema": "generator client {\n provider = \"prisma-client-js\"\n output = \"../node_modules/.prisma/local-client\"\n}\n\ndatasource db {\n provider = \"sqlite\"\n url = \"file:../../data/local.db\"\n}\n\nmodel Session {\n id String @id @default(cuid())\n token String @unique\n createdAt DateTime @default(now())\n expiresAt DateTime\n}\n\nmodel Setting {\n key String @id\n value String\n}\n",
163
- "inlineSchemaHash": "e31b194b10534203be1d4e09555579ffc3126c3700c5558a06db395e2bdfcdd9",
164
- "copyEngine": true
165
- }
166
- config.dirname = '/'
167
-
168
- config.runtimeDataModel = JSON.parse("{\"models\":{\"Session\":{\"dbName\":null,\"schema\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"nativeType\":null,\"default\":{\"name\":\"cuid\",\"args\":[1]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"token\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"nativeType\":null,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"nativeType\":null,\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"nativeType\":null,\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Setting\":{\"dbName\":null,\"schema\":null,\"fields\":[{\"name\":\"key\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"nativeType\":null,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"value\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"nativeType\":null,\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false}},\"enums\":{},\"types\":{}}")
169
- defineDmmfProperty(exports.Prisma, config.runtimeDataModel)
170
- config.engineWasm = undefined
171
- config.compilerWasm = undefined
172
-
173
- config.injectableEdgeEnv = () => ({
174
- parsed: {}
175
- })
176
-
177
- if (typeof globalThis !== 'undefined' && globalThis['DEBUG'] || typeof process !== 'undefined' && process.env && process.env.DEBUG || undefined) {
178
- Debug.enable(typeof globalThis !== 'undefined' && globalThis['DEBUG'] || typeof process !== 'undefined' && process.env && process.env.DEBUG || undefined)
179
- }
180
-
181
- const PrismaClient = getPrismaClient(config)
182
- exports.PrismaClient = PrismaClient
183
- Object.assign(exports, Prisma)
184
-