@githolon/dsl 0.101.6 → 0.102.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.
Files changed (120) hide show
  1. package/dart/CHANGELOG.md +13 -0
  2. package/dart/lib/nomos_client.dart +3 -52
  3. package/dart/lib/src/dev_cloud.dart +5 -0
  4. package/dart/lib/src/home.dart +10 -15
  5. package/dart/lib/src/nms_conformance_runtime.dart +12 -0
  6. package/dart/lib/src/nms_generated_runtime.dart +29 -0
  7. package/dart/lib/src/nms_test_environment.dart +8 -0
  8. package/dart/lib/src/node_runner_transport.dart +56 -18
  9. package/dart/lib/src/provider.dart +5 -1
  10. package/dart/lib/src/record_origin.dart +55 -0
  11. package/dart/lib/src/runner/node-entry.mjs +9105 -0
  12. package/dart/lib/src/runner/offline-install.mjs +4729 -0
  13. package/dart/lib/src/session_bound.dart +23 -10
  14. package/dart/lib/src/source_trust.dart +51 -0
  15. package/dart/lib/src/test_harness.dart +490 -58
  16. package/dart/lib/src/webview_bridge.dart +167 -58
  17. package/dart/lib/src/workspace_name.dart +7 -1
  18. package/dart/pubspec.lock +3 -3
  19. package/dart/pubspec.yaml +4 -2
  20. package/dart/test/acquire_session_credentials_test.dart +34 -17
  21. package/dart/test/acquire_session_test.dart +109 -24
  22. package/dart/test/adv_realm_interference_test.dart +227 -99
  23. package/dart/test/bundle_version_pairing_test.dart +1 -1
  24. package/dart/test/cloud_bound_topology_test.dart +1 -1
  25. package/dart/test/cluster_attested_test.dart +52 -35
  26. package/dart/test/cluster_births_test.dart +5 -2
  27. package/dart/test/cluster_device_keyed_acceptance_test.dart +5 -2
  28. package/dart/test/cluster_local_autosync_test.dart +5 -2
  29. package/dart/test/cluster_scale_test.dart +5 -2
  30. package/dart/test/compat_test.dart +1 -1
  31. package/dart/test/dlq_session_surface_test.dart +2 -1
  32. package/dart/test/errors_test.dart +1 -1
  33. package/dart/test/first_app_gauntlet_test.dart +78 -51
  34. package/dart/test/four_layer_nested_topology_test.dart +1 -1
  35. package/dart/test/harness_phase_timeout_test.dart +1 -1
  36. package/dart/test/home_parity_test.dart +9 -5
  37. package/dart/test/local_sync_inert_test.dart +22 -14
  38. package/dart/test/node_runner_transport_test.dart +9 -5
  39. package/dart/test/null_slot_test.dart +1 -1
  40. package/dart/test/parent_signer_recovery_test.dart +5 -2
  41. package/dart/test/schema_validation_test.dart +1 -1
  42. package/dart/test/sealing_client_test.dart +2 -1
  43. package/dart/test/session_bound_test.dart +92 -119
  44. package/dart/test/stale_bundle_test.dart +1 -1
  45. package/dart/test/test_harness_test.dart +77 -22
  46. package/dart/test/vo_read_typing_test.dart +32 -14
  47. package/dart/test/watch_after_offer_stall_test.dart +2 -1
  48. package/dart/test/webview_bridge_test.dart +17 -4
  49. package/dart/test/wire_test.dart +1 -1
  50. package/dart_types/CHANGELOG.md +8 -0
  51. package/dart_types/lib/nomos_types.dart +7 -6
  52. package/dart_types/lib/src/activity.dart +23 -13
  53. package/dart_types/lib/src/foreign_ref.dart +61 -112
  54. package/dart_types/lib/src/invitation.dart +96 -0
  55. package/dart_types/lib/src/nomos_ref.dart +97 -59
  56. package/dart_types/pubspec.yaml +1 -1
  57. package/dart_types/test/surfaced_test.dart +125 -19
  58. package/package.json +7 -3
  59. package/runtime/generated-application-host.js +8851 -0
  60. package/src/activity.ts +2 -2
  61. package/src/aggregate.ts +24 -1
  62. package/src/application_composition.ts +282 -0
  63. package/src/application_topology.ts +151 -0
  64. package/src/attested_core.ts +2 -2
  65. package/src/authoring_context.ts +9 -0
  66. package/src/birth_meter_ensure.ts +1 -1
  67. package/src/births.ts +53 -5
  68. package/src/build_package.ts +79 -71
  69. package/src/business_birth.ts +93 -10
  70. package/src/clear_directive.ts +3 -1
  71. package/src/codegen_dart.ts +369 -420
  72. package/src/codegen_dart_application.ts +1508 -0
  73. package/src/codegen_dart_client.ts +156 -95
  74. package/src/codegen_dart_scenario.ts +299 -0
  75. package/src/codegen_openapi.ts +8 -3
  76. package/src/codegen_proof.ts +1113 -1137
  77. package/src/codegen_provider_dart.ts +12 -1
  78. package/src/codegen_ts.ts +1176 -496
  79. package/src/compat_package_main.ts +22 -10
  80. package/src/compile_package_main.ts +1536 -337
  81. package/src/contract.ts +10 -4
  82. package/src/dart_barrel_guard.ts +5 -1
  83. package/src/describe_model.ts +13 -5
  84. package/src/directive.ts +244 -5
  85. package/src/engine_entry.ts +56 -13
  86. package/src/engine_javascript.ts +31 -0
  87. package/src/evolve_preview.ts +3 -12
  88. package/src/framework/application_home.ts +24 -10
  89. package/src/framework/capability.ts +15 -1
  90. package/src/framework/domain_lifecycle.ts +8 -0
  91. package/src/framework/framework_aggregates.ts +2 -2
  92. package/src/framework/genesis.ts +11 -1
  93. package/src/framework/home.ts +178 -75
  94. package/src/framework/impure_capability.ts +24 -7
  95. package/src/framework/relations.ts +13 -5
  96. package/src/framework/workspaces.ts +13 -12
  97. package/src/generated_client_names.ts +42 -3
  98. package/src/idof_lint.ts +65 -13
  99. package/src/index.ts +46 -12
  100. package/src/intent.ts +587 -0
  101. package/src/law_check.ts +64 -16
  102. package/src/precondition.ts +16 -4
  103. package/src/read_projection_namespace.ts +2 -7
  104. package/src/recipient.ts +33 -0
  105. package/src/relation_schema.ts +93 -0
  106. package/src/secondary_ensure.ts +11 -2
  107. package/src/shape_compat.ts +11 -1
  108. package/src/usd.ts +30 -8
  109. package/src/usd_layers.ts +447 -246
  110. package/src/usd_state.ts +1 -1
  111. package/src/value_object.ts +49 -13
  112. package/src/wire_encode.ts +124 -26
  113. package/src/workspace_sharing.ts +269 -0
  114. package/src/zod_introspection.ts +84 -0
  115. package/dart/example/nomos_secure_key_store.dart +0 -82
  116. package/dart/example/nomos_webview_transport.dart +0 -63
  117. package/dart/lib/nomos_headless.dart +0 -50
  118. package/dart/lib/testing.dart +0 -14
  119. package/dart/test/co2_product_gauntlet_test.dart +0 -244
  120. package/src/framework/identity.ts +0 -537
@@ -0,0 +1,1508 @@
1
+ // Generated tenant-facing Dart application facade.
2
+ //
3
+ // The bridge-bound `<domain>.client.dart` is an implementation detail. This file is the exported
4
+ // application contract: business inputs in, business ids/models out. It deliberately has no public
5
+ // bridge/session/workspace/actor/sync surface.
6
+ import type { z } from "zod";
7
+
8
+ import type { AggregateHandle } from "./aggregate.js";
9
+ import type { Directive } from "./directive.js";
10
+ import type { Field } from "./fields.js";
11
+ import type { QueryDecl } from "./query.js";
12
+ import type { DomainModule } from "./codegen_dart.js";
13
+ import {
14
+ cap,
15
+ concreteClassName,
16
+ dartBusinessRecordInputs,
17
+ dartActionInputClassName,
18
+ introspectPayload,
19
+ readModelClassName,
20
+ registerDartNames,
21
+ } from "./codegen_dart.js";
22
+ import { dartClientClassName } from "./codegen_dart_client.js";
23
+ import { finishCount } from "./count.js";
24
+ import { finishSum } from "./sum.js";
25
+ import { isNamespacedAggregate, qualifyReadProjectionType } from "./read_projection_namespace.js";
26
+ import type { SurfacedResolverBinding } from "./surfaced_lint.js";
27
+ import {
28
+ isFrameworkAggregate,
29
+ isFrameworkDirective,
30
+ isFrameworkQuery,
31
+ } from "./framework/framework_aggregates.js";
32
+ import { isBusinessIntent } from "./intent.js";
33
+ import { contractTypeName } from "./contract.js";
34
+ import type {
35
+ WorkspaceSharingPolicy,
36
+ WorkspaceSharingReceipt,
37
+ } from "./workspace_sharing.js";
38
+
39
+ type AnyDirective = Directive<z.ZodTypeAny>;
40
+
41
+ const camel = (s: string) => s.replace(/[_\s-]+(\w)/g, (_m, c: string) => c.toUpperCase());
42
+ const lcFirst = (s: string) => (s.length ? s[0]!.toLowerCase() + s.slice(1) : s);
43
+ const reserved = new Set([
44
+ "assert", "break", "case", "catch", "class", "const", "continue", "default", "do", "else", "enum",
45
+ "extends", "false", "final", "for", "if", "in", "is", "new", "null", "rethrow", "return", "super",
46
+ "switch", "this", "throw", "true", "try", "var", "void", "while", "with", "dynamic",
47
+ ]);
48
+ const safe = (s: string) => reserved.has(s) ? `${s}_` : s;
49
+ const member = (s: string) => safe(lcFirst(camel(s)));
50
+ const TENANT_ROOT_MEMBERS = new Map<string, string>([
51
+ ["nomosWidget", "the generated Nomos Flutter bootstrap"],
52
+ ["currentUser", "the generated signed-in application participant"],
53
+ ["currentStatus", "the generated application status"],
54
+ ["watchStatus", "the generated application status watch"],
55
+ ["recipient", "the generated typed recipient boundary"],
56
+ ["createInvitation", "the generated invitation capability"],
57
+ ["redeemInvitation", "the generated invitation redemption"],
58
+ ]);
59
+
60
+ function plural(name: string): string {
61
+ if (/[^aeiou]y$/i.test(name)) return `${name.slice(0, -1)}ies`;
62
+ if (/(?:s|x|z|ch|sh)$/i.test(name)) return `${name}es`;
63
+ return `${name}s`;
64
+ }
65
+
66
+ function keyParam(key: string): string {
67
+ const parts = key.split(".").filter(Boolean);
68
+ const raw = parts.length === 0 ? "key" : parts[0]! + parts.slice(1).map(cap).join("");
69
+ const cleaned = raw.replace(/[^A-Za-z0-9_]/g, "_");
70
+ return safe(/^[0-9]/.test(cleaned) ? `v${cleaned}` : cleaned);
71
+ }
72
+
73
+ function keyType(aggregate: AggregateHandle | undefined, key: string): string {
74
+ const first = key.includes(".") ? key.split(".")[0]! : key;
75
+ const field = aggregate?.fields[first] as Field | undefined;
76
+ if (field?.kind === "int" || field?.kind === "counter") return "int";
77
+ if (field?.kind === "ref") {
78
+ if (field.foreignRef !== undefined) {
79
+ const target = field.foreignRef.targetIsContract === true
80
+ ? contractTypeName(field.foreignRef.targetAggregateId)
81
+ : concreteClassName(field.foreignRef.targetAggregateId);
82
+ return `ForeignRef<${target}>`;
83
+ }
84
+ if (field.refAggregateId !== undefined) return `NomosRef<${concreteClassName(field.refAggregateId)}>`;
85
+ }
86
+ return "String";
87
+ }
88
+
89
+ function sharingOrderValue(
90
+ policy: WorkspaceSharingPolicy,
91
+ fieldName: string,
92
+ target: "wire" | "value" = "value",
93
+ ): string {
94
+ const field = policy.orders.fields[fieldName];
95
+ if (field === undefined) {
96
+ throw new Error(`workspaceSharing(${policy.orders.id}): unknown order field '${fieldName}'`);
97
+ }
98
+ const value = `(order.${member(fieldName)} ?? (throw StateError(` +
99
+ `'Incomplete ${policy.orders.id} ${member(fieldName)}')))`;
100
+ if (target === "wire" && field.kind === "enum") return `${value}.wire`;
101
+ if (field.kind === "ref") return `NmsInternalNomosRefs.id(${value})`;
102
+ return value;
103
+ }
104
+
105
+ function sharingReceiptCondition(
106
+ policy: WorkspaceSharingPolicy,
107
+ receipt: WorkspaceSharingReceipt,
108
+ ): string {
109
+ const condition = receipt.when;
110
+ if (condition === undefined) return "true";
111
+ const field = policy.orders.fields[condition.field];
112
+ if (field === undefined) {
113
+ throw new Error(`workspaceSharing(${policy.orders.id}): unknown receipt field '${condition.field}'`);
114
+ }
115
+ if (!field.isOptional) {
116
+ if (condition.is === "absent") {
117
+ throw new Error(
118
+ `workspaceSharing(${policy.orders.id}): '${condition.field}' cannot be absent because it is required`,
119
+ );
120
+ }
121
+ return "true";
122
+ }
123
+ return `order.${member(condition.field)} ${condition.is === "present" ? "!=" : "=="} null`;
124
+ }
125
+
126
+ function sharingReceiptPayload(
127
+ policy: WorkspaceSharingPolicy,
128
+ receipt: WorkspaceSharingReceipt,
129
+ refused: boolean,
130
+ ): string[] {
131
+ const fields = introspectPayload(receipt.records.payloadSchema as z.ZodTypeAny, new Map());
132
+ return fields.flatMap((target) => {
133
+ if (target.name === "failureReason") {
134
+ return refused
135
+ ? [` 'failureReason': share.message ?? 'shareDeliveryRefused',`]
136
+ : target.optional
137
+ ? []
138
+ : (() => { throw new Error(
139
+ `workspaceSharing(${policy.orders.id}): delivered receipt '${receipt.records.id}' requires failureReason`,
140
+ ); })();
141
+ }
142
+ if (target.name === "deliveryHead" && target.optional) return [];
143
+ const sourceName = receipt.payload?.[target.name] ?? target.name;
144
+ const source = policy.orders.fields[sourceName];
145
+ if (source === undefined) {
146
+ if (target.optional) return [];
147
+ throw new Error(
148
+ `workspaceSharing(${policy.orders.id}): receipt '${receipt.records.id}' requires '${target.name}', ` +
149
+ `but the durable order does not record '${sourceName}'`,
150
+ );
151
+ }
152
+ const raw = sharingOrderValue(policy, sourceName);
153
+ const expression = source.kind === "enum" ? `${raw}.wire` : raw;
154
+ return [` ${JSON.stringify(target.name)}: ${expression},`];
155
+ });
156
+ }
157
+
158
+ export const dartApplicationClassName = (domain: string): string => `${cap(camel(domain))}Application`;
159
+ export const dartApplicationActivityClassName = (domain: string): string => `${cap(camel(domain))}Activity`;
160
+ export const dartAggregateCollectionClassName = (aggregate: AggregateHandle): string =>
161
+ `${concreteClassName(aggregate.id)}Collection`;
162
+
163
+ const aggregateConflictsClassName = (domain: string, aggregateId: string): string => {
164
+ const aggregateName = cap(camel(concreteClassName(aggregateId)));
165
+ return aggregateName === cap(camel(domain))
166
+ ? `${aggregateName}AggregateConflicts`
167
+ : `${aggregateName}Conflicts`;
168
+ };
169
+
170
+ const dartActionResultClassName = (directiveId: string): string => `${cap(camel(directiveId))}Result`;
171
+
172
+ function dartActionOutputs(
173
+ directive: AnyDirective,
174
+ aggregates: readonly AggregateHandle[],
175
+ ): { aggregateId: string; field: string; dartType: string; many: boolean }[] {
176
+ const known = new Set(aggregates.filter((aggregate) => !isFrameworkAggregate(aggregate)).map((aggregate) => aggregate.id));
177
+ const raw = [
178
+ { aggregateId: directive.aggregateId, many: false },
179
+ ...((directive.secondaryOutputs ?? []).map((output) => ({
180
+ aggregateId: output.aggregateId,
181
+ many: output.many === true,
182
+ }))),
183
+ ].filter((output) => known.has(output.aggregateId));
184
+ const outputs = raw.filter((output, index) =>
185
+ raw.findIndex((candidate) => candidate.aggregateId === output.aggregateId) === index);
186
+ const fields = new Set<string>();
187
+ return outputs.map((output) => {
188
+ const dartType = concreteClassName(output.aggregateId);
189
+ const field = member(dartType);
190
+ if (fields.has(field)) {
191
+ throw new Error(
192
+ `Dart application result for '${directive.id}' has two aggregate consequences named '${field}'; ` +
193
+ `rename one aggregate so the generated business result is unambiguous`,
194
+ );
195
+ }
196
+ fields.add(field);
197
+ return { ...output, field, dartType };
198
+ });
199
+ }
200
+
201
+ function dartConflictPublicClassNames(mod: DomainModule): string[] {
202
+ const domain = mod.domain ?? mod.name;
203
+ const bindings = mod.surfacedResolvers ?? [];
204
+ if (bindings.length === 0) return [];
205
+ const names = new Set<string>([
206
+ `${cap(camel(domain))}Conflicts`,
207
+ `${cap(camel(domain))}ConflictSummary`,
208
+ ]);
209
+ for (const binding of bindings) {
210
+ const aggregateName = cap(camel(concreteClassName(binding.aggregateId)));
211
+ names.add(aggregateConflictsClassName(domain, binding.aggregateId));
212
+ names.add(`${aggregateName}PendingConflict`);
213
+ if (binding.valuePayloadField !== undefined && binding.idPayloadField !== undefined) {
214
+ names.add(`${aggregateName}${cap(camel(binding.field))}Conflict`);
215
+ names.add(`${aggregateName}${cap(camel(binding.field))}ConflictModelUpdateRequired`);
216
+ }
217
+ }
218
+ return [...names];
219
+ }
220
+
221
+ export function dartApplicationPublicClassNames(
222
+ mod: DomainModule,
223
+ opts: { readonly tenantRoot?: boolean } = {},
224
+ ): string[] {
225
+ registerDartNames(mod.aggregates);
226
+ const actions = ((mod.applicationIntents ?? mod.directives) as AnyDirective[]).filter((directive) =>
227
+ directive &&
228
+ typeof directive.id === "string" &&
229
+ (directive as { businessBirthInternal?: unknown }).businessBirthInternal !== true &&
230
+ (directive as { businessBirth?: unknown }).businessBirth === undefined &&
231
+ (directive as { birthsChild?: unknown }).birthsChild !== true &&
232
+ !isFrameworkDirective(directive) &&
233
+ dartActionOutputs(directive, mod.aggregates).length > 0);
234
+ return [
235
+ ...(opts.tenantRoot === true
236
+ ? ["Identity", "User", ...(mod.applicationWorkspaceSharing?.length ? ["Recipient", "Invitation"] : [])]
237
+ : []),
238
+ dartApplicationClassName(mod.domain ?? mod.name),
239
+ dartApplicationActivityClassName(mod.domain ?? mod.name),
240
+ ...actions.map((directive) => dartActionResultClassName(directive.id)),
241
+ ...mod.aggregates
242
+ .filter((aggregate) =>
243
+ (aggregate as { businessBirthInternal?: unknown }).businessBirthInternal !== true &&
244
+ !isFrameworkAggregate(aggregate))
245
+ .map(dartAggregateCollectionClassName),
246
+ ...dartConflictPublicClassNames(mod),
247
+ ];
248
+ }
249
+
250
+ export interface DartApplicationOptions {
251
+ readonly packageAggregateDomains?: Readonly<Record<string, { domain: string; name: string }>>;
252
+ readonly typesImportUri?: string;
253
+ readonly explicitTypeImports?: readonly string[];
254
+ readonly supportImport?: "vendored" | "package";
255
+ readonly flutterHost?: boolean;
256
+ /// Whether this domain application is the tenant's one public Flutter root.
257
+ /// Multi-domain package children remain stateful business handles, but only
258
+ /// the generated package application may mount or expose application status.
259
+ readonly tenantRoot?: boolean;
260
+ }
261
+
262
+ /**
263
+ * Emit the exported business facade for one generated domain.
264
+ *
265
+ * The two `nomosInternal...` functions are intentionally omitted from package barrels. Generated Flutter
266
+ * hosting and generated sibling facades import them directly from `src/generated`; tenant autocomplete sees
267
+ * only `<Domain>Application`.
268
+ */
269
+ export function generateDartApplication(mod: DomainModule, opts: DartApplicationOptions = {}): string {
270
+ registerDartNames(mod.aggregates);
271
+ const domain = mod.domain ?? mod.name;
272
+ const application = dartApplicationClassName(domain);
273
+ const activity = dartApplicationActivityClassName(domain);
274
+ const client = dartClientClassName(domain);
275
+ const aggregates = new Map(mod.aggregates.map((aggregate) => [aggregate.id, aggregate]));
276
+ const packageDomains = opts.packageAggregateDomains ?? {};
277
+ const appLines: string[] = [];
278
+ const applicationMembers = new Map<string, string>();
279
+ const claimApplicationMember = (name: string, owner: string): void => {
280
+ const prior = applicationMembers.get(name);
281
+ if (prior !== undefined) {
282
+ throw new Error(
283
+ `Sorry mate — generated Dart application member '${name}' is already owned by ${prior}; ` +
284
+ `${owner} cannot be silently renamed or rerouted. Rename the tenant concept and compile again.`,
285
+ );
286
+ }
287
+ applicationMembers.set(name, owner);
288
+ };
289
+ claimApplicationMember("activity", "the generated business history");
290
+ const collectionLines = new Map<string, string[]>();
291
+ const collectionMembers = new Map<string, Set<string>>();
292
+ const siblingImports = new Map<string, Set<string>>();
293
+ const explicitTypeImportUris = new Set(opts.explicitTypeImports ?? []);
294
+ let usesWorkspaceName = false;
295
+ let usesAffectedReference = false;
296
+ let usesAffectedReferences = false;
297
+ const actionResultDeclarations: string[] = [];
298
+ const conflictDeclarations: string[] = [];
299
+ const resolverBindings: readonly SurfacedResolverBinding[] = mod.surfacedResolvers ?? [];
300
+ const sharingPolicies: readonly WorkspaceSharingPolicy[] = mod.applicationWorkspaceSharing ?? [];
301
+ const tenantRoot = opts.tenantRoot ?? opts.flutterHost === true;
302
+ if (tenantRoot) {
303
+ for (const [name, owner] of TENANT_ROOT_MEMBERS) claimApplicationMember(name, owner);
304
+ }
305
+ if (!tenantRoot && sharingPolicies.length > 0) {
306
+ for (const name of ["recipient", "createInvitation", "redeemInvitation"]) {
307
+ claimApplicationMember(name, "the generated invitation boundary");
308
+ }
309
+ }
310
+ const addSibling = (file: string, name: string) => {
311
+ if (explicitTypeImportUris.has(file)) return;
312
+ if (!siblingImports.has(file)) siblingImports.set(file, new Set());
313
+ siblingImports.get(file)!.add(name);
314
+ };
315
+ const typeImportUriFor = (name: string): string => {
316
+ for (const uri of explicitTypeImportUris) {
317
+ if (uri.endsWith(`/${name}.dart`) || uri.endsWith(`${name}.dart`)) return uri;
318
+ }
319
+ return `${name}.dart`;
320
+ };
321
+
322
+ const actionDeclarations = (mod.applicationIntents ?? mod.directives) as AnyDirective[];
323
+ const directives = actionDeclarations.filter(
324
+ (directive) => directive && typeof directive.id === "string" &&
325
+ typeof (directive as { payloadSchema?: unknown }).payloadSchema === "object" &&
326
+ (directive as { businessBirthInternal?: unknown }).businessBirthInternal !== true &&
327
+ !isFrameworkDirective(directive),
328
+ );
329
+ const actionReferenceChecks = (directive: AnyDirective): string[] => {
330
+ let specs: ReturnType<typeof introspectPayload>;
331
+ try {
332
+ specs = introspectPayload(directive.payloadSchema as z.ZodTypeAny, new Map());
333
+ } catch {
334
+ return [];
335
+ }
336
+ const recordInputIds = new Set(
337
+ dartBusinessRecordInputs(directive, mod.aggregates).map((input) => input.idField),
338
+ );
339
+ return specs
340
+ .filter((spec) => spec.toJson === "ref" && !recordInputIds.has(spec.name))
341
+ .map((spec) => {
342
+ const targetClass = spec.dartType.match(/^NomosRef<(.*)>$/)?.[1] ?? "Object?";
343
+ const targetWire = spec.referenceType ?? [...aggregates.values()].find(
344
+ (aggregate) => concreteClassName(aggregate.id) === targetClass,
345
+ )?.id ?? targetClass;
346
+ const value = `facts.${member(spec.name)}${spec.optional ? "!" : ""}`;
347
+ const check = `_nmsRequireReference(${value}, ${JSON.stringify(targetWire)}, _client.session?.workspace ?? '');`;
348
+ return spec.optional
349
+ ? ` if (facts.${member(spec.name)} != null) ${check}`
350
+ : ` ${check}`;
351
+ });
352
+ };
353
+
354
+ for (const aggregate of mod.aggregates) {
355
+ if ((aggregate as { businessBirthInternal?: unknown }).businessBirthInternal === true) continue;
356
+ if (isFrameworkAggregate(aggregate)) continue;
357
+ collectionLines.set(aggregate.id, []);
358
+ collectionMembers.set(aggregate.id, new Set());
359
+ }
360
+
361
+ // Conflicts are business decisions, so the application facade keeps the useful chain while the
362
+ // heterogeneous index, immutable proof snapshot and offer result remain implementation details.
363
+ if (resolverBindings.length > 0) {
364
+ const conflictsClass = `${cap(camel(domain))}Conflicts`;
365
+ const summaryClass = `${cap(camel(domain))}ConflictSummary`;
366
+ const rowClass = `_${cap(camel(domain))}PendingConflictRow`;
367
+ const byAggregate = new Map<string, SurfacedResolverBinding[]>();
368
+ for (const binding of resolverBindings) {
369
+ byAggregate.set(binding.aggregateId, [...(byAggregate.get(binding.aggregateId) ?? []), binding]);
370
+ }
371
+ claimApplicationMember("conflicts", "the generated conflict policy");
372
+ appLines.push(` final ${conflictsClass} conflicts;`);
373
+
374
+ conflictDeclarations.push(
375
+ `/// Pending business decisions across this application.`,
376
+ `final class ${summaryClass} {`,
377
+ ` final int total;`,
378
+ ` final int ready;`,
379
+ ` final int modelUpdatesRequired;`,
380
+ ` final Map<String, int> byAggregate;`,
381
+ ` const ${summaryClass}({required this.total, required this.ready, required this.modelUpdatesRequired, required this.byAggregate});`,
382
+ `}`,
383
+ ``,
384
+ `/// Summary -> aggregate -> typed conflict -> merge.`,
385
+ `final class ${conflictsClass} {`,
386
+ ` final ${client} _client;`,
387
+ ` const ${conflictsClass}._(this._client);`,
388
+ ``,
389
+ ` Future<${summaryClass}> summary() async {`,
390
+ ` final summary = await _client.bridge.pendingConflictSummary();`,
391
+ ` final byAggregate = (summary['byAggregate'] as Map? ?? const {}).map(`,
392
+ ` (key, value) => MapEntry(key.toString(), (value as num).toInt()));`,
393
+ ` return ${summaryClass}(`,
394
+ ` total: (summary['total'] as num? ?? 0).toInt(),`,
395
+ ` ready: (summary['ready'] as num? ?? 0).toInt(),`,
396
+ ` modelUpdatesRequired: (summary['evolutionRequired'] as num? ?? 0).toInt(),`,
397
+ ` byAggregate: Map.unmodifiable(byAggregate),`,
398
+ ` );`,
399
+ ` }`,
400
+ ...[...byAggregate.keys()].map((aggregateId) =>
401
+ ` ${aggregateConflictsClassName(domain, aggregateId)} get ${member(concreteClassName(aggregateId))} => ${aggregateConflictsClassName(domain, aggregateId)}._(_client);`,
402
+ ),
403
+ `}`,
404
+ ``,
405
+ );
406
+
407
+ for (const [aggregateId, bindings] of byAggregate) {
408
+ const aggregate = aggregates.get(aggregateId);
409
+ if (aggregate === undefined) {
410
+ throw new Error(`generated conflict policy refers to unknown aggregate '${aggregateId}' in domain '${domain}'`);
411
+ }
412
+ const runtimeAggregateType = isNamespacedAggregate(aggregate)
413
+ ? qualifyReadProjectionType(domain, aggregateId)
414
+ : aggregateId;
415
+ const aggregateName = cap(camel(concreteClassName(aggregateId)));
416
+ const pendingClass = `${aggregateName}PendingConflict`;
417
+ const aggregateClass = aggregateConflictsClassName(domain, aggregateId);
418
+ conflictDeclarations.push(
419
+ `sealed class ${pendingClass} {`,
420
+ ` const ${pendingClass}();`,
421
+ ` String get aggregateId;`,
422
+ ` String get field;`,
423
+ `}`,
424
+ ``,
425
+ );
426
+ for (const binding of bindings) {
427
+ if (binding.valuePayloadField === undefined || binding.idPayloadField === undefined) continue;
428
+ const fieldKind = aggregate.fields[binding.field]?.kind;
429
+ const mergedType = fieldKind === "string" || fieldKind === "ref" || fieldKind === "enum"
430
+ ? "String"
431
+ : fieldKind === "int"
432
+ ? "int"
433
+ : fieldKind === "bool"
434
+ ? "bool"
435
+ : "Object?";
436
+ const typeName = `${aggregateName}${cap(camel(binding.field))}Conflict`;
437
+ const evolutionTypeName = `${typeName}ModelUpdateRequired`;
438
+ const helper = `_merge${aggregateName}${cap(camel(binding.field))}`;
439
+ conflictDeclarations.push(
440
+ `/// Merge every observed business version into one result.`,
441
+ `final class ${typeName} extends ${pendingClass} {`,
442
+ ` final ${rowClass} _details;`,
443
+ ` final ${client} _client;`,
444
+ ` const ${typeName}._(this._details, this._client);`,
445
+ ` @override String get aggregateId => _details.aggregateId;`,
446
+ ` @override String get field => _details.field;`,
447
+ ` List<SurfacedObservation<${mergedType}>> get versions => List.unmodifiable(`,
448
+ ` _details.observations.map((item) => SurfacedObservation<${mergedType}>(`,
449
+ ` value: item.value as ${mergedType}, occurredAt: item.occurredAt, author: item.author)));`,
450
+ ` Future<void> merge(`,
451
+ ` ${mergedType} Function(List<SurfacedObservation<${mergedType}>> versions) reconcile,`,
452
+ ` {Map<String, Object?> rest = const {}}) async {`,
453
+ ` await _client.${helper}(_details, reconcile(versions), rest: rest);`,
454
+ ` }`,
455
+ `}`,
456
+ ``,
457
+ `/// This disagreement remains pending because the business model cannot yet compare every`,
458
+ `/// retained version. Unrelated work continues; update the model's evolution policy to unlock it.`,
459
+ `final class ${evolutionTypeName} extends ${pendingClass} {`,
460
+ ` final ${rowClass} _details;`,
461
+ ` const ${evolutionTypeName}._(this._details);`,
462
+ ` @override String get aggregateId => _details.aggregateId;`,
463
+ ` @override String get field => _details.field;`,
464
+ ` String get reason => _details.evolution?['reason']?.toString() ??`,
465
+ ` 'The business model cannot yet compare every retained version. Update its evolution policy; unrelated work can continue.';`,
466
+ `}`,
467
+ ``,
468
+ );
469
+ }
470
+ conflictDeclarations.push(
471
+ `final class ${aggregateClass} {`,
472
+ ` final ${client} _client;`,
473
+ ` const ${aggregateClass}._(this._client);`,
474
+ ` Future<List<${pendingClass}>> pending() async {`,
475
+ ` final rows = await _client._pendingConflictRows(${JSON.stringify(runtimeAggregateType)});`,
476
+ ` final decisions = <${pendingClass}>[];`,
477
+ ` for (final row in rows) {`,
478
+ ` switch (row.field) {`,
479
+ ...bindings.flatMap((binding) => {
480
+ if (binding.valuePayloadField === undefined || binding.idPayloadField === undefined) return [];
481
+ const typeName = `${aggregateName}${cap(camel(binding.field))}Conflict`;
482
+ return [
483
+ ` case ${JSON.stringify(binding.field)}:`,
484
+ ` if (row.status == 'ready') {`,
485
+ ` decisions.add(${typeName}._(row, _client));`,
486
+ ` } else if (row.status == 'evolutionRequired') {`,
487
+ ` decisions.add(${typeName}ModelUpdateRequired._(row));`,
488
+ ` } else {`,
489
+ ` throw StateError('Nomos returned an unknown conflict status: \${row.status}');`,
490
+ ` }`,
491
+ ];
492
+ }),
493
+ ` }`,
494
+ ` }`,
495
+ ` return List.unmodifiable(decisions);`,
496
+ ` }`,
497
+ `}`,
498
+ ``,
499
+ );
500
+ }
501
+ }
502
+
503
+ const addCollectionMember = (aggregateId: string, name: string, memberLines: string[]) => {
504
+ const target = collectionLines.get(aggregateId);
505
+ if (target === undefined) return false;
506
+ const names = collectionMembers.get(aggregateId)!;
507
+ if (names.has(name)) {
508
+ throw new Error(`Dart application surface collision: '${aggregateId}.${name}' is declared more than once`);
509
+ }
510
+ names.add(name);
511
+ target.push(...memberLines);
512
+ return true;
513
+ };
514
+
515
+ const sourceTrustAction = (directive: AnyDirective): {
516
+ prelude: string[];
517
+ call: string;
518
+ } => {
519
+ const trust = directive.businessSourceTrust;
520
+ const method = member(directive.id);
521
+ if (trust === undefined) {
522
+ return { prelude: [], call: `_client.${method}(facts)` };
523
+ }
524
+ const queryArgs = Object.entries(trust.queryArgs)
525
+ .sort(([a], [b]) => a.localeCompare(b))
526
+ .map(([argument, field]) => `${JSON.stringify(argument)}: facts.${member(field)}`);
527
+ const copied = Object.entries(trust.copy)
528
+ .sort(([a], [b]) => a.localeCompare(b))
529
+ .map(([target, field]) => ` ${JSON.stringify(target)}: facts.${member(field)},`);
530
+ return {
531
+ prelude: [
532
+ ` final servedSource = await _client.bridge.attestedRead(`,
533
+ ` from: facts.${member(trust.fromPayload)},`,
534
+ ` queryId: ${JSON.stringify(trust.queryId)},`,
535
+ ` args: {${queryArgs.join(", ")}},`,
536
+ ` );`,
537
+ ` final sourceEntry = NmsInternalSourceTrust.entry(servedSource);`,
538
+ ` final sourceIdentity = NmsInternalSourceTrust.attestation(sourceEntry);`,
539
+ ` await _client._offer(`,
540
+ ` ${JSON.stringify(trust.viaDirectiveId)},`,
541
+ ` {`,
542
+ ...copied,
543
+ ` ${JSON.stringify(trust.genesisField)}: NmsInternalSourceTrust.genesis(sourceIdentity),`,
544
+ ` ${JSON.stringify(trust.keyHashesField)}: [NmsInternalSourceTrust.keyHash(sourceIdentity)],`,
545
+ ` },`,
546
+ ` );`,
547
+ ],
548
+ call: `_client.${method}(facts, attestations: [sourceEntry])`,
549
+ };
550
+ };
551
+
552
+ if (sharingPolicies.length > 0) {
553
+ appLines.push(
554
+ ` /// Turn an identity-provider uid into a typed business recipient.`,
555
+ ` ///`,
556
+ ` /// A uid crosses the string boundary once. Generated invitation actions then require`,
557
+ ` /// [Recipient<User>], so an email address, principal or record id cannot be substituted.`,
558
+ ` Recipient<User> recipient(String uid) =>`,
559
+ ` NmsInternalApplicationHost.recipient<User>(uid);`,
560
+ ``,
561
+ ` /// Create a single-use invitation. Transport [Invitation.link] however your product chooses.`,
562
+ ` Future<Invitation<User>> createInvitation() =>`,
563
+ ` NmsInternalApplicationHost.createInvitation<User>(_client.bridge);`,
564
+ ``,
565
+ ` /// Redeem a transported invitation link as the verified [currentUser].`,
566
+ ` Future<void> redeemInvitation(String link) =>`,
567
+ ` NmsInternalApplicationHost.redeemInvitation<User>(`,
568
+ ` _client.bridge,`,
569
+ ` link: link,`,
570
+ ` identity: _client.actor,`,
571
+ ` policies: [`,
572
+ ...sharingPolicies.map((policy) => [
573
+ ` NmsInternalInvitationPolicy(`,
574
+ ` domain: ${JSON.stringify(domain)},`,
575
+ ` domainHash: _client.domainHash,`,
576
+ ` query: ${JSON.stringify(policy.invitationLaw.pendingByRecipient.id)},`,
577
+ ` redeem: ${JSON.stringify(policy.invitationLaw.redeem.id)},`,
578
+ ` personField: ${JSON.stringify(policy.person)},`,
579
+ ` roleField: ${JSON.stringify(policy.role)},`,
580
+ ` scopeField: ${JSON.stringify(policy.scope)},`,
581
+ ` ),`,
582
+ ]).flat(),
583
+ ` ],`,
584
+ ` );`,
585
+ ``,
586
+ );
587
+ for (const [index, policy] of sharingPolicies.entries()) {
588
+ const pendingKey = policy.pending.key[0]!;
589
+ const pendingMethod = member(policy.pending.id);
590
+ const person = sharingOrderValue(policy, policy.person, "wire");
591
+ const role = sharingOrderValue(policy, policy.role, "wire");
592
+ const scope = sharingOrderValue(policy, policy.scope, "wire");
593
+ const delivered = policy.delivered.flatMap((receipt) => [
594
+ ` if (${sharingReceiptCondition(policy, receipt)}) {`,
595
+ ` await _client._offer(`,
596
+ ` ${JSON.stringify(receipt.records.id)},`,
597
+ ` <String, Object?>{`,
598
+ ...sharingReceiptPayload(policy, receipt, false),
599
+ ` },`,
600
+ ` );`,
601
+ ` continue;`,
602
+ ` }`,
603
+ ]);
604
+ const refused = policy.refused.flatMap((receipt) => [
605
+ ` if (${sharingReceiptCondition(policy, receipt)}) {`,
606
+ ` await _client._offer(`,
607
+ ` ${JSON.stringify(receipt.records.id)},`,
608
+ ` <String, Object?>{`,
609
+ ...sharingReceiptPayload(policy, receipt, true),
610
+ ` },`,
611
+ ` );`,
612
+ ` continue;`,
613
+ ` }`,
614
+ ]);
615
+ appLines.push(
616
+ ` Future<void>? _nmsWorkspaceSharingDrain${index};`,
617
+ ` StreamSubscription<Object?>? _nmsWorkspaceSharingSync${index};`,
618
+ ` bool _nmsWorkspaceSharingNeedsRetry${index} = false;`,
619
+ ``,
620
+ ` Future<void> _nmsStartWorkspaceSharing${index}() async {`,
621
+ ` await _nmsDrainWorkspaceSharing${index}();`,
622
+ ` _nmsWorkspaceSharingSync${index} ??= _client.bridge.syncStatus.map<Object?>((status) {`,
623
+ ` if (_nmsWorkspaceSharingDrain${index} == null &&`,
624
+ ` (status.phase == SyncPhase.offline || status.phase == SyncPhase.error || status.behind > 0)) {`,
625
+ ` _nmsWorkspaceSharingNeedsRetry${index} = true;`,
626
+ ` } else if (status.phase == SyncPhase.idle && _nmsWorkspaceSharingNeedsRetry${index}) {`,
627
+ ` _nmsWorkspaceSharingNeedsRetry${index} = false;`,
628
+ ` unawaited(_nmsDrainWorkspaceSharing${index}());`,
629
+ ` }`,
630
+ ` return status;`,
631
+ ` }).listen((_) {});`,
632
+ ` }`,
633
+ ``,
634
+ ` Future<void> _nmsDrainWorkspaceSharing${index}() {`,
635
+ ` final active = _nmsWorkspaceSharingDrain${index};`,
636
+ ` if (active != null) return active;`,
637
+ ` final started = _nmsDrainWorkspaceSharingOnce${index}();`,
638
+ ` _nmsWorkspaceSharingDrain${index} = started;`,
639
+ ` return started.whenComplete(() {`,
640
+ ` if (identical(_nmsWorkspaceSharingDrain${index}, started)) {`,
641
+ ` _nmsWorkspaceSharingDrain${index} = null;`,
642
+ ` }`,
643
+ ` });`,
644
+ ` }`,
645
+ ``,
646
+ ` Future<void> _nmsDrainWorkspaceSharingOnce${index}() async {`,
647
+ ` final orders = await _client.${pendingMethod}(`,
648
+ ` ${member(pendingKey)}: ${JSON.stringify(policy.pendingValue)},`,
649
+ ` );`,
650
+ ` for (final order in orders) {`,
651
+ ` late final ApplicationShareResult share;`,
652
+ ` try {`,
653
+ ` share = await NmsInternalApplicationHost.shareWith(`,
654
+ ` _client.bridge, person: ${person}, role: ${role}, scope: ${scope}, identity: _client.actor,`,
655
+ ` );`,
656
+ ` } catch (_) {`,
657
+ ` continue;`,
658
+ ` }`,
659
+ ` if (share.awaitingRecipient) continue;`,
660
+ ` if (share.delivered) {`,
661
+ ...delivered,
662
+ ` throw StateError('No delivered receipt matches ${policy.orders.id} \${NmsInternalNomosRefs.id(order.ref)}');`,
663
+ ` }`,
664
+ ...refused,
665
+ ` throw StateError('No refused receipt matches ${policy.orders.id} \${NmsInternalNomosRefs.id(order.ref)}');`,
666
+ ` }`,
667
+ ` }`,
668
+ ``,
669
+ );
670
+ }
671
+ appLines.push(
672
+ ` Future<void> _nmsStartWorkspaceSharing() async {`,
673
+ ...sharingPolicies.map((_policy, index) => ` await _nmsStartWorkspaceSharing${index}();`),
674
+ ` }`,
675
+ ``,
676
+ ` Future<void> _nmsDrainWorkspaceSharing() async {`,
677
+ ...sharingPolicies.map((_policy, index) => ` await _nmsDrainWorkspaceSharing${index}();`),
678
+ ` }`,
679
+ ``,
680
+ );
681
+ }
682
+
683
+ const sharingInitiatorIds = new Set(
684
+ sharingPolicies.flatMap((policy) => policy.initiatedBy.map((intent) => intent.id)),
685
+ );
686
+ const afterBusinessWrite = (directive: AnyDirective): string[] =>
687
+ sharingInitiatorIds.has(directive.id) ? [` await _nmsDrainWorkspaceSharing();`] : [];
688
+ const businessActionDoc = (directive: AnyDirective): string => {
689
+ return sharingInitiatorIds.has(directive.id)
690
+ ? "One business action. Nomos durably delivers its access change and retries after restart or reconnect; do not mirror delivery in application state."
691
+ : "One business action; every consequence commits atomically.";
692
+ };
693
+
694
+ const birthCollectionsWithMine = new Set<string>();
695
+ for (const directive of directives) {
696
+ const birth = (directive as unknown as {
697
+ businessBirth?: {
698
+ aggregateId: string;
699
+ namespace: string;
700
+ rootId: string;
701
+ keyField: string;
702
+ };
703
+ birthsChild?: boolean;
704
+ }).businessBirth;
705
+ if (birth !== undefined) {
706
+ // A business birth is declared on the parent domain but its aggregate
707
+ // belongs to the child domain. Keep a generated collection for that
708
+ // child so tenant code gets navigation/read helpers alongside the root
709
+ // intent, without reintroducing a collection-shaped write method.
710
+ if (!collectionLines.has(birth.aggregateId)) {
711
+ collectionLines.set(birth.aggregateId, []);
712
+ collectionMembers.set(birth.aggregateId, new Set());
713
+ }
714
+ const owner = packageDomains[birth.aggregateId];
715
+ const targetDomain = owner?.domain ?? domain;
716
+ const targetFile = owner?.name ?? mod.name;
717
+ const targetApplication = dartApplicationClassName(targetDomain);
718
+ const opener = `nomosInternalOpen${targetApplication}`;
719
+ if (targetFile !== mod.name) {
720
+ addSibling(`${targetFile}.application.dart`, targetApplication);
721
+ addSibling(`${targetFile}.application.dart`, opener);
722
+ }
723
+ const businessBirthIntent = isBusinessIntent(directive);
724
+ if (businessBirthIntent) {
725
+ const method = member(directive.id);
726
+ claimApplicationMember(method, `business intent '${directive.id}'`);
727
+ appLines.push(
728
+ ` /// ${cap(camel(directive.id))}. One business action; the new ${birth.aggregateId} and every parent consequence commit atomically.`,
729
+ ` /// Canonical write path: pass business facts here; never mirror this action in application state`,
730
+ ` /// or call a lower-level client.`,
731
+ ` Future<${targetApplication}> ${method}(${dartActionInputClassName(directive)} facts) async {`,
732
+ ` final reference = await _client.${method}(facts);`,
733
+ ` return ${opener}(_client.bridge, reference, _client.actor);`,
734
+ ` }`,
735
+ ``,
736
+ );
737
+ } else {
738
+ addCollectionMember(birth.aggregateId, "birth", [
739
+ ` /// Give one ${birth.aggregateId} its own durable life.`,
740
+ ` Future<${targetApplication}> birth(Create${cap(camel(birth.aggregateId))}Input facts) async {`,
741
+ ` final reference = await _client.birth(facts);`,
742
+ ` return ${opener}(_client.bridge, reference, _client.actor);`,
743
+ ` }`,
744
+ ``,
745
+ ]);
746
+ }
747
+ if (opts.supportImport === "package") {
748
+ usesWorkspaceName = true;
749
+ const rootAggregate = aggregates.get(birth.aggregateId);
750
+ const rootModel = rootAggregate === undefined
751
+ ? `${concreteClassName(birth.aggregateId)}ReadModel`
752
+ : readModelClassName(rootAggregate.id);
753
+ const rootType = concreteClassName(birth.aggregateId);
754
+ const rootCollection = member(plural(rootAggregate === undefined
755
+ ? birth.aggregateId
756
+ : concreteClassName(rootAggregate.id)));
757
+ addCollectionMember(birth.aggregateId, "open", [
758
+ ` /// Open the ${birth.aggregateId} whose ${birth.keyField} is [key].`,
759
+ ` Future<${targetApplication}> open(String key) => ${opener}(`,
760
+ ` _client.bridge,`,
761
+ ` NmsInternalNomosRefs.create<${rootType}>(`,
762
+ ` workspace: deriveWorkspaceName(${JSON.stringify(birth.namespace)}, key),`,
763
+ ` type: ${JSON.stringify(birth.aggregateId)},`,
764
+ ` id: ${JSON.stringify(birth.rootId)},`,
765
+ ` ),`,
766
+ ` _client.actor,`,
767
+ ` );`,
768
+ ``,
769
+ ]);
770
+ addCollectionMember(birth.aggregateId, "mine", [
771
+ ` /// The ${birth.aggregateId} lives founded by the signed-in person.`,
772
+ ` /// Nomos follows its private birth records and returns business records only.`,
773
+ ` Future<List<${rootModel}>> mine() async {`,
774
+ ` final principal = _client.actor;`,
775
+ ` if (principal == null || principal.trim().isEmpty) {`,
776
+ ` throw StateError('The signed-in person is not available.');`,
777
+ ` }`,
778
+ ` final births = await _client.bridge.query(`,
779
+ ` 'birthRecordsByPrincipal',`,
780
+ ` {'principal': principal},`,
781
+ ` );`,
782
+ ` final records = <${rootModel}>[];`,
783
+ ` for (final row in births) {`,
784
+ ` final raw = row['data'];`,
785
+ ` if (raw is! Map) continue;`,
786
+ ` final child = (raw['child'] ?? '').toString();`,
787
+ ` if (!child.startsWith(${JSON.stringify(`${birth.namespace}-`)})) continue;`,
788
+ ` final application = await ${opener}(`,
789
+ ` _client.bridge,`,
790
+ ` NmsInternalNomosRefs.create<${rootType}>(workspace: child, type: ${JSON.stringify(birth.aggregateId)}, id: ${JSON.stringify(birth.rootId)}),`,
791
+ ` _client.actor,`,
792
+ ` );`,
793
+ ` final record = await application.${rootCollection}.find(`,
794
+ ` NmsInternalNomosRefs.create<${rootType}>(workspace: child, type: ${JSON.stringify(birth.aggregateId)}, id: ${JSON.stringify(birth.rootId)}),`,
795
+ ` );`,
796
+ ` if (record != null) records.add(record);`,
797
+ ` }`,
798
+ ` return records;`,
799
+ ` }`,
800
+ ``,
801
+ ` /// React when this person's set of ${birth.aggregateId} lives changes.`,
802
+ ` Stream<List<${rootModel}>> watchMine() {`,
803
+ ` final principal = _client.actor;`,
804
+ ` if (principal == null || principal.trim().isEmpty) {`,
805
+ ` throw StateError('The signed-in person is not available.');`,
806
+ ` }`,
807
+ ` return _client.bridge`,
808
+ ` .watch('birthRecordsByPrincipal', {'principal': principal})`,
809
+ ` .asyncMap((_) => mine());`,
810
+ ` }`,
811
+ ``,
812
+ ` /// Every ${birth.aggregateId} the signed-in person can use, whether founded or shared.`,
813
+ ` /// Nomos follows custody and authority records privately and returns business records only.`,
814
+ ` Future<List<${rootModel}>> available() async {`,
815
+ ` final records = <${rootModel}>[...await mine()];`,
816
+ ` final seen = <String>{};`,
817
+ ` for (final record in records) {`,
818
+ ` seen.add(NmsInternalRecordOrigins.require(record).workspace);`,
819
+ ` }`,
820
+ ` final shares = await _client.bridge.query('aggregatesByType', {'__type': 'SharedWithMe'}, null, _client.actor);`,
821
+ ` for (final row in shares) {`,
822
+ ` final raw = row['data'];`,
823
+ ` if (raw is! Map) continue;`,
824
+ ` final workspace = (raw['asserterWorkspace'] ?? '').toString();`,
825
+ ` final object = (raw['object'] ?? '').toString();`,
826
+ ` final prunedAt = (raw['prunedAt'] ?? '').toString();`,
827
+ ` if (!workspace.startsWith(${JSON.stringify(`${birth.namespace}-`)}) ||`,
828
+ ` object != 'workspace:self' ||`,
829
+ ` prunedAt.isNotEmpty ||`,
830
+ ` !seen.add(workspace)) continue;`,
831
+ ` final application = await ${opener}(`,
832
+ ` _client.bridge,`,
833
+ ` NmsInternalNomosRefs.create<${rootType}>(workspace: workspace, type: ${JSON.stringify(birth.aggregateId)}, id: ${JSON.stringify(birth.rootId)}),`,
834
+ ` _client.actor,`,
835
+ ` );`,
836
+ ` final record = await application.${rootCollection}.find(`,
837
+ ` NmsInternalNomosRefs.create<${rootType}>(workspace: workspace, type: ${JSON.stringify(birth.aggregateId)}, id: ${JSON.stringify(birth.rootId)}),`,
838
+ ` );`,
839
+ ` if (record != null) records.add(record);`,
840
+ ` }`,
841
+ ` return records;`,
842
+ ` }`,
843
+ ``,
844
+ ` /// React when this person's available ${birth.aggregateId} lives change.`,
845
+ ` Stream<List<${rootModel}>> watchAvailable() =>`,
846
+ ` _client.bridge`,
847
+ ` .watch('aggregatesByType', {'__type': 'SharedWithMe'}, null, _client.actor)`,
848
+ ` .asyncMap((_) => available());`,
849
+ ``,
850
+ ` /// Continue working in the business application that produced [record].`,
851
+ ` Future<${targetApplication}> applicationOf(${rootModel} record) {`,
852
+ ` final origin = NmsInternalRecordOrigins.require(record);`,
853
+ ` return ${opener}(`,
854
+ ` _client.bridge,`,
855
+ ` NmsInternalNomosRefs.create<${rootType}>(workspace: origin.workspace, type: ${JSON.stringify(birth.aggregateId)}, id: origin.id),`,
856
+ ` _client.actor,`,
857
+ ` );`,
858
+ ` }`,
859
+ ``,
860
+ ]);
861
+ birthCollectionsWithMine.add(birth.aggregateId);
862
+ }
863
+ continue;
864
+ }
865
+ // Lowered cross-workspace governance births are framework implementation, never tenant actions.
866
+ if ((directive as { birthsChild?: boolean }).birthsChild === true) continue;
867
+
868
+ const method = member(directive.id);
869
+ const payload = dartActionInputClassName(directive);
870
+ const referenceChecks = actionReferenceChecks(directive);
871
+ const outputs = dartActionOutputs(directive, mod.aggregates);
872
+ const resultClass = dartActionResultClassName(directive.id);
873
+ if (outputs.length > 0) {
874
+ usesAffectedReference = true;
875
+ usesAffectedReferences ||= outputs.some((output) => output.many);
876
+ actionResultDeclarations.push(
877
+ `/// Every business record affected by ${cap(camel(directive.id))}.`,
878
+ `final class ${resultClass} {`,
879
+ ...outputs.map((output) =>
880
+ output.many
881
+ ? ` final List<NomosRef<${output.dartType}>> ${output.field};`
882
+ : ` final NomosRef<${output.dartType}> ${output.field};`),
883
+ ` const ${resultClass}({${outputs.map((output) => `required this.${output.field}`).join(", ")}});`,
884
+ `}`,
885
+ ``,
886
+ );
887
+ }
888
+ const resultExpression = outputs.length === 0
889
+ ? undefined
890
+ : `${resultClass}(${outputs.map((output) =>
891
+ output.many
892
+ ? `${output.field}: _nmsAffectedReferences<${output.dartType}>(change, ${JSON.stringify(output.aggregateId)})`
893
+ : `${output.field}: _nmsAffectedReference<${output.dartType}>(change, ${JSON.stringify(output.aggregateId)})`).join(", ")})`;
894
+ if (isBusinessIntent(directive)) {
895
+ claimApplicationMember(method, `business intent '${directive.id}'`);
896
+ const sourceTrust = sourceTrustAction(directive);
897
+ if (resultExpression !== undefined) {
898
+ appLines.push(
899
+ ` /// ${cap(camel(directive.id))}. ${businessActionDoc(directive)}`,
900
+ ` /// Canonical write path: pass business facts here; never mirror this action in application state`,
901
+ ` /// or call a lower-level client.`,
902
+ ` Future<${resultClass}> ${method}(${payload} facts) async {`,
903
+ ...referenceChecks,
904
+ ...sourceTrust.prelude,
905
+ ` final change = await ${sourceTrust.call};`,
906
+ ...afterBusinessWrite(directive),
907
+ ` return ${resultExpression};`,
908
+ ` }`,
909
+ ``,
910
+ );
911
+ } else {
912
+ appLines.push(
913
+ ` /// ${cap(camel(directive.id))}. ${businessActionDoc(directive)}`,
914
+ ` /// Canonical write path: pass business facts here; never mirror this action in application state`,
915
+ ` /// or call a lower-level client.`,
916
+ ` Future<void> ${method}(${payload} facts) async {`,
917
+ ...referenceChecks,
918
+ ...sourceTrust.prelude,
919
+ ` await ${sourceTrust.call};`,
920
+ ...afterBusinessWrite(directive),
921
+ ` }`,
922
+ ``,
923
+ );
924
+ }
925
+ continue;
926
+ }
927
+ const sourceTrust = sourceTrustAction(directive);
928
+ if (resultExpression !== undefined) {
929
+ addCollectionMember(
930
+ directive.aggregateId,
931
+ method,
932
+ [
933
+ ` /// ${cap(camel(directive.id))}. Returns every affected typed business record reference.`,
934
+ ` Future<${resultClass}> ${method}(${payload} facts) async {`,
935
+ ...referenceChecks,
936
+ ...sourceTrust.prelude,
937
+ ` final change = await ${sourceTrust.call};`,
938
+ ` return ${resultExpression};`,
939
+ ` }`,
940
+ ``,
941
+ ],
942
+ );
943
+ } else {
944
+ addCollectionMember(directive.aggregateId, method, [
945
+ ` /// ${cap(camel(directive.id))}.`,
946
+ ` Future<void> ${method}(${payload} facts) async {`,
947
+ ...referenceChecks,
948
+ ...sourceTrust.prelude,
949
+ ` await ${sourceTrust.call};`,
950
+ ` }`,
951
+ ``,
952
+ ]);
953
+ }
954
+ }
955
+
956
+ const publicQueries = ((mod.queries ?? []) as QueryDecl[]).filter(
957
+ (candidate) =>
958
+ !isFrameworkQuery(candidate) &&
959
+ (candidate as { businessBirthInternal?: unknown }).businessBirthInternal !== true,
960
+ );
961
+ const signedInSubjectKey = (query: QueryDecl): string | undefined => {
962
+ if (query.key.length !== 1) return undefined;
963
+ const key = query.key[0];
964
+ if (key === undefined) return undefined;
965
+ return key === "subject" || key.endsWith("Subject") ? key : undefined;
966
+ };
967
+ const mineQueriesByAggregate = new Map<string, number>();
968
+ for (const query of publicQueries) {
969
+ if (signedInSubjectKey(query) !== undefined) {
970
+ mineQueriesByAggregate.set(
971
+ query.returns,
972
+ (mineQueriesByAggregate.get(query.returns) ?? 0) + 1,
973
+ );
974
+ }
975
+ }
976
+ for (const query of publicQueries) {
977
+ const model = readModelClassName(query.returns);
978
+ const aggregate = aggregates.get(query.returns);
979
+ const params = query.key.map((key) => `required ${keyType(aggregate, key)} ${keyParam(key)}`).join(", ");
980
+ const args = query.key.map((key) => `${keyParam(key)}: ${keyParam(key)}`).join(", ");
981
+ const method = member(query.id);
982
+ addCollectionMember(query.returns, method, [
983
+ ` Future<List<${model}>> ${method}({${params}}) => _client.${method}(${args});`,
984
+ ` Stream<List<${model}>> watch${cap(camel(query.id))}({${params}}) => _client.watch${cap(camel(query.id))}(${args});`,
985
+ ``,
986
+ ]);
987
+ const subjectKey = signedInSubjectKey(query);
988
+ if (subjectKey !== undefined && !birthCollectionsWithMine.has(query.returns)) {
989
+ const subjectParam = keyParam(subjectKey);
990
+ const subjectQueryCount = mineQueriesByAggregate.get(query.returns) ?? 0;
991
+ const role = cap(camel(subjectKey.replace(/Subject$/, "") || "subject"));
992
+ const mineMethod = subjectQueryCount === 1 ? "mine" : `mineAs${role}`;
993
+ const watchMineMethod = subjectQueryCount === 1 ? "watchMine" : `watchMineAs${role}`;
994
+ const relationship = subjectQueryCount === 1 ? "belonging to" : `where they are the ${role.toLowerCase()}`;
995
+ addCollectionMember(query.returns, mineMethod, [
996
+ ` /// ${cap(camel(query.returns))} records ${relationship} the signed-in person.`,
997
+ ` Future<List<${model}>> ${mineMethod}() {`,
998
+ ` final subject = _client.actor;`,
999
+ ` if (subject == null || subject.trim().isEmpty) {`,
1000
+ ` throw StateError('The signed-in person is not available.');`,
1001
+ ` }`,
1002
+ ` return _client.${method}(${subjectParam}: subject);`,
1003
+ ` }`,
1004
+ ``,
1005
+ ` Stream<List<${model}>> ${watchMineMethod}() {`,
1006
+ ` final subject = _client.actor;`,
1007
+ ` if (subject == null || subject.trim().isEmpty) {`,
1008
+ ` throw StateError('The signed-in person is not available.');`,
1009
+ ` }`,
1010
+ ` return _client.watch${cap(camel(query.id))}(${subjectParam}: subject);`,
1011
+ ` }`,
1012
+ ``,
1013
+ ]);
1014
+ }
1015
+ }
1016
+
1017
+ // Reference traversal is business navigation. Same-domain, sibling-domain and
1018
+ // cross-workspace references all resolve through the bound application; the
1019
+ // private client/session/reader objects never become tenant parameters.
1020
+ for (const aggregate of mod.aggregates.filter(
1021
+ (candidate) =>
1022
+ !isFrameworkAggregate(candidate) &&
1023
+ (candidate as { businessBirthInternal?: unknown }).businessBirthInternal !== true,
1024
+ )) {
1025
+ const sourceModel = readModelClassName(aggregate.id);
1026
+ const sameDomainRefs = Object.entries(aggregate.fields).filter(([, field]) =>
1027
+ field.kind === "ref" &&
1028
+ field.foreignRef === undefined &&
1029
+ field.refAggregateId !== undefined &&
1030
+ field.refAggregateId !== aggregate.id &&
1031
+ aggregates.has(field.refAggregateId),
1032
+ );
1033
+ const crossDomainRefs = Object.entries(aggregate.fields).filter(([, field]) => {
1034
+ if (field.kind !== "ref" || field.foreignRef !== undefined || field.refAggregateId === undefined) return false;
1035
+ if (aggregates.has(field.refAggregateId)) return false;
1036
+ const owner = packageDomains[field.refAggregateId];
1037
+ return owner !== undefined && owner.domain !== domain;
1038
+ });
1039
+ for (const [fieldName, field] of Object.entries(aggregate.fields) as [string, Field][]) {
1040
+ const foreign = field.foreignRef;
1041
+ if (field.kind !== "ref") continue;
1042
+ if (foreign === undefined) {
1043
+ const targetAggregateId = field.refAggregateId;
1044
+ if (targetAggregateId === undefined) continue;
1045
+ const sourceFieldMember = member(fieldName);
1046
+ const stem = sourceFieldMember.endsWith("Id")
1047
+ ? sourceFieldMember.slice(0, -2)
1048
+ : `${sourceFieldMember}Ref`;
1049
+ const method = member(stem);
1050
+ const memberNullable = field.isFromBirth !== true;
1051
+ const isSameDomain = sameDomainRefs.some(([name]) => name === fieldName);
1052
+ const isCrossDomain = crossDomainRefs.some(([name]) => name === fieldName);
1053
+ if (!isSameDomain && !isCrossDomain) continue;
1054
+
1055
+ if (isSameDomain) {
1056
+ const targetModel = readModelClassName(targetAggregateId);
1057
+ const targetById = safe(`${lcFirst(camel(concreteClassName(targetAggregateId)))}ById`);
1058
+ addCollectionMember(aggregate.id, method, [
1059
+ ` /// Resolve [record]'s ${fieldName} inside this business application.`,
1060
+ ` Future<${targetModel}?> ${method}(${sourceModel} record) async {`,
1061
+ ...(memberNullable
1062
+ ? [
1063
+ ` final id = record.${sourceFieldMember};`,
1064
+ ` if (id == null) return null;`,
1065
+ ` final rows = await _client.${targetById}(NmsInternalNomosRefs.id(id));`,
1066
+ ]
1067
+ : [` final rows = await _client.${targetById}(NmsInternalNomosRefs.id(record.${sourceFieldMember}));`]),
1068
+ ` return rows.isEmpty ? null : rows.first;`,
1069
+ ` }`,
1070
+ ``,
1071
+ ]);
1072
+ continue;
1073
+ }
1074
+
1075
+ const owner = packageDomains[targetAggregateId];
1076
+ if (owner === undefined) continue;
1077
+ const targetModel = readModelClassName(targetAggregateId);
1078
+ const targetCollection = member(plural(concreteClassName(targetAggregateId)));
1079
+ const targetApplication = dartApplicationClassName(owner.domain);
1080
+ const opener = `nomosInternalOpen${targetApplication}`;
1081
+ // Flat output needs the sibling type file. Layered output's ordinary
1082
+ // types barrel already exports every domain; the explicit-barrel mode
1083
+ // supplies direct package imports in explicitTypeImportUris.
1084
+ if (opts.typesImportUri === undefined || explicitTypeImportUris.size > 0) {
1085
+ explicitTypeImportUris.add(typeImportUriFor(owner.name));
1086
+ }
1087
+ addSibling(`${owner.name}.application.dart`, targetApplication);
1088
+ addSibling(`${owner.name}.application.dart`, opener);
1089
+ addCollectionMember(aggregate.id, method, [
1090
+ ` /// Resolve [record]'s ${fieldName} through its sibling business application.`,
1091
+ ` Future<${targetModel}?> ${method}(${sourceModel} record) async {`,
1092
+ ...(memberNullable
1093
+ ? [
1094
+ ` final id = record.${sourceFieldMember};`,
1095
+ ` if (id == null) return null;`,
1096
+ ` final application = await ${opener}(`,
1097
+ ` _client.bridge,`,
1098
+ ` id,`,
1099
+ ` _client.actor,`,
1100
+ ` );`,
1101
+ ` return application.${targetCollection}.find(`,
1102
+ ` id,`,
1103
+ ` );`,
1104
+ ]
1105
+ : [
1106
+ ` final id = record.${sourceFieldMember};`,
1107
+ ` final application = await ${opener}(`,
1108
+ ` _client.bridge,`,
1109
+ ` id,`,
1110
+ ` _client.actor,`,
1111
+ ` );`,
1112
+ ` return application.${targetCollection}.find(`,
1113
+ ` id,`,
1114
+ ` );`,
1115
+ ]),
1116
+ ` }`,
1117
+ ``,
1118
+ ]);
1119
+ const applicationMethod = member(`${stem}Application`);
1120
+ addCollectionMember(aggregate.id, applicationMethod, [
1121
+ ` /// Continue in the sibling business application containing [record]'s ${fieldName}.`,
1122
+ ` Future<${targetApplication}?> ${applicationMethod}(${sourceModel} record) {`,
1123
+ ...(memberNullable
1124
+ ? [
1125
+ ` final id = record.${sourceFieldMember};`,
1126
+ ` if (id == null) return Future<${targetApplication}?>.value(null);`,
1127
+ ` return ${opener}(`,
1128
+ ` _client.bridge,`,
1129
+ ` id,`,
1130
+ ` _client.actor,`,
1131
+ ` );`,
1132
+ ]
1133
+ : [
1134
+ ` final id = record.${sourceFieldMember};`,
1135
+ ` return ${opener}(`,
1136
+ ` _client.bridge,`,
1137
+ ` id,`,
1138
+ ` _client.actor,`,
1139
+ ` );`,
1140
+ ]),
1141
+ ` }`,
1142
+ ``,
1143
+ ]);
1144
+ continue;
1145
+ }
1146
+ const target = foreign.targetIsContract === true
1147
+ ? contractTypeName(foreign.targetAggregateId)
1148
+ : concreteClassName(foreign.targetAggregateId);
1149
+ const fieldMember = member(fieldName);
1150
+ const stem = fieldMember.endsWith("Id") ? fieldMember.slice(0, -2) : fieldMember;
1151
+ const method = member(`${stem}Of`);
1152
+ addCollectionMember(aggregate.id, method, [
1153
+ ` /// Follow [record]'s ${fieldName} to its typed business record.`,
1154
+ ` Future<${target}?> ${method}(${sourceModel} record) async {`,
1155
+ ` final reference = record.${fieldMember};`,
1156
+ ` if (reference == null) return null;`,
1157
+ ` final target = await NmsInternalForeignRefs.resolve(reference, _client.bridge.foreignReader());`,
1158
+ ` if (target == null) return null;`,
1159
+ ` return NmsInternalRecordOrigins.remember(target, workspace: NmsInternalNomosRefs.workspace(reference.ref), type: ${JSON.stringify(foreign.targetAggregateId)}, id: NmsInternalNomosRefs.id(reference.ref));`,
1160
+ ` }`,
1161
+ ``,
1162
+ ]);
1163
+ const targetOwner = packageDomains[foreign.targetAggregateId] ??
1164
+ (foreign.targetDomain === undefined
1165
+ ? undefined
1166
+ : Object.values(packageDomains).find(
1167
+ (candidate) => candidate.domain === foreign.targetDomain,
1168
+ ));
1169
+ if (targetOwner !== undefined && targetOwner.domain !== domain) {
1170
+ const targetApplication = dartApplicationClassName(targetOwner.domain);
1171
+ const opener = `nomosInternalOpen${targetApplication}`;
1172
+ const applicationMethod = member(`${stem}ApplicationOf`);
1173
+ addSibling(`${targetOwner.name}.application.dart`, targetApplication);
1174
+ addSibling(`${targetOwner.name}.application.dart`, opener);
1175
+ addCollectionMember(aggregate.id, applicationMethod, [
1176
+ ` /// Open the business application containing [record]'s ${fieldName}.`,
1177
+ ` Future<${targetApplication}?> ${applicationMethod}(${sourceModel} record) {`,
1178
+ ` final reference = record.${fieldMember};`,
1179
+ ` if (reference == null) return Future<${targetApplication}?>.value(null);`,
1180
+ ` return ${opener}(`,
1181
+ ` _client.bridge,`,
1182
+ ` reference.ref,`,
1183
+ ` _client.actor,`,
1184
+ ` );`,
1185
+ ` }`,
1186
+ ``,
1187
+ ]);
1188
+ }
1189
+ }
1190
+ }
1191
+
1192
+ for (const count of (mod.counts ?? []).map((raw) => finishCount(raw))) {
1193
+ const method = member(count.id);
1194
+ const groupType = count.by == null ? "" : keyType(aggregates.get(count.of), count.by);
1195
+ addCollectionMember(count.of, method, [count.by == null
1196
+ ? ` Future<int> ${method}() => _client.${method}();`
1197
+ : ` Future<int> ${method}(${groupType} ${keyParam(count.by)}) => _client.${method}(${keyParam(count.by)});`]);
1198
+ }
1199
+ for (const sum of (mod.sums ?? []).map((raw) => finishSum(raw))) {
1200
+ const method = member(sum.id);
1201
+ const groupType = sum.by == null ? "" : keyType(aggregates.get(sum.of), sum.by);
1202
+ addCollectionMember(sum.of, method, [sum.by == null
1203
+ ? ` Future<int> ${method}() => _client.${method}();`
1204
+ : ` Future<int> ${method}(${groupType} ${keyParam(sum.by)}) => _client.${method}(${keyParam(sum.by)});`]);
1205
+ }
1206
+
1207
+ for (const aggregate of mod.aggregates) {
1208
+ if ((aggregate as { businessBirthInternal?: unknown }).businessBirthInternal === true) continue;
1209
+ if (isFrameworkAggregate(aggregate)) continue;
1210
+ const concrete = concreteClassName(aggregate.id);
1211
+ const model = readModelClassName(aggregate.id);
1212
+ const stem = lcFirst(camel(concrete));
1213
+ const Cap = cap(camel(concrete));
1214
+ const aggregateRefType = `NomosRef<${concrete}>`;
1215
+ const clientList = safe(`list${Cap}s`);
1216
+ addCollectionMember(aggregate.id, "fromAddress", [
1217
+ ` /// Recover a typed ${concrete} reference from its stable public address.`,
1218
+ ` ///`,
1219
+ ` /// This creates an address, not authority. [find], [watchOne] and business actions`,
1220
+ ` /// still verify the signed-in person's access when the reference is used.`,
1221
+ ` ${aggregateRefType} fromAddress(String address) {`,
1222
+ ` final reference = NmsInternalNomosRefs.fromUri<${concrete}>(address);`,
1223
+ ` _nmsRequireReference(reference, ${JSON.stringify(aggregate.id)}, '');`,
1224
+ ` return reference;`,
1225
+ ` }`,
1226
+ ``,
1227
+ ]);
1228
+ addCollectionMember(aggregate.id, "find", [
1229
+ ` Future<${model}?> find(${aggregateRefType} reference) async {`,
1230
+ ` _nmsRequireReference(reference, ${JSON.stringify(aggregate.id)}, '');`,
1231
+ ` final targetWorkspace = NmsInternalNomosRefs.workspace(reference);`,
1232
+ ` final currentWorkspace = _client.session?.workspace ?? '';`,
1233
+ ` if (currentWorkspace.isNotEmpty && targetWorkspace != currentWorkspace) {`,
1234
+ ` final application = await nomosInternalOpen${application}(`,
1235
+ ` _client.bridge, reference, _client.actor,`,
1236
+ ` );`,
1237
+ ` return application.${member(plural(concrete))}.find(reference);`,
1238
+ ` }`,
1239
+ ` final matches = await _client.${safe(`${stem}ById`)}(NmsInternalNomosRefs.id(reference));`,
1240
+ ` return matches.isEmpty ? null : matches.single;`,
1241
+ ` }`,
1242
+ ``,
1243
+ ]);
1244
+ addCollectionMember(aggregate.id, "watchOne", [
1245
+ ` Stream<${model}?> watchOne(${aggregateRefType} reference) {`,
1246
+ ` _nmsRequireReference(reference, ${JSON.stringify(aggregate.id)}, '');`,
1247
+ ` final targetWorkspace = NmsInternalNomosRefs.workspace(reference);`,
1248
+ ` final currentWorkspace = _client.session?.workspace ?? '';`,
1249
+ ` if (currentWorkspace.isNotEmpty && targetWorkspace != currentWorkspace) {`,
1250
+ ` return Stream.fromFuture(nomosInternalOpen${application}(`,
1251
+ ` _client.bridge, reference, _client.actor,`,
1252
+ ` )).asyncExpand(`,
1253
+ ` (application) => application.${member(plural(concrete))}.watchOne(reference),`,
1254
+ ` );`,
1255
+ ` }`,
1256
+ ` return _client.watch${Cap}ById(NmsInternalNomosRefs.id(reference))`,
1257
+ ` .map((matches) => matches.isEmpty ? null : matches.single);`,
1258
+ ` }`,
1259
+ ``,
1260
+ ]);
1261
+ addCollectionMember(aggregate.id, "all", [
1262
+ ` Future<List<${model}>> all() => _client.${clientList}();`,
1263
+ ``,
1264
+ ]);
1265
+ addCollectionMember(aggregate.id, "watchAll", [
1266
+ ` Stream<List<${model}>> watchAll() => _client.watchAll${Cap}s();`,
1267
+ ``,
1268
+ ]);
1269
+ }
1270
+
1271
+ const collections: string[] = [];
1272
+ const initializers: string[] = [];
1273
+ for (const [aggregateId, members] of collectionLines) {
1274
+ const aggregate = aggregates.get(aggregateId);
1275
+ const concrete = aggregate === undefined
1276
+ ? concreteClassName(aggregateId)
1277
+ : concreteClassName(aggregate.id);
1278
+ const collection = aggregate === undefined
1279
+ ? `${concrete}Collection`
1280
+ : dartAggregateCollectionClassName(aggregate);
1281
+ const property = member(plural(concrete));
1282
+ claimApplicationMember(property, `aggregate collection '${aggregateId}'`);
1283
+ appLines.push(` final ${collection} ${property};`);
1284
+ initializers.push(`${property} = ${collection}._(client)`);
1285
+ collections.push(
1286
+ `/// The canonical generated surface for ${concrete} records and navigation.`,
1287
+ `///`,
1288
+ `/// Use these records, watches and references directly. Business actions that can change`,
1289
+ `/// one or many records live on [${application}]; never wrap or mirror either surface in`,
1290
+ `/// a repository or parallel application state.`,
1291
+ `final class ${collection} {`,
1292
+ ` final ${client} _client;`,
1293
+ ` const ${collection}._(this._client);`,
1294
+ ``,
1295
+ ...members,
1296
+ `}`,
1297
+ ``,
1298
+ );
1299
+ }
1300
+
1301
+ const imports = [...siblingImports.entries()]
1302
+ .sort(([a], [b]) => a.localeCompare(b))
1303
+ .map(([file, names]) => `import '${file}' show ${[...names].sort().join(", ")};`);
1304
+ const typesImport = opts.typesImportUri ?? `${mod.name}.dart`;
1305
+ const runtimeImport = opts.supportImport === "package"
1306
+ ? "package:nomos_client/src/nms_generated_runtime.dart"
1307
+ : "nomos_client.dart";
1308
+ const applicationInitializers = [
1309
+ "_client = client",
1310
+ ...(tenantRoot ? ["currentUser = currentUser"] : []),
1311
+ `activity = ${activity}._(client)`,
1312
+ ...(resolverBindings.length > 0 ? [`conflicts = ${cap(camel(domain))}Conflicts._(client)`] : []),
1313
+ ...initializers,
1314
+ ];
1315
+ return [
1316
+ `// AUTO-GENERATED by nomos-compile — the tenant application surface for "${domain}".`,
1317
+ `// Business facts in, business records out. Runtime lifecycle is deliberately absent.`,
1318
+ ...(sharingPolicies.length > 0 ? [`import 'dart:async' show StreamSubscription, unawaited;`] : []),
1319
+ `import '${runtimeImport}' show NmsInternalForeignRefs, NmsInternalNomosRefs, NmsInternalRecordOrigins, NomosBridge, NomosOfferOutcome, NomosRef, NomosSession${usesWorkspaceName ? ", deriveWorkspaceName" : ""}${sharingPolicies.length > 0 ? ", Invitation, Recipient, SyncPhase, User" : ""}${tenantRoot ? ", NmsInternalUsers, nmsInternalReceivingGroupNameOf" : ""};`,
1320
+ ...(opts.flutterHost === true || sharingPolicies.length > 0
1321
+ ? [
1322
+ ...(tenantRoot ? [`import 'package:flutter/widgets.dart';`] : []),
1323
+ ...(sharingPolicies.length > 0
1324
+ ? [`import 'package:nomos_flutter/src/application_share.dart' show ApplicationShareResult;`]
1325
+ : []),
1326
+ ...(opts.flutterHost === true && tenantRoot
1327
+ ? [`import 'package:nomos_flutter/src/application_status.dart' show ApplicationSignIn, ApplicationStatus;`]
1328
+ : []),
1329
+ `import 'package:nomos_flutter/src/generated_application_host.dart' show NmsInternalApplicationUserBinding${tenantRoot || sharingPolicies.length > 0 ? ", NmsInternalApplicationHost" : ""}${sharingPolicies.length > 0 ? ", NmsInternalInvitationPolicy" : ""};`,
1330
+ ]
1331
+ : []),
1332
+ `import '${typesImport}';`,
1333
+ ...[...explicitTypeImportUris].sort().map((uri) => `import '${uri}';`),
1334
+ `import '${mod.name}.client.dart' show ${client};`,
1335
+ ...imports,
1336
+ ``,
1337
+ ...(tenantRoot
1338
+ ? [
1339
+ `/// The canonical stateful entry point for this business domain.`,
1340
+ ]
1341
+ : [
1342
+ `/// A business domain handle owned by the generated package application.`,
1343
+ ]),
1344
+ `///`,
1345
+ `/// Use its business actions, records, watches, status and decisions directly. Nomos owns`,
1346
+ `/// persistence, synchronization, access delivery and recovery underneath this object;`,
1347
+ `/// do not create a parallel repository, runtime host, session, or application-state copy.`,
1348
+ ...(tenantRoot
1349
+ ? [
1350
+ `///`,
1351
+ `/// Canonical bootstrap:`,
1352
+ "/// ```dart",
1353
+ `/// return ${application}.nomosWidget(`,
1354
+ `/// cloud: cloud,`,
1355
+ `/// application: '${domain}',`,
1356
+ `/// signIn: signIn,`,
1357
+ `/// builder: (context, application) => MyBusinessUi(application: application),`,
1358
+ `/// );`,
1359
+ "/// ```",
1360
+ `/// Pass that same [${application}] down. It is the application state.`,
1361
+ ]
1362
+ : [
1363
+ `///`,
1364
+ `/// Reach this object through the generated package application. Do not mount,`,
1365
+ `/// provide, cache or reconstruct it as an independent application root.`,
1366
+ ]),
1367
+ `final class ${application} {`,
1368
+ ` final ${client} _client;`,
1369
+ ...(tenantRoot ? [` final User currentUser;`] : []),
1370
+ ` final ${activity} activity;`,
1371
+ ...appLines,
1372
+ ` ${application}._(${client} client${tenantRoot ? ", User currentUser" : ""}) : ${applicationInitializers.join(", ")};`,
1373
+ ...(opts.flutterHost === true && tenantRoot
1374
+ ? [
1375
+ ``,
1376
+ ` /// Current durability and convergence in product language.`,
1377
+ ` ApplicationStatus get currentStatus => NmsInternalApplicationHost.currentStatus(_client.bridge);`,
1378
+ ``,
1379
+ ` /// React whenever this application saves, receives work, goes offline or needs attention.`,
1380
+ ` Stream<ApplicationStatus> watchStatus() => NmsInternalApplicationHost.watchStatus(_client.bridge);`,
1381
+ ``,
1382
+ ` /// Mount the one application at the UI boundary and pass [application] down.`,
1383
+ ` ///`,
1384
+ ` /// This is the only application bootstrap tenant code should own.`,
1385
+ ` ///`,
1386
+ ` /// The object supplied to [builder] is the application state. Keep it as the only`,
1387
+ ` /// business-state dependency; do not recreate its records in providers, repositories or caches.`,
1388
+ ` static Widget nomosWidget({`,
1389
+ ` Key? key,`,
1390
+ ` required String cloud,`,
1391
+ ` required String application,`,
1392
+ ` required ApplicationSignIn signIn,`,
1393
+ ` required Widget Function(BuildContext context, ${application} application) builder,`,
1394
+ ` Widget Function(BuildContext context)? loadingBuilder,`,
1395
+ ` Widget Function(BuildContext context, Object error, VoidCallback retry)? errorBuilder,`,
1396
+ ` }) => NmsInternalApplicationHost.home<${application}>(`,
1397
+ ` key: key, cloud: cloud, application: application, signIn: signIn,`,
1398
+ ` bind: nomosInternalBind${application}, builder: builder,`,
1399
+ ` loadingBuilder: loadingBuilder, errorBuilder: errorBuilder,`,
1400
+ ` );`,
1401
+ ]
1402
+ : []),
1403
+ `}`,
1404
+ ``,
1405
+ `/// The verified business history of this application, newest first.`,
1406
+ `final class ${activity} {`,
1407
+ ` final ${client} _client;`,
1408
+ ` const ${activity}._(this._client);`,
1409
+ ``,
1410
+ ` Future<List<ActivityEvent>> all({int? limit}) => _client.activityLog(limit: limit);`,
1411
+ ``,
1412
+ ` Stream<List<ActivityEvent>> watchAll() => _client.watchActivity();`,
1413
+ `}`,
1414
+ ``,
1415
+ ...actionResultDeclarations,
1416
+ ...collections,
1417
+ ...conflictDeclarations,
1418
+ `bool _nmsReferenceTypeMatches(String actual, String expected) =>`,
1419
+ ` actual == expected || actual.endsWith('.\$expected');`,
1420
+ ``,
1421
+ `void _nmsRequireReference<T>(NomosRef<T> reference, String expectedType, String workspace) {`,
1422
+ ` if (!_nmsReferenceTypeMatches(NmsInternalNomosRefs.type(reference), expectedType)) {`,
1423
+ ` throw ArgumentError.value(reference, 'reference', 'Expected a reference to \$expectedType');`,
1424
+ ` }`,
1425
+ ` if (NmsInternalNomosRefs.workspace(reference).isEmpty) {`,
1426
+ ` throw ArgumentError.value(reference, 'reference', 'A business reference must carry its workspace');`,
1427
+ ` }`,
1428
+ ` if (workspace.isNotEmpty && NmsInternalNomosRefs.workspace(reference) != workspace) {`,
1429
+ ` throw ArgumentError.value(reference, 'reference', 'This reference belongs to a different business workspace');`,
1430
+ ` }`,
1431
+ `}`,
1432
+ ``,
1433
+ ...(usesAffectedReference
1434
+ ? [
1435
+ `NomosRef<T> _nmsAffectedReference<T>(NomosOfferOutcome change, String expectedType) {`,
1436
+ ` final matches = change.affected`,
1437
+ ` .where((affected) => _nmsReferenceTypeMatches(affected.type, expectedType))`,
1438
+ ` .toList(growable: false);`,
1439
+ ` if (matches.length != 1) {`,
1440
+ ` throw StateError('The business action expected one affected \$expectedType record, received \${matches.length}');`,
1441
+ ` }`,
1442
+ ` final affected = matches.single;`,
1443
+ ` final workspace = affected.workspace.isNotEmpty ? affected.workspace : change.workspace;`,
1444
+ ` if (workspace.isEmpty) {`,
1445
+ ` throw StateError('The affected record did not carry its business workspace');`,
1446
+ ` }`,
1447
+ ` return NmsInternalNomosRefs.create<T>(workspace: workspace, type: affected.type, id: affected.id);`,
1448
+ `}`,
1449
+ ``,
1450
+ ...(usesAffectedReferences
1451
+ ? [
1452
+ `List<NomosRef<T>> _nmsAffectedReferences<T>(NomosOfferOutcome change, String expectedType) {`,
1453
+ ` final matches = change.affected`,
1454
+ ` .where((affected) => _nmsReferenceTypeMatches(affected.type, expectedType))`,
1455
+ ` .toList(growable: false);`,
1456
+ ` return matches.map((affected) {`,
1457
+ ` final workspace = affected.workspace.isNotEmpty ? affected.workspace : change.workspace;`,
1458
+ ` if (workspace.isEmpty) {`,
1459
+ ` throw StateError('An affected record did not carry its business workspace');`,
1460
+ ` }`,
1461
+ ` return NmsInternalNomosRefs.create<T>(workspace: workspace, type: affected.type, id: affected.id);`,
1462
+ ` }).toList(growable: false);`,
1463
+ `}`,
1464
+ ``,
1465
+ ]
1466
+ : []),
1467
+ ]
1468
+ : []),
1469
+ ...(opts.flutterHost === true
1470
+ ? [
1471
+ `Future<${application}> nomosInternalBind${application}(`,
1472
+ ` NomosSession session, NmsInternalApplicationUserBinding user) async {`,
1473
+ ` final application = ${application}._(`,
1474
+ ` await ${client}.bind(session, actor: user.nmsInternalActor)${tenantRoot ? "," : ""}`,
1475
+ ...(tenantRoot
1476
+ ? [` NmsInternalUsers.create(identityId: user.nmsInternalIdentityId, identityAddress: user.nmsInternalIdentityAddress),`]
1477
+ : []),
1478
+ ` );`,
1479
+ ]
1480
+ : [
1481
+ `Future<${application}> nomosInternalBind${application}(NomosSession session, String? actor) async {`,
1482
+ ` final application = ${application}._(await ${client}.bind(session, actor: actor));`,
1483
+ ]),
1484
+ ...(sharingPolicies.length > 0 ? [` await application._nmsStartWorkspaceSharing();`] : []),
1485
+ ` return application;`,
1486
+ `}`,
1487
+ ``,
1488
+ `Future<${application}> nomosInternalOpen${application}<TAggregate>(`,
1489
+ ` NomosBridge bridge, NomosRef<TAggregate> reference, String? actor) async {`,
1490
+ ...(tenantRoot
1491
+ ? [
1492
+ ` final signedIn = actor?.trim() ?? '';`,
1493
+ ` if (signedIn.isEmpty) throw StateError('Nomos could not identify the signed-in application user.');`,
1494
+ ` final application = ${application}._(`,
1495
+ ` await ${client}.open(bridge, ref: reference, actor: signedIn),`,
1496
+ ` NmsInternalUsers.create(`,
1497
+ ` identityId: nmsInternalReceivingGroupNameOf(signedIn),`,
1498
+ ` identityAddress: 'nomos-identity:\${Uri.encodeComponent(nmsInternalReceivingGroupNameOf(signedIn))}',`,
1499
+ ` ),`,
1500
+ ` );`,
1501
+ ]
1502
+ : [` final application = ${application}._(await ${client}.open(bridge, ref: reference, actor: actor));`]),
1503
+ ...(sharingPolicies.length > 0 ? [` await application._nmsStartWorkspaceSharing();`] : []),
1504
+ ` return application;`,
1505
+ `}`,
1506
+ ``,
1507
+ ].join("\n");
1508
+ }