@twin.org/core 0.0.4-next.9 → 0.9.0

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 (46) hide show
  1. package/dist/es/index.js +3 -0
  2. package/dist/es/index.js.map +1 -1
  3. package/dist/es/models/IDuration.js +4 -0
  4. package/dist/es/models/IDuration.js.map +1 -0
  5. package/dist/es/models/coerceType.js +5 -1
  6. package/dist/es/models/coerceType.js.map +1 -1
  7. package/dist/es/types/duration.js +184 -0
  8. package/dist/es/types/duration.js.map +1 -0
  9. package/dist/es/types/durationRegExp.js +4 -0
  10. package/dist/es/types/durationRegExp.js.map +1 -0
  11. package/dist/es/types/url.js +1 -0
  12. package/dist/es/types/url.js.map +1 -1
  13. package/dist/es/utils/coerce.js +24 -0
  14. package/dist/es/utils/coerce.js.map +1 -1
  15. package/dist/es/utils/guards.js +12 -0
  16. package/dist/es/utils/guards.js.map +1 -1
  17. package/dist/es/utils/is.js +56 -7
  18. package/dist/es/utils/is.js.map +1 -1
  19. package/dist/es/utils/mutex.js +49 -8
  20. package/dist/es/utils/mutex.js.map +1 -1
  21. package/dist/es/utils/sharedObjectBuffer.js +4 -1
  22. package/dist/es/utils/sharedObjectBuffer.js.map +1 -1
  23. package/dist/types/index.d.ts +3 -0
  24. package/dist/types/models/IDuration.d.ts +45 -0
  25. package/dist/types/models/coerceType.d.ts +4 -0
  26. package/dist/types/types/duration.d.ts +29 -0
  27. package/dist/types/types/durationRegExp.d.ts +1 -0
  28. package/dist/types/types/url.d.ts +1 -0
  29. package/dist/types/utils/coerce.d.ts +9 -0
  30. package/dist/types/utils/guards.d.ts +9 -0
  31. package/dist/types/utils/is.d.ts +7 -0
  32. package/dist/types/utils/mutex.d.ts +12 -1
  33. package/docs/changelog.md +271 -0
  34. package/docs/reference/classes/Coerce.md +24 -0
  35. package/docs/reference/classes/Duration.md +88 -0
  36. package/docs/reference/classes/Guards.md +36 -0
  37. package/docs/reference/classes/Is.md +22 -0
  38. package/docs/reference/classes/Mutex.md +39 -1
  39. package/docs/reference/classes/ObjectHelper.md +8 -0
  40. package/docs/reference/classes/Url.md +4 -0
  41. package/docs/reference/index.md +3 -0
  42. package/docs/reference/interfaces/IDuration.md +83 -0
  43. package/docs/reference/variables/CoerceType.md +6 -0
  44. package/docs/reference/variables/DURATION_REG_EXP.md +3 -0
  45. package/locales/en.json +4 -2
  46. package/package.json +3 -3
@@ -1 +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"]}
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 { nameof } from \"@twin.org/nameof\";\nimport { Is } from \"./is.js\";\nimport { SharedStore } from \"./sharedStore.js\";\nimport { GeneralError } from \"../errors/generalError.js\";\nimport { ObjectHelper } from \"../helpers/objectHelper.js\";\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"]}
@@ -27,6 +27,7 @@ export * from "./helpers/randomHelper.js";
27
27
  export * from "./helpers/stringHelper.js";
28
28
  export * from "./helpers/uint8ArrayHelper.js";
29
29
  export * from "./models/coerceType.js";
30
+ export * from "./models/IDuration.js";
30
31
  export * from "./models/compressionType.js";
31
32
  export * from "./models/healthStatus.js";
32
33
  export * from "./models/IComponent.js";
@@ -47,6 +48,8 @@ export * from "./models/IValidationFailure.js";
47
48
  export * from "./models/mutexMessageTypes.js";
48
49
  export * from "./models/sharedObjectBufferMessageTypes.js";
49
50
  export * from "./types/bitString.js";
51
+ export * from "./types/duration.js";
52
+ export * from "./types/durationRegExp.js";
50
53
  export * from "./types/objectOrArray.js";
51
54
  export * from "./types/singleOccurrenceArray.js";
52
55
  export * from "./types/singleOccurrenceArrayDepthHelper.js";
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Represents a duration broken down into its component parts.
3
+ */
4
+ export interface IDuration {
5
+ /**
6
+ * The number of years.
7
+ */
8
+ years: number;
9
+ /**
10
+ * The number of months.
11
+ */
12
+ months: number;
13
+ /**
14
+ * The number of weeks.
15
+ */
16
+ weeks: number;
17
+ /**
18
+ * The number of days.
19
+ */
20
+ days: number;
21
+ /**
22
+ * The number of hours.
23
+ */
24
+ hours: number;
25
+ /**
26
+ * The number of minutes.
27
+ */
28
+ minutes: number;
29
+ /**
30
+ * The number of seconds.
31
+ */
32
+ seconds: number;
33
+ /**
34
+ * The number of milliseconds.
35
+ */
36
+ milliseconds?: number;
37
+ /**
38
+ * The number of microseconds.
39
+ */
40
+ microseconds?: number;
41
+ /**
42
+ * The number of nanoseconds.
43
+ */
44
+ nanoseconds?: number;
45
+ }
@@ -42,6 +42,10 @@ export declare const CoerceType: {
42
42
  * Uint8Array.
43
43
  */
44
44
  readonly Uint8Array: "uint8array";
45
+ /**
46
+ * Duration in seconds.
47
+ */
48
+ readonly Duration: "duration";
45
49
  };
46
50
  /**
47
51
  * The types the extracted data can be coerced to.
@@ -0,0 +1,29 @@
1
+ import type { IDuration } from "../models/IDuration.js";
2
+ /**
3
+ * Helper methods for working with ISO 8601 durations.
4
+ */
5
+ export declare class Duration {
6
+ /**
7
+ * Runtime name for the class.
8
+ */
9
+ static readonly CLASS_NAME: string;
10
+ /**
11
+ * Parse an ISO 8601 duration string into its component parts.
12
+ * @param value The string to parse.
13
+ * @returns The parsed duration, or undefined if the string is not a valid ISO 8601 duration.
14
+ */
15
+ static parse(value: string): IDuration | undefined;
16
+ /**
17
+ * Convert a duration object to an ISO 8601 duration string.
18
+ * @param duration The duration to convert.
19
+ * @returns The ISO 8601 duration string (e.g. "P1Y2M3DT4H5M6S").
20
+ */
21
+ static toString(duration: IDuration): string;
22
+ /**
23
+ * Convert a duration object to total seconds.
24
+ * Year and month components use the average values 365.25 days and 30.4375 days.
25
+ * @param duration The duration to convert.
26
+ * @returns The total number of seconds.
27
+ */
28
+ static toSeconds(duration: IDuration): number;
29
+ }
@@ -0,0 +1 @@
1
+ export declare const DURATION_REG_EXP: RegExp;
@@ -11,6 +11,7 @@ export declare class Url {
11
11
  /**
12
12
  * Create a new instance of Url.
13
13
  * @param url The url string.
14
+ * @throws GuardError if the url is not valid.
14
15
  */
15
16
  constructor(url: string);
16
17
  /**
@@ -1,4 +1,5 @@
1
1
  import { CoerceType } from "../models/coerceType.js";
2
+ import type { IDuration } from "../models/IDuration.js";
2
3
  /**
3
4
  * Coerce an object from one type to another.
4
5
  */
@@ -51,6 +52,14 @@ export declare class Coerce {
51
52
  * @returns The coerced time, or undefined if the value cannot be coerced.
52
53
  */
53
54
  static time(value: unknown): Date | undefined;
55
+ /**
56
+ * Coerce the value to a duration object.
57
+ * Accepts an IDuration object, ISO 8601 duration strings (e.g. "PT1H", "P1Y2M3DT4H5M6S"),
58
+ * or numeric values already expressed as seconds (stored in the seconds field).
59
+ * @param value The value to coerce.
60
+ * @returns The duration object, or undefined if the value cannot be coerced.
61
+ */
62
+ static duration(value: unknown): IDuration | undefined;
54
63
  /**
55
64
  * Coerce the value to an object.
56
65
  * @param value The value to coerce.
@@ -1,3 +1,4 @@
1
+ import type { IDuration } from "../models/IDuration.js";
1
2
  import type { ObjectOrArray } from "../types/objectOrArray.js";
2
3
  /**
3
4
  * Class to handle guard operations for parameters.
@@ -254,4 +255,12 @@ export declare class Guards {
254
255
  * @throws GuardError If the value does not match the assertion.
255
256
  */
256
257
  static uuidV7(source: string, property: string, value: unknown, format?: "standard" | "compact"): asserts value is string;
258
+ /**
259
+ * Is the property a valid ISO 8601 duration string or IDuration object.
260
+ * @param source The source of the error.
261
+ * @param property The name of the property.
262
+ * @param value The value to test.
263
+ * @throws GuardError If the value does not match the assertion.
264
+ */
265
+ static duration(source: string, property: string, value: unknown): asserts value is string | IDuration;
257
266
  }
@@ -1,3 +1,4 @@
1
+ import type { IDuration } from "../models/IDuration.js";
1
2
  /**
2
3
  * Class to check types of objects.
3
4
  */
@@ -227,4 +228,10 @@ export declare class Is {
227
228
  * @returns True if the value is a uuidV7 string.
228
229
  */
229
230
  static uuidV7(value: unknown, format?: "standard" | "compact"): value is string;
231
+ /**
232
+ * Is the value a valid ISO 8601 duration string or an IDuration object.
233
+ * @param value The value to test.
234
+ * @returns True if the value is a valid ISO 8601 duration string or an IDuration object.
235
+ */
236
+ static duration(value: unknown): value is string | IDuration;
230
237
  }
@@ -19,6 +19,17 @@ export declare class Mutex {
19
19
  * Runtime name for the class.
20
20
  */
21
21
  static readonly CLASS_NAME: string;
22
+ /**
23
+ * Gets the default timeout in milliseconds for lock acquisition.
24
+ * @returns The default timeout in milliseconds.
25
+ */
26
+ static getDefaultTimeoutMs(): number;
27
+ /**
28
+ * Sets the default timeout in milliseconds for lock acquisition.
29
+ * @param timeoutMs The default timeout in milliseconds.
30
+ * @throws GeneralError if timeoutMs is not a non-negative integer.
31
+ */
32
+ static setDefaultTimeoutMs(timeoutMs: number): void;
22
33
  /**
23
34
  * Acquires a lock for the given key without blocking the event loop. If the lock is already
24
35
  * held, it suspends the current async task until the lock is released or the timeout is reached.
@@ -28,7 +39,7 @@ export declare class Mutex {
28
39
  * the same key, it will suspend until the timeout elapses.
29
40
  * @param key The key to lock on.
30
41
  * @param options Lock options.
31
- * @param options.timeoutMs The maximum time to wait for the lock in milliseconds, default is 5000.
42
+ * @param options.timeoutMs The maximum time to wait for the lock in milliseconds, defaults to getDefaultTimeoutMs().
32
43
  * @param options.throwOnTimeout Whether to throw an error if the lock could not be acquired within the timeout, default is false.
33
44
  * @returns True if the lock was acquired, false if it timed out and throwOnTimeout is false.
34
45
  * @throws GeneralError if the key is invalid or if the lock could not be acquired within the timeout and throwOnTimeout is true.
package/docs/changelog.md CHANGED
@@ -1,5 +1,276 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.0](https://github.com/iotaledger/twin-framework/compare/core-v0.9.0...core-v0.9.0) (2026-06-22)
4
+
5
+
6
+ ### Features
7
+
8
+ * release to production ([b24cba1](https://github.com/iotaledger/twin-framework/commit/b24cba1b6a969278d638e632590602ec881e49fb))
9
+ * release to production ([787287d](https://github.com/iotaledger/twin-framework/commit/787287d06ea8319657401589d61fff369310c422))
10
+ * release to production ([53f4843](https://github.com/iotaledger/twin-framework/commit/53f484326b2851d7a506d2620db24c4a65cee7b3))
11
+ * release to production ([56cda4d](https://github.com/iotaledger/twin-framework/commit/56cda4da93e978c5be19ec7cfd421ae2a7fe4147))
12
+ * release to production ([f7c6586](https://github.com/iotaledger/twin-framework/commit/f7c6586f6976b903b647b4c5ac5ad9421e0c9051))
13
+ * release to production ([829d53d](https://github.com/iotaledger/twin-framework/commit/829d53d3953b1e1b40b0243c04cfdfd3842aac7b))
14
+ * release to production ([5cf3a76](https://github.com/iotaledger/twin-framework/commit/5cf3a76a09eff2e6414d0cba846c7c37400a11d6))
15
+ * release to production ([#330](https://github.com/iotaledger/twin-framework/issues/330)) ([d73f565](https://github.com/iotaledger/twin-framework/commit/d73f565588d156d23ef49b2a5718973756f7a696))
16
+ * release to production ([#382](https://github.com/iotaledger/twin-framework/issues/382)) ([bbed01a](https://github.com/iotaledger/twin-framework/commit/bbed01a605ee9724bda77a0f7feab249118c2d90))
17
+
18
+
19
+ ### Miscellaneous Chores
20
+
21
+ * release to production ([63cae24](https://github.com/iotaledger/twin-framework/commit/63cae2401f6c11f93b2a01260b665064e8bd28e0))
22
+
23
+ ## [0.9.0-next.1](https://github.com/iotaledger/twin-framework/compare/core-v0.9.0-next.0...core-v0.9.0-next.1) (2026-06-22)
24
+
25
+
26
+ ### Features
27
+
28
+ * add context id features ([#206](https://github.com/iotaledger/twin-framework/issues/206)) ([ef0d4ee](https://github.com/iotaledger/twin-framework/commit/ef0d4ee11a4f5fc6cc6f52a4958ce905c04ee13b))
29
+ * add duration support ([#362](https://github.com/iotaledger/twin-framework/issues/362)) ([24dbe8b](https://github.com/iotaledger/twin-framework/commit/24dbe8b208eb127a52024cb219d31d27731e9ae3))
30
+ * add factory.createIfExists ([aad5a53](https://github.com/iotaledger/twin-framework/commit/aad5a53cef1b1c2e04344ea46244d41e371dff9b))
31
+ * add Factory.getFactory ([ad6cd2d](https://github.com/iotaledger/twin-framework/commit/ad6cd2d30351c145c70212f8cce1b66d5e8a5235))
32
+ * add guard.dateString ([#335](https://github.com/iotaledger/twin-framework/issues/335)) ([a26a166](https://github.com/iotaledger/twin-framework/commit/a26a166eb1f62ece05b2432730cb7354feacecfc))
33
+ * add health method to components ([a88016d](https://github.com/iotaledger/twin-framework/commit/a88016d90d172413e5bc5238dc1b3e35f82fcc2c))
34
+ * add Is.class method ([4988205](https://github.com/iotaledger/twin-framework/commit/498820543e256a130b4888c958fe1d87ca865d7f))
35
+ * add isDefault option to factory registration ([a8a700b](https://github.com/iotaledger/twin-framework/commit/a8a700bb8ddaf7dd5097869a358b8fc5f7c40ce7))
36
+ * add mutex ([#316](https://github.com/iotaledger/twin-framework/issues/316)) ([1027e5a](https://github.com/iotaledger/twin-framework/commit/1027e5ac77aa9804178b4253abdeefd6f1c3d521))
37
+ * add objectHelper.split ([386830a](https://github.com/iotaledger/twin-framework/commit/386830a77f8e842a5b119be0983708e042c3b14b))
38
+ * add partial second support ([39bf087](https://github.com/iotaledger/twin-framework/commit/39bf087ddc113a9bf73e0d0e99f738f6f60c2f2d))
39
+ * add rsa cipher support ([7af6cc6](https://github.com/iotaledger/twin-framework/commit/7af6cc67512d3363bd4a2f2e87bd7733c2800147))
40
+ * add single occurrence array type ([e890e43](https://github.com/iotaledger/twin-framework/commit/e890e4399e75ae5097f3ad8b1007321cbb1ed4ac))
41
+ * add single occurrence array type ([#245](https://github.com/iotaledger/twin-framework/issues/245)) ([771dc78](https://github.com/iotaledger/twin-framework/commit/771dc78025b5546c9c9681be9494a76ab71171c6))
42
+ * add support for health i18n validation ([7a286dd](https://github.com/iotaledger/twin-framework/commit/7a286ddb0c1bfa498bf3a77126cd589042bad6de))
43
+ * add teardown to IComponent interface ([32c173c](https://github.com/iotaledger/twin-framework/commit/32c173cda115c20d3b4cee14b8a29cdc08e91555))
44
+ * add teardown to IComponent interface ([98ce864](https://github.com/iotaledger/twin-framework/commit/98ce8648e709310c4435de334825b381fb4e32cb))
45
+ * add typeName method to factory ([699fcbd](https://github.com/iotaledger/twin-framework/commit/699fcbd1168228401ddb81fdacb959b8cdc4206a))
46
+ * add uuidv7 support ([#219](https://github.com/iotaledger/twin-framework/issues/219)) ([916c657](https://github.com/iotaledger/twin-framework/commit/916c657d270ce99fafe554233739fdd9ca28635b))
47
+ * adding link header helper ([#225](https://github.com/iotaledger/twin-framework/issues/225)) ([703c072](https://github.com/iotaledger/twin-framework/commit/703c0725aceac6b6ec0c4fa729ef832d12fb3fd7))
48
+ * additional nameof operators ([a5aab60](https://github.com/iotaledger/twin-framework/commit/a5aab60bf66a86f1b7ff8af7c4f044cb03706d50))
49
+ * additional RSA methods and async ([1fceee2](https://github.com/iotaledger/twin-framework/commit/1fceee2d1248a24a7620846025fcf906495c07f4))
50
+ * bump version ([c5354fa](https://github.com/iotaledger/twin-framework/commit/c5354fa906f4493c70f2642a9175a66780a10636))
51
+ * configurable timeout for mutex ([2087e04](https://github.com/iotaledger/twin-framework/commit/2087e04464a110a3f009fbc8b066696ed6784fc2))
52
+ * eslint migration to flat config ([74427d7](https://github.com/iotaledger/twin-framework/commit/74427d78d342167f7850e49ab87269326355befe))
53
+ * expand error params to accept properties ([032e9fd](https://github.com/iotaledger/twin-framework/commit/032e9fd1388e457cde32ca1005dfe014a5a9c077))
54
+ * factory create and integrity ([#235](https://github.com/iotaledger/twin-framework/issues/235)) ([9f98b99](https://github.com/iotaledger/twin-framework/commit/9f98b99daf46eb365346fae49cc4ffba63e74cb3))
55
+ * health status grouping ([5007c29](https://github.com/iotaledger/twin-framework/commit/5007c29fe4123fe56f754450b993dbe5dc32a057))
56
+ * improve async cache edge cases ([4e57a6e](https://github.com/iotaledger/twin-framework/commit/4e57a6ec4113533b0ea903eae7d469200fa1348c))
57
+ * improve base error data extraction ([dccc933](https://github.com/iotaledger/twin-framework/commit/dccc93361a1544b41db0e7c126ff90e858d87960))
58
+ * improve error formatting ([#313](https://github.com/iotaledger/twin-framework/issues/313)) ([5a19623](https://github.com/iotaledger/twin-framework/commit/5a196231bcbf088bf9ba92a93b7478d3b8c5593f))
59
+ * improve Is.function and ModuleHelper.getModuleMethod signatures ([ecf968b](https://github.com/iotaledger/twin-framework/commit/ecf968b02934b3676be4bf7cd2d1e7f8e7af6ce2))
60
+ * improve Is.function definition to retain types ([f20b6b0](https://github.com/iotaledger/twin-framework/commit/f20b6b0dd16e74b75dc359be72b05989305c6410))
61
+ * improve signatures ([45a7d58](https://github.com/iotaledger/twin-framework/commit/45a7d58baa5b6abc8c0735656f393d402fabb4ad))
62
+ * improve signatures ([cdd24be](https://github.com/iotaledger/twin-framework/commit/cdd24be6fb898d33955b6f2f93c3ddbd73582269))
63
+ * improve signatures ([2041ae2](https://github.com/iotaledger/twin-framework/commit/2041ae214cbd8d472d5e8be8d3403cd06a114040))
64
+ * improve signatures ([d8a0ee1](https://github.com/iotaledger/twin-framework/commit/d8a0ee16542134a3f8b0653065239f42045125b4))
65
+ * improve signatures ([1d084c5](https://github.com/iotaledger/twin-framework/commit/1d084c58c7eb41ae50b0ce80ce57e48a22dd3102))
66
+ * improve signatures ([#287](https://github.com/iotaledger/twin-framework/issues/287)) ([c9f38f0](https://github.com/iotaledger/twin-framework/commit/c9f38f00e1cea8759e1105b41872a650c66c00f4))
67
+ * is and coerce accept duration object ([51dffa1](https://github.com/iotaledger/twin-framework/commit/51dffa18066eb86a14c700d90d3c1bd8b322c0fc))
68
+ * locales validation ([#197](https://github.com/iotaledger/twin-framework/issues/197)) ([55fdadb](https://github.com/iotaledger/twin-framework/commit/55fdadb13595ce0047f787bd1d4135d429a99f12))
69
+ * mutex safe usage in browser ([#339](https://github.com/iotaledger/twin-framework/issues/339)) ([02d409f](https://github.com/iotaledger/twin-framework/commit/02d409f211e91267964208d58a27f29791715086))
70
+ * nodeIdentity optional in IComponent methods ([c78dc17](https://github.com/iotaledger/twin-framework/commit/c78dc17f4357d3e1ae40e415f468d3eae13e81f4))
71
+ * relocate core packages from tools ([bcab8f3](https://github.com/iotaledger/twin-framework/commit/bcab8f3160442ea4fcaf442947462504f3d6a17d))
72
+ * shared object buffer ([#355](https://github.com/iotaledger/twin-framework/issues/355)) ([70d82ea](https://github.com/iotaledger/twin-framework/commit/70d82ea8f43f01aa840ae90ee0b1f23b064a07c6))
73
+ * simplify factory options ([7f85a85](https://github.com/iotaledger/twin-framework/commit/7f85a8553dd3008364e8e1104f2e6f6b6595d77e))
74
+ * simplify StringHelper signature ([0390403](https://github.com/iotaledger/twin-framework/commit/039040344952f91ee3c671249bc0e1c8f3b38e17))
75
+ * typescript 6 update ([1d10f31](https://github.com/iotaledger/twin-framework/commit/1d10f31e6516ec622773f45e88af82fe749b384a))
76
+ * update dependencies ([4da77ab](https://github.com/iotaledger/twin-framework/commit/4da77ab30f499e52825ac5a76f51436ceb59c26e))
77
+ * update dependencies ([f3bd015](https://github.com/iotaledger/twin-framework/commit/f3bd015efd169196b7e0335f5cab876ba6ca1d75))
78
+ * urn random switched to using uuidv7 ([606c9a2](https://github.com/iotaledger/twin-framework/commit/606c9a2ed68a10fc7fc2bc5f93388c1b71033154))
79
+ * urn random switched to using uuidv7 ([6a29f8b](https://github.com/iotaledger/twin-framework/commit/6a29f8bd573d06992b7eaa027b1daf4c2a2e1e85))
80
+ * use cause instead of inner for errors ([1f4acc4](https://github.com/iotaledger/twin-framework/commit/1f4acc4d7a6b71a134d9547da9bf40de1e1e49da))
81
+
82
+
83
+ ### Bug Fixes
84
+
85
+ * async thread lock ([97a96a5](https://github.com/iotaledger/twin-framework/commit/97a96a57f60c7b45a0ccbbfa1c43199b6e7512f9))
86
+ * ensure __decorate is defined for decorators ([103a563](https://github.com/iotaledger/twin-framework/commit/103a563ce01ebdef6240d2e590e7b026e8692684))
87
+ * improve exception handling in asyncCache ([c81b29b](https://github.com/iotaledger/twin-framework/commit/c81b29b660b152d2f0757d323430287e6491bf59))
88
+ * improve jsdoc comments ([f7c8e43](https://github.com/iotaledger/twin-framework/commit/f7c8e43fab9803dffa6461950d5b9979e25bcd24))
89
+ * linting ([b77511d](https://github.com/iotaledger/twin-framework/commit/b77511de8781707088b1beb4c92f12d7d6e0fd5f))
90
+ * mutex locking ([d5af3c1](https://github.com/iotaledger/twin-framework/commit/d5af3c1e66070f86408ba1410809689c6d22958b))
91
+ * pick non iterable keys ([399399a](https://github.com/iotaledger/twin-framework/commit/399399abfb8947c4eb235df527532ffe186986b3))
92
+ * prevent TOCTOU race in Mutex.getOrFetchLock for concurrent async callers ([#343](https://github.com/iotaledger/twin-framework/issues/343)) ([5238ad1](https://github.com/iotaledger/twin-framework/commit/5238ad1a68e24c69162204b02030c6686c15e9f4))
93
+ * remove misleading undefined from AsyncCache return type ([05b3291](https://github.com/iotaledger/twin-framework/commit/05b32914e03a63c7daf7b77952a2ebedb7ca7f6b))
94
+
95
+
96
+ ### Dependencies
97
+
98
+ * The following workspace dependencies were updated
99
+ * dependencies
100
+ * @twin.org/nameof bumped from 0.9.0-next.0 to 0.9.0-next.1
101
+ * devDependencies
102
+ * @twin.org/nameof-transformer bumped from 0.9.0-next.0 to 0.9.0-next.1
103
+ * @twin.org/nameof-vitest-plugin bumped from 0.9.0-next.0 to 0.9.0-next.1
104
+
105
+ ## [0.0.4-next.15](https://github.com/iotaledger/twin-framework/compare/core-v0.0.4-next.14...core-v0.0.4-next.15) (2026-06-19)
106
+
107
+
108
+ ### Features
109
+
110
+ * configurable timeout for mutex ([2087e04](https://github.com/iotaledger/twin-framework/commit/2087e04464a110a3f009fbc8b066696ed6784fc2))
111
+
112
+
113
+ ### Dependencies
114
+
115
+ * The following workspace dependencies were updated
116
+ * dependencies
117
+ * @twin.org/nameof bumped from 0.0.4-next.14 to 0.0.4-next.15
118
+ * devDependencies
119
+ * @twin.org/nameof-transformer bumped from 0.0.4-next.14 to 0.0.4-next.15
120
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.4-next.14 to 0.0.4-next.15
121
+
122
+ ## [0.0.4-next.14](https://github.com/iotaledger/twin-framework/compare/core-v0.0.4-next.13...core-v0.0.4-next.14) (2026-06-18)
123
+
124
+
125
+ ### Features
126
+
127
+ * add partial second support ([39bf087](https://github.com/iotaledger/twin-framework/commit/39bf087ddc113a9bf73e0d0e99f738f6f60c2f2d))
128
+
129
+
130
+ ### Dependencies
131
+
132
+ * The following workspace dependencies were updated
133
+ * dependencies
134
+ * @twin.org/nameof bumped from 0.0.4-next.13 to 0.0.4-next.14
135
+ * devDependencies
136
+ * @twin.org/nameof-transformer bumped from 0.0.4-next.13 to 0.0.4-next.14
137
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.4-next.13 to 0.0.4-next.14
138
+
139
+ ## [0.0.4-next.13](https://github.com/iotaledger/twin-framework/compare/core-v0.0.4-next.12...core-v0.0.4-next.13) (2026-06-18)
140
+
141
+
142
+ ### Features
143
+
144
+ * is and coerce accept duration object ([51dffa1](https://github.com/iotaledger/twin-framework/commit/51dffa18066eb86a14c700d90d3c1bd8b322c0fc))
145
+
146
+
147
+ ### Dependencies
148
+
149
+ * The following workspace dependencies were updated
150
+ * dependencies
151
+ * @twin.org/nameof bumped from 0.0.4-next.12 to 0.0.4-next.13
152
+ * devDependencies
153
+ * @twin.org/nameof-transformer bumped from 0.0.4-next.12 to 0.0.4-next.13
154
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.4-next.12 to 0.0.4-next.13
155
+
156
+ ## [0.0.4-next.12](https://github.com/iotaledger/twin-framework/compare/core-v0.0.4-next.11...core-v0.0.4-next.12) (2026-06-18)
157
+
158
+
159
+ ### Features
160
+
161
+ * add duration support ([#362](https://github.com/iotaledger/twin-framework/issues/362)) ([24dbe8b](https://github.com/iotaledger/twin-framework/commit/24dbe8b208eb127a52024cb219d31d27731e9ae3))
162
+
163
+
164
+ ### Dependencies
165
+
166
+ * The following workspace dependencies were updated
167
+ * dependencies
168
+ * @twin.org/nameof bumped from 0.0.4-next.11 to 0.0.4-next.12
169
+ * devDependencies
170
+ * @twin.org/nameof-transformer bumped from 0.0.4-next.11 to 0.0.4-next.12
171
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.4-next.11 to 0.0.4-next.12
172
+
173
+ ## [0.0.4-next.11](https://github.com/iotaledger/twin-framework/compare/core-v0.0.4-next.10...core-v0.0.4-next.11) (2026-06-15)
174
+
175
+
176
+ ### Bug Fixes
177
+
178
+ * mutex locking ([d5af3c1](https://github.com/iotaledger/twin-framework/commit/d5af3c1e66070f86408ba1410809689c6d22958b))
179
+
180
+
181
+ ### Dependencies
182
+
183
+ * The following workspace dependencies were updated
184
+ * dependencies
185
+ * @twin.org/nameof bumped from 0.0.4-next.10 to 0.0.4-next.11
186
+ * devDependencies
187
+ * @twin.org/nameof-transformer bumped from 0.0.4-next.10 to 0.0.4-next.11
188
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.4-next.10 to 0.0.4-next.11
189
+
190
+ ## [0.0.4-next.10](https://github.com/iotaledger/twin-framework/compare/core-v0.0.4-next.9...core-v0.0.4-next.10) (2026-06-15)
191
+
192
+
193
+ ### Features
194
+
195
+ * add context id features ([#206](https://github.com/iotaledger/twin-framework/issues/206)) ([ef0d4ee](https://github.com/iotaledger/twin-framework/commit/ef0d4ee11a4f5fc6cc6f52a4958ce905c04ee13b))
196
+ * add factory.createIfExists ([aad5a53](https://github.com/iotaledger/twin-framework/commit/aad5a53cef1b1c2e04344ea46244d41e371dff9b))
197
+ * add Factory.getFactory ([ad6cd2d](https://github.com/iotaledger/twin-framework/commit/ad6cd2d30351c145c70212f8cce1b66d5e8a5235))
198
+ * add guard.dateString ([#335](https://github.com/iotaledger/twin-framework/issues/335)) ([a26a166](https://github.com/iotaledger/twin-framework/commit/a26a166eb1f62ece05b2432730cb7354feacecfc))
199
+ * add guards arrayEndsWith and arrayStartsWith ([95d875e](https://github.com/iotaledger/twin-framework/commit/95d875ec8ccb4713c145fdde941d4cfedcec2ed3))
200
+ * add health method to components ([a88016d](https://github.com/iotaledger/twin-framework/commit/a88016d90d172413e5bc5238dc1b3e35f82fcc2c))
201
+ * add Is.class method ([4988205](https://github.com/iotaledger/twin-framework/commit/498820543e256a130b4888c958fe1d87ca865d7f))
202
+ * add isDefault option to factory registration ([a8a700b](https://github.com/iotaledger/twin-framework/commit/a8a700bb8ddaf7dd5097869a358b8fc5f7c40ce7))
203
+ * add mutex ([#316](https://github.com/iotaledger/twin-framework/issues/316)) ([1027e5a](https://github.com/iotaledger/twin-framework/commit/1027e5ac77aa9804178b4253abdeefd6f1c3d521))
204
+ * add objectHelper.split ([386830a](https://github.com/iotaledger/twin-framework/commit/386830a77f8e842a5b119be0983708e042c3b14b))
205
+ * add ObjectOrArray and ArrayHelper methods ([0ac9077](https://github.com/iotaledger/twin-framework/commit/0ac907764d64b38ad1b04b0e9c3027055b527559))
206
+ * add rsa cipher support ([7af6cc6](https://github.com/iotaledger/twin-framework/commit/7af6cc67512d3363bd4a2f2e87bd7733c2800147))
207
+ * add single occurrence array type ([e890e43](https://github.com/iotaledger/twin-framework/commit/e890e4399e75ae5097f3ad8b1007321cbb1ed4ac))
208
+ * add single occurrence array type ([#245](https://github.com/iotaledger/twin-framework/issues/245)) ([771dc78](https://github.com/iotaledger/twin-framework/commit/771dc78025b5546c9c9681be9494a76ab71171c6))
209
+ * add support for health i18n validation ([7a286dd](https://github.com/iotaledger/twin-framework/commit/7a286ddb0c1bfa498bf3a77126cd589042bad6de))
210
+ * add teardown to IComponent interface ([32c173c](https://github.com/iotaledger/twin-framework/commit/32c173cda115c20d3b4cee14b8a29cdc08e91555))
211
+ * add teardown to IComponent interface ([98ce864](https://github.com/iotaledger/twin-framework/commit/98ce8648e709310c4435de334825b381fb4e32cb))
212
+ * add typeName method to factory ([699fcbd](https://github.com/iotaledger/twin-framework/commit/699fcbd1168228401ddb81fdacb959b8cdc4206a))
213
+ * add uuidv7 support ([#219](https://github.com/iotaledger/twin-framework/issues/219)) ([916c657](https://github.com/iotaledger/twin-framework/commit/916c657d270ce99fafe554233739fdd9ca28635b))
214
+ * add zlib/deflate mime types detection ([72c472b](https://github.com/iotaledger/twin-framework/commit/72c472b5a35a973e7109336f5b6cdd84dbb8bbcb))
215
+ * adding link header helper ([#225](https://github.com/iotaledger/twin-framework/issues/225)) ([703c072](https://github.com/iotaledger/twin-framework/commit/703c0725aceac6b6ec0c4fa729ef832d12fb3fd7))
216
+ * additional nameof operators ([a5aab60](https://github.com/iotaledger/twin-framework/commit/a5aab60bf66a86f1b7ff8af7c4f044cb03706d50))
217
+ * additional RSA methods and async ([1fceee2](https://github.com/iotaledger/twin-framework/commit/1fceee2d1248a24a7620846025fcf906495c07f4))
218
+ * bump version ([c5354fa](https://github.com/iotaledger/twin-framework/commit/c5354fa906f4493c70f2642a9175a66780a10636))
219
+ * eslint migration to flat config ([74427d7](https://github.com/iotaledger/twin-framework/commit/74427d78d342167f7850e49ab87269326355befe))
220
+ * expand error params to accept properties ([032e9fd](https://github.com/iotaledger/twin-framework/commit/032e9fd1388e457cde32ca1005dfe014a5a9c077))
221
+ * factory create and integrity ([#235](https://github.com/iotaledger/twin-framework/issues/235)) ([9f98b99](https://github.com/iotaledger/twin-framework/commit/9f98b99daf46eb365346fae49cc4ffba63e74cb3))
222
+ * health status grouping ([5007c29](https://github.com/iotaledger/twin-framework/commit/5007c29fe4123fe56f754450b993dbe5dc32a057))
223
+ * improve async cache edge cases ([4e57a6e](https://github.com/iotaledger/twin-framework/commit/4e57a6ec4113533b0ea903eae7d469200fa1348c))
224
+ * improve base error data extraction ([dccc933](https://github.com/iotaledger/twin-framework/commit/dccc93361a1544b41db0e7c126ff90e858d87960))
225
+ * improve error display in CLI ([94b6ca8](https://github.com/iotaledger/twin-framework/commit/94b6ca8bdcfe3ca7671c4095b436ea7bddaae98e))
226
+ * improve error formatting ([#313](https://github.com/iotaledger/twin-framework/issues/313)) ([5a19623](https://github.com/iotaledger/twin-framework/commit/5a196231bcbf088bf9ba92a93b7478d3b8c5593f))
227
+ * improve Is.function and ModuleHelper.getModuleMethod signatures ([ecf968b](https://github.com/iotaledger/twin-framework/commit/ecf968b02934b3676be4bf7cd2d1e7f8e7af6ce2))
228
+ * improve Is.function definition to retain types ([f20b6b0](https://github.com/iotaledger/twin-framework/commit/f20b6b0dd16e74b75dc359be72b05989305c6410))
229
+ * improve signatures ([45a7d58](https://github.com/iotaledger/twin-framework/commit/45a7d58baa5b6abc8c0735656f393d402fabb4ad))
230
+ * improve signatures ([cdd24be](https://github.com/iotaledger/twin-framework/commit/cdd24be6fb898d33955b6f2f93c3ddbd73582269))
231
+ * improve signatures ([2041ae2](https://github.com/iotaledger/twin-framework/commit/2041ae214cbd8d472d5e8be8d3403cd06a114040))
232
+ * improve signatures ([d8a0ee1](https://github.com/iotaledger/twin-framework/commit/d8a0ee16542134a3f8b0653065239f42045125b4))
233
+ * improve signatures ([1d084c5](https://github.com/iotaledger/twin-framework/commit/1d084c58c7eb41ae50b0ce80ce57e48a22dd3102))
234
+ * improve signatures ([#287](https://github.com/iotaledger/twin-framework/issues/287)) ([c9f38f0](https://github.com/iotaledger/twin-framework/commit/c9f38f00e1cea8759e1105b41872a650c66c00f4))
235
+ * locales validation ([#197](https://github.com/iotaledger/twin-framework/issues/197)) ([55fdadb](https://github.com/iotaledger/twin-framework/commit/55fdadb13595ce0047f787bd1d4135d429a99f12))
236
+ * mutex safe usage in browser ([#339](https://github.com/iotaledger/twin-framework/issues/339)) ([02d409f](https://github.com/iotaledger/twin-framework/commit/02d409f211e91267964208d58a27f29791715086))
237
+ * nodeIdentity optional in IComponent methods ([c78dc17](https://github.com/iotaledger/twin-framework/commit/c78dc17f4357d3e1ae40e415f468d3eae13e81f4))
238
+ * propagate includeStackTrace on error conversion ([8471cbb](https://github.com/iotaledger/twin-framework/commit/8471cbb71f8fc98247a0e92126c438c1a8b04d9b))
239
+ * propagate includeStackTrace on error conversion ([818337d](https://github.com/iotaledger/twin-framework/commit/818337d50d14bf5a7e8b3204649aa7527115cca9))
240
+ * relocate core packages from tools ([bcab8f3](https://github.com/iotaledger/twin-framework/commit/bcab8f3160442ea4fcaf442947462504f3d6a17d))
241
+ * shared object buffer ([#355](https://github.com/iotaledger/twin-framework/issues/355)) ([70d82ea](https://github.com/iotaledger/twin-framework/commit/70d82ea8f43f01aa840ae90ee0b1f23b064a07c6))
242
+ * simplify factory options ([7f85a85](https://github.com/iotaledger/twin-framework/commit/7f85a8553dd3008364e8e1104f2e6f6b6595d77e))
243
+ * simplify StringHelper signature ([0390403](https://github.com/iotaledger/twin-framework/commit/039040344952f91ee3c671249bc0e1c8f3b38e17))
244
+ * support indexed properties set in objects ([b9c001d](https://github.com/iotaledger/twin-framework/commit/b9c001dc4614f6ff7486f4370735a553613d823a))
245
+ * typescript 6 update ([1d10f31](https://github.com/iotaledger/twin-framework/commit/1d10f31e6516ec622773f45e88af82fe749b384a))
246
+ * update dependencies ([4da77ab](https://github.com/iotaledger/twin-framework/commit/4da77ab30f499e52825ac5a76f51436ceb59c26e))
247
+ * update dependencies ([f3bd015](https://github.com/iotaledger/twin-framework/commit/f3bd015efd169196b7e0335f5cab876ba6ca1d75))
248
+ * urn random switched to using uuidv7 ([606c9a2](https://github.com/iotaledger/twin-framework/commit/606c9a2ed68a10fc7fc2bc5f93388c1b71033154))
249
+ * urn random switched to using uuidv7 ([6a29f8b](https://github.com/iotaledger/twin-framework/commit/6a29f8bd573d06992b7eaa027b1daf4c2a2e1e85))
250
+ * use cause instead of inner for errors ([1f4acc4](https://github.com/iotaledger/twin-framework/commit/1f4acc4d7a6b71a134d9547da9bf40de1e1e49da))
251
+
252
+
253
+ ### Bug Fixes
254
+
255
+ * async thread lock ([97a96a5](https://github.com/iotaledger/twin-framework/commit/97a96a57f60c7b45a0ccbbfa1c43199b6e7512f9))
256
+ * ensure __decorate is defined for decorators ([103a563](https://github.com/iotaledger/twin-framework/commit/103a563ce01ebdef6240d2e590e7b026e8692684))
257
+ * improve exception handling in asyncCache ([c81b29b](https://github.com/iotaledger/twin-framework/commit/c81b29b660b152d2f0757d323430287e6491bf59))
258
+ * improve jsdoc comments ([f7c8e43](https://github.com/iotaledger/twin-framework/commit/f7c8e43fab9803dffa6461950d5b9979e25bcd24))
259
+ * linting ([b77511d](https://github.com/iotaledger/twin-framework/commit/b77511de8781707088b1beb4c92f12d7d6e0fd5f))
260
+ * pick non iterable keys ([399399a](https://github.com/iotaledger/twin-framework/commit/399399abfb8947c4eb235df527532ffe186986b3))
261
+ * prevent TOCTOU race in Mutex.getOrFetchLock for concurrent async callers ([#343](https://github.com/iotaledger/twin-framework/issues/343)) ([5238ad1](https://github.com/iotaledger/twin-framework/commit/5238ad1a68e24c69162204b02030c6686c15e9f4))
262
+ * remove misleading undefined from AsyncCache return type ([05b3291](https://github.com/iotaledger/twin-framework/commit/05b32914e03a63c7daf7b77952a2ebedb7ca7f6b))
263
+
264
+
265
+ ### Dependencies
266
+
267
+ * The following workspace dependencies were updated
268
+ * dependencies
269
+ * @twin.org/nameof bumped from 0.0.4-next.9 to 0.0.4-next.10
270
+ * devDependencies
271
+ * @twin.org/nameof-transformer bumped from 0.0.4-next.9 to 0.0.4-next.10
272
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.4-next.9 to 0.0.4-next.10
273
+
3
274
  ## [0.0.4-next.9](https://github.com/iotaledger/twin-framework/compare/core-v0.0.4-next.8...core-v0.0.4-next.9) (2026-06-15)
4
275
 
5
276