@fairmint/canton-node-sdk 0.0.222 → 0.0.224
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/bin/canton-localnet +182 -0
- package/build/src/generated/apps/common/src/main/openapi/common-external.d.ts +295 -0
- package/build/src/generated/apps/common/src/main/openapi/common-external.d.ts.map +1 -0
- package/build/src/generated/apps/common/src/main/openapi/common-external.js +7 -0
- package/build/src/generated/apps/common/src/main/openapi/common-external.js.map +1 -0
- package/build/src/generated/apps/common/src/main/openapi/common-internal.d.ts +204 -0
- package/build/src/generated/apps/common/src/main/openapi/common-internal.d.ts.map +1 -0
- package/build/src/generated/apps/common/src/main/openapi/common-internal.js +7 -0
- package/build/src/generated/apps/common/src/main/openapi/common-internal.js.map +1 -0
- package/build/src/generated/apps/scan/src/main/openapi/scan-stream-server.d.ts +73 -0
- package/build/src/generated/apps/scan/src/main/openapi/scan-stream-server.d.ts.map +1 -0
- package/build/src/generated/apps/scan/src/main/openapi/scan-stream-server.js +7 -0
- package/build/src/generated/apps/scan/src/main/openapi/scan-stream-server.js.map +1 -0
- package/build/src/generated/apps/scan/src/main/openapi/scan.d.ts +4969 -0
- package/build/src/generated/apps/scan/src/main/openapi/scan.d.ts.map +1 -0
- package/build/src/generated/apps/scan/src/main/openapi/scan.js +7 -0
- package/build/src/generated/apps/scan/src/main/openapi/scan.js.map +1 -0
- package/build/src/generated/apps/splitwell/src/main/openapi/splitwell-internal.d.ts +636 -0
- package/build/src/generated/apps/splitwell/src/main/openapi/splitwell-internal.d.ts.map +1 -0
- package/build/src/generated/apps/splitwell/src/main/openapi/splitwell-internal.js +7 -0
- package/build/src/generated/apps/splitwell/src/main/openapi/splitwell-internal.js.map +1 -0
- package/build/src/generated/apps/sv/src/main/openapi/sv-internal.d.ts +2024 -0
- package/build/src/generated/apps/sv/src/main/openapi/sv-internal.d.ts.map +1 -0
- package/build/src/generated/apps/sv/src/main/openapi/sv-internal.js +7 -0
- package/build/src/generated/apps/sv/src/main/openapi/sv-internal.js.map +1 -0
- package/build/src/generated/apps/validator/src/main/openapi/ans-external.d.ts +189 -0
- package/build/src/generated/apps/validator/src/main/openapi/ans-external.d.ts.map +1 -0
- package/build/src/generated/apps/validator/src/main/openapi/ans-external.js +7 -0
- package/build/src/generated/apps/validator/src/main/openapi/ans-external.js.map +1 -0
- package/build/src/generated/apps/validator/src/main/openapi/scan-proxy.d.ts +969 -0
- package/build/src/generated/apps/validator/src/main/openapi/scan-proxy.d.ts.map +1 -0
- package/build/src/generated/apps/validator/src/main/openapi/scan-proxy.js +7 -0
- package/build/src/generated/apps/validator/src/main/openapi/scan-proxy.js.map +1 -0
- package/build/src/generated/apps/validator/src/main/openapi/validator-internal.d.ts +1230 -0
- package/build/src/generated/apps/validator/src/main/openapi/validator-internal.d.ts.map +1 -0
- package/build/src/generated/apps/validator/src/main/openapi/validator-internal.js +7 -0
- package/build/src/generated/apps/validator/src/main/openapi/validator-internal.js.map +1 -0
- package/build/src/generated/apps/wallet/src/main/openapi/wallet-external.d.ts +535 -0
- package/build/src/generated/apps/wallet/src/main/openapi/wallet-external.d.ts.map +1 -0
- package/build/src/generated/apps/wallet/src/main/openapi/wallet-external.js +7 -0
- package/build/src/generated/apps/wallet/src/main/openapi/wallet-external.js.map +1 -0
- package/build/src/generated/apps/wallet/src/main/openapi/wallet-internal.d.ts +2521 -0
- package/build/src/generated/apps/wallet/src/main/openapi/wallet-internal.d.ts.map +1 -0
- package/build/src/generated/apps/wallet/src/main/openapi/wallet-internal.js +7 -0
- package/build/src/generated/apps/wallet/src/main/openapi/wallet-internal.js.map +1 -0
- package/package.json +18 -8
- package/scripts/localnet-cloud.sh +180 -17
- package/.gitmodules +0 -6
- package/.nvmrc +0 -1
- package/scripts/collapse-manifest.ts +0 -66
- package/scripts/generate-all-client-methods.ts +0 -332
- package/scripts/generate-openapi-types.ts +0 -46
- package/scripts/generate-operation-tests.ts +0 -198
- package/scripts/grant-user-rights.ts +0 -213
- package/scripts/list-identity-providers.ts +0 -46
- package/scripts/localnet/localnet-cloud.sh +0 -5
- package/scripts/localnet/localnet-status.sh +0 -5
- package/scripts/localnet/setup-localnet.sh +0 -5
- package/scripts/localnet/setup-quickstart-localnet.sh +0 -5
- package/scripts/localnet/start-localnet.sh +0 -5
- package/scripts/localnet/stop-localnet.sh +0 -5
- package/scripts/localnet-status.sh +0 -5
- package/scripts/prepare-release.ts +0 -222
- package/scripts/revoke-user-rights.ts +0 -219
- package/scripts/setup-localnet.sh +0 -5
- package/scripts/setup-quickstart-localnet.sh +0 -5
- package/scripts/start-localnet.sh +0 -5
- package/scripts/stop-localnet.sh +0 -5
|
@@ -1,332 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ts-node
|
|
2
|
-
|
|
3
|
-
import * as fs from 'fs';
|
|
4
|
-
import * as path from 'path';
|
|
5
|
-
|
|
6
|
-
interface ClientConfig {
|
|
7
|
-
name: string;
|
|
8
|
-
clientFile: string;
|
|
9
|
-
operationsDir: string;
|
|
10
|
-
baseClass: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const CLIENTS: ClientConfig[] = [
|
|
14
|
-
{
|
|
15
|
-
name: 'ValidatorApiClient',
|
|
16
|
-
clientFile: path.join(__dirname, '../src/clients/validator-api/ValidatorApiClient.generated.ts'),
|
|
17
|
-
operationsDir: path.join(__dirname, '../src/clients/validator-api/operations/v0'),
|
|
18
|
-
baseClass: 'BaseClient',
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
name: 'LedgerJsonApiClient',
|
|
22
|
-
clientFile: path.join(__dirname, '../src/clients/ledger-json-api/LedgerJsonApiClient.generated.ts'),
|
|
23
|
-
operationsDir: path.join(__dirname, '../src/clients/ledger-json-api/operations/v2'),
|
|
24
|
-
baseClass: 'BaseClient',
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
name: 'ScanApiClient',
|
|
28
|
-
clientFile: path.join(__dirname, '../src/clients/scan-api/ScanApiClient.generated.ts'),
|
|
29
|
-
operationsDir: path.join(__dirname, '../src/clients/scan-api/operations/v0'),
|
|
30
|
-
baseClass: 'BaseClient',
|
|
31
|
-
},
|
|
32
|
-
];
|
|
33
|
-
|
|
34
|
-
// Recursively get all .ts files in a directory
|
|
35
|
-
function getAllTsFiles(dir: string): string[] {
|
|
36
|
-
let results: string[] = [];
|
|
37
|
-
const list = fs.readdirSync(dir);
|
|
38
|
-
list.forEach((file) => {
|
|
39
|
-
const filePath = path.join(dir, file);
|
|
40
|
-
const stat = fs.statSync(filePath);
|
|
41
|
-
if (stat.isDirectory()) {
|
|
42
|
-
results = results.concat(getAllTsFiles(filePath));
|
|
43
|
-
} else if (file.endsWith('.ts')) {
|
|
44
|
-
results.push(filePath);
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
return results;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
type OperationInfo =
|
|
51
|
-
| {
|
|
52
|
-
kind: 'api';
|
|
53
|
-
operationName: string;
|
|
54
|
-
paramsType: string;
|
|
55
|
-
responseType: string;
|
|
56
|
-
methodName?: string | undefined;
|
|
57
|
-
jsdoc?: string | undefined;
|
|
58
|
-
}
|
|
59
|
-
| {
|
|
60
|
-
kind: 'ws';
|
|
61
|
-
operationName: string;
|
|
62
|
-
paramsType: string;
|
|
63
|
-
requestType: string;
|
|
64
|
-
messageType: string;
|
|
65
|
-
jsdoc?: string | undefined;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
/** Extracts JSDoc comment that directly precedes an export declaration (no code between them) */
|
|
69
|
-
function extractJsDoc(fileContent: string, operationName: string): string | undefined {
|
|
70
|
-
// Match JSDoc comment that is immediately before export const/class (only whitespace allowed between)
|
|
71
|
-
// Must not have any code between the JSDoc closing */ and the export
|
|
72
|
-
const jsdocRegex = new RegExp(`(/\\*\\*(?:(?!\\*/).)*.?\\*/)\\s*\\nexport (?:const|class) ${operationName}\\b`, 's');
|
|
73
|
-
const match = jsdocRegex.exec(fileContent);
|
|
74
|
-
const jsdoc = match?.[1];
|
|
75
|
-
|
|
76
|
-
// Validate it's actually a JSDoc (starts with /** and ends with */)
|
|
77
|
-
if (jsdoc && jsdoc.startsWith('/**') && jsdoc.includes('*/')) {
|
|
78
|
-
// Make sure it doesn't contain code-like patterns that indicate it's not a pure JSDoc
|
|
79
|
-
if (jsdoc.includes('z.string()') || jsdoc.includes('z.object(')) {
|
|
80
|
-
return undefined;
|
|
81
|
-
}
|
|
82
|
-
return jsdoc;
|
|
83
|
-
}
|
|
84
|
-
return undefined;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** Generate a human-readable description from an operation name */
|
|
88
|
-
function generateDescriptionFromName(operationName: string): string {
|
|
89
|
-
// Split camelCase/PascalCase into words
|
|
90
|
-
const words = operationName
|
|
91
|
-
.replace(/([A-Z])/g, ' $1')
|
|
92
|
-
.trim()
|
|
93
|
-
.toLowerCase()
|
|
94
|
-
.split(' ');
|
|
95
|
-
|
|
96
|
-
// Handle common prefixes
|
|
97
|
-
const prefix = words[0];
|
|
98
|
-
const rest = words.slice(1).join(' ');
|
|
99
|
-
|
|
100
|
-
if (prefix === 'get') return `Gets ${rest}`;
|
|
101
|
-
if (prefix === 'list') return `Lists ${rest}`;
|
|
102
|
-
if (prefix === 'create') return `Creates ${rest}`;
|
|
103
|
-
if (prefix === 'delete') return `Deletes ${rest}`;
|
|
104
|
-
if (prefix === 'update') return `Updates ${rest}`;
|
|
105
|
-
if (prefix === 'submit') return `Submits ${rest}`;
|
|
106
|
-
if (prefix === 'subscribe') return `Subscribes to ${rest}`;
|
|
107
|
-
if (prefix === 'allocate') return `Allocates ${rest}`;
|
|
108
|
-
if (prefix === 'lookup') return `Looks up ${rest}`;
|
|
109
|
-
if (prefix === 'revoke') return `Revokes ${rest}`;
|
|
110
|
-
if (prefix === 'grant') return `Grants ${rest}`;
|
|
111
|
-
|
|
112
|
-
// Default: just capitalize first letter
|
|
113
|
-
return (
|
|
114
|
-
operationName.charAt(0).toUpperCase() +
|
|
115
|
-
operationName
|
|
116
|
-
.slice(1)
|
|
117
|
-
.replace(/([A-Z])/g, ' $1')
|
|
118
|
-
.trim()
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Extract operation info from a file (supports REST, WebSocket, and class-based operations)
|
|
123
|
-
function extractOperationInfos(fileContent: string): OperationInfo[] {
|
|
124
|
-
const results: OperationInfo[] = [];
|
|
125
|
-
|
|
126
|
-
// REST operations: createApiOperation or createSimpleApiOperation
|
|
127
|
-
const apiRegex = /export const (\w+)\s*=\s*create(?:Simple)?ApiOperation<\s*([^,]+),\s*([^>]+)\s*>/gs;
|
|
128
|
-
for (const match of fileContent.matchAll(apiRegex)) {
|
|
129
|
-
const operationName = match[1];
|
|
130
|
-
const paramsType = match[2];
|
|
131
|
-
const responseType = match[3];
|
|
132
|
-
if (operationName && paramsType && responseType) {
|
|
133
|
-
results.push({
|
|
134
|
-
kind: 'api',
|
|
135
|
-
operationName,
|
|
136
|
-
paramsType: paramsType.trim(),
|
|
137
|
-
responseType: responseType.trim(),
|
|
138
|
-
jsdoc: extractJsDoc(fileContent, operationName),
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Class-based operations: export class OperationName { ... execute(...) }
|
|
144
|
-
const classRegex = /export class (\w+)[^{]*{[\s\S]*?public async (execute|connect)\(/s;
|
|
145
|
-
const classMatch = classRegex.exec(fileContent);
|
|
146
|
-
if (classMatch?.[1]) {
|
|
147
|
-
const methodName = classMatch[2]; // 'execute' or 'connect'
|
|
148
|
-
results.push({
|
|
149
|
-
kind: 'api',
|
|
150
|
-
operationName: classMatch[1],
|
|
151
|
-
paramsType: 'unknown',
|
|
152
|
-
responseType: 'unknown',
|
|
153
|
-
...(methodName && { methodName }), // Only include if defined
|
|
154
|
-
jsdoc: extractJsDoc(fileContent, classMatch[1]),
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// WebSocket operations: createWebSocketOperation<Params, RequestMessage, InboundMessage>
|
|
159
|
-
// Note: Generic types can contain nested angle brackets (e.g., z.infer<...>),
|
|
160
|
-
// which are hard to parse reliably with a simple regex. We only need the
|
|
161
|
-
// operation name for generation, so match by name and ignore generic details.
|
|
162
|
-
const wsNameRegex = /export const (\w+)\s*=\s*createWebSocketOperation</s;
|
|
163
|
-
const wsNameMatch = wsNameRegex.exec(fileContent);
|
|
164
|
-
if (wsNameMatch?.[1]) {
|
|
165
|
-
results.push({
|
|
166
|
-
kind: 'ws',
|
|
167
|
-
operationName: wsNameMatch[1],
|
|
168
|
-
paramsType: 'unknown',
|
|
169
|
-
requestType: 'unknown',
|
|
170
|
-
messageType: 'unknown',
|
|
171
|
-
jsdoc: extractJsDoc(fileContent, wsNameMatch[1]),
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return results;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function relativeImportPath(from: string, to: string): string {
|
|
179
|
-
let rel = path.relative(path.dirname(from), to).replace(/\\/g, '/');
|
|
180
|
-
if (!rel.startsWith('.')) rel = `./${rel}`;
|
|
181
|
-
return rel.replace(/\.ts$/, '');
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/** Extract the first line of a JSDoc comment (the description) */
|
|
185
|
-
function extractJsDocDescription(jsdoc: string | undefined): string | undefined {
|
|
186
|
-
if (!jsdoc) return undefined;
|
|
187
|
-
|
|
188
|
-
// Remove /** and */ and trim
|
|
189
|
-
const content = jsdoc
|
|
190
|
-
.replace(/^\/\*\*\s*/s, '')
|
|
191
|
-
.replace(/\s*\*\/$/s, '')
|
|
192
|
-
.split('\n')
|
|
193
|
-
.map((line) => line.replace(/^\s*\*\s?/, ''))
|
|
194
|
-
.join('\n')
|
|
195
|
-
.trim();
|
|
196
|
-
|
|
197
|
-
// Get first paragraph (up to first blank line or @tag)
|
|
198
|
-
const firstParagraph = content.split(/\n\s*\n|\n\s*@/)[0];
|
|
199
|
-
return firstParagraph?.trim();
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function generateMethodDeclarations(ops: Array<OperationInfo & { importPath: string }>): string {
|
|
203
|
-
return ops
|
|
204
|
-
.map((op) => {
|
|
205
|
-
const methodName = operationNameToMethodName(op.operationName);
|
|
206
|
-
const description = extractJsDocDescription(op.jsdoc) ?? generateDescriptionFromName(op.operationName);
|
|
207
|
-
|
|
208
|
-
// Generate JSDoc for the method
|
|
209
|
-
const jsdocComment = ` /** ${description} */`;
|
|
210
|
-
|
|
211
|
-
if (op.kind === 'api') {
|
|
212
|
-
const opMethodName = op.methodName ?? 'execute';
|
|
213
|
-
const methodParamsType = `Parameters<InstanceType<typeof ${op.operationName}>['${opMethodName}']>[0]`;
|
|
214
|
-
const methodReturnType = `ReturnType<InstanceType<typeof ${op.operationName}>['${opMethodName}']>`;
|
|
215
|
-
if (op.paramsType === 'void') {
|
|
216
|
-
return `${jsdocComment}\n public ${methodName}!: () => ${methodReturnType};`;
|
|
217
|
-
}
|
|
218
|
-
return `${jsdocComment}\n public ${methodName}!: (params: ${methodParamsType}) => ${methodReturnType};`;
|
|
219
|
-
}
|
|
220
|
-
// WebSocket subscribe methods
|
|
221
|
-
const paramsType = `Parameters<InstanceType<typeof ${op.operationName}>['subscribe']>[0]`;
|
|
222
|
-
const messageType = `Parameters<Parameters<InstanceType<typeof ${op.operationName}>['subscribe']>[1]['onMessage']>[0]`;
|
|
223
|
-
return `${jsdocComment}\n public ${methodName}!: (params: ${paramsType}, handlers: WebSocketHandlers<${messageType}>) => Promise<WebSocketSubscription>;`;
|
|
224
|
-
})
|
|
225
|
-
.join('\n');
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function generateMethodImplementations(ops: Array<OperationInfo & { importPath: string }>): string {
|
|
229
|
-
return ops
|
|
230
|
-
.map((op) => {
|
|
231
|
-
const methodName = operationNameToMethodName(op.operationName);
|
|
232
|
-
if (op.kind === 'api') {
|
|
233
|
-
const params = op.paramsType === 'void' ? '' : 'params';
|
|
234
|
-
const opMethodName = op.methodName ?? 'execute';
|
|
235
|
-
return ` this.${methodName} = (${params}) => new ${op.operationName}(this).${opMethodName}(${params});`;
|
|
236
|
-
}
|
|
237
|
-
return ` this.${methodName} = (params, handlers) => new ${op.operationName}(this).subscribe(params, handlers);`;
|
|
238
|
-
})
|
|
239
|
-
.join('\n');
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
function generateOperationImports(opFiles: Array<{ operationName: string; importPath: string }>): string {
|
|
243
|
-
return opFiles.map((op) => `import { ${op.operationName} } from '${op.importPath}';`).join('\n');
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Convert operation name to method name (e.g., GetEventsByContractId -> getEventsByContractId)
|
|
247
|
-
function operationNameToMethodName(operationName: string): string {
|
|
248
|
-
return operationName.charAt(0).toLowerCase() + operationName.slice(1);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
function generateClientFile(clientConfig: ClientConfig): void {
|
|
252
|
-
const { name, clientFile, operationsDir, baseClass } = clientConfig;
|
|
253
|
-
|
|
254
|
-
// 1. Scan all operation files
|
|
255
|
-
const files = getAllTsFiles(operationsDir);
|
|
256
|
-
|
|
257
|
-
const allOps = files
|
|
258
|
-
.map((file) => {
|
|
259
|
-
const content = fs.readFileSync(file, 'utf8');
|
|
260
|
-
return {
|
|
261
|
-
infos: extractOperationInfos(content),
|
|
262
|
-
importPath: relativeImportPath(clientFile, file),
|
|
263
|
-
};
|
|
264
|
-
})
|
|
265
|
-
.flatMap((item) => item.infos.map((info) => ({ ...info, importPath: item.importPath })));
|
|
266
|
-
|
|
267
|
-
if (allOps.length === 0) {
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// 2. Generate method declarations, implementations, and imports
|
|
272
|
-
const methodDecls = generateMethodDeclarations(allOps);
|
|
273
|
-
const methodImpls = generateMethodImplementations(allOps);
|
|
274
|
-
const opImports = generateOperationImports(allOps);
|
|
275
|
-
|
|
276
|
-
// 3. Generate the complete client file content
|
|
277
|
-
const baseClassImport = baseClass === 'SimpleBaseClient' ? 'SimpleBaseClient' : 'BaseClient';
|
|
278
|
-
const baseClassPath = baseClass === 'SimpleBaseClient' ? '../../core' : '../../core';
|
|
279
|
-
|
|
280
|
-
// Fix API type for ledger client
|
|
281
|
-
const apiType =
|
|
282
|
-
name === 'LedgerJsonApiClient'
|
|
283
|
-
? 'LEDGER_JSON_API'
|
|
284
|
-
: name === 'ValidatorApiClient'
|
|
285
|
-
? 'VALIDATOR_API'
|
|
286
|
-
: name === 'ScanApiClient'
|
|
287
|
-
? 'SCAN_API'
|
|
288
|
-
: undefined;
|
|
289
|
-
|
|
290
|
-
const needsWsImports = allOps.some((op) => op.kind === 'ws');
|
|
291
|
-
const wsImports = needsWsImports ? `\nimport { WebSocketHandlers, WebSocketSubscription } from '../../core/ws';` : '';
|
|
292
|
-
|
|
293
|
-
const content = `import { ${baseClassImport}, CantonRuntime } from '${baseClassPath}';
|
|
294
|
-
${opImports}
|
|
295
|
-
${wsImports}
|
|
296
|
-
|
|
297
|
-
/** Client for interacting with Canton's ${name.replace('Client', '')} */
|
|
298
|
-
export class ${name} extends ${baseClass} {
|
|
299
|
-
${methodDecls}
|
|
300
|
-
|
|
301
|
-
constructor(runtime: CantonRuntime) {
|
|
302
|
-
super('${apiType}', runtime);
|
|
303
|
-
this.initializeMethods();
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
private initializeMethods(): void {
|
|
307
|
-
${methodImpls}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
`;
|
|
311
|
-
|
|
312
|
-
// 4. Write the generated file
|
|
313
|
-
fs.writeFileSync(clientFile, content);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
function generateAllClients(): void {
|
|
317
|
-
console.log('Generating all client files...');
|
|
318
|
-
|
|
319
|
-
CLIENTS.forEach((clientConfig) => {
|
|
320
|
-
try {
|
|
321
|
-
generateClientFile(clientConfig);
|
|
322
|
-
} catch (_error) {
|
|
323
|
-
console.error(`Error generating ${clientConfig.name}:`, _error);
|
|
324
|
-
}
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
console.log('Client generation complete');
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
if (require.main === module) {
|
|
331
|
-
generateAllClients();
|
|
332
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env tsx
|
|
2
|
-
|
|
3
|
-
import { execSync } from 'child_process';
|
|
4
|
-
import * as fs from 'fs';
|
|
5
|
-
import { glob } from 'glob';
|
|
6
|
-
import * as path from 'path';
|
|
7
|
-
|
|
8
|
-
function ensureDirectoryExists(filePath: string): void {
|
|
9
|
-
const dir = path.dirname(filePath);
|
|
10
|
-
if (!fs.existsSync(dir)) {
|
|
11
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function generateForFile(yamlPath: string): void {
|
|
16
|
-
const relativePath = yamlPath.replace(/^libs\/splice\//, '').replace(/\.yaml$/, '.ts');
|
|
17
|
-
const outputPath = path.join('src/generated', relativePath);
|
|
18
|
-
ensureDirectoryExists(outputPath);
|
|
19
|
-
|
|
20
|
-
execSync(`npx openapi-typescript "${yamlPath}" -o "${outputPath}"`, {
|
|
21
|
-
stdio: 'inherit',
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function main(): void {
|
|
26
|
-
const openapiUnderDir = glob.sync('libs/splice/**/openapi/**/*.yaml', {
|
|
27
|
-
nodir: true,
|
|
28
|
-
});
|
|
29
|
-
const openapiRootFiles = glob.sync('libs/splice/**/openapi.yaml', {
|
|
30
|
-
nodir: true,
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
// Merge and de-duplicate
|
|
34
|
-
const allFiles = Array.from(new Set([...openapiUnderDir, ...openapiRootFiles]));
|
|
35
|
-
|
|
36
|
-
if (allFiles.length === 0) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Generate types sequentially for clearer logs
|
|
41
|
-
for (const file of allFiles) {
|
|
42
|
-
generateForFile(file);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
main();
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env tsx
|
|
2
|
-
|
|
3
|
-
import * as fs from 'fs';
|
|
4
|
-
import { glob } from 'glob';
|
|
5
|
-
import * as path from 'path';
|
|
6
|
-
|
|
7
|
-
interface OperationInfo {
|
|
8
|
-
name: string;
|
|
9
|
-
filePath: string;
|
|
10
|
-
testPath: string;
|
|
11
|
-
paramsSchema: string;
|
|
12
|
-
method: string;
|
|
13
|
-
endpoint: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async function findOperations(): Promise<OperationInfo[]> {
|
|
17
|
-
const operations: OperationInfo[] = [];
|
|
18
|
-
|
|
19
|
-
// Find all operation files
|
|
20
|
-
const operationFiles = await glob('src/clients/**/*.ts', {
|
|
21
|
-
ignore: ['**/index.ts', '**/register.ts', '**/schemas/**', '**/*.generated.ts'],
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
for (const file of operationFiles) {
|
|
25
|
-
const content = fs.readFileSync(file, 'utf-8');
|
|
26
|
-
|
|
27
|
-
// Check if this is an operation file by looking for createApiOperation
|
|
28
|
-
if (content.includes('createApiOperation')) {
|
|
29
|
-
const nameMatch = content.match(/export const (\w+) = createApiOperation/);
|
|
30
|
-
if (nameMatch?.[1]) {
|
|
31
|
-
const name = nameMatch[1];
|
|
32
|
-
const methodMatch = content.match(/method:\s*['"`]([A-Z]+)['"`]/);
|
|
33
|
-
const endpointMatch = content.match(/endpoint\s*=\s*['"`]([^'"`]+)['"`]/);
|
|
34
|
-
const paramsSchemaMatch = content.match(/paramsSchema:\s*(\w+)/);
|
|
35
|
-
|
|
36
|
-
const method = methodMatch?.[1] ?? 'GET';
|
|
37
|
-
const endpoint = endpointMatch?.[1] ?? '/unknown';
|
|
38
|
-
const paramsSchema = paramsSchemaMatch?.[1] ?? 'z.any()';
|
|
39
|
-
|
|
40
|
-
// Generate test path
|
|
41
|
-
const relativePath = path.relative('src', file);
|
|
42
|
-
const testPath = `test/${relativePath.replace('.ts', '.test.ts')}`;
|
|
43
|
-
|
|
44
|
-
operations.push({
|
|
45
|
-
name,
|
|
46
|
-
filePath: file,
|
|
47
|
-
testPath,
|
|
48
|
-
paramsSchema,
|
|
49
|
-
method,
|
|
50
|
-
endpoint,
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return operations;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function generateTestContent(operation: OperationInfo): string {
|
|
60
|
-
const importPath = path
|
|
61
|
-
.relative(path.dirname(operation.testPath), path.dirname(operation.filePath))
|
|
62
|
-
.replace(/\\/g, '/');
|
|
63
|
-
const mockClientPath = path.relative(path.dirname(operation.testPath), 'test/utils').replace(/\\/g, '/');
|
|
64
|
-
const configPath = path.relative(path.dirname(operation.testPath), 'test/config').replace(/\\/g, '/');
|
|
65
|
-
|
|
66
|
-
const isValidatorApi = operation.filePath.includes('validator-api');
|
|
67
|
-
|
|
68
|
-
let mockClientClass = 'MockLedgerJsonApiClient';
|
|
69
|
-
let apiUrlKey = 'LEDGER_JSON_API';
|
|
70
|
-
|
|
71
|
-
if (isValidatorApi) {
|
|
72
|
-
mockClientClass = 'MockValidatorApiClient';
|
|
73
|
-
apiUrlKey = 'VALIDATOR_API';
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return `import { ${mockClientClass} } from '${mockClientPath}';
|
|
77
|
-
import { ${operation.name} } from '${importPath}/${operation.name}';
|
|
78
|
-
import { mockClientConfig, mockApiUrls } from '${configPath}/testConfig';
|
|
79
|
-
|
|
80
|
-
describe('${operation.name} Operation', () => {
|
|
81
|
-
let mockClient: ${mockClientClass};
|
|
82
|
-
let operation: InstanceType<typeof ${operation.name}>;
|
|
83
|
-
|
|
84
|
-
beforeEach(() => {
|
|
85
|
-
mockClient = new ${mockClientClass}(mockClientConfig);
|
|
86
|
-
operation = new ${operation.name}(mockClient);
|
|
87
|
-
mockClient.clearMocks();
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
describe('Network Activity Validation', () => {
|
|
91
|
-
it('should make a ${operation.method} request to the correct endpoint', async () => {
|
|
92
|
-
// Arrange
|
|
93
|
-
const params = ${operation.paramsSchema === 'z.any()' ? '{}' : '{}'};
|
|
94
|
-
const mockResponse = {};
|
|
95
|
-
const expectedUrl = \`\${mockApiUrls.${apiUrlKey}}${operation.endpoint}\`;
|
|
96
|
-
|
|
97
|
-
mockClient.setMockResponse(expectedUrl, mockResponse);
|
|
98
|
-
|
|
99
|
-
// Act
|
|
100
|
-
const result = await operation.execute(params);
|
|
101
|
-
|
|
102
|
-
// Assert
|
|
103
|
-
expect(result).toEqual(mockResponse);
|
|
104
|
-
|
|
105
|
-
const requests = mockClient.getRequests();
|
|
106
|
-
expect(requests).toHaveLength(1);
|
|
107
|
-
|
|
108
|
-
const request = requests[0];
|
|
109
|
-
expect(request).toBeDefined();
|
|
110
|
-
expect(request!.method).toBe('${operation.method}');
|
|
111
|
-
expect(request!.url).toBe(expectedUrl);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('should include proper headers in the request', async () => {
|
|
115
|
-
// Arrange
|
|
116
|
-
const params = ${operation.paramsSchema === 'z.any()' ? '{}' : '{}'};
|
|
117
|
-
const mockResponse = {};
|
|
118
|
-
const expectedUrl = \`\${mockApiUrls.${apiUrlKey}}${operation.endpoint}\`;
|
|
119
|
-
|
|
120
|
-
mockClient.setMockResponse(expectedUrl, mockResponse);
|
|
121
|
-
|
|
122
|
-
// Act
|
|
123
|
-
await operation.execute(params);
|
|
124
|
-
|
|
125
|
-
// Assert
|
|
126
|
-
const request = mockClient.getLastRequest();
|
|
127
|
-
expect(request).toBeDefined();
|
|
128
|
-
expect(request?.headers).toBeDefined();
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
describe('Error Handling', () => {
|
|
133
|
-
it('should propagate network errors', async () => {
|
|
134
|
-
// Arrange
|
|
135
|
-
const params = ${operation.paramsSchema === 'z.any()' ? '{}' : '{}'};
|
|
136
|
-
const expectedUrl = \`\${mockApiUrls.${apiUrlKey}}${operation.endpoint}\`;
|
|
137
|
-
const networkError = new Error('Network error');
|
|
138
|
-
|
|
139
|
-
mockClient.setMockError(expectedUrl, networkError);
|
|
140
|
-
|
|
141
|
-
// Act & Assert
|
|
142
|
-
await expect(operation.execute(params)).rejects.toThrow('Network error');
|
|
143
|
-
|
|
144
|
-
const requests = mockClient.getRequests();
|
|
145
|
-
expect(requests).toHaveLength(1);
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
describe('Parameter Validation', () => {
|
|
150
|
-
it('should accept valid parameters', async () => {
|
|
151
|
-
// Arrange
|
|
152
|
-
const params = ${operation.paramsSchema === 'z.any()' ? '{}' : '{}'};
|
|
153
|
-
const mockResponse = {};
|
|
154
|
-
const expectedUrl = \`\${mockApiUrls.${apiUrlKey}}${operation.endpoint}\`;
|
|
155
|
-
|
|
156
|
-
mockClient.setMockResponse(expectedUrl, mockResponse);
|
|
157
|
-
|
|
158
|
-
// Act & Assert
|
|
159
|
-
await expect(operation.execute(params)).resolves.toEqual(mockResponse);
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
`;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
async function generateTests(): Promise<void> {
|
|
167
|
-
console.log('Finding operations...');
|
|
168
|
-
const operations = await findOperations();
|
|
169
|
-
|
|
170
|
-
console.log(`Found ${operations.length} operations`);
|
|
171
|
-
|
|
172
|
-
for (const operation of operations) {
|
|
173
|
-
// Create test directory if it doesn't exist
|
|
174
|
-
const testDir = path.dirname(operation.testPath);
|
|
175
|
-
if (!fs.existsSync(testDir)) {
|
|
176
|
-
fs.mkdirSync(testDir, { recursive: true });
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Check if test already exists
|
|
180
|
-
if (fs.existsSync(operation.testPath)) {
|
|
181
|
-
console.log(`Test already exists: ${operation.testPath}`);
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const testContent = generateTestContent(operation);
|
|
186
|
-
fs.writeFileSync(operation.testPath, testContent);
|
|
187
|
-
console.log(`Generated test: ${operation.testPath}`);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
console.log('Test generation complete');
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (require.main === module) {
|
|
194
|
-
generateTests().catch((error) => {
|
|
195
|
-
console.error('Error generating tests:', error);
|
|
196
|
-
process.exit(1);
|
|
197
|
-
});
|
|
198
|
-
}
|