@jk2908/solas 0.3.1 → 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 (71) hide show
  1. package/CHANGELOG.md +5 -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/browser-router/use-search-params.d.ts +1 -0
  17. package/dist/internal/browser-router/use-search-params.js +15 -0
  18. package/dist/internal/build.js +2 -2
  19. package/dist/internal/codegen/types.d.ts +5 -0
  20. package/dist/internal/codegen/types.js +48 -0
  21. package/dist/internal/env/browser.js +6 -6
  22. package/dist/internal/env/flight.d.ts +29 -0
  23. package/dist/internal/env/flight.js +187 -0
  24. package/dist/internal/env/request-context.d.ts +1 -1
  25. package/dist/internal/env/rsc.d.ts +1 -1
  26. package/dist/internal/env/rsc.js +23 -28
  27. package/dist/internal/env/ssr.d.ts +2 -2
  28. package/dist/internal/env/ssr.js +27 -13
  29. package/dist/internal/env/utils.js +13 -1
  30. package/dist/internal/http-router/create-http-router.d.ts +6 -0
  31. package/dist/internal/{router/create-router.js → http-router/create-http-router.js} +5 -5
  32. package/dist/internal/{router → http-router}/router.d.ts +9 -9
  33. package/dist/internal/{router → http-router}/router.js +20 -19
  34. package/dist/internal/{router → http-router}/utils.d.ts +11 -3
  35. package/dist/internal/{router → http-router}/utils.js +9 -1
  36. package/dist/internal/metadata.js +10 -10
  37. package/dist/internal/prerender.d.ts +4 -9
  38. package/dist/internal/prerender.js +6 -23
  39. package/dist/internal/render/head.js +1 -1
  40. package/dist/internal/render/tree.d.ts +1 -1
  41. package/dist/internal/render/tree.js +17 -13
  42. package/dist/internal/{router/resolver.d.ts → resolver.d.ts} +41 -41
  43. package/dist/internal/{router/resolver.js → resolver.js} +7 -7
  44. package/dist/internal/server/actions.js +1 -1
  45. package/dist/internal/server/cookies.d.ts +3 -2
  46. package/dist/internal/server/cookies.js +4 -3
  47. package/dist/internal/server/dynamic.d.ts +1 -3
  48. package/dist/internal/server/dynamic.js +3 -11
  49. package/dist/internal/server/headers.d.ts +2 -2
  50. package/dist/internal/server/headers.js +3 -3
  51. package/dist/internal/server/url.d.ts +2 -2
  52. package/dist/internal/server/url.js +3 -3
  53. package/dist/navigation.d.ts +0 -2
  54. package/dist/navigation.js +0 -2
  55. package/dist/router.d.ts +3 -4
  56. package/dist/router.js +3 -4
  57. package/dist/solas.d.ts +3 -1
  58. package/dist/solas.js +1 -1
  59. package/dist/types.d.ts +15 -7
  60. package/dist/utils/logger.js +1 -1
  61. package/package.json +2 -7
  62. package/dist/internal/navigation/link.d.ts +0 -13
  63. package/dist/internal/navigation/use-search-params.d.ts +0 -11
  64. package/dist/internal/navigation/use-search-params.js +0 -34
  65. package/dist/internal/router/create-router.d.ts +0 -6
  66. package/dist/internal/router/router-context.d.ts +0 -15
  67. package/dist/internal/router/router-context.js +0 -8
  68. package/dist/internal/router/router-provider.d.ts +0 -10
  69. package/dist/internal/router/use-router.js +0 -5
  70. /package/dist/internal/{router/prefetcher.d.ts → prefetcher.d.ts} +0 -0
  71. /package/dist/internal/{router/prefetcher.js → prefetcher.js} +0 -0
@@ -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)
@@ -3,7 +3,5 @@
3
3
  * @description in prerender mode this suspends forever so the nearest Suspense
4
4
  * boundary renders its fallback into the static shell. In request mode this
5
5
  * resolves immediately
6
- * @returns void during normal requests or prerender not in ppr mode
7
- * @throws if called in prerender mode (the desired effect)
8
6
  */
9
- export declare function dynamic(): void;
7
+ export declare function dynamic(): Promise<void>;
@@ -1,22 +1,14 @@
1
- import { Logger } from '../../utils/logger.js';
2
1
  import { RequestContext } from '../env/request-context.js';
3
- const logger = new Logger();
4
2
  const NEVER = new Promise(() => { });
5
3
  /**
6
4
  * Declaratively mark render below this call as request-time only
7
5
  * @description in prerender mode this suspends forever so the nearest Suspense
8
6
  * boundary renders its fallback into the static shell. In request mode this
9
7
  * resolves immediately
10
- * @returns void during normal requests or prerender not in ppr mode
11
- * @throws if called in prerender mode (the desired effect)
12
8
  */
13
- export function dynamic() {
9
+ export async function dynamic() {
14
10
  const { prerender } = RequestContext.use();
15
- if (!prerender)
11
+ if (prerender !== 'ppr')
16
12
  return;
17
- if (prerender !== 'ppr') {
18
- logger.warn('[dynamic]', "dynamic() was called but prerender mode is not 'ppr'. This means the component will be rendered at build time, which may not be what you intended");
19
- return;
20
- }
21
- throw NEVER;
13
+ await NEVER;
22
14
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * Get the request headers as a read-only map
3
- * @returns a read-only map of request headers
3
+ * @returns a Promise resolving to a read-only map of request headers
4
4
  */
5
- export declare function headers(): ReadonlyMap<string, string>;
5
+ export declare function headers(): Promise<ReadonlyMap<string, string>>;
@@ -2,10 +2,10 @@ import { RequestContext } from '../env/request-context.js';
2
2
  import { dynamic } from './dynamic.js';
3
3
  /**
4
4
  * Get the request headers as a read-only map
5
- * @returns a read-only map of request headers
5
+ * @returns a Promise resolving to a read-only map of request headers
6
6
  */
7
- export function headers() {
8
- dynamic();
7
+ export async function headers() {
8
+ await dynamic();
9
9
  const { req, cache } = RequestContext.use();
10
10
  // use request cache if possible to avoid reconstructing the map
11
11
  if (cache.headers)
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * Get the request url as a URL instance
3
- * @returns a URL instance containing the request url
3
+ * @returns a Promise resolving to a URL instance containing the request url
4
4
  */
5
- export declare function url(): URL;
5
+ export declare function url(): Promise<URL>;
@@ -4,10 +4,10 @@ import { dynamic } from './dynamic.js';
4
4
  const logger = new Logger();
5
5
  /**
6
6
  * Get the request url as a URL instance
7
- * @returns a URL instance containing the request url
7
+ * @returns a Promise resolving to a URL instance containing the request url
8
8
  */
9
- export function url() {
10
- dynamic();
9
+ export async function url() {
10
+ await dynamic();
11
11
  const { req, cache } = RequestContext.use();
12
12
  // always return a clone so consumers can mutate (e.g. searchParams.set)
13
13
  // without corrupting the cached instance shared across the request
@@ -1,6 +1,4 @@
1
1
  export { HttpExceptionBoundary } from './internal/navigation/http-exception-boundary.js';
2
2
  export { HttpException, abort, isHttpException, } from './internal/navigation/http-exception.js';
3
- export { Link } from './internal/navigation/link.js';
4
3
  export { RedirectBoundary } from './internal/navigation/redirect-boundary.js';
5
4
  export { Redirect, isRedirect, redirect } from './internal/navigation/redirect.js';
6
- export { useSearchParams } from './internal/navigation/use-search-params.js';
@@ -1,6 +1,4 @@
1
1
  export { HttpExceptionBoundary } from './internal/navigation/http-exception-boundary.js';
2
2
  export { HttpException, abort, isHttpException, } from './internal/navigation/http-exception.js';
3
- export { Link } from './internal/navigation/link.js';
4
3
  export { RedirectBoundary } from './internal/navigation/redirect-boundary.js';
5
4
  export { Redirect, isRedirect, redirect } from './internal/navigation/redirect.js';
6
- export { useSearchParams } from './internal/navigation/use-search-params.js';
package/dist/router.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- export { createRouter } from './internal/router/create-router.js';
2
- export { Router } from './internal/router/router.js';
3
- export { RouterProvider } from './internal/router/router-provider.js';
4
- export { useRouter } from './internal/router/use-router.js';
1
+ export { Link } from './internal/browser-router/link.js';
2
+ export { useRouter } from './internal/browser-router/use-router.js';
3
+ export { useSearchParams } from './internal/browser-router/use-search-params.js';
package/dist/router.js CHANGED
@@ -1,4 +1,3 @@
1
- export { createRouter } from './internal/router/create-router.js';
2
- export { Router } from './internal/router/router.js';
3
- export { RouterProvider } from './internal/router/router-provider.js';
4
- export { useRouter } from './internal/router/use-router.js';
1
+ export { Link } from './internal/browser-router/link.js';
2
+ export { useRouter } from './internal/browser-router/use-router.js';
3
+ export { useSearchParams } from './internal/browser-router/use-search-params.js';
package/dist/solas.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import type { PluginConfig } from './types.js';
2
2
  export declare namespace Solas {
3
+ interface Routes {
4
+ }
3
5
  namespace Config {
4
6
  const NAME = "Solas";
5
7
  const SLUG: string;
@@ -12,7 +14,7 @@ export declare namespace Solas {
12
14
  const ENTRY_BROWSER = "entry.browser.tsx";
13
15
  const ASSETS_DIR = "assets";
14
16
  const $: unique symbol;
15
- const REQUEST_META: string;
17
+ const REQUEST_META_KEY: string;
16
18
  const LOG_LEVELS: readonly ["debug", "info", "warn", "error", "fatal"];
17
19
  const PRERENDER_MODES: readonly ["full", "ppr", false];
18
20
  const TRAILING_SLASH_MODES: readonly ["always", "never", "ignore"];
package/dist/solas.js CHANGED
@@ -14,7 +14,7 @@ var Solas;
14
14
  Config.ENTRY_BROWSER = 'entry.browser.tsx';
15
15
  Config.ASSETS_DIR = 'assets';
16
16
  Config.$ = Symbol(Config.SLUG);
17
- Config.REQUEST_META = `__${Config.SLUG.toUpperCase()}__`;
17
+ Config.REQUEST_META_KEY = `__${Config.SLUG.toUpperCase()}__`;
18
18
  Config.LOG_LEVELS = ['debug', 'info', 'warn', 'error', 'fatal'];
19
19
  Config.PRERENDER_MODES = ['full', 'ppr', false];
20
20
  Config.TRAILING_SLASH_MODES = ['always', 'never', 'ignore'];
package/dist/types.d.ts CHANGED
@@ -1,19 +1,20 @@
1
1
  type BunRequest = Request & {
2
2
  params?: Record<string, string | string[]>;
3
3
  };
4
- import { Solas } from './solas.js';
5
4
  import { ExportReader } from './utils/export-reader.js';
6
5
  import type { Build } from './internal/build.js';
6
+ import type { HttpRouter } from './internal/http-router/router.js';
7
7
  import type { Metadata } from './internal/metadata.js';
8
8
  import type { HttpException } from './internal/navigation/http-exception.js';
9
- import type { Router } from './internal/router/router.js';
9
+ import { BrowserRouter } from './internal/browser-router/router.js';
10
+ import { Solas } from './solas.js';
10
11
  export type LogLevel = (typeof Solas.Config.LOG_LEVELS)[number];
11
12
  type PluginConfigBase = {
12
13
  port?: number;
13
14
  precompress?: boolean;
14
15
  prerender?: Route.Prerender;
15
16
  metadata?: Metadata.Item;
16
- trailingSlash?: Route.TrailingSlash;
17
+ trailingSlash?: (typeof Solas.Config.TRAILING_SLASH_MODES)[number];
17
18
  readonly logger?: {
18
19
  level?: LogLevel;
19
20
  };
@@ -36,6 +37,13 @@ export type BuildContext = {
36
37
  knownRoutes: Set<string>;
37
38
  exportReader: ExportReader;
38
39
  };
40
+ export type RequestMeta = {
41
+ error?: HttpException | Error;
42
+ action?: boolean;
43
+ match: HttpRouter.Match | null;
44
+ parsedFormData?: FormData | null;
45
+ url?: URL;
46
+ };
39
47
  export type SolasRequest = Request & {};
40
48
  export type Segment = {
41
49
  __id: string;
@@ -68,6 +76,7 @@ export type Endpoint = {
68
76
  middlewares: (string | null)[];
69
77
  };
70
78
  export type ManifestEntry = Segment | Endpoint;
79
+ export type ManifestEntryGroup = ManifestEntry | [ManifestEntry, ...ManifestEntry[]];
71
80
  export type Manifest = Awaited<ReturnType<typeof Build.Finder.prototype.process>>['manifest'];
72
81
  export type View<TProps> = React.ComponentType<TProps> | React.LazyExoticComponent<React.ComponentType<TProps>>;
73
82
  export type StaticImport = Record<string, unknown>;
@@ -81,7 +90,7 @@ export type MapEntry = {
81
90
  '404s'?: readonly (DynamicImport | null)[];
82
91
  '500s'?: readonly (DynamicImport | null)[];
83
92
  loaders?: readonly (DynamicImport | null)[];
84
- middlewares?: readonly (Router.Middleware | null)[];
93
+ middlewares?: readonly (HttpRouter.Middleware | null)[];
85
94
  endpoint?: (req?: BunRequest) => unknown;
86
95
  };
87
96
  export type ImportMap = Record<string, MapEntry>;
@@ -92,13 +101,12 @@ export type BuildManifest = {
92
101
  prerenderRoutes: string[];
93
102
  sitemapRoutes: string[];
94
103
  precompress: boolean;
95
- trailingSlash: Route.TrailingSlash;
104
+ trailingSlash: (typeof Solas.Config.TRAILING_SLASH_MODES)[number];
96
105
  url?: PluginConfig['url'];
97
106
  };
98
107
  export declare namespace Route {
99
- type Metadata = Metadata.Item | ((input: Metadata.Input<Router.Params>) => Promise<Metadata.Item> | Metadata.Item);
108
+ type Metadata = Metadata.Item | ((input: Metadata.Input<BrowserRouter.Params>) => Promise<Metadata.Item> | Metadata.Item);
100
109
  type Prerender = (typeof Solas.Config.PRERENDER_MODES)[number];
101
- type TrailingSlash = (typeof Solas.Config.TRAILING_SLASH_MODES)[number];
102
110
  }
103
111
  export type BoundaryError = Error & {
104
112
  digest?: string;
@@ -1,5 +1,5 @@
1
- import { Solas } from '../solas.js';
2
1
  import { HttpException } from '../internal/navigation/http-exception.js';
2
+ import { Solas } from '../solas.js';
3
3
  const LEVELS = {
4
4
  debug: 0,
5
5
  info: 1,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jk2908/solas",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "A React Server Components meta-framework powered by Vite",
5
5
  "keywords": [
6
6
  "framework",
@@ -71,10 +71,6 @@
71
71
  "./error-boundary": {
72
72
  "types": "./dist/error-boundary.d.ts",
73
73
  "import": "./dist/error-boundary.js"
74
- },
75
- "./solas": {
76
- "types": "./dist/solas.d.ts",
77
- "import": "./dist/solas.js"
78
74
  }
79
75
  },
80
76
  "scripts": {
@@ -89,8 +85,7 @@
89
85
  },
90
86
  "dependencies": {
91
87
  "@vitejs/plugin-rsc": "^0.5.20",
92
- "path-to-regexp": "^8.3.0",
93
- "rsc-html-stream": "^0.0.7"
88
+ "path-to-regexp": "^8.3.0"
94
89
  },
95
90
  "devDependencies": {
96
91
  "@prettier/plugin-oxc": "^0.1.3",