@arkstack/http 0.5.1 → 0.5.3

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,1349 @@
1
+ import { Request, Response } from "clear-router";
2
+ import { definePlugin } from "clear-router/core";
3
+ import { definePlugin as definePlugin$1 } from "kanun";
4
+ import { createCipheriv, createDecipheriv, createHash, createHmac, randomBytes, timingSafeEqual } from "node:crypto";
5
+ import { DB } from "arkormx";
6
+ import { dirname, join } from "node:path";
7
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
8
+ import { Arkstack } from "@arkstack/contract";
9
+ //#region src/helpers.ts
10
+ /**
11
+ * Checks and asserts if target is a class
12
+ *
13
+ * @param target
14
+ * @returns
15
+ */
16
+ const isClass = (target) => {
17
+ return typeof target === "function" && /^class\s/.test(Function.prototype.toString.call(target));
18
+ };
19
+ const unwrapRequestSource = (source) => {
20
+ if (source.original) return unwrapRequestSource(source.original);
21
+ if (source.headers) return source;
22
+ if (source.req) return source.req;
23
+ if (source.request) return source.request;
24
+ return source;
25
+ };
26
+ const makeHeaders = (headers) => {
27
+ return new Headers(normalizeHeaders(headers));
28
+ };
29
+ const normalizeHeaders = (headers) => {
30
+ const normalized = {};
31
+ if (!headers) return normalized;
32
+ if (isHeaders(headers)) {
33
+ headers.forEach((value, key) => {
34
+ normalized[key.toLowerCase()] = value;
35
+ });
36
+ return normalized;
37
+ }
38
+ for (const [key, value] of Object.entries(headers)) {
39
+ const normalizedValue = normalizeHeaderValue(value);
40
+ if (typeof normalizedValue === "string") normalized[key.toLowerCase()] = normalizedValue;
41
+ }
42
+ return normalized;
43
+ };
44
+ const normalizeHeaderValue = (value) => {
45
+ if (Array.isArray(value)) return value.join(", ");
46
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
47
+ return value ?? void 0;
48
+ };
49
+ const isHeaders = (value) => typeof Headers !== "undefined" && value instanceof Headers;
50
+ const isRecord = (value) => {
51
+ return !!value && typeof value === "object" && !Array.isArray(value);
52
+ };
53
+ /**
54
+ * Resolve Middleware
55
+ *
56
+ * @param middleware
57
+ * @returns
58
+ */
59
+ const resolveMiddleware = (middleware) => {
60
+ if (!middleware || typeof middleware === "function" && !isClass(middleware)) return middleware;
61
+ if (middleware && typeof middleware === "object" && !isClass(middleware) && "handle" in middleware) return middleware.handle.bind(middleware);
62
+ const instance = isClass(middleware) ? new middleware() : middleware;
63
+ if (instance && typeof instance.handle === "function") return instance.handle.bind(instance);
64
+ return middleware;
65
+ };
66
+ //#endregion
67
+ //#region src/Request.ts
68
+ /**
69
+ * Represents an HTTP request, providing a consistent interface for accessing request data.
70
+ *
71
+ * @author 3m1n3nc3
72
+ */
73
+ var Request$1 = class Request$1 extends Request {
74
+ headers;
75
+ ip;
76
+ source;
77
+ currentUser;
78
+ currentAuth;
79
+ currentAuthUser;
80
+ currentAuthToken;
81
+ get user() {
82
+ return this.getSourceRequest()?.user ?? this.currentUser;
83
+ }
84
+ set user(user) {
85
+ this.currentUser = user;
86
+ }
87
+ get auth() {
88
+ return this.getSourceRequest()?.auth ?? this.currentAuth;
89
+ }
90
+ set auth(auth) {
91
+ this.currentAuth = auth;
92
+ }
93
+ get authUser() {
94
+ return this.getSourceRequest()?.authUser ?? this.currentAuthUser;
95
+ }
96
+ set authUser(user) {
97
+ this.currentAuthUser = user;
98
+ }
99
+ get authToken() {
100
+ return this.getSourceRequest()?.authToken ?? this.currentAuthToken;
101
+ }
102
+ set authToken(token) {
103
+ this.currentAuthToken = token;
104
+ }
105
+ constructor(options = {}) {
106
+ super(options);
107
+ const source = options.source ?? options.original;
108
+ const sourceRequest = isRecord(source) ? source : void 0;
109
+ this.headers = normalizeHeaders(options.headers);
110
+ if (this.method) this.method = options.method;
111
+ if (this.url) this.url = options.url;
112
+ if (this.path) this.path = options.path;
113
+ this.ip = options.ip ?? sourceRequest?.ip ?? null;
114
+ this.user = options.user ?? sourceRequest?.user;
115
+ this.auth = options.auth ?? sourceRequest?.auth;
116
+ this.authUser = options.authUser ?? sourceRequest?.authUser;
117
+ this.authToken = options.authToken ?? sourceRequest?.authToken;
118
+ this.source = source;
119
+ globalThis.request = (key) => key ? this.input(key) : this;
120
+ }
121
+ static from(source) {
122
+ if (!source) return;
123
+ if (source instanceof Request$1) return source;
124
+ const request = unwrapRequestSource(source);
125
+ return new Request$1({
126
+ headers: request.headers,
127
+ method: request.method,
128
+ url: request.originalUrl ?? request.url,
129
+ path: request.path,
130
+ ip: request.ip ?? null,
131
+ user: request.user,
132
+ auth: request.auth,
133
+ authUser: request.authUser,
134
+ authToken: request.authToken,
135
+ source: request
136
+ });
137
+ }
138
+ header(name) {
139
+ return this.headers[name.toLowerCase()];
140
+ }
141
+ bearerToken() {
142
+ const authorization = this.header("authorization");
143
+ if (!authorization?.startsWith("Bearer ")) return null;
144
+ return authorization.substring(7);
145
+ }
146
+ setUser(user) {
147
+ this.user = user;
148
+ if (isRecord(this.source)) this.source.user = user;
149
+ return this;
150
+ }
151
+ setAuthentication(auth, user, token) {
152
+ this.auth = auth;
153
+ this.authUser = user;
154
+ this.authToken = token;
155
+ this.setUser(user);
156
+ if (isRecord(this.source)) {
157
+ this.source.auth = auth;
158
+ this.source.authUser = user;
159
+ this.source.authToken = token;
160
+ }
161
+ return this;
162
+ }
163
+ syncFromSource() {
164
+ if (!isRecord(this.source)) return this;
165
+ const source = this.source;
166
+ this.user = source.user ?? this.user;
167
+ this.auth = source.auth ?? this.auth;
168
+ this.authUser = source.authUser ?? this.authUser;
169
+ this.authToken = source.authToken ?? this.authToken;
170
+ return this;
171
+ }
172
+ getSourceRequest() {
173
+ return isRecord(this.source) ? this.source : void 0;
174
+ }
175
+ clearAuthentication() {
176
+ this.auth = void 0;
177
+ this.authUser = void 0;
178
+ this.authToken = void 0;
179
+ this.user = void 0;
180
+ if (isRecord(this.source)) {
181
+ this.source.auth = void 0;
182
+ this.source.authUser = void 0;
183
+ this.source.authToken = void 0;
184
+ this.source.user = void 0;
185
+ }
186
+ return this;
187
+ }
188
+ };
189
+ //#endregion
190
+ //#region src/Response.ts
191
+ /**
192
+ * Represents an HTTP response, providing a consistent interface for accessing response data.
193
+ *
194
+ * @author 3m1n3nc3
195
+ */
196
+ var Response$1 = class Response$1 extends Response {
197
+ body;
198
+ source;
199
+ constructor(options = {}) {
200
+ super({
201
+ body: options.body,
202
+ headers: makeHeaders(options.headers),
203
+ statusCode: options.statusCode ?? 200
204
+ });
205
+ this.body = options.body ?? {};
206
+ this.source = options.source;
207
+ globalThis.response = () => this;
208
+ }
209
+ static from(source) {
210
+ if (!source) return;
211
+ if (source instanceof Response$1) return source;
212
+ return new Response$1({
213
+ statusCode: typeof source.status === "number" ? source.status : source.statusCode,
214
+ headers: source.headers,
215
+ source
216
+ });
217
+ }
218
+ status(code) {
219
+ this.statusCode = code;
220
+ if (isRecord(this.source)) if (typeof this.source.status === "function") this.source.status(code);
221
+ else this.source.statusCode = code;
222
+ return this;
223
+ }
224
+ header(name, value) {
225
+ this.headers.set(name.toLowerCase(), value);
226
+ if (isRecord(this.source) && typeof this.source.setHeader === "function") this.source.setHeader(name, value);
227
+ return this;
228
+ }
229
+ getHeaders() {
230
+ return normalizeHeaders(this.headers);
231
+ }
232
+ json(body) {
233
+ this.body = body;
234
+ if (isRecord(this.source) && typeof this.source.json === "function") return this.source.json(body);
235
+ return body;
236
+ }
237
+ send(body) {
238
+ this.body = body;
239
+ if (isRecord(this.source) && typeof this.source.send === "function") return this.source.send(body);
240
+ return body;
241
+ }
242
+ };
243
+ //#endregion
244
+ //#region src/session/FlashBag.ts
245
+ var FlashBag = class {
246
+ bag = {};
247
+ sweepKeys = /* @__PURE__ */ new Set();
248
+ constructor(items) {
249
+ this.bag = { ...items || {} };
250
+ this.sweepKeys = new Set(Object.keys(this.bag));
251
+ }
252
+ put(key, value) {
253
+ this.bag[key] = value;
254
+ this.sweepKeys.delete(key);
255
+ return this;
256
+ }
257
+ set(key, value) {
258
+ return this.put(key, value);
259
+ }
260
+ get(key, defaultValue) {
261
+ return key in this.bag ? this.bag[key] : defaultValue;
262
+ }
263
+ has(key) {
264
+ if (Array.isArray(key)) return key.every((item) => this.has(item));
265
+ if (key) return key in this.bag;
266
+ return this.any();
267
+ }
268
+ any() {
269
+ return Object.keys(this.bag).length > 0;
270
+ }
271
+ isEmpty() {
272
+ return !this.any();
273
+ }
274
+ isNotEmpty() {
275
+ return this.any();
276
+ }
277
+ keys() {
278
+ return Object.keys(this.bag);
279
+ }
280
+ all() {
281
+ return { ...this.bag };
282
+ }
283
+ clear(key) {
284
+ if (Array.isArray(key)) {
285
+ for (const item of key) {
286
+ delete this.bag[item];
287
+ this.sweepKeys.delete(item);
288
+ }
289
+ return this;
290
+ }
291
+ if (key) {
292
+ delete this.bag[key];
293
+ this.sweepKeys.delete(key);
294
+ return this;
295
+ }
296
+ this.bag = {};
297
+ this.sweepKeys.clear();
298
+ return this;
299
+ }
300
+ forget(key) {
301
+ return this.clear(key);
302
+ }
303
+ markForSweep(keys = this.keys()) {
304
+ this.sweepKeys = new Set(keys);
305
+ return this;
306
+ }
307
+ sweep() {
308
+ for (const key of this.sweepKeys) delete this.bag[key];
309
+ this.sweepKeys = new Set(Object.keys(this.bag));
310
+ return this;
311
+ }
312
+ toJSON() {
313
+ return this.all();
314
+ }
315
+ };
316
+ const sessionKey = Symbol.for("arkstack:http:session");
317
+ const asMessageRecord = (value) => {
318
+ if (!isRecord(value)) return;
319
+ return value;
320
+ };
321
+ const callRecordMethod = (source, method) => {
322
+ if (typeof source[method] !== "function") return;
323
+ return asMessageRecord(source[method]());
324
+ };
325
+ const resolveMessageRecord = (source) => {
326
+ if (!isRecord(source)) return;
327
+ if (typeof source.getMessageBag === "function") {
328
+ const bag = source.getMessageBag();
329
+ if (bag && bag !== source) {
330
+ const messages = resolveMessageRecord(bag);
331
+ if (messages) return messages;
332
+ }
333
+ }
334
+ if (typeof source.errors === "function") {
335
+ const errors = source.errors();
336
+ const messages = resolveMessageRecord(errors) || asMessageRecord(errors);
337
+ if (messages) return messages;
338
+ }
339
+ return callRecordMethod(source, "getMessages") || callRecordMethod(source, "messagesRaw") || callRecordMethod(source, "toArray") || resolveMessageRecord(source.errors) || asMessageRecord(source.errors);
340
+ };
341
+ const getValidationIssueField = (issue) => {
342
+ if (typeof issue.field === "string") return issue.field;
343
+ if (typeof issue.attribute === "string") return issue.attribute;
344
+ if (typeof issue.key === "string") return issue.key;
345
+ if (typeof issue.path === "string") return issue.path;
346
+ if (Array.isArray(issue.path)) return issue.path.join(".") || "_";
347
+ return "_";
348
+ };
349
+ const toMessages = (value) => {
350
+ if (Array.isArray(value)) return value.flatMap((item) => toMessages(item));
351
+ if (value instanceof Error) return [value.message];
352
+ if (isRecord(value) && typeof value.message === "string") return [value.message];
353
+ if (value === null || typeof value === "undefined") return [];
354
+ return [String(value)];
355
+ };
356
+ const getPath = (source, key, defaultValue) => {
357
+ const value = key.split(".").reduce((current, part) => {
358
+ if (!isRecord(current) && !Array.isArray(current)) return;
359
+ return current[part];
360
+ }, source);
361
+ return typeof value === "undefined" ? defaultValue : value;
362
+ };
363
+ //#endregion
364
+ //#region src/session/ErrorBag.ts
365
+ var ErrorBag = class ErrorBag extends FlashBag {
366
+ constructor(errors) {
367
+ super();
368
+ if (errors) {
369
+ this.merge(errors);
370
+ this.markForSweep();
371
+ }
372
+ }
373
+ add(field, message) {
374
+ const key = field || "_";
375
+ const messages = toMessages(message);
376
+ if (!messages.length) return this;
377
+ this.put(key, [...this.bag[key] || [], ...messages]);
378
+ return this;
379
+ }
380
+ addIf(condition, field, message) {
381
+ if (condition) this.add(field, message);
382
+ return this;
383
+ }
384
+ merge(errors) {
385
+ const incoming = resolveMessageRecord(errors) || (isRecord(errors) ? errors : void 0);
386
+ if (!incoming) return this.validation(errors);
387
+ for (const [field, messages] of Object.entries(incoming)) this.add(field, messages);
388
+ return this;
389
+ }
390
+ validation(error) {
391
+ if (!error) return this;
392
+ if (error instanceof ErrorBag) return this.merge(error);
393
+ const messages = resolveMessageRecord(error);
394
+ if (messages) return this.merge(messages);
395
+ if (Array.isArray(error)) {
396
+ for (const item of error) if (isRecord(item) && "message" in item) this.add(getValidationIssueField(item), item.message);
397
+ else this.add("_", item);
398
+ return this;
399
+ }
400
+ if (isRecord(error)) {
401
+ if (typeof error.errors === "function") return this.validation(error.errors());
402
+ if (error.errors) return this.validation(error.errors);
403
+ if (Array.isArray(error.issues)) return this.validation(error.issues);
404
+ if ("message" in error) return this.add(getValidationIssueField(error), error.message);
405
+ return this.merge(error);
406
+ }
407
+ if (error instanceof Error) return this.add("_", error.message);
408
+ return this.add("_", error);
409
+ }
410
+ keys() {
411
+ return Object.keys(this.bag);
412
+ }
413
+ get(field = "_") {
414
+ return [...this.bag[field] || []];
415
+ }
416
+ first(field) {
417
+ if (field) return this.bag[field]?.[0] || "";
418
+ return this.all()[0] || "";
419
+ }
420
+ has(field) {
421
+ if (Array.isArray(field)) return field.every((key) => this.has(key));
422
+ if (field) return (this.bag[field]?.length || 0) > 0;
423
+ return this.any();
424
+ }
425
+ hasAny(fields) {
426
+ return (Array.isArray(fields) ? fields : [fields]).some((key) => this.has(key));
427
+ }
428
+ missing(fields) {
429
+ return (Array.isArray(fields) ? fields : [fields]).every((key) => !this.has(key));
430
+ }
431
+ any() {
432
+ return Object.values(this.bag).some((messages) => messages.length > 0);
433
+ }
434
+ isEmpty() {
435
+ return !this.any();
436
+ }
437
+ isNotEmpty() {
438
+ return this.any();
439
+ }
440
+ count() {
441
+ return Object.values(this.bag).reduce((total, messages) => total + messages.length, 0);
442
+ }
443
+ all() {
444
+ return Object.values(this.bag).flat();
445
+ }
446
+ unique() {
447
+ return [...new Set(this.all())];
448
+ }
449
+ clear(field) {
450
+ super.clear(field);
451
+ return this;
452
+ }
453
+ forget(field) {
454
+ return this.clear(field);
455
+ }
456
+ messagesRaw() {
457
+ return this.toJSON();
458
+ }
459
+ getMessages() {
460
+ return this.messagesRaw();
461
+ }
462
+ getMessageBag() {
463
+ return this;
464
+ }
465
+ toArray() {
466
+ return this.toJSON();
467
+ }
468
+ toJSON() {
469
+ return Object.entries(this.bag).reduce((errors, [field, messages]) => {
470
+ errors[field] = [...messages];
471
+ return errors;
472
+ }, {});
473
+ }
474
+ };
475
+ //#endregion
476
+ //#region src/session/Session.ts
477
+ var Session = class Session {
478
+ errors;
479
+ flashBag;
480
+ id;
481
+ data;
482
+ persistent;
483
+ saveQueue = Promise.resolve();
484
+ constructor(initial, persistent) {
485
+ const current = initial instanceof Session ? initial : void 0;
486
+ const state = current ? current.snapshot() : initial && ("data" in initial || "errors" in initial || "flash" in initial) ? initial : { data: initial };
487
+ this.id = persistent?.id ?? current?.id;
488
+ this.persistent = persistent ?? current?.persistent;
489
+ this.saveQueue = current?.saveQueue ?? this.saveQueue;
490
+ this.data = current ? current.data : { ...state.data || {} };
491
+ this.errors = current ? current.errors : state.errors instanceof ErrorBag ? state.errors : new ErrorBag(state.errors);
492
+ this.flashBag = current ? current.flashBag : state.flash instanceof FlashBag ? state.flash : new FlashBag(state.flash);
493
+ const helper = ((key) => key ? this.get(key) : this);
494
+ Object.assign(helper, {
495
+ get: this.get.bind(this),
496
+ put: this.put.bind(this),
497
+ set: this.set.bind(this),
498
+ has: this.has.bind(this),
499
+ forget: this.forget.bind(this),
500
+ clear: this.clear.bind(this),
501
+ all: this.all.bind(this),
502
+ flash: this.flash.bind(this),
503
+ getFlash: this.getFlash.bind(this),
504
+ hasErrors: this.hasErrors.bind(this),
505
+ clearErrors: this.clearErrors.bind(this),
506
+ errors: this.errors,
507
+ flashBag: this.flashBag
508
+ });
509
+ globalThis.session = helper;
510
+ }
511
+ snapshot() {
512
+ return {
513
+ data: this.all(),
514
+ errors: this.errors.toJSON(),
515
+ flash: this.flashBag.toJSON()
516
+ };
517
+ }
518
+ queuePersist() {
519
+ this.save();
520
+ }
521
+ async save() {
522
+ const payload = this.snapshot();
523
+ const previous = this.saveQueue.catch(() => void 0);
524
+ this.saveQueue = previous.then(async () => {
525
+ await this.persistent?.save(payload);
526
+ });
527
+ await this.saveQueue;
528
+ return this;
529
+ }
530
+ async destroy() {
531
+ this.data = {};
532
+ this.errors.clear();
533
+ this.flashBag.clear();
534
+ await this.persistent?.destroy?.();
535
+ return this;
536
+ }
537
+ /**
538
+ * Get an item from the session bag
539
+ *
540
+ * @param key
541
+ * @param defaultValue
542
+ * @returns
543
+ */
544
+ get(key, defaultValue) {
545
+ return key in this.data ? this.data[key] : defaultValue;
546
+ }
547
+ /**
548
+ * Add an item to the session bag
549
+ *
550
+ * @param key
551
+ * @param defaultValue
552
+ * @returns
553
+ */
554
+ put(key, value) {
555
+ this.data[key] = value;
556
+ this.queuePersist();
557
+ return this;
558
+ }
559
+ /**
560
+ * Add an item to the session bag
561
+ *
562
+ * @param key
563
+ * @param defaultValue
564
+ * @returns
565
+ */
566
+ set(key, value) {
567
+ return this.put(key, value);
568
+ }
569
+ /**
570
+ * Check if an item exist in the session bag
571
+ *
572
+ * @param key
573
+ * @returns
574
+ */
575
+ has(key) {
576
+ return key in this.data;
577
+ }
578
+ /**
579
+ * Remove an item from the session bag
580
+ *
581
+ * @param key
582
+ * @returns
583
+ */
584
+ forget(key) {
585
+ delete this.data[key];
586
+ this.queuePersist();
587
+ return this;
588
+ }
589
+ /**
590
+ * Clear the session bag
591
+ *
592
+ * @returns
593
+ */
594
+ clear() {
595
+ this.data = {};
596
+ this.errors.clear();
597
+ this.flashBag.clear();
598
+ this.queuePersist();
599
+ return this;
600
+ }
601
+ /**
602
+ * Get all items in the session bag
603
+ *
604
+ * @returns
605
+ */
606
+ all() {
607
+ return { ...this.data };
608
+ }
609
+ /**
610
+ * Add a flash item for the next request
611
+ *
612
+ * @param key
613
+ * @param value
614
+ * @returns
615
+ */
616
+ flash(key, value) {
617
+ this.flashBag.put(key, value);
618
+ this.queuePersist();
619
+ return this;
620
+ }
621
+ /**
622
+ * Get a flash item
623
+ *
624
+ * @param key
625
+ * @param defaultValue
626
+ * @returns
627
+ */
628
+ getFlash(key, defaultValue) {
629
+ return this.flashBag.get(key, defaultValue);
630
+ }
631
+ /**
632
+ * Sweep flashed data that was loaded for this request
633
+ *
634
+ * @returns
635
+ */
636
+ async sweepFlash() {
637
+ this.errors.sweep();
638
+ this.flashBag.sweep();
639
+ await this.save();
640
+ return this;
641
+ }
642
+ /**
643
+ * Add an error to the session error bag
644
+ *
645
+ * @param field
646
+ * @param message
647
+ * @returns
648
+ */
649
+ addError(field, message) {
650
+ this.errors.add(field, message);
651
+ this.queuePersist();
652
+ return this;
653
+ }
654
+ /**
655
+ * Add multiple errors to the session error bag
656
+ *
657
+ * @param errors
658
+ * @returns
659
+ */
660
+ addErrors(errors) {
661
+ this.errors.merge(errors);
662
+ this.queuePersist();
663
+ return this;
664
+ }
665
+ /**
666
+ * Add a validation error to the session error bag
667
+ *
668
+ * @param error
669
+ * @returns
670
+ */
671
+ addValidationErrors(error) {
672
+ this.errors.validation(error);
673
+ this.queuePersist();
674
+ return this;
675
+ }
676
+ /**
677
+ * Check if the session error bag has any errors
678
+ *
679
+ * @param field
680
+ * @returns
681
+ */
682
+ hasErrors(field) {
683
+ return this.errors.has(field);
684
+ }
685
+ /**
686
+ * Clear all errors in the session error bag
687
+ *
688
+ * @param field
689
+ * @returns
690
+ */
691
+ clearErrors(field) {
692
+ this.errors.clear(field);
693
+ this.queuePersist();
694
+ return this;
695
+ }
696
+ /**
697
+ * Parse session for views
698
+ *
699
+ * @returns
700
+ */
701
+ forView() {
702
+ return {
703
+ ...this.all(),
704
+ errors: this.errors,
705
+ flash: this.flashBag
706
+ };
707
+ }
708
+ /**
709
+ * Return session as json
710
+ *
711
+ * @returns
712
+ */
713
+ toJSON() {
714
+ return {
715
+ ...this.all(),
716
+ errors: this.errors.toJSON(),
717
+ flash: this.flashBag.toJSON()
718
+ };
719
+ }
720
+ };
721
+ //#endregion
722
+ //#region src/old.ts
723
+ const requestInput = () => {
724
+ const request = globalThis.request?.();
725
+ if (request instanceof Request$1) {
726
+ if (isRecord(request.body)) return request.body;
727
+ const source = isRecord(request.source) ? request.source : void 0;
728
+ if (source && typeof source.getBody === "function") return source.getBody() ?? {};
729
+ if (isRecord(source?.body)) return source.body;
730
+ }
731
+ if (isRecord(request) && typeof request.getBody === "function") return request.getBody() ?? {};
732
+ return isRecord(request?.body) ? request.body : {};
733
+ };
734
+ const old = (key, defaultValue) => {
735
+ const input = requestInput();
736
+ if (!key) return input;
737
+ return getPath(input, key, defaultValue);
738
+ };
739
+ //#endregion
740
+ //#region src/session/helpers.ts
741
+ const sweepRegisteredKey = Symbol.for("arkstack:http:flash-sweep-registered");
742
+ const attachSessionProperty = (target, session) => {
743
+ target.httpSession = session;
744
+ if (!("session" in target) || target.session instanceof Session) target.session = session;
745
+ };
746
+ const responseSource = (target) => {
747
+ return target.res ?? target.ctx?.res ?? target.response?.source ?? target.clearResponse?.source ?? target.context?.res ?? target.context?.response?.source;
748
+ };
749
+ const registerResponseFlashSweep = (target, session) => {
750
+ if (!isRecord(target)) return;
751
+ const current = session ?? getSession(target);
752
+ const res = responseSource(target);
753
+ if (!(current instanceof Session) || !isRecord(res) || typeof res.end !== "function" || res[sweepRegisteredKey]) return;
754
+ res[sweepRegisteredKey] = true;
755
+ const end = res.end.bind(res);
756
+ res.end = (...args) => {
757
+ current.sweepFlash().catch(() => void 0).finally(() => end(...args));
758
+ return res;
759
+ };
760
+ };
761
+ const attachViewState = (target, session) => {
762
+ attachSessionProperty(target, session);
763
+ target.errors = session.errors;
764
+ if (isRecord(target.req)) {
765
+ attachSessionProperty(target.req, session);
766
+ target.req.errors = session.errors;
767
+ target.req.old = old;
768
+ }
769
+ if (isRecord(target.res)) target.res.locals = {
770
+ ...target.res.locals || {},
771
+ session,
772
+ errors: session.errors,
773
+ flash: session.flashBag,
774
+ old
775
+ };
776
+ if (isRecord(target.response?.source)) target.response.source.locals = {
777
+ ...target.response.source.locals || {},
778
+ session,
779
+ errors: session.errors,
780
+ flash: session.flashBag,
781
+ old
782
+ };
783
+ if (isRecord(target.context)) {
784
+ attachSessionProperty(target.context, session);
785
+ target.context.errors = session.errors;
786
+ target.context.flash = session.flashBag;
787
+ target.context.old = old;
788
+ }
789
+ if (isRecord(target.state)) {
790
+ attachSessionProperty(target.state, session);
791
+ target.state.errors = session.errors;
792
+ target.state.flash = session.flashBag;
793
+ target.state.old = old;
794
+ }
795
+ if (typeof target.set === "function") {
796
+ target.set("session", session);
797
+ target.set("errors", session.errors);
798
+ target.set("flash", session.flashBag);
799
+ target.set("old", old);
800
+ }
801
+ };
802
+ /**
803
+ * Ensure a valid session exists
804
+ *
805
+ * @param ctx
806
+ * @param initial
807
+ * @returns
808
+ */
809
+ const ensureSession = (ctx, initial, persistent) => {
810
+ if (!isRecord(ctx)) return new Session(initial, persistent);
811
+ const existing = ctx[sessionKey] ?? (ctx.session instanceof Session ? ctx.session : void 0) ?? (isRecord(ctx.req) && ctx.req.httpSession instanceof Session ? ctx.req.httpSession : void 0);
812
+ const session = existing instanceof Session ? existing : new Session(initial, persistent);
813
+ ctx[sessionKey] = session;
814
+ attachViewState(ctx, session);
815
+ return session;
816
+ };
817
+ /**
818
+ * Get the current session
819
+ *
820
+ * @param ctx
821
+ * @returns
822
+ */
823
+ const getSession = (ctx) => {
824
+ if (!isRecord(ctx)) return;
825
+ const session = ctx[sessionKey] ?? (ctx.httpSession instanceof Session ? ctx.httpSession : void 0) ?? (ctx.session instanceof Session ? ctx.session : void 0) ?? (isRecord(ctx.req) && ctx.req.httpSession instanceof Session ? ctx.req.httpSession : void 0) ?? (isRecord(ctx.context) && ctx.context.httpSession instanceof Session ? ctx.context.httpSession : void 0);
826
+ return session instanceof Session ? session : void 0;
827
+ };
828
+ //#endregion
829
+ //#region src/session/cookie.ts
830
+ const generateSessionId = () => randomBytes(32).toString("base64url");
831
+ const signValue = (value, secret) => createHmac("sha256", secret).update(value).digest("base64url");
832
+ const encodeSignedValue = (value, secret) => `${value}.${signValue(value, secret)}`;
833
+ const decodeSignedValue = (value, secret) => {
834
+ if (!value) return void 0;
835
+ const index = value.lastIndexOf(".");
836
+ if (index < 1) return void 0;
837
+ const payload = value.slice(0, index);
838
+ const signature = value.slice(index + 1);
839
+ const expected = signValue(payload, secret);
840
+ const signatureBuffer = Buffer.from(signature);
841
+ const expectedBuffer = Buffer.from(expected);
842
+ if (signatureBuffer.length !== expectedBuffer.length) return void 0;
843
+ return timingSafeEqual(signatureBuffer, expectedBuffer) ? payload : void 0;
844
+ };
845
+ const encodeJson = (value) => Buffer.from(JSON.stringify(value), "utf8").toString("base64url");
846
+ const decodeJson = (value) => {
847
+ if (!value) return void 0;
848
+ try {
849
+ return JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
850
+ } catch {
851
+ return;
852
+ }
853
+ };
854
+ const parseCookies = (header) => {
855
+ return (Array.isArray(header) ? header.join("; ") : header || "").split(";").reduce((cookies, part) => {
856
+ const index = part.indexOf("=");
857
+ if (index < 0) return cookies;
858
+ const key = part.slice(0, index).trim();
859
+ const value = part.slice(index + 1).trim();
860
+ if (key) cookies[key] = decodeURIComponent(value);
861
+ return cookies;
862
+ }, {});
863
+ };
864
+ const getCookie = (context, name) => {
865
+ const ctx = isRecord(context.ctx) ? context.ctx : context;
866
+ const headers = (context.request || ctx.clearRequest || ctx.req || ctx.request)?.headers || ctx.headers || ctx.req?.headers || ctx.request?.headers;
867
+ return parseCookies(typeof headers?.get === "function" ? headers.get("cookie") : headers?.cookie)[name];
868
+ };
869
+ const serializeCookie = (name, value, options = {}) => {
870
+ const parts = [`${name}=${encodeURIComponent(value)}`];
871
+ if (typeof options.maxAge === "number") parts.push(`Max-Age=${Math.max(0, Math.floor(options.maxAge))}`);
872
+ if (options.expires) parts.push(`Expires=${options.expires.toUTCString()}`);
873
+ parts.push(`Path=${options.path || "/"}`);
874
+ if (options.domain) parts.push(`Domain=${options.domain}`);
875
+ if (options.httpOnly !== false) parts.push("HttpOnly");
876
+ if (options.secure) parts.push("Secure");
877
+ if (options.sameSite) parts.push(`SameSite=${options.sameSite}`);
878
+ return parts.join("; ");
879
+ };
880
+ const splitSetCookieHeader = (value) => {
881
+ return value.split(/,\s*(?=[^;,\s]+=)/).filter(Boolean);
882
+ };
883
+ const withoutCookie = (current, cookieName) => {
884
+ return (Array.isArray(current) ? current.flatMap((item) => splitSetCookieHeader(String(item))) : typeof current === "string" ? splitSetCookieHeader(current) : []).filter((cookie) => !cookie.trim().startsWith(`${cookieName}=`));
885
+ };
886
+ const upsertHeaderValue = (target, headerName, cookieName, value) => {
887
+ if (!target) return false;
888
+ if (typeof target.setHeader === "function") {
889
+ const next = [...withoutCookie(typeof target.getHeader === "function" ? target.getHeader(headerName) : void 0, cookieName), value];
890
+ target.setHeader(headerName, next);
891
+ return true;
892
+ }
893
+ if (target.headers && typeof target.headers.set === "function") {
894
+ const next = [...withoutCookie(target.headers.get(headerName), cookieName), value];
895
+ target.headers.set(headerName, next.join(", "));
896
+ return true;
897
+ }
898
+ if (typeof target.appendHeader === "function") {
899
+ target.appendHeader(headerName, value);
900
+ return true;
901
+ }
902
+ if (typeof target.append === "function") {
903
+ target.append(headerName, value);
904
+ return true;
905
+ }
906
+ return false;
907
+ };
908
+ const setCookie = (context, name, value, options = {}) => {
909
+ const ctx = isRecord(context.ctx) ? context.ctx : context;
910
+ const cookie = serializeCookie(name, value, options);
911
+ const response = context.response || ctx.clearResponse;
912
+ if (response?.headers && typeof response.headers.set === "function") {
913
+ const next = [...withoutCookie(response.headers.get("set-cookie"), name), cookie];
914
+ response.headers.set("set-cookie", next.join(", "));
915
+ }
916
+ upsertHeaderValue(response?.source, "Set-Cookie", name, cookie) || upsertHeaderValue(ctx.res, "Set-Cookie", name, cookie) || upsertHeaderValue(ctx.response, "Set-Cookie", name, cookie) || upsertHeaderValue(ctx.response?.source, "Set-Cookie", name, cookie) || upsertHeaderValue(ctx.event?.res, "Set-Cookie", name, cookie);
917
+ return cookie;
918
+ };
919
+ //#endregion
920
+ //#region src/session/serialization.ts
921
+ const byteLength = (value) => Buffer.byteLength(value, "utf8");
922
+ const serializeValue = (value) => {
923
+ if (value === null || typeof value === "undefined") return "N;";
924
+ if (typeof value === "boolean") return `b:${value ? 1 : 0};`;
925
+ if (typeof value === "number") return Number.isInteger(value) ? `i:${value};` : `d:${value};`;
926
+ if (typeof value === "string") return `s:${byteLength(value)}:"${value}";`;
927
+ if (Array.isArray(value)) return serializeEntries(value.map((item, index) => [index, item]));
928
+ if (typeof value === "object") return serializeEntries(Object.entries(value));
929
+ return serializeValue(String(value));
930
+ };
931
+ const serializeEntries = (entries) => {
932
+ return `a:${entries.length}:{${entries.map(([key, value]) => serializeValue(key) + serializeValue(value)).join("")}}`;
933
+ };
934
+ var Parser = class {
935
+ source;
936
+ offset = 0;
937
+ constructor(source) {
938
+ this.source = source;
939
+ }
940
+ parse() {
941
+ const type = this.source[this.offset];
942
+ this.offset += type === "N" ? 1 : 2;
943
+ switch (type) {
944
+ case "N":
945
+ this.expect(";");
946
+ return null;
947
+ case "b": return this.readUntil(";") === "1";
948
+ case "i": return Number.parseInt(this.readUntil(";"), 10);
949
+ case "d": return Number.parseFloat(this.readUntil(";"));
950
+ case "s": return this.parseString();
951
+ case "a": return this.parseArray();
952
+ default: throw new Error(`Unsupported serialized session value: ${type}`);
953
+ }
954
+ }
955
+ parseString() {
956
+ const length = Number.parseInt(this.readUntil(":"), 10);
957
+ this.expect("\"");
958
+ let end = this.offset;
959
+ let bytes = 0;
960
+ while (end < this.source.length && bytes < length) {
961
+ const char = this.source[end];
962
+ bytes += Buffer.byteLength(char, "utf8");
963
+ end += 1;
964
+ }
965
+ const value = this.source.slice(this.offset, end);
966
+ this.offset = end;
967
+ this.expect("\"");
968
+ this.expect(";");
969
+ return value;
970
+ }
971
+ parseArray() {
972
+ const length = Number.parseInt(this.readUntil(":"), 10);
973
+ this.expect("{");
974
+ const entries = [];
975
+ let sequential = true;
976
+ for (let index = 0; index < length; index += 1) {
977
+ const key = this.parse();
978
+ const value = this.parse();
979
+ entries.push([key, value]);
980
+ if (key !== index) sequential = false;
981
+ }
982
+ this.expect("}");
983
+ if (sequential) return entries.map(([, value]) => value);
984
+ return entries.reduce((record, [key, value]) => {
985
+ record[String(key)] = value;
986
+ return record;
987
+ }, {});
988
+ }
989
+ readUntil(token) {
990
+ const index = this.source.indexOf(token, this.offset);
991
+ if (index < 0) throw new Error("Invalid serialized session payload");
992
+ const value = this.source.slice(this.offset, index);
993
+ this.offset = index + token.length;
994
+ return value;
995
+ }
996
+ expect(token) {
997
+ if (this.source.slice(this.offset, this.offset + token.length) !== token) throw new Error("Invalid serialized session payload");
998
+ this.offset += token.length;
999
+ }
1000
+ };
1001
+ const encodeSessionPayload = (payload) => {
1002
+ return serializeValue(payload);
1003
+ };
1004
+ const normalizeSessionPayload = (payload) => {
1005
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) return payload;
1006
+ const record = payload;
1007
+ for (const key of [
1008
+ "data",
1009
+ "errors",
1010
+ "flash"
1011
+ ]) if (Array.isArray(record[key]) && record[key].length === 0) record[key] = {};
1012
+ return record;
1013
+ };
1014
+ const decodeSessionPayload = (value) => {
1015
+ if (!value) return;
1016
+ try {
1017
+ return normalizeSessionPayload(new Parser(value).parse());
1018
+ } catch {
1019
+ return;
1020
+ }
1021
+ };
1022
+ //#endregion
1023
+ //#region src/session/encryption.ts
1024
+ const keyFromSecret = (secret) => {
1025
+ if (secret.startsWith("base64:")) {
1026
+ const decoded = Buffer.from(secret.slice(7), "base64");
1027
+ if (decoded.length === 32) return decoded;
1028
+ }
1029
+ const raw = Buffer.from(secret, "base64");
1030
+ if (raw.length === 32) return raw;
1031
+ return createHash("sha256").update(secret).digest();
1032
+ };
1033
+ const macFor = (iv, value, key) => {
1034
+ return createHmac("sha256", key).update(iv + value).digest("hex");
1035
+ };
1036
+ const encryptSessionValue = (value, secret) => {
1037
+ const key = keyFromSecret(secret);
1038
+ const iv = randomBytes(16);
1039
+ const ivValue = iv.toString("base64");
1040
+ const cipher = createCipheriv("aes-256-cbc", key, iv);
1041
+ const encrypted = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]).toString("base64");
1042
+ const payload = {
1043
+ iv: ivValue,
1044
+ value: encrypted,
1045
+ mac: macFor(ivValue, encrypted, key),
1046
+ tag: ""
1047
+ };
1048
+ return JSON.stringify(payload);
1049
+ };
1050
+ const decryptSessionValue = (payload, secret) => {
1051
+ if (!payload) return;
1052
+ try {
1053
+ const decoded = JSON.parse(payload.startsWith("{") ? payload : Buffer.from(payload, "base64").toString("utf8"));
1054
+ if (!decoded.iv || !decoded.value || !decoded.mac) return;
1055
+ const key = keyFromSecret(secret);
1056
+ const expected = macFor(decoded.iv, decoded.value, key);
1057
+ const actualBuffer = Buffer.from(decoded.mac);
1058
+ const expectedBuffer = Buffer.from(expected);
1059
+ if (actualBuffer.length !== expectedBuffer.length || !timingSafeEqual(actualBuffer, expectedBuffer)) return;
1060
+ const decipher = createDecipheriv("aes-256-cbc", key, Buffer.from(decoded.iv, "base64"));
1061
+ return Buffer.concat([decipher.update(Buffer.from(decoded.value, "base64")), decipher.final()]).toString("utf8");
1062
+ } catch {
1063
+ return;
1064
+ }
1065
+ };
1066
+ //#endregion
1067
+ //#region src/session/drivers/BaseSessionDriver.ts
1068
+ const defaultSecret = () => String(process.env.SESSION_SECRET || process.env.APP_KEY || "arkstack-session-secret");
1069
+ const defaultcookie_options = (ttl) => ({
1070
+ httpOnly: true,
1071
+ sameSite: "Lax",
1072
+ secure: process.env.NODE_ENV === "production",
1073
+ path: "/",
1074
+ maxAge: ttl
1075
+ });
1076
+ var BaseSessionDriver = class {
1077
+ cookie;
1078
+ secret;
1079
+ ttl;
1080
+ cookie_options;
1081
+ constructor(options = {}) {
1082
+ this.cookie = options.cookie || "arkstack_session";
1083
+ this.secret = options.secret || defaultSecret();
1084
+ this.ttl = options.ttl;
1085
+ this.cookie_options = {
1086
+ ...defaultcookie_options(options.ttl),
1087
+ ...options.cookie_options || {}
1088
+ };
1089
+ }
1090
+ readSessionId(context) {
1091
+ return decodeSignedValue(getCookie(context, this.cookie), this.secret);
1092
+ }
1093
+ encryptPayload(value) {
1094
+ return encryptSessionValue(value, this.secret);
1095
+ }
1096
+ decryptPayload(value) {
1097
+ return decryptSessionValue(value, this.secret);
1098
+ }
1099
+ writeSessionId(context, id) {
1100
+ setCookie(context, this.cookie, encodeSignedValue(id, this.secret), this.cookie_options);
1101
+ }
1102
+ };
1103
+ //#endregion
1104
+ //#region src/session/drivers/CookieSessionDriver.ts
1105
+ var CookieSessionDriver = class extends BaseSessionDriver {
1106
+ async start(context) {
1107
+ const cookie = getCookie(context, this.cookie);
1108
+ const decoded = this.decryptPayload(cookie) ?? decodeSignedValue(cookie, this.secret);
1109
+ const payload = decodeSessionPayload(decoded) ?? decodeJson(decoded);
1110
+ const id = payload?.id || generateSessionId();
1111
+ const state = payload ? {
1112
+ data: payload.data,
1113
+ errors: payload.errors,
1114
+ flash: payload.flash
1115
+ } : void 0;
1116
+ const save = async (next) => {
1117
+ setCookie(context, this.cookie, this.encryptPayload(encodeSessionPayload({
1118
+ id,
1119
+ ...next
1120
+ })), this.cookie_options);
1121
+ };
1122
+ const destroy = async () => {
1123
+ setCookie(context, this.cookie, "", {
1124
+ ...this.cookie_options,
1125
+ maxAge: 0,
1126
+ expires: /* @__PURE__ */ new Date(0)
1127
+ });
1128
+ };
1129
+ return {
1130
+ id,
1131
+ state,
1132
+ save,
1133
+ destroy
1134
+ };
1135
+ }
1136
+ };
1137
+ //#endregion
1138
+ //#region src/session/drivers/DatabaseSessionDriver.ts
1139
+ var DatabaseSessionDriver = class extends BaseSessionDriver {
1140
+ tableName;
1141
+ constructor(options = {}) {
1142
+ super(options);
1143
+ this.tableName = options.table || "sessions";
1144
+ }
1145
+ table() {
1146
+ return DB.table(this.tableName);
1147
+ }
1148
+ async start(context) {
1149
+ const id = this.readSessionId(context) || generateSessionId();
1150
+ this.writeSessionId(context, id);
1151
+ const row = await this.table().where({ id }).first();
1152
+ const state = isRecord(row) && typeof row.payload === "string" ? decodeSessionPayload(this.decryptPayload(row.payload) ?? row.payload) ?? decodeJson(this.decryptPayload(row.payload) ?? row.payload) : isRecord(row?.payload) ? row.payload : void 0;
1153
+ const save = async (payload) => {
1154
+ const now = /* @__PURE__ */ new Date();
1155
+ const values = {
1156
+ id,
1157
+ payload: this.encryptPayload(encodeSessionPayload(payload)),
1158
+ updatedAt: now,
1159
+ expiresAt: this.ttl ? new Date(now.getTime() + this.ttl * 1e3) : null
1160
+ };
1161
+ if (await this.table().where({ id }).first()) await this.table().where({ id }).update(values);
1162
+ else await this.table().insert({
1163
+ ...values,
1164
+ createdAt: now
1165
+ });
1166
+ this.writeSessionId(context, id);
1167
+ };
1168
+ const destroy = async () => {
1169
+ await this.table().where({ id }).delete();
1170
+ setCookie(context, this.cookie, "", {
1171
+ ...this.cookie_options,
1172
+ maxAge: 0,
1173
+ expires: /* @__PURE__ */ new Date(0)
1174
+ });
1175
+ };
1176
+ return {
1177
+ id,
1178
+ state,
1179
+ save,
1180
+ destroy
1181
+ };
1182
+ }
1183
+ };
1184
+ //#endregion
1185
+ //#region src/session/drivers/FileSessionDriver.ts
1186
+ var FileSessionDriver = class extends BaseSessionDriver {
1187
+ directory;
1188
+ constructor(options = {}) {
1189
+ super(options);
1190
+ this.directory = options.directory || join(Arkstack.rootDir(), "storage", "framework", "sessions");
1191
+ }
1192
+ path(id) {
1193
+ return join(this.directory, id);
1194
+ }
1195
+ async start(context) {
1196
+ const id = this.readSessionId(context) || generateSessionId();
1197
+ this.writeSessionId(context, id);
1198
+ let state;
1199
+ try {
1200
+ const contents = await readFile(this.path(id), "utf8");
1201
+ const payload = this.decryptPayload(contents) ?? contents;
1202
+ state = decodeSessionPayload(payload) ?? JSON.parse(payload);
1203
+ } catch {
1204
+ state = void 0;
1205
+ }
1206
+ const save = async (payload) => {
1207
+ const path = this.path(id);
1208
+ await mkdir(dirname(path), { recursive: true });
1209
+ await writeFile(path, this.encryptPayload(encodeSessionPayload(payload)), "utf8");
1210
+ this.writeSessionId(context, id);
1211
+ };
1212
+ const destroy = async () => {
1213
+ await rm(this.path(id), { force: true });
1214
+ setCookie(context, this.cookie, "", {
1215
+ ...this.cookie_options,
1216
+ maxAge: 0,
1217
+ expires: /* @__PURE__ */ new Date(0)
1218
+ });
1219
+ };
1220
+ return {
1221
+ id,
1222
+ state,
1223
+ save,
1224
+ destroy
1225
+ };
1226
+ }
1227
+ };
1228
+ //#endregion
1229
+ //#region src/session/config.ts
1230
+ let configuredDriver;
1231
+ const readAppSessionConfig = () => {
1232
+ try {
1233
+ if (!globalThis.config) return;
1234
+ return {
1235
+ driver: config("session.driver", "cookie"),
1236
+ cookie: config("session.cookie", "arkstack_session"),
1237
+ secret: config("session.secret"),
1238
+ ttl: config("session.ttl", 3600 * 24 * 7),
1239
+ cookie_options: {
1240
+ path: config("session.path", "/"),
1241
+ httpOnly: config("session.http_only", true),
1242
+ secure: config("session.secure", true),
1243
+ sameSite: config("session.same_site", "Lax")
1244
+ },
1245
+ file: { directory: config("session.directory") },
1246
+ database: { table: config("session.table", "sessions") }
1247
+ };
1248
+ } catch {
1249
+ return;
1250
+ }
1251
+ };
1252
+ const createSessionDriver = (config = {}) => {
1253
+ if (config.driver && typeof config.driver !== "string") return config.driver;
1254
+ const common = {
1255
+ cookie: config.cookie,
1256
+ secret: config.secret,
1257
+ ttl: config.ttl,
1258
+ cookie_options: config.cookie_options
1259
+ };
1260
+ switch (config.driver || "cookie") {
1261
+ case "file": return new FileSessionDriver({
1262
+ ...common,
1263
+ directory: config.file?.directory
1264
+ });
1265
+ case "database": return new DatabaseSessionDriver({
1266
+ ...common,
1267
+ table: config.database?.table
1268
+ });
1269
+ default: return new CookieSessionDriver(common);
1270
+ }
1271
+ };
1272
+ const configureSession = (config) => {
1273
+ configuredDriver = typeof config.start === "function" ? config : createSessionDriver(config);
1274
+ return configuredDriver;
1275
+ };
1276
+ const getSessionDriver = () => {
1277
+ if (!configuredDriver) configuredDriver = createSessionDriver(readAppSessionConfig());
1278
+ return configuredDriver;
1279
+ };
1280
+ //#endregion
1281
+ //#region src/plugins.ts
1282
+ const arkstackHttpPlugin = definePlugin({
1283
+ name: "arkstack-http",
1284
+ setup: ({ bind, useHttpContext }) => {
1285
+ bind(Session, async ({ ctx }) => {
1286
+ const existing = getSession(ctx);
1287
+ if (existing) return existing;
1288
+ const persistent = await getSessionDriver().start(ctx);
1289
+ const session = ensureSession(ctx, persistent.state, persistent);
1290
+ attachViewState(ctx, session);
1291
+ registerResponseFlashSweep(ctx, session);
1292
+ return session;
1293
+ });
1294
+ bind(Request$1, ({ request, ctx }) => {
1295
+ return (request instanceof Request$1 ? request : Request$1.from(request ?? ctx)).syncFromSource();
1296
+ });
1297
+ useHttpContext((context) => {
1298
+ const session = getSession(context.ctx);
1299
+ if (session) {
1300
+ context.httpSession = session;
1301
+ if (!("session" in context) || context.session instanceof Session) context.session = session;
1302
+ context.errors = session.errors;
1303
+ attachViewState(context.ctx, session);
1304
+ attachViewState(context, session);
1305
+ registerResponseFlashSweep(context, session);
1306
+ } else delete globalThis.session;
1307
+ globalThis.request = (key) => key ? context.request.input(key) : context.request;
1308
+ });
1309
+ }
1310
+ });
1311
+ const kanunSessionPlugin = definePlugin$1({
1312
+ name: "kanun-session-plugin",
1313
+ install({ onValidationError }) {
1314
+ onValidationError((validator) => {
1315
+ const currentSession = globalThis.session?.();
1316
+ if (currentSession instanceof Session) currentSession.addValidationErrors(validator);
1317
+ });
1318
+ }
1319
+ });
1320
+ //#endregion
1321
+ //#region src/redirect.ts
1322
+ const defaultRedirectStatus = 302;
1323
+ const headerValue = (value) => {
1324
+ if (Array.isArray(value)) return typeof value[0] === "string" ? value[0] : void 0;
1325
+ return typeof value === "string" ? value : void 0;
1326
+ };
1327
+ const redirectBackTarget = (fallback = "/") => {
1328
+ const request = globalThis.request?.();
1329
+ if (request instanceof Request$1) return request.header("referer") || request.header("referrer") || fallback;
1330
+ const source = isRecord(request?.source) ? request.source : void 0;
1331
+ const headers = isRecord(source?.headers) ? source.headers : void 0;
1332
+ return headerValue(headers?.referer) || headerValue(headers?.referrer) || fallback;
1333
+ };
1334
+ const resolveRedirectTarget = (to = "back", fallback = "/") => {
1335
+ if (!to || to === "back" || to === "source") return redirectBackTarget(fallback);
1336
+ return to;
1337
+ };
1338
+ const redirect = (to = "back", status = defaultRedirectStatus) => {
1339
+ const target = resolveRedirectTarget(to);
1340
+ const response = globalThis.response?.() ?? new Response$1();
1341
+ response.status(status);
1342
+ response.header("Location", target);
1343
+ response.body = null;
1344
+ const source = response.source;
1345
+ if (isRecord(source) && typeof source.redirect === "function") source.redirect(status, target);
1346
+ return response;
1347
+ };
1348
+ //#endregion
1349
+ export { registerResponseFlashSweep as A, normalizeHeaderValue as B, parseCookies as C, attachViewState as D, signValue as E, Response$1 as F, resolveMiddleware as H, Request$1 as I, isHeaders as L, Session as M, ErrorBag as N, ensureSession as O, FlashBag as P, isRecord as R, getCookie as S, setCookie as T, unwrapRequestSource as U, normalizeHeaders as V, decodeJson as _, kanunSessionPlugin as a, encodeSignedValue as b, getSessionDriver as c, CookieSessionDriver as d, BaseSessionDriver as f, encodeSessionPayload as g, decodeSessionPayload as h, arkstackHttpPlugin as i, old as j, getSession as k, FileSessionDriver as l, encryptSessionValue as m, redirectBackTarget as n, configureSession as o, decryptSessionValue as p, resolveRedirectTarget as r, createSessionDriver as s, redirect as t, DatabaseSessionDriver as u, decodeSignedValue as v, serializeCookie as w, generateSessionId as x, encodeJson as y, makeHeaders as z };