javonet-nodejs-sdk 2.6.12 → 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 (99) 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/DestructReferenceHandler.cjs +9 -2
  5. package/dist/core/handler/DtoPropertyHandler.cjs +36 -0
  6. package/dist/core/handler/Handler.cjs +14 -9
  7. package/dist/core/handler/PassDelegateHandler.cjs +1 -1
  8. package/dist/core/handler/ProjectResultAsDtoHandler.cjs +66 -0
  9. package/dist/core/handler/SetInstanceFieldHandler.cjs +1 -1
  10. package/dist/core/handler/SetStaticFieldHandler.cjs +6 -6
  11. package/dist/core/interpreter/Interpreter.cjs +14 -2
  12. package/dist/core/protocol/CommandDeserializer.cjs +8 -0
  13. package/dist/core/protocol/TypeDeserializer.cjs +4 -0
  14. package/dist/core/protocol/TypeSerializer.cjs +10 -5
  15. package/dist/core/referenceCache/ReferencesCache.cjs +8 -5
  16. package/dist/core/webSocketClient/WebSocketClient.cjs +23 -5
  17. package/dist/core/webSocketClient/WebSocketClientBrowser.cjs +23 -5
  18. package/dist/plugins/PluginImplementationRegistry.cjs +214 -0
  19. package/dist/plugins/PluginPayloadBuilder.cjs +76 -0
  20. package/dist/plugins/PluginRegistry.cjs +255 -0
  21. package/dist/plugins/index.cjs +51 -0
  22. package/dist/plugins/interfaces/ICommunityPlugin.cjs +44 -0
  23. package/dist/plugins/interfaces/ICommunityReceivingPlugin.cjs +62 -0
  24. package/dist/plugins/interfaces/ICommunitySendingPlugin.cjs +59 -0
  25. package/dist/plugins/settings/BasePluginSettings.cjs +67 -0
  26. package/dist/plugins/settings/JwtGraftocodePluginSettings.cjs +40 -0
  27. package/dist/sdk/InvocationContext.cjs +133 -12
  28. package/dist/sdk/Javonet.cjs +3 -0
  29. package/dist/sdk/RuntimeContext.cjs +27 -9
  30. package/dist/sdk/configuration/configResolvers/ConfigResolver.cjs +7 -6
  31. package/dist/sdk/tools/DtoHelper.cjs +100 -0
  32. package/dist/types/core/handler/ArraySetItemHandler.d.ts +1 -1
  33. package/dist/types/core/handler/AsDtoHandler.d.ts +7 -0
  34. package/dist/types/core/handler/AsKwargsHandler.d.ts +1 -1
  35. package/dist/types/core/handler/DestructReferenceHandler.d.ts +2 -1
  36. package/dist/types/core/handler/DtoPropertyHandler.d.ts +7 -0
  37. package/dist/types/core/handler/ProjectResultAsDtoHandler.d.ts +9 -0
  38. package/dist/types/core/handler/SetInstanceFieldHandler.d.ts +1 -1
  39. package/dist/types/core/handler/SetStaticFieldHandler.d.ts +1 -1
  40. package/dist/types/core/interpreter/Interpreter.d.ts +3 -3
  41. package/dist/types/core/protocol/CommandDeserializer.d.ts +1 -0
  42. package/dist/types/core/protocol/TypeDeserializer.d.ts +1 -0
  43. package/dist/types/core/protocol/TypeSerializer.d.ts +1 -0
  44. package/dist/types/core/referenceCache/ReferencesCache.d.ts +3 -3
  45. package/dist/types/plugins/PluginImplementationRegistry.d.ts +121 -0
  46. package/dist/types/plugins/PluginPayloadBuilder.d.ts +32 -0
  47. package/dist/types/plugins/PluginRegistry.d.ts +157 -0
  48. package/dist/types/plugins/index.d.ts +8 -0
  49. package/dist/types/plugins/interfaces/ICommunityPlugin.d.ts +14 -0
  50. package/dist/types/plugins/interfaces/ICommunityReceivingPlugin.d.ts +50 -0
  51. package/dist/types/plugins/interfaces/ICommunitySendingPlugin.d.ts +27 -0
  52. package/dist/types/plugins/settings/BasePluginSettings.d.ts +45 -0
  53. package/dist/types/plugins/settings/JwtGraftocodePluginSettings.d.ts +32 -0
  54. package/dist/types/sdk/InvocationContext.d.ts +20 -2
  55. package/dist/types/sdk/Javonet.d.ts +2 -1
  56. package/dist/types/sdk/RuntimeContext.d.ts +6 -0
  57. package/dist/types/sdk/configuration/configResolvers/ConfigResolver.d.ts +3 -6
  58. package/dist/types/sdk/tools/DtoHelper.d.ts +35 -0
  59. package/dist/types/utils/Command.d.ts +5 -0
  60. package/dist/types/utils/CommandType.d.ts +3 -0
  61. package/dist/types/utils/Type.d.ts +2 -1
  62. package/dist/utils/Command.cjs +40 -0
  63. package/dist/utils/CommandType.cjs +4 -1
  64. package/dist/utils/Type.cjs +2 -1
  65. package/lib/core/handler/ArraySetItemHandler.js +11 -10
  66. package/lib/core/handler/AsDtoHandler.js +11 -0
  67. package/lib/core/handler/AsKwargsHandler.js +1 -1
  68. package/lib/core/handler/DestructReferenceHandler.js +11 -3
  69. package/lib/core/handler/DtoPropertyHandler.js +11 -0
  70. package/lib/core/handler/Handler.js +16 -7
  71. package/lib/core/handler/PassDelegateHandler.js +2 -1
  72. package/lib/core/handler/ProjectResultAsDtoHandler.js +47 -0
  73. package/lib/core/handler/SetInstanceFieldHandler.js +1 -1
  74. package/lib/core/handler/SetStaticFieldHandler.js +6 -6
  75. package/lib/core/interpreter/Interpreter.js +18 -4
  76. package/lib/core/protocol/CommandDeserializer.js +9 -0
  77. package/lib/core/protocol/TypeDeserializer.js +5 -0
  78. package/lib/core/protocol/TypeSerializer.js +11 -8
  79. package/lib/core/referenceCache/ReferencesCache.js +9 -5
  80. package/lib/core/webSocketClient/WebSocketClient.js +30 -5
  81. package/lib/core/webSocketClient/WebSocketClientBrowser.js +29 -5
  82. package/lib/plugins/PluginImplementationRegistry.js +231 -0
  83. package/lib/plugins/PluginPayloadBuilder.js +60 -0
  84. package/lib/plugins/PluginRegistry.js +295 -0
  85. package/lib/plugins/index.js +29 -0
  86. package/lib/plugins/interfaces/ICommunityPlugin.js +25 -0
  87. package/lib/plugins/interfaces/ICommunityReceivingPlugin.js +57 -0
  88. package/lib/plugins/interfaces/ICommunitySendingPlugin.js +42 -0
  89. package/lib/plugins/settings/BasePluginSettings.js +79 -0
  90. package/lib/plugins/settings/JwtGraftocodePluginSettings.js +43 -0
  91. package/lib/sdk/InvocationContext.js +149 -14
  92. package/lib/sdk/Javonet.js +2 -0
  93. package/lib/sdk/RuntimeContext.js +33 -9
  94. package/lib/sdk/configuration/configResolvers/ConfigResolver.js +14 -8
  95. package/lib/sdk/tools/DtoHelper.js +90 -0
  96. package/lib/utils/Command.js +47 -0
  97. package/lib/utils/CommandType.js +3 -0
  98. package/lib/utils/Type.js +2 -1
  99. package/package.json +5 -1
@@ -7,11 +7,14 @@ import { CommandSerializer } from '../protocol/CommandSerializer.js'
7
7
  import { TransmitterWebsocketBrowser } from '../transmitter/TransmitterWebsocketBrowser.js'
8
8
  import { TransmitterWebsocket } from '../transmitter/TransmitterWebsocket.js'
9
9
  import { Handler } from '../handler/Handler.js'
10
-
10
+ import { PluginRegistry } from '../../plugins/PluginRegistry.js'
11
+ import { PluginPayloadBuilder } from '../../plugins/PluginPayloadBuilder.js'
12
+ import { Command } from '../../utils/Command.js'
13
+ import { CommandType } from '../../utils/CommandType.js'
14
+
11
15
  /**
12
16
  * @typedef {import('../../utils/connectionData/IConnectionData.js').IConnectionData} IConnectionData
13
17
  * @typedef {typeof import('../../types.d.ts').RuntimeName} RuntimeNameType
14
- * @typedef {import('../../utils/Command.js').Command} Command
15
18
  */
16
19
 
17
20
  /** @type {typeof import('../receiver/Receiver.js').Receiver} */
@@ -26,13 +29,24 @@ const requireDynamic = getRequire(import.meta.url)
26
29
  export class Interpreter {
27
30
 
28
31
  /**
29
- *
32
+ * @param {string} runtimeContextId
30
33
  * @param {Command} command
31
34
  * @param {IConnectionData} connectionData
32
35
  * @returns {Promise<Command>}
33
36
  */
34
- static async execute(command, connectionData) {
37
+ static async execute(runtimeContextId, command, connectionData) {
35
38
  try {
39
+ if (PluginRegistry.arePluginsRegisteredForRuntime(runtimeContextId)) {
40
+ const serializedPlugins = PluginPayloadBuilder.buildPluginsPayload(runtimeContextId)
41
+
42
+ // Wrap the command with PluginWrapper
43
+ command = new Command(
44
+ command.runtimeName,
45
+ CommandType.PluginWrapper,
46
+ [serializedPlugins, command]
47
+ )
48
+ }
49
+
36
50
  let messageByteArray = CommandSerializer.serialize(command, connectionData)
37
51
 
38
52
  if (!(messageByteArray instanceof Uint8Array)) {
@@ -60,6 +60,8 @@ export class CommandDeserializer {
60
60
  return this.readUInt()
61
61
  case 'JAVONET_NULL':
62
62
  return this.readNull()
63
+ case 'JAVONET_UNDEFINED':
64
+ return this.readUndefined()
63
65
  default:
64
66
  throw 'Unknown type - not supported in JavaScript'
65
67
  }
@@ -186,4 +188,11 @@ export class CommandDeserializer {
186
188
  this.position += size
187
189
  return TypeDeserializer.deserializeNull()
188
190
  }
191
+
192
+ readUndefined() {
193
+ const size = 1
194
+ this.position += 2
195
+ this.position += size
196
+ return TypeDeserializer.deserializeUndefined()
197
+ }
189
198
  }
@@ -114,4 +114,9 @@ export class TypeDeserializer {
114
114
  static deserializeNull(encodedNull = null) {
115
115
  return null
116
116
  }
117
+
118
+ // eslint-disable-next-line no-unused-vars
119
+ static deserializeUndefined(encodedUndefined = null) {
120
+ return undefined
121
+ }
117
122
  }
@@ -15,6 +15,9 @@ class TypeSerializer {
15
15
  if (payload_item === null) {
16
16
  return TypeSerializer.serializeNull()
17
17
  }
18
+ if (payload_item === undefined) {
19
+ return TypeSerializer.serializeUndefined()
20
+ }
18
21
  if (payload_item instanceof Command) {
19
22
  return TypeSerializer.serializeCommand(payload_item)
20
23
  } else if (typeof payload_item === 'number') {
@@ -36,14 +39,6 @@ class TypeSerializer {
36
39
  payload_item
37
40
  throw new CustomError(message, 'JAVONET_PROMISE')
38
41
  }
39
- if (payload_item === undefined) {
40
- throw Error(
41
- 'Unsupported payload item type: ' +
42
- typeof payload_item +
43
- ' for payload item: ' +
44
- payload_item
45
- )
46
- }
47
42
  throw Error(
48
43
  'Unsupported payload item type: ' +
49
44
  (payload_item?.constructor?.name || typeof payload_item) +
@@ -185,6 +180,14 @@ class TypeSerializer {
185
180
  return buffer
186
181
  }
187
182
 
183
+ static serializeUndefined() {
184
+ const buffer = Buffer.alloc(3)
185
+ buffer.writeUInt8(Type.JAVONET_UNDEFINED, 0)
186
+ buffer.writeUInt8(1, 1)
187
+ buffer.writeUInt8(0, 2)
188
+ return buffer
189
+ }
190
+
188
191
  /**
189
192
  * @param {number} int_value
190
193
  */
@@ -41,12 +41,16 @@ class ReferencesCache {
41
41
  }
42
42
 
43
43
  /**
44
- * @param {string} id
45
- * @returns {number}
44
+ * @param {string} reference_guid
45
+ * @returns {boolean}
46
46
  */
47
- deleteReference(id) {
48
- delete _cache[id]
49
- return 0
47
+ deleteReference(reference_guid) {
48
+ if (reference_guid == null || typeof reference_guid !== 'string') {
49
+ return false
50
+ }
51
+
52
+ delete _cache[reference_guid]
53
+ return true
50
54
  }
51
55
  }
52
56
 
@@ -165,7 +165,9 @@ class WebSocketClient {
165
165
  if (messageQueue[this.url]) {
166
166
  // Reject any pending messages
167
167
  messageQueue[this.url].forEach(({ reject }) => {
168
- reject(new Error('WebSocket disconnected'))
168
+ reject(new Error(
169
+ `WebSocket to ${this.url} was disconnected. The connection was closed before the operation completed.`
170
+ ))
169
171
  })
170
172
  delete messageQueue[this.url]
171
173
  }
@@ -185,7 +187,10 @@ class WebSocketClient {
185
187
  WebSocket = requireDynamic('ws')
186
188
  } catch (error) {
187
189
  if (/** @type {{ code?: string }} */ (error).code === 'MODULE_NOT_FOUND') {
188
- throw new Error('ws module not found. Please install it using npm install ws')
190
+ throw new Error(
191
+ 'WebSocket connection failed: the "ws" package is not installed. ' +
192
+ 'Install it with: npm install ws'
193
+ )
189
194
  }
190
195
  throw error
191
196
  }
@@ -193,7 +198,10 @@ class WebSocketClient {
193
198
 
194
199
  return new Promise((resolve, reject) => {
195
200
  if (!WebSocket) {
196
- return reject(new Error('ws client is null'))
201
+ return reject(new Error(
202
+ 'WebSocket connection failed: ws client is not available. ' +
203
+ 'In Node.js ensure the "ws" package is installed (npm install ws).'
204
+ ))
197
205
  }
198
206
 
199
207
  let client = clients[this.url]
@@ -202,10 +210,27 @@ class WebSocketClient {
202
210
  clients[this.url] = client
203
211
  }
204
212
 
213
+ const wrapReject = (/** @type {Error | unknown} */ err) => {
214
+ const msg = err instanceof Error ? err.message : String(err)
215
+ const wrapped = new Error(
216
+ `WebSocket connection to ${this.url} failed: ${msg}. ` +
217
+ 'Check that the server is running, the URL is correct (ws:// or wss://), ' +
218
+ 'and that no firewall or proxy is blocking the connection.'
219
+ )
220
+ if (err instanceof Error && 'cause' in Error.prototype) {
221
+ /** @type {{ cause?: unknown }} */ (wrapped).cause = err
222
+ }
223
+ reject(wrapped)
224
+ }
225
+
205
226
  client.on(WebSocketStateEvent.OPEN, () => resolve(client))
206
- client.on(WebSocketStateEvent.ERROR, (/** @type {unknown} */ error) => reject(error))
227
+ client.on(WebSocketStateEvent.ERROR, (/** @type {unknown} */ error) => wrapReject(error))
207
228
  client.on(WebSocketStateEvent.CLOSE, () => {
208
- reject(new Error('Connection closed before receiving message'))
229
+ reject(new Error(
230
+ `WebSocket connection to ${this.url} was closed before it could be established. ` +
231
+ 'The server may have refused the connection, closed immediately, or a network error occurred. ' +
232
+ 'Check that the server is running and the URL is correct.'
233
+ ))
209
234
  })
210
235
  })
211
236
  }
@@ -136,7 +136,9 @@ class WebSocketClientBrowser {
136
136
 
137
137
  if (messageQueue[this.url]) {
138
138
  messageQueue[this.url].forEach(({ reject }) => {
139
- reject(new Error('WebSocket disconnected'))
139
+ reject(new Error(
140
+ `WebSocket to ${this.url} was disconnected. The connection was closed before the operation completed.`
141
+ ))
140
142
  })
141
143
  delete messageQueue[this.url]
142
144
  }
@@ -152,7 +154,10 @@ class WebSocketClientBrowser {
152
154
  _connect() {
153
155
  return new Promise((resolve, reject) => {
154
156
  if (!WsClient) {
155
- return reject(new Error('missing WebSocket client'))
157
+ return reject(new Error(
158
+ `WebSocket connection to ${this.url} failed: WebSocket API is not available in this environment. ` +
159
+ 'Ensure the code runs in a browser or environment that supports WebSocket.'
160
+ ))
156
161
  }
157
162
 
158
163
  let client = clients[this.url]
@@ -162,10 +167,27 @@ class WebSocketClientBrowser {
162
167
  clients[this.url] = client
163
168
  }
164
169
 
170
+ const wrapReject = (/** @type {Event | Error | unknown} */ err) => {
171
+ const msg = err instanceof Error ? err.message : (err instanceof Event && err.type ? `Event: ${err.type}` : String(err))
172
+ const wrapped = new Error(
173
+ `WebSocket connection to ${this.url} failed: ${msg}. ` +
174
+ 'Check that the server is running, the URL is correct (ws:// or wss://), ' +
175
+ 'and that no firewall, CORS, or proxy is blocking the connection.'
176
+ )
177
+ if (err instanceof Error && 'cause' in Error.prototype) {
178
+ /** @type {{ cause?: unknown }} */ (wrapped).cause = err
179
+ }
180
+ reject(wrapped)
181
+ }
182
+
165
183
  client.addEventListener(WebSocketState.OPEN, () => resolve(client))
166
- client.addEventListener(WebSocketState.ERROR, reject)
184
+ client.addEventListener(WebSocketState.ERROR, (e) => wrapReject(e))
167
185
  client.addEventListener(WebSocketState.CLOSE, () => {
168
- reject(new Error('Connection closed before receiving message'))
186
+ reject(new Error(
187
+ `WebSocket connection to ${this.url} was closed before it could be established. ` +
188
+ 'The server may have refused the connection or a network error occurred. ' +
189
+ 'Check that the server is running and the URL is correct.'
190
+ ))
169
191
  })
170
192
  })
171
193
  }
@@ -185,7 +207,9 @@ class WebSocketClientBrowser {
185
207
 
186
208
  const handleMessage = (/** @type {any} */ message) => {
187
209
  if (!message?.data) {
188
- return reject(new Error('Invalid message received'))
210
+ return reject(new Error(
211
+ `WebSocket to ${this.url}: invalid message received (no data). The server response may be malformed.`
212
+ ))
189
213
  }
190
214
 
191
215
  const byteArray = new Uint8Array(message?.data)
@@ -0,0 +1,231 @@
1
+ /**
2
+ * A registry for managing plugins and their implementations.
3
+ * Provides access to registered plugin instances and their typed variants,
4
+ * allowing for dynamic plugin resolution at runtime.
5
+ *
6
+ * This class acts as a lightweight alternative to a dependency injection container,
7
+ * supporting both singleton and transient lifecycles for plugins.
8
+ * Plugins can be resolved by type, and optionally created with constructor arguments.
9
+ */
10
+ export class PluginImplementationRegistry {
11
+ /** @type {Map<string, Function>} Map of interface names to implementation constructors for transient plugins */
12
+ static #transientImplementations = new Map()
13
+
14
+ /** @type {Map<Function, object>} Map of implementation types to singleton instances */
15
+ static #singletonImplementations = new Map()
16
+
17
+ /** @type {Map<Function, object>} Map of settings types to configuration objects */
18
+ static #pluginConfigurations = new Map()
19
+
20
+ /**
21
+ * Registers a transient plugin implementation. A new instance will be created each time the plugin is resolved.
22
+ *
23
+ * @param {Function} InterfaceClass - The interface or base class of the plugin (must be a class/constructor)
24
+ * @param {Function} ImplementationClass - The concrete class that implements InterfaceClass
25
+ * @throws {TypeError} If InterfaceClass is not a function/constructor
26
+ * @throws {TypeError} If ImplementationClass is not a function/constructor
27
+ *
28
+ * @example
29
+ * PluginImplementationRegistry.registerPluginTransientImplementation(IJwtAuthorize, JwtAuthorizeImpl)
30
+ */
31
+ static registerPluginTransientImplementation(InterfaceClass, ImplementationClass) {
32
+ if (typeof InterfaceClass !== 'function') {
33
+ throw new TypeError(`${InterfaceClass?.name || 'InterfaceClass'} is not a function. Only classes/constructors are allowed.`)
34
+ }
35
+
36
+ if (typeof ImplementationClass !== 'function') {
37
+ throw new TypeError(`${ImplementationClass?.name || 'ImplementationClass'} is not a function. Only classes/constructors are allowed.`)
38
+ }
39
+
40
+ // Use the interface class name as the key
41
+ const interfaceName = InterfaceClass.name || InterfaceClass.toString()
42
+ PluginImplementationRegistry.#transientImplementations.set(interfaceName, ImplementationClass)
43
+ }
44
+
45
+ /**
46
+ * Resolves a new instance of a transiently registered plugin.
47
+ *
48
+ * @param {Function} InterfaceClass - The type of the plugin interface to resolve
49
+ * @param {...any} constructorArgs - Optional arguments to pass to the constructor
50
+ * @returns {object} A newly created instance of the registered implementation
51
+ * @throws {Error} If the plugin type has not been registered as transient
52
+ * @throws {TypeError} If InterfaceClass is not a function/constructor
53
+ *
54
+ * @example
55
+ * const jwtAuthorize = PluginImplementationRegistry.resolvePluginTransientImplementation(IJwtAuthorize)
56
+ */
57
+ static resolvePluginTransientImplementation(InterfaceClass, ...constructorArgs) {
58
+ if (typeof InterfaceClass !== 'function') {
59
+ throw new TypeError(`${InterfaceClass?.name || 'InterfaceClass'} is not a function. Only classes/constructors are allowed.`)
60
+ }
61
+
62
+ const interfaceName = InterfaceClass.name || InterfaceClass.toString()
63
+ const ImplementationClass = PluginImplementationRegistry.#transientImplementations.get(interfaceName)
64
+
65
+ if (!ImplementationClass) {
66
+ throw new Error(`No transient implementation registered for ${interfaceName}.`)
67
+ }
68
+
69
+ try {
70
+ // Create new instance with constructor arguments if provided
71
+ const instance = constructorArgs.length > 0
72
+ ? new ImplementationClass(...constructorArgs)
73
+ : new ImplementationClass()
74
+
75
+ if (!instance) {
76
+ throw new Error(`Could not instantiate ${ImplementationClass.name || 'implementation'}.`)
77
+ }
78
+
79
+ return instance
80
+ } catch (error) {
81
+ throw new Error(`Failed to create instance of ${ImplementationClass.name || 'implementation'}: ${error.message}`)
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Registers a plugin configuration/settings object.
87
+ *
88
+ * @param {Function} SettingsClass - The settings class type
89
+ * @param {object} settings - The settings instance to register
90
+ * @throws {TypeError} If SettingsClass is not a function/constructor
91
+ * @throws {TypeError} If settings is not an object
92
+ *
93
+ * @example
94
+ * const settings = new JwtGraftocodePluginSettings()
95
+ * settings.username = 'user'
96
+ * settings.secretKey = 'key'
97
+ * PluginImplementationRegistry.registerPluginConfiguration(JwtGraftocodePluginSettings, settings)
98
+ */
99
+ static registerPluginConfiguration(SettingsClass, settings) {
100
+ if (typeof SettingsClass !== 'function') {
101
+ throw new TypeError(`${SettingsClass?.name || 'SettingsClass'} is not a function. Only classes/constructors are allowed.`)
102
+ }
103
+
104
+ if (!settings || typeof settings !== 'object') {
105
+ throw new TypeError('settings must be an object')
106
+ }
107
+
108
+ PluginImplementationRegistry.#pluginConfigurations.set(SettingsClass, settings)
109
+ }
110
+
111
+ /**
112
+ * Retrieves the configuration object of the specified type for a given plugin.
113
+ *
114
+ * @param {Function} SettingsClass - The expected configuration type
115
+ * @returns {object | null} The configuration object, or null if not found
116
+ * @throws {TypeError} If SettingsClass is not a function/constructor
117
+ *
118
+ * @example
119
+ * const config = PluginImplementationRegistry.getPluginConfiguration(JwtGraftocodePluginSettings)
120
+ * if (config) {
121
+ * console.log(config.username)
122
+ * }
123
+ */
124
+ static getPluginConfiguration(SettingsClass) {
125
+ if (typeof SettingsClass !== 'function') {
126
+ throw new TypeError(`${SettingsClass?.name || 'SettingsClass'} is not a function. Only classes/constructors are allowed.`)
127
+ }
128
+
129
+ const config = PluginImplementationRegistry.#pluginConfigurations.get(SettingsClass)
130
+ return config || null
131
+ }
132
+
133
+ /**
134
+ * Checks whether a configuration has been registered for the specified plugin type.
135
+ *
136
+ * @param {Function} SettingsClass - The settings class type to check
137
+ * @returns {boolean} True if a configuration entry exists, false otherwise
138
+ * @throws {TypeError} If SettingsClass is not a function/constructor
139
+ *
140
+ * @example
141
+ * if (PluginImplementationRegistry.isPluginConfigurationAvailable(JwtGraftocodePluginSettings)) {
142
+ * // Configuration exists
143
+ * }
144
+ */
145
+ static isPluginConfigurationAvailable(SettingsClass) {
146
+ if (typeof SettingsClass !== 'function') {
147
+ throw new TypeError(`${SettingsClass?.name || 'SettingsClass'} is not a function. Only classes/constructors are allowed.`)
148
+ }
149
+
150
+ return PluginImplementationRegistry.#pluginConfigurations.has(SettingsClass)
151
+ }
152
+
153
+ /**
154
+ * Registers a new plugin implementation of the specified type and stores it as a singleton instance.
155
+ *
156
+ * @param {Function} InterfaceClass - The interface or base class that the plugin implementation inherits or implements
157
+ * @param {Function} ImplementationClass - The concrete plugin type to register
158
+ * @param {...any} constructorArgs - Optional arguments to pass to the constructor
159
+ * @throws {TypeError} If InterfaceClass is not a function/constructor
160
+ * @throws {TypeError} If ImplementationClass is not a function/constructor
161
+ *
162
+ * @example
163
+ * PluginImplementationRegistry.registerPluginSingletonImplementation(IJwtAuthorize, JwtAuthorizeImpl)
164
+ * // Or with constructor arguments:
165
+ * PluginImplementationRegistry.registerPluginSingletonImplementation(IJwtAuthorize, JwtAuthorizeImpl, 'arg1', 'arg2')
166
+ */
167
+ static registerPluginSingletonImplementation(InterfaceClass, ImplementationClass, ...constructorArgs) {
168
+ if (typeof InterfaceClass !== 'function') {
169
+ throw new TypeError(`${InterfaceClass?.name || 'InterfaceClass'} is not a function. Only classes/constructors are allowed.`)
170
+ }
171
+
172
+ if (typeof ImplementationClass !== 'function') {
173
+ throw new TypeError(`${ImplementationClass?.name || 'ImplementationClass'} is not a function. Only classes/constructors are allowed.`)
174
+ }
175
+
176
+ try {
177
+ // Create instance with constructor arguments if provided
178
+ const instance = constructorArgs.length > 0
179
+ ? new ImplementationClass(...constructorArgs)
180
+ : new ImplementationClass()
181
+
182
+ if (!instance) {
183
+ throw new Error(`Could not create instance of ${ImplementationClass.name || 'implementation'}.`)
184
+ }
185
+
186
+ PluginImplementationRegistry.#singletonImplementations.set(ImplementationClass, instance)
187
+ } catch (error) {
188
+ throw new Error(`Failed to register singleton implementation: ${error.message}`)
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Resolves a registered plugin implementation by its type.
194
+ *
195
+ * @param {Function} ImplementationClass - The type of the plugin implementation to resolve
196
+ * @returns {object} The registered plugin instance
197
+ * @throws {Error} If no plugin instance of the specified type is found in the registry
198
+ * @throws {TypeError} If ImplementationClass is not a function/constructor
199
+ *
200
+ * @example
201
+ * const plugin = PluginImplementationRegistry.resolvePluginSingletonImplementation(JwtAuthorizeImpl)
202
+ */
203
+ static resolvePluginSingletonImplementation(ImplementationClass) {
204
+ if (typeof ImplementationClass !== 'function') {
205
+ throw new TypeError(`${ImplementationClass?.name || 'ImplementationClass'} is not a function. Only classes/constructors are allowed.`)
206
+ }
207
+
208
+ const instance = PluginImplementationRegistry.#singletonImplementations.get(ImplementationClass)
209
+
210
+ if (!instance) {
211
+ throw new Error(`No singleton implementation registered for ${ImplementationClass.name || 'implementation'}.`)
212
+ }
213
+
214
+ return instance
215
+ }
216
+
217
+ /**
218
+ * Resets the entire plugin implementation registry to its initial state.
219
+ * Clears all registered plugins and configurations.
220
+ * Useful for testing or reconfiguration scenarios.
221
+ *
222
+ * @example
223
+ * PluginImplementationRegistry.reset()
224
+ * // Can now register new plugins
225
+ */
226
+ static reset() {
227
+ PluginImplementationRegistry.#transientImplementations.clear()
228
+ PluginImplementationRegistry.#singletonImplementations.clear()
229
+ PluginImplementationRegistry.#pluginConfigurations.clear()
230
+ }
231
+ }
@@ -0,0 +1,60 @@
1
+ import { PluginRegistry } from "./PluginRegistry.js"
2
+
3
+ /**
4
+ * Builds serialized payloads from registered plugins.
5
+ * This class is responsible for retrieving plugins from the registry,
6
+ * executing them, and serializing their output for transmission.
7
+ */
8
+ export class PluginPayloadBuilder {
9
+ /**
10
+ * Builds the plugin payload for a specific runtime context.
11
+ * Retrieves the sending plugin registered for the given runtime context ID,
12
+ * executes it, and returns the serialized result.
13
+ *
14
+ * @param {string} runtimeContextId - The UUID of the runtime context
15
+ * @returns {string} JSON-serialized plugin payload
16
+ * @throws {Error} If no plugin is found for the runtime context
17
+ * @throws {Error} If plugin execution fails
18
+ *
19
+ * @example
20
+ * const payload = PluginPayloadBuilder.buildPluginsPayload(runtimeContextId)
21
+ * // Returns: '{"token":"abc123","timestamp":1234567890}'
22
+ */
23
+ static buildPluginsPayload(runtimeContextId) {
24
+ const result = PluginRegistry.tryGetSendingPlugin(runtimeContextId)
25
+
26
+ if (result.success && result.plugin) {
27
+ try {
28
+ // Execute the plugin to get the payload data
29
+ const payload = result.plugin.execute()
30
+
31
+ if (!result?.plugin?.pluginId) {
32
+ throw new Error('Plugin execution failed pluginId is not defined')
33
+ }
34
+ const objectPayload = { [result?.plugin?.pluginId]: payload }
35
+ return PluginPayloadBuilder.serializeObject(objectPayload)
36
+ } catch (error) {
37
+ throw new Error(`Plugin execution failed for runtime context ${runtimeContextId}: ${error.message}`)
38
+ }
39
+ }
40
+
41
+ throw new Error(`Plugin for runtime context ${runtimeContextId} not found.`)
42
+ }
43
+
44
+ /**
45
+ * Serializes an object to JSON string.
46
+ *
47
+ * @param {object} payload - The payload object to serialize
48
+ * @returns {string} JSON-serialized string
49
+ * @throws {Error} If serialization fails
50
+ *
51
+ * @private
52
+ */
53
+ static serializeObject(payload) {
54
+ try {
55
+ return JSON.stringify(payload)
56
+ } catch (error) {
57
+ throw new Error(`Failed to serialize plugin payload: ${error.message}`)
58
+ }
59
+ }
60
+ }