@pagepocket/lib 0.6.0 → 0.6.2

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/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, InterceptOptions, InterceptSession, InterceptTarget, NetworkEvent, NetworkEventHandlers, NetworkInterceptorAdapter, NetworkRequestEvent, NetworkRequestFailedEvent, NetworkResponseEvent, PagePocketOptions, PageSnapshot, PathResolver, ResourceFilter, ResourceType, SnapshotFile, WriteFSOptions, WriteResult, ZipOptions } from "./types";
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";
@@ -1,4 +1,5 @@
1
- import type { ApiRecord, ContentRef, ContentStore, NetworkEvent, NetworkRequestEvent, NetworkRequestFailedEvent, NetworkResponseEvent, ResourceFilter } from "./types";
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;
@@ -1,4 +1,5 @@
1
- import type { CaptureOptions, InterceptTarget, PagePocketOptions, PageSnapshot } from "./types";
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;
@@ -1,4 +1,5 @@
1
- import type { PathResolver, ResourceType } from "./types";
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/types.d.ts CHANGED
@@ -1,81 +1,5 @@
1
- export type ResourceType = "document" | "stylesheet" | "script" | "image" | "font" | "media" | "xhr" | "fetch" | "other" | (string & {});
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
- export type InterceptOptions = Record<string, unknown>;
69
- export type NavigateOptions = Record<string, unknown>;
70
- export interface InterceptSession {
71
- navigate?(url: string, options?: NavigateOptions): Promise<void>;
72
- stop(): Promise<void>;
73
- }
74
- export interface NetworkInterceptorAdapter {
75
- readonly name: string;
76
- readonly capabilities: InterceptorCapabilities;
77
- start(target: InterceptTarget, handlers: NetworkEventHandlers, options?: InterceptOptions): Promise<InterceptSession>;
78
- }
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";
79
3
  export interface PathResolver {
80
4
  resolve(input: {
81
5
  url: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pagepocket/lib",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
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,7 +12,8 @@
12
12
  "license": "ISC",
13
13
  "dependencies": {
14
14
  "cheerio": "^1.0.0-rc.12",
15
- "@pagepocket/uni-fs": "0.6.0"
15
+ "@pagepocket/uni-fs": "0.6.2",
16
+ "@pagepocket/interceptor": "0.6.2"
16
17
  },
17
18
  "devDependencies": {
18
19
  "@playwright/test": "^1.50.1",
@@ -20,14 +21,13 @@
20
21
  "esbuild": "^0.25.1",
21
22
  "prettier": "^3.7.4",
22
23
  "tsx": "^4.19.3",
23
- "typescript": "^5.4.5",
24
- "@pagepocket/cdp-adapter": "0.6.0"
24
+ "typescript": "^5.4.5"
25
25
  },
26
26
  "scripts": {
27
27
  "build": "tsc",
28
28
  "format": "prettier --write .",
29
29
  "format:check": "prettier --check .",
30
- "test": "tsx --test specs/**/*.test.ts",
30
+ "test": "tsx --test specs/*.spec.ts",
31
31
  "test:e2e": "playwright test -c e2e/playwright.config.ts"
32
32
  }
33
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[];
@@ -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;