@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.
Files changed (98) hide show
  1. package/BUILD.md +125 -0
  2. package/LICENSE +17 -0
  3. package/README.md +97 -0
  4. package/lib/scripts/build.d.ts +9 -0
  5. package/lib/scripts/build.js +366 -0
  6. package/lib/scripts/enhance-types.d.ts +3 -0
  7. package/lib/scripts/enhance-types.js +174 -0
  8. package/lib/scripts/generate-asyncapi-types.d.ts +9 -0
  9. package/lib/scripts/generate-asyncapi-types.js +129 -0
  10. package/lib/scripts/generate-schema-modules.d.ts +3 -0
  11. package/lib/scripts/generate-schema-modules.js +33 -0
  12. package/lib/src/TemplateEmitterMap.d.ts +40 -0
  13. package/lib/src/TemplateEmitterMap.js +57 -0
  14. package/lib/src/client.d.ts +55 -0
  15. package/lib/src/client.js +62 -0
  16. package/lib/src/generated/api.d.ts +7147 -0
  17. package/lib/src/generated/api.js +1 -0
  18. package/lib/src/generated/async-api.d.ts +1665 -0
  19. package/lib/src/generated/async-api.js +1 -0
  20. package/lib/src/generated/asyncapi-schema.d.ts +1 -0
  21. package/lib/src/generated/asyncapi-schema.js +2233 -0
  22. package/lib/src/generated/openapi-schema.d.ts +1 -0
  23. package/lib/src/generated/openapi-schema.js +7321 -0
  24. package/lib/src/generated/sdk-version.d.ts +1 -0
  25. package/lib/src/generated/sdk-version.js +3 -0
  26. package/lib/src/index.d.ts +8 -0
  27. package/lib/src/index.js +8 -0
  28. package/lib/src/ledger.d.ts +188 -0
  29. package/lib/src/ledger.js +849 -0
  30. package/lib/src/ledger.test.d.ts +1 -0
  31. package/lib/src/ledger.test.js +23 -0
  32. package/lib/src/logger.d.ts +41 -0
  33. package/lib/src/logger.js +56 -0
  34. package/lib/src/multistream.d.ts +47 -0
  35. package/lib/src/multistream.js +123 -0
  36. package/lib/src/translate.d.ts +5 -0
  37. package/lib/src/translate.js +30 -0
  38. package/lib/src/types.d.ts +201 -0
  39. package/lib/src/types.js +1 -0
  40. package/lib/src/util.d.ts +3 -0
  41. package/lib/src/util.js +7 -0
  42. package/lib/src/validation.d.ts +27 -0
  43. package/lib/src/validation.js +182 -0
  44. package/lib/src/valueTypes.d.ts +34 -0
  45. package/lib/src/valueTypes.js +76 -0
  46. package/lib/src/websocket.d.ts +69 -0
  47. package/lib/src/websocket.js +125 -0
  48. package/lib/tsconfig.tsbuildinfo +1 -0
  49. package/lib-lite/scripts/build.d.ts +9 -0
  50. package/lib-lite/scripts/build.js +366 -0
  51. package/lib-lite/scripts/enhance-types.d.ts +3 -0
  52. package/lib-lite/scripts/enhance-types.js +174 -0
  53. package/lib-lite/scripts/generate-asyncapi-types.d.ts +9 -0
  54. package/lib-lite/scripts/generate-asyncapi-types.js +129 -0
  55. package/lib-lite/scripts/generate-schema-modules.d.ts +3 -0
  56. package/lib-lite/scripts/generate-schema-modules.js +33 -0
  57. package/lib-lite/src/TemplateEmitterMap.d.ts +40 -0
  58. package/lib-lite/src/TemplateEmitterMap.js +57 -0
  59. package/lib-lite/src/client.d.ts +55 -0
  60. package/lib-lite/src/client.js +62 -0
  61. package/lib-lite/src/generated/api.d.ts +7147 -0
  62. package/lib-lite/src/generated/api.js +1 -0
  63. package/lib-lite/src/generated/async-api.d.ts +1665 -0
  64. package/lib-lite/src/generated/async-api.js +1 -0
  65. package/lib-lite/src/generated/asyncapi-schema.d.ts +1 -0
  66. package/lib-lite/src/generated/asyncapi-schema.js +2 -0
  67. package/lib-lite/src/generated/openapi-schema.d.ts +1 -0
  68. package/lib-lite/src/generated/openapi-schema.js +2 -0
  69. package/lib-lite/src/generated/sdk-version.d.ts +1 -0
  70. package/lib-lite/src/generated/sdk-version.js +3 -0
  71. package/lib-lite/src/index.d.ts +8 -0
  72. package/lib-lite/src/index.js +8 -0
  73. package/lib-lite/src/ledger.d.ts +188 -0
  74. package/lib-lite/src/ledger.js +849 -0
  75. package/lib-lite/src/ledger.test.d.ts +1 -0
  76. package/lib-lite/src/ledger.test.js +23 -0
  77. package/lib-lite/src/logger.d.ts +41 -0
  78. package/lib-lite/src/logger.js +56 -0
  79. package/lib-lite/src/multistream.d.ts +47 -0
  80. package/lib-lite/src/multistream.js +123 -0
  81. package/lib-lite/src/translate.d.ts +5 -0
  82. package/lib-lite/src/translate.js +30 -0
  83. package/lib-lite/src/types.d.ts +201 -0
  84. package/lib-lite/src/types.js +1 -0
  85. package/lib-lite/src/util.d.ts +3 -0
  86. package/lib-lite/src/util.js +7 -0
  87. package/lib-lite/src/validation.d.ts +14 -0
  88. package/lib-lite/src/validation.js +31 -0
  89. package/lib-lite/src/valueTypes.d.ts +34 -0
  90. package/lib-lite/src/valueTypes.js +76 -0
  91. package/lib-lite/src/websocket.d.ts +69 -0
  92. package/lib-lite/src/websocket.js +125 -0
  93. package/lib-lite/tsconfig.temp.tsbuildinfo +1 -0
  94. package/package.json +72 -0
  95. package/scripts/build.ts +456 -0
  96. package/scripts/enhance-types.ts +223 -0
  97. package/scripts/generate-asyncapi-types.ts +158 -0
  98. package/scripts/generate-schema-modules.ts +52 -0
@@ -0,0 +1,366 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Unified build script that uses SDK version-based spec files and runs all build steps
4
+ */
5
+ import { join, dirname } from "path";
6
+ import { fileURLToPath } from "url";
7
+ import { exec } from "child_process";
8
+ import { promisify } from "util";
9
+ import { readdir, writeFile, mkdir, unlink, copyFile } from "fs/promises";
10
+ import { existsSync } from "fs";
11
+ import { brandTypes } from "./enhance-types.js";
12
+ import { generateSchemaModule } from "./generate-schema-modules.js";
13
+ import { generateAsyncApiTypes } from "./generate-asyncapi-types.js";
14
+ const execAsync = promisify(exec);
15
+ const __filename = fileURLToPath(import.meta.url);
16
+ const __dirname = dirname(__filename);
17
+ const projectRoot = join(__dirname, "..");
18
+ /**
19
+ * Get SDK version from command line arguments or discover latest
20
+ */
21
+ function getSdkVersion() {
22
+ const args = process.argv.slice(2);
23
+ const versionArg = args.find(arg => arg.startsWith("--sdk-version="));
24
+ if (versionArg) {
25
+ return versionArg.split("=")[1];
26
+ }
27
+ return undefined;
28
+ }
29
+ /**
30
+ * Discover spec files based on SDK version or find latest version-based specs
31
+ */
32
+ async function discoverSpecFiles(sdkVersion) {
33
+ const specsDir = join(projectRoot, "specs");
34
+ const files = await readdir(specsDir);
35
+ if (sdkVersion) {
36
+ console.log(`šŸ” Looking for spec files with SDK version: ${sdkVersion}`);
37
+ const openApiFile = `openapi_${sdkVersion}.yaml`;
38
+ const asyncApiFile = `asyncapi_${sdkVersion}.yaml`;
39
+ const result = {};
40
+ if (files.includes(openApiFile)) {
41
+ result.openapi = join(specsDir, openApiFile);
42
+ console.log(`šŸ“„ Found OpenAPI spec: ${openApiFile}`);
43
+ }
44
+ else {
45
+ console.warn(`āš ļø OpenAPI spec not found: ${openApiFile}`);
46
+ }
47
+ if (files.includes(asyncApiFile)) {
48
+ result.asyncapi = join(specsDir, asyncApiFile);
49
+ console.log(`šŸ“„ Found AsyncAPI spec: ${asyncApiFile}`);
50
+ }
51
+ else {
52
+ console.warn(`āš ļø AsyncAPI spec not found: ${asyncApiFile}`);
53
+ }
54
+ return { specs: result, version: sdkVersion };
55
+ }
56
+ else {
57
+ console.log("šŸ” Auto-discovering latest version-based spec files...");
58
+ // Look for files with version pattern (not date pattern)
59
+ const versionPattern = /\d+\.\d+\.\d+(-snapshot\.\d{8}\.\d+)?/;
60
+ const openApiFiles = files
61
+ .filter(f => f.startsWith("openapi_") && f.endsWith(".yaml") && versionPattern.test(f))
62
+ .sort()
63
+ .reverse();
64
+ const asyncApiFiles = files
65
+ .filter(f => f.startsWith("asyncapi_") && f.endsWith(".yaml") && versionPattern.test(f))
66
+ .sort()
67
+ .reverse();
68
+ const result = {};
69
+ let detectedVersion = "";
70
+ if (openApiFiles.length > 0) {
71
+ result.openapi = join(specsDir, openApiFiles[0]);
72
+ console.log(`šŸ“„ Found OpenAPI spec: ${openApiFiles[0]}`);
73
+ // Extract version from filename
74
+ const match = openApiFiles[0].match(/openapi_(.+)\.yaml/);
75
+ if (match)
76
+ detectedVersion = match[1];
77
+ }
78
+ if (asyncApiFiles.length > 0) {
79
+ result.asyncapi = join(specsDir, asyncApiFiles[0]);
80
+ console.log(`šŸ“„ Found AsyncAPI spec: ${asyncApiFiles[0]}`);
81
+ // Use version from AsyncAPI if OpenAPI wasn't found
82
+ if (!detectedVersion) {
83
+ const match = asyncApiFiles[0].match(/asyncapi_(.+)\.yaml/);
84
+ if (match)
85
+ detectedVersion = match[1];
86
+ }
87
+ }
88
+ if (!detectedVersion) {
89
+ throw new Error("Could not detect SDK version from spec files. Please specify --sdk-version=<version>");
90
+ }
91
+ console.log(`šŸŽÆ Detected SDK version: ${detectedVersion}`);
92
+ return { specs: result, version: detectedVersion };
93
+ }
94
+ }
95
+ /**
96
+ * Generate OpenAPI types using openapi-typescript
97
+ */
98
+ async function generateTypes(specPath) {
99
+ console.log("šŸ”„ Generating OpenAPI types...");
100
+ const outputPath = join(projectRoot, "src", "generated", "api.ts");
101
+ try {
102
+ const { stdout, stderr } = await execAsync(`pnpm exec openapi-typescript "${specPath}" --output "${outputPath}"`);
103
+ if (stderr && !stderr.includes("Warning")) {
104
+ console.warn("openapi-typescript warnings:", stderr);
105
+ }
106
+ if (stdout) {
107
+ console.log(stdout);
108
+ }
109
+ console.log("āœ… Generated OpenAPI types");
110
+ }
111
+ catch (error) {
112
+ console.error("āŒ Error generating OpenAPI types:", error.message);
113
+ throw error;
114
+ }
115
+ }
116
+ /**
117
+ * Generate schema modules for both OpenAPI and AsyncAPI
118
+ */
119
+ function generateSchemaModules(specs) {
120
+ console.log("šŸ”„ Generating schema modules...");
121
+ const outputDir = join(projectRoot, "src", "generated");
122
+ if (specs.openapi) {
123
+ generateSchemaModule(specs.openapi, join(outputDir, "openapi-schema.ts"), "OPENAPI_SCHEMA");
124
+ }
125
+ if (specs.asyncapi) {
126
+ generateSchemaModule(specs.asyncapi, join(outputDir, "asyncapi-schema.ts"), "ASYNCAPI_SCHEMA");
127
+ }
128
+ console.log("āœ… All schema modules generated");
129
+ }
130
+ /**
131
+ * Ensure the generated directory exists
132
+ */
133
+ async function ensureGeneratedDirectory() {
134
+ const outputDir = join(projectRoot, "src", "generated");
135
+ await mkdir(outputDir, { recursive: true });
136
+ }
137
+ /**
138
+ * Clean up any temporary files
139
+ */
140
+ async function cleanupTempFiles() {
141
+ const generatedDir = join(projectRoot, "src", "generated");
142
+ const tempFiles = [
143
+ join(generatedDir, "api.ts.tmp"),
144
+ join(generatedDir, "async-api.ts.tmp"),
145
+ join(projectRoot, "temp-asyncapi-as-openapi.yaml"),
146
+ ];
147
+ for (const tempFile of tempFiles) {
148
+ if (existsSync(tempFile)) {
149
+ try {
150
+ await unlink(tempFile);
151
+ console.log(`🧹 Cleaned up ${tempFile}`);
152
+ }
153
+ catch (error) {
154
+ // Ignore cleanup errors, but log them
155
+ console.warn(`āš ļø Could not clean up ${tempFile}:`, error.message);
156
+ }
157
+ }
158
+ }
159
+ }
160
+ /**
161
+ * Generate SDK version constant file
162
+ */
163
+ async function generateSdkVersionFile(version) {
164
+ console.log("šŸ”„ Generating SDK version constant...");
165
+ const outputDir = join(projectRoot, "src", "generated");
166
+ const outputPath = join(outputDir, "sdk-version.ts");
167
+ const content = `// Auto-generated file - do not edit manually
168
+ // Generated from SDK version: ${version}
169
+
170
+ export const SDK_VERSION = "${version}";
171
+ `;
172
+ await writeFile(outputPath, content, "utf-8");
173
+ console.log(`āœ… Generated SDK version constant: ${version}`);
174
+ }
175
+ /**
176
+ * Brand API types with value.proto constraints (in-place)
177
+ */
178
+ function brandApiTypes() {
179
+ console.log("šŸ”„ Branding API types in-place...");
180
+ // Brand OpenAPI types (overwrite original file)
181
+ const apiPath = join(projectRoot, "src", "generated", "api.ts");
182
+ brandTypes(apiPath, apiPath, "OpenAPI");
183
+ // Brand AsyncAPI types (overwrite original file)
184
+ const asyncApiPath = join(projectRoot, "src", "generated", "async-api.ts");
185
+ brandTypes(asyncApiPath, asyncApiPath, "AsyncAPI");
186
+ console.log("āœ… All types branded in-place");
187
+ }
188
+ /**
189
+ * Create stub schema files for lite build
190
+ */
191
+ async function createStubSchemaFiles() {
192
+ console.log("šŸ”„ Creating stub schema files for lite build...");
193
+ const outputDir = join(projectRoot, "src", "generated");
194
+ const openApiStub = `// Stub file for lite build - no schema content
195
+ export const OPENAPI_SCHEMA = "";
196
+ `;
197
+ const asyncApiStub = `// Stub file for lite build - no schema content
198
+ export const ASYNCAPI_SCHEMA = "";
199
+ `;
200
+ await writeFile(join(outputDir, "openapi-schema.ts"), openApiStub, "utf-8");
201
+ await writeFile(join(outputDir, "asyncapi-schema.ts"), asyncApiStub, "utf-8");
202
+ console.log("āœ… Created stub schema files");
203
+ }
204
+ /**
205
+ * Replace validation module with lite version in lib-lite
206
+ */
207
+ async function useLiteValidation() {
208
+ console.log("šŸ”„ Replacing validation with lite version...");
209
+ const libLiteDir = join(projectRoot, "lib-lite", "src");
210
+ // Copy validation-lite.js over validation.js
211
+ const liteValidationJs = join(libLiteDir, "validation-lite.js");
212
+ const validationJs = join(libLiteDir, "validation.js");
213
+ if (existsSync(liteValidationJs)) {
214
+ await copyFile(liteValidationJs, validationJs);
215
+ await unlink(liteValidationJs);
216
+ console.log("āœ… Replaced validation.js with lite version");
217
+ }
218
+ // Copy validation-lite.d.ts over validation.d.ts
219
+ const liteValidationDts = join(libLiteDir, "validation-lite.d.ts");
220
+ const validationDts = join(libLiteDir, "validation.d.ts");
221
+ if (existsSync(liteValidationDts)) {
222
+ await copyFile(liteValidationDts, validationDts);
223
+ await unlink(liteValidationDts);
224
+ console.log("āœ… Replaced validation.d.ts with lite version");
225
+ }
226
+ // Also remove validation-lite files from lib/ (they shouldn't be there)
227
+ const libDir = join(projectRoot, "lib", "src");
228
+ const libLiteValidationJs = join(libDir, "validation-lite.js");
229
+ const libLiteValidationDts = join(libDir, "validation-lite.d.ts");
230
+ if (existsSync(libLiteValidationJs)) {
231
+ await unlink(libLiteValidationJs);
232
+ }
233
+ if (existsSync(libLiteValidationDts)) {
234
+ await unlink(libLiteValidationDts);
235
+ }
236
+ }
237
+ /**
238
+ * Compile TypeScript code
239
+ */
240
+ async function compileTypeScript(outDir = "lib") {
241
+ console.log(`šŸ”„ Compiling TypeScript to ${outDir}...`);
242
+ try {
243
+ let tscCommand = `pnpm exec tsc -p ${join(projectRoot, "tsconfig.json")}`;
244
+ let aliasCommand = `pnpm exec tsc-alias -p ${join(projectRoot, "tsconfig.json")}`;
245
+ if (outDir !== "lib") {
246
+ // Create a temporary tsconfig for different output directory
247
+ const tempTsConfig = {
248
+ extends: "./tsconfig.json",
249
+ compilerOptions: {
250
+ outDir: `./${outDir}`,
251
+ declarationDir: `./${outDir}`,
252
+ },
253
+ };
254
+ const tempConfigPath = join(projectRoot, "tsconfig.temp.json");
255
+ await writeFile(tempConfigPath, JSON.stringify(tempTsConfig, null, 2), "utf-8");
256
+ tscCommand = `pnpm exec tsc -p ${tempConfigPath}`;
257
+ aliasCommand = `pnpm exec tsc-alias -p ${tempConfigPath}`;
258
+ }
259
+ // Run tsc first (tsc-alias depends on tsc output)
260
+ const { stdout: tscStdout, stderr: tscStderr } = await execAsync(tscCommand);
261
+ if (tscStderr) {
262
+ console.error("TypeScript compiler errors/warnings:");
263
+ console.error(tscStderr);
264
+ }
265
+ if (tscStdout) {
266
+ console.log("TypeScript compiler output:");
267
+ console.log(tscStdout);
268
+ }
269
+ // Run tsc-alias after tsc completes
270
+ const { stdout: aliasStdout, stderr: aliasStderr } = await execAsync(aliasCommand);
271
+ if (aliasStderr) {
272
+ console.warn("tsc-alias warnings:", aliasStderr);
273
+ }
274
+ if (aliasStdout) {
275
+ console.log(aliasStdout);
276
+ }
277
+ // Clean up temp config if created
278
+ if (outDir !== "lib") {
279
+ const tempConfigPath = join(projectRoot, "tsconfig.temp.json");
280
+ if (existsSync(tempConfigPath)) {
281
+ await unlink(tempConfigPath);
282
+ }
283
+ }
284
+ console.log(`āœ… TypeScript compilation complete for ${outDir}`);
285
+ }
286
+ catch (error) {
287
+ console.error(`āŒ Error compiling TypeScript to ${outDir}:`);
288
+ console.error(error.message);
289
+ // If the error has stdout/stderr, display them
290
+ if (error && typeof error === "object" && "stdout" in error && error.stdout) {
291
+ console.error("TypeScript stdout:");
292
+ console.error(error.stdout);
293
+ }
294
+ if (error && typeof error === "object" && "stderr" in error && error.stderr) {
295
+ console.error("TypeScript stderr:");
296
+ console.error(error.stderr);
297
+ }
298
+ console.error("šŸ’” Note: TypeScript compilation failed, but code generation was successful.");
299
+ console.error("šŸ’” Check the errors above and fix any type issues in your source files.");
300
+ throw error;
301
+ }
302
+ }
303
+ /**
304
+ * Main build function - builds both regular and lite versions
305
+ */
306
+ async function build() {
307
+ console.log("šŸš€ Starting SDK version-aware build process...");
308
+ try {
309
+ // Step 1: Get SDK version and discover spec files
310
+ const sdkVersion = getSdkVersion();
311
+ const { specs, version } = await discoverSpecFiles(sdkVersion);
312
+ if (!specs.openapi && !specs.asyncapi) {
313
+ throw new Error(`No spec files found for SDK version: ${version}`);
314
+ }
315
+ // Step 2: Ensure generated directory exists
316
+ await ensureGeneratedDirectory();
317
+ // Step 3: Generate SDK version constant (independent, can run early)
318
+ const sdkVersionPromise = generateSdkVersionFile(version);
319
+ // Step 4: Run type generation in parallel (all independent)
320
+ const parallelTasks = [];
321
+ if (specs.openapi) {
322
+ parallelTasks.push(generateTypes(specs.openapi));
323
+ }
324
+ if (specs.asyncapi) {
325
+ parallelTasks.push(generateAsyncApiTypes(specs.asyncapi));
326
+ }
327
+ // Wait for SDK version file and all parallel type generation
328
+ await Promise.all([sdkVersionPromise, ...parallelTasks]);
329
+ // Step 5: Brand types (depends on generated types)
330
+ brandApiTypes();
331
+ console.log("šŸŽÆ Building regular version with full schemas...");
332
+ // Step 6a: Generate full schema modules for regular build
333
+ generateSchemaModules(specs);
334
+ // Step 6b: Compile regular TypeScript (depends on all previous steps)
335
+ await compileTypeScript("lib");
336
+ console.log("šŸŽÆ Building lite version without schemas...");
337
+ // Step 7a: Create stub schema files for lite build
338
+ await createStubSchemaFiles();
339
+ // Step 7b: Compile lite TypeScript to separate directory
340
+ await compileTypeScript("lib-lite");
341
+ // Step 7c: Replace validation with lite version (no yaml dependency)
342
+ await useLiteValidation();
343
+ // Step 8: Clean up any temporary files
344
+ await cleanupTempFiles();
345
+ console.log(`šŸŽ‰ Both regular and lite builds completed successfully for version: ${version}!`);
346
+ console.log(`šŸ“¦ Regular build: lib/ (with validation schemas)`);
347
+ console.log(`šŸ“¦ Lite build: lib-lite/ (without validation schemas)`);
348
+ }
349
+ catch (error) {
350
+ console.error("āŒ Build failed:", error.message);
351
+ // Attempt cleanup even on failure
352
+ try {
353
+ await cleanupTempFiles();
354
+ }
355
+ catch (cleanupError) {
356
+ console.warn("āš ļø Could not clean up temp files after build failure");
357
+ }
358
+ process.exit(1);
359
+ }
360
+ }
361
+ // Run the build if this script is executed directly
362
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
363
+ build();
364
+ }
365
+ // Export for use in other scripts
366
+ export { build };
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ declare function brandTypes(inputPath: string, outputPath: string, label?: string): void;
3
+ export { brandTypes };
@@ -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 };