@metamask/ocap-kernel
Version:
OCap kernel core components
205 lines • 7.4 kB
JavaScript
;
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