javonet-nodejs-sdk 2.6.13 → 2.6.14

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 (90) hide show
  1. package/dist/core/handler/ArraySetItemHandler.cjs +9 -8
  2. package/dist/core/handler/AsDtoHandler.cjs +36 -0
  3. package/dist/core/handler/AsKwargsHandler.cjs +1 -1
  4. package/dist/core/handler/DtoPropertyHandler.cjs +36 -0
  5. package/dist/core/handler/Handler.cjs +14 -9
  6. package/dist/core/handler/PassDelegateHandler.cjs +1 -1
  7. package/dist/core/handler/ProjectResultAsDtoHandler.cjs +66 -0
  8. package/dist/core/handler/SetInstanceFieldHandler.cjs +1 -1
  9. package/dist/core/handler/SetStaticFieldHandler.cjs +6 -6
  10. package/dist/core/interpreter/Interpreter.cjs +14 -2
  11. package/dist/core/protocol/CommandDeserializer.cjs +8 -0
  12. package/dist/core/protocol/TypeDeserializer.cjs +4 -0
  13. package/dist/core/protocol/TypeSerializer.cjs +10 -5
  14. package/dist/core/webSocketClient/WebSocketClient.cjs +23 -5
  15. package/dist/core/webSocketClient/WebSocketClientBrowser.cjs +23 -5
  16. package/dist/plugins/PluginImplementationRegistry.cjs +214 -0
  17. package/dist/plugins/PluginPayloadBuilder.cjs +76 -0
  18. package/dist/plugins/PluginRegistry.cjs +255 -0
  19. package/dist/plugins/index.cjs +51 -0
  20. package/dist/plugins/interfaces/ICommunityPlugin.cjs +44 -0
  21. package/dist/plugins/interfaces/ICommunityReceivingPlugin.cjs +62 -0
  22. package/dist/plugins/interfaces/ICommunitySendingPlugin.cjs +59 -0
  23. package/dist/plugins/settings/BasePluginSettings.cjs +67 -0
  24. package/dist/plugins/settings/JwtGraftocodePluginSettings.cjs +40 -0
  25. package/dist/sdk/InvocationContext.cjs +133 -12
  26. package/dist/sdk/RuntimeContext.cjs +27 -9
  27. package/dist/sdk/configuration/configResolvers/ConfigResolver.cjs +7 -6
  28. package/dist/sdk/tools/DtoHelper.cjs +100 -0
  29. package/dist/types/core/handler/ArraySetItemHandler.d.ts +1 -1
  30. package/dist/types/core/handler/AsDtoHandler.d.ts +7 -0
  31. package/dist/types/core/handler/AsKwargsHandler.d.ts +1 -1
  32. package/dist/types/core/handler/DtoPropertyHandler.d.ts +7 -0
  33. package/dist/types/core/handler/ProjectResultAsDtoHandler.d.ts +9 -0
  34. package/dist/types/core/handler/SetInstanceFieldHandler.d.ts +1 -1
  35. package/dist/types/core/handler/SetStaticFieldHandler.d.ts +1 -1
  36. package/dist/types/core/interpreter/Interpreter.d.ts +3 -3
  37. package/dist/types/core/protocol/CommandDeserializer.d.ts +1 -0
  38. package/dist/types/core/protocol/TypeDeserializer.d.ts +1 -0
  39. package/dist/types/core/protocol/TypeSerializer.d.ts +1 -0
  40. package/dist/types/plugins/PluginImplementationRegistry.d.ts +121 -0
  41. package/dist/types/plugins/PluginPayloadBuilder.d.ts +32 -0
  42. package/dist/types/plugins/PluginRegistry.d.ts +157 -0
  43. package/dist/types/plugins/index.d.ts +8 -0
  44. package/dist/types/plugins/interfaces/ICommunityPlugin.d.ts +14 -0
  45. package/dist/types/plugins/interfaces/ICommunityReceivingPlugin.d.ts +50 -0
  46. package/dist/types/plugins/interfaces/ICommunitySendingPlugin.d.ts +27 -0
  47. package/dist/types/plugins/settings/BasePluginSettings.d.ts +45 -0
  48. package/dist/types/plugins/settings/JwtGraftocodePluginSettings.d.ts +32 -0
  49. package/dist/types/sdk/InvocationContext.d.ts +20 -2
  50. package/dist/types/sdk/RuntimeContext.d.ts +6 -0
  51. package/dist/types/sdk/configuration/configResolvers/ConfigResolver.d.ts +3 -6
  52. package/dist/types/sdk/tools/DtoHelper.d.ts +35 -0
  53. package/dist/types/utils/Command.d.ts +5 -0
  54. package/dist/types/utils/CommandType.d.ts +3 -0
  55. package/dist/types/utils/Type.d.ts +2 -1
  56. package/dist/utils/Command.cjs +40 -0
  57. package/dist/utils/CommandType.cjs +4 -1
  58. package/dist/utils/Type.cjs +2 -1
  59. package/lib/core/handler/ArraySetItemHandler.js +11 -10
  60. package/lib/core/handler/AsDtoHandler.js +11 -0
  61. package/lib/core/handler/AsKwargsHandler.js +1 -1
  62. package/lib/core/handler/DtoPropertyHandler.js +11 -0
  63. package/lib/core/handler/Handler.js +16 -7
  64. package/lib/core/handler/PassDelegateHandler.js +2 -1
  65. package/lib/core/handler/ProjectResultAsDtoHandler.js +47 -0
  66. package/lib/core/handler/SetInstanceFieldHandler.js +1 -1
  67. package/lib/core/handler/SetStaticFieldHandler.js +6 -6
  68. package/lib/core/interpreter/Interpreter.js +18 -4
  69. package/lib/core/protocol/CommandDeserializer.js +9 -0
  70. package/lib/core/protocol/TypeDeserializer.js +5 -0
  71. package/lib/core/protocol/TypeSerializer.js +11 -8
  72. package/lib/core/webSocketClient/WebSocketClient.js +30 -5
  73. package/lib/core/webSocketClient/WebSocketClientBrowser.js +29 -5
  74. package/lib/plugins/PluginImplementationRegistry.js +231 -0
  75. package/lib/plugins/PluginPayloadBuilder.js +60 -0
  76. package/lib/plugins/PluginRegistry.js +295 -0
  77. package/lib/plugins/index.js +29 -0
  78. package/lib/plugins/interfaces/ICommunityPlugin.js +25 -0
  79. package/lib/plugins/interfaces/ICommunityReceivingPlugin.js +57 -0
  80. package/lib/plugins/interfaces/ICommunitySendingPlugin.js +42 -0
  81. package/lib/plugins/settings/BasePluginSettings.js +79 -0
  82. package/lib/plugins/settings/JwtGraftocodePluginSettings.js +43 -0
  83. package/lib/sdk/InvocationContext.js +149 -14
  84. package/lib/sdk/RuntimeContext.js +33 -9
  85. package/lib/sdk/configuration/configResolvers/ConfigResolver.js +14 -8
  86. package/lib/sdk/tools/DtoHelper.js +90 -0
  87. package/lib/utils/Command.js +47 -0
  88. package/lib/utils/CommandType.js +3 -0
  89. package/lib/utils/Type.js +2 -1
  90. package/package.json +5 -1
@@ -6,6 +6,7 @@ import { CommandType } from '../utils/CommandType.js'
6
6
  import { ExceptionThrower } from '../utils/exception/ExceptionThrower.js'
7
7
  import { RuntimeName } from '../utils/RuntimeName.js'
8
8
  import { TypesHandler } from '../utils/TypesHandler.js'
9
+ import { DtoHelper } from './tools/DtoHelper.js'
9
10
  import { v4 as uuidv4 } from 'uuid' // add lightweight uuid generation
10
11
 
11
12
  /**
@@ -40,6 +41,8 @@ class AsyncLock {
40
41
  * @class
41
42
  */
42
43
  class InvocationContext {
44
+ /** @type {string} */
45
+ #runtimeContextId
43
46
  /** @type {RuntimeNameType} */
44
47
  #runtimeName
45
48
  /** @type {IConnectionData} */
@@ -50,6 +53,8 @@ class InvocationContext {
50
53
  #responseCommand = null
51
54
  /** @type {boolean} */
52
55
  #isExecuted = false
56
+ /** @type {boolean} */
57
+ #isDto = false
53
58
  /** @type {string | null} */
54
59
  #guid = null
55
60
  /** @type {AsyncLock} */
@@ -62,18 +67,22 @@ class InvocationContext {
62
67
 
63
68
  /**
64
69
  *
70
+ * @param {string} runtimeContextId
65
71
  * @param {RuntimeNameType} runtimeName
66
72
  * @param {IConnectionData} connectionData
67
- * @param {Command} command
73
+ * @param {Command | null} command
68
74
  * @param {boolean} isExecuted
75
+ * @param {boolean} isDto
69
76
  */
70
- constructor(runtimeName, connectionData, command, isExecuted = false) {
77
+ constructor(runtimeContextId, runtimeName, connectionData, command, isExecuted = false, isDto = false) {
78
+ this.#runtimeContextId = runtimeContextId
71
79
  this.#runtimeName = runtimeName
72
80
  this.#connectionData = connectionData
73
81
  this.#currentCommand = command
74
82
  this.#responseCommand = null
75
83
  this.#isExecuted = isExecuted
76
84
  this.#guid = uuidv4()
85
+ this.#isDto = isDto
77
86
  }
78
87
 
79
88
  /**
@@ -90,9 +99,12 @@ class InvocationContext {
90
99
  */
91
100
  #createInstanceContext(localCommand) {
92
101
  return new InvocationContext(
102
+ this.#runtimeContextId,
93
103
  this.#runtimeName,
94
104
  this.#connectionData,
95
- this.#buildCommand(localCommand)
105
+ this.#buildCommand(localCommand),
106
+ this.#isExecuted,
107
+ this.#isDto
96
108
  )
97
109
  }
98
110
 
@@ -142,6 +154,10 @@ class InvocationContext {
142
154
  throw new Error('currentCommand is undefined in Invocation Context execute method')
143
155
  }
144
156
 
157
+ if (this.#isDto) {
158
+ return this;
159
+ }
160
+
145
161
  const entries = Array.from(InvocationContext._invocationContexts.entries())
146
162
  entries.sort((a, b) => String(a[0]).localeCompare(String(b[0])))
147
163
  const releases = []
@@ -154,11 +170,11 @@ class InvocationContext {
154
170
  try {
155
171
  // Execute command on interpreter
156
172
  this.#responseCommand = await Interpreter.execute(
173
+ this.#runtimeContextId,
157
174
  this.#currentCommand,
158
175
  this.#connectionData
159
176
  )
160
177
 
161
-
162
178
  if (!this.#responseCommand) {
163
179
  throw new Error('responseCommand is undefined in Invocation Context execute method')
164
180
  }
@@ -169,16 +185,26 @@ class InvocationContext {
169
185
  // Process ValueForUpdate commands in response payload
170
186
  this.#responseCommand = this.#processUpdateInvocationContextCommands(this.#responseCommand)
171
187
 
172
- if (this.#responseCommand.commandType === CommandType.CreateClassInstance) {
173
- this.#currentCommand = this.#responseCommand
174
- this.#isExecuted = true
175
- return this
188
+ if (this.#currentCommand.commandType === CommandType.ProjectResultAsDto) {
189
+ return this.#convertResponseToDto()
176
190
  }
177
- return new InvocationContext(this.#runtimeName, this.#connectionData, this.#responseCommand, true)
191
+
192
+ return new InvocationContext(
193
+ this.#runtimeContextId,
194
+ this.#runtimeName,
195
+ this.#connectionData,
196
+ this.#responseCommand,
197
+ true,
198
+ this.#isDto
199
+ )
178
200
  } finally {
179
201
  // Release all locks in reverse order
180
202
  for (let i = releases.length - 1; i >= 0; i--) {
181
- try { releases[i]() } catch { /* ignore */ }
203
+ try {
204
+ releases[i]()
205
+ } catch {
206
+ /* ignore */
207
+ }
182
208
  }
183
209
  }
184
210
  }
@@ -217,7 +243,9 @@ class InvocationContext {
217
243
  const instanceGuid = String(cmdPayload[1])
218
244
  const invCtx = InvocationContext._invocationContexts.get(contextGuid)
219
245
  if (invCtx) {
220
- invCtx.#currentCommand = new Command(invCtx.#runtimeName, CommandType.Reference, [instanceGuid])
246
+ invCtx.#currentCommand = new Command(invCtx.#runtimeName, CommandType.Reference, [
247
+ instanceGuid,
248
+ ])
221
249
  InvocationContext._invocationContexts.delete(contextGuid)
222
250
  }
223
251
  }
@@ -281,6 +309,9 @@ class InvocationContext {
281
309
  createInstance(...args) {
282
310
  const localCommand = new Command(this.#runtimeName, CommandType.CreateClassInstance, [...args])
283
311
  const createInstanceInvCtx = this.#createInstanceContext(localCommand)
312
+ if (this.#isDto) {
313
+ return createInstanceInvCtx;
314
+ }
284
315
  return createInstanceInvCtx.#registerForUpdate()
285
316
  }
286
317
 
@@ -289,7 +320,9 @@ class InvocationContext {
289
320
  * @returns {InvocationContext}
290
321
  */
291
322
  #registerForUpdate() {
292
- this.#currentCommand = this.#buildCommand(new Command(this.#runtimeName, CommandType.RegisterForUpdate, [this.getGuid()]))
323
+ this.#currentCommand = this.#buildCommand(
324
+ new Command(this.#runtimeName, CommandType.RegisterForUpdate, [this.getGuid()])
325
+ )
293
326
  InvocationContext._invocationContexts.set(this.getGuid(), this)
294
327
  return this
295
328
  }
@@ -302,6 +335,19 @@ class InvocationContext {
302
335
  * @method
303
336
  */
304
337
  getInstanceField(fieldName) {
338
+ if (this.#isDto) {
339
+ // @ts-ignore
340
+ const value = DtoHelper.tryGetDtoFieldValue(this.#currentCommand, fieldName)
341
+ const valueCommand = new Command(this.#runtimeName, CommandType.Value, [value])
342
+ return new InvocationContext(
343
+ this.#runtimeContextId,
344
+ this.#runtimeName,
345
+ this.#connectionData,
346
+ valueCommand,
347
+ false,
348
+ this.#isDto
349
+ )
350
+ }
305
351
  let localCommand = new Command(this.#runtimeName, CommandType.GetInstanceField, [fieldName])
306
352
  return this.#createInstanceContext(localCommand)
307
353
  }
@@ -316,7 +362,19 @@ class InvocationContext {
316
362
  */
317
363
  setInstanceField(fieldName, value) {
318
364
  let localCommand = new Command(this.#runtimeName, CommandType.SetInstanceField, [fieldName, value])
319
- return this.#createInstanceContext(localCommand)
365
+ const newCommand = this.#buildCommand(localCommand)
366
+ if (this.#isDto) {
367
+ this.#currentCommand = newCommand
368
+ return this
369
+ }
370
+ return new InvocationContext(
371
+ this.#runtimeContextId,
372
+ this.#runtimeName,
373
+ this.#connectionData,
374
+ newCommand,
375
+ this.#isExecuted,
376
+ this.#isDto
377
+ )
320
378
  }
321
379
 
322
380
  /**
@@ -493,6 +551,46 @@ class InvocationContext {
493
551
  return this.#createInstanceContext(localCommand)
494
552
  }
495
553
 
554
+ /**
555
+ * Returns a new InvocationContext that acts as a DTO (Data Transfer Object).
556
+ * When actAsDto is true, field get/set operate on local state without remote calls.
557
+ * @param {boolean} actAsDto - Whether to treat this context as a DTO.
558
+ * @returns {InvocationContext} A new InvocationContext with the DTO flag set.
559
+ * @method
560
+ */
561
+ asDto(actAsDto = true) {
562
+ return new InvocationContext(
563
+ this.#runtimeContextId,
564
+ this.#runtimeName,
565
+ this.#connectionData,
566
+ this.#currentCommand,
567
+ false,
568
+ actAsDto
569
+ )
570
+ }
571
+
572
+ /**
573
+ * Projects the result as a DTO with the specified property names.
574
+ * When propertiesNames is a single empty string, returns this context unchanged.
575
+ * @param {...string} propertiesNames - Names of properties to include in the DTO.
576
+ * @returns {InvocationContext} A new InvocationContext that will materialize as a DTO.
577
+ * @method
578
+ */
579
+ projectResultAsDto(...propertiesNames) {
580
+ if (propertiesNames.length === 1 && (propertiesNames[0] === null || propertiesNames[0] === '')) {
581
+ return this
582
+ }
583
+ let localCommand = new Command(this.#runtimeName, CommandType.ProjectResultAsDto, propertiesNames)
584
+ return new InvocationContext(
585
+ this.#runtimeContextId,
586
+ this.#runtimeName,
587
+ this.#connectionData,
588
+ this.#buildCommand(localCommand),
589
+ false,
590
+ false
591
+ )
592
+ }
593
+
496
594
  /**
497
595
  * Retrieves the type of the object from the target runtime.
498
596
  * @returns {Promise<string>} The type of the object.
@@ -502,6 +600,7 @@ class InvocationContext {
502
600
  async getResultType() {
503
601
  const localCommand = new Command(this.#runtimeName, CommandType.GetResultType, [])
504
602
  const invocationContext = new InvocationContext(
603
+ this.#runtimeContextId,
505
604
  this.#runtimeName,
506
605
  this.#connectionData,
507
606
  this.#buildCommand(localCommand)
@@ -529,6 +628,7 @@ class InvocationContext {
529
628
  async retrieveArray() {
530
629
  const localCommand = new Command(this.#runtimeName, CommandType.RetrieveArray, [])
531
630
  const localInvCtx = new InvocationContext(
631
+ this.#runtimeContextId,
532
632
  this.#runtimeName,
533
633
  this.#connectionData,
534
634
  this.#buildCommand(localCommand)
@@ -558,9 +658,12 @@ class InvocationContext {
558
658
 
559
659
  /**
560
660
  * @param {Command} command
561
- * @returns {Command}
661
+ * @returns {Command | null}
562
662
  */
563
663
  #buildCommand(command) {
664
+ if (!command) {
665
+ return this.#currentCommand
666
+ }
564
667
  for (let i = 0; i < command.payload.length; i++) {
565
668
  command.payload[i] = this.#encapsulatePayloadItem(command.payload[i])
566
669
  }
@@ -603,6 +706,38 @@ class InvocationContext {
603
706
  )
604
707
  }
605
708
  }
709
+
710
+ #convertResponseToDto() {
711
+ /** @type {Command} */
712
+ // @ts-ignore
713
+ const responseCommand = this.#responseCommand
714
+ // const instanceReferenceCommand = responseCommand.payload[0]
715
+ const getTypeCommand = responseCommand.payload[1]
716
+
717
+ let projectResultAsDtoInvCtx = new InvocationContext(
718
+ this.#runtimeContextId,
719
+ this.#runtimeName,
720
+ this.#connectionData,
721
+ new Command(this.#runtimeName, CommandType.CreateClassInstance, [getTypeCommand]),
722
+ true,
723
+ true
724
+ )
725
+
726
+ if (responseCommand.payload.length >= 3) {
727
+ for (let i = 2; i < responseCommand.payload.length; i++) {
728
+ const item = responseCommand.payload[i]
729
+ if (item instanceof Command && item.commandType === CommandType.DtoProperty) {
730
+ const propertyName = item.payload[0] ? String(item.payload[0]) : ''
731
+ const propertyValue = item.payload.length > 1 ? item.payload[1] : ''
732
+ projectResultAsDtoInvCtx = projectResultAsDtoInvCtx.setInstanceField(
733
+ propertyName,
734
+ propertyValue
735
+ )
736
+ }
737
+ }
738
+ }
739
+ return projectResultAsDtoInvCtx
740
+ }
606
741
  }
607
742
 
608
743
  export { InvocationContext }
@@ -11,6 +11,7 @@ import { TypesHandler } from '../utils/TypesHandler.js'
11
11
  import { UtilsConst } from '../utils/UtilsConst.js'
12
12
  import { Config } from "./configuration/Config.js"
13
13
  import { getRequire, isNodejsRuntime } from '../utils/Runtime.js'
14
+ import { generateGUID } from '../utils/guid/generateGuid.js'
14
15
 
15
16
 
16
17
  /**
@@ -47,6 +48,8 @@ class RuntimeContext {
47
48
  #currentCommand = null
48
49
  /** @type {Command | null} */
49
50
  #responseCommand = null
51
+ /** @type {string} Unique identifier for this runtime context instance */
52
+ #runtimeContextId = ''
50
53
 
51
54
  /**
52
55
  * @param {RuntimeNameType} runtimeName
@@ -56,6 +59,8 @@ class RuntimeContext {
56
59
  this._isExecuted = false
57
60
  this.runtimeName = runtimeName
58
61
  this.connectionData = connectionData
62
+ // Generate unique runtime context ID (GUID/UUID)
63
+ this.#runtimeContextId = generateGUID()
59
64
 
60
65
  if (this.connectionData.connectionType === ConnectionType.WEB_SOCKET) {
61
66
  return
@@ -125,6 +130,8 @@ class RuntimeContext {
125
130
  const runtimeCtx = RuntimeContext.memoryRuntimeContexts.get(key)
126
131
  if (!runtimeCtx) throw new Error('Runtime context not found')
127
132
  runtimeCtx.#currentCommand = null
133
+ // Regenerate runtime context ID for each getInstance call (matches .NET behavior)
134
+ runtimeCtx.#runtimeContextId = generateGUID()
128
135
  return runtimeCtx
129
136
  } else {
130
137
  if (!_Transmitter) {
@@ -141,6 +148,8 @@ class RuntimeContext {
141
148
  const runtimeCtx = RuntimeContext.networkRuntimeContexts.get(key1)
142
149
  if (!runtimeCtx) throw new Error('Runtime context not found')
143
150
  runtimeCtx.#currentCommand = null
151
+ // Regenerate runtime context ID for each getInstance call (matches .NET behavior)
152
+ runtimeCtx.#runtimeContextId = generateGUID()
144
153
  return runtimeCtx
145
154
  } else {
146
155
  if (!_Transmitter) {
@@ -158,6 +167,8 @@ class RuntimeContext {
158
167
  const runtimeCtx = RuntimeContext.webSocketRuntimeContexts.get(key2)
159
168
  if (!runtimeCtx) throw new Error('Runtime context not found')
160
169
  runtimeCtx.#currentCommand = null
170
+ // Regenerate runtime context ID for each getInstance call (matches .NET behavior)
171
+ runtimeCtx.#runtimeContextId = generateGUID()
161
172
  return runtimeCtx
162
173
  } else {
163
174
  const runtimeCtx = new RuntimeContext(runtimeName, connectionData)
@@ -170,6 +181,15 @@ class RuntimeContext {
170
181
  }
171
182
  }
172
183
 
184
+ /**
185
+ * Gets the unique runtime context ID for this instance.
186
+ * This ID is used to associate plugins with specific runtime contexts.
187
+ * @returns {string} The runtime context ID (GUID/UUID string)
188
+ */
189
+ get runtimeContextId() {
190
+ return this.#runtimeContextId
191
+ }
192
+
173
193
  /**
174
194
  * Executes the current command. The initial state of RuntimeContext is non-materialized,
175
195
  * wrapping either a single command or a chain of recursively nested commands.
@@ -184,7 +204,8 @@ class RuntimeContext {
184
204
  if (!this.#currentCommand) {
185
205
  throw new Error('currentCommand is undefined in Runtime Context execute method')
186
206
  }
187
- this.#responseCommand = await Interpreter.execute(this.#currentCommand, this.connectionData)
207
+ // Pass the runtime context ID to the interpreter for plugin resolution
208
+ this.#responseCommand = await Interpreter.execute(this.#runtimeContextId, this.#currentCommand, this.connectionData)
188
209
  this.#currentCommand = null
189
210
  if (this.#responseCommand === undefined) {
190
211
  throw new Error('responseCommand is undefined in Runtime Context execute method')
@@ -221,7 +242,7 @@ class RuntimeContext {
221
242
  getType(typeName, ...args) {
222
243
  let localCommand = new Command(this.runtimeName, CommandType.GetType, [typeName, ...args])
223
244
  this.#currentCommand = null
224
- return new InvocationContext(this.runtimeName, this.connectionData, this.#buildCommand(localCommand))
245
+ return new InvocationContext(this.#runtimeContextId, this.runtimeName, this.connectionData, this.#buildCommand(localCommand))
225
246
  }
226
247
 
227
248
  /**
@@ -236,7 +257,7 @@ class RuntimeContext {
236
257
  cast(...args) {
237
258
  let localCommand = new Command(this.runtimeName, CommandType.Cast, args)
238
259
  this.#currentCommand = null
239
- return new InvocationContext(this.runtimeName, this.connectionData, this.#buildCommand(localCommand))
260
+ return new InvocationContext(this.#runtimeContextId, this.runtimeName, this.connectionData, this.#buildCommand(localCommand))
240
261
  }
241
262
 
242
263
  /**
@@ -251,7 +272,7 @@ class RuntimeContext {
251
272
  getEnumItem(...args) {
252
273
  let localCommand = new Command(this.runtimeName, CommandType.GetEnumItem, args)
253
274
  this.#currentCommand = null
254
- return new InvocationContext(this.runtimeName, this.connectionData, this.#buildCommand(localCommand))
275
+ return new InvocationContext(this.#runtimeContextId, this.runtimeName, this.connectionData, this.#buildCommand(localCommand))
255
276
  }
256
277
 
257
278
  /**
@@ -266,7 +287,7 @@ class RuntimeContext {
266
287
  asRef(...args) {
267
288
  let localCommand = new Command(this.runtimeName, CommandType.AsRef, args)
268
289
  this.#currentCommand = null
269
- return new InvocationContext(this.runtimeName, this.connectionData, this.#buildCommand(localCommand))
290
+ return new InvocationContext(this.#runtimeContextId, this.runtimeName, this.connectionData, this.#buildCommand(localCommand))
270
291
  }
271
292
 
272
293
  /**
@@ -281,7 +302,7 @@ class RuntimeContext {
281
302
  asOut(...args) {
282
303
  let localCommand = new Command(this.runtimeName, CommandType.AsOut, args)
283
304
  this.#currentCommand = null
284
- return new InvocationContext(this.runtimeName, this.connectionData, this.#buildCommand(localCommand))
305
+ return new InvocationContext(this.#runtimeContextId, this.runtimeName, this.connectionData, this.#buildCommand(localCommand))
285
306
  }
286
307
 
287
308
  /**
@@ -294,7 +315,7 @@ class RuntimeContext {
294
315
  getGlobalField(fieldName) {
295
316
  let localCommand = new Command(this.runtimeName, CommandType.GetGlobalField, [fieldName])
296
317
  this.#currentCommand = null
297
- return new InvocationContext(this.runtimeName, this.connectionData, this.#buildCommand(localCommand))
318
+ return new InvocationContext(this.#runtimeContextId, this.runtimeName, this.connectionData, this.#buildCommand(localCommand))
298
319
  }
299
320
 
300
321
  /**
@@ -313,14 +334,17 @@ class RuntimeContext {
313
334
  ...args,
314
335
  ])
315
336
  this.#currentCommand = null
316
- return new InvocationContext(this.runtimeName, this.connectionData, this.#buildCommand(localCommand))
337
+ return new InvocationContext(this.#runtimeContextId, this.runtimeName, this.connectionData, this.#buildCommand(localCommand))
317
338
  }
318
339
 
319
340
  /**
320
341
  * @param {Command} command
321
- * @returns {Command}
342
+ * @returns {Command | null}
322
343
  */
323
344
  #buildCommand(command) {
345
+ if (!command) {
346
+ return this.#currentCommand
347
+ }
324
348
  for (let i = 0; i < command.payload.length; i++) {
325
349
  command.payload[i] = this.#encapsulatePayloadItem(command.payload[i])
326
350
  }
@@ -20,40 +20,43 @@ class ConfigResolver {
20
20
  }
21
21
 
22
22
  /**
23
- * Build appropriate connection data object from a host string.
24
- * - empty / null => InMemoryConnectionData
25
- * - "inmemory" or "in-memory" => InMemoryConnectionData
26
- * - ws:// or wss:// => WsConnectionData(fullAddress)
27
- * - tcp://... => parsed by parseTcp
28
- * - host:port or host:port/... => TcpConnectionData(host, port) if valid, otherwise InMemoryConnectionData
23
+ * Builds connection data from the host string from config (JSON, YAML, or connection string).
24
+ * Recognized formats: empty (in-memory), "inmemory"/"in-memory", ws://..., wss://..., tcp://host:port, host:port.
25
+ * When the value is missing, invalid, or unparseable, falls back to InMemoryConnectionData and logs the reason.
29
26
  * @param {string} hostValue
30
27
  * @returns {InMemoryConnectionData|WsConnectionData|TcpConnectionData}
31
28
  */
32
29
  static buildConnectionData(hostValue) {
30
+ // No host specified: use in-memory (local) connection; no remote server.
33
31
  if (!hostValue || String(hostValue).trim() === '') {
32
+ console.warn('Host not specified; using in-memory.')
34
33
  return new InMemoryConnectionData()
35
34
  }
36
35
 
37
36
  const hv = String(hostValue).trim()
38
37
  const lower = hv.toLowerCase()
39
38
 
39
+ // Explicit in-memory choice from config; no console message (intended by user).
40
40
  if (lower === 'inmemory' || lower === 'in-memory') {
41
41
  return new InMemoryConnectionData()
42
42
  }
43
43
 
44
+ // WebSocket URL: use as-is for WsConnectionData.
44
45
  if (lower.startsWith('ws://') || lower.startsWith('wss://')) {
45
46
  return new WsConnectionData(hv)
46
47
  }
47
48
 
49
+ // tcp://host:port — parse and create TcpConnectionData; on parse failure, fall back to in-memory.
48
50
  if (lower.startsWith('tcp://')) {
49
51
  try {
50
- // slice off 'tcp://'
51
52
  return ConfigResolver.parseTcp(hv.slice(6))
52
53
  } catch (e) {
54
+ console.warn("Invalid or unparseable tcp:// address '" + hv + "'; falling back to in-memory.")
53
55
  return new InMemoryConnectionData()
54
56
  }
55
57
  }
56
58
 
59
+ // host:port (or host:port/path) — try to create TcpConnectionData; on failure, fall back to in-memory.
57
60
  const colon = hv.indexOf(':')
58
61
  if (colon > 0 && colon < hv.length - 1) {
59
62
  let portPart = hv.substring(colon + 1)
@@ -71,14 +74,17 @@ class ConfigResolver {
71
74
  try {
72
75
  return new TcpConnectionData(hostOnly, port)
73
76
  } catch (e) {
77
+ console.warn("Could not create TCP connection for '" + hv + "'; falling back to in-memory.")
74
78
  return new InMemoryConnectionData()
75
79
  }
76
80
  }
77
81
  } catch (e) {
78
- // fall through to in-memory
82
+ // Port not a valid number; fall through to final fallback below.
79
83
  }
80
84
  }
81
85
 
86
+ // Unrecognized format: expected empty, "inmemory", "in-memory", ws://..., wss://..., tcp://host:port, or host:port.
87
+ console.warn("Host value '" + hv + "' is not a recognized connection format (expected: empty, 'inmemory', 'in-memory', 'ws://...', 'wss://...', 'tcp://host:port', or 'host:port'). Falling back to in-memory.")
82
88
  return new InMemoryConnectionData()
83
89
  }
84
90
 
@@ -0,0 +1,90 @@
1
+ // @ts-check
2
+
3
+ import { Command } from '../../utils/Command.js'
4
+ import { CommandType } from '../../utils/CommandType.js'
5
+
6
+ export class DtoHelper {
7
+ /**
8
+ * Attempts to retrieve a DTO field value by field name from a command chain.
9
+ *
10
+ * @param {Command | null} command - The command to search through.
11
+ * @param {string} fieldName - The name of the field to retrieve.
12
+ * @returns {any}
13
+ */
14
+ static tryGetDtoFieldValue(command, fieldName) {
15
+ let value = null
16
+ let currentCommand = command
17
+
18
+ while (currentCommand !== undefined && currentCommand !== null) {
19
+ if (
20
+ currentCommand.commandType === CommandType.SetInstanceField &&
21
+ DtoHelper._tryGetDtoFieldName(currentCommand) &&
22
+ DtoHelper._getDtoFieldName(currentCommand) === fieldName
23
+ ) {
24
+ value = DtoHelper._tryGetDtoFieldValueFromCommand(currentCommand)
25
+ return value
26
+ }
27
+
28
+ if (!currentCommand.payload || currentCommand.payload.length === 0) {
29
+ break
30
+ }
31
+
32
+ const firstPayload = currentCommand.payload[0]
33
+ currentCommand = (firstPayload instanceof Command) ? firstPayload : null
34
+ }
35
+
36
+ return null
37
+ }
38
+
39
+ /**
40
+ * Attempts to extract the field name from a SetInstanceField command.
41
+ * @param {Command} command
42
+ * @returns {boolean}
43
+ */
44
+ static _tryGetDtoFieldName(command) {
45
+ if (command.payload.length < 2) {
46
+ return false
47
+ }
48
+ const nameValue = DtoHelper._tryGetPayloadValue(command.payload[1])
49
+ return typeof nameValue === 'string'
50
+ }
51
+
52
+ /**
53
+ * Extracts the field name from a SetInstanceField command.
54
+ * @param {Command} command
55
+ * @returns {string}
56
+ */
57
+ static _getDtoFieldName(command) {
58
+ if (command.payload.length < 2) {
59
+ return ''
60
+ }
61
+ const nameValue = DtoHelper._tryGetPayloadValue(command.payload[1])
62
+ return (typeof nameValue === 'string') ? nameValue : ""
63
+ }
64
+
65
+ /**
66
+ * Attempts to extract the field value from a SetInstanceField command.
67
+ * @param {Command} command
68
+ * @returns {any}
69
+ */
70
+ static _tryGetDtoFieldValueFromCommand(command) {
71
+ if (command.payload.length < 3) {
72
+ return null
73
+ }
74
+ return DtoHelper._tryGetPayloadValue(command.payload[2])
75
+ }
76
+
77
+ /**
78
+ * Attempts to extract a value from a payload item, handling Value command types.
79
+ * @param {any} payloadItem
80
+ * @returns {any}
81
+ */
82
+ static _tryGetPayloadValue(payloadItem) {
83
+ if ((payloadItem instanceof Command) &&
84
+ payloadItem.commandType === CommandType.Value &&
85
+ payloadItem.payload.length > 0) {
86
+ return payloadItem.payload[0]
87
+ }
88
+ return payloadItem
89
+ }
90
+ }
@@ -69,4 +69,51 @@ export class Command {
69
69
  return new Command(this.runtimeName, this.commandType, [current_command].concat(this.payload))
70
70
  }
71
71
  }
72
+
73
+ /**
74
+ * Returns string representation of the Command instance for debugging purposes.
75
+ * @returns {string}
76
+ */
77
+ toString() {
78
+ try {
79
+ let result = ' { '
80
+ result += `RuntimeName: ${this.runtimeName}, `
81
+ result += `CommandType: ${this.commandType}, `
82
+ result += 'Payload: \n'
83
+ result += ' ['
84
+
85
+ /**
86
+ * @type {string | any[]}
87
+ */
88
+ const payload = this.payload || []
89
+ const len = payload.length
90
+
91
+ if (len > 0) {
92
+ result += '\n'
93
+ for (let i = 0; i < len; i++) {
94
+ const item = payload[i]
95
+ let itemStr
96
+ if (item === null || item === undefined) {
97
+ itemStr = 'null'
98
+ } else if (typeof item === 'string') {
99
+ itemStr = ` "${item}"`
100
+ } else if (item instanceof Command) {
101
+ itemStr = item.toString().replace(/\n/g, '\n ')
102
+ } else {
103
+ itemStr = ' ' + String(item)
104
+ }
105
+ result += ' ' + itemStr
106
+ result += i < len - 1 ? ',\n' : '\n'
107
+ }
108
+ result += ' ]\n'
109
+ } else {
110
+ result += ' ]\n'
111
+ }
112
+
113
+ result += ' }'
114
+ return result
115
+ } catch (ex) {
116
+ return 'Error while converting command to string:' + (ex);
117
+ }
118
+ }
72
119
  }
@@ -50,4 +50,7 @@ export const CommandType = /** @type {const} */ ({
50
50
  GetGlobalField: 47,
51
51
  RegisterForUpdate: 48,
52
52
  ValueForUpdate: 49,
53
+ AsDto: 50,
54
+ DtoProperty: 51,
55
+ ProjectResultAsDto: 52,
53
56
  })
package/lib/utils/Type.js CHANGED
@@ -12,5 +12,6 @@ export const Type = /** @type {const} */ ({
12
12
  JAVONET_UNSIGNED_LONG_LONG: 9,
13
13
  JAVONET_UNSIGNED_INTEGER: 10,
14
14
  JAVONET_NULL: 11,
15
- JAVONET_VOID: 12,
15
+ JAVONET_UNDEFINED: 12,
16
+ JAVONET_VOID: 13,
16
17
  })