@distrohelena/canton-typescript-sdk 0.1.51 → 0.1.53
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/dist/cjs/daml-interface/emission/project-emitter.js +1 -0
- package/dist/cjs/daml-interface/emission/registry-emitter.js +17 -3
- package/dist/cjs/daml-interface/emission/template-binding-emitter.js +42 -17
- package/dist/cjs/daml-interface/emission-model/generated-template-binding.js +3 -0
- package/dist/cjs/daml-interface/runtime/daml-event-source-normalizer.js +13 -0
- package/dist/daml-interface/emission/project-emitter.js +1 -0
- package/dist/daml-interface/emission/registry-emitter.js +17 -3
- package/dist/daml-interface/emission/template-binding-emitter.d.ts +17 -1
- package/dist/daml-interface/emission/template-binding-emitter.js +42 -17
- package/dist/daml-interface/emission-model/generated-template-binding.d.ts +3 -0
- package/dist/daml-interface/emission-model/generated-template-binding.js +3 -0
- package/dist/daml-interface/runtime/daml-event-source-normalizer.d.ts +4 -0
- package/dist/daml-interface/runtime/daml-event-source-normalizer.js +13 -0
- package/node/start-local.sh +111 -4
- package/node/stop-local.sh +25 -1
- package/package.json +2 -1
|
@@ -30,6 +30,7 @@ class ProjectEmitter {
|
|
|
30
30
|
/** Emits the complete in-memory DAML interface project from analyzed templates. */
|
|
31
31
|
emitProject(analysis, moduleImportStyle = daml_module_import_style_js_1.DamlModuleImportStyles.esm) {
|
|
32
32
|
this.namedTypeEmitter.prepareProjectOrThrow(analysis.templates, analysis.typeDefinitions, analysis.packageMetadata);
|
|
33
|
+
this.templateBindingEmitter.providePackageMetadata(analysis.packageMetadata);
|
|
33
34
|
const templateBindingFiles = analysis.templates.map((template) => this.templateBindingEmitter.emitTemplateBindingFile(template));
|
|
34
35
|
const namedTypeFiles = this.namedTypeEmitter.emitPreparedNamedTypeFiles(analysis.typeDefinitions, templateBindingFiles, moduleImportStyle);
|
|
35
36
|
const templateFiles = analysis.templates.map((template) => this.templateBindingEmitter.emitTemplateFile(template, namedTypeFiles, moduleImportStyle));
|
|
@@ -22,7 +22,7 @@ class RegistryEmitter {
|
|
|
22
22
|
"export class GeneratedRegistry {",
|
|
23
23
|
" public static fromCreatedEvent(event: DamlCreatedEventSource): unknown {",
|
|
24
24
|
" const normalized = DamlEventSourceNormalizer.normalizeCreated(event);",
|
|
25
|
-
" switch (`${normalized.metadata.templateId.
|
|
25
|
+
" switch (`${normalized.metadata.templateId.moduleName}:${normalized.metadata.templateId.entityName}`) {",
|
|
26
26
|
...createdCases,
|
|
27
27
|
" default:",
|
|
28
28
|
" throw new DamlMaterializationError(\"template ID\", \"no generated template binding matches the created event\");",
|
|
@@ -31,7 +31,7 @@ class RegistryEmitter {
|
|
|
31
31
|
"",
|
|
32
32
|
" public static fromExercisedEvent(event: DamlExercisedEventSource): unknown {",
|
|
33
33
|
" const normalized = DamlEventSourceNormalizer.normalizeExercised(event);",
|
|
34
|
-
" switch (`${normalized.metadata.templateId.
|
|
34
|
+
" switch (`${normalized.metadata.templateId.moduleName}:${normalized.metadata.templateId.entityName}`) {",
|
|
35
35
|
...exercisedCases,
|
|
36
36
|
" default:",
|
|
37
37
|
" throw new DamlMaterializationError(\"template ID\", \"no generated template binding matches the exercised event\");",
|
|
@@ -43,7 +43,21 @@ class RegistryEmitter {
|
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
45
|
emitCases(project, methodName) {
|
|
46
|
-
|
|
46
|
+
// Dispatch is by module:entity — package ids are version-specific under smart contract upgrades,
|
|
47
|
+
// so any version of a template routes to its binding; the binding's own identity guard then
|
|
48
|
+
// verifies the package name when the event carries one.
|
|
49
|
+
const seen = new Map();
|
|
50
|
+
return project.templateFiles.map((file) => {
|
|
51
|
+
const parts = file.binding.templateIdLiteral.split(":");
|
|
52
|
+
const dispatchKey = `${parts[1]}:${parts[2]}`;
|
|
53
|
+
const existing = seen.get(dispatchKey);
|
|
54
|
+
if (existing !== undefined && existing !== file.binding.templateIdLiteral) {
|
|
55
|
+
throw new Error(`Generated registry cannot dispatch '${dispatchKey}': templates '${existing}' and `
|
|
56
|
+
+ `'${file.binding.templateIdLiteral}' collide on module:entity across packages.`);
|
|
57
|
+
}
|
|
58
|
+
seen.set(dispatchKey, file.binding.templateIdLiteral);
|
|
59
|
+
return ` case "${dispatchKey}":\n return ${file.binding.className}.${methodName}(event);`;
|
|
60
|
+
});
|
|
47
61
|
}
|
|
48
62
|
}
|
|
49
63
|
exports.RegistryEmitter = RegistryEmitter;
|
|
@@ -10,10 +10,15 @@ const relative_module_specifier_js_1 = require("./relative-module-specifier.js")
|
|
|
10
10
|
/** Emits typed contract and exercise-event bindings for analyzed DAML templates. */
|
|
11
11
|
class TemplateBindingEmitter {
|
|
12
12
|
nameResolver;
|
|
13
|
+
packageMetadata;
|
|
13
14
|
constructor(nameResolver = new type_script_name_resolver_js_1.TypeScriptNameResolver()) {
|
|
14
15
|
this.nameResolver = nameResolver;
|
|
15
16
|
void this.nameResolver;
|
|
16
17
|
}
|
|
18
|
+
/** Supplies per-package metadata so emitted identity checks can match by package NAME across upgrades. */
|
|
19
|
+
providePackageMetadata(metadata) {
|
|
20
|
+
this.packageMetadata = metadata;
|
|
21
|
+
}
|
|
17
22
|
/** Prepares stable collision-safe names before emitting a complete project. */
|
|
18
23
|
prepareTemplatesOrThrow(templates) {
|
|
19
24
|
this.nameResolver.prepareTemplatesOrThrow(templates);
|
|
@@ -49,7 +54,8 @@ class TemplateBindingEmitter {
|
|
|
49
54
|
templateIdentityKey: this.nameResolver.getTemplateIdentityKey(template),
|
|
50
55
|
namespaceAlias: this.nameResolver.getNamespaceAlias(template),
|
|
51
56
|
className,
|
|
52
|
-
templateIdLiteral: this.
|
|
57
|
+
templateIdLiteral: this.templateIdLiteral(template),
|
|
58
|
+
packageName: this.packageMetadata?.get(template.templateId.packageId)?.packageName,
|
|
53
59
|
path: this.nameResolver.getTemplateFilePath(template),
|
|
54
60
|
createFieldsTypeName: this.nameResolver.getCreateFieldsTypeName(template),
|
|
55
61
|
createdEventTypeName: this.nameResolver.getCreatedEventTypeName(template),
|
|
@@ -133,6 +139,7 @@ class TemplateBindingEmitter {
|
|
|
133
139
|
return [
|
|
134
140
|
`export class ${binding.className} extends ${this.getSdkName("DamlTemplate", runtimeWrapperTypeNames)} implements ${binding.createFieldsTypeName} {`,
|
|
135
141
|
` public static readonly templateId = ${JSON.stringify(binding.templateIdLiteral)};`,
|
|
142
|
+
...(binding.packageName === undefined ? [] : [` public static readonly packageName = ${JSON.stringify(binding.packageName)};`]),
|
|
136
143
|
` private static readonly descriptor: ${this.getSdkName("DamlTypeDescriptor", runtimeWrapperTypeNames)} = ${this.emitTemplateDescriptor(binding)};`,
|
|
137
144
|
"",
|
|
138
145
|
...binding.createFields.map((field) => ` public readonly ${field.propertyName}: ${field.typeName};`),
|
|
@@ -144,7 +151,7 @@ class TemplateBindingEmitter {
|
|
|
144
151
|
"",
|
|
145
152
|
` public static fromCreatedEvent(event: ${this.getSdkName("DamlCreatedEventSource", runtimeWrapperTypeNames)}): ${binding.className} {`,
|
|
146
153
|
` const normalized = ${this.getSdkName("DamlEventSourceNormalizer", runtimeWrapperTypeNames)}.normalizeCreated(event);`,
|
|
147
|
-
` ${binding.className}.assertTemplateIdentity(normalized.metadata.templateId);`,
|
|
154
|
+
` ${binding.className}.assertTemplateIdentity(normalized.metadata.templateId, normalized.metadata.packageName);`,
|
|
148
155
|
` const fields = ${this.getSdkName("DamlValueMaterializer", runtimeWrapperTypeNames)}.materialize<${binding.createFieldsTypeName}>(${this.getSdkName("DamlValueConverter", runtimeWrapperTypeNames)}.decode(normalized.payload, ${binding.className}.descriptor, GeneratedDamlTypeDescriptorRegistry, "create arguments"));`,
|
|
149
156
|
` return new ${binding.className}(`,
|
|
150
157
|
" normalized.contractId,",
|
|
@@ -154,7 +161,7 @@ class TemplateBindingEmitter {
|
|
|
154
161
|
"",
|
|
155
162
|
` public static fromExercisedEvent(event: ${this.getSdkName("DamlExercisedEventSource", runtimeWrapperTypeNames)}): ${exercisedReturnType} {`,
|
|
156
163
|
` const normalized = ${this.getSdkName("DamlEventSourceNormalizer", runtimeWrapperTypeNames)}.normalizeExercised(event);`,
|
|
157
|
-
` ${binding.className}.assertTemplateIdentity(normalized.metadata.templateId);`,
|
|
164
|
+
` ${binding.className}.assertTemplateIdentity(normalized.metadata.templateId, normalized.metadata.packageName);`,
|
|
158
165
|
" switch (normalized.choice) {",
|
|
159
166
|
...binding.choices.map((choice) => ` case ${JSON.stringify(choice.name)}:\n return ${choice.exercisedEventTypeName}.fromNormalizedEvent(normalized);`),
|
|
160
167
|
" default:",
|
|
@@ -162,11 +169,7 @@ class TemplateBindingEmitter {
|
|
|
162
169
|
" }",
|
|
163
170
|
" }",
|
|
164
171
|
"",
|
|
165
|
-
|
|
166
|
-
` if (identity.packageId !== ${JSON.stringify(this.packageId(binding))} || identity.moduleName !== ${JSON.stringify(this.moduleName(binding))} || identity.entityName !== ${JSON.stringify(this.entityName(binding))}) {`,
|
|
167
|
-
` throw new ${this.getSdkName("DamlMaterializationError", runtimeWrapperTypeNames)}("template ID", \`Expected template '${binding.templateIdLiteral}' but received '\${identity.packageId}:\${identity.moduleName}:\${identity.entityName}'\`);`,
|
|
168
|
-
" }",
|
|
169
|
-
" }",
|
|
172
|
+
...this.emitAssertTemplateIdentity(binding, runtimeWrapperTypeNames),
|
|
170
173
|
"}",
|
|
171
174
|
].join("\n");
|
|
172
175
|
}
|
|
@@ -193,12 +196,12 @@ class TemplateBindingEmitter {
|
|
|
193
196
|
"",
|
|
194
197
|
` public static fromExercisedEvent(event: ${this.getSdkName("DamlExercisedEventSource", runtimeWrapperTypeNames)}): ${choice.exercisedEventTypeName} {`,
|
|
195
198
|
` const normalized = ${this.getSdkName("DamlEventSourceNormalizer", runtimeWrapperTypeNames)}.normalizeExercised(event);`,
|
|
196
|
-
` ${choice.exercisedEventTypeName}.assertTemplateIdentity(normalized.metadata.templateId);`,
|
|
199
|
+
` ${choice.exercisedEventTypeName}.assertTemplateIdentity(normalized.metadata.templateId, normalized.metadata.packageName);`,
|
|
197
200
|
` return ${choice.exercisedEventTypeName}.fromNormalizedEvent(normalized);`,
|
|
198
201
|
" }",
|
|
199
202
|
"",
|
|
200
203
|
` public static fromNormalizedEvent(event: ${this.getSdkName("DamlNormalizedExercisedEvent", runtimeWrapperTypeNames)}): ${choice.exercisedEventTypeName} {`,
|
|
201
|
-
` ${choice.exercisedEventTypeName}.assertTemplateIdentity(event.metadata.templateId);`,
|
|
204
|
+
` ${choice.exercisedEventTypeName}.assertTemplateIdentity(event.metadata.templateId, event.metadata.packageName);`,
|
|
202
205
|
` if (event.choice !== ${JSON.stringify(choice.name)}) {`,
|
|
203
206
|
` throw new ${this.getSdkName("DamlMaterializationError", runtimeWrapperTypeNames)}("choice", \`Expected choice '${choice.name}' but received '\${event.choice}'\`);`,
|
|
204
207
|
" }",
|
|
@@ -207,14 +210,28 @@ class TemplateBindingEmitter {
|
|
|
207
210
|
` return new ${choice.exercisedEventTypeName}(event.contractId, argument, result, event.consuming, event.metadata);`,
|
|
208
211
|
" }",
|
|
209
212
|
"",
|
|
210
|
-
|
|
211
|
-
` if (identity.packageId !== ${JSON.stringify(this.packageId(binding))} || identity.moduleName !== ${JSON.stringify(this.moduleName(binding))} || identity.entityName !== ${JSON.stringify(this.entityName(binding))}) {`,
|
|
212
|
-
` throw new ${this.getSdkName("DamlMaterializationError", runtimeWrapperTypeNames)}("template ID", \`Expected template '${binding.templateIdLiteral}' but received '\${identity.packageId}:\${identity.moduleName}:\${identity.entityName}'\`);`,
|
|
213
|
-
" }",
|
|
214
|
-
" }",
|
|
213
|
+
...this.emitAssertTemplateIdentity(binding, runtimeWrapperTypeNames),
|
|
215
214
|
"}",
|
|
216
215
|
].join("\n");
|
|
217
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* Emits the identity guard for materialization. Module and entity must match exactly; the package is
|
|
219
|
+
* matched by NAME when the event provides one — never by exact package id, because smart contract
|
|
220
|
+
* upgrades give every version its own id while contracts from any version stay materializable.
|
|
221
|
+
*/
|
|
222
|
+
emitAssertTemplateIdentity(binding, runtimeWrapperTypeNames) {
|
|
223
|
+
const lines = [
|
|
224
|
+
" private static assertTemplateIdentity(identity: { readonly packageId: string; readonly moduleName: string; readonly entityName: string }, packageName?: string): void {",
|
|
225
|
+
` if (identity.moduleName !== ${JSON.stringify(this.moduleName(binding))} || identity.entityName !== ${JSON.stringify(this.entityName(binding))}) {`,
|
|
226
|
+
` throw new ${this.getSdkName("DamlMaterializationError", runtimeWrapperTypeNames)}("template ID", \`Expected template '${binding.templateIdLiteral}' but received '\${identity.packageId}:\${identity.moduleName}:\${identity.entityName}'\`);`,
|
|
227
|
+
" }",
|
|
228
|
+
];
|
|
229
|
+
if (binding.packageName !== undefined) {
|
|
230
|
+
lines.push(` if (packageName !== undefined && packageName !== ${JSON.stringify(binding.packageName)}) {`, ` throw new ${this.getSdkName("DamlMaterializationError", runtimeWrapperTypeNames)}("template ID", \`Expected package '${binding.packageName}' but received '\${packageName}' for template '${binding.templateIdLiteral}'\`);`, " }");
|
|
231
|
+
}
|
|
232
|
+
lines.push(" }");
|
|
233
|
+
return lines;
|
|
234
|
+
}
|
|
218
235
|
emitTemplateDescriptor(binding) {
|
|
219
236
|
return `{ kind: "record", fields: [${binding.createFields.map((field) => `{ damlLabel: ${JSON.stringify(field.name)}, propertyName: ${JSON.stringify(field.propertyName)}, type: ${this.emitDescriptor(field.type)} }`).join(", ")}] }`;
|
|
220
237
|
}
|
|
@@ -515,8 +532,16 @@ class TemplateBindingEmitter {
|
|
|
515
532
|
}
|
|
516
533
|
return (hash >>> 0).toString(36).padStart(6, "0").slice(-6);
|
|
517
534
|
}
|
|
518
|
-
|
|
519
|
-
|
|
535
|
+
/**
|
|
536
|
+
* The emitted identity literal NEVER contains a package id when the package name is known: package ids
|
|
537
|
+
* are version-specific under smart contract upgrades, while packageName:module:entity is stable. The
|
|
538
|
+
* id-based form remains only for metadata-less invocations (tests driving the emitter directly).
|
|
539
|
+
*/
|
|
540
|
+
templateIdLiteral(template) {
|
|
541
|
+
const packageName = this.packageMetadata?.get(template.templateId.packageId)?.packageName;
|
|
542
|
+
return packageName === undefined
|
|
543
|
+
? this.nameResolver.getTemplateIdLiteral(template)
|
|
544
|
+
: `${packageName}:${template.templateId.moduleName}:${template.templateId.templateName}`;
|
|
520
545
|
}
|
|
521
546
|
moduleName(binding) {
|
|
522
547
|
return binding.templateIdLiteral.split(":")[1];
|
|
@@ -21,6 +21,8 @@ class GeneratedTemplateBinding {
|
|
|
21
21
|
namespaceAlias;
|
|
22
22
|
className;
|
|
23
23
|
templateIdLiteral;
|
|
24
|
+
/** The owning package's name; enables upgrade-aware (name-based) identity checks in emitted code. */
|
|
25
|
+
packageName;
|
|
24
26
|
path;
|
|
25
27
|
createFieldsTypeName;
|
|
26
28
|
createdEventTypeName;
|
|
@@ -31,6 +33,7 @@ class GeneratedTemplateBinding {
|
|
|
31
33
|
this.namespaceAlias = init.namespaceAlias ?? init.className;
|
|
32
34
|
this.className = init.className;
|
|
33
35
|
this.templateIdLiteral = init.templateIdLiteral;
|
|
36
|
+
this.packageName = init.packageName;
|
|
34
37
|
this.path = init.path;
|
|
35
38
|
this.createFieldsTypeName = init.createFieldsTypeName;
|
|
36
39
|
this.createdEventTypeName = init.createdEventTypeName;
|
|
@@ -147,6 +147,7 @@ function identityFrom(value, path) {
|
|
|
147
147
|
function freezeCreatedMetadata(event, templateId) {
|
|
148
148
|
return Object.freeze(removeUndefined({
|
|
149
149
|
templateId,
|
|
150
|
+
packageName: optionalPackageName(event, "created event source"),
|
|
150
151
|
offset: optionalString(event, ["offset", "createdEventOffset", "created_event_offset"], "offset", "created event source"),
|
|
151
152
|
nodeId: optionalNodeId(event, ["nodeId", "node_id"], "node ID", "created event source"),
|
|
152
153
|
witnessParties: optionalStringArray(event, ["witnessParties", "witness_parties", "witnesses"], "witness parties", "created event source"),
|
|
@@ -160,6 +161,7 @@ function freezeExercisedMetadata(event, source, templateId) {
|
|
|
160
161
|
const transaction = asObject(readProperty(event, ["transaction"]).value) ?? asObject(root?.transaction);
|
|
161
162
|
return Object.freeze(removeUndefined({
|
|
162
163
|
templateId,
|
|
164
|
+
packageName: optionalPackageName(event, "exercised event source"),
|
|
163
165
|
offset: optionalString(event, ["offset"], "offset", "exercised event source")
|
|
164
166
|
?? optionalString(transaction ?? {}, ["offset"], "offset", "exercised event source"),
|
|
165
167
|
nodeId: optionalNodeId(event, ["nodeId", "node_id"], "node ID", "exercised event source"),
|
|
@@ -527,6 +529,17 @@ function cloneAndFreeze(value, seen = new WeakMap()) {
|
|
|
527
529
|
function removeUndefined(value) {
|
|
528
530
|
return Object.fromEntries(Object.entries(value).filter(([, field]) => field !== undefined));
|
|
529
531
|
}
|
|
532
|
+
/** Protobuf-generated events default packageName to ""; treat the default as absent, not invalid. */
|
|
533
|
+
function optionalPackageName(event, context) {
|
|
534
|
+
const { value } = readProperty(event, ["packageName", "package_name"]);
|
|
535
|
+
if (value === undefined || value === null || value === "") {
|
|
536
|
+
return undefined;
|
|
537
|
+
}
|
|
538
|
+
if (typeof value !== "string") {
|
|
539
|
+
throw sourceError(context, "package name must be a string");
|
|
540
|
+
}
|
|
541
|
+
return value;
|
|
542
|
+
}
|
|
530
543
|
function sourceError(path, detail) {
|
|
531
544
|
return new daml_materialization_error_js_1.DamlMaterializationError(path, detail);
|
|
532
545
|
}
|
|
@@ -27,6 +27,7 @@ export class ProjectEmitter {
|
|
|
27
27
|
/** Emits the complete in-memory DAML interface project from analyzed templates. */
|
|
28
28
|
emitProject(analysis, moduleImportStyle = DamlModuleImportStyles.esm) {
|
|
29
29
|
this.namedTypeEmitter.prepareProjectOrThrow(analysis.templates, analysis.typeDefinitions, analysis.packageMetadata);
|
|
30
|
+
this.templateBindingEmitter.providePackageMetadata(analysis.packageMetadata);
|
|
30
31
|
const templateBindingFiles = analysis.templates.map((template) => this.templateBindingEmitter.emitTemplateBindingFile(template));
|
|
31
32
|
const namedTypeFiles = this.namedTypeEmitter.emitPreparedNamedTypeFiles(analysis.typeDefinitions, templateBindingFiles, moduleImportStyle);
|
|
32
33
|
const templateFiles = analysis.templates.map((template) => this.templateBindingEmitter.emitTemplateFile(template, namedTypeFiles, moduleImportStyle));
|
|
@@ -19,7 +19,7 @@ export class RegistryEmitter {
|
|
|
19
19
|
"export class GeneratedRegistry {",
|
|
20
20
|
" public static fromCreatedEvent(event: DamlCreatedEventSource): unknown {",
|
|
21
21
|
" const normalized = DamlEventSourceNormalizer.normalizeCreated(event);",
|
|
22
|
-
" switch (`${normalized.metadata.templateId.
|
|
22
|
+
" switch (`${normalized.metadata.templateId.moduleName}:${normalized.metadata.templateId.entityName}`) {",
|
|
23
23
|
...createdCases,
|
|
24
24
|
" default:",
|
|
25
25
|
" throw new DamlMaterializationError(\"template ID\", \"no generated template binding matches the created event\");",
|
|
@@ -28,7 +28,7 @@ export class RegistryEmitter {
|
|
|
28
28
|
"",
|
|
29
29
|
" public static fromExercisedEvent(event: DamlExercisedEventSource): unknown {",
|
|
30
30
|
" const normalized = DamlEventSourceNormalizer.normalizeExercised(event);",
|
|
31
|
-
" switch (`${normalized.metadata.templateId.
|
|
31
|
+
" switch (`${normalized.metadata.templateId.moduleName}:${normalized.metadata.templateId.entityName}`) {",
|
|
32
32
|
...exercisedCases,
|
|
33
33
|
" default:",
|
|
34
34
|
" throw new DamlMaterializationError(\"template ID\", \"no generated template binding matches the exercised event\");",
|
|
@@ -40,6 +40,20 @@ export class RegistryEmitter {
|
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
emitCases(project, methodName) {
|
|
43
|
-
|
|
43
|
+
// Dispatch is by module:entity — package ids are version-specific under smart contract upgrades,
|
|
44
|
+
// so any version of a template routes to its binding; the binding's own identity guard then
|
|
45
|
+
// verifies the package name when the event carries one.
|
|
46
|
+
const seen = new Map();
|
|
47
|
+
return project.templateFiles.map((file) => {
|
|
48
|
+
const parts = file.binding.templateIdLiteral.split(":");
|
|
49
|
+
const dispatchKey = `${parts[1]}:${parts[2]}`;
|
|
50
|
+
const existing = seen.get(dispatchKey);
|
|
51
|
+
if (existing !== undefined && existing !== file.binding.templateIdLiteral) {
|
|
52
|
+
throw new Error(`Generated registry cannot dispatch '${dispatchKey}': templates '${existing}' and `
|
|
53
|
+
+ `'${file.binding.templateIdLiteral}' collide on module:entity across packages.`);
|
|
54
|
+
}
|
|
55
|
+
seen.set(dispatchKey, file.binding.templateIdLiteral);
|
|
56
|
+
return ` case "${dispatchKey}":\n return ${file.binding.className}.${methodName}(event);`;
|
|
57
|
+
});
|
|
44
58
|
}
|
|
45
59
|
}
|
|
@@ -6,7 +6,12 @@ import { type DamlModuleImportStyle } from "./daml-module-import-style.js";
|
|
|
6
6
|
/** Emits typed contract and exercise-event bindings for analyzed DAML templates. */
|
|
7
7
|
export declare class TemplateBindingEmitter {
|
|
8
8
|
private readonly nameResolver;
|
|
9
|
+
private packageMetadata?;
|
|
9
10
|
constructor(nameResolver?: TypeScriptNameResolver);
|
|
11
|
+
/** Supplies per-package metadata so emitted identity checks can match by package NAME across upgrades. */
|
|
12
|
+
providePackageMetadata(metadata: ReadonlyMap<string, {
|
|
13
|
+
readonly packageName: string;
|
|
14
|
+
}>): void;
|
|
10
15
|
/** Prepares stable collision-safe names before emitting a complete project. */
|
|
11
16
|
prepareTemplatesOrThrow(templates: readonly AnalyzedTemplate[]): void;
|
|
12
17
|
/** Emits a generated TypeScript file for one analyzed DAML template. */
|
|
@@ -18,6 +23,12 @@ export declare class TemplateBindingEmitter {
|
|
|
18
23
|
private emitImports;
|
|
19
24
|
private emitTemplateClass;
|
|
20
25
|
private emitChoiceEventClass;
|
|
26
|
+
/**
|
|
27
|
+
* Emits the identity guard for materialization. Module and entity must match exactly; the package is
|
|
28
|
+
* matched by NAME when the event provides one — never by exact package id, because smart contract
|
|
29
|
+
* upgrades give every version its own id while contracts from any version stay materializable.
|
|
30
|
+
*/
|
|
31
|
+
private emitAssertTemplateIdentity;
|
|
21
32
|
private emitTemplateDescriptor;
|
|
22
33
|
private emitDescriptor;
|
|
23
34
|
private getTypeName;
|
|
@@ -33,7 +44,12 @@ export declare class TemplateBindingEmitter {
|
|
|
33
44
|
private getNamedReferenceKey;
|
|
34
45
|
private toTypeName;
|
|
35
46
|
private shortHash;
|
|
36
|
-
|
|
47
|
+
/**
|
|
48
|
+
* The emitted identity literal NEVER contains a package id when the package name is known: package ids
|
|
49
|
+
* are version-specific under smart contract upgrades, while packageName:module:entity is stable. The
|
|
50
|
+
* id-based form remains only for metadata-less invocations (tests driving the emitter directly).
|
|
51
|
+
*/
|
|
52
|
+
private templateIdLiteral;
|
|
37
53
|
private moduleName;
|
|
38
54
|
private entityName;
|
|
39
55
|
private relativeFilePath;
|
|
@@ -7,10 +7,15 @@ import { RelativeModuleSpecifier } from "./relative-module-specifier.js";
|
|
|
7
7
|
/** Emits typed contract and exercise-event bindings for analyzed DAML templates. */
|
|
8
8
|
export class TemplateBindingEmitter {
|
|
9
9
|
nameResolver;
|
|
10
|
+
packageMetadata;
|
|
10
11
|
constructor(nameResolver = new TypeScriptNameResolver()) {
|
|
11
12
|
this.nameResolver = nameResolver;
|
|
12
13
|
void this.nameResolver;
|
|
13
14
|
}
|
|
15
|
+
/** Supplies per-package metadata so emitted identity checks can match by package NAME across upgrades. */
|
|
16
|
+
providePackageMetadata(metadata) {
|
|
17
|
+
this.packageMetadata = metadata;
|
|
18
|
+
}
|
|
14
19
|
/** Prepares stable collision-safe names before emitting a complete project. */
|
|
15
20
|
prepareTemplatesOrThrow(templates) {
|
|
16
21
|
this.nameResolver.prepareTemplatesOrThrow(templates);
|
|
@@ -46,7 +51,8 @@ export class TemplateBindingEmitter {
|
|
|
46
51
|
templateIdentityKey: this.nameResolver.getTemplateIdentityKey(template),
|
|
47
52
|
namespaceAlias: this.nameResolver.getNamespaceAlias(template),
|
|
48
53
|
className,
|
|
49
|
-
templateIdLiteral: this.
|
|
54
|
+
templateIdLiteral: this.templateIdLiteral(template),
|
|
55
|
+
packageName: this.packageMetadata?.get(template.templateId.packageId)?.packageName,
|
|
50
56
|
path: this.nameResolver.getTemplateFilePath(template),
|
|
51
57
|
createFieldsTypeName: this.nameResolver.getCreateFieldsTypeName(template),
|
|
52
58
|
createdEventTypeName: this.nameResolver.getCreatedEventTypeName(template),
|
|
@@ -130,6 +136,7 @@ export class TemplateBindingEmitter {
|
|
|
130
136
|
return [
|
|
131
137
|
`export class ${binding.className} extends ${this.getSdkName("DamlTemplate", runtimeWrapperTypeNames)} implements ${binding.createFieldsTypeName} {`,
|
|
132
138
|
` public static readonly templateId = ${JSON.stringify(binding.templateIdLiteral)};`,
|
|
139
|
+
...(binding.packageName === undefined ? [] : [` public static readonly packageName = ${JSON.stringify(binding.packageName)};`]),
|
|
133
140
|
` private static readonly descriptor: ${this.getSdkName("DamlTypeDescriptor", runtimeWrapperTypeNames)} = ${this.emitTemplateDescriptor(binding)};`,
|
|
134
141
|
"",
|
|
135
142
|
...binding.createFields.map((field) => ` public readonly ${field.propertyName}: ${field.typeName};`),
|
|
@@ -141,7 +148,7 @@ export class TemplateBindingEmitter {
|
|
|
141
148
|
"",
|
|
142
149
|
` public static fromCreatedEvent(event: ${this.getSdkName("DamlCreatedEventSource", runtimeWrapperTypeNames)}): ${binding.className} {`,
|
|
143
150
|
` const normalized = ${this.getSdkName("DamlEventSourceNormalizer", runtimeWrapperTypeNames)}.normalizeCreated(event);`,
|
|
144
|
-
` ${binding.className}.assertTemplateIdentity(normalized.metadata.templateId);`,
|
|
151
|
+
` ${binding.className}.assertTemplateIdentity(normalized.metadata.templateId, normalized.metadata.packageName);`,
|
|
145
152
|
` const fields = ${this.getSdkName("DamlValueMaterializer", runtimeWrapperTypeNames)}.materialize<${binding.createFieldsTypeName}>(${this.getSdkName("DamlValueConverter", runtimeWrapperTypeNames)}.decode(normalized.payload, ${binding.className}.descriptor, GeneratedDamlTypeDescriptorRegistry, "create arguments"));`,
|
|
146
153
|
` return new ${binding.className}(`,
|
|
147
154
|
" normalized.contractId,",
|
|
@@ -151,7 +158,7 @@ export class TemplateBindingEmitter {
|
|
|
151
158
|
"",
|
|
152
159
|
` public static fromExercisedEvent(event: ${this.getSdkName("DamlExercisedEventSource", runtimeWrapperTypeNames)}): ${exercisedReturnType} {`,
|
|
153
160
|
` const normalized = ${this.getSdkName("DamlEventSourceNormalizer", runtimeWrapperTypeNames)}.normalizeExercised(event);`,
|
|
154
|
-
` ${binding.className}.assertTemplateIdentity(normalized.metadata.templateId);`,
|
|
161
|
+
` ${binding.className}.assertTemplateIdentity(normalized.metadata.templateId, normalized.metadata.packageName);`,
|
|
155
162
|
" switch (normalized.choice) {",
|
|
156
163
|
...binding.choices.map((choice) => ` case ${JSON.stringify(choice.name)}:\n return ${choice.exercisedEventTypeName}.fromNormalizedEvent(normalized);`),
|
|
157
164
|
" default:",
|
|
@@ -159,11 +166,7 @@ export class TemplateBindingEmitter {
|
|
|
159
166
|
" }",
|
|
160
167
|
" }",
|
|
161
168
|
"",
|
|
162
|
-
|
|
163
|
-
` if (identity.packageId !== ${JSON.stringify(this.packageId(binding))} || identity.moduleName !== ${JSON.stringify(this.moduleName(binding))} || identity.entityName !== ${JSON.stringify(this.entityName(binding))}) {`,
|
|
164
|
-
` throw new ${this.getSdkName("DamlMaterializationError", runtimeWrapperTypeNames)}("template ID", \`Expected template '${binding.templateIdLiteral}' but received '\${identity.packageId}:\${identity.moduleName}:\${identity.entityName}'\`);`,
|
|
165
|
-
" }",
|
|
166
|
-
" }",
|
|
169
|
+
...this.emitAssertTemplateIdentity(binding, runtimeWrapperTypeNames),
|
|
167
170
|
"}",
|
|
168
171
|
].join("\n");
|
|
169
172
|
}
|
|
@@ -190,12 +193,12 @@ export class TemplateBindingEmitter {
|
|
|
190
193
|
"",
|
|
191
194
|
` public static fromExercisedEvent(event: ${this.getSdkName("DamlExercisedEventSource", runtimeWrapperTypeNames)}): ${choice.exercisedEventTypeName} {`,
|
|
192
195
|
` const normalized = ${this.getSdkName("DamlEventSourceNormalizer", runtimeWrapperTypeNames)}.normalizeExercised(event);`,
|
|
193
|
-
` ${choice.exercisedEventTypeName}.assertTemplateIdentity(normalized.metadata.templateId);`,
|
|
196
|
+
` ${choice.exercisedEventTypeName}.assertTemplateIdentity(normalized.metadata.templateId, normalized.metadata.packageName);`,
|
|
194
197
|
` return ${choice.exercisedEventTypeName}.fromNormalizedEvent(normalized);`,
|
|
195
198
|
" }",
|
|
196
199
|
"",
|
|
197
200
|
` public static fromNormalizedEvent(event: ${this.getSdkName("DamlNormalizedExercisedEvent", runtimeWrapperTypeNames)}): ${choice.exercisedEventTypeName} {`,
|
|
198
|
-
` ${choice.exercisedEventTypeName}.assertTemplateIdentity(event.metadata.templateId);`,
|
|
201
|
+
` ${choice.exercisedEventTypeName}.assertTemplateIdentity(event.metadata.templateId, event.metadata.packageName);`,
|
|
199
202
|
` if (event.choice !== ${JSON.stringify(choice.name)}) {`,
|
|
200
203
|
` throw new ${this.getSdkName("DamlMaterializationError", runtimeWrapperTypeNames)}("choice", \`Expected choice '${choice.name}' but received '\${event.choice}'\`);`,
|
|
201
204
|
" }",
|
|
@@ -204,14 +207,28 @@ export class TemplateBindingEmitter {
|
|
|
204
207
|
` return new ${choice.exercisedEventTypeName}(event.contractId, argument, result, event.consuming, event.metadata);`,
|
|
205
208
|
" }",
|
|
206
209
|
"",
|
|
207
|
-
|
|
208
|
-
` if (identity.packageId !== ${JSON.stringify(this.packageId(binding))} || identity.moduleName !== ${JSON.stringify(this.moduleName(binding))} || identity.entityName !== ${JSON.stringify(this.entityName(binding))}) {`,
|
|
209
|
-
` throw new ${this.getSdkName("DamlMaterializationError", runtimeWrapperTypeNames)}("template ID", \`Expected template '${binding.templateIdLiteral}' but received '\${identity.packageId}:\${identity.moduleName}:\${identity.entityName}'\`);`,
|
|
210
|
-
" }",
|
|
211
|
-
" }",
|
|
210
|
+
...this.emitAssertTemplateIdentity(binding, runtimeWrapperTypeNames),
|
|
212
211
|
"}",
|
|
213
212
|
].join("\n");
|
|
214
213
|
}
|
|
214
|
+
/**
|
|
215
|
+
* Emits the identity guard for materialization. Module and entity must match exactly; the package is
|
|
216
|
+
* matched by NAME when the event provides one — never by exact package id, because smart contract
|
|
217
|
+
* upgrades give every version its own id while contracts from any version stay materializable.
|
|
218
|
+
*/
|
|
219
|
+
emitAssertTemplateIdentity(binding, runtimeWrapperTypeNames) {
|
|
220
|
+
const lines = [
|
|
221
|
+
" private static assertTemplateIdentity(identity: { readonly packageId: string; readonly moduleName: string; readonly entityName: string }, packageName?: string): void {",
|
|
222
|
+
` if (identity.moduleName !== ${JSON.stringify(this.moduleName(binding))} || identity.entityName !== ${JSON.stringify(this.entityName(binding))}) {`,
|
|
223
|
+
` throw new ${this.getSdkName("DamlMaterializationError", runtimeWrapperTypeNames)}("template ID", \`Expected template '${binding.templateIdLiteral}' but received '\${identity.packageId}:\${identity.moduleName}:\${identity.entityName}'\`);`,
|
|
224
|
+
" }",
|
|
225
|
+
];
|
|
226
|
+
if (binding.packageName !== undefined) {
|
|
227
|
+
lines.push(` if (packageName !== undefined && packageName !== ${JSON.stringify(binding.packageName)}) {`, ` throw new ${this.getSdkName("DamlMaterializationError", runtimeWrapperTypeNames)}("template ID", \`Expected package '${binding.packageName}' but received '\${packageName}' for template '${binding.templateIdLiteral}'\`);`, " }");
|
|
228
|
+
}
|
|
229
|
+
lines.push(" }");
|
|
230
|
+
return lines;
|
|
231
|
+
}
|
|
215
232
|
emitTemplateDescriptor(binding) {
|
|
216
233
|
return `{ kind: "record", fields: [${binding.createFields.map((field) => `{ damlLabel: ${JSON.stringify(field.name)}, propertyName: ${JSON.stringify(field.propertyName)}, type: ${this.emitDescriptor(field.type)} }`).join(", ")}] }`;
|
|
217
234
|
}
|
|
@@ -512,8 +529,16 @@ export class TemplateBindingEmitter {
|
|
|
512
529
|
}
|
|
513
530
|
return (hash >>> 0).toString(36).padStart(6, "0").slice(-6);
|
|
514
531
|
}
|
|
515
|
-
|
|
516
|
-
|
|
532
|
+
/**
|
|
533
|
+
* The emitted identity literal NEVER contains a package id when the package name is known: package ids
|
|
534
|
+
* are version-specific under smart contract upgrades, while packageName:module:entity is stable. The
|
|
535
|
+
* id-based form remains only for metadata-less invocations (tests driving the emitter directly).
|
|
536
|
+
*/
|
|
537
|
+
templateIdLiteral(template) {
|
|
538
|
+
const packageName = this.packageMetadata?.get(template.templateId.packageId)?.packageName;
|
|
539
|
+
return packageName === undefined
|
|
540
|
+
? this.nameResolver.getTemplateIdLiteral(template)
|
|
541
|
+
: `${packageName}:${template.templateId.moduleName}:${template.templateId.templateName}`;
|
|
517
542
|
}
|
|
518
543
|
moduleName(binding) {
|
|
519
544
|
return binding.templateIdLiteral.split(":")[1];
|
|
@@ -19,6 +19,8 @@ export declare class GeneratedTemplateBinding {
|
|
|
19
19
|
readonly namespaceAlias: string;
|
|
20
20
|
readonly className: string;
|
|
21
21
|
readonly templateIdLiteral: string;
|
|
22
|
+
/** The owning package's name; enables upgrade-aware (name-based) identity checks in emitted code. */
|
|
23
|
+
readonly packageName?: string;
|
|
22
24
|
readonly path: string;
|
|
23
25
|
readonly createFieldsTypeName: string;
|
|
24
26
|
readonly createdEventTypeName: string;
|
|
@@ -29,6 +31,7 @@ export declare class GeneratedTemplateBinding {
|
|
|
29
31
|
namespaceAlias?: string;
|
|
30
32
|
className: string;
|
|
31
33
|
templateIdLiteral: string;
|
|
34
|
+
packageName?: string;
|
|
32
35
|
path: string;
|
|
33
36
|
createFieldsTypeName: string;
|
|
34
37
|
createdEventTypeName: string;
|
|
@@ -17,6 +17,8 @@ export class GeneratedTemplateBinding {
|
|
|
17
17
|
namespaceAlias;
|
|
18
18
|
className;
|
|
19
19
|
templateIdLiteral;
|
|
20
|
+
/** The owning package's name; enables upgrade-aware (name-based) identity checks in emitted code. */
|
|
21
|
+
packageName;
|
|
20
22
|
path;
|
|
21
23
|
createFieldsTypeName;
|
|
22
24
|
createdEventTypeName;
|
|
@@ -27,6 +29,7 @@ export class GeneratedTemplateBinding {
|
|
|
27
29
|
this.namespaceAlias = init.namespaceAlias ?? init.className;
|
|
28
30
|
this.className = init.className;
|
|
29
31
|
this.templateIdLiteral = init.templateIdLiteral;
|
|
32
|
+
this.packageName = init.packageName;
|
|
30
33
|
this.path = init.path;
|
|
31
34
|
this.createFieldsTypeName = init.createFieldsTypeName;
|
|
32
35
|
this.createdEventTypeName = init.createdEventTypeName;
|
|
@@ -8,6 +8,8 @@ export type DamlCreatedEventSource = CreatedEvent | GetContractResponse | Active
|
|
|
8
8
|
export type DamlExercisedEventSource = ExercisedEvent | ExerciseResult | Event | DamlJsonEventRecord;
|
|
9
9
|
export type DamlCreatedEventMetadata = {
|
|
10
10
|
readonly templateId: DamlTypeIdentity;
|
|
11
|
+
/** The template's package NAME — the stable identity across smart-contract-upgrade versions. */
|
|
12
|
+
readonly packageName?: string;
|
|
11
13
|
readonly offset?: string;
|
|
12
14
|
readonly nodeId?: number;
|
|
13
15
|
readonly witnessParties?: readonly string[];
|
|
@@ -18,6 +20,8 @@ export type DamlCreatedEventMetadata = {
|
|
|
18
20
|
};
|
|
19
21
|
export type DamlExercisedEventMetadata = {
|
|
20
22
|
readonly templateId: DamlTypeIdentity;
|
|
23
|
+
/** The template's package NAME — the stable identity across smart-contract-upgrade versions. */
|
|
24
|
+
readonly packageName?: string;
|
|
21
25
|
readonly offset?: string;
|
|
22
26
|
readonly nodeId?: number;
|
|
23
27
|
readonly actingParties?: readonly string[];
|
|
@@ -143,6 +143,7 @@ function identityFrom(value, path) {
|
|
|
143
143
|
function freezeCreatedMetadata(event, templateId) {
|
|
144
144
|
return Object.freeze(removeUndefined({
|
|
145
145
|
templateId,
|
|
146
|
+
packageName: optionalPackageName(event, "created event source"),
|
|
146
147
|
offset: optionalString(event, ["offset", "createdEventOffset", "created_event_offset"], "offset", "created event source"),
|
|
147
148
|
nodeId: optionalNodeId(event, ["nodeId", "node_id"], "node ID", "created event source"),
|
|
148
149
|
witnessParties: optionalStringArray(event, ["witnessParties", "witness_parties", "witnesses"], "witness parties", "created event source"),
|
|
@@ -156,6 +157,7 @@ function freezeExercisedMetadata(event, source, templateId) {
|
|
|
156
157
|
const transaction = asObject(readProperty(event, ["transaction"]).value) ?? asObject(root?.transaction);
|
|
157
158
|
return Object.freeze(removeUndefined({
|
|
158
159
|
templateId,
|
|
160
|
+
packageName: optionalPackageName(event, "exercised event source"),
|
|
159
161
|
offset: optionalString(event, ["offset"], "offset", "exercised event source")
|
|
160
162
|
?? optionalString(transaction ?? {}, ["offset"], "offset", "exercised event source"),
|
|
161
163
|
nodeId: optionalNodeId(event, ["nodeId", "node_id"], "node ID", "exercised event source"),
|
|
@@ -523,6 +525,17 @@ function cloneAndFreeze(value, seen = new WeakMap()) {
|
|
|
523
525
|
function removeUndefined(value) {
|
|
524
526
|
return Object.fromEntries(Object.entries(value).filter(([, field]) => field !== undefined));
|
|
525
527
|
}
|
|
528
|
+
/** Protobuf-generated events default packageName to ""; treat the default as absent, not invalid. */
|
|
529
|
+
function optionalPackageName(event, context) {
|
|
530
|
+
const { value } = readProperty(event, ["packageName", "package_name"]);
|
|
531
|
+
if (value === undefined || value === null || value === "") {
|
|
532
|
+
return undefined;
|
|
533
|
+
}
|
|
534
|
+
if (typeof value !== "string") {
|
|
535
|
+
throw sourceError(context, "package name must be a string");
|
|
536
|
+
}
|
|
537
|
+
return value;
|
|
538
|
+
}
|
|
526
539
|
function sourceError(path, detail) {
|
|
527
540
|
return new DamlMaterializationError(path, detail);
|
|
528
541
|
}
|
package/node/start-local.sh
CHANGED
|
@@ -98,6 +98,19 @@ es256_runtime_dir() {
|
|
|
98
98
|
printf '%s\n' "${START_LOCAL_ES256_RUNTIME_DIR:-$REPO_ROOT/.generated/localnet-es256}"
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
resolve_no_auth_enabled() {
|
|
102
|
+
local value="${LOCALNET_NO_AUTH:-0}"
|
|
103
|
+
if [[ "$value" != "0" && "$value" != "1" ]]; then
|
|
104
|
+
echo "LOCALNET_NO_AUTH must be 0 or 1." >&2
|
|
105
|
+
return 1
|
|
106
|
+
fi
|
|
107
|
+
printf '%s\n' "$value"
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
no_auth_runtime_dir() {
|
|
111
|
+
printf '%s\n' "${START_LOCAL_NO_AUTH_RUNTIME_DIR:-$REPO_ROOT/.generated/localnet-no-auth}"
|
|
112
|
+
}
|
|
113
|
+
|
|
101
114
|
resolve_tls_enabled() {
|
|
102
115
|
local value="${LOCALNET_TLS:-0}"
|
|
103
116
|
if [[ "$value" != "0" && "$value" != "1" ]]; then
|
|
@@ -447,6 +460,66 @@ EOF
|
|
|
447
460
|
export LOCALNET_ES256_TOKEN_PATH="$token_path"
|
|
448
461
|
}
|
|
449
462
|
|
|
463
|
+
prepare_no_auth_runtime_files() {
|
|
464
|
+
if [[ "$(resolve_no_auth_enabled)" != "1" ]]; then
|
|
465
|
+
return 0
|
|
466
|
+
fi
|
|
467
|
+
|
|
468
|
+
local runtime_dir
|
|
469
|
+
runtime_dir="$(no_auth_runtime_dir)"
|
|
470
|
+
local compose_file="$runtime_dir/compose-no-auth.yaml"
|
|
471
|
+
local canton_config_file="$runtime_dir/canton-no-auth.conf"
|
|
472
|
+
mkdir -p "$runtime_dir"
|
|
473
|
+
|
|
474
|
+
cat > "$canton_config_file" <<EOF
|
|
475
|
+
include file("/app/base-app.conf")
|
|
476
|
+
|
|
477
|
+
$(if [[ "$(resolve_tls_enabled)" == "1" ]]; then printf 'include file("/app/localnet-tls.conf")\n'; fi)
|
|
478
|
+
|
|
479
|
+
EOF
|
|
480
|
+
local participant profile
|
|
481
|
+
for participant in app-provider app-user sv; do
|
|
482
|
+
case "$participant" in
|
|
483
|
+
app-provider) profile="$APP_PROVIDER_PROFILE" ;;
|
|
484
|
+
app-user) profile="$APP_USER_PROFILE" ;;
|
|
485
|
+
sv) profile="$SV_PROFILE" ;;
|
|
486
|
+
esac
|
|
487
|
+
if [[ "$profile" != "off" ]]; then
|
|
488
|
+
cat >> "$canton_config_file" <<EOF
|
|
489
|
+
canton.participants.${participant}.ledger-api.auth-services = []
|
|
490
|
+
EOF
|
|
491
|
+
fi
|
|
492
|
+
done
|
|
493
|
+
|
|
494
|
+
cat > "$compose_file" <<EOF
|
|
495
|
+
services:
|
|
496
|
+
canton:
|
|
497
|
+
volumes:
|
|
498
|
+
- "${LOCALNET_DIR}/conf/canton/app.conf:/app/base-app.conf:ro"
|
|
499
|
+
- "$canton_config_file:/app/app.conf:ro"
|
|
500
|
+
pqs-app-provider:
|
|
501
|
+
environment:
|
|
502
|
+
SCRIBE_SOURCE_LEDGER_AUTH: NoAuth
|
|
503
|
+
pqs-app-user:
|
|
504
|
+
environment:
|
|
505
|
+
SCRIBE_SOURCE_LEDGER_AUTH: NoAuth
|
|
506
|
+
pqs-sv:
|
|
507
|
+
environment:
|
|
508
|
+
SCRIBE_SOURCE_LEDGER_AUTH: NoAuth
|
|
509
|
+
EOF
|
|
510
|
+
export LOCALNET_NO_AUTH_COMPOSE_FILE="$compose_file"
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
append_no_auth_args() {
|
|
514
|
+
local -n compose_args_ref="$1"
|
|
515
|
+
if [[ "$(resolve_no_auth_enabled)" == "1" ]]; then
|
|
516
|
+
local compose_file="$(no_auth_runtime_dir)/compose-no-auth.yaml"
|
|
517
|
+
if [[ -f "$compose_file" ]]; then
|
|
518
|
+
compose_args_ref+=( -f "$compose_file" )
|
|
519
|
+
fi
|
|
520
|
+
fi
|
|
521
|
+
}
|
|
522
|
+
|
|
450
523
|
append_es256_args() {
|
|
451
524
|
local -n compose_args_ref="$1"
|
|
452
525
|
if [[ "$(resolve_es256_enabled)" == "1" ]]; then
|
|
@@ -485,6 +558,23 @@ docker_compose() {
|
|
|
485
558
|
"${DOCKER_COMPOSE_CMD[@]}" "$@"
|
|
486
559
|
}
|
|
487
560
|
|
|
561
|
+
resolve_extra_participant_auth() {
|
|
562
|
+
local index="$1"
|
|
563
|
+
local var="EXTRA_PARTICIPANT_${index}_AUTH"
|
|
564
|
+
local value="${!var:-}"
|
|
565
|
+
if [[ -z "$value" ]]; then
|
|
566
|
+
if [[ "$(resolve_no_auth_enabled)" == "1" ]]; then value="none"; else value="jwt"; fi
|
|
567
|
+
fi
|
|
568
|
+
case "$value" in
|
|
569
|
+
jwt|none) ;;
|
|
570
|
+
*)
|
|
571
|
+
echo "EXTRA_PARTICIPANT_${index}_AUTH must be 'jwt' or 'none'." >&2
|
|
572
|
+
return 1
|
|
573
|
+
;;
|
|
574
|
+
esac
|
|
575
|
+
printf '%s\n' "$value"
|
|
576
|
+
}
|
|
577
|
+
|
|
488
578
|
resolve_extra_participants() {
|
|
489
579
|
local value="${EXTRA_PARTICIPANTS:-0}"
|
|
490
580
|
if [[ ! "$value" =~ ^[0-9]+$ ]]; then
|
|
@@ -684,6 +774,16 @@ EOF
|
|
|
684
774
|
done
|
|
685
775
|
fi
|
|
686
776
|
|
|
777
|
+
local no_auth_index
|
|
778
|
+
for ((no_auth_index = 1; no_auth_index <= count; no_auth_index++)); do
|
|
779
|
+
if [[ "$(resolve_extra_participant_auth "$no_auth_index")" == "none" ]]; then
|
|
780
|
+
cat >> "$canton_config_file" <<EOF
|
|
781
|
+
canton.participants.extra-${no_auth_index}.ledger-api.auth-services = []
|
|
782
|
+
|
|
783
|
+
EOF
|
|
784
|
+
fi
|
|
785
|
+
done
|
|
786
|
+
|
|
687
787
|
if [[ "$(resolve_tls_enabled)" == "1" ]]; then
|
|
688
788
|
local index
|
|
689
789
|
for ((index = 1; index <= count; index++)); do
|
|
@@ -842,7 +942,7 @@ EOF
|
|
|
842
942
|
environment:
|
|
843
943
|
SCRIBE_SOURCE_LEDGER_HOST: canton
|
|
844
944
|
SCRIBE_SOURCE_LEDGER_PORT: \${EXTRA_PARTICIPANT_${index}_LEDGER_API_PORT}
|
|
845
|
-
SCRIBE_SOURCE_LEDGER_AUTH: OAuth
|
|
945
|
+
SCRIBE_SOURCE_LEDGER_AUTH: $(if [[ "$(resolve_extra_participant_auth "$index")" == "none" ]]; then printf 'NoAuth'; else printf 'OAuth'; fi)
|
|
846
946
|
SCRIBE_TARGET_POSTGRES_HOST: postgres-pqs-extra-${index}
|
|
847
947
|
SCRIBE_TARGET_POSTGRES_PORT: 5432
|
|
848
948
|
SCRIBE_TARGET_POSTGRES_DATABASE: \${EXTRA_PQS_${index}_POSTGRES_DB}
|
|
@@ -1218,8 +1318,9 @@ start_ledger_stack() {
|
|
|
1218
1318
|
load_localnet_common_env "$localnet_dir/env/common.env"
|
|
1219
1319
|
prepare_tls_runtime_files
|
|
1220
1320
|
prepare_es256_runtime_files
|
|
1321
|
+
prepare_no_auth_runtime_files
|
|
1221
1322
|
local extra_participants
|
|
1222
|
-
extra_participants="$(resolve_extra_participants)"
|
|
1323
|
+
extra_participants="$(resolve_extra_participants)"
|
|
1223
1324
|
if (( extra_participants > 0 )) && [[ "$auth_mode" != "shared-secret" ]]; then
|
|
1224
1325
|
echo "EXTRA_PARTICIPANTS with extra PQS currently supports AUTH_MODE=shared-secret only." >&2
|
|
1225
1326
|
return 1
|
|
@@ -1252,6 +1353,7 @@ start_ledger_stack() {
|
|
|
1252
1353
|
append_extra_participant_args "$extra_participants" "$auth_mode" compose_args
|
|
1253
1354
|
append_tls_args compose_args
|
|
1254
1355
|
append_es256_args compose_args
|
|
1356
|
+
append_no_auth_args compose_args
|
|
1255
1357
|
docker_compose "${compose_args[@]}" down -v --remove-orphans
|
|
1256
1358
|
mapfile -t startup_services < <(prerequisite_services "$auth_mode")
|
|
1257
1359
|
mapfile -t followup_services < <(dependent_services)
|
|
@@ -1289,17 +1391,22 @@ load_repo_root_env
|
|
|
1289
1391
|
QUICKSTART_DIR="$(resolve_quickstart_dir)"
|
|
1290
1392
|
cd "$QUICKSTART_DIR"
|
|
1291
1393
|
|
|
1394
|
+
if [[ "$(resolve_no_auth_enabled)" == "1" && "$(resolve_es256_enabled)" == "1" ]]; then
|
|
1395
|
+
echo "LOCALNET_NO_AUTH=1 conflicts with LOCALNET_ES256_JWT=1: pick one auth mode." >&2
|
|
1396
|
+
exit 1
|
|
1397
|
+
fi
|
|
1398
|
+
|
|
1292
1399
|
if [[ ! -f .env.local ]]; then
|
|
1293
1400
|
echo ".env.local not found. Bootstrapping Quickstart with 'make setup'."
|
|
1294
1401
|
make setup
|
|
1295
1402
|
fi
|
|
1296
1403
|
|
|
1297
|
-
if [[ "$(resolve_es256_enabled)" != "1" && "$(resolve_tls_enabled)" != "1" ]] && make_target_exists start-local-ledger; then
|
|
1404
|
+
if [[ "$(resolve_es256_enabled)" != "1" && "$(resolve_tls_enabled)" != "1" && "$(resolve_no_auth_enabled)" != "1" ]] && make_target_exists start-local-ledger; then
|
|
1298
1405
|
make start-local-ledger
|
|
1299
1406
|
else
|
|
1300
1407
|
auth_mode="$(read_env_value AUTH_MODE || true)"
|
|
1301
1408
|
auth_mode="${auth_mode:-shared-secret}"
|
|
1302
|
-
if [[ "$(resolve_es256_enabled)" == "1" || "$(resolve_tls_enabled)" == "1" ]]; then
|
|
1409
|
+
if [[ "$(resolve_es256_enabled)" == "1" || "$(resolve_tls_enabled)" == "1" || "$(resolve_no_auth_enabled)" == "1" ]]; then
|
|
1303
1410
|
echo "Generated localnet transport overlay enabled. Starting ledger-only compose stack directly."
|
|
1304
1411
|
else
|
|
1305
1412
|
echo "start-local-ledger target not found. Starting ledger-only compose stack directly."
|
package/node/stop-local.sh
CHANGED
|
@@ -92,6 +92,19 @@ es256_runtime_dir() {
|
|
|
92
92
|
printf '%s\n' "${START_LOCAL_ES256_RUNTIME_DIR:-$REPO_ROOT/.generated/localnet-es256}"
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
resolve_no_auth_enabled() {
|
|
96
|
+
local value="${LOCALNET_NO_AUTH:-0}"
|
|
97
|
+
if [[ "$value" != "0" && "$value" != "1" ]]; then
|
|
98
|
+
echo "LOCALNET_NO_AUTH must be 0 or 1." >&2
|
|
99
|
+
return 1
|
|
100
|
+
fi
|
|
101
|
+
printf '%s\n' "$value"
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
no_auth_runtime_dir() {
|
|
105
|
+
printf '%s\n' "${START_LOCAL_NO_AUTH_RUNTIME_DIR:-$REPO_ROOT/.generated/localnet-no-auth}"
|
|
106
|
+
}
|
|
107
|
+
|
|
95
108
|
resolve_tls_enabled() {
|
|
96
109
|
local value="${LOCALNET_TLS:-0}"
|
|
97
110
|
if [[ "$value" != "0" && "$value" != "1" ]]; then
|
|
@@ -125,6 +138,16 @@ append_tls_args() {
|
|
|
125
138
|
fi
|
|
126
139
|
}
|
|
127
140
|
|
|
141
|
+
append_no_auth_args() {
|
|
142
|
+
local -n compose_args_ref="$1"
|
|
143
|
+
if [[ "$(resolve_no_auth_enabled)" == "1" ]]; then
|
|
144
|
+
local compose_file="$(no_auth_runtime_dir)/compose-no-auth.yaml"
|
|
145
|
+
if [[ -f "$compose_file" ]]; then
|
|
146
|
+
compose_args_ref+=( -f "$compose_file" )
|
|
147
|
+
fi
|
|
148
|
+
fi
|
|
149
|
+
}
|
|
150
|
+
|
|
128
151
|
resolve_docker_compose_cmd() {
|
|
129
152
|
if (( ${#DOCKER_COMPOSE_CMD[@]} > 0 )); then
|
|
130
153
|
return 0
|
|
@@ -203,6 +226,7 @@ stop_ledger_stack() {
|
|
|
203
226
|
append_existing_extra_participant_args compose_args
|
|
204
227
|
append_tls_args compose_args
|
|
205
228
|
append_es256_args compose_args
|
|
229
|
+
append_no_auth_args compose_args
|
|
206
230
|
docker_compose "${compose_args[@]}" down -v --remove-orphans
|
|
207
231
|
}
|
|
208
232
|
|
|
@@ -215,7 +239,7 @@ if [[ ! -f .env.local ]]; then
|
|
|
215
239
|
exit 0
|
|
216
240
|
fi
|
|
217
241
|
|
|
218
|
-
if [[ "$(resolve_es256_enabled)" != "1" && "$(resolve_tls_enabled)" != "1" ]] && make_target_exists stop-local-ledger; then
|
|
242
|
+
if [[ "$(resolve_es256_enabled)" != "1" && "$(resolve_tls_enabled)" != "1" && "$(resolve_no_auth_enabled)" != "1" ]] && make_target_exists stop-local-ledger; then
|
|
219
243
|
make stop-local-ledger
|
|
220
244
|
else
|
|
221
245
|
auth_mode="$(read_env_value AUTH_MODE || true)"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@distrohelena/canton-typescript-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.53",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -87,6 +87,7 @@
|
|
|
87
87
|
},
|
|
88
88
|
"scripts": {
|
|
89
89
|
"build": "node ./scripts/clean-dist.mjs && tsc -p tsconfig.json && tsc -p tsconfig.cjs.json && node ./scripts/write-cjs-package.mjs",
|
|
90
|
+
"prepublishOnly": "npm run build && npm run test:unit",
|
|
90
91
|
"generate:daml-interface": "node ./dist/daml-interface/cli/daml-interface-cli-main.js",
|
|
91
92
|
"generate:grpc": "node ./scripts/generate-grpc-bindings.mjs",
|
|
92
93
|
"examples:check": "npm run build && tsc -p tsconfig.examples.json --noEmit",
|