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.
- package/dist/core/handler/ArraySetItemHandler.cjs +9 -8
- package/dist/core/handler/AsDtoHandler.cjs +36 -0
- package/dist/core/handler/AsKwargsHandler.cjs +1 -1
- package/dist/core/handler/DestructReferenceHandler.cjs +9 -2
- package/dist/core/handler/DtoPropertyHandler.cjs +36 -0
- package/dist/core/handler/Handler.cjs +14 -9
- package/dist/core/handler/PassDelegateHandler.cjs +1 -1
- package/dist/core/handler/ProjectResultAsDtoHandler.cjs +66 -0
- package/dist/core/handler/SetInstanceFieldHandler.cjs +1 -1
- package/dist/core/handler/SetStaticFieldHandler.cjs +6 -6
- package/dist/core/interpreter/Interpreter.cjs +14 -2
- package/dist/core/protocol/CommandDeserializer.cjs +8 -0
- package/dist/core/protocol/TypeDeserializer.cjs +4 -0
- package/dist/core/protocol/TypeSerializer.cjs +10 -5
- package/dist/core/referenceCache/ReferencesCache.cjs +8 -5
- package/dist/core/webSocketClient/WebSocketClient.cjs +23 -5
- package/dist/core/webSocketClient/WebSocketClientBrowser.cjs +23 -5
- package/dist/plugins/PluginImplementationRegistry.cjs +214 -0
- package/dist/plugins/PluginPayloadBuilder.cjs +76 -0
- package/dist/plugins/PluginRegistry.cjs +255 -0
- package/dist/plugins/index.cjs +51 -0
- package/dist/plugins/interfaces/ICommunityPlugin.cjs +44 -0
- package/dist/plugins/interfaces/ICommunityReceivingPlugin.cjs +62 -0
- package/dist/plugins/interfaces/ICommunitySendingPlugin.cjs +59 -0
- package/dist/plugins/settings/BasePluginSettings.cjs +67 -0
- package/dist/plugins/settings/JwtGraftocodePluginSettings.cjs +40 -0
- package/dist/sdk/InvocationContext.cjs +133 -12
- package/dist/sdk/Javonet.cjs +3 -0
- package/dist/sdk/RuntimeContext.cjs +27 -9
- package/dist/sdk/configuration/configResolvers/ConfigResolver.cjs +7 -6
- package/dist/sdk/tools/DtoHelper.cjs +100 -0
- package/dist/types/core/handler/ArraySetItemHandler.d.ts +1 -1
- package/dist/types/core/handler/AsDtoHandler.d.ts +7 -0
- package/dist/types/core/handler/AsKwargsHandler.d.ts +1 -1
- package/dist/types/core/handler/DestructReferenceHandler.d.ts +2 -1
- package/dist/types/core/handler/DtoPropertyHandler.d.ts +7 -0
- package/dist/types/core/handler/ProjectResultAsDtoHandler.d.ts +9 -0
- package/dist/types/core/handler/SetInstanceFieldHandler.d.ts +1 -1
- package/dist/types/core/handler/SetStaticFieldHandler.d.ts +1 -1
- package/dist/types/core/interpreter/Interpreter.d.ts +3 -3
- package/dist/types/core/protocol/CommandDeserializer.d.ts +1 -0
- package/dist/types/core/protocol/TypeDeserializer.d.ts +1 -0
- package/dist/types/core/protocol/TypeSerializer.d.ts +1 -0
- package/dist/types/core/referenceCache/ReferencesCache.d.ts +3 -3
- package/dist/types/plugins/PluginImplementationRegistry.d.ts +121 -0
- package/dist/types/plugins/PluginPayloadBuilder.d.ts +32 -0
- package/dist/types/plugins/PluginRegistry.d.ts +157 -0
- package/dist/types/plugins/index.d.ts +8 -0
- package/dist/types/plugins/interfaces/ICommunityPlugin.d.ts +14 -0
- package/dist/types/plugins/interfaces/ICommunityReceivingPlugin.d.ts +50 -0
- package/dist/types/plugins/interfaces/ICommunitySendingPlugin.d.ts +27 -0
- package/dist/types/plugins/settings/BasePluginSettings.d.ts +45 -0
- package/dist/types/plugins/settings/JwtGraftocodePluginSettings.d.ts +32 -0
- package/dist/types/sdk/InvocationContext.d.ts +20 -2
- package/dist/types/sdk/Javonet.d.ts +2 -1
- package/dist/types/sdk/RuntimeContext.d.ts +6 -0
- package/dist/types/sdk/configuration/configResolvers/ConfigResolver.d.ts +3 -6
- package/dist/types/sdk/tools/DtoHelper.d.ts +35 -0
- package/dist/types/utils/Command.d.ts +5 -0
- package/dist/types/utils/CommandType.d.ts +3 -0
- package/dist/types/utils/Type.d.ts +2 -1
- package/dist/utils/Command.cjs +40 -0
- package/dist/utils/CommandType.cjs +4 -1
- package/dist/utils/Type.cjs +2 -1
- package/lib/core/handler/ArraySetItemHandler.js +11 -10
- package/lib/core/handler/AsDtoHandler.js +11 -0
- package/lib/core/handler/AsKwargsHandler.js +1 -1
- package/lib/core/handler/DestructReferenceHandler.js +11 -3
- package/lib/core/handler/DtoPropertyHandler.js +11 -0
- package/lib/core/handler/Handler.js +16 -7
- package/lib/core/handler/PassDelegateHandler.js +2 -1
- package/lib/core/handler/ProjectResultAsDtoHandler.js +47 -0
- package/lib/core/handler/SetInstanceFieldHandler.js +1 -1
- package/lib/core/handler/SetStaticFieldHandler.js +6 -6
- package/lib/core/interpreter/Interpreter.js +18 -4
- package/lib/core/protocol/CommandDeserializer.js +9 -0
- package/lib/core/protocol/TypeDeserializer.js +5 -0
- package/lib/core/protocol/TypeSerializer.js +11 -8
- package/lib/core/referenceCache/ReferencesCache.js +9 -5
- package/lib/core/webSocketClient/WebSocketClient.js +30 -5
- package/lib/core/webSocketClient/WebSocketClientBrowser.js +29 -5
- package/lib/plugins/PluginImplementationRegistry.js +231 -0
- package/lib/plugins/PluginPayloadBuilder.js +60 -0
- package/lib/plugins/PluginRegistry.js +295 -0
- package/lib/plugins/index.js +29 -0
- package/lib/plugins/interfaces/ICommunityPlugin.js +25 -0
- package/lib/plugins/interfaces/ICommunityReceivingPlugin.js +57 -0
- package/lib/plugins/interfaces/ICommunitySendingPlugin.js +42 -0
- package/lib/plugins/settings/BasePluginSettings.js +79 -0
- package/lib/plugins/settings/JwtGraftocodePluginSettings.js +43 -0
- package/lib/sdk/InvocationContext.js +149 -14
- package/lib/sdk/Javonet.js +2 -0
- package/lib/sdk/RuntimeContext.js +33 -9
- package/lib/sdk/configuration/configResolvers/ConfigResolver.js +14 -8
- package/lib/sdk/tools/DtoHelper.js +90 -0
- package/lib/utils/Command.js +47 -0
- package/lib/utils/CommandType.js +3 -0
- package/lib/utils/Type.js +2 -1
- 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
|
}
|
|
@@ -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}
|
|
45
|
-
* @returns {
|
|
44
|
+
* @param {string} reference_guid
|
|
45
|
+
* @returns {boolean}
|
|
46
46
|
*/
|
|
47
|
-
deleteReference(
|
|
48
|
-
|
|
49
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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) =>
|
|
227
|
+
client.on(WebSocketStateEvent.ERROR, (/** @type {unknown} */ error) => wrapReject(error))
|
|
207
228
|
client.on(WebSocketStateEvent.CLOSE, () => {
|
|
208
|
-
reject(new Error(
|
|
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(
|
|
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(
|
|
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,
|
|
184
|
+
client.addEventListener(WebSocketState.ERROR, (e) => wrapReject(e))
|
|
167
185
|
client.addEventListener(WebSocketState.CLOSE, () => {
|
|
168
|
-
reject(new Error(
|
|
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(
|
|
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
|
+
}
|