@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.
Files changed (2) hide show
  1. package/README.md +20 -11
  2. 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 *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.
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 *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.
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) showError(err.body); // raw response body
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@urbicon-ui/sveltekit-utils",
3
- "version": "8.21.0",
3
+ "version": "8.22.0",
4
4
  "description": "SvelteKit helper utilities — createCronRunner, streamSse, and URL-state runes",
5
5
  "license": "MIT",
6
6
  "repository": {