@orieljs/framework 0.1.0-alpha.2 → 0.1.0-alpha.4

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.
@@ -1,12 +1,15 @@
1
1
  import {
2
2
  Arr,
3
3
  ClassRegistry as ClassRegistry2,
4
+ Collection,
4
5
  Container,
5
6
  ContainerNotAvailableError,
7
+ ServiceProvider,
6
8
  Str,
7
9
  currentContainer,
8
- facade
9
- } from "./chunk-IRHK7GH7.js";
10
+ facade,
11
+ inject
12
+ } from "./chunk-DCI2T7WJ.js";
10
13
  import {
11
14
  Application,
12
15
  AuthFactory,
@@ -14,6 +17,8 @@ import {
14
17
  ClassRegistry,
15
18
  ConfigRepository,
16
19
  ConsoleKernel,
20
+ CookieJar,
21
+ CurrentRoute,
17
22
  DatabaseManager,
18
23
  Encrypter,
19
24
  EventDispatcher,
@@ -22,15 +27,23 @@ import {
22
27
  FilesystemManager,
23
28
  HttpKernel,
24
29
  HttpRequest,
30
+ LiveManager,
31
+ LogManager,
25
32
  MailManager,
26
33
  MiddlewareCallbacks,
27
34
  NotificationDispatcher,
35
+ PipelineFactory,
28
36
  QueueManager,
37
+ Redirector,
38
+ ResponseFactory,
39
+ Router,
29
40
  RoutingPaths,
30
41
  Session,
31
42
  UrlGenerator,
32
- ViewFactory
33
- } from "./chunk-3ZRPBRAE.js";
43
+ ValidationFactory,
44
+ ViewFactory,
45
+ token
46
+ } from "./chunk-WSGWYXAO.js";
34
47
 
35
48
  // ../Config/dist/index.js
36
49
  import { existsSync, readFileSync } from "fs";
@@ -80,12 +93,20 @@ function env(key, defaultValue) {
80
93
  const quoted = /^(["'])(.*)\1$/s.exec(raw);
81
94
  return quoted ? quoted[2] : raw;
82
95
  }
96
+ function singleQuoted(source) {
97
+ const keys = /* @__PURE__ */ new Set();
98
+ for (const match of source.matchAll(/^[ \t]*(?:export[ \t]+)?([\w.-]+)[ \t]*=[ \t]*'/gm)) keys.add(match[1]);
99
+ return keys;
100
+ }
83
101
  function loadEnvironment(basePath) {
84
102
  const appEnv = process.env.APP_ENV;
85
103
  const specific = appEnv ? join(basePath, `.env.${appEnv}`) : void 0;
86
104
  const file = specific && existsSync(specific) ? specific : join(basePath, ".env");
87
105
  if (!existsSync(file)) return void 0;
88
- for (const [key, value] of Object.entries(parse(readFileSync(file)))) {
106
+ const source = readFileSync(file, "utf8");
107
+ const literal = singleQuoted(source);
108
+ for (const [key, raw] of Object.entries(parse(source))) {
109
+ const value = literal.has(key) ? raw : raw.replace(/\$\{([A-Za-z_][A-Za-z0-9_.]*)\}/g, (match, name) => process.env[name] ?? match);
89
110
  if (process.env[key] === void 0) process.env[key] = value;
90
111
  }
91
112
  return file;
@@ -132,10 +153,11 @@ var ApplicationBuilder = class {
132
153
  return this;
133
154
  }
134
155
  withRouting(options) {
135
- const abs = (p) => p === void 0 ? void 0 : isAbsolute(p) ? p : join4(this.app.basePath(), p);
156
+ const abs = (p) => isAbsolute(p) ? p : join4(this.app.basePath(), p);
157
+ const each = (p) => Array.isArray(p) ? p.map(abs) : abs(p);
136
158
  const paths = { apiPrefix: options.apiPrefix ?? "api" };
137
- if (options.web !== void 0) paths.web = abs(options.web);
138
- if (options.api !== void 0) paths.api = abs(options.api);
159
+ if (options.web !== void 0) paths.web = each(options.web);
160
+ if (options.api !== void 0) paths.api = each(options.api);
139
161
  if (options.console !== void 0) paths.console = abs(options.console);
140
162
  if (options.health !== void 0) paths.health = options.health;
141
163
  this.routing = paths;
@@ -315,9 +337,9 @@ var Vite = class {
315
337
  const stylesheets = uniqueTags.filter((tag) => tag.startsWith("<link"));
316
338
  const scripts = uniqueTags.filter((tag) => !tag.startsWith("<link"));
317
339
  const seen = /* @__PURE__ */ new Set();
318
- const uniquePreloads = preloads.filter(([, url]) => seen.has(url) ? false : (seen.add(url), true));
319
- const sorted = [...uniquePreloads.filter(([, url]) => CSS_PATH.test(url)), ...uniquePreloads.filter(([, url]) => !CSS_PATH.test(url))];
320
- const preloadTags = sorted.map(([src, url, chunk2]) => this.#preloadTag(src, url, chunk2, manifest));
340
+ const uniquePreloads = preloads.filter(([, url2]) => seen.has(url2) ? false : (seen.add(url2), true));
341
+ const sorted = [...uniquePreloads.filter(([, url2]) => CSS_PATH.test(url2)), ...uniquePreloads.filter(([, url2]) => !CSS_PATH.test(url2))];
342
+ const preloadTags = sorted.map(([src, url2, chunk2]) => this.#preloadTag(src, url2, chunk2, manifest));
321
343
  return new ViteHtml(preloadTags.join("") + stylesheets.join("") + scripts.join(""));
322
344
  }
323
345
  /** Laravel's @viteReactRefresh: the React Refresh preamble while the dev server runs; nothing otherwise. */
@@ -402,41 +424,41 @@ var Vite = class {
402
424
  }
403
425
  return imports;
404
426
  }
405
- #tagForChunk(src, url, chunk2, manifest) {
406
- if (CSS_PATH.test(url)) {
407
- const attributes2 = this.#parseAttributes({ rel: "stylesheet", href: url, nonce: this.#nonce ?? false, ...this.#styleAttributes(src, url, chunk2, manifest) });
427
+ #tagForChunk(src, url2, chunk2, manifest) {
428
+ if (CSS_PATH.test(url2)) {
429
+ const attributes2 = this.#parseAttributes({ rel: "stylesheet", href: url2, nonce: this.#nonce ?? false, ...this.#styleAttributes(src, url2, chunk2, manifest) });
408
430
  return `<link ${attributes2.join(" ")} />`;
409
431
  }
410
- const attributes = this.#parseAttributes({ type: "module", src: url, nonce: this.#nonce ?? false, ...this.#scriptAttributes(src, url, chunk2, manifest) });
432
+ const attributes = this.#parseAttributes({ type: "module", src: url2, nonce: this.#nonce ?? false, ...this.#scriptAttributes(src, url2, chunk2, manifest) });
411
433
  return `<script ${attributes.join(" ")}></script>`;
412
434
  }
413
- #preloadTag(src, url, chunk2, manifest) {
414
- const css = CSS_PATH.test(url);
435
+ #preloadTag(src, url2, chunk2, manifest) {
436
+ const css = CSS_PATH.test(url2);
415
437
  let attributes = {
416
438
  rel: css ? "preload" : "modulepreload",
417
439
  as: css ? "style" : "script",
418
- href: url,
440
+ href: url2,
419
441
  nonce: this.#nonce ?? false,
420
- crossorigin: (css ? this.#styleAttributes(src, url, chunk2, manifest) : this.#scriptAttributes(src, url, chunk2, manifest)).crossorigin ?? false
442
+ crossorigin: (css ? this.#styleAttributes(src, url2, chunk2, manifest) : this.#scriptAttributes(src, url2, chunk2, manifest)).crossorigin ?? false
421
443
  };
422
444
  if (this.#integrityKey !== false) attributes.integrity = chunk2?.[this.#integrityKey] ?? false;
423
445
  for (const resolver of this.#preloadResolvers) {
424
- const resolved = resolver(src, url, chunk2, manifest);
446
+ const resolved = resolver(src, url2, chunk2, manifest);
425
447
  if (resolved === false) return "";
426
448
  attributes = { ...attributes, ...resolved };
427
449
  }
428
450
  const { href: _href, ...rest } = attributes;
429
- this.#preloaded[url] = this.#parseAttributes(rest);
451
+ this.#preloaded[url2] = this.#parseAttributes(rest);
430
452
  return `<link ${this.#parseAttributes(attributes).join(" ")} />`;
431
453
  }
432
- #scriptAttributes(src, url, chunk2, manifest) {
454
+ #scriptAttributes(src, url2, chunk2, manifest) {
433
455
  let attributes = this.#integrityKey !== false ? { integrity: chunk2?.[this.#integrityKey] ?? false } : {};
434
- for (const resolver of this.#scriptResolvers) attributes = { ...attributes, ...resolver(src, url, chunk2, manifest) };
456
+ for (const resolver of this.#scriptResolvers) attributes = { ...attributes, ...resolver(src, url2, chunk2, manifest) };
435
457
  return attributes;
436
458
  }
437
- #styleAttributes(src, url, chunk2, manifest) {
459
+ #styleAttributes(src, url2, chunk2, manifest) {
438
460
  let attributes = this.#integrityKey !== false ? { integrity: chunk2?.[this.#integrityKey] ?? false } : {};
439
- for (const resolver of this.#styleResolvers) attributes = { ...attributes, ...resolver(src, url, chunk2, manifest) };
461
+ for (const resolver of this.#styleResolvers) attributes = { ...attributes, ...resolver(src, url2, chunk2, manifest) };
440
462
  return attributes;
441
463
  }
442
464
  /** false/null/undefined are dropped, true is a bare attribute, anything else name="value". */
@@ -468,7 +490,7 @@ var Application2 = class _Application extends Container {
468
490
  this.scoped(Vite, () => new Vite(this));
469
491
  }
470
492
  base;
471
- version = "0.1.0-alpha.1";
493
+ version = "0.1.0-alpha.4";
472
494
  providers = [];
473
495
  bootingCallbacks = [];
474
496
  bootedCallbacks = [];
@@ -480,144 +502,3090 @@ var Application2 = class _Application extends Container {
480
502
  static configure(options) {
481
503
  return new ApplicationBuilder(new _Application(options.basePath));
482
504
  }
483
- basePath(path = "") {
484
- return join3(this.base, path);
505
+ basePath(path = "") {
506
+ return join3(this.base, path);
507
+ }
508
+ appPath(path = "") {
509
+ return this.basePath(join3("app", path));
510
+ }
511
+ bootstrapPath(path = "") {
512
+ return this.basePath(join3("bootstrap", path));
513
+ }
514
+ configPath(path = "") {
515
+ return this.basePath(join3("config", path));
516
+ }
517
+ databasePath(path = "") {
518
+ return this.basePath(join3("database", path));
519
+ }
520
+ publicPath(path = "") {
521
+ return this.basePath(join3("public", path));
522
+ }
523
+ resourcePath(path = "") {
524
+ return this.basePath(join3("resources", path));
525
+ }
526
+ storagePath(path = "") {
527
+ return this.basePath(join3("storage", path));
528
+ }
529
+ environment(...environments) {
530
+ const fallback = process.env.APP_ENV ?? "production";
531
+ const current = this.bound(ConfigRepository) ? String(this.make(ConfigRepository).get("app.env", fallback)) : fallback;
532
+ return environments.length === 0 ? current : environments.some((pattern) => Str.is(pattern, current));
533
+ }
534
+ isProduction() {
535
+ return this.environment("production");
536
+ }
537
+ isLocal() {
538
+ return this.environment("local");
539
+ }
540
+ runningUnitTests() {
541
+ return this.environment("testing");
542
+ }
543
+ runningInConsole() {
544
+ return this.inConsole;
545
+ }
546
+ setRunningInConsole(value = true) {
547
+ this.inConsole = value;
548
+ }
549
+ async register(provider) {
550
+ const cls = typeof provider === "function" ? provider : provider.constructor;
551
+ const existing = this.providers.find((p) => p.constructor === cls);
552
+ if (existing) return existing;
553
+ const instance = typeof provider === "function" ? new provider(this) : provider;
554
+ try {
555
+ instance.register?.();
556
+ } catch (error) {
557
+ throw new ProviderRegisterError(instance.constructor.name, error);
558
+ }
559
+ this.providers.push(instance);
560
+ if (this.hasBooted) await this.bootProvider(instance);
561
+ return instance;
562
+ }
563
+ getProviders() {
564
+ return [...this.providers];
565
+ }
566
+ boot() {
567
+ return this.bootPromise ??= this.runBoot();
568
+ }
569
+ isBooted() {
570
+ return this.hasBooted;
571
+ }
572
+ booting(callback) {
573
+ this.bootingCallbacks.push(callback);
574
+ }
575
+ /** If already booted, the callback runs immediately (a rejection surfaces as an unhandled rejection). */
576
+ booted(callback) {
577
+ this.bootedCallbacks.push(callback);
578
+ if (this.hasBooted) void callback(this);
579
+ }
580
+ terminating(callback) {
581
+ this.terminatingCallbacks.push(callback);
582
+ }
583
+ async handle(request2) {
584
+ await this.boot();
585
+ const scope = this.createScope();
586
+ return scope.run(() => scope.make(HttpKernel).handle(request2));
587
+ }
588
+ async terminate() {
589
+ for (const callback of this.terminatingCallbacks) await callback();
590
+ }
591
+ async runBoot() {
592
+ for (const callback of this.bootingCallbacks) await callback(this);
593
+ for (let i = 0; i < this.providers.length; i++) await this.bootProvider(this.providers[i]);
594
+ this.hasBooted = true;
595
+ for (const callback of this.bootedCallbacks) await callback(this);
596
+ }
597
+ async bootProvider(provider) {
598
+ try {
599
+ await provider.boot?.();
600
+ } catch (error) {
601
+ throw new ProviderBootError(provider.constructor.name, error);
602
+ }
603
+ }
604
+ };
605
+ var ClosureKernel = class {
606
+ routes = /* @__PURE__ */ new Map();
607
+ on(method, path, handler) {
608
+ this.routes.set(`${method.toUpperCase()} ${path}`, handler);
609
+ return this;
610
+ }
611
+ async handle(request2) {
612
+ const handler = this.routes.get(`${request2.method} ${new URL(request2.url).pathname}`);
613
+ if (!handler) return new Response("Not Found", { status: 404, headers: { "content-type": "text/plain; charset=utf-8" } });
614
+ return toResponse(await handler(request2));
615
+ }
616
+ };
617
+ function toResponse(output) {
618
+ if (output instanceof Response) return output;
619
+ if (output === void 0 || output === null) return new Response(null, { status: 204 });
620
+ if (typeof output === "string") return new Response(output, { headers: { "content-type": "text/html; charset=utf-8" } });
621
+ return Response.json(output);
622
+ }
623
+
624
+ // ../Http/dist/index.js
625
+ import { createReadStream } from "fs";
626
+ import { stat } from "fs/promises";
627
+ import { basename } from "path";
628
+ import { Readable } from "stream";
629
+ import { readFile } from "fs/promises";
630
+ import { fileURLToPath } from "url";
631
+ import { createReadStream as createReadStream2 } from "fs";
632
+ import { stat as stat2 } from "fs/promises";
633
+ import { Readable as Readable2 } from "stream";
634
+ import { relative, resolve, sep } from "path";
635
+ import { createServer } from "http";
636
+ import { Readable as Readable3 } from "stream";
637
+ import { pipeline } from "stream/promises";
638
+ import { existsSync as existsSync4 } from "fs";
639
+ import { mkdir, readFile as readFile2, writeFile } from "fs/promises";
640
+ import { dirname } from "path";
641
+ var HttpException = class extends Error {
642
+ constructor(status, message = "", headers = {}) {
643
+ super(message);
644
+ this.status = status;
645
+ this.headers = headers;
646
+ }
647
+ status;
648
+ headers;
649
+ kind = "Http";
650
+ name = "HttpException";
651
+ };
652
+ var HttpResponseException = class extends Error {
653
+ constructor(response2) {
654
+ super(`HTTP response exception (${response2.status}).`);
655
+ this.response = response2;
656
+ this.status = response2.status;
657
+ }
658
+ response;
659
+ kind = "Http";
660
+ name = "HttpResponseException";
661
+ status;
662
+ };
663
+ var HttpServerError = class extends Error {
664
+ kind = "Server";
665
+ status = 500;
666
+ name = "HttpServerError";
667
+ };
668
+ function exceedsJsonDepth(text, maxDepth) {
669
+ let depth = 0;
670
+ let inString = false;
671
+ for (let i = 0; i < text.length; i++) {
672
+ const code = text.charCodeAt(i);
673
+ if (inString) {
674
+ if (code === 92) i++;
675
+ else if (code === 34) inString = false;
676
+ } else if (code === 34) inString = true;
677
+ else if (code === 123 || code === 91) {
678
+ if (++depth > maxDepth) return true;
679
+ } else if (code === 125 || code === 93) depth--;
680
+ }
681
+ return false;
682
+ }
683
+ async function readBody(raw, limit) {
684
+ if (raw.body === null) return new Uint8Array(0);
685
+ const declared = Number(raw.headers.get("content-length") ?? NaN);
686
+ if (limit !== null && Number.isFinite(declared) && declared > limit) {
687
+ await raw.body.cancel();
688
+ throw new HttpException(413, "Content Too Large");
689
+ }
690
+ const reader = raw.body.getReader();
691
+ const chunks = [];
692
+ let size = 0;
693
+ for (; ; ) {
694
+ const { done, value } = await reader.read();
695
+ if (done) break;
696
+ size += value.byteLength;
697
+ if (limit !== null && size > limit) {
698
+ await reader.cancel();
699
+ throw new HttpException(413, "Content Too Large");
700
+ }
701
+ chunks.push(value);
702
+ }
703
+ const out = new Uint8Array(size);
704
+ let offset = 0;
705
+ for (const chunk2 of chunks) {
706
+ out.set(chunk2, offset);
707
+ offset += chunk2.byteLength;
708
+ }
709
+ return out;
710
+ }
711
+ function requestScope() {
712
+ const container = currentContainer();
713
+ return container !== void 0 && container.bound(HttpRequest) ? container : void 0;
714
+ }
715
+ function currentRequest() {
716
+ return requestScope()?.make(HttpRequest);
717
+ }
718
+ function optionalSession() {
719
+ const scope = requestScope();
720
+ return scope?.bound(Session) ? scope.make(Session) : void 0;
721
+ }
722
+ function optionalCookieJar() {
723
+ const scope = requestScope();
724
+ return scope?.bound(CookieJar) ? scope.make(CookieJar) : void 0;
725
+ }
726
+ function serializeCookie(cookie) {
727
+ const parts = [`${encodeURIComponent(cookie.name)}=${encodeURIComponent(cookie.value)}`, `Path=${cookie.path}`];
728
+ if (cookie.domain !== void 0) parts.push(`Domain=${cookie.domain}`);
729
+ if (cookie.secure) parts.push("Secure");
730
+ if (cookie.httpOnly) parts.push("HttpOnly");
731
+ parts.push(`SameSite=${cookie.sameSite.charAt(0).toUpperCase()}${cookie.sameSite.slice(1)}`);
732
+ if (cookie.minutes < 0) parts.push("Max-Age=0", "Expires=Thu, 01 Jan 1970 00:00:00 GMT");
733
+ else if (cookie.minutes > 0) parts.push(`Max-Age=${Math.round(cookie.minutes * 60)}`);
734
+ return parts.join("; ");
735
+ }
736
+ var HttpRedirectResponse = class {
737
+ constructor(targetUrl, status = 302, headers) {
738
+ this.targetUrl = targetUrl;
739
+ this.status = status;
740
+ this.#headers = new Headers(headers);
741
+ }
742
+ targetUrl;
743
+ status;
744
+ #headers;
745
+ with(key, value) {
746
+ const session = optionalSession();
747
+ if (session === void 0) return this;
748
+ if (typeof key === "string") session.flash(key, value);
749
+ else for (const [name, item] of Object.entries(key)) session.flash(name, item);
750
+ return this;
751
+ }
752
+ /** Flashes `errors` as { [bag]: { field: string[] } }, keeping the other bags already flashed. */
753
+ withErrors(errors, bag = "default") {
754
+ const session = optionalSession();
755
+ if (session === void 0) return this;
756
+ const source = typeof errors.toObject === "function" ? errors.toObject() : errors;
757
+ const normalized = Object.fromEntries(
758
+ Object.entries(source).map(([field, messages]) => [field, Array.isArray(messages) ? [...messages] : [messages]])
759
+ );
760
+ const existing = session.get("errors", {});
761
+ session.flash("errors", { ...existing, [bag]: normalized });
762
+ return this;
763
+ }
764
+ /** Flashes `input`, or the current request's input (files are never part of input()). */
765
+ withInput(input) {
766
+ const session = optionalSession();
767
+ if (session === void 0) return this;
768
+ session.flashInput(input ?? currentRequest()?.input() ?? {});
769
+ return this;
770
+ }
771
+ /**
772
+ * Always a Set-Cookie header on this response (Laravel's RedirectResponse::withCookie). A bound CookieJar only
773
+ * supplies the defaults (path, domain, secure, same-site); queueing would lose the cookie outside the web group.
774
+ */
775
+ withCookie(name, value, minutes = 0) {
776
+ const jar = optionalCookieJar();
777
+ const cookie = jar !== void 0 ? jar.make(name, value, minutes) : { name, value, minutes, path: "/", secure: false, httpOnly: true, sameSite: "lax" };
778
+ this.#headers.append("Set-Cookie", serializeCookie(cookie));
779
+ return this;
780
+ }
781
+ header(name, value) {
782
+ this.#headers.set(name, value);
783
+ return this;
784
+ }
785
+ toResponse() {
786
+ const headers = new Headers(this.#headers);
787
+ headers.set("Location", this.targetUrl);
788
+ return new Response(null, { status: this.status, headers });
789
+ }
790
+ };
791
+ function pathOf(url2) {
792
+ let parsed;
793
+ try {
794
+ parsed = new URL(url2, "http://localhost");
795
+ } catch {
796
+ return "/";
797
+ }
798
+ return `${parsed.pathname.replace(/^\/+/, "/")}${parsed.search}${parsed.hash}`;
799
+ }
800
+ var HttpRedirector = class {
801
+ to(path, status = 302, headers) {
802
+ return new HttpRedirectResponse(inject(UrlGenerator).to(path), status, headers);
803
+ }
804
+ route(name, parameters, status = 302) {
805
+ return new HttpRedirectResponse(inject(UrlGenerator).route(name, parameters), status);
806
+ }
807
+ back(fallback, status = 302) {
808
+ return new HttpRedirectResponse(inject(UrlGenerator).previous(fallback), status);
809
+ }
810
+ /** Pulls url.intended from the session (put there by guest()), else goes to `fallback`. */
811
+ intended(fallback = "/", status = 302) {
812
+ const path = optionalSession()?.pull("url.intended") ?? fallback;
813
+ return this.to(path, status);
814
+ }
815
+ away(url2, status = 302) {
816
+ return new HttpRedirectResponse(url2, status);
817
+ }
818
+ /**
819
+ * Remembers where a guest was going (a GET page, else the previous URL), then redirects to `path`. Only the path
820
+ * is stored, so intended() goes back on whichever host serves the app, and never to another site's Referer.
821
+ */
822
+ guest(path, status = 302) {
823
+ const session = optionalSession();
824
+ const request2 = currentRequest();
825
+ if (session !== void 0) {
826
+ const intended = request2 !== void 0 && request2.method() === "GET" && !request2.expectsJson() ? request2.fullUrl() : inject(UrlGenerator).previous();
827
+ session.put("url.intended", pathOf(intended));
828
+ }
829
+ return this.to(path, status);
830
+ }
831
+ setIntendedUrl(url2) {
832
+ optionalSession()?.put("url.intended", url2);
833
+ }
834
+ refresh(status = 302) {
835
+ return this.to(currentRequest()?.fullUrl() ?? inject(UrlGenerator).current(), status);
836
+ }
837
+ };
838
+ var KEY = /* @__PURE__ */ Symbol.for("oriel:http.client-address");
839
+ var store = globalThis[KEY] ??= /* @__PURE__ */ new WeakMap();
840
+ function setClientAddress(request2, address) {
841
+ if (address !== void 0 && address !== "") store.set(request2, address);
842
+ }
843
+ function clientAddress(request2) {
844
+ return store.get(request2);
845
+ }
846
+ var TYPES = {
847
+ avif: "image/avif",
848
+ css: "text/css",
849
+ csv: "text/csv",
850
+ doc: "application/msword",
851
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
852
+ gif: "image/gif",
853
+ gz: "application/gzip",
854
+ html: "text/html",
855
+ ico: "image/vnd.microsoft.icon",
856
+ jpg: "image/jpeg",
857
+ js: "text/javascript",
858
+ map: "application/json",
859
+ json: "application/json",
860
+ md: "text/markdown",
861
+ mp3: "audio/mpeg",
862
+ mp4: "video/mp4",
863
+ otf: "font/otf",
864
+ pdf: "application/pdf",
865
+ png: "image/png",
866
+ svg: "image/svg+xml",
867
+ ttf: "font/ttf",
868
+ txt: "text/plain",
869
+ wasm: "application/wasm",
870
+ webm: "video/webm",
871
+ webp: "image/webp",
872
+ woff: "font/woff",
873
+ woff2: "font/woff2",
874
+ xls: "application/vnd.ms-excel",
875
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
876
+ xml: "application/xml",
877
+ zip: "application/zip"
878
+ };
879
+ var ALIASES = { jpeg: "jpg", htm: "html", mjs: "js", text: "txt" };
880
+ var EXTENSIONS = Object.fromEntries(Object.entries(TYPES).map(([extension, type]) => [type, extension]));
881
+ function mimeFromExtension(pathOrExtension) {
882
+ const extension = pathOrExtension.slice(pathOrExtension.lastIndexOf(".") + 1).toLowerCase();
883
+ return TYPES[ALIASES[extension] ?? extension] ?? "application/octet-stream";
884
+ }
885
+ function extensionFromMime(type) {
886
+ return EXTENSIONS[type.split(";")[0].trim().toLowerCase()];
887
+ }
888
+ var FormUploadedFile = class {
889
+ constructor(file) {
890
+ this.file = file;
891
+ this.clientName = file.name ?? "";
892
+ this.mimeType = file.type === "" ? "application/octet-stream" : file.type;
893
+ this.size = file.size;
894
+ }
895
+ file;
896
+ clientName;
897
+ mimeType;
898
+ size;
899
+ /** Guessed from the MIME type, falling back to the client's extension (Laravel's extension()). */
900
+ extension() {
901
+ return extensionFromMime(this.mimeType) ?? this.clientExtension();
902
+ }
903
+ clientExtension() {
904
+ const dot = this.clientName.lastIndexOf(".");
905
+ return dot === -1 ? "" : this.clientName.slice(dot + 1).toLowerCase();
906
+ }
907
+ /** A random 40-character name with the guessed extension. */
908
+ hashName() {
909
+ const extension = this.extension();
910
+ return extension === "" ? Str.random(40) : `${Str.random(40)}.${extension}`;
911
+ }
912
+ arrayBuffer() {
913
+ return this.file.arrayBuffer();
914
+ }
915
+ text() {
916
+ return this.file.text();
917
+ }
918
+ stream() {
919
+ return this.file.stream();
920
+ }
921
+ store(directory, disk) {
922
+ return inject(FilesystemManager).disk(disk).putFile(directory, this);
923
+ }
924
+ storeAs(directory, name, disk) {
925
+ return inject(FilesystemManager).disk(disk).putFileAs(directory, this, name);
926
+ }
927
+ };
928
+ var FORBIDDEN = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
929
+ var INTEGER_KEY = /^(?:0|[1-9]\d*)$/;
930
+ var isPlainObject = (value) => {
931
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
932
+ const prototype = Object.getPrototypeOf(value);
933
+ return prototype === Object.prototype || prototype === null;
934
+ };
935
+ function parseBracketKey(name) {
936
+ const open = name.indexOf("[");
937
+ if (open <= 0 || !name.endsWith("]")) return [name];
938
+ const segments = [name.slice(0, open)];
939
+ const rest = name.slice(open);
940
+ const matches2 = rest.match(/\[[^[\]]*\]/g);
941
+ if (matches2 === null || matches2.join("") !== rest) return [name];
942
+ for (const match of matches2) segments.push(match.slice(1, -1));
943
+ return segments;
944
+ }
945
+ function normalizeLists(value) {
946
+ if (Array.isArray(value)) return value.map(normalizeLists);
947
+ if (!isPlainObject(value)) return value;
948
+ const keys = Object.keys(value);
949
+ const out = {};
950
+ for (const key of keys) out[key] = normalizeLists(value[key]);
951
+ if (keys.length > 0 && keys.every((key, index) => key === String(index))) return keys.map((key) => out[key]);
952
+ return out;
953
+ }
954
+ function fromEntries(entries) {
955
+ const root = {};
956
+ const nextIndex = /* @__PURE__ */ new WeakMap();
957
+ for (const [name, value] of entries) {
958
+ const segments = parseBracketKey(name);
959
+ if (segments.some((segment) => FORBIDDEN.has(segment))) continue;
960
+ let node = root;
961
+ for (let i = 0; i < segments.length; i++) {
962
+ let segment = segments[i];
963
+ const next = nextIndex.get(node) ?? 0;
964
+ if (segment === "") {
965
+ segment = String(next);
966
+ nextIndex.set(node, next + 1);
967
+ } else if (INTEGER_KEY.test(segment) && Number(segment) >= next) {
968
+ nextIndex.set(node, Number(segment) + 1);
969
+ }
970
+ if (i === segments.length - 1) {
971
+ node[segment] = value;
972
+ break;
973
+ }
974
+ if (!isPlainObject(node[segment])) node[segment] = {};
975
+ node = node[segment];
976
+ }
977
+ }
978
+ return normalizeLists(root);
979
+ }
980
+ function sanitize(value) {
981
+ if (Array.isArray(value)) return value.map(sanitize);
982
+ if (!isPlainObject(value)) return value;
983
+ const out = {};
984
+ for (const [key, item] of Object.entries(value)) if (!FORBIDDEN.has(key)) out[key] = sanitize(item);
985
+ return out;
986
+ }
987
+ function mergeRecursive(base, override) {
988
+ const out = { ...base };
989
+ for (const [key, value] of Object.entries(override)) {
990
+ if (FORBIDDEN.has(key)) continue;
991
+ const current = out[key];
992
+ out[key] = (isPlainObject(current) || Array.isArray(current)) && (isPlainObject(value) || Array.isArray(value)) ? normalizeLists(mergeRecursive({ ...current }, { ...value })) : value;
993
+ }
994
+ return out;
995
+ }
996
+ function isBlank(value) {
997
+ if (value === null || value === void 0) return true;
998
+ if (typeof value === "string") return value.trim() === "";
999
+ if (Array.isArray(value)) return value.length === 0;
1000
+ if (isPlainObject(value)) return Object.keys(value).length === 0;
1001
+ return false;
1002
+ }
1003
+ function transformLeaves(tree, transform, prefix = "") {
1004
+ const out = {};
1005
+ for (const [key, value] of Object.entries(tree)) out[key] = transformValue(`${prefix}${key}`, value, transform);
1006
+ return out;
1007
+ }
1008
+ function transformValue(path, value, transform) {
1009
+ if (Array.isArray(value)) return value.map((item, index) => transformValue(`${path}.${index}`, item, transform));
1010
+ if (isPlainObject(value)) return transformLeaves(value, transform, `${path}.`);
1011
+ return transform(path, value);
1012
+ }
1013
+ var TRUE_VALUES = /* @__PURE__ */ new Set(["1", "true", "on", "yes"]);
1014
+ var normalizeIp = (ip) => ip.trim().replace(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/i, "$1");
1015
+ function ipv4ToNumber(ip) {
1016
+ const parts = ip.split(".");
1017
+ if (parts.length !== 4 || parts.some((part) => !/^\d{1,3}$/.test(part) || Number(part) > 255)) return void 0;
1018
+ return parts.reduce((total, part) => total * 256 + Number(part), 0);
1019
+ }
1020
+ function ipMatches(ip, patterns) {
1021
+ const address = normalizeIp(ip);
1022
+ return patterns.some((pattern) => {
1023
+ if (pattern === "*") return true;
1024
+ if (!pattern.includes("/")) return normalizeIp(pattern) === address;
1025
+ const [range, bits] = pattern.split("/");
1026
+ const base = ipv4ToNumber(range);
1027
+ const value = ipv4ToNumber(address);
1028
+ const size = Number(bits);
1029
+ if (base === void 0 || value === void 0 || !(size >= 0 && size <= 32)) return false;
1030
+ const mask = size === 0 ? 0 : ~0 << 32 - size >>> 0;
1031
+ return (base & mask) >>> 0 === (value & mask) >>> 0;
1032
+ });
1033
+ }
1034
+ var IncomingRequest = class {
1035
+ constructor(raw, options = {}) {
1036
+ this.raw = raw;
1037
+ this.#url = new URL(raw.url);
1038
+ this.#trustedProxies = options.trustedProxies ?? [];
1039
+ this.#maxBodySize = options.maxBodySize === void 0 ? 8 * 1024 * 1024 : options.maxBodySize;
1040
+ this.#maxJsonDepth = options.maxJsonDepth === void 0 ? 512 : options.maxJsonDepth;
1041
+ this.#query = fromEntries(this.#url.searchParams.entries());
1042
+ this.#method = raw.method.toUpperCase();
1043
+ }
1044
+ raw;
1045
+ #url;
1046
+ #trustedProxies;
1047
+ #maxBodySize;
1048
+ #maxJsonDepth;
1049
+ #query;
1050
+ #body = {};
1051
+ #files = {};
1052
+ #content = "";
1053
+ #method;
1054
+ #cookies;
1055
+ #loading;
1056
+ #userResolver;
1057
+ /** Reads and parses the body (JSON, urlencoded or multipart) once, then applies method spoofing. */
1058
+ load() {
1059
+ return this.#loading ??= this.#load();
1060
+ }
1061
+ async #load() {
1062
+ const method = this.raw.method.toUpperCase();
1063
+ if (method !== "GET" && method !== "HEAD" && this.raw.body !== null) {
1064
+ const type = (this.raw.headers.get("content-type") ?? "").toLowerCase();
1065
+ const isJson = type.includes("/json") || type.includes("+json");
1066
+ const isUrlencoded = type.startsWith("application/x-www-form-urlencoded");
1067
+ const isMultipart = type.startsWith("multipart/form-data");
1068
+ if (isJson || isUrlencoded || isMultipart) {
1069
+ const bytes = await readBody(this.raw, this.#maxBodySize);
1070
+ if (isJson && this.#maxJsonDepth !== null && exceedsJsonDepth(new TextDecoder().decode(bytes), this.#maxJsonDepth)) {
1071
+ throw new HttpException(400, "The request body is nested too deeply.");
1072
+ }
1073
+ try {
1074
+ if (isJson) {
1075
+ this.#content = new TextDecoder().decode(bytes);
1076
+ this.#body = this.#parseJson(this.#content);
1077
+ } else if (isUrlencoded) {
1078
+ this.#content = new TextDecoder().decode(bytes);
1079
+ this.#body = fromEntries(new URLSearchParams(this.#content).entries());
1080
+ } else {
1081
+ this.#parseForm(await new Response(bytes, { headers: { "content-type": this.raw.headers.get("content-type") ?? "" } }).formData());
1082
+ }
1083
+ } catch {
1084
+ }
1085
+ }
1086
+ }
1087
+ if (method === "POST") {
1088
+ const override = this.raw.headers.get("x-http-method-override") ?? this.#string(this.#body._method) ?? this.#string(this.#query._method);
1089
+ const spoofed = override?.toUpperCase();
1090
+ if (spoofed !== void 0 && /^[A-Z]+$/.test(spoofed) && !["GET", "HEAD", "CONNECT", "TRACE"].includes(spoofed)) {
1091
+ this.#method = spoofed;
1092
+ }
1093
+ }
1094
+ return this;
1095
+ }
1096
+ #parseJson(text) {
1097
+ let parsed;
1098
+ try {
1099
+ parsed = JSON.parse(text);
1100
+ } catch {
1101
+ return {};
1102
+ }
1103
+ if (Array.isArray(parsed)) return sanitize({ ...parsed });
1104
+ return isPlainObject(parsed) ? sanitize(parsed) : {};
1105
+ }
1106
+ #parseForm(form) {
1107
+ const fields = [];
1108
+ const files = [];
1109
+ for (const [name, value] of form.entries()) {
1110
+ if (typeof value === "string") fields.push([name, value]);
1111
+ else if (!((value.name ?? "") === "" && value.size === 0)) files.push([name, new FormUploadedFile(value)]);
1112
+ }
1113
+ this.#body = fromEntries(fields);
1114
+ this.#files = fromEntries(files);
1115
+ }
1116
+ #string(value) {
1117
+ return typeof value === "string" && value !== "" ? value : void 0;
1118
+ }
1119
+ method() {
1120
+ return this.#method;
1121
+ }
1122
+ isMethod(method) {
1123
+ return this.#method === method.toUpperCase();
1124
+ }
1125
+ /** The scheme and host, honouring X-Forwarded-Proto/Host from trusted proxies. */
1126
+ root() {
1127
+ const trusted = this.#fromTrustedProxy();
1128
+ const proto = trusted ? this.raw.headers.get("x-forwarded-proto")?.split(",")[0]?.trim() : void 0;
1129
+ const host = trusted ? this.raw.headers.get("x-forwarded-host")?.split(",")[0]?.trim() : void 0;
1130
+ const scheme = proto === "https" || proto === "http" ? proto : this.#url.protocol.slice(0, -1);
1131
+ return `${scheme}://${host !== void 0 && host !== "" ? host : this.#url.host}`;
1132
+ }
1133
+ /** The URL without the query string or a trailing slash. */
1134
+ url() {
1135
+ return `${this.root()}${this.#url.pathname}`.replace(/\/+$/, "");
1136
+ }
1137
+ fullUrl() {
1138
+ return this.#url.search === "" ? this.url() : `${this.url()}${this.#url.search}`;
1139
+ }
1140
+ /** 'posts/1', or '/' for the root. */
1141
+ path() {
1142
+ const trimmed = this.#url.pathname.replace(/^\/+|\/+$/g, "");
1143
+ return trimmed === "" ? "/" : trimmed;
1144
+ }
1145
+ decodedPath() {
1146
+ try {
1147
+ return decodeURIComponent(this.path());
1148
+ } catch {
1149
+ return this.path();
1150
+ }
1151
+ }
1152
+ secure() {
1153
+ return this.root().startsWith("https:");
1154
+ }
1155
+ /** request.is('admin/*'): wildcard match against the decoded path. */
1156
+ is(...patterns) {
1157
+ const path = this.decodedPath();
1158
+ return patterns.some((pattern) => {
1159
+ const trimmed = pattern.replace(/^\/+|\/+$/g, "");
1160
+ return Str.is(trimmed === "" ? "/" : trimmed, path);
1161
+ });
1162
+ }
1163
+ routeIs(...names) {
1164
+ const name = this.#currentRoute()?.route.getName();
1165
+ return name !== void 0 && names.some((pattern) => Str.is(pattern, name));
1166
+ }
1167
+ header(name, defaultValue) {
1168
+ return this.raw.headers.get(name) ?? defaultValue;
1169
+ }
1170
+ bearerToken() {
1171
+ const header = this.header("authorization", "");
1172
+ const position = header.toLowerCase().lastIndexOf("bearer ");
1173
+ if (position === -1) return void 0;
1174
+ const token2 = header.slice(position + 7).split(",")[0].trim();
1175
+ return token2 === "" ? void 0 : token2;
1176
+ }
1177
+ userAgent() {
1178
+ return this.header("user-agent");
1179
+ }
1180
+ /** The socket address, or the right-most untrusted X-Forwarded-For entry when the socket is a trusted proxy. */
1181
+ ip() {
1182
+ const socket = clientAddress(this.raw);
1183
+ if (socket === void 0) return void 0;
1184
+ const address = normalizeIp(socket);
1185
+ if (!this.#fromTrustedProxy()) return address;
1186
+ const chain = (this.raw.headers.get("x-forwarded-for") ?? "").split(",").map((entry) => normalizeIp(entry)).filter((entry) => entry !== "");
1187
+ for (let i = chain.length - 1; i >= 0; i--) if (!ipMatches(chain[i], this.#trustedProxies)) return chain[i];
1188
+ return chain[0] ?? address;
1189
+ }
1190
+ #fromTrustedProxy() {
1191
+ const socket = clientAddress(this.raw);
1192
+ return socket !== void 0 && this.#trustedProxies.length > 0 && ipMatches(socket, this.#trustedProxies);
1193
+ }
1194
+ ajax() {
1195
+ return this.header("x-requested-with") === "XMLHttpRequest";
1196
+ }
1197
+ pjax() {
1198
+ return this.header("x-pjax") === "true";
1199
+ }
1200
+ /** The first Accept type is JSON. */
1201
+ wantsJson() {
1202
+ const first = this.header("accept", "").split(",")[0].trim();
1203
+ return first.includes("/json") || first.includes("+json");
1204
+ }
1205
+ /** Laravel: (ajax and not pjax and accepts anything) or wantsJson. */
1206
+ expectsJson() {
1207
+ const first = this.header("accept", "").split(",")[0].split(";")[0].trim();
1208
+ const acceptsAny = first === "" || first === "*/*" || first === "*";
1209
+ return this.ajax() && !this.pjax() && acceptsAny || this.wantsJson();
1210
+ }
1211
+ /** The raw body text for JSON and urlencoded requests ('' otherwise; the raw stream stays unread for other types). */
1212
+ content() {
1213
+ return this.#content;
1214
+ }
1215
+ query(key, defaultValue) {
1216
+ return Arr.get(this.#query, key, defaultValue);
1217
+ }
1218
+ /** Body wins over the query string (PHP's $body + $query). Files are not input. */
1219
+ input(key, defaultValue) {
1220
+ return Arr.get({ ...this.#query, ...this.#body }, key, defaultValue);
1221
+ }
1222
+ /** Input plus files (array_replace_recursive). */
1223
+ all() {
1224
+ return mergeRecursive({ ...this.#query, ...this.#body }, this.#files);
1225
+ }
1226
+ only(...keys) {
1227
+ const all = this.all();
1228
+ const out = {};
1229
+ for (const key of keys.flat()) if (Arr.has(all, key)) Arr.set(out, key, Arr.get(all, key));
1230
+ return out;
1231
+ }
1232
+ except(...keys) {
1233
+ const all = structuredCloneInput(this.all());
1234
+ for (const key of keys.flat()) Arr.forget(all, key);
1235
+ return all;
1236
+ }
1237
+ has(...keys) {
1238
+ const all = this.all();
1239
+ return keys.flat().every((key) => Arr.has(all, key));
1240
+ }
1241
+ hasAny(...keys) {
1242
+ const all = this.all();
1243
+ return keys.flat().some((key) => Arr.has(all, key));
1244
+ }
1245
+ missing(...keys) {
1246
+ return !this.has(...keys);
1247
+ }
1248
+ filled(key) {
1249
+ return !isBlank(Arr.get(this.all(), key));
1250
+ }
1251
+ string(key, defaultValue = "") {
1252
+ const value = this.input(key);
1253
+ return value === void 0 || value === null ? defaultValue : String(value);
1254
+ }
1255
+ /** PHP's FILTER_VALIDATE_BOOLEAN: '1', 'true', 'on', 'yes' (any case) and true are true. */
1256
+ boolean(key, defaultValue = false) {
1257
+ const value = this.input(key);
1258
+ if (value === void 0) return defaultValue;
1259
+ if (typeof value === "boolean") return value;
1260
+ if (typeof value === "number") return value === 1;
1261
+ return typeof value === "string" && TRUE_VALUES.has(value.trim().toLowerCase());
1262
+ }
1263
+ /** PHP's intval: leading digits, else 0. */
1264
+ integer(key, defaultValue = 0) {
1265
+ const value = this.input(key);
1266
+ if (value === void 0) return defaultValue;
1267
+ if (typeof value === "number") return Number.isFinite(value) ? Math.trunc(value) : 0;
1268
+ if (typeof value === "boolean") return value ? 1 : 0;
1269
+ const parsed = Number.parseInt(String(value).trim(), 10);
1270
+ return Number.isNaN(parsed) ? 0 : parsed;
1271
+ }
1272
+ /** Adds or replaces top-level body values. */
1273
+ merge(values) {
1274
+ this.#body = { ...this.#body, ...sanitize(values) };
1275
+ return this;
1276
+ }
1277
+ /** Rewrites every scalar in the query and the body (TrimStrings and ConvertEmptyStringsToNull use this). */
1278
+ transformInput(transform) {
1279
+ this.#query = transformLeaves(this.#query, transform);
1280
+ this.#body = transformLeaves(this.#body, transform);
1281
+ return this;
1282
+ }
1283
+ file(key) {
1284
+ const value = Arr.get(this.#files, key);
1285
+ return value instanceof FormUploadedFile ? value : void 0;
1286
+ }
1287
+ hasFile(key) {
1288
+ const value = Arr.get(this.#files, key);
1289
+ return value instanceof FormUploadedFile || Array.isArray(value) && value.some((item) => item instanceof FormUploadedFile);
1290
+ }
1291
+ allFiles() {
1292
+ return { ...this.#files };
1293
+ }
1294
+ cookie(name) {
1295
+ return this.cookies()[name];
1296
+ }
1297
+ cookies() {
1298
+ if (this.#cookies === void 0) {
1299
+ const cookies = {};
1300
+ for (const part of (this.raw.headers.get("cookie") ?? "").split(";")) {
1301
+ const index = part.indexOf("=");
1302
+ if (index === -1) continue;
1303
+ const name = part.slice(0, index).trim();
1304
+ if (name === "" || Object.hasOwn(cookies, name)) continue;
1305
+ let value = part.slice(index + 1).trim();
1306
+ if (value.startsWith('"') && value.endsWith('"')) value = value.slice(1, -1);
1307
+ try {
1308
+ cookies[name] = decodeURIComponent(value);
1309
+ } catch {
1310
+ cookies[name] = value;
1311
+ }
1312
+ }
1313
+ this.#cookies = cookies;
1314
+ }
1315
+ return { ...this.#cookies };
1316
+ }
1317
+ /** Replaces the parsed cookies (EncryptCookies calls this after decrypting). */
1318
+ setCookies(cookies) {
1319
+ this.#cookies = { ...cookies };
1320
+ }
1321
+ /** One route parameter (after model binding), or all of them. */
1322
+ route(key) {
1323
+ const parameters = this.#currentRoute()?.parameters ?? {};
1324
+ return key === void 0 ? { ...parameters } : parameters[key];
1325
+ }
1326
+ session() {
1327
+ return inject(Session);
1328
+ }
1329
+ user(guard) {
1330
+ return this.#userResolver?.(guard);
1331
+ }
1332
+ setUserResolver(resolver) {
1333
+ this.#userResolver = resolver;
1334
+ }
1335
+ /** Validates all() through the ValidationFactory; throws its Validation error on failure. */
1336
+ validate(rules, messages, attributes) {
1337
+ return inject(ValidationFactory).make(this.all(), rules, messages, attributes).validated();
1338
+ }
1339
+ #currentRoute() {
1340
+ const container = currentContainer();
1341
+ return container?.bound(CurrentRoute) ? container.make(CurrentRoute) : void 0;
1342
+ }
1343
+ };
1344
+ function structuredCloneInput(tree) {
1345
+ const out = {};
1346
+ for (const [key, value] of Object.entries(tree)) {
1347
+ out[key] = isPlainObject(value) ? structuredCloneInput(value) : Array.isArray(value) ? [...value] : value;
1348
+ }
1349
+ return out;
1350
+ }
1351
+ function request(key, defaultValue) {
1352
+ const current = inject(HttpRequest);
1353
+ return key === void 0 ? current : current.input(key, defaultValue);
1354
+ }
1355
+ function response(content, status, headers) {
1356
+ const factory = inject(ResponseFactory);
1357
+ return content === void 0 ? factory : factory.make(content, status, headers);
1358
+ }
1359
+ function redirect(to, status) {
1360
+ const redirector = inject(Redirector);
1361
+ return to === void 0 ? redirector : redirector.to(to, status);
1362
+ }
1363
+ function back(fallback, status) {
1364
+ return inject(Redirector).back(fallback, status);
1365
+ }
1366
+ function url(path, query, absolute) {
1367
+ const generator = inject(UrlGenerator);
1368
+ return path === void 0 ? generator : generator.to(path, query, absolute);
1369
+ }
1370
+ function route(name, parameters, absolute) {
1371
+ return inject(UrlGenerator).route(name, parameters, absolute);
1372
+ }
1373
+ function abort(status, message = "", headers = {}) {
1374
+ if (status instanceof Response) throw new HttpResponseException(status);
1375
+ throw new HttpException(status, message, headers);
1376
+ }
1377
+ function abortIf(condition, status, message = "", headers = {}) {
1378
+ if (condition) abort(status, message, headers);
1379
+ }
1380
+ function abortUnless(condition, status, message = "", headers = {}) {
1381
+ if (!condition) abort(status, message, headers);
1382
+ }
1383
+ function contentDisposition(type, name) {
1384
+ const fallback = name.replace(/[^\x20-\x7e]/g, "_").replace(/["\\]/g, "\\$&");
1385
+ const header = `${type}; filename="${fallback}"`;
1386
+ return /^[\x20-\x7e]*$/.test(name) ? header : `${header}; filename*=UTF-8''${encodeURIComponent(name)}`;
1387
+ }
1388
+ var HttpResponseFactory = class {
1389
+ /** Strings default to text/html; charset=utf-8, as in Laravel. */
1390
+ make(content = null, status = 200, headers) {
1391
+ const merged = new Headers(headers);
1392
+ if (typeof content === "string" && !merged.has("content-type")) merged.set("content-type", "text/html; charset=utf-8");
1393
+ return new Response(content, { status, headers: merged });
1394
+ }
1395
+ json(data, status = 200, headers) {
1396
+ const merged = new Headers(headers);
1397
+ if (!merged.has("content-type")) merged.set("content-type", "application/json");
1398
+ return new Response(JSON.stringify(data === void 0 ? null : data), { status, headers: merged });
1399
+ }
1400
+ noContent(status = 204) {
1401
+ return new Response(null, { status });
1402
+ }
1403
+ /** Renders a view (through the ViewFactory) into an HTML response. */
1404
+ async view(name, data = {}, status = 200, headers) {
1405
+ const html = await inject(ViewFactory).make(name, data).render();
1406
+ return this.make(html, status, headers);
1407
+ }
1408
+ /** Streams a file as an attachment. A missing file is a 404. */
1409
+ download(path, name = basename(path), headers) {
1410
+ return this.#file(path, "attachment", name, headers);
1411
+ }
1412
+ /** Streams a file for the browser to display (Laravel's response()->file()). */
1413
+ file(path, headers) {
1414
+ return this.#file(path, "inline", basename(path), headers);
1415
+ }
1416
+ stream(body, status = 200, headers) {
1417
+ return new Response(body, { status, headers });
1418
+ }
1419
+ async #file(path, disposition, name, headers) {
1420
+ const info = await stat(path).catch(() => void 0);
1421
+ if (info === void 0 || !info.isFile()) throw new HttpException(404);
1422
+ const merged = new Headers(headers);
1423
+ if (!merged.has("content-type")) merged.set("content-type", mimeFromExtension(name));
1424
+ merged.set("content-length", String(info.size));
1425
+ merged.set("content-disposition", contentDisposition(disposition, name));
1426
+ const body = Readable.toWeb(createReadStream(path));
1427
+ return new Response(body, { status: 200, headers: merged });
1428
+ }
1429
+ };
1430
+ var ConvertEmptyStringsToNull = class {
1431
+ async handle(request2, next) {
1432
+ if (request2 instanceof IncomingRequest) request2.transformInput((_key, value) => value === "" ? null : value);
1433
+ return next(request2);
1434
+ }
1435
+ };
1436
+ var TRIM = /^[\s\uFEFF\u200B\u200E]+|[\s\uFEFF\u200B\u200E]+$/g;
1437
+ var TrimStrings = class {
1438
+ /** Dotted input keys left untouched. Override in a subclass to change. */
1439
+ except = ["current_password", "password", "password_confirmation"];
1440
+ async handle(request2, next) {
1441
+ if (request2 instanceof IncomingRequest) {
1442
+ request2.transformInput(
1443
+ (key, value) => typeof value === "string" && !this.except.includes(key) ? value.replace(TRIM, "") : value
1444
+ );
1445
+ }
1446
+ return next(request2);
1447
+ }
1448
+ };
1449
+ var plainServerError = () => new Response("Server Error", { status: 500, headers: { "content-type": "text/plain; charset=utf-8" } });
1450
+ var Kernel = class {
1451
+ constructor(app) {
1452
+ this.app = app;
1453
+ }
1454
+ app;
1455
+ #middleware = [TrimStrings, ConvertEmptyStringsToNull];
1456
+ #requests = /* @__PURE__ */ new WeakMap();
1457
+ getGlobalMiddleware() {
1458
+ return [...this.#middleware];
1459
+ }
1460
+ setGlobalMiddleware(middleware) {
1461
+ this.#middleware = [...middleware];
1462
+ return this;
1463
+ }
1464
+ appendMiddleware(...middleware) {
1465
+ for (const cls of middleware) if (!this.#middleware.includes(cls)) this.#middleware.push(cls);
1466
+ return this;
1467
+ }
1468
+ prependMiddleware(...middleware) {
1469
+ for (const cls of [...middleware].reverse()) if (!this.#middleware.includes(cls)) this.#middleware.unshift(cls);
1470
+ return this;
1471
+ }
1472
+ hasMiddleware(middleware) {
1473
+ return this.#middleware.includes(middleware);
1474
+ }
1475
+ /** Runs in the current request scope (Application.handle() opens one); called directly, it opens its own. */
1476
+ handle(raw) {
1477
+ const current = currentContainer();
1478
+ const scope = current !== void 0 && current !== this.app ? current : this.app.createScope();
1479
+ return scope.run(() => this.#handle(raw, scope));
1480
+ }
1481
+ async #handle(raw, scope) {
1482
+ let request2;
1483
+ let response2;
1484
+ try {
1485
+ request2 = new IncomingRequest(raw, {
1486
+ trustedProxies: this.#trustedProxies(),
1487
+ maxBodySize: this.#maxBodySize(),
1488
+ maxJsonDepth: this.#maxJsonDepth()
1489
+ });
1490
+ scope.instance(HttpRequest, request2);
1491
+ const state = { scope, request: request2, middleware: [] };
1492
+ this.#requests.set(raw, state);
1493
+ await request2.load();
1494
+ response2 = await this.#sendThroughGlobal(state);
1495
+ } catch (error) {
1496
+ request2 ??= this.#bareRequest(scope, raw);
1497
+ if (request2 !== void 0) {
1498
+ response2 = await this.#renderLast(scope, request2, error);
1499
+ } else {
1500
+ await this.#report(scope, error);
1501
+ response2 = plainServerError();
1502
+ }
1503
+ }
1504
+ if (raw.method.toUpperCase() === "HEAD" && response2.body !== null) {
1505
+ await response2.body.cancel().catch(() => void 0);
1506
+ response2 = new Response(null, { status: response2.status, statusText: response2.statusText, headers: response2.headers });
1507
+ }
1508
+ return response2;
1509
+ }
1510
+ /** The request without options, for rendering an error raised while the real one was being built. */
1511
+ #bareRequest(scope, raw) {
1512
+ try {
1513
+ const request2 = new IncomingRequest(raw);
1514
+ scope.instance(HttpRequest, request2);
1515
+ return request2;
1516
+ } catch {
1517
+ return void 0;
1518
+ }
1519
+ }
1520
+ /** Runs terminate() on the global and route middleware (inside the request's scope), then Application.terminate(), inside the same scope, so terminating callbacks see the request's bindings. */
1521
+ async terminate(raw, response2) {
1522
+ const state = this.#requests.get(raw);
1523
+ if (state !== void 0) {
1524
+ this.#requests.delete(raw);
1525
+ await state.scope.run(async () => {
1526
+ const route2 = state.scope.bound(CurrentRoute) ? state.scope.make(CurrentRoute).middleware : [];
1527
+ for (const middleware of [...state.middleware, ...route2]) {
1528
+ if (typeof middleware.terminate !== "function") continue;
1529
+ try {
1530
+ await middleware.terminate(state.request, response2);
1531
+ } catch (error) {
1532
+ await this.#report(state.scope, error);
1533
+ }
1534
+ }
1535
+ await this.app.terminate();
1536
+ });
1537
+ return;
1538
+ }
1539
+ await this.app.terminate();
1540
+ }
1541
+ #sendThroughGlobal(state) {
1542
+ const { scope, request: request2 } = state;
1543
+ const pipes = this.#middleware.map((cls) => {
1544
+ const instance = scope.make(cls);
1545
+ state.middleware.push(instance);
1546
+ return (passable, next) => this.#guard(scope, passable, () => instance.handle(passable, next));
1547
+ });
1548
+ return scope.make(PipelineFactory).make().send(request2).through(pipes).then((passable) => this.#guard(scope, passable, () => this.#dispatch(scope, passable)));
1549
+ }
1550
+ #dispatch(scope, request2) {
1551
+ return scope.make(Router).dispatch(request2);
1552
+ }
1553
+ /** Converts a Responsable result, and renders a thrown error at this stage (Laravel's Routing\Pipeline). */
1554
+ async #guard(scope, request2, run) {
1555
+ try {
1556
+ const result = await run();
1557
+ if (result instanceof Response) return result;
1558
+ const responsable = result;
1559
+ if (typeof responsable?.toResponse === "function") return await responsable.toResponse(request2);
1560
+ throw new HttpServerError("Middleware must return a Response.");
1561
+ } catch (error) {
1562
+ if (!scope.bound(ExceptionHandler)) throw error;
1563
+ const handler = scope.make(ExceptionHandler);
1564
+ await handler.report(error);
1565
+ return handler.render(request2, error);
1566
+ }
1567
+ }
1568
+ /** Last resort: the handler failed or isn't bound. Never throws. */
1569
+ async #renderLast(scope, request2, error) {
1570
+ if (scope.bound(ExceptionHandler)) {
1571
+ try {
1572
+ const handler = scope.make(ExceptionHandler);
1573
+ await handler.report(error);
1574
+ return await handler.render(request2, error);
1575
+ } catch (renderError) {
1576
+ console.error(renderError);
1577
+ }
1578
+ } else {
1579
+ console.error(error);
1580
+ }
1581
+ return plainServerError();
1582
+ }
1583
+ async #report(scope, error) {
1584
+ try {
1585
+ if (scope.bound(ExceptionHandler)) await scope.make(ExceptionHandler).report(error);
1586
+ else console.error(error);
1587
+ } catch (reportError) {
1588
+ console.error(reportError);
1589
+ }
1590
+ }
1591
+ /** config('http.max_body_size'): bytes, or null for no limit. Anything else is a configuration error. */
1592
+ #maxBodySize() {
1593
+ if (!this.app.bound(ConfigRepository)) return 8 * 1024 * 1024;
1594
+ const value = this.app.make(ConfigRepository).get("http.max_body_size", 8 * 1024 * 1024);
1595
+ if (value === null) return null;
1596
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) return value;
1597
+ throw new HttpServerError(
1598
+ `Invalid http.max_body_size ${JSON.stringify(value) ?? String(value)}: use a number of bytes (0 or more), or null for no limit.`
1599
+ );
1600
+ }
1601
+ /** config('http.max_json_depth'): how deep a JSON body may nest, or null for no limit. */
1602
+ #maxJsonDepth() {
1603
+ if (!this.app.bound(ConfigRepository)) return 512;
1604
+ const value = this.app.make(ConfigRepository).get("http.max_json_depth", 512);
1605
+ if (value === null) return null;
1606
+ if (typeof value === "number" && Number.isInteger(value) && value >= 1) return value;
1607
+ throw new HttpServerError(
1608
+ `Invalid http.max_json_depth ${JSON.stringify(value) ?? String(value)}: use a whole number (1 or more), or null for no limit.`
1609
+ );
1610
+ }
1611
+ /** config('http.trusted_proxies'): an array, or a comma-separated string such as '*' or '10.0.0.0/8,::1'. */
1612
+ #trustedProxies() {
1613
+ if (!this.app.bound(ConfigRepository)) return [];
1614
+ const value = this.app.make(ConfigRepository).get("http.trusted_proxies", []);
1615
+ if (typeof value === "string") return value.split(",").map((entry) => entry.trim()).filter((entry) => entry !== "");
1616
+ return Array.isArray(value) ? value.map(String) : [];
1617
+ }
1618
+ };
1619
+ var MiddlewareConfiguration = class {
1620
+ constructor(kernel, router, config) {
1621
+ this.kernel = kernel;
1622
+ this.router = router;
1623
+ this.config = config;
1624
+ }
1625
+ kernel;
1626
+ router;
1627
+ config;
1628
+ append(...middleware) {
1629
+ this.kernel.appendMiddleware(...middleware);
1630
+ return this;
1631
+ }
1632
+ prepend(...middleware) {
1633
+ this.kernel.prependMiddleware(...middleware);
1634
+ return this;
1635
+ }
1636
+ web(options) {
1637
+ return this.#modifyGroup("web", options);
1638
+ }
1639
+ api(options) {
1640
+ return this.#modifyGroup("api", options);
1641
+ }
1642
+ alias(aliases) {
1643
+ for (const [name, middleware] of Object.entries(aliases)) this.router.aliasMiddleware(name, middleware);
1644
+ return this;
1645
+ }
1646
+ group(name, middleware) {
1647
+ this.router.middlewareGroup(name, middleware);
1648
+ return this;
1649
+ }
1650
+ /** Stored in config('http.csrf_except'), which the Session component's VerifyCsrfToken reads. */
1651
+ validateCsrfTokens(options) {
1652
+ const current = this.config?.get("http.csrf_except", []) ?? [];
1653
+ this.config?.set("http.csrf_except", [...current, ...options.except]);
1654
+ return this;
1655
+ }
1656
+ /** Stored in config('http.redirect_guests_to'), which the Auth middleware and the exception handler read. */
1657
+ redirectGuestsTo(url2) {
1658
+ this.config?.set("http.redirect_guests_to", url2);
1659
+ return this;
1660
+ }
1661
+ /** Laravel 11's redirectUsersTo(): stored in config('http.redirect_users_to') for Auth's RedirectIfAuthenticated. */
1662
+ redirectUsersTo(url2) {
1663
+ this.config?.set("http.redirect_users_to", url2);
1664
+ return this;
1665
+ }
1666
+ /** Laravel's order: prepends, then the group minus removals, then appends. */
1667
+ #modifyGroup(name, { append = [], prepend = [], remove = [] }) {
1668
+ const current = this.router.getMiddlewareGroups()[name] ?? [];
1669
+ const moved = /* @__PURE__ */ new Set([...append, ...prepend, ...remove]);
1670
+ this.router.middlewareGroup(name, [...prepend, ...current.filter((entry) => !moved.has(entry)), ...append]);
1671
+ return this;
1672
+ }
1673
+ };
1674
+ function applyMiddlewareCallbacks(app, kernel) {
1675
+ if (!app.bound(MiddlewareCallbacks)) return;
1676
+ const callbacks = app.make(MiddlewareCallbacks);
1677
+ if (callbacks.length === 0) return;
1678
+ const config = app.bound(ConfigRepository) ? app.make(ConfigRepository) : void 0;
1679
+ const configurator = new MiddlewareConfiguration(kernel, app.make(Router), config);
1680
+ for (const callback of callbacks) callback(configurator);
1681
+ }
1682
+ var STATUS_TEXTS = {
1683
+ 400: "Bad Request",
1684
+ 401: "Unauthorized",
1685
+ 402: "Payment Required",
1686
+ 403: "Forbidden",
1687
+ 404: "Not Found",
1688
+ 405: "Method Not Allowed",
1689
+ 408: "Request Timeout",
1690
+ 409: "Conflict",
1691
+ 410: "Gone",
1692
+ 413: "Content Too Large",
1693
+ 415: "Unsupported Media Type",
1694
+ 419: "Page Expired",
1695
+ 422: "Unprocessable Content",
1696
+ 429: "Too Many Requests",
1697
+ 500: "Server Error",
1698
+ 502: "Bad Gateway",
1699
+ 503: "Service Unavailable",
1700
+ 504: "Gateway Timeout"
1701
+ };
1702
+ function statusText(status) {
1703
+ return STATUS_TEXTS[status] ?? (status >= 500 ? "Server Error" : "Error");
1704
+ }
1705
+ function escapeHtml(value) {
1706
+ return value.replace(/[&<>"']/g, (char) => `&#${char.charCodeAt(0)};`);
1707
+ }
1708
+ function minimalPage(status, message) {
1709
+ const title = escapeHtml(message);
1710
+ return `<!DOCTYPE html>
1711
+ <html lang="en">
1712
+ <head>
1713
+ <meta charset="utf-8">
1714
+ <meta name="viewport" content="width=device-width, initial-scale=1">
1715
+ <title>${title}</title>
1716
+ <style>body{margin:0;font-family:ui-sans-serif,system-ui,sans-serif;background:#f7fafc;color:#1a202c;display:flex;align-items:center;justify-content:center;min-height:100vh}.code{padding:0 1rem;border-right:1px solid #a0aec0;font-size:1.125rem}.message{padding:0 1rem;font-size:1.125rem;text-transform:uppercase;letter-spacing:.05em}</style>
1717
+ </head>
1718
+ <body><div class="code">${status}</div><div class="message">${title}</div></body>
1719
+ </html>`;
1720
+ }
1721
+ function stackFrames(error) {
1722
+ const frames = [];
1723
+ for (const raw of (error.stack ?? "").split("\n").slice(1)) {
1724
+ const text = raw.trim();
1725
+ const match = /(?:\(|@|at )((?:file:\/\/)?(?:\/|[A-Za-z]:\\)[^()]*?):(\d+):(\d+)\)?$/.exec(text);
1726
+ if (match === null) continue;
1727
+ const location = match[1];
1728
+ const file = location.startsWith("file://") ? fileURLToPath(location) : location;
1729
+ frames.push({ file, line: Number(match[2]), column: Number(match[3]), text });
1730
+ }
1731
+ return frames;
1732
+ }
1733
+ async function excerpt(frame, radius = 5) {
1734
+ const source = await readFile(frame.file, "utf8").catch(() => void 0);
1735
+ if (source === void 0) return void 0;
1736
+ const lines = source.split("\n");
1737
+ const start = Math.max(1, frame.line - radius);
1738
+ const end = Math.min(lines.length, frame.line + radius);
1739
+ const rows = [];
1740
+ for (let number = start; number <= end; number++) {
1741
+ const current = number === frame.line ? ' class="current"' : "";
1742
+ rows.push(`<tr${current}><td class="n">${number}</td><td><code>${escapeHtml(lines[number - 1] ?? "")}</code></td></tr>`);
1743
+ }
1744
+ return `<table class="source">${rows.join("")}</table>`;
1745
+ }
1746
+ async function debugPage(error, method, url2) {
1747
+ const err = error instanceof Error ? error : new Error(String(error));
1748
+ const frames = stackFrames(err);
1749
+ const appFrame = frames.find((frame) => !frame.file.includes("node_modules") && !frame.file.startsWith("node:")) ?? frames[0];
1750
+ const source = appFrame === void 0 ? void 0 : await excerpt(appFrame);
1751
+ const causes = [];
1752
+ for (let cause = err.cause, depth = 0; cause !== void 0 && depth < 5; cause = cause.cause, depth++) {
1753
+ causes.push(`<li>${escapeHtml(cause instanceof Error ? `${cause.name}: ${cause.message}` : String(cause))}</li>`);
1754
+ }
1755
+ return `<!DOCTYPE html>
1756
+ <html lang="en">
1757
+ <head>
1758
+ <meta charset="utf-8">
1759
+ <title>${escapeHtml(err.message || err.name)}</title>
1760
+ <style>body{margin:0;font-family:ui-sans-serif,system-ui,sans-serif;background:#f3f4f6;color:#111827}main{max-width:1100px;margin:2rem auto;padding:0 1rem}header{background:#fff;border-radius:.5rem;padding:1.5rem;box-shadow:0 1px 3px #0001}.class{color:#dc2626;font-weight:600}.message{font-size:1.5rem;margin:.5rem 0}.request{color:#6b7280}section{background:#fff;border-radius:.5rem;padding:1rem;margin-top:1rem;box-shadow:0 1px 3px #0001;overflow:auto}.source td{padding:0 .5rem;white-space:pre;font-size:.85rem}.source .n{color:#9ca3af;text-align:right}.source .current{background:#fee2e2}ol{font-family:ui-monospace,monospace;font-size:.85rem}</style>
1761
+ </head>
1762
+ <body><main>
1763
+ <header><div class="class">${escapeHtml(err.name)}</div><div class="message">${escapeHtml(err.message)}</div><div class="request">${escapeHtml(method)} ${escapeHtml(url2)}</div></header>
1764
+ ${appFrame === void 0 ? "" : `<section><div>${escapeHtml(appFrame.file)}:${appFrame.line}</div>${source ?? ""}</section>`}
1765
+ <section><ol>${frames.map((frame) => `<li>${escapeHtml(frame.text)}</li>`).join("")}</ol></section>
1766
+ ${causes.length === 0 ? "" : `<section><div>Caused by</div><ul>${causes.join("")}</ul></section>`}
1767
+ </main></body>
1768
+ </html>`;
1769
+ }
1770
+ var NOT_REPORTED = /* @__PURE__ */ new Set(["Http", "ModelNotFound", "Validation", "Authentication", "Authorization", "TokenMismatch"]);
1771
+ var HTTP_KINDS = /* @__PURE__ */ new Set(["Http", "ModelNotFound", "Authorization", "TokenMismatch"]);
1772
+ var kindOf = (error) => typeof error === "object" && error !== null && typeof error.kind === "string" ? error.kind : void 0;
1773
+ function statusFor(error) {
1774
+ const status = error?.status;
1775
+ switch (kindOf(error)) {
1776
+ case "Http":
1777
+ return status ?? 500;
1778
+ case "ModelNotFound":
1779
+ return 404;
1780
+ case "Authorization":
1781
+ return status ?? 403;
1782
+ case "TokenMismatch":
1783
+ return 419;
1784
+ case "Authentication":
1785
+ return 401;
1786
+ case "Validation":
1787
+ return status ?? 422;
1788
+ case "Server":
1789
+ return status ?? 500;
1790
+ default:
1791
+ return 500;
1792
+ }
1793
+ }
1794
+ function summarizeValidation(errors) {
1795
+ const messages = Object.values(errors).flat();
1796
+ if (messages.length === 0) return "The given data was invalid.";
1797
+ const more = messages.length - 1;
1798
+ return more === 0 ? messages[0] : `${messages[0]} (and ${more} more ${more === 1 ? "error" : "errors"})`;
1799
+ }
1800
+ var isHttpLike = (error) => {
1801
+ const kind = kindOf(error);
1802
+ return kind !== void 0 && HTTP_KINDS.has(kind);
1803
+ };
1804
+ var ErrorHandler = class {
1805
+ constructor(app) {
1806
+ this.app = app;
1807
+ }
1808
+ app;
1809
+ /** Input keys never flashed back on a validation redirect. */
1810
+ dontFlash = ["current_password", "password", "password_confirmation"];
1811
+ #reportCallbacks = [];
1812
+ #renderCallbacks = [];
1813
+ #dontReport = [];
1814
+ #reported = /* @__PURE__ */ new WeakSet();
1815
+ #jsonWhen;
1816
+ reportUsing(callback) {
1817
+ this.#reportCallbacks.push(callback);
1818
+ }
1819
+ renderUsing(callback) {
1820
+ this.#renderCallbacks.push(callback);
1821
+ }
1822
+ ignore(...classes) {
1823
+ this.#dontReport.push(...classes);
1824
+ }
1825
+ /** Laravel's shouldRenderJsonWhen(): the callback decides JSON or HTML instead of the Accept header. */
1826
+ shouldRenderJsonWhen(callback) {
1827
+ this.#jsonWhen = callback;
1828
+ }
1829
+ /** Laravel's shouldReturnJson(): the shouldRenderJsonWhen() callback when set, else request.expectsJson(). */
1830
+ shouldReturnJson(request2, error) {
1831
+ return this.#jsonWhen !== void 0 ? this.#jsonWhen(request2, error) : request2.expectsJson();
1832
+ }
1833
+ shouldReport(error) {
1834
+ const kind = kindOf(error);
1835
+ if (kind !== void 0 && NOT_REPORTED.has(kind)) return false;
1836
+ return !this.#dontReport.some((cls) => error instanceof cls);
1837
+ }
1838
+ /** Report callbacks first (returning false stops the default log), then LogManager.error(), else console.error. Once per error. */
1839
+ async report(error) {
1840
+ if (!this.shouldReport(error)) return;
1841
+ if (typeof error === "object" && error !== null) {
1842
+ if (this.#reported.has(error)) return;
1843
+ this.#reported.add(error);
1844
+ }
1845
+ try {
1846
+ for (const callback of this.#reportCallbacks) if (await callback(error) === false) return;
1847
+ const container = this.#container();
1848
+ if (container.bound(LogManager)) {
1849
+ const message = error instanceof Error ? error.message : String(error);
1850
+ container.make(LogManager).error(message, { exception: error });
1851
+ } else {
1852
+ console.error(error);
1853
+ }
1854
+ } catch (reportError) {
1855
+ console.error(reportError);
1856
+ }
1857
+ }
1858
+ async render(request2, error) {
1859
+ for (const callback of this.#renderCallbacks) {
1860
+ const response3 = await callback(error, request2);
1861
+ if (response3 instanceof Response) return response3;
1862
+ }
1863
+ if (error instanceof HttpResponseException) return error.response;
1864
+ const custom = await this.#renderCustom(request2, error);
1865
+ if (custom !== void 0) return custom;
1866
+ const http = this.#httpRequest(request2);
1867
+ const kind = kindOf(error);
1868
+ let response2;
1869
+ if (kind === "Validation") response2 = await this.#renderValidation(http, error);
1870
+ else if (kind === "Authentication") response2 = await this.#renderAuthentication(http, error);
1871
+ else if (this.shouldReturnJson(http, error)) response2 = this.#renderJson(error);
1872
+ else response2 = await this.#renderHtml(http, error);
1873
+ const headers = error?.headers;
1874
+ if (headers !== void 0) for (const [name, value] of Object.entries(headers)) response2.headers.set(name, value);
1875
+ return response2;
1876
+ }
1877
+ /** An error with its own render(request) or toResponse(request) (Laravel's renderable exceptions / Responsable). */
1878
+ async #renderCustom(request2, error) {
1879
+ if (typeof error !== "object" || error === null) return void 0;
1880
+ const candidate = error;
1881
+ for (const method of [candidate.render, candidate.toResponse]) {
1882
+ if (typeof method !== "function") continue;
1883
+ const response2 = await method.call(error, request2);
1884
+ if (response2 instanceof Response) return response2;
1885
+ }
1886
+ return void 0;
1887
+ }
1888
+ async #renderValidation(request2, error) {
1889
+ if (this.shouldReturnJson(request2, error)) {
1890
+ return this.#json({ message: summarizeValidation(error.errors), errors: error.errors }, statusFor(error));
1891
+ }
1892
+ const input = { ...request2.input() };
1893
+ for (const key of this.dontFlash) delete input[key];
1894
+ const container = this.#container();
1895
+ if (!container.bound(Redirector)) {
1896
+ return this.#redirect(error.redirectTo ?? request2.header("referer") ?? "/");
1897
+ }
1898
+ const redirector = container.make(Redirector);
1899
+ const redirect2 = error.redirectTo !== void 0 ? redirector.to(error.redirectTo) : redirector.back();
1900
+ const response2 = await redirect2.withInput(input).withErrors(error.errors, error.errorBag).toResponse(request2);
1901
+ return response2;
1902
+ }
1903
+ async #renderAuthentication(request2, error) {
1904
+ if (this.shouldReturnJson(request2, error)) return this.#json({ message: error.message || "Unauthenticated." }, 401);
1905
+ const target2 = error.redirectTo ?? this.#guestRedirect(request2);
1906
+ const container = this.#container();
1907
+ if (!container.bound(Redirector)) return this.#redirect(target2);
1908
+ const redirector = container.make(Redirector);
1909
+ const redirect2 = typeof redirector.guest === "function" ? redirector.guest(target2) : redirector.to(target2);
1910
+ return await redirect2.toResponse(request2);
1911
+ }
1912
+ /** config('http.redirect_guests_to'), then route('login'), then '/login'. */
1913
+ #guestRedirect(request2) {
1914
+ const configured = this.#config("http.redirect_guests_to", void 0);
1915
+ if (typeof configured === "function") return String(configured(request2));
1916
+ if (typeof configured === "string" && configured !== "") return configured;
1917
+ const container = this.#container();
1918
+ if (container.bound(UrlGenerator)) {
1919
+ const url2 = container.make(UrlGenerator);
1920
+ if (url2.hasRoute("login")) return url2.route("login");
1921
+ }
1922
+ return "/login";
1923
+ }
1924
+ #renderJson(error) {
1925
+ const status = statusFor(error);
1926
+ if (isHttpLike(error)) {
1927
+ const message = error instanceof Error && error.message !== "" ? error.message : statusText(status);
1928
+ return this.#json({ message }, status);
1929
+ }
1930
+ if (!this.#debug()) return this.#json({ message: "Server Error" }, status);
1931
+ const err = error instanceof Error ? error : new Error(String(error));
1932
+ const [frame] = stackFrames(err);
1933
+ return this.#json(
1934
+ {
1935
+ message: err.message,
1936
+ exception: err.name,
1937
+ file: frame?.file,
1938
+ line: frame?.line,
1939
+ trace: stackFrames(err).map((entry) => entry.text)
1940
+ },
1941
+ status
1942
+ );
1943
+ }
1944
+ async #renderHtml(request2, error) {
1945
+ const status = statusFor(error);
1946
+ if (!isHttpLike(error) && this.#debug()) {
1947
+ return this.#html(await debugPage(error, request2.method(), request2.fullUrl()), status);
1948
+ }
1949
+ const message = status === 403 && error instanceof Error && error.message !== "" ? error.message : statusText(status);
1950
+ const view = await this.#errorView(status, error, message);
1951
+ return this.#html(view ?? minimalPage(status, message), status);
1952
+ }
1953
+ /** resources/views/errors/{status}.tsx, then errors/4xx or errors/5xx, through the ViewFactory. */
1954
+ async #errorView(status, error, message) {
1955
+ const container = this.#container();
1956
+ if (!container.bound(ViewFactory)) return void 0;
1957
+ const views = container.make(ViewFactory);
1958
+ for (const name of [`errors.${status}`, `errors.${Math.floor(status / 100)}xx`]) {
1959
+ if (!views.exists(name)) continue;
1960
+ try {
1961
+ return await views.make(name, { exception: error, status, message }).render();
1962
+ } catch (viewError) {
1963
+ await this.report(viewError);
1964
+ return void 0;
1965
+ }
1966
+ }
1967
+ return void 0;
1968
+ }
1969
+ #httpRequest(request2) {
1970
+ if (typeof request2 === "object" && request2 !== null && typeof request2.expectsJson === "function") {
1971
+ return request2;
1972
+ }
1973
+ return new IncomingRequest(request2 instanceof Request ? request2 : new Request("http://localhost/"));
1974
+ }
1975
+ #json(body, status) {
1976
+ return new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
1977
+ }
1978
+ #html(body, status) {
1979
+ return new Response(body, { status, headers: { "content-type": "text/html; charset=utf-8" } });
1980
+ }
1981
+ #redirect(location) {
1982
+ return new Response(null, { status: 302, headers: { Location: location } });
1983
+ }
1984
+ #debug() {
1985
+ return this.#config("app.debug", false) === true;
1986
+ }
1987
+ #config(key, fallback) {
1988
+ return this.app.bound(ConfigRepository) ? this.app.make(ConfigRepository).get(key, fallback) : fallback;
1989
+ }
1990
+ /** The request scope while handling a request (so scoped services resolve), else the application. */
1991
+ #container() {
1992
+ return currentContainer() ?? this.app;
1993
+ }
1994
+ };
1995
+ var ExceptionsConfiguration = class {
1996
+ constructor(handler) {
1997
+ this.handler = handler;
1998
+ }
1999
+ handler;
2000
+ report(callback) {
2001
+ this.handler.reportUsing(callback);
2002
+ return this;
2003
+ }
2004
+ render(callback) {
2005
+ this.handler.renderUsing(callback);
2006
+ return this;
2007
+ }
2008
+ dontReport(...classes) {
2009
+ this.handler.ignore(...classes);
2010
+ return this;
2011
+ }
2012
+ /** shouldRenderJsonWhen((request) => request.is('api/*')): JSON errors for those requests, whatever they accept. */
2013
+ shouldRenderJsonWhen(callback) {
2014
+ this.handler.shouldRenderJsonWhen(callback);
2015
+ return this;
2016
+ }
2017
+ };
2018
+ function applyExceptionCallbacks(app, handler) {
2019
+ if (!app.bound(ExceptionCallbacks)) return;
2020
+ const configurator = new ExceptionsConfiguration(handler);
2021
+ for (const callback of app.make(ExceptionCallbacks)) callback(configurator);
2022
+ }
2023
+ var HIDDEN_SEGMENT = /^\.|\.(ts|tsx|mts|cts)$|[. ]$/i;
2024
+ async function servePublicFile(publicPath, request2) {
2025
+ if (request2.method !== "GET" && request2.method !== "HEAD") return void 0;
2026
+ let pathname;
2027
+ try {
2028
+ pathname = decodeURIComponent(new URL(request2.url).pathname);
2029
+ } catch {
2030
+ return void 0;
2031
+ }
2032
+ if (pathname === "/" || pathname.includes("\0")) return void 0;
2033
+ const root = resolve(publicPath);
2034
+ const file = resolve(root, `.${pathname}`);
2035
+ if (!file.startsWith(root + sep)) return void 0;
2036
+ if (relative(root, file).split(/[\\/]/).some((segment) => HIDDEN_SEGMENT.test(segment))) return void 0;
2037
+ const info = await stat2(file).catch(() => void 0);
2038
+ if (info === void 0 || !info.isFile()) return void 0;
2039
+ const type = mimeFromExtension(file);
2040
+ const headers = new Headers({
2041
+ "content-type": type.startsWith("text/") || type === "application/json" ? `${type}; charset=utf-8` : type,
2042
+ "content-length": String(info.size),
2043
+ "last-modified": info.mtime.toUTCString()
2044
+ });
2045
+ if (pathname.startsWith("/build/assets/")) headers.set("cache-control", "public, max-age=31536000, immutable");
2046
+ if (request2.method === "HEAD") return new Response(null, { status: 200, headers });
2047
+ const body = Readable2.toWeb(createReadStream2(file));
2048
+ return new Response(body, { status: 200, headers });
2049
+ }
2050
+ async function terminateRequest(app, request2, response2) {
2051
+ try {
2052
+ if (app.bound(HttpKernel)) await app.make(HttpKernel).terminate?.(request2, response2);
2053
+ } catch (error) {
2054
+ console.error(error);
2055
+ }
2056
+ }
2057
+ async function handleSafely(app, request2, publicPath) {
2058
+ try {
2059
+ if (publicPath !== void 0) {
2060
+ const file = await servePublicFile(publicPath, request2);
2061
+ if (file !== void 0) return file;
2062
+ }
2063
+ return await app.handle(request2);
2064
+ } catch (error) {
2065
+ console.error(error);
2066
+ return plainServerError();
2067
+ }
2068
+ }
2069
+ function exitOnSignals(close, closed) {
2070
+ const onSignal = () => {
2071
+ void close().then(() => process.exit(0));
2072
+ };
2073
+ process.once("SIGINT", onSignal);
2074
+ process.once("SIGTERM", onSignal);
2075
+ void closed.then(() => {
2076
+ process.off("SIGINT", onSignal);
2077
+ process.off("SIGTERM", onSignal);
2078
+ });
2079
+ }
2080
+ var displayHost = (hostname) => hostname.includes(":") ? `[${hostname}]` : hostname;
2081
+ function bunRuntime() {
2082
+ return globalThis.Bun;
2083
+ }
2084
+ async function serveBun(app, options) {
2085
+ const bun = bunRuntime();
2086
+ if (bun === void 0) throw new HttpServerError("Bun.serve is not available: the process is not running on Bun.");
2087
+ let markClosed = () => void 0;
2088
+ const closed = new Promise((resolve2) => markClosed = resolve2);
2089
+ const server = bun.serve({
2090
+ port: options.port,
2091
+ hostname: options.hostname,
2092
+ async fetch(request2, current) {
2093
+ setClientAddress(request2, current.requestIP(request2)?.address);
2094
+ const response2 = await handleSafely(app, request2, options.publicPath);
2095
+ setTimeout(() => void terminateRequest(app, request2, response2), 0);
2096
+ return response2;
2097
+ },
2098
+ error(error) {
2099
+ console.error(error);
2100
+ return plainServerError();
2101
+ }
2102
+ });
2103
+ let closing;
2104
+ const close = () => closing ??= (async () => {
2105
+ let timer;
2106
+ const forced = new Promise((resolve2) => {
2107
+ timer = setTimeout(() => void Promise.resolve(server.stop(true)).then(resolve2), options.shutdownTimeout);
2108
+ });
2109
+ await Promise.race([Promise.resolve(server.stop(false)), forced]);
2110
+ clearTimeout(timer);
2111
+ markClosed();
2112
+ })();
2113
+ if (options.signals) exitOnSignals(close, closed);
2114
+ return {
2115
+ runtime: "bun",
2116
+ hostname: options.hostname,
2117
+ port: server.port,
2118
+ url: `http://${displayHost(options.hostname)}:${server.port}`,
2119
+ close,
2120
+ closed
2121
+ };
2122
+ }
2123
+ function requestBody(req) {
2124
+ let finished = false;
2125
+ const detach = () => {
2126
+ finished = true;
2127
+ req.off("data", onData);
2128
+ req.off("end", onEnd);
2129
+ req.off("error", onError);
2130
+ req.off("close", onClose);
2131
+ };
2132
+ let controller;
2133
+ const onData = (chunk2) => {
2134
+ if (finished) return;
2135
+ controller.enqueue(new Uint8Array(chunk2.buffer, chunk2.byteOffset, chunk2.byteLength));
2136
+ if ((controller.desiredSize ?? 0) <= 0) req.pause();
2137
+ };
2138
+ const onEnd = () => {
2139
+ if (finished) return;
2140
+ detach();
2141
+ controller.close();
2142
+ };
2143
+ const onError = (error) => {
2144
+ if (finished) return;
2145
+ detach();
2146
+ controller.error(error);
2147
+ };
2148
+ const onClose = () => {
2149
+ if (!finished && !req.complete) onError(new Error("The request was aborted."));
2150
+ };
2151
+ return new ReadableStream(
2152
+ {
2153
+ start(c) {
2154
+ controller = c;
2155
+ req.on("data", onData);
2156
+ req.on("end", onEnd);
2157
+ req.on("error", onError);
2158
+ req.on("close", onClose);
2159
+ req.pause();
2160
+ },
2161
+ pull() {
2162
+ req.resume();
2163
+ },
2164
+ cancel() {
2165
+ detach();
2166
+ }
2167
+ },
2168
+ { highWaterMark: 0 }
2169
+ );
2170
+ }
2171
+ function toWebRequest(req, res, fallbackHost) {
2172
+ const target2 = req.url ?? "/";
2173
+ const url2 = new URL(`http://${req.headers.host ?? fallbackHost}${target2.startsWith("/") ? target2 : `/${target2}`}`);
2174
+ const headers = new Headers();
2175
+ for (let i = 0; i < req.rawHeaders.length; i += 2) {
2176
+ const name = req.rawHeaders[i];
2177
+ if (!name.startsWith(":")) headers.append(name, req.rawHeaders[i + 1]);
2178
+ }
2179
+ const method = (req.method ?? "GET").toUpperCase();
2180
+ const controller = new AbortController();
2181
+ res.once("close", () => {
2182
+ if (!res.writableFinished) controller.abort();
2183
+ });
2184
+ const init = { method, headers, signal: controller.signal };
2185
+ if (method !== "GET" && method !== "HEAD") {
2186
+ init.body = requestBody(req);
2187
+ init.duplex = "half";
2188
+ }
2189
+ return new Request(url2, init);
2190
+ }
2191
+ async function writeResponse(res, response2) {
2192
+ res.statusCode = response2.status;
2193
+ if (response2.statusText !== "") res.statusMessage = response2.statusText;
2194
+ response2.headers.forEach((value, name) => {
2195
+ if (name !== "set-cookie") res.setHeader(name, value);
2196
+ });
2197
+ const cookies = response2.headers.getSetCookie();
2198
+ if (cookies.length > 0) res.setHeader("set-cookie", cookies);
2199
+ if (response2.body === null) {
2200
+ res.end();
2201
+ return;
2202
+ }
2203
+ await pipeline(Readable3.fromWeb(response2.body), res);
2204
+ }
2205
+ async function serveNode(app, options) {
2206
+ const fallbackHost = `${displayHost(options.hostname)}:${options.port}`;
2207
+ const server = createServer((req, res) => {
2208
+ void (async () => {
2209
+ let request2;
2210
+ try {
2211
+ request2 = toWebRequest(req, res, fallbackHost);
2212
+ } catch {
2213
+ res.statusCode = 400;
2214
+ res.end("Bad Request");
2215
+ return;
2216
+ }
2217
+ setClientAddress(request2, req.socket.remoteAddress);
2218
+ const response2 = await handleSafely(app, request2, options.publicPath);
2219
+ try {
2220
+ await writeResponse(res, response2);
2221
+ } catch {
2222
+ res.destroy();
2223
+ }
2224
+ await terminateRequest(app, request2, response2);
2225
+ })();
2226
+ });
2227
+ await new Promise((resolve2, reject) => {
2228
+ server.once("error", reject);
2229
+ server.listen(options.port, options.hostname, () => {
2230
+ server.off("error", reject);
2231
+ resolve2();
2232
+ });
2233
+ });
2234
+ const address = server.address();
2235
+ const closed = new Promise((resolve2) => server.once("close", () => resolve2()));
2236
+ let closing;
2237
+ const close = () => closing ??= (async () => {
2238
+ const done = new Promise((resolve2) => server.close(() => resolve2()));
2239
+ server.closeIdleConnections();
2240
+ const idle = setInterval(() => server.closeIdleConnections(), 50);
2241
+ const force = setTimeout(() => server.closeAllConnections(), options.shutdownTimeout);
2242
+ await done;
2243
+ clearInterval(idle);
2244
+ clearTimeout(force);
2245
+ })();
2246
+ if (options.signals) exitOnSignals(close, closed);
2247
+ return {
2248
+ runtime: "node",
2249
+ hostname: options.hostname,
2250
+ port: address.port,
2251
+ url: `http://${displayHost(options.hostname)}:${address.port}`,
2252
+ close,
2253
+ closed
2254
+ };
2255
+ }
2256
+ function detectRuntime() {
2257
+ return bunRuntime() !== void 0 ? "bun" : "node";
2258
+ }
2259
+ async function serve(app, options = {}) {
2260
+ const resolved = {
2261
+ port: options.port ?? Number(process.env.PORT ?? process.env.SERVER_PORT ?? 8e3),
2262
+ hostname: options.hostname ?? "127.0.0.1",
2263
+ signals: options.signals ?? true,
2264
+ shutdownTimeout: options.shutdownTimeout ?? 1e4,
2265
+ ...options.publicPath !== false && { publicPath: options.publicPath ?? app.publicPath() }
2266
+ };
2267
+ await app.boot();
2268
+ const runtime = options.runtime ?? detectRuntime();
2269
+ const server = runtime === "bun" ? await serveBun(app, resolved) : await serveNode(app, resolved);
2270
+ const onListen = options.onListen ?? ((running) => console.log(`Server running on [${running.url}].`));
2271
+ onListen(server);
2272
+ return server;
2273
+ }
2274
+ function io(command) {
2275
+ return {
2276
+ option: (name) => {
2277
+ const value = command.option(name);
2278
+ return value === void 0 || value === null || value === false || value === "" ? void 0 : String(value);
2279
+ },
2280
+ flag: (name) => {
2281
+ const value = command.option(name);
2282
+ return value === true || value === "true" || value === "1";
2283
+ },
2284
+ info: (message) => command.info(message),
2285
+ line: (message) => command.line(message),
2286
+ warn: (message) => command.warn(message),
2287
+ error: (message) => command.error(message)
2288
+ };
2289
+ }
2290
+ var middlewareName = (reference) => typeof reference === "string" ? reference : reference.name;
2291
+ function routeListRows(routes) {
2292
+ const rows = routes.map((route2) => {
2293
+ const listable = route2;
2294
+ return {
2295
+ method: route2.methods.join("|"),
2296
+ uri: route2.uri,
2297
+ name: route2.getName() ?? null,
2298
+ action: typeof listable.getActionName === "function" ? listable.getActionName() : "Closure",
2299
+ middleware: typeof listable.gatherMiddleware === "function" ? listable.gatherMiddleware().map(middlewareName) : []
2300
+ };
2301
+ });
2302
+ return rows.sort((a, b) => a.uri < b.uri ? -1 : a.uri > b.uri ? 1 : 0);
2303
+ }
2304
+ function filterRouteList(rows, filters) {
2305
+ const excluded = (filters.exceptPath ?? "").split(",").filter((path) => path !== "");
2306
+ return rows.filter((row) => {
2307
+ if (filters.name !== void 0 && !(row.name ?? "").includes(filters.name)) return false;
2308
+ if (filters.path !== void 0 && !row.uri.includes(filters.path)) return false;
2309
+ if (filters.method !== void 0 && !row.method.includes(filters.method.toUpperCase())) return false;
2310
+ return !excluded.some((path) => row.uri.includes(path));
2311
+ });
2312
+ }
2313
+ function formatRouteList(rows, width) {
2314
+ const methodWidth = Math.max(...rows.map((row) => row.method.length));
2315
+ const lines = rows.map((row) => {
2316
+ const spaces = " ".repeat(Math.max(methodWidth + 6 - row.method.length, 0));
2317
+ const action = row.action === "Closure" ? row.name ?? "" : row.name === null ? row.action : `${row.name} \u203A ${row.action}`;
2318
+ const used = row.method.length + spaces.length + row.uri.length + action.length + 6 + (action === "" ? 0 : 1);
2319
+ const dots = ".".repeat(Math.max(width - used, 0));
2320
+ return ` ${row.method} ${spaces}${row.uri}${dots === "" ? "" : ` ${dots}`}${action === "" ? "" : ` ${action}`}`;
2321
+ });
2322
+ const count = `Showing [${rows.length}] ${rows.length === 1 ? "route" : "routes"}`;
2323
+ return [...lines, "", `${" ".repeat(Math.max(width - count.length - 2, 0))}${count}`];
2324
+ }
2325
+ var API_ROUTES_STUB = `import { request, Route } from '@orieljs/framework'
2326
+
2327
+ Route.get('/user', () => request().user()).middleware('auth:token')
2328
+ `;
2329
+ function addApiRouting(source) {
2330
+ const code = source.replace(/^[ \t]*\/\/.*$/gm, "");
2331
+ if (/withRouting\(\s*\{[^}]*\bapi\s*:/.test(code)) return source;
2332
+ const commented = /^([ \t]*)\/\/\s*(api:\s*['"`][^'"`\n]*['"`],?)[ \t]*$/m.exec(source);
2333
+ if (commented !== null) return source.replace(commented[0], () => `${commented[1]}${commented[2]}`);
2334
+ const web = /^([ \t]*)web:\s*(['"`])[^'"`\n]*\2[ \t]*,?[ \t]*$/m.exec(source);
2335
+ if (web !== null) {
2336
+ const line = web[0].trimEnd();
2337
+ return source.replace(web[0], () => `${line.endsWith(",") ? line : `${line},`}
2338
+ ${web[1]}api: 'routes/api.ts',`);
2339
+ }
2340
+ const inline = /withRouting\(\s*\{/.exec(source);
2341
+ if (inline !== null) return source.replace(inline[0], () => `${inline[0]} api: 'routes/api.ts',`);
2342
+ const create = /^([ \t]*)\.create\(\)/m.exec(source);
2343
+ if (create !== null) return source.replace(create[0], () => `${create[1]}.withRouting({ api: 'routes/api.ts' })
2344
+ ${create[0]}`);
2345
+ return void 0;
2346
+ }
2347
+ function registerHttpCommands(kernel, app, options = {}) {
2348
+ const serveApp = options.serve ?? serve;
2349
+ const terminalWidth = options.terminalWidth ?? (() => process.stdout.columns ?? 120);
2350
+ kernel.command(
2351
+ "serve {--host=127.0.0.1 : The host address to serve the application on} {--port= : The port to serve the application on}",
2352
+ async function() {
2353
+ const out = io(this);
2354
+ const port = out.option("port");
2355
+ if (port !== void 0 && !/^\d+$/.test(port)) {
2356
+ out.error("The --port option must be a number.");
2357
+ return 1;
2358
+ }
2359
+ const server = await serveApp(app, {
2360
+ hostname: out.option("host") ?? "127.0.0.1",
2361
+ ...port !== void 0 && { port: Number(port) },
2362
+ onListen: (running) => {
2363
+ out.info(`Server running on [${running.url}].`);
2364
+ out.line("Press Ctrl+C to stop the server");
2365
+ }
2366
+ });
2367
+ await server.closed;
2368
+ return 0;
2369
+ }
2370
+ ).purpose("Serve the application on the Oriel development server");
2371
+ kernel.command(
2372
+ "route:list {--method= : Filter the routes by method} {--name= : Filter the routes by name} {--path= : Only show routes matching the given path pattern} {--except-path= : Do not display the routes matching the given path pattern} {--reverse : Reverse the ordering of the routes} {--json : Output the route list as JSON}",
2373
+ function() {
2374
+ const out = io(this);
2375
+ if (!app.bound(Router)) {
2376
+ out.error("The router is not available. Is the Routing service provider registered?");
2377
+ return 1;
2378
+ }
2379
+ const all = routeListRows(app.make(Router).getRoutes());
2380
+ if (all.length === 0) {
2381
+ out.error("Your application doesn't have any routes.");
2382
+ return 0;
2383
+ }
2384
+ const rows = filterRouteList(all, {
2385
+ ...out.option("method") !== void 0 && { method: out.option("method") },
2386
+ ...out.option("name") !== void 0 && { name: out.option("name") },
2387
+ ...out.option("path") !== void 0 && { path: out.option("path") },
2388
+ ...out.option("except-path") !== void 0 && { exceptPath: out.option("except-path") }
2389
+ });
2390
+ if (rows.length === 0) {
2391
+ out.error("Your application doesn't have any routes matching the given criteria.");
2392
+ return 0;
2393
+ }
2394
+ if (out.flag("reverse")) rows.reverse();
2395
+ if (out.flag("json")) {
2396
+ out.line(JSON.stringify(rows));
2397
+ return 0;
2398
+ }
2399
+ for (const line of formatRouteList(rows, terminalWidth())) out.line(line);
2400
+ return 0;
2401
+ }
2402
+ ).purpose("List all registered routes");
2403
+ kernel.command("install:api {--force : Overwrite any existing API routes file}", async function() {
2404
+ const out = io(this);
2405
+ const routesFile = app.basePath("routes/api.ts");
2406
+ if (existsSync4(routesFile) && !out.flag("force")) {
2407
+ out.error("API routes file already exists.");
2408
+ } else {
2409
+ await mkdir(dirname(routesFile), { recursive: true });
2410
+ await writeFile(routesFile, API_ROUTES_STUB);
2411
+ out.info("Published API routes file.");
2412
+ }
2413
+ const bootstrapFile = app.bootstrapPath("app.ts");
2414
+ const source = existsSync4(bootstrapFile) ? await readFile2(bootstrapFile, "utf8") : void 0;
2415
+ const updated = source === void 0 ? void 0 : addApiRouting(source);
2416
+ if (updated === void 0) {
2417
+ out.warn(
2418
+ "Unable to automatically add API route definition to bootstrap file. Add api: 'routes/api.ts' to withRouting() in bootstrap/app.ts."
2419
+ );
2420
+ } else if (updated !== source) {
2421
+ await writeFile(bootstrapFile, updated);
2422
+ }
2423
+ out.info("API scaffolding installed. Run [oriel migrate] to create the personal access tokens table.");
2424
+ return 0;
2425
+ }).purpose("Create an API routes file and prepare the application for API authentication");
2426
+ }
2427
+ var HttpServiceProvider = class extends ServiceProvider {
2428
+ register() {
2429
+ this.mergeConfigFrom(
2430
+ {
2431
+ trusted_proxies: [],
2432
+ max_body_size: 8 * 1024 * 1024,
2433
+ max_json_depth: 512,
2434
+ csrf_except: [],
2435
+ redirect_guests_to: void 0,
2436
+ redirect_users_to: void 0
2437
+ },
2438
+ "http"
2439
+ );
2440
+ this.app.singleton(HttpKernel, () => new Kernel(this.app));
2441
+ this.app.singleton(ExceptionHandler, () => {
2442
+ const handler = new ErrorHandler(this.app);
2443
+ applyExceptionCallbacks(this.app, handler);
2444
+ return handler;
2445
+ });
2446
+ this.app.singleton(ResponseFactory, () => new HttpResponseFactory());
2447
+ this.app.singleton(Redirector, () => new HttpRedirector());
2448
+ }
2449
+ boot() {
2450
+ this.app.booted(() => {
2451
+ const kernel = this.app.make(HttpKernel);
2452
+ if (kernel instanceof Kernel && this.app.bound(Router)) applyMiddlewareCallbacks(this.app, kernel);
2453
+ });
2454
+ if (this.app.bound(ConsoleKernel)) registerHttpCommands(this.app.make(ConsoleKernel), this.app);
2455
+ }
2456
+ };
2457
+
2458
+ // ../Live/dist/index.js
2459
+ import { AssertionError } from "assert";
2460
+ import { isDeepStrictEqual } from "util";
2461
+ import { AsyncLocalStorage } from "async_hooks";
2462
+ import { randomBytes as randomBytes2 } from "crypto";
2463
+ import { createHmac, timingSafeEqual } from "crypto";
2464
+ import { existsSync as existsSync5, readdirSync as readdirSync2 } from "fs";
2465
+ import { join as join5, relative as relative2, sep as sep2 } from "path";
2466
+ import { pathToFileURL as pathToFileURL2 } from "url";
2467
+ import { existsSync as existsSync22 } from "fs";
2468
+ import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
2469
+ import { dirname as dirname2, join as join23, relative as relative22, sep as sep22 } from "path";
2470
+ Symbol.metadata ??= /* @__PURE__ */ Symbol.for("Symbol.metadata");
2471
+ var LISTENERS = /* @__PURE__ */ Symbol.for("oriel:live.listeners");
2472
+ var LOCKED = /* @__PURE__ */ Symbol.for("oriel:live.locked");
2473
+ var COMPUTED = /* @__PURE__ */ Symbol.for("oriel:live.computed");
2474
+ function own(metadata, key, empty2, copy) {
2475
+ if (!Object.hasOwn(metadata, key)) metadata[key] = metadata[key] === void 0 ? empty2() : copy(metadata[key]);
2476
+ return metadata[key];
2477
+ }
2478
+ function On(event) {
2479
+ return function(_method, context) {
2480
+ const listeners = own(
2481
+ context.metadata,
2482
+ LISTENERS,
2483
+ () => ({}),
2484
+ (inherited) => Object.fromEntries(Object.entries(inherited).map(([name, methods2]) => [name, [...methods2]]))
2485
+ );
2486
+ const methods = listeners[event] ??= [];
2487
+ if (!methods.includes(String(context.name))) methods.push(String(context.name));
2488
+ };
2489
+ }
2490
+ function Locked(_target, context) {
2491
+ own(context.metadata, LOCKED, () => [], (inherited) => [...inherited]).push(String(context.name));
2492
+ }
2493
+ var computedCache = /* @__PURE__ */ new WeakMap();
2494
+ function cachedGetter(name, getter) {
2495
+ return function() {
2496
+ let cache = computedCache.get(this);
2497
+ if (cache === void 0) computedCache.set(this, cache = /* @__PURE__ */ new Map());
2498
+ if (!cache.has(name)) cache.set(name, getter.call(this));
2499
+ return cache.get(name);
2500
+ };
2501
+ }
2502
+ function Computed(getter, context) {
2503
+ own(context.metadata, COMPUTED, () => [], (inherited) => [...inherited]).push(String(context.name));
2504
+ return cachedGetter(String(context.name), getter);
2505
+ }
2506
+ function forgetComputed(instance) {
2507
+ computedCache.delete(instance);
2508
+ }
2509
+ function metadataOf(cls) {
2510
+ return cls[Symbol.metadata] ?? {};
2511
+ }
2512
+ function chainOf(cls) {
2513
+ const chain = [];
2514
+ for (let c = cls; c !== null && c !== Function.prototype; c = Object.getPrototypeOf(c)) {
2515
+ chain.unshift(c);
2516
+ }
2517
+ return chain;
2518
+ }
2519
+ function staticsOf(cls, key) {
2520
+ return chainOf(cls).flatMap((c) => Object.hasOwn(c, key) && c[key] !== void 0 ? [c[key]] : []);
2521
+ }
2522
+ function listenersOf(cls) {
2523
+ const result = {};
2524
+ for (const [event, methods] of Object.entries(metadataOf(cls)[LISTENERS] ?? {})) {
2525
+ result[event] = [...methods];
2526
+ }
2527
+ for (const listeners of staticsOf(cls, "listeners")) {
2528
+ for (const [event, method] of Object.entries(listeners)) {
2529
+ const methods = result[event] ??= [];
2530
+ if (!methods.includes(method)) methods.push(method);
2531
+ }
2532
+ }
2533
+ return result;
2534
+ }
2535
+ function lockedOf(cls) {
2536
+ return /* @__PURE__ */ new Set([...metadataOf(cls)[LOCKED] ?? [], ...staticsOf(cls, "locked").flat()]);
2537
+ }
2538
+ function computedOf(cls) {
2539
+ return /* @__PURE__ */ new Set([...metadataOf(cls)[COMPUTED] ?? [], ...staticsOf(cls, "computed").flat()]);
2540
+ }
2541
+ var COMPONENT = /* @__PURE__ */ Symbol.for("oriel:live.component");
2542
+ var states = /* @__PURE__ */ new WeakMap();
2543
+ function stateOf(component) {
2544
+ let state = states.get(component);
2545
+ if (state === void 0) states.set(component, state = { id: "", name: "", errors: {}, dispatches: [], skipRender: false });
2546
+ return state;
2547
+ }
2548
+ function isComponentClass(value) {
2549
+ return typeof value === "function" && value[COMPONENT] === true;
2550
+ }
2551
+ var ErrorBag = class {
2552
+ constructor(messages) {
2553
+ this.messages = messages;
2554
+ }
2555
+ messages;
2556
+ has(key) {
2557
+ return (this.messages[key]?.length ?? 0) > 0;
2558
+ }
2559
+ first(key) {
2560
+ return key === void 0 ? this.all()[0] : this.messages[key]?.[0];
2561
+ }
2562
+ get(key) {
2563
+ return [...this.messages[key] ?? []];
2564
+ }
2565
+ all() {
2566
+ return Object.values(this.messages).flat();
2567
+ }
2568
+ any() {
2569
+ return !this.isEmpty();
2570
+ }
2571
+ isEmpty() {
2572
+ return this.count() === 0;
2573
+ }
2574
+ count() {
2575
+ return this.all().length;
2576
+ }
2577
+ toObject() {
2578
+ return Object.fromEntries(Object.entries(this.messages).map(([key, messages]) => [key, [...messages]]));
2579
+ }
2580
+ };
2581
+ var PendingEvent = class {
2582
+ constructor(event) {
2583
+ this.event = event;
2584
+ }
2585
+ event;
2586
+ /** Only this component's own listeners receive the event. */
2587
+ self() {
2588
+ this.event.self = true;
2589
+ delete this.event.to;
2590
+ return this;
2591
+ }
2592
+ /** Only components of this class (or registered name) receive the event. */
2593
+ to(component) {
2594
+ this.event.to = component;
2595
+ delete this.event.self;
2596
+ return this;
2597
+ }
2598
+ };
2599
+ var Component = class {
2600
+ static [COMPONENT] = true;
2601
+ /** The view that wraps a full-page component (default: config `live.layout`). */
2602
+ static layout;
2603
+ /** The page title passed to the layout as `title`. */
2604
+ static title;
2605
+ /** Static fallbacks for runtimes without decorators: { event: 'method' }, locked names, computed getter names. */
2606
+ static listeners;
2607
+ static locked;
2608
+ static computed;
2609
+ getId() {
2610
+ return stateOf(this).id;
2611
+ }
2612
+ getName() {
2613
+ return stateOf(this).name;
2614
+ }
2615
+ /** Dispatches a browser event; every component listening for it (@On) runs its handler. */
2616
+ dispatch(name, params = {}) {
2617
+ const event = { name, params };
2618
+ stateOf(this).dispatches.push(event);
2619
+ return new PendingEvent(event);
2620
+ }
2621
+ /** Sends the browser to `url` after this request; `navigate` uses o:navigate instead of a full page load. */
2622
+ redirect(url2, options = {}) {
2623
+ const state = stateOf(this);
2624
+ state.redirect = url2;
2625
+ if (options.navigate === true) state.redirectUsingNavigate = true;
2626
+ else delete state.redirectUsingNavigate;
2627
+ }
2628
+ redirectRoute(name, parameters, options = {}) {
2629
+ this.redirect(inject(UrlGenerator).route(name, parameters), options);
2630
+ }
2631
+ /**
2632
+ * Validates the public state. On success clears the error bag and returns the validated data; on failure fills the
2633
+ * error bag and throws (the rest of the action is skipped and the component re-renders with `errors`).
2634
+ */
2635
+ async validate(rules, messages, attributes) {
2636
+ const validator = inject(ValidationFactory).make(Object.fromEntries(Object.entries(this)), rules, messages, attributes);
2637
+ try {
2638
+ const data = await validator.validated();
2639
+ stateOf(this).errors = {};
2640
+ return data;
2641
+ } catch (error) {
2642
+ const errors = error.errors;
2643
+ if (error.kind === "Validation" && errors !== void 0) stateOf(this).errors = { ...errors };
2644
+ throw error;
2645
+ }
2646
+ }
2647
+ addError(key, message) {
2648
+ ;
2649
+ (stateOf(this).errors[key] ??= []).push(message);
2650
+ }
2651
+ /** Clears every error, or only those of `keys`. */
2652
+ resetErrorBag(...keys) {
2653
+ const state = stateOf(this);
2654
+ if (keys.length === 0) state.errors = {};
2655
+ else for (const key of keys) delete state.errors[key];
2656
+ }
2657
+ get errors() {
2658
+ return new ErrorBag(stateOf(this).errors);
2659
+ }
2660
+ /** Puts public properties (all of them when none are named) back to their initial values. */
2661
+ reset(...properties) {
2662
+ const fresh = new this.constructor();
2663
+ const names = properties.length > 0 ? properties : Object.keys(this);
2664
+ for (const name of names) if (Object.hasOwn(fresh, name)) this[name] = fresh[name];
2665
+ forgetComputed(this);
2666
+ }
2667
+ /** Keeps the current HTML: the response carries the new state but no `html` effect. */
2668
+ skipRender() {
2669
+ stateOf(this).skipRender = true;
2670
+ }
2671
+ /** A full-page component as a route action: Route.get('/counter', Counter). Route parameters go to mount(). */
2672
+ async __invoke() {
2673
+ return inject(LiveManager).page(this);
2674
+ }
2675
+ };
2676
+ var LiveError = class extends Error {
2677
+ name = "LiveError";
2678
+ kind = "Server";
2679
+ status = 500;
2680
+ };
2681
+ var CorruptComponentPayloadError = class extends LiveError {
2682
+ name = "CorruptComponentPayloadError";
2683
+ kind = "Authorization";
2684
+ status = 403;
2685
+ constructor(component = "unknown") {
2686
+ super(`Live encountered corrupt data when trying to hydrate the [${component}] component.`);
2687
+ }
2688
+ };
2689
+ var MethodNotCallableError = class extends LiveError {
2690
+ name = "MethodNotCallableError";
2691
+ kind = "Authorization";
2692
+ status = 403;
2693
+ constructor(method, component) {
2694
+ super(`Unable to call component method. Public method [${method}] not found on component [${component}].`);
2695
+ }
2696
+ };
2697
+ var PropertyNotUpdatableError = class extends LiveError {
2698
+ name = "PropertyNotUpdatableError";
2699
+ kind = "Authorization";
2700
+ status = 403;
2701
+ constructor(path, component) {
2702
+ super(`Cannot update property [${path}] on component [${component}]: it is not a public, unlocked property.`);
2703
+ }
2704
+ };
2705
+ var LiveComponentNotFoundError = class extends LiveError {
2706
+ name = "LiveComponentNotFoundError";
2707
+ kind = "Http";
2708
+ status = 404;
2709
+ constructor(component) {
2710
+ super(`Unable to find component: [${component}].`);
2711
+ }
2712
+ };
2713
+ var InvalidLiveRequestError = class extends LiveError {
2714
+ name = "InvalidLiveRequestError";
2715
+ kind = "Http";
2716
+ status = 400;
2717
+ };
2718
+ var ESCAPES = { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#039;" };
2719
+ function escapeAttribute2(value) {
2720
+ return value.replace(/[&<>"']/g, (char) => ESCAPES[char]);
2721
+ }
2722
+ var VOID = /* @__PURE__ */ new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "source", "track", "wbr"]);
2723
+ var RAW_TEXT = /* @__PURE__ */ new Set(["script", "style", "textarea", "title"]);
2724
+ var TOKEN = /<\/([A-Za-z][\w:-]*)\s*>|<([A-Za-z][\w:-]*)(?:\s+[^\s"'<>/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?)*\s*(\/?)>/g;
2725
+ function rootTag(html, component) {
2726
+ const fail22 = () => {
2727
+ throw new LiveError(`Live only supports one HTML element per component. Multiple root elements were detected for component [${component}].`);
2728
+ };
2729
+ const source = html.replace(/<!--[\s\S]*?-->/g, "").trim();
2730
+ let depth = 0;
2731
+ let root;
2732
+ let rootEnd = -1;
2733
+ TOKEN.lastIndex = 0;
2734
+ for (let match = TOKEN.exec(source); match !== null; match = TOKEN.exec(source)) {
2735
+ const [token2, closing, opening, selfClosing] = match;
2736
+ if (root === void 0 && match.index !== 0) fail22();
2737
+ if (depth === 0 && root !== void 0) fail22();
2738
+ if (opening !== void 0) {
2739
+ const tag = opening.toLowerCase();
2740
+ root ??= tag;
2741
+ if (selfClosing === "/" || VOID.has(tag)) {
2742
+ if (depth === 0) rootEnd = match.index + token2.length;
2743
+ continue;
2744
+ }
2745
+ if (RAW_TEXT.has(tag)) {
2746
+ const closer = new RegExp(`</${tag}`, "gi");
2747
+ closer.lastIndex = TOKEN.lastIndex;
2748
+ const end = closer.exec(source);
2749
+ if (end !== null) TOKEN.lastIndex = end.index;
2750
+ }
2751
+ depth++;
2752
+ } else if (closing !== void 0) {
2753
+ if (--depth < 0) fail22();
2754
+ if (depth === 0) rootEnd = match.index + token2.length;
2755
+ }
2756
+ }
2757
+ if (root === void 0 || depth !== 0 || source.slice(rootEnd).trim() !== "") fail22();
2758
+ return root;
2759
+ }
2760
+ function insertAttributesIntoRoot(html, attributes) {
2761
+ const source = html.trim();
2762
+ const match = /^((?:<!--[\s\S]*?-->\s*)*<[A-Za-z][\w:-]*)/.exec(source);
2763
+ if (match === null) throw new LiveError("Live could not find the root element of the component.");
2764
+ const rendered = Object.entries(attributes).map(([name, value]) => ` ${name}="${escapeAttribute2(value)}"`).join("");
2765
+ return match[1] + rendered + source.slice(match[1].length);
2766
+ }
2767
+ var fail = (message, actual, expected) => {
2768
+ throw new AssertionError({ message, actual, expected, operator: "live" });
2769
+ };
2770
+ var getPath = (target2, path) => path.split(".").reduce((node, segment) => typeof node === "object" && node !== null ? node[segment] : void 0, target2);
2771
+ var TestRequestScope = token("Testing.RequestScope");
2772
+ function requestScope2() {
2773
+ const container = currentContainer() ?? Container.getInstance();
2774
+ return container?.bound(TestRequestScope) === true ? container.make(TestRequestScope) : (run) => run();
2775
+ }
2776
+ var Testable = class _Testable {
2777
+ constructor(manager, scope, result) {
2778
+ this.manager = manager;
2779
+ this.scope = scope;
2780
+ this.#snapshot = JSON.stringify(result.snapshot);
2781
+ this.#html = result.html ?? "";
2782
+ this.#effects = result.effects;
2783
+ this.#instance = result.instance;
485
2784
  }
486
- appPath(path = "") {
487
- return this.basePath(join3("app", path));
2785
+ manager;
2786
+ scope;
2787
+ #snapshot;
2788
+ #html;
2789
+ #effects;
2790
+ #instance;
2791
+ /** Mounts `component` with `params`, as `<Live component={…} {...params} />` would. */
2792
+ static async create(component, params = {}) {
2793
+ const scope = requestScope2();
2794
+ const manager = inject(LiveManager);
2795
+ return new _Testable(manager, scope, await scope(() => manager.mountComponent(component, params)));
2796
+ }
2797
+ /** The component instance after the last request. */
2798
+ instance() {
2799
+ return this.#instance;
2800
+ }
2801
+ /** A public property (dotted paths reach into arrays and objects). */
2802
+ get(path) {
2803
+ return getPath(this.#instance, path);
488
2804
  }
489
- bootstrapPath(path = "") {
490
- return this.basePath(join3("bootstrap", path));
2805
+ /** The last rendered HTML, without the o:id, o:snapshot and o:effects attributes (Livewire strips them too). */
2806
+ html() {
2807
+ return this.#html.replace(/ o:(id|snapshot|effects)="[^"]*"/g, "");
491
2808
  }
492
- configPath(path = "") {
493
- return this.basePath(join3("config", path));
2809
+ /** The effects of the last request. */
2810
+ effects() {
2811
+ return this.#effects;
494
2812
  }
495
- databasePath(path = "") {
496
- return this.basePath(join3("database", path));
2813
+ async set(path, value) {
2814
+ return this.#update(typeof path === "string" ? { [path]: value } : path, []);
497
2815
  }
498
- publicPath(path = "") {
499
- return this.basePath(join3("public", path));
2816
+ async call(method, ...params) {
2817
+ return this.#update({}, [{ method, params }]);
500
2818
  }
501
- resourcePath(path = "") {
502
- return this.basePath(join3("resources", path));
2819
+ async toggle(path) {
2820
+ return this.#update({}, [{ method: "$toggle", params: [path] }]);
503
2821
  }
504
- storagePath(path = "") {
505
- return this.basePath(join3("storage", path));
2822
+ /** Dispatches an event to this component, as the browser does for its listeners. */
2823
+ async dispatch(event, params = {}) {
2824
+ return this.#update({}, [{ method: "__dispatch", params: [event, params] }]);
506
2825
  }
507
- environment(...environments) {
508
- const fallback = process.env.APP_ENV ?? "production";
509
- const current = this.bound(ConfigRepository) ? String(this.make(ConfigRepository).get("app.env", fallback)) : fallback;
510
- return environments.length === 0 ? current : environments.some((pattern) => Str.is(pattern, current));
2826
+ async refresh() {
2827
+ return this.#update({}, [{ method: "$refresh", params: [] }]);
511
2828
  }
512
- isProduction() {
513
- return this.environment("production");
2829
+ async #update(updates, calls) {
2830
+ const result = await this.scope(() => this.manager.updateComponent(this.#snapshot, updates, calls));
2831
+ this.#snapshot = JSON.stringify(result.snapshot);
2832
+ if (result.html !== void 0) this.#html = result.html;
2833
+ this.#effects = result.effects;
2834
+ this.#instance = result.instance;
2835
+ return this;
514
2836
  }
515
- isLocal() {
516
- return this.environment("local");
2837
+ assertSet(path, value) {
2838
+ const actual = this.get(path);
2839
+ if (!isDeepStrictEqual(actual, value)) fail(`Failed asserting that property [${path}] is set to the expected value.`, actual, value);
2840
+ return this;
517
2841
  }
518
- runningUnitTests() {
519
- return this.environment("testing");
2842
+ assertNotSet(path, value) {
2843
+ if (isDeepStrictEqual(this.get(path), value)) fail(`Failed asserting that property [${path}] is not set to the given value.`, this.get(path), value);
2844
+ return this;
520
2845
  }
521
- runningInConsole() {
522
- return this.inConsole;
2846
+ /** Each value, HTML-escaped, appears in the rendered HTML. */
2847
+ assertSee(...values) {
2848
+ for (const value of values) if (!this.html().includes(escapeAttribute2(value))) fail(`Failed asserting that [${value}] is in the rendered HTML.`, this.html(), value);
2849
+ return this;
523
2850
  }
524
- setRunningInConsole(value = true) {
525
- this.inConsole = value;
2851
+ assertDontSee(...values) {
2852
+ for (const value of values) if (this.html().includes(escapeAttribute2(value))) fail(`Failed asserting that [${value}] is not in the rendered HTML.`, this.html(), value);
2853
+ return this;
526
2854
  }
527
- async register(provider) {
528
- const cls = typeof provider === "function" ? provider : provider.constructor;
529
- const existing = this.providers.find((p) => p.constructor === cls);
530
- if (existing) return existing;
531
- const instance = typeof provider === "function" ? new provider(this) : provider;
532
- try {
533
- instance.register?.();
534
- } catch (error) {
535
- throw new ProviderRegisterError(instance.constructor.name, error);
2855
+ /** Each value appears in the rendered HTML as written (not escaped). */
2856
+ assertSeeHtml(...values) {
2857
+ for (const value of values) if (!this.html().includes(value)) fail(`Failed asserting that [${value}] is in the rendered HTML.`, this.html(), value);
2858
+ return this;
2859
+ }
2860
+ assertDispatched(name, params) {
2861
+ const dispatched = (this.#effects.dispatches ?? []).filter((event) => event.name === name);
2862
+ if (dispatched.length === 0) fail(`Failed asserting that an event [${name}] was dispatched.`, this.#effects.dispatches ?? [], name);
2863
+ if (params !== void 0 && !dispatched.some((event) => isDeepStrictEqual(event.params, params))) {
2864
+ fail(`Failed asserting that event [${name}] was dispatched with the expected params.`, dispatched.map((event) => event.params), params);
536
2865
  }
537
- this.providers.push(instance);
538
- if (this.hasBooted) await this.bootProvider(instance);
539
- return instance;
2866
+ return this;
540
2867
  }
541
- getProviders() {
542
- return [...this.providers];
2868
+ assertNotDispatched(name) {
2869
+ if ((this.#effects.dispatches ?? []).some((event) => event.name === name)) fail(`Failed asserting that an event [${name}] was not dispatched.`);
2870
+ return this;
543
2871
  }
544
- boot() {
545
- return this.bootPromise ??= this.runBoot();
2872
+ /** The error bag has errors for every key given (or any error at all with no keys). */
2873
+ assertHasErrors(keys = []) {
2874
+ const errors = stateOf(this.#instance).errors;
2875
+ const wanted = [keys].flat();
2876
+ if (wanted.length === 0 && Object.keys(errors).length === 0) fail("Component has no errors.", errors);
2877
+ for (const key of wanted) if ((errors[key]?.length ?? 0) === 0) fail(`Component missing error: ${key}`, errors, key);
2878
+ return this;
546
2879
  }
547
- isBooted() {
548
- return this.hasBooted;
2880
+ /** No errors for the keys given (or none at all with no keys). */
2881
+ assertHasNoErrors(keys = []) {
2882
+ const errors = stateOf(this.#instance).errors;
2883
+ const wanted = [keys].flat();
2884
+ const found = wanted.length === 0 ? Object.keys(errors).filter((key) => (errors[key]?.length ?? 0) > 0) : wanted.filter((key) => (errors[key]?.length ?? 0) > 0);
2885
+ if (found.length > 0) fail(`Component has errors: "${found.join('", "')}"`, errors);
2886
+ return this;
549
2887
  }
550
- booting(callback) {
551
- this.bootingCallbacks.push(callback);
2888
+ assertRedirect(url2) {
2889
+ const redirect2 = this.#effects.redirect;
2890
+ if (redirect2 === void 0) fail("Component did not perform a redirect.");
2891
+ if (url2 !== void 0 && redirect2 !== url2) fail("Component redirected to an unexpected URL.", redirect2, url2);
2892
+ return this;
552
2893
  }
553
- /** If already booted, the callback runs immediately (a rejection surfaces as an unhandled rejection). */
554
- booted(callback) {
555
- this.bootedCallbacks.push(callback);
556
- if (this.hasBooted) void callback(this);
2894
+ assertNoRedirect() {
2895
+ if (this.#effects.redirect !== void 0) fail("Component performed a redirect.", this.#effects.redirect);
2896
+ return this;
557
2897
  }
558
- terminating(callback) {
559
- this.terminatingCallbacks.push(callback);
2898
+ /** The last call returned `value`. */
2899
+ assertReturned(value) {
2900
+ const returned = this.#effects.returns?.at(-1);
2901
+ if (!isDeepStrictEqual(returned, value)) fail("Failed asserting that the method returned the expected value.", returned, value);
2902
+ return this;
560
2903
  }
561
- async handle(request) {
562
- await this.boot();
563
- const scope = this.createScope();
564
- return scope.run(() => scope.make(HttpKernel).handle(request));
2904
+ };
2905
+ async function live(component, params = {}, key) {
2906
+ const html = await inject(LiveManager).mount(component, params, key === void 0 ? void 0 : String(key));
2907
+ return { toHtml: () => html };
2908
+ }
2909
+ function LiveComponent({ component, "o:key": key, children: _children, ...params }) {
2910
+ return live(component, params, key);
2911
+ }
2912
+ var Live = Object.assign(LiveComponent, {
2913
+ /** Registers a component under a name (components in app/Live are registered at boot). */
2914
+ component(name, component) {
2915
+ inject(LiveManager).register(component, name);
2916
+ },
2917
+ /** Livewire::test(): mounts `component` for assertions. */
2918
+ test(component, params = {}) {
2919
+ return Testable.create(component, params);
565
2920
  }
566
- async terminate() {
567
- for (const callback of this.terminatingCallbacks) await callback();
2921
+ });
2922
+ function canonicalJson(value) {
2923
+ if (Array.isArray(value)) return `[${value.map((item) => canonicalJson(item === void 0 ? null : item)).join(",")}]`;
2924
+ if (typeof value === "object" && value !== null) {
2925
+ const entries = Object.keys(value).sort().filter((key) => value[key] !== void 0).map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`);
2926
+ return `{${entries.join(",")}}`;
568
2927
  }
569
- async runBoot() {
570
- for (const callback of this.bootingCallbacks) await callback(this);
571
- for (let i = 0; i < this.providers.length; i++) await this.bootProvider(this.providers[i]);
572
- this.hasBooted = true;
573
- for (const callback of this.bootedCallbacks) await callback(this);
2928
+ return JSON.stringify(value);
2929
+ }
2930
+ function checksum(key, payload) {
2931
+ return createHmac("sha256", key).update(canonicalJson({ data: payload.data, memo: payload.memo })).digest("hex");
2932
+ }
2933
+ function verifyChecksum(key, payload, given) {
2934
+ if (typeof given !== "string") return false;
2935
+ const expected = Buffer.from(checksum(key, payload), "utf8");
2936
+ const actual = Buffer.from(given, "utf8");
2937
+ return expected.length === actual.length && timingSafeEqual(expected, actual);
2938
+ }
2939
+ function componentName(file) {
2940
+ return file.replace(/\.(tsx|ts|jsx|js|mts|mjs)$/, "").split(/[\\/]/).map((segment) => Str.kebab(segment)).join(".");
2941
+ }
2942
+ function defaultName(cls) {
2943
+ const name = cls.name;
2944
+ return typeof name === "string" && name !== "" ? Str.kebab(name) : void 0;
2945
+ }
2946
+ function componentFiles(directory) {
2947
+ if (!existsSync5(directory)) return [];
2948
+ return readdirSync2(directory, { recursive: true, encoding: "utf8" }).filter((file) => /\.(tsx|ts|jsx|js|mts|mjs)$/.test(file) && !/\.(test|spec|d)\.[^.]+$/.test(file)).map((file) => file.split(sep2).join("/")).sort();
2949
+ }
2950
+ async function discover(directory) {
2951
+ const found = [];
2952
+ for (const file of componentFiles(directory)) {
2953
+ const module = await import(
2954
+ /* @vite-ignore */
2955
+ pathToFileURL2(join5(directory, file)).href
2956
+ );
2957
+ found.push([componentName(relative2(directory, join5(directory, file))), module.default]);
574
2958
  }
575
- async bootProvider(provider) {
2959
+ return found;
2960
+ }
2961
+ var PERSISTENT_MIDDLEWARE = ["auth", "verified", "guest"];
2962
+ function canMatch(router) {
2963
+ const methods = router;
2964
+ return ["getRoutesByMatchOrder", "getPatterns", "getPatternVersion", "getMiddleware", "getMiddlewareGroups"].every((name) => typeof methods[name] === "function");
2965
+ }
2966
+ function isMatchingRoute(route2) {
2967
+ const candidate = route2;
2968
+ return typeof candidate === "object" && candidate !== null && Array.isArray(candidate.methods) && ["match", "gatherMiddleware", "excludedMiddleware"].every((name) => typeof candidate[name] === "function");
2969
+ }
2970
+ function routePath(path) {
2971
+ const trimmed = path.replace(/^\/+|\/+$/g, "").replace(/\/{2,}/g, "/");
2972
+ return trimmed === "" ? "" : `/${trimmed}`;
2973
+ }
2974
+ function expand(references, aliases, groups, out = [], visiting = /* @__PURE__ */ new Set()) {
2975
+ for (const reference of references) {
2976
+ if (typeof reference === "function") {
2977
+ out.push({ cls: reference, parameters: [] });
2978
+ continue;
2979
+ }
2980
+ const index = reference.indexOf(":");
2981
+ const name = index === -1 ? reference : reference.slice(0, index);
2982
+ if (index === -1 && Object.hasOwn(groups, name)) {
2983
+ if (visiting.has(name)) continue;
2984
+ visiting.add(name);
2985
+ expand(groups[name], aliases, groups, out, visiting);
2986
+ visiting.delete(name);
2987
+ } else if (Object.hasOwn(aliases, name)) {
2988
+ out.push({ cls: aliases[name], parameters: index === -1 ? [] : reference.slice(index + 1).split(",") });
2989
+ }
2990
+ }
2991
+ return out;
2992
+ }
2993
+ function persistentMiddleware(router, persistent, method, path) {
2994
+ if (!canMatch(router)) throw new LiveError("Live re-applies the page route's middleware to updates, which needs Oriel's router.");
2995
+ const routes = router.getRoutesByMatchOrder();
2996
+ if (!Array.isArray(routes) || !routes.every(isMatchingRoute)) throw new LiveError("Live re-applies the page route's middleware to updates, which needs Oriel's routes.");
2997
+ const verb = method.toUpperCase();
2998
+ const target2 = routePath(path);
2999
+ const patterns = router.getPatterns();
3000
+ const version = router.getPatternVersion();
3001
+ const route2 = routes.find((candidate) => candidate.methods.includes(verb) && candidate.match(target2, patterns, version) !== void 0);
3002
+ if (route2 === void 0) return [];
3003
+ const aliases = router.getMiddleware();
3004
+ const groups = router.getMiddlewareGroups();
3005
+ const wanted = new Set(persistent.flatMap((name) => Object.hasOwn(aliases, name) ? [aliases[name]] : []));
3006
+ const excluded = new Set(expand(route2.excludedMiddleware(), aliases, groups).map((entry) => entry.cls));
3007
+ const ids = /* @__PURE__ */ new Map();
3008
+ const seen = /* @__PURE__ */ new Set();
3009
+ return expand(route2.gatherMiddleware(), aliases, groups).filter((entry) => {
3010
+ if (!ids.has(entry.cls)) ids.set(entry.cls, ids.size);
3011
+ const key = `${ids.get(entry.cls)}:${entry.parameters.join(",")}`;
3012
+ if (!wanted.has(entry.cls) || excluded.has(entry.cls) || seen.has(key)) return false;
3013
+ seen.add(key);
3014
+ return true;
3015
+ });
3016
+ }
3017
+ async function throughMiddleware(container, request2, middleware, destination) {
3018
+ let ran;
3019
+ const run = async (index) => {
3020
+ if (index === middleware.length) {
3021
+ ran = { result: await destination() };
3022
+ return new Response(null, { status: 204 });
3023
+ }
3024
+ const { cls, parameters } = middleware[index];
3025
+ const instance = container.make(cls);
3026
+ const next = () => run(index + 1);
3027
+ return instance.handle(request2, next, ...parameters);
3028
+ };
3029
+ const response2 = await run(0);
3030
+ return ran ? ran.result : response2;
3031
+ }
3032
+ function isModel(value) {
3033
+ if (typeof value !== "object" || value === null) return false;
3034
+ const candidate = value;
3035
+ return typeof candidate.getKey === "function" && typeof candidate.getKeyName === "function" && typeof candidate.getTable === "function" && typeof candidate.exists === "boolean";
3036
+ }
3037
+ var isPlainObject2 = (value) => {
3038
+ const proto = Object.getPrototypeOf(value);
3039
+ return proto === Object.prototype || proto === null;
3040
+ };
3041
+ var isTuple = (value) => Array.isArray(value) && value.length === 2 && typeof value[1] === "object" && value[1] !== null && !Array.isArray(value[1]) && typeof value[1].s === "string";
3042
+ function classNameOf(registry, cls) {
3043
+ return registry.nameOf(cls) ?? registry.register(cls);
3044
+ }
3045
+ function dehydrate(value, registry, path, seen = /* @__PURE__ */ new Set()) {
3046
+ switch (typeof value) {
3047
+ case "undefined":
3048
+ return [null, { s: "u" }];
3049
+ case "string":
3050
+ case "boolean":
3051
+ return value;
3052
+ case "number":
3053
+ if (!Number.isFinite(value)) throw new LiveError(`Property [${path}] is ${value}: Live state must be JSON (finite numbers only).`);
3054
+ return value;
3055
+ case "bigint":
3056
+ case "symbol":
3057
+ case "function":
3058
+ throw new LiveError(`Property [${path}] holds a ${typeof value}: Live state may only hold primitives, arrays, plain objects, Dates, models and collections.`);
3059
+ }
3060
+ if (value === null) return null;
3061
+ const object = value;
3062
+ if (seen.has(object)) throw new LiveError(`Property [${path}] contains a circular reference.`);
3063
+ seen.add(object);
3064
+ try {
3065
+ if (object instanceof Date) return [object.toISOString(), { s: "dt" }];
3066
+ if (Array.isArray(object)) return [object.map((item, i) => dehydrate(item, registry, `${path}.${i}`, seen)), { s: "arr" }];
3067
+ if (isModel(object)) {
3068
+ const name = classNameOf(registry, object.constructor);
3069
+ if (!object.exists) throw new LiveError(`Property [${path}] holds an unsaved [${name}] model: only saved models can be kept between requests.`);
3070
+ return [null, { s: "mdl", class: name, key: object.getKey() }];
3071
+ }
3072
+ if (object instanceof Collection) {
3073
+ const items = object.all();
3074
+ const first = items[0];
3075
+ if (items.length > 0 && isModel(first) && items.every((item) => isModel(item) && item.constructor === first.constructor)) {
3076
+ for (const [i, item] of items.entries()) {
3077
+ if (!item.exists) throw new LiveError(`Property [${path}.${i}] holds an unsaved model.`);
3078
+ }
3079
+ const name = classNameOf(registry, first.constructor);
3080
+ return [null, { s: "elcln", class: name, keys: items.map((item) => item.getKey()) }];
3081
+ }
3082
+ return [items.map((item, i) => dehydrate(item, registry, `${path}.${i}`, seen)), { s: "clctn" }];
3083
+ }
3084
+ if (isPlainObject2(object)) {
3085
+ const out = {};
3086
+ for (const [key, item] of Object.entries(object)) out[key] = dehydrate(item, registry, `${path}.${key}`, seen);
3087
+ return [out, { s: "obj" }];
3088
+ }
3089
+ const type = object.constructor?.name || "object";
3090
+ throw new LiveError(
3091
+ `Property [${path}] holds a [${type}] instance: Live state may only hold primitives, arrays, plain objects, Dates, models and collections.`
3092
+ );
3093
+ } finally {
3094
+ seen.delete(object);
3095
+ }
3096
+ }
3097
+ function modelClass(registry, name) {
3098
+ if (typeof name !== "string" || !registry.has(name)) throw new LiveError(`Unable to hydrate the [${String(name)}] model: it is not registered.`);
3099
+ return registry.resolve(name);
3100
+ }
3101
+ async function hydrate(value, registry) {
3102
+ if (!isTuple(value)) return value;
3103
+ const [inner, meta] = value;
3104
+ switch (meta.s) {
3105
+ case "u":
3106
+ return void 0;
3107
+ case "dt":
3108
+ return new Date(String(inner));
3109
+ case "arr":
3110
+ return Promise.all(inner.map((item) => hydrate(item, registry)));
3111
+ case "clctn":
3112
+ return new Collection(await Promise.all(inner.map((item) => hydrate(item, registry))));
3113
+ case "obj": {
3114
+ const out = {};
3115
+ for (const [key, item] of Object.entries(inner)) {
3116
+ Object.defineProperty(out, key, { value: await hydrate(item, registry), enumerable: true, writable: true, configurable: true });
3117
+ }
3118
+ return out;
3119
+ }
3120
+ case "mdl":
3121
+ return modelClass(registry, meta.class).findOrFail(meta.key);
3122
+ case "elcln": {
3123
+ const cls = modelClass(registry, meta.class);
3124
+ const keys = meta.keys ?? [];
3125
+ const keyName = new cls().getKeyName();
3126
+ const found = [...await cls.query().whereIn(keyName, keys).get()];
3127
+ const byKey = new Map(found.map((model) => [String(model.getKey()), model]));
3128
+ return new Collection(keys.map((key) => byKey.get(String(key))).filter((model) => model !== void 0));
3129
+ }
3130
+ }
3131
+ throw new LiveError(`Unknown snapshot value type [${String(meta.s)}].`);
3132
+ }
3133
+ var HOOKS = /* @__PURE__ */ new Set(["constructor", "render", "mount", "boot", "booted", "hydrate", "dehydrate", "updating", "updated", "rendering", "rendered", "exception"]);
3134
+ var HOOK_PREFIX = /^(updating|updated|hydrate|dehydrate)[A-Z_]/;
3135
+ var UNSAFE_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
3136
+ var BASE_API = new Set(Object.getOwnPropertyNames(Component.prototype));
3137
+ var INDEX = /^(?:0|[1-9]\d*)$/;
3138
+ function isArraySlot(array, segment) {
3139
+ return INDEX.test(segment) && Number(segment) <= array.length;
3140
+ }
3141
+ function isCallable(instance, method) {
3142
+ if (method === "" || method.startsWith("_") || method.startsWith("$") || method.startsWith("#")) return false;
3143
+ if (HOOKS.has(method) || HOOK_PREFIX.test(method) || BASE_API.has(method)) return false;
3144
+ if (lockedOf(instance.constructor).has(method)) return false;
3145
+ const isBase = (proto) => proto === Component.prototype || proto === Object.prototype;
3146
+ for (let proto = Object.getPrototypeOf(instance); proto !== null && !isBase(proto); proto = Object.getPrototypeOf(proto)) {
3147
+ const descriptor = Object.getOwnPropertyDescriptor(proto, method);
3148
+ if (descriptor !== void 0) return typeof descriptor.value === "function";
3149
+ }
3150
+ return false;
3151
+ }
3152
+ function isUpdatable(instance, path) {
3153
+ const segments = path.split(".");
3154
+ const root = segments[0];
3155
+ if (root === "" || root.startsWith("_") || segments.some((segment) => segment === "" || UNSAFE_SEGMENTS.has(segment))) return false;
3156
+ if (!Object.hasOwn(instance, root)) return false;
3157
+ const descriptor = Object.getOwnPropertyDescriptor(instance, root);
3158
+ if (!descriptor.enumerable || descriptor.get !== void 0) return false;
3159
+ const cls = instance.constructor;
3160
+ if (lockedOf(cls).has(root) || computedOf(cls).has(root)) return false;
3161
+ let node = descriptor.value;
3162
+ for (const segment of segments.slice(1)) {
3163
+ if (isModel(node) || node instanceof Collection) return false;
3164
+ if (Array.isArray(node) && !isArraySlot(node, segment)) return false;
3165
+ if (typeof node !== "object" || node === null || !Object.hasOwn(node, segment)) return true;
3166
+ node = node[segment];
3167
+ }
3168
+ return !isModel(node) && !(node instanceof Collection);
3169
+ }
3170
+ var renderContext = new AsyncLocalStorage();
3171
+ var pageRequest = new AsyncLocalStorage();
3172
+ function optional(container, token2) {
3173
+ if (!container.bound(token2)) return void 0;
3174
+ try {
3175
+ return container.make(token2);
3176
+ } catch {
3177
+ return void 0;
3178
+ }
3179
+ }
3180
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3181
+ function setOwn(object, key, value) {
3182
+ Object.defineProperty(object, key, { value, enumerable: true, writable: true, configurable: true });
3183
+ }
3184
+ async function hook(instance, name, ...args) {
3185
+ const method = instance[name];
3186
+ if (typeof method === "function") await method.apply(instance, args);
3187
+ }
3188
+ var LiveComponentManager = class {
3189
+ constructor(app) {
3190
+ this.app = app;
3191
+ }
3192
+ app;
3193
+ /** The request scope when there is one (sessions, the current route), else the application. */
3194
+ container() {
3195
+ return currentContainer() ?? this.app;
3196
+ }
3197
+ registry() {
3198
+ return this.container().make(ClassRegistry);
3199
+ }
3200
+ /** Live.component('counter', Counter): registers a component under a name the snapshots use. */
3201
+ register(component, name) {
3202
+ if (!isComponentClass(component)) throw new LiveError(`[${component.name}] is not a Live component: it must extend Component.`);
3203
+ const resolved = name ?? defaultName(component);
3204
+ if (resolved === void 0) throw new LiveError("Anonymous component classes need a name: Live.component(name, Class).");
3205
+ this.registry().register(component, resolved);
3206
+ }
3207
+ /** The registered name of a component class, registering it under its kebab-case class name the first time. */
3208
+ nameOf(component) {
3209
+ const registry = this.registry();
3210
+ const existing = registry.nameOf(component);
3211
+ if (existing !== void 0) return existing;
3212
+ this.register(component);
3213
+ return registry.nameOf(component);
3214
+ }
3215
+ resolve(name) {
3216
+ const registry = this.registry();
3217
+ if (!registry.has(name)) throw new LiveComponentNotFoundError(name);
3218
+ const cls = registry.resolve(name);
3219
+ if (!isComponentClass(cls)) throw new LiveComponentNotFoundError(name);
3220
+ return cls;
3221
+ }
3222
+ isLiveRequest(request2) {
3223
+ return request2.header("X-Oriel-Live") === "true";
3224
+ }
3225
+ /** The first render: HTML with o:id, o:snapshot and o:effects on the root element. */
3226
+ async mount(component, params = {}, key) {
3227
+ const context = renderContext.getStore();
3228
+ if (context === void 0) return (await this.mountComponent(component, params)).html;
3229
+ const childKey = key ?? `lw-${context.index++}`;
3230
+ const existing = Object.hasOwn(context.previous, childKey) ? context.previous[childKey] : void 0;
3231
+ if (existing !== void 0) {
3232
+ setOwn(context.children, childKey, existing);
3233
+ return `<${existing[0]} o:id="${escapeAttribute2(existing[1])}"></${existing[0]}>`;
3234
+ }
3235
+ const result = await this.mountComponent(component, params);
3236
+ setOwn(context.children, childKey, [result.tag, result.snapshot.memo.id]);
3237
+ return result.html;
3238
+ }
3239
+ async mountComponent(component, params = {}, existing) {
3240
+ const cls = typeof component === "string" ? this.resolve(component) : component;
3241
+ if (!isComponentClass(cls)) throw new LiveError(`[${cls.name}] is not a Live component: it must extend Component.`);
3242
+ const name = this.nameOf(cls);
3243
+ const instance = existing ?? this.container().make(cls);
3244
+ const state = stateOf(instance);
3245
+ state.id = randomBytes2(15).toString("base64url");
3246
+ state.name = name;
3247
+ await hook(instance, "boot");
3248
+ for (const [property, value] of Object.entries(params)) {
3249
+ if (Object.hasOwn(instance, property)) instance[property] = value;
3250
+ }
3251
+ this.prepareComputed(instance);
3252
+ await hook(instance, "mount", params);
3253
+ const request2 = optional(this.container(), HttpRequest);
3254
+ const page = pageRequest.getStore() ?? { path: request2?.path() ?? "", method: request2?.method() ?? "GET" };
3255
+ const memo = { id: state.id, name, path: page.path, method: page.method, children: {}, errors: {} };
3256
+ const { html, tag } = await this.render(instance, memo, {});
3257
+ const effects = this.effects(instance);
3258
+ const listeners = Object.keys(listenersOf(cls));
3259
+ if (listeners.length > 0) effects.listeners = listeners;
3260
+ const snapshot = await this.snapshot(instance, memo);
3261
+ const attributes = { "o:snapshot": JSON.stringify(snapshot), "o:effects": JSON.stringify(effects), "o:id": state.id };
3262
+ return { instance, snapshot, effects, html: insertAttributesIntoRoot(html, attributes), tag };
3263
+ }
3264
+ /** Applies each component's updates and calls, re-renders it and returns its new snapshot and effects. */
3265
+ async update(request2) {
3266
+ const components = [];
3267
+ for (const component of request2.components) {
3268
+ const { memo } = this.verify(component.snapshot);
3269
+ const result = await pageRequest.run({ path: memo.path, method: memo.method }, () => this.updateComponent(component.snapshot, component.updates, component.calls));
3270
+ components.push({ snapshot: JSON.stringify(result.snapshot), effects: result.effects });
3271
+ }
3272
+ return { components };
3273
+ }
3274
+ async updateComponent(snapshotJson, updates, calls) {
3275
+ const snapshot = this.verify(snapshotJson);
3276
+ const cls = this.resolve(snapshot.memo.name);
3277
+ const instance = this.container().make(cls);
3278
+ const state = stateOf(instance);
3279
+ state.id = snapshot.memo.id;
3280
+ state.name = snapshot.memo.name;
3281
+ state.errors = { ...snapshot.memo.errors };
3282
+ await hook(instance, "boot");
3283
+ const registry = this.registry();
3284
+ for (const [property, value] of Object.entries(snapshot.data)) {
3285
+ Object.defineProperty(instance, property, { value: await hydrate(value, registry), enumerable: true, writable: true, configurable: true });
3286
+ }
3287
+ this.prepareComputed(instance);
3288
+ await hook(instance, "hydrate");
3289
+ const returns = [];
576
3290
  try {
577
- await provider.boot?.();
3291
+ for (const [path, value] of Object.entries(updates)) await this.applyUpdate(instance, path, value);
3292
+ for (const call of calls) returns.push(await this.call(instance, call.method, call.params));
578
3293
  } catch (error) {
579
- throw new ProviderBootError(provider.constructor.name, error);
3294
+ const errors = error.errors;
3295
+ if (error.kind !== "Validation" || !isRecord(errors)) throw error;
3296
+ state.errors = { ...errors };
3297
+ }
3298
+ const memo = { ...snapshot.memo, children: { ...snapshot.memo.children } };
3299
+ const effects = this.effects(instance);
3300
+ let html;
3301
+ let tag;
3302
+ if (!state.skipRender) {
3303
+ ;
3304
+ ({ html, tag } = await this.render(instance, memo, snapshot.memo.children));
3305
+ html = insertAttributesIntoRoot(html, { "o:id": state.id });
3306
+ effects.html = html;
580
3307
  }
3308
+ if (calls.length > 0) effects.returns = returns;
3309
+ return { instance, snapshot: await this.snapshot(instance, memo), effects, html, tag };
581
3310
  }
582
- };
583
- var ClosureKernel = class {
584
- routes = /* @__PURE__ */ new Map();
585
- on(method, path, handler) {
586
- this.routes.set(`${method.toUpperCase()} ${path}`, handler);
587
- return this;
3311
+ /**
3312
+ * The route action of POST /oriel/live/update. Each component's update runs behind the auth middleware of the route
3313
+ * that rendered its page (Livewire's persistent middleware): a signed-out tab gets a 401, never the action. When a
3314
+ * middleware answers instead (a redirect), that is the response.
3315
+ */
3316
+ async handle(request2) {
3317
+ if (!this.isLiveRequest(request2)) throw new InvalidLiveRequestError("Live update requests need the X-Oriel-Live header.");
3318
+ const container = this.container();
3319
+ const persistent = optional(container, ConfigRepository)?.get("live.persistent_middleware", PERSISTENT_MIDDLEWARE) ?? PERSISTENT_MIDDLEWARE;
3320
+ const components = [];
3321
+ for (const component of this.payload(request2).components) {
3322
+ const { memo } = this.verify(component.snapshot);
3323
+ const middleware = persistentMiddleware(container.make(Router), persistent, memo.method, memo.path);
3324
+ const result = await pageRequest.run(
3325
+ { path: memo.path, method: memo.method },
3326
+ () => throughMiddleware(container, request2, middleware, () => this.updateComponent(component.snapshot, component.updates, component.calls))
3327
+ );
3328
+ if (result instanceof Response) return result;
3329
+ components.push({ snapshot: JSON.stringify(result.snapshot), effects: result.effects });
3330
+ }
3331
+ return { components };
3332
+ }
3333
+ /** A full-page component (Route.get('/counter', Counter)): mounted with the route parameters, inside the layout. */
3334
+ async page(component) {
3335
+ const container = this.container();
3336
+ const params = { ...optional(container, CurrentRoute)?.parameters ?? {} };
3337
+ const cls = component.constructor;
3338
+ const result = await this.mountComponent(cls, params, component);
3339
+ const redirect2 = result.effects.redirect;
3340
+ if (redirect2 !== void 0) {
3341
+ return optional(container, Redirector)?.to(redirect2) ?? new Response(null, { status: 302, headers: { Location: redirect2 } });
3342
+ }
3343
+ const layout = cls.layout ?? optional(container, ConfigRepository)?.get("live.layout") ?? "components.layouts.app";
3344
+ const html = result.html;
3345
+ const data = { children: { toHtml: () => html } };
3346
+ if (cls.title !== void 0) data.title = cls.title;
3347
+ return container.make(ViewFactory).make(layout, data);
3348
+ }
3349
+ /** Parses and checks a snapshot: a bad checksum throws before anything is hydrated. */
3350
+ verify(snapshotJson) {
3351
+ let snapshot;
3352
+ try {
3353
+ snapshot = typeof snapshotJson === "string" ? JSON.parse(snapshotJson) : void 0;
3354
+ } catch {
3355
+ throw new CorruptComponentPayloadError();
3356
+ }
3357
+ if (!isRecord(snapshot) || !isRecord(snapshot.data) || !isRecord(snapshot.memo)) throw new CorruptComponentPayloadError();
3358
+ const memo = snapshot.memo;
3359
+ const name = typeof memo.name === "string" ? memo.name : "unknown";
3360
+ const key = this.key();
3361
+ let valid;
3362
+ try {
3363
+ valid = verifyChecksum(key, { data: snapshot.data, memo: snapshot.memo }, snapshot.checksum);
3364
+ } catch {
3365
+ valid = false;
3366
+ }
3367
+ if (!valid) throw new CorruptComponentPayloadError(name);
3368
+ return snapshot;
588
3369
  }
589
- async handle(request) {
590
- const handler = this.routes.get(`${request.method} ${new URL(request.url).pathname}`);
591
- if (!handler) return new Response("Not Found", { status: 404, headers: { "content-type": "text/plain; charset=utf-8" } });
592
- return toResponse(await handler(request));
3370
+ key() {
3371
+ return this.container().make(Encrypter).getKey();
3372
+ }
3373
+ /** The request body, read from the raw JSON when the request has it, so TrimStrings can't alter typed values. */
3374
+ payload(request2) {
3375
+ let body;
3376
+ try {
3377
+ const content = request2.content;
3378
+ body = typeof content === "function" ? JSON.parse(content.call(request2)) : request2.all();
3379
+ } catch {
3380
+ throw new InvalidLiveRequestError("The Live update request body is not valid JSON.");
3381
+ }
3382
+ const components = isRecord(body) ? body.components : void 0;
3383
+ const valid = Array.isArray(components) && components.every(
3384
+ (component) => isRecord(component) && typeof component.snapshot === "string" && isRecord(component.updates) && Array.isArray(component.calls) && component.calls.every((call) => isRecord(call) && typeof call.method === "string" && Array.isArray(call.params))
3385
+ );
3386
+ if (!valid) throw new InvalidLiveRequestError("The Live update request body is malformed.");
3387
+ return body;
3388
+ }
3389
+ /** Makes every computed getter (static `computed` too) cache its value for the rest of the request. */
3390
+ prepareComputed(instance) {
3391
+ for (const name of computedOf(instance.constructor)) {
3392
+ for (let proto = Object.getPrototypeOf(instance); proto !== null; proto = Object.getPrototypeOf(proto)) {
3393
+ const getter = Object.getOwnPropertyDescriptor(proto, name)?.get;
3394
+ if (getter === void 0) continue;
3395
+ Object.defineProperty(instance, name, { get: cachedGetter(name, getter), enumerable: false, configurable: true });
3396
+ break;
3397
+ }
3398
+ }
3399
+ }
3400
+ async applyUpdate(instance, path, value) {
3401
+ if (!isUpdatable(instance, path)) throw new PropertyNotUpdatableError(path, stateOf(instance).name);
3402
+ const [root, ...rest] = path.split(".");
3403
+ const key = rest.length > 0 ? rest.join(".") : void 0;
3404
+ const studly = Str.studly(root);
3405
+ await hook(instance, "updating", path, value);
3406
+ await hook(instance, `updating${studly}`, value, key);
3407
+ const target2 = instance;
3408
+ if (rest.length === 0) target2[root] = value;
3409
+ else {
3410
+ const assertSlot = (node2, segment) => {
3411
+ if (Array.isArray(node2) && !isArraySlot(node2, segment)) throw new PropertyNotUpdatableError(path, stateOf(instance).name);
3412
+ };
3413
+ let node = target2[root];
3414
+ if (!isRecord(node) && !Array.isArray(node)) target2[root] = node = {};
3415
+ for (const segment of rest.slice(0, -1)) {
3416
+ assertSlot(node, segment);
3417
+ const next = node[segment];
3418
+ node = isRecord(next) || Array.isArray(next) ? next : node[segment] = {};
3419
+ }
3420
+ const last = rest.at(-1);
3421
+ assertSlot(node, last);
3422
+ Object.defineProperty(node, last, { value, enumerable: true, writable: true, configurable: true });
3423
+ }
3424
+ await hook(instance, "updated", path, value);
3425
+ await hook(instance, `updated${studly}`, value, key);
3426
+ }
3427
+ /** Runs one call from the browser: a magic action, an event, or a public method. Returns its return value. */
3428
+ async call(instance, method, params) {
3429
+ const name = stateOf(instance).name;
3430
+ switch (method) {
3431
+ case "$refresh":
3432
+ return void 0;
3433
+ case "$set":
3434
+ return this.applyUpdate(instance, String(params[0]), params[1]);
3435
+ case "$toggle": {
3436
+ const path = String(params[0]);
3437
+ const current = path.split(".").reduce((node, segment) => isRecord(node) || Array.isArray(node) ? node[segment] : void 0, instance);
3438
+ return this.applyUpdate(instance, path, !current);
3439
+ }
3440
+ case "__dispatch": {
3441
+ const [event, detail] = params;
3442
+ const listeners = listenersOf(instance.constructor);
3443
+ for (const listener of Object.hasOwn(listeners, String(event)) ? listeners[String(event)] : []) {
3444
+ if (!isCallable(instance, listener)) throw new MethodNotCallableError(listener, name);
3445
+ await instance[listener](isRecord(detail) ? detail : {});
3446
+ }
3447
+ return void 0;
3448
+ }
3449
+ }
3450
+ if (!isCallable(instance, method)) throw new MethodNotCallableError(method, name);
3451
+ return instance[method](...params);
3452
+ }
3453
+ /** Renders the component (rendering/rendered hooks, nested children tracked) and checks for a single root. */
3454
+ async render(instance, memo, previous) {
3455
+ await hook(instance, "rendering");
3456
+ const context = { previous, children: {}, index: 0 };
3457
+ const html = await renderContext.run(context, async () => {
3458
+ const output = await instance.render();
3459
+ return typeof output === "string" ? output : this.container().make(ViewFactory).renderToString(output);
3460
+ });
3461
+ await hook(instance, "rendered", html);
3462
+ memo.children = context.children;
3463
+ return { html: html.trim(), tag: rootTag(html, memo.name) };
3464
+ }
3465
+ /** Dispatches, redirects: the effects every response carries. */
3466
+ effects(instance) {
3467
+ const state = stateOf(instance);
3468
+ const effects = {};
3469
+ if (state.dispatches.length > 0) {
3470
+ effects.dispatches = state.dispatches.map(({ name, params, self, to }) => ({
3471
+ name,
3472
+ params,
3473
+ ...self ? { self } : {},
3474
+ ...to === void 0 ? {} : { to: typeof to === "string" ? to : this.nameOf(to) }
3475
+ }));
3476
+ }
3477
+ if (state.redirect !== void 0) effects.redirect = state.redirect;
3478
+ if (state.redirectUsingNavigate) effects.redirectUsingNavigate = true;
3479
+ return effects;
3480
+ }
3481
+ /** dehydrate() hook, then the state as snapshot data, the error bag, and the checksum over both. */
3482
+ async snapshot(instance, memo) {
3483
+ await hook(instance, "dehydrate");
3484
+ const registry = this.registry();
3485
+ const data = {};
3486
+ for (const [property, value] of Object.entries(instance)) data[property] = dehydrate(value, registry, `${memo.name}.${property}`);
3487
+ const signed = { data, memo: { ...memo, errors: { ...stateOf(instance).errors } } };
3488
+ return { ...signed, checksum: checksum(this.key(), signed) };
593
3489
  }
594
3490
  };
595
- function toResponse(output) {
596
- if (output instanceof Response) return output;
597
- if (output === void 0 || output === null) return new Response(null, { status: 204 });
598
- if (typeof output === "string") return new Response(output, { headers: { "content-type": "text/html; charset=utf-8" } });
599
- return Response.json(output);
3491
+ var COMPONENT_STUB = `import { Component } from '@orieljs/framework'
3492
+
3493
+ export default class {{ class }} extends Component {
3494
+ render() {
3495
+ return <div></div>
3496
+ }
3497
+ }
3498
+ `;
3499
+ var TEST_STUB = `import { Live } from '@orieljs/framework'
3500
+ import { expect, test } from 'vitest'
3501
+ import {{ class }} from '{{ component }}'
3502
+ import { TestCase } from '{{ testCase }}'
3503
+
3504
+ TestCase.boot()
3505
+
3506
+ test('{{ name }} renders', async () => {
3507
+ const component = await Live.test({{ class }})
3508
+ expect(component.html()).toContain('<div')
3509
+ })
3510
+ `;
3511
+ var CLASS_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
3512
+ function importPath(from, to) {
3513
+ const path = relative22(dirname2(from), to).split(sep22).join("/").replace(/\.tsx?$/, "");
3514
+ return path.startsWith(".") ? path : `./${path}`;
3515
+ }
3516
+ function registerLiveCommands(kernel, app) {
3517
+ kernel.command("make:live {name : The component name, e.g. Posts/CreatePost} {--test : Also create a test} {--force : Overwrite existing files}", async function() {
3518
+ const segments = String(this.argument("name") ?? "").split(/[/\\.]+/).filter((segment) => segment !== "").map((segment) => Str.studly(segment));
3519
+ if (segments.length === 0 || !segments.every((segment) => CLASS_NAME.test(segment))) {
3520
+ this.error(`Component name [${String(this.argument("name"))}] is not valid: use names like Posts/CreatePost.`);
3521
+ return 1;
3522
+ }
3523
+ const className = segments.at(-1);
3524
+ const config = app.bound(ConfigRepository) ? app.make(ConfigRepository) : void 0;
3525
+ const directory = config?.get("live.class_path") ?? app.appPath("Live");
3526
+ const file = join23(directory, ...segments.slice(0, -1), `${className}.tsx`);
3527
+ const force = this.option("force") === true;
3528
+ if (existsSync22(file) && !force) {
3529
+ this.error(`Component [${relative22(app.basePath(), file)}] already exists.`);
3530
+ return 1;
3531
+ }
3532
+ await mkdir2(dirname2(file), { recursive: true });
3533
+ await writeFile2(file, COMPONENT_STUB.replace("{{ class }}", () => className));
3534
+ this.info(`Component [${relative22(app.basePath(), file)}] created successfully.`);
3535
+ if (this.option("test") === true) {
3536
+ const testFile = app.basePath(join23("tests", "Feature", "Live", ...segments.slice(0, -1), `${className}.test.ts`));
3537
+ if (existsSync22(testFile) && !force) {
3538
+ this.error(`Test [${relative22(app.basePath(), testFile)}] already exists.`);
3539
+ return 1;
3540
+ }
3541
+ const name = segments.map((segment) => Str.kebab(segment)).join(".");
3542
+ const test = TEST_STUB.replaceAll("{{ class }}", () => className).replace("{{ component }}", () => importPath(testFile, file)).replace("{{ testCase }}", () => importPath(testFile, app.basePath(join23("tests", "TestCase.ts")))).replace("{{ name }}", () => name);
3543
+ await mkdir2(dirname2(testFile), { recursive: true });
3544
+ await writeFile2(testFile, test);
3545
+ this.info(`Test [${relative22(app.basePath(), testFile)}] created successfully.`);
3546
+ }
3547
+ return 0;
3548
+ }).purpose("Create a new Live component");
600
3549
  }
3550
+ var UPDATE_URI = "/oriel/live/update";
3551
+ var LiveServiceProvider = class extends ServiceProvider {
3552
+ register() {
3553
+ this.mergeConfigFrom({ class_path: this.app.appPath("Live"), layout: "components.layouts.app", persistent_middleware: [...PERSISTENT_MIDDLEWARE] }, "live");
3554
+ this.app.singleton(LiveComponentManager, () => new LiveComponentManager(this.app));
3555
+ this.app.singleton(LiveManager, (container) => container.make(LiveComponentManager));
3556
+ }
3557
+ async boot() {
3558
+ const manager = this.app.make(LiveComponentManager);
3559
+ if (this.app.bound(Router)) {
3560
+ this.app.make(Router).post(UPDATE_URI, () => manager.handle(inject(HttpRequest))).middleware("web").name("live.update");
3561
+ }
3562
+ if (this.app.bound(ConsoleKernel)) registerLiveCommands(this.app.make(ConsoleKernel), this.app);
3563
+ const directory = this.app.make(ConfigRepository).get("live.class_path", this.app.appPath("Live"));
3564
+ for (const [name, exported] of await this.app.run(() => discover(directory))) {
3565
+ if (isComponentClass(exported)) manager.register(exported, name);
3566
+ }
3567
+ }
3568
+ };
601
3569
 
602
3570
  // ../Testing/dist/index.js
603
- import { AssertionError } from "assert";
604
- import { isDeepStrictEqual } from "util";
605
- import { existsSync as existsSync4 } from "fs";
606
- import { join as join5 } from "path";
607
- import { createHmac } from "crypto";
3571
+ import { AssertionError as AssertionError2 } from "assert";
3572
+ import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
3573
+ import { existsSync as existsSync6 } from "fs";
3574
+ import { join as join6 } from "path";
3575
+ import { createHmac as createHmac2 } from "crypto";
608
3576
  import { randomUUID } from "crypto";
609
3577
  import { randomUUID as randomUUID2 } from "crypto";
610
3578
  import { randomUUID as randomUUID3 } from "crypto";
611
- import { randomBytes as randomBytes2 } from "crypto";
3579
+ import { randomBytes as randomBytes3 } from "crypto";
612
3580
  import { deflateSync } from "zlib";
613
- function fail(message, details = {}) {
614
- const error = new AssertionError({ message, actual: details.actual, expected: details.expected, operator: "fail" });
3581
+ function fail2(message, details = {}) {
3582
+ const error = new AssertionError2({ message, actual: details.actual, expected: details.expected, operator: "fail" });
615
3583
  delete error.diff;
616
- Error.captureStackTrace?.(error, fail);
3584
+ Error.captureStackTrace?.(error, fail2);
617
3585
  throw error;
618
3586
  }
619
3587
  function assert(condition, message, details) {
620
- if (!condition) fail(typeof message === "function" ? message() : message, details);
3588
+ if (!condition) fail2(typeof message === "function" ? message() : message, details);
621
3589
  }
622
3590
  function format(value) {
623
3591
  if (typeof value === "string") return JSON.stringify(value);
@@ -628,14 +3596,14 @@ function format(value) {
628
3596
  return String(value);
629
3597
  }
630
3598
  }
631
- function escapeHtml(value) {
3599
+ function escapeHtml2(value) {
632
3600
  return value.replace(/[&<>"']/g, (char) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#039;" })[char]);
633
3601
  }
634
3602
  function stripTags(html) {
635
3603
  return html.replace(/<[^>]*>/g, "");
636
3604
  }
637
3605
  function equals(a, b) {
638
- return isDeepStrictEqual(canonical(a), canonical(b));
3606
+ return isDeepStrictEqual2(canonical(a), canonical(b));
639
3607
  }
640
3608
  function canonical(value) {
641
3609
  if (Array.isArray(value)) return value.map(canonical);
@@ -780,7 +3748,7 @@ async function similar(connection, table, data) {
780
3748
  async function assertDatabaseHas(db, table, data, connection) {
781
3749
  const t = target(db, table, connection);
782
3750
  if (await constrain(t.connection.table(t.table), data).count() > 0) return;
783
- fail(`Failed asserting that a row in the table [${t.table}] matches the attributes ${JSON.stringify(data, null, 2)}.
3751
+ fail2(`Failed asserting that a row in the table [${t.table}] matches the attributes ${JSON.stringify(data, null, 2)}.
784
3752
 
785
3753
  ${await similar(t.connection, t.table, data)}`);
786
3754
  }
@@ -1101,7 +4069,7 @@ var AssertableJson = class _AssertableJson {
1101
4069
  const props = this.prop();
1102
4070
  const keys = typeof props === "object" && props !== null ? Object.keys(props) : [];
1103
4071
  if (keys.length === 0) {
1104
- fail(this.path === void 0 ? "Cannot scope directly onto the first element of the root level because it is empty." : `Cannot scope directly onto the first element of property [${this.path}] because it is empty.`);
4072
+ fail2(this.path === void 0 ? "Cannot scope directly onto the first element of the root level because it is empty." : `Cannot scope directly onto the first element of property [${this.path}] because it is empty.`);
1105
4073
  }
1106
4074
  this.interactsWith(keys[0]);
1107
4075
  return this.scope(keys[0], callback);
@@ -1110,7 +4078,7 @@ var AssertableJson = class _AssertableJson {
1110
4078
  const props = this.prop();
1111
4079
  const keys = typeof props === "object" && props !== null ? Object.keys(props) : [];
1112
4080
  if (keys.length === 0) {
1113
- fail(this.path === void 0 ? "Cannot scope directly onto each element of the root level because it is empty." : `Cannot scope directly onto each element of property [${this.path}] because it is empty.`);
4081
+ fail2(this.path === void 0 ? "Cannot scope directly onto each element of the root level because it is empty." : `Cannot scope directly onto each element of property [${this.path}] because it is empty.`);
1114
4082
  }
1115
4083
  for (const key of keys) {
1116
4084
  this.interactsWith(key);
@@ -1141,29 +4109,29 @@ function sizeOf(value) {
1141
4109
  function toPlain(value) {
1142
4110
  return value === void 0 ? void 0 : JSON.parse(JSON.stringify(value));
1143
4111
  }
1144
- function inertiaPageOf(response) {
4112
+ function inertiaPageOf(response2) {
1145
4113
  let page;
1146
4114
  try {
1147
- if (response.headers.has("X-Inertia")) page = JSON.parse(response.content);
4115
+ if (response2.headers.has("X-Inertia")) page = JSON.parse(response2.content);
1148
4116
  else {
1149
- const match = /<script data-page="[^"]*" type="application\/json">([\s\S]*?)<\/script>/.exec(response.content);
4117
+ const match = /<script data-page="[^"]*" type="application\/json">([\s\S]*?)<\/script>/.exec(response2.content);
1150
4118
  page = match === null ? void 0 : JSON.parse(match[1]);
1151
4119
  }
1152
4120
  } catch {
1153
4121
  page = void 0;
1154
4122
  }
1155
4123
  const valid = typeof page === "object" && page !== null && ["component", "props", "url", "version"].every((key) => Object.hasOwn(page, key));
1156
- if (!valid) fail("Not a valid Inertia response.");
4124
+ if (!valid) fail2("Not a valid Inertia response.");
1157
4125
  return page;
1158
4126
  }
1159
4127
  var AssertableInertia = class _AssertableInertia extends AssertableJson {
1160
4128
  #page;
1161
4129
  #response;
1162
- static fromTestResponse(response) {
1163
- const page = inertiaPageOf(response);
4130
+ static fromTestResponse(response2) {
4131
+ const page = inertiaPageOf(response2);
1164
4132
  const instance = new _AssertableInertia(page.props);
1165
4133
  instance.#page = page;
1166
- instance.#response = response;
4134
+ instance.#response = response2;
1167
4135
  return instance;
1168
4136
  }
1169
4137
  /** Scopes are plain AssertableJson: the page-level methods only make sense at the root. */
@@ -1171,7 +4139,7 @@ var AssertableInertia = class _AssertableInertia extends AssertableJson {
1171
4139
  return new AssertableJson(props, path);
1172
4140
  }
1173
4141
  get page() {
1174
- if (this.#page === void 0) fail("This assertion is only available on the page root.");
4142
+ if (this.#page === void 0) fail2("This assertion is only available on the page root.");
1175
4143
  return this.#page;
1176
4144
  }
1177
4145
  /** The component name; the page file must exist unless `shouldExist` is false (inertia.testing.ensure_pages_exist). */
@@ -1183,8 +4151,8 @@ var AssertableInertia = class _AssertableInertia extends AssertableJson {
1183
4151
  if (shouldExist === true || shouldExist === void 0 && config?.get("inertia.testing.ensure_pages_exist", true) !== false) {
1184
4152
  const paths = config?.get("inertia.pages.paths", []) ?? [];
1185
4153
  const extensions = config?.get("inertia.pages.extensions", []) ?? [];
1186
- const exists = paths.some((path) => extensions.some((extension) => existsSync4(join5(path, `${value}.${extension}`))));
1187
- if (!exists) fail(`Inertia page component file [${value}] does not exist.`);
4154
+ const exists = paths.some((path) => extensions.some((extension) => existsSync6(join6(path, `${value}.${extension}`))));
4155
+ if (!exists) fail2(`Inertia page component file [${value}] does not exist.`);
1188
4156
  }
1189
4157
  return this;
1190
4158
  }
@@ -1221,7 +4189,7 @@ var AssertableInertia = class _AssertableInertia extends AssertableJson {
1221
4189
  */
1222
4190
  async reload(callback, only, except) {
1223
4191
  const client = this.#response?.context.client;
1224
- if (client === void 0) fail("reload() needs a response from the test HTTP client.");
4192
+ if (client === void 0) fail2("reload() needs a response from the test HTTP client.");
1225
4193
  const headers = {
1226
4194
  "X-Inertia": "true",
1227
4195
  "X-Requested-With": "XMLHttpRequest",
@@ -1232,8 +4200,8 @@ var AssertableInertia = class _AssertableInertia extends AssertableJson {
1232
4200
  if (onlyList !== void 0 || exceptList !== void 0) headers["X-Inertia-Partial-Component"] = this.page.component;
1233
4201
  if (onlyList !== void 0) headers["X-Inertia-Partial-Data"] = onlyList;
1234
4202
  if (exceptList !== void 0) headers["X-Inertia-Partial-Except"] = exceptList;
1235
- const response = await client.request("GET", this.page.url, { headers });
1236
- const reloaded = _AssertableInertia.fromTestResponse(response);
4203
+ const response2 = await client.request("GET", this.page.url, { headers });
4204
+ const reloaded = _AssertableInertia.fromTestResponse(response2);
1237
4205
  reloaded.component(this.page.component, false).url(this.page.url).version(this.page.version);
1238
4206
  await callback?.(reloaded);
1239
4207
  return this;
@@ -1280,7 +4248,7 @@ var AssertableInertia = class _AssertableInertia extends AssertableJson {
1280
4248
  }
1281
4249
  };
1282
4250
  function cookiePrefix(name, key) {
1283
- return `${createHmac("sha1", key).update(`${name}v2`).digest("hex")}|`;
4251
+ return `${createHmac2("sha1", key).update(`${name}v2`).digest("hex")}|`;
1284
4252
  }
1285
4253
  function encryptCookie(encrypter, name, value) {
1286
4254
  return encrypter.encrypt(cookiePrefix(name, encrypter.getKey()) + value, false);
@@ -1328,8 +4296,8 @@ function isExpired(cookie, now = Date.now()) {
1328
4296
  if (cookie.maxAge !== void 0 && !Number.isNaN(cookie.maxAge)) return cookie.maxAge <= 0;
1329
4297
  return cookie.expires !== void 0 && cookie.expires.getTime() <= now;
1330
4298
  }
1331
- function responseCookies(response) {
1332
- return response.headers.getSetCookie().map(parseSetCookie);
4299
+ function responseCookies(response2) {
4300
+ return response2.headers.getSetCookie().map(parseSetCookie);
1333
4301
  }
1334
4302
  function cookieHeader(cookies) {
1335
4303
  return [...cookies].map(([name, value]) => `${encodeURIComponent(name)}=${encodeURIComponent(value)}`).join("; ");
@@ -1346,8 +4314,8 @@ var TestResponse = class _TestResponse {
1346
4314
  context;
1347
4315
  decoded;
1348
4316
  /** Reads the body once, so every assertion is synchronous. */
1349
- static async from(response, context = {}) {
1350
- return new _TestResponse(response, await response.text(), context);
4317
+ static async from(response2, context = {}) {
4318
+ return new _TestResponse(response2, await response2.text(), context);
1351
4319
  }
1352
4320
  get status() {
1353
4321
  return this.baseResponse.status;
@@ -1368,7 +4336,7 @@ var TestResponse = class _TestResponse {
1368
4336
  try {
1369
4337
  this.decoded = { value: JSON.parse(this.content) };
1370
4338
  } catch {
1371
- fail(`Invalid JSON was returned from the route.
4339
+ fail2(`Invalid JSON was returned from the route.
1372
4340
 
1373
4341
  ${this.excerpt()}`);
1374
4342
  }
@@ -1381,7 +4349,7 @@ ${this.excerpt()}`);
1381
4349
  }
1382
4350
  // Status ------------------------------------------------------------------------------------------------
1383
4351
  assertStatus(status) {
1384
- if (this.status !== status) fail(this.statusMessage(`Expected response status code [${status}] but received ${this.status}.`), { actual: this.status, expected: status });
4352
+ if (this.status !== status) fail2(this.statusMessage(`Expected response status code [${status}] but received ${this.status}.`), { actual: this.status, expected: status });
1385
4353
  return this;
1386
4354
  }
1387
4355
  assertOk() {
@@ -1839,11 +4807,11 @@ ${JSON.stringify(errors, null, 2)}`, { actual: errors, expected: {} });
1839
4807
  return Arr.get(this.viewDataOf(this.ensureView()), key);
1840
4808
  }
1841
4809
  // Internals ---------------------------------------------------------------------------------------------
1842
- absolute(url) {
4810
+ absolute(url2) {
1843
4811
  try {
1844
- return new URL(url, this.context.request?.url ?? "http://localhost").href;
4812
+ return new URL(url2, this.context.request?.url ?? "http://localhost").href;
1845
4813
  } catch {
1846
- return url;
4814
+ return url2;
1847
4815
  }
1848
4816
  }
1849
4817
  excerpt(text = this.content) {
@@ -1898,13 +4866,13 @@ ${this.excerpt()}`;
1898
4866
  return decryptCookie(app.make(Encrypter), cookie.name, cookie.value);
1899
4867
  }
1900
4868
  needles(value, escape) {
1901
- return [value].flat().map((needle) => escape ? escapeHtml(needle) : needle);
4869
+ return [value].flat().map((needle) => escape ? escapeHtml2(needle) : needle);
1902
4870
  }
1903
4871
  inOrder(haystack, needles) {
1904
4872
  let position = 0;
1905
4873
  for (const needle of needles) {
1906
4874
  const found = haystack.indexOf(needle, position);
1907
- if (found === -1) fail(`Failed asserting that the response contains ${format(needles)} in that order (missing or out of order: [${needle}]).`);
4875
+ if (found === -1) fail2(`Failed asserting that the response contains ${format(needles)} in that order (missing or out of order: [${needle}]).`);
1908
4876
  position = found + needle.length;
1909
4877
  }
1910
4878
  return this;
@@ -1943,9 +4911,9 @@ function recordingViewFactory(factory, views) {
1943
4911
  }
1944
4912
  function recordingExceptionHandler(handler, record) {
1945
4913
  return forward(handler, {
1946
- async render(request, error) {
4914
+ async render(request2, error) {
1947
4915
  record(error);
1948
- return handler.render(request, error);
4916
+ return handler.render(request2, error);
1949
4917
  }
1950
4918
  });
1951
4919
  }
@@ -1983,13 +4951,13 @@ var HttpClient = class {
1983
4951
  /** Requests whose actingAs users reached the guards (the AuthFactory they went through). */
1984
4952
  actingAsApplied = /* @__PURE__ */ new WeakMap();
1985
4953
  async request(method, uri, options = {}) {
1986
- let response = await this.send(this.buildRequest(method, uri, options));
1987
- if (!this.followRedirects) return response;
1988
- for (let hops = 0; isRedirect(response.status); hops++) {
1989
- if (hops >= MAX_REDIRECTS) fail(`Too many redirects (more than ${MAX_REDIRECTS}), last location [${response.headers.get("Location")}].`);
1990
- response = await this.send(this.buildRequest("GET", response.headers.get("Location") ?? "/", {}));
4954
+ let response2 = await this.send(this.buildRequest(method, uri, options));
4955
+ if (!this.followRedirects) return response2;
4956
+ for (let hops = 0; isRedirect(response2.status); hops++) {
4957
+ if (hops >= MAX_REDIRECTS) fail2(`Too many redirects (more than ${MAX_REDIRECTS}), last location [${response2.headers.get("Location")}].`);
4958
+ response2 = await this.send(this.buildRequest("GET", response2.headers.get("Location") ?? "/", {}));
1991
4959
  }
1992
- return response;
4960
+ return response2;
1993
4961
  }
1994
4962
  /** Runs `fn` in a scope prepared like a request's (carried session, actingAs users), e.g. for assertAuthenticated(). */
1995
4963
  async inScope(fn) {
@@ -2000,23 +4968,48 @@ var HttpClient = class {
2000
4968
  return fn(scope);
2001
4969
  });
2002
4970
  }
4971
+ /**
4972
+ * Runs `run` in a request scope like this client's requests have (a request for `method uri` bound, the carried
4973
+ * session, actingAs users), then carries the session and the users on, as after a request. Live.test() runs its
4974
+ * mount and updates through it.
4975
+ */
4976
+ async withinRequest(run, method = "GET", uri = "/") {
4977
+ await this.app.boot();
4978
+ const request2 = this.buildRequest(method, uri, {});
4979
+ setClientAddress2(request2, "127.0.0.1");
4980
+ const scope = this.app.createScope();
4981
+ const context = { request: request2, app: this.app, client: this };
4982
+ const result = await scope.run(async () => {
4983
+ this.beforeRequest(scope, context);
4984
+ scope.instance(HttpRequest, new IncomingRequest(request2));
4985
+ if (scope.bound(AuthFactory)) {
4986
+ try {
4987
+ await scope.make(AuthFactory).guard().check();
4988
+ } catch {
4989
+ }
4990
+ }
4991
+ return run();
4992
+ });
4993
+ await scope.run(() => this.afterRequest(scope, context));
4994
+ return result;
4995
+ }
2003
4996
  /** Runs the request through the app's real HttpKernel, in a request scope this client owns. */
2004
- async send(request) {
4997
+ async send(request2) {
2005
4998
  const app = this.app;
2006
4999
  await app.boot();
2007
- setClientAddress(request, "127.0.0.1");
5000
+ setClientAddress2(request2, "127.0.0.1");
2008
5001
  const scope = app.createScope();
2009
- const context = { request, app, client: this };
2010
- const response = await scope.run(async () => {
5002
+ const context = { request: request2, app, client: this };
5003
+ const response2 = await scope.run(async () => {
2011
5004
  await this.beforeRequest(scope, context);
2012
5005
  this.previousUrl = void 0;
2013
- return scope.make(HttpKernel).handle(request);
5006
+ return scope.make(HttpKernel).handle(request2);
2014
5007
  });
2015
5008
  await scope.run(() => this.afterRequest(scope, context));
2016
- this.rememberCookies(response);
2017
- await app.make(HttpKernel).terminate?.(request, response);
5009
+ this.rememberCookies(response2);
5010
+ await app.make(HttpKernel).terminate?.(request2, response2);
2018
5011
  if (!this.handleExceptions && context.exception !== void 0) throw context.exception;
2019
- return TestResponse.from(response, context);
5012
+ return TestResponse.from(response2, context);
2020
5013
  }
2021
5014
  /** Prepares the request scope: recorders, the carried session and actingAs users. */
2022
5015
  beforeRequest(scope, context) {
@@ -2071,12 +5064,12 @@ var HttpClient = class {
2071
5064
  }
2072
5065
  buildRequest(method, uri, options) {
2073
5066
  const verb = method.toUpperCase();
2074
- const url = new URL(uri, this.baseUrl());
5067
+ const url2 = new URL(uri, this.baseUrl());
2075
5068
  const headers = new Headers({ ...this.headers, ...options.headers });
2076
5069
  let body;
2077
5070
  const data = options.data ?? {};
2078
5071
  if (verb === "GET" || verb === "HEAD") {
2079
- for (const [key, value] of flatten(data)) if (typeof value === "string") url.searchParams.append(key, value);
5072
+ for (const [key, value] of flatten(data)) if (typeof value === "string") url2.searchParams.append(key, value);
2080
5073
  } else if (options.json) {
2081
5074
  body = JSON.stringify(data);
2082
5075
  } else if (Object.keys(data).length > 0) {
@@ -2095,7 +5088,7 @@ var HttpClient = class {
2095
5088
  }
2096
5089
  const cookies = this.cookieHeader();
2097
5090
  if (cookies !== "") headers.set("Cookie", cookies);
2098
- return new Request(url, { method: verb, headers, body });
5091
+ return new Request(url2, { method: verb, headers, body });
2099
5092
  }
2100
5093
  baseUrl() {
2101
5094
  const configured = this.app.bound(ConfigRepository) ? this.app.make(ConfigRepository).get("app.url") : void 0;
@@ -2110,8 +5103,8 @@ var HttpClient = class {
2110
5103
  return cookieHeader(values);
2111
5104
  }
2112
5105
  /** Like a browser: cookies the app sets are sent back on later requests in the same test; deleted ones are dropped. */
2113
- rememberCookies(response) {
2114
- for (const cookie of responseCookies(response)) {
5106
+ rememberCookies(response2) {
5107
+ for (const cookie of responseCookies(response2)) {
2115
5108
  if (isExpired(cookie)) this.cookies.delete(cookie.name);
2116
5109
  else this.cookies.set(cookie.name, { value: cookie.value, encrypt: false });
2117
5110
  }
@@ -2120,9 +5113,9 @@ var HttpClient = class {
2120
5113
  function isRedirect(status) {
2121
5114
  return status >= 300 && status < 400 && status !== 304;
2122
5115
  }
2123
- function setClientAddress(request, address) {
5116
+ function setClientAddress2(request2, address) {
2124
5117
  const g = globalThis;
2125
- (g[CLIENT_ADDRESS] ??= /* @__PURE__ */ new WeakMap()).set(request, address);
5118
+ (g[CLIENT_ADDRESS] ??= /* @__PURE__ */ new WeakMap()).set(request2, address);
2126
5119
  }
2127
5120
  function flatten(data, prefix = "") {
2128
5121
  const out = [];
@@ -2218,11 +5211,43 @@ var InteractsWithApplication = class {
2218
5211
  if (global === app) Container.setInstance(void 0);
2219
5212
  }
2220
5213
  }
5214
+ /**
5215
+ * Laravel's withoutVite(): in this test's requests vite([...]) and viteReactRefresh() render nothing, so feature
5216
+ * tests of pages pass before `vite build` has written public/build/manifest.json.
5217
+ */
5218
+ withoutVite() {
5219
+ const app = this.app;
5220
+ app.scoped(Vite, () => new WithoutVite(app));
5221
+ return this;
5222
+ }
2221
5223
  /** Runs in tearDown(), before the application is thrown away (in registration order). */
2222
5224
  beforeApplicationDestroyed(callback) {
2223
5225
  this.destroying.push(callback);
2224
5226
  }
2225
5227
  };
5228
+ var WithoutVite = class extends Vite {
5229
+ tags() {
5230
+ return new ViteHtml("");
5231
+ }
5232
+ reactRefresh() {
5233
+ return void 0;
5234
+ }
5235
+ asset() {
5236
+ return "";
5237
+ }
5238
+ content() {
5239
+ return "";
5240
+ }
5241
+ manifestHash() {
5242
+ return void 0;
5243
+ }
5244
+ preloadedAssets() {
5245
+ return {};
5246
+ }
5247
+ toHtml() {
5248
+ return "";
5249
+ }
5250
+ };
2226
5251
  async function disconnectAll(db) {
2227
5252
  const manager = db;
2228
5253
  if (typeof manager.disconnectAll === "function") await manager.disconnectAll();
@@ -2237,6 +5262,11 @@ var MakesHttpRequests = class extends InteractsWithApplication {
2237
5262
  createHttpClient() {
2238
5263
  return new HttpClient(this.app);
2239
5264
  }
5265
+ /** Live.test() runs in this test's request scope (Live reads the same token: no import between the packages). */
5266
+ async setUp() {
5267
+ await super.setUp();
5268
+ this.app.instance(token("Testing.RequestScope"), (run) => this.http.withinRequest(run));
5269
+ }
2240
5270
  async tearDown() {
2241
5271
  try {
2242
5272
  await super.tearDown();
@@ -2252,8 +5282,8 @@ var MakesHttpRequests = class extends InteractsWithApplication {
2252
5282
  this.http.headers[name] = value;
2253
5283
  return this;
2254
5284
  }
2255
- withToken(token, type = "Bearer") {
2256
- return this.withHeader("Authorization", `${type} ${token}`);
5285
+ withToken(token2, type = "Bearer") {
5286
+ return this.withHeader("Authorization", `${type} ${token2}`);
2257
5287
  }
2258
5288
  withBasicAuth(username, password) {
2259
5289
  return this.withToken(Buffer.from(`${username}:${password}`).toString("base64"), "Basic");
@@ -2284,9 +5314,9 @@ var MakesHttpRequests = class extends InteractsWithApplication {
2284
5314
  return this;
2285
5315
  }
2286
5316
  /** Sets the Referer and the session's previous URL, so redirect()->back() goes there. */
2287
- from(url) {
2288
- this.http.previousUrl = url;
2289
- return this.withHeader("Referer", url);
5317
+ from(url2) {
5318
+ this.http.previousUrl = url2;
5319
+ return this.withHeader("Referer", url2);
2290
5320
  }
2291
5321
  /** Puts data in the session for the following requests (Laravel's withSession()). */
2292
5322
  withSession(data) {
@@ -2436,13 +5466,13 @@ var InteractsWithConsole = class extends InteractsWithDatabase {
2436
5466
  };
2437
5467
  var TestCase = class extends InteractsWithConsole {
2438
5468
  };
2439
- function install(token, fake) {
2440
- facade(token).swap(fake);
5469
+ function install(token2, fake) {
5470
+ facade(token2).swap(fake);
2441
5471
  return fake;
2442
5472
  }
2443
- function bound(token) {
5473
+ function bound(token2) {
2444
5474
  const container = currentContainer();
2445
- return container !== void 0 && container.bound(token) ? container.make(token) : void 0;
5475
+ return container !== void 0 && container.bound(token2) ? container.make(token2) : void 0;
2446
5476
  }
2447
5477
  function nameOf(target2) {
2448
5478
  if (typeof target2 === "string") return target2;
@@ -2754,7 +5784,7 @@ var BusFake = class _BusFake {
2754
5784
  /** `[First, Second, new Third(1)]`: a dispatched First whose chain is exactly the rest (classes or equal instances). */
2755
5785
  assertChained(expectedChain) {
2756
5786
  const [first, ...rest] = expectedChain;
2757
- if (first === void 0) fail("assertChained() needs at least one job.");
5787
+ if (first === void 0) fail2("assertChained() needs at least one job.");
2758
5788
  const candidates = this.commands.filter((job) => sameJob(job, first));
2759
5789
  assert(candidates.length > 0, `The expected [${nameOf(first)}] job was not dispatched.`);
2760
5790
  const found = candidates.some((job) => {
@@ -2795,7 +5825,7 @@ var BusFake = class _BusFake {
2795
5825
  return this.jobsToFake.length === 0 || matchesAny(job, this.jobsToFake);
2796
5826
  }
2797
5827
  real() {
2798
- if (this.dispatcher === void 0) fail("Bus.fake() was given jobs to fake, but there is no real BusDispatcher for the others.");
5828
+ if (this.dispatcher === void 0) fail2("Bus.fake() was given jobs to fake, but there is no real BusDispatcher for the others.");
2799
5829
  return this.dispatcher;
2800
5830
  }
2801
5831
  };
@@ -2894,7 +5924,7 @@ var QueueFake = class _QueueFake {
2894
5924
  const jobs = this.pushed(job, callback);
2895
5925
  assert(jobs.length > 0, `The expected [${nameOf(job)}] job was not pushed.`);
2896
5926
  const found = jobs.some((pushed) => {
2897
- const chain = chainOf(pushed);
5927
+ const chain = chainOf2(pushed);
2898
5928
  return chain.length === expectedChain.length && chain.every((link, index) => sameJob(link, expectedChain[index]));
2899
5929
  });
2900
5930
  assert(found, `The expected chain was not pushed with [${nameOf(job)}].`);
@@ -2902,7 +5932,7 @@ var QueueFake = class _QueueFake {
2902
5932
  assertPushedWithoutChain(job, callback) {
2903
5933
  const jobs = this.pushed(job, callback);
2904
5934
  assert(jobs.length > 0, `The expected [${nameOf(job)}] job was not pushed.`);
2905
- assert(jobs.some((pushed) => chainOf(pushed).length === 0), `The expected [${nameOf(job)}] job was pushed with a chain.`);
5935
+ assert(jobs.some((pushed) => chainOf2(pushed).length === 0), `The expected [${nameOf(job)}] job was pushed with a chain.`);
2906
5936
  }
2907
5937
  /** Pushed jobs matching the target (raw payloads match by their registry name or display name). */
2908
5938
  pushed(job, callback) {
@@ -2948,11 +5978,11 @@ var QueueFake = class _QueueFake {
2948
5978
  return this.jobsToFake.length === 0 || matchesAny(job, this.jobsToFake);
2949
5979
  }
2950
5980
  real() {
2951
- if (this.manager === void 0) fail("Queue.fake() was given jobs to fake, but there is no real QueueManager for the others.");
5981
+ if (this.manager === void 0) fail2("Queue.fake() was given jobs to fake, but there is no real QueueManager for the others.");
2952
5982
  return this.manager;
2953
5983
  }
2954
5984
  };
2955
- function chainOf(job) {
5985
+ function chainOf2(job) {
2956
5986
  const chain = job[CHAIN];
2957
5987
  return chain?.jobs ?? [];
2958
5988
  }
@@ -3138,8 +6168,8 @@ var AnonymousNotifiable = class {
3138
6168
  }
3139
6169
  dispatcher;
3140
6170
  routes = /* @__PURE__ */ new Map();
3141
- route(channel, route) {
3142
- this.routes.set(channel, route);
6171
+ route(channel, route2) {
6172
+ this.routes.set(channel, route2);
3143
6173
  return this;
3144
6174
  }
3145
6175
  routeNotificationFor(channel) {
@@ -3183,8 +6213,8 @@ var NotificationFake = class _NotificationFake {
3183
6213
  async sendNow(notifiables, notification, channels) {
3184
6214
  this.record(notifiables, notification, channels);
3185
6215
  }
3186
- route(channel, route) {
3187
- return new AnonymousNotifiable(this).route(channel, route);
6216
+ route(channel, route2) {
6217
+ return new AnonymousNotifiable(this).route(channel, route2);
3188
6218
  }
3189
6219
  /** The Notifications provider calls this in boot(); a fake has no channels to register. */
3190
6220
  extend(_channel, _factory) {
@@ -3288,7 +6318,7 @@ function guessMimeType(path) {
3288
6318
  }
3289
6319
  function hashName(file) {
3290
6320
  const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
3291
- const name = [...randomBytes2(40)].map((byte) => alphabet[byte % alphabet.length]).join("");
6321
+ const name = [...randomBytes3(40)].map((byte) => alphabet[byte % alphabet.length]).join("");
3292
6322
  const extension = file.extension();
3293
6323
  return extension === "" ? name : `${name}.${extension}`;
3294
6324
  }
@@ -3474,7 +6504,7 @@ var StorageFake = class _StorageFake {
3474
6504
  const disk = name ?? this.defaultDisk;
3475
6505
  const faked = this.disks.get(disk);
3476
6506
  if (faked !== void 0) return faked;
3477
- if (this.manager === void 0) fail(`Disk [${disk}] is not faked and there is no real FilesystemManager.`);
6507
+ if (this.manager === void 0) fail2(`Disk [${disk}] is not faked and there is no real FilesystemManager.`);
3478
6508
  return this.manager.disk(disk);
3479
6509
  }
3480
6510
  extend(driver, factory) {
@@ -3605,6 +6635,92 @@ export {
3605
6635
  viteReactRefresh,
3606
6636
  Application2 as Application,
3607
6637
  ClosureKernel,
6638
+ HttpException,
6639
+ HttpResponseException,
6640
+ HttpServerError,
6641
+ exceedsJsonDepth,
6642
+ requestScope,
6643
+ currentRequest,
6644
+ optionalSession,
6645
+ optionalCookieJar,
6646
+ HttpRedirectResponse,
6647
+ HttpRedirector,
6648
+ setClientAddress,
6649
+ clientAddress,
6650
+ extensionFromMime,
6651
+ FormUploadedFile,
6652
+ parseBracketKey,
6653
+ normalizeLists,
6654
+ fromEntries,
6655
+ sanitize,
6656
+ mergeRecursive,
6657
+ isBlank,
6658
+ transformLeaves,
6659
+ ipMatches,
6660
+ IncomingRequest,
6661
+ request,
6662
+ response,
6663
+ redirect,
6664
+ back,
6665
+ url,
6666
+ route,
6667
+ abort,
6668
+ abortIf,
6669
+ abortUnless,
6670
+ contentDisposition,
6671
+ HttpResponseFactory,
6672
+ ConvertEmptyStringsToNull,
6673
+ TrimStrings,
6674
+ Kernel,
6675
+ MiddlewareConfiguration,
6676
+ applyMiddlewareCallbacks,
6677
+ statusText,
6678
+ escapeHtml,
6679
+ minimalPage,
6680
+ stackFrames,
6681
+ debugPage,
6682
+ statusFor,
6683
+ summarizeValidation,
6684
+ ErrorHandler,
6685
+ ExceptionsConfiguration,
6686
+ applyExceptionCallbacks,
6687
+ servePublicFile,
6688
+ bunRuntime,
6689
+ serveBun,
6690
+ toWebRequest,
6691
+ writeResponse,
6692
+ serveNode,
6693
+ detectRuntime,
6694
+ serve,
6695
+ routeListRows,
6696
+ filterRouteList,
6697
+ formatRouteList,
6698
+ API_ROUTES_STUB,
6699
+ addApiRouting,
6700
+ registerHttpCommands,
6701
+ HttpServiceProvider,
6702
+ On,
6703
+ Locked,
6704
+ Computed,
6705
+ ErrorBag,
6706
+ PendingEvent,
6707
+ Component,
6708
+ LiveError,
6709
+ CorruptComponentPayloadError,
6710
+ MethodNotCallableError,
6711
+ PropertyNotUpdatableError,
6712
+ LiveComponentNotFoundError,
6713
+ InvalidLiveRequestError,
6714
+ TestRequestScope,
6715
+ Testable,
6716
+ live,
6717
+ Live,
6718
+ componentName,
6719
+ PERSISTENT_MIDDLEWARE,
6720
+ persistentMiddleware,
6721
+ LiveComponentManager,
6722
+ UPDATE_URI,
6723
+ LiveServiceProvider,
3608
6724
  PendingCommand,
3609
6725
  RefreshDatabaseState,
3610
6726
  runCommand,
@@ -3650,4 +6766,4 @@ export {
3650
6766
  StorageFake,
3651
6767
  FakeFile
3652
6768
  };
3653
- //# sourceMappingURL=chunk-UPZAML2Q.js.map
6769
+ //# sourceMappingURL=chunk-SBZQ6L3Z.js.map