@githolon/dsl 0.103.4 → 0.103.6
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 +8 -0
- package/dart/lib/src/runner/node-entry.mjs +333 -117
- package/dart/lib/src/runner/offline-install.mjs +173 -67
- package/dart/lib/src/session_bound.dart +111 -2
- package/dart/lib/src/test_harness.dart +51 -41
- package/dart/lib/src/webview_bridge.dart +714 -60
- package/dart/pubspec.lock +1 -1
- package/dart/pubspec.yaml +2 -2
- package/dart/test/acquire_session_test.dart +62 -5
- package/dart/test/cloud_bound_topology_test.dart +59 -38
- package/dart/test/session_bound_test.dart +25 -10
- package/dart/test/webview_bridge_test.dart +326 -7
- package/dart_types/CHANGELOG.md +5 -0
- package/dart_types/lib/nomos_types.dart +1 -0
- package/dart_types/lib/src/git_object_id.dart +37 -0
- package/dart_types/pubspec.yaml +1 -1
- package/package.json +1 -1
- package/runtime/generated-application-host.js +347 -113
- package/src/births.ts +16 -1
- package/src/business_birth.ts +55 -34
- package/src/codegen_dart.ts +68 -12
- package/src/codegen_dart_application.ts +114 -19
- package/src/codegen_dart_client.ts +149 -6
- package/src/codegen_proof.ts +16 -9
- package/src/codegen_provider_dart.ts +5 -0
- package/src/codegen_ts.ts +115 -19
- package/src/compile_package_main.ts +159 -43
- package/src/fields.ts +43 -0
- package/src/framework/domain_lifecycle.ts +108 -88
- package/src/framework/genesis.ts +2 -1
- package/src/framework/home.ts +6 -43
- package/src/framework/workspaces.ts +42 -25
- package/src/idof_lint.ts +65 -51
- package/src/index.ts +21 -1
- package/src/intent.ts +11 -1
- package/src/law_graph.ts +81 -11
- package/src/relation_schema.ts +18 -0
- package/src/shape_compat.ts +3 -0
- package/src/stable_ids_for_module.ts +7 -1
- package/src/value_object.ts +40 -0
|
@@ -7,7 +7,7 @@ import type { z } from "zod";
|
|
|
7
7
|
|
|
8
8
|
import type { AggregateHandle } from "./aggregate.js";
|
|
9
9
|
import type { Directive } from "./directive.js";
|
|
10
|
-
import { isSha256DigestSchema, type Field } from "./fields.js";
|
|
10
|
+
import { isGitObjectIdSchema, isSha256DigestSchema, type Field } from "./fields.js";
|
|
11
11
|
import type { QueryDecl } from "./query.js";
|
|
12
12
|
import type { DomainModule } from "./codegen_dart.js";
|
|
13
13
|
import {
|
|
@@ -89,6 +89,7 @@ const TENANT_ROOT_MEMBERS = new Map<string, string>([
|
|
|
89
89
|
["currentUser", "the generated signed-in application participant"],
|
|
90
90
|
["currentStatus", "the generated application status"],
|
|
91
91
|
["watchStatus", "the generated application status watch"],
|
|
92
|
+
["whenCurrent", "the generated application convergence awaitable"],
|
|
92
93
|
["recipient", "the generated typed recipient boundary"],
|
|
93
94
|
["createInvitation", "the generated invitation capability"],
|
|
94
95
|
["redeemInvitation", "the generated invitation redemption"],
|
|
@@ -129,6 +130,12 @@ function keyType(aggregate: AggregateHandle | undefined, key: string): string {
|
|
|
129
130
|
) {
|
|
130
131
|
return "Sha256Digest";
|
|
131
132
|
}
|
|
133
|
+
if (
|
|
134
|
+
field?.kind === "string" &&
|
|
135
|
+
isGitObjectIdSchema(field.zod as z.ZodTypeAny)
|
|
136
|
+
) {
|
|
137
|
+
return "GitObjectId";
|
|
138
|
+
}
|
|
132
139
|
return "String";
|
|
133
140
|
}
|
|
134
141
|
|
|
@@ -537,6 +544,17 @@ export function generateDartApplication(
|
|
|
537
544
|
if (uri.endsWith(`/${name}.dart`) || uri.endsWith(`${name}.dart`))
|
|
538
545
|
return uri;
|
|
539
546
|
}
|
|
547
|
+
// In layered output the domain types live in the generated TYPES package,
|
|
548
|
+
// never beside this application library. A relative fallback here makes
|
|
549
|
+
// the client package unmountable as soon as a generated application needs
|
|
550
|
+
// a concrete sibling-domain type (for example a typed NomosRef carried by
|
|
551
|
+
// a parent access card). Derive the same public package URI used by the
|
|
552
|
+
// compiler's explicit-barrel imports; flat output deliberately keeps the
|
|
553
|
+
// sibling-relative form below.
|
|
554
|
+
const layeredTypes = opts.typesImportUri?.match(/^package:([^/]+)\//);
|
|
555
|
+
if (layeredTypes?.[1] !== undefined) {
|
|
556
|
+
return `package:${layeredTypes[1]}/src/generated/${name}.dart`;
|
|
557
|
+
}
|
|
540
558
|
return `${name}.dart`;
|
|
541
559
|
};
|
|
542
560
|
|
|
@@ -573,7 +591,11 @@ export function generateDartApplication(
|
|
|
573
591
|
)
|
|
574
592
|
);
|
|
575
593
|
return specs
|
|
576
|
-
.filter(
|
|
594
|
+
.filter(
|
|
595
|
+
(spec) =>
|
|
596
|
+
(spec.toJson === "ref" || spec.toJson === "addressRef") &&
|
|
597
|
+
!recordInputIds.has(spec.name)
|
|
598
|
+
)
|
|
577
599
|
.map((spec) => {
|
|
578
600
|
const targetClass =
|
|
579
601
|
spec.dartType.match(/^NomosRef<(.*)>$/)?.[1] ?? "Object?";
|
|
@@ -583,7 +605,8 @@ export function generateDartApplication(
|
|
|
583
605
|
(aggregate) => concreteClassName(aggregate.id) === targetClass
|
|
584
606
|
)?.id ??
|
|
585
607
|
targetClass;
|
|
586
|
-
const expectedWorkspace =
|
|
608
|
+
const expectedWorkspace =
|
|
609
|
+
spec.toJson === "addressRef" || foreignReferenceIds.has(spec.name)
|
|
587
610
|
? "''"
|
|
588
611
|
: "_client.session?.workspace ?? ''";
|
|
589
612
|
const value = `facts.${member(spec.name)}`;
|
|
@@ -800,6 +823,31 @@ export function generateDartApplication(
|
|
|
800
823
|
path
|
|
801
824
|
.split(".")
|
|
802
825
|
.reduce((expr, part) => `${expr}.${member(part)}`, "facts");
|
|
826
|
+
const foreignInputs = dartForeignReferenceInputs(
|
|
827
|
+
directive,
|
|
828
|
+
mod.aggregates
|
|
829
|
+
);
|
|
830
|
+
// A generated input collapses a foreign `(workspace, id)` pair into one
|
|
831
|
+
// NomosRef. Source-trust orchestration must follow that public shape too:
|
|
832
|
+
// deriving the locator from the typed reference, rather than reaching for
|
|
833
|
+
// the deliberately removed raw workspace/id fields.
|
|
834
|
+
const factValue = (path: string): string => {
|
|
835
|
+
if (!path.includes(".")) {
|
|
836
|
+
const workspaceOwner = foreignInputs.find(
|
|
837
|
+
(input) => input.workspaceField === path
|
|
838
|
+
);
|
|
839
|
+
if (workspaceOwner !== undefined) {
|
|
840
|
+
return `NmsInternalNomosRefs.workspace(facts.${member(
|
|
841
|
+
workspaceOwner.idField
|
|
842
|
+
)})`;
|
|
843
|
+
}
|
|
844
|
+
const reference = foreignInputs.find((input) => input.idField === path);
|
|
845
|
+
if (reference !== undefined) {
|
|
846
|
+
return `NmsInternalNomosRefs.id(facts.${member(reference.idField)})`;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
return factPath(path);
|
|
850
|
+
};
|
|
803
851
|
const trust = directive.businessSourceTrust;
|
|
804
852
|
const method = member(directive.id);
|
|
805
853
|
if (trust === undefined) {
|
|
@@ -809,11 +857,11 @@ export function generateDartApplication(
|
|
|
809
857
|
.sort(([a], [b]) => a.localeCompare(b))
|
|
810
858
|
.map(
|
|
811
859
|
([target, field]) =>
|
|
812
|
-
` ${JSON.stringify(target)}: ${
|
|
860
|
+
` ${JSON.stringify(target)}: ${factValue(field)},`
|
|
813
861
|
);
|
|
814
862
|
return {
|
|
815
863
|
prelude: [
|
|
816
|
-
` final sourceGenesis = await _client.bridge.nomosWorkspaceGenesis(${
|
|
864
|
+
` final sourceGenesis = await _client.bridge.nomosWorkspaceGenesis(${factValue(
|
|
817
865
|
trust.fromPayload
|
|
818
866
|
)});`,
|
|
819
867
|
` await _client._offer(`,
|
|
@@ -1108,6 +1156,13 @@ export function generateDartApplication(
|
|
|
1108
1156
|
` return opened;`,
|
|
1109
1157
|
` }`,
|
|
1110
1158
|
``,
|
|
1159
|
+
` /// Open the workspace named by a typed ${rootType} custody reference.`,
|
|
1160
|
+
` /// The reference supplies identity and routing only; normal authority checks still apply.`,
|
|
1161
|
+
` Future<${targetApplication}> openReference(NomosRef<${rootType}> reference) {`,
|
|
1162
|
+
` _nmsRequireReference(reference, ${JSON.stringify(birth.aggregateId)}, '');`,
|
|
1163
|
+
` return _nmsOpenReference(reference);`,
|
|
1164
|
+
` }`,
|
|
1165
|
+
``,
|
|
1111
1166
|
` /// Open the ${birth.aggregateId} whose ${birth.keyField} is [key].`,
|
|
1112
1167
|
` Future<${targetApplication}> open(String key) => _nmsOpenReference(`,
|
|
1113
1168
|
` NmsInternalNomosRefs.create<${rootType}>(`,
|
|
@@ -1277,6 +1332,7 @@ export function generateDartApplication(
|
|
|
1277
1332
|
actionResultDeclarations.push(
|
|
1278
1333
|
`/// Every business record affected by ${cap(camel(directive.id))}.`,
|
|
1279
1334
|
`final class ${resultClass} {`,
|
|
1335
|
+
` final NomosOperationReceipt receipt;`,
|
|
1280
1336
|
...outputs.map((output) =>
|
|
1281
1337
|
output.many
|
|
1282
1338
|
? ` final List<NomosRef<${output.dartType}>> ${output.field};`
|
|
@@ -1284,7 +1340,7 @@ export function generateDartApplication(
|
|
|
1284
1340
|
? ` final NomosRef<${output.dartType}>? ${output.field};`
|
|
1285
1341
|
: ` final NomosRef<${output.dartType}> ${output.field};`
|
|
1286
1342
|
),
|
|
1287
|
-
` const ${resultClass}({${outputs
|
|
1343
|
+
` const ${resultClass}({required this.receipt, ${outputs
|
|
1288
1344
|
.map((output) => `required this.${output.field}`)
|
|
1289
1345
|
.join(", ")}});`,
|
|
1290
1346
|
`}`,
|
|
@@ -1294,7 +1350,7 @@ export function generateDartApplication(
|
|
|
1294
1350
|
const resultExpression =
|
|
1295
1351
|
outputs.length === 0
|
|
1296
1352
|
? undefined
|
|
1297
|
-
: `${resultClass}(${outputs
|
|
1353
|
+
: `${resultClass}(receipt: change.receipt, ${outputs
|
|
1298
1354
|
.map((output) =>
|
|
1299
1355
|
output.many
|
|
1300
1356
|
? `${output.field}: _nmsAffectedReferences<${
|
|
@@ -1331,11 +1387,12 @@ export function generateDartApplication(
|
|
|
1331
1387
|
` /// ${cap(camel(directive.id))}. ${businessActionDoc(directive)}`,
|
|
1332
1388
|
` /// Canonical write path: pass business facts here; never mirror this action in application state`,
|
|
1333
1389
|
` /// or call a lower-level client.`,
|
|
1334
|
-
` Future<
|
|
1390
|
+
` Future<NomosOperationReceipt> ${method}(${payload} facts) async {`,
|
|
1335
1391
|
...referenceChecks,
|
|
1336
1392
|
...sourceTrust.prelude,
|
|
1337
|
-
` await ${sourceTrust.call};`,
|
|
1393
|
+
` final change = await ${sourceTrust.call};`,
|
|
1338
1394
|
...afterBusinessWrite(directive),
|
|
1395
|
+
` return change.receipt;`,
|
|
1339
1396
|
` }`,
|
|
1340
1397
|
``
|
|
1341
1398
|
);
|
|
@@ -1359,10 +1416,11 @@ export function generateDartApplication(
|
|
|
1359
1416
|
} else {
|
|
1360
1417
|
addCollectionMember(directive.aggregateId, method, [
|
|
1361
1418
|
` /// ${cap(camel(directive.id))}.`,
|
|
1362
|
-
` Future<
|
|
1419
|
+
` Future<NomosOperationReceipt> ${method}(${payload} facts) async {`,
|
|
1363
1420
|
...referenceChecks,
|
|
1364
1421
|
...sourceTrust.prelude,
|
|
1365
|
-
` await ${sourceTrust.call};`,
|
|
1422
|
+
` final change = await ${sourceTrust.call};`,
|
|
1423
|
+
` return change.receipt;`,
|
|
1366
1424
|
` }`,
|
|
1367
1425
|
``,
|
|
1368
1426
|
]);
|
|
@@ -1834,10 +1892,16 @@ export function generateDartApplication(
|
|
|
1834
1892
|
``,
|
|
1835
1893
|
]);
|
|
1836
1894
|
addCollectionMember(aggregate.id, "all", [
|
|
1895
|
+
` @Deprecated(${JSON.stringify(
|
|
1896
|
+
`Reads every ${concrete} in the workspace. Prefer find(reference) or a declared keyed query; use all() only for an intentional whole-workspace operation.`
|
|
1897
|
+
)})`,
|
|
1837
1898
|
` Future<List<${model}>> all() => _client.${clientList}();`,
|
|
1838
1899
|
``,
|
|
1839
1900
|
]);
|
|
1840
1901
|
addCollectionMember(aggregate.id, "watchAll", [
|
|
1902
|
+
` @Deprecated(${JSON.stringify(
|
|
1903
|
+
`Watches every ${concrete} in the workspace. Prefer watchOne(reference) or a declared keyed watch; use watchAll() only for an intentional whole-workspace operation.`
|
|
1904
|
+
)})`,
|
|
1841
1905
|
` Stream<List<${model}>> watchAll() => _client.watchAll${Cap}s();`,
|
|
1842
1906
|
``,
|
|
1843
1907
|
]);
|
|
@@ -1901,7 +1965,7 @@ export function generateDartApplication(
|
|
|
1901
1965
|
...(sharingPolicies.length > 0
|
|
1902
1966
|
? [`import 'dart:async' show StreamSubscription, unawaited;`]
|
|
1903
1967
|
: []),
|
|
1904
|
-
`import '${runtimeImport}' show NmsInternalForeignRefs, NmsInternalNomosRefs, NmsInternalRecordOrigins, NomosBridge, NomosOfferOutcome, NomosRef, NomosSession${
|
|
1968
|
+
`import '${runtimeImport}' show NmsInternalForeignRefs, NmsInternalNomosRefs, NmsInternalRecordOrigins, NomosBridge, NomosOfferOutcome, NomosOperationReceipt, NomosRef, NomosSession${
|
|
1905
1969
|
usesWorkspaceName ? ", deriveWorkspaceName" : ""
|
|
1906
1970
|
}${
|
|
1907
1971
|
sharingPolicies.length > 0
|
|
@@ -1918,15 +1982,17 @@ export function generateDartApplication(
|
|
|
1918
1982
|
`import 'package:nomos_flutter/src/application_share.dart' show ApplicationShareResult;`,
|
|
1919
1983
|
]
|
|
1920
1984
|
: []),
|
|
1921
|
-
...(opts.flutterHost === true
|
|
1985
|
+
...(opts.flutterHost === true
|
|
1922
1986
|
? [
|
|
1923
|
-
`import 'package:nomos_flutter/src/application_status.dart' show
|
|
1987
|
+
`import 'package:nomos_flutter/src/application_status.dart' show ${
|
|
1988
|
+
tenantRoot ? "ApplicationSignIn, " : ""
|
|
1989
|
+
}ApplicationStatus;`,
|
|
1924
1990
|
]
|
|
1925
1991
|
: []),
|
|
1926
1992
|
`import 'package:nomos_flutter/src/generated_application_host.dart' show ${
|
|
1927
|
-
tenantRoot ? "NomosApplicationStartupState, " : ""
|
|
1993
|
+
tenantRoot ? "ApplicationModelChange, NomosApplicationStartupState, " : ""
|
|
1928
1994
|
}NmsInternalApplicationUserBinding${
|
|
1929
|
-
|
|
1995
|
+
opts.flutterHost === true || sharingPolicies.length > 0
|
|
1930
1996
|
? ", NmsInternalApplicationHost"
|
|
1931
1997
|
: ""
|
|
1932
1998
|
}${
|
|
@@ -1978,14 +2044,25 @@ export function generateDartApplication(
|
|
|
1978
2044
|
``,
|
|
1979
2045
|
` /// Prove, adopt or restore the law this application was generated against.`,
|
|
1980
2046
|
` NomosModel get model => _client.model;`,
|
|
1981
|
-
...(opts.flutterHost === true
|
|
2047
|
+
...(opts.flutterHost === true
|
|
1982
2048
|
? [
|
|
1983
2049
|
``,
|
|
1984
|
-
` /// Current durability and convergence in product language.`,
|
|
2050
|
+
` /// Current durability and convergence for this workspace in product language.`,
|
|
1985
2051
|
` ApplicationStatus get currentStatus => NmsInternalApplicationHost.currentStatus(_client.bridge);`,
|
|
1986
2052
|
``,
|
|
1987
|
-
` /// React whenever this
|
|
2053
|
+
` /// React whenever this workspace saves, receives work, goes offline or needs attention.`,
|
|
1988
2054
|
` Stream<ApplicationStatus> watchStatus() => NmsInternalApplicationHost.watchStatus(_client.bridge);`,
|
|
2055
|
+
``,
|
|
2056
|
+
` /// Wait until accepted local work is in shared custody and this workspace has caught up.`,
|
|
2057
|
+
` Future<void> whenCurrent({Duration timeout = const Duration(minutes: 2)}) =>`,
|
|
2058
|
+
` NmsInternalApplicationHost.whenCurrent(_client.bridge, timeout: timeout);`,
|
|
2059
|
+
``,
|
|
2060
|
+
` /// The parent operation which created this workspace during this application run, if any.`,
|
|
2061
|
+
` NomosOperationReceipt? get creationReceipt => NmsInternalApplicationHost.creationReceipt(_client.bridge);`,
|
|
2062
|
+
]
|
|
2063
|
+
: []),
|
|
2064
|
+
...(opts.flutterHost === true && tenantRoot
|
|
2065
|
+
? [
|
|
1989
2066
|
``,
|
|
1990
2067
|
` /// Mount the one application at the UI boundary and pass [application] down.`,
|
|
1991
2068
|
` ///`,
|
|
@@ -2001,12 +2078,16 @@ export function generateDartApplication(
|
|
|
2001
2078
|
` required Widget Function(BuildContext context, ${application} application) builder,`,
|
|
2002
2079
|
` Widget Function(BuildContext context)? loadingBuilder,`,
|
|
2003
2080
|
` Widget Function(BuildContext context, Object error, VoidCallback retry)? errorBuilder,`,
|
|
2081
|
+
` ApplicationModelChange? onModelChange,`,
|
|
2004
2082
|
` ValueChanged<NomosApplicationStartupState>? onStartupStateChanged,`,
|
|
2083
|
+
` Duration startupTimeout = const Duration(seconds: 45),`,
|
|
2005
2084
|
` }) => NmsInternalApplicationHost.home<${application}>(`,
|
|
2006
2085
|
` key: key, cloud: cloud, application: application, signIn: signIn,`,
|
|
2007
2086
|
` bind: nomosInternalBind${application}, builder: builder,`,
|
|
2008
2087
|
` loadingBuilder: loadingBuilder, errorBuilder: errorBuilder,`,
|
|
2088
|
+
` onModelChange: onModelChange,`,
|
|
2009
2089
|
` onStartupStateChanged: onStartupStateChanged,`,
|
|
2090
|
+
` startupTimeout: startupTimeout,`,
|
|
2010
2091
|
` );`,
|
|
2011
2092
|
]
|
|
2012
2093
|
: []),
|
|
@@ -2132,6 +2213,13 @@ export function generateDartApplication(
|
|
|
2132
2213
|
``,
|
|
2133
2214
|
`Future<${application}> nomosInternalOpen${application}<TAggregate>(`,
|
|
2134
2215
|
` NomosBridge bridge, NomosRef<TAggregate> reference, String? actor) async {`,
|
|
2216
|
+
...(opts.flutterHost === true
|
|
2217
|
+
? [
|
|
2218
|
+
` return NmsInternalApplicationHost.nmsInternalOpenReferencedApplication<${application}>(`,
|
|
2219
|
+
` parent: bridge,`,
|
|
2220
|
+
` open: () async {`,
|
|
2221
|
+
]
|
|
2222
|
+
: []),
|
|
2135
2223
|
...(tenantRoot
|
|
2136
2224
|
? [
|
|
2137
2225
|
` final signedIn = actor?.trim() ?? '';`,
|
|
@@ -2151,6 +2239,13 @@ export function generateDartApplication(
|
|
|
2151
2239
|
? [` await application._nmsStartWorkspaceSharing();`]
|
|
2152
2240
|
: []),
|
|
2153
2241
|
` return application;`,
|
|
2242
|
+
...(opts.flutterHost === true
|
|
2243
|
+
? [
|
|
2244
|
+
` },`,
|
|
2245
|
+
` bridgeOf: (application) => application._client.bridge,`,
|
|
2246
|
+
` );`,
|
|
2247
|
+
]
|
|
2248
|
+
: []),
|
|
2154
2249
|
`}`,
|
|
2155
2250
|
``,
|
|
2156
2251
|
].join("\n");
|
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
OWNED_PREMINTED_RECORDS_PAYLOAD_FIELD,
|
|
36
36
|
type AggregateHandle,
|
|
37
37
|
} from "./aggregate.js";
|
|
38
|
-
import { isSha256DigestSchema, type Field } from "./fields.js";
|
|
38
|
+
import { isGitObjectIdSchema, isSha256DigestSchema, type Field } from "./fields.js";
|
|
39
39
|
import type { Directive } from "./directive.js";
|
|
40
40
|
import type { QueryDecl } from "./query.js";
|
|
41
41
|
import type { DomainModule } from "./codegen_dart.js";
|
|
@@ -165,6 +165,9 @@ function dartKeyType(agg: AggregateHandle | undefined, key: string): string {
|
|
|
165
165
|
if (f?.kind === "string" && isSha256DigestSchema(f.zod as z.ZodTypeAny)) {
|
|
166
166
|
return "Sha256Digest";
|
|
167
167
|
}
|
|
168
|
+
if (f?.kind === "string" && isGitObjectIdSchema(f.zod as z.ZodTypeAny)) {
|
|
169
|
+
return "GitObjectId";
|
|
170
|
+
}
|
|
168
171
|
return "String";
|
|
169
172
|
}
|
|
170
173
|
|
|
@@ -178,6 +181,12 @@ function dartKeyExpr(agg: AggregateHandle | undefined, key: string): string {
|
|
|
178
181
|
) {
|
|
179
182
|
return `${param}.value`;
|
|
180
183
|
}
|
|
184
|
+
if (
|
|
185
|
+
field?.kind === "string" &&
|
|
186
|
+
isGitObjectIdSchema(field.zod as z.ZodTypeAny)
|
|
187
|
+
) {
|
|
188
|
+
return `${param}.value`;
|
|
189
|
+
}
|
|
181
190
|
if (field?.kind !== "ref") return param;
|
|
182
191
|
return field.foreignRef !== undefined
|
|
183
192
|
? `NmsInternalNomosRefs.id(${param}.ref)`
|
|
@@ -210,6 +219,10 @@ function rowToModel(model: string, rowVar: string, workspace = "_ws"): string {
|
|
|
210
219
|
export interface DartBridgeClientOptions {
|
|
211
220
|
/** Content identity of the built typed USDA closure (`.law.usdz`). */
|
|
212
221
|
readonly domainHash: string;
|
|
222
|
+
/** Flutter asset key for the compiler-authored USDZ transport. Generated
|
|
223
|
+
* child openers use it to inherit an accepted parent model through the
|
|
224
|
+
* child's ordinary author gate. Omit for pure-Dart/vendored clients. */
|
|
225
|
+
readonly lawAssetKey?: string;
|
|
213
226
|
/** This domain's STRUCTURAL INTERFACE (#72) — directives (payload shapes) + aggregates
|
|
214
227
|
* (read shapes, sid-keyed). Baked in as JSON so the app can negotiate shape-compatibility
|
|
215
228
|
* against the workspace's installed offer, NOT by law hash. */
|
|
@@ -1602,6 +1615,19 @@ export function generateDartBridgeClient(
|
|
|
1602
1615
|
` differences.add(NomosModelDifference(kind: kind, area: area, action: action, summary: summary));`,
|
|
1603
1616
|
` }`,
|
|
1604
1617
|
` final evolve = (report?['evolve'] as Map?)?.cast<String, Object?>();`,
|
|
1618
|
+
` for (final raw in ((evolve?['violations'] as List?) ?? const [])) {`,
|
|
1619
|
+
` if (raw is! Map) continue;`,
|
|
1620
|
+
` final violation = raw.cast<String, Object?>();`,
|
|
1621
|
+
` final rawDomain = violation['domain']?.toString().trim();`,
|
|
1622
|
+
` final rawDetail = violation['detail']?.toString().trim();`,
|
|
1623
|
+
` differences.add(NomosModelDifference(`,
|
|
1624
|
+
` kind: NomosModelDifferenceKind.rules,`,
|
|
1625
|
+
` area: rawDomain == null || rawDomain.isEmpty ? null : rawDomain,`,
|
|
1626
|
+
` summary: rawDetail == null || rawDetail.isEmpty`,
|
|
1627
|
+
` ? 'This model does not declare how to interpret an existing value.'`,
|
|
1628
|
+
` : rawDetail,`,
|
|
1629
|
+
` ));`,
|
|
1630
|
+
` }`,
|
|
1605
1631
|
` if (report != null && evolve?['ok'] != true && !differences.any((d) => d.kind == NomosModelDifferenceKind.rules)) {`,
|
|
1606
1632
|
` differences.add(const NomosModelDifference(kind: NomosModelDifferenceKind.rules, summary: 'This model does not yet explain how to interpret every existing value.'));`,
|
|
1607
1633
|
` }`,
|
|
@@ -1629,13 +1655,30 @@ export function generateDartBridgeClient(
|
|
|
1629
1655
|
` return NomosModelAdoption.adopted(`,
|
|
1630
1656
|
` summary: 'Nomos proved this model against current history and made it active.', review: review);`,
|
|
1631
1657
|
` }`,
|
|
1658
|
+
...(opts.lawAssetKey === undefined
|
|
1659
|
+
? []
|
|
1660
|
+
: [
|
|
1661
|
+
` if (stage == 'staging-required') {`,
|
|
1662
|
+
` return NomosModelAdoption.needsStaging(`,
|
|
1663
|
+
` summary: 'This workspace is running an older model. Stage this generated USDA candidate through the signed workspace gate before reviewing it.',`,
|
|
1664
|
+
` review: review,`,
|
|
1665
|
+
` stage: () async {`,
|
|
1666
|
+
` await _stageGeneratedModel();`,
|
|
1667
|
+
` return _adoptGeneratedModel();`,
|
|
1668
|
+
` },`,
|
|
1669
|
+
` );`,
|
|
1670
|
+
` }`,
|
|
1671
|
+
]),
|
|
1632
1672
|
` if (stage == 'acceptance') {`,
|
|
1633
1673
|
` return NomosModelAdoption.needsDecision(`,
|
|
1634
1674
|
` summary: 'This model would change prior business outcomes. Review the differences and record why that is intended.',`,
|
|
1635
1675
|
` review: review, accept: (reason) => _adoptGeneratedModel(reason));`,
|
|
1636
1676
|
` }`,
|
|
1637
1677
|
` final evolve = (report?['evolve'] as Map?)?.cast<String, Object?>();`,
|
|
1638
|
-
` final
|
|
1678
|
+
` final rawError = raw['error']?.toString().trim();`,
|
|
1679
|
+
` final summary = rawError != null && rawError.isNotEmpty`,
|
|
1680
|
+
` ? rawError`,
|
|
1681
|
+
` : stage == 'identity'`,
|
|
1639
1682
|
` ? 'Sign in before adopting this model so the decision is accountable.'`,
|
|
1640
1683
|
` : report != null && report['sourceVerified'] != true`,
|
|
1641
1684
|
` ? 'The current history could not be verified. Nothing changed and the current model remains active.'`,
|
|
@@ -1647,6 +1690,20 @@ export function generateDartBridgeClient(
|
|
|
1647
1690
|
` return NomosModelAdoption.blocked(summary: summary, review: review);`,
|
|
1648
1691
|
` }`,
|
|
1649
1692
|
``,
|
|
1693
|
+
...(opts.lawAssetKey === undefined
|
|
1694
|
+
? []
|
|
1695
|
+
: [
|
|
1696
|
+
` Future<void> _stageGeneratedModel() async {`,
|
|
1697
|
+
` final archive = await rootBundle.load(${dartStr(opts.lawAssetKey)});`,
|
|
1698
|
+
` await bridge.nomosInternalStageGeneratedModel(`,
|
|
1699
|
+
` domain: ${dartStr(domain)},`,
|
|
1700
|
+
` candidateHash: domainHash,`,
|
|
1701
|
+
` lawUsdz: archive.buffer.asUint8List(`,
|
|
1702
|
+
` archive.offsetInBytes, archive.lengthInBytes),`,
|
|
1703
|
+
` );`,
|
|
1704
|
+
` }`,
|
|
1705
|
+
``,
|
|
1706
|
+
]),
|
|
1650
1707
|
` Future<NomosModelRestoration> _restorePreviousGeneratedModel([String? because]) async {`,
|
|
1651
1708
|
` final raw = await bridge.nomosFrameworkRestorePreviousGeneratedModel(`,
|
|
1652
1709
|
` domain: ${dartStr(
|
|
@@ -2159,6 +2216,9 @@ export function generateDartBridgeClient(
|
|
|
2159
2216
|
? [`import 'dart:convert';`, ``]
|
|
2160
2217
|
: []),
|
|
2161
2218
|
`import '${supportImport}';`,
|
|
2219
|
+
...(opts.lawAssetKey !== undefined
|
|
2220
|
+
? [`import 'package:flutter/services.dart' show rootBundle;`]
|
|
2221
|
+
: []),
|
|
2162
2222
|
// Layered-packages mode: the sibling types live in their OWN package — one barrel import
|
|
2163
2223
|
// replaces both the relative types import and the relative framework import.
|
|
2164
2224
|
...(opts.typesImportUri !== undefined
|
|
@@ -2299,6 +2359,66 @@ export function generateDartBridgeClient(
|
|
|
2299
2359
|
` static Future<${className}> bind(NomosSession session, {String? actor${
|
|
2300
2360
|
manualEncryptionPresent ? ", NomosKeyStore? keyStore" : ""
|
|
2301
2361
|
}}) async {`,
|
|
2362
|
+
` final candidate = ${className}(session, actor: actor${
|
|
2363
|
+
manualEncryptionPresent ? ", keyStore: keyStore" : ""
|
|
2364
|
+
});`,
|
|
2365
|
+
` Future<void> adoptGeneratedModel() async {`,
|
|
2366
|
+
` final adoption = await candidate._adoptGeneratedModel();`,
|
|
2367
|
+
` if (adoption.status == NomosModelAdoptionStatus.adopted) return;`,
|
|
2368
|
+
` throw DomainClientModelAdoptionRequired(`,
|
|
2369
|
+
` expectedDomainKey: interface.domain,`,
|
|
2370
|
+
` expectedDomainHash: interface.expectedDomainHash,`,
|
|
2371
|
+
` status: adoption.status.name,`,
|
|
2372
|
+
` summary: adoption.summary,`,
|
|
2373
|
+
` review: DomainModelReview(`,
|
|
2374
|
+
` checkedActions: adoption.review.checkedActions,`,
|
|
2375
|
+
` candidateActions: adoption.review.candidateActions,`,
|
|
2376
|
+
` sameOutcome: adoption.review.sameOutcome,`,
|
|
2377
|
+
` differences: [`,
|
|
2378
|
+
` for (final difference in adoption.review.differences)`,
|
|
2379
|
+
` DomainModelDifference(`,
|
|
2380
|
+
` kind: switch (difference.kind) {`,
|
|
2381
|
+
` NomosModelDifferenceKind.rules => DomainModelDifferenceKind.rules,`,
|
|
2382
|
+
` NomosModelDifferenceKind.outcome => DomainModelDifferenceKind.outcome,`,
|
|
2383
|
+
` NomosModelDifferenceKind.evidence => DomainModelDifferenceKind.evidence,`,
|
|
2384
|
+
` },`,
|
|
2385
|
+
` area: difference.area,`,
|
|
2386
|
+
` action: difference.action,`,
|
|
2387
|
+
` summary: difference.summary,`,
|
|
2388
|
+
` ),`,
|
|
2389
|
+
` ],`,
|
|
2390
|
+
` ),`,
|
|
2391
|
+
...(opts.lawAssetKey === undefined
|
|
2392
|
+
? [` stage: null,`]
|
|
2393
|
+
: [
|
|
2394
|
+
` stage: adoption.status == NomosModelAdoptionStatus.needsStaging`,
|
|
2395
|
+
` ? () => candidate._stageGeneratedModel()`,
|
|
2396
|
+
` : null,`,
|
|
2397
|
+
]),
|
|
2398
|
+
` accept: adoption.status == NomosModelAdoptionStatus.needsDecision`,
|
|
2399
|
+
` ? (reason) async {`,
|
|
2400
|
+
` final accepted = await adoption.accept(reason);`,
|
|
2401
|
+
` if (accepted.status != NomosModelAdoptionStatus.adopted) {`,
|
|
2402
|
+
` throw StateError(accepted.summary);`,
|
|
2403
|
+
` }`,
|
|
2404
|
+
` }`,
|
|
2405
|
+
` : null,`,
|
|
2406
|
+
` );`,
|
|
2407
|
+
` }`,
|
|
2408
|
+
` try {`,
|
|
2409
|
+
` await session.assertCompatible(interface);`,
|
|
2410
|
+
` } on DomainClientSessionMismatch catch (mismatch) {`,
|
|
2411
|
+
` // A deployed successor is deliberately invisible until the generated candidate proves it`,
|
|
2412
|
+
` // against this workspace's exact history. Do that before rejecting the candidate for`,
|
|
2413
|
+
` // expecting its own newer interface; all other bind failures retain their precise cause.`,
|
|
2414
|
+
` if (!mismatch.canAttemptGeneratedModelAdoption) rethrow;`,
|
|
2415
|
+
` await adoptGeneratedModel();`,
|
|
2416
|
+
` }`,
|
|
2417
|
+
` // Shape compatibility governs readable structure; exact package identity governs`,
|
|
2418
|
+
` // which plans may author. A plan-only successor must therefore enter the same`,
|
|
2419
|
+
` // proof-gated lifecycle even when every generated read type remains compatible.`,
|
|
2420
|
+
` final law = await session.lawStatus(interface.expectedDomainHash);`,
|
|
2421
|
+
` if (law['drifted'] == true) await adoptGeneratedModel();`,
|
|
2302
2422
|
` await session.assertCompatible(interface);`,
|
|
2303
2423
|
` // RELEASE COHERENCE: the cloud's kernel must support what this law needs (e.g. a compound-key query`,
|
|
2304
2424
|
` // needs 'compoundKeys'). Throws a typed KernelCoherenceError on a stale/mismatched cloud kernel,`,
|
|
@@ -2311,9 +2431,7 @@ export function generateDartBridgeClient(
|
|
|
2311
2431
|
ownedScope
|
|
2312
2432
|
)}, domain: ${dartStr(domain)}, domainHash: ${hashConst});`,
|
|
2313
2433
|
]),
|
|
2314
|
-
` return
|
|
2315
|
-
manualEncryptionPresent ? ", keyStore: keyStore" : ""
|
|
2316
|
-
}).._preflighted = true${
|
|
2434
|
+
` return candidate.._preflighted = true${
|
|
2317
2435
|
ownedScope === undefined ? "" : ".._ownedIdentityReady = true"
|
|
2318
2436
|
};`,
|
|
2319
2437
|
` }`,
|
|
@@ -2332,9 +2450,34 @@ export function generateDartBridgeClient(
|
|
|
2332
2450
|
` throw ArgumentError('${className}.open: pass a workspace: or ref: — the workspace to open and bind');`,
|
|
2333
2451
|
` }`,
|
|
2334
2452
|
` final session = await bridge.acquireSession(ws);`,
|
|
2335
|
-
|
|
2453
|
+
...(opts.lawAssetKey === undefined
|
|
2454
|
+
? []
|
|
2455
|
+
: [
|
|
2456
|
+
` final law = await session.lawStatus(${hashConst});`,
|
|
2457
|
+
` if (law['drifted'] == true) {`,
|
|
2458
|
+
` if (bridge is! NomosSession) {`,
|
|
2459
|
+
` throw StateError('Generated child model inheritance needs a bound parent session.');`,
|
|
2460
|
+
` }`,
|
|
2461
|
+
` final archive = await rootBundle.load(${dartStr(opts.lawAssetKey)});`,
|
|
2462
|
+
` await bridge.nomosInternalEnsureGeneratedModelInChild(`,
|
|
2463
|
+
` childWorkspace: ws,`,
|
|
2464
|
+
` domain: interface.domain,`,
|
|
2465
|
+
` candidateHash: ${hashConst},`,
|
|
2466
|
+
` lawUsdz: archive.buffer.asUint8List(`,
|
|
2467
|
+
` archive.offsetInBytes, archive.lengthInBytes),`,
|
|
2468
|
+
` );`,
|
|
2469
|
+
` }`,
|
|
2470
|
+
]),
|
|
2471
|
+
` for (var modelStep = 0; modelStep < 4; modelStep += 1) {`,
|
|
2472
|
+
` try {`,
|
|
2473
|
+
` return await bind(session, actor: actor${
|
|
2336
2474
|
manualEncryptionPresent ? ", keyStore: keyStore" : ""
|
|
2337
2475
|
});`,
|
|
2476
|
+
` } on DomainClientModelAdoptionRequired catch (change) {`,
|
|
2477
|
+
` await session.nomosInternalHandleModelChange(change);`,
|
|
2478
|
+
` }`,
|
|
2479
|
+
` }`,
|
|
2480
|
+
` throw StateError('Generated model lifecycle did not settle after four governed steps.');`,
|
|
2338
2481
|
` }`,
|
|
2339
2482
|
``,
|
|
2340
2483
|
` /// The session workspace this client is bound to (from the [NomosSession] handle, when constructed with`,
|
package/src/codegen_proof.ts
CHANGED
|
@@ -326,7 +326,7 @@ function sampleOfZod(
|
|
|
326
326
|
export function synthesizePayload(
|
|
327
327
|
schema: z.ZodTypeAny,
|
|
328
328
|
where: string,
|
|
329
|
-
opts?: { voIncludeOptional?: boolean; example?: unknown },
|
|
329
|
+
opts?: { voIncludeOptional?: boolean; includeOptionalTopLevel?: boolean; example?: unknown },
|
|
330
330
|
): SynthesizedPayload {
|
|
331
331
|
if (zodKind(schema) !== "object") {
|
|
332
332
|
throw new Error(`codegen-proof: ${where} payload is not a zod object — the proof can only synthesize object payloads`);
|
|
@@ -348,8 +348,9 @@ export function synthesizePayload(
|
|
|
348
348
|
for (const [name, raw] of Object.entries(zodObjectShape(schema))) {
|
|
349
349
|
const f = raw as z.ZodTypeAny;
|
|
350
350
|
const k = zodKind(f);
|
|
351
|
-
if (k === "optional" || k === "default") continue; // optional → omit
|
|
352
351
|
if (stamped.has(name)) continue; // `…At` → omit; the client stamps it
|
|
352
|
+
if (k === "default") continue; // defaulted fields exercise their declared default
|
|
353
|
+
if (k === "optional" && opts?.includeOptionalTopLevel !== true) continue;
|
|
353
354
|
value[name] = sampleOfZod(f, name, where, opts?.voIncludeOptional === true); // path root = the payload field name
|
|
354
355
|
}
|
|
355
356
|
return { value, autoStamped };
|
|
@@ -361,11 +362,11 @@ export function synthesizePayload(
|
|
|
361
362
|
export function synthesizeDirectivePayload(
|
|
362
363
|
directive: Directive<any>,
|
|
363
364
|
where: string,
|
|
364
|
-
opts?: { voIncludeOptional?: boolean },
|
|
365
|
+
opts?: { voIncludeOptional?: boolean; includeOptionalTopLevel?: boolean; useDirectiveExample?: boolean },
|
|
365
366
|
): SynthesizedPayload {
|
|
366
367
|
return synthesizePayload(directive.payloadSchema as z.ZodTypeAny, where, {
|
|
367
368
|
...opts,
|
|
368
|
-
...(directive.payloadExample === undefined
|
|
369
|
+
...(directive.payloadExample === undefined || opts?.useDirectiveExample === false
|
|
369
370
|
? {}
|
|
370
371
|
: { example: directive.payloadExample }),
|
|
371
372
|
});
|
|
@@ -701,10 +702,11 @@ function probePlanOpsWithPayload(
|
|
|
701
702
|
export function probePlanOps(
|
|
702
703
|
dir: Directive<unknown>,
|
|
703
704
|
where: string,
|
|
705
|
+
opts?: { includeOptionalTopLevel?: boolean; useDirectiveExample?: boolean },
|
|
704
706
|
): { ops: FieldOp[]; synth: SynthesizedPayload; probePayload: Record<string, unknown> } | undefined {
|
|
705
707
|
let synth: SynthesizedPayload;
|
|
706
708
|
try {
|
|
707
|
-
synth = synthesizeDirectivePayload(dir, where);
|
|
709
|
+
synth = synthesizeDirectivePayload(dir, where, opts);
|
|
708
710
|
} catch {
|
|
709
711
|
return undefined;
|
|
710
712
|
}
|
|
@@ -3235,9 +3237,14 @@ function generateSealedApplicationProof(
|
|
|
3235
3237
|
: { applicationMaxChildWorkspacesPerUser: opts.applicationMaxChildWorkspacesPerUser }),
|
|
3236
3238
|
});
|
|
3237
3239
|
const standalone = orderedStandaloneCreates(mod)[0];
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3240
|
+
// A home/application domain normally carries an internal receiving-group self-birth before its public
|
|
3241
|
+
// business births. `childBirth` preserves that first leg for backwards-compatible proof metadata, but the
|
|
3242
|
+
// live generated application journey must exercise the first PUBLIC birth from the complete set. Looking
|
|
3243
|
+
// only at `childBirth` made a CO2 proof describe birthEstateWorkspace in its marker while executing an
|
|
3244
|
+
// unrelated AccessibleEstate write, so a broken estate genesis reached AppSim instead of failing compile.
|
|
3245
|
+
const businessBirth = (primaryLegs.childBirths ??
|
|
3246
|
+
(primaryLegs.childBirth === undefined ? [] : [primaryLegs.childBirth]))
|
|
3247
|
+
.find((birth) => birth.businessAggregateId !== undefined);
|
|
3241
3248
|
const selectedBirthDirective = businessBirth === undefined
|
|
3242
3249
|
? undefined
|
|
3243
3250
|
: (mod.applicationIntents ?? mod.directives).find(
|
|
@@ -3351,7 +3358,7 @@ function generateSealedApplicationProof(
|
|
|
3351
3358
|
|
|
3352
3359
|
if (businessBirth !== undefined) {
|
|
3353
3360
|
const businessPayload = Object.fromEntries(
|
|
3354
|
-
Object.entries(businessBirth.birthPayload).filter(([field]) => !["principal", "bornAt"
|
|
3361
|
+
Object.entries(businessBirth.birthPayload).filter(([field]) => !["principal", "bornAt"].includes(field)),
|
|
3355
3362
|
);
|
|
3356
3363
|
out.push(
|
|
3357
3364
|
`const facts = ${JSON.stringify(businessPayload)};`,
|
|
@@ -202,6 +202,11 @@ function obligationFromJsonExpr(s: PayloadFieldSpec): string {
|
|
|
202
202
|
`NmsInternalNomosRefs.create<${target}>(workspace: '', type: '${wireType}', id: ${v} as String)`;
|
|
203
203
|
return s.optional ? `${raw} == null ? null : ${wrap(raw)}` : wrap(raw);
|
|
204
204
|
}
|
|
205
|
+
case "addressRef": {
|
|
206
|
+
const target = s.dartType.replace(/^NomosRef<(.*)>$/, "$1");
|
|
207
|
+
const wrap = (v: string) => `NmsInternalNomosRefs.fromJson<${target}>(${v})`;
|
|
208
|
+
return s.optional ? `${raw} == null ? null : ${wrap(raw)}` : wrap(raw);
|
|
209
|
+
}
|
|
205
210
|
case "versionedRef": {
|
|
206
211
|
const target = s.versionedRefTarget as string;
|
|
207
212
|
const series = s.versionedRefSeries as string;
|