fix(client): durcir le cold-start du shim — ensureRepoOpen(anchor) avant lecture/écriture
Défensif : loadShim/resolveAccount/ensureAccount ouvrent l'anchor (private-store-root) avant de lire/écrire le compte shim, comme le fait déjà readScopeIndex. Robustesse same-session si l'anchor est là-mais-pas-encore-souscrit. NB (vérifié nextgraph-rs) : sur le login broker normal, le bootstrap charge le private-store dans self.repos AVANT de rendre la session à JS → un wallet FRAIS retourne 0 rows (pas RepoNotFound) et provisionne. Il n'existe AUCUN primitif JS pour ouvrir un repo *inconnu* : ce heal n'est pas un remède à un store non bootstrappé (limite NextGraph), juste une robustesse d'ouverture same-session. Tests : cold-start-anchor.test.ts (rouge-avant/vert-après unit) ; harness e2e repro-fresh-wallet (mint un wallet neuf par run — comble le trou "aucun test de démarrage à froid sur wallet vierge"). Fakes anti-fork/watch-shape honorent désormais la barrière first-State dont dépend le heal. e2e réel 42/42. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -140,6 +140,72 @@ export async function launchWalletContext(): Promise<BrowserContext> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a BRAND-NEW wallet in a BRAND-NEW profile dir and RETURN the launched
|
||||
* context, without a `.wallet-ready` marker and WITHOUT tearing the context down.
|
||||
* Unlike {@link ensureWallet} (which reuses one persistent dedicated wallet across
|
||||
* runs — so it is always "hot"), this mints a genuinely FRESH wallet each call so
|
||||
* the cold-start (private-store repo not yet in `self.repos`) can be exercised.
|
||||
*
|
||||
* Same headless nextgraph.eu creation + first-login-bootstrap flow as ensureWallet,
|
||||
* but the context stays OPEN and is returned (with its dir) so the caller can then
|
||||
* open the SDK page in the SAME profile — i.e. the very first app session over a
|
||||
* wallet that has never run the app. Caller cleans up ctx + dir.
|
||||
*/
|
||||
export async function createFreshWalletContext(): Promise<{
|
||||
ctx: BrowserContext;
|
||||
dir: string;
|
||||
name: string;
|
||||
}> {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "ng-eventually-fresh-"));
|
||||
const name = "ng-fresh-" + Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
|
||||
const ctx = await chromium.launchPersistentContext(dir, {
|
||||
headless: true,
|
||||
executablePath: resolveChromePath(),
|
||||
args: LAUNCH_ARGS,
|
||||
});
|
||||
const page = ctx.pages()[0] || (await ctx.newPage());
|
||||
page.on("pageerror", () => {});
|
||||
await page.goto("https://nextgraph.eu/", { waitUntil: "domcontentloaded", timeout: 30000 });
|
||||
const createButton = page.getByText("Create Wallet", { exact: true });
|
||||
await createButton.waitFor({ state: "visible", timeout: 15000 });
|
||||
await createButton.click();
|
||||
|
||||
await page.waitForURL("**/account*", { timeout: 15000 }).catch(() => {});
|
||||
const acceptButton = page.getByText("I accept", { exact: true });
|
||||
await acceptButton.waitFor({ state: "visible", timeout: 15000 });
|
||||
await acceptButton.click();
|
||||
|
||||
const usernameInput = page.locator("#username-input");
|
||||
await usernameInput.waitFor({ state: "visible", timeout: 30000 });
|
||||
await usernameInput.fill(name);
|
||||
const passwordInput = page.locator("#password-input");
|
||||
await passwordInput.waitFor({ state: "visible", timeout: 5000 });
|
||||
await passwordInput.fill(WALLET_PASSWORD);
|
||||
|
||||
const submitButton = page.getByText("create my wallet", { exact: false });
|
||||
await submitButton.waitFor({ state: "visible", timeout: 5000 });
|
||||
await submitButton.click();
|
||||
|
||||
await page.waitForURL("**/#/wallet/login", { timeout: 30000 });
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// First login → bootstrap the verifier repos from the broker (this is what a
|
||||
// brand-new wallet does on its very first unlock).
|
||||
const walletLink = page.getByText("Click here to login with your wallet");
|
||||
if (await walletLink.isVisible({ timeout: 5000 }).catch(() => false)) {
|
||||
await walletLink.click();
|
||||
await page.waitForTimeout(1000);
|
||||
}
|
||||
const loginPassword = page.locator('input[type="password"]');
|
||||
await loginPassword.waitFor({ state: "visible", timeout: 10000 });
|
||||
await loginPassword.fill(WALLET_PASSWORD);
|
||||
await loginPassword.press("Enter");
|
||||
await page.waitForTimeout(10000);
|
||||
await page.close().catch(() => {});
|
||||
return { ctx, dir, name };
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch a persistent context on a FRESH, EMPTY profile dir (its own userDataDir).
|
||||
* Empty local storage ⇒ empty verifier repo cache ⇒ the reconnection cold-start:
|
||||
|
||||
Reference in New Issue
Block a user