@githolon/dsl 0.102.3 → 0.102.4
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/package.json +1 -1
- package/src/codegen_dart.ts +2 -2
- package/src/codegen_dart_application.ts +90 -30
- package/src/codegen_ts.ts +1 -1
- package/src/fields.ts +8 -1
- package/src/value_object.ts +8 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@githolon/dsl",
|
|
3
|
-
"version": "0.102.
|
|
3
|
+
"version": "0.102.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Nomos 2 domain-authoring DSL: aggregates + directives in TS, executed and encoded to the Rust kernel's wire shapes.",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE.md",
|
package/src/codegen_dart.ts
CHANGED
|
@@ -1707,7 +1707,7 @@ function dartTypeOfVoField(f: ValueObjectFieldDescriptor, voName: string, fieldN
|
|
|
1707
1707
|
switch (f.type) {
|
|
1708
1708
|
case "string":
|
|
1709
1709
|
return f.idOf !== undefined
|
|
1710
|
-
? `NomosRef<${concreteClassName(f.idOf)}>`
|
|
1710
|
+
? `NomosRef<${f.idOfContract === true ? contractTypeName(f.idOf) : concreteClassName(f.idOf)}>`
|
|
1711
1711
|
: f.brand ?? "String"; // a brandedId(...) → the generated extension-type wrapper
|
|
1712
1712
|
case "number":
|
|
1713
1713
|
// a UNIT-tagged number is the generated zero-cost extension type; `.int()` is Dart int.
|
|
@@ -1742,7 +1742,7 @@ function voFromExpr(
|
|
|
1742
1742
|
switch (f.type) {
|
|
1743
1743
|
case "string":
|
|
1744
1744
|
if (f.idOf !== undefined) {
|
|
1745
|
-
const target = concreteClassName(f.idOf);
|
|
1745
|
+
const target = f.idOfContract === true ? contractTypeName(f.idOf) : concreteClassName(f.idOf);
|
|
1746
1746
|
return `NmsInternalNomosRefs.create<${target}>(workspace: '', type: '${f.idOf}', id: ${expr} as String)`;
|
|
1747
1747
|
}
|
|
1748
1748
|
return f.brand !== undefined ? `${f.brand}(${expr} as String)` : `${expr} as String`;
|
|
@@ -721,6 +721,11 @@ export function generateDartApplication(mod: DomainModule, opts: DartApplication
|
|
|
721
721
|
const targetFile = owner?.name ?? mod.name;
|
|
722
722
|
const targetApplication = dartApplicationClassName(targetDomain);
|
|
723
723
|
const opener = `nomosInternalOpen${targetApplication}`;
|
|
724
|
+
const rootAggregate = aggregates.get(birth.aggregateId);
|
|
725
|
+
const rootType = concreteClassName(birth.aggregateId);
|
|
726
|
+
const rootCollection = member(plural(rootAggregate === undefined
|
|
727
|
+
? birth.aggregateId
|
|
728
|
+
: concreteClassName(rootAggregate.id)));
|
|
724
729
|
if (targetFile !== mod.name) {
|
|
725
730
|
addSibling(`${targetFile}.application.dart`, targetApplication);
|
|
726
731
|
addSibling(`${targetFile}.application.dart`, opener);
|
|
@@ -735,7 +740,9 @@ export function generateDartApplication(mod: DomainModule, opts: DartApplication
|
|
|
735
740
|
` /// or call a lower-level client.`,
|
|
736
741
|
` Future<${targetApplication}> ${method}(${dartActionInputClassName(directive)} facts) async {`,
|
|
737
742
|
` final reference = await _client.${method}(facts);`,
|
|
738
|
-
|
|
743
|
+
opts.supportImport === "package"
|
|
744
|
+
? ` return ${rootCollection}._nmsOpenReference(reference);`
|
|
745
|
+
: ` return ${opener}(_client.bridge, reference, _client.actor);`,
|
|
739
746
|
` }`,
|
|
740
747
|
``,
|
|
741
748
|
);
|
|
@@ -744,31 +751,47 @@ export function generateDartApplication(mod: DomainModule, opts: DartApplication
|
|
|
744
751
|
` /// Give one ${birth.aggregateId} its own durable life.`,
|
|
745
752
|
` Future<${targetApplication}> birth(Create${cap(camel(birth.aggregateId))}Input facts) async {`,
|
|
746
753
|
` final reference = await _client.birth(facts);`,
|
|
747
|
-
|
|
754
|
+
opts.supportImport === "package"
|
|
755
|
+
? ` return _nmsOpenReference(reference);`
|
|
756
|
+
: ` return ${opener}(_client.bridge, reference, _client.actor);`,
|
|
748
757
|
` }`,
|
|
749
758
|
``,
|
|
750
759
|
]);
|
|
751
760
|
}
|
|
752
761
|
if (opts.supportImport === "package") {
|
|
753
762
|
usesWorkspaceName = true;
|
|
754
|
-
const rootAggregate = aggregates.get(birth.aggregateId);
|
|
755
763
|
const rootModel = rootAggregate === undefined
|
|
756
764
|
? `${concreteClassName(birth.aggregateId)}ReadModel`
|
|
757
765
|
: readModelClassName(rootAggregate.id);
|
|
758
|
-
const rootType = concreteClassName(birth.aggregateId);
|
|
759
|
-
const rootCollection = member(plural(rootAggregate === undefined
|
|
760
|
-
? birth.aggregateId
|
|
761
|
-
: concreteClassName(rootAggregate.id)));
|
|
762
766
|
addCollectionMember(birth.aggregateId, "open", [
|
|
767
|
+
` final Map<String, Future<${targetApplication}>> _nmsApplications = {};`,
|
|
768
|
+
``,
|
|
769
|
+
` Future<${targetApplication}> _nmsOpenReference(NomosRef<${rootType}> reference) {`,
|
|
770
|
+
` final workspace = NmsInternalNomosRefs.workspace(reference);`,
|
|
771
|
+
` final existing = _nmsApplications[workspace];`,
|
|
772
|
+
` if (existing != null) return existing;`,
|
|
773
|
+
` late final Future<${targetApplication}> opened;`,
|
|
774
|
+
` opened = (() async {`,
|
|
775
|
+
` try {`,
|
|
776
|
+
` return await ${opener}(_client.bridge, reference, _client.actor);`,
|
|
777
|
+
` } catch (_) {`,
|
|
778
|
+
` if (identical(_nmsApplications[workspace], opened)) {`,
|
|
779
|
+
` _nmsApplications.remove(workspace);`,
|
|
780
|
+
` }`,
|
|
781
|
+
` rethrow;`,
|
|
782
|
+
` }`,
|
|
783
|
+
` })();`,
|
|
784
|
+
` _nmsApplications[workspace] = opened;`,
|
|
785
|
+
` return opened;`,
|
|
786
|
+
` }`,
|
|
787
|
+
``,
|
|
763
788
|
` /// Open the ${birth.aggregateId} whose ${birth.keyField} is [key].`,
|
|
764
|
-
` Future<${targetApplication}> open(String key) =>
|
|
765
|
-
` _client.bridge,`,
|
|
789
|
+
` Future<${targetApplication}> open(String key) => _nmsOpenReference(`,
|
|
766
790
|
` NmsInternalNomosRefs.create<${rootType}>(`,
|
|
767
791
|
` workspace: deriveWorkspaceName(${JSON.stringify(birth.namespace)}, key),`,
|
|
768
792
|
` type: ${JSON.stringify(birth.aggregateId)},`,
|
|
769
793
|
` id: ${JSON.stringify(birth.rootId)},`,
|
|
770
794
|
` ),`,
|
|
771
|
-
` _client.actor,`,
|
|
772
795
|
` );`,
|
|
773
796
|
``,
|
|
774
797
|
]);
|
|
@@ -790,14 +813,9 @@ export function generateDartApplication(mod: DomainModule, opts: DartApplication
|
|
|
790
813
|
` if (raw is! Map) continue;`,
|
|
791
814
|
` final child = (raw['child'] ?? '').toString();`,
|
|
792
815
|
` if (!child.startsWith(${JSON.stringify(`${birth.namespace}-`)})) continue;`,
|
|
793
|
-
` final
|
|
794
|
-
`
|
|
795
|
-
`
|
|
796
|
-
` _client.actor,`,
|
|
797
|
-
` );`,
|
|
798
|
-
` final record = await application.${rootCollection}.find(`,
|
|
799
|
-
` NmsInternalNomosRefs.create<${rootType}>(workspace: child, type: ${JSON.stringify(birth.aggregateId)}, id: ${JSON.stringify(birth.rootId)}),`,
|
|
800
|
-
` );`,
|
|
816
|
+
` final reference = NmsInternalNomosRefs.create<${rootType}>(workspace: child, type: ${JSON.stringify(birth.aggregateId)}, id: ${JSON.stringify(birth.rootId)});`,
|
|
817
|
+
` final application = await _nmsOpenReference(reference);`,
|
|
818
|
+
` final record = await application.${rootCollection}.find(reference);`,
|
|
801
819
|
` if (record != null) records.add(record);`,
|
|
802
820
|
` }`,
|
|
803
821
|
` return records;`,
|
|
@@ -833,14 +851,9 @@ export function generateDartApplication(mod: DomainModule, opts: DartApplication
|
|
|
833
851
|
` object != 'workspace:self' ||`,
|
|
834
852
|
` prunedAt.isNotEmpty ||`,
|
|
835
853
|
` !seen.add(workspace)) continue;`,
|
|
836
|
-
` final
|
|
837
|
-
`
|
|
838
|
-
`
|
|
839
|
-
` _client.actor,`,
|
|
840
|
-
` );`,
|
|
841
|
-
` final record = await application.${rootCollection}.find(`,
|
|
842
|
-
` NmsInternalNomosRefs.create<${rootType}>(workspace: workspace, type: ${JSON.stringify(birth.aggregateId)}, id: ${JSON.stringify(birth.rootId)}),`,
|
|
843
|
-
` );`,
|
|
854
|
+
` final reference = NmsInternalNomosRefs.create<${rootType}>(workspace: workspace, type: ${JSON.stringify(birth.aggregateId)}, id: ${JSON.stringify(birth.rootId)});`,
|
|
855
|
+
` final application = await _nmsOpenReference(reference);`,
|
|
856
|
+
` final record = await application.${rootCollection}.find(reference);`,
|
|
844
857
|
` if (record != null) records.add(record);`,
|
|
845
858
|
` }`,
|
|
846
859
|
` return records;`,
|
|
@@ -855,10 +868,8 @@ export function generateDartApplication(mod: DomainModule, opts: DartApplication
|
|
|
855
868
|
` /// Continue working in the business application that produced [record].`,
|
|
856
869
|
` Future<${targetApplication}> applicationOf(${rootModel} record) {`,
|
|
857
870
|
` final origin = NmsInternalRecordOrigins.require(record);`,
|
|
858
|
-
` return
|
|
859
|
-
` _client.bridge,`,
|
|
871
|
+
` return _nmsOpenReference(`,
|
|
860
872
|
` NmsInternalNomosRefs.create<${rootType}>(workspace: origin.workspace, type: ${JSON.stringify(birth.aggregateId)}, id: origin.id),`,
|
|
861
|
-
` _client.actor,`,
|
|
862
873
|
` );`,
|
|
863
874
|
` }`,
|
|
864
875
|
``,
|
|
@@ -1028,6 +1039,55 @@ export function generateDartApplication(mod: DomainModule, opts: DartApplication
|
|
|
1028
1039
|
(candidate as { businessBirthInternal?: unknown }).businessBirthInternal !== true,
|
|
1029
1040
|
)) {
|
|
1030
1041
|
const sourceModel = readModelClassName(aggregate.id);
|
|
1042
|
+
for (const contractId of aggregate.implementsContracts ?? []) {
|
|
1043
|
+
const contract = contractTypeName(contractId);
|
|
1044
|
+
const method = member(`as${contract}`);
|
|
1045
|
+
addCollectionMember(aggregate.id, method, [
|
|
1046
|
+
` /// Use [record] wherever another domain declares [${contract}].`,
|
|
1047
|
+
` ///`,
|
|
1048
|
+
` /// Nomos preserves the record's workspace-qualified identity while exposing only`,
|
|
1049
|
+
` /// the contract that this aggregate explicitly implements. This does not grant access.`,
|
|
1050
|
+
` NomosRef<${contract}> ${method}(${sourceModel} record) {`,
|
|
1051
|
+
` final reference = record.ref;`,
|
|
1052
|
+
` _nmsRequireReference(reference, ${JSON.stringify(aggregate.id)}, '');`,
|
|
1053
|
+
` return NmsInternalNomosRefs.create<${contract}>(`,
|
|
1054
|
+
` workspace: reference.workspace,`,
|
|
1055
|
+
` type: ${JSON.stringify(contractId)},`,
|
|
1056
|
+
` id: reference.id,`,
|
|
1057
|
+
` );`,
|
|
1058
|
+
` }`,
|
|
1059
|
+
``,
|
|
1060
|
+
]);
|
|
1061
|
+
const resolveMethod = member(`resolve${contract}`);
|
|
1062
|
+
addCollectionMember(aggregate.id, resolveMethod, [
|
|
1063
|
+
` /// Resolve [reference] as the concrete ${sourceModel} that implements [${contract}].`,
|
|
1064
|
+
` ///`,
|
|
1065
|
+
` /// Use this after following a generated cross-application reference and opening`,
|
|
1066
|
+
` /// its business application. Nomos checks the declared contract and access on read.`,
|
|
1067
|
+
` Future<${sourceModel}?> ${resolveMethod}(ForeignRef<${contract}> reference) {`,
|
|
1068
|
+
` _nmsRequireReference(reference.ref, ${JSON.stringify(contractId)}, '');`,
|
|
1069
|
+
` return find(NmsInternalNomosRefs.create<${concreteClassName(aggregate.id)}>(`,
|
|
1070
|
+
` workspace: reference.workspace,`,
|
|
1071
|
+
` type: ${JSON.stringify(aggregate.id)},`,
|
|
1072
|
+
` id: reference.id,`,
|
|
1073
|
+
` ));`,
|
|
1074
|
+
` }`,
|
|
1075
|
+
``,
|
|
1076
|
+
]);
|
|
1077
|
+
const watchMethod = member(`watch${contract}`);
|
|
1078
|
+
addCollectionMember(aggregate.id, watchMethod, [
|
|
1079
|
+
` /// React when the concrete record behind [reference] changes.`,
|
|
1080
|
+
` Stream<${sourceModel}?> ${watchMethod}(ForeignRef<${contract}> reference) {`,
|
|
1081
|
+
` _nmsRequireReference(reference.ref, ${JSON.stringify(contractId)}, '');`,
|
|
1082
|
+
` return watchOne(NmsInternalNomosRefs.create<${concreteClassName(aggregate.id)}>(`,
|
|
1083
|
+
` workspace: reference.workspace,`,
|
|
1084
|
+
` type: ${JSON.stringify(aggregate.id)},`,
|
|
1085
|
+
` id: reference.id,`,
|
|
1086
|
+
` ));`,
|
|
1087
|
+
` }`,
|
|
1088
|
+
``,
|
|
1089
|
+
]);
|
|
1090
|
+
}
|
|
1031
1091
|
const sameDomainRefs = Object.entries(aggregate.fields).filter(([, field]) =>
|
|
1032
1092
|
field.kind === "ref" &&
|
|
1033
1093
|
field.foreignRef === undefined &&
|
|
@@ -1301,7 +1361,7 @@ export function generateDartApplication(mod: DomainModule, opts: DartApplication
|
|
|
1301
1361
|
`/// a repository or parallel application state.`,
|
|
1302
1362
|
`final class ${collection} {`,
|
|
1303
1363
|
` final ${client} _client;`,
|
|
1304
|
-
`
|
|
1364
|
+
` ${collection}._(this._client);`,
|
|
1305
1365
|
``,
|
|
1306
1366
|
...members,
|
|
1307
1367
|
`}`,
|
package/src/codegen_ts.ts
CHANGED
|
@@ -598,7 +598,7 @@ function tsTypeOfVoField(f: ValueObjectFieldDescriptor): string {
|
|
|
598
598
|
switch (f.type) {
|
|
599
599
|
case "string":
|
|
600
600
|
return f.idOf !== undefined
|
|
601
|
-
? `NomosRef<${f.idOf}>`
|
|
601
|
+
? `NomosRef<${f.idOfContract === true ? contractTypeName(f.idOf) : f.idOf}>`
|
|
602
602
|
: f.brand ?? "string"; // a brandedId(...) → the emitted branded alias
|
|
603
603
|
case "number":
|
|
604
604
|
return "number"; // `.int()`/units are Dart-side nominal products; TS keeps one number
|
package/src/fields.ts
CHANGED
|
@@ -171,7 +171,14 @@ export type ValueObjectFieldDescriptor =
|
|
|
171
171
|
/** A plain string, OR a BRANDED nominal string when `brand` is present (a `brandedId(...)`
|
|
172
172
|
* — the generated TS branded alias / Dart extension-type wrapper's name). Wire-identical
|
|
173
173
|
* to a plain string; the brand is a compile-time nominal tag only. */
|
|
174
|
-
| {
|
|
174
|
+
| {
|
|
175
|
+
readonly type: "string";
|
|
176
|
+
readonly brand?: string;
|
|
177
|
+
readonly idOf?: string;
|
|
178
|
+
/** Present only for an `idOf(recordContract(...))` target. Aggregate is the default. */
|
|
179
|
+
readonly idOfContract?: true;
|
|
180
|
+
readonly optional?: true;
|
|
181
|
+
}
|
|
175
182
|
/** `int: true` when the zod carried `.int()` (Dart `int`, TS `number`); `unit` when the field
|
|
176
183
|
* was declared through `unit(...)` — the generated Dart extension-type wrapper's name. */
|
|
177
184
|
| { readonly type: "number"; readonly int?: true; readonly unit?: string; readonly optional?: true }
|
package/src/value_object.ts
CHANGED
|
@@ -396,12 +396,17 @@ function describeZod(
|
|
|
396
396
|
switch (zodKind(schema)) {
|
|
397
397
|
case "string": {
|
|
398
398
|
const brandedBrand = (schema as unknown as Record<symbol, unknown>)[NOMOS_BRANDED_ID_BRAND];
|
|
399
|
-
const
|
|
399
|
+
const idOfTarget = idOfMetaOf(schema);
|
|
400
400
|
// idOf(Agg) REFERENCES the aggregate's canonical `NomosRef<Agg>`. Carry `idOf` in the stable
|
|
401
401
|
// descriptor so every emitter can recover the aggregate type while the wire remains a string.
|
|
402
402
|
// A standalone `brandedId("X")` remains an unrelated nominal string and declares its own wrapper.
|
|
403
|
-
if (
|
|
404
|
-
return {
|
|
403
|
+
if (idOfTarget !== undefined) {
|
|
404
|
+
return {
|
|
405
|
+
type: "string",
|
|
406
|
+
brand: `${idOfTarget}Id`,
|
|
407
|
+
idOf: idOfTarget,
|
|
408
|
+
...(idOfTargetKindOf(schema) === "contract" ? { idOfContract: true as const } : {}),
|
|
409
|
+
};
|
|
405
410
|
}
|
|
406
411
|
return typeof brandedBrand === "string" ? { type: "string", brand: brandedBrand } : { type: "string" };
|
|
407
412
|
}
|