@githolon/dsl 0.108.0 → 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 +5 -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 +6401 -2102
- package/dart/lib/src/runner/offline-install.mjs +1538 -163
- 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 +193 -112
- package/dart/lib/src/webview_bridge.dart +2120 -569
- package/dart/pubspec.yaml +2 -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 +7 -0
- package/dart_types/lib/nomos_kernel_protocol.dart +3 -2
- 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 +2021 -234
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +175 -8
- 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.yaml +1 -1
- package/dart_types/test/surfaced_test.dart +2 -1
- package/package.json +1 -1
- package/runtime/generated-application-host.js +5736 -2047
- package/src/authoring.ts +3 -2
- package/src/births.ts +21 -0
- package/src/build_package.ts +30 -0
- package/src/business_birth.ts +39 -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 +360 -32
- package/src/directive.ts +19 -4
- package/src/engine_entry.ts +45 -4
- package/src/framework/application_home.ts +2 -0
- package/src/framework/auth_provider_law.ts +262 -20
- package/src/framework/home.ts +32 -28
- package/src/framework/owned_custody.ts +19 -11
- package/src/framework/owned_custody_value.ts +51 -0
- package/src/framework/relations.ts +47 -21
- package/src/framework/signing_identity.ts +34 -0
- package/src/framework/workspaces.ts +31 -9
- 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 +20 -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 +38 -39
- 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
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbjson.dart +0 -6096
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbserver.dart +0 -50
- package/dart_types/lib/src/proto/nomos/kernel/v1/progress.pbjson.dart +0 -178
- package/dart_types/lib/src/proto/nomos/runtime/v1/runtime.pb.dart +0 -828
- package/dart_types/lib/src/proto/nomos/runtime/v1/runtime.pbenum.dart +0 -465
- package/dart_types/lib/src/proto/nomos/runtime/v1/runtime.pbjson.dart +0 -573
- package/dart_types/lib/src/proto/nomos/runtime/v1/runtime.pbserver.dart +0 -50
|
@@ -0,0 +1,1111 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
LawAnonymousValueFieldNode,
|
|
3
|
+
LawAnonymousValueTypeNode,
|
|
4
|
+
LawAnonymousValueVariantNode,
|
|
5
|
+
LawGraph,
|
|
6
|
+
LawIntentNode,
|
|
7
|
+
LawPayloadContractNode,
|
|
8
|
+
LawStructuralRef,
|
|
9
|
+
} from "./law_graph.js";
|
|
10
|
+
import { dartMemberName, unionArmClassName } from "./codegen_dart.js";
|
|
11
|
+
|
|
12
|
+
export interface TsCallUsdaEncoder {
|
|
13
|
+
readonly functionName: string;
|
|
14
|
+
readonly source: readonly string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface TsCallUsdaFieldAccess {
|
|
18
|
+
readonly value: string;
|
|
19
|
+
readonly present?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface DartCallUsdaFieldAccess {
|
|
23
|
+
/** Typed Dart expression for this canonical payload field. */
|
|
24
|
+
readonly value: string;
|
|
25
|
+
/** Whether an optional/defaulted field is present. Required fields omit this. */
|
|
26
|
+
readonly present?: string;
|
|
27
|
+
/** The expression is an exhaustive NomosSlot rather than its contained value. */
|
|
28
|
+
readonly slot?: true;
|
|
29
|
+
/** Compile-time proof that an optional framework field is absent. */
|
|
30
|
+
readonly omit?: true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface DartCallUsdaEncoder {
|
|
34
|
+
readonly functionName: string;
|
|
35
|
+
readonly source: readonly string[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const symbol = (value: string): string =>
|
|
39
|
+
value.replace(/[^A-Za-z0-9_$]+/g, "_").replace(/^[^A-Za-z_$]/, "_$&");
|
|
40
|
+
|
|
41
|
+
const requiredPath = (paths: ReadonlyMap<string, string>, id: string, what: string): string => {
|
|
42
|
+
const path = paths.get(id);
|
|
43
|
+
if (path === undefined) throw new Error(`call USDA codegen: ${what} '${id}' has no rendered law path`);
|
|
44
|
+
return path;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const pathLeaf = (path: string): string => path.slice(path.lastIndexOf("/") + 1);
|
|
48
|
+
|
|
49
|
+
interface NominalSumArm {
|
|
50
|
+
readonly discriminator: string;
|
|
51
|
+
readonly variant: LawAnonymousValueVariantNode;
|
|
52
|
+
readonly value: LawAnonymousValueTypeNode;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Resolve only the DSL's nominal `variant(...)` shape. Arbitrary structural
|
|
56
|
+
* unions remain refused: a generated call must have one stable discriminator,
|
|
57
|
+
* one exact law variant identity and one statically known arm type. */
|
|
58
|
+
function nominalSumArms(
|
|
59
|
+
nodes: ReadonlyMap<string, LawGraph["nodes"][number]>,
|
|
60
|
+
sum: LawAnonymousValueTypeNode,
|
|
61
|
+
where: string,
|
|
62
|
+
): { readonly name: string; readonly arms: readonly NominalSumArm[] } {
|
|
63
|
+
if (sum.expression.kind !== "sum" || sum.expression.semantic?.kind !== "value-object") {
|
|
64
|
+
throw new Error(
|
|
65
|
+
`${where} contains a structural sum; declare it with variant(...) so its discriminator and arms are nominal`,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
const arms = sum.expression.variants.map((ref): NominalSumArm => {
|
|
69
|
+
const variant = nodes.get(ref.id);
|
|
70
|
+
if (variant?.kind !== "structural" || variant.structuralKind !== "value-variant") {
|
|
71
|
+
throw new Error(`${where} references non-variant '${ref.id}'`);
|
|
72
|
+
}
|
|
73
|
+
const value = nodes.get(variant.value.id);
|
|
74
|
+
if (value?.kind !== "structural" || value.structuralKind !== "value-type" || value.expression.kind !== "record") {
|
|
75
|
+
throw new Error(`${where} variant '${variant.entity.label}' is not a nominal record arm`);
|
|
76
|
+
}
|
|
77
|
+
const discriminator = value.expression.fields.flatMap((fieldRef) => {
|
|
78
|
+
const field = nodes.get(fieldRef.id);
|
|
79
|
+
if (
|
|
80
|
+
field?.kind !== "structural" ||
|
|
81
|
+
field.structuralKind !== "value-field" ||
|
|
82
|
+
field.name !== "type" ||
|
|
83
|
+
field.required !== true
|
|
84
|
+
) return [];
|
|
85
|
+
const type = nodes.get(field.value.id);
|
|
86
|
+
return type?.kind === "structural" &&
|
|
87
|
+
type.structuralKind === "value-type" &&
|
|
88
|
+
type.expression.kind === "literal" &&
|
|
89
|
+
typeof type.expression.value === "string"
|
|
90
|
+
? [type.expression.value]
|
|
91
|
+
: [];
|
|
92
|
+
});
|
|
93
|
+
if (discriminator.length !== 1) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`${where} variant '${variant.entity.label}' does not carry one required literal 'type' discriminator`,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
if (variant.label !== undefined && variant.label !== discriminator[0]) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`${where} variant '${variant.label}' disagrees with discriminator '${discriminator[0]}'`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
return { discriminator: discriminator[0]!, variant, value };
|
|
104
|
+
});
|
|
105
|
+
const discriminators = new Set(arms.map((arm) => arm.discriminator));
|
|
106
|
+
if (arms.length === 0 || discriminators.size !== arms.length) {
|
|
107
|
+
throw new Error(`${where} must carry at least one uniquely discriminated nominal variant arm`);
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
name: sum.expression.semantic.name,
|
|
111
|
+
arms: [...arms].sort((left, right) => left.discriminator.localeCompare(right.discriminator)),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Shared USDA syntax helpers emitted once into a generated TypeScript client.
|
|
116
|
+
* They write only OpenUSD syntax primitives; domain shape remains expanded into schema-specific
|
|
117
|
+
* functions by {@link emitTsCallUsdaEncoder}. */
|
|
118
|
+
export const TS_CALL_USDA_PRELUDE = [
|
|
119
|
+
`declare const _nmsGeneratedCallBrand: unique symbol;`,
|
|
120
|
+
`interface NmsGeneratedCall<Domain extends string = string, Directive extends string = string> {`,
|
|
121
|
+
` readonly domain: Domain;`,
|
|
122
|
+
` readonly directive: Directive;`,
|
|
123
|
+
` readonly canonicalUsda: Uint8Array;`,
|
|
124
|
+
` readonly routingCandidates: readonly string[];`,
|
|
125
|
+
` readonly [_nmsGeneratedCallBrand]: readonly [Domain, Directive];`,
|
|
126
|
+
`}`,
|
|
127
|
+
`const _nmsGeneratedCall = <Domain extends string, Directive extends string>(domain: Domain, directive: Directive, canonicalUsda: Uint8Array, routingCandidates: readonly string[]): NmsGeneratedCall<Domain, Directive> =>`,
|
|
128
|
+
` ({ domain, directive, canonicalUsda, routingCandidates: [...new Set(routingCandidates)] }) as unknown as NmsGeneratedCall<Domain, Directive>;`,
|
|
129
|
+
``,
|
|
130
|
+
`declare const _nmsGeneratedQueryBrand: unique symbol;`,
|
|
131
|
+
`interface NmsGeneratedQuery<Domain extends string = string, Query extends string = string> {`,
|
|
132
|
+
` readonly domain: Domain;`,
|
|
133
|
+
` readonly query: Query;`,
|
|
134
|
+
` readonly canonicalArgumentsUsda: Uint8Array;`,
|
|
135
|
+
` readonly [_nmsGeneratedQueryBrand]: readonly [Domain, Query];`,
|
|
136
|
+
`}`,
|
|
137
|
+
`const _nmsGeneratedQuery = <Domain extends string, Query extends string>(domain: Domain, query: Query, canonicalArgumentsUsda: Uint8Array): NmsGeneratedQuery<Domain, Query> =>`,
|
|
138
|
+
` ({ domain, query, canonicalArgumentsUsda }) as NmsGeneratedQuery<Domain, Query>;`,
|
|
139
|
+
``,
|
|
140
|
+
// The same generated call encoder runs in browsers, Dart's JS runner and the
|
|
141
|
+
// sealed QuickJS engine. TextEncoder is a browser/Node convenience, not an
|
|
142
|
+
// ECMAScript primitive, so keep UTF-8 lowering inside the generated,
|
|
143
|
+
// deterministic transport instead of making host globals part of law.
|
|
144
|
+
`const _nmsUsdaUtf8 = (value: string): Uint8Array => {`,
|
|
145
|
+
` const bytes: number[] = [];`,
|
|
146
|
+
` for (let index = 0; index < value.length; index += 1) {`,
|
|
147
|
+
` let point = value.charCodeAt(index);`,
|
|
148
|
+
` if (point >= 0xd800 && point <= 0xdbff && index + 1 < value.length) {`,
|
|
149
|
+
` const low = value.charCodeAt(index + 1);`,
|
|
150
|
+
` if (low >= 0xdc00 && low <= 0xdfff) { point = 0x10000 + ((point - 0xd800) << 10) + (low - 0xdc00); index += 1; }`,
|
|
151
|
+
` }`,
|
|
152
|
+
` if (point <= 0x7f) bytes.push(point);`,
|
|
153
|
+
` else if (point <= 0x7ff) bytes.push(0xc0 | (point >> 6), 0x80 | (point & 0x3f));`,
|
|
154
|
+
` else if (point <= 0xffff) bytes.push(0xe0 | (point >> 12), 0x80 | ((point >> 6) & 0x3f), 0x80 | (point & 0x3f));`,
|
|
155
|
+
` else bytes.push(0xf0 | (point >> 18), 0x80 | ((point >> 12) & 0x3f), 0x80 | ((point >> 6) & 0x3f), 0x80 | (point & 0x3f));`,
|
|
156
|
+
` }`,
|
|
157
|
+
` return Uint8Array.from(bytes);`,
|
|
158
|
+
`};`,
|
|
159
|
+
`const _nmsUsdaQuote = (value: string): string => {`,
|
|
160
|
+
` let out = "\\\"";`,
|
|
161
|
+
` for (const character of value) {`,
|
|
162
|
+
` const point = character.codePointAt(0)!;`,
|
|
163
|
+
` if (character === "\\\"") out += "\\\\\\\"";`,
|
|
164
|
+
` else if (character === "\\\\") out += "\\\\\\\\";`,
|
|
165
|
+
` else if (character === "\\n") out += "\\\\n";`,
|
|
166
|
+
` else if (character === "\\r") out += "\\\\r";`,
|
|
167
|
+
` else if (character === "\\t") out += "\\\\t";`,
|
|
168
|
+
` else if (character === "\\b") out += "\\\\b";`,
|
|
169
|
+
` else if (character === "\\f") out += "\\\\f";`,
|
|
170
|
+
` else if (point < 0x20 || (point >= 0x7f && point <= 0x9f)) out += "\\\\u" + point.toString(16).padStart(4, "0");`,
|
|
171
|
+
` else out += character;`,
|
|
172
|
+
` }`,
|
|
173
|
+
` return out + "\\\"";`,
|
|
174
|
+
`};`,
|
|
175
|
+
`const _nmsUsdaLine = (out: string[], depth: number, value: string): void => { out.push(" ".repeat(depth) + value); };`,
|
|
176
|
+
`const _nmsUsdaBegin = (out: string[], depth: number, type: string, path: string): void => {`,
|
|
177
|
+
` const name = path.slice(path.lastIndexOf("/") + 1);`,
|
|
178
|
+
` _nmsUsdaLine(out, depth, "def " + type + " " + _nmsUsdaQuote(name));`,
|
|
179
|
+
` _nmsUsdaLine(out, depth, "{");`,
|
|
180
|
+
`};`,
|
|
181
|
+
`const _nmsUsdaRel = (out: string[], depth: number, name: string, target: string): void =>`,
|
|
182
|
+
` _nmsUsdaLine(out, depth, "custom rel " + name + " = <" + target + ">");`,
|
|
183
|
+
`const _nmsUsdaString = (out: string[], depth: number, type: "string" | "token", name: string, value: string): void =>`,
|
|
184
|
+
` _nmsUsdaLine(out, depth, "custom uniform " + type + " " + name + " = " + _nmsUsdaQuote(value));`,
|
|
185
|
+
`const _nmsUsdaScalar = (out: string[], depth: number, type: "bool" | "int64" | "double", name: string, value: string): void =>`,
|
|
186
|
+
` _nmsUsdaLine(out, depth, "custom uniform " + type + " " + name + " = " + value);`,
|
|
187
|
+
`const _nmsUsdaClose = (out: string[], depth: number): void => _nmsUsdaLine(out, depth, "}");`,
|
|
188
|
+
`const _nmsUsdaChildren = (out: string[], children: string[]): void => { if (children.length > 0) { out.push(""); out.push(...children); } };`,
|
|
189
|
+
`const _nmsUsdaDouble = (value: number): string => {`,
|
|
190
|
+
` if (!Number.isFinite(value)) throw new TypeError("Nomos USDA numbers must be finite");`,
|
|
191
|
+
` return Number.isInteger(value) ? value.toFixed(1) : String(value);`,
|
|
192
|
+
`};`,
|
|
193
|
+
`const _nmsUsdaInt = (value: number): string => {`,
|
|
194
|
+
` if (!Number.isSafeInteger(value)) throw new TypeError("Nomos USDA integers must be safe integers");`,
|
|
195
|
+
` return String(value);`,
|
|
196
|
+
`};`,
|
|
197
|
+
``,
|
|
198
|
+
] as const;
|
|
199
|
+
|
|
200
|
+
/** OpenUSD syntax primitives for generated Dart clients. These helpers never
|
|
201
|
+
* inspect domain values: every value access is expanded into a directive-
|
|
202
|
+
* specific encoder by {@link emitDartCallUsdaEncoder}. */
|
|
203
|
+
export const DART_CALL_USDA_PRELUDE = [
|
|
204
|
+
`String _nmsUsdaQuote(String value) {`,
|
|
205
|
+
` final out = StringBuffer('"');`,
|
|
206
|
+
` for (final point in value.runes) {`,
|
|
207
|
+
` switch (point) {`,
|
|
208
|
+
` case 0x22: out.write(r'\\"');`,
|
|
209
|
+
` case 0x5c: out.write(r'\\\\');`,
|
|
210
|
+
` case 0x0a: out.write(r'\\n');`,
|
|
211
|
+
` case 0x0d: out.write(r'\\r');`,
|
|
212
|
+
` case 0x09: out.write(r'\\t');`,
|
|
213
|
+
` case 0x08: out.write(r'\\b');`,
|
|
214
|
+
` case 0x0c: out.write(r'\\f');`,
|
|
215
|
+
` default:`,
|
|
216
|
+
` if (point < 0x20 || (point >= 0x7f && point <= 0x9f)) {`,
|
|
217
|
+
` out.write('\\\\u\${point.toRadixString(16).padLeft(4, '0')}');`,
|
|
218
|
+
` } else {`,
|
|
219
|
+
` out.write(String.fromCharCode(point));`,
|
|
220
|
+
` }`,
|
|
221
|
+
` }`,
|
|
222
|
+
` }`,
|
|
223
|
+
` return (out..write('"')).toString();`,
|
|
224
|
+
`}`,
|
|
225
|
+
`void _nmsUsdaLine(List<String> out, int depth, String value) => out.add(' ' * depth + value);`,
|
|
226
|
+
`void _nmsUsdaBegin(List<String> out, int depth, String type, String path) {`,
|
|
227
|
+
` final name = path.substring(path.lastIndexOf('/') + 1);`,
|
|
228
|
+
` _nmsUsdaLine(out, depth, 'def $type \${_nmsUsdaQuote(name)}');`,
|
|
229
|
+
` _nmsUsdaLine(out, depth, '{');`,
|
|
230
|
+
`}`,
|
|
231
|
+
`void _nmsUsdaRel(List<String> out, int depth, String name, String target) =>`,
|
|
232
|
+
` _nmsUsdaLine(out, depth, 'custom rel $name = <$target>');`,
|
|
233
|
+
`void _nmsUsdaString(List<String> out, int depth, String type, String name, String value) =>`,
|
|
234
|
+
` _nmsUsdaLine(out, depth, 'custom uniform $type $name = \${_nmsUsdaQuote(value)}');`,
|
|
235
|
+
`void _nmsUsdaScalar(List<String> out, int depth, String type, String name, String value) =>`,
|
|
236
|
+
` _nmsUsdaLine(out, depth, 'custom uniform $type $name = $value');`,
|
|
237
|
+
`void _nmsUsdaClose(List<String> out, int depth) => _nmsUsdaLine(out, depth, '}');`,
|
|
238
|
+
`void _nmsUsdaChildren(List<String> out, List<String> children) {`,
|
|
239
|
+
` if (children.isEmpty) return;`,
|
|
240
|
+
` out..add('')..addAll(children);`,
|
|
241
|
+
`}`,
|
|
242
|
+
`String _nmsUsdaDouble(double value) {`,
|
|
243
|
+
` if (!value.isFinite) throw ArgumentError.value(value, 'value', 'Nomos USDA numbers must be finite');`,
|
|
244
|
+
` return value == value.truncateToDouble() ? value.toStringAsFixed(1) : value.toString();`,
|
|
245
|
+
`}`,
|
|
246
|
+
`String _nmsUsdaInt(num value) {`,
|
|
247
|
+
` if (!value.isFinite || value != value.truncateToDouble()) {`,
|
|
248
|
+
` throw ArgumentError.value(value, 'value', 'Nomos USDA integers must be finite whole numbers');`,
|
|
249
|
+
` }`,
|
|
250
|
+
` return value.toInt().toString();`,
|
|
251
|
+
`}`,
|
|
252
|
+
``,
|
|
253
|
+
] as const;
|
|
254
|
+
|
|
255
|
+
/** Emit one directive-specific encoder directly from the same graph/path projection as law.usda.
|
|
256
|
+
* No schema descriptor, recursive value bag or runtime field-name lookup survives into the client. */
|
|
257
|
+
export function emitTsCallUsdaEncoder(
|
|
258
|
+
graph: LawGraph,
|
|
259
|
+
paths: ReadonlyMap<string, string>,
|
|
260
|
+
domain: string,
|
|
261
|
+
directive: string,
|
|
262
|
+
payloadType: string,
|
|
263
|
+
access?: ReadonlyMap<string, TsCallUsdaFieldAccess>,
|
|
264
|
+
): TsCallUsdaEncoder {
|
|
265
|
+
const nodes = new Map<string, LawGraph["nodes"][number]>(
|
|
266
|
+
graph.nodes.map((node) => [node.entity.id, node]),
|
|
267
|
+
);
|
|
268
|
+
const intent = graph.nodes.find((node): node is LawIntentNode =>
|
|
269
|
+
node.kind === "intent" && (node.entity.label === directive || node.publicRoute === directive));
|
|
270
|
+
if (intent === undefined) {
|
|
271
|
+
throw new Error(`call USDA codegen: domain '${domain}' has no law intent for '${directive}'`);
|
|
272
|
+
}
|
|
273
|
+
const contract = nodes.get(intent.accepts.id);
|
|
274
|
+
if (contract?.kind !== "payload-contract") {
|
|
275
|
+
throw new Error(`call USDA codegen: '${domain}/${directive}' accepts no payload contract`);
|
|
276
|
+
}
|
|
277
|
+
const functionName = `_nmsCallUsda_${symbol(domain)}_${symbol(directive)}`;
|
|
278
|
+
const intentPath = requiredPath(paths, intent.entity.id, "intent");
|
|
279
|
+
const contractPath = requiredPath(paths, contract.entity.id, "contract");
|
|
280
|
+
let local = 0;
|
|
281
|
+
const fresh = (prefix: string): string => `_${prefix}${local++}`;
|
|
282
|
+
|
|
283
|
+
const fieldNode = (ref: LawStructuralRef<"value-field">): LawAnonymousValueFieldNode => {
|
|
284
|
+
const node = nodes.get(ref.id);
|
|
285
|
+
if (node?.kind !== "structural" || node.structuralKind !== "value-field") {
|
|
286
|
+
throw new Error(`call USDA codegen: '${ref.id}' is not a value field`);
|
|
287
|
+
}
|
|
288
|
+
return node;
|
|
289
|
+
};
|
|
290
|
+
const typeNode = (id: string): LawAnonymousValueTypeNode => {
|
|
291
|
+
const node = nodes.get(id);
|
|
292
|
+
if (node?.kind !== "structural" || node.structuralKind !== "value-type") {
|
|
293
|
+
throw new Error(`call USDA codegen: '${id}' is not an anonymous value type`);
|
|
294
|
+
}
|
|
295
|
+
return node;
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
const emitValue = (
|
|
299
|
+
out: string[],
|
|
300
|
+
value: string,
|
|
301
|
+
valuePath: string,
|
|
302
|
+
typeId: string,
|
|
303
|
+
fieldPath: string | undefined,
|
|
304
|
+
depth: number,
|
|
305
|
+
valueIsCanonical = false,
|
|
306
|
+
): void => {
|
|
307
|
+
const node = typeNode(typeId);
|
|
308
|
+
const typePath = requiredPath(paths, typeId, "value type");
|
|
309
|
+
const semantic = node.expression.semantic;
|
|
310
|
+
const fieldLabel = fieldPath ?? typePath;
|
|
311
|
+
const lowersNominalReference = semantic?.kind === "id-reference" ||
|
|
312
|
+
semantic?.kind === "address-reference" ||
|
|
313
|
+
semantic?.kind === "versioned-reference" ||
|
|
314
|
+
semantic?.kind === "recipient";
|
|
315
|
+
const canonicalValue = valueIsCanonical || semantic === undefined
|
|
316
|
+
? value
|
|
317
|
+
: semantic.kind === "id-reference"
|
|
318
|
+
? `_nmsReferenceId(${value}, ${JSON.stringify(semantic.target)}, nmsInternalWorkspace, ${JSON.stringify(fieldLabel)})`
|
|
319
|
+
: semantic.kind === "address-reference"
|
|
320
|
+
? `_nmsCheckedReferenceData(${value}, ${JSON.stringify(semantic.target)}, "", ${JSON.stringify(fieldLabel)})`
|
|
321
|
+
: semantic.kind === "versioned-reference"
|
|
322
|
+
? `({ pinned: _nmsReferenceData(${value}.pinned), series: _nmsReferenceData(${value}.series) })`
|
|
323
|
+
: semantic.kind === "recipient"
|
|
324
|
+
? `_nmsRecipientAddress(${value}, ${JSON.stringify(fieldLabel)})`
|
|
325
|
+
: value;
|
|
326
|
+
const begin = (primType: string): void => {
|
|
327
|
+
out.push(`_nmsUsdaBegin(${outName}, ${depth}, ${JSON.stringify(primType)}, ${valuePath});`);
|
|
328
|
+
if (fieldPath !== undefined) {
|
|
329
|
+
out.push(`_nmsUsdaRel(${outName}, ${depth + 1}, "nomos:field", ${JSON.stringify(fieldPath)});`);
|
|
330
|
+
}
|
|
331
|
+
out.push(`_nmsUsdaRel(${outName}, ${depth + 1}, "nomos:type", ${JSON.stringify(typePath)});`);
|
|
332
|
+
};
|
|
333
|
+
const outName = fresh("out");
|
|
334
|
+
// Each value writes into its own lexical alias. This lets nested records/lists collect children
|
|
335
|
+
// without a generic tree representation and keeps every generated field access explicit.
|
|
336
|
+
out.push(`{ const ${outName} = ${currentOut};`);
|
|
337
|
+
switch (node.expression.kind) {
|
|
338
|
+
case "default":
|
|
339
|
+
emitValue(out, canonicalValue, valuePath, node.expression.value.id, fieldPath, depth, valueIsCanonical);
|
|
340
|
+
break;
|
|
341
|
+
case "recursive":
|
|
342
|
+
throw new Error(
|
|
343
|
+
`call USDA codegen: '${domain}/${directive}' contains recursive values; ` +
|
|
344
|
+
"add a schema-specific bounded representation before release",
|
|
345
|
+
);
|
|
346
|
+
case "nullable":
|
|
347
|
+
{
|
|
348
|
+
const nullable = fresh("nullable");
|
|
349
|
+
out.push(`const ${nullable} = ${canonicalValue};`);
|
|
350
|
+
out.push(`if (${nullable} === null) {`);
|
|
351
|
+
begin("NomosNullValue");
|
|
352
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
353
|
+
out.push(`} else {`);
|
|
354
|
+
emitValue(
|
|
355
|
+
out,
|
|
356
|
+
nullable,
|
|
357
|
+
valuePath,
|
|
358
|
+
node.expression.value.id,
|
|
359
|
+
fieldPath,
|
|
360
|
+
depth,
|
|
361
|
+
valueIsCanonical,
|
|
362
|
+
);
|
|
363
|
+
out.push(`}`);
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
case "string":
|
|
367
|
+
out.push(`_nmsRoutingCandidates.push(${canonicalValue});`);
|
|
368
|
+
begin("NomosStringValue");
|
|
369
|
+
out.push(`_nmsUsdaString(${outName}, ${depth + 1}, "string", "nomos:value", ${canonicalValue});`);
|
|
370
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
371
|
+
break;
|
|
372
|
+
case "enum":
|
|
373
|
+
begin("NomosTokenValue");
|
|
374
|
+
out.push(`_nmsUsdaString(${outName}, ${depth + 1}, "token", "nomos:value", ${canonicalValue});`);
|
|
375
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
376
|
+
break;
|
|
377
|
+
case "integer":
|
|
378
|
+
begin("NomosIntValue");
|
|
379
|
+
out.push(`_nmsUsdaScalar(${outName}, ${depth + 1}, "int64", "nomos:value", _nmsUsdaInt(${canonicalValue}));`);
|
|
380
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
381
|
+
break;
|
|
382
|
+
case "number":
|
|
383
|
+
begin("NomosNumberValue");
|
|
384
|
+
out.push(`_nmsUsdaScalar(${outName}, ${depth + 1}, "double", "nomos:value", _nmsUsdaDouble(${canonicalValue}));`);
|
|
385
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
386
|
+
break;
|
|
387
|
+
case "boolean":
|
|
388
|
+
begin("NomosBoolValue");
|
|
389
|
+
out.push(`_nmsUsdaScalar(${outName}, ${depth + 1}, "bool", "nomos:value", ${canonicalValue} ? "true" : "false");`);
|
|
390
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
391
|
+
break;
|
|
392
|
+
case "null":
|
|
393
|
+
begin("NomosNullValue");
|
|
394
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
395
|
+
break;
|
|
396
|
+
case "literal": {
|
|
397
|
+
const literal = node.expression.value;
|
|
398
|
+
if (typeof literal === "string") {
|
|
399
|
+
out.push(`_nmsRoutingCandidates.push(${JSON.stringify(literal)});`);
|
|
400
|
+
begin("NomosStringValue");
|
|
401
|
+
out.push(`_nmsUsdaString(${outName}, ${depth + 1}, "string", "nomos:value", ${JSON.stringify(literal)});`);
|
|
402
|
+
} else if (typeof literal === "boolean") {
|
|
403
|
+
begin("NomosBoolValue");
|
|
404
|
+
out.push(`_nmsUsdaScalar(${outName}, ${depth + 1}, "bool", "nomos:value", ${literal ? '"true"' : '"false"'});`);
|
|
405
|
+
} else if (typeof literal === "number" && Number.isInteger(literal)) {
|
|
406
|
+
begin("NomosIntValue");
|
|
407
|
+
out.push(`_nmsUsdaScalar(${outName}, ${depth + 1}, "int64", "nomos:value", _nmsUsdaInt(${literal}));`);
|
|
408
|
+
} else if (typeof literal === "number") {
|
|
409
|
+
begin("NomosNumberValue");
|
|
410
|
+
out.push(`_nmsUsdaScalar(${outName}, ${depth + 1}, "double", "nomos:value", _nmsUsdaDouble(${literal}));`);
|
|
411
|
+
} else {
|
|
412
|
+
throw new Error(`call USDA codegen: unsupported literal in '${domain}/${directive}'`);
|
|
413
|
+
}
|
|
414
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
case "record": {
|
|
418
|
+
begin("NomosRecordValue");
|
|
419
|
+
const children = fresh("children");
|
|
420
|
+
out.push(`const ${children}: string[] = [];`);
|
|
421
|
+
for (const ref of node.expression.fields) {
|
|
422
|
+
const child = fieldNode(ref);
|
|
423
|
+
const childPath = requiredPath(paths, child.entity.id, "value field");
|
|
424
|
+
const expression = `${canonicalValue}[${JSON.stringify(child.name)}]`;
|
|
425
|
+
const body: string[] = [];
|
|
426
|
+
if (child.required) {
|
|
427
|
+
withOut(children, () => emitValue(
|
|
428
|
+
body,
|
|
429
|
+
expression,
|
|
430
|
+
`${valuePath} + ${JSON.stringify(`/${pathLeaf(childPath)}`)}`,
|
|
431
|
+
child.value.id,
|
|
432
|
+
childPath,
|
|
433
|
+
depth + 1,
|
|
434
|
+
valueIsCanonical || lowersNominalReference,
|
|
435
|
+
));
|
|
436
|
+
out.push(...body);
|
|
437
|
+
} else {
|
|
438
|
+
const optional = fresh("optional");
|
|
439
|
+
withOut(children, () => emitValue(
|
|
440
|
+
body,
|
|
441
|
+
optional,
|
|
442
|
+
`${valuePath} + ${JSON.stringify(`/${pathLeaf(childPath)}`)}`,
|
|
443
|
+
child.value.id,
|
|
444
|
+
childPath,
|
|
445
|
+
depth + 1,
|
|
446
|
+
valueIsCanonical || lowersNominalReference,
|
|
447
|
+
));
|
|
448
|
+
out.push(
|
|
449
|
+
`const ${optional} = ${expression};`,
|
|
450
|
+
`if (${optional} !== undefined) {`,
|
|
451
|
+
...body,
|
|
452
|
+
`}`,
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
out.push(`_nmsUsdaChildren(${outName}, ${children});`);
|
|
457
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
case "list": {
|
|
461
|
+
const elementTypeId = node.expression.element.id;
|
|
462
|
+
begin("NomosListValue");
|
|
463
|
+
const children = fresh("children");
|
|
464
|
+
const index = fresh("index");
|
|
465
|
+
out.push(`const ${children}: string[] = [];`);
|
|
466
|
+
out.push(`for (let ${index} = 0; ${index} < ${canonicalValue}.length; ${index}++) {`);
|
|
467
|
+
const body: string[] = [];
|
|
468
|
+
withOut(children, () => emitValue(
|
|
469
|
+
body,
|
|
470
|
+
`${canonicalValue}[${index}]!`,
|
|
471
|
+
`${valuePath} + "/i_" + String(${index}).padStart(8, "0")`,
|
|
472
|
+
elementTypeId,
|
|
473
|
+
undefined,
|
|
474
|
+
depth + 1,
|
|
475
|
+
));
|
|
476
|
+
out.push(...body, `}`);
|
|
477
|
+
out.push(`_nmsUsdaChildren(${outName}, ${children});`);
|
|
478
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
479
|
+
break;
|
|
480
|
+
}
|
|
481
|
+
case "map": {
|
|
482
|
+
const mapValueTypeId = node.expression.value.id;
|
|
483
|
+
begin("NomosMapValue");
|
|
484
|
+
const children = fresh("children");
|
|
485
|
+
const entries = fresh("entries");
|
|
486
|
+
const index = fresh("index");
|
|
487
|
+
const key = fresh("key");
|
|
488
|
+
const entryValue = fresh("value");
|
|
489
|
+
const entryPath = fresh("entryPath");
|
|
490
|
+
out.push(`const ${children}: string[] = [];`);
|
|
491
|
+
out.push(`const ${entries} = Object.entries(${canonicalValue}).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);`);
|
|
492
|
+
out.push(`for (let ${index} = 0; ${index} < ${entries}.length; ${index}++) {`);
|
|
493
|
+
out.push(`const [${key}, ${entryValue}] = ${entries}[${index}]!;`);
|
|
494
|
+
out.push(`_nmsRoutingCandidates.push(${key});`);
|
|
495
|
+
out.push(`const ${entryPath} = ${valuePath} + "/e_" + String(${index}).padStart(8, "0");`);
|
|
496
|
+
out.push(`_nmsUsdaBegin(${children}, ${depth + 1}, "NomosMapEntry", ${entryPath});`);
|
|
497
|
+
out.push(`_nmsUsdaString(${children}, ${depth + 2}, "string", "nomos:key", ${key});`);
|
|
498
|
+
const body: string[] = [];
|
|
499
|
+
withOut(children, () => emitValue(
|
|
500
|
+
body,
|
|
501
|
+
entryValue,
|
|
502
|
+
`${entryPath} + "/Value"`,
|
|
503
|
+
mapValueTypeId,
|
|
504
|
+
undefined,
|
|
505
|
+
depth + 2,
|
|
506
|
+
));
|
|
507
|
+
out.push(`if (${body.length} > 0) ${children}.push("");`, ...body);
|
|
508
|
+
out.push(`_nmsUsdaClose(${children}, ${depth + 1});`, `}`);
|
|
509
|
+
out.push(`_nmsUsdaChildren(${outName}, ${children});`);
|
|
510
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
case "sum": {
|
|
514
|
+
const nominal = nominalSumArms(nodes, node, `call USDA codegen: '${domain}/${directive}'`);
|
|
515
|
+
const selected = fresh("sum");
|
|
516
|
+
const discriminator = fresh("discriminator");
|
|
517
|
+
out.push(`const ${selected} = ${canonicalValue};`);
|
|
518
|
+
out.push(`const ${discriminator} = ${selected}.type;`);
|
|
519
|
+
out.push(`switch (${discriminator}) {`);
|
|
520
|
+
for (const arm of nominal.arms) {
|
|
521
|
+
const armValue = fresh("arm");
|
|
522
|
+
const variantPath = requiredPath(paths, arm.variant.entity.id, "value variant");
|
|
523
|
+
out.push(`case ${JSON.stringify(arm.discriminator)}: {`);
|
|
524
|
+
out.push(`const ${armValue} = ${selected};`);
|
|
525
|
+
begin("NomosSumValue");
|
|
526
|
+
out.push(`_nmsUsdaRel(${outName}, ${depth + 1}, "nomos:variant", ${JSON.stringify(variantPath)});`);
|
|
527
|
+
const children = fresh("children");
|
|
528
|
+
out.push(`const ${children}: string[] = [];`);
|
|
529
|
+
const body: string[] = [];
|
|
530
|
+
withOut(children, () => emitValue(
|
|
531
|
+
body,
|
|
532
|
+
armValue,
|
|
533
|
+
`${valuePath} + "/Value"`,
|
|
534
|
+
arm.value.entity.id,
|
|
535
|
+
undefined,
|
|
536
|
+
depth + 1,
|
|
537
|
+
));
|
|
538
|
+
out.push(...body);
|
|
539
|
+
out.push(`_nmsUsdaChildren(${outName}, ${children});`);
|
|
540
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
541
|
+
out.push(`break;`, `}`);
|
|
542
|
+
}
|
|
543
|
+
out.push(
|
|
544
|
+
`default: throw new TypeError(${JSON.stringify(`Unknown ${nominal.name} variant arm`)} + ": " + String(${discriminator}));`,
|
|
545
|
+
`}`,
|
|
546
|
+
);
|
|
547
|
+
break;
|
|
548
|
+
}
|
|
549
|
+
case "unknown":
|
|
550
|
+
throw new Error(
|
|
551
|
+
`call USDA codegen: '${domain}/${directive}' contains ${node.expression.kind}; ` +
|
|
552
|
+
`replace the unstructured value or add a schema-specific sum encoder before release`,
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
out.push(`}`);
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
let currentOut = "out";
|
|
559
|
+
const withOut = (name: string, fn: () => void): void => {
|
|
560
|
+
const prior = currentOut;
|
|
561
|
+
currentOut = name;
|
|
562
|
+
try { fn(); } finally { currentOut = prior; }
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
const lines = [
|
|
566
|
+
`function ${functionName}(payload: ${payloadType}, nmsInternalWorkspace: string): NmsGeneratedCall<${JSON.stringify(domain)}, ${JSON.stringify(directive)}> {`,
|
|
567
|
+
` const _nmsRoutingCandidates: string[] = [];`,
|
|
568
|
+
` const out = [`,
|
|
569
|
+
` "#usda 1.0",`,
|
|
570
|
+
` "(",`,
|
|
571
|
+
` " defaultPrim = \\"Change\\"",`,
|
|
572
|
+
` ")",`,
|
|
573
|
+
` "",`,
|
|
574
|
+
` "def NomosChange \\"Change\\"",`,
|
|
575
|
+
` "{",`,
|
|
576
|
+
` " custom rel nomos:directive = <${intentPath}>",`,
|
|
577
|
+
` "",`,
|
|
578
|
+
` " def NomosCall \\"Call\\"",`,
|
|
579
|
+
` " {",`,
|
|
580
|
+
` " custom rel nomos:contract = <${contractPath}>",`,
|
|
581
|
+
` "",`,
|
|
582
|
+
` " def NomosRecordValue \\"Payload\\"",`,
|
|
583
|
+
` " {",`,
|
|
584
|
+
` " custom rel nomos:type = <${contractPath}>",`,
|
|
585
|
+
` ];`,
|
|
586
|
+
];
|
|
587
|
+
const children = fresh("payloadChildren");
|
|
588
|
+
lines.push(` const ${children}: string[] = [];`);
|
|
589
|
+
for (const ref of (contract as LawPayloadContractNode).fields) {
|
|
590
|
+
const field = fieldNode(ref);
|
|
591
|
+
const path = requiredPath(paths, field.entity.id, "payload field");
|
|
592
|
+
const fieldAccess = access?.get(field.name);
|
|
593
|
+
// A generated caller can prove a compiler-owned optional field is absent.
|
|
594
|
+
// Do not emit a dead encoder body: TypeScript still checks unreachable
|
|
595
|
+
// branches, and absence must not require a fake sentinel of the field type.
|
|
596
|
+
if (fieldAccess?.present === "false") continue;
|
|
597
|
+
const expression = fieldAccess?.value ?? `payload[${JSON.stringify(field.name)}]`;
|
|
598
|
+
const fieldSemantic = typeNode(field.value.id).expression.semantic;
|
|
599
|
+
const fieldValueIsCanonical = fieldAccess !== undefined && (
|
|
600
|
+
fieldSemantic?.kind === "id-reference" ||
|
|
601
|
+
fieldSemantic?.kind === "address-reference" ||
|
|
602
|
+
fieldSemantic?.kind === "versioned-reference" ||
|
|
603
|
+
fieldSemantic?.kind === "recipient"
|
|
604
|
+
);
|
|
605
|
+
const body: string[] = [];
|
|
606
|
+
if (field.required) {
|
|
607
|
+
withOut(children, () => emitValue(
|
|
608
|
+
body,
|
|
609
|
+
expression,
|
|
610
|
+
JSON.stringify(`/Change/Call/Payload/${pathLeaf(path)}`),
|
|
611
|
+
field.value.id,
|
|
612
|
+
path,
|
|
613
|
+
3,
|
|
614
|
+
fieldValueIsCanonical,
|
|
615
|
+
));
|
|
616
|
+
lines.push(...body.map((line) => ` ${line}`));
|
|
617
|
+
} else {
|
|
618
|
+
const optional = fresh("optional");
|
|
619
|
+
withOut(children, () => emitValue(
|
|
620
|
+
body,
|
|
621
|
+
optional,
|
|
622
|
+
JSON.stringify(`/Change/Call/Payload/${pathLeaf(path)}`),
|
|
623
|
+
field.value.id,
|
|
624
|
+
path,
|
|
625
|
+
3,
|
|
626
|
+
fieldValueIsCanonical,
|
|
627
|
+
));
|
|
628
|
+
lines.push(
|
|
629
|
+
` if (${fieldAccess?.present ?? `${expression} !== undefined`}) {`,
|
|
630
|
+
` const ${optional} = ${expression};`,
|
|
631
|
+
...body.map((line) => ` ${line}`),
|
|
632
|
+
` }`,
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
lines.push(
|
|
637
|
+
` _nmsUsdaChildren(out, ${children});`,
|
|
638
|
+
` _nmsUsdaClose(out, 2);`,
|
|
639
|
+
` _nmsUsdaClose(out, 1);`,
|
|
640
|
+
` _nmsUsdaClose(out, 0);`,
|
|
641
|
+
` return _nmsGeneratedCall(${JSON.stringify(domain)}, ${JSON.stringify(directive)}, _nmsUsdaUtf8(out.join("\\n") + "\\n"), _nmsRoutingCandidates);`,
|
|
642
|
+
`}`,
|
|
643
|
+
``,
|
|
644
|
+
);
|
|
645
|
+
return { functionName, source: lines };
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
const dartString = (value: string): string =>
|
|
649
|
+
`'${value
|
|
650
|
+
.replace(/\\/g, "\\\\")
|
|
651
|
+
.replace(/'/g, "\\'")
|
|
652
|
+
.replace(/\$/g, "\\$")}'`;
|
|
653
|
+
|
|
654
|
+
/** Emit one schema-specific Dart encoder for the same canonical call USDA as
|
|
655
|
+
* the TypeScript client. The caller supplies compile-time field expressions;
|
|
656
|
+
* generated code never reflects over a payload or builds a recursive value
|
|
657
|
+
* bag. Unsupported shapes refuse package compilation. */
|
|
658
|
+
export function emitDartCallUsdaEncoder(
|
|
659
|
+
graph: LawGraph,
|
|
660
|
+
paths: ReadonlyMap<string, string>,
|
|
661
|
+
domain: string,
|
|
662
|
+
directive: string,
|
|
663
|
+
payloadType: string,
|
|
664
|
+
access: ReadonlyMap<string, DartCallUsdaFieldAccess>,
|
|
665
|
+
extraParameters: readonly string[] = [],
|
|
666
|
+
): DartCallUsdaEncoder {
|
|
667
|
+
const nodes = new Map<string, LawGraph["nodes"][number]>(
|
|
668
|
+
graph.nodes.map((node) => [node.entity.id, node]),
|
|
669
|
+
);
|
|
670
|
+
const intent = graph.nodes.find((node): node is LawIntentNode =>
|
|
671
|
+
node.kind === "intent" &&
|
|
672
|
+
(node.entity.label === directive || node.publicRoute === directive));
|
|
673
|
+
if (intent === undefined) {
|
|
674
|
+
throw new Error(`Dart call USDA codegen: domain '${domain}' has no law intent for '${directive}'`);
|
|
675
|
+
}
|
|
676
|
+
const contract = nodes.get(intent.accepts.id);
|
|
677
|
+
if (contract?.kind !== "payload-contract") {
|
|
678
|
+
throw new Error(`Dart call USDA codegen: '${domain}/${directive}' accepts no payload contract`);
|
|
679
|
+
}
|
|
680
|
+
const functionName = `_nmsCallUsda_${symbol(domain)}_${symbol(directive)}`;
|
|
681
|
+
const intentPath = requiredPath(paths, intent.entity.id, "intent");
|
|
682
|
+
const contractPath = requiredPath(paths, contract.entity.id, "contract");
|
|
683
|
+
let local = 0;
|
|
684
|
+
const fresh = (prefix: string): string => `_${prefix}${local++}`;
|
|
685
|
+
|
|
686
|
+
const fieldNode = (ref: LawStructuralRef<"value-field">): LawAnonymousValueFieldNode => {
|
|
687
|
+
const node = nodes.get(ref.id);
|
|
688
|
+
if (node?.kind !== "structural" || node.structuralKind !== "value-field") {
|
|
689
|
+
throw new Error(`Dart call USDA codegen: '${ref.id}' is not a value field`);
|
|
690
|
+
}
|
|
691
|
+
return node;
|
|
692
|
+
};
|
|
693
|
+
const typeNode = (id: string): LawAnonymousValueTypeNode => {
|
|
694
|
+
const node = nodes.get(id);
|
|
695
|
+
if (node?.kind !== "structural" || node.structuralKind !== "value-type") {
|
|
696
|
+
throw new Error(`Dart call USDA codegen: '${id}' is not an anonymous value type`);
|
|
697
|
+
}
|
|
698
|
+
return node;
|
|
699
|
+
};
|
|
700
|
+
|
|
701
|
+
let currentOut = "out";
|
|
702
|
+
const withOut = (name: string, fn: () => void): void => {
|
|
703
|
+
const prior = currentOut;
|
|
704
|
+
currentOut = name;
|
|
705
|
+
try {
|
|
706
|
+
fn();
|
|
707
|
+
} finally {
|
|
708
|
+
currentOut = prior;
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
const emitValue = (
|
|
713
|
+
out: string[],
|
|
714
|
+
value: string,
|
|
715
|
+
valuePath: string,
|
|
716
|
+
typeId: string,
|
|
717
|
+
fieldPath: string | undefined,
|
|
718
|
+
depth: number,
|
|
719
|
+
valueIsCanonical = false,
|
|
720
|
+
): void => {
|
|
721
|
+
const node = typeNode(typeId);
|
|
722
|
+
const typePath = requiredPath(paths, typeId, "value type");
|
|
723
|
+
const semantic = node.expression.semantic;
|
|
724
|
+
const lowersNominalReference = semantic?.kind === "id-reference" ||
|
|
725
|
+
semantic?.kind === "address-reference" ||
|
|
726
|
+
semantic?.kind === "versioned-reference" ||
|
|
727
|
+
semantic?.kind === "recipient";
|
|
728
|
+
const canonicalValue = valueIsCanonical || semantic === undefined
|
|
729
|
+
? value
|
|
730
|
+
: semantic.kind === "id-reference"
|
|
731
|
+
? `NmsInternalNomosRefs.id(${value})`
|
|
732
|
+
: semantic.kind === "address-reference"
|
|
733
|
+
? `(workspace: NmsInternalNomosRefs.workspace(${value}), type: NmsInternalNomosRefs.type(${value}), id: NmsInternalNomosRefs.id(${value}))`
|
|
734
|
+
: semantic.kind === "versioned-reference"
|
|
735
|
+
? `(pinned: (workspace: NmsInternalNomosRefs.workspace(${value}.pinned), type: NmsInternalNomosRefs.type(${value}.pinned), id: NmsInternalNomosRefs.id(${value}.pinned)), series: (workspace: NmsInternalNomosRefs.workspace(${value}.series), type: NmsInternalNomosRefs.type(${value}.series), id: NmsInternalNomosRefs.id(${value}.series)))`
|
|
736
|
+
: semantic.kind === "recipient"
|
|
737
|
+
? `NmsInternalRecipients.address(${value})`
|
|
738
|
+
: semantic.kind === "sha256-digest" ||
|
|
739
|
+
semantic.kind === "git-object-id" ||
|
|
740
|
+
semantic.kind === "branded-id" ||
|
|
741
|
+
semantic.kind === "unit"
|
|
742
|
+
? `${value}.value`
|
|
743
|
+
: value;
|
|
744
|
+
const outName = fresh("out");
|
|
745
|
+
const begin = (primitive: string): void => {
|
|
746
|
+
out.push(`_nmsUsdaBegin(${outName}, ${depth}, ${dartString(primitive)}, ${valuePath});`);
|
|
747
|
+
if (fieldPath !== undefined) {
|
|
748
|
+
out.push(`_nmsUsdaRel(${outName}, ${depth + 1}, 'nomos:field', ${dartString(fieldPath)});`);
|
|
749
|
+
}
|
|
750
|
+
out.push(`_nmsUsdaRel(${outName}, ${depth + 1}, 'nomos:type', ${dartString(typePath)});`);
|
|
751
|
+
};
|
|
752
|
+
out.push(`{ final ${outName} = ${currentOut};`);
|
|
753
|
+
switch (node.expression.kind) {
|
|
754
|
+
case "default":
|
|
755
|
+
case "recursive":
|
|
756
|
+
emitValue(out, canonicalValue, valuePath, node.expression.value.id, fieldPath, depth, valueIsCanonical);
|
|
757
|
+
break;
|
|
758
|
+
case "nullable": {
|
|
759
|
+
const nullable = fresh("nullable");
|
|
760
|
+
out.push(`final ${nullable} = ${canonicalValue};`);
|
|
761
|
+
out.push(`if (${nullable} == null) {`);
|
|
762
|
+
begin("NomosNullValue");
|
|
763
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
764
|
+
out.push(`} else {`);
|
|
765
|
+
emitValue(
|
|
766
|
+
out,
|
|
767
|
+
nullable,
|
|
768
|
+
valuePath,
|
|
769
|
+
node.expression.value.id,
|
|
770
|
+
fieldPath,
|
|
771
|
+
depth,
|
|
772
|
+
valueIsCanonical,
|
|
773
|
+
);
|
|
774
|
+
out.push(`}`);
|
|
775
|
+
break;
|
|
776
|
+
}
|
|
777
|
+
case "string":
|
|
778
|
+
begin("NomosStringValue");
|
|
779
|
+
out.push(`_nmsUsdaString(${outName}, ${depth + 1}, 'string', 'nomos:value', ${canonicalValue});`);
|
|
780
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
781
|
+
break;
|
|
782
|
+
case "enum":
|
|
783
|
+
begin("NomosTokenValue");
|
|
784
|
+
out.push(`_nmsUsdaString(${outName}, ${depth + 1}, 'token', 'nomos:value', ${canonicalValue}.wire);`);
|
|
785
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
786
|
+
break;
|
|
787
|
+
case "integer":
|
|
788
|
+
begin("NomosIntValue");
|
|
789
|
+
out.push(`_nmsUsdaScalar(${outName}, ${depth + 1}, 'int64', 'nomos:value', _nmsUsdaInt(${canonicalValue}));`);
|
|
790
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
791
|
+
break;
|
|
792
|
+
case "number":
|
|
793
|
+
begin("NomosNumberValue");
|
|
794
|
+
out.push(`_nmsUsdaScalar(${outName}, ${depth + 1}, 'double', 'nomos:value', _nmsUsdaDouble((${canonicalValue}).toDouble()));`);
|
|
795
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
796
|
+
break;
|
|
797
|
+
case "boolean":
|
|
798
|
+
begin("NomosBoolValue");
|
|
799
|
+
out.push(`_nmsUsdaScalar(${outName}, ${depth + 1}, 'bool', 'nomos:value', ${canonicalValue} ? 'true' : 'false');`);
|
|
800
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
801
|
+
break;
|
|
802
|
+
case "null":
|
|
803
|
+
begin("NomosNullValue");
|
|
804
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
805
|
+
break;
|
|
806
|
+
case "literal": {
|
|
807
|
+
const literal = node.expression.value;
|
|
808
|
+
if (typeof literal === "string") {
|
|
809
|
+
begin("NomosStringValue");
|
|
810
|
+
out.push(`_nmsUsdaString(${outName}, ${depth + 1}, 'string', 'nomos:value', ${dartString(literal)});`);
|
|
811
|
+
} else if (typeof literal === "boolean") {
|
|
812
|
+
begin("NomosBoolValue");
|
|
813
|
+
out.push(`_nmsUsdaScalar(${outName}, ${depth + 1}, 'bool', 'nomos:value', '${literal ? "true" : "false"}');`);
|
|
814
|
+
} else if (typeof literal === "number" && Number.isInteger(literal)) {
|
|
815
|
+
begin("NomosIntValue");
|
|
816
|
+
out.push(`_nmsUsdaScalar(${outName}, ${depth + 1}, 'int64', 'nomos:value', _nmsUsdaInt(${literal}));`);
|
|
817
|
+
} else if (typeof literal === "number") {
|
|
818
|
+
begin("NomosNumberValue");
|
|
819
|
+
out.push(`_nmsUsdaScalar(${outName}, ${depth + 1}, 'double', 'nomos:value', _nmsUsdaDouble(${literal}));`);
|
|
820
|
+
} else {
|
|
821
|
+
throw new Error(`Dart call USDA codegen: unsupported literal in '${domain}/${directive}'`);
|
|
822
|
+
}
|
|
823
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
824
|
+
break;
|
|
825
|
+
}
|
|
826
|
+
case "record": {
|
|
827
|
+
begin("NomosRecordValue");
|
|
828
|
+
const children = fresh("children");
|
|
829
|
+
out.push(`final ${children} = <String>[];`);
|
|
830
|
+
for (const ref of node.expression.fields) {
|
|
831
|
+
const child = fieldNode(ref);
|
|
832
|
+
const childPath = requiredPath(paths, child.entity.id, "value field");
|
|
833
|
+
const expression = `${canonicalValue}.${dartMemberName(child.name)}`;
|
|
834
|
+
const body: string[] = [];
|
|
835
|
+
if (child.required) {
|
|
836
|
+
withOut(children, () =>
|
|
837
|
+
emitValue(
|
|
838
|
+
body,
|
|
839
|
+
expression,
|
|
840
|
+
`${valuePath} + ${dartString(`/${pathLeaf(childPath)}`)}`,
|
|
841
|
+
child.value.id,
|
|
842
|
+
childPath,
|
|
843
|
+
depth + 1,
|
|
844
|
+
valueIsCanonical || lowersNominalReference,
|
|
845
|
+
));
|
|
846
|
+
out.push(...body);
|
|
847
|
+
} else {
|
|
848
|
+
const optional = fresh("optional");
|
|
849
|
+
withOut(children, () =>
|
|
850
|
+
emitValue(
|
|
851
|
+
body,
|
|
852
|
+
optional,
|
|
853
|
+
`${valuePath} + ${dartString(`/${pathLeaf(childPath)}`)}`,
|
|
854
|
+
child.value.id,
|
|
855
|
+
childPath,
|
|
856
|
+
depth + 1,
|
|
857
|
+
valueIsCanonical || lowersNominalReference,
|
|
858
|
+
));
|
|
859
|
+
out.push(
|
|
860
|
+
`final ${optional} = ${expression};`,
|
|
861
|
+
`if (${optional} != null) {`,
|
|
862
|
+
...body,
|
|
863
|
+
`}`,
|
|
864
|
+
);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
out.push(`_nmsUsdaChildren(${outName}, ${children});`);
|
|
868
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
869
|
+
break;
|
|
870
|
+
}
|
|
871
|
+
case "list": {
|
|
872
|
+
const elementTypeId = node.expression.element.id;
|
|
873
|
+
begin("NomosListValue");
|
|
874
|
+
const children = fresh("children");
|
|
875
|
+
const index = fresh("index");
|
|
876
|
+
out.push(`final ${children} = <String>[];`);
|
|
877
|
+
out.push(`for (var ${index} = 0; ${index} < ${canonicalValue}.length; ${index}++) {`);
|
|
878
|
+
const body: string[] = [];
|
|
879
|
+
withOut(children, () =>
|
|
880
|
+
emitValue(
|
|
881
|
+
body,
|
|
882
|
+
`${canonicalValue}[${index}]`,
|
|
883
|
+
`${valuePath} + '/i_' + ${index}.toString().padLeft(8, '0')`,
|
|
884
|
+
elementTypeId,
|
|
885
|
+
undefined,
|
|
886
|
+
depth + 1,
|
|
887
|
+
));
|
|
888
|
+
out.push(...body, `}`);
|
|
889
|
+
out.push(`_nmsUsdaChildren(${outName}, ${children});`);
|
|
890
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
891
|
+
break;
|
|
892
|
+
}
|
|
893
|
+
case "map": {
|
|
894
|
+
const mapValueTypeId = node.expression.value.id;
|
|
895
|
+
begin("NomosMapValue");
|
|
896
|
+
const children = fresh("children");
|
|
897
|
+
const entries = fresh("entries");
|
|
898
|
+
const index = fresh("index");
|
|
899
|
+
const entry = fresh("entry");
|
|
900
|
+
const entryPath = fresh("entryPath");
|
|
901
|
+
out.push(`final ${children} = <String>[];`);
|
|
902
|
+
out.push(`final ${entries} = ${canonicalValue}.entries.toList()..sort((left, right) => left.key.compareTo(right.key));`);
|
|
903
|
+
out.push(`for (var ${index} = 0; ${index} < ${entries}.length; ${index}++) {`);
|
|
904
|
+
out.push(`final ${entry} = ${entries}[${index}];`);
|
|
905
|
+
out.push(`final ${entryPath} = ${valuePath} + '/e_' + ${index}.toString().padLeft(8, '0');`);
|
|
906
|
+
out.push(`_nmsUsdaBegin(${children}, ${depth + 1}, 'NomosMapEntry', ${entryPath});`);
|
|
907
|
+
out.push(`_nmsUsdaString(${children}, ${depth + 2}, 'string', 'nomos:key', ${entry}.key);`);
|
|
908
|
+
const body: string[] = [];
|
|
909
|
+
withOut(children, () =>
|
|
910
|
+
emitValue(
|
|
911
|
+
body,
|
|
912
|
+
`${entry}.value`,
|
|
913
|
+
`${entryPath} + '/Value'`,
|
|
914
|
+
mapValueTypeId,
|
|
915
|
+
undefined,
|
|
916
|
+
depth + 2,
|
|
917
|
+
));
|
|
918
|
+
out.push(`if (${body.length} > 0) ${children}.add('');`, ...body);
|
|
919
|
+
out.push(`_nmsUsdaClose(${children}, ${depth + 1});`, `}`);
|
|
920
|
+
out.push(`_nmsUsdaChildren(${outName}, ${children});`);
|
|
921
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
922
|
+
break;
|
|
923
|
+
}
|
|
924
|
+
case "sum": {
|
|
925
|
+
const nominal = nominalSumArms(nodes, node, `Dart call USDA codegen: '${domain}/${directive}'`);
|
|
926
|
+
const selected = fresh("sum");
|
|
927
|
+
out.push(`final ${selected} = ${canonicalValue};`);
|
|
928
|
+
nominal.arms.forEach((arm, index) => {
|
|
929
|
+
const armType = unionArmClassName(nominal.name, arm.discriminator);
|
|
930
|
+
const armValue = fresh("arm");
|
|
931
|
+
const variantPath = requiredPath(paths, arm.variant.entity.id, "value variant");
|
|
932
|
+
out.push(`${index === 0 ? "if" : "else if"} (${selected} is ${armType}) {`);
|
|
933
|
+
out.push(`final ${armValue} = ${selected} as ${armType};`);
|
|
934
|
+
begin("NomosSumValue");
|
|
935
|
+
out.push(`_nmsUsdaRel(${outName}, ${depth + 1}, 'nomos:variant', ${dartString(variantPath)});`);
|
|
936
|
+
const children = fresh("children");
|
|
937
|
+
out.push(`final ${children} = <String>[];`);
|
|
938
|
+
const body: string[] = [];
|
|
939
|
+
withOut(children, () => emitValue(
|
|
940
|
+
body,
|
|
941
|
+
armValue,
|
|
942
|
+
`${valuePath} + '/Value'`,
|
|
943
|
+
arm.value.entity.id,
|
|
944
|
+
undefined,
|
|
945
|
+
depth + 1,
|
|
946
|
+
));
|
|
947
|
+
out.push(...body);
|
|
948
|
+
out.push(`_nmsUsdaChildren(${outName}, ${children});`);
|
|
949
|
+
out.push(`_nmsUsdaClose(${outName}, ${depth});`);
|
|
950
|
+
out.push(`}`);
|
|
951
|
+
});
|
|
952
|
+
out.push(
|
|
953
|
+
`else { throw ArgumentError.value(${selected}, ${dartString(nominal.name)}, ${dartString("Unknown nominal variant arm")}); }`,
|
|
954
|
+
);
|
|
955
|
+
break;
|
|
956
|
+
}
|
|
957
|
+
case "unknown":
|
|
958
|
+
throw new Error(
|
|
959
|
+
`Dart call USDA codegen: '${domain}/${directive}' contains ${node.expression.kind}; ` +
|
|
960
|
+
"replace the unstructured value or generate an exhaustive sum encoder before release",
|
|
961
|
+
);
|
|
962
|
+
}
|
|
963
|
+
out.push(`}`);
|
|
964
|
+
};
|
|
965
|
+
|
|
966
|
+
const lines = [
|
|
967
|
+
`NmsGeneratedCall ${functionName}(${[
|
|
968
|
+
`${payloadType} payload`,
|
|
969
|
+
...extraParameters,
|
|
970
|
+
].join(", ")}) {`,
|
|
971
|
+
` final out = <String>[`,
|
|
972
|
+
` '#usda 1.0',`,
|
|
973
|
+
` '(',`,
|
|
974
|
+
` ' defaultPrim = "Change"',`,
|
|
975
|
+
` ')',`,
|
|
976
|
+
` '',`,
|
|
977
|
+
` 'def NomosChange "Change"',`,
|
|
978
|
+
` '{',`,
|
|
979
|
+
` ' custom rel nomos:directive = <${intentPath}>',`,
|
|
980
|
+
` '',`,
|
|
981
|
+
` ' def NomosCall "Call"',`,
|
|
982
|
+
` ' {',`,
|
|
983
|
+
` ' custom rel nomos:contract = <${contractPath}>',`,
|
|
984
|
+
` '',`,
|
|
985
|
+
` ' def NomosRecordValue "Payload"',`,
|
|
986
|
+
` ' {',`,
|
|
987
|
+
` ' custom rel nomos:type = <${contractPath}>',`,
|
|
988
|
+
` ];`,
|
|
989
|
+
];
|
|
990
|
+
const children = fresh("payloadChildren");
|
|
991
|
+
lines.push(` final ${children} = <String>[];`);
|
|
992
|
+
for (const ref of (contract as LawPayloadContractNode).fields) {
|
|
993
|
+
const field = fieldNode(ref);
|
|
994
|
+
const fieldAccess = access.get(field.name);
|
|
995
|
+
if (fieldAccess === undefined) {
|
|
996
|
+
throw new Error(
|
|
997
|
+
`Dart call USDA codegen: '${domain}/${directive}' has no typed access for '${field.name}'`,
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
if (fieldAccess.omit === true) {
|
|
1001
|
+
if (field.required) {
|
|
1002
|
+
throw new Error(
|
|
1003
|
+
`Dart call USDA codegen: required '${domain}/${directive}.${field.name}' cannot be omitted`,
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
1006
|
+
continue;
|
|
1007
|
+
}
|
|
1008
|
+
const path = requiredPath(paths, field.entity.id, "payload field");
|
|
1009
|
+
const fieldSemantic = typeNode(field.value.id).expression.semantic;
|
|
1010
|
+
const fieldValueIsCanonical = fieldSemantic?.kind === "id-reference" ||
|
|
1011
|
+
fieldSemantic?.kind === "address-reference" ||
|
|
1012
|
+
fieldSemantic?.kind === "versioned-reference" ||
|
|
1013
|
+
fieldSemantic?.kind === "recipient" ||
|
|
1014
|
+
fieldSemantic?.kind === "sha256-digest" ||
|
|
1015
|
+
fieldSemantic?.kind === "git-object-id" ||
|
|
1016
|
+
fieldSemantic?.kind === "branded-id" ||
|
|
1017
|
+
fieldSemantic?.kind === "unit";
|
|
1018
|
+
if (fieldAccess.slot === true) {
|
|
1019
|
+
const nullable = typeNode(field.value.id);
|
|
1020
|
+
if (nullable.expression.kind !== "nullable") {
|
|
1021
|
+
throw new Error(
|
|
1022
|
+
`Dart call USDA codegen: slot '${domain}/${directive}.${field.name}' is not nullable in the canonical contract`,
|
|
1023
|
+
);
|
|
1024
|
+
}
|
|
1025
|
+
const nullableInnerType = nullable.expression.value.id;
|
|
1026
|
+
const nullablePath = requiredPath(paths, field.value.id, "nullable value type");
|
|
1027
|
+
const nullBody = [
|
|
1028
|
+
`_nmsUsdaBegin(${children}, 3, 'NomosNullValue', ${dartString(`/Change/Call/Payload/${pathLeaf(path)}`)});`,
|
|
1029
|
+
`_nmsUsdaRel(${children}, 4, 'nomos:field', ${dartString(path)});`,
|
|
1030
|
+
`_nmsUsdaRel(${children}, 4, 'nomos:type', ${dartString(nullablePath)});`,
|
|
1031
|
+
`_nmsUsdaClose(${children}, 3);`,
|
|
1032
|
+
];
|
|
1033
|
+
const presentBody: string[] = [];
|
|
1034
|
+
withOut(children, () =>
|
|
1035
|
+
emitValue(
|
|
1036
|
+
presentBody,
|
|
1037
|
+
"nmsInternalValue",
|
|
1038
|
+
dartString(`/Change/Call/Payload/${pathLeaf(path)}`),
|
|
1039
|
+
nullableInnerType,
|
|
1040
|
+
path,
|
|
1041
|
+
3,
|
|
1042
|
+
false,
|
|
1043
|
+
));
|
|
1044
|
+
lines.push(
|
|
1045
|
+
` ${fieldAccess.value}.when<void>(`,
|
|
1046
|
+
` omitted: () {},`,
|
|
1047
|
+
` explicitNull: () {`,
|
|
1048
|
+
...nullBody.map((line) => ` ${line}`),
|
|
1049
|
+
` },`,
|
|
1050
|
+
` present: (nmsInternalValue) {`,
|
|
1051
|
+
...presentBody.map((line) => ` ${line}`),
|
|
1052
|
+
` },`,
|
|
1053
|
+
` );`,
|
|
1054
|
+
);
|
|
1055
|
+
continue;
|
|
1056
|
+
}
|
|
1057
|
+
if (field.required) {
|
|
1058
|
+
const body: string[] = [];
|
|
1059
|
+
withOut(children, () =>
|
|
1060
|
+
emitValue(
|
|
1061
|
+
body,
|
|
1062
|
+
fieldAccess.value,
|
|
1063
|
+
dartString(`/Change/Call/Payload/${pathLeaf(path)}`),
|
|
1064
|
+
field.value.id,
|
|
1065
|
+
path,
|
|
1066
|
+
3,
|
|
1067
|
+
fieldValueIsCanonical,
|
|
1068
|
+
));
|
|
1069
|
+
lines.push(...body.map((line) => ` ${line}`));
|
|
1070
|
+
} else {
|
|
1071
|
+
const present = fieldAccess.present;
|
|
1072
|
+
if (present === undefined) {
|
|
1073
|
+
throw new Error(
|
|
1074
|
+
`Dart call USDA codegen: optional '${domain}/${directive}.${field.name}' has no typed presence expression`,
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
1077
|
+
const optional = fresh("optional");
|
|
1078
|
+
const body: string[] = [];
|
|
1079
|
+
withOut(children, () =>
|
|
1080
|
+
emitValue(
|
|
1081
|
+
body,
|
|
1082
|
+
optional,
|
|
1083
|
+
dartString(`/Change/Call/Payload/${pathLeaf(path)}`),
|
|
1084
|
+
field.value.id,
|
|
1085
|
+
path,
|
|
1086
|
+
3,
|
|
1087
|
+
fieldValueIsCanonical,
|
|
1088
|
+
));
|
|
1089
|
+
lines.push(
|
|
1090
|
+
` if (${present}) {`,
|
|
1091
|
+
` final ${optional} = ${fieldAccess.value};`,
|
|
1092
|
+
...body.map((line) => ` ${line}`),
|
|
1093
|
+
` }`,
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
lines.push(
|
|
1098
|
+
` _nmsUsdaChildren(out, ${children});`,
|
|
1099
|
+
` _nmsUsdaClose(out, 2);`,
|
|
1100
|
+
` _nmsUsdaClose(out, 1);`,
|
|
1101
|
+
` _nmsUsdaClose(out, 0);`,
|
|
1102
|
+
` return NmsInternalGeneratedCalls.create(`,
|
|
1103
|
+
` domain: ${dartString(domain)},`,
|
|
1104
|
+
` directive: ${dartString(directive)},`,
|
|
1105
|
+
` canonicalUsda: Uint8List.fromList(utf8.encode('\${out.join('\\n')}\\n')),`,
|
|
1106
|
+
` );`,
|
|
1107
|
+
`}`,
|
|
1108
|
+
``,
|
|
1109
|
+
);
|
|
1110
|
+
return { functionName, source: lines };
|
|
1111
|
+
}
|