@urbicon-ui/sveltekit-utils 8.26.1 → 8.28.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 +38 -22
- package/dist/cron.d.ts +7 -9
- package/dist/cron.js +21 -12
- package/dist/search-params.d.ts +11 -1
- package/dist/search-params.js +1 -1
- package/dist/table-view.d.ts +5 -4
- package/dist/table-view.js +1 -1
- package/dist/url.svelte.d.ts +4 -4
- package/dist/url.svelte.js +6 -8
- package/dist/view-binding.svelte.js +3 -4
- package/package.json +12 -8
package/README.md
CHANGED
|
@@ -15,9 +15,7 @@ Currently shipping:
|
|
|
15
15
|
bun add @urbicon-ui/sveltekit-utils
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
Peer dependencies: `svelte` (^5.57.
|
|
19
|
-
|
|
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.
|
|
18
|
+
Peer dependencies: `svelte` (^5.57.1), `@sveltejs/kit` (^3.0.0).
|
|
21
19
|
|
|
22
20
|
## URL State (`url.svelte`)
|
|
23
21
|
|
|
@@ -104,7 +102,7 @@ Link or binding: an `href` from `withSearchParams` where the reader picks a dest
|
|
|
104
102
|
|
|
105
103
|
**Design notes**
|
|
106
104
|
|
|
107
|
-
- URL updates use `goto()` with `
|
|
105
|
+
- URL updates use `goto()` with `replace: true` and `reset: false` (scroll position and focus stay put) — suited for filter/pagination UIs, not full page transitions.
|
|
108
106
|
- `updateUrlSearchParams` and `createUrlParam`'s setter hand `goto` the pathname-qualified address `withSearchParams` returns, never a bare `?query`. `goto` resolves a relative target against `document.baseURI`, which equals the page's own URL only while the document carries no `<base href>` — with one, `?page=2` keeps the base's path, not the page's.
|
|
109
107
|
- `bindViewToUrl` is a third URL writer and does **not** go through `withSearchParams`: it merges the view axes itself and carries the URL hash across, where `withSearchParams` drops it. Do not read one policy off the other.
|
|
110
108
|
- `useUrlParam` returns getters (not Svelte stores) so consumers can read the value lazily inside `$derived`/`$effect`.
|
|
@@ -152,8 +150,8 @@ export const userView = { pageSize: 25, sort: { column: 'joined', direction: 'de
|
|
|
152
150
|
```typescript
|
|
153
151
|
// src/routes/users/+page.server.ts
|
|
154
152
|
import { searchParamsToViewSnapshot } from '@urbicon-ui/sveltekit-utils/table-view';
|
|
155
|
-
import { fetchUsers } from '
|
|
156
|
-
import { userView } from '
|
|
153
|
+
import { fetchUsers } from '#lib/server/users.js';
|
|
154
|
+
import { userView } from '#lib/view-defaults.js';
|
|
157
155
|
import type { PageServerLoad } from './$types';
|
|
158
156
|
|
|
159
157
|
export const load: PageServerLoad = async ({ url }) => ({
|
|
@@ -177,22 +175,39 @@ Fire HTTP requests against SvelteKit server endpoints on an interval. Pair with
|
|
|
177
175
|
|
|
178
176
|
**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.
|
|
179
177
|
|
|
178
|
+
The secret and the base URL are environment variables, declared once in `src/env.ts`:
|
|
179
|
+
|
|
180
|
+
<!-- typecheck -->
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
// src/env.ts
|
|
184
|
+
import { defineEnvVars } from '@sveltejs/kit/env';
|
|
185
|
+
|
|
186
|
+
export const variables = defineEnvVars({
|
|
187
|
+
// Dynamic (Kit's default): a node server reads it at start, not at build.
|
|
188
|
+
// Rejecting the empty string makes a missing secret a startup failure — not
|
|
189
|
+
// an unauthenticated cron loop.
|
|
190
|
+
CRON_SECRET: {
|
|
191
|
+
schema: (value) => {
|
|
192
|
+
if (!value) throw new Error('CRON_SECRET is not set');
|
|
193
|
+
return value;
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
// A schema that may return `undefined` makes the variable optional.
|
|
197
|
+
BASE_URL: { schema: (value) => value }
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
180
201
|
<!-- typecheck -->
|
|
181
202
|
|
|
182
203
|
```typescript
|
|
183
204
|
// src/lib/server/cron.ts
|
|
184
205
|
import { createCronRunner } from '@urbicon-ui/sveltekit-utils/cron';
|
|
185
|
-
import {
|
|
186
|
-
|
|
187
|
-
// Runtime env, not `$env/static/private`: a node server reads its secret at
|
|
188
|
-
// start, not at build. The runner needs a `string`, so a missing one is a
|
|
189
|
-
// startup failure here — not an unauthenticated cron loop.
|
|
190
|
-
const secret = env.CRON_SECRET;
|
|
191
|
-
if (!secret) throw new Error('CRON_SECRET is not set');
|
|
206
|
+
import { BASE_URL, CRON_SECRET } from '$app/env/private';
|
|
192
207
|
|
|
193
208
|
export const cron = createCronRunner({
|
|
194
|
-
secret,
|
|
195
|
-
baseUrl:
|
|
209
|
+
secret: CRON_SECRET,
|
|
210
|
+
baseUrl: BASE_URL,
|
|
196
211
|
jobs: [
|
|
197
212
|
{ path: '/api/cron/send-digest', intervalSeconds: 3600 },
|
|
198
213
|
{ path: '/api/cron/cleanup', intervalSeconds: 900, method: 'POST' }
|
|
@@ -214,12 +229,12 @@ Receive the call and verify the secret inside your endpoint:
|
|
|
214
229
|
|
|
215
230
|
```typescript
|
|
216
231
|
// src/routes/api/cron/send-digest/+server.ts
|
|
217
|
-
import {
|
|
218
|
-
import { sendDigest } from '
|
|
232
|
+
import { CRON_SECRET } from '$app/env/private';
|
|
233
|
+
import { sendDigest } from '#lib/server/digest.js';
|
|
219
234
|
import type { RequestHandler } from './$types';
|
|
220
235
|
|
|
221
236
|
export const POST: RequestHandler = async ({ request }) => {
|
|
222
|
-
if (
|
|
237
|
+
if (request.headers.get('x-cron-secret') !== CRON_SECRET) {
|
|
223
238
|
return new Response('Forbidden', { status: 403 });
|
|
224
239
|
}
|
|
225
240
|
await sendDigest();
|
|
@@ -251,8 +266,8 @@ That holds together when the endpoint is idempotent, which means
|
|
|
251
266
|
|
|
252
267
|
```typescript
|
|
253
268
|
// src/routes/api/cron/daily/+server.ts
|
|
254
|
-
import {
|
|
255
|
-
import { upsertDailyRollup } from '
|
|
269
|
+
import { CRON_SECRET } from '$app/env/private';
|
|
270
|
+
import { upsertDailyRollup } from '#lib/server/rollup.js';
|
|
256
271
|
import type { RequestHandler } from './$types';
|
|
257
272
|
|
|
258
273
|
// Which midnight: the zone your people live in. `en-CA` formats as YYYY-MM-DD.
|
|
@@ -260,7 +275,7 @@ const TIME_ZONE = 'Europe/Berlin';
|
|
|
260
275
|
const dayKey = new Intl.DateTimeFormat('en-CA', { timeZone: TIME_ZONE });
|
|
261
276
|
|
|
262
277
|
export const POST: RequestHandler = async ({ request }) => {
|
|
263
|
-
if (
|
|
278
|
+
if (request.headers.get('x-cron-secret') !== CRON_SECRET) {
|
|
264
279
|
return new Response('Forbidden', { status: 403 });
|
|
265
280
|
}
|
|
266
281
|
// Keyed on the day and written atomically, so the second call of the day —
|
|
@@ -276,6 +291,7 @@ export const POST: RequestHandler = async ({ request }) => {
|
|
|
276
291
|
|
|
277
292
|
- Simple `setInterval`-based scheduler. No drift compensation, no distributed locking, no exponential backoff — intended for single-process SvelteKit deployments. For scale-out scenarios use a real scheduler (e.g. BullMQ) and point it at the same HTTP endpoints.
|
|
278
293
|
- Header name defaults to `x-cron-secret`; override via `secretHeader`.
|
|
294
|
+
- A `POST` job sends `{}` as `application/json`. The request carries no `Origin` header, and SvelteKit's CSRF check refuses an Origin-less `POST` with a form content type or none at all — a JSON body is what lets it reach your endpoint in a built app.
|
|
279
295
|
|
|
280
296
|
## SSE Stream Reader (`sse`)
|
|
281
297
|
|
|
@@ -310,7 +326,7 @@ Emit the matching frames from the endpoint:
|
|
|
310
326
|
|
|
311
327
|
```typescript
|
|
312
328
|
// src/routes/api/chat/+server.ts
|
|
313
|
-
import { runModel } from '
|
|
329
|
+
import { runModel } from '#lib/server/model.js';
|
|
314
330
|
import type { RequestHandler } from './$types';
|
|
315
331
|
|
|
316
332
|
export const POST: RequestHandler = async ({ request }) => {
|
package/dist/cron.d.ts
CHANGED
|
@@ -8,7 +8,8 @@ export interface CronJob {
|
|
|
8
8
|
*/
|
|
9
9
|
intervalSeconds: number;
|
|
10
10
|
/**
|
|
11
|
-
* HTTP method for the request.
|
|
11
|
+
* HTTP method for the request. A `POST` carries an empty JSON object (`{}`)
|
|
12
|
+
* as its body.
|
|
12
13
|
* @default 'POST'
|
|
13
14
|
*/
|
|
14
15
|
method?: 'GET' | 'POST';
|
|
@@ -88,17 +89,14 @@ export interface CronRunner {
|
|
|
88
89
|
* @throws TypeError if `onError` is not a function.
|
|
89
90
|
* @example
|
|
90
91
|
* ```typescript
|
|
91
|
-
* // src/lib/server/cron.ts
|
|
92
|
+
* // src/lib/server/cron.ts — both variables declared in src/env.ts, the
|
|
93
|
+
* // secret with a schema that rejects the empty string (see the README)
|
|
92
94
|
* import { createCronRunner } from '@urbicon-ui/sveltekit-utils/cron';
|
|
93
|
-
* import {
|
|
94
|
-
*
|
|
95
|
-
* // runtime env: `secret` is a `string`, so a missing one fails at startup
|
|
96
|
-
* const secret = env.CRON_SECRET;
|
|
97
|
-
* if (!secret) throw new Error('CRON_SECRET is not set');
|
|
95
|
+
* import { BASE_URL, CRON_SECRET } from '$app/env/private';
|
|
98
96
|
*
|
|
99
97
|
* export const cron = createCronRunner({
|
|
100
|
-
* secret,
|
|
101
|
-
* baseUrl:
|
|
98
|
+
* secret: CRON_SECRET,
|
|
99
|
+
* baseUrl: BASE_URL,
|
|
102
100
|
* jobs: [
|
|
103
101
|
* { path: '/api/cron/send-digest', intervalSeconds: 3600 },
|
|
104
102
|
* { path: '/api/cron/cleanup', intervalSeconds: 900 }
|
package/dist/cron.js
CHANGED
|
@@ -39,6 +39,21 @@ function warnDoubleStart(path, armed) {
|
|
|
39
39
|
function warnDuplicateJob(path) {
|
|
40
40
|
console.warn(`[createCronRunner] start() armed "${path}" twice from one jobs list; the endpoint gets every tick twice. Remove the duplicate entry.`);
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* The request a job fires. A server-side `fetch` sends no `Origin`, and
|
|
44
|
+
* SvelteKit's CSRF check refuses an Origin-less `POST` whose `Content-Type` is
|
|
45
|
+
* a form type or absent — in a built app, before any hook runs. A JSON body
|
|
46
|
+
* passes it.
|
|
47
|
+
*/
|
|
48
|
+
function cronRequestInit(job, config) {
|
|
49
|
+
const method = job.method ?? 'POST';
|
|
50
|
+
const headers = {
|
|
51
|
+
[config.secretHeader ?? 'x-cron-secret']: config.secret
|
|
52
|
+
};
|
|
53
|
+
if (method === 'GET')
|
|
54
|
+
return { method, headers };
|
|
55
|
+
return { method, headers: { ...headers, 'content-type': 'application/json' }, body: '{}' };
|
|
56
|
+
}
|
|
42
57
|
/**
|
|
43
58
|
* Create a background runner that fires HTTP requests at SvelteKit server
|
|
44
59
|
* endpoints on a fixed interval — a minimal in-process cron for scheduled work
|
|
@@ -59,17 +74,14 @@ function warnDuplicateJob(path) {
|
|
|
59
74
|
* @throws TypeError if `onError` is not a function.
|
|
60
75
|
* @example
|
|
61
76
|
* ```typescript
|
|
62
|
-
* // src/lib/server/cron.ts
|
|
77
|
+
* // src/lib/server/cron.ts — both variables declared in src/env.ts, the
|
|
78
|
+
* // secret with a schema that rejects the empty string (see the README)
|
|
63
79
|
* import { createCronRunner } from '@urbicon-ui/sveltekit-utils/cron';
|
|
64
|
-
* import {
|
|
65
|
-
*
|
|
66
|
-
* // runtime env: `secret` is a `string`, so a missing one fails at startup
|
|
67
|
-
* const secret = env.CRON_SECRET;
|
|
68
|
-
* if (!secret) throw new Error('CRON_SECRET is not set');
|
|
80
|
+
* import { BASE_URL, CRON_SECRET } from '$app/env/private';
|
|
69
81
|
*
|
|
70
82
|
* export const cron = createCronRunner({
|
|
71
|
-
* secret,
|
|
72
|
-
* baseUrl:
|
|
83
|
+
* secret: CRON_SECRET,
|
|
84
|
+
* baseUrl: BASE_URL,
|
|
73
85
|
* jobs: [
|
|
74
86
|
* { path: '/api/cron/send-digest', intervalSeconds: 3600 },
|
|
75
87
|
* { path: '/api/cron/cleanup', intervalSeconds: 900 }
|
|
@@ -132,10 +144,7 @@ export function createCronRunner(config) {
|
|
|
132
144
|
const base = config.baseUrl ?? 'http://localhost:3000';
|
|
133
145
|
let response;
|
|
134
146
|
try {
|
|
135
|
-
response = await fetch(`${base}${job.path}`,
|
|
136
|
-
method: job.method ?? 'POST',
|
|
137
|
-
headers: { [config.secretHeader ?? 'x-cron-secret']: config.secret }
|
|
138
|
-
});
|
|
147
|
+
response = await fetch(`${base}${job.path}`, cronRequestInit(job, config));
|
|
139
148
|
}
|
|
140
149
|
catch (err) {
|
|
141
150
|
// Network-level failure (DNS, connection refused, abort): fetch rejected.
|
package/dist/search-params.d.ts
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The read half of `URLSearchParams`. SvelteKit types `page.url.searchParams`
|
|
3
|
+
* without the mutators, so a reader that asked for `URLSearchParams` would
|
|
4
|
+
* refuse it; a real `URLSearchParams` is one of these too.
|
|
5
|
+
*/
|
|
6
|
+
export type ReadableSearchParams = Omit<URLSearchParams, 'append' | 'delete' | 'set' | 'sort'>;
|
|
7
|
+
/** What {@link withSearchParams} reads off a URL: a `URL`, or SvelteKit's read-only `page.url`. */
|
|
8
|
+
export type ReadableUrl = Pick<URL, 'origin' | 'pathname'> & {
|
|
9
|
+
readonly searchParams: ReadableSearchParams;
|
|
10
|
+
};
|
|
1
11
|
/**
|
|
2
12
|
* What {@link withSearchParams} applies to a URL: a plain record, or a
|
|
3
13
|
* `URLSearchParams` whose repeated keys survive the merge.
|
|
@@ -38,4 +48,4 @@ export type SearchParamsPatch = URLSearchParams | Record<string, string | string
|
|
|
38
48
|
* // '/films?sort=year'
|
|
39
49
|
* ```
|
|
40
50
|
*/
|
|
41
|
-
export declare function withSearchParams(url:
|
|
51
|
+
export declare function withSearchParams(url: ReadableUrl, patch: SearchParamsPatch): string;
|
package/dist/search-params.js
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
* ```
|
|
35
35
|
*/
|
|
36
36
|
export function withSearchParams(url, patch) {
|
|
37
|
-
const next = new URLSearchParams(url.searchParams);
|
|
37
|
+
const next = new URLSearchParams(url.searchParams.toString());
|
|
38
38
|
if (patch instanceof URLSearchParams) {
|
|
39
39
|
for (const [key] of patch)
|
|
40
40
|
next.delete(key);
|
package/dist/table-view.d.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* to `sort=`, so a cleared filter set elides like every other axis (the
|
|
15
15
|
* shipped read side already tolerated it).
|
|
16
16
|
*/
|
|
17
|
+
import type { ReadableSearchParams } from './search-params.js';
|
|
17
18
|
/**
|
|
18
19
|
* Filter operators supported by the table. Mirrors `FilterOperator` from
|
|
19
20
|
* `@urbicon-ui/table`. Used as the runtime whitelist when parsing `filter`
|
|
@@ -84,7 +85,7 @@ export interface TableViewLike {
|
|
|
84
85
|
/** The URL keys a set of axes owns, with the configured prefix applied. */
|
|
85
86
|
export declare function viewAxisKeys(axes: readonly TableViewAxis[], prefix?: string): string[];
|
|
86
87
|
/** The axes a URL names — presence only for params it actually carries. */
|
|
87
|
-
export declare function viewAxesNamedBy(sp:
|
|
88
|
+
export declare function viewAxesNamedBy(sp: ReadableSearchParams, prefix?: string): TableViewAxis[];
|
|
88
89
|
/**
|
|
89
90
|
* Parse search params into a **partial** view snapshot — a key per axis the
|
|
90
91
|
* URL actually carries, and nothing else. Read tolerant per key: an
|
|
@@ -92,7 +93,7 @@ export declare function viewAxesNamedBy(sp: URLSearchParams, prefix?: string): T
|
|
|
92
93
|
* for that axis (the key was present, so the axis stays claimed), and
|
|
93
94
|
* malformed filter entries are skipped individually.
|
|
94
95
|
*/
|
|
95
|
-
export declare function searchParamsToViewPartial(sp:
|
|
96
|
+
export declare function searchParamsToViewPartial(sp: ReadableSearchParams, defaults: Pick<TableViewSnapshot, 'page' | 'pageSize'>, prefix?: string): Partial<TableViewSnapshot>;
|
|
96
97
|
/**
|
|
97
98
|
* Resolve a full view snapshot from search params: every axis the URL names
|
|
98
99
|
* comes from the URL, every other one from `defaults` — the same resolution
|
|
@@ -122,7 +123,7 @@ export declare function searchParamsToViewPartial(sp: URLSearchParams, defaults:
|
|
|
122
123
|
* });
|
|
123
124
|
* ```
|
|
124
125
|
*/
|
|
125
|
-
export declare function searchParamsToViewSnapshot(sp:
|
|
126
|
+
export declare function searchParamsToViewSnapshot(sp: ReadableSearchParams, defaults?: Partial<TableViewSnapshot>, prefix?: string): TableViewSnapshot;
|
|
126
127
|
/**
|
|
127
128
|
* Serialize a snapshot, eliding every axis that equals the defaults — the
|
|
128
129
|
* elision baseline *is* the view's defaults, structurally. `axes` restricts
|
|
@@ -160,4 +161,4 @@ export declare function assertValidViewSnapshot(snapshot: TableViewSnapshot): vo
|
|
|
160
161
|
* @returns New `URLSearchParams` with the view applied.
|
|
161
162
|
* @throws TypeError when the snapshot is structurally invalid (write strict).
|
|
162
163
|
*/
|
|
163
|
-
export declare function applyViewToSearchParams(existing:
|
|
164
|
+
export declare function applyViewToSearchParams(existing: ReadableSearchParams, snapshot: TableViewSnapshot, defaults: TableViewSnapshot, axes?: readonly TableViewAxis[], prefix?: string): URLSearchParams;
|
package/dist/table-view.js
CHANGED
|
@@ -301,7 +301,7 @@ export function assertValidViewSnapshot(snapshot) {
|
|
|
301
301
|
export function applyViewToSearchParams(existing, snapshot, defaults, axes = TABLE_VIEW_AXES, prefix = '') {
|
|
302
302
|
assertValidViewSnapshot(snapshot);
|
|
303
303
|
const serialized = viewSnapshotToSearchParams(snapshot, defaults, axes, prefix);
|
|
304
|
-
const next = new URLSearchParams(existing);
|
|
304
|
+
const next = new URLSearchParams(existing.toString());
|
|
305
305
|
for (const key of viewAxisKeys(axes, prefix)) {
|
|
306
306
|
next.delete(key);
|
|
307
307
|
}
|
package/dist/url.svelte.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type SearchParamsPatch } from './search-params.js';
|
|
1
|
+
import { type ReadableSearchParams, type SearchParamsPatch } from './search-params.js';
|
|
2
2
|
export { type SearchParamsPatch, withSearchParams } from './search-params.js';
|
|
3
3
|
export { bindViewToUrl, type UrlViewBindingOptions } from './view-binding.svelte.js';
|
|
4
4
|
/**
|
|
@@ -13,7 +13,7 @@ export type UrlParamOptions<T> = {
|
|
|
13
13
|
* Read the value out of the current search params. Return `null`/`undefined`
|
|
14
14
|
* to signal "absent" — the getter then yields {@link initial}.
|
|
15
15
|
*/
|
|
16
|
-
parse: (sp:
|
|
16
|
+
parse: (sp: ReadableSearchParams) => T | null | undefined;
|
|
17
17
|
/**
|
|
18
18
|
* Encode the value into `URLSearchParams`. The keys it produces are the ones
|
|
19
19
|
* the setter manages: on write they are cleared from the current URL and
|
|
@@ -58,7 +58,7 @@ export declare function updateUrlSearchParams(next: SearchParamsPatch, opts?: {
|
|
|
58
58
|
*
|
|
59
59
|
* `set` rewrites only the keys that `options.serialize` produces (clear +
|
|
60
60
|
* re-append) and preserves the rest, then navigates with `goto` to the address
|
|
61
|
-
* {@link withSearchParams} builds (`replaceState`, `
|
|
61
|
+
* {@link withSearchParams} builds (`replace` per `replaceState`, `reset: false`).
|
|
62
62
|
*
|
|
63
63
|
* @param _key - Ignored — `options.parse`/`options.serialize` already close
|
|
64
64
|
* over the key (see {@link useUrlArrayParam}); kept only for signature parity
|
|
@@ -68,7 +68,7 @@ export declare function updateUrlSearchParams(next: SearchParamsPatch, opts?: {
|
|
|
68
68
|
* (falling back to `initial`), `set(value)` writes it to the URL.
|
|
69
69
|
*/
|
|
70
70
|
export declare function createUrlParam<T>(_key: string, options: UrlParamOptions<T>): {
|
|
71
|
-
readonly get: (sp:
|
|
71
|
+
readonly get: (sp: ReadableSearchParams) => T;
|
|
72
72
|
readonly set: (next: T) => void;
|
|
73
73
|
};
|
|
74
74
|
/**
|
package/dist/url.svelte.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { building } from '$app/
|
|
1
|
+
import { building } from '$app/env';
|
|
2
2
|
import { goto } from '$app/navigation';
|
|
3
3
|
import { page } from '$app/state';
|
|
4
4
|
import { withSearchParams } from './search-params.js';
|
|
@@ -43,9 +43,8 @@ export { bindViewToUrl } from './view-binding.svelte.js';
|
|
|
43
43
|
// free to call it at their composition point.
|
|
44
44
|
export function updateUrlSearchParams(next, opts) {
|
|
45
45
|
goto(withSearchParams(page.url, next), {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
keepFocus: true
|
|
46
|
+
replace: opts?.replaceState ?? true,
|
|
47
|
+
reset: false
|
|
49
48
|
});
|
|
50
49
|
}
|
|
51
50
|
/**
|
|
@@ -57,7 +56,7 @@ export function updateUrlSearchParams(next, opts) {
|
|
|
57
56
|
*
|
|
58
57
|
* `set` rewrites only the keys that `options.serialize` produces (clear +
|
|
59
58
|
* re-append) and preserves the rest, then navigates with `goto` to the address
|
|
60
|
-
* {@link withSearchParams} builds (`replaceState`, `
|
|
59
|
+
* {@link withSearchParams} builds (`replace` per `replaceState`, `reset: false`).
|
|
61
60
|
*
|
|
62
61
|
* @param _key - Ignored — `options.parse`/`options.serialize` already close
|
|
63
62
|
* over the key (see {@link useUrlArrayParam}); kept only for signature parity
|
|
@@ -72,9 +71,8 @@ export function createUrlParam(_key, options) {
|
|
|
72
71
|
const get = (sp) => options.parse(sp) ?? options.initial;
|
|
73
72
|
function setValue(next) {
|
|
74
73
|
goto(withSearchParams(page.url, options.serialize(next)), {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
keepFocus: true
|
|
74
|
+
replace: options.replaceState ?? true,
|
|
75
|
+
reset: false
|
|
78
76
|
});
|
|
79
77
|
}
|
|
80
78
|
return { get, set: setValue };
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* window of the spike review.
|
|
28
28
|
*/
|
|
29
29
|
import { untrack } from 'svelte';
|
|
30
|
-
import { browser, building } from '$app/
|
|
30
|
+
import { browser, building } from '$app/env';
|
|
31
31
|
import { goto } from '$app/navigation';
|
|
32
32
|
import { page } from '$app/state';
|
|
33
33
|
import { searchParamsToViewPartial, TABLE_VIEW_AXES, viewAxesNamedBy, viewAxisKeys, viewSnapshotToSearchParams } from './table-view.js';
|
|
@@ -131,9 +131,8 @@ const writer = {
|
|
|
131
131
|
this.sentPending.add(search);
|
|
132
132
|
this.intendedSearch = search;
|
|
133
133
|
void goto(`${page.url.pathname}${search}${page.url.hash}`, {
|
|
134
|
-
replaceState,
|
|
135
|
-
|
|
136
|
-
keepFocus: true
|
|
134
|
+
replace: replaceState,
|
|
135
|
+
reset: false
|
|
137
136
|
});
|
|
138
137
|
},
|
|
139
138
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@urbicon-ui/sveltekit-utils",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.28.0",
|
|
4
4
|
"description": "SvelteKit helper utilities — createCronRunner, streamSse, and URL-state runes",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
"sse"
|
|
20
20
|
],
|
|
21
21
|
"type": "module",
|
|
22
|
+
"imports": {
|
|
23
|
+
"#lib": "./src/lib/index.js",
|
|
24
|
+
"#lib/*": "./src/lib/*"
|
|
25
|
+
},
|
|
22
26
|
"private": false,
|
|
23
27
|
"sideEffects": false,
|
|
24
28
|
"svelte": "./dist/index.js",
|
|
@@ -78,20 +82,20 @@
|
|
|
78
82
|
"test": "vitest run"
|
|
79
83
|
},
|
|
80
84
|
"peerDependencies": {
|
|
81
|
-
"svelte": "^5.57.
|
|
82
|
-
"@sveltejs/kit": "^
|
|
85
|
+
"svelte": "^5.57.1",
|
|
86
|
+
"@sveltejs/kit": "^3.0.0"
|
|
83
87
|
},
|
|
84
88
|
"devDependencies": {
|
|
85
|
-
"@sveltejs/kit": "^
|
|
86
|
-
"@sveltejs/package": "^
|
|
87
|
-
"@sveltejs/vite-plugin-svelte": "^7.3.
|
|
89
|
+
"@sveltejs/kit": "^3.0.0",
|
|
90
|
+
"@sveltejs/package": "^3.0.0",
|
|
91
|
+
"@sveltejs/vite-plugin-svelte": "^7.3.1",
|
|
88
92
|
"prettier": "^3.9.6",
|
|
89
93
|
"prettier-plugin-svelte": "^4.1.1",
|
|
90
94
|
"prettier-plugin-tailwindcss": "^0.8.1",
|
|
91
|
-
"svelte": "^5.57.
|
|
95
|
+
"svelte": "^5.57.1",
|
|
92
96
|
"svelte-check": "^4.7.6",
|
|
93
97
|
"typescript": "^6.0.3",
|
|
94
98
|
"vite": "^8.2.2",
|
|
95
|
-
"vitest": "^
|
|
99
|
+
"vitest": "^5.0.3"
|
|
96
100
|
}
|
|
97
101
|
}
|