Skip to content

Commit

Permalink
fix(context): per request lock
Browse files Browse the repository at this point in the history
  • Loading branch information
ido-pluto committed Jun 30, 2024
1 parent c3b1cb4 commit af66bce
Showing 1 changed file with 40 additions and 23 deletions.
63 changes: 40 additions & 23 deletions packages/context/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,49 +8,66 @@ declare global {
}
}

type ContextAstro = AstroGlobal | { request: Request, locals: any, props: any; };
function getContextHistory(astro: ContextAstro, name: string) {
const contexts: Map<string, any[]> = astro.locals.amContext ??= new Map();
contexts.set(name, contexts.get(name) ?? []);
return contexts.get(name);
type ContextAstro = AstroGlobal | {
request: Request,
locals: any,
props: any;
};

type AMContext = {
lock: Map<string, any>;
history: Map<string, any[]>;
};

function getAMContextFromAstro(astro: ContextAstro, name: string) {
const amContext = astro.locals.amContext ??= {
lock: new Map(),
history: new Map()
};

amContext.history.set(name, amContext.history.get(name) ?? []);
return amContext;
}

async function getContextHistoryAfterLock(astro: ContextAstro, name: string, lock?: string) {
const contexts: AMContext = getAMContextFromAstro(astro, name);

while (contexts.lock.get(lock)) {
await contexts.lock.get(lock);
}

return {
value: contexts.history.get(name),
lock: contexts.lock
};
}

export default function getContext(astro: ContextAstro, name = "default") {
return getContextHistory(astro, name).at(-1) ?? {};
const contexts: AMContext = getAMContextFromAstro(astro, name);
return contexts.history.get(name).at(-1) ?? {};
}

type AsyncContextOptions = { name?: string, context?: any, lock?: string; };
const activeLock: Map<string, Map<string, Promise<void>>> = new Map();

export async function asyncContext<T>(promise: () => Promise<T>, astro: ContextAstro, { name = "default", context = null, lock }: AsyncContextOptions = {}): Promise<T> {
activeLock.set(name, activeLock.get(name) ?? new Map());
const lockContext = activeLock.get(name);

while (lockContext.get(lock)) {
await lockContext.get(lock);
}

const contextHistory = getContextHistory(astro, name);
const contextHistory = await getContextHistoryAfterLock(astro, name);

contextHistory.push({
...contextHistory.at(-1),
contextHistory.value.push({
...contextHistory.value.at(-1),
...(context ?? astro.props)
});

let resolver: () => void | null;
if (lock) {
lockContext.set(lock, new Promise<void>(resolve => resolver = resolve));
contextHistory.lock.set(lock, new Promise<void>(resolve => resolver = resolve));
}

try {
const response = await promise();
contextHistory.pop();
contextHistory.value.pop();
return response;
} finally {
lockContext.delete(lock);
if(lockContext.size === 0) {
activeLock.delete(name);
}
contextHistory.lock.delete(lock);
resolver?.();
}
}

0 comments on commit af66bce

Please sign in to comment.