@eventvisor/core 0.14.0 → 0.16.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/CHANGELOG.md +22 -0
- package/lib/cli/plugins.js +2 -0
- package/lib/cli/plugins.js.map +1 -1
- package/lib/generate-code/index.d.ts +9 -0
- package/lib/generate-code/index.js +90 -0
- package/lib/generate-code/index.js.map +1 -0
- package/lib/generate-code/typescript/generateInterface.d.ts +2 -0
- package/lib/generate-code/typescript/generateInterface.js +80 -0
- package/lib/generate-code/typescript/generateInterface.js.map +1 -0
- package/lib/generate-code/typescript/generateInterface.spec.d.ts +1 -0
- package/lib/generate-code/typescript/generateInterface.spec.js +792 -0
- package/lib/generate-code/typescript/generateInterface.spec.js.map +1 -0
- package/lib/generate-code/typescript/index.d.ts +3 -0
- package/lib/generate-code/typescript/index.js +172 -0
- package/lib/generate-code/typescript/index.js.map +1 -0
- package/package.json +4 -4
- package/src/cli/plugins.ts +2 -0
- package/src/generate-code/index.ts +79 -0
- package/src/generate-code/typescript/generateInterface.spec.ts +847 -0
- package/src/generate-code/typescript/generateInterface.ts +89 -0
- package/src/generate-code/typescript/index.ts +199 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { JSONSchema } from "@eventvisor/types";
|
|
2
|
+
|
|
3
|
+
export function generateInterface(schema: JSONSchema, interfaceName: string): string {
|
|
4
|
+
function generateType(schema: JSONSchema, indent: number = 0): string {
|
|
5
|
+
const indentStr = " ".repeat(indent);
|
|
6
|
+
|
|
7
|
+
// Handle const
|
|
8
|
+
if (schema.const !== undefined) {
|
|
9
|
+
return JSON.stringify(schema.const);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Handle enum
|
|
13
|
+
if (schema.enum && schema.enum.length > 0) {
|
|
14
|
+
const enumValues = schema.enum.map((val) => JSON.stringify(val));
|
|
15
|
+
return enumValues.join(" | ");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Handle array
|
|
19
|
+
if (schema.type === "array") {
|
|
20
|
+
if (schema.items) {
|
|
21
|
+
if (Array.isArray(schema.items)) {
|
|
22
|
+
// Tuple type
|
|
23
|
+
const tupleTypes = schema.items.map((item) => generateType(item, indent));
|
|
24
|
+
return `[${tupleTypes.join(", ")}]`;
|
|
25
|
+
} else {
|
|
26
|
+
// Array type
|
|
27
|
+
const itemType = generateType(schema.items, indent);
|
|
28
|
+
return `${itemType}[]`;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return "any[]";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Handle object
|
|
35
|
+
if (schema.type === "object" || (schema.properties && !schema.type)) {
|
|
36
|
+
if (!schema.properties || Object.keys(schema.properties).length === 0) {
|
|
37
|
+
return "Record<string, any>";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const required = schema.required || [];
|
|
41
|
+
const properties = Object.entries(schema.properties).map(([key, propSchema]) => {
|
|
42
|
+
const isRequired = required.includes(key);
|
|
43
|
+
const optionalMarker = isRequired ? "" : "?";
|
|
44
|
+
const propType = generateType(propSchema, indent + 1);
|
|
45
|
+
return `${indentStr} ${key}${optionalMarker}: ${propType};`;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return `{\n${properties.join("\n")}\n${indentStr}}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Handle primitives
|
|
52
|
+
switch (schema.type) {
|
|
53
|
+
case "string":
|
|
54
|
+
return "string";
|
|
55
|
+
case "number":
|
|
56
|
+
case "integer":
|
|
57
|
+
return "number";
|
|
58
|
+
case "boolean":
|
|
59
|
+
return "boolean";
|
|
60
|
+
case "null":
|
|
61
|
+
return "null";
|
|
62
|
+
default:
|
|
63
|
+
return "any";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const typeDefinition = generateType(schema);
|
|
68
|
+
|
|
69
|
+
// Use 'type' for primitives, arrays, tuples, enums, const, and Record types
|
|
70
|
+
// Use 'interface' for object types with properties
|
|
71
|
+
// Object types have property declarations (contain ': ' or '?: ' followed by type)
|
|
72
|
+
// Const objects are JSON stringified and don't have property declarations
|
|
73
|
+
const trimmed = typeDefinition.trim();
|
|
74
|
+
const isArrayType = trimmed.endsWith("[]");
|
|
75
|
+
const isObjectType =
|
|
76
|
+
!isArrayType &&
|
|
77
|
+
trimmed.startsWith("{") &&
|
|
78
|
+
!trimmed.startsWith('{"') &&
|
|
79
|
+
(trimmed.includes(":\n") ||
|
|
80
|
+
trimmed.includes("?:\n") ||
|
|
81
|
+
trimmed.includes(": ") ||
|
|
82
|
+
trimmed.includes("?: "));
|
|
83
|
+
const declarationType = isObjectType ? "interface" : "type";
|
|
84
|
+
const separator = isObjectType ? " " : " = ";
|
|
85
|
+
|
|
86
|
+
const description = schema.description ? `/** ${schema.description} */\n` : "";
|
|
87
|
+
|
|
88
|
+
return `${description}export ${declarationType} ${interfaceName}${separator}${typeDefinition}`;
|
|
89
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
import { Dependencies } from "../../dependencies";
|
|
5
|
+
import { generateInterface } from "./generateInterface";
|
|
6
|
+
|
|
7
|
+
export function getInterfaceName(entityName: string, suffix: string = ""): string {
|
|
8
|
+
return (
|
|
9
|
+
entityName
|
|
10
|
+
.replace(/[^a-zA-Z0-9]+/g, " ") // Replace all special characters with spaces
|
|
11
|
+
.split(" ")
|
|
12
|
+
.filter((word) => word.length > 0) // Remove empty strings
|
|
13
|
+
.map((word) => {
|
|
14
|
+
// Check if word already has mixed case (camelCase/PascalCase pattern)
|
|
15
|
+
// This detects if there are both lowercase and uppercase letters in the word
|
|
16
|
+
const hasLowercase = /[a-z]/.test(word);
|
|
17
|
+
const hasUppercase = /[A-Z]/.test(word);
|
|
18
|
+
const hasMixedCase = hasLowercase && hasUppercase;
|
|
19
|
+
|
|
20
|
+
if (hasMixedCase) {
|
|
21
|
+
// Preserve existing casing structure, just ensure first letter is uppercase
|
|
22
|
+
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
23
|
+
}
|
|
24
|
+
// Normal PascalCase transformation for words without mixed case
|
|
25
|
+
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
26
|
+
})
|
|
27
|
+
.join("") + suffix
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function generateTypeScriptCodeForProject(deps: Dependencies, outputPath: string) {
|
|
32
|
+
const { datasource } = deps;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Attributes
|
|
36
|
+
*/
|
|
37
|
+
const generatedAttributes: {
|
|
38
|
+
entityName: string;
|
|
39
|
+
interfaceName: string;
|
|
40
|
+
code: string;
|
|
41
|
+
}[] = [];
|
|
42
|
+
|
|
43
|
+
const attributes = await datasource.listAttributes();
|
|
44
|
+
for (const attribute of attributes) {
|
|
45
|
+
const parsedAttribute = await datasource.readAttribute(attribute);
|
|
46
|
+
if (!parsedAttribute) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const interfaceName = getInterfaceName(attribute, "Attribute");
|
|
50
|
+
const interfaceCode = generateInterface(parsedAttribute, interfaceName);
|
|
51
|
+
|
|
52
|
+
generatedAttributes.push({
|
|
53
|
+
entityName: attribute,
|
|
54
|
+
interfaceName,
|
|
55
|
+
code: interfaceCode,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const attributesCodePath = path.resolve(outputPath, "attributes.ts");
|
|
60
|
+
let attributesContent = generatedAttributes
|
|
61
|
+
.map(
|
|
62
|
+
({ entityName, code }) => `/**
|
|
63
|
+
* ${entityName}
|
|
64
|
+
*/
|
|
65
|
+
${code}
|
|
66
|
+
`,
|
|
67
|
+
)
|
|
68
|
+
.join("\n");
|
|
69
|
+
|
|
70
|
+
const allAttributesContent = `
|
|
71
|
+
/**
|
|
72
|
+
* Attributes
|
|
73
|
+
*/
|
|
74
|
+
export interface Attributes {
|
|
75
|
+
${generatedAttributes.map(({ entityName, interfaceName }) => ` ${entityName}: ${interfaceName};`).join("\n")}
|
|
76
|
+
}
|
|
77
|
+
`;
|
|
78
|
+
|
|
79
|
+
attributesContent += allAttributesContent;
|
|
80
|
+
|
|
81
|
+
fs.writeFileSync(attributesCodePath, attributesContent);
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Events
|
|
85
|
+
*/
|
|
86
|
+
const generatedEvents: {
|
|
87
|
+
entityName: string;
|
|
88
|
+
interfaceName: string;
|
|
89
|
+
code: string;
|
|
90
|
+
}[] = [];
|
|
91
|
+
|
|
92
|
+
const events = await datasource.listEvents();
|
|
93
|
+
for (const event of events) {
|
|
94
|
+
const parsedEvent = await datasource.readEvent(event);
|
|
95
|
+
if (!parsedEvent) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const interfaceName = getInterfaceName(event, "Event");
|
|
99
|
+
const interfaceCode = generateInterface(parsedEvent, interfaceName);
|
|
100
|
+
|
|
101
|
+
generatedEvents.push({
|
|
102
|
+
entityName: event,
|
|
103
|
+
interfaceName,
|
|
104
|
+
code: interfaceCode,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const eventsCodePath = path.resolve(outputPath, "events.ts");
|
|
109
|
+
let eventsContent = generatedEvents
|
|
110
|
+
.map(
|
|
111
|
+
({ entityName, code }) => `/**
|
|
112
|
+
* ${entityName}
|
|
113
|
+
*/
|
|
114
|
+
${code}
|
|
115
|
+
`,
|
|
116
|
+
)
|
|
117
|
+
.join("\n");
|
|
118
|
+
|
|
119
|
+
const allEventsContent = `
|
|
120
|
+
/**
|
|
121
|
+
* Events
|
|
122
|
+
*/
|
|
123
|
+
export interface Events {
|
|
124
|
+
${generatedEvents.map(({ entityName, interfaceName }) => ` ${entityName}: ${interfaceName};`).join("\n")}
|
|
125
|
+
}
|
|
126
|
+
`;
|
|
127
|
+
|
|
128
|
+
eventsContent += allEventsContent;
|
|
129
|
+
|
|
130
|
+
fs.writeFileSync(eventsCodePath, eventsContent);
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Index
|
|
134
|
+
*/
|
|
135
|
+
let indexContent = `import type { Eventvisor } from "@eventvisor/sdk";
|
|
136
|
+
import type { Value } from "@eventvisor/types";
|
|
137
|
+
|
|
138
|
+
import type { Events } from "./events";
|
|
139
|
+
import type { Attributes } from "./attributes";
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Instance
|
|
143
|
+
*/
|
|
144
|
+
let instance: Eventvisor | null = null;
|
|
145
|
+
|
|
146
|
+
export function setInstance(instance: Eventvisor) {
|
|
147
|
+
instance = instance;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Event
|
|
152
|
+
*/
|
|
153
|
+
type TrackHandler = (eventName: string, payload: Value) => void;
|
|
154
|
+
|
|
155
|
+
let trackHandler: TrackHandler | null = null;
|
|
156
|
+
|
|
157
|
+
export function assignEventHandler(handler: TrackHandler | null) {
|
|
158
|
+
trackHandler = handler;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function track<K extends keyof Events>(eventName: K, payload: Events[K]): void {
|
|
162
|
+
if (instance) {
|
|
163
|
+
instance.track(eventName, payload as unknown as Value);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (trackHandler) {
|
|
167
|
+
trackHandler(eventName, payload as unknown as Value);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Attribute
|
|
173
|
+
*/
|
|
174
|
+
|
|
175
|
+
type SetAttributeHandler = (attributeName: string, value: Value) => void;
|
|
176
|
+
|
|
177
|
+
let setAttributeHandler: SetAttributeHandler | null = null;
|
|
178
|
+
|
|
179
|
+
export function assignAttributeHandler(handler: SetAttributeHandler | null) {
|
|
180
|
+
setAttributeHandler = handler;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function setAttribute<K extends keyof Attributes>(
|
|
184
|
+
attributeName: K,
|
|
185
|
+
value: Attributes[K],
|
|
186
|
+
): void {
|
|
187
|
+
if (instance) {
|
|
188
|
+
instance.setAttribute(attributeName, value as unknown as Value);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (setAttributeHandler) {
|
|
192
|
+
setAttributeHandler(attributeName, value as unknown as Value);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
`;
|
|
196
|
+
|
|
197
|
+
const indexCodePath = path.resolve(outputPath, "index.ts");
|
|
198
|
+
fs.writeFileSync(indexCodePath, indexContent);
|
|
199
|
+
}
|