@genesislcap/event-type-codegen 15.17.0 → 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/kotlin-parse-utils.d.ts +2 -1
- package/dist/kotlin-parse-utils.d.ts.map +1 -1
- package/dist/kotlin-parse-utils.js +10 -4
- package/dist/kotlin-parse-utils.js.map +1 -1
- package/dist/naming.d.ts +9 -0
- package/dist/naming.d.ts.map +1 -1
- package/dist/naming.js +49 -2
- package/dist/naming.js.map +1 -1
- package/dist/parse-kotlin.d.ts.map +1 -1
- package/dist/parse-kotlin.js +5 -4
- package/dist/parse-kotlin.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/kotlin-parse-utils.ts +17 -7
- package/src/naming.ts +58 -2
- package/src/parse-kotlin.ts +5 -4
- package/src/parse-metadata.ts +327 -0
- package/test/fixtures/client-out/genesis-event-types.ts +1 -1
- package/test/fixtures/golden/genesis-event-types.ts +1 -1
- package/test/kotlin-parse-utils.test.ts +42 -0
- package/test/naming.test.ts +49 -0
- package/test/parse-metadata.test.ts +208 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -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';
|
|
@@ -38,25 +38,35 @@ export type ParseKotlinPropertyOptions = {
|
|
|
38
38
|
allowBareParams?: boolean;
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
+
/** Plain or backtick-escaped Kotlin identifier (`value` for reserved words). */
|
|
42
|
+
const KOTLIN_IDENT = '(?:\\w+|`[^`]+`)';
|
|
43
|
+
|
|
44
|
+
const PROPERTY_REGEX = new RegExp(
|
|
45
|
+
`^(?:public\\s+)?(?:val|var)\\s+(${KOTLIN_IDENT})\\s*:\\s*([^=]+?)(?:\\s*=\\s*.+)?$`,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const BARE_PROPERTY_REGEX = new RegExp(`^(${KOTLIN_IDENT})\\s*:\\s*([^=]+?)(?:\\s*=\\s*.+)?$`);
|
|
49
|
+
|
|
50
|
+
const stripBackticks = (ident: string): string =>
|
|
51
|
+
ident.startsWith('`') && ident.endsWith('`') ? ident.slice(1, -1) : ident;
|
|
52
|
+
|
|
41
53
|
/**
|
|
42
54
|
* Parse a Kotlin constructor / data-class property segment into a wire-facing property.
|
|
43
|
-
* Accepts optional public modifier
|
|
55
|
+
* Accepts optional public modifier, use-site-stripped annotations, and backtick-escaped
|
|
56
|
+
* reserved identifiers (e.g. `value` → value → wire VALUE).
|
|
44
57
|
*/
|
|
45
58
|
export const parseKotlinProperty = (
|
|
46
59
|
segment: string,
|
|
47
60
|
options: ParseKotlinPropertyOptions = {},
|
|
48
61
|
): KotlinProperty | null => {
|
|
49
62
|
const cleaned = stripKotlinAnnotations(segment);
|
|
50
|
-
const propertyMatch = cleaned.match(
|
|
51
|
-
/^(?:public\s+)?(?:val|var)\s+(\w+)\s*:\s*([^=]+?)(?:\s*=\s*.+)?$/,
|
|
52
|
-
);
|
|
63
|
+
const propertyMatch = cleaned.match(PROPERTY_REGEX);
|
|
53
64
|
const match =
|
|
54
|
-
propertyMatch ??
|
|
55
|
-
(options.allowBareParams ? cleaned.match(/^(\w+)\s*:\s*([^=]+?)(?:\s*=\s*.+)?$/) : null);
|
|
65
|
+
propertyMatch ?? (options.allowBareParams ? cleaned.match(BARE_PROPERTY_REGEX) : null);
|
|
56
66
|
|
|
57
67
|
if (!match) return null;
|
|
58
68
|
|
|
59
|
-
const name = match[1];
|
|
69
|
+
const name = stripBackticks(match[1]);
|
|
60
70
|
let kotlinType = match[2].trim();
|
|
61
71
|
const optional = kotlinType.endsWith('?') || cleaned.includes('=');
|
|
62
72
|
kotlinType = kotlinType.replace(/\?$/, '').trim();
|
package/src/naming.ts
CHANGED
|
@@ -1,8 +1,64 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { pascalCase } from 'change-case';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Character classification used by {@link toUpperUnderscoreWithAcronyms}.
|
|
5
|
+
* Mirrors genesis-server `CharType` in GenesisMessage.kt.
|
|
6
|
+
*/
|
|
7
|
+
type CharType = 'UPPERCASE' | 'LOWERCASE' | 'NUMBER';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Convert camelCase / PascalCase to UPPER_UNDERSCORE, inserting `_` before digits and
|
|
11
|
+
* splitting acronyms the same way as genesis-server `toUpperUnderscoreWithAcronyms`.
|
|
12
|
+
*
|
|
13
|
+
* Examples: `addressLine1` → `ADDRESS_LINE_1`, `orderID` → `ORDER_ID`.
|
|
14
|
+
*
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
export const toUpperUnderscoreWithAcronyms = (name: string): string => {
|
|
18
|
+
if (!name) return name;
|
|
19
|
+
|
|
20
|
+
// Already wire-formatted — genesis-server passes these through unchanged.
|
|
21
|
+
if (/^[A-Z0-9]+(?:_[A-Z0-9]+)*$/.test(name)) return name;
|
|
22
|
+
|
|
23
|
+
const length = name.length;
|
|
24
|
+
const result: string[] = [];
|
|
25
|
+
// Genesis seeds non-upper (including digits) as NUMBER so the first digit after
|
|
26
|
+
// letters still gets a leading underscore on subsequent chars.
|
|
27
|
+
let lastCharacterType: CharType = name[0] >= 'A' && name[0] <= 'Z' ? 'UPPERCASE' : 'NUMBER';
|
|
28
|
+
|
|
29
|
+
result.push(name[0].toUpperCase());
|
|
30
|
+
|
|
31
|
+
const isUpperOrDigit = (c: string): boolean => (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9');
|
|
32
|
+
|
|
33
|
+
for (let i = 1; i < length; i += 1) {
|
|
34
|
+
const ch = name[i];
|
|
35
|
+
if (ch >= 'A' && ch <= 'Z') {
|
|
36
|
+
// Continue an acronym run when the next char is also upper/digit; otherwise split.
|
|
37
|
+
const continueAcronym =
|
|
38
|
+
lastCharacterType === 'UPPERCASE' && (i + 1 >= length || isUpperOrDigit(name[i + 1]));
|
|
39
|
+
if (!continueAcronym) {
|
|
40
|
+
result.push('_');
|
|
41
|
+
}
|
|
42
|
+
result.push(ch);
|
|
43
|
+
lastCharacterType = 'UPPERCASE';
|
|
44
|
+
} else if (ch >= '0' && ch <= '9') {
|
|
45
|
+
if (lastCharacterType !== 'NUMBER') {
|
|
46
|
+
result.push('_');
|
|
47
|
+
}
|
|
48
|
+
result.push(ch);
|
|
49
|
+
lastCharacterType = 'NUMBER';
|
|
50
|
+
} else {
|
|
51
|
+
result.push(ch.toUpperCase());
|
|
52
|
+
lastCharacterType = 'LOWERCASE';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return result.join('');
|
|
57
|
+
};
|
|
2
58
|
|
|
3
59
|
/** Wire field name for a Kotlin property (DETAILS uses UPPER_SNAKE_CASE). */
|
|
4
60
|
export const kotlinPropertyToWireField = (propertyName: string): string =>
|
|
5
|
-
|
|
61
|
+
toUpperUnderscoreWithAcronyms(propertyName);
|
|
6
62
|
|
|
7
63
|
/** Generated interface name for a Kotlin DTO or handler input type. */
|
|
8
64
|
export const dtoToDetailsInterfaceName = (dtoName: string): string =>
|
package/src/parse-kotlin.ts
CHANGED
|
@@ -16,10 +16,11 @@ const PRIMITIVE_TS: Record<string, string> = {
|
|
|
16
16
|
Float: 'number',
|
|
17
17
|
Boolean: 'boolean',
|
|
18
18
|
BigDecimal: 'string',
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
// Genesis wire JSON_TYPE for DATE/DATETIME is integer (epoch millis).
|
|
20
|
+
DateTime: 'number',
|
|
21
|
+
Instant: 'number',
|
|
22
|
+
LocalDate: 'number',
|
|
23
|
+
LocalDateTime: 'number',
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
const DATA_CLASS_START = /^\s*(?:data\s+)?class\s+(\w+)\s*(?:\([^)]*\))?\s*(?:\([^)]*\)|\{)/;
|