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.
- package/README.md +4 -0
- package/app/lib/agent-command.ts +85 -0
- package/app/lib/agent-readiness.ts +133 -0
- package/app/lib/apply-schema.ts +55 -0
- package/app/lib/bubble-text.ts +160 -0
- package/app/lib/cartoon-coach.ts +198 -0
- package/app/lib/cartoon-markdown.ts +83 -0
- package/app/lib/cartoon-prompt.ts +122 -0
- package/app/lib/cartoon-readiness.ts +811 -0
- package/app/lib/clean-image-sync.ts +245 -0
- package/app/lib/codex-images.ts +152 -0
- package/app/lib/cut-asset-diagnostics.ts +120 -0
- package/app/lib/cuts.ts +302 -0
- package/app/lib/fonts.ts +109 -0
- package/app/lib/generate-claude-md.ts +10 -3
- package/app/lib/generate-story-instructions.ts +731 -0
- package/app/lib/image-asset-validate.ts +123 -0
- package/app/lib/lettering-status.ts +133 -0
- package/app/lib/overlays.ts +637 -0
- package/app/lib/paths.ts +10 -0
- package/app/lib/public-title.ts +65 -0
- package/app/lib/publish.ts +16 -2
- package/app/lib/story-progress.ts +243 -0
- package/app/lib/terminal-protocol.ts +16 -0
- package/app/lib/terminal-redact.ts +50 -0
- package/app/prisma/schema.sql +25 -0
- package/app/routes/agent.ts +42 -0
- package/app/routes/codex-images.ts +67 -0
- package/app/routes/publish.ts +209 -28
- package/app/routes/stories.ts +961 -5
- package/app/routes/terminal.ts +383 -31
- package/app/server.ts +47 -12
- package/app/vite.config.ts +6 -0
- package/app/web/components/CartoonPreview.tsx +267 -0
- package/app/web/components/CartoonPublishPage.tsx +407 -0
- package/app/web/components/CartoonPublishPreview.tsx +121 -0
- package/app/web/components/CartoonStepGuide.tsx +90 -0
- package/app/web/components/CartoonWorkflowNav.tsx +68 -0
- package/app/web/components/CodexImportPicker.tsx +230 -0
- package/app/web/components/CutListPanel.tsx +1299 -0
- package/app/web/components/EpisodesPage.tsx +80 -0
- package/app/web/components/FinishEpisodePanel.tsx +151 -0
- package/app/web/components/Layout.tsx +7 -4
- package/app/web/components/LetteringEditor.tsx +1141 -0
- package/app/web/components/PreviewPanel.tsx +1017 -144
- package/app/web/components/Settings.tsx +63 -0
- package/app/web/components/StoriesPage.tsx +710 -33
- package/app/web/components/StoryBrowser.tsx +22 -14
- package/app/web/components/StoryInfoPage.tsx +266 -0
- package/app/web/components/StoryProgressPanel.tsx +516 -0
- package/app/web/components/TerminalPanel.tsx +233 -11
- package/app/web/components/WorkflowCoach.tsx +128 -0
- package/app/web/components/asset-image.tsx +114 -0
- package/app/web/components/asset-test-utils.ts +44 -0
- package/app/web/components/export-cut.ts +320 -0
- package/app/web/dist/assets/export-cut-nKQ_n2-J.js +1 -0
- package/app/web/dist/assets/index-BAZGwVwj.js +143 -0
- package/app/web/dist/assets/index-DoXH2OlP.css +32 -0
- package/app/web/dist/index.html +2 -2
- package/app/web/lib/cartoon-publish-summary.ts +43 -0
- package/app/web/lib/codex-import.ts +94 -0
- package/app/web/lib/image-compress.ts +53 -0
- package/app/web/lib/import-image.ts +58 -0
- package/app/web/lib/publish-helpers.ts +385 -0
- package/app/web/lib/upload-retry.ts +130 -0
- package/app/web/lib/verify-public-title.ts +105 -0
- package/app/web/styles.css +9 -0
- package/bin/plotlink-ows.js +53 -16
- package/bin/startup-plan.cjs +58 -0
- package/lib/genres.ts +92 -0
- package/package.json +60 -20
- package/scripts/gen-schema-sql.mjs +49 -0
- package/scripts/package-hygiene.mjs +116 -0
- package/scripts/preflight.mjs +173 -0
- package/scripts/start-smoke.mjs +128 -0
- package/app/node_modules/.prisma/local-client/client.d.ts +0 -1
- package/app/node_modules/.prisma/local-client/client.js +0 -5
- package/app/node_modules/.prisma/local-client/default.d.ts +0 -1
- package/app/node_modules/.prisma/local-client/default.js +0 -5
- package/app/node_modules/.prisma/local-client/edge.d.ts +0 -1
- package/app/node_modules/.prisma/local-client/edge.js +0 -184
- package/app/node_modules/.prisma/local-client/index-browser.js +0 -173
- package/app/node_modules/.prisma/local-client/index.d.ts +0 -3304
- package/app/node_modules/.prisma/local-client/index.js +0 -207
- package/app/node_modules/.prisma/local-client/libquery_engine-darwin-arm64.dylib.node +0 -0
- package/app/node_modules/.prisma/local-client/package.json +0 -183
- package/app/node_modules/.prisma/local-client/query_engine_bg.js +0 -2
- package/app/node_modules/.prisma/local-client/query_engine_bg.wasm +0 -0
- package/app/node_modules/.prisma/local-client/runtime/edge-esm.js +0 -35
- package/app/node_modules/.prisma/local-client/runtime/edge.js +0 -35
- package/app/node_modules/.prisma/local-client/runtime/index-browser.d.ts +0 -370
- package/app/node_modules/.prisma/local-client/runtime/index-browser.js +0 -17
- package/app/node_modules/.prisma/local-client/runtime/library.d.ts +0 -3982
- package/app/node_modules/.prisma/local-client/runtime/library.js +0 -147
- package/app/node_modules/.prisma/local-client/runtime/react-native.js +0 -84
- package/app/node_modules/.prisma/local-client/runtime/wasm-compiler-edge.js +0 -85
- package/app/node_modules/.prisma/local-client/runtime/wasm-engine-edge.js +0 -38
- package/app/node_modules/.prisma/local-client/schema.prisma +0 -21
- package/app/node_modules/.prisma/local-client/wasm-edge-light-loader.mjs +0 -5
- package/app/node_modules/.prisma/local-client/wasm-worker-loader.mjs +0 -5
- package/app/node_modules/.prisma/local-client/wasm.d.ts +0 -1
- package/app/node_modules/.prisma/local-client/wasm.js +0 -191
- package/app/web/dist/assets/index-B-2Ft7Yv.css +0 -32
- package/app/web/dist/assets/index-BFw-v-OZ.js +0 -134
- package/packages/cli/node_modules/commander/LICENSE +0 -22
- package/packages/cli/node_modules/commander/Readme.md +0 -1149
- package/packages/cli/node_modules/commander/esm.mjs +0 -16
- package/packages/cli/node_modules/commander/index.js +0 -24
- package/packages/cli/node_modules/commander/lib/argument.js +0 -149
- package/packages/cli/node_modules/commander/lib/command.js +0 -2662
- package/packages/cli/node_modules/commander/lib/error.js +0 -39
- package/packages/cli/node_modules/commander/lib/help.js +0 -709
- package/packages/cli/node_modules/commander/lib/option.js +0 -367
- package/packages/cli/node_modules/commander/lib/suggestSimilar.js +0 -101
- package/packages/cli/node_modules/commander/package-support.json +0 -16
- package/packages/cli/node_modules/commander/package.json +0 -82
- package/packages/cli/node_modules/commander/typings/esm.d.mts +0 -3
- package/packages/cli/node_modules/commander/typings/index.d.ts +0 -1045
- package/packages/cli/node_modules/resolve-from/index.d.ts +0 -31
- package/packages/cli/node_modules/resolve-from/index.js +0 -47
- package/packages/cli/node_modules/resolve-from/license +0 -9
- package/packages/cli/node_modules/resolve-from/package.json +0 -36
- package/packages/cli/node_modules/resolve-from/readme.md +0 -72
- package/packages/cli/node_modules/tsup/LICENSE +0 -21
- package/packages/cli/node_modules/tsup/README.md +0 -75
- package/packages/cli/node_modules/tsup/assets/cjs_shims.js +0 -13
- package/packages/cli/node_modules/tsup/assets/esm_shims.js +0 -9
- package/packages/cli/node_modules/tsup/assets/package.json +0 -3
- package/packages/cli/node_modules/tsup/dist/chunk-DI5BO6XE.js +0 -153
- package/packages/cli/node_modules/tsup/dist/chunk-JZ25TPTY.js +0 -42
- package/packages/cli/node_modules/tsup/dist/chunk-PEEXUWMS.js +0 -6
- package/packages/cli/node_modules/tsup/dist/chunk-TWFEYLU4.js +0 -352
- package/packages/cli/node_modules/tsup/dist/chunk-VGC3FXLU.js +0 -203
- package/packages/cli/node_modules/tsup/dist/cli-default.js +0 -12
- package/packages/cli/node_modules/tsup/dist/cli-main.js +0 -8
- package/packages/cli/node_modules/tsup/dist/cli-node.js +0 -14
- package/packages/cli/node_modules/tsup/dist/index.d.ts +0 -511
- package/packages/cli/node_modules/tsup/dist/index.js +0 -1711
- package/packages/cli/node_modules/tsup/dist/rollup.js +0 -6949
- package/packages/cli/node_modules/tsup/package.json +0 -99
- package/packages/cli/node_modules/tsup/schema.json +0 -362
- package/public/screenshot-1.png +0 -0
- package/public/screenshot-2.png +0 -0
- package/public/screenshot-3.png +0 -0
- 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 +0,0 @@
|
|
|
1
|
-
export * from "./index"
|
|
@@ -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
|
-
|