seitu 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -1
- package/dist/solid.d.mts +170 -0
- package/dist/solid.mjs +180 -0
- package/dist/svelte.d.mts +77 -0
- package/dist/svelte.mjs +91 -0
- package/package.json +38 -6
- package/skills/README.md +114 -0
- package/skills/create-computed/SKILL.md +108 -0
- package/skills/create-debounced/SKILL.md +94 -0
- package/skills/create-debounced-fn/SKILL.md +95 -0
- package/skills/{createIndexedDbStorage → create-indexed-db-storage}/SKILL.md +62 -4
- package/skills/create-is-online/SKILL.md +87 -0
- package/skills/create-media-query/SKILL.md +101 -0
- package/skills/{createReadableSubscription → create-readable-subscription}/SKILL.md +60 -4
- package/skills/create-schema-store/SKILL.md +98 -0
- package/skills/{createScrollState → create-scroll-state}/SKILL.md +60 -4
- package/skills/create-store/SKILL.md +109 -0
- package/skills/{createSubscription → create-subscription}/SKILL.md +59 -4
- package/skills/create-throttled/SKILL.md +93 -0
- package/skills/create-throttled-fn/SKILL.md +96 -0
- package/skills/{createWebStorage → create-web-storage}/SKILL.md +65 -4
- package/skills/create-web-storage-value/SKILL.md +118 -0
- package/skills/seitu-overview/SKILL.md +95 -7
- package/skills/subscription-react/SKILL.md +96 -0
- package/skills/subscription-solid/SKILL.md +97 -0
- package/skills/{useSubscription-react → use-subscription-react}/SKILL.md +69 -4
- package/skills/use-subscription-solid/SKILL.md +144 -0
- package/skills/use-subscription-svelte/SKILL.md +134 -0
- package/skills/{useSubscription-vue → use-subscription-vue}/SKILL.md +61 -4
- package/skills/Subscription-react/SKILL.md +0 -34
- package/skills/createComputed/SKILL.md +0 -50
- package/skills/createDebounced/SKILL.md +0 -37
- package/skills/createDebouncedFn/SKILL.md +0 -39
- package/skills/createIsOnline/SKILL.md +0 -30
- package/skills/createMediaQuery/SKILL.md +0 -43
- package/skills/createSchemaStore/SKILL.md +0 -42
- package/skills/createStore/SKILL.md +0 -48
- package/skills/createThrottled/SKILL.md +0 -37
- package/skills/createThrottledFn/SKILL.md +0 -40
- package/skills/createWebStorageValue/SKILL.md +0 -60
package/skills/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Seitu agent skills (TanStack Intent)
|
|
2
|
+
|
|
3
|
+
These skills teach AI assistants how to integrate [Seitu](https://seitu.letstri.dev) in **your** app — not how to work on the Seitu library monorepo.
|
|
4
|
+
|
|
5
|
+
Skills ship inside the `seitu` npm package and are versioned with each release. They include `sources` metadata pointing at docs and source files so maintainers can detect drift when documentation changes.
|
|
6
|
+
|
|
7
|
+
## Install via npm (recommended)
|
|
8
|
+
|
|
9
|
+
After adding Seitu to your project:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add seitu
|
|
13
|
+
pnpm dlx @tanstack/intent@latest install
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Intent discovers `seitu` in `node_modules`, reads the skills bundled with your installed version, and writes lightweight skill-loading guidance into your agent config (`AGENTS.md`, `CLAUDE.md`, `.cursorrules`, etc.).
|
|
17
|
+
|
|
18
|
+
List or load a specific skill:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm dlx @tanstack/intent@latest list
|
|
22
|
+
pnpm dlx @tanstack/intent@latest load seitu#create-store
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
When you `pnpm update seitu`, skills update with the package — knowledge travels through npm, not model training cutoffs.
|
|
26
|
+
|
|
27
|
+
Start with **`seitu-overview`** — module map, mental model, and decision tree.
|
|
28
|
+
|
|
29
|
+
## Manual install (Cursor)
|
|
30
|
+
|
|
31
|
+
Copy skill folders into `.cursor/skills/`:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
cp -r node_modules/seitu/skills/seitu-overview .cursor/skills/
|
|
35
|
+
cp -r node_modules/seitu/skills/create-store .cursor/skills/
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Restart Cursor or start a new agent chat so skills are picked up.
|
|
39
|
+
|
|
40
|
+
## Skills
|
|
41
|
+
|
|
42
|
+
### Core (`seitu`)
|
|
43
|
+
|
|
44
|
+
| Skill | Intent id | When to use |
|
|
45
|
+
|-------|-----------|-------------|
|
|
46
|
+
| [seitu-overview](./seitu-overview/SKILL.md) | `seitu#seitu-overview` | Read first — module map and primitive selection |
|
|
47
|
+
| [create-store](./create-store/SKILL.md) | `seitu#create-store` | Simple in-memory reactive state |
|
|
48
|
+
| [create-schema-store](./create-schema-store/SKILL.md) | `seitu#create-schema-store` | Schema-validated state (Zod, Valibot, ArkType) |
|
|
49
|
+
| [create-computed](./create-computed/SKILL.md) | `seitu#create-computed` | Derived read-only values |
|
|
50
|
+
| [create-debounced](./create-debounced/SKILL.md) | `seitu#create-debounced` | Debounce subscribable updates |
|
|
51
|
+
| [create-throttled](./create-throttled/SKILL.md) | `seitu#create-throttled` | Throttle subscribable updates |
|
|
52
|
+
| [create-debounced-fn](./create-debounced-fn/SKILL.md) | `seitu#create-debounced-fn` | Debounced function with reactive result |
|
|
53
|
+
| [create-throttled-fn](./create-throttled-fn/SKILL.md) | `seitu#create-throttled-fn` | Throttled function with reactive result |
|
|
54
|
+
| [create-subscription](./create-subscription/SKILL.md) | `seitu#create-subscription` | Low-level subscribe/notify |
|
|
55
|
+
| [create-readable-subscription](./create-readable-subscription/SKILL.md) | `seitu#create-readable-subscription` | Compose standard Readable & Subscribable |
|
|
56
|
+
|
|
57
|
+
### Web (`seitu/web`)
|
|
58
|
+
|
|
59
|
+
| Skill | Intent id | When to use |
|
|
60
|
+
|-------|-----------|-------------|
|
|
61
|
+
| [create-web-storage-value](./create-web-storage-value/SKILL.md) | `seitu#create-web-storage-value` | Single-key localStorage / sessionStorage |
|
|
62
|
+
| [create-web-storage](./create-web-storage/SKILL.md) | `seitu#create-web-storage` | Multi-key web storage |
|
|
63
|
+
| [create-indexed-db-storage](./create-indexed-db-storage/SKILL.md) | `seitu#create-indexed-db-storage` | Large or async IndexedDB state |
|
|
64
|
+
| [create-media-query](./create-media-query/SKILL.md) | `seitu#create-media-query` | Reactive CSS media queries |
|
|
65
|
+
| [create-is-online](./create-is-online/SKILL.md) | `seitu#create-is-online` | Online / offline status |
|
|
66
|
+
| [create-scroll-state](./create-scroll-state/SKILL.md) | `seitu#create-scroll-state` | Scroll position and edges |
|
|
67
|
+
|
|
68
|
+
### React (`seitu/react`)
|
|
69
|
+
|
|
70
|
+
| Skill | Intent id | When to use |
|
|
71
|
+
|-------|-----------|-------------|
|
|
72
|
+
| [use-subscription-react](./use-subscription-react/SKILL.md) | `seitu#use-subscription-react` | Hook for any Seitu primitive |
|
|
73
|
+
| [subscription-react](./subscription-react/SKILL.md) | `seitu#subscription-react` | Render-prop component |
|
|
74
|
+
|
|
75
|
+
### Vue (`seitu/vue`)
|
|
76
|
+
|
|
77
|
+
| Skill | Intent id | When to use |
|
|
78
|
+
|-------|-----------|-------------|
|
|
79
|
+
| [use-subscription-vue](./use-subscription-vue/SKILL.md) | `seitu#use-subscription-vue` | Composable for any Seitu primitive |
|
|
80
|
+
|
|
81
|
+
### Solid (`seitu/solid`)
|
|
82
|
+
|
|
83
|
+
| Skill | Intent id | When to use |
|
|
84
|
+
|-------|-----------|-------------|
|
|
85
|
+
| [use-subscription-solid](./use-subscription-solid/SKILL.md) | `seitu#use-subscription-solid` | Primitive returning an accessor for any Seitu primitive |
|
|
86
|
+
| [subscription-solid](./subscription-solid/SKILL.md) | `seitu#subscription-solid` | Render-prop component |
|
|
87
|
+
|
|
88
|
+
### Svelte (`seitu/svelte`)
|
|
89
|
+
|
|
90
|
+
| Skill | Intent id | When to use |
|
|
91
|
+
|-------|-----------|-------------|
|
|
92
|
+
| [use-subscription-svelte](./use-subscription-svelte/SKILL.md) | `seitu#use-subscription-svelte` | Binding returning a Svelte `Readable` store for any Seitu primitive |
|
|
93
|
+
|
|
94
|
+
## Registry and version history
|
|
95
|
+
|
|
96
|
+
The package includes the `tanstack-intent` npm keyword. Published versions are indexed on the [Agent Skills Registry](https://tanstack.com/intent/registry) with skill history per release.
|
|
97
|
+
|
|
98
|
+
## Without skills
|
|
99
|
+
|
|
100
|
+
- Official docs: https://seitu.letstri.dev/docs
|
|
101
|
+
- LLM-oriented export: https://seitu.letstri.dev/llms.txt
|
|
102
|
+
|
|
103
|
+
## Maintainer workflow (this repo)
|
|
104
|
+
|
|
105
|
+
From `seitu/`:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
pnpm run skills:validate # structure + packaging before publish
|
|
109
|
+
pnpm run skills:stale # flag drift vs docs/sources
|
|
110
|
+
pnpm run skills:sync-state # refresh source SHAs after doc/source edits
|
|
111
|
+
pnpm run skills:upgrade # re-apply frontmatter + Common Mistakes from _artifacts
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
CI runs `intent validate` on PRs and `intent stale` after releases (`.github/workflows/check-skills.yml`). Update `library_version` in SKILL frontmatter when cutting a release.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-computed
|
|
3
|
+
description: >-
|
|
4
|
+
Derived read-only subscription from one or many sources.
|
|
5
|
+
type: core
|
|
6
|
+
library: seitu
|
|
7
|
+
library_version: "0.16.0"
|
|
8
|
+
requires:
|
|
9
|
+
- seitu-overview
|
|
10
|
+
sources:
|
|
11
|
+
- letstri/seitu:docs/content/docs/core/computed.mdx
|
|
12
|
+
- letstri/seitu:seitu/src/core/computed.ts
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# createComputed
|
|
16
|
+
|
|
17
|
+
Derived read-only subscription from one or many sources. Lazy — only subscribes to sources when it has its own subscribers.
|
|
18
|
+
|
|
19
|
+
## Single source
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { createComputed, createStore } from 'seitu'
|
|
23
|
+
|
|
24
|
+
const store = createStore({ a: 1, b: 2 })
|
|
25
|
+
const sum = createComputed(store, s => s.a + s.b)
|
|
26
|
+
sum.get() // 3
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Multiple sources
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { createComputed, createStore } from 'seitu'
|
|
33
|
+
|
|
34
|
+
const a = createStore(1)
|
|
35
|
+
const b = createStore(2)
|
|
36
|
+
const total = createComputed([a, b], ([a, b]) => a + b)
|
|
37
|
+
total.get() // 3
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Overloads
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
function createComputed<T, R>(source: Source<T>, transform: (value: T) => R): Computed<R>
|
|
44
|
+
function createComputed<S extends Source[], R>(sources: [...S], transform: (values: SourceValues<S>) => R): Computed<R>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Interface
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
interface Computed<T> extends Readable<T>, Subscribable<T> {}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Read-only. No `set()`.
|
|
54
|
+
## Common Mistakes
|
|
55
|
+
|
|
56
|
+
### [HIGH] Recomputing manually in components
|
|
57
|
+
|
|
58
|
+
Wrong:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
const sum = a.get() + b.get()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Correct:
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
const sum = createComputed([a, b], ([x, y]) => x + y)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
createComputed subscribes to sources and caches until they notify.
|
|
71
|
+
|
|
72
|
+
### [HIGH] Creating computed inside render
|
|
73
|
+
|
|
74
|
+
Wrong:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
function App() {
|
|
78
|
+
const total = createComputed(store, s => s.a + s.b)
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Correct:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
const total = createComputed(store, s => s.a + s.b)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
New computed each render re-subscribes and loses memoization benefits.
|
|
89
|
+
|
|
90
|
+
### [MEDIUM] Writing to computed
|
|
91
|
+
|
|
92
|
+
Wrong:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
sum.set(10)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Correct:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
store.set({ a: 5, b: 5 })
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Computed is read-only; use the source store's set().
|
|
105
|
+
|
|
106
|
+
## Source
|
|
107
|
+
|
|
108
|
+
`src/core/computed.ts`
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-debounced
|
|
3
|
+
description: >-
|
|
4
|
+
Debounce updates from a source subscribable.
|
|
5
|
+
type: core
|
|
6
|
+
library: seitu
|
|
7
|
+
library_version: "0.16.0"
|
|
8
|
+
requires:
|
|
9
|
+
- seitu-overview
|
|
10
|
+
sources:
|
|
11
|
+
- letstri/seitu:docs/content/docs/core/debounced.mdx
|
|
12
|
+
- letstri/seitu:seitu/src/core/debounced.ts
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# createDebounced
|
|
16
|
+
|
|
17
|
+
Wraps a source subscribable with debounce. Emits the latest value after `wait` ms of inactivity.
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { createStore, createDebounced } from 'seitu'
|
|
21
|
+
|
|
22
|
+
const input = createStore('')
|
|
23
|
+
const debounced = createDebounced(input, 300)
|
|
24
|
+
debounced.subscribe(value => console.log('debounced:', value))
|
|
25
|
+
debounced.get() // current debounced value
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Signature
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
function createDebounced<T>(source: Readable<T> & Subscribable<T>, wait: number): Debounced<T>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Interface
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
interface Debounced<T> extends Readable<T>, Subscribable<T> {}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Read-only. Lazy subscription — only subscribes to source when it has its own subscribers.
|
|
41
|
+
## Common Mistakes
|
|
42
|
+
|
|
43
|
+
### [HIGH] Debouncing the source instead of wrapping it
|
|
44
|
+
|
|
45
|
+
Wrong:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
const raw = createStore(0)
|
|
49
|
+
setTimeout(() => raw.set(v), 300)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Correct:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
const debounced = createDebounced(raw, 300)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
createDebounced wraps a Readable & Subscribable; mutating the source bypasses debounce.
|
|
59
|
+
|
|
60
|
+
### [MEDIUM] Expecting immediate get after source change
|
|
61
|
+
|
|
62
|
+
Wrong:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
source.set(1); debounced.get() // expects 1 immediately
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Correct:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
debounced.subscribe(v => console.log(v))
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
get() returns last emitted value until wait elapses.
|
|
75
|
+
|
|
76
|
+
### [LOW] Zero or negative wait
|
|
77
|
+
|
|
78
|
+
Wrong:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
createDebounced(source, 0)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Correct:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
createDebounced(source, 300)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Invalid wait breaks timing expectations.
|
|
91
|
+
|
|
92
|
+
## Source
|
|
93
|
+
|
|
94
|
+
`src/core/debounced.ts`
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-debounced-fn
|
|
3
|
+
description: >-
|
|
4
|
+
Debounced callable with reactive return value.
|
|
5
|
+
type: core
|
|
6
|
+
library: seitu
|
|
7
|
+
library_version: "0.16.0"
|
|
8
|
+
requires:
|
|
9
|
+
- seitu-overview
|
|
10
|
+
sources:
|
|
11
|
+
- letstri/seitu:docs/content/docs/core/debounced-fn.mdx
|
|
12
|
+
- letstri/seitu:seitu/src/core/debounced-fn.ts
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# createDebouncedFn
|
|
16
|
+
|
|
17
|
+
Wraps a plain function. The return value becomes subscribable state. Each call resets the debounce timer.
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { createDebouncedFn } from 'seitu'
|
|
21
|
+
|
|
22
|
+
const search = createDebouncedFn((q: string) => fetch(`/api?q=${q}`), 300)
|
|
23
|
+
search('hello') // debounced — fires after 300ms of inactivity
|
|
24
|
+
search.get() // latest return value (undefined until first call)
|
|
25
|
+
search.subscribe(result => console.log('result:', result))
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Signature
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
function createDebouncedFn<F extends (...args: any[]) => any>(fn: F, wait: number): DebouncedFn<F>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Interface
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
interface DebouncedFn<F> extends Readable<ReturnType<F> | undefined>, Subscribable<ReturnType<F> | undefined> {
|
|
38
|
+
(...args: Parameters<F>): void
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Callable + readable + subscribable. `get()` returns `undefined` until first execution.
|
|
43
|
+
## Common Mistakes
|
|
44
|
+
|
|
45
|
+
### [HIGH] Using createDebounced on a function
|
|
46
|
+
|
|
47
|
+
Wrong:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
createDebounced(fn, 300)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Correct:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
createDebouncedFn(fn, 300)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
createDebounced wraps subscribables; createDebouncedFn wraps callables.
|
|
60
|
+
|
|
61
|
+
### [MEDIUM] Expecting synchronous return from call
|
|
62
|
+
|
|
63
|
+
Wrong:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
const result = debouncedFn()
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Correct:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
debouncedFn(); const result = useSubscription(debouncedFn)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Return value updates after debounced execution via subscription.
|
|
76
|
+
|
|
77
|
+
### [MEDIUM] Not subscribing to fn result
|
|
78
|
+
|
|
79
|
+
Wrong:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
debouncedFn(args) // never read output
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Correct:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
debouncedFn(args); debouncedFn.subscribe(console.log)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
DebouncedFn is Subscribable; subscribe or useSubscription to read latest result.
|
|
92
|
+
|
|
93
|
+
## Source
|
|
94
|
+
|
|
95
|
+
`src/core/debounced-fn.ts`
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: create-indexed-db-storage
|
|
3
3
|
description: >-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
Async IndexedDB persistence with cached sync reads.
|
|
5
|
+
type: core
|
|
6
|
+
library: seitu
|
|
7
|
+
library_version: "0.16.0"
|
|
8
|
+
requires:
|
|
9
|
+
- seitu-overview
|
|
10
|
+
sources:
|
|
11
|
+
- letstri/seitu:docs/content/docs/web/indexed-db-storage.mdx
|
|
12
|
+
- letstri/seitu:seitu/src/web/indexed-db-storage.ts
|
|
6
13
|
---
|
|
7
14
|
|
|
8
15
|
# createIndexedDbStorage
|
|
@@ -57,7 +64,58 @@ interface IndexedDbStorage<O> extends Subscribable<O>, Readable<O>, Writable<Par
|
|
|
57
64
|
clear: () => Promise<void>
|
|
58
65
|
}
|
|
59
66
|
```
|
|
67
|
+
## Common Mistakes
|
|
68
|
+
|
|
69
|
+
### [HIGH] Treating set as synchronous
|
|
70
|
+
|
|
71
|
+
Wrong:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
await storage.set(data)
|
|
75
|
+
expect(storage.get()).toEqual(persistedFromDb)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Correct:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
storage.set(data)
|
|
82
|
+
storage.subscribe(next => { /* react when cache updates */ })
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
IndexedDB writes are async; get() returns cached value immediately.
|
|
86
|
+
|
|
87
|
+
### [MEDIUM] Using in Node without fake-indexeddb
|
|
88
|
+
|
|
89
|
+
Wrong:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
createIndexedDbStorage({ ... }) // in vitest without polyfill
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Correct:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import 'fake-indexeddb/auto'
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
IndexedDB is browser-only; tests need fake-indexeddb polyfill.
|
|
102
|
+
|
|
103
|
+
### [HIGH] Missing schemas for keys
|
|
104
|
+
|
|
105
|
+
Wrong:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
createIndexedDbStorage({ dbName: 'app' })
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Correct:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
createIndexedDbStorage({ dbName: 'app', schemas: { items: z.array(z.string()) }, defaultValues: { items: [] } })
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Same as WebStorage — keys must be declared in schemas/defaultValues.
|
|
60
118
|
|
|
61
119
|
## Source
|
|
62
120
|
|
|
63
|
-
`
|
|
121
|
+
`src/web/indexed-db-storage.ts`
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-is-online
|
|
3
|
+
description: >-
|
|
4
|
+
Reactive navigator.onLine status.
|
|
5
|
+
type: core
|
|
6
|
+
library: seitu
|
|
7
|
+
library_version: "0.16.0"
|
|
8
|
+
requires:
|
|
9
|
+
- seitu-overview
|
|
10
|
+
sources:
|
|
11
|
+
- letstri/seitu:docs/content/docs/web/is-online.mdx
|
|
12
|
+
- letstri/seitu:seitu/src/web/is-online.ts
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# createIsOnline
|
|
16
|
+
|
|
17
|
+
Reactive boolean for `navigator.onLine`. Listens to `online`/`offline` window events.
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { createIsOnline } from 'seitu/web'
|
|
21
|
+
|
|
22
|
+
const online = createIsOnline()
|
|
23
|
+
online.get() // true or false
|
|
24
|
+
online.subscribe(v => console.log(v ? 'online' : 'offline'))
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Interface
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
interface IsOnline extends Subscribable<boolean>, Readable<boolean> {}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
No options. Returns `true` during SSR (when `navigator` is undefined). Lazy — only listens to events while subscribed.
|
|
34
|
+
## Common Mistakes
|
|
35
|
+
|
|
36
|
+
### [MEDIUM] Polling navigator.onLine manually
|
|
37
|
+
|
|
38
|
+
Wrong:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
setInterval(() => setOnline(navigator.onLine), 1000)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Correct:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
const online = createIsOnline()
|
|
48
|
+
online.subscribe(v => setOnline(v))
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
createIsOnline subscribes to online/offline events.
|
|
52
|
+
|
|
53
|
+
### [MEDIUM] Assuming onLine means reachable server
|
|
54
|
+
|
|
55
|
+
Wrong:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
if (online.get()) fetch('/api') // always succeeds
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Correct:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
if (online.get()) fetch('/api').catch(handleOffline)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
onLine is browser connectivity, not server health.
|
|
68
|
+
|
|
69
|
+
### [LOW] Guarding creation with typeof window
|
|
70
|
+
|
|
71
|
+
Wrong:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
const online = typeof window !== 'undefined' ? createIsOnline() : null
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Correct:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
const online = createIsOnline()
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Returns false when offline API unavailable; safe at module scope.
|
|
84
|
+
|
|
85
|
+
## Source
|
|
86
|
+
|
|
87
|
+
`src/web/is-online.ts`
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-media-query
|
|
3
|
+
description: >-
|
|
4
|
+
Reactive CSS media query with SSR defaultMatches.
|
|
5
|
+
type: core
|
|
6
|
+
library: seitu
|
|
7
|
+
library_version: "0.16.0"
|
|
8
|
+
requires:
|
|
9
|
+
- seitu-overview
|
|
10
|
+
sources:
|
|
11
|
+
- letstri/seitu:docs/content/docs/web/media-query.mdx
|
|
12
|
+
- letstri/seitu:seitu/src/web/media-query.ts
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# createMediaQuery
|
|
16
|
+
|
|
17
|
+
Reactive boolean for CSS media queries. Provides type-safe query string with autocomplete for known media features.
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { createMediaQuery } from 'seitu/web'
|
|
21
|
+
|
|
22
|
+
const isDark = createMediaQuery({ query: '(prefers-color-scheme: dark)' })
|
|
23
|
+
const isDesktop = createMediaQuery({ query: '(min-width: 768px)' })
|
|
24
|
+
|
|
25
|
+
isDark.get() // boolean
|
|
26
|
+
isDark.subscribe(matches => console.log(matches))
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Options
|
|
30
|
+
|
|
31
|
+
| Option | Type | Description |
|
|
32
|
+
|--------|------|-------------|
|
|
33
|
+
| `query` | type-safe string | CSS media query |
|
|
34
|
+
| `defaultMatches?` | `boolean` | SSR fallback (default `false`) |
|
|
35
|
+
|
|
36
|
+
## Interface
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
interface MediaQuery extends Subscribable<boolean>, Readable<boolean> {}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Read-only. Lazy — listens to `change` events only while subscribed.
|
|
43
|
+
|
|
44
|
+
## Type-safe queries
|
|
45
|
+
|
|
46
|
+
The `query` option provides autocomplete for standard media features: `min-width`, `max-width`, `prefers-color-scheme`, `orientation`, `hover`, `pointer`, etc. Supports `and`/`,` combinators.
|
|
47
|
+
## Common Mistakes
|
|
48
|
+
|
|
49
|
+
### [CRITICAL] Missing defaultMatches for SSR
|
|
50
|
+
|
|
51
|
+
Wrong:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
createMediaQuery({ query: '(min-width: 768px)' })
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Correct:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
createMediaQuery({ query: '(min-width: 768px)', defaultMatches: true })
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
get() returns false when window is undefined unless defaultMatches is set.
|
|
64
|
+
|
|
65
|
+
### [HIGH] Invalid media query string
|
|
66
|
+
|
|
67
|
+
Wrong:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
createMediaQuery({ query: '(min-width: 768' })
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Correct:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
createMediaQuery({ query: '(min-width: 768px)' })
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Malformed queries throw at creation time in the browser.
|
|
80
|
+
|
|
81
|
+
### [HIGH] Creating query inside component each render
|
|
82
|
+
|
|
83
|
+
Wrong:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
function Layout() {
|
|
87
|
+
const mq = createMediaQuery({ query: '...' })
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Correct:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
const isDesktop = createMediaQuery({ query: '(min-width: 768px)' })
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
New matchMedia listener per render; use module-level singleton.
|
|
98
|
+
|
|
99
|
+
## Source
|
|
100
|
+
|
|
101
|
+
`src/web/media-query.ts`
|