@polytric/openws-sdkgen 0.0.7 → 0.0.9

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 CHANGED
@@ -29,7 +29,7 @@ openws-sdkgen \
29
29
  --spec ./openws-spec.json \
30
30
  --out ./generated \
31
31
  --project MyCompany \
32
- --service ChatService \
32
+ --network core \
33
33
  --hostRole server \
34
34
  --language csharp \
35
35
  --environment unity
@@ -40,7 +40,7 @@ The CLI exposes the following options:
40
40
  - `--spec` (string): Path to the OpenWS spec JSON file.
41
41
  - `--out` (string): Output directory for generated code.
42
42
  - `--project` (string): Project/namespace prefix for generated code.
43
- - `--service` (string): Service name (default: `MyService`).
43
+ - `--network` (string): Network name to generate.
44
44
  - `--hostRole` (string): Participant role name that represents the host side.
45
45
  - `--language` (string): Target language (`csharp`, `javascript`, or `typescript`).
46
46
  - `--environment` (string|array): Target environment (`unity`, `node`, `browser`).
package/dist/main.cjs CHANGED
@@ -148,7 +148,11 @@ function buildIr(ctx) {
148
148
  const { request, spec } = ctx;
149
149
  if (!request) throw new Error("request is required");
150
150
  if (!spec) throw new Error("spec is required");
151
- const { hostRoles } = request;
151
+ const { hostRoles, network } = request;
152
+ const selectedNetworkSpec = spec.networks[network];
153
+ if (!selectedNetworkSpec) {
154
+ throw new Error(`Network "${network}" does not exist in the spec`);
155
+ }
152
156
  const ir = {
153
157
  package: {
154
158
  project: request.project,
@@ -158,8 +162,16 @@ function buildIr(ctx) {
158
162
  },
159
163
  networks: []
160
164
  };
161
- for (const [networkName, networkSpec] of Object.entries(spec.networks)) {
162
- const hostRoleSpecs = hostRoles.map((hostRole) => networkSpec.roles[hostRole]);
165
+ for (const [networkName, networkSpec] of [[network, selectedNetworkSpec]]) {
166
+ const hostRoleSpecs = hostRoles.map((hostRole) => {
167
+ const hostRoleSpec = networkSpec.roles[hostRole];
168
+ if (!hostRoleSpec) {
169
+ throw new Error(
170
+ `Host role "${hostRole}" does not exist in network "${networkName}"`
171
+ );
172
+ }
173
+ return hostRoleSpec;
174
+ });
163
175
  const otherRoleSpecs = {};
164
176
  for (const [roleName, roleSpec] of Object.entries(networkSpec.roles)) {
165
177
  if (!hostRoles.includes(roleName)) {
@@ -285,6 +297,7 @@ var validateBuildRequest = import_schema2.default.obj({
285
297
  specPath: import_schema2.default.str,
286
298
  outputPath: import_schema2.default.str,
287
299
  project: import_schema2.default.str,
300
+ network: import_schema2.default.str,
288
301
  hostRoles: import_schema2.default.arr(import_schema2.default.str),
289
302
  target: import_schema2.default.obj({
290
303
  csharp: import_schema2.default.obj({
@@ -308,6 +321,7 @@ function buildRequest(ctx) {
308
321
  specPath: import_node_path.default.join(import_node_process.default.cwd(), rawInput.spec),
309
322
  outputPath: import_node_path.default.join(import_node_process.default.cwd(), rawInput.out),
310
323
  project: rawInput.project,
324
+ network: toCamelCase(rawInput.network),
311
325
  hostRoles: rawInput.hostRole.map((r) => toCamelCase(r)),
312
326
  target: {
313
327
  [rawInput.language]: {
@@ -327,7 +341,22 @@ function buildRequest(ctx) {
327
341
  var import_node_fs = __toESM(require("fs"), 1);
328
342
  var import_node_path2 = __toESM(require("path"), 1);
329
343
  var import_ejs = __toESM(require("ejs"), 1);
344
+ var prettier = __toESM(require("prettier"), 1);
330
345
  var rendererCache = {};
346
+ var parserByExtension = {
347
+ ".js": "babel",
348
+ ".json": "json",
349
+ ".ts": "typescript"
350
+ };
351
+ var fallbackFormatOptions = {
352
+ semi: false,
353
+ singleQuote: true,
354
+ tabWidth: 4,
355
+ trailingComma: "es5",
356
+ printWidth: 100,
357
+ arrowParens: "avoid",
358
+ endOfLine: "lf"
359
+ };
331
360
  function renderTemplate(templatePath, data) {
332
361
  if (rendererCache[templatePath]) {
333
362
  return rendererCache[templatePath](data);
@@ -337,7 +366,17 @@ function renderTemplate(templatePath, data) {
337
366
  rendererCache[templatePath] = renderer;
338
367
  return renderer(data);
339
368
  }
340
- function executePlan(ctx) {
369
+ async function formatRenderedOutput(output, content) {
370
+ const parser = parserByExtension[import_node_path2.default.extname(output)];
371
+ if (!parser) return content;
372
+ const config = await prettier.resolveConfig(output, { editorconfig: true });
373
+ return prettier.format(content, {
374
+ ...fallbackFormatOptions,
375
+ ...config,
376
+ parser
377
+ });
378
+ }
379
+ async function executePlan(ctx) {
341
380
  const { plan } = ctx;
342
381
  if (!plan) throw new Error("plan is required");
343
382
  for (const step of plan) {
@@ -351,9 +390,9 @@ function executePlan(ctx) {
351
390
  const { getData, template, output } = step;
352
391
  if (!getData || !template) continue;
353
392
  const data = getData();
354
- console.log(data);
355
393
  import_node_fs.default.mkdirSync(import_node_path2.default.dirname(output), { recursive: true });
356
- import_node_fs.default.writeFileSync(output, renderTemplate(template, { ctx: data }));
394
+ const rendered = renderTemplate(template, { ctx: data });
395
+ import_node_fs.default.writeFileSync(output, await formatRenderedOutput(output, rendered));
357
396
  break;
358
397
  }
359
398
  }
@@ -411,6 +450,10 @@ function parseInput(ctx) {
411
450
  type: "string",
412
451
  description: "The project name",
413
452
  demandOption: true
453
+ }).option("network", {
454
+ type: "string",
455
+ description: "The network to be generated",
456
+ demandOption: true
414
457
  }).option("hostRole", {
415
458
  type: "array",
416
459
  string: true,
@@ -440,21 +483,45 @@ function parseInput(ctx) {
440
483
 
441
484
  // src/prepare-output.ts
442
485
  var import_node_fs3 = __toESM(require("fs"), 1);
486
+ var import_node_path3 = __toESM(require("path"), 1);
487
+ function pascalCase(str) {
488
+ return str.charAt(0).toUpperCase() + str.slice(1);
489
+ }
490
+ function kebabCase(str) {
491
+ return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-|-$/g, "").toLowerCase();
492
+ }
443
493
  function prepareOutput(ctx) {
444
- const { request } = ctx;
494
+ const { request, spec } = ctx;
445
495
  if (!request) throw new Error("request is required");
446
- const { outputPath } = request;
447
- import_node_fs3.default.rmSync(outputPath, { recursive: true, force: true });
448
- import_node_fs3.default.mkdirSync(outputPath, { recursive: true });
496
+ if (!spec) throw new Error("spec is required");
497
+ if (!spec.networks[request.network]) {
498
+ throw new Error(`Network "${request.network}" does not exist in the spec`);
499
+ }
500
+ const cleanupPaths = getNetworkOutputPaths(ctx);
501
+ for (const cleanupPath of cleanupPaths) {
502
+ import_node_fs3.default.rmSync(cleanupPath, { recursive: true, force: true });
503
+ }
504
+ import_node_fs3.default.mkdirSync(request.outputPath, { recursive: true });
449
505
  return ctx;
450
506
  }
507
+ function getNetworkOutputPaths(ctx) {
508
+ const { request, spec } = ctx;
509
+ if (!request) throw new Error("request is required");
510
+ if (!spec) throw new Error("spec is required");
511
+ if (request.target.csharp) {
512
+ const assemblyName = `${pascalCase(request.project)}.${pascalCase(spec.name)}.Sdk`;
513
+ const networkFolder = pascalCase(request.network);
514
+ return [import_node_path3.default.join(request.outputPath, assemblyName, networkFolder)];
515
+ }
516
+ return [import_node_path3.default.join(request.outputPath, kebabCase(spec.name), kebabCase(request.network))];
517
+ }
451
518
 
452
519
  // src/main.ts
453
520
  var Pipeline = [
454
521
  parseInput,
455
522
  buildRequest,
456
- prepareOutput,
457
523
  loadSpec,
524
+ prepareOutput,
458
525
  buildIr,
459
526
  dispatchBuildPlan,
460
527
  executePlan
@@ -466,9 +533,7 @@ async function main() {
466
533
  }
467
534
  return ctx;
468
535
  }
469
- main().then((result) => {
470
- console.log(JSON.stringify(result, null, 2));
471
- }).catch((err) => {
536
+ main().catch((err) => {
472
537
  console.error(err);
473
538
  process.exit(1);
474
539
  });
package/dist/main.js CHANGED
@@ -125,7 +125,11 @@ function buildIr(ctx) {
125
125
  const { request, spec } = ctx;
126
126
  if (!request) throw new Error("request is required");
127
127
  if (!spec) throw new Error("spec is required");
128
- const { hostRoles } = request;
128
+ const { hostRoles, network } = request;
129
+ const selectedNetworkSpec = spec.networks[network];
130
+ if (!selectedNetworkSpec) {
131
+ throw new Error(`Network "${network}" does not exist in the spec`);
132
+ }
129
133
  const ir = {
130
134
  package: {
131
135
  project: request.project,
@@ -135,8 +139,16 @@ function buildIr(ctx) {
135
139
  },
136
140
  networks: []
137
141
  };
138
- for (const [networkName, networkSpec] of Object.entries(spec.networks)) {
139
- const hostRoleSpecs = hostRoles.map((hostRole) => networkSpec.roles[hostRole]);
142
+ for (const [networkName, networkSpec] of [[network, selectedNetworkSpec]]) {
143
+ const hostRoleSpecs = hostRoles.map((hostRole) => {
144
+ const hostRoleSpec = networkSpec.roles[hostRole];
145
+ if (!hostRoleSpec) {
146
+ throw new Error(
147
+ `Host role "${hostRole}" does not exist in network "${networkName}"`
148
+ );
149
+ }
150
+ return hostRoleSpec;
151
+ });
140
152
  const otherRoleSpecs = {};
141
153
  for (const [roleName, roleSpec] of Object.entries(networkSpec.roles)) {
142
154
  if (!hostRoles.includes(roleName)) {
@@ -262,6 +274,7 @@ var validateBuildRequest = S2.obj({
262
274
  specPath: S2.str,
263
275
  outputPath: S2.str,
264
276
  project: S2.str,
277
+ network: S2.str,
265
278
  hostRoles: S2.arr(S2.str),
266
279
  target: S2.obj({
267
280
  csharp: S2.obj({
@@ -285,6 +298,7 @@ function buildRequest(ctx) {
285
298
  specPath: path.join(process2.cwd(), rawInput.spec),
286
299
  outputPath: path.join(process2.cwd(), rawInput.out),
287
300
  project: rawInput.project,
301
+ network: toCamelCase(rawInput.network),
288
302
  hostRoles: rawInput.hostRole.map((r) => toCamelCase(r)),
289
303
  target: {
290
304
  [rawInput.language]: {
@@ -304,7 +318,22 @@ function buildRequest(ctx) {
304
318
  import fs from "fs";
305
319
  import path2 from "path";
306
320
  import ejs from "ejs";
321
+ import * as prettier from "prettier";
307
322
  var rendererCache = {};
323
+ var parserByExtension = {
324
+ ".js": "babel",
325
+ ".json": "json",
326
+ ".ts": "typescript"
327
+ };
328
+ var fallbackFormatOptions = {
329
+ semi: false,
330
+ singleQuote: true,
331
+ tabWidth: 4,
332
+ trailingComma: "es5",
333
+ printWidth: 100,
334
+ arrowParens: "avoid",
335
+ endOfLine: "lf"
336
+ };
308
337
  function renderTemplate(templatePath, data) {
309
338
  if (rendererCache[templatePath]) {
310
339
  return rendererCache[templatePath](data);
@@ -314,7 +343,17 @@ function renderTemplate(templatePath, data) {
314
343
  rendererCache[templatePath] = renderer;
315
344
  return renderer(data);
316
345
  }
317
- function executePlan(ctx) {
346
+ async function formatRenderedOutput(output, content) {
347
+ const parser = parserByExtension[path2.extname(output)];
348
+ if (!parser) return content;
349
+ const config = await prettier.resolveConfig(output, { editorconfig: true });
350
+ return prettier.format(content, {
351
+ ...fallbackFormatOptions,
352
+ ...config,
353
+ parser
354
+ });
355
+ }
356
+ async function executePlan(ctx) {
318
357
  const { plan } = ctx;
319
358
  if (!plan) throw new Error("plan is required");
320
359
  for (const step of plan) {
@@ -328,9 +367,9 @@ function executePlan(ctx) {
328
367
  const { getData, template, output } = step;
329
368
  if (!getData || !template) continue;
330
369
  const data = getData();
331
- console.log(data);
332
370
  fs.mkdirSync(path2.dirname(output), { recursive: true });
333
- fs.writeFileSync(output, renderTemplate(template, { ctx: data }));
371
+ const rendered = renderTemplate(template, { ctx: data });
372
+ fs.writeFileSync(output, await formatRenderedOutput(output, rendered));
334
373
  break;
335
374
  }
336
375
  }
@@ -388,6 +427,10 @@ function parseInput(ctx) {
388
427
  type: "string",
389
428
  description: "The project name",
390
429
  demandOption: true
430
+ }).option("network", {
431
+ type: "string",
432
+ description: "The network to be generated",
433
+ demandOption: true
391
434
  }).option("hostRole", {
392
435
  type: "array",
393
436
  string: true,
@@ -417,21 +460,45 @@ function parseInput(ctx) {
417
460
 
418
461
  // src/prepare-output.ts
419
462
  import fs3 from "fs";
463
+ import path3 from "path";
464
+ function pascalCase(str) {
465
+ return str.charAt(0).toUpperCase() + str.slice(1);
466
+ }
467
+ function kebabCase(str) {
468
+ return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-|-$/g, "").toLowerCase();
469
+ }
420
470
  function prepareOutput(ctx) {
421
- const { request } = ctx;
471
+ const { request, spec } = ctx;
422
472
  if (!request) throw new Error("request is required");
423
- const { outputPath } = request;
424
- fs3.rmSync(outputPath, { recursive: true, force: true });
425
- fs3.mkdirSync(outputPath, { recursive: true });
473
+ if (!spec) throw new Error("spec is required");
474
+ if (!spec.networks[request.network]) {
475
+ throw new Error(`Network "${request.network}" does not exist in the spec`);
476
+ }
477
+ const cleanupPaths = getNetworkOutputPaths(ctx);
478
+ for (const cleanupPath of cleanupPaths) {
479
+ fs3.rmSync(cleanupPath, { recursive: true, force: true });
480
+ }
481
+ fs3.mkdirSync(request.outputPath, { recursive: true });
426
482
  return ctx;
427
483
  }
484
+ function getNetworkOutputPaths(ctx) {
485
+ const { request, spec } = ctx;
486
+ if (!request) throw new Error("request is required");
487
+ if (!spec) throw new Error("spec is required");
488
+ if (request.target.csharp) {
489
+ const assemblyName = `${pascalCase(request.project)}.${pascalCase(spec.name)}.Sdk`;
490
+ const networkFolder = pascalCase(request.network);
491
+ return [path3.join(request.outputPath, assemblyName, networkFolder)];
492
+ }
493
+ return [path3.join(request.outputPath, kebabCase(spec.name), kebabCase(request.network))];
494
+ }
428
495
 
429
496
  // src/main.ts
430
497
  var Pipeline = [
431
498
  parseInput,
432
499
  buildRequest,
433
- prepareOutput,
434
500
  loadSpec,
501
+ prepareOutput,
435
502
  buildIr,
436
503
  dispatchBuildPlan,
437
504
  executePlan
@@ -443,9 +510,7 @@ async function main() {
443
510
  }
444
511
  return ctx;
445
512
  }
446
- main().then((result) => {
447
- console.log(JSON.stringify(result, null, 2));
448
- }).catch((err) => {
513
+ main().catch((err) => {
449
514
  console.error(err);
450
515
  process.exit(1);
451
516
  });
@@ -1,4 +1,4 @@
1
- import { P as PipelineContext } from '../types-BdZPs123.cjs';
1
+ import { P as PipelineContext } from '../types-Bn36WAIG.cjs';
2
2
  import '@polytric/openws-spec/types';
3
3
 
4
4
  declare function createPlan(ctx: PipelineContext): PipelineContext;
@@ -1,4 +1,4 @@
1
- import { P as PipelineContext } from '../types-BdZPs123.js';
1
+ import { P as PipelineContext } from '../types-Bn36WAIG.js';
2
2
  import '@polytric/openws-spec/types';
3
3
 
4
4
  declare function createPlan(ctx: PipelineContext): PipelineContext;
@@ -44,9 +44,8 @@ var import_node_url = require("url");
44
44
  var __dirname = import_node_path.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
45
45
  var TEMPLATE_DIR = import_node_path.default.join(__dirname, "../templates/typescript");
46
46
  var SRC_TEMPLATE_DIR = import_node_path.default.join(TEMPLATE_DIR, "src");
47
- var CORE_TEMPLATE_DIR = import_node_path.default.join(SRC_TEMPLATE_DIR, "core");
48
- var ROLES_TEMPLATE_DIR = import_node_path.default.join(CORE_TEMPLATE_DIR, "roles");
49
- var MODELS_TEMPLATE_DIR = import_node_path.default.join(CORE_TEMPLATE_DIR, "models");
47
+ var ROLES_TEMPLATE_DIR = import_node_path.default.join(SRC_TEMPLATE_DIR, "roles");
48
+ var MODELS_TEMPLATE_DIR = import_node_path.default.join(SRC_TEMPLATE_DIR, "models");
50
49
  var SDK_TEMPLATE_DIR = import_node_path.default.join(SRC_TEMPLATE_DIR, "sdk");
51
50
  function pascalCase(str) {
52
51
  return str.charAt(0).toUpperCase() + str.slice(1);
@@ -65,7 +64,10 @@ function createPlan(ctx) {
65
64
  const language = Object.keys(request.target)[0];
66
65
  const isTypeScript = language === "typescript";
67
66
  const extension = isTypeScript ? "ts" : "js";
68
- const packageName = `@${kebabCase(ir.package.project)}/${kebabCase(ir.package.service)}-openws-sdk`;
67
+ const serviceFileName = kebabCase(ir.package.service);
68
+ const networkFileName = kebabCase(request.network);
69
+ const packageOutputPath = import_node_path.default.join(request.outputPath, serviceFileName, networkFileName);
70
+ const packageName = `@${kebabCase(ir.package.project)}/${serviceFileName}-${networkFileName}-openws-sdk`;
69
71
  const plan = [
70
72
  {
71
73
  name: `${language} package manifest`,
@@ -74,11 +76,11 @@ function createPlan(ctx) {
74
76
  isTypeScript,
75
77
  packageName,
76
78
  description: ir.package.description,
77
- version: ir.package.version ?? "0.0.0",
79
+ version: ir.package.version ?? "0.0.1",
78
80
  extension
79
81
  }),
80
82
  template: import_node_path.default.join(TEMPLATE_DIR, "package.json.ejs"),
81
- output: import_node_path.default.join(request.outputPath, "package.json")
83
+ output: import_node_path.default.join(packageOutputPath, "package.json")
82
84
  }
83
85
  ];
84
86
  if (isTypeScript) {
@@ -88,30 +90,36 @@ function createPlan(ctx) {
88
90
  command: "render",
89
91
  getData: () => ({}),
90
92
  template: import_node_path.default.join(TEMPLATE_DIR, "tsconfig.json.ejs"),
91
- output: import_node_path.default.join(request.outputPath, "tsconfig.json")
93
+ output: import_node_path.default.join(packageOutputPath, "tsconfig.json")
92
94
  },
93
95
  {
94
96
  name: `${language} tsup config`,
95
97
  command: "render",
96
98
  getData: () => ({}),
97
99
  template: import_node_path.default.join(TEMPLATE_DIR, "tsup.config.ts.ejs"),
98
- output: import_node_path.default.join(request.outputPath, "tsup.config.ts")
100
+ output: import_node_path.default.join(packageOutputPath, "tsup.config.ts")
99
101
  }
100
102
  );
101
103
  }
102
- const networkExports = [];
103
- for (const [networkName, networkSpec] of Object.entries(spec.networks)) {
104
- const networkFileName = kebabCase(networkName);
105
- const networkOutputPath = import_node_path.default.join(request.outputPath, "src", networkFileName);
106
- const sdkOutputPath = import_node_path.default.join(request.outputPath, "src", "sdk");
104
+ const selectedNetworkSpec = spec.networks[request.network];
105
+ if (!selectedNetworkSpec) {
106
+ throw new Error(`Network "${request.network}" does not exist in the spec`);
107
+ }
108
+ for (const [networkName, networkSpec] of [[request.network, selectedNetworkSpec]]) {
109
+ const sourceOutputPath = import_node_path.default.join(packageOutputPath, "src");
110
+ const sdkOutputPath = import_node_path.default.join(sourceOutputPath, "sdk");
107
111
  const allRoles = Object.values(networkSpec.roles).map(toRoleInfo);
108
112
  const rolesByName = new Map(allRoles.map((role) => [role.roleName, role]));
109
- const hostRoles = allRoles;
110
- const modelScopes = buildModelScopes(buildSpecModels(networkSpec));
111
- networkExports.push({
112
- exportName: camelCase(networkName),
113
- fileName: networkFileName
113
+ const hostRoles = request.hostRoles.map((hostRole) => {
114
+ const role = rolesByName.get(hostRole);
115
+ if (!role) {
116
+ throw new Error(
117
+ `Host role "${hostRole}" does not exist in network "${networkName}"`
118
+ );
119
+ }
120
+ return role;
114
121
  });
122
+ const modelScopes = buildModelScopes(buildSpecModels(networkSpec));
115
123
  plan.push({
116
124
  name: `${language} network ${networkName}`,
117
125
  command: "render",
@@ -125,19 +133,8 @@ function createPlan(ctx) {
125
133
  allRoles,
126
134
  extension
127
135
  }),
128
- template: import_node_path.default.join(CORE_TEMPLATE_DIR, "network.ts.ejs"),
129
- output: import_node_path.default.join(networkOutputPath, `network.${extension}`)
130
- });
131
- plan.push({
132
- name: `${language} network exports ${networkName}`,
133
- command: "render",
134
- getData: () => ({
135
- isTypeScript,
136
- extension,
137
- modelScopes
138
- }),
139
- template: import_node_path.default.join(CORE_TEMPLATE_DIR, "index.ts.ejs"),
140
- output: import_node_path.default.join(networkOutputPath, `index.${extension}`)
136
+ template: import_node_path.default.join(SRC_TEMPLATE_DIR, "network.ts.ejs"),
137
+ output: import_node_path.default.join(sourceOutputPath, `network.${extension}`)
141
138
  });
142
139
  const roleMessagesByName = /* @__PURE__ */ new Map();
143
140
  for (const role of allRoles) {
@@ -162,7 +159,7 @@ function createPlan(ctx) {
162
159
  ...role
163
160
  }),
164
161
  template: import_node_path.default.join(ROLES_TEMPLATE_DIR, "role.ts.ejs"),
165
- output: import_node_path.default.join(networkOutputPath, "roles", `${role.fileName}.${extension}`)
162
+ output: import_node_path.default.join(sourceOutputPath, "roles", `${role.fileName}.${extension}`)
166
163
  });
167
164
  }
168
165
  plan.push({
@@ -174,7 +171,7 @@ function createPlan(ctx) {
174
171
  roles: allRoles
175
172
  }),
176
173
  template: import_node_path.default.join(ROLES_TEMPLATE_DIR, "index.ts.ejs"),
177
- output: import_node_path.default.join(networkOutputPath, "roles", `index.${extension}`)
174
+ output: import_node_path.default.join(sourceOutputPath, "roles", `index.${extension}`)
178
175
  });
179
176
  for (const modelScope of modelScopes) {
180
177
  plan.push({
@@ -187,7 +184,7 @@ function createPlan(ctx) {
187
184
  }),
188
185
  template: import_node_path.default.join(MODELS_TEMPLATE_DIR, "index.ts.ejs"),
189
186
  output: import_node_path.default.join(
190
- networkOutputPath,
187
+ sourceOutputPath,
191
188
  "models",
192
189
  modelScope.fileName,
193
190
  `index.${extension}`
@@ -203,7 +200,7 @@ function createPlan(ctx) {
203
200
  }),
204
201
  template: import_node_path.default.join(MODELS_TEMPLATE_DIR, "model.ts.ejs"),
205
202
  output: import_node_path.default.join(
206
- networkOutputPath,
203
+ sourceOutputPath,
207
204
  "models",
208
205
  modelScope.fileName,
209
206
  `${model.fileName}.${extension}`
@@ -241,7 +238,6 @@ function createPlan(ctx) {
241
238
  extension,
242
239
  handlers: roleHandlers,
243
240
  networkName,
244
- networkFileName,
245
241
  networkDescription: networkSpec.description,
246
242
  networkVersion: networkSpec.version,
247
243
  remoteRoles,
@@ -262,18 +258,18 @@ function createPlan(ctx) {
262
258
  template: import_node_path.default.join(SDK_TEMPLATE_DIR, "index.ts.ejs"),
263
259
  output: import_node_path.default.join(sdkOutputPath, `index.${extension}`)
264
260
  });
261
+ plan.push({
262
+ name: `${language} package exports`,
263
+ command: "render",
264
+ getData: () => ({
265
+ isTypeScript,
266
+ extension,
267
+ modelScopes
268
+ }),
269
+ template: import_node_path.default.join(SRC_TEMPLATE_DIR, "index.ts.ejs"),
270
+ output: import_node_path.default.join(sourceOutputPath, `index.${extension}`)
271
+ });
265
272
  }
266
- plan.push({
267
- name: `${language} package exports`,
268
- command: "render",
269
- getData: () => ({
270
- isTypeScript,
271
- extension,
272
- networkExports
273
- }),
274
- template: import_node_path.default.join(SRC_TEMPLATE_DIR, "index.ts.ejs"),
275
- output: import_node_path.default.join(request.outputPath, "src", `index.${extension}`)
276
- });
277
273
  return {
278
274
  ...ctx,
279
275
  plan
@@ -1,4 +1,4 @@
1
- import { P as PipelineContext } from '../types-BdZPs123.cjs';
1
+ import { P as PipelineContext } from '../types-Bn36WAIG.cjs';
2
2
  import '@polytric/openws-spec/types';
3
3
 
4
4
  declare function createPlan(ctx: PipelineContext): PipelineContext;
@@ -1,4 +1,4 @@
1
- import { P as PipelineContext } from '../types-BdZPs123.js';
1
+ import { P as PipelineContext } from '../types-Bn36WAIG.js';
2
2
  import '@polytric/openws-spec/types';
3
3
 
4
4
  declare function createPlan(ctx: PipelineContext): PipelineContext;
@@ -4,9 +4,8 @@ import { fileURLToPath } from "url";
4
4
  var __dirname = path.dirname(fileURLToPath(import.meta.url));
5
5
  var TEMPLATE_DIR = path.join(__dirname, "../templates/typescript");
6
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");
7
+ var ROLES_TEMPLATE_DIR = path.join(SRC_TEMPLATE_DIR, "roles");
8
+ var MODELS_TEMPLATE_DIR = path.join(SRC_TEMPLATE_DIR, "models");
10
9
  var SDK_TEMPLATE_DIR = path.join(SRC_TEMPLATE_DIR, "sdk");
11
10
  function pascalCase(str) {
12
11
  return str.charAt(0).toUpperCase() + str.slice(1);
@@ -25,7 +24,10 @@ function createPlan(ctx) {
25
24
  const language = Object.keys(request.target)[0];
26
25
  const isTypeScript = language === "typescript";
27
26
  const extension = isTypeScript ? "ts" : "js";
28
- const packageName = `@${kebabCase(ir.package.project)}/${kebabCase(ir.package.service)}-openws-sdk`;
27
+ const serviceFileName = kebabCase(ir.package.service);
28
+ const networkFileName = kebabCase(request.network);
29
+ const packageOutputPath = path.join(request.outputPath, serviceFileName, networkFileName);
30
+ const packageName = `@${kebabCase(ir.package.project)}/${serviceFileName}-${networkFileName}-openws-sdk`;
29
31
  const plan = [
30
32
  {
31
33
  name: `${language} package manifest`,
@@ -34,11 +36,11 @@ function createPlan(ctx) {
34
36
  isTypeScript,
35
37
  packageName,
36
38
  description: ir.package.description,
37
- version: ir.package.version ?? "0.0.0",
39
+ version: ir.package.version ?? "0.0.1",
38
40
  extension
39
41
  }),
40
42
  template: path.join(TEMPLATE_DIR, "package.json.ejs"),
41
- output: path.join(request.outputPath, "package.json")
43
+ output: path.join(packageOutputPath, "package.json")
42
44
  }
43
45
  ];
44
46
  if (isTypeScript) {
@@ -48,30 +50,36 @@ function createPlan(ctx) {
48
50
  command: "render",
49
51
  getData: () => ({}),
50
52
  template: path.join(TEMPLATE_DIR, "tsconfig.json.ejs"),
51
- output: path.join(request.outputPath, "tsconfig.json")
53
+ output: path.join(packageOutputPath, "tsconfig.json")
52
54
  },
53
55
  {
54
56
  name: `${language} tsup config`,
55
57
  command: "render",
56
58
  getData: () => ({}),
57
59
  template: path.join(TEMPLATE_DIR, "tsup.config.ts.ejs"),
58
- output: path.join(request.outputPath, "tsup.config.ts")
60
+ output: path.join(packageOutputPath, "tsup.config.ts")
59
61
  }
60
62
  );
61
63
  }
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");
64
+ const selectedNetworkSpec = spec.networks[request.network];
65
+ if (!selectedNetworkSpec) {
66
+ throw new Error(`Network "${request.network}" does not exist in the spec`);
67
+ }
68
+ for (const [networkName, networkSpec] of [[request.network, selectedNetworkSpec]]) {
69
+ const sourceOutputPath = path.join(packageOutputPath, "src");
70
+ const sdkOutputPath = path.join(sourceOutputPath, "sdk");
67
71
  const allRoles = Object.values(networkSpec.roles).map(toRoleInfo);
68
72
  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
73
+ const hostRoles = request.hostRoles.map((hostRole) => {
74
+ const role = rolesByName.get(hostRole);
75
+ if (!role) {
76
+ throw new Error(
77
+ `Host role "${hostRole}" does not exist in network "${networkName}"`
78
+ );
79
+ }
80
+ return role;
74
81
  });
82
+ const modelScopes = buildModelScopes(buildSpecModels(networkSpec));
75
83
  plan.push({
76
84
  name: `${language} network ${networkName}`,
77
85
  command: "render",
@@ -85,19 +93,8 @@ function createPlan(ctx) {
85
93
  allRoles,
86
94
  extension
87
95
  }),
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}`)
96
+ template: path.join(SRC_TEMPLATE_DIR, "network.ts.ejs"),
97
+ output: path.join(sourceOutputPath, `network.${extension}`)
101
98
  });
102
99
  const roleMessagesByName = /* @__PURE__ */ new Map();
103
100
  for (const role of allRoles) {
@@ -122,7 +119,7 @@ function createPlan(ctx) {
122
119
  ...role
123
120
  }),
124
121
  template: path.join(ROLES_TEMPLATE_DIR, "role.ts.ejs"),
125
- output: path.join(networkOutputPath, "roles", `${role.fileName}.${extension}`)
122
+ output: path.join(sourceOutputPath, "roles", `${role.fileName}.${extension}`)
126
123
  });
127
124
  }
128
125
  plan.push({
@@ -134,7 +131,7 @@ function createPlan(ctx) {
134
131
  roles: allRoles
135
132
  }),
136
133
  template: path.join(ROLES_TEMPLATE_DIR, "index.ts.ejs"),
137
- output: path.join(networkOutputPath, "roles", `index.${extension}`)
134
+ output: path.join(sourceOutputPath, "roles", `index.${extension}`)
138
135
  });
139
136
  for (const modelScope of modelScopes) {
140
137
  plan.push({
@@ -147,7 +144,7 @@ function createPlan(ctx) {
147
144
  }),
148
145
  template: path.join(MODELS_TEMPLATE_DIR, "index.ts.ejs"),
149
146
  output: path.join(
150
- networkOutputPath,
147
+ sourceOutputPath,
151
148
  "models",
152
149
  modelScope.fileName,
153
150
  `index.${extension}`
@@ -163,7 +160,7 @@ function createPlan(ctx) {
163
160
  }),
164
161
  template: path.join(MODELS_TEMPLATE_DIR, "model.ts.ejs"),
165
162
  output: path.join(
166
- networkOutputPath,
163
+ sourceOutputPath,
167
164
  "models",
168
165
  modelScope.fileName,
169
166
  `${model.fileName}.${extension}`
@@ -201,7 +198,6 @@ function createPlan(ctx) {
201
198
  extension,
202
199
  handlers: roleHandlers,
203
200
  networkName,
204
- networkFileName,
205
201
  networkDescription: networkSpec.description,
206
202
  networkVersion: networkSpec.version,
207
203
  remoteRoles,
@@ -222,18 +218,18 @@ function createPlan(ctx) {
222
218
  template: path.join(SDK_TEMPLATE_DIR, "index.ts.ejs"),
223
219
  output: path.join(sdkOutputPath, `index.${extension}`)
224
220
  });
221
+ plan.push({
222
+ name: `${language} package exports`,
223
+ command: "render",
224
+ getData: () => ({
225
+ isTypeScript,
226
+ extension,
227
+ modelScopes
228
+ }),
229
+ template: path.join(SRC_TEMPLATE_DIR, "index.ts.ejs"),
230
+ output: path.join(sourceOutputPath, `index.${extension}`)
231
+ });
225
232
  }
226
- plan.push({
227
- name: `${language} package exports`,
228
- command: "render",
229
- getData: () => ({
230
- isTypeScript,
231
- extension,
232
- networkExports
233
- }),
234
- template: path.join(SRC_TEMPLATE_DIR, "index.ts.ejs"),
235
- output: path.join(request.outputPath, "src", `index.${extension}`)
236
- });
237
233
  return {
238
234
  ...ctx,
239
235
  plan
@@ -1,4 +1,7 @@
1
- <% for (const network of ctx.networkExports) { -%>
2
- export * as <%= network.exportName %> from './<%= network.fileName %><%= ctx.isTypeScript ? '' : '/index.js' %>'
1
+ export * from './network<%= ctx.isTypeScript ? '' : '.js' %>'
2
+ export * from './roles<%= ctx.isTypeScript ? '' : '/index.js' %>'
3
+ export * as roles from './roles<%= ctx.isTypeScript ? '' : '/index.js' %>'
4
+ <% for (const modelScope of ctx.modelScopes) { -%>
5
+ export * as <%= modelScope.varName %>Models from './models/<%= modelScope.fileName %><%= ctx.isTypeScript ? '' : '/index.js' %>'
3
6
  <% } -%>
4
7
  export * as sdk from './sdk<%= ctx.isTypeScript ? '' : '/index.js' %>'
@@ -1,20 +1,13 @@
1
1
  <% const modelImportPath = ctx.isTypeScript ? `../models/${ctx.fileName}` : `../models/${ctx.fileName}/index.js` -%>
2
2
  <% if (ctx.isTypeScript) { -%>
3
3
  <% if (ctx.messages.length > 0) { -%>
4
- import type {
5
- <% for (const message of ctx.messages) { -%>
6
- <%= message.payloadType %>,
7
- <%= message.payloadType %>Init,
8
- <% } -%>
9
- } from '<%= modelImportPath %>'
4
+ <% const payloadImports = ctx.messages.flatMap(message => [message.payloadType, `${message.payloadType}Init`]) -%>
5
+ import type { <%= payloadImports.join(', ') %> } from '<%= modelImportPath %>'
10
6
  <% } -%>
11
7
  <% const importedPeerRoles = new Map(); for (const message of ctx.messages) { for (const fromRole of message.fromRoles ?? []) { if (fromRole.roleName !== ctx.roleName) importedPeerRoles.set(fromRole.roleName, fromRole) } } -%>
12
8
  <% if (importedPeerRoles.size > 0) { -%>
13
- import {
14
- <% for (const fromRole of importedPeerRoles.values()) { -%>
15
- <%= fromRole.roleClassName %>,
16
- <% } -%>
17
- } from './index'
9
+ <% const peerRoleImports = [...importedPeerRoles.values()].map(fromRole => fromRole.roleClassName) -%>
10
+ import { <%= peerRoleImports.join(', ') %> } from './index'
18
11
  <% } -%>
19
12
 
20
13
  export interface <%= ctx.apiName %> {
@@ -30,12 +23,12 @@ export class <%= ctx.roleClassName %> {
30
23
  messages: {
31
24
  <% for (const message of ctx.messages) { -%>
32
25
  <%= message.messageName %>: {
33
- payload: <%- JSON.stringify(message.schema, null, 16) %>,
26
+ payload: <%- JSON.stringify(message.schema) %>,
34
27
  },
35
28
  <% } -%>
36
29
  },
37
30
  }
38
- static readonly endpoints = <%- JSON.stringify(ctx.endpoints, null, 4) %>
31
+ static readonly endpoints = <%- JSON.stringify(ctx.endpoints) %>
39
32
  }
40
33
 
41
34
  export class <%= ctx.hostRoleClassName %> {
@@ -60,11 +53,8 @@ export class <%= ctx.hostRoleClassName %> {
60
53
  <% } else { -%>
61
54
  <% const importedPeerRoles = new Map(); for (const message of ctx.messages) { for (const fromRole of message.fromRoles ?? []) { if (fromRole.roleName !== ctx.roleName) importedPeerRoles.set(fromRole.roleName, fromRole) } } -%>
62
55
  <% if (importedPeerRoles.size > 0) { -%>
63
- import {
64
- <% for (const fromRole of importedPeerRoles.values()) { -%>
65
- <%= fromRole.roleClassName %>,
66
- <% } -%>
67
- } from './index.js'
56
+ <% const peerRoleImports = [...importedPeerRoles.values()].map(fromRole => fromRole.roleClassName) -%>
57
+ import { <%= peerRoleImports.join(', ') %> } from './index.js'
68
58
  <% } -%>
69
59
 
70
60
  export class <%= ctx.roleClassName %> {
@@ -74,12 +64,12 @@ export class <%= ctx.roleClassName %> {
74
64
  messages: {
75
65
  <% for (const message of ctx.messages) { -%>
76
66
  <%= message.messageName %>: {
77
- payload: <%- JSON.stringify(message.schema, null, 16) %>,
67
+ payload: <%- JSON.stringify(message.schema) %>,
78
68
  },
79
69
  <% } -%>
80
70
  },
81
71
  }
82
- static endpoints = <%- JSON.stringify(ctx.endpoints, null, 4) %>
72
+ static endpoints = <%- JSON.stringify(ctx.endpoints) %>
83
73
  }
84
74
 
85
75
  export class <%= ctx.hostRoleClassName %> {
@@ -1,7 +1,6 @@
1
- <% const networkDir = ctx.networkFileName -%>
2
- <% const networkImport = ctx.isTypeScript ? `../${networkDir}/network` : `../${networkDir}/network.js` -%>
3
- <% const modelImportPath = ctx.isTypeScript ? `../${networkDir}/models/${ctx.fileName}` : `../${networkDir}/models/${ctx.fileName}/index.js` -%>
4
- <% const rolesImport = ctx.isTypeScript ? `../${networkDir}/roles` : `../${networkDir}/roles/index.js` -%>
1
+ <% const networkImport = ctx.isTypeScript ? '../network' : '../network.js' -%>
2
+ <% const modelImportPath = ctx.isTypeScript ? `../models/${ctx.fileName}` : `../models/${ctx.fileName}/index.js` -%>
3
+ <% const rolesImport = ctx.isTypeScript ? '../roles' : '../roles/index.js' -%>
5
4
  <% if (ctx.isTypeScript) { -%>
6
5
  import * as WS from '@polytric/openws/class'
7
6
  import * as Fluent from '@polytric/openws/fluent'
@@ -12,6 +12,7 @@ interface RawInput {
12
12
  spec: string;
13
13
  out: string;
14
14
  project: string;
15
+ network: string;
15
16
  hostRole: string[];
16
17
  language: 'csharp' | 'javascript' | 'typescript';
17
18
  environment: 'unity' | 'node' | 'browser';
@@ -21,6 +22,7 @@ interface BuildRequest {
21
22
  specPath: string;
22
23
  outputPath: string;
23
24
  project: string;
25
+ network: string;
24
26
  hostRoles: string[];
25
27
  target: {
26
28
  csharp?: {
@@ -12,6 +12,7 @@ interface RawInput {
12
12
  spec: string;
13
13
  out: string;
14
14
  project: string;
15
+ network: string;
15
16
  hostRole: string[];
16
17
  language: 'csharp' | 'javascript' | 'typescript';
17
18
  environment: 'unity' | 'node' | 'browser';
@@ -21,6 +22,7 @@ interface BuildRequest {
21
22
  specPath: string;
22
23
  outputPath: string;
23
24
  project: string;
25
+ network: string;
24
26
  hostRoles: string[];
25
27
  target: {
26
28
  csharp?: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polytric/openws-sdkgen",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "OpenWS SDK generator CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -36,7 +36,8 @@
36
36
  "@pocketgems/schema": "^0.1.3",
37
37
  "ajv": "^8.17.1",
38
38
  "ejs": "^3.1.10",
39
- "yargs": "^18.0.0",
39
+ "prettier": "^3.7.4",
40
+ "yargs": "^17.7.2",
40
41
  "@polytric/openws-spec": "0.0.4"
41
42
  },
42
43
  "devDependencies": {
@@ -54,8 +55,8 @@
54
55
  "scripts": {
55
56
  "build": "tsup",
56
57
  "typecheck": "tsc --noEmit",
57
- "test:csharp:unity": "node dist/main.cjs --spec ./test/spec.json --out ./generated/dotnet/unity --project Example --hostRole client --language csharp --environment unity",
58
- "test:typescript:node": "node dist/main.cjs --spec ./test/spec.json --out ./generated/typescript/node --project Example --hostRole client --language typescript --environment node",
58
+ "test:csharp:unity": "node dist/main.cjs --spec ./test/spec.json --out ./generated/dotnet/unity --project Example --network core --hostRole client --language csharp --environment unity",
59
+ "test:typescript:node": "node dist/main.cjs --spec ./test/spec.json --out ./generated/typescript/node --project Example --network core --hostRole client --language typescript --environment node",
59
60
  "test:typescript:server": "tsx ./test/typescript/server.ts",
60
61
  "test:typescript:client": "tsx ./test/typescript/client.ts"
61
62
  }
@@ -1,6 +0,0 @@
1
- export * from './network<%= ctx.isTypeScript ? '' : '.js' %>'
2
- export * from './roles<%= ctx.isTypeScript ? '' : '/index.js' %>'
3
- export * as roles from './roles<%= ctx.isTypeScript ? '' : '/index.js' %>'
4
- <% for (const modelScope of ctx.modelScopes) { -%>
5
- export * as <%= modelScope.varName %>Models from './models/<%= modelScope.fileName %><%= ctx.isTypeScript ? '' : '/index.js' %>'
6
- <% } -%>