@wenathlan/saddle 2.0.166 → 2.0.168

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.
@@ -1,3 +1,65 @@
1
+ // # scripts.ts — the one keyword file of the scripts engine domain: the
2
+ // consolidation law of the root (one keyword file per domain) merged the
3
+ // four script seats of the saddle engine into this seat — the script
4
+ // lanes (the former scriptlanes.ts body below: the sh execution through
5
+ // the universal runner, the yml and yaml interpretation through the
6
+ // house yaml subset, the babel compile roundtrip, the detached
7
+ // background runs on the house job queue and the docker-ready
8
+ // descriptors for the container lane), the tsc build engine of the
9
+ // distpackage tree (scriptsbuildengine.ts), the cargo audit security
10
+ // gate (scriptssecuritygate.ts) and the flat native surface validation
11
+ // (scriptsvalidateflatnative.ts) — every export preserved verbatim, zero
12
+ // logic changes.
13
+ // The consolidation notes: no declaration collided across the four
14
+ // sources (the three executable seats keep their top-level bindings
15
+ // block-scoped inside their lane guards), so no group suffix rides this
16
+ // seat; the executable seats answer the dispatcher law the devthink root
17
+ // keyword file set — the lane word on argv[2] names the script the run
18
+ // answers (node scripts.ts buildengine / node scripts.ts securitygate
19
+ // <cargo-audit-report> / node scripts.ts validateflatnative — the
20
+ // package manifest lanes build:engine and native:check:flat walk the
21
+ // first and the last) and the dispatcher retires the lane word from argv
22
+ // before the section runs, so every script keeps the argv contract of
23
+ // its standalone era; the engine import graph (index.ts, cli.ts,
24
+ // webserver.ts and the suite) imports the script lane surface this file
25
+ // exports without ever tripping the dispatch — only a direct
26
+ // `node scripts.ts` entry answers the lanes.
27
+
28
+ import { execFileSync, spawn } from "node:child_process";
29
+ import { existsSync } from "node:fs";
30
+ import { access, mkdir, readFile, rm, writeFile } from "node:fs/promises";
31
+ import { createRequire } from "node:module";
32
+ import { join } from "node:path";
33
+ import { fileURLToPath } from "node:url";
34
+ import { runInNewContext } from "node:vm";
35
+ import { jobqueue, runrecord, runstatuses, transitionrun } from "./execution.ts";
36
+ import { aserror, idfactory, saddleerror, validationerror } from "./foundation.ts";
37
+
38
+ /** The lane word every merged script answers: argv[2] names the lane and
39
+ * the dispatcher retires the word before the section runs, so each
40
+ * script keeps the argv contract of its standalone era (the report path
41
+ * of the security gate stays argv[2] exactly the way the former file
42
+ * read it). the entry check keeps the dispatch honest when this keyword
43
+ * file rides the engine import graph — only a direct `node scripts.ts`
44
+ * run answers the lanes, an import from index.ts, cli.ts, webserver.ts
45
+ * or the suite never trips the guard. */
46
+ const scriptslanes = new Set(["buildengine", "securitygate", "validateflatnative"]);
47
+ const scriptsentry = process.argv[1]?.endsWith("scripts.ts") || process.argv[1]?.endsWith("scripts");
48
+ const scriptslane = scriptsentry ? process.argv[2] : undefined;
49
+ if (scriptsentry) {
50
+ if (scriptslanes.has(scriptslane ?? "")) process.argv.splice(2, 1);
51
+ else {
52
+ console.error(`usage: node scripts.ts <lane> [lane arguments] — lanes: ${[...scriptslanes].join(", ")}`);
53
+ process.exit(1);
54
+ }
55
+ }
56
+
57
+ /* ════════════════════════════════════════════════════════════════════ */
58
+ /* SECTION: scriptlanes.ts — the script lanes of the saddle engine */
59
+ /* (the module surface the import graph consumes: the six sections */
60
+ /* below ride the former file verbatim). */
61
+ /* ════════════════════════════════════════════════════════════════════ */
62
+
1
63
  /**
2
64
  * scriptlanes.ts — the script lanes of the saddle engine: sh execution
3
65
  * through the universal runner, yml and yaml workflow descriptors
@@ -30,11 +92,6 @@
30
92
  * the child process.
31
93
  */
32
94
 
33
- import { spawn } from "node:child_process";
34
- import { runInNewContext } from "node:vm";
35
- import { jobqueue, runrecord, runstatuses, transitionrun } from "./execution.ts";
36
- import { aserror, idfactory, saddleerror, validationerror } from "./foundation.ts";
37
-
38
95
  /* ════════════════════════════════════════════════════════════════════ */
39
96
  /* Section 1: the server context law and the plan nouns — the lanes never touch the visitor machine. */
40
97
  /* ════════════════════════════════════════════════════════════════════ */
@@ -772,3 +829,224 @@ export function dockerdescriptor(plan: ScriptPlan): { dockerfile: string; entryp
772
829
  ].join("\n");
773
830
  return { dockerfile, entrypoint: "bash /app/plan.sh" };
774
831
  }
832
+
833
+ /* ════════════════════════════════════════════════════════════════════ */
834
+ /* SECTION: scriptsbuildengine.ts — the tsc build engine of the */
835
+ /* distpackage tree (the executable seat the lane word buildengine */
836
+ /* answers: node scripts.ts buildengine — the package manifest */
837
+ /* build:engine lane; the body rides the former file verbatim inside */
838
+ /* the lane guard, argv untouched after the dispatcher retired the */
839
+ /* lane word). */
840
+ /* ════════════════════════════════════════════════════════════════════ */
841
+ if (scriptslane === "buildengine") {
842
+ await rm("distpackage", { recursive: true, force: true });
843
+ /* the compiler rides node directly: windows refuses spawning a bare
844
+ * tsc.cmd shell (spawnSync EINVAL on the .cmd shim) and the house law keeps
845
+ * the toolchain in the global node modules, so the tsc entry is resolved
846
+ * through require paths instead of a shell command */
847
+ function compilerentry() {
848
+ const require = createRequire(import.meta.url);
849
+ for (const base of ["typescript/bin/tsc", "../typescript/bin/tsc", "../../../typescript/bin/tsc"]) {
850
+ try {
851
+ const entry = require.resolve(base);
852
+ if (existsSync(entry)) return entry;
853
+ } catch {
854
+ /* not here: walk the next candidate */
855
+ }
856
+ }
857
+ return "tsc";
858
+ }
859
+ const entry = compilerentry();
860
+ const args = entry.endsWith(".js") ? [entry, "--project", "tsconfig.json"] : ["--project", "tsconfig.json"];
861
+ execFileSync(entry.endsWith(".js") ? process.execPath : entry, args, {
862
+ stdio: "inherit",
863
+ env: { ...process.env, NODE_PATH: [process.env.NODE_PATH, "C:/Users/nathalan/node_modules"].filter(Boolean).join(";") },
864
+ });
865
+
866
+ // the target manifest CLI: the grand merge folded packager/targetcli.ts
867
+ // into distribution.ts (Section 7) whose CLI main only activates when
868
+ // argv[1] ends with targetcli.js - the historical entry point that the
869
+ // targets workflow invokes. The build regenerates the thin wrapper so
870
+ // the contract distpackage/packager/targetcli.js keeps working (nothing lost).
871
+ await mkdir("distpackage/packager", { recursive: true });
872
+ await writeFile(
873
+ "distpackage/packager/targetcli.js",
874
+ '// generated by scriptsbuildengine.ts - the thin CLI wrapper that\n// activates the target manifest CLI absorbed into distribution.ts\n// (Section 7); its CLI main runs when argv[1] ends with targetcli.js (the wrapper resolves the root source — node 26 strips the types).\nimport "../../distribution.ts";\n',
875
+ );
876
+ }
877
+
878
+ /* ════════════════════════════════════════════════════════════════════ */
879
+ /* SECTION: scriptssecuritygate.ts — the cargo audit security gate */
880
+ /* (the executable seat the lane word securitygate answers: */
881
+ /* node scripts.ts securitygate <cargo-audit-report-path> — the report */
882
+ /* path stays argv[2] after the dispatcher retired the lane word, and */
883
+ /* the SECURITY_SUMMARY_PATH override, the glib acceptance and the */
884
+ /* exit code ride the former file verbatim). */
885
+ /* ════════════════════════════════════════════════════════════════════ */
886
+ if (scriptslane === "securitygate") {
887
+ const reportPath = process.argv[2];
888
+ const summaryPath =
889
+ process.env.SECURITY_SUMMARY_PATH ?? "build/security/cargo-audit-summary.md";
890
+ const acceptedAdvisory = "RUSTSEC-2024-0429";
891
+
892
+ if (!reportPath) {
893
+ throw new TypeError("cargo audit report path is required");
894
+ }
895
+
896
+ /** Returns the advisory identifiers attached to one cargo audit finding. */
897
+ function advisoryids(finding) {
898
+ const advisory = finding?.advisory ?? {};
899
+ return [advisory.id, finding?.id, ...(advisory.aliases ?? [])]
900
+ .filter(Boolean)
901
+ .map(String);
902
+ }
903
+
904
+ /** Returns a stable package label for a cargo audit finding. */
905
+ function packagelabel(finding) {
906
+ const packageinfo = finding?.package ?? {};
907
+ return `${packageinfo.name ?? "unknown"}@${packageinfo.version ?? "unknown"}`;
908
+ }
909
+
910
+ const raw = await readFile(reportPath, "utf8");
911
+ let report;
912
+ try {
913
+ report = JSON.parse(raw);
914
+ } catch (error) {
915
+ throw new Error(`cargo audit produced invalid JSON: ${error.message}`);
916
+ }
917
+
918
+ const findings = Array.isArray(report?.vulnerabilities?.list)
919
+ ? report.vulnerabilities.list
920
+ : [];
921
+ const accepted = findings.filter(
922
+ (finding) =>
923
+ advisoryids(finding).includes(acceptedAdvisory) &&
924
+ packagelabel(finding).startsWith("glib@"),
925
+ );
926
+ const unexpected = findings.filter((finding) => !accepted.includes(finding));
927
+ const warnings = Object.values(report?.warnings ?? {}).flatMap((value) =>
928
+ Array.isArray(value) ? value : [],
929
+ );
930
+ const status =
931
+ unexpected.length > 0
932
+ ? "fail"
933
+ : accepted.length > 0
934
+ ? "accepted-risk"
935
+ : "pass";
936
+ const lines = [
937
+ "# Cargo audit security gate",
938
+ "",
939
+ `Status: **${status}**`,
940
+ `Findings: ${findings.length}`,
941
+ `Accepted transitive glib findings: ${accepted.length}`,
942
+ `Unexpected findings: ${unexpected.length}`,
943
+ `Warnings: ${warnings.length}`,
944
+ "",
945
+ "## Findings",
946
+ "",
947
+ ];
948
+
949
+ if (findings.length === 0)
950
+ lines.push("No RustSec vulnerability findings were returned.", "");
951
+ for (const finding of findings) {
952
+ const ids = advisoryids(finding).join(", ") || "unknown-advisory";
953
+ const classification = accepted.includes(finding)
954
+ ? "accepted transitive glib risk"
955
+ : "release blocking";
956
+ lines.push(`- ${classification}: ${ids} (${packagelabel(finding)})`);
957
+ }
958
+
959
+ lines.push(
960
+ "",
961
+ "The accepted glib entry is limited to the Tauri GTK3 transitive dependency documented in `docs/tauri-glib-risk-1.8.11.md`; any other advisory fails this gate.",
962
+ "",
963
+ );
964
+ await mkdir(new URL(".", `file://${process.cwd()}/${summaryPath}`).pathname, {
965
+ recursive: true,
966
+ }).catch(() => {});
967
+ await writeFile(summaryPath, `${lines.join("\n")}\n`);
968
+ console.log(lines.join("\n"));
969
+ if (unexpected.length > 0) process.exitCode = 1;
970
+ }
971
+
972
+ /* ════════════════════════════════════════════════════════════════════ */
973
+ /* SECTION: scriptsvalidateflatnative.ts — the flat native surface */
974
+ /* validation (the executable seat the lane word validateflatnative */
975
+ /* answers: node scripts.ts validateflatnative — the package manifest */
976
+ /* native:check:flat lane; import.meta.url resolves to this saddle */
977
+ /* seat in the very directory the former file lived in, so the */
978
+ /* repositoryRoot walk rides the former file verbatim). */
979
+ /* ════════════════════════════════════════════════════════════════════ */
980
+ if (scriptslane === "validateflatnative") {
981
+ /** Returns whether a path exists without turning a missing generated path into a failure. */
982
+ async function exists(path) {
983
+ try {
984
+ await access(path);
985
+ return true;
986
+ } catch {
987
+ return false;
988
+ }
989
+ }
990
+
991
+ /**
992
+ * Verifies the 2.1.0 tree contract: the native wrappers (android, ios,
993
+ * desktop, extension) are generated on the runners into build/native/*
994
+ * and never tracked, the interface is one flat tsx tree (page folders +
995
+ * loose modules, no nested support folders, no legacy window-global
996
+ * localauth.ts), and the static e2ugh console pages stay absorbed in the
997
+ * tsx pages. the tree never goes back to tracked wrappers.
998
+ */
999
+ async function validateFlatNative() {
1000
+ const repositoryRoot = fileURLToPath(new URL(".", import.meta.url));
1001
+ const webRoot = join(repositoryRoot, "web");
1002
+ const forbiddenDirectories = [
1003
+ "android",
1004
+ "ios",
1005
+ "desktop",
1006
+ "extension",
1007
+ "pages",
1008
+ "components",
1009
+ "hooks",
1010
+ "lib",
1011
+ "contexts",
1012
+ ];
1013
+ const forbiddenFiles = [
1014
+ "const.ts",
1015
+ "localauth.js",
1016
+ "login.html",
1017
+ "register.html",
1018
+ "console.html",
1019
+ "dashboard.html",
1020
+ "login.js",
1021
+ "register.js",
1022
+ "console.js",
1023
+ "dashboard.js",
1024
+ ];
1025
+
1026
+ for (const directory of forbiddenDirectories) {
1027
+ const path = join(webRoot, directory);
1028
+ if (await exists(path)) {
1029
+ throw new Error(`Forbidden native wrapper directory exists: ${path}`);
1030
+ }
1031
+ }
1032
+ for (const file of forbiddenFiles) {
1033
+ const path = join(webRoot, file);
1034
+ if (await exists(path)) {
1035
+ throw new Error(`Forbidden flat-interface file exists: ${path}`);
1036
+ }
1037
+ }
1038
+
1039
+ const canonicalTodo = join(repositoryRoot, "docs", "todo.md");
1040
+ if (!(await exists(canonicalTodo))) {
1041
+ throw new Error(`Canonical operational checklist is missing: ${canonicalTodo}`);
1042
+ }
1043
+ }
1044
+
1045
+ try {
1046
+ await validateFlatNative();
1047
+ console.log("Flat native surface validation passed.");
1048
+ } catch (error) {
1049
+ console.error("Flat native surface validation failed.", error);
1050
+ process.exitCode = 1;
1051
+ }
1052
+ }
package/tiers.ts CHANGED
@@ -35,9 +35,10 @@
35
35
  */
36
36
 
37
37
  import { createHash } from 'node:crypto';
38
+ import { createRequire } from 'node:module';
38
39
  import { tmpdir } from 'node:os';
39
40
  import { join } from 'node:path';
40
- import { DatabaseSync, type StatementSync } from 'node:sqlite';
41
+ import type { DatabaseSync, StatementSync } from 'node:sqlite';
41
42
  import { deflateRawSync, gunzipSync, gzipSync, inflateRawSync } from 'node:zlib';
42
43
 
43
44
  /* ------------------------------------------------------------------ */
@@ -350,7 +351,14 @@ export class sqlitekv implements Disposable {
350
351
  constructor(path?: string) {
351
352
  this.path = path ?? process.env.SADDLE_TIERS_DB ?? join(tmpdir(), 'saddle-tiers.db');
352
353
  this.#db = guard('sqlitekv', `sqlitekv open ${this.path}`, () => {
353
- const db = new DatabaseSync(this.path);
354
+ // the lazy driver law (the vaulthandoff pattern): the native
355
+ // node:sqlite module loads at open time alone through createRequire
356
+ // so a runtime without the builtin (the bun rungs before 1.4.4, the
357
+ // node rungs before 22.5) answers the honest guarded refusal here
358
+ // instead of crashing the whole module graph at import time.
359
+ const require = createRequire(import.meta.url);
360
+ const driversqlite = require('node:sqlite') as typeof import('node:sqlite');
361
+ const db = new driversqlite.DatabaseSync(this.path);
354
362
  db.exec(
355
363
  'PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; ' +
356
364
  'PRAGMA cache_size = 10000; PRAGMA temp_store = MEMORY;',
package/tsconfig.json CHANGED
@@ -29,5 +29,5 @@
29
29
  }
30
30
  },
31
31
  "include": ["*.ts", "*.tsx", "sol/**/*.ts", "sol/**/*.tsx", "docs/example-*.ts"],
32
- "exclude": ["node_modules", "dist", "**/*.test.ts", "coverage", "web", "tests", "build"]
32
+ "exclude": ["node_modules", "dist", "**/*.test.ts", "coverage", "web", "test", "build"]
33
33
  }