From b29a1384117aa2b8082013d6280934e72115a0f5 Mon Sep 17 00:00:00 2001 From: adlee-was-taken Date: Wed, 29 Apr 2026 23:30:26 -0400 Subject: [PATCH] feat(ext/sw): parse + commit handlers for LastPass import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit parse_lastpass_csv is a pure pass-through to the WASM bridge. import_lastpass_commit re-mints each item's ID via state.wasm.new_item_id() (same pattern as add_item), encrypts and writes per-item via git.writeFile, then writes the manifest last. Per-item commits + a final manifest commit — extension GitHost has no atomic-batch API, so the single-commit semantics the CLI provides aren't replicable here. Co-Authored-By: Claude Opus 4.7 --- .../src/service-worker/router/popup-only.ts | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/extension/src/service-worker/router/popup-only.ts b/extension/src/service-worker/router/popup-only.ts index 83a027d..15d3582 100644 --- a/extension/src/service-worker/router/popup-only.ts +++ b/extension/src/service-worker/router/popup-only.ts @@ -507,6 +507,52 @@ export async function handle( return { ok: false, error: (e as Error).message }; } } + + case 'parse_lastpass_csv': { + try { + const json: string = state.wasm.parse_lastpass_csv_json(new Uint8Array(msg.bytes)); + const parsed = JSON.parse(json) as { + items: Item[]; + warnings: Array<{ row: number; title?: string; message: string }>; + }; + return { ok: true, data: parsed }; + } catch (e) { + return { ok: false, error: (e as Error).message }; + } + } + + case 'import_lastpass_commit': { + const handle = session.getCurrent(); + if (!handle || !state.gitHost || !state.manifest) return { ok: false, error: 'vault_locked' }; + if (msg.items.length === 0) return { ok: false, error: 'no items to import' }; + + try { + const total = msg.items.length; + for (let i = 0; i < msg.items.length; i++) { + const item = msg.items[i]; + // Items arrive with IDs already minted by the WASM bridge. We + // overwrite that with a fresh extension-generated ID so the SW + // remains the single ID-issuance authority for new items in the + // remote — same pattern as `add_item`. + const id = state.wasm.new_item_id(); + const reIdItem: Item = { ...item, id }; + + await vault.encryptAndWriteItem( + state.gitHost, handle, id, reIdItem, + `import: ${reIdItem.title} (${i + 1}/${total})`, + ); + state.manifest.items[id] = itemToManifestEntry(reIdItem); + } + + await vault.encryptAndWriteManifest( + state.gitHost, handle, state.manifest, + `manifest: import ${total} items from LastPass`, + ); + return { ok: true, data: { summary: { itemCount: total } } }; + } catch (e) { + return { ok: false, error: (e as Error).message }; + } + } } }