@githolon/dsl 0.103.3 → 0.103.5

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.
Files changed (42) hide show
  1. package/dart/CHANGELOG.md +15 -0
  2. package/dart/lib/src/runner/node-entry.mjs +411 -149
  3. package/dart/lib/src/runner/offline-install.mjs +222 -99
  4. package/dart/lib/src/session_bound.dart +111 -2
  5. package/dart/lib/src/test_harness.dart +117 -66
  6. package/dart/lib/src/webview_bridge.dart +726 -61
  7. package/dart/pubspec.lock +1 -1
  8. package/dart/pubspec.yaml +4 -5
  9. package/dart/test/acquire_session_test.dart +62 -5
  10. package/dart/test/cloud_bound_topology_test.dart +59 -38
  11. package/dart/test/session_bound_test.dart +25 -10
  12. package/dart/test/webview_bridge_test.dart +357 -9
  13. package/dart_types/CHANGELOG.md +5 -0
  14. package/dart_types/lib/nomos_types.dart +1 -0
  15. package/dart_types/lib/src/git_object_id.dart +37 -0
  16. package/dart_types/pubspec.yaml +1 -1
  17. package/package.json +1 -1
  18. package/runtime/generated-application-host.js +425 -145
  19. package/src/births.ts +16 -1
  20. package/src/business_birth.ts +67 -46
  21. package/src/codegen_dart.ts +68 -12
  22. package/src/codegen_dart_application.ts +114 -19
  23. package/src/codegen_dart_client.ts +149 -6
  24. package/src/codegen_proof.ts +16 -9
  25. package/src/codegen_provider_dart.ts +5 -0
  26. package/src/codegen_ts.ts +115 -19
  27. package/src/compile_package_main.ts +142 -40
  28. package/src/fields.ts +43 -0
  29. package/src/framework/attested_receipts.ts +34 -1
  30. package/src/framework/domain_lifecycle.ts +140 -83
  31. package/src/framework/genesis.ts +2 -1
  32. package/src/framework/home.ts +20 -45
  33. package/src/framework/workspaces.ts +62 -29
  34. package/src/idof_lint.ts +65 -51
  35. package/src/index.ts +21 -1
  36. package/src/intent.ts +11 -1
  37. package/src/law_graph.ts +81 -11
  38. package/src/law_graph_usda.ts +45 -54
  39. package/src/relation_schema.ts +18 -0
  40. package/src/shape_compat.ts +3 -0
  41. package/src/stable_ids_for_module.ts +7 -1
  42. package/src/value_object.ts +40 -0
@@ -1589,74 +1589,71 @@ function defaultOpinions(
1589
1589
  owner: string,
1590
1590
  indent: number,
1591
1591
  ): string[] {
1592
- const pad = " ".repeat(indent);
1593
- if (typeof value === "string") return [`${pad}custom uniform string nomos:default = ${quote(value)}`];
1594
- if (typeof value === "boolean") return [`${pad}custom uniform bool nomos:default = ${value}`];
1592
+ // Every default, including a scalar, uses the same nominal value algebra as a call. Presence of
1593
+ // the typed `Default` child is the fact; there is no parallel property/tag representation.
1594
+ return emitNominalConstant("Default", value, owner, indent);
1595
+ }
1596
+
1597
+ enum NominalConstantPrim {
1598
+ Null = "NomosNullValue",
1599
+ Boolean = "NomosBoolValue",
1600
+ Integer = "NomosIntValue",
1601
+ Number = "NomosNumberValue",
1602
+ String = "NomosStringValue",
1603
+ List = "NomosListValue",
1604
+ Record = "NomosRecordValue",
1605
+ }
1606
+
1607
+ function nominalConstantPrim(value: import("./law_value_type.js").LawConstant): NominalConstantPrim {
1608
+ if (value === null) return NominalConstantPrim.Null;
1609
+ if (typeof value === "string") return NominalConstantPrim.String;
1610
+ if (typeof value === "boolean") return NominalConstantPrim.Boolean;
1595
1611
  if (typeof value === "number") {
1596
- return [Number.isInteger(value)
1597
- ? `${pad}custom uniform int64 nomos:default = ${value}`
1598
- : `${pad}custom uniform double nomos:default = ${value}`];
1612
+ return Number.isInteger(value) ? NominalConstantPrim.Integer : NominalConstantPrim.Number;
1599
1613
  }
1600
- if (value === null) return [`${pad}custom uniform token nomos:defaultKind = "null"`];
1601
-
1602
- // USDA dictionaries cannot faithfully represent heterogeneous/nested arrays. Preserve the
1603
- // complete constant instead as a typed prim tree: generic USD tools can traverse it, humans can
1604
- // inspect it, and the kernel parser retains every opinion without an embedded JSON sub-language.
1605
- const kind = Array.isArray(value) ? "array" : "record";
1606
- return [
1607
- `${pad}custom uniform token nomos:defaultKind = ${quote(kind)}`,
1608
- `${pad}def Scope "Default"`,
1609
- `${pad}{`,
1610
- ...emitDefaultValue(value, owner, indent + 4),
1611
- `${pad}}`,
1612
- ];
1614
+ return Array.isArray(value) ? NominalConstantPrim.List : NominalConstantPrim.Record;
1613
1615
  }
1614
1616
 
1615
- function emitDefaultValue(
1617
+ function emitNominalConstant(
1618
+ name: string,
1616
1619
  value: import("./law_value_type.js").LawConstant,
1617
1620
  owner: string,
1618
1621
  indent: number,
1619
1622
  ): string[] {
1620
1623
  const pad = " ".repeat(indent);
1624
+ const lines = [
1625
+ `${pad}def ${nominalConstantPrim(value)} ${quote(name)}`,
1626
+ `${pad}{`,
1627
+ ];
1621
1628
  if (typeof value === "string") {
1622
- return [
1623
- `${pad}custom uniform token nomos:valueKind = "string"`,
1624
- `${pad}custom uniform string nomos:value = ${quote(value)}`,
1625
- ];
1629
+ lines.push(`${pad} custom uniform string nomos:value = ${quote(value)}`);
1630
+ return [...lines, `${pad}}`];
1626
1631
  }
1627
1632
  if (typeof value === "boolean") {
1628
- return [
1629
- `${pad}custom uniform token nomos:valueKind = "bool"`,
1630
- `${pad}custom uniform bool nomos:value = ${value}`,
1631
- ];
1633
+ lines.push(`${pad} custom uniform bool nomos:value = ${value}`);
1634
+ return [...lines, `${pad}}`];
1632
1635
  }
1633
1636
  if (typeof value === "number") {
1634
1637
  if (!Number.isFinite(value)) fail(`field '${owner}' has a non-finite numeric default`);
1635
- return [
1636
- `${pad}custom uniform token nomos:valueKind = ${quote(Number.isInteger(value) ? "int" : "number")}`,
1637
- Number.isInteger(value)
1638
- ? `${pad}custom uniform int64 nomos:value = ${value}`
1639
- : `${pad}custom uniform double nomos:value = ${value}`,
1640
- ];
1638
+ lines.push(Number.isInteger(value)
1639
+ ? `${pad} custom uniform int64 nomos:value = ${value}`
1640
+ : `${pad} custom uniform double nomos:value = ${value}`);
1641
+ return [...lines, `${pad}}`];
1641
1642
  }
1642
- if (value === null) return [`${pad}custom uniform token nomos:valueKind = "null"`];
1643
+ if (value === null) return [...lines, `${pad}}`];
1643
1644
 
1644
1645
  const entries: readonly [string, import("./law_value_type.js").LawConstant][] = Array.isArray(value)
1645
1646
  ? value.map((item, index) => [String(index), item] as const)
1646
1647
  : Object.entries(value).sort(([left], [right]) => left.localeCompare(right));
1647
- const kind = Array.isArray(value) ? "array" : "record";
1648
- const lines = [`${pad}custom uniform token nomos:valueKind = ${quote(kind)}`];
1649
1648
  for (const [index, [key, item]] of entries.entries()) {
1650
- lines.push(
1651
- "",
1652
- `${pad}def Scope "v_${String(index).padStart(8, "0")}"`,
1653
- `${pad}{`,
1654
- `${pad} custom uniform string nomos:key = ${quote(key)}`,
1655
- ...emitDefaultValue(item, owner, indent + 4),
1656
- `${pad}}`,
1657
- );
1649
+ const childName = `${Array.isArray(value) ? "i" : "f"}_${String(index).padStart(8, "0")}`;
1650
+ const child = emitNominalConstant(childName, item, owner, indent + 4);
1651
+ if (!Array.isArray(value)) {
1652
+ child.splice(2, 0, `${pad} custom uniform string nomos:name = ${quote(key)}`);
1653
+ }
1654
+ lines.push("", ...child);
1658
1655
  }
1659
- return lines;
1656
+ return [...lines, `${pad}}`];
1660
1657
  }
1661
1658
 
1662
1659
  function emitField(
@@ -1703,14 +1700,8 @@ function emitField(
1703
1700
  body.push(
1704
1701
  `${pad} def NomosFieldEvolutionCase "c_${String(caseIndex).padStart(8, "0")}"`,
1705
1702
  `${pad} {`,
1706
- `${pad} def Scope "From"`,
1707
- `${pad} {`,
1708
- ...emitDefaultValue(evolutionCase.from as import("./law_value_type.js").LawConstant, field.entity.label, indent + 24),
1709
- `${pad} }`,
1710
- `${pad} def Scope "To"`,
1711
- `${pad} {`,
1712
- ...emitDefaultValue(evolutionCase.to as import("./law_value_type.js").LawConstant, field.entity.label, indent + 24),
1713
- `${pad} }`,
1703
+ ...emitNominalConstant("From", evolutionCase.from as import("./law_value_type.js").LawConstant, field.entity.label, indent + 20),
1704
+ ...emitNominalConstant("To", evolutionCase.to as import("./law_value_type.js").LawConstant, field.entity.label, indent + 20),
1714
1705
  `${pad} }`,
1715
1706
  );
1716
1707
  }
@@ -108,6 +108,24 @@ export interface RelationTypeDecl {
108
108
  readonly __isRelationType: true;
109
109
  }
110
110
 
111
+ /**
112
+ * A relation name that has deliberately crossed the authoring boundary.
113
+ *
114
+ * Plain strings remain accepted by the low-level DSL while existing tenant law migrates, but framework
115
+ * vocabulary should be coined once with {@link relationRef} and then reused by relation declarations,
116
+ * gates and tuple writes. That makes a rename a compiler-guided change instead of three unrelated string
117
+ * edits which can produce valid-looking, permanently unauthorized law.
118
+ */
119
+ declare const NOMOS_RELATION_REF: unique symbol;
120
+ export type RelationRef<Name extends string = string> = Name & {
121
+ readonly [NOMOS_RELATION_REF]: "NomosRelationRef";
122
+ };
123
+
124
+ /** Create one typed relation handle after validating the wire token once. */
125
+ export function relationRef<const Name extends string>(name: Name): RelationRef<Name> {
126
+ return requireName(name, "relation") as RelationRef<Name>;
127
+ }
128
+
111
129
  export function isRelationTypeDecl(v: unknown): v is RelationTypeDecl {
112
130
  return typeof v === "object" && v !== null && (v as { __isRelationType?: unknown }).__isRelationType === true;
113
131
  }
@@ -130,6 +130,9 @@ function payloadFieldKind(spec: PayloadFieldSpec): string {
130
130
  case "ref":
131
131
  // NomosRef<T> is aggregate-typed in the client and remains a string id on the wire.
132
132
  return "string";
133
+ case "addressRef":
134
+ // A workspace-qualified NomosRef<T> preserves its complete address object.
135
+ return "json";
133
136
  case "recipient":
134
137
  // Recipient<User> is nominal in clients and an opaque delivery address on the law wire.
135
138
  return "string";
@@ -254,7 +254,13 @@ function safeIdentityProjection(sourceShape: unknown, targetShape: unknown): boo
254
254
  const target = targetShape as Record<string, unknown>;
255
255
  const physical = (shape: Record<string, unknown>) => {
256
256
  const { kind, mapValueKind, jsonShape, encrypted } = shape;
257
- return { kind, mapValueKind, jsonShape, encrypted };
257
+ // `t.ref` and `t.foreignRef` store the target aggregate id as the same Str
258
+ // value as `t.string`; `ref` is a semantic/codegen contract, not a distinct
259
+ // kernel encoding. The JSON-schema subset check below still prevents an
260
+ // atomic NomosRef object (or any other non-string reference contract) from
261
+ // being mistaken for this identity migration.
262
+ const encodingKind = kind === "ref" ? "string" : kind;
263
+ return { kind: encodingKind, mapValueKind, jsonShape, encrypted };
258
264
  };
259
265
  return canonicalJson(physical(source)) === canonicalJson(physical(target))
260
266
  && jsonSchemaSubset(source.jsonSchema, target.jsonSchema);
@@ -321,6 +321,46 @@ const nomosAddressSchema = z.object({
321
321
  id: z.string().min(1),
322
322
  });
323
323
 
324
+ /**
325
+ * THE WORKSPACE-QUALIFIED REFERENCE BRAND — `refOf(CatalogueWorkspace)` is
326
+ * wire-identical to Nomos' typed `{ workspace, type, id }` address, while
327
+ * generated clients expose it as `NomosRef<CatalogueWorkspace>`. Unlike
328
+ * `idOf(...)`, this deliberately preserves the workspace because it is the
329
+ * canonical payload shape for cross-workspace navigation and custody effects.
330
+ */
331
+ export const NOMOS_REFOF_BRAND: unique symbol = Symbol.for("nomos.refOf");
332
+
333
+ export interface RefOfMeta {
334
+ readonly aggregateType: string;
335
+ readonly targetKind: "aggregate" | "contract";
336
+ }
337
+
338
+ export function refOf(target: RefTarget): z.ZodType<z.infer<typeof nomosAddressSchema>> {
339
+ const resolved = refTargetMeta(target, "refOf");
340
+ const schema = nomosAddressSchema.strict();
341
+ Object.defineProperty(schema, NOMOS_REFOF_BRAND, {
342
+ value: {
343
+ aggregateType: resolved.type,
344
+ targetKind: resolved.kind,
345
+ } satisfies RefOfMeta,
346
+ enumerable: false,
347
+ });
348
+ return schema;
349
+ }
350
+
351
+ /** Resolve `refOf(...)` metadata through omission/null wrappers. */
352
+ export function refOfMetaOf(zt: z.ZodTypeAny): RefOfMeta | undefined {
353
+ let cur: z.ZodTypeAny | undefined = zt;
354
+ for (let hops = 0; cur !== undefined && hops < 16; hops++) {
355
+ const branded = (cur as unknown as Record<symbol, unknown>)[NOMOS_REFOF_BRAND];
356
+ if (typeof branded === "object" && branded !== null) return branded as RefOfMeta;
357
+ const kind = zodKind(cur);
358
+ if (kind !== "optional" && kind !== "default" && kind !== "nullable") return undefined;
359
+ cur = zodDef(cur).innerType;
360
+ }
361
+ return undefined;
362
+ }
363
+
324
364
  /**
325
365
  * A payload field holding an immutable publication and its stable current-version
326
366
  * series. The kernel receives `{pinned, series}`; `current` is compiled law and