@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.
Files changed (30) hide show
  1. package/README.md +2 -2
  2. package/dist/main.cjs +53 -5
  3. package/dist/main.js +53 -5
  4. package/dist/plans/dotnet.cjs +3 -3
  5. package/dist/plans/dotnet.d.cts +2 -143
  6. package/dist/plans/dotnet.d.ts +2 -143
  7. package/dist/plans/dotnet.js +2 -2
  8. package/dist/plans/typescript.cjs +569 -0
  9. package/dist/plans/typescript.d.cts +6 -0
  10. package/dist/plans/typescript.d.ts +6 -0
  11. package/dist/plans/typescript.js +532 -0
  12. package/dist/templates/dotnet/HostRole.cs.ejs +23 -8
  13. package/dist/templates/dotnet/Model.cs.ejs +1 -1
  14. package/dist/templates/dotnet/RemoteRole.cs.ejs +7 -2
  15. package/dist/templates/dotnet/UserHostRole.cs.ejs +26 -4
  16. package/dist/templates/typescript/package.json.ejs +41 -0
  17. package/dist/templates/typescript/src/core/index.ts.ejs +6 -0
  18. package/dist/templates/typescript/src/core/models/index.ts.ejs +3 -0
  19. package/dist/templates/typescript/src/core/models/model.ts.ejs +41 -0
  20. package/dist/templates/typescript/src/core/network.ts.ejs +517 -0
  21. package/dist/templates/typescript/src/core/roles/index.ts.ejs +3 -0
  22. package/dist/templates/typescript/src/core/roles/role.ts.ejs +104 -0
  23. package/dist/templates/typescript/src/index.ts.ejs +4 -0
  24. package/dist/templates/typescript/src/sdk/index.ts.ejs +3 -0
  25. package/dist/templates/typescript/src/sdk/role.ts.ejs +372 -0
  26. package/dist/templates/typescript/tsconfig.json.ejs +14 -0
  27. package/dist/templates/typescript/tsup.config.ts.ejs +10 -0
  28. package/dist/types-BdZPs123.d.cts +115 -0
  29. package/dist/types-BdZPs123.d.ts +115 -0
  30. 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 override string Name { get; set; } = "<%= ctx.roleName %>";
13
- public override string Description { get; set; } = "<%= ctx.description ?? '' %>";
13
+ public string Name => "<%= ctx.roleName %>";
14
+ public string Description => "<%= ctx.description ?? '' %>";
14
15
 
15
- public override async Task HandleOpenAsync(RemoteRole remoteRole)
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
- await HandleOpenAsync(<%= remoteRole.varName %>).ConfigureAwait(false);
21
+ HandleOpen(<%= remoteRole.varName %>);
21
22
  }
22
23
  <% } -%>
23
24
  }
24
25
 
25
26
  <% for (const remoteRole of ctx.remoteRoles) { -%>
26
- private partial Task HandleOpenAsync(<%= remoteRole.className %> <%= remoteRole.varName %>);
27
+ partial void HandleOpen(<%= remoteRole.className %> <%= remoteRole.varName %>);
27
28
  <% } -%>
28
29
 
29
- public override async Task HandleMessageAsync(string messageName, JToken payload, RemoteRole remoteRole)
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
- await HandleMessageAsync(message, remoteRole as <%= remoteRole.className %>).ConfigureAwait(false);
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
- private partial Task HandleMessageAsync(<%= handler.modelClassName %> payload, <%= remoteRole.className %> <%= remoteRole.varName %>);
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
  }
@@ -2,7 +2,7 @@ using Newtonsoft.Json;
2
2
 
3
3
  namespace <%= ctx.namespace %>
4
4
  {
5
- public class <%= ctx.className %>
5
+ public partial class <%= ctx.className %>
6
6
  {
7
7
  <% for (const property of ctx.properties) { -%>
8
8
  [JsonProperty("<%= property.modelName %>")]
@@ -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 override IReadOnlyList<Endpoint> Endpoints { get; set; } = new List<Endpoint>
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 SendMessageAsync(fromRole, "<%= message.messageName %>", message).ConfigureAwait(false);
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
- private partial Task HandleOpenAsync(<%= remoteRole.className %> <%= remoteRole.varName %>)
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
- private partial Task HandleMessageAsync(<%= handler.modelClassName %> payload, <%= remoteRole.className %> <%= remoteRole.varName %>)
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
- return Task.CompletedTask;
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
  <% } -%>