@sourceregistry/sveltekit-enhance 1.0.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.
@@ -0,0 +1,530 @@
1
+ import { fail } from "../index.js";
2
+ const reviveMap = new Map([
3
+ ['true', () => true],
4
+ ['false', () => false],
5
+ ['null', () => null],
6
+ ['undefined', () => undefined],
7
+ ['Infinity', () => Infinity],
8
+ ['-Infinity', () => -Infinity],
9
+ ['NaN', () => NaN],
10
+ [/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/, (v) => Number(v)],
11
+ [/^\s*\{[\s\S]*}\s*$/, (v) => JSON.parse(v, reviver)],
12
+ [/^\s*\[[\s\S]*]\s*$/, (v) => JSON.parse(v, reviver)]
13
+ ]);
14
+ export const reviver = (key, value) => {
15
+ if (typeof value !== 'string')
16
+ return value;
17
+ for (const [pattern, converter] of reviveMap) {
18
+ if ((typeof pattern === 'string' && pattern === value) ||
19
+ (pattern instanceof RegExp && pattern.test(value))) {
20
+ try {
21
+ return converter(value);
22
+ }
23
+ catch {
24
+ return value;
25
+ }
26
+ }
27
+ }
28
+ return value;
29
+ };
30
+ const createContext = (data) => {
31
+ return {
32
+ get data() {
33
+ return data;
34
+ },
35
+ has: (...names) => {
36
+ if (names.length === 0)
37
+ return false;
38
+ if (names.length === 1)
39
+ return data.has(names[0]);
40
+ else
41
+ return hasOneOf(data, names);
42
+ },
43
+ hasOneOf: (...name) => hasOneOf(data, name),
44
+ string: (name) => string(data, name) ?? undefined,
45
+ _string: (name) => string(data, name),
46
+ string$: (name) => string$(data, name),
47
+ pattern$: (name, pattern) => pattern$(data, name, pattern),
48
+ enum: (name, _enum) => Enum(data, name, _enum),
49
+ enum$: (name, _enum) => Enum$(data, name, _enum),
50
+ date: (name, parser = (data, name) => data.get(name)) => date(data, name, parser),
51
+ date$: (name, parser = (data, name) => data.get(name)) => date$(data, name, parser),
52
+ process: (name, parser, processor) => process(data, name, (formdata, name) => parser.length === 2
53
+ ? parser(formdata, name)
54
+ : parser(name), processor),
55
+ number: (name) => number(data, name),
56
+ number$: (name) => number$(data, name),
57
+ boolean: (name) => boolean(data, name),
58
+ boolean$: (name) => boolean$(data, name),
59
+ json: (name, transformer = (val) => val) => json(data, name, transformer),
60
+ jsond: (options) => jsond(data, options),
61
+ json$: (name, transformer = (val) => val) => json$(data, name, transformer),
62
+ file: (name) => file(data, name),
63
+ file$: (name) => file$(data, name),
64
+ files: (name) => files(data, name),
65
+ fileRecord: (prefix, removePrefix = false) => fileRecord(data, prefix, removePrefix),
66
+ array: (name, mapper) => array(data, name, mapper),
67
+ array$: (name, mapper) => array$(data, name, mapper),
68
+ onlyIf: (condition, TRUE, FALSE = undefined) => onlyIf(condition, TRUE, FALSE),
69
+ onlyIfPresent: (key, TRUE, FALSE = undefined) => onlyIfPresent(data, key, TRUE, FALSE),
70
+ onlyIfArrayPresent: (key, TRUE, FALSE) => onlyIfArrayPresent(data, key, TRUE, FALSE),
71
+ selector: (cases, useArray = false) => selector(data, cases, useArray),
72
+ selector$: (cases, useArray = false) => selector$(data, cases, useArray),
73
+ basedOn: (val, processor = (val) => val) => basedOn(val, processor),
74
+ record: (options) => record(data, options),
75
+ validate: (schema, options = {
76
+ unpack_prefixed: true,
77
+ transform: (value) => value
78
+ }) => validate(data, schema, options)
79
+ };
80
+ };
81
+ export function hasOneOf(formdata, names) {
82
+ return names.some((name) => formdata.has(name));
83
+ }
84
+ export function string(formdata, name) {
85
+ if (!formdata.has(name)) {
86
+ return undefined;
87
+ }
88
+ const data = formdata.get(name);
89
+ if (data === 'null' || data === null)
90
+ return null;
91
+ if (typeof data != 'string') {
92
+ fail(400, { targets: [name], message: `${name} isn't of type string` });
93
+ }
94
+ return data;
95
+ }
96
+ export function string$(formdata, name) {
97
+ if (!formdata.has(name)) {
98
+ fail(400, { targets: [name], message: `${name} is required` });
99
+ }
100
+ const data = formdata.get(name);
101
+ if (typeof data != 'string') {
102
+ fail(400, { targets: [name], message: `${name} is not correct data type` });
103
+ }
104
+ return data;
105
+ }
106
+ /**
107
+ * Reads a required string and validates it against a regex pattern.
108
+ * Accepts either a prebuilt `RegExp` or a pattern string.
109
+ * Throws a SvelteKit `fail(400)` callback when required checks fail.
110
+ */
111
+ export function pattern$(formdata, name, pattern) {
112
+ if (!formdata.has(name))
113
+ fail(400, { targets: [name], message: `${name} is required` });
114
+ const data = formdata.get(name);
115
+ if (!data || typeof data != 'string')
116
+ return fail(400, { targets: [name], message: `${name} is not correct data type` });
117
+ if (typeof pattern === 'string')
118
+ pattern = new RegExp(pattern);
119
+ if (!pattern.test(data.toString()))
120
+ fail(400, { targets: [name], message: `${name} is not in correct format` });
121
+ return data;
122
+ }
123
+ export function number(formdata, name) {
124
+ if (!formdata.has(name)) {
125
+ return undefined;
126
+ }
127
+ const num = Number(formdata.get(name));
128
+ if (isNaN(num)) {
129
+ fail(400, { targets: [name], message: `${name} isn't of type number` });
130
+ }
131
+ return num;
132
+ }
133
+ export function number$(formdata, name) {
134
+ const _number = number(formdata, name);
135
+ if (_number === undefined) {
136
+ fail(400, { targets: [name], message: `${name} is required` });
137
+ }
138
+ return _number;
139
+ }
140
+ export function boolean(formdata, name) {
141
+ if (!formdata.has(name)) {
142
+ return undefined;
143
+ }
144
+ const data = formdata.get(name);
145
+ const num = Number(data);
146
+ if (!isNaN(num)) {
147
+ return num > 0;
148
+ }
149
+ else {
150
+ return data.trim().toLowerCase().startsWith('t') || data.trim().toLowerCase() === 'on';
151
+ }
152
+ }
153
+ export function boolean$(formdata, name) {
154
+ if (!formdata.has(name)) {
155
+ fail(400, { targets: [name], message: `${name} is required` });
156
+ }
157
+ const data = formdata.get(name);
158
+ const num = Number(data);
159
+ if (!isNaN(num)) {
160
+ return num > 0;
161
+ }
162
+ else {
163
+ return data.trim().toLowerCase().startsWith('t') || data.trim().toLowerCase() === 'on';
164
+ }
165
+ }
166
+ export function date(formdata, name, parser = (data, name) => data.get(name)) {
167
+ if (!formdata.has(name)) {
168
+ return undefined;
169
+ }
170
+ const val = parser(formdata, name);
171
+ if (!val) {
172
+ return undefined;
173
+ }
174
+ return new Date(val);
175
+ }
176
+ export function date$(formdata, name, parser) {
177
+ return new Date(parser(formdata, name));
178
+ }
179
+ export function json(formdata, name, transformer = (val) => val) {
180
+ if (!formdata.has(name)) {
181
+ return undefined;
182
+ }
183
+ try {
184
+ return transformer(JSON.parse(formdata.get(name), reviver));
185
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
186
+ }
187
+ catch (e) {
188
+ fail(400, { targets: [name], message: `${name} isn't of json` });
189
+ }
190
+ }
191
+ export function record(formdata, options) {
192
+ return Object.fromEntries(formdata
193
+ .keys()
194
+ .map((key) => {
195
+ let entry = [key, formdata.getAll(key)];
196
+ if (options && options.filter && !options.filter(entry))
197
+ return;
198
+ return entry;
199
+ })
200
+ .filter((v) => v !== undefined)
201
+ .map(([key, value]) => [key, Array.isArray(value) && value.length === 1 ? value[0] : value])
202
+ .map((entry) => {
203
+ if (options && options.transformer)
204
+ entry = options.transformer(entry);
205
+ return entry;
206
+ }));
207
+ }
208
+ export function json$(formdata, name, transformer = (val) => val) {
209
+ if (!formdata.has(name)) {
210
+ fail(400, { targets: [name], message: `${name} doesn't exist` });
211
+ }
212
+ try {
213
+ return transformer(JSON.parse(formdata.get(name), reviver));
214
+ }
215
+ catch (e) {
216
+ throw () => fail(400, {
217
+ targets: [name],
218
+ message: `${name} isn't of type json`,
219
+ error: e instanceof Error ? e?.message : e?.toString()
220
+ });
221
+ }
222
+ }
223
+ export function jsond(formdata, options = {
224
+ unpack_prefixed: true,
225
+ transform: (value) => value
226
+ }) {
227
+ let result = {};
228
+ formdata
229
+ .entries()
230
+ .filter(([key]) => (options.prefix_name ? key.startsWith(options.prefix_name) : key))
231
+ .map(([key, value]) => [key, (options.transform ?? ((value) => value))(value, key, formdata)])
232
+ .map(([key, value]) => (options.unpack_prefixed && options.prefix_name
233
+ ? [key.replace(options.prefix_name, ''), value]
234
+ : [key, value]))
235
+ .forEach(([key, value]) => {
236
+ const splits = key.split('.');
237
+ let context = result;
238
+ splits.forEach((part, index) => {
239
+ if (index === splits.length - 1) {
240
+ // If it's the last part, check if the key already exists
241
+ if (context[part] === undefined) {
242
+ context[part] = value;
243
+ }
244
+ else if (Array.isArray(context[part])) {
245
+ context[part].push(value);
246
+ }
247
+ else {
248
+ context[part] = [context[part], value];
249
+ }
250
+ }
251
+ else {
252
+ // If the key doesn't exist, initialize it as an object
253
+ if (!context[part])
254
+ context[part] = {};
255
+ // Move deeper into the object
256
+ context = context[part];
257
+ }
258
+ });
259
+ });
260
+ if (options.processor) {
261
+ result = options.processor(result);
262
+ }
263
+ return result;
264
+ }
265
+ export function file(formdata, name) {
266
+ if (!formdata.has(name)) {
267
+ return undefined;
268
+ }
269
+ const data = formdata.get(name);
270
+ if (data instanceof File) {
271
+ return formdata.get(name);
272
+ }
273
+ else if (data === null || data === 'null') {
274
+ return null;
275
+ }
276
+ fail(400, { targets: [name], message: `${name} isn't of type File` });
277
+ }
278
+ export function fileRecord(formdata, prefix, removePrefix = false) {
279
+ const record = {};
280
+ formdata
281
+ .entries()
282
+ .filter(([key, value]) => key.startsWith(prefix) && value instanceof File && value.size > 0 && value.name.length > 0)
283
+ .map(([key, value]) => [removePrefix ? key.replace(prefix, '') : key, value])
284
+ .forEach(([key, value]) => {
285
+ if (!(key in record) || !record[key])
286
+ return (record[key] = [value]);
287
+ else
288
+ record[key].push(value);
289
+ });
290
+ return record;
291
+ }
292
+ export function file$(formdata, name) {
293
+ if (!formdata.has(name)) {
294
+ fail(400, { targets: [name], message: `${name} doesn't exist` });
295
+ }
296
+ const data = formdata.get(name);
297
+ if (data instanceof File) {
298
+ return formdata.get(name);
299
+ }
300
+ fail(400, { targets: [name], message: `${name} isn't of type File` });
301
+ }
302
+ export function files(formdata, name) {
303
+ if (!formdata.has(name)) {
304
+ return [];
305
+ }
306
+ return (formdata.getAll(name).filter((e) => e instanceof File && e.size > 0 && e.name.length > 0));
307
+ }
308
+ export function array(formdata, name, mapper) {
309
+ if (!formdata.has(name)) {
310
+ return undefined;
311
+ }
312
+ const array = formdata.getAll(name);
313
+ if (array.length === 1 && array[0] === '[]') {
314
+ return [];
315
+ }
316
+ if (!mapper) {
317
+ return array;
318
+ }
319
+ return array.map(mapper);
320
+ }
321
+ export function array$(formdata, name, mapper) {
322
+ if (!formdata.has(name)) {
323
+ fail(400, { targets: [name], message: `${name} doesn't exist` });
324
+ }
325
+ const array = formdata.getAll(name);
326
+ if (!mapper) {
327
+ return array;
328
+ }
329
+ const res = array.map(mapper);
330
+ if (res.length === 0) {
331
+ fail(400, { targets: [name], message: `${name} length of array === 0` });
332
+ }
333
+ return res;
334
+ }
335
+ /**
336
+ * TODO not compliant with enum standard but works for now
337
+ * @param formdata
338
+ * @param name
339
+ * @param _enum
340
+ */
341
+ export function Enum(formdata, name, _enum) {
342
+ if (!formdata.has(name)) {
343
+ return undefined;
344
+ }
345
+ const data = formdata.get(name);
346
+ if (typeof data != 'string') {
347
+ return fail(400, { targets: [name], message: `${name} isn't of type in enum` });
348
+ }
349
+ if (!(data in _enum))
350
+ return undefined;
351
+ return data;
352
+ }
353
+ /**
354
+ * TODO not compliant with enum standard but works for now
355
+ * @param formdata
356
+ * @param name
357
+ * @param _enum
358
+ */
359
+ export function Enum$(formdata, name, _enum) {
360
+ const data = Enum(formdata, name, _enum);
361
+ if (!data)
362
+ return fail(400, { targets: [name], message: `${name} isn't of type in enum` });
363
+ return _enum[data];
364
+ }
365
+ export function OldArray(formdata, name, parser) {
366
+ const data = string(formdata, name);
367
+ if (!data) {
368
+ return [];
369
+ }
370
+ return parser({ data });
371
+ }
372
+ export function arrayString(formdata, name, delimiter, mapper) {
373
+ return OldArray(formdata, name, ({ data }) => data.split(delimiter).map(mapper ?? ((i) => i)));
374
+ }
375
+ export function onlyIf(condition, TRUE, FALSE = undefined) {
376
+ return condition ? TRUE : FALSE;
377
+ }
378
+ export function onlyIfPresent(formdata, key, TRUE, FALSE = undefined) {
379
+ return formdata.has(key) ? TRUE(formdata.get(key)) : FALSE;
380
+ }
381
+ export function onlyIfArrayPresent(formdata, key, TRUE, FALSE) {
382
+ return formdata.has(key) && Array.isArray(formdata.getAll(key))
383
+ ? TRUE(formdata.getAll(key))
384
+ : FALSE;
385
+ }
386
+ export function basedOn(val, processor = (val) => val) {
387
+ return processor(val);
388
+ }
389
+ export function selector(formData, cases, useArray = false) {
390
+ try {
391
+ for (const key of formData.keys()) {
392
+ if (key in cases) {
393
+ const processor = cases[key];
394
+ if (formData.has(key)) {
395
+ if (useArray) {
396
+ const data = formData.getAll(key);
397
+ if (data.length > 0) {
398
+ return processor(data, key);
399
+ }
400
+ }
401
+ else {
402
+ const data = formData.get(key);
403
+ if (data !== null) {
404
+ return processor(data, key);
405
+ }
406
+ }
407
+ }
408
+ }
409
+ }
410
+ if ('$default' in cases) {
411
+ return cases.$default(formData);
412
+ }
413
+ }
414
+ catch (error) {
415
+ // Call $error in case of any error
416
+ if ('$error' in cases) {
417
+ return cases.$error?.(error);
418
+ }
419
+ }
420
+ return undefined;
421
+ }
422
+ export function selector$(formData, cases, useArray = false) {
423
+ const result = selector(formData, cases, useArray);
424
+ if (!result) {
425
+ if ('$error' in cases) {
426
+ cases?.['$error']?.('Unable to find value');
427
+ }
428
+ fail(400, { targets: Object.keys(cases), message: `Unable to find value` });
429
+ }
430
+ return result;
431
+ }
432
+ export async function handle(data, fn, ...errorHandlers) {
433
+ try {
434
+ if (data instanceof Request &&
435
+ data.headers.has('Content-Type') &&
436
+ data.headers.get('Content-Type')?.includes('form')) {
437
+ data = await data.formData();
438
+ }
439
+ else {
440
+ return fail(400, { message: "Request doesn't contain form data" });
441
+ }
442
+ return await fn({ data: data, form: createContext(data) });
443
+ }
444
+ catch (e) {
445
+ for (const errorHandler of errorHandlers) {
446
+ try {
447
+ await errorHandler(e);
448
+ }
449
+ catch (e) {
450
+ throw e;
451
+ }
452
+ }
453
+ throw e;
454
+ }
455
+ }
456
+ export async function enhance(input) {
457
+ const data = input.request.headers.has('Content-Type') &&
458
+ input.request.headers.get('Content-Type')?.includes('form')
459
+ ? await input.request.formData()
460
+ : new FormData();
461
+ return {
462
+ form: createContext(data)
463
+ };
464
+ }
465
+ export function process(formdata, name, parser, processor) {
466
+ return processor(parser(formdata, name), name);
467
+ }
468
+ export function validate(formdata, validator, options = {
469
+ unpack_prefixed: true,
470
+ transform: (value) => {
471
+ if (value instanceof File)
472
+ return value;
473
+ else
474
+ return reviver(undefined, value);
475
+ }
476
+ }) {
477
+ let value = jsond(formdata, {
478
+ unpack_prefixed: true,
479
+ ...options,
480
+ transform: (v) => reviver(undefined, v),
481
+ });
482
+ const result = validator(value);
483
+ if (!result.success) {
484
+ return fail(400, {
485
+ message: "Missing or invalid form data",
486
+ targets: [result.errors.map(({ path }) => path.replace('$.', options.prefix_name || ''))]
487
+ });
488
+ }
489
+ return result.data;
490
+ }
491
+ export const schema = (validator, options = {
492
+ unpack_prefixed: true,
493
+ transform: (value) => value
494
+ }) => async (input) => {
495
+ const { form } = await enhance(input);
496
+ return {
497
+ form: {
498
+ ...form,
499
+ result: form.validate(validator, options)
500
+ }
501
+ };
502
+ };
503
+ export const Form = {
504
+ string,
505
+ string$,
506
+ number,
507
+ number$,
508
+ boolean,
509
+ boolean$,
510
+ date,
511
+ date$,
512
+ file,
513
+ file$,
514
+ files,
515
+ array,
516
+ array$,
517
+ json,
518
+ json$,
519
+ jsond,
520
+ process,
521
+ validate,
522
+ onlyIf,
523
+ onlyIfPresent,
524
+ onlyIfArrayPresent,
525
+ selector,
526
+ selector$,
527
+ enhance,
528
+ schema,
529
+ handle
530
+ };
@@ -0,0 +1,5 @@
1
+ export * from './auth.js';
2
+ export * from './devtools.js';
3
+ export * from './featureflag.js';
4
+ export * from './form.ts';
5
+ export * from './request-correlation.ts';
@@ -0,0 +1,5 @@
1
+ export * from './auth.js';
2
+ export * from './devtools.js';
3
+ export * from './featureflag.js';
4
+ export * from "./form.js";
5
+ export * from "./request-correlation.js";
@@ -0,0 +1,9 @@
1
+ import type { EnhanceInput } from "../index.js";
2
+ export type RequestCorrelationLocals = {
3
+ correlation_id?: string;
4
+ request_started_at?: string;
5
+ };
6
+ export declare const RequestCorrelation: {
7
+ header: string;
8
+ attach: (input: EnhanceInput<"handle">) => void;
9
+ };
@@ -0,0 +1,47 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ const CORRELATION_ID_HEADER = 'x-correlation-id';
3
+ const REQUEST_ID_HEADER = 'x-request-id';
4
+ const MAX_CORRELATION_ID_LENGTH = 128;
5
+ const SAFE_CORRELATION_ID_PATTERN = /^[A-Za-z0-9._:-]+$/;
6
+ const normalizeCorrelationId = (raw) => {
7
+ if (!raw)
8
+ return undefined;
9
+ const candidate = raw.trim();
10
+ if (!candidate)
11
+ return undefined;
12
+ if (candidate.length > MAX_CORRELATION_ID_LENGTH)
13
+ return undefined;
14
+ if (!SAFE_CORRELATION_ID_PATTERN.test(candidate))
15
+ return undefined;
16
+ return candidate;
17
+ };
18
+ const withCorrelationHeader = (response, correlationId) => {
19
+ try {
20
+ response.headers.set(CORRELATION_ID_HEADER, correlationId);
21
+ return response;
22
+ }
23
+ catch {
24
+ const headers = new Headers(response.headers);
25
+ headers.set(CORRELATION_ID_HEADER, correlationId);
26
+ return new Response(response.body, {
27
+ status: response.status,
28
+ statusText: response.statusText,
29
+ headers
30
+ });
31
+ }
32
+ };
33
+ export const RequestCorrelation = {
34
+ header: CORRELATION_ID_HEADER,
35
+ attach: (input) => {
36
+ const correlationId = normalizeCorrelationId(input.request.headers.get(CORRELATION_ID_HEADER)) ??
37
+ normalizeCorrelationId(input.request.headers.get(REQUEST_ID_HEADER)) ??
38
+ randomUUID();
39
+ if (input.locals) {
40
+ // @ts-ignore
41
+ input.locals['correlation_id'] = correlationId;
42
+ // @ts-ignore
43
+ input.locals['request_started_at'] = Date.now();
44
+ }
45
+ input.responseHandlers.push(({ response }) => withCorrelationHeader(response, correlationId));
46
+ }
47
+ };
@@ -0,0 +1,68 @@
1
+ import { type Action, type ActionResult, type Cookies, type Handle, type RequestEvent, type ResolveOptions, type ServerLoadEvent } from '@sveltejs/kit';
2
+ import type { RouteId as AppRouteId, LayoutParams as AppLayoutParams } from '$app/types';
3
+ export type MaybePromise<T> = T | Promise<T>;
4
+ export type EnhanceErrorHandler = <T = any>(err: unknown) => MaybePromise<T> | undefined | never | void;
5
+ export type EnhanceResponseHandler = (input: {
6
+ event: RequestEvent;
7
+ response: Response;
8
+ }) => MaybePromise<unknown>;
9
+ export type EnhanceCallType = 'handle' | 'load' | 'method' | 'action';
10
+ export type EnhanceInput<CallType extends EnhanceCallType = EnhanceCallType, Params extends AppLayoutParams<'/'> = AppLayoutParams<'/'>, RouteId extends AppRouteId | null = AppRouteId | null, ParentData extends Record<string, any> = Record<string, any>> = {
11
+ cookies: Cookies;
12
+ params: Params;
13
+ route: {
14
+ id: RouteId;
15
+ };
16
+ url: URL;
17
+ locals: App.Locals;
18
+ request: Request;
19
+ callType: CallType;
20
+ fetch: typeof fetch;
21
+ get errorHandlers(): EnhanceErrorHandler[];
22
+ } & (CallType extends 'handle' ? {
23
+ get responseHandlers(): EnhanceResponseHandler[];
24
+ resolve: (event?: RequestEvent) => never;
25
+ readonly event: RequestEvent;
26
+ } : CallType extends 'load' ? {
27
+ parent: ServerLoadEvent<Params, ParentData, RouteId>['parent'];
28
+ depends: ServerLoadEvent<Params, ParentData, RouteId>['depends'];
29
+ untrack: ServerLoadEvent<Params, ParentData, RouteId>['untrack'];
30
+ } : object);
31
+ export type EnhanceFunction<CallType extends EnhanceCallType = EnhanceCallType, Params extends AppLayoutParams<'/'> = AppLayoutParams<'/'>, RouteId extends AppRouteId | null = AppRouteId | null, EnhanceReturn = any> = (event: EnhanceInput<CallType, Params, RouteId>) => MaybePromise<EnhanceReturn>;
32
+ export type EnhanceAction<Params extends AppLayoutParams<'/'>, OutputData extends Record<string, any> | void, RouteId extends AppRouteId | null, EnhanceReturn extends ActionResult | never | any = ActionResult> = (event: RequestEvent<Params, RouteId> & {
33
+ context: EnhanceReturn;
34
+ }) => MaybePromise<OutputData>;
35
+ export type EnhanceLoad<Params extends AppLayoutParams<'/'> = AppLayoutParams<'/'>, ParentData extends Record<string, any> = Record<string, any>, OutputData extends Record<string, any> | void = Record<string, any> | void, RouteId extends AppRouteId | null = AppRouteId | null, EnhanceReturn extends never | any = any> = (event: ServerLoadEvent<Params, ParentData, RouteId> & {
36
+ context: EnhanceReturn;
37
+ }) => MaybePromise<OutputData>;
38
+ export type EnhanceHandle<EnhanceReturn extends never | any = any> = (input: {
39
+ event: RequestEvent;
40
+ resolve(event: RequestEvent, opts?: ResolveOptions): MaybePromise<Response>;
41
+ } & {
42
+ context: EnhanceReturn;
43
+ }) => MaybePromise<Response>;
44
+ export type EnhanceMethod<Params extends AppLayoutParams<'/'> = AppLayoutParams<'/'>, RouteId extends AppRouteId | null = AppRouteId | null, EnhanceReturn extends never | any = any> = (event: RequestEvent<Params, RouteId> & {
45
+ context: EnhanceReturn;
46
+ }) => MaybePromise<Response>;
47
+ export declare const action: <Params extends AppLayoutParams<"/"> = AppLayoutParams<"/">, OutputData extends Record<string, any> | void = Record<string, any> | void, RouteId extends AppRouteId | null = AppRouteId | null, const Enhances extends readonly EnhanceFunction<"action", Params, RouteId, object>[] = readonly EnhanceFunction<"action", Params, RouteId, object>[], EnhanceReturn extends ConcatReturnTypes<Enhances> = ConcatReturnTypes<Enhances>>(action: EnhanceAction<Params, OutputData, RouteId, EnhanceReturn>, ...enhances: [...Enhances]) => Action<Params, OutputData, RouteId>;
48
+ export declare const load: <Params extends AppLayoutParams<"/"> = AppLayoutParams<"/">, ParentData extends Record<string, any> = Record<string, any>, OutputData extends Record<string, any> | void = Record<string, any> | void, RouteId extends AppRouteId | null = AppRouteId | null, const Enhances extends readonly EnhanceFunction<"load", Params, RouteId, object>[] = readonly EnhanceFunction<"load", Params, RouteId, object>[], EnhanceReturn extends ConcatReturnTypes<Enhances> = ConcatReturnTypes<Enhances>>(load: EnhanceLoad<Params, ParentData, OutputData, RouteId, EnhanceReturn>, ...contexts: [...Enhances]) => (event: ServerLoadEvent<Params, ParentData, RouteId>) => Promise<OutputData>;
49
+ export declare const method: <Params extends AppLayoutParams<"/"> = AppLayoutParams<"/">, RouteId extends AppRouteId | null = AppRouteId | null, const Enhances extends readonly EnhanceFunction<"method", Params, RouteId, object>[] = readonly EnhanceFunction<"method", Params, RouteId, object>[], EnhanceReturn extends Awaited<ConcatReturnTypes<Enhances>> = Awaited<ConcatReturnTypes<Enhances>>>(handle: EnhanceMethod<Params, RouteId, EnhanceReturn>, ...contexts: [...Enhances]) => (event: RequestEvent<Params, RouteId>) => Promise<Response>;
50
+ export declare const handle: <const Enhances extends readonly EnhanceFunction<"handle">[] = readonly EnhanceFunction<"handle">[], EnhanceReturn extends Awaited<ConcatReturnTypes<Enhances>> = Awaited<ConcatReturnTypes<Enhances>>>(handle: EnhanceHandle<EnhanceReturn>, ...contexts: [...Enhances]) => Handle;
51
+ export declare const enhance: {
52
+ action: <Params extends AppLayoutParams<"/"> = Record<string, string>, OutputData extends Record<string, any> | void = void | Record<string, any>, RouteId extends AppRouteId | null = string | null, const Enhances extends readonly EnhanceFunction<"action", Params, RouteId, object>[] = readonly EnhanceFunction<"action", Params, RouteId, object>[], EnhanceReturn extends ConcatReturnTypes<Enhances> = ConcatReturnTypes<Enhances>>(action: EnhanceAction<Params, OutputData, RouteId, EnhanceReturn>, ...enhances: Enhances) => Action<Params, OutputData, RouteId>;
53
+ load: <Params extends AppLayoutParams<"/"> = Record<string, string>, ParentData extends Record<string, any> = Record<string, any>, OutputData extends Record<string, any> | void = void | Record<string, any>, RouteId extends AppRouteId | null = string | null, const Enhances extends readonly EnhanceFunction<"load", Params, RouteId, object>[] = readonly EnhanceFunction<"load", Params, RouteId, object>[], EnhanceReturn extends ConcatReturnTypes<Enhances> = ConcatReturnTypes<Enhances>>(load: EnhanceLoad<Params, ParentData, OutputData, RouteId, EnhanceReturn>, ...contexts: Enhances) => (event: ServerLoadEvent<Params, ParentData, RouteId>) => Promise<OutputData>;
54
+ method: <Params extends AppLayoutParams<"/"> = Record<string, string>, RouteId extends AppRouteId | null = string | null, const Enhances extends readonly EnhanceFunction<"method", Params, RouteId, object>[] = readonly EnhanceFunction<"method", Params, RouteId, object>[], EnhanceReturn extends Awaited<ConcatReturnTypes<Enhances>> = Awaited<ConcatReturnTypes<Enhances>>>(handle: EnhanceMethod<Params, RouteId, EnhanceReturn>, ...contexts: Enhances) => (event: RequestEvent<Params, RouteId>) => Promise<Response>;
55
+ handle: <const Enhances extends readonly EnhanceFunction<"handle">[] = readonly EnhanceFunction<"handle", Record<string, string>, string | null, any>[], EnhanceReturn extends Awaited<ConcatReturnTypes<Enhances>> = Awaited<ConcatReturnTypes<Enhances>>>(handle: EnhanceHandle<EnhanceReturn>, ...contexts: Enhances) => Handle;
56
+ };
57
+ export type Enhancers<Params extends AppLayoutParams<'/'> = AppLayoutParams<'/'>, RouteId extends AppRouteId | null = AppRouteId | null, EnhanceReturn extends never | any = any> = Record<string, EnhanceFunction<EnhanceCallType, Params, RouteId, EnhanceReturn>>;
58
+ export type Func = (...args: any[]) => any;
59
+ export type ConcatReturnTypes<T extends readonly Func[]> = T extends readonly [] ? Record<never, never> : T extends readonly [infer First, ...infer Rest] ? First extends Func ? Awaited<ReturnType<First>> & ConcatReturnTypes<Rest extends readonly Func[] ? Rest : []> : Record<never, never> : Record<never, never>;
60
+ export declare const fail: <T extends Record<string, unknown> | undefined = undefined>(status: number, data: T) => never;
61
+ export declare const error: (status: number, body?: {
62
+ message: string;
63
+ } extends App.Error ? App.Error | string | undefined : never) => never;
64
+ export declare const success: <T extends Record<string, unknown> | undefined = undefined>(data: T) => T;
65
+ export declare function not_good(input: {
66
+ callType: EnhanceInput['callType'];
67
+ }, status: number, arg?: App.Error | string | Record<string, unknown> | undefined): never;
68
+ export * from './helpers/index.js';