@sourceregistry/sveltekit-enhance 1.1.2 → 1.2.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 +319 -69
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +1 -0
- package/dist/helpers/request-monitor.d.ts +26 -0
- package/dist/helpers/request-monitor.js +72 -0
- package/dist/index.d.ts +1 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -1,112 +1,362 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
1
3
|
# @sourceregistry/sveltekit-enhance
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
[](https://www.npmjs.com/package/@sourceregistry/sveltekit-enhance)
|
|
8
|
+
[](https://www.npmjs.com/package/@sourceregistry/sveltekit-enhance)
|
|
9
|
+
[](./LICENSE)
|
|
10
|
+
[](https://kit.svelte.dev)
|
|
11
|
+
[](https://github.com/SourceRegistry/sveltekit-enhance/issues)
|
|
6
12
|
|
|
7
|
-
|
|
8
|
-
Use `@sourceregistry/sveltekit-enhance` to build cleaner actions, loads, methods, and hooks with reusable guards for authentication, feature flags, request correlation, and form processing.
|
|
13
|
+
Wrap actions, loads, methods, and hooks with composable enhancers. Stack auth guards, feature flags, request tracing, and form parsing without touching SvelteKit's internals.
|
|
9
14
|
|
|
10
|
-
|
|
15
|
+
[Docs](https://sourceregistry.github.io/sveltekit-enhance/) · [npm](https://www.npmjs.com/package/@sourceregistry/sveltekit-enhance) · [Issues](https://github.com/SourceRegistry/sveltekit-enhance/issues)
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- Improve observability with correlation IDs on every response.
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
---
|
|
16
20
|
|
|
17
21
|
## Installation
|
|
18
22
|
|
|
19
|
-
```
|
|
23
|
+
```sh
|
|
20
24
|
npm install @sourceregistry/sveltekit-enhance
|
|
21
25
|
```
|
|
22
26
|
|
|
23
|
-
|
|
27
|
+
**Peer dependency:** `@sveltejs/kit ^2.58.0`
|
|
24
28
|
|
|
25
|
-
|
|
29
|
+
---
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
- `enhance.load(...)`
|
|
29
|
-
- `enhance.method(...)`
|
|
30
|
-
- `enhance.handle(...)`
|
|
31
|
+
## Overview
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
```ts
|
|
34
|
+
import { enhance, Auth, RequestCorrelation, RequestMonitor, Form } from '@sourceregistry/sveltekit-enhance';
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
// hooks.server.ts
|
|
37
|
+
export const handle = enhance.handle(
|
|
38
|
+
async ({ event, resolve }) => resolve(event),
|
|
39
|
+
RequestCorrelation.attach,
|
|
40
|
+
RequestMonitor.trace({ logger: myLogger, record: metrics.record }),
|
|
41
|
+
);
|
|
35
42
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
// +server.ts
|
|
44
|
+
export const POST = enhance.method(
|
|
45
|
+
async (event) => new Response(JSON.stringify(event.context)),
|
|
46
|
+
Auth.Bearer,
|
|
47
|
+
FeatureFlag.all('PUBLIC_API_ENABLED'),
|
|
48
|
+
);
|
|
40
49
|
|
|
50
|
+
// +page.server.ts
|
|
41
51
|
export const actions = {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
default: enhance.action(
|
|
53
|
+
async (event) => {
|
|
54
|
+
const name = event.context.form.string$('name');
|
|
55
|
+
return success({ name });
|
|
56
|
+
},
|
|
57
|
+
Form.schema(myValidator),
|
|
58
|
+
),
|
|
59
|
+
};
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Core API
|
|
65
|
+
|
|
66
|
+
Import from `@sourceregistry/sveltekit-enhance`.
|
|
67
|
+
|
|
68
|
+
### `enhance.handle`
|
|
69
|
+
|
|
70
|
+
Wraps SvelteKit's `handle` hook. Enhancers run left-to-right before the handler; their return values are merged into `context`.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { enhance } from '@sourceregistry/sveltekit-enhance';
|
|
74
|
+
|
|
75
|
+
// src/hooks.server.ts
|
|
76
|
+
export const handle = enhance.handle(
|
|
77
|
+
async ({ event, resolve, context }) => resolve(event),
|
|
78
|
+
enhancerA,
|
|
79
|
+
enhancerB,
|
|
80
|
+
);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### `enhance.load`
|
|
84
|
+
|
|
85
|
+
Wraps server `load` functions.
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
// +page.server.ts
|
|
89
|
+
export const load = enhance.load(
|
|
90
|
+
async (event) => ({ user: event.context.user }),
|
|
53
91
|
Auth.Bearer,
|
|
54
|
-
|
|
55
|
-
|
|
92
|
+
);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### `enhance.action`
|
|
96
|
+
|
|
97
|
+
Wraps form actions.
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
// +page.server.ts
|
|
101
|
+
export const actions = {
|
|
102
|
+
submit: enhance.action(
|
|
103
|
+
async (event) => success(event.context),
|
|
104
|
+
Auth.Bearer,
|
|
105
|
+
Form.schema(myValidator),
|
|
106
|
+
),
|
|
56
107
|
};
|
|
57
108
|
```
|
|
58
109
|
|
|
59
|
-
|
|
110
|
+
### `enhance.method`
|
|
111
|
+
|
|
112
|
+
Wraps `+server.ts` endpoint handlers.
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
// +server.ts
|
|
116
|
+
export const GET = enhance.method(
|
|
117
|
+
async (event) => new Response(JSON.stringify(event.context)),
|
|
118
|
+
Auth.Bearer,
|
|
119
|
+
);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Utilities
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import { fail, error, success, not_good } from '@sourceregistry/sveltekit-enhance';
|
|
126
|
+
|
|
127
|
+
fail(400, { message: 'bad input' }); // throws ActionFailure — use inside actions
|
|
128
|
+
error(404, { message: 'not found' }); // throws HttpError
|
|
129
|
+
success({ id: 1 }); // typed identity helper
|
|
130
|
+
not_good(input, 403); // delegates to fail or error based on callType
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Helpers
|
|
136
|
+
|
|
137
|
+
All helpers are available from `@sourceregistry/sveltekit-enhance` or `@sourceregistry/sveltekit-enhance/helpers`.
|
|
138
|
+
|
|
139
|
+
---
|
|
60
140
|
|
|
61
|
-
|
|
62
|
-
Validates `Authorization: Bearer <token>` and returns `{ token }`.
|
|
141
|
+
### `Auth`
|
|
63
142
|
|
|
64
|
-
|
|
65
|
-
Enforces public environment-based feature flags.
|
|
143
|
+
Extracts and validates `Authorization: Bearer <token>` headers.
|
|
66
144
|
|
|
67
|
-
|
|
68
|
-
|
|
145
|
+
```ts
|
|
146
|
+
import { Auth } from '@sourceregistry/sveltekit-enhance';
|
|
69
147
|
|
|
70
|
-
|
|
71
|
-
|
|
148
|
+
export const GET = enhance.method(
|
|
149
|
+
async (event) => new Response(event.context.token),
|
|
150
|
+
Auth.Bearer,
|
|
151
|
+
);
|
|
152
|
+
```
|
|
72
153
|
|
|
73
|
-
|
|
74
|
-
Typed helpers for strings, numbers, booleans, dates, files, arrays, JSON, selector helpers, and schema-style validation workflows.
|
|
154
|
+
Returns `{ token: string }`. Throws `401` if the header is missing or malformed.
|
|
75
155
|
|
|
76
|
-
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
### `Devtools`
|
|
159
|
+
|
|
160
|
+
Silences Chrome DevTools probe requests (`/.well-known/appspecific/com.chrome.devtools.json`) with a `204 No Content`. Logs in `dev` mode.
|
|
77
161
|
|
|
78
162
|
```ts
|
|
79
|
-
|
|
80
|
-
|
|
163
|
+
import { Devtools } from '@sourceregistry/sveltekit-enhance';
|
|
164
|
+
|
|
165
|
+
export const handle = enhance.handle(myHandler, Devtools.ignore);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
### `FeatureFlag`
|
|
171
|
+
|
|
172
|
+
Guards routes behind SvelteKit public env vars (`$env/dynamic/public`). Always passes in `dev` mode.
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
import { FeatureFlag } from '@sourceregistry/sveltekit-enhance';
|
|
176
|
+
|
|
177
|
+
// All listed flags must be enabled
|
|
178
|
+
FeatureFlag.all('PUBLIC_FEATURE_A', 'PUBLIC_FEATURE_B')
|
|
179
|
+
|
|
180
|
+
// At least one flag must be enabled
|
|
181
|
+
FeatureFlag.oneOf('PUBLIC_FEATURE_A', 'PUBLIC_FEATURE_B')
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Truthy values: `true`, `TRUE`, `on`, `ON`, `1`. Returns `{ flags }` or throws `503 Feature not enabled`.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
### `RequestCorrelation`
|
|
189
|
+
|
|
190
|
+
Propagates a correlation ID across the request/response cycle.
|
|
191
|
+
|
|
192
|
+
- Reads `x-correlation-id` or `x-request-id` from incoming headers
|
|
193
|
+
- Validates: max 128 chars, pattern `[A-Za-z0-9._:-]+`
|
|
194
|
+
- Generates a UUID v4 if absent
|
|
195
|
+
- Echoes the ID back via `x-correlation-id` response header
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import { RequestCorrelation } from '@sourceregistry/sveltekit-enhance';
|
|
199
|
+
|
|
200
|
+
export const handle = enhance.handle(myHandler, RequestCorrelation.attach);
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
| Local | Type | Description |
|
|
204
|
+
|-------|------|-------------|
|
|
205
|
+
| `correlation_id` | `string` | Resolved correlation ID |
|
|
206
|
+
| `request_started_at` | `number` | `Date.now()` at attach time |
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
### `RequestMonitor`
|
|
211
|
+
|
|
212
|
+
Structured HTTP request logging and optional metrics collection. Instruments the full lifecycle: start, completion (log level by status), and unhandled errors — all with elapsed duration.
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
import { RequestMonitor } from '@sourceregistry/sveltekit-enhance';
|
|
81
216
|
|
|
82
217
|
export const handle = enhance.handle(
|
|
83
|
-
|
|
84
|
-
|
|
218
|
+
myHandler,
|
|
219
|
+
RequestCorrelation.attach,
|
|
220
|
+
RequestMonitor.trace({
|
|
221
|
+
logger: myLogger, // optional, defaults to console
|
|
222
|
+
record: metrics.record, // optional
|
|
223
|
+
}),
|
|
85
224
|
);
|
|
86
225
|
```
|
|
87
226
|
|
|
88
|
-
|
|
227
|
+
#### `TraceOptions`
|
|
228
|
+
|
|
229
|
+
| Option | Type | Default | Description |
|
|
230
|
+
|--------|------|---------|-------------|
|
|
231
|
+
| `logger` | `TraceLogger` | `console` | Must implement `debug`, `info`, `warn`, `error` |
|
|
232
|
+
| `record` | `(entry: RecordTraceMetricEntry) => any` | — | Called after every request |
|
|
233
|
+
|
|
234
|
+
#### Log events
|
|
235
|
+
|
|
236
|
+
| Event | Level | Condition |
|
|
237
|
+
|-------|-------|-----------|
|
|
238
|
+
| `http.request.started` | `debug` | Before resolve |
|
|
239
|
+
| `http.request.completed` | `info` | `status < 400` |
|
|
240
|
+
| `http.request.completed` | `warn` | `status 4xx` |
|
|
241
|
+
| `http.request.completed` | `error` | `status 5xx` |
|
|
242
|
+
| `http.request.failed` | `error` | Unhandled throw |
|
|
243
|
+
|
|
244
|
+
#### Types
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
type RecordTraceMetricEntry = { method: string; path: string; status: number; durationMs: number }
|
|
248
|
+
type TraceLogger = { debug(...args: any[]): any; info(...args: any[]): any; warn(...args: any[]): any; error(...args: any[]): any }
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Locals set: `trace: { id: string; started_at: bigint }`.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
### `Form`
|
|
256
|
+
|
|
257
|
+
Typed, ergonomic FormData extraction. Works standalone or as an enhancer.
|
|
258
|
+
|
|
259
|
+
#### As an enhancer — `Form.schema`
|
|
260
|
+
|
|
261
|
+
Validates and deserializes form data via a `Validator<T>` before the handler runs.
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
import { Form, enhance, success } from '@sourceregistry/sveltekit-enhance';
|
|
265
|
+
|
|
266
|
+
export const actions = {
|
|
267
|
+
default: enhance.action(
|
|
268
|
+
async (event) => success(event.context.form.result),
|
|
269
|
+
Form.schema(myValidator),
|
|
270
|
+
),
|
|
271
|
+
};
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
#### Standalone — `Form.handle`
|
|
275
|
+
|
|
276
|
+
```ts
|
|
277
|
+
import { Form } from '@sourceregistry/sveltekit-enhance';
|
|
278
|
+
|
|
279
|
+
await Form.handle(request, ({ form }) => {
|
|
280
|
+
const name = form.string$('name');
|
|
281
|
+
const age = form.number('age');
|
|
282
|
+
return { name, age };
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
#### Field extractors
|
|
287
|
+
|
|
288
|
+
Optional variants return `undefined` when the field is absent. Required variants (`$` suffix) throw `fail(400)`.
|
|
289
|
+
|
|
290
|
+
| Method | Returns | Notes |
|
|
291
|
+
|--------|---------|-------|
|
|
292
|
+
| `string(name)` / `string$(name)` | `string \| null \| undefined` | |
|
|
293
|
+
| `pattern$(name, pattern)` | `string` | `RegExp` or pattern string |
|
|
294
|
+
| `number(name)` / `number$(name)` | `number \| undefined` | |
|
|
295
|
+
| `boolean(name)` / `boolean$(name)` | `boolean \| undefined` | Accepts `true/false`, `1/0`, `on/off` |
|
|
296
|
+
| `date(name, parser?)` / `date$(name, parser)` | `Date \| undefined` | Custom parser supported |
|
|
297
|
+
| `json<T>(name, transformer?)` / `json$(name)` | `T \| undefined` | Optional transform fn |
|
|
298
|
+
| `jsond(options)` | `any` | All FormData → nested object via dot-notation keys |
|
|
299
|
+
| `file(name)` / `file$(name)` | `File \| null \| undefined` | |
|
|
300
|
+
| `files(name)` | `File[]` | Non-empty, named files only |
|
|
301
|
+
| `fileRecord(prefix, removePrefix?)` | `Record<string, File[]>` | Groups files by key prefix |
|
|
302
|
+
| `array<T>(name, mapper?)` / `array$(name)` | `T[] \| undefined` | |
|
|
303
|
+
| `enum(name, Enum)` / `enum$(name, Enum)` | `keyof E \| undefined` | |
|
|
304
|
+
| `record(options?)` | `Record<string, any>` | All entries; optional `filter` / `transformer` |
|
|
305
|
+
| `validate(schema, options?)` | `T` | Runs `Validator<T>`, throws `fail(400)` on failure |
|
|
306
|
+
|
|
307
|
+
#### Conditional helpers
|
|
89
308
|
|
|
90
309
|
```ts
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
FeatureFlag,
|
|
99
|
-
RequestCorrelation,
|
|
100
|
-
Devtools,
|
|
101
|
-
Form
|
|
102
|
-
} from '@sourceregistry/sveltekit-enhance';
|
|
310
|
+
form.onlyIf(condition, trueVal, falseVal?)
|
|
311
|
+
form.onlyIfPresent(key, (entry) => ..., fallback?)
|
|
312
|
+
form.onlyIfArrayPresent(key, (entries) => ..., fallback)
|
|
313
|
+
form.selector({ fieldName: (entry, key) => ..., $default?, $error? })
|
|
314
|
+
form.selector$({ ... }) // throws fail(400) if no case matches
|
|
315
|
+
form.basedOn(val, processor)
|
|
316
|
+
form.process(name, parser, processor)
|
|
103
317
|
```
|
|
104
318
|
|
|
105
|
-
|
|
319
|
+
#### Standalone functions
|
|
320
|
+
|
|
321
|
+
All `FormContext` methods are also exported as standalone functions taking `FormData` as the first argument, plus:
|
|
322
|
+
|
|
323
|
+
```ts
|
|
324
|
+
arrayString(formdata, name, delimiter, mapper?)
|
|
325
|
+
hasOneOf(formdata, names)
|
|
326
|
+
reviver(key, value) // JSON.parse reviver — coerces strings to typed primitives
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Type Reference
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
// Core
|
|
335
|
+
EnhanceInput<CallType> // event passed to all enhancers
|
|
336
|
+
EnhanceFunction<CallType> // (event: EnhanceInput) => MaybePromise<object | Response>
|
|
337
|
+
EnhanceHandle // final handle fn — ({ event, resolve, context }) => MaybePromise<Response>
|
|
338
|
+
EnhanceLoad // final load fn
|
|
339
|
+
EnhanceAction // final action fn
|
|
340
|
+
EnhanceMethod // final method fn
|
|
341
|
+
MaybePromise<T> // T | Promise<T>
|
|
342
|
+
|
|
343
|
+
// Form
|
|
344
|
+
Validator<T> // (value: unknown, path?: ValidationPath) => ValidationResult<T>
|
|
345
|
+
ValidationResult<T> // { success: true; data: T } | { success: false; errors: ValidationIssue[] }
|
|
346
|
+
ValidationIssue // { path: string; message: string; code?: string }
|
|
347
|
+
FormContext // fluent API returned by Form.enhance / Form.handle
|
|
348
|
+
InferValidator<V> // infers T from Validator<T>
|
|
349
|
+
|
|
350
|
+
// Helpers
|
|
351
|
+
TraceLogger // { debug, info, warn, error }
|
|
352
|
+
TraceOptions // { logger?: TraceLogger; record?: (entry) => any }
|
|
353
|
+
RecordTraceMetricEntry // { method: string; path: string; status: number; durationMs: number }
|
|
354
|
+
RequestTraceLocals // { trace?: { id: string; started_at: bigint } }
|
|
355
|
+
RequestCorrelationLocals // { correlation_id?: string; request_started_at?: number }
|
|
356
|
+
```
|
|
106
357
|
|
|
107
|
-
|
|
108
|
-
- Node.js runtime (matching your SvelteKit adapter/runtime support)
|
|
358
|
+
---
|
|
109
359
|
|
|
110
360
|
## License
|
|
111
361
|
|
|
112
|
-
Apache-2.0
|
|
362
|
+
[Apache-2.0](./LICENSE) © [A.P.A. Slaa](https://github.com/SourceRegistry)
|
package/dist/helpers/index.d.ts
CHANGED
package/dist/helpers/index.js
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { EnhanceFunction } from "../index.js";
|
|
2
|
+
export type RequestTraceLocals = {
|
|
3
|
+
trace?: {
|
|
4
|
+
id: string;
|
|
5
|
+
started_at: bigint;
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
export type RecordTraceMetricEntry = {
|
|
9
|
+
method: string;
|
|
10
|
+
path: string;
|
|
11
|
+
status: number;
|
|
12
|
+
durationMs: number;
|
|
13
|
+
};
|
|
14
|
+
export type TraceLogger = {
|
|
15
|
+
debug: (...args: any[]) => any;
|
|
16
|
+
info: (...args: any[]) => any;
|
|
17
|
+
warn: (...args: any[]) => any;
|
|
18
|
+
error: (...args: any[]) => any;
|
|
19
|
+
};
|
|
20
|
+
export type TraceOptions = {
|
|
21
|
+
logger?: TraceLogger;
|
|
22
|
+
record?: (entry: RecordTraceMetricEntry) => any;
|
|
23
|
+
};
|
|
24
|
+
export declare const RequestMonitor: {
|
|
25
|
+
trace: (options?: TraceOptions) => EnhanceFunction<"handle">;
|
|
26
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const durationMs = (startedAt) => Number(process.hrtime.bigint() - startedAt) / 1_000_000;
|
|
2
|
+
const resolveRoute = (event) => event.route.id ?? event.url.pathname;
|
|
3
|
+
export const RequestMonitor = {
|
|
4
|
+
trace: (options = {}) => {
|
|
5
|
+
const { logger = console, record, } = options;
|
|
6
|
+
return async (event) => {
|
|
7
|
+
const locals = event.locals;
|
|
8
|
+
const requestId = locals.requestId ?? "unknown";
|
|
9
|
+
const route = resolveRoute(event);
|
|
10
|
+
locals.trace = {
|
|
11
|
+
id: requestId,
|
|
12
|
+
started_at: process.hrtime.bigint()
|
|
13
|
+
};
|
|
14
|
+
logger.debug("http.request.started", {
|
|
15
|
+
request_id: requestId,
|
|
16
|
+
method: event.request.method,
|
|
17
|
+
route,
|
|
18
|
+
client_ip: event?.getClientAddress?.()
|
|
19
|
+
});
|
|
20
|
+
try {
|
|
21
|
+
const response = await event.resolve(event.event);
|
|
22
|
+
if (!response)
|
|
23
|
+
return response;
|
|
24
|
+
const elapsedMs = Number(durationMs(locals.trace.started_at).toFixed(2));
|
|
25
|
+
record?.({
|
|
26
|
+
method: event.request.method,
|
|
27
|
+
path: route,
|
|
28
|
+
status: response.status,
|
|
29
|
+
durationMs: elapsedMs,
|
|
30
|
+
});
|
|
31
|
+
const context = {
|
|
32
|
+
request_id: requestId,
|
|
33
|
+
method: event.request.method,
|
|
34
|
+
route,
|
|
35
|
+
status: response.status,
|
|
36
|
+
duration_ms: elapsedMs,
|
|
37
|
+
client_ip: event?.getClientAddress?.()
|
|
38
|
+
};
|
|
39
|
+
if (response.status >= 500) {
|
|
40
|
+
logger.error("http.request.completed", context);
|
|
41
|
+
return response;
|
|
42
|
+
}
|
|
43
|
+
if (response.status >= 400) {
|
|
44
|
+
logger.warn("http.request.completed", context);
|
|
45
|
+
return response;
|
|
46
|
+
}
|
|
47
|
+
logger.info("http.request.completed", context);
|
|
48
|
+
return response;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
const elapsedMs = Number(durationMs(locals.trace.started_at).toFixed(2));
|
|
52
|
+
record?.({
|
|
53
|
+
method: event.request.method,
|
|
54
|
+
path: route,
|
|
55
|
+
status: 500,
|
|
56
|
+
durationMs: elapsedMs,
|
|
57
|
+
});
|
|
58
|
+
logger.error("http.request.failed", {
|
|
59
|
+
request_id: requestId,
|
|
60
|
+
method: event.request.method,
|
|
61
|
+
route,
|
|
62
|
+
duration_ms: elapsedMs,
|
|
63
|
+
client_ip: event?.getClientAddress?.(),
|
|
64
|
+
error: error instanceof Error
|
|
65
|
+
? { name: error.name, message: error.message }
|
|
66
|
+
: { value: String(error) }
|
|
67
|
+
});
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export type EnhanceInput<CallType extends EnhanceCallType = EnhanceCallType, Par
|
|
|
18
18
|
request: Request;
|
|
19
19
|
callType: CallType;
|
|
20
20
|
fetch: typeof fetch;
|
|
21
|
+
getClientAddress?: () => string;
|
|
21
22
|
get errorHandlers(): EnhanceErrorHandler[];
|
|
22
23
|
} & (CallType extends 'handle' ? {
|
|
23
24
|
get responseHandlers(): EnhanceResponseHandler[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sourceregistry/sveltekit-enhance",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Composable enhance and form utilities for SvelteKit actions, loads, methods, and hooks.",
|
|
5
5
|
"author": "A.P.A. Slaa (a.p.a.slaa@projectsource.nl)",
|
|
6
6
|
"scripts": {
|
|
@@ -31,6 +31,10 @@
|
|
|
31
31
|
"types": "./dist/index.d.ts",
|
|
32
32
|
"svelte": "./dist/index.js",
|
|
33
33
|
"default": "./dist/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./helpers": {
|
|
36
|
+
"types": "./dist/helpers/index.d.ts",
|
|
37
|
+
"default": "./dist/helpers/index.js"
|
|
34
38
|
}
|
|
35
39
|
},
|
|
36
40
|
"repository": {
|