@githolon/dsl 0.5.1 → 0.7.0

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/src/usd_layers.ts CHANGED
@@ -99,12 +99,14 @@ export function usdaStringArray(items: readonly string[]): string {
99
99
 
100
100
  /**
101
101
  * A `WireDriver` as a READABLE usda token: unit drivers are their bare serde
102
- * string (`"Lww"`, `"AddWins"`, …); `MapOf` nests as `MapOf(<inner>)`. Readable on
103
- * purpose — the usdcat/usdview tree is the visual proof, so the merge law of every
104
- * field must be legible in it (hex would bury the point).
102
+ * string (`"Lww"`, `"AddWins"`, `"LwwCausal"`, …); `MapOf` nests as `MapOf(<inner>)`;
103
+ * the EXPLICIT TIME-AUTHORITY `LwwBy` carries its principal as `LwwBy(<principal>)`.
104
+ * Readable on purpose — the usdcat/usdview tree is the visual proof, so the merge law
105
+ * (and the time-authority) of every field must be legible in it (hex would bury the point).
105
106
  */
106
107
  export function driverToken(driver: WireDriver): string {
107
108
  if (typeof driver === "string") return driver;
109
+ if ("LwwBy" in driver) return `LwwBy(${driver.LwwBy})`;
108
110
  return `MapOf(${driverToken(driver.MapOf)})`;
109
111
  }
110
112
 
@@ -113,6 +115,15 @@ export function parseDriverToken(token: string): WireDriver {
113
115
  if (token.startsWith("MapOf(") && token.endsWith(")")) {
114
116
  return { MapOf: parseDriverToken(token.slice("MapOf(".length, -1)) };
115
117
  }
118
+ // `LwwBy(<principal>)` — the principal is an opaque non-empty string (uids carry
119
+ // hyphens, so it is NOT the bare-identifier shape the unit-driver branch validates).
120
+ if (token.startsWith("LwwBy(") && token.endsWith(")")) {
121
+ const principal = token.slice("LwwBy(".length, -1);
122
+ if (principal.length === 0) {
123
+ throw new Error(`usd-layers: LwwBy driver token names no time-authority principal`);
124
+ }
125
+ return { LwwBy: principal };
126
+ }
116
127
  if (!/^[A-Za-z][A-Za-z0-9]*$/.test(token)) {
117
128
  throw new Error(`usd-layers: malformed driver token '${token}'`);
118
129
  }
@@ -348,6 +359,12 @@ export function usdaLayerText(
348
359
  ...(layer.nomosReadGate !== undefined
349
360
  ? [` custom string nomos:readGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosReadGate)))}`]
350
361
  : []),
362
+ ...(layer.nomosReadVisibility !== undefined
363
+ ? [` custom string nomos:readVisibilityHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosReadVisibility)))}`]
364
+ : []),
365
+ ...(layer.nomosEncryption !== undefined
366
+ ? [` custom string nomos:encryptionHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosEncryption)))}`]
367
+ : []),
351
368
  ``,
352
369
  ...body,
353
370
  ` }`,
@@ -502,6 +519,36 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
502
519
  readGate = { v: 1, queries: queriesRec, directives: directivesRec };
503
520
  }
504
521
  }
522
+ // The read-visibility gate (slice 5.1): per-aggregate required-read caps merged
523
+ // later-wins, SORTED keys (matching the composed emission). Omit-when-empty.
524
+ let readVisibility: UsdLayer["nomosReadVisibility"];
525
+ {
526
+ const a = new Map<string, { requires: string }>();
527
+ for (const l of stack) {
528
+ if (l.nomosReadVisibility === undefined) continue;
529
+ for (const [type, spec] of Object.entries(l.nomosReadVisibility.aggregates)) a.set(type, spec);
530
+ }
531
+ if (a.size > 0) {
532
+ const aggregatesRec: Record<string, { requires: string }> = {};
533
+ for (const k of [...a.keys()].sort()) aggregatesRec[k] = a.get(k)!;
534
+ readVisibility = { v: 1, aggregates: aggregatesRec };
535
+ }
536
+ }
537
+ // The encryption gate (slice 2): per-aggregate encrypted-field sets merged later-wins,
538
+ // SORTED keys (matching the composed emission). Omit-when-empty.
539
+ let encryption: UsdLayer["nomosEncryption"];
540
+ {
541
+ const a = new Map<string, { scope: string; fields: string[]; delegatable: false }>();
542
+ for (const l of stack) {
543
+ if (l.nomosEncryption === undefined) continue;
544
+ for (const [type, spec] of Object.entries(l.nomosEncryption.aggregates)) a.set(type, spec);
545
+ }
546
+ if (a.size > 0) {
547
+ const aggregatesRec: Record<string, { scope: string; fields: string[]; delegatable: false }> = {};
548
+ for (const k of [...a.keys()].sort()) aggregatesRec[k] = a.get(k)!;
549
+ encryption = { v: 1, aggregates: aggregatesRec };
550
+ }
551
+ }
505
552
  return {
506
553
  path,
507
554
  prims: [...prims.keys()].sort().map((p) => prims.get(p)!),
@@ -510,6 +557,8 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
510
557
  ...(combineds !== undefined ? { combineds } : {}),
511
558
  ...(stableIds !== undefined ? { nomosStableIds: stableIds } : {}),
512
559
  ...(readGate !== undefined ? { nomosReadGate: readGate } : {}),
560
+ ...(readVisibility !== undefined ? { nomosReadVisibility: readVisibility } : {}),
561
+ ...(encryption !== undefined ? { nomosEncryption: encryption } : {}),
513
562
  };
514
563
  });
515
564
 
@@ -629,6 +678,8 @@ export function parseUsdaDocument(text: string): UsdDocument {
629
678
  const domainByScope = new Map<string, string>();
630
679
  const stableIdsByDomain = new Map<string, UsdLayer["nomosStableIds"]>();
631
680
  const readGateByDomain = new Map<string, UsdLayer["nomosReadGate"]>();
681
+ const readVisibilityByDomain = new Map<string, UsdLayer["nomosReadVisibility"]>();
682
+ const encryptionByDomain = new Map<string, UsdLayer["nomosEncryption"]>();
632
683
  for (const p of prims) {
633
684
  if (p.attrs.get("nomos:kind") === "domain") {
634
685
  const domain = attrString(p, "nomos:domain");
@@ -642,6 +693,16 @@ export function parseUsdaDocument(text: string): UsdDocument {
642
693
  if (typeof rgHex === "string") {
643
694
  readGateByDomain.set(domain, JSON.parse(unhexUtf8(rgHex)) as UsdLayer["nomosReadGate"]);
644
695
  }
696
+ // THE READ-VISIBILITY GATE (slice 5.1): the hex-carried per-aggregate cap map.
697
+ const rvHex = p.attrs.get("nomos:readVisibilityHex");
698
+ if (typeof rvHex === "string") {
699
+ readVisibilityByDomain.set(domain, JSON.parse(unhexUtf8(rvHex)) as UsdLayer["nomosReadVisibility"]);
700
+ }
701
+ // THE ENCRYPTION GATE (slice 2): the hex-carried per-aggregate encrypted-field map.
702
+ const encHex = p.attrs.get("nomos:encryptionHex");
703
+ if (typeof encHex === "string") {
704
+ encryptionByDomain.set(domain, JSON.parse(unhexUtf8(encHex)) as UsdLayer["nomosEncryption"]);
705
+ }
645
706
  }
646
707
  }
647
708
 
@@ -731,6 +792,8 @@ export function parseUsdaDocument(text: string): UsdDocument {
731
792
  const b = buckets.get(domain)!;
732
793
  const sids = stableIdsByDomain.get(domain);
733
794
  const rg = readGateByDomain.get(domain);
795
+ const rv = readVisibilityByDomain.get(domain);
796
+ const enc = encryptionByDomain.get(domain);
734
797
  return {
735
798
  path: `/Nomos/${domain}`,
736
799
  prims: [...b.prims].sort(byPath),
@@ -739,6 +802,8 @@ export function parseUsdaDocument(text: string): UsdDocument {
739
802
  ...(b.combineds.length > 0 ? { combineds: [...b.combineds].sort(byId) } : {}),
740
803
  ...(sids !== undefined ? { nomosStableIds: sids } : {}),
741
804
  ...(rg !== undefined ? { nomosReadGate: rg } : {}),
805
+ ...(rv !== undefined ? { nomosReadVisibility: rv } : {}),
806
+ ...(enc !== undefined ? { nomosEncryption: enc } : {}),
742
807
  };
743
808
  })
744
809
  .sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
package/src/wire.ts CHANGED
@@ -130,19 +130,23 @@ export interface WireRead {
130
130
 
131
131
  /**
132
132
  * `Driver` — the kernel's externally-tagged serde encoding (`nomos2/kernel/src/lib.rs`).
133
- * Unit variants → bare strings; `MapOf` → `{ MapOf: <inner> }`. This mirrors the kernel
134
- * variants the DSL `encodeDriver` emits: the four originals plus the structural CRDT unit
135
- * drivers `RemoveWins` / `Counter` / `LastPosition` (each a real `merge_field` arm).
133
+ * Unit variants → bare strings; `MapOf` → `{ MapOf: <inner> }`; the newtype `LwwBy(String)`
134
+ * → `{ LwwBy: <principal> }`. This mirrors the kernel variants the DSL `encodeDriver` emits:
135
+ * the four originals, the structural CRDT unit drivers `RemoveWins` / `Counter` /
136
+ * `LastPosition`, and the EXPLICIT TIME-AUTHORITY drivers `LwwCausal` (causal-only LWW) /
137
+ * `LwwBy` (principal-as-time-authority LWW) — each a real `merge_field` arm.
136
138
  * (`NumericMax`/`NumericMin`/`ImmutableAfterCreate`/`OrderedList` are kernel variants no
137
139
  * DSL driver encodes to yet — added here only as the DSL gains a constant for them.)
138
140
  */
139
141
  export type WireDriver =
140
142
  | "Lww"
143
+ | "LwwCausal"
141
144
  | "AddWins"
142
145
  | "Conflict"
143
146
  | "RemoveWins"
144
147
  | "Counter"
145
148
  | "LastPosition"
149
+ | { LwwBy: string }
146
150
  | { MapOf: WireDriver };
147
151
 
148
152
  /** `Schema = BTreeMap<FieldId, Driver>` — a plain object, field -> Driver. */