@polytric/openws-sdkgen 0.0.4 → 0.0.6
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 +2 -2
- package/dist/main.cjs +53 -5
- package/dist/main.js +53 -5
- package/dist/plans/dotnet.cjs +3 -3
- package/dist/plans/dotnet.d.cts +2 -143
- package/dist/plans/dotnet.d.ts +2 -143
- package/dist/plans/dotnet.js +2 -2
- package/dist/plans/typescript.cjs +569 -0
- package/dist/plans/typescript.d.cts +6 -0
- package/dist/plans/typescript.d.ts +6 -0
- package/dist/plans/typescript.js +532 -0
- package/dist/templates/dotnet/HostRole.cs.ejs +23 -8
- package/dist/templates/dotnet/Model.cs.ejs +1 -1
- package/dist/templates/dotnet/RemoteRole.cs.ejs +7 -2
- package/dist/templates/dotnet/UserHostRole.cs.ejs +26 -4
- package/dist/templates/typescript/package.json.ejs +41 -0
- package/dist/templates/typescript/src/core/index.ts.ejs +6 -0
- package/dist/templates/typescript/src/core/models/index.ts.ejs +3 -0
- package/dist/templates/typescript/src/core/models/model.ts.ejs +41 -0
- package/dist/templates/typescript/src/core/network.ts.ejs +517 -0
- package/dist/templates/typescript/src/core/roles/index.ts.ejs +3 -0
- package/dist/templates/typescript/src/core/roles/role.ts.ejs +104 -0
- package/dist/templates/typescript/src/index.ts.ejs +4 -0
- package/dist/templates/typescript/src/sdk/index.ts.ejs +3 -0
- package/dist/templates/typescript/src/sdk/role.ts.ejs +372 -0
- package/dist/templates/typescript/tsconfig.json.ejs +14 -0
- package/dist/templates/typescript/tsup.config.ts.ejs +10 -0
- package/dist/types-BdZPs123.d.cts +115 -0
- package/dist/types-BdZPs123.d.ts +115 -0
- package/package.json +13 -4
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
// src/plans/typescript.ts
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
var TEMPLATE_DIR = path.join(__dirname, "../templates/typescript");
|
|
6
|
+
var SRC_TEMPLATE_DIR = path.join(TEMPLATE_DIR, "src");
|
|
7
|
+
var CORE_TEMPLATE_DIR = path.join(SRC_TEMPLATE_DIR, "core");
|
|
8
|
+
var ROLES_TEMPLATE_DIR = path.join(CORE_TEMPLATE_DIR, "roles");
|
|
9
|
+
var MODELS_TEMPLATE_DIR = path.join(CORE_TEMPLATE_DIR, "models");
|
|
10
|
+
var SDK_TEMPLATE_DIR = path.join(SRC_TEMPLATE_DIR, "sdk");
|
|
11
|
+
function pascalCase(str) {
|
|
12
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
13
|
+
}
|
|
14
|
+
function camelCase(str) {
|
|
15
|
+
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
16
|
+
}
|
|
17
|
+
function kebabCase(str) {
|
|
18
|
+
return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-|-$/g, "").toLowerCase();
|
|
19
|
+
}
|
|
20
|
+
function createPlan(ctx) {
|
|
21
|
+
const { ir, request, spec } = ctx;
|
|
22
|
+
if (!ir) throw new Error("ir is required");
|
|
23
|
+
if (!request) throw new Error("request is required");
|
|
24
|
+
if (!spec) throw new Error("spec is required");
|
|
25
|
+
const language = Object.keys(request.target)[0];
|
|
26
|
+
const isTypeScript = language === "typescript";
|
|
27
|
+
const extension = isTypeScript ? "ts" : "js";
|
|
28
|
+
const packageName = `@${kebabCase(ir.package.project)}/${kebabCase(ir.package.service)}-openws-sdk`;
|
|
29
|
+
const plan = [
|
|
30
|
+
{
|
|
31
|
+
name: `${language} package manifest`,
|
|
32
|
+
command: "render",
|
|
33
|
+
getData: () => ({
|
|
34
|
+
isTypeScript,
|
|
35
|
+
packageName,
|
|
36
|
+
description: ir.package.description,
|
|
37
|
+
version: ir.package.version ?? "0.0.0",
|
|
38
|
+
extension
|
|
39
|
+
}),
|
|
40
|
+
template: path.join(TEMPLATE_DIR, "package.json.ejs"),
|
|
41
|
+
output: path.join(request.outputPath, "package.json")
|
|
42
|
+
}
|
|
43
|
+
];
|
|
44
|
+
if (isTypeScript) {
|
|
45
|
+
plan.push(
|
|
46
|
+
{
|
|
47
|
+
name: `${language} tsconfig`,
|
|
48
|
+
command: "render",
|
|
49
|
+
getData: () => ({}),
|
|
50
|
+
template: path.join(TEMPLATE_DIR, "tsconfig.json.ejs"),
|
|
51
|
+
output: path.join(request.outputPath, "tsconfig.json")
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: `${language} tsup config`,
|
|
55
|
+
command: "render",
|
|
56
|
+
getData: () => ({}),
|
|
57
|
+
template: path.join(TEMPLATE_DIR, "tsup.config.ts.ejs"),
|
|
58
|
+
output: path.join(request.outputPath, "tsup.config.ts")
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
const networkExports = [];
|
|
63
|
+
for (const [networkName, networkSpec] of Object.entries(spec.networks)) {
|
|
64
|
+
const networkFileName = kebabCase(networkName);
|
|
65
|
+
const networkOutputPath = path.join(request.outputPath, "src", networkFileName);
|
|
66
|
+
const sdkOutputPath = path.join(request.outputPath, "src", "sdk");
|
|
67
|
+
const allRoles = Object.values(networkSpec.roles).map(toRoleInfo);
|
|
68
|
+
const rolesByName = new Map(allRoles.map((role) => [role.roleName, role]));
|
|
69
|
+
const hostRoles = allRoles;
|
|
70
|
+
const modelScopes = buildModelScopes(buildSpecModels(networkSpec));
|
|
71
|
+
networkExports.push({
|
|
72
|
+
exportName: camelCase(networkName),
|
|
73
|
+
fileName: networkFileName
|
|
74
|
+
});
|
|
75
|
+
plan.push({
|
|
76
|
+
name: `${language} network ${networkName}`,
|
|
77
|
+
command: "render",
|
|
78
|
+
getData: () => ({
|
|
79
|
+
isTypeScript,
|
|
80
|
+
networkName,
|
|
81
|
+
networkClassName: `${pascalCase(networkName)}Network`,
|
|
82
|
+
description: networkSpec.description,
|
|
83
|
+
version: networkSpec.version,
|
|
84
|
+
hostRoles,
|
|
85
|
+
allRoles,
|
|
86
|
+
extension
|
|
87
|
+
}),
|
|
88
|
+
template: path.join(CORE_TEMPLATE_DIR, "network.ts.ejs"),
|
|
89
|
+
output: path.join(networkOutputPath, `network.${extension}`)
|
|
90
|
+
});
|
|
91
|
+
plan.push({
|
|
92
|
+
name: `${language} network exports ${networkName}`,
|
|
93
|
+
command: "render",
|
|
94
|
+
getData: () => ({
|
|
95
|
+
isTypeScript,
|
|
96
|
+
extension,
|
|
97
|
+
modelScopes
|
|
98
|
+
}),
|
|
99
|
+
template: path.join(CORE_TEMPLATE_DIR, "index.ts.ejs"),
|
|
100
|
+
output: path.join(networkOutputPath, `index.${extension}`)
|
|
101
|
+
});
|
|
102
|
+
const roleMessagesByName = /* @__PURE__ */ new Map();
|
|
103
|
+
for (const role of allRoles) {
|
|
104
|
+
const roleSpec = networkSpec.roles[role.roleName];
|
|
105
|
+
const messages = Object.entries(roleSpec.messages).map(
|
|
106
|
+
([messageName, messageSpec]) => toMessageInfo(messageName, messageSpec, rolesByName)
|
|
107
|
+
);
|
|
108
|
+
roleMessagesByName.set(role.roleName, messages);
|
|
109
|
+
}
|
|
110
|
+
const rolesWithMessages = allRoles.map((role) => ({
|
|
111
|
+
...role,
|
|
112
|
+
messages: roleMessagesByName.get(role.roleName) ?? []
|
|
113
|
+
}));
|
|
114
|
+
for (const role of rolesWithMessages) {
|
|
115
|
+
plan.push({
|
|
116
|
+
name: `${language} core role ${role.className}`,
|
|
117
|
+
command: "render",
|
|
118
|
+
getData: () => ({
|
|
119
|
+
isTypeScript,
|
|
120
|
+
extension,
|
|
121
|
+
peerRoles: allRoles.filter((peerRole) => peerRole.roleName !== role.roleName),
|
|
122
|
+
...role
|
|
123
|
+
}),
|
|
124
|
+
template: path.join(ROLES_TEMPLATE_DIR, "role.ts.ejs"),
|
|
125
|
+
output: path.join(networkOutputPath, "roles", `${role.fileName}.${extension}`)
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
plan.push({
|
|
129
|
+
name: `${language} core role exports ${networkName}`,
|
|
130
|
+
command: "render",
|
|
131
|
+
getData: () => ({
|
|
132
|
+
isTypeScript,
|
|
133
|
+
extension,
|
|
134
|
+
roles: allRoles
|
|
135
|
+
}),
|
|
136
|
+
template: path.join(ROLES_TEMPLATE_DIR, "index.ts.ejs"),
|
|
137
|
+
output: path.join(networkOutputPath, "roles", `index.${extension}`)
|
|
138
|
+
});
|
|
139
|
+
for (const modelScope of modelScopes) {
|
|
140
|
+
plan.push({
|
|
141
|
+
name: `${language} model exports ${modelScope.scopeName}`,
|
|
142
|
+
command: "render",
|
|
143
|
+
getData: () => ({
|
|
144
|
+
isTypeScript,
|
|
145
|
+
extension,
|
|
146
|
+
...modelScope
|
|
147
|
+
}),
|
|
148
|
+
template: path.join(MODELS_TEMPLATE_DIR, "index.ts.ejs"),
|
|
149
|
+
output: path.join(
|
|
150
|
+
networkOutputPath,
|
|
151
|
+
"models",
|
|
152
|
+
modelScope.fileName,
|
|
153
|
+
`index.${extension}`
|
|
154
|
+
)
|
|
155
|
+
});
|
|
156
|
+
for (const model of modelScope.models) {
|
|
157
|
+
plan.push({
|
|
158
|
+
name: `${language} model ${model.className}`,
|
|
159
|
+
command: "render",
|
|
160
|
+
getData: () => ({
|
|
161
|
+
isTypeScript,
|
|
162
|
+
...model
|
|
163
|
+
}),
|
|
164
|
+
template: path.join(MODELS_TEMPLATE_DIR, "model.ts.ejs"),
|
|
165
|
+
output: path.join(
|
|
166
|
+
networkOutputPath,
|
|
167
|
+
"models",
|
|
168
|
+
modelScope.fileName,
|
|
169
|
+
`${model.fileName}.${extension}`
|
|
170
|
+
)
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
for (const hostRole of hostRoles) {
|
|
175
|
+
const remoteRoles = getPeerRoles(networkSpec, rolesByName, hostRole.roleName).map(
|
|
176
|
+
(remoteRole) => ({
|
|
177
|
+
...remoteRole,
|
|
178
|
+
scopedApiName: `${hostRole.className}${remoteRole.className}Api`,
|
|
179
|
+
allowedMethodNames: getAllowedMessageMethodNames(
|
|
180
|
+
networkSpec,
|
|
181
|
+
remoteRole.roleName,
|
|
182
|
+
hostRole.roleName
|
|
183
|
+
)
|
|
184
|
+
})
|
|
185
|
+
);
|
|
186
|
+
const roleSpec = networkSpec.roles[hostRole.roleName];
|
|
187
|
+
const roleHandlers = Object.entries(roleSpec.messages).map(
|
|
188
|
+
([messageName, messageSpec]) => toHandlerInfo(
|
|
189
|
+
messageName,
|
|
190
|
+
messageSpec,
|
|
191
|
+
rolesByName,
|
|
192
|
+
hostRole.roleName,
|
|
193
|
+
allRoles
|
|
194
|
+
)
|
|
195
|
+
);
|
|
196
|
+
plan.push({
|
|
197
|
+
name: `${language} sdk role ${hostRole.className}`,
|
|
198
|
+
command: "render",
|
|
199
|
+
getData: () => ({
|
|
200
|
+
isTypeScript,
|
|
201
|
+
extension,
|
|
202
|
+
handlers: roleHandlers,
|
|
203
|
+
networkName,
|
|
204
|
+
networkDescription: networkSpec.description,
|
|
205
|
+
networkVersion: networkSpec.version,
|
|
206
|
+
remoteRoles,
|
|
207
|
+
...hostRole
|
|
208
|
+
}),
|
|
209
|
+
template: path.join(SDK_TEMPLATE_DIR, "role.ts.ejs"),
|
|
210
|
+
output: path.join(sdkOutputPath, `${hostRole.fileName}.${extension}`)
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
plan.push({
|
|
214
|
+
name: `${language} sdk exports ${networkName}`,
|
|
215
|
+
command: "render",
|
|
216
|
+
getData: () => ({
|
|
217
|
+
isTypeScript,
|
|
218
|
+
extension,
|
|
219
|
+
roles: hostRoles
|
|
220
|
+
}),
|
|
221
|
+
template: path.join(SDK_TEMPLATE_DIR, "index.ts.ejs"),
|
|
222
|
+
output: path.join(sdkOutputPath, `index.${extension}`)
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
plan.push({
|
|
226
|
+
name: `${language} package exports`,
|
|
227
|
+
command: "render",
|
|
228
|
+
getData: () => ({
|
|
229
|
+
isTypeScript,
|
|
230
|
+
extension,
|
|
231
|
+
networkExports
|
|
232
|
+
}),
|
|
233
|
+
template: path.join(SRC_TEMPLATE_DIR, "index.ts.ejs"),
|
|
234
|
+
output: path.join(request.outputPath, "src", `index.${extension}`)
|
|
235
|
+
});
|
|
236
|
+
return {
|
|
237
|
+
...ctx,
|
|
238
|
+
plan
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function buildModelScopes(models) {
|
|
242
|
+
const scopes = /* @__PURE__ */ new Map();
|
|
243
|
+
const objectModels = models.filter((model) => model.type === "object");
|
|
244
|
+
for (const model of objectModels) {
|
|
245
|
+
const scopeName = model.scopeName;
|
|
246
|
+
const scope = scopes.get(scopeName) ?? {
|
|
247
|
+
scopeName,
|
|
248
|
+
className: pascalCase(scopeName),
|
|
249
|
+
varName: camelCase(scopeName),
|
|
250
|
+
fileName: kebabCase(scopeName),
|
|
251
|
+
models: []
|
|
252
|
+
};
|
|
253
|
+
scope.models.push({
|
|
254
|
+
scopeName,
|
|
255
|
+
className: pascalCase(model.modelName),
|
|
256
|
+
fileName: kebabCase(model.modelName),
|
|
257
|
+
schema: buildModelSchema(model, objectModels),
|
|
258
|
+
properties: (model.properties ?? []).map((property) => ({
|
|
259
|
+
name: property.modelName,
|
|
260
|
+
optional: !property.required,
|
|
261
|
+
typeName: mapType(property)
|
|
262
|
+
})),
|
|
263
|
+
imports: buildModelImports(model)
|
|
264
|
+
});
|
|
265
|
+
scopes.set(scopeName, scope);
|
|
266
|
+
}
|
|
267
|
+
return [...scopes.values()];
|
|
268
|
+
}
|
|
269
|
+
function toRoleInfo(role) {
|
|
270
|
+
const className = pascalCase(role.name);
|
|
271
|
+
const fileName = kebabCase(role.name);
|
|
272
|
+
return {
|
|
273
|
+
roleName: role.name,
|
|
274
|
+
className,
|
|
275
|
+
roleClassName: className,
|
|
276
|
+
hostRoleClassName: `${className}Host`,
|
|
277
|
+
apiName: `${className}Api`,
|
|
278
|
+
varName: camelCase(role.name),
|
|
279
|
+
apiVarName: `${camelCase(role.name)}Api`,
|
|
280
|
+
fileName,
|
|
281
|
+
roleFileName: `${fileName}-role`,
|
|
282
|
+
description: role.description || "",
|
|
283
|
+
endpoints: role.endpoints || []
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
function getMessageFromRoles(message, rolesByName, currentRoleName, allRoles) {
|
|
287
|
+
const fromRoleNames = getMessageFromRoleNames(
|
|
288
|
+
message,
|
|
289
|
+
currentRoleName,
|
|
290
|
+
allRoles.map((role) => role.roleName)
|
|
291
|
+
);
|
|
292
|
+
return fromRoleNames.map((roleName) => rolesByName.get(roleName)).filter((role) => Boolean(role));
|
|
293
|
+
}
|
|
294
|
+
function getExplicitMessageFromRoles(message, rolesByName) {
|
|
295
|
+
if (!message.from) return void 0;
|
|
296
|
+
return message.from.map((roleName) => rolesByName.get(roleName)).filter((role) => Boolean(role));
|
|
297
|
+
}
|
|
298
|
+
function getPeerRoles(network, rolesByName, hostRoleName) {
|
|
299
|
+
const peers = /* @__PURE__ */ new Set();
|
|
300
|
+
const hostRole = network.roles[hostRoleName];
|
|
301
|
+
const allRoleNames = Object.keys(network.roles);
|
|
302
|
+
for (const message of Object.values(hostRole.messages)) {
|
|
303
|
+
for (const roleName of getMessageFromRoleNames(message, hostRoleName, allRoleNames)) {
|
|
304
|
+
peers.add(roleName);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
for (const [roleName, role] of Object.entries(network.roles)) {
|
|
308
|
+
if (roleName === hostRoleName) continue;
|
|
309
|
+
for (const message of Object.values(role.messages)) {
|
|
310
|
+
if (getMessageFromRoleNames(message, roleName, allRoleNames).includes(hostRoleName)) {
|
|
311
|
+
peers.add(roleName);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return [...peers].map((roleName) => rolesByName.get(roleName)).filter((role) => Boolean(role));
|
|
316
|
+
}
|
|
317
|
+
function getMessageFromRoleNames(message, targetRoleName, allRoleNames) {
|
|
318
|
+
return message.from ?? allRoleNames.filter((roleName) => roleName !== targetRoleName);
|
|
319
|
+
}
|
|
320
|
+
function getAllowedMessageMethodNames(network, targetRoleName, fromRoleName) {
|
|
321
|
+
const targetRole = network.roles[targetRoleName];
|
|
322
|
+
const allRoleNames = Object.keys(network.roles);
|
|
323
|
+
return Object.entries(targetRole.messages).filter(
|
|
324
|
+
([, message]) => getMessageFromRoleNames(message, targetRoleName, allRoleNames).includes(fromRoleName)
|
|
325
|
+
).map(([messageName]) => camelCase(messageName));
|
|
326
|
+
}
|
|
327
|
+
function buildSpecModels(network) {
|
|
328
|
+
const models = [];
|
|
329
|
+
for (const role of Object.values(network.roles)) {
|
|
330
|
+
for (const [messageName, message] of Object.entries(role.messages)) {
|
|
331
|
+
models.push(...buildIrModels(role.name, `${messageName}Payload`, message.payload));
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return models;
|
|
335
|
+
}
|
|
336
|
+
function buildIrModels(scopeName, modelName, schema) {
|
|
337
|
+
const type = schema.type;
|
|
338
|
+
const model = {
|
|
339
|
+
type,
|
|
340
|
+
scopeName,
|
|
341
|
+
modelName,
|
|
342
|
+
description: schema.description
|
|
343
|
+
};
|
|
344
|
+
switch (type) {
|
|
345
|
+
case "string":
|
|
346
|
+
case "number":
|
|
347
|
+
case "integer":
|
|
348
|
+
case "boolean":
|
|
349
|
+
case "null":
|
|
350
|
+
return [];
|
|
351
|
+
case "array": {
|
|
352
|
+
const items = schema.items;
|
|
353
|
+
if (!items) return [];
|
|
354
|
+
return buildIrModels(scopeName, modelName, items);
|
|
355
|
+
}
|
|
356
|
+
case "object": {
|
|
357
|
+
const properties = [];
|
|
358
|
+
model.properties = properties;
|
|
359
|
+
const models = [];
|
|
360
|
+
const schemaProperties = schema.properties;
|
|
361
|
+
if (!schemaProperties) return [model];
|
|
362
|
+
for (const [subName, subSchema] of Object.entries(schemaProperties)) {
|
|
363
|
+
const subModels = buildIrModels(scopeName, subName, subSchema);
|
|
364
|
+
const mainModel = subModels.find((m) => m.modelName === subName) ?? subSchema;
|
|
365
|
+
const property = {
|
|
366
|
+
type: mainModel.type,
|
|
367
|
+
description: mainModel.description,
|
|
368
|
+
scopeName: mainModel.scopeName ?? scopeName,
|
|
369
|
+
modelName: mainModel.modelName ?? subName,
|
|
370
|
+
required: schema.required?.includes(subName)
|
|
371
|
+
};
|
|
372
|
+
const itemsSource = mainModel.properties?.[0]?.items ?? mainModel.items;
|
|
373
|
+
if (itemsSource) {
|
|
374
|
+
property.items = {
|
|
375
|
+
type: itemsSource.type,
|
|
376
|
+
description: itemsSource.description,
|
|
377
|
+
scopeName: itemsSource?.scopeName ?? scopeName,
|
|
378
|
+
modelName: itemsSource?.modelName ?? subName
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
properties.push(property);
|
|
382
|
+
models.push(...subModels);
|
|
383
|
+
}
|
|
384
|
+
return [model, ...models];
|
|
385
|
+
}
|
|
386
|
+
default:
|
|
387
|
+
return [];
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
function buildModelImports(model) {
|
|
391
|
+
const imports = /* @__PURE__ */ new Map();
|
|
392
|
+
const addImport = (modelName) => {
|
|
393
|
+
if (!modelName || modelName === model.modelName) return;
|
|
394
|
+
const className = pascalCase(modelName);
|
|
395
|
+
imports.set(className, {
|
|
396
|
+
className,
|
|
397
|
+
fileName: kebabCase(modelName)
|
|
398
|
+
});
|
|
399
|
+
};
|
|
400
|
+
for (const property of model.properties ?? []) {
|
|
401
|
+
if (property.type === "object") {
|
|
402
|
+
addImport(property.modelName);
|
|
403
|
+
}
|
|
404
|
+
if (property.type === "array" && property.items?.type === "object") {
|
|
405
|
+
addImport(property.items.modelName);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return [...imports.values()];
|
|
409
|
+
}
|
|
410
|
+
function buildModelSchema(model, models, seen = /* @__PURE__ */ new Set()) {
|
|
411
|
+
const key = `${model.scopeName}:${model.modelName}`;
|
|
412
|
+
if (seen.has(key)) return { type: "object" };
|
|
413
|
+
const nextSeen = new Set(seen);
|
|
414
|
+
nextSeen.add(key);
|
|
415
|
+
const properties = {};
|
|
416
|
+
const required = [];
|
|
417
|
+
for (const property of model.properties ?? []) {
|
|
418
|
+
properties[property.modelName] = buildPropertySchema(property, models, nextSeen);
|
|
419
|
+
if (property.required) required.push(property.modelName);
|
|
420
|
+
}
|
|
421
|
+
const schema = {
|
|
422
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
423
|
+
type: "object",
|
|
424
|
+
properties,
|
|
425
|
+
additionalProperties: false
|
|
426
|
+
};
|
|
427
|
+
if (model.description) schema.description = model.description;
|
|
428
|
+
if (required.length > 0) schema.required = required;
|
|
429
|
+
return schema;
|
|
430
|
+
}
|
|
431
|
+
function buildNestedObjectSchema(model, models, seen) {
|
|
432
|
+
const { $schema: _schema, ...schema } = buildModelSchema(model, models, seen);
|
|
433
|
+
return schema;
|
|
434
|
+
}
|
|
435
|
+
function buildPropertySchema(property, models, seen) {
|
|
436
|
+
const schema = buildSchemaForType(
|
|
437
|
+
property.type,
|
|
438
|
+
property.scopeName,
|
|
439
|
+
property.modelName,
|
|
440
|
+
models,
|
|
441
|
+
seen
|
|
442
|
+
);
|
|
443
|
+
if (property.description) schema.description = property.description;
|
|
444
|
+
if (property.type === "array" && property.items) {
|
|
445
|
+
schema.items = buildSchemaForType(
|
|
446
|
+
property.items.type,
|
|
447
|
+
property.items.scopeName,
|
|
448
|
+
property.items.modelName,
|
|
449
|
+
models,
|
|
450
|
+
seen
|
|
451
|
+
);
|
|
452
|
+
if (property.items.description && typeof schema.items === "object" && schema.items !== null) {
|
|
453
|
+
const itemSchema = schema.items;
|
|
454
|
+
itemSchema.description = property.items.description;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return schema;
|
|
458
|
+
}
|
|
459
|
+
function buildSchemaForType(type, scopeName, modelName, models, seen) {
|
|
460
|
+
switch (type) {
|
|
461
|
+
case "string":
|
|
462
|
+
case "number":
|
|
463
|
+
case "integer":
|
|
464
|
+
case "boolean":
|
|
465
|
+
case "null":
|
|
466
|
+
return { type };
|
|
467
|
+
case "array":
|
|
468
|
+
return { type: "array" };
|
|
469
|
+
case "object": {
|
|
470
|
+
const model = findModel(models, scopeName, modelName);
|
|
471
|
+
if (!model) return { type: "object" };
|
|
472
|
+
return buildNestedObjectSchema(model, models, seen);
|
|
473
|
+
}
|
|
474
|
+
default:
|
|
475
|
+
return {};
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
function findModel(models, scopeName, modelName) {
|
|
479
|
+
return models.find((model) => model.scopeName === scopeName && model.modelName === modelName);
|
|
480
|
+
}
|
|
481
|
+
function toHandlerInfo(messageName, message, rolesByName, _currentRoleName, _allRoles) {
|
|
482
|
+
const payloadType = pascalCase(messageName) + "Payload";
|
|
483
|
+
const methodSuffix = pascalCase(messageName);
|
|
484
|
+
return {
|
|
485
|
+
dispatchMethodName: camelCase(messageName),
|
|
486
|
+
onMethodName: `on${methodSuffix}`,
|
|
487
|
+
listenerFieldName: `${camelCase(messageName)}Handlers`,
|
|
488
|
+
messageName,
|
|
489
|
+
payloadType,
|
|
490
|
+
payloadFileName: kebabCase(payloadType),
|
|
491
|
+
schema: toJsonValue(message.payload),
|
|
492
|
+
fromRoles: getExplicitMessageFromRoles(message, rolesByName),
|
|
493
|
+
bindFromRoles: getMessageFromRoles(message, rolesByName, _currentRoleName, _allRoles)
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
function toMessageInfo(messageName, message, rolesByName) {
|
|
497
|
+
const payloadType = pascalCase(messageName) + "Payload";
|
|
498
|
+
return {
|
|
499
|
+
methodName: camelCase(messageName),
|
|
500
|
+
messageName,
|
|
501
|
+
payloadType,
|
|
502
|
+
payloadFileName: kebabCase(payloadType),
|
|
503
|
+
schema: toJsonValue(message.payload),
|
|
504
|
+
fromRoles: getExplicitMessageFromRoles(message, rolesByName)
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
function toJsonValue(value) {
|
|
508
|
+
return JSON.parse(JSON.stringify(value));
|
|
509
|
+
}
|
|
510
|
+
function mapType(property) {
|
|
511
|
+
switch (property.type) {
|
|
512
|
+
case "string":
|
|
513
|
+
return "string";
|
|
514
|
+
case "number":
|
|
515
|
+
case "integer":
|
|
516
|
+
return "number";
|
|
517
|
+
case "boolean":
|
|
518
|
+
return "boolean";
|
|
519
|
+
case "null":
|
|
520
|
+
return "null";
|
|
521
|
+
case "array":
|
|
522
|
+
if (!property.items) return "unknown[]";
|
|
523
|
+
return `${mapType(property.items)}[]`;
|
|
524
|
+
case "object":
|
|
525
|
+
return pascalCase(property.modelName);
|
|
526
|
+
default:
|
|
527
|
+
return "unknown";
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
export {
|
|
531
|
+
createPlan as default
|
|
532
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
using System;
|
|
1
2
|
using System.Threading.Tasks;
|
|
2
3
|
using Newtonsoft.Json.Linq;
|
|
3
4
|
using Polytric.OpenWs.Core;
|
|
@@ -9,24 +10,24 @@ namespace <%= ctx.namespace %>
|
|
|
9
10
|
{
|
|
10
11
|
public partial class <%= ctx.className %> : <%= ctx.baseClassName %>
|
|
11
12
|
{
|
|
12
|
-
public
|
|
13
|
-
public
|
|
13
|
+
public string Name => "<%= ctx.roleName %>";
|
|
14
|
+
public string Description => "<%= ctx.description ?? '' %>";
|
|
14
15
|
|
|
15
|
-
public override
|
|
16
|
+
public override void HandleOpen(RemoteRole remoteRole)
|
|
16
17
|
{
|
|
17
18
|
<% for (const remoteRole of ctx.remoteRoles) { %>
|
|
18
19
|
if (remoteRole is <%= remoteRole.className %> <%= remoteRole.varName %>)
|
|
19
20
|
{
|
|
20
|
-
|
|
21
|
+
HandleOpen(<%= remoteRole.varName %>);
|
|
21
22
|
}
|
|
22
23
|
<% } -%>
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
<% for (const remoteRole of ctx.remoteRoles) { -%>
|
|
26
|
-
|
|
27
|
+
partial void HandleOpen(<%= remoteRole.className %> <%= remoteRole.varName %>);
|
|
27
28
|
<% } -%>
|
|
28
29
|
|
|
29
|
-
public override
|
|
30
|
+
public override void HandleMessage(string messageName, JToken payload, RemoteRole remoteRole)
|
|
30
31
|
{
|
|
31
32
|
switch (messageName)
|
|
32
33
|
{
|
|
@@ -34,8 +35,15 @@ namespace <%= ctx.namespace %>
|
|
|
34
35
|
case "<%= handler.messageName %>":
|
|
35
36
|
{
|
|
36
37
|
var message = payload.ToObject<<%= handler.modelClassName %>>();
|
|
38
|
+
Handle<%= handler.methodName %>(payload);
|
|
39
|
+
Handle<%= handler.methodName %>(message);
|
|
40
|
+
On<%= handler.methodName %>?.Invoke(message);
|
|
41
|
+
|
|
37
42
|
<% for (const remoteRole of ctx.remoteRoles) { %>
|
|
38
|
-
|
|
43
|
+
Handle<%= handler.methodName %>(payload, remoteRole as <%= remoteRole.className %>);
|
|
44
|
+
HandleMessage(message, remoteRole as <%= remoteRole.className %>);
|
|
45
|
+
On<%= handler.methodName %>From<%= remoteRole.className %>?.Invoke(message, remoteRole as <%= remoteRole.className %>);
|
|
46
|
+
|
|
39
47
|
<% } -%>
|
|
40
48
|
break;
|
|
41
49
|
}
|
|
@@ -44,8 +52,15 @@ namespace <%= ctx.namespace %>
|
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
<% for (const handler of ctx.handlers) { -%>
|
|
55
|
+
public event Action<<%= handler.modelClassName -%>> On<%= handler.methodName -%>;
|
|
56
|
+
partial void Handle<%= handler.methodName %>(JToken payload);
|
|
57
|
+
partial void Handle<%= handler.methodName %>(<%= handler.modelClassName -%> payload);
|
|
58
|
+
|
|
47
59
|
<% for (const remoteRole of ctx.remoteRoles) { -%>
|
|
48
|
-
|
|
60
|
+
public event Action<<%= handler.modelClassName -%>, <%= remoteRole.className -%>> On<%= handler.methodName -%>From<%= remoteRole.className -%>;
|
|
61
|
+
partial void Handle<%= handler.methodName %>(JToken payload, <%= remoteRole.className -%> <%= remoteRole.varName -%>);
|
|
62
|
+
partial void HandleMessage(<%= handler.modelClassName -%> payload, <%= remoteRole.className -%> <%= remoteRole.varName -%>);
|
|
63
|
+
|
|
49
64
|
<% } -%>
|
|
50
65
|
<% } -%>
|
|
51
66
|
}
|
|
@@ -14,7 +14,7 @@ namespace <%= ctx.namespace %>
|
|
|
14
14
|
public override string Name { get; set; } = "<%= ctx.roleName %>";
|
|
15
15
|
public override string Description { get; set; } = "<%= ctx.description ?? '' %>";
|
|
16
16
|
<% if (ctx.endpoints && ctx.endpoints.length > 0) { -%>
|
|
17
|
-
public
|
|
17
|
+
public static IReadOnlyList<Endpoint> Endpoints => new List<Endpoint>
|
|
18
18
|
{
|
|
19
19
|
<% for (const ep of ctx.endpoints) { -%>
|
|
20
20
|
new Endpoint { Scheme = "<%= ep.scheme %>", Host = "<%= ep.host %>", Port = <%= ep.port %>, Path = "<%= ep.path %>" },
|
|
@@ -25,7 +25,12 @@ namespace <%= ctx.namespace %>
|
|
|
25
25
|
<% for (const message of ctx.messages) { -%>
|
|
26
26
|
public async Task <%= message.methodName %>Async(string fromRole, <%= message.modelClassName %> message)
|
|
27
27
|
{
|
|
28
|
-
await
|
|
28
|
+
await InternalSendMessageAsync(fromRole, "<%= message.messageName %>", message).ConfigureAwait(false);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public void <%= message.methodName %>(string fromRole, <%= message.modelClassName %> message)
|
|
32
|
+
{
|
|
33
|
+
InternalQueueMessage(fromRole, "<%= message.messageName %>", message);
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
<% } -%>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
using System.Threading.Tasks;
|
|
2
|
+
using Newtonsoft.Json.Linq;
|
|
2
3
|
using Polytric.OpenWs.Core;
|
|
3
4
|
<% for (const modelImport of ctx.modelImports) { -%>
|
|
4
5
|
using <%= modelImport %>;
|
|
@@ -8,19 +9,40 @@ namespace <%= ctx.namespace %>
|
|
|
8
9
|
public partial class <%= ctx.className %>
|
|
9
10
|
{
|
|
10
11
|
<% for (const remoteRole of ctx.remoteRoles) { -%>
|
|
11
|
-
|
|
12
|
+
partial void HandleOpen(<%= remoteRole.className %> <%= remoteRole.varName %>)
|
|
12
13
|
{
|
|
13
14
|
// TODO: Implement connection handling for <%= remoteRole.className %>
|
|
14
|
-
return Task.CompletedTask;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
<% } -%>
|
|
18
18
|
<% for (const handler of ctx.handlers) { -%>
|
|
19
|
+
partial void Handle<%= handler.methodName %>(JToken payload)
|
|
20
|
+
{
|
|
21
|
+
// TODO: Handle <%= handler.messageName %>
|
|
22
|
+
// Or implement the concrete payload method above. You only need to implement one.
|
|
23
|
+
// Removing this partial method will instruct the compiler to eliminate the call to save performance.
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
partial void Handle<%= handler.methodName %>(<%= handler.modelClassName %> payload)
|
|
27
|
+
{
|
|
28
|
+
// TODO: Handle <%= handler.messageName %>
|
|
29
|
+
// Or implement the JToken method above. You only need to implement one.
|
|
30
|
+
// Removing this partial method will instruct the compiler to eliminate the call to save performance.
|
|
31
|
+
}
|
|
32
|
+
|
|
19
33
|
<% for (const remoteRole of ctx.remoteRoles) { -%>
|
|
20
|
-
|
|
34
|
+
partial void HandleMessage(<%= handler.modelClassName %> payload, <%= remoteRole.className %> <%= remoteRole.varName %>)
|
|
35
|
+
{
|
|
36
|
+
// TODO: Handle <%= handler.messageName %> from <%= remoteRole.className %>
|
|
37
|
+
// Or implement the JToken method below. You only need to implement one.
|
|
38
|
+
// Removing this partial method will instruct the compiler to eliminate the call to save performance.
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
partial void Handle<%= handler.methodName %>(JToken payload, <%= remoteRole.className %> <%= remoteRole.varName %>)
|
|
21
42
|
{
|
|
22
43
|
// TODO: Handle <%= handler.messageName %> from <%= remoteRole.className %>
|
|
23
|
-
|
|
44
|
+
// Or implement the concrete payload method above. You only need to implement one.
|
|
45
|
+
// Removing this partial method will instruct the compiler to eliminate the call to save performance.
|
|
24
46
|
}
|
|
25
47
|
|
|
26
48
|
<% } -%>
|