@pagepocket/cli 0.11.0 → 0.11.1
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 +67 -105
- package/dist/commands/plugin/add.js +19 -25
- package/dist/commands/plugin/doctor.js +22 -28
- package/dist/commands/plugin/ls.js +16 -22
- package/dist/commands/plugin/prune.js +13 -19
- package/dist/commands/plugin/remove.js +16 -22
- package/dist/commands/plugin/set.js +13 -19
- package/dist/commands/plugin/uninstall.js +29 -35
- package/dist/commands/plugin/update.js +22 -28
- package/dist/commands/strategy/add.js +14 -20
- package/dist/commands/strategy/doctor.js +11 -17
- package/dist/commands/strategy/ls.js +9 -12
- package/dist/commands/strategy/pin.js +10 -16
- package/dist/commands/strategy/remove.js +10 -16
- package/dist/commands/strategy/update.js +21 -27
- package/dist/commands/view.js +36 -42
- package/dist/index.js +9 -7
- package/dist/lib/filename.js +1 -5
- package/dist/services/config-service.js +24 -30
- package/dist/services/load-configured-plugins.js +8 -12
- package/dist/services/plugin-installer.js +6 -12
- package/dist/services/plugin-store.js +27 -68
- package/dist/services/strategy/builtin-strategy-registry.js +11 -20
- package/dist/services/strategy/strategy-analyze.js +10 -18
- package/dist/services/strategy/strategy-config.js +8 -15
- package/dist/services/strategy/strategy-fetch.js +11 -18
- package/dist/services/strategy/strategy-io.js +15 -25
- package/dist/services/strategy/strategy-normalize.js +4 -8
- package/dist/services/strategy/strategy-pack-read.js +10 -17
- package/dist/services/strategy/strategy-pack-store.js +12 -18
- package/dist/services/strategy/strategy-service.js +79 -83
- package/dist/services/strategy/types.js +1 -2
- package/dist/services/units/unit-store.js +16 -20
- package/dist/services/units/unit-validate.js +20 -5
- package/dist/services/user-packages/parse-pinned-spec.js +5 -11
- package/dist/services/user-packages/user-package-installer.js +4 -9
- package/dist/services/user-packages/user-package-store.js +19 -57
- package/dist/stages/prepare-output.js +6 -13
- package/dist/units/network-observer-unit.js +7 -10
- package/dist/utils/array.js +1 -5
- package/dist/utils/normalize-argv.js +3 -8
- package/dist/utils/parse-json.js +1 -5
- package/dist/utils/parse-plugin-options.js +1 -5
- package/dist/utils/parse-plugin-spec.js +5 -11
- package/dist/utils/validate-plugin-default-export.js +1 -5
- package/dist/utils/with-spinner.js +3 -10
- package/dist/view.js +12 -19
- package/package.json +12 -11
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.normalizeStrategyUnits = void 0;
|
|
4
|
-
const parse_pinned_spec_1 = require("../user-packages/parse-pinned-spec");
|
|
1
|
+
import { parsePinnedSpec } from "../user-packages/parse-pinned-spec.js";
|
|
5
2
|
const isRecord = (value) => {
|
|
6
3
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7
4
|
};
|
|
@@ -23,7 +20,7 @@ const isJsonValue = (value) => {
|
|
|
23
20
|
};
|
|
24
21
|
const normalizeUnitSpec = (spec, idx) => {
|
|
25
22
|
if (typeof spec === "string") {
|
|
26
|
-
const pinned =
|
|
23
|
+
const pinned = parsePinnedSpec(spec);
|
|
27
24
|
return { ref: pinned.spec, args: [] };
|
|
28
25
|
}
|
|
29
26
|
if (!isRecord(spec)) {
|
|
@@ -33,7 +30,7 @@ const normalizeUnitSpec = (spec, idx) => {
|
|
|
33
30
|
if (typeof ref !== "string") {
|
|
34
31
|
throw new Error(`Invalid unit ref at index ${idx}`);
|
|
35
32
|
}
|
|
36
|
-
const pinned =
|
|
33
|
+
const pinned = parsePinnedSpec(ref);
|
|
37
34
|
const argsRaw = spec.args;
|
|
38
35
|
const args = typeof argsRaw === "undefined" ? [] : argsRaw;
|
|
39
36
|
if (!Array.isArray(args)) {
|
|
@@ -44,8 +41,7 @@ const normalizeUnitSpec = (spec, idx) => {
|
|
|
44
41
|
}
|
|
45
42
|
return { ref: pinned.spec, args };
|
|
46
43
|
};
|
|
47
|
-
const normalizeStrategyUnits = (strategy) => {
|
|
44
|
+
export const normalizeStrategyUnits = (strategy) => {
|
|
48
45
|
const units = strategy.pipeline.units;
|
|
49
46
|
return units.map((spec, idx) => normalizeUnitSpec(spec, idx));
|
|
50
47
|
};
|
|
51
|
-
exports.normalizeStrategyUnits = normalizeStrategyUnits;
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.readStrategiesFromPackRoot = void 0;
|
|
7
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
-
const parse_json_1 = require("../../utils/parse-json");
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parseJson } from "../../utils/parse-json.js";
|
|
10
4
|
const isRecord = (value) => {
|
|
11
5
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
12
6
|
};
|
|
@@ -29,19 +23,19 @@ const isStrategyFile = (value) => {
|
|
|
29
23
|
}
|
|
30
24
|
return true;
|
|
31
25
|
};
|
|
32
|
-
const readStrategiesFromPackRoot = (packRoot) => {
|
|
33
|
-
const strategiesDir =
|
|
34
|
-
if (!
|
|
26
|
+
export const readStrategiesFromPackRoot = (packRoot) => {
|
|
27
|
+
const strategiesDir = path.join(packRoot, "strategies");
|
|
28
|
+
if (!fs.existsSync(strategiesDir)) {
|
|
35
29
|
return [];
|
|
36
30
|
}
|
|
37
|
-
const fileNames =
|
|
31
|
+
const fileNames = fs
|
|
38
32
|
.readdirSync(strategiesDir)
|
|
39
33
|
.filter((f) => f.endsWith(".strategy.json"))
|
|
40
34
|
.sort((a, b) => a.localeCompare(b));
|
|
41
35
|
return fileNames.map((fileName) => {
|
|
42
|
-
const filePath =
|
|
43
|
-
const text =
|
|
44
|
-
const parsed =
|
|
36
|
+
const filePath = path.join(strategiesDir, fileName);
|
|
37
|
+
const text = fs.readFileSync(filePath, "utf8");
|
|
38
|
+
const parsed = parseJson(text);
|
|
45
39
|
if (!parsed.ok) {
|
|
46
40
|
throw parsed.error;
|
|
47
41
|
}
|
|
@@ -51,4 +45,3 @@ const readStrategiesFromPackRoot = (packRoot) => {
|
|
|
51
45
|
return parsed.value;
|
|
52
46
|
});
|
|
53
47
|
};
|
|
54
|
-
exports.readStrategiesFromPackRoot = readStrategiesFromPackRoot;
|
|
@@ -1,19 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
exports.StrategyPackStore = void 0;
|
|
7
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
-
const user_package_installer_1 = require("../user-packages/user-package-installer");
|
|
9
|
-
const user_package_installer_2 = require("../user-packages/user-package-installer");
|
|
10
|
-
const parse_pinned_spec_1 = require("../user-packages/parse-pinned-spec");
|
|
11
|
-
const user_package_store_1 = require("../user-packages/user-package-store");
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { installPinnedPackage } from "../user-packages/user-package-installer.js";
|
|
3
|
+
import { updatePackageToLatest } from "../user-packages/user-package-installer.js";
|
|
4
|
+
import { parsePinnedSpec } from "../user-packages/parse-pinned-spec.js";
|
|
5
|
+
import { UserPackageStore } from "../user-packages/user-package-store.js";
|
|
12
6
|
const STRATEGY_PACKS_KIND = "strategy-packs";
|
|
13
7
|
const STRATEGY_PACKS_PACKAGE_JSON_NAME = "pagepocket-user-strategy-packs";
|
|
14
|
-
class StrategyPackStore {
|
|
8
|
+
export class StrategyPackStore {
|
|
9
|
+
store;
|
|
15
10
|
constructor(configService) {
|
|
16
|
-
this.store = new
|
|
11
|
+
this.store = new UserPackageStore(configService, STRATEGY_PACKS_KIND);
|
|
17
12
|
}
|
|
18
13
|
getInstallDir() {
|
|
19
14
|
return this.store.getInstallDir();
|
|
@@ -22,15 +17,15 @@ class StrategyPackStore {
|
|
|
22
17
|
return this.store.readInstalledDependencyVersions(STRATEGY_PACKS_PACKAGE_JSON_NAME);
|
|
23
18
|
}
|
|
24
19
|
installPinned(spec) {
|
|
25
|
-
const pinned =
|
|
26
|
-
|
|
20
|
+
const pinned = parsePinnedSpec(spec);
|
|
21
|
+
installPinnedPackage(this.store, {
|
|
27
22
|
packageJsonName: STRATEGY_PACKS_PACKAGE_JSON_NAME,
|
|
28
23
|
packageSpec: pinned.spec
|
|
29
24
|
});
|
|
30
25
|
return pinned;
|
|
31
26
|
}
|
|
32
27
|
updateToLatest(packageName) {
|
|
33
|
-
|
|
28
|
+
updatePackageToLatest(this.store, {
|
|
34
29
|
packageJsonName: STRATEGY_PACKS_PACKAGE_JSON_NAME,
|
|
35
30
|
packageName
|
|
36
31
|
});
|
|
@@ -39,7 +34,6 @@ class StrategyPackStore {
|
|
|
39
34
|
this.store.ensureInstallDirPackageJson(STRATEGY_PACKS_PACKAGE_JSON_NAME);
|
|
40
35
|
const req = this.store.createRequire(STRATEGY_PACKS_PACKAGE_JSON_NAME);
|
|
41
36
|
const pkgJsonPath = req.resolve(`${packageName}/package.json`);
|
|
42
|
-
return
|
|
37
|
+
return path.dirname(pkgJsonPath);
|
|
43
38
|
}
|
|
44
39
|
}
|
|
45
|
-
exports.StrategyPackStore = StrategyPackStore;
|
|
@@ -1,42 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const strategy_pack_store_1 = require("./strategy-pack-store");
|
|
19
|
-
class StrategyService {
|
|
20
|
-
constructor(configService = new config_service_1.ConfigService()) {
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { ConfigService } from "../config-service.js";
|
|
3
|
+
import { parsePinnedSpec } from "../user-packages/parse-pinned-spec.js";
|
|
4
|
+
import { UnitStore } from "../units/unit-store.js";
|
|
5
|
+
import { uniq } from "../../utils/array.js";
|
|
6
|
+
import { collectWantedVersions, computeConflicts, computeDrift, ensureNoInstalledVersionConflicts } from "./strategy-analyze.js";
|
|
7
|
+
import { listStrategyNamesFromConfig, requireStrategyInstalled, withStrategyInConfig, withoutStrategyInConfig } from "./strategy-config.js";
|
|
8
|
+
import { fetchStrategyFile } from "./strategy-fetch.js";
|
|
9
|
+
import { getStrategyPath, getStrategiesDir, readStrategyFile, writeJsonAtomic } from "./strategy-io.js";
|
|
10
|
+
import { normalizeStrategyUnits } from "./strategy-normalize.js";
|
|
11
|
+
import { readStrategiesFromPackRoot } from "./strategy-pack-read.js";
|
|
12
|
+
import { StrategyPackStore } from "./strategy-pack-store.js";
|
|
13
|
+
export class StrategyService {
|
|
14
|
+
configService;
|
|
15
|
+
unitStore;
|
|
16
|
+
packStore;
|
|
17
|
+
constructor(configService = new ConfigService()) {
|
|
21
18
|
this.configService = configService;
|
|
22
|
-
this.unitStore = new
|
|
23
|
-
this.packStore = new
|
|
19
|
+
this.unitStore = new UnitStore(configService);
|
|
20
|
+
this.packStore = new StrategyPackStore(configService);
|
|
24
21
|
}
|
|
25
22
|
ensureConfigFileExists() {
|
|
26
23
|
return this.configService.ensureConfigFileExists();
|
|
27
24
|
}
|
|
28
25
|
getStrategiesDir() {
|
|
29
|
-
return
|
|
26
|
+
return getStrategiesDir(this.configService);
|
|
30
27
|
}
|
|
31
28
|
getStrategyPath(name) {
|
|
32
|
-
return
|
|
29
|
+
return getStrategyPath(this.configService, name);
|
|
33
30
|
}
|
|
34
31
|
readStrategy(name) {
|
|
35
|
-
return
|
|
32
|
+
return readStrategyFile(this.getStrategyPath(name));
|
|
36
33
|
}
|
|
37
34
|
listInstalledStrategyNames() {
|
|
38
35
|
const config = this.configService.readConfigOrDefault();
|
|
39
|
-
return
|
|
36
|
+
return listStrategyNamesFromConfig(config);
|
|
40
37
|
}
|
|
41
38
|
async addStrategy(input) {
|
|
42
39
|
this.configService.ensureConfigFileExists();
|
|
@@ -44,7 +41,7 @@ class StrategyService {
|
|
|
44
41
|
const sourceTrimmed = input.source.trim();
|
|
45
42
|
const isPinnedNpm = (() => {
|
|
46
43
|
try {
|
|
47
|
-
|
|
44
|
+
parsePinnedSpec(sourceTrimmed);
|
|
48
45
|
return true;
|
|
49
46
|
}
|
|
50
47
|
catch {
|
|
@@ -54,29 +51,29 @@ class StrategyService {
|
|
|
54
51
|
if (isPinnedNpm) {
|
|
55
52
|
const pinned = this.packStore.installPinned(sourceTrimmed);
|
|
56
53
|
const packRoot = this.packStore.resolvePackRoot(pinned.name);
|
|
57
|
-
const strategies =
|
|
54
|
+
const strategies = readStrategiesFromPackRoot(packRoot);
|
|
58
55
|
if (strategies.length === 0) {
|
|
59
56
|
throw new Error(`No *.strategy.json found in package: ${pinned.spec}`);
|
|
60
57
|
}
|
|
61
|
-
const allUnits = strategies.flatMap((s) =>
|
|
62
|
-
const refs =
|
|
63
|
-
|
|
58
|
+
const allUnits = strategies.flatMap((s) => normalizeStrategyUnits(s));
|
|
59
|
+
const refs = uniq(allUnits.map((u) => u.ref));
|
|
60
|
+
ensureNoInstalledVersionConflicts(this.unitStore.readInstalledDependencyVersions(), refs);
|
|
64
61
|
refs.forEach((ref) => this.unitStore.installPinned(ref));
|
|
65
62
|
const installedStrategies = [];
|
|
66
63
|
strategies.forEach((strategy) => {
|
|
67
64
|
const name = strategy.name;
|
|
68
65
|
const strategyPath = this.getStrategyPath(name);
|
|
69
|
-
if (!input.force &&
|
|
66
|
+
if (!input.force && fs.existsSync(strategyPath)) {
|
|
70
67
|
return;
|
|
71
68
|
}
|
|
72
69
|
const toWrite = {
|
|
73
70
|
...strategy,
|
|
74
71
|
source: { type: "npm", value: pinned.spec }
|
|
75
72
|
};
|
|
76
|
-
|
|
73
|
+
writeJsonAtomic(strategyPath, toWrite);
|
|
77
74
|
installedStrategies.push(name);
|
|
78
75
|
});
|
|
79
|
-
const nextConfig = installedStrategies.reduce((acc, s) =>
|
|
76
|
+
const nextConfig = installedStrategies.reduce((acc, s) => withStrategyInConfig(acc, s), config);
|
|
80
77
|
const existing = nextConfig.strategyPacks ?? [];
|
|
81
78
|
const filtered = existing.filter((x) => {
|
|
82
79
|
if (typeof x === "string") {
|
|
@@ -94,60 +91,60 @@ class StrategyService {
|
|
|
94
91
|
this.configService.writeConfig({ ...nextConfig, strategyPacks });
|
|
95
92
|
return { installedRefs: refs, installedStrategies };
|
|
96
93
|
}
|
|
97
|
-
const { strategy, source } = await
|
|
98
|
-
const normalizedUnits =
|
|
99
|
-
const refs =
|
|
94
|
+
const { strategy, source } = await fetchStrategyFile(sourceTrimmed);
|
|
95
|
+
const normalizedUnits = normalizeStrategyUnits(strategy);
|
|
96
|
+
const refs = uniq(normalizedUnits.map((u) => u.ref));
|
|
100
97
|
const strategyPath = this.getStrategyPath(strategy.name);
|
|
101
|
-
if (!input.force &&
|
|
98
|
+
if (!input.force && fs.existsSync(strategyPath)) {
|
|
102
99
|
throw new Error(`Strategy already exists: ${strategy.name}`);
|
|
103
100
|
}
|
|
104
|
-
|
|
101
|
+
ensureNoInstalledVersionConflicts(this.unitStore.readInstalledDependencyVersions(), refs);
|
|
105
102
|
refs.forEach((ref) => this.unitStore.installPinned(ref));
|
|
106
103
|
const toWrite = {
|
|
107
104
|
...strategy,
|
|
108
105
|
source
|
|
109
106
|
};
|
|
110
|
-
|
|
111
|
-
this.configService.writeConfig(
|
|
107
|
+
writeJsonAtomic(strategyPath, toWrite);
|
|
108
|
+
this.configService.writeConfig(withStrategyInConfig(config, strategy.name));
|
|
112
109
|
return { installedRefs: refs, installedStrategies: [strategy.name] };
|
|
113
110
|
}
|
|
114
111
|
removeStrategy(name) {
|
|
115
112
|
this.configService.ensureConfigFileExists();
|
|
116
113
|
const config = this.configService.readConfigOrDefault();
|
|
117
|
-
|
|
114
|
+
requireStrategyInstalled(config, name);
|
|
118
115
|
const strategyPath = this.getStrategyPath(name);
|
|
119
|
-
if (
|
|
120
|
-
|
|
116
|
+
if (fs.existsSync(strategyPath)) {
|
|
117
|
+
fs.rmSync(strategyPath);
|
|
121
118
|
}
|
|
122
|
-
this.configService.writeConfig(
|
|
119
|
+
this.configService.writeConfig(withoutStrategyInConfig(config, name));
|
|
123
120
|
}
|
|
124
121
|
async updateStrategy(name, opts) {
|
|
125
122
|
this.configService.ensureConfigFileExists();
|
|
126
123
|
const config = this.configService.readConfigOrDefault();
|
|
127
|
-
const names = name ? [name] :
|
|
124
|
+
const names = name ? [name] : listStrategyNamesFromConfig(config);
|
|
128
125
|
if (names.length === 0) {
|
|
129
126
|
return;
|
|
130
127
|
}
|
|
131
128
|
const installed = this.unitStore.readInstalledDependencyVersions();
|
|
132
129
|
if (opts?.packageOnly) {
|
|
133
|
-
const packageNames =
|
|
134
|
-
.flatMap((n) =>
|
|
130
|
+
const packageNames = uniq(names
|
|
131
|
+
.flatMap((n) => normalizeStrategyUnits(this.readStrategy(n)).map((u) => parsePinnedSpec(u.ref).name))
|
|
135
132
|
.filter((x) => x.trim().length > 0));
|
|
136
133
|
packageNames.forEach((pkg) => {
|
|
137
134
|
this.unitStore.updateToLatest(pkg);
|
|
138
135
|
});
|
|
139
136
|
const afterInstalled = this.unitStore.readInstalledDependencyVersions();
|
|
140
|
-
const afterStrategies =
|
|
137
|
+
const afterStrategies = listStrategyNamesFromConfig(config).map((n) => ({
|
|
141
138
|
name: n,
|
|
142
|
-
units:
|
|
139
|
+
units: normalizeStrategyUnits(this.readStrategy(n))
|
|
143
140
|
}));
|
|
144
|
-
const wanted =
|
|
145
|
-
const conflicts =
|
|
141
|
+
const wanted = collectWantedVersions(afterStrategies);
|
|
142
|
+
const conflicts = computeConflicts(wanted);
|
|
146
143
|
if (conflicts.length > 0) {
|
|
147
144
|
throw new Error(`Strategy version conflicts detected (${conflicts.length}). Run 'pp strategy doctor' for details.`);
|
|
148
145
|
}
|
|
149
146
|
const drift = afterStrategies
|
|
150
|
-
.map((s) =>
|
|
147
|
+
.map((s) => computeDrift({ strategyName: s.name, units: s.units, installed: afterInstalled }))
|
|
151
148
|
.filter((d) => d.items.length > 0);
|
|
152
149
|
if (drift.length > 0) {
|
|
153
150
|
throw new Error("Strategy drift detected after --package-only update. Run 'pp strategy doctor'.");
|
|
@@ -166,23 +163,23 @@ class StrategyService {
|
|
|
166
163
|
npmPacksToUpdate.add(src.value);
|
|
167
164
|
continue;
|
|
168
165
|
}
|
|
169
|
-
const fetched = await
|
|
166
|
+
const fetched = await fetchStrategyFile(src.value);
|
|
170
167
|
if (fetched.strategy.name !== n) {
|
|
171
168
|
throw new Error(`Strategy name mismatch while updating ${n}: got ${fetched.strategy.name}`);
|
|
172
169
|
}
|
|
173
170
|
const file = { ...fetched.strategy, source: fetched.source };
|
|
174
|
-
const units =
|
|
171
|
+
const units = normalizeStrategyUnits(file);
|
|
175
172
|
nextStrategies.push({ name: n, file, units });
|
|
176
173
|
}
|
|
177
174
|
if (npmPacksToUpdate.size > 0) {
|
|
178
175
|
const specs = [...npmPacksToUpdate];
|
|
179
176
|
specs.forEach((spec) => {
|
|
180
|
-
const pinned =
|
|
177
|
+
const pinned = parsePinnedSpec(spec);
|
|
181
178
|
this.packStore.updateToLatest(pinned.name);
|
|
182
179
|
});
|
|
183
180
|
const installedPackVersions = this.packStore.readInstalledDependencyVersions();
|
|
184
181
|
const updatedSpecs = specs.map((spec) => {
|
|
185
|
-
const pinned =
|
|
182
|
+
const pinned = parsePinnedSpec(spec);
|
|
186
183
|
const v = installedPackVersions[pinned.name];
|
|
187
184
|
if (!v) {
|
|
188
185
|
throw new Error(`Strategy pack not installed after update: ${pinned.name}`);
|
|
@@ -191,16 +188,16 @@ class StrategyService {
|
|
|
191
188
|
});
|
|
192
189
|
const updatedFiles = [];
|
|
193
190
|
updatedSpecs.forEach((spec) => {
|
|
194
|
-
const pinned =
|
|
191
|
+
const pinned = parsePinnedSpec(spec);
|
|
195
192
|
const root = this.packStore.resolvePackRoot(pinned.name);
|
|
196
|
-
const files =
|
|
193
|
+
const files = readStrategiesFromPackRoot(root).map((f) => ({
|
|
197
194
|
...f,
|
|
198
195
|
source: { type: "npm", value: spec }
|
|
199
196
|
}));
|
|
200
197
|
updatedFiles.push(...files);
|
|
201
198
|
});
|
|
202
199
|
updatedFiles.forEach((file) => {
|
|
203
|
-
const units =
|
|
200
|
+
const units = normalizeStrategyUnits(file);
|
|
204
201
|
nextStrategies.push({ name: file.name, file, units });
|
|
205
202
|
});
|
|
206
203
|
const nextConfig = this.configService.readConfigOrDefault();
|
|
@@ -209,48 +206,48 @@ class StrategyService {
|
|
|
209
206
|
if (typeof x === "string") {
|
|
210
207
|
return x.trim().length > 0;
|
|
211
208
|
}
|
|
212
|
-
return !updatedSpecs.some((spec) =>
|
|
209
|
+
return !updatedSpecs.some((spec) => parsePinnedSpec(spec).name === x.name);
|
|
213
210
|
});
|
|
214
211
|
const packsNext = [
|
|
215
212
|
...filtered,
|
|
216
213
|
...updatedSpecs.map((spec) => {
|
|
217
|
-
const pinned =
|
|
214
|
+
const pinned = parsePinnedSpec(spec);
|
|
218
215
|
return { name: pinned.name, spec: pinned.spec };
|
|
219
216
|
})
|
|
220
217
|
];
|
|
221
218
|
this.configService.writeConfig({ ...nextConfig, strategyPacks: packsNext });
|
|
222
219
|
}
|
|
223
|
-
const allOtherNames =
|
|
220
|
+
const allOtherNames = listStrategyNamesFromConfig(config).filter((n) => !names.includes(n));
|
|
224
221
|
const otherStrategies = allOtherNames.map((n) => ({
|
|
225
222
|
name: n,
|
|
226
|
-
units:
|
|
223
|
+
units: normalizeStrategyUnits(this.readStrategy(n))
|
|
227
224
|
}));
|
|
228
|
-
const wanted =
|
|
225
|
+
const wanted = collectWantedVersions([
|
|
229
226
|
...otherStrategies,
|
|
230
227
|
...nextStrategies.map((s) => ({ name: s.name, units: s.units }))
|
|
231
228
|
]);
|
|
232
|
-
const conflicts =
|
|
229
|
+
const conflicts = computeConflicts(wanted);
|
|
233
230
|
if (conflicts.length > 0) {
|
|
234
231
|
throw new Error(`Strategy version conflicts detected (${conflicts.length}). Run 'pp strategy doctor' for details.`);
|
|
235
232
|
}
|
|
236
|
-
const refs =
|
|
237
|
-
|
|
233
|
+
const refs = uniq(nextStrategies.flatMap((s) => s.units.map((u) => u.ref)));
|
|
234
|
+
ensureNoInstalledVersionConflicts(installed, refs);
|
|
238
235
|
refs.forEach((ref) => {
|
|
239
236
|
this.unitStore.installPinned(ref);
|
|
240
237
|
});
|
|
241
238
|
nextStrategies.forEach((s) => {
|
|
242
|
-
|
|
239
|
+
writeJsonAtomic(this.getStrategyPath(s.name), s.file);
|
|
243
240
|
});
|
|
244
241
|
}
|
|
245
242
|
pinStrategy(name) {
|
|
246
243
|
this.configService.ensureConfigFileExists();
|
|
247
244
|
const config = this.configService.readConfigOrDefault();
|
|
248
|
-
|
|
245
|
+
requireStrategyInstalled(config, name);
|
|
249
246
|
const file = this.readStrategy(name);
|
|
250
|
-
const units =
|
|
247
|
+
const units = normalizeStrategyUnits(file);
|
|
251
248
|
const installed = this.unitStore.readInstalledDependencyVersions();
|
|
252
249
|
const pinnedUnits = units.map((u) => {
|
|
253
|
-
const pinned =
|
|
250
|
+
const pinned = parsePinnedSpec(u.ref);
|
|
254
251
|
const v = installed[pinned.name];
|
|
255
252
|
if (!v) {
|
|
256
253
|
throw new Error(`Unit package is not installed: ${pinned.name}`);
|
|
@@ -258,17 +255,17 @@ class StrategyService {
|
|
|
258
255
|
const nextRef = `${pinned.name}@${v}`;
|
|
259
256
|
return u.args.length === 0 ? nextRef : { ref: nextRef, args: u.args };
|
|
260
257
|
});
|
|
261
|
-
const others =
|
|
258
|
+
const others = listStrategyNamesFromConfig(config)
|
|
262
259
|
.filter((n) => n !== name)
|
|
263
|
-
.map((n) => ({ name: n, units:
|
|
264
|
-
const nextWanted =
|
|
260
|
+
.map((n) => ({ name: n, units: normalizeStrategyUnits(this.readStrategy(n)) }));
|
|
261
|
+
const nextWanted = collectWantedVersions([
|
|
265
262
|
...others,
|
|
266
263
|
{
|
|
267
264
|
name,
|
|
268
265
|
units: pinnedUnits.map((x) => typeof x === "string" ? { ref: x, args: [] } : { ref: x.ref, args: x.args ?? [] })
|
|
269
266
|
}
|
|
270
267
|
]);
|
|
271
|
-
const conflicts =
|
|
268
|
+
const conflicts = computeConflicts(nextWanted);
|
|
272
269
|
if (conflicts.length > 0) {
|
|
273
270
|
throw new Error(`Strategy version conflicts detected (${conflicts.length}). Run 'pp strategy doctor' for details.`);
|
|
274
271
|
}
|
|
@@ -279,23 +276,22 @@ class StrategyService {
|
|
|
279
276
|
units: pinnedUnits
|
|
280
277
|
}
|
|
281
278
|
};
|
|
282
|
-
|
|
279
|
+
writeJsonAtomic(this.getStrategyPath(name), nextFile);
|
|
283
280
|
}
|
|
284
281
|
doctor() {
|
|
285
282
|
this.configService.ensureConfigFileExists();
|
|
286
283
|
const config = this.configService.readConfigOrDefault();
|
|
287
|
-
const names =
|
|
284
|
+
const names = listStrategyNamesFromConfig(config);
|
|
288
285
|
const installed = this.unitStore.readInstalledDependencyVersions();
|
|
289
286
|
const strategies = names.map((n) => ({
|
|
290
287
|
name: n,
|
|
291
|
-
units:
|
|
288
|
+
units: normalizeStrategyUnits(this.readStrategy(n))
|
|
292
289
|
}));
|
|
293
|
-
const wanted =
|
|
294
|
-
const conflicts =
|
|
290
|
+
const wanted = collectWantedVersions(strategies);
|
|
291
|
+
const conflicts = computeConflicts(wanted);
|
|
295
292
|
const drift = strategies
|
|
296
|
-
.map((s) =>
|
|
293
|
+
.map((s) => computeDrift({ strategyName: s.name, units: s.units, installed }))
|
|
297
294
|
.filter((d) => d.items.length > 0);
|
|
298
295
|
return { conflicts, drift };
|
|
299
296
|
}
|
|
300
297
|
}
|
|
301
|
-
exports.StrategyService = StrategyService;
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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");
|
|
1
|
+
import { installPinnedPackage, updatePackageToLatest } from "../user-packages/user-package-installer.js";
|
|
2
|
+
import { parsePinnedSpec } from "../user-packages/parse-pinned-spec.js";
|
|
3
|
+
import { UserPackageStore } from "../user-packages/user-package-store.js";
|
|
4
|
+
import { isUnitLike, resolveUnitConstructor } from "./unit-validate.js";
|
|
8
5
|
const UNITS_KIND = "units";
|
|
9
6
|
const UNITS_PACKAGE_JSON_NAME = "pagepocket-user-units";
|
|
10
|
-
class UnitStore {
|
|
7
|
+
export class UnitStore {
|
|
8
|
+
store;
|
|
11
9
|
constructor(configService) {
|
|
12
|
-
this.store = new
|
|
10
|
+
this.store = new UserPackageStore(configService, UNITS_KIND);
|
|
13
11
|
}
|
|
14
12
|
getInstallDir() {
|
|
15
13
|
return this.store.getInstallDir();
|
|
@@ -21,14 +19,14 @@ class UnitStore {
|
|
|
21
19
|
return this.store.readInstalledPackageMeta(UNITS_PACKAGE_JSON_NAME, packageName);
|
|
22
20
|
}
|
|
23
21
|
installPinned(ref) {
|
|
24
|
-
const pinned =
|
|
25
|
-
|
|
22
|
+
const pinned = parsePinnedSpec(ref);
|
|
23
|
+
installPinnedPackage(this.store, {
|
|
26
24
|
packageJsonName: UNITS_PACKAGE_JSON_NAME,
|
|
27
25
|
packageSpec: pinned.spec
|
|
28
26
|
});
|
|
29
27
|
}
|
|
30
28
|
updateToLatest(packageName) {
|
|
31
|
-
|
|
29
|
+
updatePackageToLatest(this.store, {
|
|
32
30
|
packageJsonName: UNITS_PACKAGE_JSON_NAME,
|
|
33
31
|
packageName
|
|
34
32
|
});
|
|
@@ -37,18 +35,16 @@ class UnitStore {
|
|
|
37
35
|
return this.store.importModule(UNITS_PACKAGE_JSON_NAME, packageName);
|
|
38
36
|
}
|
|
39
37
|
async instantiateFromRef(ref, args) {
|
|
40
|
-
const pinned =
|
|
38
|
+
const pinned = parsePinnedSpec(ref);
|
|
41
39
|
const mod = await this.importUnitModule(pinned.name);
|
|
42
|
-
const
|
|
43
|
-
if (
|
|
44
|
-
throw new Error(`Unit ${pinned.name}
|
|
40
|
+
const ctor = resolveUnitConstructor(mod);
|
|
41
|
+
if (!ctor) {
|
|
42
|
+
throw new Error(`Unit ${pinned.name} does not export a Unit constructor.`);
|
|
45
43
|
}
|
|
46
|
-
const ctor = def;
|
|
47
44
|
const instance = new ctor(...args);
|
|
48
|
-
if (!
|
|
49
|
-
throw new Error(`Unit ${pinned.name}
|
|
45
|
+
if (!isUnitLike(instance)) {
|
|
46
|
+
throw new Error(`Unit ${pinned.name} exported constructor did not produce a Unit.`);
|
|
50
47
|
}
|
|
51
48
|
return instance;
|
|
52
49
|
}
|
|
53
50
|
}
|
|
54
|
-
exports.UnitStore = UnitStore;
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isUnitLike = void 0;
|
|
4
1
|
const isRecord = (value) => {
|
|
5
2
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
6
3
|
};
|
|
7
4
|
const isCallable = (value) => {
|
|
8
5
|
return typeof value === "function";
|
|
9
6
|
};
|
|
10
|
-
const isUnitLike = (value) => {
|
|
7
|
+
export const isUnitLike = (value) => {
|
|
11
8
|
if (!isRecord(value)) {
|
|
12
9
|
return false;
|
|
13
10
|
}
|
|
@@ -25,4 +22,22 @@ const isUnitLike = (value) => {
|
|
|
25
22
|
}
|
|
26
23
|
return true;
|
|
27
24
|
};
|
|
28
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Resolve a Unit constructor from an imported ESM module.
|
|
27
|
+
*
|
|
28
|
+
* Checks `mod.default` first. If it is not a function (e.g. older packages
|
|
29
|
+
* that only use named exports), falls back to the first exported function
|
|
30
|
+
* whose name ends with "Unit".
|
|
31
|
+
*
|
|
32
|
+
* @returns The constructor function, or `undefined` when none is found.
|
|
33
|
+
*/
|
|
34
|
+
export const resolveUnitConstructor = (mod) => {
|
|
35
|
+
if (typeof mod.default === "function") {
|
|
36
|
+
return mod.default;
|
|
37
|
+
}
|
|
38
|
+
const fallback = Object.values(mod).find((v) => typeof v === "function" && v.name?.endsWith("Unit"));
|
|
39
|
+
if (typeof fallback === "function") {
|
|
40
|
+
return fallback;
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
};
|
|
@@ -1,23 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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");
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
9
4
|
const isRecord = (value) => {
|
|
10
5
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
11
6
|
};
|
|
12
7
|
const isExactSemver = (value) => {
|
|
13
8
|
return /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(value);
|
|
14
9
|
};
|
|
15
|
-
const parsePinnedSpec = (input) => {
|
|
10
|
+
export const parsePinnedSpec = (input) => {
|
|
16
11
|
const trimmed = input.trim();
|
|
17
12
|
if (!trimmed) {
|
|
18
13
|
throw new Error("package spec is empty");
|
|
19
14
|
}
|
|
20
|
-
const req = (
|
|
15
|
+
const req = createRequire(import.meta.url ? fileURLToPath(import.meta.url) : path.join(process.cwd(), "package.json"));
|
|
21
16
|
const npa = req("npm-package-arg");
|
|
22
17
|
const parsed = npa(trimmed);
|
|
23
18
|
if (!isRecord(parsed)) {
|
|
@@ -40,4 +35,3 @@ const parsePinnedSpec = (input) => {
|
|
|
40
35
|
}
|
|
41
36
|
return { name, version: rawSpec, spec: `${name}@${rawSpec}` };
|
|
42
37
|
};
|
|
43
|
-
exports.parsePinnedSpec = parsePinnedSpec;
|