@pagepocket/capture-http-lighterceptor-unit 0.8.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.
@@ -0,0 +1,9 @@
1
+ import { type PagePocketContext, type PagePocketPlugin } from "@pagepocket/lib";
2
+ import { type LighterceptorAdapterOptions } from "./internal/lighterceptor-adapter.js";
3
+ export type CaptureHttpLighterceptorPluginOptions = LighterceptorAdapterOptions;
4
+ export declare class CaptureHttpLighterceptorPlugin implements PagePocketPlugin {
5
+ readonly name = "plugin:capture-http-lighterceptor";
6
+ private adapterOptions;
7
+ constructor(options?: CaptureHttpLighterceptorPluginOptions);
8
+ apply(ctx: PagePocketContext): void;
9
+ }
@@ -0,0 +1,140 @@
1
+ import { createMemoryContentStore } from "@pagepocket/lib";
2
+ import { InflightTracker, networkIdle, normalizeCompletion, timeout } from "@pagepocket/lib";
3
+ import { LighterceptorAdapter } from "./internal/lighterceptor-adapter.js";
4
+ const headersRecordToList = (headers) => {
5
+ if (!headers)
6
+ return [];
7
+ return Object.keys(headers).map((name) => ({ name, value: headers[name] }));
8
+ };
9
+ export class CaptureHttpLighterceptorPlugin {
10
+ constructor(options) {
11
+ this.name = "plugin:capture-http-lighterceptor";
12
+ this.adapterOptions = options ?? {};
13
+ }
14
+ apply(ctx) {
15
+ const contentStore = createMemoryContentStore("capture-http-lighterceptor");
16
+ const events = [];
17
+ const capabilities = {
18
+ requestHeaders: "approx",
19
+ responseHeaders: "approx",
20
+ requestBodies: false,
21
+ responseBodies: "decoded",
22
+ httpVersion: false,
23
+ remoteIp: false,
24
+ headerOrderPreserved: false
25
+ };
26
+ ctx.capture = {
27
+ events,
28
+ contentStore,
29
+ capabilities
30
+ };
31
+ const inflightTracker = new InflightTracker();
32
+ const handleNetworkEvent = async (event) => {
33
+ inflightTracker.handleEvent(event);
34
+ ctx.emitNetworkEvent?.(event);
35
+ if (event.type === "request") {
36
+ events.push({
37
+ type: "http.request",
38
+ requestId: event.requestId,
39
+ url: event.url,
40
+ method: event.method,
41
+ headers: headersRecordToList(event.headers),
42
+ timestamp: event.timestamp,
43
+ frameId: event.frameId,
44
+ resourceType: event.resourceType,
45
+ initiator: event.initiator
46
+ });
47
+ return;
48
+ }
49
+ if (event.type === "failed") {
50
+ events.push({
51
+ type: "http.failed",
52
+ requestId: event.requestId,
53
+ url: event.url,
54
+ errorText: event.errorText,
55
+ timestamp: event.timestamp
56
+ });
57
+ return;
58
+ }
59
+ const bodyRef = event.body
60
+ ? await contentStore.put(event.body, {
61
+ url: event.url,
62
+ mimeType: event.mimeType,
63
+ sizeHint: undefined
64
+ })
65
+ : undefined;
66
+ events.push({
67
+ type: "http.response",
68
+ requestId: event.requestId,
69
+ url: event.url,
70
+ status: event.status,
71
+ statusText: event.statusText,
72
+ headers: headersRecordToList(event.headers),
73
+ timestamp: event.timestamp,
74
+ mimeType: event.mimeType,
75
+ fromDiskCache: event.fromDiskCache,
76
+ fromServiceWorker: event.fromServiceWorker,
77
+ bodyRef,
78
+ bodySize: undefined
79
+ });
80
+ };
81
+ const stateKey = "captureHttpLighterceptor.session";
82
+ ctx.onInit(async () => {
83
+ const target = ctx.entry.kind === "url"
84
+ ? { kind: "url", url: ctx.entry.url }
85
+ : ctx.entry.kind === "html-string"
86
+ ? {
87
+ kind: "html",
88
+ htmlString: await ctx.whenHtml().then((h) => h.htmlString),
89
+ baseUrl: ctx.entry.baseUrl,
90
+ ...(ctx.entry.url ? { url: ctx.entry.url } : {})
91
+ }
92
+ : (() => {
93
+ throw new Error(`CaptureHttpLighterceptorPlugin does not support entry kind: ${String(ctx.entry.kind)}`);
94
+ })();
95
+ const adapter = new LighterceptorAdapter(this.adapterOptions);
96
+ const session = await adapter.start(target, {
97
+ onEvent(event) {
98
+ void handleNetworkEvent(event);
99
+ },
100
+ onError(error) {
101
+ // eslint-disable-next-line no-console
102
+ console.warn("[pagepocket][capture-http-lighterceptor] adapter error", error);
103
+ }
104
+ }, this.adapterOptions);
105
+ ctx.state[stateKey] = session;
106
+ // LighterceptorAdapter does not implement navigate; it fetches HTML itself.
107
+ // Allow tests to rely on an already-provided HTML artifact (no extra fetch).
108
+ if (!ctx.html) {
109
+ const html = await session.waitForHtml();
110
+ ctx.setHtml(html);
111
+ }
112
+ });
113
+ ctx.onBeforeNetwork(async () => {
114
+ const session = ctx.state[stateKey];
115
+ if (!session) {
116
+ throw new Error("CaptureHttpLighterceptorPlugin internal error: missing session");
117
+ }
118
+ await session.startCapture();
119
+ const completionStrategies = normalizeCompletion(ctx.options.completion);
120
+ const idleMs = ctx.options.timeoutMs ?? 5000;
121
+ const maxDurationMs = ctx.options.maxDurationMs;
122
+ const completion = completionStrategies.length > 0
123
+ ? completionStrategies
124
+ : [networkIdle(idleMs), ...(maxDurationMs !== undefined ? [timeout(maxDurationMs)] : [])];
125
+ if (completion.length === 1) {
126
+ await completion[0].wait({
127
+ now: () => Date.now(),
128
+ getStats: () => inflightTracker.getStats()
129
+ });
130
+ }
131
+ else {
132
+ await Promise.race(completion.map((strategy) => strategy.wait({
133
+ now: () => Date.now(),
134
+ getStats: () => inflightTracker.getStats()
135
+ })));
136
+ }
137
+ await session.stop();
138
+ });
139
+ }
140
+ }
@@ -0,0 +1,13 @@
1
+ import { Unit, type CaptureArtifacts } from "@pagepocket/lib";
2
+ import { type LighterceptorAdapterOptions } from "./internal/lighterceptor-adapter.js";
3
+ export type CaptureHttpLighterceptorUnitOptions = LighterceptorAdapterOptions;
4
+ export declare class CaptureHttpLighterceptorUnit extends Unit {
5
+ readonly id = "captureHttpLighterceptor";
6
+ readonly kind = "capture.http.lighterceptor";
7
+ private adapterOptions;
8
+ constructor(options?: CaptureHttpLighterceptorUnitOptions);
9
+ run(ctx: import("@pagepocket/lib").UnitContext, rt: import("@pagepocket/lib").UnitRuntime): Promise<{
10
+ capture: CaptureArtifacts;
11
+ html: {};
12
+ }>;
13
+ }
@@ -0,0 +1,126 @@
1
+ import { NETWORK } from "@pagepocket/contracts";
2
+ import { Unit, createMemoryContentStore, InflightTracker, mapKind, networkIdle, normalizeCompletion, throwUnsupportedEntryKind, timeout } from "@pagepocket/lib";
3
+ import { LighterceptorAdapter } from "./internal/lighterceptor-adapter.js";
4
+ const headersRecordToList = (headers) => {
5
+ if (!headers)
6
+ return [];
7
+ return Object.keys(headers).map((name) => ({ name, value: headers[name] }));
8
+ };
9
+ const targetBuilders = {
10
+ url: (entry) => ({
11
+ kind: "url",
12
+ url: entry.url
13
+ }),
14
+ "html-string": (entry) => ({
15
+ kind: "html",
16
+ htmlString: entry.htmlString,
17
+ baseUrl: entry.baseUrl,
18
+ ...(entry.url ? { url: entry.url } : {})
19
+ })
20
+ };
21
+ export class CaptureHttpLighterceptorUnit extends Unit {
22
+ constructor(options) {
23
+ super();
24
+ this.id = "captureHttpLighterceptor";
25
+ this.kind = "capture.http.lighterceptor";
26
+ this.adapterOptions = options ?? {};
27
+ }
28
+ async run(ctx, rt) {
29
+ const contentStore = createMemoryContentStore("capture-http-lighterceptor");
30
+ const events = [];
31
+ const capabilities = {
32
+ requestHeaders: "approx",
33
+ responseHeaders: "approx",
34
+ requestBodies: false,
35
+ responseBodies: "decoded",
36
+ httpVersion: false,
37
+ remoteIp: false,
38
+ headerOrderPreserved: false
39
+ };
40
+ const inflightTracker = new InflightTracker();
41
+ const handleNetworkEvent = async (event) => {
42
+ inflightTracker.handleEvent(event);
43
+ rt.publish(NETWORK, event);
44
+ if (event.type === "request") {
45
+ events.push({
46
+ type: "http.request",
47
+ requestId: event.requestId,
48
+ url: event.url,
49
+ method: event.method,
50
+ headers: headersRecordToList(event.headers),
51
+ timestamp: event.timestamp,
52
+ frameId: event.frameId,
53
+ resourceType: event.resourceType,
54
+ initiator: event.initiator
55
+ });
56
+ return;
57
+ }
58
+ if (event.type === "failed") {
59
+ events.push({
60
+ type: "http.failed",
61
+ requestId: event.requestId,
62
+ url: event.url,
63
+ errorText: event.errorText,
64
+ timestamp: event.timestamp
65
+ });
66
+ return;
67
+ }
68
+ const bodyRef = event.body
69
+ ? await contentStore.put(event.body, {
70
+ url: event.url,
71
+ mimeType: event.mimeType,
72
+ sizeHint: undefined
73
+ })
74
+ : undefined;
75
+ events.push({
76
+ type: "http.response",
77
+ requestId: event.requestId,
78
+ url: event.url,
79
+ status: event.status,
80
+ statusText: event.statusText,
81
+ headers: headersRecordToList(event.headers),
82
+ timestamp: event.timestamp,
83
+ mimeType: event.mimeType,
84
+ fromDiskCache: event.fromDiskCache,
85
+ fromServiceWorker: event.fromServiceWorker,
86
+ bodyRef,
87
+ bodySize: undefined
88
+ });
89
+ };
90
+ const capture = { events, contentStore, capabilities };
91
+ const adapter = new LighterceptorAdapter(this.adapterOptions);
92
+ const target = mapKind(rt.entry, targetBuilders, {
93
+ onUnsupportedKind: throwUnsupportedEntryKind("CaptureHttpLighterceptorUnit")
94
+ });
95
+ const session = await adapter.start(target, {
96
+ onEvent: (event) => {
97
+ void handleNetworkEvent(event);
98
+ },
99
+ onError: (error) => {
100
+ console.warn("[pagepocket][capture-http-lighterceptor] adapter error", error);
101
+ }
102
+ }, this.adapterOptions);
103
+ const html = ctx.value.html ?? (await session.waitForHtml());
104
+ await session.startCapture();
105
+ const completionStrategies = normalizeCompletion(rt.options.completion);
106
+ const idleMs = rt.options.timeoutMs ?? 5000;
107
+ const maxDurationMs = rt.options.maxDurationMs;
108
+ const completion = completionStrategies.length > 0
109
+ ? completionStrategies
110
+ : [networkIdle(idleMs), ...(maxDurationMs !== undefined ? [timeout(maxDurationMs)] : [])];
111
+ if (completion.length === 1) {
112
+ await completion[0].wait({
113
+ now: () => Date.now(),
114
+ getStats: () => inflightTracker.getStats()
115
+ });
116
+ }
117
+ else {
118
+ await Promise.race(completion.map((strategy) => strategy.wait({
119
+ now: () => Date.now(),
120
+ getStats: () => inflightTracker.getStats()
121
+ })));
122
+ }
123
+ await session.stop();
124
+ return { capture, html };
125
+ }
126
+ }
@@ -0,0 +1,2 @@
1
+ export { CaptureHttpLighterceptorUnit } from "./capture-http-lighterceptor-unit.js";
2
+ export type { CaptureHttpLighterceptorUnitOptions } from "./capture-http-lighterceptor-unit.js";
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { CaptureHttpLighterceptorUnit } from "./capture-http-lighterceptor-unit.js";
@@ -0,0 +1,24 @@
1
+ import type { InterceptSession, InterceptTarget, NetworkEventHandlers, NetworkInterceptorAdapter, TriggerAction } from "@pagepocket/lib";
2
+ import { type LighterceptorOptions } from "@pagepocket/lighterceptor";
3
+ export type LighterceptorAdapterOptions = LighterceptorOptions & {
4
+ triggerActions?: TriggerAction[];
5
+ };
6
+ /**
7
+ * Internal adapter for the capture plugin.
8
+ *
9
+ * Note: this is intentionally NOT published as a separate package.
10
+ */
11
+ export declare class LighterceptorAdapter implements NetworkInterceptorAdapter {
12
+ readonly name = "lighterceptor";
13
+ readonly capabilities: {
14
+ canGetResponseBody: boolean;
15
+ canStreamResponseBody: boolean;
16
+ canGetRequestBody: boolean;
17
+ providesResourceType: boolean;
18
+ canWaitForHtml: boolean;
19
+ supportsStagedCapture: boolean;
20
+ };
21
+ private options;
22
+ constructor(options?: LighterceptorAdapterOptions);
23
+ start(target: InterceptTarget, handlers: NetworkEventHandlers, options?: LighterceptorAdapterOptions): Promise<InterceptSession>;
24
+ }
@@ -0,0 +1,200 @@
1
+ import { Lighterceptor } from "@pagepocket/lighterceptor";
2
+ import { runTriggerActions } from "./trigger-actions.js";
3
+ import { decodeBase64 } from "./utils/base64.js";
4
+ import { getHeaderValue } from "./utils/headers.js";
5
+ import { inferResourceType } from "./utils/resource-type.js";
6
+ const encodeUtf8 = (input) => new TextEncoder().encode(input);
7
+ const toRequestEvent = (record) => {
8
+ const headers = record.headers ?? {};
9
+ const headersForType = record.responseHeaders && Object.keys(record.responseHeaders).length > 0
10
+ ? record.responseHeaders
11
+ : headers;
12
+ const resourceType = inferResourceType(record.source, headersForType);
13
+ const event = {
14
+ type: "request",
15
+ requestId: record.requestId,
16
+ url: record.url,
17
+ method: record.method,
18
+ headers,
19
+ resourceType: resourceType,
20
+ timestamp: record.timestamp
21
+ };
22
+ return event;
23
+ };
24
+ /**
25
+ * Internal adapter for the capture plugin.
26
+ *
27
+ * Note: this is intentionally NOT published as a separate package.
28
+ */
29
+ export class LighterceptorAdapter {
30
+ constructor(options = {}) {
31
+ this.name = "lighterceptor";
32
+ this.capabilities = {
33
+ canGetResponseBody: true,
34
+ canStreamResponseBody: false,
35
+ canGetRequestBody: false,
36
+ providesResourceType: true,
37
+ canWaitForHtml: true,
38
+ supportsStagedCapture: true
39
+ };
40
+ this.options = options;
41
+ }
42
+ async start(target, handlers, options) {
43
+ const mergedOptions = {
44
+ ...this.options,
45
+ ...(options ?? {})
46
+ };
47
+ let stopped = false;
48
+ let startedCapture = false;
49
+ let htmlArtifact;
50
+ const htmlPromise = (async () => {
51
+ if (target.kind === "html") {
52
+ return {
53
+ htmlString: target.htmlString,
54
+ baseUrl: target.baseUrl,
55
+ url: target.url,
56
+ contentType: "text/html"
57
+ };
58
+ }
59
+ if (target.kind !== "url") {
60
+ throw new Error("LighterceptorAdapter only supports url or html targets.");
61
+ }
62
+ if (typeof fetch !== "function") {
63
+ throw new Error("Global fetch is required for LighterceptorAdapter url targets.");
64
+ }
65
+ const response = await fetch(target.url, {
66
+ headers: mergedOptions.headers
67
+ });
68
+ const htmlString = await response.text();
69
+ const contentType = response.headers.get("content-type") ?? undefined;
70
+ const url = response.url || target.url;
71
+ return {
72
+ htmlString,
73
+ baseUrl: url,
74
+ url,
75
+ contentType
76
+ };
77
+ })();
78
+ const waitForHtml = async () => {
79
+ if (htmlArtifact) {
80
+ return htmlArtifact;
81
+ }
82
+ htmlArtifact = await htmlPromise;
83
+ return htmlArtifact;
84
+ };
85
+ const emitPrimaryDocumentEvents = (artifact) => {
86
+ const docUrl = artifact.url ?? artifact.baseUrl;
87
+ if (!docUrl) {
88
+ return;
89
+ }
90
+ const now = Date.now();
91
+ const requestId = `doc:${docUrl}:${now}`;
92
+ const requestEvent = {
93
+ type: "request",
94
+ requestId,
95
+ url: docUrl,
96
+ method: "GET",
97
+ headers: {},
98
+ resourceType: "document",
99
+ timestamp: now
100
+ };
101
+ handlers.onEvent(requestEvent);
102
+ const headers = {};
103
+ if (artifact.contentType) {
104
+ headers["content-type"] = artifact.contentType;
105
+ }
106
+ const responseEvent = {
107
+ type: "response",
108
+ requestId,
109
+ url: docUrl,
110
+ status: 200,
111
+ statusText: "OK",
112
+ headers,
113
+ mimeType: artifact.contentType,
114
+ timestamp: now,
115
+ body: {
116
+ kind: "buffer",
117
+ data: new TextEncoder().encode(artifact.htmlString)
118
+ }
119
+ };
120
+ handlers.onEvent(responseEvent);
121
+ };
122
+ const startCapture = async () => {
123
+ if (startedCapture) {
124
+ return;
125
+ }
126
+ startedCapture = true;
127
+ const artifact = await waitForHtml();
128
+ if (stopped) {
129
+ return;
130
+ }
131
+ emitPrimaryDocumentEvents(artifact);
132
+ // Note: `timeoutMs` is defined as the library-level *network idle duration*.
133
+ // LighterceptorAdapter does not currently map this to an internal wait.
134
+ const settleTimeMs = mergedOptions.settleTimeMs;
135
+ const lighterceptor = new Lighterceptor(artifact.htmlString, {
136
+ recursion: true,
137
+ baseUrl: artifact.baseUrl,
138
+ ...mergedOptions,
139
+ ...(settleTimeMs !== undefined ? { settleTimeMs } : {})
140
+ });
141
+ const result = await lighterceptor.run();
142
+ const networkRecords = result.networkRecords ?? [];
143
+ let sequence = 0;
144
+ for (const record of networkRecords) {
145
+ if (stopped) {
146
+ return;
147
+ }
148
+ const requestId = `${record.url}:${record.timestamp}:${sequence++}`;
149
+ const requestEvent = toRequestEvent({
150
+ url: record.url,
151
+ method: record.method || "GET",
152
+ source: record.source,
153
+ timestamp: record.timestamp,
154
+ responseHeaders: record.response?.headers,
155
+ requestId
156
+ });
157
+ handlers.onEvent(requestEvent);
158
+ if (record.response) {
159
+ const headers = record.response.headers || {};
160
+ const responseEvent = {
161
+ type: "response",
162
+ requestId: requestEvent.requestId,
163
+ url: record.url,
164
+ status: record.response.status,
165
+ statusText: record.response.statusText,
166
+ headers,
167
+ mimeType: getHeaderValue(headers, "content-type"),
168
+ timestamp: record.timestamp,
169
+ body: {
170
+ kind: "buffer",
171
+ data: record.response.bodyEncoding === "base64"
172
+ ? decodeBase64(record.response.body)
173
+ : encodeUtf8(record.response.body)
174
+ }
175
+ };
176
+ handlers.onEvent(responseEvent);
177
+ continue;
178
+ }
179
+ if (record.error) {
180
+ const failedEvent = {
181
+ type: "failed",
182
+ requestId: requestEvent.requestId,
183
+ url: record.url,
184
+ errorText: record.error,
185
+ timestamp: record.timestamp
186
+ };
187
+ handlers.onEvent(failedEvent);
188
+ }
189
+ }
190
+ await runTriggerActions(mergedOptions.triggerActions);
191
+ };
192
+ return {
193
+ waitForHtml,
194
+ startCapture,
195
+ stop: async () => {
196
+ stopped = true;
197
+ }
198
+ };
199
+ }
200
+ }
@@ -0,0 +1,2 @@
1
+ import { type TriggerAction } from "@pagepocket/lib";
2
+ export declare const runTriggerActions: (actions?: TriggerAction[]) => Promise<void>;
@@ -0,0 +1,35 @@
1
+ import { TriggerActionValues } from "@pagepocket/lib";
2
+ export const runTriggerActions = async (actions = []) => {
3
+ if (typeof document === "undefined" || typeof window === "undefined") {
4
+ return;
5
+ }
6
+ for (const action of actions) {
7
+ if (action === TriggerActionValues.HOVER) {
8
+ await simulateHoverAll();
9
+ }
10
+ if (action === TriggerActionValues.SCROLL_TO_END) {
11
+ await simulateScrollToEnd();
12
+ }
13
+ }
14
+ };
15
+ const simulateHoverAll = async () => {
16
+ const elements = Array.from(document.querySelectorAll("*"));
17
+ for (const element of elements) {
18
+ const rect = element.getBoundingClientRect();
19
+ const x = rect.left + rect.width / 2;
20
+ const y = rect.top + rect.height / 2;
21
+ const event = new MouseEvent("mouseover", {
22
+ bubbles: true,
23
+ cancelable: true,
24
+ clientX: x,
25
+ clientY: y
26
+ });
27
+ element.dispatchEvent(event);
28
+ }
29
+ };
30
+ const simulateScrollToEnd = async () => {
31
+ const scrollHeight = document.documentElement?.scrollHeight ?? document.body?.scrollHeight;
32
+ if (typeof scrollHeight === "number") {
33
+ window.scrollTo({ top: scrollHeight });
34
+ }
35
+ };
@@ -0,0 +1 @@
1
+ export declare const decodeBase64: (input: string) => Uint8Array;
@@ -0,0 +1,12 @@
1
+ export const decodeBase64 = (input) => {
2
+ if (typeof atob === "function") {
3
+ const binary = atob(input);
4
+ const bytes = new Uint8Array(binary.length);
5
+ for (let i = 0; i < binary.length; i++) {
6
+ bytes[i] = binary.charCodeAt(i);
7
+ }
8
+ return bytes;
9
+ }
10
+ // Node fallback
11
+ return Uint8Array.from(Buffer.from(input, "base64"));
12
+ };
@@ -0,0 +1 @@
1
+ export declare const getHeaderValue: (headers: Record<string, string>, name: string) => string | undefined;
@@ -0,0 +1,4 @@
1
+ export const getHeaderValue = (headers, name) => {
2
+ const key = Object.keys(headers).find((k) => k.toLowerCase() === name.toLowerCase());
3
+ return key ? headers[key] : undefined;
4
+ };
@@ -0,0 +1,2 @@
1
+ import type { ResourceType } from "@pagepocket/lib";
2
+ export declare const inferResourceType: (source: string | undefined, headers: Record<string, string>) => ResourceType | undefined;
@@ -0,0 +1,25 @@
1
+ import { getHeaderValue } from "./headers.js";
2
+ export const inferResourceType = (source, headers) => {
3
+ if (source === "fetch")
4
+ return "fetch";
5
+ if (source === "xhr")
6
+ return "xhr";
7
+ if (source === "css")
8
+ return "stylesheet";
9
+ if (source === "img")
10
+ return "image";
11
+ const contentType = (getHeaderValue(headers, "content-type") || "").toLowerCase();
12
+ if (contentType.includes("text/html"))
13
+ return "document";
14
+ if (contentType.includes("text/css"))
15
+ return "stylesheet";
16
+ if (contentType.includes("javascript"))
17
+ return "script";
18
+ if (contentType.startsWith("image/"))
19
+ return "image";
20
+ if (contentType.startsWith("font/") || contentType.includes("woff"))
21
+ return "font";
22
+ if (contentType.startsWith("audio/") || contentType.startsWith("video/"))
23
+ return "media";
24
+ return undefined;
25
+ };
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@pagepocket/capture-http-lighterceptor-unit",
3
+ "version": "0.8.0",
4
+ "description": "PagePocket plugin: capture HTTP events (lighterceptor adapter)",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "license": "ISC",
12
+ "dependencies": {
13
+ "@pagepocket/lib": "0.8.0",
14
+ "@pagepocket/contracts": "0.8.0",
15
+ "@pagepocket/lighterceptor": "0.8.0"
16
+ },
17
+ "devDependencies": {
18
+ "typescript": "^5.4.5"
19
+ },
20
+ "scripts": {
21
+ "build": "tsc -p tsconfig.json",
22
+ "test": "node -e \"process.exit(0)\""
23
+ }
24
+ }