@githolon/dsl 0.107.4 → 0.108.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dart/CHANGELOG.md +24 -0
- package/dart/lib/src/application_framework_calls.dart +299 -0
- package/dart/lib/src/generated_call.dart +96 -0
- package/dart/lib/src/nms_generated_runtime.dart +29 -0
- package/dart/lib/src/node_runner_transport.dart +77 -1
- package/dart/lib/src/result_usda.dart +487 -0
- package/dart/lib/src/runner/node-entry.mjs +10634 -6122
- package/dart/lib/src/runner/offline-install.mjs +4187 -2716
- package/dart/lib/src/runtime_v2_gateway.dart +54 -0
- package/dart/lib/src/runtime_v2_transport.dart +117 -0
- package/dart/lib/src/session_bound.dart +7 -2
- package/dart/lib/src/test_harness.dart +373 -282
- package/dart/lib/src/webview_bridge.dart +2146 -725
- package/dart/pubspec.lock +17 -1
- package/dart/pubspec.yaml +3 -2
- package/dart/test/acquire_session_credentials_test.dart +1 -1
- package/dart/test/acquire_session_test.dart +68 -14
- package/dart/test/adv_realm_interference_test.dart +6 -4
- package/dart/test/cluster_births_test.dart +11 -8
- package/dart/test/cluster_local_autosync_test.dart +1 -1
- package/dart/test/cluster_scale_test.dart +3 -2
- package/dart/test/dlq_session_surface_test.dart +52 -35
- package/dart/test/first_app_gauntlet_test.dart +1 -1
- package/dart/test/local_sync_inert_test.dart +23 -26
- package/dart/test/offer_authentication_proof_test.dart +193 -0
- package/dart/test/result_usda_test.dart +49 -0
- package/dart/test/session_bound_test.dart +8 -11
- package/dart/test/stale_bundle_test.dart +18 -3
- package/dart/test/test_harness_test.dart +14 -27
- package/dart/test/webview_bridge_test.dart +504 -83
- package/dart_types/CHANGELOG.md +17 -0
- package/dart_types/lib/nomos_kernel_protocol.dart +11 -0
- package/dart_types/lib/src/custody.dart +64 -19
- package/dart_types/lib/src/local_work_rejected.dart +30 -45
- package/dart_types/lib/src/proto/nomos/custody/v1/snapshot.pb.dart +169 -0
- package/dart_types/lib/src/proto/nomos/custody/v1/snapshot.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +17945 -0
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +933 -0
- package/dart_types/lib/src/proto/nomos/kernel/v1/progress.pb.dart +245 -0
- package/dart_types/lib/src/proto/nomos/kernel/v1/progress.pbenum.dart +160 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +8664 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pbenum.dart +369 -0
- package/dart_types/lib/src/surfaced.dart +30 -25
- package/dart_types/pubspec.lock +16 -0
- package/dart_types/pubspec.yaml +3 -1
- package/dart_types/test/surfaced_test.dart +2 -1
- package/package.json +1 -1
- package/runtime/generated-application-host.js +5760 -1879
- package/src/authoring.ts +3 -2
- package/src/birth_meter_ensure.ts +2 -0
- package/src/births.ts +21 -0
- package/src/build_package.ts +30 -0
- package/src/business_birth.ts +41 -35
- package/src/codegen_call_usda.ts +1111 -0
- package/src/codegen_dart.ts +207 -282
- package/src/codegen_dart_application.ts +195 -252
- package/src/codegen_dart_client.ts +1978 -593
- package/src/codegen_dart_framework_calls.ts +291 -0
- package/src/codegen_dart_scenario.ts +7 -0
- package/src/codegen_proof.ts +3 -4
- package/src/codegen_provider_dart.ts +21 -7
- package/src/codegen_query_usda.ts +214 -0
- package/src/codegen_result_usda.ts +685 -0
- package/src/codegen_ts.ts +3945 -1111
- package/src/compile_package_main.ts +392 -46
- package/src/directive.ts +19 -4
- package/src/engine_entry.ts +45 -4
- package/src/framework/application_home.ts +4 -0
- package/src/framework/auth_provider_law.ts +296 -20
- package/src/framework/birth_certificate_law.ts +24 -0
- package/src/framework/home.ts +83 -40
- package/src/framework/owned_custody.ts +25 -12
- package/src/framework/owned_custody_value.ts +51 -0
- package/src/framework/relations.ts +47 -21
- package/src/framework/signing_identity.ts +100 -17
- package/src/framework/workspaces.ts +39 -10
- package/src/framework_call_definitions.ts +53 -0
- package/src/framework_read_definitions.ts +45 -0
- package/src/identity_frontier.ts +58 -0
- package/src/index.ts +22 -1
- package/src/intent.ts +216 -16
- package/src/law_check.ts +19 -1
- package/src/law_graph.ts +57 -8
- package/src/law_graph_usda.ts +73 -5
- package/src/law_value_type.ts +106 -19
- package/src/read.ts +47 -0
- package/src/read_projection_namespace.ts +7 -0
- package/src/shape_compat.ts +44 -42
- package/src/stable_ids.ts +37 -2
- package/src/test_call_usda.ts +83 -0
- package/src/wire.ts +36 -2
- package/src/wire_encode.ts +45 -1
- package/src/workspace_sharing.ts +27 -10
|
@@ -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},`]),
|
package/src/codegen_proof.ts
CHANGED
|
@@ -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
|
|
2232
|
-
//
|
|
2233
|
-
|
|
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.
|
|
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 "
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
+
}
|