@jk2908/solas 0.3.0 → 0.3.2

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.
Files changed (75) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cli/build.d.ts +7 -0
  3. package/dist/cli/build.js +183 -0
  4. package/dist/cli/dev.d.ts +4 -0
  5. package/dist/cli/dev.js +13 -0
  6. package/dist/cli/preview.d.ts +1 -0
  7. package/dist/cli/preview.js +47 -0
  8. package/dist/cli.js +4 -238
  9. package/dist/index.js +2 -0
  10. package/dist/internal/browser-router/link.d.ts +17 -0
  11. package/dist/internal/{navigation → browser-router}/link.js +22 -16
  12. package/dist/internal/browser-router/router.d.ts +184 -0
  13. package/dist/internal/{router/router-provider.js → browser-router/router.js} +81 -12
  14. package/dist/internal/{router → browser-router}/use-router.d.ts +1 -1
  15. package/dist/internal/browser-router/use-router.js +5 -0
  16. package/dist/internal/{navigation → browser-router}/use-search-params.js +1 -1
  17. package/dist/internal/build.js +2 -2
  18. package/dist/internal/codegen/config.js +17 -8
  19. package/dist/internal/codegen/environments.js +7 -7
  20. package/dist/internal/codegen/manifest.js +3 -3
  21. package/dist/internal/codegen/maps.js +11 -15
  22. package/dist/internal/codegen/types.d.ts +5 -0
  23. package/dist/internal/codegen/types.js +48 -0
  24. package/dist/internal/codegen/utils.d.ts +10 -0
  25. package/dist/internal/codegen/utils.js +27 -2
  26. package/dist/internal/env/browser.js +6 -6
  27. package/dist/internal/env/flight.d.ts +29 -0
  28. package/dist/internal/env/flight.js +187 -0
  29. package/dist/internal/env/request-context.d.ts +1 -1
  30. package/dist/internal/env/rsc.d.ts +1 -1
  31. package/dist/internal/env/rsc.js +23 -28
  32. package/dist/internal/env/ssr.d.ts +2 -2
  33. package/dist/internal/env/ssr.js +27 -13
  34. package/dist/internal/env/utils.js +13 -1
  35. package/dist/internal/http-router/create-http-router.d.ts +6 -0
  36. package/dist/internal/{router/create-router.js → http-router/create-http-router.js} +5 -5
  37. package/dist/internal/{router → http-router}/router.d.ts +9 -9
  38. package/dist/internal/{router → http-router}/router.js +20 -19
  39. package/dist/internal/{router → http-router}/utils.d.ts +11 -3
  40. package/dist/internal/{router → http-router}/utils.js +9 -1
  41. package/dist/internal/metadata.js +10 -10
  42. package/dist/internal/prerender.d.ts +4 -9
  43. package/dist/internal/prerender.js +6 -23
  44. package/dist/internal/render/head.js +1 -1
  45. package/dist/internal/render/tree.d.ts +1 -1
  46. package/dist/internal/render/tree.js +17 -13
  47. package/dist/internal/{router/resolver.d.ts → resolver.d.ts} +41 -41
  48. package/dist/internal/{router/resolver.js → resolver.js} +7 -7
  49. package/dist/internal/server/actions.js +1 -1
  50. package/dist/internal/server/cookies.d.ts +3 -2
  51. package/dist/internal/server/cookies.js +4 -3
  52. package/dist/internal/server/dynamic.d.ts +1 -3
  53. package/dist/internal/server/dynamic.js +3 -11
  54. package/dist/internal/server/headers.d.ts +2 -2
  55. package/dist/internal/server/headers.js +3 -3
  56. package/dist/internal/server/url.d.ts +2 -2
  57. package/dist/internal/server/url.js +3 -3
  58. package/dist/navigation.d.ts +2 -4
  59. package/dist/navigation.js +2 -4
  60. package/dist/router.d.ts +3 -4
  61. package/dist/router.js +3 -4
  62. package/dist/solas.d.ts +3 -1
  63. package/dist/solas.js +1 -1
  64. package/dist/types.d.ts +15 -7
  65. package/dist/utils/logger.js +1 -1
  66. package/package.json +2 -7
  67. package/dist/internal/navigation/link.d.ts +0 -13
  68. package/dist/internal/router/create-router.d.ts +0 -6
  69. package/dist/internal/router/router-context.d.ts +0 -15
  70. package/dist/internal/router/router-context.js +0 -8
  71. package/dist/internal/router/router-provider.d.ts +0 -10
  72. package/dist/internal/router/use-router.js +0 -5
  73. /package/dist/internal/{navigation → browser-router}/use-search-params.d.ts +0 -0
  74. /package/dist/internal/{router/prefetcher.d.ts → prefetcher.d.ts} +0 -0
  75. /package/dist/internal/{router/prefetcher.js → prefetcher.js} +0 -0
@@ -1,13 +1,13 @@
1
1
  import path from 'node:path';
2
2
  import { match as createMatch } from 'path-to-regexp';
3
3
  import { Solas } from '../../solas.js';
4
- import { getAlternatePathname, normalisePathname, toPathPattern } from './utils.js';
5
4
  import { HttpException } from '../navigation/http-exception.js';
6
5
  import { maybeAction } from '../server/actions.js';
6
+ import { getAlternatePathname, normalisePathname, toPathPattern } from './utils.js';
7
7
  /**
8
8
  * Handle routing and matching for server requests
9
9
  */
10
- export class Router {
10
+ export class HttpRouter {
11
11
  opts;
12
12
  static #matchers = new WeakMap();
13
13
  #routes = {
@@ -54,7 +54,7 @@ export class Router {
54
54
  const routePath = !path.includes(':') && !path.includes('*')
55
55
  ? normalisePathname(path, this.opts.trailingSlash ?? 'never')
56
56
  : path;
57
- const segments = Router.#split(routePath);
57
+ const segments = HttpRouter.#split(routePath);
58
58
  const tokens = [];
59
59
  let score = 0;
60
60
  let wildcard = false;
@@ -122,7 +122,7 @@ export class Router {
122
122
  * Match a path and method, returning params and route
123
123
  */
124
124
  match(path, method) {
125
- for (const candidate of Router.#candidates(path)) {
125
+ for (const candidate of HttpRouter.#candidates(path)) {
126
126
  const direct = this.#routes.static.get(`${method}:${candidate}`);
127
127
  if (direct)
128
128
  return { route: direct, params: {} };
@@ -133,25 +133,25 @@ export class Router {
133
133
  }
134
134
  }
135
135
  // else dynamic/wildcard match
136
- const segments = Router.#split(path);
136
+ const segments = HttpRouter.#split(path);
137
137
  // try the leading-static prefix bucket first
138
138
  const prefixed = this.#routes.dynamic.byPrefix.get(segments[0] ?? '');
139
- const prefixedMatch = prefixed ? Router.#pick(prefixed, segments, method) : null;
139
+ const prefixedMatch = prefixed ? HttpRouter.#pick(prefixed, segments, method) : null;
140
140
  if (prefixedMatch)
141
141
  return prefixedMatch;
142
142
  // if the prefix bucket has no winner, fall back to all dynamic
143
143
  // routes with the same segment count
144
- const dynamicMatch = Router.#pick(this.#routes.dynamic.byLength.get(segments.length) ?? [], segments, method);
144
+ const dynamicMatch = HttpRouter.#pick(this.#routes.dynamic.byLength.get(segments.length) ?? [], segments, method);
145
145
  if (dynamicMatch)
146
146
  return dynamicMatch;
147
147
  // finally check wildcard routes, prefixed first, then fully generic ones
148
148
  const wildcardPrefixed = this.#routes.wildcard.byPrefix.get(segments[0] ?? '');
149
149
  const wildcardMatch = wildcardPrefixed
150
- ? Router.#pick(wildcardPrefixed, segments, method)
150
+ ? HttpRouter.#pick(wildcardPrefixed, segments, method)
151
151
  : null;
152
152
  if (wildcardMatch)
153
153
  return wildcardMatch;
154
- const wildcardFallbackMatch = Router.#pick(this.#routes.wildcard.fallback, segments, method);
154
+ const wildcardFallbackMatch = HttpRouter.#pick(this.#routes.wildcard.fallback, segments, method);
155
155
  if (wildcardFallbackMatch)
156
156
  return wildcardFallbackMatch;
157
157
  // no match
@@ -192,14 +192,14 @@ export class Router {
192
192
  // unmatched requests still pass through the shared error hook with the
193
193
  // same request metadata shape as matched requests
194
194
  return (this.#onError?.(error, Object.assign(req, {
195
- [Solas.Config.REQUEST_META]: { match: null, error, action },
195
+ [Solas.Config.REQUEST_META_KEY]: { match: null, error, action },
196
196
  })) ?? new Response(error.message, { status: error.status }));
197
197
  }
198
198
  const matched = match;
199
199
  // attach routing state to the request once so middleware and handlers can
200
200
  // read the same per-request metadata
201
201
  const request = Object.assign(req, {
202
- [Solas.Config.REQUEST_META]: { match: matched, action, parsedFormData },
202
+ [Solas.Config.REQUEST_META_KEY]: { match: matched, action, parsedFormData },
203
203
  });
204
204
  // global middleware stays outside route middleware by preserving
205
205
  // registration order here before composition in #run
@@ -210,7 +210,7 @@ export class Router {
210
210
  // normalise unknown throwables so the error hook always receives an Error
211
211
  const error = err instanceof Error ? err : new Error(String(err), { cause: err });
212
212
  const request = Object.assign(req, {
213
- [Solas.Config.REQUEST_META]: { match, error, action },
213
+ [Solas.Config.REQUEST_META_KEY]: { match, error, action },
214
214
  });
215
215
  if (this.#onError)
216
216
  return this.#onError(error, request);
@@ -270,7 +270,7 @@ export class Router {
270
270
  return new Response('Forbidden', { status: 403 });
271
271
  }
272
272
  // emitted assets are fingerprinted so they can be cached aggressively
273
- return Router.serve(filePath, req, config.precompress, {
273
+ return HttpRouter.serve(filePath, req, config.precompress, {
274
274
  'Cache-Control': 'public, immutable, max-age=31536000',
275
275
  });
276
276
  };
@@ -343,7 +343,7 @@ export class Router {
343
343
  * Get or create a path matcher for a route using path-to-regexp
344
344
  */
345
345
  static #getMatcher(route) {
346
- const cached = Router.#matchers.get(route);
346
+ const cached = HttpRouter.#matchers.get(route);
347
347
  if (cached)
348
348
  return cached;
349
349
  // convert route tokens back into a path pattern for path-to-regexp to compile
@@ -352,7 +352,7 @@ export class Router {
352
352
  const matcher = createMatch(path, {
353
353
  decode: false,
354
354
  });
355
- Router.#matchers.set(route, matcher);
355
+ HttpRouter.#matchers.set(route, matcher);
356
356
  return matcher;
357
357
  }
358
358
  /**
@@ -375,7 +375,8 @@ export class Router {
375
375
  for (let index = 0; index < length; index += 1) {
376
376
  // prefer static over dynamic and dynamic over wildcard at the
377
377
  // first segment position where the two routes differ
378
- const diff = Router.#getTokenRank(a.tokens[index]) - Router.#getTokenRank(b.tokens[index]);
378
+ const diff = HttpRouter.#getTokenRank(a.tokens[index]) -
379
+ HttpRouter.#getTokenRank(b.tokens[index]);
379
380
  if (diff !== 0)
380
381
  return diff;
381
382
  }
@@ -399,11 +400,11 @@ export class Router {
399
400
  }
400
401
  // skip routes that do not fit this path. Only compare specificity
401
402
  // across matched routes
402
- const params = Router.#fit(route, segments);
403
+ const params = HttpRouter.#fit(route, segments);
403
404
  if (!params)
404
405
  continue;
405
406
  // replace the winner only when this route is strictly more specific
406
- if (!best || Router.#compare(route, best) > 0) {
407
+ if (!best || HttpRouter.#compare(route, best) > 0) {
407
408
  best = route;
408
409
  bestParams = params;
409
410
  }
@@ -426,7 +427,7 @@ export class Router {
426
427
  }
427
428
  // defer the actual param extraction to the cached path-to-regexp matcher so
428
429
  // dynamic and wildcard params stay consistent with registration
429
- const matched = Router.#getMatcher(route)(segments.length ? `/${segments.join('/')}` : '/');
430
+ const matched = HttpRouter.#getMatcher(route)(segments.length ? `/${segments.join('/')}` : '/');
430
431
  if (!matched)
431
432
  return null;
432
433
  return matched.params;
@@ -1,4 +1,4 @@
1
- import type { Route } from '../../types.js';
1
+ import type { PluginConfig } from '../../types.js';
2
2
  export type PathPattern = {
3
3
  path: string;
4
4
  wildcardNames: Set<string>;
@@ -14,8 +14,16 @@ export declare function toPathPattern(route: string, paramNames?: string[]): {
14
14
  /**
15
15
  * Apply the configured trailing-slash policy to a pathname
16
16
  */
17
- export declare function normalisePathname(pathname: string, trailingSlash?: Route.TrailingSlash): string;
17
+ export declare function normalisePathname(pathname: string, trailingSlash?: PluginConfig['trailingSlash']): string;
18
18
  /**
19
- * Return the other pathname shape for a non-root route
19
+ * Return the other pathname shape for a non-root route. For use within
20
+ * trailingSlash logic to easily switch between shapes
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * getAlternatePathname('/about') // '/about/'
25
+ * getAlternatePathname('/about/') // '/about'
26
+ * getAlternatePathname('/') // '/'
27
+ * ```
20
28
  */
21
29
  export declare function getAlternatePathname(pathname: string): string;
@@ -53,7 +53,15 @@ export function normalisePathname(pathname, trailingSlash = 'never') {
53
53
  return pathname.endsWith('/') ? pathname.slice(0, -1) : pathname;
54
54
  }
55
55
  /**
56
- * Return the other pathname shape for a non-root route
56
+ * Return the other pathname shape for a non-root route. For use within
57
+ * trailingSlash logic to easily switch between shapes
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * getAlternatePathname('/about') // '/about/'
62
+ * getAlternatePathname('/about/') // '/about'
63
+ * getAlternatePathname('/') // '/'
64
+ * ```
57
65
  */
58
66
  export function getAlternatePathname(pathname) {
59
67
  if (pathname === '/')
@@ -85,15 +85,6 @@ var Metadata;
85
85
  metadata.link = [...linkMap.values()];
86
86
  return metadata;
87
87
  }
88
- /**
89
- * Clones an object using structuredClone w/ JSON fallback
90
- */
91
- static #clone(obj) {
92
- if (typeof structuredClone === 'function') {
93
- return structuredClone(obj);
94
- }
95
- return JSON.parse(JSON.stringify(obj));
96
- }
97
88
  /**
98
89
  * Gets a unique key for the meta tag
99
90
  */
@@ -123,6 +114,15 @@ var Metadata;
123
114
  }
124
115
  return this;
125
116
  }
117
+ /**
118
+ * Clones an object using structuredClone w/ JSON fallback
119
+ */
120
+ static #clone(obj) {
121
+ if (typeof structuredClone === 'function') {
122
+ return structuredClone(obj);
123
+ }
124
+ return JSON.parse(JSON.stringify(obj));
125
+ }
126
126
  /**
127
127
  * Merges metadata from all sources, sorted by priority
128
128
  */
@@ -130,7 +130,7 @@ var Metadata;
130
130
  const items = [...this.#collection].sort((a, b) => a.priority - b.priority);
131
131
  if (items.length === 0)
132
132
  return Collection.#clone(this.#base);
133
- let merged = Collection.#clone(this.#base);
133
+ let merged = this.#base;
134
134
  const res = await Promise.allSettled(items.map(item => item.task));
135
135
  const ok = res
136
136
  .filter((result) => result.status === 'fulfilled')
@@ -14,14 +14,9 @@ export declare namespace Prerender {
14
14
  type Metadata = Pick<Value, 'schema' | 'route' | 'createdAt' | 'mode'>;
15
15
  type ManifestEntry = {
16
16
  mode: Mode;
17
- createdAt: number;
18
- files?: File[];
19
- fullPrerenderFilename?: string;
20
- };
21
- type Manifest = {
22
- generatedAt: number;
23
- routes: Record<string, ManifestEntry>;
17
+ files?: readonly File[];
24
18
  };
19
+ type Manifest = Record<string, ManifestEntry>;
25
20
  /**
26
21
  * Get the root directory path where prerender artifacts are stored,
27
22
  * based on the output directory specified in the configuration
@@ -41,9 +36,9 @@ export declare namespace Prerender {
41
36
  */
42
37
  function getFilePath(outDir: string, pathname: string, fileName: string): string;
43
38
  /**
44
- * Build a deterministic file name for a full prerender html artifact
39
+ * File name used for saved full-prerender html inside each route artifact directory
45
40
  */
46
- function getFullHtmlFileName(html: string): string;
41
+ const FULL_PRERENDER_FILENAME = "prerendered.html";
47
42
  /**
48
43
  * Load the prerender artifact manifest for faster runtime route mode checks
49
44
  */
@@ -1,9 +1,9 @@
1
1
  import path from 'node:path';
2
2
  import { compile } from 'path-to-regexp';
3
- import { Solas } from '../solas.js';
4
3
  import { Logger } from '../utils/logger.js';
5
4
  import { Time } from '../utils/time.js';
6
- import { toPathPattern } from './router/utils.js';
5
+ import { Solas } from '../solas.js';
6
+ import { toPathPattern } from './http-router/utils.js';
7
7
  const logger = new Logger();
8
8
  export { Prerender };
9
9
  var Prerender;
@@ -63,12 +63,9 @@ var Prerender;
63
63
  }
64
64
  Artifact.getFilePath = getFilePath;
65
65
  /**
66
- * Build a deterministic file name for a full prerender html artifact
66
+ * File name used for saved full-prerender html inside each route artifact directory
67
67
  */
68
- function getFullHtmlFileName(html) {
69
- return `html.${Bun.hash(html).toString(16)}.html`;
70
- }
71
- Artifact.getFullHtmlFileName = getFullHtmlFileName;
68
+ Artifact.FULL_PRERENDER_FILENAME = 'prerendered.html';
72
69
  /**
73
70
  * Load the prerender artifact manifest for faster runtime route mode checks
74
71
  */
@@ -91,12 +88,7 @@ var Prerender;
91
88
  manifestCache.set(outDir, null);
92
89
  return null;
93
90
  }
94
- const generatedAt = value.generatedAt;
95
91
  const routes = value.routes;
96
- if (typeof generatedAt !== 'number') {
97
- manifestCache.set(outDir, null);
98
- return null;
99
- }
100
92
  if (!routes || typeof routes !== 'object') {
101
93
  manifestCache.set(outDir, null);
102
94
  return null;
@@ -107,16 +99,12 @@ var Prerender;
107
99
  manifestCache.set(outDir, null);
108
100
  return null;
109
101
  }
110
- const { mode, createdAt, files } = entry;
102
+ const { mode, files } = entry;
111
103
  // only allow known modes
112
104
  if (mode !== 'full' && mode !== 'ppr') {
113
105
  manifestCache.set(outDir, null);
114
106
  return null;
115
107
  }
116
- if (typeof createdAt !== 'number') {
117
- manifestCache.set(outDir, null);
118
- return null;
119
- }
120
108
  if (files !== undefined) {
121
109
  if (!Array.isArray(files)) {
122
110
  manifestCache.set(outDir, null);
@@ -133,13 +121,8 @@ var Prerender;
133
121
  }
134
122
  }
135
123
  }
136
- if (entry.fullPrerenderFilename !== undefined &&
137
- !isArtifactFileName(entry.fullPrerenderFilename)) {
138
- manifestCache.set(outDir, null);
139
- return null;
140
- }
141
124
  }
142
- const manifest = { generatedAt, routes };
125
+ const manifest = routes;
143
126
  // cache validated manifest to avoid reparsing on every request
144
127
  manifestCache.set(outDir, manifest);
145
128
  return manifest;
@@ -1,7 +1,7 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { use } from 'react';
3
- import { Solas } from '../../solas.js';
4
3
  import { Logger } from '../../utils/logger.js';
4
+ import { Solas } from '../../solas.js';
5
5
  const logger = new Logger();
6
6
  const cache = new WeakMap();
7
7
  /**
@@ -1,4 +1,4 @@
1
- import type { Resolver } from '../router/resolver.js';
1
+ import type { Resolver } from '../resolver.js';
2
2
  type Match = NonNullable<Resolver.EnhancedMatch>;
3
3
  /**
4
4
  * Render the resolved route tree for a matched page
@@ -1,8 +1,12 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Suspense } from 'react';
3
- import { HttpException, isHttpException } from '../navigation/http-exception.js';
4
3
  import { HttpExceptionBoundary } from '../navigation/http-exception-boundary.js';
4
+ import { HttpException, isHttpException } from '../navigation/http-exception.js';
5
5
  import DefaultErr from '../ui/defaults/error.js';
6
+ const UNAUTHORISED_ERROR = new HttpException(401, 'Unauthorised');
7
+ const FORBIDDEN_ERROR = new HttpException(403, 'Forbidden');
8
+ const NOT_FOUND_ERROR = new HttpException(404, 'Not found');
9
+ const SERVER_ERROR = new HttpException(500, 'Internal Server Error');
6
10
  /**
7
11
  * Render the resolved route tree for a matched page
8
12
  *
@@ -42,7 +46,7 @@ import DefaultErr from '../ui/defaults/error.js';
42
46
  * ```
43
47
  */
44
48
  export function Tree({ depth, params, error, ui, }) {
45
- const { layouts, Page, '401s': unauthorized, '403s': forbidden, '404s': notFounds, '500s': serverErrors, loaders, } = ui;
49
+ const { layouts, Page, '401s': unauthorised, '403s': forbidden, '404s': notFounds, '500s': serverErrors, loaders, } = ui;
46
50
  const Shell = layouts[0];
47
51
  if (!Shell)
48
52
  throw new Error('Shell layout is required in the route tree');
@@ -50,7 +54,7 @@ export function Tree({ depth, params, error, ui, }) {
50
54
  let inner = null;
51
55
  // map http status codes to exception components
52
56
  const httpExceptionMap = {
53
- 401: unauthorized,
57
+ 401: unauthorised,
54
58
  403: forbidden,
55
59
  404: notFounds,
56
60
  500: serverErrors,
@@ -69,7 +73,7 @@ export function Tree({ depth, params, error, ui, }) {
69
73
  for (let idx = layouts.length - 1; idx >= 1; idx--) {
70
74
  const Layout = layouts[idx];
71
75
  const Loading = loaders[idx];
72
- const Unauthorized = unauthorized[idx];
76
+ const Unauthorised = unauthorised[idx];
73
77
  const Forbidden = forbidden[idx];
74
78
  const NotFound = notFounds[idx];
75
79
  const ServerError = serverErrors[idx];
@@ -82,10 +86,10 @@ export function Tree({ depth, params, error, ui, }) {
82
86
  inner = _jsx(Suspense, { fallback: _jsx(Loading, {}), children: inner });
83
87
  }
84
88
  const errorBoundaries = {
85
- 401: Unauthorized ? (_jsx(Unauthorized, { error: new HttpException(401, 'Unauthorized') })) : null,
86
- 403: Forbidden ? _jsx(Forbidden, { error: new HttpException(403, 'Forbidden') }) : null,
87
- 404: NotFound ? _jsx(NotFound, { error: new HttpException(404, 'Not found') }) : null,
88
- 500: ServerError ? (_jsx(ServerError, { error: new HttpException(500, 'Internal Server Error') })) : null,
89
+ 401: Unauthorised ? _jsx(Unauthorised, { error: UNAUTHORISED_ERROR }) : null,
90
+ 403: Forbidden ? _jsx(Forbidden, { error: FORBIDDEN_ERROR }) : null,
91
+ 404: NotFound ? _jsx(NotFound, { error: NOT_FOUND_ERROR }) : null,
92
+ 500: ServerError ? _jsx(ServerError, { error: SERVER_ERROR }) : null,
89
93
  };
90
94
  // wrap in error boundaries (if supplied for this segment's http errors)
91
95
  if (Object.values(errorBoundaries).some(c => c !== null)) {
@@ -95,14 +99,14 @@ export function Tree({ depth, params, error, ui, }) {
95
99
  // now wrap with shell structure: shell renders immediately,
96
100
  // inner streams inside Suspense
97
101
  const ShellLoading = loaders[0];
98
- const ShellUnauthorized = unauthorized[0];
102
+ const Shellunauthorised = unauthorised[0];
99
103
  const ShellForbidden = forbidden[0];
100
104
  const ShellNotFound = notFounds[0];
101
105
  const ShellServerError = serverErrors[0];
102
106
  return (_jsx(HttpExceptionBoundary, { components: {
103
- 401: ShellUnauthorized ? _jsx(ShellUnauthorized, {}) : null,
104
- 403: ShellForbidden ? _jsx(ShellForbidden, {}) : null,
105
- 404: ShellNotFound ? _jsx(ShellNotFound, {}) : null,
106
- 500: ShellServerError ? _jsx(ShellServerError, {}) : null,
107
+ 401: Shellunauthorised ? _jsx(Shellunauthorised, { error: UNAUTHORISED_ERROR }) : null,
108
+ 403: ShellForbidden ? _jsx(ShellForbidden, { error: FORBIDDEN_ERROR }) : null,
109
+ 404: ShellNotFound ? _jsx(ShellNotFound, { error: NOT_FOUND_ERROR }) : null,
110
+ 500: ShellServerError ? _jsx(ShellServerError, { error: SERVER_ERROR }) : null,
107
111
  }, children: _jsx(Suspense, { fallback: ShellLoading ? _jsx(ShellLoading, {}) : null, children: _jsx(Shell, { params: params, children: inner }) }) }));
108
112
  }
@@ -1,7 +1,7 @@
1
- import type { ImportMap, Manifest, ManifestEntry, Primitive, View } from '../../types.js';
2
- import type { Router } from './router.js';
3
- import { Metadata } from '../metadata.js';
4
- import { HttpException } from '../navigation/http-exception.js';
1
+ import type { ImportMap, Manifest, ManifestEntry, Primitive, View } from '../types.js';
2
+ import { HttpRouter } from './http-router/router.js';
3
+ import { Metadata } from './metadata.js';
4
+ import { HttpException } from './navigation/http-exception.js';
5
5
  export declare namespace Resolver {
6
6
  type ReconciledMatch = ReturnType<Resolver['reconcile']>;
7
7
  type CachedEnhancedMatch = Omit<EnhancedMatch, 'params' | 'error'>;
@@ -9,11 +9,11 @@ export declare namespace Resolver {
9
9
  ui: {
10
10
  layouts: (View<{
11
11
  children?: React.ReactNode;
12
- params?: Router.Params;
12
+ params?: HttpRouter.Params;
13
13
  }> | null)[];
14
14
  Page: View<{
15
15
  children?: React.ReactNode;
16
- params?: Router.Params;
16
+ params?: HttpRouter.Params;
17
17
  }> | null;
18
18
  '401s': (View<{
19
19
  children?: React.ReactNode;
@@ -37,13 +37,13 @@ export declare namespace Resolver {
37
37
  };
38
38
  error?: HttpException | Error;
39
39
  endpoint?: (req?: Request & {
40
- params?: Router.Params;
40
+ params?: HttpRouter.Params;
41
41
  }) => unknown;
42
- metadata?: (input: Metadata.Input<Router.Params>) => Metadata.Task[];
42
+ metadata?: (input: Metadata.Input<HttpRouter.Params>) => Metadata.Task[];
43
43
  };
44
44
  }
45
45
  /**
46
- * Resolve router matches against the application manifest and import map
46
+ * Resolve HttpRouter matches against the application manifest and import map
47
47
  */
48
48
  export declare class Resolver {
49
49
  #private;
@@ -55,15 +55,17 @@ export declare class Resolver {
55
55
  /**
56
56
  * Narrow down a route entry to a page entry if it exists
57
57
  */
58
- static narrow(entry?: ManifestEntry | ManifestEntry[]): import("../../types.js").Segment | null;
58
+ static narrow(entry?: ManifestEntry | ManifestEntry[]): import("../types.js").Segment | null;
59
59
  /**
60
60
  * Get the status code for a matched route that may or may not have errored
61
61
  */
62
62
  static getMatchStatusCode(match: Resolver.ReconciledMatch | Resolver.EnhancedMatch | null): 200 | HttpException.StatusCode;
63
63
  /**
64
- * Reconcile a router match against a manifest entry
64
+ * Reconcile a HttpRouter match against a manifest entry
65
65
  */
66
- reconcile(path: string, match: Router.Match | null, error?: Error): {
66
+ reconcile(path: string, match: HttpRouter.Match | null, error?: Error): {
67
+ params: HttpRouter.Params;
68
+ error: Error | undefined;
67
69
  __id: string;
68
70
  __path: string;
69
71
  __params: string[];
@@ -83,9 +85,9 @@ export declare class Resolver {
83
85
  prerender: "full" | "ppr" | false;
84
86
  dynamic: boolean;
85
87
  wildcard: boolean;
86
- params: Router.Params;
87
- error: Error | undefined;
88
88
  } | {
89
+ params: {};
90
+ error: HttpException;
89
91
  __id: string;
90
92
  __path: string;
91
93
  __params: string[];
@@ -105,40 +107,19 @@ export declare class Resolver {
105
107
  prerender: "full" | "ppr" | false;
106
108
  dynamic: boolean;
107
109
  wildcard: boolean;
108
- params: {};
109
- error: HttpException;
110
110
  } | null;
111
111
  /**
112
112
  * Enhance a matched route with its associated components
113
113
  */
114
114
  enhance(match: Resolver.ReconciledMatch | null): {
115
- __id: string;
116
- __path: string;
117
- __params: string[];
118
- __kind: "$P";
119
- __depth: number;
120
- method: "get";
121
- paths: {
122
- layouts: (string | null)[];
123
- '401s': (string | null)[];
124
- '403s': (string | null)[];
125
- '404s': (string | null)[];
126
- '500s': (string | null)[];
127
- loaders: (string | null)[];
128
- middlewares: (string | null)[];
129
- page?: string | null | undefined;
130
- };
131
- prerender: "full" | "ppr" | false;
132
- dynamic: boolean;
133
- wildcard: boolean;
134
115
  ui: {
135
116
  layouts: (View<{
136
117
  children?: import("react").ReactNode;
137
- params?: Router.Params | undefined;
118
+ params?: HttpRouter.Params | undefined;
138
119
  }> | null)[];
139
120
  Page: View<{
140
121
  children?: import("react").ReactNode;
141
- params?: Router.Params | undefined;
122
+ params?: HttpRouter.Params | undefined;
142
123
  }> | null;
143
124
  '401s': (View<{
144
125
  children?: import("react").ReactNode;
@@ -161,14 +142,33 @@ export declare class Resolver {
161
142
  }> | null)[];
162
143
  };
163
144
  endpoint?: ((req?: (Request & {
164
- params?: Router.Params | undefined;
145
+ params?: HttpRouter.Params | undefined;
165
146
  }) | undefined) => unknown) | undefined;
166
- metadata?: ((input: Metadata.Input<Router.Params, Error>) => Metadata.Task[]) | undefined;
167
- params: Router.Params | {};
147
+ metadata?: ((input: Metadata.Input<HttpRouter.Params, Error>) => Metadata.Task[]) | undefined;
148
+ params: HttpRouter.Params | {};
168
149
  error: Error | HttpException | undefined;
150
+ __id: string;
151
+ __path: string;
152
+ __params: string[];
153
+ __kind: "$P";
154
+ __depth: number;
155
+ method: "get";
156
+ paths: {
157
+ layouts: (string | null)[];
158
+ '401s': (string | null)[];
159
+ '403s': (string | null)[];
160
+ '404s': (string | null)[];
161
+ '500s': (string | null)[];
162
+ loaders: (string | null)[];
163
+ middlewares: (string | null)[];
164
+ page?: string | null | undefined;
165
+ };
166
+ prerender: "full" | "ppr" | false;
167
+ dynamic: boolean;
168
+ wildcard: boolean;
169
169
  } | null;
170
170
  /**
171
171
  * Find the closest ancestor entry for a given path and property
172
172
  */
173
- closest(path: string, property: string, value?: Omit<Primitive, 'undefined'>): import("../../types.js").Segment | null;
173
+ closest(path: string, property: string, value?: Omit<Primitive, 'undefined'>): import("../types.js").Segment | null;
174
174
  }
@@ -1,12 +1,12 @@
1
1
  import { lazy } from 'react';
2
- import { Logger } from '../../utils/logger.js';
3
- import { Build } from '../build.js';
4
- import { Metadata } from '../metadata.js';
5
- import { HttpException, isHttpException } from '../navigation/http-exception.js';
2
+ import { Logger } from '../utils/logger.js';
3
+ import { Build } from './build.js';
4
+ import { Metadata } from './metadata.js';
5
+ import { HttpException, isHttpException } from './navigation/http-exception.js';
6
6
  const logger = new Logger();
7
7
  const IS_DEV = import.meta.env.DEV;
8
8
  /**
9
- * Resolve router matches against the application manifest and import map
9
+ * Resolve HttpRouter matches against the application manifest and import map
10
10
  */
11
11
  export class Resolver {
12
12
  /**
@@ -122,13 +122,13 @@ export class Resolver {
122
122
  return entry.Component;
123
123
  }
124
124
  /**
125
- * Reconcile a router match against a manifest entry
125
+ * Reconcile a HttpRouter match against a manifest entry
126
126
  */
127
127
  reconcile(path, match, error) {
128
128
  if (match) {
129
129
  const entry = Resolver.narrow(Resolver.#getEntryByPath(this.#manifest, match.route.path));
130
130
  if (entry) {
131
- // normal case, the router matched a page route so just attach request state
131
+ // normal case, the HttpRouter matched a page route so just attach request state
132
132
  return {
133
133
  ...entry,
134
134
  params: match.params,
@@ -68,7 +68,7 @@ export async function processActionRequest(req) {
68
68
  // we might have already parsed FormData in the router for multipart action
69
69
  // detection should be attached to the SolasRequest, so we can reuse that
70
70
  // to avoid parsing twice
71
- const parsedFormData = req[Solas.Config.REQUEST_META]?.parsedFormData;
71
+ const parsedFormData = req[Solas.Config.REQUEST_META_KEY]?.parsedFormData;
72
72
  const formData = parsedFormData ?? (await req.formData());
73
73
  const decodedAction = await decodeAction(formData);
74
74
  const result = await decodedAction();
@@ -1,6 +1,7 @@
1
1
  import { Cookies } from '../../utils/cookies.js';
2
2
  /**
3
3
  * Get the request cookies as a Cookies instance
4
- * @returns a read-only Cookies instance containing the request cookies
4
+ * @returns a Promise resolving to a read-only Cookies instance containing the
5
+ * request cookies
5
6
  */
6
- export declare function cookies(): Readonly<ReturnType<typeof Cookies.parse>>;
7
+ export declare function cookies(): Promise<Readonly<ReturnType<typeof Cookies.parse>>>;
@@ -3,10 +3,11 @@ import { RequestContext } from '../env/request-context.js';
3
3
  import { dynamic } from './dynamic.js';
4
4
  /**
5
5
  * Get the request cookies as a Cookies instance
6
- * @returns a read-only Cookies instance containing the request cookies
6
+ * @returns a Promise resolving to a read-only Cookies instance containing the
7
+ * request cookies
7
8
  */
8
- export function cookies() {
9
- dynamic();
9
+ export async function cookies() {
10
+ await dynamic();
10
11
  const { req, cache } = RequestContext.use();
11
12
  // use request cache if possible to avoid reparsing
12
13
  if (cache.cookies)