javonet-nodejs-sdk 2.6.7 → 2.6.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.
Files changed (51) hide show
  1. package/dist/core/handler/CreateClassInstanceHandler.cjs +35 -1
  2. package/dist/core/handler/Handler.cjs +28 -9
  3. package/dist/core/handler/LoadLibraryHandler.cjs +15 -9
  4. package/dist/core/handler/PassDelegateHandler.cjs +2 -2
  5. package/dist/core/interpreter/Interpreter.cjs +11 -33
  6. package/dist/core/protocol/CommandSerializer.cjs +3 -8
  7. package/dist/core/receiver/Receiver.cjs +6 -6
  8. package/dist/sdk/Activator.cjs +44 -0
  9. package/dist/sdk/ActivatorDetails.cjs +13 -3
  10. package/dist/sdk/ConfigRuntimeFactory.cjs +9 -0
  11. package/dist/sdk/InvocationContext.cjs +112 -66
  12. package/dist/sdk/Javonet.cjs +3 -0
  13. package/dist/sdk/RuntimeContext.cjs +8 -8
  14. package/dist/sdk/configuration/ConfigSourceResolver.cjs +43 -21
  15. package/dist/sdk/tools/ComplexTypeResolver.cjs +84 -34
  16. package/dist/types/core/handler/CreateClassInstanceHandler.d.ts +8 -0
  17. package/dist/types/core/handler/Handler.d.ts +2 -1
  18. package/dist/types/core/handler/PassDelegateHandler.d.ts +2 -2
  19. package/dist/types/core/interpreter/Interpreter.d.ts +2 -2
  20. package/dist/types/core/protocol/CommandSerializer.d.ts +3 -3
  21. package/dist/types/core/receiver/Receiver.d.ts +4 -4
  22. package/dist/types/sdk/Activator.d.ts +12 -0
  23. package/dist/types/sdk/ActivatorDetails.d.ts +5 -3
  24. package/dist/types/sdk/ConfigRuntimeFactory.d.ts +7 -0
  25. package/dist/types/sdk/InvocationContext.d.ts +19 -17
  26. package/dist/types/sdk/Javonet.d.ts +2 -1
  27. package/dist/types/sdk/RuntimeContext.d.ts +4 -3
  28. package/dist/types/sdk/configuration/ConfigSourceResolver.d.ts +21 -4
  29. package/dist/types/sdk/tools/ComplexTypeResolver.d.ts +21 -5
  30. package/dist/types/utils/CommandType.d.ts +1 -0
  31. package/dist/types/utils/Primitives.d.ts +5 -0
  32. package/dist/utils/CommandType.cjs +2 -1
  33. package/dist/utils/Primitives.cjs +201 -0
  34. package/lib/core/handler/CreateClassInstanceHandler.js +46 -1
  35. package/lib/core/handler/Handler.js +34 -9
  36. package/lib/core/handler/LoadLibraryHandler.js +28 -14
  37. package/lib/core/handler/PassDelegateHandler.js +2 -2
  38. package/lib/core/interpreter/Interpreter.js +11 -35
  39. package/lib/core/protocol/CommandSerializer.js +5 -10
  40. package/lib/core/receiver/Receiver.js +6 -6
  41. package/lib/sdk/Activator.js +22 -0
  42. package/lib/sdk/ActivatorDetails.js +18 -3
  43. package/lib/sdk/ConfigRuntimeFactory.js +10 -0
  44. package/lib/sdk/InvocationContext.js +133 -74
  45. package/lib/sdk/Javonet.js +2 -0
  46. package/lib/sdk/RuntimeContext.js +8 -8
  47. package/lib/sdk/configuration/ConfigSourceResolver.js +47 -14
  48. package/lib/sdk/tools/ComplexTypeResolver.js +104 -42
  49. package/lib/utils/CommandType.js +1 -0
  50. package/lib/utils/Primitives.js +193 -0
  51. package/package.json +4 -3
@@ -13,6 +13,7 @@ let _existsSync = null
13
13
  let _resolve = null
14
14
 
15
15
  const requireDynamic = getRequire(import.meta.url)
16
+
16
17
  class LoadLibraryHandler extends AbstractHandler {
17
18
  requiredParametersCount = 1
18
19
  /** @type {string[]} */
@@ -29,9 +30,9 @@ class LoadLibraryHandler extends AbstractHandler {
29
30
  if (command.payload.length < this.requiredParametersCount) {
30
31
  throw new Error('Load Library parameters mismatch')
31
32
  }
32
- let { payload } = command
33
- let [lib] = payload
34
33
 
34
+ const { payload } = command
35
+ const [lib] = payload
35
36
 
36
37
  if (!lib) {
37
38
  throw new Error('Cannot load module: Library path is required but was not provided')
@@ -46,33 +47,46 @@ class LoadLibraryHandler extends AbstractHandler {
46
47
  _resolve = resolve
47
48
  }
48
49
 
49
- const absolutePath = _resolve?.(lib)
50
+ // Resolve and pick an existing path (relative or absolute)
51
+ const absolutePath = _resolve ? _resolve(lib) : lib
52
+ const candidatePaths = [lib, absolutePath]
53
+ const existingPath = candidatePaths.find(p => _existsSync && _existsSync(p))
50
54
 
51
- if (!_existsSync?.(lib ?? '') && !_existsSync?.(absolutePath ?? '')) {
52
- throw new Error(`Cannot load module: Library not found: ${lib}`)
53
- }
55
+ if (!existingPath) {
56
+ throw new Error(`Cannot load module: Library not found: ${lib}`)
57
+ }
54
58
 
59
+ // Normalize to absolute path for deduplication
60
+ const normalizedPath = _resolve ? _resolve(existingPath) : existingPath
55
61
 
56
- let pathArray = lib.split(/[/\\]/)
57
- let libraryName = pathArray.length > 1 ? pathArray[pathArray.length - 1] : pathArray[0]
58
- libraryName = libraryName.replace('.js', '')
62
+ // Load only once per normalized absolute path
63
+ if (LoadLibraryHandler.loadedLibraries.includes(normalizedPath)) {
64
+ return 0
65
+ }
59
66
 
60
- let moduleExports
67
+ // Derive global name from file name (using normalized path)
68
+ const pathArray = normalizedPath.split(/[/\\]/)
69
+ let libraryName =
70
+ pathArray.length > 1 ? pathArray[pathArray.length - 1] : pathArray[0]
71
+ libraryName = libraryName.replace(/\.js$/i, '')
61
72
 
73
+ let moduleExports
62
74
  try {
63
- moduleExports = requireDynamic(lib)
64
- LoadLibraryHandler.loadedLibraries.push(lib)
75
+ // Use the normalized path when requiring to keep behavior consistent
76
+ moduleExports = requireDynamic(normalizedPath)
77
+ LoadLibraryHandler.loadedLibraries.push(normalizedPath)
65
78
  } catch (error) {
66
- throw Error('Cannot load module: ' + lib + '\n' + error)
79
+ throw new Error('Cannot load module: ' + normalizedPath + '\n' + error)
67
80
  }
81
+
68
82
  // @ts-expect-error
69
83
  global[libraryName] = moduleExports
70
84
 
71
85
  for (const [key, value] of Object.entries(moduleExports)) {
72
- // Here, `key` is the name of the export, and `value` is the exported type itself.
73
86
  // @ts-expect-error
74
87
  global[key] = value
75
88
  }
89
+
76
90
  return 0
77
91
  }
78
92
 
@@ -113,9 +113,9 @@ class PassDelegateHandler extends AbstractHandler {
113
113
  /**
114
114
  * Creates a method call to execute the command.
115
115
  * @param {Command} command - The command object.
116
- * @returns {any} The response object.
116
+ * @returns {Promise<any>} The response object.
117
117
  */
118
- createExecuteCall(command) {
118
+ async createExecuteCall(command) {
119
119
  return this.interpreter?.execute(command, new InMemoryConnectionData())
120
120
  }
121
121
 
@@ -21,7 +21,6 @@ let _Transmitter
21
21
  /** @type {typeof import('../transmitter/TransmitterWebsocket.js').TransmitterWebsocket | typeof import('../transmitter/TransmitterWebsocketBrowser.js').TransmitterWebsocketBrowser} */
22
22
  let _TransmitterWebsocket = isNodejsRuntime() ? TransmitterWebsocket : TransmitterWebsocketBrowser
23
23
 
24
-
25
24
  const requireDynamic = getRequire(import.meta.url)
26
25
 
27
26
  export class Interpreter {
@@ -40,21 +39,23 @@ export class Interpreter {
40
39
  *
41
40
  * @param {Command} command
42
41
  * @param {IConnectionData} connectionData
43
- * @returns {Command | Promise<Command>}
42
+ * @returns {Promise<Command>}
44
43
  */
45
- execute(command, connectionData) {
44
+ async execute(command, connectionData) {
46
45
  try {
47
46
  let messageByteArray = new CommandSerializer().serialize(command, connectionData)
48
47
 
49
- if (!(messageByteArray instanceof Uint8Array) && !(messageByteArray instanceof Promise)) {
50
- throw new Error('Serialized message is neither Uint8Array nor Promise<Uint8Array>')
48
+ if (!(messageByteArray instanceof Uint8Array)) {
49
+ throw new Error('Serialized message is not Uint8Array')
51
50
  }
52
51
 
53
52
  /** @type {Promise<Uint8Array> | Uint8Array | undefined} */
54
53
  let responseByteArray = undefined
55
54
 
56
55
  if (!isNodejsRuntime() && connectionData.connectionType === ConnectionType.IN_MEMORY) {
57
- throw new Error('Nodejs Core Error: inMemory is only allowed in Nodejs runtime, not in browser')
56
+ throw new Error(
57
+ 'Nodejs Core Error: inMemory is only allowed in Nodejs runtime, not in browser'
58
+ )
58
59
  }
59
60
 
60
61
  if (
@@ -68,13 +69,7 @@ export class Interpreter {
68
69
  if (!_Receiver) {
69
70
  throw new Error('Nodejs Core Error: Receiver is undefined')
70
71
  }
71
- if (messageByteArray instanceof Promise) {
72
- responseByteArray = messageByteArray.then((resolvedMessage) => {
73
- return _Receiver.sendCommand(resolvedMessage)
74
- })
75
- } else if (messageByteArray instanceof Uint8Array) {
76
- responseByteArray = _Receiver.sendCommand(messageByteArray)
77
- }
72
+ responseByteArray = await _Receiver.sendCommand(messageByteArray)
78
73
  } else if (
79
74
  connectionData.connectionType === ConnectionType.IN_MEMORY ||
80
75
  connectionData.connectionType === ConnectionType.TCP
@@ -86,34 +81,15 @@ export class Interpreter {
86
81
  if (!_Transmitter) {
87
82
  throw new Error('Nodejs Core Error: Transmitter is undefined')
88
83
  }
89
- if (messageByteArray instanceof Uint8Array) {
90
- responseByteArray = _Transmitter.sendCommand(messageByteArray)
91
- } else {
92
- responseByteArray = messageByteArray.then((resolvedMessage) => {
93
- return _Transmitter.sendCommand(resolvedMessage)
94
- })
95
- }
84
+ responseByteArray = await _Transmitter.sendCommand(messageByteArray)
96
85
  } else if (connectionData.connectionType === ConnectionType.WEB_SOCKET) {
97
- if (messageByteArray instanceof Uint8Array) {
98
- responseByteArray = _TransmitterWebsocket.sendCommand(messageByteArray, connectionData)
99
- } else {
100
- responseByteArray = messageByteArray.then((resolvedMessage) => {
101
- return _TransmitterWebsocket.sendCommand(resolvedMessage, connectionData)
102
- })
103
- }
86
+ responseByteArray = await _TransmitterWebsocket.sendCommand(messageByteArray, connectionData)
104
87
  }
105
88
 
106
89
  if (!responseByteArray) {
107
90
  throw new Error('No response received from Transmitter')
108
91
  }
109
-
110
- if (responseByteArray instanceof Promise) {
111
- return responseByteArray.then((resolvedResponse) => {
112
- return new CommandDeserializer(resolvedResponse).deserialize()
113
- })
114
- } else {
115
- return new CommandDeserializer(responseByteArray).deserialize()
116
- }
92
+ return new CommandDeserializer(responseByteArray).deserialize()
117
93
  } catch (error) {
118
94
  throw error
119
95
  }
@@ -1,3 +1,4 @@
1
+ // @ts-check
1
2
  import { TypeSerializer } from './TypeSerializer.js'
2
3
  import { ReferencesCache } from '../referenceCache/ReferencesCache.js'
3
4
  import { Command } from '../../utils/Command.js'
@@ -11,26 +12,20 @@ import { TypesHandler } from '../../utils/TypesHandler.js'
11
12
  class CommandSerializer {
12
13
  /**
13
14
  * Serializes the root command with connection data and optional runtime version.
14
- * @param {Promise<Command> | Command} rootCommand
15
+ * @param {Command} rootCommand
15
16
  * @param {IConnectionData} connectionData
16
17
  * @param {number} runtimeVersion
17
- * @returns {Promise<Uint8Array> | Uint8Array}
18
+ * @returns {Uint8Array}
18
19
  */
19
20
  serialize(rootCommand, connectionData, runtimeVersion = 0) {
21
+ /** @type {Array<Uint8Array>} */
20
22
  const buffers = []
21
23
 
22
- // Write runtime name and version
23
- if (rootCommand instanceof Promise) {
24
- return rootCommand.then(resolvedCommand => {
25
- return this.serialize(resolvedCommand, connectionData, runtimeVersion)
26
- })
27
- }
28
-
29
24
  buffers.push(Uint8Array.of(rootCommand.runtimeName, runtimeVersion))
30
25
 
31
26
  // Write connection data or default zeros
32
27
  if (connectionData) {
33
- buffers.push(connectionData.serializeConnectionData())
28
+ buffers.push(Uint8Array.from(connectionData.serializeConnectionData()))
34
29
  } else {
35
30
  buffers.push(Uint8Array.of(0, 0, 0, 0, 0, 0, 0))
36
31
  }
@@ -19,11 +19,11 @@ export class Receiver {
19
19
 
20
20
  /**
21
21
  * @param {Uint8Array} messageByteArray
22
- * @returns {Promise<Uint8Array> | Uint8Array}
22
+ * @returns {Promise<Uint8Array>}
23
23
  */
24
- static sendCommand(messageByteArray) {
24
+ static async sendCommand(messageByteArray) {
25
25
  try {
26
- let command = new Interpreter().process(messageByteArray)
26
+ let command = await new Interpreter().process(messageByteArray)
27
27
  return new CommandSerializer().serialize(command, this.connectionData)
28
28
  } catch (error) {
29
29
  const exceptionCommand = ExceptionSerializer.serializeException(
@@ -36,12 +36,12 @@ export class Receiver {
36
36
 
37
37
  /**
38
38
  * @param {Uint8Array} messageByteArray
39
- * @returns {Promise<Uint8Array> | Uint8Array}
39
+ * @returns {Promise<Uint8Array>}
40
40
  */
41
- static heartBeat(messageByteArray) {
41
+ static async heartBeat(messageByteArray) {
42
42
  let response = new Uint8Array(2)
43
43
  response[0] = messageByteArray[11]
44
44
  response[1] = messageByteArray[12] - 2
45
- return response
45
+ return Promise.resolve(response)
46
46
  }
47
47
  }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Details for activating a type with constructor arguments
3
+ */
4
+ class Activator {
5
+ /**
6
+ * Create a new instance of a type
7
+ * @param {Function} Type - The constructor function/class
8
+ * @param {any[] | any} args - The arguments to pass to the constructor
9
+ * @returns {any} The new instance
10
+ */
11
+ static createInstance(Type, args) {
12
+ if (args == null) {
13
+ args = []
14
+ }
15
+ if (!Array.isArray(args)) {
16
+ args = [args]
17
+ }
18
+ return new Type(...args)
19
+ }
20
+ }
21
+
22
+ export { Activator }
@@ -2,13 +2,28 @@
2
2
  * Details for activating a type with constructor arguments
3
3
  */
4
4
  class ActivatorDetails {
5
+ /** @type {Function} */
6
+ Type
7
+
8
+ /** @type {any[]} */
9
+ arguments
10
+
5
11
  /**
6
12
  * @param {Function} type - The constructor function/class
7
- * @param {any[]} [args] - Arguments to pass to constructor
13
+ * @param {any[]|any} [args] - Arguments to pass to constructor (array or single value)
8
14
  */
9
15
  constructor(type, args = []) {
10
- this.type = type
11
- this.arguments = args
16
+ this.Type = type
17
+ // Normalize args: ensure it's always an array
18
+ // Handles: undefined, null, single value, or array
19
+ if (args == null) {
20
+ this.arguments = []
21
+ } else if (Array.isArray(args)) {
22
+ this.arguments = args
23
+ } else {
24
+ // Single value - wrap in array
25
+ this.arguments = [args]
26
+ }
12
27
  }
13
28
  }
14
29
 
@@ -102,6 +102,16 @@ export class ConfigRuntimeFactory {
102
102
  return this.#getRuntimeContext(RuntimeName.Nodejs, configName)
103
103
  }
104
104
 
105
+ /**
106
+ * Creates RuntimeContext instance to interact with the Php runtime.
107
+ * @param {string} [configName="default"] - The name of the configuration to use (optional).
108
+ * @return {RuntimeContext} a RuntimeContext instance for the Php runtime
109
+ * @see [Javonet Guides](https://www.javonet.com/guides/v2/javascript/foundations/runtime-context)
110
+ */
111
+ php(configName = 'default') {
112
+ return this.#getRuntimeContext(RuntimeName.Php, configName)
113
+ }
114
+
105
115
  /**
106
116
  * Creates RuntimeContext instance to interact with the Python 2.7 runtime.
107
117
  * @param {string} [configName="default"] - The name of the configuration to use (optional).
@@ -3,10 +3,10 @@ import { delegatesCacheInstance } from '../core/delegatesCache/DelegatesCache.js
3
3
  import { Interpreter } from '../core/interpreter/Interpreter.js'
4
4
  import { Command } from '../utils/Command.js'
5
5
  import { CommandType } from '../utils/CommandType.js'
6
- import { ConnectionType } from '../utils/ConnectionType.js'
7
6
  import { ExceptionThrower } from '../utils/exception/ExceptionThrower.js'
8
7
  import { RuntimeName } from '../utils/RuntimeName.js'
9
8
  import { TypesHandler } from '../utils/TypesHandler.js'
9
+ import { v4 as uuidv4 } from 'uuid' // add lightweight uuid generation
10
10
 
11
11
  /**
12
12
  * @typedef {import('../types.d.ts').RuntimeName} RuntimeNameType
@@ -27,12 +27,17 @@ class InvocationContext {
27
27
  #connectionData
28
28
  /** @type {Command | null} */
29
29
  #currentCommand = null
30
- /** @type {Command | Promise<Command> | null} */
30
+ /** @type {Command | null} */
31
31
  #responseCommand = null
32
32
  /** @type {boolean} */
33
33
  #isExecuted = false
34
34
  /** @type {Interpreter | null} */
35
35
  #interpreter = null
36
+ /** @type {string | null} */
37
+ #guid = null
38
+
39
+ // Static map holding contexts waiting for materialization (guid -> InvocationContext)
40
+ static _invocationContexts = new Map()
36
41
 
37
42
  /**
38
43
  *
@@ -48,6 +53,15 @@ class InvocationContext {
48
53
  this.#responseCommand = null
49
54
  this.#isExecuted = isExecuted
50
55
  this.#interpreter = null
56
+ this.#guid = uuidv4()
57
+ }
58
+
59
+ /**
60
+ * @returns {string} guid of this InvocationContext
61
+ */
62
+ getGuid() {
63
+ // @ts-ignore
64
+ return this.#guid
51
65
  }
52
66
 
53
67
  /**
@@ -79,43 +93,31 @@ class InvocationContext {
79
93
  // this.execute();
80
94
  // }
81
95
  //}
82
- [Symbol.iterator] = () => {
96
+ /**
97
+ * Async iterator for InvocationContext arrays
98
+ * Use: for await (const item of invocationContext) { ... }
99
+ * @returns {AsyncGenerator<InvocationContext, void, unknown>}
100
+ */
101
+ async *[Symbol.asyncIterator]() {
83
102
  if (this.#currentCommand?.commandType !== CommandType.Reference) {
84
103
  throw new Error('Object is not iterable')
85
104
  }
86
- let position = -1
87
- /** @type {number} */
88
- let arraySize = 0
89
- /** @type {InvocationContext | Promise<InvocationContext>} */
90
- const sizeCtx = /** @type {any} */ (this.getSize().execute())
91
- if (sizeCtx instanceof Promise) {
92
- sizeCtx.then((ctx) => {
93
- arraySize = Number(ctx.getValue())
94
- })
95
- } else {
96
- arraySize = Number(sizeCtx.getValue())
97
- }
98
105
 
99
- return {
100
- next: () => ({
101
- value: this.getIndex(++position),
102
- done: position >= arraySize,
103
- }),
106
+ const sizeCtx = await this.getSize().execute()
107
+ const arraySize = Number(sizeCtx.getValue())
108
+
109
+ for (let position = 0; position < arraySize; position++) {
110
+ yield this.getIndex(position)
104
111
  }
105
112
  }
106
113
 
107
114
  /**
108
115
  * Executes the current command.
109
- * Because invocation context is building the intent of executing particular expression on target environment, we call the initial state of invocation context as non-materialized.
110
- * The non-materialized context wraps either single command or chain of recursively nested commands.
111
- * Commands are becoming nested through each invocation of methods on Invocation Context.
112
- * Each invocation triggers the creation of new Invocation Context instance wrapping the current command with new parent command valid for invoked method.
113
- * Developer can decide on any moment of the materialization for the context taking full control of the chunks of the expression being transferred and processed on target runtime.
114
- * @returns {Promise<InvocationContext> | InvocationContext} the InvocationContext after executing the command.
115
- * @see [Javonet Guides](https://www.javonet.com/guides/v2/javascript/foundations/execute-method)
116
+ * Returns the InvocationContext after executing the command.
117
+ * @returns {Promise<InvocationContext>}
116
118
  * @method
117
119
  */
118
- execute() {
120
+ async execute() {
119
121
  if (this.#currentCommand === null) {
120
122
  throw new Error('currentCommand is undefined in Invocation Context execute method')
121
123
  }
@@ -124,28 +126,25 @@ class InvocationContext {
124
126
  this.#interpreter = new Interpreter()
125
127
  }
126
128
 
127
- this.#responseCommand = this.#interpreter.execute(this.#currentCommand, this.#connectionData)
129
+ // Execute command on interpreter
130
+ this.#responseCommand = await this.#interpreter.execute(this.#currentCommand, this.#connectionData)
128
131
 
129
- const handleResponse = (/** @type {Command} */ resolvedResponse) => {
130
- if (!resolvedResponse) {
131
- throw new Error('responseCommand is undefined in Invocation Context execute method')
132
- }
133
- if (resolvedResponse.commandType === CommandType.Exception) {
134
- throw ExceptionThrower.throwException(resolvedResponse)
135
- }
136
- if (resolvedResponse.commandType === CommandType.CreateClassInstance) {
137
- this.#currentCommand = resolvedResponse
138
- this.#isExecuted = true
139
- return this
140
- }
141
- return new InvocationContext(this.#runtimeName, this.#connectionData, resolvedResponse, true)
132
+ if (!this.#responseCommand) {
133
+ throw new Error('responseCommand is undefined in Invocation Context execute method')
134
+ }
135
+ if (this.#responseCommand.commandType === CommandType.Exception) {
136
+ throw ExceptionThrower.throwException(this.#responseCommand)
142
137
  }
143
138
 
144
- if (this.#responseCommand instanceof Promise) {
145
- return this.#responseCommand.then(handleResponse)
146
- } else {
147
- return handleResponse(this.#responseCommand)
139
+ // Process UpdateInvocationContext commands (if any) coming from runtime
140
+ this.#responseCommand = this.#processUpdateInvocationContextCommands(this.#responseCommand)
141
+
142
+ if (this.#responseCommand.commandType === CommandType.CreateClassInstance) {
143
+ this.#currentCommand = this.#responseCommand
144
+ this.#isExecuted = true
145
+ return this
148
146
  }
147
+ return new InvocationContext(this.#runtimeName, this.#connectionData, this.#responseCommand, true)
149
148
  }
150
149
 
151
150
  /**
@@ -191,14 +190,30 @@ class InvocationContext {
191
190
 
192
191
  /**
193
192
  * Creates a new instance of a class in the target runtime.
193
+ * Adds the newly created context to the static invocation contexts map and
194
+ * includes the context GUID as the first argument of CreateClassInstance command payload.
194
195
  * @param {...any} args - The arguments to pass to the class constructor
195
196
  * @returns {InvocationContext} A new InvocationContext instance that wraps the command to create the instance.
196
- * @see [Javonet Guides](https://www.javonet.com/guides/v2/javascript/calling-methods/creating-instance-and-calling-instance-methods)
197
197
  * @method
198
198
  */
199
199
  createInstance(...args) {
200
- let localCommand = new Command(this.#runtimeName, CommandType.CreateClassInstance, args)
201
- return this.#createInstanceContext(localCommand)
200
+ const dummyCommand = new Command(this.#runtimeName, CommandType.CreateClassInstance, [
201
+ ...args,
202
+ ])
203
+ const createInstanceContext = new InvocationContext(this.#runtimeName, this.#connectionData, dummyCommand)
204
+ // include guid as first payload element followed by constructor args
205
+ let localCommand = new Command(this.#runtimeName, CommandType.CreateClassInstance, [
206
+ createInstanceContext.getGuid(),
207
+ ...args,
208
+ ])
209
+
210
+ // set current command for the new context
211
+ createInstanceContext.#currentCommand = this.#buildCommand(localCommand)
212
+
213
+ // register context for materialization
214
+ InvocationContext._invocationContexts.set(createInstanceContext.getGuid(), createInstanceContext)
215
+
216
+ return createInstanceContext
202
217
  }
203
218
 
204
219
  /**
@@ -402,29 +417,19 @@ class InvocationContext {
402
417
 
403
418
  /**
404
419
  * Retrieves the type of the object from the target runtime.
405
- * @returns {Promise<string> | string} The type of the object.
420
+ * @returns {Promise<string>} The type of the object.
406
421
  * @see [Javonet Guides](https://www.javonet.com/guides/v2/javascript/type-handling/getting-object-type)
407
422
  * @method
408
423
  */
409
- getResultType() {
424
+ async getResultType() {
410
425
  const localCommand = new Command(this.#runtimeName, CommandType.GetResultType, [])
411
426
  const invocationContext = new InvocationContext(
412
427
  this.#runtimeName,
413
428
  this.#connectionData,
414
429
  this.#buildCommand(localCommand)
415
430
  )
416
- /** @type {InvocationContext | Promise<InvocationContext>} */
417
- const execCtx = invocationContext.execute()
418
-
419
- /**
420
- * @param {InvocationContext} invCtx
421
- * @returns {string}
422
- */
423
- const extract = (invCtx) => String(invCtx.getValue())
424
-
425
- console.log(execCtx)
426
-
427
- return execCtx instanceof Promise ? execCtx.then(extract) : extract(execCtx)
431
+ const execCtx = await invocationContext.execute()
432
+ return String(execCtx.getValue())
428
433
  }
429
434
 
430
435
  /**
@@ -439,10 +444,11 @@ class InvocationContext {
439
444
 
440
445
  /**
441
446
  * Retrieves an array from the target runtime.
447
+ * @async
442
448
  * @returns {Promise<any[]>}
443
449
  * @method
444
450
  */
445
- retrieveArray() {
451
+ async retrieveArray() {
446
452
  const localCommand = new Command(this.#runtimeName, CommandType.RetrieveArray, [])
447
453
  const localInvCtx = new InvocationContext(
448
454
  this.#runtimeName,
@@ -450,16 +456,27 @@ class InvocationContext {
450
456
  this.#buildCommand(localCommand)
451
457
  )
452
458
 
453
- localInvCtx.execute() // Promise<InvocationContext> | InvocationContext
459
+ await localInvCtx.execute()
460
+
461
+ const resolve = (/** @type {any} */ item) => {
462
+ return item?.commandType === CommandType.Reference ?
463
+ new InvocationContext(
464
+ this.#runtimeName,
465
+ this.#connectionData,
466
+ item
467
+ ) : item
468
+ }
469
+
470
+ const respCommand = localInvCtx.#responseCommand
471
+ if (!respCommand) {
472
+ return []
473
+ }
454
474
 
455
- const extract = (/** @type {Command} */ respCommand) => {
456
- return respCommand.payload
475
+ if (respCommand.payload) {
476
+ return respCommand.payload.map((/** @type {any} */ item) => resolve(item))
457
477
  }
458
478
 
459
- // Normalize to Promise
460
- return localInvCtx.#responseCommand instanceof Promise
461
- ? localInvCtx.#responseCommand.then(extract)
462
- : localInvCtx.#responseCommand?.payload
479
+ return respCommand.payload || []
463
480
  }
464
481
 
465
482
  /**
@@ -506,9 +523,8 @@ class InvocationContext {
506
523
  for (let i = 0; i < newArray.length; i++) {
507
524
  newArray[i] = typeof Object
508
525
  }
509
- const args = [delegatesCacheInstance.addDelegate(payloadItem), RuntimeName.Nodejs].push(
510
- ...newArray
511
- )
526
+ const args = [delegatesCacheInstance.addDelegate(payloadItem), RuntimeName.Nodejs]
527
+ args.push(...newArray)
512
528
  return new Command(this.#runtimeName, CommandType.PassDelegate, args)
513
529
  } else if (TypesHandler.isPrimitiveOrNullOrUndefined(payloadItem)) {
514
530
  return new Command(this.#runtimeName, CommandType.Value, [payloadItem])
@@ -521,6 +537,49 @@ class InvocationContext {
521
537
  )
522
538
  }
523
539
  }
540
+
541
+ /**
542
+ * Process UpdateInvocationContext commands in the provided responseCommand payload.
543
+ * For each UpdateInvocationContext command, set the referenced InvocationContext's currentCommand to a Reference command,
544
+ * remove that InvocationContext from the static map and remove UpdateInvocationContext items from response payload.
545
+ * @param {Command} responseCommand
546
+ * @returns {Command}
547
+ */
548
+ #processUpdateInvocationContextCommands(responseCommand) {
549
+ if (!responseCommand?.payload?.length) {
550
+ return responseCommand
551
+ }
552
+
553
+ const commandsToUpdate = responseCommand.payload.filter(
554
+ (/** @type {unknown} */ item) => item instanceof Command && item.commandType === CommandType.UpdateInvocationContext
555
+ )
556
+
557
+ if (commandsToUpdate.length === 0) {
558
+ return responseCommand
559
+ }
560
+
561
+ const updatedPayload = new Set(responseCommand.payload)
562
+
563
+ for (const cmd of commandsToUpdate) {
564
+ if (cmd.payload?.length >= 2) {
565
+ const contextGuid = String(cmd.payload[0])
566
+ const instanceGuid = cmd.payload[1]
567
+ const invCtx = InvocationContext._invocationContexts.get(contextGuid)
568
+
569
+ if (invCtx) {
570
+ invCtx.#currentCommand = new Command(invCtx.#runtimeName, CommandType.Reference, [instanceGuid])
571
+ InvocationContext._invocationContexts.delete(contextGuid)
572
+ }
573
+ }
574
+ updatedPayload.delete(cmd)
575
+ }
576
+
577
+ return new Command(
578
+ responseCommand.runtimeName,
579
+ responseCommand.commandType,
580
+ Array.from(updatedPayload)
581
+ )
582
+ }
524
583
  }
525
584
 
526
585
  export { InvocationContext }
@@ -12,6 +12,7 @@ import { WsConnectionData } from '../utils/connectionData/WsConnectionData.js'
12
12
  import { UtilsConst } from '../utils/UtilsConst.js'
13
13
  import { ConfigSourceResolver } from './configuration/ConfigSourceResolver.js'
14
14
  import { ConfigPriority } from './configuration/ConfigPriority.js'
15
+ import { ComplexTypeResolver } from './tools/ComplexTypeResolver.js'
15
16
 
16
17
  /** @typedef {import('../types.d.ts').ConfigSource} ConfigSource */
17
18
 
@@ -132,4 +133,5 @@ export {
132
133
  CommandSerializer,
133
134
  CommandDeserializer,
134
135
  ConfigPriority,
136
+ ComplexTypeResolver,
135
137
  }