@pagepocket/lib 0.8.6 → 0.9.1

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,5 @@
1
+ import type { Cheerio, CheerioAPI } from "cheerio";
2
+ import type { AnyNode } from "domhandler";
3
+ export type AnyCheerio = Cheerio<AnyNode>;
4
+ export type AnyCheerioApi = CheerioAPI;
5
+ export type AnyCheerioElement = AnyNode;
@@ -0,0 +1 @@
1
+ export {};
@@ -17,7 +17,8 @@ export const networkIdle = (ms, checkInterval = 100) => ({
17
17
  }
18
18
  });
19
19
  export const normalizeCompletion = (completion) => {
20
- if (!completion)
20
+ if (!completion) {
21
21
  return [];
22
+ }
22
23
  return Array.isArray(completion) ? completion : [completion];
23
24
  };
@@ -1,3 +1,5 @@
1
+ import { rm } from "node:fs/promises";
2
+ import { isAbsolute, resolve } from "node:path";
1
3
  import { readBinary, remove, write } from "@pagepocket/uni-fs";
2
4
  import { streamFromUint8Array } from "../utils/streams.js";
3
5
  import { hashString, toUint8Array } from "../utils.js";
@@ -46,8 +48,6 @@ export class HybridContentStore {
46
48
  if (!this.baseDir || this.baseDir === "/" || this.baseDir === ".") {
47
49
  return;
48
50
  }
49
- const { resolve, isAbsolute } = await import("node:path");
50
- const { rm } = await import("node:fs/promises");
51
51
  const target = isAbsolute(this.baseDir) ? this.baseDir : resolve(this.baseDir);
52
52
  await rm(target, { recursive: true, force: true });
53
53
  }
@@ -1,4 +1,4 @@
1
- export type CssUrlResolver = (absoluteUrl: string) => Promise<string | null> | string | null;
1
+ export type CssUrlResolver = (absoluteUrl: string) => Promise<string | undefined> | string | undefined;
2
2
  type RewriteCssInput = {
3
3
  cssText: string;
4
4
  cssUrl: string;
@@ -26,7 +26,7 @@ export const rewriteCssText = async (input) => {
26
26
  return new URL(rawUrl, cssUrl).toString();
27
27
  }
28
28
  catch {
29
- return null;
29
+ return undefined;
30
30
  }
31
31
  })();
32
32
  if (absolute) {
@@ -53,7 +53,7 @@ export const rewriteCssText = async (input) => {
53
53
  return new URL(rawUrl, cssUrl).toString();
54
54
  }
55
55
  catch {
56
- return null;
56
+ return undefined;
57
57
  }
58
58
  })();
59
59
  if (absolute) {
@@ -0,0 +1 @@
1
+ export declare const replayDomRewriteScriptPart1 = "\n const transparentGif = \"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==\";\n const emptyScript = \"data:text/javascript,/*pagepocket-missing*/\";\n const emptyStyle = \"data:text/css,/*pagepocket-missing*/\";\n\n let readyResolved = false;\n if (ready && typeof ready.then === \"function\") {\n ready.then(() => {\n readyResolved = true;\n });\n } else {\n readyResolved = true;\n }\n\n const onReady = (callback) => {\n if (readyResolved) {\n callback();\n return;\n }\n if (ready && typeof ready.then === \"function\") {\n ready.then(callback);\n } else {\n callback();\n }\n };\n\n const rewritten = new WeakMap();\n\n const shouldRewriteAttr = (element, attr) => {\n try {\n const tag = String((element && element.tagName) || \"\").toLowerCase();\n const name = String(attr || \"\").toLowerCase();\n\n if (!tag || !name) {\n return false;\n }\n\n if (tag === \"a\" && name === \"href\") {\n return false;\n }\n\n if (name === \"src\") {\n return (\n tag === \"img\" ||\n tag === \"source\" ||\n tag === \"video\" ||\n tag === \"audio\" ||\n tag === \"script\" ||\n tag === \"iframe\" ||\n tag === \"object\" ||\n tag === \"embed\" ||\n tag === \"track\"\n );\n }\n\n if (name === \"href\") {\n if (tag !== \"link\") {\n return false;\n }\n\n const rel = String((element.getAttribute && element.getAttribute(\"rel\")) || \"\").toLowerCase();\n if (rel.includes(\"stylesheet\") || rel.includes(\"icon\")) {\n return true;\n }\n\n if (rel.includes(\"preload\") || rel.includes(\"prefetch\") || rel.includes(\"modulepreload\")) {\n return true;\n }\n\n return false;\n }\n\n if (name === \"srcset\") {\n return tag === \"img\" || tag === \"source\";\n }\n\n return false;\n } catch {\n return false;\n }\n };\n\n const rewriteSrcset = (value) => {\n if (!value) return value;\n\n try {\n const trimmed = String(value || \"\").trim();\n const hasFetchTransform = trimmed.includes(\"/image/fetch/\");\n const hasEncodedUrlTail = trimmed.includes(\"https%3A%2F%2F\");\n const hasCommaTokens =\n trimmed.includes(\",w_\") ||\n trimmed.includes(\", w_\") ||\n trimmed.includes(\",h_\") ||\n trimmed.includes(\", h_\") ||\n trimmed.includes(\",c_\") ||\n trimmed.includes(\", c_\");\n\n if (hasFetchTransform && hasEncodedUrlTail && hasCommaTokens) {\n return \"\";\n }\n } catch {}\n\n return value\n .split(\",\")\n .map((part) => {\n const trimmed = part.trim();\n if (!trimmed) return trimmed;\n const pieces = trimmed.split(/\\s+/, 2);\n const url = pieces[0];\n const descriptor = pieces[1];\n if (isLocalResource(url)) return trimmed;\n const localPath = findLocalPath(url);\n if (localPath) {\n return descriptor ? localPath + \" \" + descriptor : localPath;\n }\n return trimmed;\n })\n .join(\",\");\n };\n\n const rewriteElement = (element) => {\n if (!element || !element.getAttribute) return;\n if (!readyResolved) {\n onReady(() => rewriteElement(element));\n return;\n }\n const prev = rewritten.get(element);\n const currentSrc = element.getAttribute(\"src\");\n const currentHref = element.getAttribute(\"href\");\n const currentSrcset = element.getAttribute(\"srcset\");\n if (\n prev &&\n prev.src === currentSrc &&\n prev.href === currentHref &&\n prev.srcset === currentSrcset\n ) {\n return;\n }\n const tag = (element.tagName || \"\").toLowerCase();\n if (\n tag === \"img\" ||\n tag === \"source\" ||\n tag === \"video\" ||\n tag === \"audio\" ||\n tag === \"script\" ||\n tag === \"iframe\" ||\n tag === \"object\" ||\n tag === \"embed\" ||\n tag === \"track\"\n ) {\n const src = currentSrc;\n if (src && !src.startsWith(\"data:\") && !src.startsWith(\"blob:\")) {\n const next = rewriteResourceUrl(src, { kind: \"attr\", tag, attr: \"src\" });\n if (next && next !== src) {\n element.setAttribute(\"src\", next);\n return;\n }\n }\n }\n\n if (tag === \"link\") {\n const href = currentHref;\n const rel = (element.getAttribute(\"rel\") || \"\").toLowerCase();\n if (href && !href.startsWith(\"data:\") && !href.startsWith(\"blob:\")) {\n if (\n rel.includes(\"stylesheet\") ||\n rel.includes(\"icon\") ||\n rel.includes(\"preload\") ||\n rel.includes(\"prefetch\") ||\n rel.includes(\"modulepreload\")\n ) {\n const as = (element.getAttribute(\"as\") || \"\").toLowerCase();\n const fallbackType =\n rel.includes(\"stylesheet\") || (rel.includes(\"preload\") && as === \"style\")\n ? \"text/css\"\n : undefined;\n const next = rewriteResourceUrl(href, {\n kind: \"attr\",\n tag,\n attr: \"href\",\n fallbackType\n });\n if (next && next !== href) {\n element.setAttribute(\"href\", next);\n return;\n }\n }\n }\n }\n\n const srcset = currentSrcset;\n if (srcset) {\n element.setAttribute(\"srcset\", rewriteSrcset(srcset));\n }\n\n rewritten.set(element, {\n src: element.getAttribute(\"src\"),\n href: element.getAttribute(\"href\"),\n srcset: element.getAttribute(\"srcset\")\n });\n };\n";
@@ -0,0 +1,202 @@
1
+ export const replayDomRewriteScriptPart1 = `
2
+ const transparentGif = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
3
+ const emptyScript = "data:text/javascript,/*pagepocket-missing*/";
4
+ const emptyStyle = "data:text/css,/*pagepocket-missing*/";
5
+
6
+ let readyResolved = false;
7
+ if (ready && typeof ready.then === "function") {
8
+ ready.then(() => {
9
+ readyResolved = true;
10
+ });
11
+ } else {
12
+ readyResolved = true;
13
+ }
14
+
15
+ const onReady = (callback) => {
16
+ if (readyResolved) {
17
+ callback();
18
+ return;
19
+ }
20
+ if (ready && typeof ready.then === "function") {
21
+ ready.then(callback);
22
+ } else {
23
+ callback();
24
+ }
25
+ };
26
+
27
+ const rewritten = new WeakMap();
28
+
29
+ const shouldRewriteAttr = (element, attr) => {
30
+ try {
31
+ const tag = String((element && element.tagName) || "").toLowerCase();
32
+ const name = String(attr || "").toLowerCase();
33
+
34
+ if (!tag || !name) {
35
+ return false;
36
+ }
37
+
38
+ if (tag === "a" && name === "href") {
39
+ return false;
40
+ }
41
+
42
+ if (name === "src") {
43
+ return (
44
+ tag === "img" ||
45
+ tag === "source" ||
46
+ tag === "video" ||
47
+ tag === "audio" ||
48
+ tag === "script" ||
49
+ tag === "iframe" ||
50
+ tag === "object" ||
51
+ tag === "embed" ||
52
+ tag === "track"
53
+ );
54
+ }
55
+
56
+ if (name === "href") {
57
+ if (tag !== "link") {
58
+ return false;
59
+ }
60
+
61
+ const rel = String((element.getAttribute && element.getAttribute("rel")) || "").toLowerCase();
62
+ if (rel.includes("stylesheet") || rel.includes("icon")) {
63
+ return true;
64
+ }
65
+
66
+ if (rel.includes("preload") || rel.includes("prefetch") || rel.includes("modulepreload")) {
67
+ return true;
68
+ }
69
+
70
+ return false;
71
+ }
72
+
73
+ if (name === "srcset") {
74
+ return tag === "img" || tag === "source";
75
+ }
76
+
77
+ return false;
78
+ } catch {
79
+ return false;
80
+ }
81
+ };
82
+
83
+ const rewriteSrcset = (value) => {
84
+ if (!value) return value;
85
+
86
+ try {
87
+ const trimmed = String(value || "").trim();
88
+ const hasFetchTransform = trimmed.includes("/image/fetch/");
89
+ const hasEncodedUrlTail = trimmed.includes("https%3A%2F%2F");
90
+ const hasCommaTokens =
91
+ trimmed.includes(",w_") ||
92
+ trimmed.includes(", w_") ||
93
+ trimmed.includes(",h_") ||
94
+ trimmed.includes(", h_") ||
95
+ trimmed.includes(",c_") ||
96
+ trimmed.includes(", c_");
97
+
98
+ if (hasFetchTransform && hasEncodedUrlTail && hasCommaTokens) {
99
+ return "";
100
+ }
101
+ } catch {}
102
+
103
+ return value
104
+ .split(",")
105
+ .map((part) => {
106
+ const trimmed = part.trim();
107
+ if (!trimmed) return trimmed;
108
+ const pieces = trimmed.split(/\\s+/, 2);
109
+ const url = pieces[0];
110
+ const descriptor = pieces[1];
111
+ if (isLocalResource(url)) return trimmed;
112
+ const localPath = findLocalPath(url);
113
+ if (localPath) {
114
+ return descriptor ? localPath + " " + descriptor : localPath;
115
+ }
116
+ return trimmed;
117
+ })
118
+ .join(",");
119
+ };
120
+
121
+ const rewriteElement = (element) => {
122
+ if (!element || !element.getAttribute) return;
123
+ if (!readyResolved) {
124
+ onReady(() => rewriteElement(element));
125
+ return;
126
+ }
127
+ const prev = rewritten.get(element);
128
+ const currentSrc = element.getAttribute("src");
129
+ const currentHref = element.getAttribute("href");
130
+ const currentSrcset = element.getAttribute("srcset");
131
+ if (
132
+ prev &&
133
+ prev.src === currentSrc &&
134
+ prev.href === currentHref &&
135
+ prev.srcset === currentSrcset
136
+ ) {
137
+ return;
138
+ }
139
+ const tag = (element.tagName || "").toLowerCase();
140
+ if (
141
+ tag === "img" ||
142
+ tag === "source" ||
143
+ tag === "video" ||
144
+ tag === "audio" ||
145
+ tag === "script" ||
146
+ tag === "iframe" ||
147
+ tag === "object" ||
148
+ tag === "embed" ||
149
+ tag === "track"
150
+ ) {
151
+ const src = currentSrc;
152
+ if (src && !src.startsWith("data:") && !src.startsWith("blob:")) {
153
+ const next = rewriteResourceUrl(src, { kind: "attr", tag, attr: "src" });
154
+ if (next && next !== src) {
155
+ element.setAttribute("src", next);
156
+ return;
157
+ }
158
+ }
159
+ }
160
+
161
+ if (tag === "link") {
162
+ const href = currentHref;
163
+ const rel = (element.getAttribute("rel") || "").toLowerCase();
164
+ if (href && !href.startsWith("data:") && !href.startsWith("blob:")) {
165
+ if (
166
+ rel.includes("stylesheet") ||
167
+ rel.includes("icon") ||
168
+ rel.includes("preload") ||
169
+ rel.includes("prefetch") ||
170
+ rel.includes("modulepreload")
171
+ ) {
172
+ const as = (element.getAttribute("as") || "").toLowerCase();
173
+ const fallbackType =
174
+ rel.includes("stylesheet") || (rel.includes("preload") && as === "style")
175
+ ? "text/css"
176
+ : undefined;
177
+ const next = rewriteResourceUrl(href, {
178
+ kind: "attr",
179
+ tag,
180
+ attr: "href",
181
+ fallbackType
182
+ });
183
+ if (next && next !== href) {
184
+ element.setAttribute("href", next);
185
+ return;
186
+ }
187
+ }
188
+ }
189
+ }
190
+
191
+ const srcset = currentSrcset;
192
+ if (srcset) {
193
+ element.setAttribute("srcset", rewriteSrcset(srcset));
194
+ }
195
+
196
+ rewritten.set(element, {
197
+ src: element.getAttribute("src"),
198
+ href: element.getAttribute("href"),
199
+ srcset: element.getAttribute("srcset")
200
+ });
201
+ };
202
+ `;
@@ -0,0 +1 @@
1
+ export declare const replayDomRewriteScriptPart2 = "\n const originalSetAttribute = Element.prototype.setAttribute;\n Element.prototype.setAttribute = function(name, value) {\n const attr = String(name).toLowerCase();\n if (attr === \"src\" || attr === \"href\" || attr === \"srcset\") {\n if (!readyResolved) {\n const pendingValue = String(value);\n onReady(() => originalSetAttribute.call(this, name, pendingValue));\n return;\n }\n if (attr === \"srcset\") {\n const rewritten = rewriteSrcset(String(value));\n return originalSetAttribute.call(this, name, rewritten);\n }\n\n if (!shouldRewriteAttr(this, attr)) {\n return originalSetAttribute.call(this, name, value);\n }\n\n const tag = (this.tagName || \"\").toLowerCase();\n const rel = (this.getAttribute && this.getAttribute(\"rel\")) || \"\";\n const relLower = rel.toLowerCase();\n const as = String((this.getAttribute && this.getAttribute(\"as\")) || \"\").toLowerCase();\n const fallbackType =\n attr === \"href\" && (relLower.includes(\"stylesheet\") || (relLower.includes(\"preload\") && as === \"style\"))\n ? \"text/css\"\n : undefined;\n\n const next = rewriteResourceUrl(String(value), {\n kind: \"setAttribute\",\n tag,\n attr,\n fallbackType\n });\n\n return originalSetAttribute.call(this, name, next);\n }\n return originalSetAttribute.call(this, name, value);\n };\n\n const patchProperty = (proto, prop, handler) => {\n try {\n const desc = Object.getOwnPropertyDescriptor(proto, prop);\n if (!desc || !desc.set) return;\n Object.defineProperty(proto, prop, {\n configurable: true,\n get: desc.get,\n set: function(value) {\n return handler.call(this, value, desc.set);\n }\n });\n } catch {}\n };\n\n patchProperty(HTMLImageElement.prototype, \"src\", function(value, setter) {\n const rawValue = String(value);\n if (!readyResolved) {\n onReady(() => {\n const next = rewriteResourceUrl(rawValue, { kind: \"setter\", tag: \"img\", attr: \"src\" });\n setter.call(this, next);\n });\n return;\n }\n const next = rewriteResourceUrl(rawValue, { kind: \"setter\", tag: \"img\", attr: \"src\" });\n setter.call(this, next);\n });\n\n patchProperty(HTMLScriptElement.prototype, \"src\", function(value, setter) {\n const rawValue = String(value);\n if (!readyResolved) {\n onReady(() => {\n const next = rewriteResourceUrl(rawValue, { kind: \"setter\", tag: \"script\", attr: \"src\" });\n setter.call(this, next);\n });\n return;\n }\n const next = rewriteResourceUrl(rawValue, { kind: \"setter\", tag: \"script\", attr: \"src\" });\n setter.call(this, next);\n });\n\n patchProperty(HTMLLinkElement.prototype, \"href\", function(value, setter) {\n const rawValue = String(value);\n const rel = (this.getAttribute && this.getAttribute(\"rel\")) || \"\";\n const relLower = rel.toLowerCase();\n const as = String((this.getAttribute && this.getAttribute(\"as\")) || \"\").toLowerCase();\n if (!readyResolved) {\n onReady(() => {\n if (\n !relLower.includes(\"stylesheet\") &&\n !relLower.includes(\"icon\") &&\n !relLower.includes(\"preload\") &&\n !relLower.includes(\"prefetch\") &&\n !relLower.includes(\"modulepreload\")\n ) {\n setter.call(this, rawValue);\n return;\n }\n const fallbackType =\n relLower.includes(\"stylesheet\") || (relLower.includes(\"preload\") && as === \"style\")\n ? \"text/css\"\n : undefined;\n const next = rewriteResourceUrl(rawValue, {\n kind: \"setter\",\n tag: \"link\",\n attr: \"href\",\n fallbackType\n });\n setter.call(this, next);\n });\n return;\n }\n if (\n !relLower.includes(\"stylesheet\") &&\n !relLower.includes(\"icon\") &&\n !relLower.includes(\"preload\") &&\n !relLower.includes(\"prefetch\") &&\n !relLower.includes(\"modulepreload\")\n ) {\n setter.call(this, rawValue);\n return;\n }\n const fallbackType =\n relLower.includes(\"stylesheet\") || (relLower.includes(\"preload\") && as === \"style\")\n ? \"text/css\"\n : undefined;\n const next = rewriteResourceUrl(rawValue, {\n kind: \"setter\",\n tag: \"link\",\n attr: \"href\",\n fallbackType\n });\n setter.call(this, next);\n });\n\n patchProperty(HTMLImageElement.prototype, \"srcset\", function(value, setter) {\n const rawValue = String(value);\n if (!readyResolved) {\n onReady(() => {\n const next = rewriteSrcset(rawValue);\n setter.call(this, next);\n });\n return;\n }\n const next = rewriteSrcset(rawValue);\n setter.call(this, next);\n });\n\n const observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n if (mutation.type === \"attributes\" && mutation.target) {\n rewriteElement(mutation.target);\n }\n if (mutation.type === \"childList\") {\n mutation.addedNodes.forEach((node) => {\n if (node && node.nodeType === 1) {\n rewriteElement(node);\n }\n });\n }\n }\n });\n\n observer.observe(document.documentElement, {\n attributes: true,\n childList: true,\n subtree: true,\n attributeFilter: [\"src\", \"href\", \"srcset\", \"rel\", \"as\"]\n });\n\n document\n .querySelectorAll(\"img,source,video,audio,script,link,iframe\")\n .forEach((el) => rewriteElement(el));\n";
@@ -0,0 +1,173 @@
1
+ export const replayDomRewriteScriptPart2 = `
2
+ const originalSetAttribute = Element.prototype.setAttribute;
3
+ Element.prototype.setAttribute = function(name, value) {
4
+ const attr = String(name).toLowerCase();
5
+ if (attr === "src" || attr === "href" || attr === "srcset") {
6
+ if (!readyResolved) {
7
+ const pendingValue = String(value);
8
+ onReady(() => originalSetAttribute.call(this, name, pendingValue));
9
+ return;
10
+ }
11
+ if (attr === "srcset") {
12
+ const rewritten = rewriteSrcset(String(value));
13
+ return originalSetAttribute.call(this, name, rewritten);
14
+ }
15
+
16
+ if (!shouldRewriteAttr(this, attr)) {
17
+ return originalSetAttribute.call(this, name, value);
18
+ }
19
+
20
+ const tag = (this.tagName || "").toLowerCase();
21
+ const rel = (this.getAttribute && this.getAttribute("rel")) || "";
22
+ const relLower = rel.toLowerCase();
23
+ const as = String((this.getAttribute && this.getAttribute("as")) || "").toLowerCase();
24
+ const fallbackType =
25
+ attr === "href" && (relLower.includes("stylesheet") || (relLower.includes("preload") && as === "style"))
26
+ ? "text/css"
27
+ : undefined;
28
+
29
+ const next = rewriteResourceUrl(String(value), {
30
+ kind: "setAttribute",
31
+ tag,
32
+ attr,
33
+ fallbackType
34
+ });
35
+
36
+ return originalSetAttribute.call(this, name, next);
37
+ }
38
+ return originalSetAttribute.call(this, name, value);
39
+ };
40
+
41
+ const patchProperty = (proto, prop, handler) => {
42
+ try {
43
+ const desc = Object.getOwnPropertyDescriptor(proto, prop);
44
+ if (!desc || !desc.set) return;
45
+ Object.defineProperty(proto, prop, {
46
+ configurable: true,
47
+ get: desc.get,
48
+ set: function(value) {
49
+ return handler.call(this, value, desc.set);
50
+ }
51
+ });
52
+ } catch {}
53
+ };
54
+
55
+ patchProperty(HTMLImageElement.prototype, "src", function(value, setter) {
56
+ const rawValue = String(value);
57
+ if (!readyResolved) {
58
+ onReady(() => {
59
+ const next = rewriteResourceUrl(rawValue, { kind: "setter", tag: "img", attr: "src" });
60
+ setter.call(this, next);
61
+ });
62
+ return;
63
+ }
64
+ const next = rewriteResourceUrl(rawValue, { kind: "setter", tag: "img", attr: "src" });
65
+ setter.call(this, next);
66
+ });
67
+
68
+ patchProperty(HTMLScriptElement.prototype, "src", function(value, setter) {
69
+ const rawValue = String(value);
70
+ if (!readyResolved) {
71
+ onReady(() => {
72
+ const next = rewriteResourceUrl(rawValue, { kind: "setter", tag: "script", attr: "src" });
73
+ setter.call(this, next);
74
+ });
75
+ return;
76
+ }
77
+ const next = rewriteResourceUrl(rawValue, { kind: "setter", tag: "script", attr: "src" });
78
+ setter.call(this, next);
79
+ });
80
+
81
+ patchProperty(HTMLLinkElement.prototype, "href", function(value, setter) {
82
+ const rawValue = String(value);
83
+ const rel = (this.getAttribute && this.getAttribute("rel")) || "";
84
+ const relLower = rel.toLowerCase();
85
+ const as = String((this.getAttribute && this.getAttribute("as")) || "").toLowerCase();
86
+ if (!readyResolved) {
87
+ onReady(() => {
88
+ if (
89
+ !relLower.includes("stylesheet") &&
90
+ !relLower.includes("icon") &&
91
+ !relLower.includes("preload") &&
92
+ !relLower.includes("prefetch") &&
93
+ !relLower.includes("modulepreload")
94
+ ) {
95
+ setter.call(this, rawValue);
96
+ return;
97
+ }
98
+ const fallbackType =
99
+ relLower.includes("stylesheet") || (relLower.includes("preload") && as === "style")
100
+ ? "text/css"
101
+ : undefined;
102
+ const next = rewriteResourceUrl(rawValue, {
103
+ kind: "setter",
104
+ tag: "link",
105
+ attr: "href",
106
+ fallbackType
107
+ });
108
+ setter.call(this, next);
109
+ });
110
+ return;
111
+ }
112
+ if (
113
+ !relLower.includes("stylesheet") &&
114
+ !relLower.includes("icon") &&
115
+ !relLower.includes("preload") &&
116
+ !relLower.includes("prefetch") &&
117
+ !relLower.includes("modulepreload")
118
+ ) {
119
+ setter.call(this, rawValue);
120
+ return;
121
+ }
122
+ const fallbackType =
123
+ relLower.includes("stylesheet") || (relLower.includes("preload") && as === "style")
124
+ ? "text/css"
125
+ : undefined;
126
+ const next = rewriteResourceUrl(rawValue, {
127
+ kind: "setter",
128
+ tag: "link",
129
+ attr: "href",
130
+ fallbackType
131
+ });
132
+ setter.call(this, next);
133
+ });
134
+
135
+ patchProperty(HTMLImageElement.prototype, "srcset", function(value, setter) {
136
+ const rawValue = String(value);
137
+ if (!readyResolved) {
138
+ onReady(() => {
139
+ const next = rewriteSrcset(rawValue);
140
+ setter.call(this, next);
141
+ });
142
+ return;
143
+ }
144
+ const next = rewriteSrcset(rawValue);
145
+ setter.call(this, next);
146
+ });
147
+
148
+ const observer = new MutationObserver((mutations) => {
149
+ for (const mutation of mutations) {
150
+ if (mutation.type === "attributes" && mutation.target) {
151
+ rewriteElement(mutation.target);
152
+ }
153
+ if (mutation.type === "childList") {
154
+ mutation.addedNodes.forEach((node) => {
155
+ if (node && node.nodeType === 1) {
156
+ rewriteElement(node);
157
+ }
158
+ });
159
+ }
160
+ }
161
+ });
162
+
163
+ observer.observe(document.documentElement, {
164
+ attributes: true,
165
+ childList: true,
166
+ subtree: true,
167
+ attributeFilter: ["src", "href", "srcset", "rel", "as"]
168
+ });
169
+
170
+ document
171
+ .querySelectorAll("img,source,video,audio,script,link,iframe")
172
+ .forEach((el) => rewriteElement(el));
173
+ `;