@sparkelf/dsh-plus 0.1.0-rc.29 → 0.1.0-rc.31

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/lib/bin.js CHANGED
@@ -3,9 +3,10 @@ import { r as runApply } from "./apply-e0aMC4B4.js";
3
3
  import { createRequire } from "node:module";
4
4
  import { execFileSync, spawn, spawnSync } from "node:child_process";
5
5
  import { createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
6
- import { dirname, join, resolve } from "node:path";
6
+ import { dirname, join, posix, resolve, win32 } from "node:path";
7
7
  import { homedir } from "node:os";
8
8
  import semver from "semver";
9
+ import { fileURLToPath } from "node:url";
9
10
  import { createServer } from "node:net";
10
11
  //#region lib/types/registry-versions.js
11
12
  /**
@@ -231,6 +232,27 @@ function resolveHome(env = process.env) {
231
232
  return configured === void 0 || configured === "" ? join(homedir(), ".dsh") : resolve(configured);
232
233
  }
233
234
  /**
235
+ * Whether one absolute path is the other or sits beneath it.
236
+ *
237
+ * A path outside the parent has a relative form starting with `..`, which every
238
+ * platform answers the same way. Comparing the text against a literal separator is
239
+ * not: Windows separates with a backslash, so a nested path never matched its own
240
+ * ancestor and every Windows installation reported the distribution as sitting
241
+ * outside its own tree.
242
+ *
243
+ * @param candidate - absolute path to test.
244
+ * @param parent - absolute path that may contain it.
245
+ * @param platform - separating convention, injectable so the Windows answer is
246
+ * testable where the suite runs on a POSIX host.
247
+ * @returns true when the candidate is the parent or inside it.
248
+ */
249
+ function isWithin(candidate, parent, platform = process.platform) {
250
+ if (candidate === parent) return true;
251
+ const path = platform === "win32" ? win32 : posix;
252
+ const inside = path.relative(parent, candidate);
253
+ return inside !== "" && !inside.startsWith("..") && !inside.startsWith(path.sep + "..");
254
+ }
255
+ /**
234
256
  * Resolve the installed distribution directory from one requiring anchor.
235
257
  *
236
258
  * The anchor must be a path inside the consumer's own tree. Resolving from this
@@ -247,9 +269,14 @@ function resolveDistributionDirectory(anchor) {
247
269
  } catch {
248
270
  throw new Error("@sparkelf/dsh-plus is not installed in " + dirname(anchor) + "; run this command from the directory that installed it");
249
271
  }
250
- const consumerRoot = resolve(dirname(anchor));
251
- if (!resolved.startsWith(consumerRoot + "/")) throw new Error("@sparkelf/dsh-plus resolved outside " + consumerRoot + " (found " + resolved + "); run this command from the directory that installed it");
252
- return resolved;
272
+ let current = resolve(dirname(anchor));
273
+ for (;;) {
274
+ if (isWithin(resolved, current)) return resolved;
275
+ const parent = dirname(current);
276
+ if (parent === current) break;
277
+ current = parent;
278
+ }
279
+ throw new Error("@sparkelf/dsh-plus resolved outside " + resolve(dirname(anchor)) + " (found " + resolved + "); run this command from the directory that installed it");
253
280
  }
254
281
  /** The reviewed bundle order and pins the installed distribution declares. */
255
282
  function readDistributionProfile(distributionDirectory) {
@@ -425,6 +452,34 @@ function parseStartOptions(argv) {
425
452
  foreground
426
453
  };
427
454
  }
455
+ /**
456
+ * Where the installation that owns this command keeps its packages.
457
+ *
458
+ * A global install places the command in a shared prefix and a local install places
459
+ * it in a project; neither has anything to do with the directory the user happens to
460
+ * be in. The search therefore starts at this module own file and climbs to the tree
461
+ * npm laid out around it.
462
+ *
463
+ * The test is the launcher, not the distribution: inside a workspace the package
464
+ * resolves its own name, so looking for the distribution would stop at the package
465
+ * rather than at the tree that owns every dependency.
466
+ *
467
+ * @returns absolute path to the installation root.
468
+ */
469
+ function installationRoot() {
470
+ const here = dirname(fileURLToPath(import.meta.url));
471
+ let current = here;
472
+ for (;;) {
473
+ if (existsSync(join(current, "node_modules", "@deepseek-ai", "dsh"))) return current;
474
+ const parent = dirname(current);
475
+ if (parent === current) return here;
476
+ current = parent;
477
+ }
478
+ }
479
+ /** The package.json this installation resolves its dependencies from. */
480
+ function installationAnchor() {
481
+ return join(installationRoot(), "package.json");
482
+ }
428
483
  /** The launcher entry this installation must drive. */
429
484
  function launcherEntry(anchor) {
430
485
  return createRequire(anchor).resolve("@deepseek-ai/dsh/lib/bin.js");
@@ -445,9 +500,9 @@ function runForeground(entry, port, host, open) {
445
500
  }
446
501
  async function start(argv) {
447
502
  const options = parseStartOptions(argv);
448
- const anchor = join(process.cwd(), "package.json");
503
+ const anchor = installationAnchor();
449
504
  const paths = resolvePaths(anchor);
450
- const created = ensureProfile(paths, process.cwd());
505
+ const created = ensureProfile(paths, installationRoot());
451
506
  console.log(created ? "Created the plus profile at " + paths.profileDirectory : "Using the existing plus profile");
452
507
  const entry = launcherEntry(anchor);
453
508
  if (options.foreground) return runForeground(entry, options.port, options.host, options.open);
@@ -506,7 +561,7 @@ async function startDetached(home, entry, options) {
506
561
  return 0;
507
562
  }
508
563
  async function stop() {
509
- const { home } = resolvePaths(join(process.cwd(), "package.json"));
564
+ const { home } = resolvePaths(installationAnchor());
510
565
  const state = readState(home);
511
566
  if (state === void 0) {
512
567
  clearState(home);
@@ -527,7 +582,7 @@ async function stop() {
527
582
  return 0;
528
583
  }
529
584
  function status() {
530
- const state = readState(resolvePaths(join(process.cwd(), "package.json")).home);
585
+ const state = readState(resolvePaths(installationAnchor()).home);
531
586
  if (state === void 0) {
532
587
  console.log("Plus is not running.");
533
588
  console.log("Start it with: dsh-plus start");
@@ -548,7 +603,7 @@ function status() {
548
603
  async function update(argv) {
549
604
  const checkOnly = argv.includes("--check");
550
605
  const assumeYes = argv.includes("--yes");
551
- const paths = resolvePaths(join(process.cwd(), "package.json"));
606
+ const paths = resolvePaths(installationAnchor());
552
607
  const distribution = readDistributionProfile(paths.distributionDirectory);
553
608
  const installed = distribution.version;
554
609
  const newer = newerVersion(distribution.name, installed);
@@ -600,7 +655,7 @@ function confirm(question) {
600
655
  });
601
656
  }
602
657
  async function doctor() {
603
- const paths = resolvePaths(join(process.cwd(), "package.json"));
658
+ const paths = resolvePaths(installationAnchor());
604
659
  let failures = 0;
605
660
  const check = (ok, line) => {
606
661
  console.log((ok ? "ok " : "FAIL ") + line);
@@ -10,7 +10,8 @@
10
10
  import { spawnSync } from 'node:child_process';
11
11
  import { existsSync, readFileSync, writeFileSync } from 'node:fs';
12
12
  import { createRequire } from 'node:module';
13
- import { join } from 'node:path';
13
+ import { dirname, join } from 'node:path';
14
+ import { fileURLToPath } from 'node:url';
14
15
  import { newerVersion } from "./registry-versions.js";
15
16
  import { DEFAULT_PORT, STOP_GRACE_MILLISECONDS, choosePort, clearState, isRunning, portAvailable, readState, spawnServer, stateDirectory, waitForServer, writeState, } from "./standalone-server.js";
16
17
  import { STANDALONE_PROFILE, ensureProfile, readDistributionProfile, resolvePaths, } from "./standalone-profile.js";
@@ -51,6 +52,38 @@ function parseStartOptions(argv) {
51
52
  }
52
53
  return { port, host, open, foreground };
53
54
  }
55
+ /**
56
+ * Where the installation that owns this command keeps its packages.
57
+ *
58
+ * A global install places the command in a shared prefix and a local install places
59
+ * it in a project; neither has anything to do with the directory the user happens to
60
+ * be in. The search therefore starts at this module own file and climbs to the tree
61
+ * npm laid out around it.
62
+ *
63
+ * The test is the launcher, not the distribution: inside a workspace the package
64
+ * resolves its own name, so looking for the distribution would stop at the package
65
+ * rather than at the tree that owns every dependency.
66
+ *
67
+ * @returns absolute path to the installation root.
68
+ */
69
+ function installationRoot() {
70
+ const here = dirname(fileURLToPath(import.meta.url));
71
+ let current = here;
72
+ for (;;) {
73
+ if (existsSync(join(current, 'node_modules', '@deepseek-ai', 'dsh')))
74
+ return current;
75
+ const parent = dirname(current);
76
+ // A tree npm did not lay out has no such ancestor; the module own directory
77
+ // keeps the failure message pointing at the installation that was searched.
78
+ if (parent === current)
79
+ return here;
80
+ current = parent;
81
+ }
82
+ }
83
+ /** The package.json this installation resolves its dependencies from. */
84
+ function installationAnchor() {
85
+ return join(installationRoot(), 'package.json');
86
+ }
54
87
  /** The launcher entry this installation must drive. */
55
88
  function launcherEntry(anchor) {
56
89
  return createRequire(anchor).resolve('@deepseek-ai/dsh/lib/bin.js');
@@ -65,9 +98,9 @@ function runForeground(entry, port, host, open) {
65
98
  }
66
99
  async function start(argv) {
67
100
  const options = parseStartOptions(argv);
68
- const anchor = join(process.cwd(), 'package.json');
101
+ const anchor = installationAnchor();
69
102
  const paths = resolvePaths(anchor);
70
- const created = ensureProfile(paths, process.cwd());
103
+ const created = ensureProfile(paths, installationRoot());
71
104
  console.log(created
72
105
  ? 'Created the ' + STANDALONE_PROFILE + ' profile at ' + paths.profileDirectory
73
106
  : 'Using the existing ' + STANDALONE_PROFILE + ' profile');
@@ -111,7 +144,7 @@ async function startDetached(home, entry, options) {
111
144
  return 0;
112
145
  }
113
146
  async function stop() {
114
- const anchor = join(process.cwd(), 'package.json');
147
+ const anchor = installationAnchor();
115
148
  const { home } = resolvePaths(anchor);
116
149
  const state = readState(home);
117
150
  if (state === undefined) {
@@ -133,7 +166,7 @@ async function stop() {
133
166
  return 0;
134
167
  }
135
168
  function status() {
136
- const anchor = join(process.cwd(), 'package.json');
169
+ const anchor = installationAnchor();
137
170
  const paths = resolvePaths(anchor);
138
171
  const state = readState(paths.home);
139
172
  if (state === undefined) {
@@ -156,7 +189,7 @@ function status() {
156
189
  async function update(argv) {
157
190
  const checkOnly = argv.includes('--check');
158
191
  const assumeYes = argv.includes('--yes');
159
- const anchor = join(process.cwd(), 'package.json');
192
+ const anchor = installationAnchor();
160
193
  const paths = resolvePaths(anchor);
161
194
  const distribution = readDistributionProfile(paths.distributionDirectory);
162
195
  const installed = distribution.version;
@@ -210,7 +243,7 @@ function confirm(question) {
210
243
  });
211
244
  }
212
245
  async function doctor() {
213
- const anchor = join(process.cwd(), 'package.json');
246
+ const anchor = installationAnchor();
214
247
  const paths = resolvePaths(anchor);
215
248
  let failures = 0;
216
249
  const check = (ok, line) => {
@@ -21,6 +21,22 @@ export interface StandalonePaths {
21
21
  }
22
22
  /** Resolve the DSH home, honouring the environment override the launcher itself reads. */
23
23
  export declare function resolveHome(env?: NodeJS.ProcessEnv): string;
24
+ /**
25
+ * Whether one absolute path is the other or sits beneath it.
26
+ *
27
+ * A path outside the parent has a relative form starting with `..`, which every
28
+ * platform answers the same way. Comparing the text against a literal separator is
29
+ * not: Windows separates with a backslash, so a nested path never matched its own
30
+ * ancestor and every Windows installation reported the distribution as sitting
31
+ * outside its own tree.
32
+ *
33
+ * @param candidate - absolute path to test.
34
+ * @param parent - absolute path that may contain it.
35
+ * @param platform - separating convention, injectable so the Windows answer is
36
+ * testable where the suite runs on a POSIX host.
37
+ * @returns true when the candidate is the parent or inside it.
38
+ */
39
+ export declare function isWithin(candidate: string, parent: string, platform?: NodeJS.Platform): boolean;
24
40
  /**
25
41
  * Resolve the installed distribution directory from one requiring anchor.
26
42
  *
@@ -11,7 +11,7 @@
11
11
  import { existsSync, mkdirSync, readdirSync, readFileSync, symlinkSync, writeFileSync } from 'node:fs';
12
12
  import { createRequire } from 'node:module';
13
13
  import { homedir } from 'node:os';
14
- import { dirname, join, resolve } from 'node:path';
14
+ import { dirname, join, posix, resolve, win32 } from 'node:path';
15
15
  /** Profile name a standalone installation owns. */
16
16
  export const STANDALONE_PROFILE = 'plus';
17
17
  function requireRecord(value, label) {
@@ -29,6 +29,28 @@ export function resolveHome(env = process.env) {
29
29
  const configured = env.DSH_HOME;
30
30
  return configured === undefined || configured === '' ? join(homedir(), '.dsh') : resolve(configured);
31
31
  }
32
+ /**
33
+ * Whether one absolute path is the other or sits beneath it.
34
+ *
35
+ * A path outside the parent has a relative form starting with `..`, which every
36
+ * platform answers the same way. Comparing the text against a literal separator is
37
+ * not: Windows separates with a backslash, so a nested path never matched its own
38
+ * ancestor and every Windows installation reported the distribution as sitting
39
+ * outside its own tree.
40
+ *
41
+ * @param candidate - absolute path to test.
42
+ * @param parent - absolute path that may contain it.
43
+ * @param platform - separating convention, injectable so the Windows answer is
44
+ * testable where the suite runs on a POSIX host.
45
+ * @returns true when the candidate is the parent or inside it.
46
+ */
47
+ export function isWithin(candidate, parent, platform = process.platform) {
48
+ if (candidate === parent)
49
+ return true;
50
+ const path = platform === 'win32' ? win32 : posix;
51
+ const inside = path.relative(parent, candidate);
52
+ return inside !== '' && !inside.startsWith('..') && !inside.startsWith(path.sep + '..');
53
+ }
32
54
  /**
33
55
  * Resolve the installed distribution directory from one requiring anchor.
34
56
  *
@@ -49,11 +71,19 @@ export function resolveDistributionDirectory(anchor) {
49
71
  }
50
72
  // A resolution that leaves the consumer tree means the module answered from its own
51
73
  // installation, which reports health for a distribution the consumer never installed.
52
- const consumerRoot = resolve(dirname(anchor));
53
- if (!resolved.startsWith(consumerRoot + '/')) {
54
- throw new Error('@sparkelf/dsh-plus resolved outside ' + consumerRoot + ' (found ' + resolved + '); run this command from the directory that installed it');
74
+ // The comparison walks up from the anchor because the resolved package sits under the
75
+ // anchor's \`node_modules\`, not beside it: requiring \`<root>/package.json\` legitimately
76
+ // reports \`<root>/node_modules/@sparkelf/dsh-plus\`.
77
+ let current = resolve(dirname(anchor));
78
+ for (;;) {
79
+ if (isWithin(resolved, current))
80
+ return resolved;
81
+ const parent = dirname(current);
82
+ if (parent === current)
83
+ break;
84
+ current = parent;
55
85
  }
56
- return resolved;
86
+ throw new Error('@sparkelf/dsh-plus resolved outside ' + resolve(dirname(anchor)) + ' (found ' + resolved + '); run this command from the directory that installed it');
57
87
  }
58
88
  /** The reviewed bundle order and pins the installed distribution declares. */
59
89
  export function readDistributionProfile(distributionDirectory) {
package/package.json CHANGED
@@ -138,5 +138,5 @@
138
138
  },
139
139
  "type": "module",
140
140
  "types": "lib/types/index.d.ts",
141
- "version": "0.1.0-rc.29"
141
+ "version": "0.1.0-rc.31"
142
142
  }