@genesislcap/event-type-codegen 15.17.1 → 15.18.0
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 +15 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +46 -1
- package/dist/index.js.map +1 -1
- package/dist/parse-metadata.d.ts +58 -0
- package/dist/parse-metadata.d.ts.map +1 -0
- package/dist/parse-metadata.js +231 -0
- package/dist/parse-metadata.js.map +1 -0
- package/package.json +3 -3
- package/src/index.ts +65 -1
- package/src/parse-metadata.ts +327 -0
- package/test/parse-metadata.test.ts +208 -0
- package/tsconfig.tsbuildinfo +1 -1
package/README.md
CHANGED
|
@@ -71,6 +71,21 @@ await commitGenesisEvent(
|
|
|
71
71
|
);
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
## Metadata dump path (FUI-2608)
|
|
75
|
+
|
|
76
|
+
Generate from a live-server snapshot instead of (or in addition to) Kotlin source scan:
|
|
77
|
+
|
|
78
|
+
1. In a connected app with `foundation-header`, press **Ctrl/Cmd+Shift+E** (dev builds only) to download `event-metadata.json`.
|
|
79
|
+
2. Run:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
genx generate event-types --from-metadata event-metadata.json
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
This uses inbound JSON Schema DETAILS (field names, `JSON_TYPE`, requiredness) as the contract — useful for comparing against the Kotlin-scan output and for handlers the source scanner misses.
|
|
86
|
+
|
|
87
|
+
Wire field naming for the Kotlin path matches genesis-server `toUpperUnderscoreWithAcronyms` (e.g. `addressLine1` → `ADDRESS_LINE_1`). Date/DateTime Kotlin types map to TypeScript `number` (epoch millis / `JSON_TYPE: integer`).
|
|
88
|
+
|
|
74
89
|
## Migration
|
|
75
90
|
|
|
76
91
|
| App | Action |
|
package/dist/index.d.ts
CHANGED
|
@@ -5,8 +5,17 @@ export type GenerateEventTypesOptions = {
|
|
|
5
5
|
packageGenesis?: {
|
|
6
6
|
eventTypes?: unknown;
|
|
7
7
|
};
|
|
8
|
+
/** When set, generate from a header-captured metadata dump instead of Kotlin sources. */
|
|
9
|
+
fromMetadataPath?: string;
|
|
8
10
|
};
|
|
11
|
+
export declare function generateEventTypesFromMetadata(options: {
|
|
12
|
+
clientRoot: string;
|
|
13
|
+
metadataPath: string;
|
|
14
|
+
output?: string;
|
|
15
|
+
}): Promise<GenerateResult>;
|
|
9
16
|
export declare function generateEventTypes(options: GenerateEventTypesOptions): Promise<GenerateResult | null>;
|
|
10
17
|
export * from './types';
|
|
11
18
|
export { resolveEventTypesConfig } from './config';
|
|
19
|
+
export { parseEventMetadataDump, renderEventTypesFromMetadataContracts, eventNameToDetailsInterfaceName, type EventMetadataDump, type MetadataEventContract, type MetadataField, } from './parse-metadata';
|
|
20
|
+
export { toUpperUnderscoreWithAcronyms, kotlinPropertyToWireField } from './naming';
|
|
12
21
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,wBAAwB,EAAE,cAAc,EAAa,MAAM,SAAS,CAAC;AA4CnF,MAAM,MAAM,yBAAyB,GAAG;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,wBAAwB,CAAC;IAClC,cAAc,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC1C,yFAAyF;IACzF,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,wBAAsB,8BAA8B,CAAC,OAAO,EAAE;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,cAAc,CAAC,CAkC1B;AAED,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CA8DhC;AAED,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AACnD,OAAO,EACL,sBAAsB,EACtB,qCAAqC,EACrC,+BAA+B,EAC/B,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,GACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,6BAA6B,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolveEventTypesConfig = void 0;
|
|
3
|
+
exports.kotlinPropertyToWireField = exports.toUpperUnderscoreWithAcronyms = exports.eventNameToDetailsInterfaceName = exports.renderEventTypesFromMetadataContracts = exports.parseEventMetadataDump = exports.resolveEventTypesConfig = void 0;
|
|
4
|
+
exports.generateEventTypesFromMetadata = generateEventTypesFromMetadata;
|
|
4
5
|
exports.generateEventTypes = generateEventTypes;
|
|
5
6
|
const tslib_1 = require("tslib");
|
|
6
7
|
const node_fs_1 = require("node:fs");
|
|
@@ -11,6 +12,7 @@ const config_1 = require("./config");
|
|
|
11
12
|
const parse_dao_1 = require("./parse-dao");
|
|
12
13
|
const parse_enums_1 = require("./parse-enums");
|
|
13
14
|
const parse_kotlin_1 = require("./parse-kotlin");
|
|
15
|
+
const parse_metadata_1 = require("./parse-metadata");
|
|
14
16
|
const render_1 = require("./render");
|
|
15
17
|
const isDtoScanPath = (filePath) => filePath.replace(/\\/g, '/').includes('/dto/');
|
|
16
18
|
const isExcludedHandlerFile = (filePath) => {
|
|
@@ -45,10 +47,46 @@ const mergeTypeMaps = (primary, secondary) => {
|
|
|
45
47
|
}
|
|
46
48
|
return merged;
|
|
47
49
|
};
|
|
50
|
+
function generateEventTypesFromMetadata(options) {
|
|
51
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
52
|
+
var _a;
|
|
53
|
+
const metadataPath = (0, node_path_1.resolve)(options.clientRoot, options.metadataPath);
|
|
54
|
+
if (!(0, node_fs_1.existsSync)(metadataPath)) {
|
|
55
|
+
throw new Error(`eventTypes: metadata dump not found at ${metadataPath}`);
|
|
56
|
+
}
|
|
57
|
+
let dump;
|
|
58
|
+
try {
|
|
59
|
+
dump = JSON.parse((0, node_fs_1.readFileSync)(metadataPath, 'utf8'));
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
throw new Error(`eventTypes: failed to parse metadata JSON dump at ${metadataPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
63
|
+
}
|
|
64
|
+
const { contracts, enums, unmapped } = (0, parse_metadata_1.parseEventMetadataDump)(dump);
|
|
65
|
+
const contents = (0, parse_metadata_1.renderEventTypesFromMetadataContracts)(contracts, enums, unmapped);
|
|
66
|
+
const outputRel = (_a = options.output) !== null && _a !== void 0 ? _a : 'src/generated/genesis-event-types.ts';
|
|
67
|
+
const outputPath = (0, node_path_1.resolve)(options.clientRoot, outputRel);
|
|
68
|
+
yield (0, promises_1.mkdir)((0, node_path_1.dirname)(outputPath), { recursive: true });
|
|
69
|
+
yield (0, promises_1.writeFile)(outputPath, contents, 'utf8');
|
|
70
|
+
consola_1.consola.info(`eventTypes: wrote ${outputPath} from metadata (${contracts.length} events, ${unmapped.length} unmapped)`);
|
|
71
|
+
return {
|
|
72
|
+
outputPath,
|
|
73
|
+
eventCount: contracts.length,
|
|
74
|
+
dtoCount: contracts.length,
|
|
75
|
+
unmappedCount: unmapped.length,
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
}
|
|
48
79
|
function generateEventTypes(options) {
|
|
49
80
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
50
81
|
var _a;
|
|
51
82
|
const resolved = (_a = options.config) !== null && _a !== void 0 ? _a : (0, config_1.resolveEventTypesConfig)(options.clientRoot, undefined, options.packageGenesis);
|
|
83
|
+
if (options.fromMetadataPath) {
|
|
84
|
+
return generateEventTypesFromMetadata({
|
|
85
|
+
clientRoot: options.clientRoot,
|
|
86
|
+
metadataPath: options.fromMetadataPath,
|
|
87
|
+
output: resolved.output,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
52
90
|
if (!resolved.enabled) {
|
|
53
91
|
return null;
|
|
54
92
|
}
|
|
@@ -89,4 +127,11 @@ function generateEventTypes(options) {
|
|
|
89
127
|
tslib_1.__exportStar(require("./types"), exports);
|
|
90
128
|
var config_2 = require("./config");
|
|
91
129
|
Object.defineProperty(exports, "resolveEventTypesConfig", { enumerable: true, get: function () { return config_2.resolveEventTypesConfig; } });
|
|
130
|
+
var parse_metadata_2 = require("./parse-metadata");
|
|
131
|
+
Object.defineProperty(exports, "parseEventMetadataDump", { enumerable: true, get: function () { return parse_metadata_2.parseEventMetadataDump; } });
|
|
132
|
+
Object.defineProperty(exports, "renderEventTypesFromMetadataContracts", { enumerable: true, get: function () { return parse_metadata_2.renderEventTypesFromMetadataContracts; } });
|
|
133
|
+
Object.defineProperty(exports, "eventNameToDetailsInterfaceName", { enumerable: true, get: function () { return parse_metadata_2.eventNameToDetailsInterfaceName; } });
|
|
134
|
+
var naming_1 = require("./naming");
|
|
135
|
+
Object.defineProperty(exports, "toUpperUnderscoreWithAcronyms", { enumerable: true, get: function () { return naming_1.toUpperUnderscoreWithAcronyms; } });
|
|
136
|
+
Object.defineProperty(exports, "kotlinPropertyToWireField", { enumerable: true, get: function () { return naming_1.kotlinPropertyToWireField; } });
|
|
92
137
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** Wire-named DETAILS property from live event metadata / JSON Schema. */
|
|
2
|
+
export type MetadataField = {
|
|
3
|
+
/** Already UPPER_SNAKE wire name (e.g. ADDRESS_LINE_1). */
|
|
4
|
+
name: string;
|
|
5
|
+
/** Emitted TypeScript type (e.g. string, number, Side). */
|
|
6
|
+
tsType: string;
|
|
7
|
+
optional: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type MetadataEventContract = {
|
|
10
|
+
eventName: string;
|
|
11
|
+
detailsInterface: string;
|
|
12
|
+
fields: MetadataField[];
|
|
13
|
+
};
|
|
14
|
+
export type MetadataEnumDef = {
|
|
15
|
+
name: string;
|
|
16
|
+
values: string[];
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Dump shape produced by foundation-header dev capture (FUI-2608).
|
|
20
|
+
* Prefer `schema.INBOUND` DETAILS; fall back to `metadata.FIELD`.
|
|
21
|
+
*/
|
|
22
|
+
export type EventMetadataDump = {
|
|
23
|
+
capturedAt?: string;
|
|
24
|
+
source?: string;
|
|
25
|
+
events: Record<string, {
|
|
26
|
+
metadata?: {
|
|
27
|
+
FIELD?: Array<{
|
|
28
|
+
NAME: string;
|
|
29
|
+
TYPE?: string;
|
|
30
|
+
JSON_TYPE?: string;
|
|
31
|
+
OPTIONAL?: boolean;
|
|
32
|
+
VALID_VALUES?: string;
|
|
33
|
+
}>;
|
|
34
|
+
NAME?: string;
|
|
35
|
+
};
|
|
36
|
+
schema?: {
|
|
37
|
+
INBOUND?: {
|
|
38
|
+
properties?: Record<string, unknown>;
|
|
39
|
+
required?: string[];
|
|
40
|
+
};
|
|
41
|
+
MESSAGE_TYPE?: string;
|
|
42
|
+
};
|
|
43
|
+
}>;
|
|
44
|
+
};
|
|
45
|
+
/** Quote a string for safe emission as a TS string literal / object key. */
|
|
46
|
+
export declare const quoteTsString: (s: string) => string;
|
|
47
|
+
/** EVENT_BROKER_INSERT → BrokerInsertDetails */
|
|
48
|
+
export declare const eventNameToDetailsInterfaceName: (eventName: string) => string;
|
|
49
|
+
/**
|
|
50
|
+
* Parse a header-captured event-metadata.json dump into per-event contracts.
|
|
51
|
+
*/
|
|
52
|
+
export declare const parseEventMetadataDump: (dump: EventMetadataDump) => {
|
|
53
|
+
contracts: MetadataEventContract[];
|
|
54
|
+
enums: MetadataEnumDef[];
|
|
55
|
+
unmapped: string[];
|
|
56
|
+
};
|
|
57
|
+
export declare const renderEventTypesFromMetadataContracts: (contracts: MetadataEventContract[], enums: MetadataEnumDef[], unmapped: string[]) => string;
|
|
58
|
+
//# sourceMappingURL=parse-metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-metadata.d.ts","sourceRoot":"","sources":["../src/parse-metadata.ts"],"names":[],"mappings":"AAEA,0EAA0E;AAC1E,MAAM,MAAM,aAAa,GAAG;IAC1B,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CACZ,MAAM,EACN;QACE,QAAQ,CAAC,EAAE;YACT,KAAK,CAAC,EAAE,KAAK,CAAC;gBACZ,IAAI,EAAE,MAAM,CAAC;gBACb,IAAI,CAAC,EAAE,MAAM,CAAC;gBACd,SAAS,CAAC,EAAE,MAAM,CAAC;gBACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;gBACnB,YAAY,CAAC,EAAE,MAAM,CAAC;aACvB,CAAC,CAAC;YACH,IAAI,CAAC,EAAE,MAAM,CAAC;SACf,CAAC;QACF,MAAM,CAAC,EAAE;YACP,OAAO,CAAC,EAAE;gBACR,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBACrC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;aACrB,CAAC;YACF,YAAY,CAAC,EAAE,MAAM,CAAC;SACvB,CAAC;KACH,CACF,CAAC;CACH,CAAC;AAKF,4EAA4E;AAC5E,eAAO,MAAM,aAAa,GAAI,GAAG,MAAM,KAAG,MACY,CAAC;AAMvD,gDAAgD;AAChD,eAAO,MAAM,+BAA+B,GAAI,WAAW,MAAM,KAAG,MASnE,CAAC;AAqHF;;GAEG;AACH,eAAO,MAAM,sBAAsB,GACjC,MAAM,iBAAiB,KACtB;IAAE,SAAS,EAAE,qBAAqB,EAAE,CAAC;IAAC,KAAK,EAAE,eAAe,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAkEpF,CAAC;AAEF,eAAO,MAAM,qCAAqC,GAChD,WAAW,qBAAqB,EAAE,EAClC,OAAO,eAAe,EAAE,EACxB,UAAU,MAAM,EAAE,KACjB,MA0DF,CAAC"}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderEventTypesFromMetadataContracts = exports.parseEventMetadataDump = exports.eventNameToDetailsInterfaceName = exports.quoteTsString = void 0;
|
|
4
|
+
const naming_1 = require("./naming");
|
|
5
|
+
const isRecord = (value) => typeof value === 'object' && value != null && !Array.isArray(value);
|
|
6
|
+
/** Quote a string for safe emission as a TS string literal / object key. */
|
|
7
|
+
const quoteTsString = (s) => `'${s.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
|
|
8
|
+
exports.quoteTsString = quoteTsString;
|
|
9
|
+
/** Bare identifier when legal; otherwise a quoted string key. */
|
|
10
|
+
const tsPropertyKey = (name) => /^[A-Za-z_$][\w$]*$/.test(name) ? name : (0, exports.quoteTsString)(name);
|
|
11
|
+
/** EVENT_BROKER_INSERT → BrokerInsertDetails */
|
|
12
|
+
const eventNameToDetailsInterfaceName = (eventName) => {
|
|
13
|
+
const base = eventName.startsWith('EVENT_') ? eventName.slice('EVENT_'.length) : eventName;
|
|
14
|
+
const pascal = base
|
|
15
|
+
.toLowerCase()
|
|
16
|
+
.split('_')
|
|
17
|
+
.filter(Boolean)
|
|
18
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
19
|
+
.join('');
|
|
20
|
+
return `${pascal}Details`;
|
|
21
|
+
};
|
|
22
|
+
exports.eventNameToDetailsInterfaceName = eventNameToDetailsInterfaceName;
|
|
23
|
+
/** Build a valid PascalCase TypeScript identifier from an enum name hint. */
|
|
24
|
+
const toSafeEnumIdentifier = (enumNameHint) => {
|
|
25
|
+
const enumName = enumNameHint
|
|
26
|
+
.split(/[^A-Za-z0-9]+/)
|
|
27
|
+
.filter(Boolean)
|
|
28
|
+
.map((p) => p.charAt(0).toUpperCase() + p.slice(1).toLowerCase())
|
|
29
|
+
.join('');
|
|
30
|
+
if (!enumName)
|
|
31
|
+
return 'GeneratedEnum';
|
|
32
|
+
return /^[A-Za-z]/.test(enumName) ? enumName : `E${enumName}`;
|
|
33
|
+
};
|
|
34
|
+
const valuesKey = (values) => values.join('\0');
|
|
35
|
+
/**
|
|
36
|
+
* Register an enum under a collision-safe name. Same values → reuse; different values →
|
|
37
|
+
* disambiguate (`Foo`, `Foo2`, …) and report via `unmapped`.
|
|
38
|
+
*/
|
|
39
|
+
const registerEnum = (enums, preferredName, values, unmapped, context) => {
|
|
40
|
+
const existing = enums.get(preferredName);
|
|
41
|
+
if (!existing) {
|
|
42
|
+
enums.set(preferredName, values);
|
|
43
|
+
return preferredName;
|
|
44
|
+
}
|
|
45
|
+
if (valuesKey(existing) === valuesKey(values)) {
|
|
46
|
+
return preferredName;
|
|
47
|
+
}
|
|
48
|
+
let n = 2;
|
|
49
|
+
let candidate = `${preferredName}${n}`;
|
|
50
|
+
while (enums.has(candidate) && valuesKey(enums.get(candidate)) !== valuesKey(values)) {
|
|
51
|
+
n += 1;
|
|
52
|
+
candidate = `${preferredName}${n}`;
|
|
53
|
+
}
|
|
54
|
+
if (!enums.has(candidate)) {
|
|
55
|
+
enums.set(candidate, values);
|
|
56
|
+
unmapped.push(`${context}: enum name collision for ${preferredName}; emitted as ${candidate}`);
|
|
57
|
+
}
|
|
58
|
+
return candidate;
|
|
59
|
+
};
|
|
60
|
+
const jsonSchemaTypeToTs = (propSchema, enums, enumNameHint, unmapped, context) => {
|
|
61
|
+
if (Array.isArray(propSchema.enum) && propSchema.enum.every((v) => typeof v === 'string')) {
|
|
62
|
+
const values = propSchema.enum;
|
|
63
|
+
const safeName = toSafeEnumIdentifier(enumNameHint);
|
|
64
|
+
return registerEnum(enums, safeName, values, unmapped, context);
|
|
65
|
+
}
|
|
66
|
+
if (Array.isArray(propSchema.oneOf)) {
|
|
67
|
+
const nonNull = propSchema.oneOf.find((branch) => isRecord(branch) && branch.type !== 'null');
|
|
68
|
+
if (isRecord(nonNull)) {
|
|
69
|
+
return jsonSchemaTypeToTs(nonNull, enums, enumNameHint, unmapped, context);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const type = propSchema.type;
|
|
73
|
+
if (type === 'integer' || type === 'number')
|
|
74
|
+
return 'number';
|
|
75
|
+
if (type === 'boolean')
|
|
76
|
+
return 'boolean';
|
|
77
|
+
if (type === 'array') {
|
|
78
|
+
const items = isRecord(propSchema.items) ? propSchema.items : {};
|
|
79
|
+
return `${jsonSchemaTypeToTs(items, enums, enumNameHint, unmapped, context)}[]`;
|
|
80
|
+
}
|
|
81
|
+
if (type === 'object')
|
|
82
|
+
return 'Record<string, unknown>';
|
|
83
|
+
return 'string';
|
|
84
|
+
};
|
|
85
|
+
const metadataJsonTypeToTs = (jsonType, fieldType) => {
|
|
86
|
+
const jt = (jsonType !== null && jsonType !== void 0 ? jsonType : '').toLowerCase();
|
|
87
|
+
if (jt === 'integer' || jt === 'number')
|
|
88
|
+
return 'number';
|
|
89
|
+
if (jt === 'boolean')
|
|
90
|
+
return 'boolean';
|
|
91
|
+
if ((fieldType !== null && fieldType !== void 0 ? fieldType : '').toUpperCase() === 'DATETIME' ||
|
|
92
|
+
(fieldType !== null && fieldType !== void 0 ? fieldType : '').toUpperCase() === 'DATE') {
|
|
93
|
+
return 'number';
|
|
94
|
+
}
|
|
95
|
+
return 'string';
|
|
96
|
+
};
|
|
97
|
+
const extractDetailsSchema = (inbound) => {
|
|
98
|
+
if (!inbound)
|
|
99
|
+
return null;
|
|
100
|
+
const rootProps = isRecord(inbound.properties) ? inbound.properties : undefined;
|
|
101
|
+
if (!rootProps)
|
|
102
|
+
return null;
|
|
103
|
+
// Event inbound is usually { DETAILS: { type: object, properties: {...}, required: [...] } }
|
|
104
|
+
const details = rootProps.DETAILS;
|
|
105
|
+
if (isRecord(details) && isRecord(details.properties)) {
|
|
106
|
+
const required = Array.isArray(details.required)
|
|
107
|
+
? new Set(details.required.filter((r) => typeof r === 'string'))
|
|
108
|
+
: new Set();
|
|
109
|
+
return { properties: details.properties, required };
|
|
110
|
+
}
|
|
111
|
+
// Some schemas flatten DETAILS properties at the inbound root.
|
|
112
|
+
const required = Array.isArray(inbound.required)
|
|
113
|
+
? new Set(inbound.required.filter((r) => typeof r === 'string'))
|
|
114
|
+
: new Set();
|
|
115
|
+
return { properties: rootProps, required };
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Parse a header-captured event-metadata.json dump into per-event contracts.
|
|
119
|
+
*/
|
|
120
|
+
const parseEventMetadataDump = (dump) => {
|
|
121
|
+
var _a, _b, _c, _d;
|
|
122
|
+
const contracts = [];
|
|
123
|
+
const enums = new Map();
|
|
124
|
+
const unmapped = [];
|
|
125
|
+
const eventNames = Object.keys((_a = dump.events) !== null && _a !== void 0 ? _a : {}).sort((a, b) => a.localeCompare(b));
|
|
126
|
+
for (const eventName of eventNames) {
|
|
127
|
+
if (!eventName.startsWith('EVENT_')) {
|
|
128
|
+
unmapped.push(`${eventName}: not an EVENT_* handler`);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const entry = dump.events[eventName];
|
|
132
|
+
const inbound = isRecord((_b = entry === null || entry === void 0 ? void 0 : entry.schema) === null || _b === void 0 ? void 0 : _b.INBOUND)
|
|
133
|
+
? entry.schema.INBOUND
|
|
134
|
+
: undefined;
|
|
135
|
+
const detailsSchema = extractDetailsSchema(inbound);
|
|
136
|
+
const fields = [];
|
|
137
|
+
if (detailsSchema) {
|
|
138
|
+
for (const [fieldName, rawProp] of Object.entries(detailsSchema.properties)) {
|
|
139
|
+
if (fieldName === 'MESSAGE_TYPE' || fieldName === 'SOURCE_REF')
|
|
140
|
+
continue;
|
|
141
|
+
if (!isRecord(rawProp))
|
|
142
|
+
continue;
|
|
143
|
+
const optional = !detailsSchema.required.has(fieldName);
|
|
144
|
+
const enumNameHint = eventName.startsWith('EVENT_')
|
|
145
|
+
? `${eventName.slice('EVENT_'.length)}_${fieldName}`
|
|
146
|
+
: `${eventName}_${fieldName}`;
|
|
147
|
+
fields.push({
|
|
148
|
+
name: fieldName,
|
|
149
|
+
tsType: jsonSchemaTypeToTs(rawProp, enums, enumNameHint, unmapped, `${eventName}.${fieldName}`),
|
|
150
|
+
optional,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
else if ((_d = (_c = entry === null || entry === void 0 ? void 0 : entry.metadata) === null || _c === void 0 ? void 0 : _c.FIELD) === null || _d === void 0 ? void 0 : _d.length) {
|
|
155
|
+
for (const field of entry.metadata.FIELD) {
|
|
156
|
+
fields.push({
|
|
157
|
+
name: field.NAME,
|
|
158
|
+
tsType: metadataJsonTypeToTs(field.JSON_TYPE, field.TYPE),
|
|
159
|
+
optional: field.OPTIONAL !== false,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
unmapped.push(`${eventName}: no INBOUND DETAILS schema or metadata.FIELD`);
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
contracts.push({
|
|
168
|
+
eventName,
|
|
169
|
+
detailsInterface: (0, exports.eventNameToDetailsInterfaceName)(eventName),
|
|
170
|
+
fields,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
const enumDefs = [...enums.entries()]
|
|
174
|
+
.map(([name, values]) => ({ name, values }))
|
|
175
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
176
|
+
return { contracts, enums: enumDefs, unmapped };
|
|
177
|
+
};
|
|
178
|
+
exports.parseEventMetadataDump = parseEventMetadataDump;
|
|
179
|
+
const renderEventTypesFromMetadataContracts = (contracts, enums, unmapped) => {
|
|
180
|
+
const unmappedComment = unmapped.length > 0
|
|
181
|
+
? `/**\n * Unmapped handlers (not included in EventDetailsMap):\n${unmapped
|
|
182
|
+
.map((line) => ` * - ${line}`)
|
|
183
|
+
.join('\n')}\n */\n\n`
|
|
184
|
+
: '';
|
|
185
|
+
const enumExports = enums
|
|
186
|
+
.map((e) => {
|
|
187
|
+
const entries = e.values
|
|
188
|
+
.map((value) => ` ${(0, exports.quoteTsString)(value)}: ${(0, exports.quoteTsString)(value)},`)
|
|
189
|
+
.join('\n');
|
|
190
|
+
return `export const ${e.name} = {\n${entries}\n} as const;\n\nexport type ${e.name} = (typeof ${e.name})[keyof typeof ${e.name}];`;
|
|
191
|
+
})
|
|
192
|
+
.join('\n\n');
|
|
193
|
+
const interfaces = contracts
|
|
194
|
+
.map((c) => {
|
|
195
|
+
const lines = c.fields.map((f) => {
|
|
196
|
+
const optional = f.optional ? '?' : '';
|
|
197
|
+
return ` ${tsPropertyKey(f.name)}${optional}: ${f.tsType};`;
|
|
198
|
+
});
|
|
199
|
+
return `export interface ${c.detailsInterface} {\n${lines.join('\n')}\n}`;
|
|
200
|
+
})
|
|
201
|
+
.join('\n\n');
|
|
202
|
+
const mapEntries = contracts
|
|
203
|
+
.map((c) => ` ${tsPropertyKey(c.eventName)}: ${c.detailsInterface};`)
|
|
204
|
+
.join('\n');
|
|
205
|
+
const eventClasses = contracts
|
|
206
|
+
.map((c) => {
|
|
207
|
+
const className = (0, naming_1.eventNameToEventClassName)(c.eventName);
|
|
208
|
+
return `export class ${className} extends GenesisEvent<'${c.eventName}', ${c.detailsInterface}> {
|
|
209
|
+
readonly MESSAGE_TYPE = '${c.eventName}' as const;
|
|
210
|
+
|
|
211
|
+
constructor(details: ${c.detailsInterface}) {
|
|
212
|
+
super(details);
|
|
213
|
+
}
|
|
214
|
+
}`;
|
|
215
|
+
})
|
|
216
|
+
.join('\n\n');
|
|
217
|
+
const foundationCommsImport = contracts.length > 0 ? `import { GenesisEvent } from '@genesislcap/foundation-comms';\n\n` : '';
|
|
218
|
+
return `// AUTO-GENERATED — do not edit
|
|
219
|
+
// Source: event metadata dump (genx generate event-types --from-metadata)
|
|
220
|
+
|
|
221
|
+
${unmappedComment}${foundationCommsImport}${enumExports ? `${enumExports}\n\n` : ''}${interfaces}
|
|
222
|
+
|
|
223
|
+
export interface EventDetailsMap {
|
|
224
|
+
${mapEntries}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export type GenesisEventName = keyof EventDetailsMap;
|
|
228
|
+
${eventClasses ? `\n${eventClasses}\n` : ''}`;
|
|
229
|
+
};
|
|
230
|
+
exports.renderEventTypesFromMetadataContracts = renderEventTypesFromMetadataContracts;
|
|
231
|
+
//# sourceMappingURL=parse-metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-metadata.js","sourceRoot":"","sources":["../src/parse-metadata.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genesislcap/event-type-codegen",
|
|
3
3
|
"description": "Build-time TypeScript codegen from Kotlin event handler DTOs",
|
|
4
|
-
"version": "15.
|
|
4
|
+
"version": "15.18.0",
|
|
5
5
|
"license": "SEE LICENSE IN license.txt",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"consola": "^3.0.2"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@genesislcap/foundation-testing": "15.
|
|
24
|
+
"@genesislcap/foundation-testing": "15.18.0",
|
|
25
25
|
"@types/node": "^22.10.2",
|
|
26
26
|
"tsx": "^4.7.0",
|
|
27
27
|
"uvu": "0.5.4"
|
|
@@ -34,5 +34,5 @@
|
|
|
34
34
|
"publishConfig": {
|
|
35
35
|
"access": "public"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "a92013873774dbc7f67a32d37994f10777f86383"
|
|
38
38
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, globSync } from 'node:fs';
|
|
1
|
+
import { existsSync, globSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { dirname, join, resolve } from 'node:path';
|
|
4
4
|
import { consola } from 'consola';
|
|
@@ -7,6 +7,11 @@ import { resolveEventTypesConfig } from './config';
|
|
|
7
7
|
import { parseGeneratedDaosFromSources } from './parse-dao';
|
|
8
8
|
import { parseGeneratedEnumsFromSources } from './parse-enums';
|
|
9
9
|
import { parseHandlersFromSources, parseDtosFromSources } from './parse-kotlin';
|
|
10
|
+
import {
|
|
11
|
+
parseEventMetadataDump,
|
|
12
|
+
renderEventTypesFromMetadataContracts,
|
|
13
|
+
type EventMetadataDump,
|
|
14
|
+
} from './parse-metadata';
|
|
10
15
|
import { renderEventTypesFile } from './render';
|
|
11
16
|
import type { EventTypesResolvedConfig, GenerateResult, KotlinDto } from './types';
|
|
12
17
|
|
|
@@ -56,8 +61,50 @@ export type GenerateEventTypesOptions = {
|
|
|
56
61
|
clientRoot: string;
|
|
57
62
|
config?: EventTypesResolvedConfig;
|
|
58
63
|
packageGenesis?: { eventTypes?: unknown };
|
|
64
|
+
/** When set, generate from a header-captured metadata dump instead of Kotlin sources. */
|
|
65
|
+
fromMetadataPath?: string;
|
|
59
66
|
};
|
|
60
67
|
|
|
68
|
+
export async function generateEventTypesFromMetadata(options: {
|
|
69
|
+
clientRoot: string;
|
|
70
|
+
metadataPath: string;
|
|
71
|
+
output?: string;
|
|
72
|
+
}): Promise<GenerateResult> {
|
|
73
|
+
const metadataPath = resolve(options.clientRoot, options.metadataPath);
|
|
74
|
+
if (!existsSync(metadataPath)) {
|
|
75
|
+
throw new Error(`eventTypes: metadata dump not found at ${metadataPath}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let dump: EventMetadataDump;
|
|
79
|
+
try {
|
|
80
|
+
dump = JSON.parse(readFileSync(metadataPath, 'utf8')) as EventMetadataDump;
|
|
81
|
+
} catch (err) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`eventTypes: failed to parse metadata JSON dump at ${metadataPath}: ${
|
|
84
|
+
err instanceof Error ? err.message : String(err)
|
|
85
|
+
}`,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
const { contracts, enums, unmapped } = parseEventMetadataDump(dump);
|
|
89
|
+
const contents = renderEventTypesFromMetadataContracts(contracts, enums, unmapped);
|
|
90
|
+
|
|
91
|
+
const outputRel = options.output ?? 'src/generated/genesis-event-types.ts';
|
|
92
|
+
const outputPath = resolve(options.clientRoot, outputRel);
|
|
93
|
+
await mkdir(dirname(outputPath), { recursive: true });
|
|
94
|
+
await writeFile(outputPath, contents, 'utf8');
|
|
95
|
+
|
|
96
|
+
consola.info(
|
|
97
|
+
`eventTypes: wrote ${outputPath} from metadata (${contracts.length} events, ${unmapped.length} unmapped)`,
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
outputPath,
|
|
102
|
+
eventCount: contracts.length,
|
|
103
|
+
dtoCount: contracts.length,
|
|
104
|
+
unmappedCount: unmapped.length,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
61
108
|
export async function generateEventTypes(
|
|
62
109
|
options: GenerateEventTypesOptions,
|
|
63
110
|
): Promise<GenerateResult | null> {
|
|
@@ -65,6 +112,14 @@ export async function generateEventTypes(
|
|
|
65
112
|
options.config ??
|
|
66
113
|
resolveEventTypesConfig(options.clientRoot, undefined, options.packageGenesis);
|
|
67
114
|
|
|
115
|
+
if (options.fromMetadataPath) {
|
|
116
|
+
return generateEventTypesFromMetadata({
|
|
117
|
+
clientRoot: options.clientRoot,
|
|
118
|
+
metadataPath: options.fromMetadataPath,
|
|
119
|
+
output: resolved.output,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
68
123
|
if (!resolved.enabled) {
|
|
69
124
|
return null;
|
|
70
125
|
}
|
|
@@ -118,3 +173,12 @@ export async function generateEventTypes(
|
|
|
118
173
|
|
|
119
174
|
export * from './types';
|
|
120
175
|
export { resolveEventTypesConfig } from './config';
|
|
176
|
+
export {
|
|
177
|
+
parseEventMetadataDump,
|
|
178
|
+
renderEventTypesFromMetadataContracts,
|
|
179
|
+
eventNameToDetailsInterfaceName,
|
|
180
|
+
type EventMetadataDump,
|
|
181
|
+
type MetadataEventContract,
|
|
182
|
+
type MetadataField,
|
|
183
|
+
} from './parse-metadata';
|
|
184
|
+
export { toUpperUnderscoreWithAcronyms, kotlinPropertyToWireField } from './naming';
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { eventNameToEventClassName } from './naming';
|
|
2
|
+
|
|
3
|
+
/** Wire-named DETAILS property from live event metadata / JSON Schema. */
|
|
4
|
+
export type MetadataField = {
|
|
5
|
+
/** Already UPPER_SNAKE wire name (e.g. ADDRESS_LINE_1). */
|
|
6
|
+
name: string;
|
|
7
|
+
/** Emitted TypeScript type (e.g. string, number, Side). */
|
|
8
|
+
tsType: string;
|
|
9
|
+
optional: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type MetadataEventContract = {
|
|
13
|
+
eventName: string;
|
|
14
|
+
detailsInterface: string;
|
|
15
|
+
fields: MetadataField[];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type MetadataEnumDef = {
|
|
19
|
+
name: string;
|
|
20
|
+
values: string[];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Dump shape produced by foundation-header dev capture (FUI-2608).
|
|
25
|
+
* Prefer `schema.INBOUND` DETAILS; fall back to `metadata.FIELD`.
|
|
26
|
+
*/
|
|
27
|
+
export type EventMetadataDump = {
|
|
28
|
+
capturedAt?: string;
|
|
29
|
+
source?: string;
|
|
30
|
+
events: Record<
|
|
31
|
+
string,
|
|
32
|
+
{
|
|
33
|
+
metadata?: {
|
|
34
|
+
FIELD?: Array<{
|
|
35
|
+
NAME: string;
|
|
36
|
+
TYPE?: string;
|
|
37
|
+
JSON_TYPE?: string;
|
|
38
|
+
OPTIONAL?: boolean;
|
|
39
|
+
VALID_VALUES?: string;
|
|
40
|
+
}>;
|
|
41
|
+
NAME?: string;
|
|
42
|
+
};
|
|
43
|
+
schema?: {
|
|
44
|
+
INBOUND?: {
|
|
45
|
+
properties?: Record<string, unknown>;
|
|
46
|
+
required?: string[];
|
|
47
|
+
};
|
|
48
|
+
MESSAGE_TYPE?: string;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
>;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
55
|
+
typeof value === 'object' && value != null && !Array.isArray(value);
|
|
56
|
+
|
|
57
|
+
/** Quote a string for safe emission as a TS string literal / object key. */
|
|
58
|
+
export const quoteTsString = (s: string): string =>
|
|
59
|
+
`'${s.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
|
|
60
|
+
|
|
61
|
+
/** Bare identifier when legal; otherwise a quoted string key. */
|
|
62
|
+
const tsPropertyKey = (name: string): string =>
|
|
63
|
+
/^[A-Za-z_$][\w$]*$/.test(name) ? name : quoteTsString(name);
|
|
64
|
+
|
|
65
|
+
/** EVENT_BROKER_INSERT → BrokerInsertDetails */
|
|
66
|
+
export const eventNameToDetailsInterfaceName = (eventName: string): string => {
|
|
67
|
+
const base = eventName.startsWith('EVENT_') ? eventName.slice('EVENT_'.length) : eventName;
|
|
68
|
+
const pascal = base
|
|
69
|
+
.toLowerCase()
|
|
70
|
+
.split('_')
|
|
71
|
+
.filter(Boolean)
|
|
72
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
73
|
+
.join('');
|
|
74
|
+
return `${pascal}Details`;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/** Build a valid PascalCase TypeScript identifier from an enum name hint. */
|
|
78
|
+
const toSafeEnumIdentifier = (enumNameHint: string): string => {
|
|
79
|
+
const enumName = enumNameHint
|
|
80
|
+
.split(/[^A-Za-z0-9]+/)
|
|
81
|
+
.filter(Boolean)
|
|
82
|
+
.map((p) => p.charAt(0).toUpperCase() + p.slice(1).toLowerCase())
|
|
83
|
+
.join('');
|
|
84
|
+
if (!enumName) return 'GeneratedEnum';
|
|
85
|
+
return /^[A-Za-z]/.test(enumName) ? enumName : `E${enumName}`;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const valuesKey = (values: string[]): string => values.join('\0');
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Register an enum under a collision-safe name. Same values → reuse; different values →
|
|
92
|
+
* disambiguate (`Foo`, `Foo2`, …) and report via `unmapped`.
|
|
93
|
+
*/
|
|
94
|
+
const registerEnum = (
|
|
95
|
+
enums: Map<string, string[]>,
|
|
96
|
+
preferredName: string,
|
|
97
|
+
values: string[],
|
|
98
|
+
unmapped: string[],
|
|
99
|
+
context: string,
|
|
100
|
+
): string => {
|
|
101
|
+
const existing = enums.get(preferredName);
|
|
102
|
+
if (!existing) {
|
|
103
|
+
enums.set(preferredName, values);
|
|
104
|
+
return preferredName;
|
|
105
|
+
}
|
|
106
|
+
if (valuesKey(existing) === valuesKey(values)) {
|
|
107
|
+
return preferredName;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let n = 2;
|
|
111
|
+
let candidate = `${preferredName}${n}`;
|
|
112
|
+
while (enums.has(candidate) && valuesKey(enums.get(candidate)!) !== valuesKey(values)) {
|
|
113
|
+
n += 1;
|
|
114
|
+
candidate = `${preferredName}${n}`;
|
|
115
|
+
}
|
|
116
|
+
if (!enums.has(candidate)) {
|
|
117
|
+
enums.set(candidate, values);
|
|
118
|
+
unmapped.push(`${context}: enum name collision for ${preferredName}; emitted as ${candidate}`);
|
|
119
|
+
}
|
|
120
|
+
return candidate;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const jsonSchemaTypeToTs = (
|
|
124
|
+
propSchema: Record<string, unknown>,
|
|
125
|
+
enums: Map<string, string[]>,
|
|
126
|
+
enumNameHint: string,
|
|
127
|
+
unmapped: string[],
|
|
128
|
+
context: string,
|
|
129
|
+
): string => {
|
|
130
|
+
if (Array.isArray(propSchema.enum) && propSchema.enum.every((v) => typeof v === 'string')) {
|
|
131
|
+
const values = propSchema.enum as string[];
|
|
132
|
+
const safeName = toSafeEnumIdentifier(enumNameHint);
|
|
133
|
+
return registerEnum(enums, safeName, values, unmapped, context);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (Array.isArray(propSchema.oneOf)) {
|
|
137
|
+
const nonNull = (propSchema.oneOf as unknown[]).find(
|
|
138
|
+
(branch) => isRecord(branch) && branch.type !== 'null',
|
|
139
|
+
);
|
|
140
|
+
if (isRecord(nonNull)) {
|
|
141
|
+
return jsonSchemaTypeToTs(nonNull, enums, enumNameHint, unmapped, context);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const type = propSchema.type;
|
|
146
|
+
if (type === 'integer' || type === 'number') return 'number';
|
|
147
|
+
if (type === 'boolean') return 'boolean';
|
|
148
|
+
if (type === 'array') {
|
|
149
|
+
const items = isRecord(propSchema.items) ? propSchema.items : {};
|
|
150
|
+
return `${jsonSchemaTypeToTs(items, enums, enumNameHint, unmapped, context)}[]`;
|
|
151
|
+
}
|
|
152
|
+
if (type === 'object') return 'Record<string, unknown>';
|
|
153
|
+
return 'string';
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const metadataJsonTypeToTs = (jsonType?: string, fieldType?: string): string => {
|
|
157
|
+
const jt = (jsonType ?? '').toLowerCase();
|
|
158
|
+
if (jt === 'integer' || jt === 'number') return 'number';
|
|
159
|
+
if (jt === 'boolean') return 'boolean';
|
|
160
|
+
if (
|
|
161
|
+
(fieldType ?? '').toUpperCase() === 'DATETIME' ||
|
|
162
|
+
(fieldType ?? '').toUpperCase() === 'DATE'
|
|
163
|
+
) {
|
|
164
|
+
return 'number';
|
|
165
|
+
}
|
|
166
|
+
return 'string';
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const extractDetailsSchema = (
|
|
170
|
+
inbound: Record<string, unknown> | undefined,
|
|
171
|
+
): { properties: Record<string, unknown>; required: Set<string> } | null => {
|
|
172
|
+
if (!inbound) return null;
|
|
173
|
+
const rootProps = isRecord(inbound.properties) ? inbound.properties : undefined;
|
|
174
|
+
if (!rootProps) return null;
|
|
175
|
+
|
|
176
|
+
// Event inbound is usually { DETAILS: { type: object, properties: {...}, required: [...] } }
|
|
177
|
+
const details = rootProps.DETAILS;
|
|
178
|
+
if (isRecord(details) && isRecord(details.properties)) {
|
|
179
|
+
const required = Array.isArray(details.required)
|
|
180
|
+
? new Set(details.required.filter((r): r is string => typeof r === 'string'))
|
|
181
|
+
: new Set<string>();
|
|
182
|
+
return { properties: details.properties, required };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Some schemas flatten DETAILS properties at the inbound root.
|
|
186
|
+
const required = Array.isArray(inbound.required)
|
|
187
|
+
? new Set(inbound.required.filter((r): r is string => typeof r === 'string'))
|
|
188
|
+
: new Set<string>();
|
|
189
|
+
return { properties: rootProps, required };
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Parse a header-captured event-metadata.json dump into per-event contracts.
|
|
194
|
+
*/
|
|
195
|
+
export const parseEventMetadataDump = (
|
|
196
|
+
dump: EventMetadataDump,
|
|
197
|
+
): { contracts: MetadataEventContract[]; enums: MetadataEnumDef[]; unmapped: string[] } => {
|
|
198
|
+
const contracts: MetadataEventContract[] = [];
|
|
199
|
+
const enums = new Map<string, string[]>();
|
|
200
|
+
const unmapped: string[] = [];
|
|
201
|
+
|
|
202
|
+
const eventNames = Object.keys(dump.events ?? {}).sort((a, b) => a.localeCompare(b));
|
|
203
|
+
|
|
204
|
+
for (const eventName of eventNames) {
|
|
205
|
+
if (!eventName.startsWith('EVENT_')) {
|
|
206
|
+
unmapped.push(`${eventName}: not an EVENT_* handler`);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const entry = dump.events[eventName];
|
|
211
|
+
const inbound = isRecord(entry?.schema?.INBOUND)
|
|
212
|
+
? (entry.schema.INBOUND as Record<string, unknown>)
|
|
213
|
+
: undefined;
|
|
214
|
+
const detailsSchema = extractDetailsSchema(inbound);
|
|
215
|
+
|
|
216
|
+
const fields: MetadataField[] = [];
|
|
217
|
+
|
|
218
|
+
if (detailsSchema) {
|
|
219
|
+
for (const [fieldName, rawProp] of Object.entries(detailsSchema.properties)) {
|
|
220
|
+
if (fieldName === 'MESSAGE_TYPE' || fieldName === 'SOURCE_REF') continue;
|
|
221
|
+
if (!isRecord(rawProp)) continue;
|
|
222
|
+
const optional = !detailsSchema.required.has(fieldName);
|
|
223
|
+
const enumNameHint = eventName.startsWith('EVENT_')
|
|
224
|
+
? `${eventName.slice('EVENT_'.length)}_${fieldName}`
|
|
225
|
+
: `${eventName}_${fieldName}`;
|
|
226
|
+
fields.push({
|
|
227
|
+
name: fieldName,
|
|
228
|
+
tsType: jsonSchemaTypeToTs(
|
|
229
|
+
rawProp,
|
|
230
|
+
enums,
|
|
231
|
+
enumNameHint,
|
|
232
|
+
unmapped,
|
|
233
|
+
`${eventName}.${fieldName}`,
|
|
234
|
+
),
|
|
235
|
+
optional,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
} else if (entry?.metadata?.FIELD?.length) {
|
|
239
|
+
for (const field of entry.metadata.FIELD) {
|
|
240
|
+
fields.push({
|
|
241
|
+
name: field.NAME,
|
|
242
|
+
tsType: metadataJsonTypeToTs(field.JSON_TYPE, field.TYPE),
|
|
243
|
+
optional: field.OPTIONAL !== false,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
} else {
|
|
247
|
+
unmapped.push(`${eventName}: no INBOUND DETAILS schema or metadata.FIELD`);
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
contracts.push({
|
|
252
|
+
eventName,
|
|
253
|
+
detailsInterface: eventNameToDetailsInterfaceName(eventName),
|
|
254
|
+
fields,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const enumDefs: MetadataEnumDef[] = [...enums.entries()]
|
|
259
|
+
.map(([name, values]) => ({ name, values }))
|
|
260
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
261
|
+
|
|
262
|
+
return { contracts, enums: enumDefs, unmapped };
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
export const renderEventTypesFromMetadataContracts = (
|
|
266
|
+
contracts: MetadataEventContract[],
|
|
267
|
+
enums: MetadataEnumDef[],
|
|
268
|
+
unmapped: string[],
|
|
269
|
+
): string => {
|
|
270
|
+
const unmappedComment =
|
|
271
|
+
unmapped.length > 0
|
|
272
|
+
? `/**\n * Unmapped handlers (not included in EventDetailsMap):\n${unmapped
|
|
273
|
+
.map((line) => ` * - ${line}`)
|
|
274
|
+
.join('\n')}\n */\n\n`
|
|
275
|
+
: '';
|
|
276
|
+
|
|
277
|
+
const enumExports = enums
|
|
278
|
+
.map((e) => {
|
|
279
|
+
const entries = e.values
|
|
280
|
+
.map((value) => ` ${quoteTsString(value)}: ${quoteTsString(value)},`)
|
|
281
|
+
.join('\n');
|
|
282
|
+
return `export const ${e.name} = {\n${entries}\n} as const;\n\nexport type ${e.name} = (typeof ${e.name})[keyof typeof ${e.name}];`;
|
|
283
|
+
})
|
|
284
|
+
.join('\n\n');
|
|
285
|
+
|
|
286
|
+
const interfaces = contracts
|
|
287
|
+
.map((c) => {
|
|
288
|
+
const lines = c.fields.map((f) => {
|
|
289
|
+
const optional = f.optional ? '?' : '';
|
|
290
|
+
return ` ${tsPropertyKey(f.name)}${optional}: ${f.tsType};`;
|
|
291
|
+
});
|
|
292
|
+
return `export interface ${c.detailsInterface} {\n${lines.join('\n')}\n}`;
|
|
293
|
+
})
|
|
294
|
+
.join('\n\n');
|
|
295
|
+
|
|
296
|
+
const mapEntries = contracts
|
|
297
|
+
.map((c) => ` ${tsPropertyKey(c.eventName)}: ${c.detailsInterface};`)
|
|
298
|
+
.join('\n');
|
|
299
|
+
|
|
300
|
+
const eventClasses = contracts
|
|
301
|
+
.map((c) => {
|
|
302
|
+
const className = eventNameToEventClassName(c.eventName);
|
|
303
|
+
return `export class ${className} extends GenesisEvent<'${c.eventName}', ${c.detailsInterface}> {
|
|
304
|
+
readonly MESSAGE_TYPE = '${c.eventName}' as const;
|
|
305
|
+
|
|
306
|
+
constructor(details: ${c.detailsInterface}) {
|
|
307
|
+
super(details);
|
|
308
|
+
}
|
|
309
|
+
}`;
|
|
310
|
+
})
|
|
311
|
+
.join('\n\n');
|
|
312
|
+
|
|
313
|
+
const foundationCommsImport =
|
|
314
|
+
contracts.length > 0 ? `import { GenesisEvent } from '@genesislcap/foundation-comms';\n\n` : '';
|
|
315
|
+
|
|
316
|
+
return `// AUTO-GENERATED — do not edit
|
|
317
|
+
// Source: event metadata dump (genx generate event-types --from-metadata)
|
|
318
|
+
|
|
319
|
+
${unmappedComment}${foundationCommsImport}${enumExports ? `${enumExports}\n\n` : ''}${interfaces}
|
|
320
|
+
|
|
321
|
+
export interface EventDetailsMap {
|
|
322
|
+
${mapEntries}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export type GenesisEventName = keyof EventDetailsMap;
|
|
326
|
+
${eventClasses ? `\n${eventClasses}\n` : ''}`;
|
|
327
|
+
};
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
eventNameToDetailsInterfaceName,
|
|
5
|
+
parseEventMetadataDump,
|
|
6
|
+
renderEventTypesFromMetadataContracts,
|
|
7
|
+
type EventMetadataDump,
|
|
8
|
+
} from '../src/parse-metadata';
|
|
9
|
+
|
|
10
|
+
const suite = createLogicSuite('parse-metadata');
|
|
11
|
+
|
|
12
|
+
const sampleDump: EventMetadataDump = {
|
|
13
|
+
capturedAt: '2026-08-24T10:00:00.000Z',
|
|
14
|
+
events: {
|
|
15
|
+
EVENT_BROKER_INSERT: {
|
|
16
|
+
schema: {
|
|
17
|
+
MESSAGE_TYPE: 'EVENT_BROKER_INSERT',
|
|
18
|
+
INBOUND: {
|
|
19
|
+
properties: {
|
|
20
|
+
DETAILS: {
|
|
21
|
+
type: 'object',
|
|
22
|
+
required: ['BROKER_NAME'],
|
|
23
|
+
properties: {
|
|
24
|
+
BROKER_ID: {
|
|
25
|
+
oneOf: [{ type: 'null' }, { type: 'string' }],
|
|
26
|
+
genesisType: 'STRING',
|
|
27
|
+
},
|
|
28
|
+
BROKER_NAME: { type: 'string', genesisType: 'STRING' },
|
|
29
|
+
ADDRESS_LINE_1: {
|
|
30
|
+
oneOf: [{ type: 'null' }, { type: 'string' }],
|
|
31
|
+
},
|
|
32
|
+
UPDATE_DATE: { type: 'integer', genesisType: 'DATETIME' },
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
EVENT_BROKER_AMEND: {
|
|
40
|
+
schema: {
|
|
41
|
+
INBOUND: {
|
|
42
|
+
properties: {
|
|
43
|
+
DETAILS: {
|
|
44
|
+
type: 'object',
|
|
45
|
+
required: ['BROKER_ID', 'BROKER_NAME'],
|
|
46
|
+
properties: {
|
|
47
|
+
BROKER_ID: { type: 'string' },
|
|
48
|
+
BROKER_NAME: { type: 'string' },
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
suite('derives per-event details interface names', () => {
|
|
59
|
+
assert.equal(eventNameToDetailsInterfaceName('EVENT_BROKER_INSERT'), 'BrokerInsertDetails');
|
|
60
|
+
assert.equal(eventNameToDetailsInterfaceName('EVENT_TRADE_MODIFY'), 'TradeModifyDetails');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
suite('parses schema DETAILS with correct optionality and integer dates', () => {
|
|
64
|
+
const { contracts, unmapped } = parseEventMetadataDump(sampleDump);
|
|
65
|
+
assert.equal(unmapped.length, 0);
|
|
66
|
+
assert.equal(contracts.length, 2);
|
|
67
|
+
|
|
68
|
+
const insert = contracts.find((c) => c.eventName === 'EVENT_BROKER_INSERT')!;
|
|
69
|
+
const brokerId = insert.fields.find((f) => f.name === 'BROKER_ID')!;
|
|
70
|
+
const brokerName = insert.fields.find((f) => f.name === 'BROKER_NAME')!;
|
|
71
|
+
const address = insert.fields.find((f) => f.name === 'ADDRESS_LINE_1')!;
|
|
72
|
+
const updateDate = insert.fields.find((f) => f.name === 'UPDATE_DATE')!;
|
|
73
|
+
|
|
74
|
+
assert.equal(brokerId.optional, true);
|
|
75
|
+
assert.equal(brokerName.optional, false);
|
|
76
|
+
assert.equal(address.name, 'ADDRESS_LINE_1');
|
|
77
|
+
assert.equal(updateDate.tsType, 'number');
|
|
78
|
+
|
|
79
|
+
const amend = contracts.find((c) => c.eventName === 'EVENT_BROKER_AMEND')!;
|
|
80
|
+
assert.equal(amend.fields.find((f) => f.name === 'BROKER_ID')!.optional, false);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
suite('renders separate insert/amend interfaces from metadata', () => {
|
|
84
|
+
const { contracts, enums, unmapped } = parseEventMetadataDump(sampleDump);
|
|
85
|
+
const rendered = renderEventTypesFromMetadataContracts(contracts, enums, unmapped);
|
|
86
|
+
assert.is(rendered.includes('export interface BrokerInsertDetails'), true);
|
|
87
|
+
assert.is(rendered.includes('export interface BrokerAmendDetails'), true);
|
|
88
|
+
assert.is(rendered.includes('ADDRESS_LINE_1?: string'), true);
|
|
89
|
+
assert.is(rendered.includes('UPDATE_DATE?: number'), true);
|
|
90
|
+
assert.is(rendered.includes('EVENT_BROKER_INSERT: BrokerInsertDetails'), true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
suite('scopes enum names per event to avoid cross-event collisions', () => {
|
|
94
|
+
const enumDump: EventMetadataDump = {
|
|
95
|
+
events: {
|
|
96
|
+
EVENT_TRADE_INSERT: {
|
|
97
|
+
schema: {
|
|
98
|
+
INBOUND: {
|
|
99
|
+
properties: {
|
|
100
|
+
DETAILS: {
|
|
101
|
+
type: 'object',
|
|
102
|
+
properties: {
|
|
103
|
+
SIDE: { type: 'string', enum: ['BUY', 'SELL'] },
|
|
104
|
+
status: { type: 'string', enum: ['OPEN', 'CLOSED'] },
|
|
105
|
+
SETTLEMENT: { type: 'string', enum: ['T+1', 'N/A', "O'Brien"] },
|
|
106
|
+
'MY-FIELD': { type: 'string', enum: ['A', 'B'] },
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
EVENT_ORDER_INSERT: {
|
|
114
|
+
schema: {
|
|
115
|
+
INBOUND: {
|
|
116
|
+
properties: {
|
|
117
|
+
DETAILS: {
|
|
118
|
+
type: 'object',
|
|
119
|
+
properties: {
|
|
120
|
+
SIDE: { type: 'string', enum: ['LONG', 'SHORT'] },
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
// Hashes to the same PascalCase as EVENT_TRADE_INSERT.SIDE → TradeInsertSide
|
|
128
|
+
EVENT_TRADE: {
|
|
129
|
+
schema: {
|
|
130
|
+
INBOUND: {
|
|
131
|
+
properties: {
|
|
132
|
+
DETAILS: {
|
|
133
|
+
type: 'object',
|
|
134
|
+
properties: {
|
|
135
|
+
INSERT_SIDE: { type: 'string', enum: ['IN', 'OUT'] },
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const { contracts, enums, unmapped } = parseEventMetadataDump(enumDump);
|
|
146
|
+
|
|
147
|
+
const tradeSide = enums.find((e) => e.name === 'TradeInsertSide');
|
|
148
|
+
const orderSide = enums.find((e) => e.name === 'OrderInsertSide');
|
|
149
|
+
const tradeStatus = enums.find((e) => e.name === 'TradeInsertStatus');
|
|
150
|
+
const settlement = enums.find((e) => e.name === 'TradeInsertSettlement');
|
|
151
|
+
const myField = enums.find((e) => e.name === 'TradeInsertMyField');
|
|
152
|
+
const collision = enums.find((e) => e.name === 'TradeInsertSide2');
|
|
153
|
+
|
|
154
|
+
assert.ok(tradeSide);
|
|
155
|
+
assert.ok(orderSide);
|
|
156
|
+
assert.ok(tradeStatus);
|
|
157
|
+
assert.ok(settlement);
|
|
158
|
+
assert.ok(myField);
|
|
159
|
+
assert.ok(collision);
|
|
160
|
+
// EVENT_TRADE sorts before EVENT_TRADE_INSERT, so INSERT_SIDE wins TradeInsertSide
|
|
161
|
+
assert.equal(tradeSide!.values.join(','), 'IN,OUT');
|
|
162
|
+
assert.equal(orderSide!.values.join(','), 'LONG,SHORT');
|
|
163
|
+
assert.equal(tradeStatus!.values.join(','), 'OPEN,CLOSED');
|
|
164
|
+
assert.equal(settlement!.values.join(','), "T+1,N/A,O'Brien");
|
|
165
|
+
assert.equal(collision!.values.join(','), 'BUY,SELL');
|
|
166
|
+
assert.ok(unmapped.some((line) => line.includes('enum name collision for TradeInsertSide')));
|
|
167
|
+
|
|
168
|
+
const trade = contracts.find((c) => c.eventName === 'EVENT_TRADE_INSERT')!;
|
|
169
|
+
assert.equal(trade.fields.find((f) => f.name === 'SIDE')!.tsType, 'TradeInsertSide2');
|
|
170
|
+
assert.equal(trade.fields.find((f) => f.name === 'status')!.tsType, 'TradeInsertStatus');
|
|
171
|
+
assert.equal(trade.fields.find((f) => f.name === 'MY-FIELD')!.tsType, 'TradeInsertMyField');
|
|
172
|
+
|
|
173
|
+
const tradeOnly = contracts.find((c) => c.eventName === 'EVENT_TRADE')!;
|
|
174
|
+
assert.equal(tradeOnly.fields.find((f) => f.name === 'INSERT_SIDE')!.tsType, 'TradeInsertSide');
|
|
175
|
+
|
|
176
|
+
const rendered = renderEventTypesFromMetadataContracts(contracts, enums, unmapped);
|
|
177
|
+
assert.is(rendered.includes('export const TradeInsertSide'), true);
|
|
178
|
+
assert.is(rendered.includes('export const OrderInsertSide'), true);
|
|
179
|
+
assert.is(rendered.includes('SIDE?: TradeInsertSide2'), true);
|
|
180
|
+
assert.is(rendered.includes("'MY-FIELD'?: TradeInsertMyField"), true);
|
|
181
|
+
// Non-identifier / quote-bearing values must be quoted on both sides
|
|
182
|
+
assert.is(rendered.includes("'T+1': 'T+1'"), true);
|
|
183
|
+
assert.is(rendered.includes("'N/A': 'N/A'"), true);
|
|
184
|
+
assert.is(rendered.includes("'O\\'Brien': 'O\\'Brien'"), true);
|
|
185
|
+
// Emitted file must be syntactically valid JS for the enum object
|
|
186
|
+
const enumBlock = rendered.slice(
|
|
187
|
+
rendered.indexOf('export const TradeInsertSettlement'),
|
|
188
|
+
rendered.indexOf('export type TradeInsertSettlement'),
|
|
189
|
+
);
|
|
190
|
+
assert.ok(enumBlock.includes('as const'));
|
|
191
|
+
const runnable = enumBlock.replace(/^export\s+/m, '').replace(/\s+as const/, '');
|
|
192
|
+
// eslint-disable-next-line no-new-func -- assert generated enum object parses
|
|
193
|
+
const parsed = new Function(`${runnable}; return TradeInsertSettlement;`)();
|
|
194
|
+
assert.equal(parsed['T+1'], 'T+1');
|
|
195
|
+
assert.equal(parsed['N/A'], 'N/A');
|
|
196
|
+
assert.equal(parsed["O'Brien"], "O'Brien");
|
|
197
|
+
|
|
198
|
+
const ifaceStart = rendered.indexOf('export interface TradeInsertDetails {');
|
|
199
|
+
const ifaceEnd = rendered.indexOf('}', ifaceStart);
|
|
200
|
+
const ifaceBody = rendered.slice(rendered.indexOf('{', ifaceStart) + 1, ifaceEnd);
|
|
201
|
+
const objectLiteral = `{${ifaceBody.replace(/\?:/g, ':').replace(/: [^;]+;/g, ': 1,')}}`;
|
|
202
|
+
// eslint-disable-next-line no-new-func -- assert generated interface keys parse
|
|
203
|
+
const keys = new Function(`return ${objectLiteral};`)() as Record<string, number>;
|
|
204
|
+
assert.equal(keys['MY-FIELD'], 1);
|
|
205
|
+
assert.equal(keys.SIDE, 1);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
suite.run();
|
package/tsconfig.tsbuildinfo
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./src/config.ts","./src/index.ts","./src/kotlin-parse-utils.ts","./src/naming.ts","./src/parse-dao.ts","./src/parse-enums.ts","./src/parse-kotlin.ts","./src/render.ts","./src/types.ts"],"version":"5.9.2"}
|
|
1
|
+
{"root":["./src/config.ts","./src/index.ts","./src/kotlin-parse-utils.ts","./src/naming.ts","./src/parse-dao.ts","./src/parse-enums.ts","./src/parse-kotlin.ts","./src/parse-metadata.ts","./src/render.ts","./src/types.ts"],"version":"5.9.2"}
|