toiljs 0.0.58 → 0.0.60
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 +19 -0
- package/build/cli/.tsbuildinfo +1 -1
- package/build/cli/index.js +309 -116
- package/build/compiler/.tsbuildinfo +1 -1
- package/build/devserver/.tsbuildinfo +1 -1
- package/build/devserver/db/catalog.d.ts +1 -0
- package/build/devserver/db/catalog.js +80 -0
- package/build/devserver/db/database.d.ts +64 -0
- package/build/devserver/db/database.js +662 -0
- package/build/devserver/db/index.d.ts +3 -0
- package/build/devserver/db/index.js +3 -0
- package/build/devserver/db/types.d.ts +58 -0
- package/build/devserver/db/types.js +20 -0
- package/build/devserver/email/index.js +1 -1
- package/build/devserver/index.d.ts +9 -24
- package/build/devserver/index.js +4 -165
- package/build/devserver/{host.d.ts → runtime/host.d.ts} +1 -1
- package/build/devserver/{host.js → runtime/host.js} +6 -6
- package/build/devserver/{module.d.ts → runtime/module.d.ts} +1 -1
- package/build/devserver/{module.js → runtime/module.js} +8 -1
- package/build/devserver/server.d.ts +17 -0
- package/build/devserver/server.js +164 -0
- package/docs/time.md +2 -2
- package/examples/basic/server/migrations/GuestEntry.migration.ts +39 -0
- package/package.json +5 -2
- package/server/runtime/time.ts +3 -3
- package/src/cli/create.ts +38 -1
- package/src/cli/db.ts +158 -0
- package/src/cli/diagnostics.ts +19 -0
- package/src/cli/doctor.ts +20 -0
- package/src/cli/index.ts +10 -0
- package/src/cli/update.ts +58 -0
- package/src/devserver/db/catalog.ts +100 -0
- package/src/devserver/db/database.ts +1169 -0
- package/src/devserver/db/index.ts +18 -0
- package/src/devserver/db/types.ts +76 -0
- package/src/devserver/email/index.ts +1 -1
- package/src/devserver/index.ts +19 -287
- package/src/devserver/{host.ts → runtime/host.ts} +6 -6
- package/src/devserver/{module.ts → runtime/module.ts} +13 -1
- package/src/devserver/server.ts +292 -0
- package/test/db.test.ts +0 -0
- package/test/devserver-database.test.ts +114 -9
- package/test/devserver-pqauth.test.ts +1 -1
- package/test/devserver-secrets.test.ts +5 -1
- package/test/doctor.test.ts +13 -0
- package/test/example-guestbook.test.ts +43 -1
- package/test/pqauth-e2e.test.ts +1 -1
- package/build/devserver/database.d.ts +0 -8
- package/build/devserver/database.js +0 -418
- package/src/devserver/database.ts +0 -618
- /package/build/devserver/{dotenv.d.ts → config/dotenv.d.ts} +0 -0
- /package/build/devserver/{dotenv.js → config/dotenv.js} +0 -0
- /package/build/devserver/{env.d.ts → config/env.d.ts} +0 -0
- /package/build/devserver/{env.js → config/env.js} +0 -0
- /package/build/devserver/{ratelimit.d.ts → config/ratelimit.d.ts} +0 -0
- /package/build/devserver/{ratelimit.js → config/ratelimit.js} +0 -0
- /package/build/devserver/{cache.d.ts → http/cache.d.ts} +0 -0
- /package/build/devserver/{cache.js → http/cache.js} +0 -0
- /package/build/devserver/{envelope.d.ts → http/envelope.d.ts} +0 -0
- /package/build/devserver/{envelope.js → http/envelope.js} +0 -0
- /package/build/devserver/{proxy.d.ts → http/proxy.d.ts} +0 -0
- /package/build/devserver/{proxy.js → http/proxy.js} +0 -0
- /package/build/devserver/{crypto.d.ts → runtime/crypto.d.ts} +0 -0
- /package/build/devserver/{crypto.js → runtime/crypto.js} +0 -0
- /package/src/devserver/{dotenv.ts → config/dotenv.ts} +0 -0
- /package/src/devserver/{env.ts → config/env.ts} +0 -0
- /package/src/devserver/{ratelimit.ts → config/ratelimit.ts} +0 -0
- /package/src/devserver/{cache.ts → http/cache.ts} +0 -0
- /package/src/devserver/{envelope.ts → http/envelope.ts} +0 -0
- /package/src/devserver/{proxy.ts → http/proxy.ts} +0 -0
- /package/src/devserver/{crypto.ts → runtime/crypto.ts} +0 -0
package/build/cli/index.js
CHANGED
|
@@ -1666,12 +1666,12 @@ function gradientAt(t2) {
|
|
|
1666
1666
|
}
|
|
1667
1667
|
function gradientLine(line) {
|
|
1668
1668
|
const n2 = line.length;
|
|
1669
|
-
let
|
|
1669
|
+
let out2 = "";
|
|
1670
1670
|
for (let i2 = 0; i2 < n2; i2++) {
|
|
1671
1671
|
const [r2, g, b] = gradientAt(n2 > 1 ? i2 / (n2 - 1) : 0);
|
|
1672
|
-
|
|
1672
|
+
out2 += `\x1B[38;2;${r2};${g};${b}m${line[i2]}`;
|
|
1673
1673
|
}
|
|
1674
|
-
return
|
|
1674
|
+
return out2 + "\x1B[39m";
|
|
1675
1675
|
}
|
|
1676
1676
|
function version() {
|
|
1677
1677
|
try {
|
|
@@ -2010,6 +2010,7 @@ function isPackageManager(pm) {
|
|
|
2010
2010
|
}
|
|
2011
2011
|
|
|
2012
2012
|
// src/cli/create.ts
|
|
2013
|
+
var MIGRATIONS_README = "# migrations/\n\nToilDB schema migrations. One file per evolving `@data` value type, named `<Type>.migration.ts`\n(e.g. `User.migration.ts`).\n\nEach file keeps the OLD `@data` shapes (e.g. `UserV1`) alongside the `@migrate` transform(s) that\ncarry old records forward, and imports the CURRENT value type from the app. The build\nauto-discovers every `*.migration.ts` under the project.\n\nDo NOT put `@migrate` anywhere else: a `@migrate` outside a `*.migration.ts` file in a\n`migrations/` folder is a compile error.\n\n## Example\n\n`User.migration.ts`:\n\n```ts\nimport { User } from '../models/User';\n\n// The previous on-disk shape of a User record.\n@data\nexport class UserV1 {\n name: string = '';\n}\n\n// Carry a UserV1 forward into the current User. Runs once per record on read.\n@migrate\nexport function up(old: UserV1, into: User): void {\n into.name = old.name;\n}\n```\n";
|
|
2013
2014
|
var PREPROCESSOR_LABEL2 = {
|
|
2014
2015
|
css: "Plain CSS",
|
|
2015
2016
|
sass: "Sass (SCSS)",
|
|
@@ -2038,7 +2039,7 @@ function scaffold(name, template, features, aiTools, images) {
|
|
|
2038
2039
|
"@types/react-dom": "^19.2.3",
|
|
2039
2040
|
eslint: "^10.2.0",
|
|
2040
2041
|
prettier: "^3.8.1",
|
|
2041
|
-
toilscript: "^0.1.
|
|
2042
|
+
toilscript: "^0.1.37",
|
|
2042
2043
|
typescript: "^6.0.3"
|
|
2043
2044
|
};
|
|
2044
2045
|
for (const dep of requiredPackages(features).sort()) {
|
|
@@ -2170,7 +2171,11 @@ export default defineConfig({
|
|
|
2170
2171
|
"",
|
|
2171
2172
|
" npm run build",
|
|
2172
2173
|
""
|
|
2173
|
-
].join("\n")
|
|
2174
|
+
].join("\n"),
|
|
2175
|
+
// ToilDB migrations live under server/migrations/ (folder + `*.migration.ts` is enforced by
|
|
2176
|
+
// the compiler; the build auto-discovers them). Ship the folder, documented, with no live
|
|
2177
|
+
// *.migration.ts: a @migrate referencing types a fresh project lacks would break the build.
|
|
2178
|
+
"server/migrations/README.md": MIGRATIONS_README
|
|
2174
2179
|
};
|
|
2175
2180
|
if (template === "minimal") {
|
|
2176
2181
|
Object.assign(files, minimalClient(name, features));
|
|
@@ -2223,7 +2228,7 @@ function minimalServer() {
|
|
|
2223
2228
|
"server/toil-server-env.d.ts": TOIL_SERVER_ENV_DTS,
|
|
2224
2229
|
"server/core/AppHandler.ts": "import { ToilHandler, Request, Response, Method } from 'toiljs/server/runtime';\n\n/** Every request enters here. Add `@rest` controllers under routes/ as you grow. */\nexport class AppHandler extends ToilHandler {\n public handle(req: Request): Response {\n if (req.method != Method.GET && req.method != Method.HEAD) {\n return Response.empty(405).setHeader('allow', 'GET, HEAD');\n }\n if (req.path == '/api/hello') {\n return Response.text('hello from toiljs\\n');\n }\n if (req.path == '/api/hash') {\n // `crypto` is a global (no import), synchronous Web Crypto.\n return Response.text(crypto.toHex(crypto.sha256Text(req.path)) + '\\n');\n }\n // Yield page routes and assets to the client: under `toiljs dev`\n // this falls through to Vite so the app renders at /.\n return Response.unhandled();\n }\n}\n",
|
|
2225
2230
|
"server/main.ts": "import { Server } from 'toiljs/server/runtime';\nimport { revertOnError } from 'toiljs/server/runtime/abort/abort';\n\nimport { AppHandler } from './core/AppHandler';\n\n// As you add surface modules (@rest routes, @service/@remote RPC), import them here\n// so a direct `toilscript` run builds the same server `toiljs build` does, e.g.:\n// import './routes/Players';\n\n// Wire your handler here.\nServer.handler = () => new AppHandler();\n\n// Required: re-export the WASM entry points and the abort hook.\nexport * from 'toiljs/server/runtime/exports';\nexport function abort(message: string, fileName: string, line: u32, column: u32): void {\n revertOnError(message, fileName, line, column);\n}\n",
|
|
2226
|
-
"server/README.md": "# server/\n\nYour ToilScript backend, compiled to a single WebAssembly module. One folder per concern:\n\n| Folder | What lives here |\n| --- | --- |\n| `main.ts` | The entry point: wires the handler and imports the surface modules. |\n| `core/` | The request handler and shared app logic (state, helpers). |\n| `models/` | `@data` classes, the typed wire model shared by HTTP and RPC. One type per file. |\n| `routes/` | `@rest` controllers (HTTP). One controller per file, named after its class. |\n| `services/` | `@service` classes and free `@remote` functions (typed RPC). |\n| `scheduled/` | Reserved for scheduled tasks (not shipped yet). |\n\nNew decorated files are picked up automatically by `toiljs build`/`dev`; also add an import\nin `main.ts` so a direct `toilscript` run builds the same server.\n"
|
|
2231
|
+
"server/README.md": "# server/\n\nYour ToilScript backend, compiled to a single WebAssembly module. One folder per concern:\n\n| Folder | What lives here |\n| --- | --- |\n| `main.ts` | The entry point: wires the handler and imports the surface modules. |\n| `core/` | The request handler and shared app logic (state, helpers). |\n| `models/` | `@data` classes, the typed wire model shared by HTTP and RPC. One type per file. |\n| `migrations/` | ToilDB schema migrations: a `<Type>.migration.ts` per evolving `@data` value type, holding the old shapes + the `@migrate` transform. |\n| `routes/` | `@rest` controllers (HTTP). One controller per file, named after its class. |\n| `services/` | `@service` classes and free `@remote` functions (typed RPC). |\n| `scheduled/` | Reserved for scheduled tasks (not shipped yet). |\n\nNew decorated files are picked up automatically by `toiljs build`/`dev`; also add an import\nin `main.ts` so a direct `toilscript` run builds the same server.\n"
|
|
2227
2232
|
};
|
|
2228
2233
|
}
|
|
2229
2234
|
function minimalClient(name, features) {
|
|
@@ -2528,10 +2533,137 @@ async function runCreate(opts) {
|
|
|
2528
2533
|
outro(`Created ${accent(path4.basename(name))}, happy building! ${dim("(v" + version() + ")")}`);
|
|
2529
2534
|
}
|
|
2530
2535
|
|
|
2531
|
-
// src/cli/
|
|
2536
|
+
// src/cli/db.ts
|
|
2532
2537
|
import fs4 from "node:fs";
|
|
2533
|
-
import { createRequire } from "node:module";
|
|
2534
2538
|
import path5 from "node:path";
|
|
2539
|
+
var FAMILIES = ["store", "views", "members", "counters", "events", "eventDedup", "capacity"];
|
|
2540
|
+
function devdataPath(opts) {
|
|
2541
|
+
return path5.join(path5.resolve(opts.root ?? process.cwd()), ".toil", "devdata.json");
|
|
2542
|
+
}
|
|
2543
|
+
function out(line) {
|
|
2544
|
+
process.stdout.write(line + "\n");
|
|
2545
|
+
}
|
|
2546
|
+
function familyCounts(file) {
|
|
2547
|
+
let snap;
|
|
2548
|
+
try {
|
|
2549
|
+
snap = JSON.parse(fs4.readFileSync(file, "utf8"));
|
|
2550
|
+
} catch {
|
|
2551
|
+
return null;
|
|
2552
|
+
}
|
|
2553
|
+
const counts = {};
|
|
2554
|
+
for (const f of FAMILIES) {
|
|
2555
|
+
const fam = snap[f];
|
|
2556
|
+
counts[f] = fam && typeof fam === "object" ? Object.keys(fam).length : 0;
|
|
2557
|
+
}
|
|
2558
|
+
return counts;
|
|
2559
|
+
}
|
|
2560
|
+
function isSnapshot(v) {
|
|
2561
|
+
return typeof v === "object" && v !== null && FAMILIES.every((f) => f in v);
|
|
2562
|
+
}
|
|
2563
|
+
function runDb(action, fileArg, opts) {
|
|
2564
|
+
const file = devdataPath(opts);
|
|
2565
|
+
const rel = path5.relative(process.cwd(), file) || file;
|
|
2566
|
+
switch (action) {
|
|
2567
|
+
case "reset":
|
|
2568
|
+
case "purge": {
|
|
2569
|
+
if (!fs4.existsSync(file)) {
|
|
2570
|
+
out(dim(` dev database already empty (${rel} not found)`));
|
|
2571
|
+
return;
|
|
2572
|
+
}
|
|
2573
|
+
fs4.rmSync(file);
|
|
2574
|
+
out(success(" \u2713 ") + "dev database reset " + dim(`(${rel} deleted)`));
|
|
2575
|
+
return;
|
|
2576
|
+
}
|
|
2577
|
+
case "export": {
|
|
2578
|
+
if (!fs4.existsSync(file)) {
|
|
2579
|
+
out(warn(" ! ") + `nothing to export (${rel} not found)`);
|
|
2580
|
+
process.exitCode = 1;
|
|
2581
|
+
return;
|
|
2582
|
+
}
|
|
2583
|
+
let pretty;
|
|
2584
|
+
try {
|
|
2585
|
+
pretty = JSON.stringify(JSON.parse(fs4.readFileSync(file, "utf8")), null, 2) + "\n";
|
|
2586
|
+
} catch {
|
|
2587
|
+
out(danger(" \u2717 ") + `the dev database at ${rel} is unreadable`);
|
|
2588
|
+
process.exitCode = 1;
|
|
2589
|
+
return;
|
|
2590
|
+
}
|
|
2591
|
+
if (fileArg === void 0) {
|
|
2592
|
+
process.stdout.write(pretty);
|
|
2593
|
+
return;
|
|
2594
|
+
}
|
|
2595
|
+
fs4.writeFileSync(path5.resolve(fileArg), pretty);
|
|
2596
|
+
out(success(" \u2713 ") + "exported dev database to " + dim(fileArg));
|
|
2597
|
+
return;
|
|
2598
|
+
}
|
|
2599
|
+
case "import": {
|
|
2600
|
+
if (fileArg === void 0) {
|
|
2601
|
+
out(danger(" \u2717 ") + "usage: " + dim("toiljs db import <file>"));
|
|
2602
|
+
process.exitCode = 1;
|
|
2603
|
+
return;
|
|
2604
|
+
}
|
|
2605
|
+
let parsed;
|
|
2606
|
+
try {
|
|
2607
|
+
parsed = JSON.parse(fs4.readFileSync(path5.resolve(fileArg), "utf8"));
|
|
2608
|
+
} catch (e) {
|
|
2609
|
+
out(danger(" \u2717 ") + `cannot read/parse ${fileArg}: ${e.message}`);
|
|
2610
|
+
process.exitCode = 1;
|
|
2611
|
+
return;
|
|
2612
|
+
}
|
|
2613
|
+
if (!isSnapshot(parsed)) {
|
|
2614
|
+
out(danger(" \u2717 ") + `${fileArg} is not a toiljs dev database snapshot`);
|
|
2615
|
+
process.exitCode = 1;
|
|
2616
|
+
return;
|
|
2617
|
+
}
|
|
2618
|
+
fs4.mkdirSync(path5.dirname(file), { recursive: true });
|
|
2619
|
+
fs4.writeFileSync(file, JSON.stringify(parsed));
|
|
2620
|
+
out(success(" \u2713 ") + `imported dev database from ${dim(fileArg)} ` + dim(`(${rel})`));
|
|
2621
|
+
return;
|
|
2622
|
+
}
|
|
2623
|
+
case "path":
|
|
2624
|
+
out(file);
|
|
2625
|
+
return;
|
|
2626
|
+
case "status":
|
|
2627
|
+
case "info": {
|
|
2628
|
+
out(bold(" dev database") + dim(` ${rel}`));
|
|
2629
|
+
if (!fs4.existsSync(file)) {
|
|
2630
|
+
out(dim(" (empty \u2014 no data written yet; run the dev server to populate it)"));
|
|
2631
|
+
return;
|
|
2632
|
+
}
|
|
2633
|
+
const counts = familyCounts(file);
|
|
2634
|
+
if (counts === null) {
|
|
2635
|
+
out(danger(" \u2717 unreadable snapshot"));
|
|
2636
|
+
process.exitCode = 1;
|
|
2637
|
+
return;
|
|
2638
|
+
}
|
|
2639
|
+
out(dim(` ${(fs4.statSync(file).size / 1024).toFixed(1)} KiB`));
|
|
2640
|
+
const nonEmpty = FAMILIES.filter((f) => counts[f] > 0);
|
|
2641
|
+
if (nonEmpty.length === 0) out(dim(" (no rows)"));
|
|
2642
|
+
else for (const f of nonEmpty) out(" " + accent(f.padEnd(12)) + String(counts[f]));
|
|
2643
|
+
return;
|
|
2644
|
+
}
|
|
2645
|
+
default:
|
|
2646
|
+
out(
|
|
2647
|
+
[
|
|
2648
|
+
bold("Usage") + " " + dim("toiljs db") + " <action>",
|
|
2649
|
+
"",
|
|
2650
|
+
bold("Actions"),
|
|
2651
|
+
" " + accent("status".padEnd(16)) + dim("show the dev DB path + per-family row counts"),
|
|
2652
|
+
" " + accent("reset".padEnd(16)) + dim("delete all dev data (alias: purge)"),
|
|
2653
|
+
" " + accent("export [file]".padEnd(16)) + dim("write a snapshot to <file> (or stdout)"),
|
|
2654
|
+
" " + accent("import <file>".padEnd(16)) + dim("replace the dev DB with a snapshot"),
|
|
2655
|
+
" " + accent("path".padEnd(16)) + dim("print the devdata.json path")
|
|
2656
|
+
].join("\n")
|
|
2657
|
+
);
|
|
2658
|
+
if (action !== void 0) process.exitCode = 1;
|
|
2659
|
+
return;
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
// src/cli/doctor.ts
|
|
2664
|
+
import fs5 from "node:fs";
|
|
2665
|
+
import { createRequire } from "node:module";
|
|
2666
|
+
import path6 from "node:path";
|
|
2535
2667
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
2536
2668
|
import {
|
|
2537
2669
|
loadConfig as loadConfig2,
|
|
@@ -2879,6 +3011,15 @@ function checkServerTsPlugin(present) {
|
|
|
2879
3011
|
fix: 'Run `toiljs doctor --fix` to add { "plugins": [{ "name": "toilscript/std/ts-plugin.cjs" }] } to your server tsconfig, then pick the workspace TypeScript version and restart the TS server.'
|
|
2880
3012
|
};
|
|
2881
3013
|
}
|
|
3014
|
+
function checkMigrationsDir(exists) {
|
|
3015
|
+
return exists ? { id: "migrations-dir", label: "server/migrations/ directory", status: "pass" } : {
|
|
3016
|
+
id: "migrations-dir",
|
|
3017
|
+
label: "server/migrations/ directory",
|
|
3018
|
+
status: "warn",
|
|
3019
|
+
detail: "no server/migrations/ folder; ToilDB @migrate functions must live in a *.migration.ts file under it",
|
|
3020
|
+
fix: "Create server/migrations/ (one <Type>.migration.ts per evolving @data value type), or run `toiljs update` to add it."
|
|
3021
|
+
};
|
|
3022
|
+
}
|
|
2882
3023
|
function checkAuthSecrets(f) {
|
|
2883
3024
|
if (!f.usesAuth || f.sessionSecretSet) {
|
|
2884
3025
|
return { id: "auth-secrets", label: "Session secret", status: "pass" };
|
|
@@ -2911,7 +3052,7 @@ function hasFailures(summary) {
|
|
|
2911
3052
|
// src/cli/doctor.ts
|
|
2912
3053
|
function readJsonObject(file) {
|
|
2913
3054
|
try {
|
|
2914
|
-
const parsed = JSON.parse(
|
|
3055
|
+
const parsed = JSON.parse(fs5.readFileSync(file, "utf8"));
|
|
2915
3056
|
return typeof parsed === "object" && parsed !== null ? parsed : null;
|
|
2916
3057
|
} catch {
|
|
2917
3058
|
return null;
|
|
@@ -2919,22 +3060,22 @@ function readJsonObject(file) {
|
|
|
2919
3060
|
}
|
|
2920
3061
|
function stringRecord(value) {
|
|
2921
3062
|
if (typeof value !== "object" || value === null) return {};
|
|
2922
|
-
const
|
|
2923
|
-
for (const [k, v] of Object.entries(value)) if (typeof v === "string")
|
|
2924
|
-
return
|
|
3063
|
+
const out2 = {};
|
|
3064
|
+
for (const [k, v] of Object.entries(value)) if (typeof v === "string") out2[k] = v;
|
|
3065
|
+
return out2;
|
|
2925
3066
|
}
|
|
2926
3067
|
function readFile(file) {
|
|
2927
3068
|
try {
|
|
2928
|
-
return
|
|
3069
|
+
return fs5.readFileSync(file, "utf8");
|
|
2929
3070
|
} catch {
|
|
2930
3071
|
return null;
|
|
2931
3072
|
}
|
|
2932
3073
|
}
|
|
2933
3074
|
function writeFile(file, content) {
|
|
2934
|
-
|
|
3075
|
+
fs5.writeFileSync(file, content);
|
|
2935
3076
|
}
|
|
2936
3077
|
function isPackageInstalled(root, name) {
|
|
2937
|
-
const require2 = createRequire(
|
|
3078
|
+
const require2 = createRequire(path6.join(root, "package.json"));
|
|
2938
3079
|
for (const id of [`${name}/package.json`, name]) {
|
|
2939
3080
|
try {
|
|
2940
3081
|
require2.resolve(id);
|
|
@@ -2943,8 +3084,8 @@ function isPackageInstalled(root, name) {
|
|
|
2943
3084
|
}
|
|
2944
3085
|
}
|
|
2945
3086
|
for (let dir = root; ; ) {
|
|
2946
|
-
if (
|
|
2947
|
-
const parent =
|
|
3087
|
+
if (fs5.existsSync(path6.join(dir, "node_modules", name, "package.json"))) return true;
|
|
3088
|
+
const parent = path6.dirname(dir);
|
|
2948
3089
|
if (parent === dir) return false;
|
|
2949
3090
|
dir = parent;
|
|
2950
3091
|
}
|
|
@@ -2959,14 +3100,14 @@ function looksLikeSemverRange(range) {
|
|
|
2959
3100
|
return /^\s*[v^~>=<]*\s*\d+\.\d+/.test(range);
|
|
2960
3101
|
}
|
|
2961
3102
|
function gatherRpcFacts(root) {
|
|
2962
|
-
const pkg = readJsonObject(
|
|
3103
|
+
const pkg = readJsonObject(path6.join(root, "package.json"));
|
|
2963
3104
|
const scripts = pkg ? stringRecord(pkg.scripts) : {};
|
|
2964
3105
|
const deps = {
|
|
2965
3106
|
...pkg ? stringRecord(pkg.dependencies) : {},
|
|
2966
3107
|
...pkg ? stringRecord(pkg.devDependencies) : {}
|
|
2967
3108
|
};
|
|
2968
|
-
const tsconfig = readJsonObject(
|
|
2969
|
-
const gitignore = readFile(
|
|
3109
|
+
const tsconfig = readJsonObject(path6.join(root, "tsconfig.json"));
|
|
3110
|
+
const gitignore = readFile(path6.join(root, ".gitignore"));
|
|
2970
3111
|
const buildServerWired = [scripts["build:server"], scripts["build"]].some(
|
|
2971
3112
|
(s) => typeof s === "string" && s.includes("--rpcModule")
|
|
2972
3113
|
);
|
|
@@ -2985,31 +3126,31 @@ function gatherRpcFacts(root) {
|
|
|
2985
3126
|
function serverSources(root, toilconfig) {
|
|
2986
3127
|
const dirs = /* @__PURE__ */ new Set();
|
|
2987
3128
|
const entries = Array.isArray(toilconfig?.entries) ? toilconfig.entries.filter((e) => typeof e === "string") : [];
|
|
2988
|
-
for (const e of entries) dirs.add(
|
|
2989
|
-
const
|
|
3129
|
+
for (const e of entries) dirs.add(path6.dirname(path6.resolve(root, e)));
|
|
3130
|
+
const out2 = [];
|
|
2990
3131
|
const cap = 200;
|
|
2991
3132
|
const maxDepth = 16;
|
|
2992
3133
|
const visit = (current, depth) => {
|
|
2993
|
-
if (
|
|
3134
|
+
if (out2.length >= cap || depth > maxDepth) return;
|
|
2994
3135
|
let listing;
|
|
2995
3136
|
try {
|
|
2996
|
-
listing =
|
|
3137
|
+
listing = fs5.readdirSync(current, { withFileTypes: true });
|
|
2997
3138
|
} catch {
|
|
2998
3139
|
return;
|
|
2999
3140
|
}
|
|
3000
3141
|
for (const entry of listing) {
|
|
3001
|
-
if (
|
|
3002
|
-
const full =
|
|
3142
|
+
if (out2.length >= cap) break;
|
|
3143
|
+
const full = path6.join(current, entry.name);
|
|
3003
3144
|
if (entry.isDirectory()) {
|
|
3004
3145
|
if (entry.name !== "node_modules") visit(full, depth + 1);
|
|
3005
3146
|
} else if (entry.name.endsWith(".ts") && !entry.name.endsWith(".d.ts")) {
|
|
3006
3147
|
const src = readFile(full);
|
|
3007
|
-
if (src !== null)
|
|
3148
|
+
if (src !== null) out2.push(src);
|
|
3008
3149
|
}
|
|
3009
3150
|
}
|
|
3010
3151
|
};
|
|
3011
3152
|
for (const dir of dirs) visit(dir, 0);
|
|
3012
|
-
return
|
|
3153
|
+
return out2;
|
|
3013
3154
|
}
|
|
3014
3155
|
function gatherRestFacts(root, toilconfig) {
|
|
3015
3156
|
let hasControllers = false;
|
|
@@ -3023,7 +3164,7 @@ function gatherRestFacts(root, toilconfig) {
|
|
|
3023
3164
|
return { hasControllers, dispatched };
|
|
3024
3165
|
}
|
|
3025
3166
|
function secretDefined(root, key) {
|
|
3026
|
-
const raw = readFile(
|
|
3167
|
+
const raw = readFile(path6.join(root, ".env.secrets"));
|
|
3027
3168
|
if (raw === null) return false;
|
|
3028
3169
|
return new RegExp(`^\\s*(?:export\\s+)?${key}\\s*=\\s*\\S`, "m").test(raw);
|
|
3029
3170
|
}
|
|
@@ -3041,14 +3182,24 @@ var TS_PLUGIN_NAME = "toilscript/std/ts-plugin.cjs";
|
|
|
3041
3182
|
function serverTsconfigPath(root, toilconfig) {
|
|
3042
3183
|
const dirs = /* @__PURE__ */ new Set();
|
|
3043
3184
|
const entries = Array.isArray(toilconfig?.entries) ? toilconfig.entries.filter((e) => typeof e === "string") : [];
|
|
3044
|
-
for (const e of entries) dirs.add(
|
|
3045
|
-
if (dirs.size === 0) dirs.add(
|
|
3185
|
+
for (const e of entries) dirs.add(path6.dirname(path6.resolve(root, e)));
|
|
3186
|
+
if (dirs.size === 0) dirs.add(path6.join(root, "server"));
|
|
3046
3187
|
for (const dir of dirs) {
|
|
3047
|
-
const p =
|
|
3048
|
-
if (
|
|
3188
|
+
const p = path6.join(dir, "tsconfig.json");
|
|
3189
|
+
if (fs5.existsSync(p)) return p;
|
|
3049
3190
|
}
|
|
3050
3191
|
return null;
|
|
3051
3192
|
}
|
|
3193
|
+
function serverMigrationsExist(root, toilconfig) {
|
|
3194
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
3195
|
+
const entries = Array.isArray(toilconfig?.entries) ? toilconfig.entries.filter((e) => typeof e === "string") : [];
|
|
3196
|
+
for (const e of entries) dirs.add(path6.dirname(path6.resolve(root, e)));
|
|
3197
|
+
if (dirs.size === 0) dirs.add(path6.join(root, "server"));
|
|
3198
|
+
for (const dir of dirs) {
|
|
3199
|
+
if (fs5.existsSync(path6.join(dir, "migrations"))) return true;
|
|
3200
|
+
}
|
|
3201
|
+
return false;
|
|
3202
|
+
}
|
|
3052
3203
|
function tsconfigHasToilPlugin(tsconfig) {
|
|
3053
3204
|
const plugins = asRecord(tsconfig?.compilerOptions)?.plugins;
|
|
3054
3205
|
if (!Array.isArray(plugins)) return false;
|
|
@@ -3060,7 +3211,7 @@ function tsconfigHasToilPlugin(tsconfig) {
|
|
|
3060
3211
|
function applyRpcFix(root) {
|
|
3061
3212
|
const changed = [];
|
|
3062
3213
|
const skipped = [];
|
|
3063
|
-
const pkgPath =
|
|
3214
|
+
const pkgPath = path6.join(root, "package.json");
|
|
3064
3215
|
const pkgRaw = readFile(pkgPath);
|
|
3065
3216
|
const pkg = pkgRaw !== null ? readJsonObject(pkgPath) : null;
|
|
3066
3217
|
if (pkg !== null) {
|
|
@@ -3099,7 +3250,7 @@ function applyRpcFix(root) {
|
|
|
3099
3250
|
} else if (pkgRaw !== null) {
|
|
3100
3251
|
skipped.push("package.json (unparseable)");
|
|
3101
3252
|
}
|
|
3102
|
-
const tsPath =
|
|
3253
|
+
const tsPath = path6.join(root, "tsconfig.json");
|
|
3103
3254
|
const tsRaw = readFile(tsPath);
|
|
3104
3255
|
const tsconfig = tsRaw !== null ? readJsonObject(tsPath) : null;
|
|
3105
3256
|
if (tsconfig !== null) {
|
|
@@ -3138,7 +3289,7 @@ function applyRpcFix(root) {
|
|
|
3138
3289
|
} else if (tsRaw !== null) {
|
|
3139
3290
|
skipped.push('tsconfig.json (JSON with comments, add "shared" + paths by hand)');
|
|
3140
3291
|
}
|
|
3141
|
-
const giPath =
|
|
3292
|
+
const giPath = path6.join(root, ".gitignore");
|
|
3142
3293
|
const giRaw = readFile(giPath);
|
|
3143
3294
|
if (giRaw === null) {
|
|
3144
3295
|
writeFile(giPath, `${RPC_GITIGNORE_LINE}
|
|
@@ -3150,21 +3301,21 @@ function applyRpcFix(root) {
|
|
|
3150
3301
|
`);
|
|
3151
3302
|
changed.push(".gitignore");
|
|
3152
3303
|
}
|
|
3153
|
-
const serverToilconfig = readJsonObject(
|
|
3304
|
+
const serverToilconfig = readJsonObject(path6.join(root, "toilconfig.json"));
|
|
3154
3305
|
if (serverToilconfig !== null) {
|
|
3155
3306
|
const entries = Array.isArray(serverToilconfig.entries) ? serverToilconfig.entries.filter(
|
|
3156
3307
|
(e) => typeof e === "string"
|
|
3157
3308
|
) : [];
|
|
3158
3309
|
const dirs = /* @__PURE__ */ new Set();
|
|
3159
|
-
for (const e of entries) dirs.add(
|
|
3160
|
-
if (dirs.size === 0) dirs.add(
|
|
3310
|
+
for (const e of entries) dirs.add(path6.dirname(path6.resolve(root, e)));
|
|
3311
|
+
if (dirs.size === 0) dirs.add(path6.join(root, "server"));
|
|
3161
3312
|
for (const dir of dirs) {
|
|
3162
|
-
const envPath =
|
|
3313
|
+
const envPath = path6.join(dir, "toil-server-env.d.ts");
|
|
3163
3314
|
if (readFile(envPath) !== TOIL_SERVER_ENV_DTS2) {
|
|
3164
3315
|
try {
|
|
3165
|
-
|
|
3316
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
3166
3317
|
writeFile(envPath, TOIL_SERVER_ENV_DTS2);
|
|
3167
|
-
changed.push(
|
|
3318
|
+
changed.push(path6.relative(root, envPath));
|
|
3168
3319
|
} catch {
|
|
3169
3320
|
}
|
|
3170
3321
|
}
|
|
@@ -3194,7 +3345,7 @@ function prettierPluginPresent(root, pkg) {
|
|
|
3194
3345
|
return true;
|
|
3195
3346
|
}
|
|
3196
3347
|
for (const name of PRETTIER_CONFIG_FILES) {
|
|
3197
|
-
const raw = readFile(
|
|
3348
|
+
const raw = readFile(path6.join(root, name));
|
|
3198
3349
|
if (raw !== null && PRETTIER_MENTION.test(raw)) return true;
|
|
3199
3350
|
}
|
|
3200
3351
|
return false;
|
|
@@ -3203,7 +3354,7 @@ function applyPrettierFix(root, pkg) {
|
|
|
3203
3354
|
const changed = [];
|
|
3204
3355
|
const skipped = [];
|
|
3205
3356
|
if (prettierPluginPresent(root, pkg)) return { changed, skipped };
|
|
3206
|
-
const pkgPath =
|
|
3357
|
+
const pkgPath = path6.join(root, "package.json");
|
|
3207
3358
|
const pkgConfig = pkg ? asRecord(pkg.prettier) : null;
|
|
3208
3359
|
if (pkgConfig !== null) {
|
|
3209
3360
|
const full = readJsonObject(pkgPath);
|
|
@@ -3219,7 +3370,7 @@ function applyPrettierFix(root, pkg) {
|
|
|
3219
3370
|
}
|
|
3220
3371
|
}
|
|
3221
3372
|
for (const name of [".prettierrc", ".prettierrc.json"]) {
|
|
3222
|
-
const filePath =
|
|
3373
|
+
const filePath = path6.join(root, name);
|
|
3223
3374
|
const raw = readFile(filePath);
|
|
3224
3375
|
if (raw === null) continue;
|
|
3225
3376
|
const obj = readJsonObject(filePath);
|
|
@@ -3232,13 +3383,13 @@ function applyPrettierFix(root, pkg) {
|
|
|
3232
3383
|
changed.push(name);
|
|
3233
3384
|
return { changed, skipped };
|
|
3234
3385
|
}
|
|
3235
|
-
const jsConfig = PRETTIER_CONFIG_FILES.find((name) => readFile(
|
|
3386
|
+
const jsConfig = PRETTIER_CONFIG_FILES.find((name) => readFile(path6.join(root, name)) !== null);
|
|
3236
3387
|
if (jsConfig) {
|
|
3237
3388
|
skipped.push(`${jsConfig} (add "${PRETTIER_PLUGIN}" to its plugins by hand)`);
|
|
3238
3389
|
return { changed, skipped };
|
|
3239
3390
|
}
|
|
3240
3391
|
writeFile(
|
|
3241
|
-
|
|
3392
|
+
path6.join(root, ".prettierrc.json"),
|
|
3242
3393
|
JSON.stringify({ plugins: [PRETTIER_PLUGIN] }, null, 4) + "\n"
|
|
3243
3394
|
);
|
|
3244
3395
|
changed.push(".prettierrc.json");
|
|
@@ -3251,7 +3402,7 @@ function applyServerEditorFix(root, toilconfig) {
|
|
|
3251
3402
|
if (tsPath === null) {
|
|
3252
3403
|
skipped.push("server/tsconfig.json (not found; add the toilscript ts-plugin by hand)");
|
|
3253
3404
|
} else {
|
|
3254
|
-
const rel =
|
|
3405
|
+
const rel = path6.relative(root, tsPath);
|
|
3255
3406
|
const raw = readFile(tsPath);
|
|
3256
3407
|
const parsed = raw !== null ? readJsonObject(tsPath) : null;
|
|
3257
3408
|
if (parsed === null) {
|
|
@@ -3265,7 +3416,7 @@ function applyServerEditorFix(root, toilconfig) {
|
|
|
3265
3416
|
changed.push(rel);
|
|
3266
3417
|
}
|
|
3267
3418
|
}
|
|
3268
|
-
const vsPath =
|
|
3419
|
+
const vsPath = path6.join(root, ".vscode", "settings.json");
|
|
3269
3420
|
const vsRaw = readFile(vsPath);
|
|
3270
3421
|
const vs = vsRaw !== null ? readJsonObject(vsPath) : {};
|
|
3271
3422
|
if (vs === null) {
|
|
@@ -3281,7 +3432,7 @@ function applyServerEditorFix(root, toilconfig) {
|
|
|
3281
3432
|
touched = true;
|
|
3282
3433
|
}
|
|
3283
3434
|
if (touched) {
|
|
3284
|
-
|
|
3435
|
+
fs5.mkdirSync(path6.dirname(vsPath), { recursive: true });
|
|
3285
3436
|
writeFile(vsPath, JSON.stringify(vs, null, 4) + "\n");
|
|
3286
3437
|
changed.push(".vscode/settings.json");
|
|
3287
3438
|
}
|
|
@@ -3289,8 +3440,8 @@ function applyServerEditorFix(root, toilconfig) {
|
|
|
3289
3440
|
return { changed, skipped };
|
|
3290
3441
|
}
|
|
3291
3442
|
function frameworkMeta() {
|
|
3292
|
-
const pkgPath =
|
|
3293
|
-
|
|
3443
|
+
const pkgPath = path6.resolve(
|
|
3444
|
+
path6.dirname(fileURLToPath3(import.meta.url)),
|
|
3294
3445
|
"..",
|
|
3295
3446
|
"..",
|
|
3296
3447
|
"package.json"
|
|
@@ -3303,34 +3454,34 @@ function frameworkMeta() {
|
|
|
3303
3454
|
var LOCKFILES = ["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb"];
|
|
3304
3455
|
function readEntry(clientAbsDir) {
|
|
3305
3456
|
for (const name of ["toil.tsx", "toil.jsx", "main.tsx", "main.jsx"]) {
|
|
3306
|
-
const source = readFile(
|
|
3457
|
+
const source = readFile(path6.join(clientAbsDir, name));
|
|
3307
3458
|
if (source !== null && /toiljs\/routes|\bmount\s*\(/.test(source)) return source;
|
|
3308
3459
|
}
|
|
3309
3460
|
return null;
|
|
3310
3461
|
}
|
|
3311
3462
|
function collectSources(root, dir, cap) {
|
|
3312
|
-
const
|
|
3463
|
+
const out2 = [];
|
|
3313
3464
|
const visit = (current) => {
|
|
3314
|
-
if (
|
|
3465
|
+
if (out2.length >= cap) return;
|
|
3315
3466
|
let entries;
|
|
3316
3467
|
try {
|
|
3317
|
-
entries =
|
|
3468
|
+
entries = fs5.readdirSync(current, { withFileTypes: true });
|
|
3318
3469
|
} catch {
|
|
3319
3470
|
return;
|
|
3320
3471
|
}
|
|
3321
3472
|
for (const entry of entries) {
|
|
3322
|
-
if (
|
|
3323
|
-
const full =
|
|
3473
|
+
if (out2.length >= cap) break;
|
|
3474
|
+
const full = path6.join(current, entry.name);
|
|
3324
3475
|
if (entry.isDirectory()) {
|
|
3325
3476
|
if (entry.name !== "node_modules") visit(full);
|
|
3326
3477
|
} else if (/\.(tsx|jsx)$/.test(entry.name)) {
|
|
3327
3478
|
const source = readFile(full);
|
|
3328
|
-
if (source !== null)
|
|
3479
|
+
if (source !== null) out2.push({ path: path6.relative(root, full), source });
|
|
3329
3480
|
}
|
|
3330
3481
|
}
|
|
3331
3482
|
};
|
|
3332
3483
|
visit(dir);
|
|
3333
|
-
return
|
|
3484
|
+
return out2;
|
|
3334
3485
|
}
|
|
3335
3486
|
function glyph(status) {
|
|
3336
3487
|
if (status === "pass") return success("\u2713");
|
|
@@ -3339,31 +3490,31 @@ function glyph(status) {
|
|
|
3339
3490
|
}
|
|
3340
3491
|
function renderHuman(groups) {
|
|
3341
3492
|
const summary = summarize(groups);
|
|
3342
|
-
const
|
|
3493
|
+
const out2 = [];
|
|
3343
3494
|
for (const group of groups) {
|
|
3344
|
-
|
|
3495
|
+
out2.push(" " + bold(group.title));
|
|
3345
3496
|
for (const check of group.checks) {
|
|
3346
3497
|
let line = ` ${glyph(check.status)} ${check.label}`;
|
|
3347
3498
|
if (check.detail) line += dim(` ${check.detail}`);
|
|
3348
|
-
|
|
3499
|
+
out2.push(line);
|
|
3349
3500
|
if (check.fix && check.status !== "pass")
|
|
3350
|
-
|
|
3501
|
+
out2.push(" " + dim(`fix: ${check.fix}`));
|
|
3351
3502
|
}
|
|
3352
|
-
|
|
3503
|
+
out2.push("");
|
|
3353
3504
|
}
|
|
3354
3505
|
const parts = [success(`${String(summary.pass)} passed`)];
|
|
3355
3506
|
if (summary.warn > 0) {
|
|
3356
3507
|
parts.push(warn(`${String(summary.warn)} warning${summary.warn === 1 ? "" : "s"}`));
|
|
3357
3508
|
}
|
|
3358
3509
|
if (summary.fail > 0) parts.push(danger(`${String(summary.fail)} failed`));
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
process.stdout.write(
|
|
3510
|
+
out2.push(" " + parts.join(dim(", ")));
|
|
3511
|
+
out2.push("");
|
|
3512
|
+
process.stdout.write(out2.join("\n") + "\n");
|
|
3362
3513
|
}
|
|
3363
3514
|
async function runDoctor(opts) {
|
|
3364
|
-
const root =
|
|
3515
|
+
const root = path6.resolve(opts.root ?? opts.cwd);
|
|
3365
3516
|
const meta = frameworkMeta();
|
|
3366
|
-
const projectPkg = readJsonObject(
|
|
3517
|
+
const projectPkg = readJsonObject(path6.join(root, "package.json"));
|
|
3367
3518
|
const deps = {
|
|
3368
3519
|
...projectPkg ? stringRecord(projectPkg.dependencies) : {},
|
|
3369
3520
|
...projectPkg ? stringRecord(projectPkg.devDependencies) : {}
|
|
@@ -3375,11 +3526,11 @@ async function runDoctor(opts) {
|
|
|
3375
3526
|
} catch (err) {
|
|
3376
3527
|
configError = err instanceof Error ? err.message : String(err);
|
|
3377
3528
|
}
|
|
3378
|
-
const clientAbsDir = cfg ? cfg.clientAbsDir :
|
|
3379
|
-
const routesAbsDir = cfg ? cfg.routesAbsDir :
|
|
3380
|
-
const publicDir = cfg ? cfg.publicDir :
|
|
3529
|
+
const clientAbsDir = cfg ? cfg.clientAbsDir : path6.join(root, "client");
|
|
3530
|
+
const routesAbsDir = cfg ? cfg.routesAbsDir : path6.join(clientAbsDir, "routes");
|
|
3531
|
+
const publicDir = cfg ? cfg.publicDir : path6.join(clientAbsDir, "public");
|
|
3381
3532
|
const entrySource = readEntry(clientAbsDir);
|
|
3382
|
-
const indexHtml = readFile(
|
|
3533
|
+
const indexHtml = readFile(path6.join(publicDir, "index.html"));
|
|
3383
3534
|
const routes = scanRoutes(routesAbsDir);
|
|
3384
3535
|
const mainPatterns = routes.filter((r2) => r2.slot === void 0).map((r2) => r2.pattern);
|
|
3385
3536
|
const assetIssues = findRelativeAssets(collectSources(root, clientAbsDir, 200));
|
|
@@ -3392,14 +3543,14 @@ async function runDoctor(opts) {
|
|
|
3392
3543
|
}
|
|
3393
3544
|
const ppPkg = preprocessorImported ? PREPROCESSOR_PKG[preprocessorImported] : null;
|
|
3394
3545
|
const preprocessorInstalled = ppPkg === null || ppPkg in deps;
|
|
3395
|
-
const toilconfig = readJsonObject(
|
|
3546
|
+
const toilconfig = readJsonObject(path6.join(root, "toilconfig.json"));
|
|
3396
3547
|
const serverPresent = toilconfig !== null;
|
|
3397
3548
|
let missingEntries = [];
|
|
3398
3549
|
let toilscriptInstalled = false;
|
|
3399
3550
|
let wasmExists = false;
|
|
3400
3551
|
if (toilconfig) {
|
|
3401
3552
|
const entries = Array.isArray(toilconfig.entries) ? toilconfig.entries.filter((e) => typeof e === "string") : [];
|
|
3402
|
-
missingEntries = entries.filter((e) => !
|
|
3553
|
+
missingEntries = entries.filter((e) => !fs5.existsSync(path6.join(root, e)));
|
|
3403
3554
|
toilscriptInstalled = isPackageInstalled(root, "toilscript");
|
|
3404
3555
|
const targets = typeof toilconfig.targets === "object" && toilconfig.targets !== null ? toilconfig.targets : {};
|
|
3405
3556
|
const outFiles = [];
|
|
@@ -3409,10 +3560,10 @@ async function runDoctor(opts) {
|
|
|
3409
3560
|
if (typeof outFile === "string") outFiles.push(outFile);
|
|
3410
3561
|
}
|
|
3411
3562
|
}
|
|
3412
|
-
wasmExists = outFiles.some((f) =>
|
|
3563
|
+
wasmExists = outFiles.some((f) => fs5.existsSync(path6.join(root, f)));
|
|
3413
3564
|
if (!wasmExists && outFiles.length === 0) {
|
|
3414
3565
|
try {
|
|
3415
|
-
wasmExists =
|
|
3566
|
+
wasmExists = fs5.readdirSync(path6.join(root, "build", "server")).some((f) => f.endsWith(".wasm"));
|
|
3416
3567
|
} catch {
|
|
3417
3568
|
wasmExists = false;
|
|
3418
3569
|
}
|
|
@@ -3428,7 +3579,7 @@ async function runDoctor(opts) {
|
|
|
3428
3579
|
const authFacts = gatherAuthFacts(root, toilconfig);
|
|
3429
3580
|
const prettierPresent = prettierPluginPresent(
|
|
3430
3581
|
root,
|
|
3431
|
-
readJsonObject(
|
|
3582
|
+
readJsonObject(path6.join(root, "package.json"))
|
|
3432
3583
|
);
|
|
3433
3584
|
const serverTsPath = serverPresent ? serverTsconfigPath(root, toilconfig) : null;
|
|
3434
3585
|
const serverTsParsed = serverTsPath ? readJsonObject(serverTsPath) : null;
|
|
@@ -3452,7 +3603,7 @@ async function runDoctor(opts) {
|
|
|
3452
3603
|
checkNode(process.versions.node, meta.node),
|
|
3453
3604
|
checkToiljsInstalled("toiljs" in deps ? version() : null),
|
|
3454
3605
|
...peerChecks,
|
|
3455
|
-
checkPackageManager(LOCKFILES.filter((f) =>
|
|
3606
|
+
checkPackageManager(LOCKFILES.filter((f) => fs5.existsSync(path6.join(root, f))))
|
|
3456
3607
|
]
|
|
3457
3608
|
},
|
|
3458
3609
|
{
|
|
@@ -3461,13 +3612,13 @@ async function runDoctor(opts) {
|
|
|
3461
3612
|
checkDir(
|
|
3462
3613
|
"client-dir",
|
|
3463
3614
|
"client/ directory",
|
|
3464
|
-
|
|
3615
|
+
fs5.existsSync(clientAbsDir),
|
|
3465
3616
|
"Create a client/ directory for your app."
|
|
3466
3617
|
),
|
|
3467
3618
|
checkDir(
|
|
3468
3619
|
"routes-dir",
|
|
3469
3620
|
"routes/ directory",
|
|
3470
|
-
|
|
3621
|
+
fs5.existsSync(routesAbsDir),
|
|
3471
3622
|
"Create client/routes/ and add an index.tsx."
|
|
3472
3623
|
),
|
|
3473
3624
|
checkRootElement(indexHtml),
|
|
@@ -3502,7 +3653,8 @@ async function runDoctor(opts) {
|
|
|
3502
3653
|
checkRpcWiring(rpcFacts),
|
|
3503
3654
|
checkRestDispatch(restFacts),
|
|
3504
3655
|
checkPrettierPlugin(prettierPresent),
|
|
3505
|
-
checkServerTsPlugin(serverTsPluginPresent)
|
|
3656
|
+
checkServerTsPlugin(serverTsPluginPresent),
|
|
3657
|
+
checkMigrationsDir(serverMigrationsExist(root, toilconfig))
|
|
3506
3658
|
] : [checkToilconfig(false)]
|
|
3507
3659
|
}
|
|
3508
3660
|
];
|
|
@@ -3525,30 +3677,30 @@ async function runDoctor(opts) {
|
|
|
3525
3677
|
if (hasFailures(summary)) process.exitCode = 1;
|
|
3526
3678
|
}
|
|
3527
3679
|
function renderRpcFix(result) {
|
|
3528
|
-
const
|
|
3680
|
+
const out2 = [];
|
|
3529
3681
|
if (result.changed.length > 0) {
|
|
3530
|
-
|
|
3682
|
+
out2.push(" " + success("fixed server wiring") + dim(` ${result.changed.join(", ")}`));
|
|
3531
3683
|
if (result.changed.includes("package.json")) {
|
|
3532
|
-
|
|
3684
|
+
out2.push(
|
|
3533
3685
|
" " + dim("run your installer (npm/pnpm/yarn) if the toilscript version changed.")
|
|
3534
3686
|
);
|
|
3535
3687
|
}
|
|
3536
3688
|
} else {
|
|
3537
|
-
|
|
3689
|
+
out2.push(" " + dim("server wiring already in place, nothing to fix."));
|
|
3538
3690
|
}
|
|
3539
|
-
for (const item of result.skipped)
|
|
3540
|
-
process.stdout.write(
|
|
3691
|
+
for (const item of result.skipped) out2.push(" " + warn("skipped") + dim(` ${item}`));
|
|
3692
|
+
process.stdout.write(out2.join("\n") + "\n\n");
|
|
3541
3693
|
}
|
|
3542
3694
|
|
|
3543
3695
|
// src/cli/notify.ts
|
|
3544
|
-
import
|
|
3696
|
+
import fs7 from "node:fs";
|
|
3545
3697
|
import os from "node:os";
|
|
3546
|
-
import
|
|
3698
|
+
import path8 from "node:path";
|
|
3547
3699
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
3548
3700
|
|
|
3549
3701
|
// src/cli/update.ts
|
|
3550
|
-
import
|
|
3551
|
-
import
|
|
3702
|
+
import fs6 from "node:fs";
|
|
3703
|
+
import path7 from "node:path";
|
|
3552
3704
|
|
|
3553
3705
|
// src/cli/updates.ts
|
|
3554
3706
|
function parseVersion2(v) {
|
|
@@ -3571,9 +3723,9 @@ function parseNcuJson(stdout2) {
|
|
|
3571
3723
|
try {
|
|
3572
3724
|
const parsed = JSON.parse(stdout2.slice(start2, end + 1));
|
|
3573
3725
|
if (typeof parsed !== "object" || parsed === null) return {};
|
|
3574
|
-
const
|
|
3575
|
-
for (const [k, v] of Object.entries(parsed)) if (typeof v === "string")
|
|
3576
|
-
return
|
|
3726
|
+
const out2 = {};
|
|
3727
|
+
for (const [k, v] of Object.entries(parsed)) if (typeof v === "string") out2[k] = v;
|
|
3728
|
+
return out2;
|
|
3577
3729
|
} catch {
|
|
3578
3730
|
return {};
|
|
3579
3731
|
}
|
|
@@ -3588,23 +3740,50 @@ function buildRows(upgraded, currentDeps) {
|
|
|
3588
3740
|
|
|
3589
3741
|
// src/cli/update.ts
|
|
3590
3742
|
function detectPackageManager2(root) {
|
|
3591
|
-
if (
|
|
3592
|
-
if (
|
|
3593
|
-
if (
|
|
3743
|
+
if (fs6.existsSync(path7.join(root, "pnpm-lock.yaml"))) return { name: "pnpm", ncuName: "pnpm" };
|
|
3744
|
+
if (fs6.existsSync(path7.join(root, "yarn.lock"))) return { name: "yarn", ncuName: "yarn" };
|
|
3745
|
+
if (fs6.existsSync(path7.join(root, "bun.lockb"))) return { name: "bun", ncuName: "bun" };
|
|
3594
3746
|
return { name: "npm", ncuName: "npm" };
|
|
3595
3747
|
}
|
|
3596
3748
|
function readDependencies(pkgPath) {
|
|
3597
|
-
const parsed = JSON.parse(
|
|
3749
|
+
const parsed = JSON.parse(fs6.readFileSync(pkgPath, "utf8"));
|
|
3598
3750
|
if (typeof parsed !== "object" || parsed === null) return {};
|
|
3599
3751
|
const pkg = parsed;
|
|
3600
3752
|
const merge = (v) => {
|
|
3601
3753
|
if (typeof v !== "object" || v === null) return {};
|
|
3602
|
-
const
|
|
3603
|
-
for (const [k, val] of Object.entries(v)) if (typeof val === "string")
|
|
3604
|
-
return
|
|
3754
|
+
const out2 = {};
|
|
3755
|
+
for (const [k, val] of Object.entries(v)) if (typeof val === "string") out2[k] = val;
|
|
3756
|
+
return out2;
|
|
3605
3757
|
};
|
|
3606
3758
|
return { ...merge(pkg.dependencies), ...merge(pkg.devDependencies) };
|
|
3607
3759
|
}
|
|
3760
|
+
function serverDirs(root) {
|
|
3761
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
3762
|
+
try {
|
|
3763
|
+
const parsed = JSON.parse(
|
|
3764
|
+
fs6.readFileSync(path7.join(root, "toilconfig.json"), "utf8")
|
|
3765
|
+
);
|
|
3766
|
+
const entries = typeof parsed === "object" && parsed !== null && Array.isArray(parsed.entries) ? parsed.entries.filter(
|
|
3767
|
+
(e) => typeof e === "string"
|
|
3768
|
+
) : [];
|
|
3769
|
+
for (const e of entries) dirs.add(path7.dirname(path7.resolve(root, e)));
|
|
3770
|
+
} catch {
|
|
3771
|
+
}
|
|
3772
|
+
if (dirs.size === 0) dirs.add(path7.join(root, "server"));
|
|
3773
|
+
return [...dirs];
|
|
3774
|
+
}
|
|
3775
|
+
function ensureMigrationsDirs(root) {
|
|
3776
|
+
const created = [];
|
|
3777
|
+
for (const dir of serverDirs(root)) {
|
|
3778
|
+
if (!fs6.existsSync(dir)) continue;
|
|
3779
|
+
const migrations = path7.join(dir, "migrations");
|
|
3780
|
+
if (fs6.existsSync(migrations)) continue;
|
|
3781
|
+
fs6.mkdirSync(migrations, { recursive: true });
|
|
3782
|
+
fs6.writeFileSync(path7.join(migrations, "README.md"), MIGRATIONS_README);
|
|
3783
|
+
created.push(path7.relative(root, migrations) || "migrations");
|
|
3784
|
+
}
|
|
3785
|
+
return created;
|
|
3786
|
+
}
|
|
3608
3787
|
function bumpColor(bump, text2) {
|
|
3609
3788
|
if (bump === "major") return danger(text2);
|
|
3610
3789
|
if (bump === "minor") return warn(text2);
|
|
@@ -3616,9 +3795,9 @@ function rowLine(row) {
|
|
|
3616
3795
|
}
|
|
3617
3796
|
var TARGETS = /* @__PURE__ */ new Set(["latest", "minor", "patch", "newest", "greatest"]);
|
|
3618
3797
|
async function runUpdate(opts) {
|
|
3619
|
-
const root =
|
|
3620
|
-
const pkgPath =
|
|
3621
|
-
if (!
|
|
3798
|
+
const root = path7.resolve(opts.root ?? opts.cwd);
|
|
3799
|
+
const pkgPath = path7.join(root, "package.json");
|
|
3800
|
+
if (!fs6.existsSync(pkgPath)) {
|
|
3622
3801
|
throw new Error("No package.json here. Run from your project root or pass --root <dir>.");
|
|
3623
3802
|
}
|
|
3624
3803
|
const currentDeps = readDependencies(pkgPath);
|
|
@@ -3634,6 +3813,13 @@ async function runUpdate(opts) {
|
|
|
3634
3813
|
...extra
|
|
3635
3814
|
];
|
|
3636
3815
|
intro(accent("toiljs update"));
|
|
3816
|
+
const createdMigrations = ensureMigrationsDirs(root);
|
|
3817
|
+
if (createdMigrations.length > 0) {
|
|
3818
|
+
note(
|
|
3819
|
+
createdMigrations.map((p) => `${dim("+")} ${p}/`).join("\n"),
|
|
3820
|
+
"Created ToilDB migrations folder"
|
|
3821
|
+
);
|
|
3822
|
+
}
|
|
3637
3823
|
const s = spinner();
|
|
3638
3824
|
s.start("Checking the registry for updates");
|
|
3639
3825
|
const res = await capture("npx", ncuArgs(["--jsonUpgraded"]), root);
|
|
@@ -3773,8 +3959,8 @@ function findOutdated(latest, projectVersion, cliVersion, cliIsLocal, pm) {
|
|
|
3773
3959
|
var REGISTRY_URL = "https://registry.npmjs.org/toiljs/latest";
|
|
3774
3960
|
var FETCH_TIMEOUT_MS = 2e3;
|
|
3775
3961
|
function cacheFile() {
|
|
3776
|
-
const base = process.env.XDG_CACHE_HOME ??
|
|
3777
|
-
return
|
|
3962
|
+
const base = process.env.XDG_CACHE_HOME ?? path8.join(os.homedir(), ".cache");
|
|
3963
|
+
return path8.join(base, "toiljs", "update-check.json");
|
|
3778
3964
|
}
|
|
3779
3965
|
async function fetchLatest() {
|
|
3780
3966
|
const ctrl = new AbortController();
|
|
@@ -3800,22 +3986,22 @@ async function fetchLatest() {
|
|
|
3800
3986
|
async function resolveLatest() {
|
|
3801
3987
|
const file = cacheFile();
|
|
3802
3988
|
try {
|
|
3803
|
-
const cached = parseCheckCache(
|
|
3989
|
+
const cached = parseCheckCache(fs7.readFileSync(file, "utf8"));
|
|
3804
3990
|
if (cached && isCacheFresh(cached, Date.now())) return cached.latest;
|
|
3805
3991
|
} catch {
|
|
3806
3992
|
}
|
|
3807
3993
|
const latest = await fetchLatest();
|
|
3808
3994
|
try {
|
|
3809
|
-
|
|
3810
|
-
|
|
3995
|
+
fs7.mkdirSync(path8.dirname(file), { recursive: true });
|
|
3996
|
+
fs7.writeFileSync(file, JSON.stringify({ latest, checkedAt: Date.now() }));
|
|
3811
3997
|
} catch {
|
|
3812
3998
|
}
|
|
3813
3999
|
return latest;
|
|
3814
4000
|
}
|
|
3815
4001
|
function projectToiljsVersion(root) {
|
|
3816
4002
|
try {
|
|
3817
|
-
const raw =
|
|
3818
|
-
|
|
4003
|
+
const raw = fs7.readFileSync(
|
|
4004
|
+
path8.join(root, "node_modules", "toiljs", "package.json"),
|
|
3819
4005
|
"utf8"
|
|
3820
4006
|
);
|
|
3821
4007
|
const parsed = JSON.parse(raw);
|
|
@@ -3840,9 +4026,9 @@ async function notifyIfOutdated(rootArg) {
|
|
|
3840
4026
|
if (env.TOILJS_NO_UPDATE_CHECK || env.NO_UPDATE_NOTIFIER || env.CI) return;
|
|
3841
4027
|
const latest = await resolveLatest();
|
|
3842
4028
|
if (!latest) return;
|
|
3843
|
-
const root =
|
|
3844
|
-
const cliDir =
|
|
3845
|
-
const cliIsLocal = cliDir.startsWith(
|
|
4029
|
+
const root = path8.resolve(rootArg ?? process.cwd());
|
|
4030
|
+
const cliDir = path8.dirname(fileURLToPath4(import.meta.url));
|
|
4031
|
+
const cliIsLocal = cliDir.startsWith(path8.join(root, "node_modules") + path8.sep);
|
|
3846
4032
|
const rows = findOutdated(
|
|
3847
4033
|
latest,
|
|
3848
4034
|
projectToiljsVersion(root),
|
|
@@ -3954,6 +4140,7 @@ function printHelp() {
|
|
|
3954
4140
|
cmd("start", "self-host the built app (hyper-express / uWS)"),
|
|
3955
4141
|
cmd("doctor", "diagnose project setup and dependencies"),
|
|
3956
4142
|
cmd("update", "check for and apply dependency updates"),
|
|
4143
|
+
cmd("db <action>", "manage dev DB data (status | reset | export | import)"),
|
|
3957
4144
|
"",
|
|
3958
4145
|
bold("Options"),
|
|
3959
4146
|
cmd("--root <dir>", "project root (default: current directory)"),
|
|
@@ -4054,6 +4241,12 @@ async function main() {
|
|
|
4054
4241
|
target: flags.target
|
|
4055
4242
|
});
|
|
4056
4243
|
break;
|
|
4244
|
+
case "db": {
|
|
4245
|
+
const action = rest[0];
|
|
4246
|
+
const fileArg = rest[1] !== void 0 && !rest[1].startsWith("-") ? rest[1] : void 0;
|
|
4247
|
+
runDb(action, fileArg, { root: flags.root });
|
|
4248
|
+
break;
|
|
4249
|
+
}
|
|
4057
4250
|
case "help":
|
|
4058
4251
|
case "--help":
|
|
4059
4252
|
case "-h":
|