feat: compose executable frontend runtime

This commit is contained in:
donghyeon-ka
2026-07-25 23:42:39 +09:00
parent 9200c80149
commit 9a120e6d45
15 changed files with 505 additions and 21 deletions
@@ -4,6 +4,9 @@
*
* @param {{
* readState(): import("../../application/ports/auth-session-port.js").SessionState,
* subscribe(listener: () => void): () => void,
* beginSignIn(returnTo?: string): Promise<void>,
* signOut(): Promise<void>,
* attachCredential(request: Request): Promise<Request>,
* recoverSession(): Promise<"restored" | "no-session">,
* notifyUnauthenticated(): void
@@ -15,6 +18,16 @@ export function createExternalAuthSessionAdapter(owner) {
getState() {
return owner.readState();
},
subscribe(listener) {
return owner.subscribe(listener);
},
async beginSignIn(returnTo) {
await owner.beginSignIn(returnTo);
},
async signOut() {
await owner.signOut();
},
/** @param {Request} request */
async attach(request) {
const attached = await owner.attachCredential(request);
if (!(attached instanceof Request)) {
@@ -38,8 +51,74 @@ export function createExternalAuthSessionAdapter(owner) {
export function createAnonymousSessionAdapter() {
return createExternalAuthSessionAdapter({
readState: () => "unauthenticated",
subscribe: () => () => {},
beginSignIn: async () => {},
signOut: async () => {},
attachCredential: async (request) => request,
recoverSession: async () => "no-session",
notifyUnauthenticated: () => {},
});
}
/**
* Local/test-only session seam. It never creates or stores credentials.
*
* @param {import("../../application/ports/auth-session-port.js").SessionState} [initialState]
*/
export function createDemoSessionAdapter(initialState = "unauthenticated") {
let state = initialState;
const listeners = new Set();
function notify() {
for (const listener of listeners) listener();
}
/** @param {import("../../application/ports/auth-session-port.js").SessionState} next */
function setState(next) {
state = next;
notify();
}
return Object.freeze({
getState: () => state,
/** @param {() => void} listener */
subscribe(listener) {
listeners.add(listener);
return () => listeners.delete(listener);
},
async beginSignIn() {
setState("authenticated");
},
async signOut() {
setState("unauthenticated");
},
/** @param {Request} request */
async attach(request) {
return request;
},
async recover() {
if (state === "recovery-pending") {
setState("authenticated");
return /** @type {const} */ ("restored");
}
return /** @type {const} */ ("no-session");
},
onUnauthenticated() {
setState("unauthenticated");
},
setState,
});
}
export function createUnavailableSessionAdapter() {
return Object.freeze({
getState: () => /** @type {const} */ ("integration-failed"),
subscribe: () => () => {},
beginSignIn: async () => {},
signOut: async () => {},
/** @param {Request} request */
attach: async (request) => request,
recover: async () => /** @type {const} */ ("no-session"),
onUnauthenticated: () => {},
});
}