@githolon/dsl 0.61.1 → 0.61.3
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/package.json +1 -1
- package/src/codegen_proof.ts +72 -6
- package/src/compat_package_main.ts +28 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@githolon/dsl",
|
|
3
|
-
"version": "0.61.
|
|
3
|
+
"version": "0.61.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Nomos 2 domain-authoring DSL: aggregates + directives in TS, executed and encoded to the Rust kernel's wire shapes.",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE.md",
|
package/src/codegen_proof.ts
CHANGED
|
@@ -2108,17 +2108,16 @@ export function legsForDomain(mod: DomainModule, modules: readonly DomainModule[
|
|
|
2108
2108
|
// only the first left the other children compile-valid-only (unprovable through their birth).
|
|
2109
2109
|
const parentBirths = allBirths.filter((b) => b.parentDomain === domain);
|
|
2110
2110
|
const childBirths = parentBirths.map((b) => {
|
|
2111
|
-
const withParent = attachParentSetup(b, allBirths);
|
|
2112
2111
|
// BIRTHS ISOLATION: with >1 birth leg, uniquify each leg's synthesized child so the legs don't collide
|
|
2113
2112
|
// on one generated child workspace name (the discriminator = the birth directive id, unique per leg).
|
|
2113
|
+
// MUTABLE leg — uniquify does NOT short-circuit the relationSetup attachment below (co2's two sibling
|
|
2114
|
+
// gated births both need BOTH: a distinct child AND the seeded authority chain).
|
|
2115
|
+
let withParent = attachParentSetup(b, allBirths);
|
|
2114
2116
|
if (parentBirths.length > 1) {
|
|
2115
2117
|
const birthDir = (mod.directives as Directive<unknown>[]).find((d) => d?.id === b.birthDirectiveId);
|
|
2116
2118
|
const schema = birthDir?.payloadSchema as z.ZodTypeAny | undefined;
|
|
2117
|
-
const
|
|
2118
|
-
|
|
2119
|
-
if (uniq !== withParent.birthPayload) {
|
|
2120
|
-
return { ...withParent, birthPayload: uniq };
|
|
2121
|
-
}
|
|
2119
|
+
const uniq = uniquifyBirthPayload(withParent.birthPayload, b.birthDirectiveId, schema);
|
|
2120
|
+
if (uniq !== withParent.birthPayload) withParent = { ...withParent, birthPayload: uniq };
|
|
2122
2121
|
}
|
|
2123
2122
|
// A GATED birth with no parent-birth authority path: seat its relation via the SAME granter/seed
|
|
2124
2123
|
// discovery the relation-bootstrap leg uses (the law's own authority path, e.g. co2's
|
|
@@ -2340,6 +2339,65 @@ function v2CatchFinally(): string[] {
|
|
|
2340
2339
|
* lawHash are injected from the deployed law (the child install resolves bytes by hash from the parent's
|
|
2341
2340
|
* custody; a `sample-*` hash would fail). The dispatch rides the gateway offer lane (authenticated principal).
|
|
2342
2341
|
*/
|
|
2342
|
+
/**
|
|
2343
|
+
* THE LIVE AUTHORITY-CHAIN SEATING for a GATED `.births()` leg (the co2 case). A birth gated by
|
|
2344
|
+
* `.requires(R)` needs its actor to HOLD R before the birth admits — and R may itself be reachable only
|
|
2345
|
+
* through a RECURSIVE chain of grants (co2: seedPlatformFounder → seedPlatformAdmin [grants co2Admin] →
|
|
2346
|
+
* grantCo2EstateCreator [grants co2EstateCreator] → birthEstateWorkspace). This walks the discovered
|
|
2347
|
+
* `relationSetup` chain and emits each step as a cloud dispatch under the SAME `birthActor`, seating every
|
|
2348
|
+
* relation ON that actor (subject/founder/grantedBy fields overwritten to it) — so one actor plays founder,
|
|
2349
|
+
* admin, and creator, ending holding R. Recurses granter-authority-first (deepest prerequisite runs first).
|
|
2350
|
+
* `actorVar`/`wsVar` are the runtime var names the birth leg already binds.
|
|
2351
|
+
*/
|
|
2352
|
+
function liveAuthorityChainLines(setup: RelationBootstrapLeg, actorVar: string, wsVar: string): string[] {
|
|
2353
|
+
const lines: string[] = [];
|
|
2354
|
+
// 1. the granter's OWN authority first (recursive — the deepest prerequisite is seated before its dependents).
|
|
2355
|
+
if (setup.granterAuthority !== undefined) {
|
|
2356
|
+
lines.push(...liveAuthorityChainLines(setup.granterAuthority, actorVar, wsVar));
|
|
2357
|
+
}
|
|
2358
|
+
// A runtime dispatch: build the payload literal, overwrite subject/founder/grantedBy/routed fields to the
|
|
2359
|
+
// actor (seat the relation ON it), stamp the auto-stamped fields, POST to the parent ws as the actor.
|
|
2360
|
+
const dispatch = (
|
|
2361
|
+
directiveId: string,
|
|
2362
|
+
payload: Record<string, unknown>,
|
|
2363
|
+
routed: readonly string[],
|
|
2364
|
+
autoStamped: readonly string[],
|
|
2365
|
+
label: string,
|
|
2366
|
+
): string[] => {
|
|
2367
|
+
const overwrite = new Set<string>(routed);
|
|
2368
|
+
const entries = Object.entries(payload).map(([k, v]) =>
|
|
2369
|
+
overwrite.has(k) || /^(subject|principal|grantedBy|grantee|member|user|owner|founder)$/i.test(k)
|
|
2370
|
+
? `${JSON.stringify(k)}: ${actorVar}`
|
|
2371
|
+
: `${JSON.stringify(k)}: ${JSON.stringify(v)}`,
|
|
2372
|
+
);
|
|
2373
|
+
return [
|
|
2374
|
+
` {`,
|
|
2375
|
+
` const cp: Record<string, unknown> = { ${entries.join(", ")} };`,
|
|
2376
|
+
...autoStamped.map((f) => ` cp[${JSON.stringify(f)}] = new Date().toISOString();`),
|
|
2377
|
+
` const cr = await fetch(\`\${CLOUD}/v2/workspaces/\${${wsVar}}/${directiveId}\`, { method: "POST", headers: { "content-type": "application/json", "x-nomos-principal": ${actorVar} }, body: JSON.stringify(cp) });`,
|
|
2378
|
+
` const cd = await cr.json();`,
|
|
2379
|
+
` if (!cd.ok) fail(\`authority chain (${label}) ${directiveId} refused: \${JSON.stringify(cd)}\`);`,
|
|
2380
|
+
` ok(\`authority chain — ${directiveId} seated (as \${${actorVar}})\`);`,
|
|
2381
|
+
` }`,
|
|
2382
|
+
];
|
|
2383
|
+
};
|
|
2384
|
+
// 2. this leg's prerequisite seeds (as the actor = founder).
|
|
2385
|
+
for (const seed of setup.prerequisiteSeeds ?? []) {
|
|
2386
|
+
lines.push(...dispatch(seed.directiveId, seed.payload, [], seed.autoStamped, "prerequisite"));
|
|
2387
|
+
}
|
|
2388
|
+
// 3. the grant that seats this leg's relation ON the actor (subject = actor).
|
|
2389
|
+
lines.push(
|
|
2390
|
+
...dispatch(
|
|
2391
|
+
setup.grantDirectiveId,
|
|
2392
|
+
setup.grantPayload,
|
|
2393
|
+
[...setup.grantSubjectFields, ...setup.grantGrantedByFields],
|
|
2394
|
+
setup.grantAutoStamped,
|
|
2395
|
+
"grant",
|
|
2396
|
+
),
|
|
2397
|
+
);
|
|
2398
|
+
return lines;
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2343
2401
|
function liveChildBirthLegLines(cb: ChildBirthLeg, opts: TsProofOptions, n: () => number): string[] {
|
|
2344
2402
|
const hashConst = tsHashConstName(opts.packageName);
|
|
2345
2403
|
// The birth payload literal: the synthesized values + the REAL hashes onto the discovered fields + the
|
|
@@ -2403,6 +2461,14 @@ function liveChildBirthLegLines(cb: ChildBirthLeg, opts: TsProofOptions, n: () =
|
|
|
2403
2461
|
: []),
|
|
2404
2462
|
` birthPayload[${JSON.stringify(cb.lawHashField ?? "lawHash")}] = ${hashConst};`,
|
|
2405
2463
|
...cb.birthAutoStamped.map((f) => ` birthPayload[${JSON.stringify(f)}] = new Date().toISOString();`),
|
|
2464
|
+
// GATED BIRTH: seat the FULL authority chain on birthActor before the birth (co2: seedPlatformFounder →
|
|
2465
|
+
// seedPlatformAdmin → grantCo2EstateCreator → the birth). Without this the .requires(R) birth is refused.
|
|
2466
|
+
...(cb.relationSetup !== undefined
|
|
2467
|
+
? [
|
|
2468
|
+
` // seat the .requires("${cb.birthRequiresRelation}") authority chain on birthActor before the gated birth:`,
|
|
2469
|
+
...liveAuthorityChainLines(cb.relationSetup, "birthActor", "birthParentWs"),
|
|
2470
|
+
]
|
|
2471
|
+
: []),
|
|
2406
2472
|
` const br = await fetch(\`\${CLOUD}/v2/workspaces/\${birthParentWs}/${cb.birthDirectiveId}\`, { method: "POST", headers: { "content-type": "application/json", "x-nomos-principal": birthActor }, body: JSON.stringify(birthPayload) });`,
|
|
2407
2473
|
` const bd = await br.json();`,
|
|
2408
2474
|
` if (!bd.ok) fail(\`child birth offer ${cb.parentDomain}/${cb.birthDirectiveId} refused: \${JSON.stringify(bd)}\`);`,
|
|
@@ -97,11 +97,29 @@ function loadFileOffer(arg: string): { offer: InterfaceMap; source: string } {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
/** Read a LIVE workspace's on-ledger offer via connect() (`@githolon/client`). */
|
|
100
|
+
/**
|
|
101
|
+
* Parse a live compat target URL into its cloud BASE and workspace. The base is everything BEFORE
|
|
102
|
+
* `/v2/workspaces/` — NOT just the origin — so a gateway mounted under a PATH PREFIX
|
|
103
|
+
* (`https://host/nomos/cloud/v2/workspaces/<ws>`) keeps that prefix. Otherwise the client would fetch the
|
|
104
|
+
* runtime at the wrong base (`https://host/v1/runtime/holon.wasm` 404s instead of
|
|
105
|
+
* `https://host/nomos/cloud/v1/runtime/holon.wasm`). Split on the marker so an arbitrary-depth prefix
|
|
106
|
+
* survives; without the marker the whole URL (a `--workspace`-paired base) is the cloud.
|
|
107
|
+
*/
|
|
108
|
+
export function parseLiveTarget(
|
|
109
|
+
url: string,
|
|
110
|
+
workspaceFlag?: string,
|
|
111
|
+
): { cloud: string; workspace: string | undefined } {
|
|
112
|
+
const marker = "/v2/workspaces/";
|
|
113
|
+
const at = url.indexOf(marker);
|
|
114
|
+
const cloud = (at >= 0 ? url.slice(0, at) : url.replace(/[?#].*$/, "")).replace(/\/+$/, "");
|
|
115
|
+
const wsFromUrl = at >= 0 ? url.slice(at + marker.length).split(/[/?#]/)[0] : undefined;
|
|
116
|
+
const workspace = workspaceFlag ?? (wsFromUrl !== undefined && wsFromUrl !== "" ? wsFromUrl : undefined);
|
|
117
|
+
return { cloud, workspace };
|
|
118
|
+
}
|
|
119
|
+
|
|
100
120
|
async function loadLiveOffer(url: string, workspaceFlag?: string): Promise<{ offer: InterfaceMap; source: string }> {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const cloud = m[1]!;
|
|
104
|
-
const workspace = workspaceFlag ?? m[2];
|
|
121
|
+
if (!/^https?:\/\//.test(url)) fail(`could not parse the live target URL: ${url}`);
|
|
122
|
+
const { cloud, workspace } = parseLiveTarget(url, workspaceFlag);
|
|
105
123
|
if (!workspace) fail(`live target needs a workspace — use https://host/v2/workspaces/<ws> or add --workspace <ws>`);
|
|
106
124
|
let connect: (opts: { cloud: string; workspace: string }) => Promise<{ interfaceOffer(): Promise<InterfaceMap | null> }>;
|
|
107
125
|
try {
|
|
@@ -184,4 +202,9 @@ async function main(): Promise<void> {
|
|
|
184
202
|
process.exit(incompatible.length > 0 ? 1 : 0);
|
|
185
203
|
}
|
|
186
204
|
|
|
187
|
-
|
|
205
|
+
// Run ONLY as the entry point (the compat launcher), never on import (the test imports parseLiveTarget).
|
|
206
|
+
{
|
|
207
|
+
const { pathToFileURL } = await import("node:url");
|
|
208
|
+
const entry = process.argv[1];
|
|
209
|
+
if (entry !== undefined && import.meta.url === pathToFileURL(entry).href) void main();
|
|
210
|
+
}
|