50 lines
2.5 KiB
TypeScript
50 lines
2.5 KiB
TypeScript
// Durable, background credential waits. Admission and completion share one SQLite commit.
|
|
import { defineDoc, defineTask, defineExtension } from '@earendil-works/pi-durable';
|
|
const Requests = defineDoc<{ ids: string[] }>({
|
|
kind: 'sausage.credential.requests', version: 1, scope: 'conversation',
|
|
history: 'latest', fork: 'initial', initial: () => ({ ids: [] }), checkpointWhen: () => false,
|
|
});
|
|
const Wait = defineTask<{ id: string }, { phase: 'wait' }, null>({
|
|
name: 'sausage.credential.wait', version: 1, initial: () => ({ phase: 'wait' }),
|
|
phases: {
|
|
wait: async (task, runtime, context) => {
|
|
const request = Deno.core.ops.op_credential_requests().find((r: any) => r.id === task.input.id);
|
|
if (!request || request.state === 'pending') {
|
|
await runtime.sleepUntil(Date.now() + 1000, { phase: 'wait' }, context);
|
|
return;
|
|
}
|
|
const conversation = (await runtime.conversation(runtime.conversationId, context))!;
|
|
await runtime.commit(async (tx) => {
|
|
await conversation.submit(tx, { type: 'input', content: request.message, requestId: `credential:${request.id}` });
|
|
return { status: 'terminal', outcome: { status: 'completed', result: null } };
|
|
}, context);
|
|
},
|
|
},
|
|
abort: (_task, runtime, context) => runtime.commit(() => ({ status: 'terminal', outcome: { status: 'aborted' } }), context),
|
|
});
|
|
export const CredentialRequests = defineExtension({ name: 'sausage.credentials', tasks: [Wait] });
|
|
export async function discoverRequests(harness: any, context: any) {
|
|
for (const request of Deno.core.ops.op_credential_requests()) {
|
|
const conversation = await harness.conversation(Number(request.conversation), context);
|
|
if (!conversation) continue;
|
|
await conversation.commit(async (tx: any) => {
|
|
const recorded = await tx.doc(Requests, conversation.id);
|
|
if (recorded.ids.includes(request.id)) return;
|
|
await tx.createTask(Wait, { id: request.id }, { ownership: { kind: 'conversation' }, background: true });
|
|
recorded.ids.push(request.id);
|
|
}, context);
|
|
}
|
|
}
|
|
export async function startRequests(harness: any, context: any) {
|
|
await discoverRequests(harness, context);
|
|
// Filed notifications start waits. The deadline reconciles other handles and lost notifications.
|
|
let active = true;
|
|
const pending = (async () => {
|
|
while (active) {
|
|
await Deno.core.ops.op_credential_next();
|
|
if (active) await discoverRequests(harness, context);
|
|
}
|
|
})();
|
|
return async () => { active = false; await pending; };
|
|
}
|