@polytric/openws-sdkgen 0.0.17 → 0.0.19

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.
@@ -39,10 +39,12 @@ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${_
39
39
  var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
40
40
 
41
41
  // src/plans/dotnet.ts
42
+ var import_node_crypto = require("crypto");
42
43
  var import_node_path = __toESM(require("path"), 1);
43
44
  var import_node_url = require("url");
44
45
  var __dirname = import_node_path.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
45
46
  var TEMPLATE_DIR = import_node_path.default.join(__dirname, "../templates/dotnet");
47
+ var UNITY_META_GUID_PREFIX = "openws-sdkgen/unity-meta/";
46
48
  function pascalCase(str) {
47
49
  return str.charAt(0).toUpperCase() + str.slice(1);
48
50
  }
@@ -55,7 +57,12 @@ function createPlan(ctx) {
55
57
  if (!request) throw new Error("request is required");
56
58
  const assemblyName = `${pascalCase(ir.package.project)}.${pascalCase(ir.package.service)}.Sdk`;
57
59
  ir.assemblyName = assemblyName;
58
- const plan = [
60
+ const plan = [];
61
+ const folderMetaOutputs = /* @__PURE__ */ new Set();
62
+ pushUnityAssetStep(
63
+ plan,
64
+ request.outputPath,
65
+ folderMetaOutputs,
59
66
  {
60
67
  name: "assembly definition",
61
68
  command: "render",
@@ -63,6 +70,12 @@ function createPlan(ctx) {
63
70
  template: import_node_path.default.join(TEMPLATE_DIR, "Service.asmdef.ejs"),
64
71
  output: import_node_path.default.join(request.outputPath, assemblyName, `${assemblyName}.asmdef`)
65
72
  },
73
+ "UnityAssemblyDefinition.meta.ejs"
74
+ );
75
+ pushUnityAssetStep(
76
+ plan,
77
+ request.outputPath,
78
+ folderMetaOutputs,
66
79
  {
67
80
  name: "user assembly reference",
68
81
  command: "render",
@@ -73,8 +86,9 @@ function createPlan(ctx) {
73
86
  `${assemblyName}.User`,
74
87
  `${assemblyName}.User.asmref`
75
88
  )
76
- }
77
- ];
89
+ },
90
+ "UnityAssemblyDefinitionReference.meta.ejs"
91
+ );
78
92
  for (const networkIr of ir.networks) {
79
93
  const networkNamespace = `${pascalCase(ir.package.project)}.${pascalCase(ir.package.service)}.${pascalCase(networkIr.name)}`;
80
94
  const networkClassName = `${pascalCase(networkIr.name)}Network`;
@@ -107,7 +121,7 @@ function createPlan(ctx) {
107
121
  }
108
122
  const allRoles = [...hostRoles, ...remoteRoles];
109
123
  const allModelImports = allRoles.map((role) => `${networkNamespace}.Models.${role.className}`);
110
- plan.push({
124
+ pushUnityCSharpStep(plan, request.outputPath, folderMetaOutputs, {
111
125
  name: `network ${networkIr.name}`,
112
126
  command: "render",
113
127
  getData: () => ({
@@ -144,7 +158,7 @@ function createPlan(ctx) {
144
158
  for (const hostRole of hostRoles) {
145
159
  const roleHandlers = networkIr.handlers.filter((h) => h.roleName === hostRole.roleName);
146
160
  const modelImports = [`${networkNamespace}.Models.${hostRole.className}`];
147
- plan.push({
161
+ pushUnityCSharpStep(plan, request.outputPath, folderMetaOutputs, {
148
162
  name: `host role ${hostRole.className}`,
149
163
  command: "render",
150
164
  getData: () => ({
@@ -157,7 +171,7 @@ function createPlan(ctx) {
157
171
  template: import_node_path.default.join(TEMPLATE_DIR, "HostRole.cs.ejs"),
158
172
  output: import_node_path.default.join(networkOutputPath, "Roles", `${hostRole.className}.cs`)
159
173
  });
160
- plan.push({
174
+ pushUnityCSharpStep(plan, request.outputPath, folderMetaOutputs, {
161
175
  name: `user host role ${hostRole.className}`,
162
176
  command: "render",
163
177
  getData: () => ({
@@ -174,7 +188,7 @@ function createPlan(ctx) {
174
188
  for (const remoteRole of remoteRoles) {
175
189
  const roleMessages = networkIr.messages.filter((m) => m.roleName === remoteRole.roleName);
176
190
  const modelImports = [`${networkNamespace}.Models.${remoteRole.className}`];
177
- plan.push({
191
+ pushUnityCSharpStep(plan, request.outputPath, folderMetaOutputs, {
178
192
  name: `remote role ${remoteRole.className}`,
179
193
  command: "render",
180
194
  getData: () => ({
@@ -189,7 +203,7 @@ function createPlan(ctx) {
189
203
  }
190
204
  for (const modelIr of networkIr.models) {
191
205
  if (modelIr.type !== "object") continue;
192
- plan.push({
206
+ pushUnityCSharpStep(plan, request.outputPath, folderMetaOutputs, {
193
207
  name: `model ${modelIr.className}`,
194
208
  command: "render",
195
209
  getData: () => modelIr,
@@ -208,6 +222,60 @@ function createPlan(ctx) {
208
222
  plan
209
223
  };
210
224
  }
225
+ function pushUnityCSharpStep(plan, outputRoot, folderMetaOutputs, step) {
226
+ pushUnityAssetStep(plan, outputRoot, folderMetaOutputs, step, "UnityMonoScript.meta.ejs");
227
+ }
228
+ function pushUnityAssetStep(plan, outputRoot, folderMetaOutputs, step, metaTemplate) {
229
+ pushUnityFolderMetaSteps(plan, outputRoot, folderMetaOutputs, import_node_path.default.dirname(step.output));
230
+ plan.push(step);
231
+ plan.push({
232
+ name: `${step.name} meta`,
233
+ command: "render",
234
+ getData: () => ({
235
+ guid: unityMetaGuid(outputRoot, step.output)
236
+ }),
237
+ template: import_node_path.default.join(TEMPLATE_DIR, metaTemplate),
238
+ output: `${step.output}.meta`
239
+ });
240
+ }
241
+ function pushUnityFolderMetaSteps(plan, outputRoot, folderMetaOutputs, leafFolderPath) {
242
+ const folders = getGeneratedFolders(outputRoot, leafFolderPath);
243
+ for (const folderPath of folders) {
244
+ const metaOutput = `${folderPath}.meta`;
245
+ if (folderMetaOutputs.has(metaOutput)) continue;
246
+ folderMetaOutputs.add(metaOutput);
247
+ plan.push({
248
+ name: `folder ${normalizeRelativeAssetPath(outputRoot, folderPath)} meta`,
249
+ command: "render",
250
+ getData: () => ({
251
+ guid: unityMetaGuid(outputRoot, folderPath)
252
+ }),
253
+ template: import_node_path.default.join(TEMPLATE_DIR, "UnityFolder.meta.ejs"),
254
+ output: metaOutput
255
+ });
256
+ }
257
+ }
258
+ function getGeneratedFolders(outputRoot, leafFolderPath) {
259
+ const folders = [];
260
+ let folderPath = import_node_path.default.normalize(leafFolderPath);
261
+ while (isGeneratedPath(outputRoot, folderPath)) {
262
+ folders.push(folderPath);
263
+ const parentPath = import_node_path.default.dirname(folderPath);
264
+ if (parentPath === folderPath) break;
265
+ folderPath = parentPath;
266
+ }
267
+ return folders.reverse();
268
+ }
269
+ function isGeneratedPath(outputRoot, assetPath) {
270
+ const relativePath = import_node_path.default.relative(import_node_path.default.normalize(outputRoot), import_node_path.default.normalize(assetPath));
271
+ return relativePath !== "" && relativePath !== ".." && !relativePath.startsWith(`..${import_node_path.default.sep}`) && !import_node_path.default.isAbsolute(relativePath);
272
+ }
273
+ function unityMetaGuid(outputRoot, assetPath) {
274
+ return (0, import_node_crypto.createHash)("md5").update(`${UNITY_META_GUID_PREFIX}${normalizeRelativeAssetPath(outputRoot, assetPath)}`).digest("hex");
275
+ }
276
+ function normalizeRelativeAssetPath(outputRoot, assetPath) {
277
+ return import_node_path.default.relative(import_node_path.default.normalize(outputRoot), import_node_path.default.normalize(assetPath)).replaceAll(import_node_path.default.sep, "/").replaceAll("\\", "/");
278
+ }
211
279
  function mapType(property) {
212
280
  switch (property.type) {
213
281
  case "string":
@@ -1,8 +1,10 @@
1
1
  // src/plans/dotnet.ts
2
+ import { createHash } from "crypto";
2
3
  import path from "path";
3
4
  import { fileURLToPath } from "url";
4
5
  var __dirname = path.dirname(fileURLToPath(import.meta.url));
5
6
  var TEMPLATE_DIR = path.join(__dirname, "../templates/dotnet");
7
+ var UNITY_META_GUID_PREFIX = "openws-sdkgen/unity-meta/";
6
8
  function pascalCase(str) {
7
9
  return str.charAt(0).toUpperCase() + str.slice(1);
8
10
  }
@@ -15,7 +17,12 @@ function createPlan(ctx) {
15
17
  if (!request) throw new Error("request is required");
16
18
  const assemblyName = `${pascalCase(ir.package.project)}.${pascalCase(ir.package.service)}.Sdk`;
17
19
  ir.assemblyName = assemblyName;
18
- const plan = [
20
+ const plan = [];
21
+ const folderMetaOutputs = /* @__PURE__ */ new Set();
22
+ pushUnityAssetStep(
23
+ plan,
24
+ request.outputPath,
25
+ folderMetaOutputs,
19
26
  {
20
27
  name: "assembly definition",
21
28
  command: "render",
@@ -23,6 +30,12 @@ function createPlan(ctx) {
23
30
  template: path.join(TEMPLATE_DIR, "Service.asmdef.ejs"),
24
31
  output: path.join(request.outputPath, assemblyName, `${assemblyName}.asmdef`)
25
32
  },
33
+ "UnityAssemblyDefinition.meta.ejs"
34
+ );
35
+ pushUnityAssetStep(
36
+ plan,
37
+ request.outputPath,
38
+ folderMetaOutputs,
26
39
  {
27
40
  name: "user assembly reference",
28
41
  command: "render",
@@ -33,8 +46,9 @@ function createPlan(ctx) {
33
46
  `${assemblyName}.User`,
34
47
  `${assemblyName}.User.asmref`
35
48
  )
36
- }
37
- ];
49
+ },
50
+ "UnityAssemblyDefinitionReference.meta.ejs"
51
+ );
38
52
  for (const networkIr of ir.networks) {
39
53
  const networkNamespace = `${pascalCase(ir.package.project)}.${pascalCase(ir.package.service)}.${pascalCase(networkIr.name)}`;
40
54
  const networkClassName = `${pascalCase(networkIr.name)}Network`;
@@ -67,7 +81,7 @@ function createPlan(ctx) {
67
81
  }
68
82
  const allRoles = [...hostRoles, ...remoteRoles];
69
83
  const allModelImports = allRoles.map((role) => `${networkNamespace}.Models.${role.className}`);
70
- plan.push({
84
+ pushUnityCSharpStep(plan, request.outputPath, folderMetaOutputs, {
71
85
  name: `network ${networkIr.name}`,
72
86
  command: "render",
73
87
  getData: () => ({
@@ -104,7 +118,7 @@ function createPlan(ctx) {
104
118
  for (const hostRole of hostRoles) {
105
119
  const roleHandlers = networkIr.handlers.filter((h) => h.roleName === hostRole.roleName);
106
120
  const modelImports = [`${networkNamespace}.Models.${hostRole.className}`];
107
- plan.push({
121
+ pushUnityCSharpStep(plan, request.outputPath, folderMetaOutputs, {
108
122
  name: `host role ${hostRole.className}`,
109
123
  command: "render",
110
124
  getData: () => ({
@@ -117,7 +131,7 @@ function createPlan(ctx) {
117
131
  template: path.join(TEMPLATE_DIR, "HostRole.cs.ejs"),
118
132
  output: path.join(networkOutputPath, "Roles", `${hostRole.className}.cs`)
119
133
  });
120
- plan.push({
134
+ pushUnityCSharpStep(plan, request.outputPath, folderMetaOutputs, {
121
135
  name: `user host role ${hostRole.className}`,
122
136
  command: "render",
123
137
  getData: () => ({
@@ -134,7 +148,7 @@ function createPlan(ctx) {
134
148
  for (const remoteRole of remoteRoles) {
135
149
  const roleMessages = networkIr.messages.filter((m) => m.roleName === remoteRole.roleName);
136
150
  const modelImports = [`${networkNamespace}.Models.${remoteRole.className}`];
137
- plan.push({
151
+ pushUnityCSharpStep(plan, request.outputPath, folderMetaOutputs, {
138
152
  name: `remote role ${remoteRole.className}`,
139
153
  command: "render",
140
154
  getData: () => ({
@@ -149,7 +163,7 @@ function createPlan(ctx) {
149
163
  }
150
164
  for (const modelIr of networkIr.models) {
151
165
  if (modelIr.type !== "object") continue;
152
- plan.push({
166
+ pushUnityCSharpStep(plan, request.outputPath, folderMetaOutputs, {
153
167
  name: `model ${modelIr.className}`,
154
168
  command: "render",
155
169
  getData: () => modelIr,
@@ -168,6 +182,60 @@ function createPlan(ctx) {
168
182
  plan
169
183
  };
170
184
  }
185
+ function pushUnityCSharpStep(plan, outputRoot, folderMetaOutputs, step) {
186
+ pushUnityAssetStep(plan, outputRoot, folderMetaOutputs, step, "UnityMonoScript.meta.ejs");
187
+ }
188
+ function pushUnityAssetStep(plan, outputRoot, folderMetaOutputs, step, metaTemplate) {
189
+ pushUnityFolderMetaSteps(plan, outputRoot, folderMetaOutputs, path.dirname(step.output));
190
+ plan.push(step);
191
+ plan.push({
192
+ name: `${step.name} meta`,
193
+ command: "render",
194
+ getData: () => ({
195
+ guid: unityMetaGuid(outputRoot, step.output)
196
+ }),
197
+ template: path.join(TEMPLATE_DIR, metaTemplate),
198
+ output: `${step.output}.meta`
199
+ });
200
+ }
201
+ function pushUnityFolderMetaSteps(plan, outputRoot, folderMetaOutputs, leafFolderPath) {
202
+ const folders = getGeneratedFolders(outputRoot, leafFolderPath);
203
+ for (const folderPath of folders) {
204
+ const metaOutput = `${folderPath}.meta`;
205
+ if (folderMetaOutputs.has(metaOutput)) continue;
206
+ folderMetaOutputs.add(metaOutput);
207
+ plan.push({
208
+ name: `folder ${normalizeRelativeAssetPath(outputRoot, folderPath)} meta`,
209
+ command: "render",
210
+ getData: () => ({
211
+ guid: unityMetaGuid(outputRoot, folderPath)
212
+ }),
213
+ template: path.join(TEMPLATE_DIR, "UnityFolder.meta.ejs"),
214
+ output: metaOutput
215
+ });
216
+ }
217
+ }
218
+ function getGeneratedFolders(outputRoot, leafFolderPath) {
219
+ const folders = [];
220
+ let folderPath = path.normalize(leafFolderPath);
221
+ while (isGeneratedPath(outputRoot, folderPath)) {
222
+ folders.push(folderPath);
223
+ const parentPath = path.dirname(folderPath);
224
+ if (parentPath === folderPath) break;
225
+ folderPath = parentPath;
226
+ }
227
+ return folders.reverse();
228
+ }
229
+ function isGeneratedPath(outputRoot, assetPath) {
230
+ const relativePath = path.relative(path.normalize(outputRoot), path.normalize(assetPath));
231
+ return relativePath !== "" && relativePath !== ".." && !relativePath.startsWith(`..${path.sep}`) && !path.isAbsolute(relativePath);
232
+ }
233
+ function unityMetaGuid(outputRoot, assetPath) {
234
+ return createHash("md5").update(`${UNITY_META_GUID_PREFIX}${normalizeRelativeAssetPath(outputRoot, assetPath)}`).digest("hex");
235
+ }
236
+ function normalizeRelativeAssetPath(outputRoot, assetPath) {
237
+ return path.relative(path.normalize(outputRoot), path.normalize(assetPath)).replaceAll(path.sep, "/").replaceAll("\\", "/");
238
+ }
171
239
  function mapType(property) {
172
240
  switch (property.type) {
173
241
  case "string":
@@ -0,0 +1,7 @@
1
+ fileFormatVersion: 2
2
+ guid: <%= ctx.guid %>
3
+ AssemblyDefinitionImporter:
4
+ externalObjects: {}
5
+ userData:
6
+ assetBundleName:
7
+ assetBundleVariant:
@@ -0,0 +1,7 @@
1
+ fileFormatVersion: 2
2
+ guid: <%= ctx.guid %>
3
+ AssemblyDefinitionReferenceImporter:
4
+ externalObjects: {}
5
+ userData:
6
+ assetBundleName:
7
+ assetBundleVariant:
@@ -0,0 +1,8 @@
1
+ fileFormatVersion: 2
2
+ guid: <%= ctx.guid %>
3
+ folderAsset: yes
4
+ DefaultImporter:
5
+ externalObjects: {}
6
+ userData:
7
+ assetBundleName:
8
+ assetBundleVariant:
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: <%= ctx.guid %>
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -133,13 +133,13 @@ export class <%= ctx.className %> {
133
133
  <% } -%>
134
134
  <% for (const remoteRole of ctx.remoteRoles) { -%>
135
135
  this.binder.fromRoles[<%- JSON.stringify(remoteRole.roleName) %>].onOpen(async (fromRole, peer) => {
136
- await this.handleOpen(fromRole, peer as <%= ctx.className %>Peer)
136
+ await this.handleOpen(fromRole, peer as unknown as <%= ctx.className %>Peer)
137
137
  })
138
138
  this.binder.fromRoles[<%- JSON.stringify(remoteRole.roleName) %>].onClose(async (fromRole, peer) => {
139
- await this.handleClose(fromRole, peer as <%= ctx.className %>Peer)
139
+ await this.handleClose(fromRole, peer as unknown as <%= ctx.className %>Peer)
140
140
  })
141
141
  this.binder.fromRoles[<%- JSON.stringify(remoteRole.roleName) %>].onError(async (fromRole, peer, error) => {
142
- await this.handleError(fromRole, peer as <%= ctx.className %>Peer, error)
142
+ await this.handleError(fromRole, peer as unknown as <%= ctx.className %>Peer, error)
143
143
  })
144
144
  <% } -%>
145
145
  if (canBindTransport(transport)) {
@@ -159,13 +159,21 @@ export class <%= ctx.className %> {
159
159
  case <%- JSON.stringify(remoteRole.roleName) %>: {
160
160
  const remoteEndpoint = endpoint ?? (<%- remoteRole.endpoints.length > 0 ? JSON.stringify(remoteRole.endpoints[0]) : 'undefined' %> as OpenWsEndpoint | undefined)
161
161
  await this.transport.connect?.(roleName, remoteEndpoint)
162
- const session = this.runtime.newSession(this.sendEnvelope)
162
+ let connection: <%= ctx.className %>Connection | undefined
163
+ const session = this.runtime.newSession(this.sendEnvelope, async () => {
164
+ if (!connection) {
165
+ await session.close()
166
+ return
167
+ }
168
+ await this.closeConnection(connection)
169
+ })
163
170
  const <%= remoteRole.varName %>Peer = await session.open(<%- JSON.stringify(remoteRole.roleName) %>)
164
- this.connections.add({
171
+ connection = {
165
172
  roleName: <%- JSON.stringify(remoteRole.roleName) %>,
166
173
  session,
167
174
  peer: <%= remoteRole.varName %>Peer,
168
- })
175
+ }
176
+ this.connections.add(connection)
169
177
  this.<%= remoteRole.peerVarName %> = <%= remoteRole.varName %>Peer as unknown as <%= remoteRole.scopedPeerName %>
170
178
  <% for (const handler of ctx.handlers) { -%>
171
179
  <% const handlerDefaultPeer = handler.bindFromRoles.find(fromRole => ctx.remoteRoles.some(remoteRole => remoteRole.roleName === fromRole.roleName)) ?? ctx.remoteRoles[0] -%>
@@ -198,11 +206,11 @@ export class <%= ctx.className %> {
198
206
  await this.closeSessions(peer)
199
207
  return
200
208
  }
201
- const connection = this.findConnectionByPeer(peer as PeerProto)
209
+ const connection = this.findConnectionByPeer(peer as unknown as PeerProto)
202
210
  if (!connection) {
203
211
  throw new Error('Peer is not connected')
204
212
  }
205
- await this.closeConnection(connection)
213
+ await this.runtime.disconnect(connection.peer)
206
214
  }
207
215
 
208
216
  /**
@@ -518,13 +526,21 @@ export class <%= ctx.className %> {
518
526
  case <%- JSON.stringify(remoteRole.roleName) %>: {
519
527
  const remoteEndpoint = endpoint ?? <%- remoteRole.endpoints.length > 0 ? JSON.stringify(remoteRole.endpoints[0]) : 'undefined' %>
520
528
  await this.transport.connect?.(roleName, remoteEndpoint)
521
- const session = this.runtime.newSession(this.sendEnvelope)
529
+ let connection
530
+ const session = this.runtime.newSession(this.sendEnvelope, async () => {
531
+ if (!connection) {
532
+ await session.close()
533
+ return
534
+ }
535
+ await this.#closeConnection(connection)
536
+ })
522
537
  this.<%= remoteRole.peerVarName %> = await session.open(<%- JSON.stringify(remoteRole.roleName) %>)
523
- this.#connections.add({
538
+ connection = {
524
539
  roleName: <%- JSON.stringify(remoteRole.roleName) %>,
525
540
  session,
526
541
  peer: this.<%= remoteRole.peerVarName %>,
527
- })
542
+ }
543
+ this.#connections.add(connection)
528
544
  <% for (const handler of ctx.handlers) { -%>
529
545
  <% const handlerDefaultPeer = handler.bindFromRoles.find(fromRole => ctx.remoteRoles.some(remoteRole => remoteRole.roleName === fromRole.roleName)) ?? ctx.remoteRoles[0] -%>
530
546
  <% if (handlerDefaultPeer?.roleName === remoteRole.roleName) { -%>
@@ -556,7 +572,7 @@ export class <%= ctx.className %> {
556
572
  if (!connection) {
557
573
  throw new Error('Peer is not connected')
558
574
  }
559
- await this.#closeConnection(connection)
575
+ await this.runtime.disconnect(connection.peer)
560
576
  }
561
577
 
562
578
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polytric/openws-sdkgen",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "description": "OpenWS SDK generator CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -50,12 +50,12 @@
50
50
  "tsx": "^4.21.0",
51
51
  "typescript": "^5.9.3",
52
52
  "ws": "^8.18.3",
53
- "@polytric/openws": "^0.0.10"
53
+ "@polytric/openws": "^0.0.11"
54
54
  },
55
55
  "scripts": {
56
56
  "build": "tsup",
57
57
  "typecheck": "tsc --noEmit",
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",
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 && node --test ./test/dotnet/unity-meta.test.mjs",
59
59
  "test:javascript:node": "node dist/main.cjs --spec ./test/spec.json --out ./generated/javascript/node --project Example --network core --hostRole client --language javascript --environment node",
60
60
  "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",
61
61
  "test:typescript:server": "tsx ./test/typescript/server.ts",