@urbicon-ui/sveltekit-utils 8.8.0 → 8.10.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 +30 -9
- package/dist/cron.d.ts +5 -1
- package/dist/cron.js +5 -1
- package/dist/table-view.d.ts +1 -1
- package/dist/table-view.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,6 +46,7 @@ Bind a typed, reactive value to a URL search param. When the value changes, the
|
|
|
46
46
|
|
|
47
47
|
Low-level escape hatch if you prefer to update multiple params at once:
|
|
48
48
|
|
|
49
|
+
<!-- typecheck -->
|
|
49
50
|
```typescript
|
|
50
51
|
import { updateUrlSearchParams } from '@urbicon-ui/sveltekit-utils/url.svelte';
|
|
51
52
|
|
|
@@ -90,14 +91,20 @@ Because the binding re-reads the URL rather than capturing it, the browser's bac
|
|
|
90
91
|
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.
|
|
91
92
|
|
|
92
93
|
```typescript
|
|
93
|
-
// view-defaults.ts — imported by
|
|
94
|
-
|
|
94
|
+
// src/lib/view-defaults.ts — imported by the component and by the load function.
|
|
95
|
+
// `as const` keeps `direction` a `'desc'`, not a `string` the snapshot rejects.
|
|
96
|
+
export const userView = { pageSize: 25, sort: { column: 'joined', direction: 'desc' } } as const;
|
|
97
|
+
```
|
|
95
98
|
|
|
96
|
-
|
|
99
|
+
<!-- typecheck -->
|
|
100
|
+
```typescript
|
|
101
|
+
// src/routes/users/+page.server.ts
|
|
97
102
|
import { searchParamsToViewSnapshot } from '@urbicon-ui/sveltekit-utils/table-view';
|
|
98
|
-
import {
|
|
103
|
+
import { fetchUsers } from '$lib/server/users';
|
|
104
|
+
import { userView } from '$lib/view-defaults';
|
|
105
|
+
import type { PageServerLoad } from './$types';
|
|
99
106
|
|
|
100
|
-
export const load = async ({ url }) => ({
|
|
107
|
+
export const load: PageServerLoad = async ({ url }) => ({
|
|
101
108
|
initialResult: await fetchUsers(searchParamsToViewSnapshot(url.searchParams, userView))
|
|
102
109
|
});
|
|
103
110
|
```
|
|
@@ -116,13 +123,20 @@ The `./table-query` subpath that used to hold a second copy of this codec — sa
|
|
|
116
123
|
|
|
117
124
|
Fire HTTP requests against SvelteKit server endpoints on an interval. Pair with a shared-secret header so endpoints can authenticate scheduled calls.
|
|
118
125
|
|
|
126
|
+
<!-- typecheck -->
|
|
119
127
|
```typescript
|
|
120
128
|
// src/lib/server/cron.ts
|
|
121
129
|
import { createCronRunner } from '@urbicon-ui/sveltekit-utils/cron';
|
|
122
130
|
import { env } from '$env/dynamic/private';
|
|
123
131
|
|
|
132
|
+
// Runtime env, not `$env/static/private`: a node server reads its secret at
|
|
133
|
+
// start, not at build. The runner needs a `string`, so a missing one is a
|
|
134
|
+
// startup failure here — not an unauthenticated cron loop.
|
|
135
|
+
const secret = env.CRON_SECRET;
|
|
136
|
+
if (!secret) throw new Error('CRON_SECRET is not set');
|
|
137
|
+
|
|
124
138
|
export const cron = createCronRunner({
|
|
125
|
-
secret
|
|
139
|
+
secret,
|
|
126
140
|
baseUrl: env.BASE_URL,
|
|
127
141
|
jobs: [
|
|
128
142
|
{ path: '/api/cron/send-digest', intervalSeconds: 3600 },
|
|
@@ -136,12 +150,15 @@ cron.start();
|
|
|
136
150
|
|
|
137
151
|
Receive the call and verify the secret inside your endpoint:
|
|
138
152
|
|
|
153
|
+
<!-- typecheck -->
|
|
139
154
|
```typescript
|
|
140
155
|
// src/routes/api/cron/send-digest/+server.ts
|
|
141
156
|
import { env } from '$env/dynamic/private';
|
|
157
|
+
import { sendDigest } from '$lib/server/digest';
|
|
158
|
+
import type { RequestHandler } from './$types';
|
|
142
159
|
|
|
143
|
-
export const POST = async ({ request }) => {
|
|
144
|
-
if (request.headers.get('x-cron-secret') !== env.CRON_SECRET) {
|
|
160
|
+
export const POST: RequestHandler = async ({ request }) => {
|
|
161
|
+
if (!env.CRON_SECRET || request.headers.get('x-cron-secret') !== env.CRON_SECRET) {
|
|
145
162
|
return new Response('Forbidden', { status: 403 });
|
|
146
163
|
}
|
|
147
164
|
await sendDigest();
|
|
@@ -182,9 +199,13 @@ controller.abort();
|
|
|
182
199
|
|
|
183
200
|
Emit the matching frames from the endpoint:
|
|
184
201
|
|
|
202
|
+
<!-- typecheck -->
|
|
185
203
|
```typescript
|
|
186
204
|
// src/routes/api/chat/+server.ts
|
|
187
|
-
|
|
205
|
+
import { runModel } from '$lib/server/model';
|
|
206
|
+
import type { RequestHandler } from './$types';
|
|
207
|
+
|
|
208
|
+
export const POST: RequestHandler = async ({ request }) => {
|
|
188
209
|
const stream = new ReadableStream({
|
|
189
210
|
async start(controller) {
|
|
190
211
|
const enc = new TextEncoder();
|
package/dist/cron.d.ts
CHANGED
|
@@ -80,8 +80,12 @@ export interface CronRunner {
|
|
|
80
80
|
* import { createCronRunner } from '@urbicon-ui/sveltekit-utils/cron';
|
|
81
81
|
* import { env } from '$env/dynamic/private';
|
|
82
82
|
*
|
|
83
|
+
* // runtime env: `secret` is a `string`, so a missing one fails at startup
|
|
84
|
+
* const secret = env.CRON_SECRET;
|
|
85
|
+
* if (!secret) throw new Error('CRON_SECRET is not set');
|
|
86
|
+
*
|
|
83
87
|
* export const cron = createCronRunner({
|
|
84
|
-
* secret
|
|
88
|
+
* secret,
|
|
85
89
|
* baseUrl: env.BASE_URL,
|
|
86
90
|
* jobs: [
|
|
87
91
|
* { path: '/api/cron/send-digest', intervalSeconds: 3600 },
|
package/dist/cron.js
CHANGED
|
@@ -16,8 +16,12 @@
|
|
|
16
16
|
* import { createCronRunner } from '@urbicon-ui/sveltekit-utils/cron';
|
|
17
17
|
* import { env } from '$env/dynamic/private';
|
|
18
18
|
*
|
|
19
|
+
* // runtime env: `secret` is a `string`, so a missing one fails at startup
|
|
20
|
+
* const secret = env.CRON_SECRET;
|
|
21
|
+
* if (!secret) throw new Error('CRON_SECRET is not set');
|
|
22
|
+
*
|
|
19
23
|
* export const cron = createCronRunner({
|
|
20
|
-
* secret
|
|
24
|
+
* secret,
|
|
21
25
|
* baseUrl: env.BASE_URL,
|
|
22
26
|
* jobs: [
|
|
23
27
|
* { path: '/api/cron/send-digest', intervalSeconds: 3600 },
|
package/dist/table-view.d.ts
CHANGED
|
@@ -115,7 +115,7 @@ export declare function searchParamsToViewPartial(sp: URLSearchParams, defaults:
|
|
|
115
115
|
* @example
|
|
116
116
|
* ```ts
|
|
117
117
|
* // shared with the component that calls createTableView({ defaults })
|
|
118
|
-
* export const invoiceView = { pageSize: 25, sort: { column: 'date', direction: 'desc' } };
|
|
118
|
+
* export const invoiceView = { pageSize: 25, sort: { column: 'date', direction: 'desc' } } as const;
|
|
119
119
|
*
|
|
120
120
|
* export const load = async ({ url }) => ({
|
|
121
121
|
* initialResult: await fetchInvoices(searchParamsToViewSnapshot(url.searchParams, invoiceView))
|
package/dist/table-view.js
CHANGED
|
@@ -155,7 +155,7 @@ export function searchParamsToViewPartial(sp, defaults, prefix = '') {
|
|
|
155
155
|
* @example
|
|
156
156
|
* ```ts
|
|
157
157
|
* // shared with the component that calls createTableView({ defaults })
|
|
158
|
-
* export const invoiceView = { pageSize: 25, sort: { column: 'date', direction: 'desc' } };
|
|
158
|
+
* export const invoiceView = { pageSize: 25, sort: { column: 'date', direction: 'desc' } } as const;
|
|
159
159
|
*
|
|
160
160
|
* export const load = async ({ url }) => ({
|
|
161
161
|
* initialResult: await fetchInvoices(searchParamsToViewSnapshot(url.searchParams, invoiceView))
|