@pagepocket/lib 0.5.1 → 0.6.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.
- package/README.md +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/network-store.d.ts +2 -1
- package/dist/pagepocket.d.ts +2 -1
- package/dist/pagepocket.js +3 -5
- package/dist/path-resolver.d.ts +2 -1
- package/dist/path-resolver.js +1 -3
- package/dist/resource-filter.js +1 -8
- package/dist/snapshot-builder.d.ts +1 -1
- package/dist/snapshot-builder.js +9 -5
- package/dist/types.d.ts +2 -81
- package/dist/utils.js +1 -1
- package/package.json +7 -3
- package/dist/download-resources.d.ts +0 -25
- package/dist/download-resources.js +0 -160
- package/dist/network-records.d.ts +0 -4
- package/dist/network-records.js +0 -83
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ import { PagePocket } from "@pagepocket/lib";
|
|
|
17
17
|
import { CdpAdapter } from "@pagepocket/cdp-adapter";
|
|
18
18
|
|
|
19
19
|
const interceptor = new CdpAdapter();
|
|
20
|
-
const snapshot = await PagePocket.
|
|
20
|
+
const snapshot = await PagePocket.fromTarget({ kind: "cdp-tab", tabId: 123 }).capture({
|
|
21
21
|
interceptor
|
|
22
22
|
});
|
|
23
23
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { PagePocket } from "./pagepocket";
|
|
2
|
-
export type { ApiRecord, ApiSnapshot, CaptureOptions, CompletionContext, CompletionStrategy, ContentRef, ContentStore, ContentStoreHandle,
|
|
2
|
+
export type { ApiRecord, ApiSnapshot, CaptureOptions, CompletionContext, CompletionStrategy, ContentRef, ContentStore, ContentStoreHandle, NetworkRequestEvent, NetworkRequestFailedEvent, NetworkResponseEvent, PagePocketOptions, PageSnapshot, PathResolver, ResourceFilter, SnapshotFile, WriteFSOptions, WriteResult, ZipOptions } from "./types";
|
|
3
3
|
export { HybridContentStore } from "./content-store";
|
|
4
4
|
export { createDefaultPathResolver, withPrefixPathResolver } from "./path-resolver";
|
|
5
5
|
export { createDefaultResourceFilter } from "./resource-filter";
|
package/dist/network-store.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { NetworkEvent, NetworkRequestEvent, NetworkRequestFailedEvent, NetworkResponseEvent } from "@pagepocket/interceptor";
|
|
2
|
+
import type { ApiRecord, ContentRef, ContentStore, ResourceFilter } from "./types";
|
|
2
3
|
type Limits = {
|
|
3
4
|
maxTotalBytes?: number;
|
|
4
5
|
maxSingleResourceBytes?: number;
|
package/dist/pagepocket.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { InterceptTarget } from "@pagepocket/interceptor";
|
|
2
|
+
import type { CaptureOptions, PagePocketOptions, PageSnapshot } from "./types";
|
|
2
3
|
export declare class PagePocket {
|
|
3
4
|
private target;
|
|
4
5
|
private options;
|
package/dist/pagepocket.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PagePocket = void 0;
|
|
4
|
-
const content_store_1 = require("./content-store");
|
|
5
4
|
const completion_1 = require("./completion");
|
|
5
|
+
const content_store_1 = require("./content-store");
|
|
6
|
+
const network_store_1 = require("./network-store");
|
|
6
7
|
const path_resolver_1 = require("./path-resolver");
|
|
7
8
|
const resource_filter_1 = require("./resource-filter");
|
|
8
9
|
const snapshot_builder_1 = require("./snapshot-builder");
|
|
9
|
-
const network_store_1 = require("./network-store");
|
|
10
10
|
class PagePocket {
|
|
11
11
|
constructor(target, options) {
|
|
12
12
|
this.target = target;
|
|
@@ -29,9 +29,7 @@ class PagePocket {
|
|
|
29
29
|
const rewriteCSS = options?.rewriteCSS ?? true;
|
|
30
30
|
const limits = options?.limits;
|
|
31
31
|
const completionStrategies = (0, completion_1.normalizeCompletion)(options?.completion);
|
|
32
|
-
const completion = completionStrategies.length > 0
|
|
33
|
-
? completionStrategies
|
|
34
|
-
: [(0, completion_1.networkIdle)(1000), (0, completion_1.timeout)(5000)];
|
|
32
|
+
const completion = completionStrategies.length > 0 ? completionStrategies : [(0, completion_1.networkIdle)(1000), (0, completion_1.timeout)(5000)];
|
|
35
33
|
const store = new network_store_1.NetworkStore({
|
|
36
34
|
contentStore,
|
|
37
35
|
filter,
|
package/dist/path-resolver.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ResourceType } from "@pagepocket/interceptor";
|
|
2
|
+
import type { PathResolver } from "./types";
|
|
2
3
|
export declare const createDefaultPathResolver: () => PathResolver;
|
|
3
4
|
export declare const resolveCrossOrigin: (url: string, entryUrl: string) => boolean;
|
|
4
5
|
export declare const withPrefixPathResolver: (resolver: PathResolver, prefix: string) => PathResolver;
|
package/dist/path-resolver.js
CHANGED
|
@@ -43,9 +43,7 @@ const createDefaultPathResolver = () => {
|
|
|
43
43
|
const pathname = normalizePathname(parsed?.pathname || "/");
|
|
44
44
|
const queryHash = `${parsed?.search || ""}${parsed?.hash || ""}`;
|
|
45
45
|
const suffix = queryHash ? `__ppq_${(0, utils_1.hashString)(queryHash)}` : "";
|
|
46
|
-
const basePath = input.isCrossOrigin
|
|
47
|
-
? `/external_resources${pathname}`
|
|
48
|
-
: `${pathname}`;
|
|
46
|
+
const basePath = input.isCrossOrigin ? `/external_resources${pathname}` : `${pathname}`;
|
|
49
47
|
let resolvedPath = suffix ? withSuffix(basePath, suffix) : basePath;
|
|
50
48
|
const collisionKey = resolvedPath;
|
|
51
49
|
const existingUrl = usedPaths.get(collisionKey);
|
package/dist/resource-filter.js
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createDefaultResourceFilter = void 0;
|
|
4
|
-
const DEFAULT_ALLOWED = new Set([
|
|
5
|
-
"document",
|
|
6
|
-
"stylesheet",
|
|
7
|
-
"script",
|
|
8
|
-
"image",
|
|
9
|
-
"font",
|
|
10
|
-
"media"
|
|
11
|
-
]);
|
|
4
|
+
const DEFAULT_ALLOWED = new Set(["document", "stylesheet", "script", "image", "font", "media"]);
|
|
12
5
|
const isSkippableUrl = (url) => url.startsWith("data:") ||
|
|
13
6
|
url.startsWith("blob:") ||
|
|
14
7
|
url.startsWith("mailto:") ||
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ContentStore, PageSnapshot, PathResolver } from "./types";
|
|
2
1
|
import type { ApiEntry, StoredResource } from "./network-store";
|
|
2
|
+
import type { ContentStore, PageSnapshot, PathResolver } from "./types";
|
|
3
3
|
type BuildOptions = {
|
|
4
4
|
entryUrl: string;
|
|
5
5
|
createdAt: number;
|
package/dist/snapshot-builder.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.buildSnapshot = void 0;
|
|
4
|
-
const path_resolver_1 = require("./path-resolver");
|
|
5
|
-
const snapshot_1 = require("./snapshot");
|
|
6
4
|
const css_rewrite_1 = require("./css-rewrite");
|
|
5
|
+
const path_resolver_1 = require("./path-resolver");
|
|
7
6
|
const rewrite_links_1 = require("./rewrite-links");
|
|
7
|
+
const snapshot_1 = require("./snapshot");
|
|
8
8
|
const utils_1 = require("./utils");
|
|
9
9
|
const streamToUint8Array = async (stream) => {
|
|
10
10
|
const reader = stream.getReader();
|
|
@@ -80,7 +80,7 @@ const groupResources = (input) => {
|
|
|
80
80
|
});
|
|
81
81
|
}
|
|
82
82
|
const primaryGroup = primaryDoc
|
|
83
|
-
? groups.get(primaryDoc.request.frameId ?? primaryDoc.request.requestId) ?? null
|
|
83
|
+
? (groups.get(primaryDoc.request.frameId ?? primaryDoc.request.requestId) ?? null)
|
|
84
84
|
: null;
|
|
85
85
|
const groupByUrl = new Map();
|
|
86
86
|
for (const group of groups.values()) {
|
|
@@ -92,7 +92,9 @@ const groupResources = (input) => {
|
|
|
92
92
|
}
|
|
93
93
|
const frameId = resource.request.frameId;
|
|
94
94
|
const byFrame = frameId ? groups.get(frameId) : undefined;
|
|
95
|
-
const byInitiator = resource.request.initiator?.url
|
|
95
|
+
const byInitiator = resource.request.initiator?.url
|
|
96
|
+
? groupByUrl.get(resource.request.initiator.url)
|
|
97
|
+
: undefined;
|
|
96
98
|
const target = byFrame ?? byInitiator ?? primaryGroup ?? Array.from(groups.values())[0];
|
|
97
99
|
if (target) {
|
|
98
100
|
target.resources.push(resource);
|
|
@@ -101,7 +103,9 @@ const groupResources = (input) => {
|
|
|
101
103
|
for (const entry of input.apiEntries) {
|
|
102
104
|
const frameId = entry.request.frameId;
|
|
103
105
|
const byFrame = frameId ? groups.get(frameId) : undefined;
|
|
104
|
-
const byInitiator = entry.request.initiator?.url
|
|
106
|
+
const byInitiator = entry.request.initiator?.url
|
|
107
|
+
? groupByUrl.get(entry.request.initiator.url)
|
|
108
|
+
: undefined;
|
|
105
109
|
const target = byFrame ?? byInitiator ?? primaryGroup ?? Array.from(groups.values())[0];
|
|
106
110
|
if (target) {
|
|
107
111
|
target.apiEntries.push(entry);
|
package/dist/types.d.ts
CHANGED
|
@@ -1,84 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
export type BodySource
|
|
3
|
-
kind: "buffer";
|
|
4
|
-
data: Uint8Array;
|
|
5
|
-
} | {
|
|
6
|
-
kind: "stream";
|
|
7
|
-
stream: ReadableStream<Uint8Array>;
|
|
8
|
-
} | {
|
|
9
|
-
kind: "late";
|
|
10
|
-
read: () => Promise<Uint8Array>;
|
|
11
|
-
};
|
|
12
|
-
export interface NetworkRequestEvent {
|
|
13
|
-
type: "request";
|
|
14
|
-
requestId: string;
|
|
15
|
-
url: string;
|
|
16
|
-
method: string;
|
|
17
|
-
headers: Record<string, string>;
|
|
18
|
-
frameId?: string;
|
|
19
|
-
resourceType?: ResourceType;
|
|
20
|
-
initiator?: {
|
|
21
|
-
type?: string;
|
|
22
|
-
url?: string;
|
|
23
|
-
};
|
|
24
|
-
timestamp: number;
|
|
25
|
-
}
|
|
26
|
-
export interface NetworkResponseEvent {
|
|
27
|
-
type: "response";
|
|
28
|
-
requestId: string;
|
|
29
|
-
url: string;
|
|
30
|
-
status: number;
|
|
31
|
-
statusText?: string;
|
|
32
|
-
headers: Record<string, string>;
|
|
33
|
-
mimeType?: string;
|
|
34
|
-
fromDiskCache?: boolean;
|
|
35
|
-
fromServiceWorker?: boolean;
|
|
36
|
-
timestamp: number;
|
|
37
|
-
body?: BodySource;
|
|
38
|
-
}
|
|
39
|
-
export interface NetworkRequestFailedEvent {
|
|
40
|
-
type: "failed";
|
|
41
|
-
requestId: string;
|
|
42
|
-
url: string;
|
|
43
|
-
errorText: string;
|
|
44
|
-
timestamp: number;
|
|
45
|
-
}
|
|
46
|
-
export type NetworkEvent = NetworkRequestEvent | NetworkResponseEvent | NetworkRequestFailedEvent;
|
|
47
|
-
export interface NetworkEventHandlers {
|
|
48
|
-
onEvent(event: NetworkEvent): void;
|
|
49
|
-
onError?(error: Error): void;
|
|
50
|
-
onLog?(msg: string, meta?: unknown): void;
|
|
51
|
-
}
|
|
52
|
-
export interface InterceptorCapabilities {
|
|
53
|
-
canGetResponseBody: boolean;
|
|
54
|
-
canStreamResponseBody: boolean;
|
|
55
|
-
canGetRequestBody: boolean;
|
|
56
|
-
providesResourceType: boolean;
|
|
57
|
-
}
|
|
58
|
-
export type InterceptTarget = {
|
|
59
|
-
kind: "url";
|
|
60
|
-
url: string;
|
|
61
|
-
} | {
|
|
62
|
-
kind: "puppeteer-page";
|
|
63
|
-
page: unknown;
|
|
64
|
-
} | {
|
|
65
|
-
kind: "cdp-tab";
|
|
66
|
-
tabId: number;
|
|
67
|
-
} | {
|
|
68
|
-
kind: "cdp-session";
|
|
69
|
-
session: unknown;
|
|
70
|
-
};
|
|
71
|
-
export type InterceptOptions = Record<string, unknown>;
|
|
72
|
-
export type NavigateOptions = Record<string, unknown>;
|
|
73
|
-
export interface InterceptSession {
|
|
74
|
-
navigate?(url: string, options?: NavigateOptions): Promise<void>;
|
|
75
|
-
stop(): Promise<void>;
|
|
76
|
-
}
|
|
77
|
-
export interface NetworkInterceptorAdapter {
|
|
78
|
-
readonly name: string;
|
|
79
|
-
readonly capabilities: InterceptorCapabilities;
|
|
80
|
-
start(target: InterceptTarget, handlers: NetworkEventHandlers, options?: InterceptOptions): Promise<InterceptSession>;
|
|
81
|
-
}
|
|
1
|
+
import type { BodySource, NetworkInterceptorAdapter, NetworkRequestEvent, NetworkResponseEvent, ResourceType } from "@pagepocket/interceptor";
|
|
2
|
+
export type { BodySource, InterceptOptions, InterceptSession, InterceptTarget, InterceptorActions, InterceptorCapabilities, NavigateOptions, NetworkEvent, NetworkEventHandlers, NetworkInterceptorAdapter, NetworkRequestEvent, NetworkRequestFailedEvent, NetworkResponseEvent, ResourceType, TriggerAction } from "@pagepocket/interceptor";
|
|
82
3
|
export interface PathResolver {
|
|
83
4
|
resolve(input: {
|
|
84
5
|
url: string;
|
package/dist/utils.js
CHANGED
|
@@ -17,7 +17,7 @@ const hashString = (value) => {
|
|
|
17
17
|
exports.hashString = hashString;
|
|
18
18
|
const stripLeadingSlash = (value) => value.replace(/^\/+/, "");
|
|
19
19
|
exports.stripLeadingSlash = stripLeadingSlash;
|
|
20
|
-
const ensureLeadingSlash = (value) => value.startsWith("/") ? value : `/${value}
|
|
20
|
+
const ensureLeadingSlash = (value) => (value.startsWith("/") ? value : `/${value}`);
|
|
21
21
|
exports.ensureLeadingSlash = ensureLeadingSlash;
|
|
22
22
|
const sanitizePosixPath = (value) => {
|
|
23
23
|
const parts = value.split("/").filter(Boolean);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pagepocket/lib",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Library for rewriting HTML snapshots and inlining local resources.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -12,10 +12,13 @@
|
|
|
12
12
|
"license": "ISC",
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"cheerio": "^1.0.0-rc.12",
|
|
15
|
-
"@pagepocket/
|
|
15
|
+
"@pagepocket/interceptor": "0.6.1",
|
|
16
|
+
"@pagepocket/uni-fs": "0.6.1"
|
|
16
17
|
},
|
|
17
18
|
"devDependencies": {
|
|
19
|
+
"@playwright/test": "^1.50.1",
|
|
18
20
|
"@types/node": "^20.11.30",
|
|
21
|
+
"esbuild": "^0.25.1",
|
|
19
22
|
"prettier": "^3.7.4",
|
|
20
23
|
"tsx": "^4.19.3",
|
|
21
24
|
"typescript": "^5.4.5"
|
|
@@ -24,6 +27,7 @@
|
|
|
24
27
|
"build": "tsc",
|
|
25
28
|
"format": "prettier --write .",
|
|
26
29
|
"format:check": "prettier --check .",
|
|
27
|
-
"test": "tsx --test specs
|
|
30
|
+
"test": "tsx --test specs/*.spec.ts",
|
|
31
|
+
"test:e2e": "playwright test -c e2e/playwright.config.ts"
|
|
28
32
|
}
|
|
29
33
|
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { type ResourceReference, type SrcsetReference } from "./resources";
|
|
2
|
-
import type { SnapshotData } from "./types";
|
|
3
|
-
export type DownloadedResource = {
|
|
4
|
-
url: string;
|
|
5
|
-
filename: string;
|
|
6
|
-
extension: string;
|
|
7
|
-
localPath: string;
|
|
8
|
-
contentType?: string | null;
|
|
9
|
-
size?: number;
|
|
10
|
-
};
|
|
11
|
-
type DownloadResourcesInput = {
|
|
12
|
-
baseUrl: string;
|
|
13
|
-
assetsDirName: string;
|
|
14
|
-
resourceUrls: ResourceReference[];
|
|
15
|
-
srcsetItems: SrcsetReference[];
|
|
16
|
-
referer?: string;
|
|
17
|
-
};
|
|
18
|
-
type DownloadResourcesResult = {
|
|
19
|
-
resourceMap: Map<string, DownloadedResource>;
|
|
20
|
-
resourceMeta: SnapshotData["resources"];
|
|
21
|
-
downloadedCount: number;
|
|
22
|
-
failedCount: number;
|
|
23
|
-
};
|
|
24
|
-
export declare const downloadResources: (input: DownloadResourcesInput) => Promise<DownloadResourcesResult>;
|
|
25
|
-
export {};
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.downloadResources = void 0;
|
|
37
|
-
const content_type_1 = require("./content-type");
|
|
38
|
-
const resources_1 = require("./resources");
|
|
39
|
-
const FNV_OFFSET = 0x811c9dc5;
|
|
40
|
-
const FNV_PRIME = 0x01000193;
|
|
41
|
-
const shouldSkipUrl = (value) => {
|
|
42
|
-
const trimmed = value.trim();
|
|
43
|
-
return (!trimmed ||
|
|
44
|
-
trimmed.startsWith("data:") ||
|
|
45
|
-
trimmed.startsWith("blob:") ||
|
|
46
|
-
trimmed.startsWith("mailto:") ||
|
|
47
|
-
trimmed.startsWith("tel:") ||
|
|
48
|
-
trimmed.startsWith("javascript:") ||
|
|
49
|
-
trimmed.startsWith("#"));
|
|
50
|
-
};
|
|
51
|
-
const hashUrl = (value) => {
|
|
52
|
-
let hash = FNV_OFFSET;
|
|
53
|
-
for (let i = 0; i < value.length; i += 1) {
|
|
54
|
-
hash ^= value.charCodeAt(i);
|
|
55
|
-
hash = (hash * FNV_PRIME) >>> 0;
|
|
56
|
-
}
|
|
57
|
-
return hash.toString(16).padStart(8, "0");
|
|
58
|
-
};
|
|
59
|
-
const extensionFromUrl = (url) => {
|
|
60
|
-
try {
|
|
61
|
-
const pathname = new URL(url).pathname;
|
|
62
|
-
const lastDot = pathname.lastIndexOf(".");
|
|
63
|
-
if (lastDot === -1) {
|
|
64
|
-
return "";
|
|
65
|
-
}
|
|
66
|
-
return pathname.slice(lastDot);
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
return "";
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
const normalizeExtension = (extension) => {
|
|
73
|
-
if (!extension) {
|
|
74
|
-
return "";
|
|
75
|
-
}
|
|
76
|
-
return extension.startsWith(".") ? extension.slice(1) : extension;
|
|
77
|
-
};
|
|
78
|
-
const buildLocalPath = (assetsDirName, filename, extension) => {
|
|
79
|
-
const normalizedExtension = normalizeExtension(extension);
|
|
80
|
-
if (!normalizedExtension) {
|
|
81
|
-
return `${assetsDirName}/${filename}`;
|
|
82
|
-
}
|
|
83
|
-
return `${assetsDirName}/${filename}.${normalizedExtension}`;
|
|
84
|
-
};
|
|
85
|
-
const collectSrcsetUrls = (items, baseUrl) => {
|
|
86
|
-
const urls = [];
|
|
87
|
-
for (const item of items) {
|
|
88
|
-
const parts = item.value.split(",").map((part) => part.trim());
|
|
89
|
-
for (const part of parts) {
|
|
90
|
-
const [rawUrl] = part.split(/\s+/, 2);
|
|
91
|
-
if (!rawUrl) {
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
const absolute = (0, resources_1.toAbsoluteUrl)(baseUrl, rawUrl);
|
|
95
|
-
urls.push(absolute);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return urls;
|
|
99
|
-
};
|
|
100
|
-
const downloadResources = async (input) => {
|
|
101
|
-
const { write, exists } = await Promise.resolve().then(() => __importStar(require("@pagepocket/uni-fs")));
|
|
102
|
-
const resourceMap = new Map();
|
|
103
|
-
const resourceMeta = [];
|
|
104
|
-
let downloadedCount = 0;
|
|
105
|
-
let failedCount = 0;
|
|
106
|
-
const srcsetUrls = collectSrcsetUrls(input.srcsetItems, input.baseUrl);
|
|
107
|
-
const candidateUrls = [...input.resourceUrls.map((resource) => resource.url), ...srcsetUrls];
|
|
108
|
-
for (const candidate of candidateUrls) {
|
|
109
|
-
if (shouldSkipUrl(candidate)) {
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
const url = (0, resources_1.toAbsoluteUrl)(input.baseUrl, candidate);
|
|
113
|
-
if (!url || resourceMap.has(url)) {
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
try {
|
|
117
|
-
const headers = {};
|
|
118
|
-
if (input.referer) {
|
|
119
|
-
headers.referer = input.referer;
|
|
120
|
-
}
|
|
121
|
-
const response = await fetch(url, { redirect: "follow", headers });
|
|
122
|
-
const contentType = response.headers.get("content-type");
|
|
123
|
-
const buffer = new Uint8Array(await response.arrayBuffer());
|
|
124
|
-
const extFromUrl = extensionFromUrl(url);
|
|
125
|
-
const extFromType = (0, content_type_1.extensionFromContentType)(contentType);
|
|
126
|
-
const extension = normalizeExtension(extFromUrl || extFromType);
|
|
127
|
-
const filename = hashUrl(url);
|
|
128
|
-
const localPath = buildLocalPath(input.assetsDirName, filename, extension);
|
|
129
|
-
if (!(await exists(input.assetsDirName + "/" + filename, extension))) {
|
|
130
|
-
await write(input.assetsDirName + "/" + filename, extension, buffer);
|
|
131
|
-
}
|
|
132
|
-
const entry = {
|
|
133
|
-
url,
|
|
134
|
-
filename,
|
|
135
|
-
extension,
|
|
136
|
-
localPath,
|
|
137
|
-
contentType,
|
|
138
|
-
size: buffer.byteLength
|
|
139
|
-
};
|
|
140
|
-
resourceMap.set(url, entry);
|
|
141
|
-
resourceMeta.push({
|
|
142
|
-
url,
|
|
143
|
-
localPath,
|
|
144
|
-
contentType,
|
|
145
|
-
size: buffer.byteLength
|
|
146
|
-
});
|
|
147
|
-
downloadedCount += 1;
|
|
148
|
-
}
|
|
149
|
-
catch {
|
|
150
|
-
failedCount += 1;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
return {
|
|
154
|
-
resourceMap,
|
|
155
|
-
resourceMeta,
|
|
156
|
-
downloadedCount,
|
|
157
|
-
failedCount
|
|
158
|
-
};
|
|
159
|
-
};
|
|
160
|
-
exports.downloadResources = downloadResources;
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { CapturedNetworkRecord, NetworkRecord } from "./types";
|
|
2
|
-
export declare const toDataUrlFromRecord: (record: NetworkRecord) => string | null;
|
|
3
|
-
export declare const findFaviconDataUrl: (records: NetworkRecord[]) => string | null;
|
|
4
|
-
export declare const mapCapturedNetworkRecords: (records: CapturedNetworkRecord[] | undefined) => NetworkRecord[];
|
package/dist/network-records.js
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.mapCapturedNetworkRecords = exports.findFaviconDataUrl = exports.toDataUrlFromRecord = void 0;
|
|
4
|
-
const getHeaderValue = (headers, name) => {
|
|
5
|
-
for (const key in headers) {
|
|
6
|
-
if (key.toLowerCase() === name.toLowerCase()) {
|
|
7
|
-
return headers[key];
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
return undefined;
|
|
11
|
-
};
|
|
12
|
-
const toBase64 = (value) => {
|
|
13
|
-
const bufferConstructor = globalThis.Buffer;
|
|
14
|
-
if (bufferConstructor) {
|
|
15
|
-
return bufferConstructor.from(value, "utf-8").toString("base64");
|
|
16
|
-
}
|
|
17
|
-
if (typeof btoa === "function") {
|
|
18
|
-
return btoa(value);
|
|
19
|
-
}
|
|
20
|
-
return "";
|
|
21
|
-
};
|
|
22
|
-
const toDataUrlFromRecord = (record) => {
|
|
23
|
-
if (!record)
|
|
24
|
-
return null;
|
|
25
|
-
const headers = record.responseHeaders || {};
|
|
26
|
-
const contentType = getHeaderValue(headers, "content-type") || "application/octet-stream";
|
|
27
|
-
if (record.responseEncoding === "base64" && record.responseBodyBase64) {
|
|
28
|
-
return `data:${contentType};base64,${record.responseBodyBase64}`;
|
|
29
|
-
}
|
|
30
|
-
if (record.responseBody) {
|
|
31
|
-
const encoded = toBase64(record.responseBody);
|
|
32
|
-
if (!encoded) {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
return `data:${contentType};base64,${encoded}`;
|
|
36
|
-
}
|
|
37
|
-
return null;
|
|
38
|
-
};
|
|
39
|
-
exports.toDataUrlFromRecord = toDataUrlFromRecord;
|
|
40
|
-
const findFaviconDataUrl = (records) => {
|
|
41
|
-
for (const record of records) {
|
|
42
|
-
if (!record || !record.url)
|
|
43
|
-
continue;
|
|
44
|
-
const headers = record.responseHeaders || {};
|
|
45
|
-
const contentType = (getHeaderValue(headers, "content-type") || "").toLowerCase();
|
|
46
|
-
const pathname = (() => {
|
|
47
|
-
try {
|
|
48
|
-
return new URL(record.url).pathname;
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
return record.url;
|
|
52
|
-
}
|
|
53
|
-
})();
|
|
54
|
-
const looksLikeFavicon = contentType.includes("icon") || /favicon(\.[a-z0-9]+)?$/i.test(pathname || "");
|
|
55
|
-
if (!looksLikeFavicon)
|
|
56
|
-
continue;
|
|
57
|
-
const dataUrl = (0, exports.toDataUrlFromRecord)(record);
|
|
58
|
-
if (dataUrl)
|
|
59
|
-
return dataUrl;
|
|
60
|
-
}
|
|
61
|
-
return null;
|
|
62
|
-
};
|
|
63
|
-
exports.findFaviconDataUrl = findFaviconDataUrl;
|
|
64
|
-
const mapCapturedNetworkRecords = (records) => {
|
|
65
|
-
if (!records)
|
|
66
|
-
return [];
|
|
67
|
-
return records.map((record) => {
|
|
68
|
-
const response = record.response;
|
|
69
|
-
return {
|
|
70
|
-
url: record.url,
|
|
71
|
-
method: record.method,
|
|
72
|
-
status: response?.status,
|
|
73
|
-
statusText: response?.statusText,
|
|
74
|
-
responseHeaders: response?.headers,
|
|
75
|
-
responseBody: response?.bodyEncoding === "text" ? response.body : undefined,
|
|
76
|
-
responseBodyBase64: response?.bodyEncoding === "base64" ? response.body : undefined,
|
|
77
|
-
responseEncoding: response?.bodyEncoding,
|
|
78
|
-
error: record.error,
|
|
79
|
-
timestamp: record.timestamp
|
|
80
|
-
};
|
|
81
|
-
});
|
|
82
|
-
};
|
|
83
|
-
exports.mapCapturedNetworkRecords = mapCapturedNetworkRecords;
|