@githolon/dsl 0.112.4 → 0.112.6

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.
@@ -36,12 +36,14 @@ export function writePlanCompileProof(options: {
36
36
  plan: string;
37
37
  planSha256: string;
38
38
  inputs: readonly { readonly path: string; readonly sha256: string }[];
39
+ sourceRoot?: string;
39
40
  }): void;
40
41
  export function readPlanCompileProof(options: {
41
42
  proofPath: string;
42
43
  baseIdentity: string;
43
44
  runtimeSha256: string;
44
45
  digestFile?: (file: string) => string;
46
+ sourceRoot?: string;
45
47
  }): { javascript: string; sha256: string } | undefined;
46
48
  export function writeKeyProofUsda(file: string, format: string, key: string): void;
47
49
  export function readKeyProofUsda(file: string, format: string): string | undefined;
package/compile_proof.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // A content-addressed compiler proof expressed as USDA, not a parallel JSON manifest.
2
- // It is disposable build cache metadata under node_modules/.cache: law and custody
2
+ // It is disposable build cache metadata under .nomos-cache: law and custody
3
3
  // remain the emitted typed USDA closure.
4
4
  import { createHash } from "node:crypto";
5
5
  import {
@@ -123,7 +123,7 @@ export function compileInputs({ cwd, dslDir, configPath, argumentIdentity }) {
123
123
 
124
124
  export function compileProofPath(cwd, configPath) {
125
125
  const key = sha256(path.resolve(configPath)).slice(0, 24);
126
- return path.join(cwd, "node_modules", ".cache", "nomos", `${key}.compile-proof.usda`);
126
+ return path.join(cwd, ".nomos-cache", `${key}.compile-proof.usda`);
127
127
  }
128
128
 
129
129
  function artifactPrim(kind, entry, proofDir) {
@@ -253,14 +253,14 @@ export function readCompileProof({ proofPath, inputs }) {
253
253
  };
254
254
  }
255
255
 
256
- function emitPlanCompileProof({ proofPath, baseIdentity, runtimeSha256, plan, planSha256, inputs }) {
256
+ function emitPlanCompileProof({ proofPath, baseIdentity, runtimeSha256, plan, planSha256, inputs, sourceRoot }) {
257
257
  const proofDir = path.dirname(proofPath);
258
- const inputPrims = inputs.map((entry) => artifactPrim("input", entry, proofDir));
258
+ const inputPrims = inputs.map((entry) => artifactPrim("input", entry, sourceRoot ?? proofDir));
259
259
  return [
260
260
  "#usda 1.0",
261
261
  "(",
262
262
  " customLayerData = {",
263
- ' string "nomos:format" = "nomos.plan-compile-proof.v1"',
263
+ ` string "nomos:format" = "nomos.plan-compile-proof.${sourceRoot === undefined ? "v1" : "v2"}"`,
264
264
  " }",
265
265
  ")",
266
266
  "",
@@ -284,28 +284,33 @@ function emitPlanCompileProof({ proofPath, baseIdentity, runtimeSha256, plan, pl
284
284
  ].join("\n");
285
285
  }
286
286
 
287
- export function writePlanCompileProof({ proofPath, baseIdentity, runtimeSha256, plan, planSha256, inputs }) {
287
+ export function writePlanCompileProof({ proofPath, baseIdentity, runtimeSha256, plan, planSha256, inputs, sourceRoot }) {
288
288
  if (![baseIdentity, runtimeSha256, planSha256, ...inputs.map((entry) => entry.sha256)].every((value) => HASH.test(value))) {
289
289
  throw new Error("plan compile proof requires content-addressed identities");
290
290
  }
291
+ if (sourceRoot !== undefined && inputs.some(({ path: file }) => {
292
+ const relative = path.relative(sourceRoot, file);
293
+ return relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative);
294
+ })) throw new Error("portable plan compile proof requires inputs inside source root");
291
295
  mkdirSync(path.dirname(proofPath), { recursive: true });
292
296
  const temporary = `${proofPath}.tmp-${process.pid}`;
293
297
  writeFileSync(
294
298
  temporary,
295
- emitPlanCompileProof({ proofPath, baseIdentity, runtimeSha256, plan, planSha256, inputs }),
299
+ emitPlanCompileProof({ proofPath, baseIdentity, runtimeSha256, plan, planSha256, inputs, sourceRoot }),
296
300
  "utf8",
297
301
  );
298
302
  renameSync(temporary, proofPath);
299
303
  }
300
304
 
301
- export function readPlanCompileProof({ proofPath, baseIdentity, runtimeSha256, digestFile }) {
305
+ export function readPlanCompileProof({ proofPath, baseIdentity, runtimeSha256, digestFile, sourceRoot }) {
302
306
  let text;
303
307
  try {
304
308
  text = readFileSync(proofPath, "utf8");
305
309
  } catch {
306
310
  return undefined;
307
311
  }
308
- if (!text.includes('string "nomos:format" = "nomos.plan-compile-proof.v1"')) return undefined;
312
+ const format = `nomos.plan-compile-proof.${sourceRoot === undefined ? "v1" : "v2"}`;
313
+ if (!text.includes(`string "nomos:format" = "${format}"`)) return undefined;
309
314
  if (property(text, "baseIdentity") !== baseIdentity || property(text, "runtimeSha256") !== runtimeSha256) {
310
315
  return undefined;
311
316
  }
@@ -314,7 +319,9 @@ export function readPlanCompileProof({ proofPath, baseIdentity, runtimeSha256, d
314
319
  const inputs = [];
315
320
  const inputPattern = /def NomosCompileInput "[^"]+"\s*\{\s*custom uniform asset nomos:path = @([^@]+)@\s*custom uniform string nomos:sha256 = "([0-9a-f]{64})"\s*\}/gu;
316
321
  for (const match of inputsScope.matchAll(inputPattern)) {
317
- inputs.push({ path: path.resolve(path.dirname(proofPath), match[1]), sha256: match[2] });
322
+ const relative = match[1];
323
+ if (sourceRoot !== undefined && (path.isAbsolute(relative) || relative === ".." || relative.startsWith("../"))) return undefined;
324
+ inputs.push({ path: path.resolve(sourceRoot ?? path.dirname(proofPath), relative), sha256: match[2] });
318
325
  }
319
326
  if (inputs.length === 0) return undefined;
320
327
  for (const input of inputs) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@githolon/dsl",
3
- "version": "0.112.4",
3
+ "version": "0.112.6",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Nomos 2 domain-authoring DSL: aggregates + directives in TS, executed and encoded to the Rust kernel's wire shapes.",
@@ -73,7 +73,7 @@
73
73
  "test": "vitest run"
74
74
  },
75
75
  "dependencies": {
76
- "@githolon/runtime": "^0.112.4",
76
+ "@githolon/runtime": "^0.112.6",
77
77
  "@bufbuild/protobuf": "^2.13.0",
78
78
  "@bufbuild/protoc-gen-es": "^2.13.0",
79
79
  "esbuild": "0.24.2",
@@ -46,6 +46,7 @@ import type { Directive } from "./directive.js";
46
46
  import { execFile, execFileSync } from "node:child_process";
47
47
  import { promisify } from "node:util";
48
48
  import { checkOperationContracts } from "./operation_contract_check.js";
49
+ import { compilerIntermediateCacheRoot, compilerSourceName, compilerSourceRoot } from "./compiler_cache.js";
49
50
  import {
50
51
  APPLICATION_CONTRACT_MODULE,
51
52
  assertGeneratedClientBundleBoundary,
@@ -1902,10 +1903,7 @@ function tsOperationProtoCache(
1902
1903
  .update(readFileSync(generatorPackage))
1903
1904
  .digest("hex");
1904
1905
  const cacheDir = path.join(
1905
- cacheRoot,
1906
- "node_modules",
1907
- ".cache",
1908
- "nomos",
1906
+ compilerIntermediateCacheRoot(cacheRoot),
1909
1907
  "operation-protobuf",
1910
1908
  "typescript"
1911
1909
  );
@@ -1970,8 +1968,11 @@ function dartOperationProtoCache(
1970
1968
  path.join(path.dirname(path.dirname(plugin)), "pubspec.lock"),
1971
1969
  ];
1972
1970
  const digest = createHash("sha256");
1971
+ const sourceRoot = compilerSourceRoot();
1973
1972
  for (const file of files) {
1974
- digest.update(`\0${file}\0`);
1973
+ const name = sourceRoot !== undefined && path.resolve(file).startsWith(`${sourceRoot}${path.sep}`)
1974
+ ? compilerSourceName(file, sourceRoot) : file;
1975
+ digest.update(`\0${name}\0`);
1975
1976
  if (existsSync(file)) digest.update(readFileSync(file));
1976
1977
  }
1977
1978
  return digest.digest("hex");
@@ -1985,10 +1986,7 @@ function dartOperationProtoCache(
1985
1986
  .update(generatorIdentity)
1986
1987
  .digest("hex");
1987
1988
  const cacheDir = path.join(
1988
- process.cwd(),
1989
- "node_modules",
1990
- ".cache",
1991
- "nomos",
1989
+ compilerIntermediateCacheRoot(process.cwd()),
1992
1990
  "operation-protobuf",
1993
1991
  "dart"
1994
1992
  );
@@ -2546,7 +2544,7 @@ const NOMOS_TYPES_DEP = "^0.11.3";
2546
2544
  // DomainNotInstalledForSession, engine.follow, pendingConflicts lanes) — the generated client
2547
2545
  // REQUIRES this surface. 0.48.1 fixes NomosSession.watchPendingConflicts delegation.
2548
2546
  const NOMOS_CLIENT_DEP = "^0.66.11";
2549
- const NOMOS_FLUTTER_DEP = "^0.65.17";
2547
+ const NOMOS_FLUTTER_DEP = "^0.65.19";
2550
2548
  // DRIFT TRIPWIRE: pubspec_pins.test.ts asserts these equal the CURRENT versions in the Dart and Flutter
2551
2549
  // package pubspecs — a release without this bump fails the suite, so tenants never
2552
2550
  // land back in dependency_overrides (the exact co2 report this closes, third occurrence).
@@ -6442,6 +6440,7 @@ async function main(): Promise<void> {
6442
6440
  baseIdentity: string;
6443
6441
  runtimeSha256: string;
6444
6442
  digestFile?: (file: string) => string;
6443
+ sourceRoot?: string;
6445
6444
  }): { javascript: string; sha256: string } | undefined;
6446
6445
  writePlanCompileProof(options: {
6447
6446
  proofPath: string;
@@ -6450,15 +6449,20 @@ async function main(): Promise<void> {
6450
6449
  plan: string;
6451
6450
  planSha256: string;
6452
6451
  inputs: readonly { readonly path: string; readonly sha256: string }[];
6452
+ sourceRoot?: string;
6453
6453
  }): void;
6454
6454
  };
6455
6455
  const planCacheDir = path.join(
6456
- cfgDir,
6457
- "node_modules",
6458
- ".cache",
6459
- "nomos",
6456
+ compilerIntermediateCacheRoot(cfgDir),
6460
6457
  "plans"
6461
6458
  );
6459
+ const configuredSourceRoot = compilerSourceRoot();
6460
+ const planSourceRoot = configuredSourceRoot !== undefined &&
6461
+ !path.relative(configuredSourceRoot, cfgDir).startsWith("..") &&
6462
+ !path.isAbsolute(path.relative(configuredSourceRoot, cfgDir))
6463
+ ? configuredSourceRoot : undefined;
6464
+ const portablePlanText = (value: string): string => planSourceRoot === undefined
6465
+ ? value : value.replaceAll(planSourceRoot, "<nomos-source-root>");
6462
6466
  const planInputDigestCache = new Map<string, string>();
6463
6467
  const planBytesDigest = (bytes: Uint8Array): string =>
6464
6468
  createHash("sha256").update(bytes).digest("hex");
@@ -6910,10 +6914,10 @@ async function main(): Promise<void> {
6910
6914
  [
6911
6915
  planCompilerIdentity,
6912
6916
  runtimeSha256,
6913
- path.resolve(cfgDir),
6917
+ planSourceRoot === undefined ? path.resolve(cfgDir) : compilerSourceName(cfgDir, planSourceRoot),
6914
6918
  compact ? "compact" : "readable",
6915
- signature,
6916
- entrySource,
6919
+ portablePlanText(signature),
6920
+ portablePlanText(entrySource),
6917
6921
  ].join("\0")
6918
6922
  );
6919
6923
  const cachePlanPath = path.join(planCacheDir, `${baseIdentity}.mjs`);
@@ -6928,6 +6932,7 @@ async function main(): Promise<void> {
6928
6932
  baseIdentity,
6929
6933
  runtimeSha256,
6930
6934
  digestFile: planInputDigest,
6935
+ ...(planSourceRoot === undefined ? {} : { sourceRoot: planSourceRoot }),
6931
6936
  });
6932
6937
  let bundled: string;
6933
6938
  if (cached !== undefined) {
@@ -6984,7 +6989,11 @@ async function main(): Promise<void> {
6984
6989
  .filter((file) => existsSync(file))
6985
6990
  .map((file) => ({ path: file, sha256: planInputDigest(file) }))
6986
6991
  .sort((left, right) => left.path.localeCompare(right.path));
6987
- if (inputs.length > 0) {
6992
+ const inputsArePortable = planSourceRoot === undefined || inputs.every(({ path: file }) => {
6993
+ const relative = path.relative(planSourceRoot, file);
6994
+ return relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
6995
+ });
6996
+ if (inputs.length > 0 && inputsArePortable) {
6988
6997
  mkdirSync(planCacheDir, { recursive: true });
6989
6998
  const temporary = `${cachePlanPath}.tmp-${process.pid}`;
6990
6999
  writeFileSync(temporary, bundled, "utf8");
@@ -6996,6 +7005,7 @@ async function main(): Promise<void> {
6996
7005
  plan: cachePlanPath,
6997
7006
  planSha256: sha256HexUtf8(bundled),
6998
7007
  inputs,
7008
+ ...(planSourceRoot === undefined ? {} : { sourceRoot: planSourceRoot }),
6999
7009
  });
7000
7010
  }
7001
7011
  }
@@ -8369,10 +8379,7 @@ async function main(): Promise<void> {
8369
8379
  .update(readFileSync(fileURLToPath(import.meta.url)))
8370
8380
  .digest("hex");
8371
8381
  const declarationCacheDir = path.join(
8372
- cfgDir,
8373
- "node_modules",
8374
- ".cache",
8375
- "nomos",
8382
+ compilerIntermediateCacheRoot(cfgDir),
8376
8383
  "typescript-declarations"
8377
8384
  );
8378
8385
  const declarationCachePath = path.join(
@@ -9468,7 +9475,7 @@ async function main(): Promise<void> {
9468
9475
  if (analyzeFlag) runDartAnalyze(dartOut);
9469
9476
 
9470
9477
  // The unchanged lane is itself USDA: a disposable content proof under
9471
- // node_modules/.cache relates authored/compiler inputs to every generated
9478
+ // .nomos-cache relates authored/compiler inputs to every generated
9472
9479
  // artifact. It is not another law manifest and carries no semantic opinions.
9473
9480
  const compileProofPath = process.env.NOMOS_COMPILE_PROOF_PATH;
9474
9481
  const compileArgumentIdentity = process.env.NOMOS_COMPILE_ARGUMENT_IDENTITY;
@@ -0,0 +1,26 @@
1
+ import { homedir } from "node:os";
2
+ import path from "node:path";
3
+
4
+ /** Disposable compiler intermediates may live on one machine for all local runners. */
5
+ export function compilerIntermediateCacheRoot(localRoot: string): string {
6
+ const configured = process.env.NOMOS_COMPILER_CACHE_ROOT;
7
+ if (configured === undefined || configured === "") return path.join(localRoot, ".nomos-cache");
8
+ const expanded = configured === "~" ? homedir() : configured.startsWith(`~${path.sep}`)
9
+ ? path.join(homedir(), configured.slice(2)) : configured;
10
+ if (!path.isAbsolute(expanded)) throw new Error("NOMOS_COMPILER_CACHE_ROOT must be absolute or start with ~/");
11
+ return path.resolve(expanded);
12
+ }
13
+
14
+ /** Relative source names keep content-addressed plans portable between runner checkouts. */
15
+ export function compilerSourceRoot(): string | undefined {
16
+ const configured = process.env.NOMOS_COMPILER_SOURCE_ROOT;
17
+ return configured === undefined || configured === "" ? undefined : path.resolve(configured);
18
+ }
19
+
20
+ export function compilerSourceName(file: string, sourceRoot: string): string {
21
+ const relative = path.relative(sourceRoot, path.resolve(file));
22
+ if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
23
+ throw new Error(`compiler input is outside NOMOS_COMPILER_SOURCE_ROOT: ${file}`);
24
+ }
25
+ return (relative || ".").replaceAll(path.sep, "/");
26
+ }
@@ -2,6 +2,7 @@ import { execFileSync } from 'node:child_process';
2
2
  import { createHash } from 'node:crypto';
3
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
4
4
  import path from 'node:path';
5
+ import { compilerIntermediateCacheRoot } from './compiler_cache.js';
5
6
 
6
7
  /** Validate the emitted law, not a second in-memory interpretation of the DSL.
7
8
  * Cached results accelerate compilation only; they never enter custody or stand
@@ -14,7 +15,7 @@ export function checkOperationContracts(lawPath: string, descriptorPath: string,
14
15
  const key = createHash('sha256').update('nomos.operation-contract-check.v1\0')
15
16
  .update(readFileSync(artifact)).update(readFileSync(checker)).update(readFileSync(lawPath))
16
17
  .update(readFileSync(descriptorPath)).digest('hex');
17
- const cache = path.join(cacheRoot, 'node_modules', '.cache', 'nomos', 'operation-contracts');
18
+ const cache = path.join(compilerIntermediateCacheRoot(cacheRoot), 'operation-contracts');
18
19
  const receipt = path.join(cache, `${key}.ok`);
19
20
  if (existsSync(receipt)) return;
20
21
  try {