@urbicon-ui/sveltekit-utils 8.21.0 → 8.22.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 +20 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ Currently shipping:
|
|
|
15
15
|
bun add @urbicon-ui/sveltekit-utils
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
Peer dependencies: `svelte` (^5), `@sveltejs/kit`.
|
|
18
|
+
Peer dependencies: `svelte` (^5.57.0), `@sveltejs/kit`.
|
|
19
19
|
|
|
20
20
|
The declared `@sveltejs/kit` range is 2.x. The package runs under SvelteKit 3 `next` as well; the incorrect-peer warning `bun add` prints there is expected and stays until Kit 3 has a release candidate, when the range widens.
|
|
21
21
|
|
|
@@ -47,6 +47,7 @@ Bind a typed, reactive value to a URL search param. When the value changes, the
|
|
|
47
47
|
Low-level escape hatch if you prefer to update multiple params at once:
|
|
48
48
|
|
|
49
49
|
<!-- typecheck -->
|
|
50
|
+
|
|
50
51
|
```typescript
|
|
51
52
|
import { updateUrlSearchParams } from '@urbicon-ui/sveltekit-utils/url.svelte';
|
|
52
53
|
|
|
@@ -56,6 +57,7 @@ updateUrlSearchParams({ page: '1', tag: ['a', 'b'] }, { replaceState: true });
|
|
|
56
57
|
A link needs an address, not a setter. `withSearchParams(url, patch)` is the pure core that `updateUrlSearchParams` and `createUrlParam`'s setter navigate to: the address `url` has after `patch` — a scalar `set`s its key, an array `append`s each element, `null` removes the key, every other param stays as it is. An empty string is a value and keeps its key (`?a=`); an empty array appends nothing and so removes it. It reads nothing from the page and navigates nowhere, so the same call builds a link's `href` and a redirect's location.
|
|
57
58
|
|
|
58
59
|
<!-- typecheck -->
|
|
60
|
+
|
|
59
61
|
```typescript
|
|
60
62
|
import { withSearchParams } from '@urbicon-ui/sveltekit-utils/search-params';
|
|
61
63
|
|
|
@@ -69,6 +71,7 @@ withSearchParams(url, { sort: null, page: null }); // '/archive'
|
|
|
69
71
|
`./search-params` is the import path that reaches no `$app/*` module, so a `load`, a form action or a plain test can use it; importing `withSearchParams` from `./url.svelte` (or from the package root) is the same function, but pulls SvelteKit's client runtime along:
|
|
70
72
|
|
|
71
73
|
<!-- typecheck -->
|
|
74
|
+
|
|
72
75
|
```typescript
|
|
73
76
|
// src/routes/archive/+page.server.ts
|
|
74
77
|
import { withSearchParams } from '@urbicon-ui/sveltekit-utils/search-params';
|
|
@@ -136,7 +139,7 @@ The second argument is optional; every option has a default:
|
|
|
136
139
|
|
|
137
140
|
Because the binding re-reads the URL rather than capturing it, the browser's back button works: navigating back to a URL that no longer names `?sort` returns the table to its default sort.
|
|
138
141
|
|
|
139
|
-
The pure serializers work without SvelteKit — e.g. to parse the incoming query in a server `load` and fetch the first page during SSR. Use `searchParamsToViewSnapshot` from `./table-view`: it takes the
|
|
142
|
+
The pure serializers work without SvelteKit — e.g. to parse the incoming query in a server `load` and fetch the first page during SSR. Use `searchParamsToViewSnapshot` from `./table-view`: it takes the _same_ defaults object the component hands `createTableView`, so the server cannot resolve an absent param differently from the client, and it hands back the very shape a managed `source.query` receives.
|
|
140
143
|
|
|
141
144
|
```typescript
|
|
142
145
|
// src/lib/view-defaults.ts — imported by the component and by the load function.
|
|
@@ -145,6 +148,7 @@ export const userView = { pageSize: 25, sort: { column: 'joined', direction: 'de
|
|
|
145
148
|
```
|
|
146
149
|
|
|
147
150
|
<!-- typecheck -->
|
|
151
|
+
|
|
148
152
|
```typescript
|
|
149
153
|
// src/routes/users/+page.server.ts
|
|
150
154
|
import { searchParamsToViewSnapshot } from '@urbicon-ui/sveltekit-utils/table-view';
|
|
@@ -161,7 +165,7 @@ The `./table-query` subpath that used to hold a second copy of this codec — sa
|
|
|
161
165
|
|
|
162
166
|
**Design notes**
|
|
163
167
|
|
|
164
|
-
- **Default elision** — an axis whose value equals its default is not written; a table in its default state leaves the URL clean. The baseline
|
|
168
|
+
- **Default elision** — an axis whose value equals its default is not written; a table in its default state leaves the URL clean. The baseline _is_ `view.defaults`, read off the object the binding decorates, so there is no second copy of the defaults to keep in step with the table's own.
|
|
165
169
|
- **Read tolerant, write strict** — an unparsable value on a param the URL actually carries falls back to that axis' default, and malformed `filter` entries are skipped individually. `assertValidViewSnapshot` is the strict half: it throws on a structurally invalid view (non-positive page, unknown operator) instead of writing corrupt state, and `applyViewToSearchParams` calls it. `viewSnapshotToSearchParams` deliberately does not — it runs inside the binding on every view change, where a throw would cost the page rather than the URL.
|
|
166
170
|
- **Namespacing** — `prefix: 't_'` scopes all keys (`?t_q=…`) for multiple bound tables on one page; unrelated params are always preserved. Two prefixless bindings would manage the same keys, so that throws at registration instead of producing a link that loads the wrong table.
|
|
167
171
|
- **One writer per page** — every binding submits into one coalescing URL writer, so two tables land in a single navigation, each replacing only its own keys. A landing URL the writer itself sent is not applied back onto the view: an edit made while that navigation was in flight survives instead of being overwritten by the URL it raced.
|
|
@@ -174,6 +178,7 @@ Fire HTTP requests against SvelteKit server endpoints on an interval. Pair with
|
|
|
174
178
|
**Import from `@urbicon-ui/sveltekit-utils/cron`**, not from the package root. The runner is wired up in server code — `hooks.server.ts`, or a module it imports — and the root barrel carries `url.svelte` along, whose `$app/navigation` and `$app/state` imports are SvelteKit's client runtime. The subpath reaches no `$app/*` module at all.
|
|
175
179
|
|
|
176
180
|
<!-- typecheck -->
|
|
181
|
+
|
|
177
182
|
```typescript
|
|
178
183
|
// src/lib/server/cron.ts
|
|
179
184
|
import { createCronRunner } from '@urbicon-ui/sveltekit-utils/cron';
|
|
@@ -206,6 +211,7 @@ The first fire happens **after** one interval: `start()` arms the timers, it doe
|
|
|
206
211
|
Receive the call and verify the secret inside your endpoint:
|
|
207
212
|
|
|
208
213
|
<!-- typecheck -->
|
|
214
|
+
|
|
209
215
|
```typescript
|
|
210
216
|
// src/routes/api/cron/send-digest/+server.ts
|
|
211
217
|
import { env } from '$env/dynamic/private';
|
|
@@ -242,6 +248,7 @@ That holds together when the endpoint is idempotent, which means
|
|
|
242
248
|
- a restart loses nothing _within_ a day. Whether it can lose a whole one depends on the shape: a job that computes from state — last activity, say — heals a skipped day on its next tick, while a per-day rollup like the one below only ever writes today and needs a backfill for the day the process was down.
|
|
243
249
|
|
|
244
250
|
<!-- typecheck -->
|
|
251
|
+
|
|
245
252
|
```typescript
|
|
246
253
|
// src/routes/api/cron/daily/+server.ts
|
|
247
254
|
import { env } from '$env/dynamic/private';
|
|
@@ -288,7 +295,8 @@ try {
|
|
|
288
295
|
else if (ev.event === 'error') throw new Error(JSON.parse(ev.data).message);
|
|
289
296
|
}
|
|
290
297
|
} catch (err) {
|
|
291
|
-
if (err instanceof SseRequestError)
|
|
298
|
+
if (err instanceof SseRequestError)
|
|
299
|
+
showError(err.body); // raw response body
|
|
292
300
|
else if ((err as Error).name !== 'AbortError') throw err;
|
|
293
301
|
}
|
|
294
302
|
|
|
@@ -299,6 +307,7 @@ controller.abort();
|
|
|
299
307
|
Emit the matching frames from the endpoint:
|
|
300
308
|
|
|
301
309
|
<!-- typecheck -->
|
|
310
|
+
|
|
302
311
|
```typescript
|
|
303
312
|
// src/routes/api/chat/+server.ts
|
|
304
313
|
import { runModel } from '$lib/server/model';
|
|
@@ -327,14 +336,14 @@ export const POST: RequestHandler = async ({ request }) => {
|
|
|
327
336
|
|
|
328
337
|
## Exports
|
|
329
338
|
|
|
330
|
-
| Subpath | Contents
|
|
331
|
-
| ----------------- |
|
|
332
|
-
| `.` | Barrel of all modules
|
|
333
|
-
| `./url.svelte` | `useUrlParam`, `useUrlArrayParam`, `createUrlParam`, `updateUrlSearchParams`, `bindViewToUrl`, types (re-exports `withSearchParams`)
|
|
334
|
-
| `./search-params` | `withSearchParams`, `SearchParamsPatch` — no `$app/*` import
|
|
339
|
+
| Subpath | Contents |
|
|
340
|
+
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
341
|
+
| `.` | Barrel of all modules |
|
|
342
|
+
| `./url.svelte` | `useUrlParam`, `useUrlArrayParam`, `createUrlParam`, `updateUrlSearchParams`, `bindViewToUrl`, types (re-exports `withSearchParams`) |
|
|
343
|
+
| `./search-params` | `withSearchParams`, `SearchParamsPatch` — no `$app/*` import |
|
|
335
344
|
| `./table-view` | `searchParamsToViewSnapshot`, `searchParamsToViewPartial`, `viewSnapshotToSearchParams`, `applyViewToSearchParams`, `assertValidViewSnapshot`, `viewAxesNamedBy`, `viewAxisKeys`, `TABLE_VIEW_AXES`, `TABLE_VIEW_FILTER_OPERATORS`, `TableViewLike`, types |
|
|
336
|
-
| `./cron` | `createCronRunner`, `CronJob`, `CronRunnerConfig`, `CronRunner`
|
|
337
|
-
| `./sse` | `streamSse`, `SseEvent`, `StreamSseOptions`, `SseRequestError`
|
|
345
|
+
| `./cron` | `createCronRunner`, `CronJob`, `CronRunnerConfig`, `CronRunner` |
|
|
346
|
+
| `./sse` | `streamSse`, `SseEvent`, `StreamSseOptions`, `SseRequestError` |
|
|
338
347
|
|
|
339
348
|
`bindViewToUrl` lives in its own module (`view-binding.svelte.ts`) and is re-exported from `./url.svelte`, which is its documented import path — it has no subpath of its own. `./search-params`, `./table-view` and `./cron` are SvelteKit-free (they touch no `$app/*`), which is what lets a `load` function, `hooks.server.ts` and a plain test use them; `./url.svelte` is the half that needs the router — importing it from server code pulls SvelteKit's client runtime in, which is why `withSearchParams` has a subpath of its own as well as the re-export.
|
|
340
349
|
|