@githolon/dsl 0.40.0 → 0.41.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/dart/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.30.0
2
+
3
+ - RUNTIME AGGREGATE METADATA: `NomosAggregateMeta` / `NomosFieldMeta` / `mergeAggregateMeta` (`src/aggregate_meta.dart`) — the support types for the generated per-domain `const Map<String, NomosAggregateMeta> <domain>AggregateMeta` registry (`nomos-compile` ≥0.41 tooling). Generic surfaces (history/audit UIs, op renderers, in-app agents) resolve `NomosAggregateMeta.typeOfId(op.aggregate)` → field kinds, enum wire values, VO/variant names, and the typed `fromJson` decoder — the compiler's knowledge emitted as data, derived from the law's own declarations.
4
+
1
5
  ## 0.29.0
2
6
 
3
7
  - LAW-MINTED IDS RETURN TO THE CALLER: `NomosBridge.offerCreates(...)` → `NomosOfferOutcome{head, created}` — a `.creates` plan mints its aggregate id inside the sealed intent (payloads never carry one); the offer now hands the minted ids back, so generated create methods return the typed id with no query-after round-trip. New types `NomosOfferOutcome` / `NomosCreated`.
@@ -30,6 +30,9 @@ export 'src/offer.dart';
30
30
  export 'src/home.dart';
31
31
  export 'src/subscriptions.dart';
32
32
  export 'src/schema_validation.dart';
33
+ // RUNTIME AGGREGATE METADATA — the generated per-domain registry's support types
34
+ // (lookup by aggregateType for generic surfaces: history UIs, op renderers, agents).
35
+ export 'src/aggregate_meta.dart';
33
36
  // THE WEBVIEW BRIDGE (app-binding boundary) — the async Dart runtime that drives the REAL githolon running
34
37
  // in a WebView, so the generated typed client never needs a `MemoryPersistence` fake.
35
38
  export 'src/webview_bridge.dart';
@@ -0,0 +1,80 @@
1
+ // RUNTIME AGGREGATE METADATA (architecture/dart_codegen_parity.md — the registry slice).
2
+ //
3
+ // Dart is nominal and has no runtime type derivation, so everything the compiler knows about an
4
+ // aggregate's shape is invisible at runtime unless it is EMITTED AS DATA. `nomos-compile`
5
+ // generates one `const Map<String, NomosAggregateMeta>` per domain (keyed by the projection's
6
+ // wire type, e.g. `drawing.Site`), so generic surfaces — history/audit UIs over `historyOf`,
7
+ // undo previews, op renderers, an in-app agent introspecting state — can resolve
8
+ // `(aggregateType, field, rawValue)` to typed knowledge with NO hand-maintained switch.
9
+ //
10
+ // THE LINE THIS HOLDS: this is client-side reflection over the LAW'S DECLARED SHAPE — derived
11
+ // from the same descriptors the package IR carries, never hand-authored, never a second source
12
+ // of truth. If it disagrees with the law, the generator is broken, not the registry.
13
+
14
+ /// Runtime metadata for ONE field of an aggregate — the compiler's knowledge, emitted as data.
15
+ class NomosFieldMeta {
16
+ /// The DSL field kind (`string` | `int` | `double` | `bool` | `enum` | `set` | `map` |
17
+ /// `json` | `ref` | `counter` | `position` | `evidence` | ...).
18
+ final String kind;
19
+
20
+ /// For `enum` kinds: the exact wire values the law's zod validates.
21
+ final List<String>? enumValues;
22
+
23
+ /// For `json` leaves that are DECLARED value objects: the VO / variant name
24
+ /// (the generated Dart class / sealed base of the same name decodes it).
25
+ final String? vo;
26
+
27
+ /// True when [vo] names a `variant(...)` sum type (decode lands on a sealed hierarchy
28
+ /// whose `Unknown` arm carries arms this client's law version doesn't know).
29
+ final bool isVariant;
30
+
31
+ /// For `ref` kinds: the referenced aggregate's wire type, when the law declares it.
32
+ final String? refType;
33
+
34
+ const NomosFieldMeta({
35
+ required this.kind,
36
+ this.enumValues,
37
+ this.vo,
38
+ this.isVariant = false,
39
+ this.refType,
40
+ });
41
+ }
42
+
43
+ /// Runtime metadata for ONE aggregate, keyed by its projection wire type. [fromRow] is the
44
+ /// generated typed decoder (`<Agg>.fromJson`) — the same one every generated accessor uses,
45
+ /// surfaced here so a runtime-dispatched row still decodes to the REAL typed class.
46
+ class NomosAggregateMeta {
47
+ /// The projection wire type (`<domain>.<Aggregate>`).
48
+ final String type;
49
+
50
+ /// The aggregate's declared label (`Site`).
51
+ final String label;
52
+
53
+ /// Declared field name → its metadata.
54
+ final Map<String, NomosFieldMeta> fields;
55
+
56
+ /// The generated typed decoder (`Site.fromJson`) — returns the generated aggregate class
57
+ /// (typed as [Object] here; cast at the call site, or drive it generically via [fields]).
58
+ final Object Function(Map<String, dynamic> data, [String rowId]) fromRow;
59
+
60
+ const NomosAggregateMeta({
61
+ required this.type,
62
+ required this.label,
63
+ required this.fields,
64
+ required this.fromRow,
65
+ });
66
+
67
+ /// The AGGREGATE WIRE TYPE of a kernel aggregate id: minted ids are
68
+ /// `<domain>.<Type>_<uuidv7>`, so the type is the prefix before the trailing
69
+ /// `_<uuid>` (the same rule `@githolon/client` applies to Create events).
70
+ /// Returns the input unchanged when no uuid suffix is present (already a type).
71
+ static String typeOfId(String aggregateId) =>
72
+ aggregateId.replaceFirst(RegExp(r'_[0-9a-fA-F-]+$'), '');
73
+ }
74
+
75
+ /// Merge per-domain registries into one lookup (a package with several domains). Later maps
76
+ /// win on key collisions — collisions only occur when two domains declare the SAME wire type,
77
+ /// which the compiler already refuses.
78
+ Map<String, NomosAggregateMeta> mergeAggregateMeta(
79
+ Iterable<Map<String, NomosAggregateMeta>> registries) =>
80
+ {for (final r in registries) ...r};
package/dart/pubspec.yaml CHANGED
@@ -11,7 +11,7 @@ description: >-
11
11
  Nomos 2 Dart frontend types — the hand-written kernel wire types plus the
12
12
  code-generated per-domain aggregate + directive-payload types. (De)serializes
13
13
  the EXACT serde JSON the Rust kernel + projection produce/consume.
14
- version: 0.29.0
14
+ version: 0.30.0
15
15
 
16
16
  environment:
17
17
  sdk: ^3.5.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@githolon/dsl",
3
- "version": "0.40.0",
3
+ "version": "0.41.0",
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",
@@ -1536,6 +1536,60 @@ function emitUnionClasses(d: ValueObjectDescriptor): string {
1536
1536
  return [base, ...armClasses, unknown].join("\n\n");
1537
1537
  }
1538
1538
 
1539
+ /** THE RUNTIME AGGREGATE-META REGISTRY (dart_codegen_parity.md): one generated
1540
+ * `const Map<String, NomosAggregateMeta>` per domain, keyed by the projection wire type
1541
+ * (`<domain>.<Aggregate>`). Generic surfaces (history/audit UIs, op renderers, in-app agents)
1542
+ * resolve `op.aggregateType` → field kinds / enum wire values / VO+variant names / the typed
1543
+ * `fromJson` decoder — the compiler's knowledge, emitted as data, derived from the SAME
1544
+ * declarations the law carries (never hand-authored; never a second source of truth). */
1545
+ function emitAggregateMetaRegistry(
1546
+ domainKey: string,
1547
+ aggregates: readonly AggregateHandle[],
1548
+ ): string {
1549
+ if (aggregates.length === 0) return "";
1550
+ const entries = aggregates.map((agg) => {
1551
+ const wireType = `${domainKey}.${agg.id}`;
1552
+ const fieldLines = Object.entries(agg.fields as Record<string, Field>).map(([name, f]) => {
1553
+ const parts: string[] = [`kind: '${f.kind}'`];
1554
+ if (f.kind === "enum") {
1555
+ try {
1556
+ const values = zodEnumValues(f.zod as z.ZodTypeAny);
1557
+ parts.push(`enumValues: [${values.map((v) => `'${v}'`).join(", ")}]`);
1558
+ } catch { /* enum without introspectable values — kind alone still serves */ }
1559
+ }
1560
+ const vo = (f as { valueObject?: { name: string; descriptor?: { union?: unknown } } }).valueObject;
1561
+ if (vo !== undefined) {
1562
+ parts.push(`vo: '${vo.name}'`);
1563
+ if (vo.descriptor && (vo.descriptor as { union?: unknown }).union !== undefined) parts.push(`isVariant: true`);
1564
+ }
1565
+ const refTarget = (f as { refAggregateId?: string }).refAggregateId;
1566
+ if (f.kind === "ref" && refTarget !== undefined) {
1567
+ parts.push(`refType: '${domainKey}.${refTarget}'`);
1568
+ }
1569
+ return ` '${name}': NomosFieldMeta(${parts.join(", ")}),`;
1570
+ });
1571
+ return [
1572
+ ` '${wireType}': NomosAggregateMeta(`,
1573
+ ` type: '${wireType}',`,
1574
+ ` label: '${agg.id}',`,
1575
+ ` fields: {`,
1576
+ ...fieldLines,
1577
+ ` },`,
1578
+ ` fromRow: ${agg.id}.fromJson,`,
1579
+ ` ),`,
1580
+ ].join("\n");
1581
+ });
1582
+ const mapName = `${dartMemberName(domainKey)}AggregateMeta`;
1583
+ return [
1584
+ `/// RUNTIME lookup of this domain's aggregate shapes by projection wire type`,
1585
+ `/// (\`NomosAggregateMeta.typeOfId(op.aggregate)\` resolves an instance id to its key).`,
1586
+ `/// Derived from the law's own declarations — the compiler's knowledge as const data.`,
1587
+ `const Map<String, NomosAggregateMeta> ${mapName} = {`,
1588
+ ...entries,
1589
+ `};`,
1590
+ ].join("\n");
1591
+ }
1592
+
1539
1593
  function emitValueObjectClass(d: ValueObjectDescriptor): string {
1540
1594
  if (d.union !== undefined) return emitUnionClasses(d);
1541
1595
  if (d.geometry !== undefined) {
@@ -3212,6 +3266,8 @@ export function generateDartDomain(mod: DomainModule, opts: GenerateDartDomainOp
3212
3266
  ),
3213
3267
  )
3214
3268
  .join("\n\n");
3269
+ // the runtime aggregate-meta registry (lookup by op.aggregateType — generic surfaces).
3270
+ const aggregateMetaBlock = emitAggregateMetaRegistry(domainKey, localAggregates);
3215
3271
  const dirBlock = localDirectives
3216
3272
  .map((d) => emitDirective(d, domainKey, dartTypes))
3217
3273
  .join("\n\n");
@@ -3426,6 +3482,13 @@ export function generateDartDomain(mod: DomainModule, opts: GenerateDartDomainOp
3426
3482
  `// ---- aggregates (projection types) ----`,
3427
3483
  aggBlock,
3428
3484
  ``,
3485
+ ...(aggregateMetaBlock !== ""
3486
+ ? [
3487
+ `// ---- runtime aggregate metadata (lookup by op.aggregateType) ----`,
3488
+ aggregateMetaBlock,
3489
+ ``,
3490
+ ]
3491
+ : []),
3429
3492
  ...(readModelBlock !== ""
3430
3493
  ? [
3431
3494
  `// ---- generated read models (from aggregate DSL fields) ----`,
@@ -353,6 +353,9 @@ const DART_SUPPORT_SRC = [
353
353
  "offer.dart",
354
354
  "subscriptions.dart",
355
355
  "provider.dart",
356
+ // Runtime aggregate-meta support types (NomosAggregateMeta/NomosFieldMeta) — the generated
357
+ // per-domain registry consts reference them through the barrel.
358
+ "aggregate_meta.dart",
356
359
  // The async WebView bridge runtime (NomosBridge / BridgeTransport) the generated
357
360
  // `<name>.client.dart` drives — the barrel re-exports it, so it must be vendored too.
358
361
  "webview_bridge.dart",