@jk2908/solas 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +333 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +219 -0
  5. package/dist/error-boundary.d.ts +1 -0
  6. package/dist/error-boundary.js +1 -0
  7. package/dist/index.d.ts +7 -0
  8. package/dist/index.js +235 -0
  9. package/dist/internal/build.d.ts +104 -0
  10. package/dist/internal/build.js +633 -0
  11. package/dist/internal/codegen/config.d.ts +5 -0
  12. package/dist/internal/codegen/config.js +19 -0
  13. package/dist/internal/codegen/environments.d.ts +12 -0
  14. package/dist/internal/codegen/environments.js +42 -0
  15. package/dist/internal/codegen/manifest.d.ts +5 -0
  16. package/dist/internal/codegen/manifest.js +15 -0
  17. package/dist/internal/codegen/maps.d.ts +5 -0
  18. package/dist/internal/codegen/maps.js +75 -0
  19. package/dist/internal/codegen/utils.d.ts +1 -0
  20. package/dist/internal/codegen/utils.js +2 -0
  21. package/dist/internal/env/browser.d.ts +4 -0
  22. package/dist/internal/env/browser.js +58 -0
  23. package/dist/internal/env/request-context.d.ts +19 -0
  24. package/dist/internal/env/request-context.js +2 -0
  25. package/dist/internal/env/rsc.d.ts +39 -0
  26. package/dist/internal/env/rsc.js +368 -0
  27. package/dist/internal/env/ssr.d.ts +42 -0
  28. package/dist/internal/env/ssr.js +149 -0
  29. package/dist/internal/env/utils.d.ts +2 -0
  30. package/dist/internal/env/utils.js +28 -0
  31. package/dist/internal/metadata.d.ts +81 -0
  32. package/dist/internal/metadata.js +185 -0
  33. package/dist/internal/navigation/http-exception-boundary.d.ts +12 -0
  34. package/dist/internal/navigation/http-exception-boundary.js +48 -0
  35. package/dist/internal/navigation/http-exception.d.ts +33 -0
  36. package/dist/internal/navigation/http-exception.js +45 -0
  37. package/dist/internal/navigation/link.d.ts +13 -0
  38. package/dist/internal/navigation/link.js +63 -0
  39. package/dist/internal/navigation/redirect-boundary.d.ts +12 -0
  40. package/dist/internal/navigation/redirect-boundary.js +39 -0
  41. package/dist/internal/navigation/redirect.d.ts +21 -0
  42. package/dist/internal/navigation/redirect.js +63 -0
  43. package/dist/internal/navigation/use-search-params.d.ts +1 -0
  44. package/dist/internal/navigation/use-search-params.js +13 -0
  45. package/dist/internal/prerender.d.ts +151 -0
  46. package/dist/internal/prerender.js +422 -0
  47. package/dist/internal/render/head.d.ts +4 -0
  48. package/dist/internal/render/head.js +38 -0
  49. package/dist/internal/render/tree.d.ts +47 -0
  50. package/dist/internal/render/tree.js +108 -0
  51. package/dist/internal/router/create-router.d.ts +6 -0
  52. package/dist/internal/router/create-router.js +95 -0
  53. package/dist/internal/router/pattern.d.ts +8 -0
  54. package/dist/internal/router/pattern.js +31 -0
  55. package/dist/internal/router/prefetcher.d.ts +47 -0
  56. package/dist/internal/router/prefetcher.js +90 -0
  57. package/dist/internal/router/resolver.d.ts +174 -0
  58. package/dist/internal/router/resolver.js +356 -0
  59. package/dist/internal/router/router-context.d.ts +11 -0
  60. package/dist/internal/router/router-context.js +7 -0
  61. package/dist/internal/router/router-provider.d.ts +6 -0
  62. package/dist/internal/router/router-provider.js +131 -0
  63. package/dist/internal/router/router.d.ts +79 -0
  64. package/dist/internal/router/router.js +417 -0
  65. package/dist/internal/router/use-router.d.ts +5 -0
  66. package/dist/internal/router/use-router.js +5 -0
  67. package/dist/internal/server/cookies.d.ts +6 -0
  68. package/dist/internal/server/cookies.js +17 -0
  69. package/dist/internal/server/dynamic.d.ts +9 -0
  70. package/dist/internal/server/dynamic.js +22 -0
  71. package/dist/internal/server/headers.d.ts +5 -0
  72. package/dist/internal/server/headers.js +19 -0
  73. package/dist/internal/server/url.d.ts +5 -0
  74. package/dist/internal/server/url.js +16 -0
  75. package/dist/internal/ui/defaults/error.d.ts +4 -0
  76. package/dist/internal/ui/defaults/error.js +6 -0
  77. package/dist/internal/ui/error-boundary.d.ts +26 -0
  78. package/dist/internal/ui/error-boundary.js +41 -0
  79. package/dist/navigation.d.ts +6 -0
  80. package/dist/navigation.js +6 -0
  81. package/dist/prerender.d.ts +1 -0
  82. package/dist/prerender.js +1 -0
  83. package/dist/router.d.ts +4 -0
  84. package/dist/router.js +4 -0
  85. package/dist/server.d.ts +4 -0
  86. package/dist/server.js +4 -0
  87. package/dist/solas.d.ts +32 -0
  88. package/dist/solas.js +125 -0
  89. package/dist/types.d.ts +93 -0
  90. package/dist/types.js +1 -0
  91. package/dist/utils/compress.d.ts +11 -0
  92. package/dist/utils/compress.js +76 -0
  93. package/dist/utils/context.d.ts +6 -0
  94. package/dist/utils/context.js +25 -0
  95. package/dist/utils/cookies.d.ts +3 -0
  96. package/dist/utils/cookies.js +35 -0
  97. package/dist/utils/export-reader.d.ts +29 -0
  98. package/dist/utils/export-reader.js +117 -0
  99. package/dist/utils/format.d.ts +6 -0
  100. package/dist/utils/format.js +72 -0
  101. package/dist/utils/logger.d.ts +52 -0
  102. package/dist/utils/logger.js +105 -0
  103. package/dist/utils/time.d.ts +4 -0
  104. package/dist/utils/time.js +29 -0
  105. package/package.json +111 -0
@@ -0,0 +1,63 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useRef } from 'react';
4
+ import { useRouter } from '../router/use-router';
5
+ /**
6
+ * A link component that navigates to a given href
7
+ * @param href - the href to navigate to
8
+ * @param prefetch - when to prefetch the linked page, defaults to 'intent'
9
+ * @param rest - other props to pass to the underlying anchor element
10
+ * @returns a link element that navigates to the given href
11
+ */
12
+ export function Link({ children, href, prefetch = 'intent', ...rest }) {
13
+ const { go, prefetch: prefetcher } = useRouter();
14
+ const timer = useRef(null);
15
+ return (_jsx("a", { ...rest, href: href, onClick: e => {
16
+ rest.onClick?.(e);
17
+ if (e.defaultPrevented)
18
+ return;
19
+ // only intercept plain left-click same-origin navigations
20
+ if (e.button !== 0)
21
+ return;
22
+ if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
23
+ return;
24
+ if (rest.target && rest.target !== '_self')
25
+ return;
26
+ if (rest.download)
27
+ return;
28
+ const to = new URL(href, window.location.origin);
29
+ if (to.origin !== window.location.origin)
30
+ return;
31
+ e.preventDefault();
32
+ go(to.pathname + to.search + to.hash);
33
+ }, onFocus: e => {
34
+ rest.onFocus?.(e);
35
+ if (e.defaultPrevented)
36
+ return;
37
+ if (prefetch !== 'intent')
38
+ return;
39
+ prefetcher(href);
40
+ }, onTouchStart: e => {
41
+ rest.onTouchStart?.(e);
42
+ if (e.defaultPrevented)
43
+ return;
44
+ if (prefetch !== 'intent')
45
+ return;
46
+ prefetcher(href);
47
+ }, onMouseEnter: e => {
48
+ rest.onMouseEnter?.(e);
49
+ if (e.defaultPrevented)
50
+ return;
51
+ if (prefetch !== 'hover')
52
+ return;
53
+ timer.current = setTimeout(() => {
54
+ prefetcher(href);
55
+ }, 100);
56
+ }, onMouseLeave: e => {
57
+ rest.onMouseLeave?.(e);
58
+ if (timer.current) {
59
+ clearTimeout(timer.current);
60
+ timer.current = null;
61
+ }
62
+ }, children: children }));
63
+ }
@@ -0,0 +1,12 @@
1
+ import type { BoundaryError } from '../../types';
2
+ export type Props = {
3
+ fallback: ((error: BoundaryError) => React.ReactNode) | React.ReactNode;
4
+ children: React.ReactNode;
5
+ };
6
+ /**
7
+ * A component that catches redirect errors in its child component tree and performs
8
+ * a client-side redirect using a meta refresh tag
9
+ */
10
+ export declare function RedirectBoundary({ children }: {
11
+ children: React.ReactNode;
12
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,39 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { Component } from 'react';
4
+ import { isRedirect, REDIRECT_DIGEST_PREFIX } from './redirect';
5
+ class Boundary extends Component {
6
+ constructor(props) {
7
+ super(props);
8
+ this.state = { error: null };
9
+ }
10
+ static getDerivedStateFromError(error) {
11
+ return { error };
12
+ }
13
+ render() {
14
+ const { error } = this.state;
15
+ if (!error)
16
+ return this.props.children;
17
+ return typeof this.props.fallback === 'function'
18
+ ? this.props.fallback(error)
19
+ : this.props.fallback;
20
+ }
21
+ }
22
+ /**
23
+ * A component that catches redirect errors in its child component tree and performs
24
+ * a client-side redirect using a meta refresh tag
25
+ */
26
+ export function RedirectBoundary({ children }) {
27
+ return (_jsx(Boundary, { fallback: err => {
28
+ if (!isRedirect(err))
29
+ throw err;
30
+ if ('digest' in err && typeof err.digest === 'string') {
31
+ const [type, ...rest] = err.digest.split(':');
32
+ if (type === REDIRECT_DIGEST_PREFIX) {
33
+ const [, url] = rest;
34
+ return _jsx("meta", { httpEquiv: "refresh", content: `0;url=${url}` });
35
+ }
36
+ }
37
+ return null;
38
+ }, children: children }));
39
+ }
@@ -0,0 +1,21 @@
1
+ export type RedirectStatusCode = 301 | 302 | 307 | 308;
2
+ /**
3
+ * Redirect exception class to signal a redirect
4
+ */
5
+ export declare class Redirect extends Error {
6
+ readonly url: string;
7
+ readonly status: RedirectStatusCode;
8
+ digest?: string;
9
+ constructor(url: string, status?: RedirectStatusCode);
10
+ }
11
+ export declare const REDIRECT_DIGEST_PREFIX = "REDIRECT";
12
+ /**
13
+ * Check if an error is a Redirect error
14
+ */
15
+ export declare function isRedirect(err: unknown): err is Redirect;
16
+ /**
17
+ * Throws a Redirect exc`eption to signal a redirect
18
+ * @param url - the application-relative URL or absolute http/https URL to redirect to
19
+ * @param status - the HTTP status code for the redirect, defaults to 307
20
+ */
21
+ export declare function redirect(url: string, status?: RedirectStatusCode): never;
@@ -0,0 +1,63 @@
1
+ import { Solas } from '../../solas';
2
+ /**
3
+ * Redirect exception class to signal a redirect
4
+ */
5
+ export class Redirect extends Error {
6
+ url;
7
+ status;
8
+ digest;
9
+ constructor(url, status = 307) {
10
+ super(`Redirecting to ${url} with status ${status}`);
11
+ this.url = url;
12
+ this.status = status;
13
+ this.name = 'Redirect';
14
+ this.digest = `${REDIRECT_DIGEST_PREFIX}:${status}:${url}`;
15
+ }
16
+ }
17
+ export const REDIRECT_DIGEST_PREFIX = 'REDIRECT';
18
+ /**
19
+ * Validate a url for use in the redirect() function
20
+ */
21
+ function validate(url) {
22
+ if (url.startsWith('//')) {
23
+ throw new TypeError(`[${Solas.Config.NAME}] redirect() does not allow protocol-relative urls`);
24
+ }
25
+ // reject urls with control characters to prevent header injection
26
+ for (const char of url) {
27
+ if (char === '\r' || char === '\n') {
28
+ throw new TypeError(`[${Solas.Config.NAME}] redirect() does not allow control characters`);
29
+ }
30
+ }
31
+ // good
32
+ if (url.startsWith('/'))
33
+ return;
34
+ let parsed;
35
+ try {
36
+ parsed = new URL(url);
37
+ }
38
+ catch {
39
+ throw new TypeError(`[${Solas.Config.NAME}] redirect() only supports relative paths or absolute http/https urls`);
40
+ }
41
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
42
+ throw new TypeError(`[${Solas.Config.NAME}] redirect() only supports http:// and https:// urls`);
43
+ }
44
+ }
45
+ /**
46
+ * Check if an error is a Redirect error
47
+ */
48
+ export function isRedirect(err) {
49
+ return (typeof err === 'object' &&
50
+ err !== null &&
51
+ 'digest' in err &&
52
+ typeof err.digest === 'string' &&
53
+ err.digest.startsWith(REDIRECT_DIGEST_PREFIX));
54
+ }
55
+ /**
56
+ * Throws a Redirect exc`eption to signal a redirect
57
+ * @param url - the application-relative URL or absolute http/https URL to redirect to
58
+ * @param status - the HTTP status code for the redirect, defaults to 307
59
+ */
60
+ export function redirect(url, status = 307) {
61
+ validate(url);
62
+ throw new Redirect(url, status);
63
+ }
@@ -0,0 +1 @@
1
+ export declare function useSearchParams(): URLSearchParams;
@@ -0,0 +1,13 @@
1
+ import { useMemo, useSyncExternalStore } from 'react';
2
+ import { Solas } from '../../solas';
3
+ export function useSearchParams() {
4
+ const search = useSyncExternalStore(fn => {
5
+ window.addEventListener('popstate', fn);
6
+ window.addEventListener(Solas.Events.names.NAVIGATION, fn);
7
+ return () => {
8
+ window.removeEventListener('popstate', fn);
9
+ window.removeEventListener(Solas.Events.names.NAVIGATION, fn);
10
+ };
11
+ }, () => window.location.search, () => '');
12
+ return useMemo(() => new URLSearchParams(search), [search]);
13
+ }
@@ -0,0 +1,151 @@
1
+ import type { BuildContext } from '../types';
2
+ export declare namespace Prerender {
3
+ namespace Artifact {
4
+ type Mode = 'full' | 'ppr';
5
+ type File = 'html' | 'prelude' | 'postponed' | 'metadata';
6
+ type Value = {
7
+ schema: string;
8
+ route: string;
9
+ createdAt: number;
10
+ mode: Mode;
11
+ html: string;
12
+ postponed?: unknown;
13
+ };
14
+ type Metadata = Pick<Value, 'schema' | 'route' | 'createdAt' | 'mode'>;
15
+ type ManifestEntry = {
16
+ mode: Mode;
17
+ createdAt: number;
18
+ files?: File[];
19
+ };
20
+ type Manifest = {
21
+ generatedAt: number;
22
+ routes: Record<string, ManifestEntry>;
23
+ };
24
+ /**
25
+ * Get the root directory path where prerender artifacts are stored,
26
+ * based on the output directory specified in the configuration
27
+ */
28
+ function getRootPath(outDir: string): string;
29
+ /**
30
+ * Get the file system path for the prerender artifact manifest, which
31
+ * contains metadata about all prerendered routes and their artifacts
32
+ */
33
+ function getManifestPath(outDir: string): string;
34
+ /**
35
+ * Get the file system path for storing prerender artifacts for a given route
36
+ */
37
+ function getPath(outDir: string, pathname: string): string;
38
+ /**
39
+ * Load the prerender artifact manifest for faster runtime route mode checks
40
+ */
41
+ function loadManifest(outDir: string): Promise<Manifest | null>;
42
+ /**
43
+ * Load the postponed state for a given route from the file system, if it exists
44
+ */
45
+ function loadPostponedState(outDir: string, pathname: string): Promise<any>;
46
+ /**
47
+ * Load the prelude HTML for a given route from the file system, if it exists
48
+ */
49
+ function loadPrelude(outDir: string, pathname: string): Promise<string | null>;
50
+ /**
51
+ * Load the prerender artifact metadata for a given route from the file system, if it exists and is valid
52
+ */
53
+ function loadMetadata(outDir: string, pathname: string): Promise<{
54
+ schema: string;
55
+ route: string;
56
+ createdAt: number;
57
+ mode: "full" | "ppr";
58
+ } | null>;
59
+ /**
60
+ * Check if a prerender artifact is compatible with the current application version and route,
61
+ * based on its metadata
62
+ */
63
+ function isCompatible(artifactMetadata: Metadata, pathname: string, mode: Mode): boolean;
64
+ /**
65
+ * Compose the prelude HTML and the resume stream into a single HTML stream, by injecting the resume stream
66
+ * into the prelude at the appropriate location (before </body> or </html>)
67
+ */
68
+ function composePreludeAndResume(prelude: string, resumeStream: ReadableStream<Uint8Array>): ReadableStream<Uint8Array<ArrayBufferLike>>;
69
+ }
70
+ type Result = {
71
+ route: string;
72
+ artifact: Artifact.Value;
73
+ } | {
74
+ route: string;
75
+ status: number;
76
+ } | {
77
+ route: string;
78
+ error: unknown;
79
+ };
80
+ namespace Runtime {
81
+ /**
82
+ * Custom error class to indicate that prerendering has been postponed to request-time
83
+ */
84
+ class Postponed extends Error {
85
+ constructor(message?: string);
86
+ }
87
+ /**
88
+ * Type guard to check if an error is a Postponed error, including wrapped errors like
89
+ * AbortError or TimeoutError
90
+ */
91
+ function isPostponed(error: unknown): boolean;
92
+ }
93
+ namespace Build {
94
+ /**
95
+ * Get the prerender timeout value from the environment variable, or return the default
96
+ * if it's not set or invalid
97
+ */
98
+ function getTimeout(): number;
99
+ /**
100
+ * Get the prerender concurrency value from the environment variable, or return the default
101
+ * if it's not set or invalid
102
+ */
103
+ function getConcurrency(): number;
104
+ /**
105
+ * Extract the prerendering mode ('full', 'ppr', or false) from the source code of a route module, by
106
+ * looking for an exported `prerender` binding and validating its value
107
+ */
108
+ function getStaticFlag(filePath: string, buildContext: BuildContext): Promise<"full" | "ppr" | false | undefined>;
109
+ /**
110
+ * Get the list of static parameters for a dynamic route, by looking for an exported `params` function
111
+ * in the route module and calling it to get the list of parameter objects
112
+ */
113
+ function getStaticParams(filePath: string, buildContext: BuildContext): Promise<Record<string, string | number | (string | number)[]>[]>;
114
+ /**
115
+ * Generate the list of prerenderable routes for a dynamic route, by combining the static parameters obtained from
116
+ * the route module with the route pattern, and filtering out any routes that still contain dynamic segments
117
+ */
118
+ function getDynamicRouteList(route: string, paramNames: string[], staticParams: Record<string, string | number | (string | number)[]>[]): string[];
119
+ /**
120
+ * Function to prerender a single route by making a request to the route with special headers, and returning the
121
+ * result which includes either the prerender artifact or an error/status code if the prerendering failed or was
122
+ * postponed to request-time
123
+ */
124
+ function get(app: {
125
+ fetch: (req: Request) => Promise<Response>;
126
+ }, route: string, opts: {
127
+ timeout: number;
128
+ origin?: string;
129
+ }): Promise<{
130
+ route: string;
131
+ status: number;
132
+ artifact?: undefined;
133
+ } | {
134
+ status?: undefined;
135
+ route: string;
136
+ artifact: any;
137
+ }>;
138
+ /**
139
+ * Run the prerendering process for a list of routes, with a specified concurrency limit and timeout for
140
+ * each route, by calling the 'get' function for each route and yielding the results as they
141
+ * become available
142
+ */
143
+ function run(app: {
144
+ fetch: (req: Request) => Promise<Response>;
145
+ }, routes: string[], opts: {
146
+ timeout: number;
147
+ concurrency?: number;
148
+ origin?: string;
149
+ }): AsyncGenerator<Result, void, unknown>;
150
+ }
151
+ }