@ws-test-realm/admin-kit 0.1.7 → 0.1.8

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/bin/ws-modules.js CHANGED
@@ -3,58 +3,76 @@
3
3
  // workspace.
4
4
  //
5
5
  // Usage:
6
- // ws-modules <names...|all> [--build] [--pack] [--deploy] [--with-deps]
6
+ // ws-modules <names...|all> [--build] [--pack] [--deploy] [--purge] [--with-deps]
7
7
  //
8
8
  // <names...> one or more project names from angular.json
9
9
  // all expand to every project in angular.json
10
10
  // --build ng build each lib (topo-ordered)
11
11
  // --pack federation-pack each lib into dist/admin-remotes/<id>/
12
12
  // --deploy push jar + types into <wpmRoot>/<modulesFolder>/<id>-*.jar
13
+ // --purge strip prior federation contribution from the jar (or ext
14
+ // dir) and host's .federation/<id>/ types dir. Resolves the
15
+ // target the same way --deploy does (wsconfig override >
16
+ // project package.json's wsmodules.target / pom).
13
17
  // --with-deps expand selection to include each name's in-workspace deps
14
18
  //
15
19
  // Behaviour:
16
20
  // - No args at all → print usage, exit 1
17
21
  // - Positional + step flag(s) → run exactly those steps, non-interactive
18
22
  // - Positional, no step flags → prompt y/n for each of build/pack/deploy
19
- // (and --with-deps); run the selected set
23
+ // (and --with-deps); run the selected set.
24
+ // Purge is NOT prompted (destructive +
25
+ // not part of the normal wire cycle).
20
26
  // - Step flag(s) but no positional → error (need to know what to operate on)
21
27
  //
28
+ // Step ordering when multiple are passed: purge → build → pack → deploy.
29
+ //
22
30
  // Examples:
23
31
  // ws-modules all # interactive
24
32
  // ws-modules db-login # interactive, just this project
25
33
  // ws-modules all --build --pack --deploy # wire the whole stack
26
34
  // ws-modules db-login --build --with-deps # build db-login + its deps
27
35
  // ws-modules crm shop-common --pack # pack two specific projects
36
+ // ws-modules all --purge # strip stale contributions
37
+ // ws-modules all --purge --build --pack --deploy # nuke + rewire
28
38
 
29
39
  const readline = require("readline");
30
40
  const { buildLibs, packRemotes } = require("../lib/build-modules");
31
41
  const { deployRemotes } = require("../lib/deploy-remotes");
42
+ const { purgeRemotes } = require("../lib/purge-remotes");
32
43
 
33
- const STEP_FLAGS = ["build", "pack", "deploy"];
44
+ const STEP_FLAGS = ["purge", "build", "pack", "deploy"];
34
45
  const PASSIVE_FLAGS = ["with-deps"];
35
46
  const KNOWN_FLAGS = new Set([...STEP_FLAGS, ...PASSIVE_FLAGS]);
36
47
 
37
- const USAGE = `ws-modules <names...|all> [--build] [--pack] [--deploy] [--with-deps]
48
+ const USAGE = `ws-modules <names...|all> [--build] [--pack] [--deploy] [--purge] [--with-deps]
38
49
 
39
- Drive build / pack / deploy across an admin-modules workspace.
50
+ Drive build / pack / deploy / purge across an admin-modules workspace.
40
51
 
41
52
  Positional:
42
53
  <names...> one or more project names from angular.json
43
54
  all expand to every project in angular.json
44
55
 
45
- Step flags (run exactly the steps you list — if none, you'll be prompted):
56
+ Step flags (run exactly the steps you list — if none, you'll be prompted for
57
+ build/pack/deploy; purge is never prompted):
46
58
  --build ng build each lib (topo-ordered)
47
59
  --pack federation-pack each lib into dist/admin-remotes/<id>/
48
60
  --deploy push jar + types into <wpmRoot>/<modulesFolder>/<id>-*.jar
61
+ --purge strip prior federation contribution from the jar (or ext
62
+ dir) and host's .federation/<id>/ types dir
49
63
 
50
64
  Other:
51
65
  --with-deps expand selection to include each name's in-workspace deps
52
66
 
67
+ Order when multiple steps are passed: purge → build → pack → deploy.
68
+
53
69
  Examples:
54
70
  ws-modules all
55
71
  ws-modules db-login --build
56
72
  ws-modules all --build --pack --deploy
57
73
  ws-modules crm shop-common --pack
74
+ ws-modules all --purge
75
+ ws-modules all --purge --build --pack --deploy
58
76
  `;
59
77
 
60
78
  function parseArgv(argv) {
@@ -143,6 +161,7 @@ async function main() {
143
161
  let withDeps;
144
162
  if (stepFlagsPresent) {
145
163
  steps = {
164
+ purge: !!argv.flags.purge,
146
165
  build: !!argv.flags.build,
147
166
  pack: !!argv.flags.pack,
148
167
  deploy: !!argv.flags.deploy,
@@ -150,11 +169,11 @@ async function main() {
150
169
  withDeps = !!argv.flags["with-deps"];
151
170
  } else {
152
171
  const ans = await promptForSteps(!!argv.flags["with-deps"]);
153
- steps = { build: ans.build, pack: ans.pack, deploy: ans.deploy };
172
+ steps = { purge: false, build: ans.build, pack: ans.pack, deploy: ans.deploy };
154
173
  withDeps = ans.withDeps;
155
174
  }
156
175
 
157
- if (!steps.build && !steps.pack && !steps.deploy) {
176
+ if (!steps.purge && !steps.build && !steps.pack && !steps.deploy) {
158
177
  console.log("No steps selected. Nothing to do.");
159
178
  return;
160
179
  }
@@ -164,10 +183,11 @@ async function main() {
164
183
 
165
184
  console.log(
166
185
  `\nws-modules: ${argv.positional.join(" ")} →` +
167
- ` build=${steps.build} pack=${steps.pack} deploy=${steps.deploy}` +
186
+ ` purge=${steps.purge} build=${steps.build} pack=${steps.pack} deploy=${steps.deploy}` +
168
187
  ` with-deps=${withDeps}`
169
188
  );
170
189
 
190
+ if (steps.purge) purgeRemotes(common);
171
191
  if (steps.build) buildLibs(common);
172
192
  if (steps.pack) packRemotes(common);
173
193
  if (steps.deploy) deployRemotes(common);
@@ -2,85 +2,8 @@ const fs = require("fs");
2
2
  const path = require("path");
3
3
  const { packIntoJar } = require("./pack-into-jar");
4
4
  const { loadOrder, partitionByKind } = require("./build-modules");
5
- const { resolveProjectPom, readArtifactId } = require("./pom");
6
- const { resolveOne, findMatches } = require("./jar-glob");
7
-
8
- function readWsconfig(workspaceDir) {
9
- const file = path.join(workspaceDir, "wsconfig.json");
10
- if (!fs.existsSync(file)) {
11
- throw new Error(
12
- `No wsconfig.json in ${workspaceDir}. Run \`ws-wire-host <wpm-root>\` first.`
13
- );
14
- }
15
- return JSON.parse(fs.readFileSync(file, "utf8"));
16
- }
17
-
18
- function readModulesJson(wpmRoot) {
19
- const file = path.join(wpmRoot, "modules.json");
20
- if (!fs.existsSync(file)) {
21
- throw new Error(`No modules.json at ${wpmRoot}.`);
22
- }
23
- return JSON.parse(fs.readFileSync(file, "utf8"));
24
- }
25
-
26
- function readProjectPkg(workspaceDir, name) {
27
- const pkgPath = path.join(workspaceDir, "projects", name, "package.json");
28
- if (!fs.existsSync(pkgPath)) return {};
29
- try {
30
- return JSON.parse(fs.readFileSync(pkgPath, "utf8"));
31
- } catch {
32
- return {};
33
- }
34
- }
35
-
36
- // Decide jar vs ext for a project, honouring wsconfig override > package.json.
37
- // Returns { kind: "jar", jarPath } or { kind: "ext", extDir }.
38
- function resolveTarget({ workspaceDir, projectName, wsconfig, wpmRoot, modulesFolder }) {
39
- const modulesDir = path.resolve(wpmRoot, modulesFolder);
40
- const extDir = path.join(
41
- wpmRoot,
42
- "scripts",
43
- "ext-admin-remotes",
44
- projectName
45
- );
46
-
47
- // 1. wsconfig override
48
- const override =
49
- wsconfig.deployOverrides && wsconfig.deployOverrides[projectName];
50
- if (override) {
51
- if (override.jar && override.ext) {
52
- throw new Error(
53
- `wsconfig.deployOverrides.${projectName}: set exactly one of 'jar' or 'ext', not both`
54
- );
55
- }
56
- if (override.ext === true) {
57
- return { kind: "ext", extDir };
58
- }
59
- if (override.jar) {
60
- const jarPath = resolveOne(modulesDir, override.jar, projectName);
61
- return { kind: "jar", jarPath };
62
- }
63
- }
64
-
65
- // 2. project package.json
66
- const pkg = readProjectPkg(workspaceDir, projectName);
67
- const target = (pkg.wsmodules && pkg.wsmodules.target) || "jar";
68
- if (target === "ext") {
69
- return { kind: "ext", extDir };
70
- }
71
- if (target !== "jar") {
72
- throw new Error(
73
- `Invalid wsmodules.target for ${projectName}: ${JSON.stringify(target)} (expected 'jar' or 'ext')`
74
- );
75
- }
76
-
77
- // 3. pom-derived artifactId
78
- const pomField = pkg.wsmodules && pkg.wsmodules.pom;
79
- const pomPath = resolveProjectPom(workspaceDir, projectName, pomField);
80
- const artifactId = readArtifactId(pomPath);
81
- const jarPath = resolveOne(modulesDir, `${artifactId}-*.jar`, projectName);
82
- return { kind: "jar", jarPath, pomPath, artifactId };
83
- }
5
+ const { findMatches } = require("./jar-glob");
6
+ const { loadDeployContext, resolveTarget } = require("./target-resolution");
84
7
 
85
8
  function copyTypes(distLib, hostFederationDir) {
86
9
  if (fs.existsSync(hostFederationDir)) {
@@ -94,7 +17,6 @@ function copyTypes(distLib, hostFederationDir) {
94
17
  // dir (replace-existing semantics). Also warn if a same-id-named jar exists
95
18
  // in the modules folder — possible name-collision abuse signal.
96
19
  function deployExt({ workspaceDir, id, modulesDir, extDir }) {
97
- // Collision check: same constrained-glob rules as everywhere else.
98
20
  const collisions = findMatches(modulesDir, `${id}-*.jar`);
99
21
  if (collisions.length) {
100
22
  const list = collisions.map((c) => path.join(modulesDir, c)).join(", ");
@@ -113,7 +35,6 @@ function deployExt({ workspaceDir, id, modulesDir, extDir }) {
113
35
  }
114
36
  fs.mkdirSync(extDir, { recursive: true });
115
37
  fs.cpSync(srcDir, extDir, { recursive: true });
116
- // Count files for the report.
117
38
  let fileCount = 0;
118
39
  const walk = (d) => {
119
40
  for (const e of fs.readdirSync(d, { withFileTypes: true })) {
@@ -131,7 +52,8 @@ function deployOne({
131
52
  id,
132
53
  wpmRoot,
133
54
  modulesFolder,
134
- adminBuildFolder,
55
+ adminDir,
56
+ modulesDir,
135
57
  wsconfig,
136
58
  }) {
137
59
  const distRemoteDir = path.join(workspaceDir, "dist", "admin-remotes", id);
@@ -146,8 +68,6 @@ function deployOne({
146
68
  throw new Error(`Lib dist missing: ${distLibDir}. Run \`build\` first.`);
147
69
  }
148
70
 
149
- const modulesDir = path.resolve(wpmRoot, modulesFolder);
150
- const adminDir = path.resolve(wpmRoot, adminBuildFolder);
151
71
  const hostFederationDir = path.join(adminDir, ".federation", id);
152
72
 
153
73
  const target = resolveTarget({
@@ -158,7 +78,6 @@ function deployOne({
158
78
  modulesFolder,
159
79
  });
160
80
 
161
- // Perform the artifact deploy first; only on success do we touch host types.
162
81
  let artifactReport;
163
82
  if (target.kind === "jar") {
164
83
  const packResult = packIntoJar({
@@ -183,22 +102,13 @@ function deployOne({
183
102
  artifactReport = { kind: "ext", ...extResult };
184
103
  }
185
104
 
186
- // Artifact deploy succeeded — now publish types.
187
105
  copyTypes(distLibDir, hostFederationDir);
188
106
 
189
107
  return { ...artifactReport, hostFederationDir };
190
108
  }
191
109
 
192
110
  function deployRemotes({ workspaceDir, restrictTo = [], withDeps = false }) {
193
- const wsconfig = readWsconfig(workspaceDir);
194
- if (!wsconfig.wpmRoot) {
195
- throw new Error(
196
- "wsconfig.json missing wpmRoot. Run `ws-wire-host <wpm-root>`."
197
- );
198
- }
199
- const modulesJson = readModulesJson(wsconfig.wpmRoot);
200
- const modulesFolder = modulesJson.modulesFolder || "./modules";
201
- const adminBuildFolder = modulesJson.adminBuildFolder || "./admin";
111
+ const ctx = loadDeployContext(workspaceDir);
202
112
 
203
113
  const { order } = loadOrder({ workspaceDir, restrictTo, withDeps });
204
114
  const { remotes, libraries } = partitionByKind(workspaceDir, order);
@@ -217,10 +127,11 @@ function deployRemotes({ workspaceDir, restrictTo = [], withDeps = false }) {
217
127
  const r = deployOne({
218
128
  workspaceDir,
219
129
  id,
220
- wpmRoot: wsconfig.wpmRoot,
221
- modulesFolder,
222
- adminBuildFolder,
223
- wsconfig,
130
+ wpmRoot: ctx.wpmRoot,
131
+ modulesFolder: ctx.modulesFolder,
132
+ adminDir: ctx.adminDir,
133
+ modulesDir: ctx.modulesDir,
134
+ wsconfig: ctx.wsconfig,
224
135
  });
225
136
  if (r.kind === "jar") {
226
137
  console.log(` target: jar`);
@@ -0,0 +1,50 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const AdmZip = require("adm-zip");
4
+
5
+ // Strip a federated remote contribution from a jar by dropping every entry
6
+ // under META-INF/federation/<id>/. Inverse of packIntoJar. Returns
7
+ // { jarPath, droppedEntries, hadContribution }.
8
+ function purgeJar({ jarPath, id }) {
9
+ if (!fs.existsSync(jarPath)) {
10
+ throw new Error(`Jar not found: ${jarPath}`);
11
+ }
12
+
13
+ const contribRoot = `META-INF/federation/${id}/`;
14
+ const original = new AdmZip(jarPath);
15
+ const out = new AdmZip();
16
+
17
+ let dropped = 0;
18
+ for (const entry of original.getEntries()) {
19
+ if (entry.entryName.startsWith(contribRoot)) {
20
+ dropped++;
21
+ continue;
22
+ }
23
+ if (entry.isDirectory) {
24
+ out.addFile(entry.entryName, Buffer.alloc(0), "", entry.attr);
25
+ } else {
26
+ out.addFile(
27
+ entry.entryName,
28
+ entry.getData(),
29
+ entry.comment,
30
+ entry.attr
31
+ );
32
+ }
33
+ }
34
+
35
+ if (dropped === 0) {
36
+ return { jarPath, droppedEntries: 0, hadContribution: false };
37
+ }
38
+
39
+ const dir = path.dirname(jarPath);
40
+ const tmp = path.join(
41
+ dir,
42
+ `.${path.basename(jarPath)}.repack.${process.pid}.tmp`
43
+ );
44
+ out.writeZip(tmp);
45
+ fs.renameSync(tmp, jarPath);
46
+
47
+ return { jarPath, droppedEntries: dropped, hadContribution: true };
48
+ }
49
+
50
+ module.exports = { purgeJar };
@@ -0,0 +1,120 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { purgeJar } = require("./purge-jar");
4
+ const { loadOrder, partitionByKind } = require("./build-modules");
5
+ const { loadDeployContext, resolveTarget } = require("./target-resolution");
6
+
7
+ // Purge a single remote's deployed footprint:
8
+ // - jar target: drop META-INF/federation/<id>/ entries from the jar
9
+ // - ext target: remove <wpmRoot>/scripts/ext-admin-remotes/<id>/
10
+ // - always: remove host's <adminDir>/.federation/<id>/ types dir
11
+ //
12
+ // Uses the exact same target resolution as deploy — wsconfig override >
13
+ // project package.json (target=ext | pom-derived artifactId). No part of the
14
+ // jar location is implied from the project id; same abuse-resistance as deploy.
15
+ function purgeOne({
16
+ workspaceDir,
17
+ id,
18
+ wpmRoot,
19
+ modulesFolder,
20
+ adminDir,
21
+ wsconfig,
22
+ }) {
23
+ const hostFederationDir = path.join(adminDir, ".federation", id);
24
+
25
+ const target = resolveTarget({
26
+ workspaceDir,
27
+ projectName: id,
28
+ wsconfig,
29
+ wpmRoot,
30
+ modulesFolder,
31
+ });
32
+
33
+ let artifactReport;
34
+ if (target.kind === "jar") {
35
+ const result = purgeJar({ jarPath: target.jarPath, id });
36
+ artifactReport = {
37
+ kind: "jar",
38
+ jarPath: target.jarPath,
39
+ pomPath: target.pomPath,
40
+ artifactId: target.artifactId,
41
+ ...result,
42
+ };
43
+ } else {
44
+ let removed = false;
45
+ if (fs.existsSync(target.extDir)) {
46
+ fs.rmSync(target.extDir, { recursive: true, force: true });
47
+ removed = true;
48
+ }
49
+ artifactReport = { kind: "ext", extDir: target.extDir, removedExtDir: removed };
50
+ }
51
+
52
+ let typesRemoved = false;
53
+ if (fs.existsSync(hostFederationDir)) {
54
+ fs.rmSync(hostFederationDir, { recursive: true, force: true });
55
+ typesRemoved = true;
56
+ }
57
+
58
+ return { ...artifactReport, hostFederationDir, typesRemoved };
59
+ }
60
+
61
+ function purgeRemotes({ workspaceDir, restrictTo = [], withDeps = false }) {
62
+ const ctx = loadDeployContext(workspaceDir);
63
+
64
+ const { order } = loadOrder({ workspaceDir, restrictTo, withDeps });
65
+ const { remotes, libraries } = partitionByKind(workspaceDir, order);
66
+
67
+ console.log(`\n=== Purge order ===`);
68
+ remotes.forEach((n, i) => console.log(` ${i + 1}. ${n}`));
69
+ if (libraries.length) {
70
+ console.log(
71
+ ` (skipping libraries: ${libraries.join(", ")} — wsmodules.kind=library)`
72
+ );
73
+ }
74
+
75
+ const results = [];
76
+ for (const id of remotes) {
77
+ console.log(`\n=== purge ${id} ===`);
78
+ try {
79
+ const r = purgeOne({
80
+ workspaceDir,
81
+ id,
82
+ wpmRoot: ctx.wpmRoot,
83
+ modulesFolder: ctx.modulesFolder,
84
+ adminDir: ctx.adminDir,
85
+ wsconfig: ctx.wsconfig,
86
+ });
87
+ if (r.kind === "jar") {
88
+ console.log(` target: jar`);
89
+ console.log(` jar: ${r.jarPath}`);
90
+ if (r.artifactId) console.log(` artifactId: ${r.artifactId}`);
91
+ if (r.pomPath) console.log(` pom: ${r.pomPath}`);
92
+ if (r.hadContribution) {
93
+ console.log(` dropped: ${r.droppedEntries} entries`);
94
+ } else {
95
+ console.log(` dropped: (nothing to purge)`);
96
+ }
97
+ } else {
98
+ console.log(` target: ext`);
99
+ console.log(` ext dir: ${r.extDir}`);
100
+ console.log(` ext dir: ${r.removedExtDir ? "removed" : "(absent)"}`);
101
+ }
102
+ console.log(
103
+ ` host types: ${r.typesRemoved ? "removed" : "(absent)"} (${r.hostFederationDir})`
104
+ );
105
+ results.push({ id, ok: true, ...r });
106
+ } catch (err) {
107
+ console.error(` \x1b[31mfailed:\x1b[0m ${err.message}`);
108
+ results.push({ id, ok: false, error: err.message });
109
+ }
110
+ }
111
+
112
+ const ok = results.filter((r) => r.ok).length;
113
+ const failed = results.length - ok;
114
+ console.log(
115
+ `\nPurged ${ok} module(s)${failed ? `, ${failed} failed` : ""}.`
116
+ );
117
+ return { results };
118
+ }
119
+
120
+ module.exports = { purgeRemotes, purgeOne };
@@ -0,0 +1,121 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { resolveProjectPom, readArtifactId } = require("./pom");
4
+ const { resolveOne } = require("./jar-glob");
5
+
6
+ function readWsconfig(workspaceDir) {
7
+ const file = path.join(workspaceDir, "wsconfig.json");
8
+ if (!fs.existsSync(file)) {
9
+ throw new Error(
10
+ `No wsconfig.json in ${workspaceDir}. Run \`ws-wire-host <wpm-root>\` first.`
11
+ );
12
+ }
13
+ return JSON.parse(fs.readFileSync(file, "utf8"));
14
+ }
15
+
16
+ function readModulesJson(wpmRoot) {
17
+ const file = path.join(wpmRoot, "modules.json");
18
+ if (!fs.existsSync(file)) {
19
+ throw new Error(`No modules.json at ${wpmRoot}.`);
20
+ }
21
+ return JSON.parse(fs.readFileSync(file, "utf8"));
22
+ }
23
+
24
+ function readProjectPkg(workspaceDir, name) {
25
+ const pkgPath = path.join(workspaceDir, "projects", name, "package.json");
26
+ if (!fs.existsSync(pkgPath)) return {};
27
+ try {
28
+ return JSON.parse(fs.readFileSync(pkgPath, "utf8"));
29
+ } catch {
30
+ return {};
31
+ }
32
+ }
33
+
34
+ // Load the deployment context once and reuse across step runners (deploy,
35
+ // purge, ...). Returns { wsconfig, wpmRoot, modulesFolder, modulesDir,
36
+ // adminBuildFolder, adminDir }.
37
+ function loadDeployContext(workspaceDir) {
38
+ const wsconfig = readWsconfig(workspaceDir);
39
+ if (!wsconfig.wpmRoot) {
40
+ throw new Error(
41
+ "wsconfig.json missing wpmRoot. Run `ws-wire-host <wpm-root>`."
42
+ );
43
+ }
44
+ const modulesJson = readModulesJson(wsconfig.wpmRoot);
45
+ const modulesFolder = modulesJson.modulesFolder || "./modules";
46
+ const adminBuildFolder = modulesJson.adminBuildFolder || "./admin";
47
+ const modulesDir = path.resolve(wsconfig.wpmRoot, modulesFolder);
48
+ const adminDir = path.resolve(wsconfig.wpmRoot, adminBuildFolder);
49
+ return {
50
+ wsconfig,
51
+ wpmRoot: wsconfig.wpmRoot,
52
+ modulesFolder,
53
+ adminBuildFolder,
54
+ modulesDir,
55
+ adminDir,
56
+ };
57
+ }
58
+
59
+ // Decide jar vs ext for a project, honouring wsconfig override > package.json.
60
+ // Returns { kind: "jar", jarPath, pomPath?, artifactId? } or
61
+ // { kind: "ext", extDir }.
62
+ function resolveTarget({
63
+ workspaceDir,
64
+ projectName,
65
+ wsconfig,
66
+ wpmRoot,
67
+ modulesFolder,
68
+ }) {
69
+ const modulesDir = path.resolve(wpmRoot, modulesFolder);
70
+ const extDir = path.join(
71
+ wpmRoot,
72
+ "scripts",
73
+ "ext-admin-remotes",
74
+ projectName
75
+ );
76
+
77
+ // 1. wsconfig override
78
+ const override =
79
+ wsconfig.deployOverrides && wsconfig.deployOverrides[projectName];
80
+ if (override) {
81
+ if (override.jar && override.ext) {
82
+ throw new Error(
83
+ `wsconfig.deployOverrides.${projectName}: set exactly one of 'jar' or 'ext', not both`
84
+ );
85
+ }
86
+ if (override.ext === true) {
87
+ return { kind: "ext", extDir };
88
+ }
89
+ if (override.jar) {
90
+ const jarPath = resolveOne(modulesDir, override.jar, projectName);
91
+ return { kind: "jar", jarPath };
92
+ }
93
+ }
94
+
95
+ // 2. project package.json
96
+ const pkg = readProjectPkg(workspaceDir, projectName);
97
+ const target = (pkg.wsmodules && pkg.wsmodules.target) || "jar";
98
+ if (target === "ext") {
99
+ return { kind: "ext", extDir };
100
+ }
101
+ if (target !== "jar") {
102
+ throw new Error(
103
+ `Invalid wsmodules.target for ${projectName}: ${JSON.stringify(target)} (expected 'jar' or 'ext')`
104
+ );
105
+ }
106
+
107
+ // 3. pom-derived artifactId
108
+ const pomField = pkg.wsmodules && pkg.wsmodules.pom;
109
+ const pomPath = resolveProjectPom(workspaceDir, projectName, pomField);
110
+ const artifactId = readArtifactId(pomPath);
111
+ const jarPath = resolveOne(modulesDir, `${artifactId}-*.jar`, projectName);
112
+ return { kind: "jar", jarPath, pomPath, artifactId };
113
+ }
114
+
115
+ module.exports = {
116
+ readWsconfig,
117
+ readModulesJson,
118
+ readProjectPkg,
119
+ loadDeployContext,
120
+ resolveTarget,
121
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ws-test-realm/admin-kit",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Workflow CLI + scaffolding for Wiresphere admin-modules workspaces. Ships `ws-init-workspace` (init/merge into a project, stamps @wiresphere/shared's `getOverrides()` into the workspace's npm overrides block), `ws-modules` (build/pack/deploy driver), `ws-generate-module`/`ws-drop-module`, `ws-wire-host`, `ws-wire-pom`, `ws-sync-paths`, and `ws-pack-remote`. Depends on @wiresphere/devkit (toolchain BOM, peerDeps); pairs with @wiresphere/shared (runtime BOM + MF share map + overrides manifest). Requires npm 9+ for overrides + peerDep resolution to coexist cleanly.",
5
5
  "license": "Artistic-2.0",
6
6
  "publishConfig": {
@@ -12,6 +12,7 @@
12
12
  "build": "ws-modules --build",
13
13
  "pack": "ws-modules --pack",
14
14
  "deploy": "ws-modules --deploy",
15
+ "purge": "ws-modules --purge",
15
16
  "wire": "ws-modules --build --pack --deploy"
16
17
  },
17
18
  "prettier": "@ws-test-realm/devkit/prettier.config.js",