32 lines
1.4 KiB
JavaScript
32 lines
1.4 KiB
JavaScript
// In-memory synthetic store only; no filesystem or production backend.
|
|
import { refuse, validateFixtureCredential } from './execution.mjs';
|
|
const stores = new WeakSet();
|
|
export const isFixtureStore = store => stores.has(store);
|
|
export function createFixtureCredentialStore(initial) {
|
|
const records = new Map(), locks = new Set();
|
|
for (const [ref, value] of Object.entries(initial)) {
|
|
if (!/^[a-z0-9][a-z0-9._-]{0,63}\/[a-z0-9][a-z0-9._-]{0,63}$/.test(ref)) refuse('invalid-account-ref');
|
|
records.set(ref, validateFixtureCredential(value, value?.type ?? 'none'));
|
|
}
|
|
const store = Object.freeze({
|
|
fixtureOnly: true,
|
|
async transaction(refs, callback) {
|
|
const keys = [...new Set(refs)].sort();
|
|
for (const key of keys) {
|
|
if (!records.has(key)) refuse('missing-fixture-credential');
|
|
if (locks.has(key)) refuse('credential-busy');
|
|
}
|
|
keys.forEach(key => locks.add(key));
|
|
try {
|
|
const draft = new Map(keys.map(key => [key, structuredClone(records.get(key))]));
|
|
const result = await callback(draft);
|
|
for (const key of keys) validateFixtureCredential(draft.get(key), records.get(key)?.type ?? 'none');
|
|
keys.forEach(key => records.set(key, structuredClone(draft.get(key))));
|
|
return result;
|
|
} finally { keys.forEach(key => locks.delete(key)); }
|
|
},
|
|
});
|
|
stores.add(store);
|
|
return store;
|
|
}
|