Back to Black Site Map Editor
SOURCE / PINNED RELEASE

Made of little things.

Black Site Map Editor

Release
32a57733b7e9…
Author-recorded commit
3f54890f542f…
License
LICENSE
Author’s source reference
nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.shakespeare.diy%2F/black-site-map-editor

Archive hash verified: 7a4b1abbac7db5b0…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

docs/examples/backend-client.ts
// Optional NAP-CVM client. Operation names and game rules belong to your project.
export type BackendModule = { napplet: string; name: string };
export type BackendTarget = { module: BackendModule; release: string; instance?: string };
export type BackendIntent = {
    target: BackendTarget;
    operation: string;
    input: unknown;
    requestId: string;
    expiresAt: number;
};
export type BackendResult<T> = {
    ok: true;
    release: string;
    requestId: string;
    instance: string;
    revision: number;
    result: T;
};
export class BackendCallError extends Error {
    constructor(
        message: string,
        readonly definitive: boolean,
        readonly code?: string,
    ) {
        super(message);
    }
}
export function backendClient(napplet: any) {
    async function call<T>(name: string, args: unknown): Promise<T> {
        let reply: any;
        // First account-bound calls include a host popup and extension/remote signing.
        // This is the UI deadline, not a longer handler execution or retry window.
        const accountCall = [
            'soy_backend_invoke',
            'soy_backend_changes',
            'soy_backend_purge_plan',
            'soy_backend_purge_confirm',
        ].includes(name);
        try {
            reply = await napplet.cvm.registry.call('soy.backends.v1', name, args, {
                timeoutMs: accountCall ? 120000 : 15000,
            });
        } catch (error) {
            throw new BackendCallError(
                error instanceof Error ? error.message : 'Connection interrupted',
                false,
            );
        }
        const value =
            reply.structuredContent ??
            JSON.parse(reply.content?.find((item: any) => item.type === 'text')?.text ?? '{}');
        if (reply.isError)
            throw new BackendCallError(
                value.error?.message ?? 'Backend rejected this operation',
                true,
                value.error?.code,
            );
        return value as T;
    }
    return {
        call,
        describe: (module: BackendModule, release?: string) =>
            call<{
                active: string | null;
                release?: string;
                disabled: boolean;
                schemas?: unknown;
                receipt?: unknown;
            }>('soy_backend_describe', { module, ...(release ? { release } : {}) }),
        // Create this ONCE per user intent. Retain it until the result is known.
        intent: (target: BackendTarget, operation: string, input: unknown): BackendIntent => ({
            target,
            operation,
            input,
            requestId: crypto.randomUUID(),
            expiresAt: Math.floor(Date.now() / 1000) + 240,
        }),
        invoke: <T>(intent: BackendIntent) => call<BackendResult<T>>('soy_backend_invoke', intent),
    };
}