pagespeed-quest 0.1.2 → 0.3.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 (72) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.ja.md +22 -2
  3. package/README.md +21 -1
  4. package/package.json +25 -21
  5. package/build/main/adhoc.d.ts +0 -2
  6. package/build/main/adhoc.js +0 -48
  7. package/build/main/command.d.ts +0 -2
  8. package/build/main/command.js +0 -103
  9. package/build/main/common-types.d.ts +0 -41
  10. package/build/main/common-types.js +0 -3
  11. package/build/main/content-encoding.d.ts +0 -11
  12. package/build/main/content-encoding.js +0 -51
  13. package/build/main/encoding.d.ts +0 -11
  14. package/build/main/encoding.js +0 -51
  15. package/build/main/formatting.d.ts +0 -8
  16. package/build/main/formatting.js +0 -64
  17. package/build/main/http.d.ts +0 -27
  18. package/build/main/http.js +0 -106
  19. package/build/main/index.d.ts +0 -9
  20. package/build/main/index.js +0 -26
  21. package/build/main/inventory.d.ts +0 -39
  22. package/build/main/inventory.js +0 -171
  23. package/build/main/logger.d.ts +0 -20
  24. package/build/main/logger.js +0 -22
  25. package/build/main/playback/inventory.d.ts +0 -0
  26. package/build/main/playback/inventory.js +0 -47
  27. package/build/main/playback.d.ts +0 -22
  28. package/build/main/playback.js +0 -111
  29. package/build/main/proxy.d.ts +0 -40
  30. package/build/main/proxy.js +0 -111
  31. package/build/main/recording/proxy.d.ts +0 -28
  32. package/build/main/recording/proxy.js +0 -90
  33. package/build/main/recording.d.ts +0 -28
  34. package/build/main/recording.js +0 -95
  35. package/build/main/throttling.d.ts +0 -34
  36. package/build/main/throttling.js +0 -88
  37. package/build/main/url.d.ts +0 -3
  38. package/build/main/url.js +0 -67
  39. package/build/module/adhoc.d.ts +0 -2
  40. package/build/module/adhoc.js +0 -40
  41. package/build/module/command.d.ts +0 -2
  42. package/build/module/command.js +0 -98
  43. package/build/module/common-types.d.ts +0 -41
  44. package/build/module/common-types.js +0 -2
  45. package/build/module/content-encoding.d.ts +0 -11
  46. package/build/module/content-encoding.js +0 -43
  47. package/build/module/encoding.d.ts +0 -11
  48. package/build/module/encoding.js +0 -43
  49. package/build/module/formatting.d.ts +0 -8
  50. package/build/module/formatting.js +0 -55
  51. package/build/module/http.d.ts +0 -27
  52. package/build/module/http.js +0 -96
  53. package/build/module/index.d.ts +0 -9
  54. package/build/module/index.js +0 -10
  55. package/build/module/inventory.d.ts +0 -39
  56. package/build/module/inventory.js +0 -165
  57. package/build/module/logger.d.ts +0 -20
  58. package/build/module/logger.js +0 -15
  59. package/build/module/playback/inventory.d.ts +0 -0
  60. package/build/module/playback/inventory.js +0 -47
  61. package/build/module/playback.d.ts +0 -22
  62. package/build/module/playback.js +0 -102
  63. package/build/module/proxy.d.ts +0 -40
  64. package/build/module/proxy.js +0 -111
  65. package/build/module/recording/proxy.d.ts +0 -28
  66. package/build/module/recording/proxy.js +0 -85
  67. package/build/module/recording.d.ts +0 -28
  68. package/build/module/recording.js +0 -92
  69. package/build/module/throttling.d.ts +0 -34
  70. package/build/module/throttling.js +0 -89
  71. package/build/module/url.d.ts +0 -3
  72. package/build/module/url.js +0 -59
@@ -1,106 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.stringifyContentTypeHeader = exports.parseContentTypeHeader = exports.requestContentFilePath = exports.normalizeUrl = void 0;
7
- const crypto_1 = __importDefault(require("crypto"));
8
- const path_1 = __importDefault(require("path"));
9
- const DirectoryIndex = 'index.html';
10
- const BasenameMaxLength = 196;
11
- const HashLength = 8;
12
- const IgnoreParams = process.env.IGNORE_PARAMS || 'ts';
13
- const IgnoreParamsRegex = IgnoreParams.split(/\s*,\s*/).map((p) => new RegExp(`(?<=[?&]${p}=)[^&]*`, 'g'));
14
- /**
15
- * Remove IgnoreParams from URL
16
- * Default: ts (timestamp) assumed different for each request
17
- * @param url
18
- * @returns
19
- */
20
- function normalizeUrl(url) {
21
- const urlObj = typeof url === 'string' ? new URL(url) : url;
22
- // Remove dynamic params
23
- if (urlObj.search !== '') {
24
- urlObj.search = IgnoreParamsRegex.reduce((search, re) => {
25
- return search.replace(re, '');
26
- }, urlObj.search);
27
- }
28
- return urlObj;
29
- }
30
- exports.normalizeUrl = normalizeUrl;
31
- /**
32
- * Convert URL to content file path
33
- * For example:
34
- * https://example.com/foo/bar.html?hoge=123 -> example.com/foo/bar~hoge=123.html
35
- * - Add directory index (index.html) if URL ends with slash
36
- * - Shorten too long basename with short hash
37
- * - Ignore dynamic params for example: ts=123
38
- * @param url
39
- * @returns
40
- */
41
- function requestContentFilePath(method, url) {
42
- const urlObj = normalizeUrl(url);
43
- const protocol = urlObj.protocol.replace(/:/g, '');
44
- const host = urlObj.host.replace(/:/g, '~');
45
- // Directory Index (index.html)
46
- let pathname = urlObj.pathname;
47
- if (pathname.endsWith('/')) {
48
- pathname += DirectoryIndex;
49
- }
50
- else {
51
- const ext = path_1.default.extname(pathname);
52
- if (ext === '') {
53
- pathname = path_1.default.join(pathname, DirectoryIndex);
54
- }
55
- }
56
- const dir = path_1.default.dirname(pathname);
57
- const ext = path_1.default.extname(pathname);
58
- const base = path_1.default.basename(pathname, ext);
59
- let filename = base;
60
- // Search params
61
- if (urlObj.search !== '') {
62
- // Remove dynamic params
63
- const search = IgnoreParamsRegex.reduce((search, re) => {
64
- return search.replace(re, '');
65
- }, urlObj.search);
66
- filename = [filename, search.slice(1)].join('~');
67
- }
68
- // Shorten too long basename
69
- if (filename.length > BasenameMaxLength) {
70
- const trunk = filename.slice(0, BasenameMaxLength);
71
- const hash = crypto_1.default.createHash('sha1');
72
- hash.update(filename);
73
- const digest = hash.digest('hex').slice(0, HashLength);
74
- filename = [trunk, digest].join('_');
75
- }
76
- // Extension
77
- filename += ext;
78
- // Content file relative path
79
- const relPath = path_1.default.join(dir, filename);
80
- return path_1.default.join(method, protocol, host, relPath);
81
- }
82
- exports.requestContentFilePath = requestContentFilePath;
83
- function parseContentTypeHeader(contentType) {
84
- const [mime, ...params] = contentType.split(/;/).map((s) => s.trim());
85
- const charsetParam = params.find((p) => p.startsWith('charset='));
86
- return {
87
- mime,
88
- charset: charsetParam ? charsetParam.slice('charset='.length) : undefined,
89
- };
90
- }
91
- exports.parseContentTypeHeader = parseContentTypeHeader;
92
- function stringifyContentTypeHeader(mime, charset, original) {
93
- const params = original ? original.split(/;/).map((s) => s.trim()) : [];
94
- if (mime)
95
- params[0] = mime;
96
- if (charset) {
97
- const charsetParamIndex = params.findIndex((p) => p.startsWith('charset='));
98
- if (charsetParamIndex >= 0)
99
- params.splice(charsetParamIndex, 1, `charset=${charset}`);
100
- else
101
- params.splice(1, 0, `charset=${charset}`);
102
- }
103
- return params.join('; ');
104
- }
105
- exports.stringifyContentTypeHeader = stringifyContentTypeHeader;
106
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9odHRwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLG9EQUEyQjtBQUMzQixnREFBdUI7QUFFdkIsTUFBTSxjQUFjLEdBQUcsWUFBWSxDQUFBO0FBQ25DLE1BQU0saUJBQWlCLEdBQUcsR0FBRyxDQUFBO0FBQzdCLE1BQU0sVUFBVSxHQUFHLENBQUMsQ0FBQTtBQUNwQixNQUFNLFlBQVksR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLGFBQWEsSUFBSSxJQUFJLENBQUE7QUFDdEQsTUFBTSxpQkFBaUIsR0FBRyxZQUFZLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsSUFBSSxNQUFNLENBQUMsV0FBVyxDQUFDLFNBQVMsRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFBO0FBSTFHOzs7OztHQUtHO0FBQ0gsU0FBZ0IsWUFBWSxDQUFDLEdBQWlCO0lBQzVDLE1BQU0sTUFBTSxHQUFHLE9BQU8sR0FBRyxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsSUFBSSxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQTtJQUUzRCx3QkFBd0I7SUFDeEIsSUFBSSxNQUFNLENBQUMsTUFBTSxLQUFLLEVBQUUsRUFBRTtRQUN4QixNQUFNLENBQUMsTUFBTSxHQUFHLGlCQUFpQixDQUFDLE1BQU0sQ0FBUyxDQUFDLE1BQU0sRUFBRSxFQUFFLEVBQUUsRUFBRTtZQUM5RCxPQUFPLE1BQU0sQ0FBQyxPQUFPLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFBO1FBQy9CLENBQUMsRUFBRSxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUE7S0FDbEI7SUFFRCxPQUFPLE1BQU0sQ0FBQTtBQUNmLENBQUM7QUFYRCxvQ0FXQztBQUVEOzs7Ozs7Ozs7R0FTRztBQUNILFNBQWdCLHNCQUFzQixDQUFD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
@@ -1,9 +0,0 @@
1
- export * from './encoding';
2
- export * from './formatting';
3
- export * from './http';
4
- export * from './inventory';
5
- export * from './logger';
6
- export * from './playback';
7
- export * from './proxy';
8
- export * from './recording';
9
- export * from './throttling';
@@ -1,26 +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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./encoding"), exports);
18
- __exportStar(require("./formatting"), exports);
19
- __exportStar(require("./http"), exports);
20
- __exportStar(require("./inventory"), exports);
21
- __exportStar(require("./logger"), exports);
22
- __exportStar(require("./playback"), exports);
23
- __exportStar(require("./proxy"), exports);
24
- __exportStar(require("./recording"), exports);
25
- __exportStar(require("./throttling"), exports);
26
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLDZDQUEwQjtBQUMxQiwrQ0FBNEI7QUFDNUIseUNBQXNCO0FBQ3RCLDhDQUEyQjtBQUMzQiwyQ0FBd0I7QUFDeEIsNkNBQTBCO0FBQzFCLDBDQUF1QjtBQUN2Qiw4Q0FBMkI7QUFDM0IsK0NBQTRCIn0=
@@ -1,39 +0,0 @@
1
- /// <reference types="node" />
2
- import { ContentEncodingType } from './encoding';
3
- import { HttpHeaders } from './http';
4
- export interface Resource {
5
- method: string;
6
- url: string;
7
- ttfbMs: number;
8
- mbps?: number;
9
- statusCode?: number;
10
- errorMessage?: string;
11
- rawHeaders?: HttpHeaders;
12
- contentEncoding?: ContentEncodingType;
13
- contentTypeMime?: string;
14
- contentTypeCharset?: string;
15
- contentFilePath?: string;
16
- minify?: boolean;
17
- }
18
- export interface Transaction {
19
- method: string;
20
- url: string;
21
- ttfbMs: number;
22
- statusCode?: number;
23
- errorMessage?: string;
24
- rawHeaders?: HttpHeaders;
25
- content?: Buffer;
26
- durationMs?: number;
27
- }
28
- export interface Inventory {
29
- entryUrl?: string;
30
- resources: Resource[];
31
- }
32
- export declare class InventoryRepository {
33
- dirPath: string;
34
- constructor(dirPath?: string);
35
- saveInventory(inventory: Inventory): Promise<void>;
36
- loadInventory(): Promise<Inventory>;
37
- saveTransactions(transactions: Transaction[]): Promise<Resource[]>;
38
- loadTransactions(resources: Resource[]): Promise<Transaction[]>;
39
- }
@@ -1,171 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.InventoryRepository = void 0;
7
- const fs_1 = __importDefault(require("fs"));
8
- const promises_1 = __importDefault(require("fs/promises"));
9
- const path_1 = __importDefault(require("path"));
10
- const encoding_1 = require("./encoding");
11
- const formatting_1 = require("./formatting");
12
- const http_1 = require("./http");
13
- const logger_1 = require("./logger");
14
- const InventoryDir = 'inventory';
15
- const IndexFile = 'index.json';
16
- class InventoryRepository {
17
- constructor(dirPath) {
18
- this.dirPath = dirPath || path_1.default.join(process.cwd(), InventoryDir);
19
- }
20
- async saveInventory(inventory) {
21
- const inventoryJson = JSON.stringify(inventory, null, 2);
22
- await promises_1.default.mkdir(this.dirPath, { recursive: true });
23
- await promises_1.default.writeFile(path_1.default.join(this.dirPath, IndexFile), inventoryJson);
24
- }
25
- async loadInventory() {
26
- const inventoryJson = await promises_1.default.readFile(path_1.default.join(this.dirPath, IndexFile), 'utf8');
27
- const inventory = JSON.parse(inventoryJson);
28
- return inventory;
29
- }
30
- async saveTransactions(transactions) {
31
- // To keep transactions order in Promise.all,
32
- // store transactions and resources in a map.
33
- const map = new Map();
34
- await promises_1.default.mkdir(this.dirPath, { recursive: true });
35
- const saveTransaction = async (transaction) => {
36
- const resource = {
37
- method: transaction.method,
38
- url: transaction.url,
39
- ttfbMs: transaction.ttfbMs,
40
- statusCode: transaction.statusCode,
41
- errorMessage: transaction.errorMessage,
42
- rawHeaders: transaction.rawHeaders,
43
- };
44
- // Headers
45
- if (transaction.rawHeaders) {
46
- if (transaction.rawHeaders['content-type']) {
47
- const { mime, charset } = (0, http_1.parseContentTypeHeader)(transaction.rawHeaders['content-type']);
48
- if (mime)
49
- resource.contentTypeMime = mime;
50
- if (charset)
51
- resource.contentTypeCharset = charset;
52
- }
53
- if (transaction.rawHeaders['content-encoding']) {
54
- const contentEncoding = transaction.rawHeaders['content-encoding'];
55
- if (contentEncoding)
56
- resource.contentEncoding = contentEncoding;
57
- }
58
- }
59
- // Mbps
60
- if (transaction.durationMs && transaction.content) {
61
- const contentBits = transaction.content.length * 8;
62
- const seconds = transaction.durationMs / 1000;
63
- const mega = 1024 * 1024;
64
- resource.mbps = contentBits / seconds / mega;
65
- }
66
- // Content
67
- if (transaction.content) {
68
- const steps = {};
69
- const contentFilePath = (0, http_1.requestContentFilePath)(resource.method, resource.url);
70
- const fullPath = path_1.default.join(this.dirPath, contentFilePath);
71
- // Content-Encoding
72
- steps.decoded = resource.contentEncoding
73
- ? await (0, encoding_1.decompress)(resource.contentEncoding, transaction.content)
74
- : transaction.content;
75
- // Try to make editable (utf8, beautify)
76
- steps.editable = steps.decoded;
77
- if ((0, formatting_1.isText)(resource.contentTypeMime)) {
78
- try {
79
- steps.editable = await (0, formatting_1.convertEditableText)(steps.decoded, resource.contentTypeMime, resource.contentTypeCharset);
80
- resource.contentTypeCharset = 'utf-8';
81
- }
82
- catch (err) {
83
- (0, logger_1.logger)().error({ err, resource }, `Formatting failed ${transaction.url}: ${err.message}`);
84
- }
85
- }
86
- await promises_1.default.mkdir(path_1.default.dirname(fullPath), { recursive: true });
87
- await promises_1.default.writeFile(fullPath, steps.editable);
88
- resource.contentFilePath = contentFilePath;
89
- }
90
- map.set(transaction, resource);
91
- };
92
- const tryToSaveTransaction = async (transaction) => {
93
- try {
94
- await saveTransaction(transaction);
95
- }
96
- catch (err) {
97
- (0, logger_1.logger)().error({ err, method: transaction.method, url: transaction.url }, `Failed to save transaction ${transaction.url}: ${err.message}`);
98
- }
99
- };
100
- await Promise.all(transactions.map(tryToSaveTransaction));
101
- // Restore transactions order after Promise.all
102
- const resources = transactions.reduce((resources, transaction) => {
103
- const resource = map.get(transaction);
104
- if (resource)
105
- resources.push(resource);
106
- return resources;
107
- }, []);
108
- return resources;
109
- }
110
- async loadTransactions(resources) {
111
- const map = new Map();
112
- const loadTransaction = async (resource) => {
113
- const transaction = {
114
- method: resource.method,
115
- url: resource.url,
116
- ttfbMs: resource.ttfbMs,
117
- statusCode: resource.statusCode,
118
- errorMessage: resource.errorMessage,
119
- rawHeaders: Object.assign({}, (resource.rawHeaders || {})),
120
- };
121
- // content
122
- if (resource.contentFilePath) {
123
- const fullPath = path_1.default.join(this.dirPath, resource.contentFilePath);
124
- if (fs_1.default.existsSync(fullPath)) {
125
- const content = await promises_1.default.readFile(fullPath);
126
- // encoding
127
- if (resource.contentEncoding) {
128
- transaction.content = await (0, encoding_1.compress)(resource.contentEncoding, content);
129
- transaction.rawHeaders['content-encoding'] = resource.contentEncoding;
130
- }
131
- else {
132
- transaction.content = content;
133
- delete transaction.rawHeaders['content-encoding'];
134
- }
135
- // length
136
- transaction.rawHeaders['content-length'] = `${transaction.content.length}`;
137
- // duration
138
- const bytesPerMs = resource.mbps ? (resource.mbps * 1024 * 1024) / 8 / 1000 : 0;
139
- transaction.durationMs = bytesPerMs ? content.length / bytesPerMs : 0;
140
- }
141
- }
142
- else {
143
- transaction.rawHeaders['content-length'] = '0';
144
- transaction.durationMs = 0;
145
- }
146
- // Content-Type
147
- if (resource.contentTypeMime) {
148
- transaction.rawHeaders['content-type'] = (0, http_1.stringifyContentTypeHeader)(resource.contentTypeMime, resource.contentTypeCharset);
149
- }
150
- map.set(resource, transaction);
151
- };
152
- const tryToLoadTransaction = async (resource) => {
153
- try {
154
- await loadTransaction(resource);
155
- }
156
- catch (err) {
157
- (0, logger_1.logger)().error({ err, resource }, `Loading transaction failed ${resource.url}: ${err.message}`);
158
- }
159
- };
160
- await Promise.all(resources.map(tryToLoadTransaction));
161
- const transactions = resources.reduce((transactions, resource) => {
162
- const transaction = map.get(resource);
163
- if (transaction)
164
- transactions.push(transaction);
165
- return transactions;
166
- }, []);
167
- return transactions;
168
- }
169
- }
170
- exports.InventoryRepository = InventoryRepository;
171
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW52ZW50b3J5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2ludmVudG9yeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSw0Q0FBbUI7QUFDbkIsMkRBQTZCO0FBQzdCLGdEQUF1QjtBQUV2Qix5Q0FBc0U7QUFDdEUsNkNBQTBEO0FBQzFELGlDQUFnSDtBQUNoSCxxQ0FBaUM7QUFFakMsTUFBTSxZQUFZLEdBQUcsV0FBVyxDQUFBO0FBQ2hDLE1BQU0sU0FBUyxHQUFHLFlBQVksQ0FBQTtBQWlDOUIsTUFBYSxtQkFBbUI7SUFHOUIsWUFBWSxPQUFnQjtRQUMxQixJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sSUFBSSxjQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsRUFBRSxZQUFZLENBQUMsQ0FBQTtJQUNsRSxDQUFDO0lBRUQsS0FBSyxDQUFDLGFBQWEsQ0FBQyxTQUFvQjtRQUN0QyxNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUE7UUFDeEQsTUFBTSxrQkFBRyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUE7UUFDbEQsTUFBTSxrQkFBRyxDQUFDLFNBQVMsQ0FBQyxjQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsU0FBUyxDQUFDLEVBQUUsYUFBYSxDQUFDLENBQUE7SUFDeEUsQ0FBQztJQUVELEtBQUssQ0FBQyxhQUFhO1FBQ2pCLE1BQU0sYUFBYSxHQUFHLE1BQU0sa0JBQUcsQ0FBQyxRQUFRLENBQUMsY0FBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLFNBQVMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFBO1FBQ3BGLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsYUFB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
@@ -1,20 +0,0 @@
1
- export declare const singleton: import("pino").Logger<{
2
- transport: {
3
- target: string;
4
- options: {
5
- levelFirst: boolean;
6
- hideObject: boolean;
7
- };
8
- };
9
- level: string;
10
- }>;
11
- export declare function logger(): import("pino").Logger<{
12
- transport: {
13
- target: string;
14
- options: {
15
- levelFirst: boolean;
16
- hideObject: boolean;
17
- };
18
- };
19
- level: string;
20
- }>;
@@ -1,22 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.logger = exports.singleton = void 0;
7
- const pino_1 = __importDefault(require("pino"));
8
- exports.singleton = (0, pino_1.default)({
9
- transport: {
10
- target: 'pino-pretty',
11
- options: {
12
- levelFirst: true,
13
- hideObject: Boolean(!process.env.LOG_OBJECTS),
14
- },
15
- },
16
- level: process.env.LOG_LEVEL || 'info',
17
- });
18
- function logger() {
19
- return exports.singleton;
20
- }
21
- exports.logger = logger;
22
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9nZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2xvZ2dlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxnREFBdUI7QUFFVixRQUFBLFNBQVMsR0FBRyxJQUFBLGNBQUksRUFBQztJQUM1QixTQUFTLEVBQUU7UUFDVCxNQUFNLEVBQUUsYUFBYTtRQUNyQixPQUFPLEVBQUU7WUFDUCxVQUFVLEVBQUUsSUFBSTtZQUNoQixVQUFVLEVBQUUsT0FBTyxDQUFDLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUM7U0FDOUM7S0FDRjtJQUNELEtBQUssRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLFNBQVMsSUFBSSxNQUFNO0NBQ3ZDLENBQUMsQ0FBQTtBQUVGLFNBQWdCLE1BQU07SUFDcEIsT0FBTyxpQkFBUyxDQUFBO0FBQ2xCLENBQUM7QUFGRCx3QkFFQyJ9
File without changes
@@ -1,47 +0,0 @@
1
- // import Fsp from 'fs/promises'
2
- // import Path from 'path'
3
- // import { Inventory, PlaybackResource } from '../common'
4
- // import { compress } from '../encoding'
5
- // type ResourceIndex = Map<string, Map<string, PlaybackResource>>
6
- // const ContentChunkSize = 16 * 1024
7
- // export class PlaybackInventory {
8
- // playbackResources: PlaybackResource[] = []
9
- // playbackResourcesIndex: ResourceIndex = new Map()
10
- // async load(dirPath: string) {
11
- // const inventoryPath = Path.join(dirPath, 'inventory.json')
12
- // const inventoryJson = await Fsp.readFile(inventoryPath, 'utf8')
13
- // const inventory: Inventory = JSON.parse(inventoryJson)
14
- // for (const resource of inventory.resources) {
15
- // try {
16
- // const playbackResource: PlaybackResource = {
17
- // method: resource.method,
18
- // url: resource.url,
19
- // ttfbMs: resource.ttfbMs,
20
- // statusCode: resource.statusCode,
21
- // contentTypeMime: resource.contentTypeMime,
22
- // contentEncoding: resource.contentEncoding,
23
- // headers: resource.headers,
24
- // contentChunks: [],
25
- // contentLength: 0,
26
- // durationMs: 0,
27
- // }
28
- // const content = await Fsp.readFile(Path.join(dirPath, resource.contentFilePath))
29
- // const encoded = await compress(resource.contentEncoding, content)
30
- // for (let i = 0; i < encoded.length; i += ContentChunkSize) {
31
- // playbackResource.contentChunks.push(encoded.subarray(i, i + ContentChunkSize))
32
- // }
33
- // playbackResource.contentLength = encoded.length
34
- // this.playbackResources.push(playbackResource)
35
- // const urlIndex = (this.playbackResourcesIndex[resource.method] ??= new Map())
36
- // urlIndex[resource.url] = playbackResource
37
- // } catch (err) {
38
- // // TODO error handling
39
- // console.error(err)
40
- // }
41
- // }
42
- // }
43
- // find(method: string, url: string) {
44
- // return this.playbackResourcesIndex.get(method)?.get(url)
45
- // }
46
- // }
47
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW52ZW50b3J5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3BsYXliYWNrL2ludmVudG9yeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxnQ0FBZ0M7QUFDaEMsMEJBQTBCO0FBRTFCLDBEQUEwRDtBQUMxRCx5Q0FBeUM7QUFFekMsa0VBQWtFO0FBQ2xFLHFDQUFxQztBQUVyQyxtQ0FBbUM7QUFDbkMsK0NBQStDO0FBRS9DLHNEQUFzRDtBQUV0RCxrQ0FBa0M7QUFDbEMsaUVBQWlFO0FBQ2pFLHNFQUFzRTtBQUN0RSw2REFBNkQ7QUFFN0Qsb0RBQW9EO0FBQ3BELGNBQWM7QUFDZCx1REFBdUQ7QUFDdkQscUNBQXFDO0FBQ3JDLCtCQUErQjtBQUMvQixxQ0FBcUM7QUFDckMsNkNBQTZDO0FBQzdDLHVEQUF1RDtBQUN2RCx1REFBdUQ7QUFDdkQsdUNBQXVDO0FBQ3ZDLCtCQUErQjtBQUMvQiw4QkFBOEI7QUFDOUIsMkJBQTJCO0FBQzNCLFlBQVk7QUFFWiwyRkFBMkY7QUFDM0YsNEVBQTRFO0FBQzVFLHVFQUF1RTtBQUN2RSwyRkFBMkY7QUFDM0YsWUFBWTtBQUNaLDBEQUEwRDtBQUUxRCx3REFBd0Q7QUFDeEQsd0ZBQXdGO0FBQ3hGLG9EQUFvRDtBQUNwRCx3QkFBd0I7QUFDeEIsaUNBQWlDO0FBQ2pDLDZCQUE2QjtBQUM3QixVQUFVO0FBQ1YsUUFBUTtBQUNSLE1BQU07QUFFTix3Q0FBd0M7QUFDeEMsK0RBQStEO0FBQy9ELE1BQU07QUFDTixJQUFJIn0=
@@ -1,22 +0,0 @@
1
- /// <reference types="node" />
2
- import { HttpHeaders } from './http';
3
- import { Inventory } from './inventory';
4
- import { Proxy, WithProxyOptions } from './proxy';
5
- export interface PlaybackTransaction {
6
- method: string;
7
- url: string;
8
- ttfbMs: number;
9
- statusCode?: number;
10
- err?: Error;
11
- rawHeaders?: HttpHeaders;
12
- contentChunks: Buffer[];
13
- contentLength: number;
14
- durationMs: number;
15
- }
16
- export declare class PlaybackProxy extends Proxy {
17
- transactionsMap: Map<string, Map<string, PlaybackTransaction>>;
18
- loadTransactions(inventory: Inventory): Promise<void>;
19
- setup(): Promise<void>;
20
- shutdown(): Promise<void>;
21
- }
22
- export declare function withPlaybackProxy(fn: (proxy: PlaybackProxy) => Promise<void>, options?: WithProxyOptions): Promise<void | PlaybackProxy>;
@@ -1,111 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.withPlaybackProxy = exports.PlaybackProxy = void 0;
4
- const logger_1 = require("./logger");
5
- const proxy_1 = require("./proxy");
6
- const ChunkSize = 1024 * 16;
7
- class PlaybackProxy extends proxy_1.Proxy {
8
- constructor() {
9
- super(...arguments);
10
- this.transactionsMap = new Map();
11
- }
12
- async loadTransactions(inventory) {
13
- const transactions = await this.inventoryRepository.loadTransactions(inventory.resources);
14
- for (const transaction of transactions) {
15
- const playbackTransaction = {
16
- method: transaction.method,
17
- url: transaction.url,
18
- ttfbMs: transaction.ttfbMs,
19
- statusCode: transaction.statusCode,
20
- err: transaction.errorMessage ? new Error(transaction.errorMessage) : undefined,
21
- rawHeaders: transaction.rawHeaders || {},
22
- contentChunks: [],
23
- contentLength: 0,
24
- durationMs: transaction.durationMs || 0,
25
- };
26
- if (transaction.content) {
27
- const maxChunks = 10;
28
- const minInterval = 10;
29
- const chunks = Math.min(maxChunks, Math.floor(playbackTransaction.durationMs / minInterval));
30
- playbackTransaction.contentChunks = [];
31
- const chunkSize = Math.max(ChunkSize, Math.ceil(transaction.content.length / chunks));
32
- for (let i = 0; i <= transaction.content.length; i += chunkSize) {
33
- playbackTransaction.contentChunks.push(transaction.content.subarray(i, i + chunkSize));
34
- }
35
- }
36
- if (!this.transactionsMap.has(transaction.method)) {
37
- this.transactionsMap.set(transaction.method, new Map());
38
- }
39
- this.transactionsMap.get(transaction.method).set(transaction.url, playbackTransaction);
40
- }
41
- }
42
- async setup() {
43
- const inventory = await this.inventoryRepository.loadInventory();
44
- await this.loadTransactions(inventory);
45
- if (inventory.entryUrl)
46
- this.entryUrl = inventory.entryUrl;
47
- let requestNumber = 1;
48
- this.proxy.onRequest((ctx, onRequestComplete) => {
49
- var _a;
50
- const number = requestNumber++;
51
- const identifier = proxy_1.Proxy.contextRequest(ctx);
52
- const transaction = (_a = this.transactionsMap.get(identifier.method)) === null || _a === void 0 ? void 0 : _a.get(identifier.url);
53
- if (!transaction) {
54
- (0, logger_1.logger)().warn({ number, identifier }, `Request #${number} ${identifier.url} (${identifier.method}) not found in inventory`);
55
- return;
56
- }
57
- const contentStream = this.createThrottlingTransform() || ctx.proxyToClientResponse;
58
- if (contentStream !== ctx.proxyToClientResponse) {
59
- contentStream.pipe(ctx.proxyToClientResponse);
60
- }
61
- (0, logger_1.logger)().debug({ number, identifier }, `Request #${number} ${transaction.url} started`);
62
- ctx.onError((_, err) => {
63
- (0, logger_1.logger)().warn({ number, identifier, err }, `Request #${number} ${transaction.url} failed: ${err.message}`);
64
- });
65
- setTimeout(() => {
66
- // Error
67
- if (transaction.err) {
68
- return onRequestComplete(transaction.err);
69
- }
70
- // Status code
71
- ctx.proxyToClientResponse.statusCode = transaction.statusCode || 500;
72
- // Headers
73
- if (transaction.rawHeaders) {
74
- for (const [key, value] of Object.entries(transaction.rawHeaders)) {
75
- if (ctx.proxyToClientResponse.headersSent)
76
- break;
77
- ctx.proxyToClientResponse.setHeader(key, value);
78
- }
79
- }
80
- // Empty content body
81
- if (!transaction.contentChunks || transaction.contentChunks.length === 0) {
82
- contentStream.end();
83
- return;
84
- }
85
- // Content body
86
- const chunks = [...transaction.contentChunks];
87
- const intervalMs = transaction.durationMs / transaction.contentChunks.length;
88
- const interval = setInterval(() => {
89
- const chunk = chunks.shift();
90
- if (chunk) {
91
- contentStream.write(chunk);
92
- }
93
- if (chunks.length === 0) {
94
- clearInterval(interval);
95
- contentStream.end();
96
- (0, logger_1.logger)().debug({ number, identifier }, `Request #${number} ${transaction.url} completed`);
97
- }
98
- }, intervalMs);
99
- }, transaction.ttfbMs);
100
- });
101
- }
102
- async shutdown() {
103
- // nothing to do
104
- }
105
- }
106
- exports.PlaybackProxy = PlaybackProxy;
107
- async function withPlaybackProxy(fn, options) {
108
- return await (0, proxy_1.withProxy)(PlaybackProxy, fn, options || {});
109
- }
110
- exports.withPlaybackProxy = withPlaybackProxy;
111
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGxheWJhY2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcGxheWJhY2sudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBRUEscUNBQWlDO0FBQ2pDLG1DQUE0RDtBQUU1RCxNQUFNLFNBQVMsR0FBRyxJQUFJLEdBQUcsRUFBRSxDQUFBO0FBYzNCLE1BQWEsYUFBYyxTQUFRLGFBQUs7SUFBeEM7O1FBQ0Usb0JBQWUsR0FBa0QsSUFBSSxHQUFHLEVBQUUsQ0FBQTtJQWdINUUsQ0FBQztJQTlHQyxLQUFLLENBQUMsZ0JBQWdCLENBQUMsU0FBb0I7UUFDekMsTUFBTSxZQUFZLEdBQUcsTUFBTSxJQUFJLENBQUMsbUJBQW1CLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxDQUFDLFNBQVMsQ0FBQyxDQUFBO1FBRXpGLEtBQUssTUFBTSxXQUFXLElBQUksWUFBWSxFQUFFO1lBQ3RDLE1BQU0sbUJBQW1CLEdBQXdCO2dCQUMvQyxNQUFNLEVBQUUsV0FBVyxDQUFDLE1BQU07Z0JBQzFCLEdBQUcsRUFBRSxXQUFXLENBQUMsR0FBRztnQkFDcEIsTUFBTSxFQUFFLFdBQVcsQ0FBQyxNQUFNO2dCQUMxQixVQUFVLEVBQUUsV0FBVyxDQUFDLFVBQVU7Z0JBQ2xDLEdBQUcsRUFBRSxXQUFXLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxXQUFXLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVM7Z0JBQy9FLFVBQVUsRUFBRSxXQUFXLENBQUMsVUFBVSxJQUFJLEVBQUU7Z0JBQ3hDLGFBQWEsRUFBRSxFQUFFO2dCQUNqQixhQUFhLEVBQUUsQ0FBQztnQkFDaEIsVUFBVSxFQUFFLFdBQVcsQ0FBQyxVQUFVLElBQUksQ0FBQzthQUN4QyxDQUFBO1lBRUQsSUFBSSxXQUFXLENBQUMsT0FBTyxFQUFFO2dCQUN2QixNQUFNLFNBQVMsR0FBRyxFQUFFLENBQUE7Z0JBQ3BCLE1BQU0sV0FBVyxHQUFHLEVBQUUsQ0FB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