mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
docs: catalog INostrClient relay-client extensions so they're discoverable
The one-shot/high-level relay ops (fetchAll, fetchFirst, fetchAllPages, publishAndConfirm, count, negentropy sync/reconcile, …) are INostrClient extension functions spread across ~8 files with no index, so they don't surface under "usages of NostrClient" or in completion — easy to miss and re-implement (as just happened with a bespoke fetchRaw duplicating fetchAll). - Add accessories/README.md cataloging each public extension with a one-line "use when". - CLAUDE.md (Feature Workflow): point at that package/README before hand-rolling a subscribe/REQ/publish loop. - relay-client skill: add a Related note steering headless/one-shot callers to the accessories instead of Subscribable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JgL1WTV4Hkp2uuXcUHCHGt
This commit is contained in:
@@ -160,6 +160,17 @@ Summarize the survey in your plan: for each component, note whether it's
|
||||
reused as-is, extracted from `amethyst/` to `commons/`, genuinely new
|
||||
(platform-specific only), or a duplicate of an existing pattern to avoid.
|
||||
|
||||
**Relay client ops already exist — don't hand-roll subscribe/REQ/publish loops.**
|
||||
One-shot and high-level relay operations (fetch a set, fetch one, page past the
|
||||
relay cap, publish-and-confirm, NIP-45 count, NIP-77 sync/reconcile) are
|
||||
`INostrClient` **extension functions** in
|
||||
`quartz/…/nip01Core/relay/client/accessories/` (+ `…/reqs/` for the flow/subscribe
|
||||
helpers). Because they're extensions, they don't surface under "usages of
|
||||
`NostrClient`" or in completion — grep that package (or read its `README.md`, which
|
||||
catalogs them) before writing a new subscription/collect loop. Reuse `fetchAll`,
|
||||
`fetchFirst`, `fetchAllPages`, `publishAndConfirm`, `count`, `negentropyReconcile`,
|
||||
etc. instead of re-implementing them.
|
||||
|
||||
**Share vs keep platform-native:**
|
||||
|
||||
- **Share** → `quartz/commonMain/` (business logic, data models, protocol) and
|
||||
|
||||
@@ -123,6 +123,12 @@ Each subscription tracks "End of Stored Events" per relay. The eose manager in `
|
||||
|
||||
## Related
|
||||
|
||||
- **Headless / one-shot client ops** (CLI, geode, tests, non-compose code): don't go
|
||||
through `Subscribable` — use the `INostrClient` extension functions in
|
||||
`quartz/…/nip01Core/relay/client/accessories/` (`fetchAll`, `fetchFirst`,
|
||||
`fetchAllPages`, `publishAndConfirm`, `count`, `negentropyReconcile`/`negentropySync`,
|
||||
…). They're extensions, so they don't show up under "usages of `NostrClient`" — see
|
||||
that package's `README.md` for the catalog before writing a raw subscribe/collect loop.
|
||||
- `nostr-expert/references/tag-patterns.md` — how tags inform what a filter needs to look for.
|
||||
- `kotlin-coroutines/references/relay-patterns.md` — relay pool internals (sibling layer beneath assemblers).
|
||||
- `feed-patterns` skill — feeds compose several Subscribables (content + metadata + reactions).
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
# `INostrClient` accessories
|
||||
|
||||
One-shot / high-level relay operations, written as **extension functions** on
|
||||
`INostrClient`. They live here (and in `../reqs/`) rather than on the client class,
|
||||
so they don't show up under "usages of `NostrClient`" or in method completion — you
|
||||
only find them by knowing this package exists.
|
||||
|
||||
**Before writing a new subscribe / REQ / publish loop, look here first.** Most of what
|
||||
a caller needs (fetch a set, fetch one, page past the relay cap, publish-and-confirm,
|
||||
count, negentropy sync/reconcile) already exists.
|
||||
|
||||
Import as `com.vitorpamplona.quartz.nip01Core.relay.client.accessories.<name>` (or
|
||||
`...client.reqs.<name>` for the flow/subscribe helpers).
|
||||
|
||||
## One-shot reads (subscribe → collect → return)
|
||||
|
||||
| Function | File | Use when |
|
||||
| --- | --- | --- |
|
||||
| `fetchAll(relay, filter, timeoutMs)` | `NostrClientFetchAllExt` | Get every event matching a filter in one REQ, deduped by id, until EOSE or timeout. **No verify, no store** — just the events. |
|
||||
| `fetchFirst(relay, filter, timeoutMs)` | `NostrClientFetchFirstExt` | Get the first matching event and stop (returns `null` on none/timeout). |
|
||||
| `fetchAllPages(relay, filters, timeoutMs)` | `NostrClientFetchAllPagesExt` | Fully retrieve a result set larger than the relay's per-REQ cap (strfry `limit`, ~500) by walking a `created_at` cursor. Bound it with the filter's `limit`. |
|
||||
| `fetchAllPagesFromPool(filters, ...)` | `NostrClientFetchAllPagesPoolExt` | Same paging, across several relays at once, deduped across them. |
|
||||
|
||||
## Streaming (`Flow`)
|
||||
|
||||
| Function | File | Use when |
|
||||
| --- | --- | --- |
|
||||
| `fetchAsFlow(relay, filter)` | `../reqs/NostrClientFetchAsFlowExt` | Emit the accumulating list on each arrival; completes on EOSE. One-shot query as a flow. |
|
||||
| `subscribeAsFlow(relay, filter)` | `../reqs/NostrClientSubscribeAsFlowExt` | Live subscription as a flow (stays open past EOSE; re-sends the REQ on reconnect). |
|
||||
| `subscribe(subId, filters, listener)` | `../reqs/StaticSubscription`, `DynamicSubscription` | Raw live subscription with a `SubscriptionListener`. The lowest-level primitive the above build on. |
|
||||
|
||||
## Publish
|
||||
|
||||
| Function | File | Use when |
|
||||
| --- | --- | --- |
|
||||
| `publishAndConfirm(event, relays, timeout)` | `NostrClientPublishExt` | Send an EVENT and wait for `OK`; returns whether any relay accepted it. |
|
||||
| `publishAndConfirmDetailed(event, relays, timeout)` | `NostrClientPublishExt` | Same, but returns the per-relay accepted/rejected map. |
|
||||
|
||||
## Count (NIP-45)
|
||||
|
||||
| Function | File | Use when |
|
||||
| --- | --- | --- |
|
||||
| `count(relay, filter, timeoutMs)` | `NostrClientCountExt` | NIP-45 `COUNT` against one relay (`null` on timeout / no support). |
|
||||
| `countMerged(relays, filter, ...)` | `NostrClientCountExt` | Merged count across relays. |
|
||||
|
||||
## Negentropy (NIP-77)
|
||||
|
||||
| Function | File | Use when |
|
||||
| --- | --- | --- |
|
||||
| `negentropySync(relay, filter, ...)` | `NostrClientNegentropySyncExt` | Download everything a relay holds for a filter, diffing against `localEntries` and by-id downloading only the diff. Throws `NegentropySyncException` if the relay can't reconcile (no fallback). |
|
||||
| `negentropySyncOrFetch(relay, filter, ...)` | `NostrClientNegentropySyncExt` | Same, but transparently falls back to `fetchAllPages` when the relay can't reconcile. The "just get the events" combinator. |
|
||||
| `negentropySyncEvents` / `negentropySyncOrFetchEvents` | `NostrClientNegentropySyncEventsExt` | The two above as an O(1)-memory `Flow<Event>`. |
|
||||
| `negentropyReconcile(relay, filter, localEntries, onNeedIds, onHaveIds)` | `NostrClientNegentropySyncExt` | **Pure diff, no I/O** — streams the two directions (`need` = relay has & we lack; `have` = we have & relay lacks) to callbacks. Compose your own download/upload on top. |
|
||||
| `negentropyReconcileIds(relay, filter, localEntries)` | `NostrClientNegentropySyncExt` | Same diff, materialized into `needIds` / `haveIds` lists (small sets only). |
|
||||
|
||||
`fetchByIds`, `reconcileStreaming`, `syncPipeline` in `NostrClientNegentropySyncExt`
|
||||
are `internal` implementation details — not part of the public surface.
|
||||
|
||||
---
|
||||
|
||||
_Keep this table in sync when you add a public `INostrClient` extension here._
|
||||
Reference in New Issue
Block a user