@c7-digital/ledger 0.0.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/BUILD.md +125 -0
- package/LICENSE +17 -0
- package/README.md +97 -0
- package/lib/scripts/build.d.ts +9 -0
- package/lib/scripts/build.js +366 -0
- package/lib/scripts/enhance-types.d.ts +3 -0
- package/lib/scripts/enhance-types.js +174 -0
- package/lib/scripts/generate-asyncapi-types.d.ts +9 -0
- package/lib/scripts/generate-asyncapi-types.js +129 -0
- package/lib/scripts/generate-schema-modules.d.ts +3 -0
- package/lib/scripts/generate-schema-modules.js +33 -0
- package/lib/src/TemplateEmitterMap.d.ts +40 -0
- package/lib/src/TemplateEmitterMap.js +57 -0
- package/lib/src/client.d.ts +55 -0
- package/lib/src/client.js +62 -0
- package/lib/src/generated/api.d.ts +7147 -0
- package/lib/src/generated/api.js +1 -0
- package/lib/src/generated/async-api.d.ts +1665 -0
- package/lib/src/generated/async-api.js +1 -0
- package/lib/src/generated/asyncapi-schema.d.ts +1 -0
- package/lib/src/generated/asyncapi-schema.js +2233 -0
- package/lib/src/generated/openapi-schema.d.ts +1 -0
- package/lib/src/generated/openapi-schema.js +7321 -0
- package/lib/src/generated/sdk-version.d.ts +1 -0
- package/lib/src/generated/sdk-version.js +3 -0
- package/lib/src/index.d.ts +8 -0
- package/lib/src/index.js +8 -0
- package/lib/src/ledger.d.ts +188 -0
- package/lib/src/ledger.js +849 -0
- package/lib/src/ledger.test.d.ts +1 -0
- package/lib/src/ledger.test.js +23 -0
- package/lib/src/logger.d.ts +41 -0
- package/lib/src/logger.js +56 -0
- package/lib/src/multistream.d.ts +47 -0
- package/lib/src/multistream.js +123 -0
- package/lib/src/translate.d.ts +5 -0
- package/lib/src/translate.js +30 -0
- package/lib/src/types.d.ts +201 -0
- package/lib/src/types.js +1 -0
- package/lib/src/util.d.ts +3 -0
- package/lib/src/util.js +7 -0
- package/lib/src/validation.d.ts +27 -0
- package/lib/src/validation.js +182 -0
- package/lib/src/valueTypes.d.ts +34 -0
- package/lib/src/valueTypes.js +76 -0
- package/lib/src/websocket.d.ts +69 -0
- package/lib/src/websocket.js +125 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/lib-lite/scripts/build.d.ts +9 -0
- package/lib-lite/scripts/build.js +366 -0
- package/lib-lite/scripts/enhance-types.d.ts +3 -0
- package/lib-lite/scripts/enhance-types.js +174 -0
- package/lib-lite/scripts/generate-asyncapi-types.d.ts +9 -0
- package/lib-lite/scripts/generate-asyncapi-types.js +129 -0
- package/lib-lite/scripts/generate-schema-modules.d.ts +3 -0
- package/lib-lite/scripts/generate-schema-modules.js +33 -0
- package/lib-lite/src/TemplateEmitterMap.d.ts +40 -0
- package/lib-lite/src/TemplateEmitterMap.js +57 -0
- package/lib-lite/src/client.d.ts +55 -0
- package/lib-lite/src/client.js +62 -0
- package/lib-lite/src/generated/api.d.ts +7147 -0
- package/lib-lite/src/generated/api.js +1 -0
- package/lib-lite/src/generated/async-api.d.ts +1665 -0
- package/lib-lite/src/generated/async-api.js +1 -0
- package/lib-lite/src/generated/asyncapi-schema.d.ts +1 -0
- package/lib-lite/src/generated/asyncapi-schema.js +2 -0
- package/lib-lite/src/generated/openapi-schema.d.ts +1 -0
- package/lib-lite/src/generated/openapi-schema.js +2 -0
- package/lib-lite/src/generated/sdk-version.d.ts +1 -0
- package/lib-lite/src/generated/sdk-version.js +3 -0
- package/lib-lite/src/index.d.ts +8 -0
- package/lib-lite/src/index.js +8 -0
- package/lib-lite/src/ledger.d.ts +188 -0
- package/lib-lite/src/ledger.js +849 -0
- package/lib-lite/src/ledger.test.d.ts +1 -0
- package/lib-lite/src/ledger.test.js +23 -0
- package/lib-lite/src/logger.d.ts +41 -0
- package/lib-lite/src/logger.js +56 -0
- package/lib-lite/src/multistream.d.ts +47 -0
- package/lib-lite/src/multistream.js +123 -0
- package/lib-lite/src/translate.d.ts +5 -0
- package/lib-lite/src/translate.js +30 -0
- package/lib-lite/src/types.d.ts +201 -0
- package/lib-lite/src/types.js +1 -0
- package/lib-lite/src/util.d.ts +3 -0
- package/lib-lite/src/util.js +7 -0
- package/lib-lite/src/validation.d.ts +14 -0
- package/lib-lite/src/validation.js +31 -0
- package/lib-lite/src/valueTypes.d.ts +34 -0
- package/lib-lite/src/valueTypes.js +76 -0
- package/lib-lite/src/websocket.d.ts +69 -0
- package/lib-lite/src/websocket.js +125 -0
- package/lib-lite/tsconfig.temp.tsbuildinfo +1 -0
- package/package.json +72 -0
- package/scripts/build.ts +456 -0
- package/scripts/enhance-types.ts +223 -0
- package/scripts/generate-asyncapi-types.ts +158 -0
- package/scripts/generate-schema-modules.ts +52 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Script to automatically brand OpenAPI generated types with value.proto constraints
|
|
3
|
+
// Ideally the spec would provide this, but this is sufficient for now.
|
|
4
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
5
|
+
// Patterns to find fields with value.proto references based on JSDoc comments
|
|
6
|
+
// Each entry automatically handles both string and string[] cases
|
|
7
|
+
const VALUE_PROTO_PATTERNS = [
|
|
8
|
+
// LedgerString patterns
|
|
9
|
+
{
|
|
10
|
+
commentPattern: "Must be a valid LedgerString",
|
|
11
|
+
targetType: "LedgerString",
|
|
12
|
+
},
|
|
13
|
+
// PartyIdString patterns
|
|
14
|
+
{
|
|
15
|
+
commentPattern: "Must be a valid PartyIdString",
|
|
16
|
+
targetType: "PartyIdString",
|
|
17
|
+
},
|
|
18
|
+
// UserIdString patterns
|
|
19
|
+
{
|
|
20
|
+
commentPattern: "Must be a valid UserIdString",
|
|
21
|
+
targetType: "UserIdString",
|
|
22
|
+
},
|
|
23
|
+
// NameString patterns
|
|
24
|
+
{
|
|
25
|
+
commentPattern: "Must be a valid NameString",
|
|
26
|
+
targetType: "NameString",
|
|
27
|
+
},
|
|
28
|
+
// PackageIdString patterns
|
|
29
|
+
{
|
|
30
|
+
commentPattern: "Must be a valid PackageIdString",
|
|
31
|
+
targetType: "PackageIdString",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
commentPattern: "The identifier uses the package-id reference format",
|
|
35
|
+
targetType: "PackageIdString",
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
function brandApiTypes(inputPath, outputPath, label) {
|
|
39
|
+
console.log(`🔄 Branding ${label} types with value.proto constraints...`);
|
|
40
|
+
const content = readFileSync(inputPath, "utf-8");
|
|
41
|
+
let brandedContent = content;
|
|
42
|
+
// Add imports at the top if not already present
|
|
43
|
+
if (!content.includes('from "../valueTypes.js"')) {
|
|
44
|
+
const importStatement = `import { LedgerString, PartyIdString, UserIdString, NameString, PackageIdString } from "../valueTypes.js";\n\n`;
|
|
45
|
+
brandedContent = importStatement + brandedContent;
|
|
46
|
+
}
|
|
47
|
+
let replacementCount = 0;
|
|
48
|
+
// Track usage of each comment pattern
|
|
49
|
+
const patternUsage = new Map();
|
|
50
|
+
// This regex captures JSDoc comments followed by field declarations
|
|
51
|
+
// We're looking for: /** JSDoc comment */ fieldName?: fieldType;
|
|
52
|
+
//
|
|
53
|
+
// Regex breakdown:
|
|
54
|
+
// /\/\*\* - Match opening /** (literal forward slash, asterisk, asterisk)
|
|
55
|
+
// ([^*]|[\r\n]|(\*+([^*/]|[\r\n])))* - Match JSDoc content (non-greedy):
|
|
56
|
+
// [^*] - Any character except asterisk
|
|
57
|
+
// |[\r\n] - OR newline characters (carriage return or line feed)
|
|
58
|
+
// |(\*+([^*/]|[\r\n])) - OR one or more asterisks followed by non-closing chars
|
|
59
|
+
// \*+\/ - Match closing */ (one or more asterisks + forward slash)
|
|
60
|
+
// \s* - Match optional whitespace after JSDoc
|
|
61
|
+
// (\w+) - Capture group 4: field name (word characters)
|
|
62
|
+
// (\?)? - Capture group 5: optional question mark for optional fields
|
|
63
|
+
// \s*:\s* - Match colon with optional whitespace around it
|
|
64
|
+
// ([^;,\}\n]+) - Capture group 6: field type (everything except semicolon, comma, closing brace, newline)
|
|
65
|
+
//
|
|
66
|
+
// The regex handles multi-line JSDoc with @description tags and indented fields within object types
|
|
67
|
+
const fieldRegex = /\/\*\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+\/\s*(\w+)(\?)?\s*:\s*([^;,\}\n]+)/g;
|
|
68
|
+
// Use a more compatible approach for regex matching
|
|
69
|
+
const matches = [];
|
|
70
|
+
let match;
|
|
71
|
+
while ((match = fieldRegex.exec(content)) !== null) {
|
|
72
|
+
matches.push(match);
|
|
73
|
+
}
|
|
74
|
+
// Process each match
|
|
75
|
+
for (const match of matches) {
|
|
76
|
+
if (match.length < 6)
|
|
77
|
+
continue;
|
|
78
|
+
const fullMatch = match[0];
|
|
79
|
+
const jsDocComment = match[0].substring(3, match[0].indexOf("*/")); // Extract JSDoc content
|
|
80
|
+
const fieldName = match[4]; // Field name
|
|
81
|
+
const isOptional = match[5] === "?"; // Optional marker
|
|
82
|
+
const fieldType = match[6]; // Field type (could be anything)
|
|
83
|
+
// Skip if we don't have the necessary data
|
|
84
|
+
if (!fullMatch || !fieldName || !fieldType)
|
|
85
|
+
continue;
|
|
86
|
+
// Check if this is a string-related type that we care about
|
|
87
|
+
const trimmedFieldType = fieldType.trim();
|
|
88
|
+
if (trimmedFieldType === "string" || trimmedFieldType === "string[]") {
|
|
89
|
+
// Check if the JSDoc comment matches any of our patterns
|
|
90
|
+
for (const { commentPattern, targetType } of VALUE_PROTO_PATTERNS) {
|
|
91
|
+
// Check if JSDoc contains the pattern
|
|
92
|
+
if (jsDocComment.includes(commentPattern)) {
|
|
93
|
+
// Extra validation: ensure this is actually a value.proto constraint comment
|
|
94
|
+
if (commentPattern.includes("Must be a valid") ||
|
|
95
|
+
commentPattern.includes("package-id reference format")) {
|
|
96
|
+
// Determine the correct target type based on whether it's an array
|
|
97
|
+
const finalTargetType = trimmedFieldType === "string[]" ? `${targetType}[]` : targetType;
|
|
98
|
+
// Create the branded declaration
|
|
99
|
+
const brandedDeclaration = fullMatch.replace(trimmedFieldType, finalTargetType);
|
|
100
|
+
// Replace in the content
|
|
101
|
+
brandedContent = brandedContent.replace(fullMatch, brandedDeclaration);
|
|
102
|
+
replacementCount++;
|
|
103
|
+
// Track pattern usage
|
|
104
|
+
const fieldExample = `${fieldName}${isOptional ? "?" : ""}: ${trimmedFieldType} → ${finalTargetType}`;
|
|
105
|
+
if (!patternUsage.has(commentPattern)) {
|
|
106
|
+
patternUsage.set(commentPattern, { count: 0, examples: [] });
|
|
107
|
+
}
|
|
108
|
+
const usage = patternUsage.get(commentPattern);
|
|
109
|
+
usage.count++;
|
|
110
|
+
if (usage.examples.length < 3) {
|
|
111
|
+
// Keep up to 3 examples per pattern
|
|
112
|
+
usage.examples.push(fieldExample);
|
|
113
|
+
}
|
|
114
|
+
// Log the branding with field name
|
|
115
|
+
const optionalMarker = isOptional ? "?" : "";
|
|
116
|
+
console.log(`✅ Branded ${fieldName}${optionalMarker}: ${trimmedFieldType} → ${finalTargetType}`);
|
|
117
|
+
// console.log(`JSDoc: ${jsDocComment}`);
|
|
118
|
+
// console.log(`Pattern: ${commentPattern}`);
|
|
119
|
+
// console.log(`---------------------------------`);
|
|
120
|
+
break; // Stop checking patterns once we've found a match
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Write branded content directly to the output file
|
|
127
|
+
console.log(`📝 Writing branded types to ${outputPath}...`);
|
|
128
|
+
writeFileSync(outputPath, brandedContent);
|
|
129
|
+
console.log(`🎉 Branded ${replacementCount} field declarations with value.proto constraints`);
|
|
130
|
+
// Report pattern usage summary
|
|
131
|
+
if (patternUsage.size > 0) {
|
|
132
|
+
console.log(`\n📊 Pattern Usage Summary:`);
|
|
133
|
+
console.log(`════════════════════════════════════════`);
|
|
134
|
+
// Sort patterns by usage count (descending)
|
|
135
|
+
const sortedPatterns = Array.from(patternUsage.entries()).sort(([, a], [, b]) => b.count - a.count);
|
|
136
|
+
for (const [pattern, { count, examples }] of sortedPatterns) {
|
|
137
|
+
console.log(`\n🔖 "${pattern}"`);
|
|
138
|
+
console.log(` Used ${count} time${count === 1 ? "" : "s"}`);
|
|
139
|
+
console.log(` Examples:`);
|
|
140
|
+
for (const example of examples) {
|
|
141
|
+
console.log(` • ${example}`);
|
|
142
|
+
}
|
|
143
|
+
if (count > examples.length) {
|
|
144
|
+
console.log(` ... and ${count - examples.length} more`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
console.log(`\n════════════════════════════════════════`);
|
|
148
|
+
}
|
|
149
|
+
return replacementCount;
|
|
150
|
+
}
|
|
151
|
+
function brandTypes(inputPath, outputPath, label = "API") {
|
|
152
|
+
brandApiTypes(inputPath, outputPath, label);
|
|
153
|
+
}
|
|
154
|
+
// Export the function for use in other scripts
|
|
155
|
+
export { brandTypes };
|
|
156
|
+
// Check if this script is being run directly
|
|
157
|
+
// For ES modules, we check if the import.meta.url matches the process.argv[1]
|
|
158
|
+
const isMainModule = process.argv[1] &&
|
|
159
|
+
(process.argv[1].endsWith("brand-types.ts") || process.argv[1].endsWith("enhance-types.ts"));
|
|
160
|
+
if (isMainModule) {
|
|
161
|
+
const args = process.argv.slice(2);
|
|
162
|
+
if (args.length < 2) {
|
|
163
|
+
console.error("Usage: brand-types.ts <input-file> <output-file> [label]");
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
if (!args[0] || !args[1]) {
|
|
167
|
+
console.error("Input and output files must be specified.");
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
const inputPath = args[0];
|
|
171
|
+
const outputPath = args[1];
|
|
172
|
+
const label = args[2] || "API";
|
|
173
|
+
brandTypes(inputPath, outputPath, label);
|
|
174
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Generate TypeScript types from AsyncAPI specification by converting to OpenAPI format
|
|
4
|
+
*
|
|
5
|
+
* This script converts the AsyncAPI YAML to OpenAPI format and uses the existing
|
|
6
|
+
* openapi-typescript generator to create types. Much simpler than custom schema parsing.
|
|
7
|
+
*/
|
|
8
|
+
declare function generateAsyncApiTypes(specPath?: string): Promise<void>;
|
|
9
|
+
export { generateAsyncApiTypes };
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Generate TypeScript types from AsyncAPI specification by converting to OpenAPI format
|
|
4
|
+
*
|
|
5
|
+
* This script converts the AsyncAPI YAML to OpenAPI format and uses the existing
|
|
6
|
+
* openapi-typescript generator to create types. Much simpler than custom schema parsing.
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from "fs";
|
|
9
|
+
import * as path from "path";
|
|
10
|
+
import * as yaml from "yaml";
|
|
11
|
+
import { exec } from "child_process";
|
|
12
|
+
import { promisify } from "util";
|
|
13
|
+
const execAsync = promisify(exec);
|
|
14
|
+
const __dirname = process.cwd();
|
|
15
|
+
async function generateAsyncApiTypes(specPath) {
|
|
16
|
+
try {
|
|
17
|
+
// Get spec path from parameter or command line argument
|
|
18
|
+
const asyncApiSpecPath = specPath || process.argv[2];
|
|
19
|
+
if (!asyncApiSpecPath) {
|
|
20
|
+
console.error("Usage: node generate-asyncapi-types.ts <path-to-asyncapi-spec>");
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
// Read the AsyncAPI specification
|
|
24
|
+
const asyncApiPath = path.resolve(asyncApiSpecPath);
|
|
25
|
+
const asyncApiContent = fs.readFileSync(asyncApiPath, "utf8");
|
|
26
|
+
const asyncApiSpec = yaml.parse(asyncApiContent);
|
|
27
|
+
// Convert AsyncAPI to OpenAPI format by changing the header
|
|
28
|
+
const openApiSpec = {
|
|
29
|
+
...asyncApiSpec,
|
|
30
|
+
openapi: "3.0.3",
|
|
31
|
+
info: asyncApiSpec.info,
|
|
32
|
+
components: asyncApiSpec.components,
|
|
33
|
+
// Remove AsyncAPI-specific fields
|
|
34
|
+
asyncapi: undefined,
|
|
35
|
+
channels: undefined,
|
|
36
|
+
operations: undefined,
|
|
37
|
+
// Add empty paths since OpenAPI requires it
|
|
38
|
+
paths: {},
|
|
39
|
+
};
|
|
40
|
+
// Clean up undefined properties
|
|
41
|
+
delete openApiSpec.asyncapi;
|
|
42
|
+
delete openApiSpec.channels;
|
|
43
|
+
delete openApiSpec.operations;
|
|
44
|
+
// Create temporary OpenAPI file
|
|
45
|
+
const tempOpenApiPath = path.join(__dirname, "temp-asyncapi-as-openapi.yaml");
|
|
46
|
+
fs.writeFileSync(tempOpenApiPath, yaml.stringify(openApiSpec));
|
|
47
|
+
console.log(`📝 Created temporary OpenAPI file: ${tempOpenApiPath}`);
|
|
48
|
+
console.log(`🔄 Generating types using openapi-typescript...`);
|
|
49
|
+
// Use the existing openapi-typescript generator
|
|
50
|
+
const outputPath = path.join(__dirname, "src", "generated", "async-api.ts");
|
|
51
|
+
const generateCommand = `pnpm exec openapi-typescript "${tempOpenApiPath}" --output "${outputPath}"`;
|
|
52
|
+
const { stdout, stderr } = await execAsync(generateCommand);
|
|
53
|
+
if (stderr && !stderr.includes("Warning")) {
|
|
54
|
+
console.warn("openapi-typescript warnings:", stderr);
|
|
55
|
+
}
|
|
56
|
+
if (stdout) {
|
|
57
|
+
console.log("openapi-typescript output:", stdout);
|
|
58
|
+
}
|
|
59
|
+
// Now add channels interface for WebSocket connections
|
|
60
|
+
console.log(`🔄 Adding channels interface for WebSocket connections...`);
|
|
61
|
+
// Parse the original AsyncAPI spec to extract channels
|
|
62
|
+
let channelsInterface = "\nexport interface channels {\n";
|
|
63
|
+
if (asyncApiSpec.channels) {
|
|
64
|
+
for (const [channelPath, channelData] of Object.entries(asyncApiSpec.channels)) {
|
|
65
|
+
const channel = channelData;
|
|
66
|
+
channelsInterface += ` "${channelPath}": {\n`;
|
|
67
|
+
// Add description if available
|
|
68
|
+
if (channel.description) {
|
|
69
|
+
channelsInterface += ` /** @description ${channel.description} */\n`;
|
|
70
|
+
}
|
|
71
|
+
// Add operations (publish/subscribe)
|
|
72
|
+
if (channel.publish) {
|
|
73
|
+
channelsInterface += ` publish: {\n`;
|
|
74
|
+
if (channel.publish.description) {
|
|
75
|
+
channelsInterface += ` /** @description ${channel.publish.description} */\n`;
|
|
76
|
+
}
|
|
77
|
+
if (channel.publish.message) {
|
|
78
|
+
const messageRef = channel.publish.message.$ref || channel.publish.message;
|
|
79
|
+
const messageName = typeof messageRef === "string" && messageRef.includes("#/")
|
|
80
|
+
? messageRef.replace("#/components/messages/", "")
|
|
81
|
+
: messageRef;
|
|
82
|
+
channelsInterface += ` message: components["schemas"]["${messageName}"];\n`;
|
|
83
|
+
}
|
|
84
|
+
channelsInterface += ` };\n`;
|
|
85
|
+
}
|
|
86
|
+
if (channel.subscribe) {
|
|
87
|
+
channelsInterface += ` subscribe: {\n`;
|
|
88
|
+
if (channel.subscribe.description) {
|
|
89
|
+
channelsInterface += ` /** @description ${channel.subscribe.description} */\n`;
|
|
90
|
+
}
|
|
91
|
+
if (channel.subscribe.message) {
|
|
92
|
+
const messageRef = channel.subscribe.message.$ref || channel.subscribe.message;
|
|
93
|
+
const messageName = typeof messageRef === "string" && messageRef.includes("#/")
|
|
94
|
+
? messageRef.replace("#/components/messages/", "")
|
|
95
|
+
: messageRef;
|
|
96
|
+
channelsInterface += ` message: components["schemas"]["${messageName}"];\n`;
|
|
97
|
+
}
|
|
98
|
+
channelsInterface += ` };\n`;
|
|
99
|
+
}
|
|
100
|
+
channelsInterface += ` };\n`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
channelsInterface += "}\n";
|
|
104
|
+
// Append channels interface to the generated file
|
|
105
|
+
const generatedContent = fs.readFileSync(outputPath, "utf8");
|
|
106
|
+
const brandedContent = generatedContent + channelsInterface;
|
|
107
|
+
fs.writeFileSync(outputPath, brandedContent);
|
|
108
|
+
// Clean up temporary file
|
|
109
|
+
fs.unlinkSync(tempOpenApiPath);
|
|
110
|
+
console.log(`🧹 Cleaned up temporary file`);
|
|
111
|
+
console.log(`✅ Generated AsyncAPI types with channels interface: ${outputPath}`);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
console.error("❌ Error generating AsyncAPI types:", error.message);
|
|
115
|
+
// Clean up temporary file on error
|
|
116
|
+
const tempOpenApiPath = path.join(__dirname, "temp-asyncapi-as-openapi.yaml");
|
|
117
|
+
if (fs.existsSync(tempOpenApiPath)) {
|
|
118
|
+
fs.unlinkSync(tempOpenApiPath);
|
|
119
|
+
console.log(`🧹 Cleaned up temporary file after error`);
|
|
120
|
+
}
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// Export the function for use in other scripts
|
|
125
|
+
export { generateAsyncApiTypes };
|
|
126
|
+
// Only run directly if this file is executed as main module
|
|
127
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
128
|
+
generateAsyncApiTypes();
|
|
129
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
3
|
+
import { join, dirname } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = dirname(__filename);
|
|
7
|
+
function generateSchemaModule(yamlPath, outputPath, exportName) {
|
|
8
|
+
const yamlContent = readFileSync(yamlPath, "utf-8");
|
|
9
|
+
// Escape the YAML content for TypeScript string literal
|
|
10
|
+
const escapedContent = yamlContent
|
|
11
|
+
.replace(/\\/g, "\\\\")
|
|
12
|
+
.replace(/`/g, "\\`")
|
|
13
|
+
.replace(/\$\{/g, "\\${");
|
|
14
|
+
const tsContent = `// Auto-generated file - do not edit manually
|
|
15
|
+
// Generated from ${yamlPath}
|
|
16
|
+
|
|
17
|
+
export const ${exportName} = \`${escapedContent}\`;
|
|
18
|
+
`;
|
|
19
|
+
writeFileSync(outputPath, tsContent, "utf-8");
|
|
20
|
+
console.log(`✅ Generated ${outputPath} from ${yamlPath}`);
|
|
21
|
+
}
|
|
22
|
+
// Export the function for use in other scripts
|
|
23
|
+
export { generateSchemaModule };
|
|
24
|
+
// Only run directly if this file is executed as main module
|
|
25
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
26
|
+
const projectRoot = join(__dirname, "..");
|
|
27
|
+
const specsDir = join(projectRoot, "specs");
|
|
28
|
+
const outputDir = join(projectRoot, "src", "generated");
|
|
29
|
+
// Generate modules for both schemas
|
|
30
|
+
generateSchemaModule(join(specsDir, "openapi_2025-08-31.yaml"), join(outputDir, "openapi-schema.ts"), "OPENAPI_SCHEMA");
|
|
31
|
+
generateSchemaModule(join(specsDir, "asyncapi_2025-08-31.yaml"), join(outputDir, "asyncapi-schema.ts"), "ASYNCAPI_SCHEMA");
|
|
32
|
+
console.log("🎉 All schema modules generated successfully!");
|
|
33
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { EventEmitter } from "eventemitter3";
|
|
2
|
+
import { PackageIdString } from "./valueTypes.js";
|
|
3
|
+
/**
|
|
4
|
+
* A specialized Map wrapper for template emitters that uses partiallyQualified
|
|
5
|
+
* template IDs for comparison to ensure consistent lookup regardless of package ID.
|
|
6
|
+
*/
|
|
7
|
+
export declare class TemplateEmitterMap {
|
|
8
|
+
private emitters;
|
|
9
|
+
/**
|
|
10
|
+
* Set an EventEmitter for a specific template ID
|
|
11
|
+
* @param templateId The full template ID
|
|
12
|
+
* @param emitter The EventEmitter instance
|
|
13
|
+
*/
|
|
14
|
+
set(templateId: PackageIdString, emitter: EventEmitter): void;
|
|
15
|
+
/**
|
|
16
|
+
* Get the EventEmitter for a specific template ID
|
|
17
|
+
* @param templateId The template ID (full or partial)
|
|
18
|
+
* @returns The EventEmitter or undefined if not found
|
|
19
|
+
*/
|
|
20
|
+
get(templateId: PackageIdString): EventEmitter | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Check if an emitter exists for a specific template ID
|
|
23
|
+
* @param templateId The template ID (full or partial)
|
|
24
|
+
* @returns True if an emitter exists, false otherwise
|
|
25
|
+
*/
|
|
26
|
+
has(templateId: PackageIdString): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Get all emitters in the map
|
|
29
|
+
* @returns An iterator of all EventEmitter instances
|
|
30
|
+
*/
|
|
31
|
+
values(): IterableIterator<EventEmitter>;
|
|
32
|
+
/**
|
|
33
|
+
* Clear all emitters from the map
|
|
34
|
+
*/
|
|
35
|
+
clear(): void;
|
|
36
|
+
/**
|
|
37
|
+
* Get the number of emitters in the map
|
|
38
|
+
*/
|
|
39
|
+
get size(): number;
|
|
40
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { partiallyQualified } from "./util.js";
|
|
2
|
+
/**
|
|
3
|
+
* A specialized Map wrapper for template emitters that uses partiallyQualified
|
|
4
|
+
* template IDs for comparison to ensure consistent lookup regardless of package ID.
|
|
5
|
+
*/
|
|
6
|
+
export class TemplateEmitterMap {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.emitters = new Map();
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Set an EventEmitter for a specific template ID
|
|
12
|
+
* @param templateId The full template ID
|
|
13
|
+
* @param emitter The EventEmitter instance
|
|
14
|
+
*/
|
|
15
|
+
set(templateId, emitter) {
|
|
16
|
+
const partialId = partiallyQualified(templateId);
|
|
17
|
+
this.emitters.set(partialId, { fullId: templateId, emitter });
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get the EventEmitter for a specific template ID
|
|
21
|
+
* @param templateId The template ID (full or partial)
|
|
22
|
+
* @returns The EventEmitter or undefined if not found
|
|
23
|
+
*/
|
|
24
|
+
get(templateId) {
|
|
25
|
+
const partialId = partiallyQualified(templateId);
|
|
26
|
+
return this.emitters.get(partialId)?.emitter;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Check if an emitter exists for a specific template ID
|
|
30
|
+
* @param templateId The template ID (full or partial)
|
|
31
|
+
* @returns True if an emitter exists, false otherwise
|
|
32
|
+
*/
|
|
33
|
+
has(templateId) {
|
|
34
|
+
const partialId = partiallyQualified(templateId);
|
|
35
|
+
return this.emitters.has(partialId);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get all emitters in the map
|
|
39
|
+
* @returns An iterator of all EventEmitter instances
|
|
40
|
+
*/
|
|
41
|
+
values() {
|
|
42
|
+
return Array.from(this.emitters.values())
|
|
43
|
+
.map(entry => entry.emitter)[Symbol.iterator]();
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Clear all emitters from the map
|
|
47
|
+
*/
|
|
48
|
+
clear() {
|
|
49
|
+
this.emitters.clear();
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get the number of emitters in the map
|
|
53
|
+
*/
|
|
54
|
+
get size() {
|
|
55
|
+
return this.emitters.size;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Low-level HTTP client for Canton's OpenAPI v2 JSON API
|
|
3
|
+
*
|
|
4
|
+
* This client provides direct, type-safe access to the raw OpenAPI endpoints
|
|
5
|
+
* with minimal abstraction. It handles authentication and request/response
|
|
6
|
+
* serialization but preserves the original API structure and data types.
|
|
7
|
+
*
|
|
8
|
+
* Use this when you need full control over the API calls or access to
|
|
9
|
+
* endpoints not covered by the higher-level Ledger abstraction.
|
|
10
|
+
*/
|
|
11
|
+
import { operations } from "./generated/api.js";
|
|
12
|
+
import { ValidationMode } from "./validation.js";
|
|
13
|
+
type SubmitAndWaitOperation = operations["postV2CommandsSubmit-and-wait"];
|
|
14
|
+
type SubmitAndWaitRequest = SubmitAndWaitOperation["requestBody"]["content"]["application/json"];
|
|
15
|
+
type SubmitAndWaitResponse = SubmitAndWaitOperation["responses"]["200"]["content"]["application/json"];
|
|
16
|
+
type SubmitAndWaitForTransactionOperation = operations["postV2CommandsSubmit-and-wait-for-transaction"];
|
|
17
|
+
type SubmitAndWaitForTransactionRequest = SubmitAndWaitForTransactionOperation["requestBody"]["content"]["application/json"];
|
|
18
|
+
type SubmitAndWaitForTransactionResponse = SubmitAndWaitForTransactionOperation["responses"]["200"]["content"]["application/json"];
|
|
19
|
+
type GetPartiesOperation = operations["getV2Parties"];
|
|
20
|
+
type GetPartiesResponse = GetPartiesOperation["responses"]["200"]["content"]["application/json"];
|
|
21
|
+
type GetUserOperation = operations["getV2UsersUser-id"];
|
|
22
|
+
type GetUserResponse = GetUserOperation["responses"]["200"]["content"]["application/json"];
|
|
23
|
+
type GetUserRightsOperation = operations["getV2UsersUser-idRights"];
|
|
24
|
+
type GetUserRightsResponse = GetUserRightsOperation["responses"]["200"]["content"]["application/json"];
|
|
25
|
+
type QueryActiveContractsOperation = operations["postV2StateActive-contracts"];
|
|
26
|
+
type QueryActiveContractsRequest = QueryActiveContractsOperation["requestBody"]["content"]["application/json"];
|
|
27
|
+
type QueryActiveContractsResponse = QueryActiveContractsOperation["responses"]["200"]["content"]["application/json"];
|
|
28
|
+
type AllocatePartyOperation = operations["postV2Parties"];
|
|
29
|
+
type AllocatePartyRequest = AllocatePartyOperation["requestBody"]["content"]["application/json"];
|
|
30
|
+
type AllocatePartyResponse = AllocatePartyOperation["responses"]["200"]["content"]["application/json"];
|
|
31
|
+
type GetLedgerEndOperation = operations["getV2StateLedger-end"];
|
|
32
|
+
type GetLedgerEndResponse = GetLedgerEndOperation["responses"]["200"]["content"]["application/json"];
|
|
33
|
+
export type transaction_shape = "TRANSACTION_SHAPE_ACS_DELTA" | "TRANSACTION_SHAPE_LEDGER_EFFECTS";
|
|
34
|
+
export interface TypedHttpClientConfig {
|
|
35
|
+
token: string;
|
|
36
|
+
baseUrl: string;
|
|
37
|
+
validation?: ValidationMode;
|
|
38
|
+
openApiSchemaPath?: string;
|
|
39
|
+
}
|
|
40
|
+
export declare class TypedHttpClient {
|
|
41
|
+
readonly token: string;
|
|
42
|
+
readonly baseUrl: string;
|
|
43
|
+
private validator?;
|
|
44
|
+
constructor(config: TypedHttpClientConfig);
|
|
45
|
+
private request;
|
|
46
|
+
submitAndWait(commands: SubmitAndWaitRequest): Promise<SubmitAndWaitResponse>;
|
|
47
|
+
submitAndWaitForTransaction(commands: SubmitAndWaitForTransactionRequest): Promise<SubmitAndWaitForTransactionResponse>;
|
|
48
|
+
getParties(): Promise<GetPartiesResponse>;
|
|
49
|
+
getUserInfo(userId: string): Promise<GetUserResponse>;
|
|
50
|
+
getUserRights(userId: string): Promise<GetUserRightsResponse>;
|
|
51
|
+
queryActiveContracts(queryRequest: QueryActiveContractsRequest): Promise<QueryActiveContractsResponse>;
|
|
52
|
+
allocateParty(request: AllocatePartyRequest): Promise<AllocatePartyResponse>;
|
|
53
|
+
getLedgerEnd(): Promise<GetLedgerEndResponse>;
|
|
54
|
+
}
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { SchemaValidator } from "./validation.js";
|
|
2
|
+
import fetch from "cross-fetch";
|
|
3
|
+
export class TypedHttpClient {
|
|
4
|
+
constructor(config) {
|
|
5
|
+
this.token = config.token;
|
|
6
|
+
this.baseUrl = config.baseUrl;
|
|
7
|
+
this.validator = config.validation
|
|
8
|
+
? new SchemaValidator("openapi", config.validation)
|
|
9
|
+
: undefined;
|
|
10
|
+
}
|
|
11
|
+
async request(path, method, body, responseSchemaName, isArrayResponse) {
|
|
12
|
+
const url = `${this.baseUrl}${path}`;
|
|
13
|
+
const requestInit = {
|
|
14
|
+
method,
|
|
15
|
+
headers: {
|
|
16
|
+
Authorization: `Bearer ${this.token}`,
|
|
17
|
+
"Content-Type": "application/json",
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
if (body) {
|
|
21
|
+
requestInit.body = JSON.stringify(body);
|
|
22
|
+
}
|
|
23
|
+
const response = await fetch(url, requestInit);
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
26
|
+
}
|
|
27
|
+
const parsed = await response.json();
|
|
28
|
+
if (this.validator && responseSchemaName) {
|
|
29
|
+
if (isArrayResponse) {
|
|
30
|
+
return this.validator.validateArraySchema(parsed, responseSchemaName);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
return this.validator.validateSchema(parsed, responseSchemaName);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return parsed;
|
|
37
|
+
}
|
|
38
|
+
async submitAndWait(commands) {
|
|
39
|
+
return this.request("/v2/commands/submit-and-wait", "POST", commands, "#/components/schemas/SubmitAndWaitResponse");
|
|
40
|
+
}
|
|
41
|
+
async submitAndWaitForTransaction(commands) {
|
|
42
|
+
return this.request("/v2/commands/submit-and-wait-for-transaction", "POST", commands, "#/components/schemas/JsSubmitAndWaitForTransactionResponse");
|
|
43
|
+
}
|
|
44
|
+
async getParties() {
|
|
45
|
+
return this.request("/v2/parties", "GET", undefined, "#/components/schemas/GetPartiesResponse");
|
|
46
|
+
}
|
|
47
|
+
async getUserInfo(userId) {
|
|
48
|
+
return this.request(`/v2/users/${userId}`, "GET", undefined, "#/components/schemas/GetUserResponse");
|
|
49
|
+
}
|
|
50
|
+
async getUserRights(userId) {
|
|
51
|
+
return this.request(`/v2/users/${userId}/rights`, "GET", undefined, "#/components/schemas/ListUserRightsResponse");
|
|
52
|
+
}
|
|
53
|
+
async queryActiveContracts(queryRequest) {
|
|
54
|
+
return this.request("/v2/state/active-contracts", "POST", queryRequest, "#/components/schemas/JsGetActiveContractsResponse", true);
|
|
55
|
+
}
|
|
56
|
+
async allocateParty(request) {
|
|
57
|
+
return this.request("/v2/parties", "POST", request, "#/components/schemas/AllocatePartyResponse");
|
|
58
|
+
}
|
|
59
|
+
async getLedgerEnd() {
|
|
60
|
+
return this.request("/v2/state/ledger-end", "GET", undefined, "#/components/schemas/GetLedgerEndResponse");
|
|
61
|
+
}
|
|
62
|
+
}
|