@sparkelf/dsh-plus 0.1.0-rc.39 → 0.1.0-rc.41

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
@@ -363,11 +363,40 @@ function installProfilePackages(paths, consumerDirectory) {
363
363
  linkNestedBundles(paths, profileModules);
364
364
  return;
365
365
  }
366
- runPnpm(paths.profileDirectory, ["install", "--no-frozen-lockfile"], "pnpm install in the plus profile");
366
+ installWithRegistryFallback(paths);
367
367
  alignReplacedPackageNames(profileModules);
368
368
  linkNestedBundles(paths, profileModules);
369
369
  }
370
370
  /**
371
+ * Install the profile from the first registry that answers.
372
+ *
373
+ * The install fetches a full dependency closure, and a mainland consumer reaches the
374
+ * mirror far faster than the origin. Trying the preferred registry and falling back once
375
+ * keeps a first start quick without failing when the preferred one is unreachable.
376
+ *
377
+ * @param paths - resolved standalone paths.
378
+ */
379
+ function installWithRegistryFallback(paths) {
380
+ const registries = registryOrder();
381
+ for (const [index, registry] of registries.entries()) {
382
+ const result = spawnSync("pnpm", [
383
+ "install",
384
+ "--no-frozen-lockfile",
385
+ "--registry",
386
+ registry
387
+ ], {
388
+ cwd: paths.profileDirectory,
389
+ stdio: "inherit",
390
+ shell: process.platform === "win32"
391
+ });
392
+ if (result.error !== void 0) throw result.error;
393
+ if (result.status === 0) return;
394
+ const next = registries[index + 1];
395
+ if (next === void 0) throw new Error("pnpm install in the plus profile failed with exit code " + String(result.status));
396
+ console.log("Install from " + registry + " failed; trying " + next + ".");
397
+ }
398
+ }
399
+ /**
371
400
  * Make each replaced package declare the name of the location it occupies.
372
401
  *
373
402
  * \`overrides\` installs our build at the official path, but the manifest inside still
@@ -401,25 +430,29 @@ function alignReplacedPackageNames(profileModules) {
401
430
  renameSync(temporary, manifestPath);
402
431
  }
403
432
  }
433
+ /** The npm registry the distribution installs from by default. */
434
+ const OFFICIAL_REGISTRY = "https://registry.npmjs.org";
435
+ /** The mainland mirror, which serves the same packages and answers faster there. */
436
+ const MAINLAND_REGISTRY = "https://registry.npmmirror.com";
404
437
  /**
405
- * Run pnpm in one directory, inheriting its output.
438
+ * The registries to try, in order, for this machine.
406
439
  *
407
- * Windows resolves a command through its shell: \`spawnSync\` on a \`.cmd\` shim fails
408
- * with EINVAL, so the call goes through \`cmd.exe\` there. This matches the runner in
409
- * \`apply.ts\`, which the apply path has used on Windows since it shipped.
440
+ * A mainland locale reaches the mirror faster than the origin, which is why the Desktop
441
+ * installer already prefers it there. The same choice belongs to the profile install: it
442
+ * fetches a full dependency closure, so the delay is the first thing a consumer notices.
443
+ * `DSH_PLUS_INSTALL_REGISTRY` overrides the choice, and a failure falls back once.
410
444
  *
411
- * @param cwd - the directory pnpm runs in.
412
- * @param args - pnpm arguments, without the executable.
413
- * @param label - the failing step's name, for the error.
445
+ * @returns registries to try in order.
414
446
  */
415
- function runPnpm(cwd, args, label) {
416
- const result = spawnSync("pnpm", [...args], {
417
- cwd,
418
- stdio: "inherit",
419
- shell: process.platform === "win32"
420
- });
421
- if (result.error !== void 0) throw result.error;
422
- if (result.status !== 0) throw new Error(label + " failed with exit code " + String(result.status));
447
+ function registryOrder() {
448
+ const configured = process.env.DSH_PLUS_INSTALL_REGISTRY;
449
+ if (configured !== void 0 && configured !== "") return [configured, MAINLAND_REGISTRY];
450
+ const locale = [
451
+ process.env.LANG,
452
+ process.env.LC_ALL,
453
+ Intl.DateTimeFormat().resolvedOptions().locale
454
+ ].filter((value) => value !== void 0).join(" ").toLowerCase();
455
+ return locale.includes("zh") || locale.includes("cn") ? [MAINLAND_REGISTRY, OFFICIAL_REGISTRY] : [OFFICIAL_REGISTRY, MAINLAND_REGISTRY];
423
456
  }
424
457
  /**
425
458
  * Report whether pnpm can run.
@@ -447,7 +480,9 @@ function pnpmAvailable() {
447
480
  * @returns commands to try in order, stopping at the first that works.
448
481
  */
449
482
  function pnpmInstallCommands() {
450
- return ["corepack enable pnpm", "npm install -g pnpm"];
483
+ const registry = registryOrder()[0];
484
+ if (registry === void 0) return ["corepack enable pnpm", "npm install -g pnpm"];
485
+ return ["corepack enable pnpm", "npm install -g pnpm --registry " + registry];
451
486
  }
452
487
  /**
453
488
  * The command to show a consumer who declines the automatic install.
@@ -743,9 +778,14 @@ async function start(argv) {
743
778
  return 1;
744
779
  }
745
780
  let installed = false;
781
+ const preferred = registryOrder()[0];
746
782
  for (const candidate of pnpmInstallCommands()) if (spawnSync(candidate, {
747
783
  stdio: "inherit",
748
- shell: true
784
+ shell: true,
785
+ env: preferred === void 0 ? process.env : {
786
+ ...process.env,
787
+ COREPACK_NPM_REGISTRY: preferred
788
+ }
749
789
  }).status === 0 && pnpmAvailable()) {
750
790
  installed = true;
751
791
  break;
@@ -14,7 +14,7 @@ import { dirname, join } from 'node:path';
14
14
  import { fileURLToPath } from 'node:url';
15
15
  import { newerVersion } from "./registry-versions.js";
16
16
  import { DEFAULT_PORT, STOP_GRACE_MILLISECONDS, choosePort, clearState, isRunning, portAvailable, readState, spawnServer, stateDirectory, waitForAuthenticatedUrl, waitForServer, writeState, } from "./standalone-server.js";
17
- import { STANDALONE_PROFILE, applyProfileNpmPatches, ensureProfile, pnpmAvailable, pnpmInstallCommand, pnpmInstallCommands, readDistributionProfile, resolvePaths, } from "./standalone-profile.js";
17
+ import { STANDALONE_PROFILE, applyProfileNpmPatches, ensureProfile, pnpmAvailable, pnpmInstallCommand, pnpmInstallCommands, registryOrder, readDistributionProfile, resolvePaths, } from "./standalone-profile.js";
18
18
  /** Milliseconds a start waits for the server to answer before reporting failure. */
19
19
  const READY_TIMEOUT_MILLISECONDS = 90_000;
20
20
  function parseStartOptions(argv) {
@@ -114,8 +114,17 @@ async function start(argv) {
114
114
  // Try each installer in turn: Corepack is absent on an installation that disabled it,
115
115
  // and npm succeeds there. A failure reports the commands rather than a stack trace.
116
116
  let installed = false;
117
+ const preferred = registryOrder()[0];
117
118
  for (const candidate of pnpmInstallCommands()) {
118
- const attempt = spawnSync(candidate, { stdio: 'inherit', shell: true });
119
+ // Corepack reads its registry from the environment; npm takes a flag, which the
120
+ // command already carries. Setting it for both keeps the download on the mirror.
121
+ const attempt = spawnSync(candidate, {
122
+ stdio: 'inherit',
123
+ shell: true,
124
+ env: preferred === undefined
125
+ ? process.env
126
+ : { ...process.env, COREPACK_NPM_REGISTRY: preferred },
127
+ });
119
128
  if (attempt.status === 0 && pnpmAvailable()) {
120
129
  installed = true;
121
130
  break;
@@ -94,6 +94,17 @@ export declare function installProfilePackages(paths: StandalonePaths, consumerD
94
94
  * @param profileModules - the profile's \`node_modules\` directory.
95
95
  */
96
96
  export declare function alignReplacedPackageNames(profileModules: string): void;
97
+ /**
98
+ * The registries to try, in order, for this machine.
99
+ *
100
+ * A mainland locale reaches the mirror faster than the origin, which is why the Desktop
101
+ * installer already prefers it there. The same choice belongs to the profile install: it
102
+ * fetches a full dependency closure, so the delay is the first thing a consumer notices.
103
+ * `DSH_PLUS_INSTALL_REGISTRY` overrides the choice, and a failure falls back once.
104
+ *
105
+ * @returns registries to try in order.
106
+ */
107
+ export declare function registryOrder(): readonly string[];
97
108
  /**
98
109
  * Report whether pnpm can run.
99
110
  *
@@ -159,10 +159,38 @@ export function installProfilePackages(paths, consumerDirectory) {
159
159
  linkNestedBundles(paths, profileModules);
160
160
  return;
161
161
  }
162
- runPnpm(paths.profileDirectory, ['install', '--no-frozen-lockfile'], 'pnpm install in the plus profile');
162
+ installWithRegistryFallback(paths);
163
163
  alignReplacedPackageNames(profileModules);
164
164
  linkNestedBundles(paths, profileModules);
165
165
  }
166
+ /**
167
+ * Install the profile from the first registry that answers.
168
+ *
169
+ * The install fetches a full dependency closure, and a mainland consumer reaches the
170
+ * mirror far faster than the origin. Trying the preferred registry and falling back once
171
+ * keeps a first start quick without failing when the preferred one is unreachable.
172
+ *
173
+ * @param paths - resolved standalone paths.
174
+ */
175
+ function installWithRegistryFallback(paths) {
176
+ const registries = registryOrder();
177
+ for (const [index, registry] of registries.entries()) {
178
+ const result = spawnSync('pnpm', ['install', '--no-frozen-lockfile', '--registry', registry], {
179
+ cwd: paths.profileDirectory,
180
+ stdio: 'inherit',
181
+ shell: process.platform === 'win32',
182
+ });
183
+ if (result.error !== undefined)
184
+ throw result.error;
185
+ if (result.status === 0)
186
+ return;
187
+ const next = registries[index + 1];
188
+ if (next === undefined) {
189
+ throw new Error('pnpm install in the plus profile failed with exit code ' + String(result.status));
190
+ }
191
+ console.log('Install from ' + registry + ' failed; trying ' + next + '.');
192
+ }
193
+ }
166
194
  /**
167
195
  * Make each replaced package declare the name of the location it occupies.
168
196
  *
@@ -197,27 +225,34 @@ export function alignReplacedPackageNames(profileModules) {
197
225
  renameSync(temporary, manifestPath);
198
226
  }
199
227
  }
228
+ /** The npm registry the distribution installs from by default. */
229
+ const OFFICIAL_REGISTRY = 'https://registry.npmjs.org';
230
+ /** The mainland mirror, which serves the same packages and answers faster there. */
231
+ const MAINLAND_REGISTRY = 'https://registry.npmmirror.com';
200
232
  /**
201
- * Run pnpm in one directory, inheriting its output.
233
+ * The registries to try, in order, for this machine.
202
234
  *
203
- * Windows resolves a command through its shell: \`spawnSync\` on a \`.cmd\` shim fails
204
- * with EINVAL, so the call goes through \`cmd.exe\` there. This matches the runner in
205
- * \`apply.ts\`, which the apply path has used on Windows since it shipped.
235
+ * A mainland locale reaches the mirror faster than the origin, which is why the Desktop
236
+ * installer already prefers it there. The same choice belongs to the profile install: it
237
+ * fetches a full dependency closure, so the delay is the first thing a consumer notices.
238
+ * `DSH_PLUS_INSTALL_REGISTRY` overrides the choice, and a failure falls back once.
206
239
  *
207
- * @param cwd - the directory pnpm runs in.
208
- * @param args - pnpm arguments, without the executable.
209
- * @param label - the failing step's name, for the error.
240
+ * @returns registries to try in order.
210
241
  */
211
- function runPnpm(cwd, args, label) {
212
- const result = spawnSync('pnpm', [...args], {
213
- cwd,
214
- stdio: 'inherit',
215
- shell: process.platform === 'win32',
216
- });
217
- if (result.error !== undefined)
218
- throw result.error;
219
- if (result.status !== 0)
220
- throw new Error(label + ' failed with exit code ' + String(result.status));
242
+ export function registryOrder() {
243
+ const configured = process.env.DSH_PLUS_INSTALL_REGISTRY;
244
+ if (configured !== undefined && configured !== '')
245
+ return [configured, MAINLAND_REGISTRY];
246
+ // Intl reports the system locale on every platform. The POSIX variables are empty on
247
+ // Windows, so reading them alone classified every Windows console as non-mainland and
248
+ // reached the origin first — measured in a consumer's log, which showed npmjs failing
249
+ // before the mirror answered.
250
+ const locale = [process.env.LANG, process.env.LC_ALL, Intl.DateTimeFormat().resolvedOptions().locale]
251
+ .filter(value => value !== undefined)
252
+ .join(' ')
253
+ .toLowerCase();
254
+ const mainlandFirst = locale.includes('zh') || locale.includes('cn');
255
+ return mainlandFirst ? [MAINLAND_REGISTRY, OFFICIAL_REGISTRY] : [OFFICIAL_REGISTRY, MAINLAND_REGISTRY];
221
256
  }
222
257
  /**
223
258
  * Report whether pnpm can run.
@@ -246,7 +281,13 @@ export function pnpmAvailable() {
246
281
  * @returns commands to try in order, stopping at the first that works.
247
282
  */
248
283
  export function pnpmInstallCommands() {
249
- return ['corepack enable pnpm', 'npm install -g pnpm'];
284
+ const registry = registryOrder()[0];
285
+ if (registry === undefined)
286
+ return ['corepack enable pnpm', 'npm install -g pnpm'];
287
+ // Both installers take the registry explicitly. npm does so with a flag; corepack reads
288
+ // COREPACK_NPM_REGISTRY, which the caller sets. A mainland consumer therefore downloads
289
+ // the package from the mirror, for the same reason the profile install prefers it.
290
+ return ['corepack enable pnpm', 'npm install -g pnpm --registry ' + registry];
250
291
  }
251
292
  /**
252
293
  * The command to show a consumer who declines the automatic install.
package/package.json CHANGED
@@ -58,16 +58,15 @@
58
58
  ],
59
59
  "profile": {
60
60
  "allowBuilds": {
61
- "@deepseek-ai/dsh-subprocess-local@0.1.5-rc.2": true,
62
- "@google/genai@1.52.0": false,
63
- "@officecli/officecli@1.0.147": true,
64
- "cpu-features@0.0.10": false,
65
- "koffi@3.3.0": true,
66
- "node-pty@1.1.0": true,
67
- "node-pty@1.2.0-beta.15": true,
68
- "oracledb@7.0.1": true,
69
- "protobufjs@7.6.6": false,
70
- "ssh2@1.17.0": true
61
+ "@deepseek-ai/dsh-subprocess-local": true,
62
+ "@google/genai": false,
63
+ "@officecli/officecli": true,
64
+ "cpu-features": false,
65
+ "koffi": true,
66
+ "node-pty": true,
67
+ "oracledb": true,
68
+ "protobufjs": false,
69
+ "ssh2": true
71
70
  },
72
71
  "bundles": [
73
72
  "@deepseek-ai/dsh-base",
@@ -166,5 +165,5 @@
166
165
  },
167
166
  "type": "module",
168
167
  "types": "lib/types/index.d.ts",
169
- "version": "0.1.0-rc.39"
168
+ "version": "0.1.0-rc.41"
170
169
  }