@genesislcap/event-type-codegen 14.489.0-canary.FUI-2574-1

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 (52) hide show
  1. package/README.md +80 -0
  2. package/dist/config.d.ts +7 -0
  3. package/dist/config.d.ts.map +1 -0
  4. package/dist/config.js +58 -0
  5. package/dist/config.js.map +1 -0
  6. package/dist/index.d.ts +12 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +90 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/naming.d.ts +9 -0
  11. package/dist/naming.d.ts.map +1 -0
  12. package/dist/naming.js +26 -0
  13. package/dist/naming.js.map +1 -0
  14. package/dist/parse-dao.d.ts +18 -0
  15. package/dist/parse-dao.d.ts.map +1 -0
  16. package/dist/parse-dao.js +175 -0
  17. package/dist/parse-dao.js.map +1 -0
  18. package/dist/parse-enums.d.ts +16 -0
  19. package/dist/parse-enums.d.ts.map +1 -0
  20. package/dist/parse-enums.js +98 -0
  21. package/dist/parse-enums.js.map +1 -0
  22. package/dist/parse-kotlin.d.ts +15 -0
  23. package/dist/parse-kotlin.d.ts.map +1 -0
  24. package/dist/parse-kotlin.js +190 -0
  25. package/dist/parse-kotlin.js.map +1 -0
  26. package/dist/render.d.ts +4 -0
  27. package/dist/render.d.ts.map +1 -0
  28. package/dist/render.js +89 -0
  29. package/dist/render.js.map +1 -0
  30. package/dist/types.d.ts +51 -0
  31. package/dist/types.d.ts.map +1 -0
  32. package/dist/types.js +3 -0
  33. package/dist/types.js.map +1 -0
  34. package/license.txt +46 -0
  35. package/package.json +38 -0
  36. package/src/config.ts +67 -0
  37. package/src/index.ts +118 -0
  38. package/src/naming.ts +24 -0
  39. package/src/parse-dao.ts +186 -0
  40. package/src/parse-enums.ts +105 -0
  41. package/src/parse-kotlin.ts +216 -0
  42. package/src/render.ts +118 -0
  43. package/src/types.ts +56 -0
  44. package/test/fixtures/client-out/genesis-event-types.ts +102 -0
  45. package/test/fixtures/golden/genesis-event-types.ts +102 -0
  46. package/test/fixtures/mini-server/generated-dao/global/genesis/gen/dao/Instrument.kt +13 -0
  47. package/test/fixtures/mini-server/generated-dao/global/genesis/gen/dao/Trade.kt +51 -0
  48. package/test/fixtures/mini-server/src/main/kotlin/com/example/Handlers.kt +16 -0
  49. package/test/fixtures/mini-server/src/main/kotlin/com/example/dto/Samples.kt +10 -0
  50. package/test/generate.test.ts +88 -0
  51. package/tsconfig.json +12 -0
  52. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,216 @@
1
+ import { readFileSync } from 'node:fs';
2
+
3
+ import { handlerInputToDetailsInterfaceName } from './naming';
4
+ import { kotlinEnumTypeRef } from './parse-enums';
5
+ import type { KotlinEnumMap } from './parse-enums';
6
+ import type { KotlinDto, KotlinProperty, MappedHandler, UnmappedHandler } from './types';
7
+
8
+ const PRIMITIVE_TS: Record<string, string> = {
9
+ String: 'string',
10
+ Int: 'number',
11
+ Long: 'number',
12
+ Short: 'number',
13
+ Byte: 'number',
14
+ Double: 'number',
15
+ Float: 'number',
16
+ Boolean: 'boolean',
17
+ BigDecimal: 'string',
18
+ DateTime: 'string',
19
+ Instant: 'string',
20
+ LocalDate: 'string',
21
+ LocalDateTime: 'string',
22
+ };
23
+
24
+ const DATA_CLASS_START = /^\s*(?:data\s+)?class\s+(\w+)\s*(?:\([^)]*\))?\s*(?:\([^)]*\)|\{)/;
25
+
26
+ const splitTopLevelArgs = (body: string): string[] => {
27
+ const parts: string[] = [];
28
+ let depth = 0;
29
+ let current = '';
30
+ for (const ch of body) {
31
+ if (ch === '(' || ch === '<' || ch === '[') depth += 1;
32
+ if (ch === ')' || ch === '>' || ch === ']') depth -= 1;
33
+ if (ch === ',' && depth === 0) {
34
+ parts.push(current.trim());
35
+ current = '';
36
+ continue;
37
+ }
38
+ current += ch;
39
+ }
40
+ if (current.trim()) parts.push(current.trim());
41
+ return parts;
42
+ };
43
+
44
+ const stripKotlinAnnotations = (segment: string): string =>
45
+ segment
46
+ .replace(/@[A-Za-z_][\w.]*(?:\s*<[^>]*>)?(?:\s*\([^)]*\))?/g, ' ')
47
+ .replace(/\s+/g, ' ')
48
+ .trim();
49
+
50
+ const parseProperty = (segment: string): KotlinProperty | null => {
51
+ const cleaned = stripKotlinAnnotations(segment);
52
+ const match = cleaned.match(/^(?:val|var)\s+(\w+)\s*:\s*([^=]+?)(?:\s*=\s*.+)?$/);
53
+ if (!match) return null;
54
+ const name = match[1];
55
+ let kotlinType = match[2].trim();
56
+ const optional = kotlinType.endsWith('?') || cleaned.includes('=');
57
+ kotlinType = kotlinType.replace(/\?$/, '').trim();
58
+ return { name, kotlinType, optional };
59
+ };
60
+
61
+ const extractDataClasses = (content: string): { name: string; properties: KotlinProperty[] }[] => {
62
+ const results: { name: string; properties: KotlinProperty[] }[] = [];
63
+ const classRegex = /(?:^|\n)\s*(?:data\s+)?class\s+(\w+)\s*\(/gm;
64
+ let match: RegExpExecArray | null;
65
+
66
+ while ((match = classRegex.exec(content)) !== null) {
67
+ const name = match[1];
68
+ const openParenIndex = match.index + match[0].length - 1;
69
+ let depth = 0;
70
+ let closeIndex = -1;
71
+ for (let i = openParenIndex; i < content.length; i += 1) {
72
+ const ch = content[i];
73
+ if (ch === '(') depth += 1;
74
+ if (ch === ')') {
75
+ depth -= 1;
76
+ if (depth === 0) {
77
+ closeIndex = i;
78
+ break;
79
+ }
80
+ }
81
+ }
82
+ if (closeIndex === -1) continue;
83
+
84
+ const body = content.slice(openParenIndex + 1, closeIndex);
85
+ const properties = splitTopLevelArgs(body)
86
+ .map(parseProperty)
87
+ .filter((p): p is KotlinProperty => p != null);
88
+
89
+ if (properties.length > 0) {
90
+ results.push({ name, properties });
91
+ }
92
+ }
93
+
94
+ return results;
95
+ };
96
+
97
+ export const parseDtosFromSources = (files: string[]): Map<string, KotlinDto> => {
98
+ const dtos = new Map<string, KotlinDto>();
99
+
100
+ for (const filePath of files) {
101
+ const rawContent = readFileSync(filePath, 'utf8');
102
+ const content = rawContent.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/.*$/gm, '');
103
+ for (const parsed of extractDataClasses(content)) {
104
+ dtos.set(parsed.name, {
105
+ name: parsed.name,
106
+ properties: parsed.properties,
107
+ filePath,
108
+ });
109
+ }
110
+ }
111
+
112
+ return dtos;
113
+ };
114
+
115
+ const HANDLER_REGEX =
116
+ /(?:eventHandler|contextEventHandler)\s*<\s*([^<>,]+?)(?:\s*,\s*[^>]+)?\s*>\s*\(\s*(?:name\s*=\s*)?"([^"]+)"[^)]*\)/g;
117
+
118
+ const HANDLER_INDEX_INPUT = /^(\w+)\.(By\w+)$/;
119
+
120
+ const unmappedReason = (inputType: string): string | null => {
121
+ const trimmed = inputType.trim();
122
+ if (trimmed === 'Unit') return 'Unit input';
123
+ if (HANDLER_INDEX_INPUT.test(trimmed)) return null;
124
+ if (trimmed.includes('.')) return 'Non-DTO type (DAO/index/nested qualified type)';
125
+ return null;
126
+ };
127
+
128
+ export const parseHandlersFromSources = (
129
+ files: string[],
130
+ dtos: Map<string, KotlinDto>,
131
+ ): { mapped: MappedHandler[]; unmapped: UnmappedHandler[] } => {
132
+ const mapped: MappedHandler[] = [];
133
+ const unmapped: UnmappedHandler[] = [];
134
+ const seenEvents = new Set<string>();
135
+
136
+ for (const filePath of files) {
137
+ const content = readFileSync(filePath, 'utf8');
138
+ const stripped = content.replace(/\/\*[\s\S]*?\*\//g, '');
139
+
140
+ const tryMatch = (regex: RegExp) => {
141
+ let handlerMatch: RegExpExecArray | null;
142
+ regex.lastIndex = 0;
143
+ while ((handlerMatch = regex.exec(stripped)) !== null) {
144
+ const inputType = handlerMatch[1].trim();
145
+ const handlerName = handlerMatch[2].trim();
146
+ const eventName = handlerName.startsWith('EVENT_') ? handlerName : `EVENT_${handlerName}`;
147
+
148
+ const skipReason = unmappedReason(inputType);
149
+ if (skipReason) {
150
+ unmapped.push({
151
+ handlerName,
152
+ inputType,
153
+ reason: skipReason,
154
+ sourceFile: filePath,
155
+ });
156
+ continue;
157
+ }
158
+
159
+ if (!dtos.has(inputType)) {
160
+ unmapped.push({
161
+ handlerName,
162
+ inputType,
163
+ reason: 'No matching data class or generated DAO',
164
+ sourceFile: filePath,
165
+ });
166
+ continue;
167
+ }
168
+
169
+ if (seenEvents.has(eventName)) continue;
170
+ seenEvents.add(eventName);
171
+
172
+ mapped.push({
173
+ eventName,
174
+ inputType,
175
+ detailsInterface: handlerInputToDetailsInterfaceName(inputType),
176
+ sourceFile: filePath,
177
+ });
178
+ }
179
+ };
180
+
181
+ tryMatch(HANDLER_REGEX);
182
+ }
183
+
184
+ mapped.sort((a, b) => a.eventName.localeCompare(b.eventName));
185
+ unmapped.sort((a, b) => a.handlerName.localeCompare(b.handlerName));
186
+
187
+ return { mapped, unmapped };
188
+ };
189
+
190
+ export const kotlinTypeToTs = (
191
+ kotlinType: string,
192
+ dtos: Map<string, KotlinDto>,
193
+ enums: KotlinEnumMap = new Map(),
194
+ ): string => {
195
+ const listMatch = kotlinType.match(/^(?:List|MutableList|Set|MutableSet)<(.+)>$/);
196
+ if (listMatch) {
197
+ return `${kotlinTypeToTs(listMatch[1].trim(), dtos, enums)}[]`;
198
+ }
199
+
200
+ if (PRIMITIVE_TS[kotlinType]) {
201
+ return PRIMITIVE_TS[kotlinType];
202
+ }
203
+
204
+ const enumTs = kotlinEnumTypeRef(kotlinType, enums);
205
+ if (enumTs) {
206
+ return enumTs;
207
+ }
208
+
209
+ if (dtos.has(kotlinType)) {
210
+ return handlerInputToDetailsInterfaceName(kotlinType);
211
+ }
212
+
213
+ return 'unknown';
214
+ };
215
+
216
+ export { extractDataClasses, DATA_CLASS_START };
package/src/render.ts ADDED
@@ -0,0 +1,118 @@
1
+ import {
2
+ kotlinPropertyToWireField,
3
+ handlerInputToDetailsInterfaceName,
4
+ eventNameToEventClassName,
5
+ } from './naming';
6
+ import { kotlinEnumTypeRef, type KotlinEnumMap } from './parse-enums';
7
+ import { kotlinTypeToTs } from './parse-kotlin';
8
+ import type { KotlinDto, MappedHandler, UnmappedHandler } from './types';
9
+
10
+ const renderInterface = (
11
+ dto: KotlinDto,
12
+ dtos: Map<string, KotlinDto>,
13
+ enums: KotlinEnumMap,
14
+ ): string => {
15
+ const interfaceName = handlerInputToDetailsInterfaceName(dto.name);
16
+ const lines = dto.properties.map((prop) => {
17
+ const wireName = kotlinPropertyToWireField(prop.name);
18
+ const tsType = kotlinTypeToTs(prop.kotlinType, dtos, enums);
19
+ const optional = prop.optional ? '?' : '';
20
+ return ` ${wireName}${optional}: ${tsType};`;
21
+ });
22
+
23
+ return `export interface ${interfaceName} {\n${lines.join('\n')}\n}`;
24
+ };
25
+
26
+ const renderEventClasses = (mapped: MappedHandler[]): string => {
27
+ if (mapped.length === 0) return '';
28
+
29
+ const sorted = [...mapped].sort((a, b) => a.eventName.localeCompare(b.eventName));
30
+
31
+ return sorted
32
+ .map((handler) => {
33
+ const className = eventNameToEventClassName(handler.eventName);
34
+ return `export class ${className} extends GenesisEvent<'${handler.eventName}', ${handler.detailsInterface}> {
35
+ readonly MESSAGE_TYPE = '${handler.eventName}' as const;
36
+
37
+ constructor(details: ${handler.detailsInterface}) {
38
+ super(details);
39
+ }
40
+ }`;
41
+ })
42
+ .join('\n\n');
43
+ };
44
+
45
+ const renderUnmappedComment = (unmapped: UnmappedHandler[]): string => {
46
+ if (unmapped.length === 0) return '';
47
+
48
+ const lines = unmapped.map(
49
+ (entry) =>
50
+ ` * - ${entry.handlerName} (${entry.inputType}): ${entry.reason} [${entry.sourceFile.split(/[/\\]/).pop()}]`,
51
+ );
52
+
53
+ return `/**\n * Unmapped handlers (not included in EventDetailsMap):\n${lines.join('\n')}\n */\n\n`;
54
+ };
55
+
56
+ const renderEnumExports = (enums: KotlinEnumMap, usedEnumNames: Set<string>): string => {
57
+ const blocks: string[] = [];
58
+ for (const name of [...usedEnumNames].sort()) {
59
+ const values = enums.get(name);
60
+ if (!values?.length) continue;
61
+ const entries = values.map((value) => ` ${value}: '${value}',`).join('\n');
62
+ blocks.push(
63
+ `export const ${name} = {\n${entries}\n} as const;\n\nexport type ${name} = (typeof ${name})[keyof typeof ${name}];`,
64
+ );
65
+ }
66
+ return blocks.length > 0 ? `${blocks.join('\n\n')}\n\n` : '';
67
+ };
68
+
69
+ const collectUsedEnumNames = (
70
+ mapped: MappedHandler[],
71
+ dtos: Map<string, KotlinDto>,
72
+ enums: KotlinEnumMap,
73
+ ): Set<string> => {
74
+ const used = new Set<string>();
75
+ for (const handler of mapped) {
76
+ const dto = dtos.get(handler.inputType);
77
+ if (!dto) continue;
78
+ for (const prop of dto.properties) {
79
+ const ref = kotlinEnumTypeRef(prop.kotlinType, enums);
80
+ if (ref) used.add(ref);
81
+ }
82
+ }
83
+ return used;
84
+ };
85
+
86
+ export const renderEventTypesFile = (
87
+ mapped: MappedHandler[],
88
+ unmapped: UnmappedHandler[],
89
+ dtos: Map<string, KotlinDto>,
90
+ enums: KotlinEnumMap = new Map(),
91
+ ): string => {
92
+ const usedDtos = new Set(mapped.map((m) => m.inputType));
93
+ const interfaces = [...usedDtos]
94
+ .map((name) => dtos.get(name))
95
+ .filter((dto): dto is KotlinDto => dto != null)
96
+ .sort((a, b) => a.name.localeCompare(b.name))
97
+ .map((dto) => renderInterface(dto, dtos, enums));
98
+
99
+ const usedEnumNames = collectUsedEnumNames(mapped, dtos, enums);
100
+ const enumExports = renderEnumExports(enums, usedEnumNames);
101
+
102
+ const mapEntries = mapped.map((m) => ` ${m.eventName}: ${m.detailsInterface};`).join('\n');
103
+
104
+ const eventClasses = renderEventClasses(mapped);
105
+ const foundationCommsImport =
106
+ mapped.length > 0 ? `import { GenesisEvent } from '@genesislcap/foundation-comms';\n\n` : '';
107
+
108
+ return `// AUTO-GENERATED — do not edit
109
+
110
+ ${renderUnmappedComment(unmapped)}${foundationCommsImport}${enumExports}${interfaces.join('\n\n')}
111
+
112
+ export interface EventDetailsMap {
113
+ ${mapEntries}
114
+ }
115
+
116
+ export type GenesisEventName = keyof EventDetailsMap;
117
+ ${eventClasses ? `\n${eventClasses}\n` : ''}`;
118
+ };
package/src/types.ts ADDED
@@ -0,0 +1,56 @@
1
+ export type EventTypesConfig = {
2
+ enabled?: boolean;
3
+ serverModule?: string;
4
+ kotlinSources?: string[];
5
+ /**
6
+ * Glob patterns (relative to the parent of `serverModule`, typically `../server`)
7
+ * for Genesis generated table DAO Kotlin sources (`…/gen/dao/Trade.kt`).
8
+ */
9
+ generatedDaoSources?: string[];
10
+ handlerScan?: string[];
11
+ output?: string;
12
+ runOnBuild?: boolean;
13
+ };
14
+
15
+ export type EventTypesResolvedConfig = {
16
+ enabled: boolean;
17
+ serverModule: string;
18
+ kotlinSources: string[];
19
+ generatedDaoSources: string[];
20
+ handlerScan: string[];
21
+ output: string;
22
+ runOnBuild: boolean;
23
+ };
24
+
25
+ export type KotlinProperty = {
26
+ name: string;
27
+ kotlinType: string;
28
+ optional: boolean;
29
+ };
30
+
31
+ export type KotlinDto = {
32
+ name: string;
33
+ properties: KotlinProperty[];
34
+ filePath: string;
35
+ };
36
+
37
+ export type MappedHandler = {
38
+ eventName: string;
39
+ inputType: string;
40
+ detailsInterface: string;
41
+ sourceFile: string;
42
+ };
43
+
44
+ export type UnmappedHandler = {
45
+ handlerName: string;
46
+ inputType: string;
47
+ reason: string;
48
+ sourceFile: string;
49
+ };
50
+
51
+ export type GenerateResult = {
52
+ outputPath: string;
53
+ eventCount: number;
54
+ dtoCount: number;
55
+ unmappedCount: number;
56
+ };
@@ -0,0 +1,102 @@
1
+ // AUTO-GENERATED — do not edit
2
+
3
+ /**
4
+ * Unmapped handlers (not included in EventDetailsMap):
5
+ * - NOOP (Unit): Unit input [Handlers.kt]
6
+ */
7
+
8
+ import { GenesisEvent } from '@genesislcap/foundation-comms';
9
+
10
+ export const Side = {
11
+ BUY: 'BUY',
12
+ SELL: 'SELL',
13
+ } as const;
14
+
15
+ export type Side = (typeof Side)[keyof typeof Side];
16
+
17
+ export interface FooInputDetails {
18
+ FOO_ID: string;
19
+ BAR_ID?: string;
20
+ }
21
+
22
+ export interface InstrumentByIdDetails {
23
+ INSTRUMENT_ID: string;
24
+ }
25
+
26
+ export interface NestedInputDetails {
27
+ ITEMS: FooInputDetails[];
28
+ }
29
+
30
+ export interface TradeDetails {
31
+ TRADE_ID?: string;
32
+ INSTRUMENT_ID: string;
33
+ COUNTERPARTY_ID: string;
34
+ QUANTITY: number;
35
+ SIDE?: Side;
36
+ PRICE: number;
37
+ COMMISSION?: string;
38
+ CREATED_AT?: string;
39
+ }
40
+
41
+ export interface TradeByIdDetails {
42
+ TRADE_ID: string;
43
+ }
44
+
45
+ export interface EventDetailsMap {
46
+ EVENT_BAR_BAZ: NestedInputDetails;
47
+ EVENT_CONTEXT_FOO: FooInputDetails;
48
+ EVENT_DAO_HANDLER: InstrumentByIdDetails;
49
+ EVENT_FOO: FooInputDetails;
50
+ EVENT_TRADE_DELETE: TradeByIdDetails;
51
+ EVENT_TRADE_INSERT: TradeDetails;
52
+ }
53
+
54
+ export type GenesisEventName = keyof EventDetailsMap;
55
+
56
+ export class EventBarBaz extends GenesisEvent<'EVENT_BAR_BAZ', NestedInputDetails> {
57
+ readonly MESSAGE_TYPE = 'EVENT_BAR_BAZ' as const;
58
+
59
+ constructor(details: NestedInputDetails) {
60
+ super(details);
61
+ }
62
+ }
63
+
64
+ export class EventContextFoo extends GenesisEvent<'EVENT_CONTEXT_FOO', FooInputDetails> {
65
+ readonly MESSAGE_TYPE = 'EVENT_CONTEXT_FOO' as const;
66
+
67
+ constructor(details: FooInputDetails) {
68
+ super(details);
69
+ }
70
+ }
71
+
72
+ export class EventDaoHandler extends GenesisEvent<'EVENT_DAO_HANDLER', InstrumentByIdDetails> {
73
+ readonly MESSAGE_TYPE = 'EVENT_DAO_HANDLER' as const;
74
+
75
+ constructor(details: InstrumentByIdDetails) {
76
+ super(details);
77
+ }
78
+ }
79
+
80
+ export class EventFoo extends GenesisEvent<'EVENT_FOO', FooInputDetails> {
81
+ readonly MESSAGE_TYPE = 'EVENT_FOO' as const;
82
+
83
+ constructor(details: FooInputDetails) {
84
+ super(details);
85
+ }
86
+ }
87
+
88
+ export class EventTradeDelete extends GenesisEvent<'EVENT_TRADE_DELETE', TradeByIdDetails> {
89
+ readonly MESSAGE_TYPE = 'EVENT_TRADE_DELETE' as const;
90
+
91
+ constructor(details: TradeByIdDetails) {
92
+ super(details);
93
+ }
94
+ }
95
+
96
+ export class EventTradeInsert extends GenesisEvent<'EVENT_TRADE_INSERT', TradeDetails> {
97
+ readonly MESSAGE_TYPE = 'EVENT_TRADE_INSERT' as const;
98
+
99
+ constructor(details: TradeDetails) {
100
+ super(details);
101
+ }
102
+ }
@@ -0,0 +1,102 @@
1
+ // AUTO-GENERATED — do not edit
2
+
3
+ /**
4
+ * Unmapped handlers (not included in EventDetailsMap):
5
+ * - NOOP (Unit): Unit input [Handlers.kt]
6
+ */
7
+
8
+ import { GenesisEvent } from '@genesislcap/foundation-comms';
9
+
10
+ export const Side = {
11
+ BUY: 'BUY',
12
+ SELL: 'SELL',
13
+ } as const;
14
+
15
+ export type Side = (typeof Side)[keyof typeof Side];
16
+
17
+ export interface FooInputDetails {
18
+ FOO_ID: string;
19
+ BAR_ID?: string;
20
+ }
21
+
22
+ export interface InstrumentByIdDetails {
23
+ INSTRUMENT_ID: string;
24
+ }
25
+
26
+ export interface NestedInputDetails {
27
+ ITEMS: FooInputDetails[];
28
+ }
29
+
30
+ export interface TradeDetails {
31
+ TRADE_ID?: string;
32
+ INSTRUMENT_ID: string;
33
+ COUNTERPARTY_ID: string;
34
+ QUANTITY: number;
35
+ SIDE?: Side;
36
+ PRICE: number;
37
+ COMMISSION?: string;
38
+ CREATED_AT?: string;
39
+ }
40
+
41
+ export interface TradeByIdDetails {
42
+ TRADE_ID: string;
43
+ }
44
+
45
+ export interface EventDetailsMap {
46
+ EVENT_BAR_BAZ: NestedInputDetails;
47
+ EVENT_CONTEXT_FOO: FooInputDetails;
48
+ EVENT_DAO_HANDLER: InstrumentByIdDetails;
49
+ EVENT_FOO: FooInputDetails;
50
+ EVENT_TRADE_DELETE: TradeByIdDetails;
51
+ EVENT_TRADE_INSERT: TradeDetails;
52
+ }
53
+
54
+ export type GenesisEventName = keyof EventDetailsMap;
55
+
56
+ export class EventBarBaz extends GenesisEvent<'EVENT_BAR_BAZ', NestedInputDetails> {
57
+ readonly MESSAGE_TYPE = 'EVENT_BAR_BAZ' as const;
58
+
59
+ constructor(details: NestedInputDetails) {
60
+ super(details);
61
+ }
62
+ }
63
+
64
+ export class EventContextFoo extends GenesisEvent<'EVENT_CONTEXT_FOO', FooInputDetails> {
65
+ readonly MESSAGE_TYPE = 'EVENT_CONTEXT_FOO' as const;
66
+
67
+ constructor(details: FooInputDetails) {
68
+ super(details);
69
+ }
70
+ }
71
+
72
+ export class EventDaoHandler extends GenesisEvent<'EVENT_DAO_HANDLER', InstrumentByIdDetails> {
73
+ readonly MESSAGE_TYPE = 'EVENT_DAO_HANDLER' as const;
74
+
75
+ constructor(details: InstrumentByIdDetails) {
76
+ super(details);
77
+ }
78
+ }
79
+
80
+ export class EventFoo extends GenesisEvent<'EVENT_FOO', FooInputDetails> {
81
+ readonly MESSAGE_TYPE = 'EVENT_FOO' as const;
82
+
83
+ constructor(details: FooInputDetails) {
84
+ super(details);
85
+ }
86
+ }
87
+
88
+ export class EventTradeDelete extends GenesisEvent<'EVENT_TRADE_DELETE', TradeByIdDetails> {
89
+ readonly MESSAGE_TYPE = 'EVENT_TRADE_DELETE' as const;
90
+
91
+ constructor(details: TradeByIdDetails) {
92
+ super(details);
93
+ }
94
+ }
95
+
96
+ export class EventTradeInsert extends GenesisEvent<'EVENT_TRADE_INSERT', TradeDetails> {
97
+ readonly MESSAGE_TYPE = 'EVENT_TRADE_INSERT' as const;
98
+
99
+ constructor(details: TradeDetails) {
100
+ super(details);
101
+ }
102
+ }
@@ -0,0 +1,13 @@
1
+ package global.genesis.gen.dao
2
+
3
+ import global.genesis.db.entity.TableEntity
4
+ import java.io.Serializable
5
+
6
+ /** Fixture stand-in for generated Instrument DAO (index only). */
7
+ public class Instrument public constructor(
8
+ public var instrumentId: String,
9
+ ) : Serializable, TableEntity {
10
+ public data class ById(
11
+ public val instrumentId: String,
12
+ )
13
+ }
@@ -0,0 +1,51 @@
1
+ package global.genesis.gen.dao
2
+
3
+ import global.genesis.db.entity.TableEntity
4
+ import global.genesis.dictionary.Field
5
+ import global.genesis.dictionary.annotation.GenesisType
6
+ import global.genesis.message.core.annotation.JsonStringDefault
7
+ import global.genesis.message.core.annotation.Title
8
+ import java.io.Serializable
9
+ import java.math.BigDecimal
10
+ import org.joda.time.DateTime
11
+
12
+ /**
13
+ * Fixture stand-in for Genesis generated Trade DAO (trimmed constructor).
14
+ */
15
+ @Title("Trade")
16
+ public class Trade public constructor(
17
+ tradeId: String? = null,
18
+ @Title("Instrument Id")
19
+ @GenesisType(Field.Type.STRING)
20
+ public var instrumentId: String, // instrument ref, required
21
+ @Title("Counterparty Id")
22
+ @GenesisType(Field.Type.STRING)
23
+ public var counterpartyId: String,
24
+ @Title("Quantity")
25
+ @GenesisType(Field.Type.INT)
26
+ public var quantity: Int,
27
+ @Title("Side")
28
+ @GenesisType(Field.Type.ENUM)
29
+ @JsonStringDefault("BUY")
30
+ public var side: Side = Side.BUY,
31
+ @Title("Price")
32
+ @GenesisType(Field.Type.DOUBLE)
33
+ public var price: Double,
34
+ @Title("Commission")
35
+ @GenesisType(Field.Type.BIGDECIMAL)
36
+ public var commission: BigDecimal? = null,
37
+ @Title("Created At")
38
+ @GenesisType(Field.Type.DATETIME)
39
+ public var createdAt: DateTime? = null,
40
+ ) : Serializable, TableEntity {
41
+ public var tradeId: String = tradeId ?: ""
42
+
43
+ public data class ById(
44
+ public val tradeId: String,
45
+ )
46
+ }
47
+
48
+ enum class Side {
49
+ BUY,
50
+ SELL,
51
+ }
@@ -0,0 +1,16 @@
1
+ package com.example
2
+
3
+ import com.example.dto.FooInput
4
+ import com.example.dto.NestedInput
5
+
6
+ fun register() {
7
+ eventHandler<FooInput>("FOO")
8
+ eventHandler<NestedInput>("BAR_BAZ")
9
+ contextEventHandler<FooInput, String>(name = "CONTEXT_FOO")
10
+ // Full table DAO — resolved from generated-dao sources
11
+ eventHandler<Trade>("TRADE_INSERT", transactional = true)
12
+ eventHandler<Trade.ById>("TRADE_DELETE", transactional = true)
13
+ // Index type — resolved from generated DAO nested data class
14
+ eventHandler<Instrument.ById>("DAO_HANDLER", transactional = true)
15
+ eventHandler<Unit>("NOOP")
16
+ }
@@ -0,0 +1,10 @@
1
+ package com.example.dto
2
+
3
+ data class FooInput(
4
+ @NotNull val fooId: String, // unique id, required
5
+ /* optional bar, with comma */ val barId: String? = null,
6
+ )
7
+
8
+ data class NestedInput(
9
+ val items: List<FooInput>,
10
+ )