Finish pass on docs
This commit is contained in:
@@ -1,60 +0,0 @@
|
||||
# Basic Utilities
|
||||
|
||||
## synced
|
||||
Creates a writable store that automatically synchronizes its value with localStorage.
|
||||
|
||||
```typescript
|
||||
const myStore = synced('storage-key', 'default value');
|
||||
```
|
||||
|
||||
## getter
|
||||
Creates a function that returns the current value of a store without subscribing to it.
|
||||
|
||||
```typescript
|
||||
const myStore = writable('value');
|
||||
const getValue = getter(myStore);
|
||||
|
||||
```
|
||||
|
||||
## withGetter
|
||||
Enhances a store by adding a getter method to access its current value.
|
||||
|
||||
```typescript
|
||||
const myStore = withGetter(writable('value'));
|
||||
console.log(myStore.get()); // 'value'
|
||||
```
|
||||
|
||||
## throttled
|
||||
Creates a store that limits how often subscribers receive updates.
|
||||
|
||||
```typescript
|
||||
const throttledStore = throttled(1000, myStore); // Updates at most once per second
|
||||
```
|
||||
|
||||
## custom
|
||||
Creates a custom store with optional throttling and custom set behavior.
|
||||
|
||||
```typescript
|
||||
const customStore = custom(
|
||||
set => {
|
||||
// Setup logic
|
||||
return () => {
|
||||
// Cleanup logic
|
||||
};
|
||||
},
|
||||
{ throttle: 1000 }
|
||||
);
|
||||
```
|
||||
|
||||
## adapter
|
||||
Creates a derived store that can transform values between two types while maintaining two-way binding.
|
||||
|
||||
```typescript
|
||||
const adaptedStore = adapter({
|
||||
store: originalStore,
|
||||
forward: (source) => /* transform to target */,
|
||||
backward: (target) => /* transform back to source */
|
||||
});
|
||||
```
|
||||
|
||||
This is particularly useful when you need to transform data structures while maintaining the ability to update the original store.
|
||||
@@ -0,0 +1,71 @@
|
||||
# Collection
|
||||
|
||||
Utilities for creating reactive collections with automatic loading, caching, and staleness management using Svelte stores.
|
||||
|
||||
## Functions
|
||||
|
||||
### collection(options)
|
||||
|
||||
Creates a reactive collection that automatically loads missing items and manages freshness.
|
||||
|
||||
**Options:**
|
||||
- `name` - Collection name for freshness tracking
|
||||
- `store` - Readable store containing array of items
|
||||
- `getKey` - Function to extract unique key from items
|
||||
- `load` - Async function to load missing items
|
||||
|
||||
**Returns:**
|
||||
- `indexStore` - Derived store with items indexed by key
|
||||
- `deriveItem(key, relays)` - Creates a derived store for a specific item
|
||||
- `loadItem(key, relays)` - Manually loads an item
|
||||
- `onItem(callback)` - Subscribe to individual item updates
|
||||
|
||||
### makeCachedLoader(options)
|
||||
|
||||
Creates a cached loader function with staleness checking and exponential backoff.
|
||||
|
||||
**Options:**
|
||||
- `name` - Loader name for freshness tracking
|
||||
- `indexStore` - Store containing indexed items
|
||||
- `load` - Async function to load items
|
||||
- `subscribers` - Array of item update subscribers
|
||||
|
||||
### Freshness Management
|
||||
|
||||
- `getFreshness(ns, key)` - Get last update timestamp for an item
|
||||
- `setFreshnessImmediate(update)` - Immediately update freshness
|
||||
- `setFreshnessThrottled(update)` - Throttled freshness updates
|
||||
|
||||
## Example
|
||||
|
||||
```typescript
|
||||
import {writable} from 'svelte/store'
|
||||
import {derived, readable} from "svelte/store"
|
||||
import {readProfile, PROFILE, PublishedProfile} from "@welshman/util"
|
||||
import {Repository} from "@welshman/relay"
|
||||
import {deriveEventsMapped, collection, withGetter} from "@welshman/store"
|
||||
|
||||
const repository = new Repository()
|
||||
|
||||
export const profiles = writable([])
|
||||
|
||||
export const {
|
||||
indexStore: profilesByPubkey,
|
||||
deriveItem: deriveProfile,
|
||||
loadItem: loadProfile,
|
||||
} = collection({
|
||||
name: "profiles",
|
||||
store: profiles,
|
||||
getKey: profile => profile.event.pubkey,
|
||||
load: (pubkey: string) => // Load the user's profile
|
||||
})
|
||||
|
||||
// Get a reactive store for a specific profile
|
||||
const hints = [/* optional relay hints to load from */]
|
||||
const userProfile = deriveProfile("user-pubkey", hints)
|
||||
|
||||
// Subscribe to profile updates
|
||||
userProfile.subscribe(profile => {
|
||||
console.log("Profile updated:", profile)
|
||||
})
|
||||
```
|
||||
@@ -0,0 +1,56 @@
|
||||
# Custom Store
|
||||
|
||||
Utility for creating custom Svelte stores with start/stop lifecycle and optional throttling.
|
||||
|
||||
## Functions
|
||||
|
||||
### custom(start, options)
|
||||
|
||||
Creates a custom store that starts when first subscribed and stops when last subscriber unsubscribes.
|
||||
|
||||
**Parameters:**
|
||||
- `start` - Function called when first subscriber is added. Receives a `set` function and should return an unsubscriber function
|
||||
- `options` - Optional configuration object
|
||||
|
||||
**Options:**
|
||||
- `throttle` - Throttle subscriber notifications (milliseconds)
|
||||
- `onUpdate` - Callback function called when store value is set
|
||||
|
||||
**Returns:** WritableWithGetter store with `get()`, `set()`, `update()`, and `subscribe()` methods
|
||||
|
||||
## Example
|
||||
|
||||
```typescript
|
||||
import {custom} from "@welshman/store"
|
||||
|
||||
// Create a store that tracks window width
|
||||
const windowWidth = custom(
|
||||
set => {
|
||||
const updateWidth = () => set(window.innerWidth)
|
||||
|
||||
// Set initial value
|
||||
updateWidth()
|
||||
|
||||
// Listen for resize events
|
||||
window.addEventListener('resize', updateWidth)
|
||||
|
||||
// Return cleanup function
|
||||
return () => window.removeEventListener('resize', updateWidth)
|
||||
},
|
||||
{
|
||||
throttle: 100, // Throttle updates to every 100ms
|
||||
onUpdate: (width) => console.log(`Window width: ${width}px`)
|
||||
}
|
||||
)
|
||||
|
||||
// Subscribe to changes
|
||||
const unsubscribe = windowWidth.subscribe(width => {
|
||||
console.log("Width changed:", width)
|
||||
})
|
||||
|
||||
// Get current value
|
||||
console.log("Current width:", windowWidth.get())
|
||||
|
||||
// Clean up
|
||||
unsubscribe()
|
||||
```
|
||||
@@ -1,122 +0,0 @@
|
||||
# Event-Based Stores
|
||||
|
||||
## deriveEventsMapped
|
||||
Creates a store that maintains a mapped collection of events from a repository.
|
||||
Useful when you want to transform events into a different data structure while maintaining reactivity.
|
||||
|
||||
```typescript
|
||||
import {Repository, NAMED_PEOPLE, type TrustedEvent} from '@welshman/util'
|
||||
import {deriveEventsMapped} from '@welshman/store'
|
||||
|
||||
interface UserProfile {
|
||||
name: string;
|
||||
about: string;
|
||||
pubkey: string;
|
||||
}
|
||||
|
||||
const repository = new Repository()
|
||||
|
||||
const profiles = deriveEventsMapped<UserProfile>(repository, {
|
||||
filters: [{kinds: [PROFILE]}],
|
||||
eventToItem: (event: TrustedEvent) => ({
|
||||
name: event.content.name,
|
||||
about: event.content.about,
|
||||
pubkey: event.pubkey,
|
||||
}),
|
||||
itemToEvent: (profile: UserProfile) => ({
|
||||
// Convert profile back to event format
|
||||
kind: PROFILE,
|
||||
pubkey: profile.pubkey,
|
||||
content: {
|
||||
name: profile.name,
|
||||
about: profile.about,
|
||||
}
|
||||
}),
|
||||
throttle: 1000, // Optional: throttle updates
|
||||
includeDeleted: false // Optional: exclude deleted events
|
||||
})
|
||||
```
|
||||
|
||||
## deriveEvents
|
||||
Creates a store that maintains a collection of raw events from a repository.
|
||||
Useful when you want to work directly with events without transformation.
|
||||
|
||||
```typescript
|
||||
import {Repository} from '@welshman/util'
|
||||
import {deriveEvents} from '@welshman/store'
|
||||
|
||||
const repository = new Repository()
|
||||
|
||||
const textNotes = deriveEvents(repository, {
|
||||
filters: [{kinds: [NOTE], // kind 1 = text note
|
||||
authors: ['pubkey1', 'pubkey2']}],
|
||||
throttle: 500,
|
||||
includeDeleted: false
|
||||
})
|
||||
|
||||
// Subscribe to changes
|
||||
textNotes.subscribe(events => {
|
||||
console.log('New text notes:', events)
|
||||
})
|
||||
```
|
||||
|
||||
## deriveEvent
|
||||
Creates a store that tracks a single event by its ID or address.
|
||||
Returns a derived store containing the event or undefined.
|
||||
|
||||
```typescript
|
||||
import {Repository} from '@welshman/util'
|
||||
import {deriveEvent} from '@welshman/store'
|
||||
|
||||
const repository = new Repository()
|
||||
|
||||
const specificEvent = deriveEvent(repository, 'event_id_or_address')
|
||||
|
||||
// Subscribe to changes of the specific event
|
||||
specificEvent.subscribe(event => {
|
||||
if (event) {
|
||||
console.log('Event updated:', event)
|
||||
} else {
|
||||
console.log('Event not found')
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## deriveIsDeleted
|
||||
Creates a store that tracks whether an event has been deleted. Returns a boolean store.
|
||||
|
||||
```typescript
|
||||
import {Repository} from '@welshman/util'
|
||||
import {deriveIsDeleted} from '@welshman/store'
|
||||
|
||||
const repository = new Repository()
|
||||
const event = /* your event */
|
||||
|
||||
const isDeleted = deriveIsDeleted(repository, event)
|
||||
|
||||
// Subscribe to deletion status changes
|
||||
isDeleted.subscribe(deleted => {
|
||||
console.log('Event deleted status:', deleted)
|
||||
})
|
||||
```
|
||||
|
||||
## deriveIsDeletedByAddress
|
||||
Creates a store that tracks whether an event has been deleted by address.
|
||||
Similar to deriveIsDeleted but checks deletion by address instead of event ID.
|
||||
|
||||
```typescript
|
||||
import {Repository} from '@welshman/util'
|
||||
import {deriveIsDeletedByAddress} from '@welshman/store'
|
||||
|
||||
const repository = new Repository()
|
||||
const event = /* your event */
|
||||
|
||||
const isDeletedByAddress = deriveIsDeletedByAddress(repository, event)
|
||||
|
||||
// Subscribe to address-based deletion status changes
|
||||
isDeletedByAddress.subscribe(deleted => {
|
||||
if (deleted) {
|
||||
console.log('Event has been deleted by address')
|
||||
}
|
||||
})
|
||||
```
|
||||
@@ -0,0 +1,55 @@
|
||||
# Getter
|
||||
|
||||
Utilities for adding synchronous `get()` methods to Svelte stores, allowing immediate value access without subscribing. Note that this has performance implications, since it will activate a subscription that will never get unsubscribed. Do not use this on stores that require complex calculations, or which are created and destroyed.
|
||||
|
||||
## Functions
|
||||
|
||||
### getter(store)
|
||||
|
||||
Creates a getter function that returns the current value of a store.
|
||||
|
||||
**Parameters:**
|
||||
- `store` - Any readable Svelte store
|
||||
|
||||
**Returns:** Function that returns the current store value
|
||||
|
||||
### withGetter(store)
|
||||
|
||||
Enhances a store by adding a synchronous `get()` method.
|
||||
|
||||
**Parameters:**
|
||||
- `store` - Readable or writable Svelte store
|
||||
|
||||
**Returns:** Store with added `get()` method
|
||||
|
||||
## Types
|
||||
|
||||
- `ReadableWithGetter<T>` - Readable store with `get()` method
|
||||
- `WritableWithGetter<T>` - Writable store with `get()` method
|
||||
|
||||
## Example
|
||||
|
||||
```typescript
|
||||
import {writable, derived} from "svelte/store"
|
||||
import {withGetter, getter} from "@welshman/store"
|
||||
|
||||
// Create enhanced stores with getter methods
|
||||
const count = withGetter(writable(0))
|
||||
const doubled = withGetter(derived(count, $count => $count * 2))
|
||||
|
||||
// Access values synchronously without subscribing
|
||||
console.log(count.get()) // 0
|
||||
console.log(doubled.get()) // 0
|
||||
|
||||
// Update the store
|
||||
count.set(5)
|
||||
|
||||
// Get updated values immediately
|
||||
console.log(count.get()) // 5
|
||||
console.log(doubled.get()) // 10
|
||||
|
||||
// Alternative: create getter function separately
|
||||
const regularStore = writable(42)
|
||||
const getValue = getter(regularStore)
|
||||
console.log(getValue()) // 42
|
||||
```
|
||||
@@ -1,6 +1,7 @@
|
||||
# @welshman/store
|
||||
|
||||
[](https://npmjs.com/package/@welshman/store)
|
||||
|
||||
A utility package providing welshman-specific svelte store functionality and utilities for managing state. While it's primarily built for use with Svelte's store system, the concepts could be valuable for developers familiar with reactive programming patterns like RxJS.
|
||||
|
||||
## What's Included
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
# Repository
|
||||
|
||||
Reactive Svelte stores for querying and mapping events from a Repository with automatic updates.
|
||||
|
||||
## Functions
|
||||
|
||||
### deriveEventsMapped(repository, options)
|
||||
|
||||
Creates a reactive store that maps events to custom items and keeps them synchronized with repository updates.
|
||||
|
||||
**Options:**
|
||||
- `filters` - Array of Nostr filters to query events
|
||||
- `eventToItem` - Function to transform events to items (can return Promise)
|
||||
- `itemToEvent` - Function to extract the event from an item
|
||||
- `throttle?` - Throttle updates (milliseconds, default: 0)
|
||||
- `includeDeleted?` - Include deleted events (default: false)
|
||||
|
||||
### deriveEvents(repository, options)
|
||||
|
||||
Creates a reactive store of events without transformation.
|
||||
|
||||
**Options:**
|
||||
- `filters` - Array of Nostr filters
|
||||
- `throttle?` - Throttle updates
|
||||
- `includeDeleted?` - Include deleted events
|
||||
|
||||
### deriveEvent(repository, idOrAddress)
|
||||
|
||||
Creates a reactive store for a single event by ID or address.
|
||||
|
||||
### deriveIsDeleted(repository, event)
|
||||
|
||||
Creates a reactive store that tracks whether an event is deleted.
|
||||
|
||||
### deriveIsDeletedByAddress(repository, event)
|
||||
|
||||
Creates a reactive store that tracks whether an event is deleted by address.
|
||||
|
||||
## Example
|
||||
|
||||
```typescript
|
||||
import {Repository} from "@welshman/relay"
|
||||
import {deriveEventsMapped, deriveEvents} from "@welshman/store"
|
||||
import {readProfile, PROFILE} from "@welshman/util"
|
||||
|
||||
const repository = new Repository()
|
||||
|
||||
// Reactive store of text notes
|
||||
const textNotes = deriveEvents(repository, {
|
||||
filters: [{kinds: [1], limit: 100}],
|
||||
throttle: 100
|
||||
})
|
||||
|
||||
// Reactive store of profiles mapped to custom objects
|
||||
const profiles = deriveEventsMapped(repository, {
|
||||
filters: [{kinds: [PROFILE]}],
|
||||
eventToItem: event => readProfile(event),
|
||||
itemToEvent: profile => profile.event,
|
||||
includeDeleted: false
|
||||
})
|
||||
|
||||
// Subscribe to updates
|
||||
textNotes.subscribe(notes => {
|
||||
console.log(`Found ${notes.length} text notes`)
|
||||
})
|
||||
|
||||
profiles.subscribe(profiles => {
|
||||
console.log(`Found ${profiles.length} profiles`)
|
||||
})
|
||||
|
||||
// Add some events to the repository
|
||||
repository.publish(someTextNoteEvent)
|
||||
repository.publish(someProfileEvent)
|
||||
```
|
||||
@@ -0,0 +1,44 @@
|
||||
# Synced Store
|
||||
|
||||
Utility for creating Svelte stores that automatically persist to and restore from localStorage.
|
||||
|
||||
## Functions
|
||||
|
||||
### synced(key, defaultValue)
|
||||
|
||||
Creates a writable store that synchronizes with localStorage using JSON serialization.
|
||||
|
||||
**Parameters:**
|
||||
- `key` - localStorage key to store the value under
|
||||
- `defaultValue` - Default value if nothing exists in localStorage
|
||||
|
||||
**Returns:** Writable Svelte store that persists changes to localStorage
|
||||
|
||||
The store automatically:
|
||||
- Loads initial value from localStorage on creation
|
||||
- Saves any changes back to localStorage
|
||||
- Falls back to defaultValue if localStorage is empty or invalid
|
||||
|
||||
## Example
|
||||
|
||||
```typescript
|
||||
import {synced} from "@welshman/store"
|
||||
|
||||
// Create a store that persists user preferences
|
||||
const userPreferences = synced("user-prefs", {
|
||||
theme: "dark",
|
||||
notifications: true,
|
||||
language: "en"
|
||||
})
|
||||
|
||||
// Use like any writable store
|
||||
userPreferences.subscribe(prefs => {
|
||||
console.log("Preferences:", prefs)
|
||||
})
|
||||
|
||||
// Update the store - automatically saves to localStorage
|
||||
userPreferences.update(prefs => ({
|
||||
...prefs,
|
||||
theme: "light"
|
||||
}))
|
||||
```
|
||||
@@ -0,0 +1,48 @@
|
||||
# Throttled Store
|
||||
|
||||
Utility for wrapping Svelte stores to throttle subscriber notifications, reducing update frequency for performance.
|
||||
|
||||
## Functions
|
||||
|
||||
### throttled(delay, store)
|
||||
|
||||
Creates a throttled version of a store that limits how often subscribers are notified.
|
||||
|
||||
**Parameters:**
|
||||
- `delay` - Throttle delay in milliseconds (0 disables throttling)
|
||||
- `store` - Any readable Svelte store
|
||||
|
||||
**Returns:** Store with throttled subscription behavior
|
||||
|
||||
When `delay` is 0, returns the original store unchanged. Otherwise, wraps the store so that subscribers receive updates at most once per delay period.
|
||||
|
||||
## Example
|
||||
|
||||
```typescript
|
||||
import {writable} from "svelte/store"
|
||||
import {throttled} from "@welshman/store"
|
||||
|
||||
// Create a regular store that updates frequently
|
||||
const fastStore = writable(0)
|
||||
|
||||
// Create a throttled version that only notifies every 100ms
|
||||
const slowStore = throttled(100, fastStore)
|
||||
|
||||
// Subscribe to both stores
|
||||
fastStore.subscribe(value => console.log("Fast:", value))
|
||||
slowStore.subscribe(value => console.log("Slow:", value))
|
||||
|
||||
// Rapidly update the store
|
||||
let count = 0
|
||||
const interval = setInterval(() => {
|
||||
fastStore.set(++count)
|
||||
|
||||
if (count >= 10) {
|
||||
clearInterval(interval)
|
||||
}
|
||||
}, 10) // Update every 10ms
|
||||
|
||||
// Output:
|
||||
// Fast: 1, Fast: 2, Fast: 3, ... (every update)
|
||||
// Slow: 1, Slow: 5, Slow: 10 (throttled to ~100ms intervals)
|
||||
```
|
||||
Reference in New Issue
Block a user