@pagepocket/cli 0.9.2 → 0.11.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.
- package/dist/commands/archive.js +188 -96
- package/dist/commands/strategy/add.js +34 -0
- package/dist/commands/strategy/doctor.js +34 -0
- package/dist/commands/strategy/ls.js +25 -0
- package/dist/commands/strategy/pin.js +21 -0
- package/dist/commands/strategy/remove.js +21 -0
- package/dist/commands/strategy/update.js +34 -0
- package/dist/services/config-service.js +54 -22
- package/dist/services/strategy/builtin-strategy-registry.js +32 -0
- package/dist/services/strategy/strategy-analyze.js +67 -0
- package/dist/services/strategy/strategy-config.js +25 -0
- package/dist/services/strategy/strategy-fetch.js +71 -0
- package/dist/services/strategy/strategy-io.js +59 -0
- package/dist/services/strategy/strategy-normalize.js +51 -0
- package/dist/services/strategy/strategy-pack-read.js +54 -0
- package/dist/services/strategy/strategy-pack-store.js +45 -0
- package/dist/services/strategy/strategy-service.js +301 -0
- package/dist/services/strategy/types.js +2 -0
- package/dist/services/units/unit-store.js +54 -0
- package/dist/services/units/unit-validate.js +28 -0
- package/dist/services/user-packages/parse-pinned-spec.js +43 -0
- package/dist/services/user-packages/user-package-installer.js +47 -0
- package/dist/services/user-packages/user-package-store.js +126 -0
- package/dist/utils/array.js +7 -0
- package/dist/utils/parse-plugin-spec.js +7 -1
- package/package.json +11 -10
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UnitStore = void 0;
|
|
4
|
+
const user_package_installer_1 = require("../user-packages/user-package-installer");
|
|
5
|
+
const parse_pinned_spec_1 = require("../user-packages/parse-pinned-spec");
|
|
6
|
+
const user_package_store_1 = require("../user-packages/user-package-store");
|
|
7
|
+
const unit_validate_1 = require("./unit-validate");
|
|
8
|
+
const UNITS_KIND = "units";
|
|
9
|
+
const UNITS_PACKAGE_JSON_NAME = "pagepocket-user-units";
|
|
10
|
+
class UnitStore {
|
|
11
|
+
constructor(configService) {
|
|
12
|
+
this.store = new user_package_store_1.UserPackageStore(configService, UNITS_KIND);
|
|
13
|
+
}
|
|
14
|
+
getInstallDir() {
|
|
15
|
+
return this.store.getInstallDir();
|
|
16
|
+
}
|
|
17
|
+
readInstalledDependencyVersions() {
|
|
18
|
+
return this.store.readInstalledDependencyVersions(UNITS_PACKAGE_JSON_NAME);
|
|
19
|
+
}
|
|
20
|
+
readInstalledPackageMeta(packageName) {
|
|
21
|
+
return this.store.readInstalledPackageMeta(UNITS_PACKAGE_JSON_NAME, packageName);
|
|
22
|
+
}
|
|
23
|
+
installPinned(ref) {
|
|
24
|
+
const pinned = (0, parse_pinned_spec_1.parsePinnedSpec)(ref);
|
|
25
|
+
(0, user_package_installer_1.installPinnedPackage)(this.store, {
|
|
26
|
+
packageJsonName: UNITS_PACKAGE_JSON_NAME,
|
|
27
|
+
packageSpec: pinned.spec
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
updateToLatest(packageName) {
|
|
31
|
+
(0, user_package_installer_1.updatePackageToLatest)(this.store, {
|
|
32
|
+
packageJsonName: UNITS_PACKAGE_JSON_NAME,
|
|
33
|
+
packageName
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
async importUnitModule(packageName) {
|
|
37
|
+
return this.store.importModule(UNITS_PACKAGE_JSON_NAME, packageName);
|
|
38
|
+
}
|
|
39
|
+
async instantiateFromRef(ref, args) {
|
|
40
|
+
const pinned = (0, parse_pinned_spec_1.parsePinnedSpec)(ref);
|
|
41
|
+
const mod = await this.importUnitModule(pinned.name);
|
|
42
|
+
const def = mod.default;
|
|
43
|
+
if (typeof def !== "function") {
|
|
44
|
+
throw new Error(`Unit ${pinned.name} must default export a constructor.`);
|
|
45
|
+
}
|
|
46
|
+
const ctor = def;
|
|
47
|
+
const instance = new ctor(...args);
|
|
48
|
+
if (!(0, unit_validate_1.isUnitLike)(instance)) {
|
|
49
|
+
throw new Error(`Unit ${pinned.name} default export did not construct a Unit.`);
|
|
50
|
+
}
|
|
51
|
+
return instance;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.UnitStore = UnitStore;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isUnitLike = void 0;
|
|
4
|
+
const isRecord = (value) => {
|
|
5
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
6
|
+
};
|
|
7
|
+
const isCallable = (value) => {
|
|
8
|
+
return typeof value === "function";
|
|
9
|
+
};
|
|
10
|
+
const isUnitLike = (value) => {
|
|
11
|
+
if (!isRecord(value)) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
if (typeof value.id !== "string" || value.id.trim().length === 0) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
if (typeof value.kind !== "string" || value.kind.trim().length === 0) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
if (!isCallable(value.run)) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
if (!isCallable(value.merge)) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
return true;
|
|
27
|
+
};
|
|
28
|
+
exports.isUnitLike = isUnitLike;
|
|
@@ -0,0 +1,43 @@
|
|
|
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.parsePinnedSpec = void 0;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const node_module_1 = require("node:module");
|
|
9
|
+
const isRecord = (value) => {
|
|
10
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
11
|
+
};
|
|
12
|
+
const isExactSemver = (value) => {
|
|
13
|
+
return /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(value);
|
|
14
|
+
};
|
|
15
|
+
const parsePinnedSpec = (input) => {
|
|
16
|
+
const trimmed = input.trim();
|
|
17
|
+
if (!trimmed) {
|
|
18
|
+
throw new Error("package spec is empty");
|
|
19
|
+
}
|
|
20
|
+
const req = (0, node_module_1.createRequire)(typeof __filename === "string" ? __filename : node_path_1.default.join(process.cwd(), "package.json"));
|
|
21
|
+
const npa = req("npm-package-arg");
|
|
22
|
+
const parsed = npa(trimmed);
|
|
23
|
+
if (!isRecord(parsed)) {
|
|
24
|
+
throw new Error(`Invalid package spec: ${trimmed}`);
|
|
25
|
+
}
|
|
26
|
+
const name = parsed.name;
|
|
27
|
+
if (typeof name !== "string" || name.trim().length === 0) {
|
|
28
|
+
throw new Error(`Invalid package spec (missing name): ${trimmed}`);
|
|
29
|
+
}
|
|
30
|
+
const type = parsed.type;
|
|
31
|
+
if (type !== "version") {
|
|
32
|
+
throw new Error(`Package spec must pin an exact version (got ${String(type)}): ${trimmed}`);
|
|
33
|
+
}
|
|
34
|
+
const rawSpec = parsed.rawSpec;
|
|
35
|
+
if (typeof rawSpec !== "string" || rawSpec.trim().length === 0) {
|
|
36
|
+
throw new Error(`Package spec must pin an exact version: ${trimmed}`);
|
|
37
|
+
}
|
|
38
|
+
if (!isExactSemver(rawSpec)) {
|
|
39
|
+
throw new Error(`Package spec must pin an exact version (got ${rawSpec}): ${trimmed}`);
|
|
40
|
+
}
|
|
41
|
+
return { name, version: rawSpec, spec: `${name}@${rawSpec}` };
|
|
42
|
+
};
|
|
43
|
+
exports.parsePinnedSpec = parsePinnedSpec;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.updatePackageToLatest = exports.installPinnedPackage = void 0;
|
|
4
|
+
const node_child_process_1 = require("node:child_process");
|
|
5
|
+
const toError = (value) => {
|
|
6
|
+
if (value instanceof Error) {
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
return new Error(String(value));
|
|
10
|
+
};
|
|
11
|
+
const tryRun = (cwd, cmd, args) => {
|
|
12
|
+
const r = (0, node_child_process_1.spawnSync)(cmd, args, {
|
|
13
|
+
cwd,
|
|
14
|
+
stdio: "inherit",
|
|
15
|
+
shell: false
|
|
16
|
+
});
|
|
17
|
+
if (r.error) {
|
|
18
|
+
return { ok: false, error: toError(r.error) };
|
|
19
|
+
}
|
|
20
|
+
if (typeof r.status === "number" && r.status !== 0) {
|
|
21
|
+
return { ok: false, error: new Error(`${cmd} exited with code ${r.status}`) };
|
|
22
|
+
}
|
|
23
|
+
return { ok: true };
|
|
24
|
+
};
|
|
25
|
+
const installWithFallback = (installDir, packageSpec) => {
|
|
26
|
+
const pnpm = tryRun(installDir, "pnpm", ["add", "--save-exact", "--silent", packageSpec]);
|
|
27
|
+
if (pnpm.ok) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const npm = tryRun(installDir, "npm", ["install", "--save-exact", "--silent", packageSpec]);
|
|
31
|
+
if (npm.ok) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
throw new Error(`Failed to install ${packageSpec}. Ensure pnpm or npm is available.`);
|
|
35
|
+
};
|
|
36
|
+
const installPinnedPackage = (store, input) => {
|
|
37
|
+
store.ensureInstallDirPackageJson(input.packageJsonName);
|
|
38
|
+
const installDir = store.getInstallDir();
|
|
39
|
+
installWithFallback(installDir, input.packageSpec);
|
|
40
|
+
};
|
|
41
|
+
exports.installPinnedPackage = installPinnedPackage;
|
|
42
|
+
const updatePackageToLatest = (store, input) => {
|
|
43
|
+
store.ensureInstallDirPackageJson(input.packageJsonName);
|
|
44
|
+
const installDir = store.getInstallDir();
|
|
45
|
+
installWithFallback(installDir, `${input.packageName}@latest`);
|
|
46
|
+
};
|
|
47
|
+
exports.updatePackageToLatest = updatePackageToLatest;
|
|
@@ -0,0 +1,126 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.UserPackageStore = void 0;
|
|
40
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
41
|
+
const node_module_1 = require("node:module");
|
|
42
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
43
|
+
const node_url_1 = require("node:url");
|
|
44
|
+
const parse_json_1 = require("../../utils/parse-json");
|
|
45
|
+
const isRecord = (value) => {
|
|
46
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
47
|
+
};
|
|
48
|
+
const isStringRecord = (value) => {
|
|
49
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return Object.values(value).every((v) => typeof v === "string");
|
|
53
|
+
};
|
|
54
|
+
class UserPackageStore {
|
|
55
|
+
constructor(configService, kind) {
|
|
56
|
+
this.configService = configService;
|
|
57
|
+
this.kind = kind;
|
|
58
|
+
}
|
|
59
|
+
getInstallDir() {
|
|
60
|
+
return node_path_1.default.join(this.configService.getDataDir(), this.kind);
|
|
61
|
+
}
|
|
62
|
+
ensureInstallDir() {
|
|
63
|
+
node_fs_1.default.mkdirSync(this.getInstallDir(), { recursive: true });
|
|
64
|
+
}
|
|
65
|
+
ensureInstallDirPackageJson(packageJsonName) {
|
|
66
|
+
this.ensureInstallDir();
|
|
67
|
+
const pkgJsonPath = node_path_1.default.join(this.getInstallDir(), "package.json");
|
|
68
|
+
if (node_fs_1.default.existsSync(pkgJsonPath)) {
|
|
69
|
+
return pkgJsonPath;
|
|
70
|
+
}
|
|
71
|
+
const pkgJson = {
|
|
72
|
+
name: packageJsonName,
|
|
73
|
+
private: true,
|
|
74
|
+
version: "0.0.0"
|
|
75
|
+
};
|
|
76
|
+
node_fs_1.default.writeFileSync(pkgJsonPath, `${JSON.stringify(pkgJson, undefined, 2)}\n`, "utf8");
|
|
77
|
+
return pkgJsonPath;
|
|
78
|
+
}
|
|
79
|
+
createRequire(packageJsonName) {
|
|
80
|
+
const pkgJsonPath = this.ensureInstallDirPackageJson(packageJsonName);
|
|
81
|
+
return (0, node_module_1.createRequire)(pkgJsonPath);
|
|
82
|
+
}
|
|
83
|
+
resolveInstalledPackageJsonPath(packageJsonName, packageName) {
|
|
84
|
+
const req = this.createRequire(packageJsonName);
|
|
85
|
+
return req.resolve(`${packageName}/package.json`);
|
|
86
|
+
}
|
|
87
|
+
readInstalledPackageMeta(packageJsonName, packageName) {
|
|
88
|
+
try {
|
|
89
|
+
const pkgJsonPath = this.resolveInstalledPackageJsonPath(packageJsonName, packageName);
|
|
90
|
+
const text = node_fs_1.default.readFileSync(pkgJsonPath, "utf8");
|
|
91
|
+
const json = (0, parse_json_1.parseJson)(text);
|
|
92
|
+
if (!json.ok || !isRecord(json.value)) {
|
|
93
|
+
return { name: packageName };
|
|
94
|
+
}
|
|
95
|
+
const o = json.value;
|
|
96
|
+
return {
|
|
97
|
+
name: typeof o.name === "string" ? o.name : packageName,
|
|
98
|
+
description: typeof o.description === "string" ? o.description : undefined,
|
|
99
|
+
version: typeof o.version === "string" ? o.version : undefined
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
readInstalledDependencyVersions(packageJsonName) {
|
|
107
|
+
const pkgJsonPath = this.ensureInstallDirPackageJson(packageJsonName);
|
|
108
|
+
const text = node_fs_1.default.readFileSync(pkgJsonPath, "utf8");
|
|
109
|
+
const parsed = (0, parse_json_1.parseJson)(text);
|
|
110
|
+
if (!parsed.ok || !isRecord(parsed.value)) {
|
|
111
|
+
return {};
|
|
112
|
+
}
|
|
113
|
+
const dependencies = parsed.value.dependencies;
|
|
114
|
+
if (!isStringRecord(dependencies)) {
|
|
115
|
+
return {};
|
|
116
|
+
}
|
|
117
|
+
return dependencies;
|
|
118
|
+
}
|
|
119
|
+
async importModule(packageJsonName, packageName) {
|
|
120
|
+
const req = this.createRequire(packageJsonName);
|
|
121
|
+
const resolved = req.resolve(packageName);
|
|
122
|
+
const mod = await Promise.resolve(`${(0, node_url_1.pathToFileURL)(resolved).href}`).then(s => __importStar(require(s)));
|
|
123
|
+
return isRecord(mod) ? mod : {};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
exports.UserPackageStore = UserPackageStore;
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.parsePluginSpec = void 0;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const node_module_1 = require("node:module");
|
|
4
9
|
const parsePluginSpec = (spec) => {
|
|
5
10
|
const trimmed = spec.trim();
|
|
6
11
|
if (!trimmed) {
|
|
7
12
|
throw new Error("plugin spec is empty");
|
|
8
13
|
}
|
|
9
|
-
const
|
|
14
|
+
const req = (0, node_module_1.createRequire)(typeof __filename === "string" ? __filename : node_path_1.default.join(process.cwd(), "package.json"));
|
|
15
|
+
const npa = req("npm-package-arg");
|
|
10
16
|
const parsed = npa(trimmed);
|
|
11
17
|
if (!parsed || typeof parsed.name !== "string" || !parsed.name) {
|
|
12
18
|
throw new Error(`Invalid plugin spec: ${trimmed}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pagepocket/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "CLI for capturing offline snapshots of web pages.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -21,15 +21,16 @@
|
|
|
21
21
|
"koa-static": "^5.0.0",
|
|
22
22
|
"npm-package-arg": "^13.0.2",
|
|
23
23
|
"ora": "^9.0.0",
|
|
24
|
-
"@pagepocket/build-snapshot-unit": "0.
|
|
25
|
-
"@pagepocket/capture-http-lighterceptor-unit": "0.
|
|
26
|
-
"@pagepocket/
|
|
27
|
-
"@pagepocket/capture-http-puppeteer-unit": "0.
|
|
28
|
-
"@pagepocket/
|
|
29
|
-
"@pagepocket/
|
|
30
|
-
"@pagepocket/
|
|
31
|
-
"@pagepocket/plugin-yt-dlp": "0.
|
|
32
|
-
"@pagepocket/
|
|
24
|
+
"@pagepocket/build-snapshot-unit": "0.11.0",
|
|
25
|
+
"@pagepocket/capture-http-lighterceptor-unit": "0.11.0",
|
|
26
|
+
"@pagepocket/capture-http-cdp-unit": "0.11.0",
|
|
27
|
+
"@pagepocket/capture-http-puppeteer-unit": "0.11.0",
|
|
28
|
+
"@pagepocket/contracts": "0.11.0",
|
|
29
|
+
"@pagepocket/lib": "0.11.0",
|
|
30
|
+
"@pagepocket/builtin-strategy": "0.11.0",
|
|
31
|
+
"@pagepocket/plugin-yt-dlp": "0.11.0",
|
|
32
|
+
"@pagepocket/single-file-unit": "0.11.0",
|
|
33
|
+
"@pagepocket/write-down-unit": "0.11.0"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
36
|
"@types/koa": "^2.15.0",
|