seitu 0.4.7 → 0.5.1

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.
@@ -8,8 +8,13 @@ export interface Store<T> extends Readable<T>, Writable<T, T>, Subscribable<T> {
8
8
  * - **With schema-store**: use as the state backing for a memory provider.
9
9
  *
10
10
  * @example
11
+ * ```ts twoslash
12
+ * import { createStore } from 'seitu'
13
+ *
11
14
  * const store = createStore({ count: 0 })
12
15
  * store.set(prev => ({ ...prev, count: prev.count + 1 }))
13
16
  * store.subscribe(state => console.log(state))
17
+ * store.get() // { count: 0 }
18
+ * ```
14
19
  */
15
20
  export declare function createStore<T>(initial: T): Store<T>;
@@ -1,3 +1,4 @@
1
+ export * from './is-online';
1
2
  export * from './local-storage';
2
3
  export * from './local-storage-value';
3
4
  export * from './media-query';
@@ -0,0 +1,33 @@
1
+ import type { Destroyable, Readable, Subscribable } from '../core/index';
2
+ export interface IsOnline extends Subscribable<boolean>, Readable<boolean>, Destroyable {
3
+ }
4
+ /**
5
+ * Creates a reactive handle for browser online status.
6
+ *
7
+ * @example Vanilla
8
+ * ```ts twoslash
9
+ * import { createIsOnline } from 'seitu/web'
10
+ *
11
+ * const isOnline = createIsOnline()
12
+ *
13
+ * isOnline.subscribe(value => {
14
+ * console.log(value ? 'online' : 'offline')
15
+ * })
16
+ *
17
+ * console.log(isOnline.get())
18
+ * ```
19
+ *
20
+ * @example React
21
+ * ```tsx twoslash title="page.tsx"
22
+ * import { createIsOnline } from 'seitu/web'
23
+ * import { useSubscription } from 'seitu/react'
24
+ *
25
+ * const isOnline = createIsOnline()
26
+ *
27
+ * function Status() {
28
+ * const online = useSubscription(isOnline)
29
+ * return online ? 'Connected' : 'Disconnected'
30
+ * }
31
+ * ```
32
+ */
33
+ export declare function createIsOnline(): IsOnline;
@@ -0,0 +1 @@
1
+ export {};
@@ -6,11 +6,21 @@ export interface WebStorageValue<V> extends Subscribable<V>, Readable<V>, Writab
6
6
  export interface WebStorageValueOptionsWithStorage<Storage extends WebStorage<any>, K extends keyof Storage['~']['output']> {
7
7
  storage: Storage;
8
8
  key: K;
9
+ /**
10
+ * If true, the value will be cleared if the validation fails.
11
+ *
12
+ * @default true
13
+ */
14
+ clearOnValidationFailure?: boolean;
9
15
  }
10
16
  export interface WebStorageValueOptionsWithSchema<S extends StandardSchemaV1<unknown>> {
11
17
  schema: S;
12
18
  key: string;
13
19
  defaultValue: StandardSchemaV1.InferOutput<S>;
20
+ /**
21
+ * If true, the value will be cleared if the validation fails.
22
+ */
23
+ clearOnValidationFailure?: boolean;
14
24
  }
15
25
  export declare function createWebStorageValue<Storage extends WebStorage<any>, K extends keyof Storage['~']['output']>(options: WebStorageValueOptionsWithStorage<Storage, K>): WebStorageValue<Storage['~']['output'][K]>;
16
26
  export declare function createWebStorageValue<S extends StandardSchemaV1<unknown>>(options: WebStorageValueOptionsWithSchema<S> & {
@@ -1,6 +1,12 @@
1
1
  import type { SchemaStore, SchemaStoreOptions, SchemaStoreOutput, SchemaStoreSchema } from '../core/index';
2
2
  export interface WebStorageOptions<S extends SchemaStoreSchema> extends Omit<SchemaStoreOptions<S>, 'provider'> {
3
3
  keyTransform?: (key: keyof S) => string;
4
+ /**
5
+ * If true, the stored value will be cleared if the validation fails.
6
+ *
7
+ * @default true
8
+ */
9
+ clearOnValidationFailure?: boolean;
4
10
  }
5
11
  export interface WebStorage<O extends Record<string, unknown>> extends SchemaStore<O> {
6
12
  '~': {
package/dist/web.js CHANGED
@@ -1,7 +1,23 @@
1
1
  import { i as e, n as t, o as n } from "./core-3fd1NXIt.js";
2
+ //#region src/web/is-online.ts
3
+ function r() {
4
+ let { subscribe: e, notify: t } = n(), r = () => typeof navigator > "u" ? !0 : navigator.onLine, i = () => t();
5
+ return typeof window < "u" && (window.addEventListener("online", i), window.addEventListener("offline", i)), {
6
+ get: r,
7
+ subscribe: (t) => e(() => t(r())),
8
+ destroy: () => {
9
+ typeof window < "u" && (window.removeEventListener("online", i), window.removeEventListener("offline", i));
10
+ },
11
+ "~": {
12
+ output: null,
13
+ notify: t
14
+ }
15
+ };
16
+ }
17
+ //#endregion
2
18
  //#region src/web/web-storage.ts
3
- function r(n) {
4
- let { kind: r, keyTransform: i, defaultValues: a, schemas: o } = n, s = !1, c = t({
19
+ function i(n) {
20
+ let { kind: r, keyTransform: i, defaultValues: a, schemas: o, clearOnValidationFailure: s } = n, c = !1, l = t({
5
21
  defaultValues: a,
6
22
  schemas: o,
7
23
  provider: {
@@ -9,68 +25,68 @@ function r(n) {
9
25
  if (typeof window > "u") return a;
10
26
  let t = window[r], n = { ...a };
11
27
  for (let r in n) {
12
- let s = t.getItem(i ? i(r) : r);
13
- if (s === null) {
28
+ let c = t.getItem(i ? i(r) : r);
29
+ if (c === null) {
14
30
  n[r] = a[r];
15
31
  continue;
16
32
  }
17
- let c = e(s), l = o[r]["~standard"].validate(c);
18
- if (l instanceof Promise) throw TypeError("[createWebSchemaStore] Validation schema should not return a Promise.");
19
- l.issues && console.warn(JSON.stringify(l.issues, null, 2), { cause: l.issues }), n[r] = l.issues ? a[r] : l.value;
33
+ let l = e(c), u = o[r]["~standard"].validate(l);
34
+ if (u instanceof Promise) throw TypeError("[createWebSchemaStore] Validation schema should not return a Promise.");
35
+ u.issues && (s ? t.removeItem(i ? i(r) : r) : console.warn(JSON.stringify(u.issues, null, 2), { cause: u.issues })), n[r] = u.issues ? a[r] : u.value;
20
36
  }
21
37
  return n;
22
38
  },
23
39
  set: (e) => {
24
40
  if (typeof window > "u") return;
25
41
  let t = window[r];
26
- s = !0, Object.entries(e).forEach(([e, n]) => {
42
+ c = !0, Object.entries(e).forEach(([e, n]) => {
27
43
  let r = typeof n == "string" ? n : JSON.stringify(n);
28
44
  t.setItem(i ? i(e) : e, r);
29
- }), window.dispatchEvent(new Event("storage")), s = !1;
45
+ }), window.dispatchEvent(new Event("storage")), c = !1;
30
46
  }
31
47
  }
32
- }), l = () => {
33
- s || c["~"].notify();
48
+ }), u = () => {
49
+ c || l["~"].notify();
34
50
  };
35
- return typeof window < "u" && window.addEventListener("storage", l), {
36
- ...c,
51
+ return typeof window < "u" && window.addEventListener("storage", u), {
52
+ ...l,
37
53
  destroy: () => {
38
- c.destroy?.(), typeof window < "u" && window.removeEventListener("storage", l);
54
+ l.destroy?.(), typeof window < "u" && window.removeEventListener("storage", u);
39
55
  },
40
56
  "~": {
41
57
  kind: r,
42
- ...c["~"]
58
+ ...l["~"]
43
59
  }
44
60
  };
45
61
  }
46
62
  //#endregion
47
63
  //#region src/web/local-storage.ts
48
- function i(e) {
49
- return r({
64
+ function a(e) {
65
+ return i({
50
66
  kind: "localStorage",
51
67
  ...e
52
68
  });
53
69
  }
54
70
  //#endregion
55
71
  //#region src/web/web-storage-value.ts
56
- function a(t) {
72
+ function o(t) {
57
73
  let r = "storage" in t ? t.storage["~"].kind : t.kind, i = !1, a = `${r}Value`;
58
74
  if ("schema" in t && t.defaultValue === void 0) throw Error(`[${a}] Default value is required`);
59
75
  if (t.key === void 0) throw Error(`[${a}] Key is required`);
60
76
  if (!("schema" in t || "storage" in t)) throw Error(`[${a}] Either schema or storage must be provided`);
61
77
  let o = ("schema" in t ? t.defaultValue : t.storage.getDefaultValue(t.key)) ?? null, { subscribe: s, notify: c } = n(), l = () => {
62
78
  if (typeof window > "u") return o;
63
- let n = window[r].getItem(t.key);
64
- if (n === null) return o;
65
- let i = e(n);
79
+ let n = window[r], i = n.getItem(t.key);
80
+ if (i === null) return o;
81
+ let a = e(i);
66
82
  try {
67
83
  if ("schema" in t) {
68
- let e = t.schema["~standard"].validate(i);
84
+ let e = t.schema["~standard"].validate(a);
69
85
  if (e instanceof Promise) throw TypeError("Validation schema should not return a Promise.");
70
- return e.issues ? (console.error(JSON.stringify(e.issues, null, 2), { cause: e.issues }), o) : e.value;
71
- } else return i;
86
+ return e.issues ? (t.clearOnValidationFailure ? n.removeItem(t.key) : console.warn(JSON.stringify(e.issues, null, 2), { cause: e.issues }), o) : e.value;
87
+ } else return a;
72
88
  } catch {
73
- return o !== void 0 && typeof o != "string" ? o : i;
89
+ return o !== void 0 && typeof o != "string" ? o : a;
74
90
  }
75
91
  }, u = (e) => {
76
92
  i || e.key === t.key && c();
@@ -97,15 +113,15 @@ function a(t) {
97
113
  }
98
114
  //#endregion
99
115
  //#region src/web/local-storage-value.ts
100
- function o(e) {
101
- return "storage" in e ? a(e) : a({
116
+ function s(e) {
117
+ return "storage" in e ? o(e) : o({
102
118
  ...e,
103
119
  kind: "localStorage"
104
120
  });
105
121
  }
106
122
  //#endregion
107
123
  //#region src/web/media-query.ts
108
- function s(e) {
124
+ function c(e) {
109
125
  let { subscribe: t, notify: r } = n(), i = typeof window > "u" ? null : window.matchMedia(e.query), a = () => i?.matches ?? e.defaultMatches ?? !1, o = () => r();
110
126
  return i?.addEventListener("change", o), {
111
127
  get: a,
@@ -121,11 +137,11 @@ function s(e) {
121
137
  }
122
138
  //#endregion
123
139
  //#region src/web/scroll-state.ts
124
- var c = {
140
+ var l = {
125
141
  reached: !1,
126
142
  remaining: 0
127
143
  };
128
- function l(e) {
144
+ function u(e) {
129
145
  let { direction: t = "both", threshold: r = 0 } = e, { subscribe: i, notify: a } = n(), o = typeof r == "number" ? {
130
146
  top: r,
131
147
  bottom: r,
@@ -136,31 +152,31 @@ function l(e) {
136
152
  bottom: r.bottom ?? 0,
137
153
  left: r.left ?? 0,
138
154
  right: r.right ?? 0
139
- }, s = () => typeof e.element == "function" ? e.element() : e.element, l = () => {
155
+ }, s = () => typeof e.element == "function" ? e.element() : e.element, c = () => {
140
156
  let e = s(), n = (e, t) => ({
141
157
  reached: e,
142
158
  remaining: Math.max(0, t)
143
159
  });
144
160
  if (!e) return {
145
- top: c,
146
- bottom: c,
147
- left: c,
148
- right: c
161
+ top: l,
162
+ bottom: l,
163
+ left: l,
164
+ right: l
149
165
  };
150
- let r = e.scrollTop, i = e.scrollHeight - e.scrollTop - e.clientHeight, a = e.scrollLeft, l = e.scrollWidth - e.scrollLeft - e.clientWidth;
166
+ let r = e.scrollTop, i = e.scrollHeight - e.scrollTop - e.clientHeight, a = e.scrollLeft, c = e.scrollWidth - e.scrollLeft - e.clientWidth;
151
167
  return {
152
- top: t === "horizontal" ? c : n(r <= o.top, r),
153
- bottom: t === "horizontal" ? c : n(i <= o.bottom, i),
154
- left: t === "vertical" ? c : n(a <= o.left, a),
155
- right: t === "vertical" ? c : n(l <= o.right, l)
168
+ top: t === "horizontal" ? l : n(r <= o.top, r),
169
+ bottom: t === "horizontal" ? l : n(i <= o.bottom, i),
170
+ left: t === "vertical" ? l : n(a <= o.left, a),
171
+ right: t === "vertical" ? l : n(c <= o.right, c)
156
172
  };
157
173
  };
158
174
  return {
159
- get: l,
175
+ get: c,
160
176
  subscribe: (e) => {
161
177
  let t = s();
162
- if (!t) return e(l()), () => {};
163
- let n = i(() => e(l())), r = () => e(l());
178
+ if (!t) return e(c()), () => {};
179
+ let n = i(() => e(c())), r = () => e(c());
164
180
  return t.addEventListener("scroll", r, { passive: !0 }), () => {
165
181
  n(), t.removeEventListener("scroll", r);
166
182
  };
@@ -173,19 +189,19 @@ function l(e) {
173
189
  }
174
190
  //#endregion
175
191
  //#region src/web/session-storage.ts
176
- function u(e) {
177
- return r({
192
+ function d(e) {
193
+ return i({
178
194
  kind: "sessionStorage",
179
195
  ...e
180
196
  });
181
197
  }
182
198
  //#endregion
183
199
  //#region src/web/session-storage-value.ts
184
- function d(e) {
185
- return "storage" in e ? a(e) : a({
200
+ function f(e) {
201
+ return "storage" in e ? o(e) : o({
186
202
  ...e,
187
203
  kind: "sessionStorage"
188
204
  });
189
205
  }
190
206
  //#endregion
191
- export { i as createLocalStorage, o as createLocalStorageValue, s as createMediaQuery, l as createScrollState, u as createSessionStorage, d as createSessionStorageValue };
207
+ export { r as createIsOnline, a as createLocalStorage, s as createLocalStorageValue, c as createMediaQuery, u as createScrollState, d as createSessionStorage, f as createSessionStorageValue };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "seitu",
3
3
  "displayName": "Seitu",
4
4
  "type": "module",
5
- "version": "0.4.7",
5
+ "version": "0.5.1",
6
6
  "private": false,
7
7
  "author": "Valerii Strilets",
8
8
  "license": "MIT",
@@ -64,14 +64,14 @@
64
64
  "@testing-library/jest-dom": "^6.9.1",
65
65
  "@testing-library/react": "^16.3.2",
66
66
  "@types/react": "^19.2.14",
67
- "@vitejs/plugin-react": "^6.0.1",
68
- "happy-dom": "^20.8.4",
67
+ "@vitejs/plugin-react": "^5.1.4",
68
+ "happy-dom": "^20.8.3",
69
69
  "react-dom": "^19.2.4",
70
70
  "type-fest": "^5.4.4",
71
71
  "typescript": "^5.9.3",
72
- "vite": "^8.0.0",
72
+ "vite": "^8.0.0-beta.16",
73
73
  "vite-plugin-dts": "^4.5.4",
74
- "vitest": "^4.1.0",
74
+ "vitest": "^4.0.18",
75
75
  "zod": "^4.3.6"
76
76
  },
77
77
  "scripts": {