@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
package/dist/commands/archive.js
CHANGED
|
@@ -1,86 +1,208 @@
|
|
|
1
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
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
const node_module_1 = require("node:module");
|
|
40
|
+
const node_url_1 = require("node:url");
|
|
6
41
|
const core_1 = require("@oclif/core");
|
|
7
|
-
const build_snapshot_unit_1 = require("@pagepocket/build-snapshot-unit");
|
|
8
|
-
const capture_http_cdp_unit_1 = require("@pagepocket/capture-http-cdp-unit");
|
|
9
|
-
const capture_http_lighterceptor_unit_1 = require("@pagepocket/capture-http-lighterceptor-unit");
|
|
10
|
-
const capture_http_puppeteer_unit_1 = require("@pagepocket/capture-http-puppeteer-unit");
|
|
11
42
|
const lib_1 = require("@pagepocket/lib");
|
|
12
|
-
const single_file_unit_1 = require("@pagepocket/single-file-unit");
|
|
13
|
-
const write_down_unit_1 = require("@pagepocket/write-down-unit");
|
|
14
43
|
const chalk_1 = __importDefault(require("chalk"));
|
|
15
44
|
const load_configured_plugins_1 = require("../services/load-configured-plugins");
|
|
16
|
-
const
|
|
45
|
+
const config_service_1 = require("../services/config-service");
|
|
46
|
+
const builtin_strategy_registry_1 = require("../services/strategy/builtin-strategy-registry");
|
|
47
|
+
const strategy_service_1 = require("../services/strategy/strategy-service");
|
|
48
|
+
const strategy_normalize_1 = require("../services/strategy/strategy-normalize");
|
|
49
|
+
const parse_pinned_spec_1 = require("../services/user-packages/parse-pinned-spec");
|
|
50
|
+
const unit_validate_1 = require("../services/units/unit-validate");
|
|
51
|
+
const unit_store_1 = require("../services/units/unit-store");
|
|
52
|
+
const array_1 = require("../utils/array");
|
|
17
53
|
const with_spinner_1 = require("../utils/with-spinner");
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
54
|
+
const buildMissingStrategyError = (input) => {
|
|
55
|
+
const available = (0, array_1.uniq)([...input.installedNames, ...(0, builtin_strategy_registry_1.listBuiltinStrategyNames)()])
|
|
56
|
+
.filter((name) => name.trim().length > 0)
|
|
57
|
+
.sort((left, right) => left.localeCompare(right));
|
|
58
|
+
const suffix = available.length > 0 ? ` Available strategies: ${available.join(", ")}` : " No strategies found.";
|
|
59
|
+
return new Error(`Strategy not found: ${input.strategyName}.${suffix}`);
|
|
60
|
+
};
|
|
61
|
+
const resolveStrategy = (input) => {
|
|
62
|
+
const installedNames = input.strategyService.listInstalledStrategyNames();
|
|
63
|
+
if (installedNames.includes(input.strategyName)) {
|
|
64
|
+
return {
|
|
65
|
+
name: input.strategyName,
|
|
66
|
+
strategyFile: input.strategyService.readStrategy(input.strategyName),
|
|
67
|
+
source: "installed"
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const builtin = (0, builtin_strategy_registry_1.readBuiltinStrategy)(input.strategyName);
|
|
71
|
+
if (builtin) {
|
|
72
|
+
return {
|
|
73
|
+
name: input.strategyName,
|
|
74
|
+
strategyFile: builtin,
|
|
75
|
+
source: "builtin"
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
throw buildMissingStrategyError({
|
|
79
|
+
strategyName: input.strategyName,
|
|
80
|
+
installedNames
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
const assertNoDuplicateUnitIds = (input) => {
|
|
84
|
+
const seen = new Set();
|
|
85
|
+
const dup = input.units.find((u) => {
|
|
86
|
+
if (seen.has(u.id)) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
seen.add(u.id);
|
|
90
|
+
return false;
|
|
91
|
+
});
|
|
92
|
+
if (dup) {
|
|
93
|
+
throw new Error(`Duplicate unit id detected in strategy ${input.strategyName}: ${dup.id}`);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
const loadInstalledStrategyUnits = async (input) => {
|
|
97
|
+
const strategyService = new strategy_service_1.StrategyService(input.configService);
|
|
98
|
+
const unitStore = new unit_store_1.UnitStore(input.configService);
|
|
99
|
+
strategyService.ensureConfigFileExists();
|
|
100
|
+
const strategyFile = strategyService.readStrategy(input.strategyName);
|
|
101
|
+
const normalized = (0, strategy_normalize_1.normalizeStrategyUnits)(strategyFile);
|
|
102
|
+
const installed = unitStore.readInstalledDependencyVersions();
|
|
103
|
+
const drift = normalized.flatMap((u) => {
|
|
104
|
+
const pinned = (0, parse_pinned_spec_1.parsePinnedSpec)(u.ref);
|
|
105
|
+
const installedVersion = installed[pinned.name];
|
|
106
|
+
if (!installedVersion || installedVersion !== pinned.version) {
|
|
107
|
+
return [
|
|
108
|
+
{
|
|
109
|
+
name: pinned.name,
|
|
110
|
+
pinned: pinned.version,
|
|
111
|
+
installed: installedVersion
|
|
112
|
+
}
|
|
113
|
+
];
|
|
114
|
+
}
|
|
115
|
+
return [];
|
|
116
|
+
});
|
|
117
|
+
if (drift.length > 0) {
|
|
118
|
+
const details = drift
|
|
119
|
+
.map((d) => {
|
|
120
|
+
const installedText = typeof d.installed === "string" ? d.installed : "(not installed)";
|
|
121
|
+
return `${d.name}: pinned ${d.pinned}, installed ${installedText}`;
|
|
122
|
+
})
|
|
123
|
+
.join("; ");
|
|
124
|
+
throw new Error(`Strategy drift detected (${input.strategyName}). ${details}. ` +
|
|
125
|
+
`Fix: pp strategy update ${input.strategyName} OR pp strategy pin ${input.strategyName}`);
|
|
126
|
+
}
|
|
127
|
+
const units = await Promise.all(normalized.map(async (u) => unitStore.instantiateFromRef(u.ref, u.args)));
|
|
128
|
+
assertNoDuplicateUnitIds({
|
|
129
|
+
strategyName: input.strategyName,
|
|
130
|
+
units
|
|
131
|
+
});
|
|
132
|
+
return units;
|
|
133
|
+
};
|
|
134
|
+
const instantiateBuiltinUnit = async (input) => {
|
|
135
|
+
const req = (0, node_module_1.createRequire)(__filename);
|
|
136
|
+
const pinned = (0, parse_pinned_spec_1.parsePinnedSpec)(input.ref);
|
|
137
|
+
const resolved = req.resolve(pinned.name);
|
|
138
|
+
const mod = (await Promise.resolve(`${(0, node_url_1.pathToFileURL)(resolved).href}`).then(s => __importStar(require(s))));
|
|
139
|
+
const def = mod.default;
|
|
140
|
+
if (typeof def !== "function") {
|
|
141
|
+
throw new Error(`Unit ${pinned.name} must default export a constructor.`);
|
|
142
|
+
}
|
|
143
|
+
const ctor = def;
|
|
144
|
+
const instance = new ctor(...input.args);
|
|
145
|
+
if (!(0, unit_validate_1.isUnitLike)(instance)) {
|
|
146
|
+
throw new Error(`Unit ${pinned.name} default export did not construct a Unit.`);
|
|
22
147
|
}
|
|
23
|
-
return
|
|
148
|
+
return instance;
|
|
149
|
+
};
|
|
150
|
+
const loadBuiltinStrategyUnits = async (input) => {
|
|
151
|
+
const normalized = (0, strategy_normalize_1.normalizeStrategyUnits)(input.strategyFile);
|
|
152
|
+
const units = await Promise.all(normalized.map(async (u) => instantiateBuiltinUnit({
|
|
153
|
+
ref: u.ref,
|
|
154
|
+
args: u.args
|
|
155
|
+
})));
|
|
156
|
+
assertNoDuplicateUnitIds({
|
|
157
|
+
strategyName: input.strategyName,
|
|
158
|
+
units
|
|
159
|
+
});
|
|
160
|
+
return units;
|
|
161
|
+
};
|
|
162
|
+
const loadStrategyUnits = async (input) => {
|
|
163
|
+
if (input.strategy.source === "installed") {
|
|
164
|
+
return loadInstalledStrategyUnits({
|
|
165
|
+
strategyName: input.strategy.name,
|
|
166
|
+
configService: input.configService
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
return loadBuiltinStrategyUnits({
|
|
170
|
+
strategyName: input.strategy.name,
|
|
171
|
+
strategyFile: input.strategy.strategyFile
|
|
172
|
+
});
|
|
24
173
|
};
|
|
25
174
|
class ArchiveCommand extends core_1.Command {
|
|
26
175
|
async run() {
|
|
27
176
|
const { args, flags } = await this.parse(ArchiveCommand);
|
|
28
177
|
const targetUrl = args.url;
|
|
29
|
-
const outputFlag = flags.output ? flags.output.trim() : undefined;
|
|
30
|
-
const emit = flags.emit;
|
|
31
|
-
const type = flags.type;
|
|
32
|
-
const overwrite = flags.overwrite === true;
|
|
33
|
-
const triggerActions = (flags.action ?? []);
|
|
34
|
-
const runtime = flags.runtime;
|
|
35
178
|
const timeoutMs = typeof flags.timeout === "number" ? flags.timeout : undefined;
|
|
36
179
|
const maxDurationMs = typeof flags.maxDuration === "number" ? flags.maxDuration : undefined;
|
|
37
|
-
const
|
|
38
|
-
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
|
|
39
|
-
accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
|
40
|
-
"accept-language": "en-US,en;q=0.9",
|
|
41
|
-
referer: targetUrl
|
|
42
|
-
};
|
|
180
|
+
const strategyName = typeof flags.strategy === "string" ? flags.strategy.trim() : undefined;
|
|
43
181
|
const pagepocket = lib_1.PagePocket.fromURL(targetUrl);
|
|
44
|
-
if (type === "single-file" && emit === "zip") {
|
|
45
|
-
throw new Error("--type single-file is not compatible with --emit zip (single-file is a directory output)");
|
|
46
|
-
}
|
|
47
182
|
const result = await (0, with_spinner_1.withSpinner)(async (spinner) => {
|
|
48
183
|
{
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
new build_snapshot_unit_1.BuildSnapshotUnit()
|
|
67
|
-
];
|
|
68
|
-
if (type === "single-file") {
|
|
69
|
-
units.push(new single_file_unit_1.SingleFileUnit());
|
|
70
|
-
}
|
|
71
|
-
units.push(new write_down_unit_1.WriteDownUnit(writeDownOptions));
|
|
72
|
-
const entryTarget = runtime === "cdp"
|
|
73
|
-
? (() => {
|
|
74
|
-
const tabId = flags.tabId;
|
|
75
|
-
if (typeof tabId !== "number") {
|
|
76
|
-
throw new Error("--tabId is required when --runtime cdp");
|
|
77
|
-
}
|
|
78
|
-
return lib_1.PagePocket.fromCDPTab(tabId);
|
|
79
|
-
})()
|
|
80
|
-
: pagepocket;
|
|
184
|
+
const configService = new config_service_1.ConfigService();
|
|
185
|
+
const name = strategyName && strategyName.length > 0 ? strategyName : "default";
|
|
186
|
+
const strategyService = new strategy_service_1.StrategyService(configService);
|
|
187
|
+
strategyService.ensureConfigFileExists();
|
|
188
|
+
const strategy = resolveStrategy({
|
|
189
|
+
strategyName: name,
|
|
190
|
+
strategyService
|
|
191
|
+
});
|
|
192
|
+
const units = await loadStrategyUnits({
|
|
193
|
+
strategy,
|
|
194
|
+
configService
|
|
195
|
+
});
|
|
196
|
+
const entryTarget = pagepocket;
|
|
197
|
+
const strategyFile = strategy.strategyFile;
|
|
198
|
+
const captureOptions = strategyFile.pipeline.captureOptions;
|
|
199
|
+
const effectiveTimeoutMs = typeof timeoutMs === "number" ? timeoutMs : captureOptions?.timeoutMs;
|
|
200
|
+
const effectiveMaxDurationMs = typeof maxDurationMs === "number" ? maxDurationMs : captureOptions?.maxDurationMs;
|
|
81
201
|
const result = await entryTarget.capture({
|
|
82
|
-
timeoutMs,
|
|
83
|
-
|
|
202
|
+
...(typeof effectiveTimeoutMs === "number" ? { timeoutMs: effectiveTimeoutMs } : {}),
|
|
203
|
+
...(typeof effectiveMaxDurationMs === "number"
|
|
204
|
+
? { maxDurationMs: effectiveMaxDurationMs }
|
|
205
|
+
: {}),
|
|
84
206
|
blacklist: [...lib_1.ga, ...lib_1.ns],
|
|
85
207
|
units,
|
|
86
208
|
plugins: await (0, load_configured_plugins_1.loadConfiguredPlugins)().catch(() => [])
|
|
@@ -109,47 +231,17 @@ ArchiveCommand.flags = {
|
|
|
109
231
|
help: core_1.Flags.help({
|
|
110
232
|
char: "h"
|
|
111
233
|
}),
|
|
112
|
-
output: core_1.Flags.string({
|
|
113
|
-
char: "o",
|
|
114
|
-
description: "Output directory"
|
|
115
|
-
}),
|
|
116
|
-
emit: core_1.Flags.string({
|
|
117
|
-
description: "Output format",
|
|
118
|
-
options: ["raw", "zip"],
|
|
119
|
-
default: "raw"
|
|
120
|
-
}),
|
|
121
|
-
type: core_1.Flags.string({
|
|
122
|
-
description: "Snapshot type",
|
|
123
|
-
options: ["directory", "single-file"],
|
|
124
|
-
default: "directory"
|
|
125
|
-
}),
|
|
126
|
-
overwrite: core_1.Flags.boolean({
|
|
127
|
-
description: "Overwrite existing output directory instead of suffixing",
|
|
128
|
-
default: false
|
|
129
|
-
}),
|
|
130
|
-
action: core_1.Flags.string({
|
|
131
|
-
char: "a",
|
|
132
|
-
description: "Trigger action to run during/after capture (repeatable). Use -a multiple times to run multiple actions.",
|
|
133
|
-
options: Object.values({ HOVER: "HOVER", SCROLL_TO_END: "SCROLL_TO_END" }),
|
|
134
|
-
multiple: true
|
|
135
|
-
}),
|
|
136
|
-
runtime: core_1.Flags.string({
|
|
137
|
-
char: "r",
|
|
138
|
-
description: "Interceptor runtime to use",
|
|
139
|
-
options: ["lighterceptor", "puppeteer", "cdp"],
|
|
140
|
-
default: "puppeteer"
|
|
141
|
-
}),
|
|
142
|
-
tabId: core_1.Flags.integer({
|
|
143
|
-
description: "CDP tabId (required when --runtime cdp)"
|
|
144
|
-
}),
|
|
145
234
|
timeout: core_1.Flags.integer({
|
|
146
235
|
char: "t",
|
|
147
236
|
description: "Network idle duration in milliseconds before capture stops",
|
|
148
|
-
|
|
237
|
+
required: false
|
|
149
238
|
}),
|
|
150
239
|
maxDuration: core_1.Flags.integer({
|
|
151
240
|
description: "Hard max capture duration in milliseconds",
|
|
152
|
-
|
|
241
|
+
required: false
|
|
242
|
+
}),
|
|
243
|
+
strategy: core_1.Flags.string({
|
|
244
|
+
description: "Run an installed or built-in strategy (unit pipeline) by name"
|
|
153
245
|
})
|
|
154
246
|
};
|
|
155
247
|
exports.default = ArchiveCommand;
|
|
@@ -0,0 +1,34 @@
|
|
|
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
|
+
const core_1 = require("@oclif/core");
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const strategy_service_1 = require("../../services/strategy/strategy-service");
|
|
9
|
+
class StrategyAddCommand extends core_1.Command {
|
|
10
|
+
async run() {
|
|
11
|
+
const { args, flags } = await this.parse(StrategyAddCommand);
|
|
12
|
+
const service = new strategy_service_1.StrategyService();
|
|
13
|
+
service.ensureConfigFileExists();
|
|
14
|
+
const out = await service.addStrategy({
|
|
15
|
+
source: args.source,
|
|
16
|
+
force: flags.force === true
|
|
17
|
+
});
|
|
18
|
+
out.installedStrategies.forEach((name) => {
|
|
19
|
+
this.log(chalk_1.default.green(`Added strategy: ${name}`));
|
|
20
|
+
});
|
|
21
|
+
out.installedRefs.forEach((ref) => {
|
|
22
|
+
this.log(chalk_1.default.green(`Installed unit: ${ref}`));
|
|
23
|
+
});
|
|
24
|
+
void flags;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
StrategyAddCommand.description = "Install strategies from a file/url OR install a strategy pack from a pinned npm package (applies all *.strategy.json).";
|
|
28
|
+
StrategyAddCommand.args = {
|
|
29
|
+
source: core_1.Args.string({ description: "strategy file/url OR pinned npm package", required: true })
|
|
30
|
+
};
|
|
31
|
+
StrategyAddCommand.flags = {
|
|
32
|
+
force: core_1.Flags.boolean({ description: "overwrite existing strategy", default: false })
|
|
33
|
+
};
|
|
34
|
+
exports.default = StrategyAddCommand;
|
|
@@ -0,0 +1,34 @@
|
|
|
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
|
+
const core_1 = require("@oclif/core");
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const strategy_service_1 = require("../../services/strategy/strategy-service");
|
|
9
|
+
class StrategyDoctorCommand extends core_1.Command {
|
|
10
|
+
async run() {
|
|
11
|
+
const service = new strategy_service_1.StrategyService();
|
|
12
|
+
const report = service.doctor();
|
|
13
|
+
report.conflicts.forEach((c) => {
|
|
14
|
+
this.log(chalk_1.default.red(`[CONFLICT] ${c.name}`));
|
|
15
|
+
Object.entries(c.versions).forEach(([version, strategies]) => {
|
|
16
|
+
this.log(chalk_1.default.red(` ${version}: ${strategies.join(", ")}`));
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
report.drift.forEach((d) => {
|
|
20
|
+
this.log(chalk_1.default.yellow(`[DRIFT] ${d.strategy}`));
|
|
21
|
+
d.items.forEach((item) => {
|
|
22
|
+
const installed = typeof item.installed === "string" ? item.installed : "(not installed)";
|
|
23
|
+
this.log(chalk_1.default.yellow(` ${item.name}: pinned ${item.pinned}, installed ${installed}`));
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
if (report.conflicts.length === 0 && report.drift.length === 0) {
|
|
27
|
+
this.log(chalk_1.default.green("Strategy doctor checks passed."));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
throw new Error("Strategy doctor checks failed.");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
StrategyDoctorCommand.description = "Diagnose strategy conflicts and unit package drift.";
|
|
34
|
+
exports.default = StrategyDoctorCommand;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const builtin_strategy_registry_1 = require("../../services/strategy/builtin-strategy-registry");
|
|
5
|
+
const strategy_service_1 = require("../../services/strategy/strategy-service");
|
|
6
|
+
const array_1 = require("../../utils/array");
|
|
7
|
+
class StrategyLsCommand extends core_1.Command {
|
|
8
|
+
async run() {
|
|
9
|
+
const service = new strategy_service_1.StrategyService();
|
|
10
|
+
service.ensureConfigFileExists();
|
|
11
|
+
const builtin = (0, builtin_strategy_registry_1.listBuiltinStrategyNames)();
|
|
12
|
+
const installed = service.listInstalledStrategyNames();
|
|
13
|
+
const names = (0, array_1.uniq)([...builtin, ...installed])
|
|
14
|
+
.map((n) => n.trim())
|
|
15
|
+
.filter((n) => n.length > 0)
|
|
16
|
+
.sort((a, b) => a.localeCompare(b));
|
|
17
|
+
if (names.length === 0) {
|
|
18
|
+
this.log("No strategies available.");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
names.forEach((n) => this.log(n));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
StrategyLsCommand.description = "List available strategies.";
|
|
25
|
+
exports.default = StrategyLsCommand;
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
const core_1 = require("@oclif/core");
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const strategy_service_1 = require("../../services/strategy/strategy-service");
|
|
9
|
+
class StrategyPinCommand extends core_1.Command {
|
|
10
|
+
async run() {
|
|
11
|
+
const { args } = await this.parse(StrategyPinCommand);
|
|
12
|
+
const service = new strategy_service_1.StrategyService();
|
|
13
|
+
service.pinStrategy(args.name);
|
|
14
|
+
this.log(chalk_1.default.green(`Pinned strategy: ${args.name}`));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
StrategyPinCommand.description = "Pin a strategy file to currently installed unit package versions.";
|
|
18
|
+
StrategyPinCommand.args = {
|
|
19
|
+
name: core_1.Args.string({ description: "strategy name", required: true })
|
|
20
|
+
};
|
|
21
|
+
exports.default = StrategyPinCommand;
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
const core_1 = require("@oclif/core");
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const strategy_service_1 = require("../../services/strategy/strategy-service");
|
|
9
|
+
class StrategyRemoveCommand extends core_1.Command {
|
|
10
|
+
async run() {
|
|
11
|
+
const { args } = await this.parse(StrategyRemoveCommand);
|
|
12
|
+
const service = new strategy_service_1.StrategyService();
|
|
13
|
+
service.removeStrategy(args.name);
|
|
14
|
+
this.log(chalk_1.default.green(`Removed strategy: ${args.name}`));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
StrategyRemoveCommand.description = "Remove an installed strategy.";
|
|
18
|
+
StrategyRemoveCommand.args = {
|
|
19
|
+
name: core_1.Args.string({ description: "strategy name", required: true })
|
|
20
|
+
};
|
|
21
|
+
exports.default = StrategyRemoveCommand;
|
|
@@ -0,0 +1,34 @@
|
|
|
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
|
+
const core_1 = require("@oclif/core");
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const strategy_service_1 = require("../../services/strategy/strategy-service");
|
|
9
|
+
class StrategyUpdateCommand extends core_1.Command {
|
|
10
|
+
async run() {
|
|
11
|
+
const { args, flags } = await this.parse(StrategyUpdateCommand);
|
|
12
|
+
const service = new strategy_service_1.StrategyService();
|
|
13
|
+
await service.updateStrategy(args.name, { packageOnly: flags.packageOnly === true });
|
|
14
|
+
if (flags.packageOnly) {
|
|
15
|
+
this.log(chalk_1.default.yellow("WARNING: Updated unit packages without updating strategy files. This may cause version drift."));
|
|
16
|
+
this.log(chalk_1.default.gray("If drift occurs, fix by updating packages to pinned versions:"));
|
|
17
|
+
this.log(chalk_1.default.gray(` pp strategy update${args.name ? ` ${args.name}` : ""}`));
|
|
18
|
+
this.log(chalk_1.default.gray("Or pin a strategy to installed versions:"));
|
|
19
|
+
this.log(chalk_1.default.gray(` pp strategy pin ${args.name ?? "<strategy-name>"}`));
|
|
20
|
+
}
|
|
21
|
+
this.log(chalk_1.default.green("Strategy update completed."));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
StrategyUpdateCommand.description = "Update a strategy (or all strategies).";
|
|
25
|
+
StrategyUpdateCommand.args = {
|
|
26
|
+
name: core_1.Args.string({ description: "strategy name (optional)", required: false })
|
|
27
|
+
};
|
|
28
|
+
StrategyUpdateCommand.flags = {
|
|
29
|
+
packageOnly: core_1.Flags.boolean({
|
|
30
|
+
description: "update unit packages to latest only",
|
|
31
|
+
default: false
|
|
32
|
+
})
|
|
33
|
+
};
|
|
34
|
+
exports.default = StrategyUpdateCommand;
|
|
@@ -10,7 +10,9 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
10
10
|
const env_paths_1 = __importDefault(require("env-paths"));
|
|
11
11
|
const parse_json_1 = require("../utils/parse-json");
|
|
12
12
|
const defaultConfig = {
|
|
13
|
-
plugins: []
|
|
13
|
+
plugins: [],
|
|
14
|
+
strategies: [],
|
|
15
|
+
strategyPacks: []
|
|
14
16
|
};
|
|
15
17
|
class ConfigService {
|
|
16
18
|
constructor(appName = "pagepocket") {
|
|
@@ -57,27 +59,57 @@ class ConfigService {
|
|
|
57
59
|
return defaultConfig;
|
|
58
60
|
}
|
|
59
61
|
const pluginsRaw = value.plugins;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
62
|
+
const plugins = Array.isArray(pluginsRaw)
|
|
63
|
+
? pluginsRaw
|
|
64
|
+
.map((entry) => {
|
|
65
|
+
if (typeof entry === "string") {
|
|
66
|
+
return entry;
|
|
67
|
+
}
|
|
68
|
+
if (this.isObject(entry) && typeof entry.name === "string") {
|
|
69
|
+
const spec = "spec" in entry && typeof entry.spec === "string"
|
|
70
|
+
? entry.spec
|
|
71
|
+
: undefined;
|
|
72
|
+
const options = "options" in entry ? entry.options : undefined;
|
|
73
|
+
return {
|
|
74
|
+
name: entry.name,
|
|
75
|
+
...(typeof spec === "undefined" ? {} : { spec }),
|
|
76
|
+
...(typeof options === "undefined" ? {} : { options })
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return undefined;
|
|
80
|
+
})
|
|
81
|
+
.filter((x) => typeof x !== "undefined")
|
|
82
|
+
: [];
|
|
83
|
+
const strategiesRaw = value.strategies;
|
|
84
|
+
const strategies = Array.isArray(strategiesRaw)
|
|
85
|
+
? strategiesRaw
|
|
86
|
+
.map((x) => (typeof x === "string" ? x.trim() : ""))
|
|
87
|
+
.filter((x) => x.length > 0)
|
|
88
|
+
: [];
|
|
89
|
+
const packsRaw = value.strategyPacks;
|
|
90
|
+
const strategyPacks = Array.isArray(packsRaw)
|
|
91
|
+
? packsRaw
|
|
92
|
+
.map((entry) => {
|
|
93
|
+
if (typeof entry === "string") {
|
|
94
|
+
return entry.trim();
|
|
95
|
+
}
|
|
96
|
+
if (this.isObject(entry) &&
|
|
97
|
+
typeof entry.name === "string" &&
|
|
98
|
+
typeof entry.spec === "string") {
|
|
99
|
+
return {
|
|
100
|
+
name: entry.name.trim(),
|
|
101
|
+
spec: entry.spec.trim()
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return undefined;
|
|
105
|
+
})
|
|
106
|
+
.filter((x) => Boolean(x))
|
|
107
|
+
: [];
|
|
108
|
+
return {
|
|
109
|
+
plugins,
|
|
110
|
+
strategies,
|
|
111
|
+
strategyPacks
|
|
112
|
+
};
|
|
81
113
|
}
|
|
82
114
|
readConfig() {
|
|
83
115
|
const configPath = this.getConfigPath();
|
|
@@ -0,0 +1,32 @@
|
|
|
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.readBuiltinStrategy = exports.listBuiltinStrategyNames = exports.readBuiltinStrategies = void 0;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const node_module_1 = require("node:module");
|
|
9
|
+
const strategy_pack_read_1 = require("./strategy-pack-read");
|
|
10
|
+
const readBuiltinStrategiesUnsafe = () => {
|
|
11
|
+
const req = (0, node_module_1.createRequire)(__filename);
|
|
12
|
+
const pkgJsonPath = req.resolve("@pagepocket/builtin-strategy/package.json");
|
|
13
|
+
const packRoot = node_path_1.default.dirname(pkgJsonPath);
|
|
14
|
+
return (0, strategy_pack_read_1.readStrategiesFromPackRoot)(packRoot);
|
|
15
|
+
};
|
|
16
|
+
const readBuiltinStrategies = () => {
|
|
17
|
+
try {
|
|
18
|
+
return readBuiltinStrategiesUnsafe();
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
exports.readBuiltinStrategies = readBuiltinStrategies;
|
|
25
|
+
const listBuiltinStrategyNames = () => {
|
|
26
|
+
return (0, exports.readBuiltinStrategies)().map((strategy) => strategy.name);
|
|
27
|
+
};
|
|
28
|
+
exports.listBuiltinStrategyNames = listBuiltinStrategyNames;
|
|
29
|
+
const readBuiltinStrategy = (name) => {
|
|
30
|
+
return (0, exports.readBuiltinStrategies)().find((strategy) => strategy.name === name);
|
|
31
|
+
};
|
|
32
|
+
exports.readBuiltinStrategy = readBuiltinStrategy;
|