@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.
@@ -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 network_observer_unit_1 = require("../units/network-observer-unit");
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 MAX_STATUS_URL_LENGTH = 80;
19
- const formatStatusUrl = (url) => {
20
- if (url.length <= MAX_STATUS_URL_LENGTH) {
21
- return url;
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 `${url.slice(0, MAX_STATUS_URL_LENGTH - 3)}...`;
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 headers = {
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 writeDownOptions = {
50
- type: emit,
51
- outputPath: outputFlag ?? process.cwd(),
52
- overwrite
53
- };
54
- const captureUnit = runtime === "puppeteer"
55
- ? new capture_http_puppeteer_unit_1.CaptureHttpPuppeteerUnit({ triggerActions })
56
- : runtime === "lighterceptor"
57
- ? new capture_http_lighterceptor_unit_1.CaptureHttpLighterceptorUnit({ headers, triggerActions })
58
- : new capture_http_cdp_unit_1.CaptureHttpCdpUnit({ triggerActions });
59
- const units = [
60
- new network_observer_unit_1.NetworkObserverUnit({
61
- onRequest: (event) => {
62
- spinner.text = `Freezing page (${formatStatusUrl(event.url)})`;
63
- }
64
- }),
65
- captureUnit,
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
- maxDurationMs,
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
- default: 5000
237
+ required: false
149
238
  }),
150
239
  maxDuration: core_1.Flags.integer({
151
240
  description: "Hard max capture duration in milliseconds",
152
- default: 60000
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
- if (!Array.isArray(pluginsRaw)) {
61
- return defaultConfig;
62
- }
63
- const plugins = 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" ? entry.spec : undefined;
70
- const options = "options" in entry ? entry.options : undefined;
71
- return {
72
- name: entry.name,
73
- ...(typeof spec === "undefined" ? {} : { spec }),
74
- ...(typeof options === "undefined" ? {} : { options })
75
- };
76
- }
77
- return undefined;
78
- })
79
- .filter((x) => typeof x !== "undefined");
80
- return { plugins };
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;