feat: compose TechLog static and mock adapters
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
import { StudioGatewayError } from "../../application/ports/studio-gateway-error.ts";
|
||||
import type { ProblemDetails } from "../../contracts/studio/contract.ts";
|
||||
import { stableStringify } from "./stable-stringify.ts";
|
||||
|
||||
export type CursorPayload = { binding: string; lastValue: string; lastId: string };
|
||||
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
||||
|
||||
function invalid(detail: string) {
|
||||
const problem: ProblemDetails = {
|
||||
type: "https://techlog.local/problems/request-validation-failed", title: "Request validation failed",
|
||||
status: 422, detail, code: "REQUEST_VALIDATION_FAILED", retryable: false,
|
||||
fieldErrors: [{ path: "/cursor", message: detail }],
|
||||
};
|
||||
return new StudioGatewayError(problem);
|
||||
}
|
||||
|
||||
export function cursorBinding(value: unknown) { return stableStringify(value); }
|
||||
|
||||
export function encodeCursor(payload: CursorPayload): string {
|
||||
const bytes = new TextEncoder().encode(stableStringify(payload));
|
||||
let binary = "";
|
||||
for (const byte of bytes) binary += String.fromCharCode(byte);
|
||||
return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
|
||||
}
|
||||
|
||||
export function decodeCursor(cursor: string, binding: string): CursorPayload {
|
||||
try {
|
||||
const base64 = cursor.replaceAll("-", "+").replaceAll("_", "/").padEnd(Math.ceil(cursor.length / 4) * 4, "=");
|
||||
const bytes = Uint8Array.from(atob(base64), (character) => character.charCodeAt(0));
|
||||
const parsed = JSON.parse(new TextDecoder().decode(bytes)) as Partial<CursorPayload>;
|
||||
if (parsed.binding !== binding || typeof parsed.lastValue !== "string" || typeof parsed.lastId !== "string" || !UUID.test(parsed.lastId)) {
|
||||
throw invalid("Cursor does not match the normalized filters and sort.");
|
||||
}
|
||||
return parsed as CursorPayload;
|
||||
} catch (error) {
|
||||
if (error instanceof StudioGatewayError) throw error;
|
||||
throw invalid("Cursor is malformed.");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user