@githolon/dsl 0.108.0 → 0.108.2

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 (95) hide show
  1. package/dart/CHANGELOG.md +5 -0
  2. package/dart/lib/src/application_framework_calls.dart +299 -0
  3. package/dart/lib/src/generated_call.dart +96 -0
  4. package/dart/lib/src/nms_generated_runtime.dart +29 -0
  5. package/dart/lib/src/node_runner_transport.dart +77 -1
  6. package/dart/lib/src/result_usda.dart +487 -0
  7. package/dart/lib/src/runner/node-entry.mjs +6401 -2102
  8. package/dart/lib/src/runner/offline-install.mjs +1538 -163
  9. package/dart/lib/src/runtime_v2_gateway.dart +54 -0
  10. package/dart/lib/src/runtime_v2_transport.dart +117 -0
  11. package/dart/lib/src/session_bound.dart +7 -2
  12. package/dart/lib/src/test_harness.dart +193 -112
  13. package/dart/lib/src/webview_bridge.dart +2120 -569
  14. package/dart/pubspec.yaml +2 -2
  15. package/dart/test/acquire_session_credentials_test.dart +1 -1
  16. package/dart/test/acquire_session_test.dart +68 -14
  17. package/dart/test/adv_realm_interference_test.dart +6 -4
  18. package/dart/test/cluster_births_test.dart +11 -8
  19. package/dart/test/cluster_local_autosync_test.dart +1 -1
  20. package/dart/test/cluster_scale_test.dart +3 -2
  21. package/dart/test/dlq_session_surface_test.dart +52 -35
  22. package/dart/test/first_app_gauntlet_test.dart +1 -1
  23. package/dart/test/local_sync_inert_test.dart +23 -26
  24. package/dart/test/offer_authentication_proof_test.dart +193 -0
  25. package/dart/test/result_usda_test.dart +49 -0
  26. package/dart/test/session_bound_test.dart +8 -11
  27. package/dart/test/stale_bundle_test.dart +18 -3
  28. package/dart/test/test_harness_test.dart +14 -27
  29. package/dart/test/webview_bridge_test.dart +504 -83
  30. package/dart_types/CHANGELOG.md +7 -0
  31. package/dart_types/lib/nomos_kernel_protocol.dart +3 -2
  32. package/dart_types/lib/src/custody.dart +64 -19
  33. package/dart_types/lib/src/local_work_rejected.dart +30 -45
  34. package/dart_types/lib/src/proto/nomos/custody/v1/snapshot.pb.dart +169 -0
  35. package/dart_types/lib/src/proto/nomos/custody/v1/snapshot.pbenum.dart +11 -0
  36. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +2021 -234
  37. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +175 -8
  38. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +8664 -0
  39. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pbenum.dart +369 -0
  40. package/dart_types/lib/src/surfaced.dart +30 -25
  41. package/dart_types/pubspec.yaml +1 -1
  42. package/dart_types/test/surfaced_test.dart +2 -1
  43. package/package.json +1 -1
  44. package/runtime/generated-application-host.js +5736 -2047
  45. package/src/authoring.ts +3 -2
  46. package/src/births.ts +21 -0
  47. package/src/build_package.ts +30 -0
  48. package/src/business_birth.ts +39 -35
  49. package/src/codegen_call_usda.ts +1111 -0
  50. package/src/codegen_dart.ts +207 -282
  51. package/src/codegen_dart_application.ts +195 -252
  52. package/src/codegen_dart_client.ts +1978 -593
  53. package/src/codegen_dart_framework_calls.ts +291 -0
  54. package/src/codegen_dart_scenario.ts +7 -0
  55. package/src/codegen_proof.ts +3 -4
  56. package/src/codegen_provider_dart.ts +21 -7
  57. package/src/codegen_query_usda.ts +214 -0
  58. package/src/codegen_result_usda.ts +685 -0
  59. package/src/codegen_ts.ts +3945 -1111
  60. package/src/compile_package_main.ts +360 -32
  61. package/src/directive.ts +19 -4
  62. package/src/engine_entry.ts +45 -4
  63. package/src/framework/application_home.ts +2 -0
  64. package/src/framework/auth_provider_law.ts +262 -20
  65. package/src/framework/home.ts +32 -28
  66. package/src/framework/owned_custody.ts +19 -11
  67. package/src/framework/owned_custody_value.ts +51 -0
  68. package/src/framework/relations.ts +57 -21
  69. package/src/framework/signing_identity.ts +34 -0
  70. package/src/framework/workspaces.ts +31 -9
  71. package/src/framework_call_definitions.ts +53 -0
  72. package/src/framework_read_definitions.ts +45 -0
  73. package/src/identity_frontier.ts +58 -0
  74. package/src/index.ts +20 -1
  75. package/src/intent.ts +216 -16
  76. package/src/law_check.ts +19 -1
  77. package/src/law_graph.ts +57 -8
  78. package/src/law_graph_usda.ts +73 -5
  79. package/src/law_value_type.ts +106 -19
  80. package/src/read.ts +47 -0
  81. package/src/read_projection_namespace.ts +7 -0
  82. package/src/shape_compat.ts +38 -39
  83. package/src/stable_ids.ts +37 -2
  84. package/src/stable_ids_for_module.ts +64 -4
  85. package/src/test_call_usda.ts +83 -0
  86. package/src/wire.ts +36 -2
  87. package/src/wire_encode.ts +45 -1
  88. package/src/workspace_sharing.ts +27 -10
  89. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbjson.dart +0 -6096
  90. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbserver.dart +0 -50
  91. package/dart_types/lib/src/proto/nomos/kernel/v1/progress.pbjson.dart +0 -178
  92. package/dart_types/lib/src/proto/nomos/runtime/v1/runtime.pb.dart +0 -828
  93. package/dart_types/lib/src/proto/nomos/runtime/v1/runtime.pbenum.dart +0 -465
  94. package/dart_types/lib/src/proto/nomos/runtime/v1/runtime.pbjson.dart +0 -573
  95. package/dart_types/lib/src/proto/nomos/runtime/v1/runtime.pbserver.dart +0 -50
@@ -0,0 +1,291 @@
1
+ import type { z } from "zod";
2
+
3
+ import type { Directive } from "./directive.js";
4
+ import type { DomainModule } from "./codegen_dart.js";
5
+ import { dartMemberName, introspectPayload } from "./codegen_dart.js";
6
+ import type { PayloadFieldSpec } from "./codegen_dart.js";
7
+ import {
8
+ DART_CALL_USDA_PRELUDE,
9
+ emitDartCallUsdaEncoder,
10
+ type DartCallUsdaFieldAccess,
11
+ } from "./codegen_call_usda.js";
12
+ import type { LawGraph } from "./law_graph.js";
13
+ import { fixedFrameworkCallDefinitions } from "./framework_call_definitions.js";
14
+ import { APPLICATION_HOME_DOMAIN } from "./framework/application_home_domain.js";
15
+ import { workspaceSharingInvitationRedeemPayload } from "./workspace_sharing.js";
16
+ import { zodArrayElement, zodDef, zodObjectShape } from "./zod_introspection.js";
17
+
18
+ type AnyDirective = Directive<unknown>;
19
+
20
+ const frameworkFieldAccess = (field: PayloadFieldSpec): DartCallUsdaFieldAccess => {
21
+ const member = `payload.${dartMemberName(field.name)}`;
22
+ if (field.nullAdmits === true) return { value: member, slot: true };
23
+ const value = field.optional ? `${member}!` : member;
24
+ const present = field.optional ? { present: `${member} != null` } : {};
25
+ switch (field.shape) {
26
+ case "scalar":
27
+ case "enum":
28
+ case "strList":
29
+ case "wrappedList":
30
+ case "refList":
31
+ case "record":
32
+ case "recordList":
33
+ case "stringMap":
34
+ case "vo":
35
+ case "voList":
36
+ return { value, ...present };
37
+ case "wrapped":
38
+ return { value: `${value}.value`, ...present };
39
+ default:
40
+ throw new Error(
41
+ `Dart framework call codegen: fixed field '${field.name}' uses unsupported '${field.shape}' input`,
42
+ );
43
+ }
44
+ };
45
+
46
+ const FIXED_CALLS = fixedFrameworkCallDefinitions
47
+ .filter((definition) => definition.dartBirth === true)
48
+ .map((definition) => ({ kind: definition.kind, directive: definition.directive.id }));
49
+
50
+ const certificateArgumentType = (field: PayloadFieldSpec): string | undefined =>
51
+ (field.name === "certificate" || field.name === "delegationCertificate") && field.shape === "record"
52
+ ? `BirthCertificate${field.optional ? "?" : ""}`
53
+ : field.name === "links" && field.shape === "recordList"
54
+ ? "List<BirthCertificate>"
55
+ : undefined;
56
+
57
+ const certificatePayloadExpression = (field: PayloadFieldSpec): string | undefined =>
58
+ (field.name === "certificate" || field.name === "delegationCertificate") && field.shape === "record"
59
+ ? field.optional
60
+ ? `${dartMemberName(field.name)} == null ? null : _nmsBirthCertificateLawValue(${dartMemberName(field.name)})`
61
+ : `_nmsBirthCertificateLawValue(${dartMemberName(field.name)})`
62
+ : field.name === "links" && field.shape === "recordList"
63
+ ? "links.map(_nmsBirthCertificateLawValue).toList(growable: false)"
64
+ : undefined;
65
+
66
+ const birthCertificateMemberExpression = (name: string): string => {
67
+ switch (name) {
68
+ case "child":
69
+ case "founder":
70
+ case "verified":
71
+ case "parentId":
72
+ return `certificate.${name}`;
73
+ case "parentKey":
74
+ case "signature":
75
+ return `base64Encode(certificate.${name})`;
76
+ case "epoch":
77
+ return "certificate.epoch.toInt()";
78
+ case "verifiedVia":
79
+ case "idpToken":
80
+ case "authProviderIssuer":
81
+ case "authProviderJwks":
82
+ case "authProviderAudience": {
83
+ const cap = name[0]!.toUpperCase() + name.slice(1);
84
+ return `certificate.has${cap}() ? certificate.${name} : null`;
85
+ }
86
+ case "capturedClock":
87
+ return "certificate.hasCapturedClock() ? certificate.capturedClock.toInt() : null";
88
+ default:
89
+ throw new Error(
90
+ `Dart framework call codegen: BirthCertificate law field '${name}' has no generated Protobuf binding`,
91
+ );
92
+ }
93
+ };
94
+
95
+ export interface DartFrameworkCallLaw {
96
+ readonly module: DomainModule;
97
+ readonly graph: LawGraph;
98
+ readonly paths: ReadonlyMap<string, string>;
99
+ }
100
+
101
+ /** Emit the pre-bind framework call factory from the exact composed law graph.
102
+ * The output contains no schemas or dynamic values: every field access and USDA
103
+ * relationship is expanded by the compiler. */
104
+ export function generateDartApplicationFrameworkCalls(
105
+ laws: readonly DartFrameworkCallLaw[],
106
+ ): string | undefined {
107
+ const fixedEmitted = FIXED_CALLS.flatMap((definition) => laws.flatMap((law) => {
108
+ const declares = law.graph.nodes.some((node) =>
109
+ node.kind === "intent" &&
110
+ (node.entity.label === definition.directive || node.publicRoute === definition.directive));
111
+ if (!declares) return [];
112
+ const directive = (law.module.directives as AnyDirective[]).find((candidate) =>
113
+ candidate.id === definition.directive);
114
+ if (directive === undefined) {
115
+ throw new Error(
116
+ `Dart framework call codegen: '${law.graph.domain.entity.label}/${definition.directive}' has no authored directive`,
117
+ );
118
+ }
119
+ const fields = introspectPayload(
120
+ directive.payloadSchema as z.ZodTypeAny,
121
+ new Map(),
122
+ );
123
+ const parameterType = (field: (typeof fields)[number]) => field.nullAdmits === true
124
+ ? `NomosSlot<${field.dartType}>`
125
+ : `${field.dartType}${field.optional ? "?" : ""}`;
126
+ const payloadType = `({${fields.map((field) =>
127
+ `${parameterType(field)} ${dartMemberName(field.name)}`).join(", ")}})`;
128
+ const access = new Map<string, DartCallUsdaFieldAccess>(fields.map((field) => [
129
+ field.name,
130
+ frameworkFieldAccess(field),
131
+ ]));
132
+ const encoder = emitDartCallUsdaEncoder(
133
+ law.graph,
134
+ law.paths,
135
+ law.graph.domain.entity.label,
136
+ definition.directive,
137
+ payloadType,
138
+ access,
139
+ );
140
+ return [{ ...definition, domain: law.graph.domain.entity.label, fields, payloadType, encoder, routeByDirective: false as const }];
141
+ }));
142
+ const invitationEmitted = laws.flatMap((law) =>
143
+ (law.module.applicationWorkspaceSharing ?? []).map((policy) => {
144
+ const directive = policy.invitationLaw.redeem as AnyDirective;
145
+ const fields = introspectPayload(directive.payloadSchema as z.ZodTypeAny, new Map());
146
+ const parameterType = (field: (typeof fields)[number]) => field.nullAdmits === true
147
+ ? `NomosSlot<${field.dartType}>`
148
+ : `${field.dartType}${field.optional ? "?" : ""}`;
149
+ const payloadType = `({${fields.map((field) =>
150
+ `${parameterType(field)} ${dartMemberName(field.name)}`).join(", ")}})`;
151
+ const access = new Map<string, DartCallUsdaFieldAccess>(fields.map((field) => [
152
+ field.name,
153
+ frameworkFieldAccess(field),
154
+ ]));
155
+ const encoder = emitDartCallUsdaEncoder(
156
+ law.graph,
157
+ law.paths,
158
+ law.graph.domain.entity.label,
159
+ directive.id,
160
+ payloadType,
161
+ access,
162
+ );
163
+ return {
164
+ kind: "redeemInvitation",
165
+ directive: directive.id,
166
+ domain: law.graph.domain.entity.label,
167
+ fields,
168
+ payloadType,
169
+ encoder,
170
+ routeByDirective: true as const,
171
+ };
172
+ }));
173
+ const emitted = [...fixedEmitted, ...invitationEmitted];
174
+ if (emitted.length === 0) return undefined;
175
+
176
+ const certificateEntry = emitted.find((entry) =>
177
+ entry.fields.some((field) => certificateArgumentType(field) !== undefined));
178
+ const certificateField = certificateEntry?.fields
179
+ .find((field) => certificateArgumentType(field) !== undefined);
180
+ const certificateRecordType = certificateField === undefined
181
+ ? undefined
182
+ : certificateField.shape === "record"
183
+ ? certificateField.dartType
184
+ : /^List<(.+)>$/.exec(certificateField.dartType)?.[1];
185
+ const certificateRecordFields = certificateRecordType === undefined
186
+ ? []
187
+ : (() => {
188
+ if (certificateEntry === undefined || certificateField === undefined) {
189
+ throw new Error("Dart framework call codegen: cannot locate the BirthCertificate law record");
190
+ }
191
+ const directive = (laws.find((law) => law.graph.domain.entity.label === certificateEntry.domain)?.module.directives as AnyDirective[] | undefined)
192
+ ?.find((candidate) => candidate.id === certificateEntry.directive);
193
+ if (directive === undefined) {
194
+ throw new Error("Dart framework call codegen: cannot locate the BirthCertificate directive");
195
+ }
196
+ let schema = zodObjectShape(directive.payloadSchema as z.ZodTypeAny)[certificateField.name];
197
+ if (schema === undefined) {
198
+ throw new Error("Dart framework call codegen: cannot locate the BirthCertificate payload field");
199
+ }
200
+ while (zodDef(schema).innerType !== undefined) schema = zodDef(schema).innerType!;
201
+ if (certificateField.shape === "recordList") schema = zodArrayElement(schema);
202
+ return introspectPayload(schema, new Map());
203
+ })();
204
+
205
+ const invitationContractFields = introspectPayload(
206
+ workspaceSharingInvitationRedeemPayload,
207
+ new Map(),
208
+ );
209
+ const fixedMethodFields = new Map(
210
+ fixedFrameworkCallDefinitions
211
+ .filter((definition) => definition.dartBirth === true)
212
+ .map((definition) => [
213
+ definition.kind,
214
+ introspectPayload(definition.directive.payloadSchema as z.ZodTypeAny, new Map()),
215
+ ] as const),
216
+ );
217
+ // The runtime interface is deliberately exhaustive. A composed application implements every
218
+ // method even when its law has no matching route; that method then fails locally at the typed
219
+ // switch instead of making the generated class abstract or inventing a dynamic fallback.
220
+ const methods = [...new Set([
221
+ ...fixedFrameworkCallDefinitions
222
+ .filter((definition) => definition.dartBirth === true)
223
+ .map((definition) => definition.kind),
224
+ "redeemInvitation",
225
+ ])].flatMap((kind) => {
226
+ const alternatives = emitted.filter((entry) => entry.kind === kind);
227
+ const fields = alternatives[0]?.fields ?? fixedMethodFields.get(kind) ?? invitationContractFields;
228
+ const routeByDirective = kind === "redeemInvitation" || alternatives.some((entry) => entry.routeByDirective);
229
+ const parameterType = (field: (typeof fields)[number]) => field.nullAdmits === true
230
+ ? `NomosSlot<${field.dartType}>`
231
+ : `${field.dartType}${field.optional ? "?" : ""}`;
232
+ const signature = fields.map((field) =>
233
+ ` ${field.optional ? "" : "required "}${certificateArgumentType(field) ?? parameterType(field)} ${dartMemberName(field.name)},`);
234
+ const payload = fields.map((field) =>
235
+ ` ${dartMemberName(field.name)}: ${certificatePayloadExpression(field) ?? dartMemberName(field.name)},`);
236
+ return [
237
+ ` @override`,
238
+ ` NmsGeneratedCall ${kind}({`,
239
+ ` required String domain,`,
240
+ ` required String workspace,`,
241
+ ...(routeByDirective ? [` required String directive,`] : []),
242
+ ...signature,
243
+ ` }) {`,
244
+ ` final payload = (`,
245
+ ...payload,
246
+ ` );`,
247
+ ` return switch (${routeByDirective ? "'$domain/$directive'" : "domain"}) {`,
248
+ ...alternatives.map((entry) =>
249
+ ` ${JSON.stringify(routeByDirective ? `${entry.domain}/${entry.directive}` : entry.domain)} => ${entry.encoder.functionName}(payload),`),
250
+ ` _ => throw StateError("The compiled application has no '${kind}' call in domain '$domain'."),`,
251
+ ` };`,
252
+ ` }`,
253
+ ``,
254
+ ];
255
+ });
256
+
257
+ return [
258
+ `// AUTO-GENERATED by nomos-compile — pre-bind framework calls for this exact application law.`,
259
+ `// No host authority or payload schema lives here. Do not edit.`,
260
+ `// ignore_for_file: invalid_use_of_internal_member, unused_element, unused_import, unused_shown_name`,
261
+ `import 'dart:convert' show base64Encode, utf8;`,
262
+ `import 'dart:typed_data' show Uint8List;`,
263
+ `import 'package:nomos_types/nomos_kernel_protocol.dart' show BirthCertificate;`,
264
+ `import 'package:nomos_types/nomos_types.dart' show NomosSlot, Sha256Digest;`,
265
+ `import 'package:nomos_client/src/application_framework_calls.dart';`,
266
+ `import 'package:nomos_client/src/generated_call.dart';`,
267
+ ``,
268
+ ...DART_CALL_USDA_PRELUDE,
269
+ ...(certificateRecordType === undefined
270
+ ? []
271
+ : [
272
+ `${certificateRecordType} _nmsBirthCertificateLawValue(BirthCertificate certificate) => (`,
273
+ ...certificateRecordFields.map((field) =>
274
+ ` ${dartMemberName(field.name)}: ${birthCertificateMemberExpression(field.name)},`),
275
+ `);`,
276
+ ``,
277
+ ]),
278
+ ...emitted.flatMap((entry) => entry.encoder.source),
279
+ `final class _NmsCompiledApplicationFrameworkCalls implements NmsApplicationFrameworkCalls {`,
280
+ ` const _NmsCompiledApplicationFrameworkCalls();`,
281
+ ` @override`,
282
+ ` String get applicationHomeDomain => ${JSON.stringify(APPLICATION_HOME_DOMAIN)};`,
283
+ ``,
284
+ ...methods,
285
+ `}`,
286
+ ``,
287
+ `const NmsApplicationFrameworkCalls nomosInternalApplicationFrameworkCalls =`,
288
+ ` _NmsCompiledApplicationFrameworkCalls();`,
289
+ ``,
290
+ ].join("\n");
291
+ }
@@ -91,6 +91,7 @@ export function generateDartScenario(
91
91
  readonly applicationFile?: string;
92
92
  readonly applicationModules?: readonly DomainModule[];
93
93
  readonly applicationMaxChildWorkspacesPerUser?: number;
94
+ readonly applicationFrameworkCallsFile?: string;
94
95
  } = {},
95
96
  ): string {
96
97
  const domain = options.surfaceName ?? mod.domain ?? mod.name;
@@ -116,6 +117,9 @@ export function generateDartScenario(
116
117
  `import 'package:flutter/widgets.dart' show WidgetsFlutterBinding;`,
117
118
  `import 'package:${clientPackage}/${clientPackage}.dart' show ${application};`,
118
119
  `import 'package:${clientPackage}/src/generated/${applicationFile}.dart' show ${bind};`,
120
+ ...(options.applicationFrameworkCallsFile === undefined
121
+ ? []
122
+ : [`import 'package:${clientPackage}/src/generated/${options.applicationFrameworkCallsFile}.dart' show nomosInternalApplicationFrameworkCalls;`]),
119
123
  `import 'package:nomos_flutter/src/generated_application_host.dart' show NmsInternalApplicationUserBinding;`,
120
124
  `import 'package:nomos_client/src/home.dart' show nmsInternalReceivingGroupNameOf;`,
121
125
  `import 'package:nomos_client/src/nms_test_environment.dart' show NomosTestCloud, NomosTestHarness;`,
@@ -196,6 +200,9 @@ export function generateDartScenario(
196
200
  ` final packageFiles = await _applicationPackages();`,
197
201
  ` final environment = await NomosTestHarness.ephemeral(`,
198
202
  ` lawUsdzPaths: packageFiles.map((file) => file.path).toList(growable: false),`,
203
+ ...(options.applicationFrameworkCallsFile === undefined
204
+ ? []
205
+ : [` applicationFrameworkCalls: nomosInternalApplicationFrameworkCalls,`]),
199
206
  ...(options.applicationMaxChildWorkspacesPerUser === undefined
200
207
  ? []
201
208
  : [` applicationMaxChildWorkspacesPerUser: ${options.applicationMaxChildWorkspacesPerUser},`]),
@@ -2228,10 +2228,9 @@ export function discoverChildBirthLegOrReason(
2228
2228
  businessBirth?: { aggregateId?: string; publicPayloadSchema?: z.ZodTypeAny };
2229
2229
  }).businessBirth;
2230
2230
  if (businessBirth !== undefined) {
2231
- // The real generated client fills this opaque carrier with locally sealed genesis facts. Discovery only
2232
- // needs the recipe's shape, so an empty JSON object preserves the synthesized plaintext facts while
2233
- // exercising the same parsing branch without inventing a key inside the compiler.
2234
- probePayload[BUSINESS_BIRTH_SEED_PAYLOAD_FIELD] = "{}";
2231
+ // The real generated client fills this typed carrier with locally sealed genesis facts. Discovery
2232
+ // preserves the synthesized facts while avoiding any parallel string serialization boundary.
2233
+ probePayload[BUSINESS_BIRTH_SEED_PAYLOAD_FIELD] = { ...probePayload };
2235
2234
  }
2236
2235
  try {
2237
2236
  withBirthProbeGlobals(() => {
@@ -56,7 +56,6 @@ import { z } from "zod";
56
56
  import {
57
57
  cap,
58
58
  introspectPayload,
59
- toJsonExpr,
60
59
  type PayloadFieldSpec,
61
60
  } from "./codegen_dart.js";
62
61
 
@@ -163,7 +162,14 @@ function obligationCtorParam(s: PayloadFieldSpec): string {
163
162
  */
164
163
  function obligationFromJsonExpr(s: PayloadFieldSpec): string {
165
164
  const raw = `j['${s.name}']`;
166
- switch (s.toJson) {
165
+ switch (s.shape) {
166
+ case "record":
167
+ case "recordList":
168
+ case "stringMap":
169
+ throw new Error(
170
+ `provider Dart codegen: obligation field '${s.name}' is a typed composite; ` +
171
+ "generate its protobuf provider contract instead of decoding it from JSON",
172
+ );
167
173
  case "scalar": {
168
174
  // int reads tolerate a JSON number that decodes as double/int; cast via `as num`.
169
175
  if (s.dartType === "int") {
@@ -189,10 +195,19 @@ function obligationFromJsonExpr(s: PayloadFieldSpec): string {
189
195
  ? `(${raw} as List<Object?>?)?.cast<String>()`
190
196
  : `(${raw} as List<Object?>).cast<String>()`;
191
197
  }
192
- case "json": {
193
- // A nested object → `Map<String,Object?>`; a `List<Object?>` payload (rare) stays
194
- // a list. The Dart type drives the cast.
195
- return s.optional ? `${raw} as ${s.dartType}?` : `${raw} as ${s.dartType}`;
198
+ case "wrappedList": {
199
+ const elem = s.dartType.replace(/^List<(.*)>$/, "$1");
200
+ const wrapped = s.wrappedScalarKind === "number"
201
+ ? `${elem}((e as num).toDouble())`
202
+ : `${elem}(e as String)`;
203
+ const decode = `(${raw} as List).map((e) => ${wrapped}).toList(growable: false)`;
204
+ return s.optional ? `${raw} == null ? null : ${decode}` : decode;
205
+ }
206
+ case "refList": {
207
+ const target = s.dartType.replace(/^List<NomosRef<(.*)>>$/, "$1");
208
+ const wireType = s.referenceType ?? target;
209
+ const decode = `(${raw} as List).map((e) => NmsInternalNomosRefs.create<${target}>(workspace: '', type: '${wireType}', id: e as String)).toList(growable: false)`;
210
+ return s.optional ? `${raw} == null ? null : ${decode}` : decode;
196
211
  }
197
212
  case "vo": {
198
213
  // A declared value object — decode through the generated class (typed both ways).
@@ -473,6 +488,5 @@ export function emitProviderSdk(
473
488
  enumDartTypes: Map<string, string>,
474
489
  ): string {
475
490
  if (tasks.length === 0) return "";
476
- void toJsonExpr; // symmetry marker (see doc); the encode reuses the peer payload classes.
477
491
  return tasks.map((t) => emitProviderTask(t, enumDartTypes)).join("\n\n");
478
492
  }
@@ -0,0 +1,214 @@
1
+ import type { LawGraph, LawQueryReadNode } from "./law_graph.js";
2
+
3
+ export type QueryScalarKind = "string" | "reference" | "token" | "integer" | "boolean";
4
+
5
+ export interface DartQueryUsdaParameter {
6
+ readonly authoredPath: string;
7
+ readonly name: string;
8
+ readonly type: "String" | "int" | "bool";
9
+ readonly value: string;
10
+ readonly kind: QueryScalarKind;
11
+ }
12
+
13
+ export interface DartQueryUsdaEncoder {
14
+ readonly functionName: string;
15
+ readonly call: string;
16
+ readonly source: readonly string[];
17
+ }
18
+
19
+ export interface TsQueryUsdaParameter {
20
+ readonly authoredPath: string;
21
+ readonly name: string;
22
+ readonly type: "string" | "number" | "boolean";
23
+ readonly value: string;
24
+ readonly kind: QueryScalarKind;
25
+ }
26
+
27
+ export interface TsQueryUsdaEncoder {
28
+ readonly functionName: string;
29
+ readonly call: string;
30
+ readonly source: readonly string[];
31
+ }
32
+
33
+ const symbol = (value: string): string =>
34
+ value.replace(/[^A-Za-z0-9_$]+/g, "_").replace(/^[^A-Za-z_$]/, "_$&");
35
+
36
+ const pathLeaf = (path: string): string => path.slice(path.lastIndexOf("/") + 1);
37
+
38
+ const dartString = (value: string): string => JSON.stringify(value);
39
+
40
+ /**
41
+ * Emit one query-specific USDA encoder from the same composed graph that emitted law.usda.
42
+ *
43
+ * There is intentionally no schema interpreter or generic value bag in the generated runtime:
44
+ * each parameter has one Dart type, one OpenUSD value type and exact query/field relationships.
45
+ */
46
+ export function emitDartQueryUsdaEncoder(
47
+ graph: LawGraph,
48
+ paths: ReadonlyMap<string, string>,
49
+ domain: string,
50
+ queryId: string,
51
+ parameters: readonly DartQueryUsdaParameter[],
52
+ ): DartQueryUsdaEncoder {
53
+ const query = graph.nodes.find(
54
+ (node): node is LawQueryReadNode =>
55
+ node.kind === "read" && node.readKind === "query" && node.entity.label === queryId,
56
+ );
57
+ if (query === undefined) {
58
+ throw new Error(`query USDA codegen: domain '${domain}' has no law query '${queryId}'`);
59
+ }
60
+ const queryPath = paths.get(query.entity.id);
61
+ if (queryPath === undefined) {
62
+ throw new Error(`query USDA codegen: '${domain}/${queryId}' has no rendered law path`);
63
+ }
64
+ if (query.key.length !== parameters.length) {
65
+ throw new Error(
66
+ `query USDA codegen: '${domain}/${queryId}' has ${query.key.length} law keys but ${parameters.length} generated parameters`,
67
+ );
68
+ }
69
+ const functionName = `_nmsQueryUsda_${symbol(domain)}_${symbol(queryId)}`;
70
+ const signature = parameters.map((parameter) => `${parameter.type} ${parameter.name}`).join(", ");
71
+ const lines = [
72
+ `NmsGeneratedQuery ${functionName}(${signature}) {`,
73
+ ` final out = <String>[`,
74
+ ` '#usda 1.0',`,
75
+ ` '(',`,
76
+ ` ' defaultPrim = "Query"',`,
77
+ ` ')',`,
78
+ ` '',`,
79
+ ` 'def NomosQuery "Query"',`,
80
+ ` '{',`,
81
+ ` ' custom rel nomos:query = <${queryPath}>',`,
82
+ ` '',`,
83
+ ` ' def NomosQueryArguments "Arguments"',`,
84
+ ` ' {',`,
85
+ ` ];`,
86
+ ];
87
+ for (let index = 0; index < parameters.length; index++) {
88
+ const parameter = parameters[index]!;
89
+ const key = query.key[index]!;
90
+ if (key.authoredPath !== parameter.authoredPath) {
91
+ throw new Error(
92
+ `query USDA codegen: '${domain}/${queryId}' key ${index} is '${key.authoredPath}', not '${parameter.authoredPath}'`,
93
+ );
94
+ }
95
+ const fieldPath = paths.get(key.field.id);
96
+ if (fieldPath === undefined) {
97
+ throw new Error(
98
+ `query USDA codegen: '${domain}/${queryId}.${parameter.authoredPath}' has no rendered field path`,
99
+ );
100
+ }
101
+ const valuePath = `/Query/Arguments/${pathLeaf(fieldPath)}`;
102
+ const [primType, declaredType, expression] = (() => {
103
+ switch (parameter.kind) {
104
+ case "string":
105
+ return ["NomosStringValue", "string", `_nmsUsdaQuote(${parameter.name})`] as const;
106
+ case "reference":
107
+ return ["NomosReferenceValue", "string", `_nmsUsdaQuote(${parameter.name})`] as const;
108
+ case "token":
109
+ return ["NomosTokenValue", "token", `_nmsUsdaQuote(${parameter.name})`] as const;
110
+ case "integer":
111
+ return ["NomosIntValue", "int64", `_nmsUsdaInt(${parameter.name})`] as const;
112
+ case "boolean":
113
+ return ["NomosBoolValue", "bool", `${parameter.name} ? 'true' : 'false'`] as const;
114
+ }
115
+ })();
116
+ lines.push(
117
+ ` _nmsUsdaBegin(out, 2, ${dartString(primType)}, ${dartString(valuePath)});`,
118
+ ` _nmsUsdaRel(out, 3, 'nomos:field', ${dartString(fieldPath)});`,
119
+ ` _nmsUsdaLine(out, 3, 'custom uniform string[] nomos:keyPath = [' + _nmsUsdaQuote(${dartString(parameter.authoredPath)}) + ']');`,
120
+ ` _nmsUsdaLine(out, 3, 'custom uniform ${declaredType} nomos:value = ' + ${expression});`,
121
+ ` _nmsUsdaClose(out, 2);`,
122
+ );
123
+ }
124
+ lines.push(
125
+ ` _nmsUsdaClose(out, 1);`,
126
+ ` _nmsUsdaClose(out, 0);`,
127
+ ` return NmsInternalGeneratedQueries.create(`,
128
+ ` domain: ${dartString(domain)},`,
129
+ ` query: ${dartString(queryId)},`,
130
+ ` canonicalArgumentsUsda: Uint8List.fromList(utf8.encode('\${out.join('\\n')}\\n')),`,
131
+ ` );`,
132
+ `}`,
133
+ ``,
134
+ );
135
+ return {
136
+ functionName,
137
+ call: `${functionName}(${parameters.map((parameter) => parameter.value).join(", ")})`,
138
+ source: lines,
139
+ };
140
+ }
141
+
142
+ export function emitTsQueryUsdaEncoder(
143
+ graph: LawGraph,
144
+ paths: ReadonlyMap<string, string>,
145
+ domain: string,
146
+ queryId: string,
147
+ parameters: readonly TsQueryUsdaParameter[],
148
+ ): TsQueryUsdaEncoder {
149
+ const query = graph.nodes.find(
150
+ (node): node is LawQueryReadNode =>
151
+ node.kind === "read" && node.readKind === "query" && node.entity.label === queryId,
152
+ );
153
+ if (query === undefined) throw new Error(`query USDA codegen: domain '${domain}' has no law query '${queryId}'`);
154
+ const queryPath = paths.get(query.entity.id);
155
+ if (queryPath === undefined) throw new Error(`query USDA codegen: '${domain}/${queryId}' has no rendered law path`);
156
+ if (query.key.length !== parameters.length) {
157
+ throw new Error(`query USDA codegen: '${domain}/${queryId}' key/parameter length differs`);
158
+ }
159
+ const functionName = `_nmsQueryUsda_${symbol(domain)}_${symbol(queryId)}`;
160
+ const lines = [
161
+ `function ${functionName}(${parameters.map((parameter) => `${parameter.name}: ${parameter.type}`).join(", ")}): NmsGeneratedQuery<${JSON.stringify(domain)}, ${JSON.stringify(queryId)}> {`,
162
+ ` const out: string[] = [`,
163
+ ` "#usda 1.0",`,
164
+ ` "(",`,
165
+ ` ' defaultPrim = "Query"',`,
166
+ ` ")",`,
167
+ ` "",`,
168
+ ` 'def NomosQuery "Query"',`,
169
+ ` "{",`,
170
+ ` " custom rel nomos:query = <${queryPath}>",`,
171
+ ` "",`,
172
+ ` ' def NomosQueryArguments "Arguments"',`,
173
+ ` " {",`,
174
+ ` ];`,
175
+ ];
176
+ for (let index = 0; index < parameters.length; index++) {
177
+ const parameter = parameters[index]!;
178
+ const key = query.key[index]!;
179
+ if (key.authoredPath !== parameter.authoredPath) {
180
+ throw new Error(`query USDA codegen: '${domain}/${queryId}' key ${index} identity differs`);
181
+ }
182
+ const fieldPath = paths.get(key.field.id);
183
+ if (fieldPath === undefined) throw new Error(`query USDA codegen: '${domain}/${queryId}.${parameter.authoredPath}' has no field path`);
184
+ const valuePath = `/Query/Arguments/${pathLeaf(fieldPath)}`;
185
+ const [primType, declaredType, expression] = (() => {
186
+ switch (parameter.kind) {
187
+ case "string": return ["NomosStringValue", "string", `_nmsUsdaQuote(${parameter.name})`] as const;
188
+ case "reference": return ["NomosReferenceValue", "string", `_nmsUsdaQuote(${parameter.name})`] as const;
189
+ case "token": return ["NomosTokenValue", "token", `_nmsUsdaQuote(${parameter.name})`] as const;
190
+ case "integer": return ["NomosIntValue", "int64", `_nmsUsdaInt(${parameter.name})`] as const;
191
+ case "boolean": return ["NomosBoolValue", "bool", `${parameter.name} ? "true" : "false"`] as const;
192
+ }
193
+ })();
194
+ lines.push(
195
+ ` _nmsUsdaBegin(out, 2, ${JSON.stringify(primType)}, ${JSON.stringify(valuePath)});`,
196
+ ` _nmsUsdaRel(out, 3, "nomos:field", ${JSON.stringify(fieldPath)});`,
197
+ ` _nmsUsdaLine(out, 3, "custom uniform string[] nomos:keyPath = [" + _nmsUsdaQuote(${JSON.stringify(parameter.authoredPath)}) + "]");`,
198
+ ` _nmsUsdaLine(out, 3, "custom uniform ${declaredType} nomos:value = " + ${expression});`,
199
+ ` _nmsUsdaClose(out, 2);`,
200
+ );
201
+ }
202
+ lines.push(
203
+ ` _nmsUsdaClose(out, 1);`,
204
+ ` _nmsUsdaClose(out, 0);`,
205
+ ` return _nmsGeneratedQuery(${JSON.stringify(domain)}, ${JSON.stringify(queryId)}, _nmsUsdaUtf8(out.join("\\n") + "\\n"));`,
206
+ `}`,
207
+ ``,
208
+ );
209
+ return {
210
+ functionName,
211
+ call: `${functionName}(${parameters.map((parameter) => parameter.value).join(", ")})`,
212
+ source: lines,
213
+ };
214
+ }