@rebask/core 0.2.1 → 0.4.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 (210) hide show
  1. package/dist/components/head.d.ts +2 -11
  2. package/dist/components/head.d.ts.map +1 -1
  3. package/dist/components/head.js +12 -24
  4. package/dist/components/image.d.ts +16 -3
  5. package/dist/components/image.d.ts.map +1 -1
  6. package/dist/components/image.js +50 -68
  7. package/dist/components/link.d.ts.map +1 -1
  8. package/dist/components/link.js +6 -2
  9. package/dist/entry.browser.js +2 -2
  10. package/dist/entry.ssr.d.ts.map +1 -1
  11. package/dist/entry.ssr.js +12 -7
  12. package/dist/handlers/rsc/background-render.js +2 -2
  13. package/dist/handlers/rsc/render-request.js +2 -2
  14. package/dist/handlers/rsc/render-to-response.js +2 -2
  15. package/dist/handlers/rsc/state.d.ts.map +1 -1
  16. package/dist/handlers/rsc/state.js +11 -5
  17. package/dist/lib/client-route-prediction.d.ts +1 -1
  18. package/dist/lib/client-route-prediction.d.ts.map +1 -1
  19. package/dist/lib/client-route-prediction.js +2 -7
  20. package/dist/lib/head/head-manager.d.ts +9 -0
  21. package/dist/lib/head/head-manager.d.ts.map +1 -0
  22. package/dist/lib/head/head-manager.js +98 -0
  23. package/dist/lib/head/head-script.js +8 -13
  24. package/dist/lib/image-optimization/fetch-remote-image.d.ts +17 -0
  25. package/dist/lib/image-optimization/fetch-remote-image.d.ts.map +1 -0
  26. package/dist/lib/image-optimization/fetch-remote-image.js +157 -0
  27. package/dist/lib/image-optimization/get-img-props.d.ts +3 -5
  28. package/dist/lib/image-optimization/get-img-props.d.ts.map +1 -1
  29. package/dist/lib/image-optimization/get-img-props.js +66 -40
  30. package/dist/lib/image-optimization/handler.d.ts +0 -3
  31. package/dist/lib/image-optimization/handler.d.ts.map +1 -1
  32. package/dist/lib/image-optimization/handler.js +339 -124
  33. package/dist/lib/image-optimization/image-config.d.ts +31 -4
  34. package/dist/lib/image-optimization/image-config.d.ts.map +1 -1
  35. package/dist/lib/image-optimization/image-config.js +6 -0
  36. package/dist/lib/image-optimization/image-error.d.ts +8 -0
  37. package/dist/lib/image-optimization/image-error.d.ts.map +1 -0
  38. package/dist/lib/image-optimization/image-error.js +10 -0
  39. package/dist/lib/image-optimization/image-plugin.d.ts +2 -0
  40. package/dist/lib/image-optimization/image-plugin.d.ts.map +1 -1
  41. package/dist/lib/image-optimization/image-plugin.js +36 -16
  42. package/dist/lib/image-optimization/is-private-ip.d.ts +2 -0
  43. package/dist/lib/image-optimization/is-private-ip.d.ts.map +1 -0
  44. package/dist/lib/image-optimization/is-private-ip.js +43 -0
  45. package/dist/lib/image-optimization/match-patterns.d.ts +4 -0
  46. package/dist/lib/image-optimization/match-patterns.d.ts.map +1 -0
  47. package/dist/lib/image-optimization/match-patterns.js +39 -0
  48. package/dist/lib/image-optimization/read-body.d.ts +2 -0
  49. package/dist/lib/image-optimization/read-body.d.ts.map +1 -0
  50. package/dist/lib/image-optimization/read-body.js +27 -0
  51. package/dist/lib/image-optimization/static-image-loader.d.ts +2 -0
  52. package/dist/lib/image-optimization/static-image-loader.d.ts.map +1 -0
  53. package/dist/lib/image-optimization/static-image-loader.js +60 -0
  54. package/dist/lib/plugin-names.d.ts +3 -0
  55. package/dist/lib/plugin-names.d.ts.map +1 -0
  56. package/dist/lib/plugin-names.js +2 -0
  57. package/dist/lib/proxy/build-proxy.d.ts +1 -1
  58. package/dist/lib/proxy/build-proxy.d.ts.map +1 -1
  59. package/dist/lib/proxy/build-proxy.js +4 -11
  60. package/dist/lib/proxy/pattern.d.ts +7 -0
  61. package/dist/lib/proxy/pattern.d.ts.map +1 -0
  62. package/dist/lib/proxy/pattern.js +18 -0
  63. package/dist/lib/proxy/types.d.ts +3 -1
  64. package/dist/lib/proxy/types.d.ts.map +1 -1
  65. package/dist/lib/route-prediction.d.ts.map +1 -1
  66. package/dist/lib/rsc-request.d.ts.map +1 -1
  67. package/dist/lib/rsc-request.js +6 -4
  68. package/dist/lib/server-router/build-entries.d.ts +76 -0
  69. package/dist/lib/server-router/build-entries.d.ts.map +1 -0
  70. package/dist/lib/server-router/build-entries.js +74 -0
  71. package/dist/lib/server-router/get-router.d.ts.map +1 -1
  72. package/dist/lib/server-router/get-router.js +55 -105
  73. package/dist/lib/server-router/rou3.d.ts +20 -0
  74. package/dist/lib/server-router/rou3.d.ts.map +1 -0
  75. package/dist/lib/server-router/rou3.js +55 -0
  76. package/dist/lib/server-router/route-entries.d.ts +16 -0
  77. package/dist/lib/server-router/route-entries.d.ts.map +1 -0
  78. package/dist/lib/server-router/route-entries.js +67 -0
  79. package/dist/lib/server-router/types.d.ts +24 -33
  80. package/dist/lib/server-router/types.d.ts.map +1 -1
  81. package/dist/lib/server-router/types.js +1 -6
  82. package/dist/lib/types.d.ts +2 -2
  83. package/dist/lib/types.d.ts.map +1 -1
  84. package/dist/plugins/prerender.d.ts +3 -4
  85. package/dist/plugins/prerender.d.ts.map +1 -1
  86. package/dist/plugins/prerender.js +31 -0
  87. package/dist/plugins/proxy.d.ts.map +1 -1
  88. package/dist/plugins/proxy.js +7 -1
  89. package/dist/plugins/router/rou3-compiled.d.ts +9 -0
  90. package/dist/plugins/router/rou3-compiled.d.ts.map +1 -0
  91. package/dist/plugins/router/rou3-compiled.js +14 -0
  92. package/dist/plugins/router/rou3-router.d.ts +3 -0
  93. package/dist/plugins/router/rou3-router.d.ts.map +1 -0
  94. package/dist/plugins/router/rou3-router.js +16 -0
  95. package/dist/plugins/router/router-plugin.d.ts +6 -16
  96. package/dist/plugins/router/router-plugin.d.ts.map +1 -1
  97. package/dist/plugins/router/router-plugin.js +43 -22
  98. package/dist/proxy.d.ts +1 -0
  99. package/dist/proxy.d.ts.map +1 -1
  100. package/dist/proxy.js +1 -0
  101. package/dist/request-context.d.ts +0 -2
  102. package/dist/request-context.d.ts.map +1 -1
  103. package/dist/request-context.js +0 -4
  104. package/dist/router.d.ts +1 -1
  105. package/dist/router.d.ts.map +1 -1
  106. package/dist/router.js +4 -4
  107. package/dist/server-router.d.ts +3 -3
  108. package/dist/server-router.d.ts.map +1 -1
  109. package/dist/server-router.js +2 -2
  110. package/dist/vite.d.ts +8 -3
  111. package/dist/vite.d.ts.map +1 -1
  112. package/dist/vite.js +72 -33
  113. package/package.json +6 -4
  114. package/dist/components/error-boundary-client.d.ts +0 -49
  115. package/dist/components/error-boundary-client.d.ts.map +0 -1
  116. package/dist/components/error-boundary-client.js +0 -71
  117. package/dist/components/error-boundary.browser.d.ts +0 -8
  118. package/dist/components/error-boundary.browser.d.ts.map +0 -1
  119. package/dist/components/error-boundary.browser.js +0 -6
  120. package/dist/components/error.d.ts +0 -2
  121. package/dist/components/error.d.ts.map +0 -1
  122. package/dist/components/error.js +0 -5
  123. package/dist/components/not-found-activator.d.ts +0 -11
  124. package/dist/components/not-found-activator.d.ts.map +0 -1
  125. package/dist/components/not-found-activator.js +0 -26
  126. package/dist/components/not-found-boundary-client.d.ts +0 -3
  127. package/dist/components/not-found-boundary-client.d.ts.map +0 -1
  128. package/dist/components/not-found-boundary-client.js +0 -20
  129. package/dist/components/not-found-boundary.browser.d.ts +0 -8
  130. package/dist/components/not-found-boundary.browser.d.ts.map +0 -1
  131. package/dist/components/not-found-boundary.browser.js +0 -4
  132. package/dist/components/not-found-boundary.d.ts +0 -8
  133. package/dist/components/not-found-boundary.d.ts.map +0 -1
  134. package/dist/components/not-found-boundary.js +0 -9
  135. package/dist/components/not-found-thrower.d.ts +0 -2
  136. package/dist/components/not-found-thrower.d.ts.map +0 -1
  137. package/dist/components/not-found-thrower.js +0 -7
  138. package/dist/components/not-found.d.ts +0 -2
  139. package/dist/components/not-found.d.ts.map +0 -1
  140. package/dist/components/not-found.js +0 -5
  141. package/dist/components/status-page.d.ts +0 -6
  142. package/dist/components/status-page.d.ts.map +0 -1
  143. package/dist/components/status-page.js +0 -61
  144. package/dist/handler.d.ts +0 -12
  145. package/dist/handler.d.ts.map +0 -1
  146. package/dist/handler.js +0 -21
  147. package/dist/handlers/rsc/constants.d.ts +0 -3
  148. package/dist/handlers/rsc/constants.d.ts.map +0 -1
  149. package/dist/handlers/rsc/constants.js +0 -2
  150. package/dist/handlers/rsc/create-response.d.ts +0 -3
  151. package/dist/handlers/rsc/create-response.d.ts.map +0 -1
  152. package/dist/handlers/rsc/create-response.js +0 -378
  153. package/dist/handlers/rsc/render-with-retries.d.ts +0 -4
  154. package/dist/handlers/rsc/render-with-retries.d.ts.map +0 -1
  155. package/dist/handlers/rsc/render-with-retries.js +0 -154
  156. package/dist/handlers/rsc/retry-loop.d.ts +0 -3
  157. package/dist/handlers/rsc/retry-loop.d.ts.map +0 -1
  158. package/dist/handlers/rsc/retry-loop.js +0 -145
  159. package/dist/handlers/rsc/stream-rsc-payload.d.ts +0 -9
  160. package/dist/handlers/rsc/stream-rsc-payload.d.ts.map +0 -1
  161. package/dist/handlers/rsc/stream-rsc-payload.js +0 -20
  162. package/dist/handlers/rsc/types.d.ts +0 -26
  163. package/dist/handlers/rsc/types.d.ts.map +0 -1
  164. package/dist/handlers/rsc/types.js +0 -7
  165. package/dist/handlers/rsc.d.ts +0 -20
  166. package/dist/handlers/rsc.d.ts.map +0 -1
  167. package/dist/handlers/rsc.js +0 -872
  168. package/dist/integration.d.ts +0 -14
  169. package/dist/integration.d.ts.map +0 -1
  170. package/dist/integration.js +0 -9
  171. package/dist/lib/dev-module-load-race.d.ts +0 -8
  172. package/dist/lib/dev-module-load-race.d.ts.map +0 -1
  173. package/dist/lib/dev-module-load-race.js +0 -31
  174. package/dist/lib/dev-page-exports.d.ts +0 -5
  175. package/dist/lib/dev-page-exports.d.ts.map +0 -1
  176. package/dist/lib/dev-page-exports.js +0 -14
  177. package/dist/lib/environment-names.d.ts +0 -11
  178. package/dist/lib/environment-names.d.ts.map +0 -1
  179. package/dist/lib/environment-names.js +0 -10
  180. package/dist/lib/initial-hash-scroll.d.ts.map +0 -1
  181. package/dist/lib/not-found.d.ts +0 -5
  182. package/dist/lib/not-found.d.ts.map +0 -1
  183. package/dist/lib/not-found.js +0 -7
  184. package/dist/lib/page-exports.d.ts +0 -9
  185. package/dist/lib/page-exports.d.ts.map +0 -1
  186. package/dist/lib/page-exports.js +0 -47
  187. package/dist/lib/pattern-segments.d.ts +0 -8
  188. package/dist/lib/pattern-segments.d.ts.map +0 -1
  189. package/dist/lib/pattern-segments.js +0 -22
  190. package/dist/lib/ppr/cache-status.d.ts +0 -3
  191. package/dist/lib/ppr/cache-status.d.ts.map +0 -1
  192. package/dist/lib/ppr/cache-status.js +0 -5
  193. package/dist/lib/server-router-contract.d.ts +0 -42
  194. package/dist/lib/server-router-contract.d.ts.map +0 -1
  195. package/dist/lib/server-router-contract.js +0 -6
  196. package/dist/lib/server-router-loader.d.ts +0 -10
  197. package/dist/lib/server-router-loader.d.ts.map +0 -1
  198. package/dist/lib/server-router-loader.js +0 -180
  199. package/dist/lib/server-router.d.ts +0 -8
  200. package/dist/lib/server-router.d.ts.map +0 -1
  201. package/dist/lib/server-router.js +0 -173
  202. package/dist/plugins/ppr-upgrade.d.ts +0 -36
  203. package/dist/plugins/ppr-upgrade.d.ts.map +0 -1
  204. package/dist/plugins/ppr-upgrade.js +0 -32
  205. package/dist/plugins/server-router-fallback.d.ts +0 -15
  206. package/dist/plugins/server-router-fallback.d.ts.map +0 -1
  207. package/dist/plugins/server-router-fallback.js +0 -30
  208. package/dist/server.d.ts +0 -10
  209. package/dist/server.d.ts.map +0 -1
  210. package/dist/server.js +0 -57
@@ -0,0 +1,98 @@
1
+ import { createElement } from "react";
2
+ import { createPortal, flushSync } from "react-dom";
3
+ import { createRoot } from "react-dom/client";
4
+ const heads = new Map();
5
+ let root = null;
6
+ let titleElement = null;
7
+ let scheduled = false;
8
+ function getText(children) {
9
+ if (typeof children === "string" ||
10
+ typeof children === "number" ||
11
+ typeof children === "bigint") {
12
+ return String(children);
13
+ }
14
+ if (Array.isArray(children))
15
+ return children.map(getText).join("");
16
+ return "";
17
+ }
18
+ function updateTitle(entry) {
19
+ if (!entry) {
20
+ titleElement?.remove();
21
+ titleElement = null;
22
+ return;
23
+ }
24
+ if (!titleElement)
25
+ titleElement = document.createElement("title");
26
+ const firstTitle = document.head.querySelector("title");
27
+ if (firstTitle !== titleElement)
28
+ document.head.insertBefore(titleElement, firstTitle);
29
+ titleElement.textContent = getText(entry.children);
30
+ }
31
+ function updateHead() {
32
+ scheduled = false;
33
+ if (!root) {
34
+ for (const element of document.querySelectorAll("[data-rebask-head]")) {
35
+ element.remove();
36
+ }
37
+ new MutationObserver((mutations) => {
38
+ for (const mutation of mutations) {
39
+ for (const node of mutation.addedNodes) {
40
+ if (node.nodeType !== 1)
41
+ continue;
42
+ const element = node;
43
+ if (element.matches("[data-rebask-head]"))
44
+ element.remove();
45
+ else
46
+ for (const child of element.querySelectorAll("[data-rebask-head]"))
47
+ child.remove();
48
+ }
49
+ }
50
+ }).observe(document.documentElement, { childList: true, subtree: true });
51
+ root = createRoot(document.createElement("div"));
52
+ }
53
+ const keyed = new Map();
54
+ const elements = [];
55
+ let title;
56
+ for (const [id, entries] of heads) {
57
+ for (let i = 0; i < entries.length; i++) {
58
+ const entry = entries[i];
59
+ let key = entry.tag.toLowerCase();
60
+ if (key === "meta") {
61
+ const attr = entry.props.name || entry.props.property || entry.props.httpEquiv;
62
+ key = attr ? `${key}:${String(attr)}` : null;
63
+ }
64
+ else if (key === "link") {
65
+ key = entry.props.rel === "canonical" ? "link:canonical" : null;
66
+ }
67
+ if (key === null) {
68
+ elements.push(createElement(entry.tag, { key: `${id}:${i}`, ...entry.props }, entry.children));
69
+ }
70
+ else if (key === "title") {
71
+ title = entry;
72
+ }
73
+ else {
74
+ keyed.set(key, entry);
75
+ }
76
+ }
77
+ }
78
+ for (const [key, entry] of keyed) {
79
+ elements.push(createElement(entry.tag, { key, ...entry.props }, entry.children));
80
+ }
81
+ const headRoot = root;
82
+ flushSync(() => headRoot.render(createPortal(elements, document.head)));
83
+ updateTitle(title);
84
+ }
85
+ function scheduleUpdate() {
86
+ if (scheduled)
87
+ return;
88
+ scheduled = true;
89
+ queueMicrotask(updateHead);
90
+ }
91
+ export function registerHead(id, entries) {
92
+ heads.set(id, entries);
93
+ scheduleUpdate();
94
+ }
95
+ export function unregisterHead(id) {
96
+ if (heads.delete(id))
97
+ scheduleUpdate();
98
+ }
@@ -1,7 +1,8 @@
1
- // This script is inlined in the browser when the `HeadScript` component is used.
1
+ // This script is inlined in the document bootstrap.
2
2
  function dedupeHeadElements() {
3
- const seen = new Map();
4
- for (const element of document.querySelectorAll("[data-order]")) {
3
+ const seen = new Set();
4
+ // Keep the last element per key.
5
+ for (const element of Array.from(document.querySelectorAll("[data-rebask-head]")).reverse()) {
5
6
  let key = element.tagName.toLowerCase();
6
7
  if (key === "meta") {
7
8
  const attr = element.getAttribute("name") ||
@@ -16,23 +17,17 @@ function dedupeHeadElements() {
16
17
  continue;
17
18
  key = "link:canonical";
18
19
  }
19
- const order = Number(element.getAttribute("data-order")) || 0;
20
- const previous = seen.get(key);
21
- if (!previous || order > previous.order) {
22
- if (previous)
23
- previous.element.remove();
24
- seen.set(key, { element, order });
25
- }
26
- else {
20
+ if (seen.has(key))
27
21
  element.remove();
28
- }
22
+ else
23
+ seen.add(key);
29
24
  }
30
25
  }
31
26
  dedupeHeadElements();
32
27
  const observer = new MutationObserver((mutations) => {
33
28
  for (const mutation of mutations) {
34
29
  for (const node of mutation.addedNodes) {
35
- if (node.nodeType === 1 && node.matches?.("[data-order]")) {
30
+ if (node.nodeType === 1 && node.matches?.("[data-rebask-head]")) {
36
31
  dedupeHeadElements();
37
32
  return;
38
33
  }
@@ -0,0 +1,17 @@
1
+ import type { ImageConfigComplete } from "./image-config.js";
2
+ type RemoteImageConfig = Pick<ImageConfigComplete, "remotePatterns" | "dangerouslyAllowLocalIP" | "maximumRedirects" | "maximumResponseBody">;
3
+ export type RemoteImageCacheControl = {
4
+ maxAge: number | undefined;
5
+ browserMaxAge: number | undefined;
6
+ sharedMaxAge: number | undefined;
7
+ noStore: boolean;
8
+ private: boolean;
9
+ noCache: boolean;
10
+ };
11
+ export declare const PUBLIC_IMAGE_CACHE_CONTROL: RemoteImageCacheControl;
12
+ export declare function fetchRemoteImage(href: string, config: RemoteImageConfig): Promise<{
13
+ buffer: Buffer;
14
+ cacheControl: RemoteImageCacheControl;
15
+ }>;
16
+ export {};
17
+ //# sourceMappingURL=fetch-remote-image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-remote-image.d.ts","sourceRoot":"","sources":["../../../src/lib/image-optimization/fetch-remote-image.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAS5D,KAAK,iBAAiB,GAAG,IAAI,CAC5B,mBAAmB,EACnB,gBAAgB,GAAG,yBAAyB,GAAG,kBAAkB,GAAG,qBAAqB,CACzF,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG;IACrC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,eAAO,MAAM,0BAA0B,EAAE,uBAOxC,CAAA;AAwGD,wBAAsB,gBAAgB,CACrC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,iBAAiB,GACvB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,uBAAuB,CAAA;CAAE,CAAC,CAiEpE"}
@@ -0,0 +1,157 @@
1
+ import { lookup } from "node:dns/promises";
2
+ import { request as httpRequest } from "node:http";
3
+ import { request as httpsRequest } from "node:https";
4
+ import { isIP } from "node:net";
5
+ import { ImageError, INVALID_UPSTREAM, NOT_ALLOWED, TIMED_OUT } from "./image-error.js";
6
+ import { isPrivateIp } from "./is-private-ip.js";
7
+ import { hasRemoteMatch } from "./match-patterns.js";
8
+ import { readBody } from "./read-body.js";
9
+ const TIMEOUT_MS = 7000;
10
+ const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
11
+ export const PUBLIC_IMAGE_CACHE_CONTROL = {
12
+ maxAge: undefined,
13
+ browserMaxAge: undefined,
14
+ sharedMaxAge: undefined,
15
+ noStore: false,
16
+ private: false,
17
+ noCache: false,
18
+ };
19
+ function parseCacheControl(cacheControl) {
20
+ let maxAge;
21
+ let sMaxAge;
22
+ let noStore = false;
23
+ let privateCache = false;
24
+ let noCache = false;
25
+ for (const directive of cacheControl?.split(",") ?? []) {
26
+ const [rawKey, rawValue] = directive.trim().split("=", 2);
27
+ if (!rawKey)
28
+ continue;
29
+ const key = rawKey.trim().toLowerCase();
30
+ const parsed = rawValue ? parseInt(rawValue.replace(/"/g, "").trim(), 10) : Number.NaN;
31
+ const validAge = !Number.isNaN(parsed);
32
+ if (key === "max-age" && validAge) {
33
+ maxAge = parsed;
34
+ }
35
+ else if (key === "s-maxage" && validAge) {
36
+ sMaxAge = parsed;
37
+ }
38
+ else if (key === "no-store") {
39
+ noStore = true;
40
+ }
41
+ else if (key === "private") {
42
+ privateCache = true;
43
+ }
44
+ else if (key === "no-cache") {
45
+ noCache = true;
46
+ }
47
+ }
48
+ return {
49
+ maxAge: sMaxAge ?? maxAge,
50
+ browserMaxAge: maxAge,
51
+ sharedMaxAge: sMaxAge,
52
+ noStore,
53
+ private: privateCache,
54
+ noCache,
55
+ };
56
+ }
57
+ async function resolveAddresses(hostname) {
58
+ const literalFamily = isIP(hostname);
59
+ if (literalFamily) {
60
+ return [{ address: hostname, family: literalFamily }];
61
+ }
62
+ try {
63
+ return await lookup(hostname, { all: true });
64
+ }
65
+ catch {
66
+ throw new ImageError(400, INVALID_UPSTREAM);
67
+ }
68
+ }
69
+ // Pin the connection to the addresses checked above so a second DNS lookup cannot rebind.
70
+ function createPinnedLookup(hostname, addresses) {
71
+ return (name, options, callback) => {
72
+ if (name !== hostname) {
73
+ const error = new Error(`Unexpected lookup for ${name}`);
74
+ error.code = "ENOTFOUND";
75
+ callback(error, [], 0);
76
+ return;
77
+ }
78
+ const family = typeof options.family === "number" ? options.family : 0;
79
+ const matched = family ? addresses.filter((address) => address.family === family) : addresses;
80
+ if (matched.length === 0) {
81
+ const error = new Error(`No address available for ${hostname}`);
82
+ error.code = "ENOTFOUND";
83
+ callback(error, [], 0);
84
+ return;
85
+ }
86
+ if (options.all) {
87
+ callback(null, matched);
88
+ }
89
+ else {
90
+ callback(null, matched[0].address, matched[0].family);
91
+ }
92
+ };
93
+ }
94
+ function requestImage(url, hostname, addresses, signal) {
95
+ return new Promise((resolve, reject) => {
96
+ const request = url.protocol === "https:" ? httpsRequest : httpRequest;
97
+ const req = request(url, {
98
+ lookup: isIP(hostname) ? undefined : createPinnedLookup(hostname, addresses),
99
+ signal,
100
+ headers: { accept: "*/*", "accept-encoding": "identity", "user-agent": "rebask" },
101
+ }, resolve);
102
+ req.on("error", reject);
103
+ req.end();
104
+ });
105
+ }
106
+ export async function fetchRemoteImage(href, config) {
107
+ let current = new URL(href);
108
+ for (let redirects = 0; redirects <= config.maximumRedirects; redirects++) {
109
+ if (!hasRemoteMatch(config.remotePatterns, current)) {
110
+ throw new ImageError(400, NOT_ALLOWED);
111
+ }
112
+ const hostname = current.hostname.replace(/^\[|\]$/g, "");
113
+ const addresses = await resolveAddresses(hostname);
114
+ if (!config.dangerouslyAllowLocalIP &&
115
+ addresses.some((address) => isPrivateIp(address.address))) {
116
+ throw new ImageError(400, NOT_ALLOWED);
117
+ }
118
+ const signal = AbortSignal.timeout(TIMEOUT_MS);
119
+ let response;
120
+ try {
121
+ response = await requestImage(current, hostname, addresses, signal);
122
+ }
123
+ catch {
124
+ if (signal.aborted) {
125
+ throw new ImageError(504, TIMED_OUT);
126
+ }
127
+ throw new ImageError(400, INVALID_UPSTREAM);
128
+ }
129
+ const statusCode = response.statusCode ?? 0;
130
+ const location = response.headers.location;
131
+ if (REDIRECT_STATUSES.has(statusCode) && location) {
132
+ response.destroy();
133
+ if (redirects === config.maximumRedirects) {
134
+ throw new ImageError(508, INVALID_UPSTREAM);
135
+ }
136
+ let next;
137
+ try {
138
+ next = new URL(location, current);
139
+ }
140
+ catch {
141
+ throw new ImageError(400, INVALID_UPSTREAM);
142
+ }
143
+ if (next.protocol !== "http:" && next.protocol !== "https:") {
144
+ throw new ImageError(400, NOT_ALLOWED);
145
+ }
146
+ current = next;
147
+ continue;
148
+ }
149
+ if (statusCode < 200 || statusCode >= 300) {
150
+ response.destroy();
151
+ throw new ImageError(400, INVALID_UPSTREAM);
152
+ }
153
+ const buffer = await readBody(response, config.maximumResponseBody, signal);
154
+ return { buffer, cacheControl: parseCacheControl(response.headers["cache-control"]) };
155
+ }
156
+ throw new ImageError(508, INVALID_UPSTREAM);
157
+ }
@@ -1,12 +1,10 @@
1
1
  import type { CSSProperties, JSX } from "react";
2
- import type { ImageConfigComplete, ImageLoaderProps } from "./image-config.js";
2
+ import type { ImageBlur, ImageConfigComplete, ImageLoaderProps } from "./image-config.js";
3
3
  export interface StaticImageData {
4
4
  src: string;
5
5
  height: number;
6
6
  width: number;
7
- blurDataURL?: string;
8
- blurWidth?: number;
9
- blurHeight?: number;
7
+ blur?: ImageBlur;
10
8
  }
11
9
  export interface StaticRequire {
12
10
  default: StaticImageData;
@@ -44,7 +42,7 @@ export type PlaceholderStyle = Partial<Pick<CSSProperties, "backgroundSize" | "b
44
42
  /**
45
43
  * A shared function, used on both client and server, to generate the props for <img>.
46
44
  */
47
- export declare function getImgProps({ src, sizes, unoptimized, preload, loading, className, quality, width, height, fill, style, onLoad: _onLoad, placeholder, blurDataURL, fetchPriority, decoding, ...rest }: ImageProps, _state: {
45
+ export declare function getImgProps({ src, sizes, unoptimized, preload, loading, className, quality, width, height, fill, style, loader: loaderProp, placeholder, blurDataURL, fetchPriority, decoding, ...rest }: ImageProps, _state: {
48
46
  imgConf: ImageConfigComplete;
49
47
  showAltText?: boolean;
50
48
  blurComplete?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"get-img-props.d.ts","sourceRoot":"","sources":["../../../src/lib/image-optimization/get-img-props.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"get-img-props.d.ts","sourceRoot":"","sources":["../../../src/lib/image-optimization/get-img-props.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,9 @@ import { warnOnce } from "./warn-once.js";
5
5
  const VALID_LOADING_VALUES = ["lazy", "eager", undefined];
6
6
  // Object-fit values that are not valid background-size values
7
7
  const INVALID_BACKGROUND_SIZE_VALUES = ["-moz-initial", "fill", "none", "scale-down", undefined];
8
+ const allImgs = new Map();
9
+ let perfObserver;
10
+ const preparedConfigs = new WeakMap();
8
11
  function isStaticRequire(src) {
9
12
  return src.default !== undefined;
10
13
  }
@@ -33,7 +36,8 @@ function getWidths({ deviceSizes, allSizes }, width, sizes) {
33
36
  // Find all the "vw" percent sizes used in the sizes prop
34
37
  const viewportWidthRe = /(^|\s)(1?\d?\d)vw/g;
35
38
  const percentSizes = [];
36
- for (let match; (match = viewportWidthRe.exec(sizes)); match) {
39
+ let match;
40
+ while ((match = viewportWidthRe.exec(sizes))) {
37
41
  percentSizes.push(parseInt(match[2]));
38
42
  }
39
43
  if (percentSizes.length) {
@@ -58,14 +62,14 @@ function getWidths({ deviceSizes, allSizes }, width, sizes) {
58
62
  // > wasteful as the human eye cannot see that level of detail without
59
63
  // > something like a magnifying glass.
60
64
  // https://blog.twitter.com/engineering/en_us/topics/infrastructure/2019/capping-image-fidelity-on-ultra-high-resolution-devices.html
61
- [width, width * 2 /*, width * 3*/].map((w) => allSizes.find((p) => p >= w) || allSizes[allSizes.length - 1])),
65
+ [width, width * 2].map((w) => allSizes.find((p) => p >= w) || allSizes[allSizes.length - 1])),
62
66
  ];
63
67
  return { widths, kind: "x" };
64
68
  }
65
69
  /**
66
70
  * A shared function, used on both client and server, to generate the props for <img>.
67
71
  */
68
- export function getImgProps({ src, sizes, unoptimized = false, preload = false, loading, className, quality, width, height, fill = false, style, onLoad: _onLoad, placeholder = "empty", blurDataURL, fetchPriority, decoding = "async", ...rest }, _state) {
72
+ export function getImgProps({ src, sizes, unoptimized = false, preload = false, loading, className, quality, width, height, fill = false, style, loader: loaderProp, placeholder = "empty", blurDataURL, fetchPriority, decoding = "async", ...rest }, _state) {
69
73
  const { imgConf, showAltText, blurComplete } = _state;
70
74
  let config;
71
75
  const c = imgConf || imageConfigDefault;
@@ -73,19 +77,22 @@ export function getImgProps({ src, sizes, unoptimized = false, preload = false,
73
77
  config = c;
74
78
  }
75
79
  else {
76
- const allSizes = [...c.deviceSizes, ...c.imageSizes].sort((a, b) => a - b);
77
- const deviceSizes = c.deviceSizes.sort((a, b) => a - b);
78
- const qualities = c.qualities?.sort((a, b) => a - b);
79
- config = { ...c, allSizes, deviceSizes, qualities };
80
+ const prepared = preparedConfigs.get(c);
81
+ if (prepared) {
82
+ config = prepared;
83
+ }
84
+ else {
85
+ const allSizes = [...c.deviceSizes, ...c.imageSizes].sort((a, b) => a - b);
86
+ const deviceSizes = [...c.deviceSizes].sort((a, b) => a - b);
87
+ const qualities = c.qualities ? [...c.qualities].sort((a, b) => a - b) : undefined;
88
+ config = { ...c, allSizes, deviceSizes, qualities };
89
+ preparedConfigs.set(c, config);
90
+ }
80
91
  }
81
- const loader = rest.loader || defaultLoader;
82
- // Remove property so it's not spread on <img> element
83
- delete rest.loader;
84
- delete rest.srcSet;
92
+ const loader = loaderProp || defaultLoader;
85
93
  // This special value indicates that the user
86
94
  // didn't define a "loader" prop.
87
95
  const isDefaultLoader = "__rebask_img_default" in loader;
88
- let staticSrc = "";
89
96
  let widthInt = getInt(width);
90
97
  let heightInt = getInt(height);
91
98
  let blurWidth;
@@ -98,10 +105,10 @@ export function getImgProps({ src, sizes, unoptimized = false, preload = false,
98
105
  if (!staticImageData.height || !staticImageData.width) {
99
106
  throw new Error(`An object should only be passed to the image component src parameter if it comes from a static image import. It must include height and width. Received ${JSON.stringify(staticImageData)}`);
100
107
  }
101
- blurWidth = staticImageData.blurWidth;
102
- blurHeight = staticImageData.blurHeight;
103
- blurDataURL = blurDataURL || staticImageData.blurDataURL;
104
- staticSrc = staticImageData.src;
108
+ blurWidth = staticImageData.blur?.width;
109
+ blurHeight = staticImageData.blur?.height;
110
+ blurDataURL = blurDataURL || staticImageData.blur?.dataURL;
111
+ src = staticImageData.src;
105
112
  if (!fill) {
106
113
  if (!widthInt && !heightInt) {
107
114
  widthInt = staticImageData.width;
@@ -117,7 +124,6 @@ export function getImgProps({ src, sizes, unoptimized = false, preload = false,
117
124
  }
118
125
  }
119
126
  }
120
- src = typeof src === "string" ? src : staticSrc;
121
127
  let isLazy = !preload && (loading === "lazy" || typeof loading === "undefined");
122
128
  if (!src || src.startsWith("data:") || src.startsWith("blob:")) {
123
129
  // https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
@@ -133,13 +139,7 @@ export function getImgProps({ src, sizes, unoptimized = false, preload = false,
133
139
  }
134
140
  const qualityInt = getInt(quality);
135
141
  if (import.meta.env.DEV) {
136
- if (!src) {
137
- // React doesn't show the stack trace and there's
138
- // no `src` to help identify which image, so we
139
- // instead console.error(ref) during mount.
140
- unoptimized = true;
141
- }
142
- else {
142
+ if (src) {
143
143
  if (fill) {
144
144
  if (width) {
145
145
  throw new Error(`Image with src "${src}" has both "width" and "fill" properties. Only one should be used.`);
@@ -170,12 +170,10 @@ export function getImgProps({ src, sizes, unoptimized = false, preload = false,
170
170
  else if (isNaN(heightInt)) {
171
171
  throw new Error(`Image with src "${src}" has invalid "height" property. Expected a numeric value in pixels but received "${height}".`);
172
172
  }
173
- const startsWithSpaceOrControl = src.length > 0 && src.charCodeAt(0) <= 0x20;
174
- if (startsWithSpaceOrControl) {
173
+ if (src.length > 0 && src.charCodeAt(0) <= 0x20) {
175
174
  throw new Error(`Image with src "${src}" cannot start with a space or control character. Use src.trimStart() to remove it or encodeURIComponent(src) to keep it.`);
176
175
  }
177
- const endsWithSpaceOrControl = src.length > 0 && src.charCodeAt(src.length - 1) <= 0x20;
178
- if (endsWithSpaceOrControl) {
176
+ if (src.length > 0 && src.charCodeAt(src.length - 1) <= 0x20) {
179
177
  throw new Error(`Image with src "${src}" cannot end with a space or control character. Use src.trimEnd() to remove it or encodeURIComponent(src) to keep it.`);
180
178
  }
181
179
  }
@@ -191,12 +189,10 @@ export function getImgProps({ src, sizes, unoptimized = false, preload = false,
191
189
  !placeholder.startsWith("data:image/")) {
192
190
  throw new Error(`Image with src "${src}" has invalid "placeholder" property "${placeholder}".`);
193
191
  }
194
- if (placeholder !== "empty") {
195
- if (widthInt && heightInt && widthInt * heightInt < 1600) {
196
- warnOnce(`Image with src "${src}" is smaller than 40x40. Consider removing the "placeholder" property to improve performance.`);
197
- }
192
+ if (placeholder !== "empty" && widthInt && heightInt && widthInt * heightInt < 1600) {
193
+ warnOnce(`Image with src "${src}" is smaller than 40x40. Consider removing the "placeholder" property to improve performance.`);
198
194
  }
199
- if (qualityInt && config.qualities && !config.qualities.includes(qualityInt)) {
195
+ if (qualityInt && config.qualities?.length && !config.qualities.includes(qualityInt)) {
200
196
  warnOnce(`Image with src "${src}" is using quality "${qualityInt}" which is not configured in images.qualities [${config.qualities.join(", ")}]. Please update your config to [${[...config.qualities, qualityInt].sort().join(", ")}].`);
201
197
  }
202
198
  if (placeholder === "blur" && !blurDataURL) {
@@ -205,6 +201,7 @@ export function getImgProps({ src, sizes, unoptimized = false, preload = false,
205
201
  Possible solutions:
206
202
  - Add a "blurDataURL" property, the contents should be a small Data URL to represent the image
207
203
  - Change the "src" property to a static import with one of the supported file types: ${VALID_BLUR_EXT.join(",")} (animated images not supported)
204
+ - Install \`sharp\` so Rebask can generate the blur placeholder for static imports
208
205
  - Remove the "placeholder" property, effectively no blur effect`);
209
206
  }
210
207
  }
@@ -219,6 +216,7 @@ export function getImgProps({ src, sizes, unoptimized = false, preload = false,
219
216
  bottom: 0,
220
217
  }
221
218
  : {}, showAltText ? {} : { color: "transparent" }, style);
219
+ const objectFit = imgStyle.objectFit || "cover";
222
220
  const backgroundImage = !blurComplete && placeholder !== "empty"
223
221
  ? placeholder === "blur"
224
222
  ? `url("data:image/svg+xml;charset=utf-8,${getImageBlurSvg({
@@ -227,11 +225,10 @@ export function getImgProps({ src, sizes, unoptimized = false, preload = false,
227
225
  blurWidth,
228
226
  blurHeight,
229
227
  blurDataURL: blurDataURL || "", // assume not undefined
230
- objectFit: imgStyle.objectFit || "cover",
228
+ objectFit,
231
229
  })}")`
232
230
  : `url("${placeholder}")` // assume `data:image/`
233
231
  : null;
234
- const objectFit = imgStyle.objectFit || "cover";
235
232
  const backgroundSize = !INVALID_BACKGROUND_SIZE_VALUES.includes(objectFit)
236
233
  ? objectFit
237
234
  : objectFit === "fill"
@@ -265,18 +262,16 @@ export function getImgProps({ src, sizes, unoptimized = false, preload = false,
265
262
  }
266
263
  else {
267
264
  const { widths, kind } = getWidths(config, widthInt, sizes);
268
- const last = widths.length - 1;
265
+ const urls = widths.map((width) => loader({ config, src, quality: qualityInt, width }));
269
266
  imgSizes = !sizes && kind === "w" ? "100vw" : sizes;
270
- imgSrcSet = widths
271
- .map((w, i) => `${loader({ config, src, quality: qualityInt, width: w })} ${kind === "w" ? w : i + 1}${kind}`)
272
- .join(", ");
267
+ imgSrcSet = urls.map((url, i) => `${url} ${kind === "w" ? widths[i] : i + 1}${kind}`).join(", ");
273
268
  // It's intended to keep `src` the last attribute because React updates
274
269
  // attributes in order. If we keep `src` the first one, Safari will
275
270
  // immediately start to fetch `src`, before `sizes` and `srcSet` are even
276
271
  // updated by React. That causes multiple unnecessary requests if `srcSet`
277
272
  // and `sizes` are defined.
278
273
  // This bug cannot be reproduced in Chrome or Firefox.
279
- imgSrc = loader({ config, src, quality: qualityInt, width: widths[last] });
274
+ imgSrc = urls[urls.length - 1];
280
275
  }
281
276
  const loadingFinal = isLazy ? "lazy" : loading;
282
277
  const props = {
@@ -293,5 +288,36 @@ export function getImgProps({ src, sizes, unoptimized = false, preload = false,
293
288
  src: imgSrc,
294
289
  };
295
290
  const meta = { unoptimized, preload, placeholder, fill };
291
+ if (import.meta.env.DEV && typeof window !== "undefined") {
292
+ if (!perfObserver && window.PerformanceObserver) {
293
+ perfObserver = new PerformanceObserver((entryList) => {
294
+ for (const entry of entryList.getEntries()) {
295
+ const imgSrc = entry.element?.src ?? "";
296
+ const image = allImgs.get(imgSrc);
297
+ if (image &&
298
+ image.loading === "lazy" &&
299
+ image.placeholder === "empty" &&
300
+ !image.src.startsWith("data:") &&
301
+ !image.src.startsWith("blob:")) {
302
+ warnOnce(`Image with src "${image.src}" was detected as the Largest Contentful Paint (LCP). Please add the \`loading="eager"\` property if this image is above the fold.`);
303
+ }
304
+ }
305
+ });
306
+ try {
307
+ perfObserver.observe({ type: "largest-contentful-paint", buffered: true });
308
+ }
309
+ catch (error) {
310
+ console.error(error);
311
+ }
312
+ }
313
+ let fullUrl;
314
+ try {
315
+ fullUrl = new URL(props.src);
316
+ }
317
+ catch {
318
+ fullUrl = new URL(props.src, window.location.href);
319
+ }
320
+ allImgs.set(fullUrl.href, { src, loading: loadingFinal, placeholder });
321
+ }
296
322
  return { props, meta };
297
323
  }
@@ -1,5 +1,2 @@
1
- /**
2
- * Handler for image optimization requests.
3
- */
4
1
  export declare function handleImage(request: Request, sharp: any): Promise<Response>;
5
2
  //# sourceMappingURL=handler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/lib/image-optimization/handler.ts"],"names":[],"mappings":"AAkFA;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAgHjF"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/lib/image-optimization/handler.ts"],"names":[],"mappings":"AA+TA,wBAAsB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CA6LjF"}