@sveltebase/utils 0.2.2 → 0.3.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 +332 -0
- package/dist/async.svelte.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# `@sveltebase/utils`
|
|
2
|
+
|
|
3
|
+
Utility helpers for Svelte 5 apps, with small primitives for cookies, async actions, timestamps, waiting, and toast-friendly error handling.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
With Bun:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bun add @sveltebase/utils
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
If you want toast notifications from `createAsync` or `tryCatch`, also install `svelte-sonner`:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bun add svelte-sonner
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
> `svelte` is a peer dependency and should already exist in your app.
|
|
20
|
+
|
|
21
|
+
## What’s included
|
|
22
|
+
|
|
23
|
+
- `Cookies` — browser cookie helpers
|
|
24
|
+
- `createAsync` — wraps async functions with loading and error state
|
|
25
|
+
- `tryCatch` — run a task with built-in toast/error handling
|
|
26
|
+
- `timestamps` — generate `createdAt` / `updatedAt` values
|
|
27
|
+
- `wait` — simple promise-based delay helper
|
|
28
|
+
- `TryCatchReturn` — shared return type for async helpers
|
|
29
|
+
|
|
30
|
+
## Exports
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import {
|
|
34
|
+
Cookies,
|
|
35
|
+
createAsync,
|
|
36
|
+
timestamps,
|
|
37
|
+
tryCatch,
|
|
38
|
+
wait,
|
|
39
|
+
type TryCatchReturn
|
|
40
|
+
} from "@sveltebase/utils";
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## `Cookies`
|
|
46
|
+
|
|
47
|
+
A small browser-only cookie helper with `set`, `get`, and `remove`.
|
|
48
|
+
|
|
49
|
+
### Example
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { Cookies } from "@sveltebase/utils";
|
|
53
|
+
|
|
54
|
+
Cookies.set("theme", "dark", {
|
|
55
|
+
path: "/",
|
|
56
|
+
sameSite: "Lax",
|
|
57
|
+
expires: 30
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const theme = Cookies.get("theme");
|
|
61
|
+
|
|
62
|
+
Cookies.remove("theme");
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### API
|
|
66
|
+
|
|
67
|
+
#### `Cookies.set(name, value, options?)`
|
|
68
|
+
|
|
69
|
+
Sets a cookie.
|
|
70
|
+
|
|
71
|
+
Options:
|
|
72
|
+
|
|
73
|
+
- `expires?: number` — number of days
|
|
74
|
+
- `path?: string`
|
|
75
|
+
- `domain?: string`
|
|
76
|
+
- `secure?: boolean`
|
|
77
|
+
- `sameSite?: "Lax" | "Strict" | "None"`
|
|
78
|
+
- `partitioned?: boolean`
|
|
79
|
+
|
|
80
|
+
#### `Cookies.get(name)`
|
|
81
|
+
|
|
82
|
+
Returns the cookie value as `string | null`.
|
|
83
|
+
|
|
84
|
+
#### `Cookies.remove(name, options?)`
|
|
85
|
+
|
|
86
|
+
Removes a cookie by setting an expired value.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## `createAsync`
|
|
91
|
+
|
|
92
|
+
Wraps an async function and gives you:
|
|
93
|
+
|
|
94
|
+
- loading state
|
|
95
|
+
- last thrown error
|
|
96
|
+
- optional success/error toasts
|
|
97
|
+
|
|
98
|
+
Your async function can return:
|
|
99
|
+
|
|
100
|
+
- `{ success: string }`
|
|
101
|
+
- `{ error: string }`
|
|
102
|
+
- `null`
|
|
103
|
+
- `void`
|
|
104
|
+
|
|
105
|
+
### Example
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { createAsync } from "@sveltebase/utils";
|
|
109
|
+
|
|
110
|
+
const saveProfile = createAsync(async (name: string) => {
|
|
111
|
+
const response = await fetch("/api/profile", {
|
|
112
|
+
method: "POST",
|
|
113
|
+
headers: {
|
|
114
|
+
"Content-Type": "application/json"
|
|
115
|
+
},
|
|
116
|
+
body: JSON.stringify({ name })
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
if (!response.ok) {
|
|
120
|
+
return { error: "Failed to save profile" };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return { success: "Profile saved" };
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
await saveProfile.run("Ahror");
|
|
127
|
+
|
|
128
|
+
console.log(saveProfile.isLoading());
|
|
129
|
+
console.log(saveProfile.error);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Keyed loading states
|
|
133
|
+
|
|
134
|
+
Use `runWithKey` when you want separate loading states for multiple actions.
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import { createAsync } from "@sveltebase/utils";
|
|
138
|
+
|
|
139
|
+
const removeItem = createAsync(async (id: string) => {
|
|
140
|
+
const response = await fetch(`/api/items/${id}`, {
|
|
141
|
+
method: "DELETE"
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
if (!response.ok) {
|
|
145
|
+
return { error: "Delete failed" };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return { success: "Item removed" };
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
await removeItem.runWithKey("item-42", "42");
|
|
152
|
+
|
|
153
|
+
const isDeleting = removeItem.isLoading("item-42");
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### API
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
const task = createAsync(asyncFn);
|
|
160
|
+
|
|
161
|
+
task.run(...args);
|
|
162
|
+
task.runWithKey(key, ...args);
|
|
163
|
+
task.isLoading(key?);
|
|
164
|
+
task.error;
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Notes
|
|
168
|
+
|
|
169
|
+
- Toasts are shown with `svelte-sonner` when available in the browser.
|
|
170
|
+
- In development, thrown errors are also logged to the console.
|
|
171
|
+
- On the server, toast behavior is skipped safely.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## `tryCatch`
|
|
176
|
+
|
|
177
|
+
Runs a task and handles success/error messages in a consistent way.
|
|
178
|
+
|
|
179
|
+
### Example
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
import { tryCatch } from "@sveltebase/utils";
|
|
183
|
+
|
|
184
|
+
await tryCatch(async () => {
|
|
185
|
+
const response = await fetch("/api/invite", { method: "POST" });
|
|
186
|
+
|
|
187
|
+
if (!response.ok) {
|
|
188
|
+
return { error: "Could not send invite" };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return { success: "Invite sent" };
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Throwing errors
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import { tryCatch } from "@sveltebase/utils";
|
|
199
|
+
|
|
200
|
+
await tryCatch(async () => {
|
|
201
|
+
const response = await fetch("/api/private");
|
|
202
|
+
|
|
203
|
+
if (response.status === 401) {
|
|
204
|
+
throw new Error("Unauthorized");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return { success: "Loaded" };
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## `timestamps`
|
|
214
|
+
|
|
215
|
+
Creates timestamp objects using `Date.now()`.
|
|
216
|
+
|
|
217
|
+
### Example
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
import { timestamps } from "@sveltebase/utils";
|
|
221
|
+
|
|
222
|
+
const created = timestamps(false);
|
|
223
|
+
// { createdAt: 1712345678901, updatedAt: 1712345678901 }
|
|
224
|
+
|
|
225
|
+
const updated = timestamps(true);
|
|
226
|
+
// { updatedAt: 1712345678901 }
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Common usage
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
import { timestamps } from "@sveltebase/utils";
|
|
233
|
+
|
|
234
|
+
const post = {
|
|
235
|
+
id: crypto.randomUUID(),
|
|
236
|
+
title: "Hello",
|
|
237
|
+
...timestamps(false)
|
|
238
|
+
};
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## `wait`
|
|
244
|
+
|
|
245
|
+
Returns a promise that resolves after the given number of milliseconds.
|
|
246
|
+
|
|
247
|
+
### Example
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
import { wait } from "@sveltebase/utils";
|
|
251
|
+
|
|
252
|
+
await wait(500);
|
|
253
|
+
console.log("Done");
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Useful for:
|
|
257
|
+
|
|
258
|
+
- delaying UI transitions
|
|
259
|
+
- retry flows
|
|
260
|
+
- testing async behavior
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## `TryCatchReturn`
|
|
265
|
+
|
|
266
|
+
A shared type for functions used with `createAsync` and `tryCatch`.
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
import type { TryCatchReturn } from "@sveltebase/utils";
|
|
270
|
+
|
|
271
|
+
async function submitForm(): Promise<TryCatchReturn> {
|
|
272
|
+
return { success: "Submitted" };
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Possible values:
|
|
277
|
+
|
|
278
|
+
```ts
|
|
279
|
+
type TryCatchReturn =
|
|
280
|
+
| { success: string; error?: never }
|
|
281
|
+
| { error: string; success?: never }
|
|
282
|
+
| null
|
|
283
|
+
| void;
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Svelte example
|
|
289
|
+
|
|
290
|
+
```svelte
|
|
291
|
+
<script lang="ts">
|
|
292
|
+
import { createAsync } from "@sveltebase/utils";
|
|
293
|
+
|
|
294
|
+
let name = $state("");
|
|
295
|
+
|
|
296
|
+
const save = createAsync(async (value: string) => {
|
|
297
|
+
const response = await fetch("/api/profile", {
|
|
298
|
+
method: "POST",
|
|
299
|
+
headers: {
|
|
300
|
+
"Content-Type": "application/json"
|
|
301
|
+
},
|
|
302
|
+
body: JSON.stringify({ name: value })
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
if (!response.ok) {
|
|
306
|
+
return { error: "Could not save profile" };
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return { success: "Profile saved" };
|
|
310
|
+
});
|
|
311
|
+
</script>
|
|
312
|
+
|
|
313
|
+
<input bind:value={name} placeholder="Your name" />
|
|
314
|
+
|
|
315
|
+
<button onclick={() => save.run(name)} disabled={save.isLoading()}>
|
|
316
|
+
{save.isLoading() ? "Saving..." : "Save"}
|
|
317
|
+
</button>
|
|
318
|
+
|
|
319
|
+
{#if save.error}
|
|
320
|
+
<p>{save.error.message}</p>
|
|
321
|
+
{/if}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## Notes
|
|
325
|
+
|
|
326
|
+
- `Cookies` works only in the browser.
|
|
327
|
+
- `createAsync` and `tryCatch` integrate with `svelte-sonner` if it is installed.
|
|
328
|
+
- The package is designed for Svelte 5 projects.
|
|
329
|
+
|
|
330
|
+
## License
|
|
331
|
+
|
|
332
|
+
ISC
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async.svelte.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"async.svelte.d.ts","sourceRoot":"","sources":["async.svelte.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,cAAc,GACtB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GAClC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,KAAK,CAAA;CAAE,GAClC,IAAI,GACJ,IAAI,CAAC;AAqCT,wBAAgB,WAAW,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,cAAc,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,EAC/F,OAAO,EAAE,CAAC;IAmDR;;;OAGG;oBACa,MAAM;;mBAhBI,UAAU,CAAC,CAAC,CAAC;sBAOV,MAAM,WAAW,UAAU,CAAC,CAAC,CAAC;EAkB9D"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAuCD,eAAO,MAAM,OAAO;cACR,MAAM,SAAS,MAAM,YAAW,aAAa,GAAQ,IAAI;cA6CzD,MAAM,GAAG,MAAM,GAAG,IAAI;iBAYnB,MAAM,YAAW,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,QAAQ,CAAC,GAAQ,IAAI;CAGjF,CAAC;AAEF,wBAAgB,UAAU,CAAC,CAAC,SAAS,OAAO,EAC1C,UAAU,EAAE,CAAC,GACZ,CAAC,SAAS,IAAI,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAInF;AAED,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,iBAkBlF;AAED,eAAO,MAAM,IAAI,GAAI,IAAI,MAAM,qBAAsD,CAAC;AAEtF,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
|