/** * What NextGraph's own pages LOOK like — addresses, selectors, and the inventory of screens * the sign-in walks through. Description only: nothing here drives a browser. * * ── Why it is a separate file, and why it is data ──────────────────────────── * Two kinds of knowledge live in this package and they age at completely different rates. * How to cross a broker — dispatch on the screen you can see, never on elapsed time; identify * the application by its origin, never by a substring — is a *method*, and it has survived * every change upstream. WHICH selector shows a wallet list is a *fact about today's markup*, * and it changes whenever the wallet application is restyled. * * Keeping the second kind as plain data has two consequences worth the split. Upstream * changes a selector: you edit a string in this file and no control flow moves. And the * driving code below (`broker.ts`, `wallet.ts`) reads this inventory rather than embedding * it, so a harness built on some other browser driver would reuse this file whole and * rewrite only the driving. That adapter is NOT built here — the point is only that * building it would not be a rewrite. * * The screen inventory is deliberately SERIALIZABLE: it is handed to the browser as an * argument (see `readBrokerScreen` in `broker.ts`), so the same description that names a * screen in a failure message is the one the recognition dispatched on. That rules out * regular expressions as values, hence {@link TextPattern}. * * VERIFIED 2026-08-14 against the live pages unless noted; the upstream source is * `nextgraph-rs` (`infra/ngnet/redir`, `engine/broker/auth`, `app/ui-common`), read but * never modified. */ // ── the wallet application (nextgraph.eu) ─────────────────────────────────── /** * Where a wallet is created and where one is imported. The wallet application is a real * application like any other — this harness drives its actual interface rather than * reaching behind it, because a wallet obtained any other way is not the one a person has. */ export const WALLET_APP = { home: "https://nextgraph.eu/", /** The standalone import/unlock route, reachable without going through the broker. */ login: "https://nextgraph.eu/#/wallet/login", } as const; /** The creation flow, screen by screen, as labels and selectors. */ export const WALLET_CREATION = { /** Step 1 — the home page's entry point. */ createWallet: "Create Wallet", /** Step 2 — the terms screen, reached on the `/account` route. */ acceptTerms: "I accept", /** The URL glob that route is awaited by. */ termsRoute: "**/account*", /** Step 3 — the credentials form. */ username: "#username-input", password: "#password-input", /** Matched loosely: the button's caption is not stable in case. */ submit: "create my wallet", /** Step 4 — creation lands here, and the first unlock happens from it. */ landsOn: "**/#/wallet/login", /** Offered on the login route when a wallet is already on the device. */ loginWithThisWallet: "Click here to login with your wallet", passwordField: 'input[type="password"]', } as const; /** The import-a-wallet-file flow on the same login route. */ export const WALLET_IMPORT = { fileInput: "input[type=file]", passwordField: "input[type=password]", /** Shown by some builds after the password; absent in others, so it is probed, not awaited. */ confirm: /Confirm/i, } as const; // ── the broker crossing (nextgraph.net/redir → the broker's auth page) ────── /** The redirect that hands an application's address to the broker. */ export function brokerRedirectFor(appUrl: string): string { return `https://nextgraph.net/redir/#/?o=${encodeURIComponent(appUrl)}`; } /** * The distinct screens the crossing can be on. * * - `choose-broker` — the redirect page with MORE than one broker to pick from. Not observed * on hosts that resolve to a single broker (which auto-selects), so it is described from * the upstream source rather than from observation. * - `login-offered` — "We could not find a wallet on this device… Login". The entry screen of * every sign-in observed, first actor and later ones alike. * - `wallet-list` — "Select a wallet to login with", one box per wallet. * - `password` — "Enter your password". Reached by the FIRST actor only: the wallet is * broadcast between the broker origin's tabs over a `BroadcastChannel` named `ng_wallet`, * so a later actor's wallet is already in `opened_wallets` and selecting it logs straight * in (`ui-common/src/routes/WalletLogin.svelte`, the `$opened_wallets[selected]` path). * VERIFIED 2026-08-14, three consecutive sign-ins in one browser context. * - `working` — a splash, "Opening your wallet…", "Wallet opened for …". Nothing to do but * wait for it to become something else. Note that SUCCESS is one of these: the final screen * never stops being `working`, which is why the application's frame is watched separately * rather than inferred from the screen. * - `error` — the broker said no ("An error occurred", "Invalid request"). Terminal. */ export type BrokerScreen = | "choose-broker" | "login-offered" | "wallet-list" | "password" | "working" | "error"; /** * A regular expression as data, because the inventory crosses into the browser and a * `RegExp` does not survive that trip. Rebuilt on the far side with `new RegExp(...)`. */ export interface TextPattern { readonly source: string; readonly flags: string; } /** How a screen is told apart from the ones described BEFORE it. */ export type ScreenSignature = /** Any of these selectors matches an element with a non-zero box. */ | { readonly kind: "rendered"; readonly selectors: readonly string[] } /** A rendered `