seitu 0.15.1 → 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.
@@ -0,0 +1,170 @@
1
+ import { _ as Readable, v as Subscribable } from "./index-DwN8Up-P.mjs";
2
+ import { Accessor, JSX } from "solid-js";
3
+
4
+ //#region src/solid/hooks.d.ts
5
+ interface UseSubscriptionOptions<S extends Subscribable<any> & Readable<any>, R = S['~']['output']> {
6
+ selector?: (value: S['~']['output']) => R;
7
+ isEqual?: (prev: R, next: R) => boolean;
8
+ }
9
+ /**
10
+ * Use this primitive to subscribe to a reactive value. Accepts a subscription object
11
+ * directly, or an accessor/getter that returns one. The getter is reactive — when it
12
+ * reads a signal, the subscription is recreated and re-subscribed automatically.
13
+ *
14
+ * Returns a Solid `Accessor<R>`: call it (`value()`) to read the current value inside JSX
15
+ * or another reactive scope.
16
+ *
17
+ * @example Inline subscription
18
+ * ```tsx
19
+ * import { createWebStorageValue } from 'seitu/web'
20
+ * import { useSubscription } from 'seitu/solid'
21
+ * import * as z from 'zod'
22
+ *
23
+ * function Counter() {
24
+ * const value = useSubscription(() => createWebStorageValue({
25
+ * type: 'sessionStorage',
26
+ * key: 'test',
27
+ * defaultValue: 0,
28
+ * schema: z.number(),
29
+ * }))
30
+ *
31
+ * return <div>{value()}</div>
32
+ * }
33
+ * ```
34
+ *
35
+ * @example Instance outside of component
36
+ * ```tsx
37
+ * import { createWebStorage } from 'seitu/web'
38
+ * import { useSubscription } from 'seitu/solid'
39
+ * import * as z from 'zod'
40
+ *
41
+ * const sessionStorage = createWebStorage({
42
+ * type: 'sessionStorage',
43
+ * schemas: { count: z.number(), name: z.string() },
44
+ * defaultValues: { count: 0, name: '' },
45
+ * })
46
+ *
47
+ * function Counter() {
48
+ * const value = useSubscription(sessionStorage)
49
+ * return <div>{value().count}</div>
50
+ * }
51
+ * ```
52
+ *
53
+ * @example Subscription with selector
54
+ * ```tsx
55
+ * import { createWebStorage } from 'seitu/web'
56
+ * import { useSubscription } from 'seitu/solid'
57
+ * import * as z from 'zod'
58
+ *
59
+ * const sessionStorage = createWebStorage({
60
+ * type: 'sessionStorage',
61
+ * schemas: {
62
+ * count: z.number(),
63
+ * name: z.string(),
64
+ * },
65
+ * defaultValues: { count: 0, name: '' },
66
+ * })
67
+ *
68
+ * function Counter() {
69
+ * // Usage with selector, the accessor only updates when count changes
70
+ * const count = useSubscription(sessionStorage, { selector: value => value.count })
71
+ *
72
+ * return <div>{count()}</div>
73
+ * }
74
+ * ```
75
+ *
76
+ * @example Reactive source (re-subscribes when a signal changes)
77
+ * ```tsx
78
+ * import { createSignal } from 'solid-js'
79
+ * import { createWebStorageValue } from 'seitu/web'
80
+ * import { useSubscription } from 'seitu/solid'
81
+ * import * as z from 'zod'
82
+ *
83
+ * function User() {
84
+ * const [userId, setUserId] = createSignal('user-1')
85
+ * const data = useSubscription(() => createWebStorageValue({
86
+ * type: 'localStorage',
87
+ * key: `user:${userId()}`,
88
+ * schema: z.object({ name: z.string() }),
89
+ * defaultValue: { name: '' },
90
+ * }))
91
+ *
92
+ * return <div>{data().name}</div>
93
+ * }
94
+ * ```
95
+ *
96
+ * @example Ref example
97
+ * ```tsx
98
+ * import { createScrollState } from 'seitu/web'
99
+ * import { useSubscription } from 'seitu/solid'
100
+ *
101
+ * function Page() {
102
+ * let ref: HTMLDivElement | undefined
103
+ * const state = useSubscription(() => createScrollState({ element: () => ref, direction: 'vertical' }))
104
+ *
105
+ * return (
106
+ * <div ref={ref}>
107
+ * {String(state().top.reached)}
108
+ * </div>
109
+ * )
110
+ * }
111
+ * ```
112
+ */
113
+ declare function useSubscription<S extends Subscribable<any> & Readable<any>, R = S['~']['output']>(source: S | Accessor<S>, options?: UseSubscriptionOptions<S, R>): Accessor<R>;
114
+ //#endregion
115
+ //#region src/solid/components.d.ts
116
+ interface SubscriptionProps<S extends Subscribable<any> & Readable<any>, R = S['~']['output']> extends Pick<UseSubscriptionOptions<S, R>, 'selector'> {
117
+ value: S;
118
+ children: (value: Accessor<R>) => JSX.Element;
119
+ }
120
+ /**
121
+ * Declarative component that subscribes to a reactive value and passes an accessor to a render function.
122
+ * Unlike React, the children function runs once and receives a Solid `Accessor<R>` — call it (`value()`)
123
+ * inside the returned JSX so updates stay fine-grained. Use when you prefer a component API over the
124
+ * useSubscription primitive.
125
+ *
126
+ * @example Basic usage
127
+ * ```tsx
128
+ * import { createWebStorage } from 'seitu/web'
129
+ * import { Subscription } from 'seitu/solid'
130
+ * import * as z from 'zod'
131
+ *
132
+ * const sessionStorage = createWebStorage({
133
+ * type: 'sessionStorage',
134
+ * schemas: { count: z.number(), name: z.string() },
135
+ * defaultValues: { count: 0, name: '' },
136
+ * })
137
+ *
138
+ * function Page() {
139
+ * return (
140
+ * <Subscription value={sessionStorage}>
141
+ * {value => <div>{value().count}</div>}
142
+ * </Subscription>
143
+ * )
144
+ * }
145
+ * ```
146
+ *
147
+ * @example With selector
148
+ * ```tsx
149
+ * import { createWebStorage } from 'seitu/web'
150
+ * import { Subscription } from 'seitu/solid'
151
+ * import * as z from 'zod'
152
+ *
153
+ * const sessionStorage = createWebStorage({
154
+ * type: 'sessionStorage',
155
+ * schemas: { count: z.number(), name: z.string() },
156
+ * defaultValues: { count: 0, name: '' },
157
+ * })
158
+ *
159
+ * function Page() {
160
+ * return (
161
+ * <Subscription value={sessionStorage} selector={v => v.count}>
162
+ * {count => <div>{count()}</div>}
163
+ * </Subscription>
164
+ * )
165
+ * }
166
+ * ```
167
+ */
168
+ declare function Subscription<S extends Subscribable<any> & Readable<any>, R = S['~']['output']>(props: SubscriptionProps<S, R>): JSX.Element;
169
+ //#endregion
170
+ export { Subscription, SubscriptionProps, UseSubscriptionOptions, useSubscription };
package/dist/solid.mjs ADDED
@@ -0,0 +1,180 @@
1
+ import { deepEqual } from "fast-equals";
2
+ import { createEffect, createMemo, createSignal, onCleanup } from "solid-js";
3
+ //#region src/solid/hooks.ts
4
+ /**
5
+ * Use this primitive to subscribe to a reactive value. Accepts a subscription object
6
+ * directly, or an accessor/getter that returns one. The getter is reactive — when it
7
+ * reads a signal, the subscription is recreated and re-subscribed automatically.
8
+ *
9
+ * Returns a Solid `Accessor<R>`: call it (`value()`) to read the current value inside JSX
10
+ * or another reactive scope.
11
+ *
12
+ * @example Inline subscription
13
+ * ```tsx
14
+ * import { createWebStorageValue } from 'seitu/web'
15
+ * import { useSubscription } from 'seitu/solid'
16
+ * import * as z from 'zod'
17
+ *
18
+ * function Counter() {
19
+ * const value = useSubscription(() => createWebStorageValue({
20
+ * type: 'sessionStorage',
21
+ * key: 'test',
22
+ * defaultValue: 0,
23
+ * schema: z.number(),
24
+ * }))
25
+ *
26
+ * return <div>{value()}</div>
27
+ * }
28
+ * ```
29
+ *
30
+ * @example Instance outside of component
31
+ * ```tsx
32
+ * import { createWebStorage } from 'seitu/web'
33
+ * import { useSubscription } from 'seitu/solid'
34
+ * import * as z from 'zod'
35
+ *
36
+ * const sessionStorage = createWebStorage({
37
+ * type: 'sessionStorage',
38
+ * schemas: { count: z.number(), name: z.string() },
39
+ * defaultValues: { count: 0, name: '' },
40
+ * })
41
+ *
42
+ * function Counter() {
43
+ * const value = useSubscription(sessionStorage)
44
+ * return <div>{value().count}</div>
45
+ * }
46
+ * ```
47
+ *
48
+ * @example Subscription with selector
49
+ * ```tsx
50
+ * import { createWebStorage } from 'seitu/web'
51
+ * import { useSubscription } from 'seitu/solid'
52
+ * import * as z from 'zod'
53
+ *
54
+ * const sessionStorage = createWebStorage({
55
+ * type: 'sessionStorage',
56
+ * schemas: {
57
+ * count: z.number(),
58
+ * name: z.string(),
59
+ * },
60
+ * defaultValues: { count: 0, name: '' },
61
+ * })
62
+ *
63
+ * function Counter() {
64
+ * // Usage with selector, the accessor only updates when count changes
65
+ * const count = useSubscription(sessionStorage, { selector: value => value.count })
66
+ *
67
+ * return <div>{count()}</div>
68
+ * }
69
+ * ```
70
+ *
71
+ * @example Reactive source (re-subscribes when a signal changes)
72
+ * ```tsx
73
+ * import { createSignal } from 'solid-js'
74
+ * import { createWebStorageValue } from 'seitu/web'
75
+ * import { useSubscription } from 'seitu/solid'
76
+ * import * as z from 'zod'
77
+ *
78
+ * function User() {
79
+ * const [userId, setUserId] = createSignal('user-1')
80
+ * const data = useSubscription(() => createWebStorageValue({
81
+ * type: 'localStorage',
82
+ * key: `user:${userId()}`,
83
+ * schema: z.object({ name: z.string() }),
84
+ * defaultValue: { name: '' },
85
+ * }))
86
+ *
87
+ * return <div>{data().name}</div>
88
+ * }
89
+ * ```
90
+ *
91
+ * @example Ref example
92
+ * ```tsx
93
+ * import { createScrollState } from 'seitu/web'
94
+ * import { useSubscription } from 'seitu/solid'
95
+ *
96
+ * function Page() {
97
+ * let ref: HTMLDivElement | undefined
98
+ * const state = useSubscription(() => createScrollState({ element: () => ref, direction: 'vertical' }))
99
+ *
100
+ * return (
101
+ * <div ref={ref}>
102
+ * {String(state().top.reached)}
103
+ * </div>
104
+ * )
105
+ * }
106
+ * ```
107
+ */
108
+ function useSubscription(source, options) {
109
+ const { selector, isEqual = deepEqual } = options ?? {};
110
+ const getSource = typeof source === "function" ? source : () => source;
111
+ function getSnapshot(sub) {
112
+ return selector ? selector(sub.get()) : sub.get();
113
+ }
114
+ const sub = createMemo(() => getSource());
115
+ const [state, setState] = createSignal(getSnapshot(sub()), { equals: isEqual });
116
+ createEffect(() => {
117
+ const current = sub();
118
+ setState(() => getSnapshot(current));
119
+ onCleanup(current.subscribe(() => {
120
+ setState(() => getSnapshot(current));
121
+ }));
122
+ });
123
+ return state;
124
+ }
125
+ //#endregion
126
+ //#region src/solid/components.ts
127
+ /**
128
+ * Declarative component that subscribes to a reactive value and passes an accessor to a render function.
129
+ * Unlike React, the children function runs once and receives a Solid `Accessor<R>` — call it (`value()`)
130
+ * inside the returned JSX so updates stay fine-grained. Use when you prefer a component API over the
131
+ * useSubscription primitive.
132
+ *
133
+ * @example Basic usage
134
+ * ```tsx
135
+ * import { createWebStorage } from 'seitu/web'
136
+ * import { Subscription } from 'seitu/solid'
137
+ * import * as z from 'zod'
138
+ *
139
+ * const sessionStorage = createWebStorage({
140
+ * type: 'sessionStorage',
141
+ * schemas: { count: z.number(), name: z.string() },
142
+ * defaultValues: { count: 0, name: '' },
143
+ * })
144
+ *
145
+ * function Page() {
146
+ * return (
147
+ * <Subscription value={sessionStorage}>
148
+ * {value => <div>{value().count}</div>}
149
+ * </Subscription>
150
+ * )
151
+ * }
152
+ * ```
153
+ *
154
+ * @example With selector
155
+ * ```tsx
156
+ * import { createWebStorage } from 'seitu/web'
157
+ * import { Subscription } from 'seitu/solid'
158
+ * import * as z from 'zod'
159
+ *
160
+ * const sessionStorage = createWebStorage({
161
+ * type: 'sessionStorage',
162
+ * schemas: { count: z.number(), name: z.string() },
163
+ * defaultValues: { count: 0, name: '' },
164
+ * })
165
+ *
166
+ * function Page() {
167
+ * return (
168
+ * <Subscription value={sessionStorage} selector={v => v.count}>
169
+ * {count => <div>{count()}</div>}
170
+ * </Subscription>
171
+ * )
172
+ * }
173
+ * ```
174
+ */
175
+ function Subscription(props) {
176
+ const value = useSubscription(() => props.value, { selector: props.selector });
177
+ return props.children(value);
178
+ }
179
+ //#endregion
180
+ export { Subscription, useSubscription };
@@ -0,0 +1,77 @@
1
+ import { _ as Readable$1, v as Subscribable } from "./index-DwN8Up-P.mjs";
2
+ import { Readable } from "svelte/store";
3
+
4
+ //#region src/svelte/hooks.d.ts
5
+ interface UseSubscriptionOptions<S extends Subscribable<any> & Readable$1<any>, R = S['~']['output']> {
6
+ selector?: (value: S['~']['output']) => R;
7
+ isEqual?: (prev: R, next: R) => boolean;
8
+ }
9
+ /**
10
+ * Use this function to subscribe to a reactive value from a Svelte component.
11
+ * Accepts a subscription object directly, or a factory function that is called
12
+ * once to create one.
13
+ *
14
+ * Returns a Svelte `Readable` store — read it with the `$` auto-subscription
15
+ * (`$value`) in markup. The underlying subscription is created lazily on the
16
+ * first subscriber and torn down when the last one leaves.
17
+ *
18
+ * @example Inline subscription
19
+ * ```svelte
20
+ * <script lang="ts">
21
+ * import { createWebStorageValue } from 'seitu/web'
22
+ * import { useSubscription } from 'seitu/svelte'
23
+ * import * as z from 'zod'
24
+ *
25
+ * const value = useSubscription(() => createWebStorageValue({
26
+ * type: 'sessionStorage',
27
+ * key: 'test',
28
+ * defaultValue: 0,
29
+ * schema: z.number(),
30
+ * }))
31
+ * </script>
32
+ *
33
+ * <div>{$value}</div>
34
+ * ```
35
+ *
36
+ * @example Instance outside of component
37
+ * ```svelte
38
+ * <script lang="ts">
39
+ * import { createWebStorage } from 'seitu/web'
40
+ * import { useSubscription } from 'seitu/svelte'
41
+ * import * as z from 'zod'
42
+ *
43
+ * const sessionStorage = createWebStorage({
44
+ * type: 'sessionStorage',
45
+ * schemas: { count: z.number(), name: z.string() },
46
+ * defaultValues: { count: 0, name: '' },
47
+ * })
48
+ *
49
+ * const value = useSubscription(sessionStorage)
50
+ * </script>
51
+ *
52
+ * <div>{$value.count}</div>
53
+ * ```
54
+ *
55
+ * @example With selector
56
+ * ```svelte
57
+ * <script lang="ts">
58
+ * import { createWebStorage } from 'seitu/web'
59
+ * import { useSubscription } from 'seitu/svelte'
60
+ * import * as z from 'zod'
61
+ *
62
+ * const sessionStorage = createWebStorage({
63
+ * type: 'sessionStorage',
64
+ * schemas: { count: z.number(), name: z.string() },
65
+ * defaultValues: { count: 0, name: '' },
66
+ * })
67
+ *
68
+ * // Updates only when count changes
69
+ * const count = useSubscription(sessionStorage, { selector: v => v.count })
70
+ * </script>
71
+ *
72
+ * <div>{$count}</div>
73
+ * ```
74
+ */
75
+ declare function useSubscription<S extends Subscribable<any> & Readable$1<any>, R = S['~']['output']>(source: S | (() => S), options?: UseSubscriptionOptions<S, R>): Readable<R>;
76
+ //#endregion
77
+ export { UseSubscriptionOptions, useSubscription };
@@ -0,0 +1,91 @@
1
+ import { deepEqual } from "fast-equals";
2
+ import { readable } from "svelte/store";
3
+ //#region src/svelte/hooks.ts
4
+ /**
5
+ * Use this function to subscribe to a reactive value from a Svelte component.
6
+ * Accepts a subscription object directly, or a factory function that is called
7
+ * once to create one.
8
+ *
9
+ * Returns a Svelte `Readable` store — read it with the `$` auto-subscription
10
+ * (`$value`) in markup. The underlying subscription is created lazily on the
11
+ * first subscriber and torn down when the last one leaves.
12
+ *
13
+ * @example Inline subscription
14
+ * ```svelte
15
+ * <script lang="ts">
16
+ * import { createWebStorageValue } from 'seitu/web'
17
+ * import { useSubscription } from 'seitu/svelte'
18
+ * import * as z from 'zod'
19
+ *
20
+ * const value = useSubscription(() => createWebStorageValue({
21
+ * type: 'sessionStorage',
22
+ * key: 'test',
23
+ * defaultValue: 0,
24
+ * schema: z.number(),
25
+ * }))
26
+ * <\/script>
27
+ *
28
+ * <div>{$value}</div>
29
+ * ```
30
+ *
31
+ * @example Instance outside of component
32
+ * ```svelte
33
+ * <script lang="ts">
34
+ * import { createWebStorage } from 'seitu/web'
35
+ * import { useSubscription } from 'seitu/svelte'
36
+ * import * as z from 'zod'
37
+ *
38
+ * const sessionStorage = createWebStorage({
39
+ * type: 'sessionStorage',
40
+ * schemas: { count: z.number(), name: z.string() },
41
+ * defaultValues: { count: 0, name: '' },
42
+ * })
43
+ *
44
+ * const value = useSubscription(sessionStorage)
45
+ * <\/script>
46
+ *
47
+ * <div>{$value.count}</div>
48
+ * ```
49
+ *
50
+ * @example With selector
51
+ * ```svelte
52
+ * <script lang="ts">
53
+ * import { createWebStorage } from 'seitu/web'
54
+ * import { useSubscription } from 'seitu/svelte'
55
+ * import * as z from 'zod'
56
+ *
57
+ * const sessionStorage = createWebStorage({
58
+ * type: 'sessionStorage',
59
+ * schemas: { count: z.number(), name: z.string() },
60
+ * defaultValues: { count: 0, name: '' },
61
+ * })
62
+ *
63
+ * // Updates only when count changes
64
+ * const count = useSubscription(sessionStorage, { selector: v => v.count })
65
+ * <\/script>
66
+ *
67
+ * <div>{$count}</div>
68
+ * ```
69
+ */
70
+ function useSubscription(source, options) {
71
+ const { selector, isEqual = deepEqual } = options ?? {};
72
+ const sub = typeof source === "function" ? source() : source;
73
+ function getSnapshot() {
74
+ return selector ? selector(sub.get()) : sub.get();
75
+ }
76
+ const initial = getSnapshot();
77
+ return readable(initial, (set) => {
78
+ let current = initial;
79
+ function refresh() {
80
+ const next = getSnapshot();
81
+ if (!isEqual(current, next)) {
82
+ current = next;
83
+ set(next);
84
+ }
85
+ }
86
+ refresh();
87
+ return sub.subscribe(refresh);
88
+ });
89
+ }
90
+ //#endregion
91
+ export { useSubscription };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "seitu",
3
3
  "displayName": "Seitu",
4
4
  "type": "module",
5
- "version": "0.15.1",
5
+ "version": "0.16.0",
6
6
  "private": false,
7
7
  "author": "Valerii Strilets",
8
8
  "license": "MIT",
@@ -20,6 +20,9 @@
20
20
  "typescript",
21
21
  "react",
22
22
  "vue",
23
+ "solid",
24
+ "solid-js",
25
+ "svelte",
23
26
  "utils",
24
27
  "type-safe",
25
28
  "tanstack-intent"
@@ -45,6 +48,14 @@
45
48
  "types": "./dist/vue/index.d.mts",
46
49
  "import": "./dist/vue.mjs"
47
50
  },
51
+ "./solid": {
52
+ "types": "./dist/solid/index.d.mts",
53
+ "import": "./dist/solid.mjs"
54
+ },
55
+ "./svelte": {
56
+ "types": "./dist/svelte/index.d.mts",
57
+ "import": "./dist/svelte.mjs"
58
+ },
48
59
  "./utils": {
49
60
  "types": "./dist/utils/index.d.mts",
50
61
  "import": "./dist/utils.mjs"
@@ -62,6 +73,8 @@
62
73
  "peerDependencies": {
63
74
  "react": ">=19",
64
75
  "react-dom": ">=19",
76
+ "solid-js": ">=1.9",
77
+ "svelte": ">=5",
65
78
  "vue": ">=3.5"
66
79
  },
67
80
  "peerDependenciesMeta": {
@@ -71,6 +84,12 @@
71
84
  "react-dom": {
72
85
  "optional": true
73
86
  },
87
+ "solid-js": {
88
+ "optional": true
89
+ },
90
+ "svelte": {
91
+ "optional": true
92
+ },
74
93
  "vue": {
75
94
  "optional": true
76
95
  }
@@ -79,21 +98,26 @@
79
98
  "fast-equals": "^6.0.0"
80
99
  },
81
100
  "devDependencies": {
101
+ "@solidjs/testing-library": "^0.8.10",
82
102
  "@standard-schema/spec": "^1.1.0",
83
103
  "@tanstack/intent": "^0.0.42",
84
104
  "@testing-library/jest-dom": "^6.9.1",
85
105
  "@testing-library/react": "^16.3.2",
86
106
  "@types/react": "^19.2.17",
107
+ "@vitejs/plugin-react": "^6.0.2",
87
108
  "fake-indexeddb": "^6.2.5",
88
109
  "happy-dom": "^20.10.2",
89
110
  "react-dom": "^19.2.7",
111
+ "solid-js": "^1.9.13",
112
+ "svelte": "^5.56.3",
90
113
  "tsdown": "^0.22.2",
91
114
  "type-fest": "^5.7.0",
92
115
  "typescript": "^6.0.3",
93
- "vite": "^6.4.3",
94
- "vitest": "^3.2.6",
95
- "vue": "^3.5.35",
96
- "yaml": "^2.8.3",
116
+ "vite": "^8.0.16",
117
+ "vite-plugin-solid": "^2.11.12",
118
+ "vitest": "^4.1.8",
119
+ "vue": "^3.5.38",
120
+ "yaml": "^2.9.0",
97
121
  "zod": "^4.4.3"
98
122
  },
99
123
  "scripts": {
package/skills/README.md CHANGED
@@ -78,6 +78,19 @@ Restart Cursor or start a new agent chat so skills are picked up.
78
78
  |-------|-----------|-------------|
79
79
  | [use-subscription-vue](./use-subscription-vue/SKILL.md) | `seitu#use-subscription-vue` | Composable for any Seitu primitive |
80
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
+
81
94
  ## Registry and version history
82
95
 
83
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.
@@ -4,7 +4,7 @@ description: >-
4
4
  Derived read-only subscription from one or many sources.
5
5
  type: core
6
6
  library: seitu
7
- library_version: "0.15.1"
7
+ library_version: "0.16.0"
8
8
  requires:
9
9
  - seitu-overview
10
10
  sources:
@@ -4,7 +4,7 @@ description: >-
4
4
  Debounce updates from a source subscribable.
5
5
  type: core
6
6
  library: seitu
7
- library_version: "0.15.1"
7
+ library_version: "0.16.0"
8
8
  requires:
9
9
  - seitu-overview
10
10
  sources:
@@ -4,7 +4,7 @@ description: >-
4
4
  Debounced callable with reactive return value.
5
5
  type: core
6
6
  library: seitu
7
- library_version: "0.15.1"
7
+ library_version: "0.16.0"
8
8
  requires:
9
9
  - seitu-overview
10
10
  sources:
@@ -4,7 +4,7 @@ description: >-
4
4
  Async IndexedDB persistence with cached sync reads.
5
5
  type: core
6
6
  library: seitu
7
- library_version: "0.15.1"
7
+ library_version: "0.16.0"
8
8
  requires:
9
9
  - seitu-overview
10
10
  sources:
@@ -4,7 +4,7 @@ description: >-
4
4
  Reactive navigator.onLine status.
5
5
  type: core
6
6
  library: seitu
7
- library_version: "0.15.1"
7
+ library_version: "0.16.0"
8
8
  requires:
9
9
  - seitu-overview
10
10
  sources:
@@ -4,7 +4,7 @@ description: >-
4
4
  Reactive CSS media query with SSR defaultMatches.
5
5
  type: core
6
6
  library: seitu
7
- library_version: "0.15.1"
7
+ library_version: "0.16.0"
8
8
  requires:
9
9
  - seitu-overview
10
10
  sources:
@@ -4,7 +4,7 @@ description: >-
4
4
  Compose get + subscribe + notify into standard Readable & Subscribable.
5
5
  type: core
6
6
  library: seitu
7
- library_version: "0.15.1"
7
+ library_version: "0.16.0"
8
8
  requires:
9
9
  - seitu-overview
10
10
  - create-subscription
@@ -4,7 +4,7 @@ description: >-
4
4
  Standard Schema validated store with default fallback on invalid data.
5
5
  type: core
6
6
  library: seitu
7
- library_version: "0.15.1"
7
+ library_version: "0.16.0"
8
8
  requires:
9
9
  - seitu-overview
10
10
  sources:
@@ -4,7 +4,7 @@ description: >-
4
4
  Scroll position, edges, and remaining distance for an element.
5
5
  type: core
6
6
  library: seitu
7
- library_version: "0.15.1"
7
+ library_version: "0.16.0"
8
8
  requires:
9
9
  - seitu-overview
10
10
  sources:
@@ -4,7 +4,7 @@ description: >-
4
4
  Minimal in-memory reactive store with get, set, and subscribe.
5
5
  type: core
6
6
  library: seitu
7
- library_version: "0.15.1"
7
+ library_version: "0.16.0"
8
8
  requires:
9
9
  - seitu-overview
10
10
  sources:
@@ -4,7 +4,7 @@ description: >-
4
4
  Low-level subscribe/notify for custom primitives.
5
5
  type: core
6
6
  library: seitu
7
- library_version: "0.15.1"
7
+ library_version: "0.16.0"
8
8
  requires:
9
9
  - seitu-overview
10
10
  sources:
@@ -4,7 +4,7 @@ description: >-
4
4
  Throttle updates from a source subscribable.
5
5
  type: core
6
6
  library: seitu
7
- library_version: "0.15.1"
7
+ library_version: "0.16.0"
8
8
  requires:
9
9
  - seitu-overview
10
10
  sources:
@@ -4,7 +4,7 @@ description: >-
4
4
  Throttled callable with reactive return value.
5
5
  type: core
6
6
  library: seitu
7
- library_version: "0.15.1"
7
+ library_version: "0.16.0"
8
8
  requires:
9
9
  - seitu-overview
10
10
  sources:
@@ -4,7 +4,7 @@ description: >-
4
4
  Multi-key localStorage/sessionStorage with per-key schemas.
5
5
  type: core
6
6
  library: seitu
7
- library_version: "0.15.1"
7
+ library_version: "0.16.0"
8
8
  requires:
9
9
  - seitu-overview
10
10
  - create-web-storage-value
@@ -4,7 +4,7 @@ description: >-
4
4
  Single-key localStorage/sessionStorage with schema validation.
5
5
  type: core
6
6
  library: seitu
7
- library_version: "0.15.1"
7
+ library_version: "0.16.0"
8
8
  requires:
9
9
  - seitu-overview
10
10
  sources:
@@ -4,7 +4,7 @@ description: >-
4
4
  Module map, mental model, decision tree, SSR — read before other Seitu skills.
5
5
  type: lifecycle
6
6
  library: seitu
7
- library_version: "0.15.1"
7
+ library_version: "0.16.0"
8
8
  sources:
9
9
  - letstri/seitu:docs/content/docs/index.mdx
10
10
  - letstri/seitu:seitu/src/core/index.ts
@@ -13,7 +13,7 @@ sources:
13
13
  # Seitu Overview
14
14
 
15
15
  Seitu is a type-safe reactive primitives library. Framework-agnostic core with
16
- React and Vue bindings. Every primitive shares the same `get()` / `subscribe()`
16
+ React, Vue, Solid, and Svelte bindings. Every primitive shares the same `get()` / `subscribe()`
17
17
  API — no actions, no reducers, no context providers.
18
18
 
19
19
  ## Module map
@@ -24,9 +24,11 @@ API — no actions, no reducers, no context providers.
24
24
  | `seitu/web` | Browser persistence (localStorage, sessionStorage, IndexedDB) and DOM state |
25
25
  | `seitu/react` | `useSubscription` hook + `Subscription` component |
26
26
  | `seitu/vue` | `useSubscription` composable |
27
+ | `seitu/solid` | `useSubscription` primitive (returns `Accessor`) + `Subscription` component |
28
+ | `seitu/svelte` | `useSubscription` (returns a Svelte `Readable` store) |
27
29
  | `seitu/utils` | Helpers (`repairValueObjectWithDefault`) |
28
30
 
29
- ESM-only. Node >= 22. React >= 19 and Vue >= 3.5 are optional peer deps.
31
+ ESM-only. Node >= 22. React >= 19, Vue >= 3.5, Solid >= 1.9, and Svelte >= 5 are optional peer deps.
30
32
 
31
33
  ## Mental model
32
34
 
@@ -101,7 +103,9 @@ What do you need?
101
103
 
102
104
  └─ Framework integration
103
105
  ├─ React → useSubscription (hook) or Subscription (component)
104
- └─ Vue → useSubscription (composable)
106
+ ├─ Vue → useSubscription (composable)
107
+ ├─ Solid → useSubscription (returns Accessor) or Subscription (component)
108
+ └─ Svelte → useSubscription (returns a Readable store, read with $value)
105
109
  ```
106
110
 
107
111
  ## Framework binding
@@ -122,6 +126,21 @@ const value = useSubscription(store)
122
126
  const data = useSubscription(computed(() => createWebStorageValue({ ... })))
123
127
  ```
124
128
 
129
+ ```tsx
130
+ // Solid — instance or reactive getter; returns an Accessor, read with value()
131
+ const value = useSubscription(store)
132
+ const data = useSubscription(() => createWebStorageValue({ ... }))
133
+ ```
134
+
135
+ ```svelte
136
+ <!-- Svelte — instance or factory; returns a Readable store, read with $value -->
137
+ <script lang="ts">
138
+ const value = useSubscription(store)
139
+ const data = useSubscription(() => createWebStorageValue({ ... }))
140
+ </script>
141
+ <div>{$value}</div>
142
+ ```
143
+
125
144
  ## SSR
126
145
 
127
146
  All `seitu/web` primitives return defaults when `window` / `navigator` is
@@ -4,7 +4,7 @@ description: >-
4
4
  Render-prop Subscription component for React.
5
5
  type: framework
6
6
  library: seitu
7
- library_version: "0.15.1"
7
+ library_version: "0.16.0"
8
8
  requires:
9
9
  - seitu-overview
10
10
  - use-subscription-react
@@ -0,0 +1,97 @@
1
+ ---
2
+ name: subscription-solid
3
+ description: >-
4
+ Render-prop Subscription component for Solid.
5
+ type: framework
6
+ library: seitu
7
+ library_version: "0.16.0"
8
+ requires:
9
+ - seitu-overview
10
+ - use-subscription-solid
11
+ sources:
12
+ - letstri/seitu:docs/content/docs/solid/components.mdx
13
+ - letstri/seitu:seitu/src/solid/components.ts
14
+ ---
15
+
16
+ # Subscription (Solid component)
17
+
18
+ Declarative render-prop component from `seitu/solid`. The children function runs once and
19
+ receives a Solid `Accessor<R>` — read it with `value()` so updates stay fine-grained.
20
+
21
+ ```tsx
22
+ import { Subscription } from 'seitu/solid'
23
+
24
+ <Subscription value={storage}>
25
+ {value => <div>{value().count}</div>}
26
+ </Subscription>
27
+
28
+ <Subscription value={storage} selector={v => v.count}>
29
+ {count => <div>{count()}</div>}
30
+ </Subscription>
31
+ ```
32
+
33
+ ## Props
34
+
35
+ | Prop | Type | Description |
36
+ |------|------|-------------|
37
+ | `value` | `Subscribable & Readable` | The reactive source |
38
+ | `selector?` | `(value) => R` | Optional selector for granular updates |
39
+ | `children` | `(value: Accessor<R>) => JSX.Element` | Render function receiving an accessor |
40
+
41
+ ## Common Mistakes
42
+
43
+ ### [CRITICAL] Treating children's argument as a value, not an accessor
44
+
45
+ Wrong:
46
+
47
+ ```tsx
48
+ <Subscription value={store}>
49
+ {value => <div>{value.count}</div>}
50
+ </Subscription>
51
+ ```
52
+
53
+ Correct:
54
+
55
+ ```tsx
56
+ <Subscription value={store}>
57
+ {value => <div>{value().count}</div>}
58
+ </Subscription>
59
+ ```
60
+
61
+ Unlike React, the children function runs once and receives a Solid `Accessor`; call it (`value()`) inside JSX.
62
+
63
+ ### [LOW] Preferring the component over the primitive without reason
64
+
65
+ Wrong:
66
+
67
+ ```tsx
68
+ <Subscription value={s}>{v => <Child value={v()} />}</Subscription>
69
+ ```
70
+
71
+ Correct:
72
+
73
+ ```tsx
74
+ const v = useSubscription(s); return <Child value={v()} />
75
+ ```
76
+
77
+ `useSubscription` is simpler for most cases; `Subscription` is for render-prop composition.
78
+
79
+ ### [CRITICAL] Importing from seitu/react in Solid
80
+
81
+ Wrong:
82
+
83
+ ```ts
84
+ import { Subscription } from 'seitu/react'
85
+ ```
86
+
87
+ Correct:
88
+
89
+ ```ts
90
+ import { Subscription } from 'seitu/solid'
91
+ ```
92
+
93
+ The React component relies on React internals; Solid apps must use `seitu/solid`.
94
+
95
+ ## Source
96
+
97
+ `src/solid/components.ts`
@@ -4,7 +4,7 @@ description: >-
4
4
  React hook for any Seitu primitive via useSyncExternalStore.
5
5
  type: framework
6
6
  library: seitu
7
- library_version: "0.15.1"
7
+ library_version: "0.16.0"
8
8
  requires:
9
9
  - seitu-overview
10
10
  sources:
@@ -0,0 +1,144 @@
1
+ ---
2
+ name: use-subscription-solid
3
+ description: >-
4
+ Solid primitive returning an Accessor for any Seitu primitive.
5
+ type: framework
6
+ library: seitu
7
+ library_version: "0.16.0"
8
+ requires:
9
+ - seitu-overview
10
+ sources:
11
+ - letstri/seitu:docs/content/docs/solid/hooks.mdx
12
+ - letstri/seitu:seitu/src/solid/hooks.ts
13
+ ---
14
+
15
+ # useSubscription (Solid)
16
+
17
+ Core Solid primitive from `seitu/solid`. Returns a Solid `Accessor<T>` (`() => T`) that stays in sync with the source. Works with any `Subscribable<T> & Readable<T>`. Accepts a raw instance or a reactive getter.
18
+
19
+ ## Basic usage (module-level instance)
20
+
21
+ ```tsx
22
+ import { useSubscription } from 'seitu/solid'
23
+ import { createStore } from 'seitu'
24
+
25
+ const count = createStore(0)
26
+
27
+ function Counter() {
28
+ const value = useSubscription(count)
29
+ return <button onClick={() => count.set(v => v + 1)}>{value()}</button>
30
+ }
31
+ ```
32
+
33
+ ## Inline subscription (getter form)
34
+
35
+ ```tsx
36
+ import { useSubscription } from 'seitu/solid'
37
+ import { createScrollState } from 'seitu/web'
38
+
39
+ function ScrollTracker() {
40
+ let ref: HTMLDivElement | undefined
41
+ const state = useSubscription(() =>
42
+ createScrollState({ element: () => ref, direction: 'vertical' })
43
+ )
44
+ return <div ref={ref}>{state().top.reached ? 'at top' : 'scrolled'}</div>
45
+ }
46
+ ```
47
+
48
+ ## Reactive source (re-subscribes when a signal changes)
49
+
50
+ ```tsx
51
+ import { createSignal } from 'solid-js'
52
+ import { useSubscription } from 'seitu/solid'
53
+ import { createWebStorageValue } from 'seitu/web'
54
+
55
+ function UserStorage(props: { userId: string }) {
56
+ const data = useSubscription(() =>
57
+ createWebStorageValue({ type: 'localStorage', key: `user:${props.userId}`, schema, defaultValue })
58
+ )
59
+ return <div>{data().name}</div>
60
+ }
61
+ ```
62
+
63
+ ## With selector (granular updates)
64
+
65
+ ```tsx
66
+ const count = useSubscription(storage, { selector: v => v.count })
67
+ // count() only changes when the selected value changes
68
+ ```
69
+
70
+ ## Options
71
+
72
+ | Option | Type | Description |
73
+ |--------|------|-------------|
74
+ | `selector?` | `(value) => R` | Derive subset; the accessor updates only when it changes |
75
+ | `isEqual?` | `(prev, next) => boolean` | Custom equality (default: `deepEqual`) |
76
+
77
+ ## Returns
78
+
79
+ `Accessor<R>` — a getter function. Read it with `value()` inside JSX or any tracked scope.
80
+
81
+ ## Common Mistakes
82
+
83
+ ### [CRITICAL] Reading the accessor without calling it
84
+
85
+ Wrong:
86
+
87
+ ```tsx
88
+ const value = useSubscription(store)
89
+ return <div>{value}</div>
90
+ ```
91
+
92
+ Correct:
93
+
94
+ ```tsx
95
+ const value = useSubscription(store)
96
+ return <div>{value()}</div>
97
+ ```
98
+
99
+ `useSubscription` returns a Solid `Accessor`; you must call it (`value()`) to read and track the value.
100
+
101
+ ### [HIGH] Passing reactive props directly instead of a getter
102
+
103
+ Wrong:
104
+
105
+ ```tsx
106
+ function Item(props: { store: Store<number> }) {
107
+ const value = useSubscription(props.store)
108
+ }
109
+ ```
110
+
111
+ Correct:
112
+
113
+ ```tsx
114
+ function Item(props: { store: Store<number> }) {
115
+ const value = useSubscription(() => props.store)
116
+ }
117
+ ```
118
+
119
+ Reading `props.store` outside a tracked scope loses reactivity; the getter form re-subscribes when the prop changes.
120
+
121
+ ### [CRITICAL] Importing from seitu/react in Solid
122
+
123
+ Wrong:
124
+
125
+ ```ts
126
+ import { useSubscription } from 'seitu/react'
127
+ ```
128
+
129
+ Correct:
130
+
131
+ ```ts
132
+ import { useSubscription } from 'seitu/solid'
133
+ ```
134
+
135
+ Solid apps must use the `seitu/solid` primitive — the React hook relies on `useSyncExternalStore`.
136
+
137
+ ## See also
138
+
139
+ - [`subscription-solid`](../subscription-solid/SKILL.md) — Render-prop component alternative.
140
+ - [`create-scroll-state`](../create-scroll-state/SKILL.md) — Scroll tracking uses the getter + ref pattern in Solid.
141
+
142
+ ## Source
143
+
144
+ `src/solid/hooks.ts`
@@ -0,0 +1,134 @@
1
+ ---
2
+ name: use-subscription-svelte
3
+ description: >-
4
+ Svelte binding returning a Readable store for any Seitu primitive.
5
+ type: framework
6
+ library: seitu
7
+ library_version: "0.16.0"
8
+ requires:
9
+ - seitu-overview
10
+ sources:
11
+ - letstri/seitu:docs/content/docs/svelte/hooks.mdx
12
+ - letstri/seitu:seitu/src/svelte/hooks.ts
13
+ ---
14
+
15
+ # useSubscription (Svelte)
16
+
17
+ Binding from `seitu/svelte`. Returns a Svelte [`Readable`](https://svelte.dev/docs/svelte-store#readable)
18
+ store that stays in sync with any `Subscribable<T> & Readable<T>`. Read it with the `$`
19
+ auto-subscription (`$value`) in markup. The source subscription is created lazily on the
20
+ first subscriber and torn down when the last one leaves (SSR-safe, auto-cleanup).
21
+
22
+ ## Basic usage (module-level instance)
23
+
24
+ ```svelte
25
+ <script lang="ts">
26
+ import { useSubscription } from 'seitu/svelte'
27
+ import { createStore } from 'seitu'
28
+
29
+ const count = createStore(0)
30
+ const value = useSubscription(count)
31
+ </script>
32
+
33
+ <button onclick={() => count.set(v => v + 1)}>{$value}</button>
34
+ ```
35
+
36
+ ## Inline subscription (factory form)
37
+
38
+ ```svelte
39
+ <script lang="ts">
40
+ import { useSubscription } from 'seitu/svelte'
41
+ import { createWebStorageValue } from 'seitu/web'
42
+ import * as z from 'zod'
43
+
44
+ const value = useSubscription(() => createWebStorageValue({
45
+ type: 'sessionStorage',
46
+ key: 'test',
47
+ defaultValue: 0,
48
+ schema: z.number(),
49
+ }))
50
+ </script>
51
+
52
+ <div>{$value}</div>
53
+ ```
54
+
55
+ ## With selector (granular updates)
56
+
57
+ ```svelte
58
+ <script lang="ts">
59
+ const count = useSubscription(storage, { selector: v => v.count })
60
+ </script>
61
+
62
+ <div>{$count}</div>
63
+ ```
64
+
65
+ ## Options
66
+
67
+ | Option | Type | Description |
68
+ |--------|------|-------------|
69
+ | `selector?` | `(value) => R` | Derive a subset; the store updates only when it changes |
70
+ | `isEqual?` | `(prev, next) => boolean` | Custom equality (default: `deepEqual`) |
71
+
72
+ ## Returns
73
+
74
+ `Readable<R>` (from `svelte/store`) — read it as `$value` in markup, or with `get(value)` in scripts.
75
+
76
+ ## Common Mistakes
77
+
78
+ ### [CRITICAL] Reading the store without the `$` prefix
79
+
80
+ Wrong:
81
+
82
+ ```svelte
83
+ <div>{value}</div>
84
+ ```
85
+
86
+ Correct:
87
+
88
+ ```svelte
89
+ <div>{$value}</div>
90
+ ```
91
+
92
+ `useSubscription` returns a Svelte store; the `$` prefix auto-subscribes and reads the current value.
93
+
94
+ ### [MEDIUM] Passing a factory expecting reactivity to a changing source
95
+
96
+ Wrong:
97
+
98
+ ```svelte
99
+ <script lang="ts">
100
+ // The factory runs once; it does not re-run when `id` changes.
101
+ const value = useSubscription(() => createWebStorageValue({ key: `user:${id}`, ... }))
102
+ </script>
103
+ ```
104
+
105
+ Correct:
106
+
107
+ ```svelte
108
+ <script lang="ts">
109
+ // Recreate via a keyed block or derive the source explicitly when it must change.
110
+ const value = useSubscription(createWebStorageValue({ key: `user:${id}`, ... }))
111
+ </script>
112
+ ```
113
+
114
+ The factory is a one-time initializer; for a source that changes over time, recreate the binding (e.g. in a `{#key}` block).
115
+
116
+ ### [CRITICAL] Importing from seitu/react in Svelte
117
+
118
+ Wrong:
119
+
120
+ ```ts
121
+ import { useSubscription } from 'seitu/react'
122
+ ```
123
+
124
+ Correct:
125
+
126
+ ```ts
127
+ import { useSubscription } from 'seitu/svelte'
128
+ ```
129
+
130
+ The React hook relies on `useSyncExternalStore`; Svelte apps must use the `seitu/svelte` binding.
131
+
132
+ ## Source
133
+
134
+ `src/svelte/hooks.ts`
@@ -4,7 +4,7 @@ description: >-
4
4
  Vue composable returning readonly ShallowRef.
5
5
  type: framework
6
6
  library: seitu
7
- library_version: "0.15.1"
7
+ library_version: "0.16.0"
8
8
  requires:
9
9
  - seitu-overview
10
10
  sources: