@distrohelena/canton-explorer 0.1.0 → 0.1.2
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/README.md +26 -0
- package/config/nodes.example.json +5 -1
- package/dist/public/assets/DebuggerView-Cf8sZ-NE.js +2 -0
- package/dist/public/assets/css.worker-BKx7BC4h.js +89 -0
- package/dist/public/assets/editor-C5uazxST.css +1 -0
- package/dist/public/assets/editor.api-8w-mShFJ.js +820 -0
- package/dist/public/assets/editor.worker-DQJwLooG.js +26 -0
- package/dist/public/assets/html.worker-ChRLLxCa.js +502 -0
- package/dist/public/assets/index-C6ucHbtz.js +1 -0
- package/dist/public/assets/index-D9pVnHtz.css +1 -0
- package/dist/public/assets/json.worker-DR52qCex.js +58 -0
- package/dist/public/assets/ts.worker-Cqvg7802.js +67734 -0
- package/dist/public/index.html +2 -2
- package/dist/src/api/debugger.controller.d.ts +16 -0
- package/dist/src/api/debugger.controller.js +105 -0
- package/dist/src/api/debugger.controller.js.map +1 -0
- package/dist/src/app-bootstrap.d.ts +1 -1
- package/dist/src/app-bootstrap.js +6 -2
- package/dist/src/app-bootstrap.js.map +1 -1
- package/dist/src/app.module.js +4 -1
- package/dist/src/app.module.js.map +1 -1
- package/dist/src/config/node-config.schema.d.ts +11 -0
- package/dist/src/config/node-config.schema.js +8 -0
- package/dist/src/config/node-config.schema.js.map +1 -1
- package/dist/src/config/node-config.service.d.ts +2 -1
- package/dist/src/config/node-config.service.js +3 -0
- package/dist/src/config/node-config.service.js.map +1 -1
- package/dist/src/debugger/debugger.service.d.ts +158 -0
- package/dist/src/debugger/debugger.service.js +746 -0
- package/dist/src/debugger/debugger.service.js.map +1 -0
- package/dist/src/domain/node.types.d.ts +1 -1
- package/dist/src/packages/daml-decoder.types.d.ts +8 -1
- package/dist/src/packages/daml-value-decoder.service.js.map +1 -1
- package/dist/src/packages/package-cache.service.js +103 -7
- package/dist/src/packages/package-cache.service.js.map +1 -1
- package/dist/src/packages/package-registry.service.js.map +1 -1
- package/dist/src/packages/package-sync.service.js +31 -3
- package/dist/src/packages/package-sync.service.js.map +1 -1
- package/dist/src/packages/pqs-package.service.js +15 -80
- package/dist/src/packages/pqs-package.service.js.map +1 -1
- package/dist/src/pqs/pqs-schema.d.ts +3 -0
- package/dist/src/pqs/pqs-schema.js +32 -0
- package/dist/src/pqs/pqs-schema.js.map +1 -0
- package/dist/src/pqs/pqs-summary.service.d.ts +3 -3
- package/dist/src/pqs/pqs-summary.service.js +619 -1414
- package/dist/src/pqs/pqs-summary.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +35 -31
- package/dist/public/assets/index-NfJYgVwb.css +0 -1
- package/dist/public/assets/index-ybPCCjei.js +0 -1
|
@@ -0,0 +1,746 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.DebuggerService = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const node_fs_1 = require("node:fs");
|
|
18
|
+
const node_path_1 = require("node:path");
|
|
19
|
+
const grpc_client_factory_1 = require("../grpc/grpc-client.factory");
|
|
20
|
+
const node_config_service_1 = require("../config/node-config.service");
|
|
21
|
+
const package_cache_service_1 = require("../packages/package-cache.service");
|
|
22
|
+
const package_sync_service_1 = require("../packages/package-sync.service");
|
|
23
|
+
const pqs_summary_service_1 = require("../pqs/pqs-summary.service");
|
|
24
|
+
let DebuggerService = class DebuggerService {
|
|
25
|
+
grpcClientFactory;
|
|
26
|
+
packageCacheService;
|
|
27
|
+
packageSyncService;
|
|
28
|
+
pqsSummaryService;
|
|
29
|
+
nodeConfigService;
|
|
30
|
+
sdkModulesPromise;
|
|
31
|
+
sessionStore = null;
|
|
32
|
+
sessionArtifacts = new Map();
|
|
33
|
+
constructor(grpcClientFactory, packageCacheService, packageSyncService, pqsSummaryService, nodeConfigService) {
|
|
34
|
+
this.grpcClientFactory = grpcClientFactory;
|
|
35
|
+
this.packageCacheService = packageCacheService;
|
|
36
|
+
this.packageSyncService = packageSyncService;
|
|
37
|
+
this.pqsSummaryService = pqsSummaryService;
|
|
38
|
+
this.nodeConfigService = nodeConfigService;
|
|
39
|
+
}
|
|
40
|
+
async createSession(nodeId, offset) {
|
|
41
|
+
const normalizedNodeId = nodeId.trim();
|
|
42
|
+
const normalizedOffset = offset.trim();
|
|
43
|
+
if (!normalizedNodeId) {
|
|
44
|
+
throw new common_1.BadRequestException('nodeId is required');
|
|
45
|
+
}
|
|
46
|
+
if (!normalizedOffset) {
|
|
47
|
+
throw new common_1.BadRequestException('offset is required');
|
|
48
|
+
}
|
|
49
|
+
const node = this.getNodeConfig(normalizedNodeId);
|
|
50
|
+
if (node.mode !== 'pqs_with_grpc') {
|
|
51
|
+
throw new common_1.BadRequestException(`Node ${node.id} does not support debugger replay without gRPC`);
|
|
52
|
+
}
|
|
53
|
+
const { debuggerSdk, damlLfSdk } = await this.loadSdkModules();
|
|
54
|
+
const sessionStore = await this.getSessionStore();
|
|
55
|
+
const client = (await this.grpcClientFactory.create(node));
|
|
56
|
+
const packageReadService = await this.createPackageReadService(client);
|
|
57
|
+
let stage = 'bootstrap';
|
|
58
|
+
try {
|
|
59
|
+
stage = 'load-update-detail';
|
|
60
|
+
const updateDetail = await this.pqsSummaryService.fetchUpdateDetail(node, normalizedOffset);
|
|
61
|
+
stage = 'resolve-replay-parties';
|
|
62
|
+
const replayAccess = await this.resolveReplayAccess(node, client, updateDetail);
|
|
63
|
+
stage = 'load-update';
|
|
64
|
+
const updateLoader = new debuggerSdk.ReplayUpdateLoader({
|
|
65
|
+
updateService: client.updateService,
|
|
66
|
+
visibleParties: replayAccess.updateVisibleParties,
|
|
67
|
+
});
|
|
68
|
+
const snapshot = await updateLoader.loadOrThrowAsync(normalizedOffset);
|
|
69
|
+
stage = 'build-environment';
|
|
70
|
+
const environmentBuilder = new debuggerSdk.LedgerReplayEnvironmentBuilder({
|
|
71
|
+
contractService: this.createReplayContractService(node, client),
|
|
72
|
+
eventQueryService: this.createReplayEventQueryService(node, client),
|
|
73
|
+
queryingParties: replayAccess.queryParties,
|
|
74
|
+
});
|
|
75
|
+
const environment = await environmentBuilder.buildOrThrowAsync(snapshot);
|
|
76
|
+
stage = 'resolve-artifacts';
|
|
77
|
+
const artifactResolver = new debuggerSdk.ReplayArtifactResolver({
|
|
78
|
+
participantPackageService: packageReadService,
|
|
79
|
+
});
|
|
80
|
+
const resolvedArtifacts = await artifactResolver.resolveAsync(environment.packageIds);
|
|
81
|
+
stage = 'sync-packages';
|
|
82
|
+
await this.packageSyncService.syncPackagesById(node, [...resolvedArtifacts.packageIds]);
|
|
83
|
+
stage = 'load-compilation';
|
|
84
|
+
const compilation = this.loadCompilationOrThrow(damlLfSdk, resolvedArtifacts.packageIds);
|
|
85
|
+
stage = 'load-source-bundles';
|
|
86
|
+
const sourceBundles = await this.loadSourceBundles(packageReadService, resolvedArtifacts.dars, damlLfSdk);
|
|
87
|
+
stage = 'index-sources';
|
|
88
|
+
const indexedCompilation = debuggerSdk.SourceIndexedCompilation.createOrThrow(compilation, sourceBundles);
|
|
89
|
+
stage = 'load-session';
|
|
90
|
+
const sessionLoader = new debuggerSdk.LedgerReplaySessionLoader({
|
|
91
|
+
updateLoader: {
|
|
92
|
+
loadOrThrowAsync: async () => snapshot,
|
|
93
|
+
},
|
|
94
|
+
environmentBuilder: {
|
|
95
|
+
buildOrThrowAsync: async () => environment,
|
|
96
|
+
},
|
|
97
|
+
definitionResolver: new debuggerSdk.ReplayEntrypointDefinitionResolver(indexedCompilation),
|
|
98
|
+
sourceMapper: new debuggerSdk.DamlSourceMapper(indexedCompilation),
|
|
99
|
+
evaluator: new damlLfSdk.DamlLfEvaluator(compilation),
|
|
100
|
+
determinismValidator: new debuggerSdk.ReplayDeterminismValidator(),
|
|
101
|
+
});
|
|
102
|
+
const debuggerClient = new debuggerSdk.LedgerReplayDebuggerClient({
|
|
103
|
+
sessionLoader,
|
|
104
|
+
sessionStore: sessionStore,
|
|
105
|
+
});
|
|
106
|
+
stage = 'create-session';
|
|
107
|
+
const session = await debuggerClient.loadSessionAsync(new debuggerSdk.ReplaySessionRequest({
|
|
108
|
+
offset: normalizedOffset,
|
|
109
|
+
}));
|
|
110
|
+
if (!session.sessionId) {
|
|
111
|
+
throw new Error('Replay session did not return a session id');
|
|
112
|
+
}
|
|
113
|
+
this.sessionArtifacts.set(session.sessionId, {
|
|
114
|
+
nodeId: node.id,
|
|
115
|
+
updateId: snapshot.updateId ?? null,
|
|
116
|
+
offset: snapshot.offset,
|
|
117
|
+
sourceFilesByPath: this.flattenSourceFiles(sourceBundles),
|
|
118
|
+
realEvents: updateDetail.events ?? [],
|
|
119
|
+
});
|
|
120
|
+
return this.getSession(session.sessionId);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
console.error('[DebuggerService] createSession failed at stage:', stage, error);
|
|
124
|
+
const message = error instanceof Error ? error.message : 'Debugger session bootstrap failed';
|
|
125
|
+
if (message.includes('debug/source-map.json')) {
|
|
126
|
+
throw new common_1.BadRequestException('Debugger requires DAR source maps, but the relevant DAR does not contain debug/source-map.json.');
|
|
127
|
+
}
|
|
128
|
+
if (message.includes('UPDATE_NOT_FOUND')) {
|
|
129
|
+
throw new common_1.NotFoundException('Update not found on this node, or not visible to the configured ledger user.');
|
|
130
|
+
}
|
|
131
|
+
throw new common_1.InternalServerErrorException(message);
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
await client.disposeAsync?.();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
getSession(sessionId) {
|
|
138
|
+
const sessionStore = this.getSessionStoreSync();
|
|
139
|
+
const artifacts = this.sessionArtifacts.get(sessionId);
|
|
140
|
+
if (!artifacts) {
|
|
141
|
+
throw new common_1.NotFoundException(`Unknown debugger session: ${sessionId}`);
|
|
142
|
+
}
|
|
143
|
+
const metadata = sessionStore.getSessionMetadataOrThrow(sessionId);
|
|
144
|
+
const currentStep = sessionStore.getCurrentStepOrThrow(sessionId);
|
|
145
|
+
const sourceLocation = currentStep.sourceLocation;
|
|
146
|
+
const sourcePath = sourceLocation?.path ?? null;
|
|
147
|
+
const sourceContent = sourcePath ? artifacts.sourceFilesByPath.get(sourcePath) ?? null : null;
|
|
148
|
+
return {
|
|
149
|
+
sessionId,
|
|
150
|
+
nodeId: artifacts.nodeId,
|
|
151
|
+
updateId: artifacts.updateId,
|
|
152
|
+
offset: artifacts.offset,
|
|
153
|
+
stepCount: metadata.stepCount ?? 0,
|
|
154
|
+
currentStepIndex: currentStep.stepIndex,
|
|
155
|
+
isTerminal: typeof metadata.stepCount === 'number'
|
|
156
|
+
? currentStep.stepIndex >= Math.max(0, metadata.stepCount - 1)
|
|
157
|
+
: false,
|
|
158
|
+
currentStep: this.mapStep(currentStep),
|
|
159
|
+
source: sourcePath && sourceContent !== null
|
|
160
|
+
? {
|
|
161
|
+
path: sourcePath,
|
|
162
|
+
content: sourceContent,
|
|
163
|
+
startLine: sourceLocation?.startLine ?? null,
|
|
164
|
+
startColumn: sourceLocation?.startColumn ?? null,
|
|
165
|
+
endLine: sourceLocation?.endLine ?? null,
|
|
166
|
+
endColumn: sourceLocation?.endColumn ?? null,
|
|
167
|
+
}
|
|
168
|
+
: null,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
listEvents(sessionId) {
|
|
172
|
+
const sessionStore = this.getSessionStoreSync();
|
|
173
|
+
const metadata = sessionStore.getSessionMetadataOrThrow(sessionId);
|
|
174
|
+
const currentStep = sessionStore.getCurrentStepOrThrow(sessionId);
|
|
175
|
+
const artifacts = this.sessionArtifacts.get(sessionId);
|
|
176
|
+
if (!artifacts) {
|
|
177
|
+
throw new common_1.NotFoundException(`Unknown debugger session: ${sessionId}`);
|
|
178
|
+
}
|
|
179
|
+
const trace = sessionStore.getTraceSliceOrThrow(sessionId, 0, metadata.stepCount ?? 0);
|
|
180
|
+
return {
|
|
181
|
+
sessionId,
|
|
182
|
+
currentStepId: currentStep.stepId ?? null,
|
|
183
|
+
realEvents: artifacts.realEvents.map((event) => this.mapRealEvent(event)),
|
|
184
|
+
replayEvents: trace
|
|
185
|
+
.filter((step) => step.stateDelta !== undefined)
|
|
186
|
+
.map((step) => this.mapEvent(step)),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
stepInto(sessionId) {
|
|
190
|
+
this.getSessionStoreSync().advanceIntoOrThrow(sessionId);
|
|
191
|
+
return this.getSession(sessionId);
|
|
192
|
+
}
|
|
193
|
+
stepBack(sessionId) {
|
|
194
|
+
this.getSessionStoreSync().advanceBackOrThrow(sessionId);
|
|
195
|
+
return this.getSession(sessionId);
|
|
196
|
+
}
|
|
197
|
+
stepOver(sessionId) {
|
|
198
|
+
this.getSessionStoreSync().advanceOverOrThrow(sessionId);
|
|
199
|
+
return this.getSession(sessionId);
|
|
200
|
+
}
|
|
201
|
+
stepOut(sessionId) {
|
|
202
|
+
this.getSessionStoreSync().advanceOutOrThrow(sessionId);
|
|
203
|
+
return this.getSession(sessionId);
|
|
204
|
+
}
|
|
205
|
+
continue(sessionId) {
|
|
206
|
+
this.getSessionStoreSync().continueOrThrow(sessionId);
|
|
207
|
+
return this.getSession(sessionId);
|
|
208
|
+
}
|
|
209
|
+
jumpToStep(sessionId, stepId) {
|
|
210
|
+
this.getSessionStoreSync().setCurrentStepByIdOrThrow(sessionId, stepId);
|
|
211
|
+
return this.getSession(sessionId);
|
|
212
|
+
}
|
|
213
|
+
disposeSession(sessionId) {
|
|
214
|
+
this.getSessionStoreSync().dispose(sessionId);
|
|
215
|
+
this.sessionArtifacts.delete(sessionId);
|
|
216
|
+
}
|
|
217
|
+
async loadSdkModules() {
|
|
218
|
+
if (!this.sdkModulesPromise) {
|
|
219
|
+
this.sdkModulesPromise = Promise.all([
|
|
220
|
+
import('@distrohelena/canton-typescript-sdk'),
|
|
221
|
+
import('@distrohelena/canton-typescript-sdk/debugger'),
|
|
222
|
+
import('@distrohelena/canton-typescript-sdk/daml-lf'),
|
|
223
|
+
]).then(([rootSdk, debuggerSdk, damlLfSdk]) => ({ rootSdk, debuggerSdk, damlLfSdk }));
|
|
224
|
+
}
|
|
225
|
+
return this.sdkModulesPromise;
|
|
226
|
+
}
|
|
227
|
+
async getSessionStore() {
|
|
228
|
+
if (!this.sessionStore) {
|
|
229
|
+
const { debuggerSdk } = await this.loadSdkModules();
|
|
230
|
+
this.sessionStore = new debuggerSdk.InMemoryReplaySessionStore();
|
|
231
|
+
}
|
|
232
|
+
return this.sessionStore;
|
|
233
|
+
}
|
|
234
|
+
getSessionStoreSync() {
|
|
235
|
+
if (!this.sessionStore) {
|
|
236
|
+
throw new common_1.NotFoundException('Debugger session store is not initialized');
|
|
237
|
+
}
|
|
238
|
+
return this.sessionStore;
|
|
239
|
+
}
|
|
240
|
+
getNodeConfig(id) {
|
|
241
|
+
const node = this.nodeConfigService?.list().find((candidate) => candidate.id === id);
|
|
242
|
+
if (!node) {
|
|
243
|
+
throw new common_1.NotFoundException(`Unknown node: ${id}`);
|
|
244
|
+
}
|
|
245
|
+
return node;
|
|
246
|
+
}
|
|
247
|
+
loadCompilationOrThrow(damlLfSdk, packageIds) {
|
|
248
|
+
const packageLoader = new damlLfSdk.DamlLfPackageLoader();
|
|
249
|
+
const packages = packageIds.map((packageId) => {
|
|
250
|
+
const cachedPackage = this.packageCacheService.getPackage(packageId);
|
|
251
|
+
if (!cachedPackage) {
|
|
252
|
+
throw new common_1.NotFoundException(`Missing cached package for debugger replay: ${packageId}`);
|
|
253
|
+
}
|
|
254
|
+
return packageLoader.loadPackageOrThrow(cachedPackage.data);
|
|
255
|
+
});
|
|
256
|
+
return damlLfSdk.DamlLfCompilation.createOrThrow(new damlLfSdk.DamlLfWorkspace(packages));
|
|
257
|
+
}
|
|
258
|
+
async loadSourceBundles(packageReadService, dars, damlLfSdk) {
|
|
259
|
+
const { rootSdk } = await this.loadSdkModules();
|
|
260
|
+
const sourceBundleLoader = new damlLfSdk.DarSourceBundleLoader();
|
|
261
|
+
const sourceBundles = [];
|
|
262
|
+
for (const dar of dars) {
|
|
263
|
+
const response = await packageReadService.getDarAsync(new rootSdk.GetDarRequest({
|
|
264
|
+
mainPackageId: dar.main,
|
|
265
|
+
}));
|
|
266
|
+
sourceBundles.push(await sourceBundleLoader.loadSourceBundleOrThrowAsync(response.payload));
|
|
267
|
+
}
|
|
268
|
+
return sourceBundles;
|
|
269
|
+
}
|
|
270
|
+
flattenSourceFiles(sourceBundles) {
|
|
271
|
+
const sourceFiles = new Map();
|
|
272
|
+
for (const bundle of sourceBundles) {
|
|
273
|
+
for (const sourceFile of bundle.sourceFiles) {
|
|
274
|
+
if (!sourceFiles.has(sourceFile.path)) {
|
|
275
|
+
sourceFiles.set(sourceFile.path, sourceFile.content);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return sourceFiles;
|
|
280
|
+
}
|
|
281
|
+
createReplayContractService(node, client) {
|
|
282
|
+
return {
|
|
283
|
+
getContractAsync: async (request) => {
|
|
284
|
+
try {
|
|
285
|
+
return await client.contractService.getContractAsync(request);
|
|
286
|
+
}
|
|
287
|
+
catch (error) {
|
|
288
|
+
const fallback = await this.buildPqsReplayCreatedEvent(node, request);
|
|
289
|
+
if (fallback) {
|
|
290
|
+
return {
|
|
291
|
+
createdEvent: fallback,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
throw error;
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
createReplayEventQueryService(node, client) {
|
|
300
|
+
return {
|
|
301
|
+
getEventsByContractIdAsync: async (request) => {
|
|
302
|
+
try {
|
|
303
|
+
return await client.eventQueryService.getEventsByContractIdAsync(request);
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
const fallback = await this.buildPqsReplayCreatedEvent(node, request);
|
|
307
|
+
if (fallback) {
|
|
308
|
+
return {
|
|
309
|
+
created: {
|
|
310
|
+
createdEvent: fallback,
|
|
311
|
+
synchronizerId: 'pqs-fallback',
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
throw error;
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
mapStep(step) {
|
|
321
|
+
return {
|
|
322
|
+
stepId: step.stepId ?? null,
|
|
323
|
+
stepIndex: step.stepIndex,
|
|
324
|
+
phase: step.phase,
|
|
325
|
+
stackFrames: (step.stackFrames ?? []).map((frame) => ({
|
|
326
|
+
frameId: frame.frameId ?? null,
|
|
327
|
+
name: frame.name ?? null,
|
|
328
|
+
})),
|
|
329
|
+
scopes: (step.scopes ?? []).map((scope) => this.mapScope(scope)),
|
|
330
|
+
locals: [...(step.locals ?? [])],
|
|
331
|
+
arguments: [...(step.arguments ?? [])],
|
|
332
|
+
sourceLocation: this.mapSourceLocation(step.sourceLocation),
|
|
333
|
+
valuePreview: step.valuePreview
|
|
334
|
+
? {
|
|
335
|
+
kind: step.valuePreview.kind,
|
|
336
|
+
display: step.valuePreview.display,
|
|
337
|
+
}
|
|
338
|
+
: null,
|
|
339
|
+
stateDelta: this.mapStateDelta(step.stateDelta),
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
mapEvent(step) {
|
|
343
|
+
const stateDelta = this.mapStateDelta(step.stateDelta);
|
|
344
|
+
return {
|
|
345
|
+
stepId: step.stepId ?? null,
|
|
346
|
+
stepIndex: step.stepIndex,
|
|
347
|
+
phase: step.phase,
|
|
348
|
+
kind: stateDelta?.kind ?? null,
|
|
349
|
+
eventOrdinal: stateDelta?.eventOrdinal ?? null,
|
|
350
|
+
comparisonKey: stateDelta?.comparisonKey ?? null,
|
|
351
|
+
createdContractId: stateDelta?.createdContractId ?? null,
|
|
352
|
+
targetContractId: stateDelta?.targetContractId ?? null,
|
|
353
|
+
templateId: stateDelta?.templateId ?? null,
|
|
354
|
+
choice: stateDelta?.choice ?? null,
|
|
355
|
+
choiceArgument: stateDelta?.choiceArgument ?? null,
|
|
356
|
+
payload: stateDelta?.payload ?? null,
|
|
357
|
+
consuming: stateDelta?.consuming ?? null,
|
|
358
|
+
sourceLocation: this.mapSourceLocation(step.sourceLocation),
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
mapRealEvent(event) {
|
|
362
|
+
return {
|
|
363
|
+
eventId: event.eventId ?? null,
|
|
364
|
+
eventKind: event.eventKind,
|
|
365
|
+
contractId: event.contractId ?? null,
|
|
366
|
+
packageId: event.packageId ?? null,
|
|
367
|
+
templateId: event.templateId ?? null,
|
|
368
|
+
choice: event.choice ?? null,
|
|
369
|
+
witnesses: [...(event.witnesses ?? [])],
|
|
370
|
+
createData: event.createData?.status === 'decoded' ? event.createData.value : null,
|
|
371
|
+
exerciseArgument: event.exerciseData?.argument.status === 'decoded'
|
|
372
|
+
? event.exerciseData.argument.value
|
|
373
|
+
: null,
|
|
374
|
+
exerciseResult: event.exerciseData?.result.status === 'decoded'
|
|
375
|
+
? event.exerciseData.result.value
|
|
376
|
+
: null,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
mapScope(scope) {
|
|
380
|
+
return {
|
|
381
|
+
frameId: scope.frameId ?? null,
|
|
382
|
+
name: scope.name ?? null,
|
|
383
|
+
variables: (scope.variables ?? []).map((variable) => ({
|
|
384
|
+
name: variable.name ?? null,
|
|
385
|
+
kind: variable.kind ?? null,
|
|
386
|
+
value: variable.value ?? null,
|
|
387
|
+
})),
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
mapStateDelta(stateDelta) {
|
|
391
|
+
if (!stateDelta) {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
return {
|
|
395
|
+
kind: stateDelta.kind ?? null,
|
|
396
|
+
eventOrdinal: stateDelta.eventOrdinal ?? null,
|
|
397
|
+
comparisonKey: stateDelta.comparisonKey ?? null,
|
|
398
|
+
createdContractId: stateDelta.createdContractId ?? null,
|
|
399
|
+
targetContractId: stateDelta.targetContractId ?? null,
|
|
400
|
+
templateId: stateDelta.templateId
|
|
401
|
+
? {
|
|
402
|
+
packageId: stateDelta.templateId.packageId ?? null,
|
|
403
|
+
moduleName: stateDelta.templateId.moduleName ?? null,
|
|
404
|
+
entityName: stateDelta.templateId.entityName ?? null,
|
|
405
|
+
}
|
|
406
|
+
: null,
|
|
407
|
+
choice: stateDelta.choice ?? null,
|
|
408
|
+
choiceArgument: stateDelta.choiceArgument ?? null,
|
|
409
|
+
payload: stateDelta.payload ?? null,
|
|
410
|
+
consuming: stateDelta.consuming ?? null,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
mapSourceLocation(sourceLocation) {
|
|
414
|
+
return sourceLocation
|
|
415
|
+
? {
|
|
416
|
+
path: sourceLocation.path ?? null,
|
|
417
|
+
startLine: sourceLocation.startLine ?? null,
|
|
418
|
+
startColumn: sourceLocation.startColumn ?? null,
|
|
419
|
+
endLine: sourceLocation.endLine ?? null,
|
|
420
|
+
endColumn: sourceLocation.endColumn ?? null,
|
|
421
|
+
}
|
|
422
|
+
: null;
|
|
423
|
+
}
|
|
424
|
+
async buildPqsReplayCreatedEvent(node, request) {
|
|
425
|
+
const contractId = request && typeof request === 'object' && 'contractId' in request
|
|
426
|
+
? request.contractId
|
|
427
|
+
: undefined;
|
|
428
|
+
if (typeof contractId !== 'string' || contractId.length === 0) {
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
let contractDetail;
|
|
432
|
+
try {
|
|
433
|
+
contractDetail = await this.pqsSummaryService.fetchContractDetail(node, contractId);
|
|
434
|
+
}
|
|
435
|
+
catch {
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
if (contractDetail.contractData?.status !== 'decoded') {
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
const templateId = this.parseTemplateIdentifier(contractDetail.packageId, contractDetail.templateId);
|
|
442
|
+
if (!templateId) {
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
return {
|
|
446
|
+
contractId,
|
|
447
|
+
templateId,
|
|
448
|
+
createArguments: this.toReplayLedgerValue(contractDetail.contractData),
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
parseTemplateIdentifier(packageId, templateId) {
|
|
452
|
+
if (!packageId || !templateId) {
|
|
453
|
+
return null;
|
|
454
|
+
}
|
|
455
|
+
const separatorIndex = templateId.lastIndexOf(':');
|
|
456
|
+
if (separatorIndex <= 0 || separatorIndex >= templateId.length - 1) {
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
return {
|
|
460
|
+
packageId,
|
|
461
|
+
moduleName: templateId.slice(0, separatorIndex),
|
|
462
|
+
entityName: templateId.slice(separatorIndex + 1),
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
toReplayLedgerValue(state) {
|
|
466
|
+
if (state.status !== 'decoded') {
|
|
467
|
+
return undefined;
|
|
468
|
+
}
|
|
469
|
+
return this.decodeNodeValue(state.value);
|
|
470
|
+
}
|
|
471
|
+
decodeNodeValue(value) {
|
|
472
|
+
if (typeof value === 'string'
|
|
473
|
+
|| typeof value === 'number'
|
|
474
|
+
|| typeof value === 'boolean') {
|
|
475
|
+
return value;
|
|
476
|
+
}
|
|
477
|
+
switch (value.kind) {
|
|
478
|
+
case 'contract_id':
|
|
479
|
+
return {
|
|
480
|
+
sum: {
|
|
481
|
+
oneofKind: 'contractId',
|
|
482
|
+
contractId: value.value,
|
|
483
|
+
},
|
|
484
|
+
};
|
|
485
|
+
case 'record':
|
|
486
|
+
return {
|
|
487
|
+
fields: value.fields.map((field) => ({
|
|
488
|
+
label: field.label,
|
|
489
|
+
value: this.decodeNodeValue(field.value),
|
|
490
|
+
})),
|
|
491
|
+
};
|
|
492
|
+
case 'variant':
|
|
493
|
+
return {
|
|
494
|
+
sum: {
|
|
495
|
+
oneofKind: 'variant',
|
|
496
|
+
variant: {
|
|
497
|
+
constructor: value.constructor,
|
|
498
|
+
value: value.value === null ? undefined : this.decodeNodeValue(value.value),
|
|
499
|
+
},
|
|
500
|
+
},
|
|
501
|
+
};
|
|
502
|
+
case 'enum':
|
|
503
|
+
return {
|
|
504
|
+
sum: {
|
|
505
|
+
oneofKind: 'enum',
|
|
506
|
+
enum: {
|
|
507
|
+
constructor: value.constructor,
|
|
508
|
+
},
|
|
509
|
+
},
|
|
510
|
+
};
|
|
511
|
+
case 'list':
|
|
512
|
+
return {
|
|
513
|
+
sum: {
|
|
514
|
+
oneofKind: 'list',
|
|
515
|
+
list: {
|
|
516
|
+
elements: value.items.map((item) => this.decodeNodeValue(item)),
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
};
|
|
520
|
+
case 'optional':
|
|
521
|
+
return {
|
|
522
|
+
sum: {
|
|
523
|
+
oneofKind: 'optional',
|
|
524
|
+
optional: value.value === null
|
|
525
|
+
? {}
|
|
526
|
+
: {
|
|
527
|
+
value: this.decodeNodeValue(value.value),
|
|
528
|
+
},
|
|
529
|
+
},
|
|
530
|
+
};
|
|
531
|
+
case 'text_map':
|
|
532
|
+
return {
|
|
533
|
+
sum: {
|
|
534
|
+
oneofKind: 'textMap',
|
|
535
|
+
textMap: {
|
|
536
|
+
entries: value.entries.map((entry) => ({
|
|
537
|
+
key: entry.key,
|
|
538
|
+
value: this.decodeNodeValue(entry.value),
|
|
539
|
+
})),
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
};
|
|
543
|
+
case 'gen_map':
|
|
544
|
+
return {
|
|
545
|
+
sum: {
|
|
546
|
+
oneofKind: 'genMap',
|
|
547
|
+
genMap: {
|
|
548
|
+
entries: value.entries.map((entry) => ({
|
|
549
|
+
key: this.decodeNodeValue(entry.key),
|
|
550
|
+
value: this.decodeNodeValue(entry.value),
|
|
551
|
+
})),
|
|
552
|
+
},
|
|
553
|
+
},
|
|
554
|
+
};
|
|
555
|
+
case 'unit':
|
|
556
|
+
return {
|
|
557
|
+
sum: {
|
|
558
|
+
oneofKind: 'unit',
|
|
559
|
+
},
|
|
560
|
+
};
|
|
561
|
+
default:
|
|
562
|
+
return undefined;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
collectVisibleParties(updateDetail) {
|
|
566
|
+
return [
|
|
567
|
+
...new Set([
|
|
568
|
+
...(updateDetail.parties ?? []),
|
|
569
|
+
...updateDetail.events.flatMap((event) => event.witnesses ?? []),
|
|
570
|
+
]),
|
|
571
|
+
].filter((party) => typeof party === 'string' && party.length > 0);
|
|
572
|
+
}
|
|
573
|
+
async resolveReplayAccess(node, client, updateDetail) {
|
|
574
|
+
const grantedAccess = await this.fetchGrantedReplayAccess(node, client);
|
|
575
|
+
const detailParties = this.collectVisibleParties(updateDetail);
|
|
576
|
+
if (grantedAccess.canReadAsAnyParty) {
|
|
577
|
+
return {
|
|
578
|
+
updateVisibleParties: undefined,
|
|
579
|
+
queryParties: detailParties.length > 0 ? detailParties : undefined,
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
const grantedParties = grantedAccess.parties;
|
|
583
|
+
if (grantedParties.length === 0) {
|
|
584
|
+
return {
|
|
585
|
+
updateVisibleParties: detailParties.length > 0 ? detailParties : undefined,
|
|
586
|
+
queryParties: detailParties.length > 0 ? detailParties : undefined,
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
if (detailParties.length === 0) {
|
|
590
|
+
return {
|
|
591
|
+
updateVisibleParties: grantedParties,
|
|
592
|
+
queryParties: grantedParties,
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
const grantedPartySet = new Set(grantedParties);
|
|
596
|
+
const overlappingParties = detailParties.filter((party) => grantedPartySet.has(party));
|
|
597
|
+
const parties = overlappingParties.length > 0 ? overlappingParties : grantedParties;
|
|
598
|
+
return {
|
|
599
|
+
updateVisibleParties: parties,
|
|
600
|
+
queryParties: parties,
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
async fetchGrantedReplayAccess(node, client) {
|
|
604
|
+
const authConfig = node.grpc.auth;
|
|
605
|
+
if (!authConfig || authConfig.kind !== 'shared_secret_jwt') {
|
|
606
|
+
return {
|
|
607
|
+
parties: [],
|
|
608
|
+
canReadAsAnyParty: false,
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
const response = await client.userManagementService.listUserRightsAsync({
|
|
612
|
+
userId: authConfig.user,
|
|
613
|
+
});
|
|
614
|
+
const rights = response.rights ?? [];
|
|
615
|
+
return {
|
|
616
|
+
canReadAsAnyParty: rights.some((right) => right?.type === 'canReadAsAnyParty'),
|
|
617
|
+
parties: [
|
|
618
|
+
...new Set(rights
|
|
619
|
+
.filter((right) => typeof right?.type === 'string'
|
|
620
|
+
&& (right.type === 'canActAs' || right.type === 'canReadAs')
|
|
621
|
+
&& typeof right.party === 'string'
|
|
622
|
+
&& right.party.length > 0)
|
|
623
|
+
.map((right) => right.party)),
|
|
624
|
+
],
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
async createPackageReadService(client) {
|
|
628
|
+
const localDebugDars = await this.loadLocalDebugDars();
|
|
629
|
+
if (localDebugDars.size === 0) {
|
|
630
|
+
return client.participantPackageService;
|
|
631
|
+
}
|
|
632
|
+
return {
|
|
633
|
+
getPackageReferencesAsync: async (request) => {
|
|
634
|
+
const packageId = this.extractPackageId(request);
|
|
635
|
+
const localDar = packageId ? localDebugDars.get(packageId) : undefined;
|
|
636
|
+
if (localDar) {
|
|
637
|
+
return {
|
|
638
|
+
dars: [{ main: localDar.mainPackageId }],
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
return client.participantPackageService.getPackageReferencesAsync(request);
|
|
642
|
+
},
|
|
643
|
+
getDarAsync: async (request) => {
|
|
644
|
+
const mainPackageId = this.extractMainPackageId(request);
|
|
645
|
+
const localDar = mainPackageId ? localDebugDars.get(mainPackageId) : undefined;
|
|
646
|
+
if (localDar) {
|
|
647
|
+
return {
|
|
648
|
+
payload: localDar.payload,
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
return client.participantPackageService.getDarAsync(request);
|
|
652
|
+
},
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
async loadLocalDebugDars() {
|
|
656
|
+
const directory = this.getLocalDebugDarDirectory();
|
|
657
|
+
const entries = new Map();
|
|
658
|
+
if (!directory || !this.pathExists(directory, 'directory')) {
|
|
659
|
+
return entries;
|
|
660
|
+
}
|
|
661
|
+
const { damlLfSdk } = await this.loadSdkModules();
|
|
662
|
+
const archiveLoader = new damlLfSdk.DarArchiveLoader();
|
|
663
|
+
const packageLoader = new damlLfSdk.DamlLfPackageLoader();
|
|
664
|
+
const sourceBundleLoader = new damlLfSdk.DarSourceBundleLoader();
|
|
665
|
+
for (const filePath of this.listDarFiles(directory)) {
|
|
666
|
+
try {
|
|
667
|
+
const payload = (0, node_fs_1.readFileSync)(filePath);
|
|
668
|
+
await sourceBundleLoader.loadSourceBundleOrThrowAsync(payload);
|
|
669
|
+
const archive = await archiveLoader.loadDarOrThrowAsync(payload);
|
|
670
|
+
const mainPackage = packageLoader.loadPackageOrThrow(archive.mainPackageEntry.bytes);
|
|
671
|
+
entries.set(mainPackage.packageId, {
|
|
672
|
+
mainPackageId: mainPackage.packageId,
|
|
673
|
+
payload,
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
catch (error) {
|
|
677
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
678
|
+
console.warn(`[DebuggerService] Skipping local debug DAR ${filePath}: ${message}`);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
return entries;
|
|
682
|
+
}
|
|
683
|
+
getLocalDebugDarDirectory() {
|
|
684
|
+
const configuredDirectory = this.nodeConfigService?.getDebuggerConfig().localDarDirectory?.trim();
|
|
685
|
+
if (configuredDirectory) {
|
|
686
|
+
return (0, node_path_1.resolve)(configuredDirectory);
|
|
687
|
+
}
|
|
688
|
+
const currentWorkingDirectoryPath = (0, node_path_1.resolve)(process.cwd(), 'debug-dars');
|
|
689
|
+
if (this.pathExists(currentWorkingDirectoryPath, 'directory')) {
|
|
690
|
+
return currentWorkingDirectoryPath;
|
|
691
|
+
}
|
|
692
|
+
const parentDirectoryPath = (0, node_path_1.resolve)(process.cwd(), '..', 'debug-dars');
|
|
693
|
+
if (this.pathExists(parentDirectoryPath, 'directory')) {
|
|
694
|
+
return parentDirectoryPath;
|
|
695
|
+
}
|
|
696
|
+
return currentWorkingDirectoryPath;
|
|
697
|
+
}
|
|
698
|
+
listDarFiles(rootDir) {
|
|
699
|
+
const files = [];
|
|
700
|
+
for (const entry of (0, node_fs_1.readdirSync)(rootDir, { withFileTypes: true })) {
|
|
701
|
+
const absolutePath = (0, node_path_1.resolve)(rootDir, entry.name);
|
|
702
|
+
if (entry.isDirectory()) {
|
|
703
|
+
files.push(...this.listDarFiles(absolutePath));
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
if (entry.isFile() && absolutePath.endsWith('.dar')) {
|
|
707
|
+
files.push(absolutePath);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
return files.sort();
|
|
711
|
+
}
|
|
712
|
+
pathExists(candidatePath, kind) {
|
|
713
|
+
try {
|
|
714
|
+
const stats = (0, node_fs_1.statSync)(candidatePath);
|
|
715
|
+
return kind === 'directory' ? stats.isDirectory() : stats.isFile();
|
|
716
|
+
}
|
|
717
|
+
catch {
|
|
718
|
+
return false;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
extractPackageId(request) {
|
|
722
|
+
if (!request || typeof request !== 'object') {
|
|
723
|
+
return null;
|
|
724
|
+
}
|
|
725
|
+
const candidate = request.packageId;
|
|
726
|
+
return typeof candidate === 'string' && candidate.length > 0 ? candidate : null;
|
|
727
|
+
}
|
|
728
|
+
extractMainPackageId(request) {
|
|
729
|
+
if (!request || typeof request !== 'object') {
|
|
730
|
+
return null;
|
|
731
|
+
}
|
|
732
|
+
const candidate = request.mainPackageId;
|
|
733
|
+
return typeof candidate === 'string' && candidate.length > 0 ? candidate : null;
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
exports.DebuggerService = DebuggerService;
|
|
737
|
+
exports.DebuggerService = DebuggerService = __decorate([
|
|
738
|
+
(0, common_1.Injectable)(),
|
|
739
|
+
__param(4, (0, common_1.Optional)()),
|
|
740
|
+
__metadata("design:paramtypes", [grpc_client_factory_1.GrpcClientFactory,
|
|
741
|
+
package_cache_service_1.PackageCacheService,
|
|
742
|
+
package_sync_service_1.PackageSyncService,
|
|
743
|
+
pqs_summary_service_1.PqsSummaryService,
|
|
744
|
+
node_config_service_1.NodeConfigService])
|
|
745
|
+
], DebuggerService);
|
|
746
|
+
//# sourceMappingURL=debugger.service.js.map
|