@distrohelena/canton-typescript-sdk 0.1.27 → 0.1.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/daml-interface/analysis/analyzed-daml-type-definition.d.ts +6 -1
  2. package/dist/daml-interface/analysis/analyzed-daml-type.d.ts +7 -1
  3. package/dist/daml-interface/analysis/daml-interface-analyzer.d.ts +6 -0
  4. package/dist/daml-interface/analysis/daml-interface-analyzer.js +92 -25
  5. package/dist/daml-interface/emission/named-type-emitter.d.ts +7 -1
  6. package/dist/daml-interface/emission/named-type-emitter.js +86 -21
  7. package/dist/daml-interface/emission/project-emitter.js +1 -1
  8. package/dist/daml-interface/emission/registry-emitter.js +3 -3
  9. package/dist/daml-interface/emission/support-file-emitter.d.ts +2 -0
  10. package/dist/daml-interface/emission/support-file-emitter.js +45 -20
  11. package/dist/daml-interface/emission/template-binding-emitter.js +58 -16
  12. package/dist/daml-interface/emission/type-script-name-resolver.d.ts +8 -2
  13. package/dist/daml-interface/emission/type-script-name-resolver.js +41 -6
  14. package/dist/daml-interface/index.d.ts +3 -3
  15. package/dist/daml-interface/index.js +3 -3
  16. package/dist/daml-interface/runtime/daml-event-source-normalizer.d.ts +8 -11
  17. package/dist/daml-interface/runtime/daml-event-source-normalizer.js +14 -11
  18. package/dist/daml-interface/runtime/daml-type-descriptor.d.ts +2 -1
  19. package/dist/daml-interface/runtime/daml-value-converter.d.ts +5 -2
  20. package/dist/daml-interface/runtime/daml-value-converter.js +11 -5
  21. package/dist/daml-interface/runtime/daml-value-materializer.d.ts +4 -1
  22. package/dist/daml-interface/runtime/daml-value-materializer.js +7 -1
  23. package/dist/daml-lf/daml-lf-compilation.d.ts +3 -0
  24. package/dist/daml-lf/daml-lf-compilation.js +4 -0
  25. package/dist/daml-lf/model/daml-lf-data-type.d.ts +19 -0
  26. package/dist/daml-lf/model/daml-lf-data-type.js +15 -0
  27. package/dist/daml-lf/model/daml-lf-type.d.ts +13 -0
  28. package/dist/daml-lf/model/daml-lf-type.js +24 -0
  29. package/dist/daml-lf/model/lf-2-model-mapper.d.ts +4 -0
  30. package/dist/daml-lf/model/lf-2-model-mapper.js +77 -5
  31. package/package.json +1 -1
@@ -1,6 +1,11 @@
1
1
  import { AnalyzedDamlEnumType, AnalyzedDamlRecordType, AnalyzedDamlVariantType } from "./analyzed-daml-type.js";
2
+ import { DamlLfTypeParameter } from "../../daml-lf/model/daml-lf-data-type.js";
2
3
  import { TypeConReference } from "../../daml-lf/model/type-con-reference.js";
3
4
  /** A reachable named DAML data type and its complete serializable shape. */
4
5
  export type AnalyzedDamlTypeDefinition = {
5
6
  readonly identity: TypeConReference;
6
- } & (AnalyzedDamlRecordType | AnalyzedDamlVariantType | AnalyzedDamlEnumType);
7
+ } & ((AnalyzedDamlRecordType & {
8
+ readonly typeParameters: readonly DamlLfTypeParameter[];
9
+ }) | (AnalyzedDamlVariantType & {
10
+ readonly typeParameters: readonly DamlLfTypeParameter[];
11
+ }) | AnalyzedDamlEnumType);
@@ -46,9 +46,15 @@ export type AnalyzedDamlEnumType = {
46
46
  readonly kind: "enum";
47
47
  readonly constructors: readonly string[];
48
48
  };
49
+ export type AnalyzedDamlTypeVariableType = {
50
+ readonly kind: "typeVariable";
51
+ readonly name?: string;
52
+ readonly internedStringIndex: number;
53
+ };
49
54
  export type AnalyzedDamlNamedReferenceType = {
50
55
  readonly kind: "namedReference";
51
56
  readonly identity: TypeConReference;
57
+ readonly typeArguments: readonly AnalyzedDamlType[];
52
58
  };
53
59
  /** Closed, immutable description of a DAML value serializable by the generator. */
54
- export type AnalyzedDamlType = AnalyzedDamlPrimitiveType | AnalyzedDamlContractIdType | AnalyzedDamlOptionalType | AnalyzedDamlListType | AnalyzedDamlTextMapType | AnalyzedDamlGenMapType | AnalyzedDamlRecordType | AnalyzedDamlVariantType | AnalyzedDamlEnumType | AnalyzedDamlNamedReferenceType;
60
+ export type AnalyzedDamlType = AnalyzedDamlPrimitiveType | AnalyzedDamlContractIdType | AnalyzedDamlOptionalType | AnalyzedDamlListType | AnalyzedDamlTextMapType | AnalyzedDamlGenMapType | AnalyzedDamlRecordType | AnalyzedDamlVariantType | AnalyzedDamlEnumType | AnalyzedDamlTypeVariableType | AnalyzedDamlNamedReferenceType;
@@ -1,12 +1,18 @@
1
1
  import { DamlLfCompilation } from "../../daml-lf/daml-lf-compilation.js";
2
2
  import { AnalyzedDamlTypeDefinition } from "./analyzed-daml-type-definition.js";
3
3
  import { AnalyzedTemplate } from "./analyzed-template.js";
4
+ export type DamlInterfacePackageMetadata = {
5
+ readonly packageName: string;
6
+ readonly packageVersion: string;
7
+ };
4
8
  export declare class DamlInterfaceAnalysisResult {
5
9
  readonly templates: readonly AnalyzedTemplate[];
6
10
  readonly typeDefinitions: readonly AnalyzedDamlTypeDefinition[];
11
+ readonly packageMetadata: ReadonlyMap<string, DamlInterfacePackageMetadata>;
7
12
  constructor(init: {
8
13
  templates: readonly AnalyzedTemplate[];
9
14
  typeDefinitions: readonly AnalyzedDamlTypeDefinition[];
15
+ packageMetadata?: ReadonlyMap<string, DamlInterfacePackageMetadata>;
10
16
  });
11
17
  }
12
18
  export declare class DamlInterfaceAnalyzer {
@@ -6,9 +6,11 @@ import { AnalyzedTemplate, AnalyzedTemplateField } from "./analyzed-template.js"
6
6
  export class DamlInterfaceAnalysisResult {
7
7
  templates;
8
8
  typeDefinitions;
9
+ packageMetadata;
9
10
  constructor(init) {
10
11
  this.templates = Object.freeze([...init.templates]);
11
12
  this.typeDefinitions = Object.freeze([...init.typeDefinitions]);
13
+ this.packageMetadata = new Map(init.packageMetadata);
12
14
  }
13
15
  }
14
16
  export class DamlInterfaceAnalyzer {
@@ -22,6 +24,13 @@ export class DamlInterfaceAnalyzer {
22
24
  return new DamlInterfaceAnalysisResult({
23
25
  templates,
24
26
  typeDefinitions: typeBuilder.getTypeDefinitions(),
27
+ packageMetadata: new Map(compilation.getPackages().map((pkg) => [
28
+ pkg.packageId,
29
+ {
30
+ packageName: pkg.packageName,
31
+ packageVersion: pkg.packageVersion,
32
+ },
33
+ ])),
25
34
  });
26
35
  }
27
36
  analyzeTemplateOrThrow(template, typeBuilder) {
@@ -88,15 +97,32 @@ class AnalyzedDamlTypeBuilder {
88
97
  this.semanticModel = semanticModel;
89
98
  this.toCamelCase = toCamelCase;
90
99
  }
91
- buildOrThrow(type, context) {
92
- if (type.typeConReference !== undefined) {
93
- if (type.builtinType !== DamlLfBuiltinType.unknown) {
94
- throw this.unsupported(context, "a type constructor cannot also be a builtin type");
100
+ buildOrThrow(type, context, typeParameters = new Map()) {
101
+ if (type.diagnosticForall !== undefined) {
102
+ throw this.unsupported(context, "retained forall types are not supported");
103
+ }
104
+ else if (type.typeVariable !== undefined) {
105
+ if (type.typeVariable.name === undefined) {
106
+ throw this.unsupported(context, `type variable '${this.getTypeVariableName(type.typeVariable)}' requires a resolved name`);
95
107
  }
96
108
  else if (type.typeArguments.length !== 0) {
97
- throw this.unsupported(context, "generic named type applications are not supported");
109
+ throw this.unsupported(context, "applied type variables are not supported");
110
+ }
111
+ const parameter = typeParameters.get(type.typeVariable.internedStringIndex);
112
+ if (parameter === undefined) {
113
+ throw this.unsupported(context, `unbound type variable '${this.getTypeVariableName(type.typeVariable)}'`);
114
+ }
115
+ return Object.freeze({
116
+ kind: "typeVariable",
117
+ ...(parameter.name === undefined ? {} : { name: parameter.name }),
118
+ internedStringIndex: parameter.internedStringIndex,
119
+ });
120
+ }
121
+ else if (type.typeConReference !== undefined) {
122
+ if (type.builtinType !== DamlLfBuiltinType.unknown) {
123
+ throw this.unsupported(context, "a type constructor cannot also be a builtin type");
98
124
  }
99
- return this.buildNamedReferenceOrThrow(type.typeConReference, context);
125
+ return this.buildNamedReferenceOrThrow(type.typeConReference, type.typeArguments, context, typeParameters);
100
126
  }
101
127
  switch (type.builtinType) {
102
128
  case DamlLfBuiltinType.unit:
@@ -135,24 +161,24 @@ class AnalyzedDamlTypeBuilder {
135
161
  case DamlLfBuiltinType.optional:
136
162
  return Object.freeze({
137
163
  kind: "optional",
138
- element: this.buildUnaryArgumentOrThrow(type, context),
164
+ element: this.buildUnaryArgumentOrThrow(type, context, typeParameters),
139
165
  });
140
166
  case DamlLfBuiltinType.list:
141
167
  return Object.freeze({
142
168
  kind: "list",
143
- element: this.buildUnaryArgumentOrThrow(type, context),
169
+ element: this.buildUnaryArgumentOrThrow(type, context, typeParameters),
144
170
  });
145
171
  case DamlLfBuiltinType.textMap:
146
172
  return Object.freeze({
147
173
  kind: "textMap",
148
- value: this.buildUnaryArgumentOrThrow(type, context),
174
+ value: this.buildUnaryArgumentOrThrow(type, context, typeParameters),
149
175
  });
150
176
  case DamlLfBuiltinType.genMap:
151
177
  this.assertArgumentCountOrThrow(type, 2, context);
152
178
  return Object.freeze({
153
179
  kind: "genMap",
154
- key: this.buildOrThrow(type.typeArguments[0], context),
155
- value: this.buildOrThrow(type.typeArguments[1], context),
180
+ key: this.buildOrThrow(type.typeArguments[0], context, typeParameters),
181
+ value: this.buildOrThrow(type.typeArguments[1], context, typeParameters),
156
182
  });
157
183
  case DamlLfBuiltinType.unknown:
158
184
  throw this.unsupported(context, "the type is not serializable");
@@ -167,49 +193,50 @@ class AnalyzedDamlTypeBuilder {
167
193
  return definition;
168
194
  }));
169
195
  }
170
- buildUnaryArgumentOrThrow(type, context) {
196
+ buildUnaryArgumentOrThrow(type, context, typeParameters) {
171
197
  this.assertArgumentCountOrThrow(type, 1, context);
172
- return this.buildOrThrow(type.typeArguments[0], context);
198
+ return this.buildOrThrow(type.typeArguments[0], context, typeParameters);
173
199
  }
174
- buildNamedReferenceOrThrow(reference, context) {
200
+ buildNamedReferenceOrThrow(reference, typeArguments, context, typeParameters) {
175
201
  const key = this.getDefinitionKey(reference);
176
202
  const identity = this.getCanonicalIdentity(reference, key);
203
+ const dataType = this.getDataTypeOrThrow(reference, context);
204
+ this.assertSupportedDataTypeParametersOrThrow(dataType, context);
205
+ this.assertNamedArgumentCountOrThrow(typeArguments, dataType, context);
206
+ const analyzedTypeArguments = Object.freeze(typeArguments.map((argument) => this.buildOrThrow(argument, context, typeParameters)));
177
207
  if (!this.definitions.has(key)) {
178
208
  this.definitionKeys.push(key);
179
209
  this.definitions.set(key, undefined);
180
- this.definitions.set(key, this.buildNamedDefinitionOrThrow(reference, identity, context));
210
+ this.definitions.set(key, this.buildNamedDefinitionOrThrow(reference, identity, dataType));
181
211
  }
182
212
  return Object.freeze({
183
213
  kind: "namedReference",
184
214
  identity,
215
+ typeArguments: analyzedTypeArguments,
185
216
  });
186
217
  }
187
- buildNamedDefinitionOrThrow(reference, identity, context) {
188
- let dataType;
189
- try {
190
- dataType = this.semanticModel.getDataTypeOrThrow(reference);
191
- }
192
- catch {
193
- throw this.unsupported(context, `could not resolve named type '${reference.packageId}:${reference.moduleName}:${reference.name}'`);
194
- }
218
+ buildNamedDefinitionOrThrow(reference, identity, dataType) {
219
+ const typeParameters = this.createTypeParameterScope(dataType.typeParameters);
195
220
  if (dataType.definition.kind === "record") {
196
221
  return Object.freeze({
197
222
  identity,
223
+ typeParameters: Object.freeze([...dataType.typeParameters]),
198
224
  kind: "record",
199
225
  fields: Object.freeze(dataType.definition.fields.map((field) => Object.freeze({
200
226
  damlLabel: field.name,
201
227
  propertyName: this.toCamelCase(field.name),
202
- type: this.buildOrThrow(field.type, `field '${field.name}' of record '${reference.name}'`),
228
+ type: this.buildOrThrow(field.type, `field '${field.name}' of record '${reference.name}'`, typeParameters),
203
229
  }))),
204
230
  });
205
231
  }
206
232
  else if (dataType.definition.kind === "variant") {
207
233
  return Object.freeze({
208
234
  identity,
235
+ typeParameters: Object.freeze([...dataType.typeParameters]),
209
236
  kind: "variant",
210
237
  constructors: Object.freeze(dataType.definition.constructors.map((constructor) => Object.freeze({
211
238
  constructor: constructor.name,
212
- payload: this.buildOrThrow(constructor.type, `constructor '${constructor.name}' of variant '${reference.name}'`),
239
+ payload: this.buildOrThrow(constructor.type, `constructor '${constructor.name}' of variant '${reference.name}'`, typeParameters),
213
240
  }))),
214
241
  });
215
242
  }
@@ -224,6 +251,46 @@ class AnalyzedDamlTypeBuilder {
224
251
  throw this.unsupported(context, `builtin '${type.builtinType}' requires ${expectedCount} type argument${expectedCount === 1 ? "" : "s"}`);
225
252
  }
226
253
  }
254
+ getDataTypeOrThrow(reference, context) {
255
+ try {
256
+ return this.semanticModel.getDataTypeOrThrow(reference);
257
+ }
258
+ catch {
259
+ throw this.unsupported(context, `could not resolve named type '${reference.packageId}:${reference.moduleName}:${reference.name}'`);
260
+ }
261
+ }
262
+ assertSupportedDataTypeParametersOrThrow(dataType, context) {
263
+ if (dataType.definition.kind === "enum" &&
264
+ dataType.typeParameters.length !== 0) {
265
+ throw this.unsupported(context, "generic enums are not supported");
266
+ }
267
+ for (const parameter of dataType.typeParameters) {
268
+ if (parameter.name === undefined) {
269
+ throw this.unsupported(context, `type parameter '${this.getTypeParameterName(parameter)}' requires a resolved name`);
270
+ }
271
+ else if (parameter.kind.kind !== "star") {
272
+ throw this.unsupported(context, `type parameter '${this.getTypeParameterName(parameter)}' must have kind '*'`);
273
+ }
274
+ }
275
+ }
276
+ assertNamedArgumentCountOrThrow(typeArguments, dataType, context) {
277
+ const expectedCount = dataType.typeParameters.length;
278
+ if (typeArguments.length !== expectedCount) {
279
+ throw this.unsupported(context, `named type '${dataType.name}' requires ${expectedCount} type argument${expectedCount === 1 ? "" : "s"}`);
280
+ }
281
+ }
282
+ createTypeParameterScope(parameters) {
283
+ return new Map(parameters.map((parameter) => [
284
+ parameter.internedStringIndex,
285
+ parameter,
286
+ ]));
287
+ }
288
+ getTypeVariableName(typeVariable) {
289
+ return typeVariable.name ?? `#${typeVariable.internedStringIndex}`;
290
+ }
291
+ getTypeParameterName(parameter) {
292
+ return parameter.name ?? `#${parameter.internedStringIndex}`;
293
+ }
227
294
  getDefinitionKey(reference) {
228
295
  return `${reference.packageId}::${reference.moduleName}::${reference.name}`;
229
296
  }
@@ -1,4 +1,5 @@
1
1
  import { AnalyzedDamlTypeDefinition } from "../analysis/analyzed-daml-type-definition.js";
2
+ import { DamlInterfacePackageMetadata } from "../analysis/daml-interface-analyzer.js";
2
3
  import { GeneratedNamedTypeFile } from "../emission-model/generated-named-type-file.js";
3
4
  import { GeneratedTemplateBindingFile } from "../emission-model/generated-template-binding-file.js";
4
5
  import { TypeScriptNameResolver } from "./type-script-name-resolver.js";
@@ -7,7 +8,7 @@ export declare class NamedTypeEmitter {
7
8
  private readonly nameResolver;
8
9
  constructor(nameResolver?: TypeScriptNameResolver);
9
10
  /** Prepares shared package/module output mappings with template emission. */
10
- prepareProjectOrThrow(templates: readonly import("../analysis/analyzed-template.js").AnalyzedTemplate[], definitions: readonly AnalyzedDamlTypeDefinition[]): void;
11
+ prepareProjectOrThrow(templates: readonly import("../analysis/analyzed-template.js").AnalyzedTemplate[], definitions: readonly AnalyzedDamlTypeDefinition[], packageMetadata?: ReadonlyMap<string, DamlInterfacePackageMetadata>): void;
11
12
  /** Emits one `types.ts` module for every reachable DAML package/module identity. */
12
13
  emitNamedTypeFiles(definitions: readonly AnalyzedDamlTypeDefinition[]): readonly GeneratedNamedTypeFile[];
13
14
  /** Emits named type files from an already prepared shared name resolver. */
@@ -19,6 +20,11 @@ export declare class NamedTypeEmitter {
19
20
  private getNamedReferences;
20
21
  private getTypeName;
21
22
  private getPrimitiveTypeName;
23
+ private resolveTypeParameterNames;
24
+ private getDefinitionTypeParameters;
25
+ private getTypeParameterName;
26
+ private getTypeParameters;
27
+ private getTypeParameterKey;
22
28
  private resolveModulesOrThrow;
23
29
  private resolveDefinitionNamesOrThrow;
24
30
  private resolveCollisionSafeNames;
@@ -9,8 +9,8 @@ export class NamedTypeEmitter {
9
9
  void this.nameResolver;
10
10
  }
11
11
  /** Prepares shared package/module output mappings with template emission. */
12
- prepareProjectOrThrow(templates, definitions) {
13
- this.nameResolver.prepareProjectOrThrow(templates, definitions);
12
+ prepareProjectOrThrow(templates, definitions, packageMetadata = new Map()) {
13
+ this.nameResolver.prepareProjectOrThrow(templates, definitions, packageMetadata);
14
14
  }
15
15
  /** Emits one `types.ts` module for every reachable DAML package/module identity. */
16
16
  emitNamedTypeFiles(definitions) {
@@ -25,14 +25,15 @@ export class NamedTypeEmitter {
25
25
  return [...modules.values()].map((module) => {
26
26
  const moduleDefinitions = definitions.filter((definition) => definition.identity.packageId === module.packageId
27
27
  && definition.identity.moduleName === module.moduleName);
28
- const externalTypeAliases = this.resolveExternalTypeAliases(module, moduleDefinitions, names);
28
+ const externalTypeAliases = this.resolveExternalTypeAliases(module, moduleDefinitions, modules, names);
29
+ const typeParameterNames = this.resolveTypeParameterNames(moduleDefinitions, names, externalTypeAliases);
29
30
  const imports = this.emitImports(module, moduleDefinitions, modules, names, externalTypeAliases);
30
31
  return new GeneratedNamedTypeFile({
31
32
  path: module.path,
32
33
  contents: [
33
34
  ...imports,
34
35
  ...(imports.length === 0 ? [] : [""]),
35
- ...moduleDefinitions.map((definition) => this.emitDefinition(definition, names, externalTypeAliases, fieldPropertyNames)),
36
+ ...moduleDefinitions.map((definition) => this.emitDefinition(definition, names, externalTypeAliases, fieldPropertyNames, typeParameterNames)),
36
37
  "",
37
38
  ].join("\n"),
38
39
  packageId: module.packageId,
@@ -47,19 +48,21 @@ export class NamedTypeEmitter {
47
48
  });
48
49
  });
49
50
  }
50
- emitDefinition(definition, names, externalTypeAliases, fieldPropertyNames) {
51
+ emitDefinition(definition, names, externalTypeAliases, fieldPropertyNames, typeParameterNames) {
51
52
  const name = this.getDefinitionName(definition, names);
53
+ const parameters = this.getDefinitionTypeParameters(definition, typeParameterNames);
54
+ const declarationName = `${name}${parameters.length === 0 ? "" : `<${parameters.join(", ")}>`}`;
52
55
  if (definition.kind === "record") {
53
56
  return [
54
- `export interface ${name} {`,
55
- ...definition.fields.map((field, index) => ` readonly ${this.getFieldPropertyName(definition, index, fieldPropertyNames)}: ${this.getTypeName(field.type, names, externalTypeAliases)};`),
57
+ `export interface ${declarationName} {`,
58
+ ...definition.fields.map((field, index) => ` readonly ${this.getFieldPropertyName(definition, index, fieldPropertyNames)}: ${this.getTypeName(field.type, definition, names, externalTypeAliases, typeParameterNames)};`),
56
59
  "}",
57
60
  ].join("\n");
58
61
  }
59
62
  else if (definition.kind === "variant") {
60
63
  return [
61
- `export type ${name} =`,
62
- ...definition.constructors.map((constructor) => ` | { readonly tag: ${JSON.stringify(constructor.constructor)}; readonly value: ${this.getTypeName(constructor.payload, names, externalTypeAliases)}; }`),
64
+ `export type ${declarationName} =`,
65
+ ...definition.constructors.map((constructor) => ` | { readonly tag: ${JSON.stringify(constructor.constructor)}; readonly value: ${this.getTypeName(constructor.payload, definition, names, externalTypeAliases, typeParameterNames)}; }`),
63
66
  ].map((line, index, lines) => index === lines.length - 1 ? `${line};` : line).join("\n");
64
67
  }
65
68
  return `export type ${name} = ${definition.constructors.map((constructor) => JSON.stringify(constructor)).join(" | ")};`;
@@ -94,11 +97,13 @@ export class NamedTypeEmitter {
94
97
  .sort(([left], [right]) => left.localeCompare(right))
95
98
  .map(([path, importedNames]) => `import type { ${[...importedNames.entries()]
96
99
  .sort(([left], [right]) => left.localeCompare(right))
97
- .map(([exportedName, alias]) => `${exportedName} as ${alias}`)
100
+ .map(([exportedName, alias]) => exportedName === alias
101
+ ? exportedName
102
+ : `${exportedName} as ${alias}`)
98
103
  .join(", ")} } from ${JSON.stringify(path)};`),
99
104
  ];
100
105
  }
101
- resolveExternalTypeAliases(module, definitions, names) {
106
+ resolveExternalTypeAliases(module, definitions, modules, names) {
102
107
  const references = new Map();
103
108
  for (const definition of definitions) {
104
109
  for (const reference of this.getNamedReferences(definition)) {
@@ -112,11 +117,16 @@ export class NamedTypeEmitter {
112
117
  const aliases = new Map();
113
118
  const usedNames = new Set(definitions.map((definition) => this.getDefinitionName(definition, names)));
114
119
  for (const [key, identity] of [...references.entries()].sort(([left], [right]) => left.localeCompare(right))) {
115
- const baseName = this.safeTypeName(`${identity.packageId} ${identity.moduleName} ${identity.name}`);
116
- let alias = baseName;
120
+ const referencedModule = modules.get(this.getModuleKey(identity.packageId, identity.moduleName));
121
+ if (referencedModule === undefined) {
122
+ throw new Error(`Cannot emit unresolved named DAML type '${identity.name}'`);
123
+ }
124
+ const exportedName = this.getNamedReferenceTypeName(identity, names);
125
+ const baseName = this.safeTypeName(`${referencedModule.namespaceAlias} ${exportedName}`);
126
+ let alias = exportedName;
117
127
  let escalation = 2;
118
128
  if (usedNames.has(alias)) {
119
- alias = `${baseName}_${this.shortHash(key)}`;
129
+ alias = baseName;
120
130
  }
121
131
  while (usedNames.has(alias)) {
122
132
  alias = `${baseName}_${this.shortHash(key)}_${escalation}`;
@@ -138,6 +148,9 @@ export class NamedTypeEmitter {
138
148
  switch (type.kind) {
139
149
  case "namedReference":
140
150
  yield type;
151
+ for (const typeArgument of type.typeArguments ?? []) {
152
+ yield* this.getNamedReferences(typeArgument);
153
+ }
141
154
  return;
142
155
  case "contractId": return;
143
156
  case "optional":
@@ -163,29 +176,36 @@ export class NamedTypeEmitter {
163
176
  return;
164
177
  case "primitive":
165
178
  case "enum":
179
+ case "typeVariable":
166
180
  return;
167
181
  }
168
182
  }
169
- getTypeName(type, names, externalTypeAliases) {
183
+ getTypeName(type, definition, names, externalTypeAliases, typeParameterNames) {
170
184
  switch (type.kind) {
171
185
  case "primitive":
172
186
  return this.getPrimitiveTypeName(type.builtinType);
173
187
  case "contractId":
174
188
  return "string";
175
189
  case "optional":
176
- return `${this.getTypeName(type.element, names, externalTypeAliases)} | undefined`;
190
+ return `${this.getTypeName(type.element, definition, names, externalTypeAliases, typeParameterNames)} | undefined`;
177
191
  case "list":
178
- return `readonly ${this.getTypeName(type.element, names, externalTypeAliases)}[]`;
192
+ return `readonly ${this.getTypeName(type.element, definition, names, externalTypeAliases, typeParameterNames)}[]`;
179
193
  case "textMap":
180
- return `ReadonlyMap<string, ${this.getTypeName(type.value, names, externalTypeAliases)}>`;
194
+ return `ReadonlyMap<string, ${this.getTypeName(type.value, definition, names, externalTypeAliases, typeParameterNames)}>`;
181
195
  case "genMap":
182
- return `ReadonlyMap<${this.getTypeName(type.key, names, externalTypeAliases)}, ${this.getTypeName(type.value, names, externalTypeAliases)}>`;
196
+ return `ReadonlyMap<${this.getTypeName(type.key, definition, names, externalTypeAliases, typeParameterNames)}, ${this.getTypeName(type.value, definition, names, externalTypeAliases, typeParameterNames)}>`;
183
197
  case "record":
184
198
  case "variant":
185
199
  case "enum":
186
200
  throw new Error("Named DAML declarations must not contain anonymous record, variant, or enum shapes");
187
- case "namedReference":
188
- return externalTypeAliases.get(this.getDefinitionKey(type.identity.packageId, type.identity.moduleName, type.identity.name)) ?? this.getNamedReferenceTypeName(type.identity, names);
201
+ case "typeVariable":
202
+ return this.getTypeParameterName(definition, type, typeParameterNames);
203
+ case "namedReference": {
204
+ const name = externalTypeAliases.get(this.getDefinitionKey(type.identity.packageId, type.identity.moduleName, type.identity.name)) ?? this.getNamedReferenceTypeName(type.identity, names);
205
+ return (type.typeArguments?.length ?? 0) === 0
206
+ ? name
207
+ : `${name}<${(type.typeArguments ?? []).map((argument) => this.getTypeName(argument, definition, names, externalTypeAliases, typeParameterNames)).join(", ")}>`;
208
+ }
189
209
  }
190
210
  }
191
211
  getPrimitiveTypeName(type) {
@@ -210,6 +230,46 @@ export class NamedTypeEmitter {
210
230
  throw new Error(`Cannot emit unsupported primitive DAML type '${type}'`);
211
231
  }
212
232
  }
233
+ resolveTypeParameterNames(definitions, names, externalTypeAliases) {
234
+ const values = definitions.flatMap((definition) => this.getTypeParameters(definition).map((parameter) => ({
235
+ definition,
236
+ parameter,
237
+ })));
238
+ const moduleBindings = new Set([
239
+ ...definitions.map((definition) => this.getDefinitionName(definition, names)),
240
+ ...externalTypeAliases.values(),
241
+ ]);
242
+ for (const definition of definitions) {
243
+ for (const runtimeType of this.getRuntimePrimitiveTypes(definition)) {
244
+ moduleBindings.add(runtimeType);
245
+ }
246
+ }
247
+ const reservedNamesByDefinition = new Map(definitions.map((definition) => [
248
+ this.getDefinitionKey(definition.identity.packageId, definition.identity.moduleName, definition.identity.name),
249
+ moduleBindings,
250
+ ]));
251
+ const resolved = this.resolveCollisionSafeNames(values, ({ parameter }) => this.safeTypeName(parameter.name ?? `T${parameter.internedStringIndex}`), ({ definition }) => this.getDefinitionKey(definition.identity.packageId, definition.identity.moduleName, definition.identity.name), "_", reservedNamesByDefinition);
252
+ return new Map(values.map((value) => [
253
+ this.getTypeParameterKey(value.definition, value.parameter.internedStringIndex),
254
+ resolved.get(value),
255
+ ]));
256
+ }
257
+ getDefinitionTypeParameters(definition, typeParameterNames) {
258
+ return this.getTypeParameters(definition).map((parameter) => this.getTypeParameterName(definition, parameter, typeParameterNames));
259
+ }
260
+ getTypeParameterName(definition, parameter, typeParameterNames) {
261
+ const name = typeParameterNames.get(this.getTypeParameterKey(definition, parameter.internedStringIndex));
262
+ if (name === undefined) {
263
+ throw new Error(`Cannot resolve generic parameter '${parameter.internedStringIndex}' for '${definition.identity.name}'`);
264
+ }
265
+ return name;
266
+ }
267
+ getTypeParameters(definition) {
268
+ return definition.kind === "enum" ? [] : definition.typeParameters ?? [];
269
+ }
270
+ getTypeParameterKey(definition, internedStringIndex) {
271
+ return `${this.getDefinitionKey(definition.identity.packageId, definition.identity.moduleName, definition.identity.name)}\u0000type-parameter\u0000${internedStringIndex}`;
272
+ }
213
273
  resolveModulesOrThrow(definitions) {
214
274
  const identities = new Map();
215
275
  for (const definition of definitions) {
@@ -426,7 +486,12 @@ export class NamedTypeEmitter {
426
486
  }
427
487
  return;
428
488
  case "enum":
489
+ case "typeVariable":
490
+ return;
429
491
  case "namedReference":
492
+ for (const typeArgument of type.typeArguments ?? []) {
493
+ yield* this.getRuntimePrimitiveTypes(typeArgument);
494
+ }
430
495
  return;
431
496
  }
432
497
  }
@@ -24,7 +24,7 @@ export class ProjectEmitter {
24
24
  }
25
25
  /** Emits the complete in-memory DAML interface project from analyzed templates. */
26
26
  emitProject(analysis) {
27
- this.namedTypeEmitter.prepareProjectOrThrow(analysis.templates, analysis.typeDefinitions);
27
+ this.namedTypeEmitter.prepareProjectOrThrow(analysis.templates, analysis.typeDefinitions, analysis.packageMetadata);
28
28
  const templateBindingFiles = analysis.templates.map((template) => this.templateBindingEmitter.emitTemplateBindingFile(template));
29
29
  const namedTypeFiles = this.namedTypeEmitter.emitPreparedNamedTypeFiles(analysis.typeDefinitions, templateBindingFiles);
30
30
  const templateFiles = analysis.templates.map((template) => this.templateBindingEmitter.emitTemplateFile(template, namedTypeFiles));
@@ -13,13 +13,13 @@ export class RegistryEmitter {
13
13
  return new GeneratedRegistryFile({
14
14
  path: "generated/registry.ts",
15
15
  contents: [
16
- 'import { DamlMaterializationError, normalizeDamlCreatedEventSource, normalizeDamlExercisedEventSource } from "@distrohelena/canton-typescript-sdk/daml-interface";',
16
+ 'import { DamlEventSourceNormalizer, DamlMaterializationError } from "@distrohelena/canton-typescript-sdk/daml-interface";',
17
17
  'import type { DamlCreatedEventSource, DamlExercisedEventSource } from "@distrohelena/canton-typescript-sdk/daml-interface";',
18
18
  ...importLines,
19
19
  "",
20
20
  "export class GeneratedRegistry {",
21
21
  " public static fromCreatedEvent(event: DamlCreatedEventSource): unknown {",
22
- " const normalized = normalizeDamlCreatedEventSource(event);",
22
+ " const normalized = DamlEventSourceNormalizer.normalizeCreated(event);",
23
23
  " switch (`${normalized.metadata.templateId.packageId}:${normalized.metadata.templateId.moduleName}:${normalized.metadata.templateId.entityName}`) {",
24
24
  ...createdCases,
25
25
  " default:",
@@ -28,7 +28,7 @@ export class RegistryEmitter {
28
28
  " }",
29
29
  "",
30
30
  " public static fromExercisedEvent(event: DamlExercisedEventSource): unknown {",
31
- " const normalized = normalizeDamlExercisedEventSource(event);",
31
+ " const normalized = DamlEventSourceNormalizer.normalizeExercised(event);",
32
32
  " switch (`${normalized.metadata.templateId.packageId}:${normalized.metadata.templateId.moduleName}:${normalized.metadata.templateId.entityName}`) {",
33
33
  ...exercisedCases,
34
34
  " default:",
@@ -15,8 +15,10 @@ export declare class SupportFileEmitter {
15
15
  private getExportedSymbols;
16
16
  private describeTemplate;
17
17
  private emitDescriptorRegistry;
18
+ private emitFactoryBody;
18
19
  private emitDescriptor;
19
20
  private emitDefinitionDescriptor;
21
+ private getTypeVariableSubstitution;
20
22
  private getFieldPropertyName;
21
23
  private getFieldKey;
22
24
  private getIdentityKey;
@@ -147,9 +147,7 @@ export class SupportFileEmitter {
147
147
  throw new Error(`Cannot emit duplicate named DAML type descriptor '${identityKey}'`);
148
148
  }
149
149
  identities.add(identityKey);
150
- return [
151
- ` ${JSON.stringify(identityKey)}: () => deepFreeze(${this.emitDefinitionDescriptor(definition, fieldPropertyNames)} satisfies DamlTypeDescriptor),`,
152
- ].join("");
150
+ return ` ${JSON.stringify(identityKey)}: (typeArguments) => {\n${this.emitFactoryBody(definition, fieldPropertyNames)}\n },`;
153
151
  });
154
152
  return new GeneratedSupportFile({
155
153
  path: "generated/support/descriptors.ts",
@@ -168,48 +166,75 @@ export class SupportFileEmitter {
168
166
  " return value;",
169
167
  "}",
170
168
  "",
171
- "const generatedDamlTypeDescriptorFactories: Readonly<Record<string, () => DamlTypeDescriptor>> = Object.freeze({",
169
+ "const generatedDamlTypeDescriptorFactories: Readonly<Record<string, (typeArguments: readonly DamlTypeDescriptor[]) => DamlTypeDescriptor>> = Object.freeze({",
172
170
  ...factories,
173
171
  "});",
174
172
  "",
175
- "export const generatedDamlTypeDescriptorRegistry: DamlTypeDescriptorRegistry = Object.freeze({",
176
- " resolve(identity) {",
177
- " return generatedDamlTypeDescriptorFactories[`${identity.packageId}:${identity.moduleName}:${identity.entityName}`];",
178
- " },",
179
- "});",
173
+ "export class GeneratedDamlTypeDescriptorRegistry {",
174
+ " private constructor() {}",
175
+ "",
176
+ " public static resolve(identity: Parameters<DamlTypeDescriptorRegistry[\"resolve\"]>[0], typeArguments: Parameters<DamlTypeDescriptorRegistry[\"resolve\"]>[1]): ReturnType<DamlTypeDescriptorRegistry[\"resolve\"]> {",
177
+ " const factory = generatedDamlTypeDescriptorFactories[`${identity.packageId}:${identity.moduleName}:${identity.entityName}`];",
178
+ "",
179
+ " return factory === undefined ? undefined : factory(typeArguments);",
180
+ " }",
181
+ "}",
180
182
  "",
181
183
  ].join("\n"),
182
184
  });
183
185
  }
184
- emitDescriptor(type) {
186
+ emitFactoryBody(definition, fieldPropertyNames) {
187
+ const typeParameters = definition.kind === "enum" ? [] : definition.typeParameters ?? [];
188
+ const substitutions = new Map(typeParameters.map((parameter, index) => [
189
+ parameter.internedStringIndex,
190
+ `typeArguments[${index}]!`,
191
+ ]));
192
+ return [
193
+ ` if (typeArguments.length !== ${typeParameters.length}) {`,
194
+ ` throw new Error(${JSON.stringify(`Expected ${typeParameters.length} type arguments for ${definition.identity.packageId}:${definition.identity.moduleName}:${definition.identity.name}`)});`,
195
+ " }",
196
+ "",
197
+ ` return deepFreeze(${this.emitDefinitionDescriptor(definition, fieldPropertyNames, substitutions)} satisfies DamlTypeDescriptor);`,
198
+ ].join("\n");
199
+ }
200
+ emitDescriptor(type, substitutions = new Map()) {
185
201
  switch (type.kind) {
186
202
  case "primitive":
187
203
  return `{ kind: "primitive", primitive: ${JSON.stringify(type.builtinType)}${type.numericScale === undefined ? "" : `, numericScale: ${type.numericScale}`} }`;
188
204
  case "contractId":
189
205
  return '{ kind: "contractId" }';
190
206
  case "optional":
191
- return `{ kind: "optional", element: ${this.emitDescriptor(type.element)} }`;
207
+ return `{ kind: "optional", element: ${this.emitDescriptor(type.element, substitutions)} }`;
192
208
  case "list":
193
- return `{ kind: "list", element: ${this.emitDescriptor(type.element)} }`;
209
+ return `{ kind: "list", element: ${this.emitDescriptor(type.element, substitutions)} }`;
194
210
  case "textMap":
195
- return `{ kind: "textMap", value: ${this.emitDescriptor(type.value)} }`;
211
+ return `{ kind: "textMap", value: ${this.emitDescriptor(type.value, substitutions)} }`;
196
212
  case "genMap":
197
- return `{ kind: "genMap", key: ${this.emitDescriptor(type.key)}, value: ${this.emitDescriptor(type.value)} }`;
213
+ return `{ kind: "genMap", key: ${this.emitDescriptor(type.key, substitutions)}, value: ${this.emitDescriptor(type.value, substitutions)} }`;
198
214
  case "record":
199
- return `{ kind: "record", fields: [${type.fields.map((field) => `{ damlLabel: ${JSON.stringify(field.damlLabel)}, propertyName: ${JSON.stringify(field.propertyName)}, type: ${this.emitDescriptor(field.type)} }`).join(", ")}] }`;
215
+ return `{ kind: "record", fields: [${type.fields.map((field) => `{ damlLabel: ${JSON.stringify(field.damlLabel)}, propertyName: ${JSON.stringify(field.propertyName)}, type: ${this.emitDescriptor(field.type, substitutions)} }`).join(", ")}] }`;
200
216
  case "variant":
201
- return `{ kind: "variant", constructors: [${type.constructors.map((constructor) => `{ constructor: ${JSON.stringify(constructor.constructor)}, payload: ${this.emitDescriptor(constructor.payload)} }`).join(", ")}] }`;
217
+ return `{ kind: "variant", constructors: [${type.constructors.map((constructor) => `{ constructor: ${JSON.stringify(constructor.constructor)}, payload: ${this.emitDescriptor(constructor.payload, substitutions)} }`).join(", ")}] }`;
202
218
  case "enum":
203
219
  return `{ kind: "enum", constructors: [${type.constructors.map((constructor) => JSON.stringify(constructor)).join(", ")}] }`;
220
+ case "typeVariable":
221
+ return this.getTypeVariableSubstitution(type, substitutions);
204
222
  case "namedReference":
205
- return `{ kind: "namedReference", identity: { packageId: ${JSON.stringify(type.identity.packageId)}, moduleName: ${JSON.stringify(type.identity.moduleName)}, entityName: ${JSON.stringify(type.identity.name)} } }`;
223
+ return `{ kind: "namedReference", identity: { packageId: ${JSON.stringify(type.identity.packageId)}, moduleName: ${JSON.stringify(type.identity.moduleName)}, entityName: ${JSON.stringify(type.identity.name)} }, typeArguments: [${(type.typeArguments ?? []).map((argument) => this.emitDescriptor(argument, substitutions)).join(", ")}] }`;
206
224
  }
207
225
  }
208
- emitDefinitionDescriptor(definition, fieldPropertyNames) {
226
+ emitDefinitionDescriptor(definition, fieldPropertyNames, substitutions) {
209
227
  if (definition.kind !== "record") {
210
- return this.emitDescriptor(definition);
228
+ return this.emitDescriptor(definition, substitutions);
229
+ }
230
+ return `{ kind: "record", fields: [${definition.fields.map((field, index) => `{ damlLabel: ${JSON.stringify(field.damlLabel)}, propertyName: ${JSON.stringify(this.getFieldPropertyName(definition, index, fieldPropertyNames))}, type: ${this.emitDescriptor(field.type, substitutions)} }`).join(", ")}] }`;
231
+ }
232
+ getTypeVariableSubstitution(type, substitutions) {
233
+ const substitution = substitutions.get(type.internedStringIndex);
234
+ if (substitution === undefined) {
235
+ throw new Error(`Cannot emit unbound generic DAML type variable '${type.name ?? `#${type.internedStringIndex}`}'`);
211
236
  }
212
- return `{ kind: "record", fields: [${definition.fields.map((field, index) => `{ damlLabel: ${JSON.stringify(field.damlLabel)}, propertyName: ${JSON.stringify(this.getFieldPropertyName(definition, index, fieldPropertyNames))}, type: ${this.emitDescriptor(field.type)} }`).join(", ")}] }`;
237
+ return substitution;
213
238
  }
214
239
  getFieldPropertyName(definition, index, names) {
215
240
  const name = names.get(this.getFieldKey(definition, index));