UNPKG

@metamask/ocap-kernel

Version:
205 lines 7.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getBaseMethods = getBaseMethods; /** * Get the base store methods for managing stored values and queues. * * @param kv - The key/value store to provide the underlying persistence mechanism. * @returns An object with methods for managing stored values and queues. */ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type function getBaseMethods(kv) { /** * Get the key for the reachable flag and vatSlot for a given endpoint and kref. * * @param endpointId - The endpoint for which the reachable flag is being set. * @param kref - The kref. * @returns The key for the reachable flag and vatSlot. */ function getSlotKey(endpointId, kref) { return `${endpointId}.c.${kref}`; } /** * Generate the storage key for a kernel entity's reference count. * * @param kref - The KRef of interest. * @returns the key to store the indicated reference count at. */ function refCountKey(kref) { return `${kref}.refCount`; } /** * Generate the storage key for a kernel entity's owner. * * @param kref - The KRef of interest. * @returns the key to store the indicated owner at. */ function getOwnerKey(kref) { return `${kref}.owner`; } /** * Generate the storage key for a kernel entity's revoked flag. * * @param kref - The KRef of interest. * @returns the key to store the indicated revoked flag at. */ function getRevokedKey(kref) { return `${kref}.revoked`; } /** * Increment the value of a persistently stored counter. * * Note that the while the value is interpreted as an integer (in order to * enable it to be incremented), it is stored and returned in the form of a * string. This is because (a) our persistent storage only stores strings, and * (b) the sole purpose of one of these counters is simply to provide an * unending sequence of unique values; we don't actually use them as numbers * or, indeed, even care at all if this sequence is produced using numbers. * * @param value - Reference to the stored value to be incremented. * @returns The value as it was prior to being incremented. */ function incCounter(value) { const current = value.get(); const next = `${Number(current) + 1}`; value.set(next); return current; } /** * Provide a stored value object for which we keep an in-memory cache. We only * touch persistent storage if the value hasn't ever been read of if it is * modified; otherwise we can service read requests from memory. * * IMPORTANT NOTE: in order for the cache to work, all subsequent accesses to * the value MUST be made through a common stored value object. * * @param key - A key string that identifies the value. * @param init - If provided, an initial setting if the stored entity does not exist. * @returns An object for interacting with the value. */ function provideCachedStoredValue(key, init) { let value = kv.get(key); if (value === undefined && init !== undefined) { kv.set(key, init); value = init; } return harden({ get() { return value; }, set(newValue) { value = newValue; kv.set(key, value); }, delete() { value = undefined; kv.delete(key); }, }); } /** * Provide a stored value object that is kept in persistent storage without caching. * * @param key - A key string that identifies the value. * @param init - If provided, an initial setting if the stored entity does not exist. * @returns An object for interacting with the value. */ function provideRawStoredValue(key, init) { if (kv.get(key) === undefined && init !== undefined) { kv.set(key, init); } return harden({ get: () => kv.get(key), set: (newValue) => kv.set(key, newValue), delete: () => kv.delete(key), }); } /** * Produce an object to access a persistently stored queue. * * @param queueName - The name for the queue (must be unique among queues). * @param cached - Optional flag: set to true if the queue should cache its * limit indices in memory (only do this if the queue is going to be accessed or * checked frequently). * @returns An object for interacting with the queue. */ function provideStoredQueue(queueName, cached = false) { const qk = `queue.${queueName}`; // Note: cached=true ==> caches only the head & tail indices, NOT the queue entries themselves const provideValue = cached ? provideCachedStoredValue : provideRawStoredValue; const head = provideValue(`${qk}.head`, '1'); const tail = provideValue(`${qk}.tail`, '1'); if (head.get() === undefined || tail.get() === undefined) { throw Error(`queue ${queueName} not initialized`); } return { enqueue(item) { if (head.get() === undefined) { throw Error(`enqueue into deleted queue ${queueName}`); } const entryPos = incCounter(head); kv.set(`${qk}.${entryPos}`, JSON.stringify(item)); }, dequeue() { const headPos = head.get(); if (headPos === undefined) { return undefined; } const tailPos = tail.get(); if (tailPos !== headPos) { const entry = kv.getRequired(`${qk}.${tailPos}`); kv.delete(`${qk}.${tailPos}`); incCounter(tail); return JSON.parse(entry); } return undefined; }, delete() { const headPos = head.get(); if (headPos !== undefined) { let tailPos = tail.get(); while (tailPos !== headPos) { kv.delete(`${qk}.${tailPos}`); tailPos = `${Number(tailPos) + 1}`; } head.delete(); tail.delete(); } }, }; } /** * Generator that yields all the keys beginning with a given prefix. * * @param prefix - The prefix of interest. * * @yields the keys that start with `prefix`. */ function* getPrefixedKeys(prefix) { let key = prefix; for (;;) { key = kv.getNextKey(key); if (!key) { break; } if (!key.startsWith(prefix)) { break; } yield key; } } return { getSlotKey, refCountKey, getOwnerKey, getRevokedKey, incCounter, provideCachedStoredValue, provideRawStoredValue, provideStoredQueue, getPrefixedKeys, }; } //# sourceMappingURL=base.cjs.map