@urbicon-ui/sveltekit-utils 8.26.0 → 8.27.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 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.0), `@sveltejs/kit`.
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 `replaceState: true`, `noScroll: true`, `keepFocus: true` — suited for filter/pagination UIs, not full page transitions.
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 '$lib/server/users';
156
- import { userView } from '$lib/view-defaults';
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 { env } from '$env/dynamic/private';
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: env.BASE_URL,
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 { env } from '$env/dynamic/private';
218
- import { sendDigest } from '$lib/server/digest';
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 (!env.CRON_SECRET || request.headers.get('x-cron-secret') !== env.CRON_SECRET) {
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 { env } from '$env/dynamic/private';
255
- import { upsertDailyRollup } from '$lib/server/rollup';
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 (!env.CRON_SECRET || request.headers.get('x-cron-secret') !== env.CRON_SECRET) {
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 '$lib/server/model';
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 { env } from '$env/dynamic/private';
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: env.BASE_URL,
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 { env } from '$env/dynamic/private';
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: env.BASE_URL,
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.
@@ -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: URL, patch: SearchParamsPatch): string;
51
+ export declare function withSearchParams(url: ReadableUrl, patch: SearchParamsPatch): string;
@@ -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);
@@ -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: URLSearchParams, prefix?: string): TableViewAxis[];
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: URLSearchParams, defaults: Pick<TableViewSnapshot, 'page' | 'pageSize'>, prefix?: string): Partial<TableViewSnapshot>;
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: URLSearchParams, defaults?: Partial<TableViewSnapshot>, prefix?: string): TableViewSnapshot;
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: URLSearchParams, snapshot: TableViewSnapshot, defaults: TableViewSnapshot, axes?: readonly TableViewAxis[], prefix?: string): URLSearchParams;
164
+ export declare function applyViewToSearchParams(existing: ReadableSearchParams, snapshot: TableViewSnapshot, defaults: TableViewSnapshot, axes?: readonly TableViewAxis[], prefix?: string): URLSearchParams;
@@ -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
  }
@@ -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: URLSearchParams) => T | null | undefined;
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`, `noScroll`, `keepFocus`).
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: URLSearchParams) => T;
71
+ readonly get: (sp: ReadableSearchParams) => T;
72
72
  readonly set: (next: T) => void;
73
73
  };
74
74
  /**
@@ -1,4 +1,4 @@
1
- import { building } from '$app/environment';
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
- replaceState: opts?.replaceState ?? true,
47
- noScroll: true,
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`, `noScroll`, `keepFocus`).
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
- replaceState: options.replaceState ?? true,
76
- noScroll: true,
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/environment';
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
- noScroll: true,
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.26.0",
3
+ "version": "8.27.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.0",
82
- "@sveltejs/kit": "^2.70.3"
85
+ "svelte": "^5.57.1",
86
+ "@sveltejs/kit": "^3.0.0"
83
87
  },
84
88
  "devDependencies": {
85
- "@sveltejs/kit": "^2.70.3",
86
- "@sveltejs/package": "^2.5.8",
87
- "@sveltejs/vite-plugin-svelte": "^7.3.0",
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.0",
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": "^4.1.11"
99
+ "vitest": "^5.0.3"
96
100
  }
97
101
  }