| … | … | @@ -1,6 +1,6 @@ |
| 1 | 1 | { |
| 2 | 2 | "name": "atproto-did-peer", |
| 3 | "version": "0.2.11", | |
| 3 | "version": "0.2.12", | |
| 4 | 4 | "description": "Keyless, direction-free P2P reader/seeder for an atproto DID's repo+firehose Hypercores: announce-push discovery + event-driven head reconciliation + a keyless read surface. The shared primitive beneath atproto-bridge, atproto-relay and favela (extracted from atproto-bridge did-peer.js).", |
| 5 | 5 | "type": "module", |
| 6 | 6 | "main": "index.js", |
| … | … | @@ -69,7 +69,8 @@ await swarm.join(discoveryTopic(DID), { server: false, client: true }).flushed() |
| 69 | 69 | const counts = [] |
| 70 | 70 | for (let i = 0; i < 40; i++) { |
| 71 | 71 | const f = await router.front(DID).catch(() => null) |
| 72 | const recs = f ? await f.listRecords('app.bsky.feed.post').catch(() => []) : [] | |
| 72 | // listRecords returns { records, cursor } per the lexicon (0.2.10) — NOT a bare array. | |
| 73 | const recs = f ? ((await f.listRecords('app.bsky.feed.post').catch(() => null)) || {}).records || [] : [] | |
| 73 | 74 | counts.push(recs.length) |
| 74 | 75 | await new Promise((r) => setTimeout(r, 300)) |
| 75 | 76 | } |
| … | … | @@ -80,7 +80,8 @@ await swarm.join(discoveryTopic(DID), { server: false, client: true }).flushed() |
| 80 | 80 | const counts = [] |
| 81 | 81 | for (let i = 0; i < 40; i++) { |
| 82 | 82 | const f = await router.front(DID).catch(() => null) |
| 83 | const recs = f ? await f.listRecords('app.bsky.feed.post').catch(() => []) : [] | |
| 83 | // listRecords returns { records, cursor } per the lexicon (0.2.10) — NOT a bare array. | |
| 84 | const recs = f ? ((await f.listRecords('app.bsky.feed.post').catch(() => null)) || {}).records || [] : [] | |
| 84 | 85 | counts.push(recs.length) |
| 85 | 86 | await new Promise((r) => setTimeout(r, 300)) |
| 86 | 87 | } |
| … | … | @@ -79,7 +79,8 @@ function reader (opts = {}) { |
| 79 | 79 | async function served (router) { |
| 80 | 80 | const f = await router.front(DID).catch(() => null) |
| 81 | 81 | if (!f) return -1 |
| 82 | return (await f.listRecords('app.bsky.feed.post').catch(() => [])).length | |
| 82 | // listRecords returns { records, cursor } per the lexicon (0.2.10) — NOT a bare array. | |
| 83 | return (((await f.listRecords('app.bsky.feed.post').catch(() => null)) || {}).records || []).length | |
| 83 | 84 | } |
| 84 | 85 | async function waitServed (router, want, ms = 15000) { |
| 85 | 86 | const end = Date.now() + ms |
| … | … | @@ -109,7 +109,7 @@ const router = new DidPeerRouter(store, { |
| 109 | 109 | toClose.push(async () => { try { await swarm.destroy() } catch {}; try { await router.close() } catch {}; try { await store.close() } catch {} }) |
| 110 | 110 | |
| 111 | 111 | const servedRev = async () => { const f = await router.front(DID).catch(() => null); const lc = f && f.getLatestCommit(); return lc ? lc.rev : null } |
| 112 | const servedRecs = async () => { const f = await router.front(DID).catch(() => null); return f ? (await f.listRecords('app.bsky.feed.post').catch(() => [])).length : -1 } | |
| 112 | const servedRecs = async () => { const f = await router.front(DID).catch(() => null); return f ? (((await f.listRecords('app.bsky.feed.post').catch(() => null)) || {}).records || []).length : -1 } | |
| 113 | 113 | async function waitRev (want, ms = 20000) { const end = Date.now() + ms; let last = null; while (Date.now() < end) { last = await servedRev(); if (last === want) return last; await sleep(250) } return last } |
| 114 | 114 | |
| 115 | 115 | // 1. Same line: the fresher co-owner wins, continuity permits it (its frames CONTAIN the lagging head). |
| … | … | @@ -46,7 +46,15 @@ const head = await reader.ready({ timeout: 20000 }) |
| 46 | 46 | assert(!!head, 'noRepo reader reached a VERIFIED head via the firehose (no repo core)') |
| 47 | 47 | assert(reader.repo === null, 'repo core was NEVER opened (this.repo === null) — no repo-core replica') |
| 48 | 48 | assert(reader.seq && reader.seq.core.length >= 1, 'firehose core IS opened + replicated') |
| 49 | assert((await reader.listRecords('app.bsky.feed.post')).length === 0, 'listRecords returns [] (records come from the shared store, not the repo core)') | |
| 49 | // ⛔ THE SHAPE, NOT THE EMPTINESS, IS WHAT BROKE HERE. 0.2.10 made listRecords return { records, cursor } | |
| 50 | // per the lexicon instead of a bare array; this assertion still read `.length` off the wrapper, so it was | |
| 51 | // comparing `undefined === 0` and failing on a code path that had never changed behaviour. The noRepo | |
| 52 | // contract below is exactly as it always was — zero records, because they live in the consumer's shared | |
| 53 | // OverlayRepoStore and not in a repo core this peer never opened. | |
| 54 | const listed = await reader.listRecords('app.bsky.feed.post') | |
| 55 | assert(Array.isArray(listed.records), 'listRecords returns the lexicon shape { records, cursor }') | |
| 56 | assert(listed.records.length === 0, 'and noRepo yields NO records (they come from the shared store, not a repo core)') | |
| 57 | assert(listed.cursor === undefined, 'with no cursor — its ABSENCE is how a pager knows to stop') | |
| 50 | 58 | assert((await reader.getRecord('app.bsky.feed.post', '1')) === null, 'getRecord returns null in noRepo mode') |
| 51 | 59 | |
| 52 | 60 | console.log('\nNO-REPO OK: firehose-only consumer verifies the head + follows the tail with NO repo-core replica') |
| … | … | @@ -42,7 +42,7 @@ console.log('VERIFIED HEAD:', { rev: head.rev, cid: head.cid.slice(0, 24) + '… |
| 42 | 42 | console.log('collections:', await peer.collections()) |
| 43 | 43 | const rec = await peer.getRecord('app.bsky.feed.post', KNOWN_RKEY) |
| 44 | 44 | console.log('getRecord ->', JSON.stringify(rec?.value?.text || rec).slice(0, 66)) |
| 45 | const list = await peer.listRecords('app.bsky.feed.post') | |
| 45 | const list = (await peer.listRecords('app.bsky.feed.post')).records | |
| 46 | 46 | console.log('listRecords ->', list.length, 'posts') |
| 47 | 47 | const { carBytes } = await peer.getRepo() |
| 48 | 48 | console.log('getRepo ->', carBytes.length, 'CAR bytes (keyless, DID-key-verified)') |