@pagepocket/lib 0.8.6 → 0.9.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 (64) hide show
  1. package/dist/cheerio/types.d.ts +5 -0
  2. package/dist/cheerio/types.js +1 -0
  3. package/dist/core/completion.js +2 -1
  4. package/dist/core/content-store.js +2 -2
  5. package/dist/css-rewrite.d.ts +1 -1
  6. package/dist/css-rewrite.js +2 -2
  7. package/dist/hackers/replay-dom-rewrite/script-part-1.d.ts +1 -0
  8. package/dist/hackers/replay-dom-rewrite/script-part-1.js +202 -0
  9. package/dist/hackers/replay-dom-rewrite/script-part-2.d.ts +1 -0
  10. package/dist/hackers/replay-dom-rewrite/script-part-2.js +173 -0
  11. package/dist/hackers/replay-dom-rewrite.js +3 -373
  12. package/dist/hackers/replay-svg-image.js +32 -8
  13. package/dist/hackers/replay-xhr.js +3 -3
  14. package/dist/path-resolver.js +2 -2
  15. package/dist/replace-elements/actions.d.ts +3 -3
  16. package/dist/replace-elements/actions.js +36 -16
  17. package/dist/replace-elements/match.d.ts +2 -2
  18. package/dist/replace-elements/match.js +22 -11
  19. package/dist/replace-elements/normalize.d.ts +1 -1
  20. package/dist/replace-elements/normalize.js +4 -2
  21. package/dist/replay/match-api.js +29 -17
  22. package/dist/replay/templates/replay-script-template.js +16 -332
  23. package/dist/replay/templates/replay-script-template.part-1.d.ts +5 -0
  24. package/dist/replay/templates/replay-script-template.part-1.js +101 -0
  25. package/dist/replay/templates/replay-script-template.part-2.d.ts +3 -0
  26. package/dist/replay/templates/replay-script-template.part-2.js +222 -0
  27. package/dist/replay/templates/replay-script-template.part-3.d.ts +3 -0
  28. package/dist/replay/templates/replay-script-template.part-3.js +9 -0
  29. package/dist/resource-proxy/pathname-variants.js +8 -5
  30. package/dist/resource-proxy.js +10 -10
  31. package/dist/resources.d.ts +3 -2
  32. package/dist/resources.js +6 -3
  33. package/dist/rewrite-links/js-imports.d.ts +1 -1
  34. package/dist/rewrite-links/link-rel.d.ts +2 -2
  35. package/dist/rewrite-links/meta-refresh.d.ts +1 -1
  36. package/dist/rewrite-links/meta-refresh.js +6 -3
  37. package/dist/rewrite-links/srcset.d.ts +1 -1
  38. package/dist/rewrite-links/srcset.js +4 -2
  39. package/dist/rewrite-links/url-resolve.d.ts +2 -2
  40. package/dist/rewrite-links/url-resolve.js +2 -2
  41. package/dist/rewrite-links.d.ts +1 -1
  42. package/dist/rewrite-links.js +12 -6
  43. package/dist/snapshot-builder/build-snapshot.js +2 -3
  44. package/dist/snapshot-builder/capture-index/index-capture.js +2 -1
  45. package/dist/snapshot-builder/emit-document.d.ts +1 -1
  46. package/dist/snapshot-builder/emit-document.js +1 -1
  47. package/dist/snapshot-builder/grouping.js +2 -2
  48. package/dist/snapshot-builder/path-map.d.ts +1 -1
  49. package/dist/snapshot-builder/path-map.js +1 -1
  50. package/dist/snapshot-builder/resources-path.js +8 -4
  51. package/dist/snapshot-builder/rewrite-resource.d.ts +2 -2
  52. package/dist/snapshot-builder/rewrite-resource.js +2 -2
  53. package/dist/types.d.ts +3 -3
  54. package/dist/units/internal/async-queue.d.ts +9 -0
  55. package/dist/units/internal/async-queue.js +57 -0
  56. package/dist/units/internal/deferred-tracker.d.ts +5 -0
  57. package/dist/units/internal/deferred-tracker.js +13 -0
  58. package/dist/units/internal/runtime.d.ts +37 -0
  59. package/dist/units/internal/runtime.js +113 -0
  60. package/dist/units/runner.js +3 -184
  61. package/dist/utils.d.ts +1 -1
  62. package/dist/utils.js +6 -6
  63. package/package.json +5 -4
  64. package/README.md +0 -357
@@ -0,0 +1,222 @@
1
+ export const buildReplayInjectedScriptPart2 = (options) => {
2
+ const { matchAPISource } = options;
3
+ return `
4
+ const matchAPI = ${matchAPISource};
5
+
6
+ const primeLookups = (snapshot) => {
7
+ records = snapshot.records || [];
8
+ byKey.clear();
9
+ for (const record of records) {
10
+ if (!record || !record.url || !record.method) continue;
11
+ const keys = makeVariantKeys(record.method, record.url, record.requestBody || record.requestBodyBase64 || "");
12
+ for (const key of keys) {
13
+ if (!byKey.has(key)) {
14
+ byKey.set(key, record);
15
+ }
16
+ }
17
+ }
18
+ };
19
+
20
+ const ready = (async () => {
21
+ const [apiSnapshot, resourcesPath] = await Promise.all([loadApiSnapshot(), loadResourcesPath()]);
22
+ resourceIndex = __pagepocketResourceProxy.buildIndex(resourcesPath || {});
23
+
24
+ embeddedByPath = new Map();
25
+ try {
26
+ const items = (resourcesPath && resourcesPath.items) || [];
27
+ for (const item of items) {
28
+ if (!item || !item.path || !item.data || item.dataEncoding !== "base64") {
29
+ continue;
30
+ }
31
+ embeddedByPath.set(item.path, item);
32
+ }
33
+ } catch {}
34
+
35
+ const snapshot = apiSnapshot || {};
36
+ primeLookups(snapshot);
37
+ return snapshot;
38
+ })();
39
+
40
+ const isLocalResource = (value) => {
41
+ if (!value) return false;
42
+ if (value.startsWith("data:") || value.startsWith("blob:")) return true;
43
+ if (value.startsWith("//")) return false;
44
+ if (value.startsWith("/")) return true;
45
+ return false;
46
+ };
47
+
48
+ const findRecord = (method, url, body) => {
49
+ return matchAPI({ records, byKey, baseUrl, method, url, body });
50
+ };
51
+
52
+ const urlLookupCache = new Map();
53
+ const findByUrl = (url) => {
54
+ if (isLocalResource(url)) return null;
55
+ if (urlLookupCache.has(url)) {
56
+ return urlLookupCache.get(url);
57
+ }
58
+ const record = matchAPI({ records, byKey, baseUrl, method: "GET", url, body: "" });
59
+ urlLookupCache.set(url, record || null);
60
+ return record;
61
+ };
62
+
63
+ const findLocalPath = (url) => {
64
+ try {
65
+ if (!url || isLocalResource(String(url))) {
66
+ return null;
67
+ }
68
+ if (!resourceIndex) {
69
+ return null;
70
+ }
71
+
72
+ const resolvedPath = __pagepocketResourceProxy.resolveToLocalPath(String(url), baseUrl, resourceIndex);
73
+ if (!resolvedPath) {
74
+ return null;
75
+ }
76
+
77
+ const embedded = embeddedByPath && embeddedByPath.get(resolvedPath);
78
+ if (embedded && embedded.dataEncoding === "base64" && embedded.data) {
79
+ if (blobUrlByPath.has(resolvedPath)) {
80
+ return blobUrlByPath.get(resolvedPath);
81
+ }
82
+
83
+ const binary = atob(embedded.data);
84
+ const bytes = new Uint8Array(binary.length);
85
+ for (let i = 0; i < binary.length; i += 1) {
86
+ bytes[i] = binary.charCodeAt(i);
87
+ }
88
+
89
+ const mime = embedded.mimeType || "application/octet-stream";
90
+ const blob = new Blob([bytes], { type: mime });
91
+ const blobUrl = URL.createObjectURL(blob);
92
+ blobUrlByPath.set(resolvedPath, blobUrl);
93
+ return blobUrl;
94
+ }
95
+
96
+ return resolvedPath;
97
+ } catch {
98
+ return null;
99
+ }
100
+ };
101
+
102
+ const rewriteResourceUrl = (value, ctx) => {
103
+ try {
104
+ const raw = String(value || "");
105
+ if (!raw) {
106
+ return raw;
107
+ }
108
+
109
+ if (isLocalResource(raw)) {
110
+ return raw;
111
+ }
112
+
113
+ const localPath = findLocalPath(raw);
114
+ if (localPath) {
115
+ return localPath;
116
+ }
117
+
118
+ const fallbackType = ctx && ctx.fallbackType ? String(ctx.fallbackType) : undefined;
119
+ const record = findByUrl(raw);
120
+ if (record) {
121
+ const dataUrl = toDataUrl(record, fallbackType);
122
+ if (dataUrl) {
123
+ return dataUrl;
124
+ }
125
+ }
126
+
127
+ return raw;
128
+ } catch {
129
+ try {
130
+ return String(value || "");
131
+ } catch {
132
+ return value;
133
+ }
134
+ }
135
+ };
136
+
137
+ const defineProp = (obj, key, value) => {
138
+ try {
139
+ Object.defineProperty(obj, key, { value, configurable: true });
140
+ } catch {}
141
+ };
142
+
143
+ const decodeBase64 = (input) => {
144
+ try {
145
+ const binary = atob(input || "");
146
+ const bytes = new Uint8Array(binary.length);
147
+ Array.from(binary).forEach((char, index) => {
148
+ bytes[index] = char.charCodeAt(0);
149
+ });
150
+ return bytes;
151
+ } catch {
152
+ return new Uint8Array();
153
+ }
154
+ };
155
+
156
+ const bytesToBase64 = (bytes) => {
157
+ const binary = Array.from(bytes, (value) => String.fromCharCode(value)).join("");
158
+ return btoa(binary);
159
+ };
160
+
161
+ const textToBase64 = (text) => {
162
+ try {
163
+ const bytes = new TextEncoder().encode(text || "");
164
+ return bytesToBase64(bytes);
165
+ } catch {
166
+ return btoa(text || "");
167
+ }
168
+ };
169
+
170
+ const getContentType = (record) => {
171
+ const headers = record.responseHeaders || {};
172
+ for (const key in headers) {
173
+ if (key.toLowerCase() === "content-type") {
174
+ return headers[key] || "application/octet-stream";
175
+ }
176
+ }
177
+ return "application/octet-stream";
178
+ };
179
+
180
+ const dataUrlCache = new Map();
181
+ const toDataUrl = (record, fallbackType) => {
182
+ if (!record) return "";
183
+ const contentType = getContentType(record) || fallbackType || "application/octet-stream";
184
+ const cacheKey = (record.url || "") + "|" + contentType + "|" + (record.responseEncoding || "") + "|" +
185
+ (record.responseBodyBase64 ? "b64:" + record.responseBodyBase64.length : "txt:" + (record.responseBody ? record.responseBody.length : 0));
186
+ if (dataUrlCache.has(cacheKey)) {
187
+ return dataUrlCache.get(cacheKey);
188
+ }
189
+ if (record.responseEncoding === "base64" && record.responseBodyBase64) {
190
+ const dataUrl = "data:" + contentType + ";base64," + record.responseBodyBase64;
191
+ dataUrlCache.set(cacheKey, dataUrl);
192
+ return dataUrl;
193
+ }
194
+ if (record.responseBody) {
195
+ const dataUrl = "data:" + contentType + ";base64," + textToBase64(record.responseBody);
196
+ dataUrlCache.set(cacheKey, dataUrl);
197
+ return dataUrl;
198
+ }
199
+ const dataUrl = "data:" + (fallbackType || "application/octet-stream") + ",";
200
+ dataUrlCache.set(cacheKey, dataUrl);
201
+ return dataUrl;
202
+ };
203
+
204
+ const responseFromRecord = (record) => {
205
+ const headers = new Headers(record.responseHeaders || {});
206
+ if (record.responseEncoding === "base64" && record.responseBodyBase64) {
207
+ const bytes = decodeBase64(record.responseBodyBase64);
208
+ return new Response(bytes, {
209
+ status: record.status || 200,
210
+ statusText: record.statusText || "OK",
211
+ headers
212
+ });
213
+ }
214
+ const bodyText = record.responseBody || "";
215
+ return new Response(bodyText, {
216
+ status: record.status || 200,
217
+ statusText: record.statusText || "OK",
218
+ headers
219
+ });
220
+ };
221
+ `;
222
+ };
@@ -0,0 +1,3 @@
1
+ export declare const buildReplayInjectedScriptPart3: (options: {
2
+ hackerScripts: string;
3
+ }) => string;
@@ -0,0 +1,9 @@
1
+ export const buildReplayInjectedScriptPart3 = (options) => {
2
+ const { hackerScripts } = options;
3
+ return `
4
+
5
+ ${hackerScripts}
6
+ })();
7
+ </script>
8
+ `;
9
+ };
@@ -1,15 +1,17 @@
1
1
  import { stripHash, stripTrailingSlash } from "@pagepocket/shared";
2
2
  const isLikelyHostname = (value) => {
3
- if (!value)
3
+ if (!value) {
4
4
  return false;
5
- if (value === "localhost")
5
+ }
6
+ if (value === "localhost") {
6
7
  return true;
8
+ }
7
9
  return value.includes(".");
8
10
  };
9
11
  const encodeEmbeddedUrlTailIfPresent = (pathname) => {
10
12
  const raw = String(pathname || "");
11
13
  if (!raw.includes("/http")) {
12
- return null;
14
+ return undefined;
13
15
  }
14
16
  const parts = raw.split("/");
15
17
  for (let i = 0; i < parts.length; i += 1) {
@@ -28,13 +30,14 @@ const encodeEmbeddedUrlTailIfPresent = (pathname) => {
28
30
  const rebuilt = nextParts.join("/") || "/";
29
31
  return rebuilt.startsWith("/") ? rebuilt : "/" + rebuilt;
30
32
  }
31
- return null;
33
+ return undefined;
32
34
  };
33
35
  export const makePathnameVariants = (pathname) => {
34
36
  const variants = new Set();
35
37
  const push = (value) => {
36
- if (!value)
38
+ if (!value) {
37
39
  return;
40
+ }
38
41
  variants.add(value);
39
42
  };
40
43
  push(pathname);
@@ -11,12 +11,12 @@ const canonicalizeHttpUrlForIndex = (url) => {
11
11
  }
12
12
  return url.toString();
13
13
  };
14
- const toUrlOrNull = (value) => {
14
+ const toUrlOrUndefined = (value) => {
15
15
  try {
16
16
  return new URL(value);
17
17
  }
18
18
  catch {
19
- return null;
19
+ return undefined;
20
20
  }
21
21
  };
22
22
  export const buildResourceProxyIndex = (snapshot) => {
@@ -29,7 +29,7 @@ export const buildResourceProxyIndex = (snapshot) => {
29
29
  if (!item || !item.url || !item.path) {
30
30
  continue;
31
31
  }
32
- const parsed = toUrlOrNull(item.url);
32
+ const parsed = toUrlOrUndefined(item.url);
33
33
  if (!parsed) {
34
34
  continue;
35
35
  }
@@ -81,14 +81,14 @@ const uniqByPath = (items) => {
81
81
  };
82
82
  const preferSingle = (items, baseUrl, suffixLength) => {
83
83
  if (items.length <= 1) {
84
- return items[0] ?? null;
84
+ return items[0];
85
85
  }
86
86
  const baseParsed = (() => {
87
87
  try {
88
88
  return new URL(baseUrl);
89
89
  }
90
90
  catch {
91
- return null;
91
+ return undefined;
92
92
  }
93
93
  })();
94
94
  if (baseParsed) {
@@ -103,14 +103,14 @@ const preferSingle = (items, baseUrl, suffixLength) => {
103
103
  // If still ambiguous, only accept when the match key is strong.
104
104
  // We treat very short suffix matches (or basename-only) as too risky.
105
105
  if (suffixLength < 2) {
106
- return null;
106
+ return undefined;
107
107
  }
108
- return null;
108
+ return undefined;
109
109
  };
110
110
  const tryCandidates = (items, baseUrl, suffixLength) => {
111
111
  const unique = uniqByPath(items);
112
112
  if (unique.length === 0) {
113
- return null;
113
+ return undefined;
114
114
  }
115
115
  if (unique.length === 1) {
116
116
  return unique[0];
@@ -131,12 +131,12 @@ export const resolveToLocalPath = (options) => {
131
131
  if (!requestUrl) {
132
132
  return undefined;
133
133
  }
134
- let abs = null;
134
+ let abs;
135
135
  try {
136
136
  abs = new URL(requestUrl, baseUrl);
137
137
  }
138
138
  catch {
139
- abs = null;
139
+ abs = undefined;
140
140
  }
141
141
  if (!abs) {
142
142
  return undefined;
@@ -1,11 +1,12 @@
1
1
  import * as cheerio from "cheerio";
2
+ import type { AnyCheerioElement } from "./cheerio/types.js";
2
3
  export type ResourceReference = {
3
4
  attr: string;
4
- element: any;
5
+ element: AnyCheerioElement;
5
6
  url: string;
6
7
  };
7
8
  export type SrcsetReference = {
8
- element: any;
9
+ element: AnyCheerioElement;
9
10
  value: string;
10
11
  };
11
12
  export declare const toAbsoluteUrl: (baseUrl: string, resourceUrl: string) => string;
package/dist/resources.js CHANGED
@@ -12,7 +12,8 @@ export const extractResourceUrls = (html, baseUrl) => {
12
12
  const urls = [];
13
13
  const collect = (selector, attr) => {
14
14
  $(selector).each((_, element) => {
15
- const value = $(element).attr(attr);
15
+ const $element = $(element);
16
+ const value = $element.attr(attr);
16
17
  if (value) {
17
18
  urls.push({ attr, element });
18
19
  }
@@ -27,13 +28,15 @@ export const extractResourceUrls = (html, baseUrl) => {
27
28
  collect("audio[src]", "src");
28
29
  const srcsetItems = [];
29
30
  $("img[srcset], source[srcset]").each((_, element) => {
30
- const value = $(element).attr("srcset");
31
+ const $element = $(element);
32
+ const value = $element.attr("srcset");
31
33
  if (value) {
32
34
  srcsetItems.push({ element, value });
33
35
  }
34
36
  });
35
37
  const resourceUrls = urls.map(({ attr, element }) => {
36
- const value = $(element).attr(attr) || "";
38
+ const $element = $(element);
39
+ const value = $element.attr(attr) || "";
37
40
  return {
38
41
  attr,
39
42
  element,
@@ -1,3 +1,3 @@
1
- type UrlResolver = (absoluteUrl: string) => string | null;
1
+ type UrlResolver = (absoluteUrl: string) => string | undefined;
2
2
  export declare const rewriteJsText: (source: string, resolve: UrlResolver, baseUrl: string) => Promise<string>;
3
3
  export {};
@@ -1,2 +1,2 @@
1
- import * as cheerio from "cheerio";
2
- export declare const shouldRewriteLinkHref: ($element: cheerio.Cheerio<any>) => boolean;
1
+ import type { AnyCheerio } from "../cheerio/types.js";
2
+ export declare const shouldRewriteLinkHref: ($element: AnyCheerio) => boolean;
@@ -1,3 +1,3 @@
1
- type UrlResolver = (absoluteUrl: string) => string | null;
1
+ type UrlResolver = (absoluteUrl: string) => string | undefined;
2
2
  export declare const rewriteMetaRefresh: (content: string, baseUrl: string, resolve: UrlResolver) => string;
3
3
  export {};
@@ -1,11 +1,13 @@
1
1
  import { resolveUrlValue } from "./url-resolve.js";
2
2
  export const rewriteMetaRefresh = (content, baseUrl, resolve) => {
3
3
  const parts = content.split(";");
4
- if (parts.length < 2)
4
+ if (parts.length < 2) {
5
5
  return content;
6
+ }
6
7
  const urlPartIndex = parts.findIndex((part) => part.trim().toLowerCase().startsWith("url="));
7
- if (urlPartIndex === -1)
8
+ if (urlPartIndex === -1) {
8
9
  return content;
10
+ }
9
11
  const urlPart = parts[urlPartIndex];
10
12
  let rawUrl = urlPart.split("=").slice(1).join("=").trim();
11
13
  if ((rawUrl.startsWith('"') && rawUrl.endsWith('"')) ||
@@ -13,8 +15,9 @@ export const rewriteMetaRefresh = (content, baseUrl, resolve) => {
13
15
  rawUrl = rawUrl.slice(1, -1).trim();
14
16
  }
15
17
  const resolved = resolveUrlValue(rawUrl, baseUrl, resolve);
16
- if (!resolved)
18
+ if (!resolved) {
17
19
  return content;
20
+ }
18
21
  const next = `url=${resolved}`;
19
22
  const nextParts = parts.slice();
20
23
  nextParts[urlPartIndex] = next;
@@ -1,3 +1,3 @@
1
- type UrlResolver = (absoluteUrl: string) => string | null;
1
+ type UrlResolver = (absoluteUrl: string) => string | undefined;
2
2
  export declare const rewriteSrcsetValue: (value: string, baseUrl: string, resolve: UrlResolver) => string;
3
3
  export {};
@@ -16,8 +16,9 @@ const isUnsafeSrcsetValue = (value) => {
16
16
  };
17
17
  const isDescriptorToken = (token) => {
18
18
  const trimmed = token.trim();
19
- if (!trimmed)
19
+ if (!trimmed) {
20
20
  return false;
21
+ }
21
22
  return /^\d+(\.\d+)?x$/i.test(trimmed) || /^\d+w$/i.test(trimmed);
22
23
  };
23
24
  const parseSrcset = (input) => {
@@ -43,8 +44,9 @@ const stringifySrcset = (candidates) => {
43
44
  return candidates
44
45
  .map((c) => {
45
46
  const url = c.url.trim();
46
- if (!c.descriptor)
47
+ if (!c.descriptor) {
47
48
  return url;
49
+ }
48
50
  return `${url} ${c.descriptor.trim()}`;
49
51
  })
50
52
  .filter(Boolean)
@@ -1,3 +1,3 @@
1
- type UrlResolver = (absoluteUrl: string) => string | null;
2
- export declare const resolveUrlValue: (value: string, baseUrl: string, resolve: UrlResolver) => string | null;
1
+ type UrlResolver = (absoluteUrl: string) => string | undefined;
2
+ export declare const resolveUrlValue: (value: string, baseUrl: string, resolve: UrlResolver) => string | undefined;
3
3
  export {};
@@ -1,13 +1,13 @@
1
1
  import { shouldSkipValue } from "./skip.js";
2
2
  export const resolveUrlValue = (value, baseUrl, resolve) => {
3
3
  if (shouldSkipValue(value)) {
4
- return null;
4
+ return undefined;
5
5
  }
6
6
  try {
7
7
  const absolute = new URL(value, baseUrl).toString();
8
8
  return resolve(absolute);
9
9
  }
10
10
  catch {
11
- return null;
11
+ return undefined;
12
12
  }
13
13
  };
@@ -1,6 +1,6 @@
1
1
  import { rewriteJsText } from "./rewrite-links/js-imports.js";
2
2
  import type { ReplaceElementsConfig } from "./types.js";
3
- type UrlResolver = (absoluteUrl: string) => string | null;
3
+ type UrlResolver = (absoluteUrl: string) => string | undefined;
4
4
  export { rewriteJsText };
5
5
  export declare const rewriteEntryHtml: (input: {
6
6
  html: string;
@@ -28,8 +28,9 @@ export const rewriteEntryHtml = async (input) => {
28
28
  const rewriteAttr = (selector, attr) => {
29
29
  $(selector).each((_, element) => {
30
30
  const value = $(element).attr(attr);
31
- if (!value)
31
+ if (!value) {
32
32
  return;
33
+ }
33
34
  const resolved = resolveUrlValue(value, baseUrl, resolve);
34
35
  if (resolved) {
35
36
  $(element).attr(attr, resolved);
@@ -53,8 +54,9 @@ export const rewriteEntryHtml = async (input) => {
53
54
  return;
54
55
  }
55
56
  const value = el.attr("href");
56
- if (!value)
57
+ if (!value) {
57
58
  return;
59
+ }
58
60
  const resolved = resolveUrlValue(value, baseUrl, resolve);
59
61
  if (resolved) {
60
62
  el.attr("href", resolved);
@@ -67,25 +69,29 @@ export const rewriteEntryHtml = async (input) => {
67
69
  rewriteDataAttrs("[data-url]", "data-url");
68
70
  $("img[srcset], source[srcset]").each((_, element) => {
69
71
  const value = $(element).attr("srcset");
70
- if (!value)
72
+ if (!value) {
71
73
  return;
74
+ }
72
75
  const rewritten = rewriteSrcsetValue(value, baseUrl, resolve);
73
76
  $(element).attr("srcset", rewritten);
74
77
  });
75
78
  $("link[imagesrcset]").each((_, element) => {
76
79
  const value = $(element).attr("imagesrcset");
77
- if (!value)
80
+ if (!value) {
78
81
  return;
82
+ }
79
83
  const rewritten = rewriteSrcsetValue(value, baseUrl, resolve);
80
84
  $(element).attr("imagesrcset", rewritten);
81
85
  });
82
86
  $("meta[http-equiv]").each((_, element) => {
83
87
  const httpEquiv = ($(element).attr("http-equiv") || "").toLowerCase();
84
- if (httpEquiv !== "refresh")
88
+ if (httpEquiv !== "refresh") {
85
89
  return;
90
+ }
86
91
  const content = $(element).attr("content");
87
- if (!content)
92
+ if (!content) {
88
93
  return;
94
+ }
89
95
  const rewritten = rewriteMetaRefresh(content, baseUrl, resolve);
90
96
  $(element).attr("content", rewritten);
91
97
  });
@@ -8,7 +8,6 @@ import { responseMimeType } from "./http.js";
8
8
  import { docDirFromUrl, resolveSnapshotPath } from "./path-map.js";
9
9
  import { buildResourcesPathSnapshot } from "./resources-path.js";
10
10
  import { maybeRewriteScript, maybeRewriteStylesheet } from "./rewrite-resource.js";
11
- // NOTE: helpers were extracted into snapshot-builder/* modules.
12
11
  export const buildSnapshot = async (input) => {
13
12
  const warnings = input.warnings;
14
13
  const contentStore = input.capture.contentStore;
@@ -97,7 +96,7 @@ export const buildSnapshot = async (input) => {
97
96
  });
98
97
  }
99
98
  const apiSnapshot = buildApiSnapshot(group.url, input.createdAt, group.apiEntries);
100
- const apiBytes = new TextEncoder().encode(`${JSON.stringify(apiSnapshot, null, 2)}\n`);
99
+ const apiBytes = new TextEncoder().encode(`${JSON.stringify(apiSnapshot, undefined, 2)}\n`);
101
100
  const apiRef = await contentStore.put({ kind: "buffer", data: apiBytes }, { url: apiPath, mimeType: "application/json", sizeHint: apiBytes.byteLength });
102
101
  files.push({
103
102
  path: apiPath,
@@ -109,7 +108,7 @@ export const buildSnapshot = async (input) => {
109
108
  }
110
109
  {
111
110
  const resourcesPath = buildResourcesPathSnapshot(input.createdAt, files);
112
- const bytes = new TextEncoder().encode(`${JSON.stringify(resourcesPath, null, 2)}\n`);
111
+ const bytes = new TextEncoder().encode(`${JSON.stringify(resourcesPath, undefined, 2)}\n`);
113
112
  files.push({
114
113
  path: "/resources_path.json",
115
114
  mimeType: "application/json",
@@ -9,8 +9,9 @@ const buildByRequestId = (events) => {
9
9
  const byId = new Map();
10
10
  const ensure = (requestId) => {
11
11
  const existing = byId.get(requestId);
12
- if (existing)
12
+ if (existing) {
13
13
  return existing;
14
+ }
14
15
  const created = {};
15
16
  byId.set(requestId, created);
16
17
  return created;
@@ -5,7 +5,7 @@ export declare const emitDocumentFile: (input: {
5
5
  entryUrl: string;
6
6
  groupUrl: string;
7
7
  apiPath: string;
8
- resolve: (absoluteUrl: string) => string | null;
8
+ resolve: (absoluteUrl: string) => string | undefined;
9
9
  rewriteEntry: boolean;
10
10
  replaceElements: BuildOptions["replaceElements"];
11
11
  contentStore: BuildOptions["capture"]["contentStore"];
@@ -5,7 +5,7 @@ export const emitDocumentFile = async (input) => {
5
5
  const stream = await input.contentStore.open(input.resource.contentRef);
6
6
  const bytes = await streamToUint8Array(stream);
7
7
  const decoded = decodeUtf8(bytes);
8
- if (decoded === null) {
8
+ if (typeof decoded === "undefined") {
9
9
  return {
10
10
  file: {
11
11
  path: input.path,
@@ -52,8 +52,8 @@ export const groupResources = (input) => {
52
52
  });
53
53
  }
54
54
  const primaryGroup = primaryDoc
55
- ? (groups.get(primaryDoc.request.frameId ?? primaryDoc.request.requestId) ?? null)
56
- : null;
55
+ ? groups.get(primaryDoc.request.frameId ?? primaryDoc.request.requestId)
56
+ : undefined;
57
57
  const groupByUrl = new Map();
58
58
  for (const group of groups.values()) {
59
59
  groupByUrl.set(group.url, group);
@@ -1,3 +1,3 @@
1
1
  export declare const escapePercentForStaticServers: (value: string) => string;
2
2
  export declare const docDirFromUrl: (url: string) => string;
3
- export declare const resolveSnapshotPath: (urlToPath: Map<string, string>, absoluteUrl: string) => string | null;
3
+ export declare const resolveSnapshotPath: (urlToPath: Map<string, string>, absoluteUrl: string) => string | undefined;
@@ -24,7 +24,7 @@ export const docDirFromUrl = (url) => {
24
24
  export const resolveSnapshotPath = (urlToPath, absoluteUrl) => {
25
25
  const resolved = urlToPath.get(absoluteUrl);
26
26
  if (!resolved) {
27
- return null;
27
+ return undefined;
28
28
  }
29
29
  // Defensive: resolved should be a path, but avoid breaking any unexpected
30
30
  // absolute URLs.
@@ -29,14 +29,18 @@ export const buildResourcesPathSnapshot = (createdAt, files) => {
29
29
  });
30
30
  }
31
31
  items.sort((left, right) => {
32
- if (left.path < right.path)
32
+ if (left.path < right.path) {
33
33
  return -1;
34
- if (left.path > right.path)
34
+ }
35
+ if (left.path > right.path) {
35
36
  return 1;
36
- if (left.url < right.url)
37
+ }
38
+ if (left.url < right.url) {
37
39
  return -1;
38
- if (left.url > right.url)
40
+ }
41
+ if (left.url > right.url) {
39
42
  return 1;
43
+ }
40
44
  return 0;
41
45
  });
42
46
  return {