@twin.org/core 0.0.4-next.1 → 0.0.4-next.11

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 (101) hide show
  1. package/dist/es/encoding/base64.js +2 -1
  2. package/dist/es/encoding/base64.js.map +1 -1
  3. package/dist/es/errors/conflictError.js +1 -1
  4. package/dist/es/errors/conflictError.js.map +1 -1
  5. package/dist/es/errors/generalError.js +1 -1
  6. package/dist/es/errors/generalError.js.map +1 -1
  7. package/dist/es/errors/notImplementedError.js +1 -1
  8. package/dist/es/errors/notImplementedError.js.map +1 -1
  9. package/dist/es/errors/validationError.js +1 -1
  10. package/dist/es/errors/validationError.js.map +1 -1
  11. package/dist/es/factories/factory.js +9 -0
  12. package/dist/es/factories/factory.js.map +1 -1
  13. package/dist/es/helpers/arrayHelper.js +1 -1
  14. package/dist/es/helpers/arrayHelper.js.map +1 -1
  15. package/dist/es/helpers/errorHelper.js +1 -1
  16. package/dist/es/helpers/errorHelper.js.map +1 -1
  17. package/dist/es/helpers/jsonHelper.js +1 -1
  18. package/dist/es/helpers/jsonHelper.js.map +1 -1
  19. package/dist/es/helpers/objectHelper.js +2 -2
  20. package/dist/es/helpers/objectHelper.js.map +1 -1
  21. package/dist/es/helpers/stringHelper.js +2 -2
  22. package/dist/es/helpers/stringHelper.js.map +1 -1
  23. package/dist/es/index.js +4 -0
  24. package/dist/es/index.js.map +1 -1
  25. package/dist/es/models/IComponent.js.map +1 -1
  26. package/dist/es/models/ILocale.js.map +1 -1
  27. package/dist/es/models/ISharedObjectBufferOptions.js +4 -0
  28. package/dist/es/models/ISharedObjectBufferOptions.js.map +1 -0
  29. package/dist/es/models/ISharedObjectBufferWorkerMessage.js +2 -0
  30. package/dist/es/models/ISharedObjectBufferWorkerMessage.js.map +1 -0
  31. package/dist/es/models/IValidationFailure.js.map +1 -1
  32. package/dist/es/models/sharedObjectBufferMessageTypes.js +13 -0
  33. package/dist/es/models/sharedObjectBufferMessageTypes.js.map +1 -0
  34. package/dist/es/utils/asyncCache.js +4 -1
  35. package/dist/es/utils/asyncCache.js.map +1 -1
  36. package/dist/es/utils/coerce.js +10 -20
  37. package/dist/es/utils/coerce.js.map +1 -1
  38. package/dist/es/utils/compression.js +1 -1
  39. package/dist/es/utils/compression.js.map +1 -1
  40. package/dist/es/utils/guards.js +36 -0
  41. package/dist/es/utils/guards.js.map +1 -1
  42. package/dist/es/utils/is.js +9 -9
  43. package/dist/es/utils/is.js.map +1 -1
  44. package/dist/es/utils/mutex.js +55 -17
  45. package/dist/es/utils/mutex.js.map +1 -1
  46. package/dist/es/utils/sharedObjectBuffer.js +305 -0
  47. package/dist/es/utils/sharedObjectBuffer.js.map +1 -0
  48. package/dist/es/utils/sharedStore.js +2 -2
  49. package/dist/es/utils/sharedStore.js.map +1 -1
  50. package/dist/types/errors/conflictError.d.ts +1 -1
  51. package/dist/types/errors/generalError.d.ts +1 -1
  52. package/dist/types/errors/notImplementedError.d.ts +1 -1
  53. package/dist/types/errors/validationError.d.ts +1 -1
  54. package/dist/types/factories/factory.d.ts +6 -0
  55. package/dist/types/helpers/arrayHelper.d.ts +1 -1
  56. package/dist/types/helpers/errorHelper.d.ts +1 -1
  57. package/dist/types/helpers/jsonHelper.d.ts +1 -1
  58. package/dist/types/helpers/objectHelper.d.ts +2 -2
  59. package/dist/types/helpers/stringHelper.d.ts +2 -2
  60. package/dist/types/index.d.ts +4 -0
  61. package/dist/types/models/IComponent.d.ts +2 -2
  62. package/dist/types/models/ILocale.d.ts +1 -1
  63. package/dist/types/models/ISharedObjectBufferOptions.d.ts +17 -0
  64. package/dist/types/models/ISharedObjectBufferWorkerMessage.d.ts +29 -0
  65. package/dist/types/models/IValidationFailure.d.ts +1 -1
  66. package/dist/types/models/sharedObjectBufferMessageTypes.d.ts +13 -0
  67. package/dist/types/utils/asyncCache.d.ts +1 -8
  68. package/dist/types/utils/coerce.d.ts +10 -20
  69. package/dist/types/utils/compression.d.ts +1 -1
  70. package/dist/types/utils/guards.d.ts +24 -0
  71. package/dist/types/utils/is.d.ts +9 -9
  72. package/dist/types/utils/sharedObjectBuffer.d.ts +74 -0
  73. package/dist/types/utils/sharedStore.d.ts +2 -2
  74. package/docs/changelog.md +242 -0
  75. package/docs/reference/classes/ArrayHelper.md +1 -1
  76. package/docs/reference/classes/AsyncCache.md +1 -1
  77. package/docs/reference/classes/Coerce.md +10 -50
  78. package/docs/reference/classes/Compression.md +1 -1
  79. package/docs/reference/classes/ConflictError.md +1 -1
  80. package/docs/reference/classes/ErrorHelper.md +1 -1
  81. package/docs/reference/classes/Factory.md +28 -0
  82. package/docs/reference/classes/GeneralError.md +1 -1
  83. package/docs/reference/classes/Guards.md +108 -0
  84. package/docs/reference/classes/Is.md +9 -9
  85. package/docs/reference/classes/JsonHelper.md +1 -1
  86. package/docs/reference/classes/NotImplementedError.md +1 -1
  87. package/docs/reference/classes/ObjectHelper.md +2 -2
  88. package/docs/reference/classes/SharedObjectBuffer.md +192 -0
  89. package/docs/reference/classes/SharedStore.md +2 -2
  90. package/docs/reference/classes/StringHelper.md +16 -13
  91. package/docs/reference/classes/ValidationError.md +1 -1
  92. package/docs/reference/index.md +5 -0
  93. package/docs/reference/interfaces/IComponent.md +2 -2
  94. package/docs/reference/interfaces/ILocale.md +1 -1
  95. package/docs/reference/interfaces/ISharedObjectBufferOptions.md +33 -0
  96. package/docs/reference/interfaces/ISharedObjectBufferWorkerMessage.md +44 -0
  97. package/docs/reference/interfaces/IValidationFailure.md +1 -1
  98. package/docs/reference/type-aliases/SharedObjectBufferMessageTypes.md +5 -0
  99. package/docs/reference/variables/SharedObjectBufferMessageTypes.md +13 -0
  100. package/locales/en.json +10 -2
  101. package/package.json +4 -4
@@ -0,0 +1,305 @@
1
+ import { GeneralError, Is, ObjectHelper, SharedStore } from "@twin.org/core";
2
+ import { SharedObjectBufferMessageTypes } from "../models/sharedObjectBufferMessageTypes.js";
3
+ /**
4
+ * Manages per-object SharedArrayBuffers that store objects as UTF-8 JSON.
5
+ * Buffer layout: 4-byte Int32 header (current data byte length) followed by the JSON-encoded object.
6
+ * Buffers are explicitly created with create and cached in SharedStore.
7
+ * On a worker thread an existing buffer is fetched via a MessagePort handshake
8
+ * (same protocol as Mutex) and cached locally.
9
+ * Buffers grow automatically when the payload exceeds capacity; when the payload drops well below
10
+ * capacity the buffer is replaced with a smaller one. The caller must hold the objectId-keyed Mutex
11
+ * around every read and write. The main thread's worker message handler must forward messages to
12
+ * both Mutex.handleWorkerMessage and SharedObjectBuffer.handleWorkerMessage.
13
+ */
14
+ export class SharedObjectBuffer {
15
+ /**
16
+ * Runtime name for the class.
17
+ */
18
+ static CLASS_NAME = "SharedObjectBuffer";
19
+ /**
20
+ * Default payload capacity per object (1 MiB). The first caller that creates the buffer
21
+ * for an object determines its initial capacity; later callers that pass a different value
22
+ * are ignored.
23
+ */
24
+ static DEFAULT_CAPACITY_BYTES = 1 * 1024 * 1024;
25
+ /**
26
+ * Default upper bound for how large a buffer may grow (256 MiB).
27
+ * Override per-object via the maxCapacityBytes option on create.
28
+ */
29
+ static MAX_CAPACITY_BYTES = 256 * 1024 * 1024;
30
+ /**
31
+ * Bytes reserved for the data-length header at the start of each buffer.
32
+ * @internal
33
+ */
34
+ static _HEADER_BYTES = 4;
35
+ /**
36
+ * Multiplier applied to the current capacity when growing or sizing a replacement buffer.
37
+ * @internal
38
+ */
39
+ static _GROW_FACTOR = 2;
40
+ /**
41
+ * Fraction of payload capacity below which a buffer is replaced with a smaller one.
42
+ * A value of 0.25 means the buffer is shrunk when the payload is below 25 % of capacity.
43
+ * Shrinking only applies when the capacity already exceeds DEFAULT_CAPACITY_BYTES.
44
+ * @internal
45
+ */
46
+ static _SHRINK_THRESHOLD = 0.25;
47
+ /**
48
+ * SharedStore key under which the per-object buffer map lives on each thread.
49
+ * @internal
50
+ */
51
+ static _STORE_KEY = "sharedObjectBuffers";
52
+ /**
53
+ * Cached worker_threads module; undefined = not yet loaded, null = unavailable.
54
+ * @internal
55
+ */
56
+ // false positive: this is a type not an actual import
57
+ // eslint-disable-next-line @typescript-eslint/consistent-type-imports
58
+ static _workerThreadsModule;
59
+ /**
60
+ * Create the buffer for the given objectId if it does not already exist.
61
+ * Must be called while holding Mutex.lock(objectId).
62
+ * @param objectId The object id that identifies the buffer.
63
+ * @param options Optional capacity configuration used when creating the buffer.
64
+ */
65
+ static async create(objectId, options) {
66
+ await SharedObjectBuffer.getOrFetchBuffer(objectId, options ?? {});
67
+ }
68
+ /**
69
+ * Read and decode the object stored for the given objectId.
70
+ * Must be called while holding Mutex.lock(objectId).
71
+ * @param objectId The object id that identifies the buffer.
72
+ * @returns The stored object, or undefined when nothing has been written yet.
73
+ */
74
+ static async read(objectId) {
75
+ const buf = await SharedObjectBuffer.getOrFetchBuffer(objectId);
76
+ if (Is.undefined(buf)) {
77
+ return undefined;
78
+ }
79
+ return SharedObjectBuffer.decode(buf);
80
+ }
81
+ /**
82
+ * Encode and write the object into the buffer for the given objectId.
83
+ * The buffer must already exist, usually by calling create first.
84
+ * When the encoded payload exceeds the current buffer capacity the buffer is grown
85
+ * in-place via SharedArrayBuffer.grow so every thread with a reference sees the
86
+ * new size without any pointer swap. When the payload is smaller than
87
+ * _SHRINK_THRESHOLD of the current capacity and the capacity exceeds
88
+ * DEFAULT_CAPACITY_BYTES, the buffer is replaced with a smaller one on the calling thread.
89
+ * Must be called while holding Mutex.lock(objectId).
90
+ * @param objectId The object id that identifies the buffer.
91
+ * @param value The object to persist.
92
+ */
93
+ static async write(objectId, value) {
94
+ const encoded = ObjectHelper.toBytes(value);
95
+ let buf = await SharedObjectBuffer.getOrFetchBuffer(objectId);
96
+ if (Is.undefined(buf)) {
97
+ throw new GeneralError(SharedObjectBuffer.CLASS_NAME, "notCreated", {
98
+ objectId
99
+ });
100
+ }
101
+ const payloadCapacity = buf.byteLength - SharedObjectBuffer._HEADER_BYTES;
102
+ const maxPayloadCapacity = buf.maxByteLength - SharedObjectBuffer._HEADER_BYTES;
103
+ if (encoded.length > payloadCapacity) {
104
+ // Grow the buffer in-place. SharedArrayBuffer.grow is atomic: all threads that
105
+ // hold a reference see the enlarged buffer without a pointer swap.
106
+ const newPayloadCapacity = Math.max(encoded.length, payloadCapacity * SharedObjectBuffer._GROW_FACTOR);
107
+ const newByteLength = Math.min(SharedObjectBuffer._HEADER_BYTES + newPayloadCapacity, buf.maxByteLength);
108
+ if (encoded.length > newByteLength - SharedObjectBuffer._HEADER_BYTES) {
109
+ throw new GeneralError(SharedObjectBuffer.CLASS_NAME, "capacityExceeded", {
110
+ objectId,
111
+ required: encoded.length,
112
+ capacity: maxPayloadCapacity
113
+ });
114
+ }
115
+ buf.grow(newByteLength);
116
+ }
117
+ else if (payloadCapacity > SharedObjectBuffer.DEFAULT_CAPACITY_BYTES &&
118
+ encoded.length < payloadCapacity * SharedObjectBuffer._SHRINK_THRESHOLD) {
119
+ // Replace with a smaller buffer when far below capacity. The caller writes
120
+ // the payload into the returned buffer immediately after.
121
+ buf = SharedObjectBuffer.shrinkBuffer(objectId, encoded.length, buf.maxByteLength);
122
+ }
123
+ new Uint8Array(buf, SharedObjectBuffer._HEADER_BYTES).set(encoded);
124
+ Atomics.store(new Int32Array(buf, 0, 1), 0, encoded.length);
125
+ }
126
+ /**
127
+ * Remove the stored object and release the buffer for the given objectId.
128
+ * The entry is deleted from the local cache so subsequent reads or writes will
129
+ * create or fetch a fresh buffer. Worker threads that have cached the old buffer
130
+ * reference continue using it until they restart or re-request via the worker protocol.
131
+ * Must be called while holding Mutex.lock(objectId).
132
+ * @param objectId The object id that identifies the buffer.
133
+ */
134
+ static remove(objectId) {
135
+ delete SharedObjectBuffer.getBuffers()[objectId];
136
+ }
137
+ /**
138
+ * Inspect a message from a worker thread and, if it is a SharedObjectBuffer
139
+ * buffer-fetch request, respond to it synchronously.
140
+ * Call this from the main thread's worker message handler alongside
141
+ * Mutex.handleWorkerMessage.
142
+ * @param msg The raw message received from the worker.
143
+ * @returns True if the message was a SharedObjectBuffer protocol message, false otherwise.
144
+ */
145
+ static handleWorkerMessage(msg) {
146
+ if (!Is.object(msg) ||
147
+ msg.type !== SharedObjectBufferMessageTypes.GetBuffer) {
148
+ return false;
149
+ }
150
+ const { objectId: objectName, port, signal, options } = msg;
151
+ const buf = Is.object(options)
152
+ ? SharedObjectBuffer.getOrCreateBuffer(objectName, options)
153
+ : SharedObjectBuffer.getBuffers()[objectName];
154
+ // Deliver the buffer before waking the worker so it is in the port's receive
155
+ // queue when Atomics.wait returns (same ordering guarantee as Mutex).
156
+ port.postMessage({ buffer: buf });
157
+ Atomics.notify(new Int32Array(signal), 0, 1);
158
+ port.close();
159
+ return true;
160
+ }
161
+ /**
162
+ * Decode the object from a SharedArrayBuffer.
163
+ * @param buf The SharedArrayBuffer to decode.
164
+ * @returns The decoded object, or undefined when nothing has been written.
165
+ * @internal
166
+ */
167
+ static decode(buf) {
168
+ const dataLen = Atomics.load(new Int32Array(buf, 0, 1), 0);
169
+ if (dataLen === 0) {
170
+ return undefined;
171
+ }
172
+ const bytes = new Uint8Array(buf, SharedObjectBuffer._HEADER_BYTES, dataLen);
173
+ return ObjectHelper.fromBytes(bytes);
174
+ }
175
+ /**
176
+ * Return the cached buffer for the object, creating or fetching it if needed.
177
+ * Applies a double-check after the async loadWorkerThreads call to handle the
178
+ * case where another coroutine populated the cache while this one yielded.
179
+ * @param objectId The object id that identifies the buffer.
180
+ * @param options Optional capacity configuration when creating the buffer.
181
+ * @returns The SharedArrayBuffer for the object.
182
+ * @internal
183
+ */
184
+ static async getOrFetchBuffer(objectId, options) {
185
+ const buffers = SharedObjectBuffer.getBuffers();
186
+ if (!Is.undefined(buffers[objectId])) {
187
+ return buffers[objectId];
188
+ }
189
+ const wt = await SharedObjectBuffer.loadWorkerThreads();
190
+ // Re-check after the await: another coroutine may have populated the cache.
191
+ if (!Is.undefined(buffers[objectId])) {
192
+ return buffers[objectId];
193
+ }
194
+ if (Is.empty(wt) || wt.isMainThread) {
195
+ if (!Is.object(options)) {
196
+ return buffers[objectId];
197
+ }
198
+ return SharedObjectBuffer.getOrCreateBuffer(objectId, options);
199
+ }
200
+ // Worker thread: synchronously request the SharedArrayBuffer from the main thread.
201
+ if (Is.empty(wt.parentPort)) {
202
+ throw new GeneralError(SharedObjectBuffer.CLASS_NAME, "bufferFetchFailed", { objectId });
203
+ }
204
+ const { port1, port2 } = new wt.MessageChannel();
205
+ const signal = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
206
+ const request = {
207
+ type: SharedObjectBufferMessageTypes.GetBuffer,
208
+ objectId,
209
+ signal: signal.buffer,
210
+ port: port2,
211
+ options
212
+ };
213
+ wt.parentPort.postMessage(request, [port2]);
214
+ try {
215
+ // Block until the main thread posts the buffer and fires Atomics.notify.
216
+ // The response is guaranteed to be in port1's queue when wait returns because
217
+ // port.postMessage executes before Atomics.notify on the main thread.
218
+ const waitResult = Atomics.wait(signal, 0, 0, 30_000);
219
+ if (waitResult === "timed-out") {
220
+ throw new GeneralError(SharedObjectBuffer.CLASS_NAME, "bufferFetchFailed", { objectId });
221
+ }
222
+ const response = wt.receiveMessageOnPort(port1);
223
+ if (Is.empty(response)) {
224
+ throw new GeneralError(SharedObjectBuffer.CLASS_NAME, "bufferFetchFailed", { objectId });
225
+ }
226
+ if (Is.undefined(response.message.buffer)) {
227
+ return undefined;
228
+ }
229
+ buffers[objectId] = response.message.buffer;
230
+ return buffers[objectId];
231
+ }
232
+ finally {
233
+ port1.close();
234
+ }
235
+ }
236
+ /**
237
+ * Get or create the growable buffer on the main thread (or in a fork-mode process).
238
+ * @param objectId The object id that identifies the buffer.
239
+ * @param options Optional capacity configuration for new buffers.
240
+ * @returns The existing or newly created SharedArrayBuffer.
241
+ * @internal
242
+ */
243
+ static getOrCreateBuffer(objectId, options) {
244
+ const buffers = SharedObjectBuffer.getBuffers();
245
+ if (Is.undefined(buffers[objectId])) {
246
+ const maxCapacity = options?.maxCapacityBytes ?? SharedObjectBuffer.MAX_CAPACITY_BYTES;
247
+ const initialCapacity = Math.min(options?.initialCapacityBytes ?? SharedObjectBuffer.DEFAULT_CAPACITY_BYTES, maxCapacity);
248
+ buffers[objectId] = new SharedArrayBuffer(SharedObjectBuffer._HEADER_BYTES + initialCapacity, {
249
+ maxByteLength: SharedObjectBuffer._HEADER_BYTES + maxCapacity
250
+ });
251
+ }
252
+ return buffers[objectId];
253
+ }
254
+ /**
255
+ * Create a new smaller SharedArrayBuffer, register it in SharedStore, and return it.
256
+ * The new buffer is empty; write writes the current payload into it immediately
257
+ * after this call returns. Callers on other threads that have cached the old buffer
258
+ * reference continue using it until they restart or re-request via the worker protocol.
259
+ * @param objectId The object id that identifies the buffer.
260
+ * @param dataLen Byte length of the payload that will be written next.
261
+ * @param maxByteLength The maxByteLength to preserve from the old buffer.
262
+ * @returns The new, smaller SharedArrayBuffer registered in SharedStore.
263
+ * @internal
264
+ */
265
+ static shrinkBuffer(objectId, dataLen, maxByteLength) {
266
+ const newCapacity = Math.max(dataLen * SharedObjectBuffer._GROW_FACTOR, SharedObjectBuffer.DEFAULT_CAPACITY_BYTES);
267
+ const newBuf = new SharedArrayBuffer(SharedObjectBuffer._HEADER_BYTES + newCapacity, {
268
+ maxByteLength
269
+ });
270
+ SharedObjectBuffer.getBuffers()[objectId] = newBuf;
271
+ return newBuf;
272
+ }
273
+ /**
274
+ * Return the per-thread buffer map from SharedStore, creating it if absent.
275
+ * @returns The map of object id to SharedArrayBuffer.
276
+ * @internal
277
+ */
278
+ static getBuffers() {
279
+ let buffers = SharedStore.get(SharedObjectBuffer._STORE_KEY);
280
+ if (Is.undefined(buffers)) {
281
+ buffers = {};
282
+ SharedStore.set(SharedObjectBuffer._STORE_KEY, buffers);
283
+ }
284
+ return buffers;
285
+ }
286
+ /**
287
+ * Lazily load node:worker_threads, returning null in environments where it is unavailable.
288
+ * @returns The worker_threads module or null.
289
+ * @internal
290
+ */
291
+ // false positive: this is a type not an actual import
292
+ // eslint-disable-next-line @typescript-eslint/consistent-type-imports
293
+ static async loadWorkerThreads() {
294
+ if (SharedObjectBuffer._workerThreadsModule === undefined) {
295
+ try {
296
+ SharedObjectBuffer._workerThreadsModule = await import("node:worker_threads");
297
+ }
298
+ catch {
299
+ SharedObjectBuffer._workerThreadsModule = null;
300
+ }
301
+ }
302
+ return SharedObjectBuffer._workerThreadsModule;
303
+ }
304
+ }
305
+ //# sourceMappingURL=sharedObjectBuffer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sharedObjectBuffer.js","sourceRoot":"","sources":["../../../src/utils/sharedObjectBuffer.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { MessagePort } from \"node:worker_threads\";\nimport { GeneralError, Is, ObjectHelper, SharedStore } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { ISharedObjectBufferOptions } from \"../models/ISharedObjectBufferOptions.js\";\nimport type { ISharedObjectBufferWorkerMessage } from \"../models/ISharedObjectBufferWorkerMessage.js\";\nimport { SharedObjectBufferMessageTypes } from \"../models/sharedObjectBufferMessageTypes.js\";\n\n/**\n * Manages per-object SharedArrayBuffers that store objects as UTF-8 JSON.\n * Buffer layout: 4-byte Int32 header (current data byte length) followed by the JSON-encoded object.\n * Buffers are explicitly created with create and cached in SharedStore.\n * On a worker thread an existing buffer is fetched via a MessagePort handshake\n * (same protocol as Mutex) and cached locally.\n * Buffers grow automatically when the payload exceeds capacity; when the payload drops well below\n * capacity the buffer is replaced with a smaller one. The caller must hold the objectId-keyed Mutex\n * around every read and write. The main thread's worker message handler must forward messages to\n * both Mutex.handleWorkerMessage and SharedObjectBuffer.handleWorkerMessage.\n */\nexport class SharedObjectBuffer {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<SharedObjectBuffer>();\n\n\t/**\n\t * Default payload capacity per object (1 MiB). The first caller that creates the buffer\n\t * for an object determines its initial capacity; later callers that pass a different value\n\t * are ignored.\n\t */\n\tpublic static readonly DEFAULT_CAPACITY_BYTES = 1 * 1024 * 1024;\n\n\t/**\n\t * Default upper bound for how large a buffer may grow (256 MiB).\n\t * Override per-object via the maxCapacityBytes option on create.\n\t */\n\tpublic static readonly MAX_CAPACITY_BYTES = 256 * 1024 * 1024;\n\n\t/**\n\t * Bytes reserved for the data-length header at the start of each buffer.\n\t * @internal\n\t */\n\tprivate static readonly _HEADER_BYTES = 4;\n\n\t/**\n\t * Multiplier applied to the current capacity when growing or sizing a replacement buffer.\n\t * @internal\n\t */\n\tprivate static readonly _GROW_FACTOR = 2;\n\n\t/**\n\t * Fraction of payload capacity below which a buffer is replaced with a smaller one.\n\t * A value of 0.25 means the buffer is shrunk when the payload is below 25 % of capacity.\n\t * Shrinking only applies when the capacity already exceeds DEFAULT_CAPACITY_BYTES.\n\t * @internal\n\t */\n\tprivate static readonly _SHRINK_THRESHOLD = 0.25;\n\n\t/**\n\t * SharedStore key under which the per-object buffer map lives on each thread.\n\t * @internal\n\t */\n\tprivate static readonly _STORE_KEY = \"sharedObjectBuffers\";\n\n\t/**\n\t * Cached worker_threads module; undefined = not yet loaded, null = unavailable.\n\t * @internal\n\t */\n\t// false positive: this is a type not an actual import\n\t// eslint-disable-next-line @typescript-eslint/consistent-type-imports\n\tprivate static _workerThreadsModule: typeof import(\"node:worker_threads\") | null | undefined;\n\n\t/**\n\t * Create the buffer for the given objectId if it does not already exist.\n\t * Must be called while holding Mutex.lock(objectId).\n\t * @param objectId The object id that identifies the buffer.\n\t * @param options Optional capacity configuration used when creating the buffer.\n\t */\n\tpublic static async create(\n\t\tobjectId: string,\n\t\toptions?: ISharedObjectBufferOptions\n\t): Promise<void> {\n\t\tawait SharedObjectBuffer.getOrFetchBuffer(objectId, options ?? {});\n\t}\n\n\t/**\n\t * Read and decode the object stored for the given objectId.\n\t * Must be called while holding Mutex.lock(objectId).\n\t * @param objectId The object id that identifies the buffer.\n\t * @returns The stored object, or undefined when nothing has been written yet.\n\t */\n\tpublic static async read<T>(objectId: string): Promise<T | undefined> {\n\t\tconst buf = await SharedObjectBuffer.getOrFetchBuffer(objectId);\n\t\tif (Is.undefined(buf)) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn SharedObjectBuffer.decode<T>(buf);\n\t}\n\n\t/**\n\t * Encode and write the object into the buffer for the given objectId.\n\t * The buffer must already exist, usually by calling create first.\n\t * When the encoded payload exceeds the current buffer capacity the buffer is grown\n\t * in-place via SharedArrayBuffer.grow so every thread with a reference sees the\n\t * new size without any pointer swap. When the payload is smaller than\n\t * _SHRINK_THRESHOLD of the current capacity and the capacity exceeds\n\t * DEFAULT_CAPACITY_BYTES, the buffer is replaced with a smaller one on the calling thread.\n\t * Must be called while holding Mutex.lock(objectId).\n\t * @param objectId The object id that identifies the buffer.\n\t * @param value The object to persist.\n\t */\n\tpublic static async write<T>(objectId: string, value: T): Promise<void> {\n\t\tconst encoded = ObjectHelper.toBytes(value);\n\t\tlet buf = await SharedObjectBuffer.getOrFetchBuffer(objectId);\n\n\t\tif (Is.undefined(buf)) {\n\t\t\tthrow new GeneralError(SharedObjectBuffer.CLASS_NAME, \"notCreated\", {\n\t\t\t\tobjectId\n\t\t\t});\n\t\t}\n\n\t\tconst payloadCapacity = buf.byteLength - SharedObjectBuffer._HEADER_BYTES;\n\t\tconst maxPayloadCapacity = buf.maxByteLength - SharedObjectBuffer._HEADER_BYTES;\n\n\t\tif (encoded.length > payloadCapacity) {\n\t\t\t// Grow the buffer in-place. SharedArrayBuffer.grow is atomic: all threads that\n\t\t\t// hold a reference see the enlarged buffer without a pointer swap.\n\t\t\tconst newPayloadCapacity = Math.max(\n\t\t\t\tencoded.length,\n\t\t\t\tpayloadCapacity * SharedObjectBuffer._GROW_FACTOR\n\t\t\t);\n\t\t\tconst newByteLength = Math.min(\n\t\t\t\tSharedObjectBuffer._HEADER_BYTES + newPayloadCapacity,\n\t\t\t\tbuf.maxByteLength\n\t\t\t);\n\t\t\tif (encoded.length > newByteLength - SharedObjectBuffer._HEADER_BYTES) {\n\t\t\t\tthrow new GeneralError(SharedObjectBuffer.CLASS_NAME, \"capacityExceeded\", {\n\t\t\t\t\tobjectId,\n\t\t\t\t\trequired: encoded.length,\n\t\t\t\t\tcapacity: maxPayloadCapacity\n\t\t\t\t});\n\t\t\t}\n\t\t\tbuf.grow(newByteLength);\n\t\t} else if (\n\t\t\tpayloadCapacity > SharedObjectBuffer.DEFAULT_CAPACITY_BYTES &&\n\t\t\tencoded.length < payloadCapacity * SharedObjectBuffer._SHRINK_THRESHOLD\n\t\t) {\n\t\t\t// Replace with a smaller buffer when far below capacity. The caller writes\n\t\t\t// the payload into the returned buffer immediately after.\n\t\t\tbuf = SharedObjectBuffer.shrinkBuffer(objectId, encoded.length, buf.maxByteLength);\n\t\t}\n\n\t\tnew Uint8Array(buf, SharedObjectBuffer._HEADER_BYTES).set(encoded);\n\t\tAtomics.store(new Int32Array(buf, 0, 1), 0, encoded.length);\n\t}\n\n\t/**\n\t * Remove the stored object and release the buffer for the given objectId.\n\t * The entry is deleted from the local cache so subsequent reads or writes will\n\t * create or fetch a fresh buffer. Worker threads that have cached the old buffer\n\t * reference continue using it until they restart or re-request via the worker protocol.\n\t * Must be called while holding Mutex.lock(objectId).\n\t * @param objectId The object id that identifies the buffer.\n\t */\n\tpublic static remove(objectId: string): void {\n\t\tdelete SharedObjectBuffer.getBuffers()[objectId];\n\t}\n\n\t/**\n\t * Inspect a message from a worker thread and, if it is a SharedObjectBuffer\n\t * buffer-fetch request, respond to it synchronously.\n\t * Call this from the main thread's worker message handler alongside\n\t * Mutex.handleWorkerMessage.\n\t * @param msg The raw message received from the worker.\n\t * @returns True if the message was a SharedObjectBuffer protocol message, false otherwise.\n\t */\n\tpublic static handleWorkerMessage(msg: unknown): boolean {\n\t\tif (\n\t\t\t!Is.object<ISharedObjectBufferWorkerMessage>(msg) ||\n\t\t\tmsg.type !== SharedObjectBufferMessageTypes.GetBuffer\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst {\n\t\t\tobjectId: objectName,\n\t\t\tport,\n\t\t\tsignal,\n\t\t\toptions\n\t\t} = msg as ISharedObjectBufferWorkerMessage & {\n\t\t\tport: MessagePort;\n\t\t};\n\n\t\tconst buf = Is.object(options)\n\t\t\t? SharedObjectBuffer.getOrCreateBuffer(objectName, options)\n\t\t\t: SharedObjectBuffer.getBuffers()[objectName];\n\n\t\t// Deliver the buffer before waking the worker so it is in the port's receive\n\t\t// queue when Atomics.wait returns (same ordering guarantee as Mutex).\n\t\tport.postMessage({ buffer: buf });\n\t\tAtomics.notify(new Int32Array(signal), 0, 1);\n\t\tport.close();\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Decode the object from a SharedArrayBuffer.\n\t * @param buf The SharedArrayBuffer to decode.\n\t * @returns The decoded object, or undefined when nothing has been written.\n\t * @internal\n\t */\n\tprivate static decode<T>(buf: SharedArrayBuffer): T | undefined {\n\t\tconst dataLen = Atomics.load(new Int32Array(buf, 0, 1), 0);\n\t\tif (dataLen === 0) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst bytes = new Uint8Array(buf, SharedObjectBuffer._HEADER_BYTES, dataLen);\n\t\treturn ObjectHelper.fromBytes<T>(bytes);\n\t}\n\n\t/**\n\t * Return the cached buffer for the object, creating or fetching it if needed.\n\t * Applies a double-check after the async loadWorkerThreads call to handle the\n\t * case where another coroutine populated the cache while this one yielded.\n\t * @param objectId The object id that identifies the buffer.\n\t * @param options Optional capacity configuration when creating the buffer.\n\t * @returns The SharedArrayBuffer for the object.\n\t * @internal\n\t */\n\tprivate static async getOrFetchBuffer(\n\t\tobjectId: string,\n\t\toptions?: ISharedObjectBufferOptions\n\t): Promise<SharedArrayBuffer | undefined> {\n\t\tconst buffers = SharedObjectBuffer.getBuffers();\n\n\t\tif (!Is.undefined(buffers[objectId])) {\n\t\t\treturn buffers[objectId];\n\t\t}\n\n\t\tconst wt = await SharedObjectBuffer.loadWorkerThreads();\n\n\t\t// Re-check after the await: another coroutine may have populated the cache.\n\t\tif (!Is.undefined(buffers[objectId])) {\n\t\t\treturn buffers[objectId];\n\t\t}\n\n\t\tif (Is.empty(wt) || wt.isMainThread) {\n\t\t\tif (!Is.object(options)) {\n\t\t\t\treturn buffers[objectId];\n\t\t\t}\n\t\t\treturn SharedObjectBuffer.getOrCreateBuffer(objectId, options);\n\t\t}\n\n\t\t// Worker thread: synchronously request the SharedArrayBuffer from the main thread.\n\t\tif (Is.empty(wt.parentPort)) {\n\t\t\tthrow new GeneralError(SharedObjectBuffer.CLASS_NAME, \"bufferFetchFailed\", { objectId });\n\t\t}\n\n\t\tconst { port1, port2 } = new wt.MessageChannel();\n\t\tconst signal = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));\n\n\t\tconst request: ISharedObjectBufferWorkerMessage = {\n\t\t\ttype: SharedObjectBufferMessageTypes.GetBuffer,\n\t\t\tobjectId,\n\t\t\tsignal: signal.buffer,\n\t\t\tport: port2,\n\t\t\toptions\n\t\t};\n\n\t\twt.parentPort.postMessage(request, [port2]);\n\n\t\ttry {\n\t\t\t// Block until the main thread posts the buffer and fires Atomics.notify.\n\t\t\t// The response is guaranteed to be in port1's queue when wait returns because\n\t\t\t// port.postMessage executes before Atomics.notify on the main thread.\n\t\t\tconst waitResult = Atomics.wait(signal, 0, 0, 30_000);\n\t\t\tif (waitResult === \"timed-out\") {\n\t\t\t\tthrow new GeneralError(SharedObjectBuffer.CLASS_NAME, \"bufferFetchFailed\", { objectId });\n\t\t\t}\n\n\t\t\tconst response = wt.receiveMessageOnPort(port1) as {\n\t\t\t\tmessage: { buffer?: SharedArrayBuffer };\n\t\t\t} | null;\n\n\t\t\tif (Is.empty(response)) {\n\t\t\t\tthrow new GeneralError(SharedObjectBuffer.CLASS_NAME, \"bufferFetchFailed\", { objectId });\n\t\t\t}\n\n\t\t\tif (Is.undefined(response.message.buffer)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tbuffers[objectId] = response.message.buffer;\n\t\t\treturn buffers[objectId];\n\t\t} finally {\n\t\t\tport1.close();\n\t\t}\n\t}\n\n\t/**\n\t * Get or create the growable buffer on the main thread (or in a fork-mode process).\n\t * @param objectId The object id that identifies the buffer.\n\t * @param options Optional capacity configuration for new buffers.\n\t * @returns The existing or newly created SharedArrayBuffer.\n\t * @internal\n\t */\n\tprivate static getOrCreateBuffer(\n\t\tobjectId: string,\n\t\toptions?: ISharedObjectBufferOptions\n\t): SharedArrayBuffer {\n\t\tconst buffers = SharedObjectBuffer.getBuffers();\n\t\tif (Is.undefined(buffers[objectId])) {\n\t\t\tconst maxCapacity = options?.maxCapacityBytes ?? SharedObjectBuffer.MAX_CAPACITY_BYTES;\n\t\t\tconst initialCapacity = Math.min(\n\t\t\t\toptions?.initialCapacityBytes ?? SharedObjectBuffer.DEFAULT_CAPACITY_BYTES,\n\t\t\t\tmaxCapacity\n\t\t\t);\n\t\t\tbuffers[objectId] = new SharedArrayBuffer(\n\t\t\t\tSharedObjectBuffer._HEADER_BYTES + initialCapacity,\n\t\t\t\t{\n\t\t\t\t\tmaxByteLength: SharedObjectBuffer._HEADER_BYTES + maxCapacity\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t\treturn buffers[objectId];\n\t}\n\n\t/**\n\t * Create a new smaller SharedArrayBuffer, register it in SharedStore, and return it.\n\t * The new buffer is empty; write writes the current payload into it immediately\n\t * after this call returns. Callers on other threads that have cached the old buffer\n\t * reference continue using it until they restart or re-request via the worker protocol.\n\t * @param objectId The object id that identifies the buffer.\n\t * @param dataLen Byte length of the payload that will be written next.\n\t * @param maxByteLength The maxByteLength to preserve from the old buffer.\n\t * @returns The new, smaller SharedArrayBuffer registered in SharedStore.\n\t * @internal\n\t */\n\tprivate static shrinkBuffer(\n\t\tobjectId: string,\n\t\tdataLen: number,\n\t\tmaxByteLength: number\n\t): SharedArrayBuffer {\n\t\tconst newCapacity = Math.max(\n\t\t\tdataLen * SharedObjectBuffer._GROW_FACTOR,\n\t\t\tSharedObjectBuffer.DEFAULT_CAPACITY_BYTES\n\t\t);\n\t\tconst newBuf = new SharedArrayBuffer(SharedObjectBuffer._HEADER_BYTES + newCapacity, {\n\t\t\tmaxByteLength\n\t\t});\n\t\tSharedObjectBuffer.getBuffers()[objectId] = newBuf;\n\t\treturn newBuf;\n\t}\n\n\t/**\n\t * Return the per-thread buffer map from SharedStore, creating it if absent.\n\t * @returns The map of object id to SharedArrayBuffer.\n\t * @internal\n\t */\n\tprivate static getBuffers(): { [objectId: string]: SharedArrayBuffer } {\n\t\tlet buffers = SharedStore.get<{ [objectId: string]: SharedArrayBuffer }>(\n\t\t\tSharedObjectBuffer._STORE_KEY\n\t\t);\n\t\tif (Is.undefined(buffers)) {\n\t\t\tbuffers = {};\n\t\t\tSharedStore.set(SharedObjectBuffer._STORE_KEY, buffers);\n\t\t}\n\t\treturn buffers;\n\t}\n\n\t/**\n\t * Lazily load node:worker_threads, returning null in environments where it is unavailable.\n\t * @returns The worker_threads module or null.\n\t * @internal\n\t */\n\t// false positive: this is a type not an actual import\n\t// eslint-disable-next-line @typescript-eslint/consistent-type-imports\n\tprivate static async loadWorkerThreads(): Promise<typeof import(\"node:worker_threads\") | null> {\n\t\tif (SharedObjectBuffer._workerThreadsModule === undefined) {\n\t\t\ttry {\n\t\t\t\tSharedObjectBuffer._workerThreadsModule = await import(\"node:worker_threads\");\n\t\t\t} catch {\n\t\t\t\tSharedObjectBuffer._workerThreadsModule = null;\n\t\t\t}\n\t\t}\n\t\treturn SharedObjectBuffer._workerThreadsModule;\n\t}\n}\n"]}
@@ -2,8 +2,8 @@
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
3
  import { Is } from "./is.js";
4
4
  /**
5
- * Provide a store for shared objects which can be accesses through multiple
6
- * instance loads of a packages.
5
+ * Provide a store for shared objects which can be accessed through multiple
6
+ * instance loads of a package.
7
7
  */
8
8
  export class SharedStore {
9
9
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"sharedStore.js","sourceRoot":"","sources":["../../../src/utils/sharedStore.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAE7B;;;GAGG;AACH,MAAM,OAAO,WAAW;IACvB;;;;OAIG;IACI,MAAM,CAAC,GAAG,CAAc,IAAY;QAC1C,8DAA8D;QAC9D,MAAM,MAAM,GAAI,UAAkB,CAAC,eAAe,CAAC;QACnD,IAAI,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,OAAO;QACR,CAAC;QAED,OAAO,MAAM,CAAC,IAAI,CAAM,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,GAAG,CAAc,IAAY,EAAE,KAAQ;QACpD,8DAA8D;QAC9D,IAAI,EAAE,CAAC,SAAS,CAAE,UAAkB,CAAC,eAAe,CAAC,EAAE,CAAC;YACvD,8DAA8D;YAC7D,UAAkB,CAAC,eAAe,GAAG,EAAE,CAAC;QAC1C,CAAC;QAED,8DAA8D;QAC7D,UAAkB,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;IACnD,CAAC;IAED;;;OAGG;IACI,MAAM,CAAC,MAAM,CAAC,IAAY;QAChC,8DAA8D;QAC9D,MAAM,MAAM,GAAI,UAAkB,CAAC,eAAe,CAAC;QACnD,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;IACF,CAAC;CACD","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Is } from \"./is.js\";\n\n/**\n * Provide a store for shared objects which can be accesses through multiple\n * instance loads of a packages.\n */\nexport class SharedStore {\n\t/**\n\t * Get a property from the shared store.\n\t * @param prop The name of the property to get.\n\t * @returns The property if it exists.\n\t */\n\tpublic static get<T = unknown>(prop: string): T | undefined {\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\tconst shared = (globalThis as any).__TWIN_SHARED__;\n\t\tif (Is.undefined(shared)) {\n\t\t\treturn;\n\t\t}\n\n\t\treturn shared[prop] as T;\n\t}\n\n\t/**\n\t * Set the property in the shared store.\n\t * @param prop The name of the property to set.\n\t * @param value The value to set.\n\t */\n\tpublic static set<T = unknown>(prop: string, value: T): void {\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\tif (Is.undefined((globalThis as any).__TWIN_SHARED__)) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t\t(globalThis as any).__TWIN_SHARED__ = {};\n\t\t}\n\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t(globalThis as any).__TWIN_SHARED__[prop] = value;\n\t}\n\n\t/**\n\t * Remove a property from the shared store.\n\t * @param prop The name of the property to remove.\n\t */\n\tpublic static remove(prop: string): void {\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\tconst shared = (globalThis as any).__TWIN_SHARED__;\n\t\tif (!Is.undefined(shared)) {\n\t\t\tdelete shared[prop];\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"sharedStore.js","sourceRoot":"","sources":["../../../src/utils/sharedStore.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAE7B;;;GAGG;AACH,MAAM,OAAO,WAAW;IACvB;;;;OAIG;IACI,MAAM,CAAC,GAAG,CAAc,IAAY;QAC1C,8DAA8D;QAC9D,MAAM,MAAM,GAAI,UAAkB,CAAC,eAAe,CAAC;QACnD,IAAI,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,OAAO;QACR,CAAC;QAED,OAAO,MAAM,CAAC,IAAI,CAAM,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,GAAG,CAAc,IAAY,EAAE,KAAQ;QACpD,8DAA8D;QAC9D,IAAI,EAAE,CAAC,SAAS,CAAE,UAAkB,CAAC,eAAe,CAAC,EAAE,CAAC;YACvD,8DAA8D;YAC7D,UAAkB,CAAC,eAAe,GAAG,EAAE,CAAC;QAC1C,CAAC;QAED,8DAA8D;QAC7D,UAAkB,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;IACnD,CAAC;IAED;;;OAGG;IACI,MAAM,CAAC,MAAM,CAAC,IAAY;QAChC,8DAA8D;QAC9D,MAAM,MAAM,GAAI,UAAkB,CAAC,eAAe,CAAC;QACnD,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;IACF,CAAC;CACD","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Is } from \"./is.js\";\n\n/**\n * Provide a store for shared objects which can be accessed through multiple\n * instance loads of a package.\n */\nexport class SharedStore {\n\t/**\n\t * Get a property from the shared store.\n\t * @param prop The name of the property to get.\n\t * @returns The property if it exists.\n\t */\n\tpublic static get<T = unknown>(prop: string): T | undefined {\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\tconst shared = (globalThis as any).__TWIN_SHARED__;\n\t\tif (Is.undefined(shared)) {\n\t\t\treturn;\n\t\t}\n\n\t\treturn shared[prop] as T;\n\t}\n\n\t/**\n\t * Set the property in the shared store.\n\t * @param prop The name of the property to set.\n\t * @param value The value to set.\n\t */\n\tpublic static set<T = unknown>(prop: string, value: T): void {\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\tif (Is.undefined((globalThis as any).__TWIN_SHARED__)) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t\t(globalThis as any).__TWIN_SHARED__ = {};\n\t\t}\n\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t(globalThis as any).__TWIN_SHARED__[prop] = value;\n\t}\n\n\t/**\n\t * Remove a property from the shared store.\n\t * @param prop The name of the property to remove.\n\t */\n\tpublic static remove(prop: string): void {\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\tconst shared = (globalThis as any).__TWIN_SHARED__;\n\t\tif (!Is.undefined(shared)) {\n\t\t\tdelete shared[prop];\n\t\t}\n\t}\n}\n"]}
@@ -14,7 +14,7 @@ export declare class ConflictError extends BaseError {
14
14
  * @param conflictId The id that has conflicts.
15
15
  * @param conflicts The conflicts that occurred.
16
16
  * @param properties Any additional information for the error.
17
- * @param cause The cause or the error if we have wrapped another error.
17
+ * @param cause The cause of the error if we have wrapped another error.
18
18
  */
19
19
  constructor(source: string, message: string, conflictId?: string, conflicts?: string[], properties?: {
20
20
  [id: string]: unknown;
@@ -1,6 +1,6 @@
1
1
  import { BaseError } from "./baseError.js";
2
2
  /**
3
- * Class to handle errors.
3
+ * Class to handle general-purpose errors.
4
4
  */
5
5
  export declare class GeneralError extends BaseError {
6
6
  /**
@@ -1,6 +1,6 @@
1
1
  import { BaseError } from "./baseError.js";
2
2
  /**
3
- * Class to handle errors.
3
+ * Class to handle errors raised when a method has not been implemented.
4
4
  */
5
5
  export declare class NotImplementedError extends BaseError {
6
6
  /**
@@ -5,7 +5,7 @@ import type { IValidationFailure } from "../models/IValidationFailure.js";
5
5
  */
6
6
  export declare class ValidationError extends BaseError {
7
7
  /**
8
- * Runtime name for the class.s
8
+ * Runtime name for the class.
9
9
  */
10
10
  static readonly CLASS_NAME: string;
11
11
  /**
@@ -21,6 +21,12 @@ export declare class Factory<T> {
21
21
  static getFactories(): {
22
22
  [typeName: string]: Factory<unknown>;
23
23
  };
24
+ /**
25
+ * Get a specific factory by type name.
26
+ * @param typeName The type name of the factory.
27
+ * @returns The factory instance if it exists, otherwise undefined.
28
+ */
29
+ static getFactory<T = unknown>(typeName: string): Factory<T> | undefined;
24
30
  /**
25
31
  * Reset all the factories, which removes any created instances, but not the registrations.
26
32
  */
@@ -6,7 +6,7 @@ export declare class ArrayHelper {
6
6
  * Do the two arrays match.
7
7
  * @param arr1 The first array.
8
8
  * @param arr2 The second array.
9
- * @returns True if both arrays are empty of have the same values.
9
+ * @returns True if both arrays are empty or have the same values.
10
10
  */
11
11
  static matches(arr1: unknown, arr2: unknown): boolean;
12
12
  /**
@@ -4,7 +4,7 @@ import type { IError } from "../models/IError.js";
4
4
  */
5
5
  export declare class ErrorHelper {
6
6
  /**
7
- * Format Errors and returns just their messages.
7
+ * Format errors and returns just their messages.
8
8
  * @param error The error to format.
9
9
  * @param options Options for formatting the error.
10
10
  * @param options.includeStack Whether to include the stack trace in the output, defaults to false.
@@ -26,7 +26,7 @@ export declare class JsonHelper {
26
26
  * Applies a RFC 6902 diff set to an object.
27
27
  * Based on https://www.rfc-editor.org/rfc/rfc6902.
28
28
  * @param object The object to patch.
29
- * @param patches The second object.
29
+ * @param patches The patch operations to apply.
30
30
  * @returns The updated object.
31
31
  * @throws GeneralError if the patch fails.
32
32
  */
@@ -99,13 +99,13 @@ export declare class ObjectHelper {
99
99
  */
100
100
  static omit<T, K extends keyof T>(obj: T | undefined, keys?: K[]): Omit<T, K> | undefined;
101
101
  /**
102
- * Converter the non JSON primitives to extended types.
102
+ * Convert the non JSON primitives to extended types.
103
103
  * @param obj The object to convert.
104
104
  * @returns The object with extended properties.
105
105
  */
106
106
  static toExtended(obj: any): any;
107
107
  /**
108
- * Converter the extended types to non JSON primitives.
108
+ * Convert the extended types to non JSON primitives.
109
109
  * @param obj The object to convert.
110
110
  * @returns The object with regular properties.
111
111
  */
@@ -76,8 +76,8 @@ export declare class StringHelper {
76
76
  static words(input: string): string[];
77
77
  /**
78
78
  * Check if a Node.js Buffer or Uint8Array is UTF-8.
79
- * Url https://tools.ietf.org/html/rfc3629
80
- * Source https://github.com/hcodes/isutf8
79
+ * @see https://tools.ietf.org/html/rfc3629
80
+ * @see https://github.com/hcodes/isutf8
81
81
  * UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4.
82
82
  * UTF8-1 = %x00-7F.
83
83
  * UTF8-2 = %xC2-DF UTF8-tail.
@@ -40,9 +40,12 @@ export * from "./models/ILocaleDictionary.js";
40
40
  export * from "./models/ILocalesIndex.js";
41
41
  export * from "./models/IMutexWorkerMessage.js";
42
42
  export * from "./models/IPatchOperation.js";
43
+ export * from "./models/ISharedObjectBufferOptions.js";
44
+ export * from "./models/ISharedObjectBufferWorkerMessage.js";
43
45
  export * from "./models/IUrlParts.js";
44
46
  export * from "./models/IValidationFailure.js";
45
47
  export * from "./models/mutexMessageTypes.js";
48
+ export * from "./models/sharedObjectBufferMessageTypes.js";
46
49
  export * from "./types/bitString.js";
47
50
  export * from "./types/objectOrArray.js";
48
51
  export * from "./types/singleOccurrenceArray.js";
@@ -57,5 +60,6 @@ export * from "./utils/guards.js";
57
60
  export * from "./utils/i18n.js";
58
61
  export * from "./utils/is.js";
59
62
  export * from "./utils/mutex.js";
63
+ export * from "./utils/sharedObjectBuffer.js";
60
64
  export * from "./utils/sharedStore.js";
61
65
  export * from "./utils/validation.js";
@@ -23,13 +23,13 @@ export interface IComponent {
23
23
  /**
24
24
  * The component needs to be started when the node is initialized.
25
25
  * @param nodeLoggingComponentType The node logging component type.
26
- * @returns Nothing.
26
+ * @returns A promise that resolves when the component has started.
27
27
  */
28
28
  start?(nodeLoggingComponentType?: string): Promise<void>;
29
29
  /**
30
30
  * The component needs to be stopped when the node is closed.
31
31
  * @param nodeLoggingComponentType The node logging component type.
32
- * @returns Nothing.
32
+ * @returns A promise that resolves when the component has stopped.
33
33
  */
34
34
  stop?(nodeLoggingComponentType?: string): Promise<void>;
35
35
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Model for a local.
2
+ * Model for a locale.
3
3
  */
4
4
  export interface ILocale {
5
5
  /**
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Options for configuring buffer capacity when creating a shared object buffer.
3
+ */
4
+ export interface ISharedObjectBufferOptions {
5
+ /**
6
+ * Initial payload capacity hint in bytes.
7
+ * Only honoured when the buffer does not yet exist; ignored on subsequent writes.
8
+ * @default 1 MiB.
9
+ */
10
+ initialCapacityBytes?: number;
11
+ /**
12
+ * Maximum allowed payload capacity in bytes. The buffer will never grow beyond this limit.
13
+ * Only honoured when the buffer does not yet exist; ignored on subsequent writes.
14
+ * @default 256 MiB.
15
+ */
16
+ maxCapacityBytes?: number;
17
+ }
@@ -0,0 +1,29 @@
1
+ import type { MessagePort } from "node:worker_threads";
2
+ import type { ISharedObjectBufferOptions } from "./ISharedObjectBufferOptions.js";
3
+ import type { SharedObjectBufferMessageTypes } from "./sharedObjectBufferMessageTypes.js";
4
+ /**
5
+ * Message sent from a worker thread to the main thread to request the SharedArrayBuffer for an object.
6
+ */
7
+ export interface ISharedObjectBufferWorkerMessage {
8
+ /**
9
+ * The message type discriminant.
10
+ */
11
+ type: typeof SharedObjectBufferMessageTypes.GetBuffer;
12
+ /**
13
+ * The object id name that identifies which buffer is being requested.
14
+ */
15
+ objectId: string;
16
+ /**
17
+ * One-shot SharedArrayBuffer used for the Atomics.wait/notify handshake so the
18
+ * worker can block synchronously until the main thread has posted the response.
19
+ */
20
+ signal: SharedArrayBuffer;
21
+ /**
22
+ * MessagePort through which the main thread returns the object buffer.
23
+ */
24
+ port: MessagePort;
25
+ /**
26
+ * Options for creating or fetching the buffer.
27
+ */
28
+ options?: ISharedObjectBufferOptions;
29
+ }
@@ -7,7 +7,7 @@ export interface IValidationFailure {
7
7
  */
8
8
  property: string;
9
9
  /**
10
- * The reason the validation failed as an i18 resource error.
10
+ * The reason the validation failed as an i18n resource key.
11
11
  */
12
12
  reason: string;
13
13
  /**
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Message type constants for the SharedObjectBuffer worker-to-main-thread protocol.
3
+ */
4
+ export declare const SharedObjectBufferMessageTypes: {
5
+ /**
6
+ * Worker requests the SharedArrayBuffer for a named object from the main thread.
7
+ */
8
+ readonly GetBuffer: "twin:sharedObjectBuffer:getBuffer";
9
+ };
10
+ /**
11
+ * Union of all SharedObjectBuffer message type strings.
12
+ */
13
+ export type SharedObjectBufferMessageTypes = (typeof SharedObjectBufferMessageTypes)[keyof typeof SharedObjectBufferMessageTypes];
@@ -23,7 +23,7 @@ export declare class AsyncCache {
23
23
  * @param key The key to set in the cache.
24
24
  * @param value The value to set in the cache.
25
25
  * @param ttlMs The TTL of the entry in the cache in milliseconds. Defaults to 1000 (1 second).
26
- * @returns Nothing.
26
+ * @returns A promise that resolves when the entry has been stored.
27
27
  */
28
28
  static set<T = unknown>(key: string, value: T, ttlMs?: number): Promise<void>;
29
29
  /**
@@ -40,11 +40,4 @@ export declare class AsyncCache {
40
40
  * Perform a cleanup of the expired entries in the cache.
41
41
  */
42
42
  static cleanupExpired(): void;
43
- /**
44
- * Resolve a waiter by re-running its request method safely.
45
- * @param requestMethod The method to execute.
46
- * @param resolve The resolver for the waiter.
47
- * @param reject The rejector for the waiter.
48
- */
49
- private static resolveWaiter;
50
43
  }