@superblocksteam/sdk 2.0.123 → 2.0.124-next.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.
- package/.turbo/turbo-build.log +1 -1
- package/dist/cli-replacement/automatic-upgrades.d.ts +37 -1
- package/dist/cli-replacement/automatic-upgrades.d.ts.map +1 -1
- package/dist/cli-replacement/automatic-upgrades.js +162 -10
- package/dist/cli-replacement/automatic-upgrades.js.map +1 -1
- package/dist/cli-replacement/automatic-upgrades.test.js +377 -8
- package/dist/cli-replacement/automatic-upgrades.test.js.map +1 -1
- package/dist/cli-replacement/dependency-install-classifier.d.mts +21 -0
- package/dist/cli-replacement/dependency-install-classifier.d.mts.map +1 -0
- package/dist/cli-replacement/dependency-install-classifier.mjs +83 -0
- package/dist/cli-replacement/dependency-install-classifier.mjs.map +1 -0
- package/dist/cli-replacement/dependency-install-classifier.test.d.mts +2 -0
- package/dist/cli-replacement/dependency-install-classifier.test.d.mts.map +1 -0
- package/dist/cli-replacement/dependency-install-classifier.test.mjs +51 -0
- package/dist/cli-replacement/dependency-install-classifier.test.mjs.map +1 -0
- package/dist/cli-replacement/dev-s3-restore.test.mjs +170 -14
- package/dist/cli-replacement/dev-s3-restore.test.mjs.map +1 -1
- package/dist/cli-replacement/dev-startup-git-before-dbfs-order.test.mjs +33 -2
- package/dist/cli-replacement/dev-startup-git-before-dbfs-order.test.mjs.map +1 -1
- package/dist/cli-replacement/dev-token-priming.test.d.mts +31 -0
- package/dist/cli-replacement/dev-token-priming.test.d.mts.map +1 -0
- package/dist/cli-replacement/dev-token-priming.test.mjs +87 -0
- package/dist/cli-replacement/dev-token-priming.test.mjs.map +1 -0
- package/dist/cli-replacement/dev.d.mts +36 -0
- package/dist/cli-replacement/dev.d.mts.map +1 -1
- package/dist/cli-replacement/dev.interception.test.d.mts +2 -0
- package/dist/cli-replacement/dev.interception.test.d.mts.map +1 -0
- package/dist/cli-replacement/dev.interception.test.mjs +68 -0
- package/dist/cli-replacement/dev.interception.test.mjs.map +1 -0
- package/dist/cli-replacement/dev.mjs +396 -62
- package/dist/cli-replacement/dev.mjs.map +1 -1
- package/dist/cli-replacement/home-npmrc.d.mts +180 -0
- package/dist/cli-replacement/home-npmrc.d.mts.map +1 -0
- package/dist/cli-replacement/home-npmrc.mjs +283 -0
- package/dist/cli-replacement/home-npmrc.mjs.map +1 -0
- package/dist/cli-replacement/home-npmrc.test.d.mts +10 -0
- package/dist/cli-replacement/home-npmrc.test.d.mts.map +1 -0
- package/dist/cli-replacement/home-npmrc.test.mjs +582 -0
- package/dist/cli-replacement/home-npmrc.test.mjs.map +1 -0
- package/dist/cli-replacement/install-packages.classify.test.d.mts +2 -0
- package/dist/cli-replacement/install-packages.classify.test.d.mts.map +1 -0
- package/dist/cli-replacement/install-packages.classify.test.mjs +125 -0
- package/dist/cli-replacement/install-packages.classify.test.mjs.map +1 -0
- package/dist/cli-replacement/install-packages.npm-registry.test.d.mts +2 -0
- package/dist/cli-replacement/install-packages.npm-registry.test.d.mts.map +1 -0
- package/dist/cli-replacement/install-packages.npm-registry.test.mjs +260 -0
- package/dist/cli-replacement/install-packages.npm-registry.test.mjs.map +1 -0
- package/dist/cli-replacement/post-upgrade-lockfile-strip.d.mts +58 -0
- package/dist/cli-replacement/post-upgrade-lockfile-strip.d.mts.map +1 -0
- package/dist/cli-replacement/post-upgrade-lockfile-strip.mjs +224 -0
- package/dist/cli-replacement/post-upgrade-lockfile-strip.mjs.map +1 -0
- package/dist/cli-replacement/post-upgrade-lockfile-strip.test.d.mts +11 -0
- package/dist/cli-replacement/post-upgrade-lockfile-strip.test.d.mts.map +1 -0
- package/dist/cli-replacement/post-upgrade-lockfile-strip.test.mjs +317 -0
- package/dist/cli-replacement/post-upgrade-lockfile-strip.test.mjs.map +1 -0
- package/dist/cli-replacement/userconfig-env.integration.test.d.mts +26 -0
- package/dist/cli-replacement/userconfig-env.integration.test.d.mts.map +1 -0
- package/dist/cli-replacement/userconfig-env.integration.test.mjs +148 -0
- package/dist/cli-replacement/userconfig-env.integration.test.mjs.map +1 -0
- package/dist/dev-utils/dev-server-metrics.d.mts +25 -0
- package/dist/dev-utils/dev-server-metrics.d.mts.map +1 -1
- package/dist/dev-utils/dev-server-metrics.mjs +84 -0
- package/dist/dev-utils/dev-server-metrics.mjs.map +1 -1
- package/dist/dev-utils/dev-server-metrics.test.d.mts +2 -0
- package/dist/dev-utils/dev-server-metrics.test.d.mts.map +1 -0
- package/dist/dev-utils/dev-server-metrics.test.mjs +26 -0
- package/dist/dev-utils/dev-server-metrics.test.mjs.map +1 -0
- package/dist/dev-utils/dev-server.d.mts +23 -1
- package/dist/dev-utils/dev-server.d.mts.map +1 -1
- package/dist/dev-utils/dev-server.mjs +21 -9
- package/dist/dev-utils/dev-server.mjs.map +1 -1
- package/dist/dev-utils/dev-server.status.test.d.mts +2 -0
- package/dist/dev-utils/dev-server.status.test.d.mts.map +1 -0
- package/dist/dev-utils/dev-server.status.test.mjs +41 -0
- package/dist/dev-utils/dev-server.status.test.mjs.map +1 -0
- package/dist/dev-utils/token-manager.d.ts +31 -0
- package/dist/dev-utils/token-manager.d.ts.map +1 -1
- package/dist/dev-utils/token-manager.js +34 -0
- package/dist/dev-utils/token-manager.js.map +1 -1
- package/dist/telemetry/local-obs.js +1 -1
- package/dist/telemetry/local-obs.js.map +1 -1
- package/dist/telemetry/util.js +1 -1
- package/dist/types/scoped-jwt-token-payload.d.ts +1 -0
- package/dist/types/scoped-jwt-token-payload.d.ts.map +1 -1
- package/dist/version-control.d.mts.map +1 -1
- package/dist/version-control.mjs +6 -7
- package/dist/version-control.mjs.map +1 -1
- package/package.json +12 -12
- package/src/cli-replacement/automatic-upgrades.test.ts +530 -8
- package/src/cli-replacement/automatic-upgrades.ts +179 -7
- package/src/cli-replacement/dependency-install-classifier.mts +118 -0
- package/src/cli-replacement/dependency-install-classifier.test.mts +72 -0
- package/src/cli-replacement/dev-s3-restore.test.mts +210 -14
- package/src/cli-replacement/dev-startup-git-before-dbfs-order.test.mts +35 -2
- package/src/cli-replacement/dev-token-priming.test.mts +103 -0
- package/src/cli-replacement/dev.interception.test.mts +80 -0
- package/src/cli-replacement/dev.mts +495 -92
- package/src/cli-replacement/home-npmrc.mts +409 -0
- package/src/cli-replacement/home-npmrc.test.mts +757 -0
- package/src/cli-replacement/install-packages.classify.test.mts +168 -0
- package/src/cli-replacement/install-packages.npm-registry.test.mts +345 -0
- package/src/cli-replacement/post-upgrade-lockfile-strip.mts +296 -0
- package/src/cli-replacement/post-upgrade-lockfile-strip.test.mts +482 -0
- package/src/cli-replacement/userconfig-env.integration.test.mts +189 -0
- package/src/dev-utils/dev-server-metrics.mts +96 -0
- package/src/dev-utils/dev-server-metrics.test.mts +38 -0
- package/src/dev-utils/dev-server.mts +48 -8
- package/src/dev-utils/dev-server.status.test.mts +58 -0
- package/src/dev-utils/token-manager.ts +36 -0
- package/src/telemetry/local-obs.ts +1 -1
- package/src/telemetry/util.ts +1 -1
- package/src/types/scoped-jwt-token-payload.ts +1 -0
- package/src/version-control.mts +8 -6
- package/tsconfig.tsbuildinfo +1 -1
- package/.turbo/turbo-publish-package.log +0 -0
|
@@ -13,19 +13,22 @@ import { maskUnixSignals } from "@superblocksteam/util";
|
|
|
13
13
|
import { AiService, AiServiceFeatureFlags, SnapshotManager, isSdkApiTemplate, stripResolvedFromLockfile, } from "@superblocksteam/vite-plugin-file-sync/ai-service";
|
|
14
14
|
import { createGitService } from "@superblocksteam/vite-plugin-file-sync/git-service";
|
|
15
15
|
import { LockService, LockType, } from "@superblocksteam/vite-plugin-file-sync/lock-service";
|
|
16
|
+
import { shouldIgnoreInstallScripts, } from "@superblocksteam/vite-plugin-file-sync/npm-registry";
|
|
16
17
|
import { OperationQueue } from "@superblocksteam/vite-plugin-file-sync/operation-queue";
|
|
17
18
|
import { AutoConnectingRpcClient } from "@superblocksteam/vite-plugin-file-sync/server-rpc";
|
|
18
19
|
import { SyncService } from "@superblocksteam/vite-plugin-file-sync/sync-service";
|
|
20
|
+
import { devServerMetrics } from "../dev-utils/dev-server-metrics.mjs";
|
|
19
21
|
import { createDevServer } from "../dev-utils/dev-server.mjs";
|
|
20
22
|
import { AUTO_UPGRADE_EXIT_CODE } from "../index.js";
|
|
21
23
|
import { getTracer } from "../telemetry/index.js";
|
|
22
24
|
import { getErrorMeta, getLogger } from "../telemetry/logging.js";
|
|
23
|
-
import { checkVersionsAndWritePackageJson } from "./automatic-upgrades.js";
|
|
25
|
+
import { buildInstallEnv, checkVersionsAndWritePackageJson, } from "./automatic-upgrades.js";
|
|
26
|
+
import { classifyInitialInstallError, InitialInstallFailed, } from "./dependency-install-classifier.mjs";
|
|
24
27
|
import { ensureRemoteHasDefaultBranch, getGitErrorFields, } from "./git-repo-setup.mjs";
|
|
28
|
+
import { superblocksLogsPath, superblocksNpmrcPath, syncHomeNpmrc, } from "./home-npmrc.mjs";
|
|
25
29
|
import { normalizeWorkspaceProtocolForNpm } from "./normalize-workspace-protocol.js";
|
|
26
30
|
import { didPackageJsonSnapshotChange, packageJsonSnapshot, packageJsonSnapshotDiagnostic, restoreManagedPackageDependencies, } from "./package-json-snapshot.mjs";
|
|
27
31
|
import { getCurrentCliVersion } from "./version-detection.js";
|
|
28
|
-
const exec = promisify(child_process.exec);
|
|
29
32
|
const passErrorToVSCode = (message, logger) => {
|
|
30
33
|
if (message && process.env.SUPERBLOCKS_VSCODE === "true") {
|
|
31
34
|
// Prefixing with `clierr:` will make the VS code extension capture this message and show it to the user.
|
|
@@ -160,7 +163,7 @@ async function normalizePackageJsonForNpm(cwd, logger) {
|
|
|
160
163
|
throw new Error(`Failed to write normalized package.json at ${packageJsonPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
161
164
|
}
|
|
162
165
|
}
|
|
163
|
-
async function installPackages(cwd, logger) {
|
|
166
|
+
export async function installPackages(cwd, logger, npmRegistryClient) {
|
|
164
167
|
try {
|
|
165
168
|
const pm = await detect({
|
|
166
169
|
strategies: [
|
|
@@ -181,23 +184,144 @@ async function installPackages(cwd, logger) {
|
|
|
181
184
|
// resolve a published version instead.
|
|
182
185
|
await normalizePackageJsonForNpm(cwd, logger);
|
|
183
186
|
}
|
|
184
|
-
|
|
187
|
+
// Honor the per-org `allow_install_scripts` policy on the dev-server
|
|
188
|
+
// startup install just like AppShell does for agent-driven installs
|
|
189
|
+
// (see `shell.ts`). When the org has opted out (`allowInstallScripts ===
|
|
190
|
+
// false`), append `--ignore-scripts` so lifecycle scripts don't run
|
|
191
|
+
// during dev-server boot. `undefined` (LD flag off, server omitted the
|
|
192
|
+
// field, no client wired up, or the resolution itself failed) keeps
|
|
193
|
+
// today's behaviour. The client owns last-known-good / cross-outage
|
|
194
|
+
// policy preservation; we deliberately swallow resolution errors here
|
|
195
|
+
// so a transient registry-endpoint outage doesn't abort the user's
|
|
196
|
+
// startup install — the policy default is "scripts allowed" anyway.
|
|
197
|
+
let ignoreScripts = false;
|
|
198
|
+
if (npmRegistryClient) {
|
|
199
|
+
try {
|
|
200
|
+
const result = await npmRegistryClient.getConfig();
|
|
201
|
+
ignoreScripts = shouldIgnoreInstallScripts(result);
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
logger.warn("Could not resolve npm install-scripts policy; proceeding without --ignore-scripts", getErrorMeta(err));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// `--json` makes npm channel the structured error envelope (code +
|
|
208
|
+
// summary + detail) onto stdout so a failure can be classified into a
|
|
209
|
+
// DependencyInstallError. Output-only: it does not change resolution,
|
|
210
|
+
// only how npm reports it. The success-path `logger.info(stdout)` below
|
|
211
|
+
// then logs JSON, which is acceptable.
|
|
212
|
+
const baseArgs = pm.agent === "npm" ? ["--fund=false", "--audit=false", "--json"] : [];
|
|
213
|
+
const installArgs = ignoreScripts
|
|
214
|
+
? [...baseArgs, "--ignore-scripts"]
|
|
215
|
+
: baseArgs;
|
|
216
|
+
const installCommand = resolveCommand(pm.agent, "install", installArgs);
|
|
185
217
|
if (!installCommand) {
|
|
186
218
|
logger.warn(`Could not determine install command for ${pm.agent}, skipping package installation`);
|
|
187
219
|
return;
|
|
188
220
|
}
|
|
189
221
|
const { command, args } = installCommand;
|
|
190
222
|
logger.info(`Running ${command} ${args.join(" ")} to install dependencies…`);
|
|
223
|
+
// Pin both npm and pnpm to the Superblocks-owned userconfig via env
|
|
224
|
+
// overlay. `buildInstallEnv` (from `automatic-upgrades.ts`) sets
|
|
225
|
+
// both `NPM_CONFIG_USERCONFIG` (npm, pnpm <= 10) and
|
|
226
|
+
// `PNPM_CONFIG_USERCONFIG` (pnpm 11+) — pnpm 11 stopped honouring
|
|
227
|
+
// the `npm_config_*` env vars. CLI flags differ between agents
|
|
228
|
+
// (`--userconfig=` for npm, `--config.userconfig=` for pnpm), so
|
|
229
|
+
// the env overlay is the uniform mechanism. Without this, customer
|
|
230
|
+
// pods using a private registry would read default `~/.npmrc`
|
|
231
|
+
// (empty after the relocation) and fail to resolve private
|
|
232
|
+
// packages. Centralised in `buildInstallEnv` so this and the CLI
|
|
233
|
+
// auto-upgrade install stay in sync.
|
|
234
|
+
// Ensure the per-app log dir exists so npm's debug log (routed via
|
|
235
|
+
// `NPM_CONFIG_LOGS_DIR` in `buildInstallEnv`) lands in a predictable
|
|
236
|
+
// place. npm would create it on demand, but doing it up front means the
|
|
237
|
+
// folder exists even for pnpm runs (which ignore the var) and for any
|
|
238
|
+
// log-collection sidecar watching `<app>/.superblocks/logs`.
|
|
239
|
+
const logsDir = superblocksLogsPath(cwd);
|
|
240
|
+
await nodeFs.mkdir(logsDir, { recursive: true }).catch(() => undefined);
|
|
241
|
+
// Resolve the promisified `exec` at CALL time — not module scope — so a
|
|
242
|
+
// test's `vi.mock("node:child_process")` is always honoured regardless of
|
|
243
|
+
// when `dev.mjs` was first evaluated. A module-scope capture binds to
|
|
244
|
+
// whichever `child_process.exec` was live at first import; if a sibling
|
|
245
|
+
// test imports this module before installing its own mock (e.g.
|
|
246
|
+
// `dev-token-priming` / `dev.interception`, which don't mock
|
|
247
|
+
// `child_process`), that binding is the REAL npm and the classify test's
|
|
248
|
+
// mock silently never takes effect — the exact APPS-4450 CI failure
|
|
249
|
+
// (`category: "unknown"`, `npmErrorCode: undefined` from real npm against a
|
|
250
|
+
// missing `/tmp/app`).
|
|
251
|
+
const exec = promisify(child_process.exec);
|
|
191
252
|
const { stdout } = await exec(`${command} ${args.join(" ")}`, {
|
|
192
253
|
cwd,
|
|
254
|
+
env: buildInstallEnv(superblocksNpmrcPath(), logsDir),
|
|
193
255
|
});
|
|
194
256
|
logger.info("Package installation completed successfully");
|
|
195
257
|
logger.info(stdout);
|
|
196
258
|
}
|
|
197
259
|
catch (error) {
|
|
198
260
|
logger.error("Error during package installation", getErrorMeta(error));
|
|
199
|
-
|
|
261
|
+
// `util.promisify(child_process.exec)` preserves `.stdout`/`.stderr` on
|
|
262
|
+
// the rejected error separately; with `--json`, the structured npm error
|
|
263
|
+
// envelope lands on `.stdout`. Classify it into a DependencyInstallError
|
|
264
|
+
// and throw the `InitialInstallFailed` marker so the `dev()` catch can
|
|
265
|
+
// degrade by origin rather than crash-looping the dev-server pod.
|
|
266
|
+
const ctx = await buildInstallParseContext(cwd, npmRegistryClient);
|
|
267
|
+
const f = error;
|
|
268
|
+
const serverError = classifyInitialInstallError({ stdout: f.stdout, stderr: f.stderr, message: f.message }, ctx);
|
|
269
|
+
throw new InitialInstallFailed(serverError);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Build the `ParseContext` the dependency-install classifier needs from the
|
|
274
|
+
* failing install's working directory + registry client:
|
|
275
|
+
*
|
|
276
|
+
* - `requestedPackages` — the union of `dependencies` + `devDependencies`
|
|
277
|
+
* in the app's `package.json`, used by the registry-blocked renderers to
|
|
278
|
+
* name the failing specs when npm's `--json` `detail` doesn't.
|
|
279
|
+
* - `hasAnyRegistryConfigured` — the tri-state derived from the registry
|
|
280
|
+
* client's most recent `getConfig()`, mirroring AppShell's
|
|
281
|
+
* `deriveHasAnyRegistryConfigured` (`shell.ts`): `configured`/`stale`
|
|
282
|
+
* → `true` (rows known to exist), `not-configured` → `false` (deliberate
|
|
283
|
+
* "no rows"), `unreachable` → `undefined` ("we don't know" — the renderer
|
|
284
|
+
* falls back to the default variant). Left `undefined` when no client is
|
|
285
|
+
* wired in or the resolution itself throws.
|
|
286
|
+
*
|
|
287
|
+
* Best-effort throughout: a missing/unparseable package.json or a registry
|
|
288
|
+
* outage must not mask the underlying install failure, so both lookups are
|
|
289
|
+
* caught and degrade to empty/undefined.
|
|
290
|
+
*/
|
|
291
|
+
async function buildInstallParseContext(cwd, npmRegistryClient) {
|
|
292
|
+
let requestedPackages = [];
|
|
293
|
+
try {
|
|
294
|
+
const pkg = JSON.parse(await nodeFs.readFile(path.join(cwd, "package.json"), "utf8"));
|
|
295
|
+
requestedPackages = Object.entries({
|
|
296
|
+
...(pkg.dependencies ?? {}),
|
|
297
|
+
...(pkg.devDependencies ?? {}),
|
|
298
|
+
}).map(([name, version]) => ({ name, version: String(version) }));
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
/* best-effort: a missing/unparseable package.json yields no specs */
|
|
200
302
|
}
|
|
303
|
+
let hasAnyRegistryConfigured;
|
|
304
|
+
if (npmRegistryClient) {
|
|
305
|
+
try {
|
|
306
|
+
const result = await npmRegistryClient.getConfig();
|
|
307
|
+
switch (result.source) {
|
|
308
|
+
case "configured":
|
|
309
|
+
case "stale":
|
|
310
|
+
hasAnyRegistryConfigured = true;
|
|
311
|
+
break;
|
|
312
|
+
case "not-configured":
|
|
313
|
+
hasAnyRegistryConfigured = false;
|
|
314
|
+
break;
|
|
315
|
+
case "unreachable":
|
|
316
|
+
hasAnyRegistryConfigured = undefined;
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
catch {
|
|
321
|
+
/* leave undefined → renderers treat as "don't know" / default variant */
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return { requestedPackages, hasAnyRegistryConfigured };
|
|
201
325
|
}
|
|
202
326
|
export var DevServerAutoUpgradeMode;
|
|
203
327
|
(function (DevServerAutoUpgradeMode) {
|
|
@@ -212,8 +336,61 @@ export var DevServerAutoUpgradeMode;
|
|
|
212
336
|
*/
|
|
213
337
|
DevServerAutoUpgradeMode["SKIP_CLI_ONLY"] = "skip-cli-only";
|
|
214
338
|
})(DevServerAutoUpgradeMode || (DevServerAutoUpgradeMode = {}));
|
|
339
|
+
/**
|
|
340
|
+
* Seed `tokenManager` with the initial token the CLI received from auth.json
|
|
341
|
+
* (standalone dev) or `/_sb_activate` (SABS live-edit pod activation), so the
|
|
342
|
+
* `NpmRegistryClient`'s JWT source has a usable bearer credential on cold
|
|
343
|
+
* boot.
|
|
344
|
+
*
|
|
345
|
+
* Without this seeding, `TokenManager.updateToken` is only ever called by
|
|
346
|
+
* `AuthHotReloadServer` (`auth-hot-reload.mts`) — and that socket is
|
|
347
|
+
* explicitly disabled on live-edit pods (`sabs/entrypoint-local.sh` sets
|
|
348
|
+
* `SUPERBLOCKS_AUTH_HOT_RELOAD=false`), so `NpmRegistryClient.getConfig()`
|
|
349
|
+
* cannot authenticate its server fetch on cold boot. The result is
|
|
350
|
+
* `source: "unreachable"`, `syncHomeNpmrc` skips with `~/.npmrc` left
|
|
351
|
+
* untouched, and the CLI auto-upgrade that fires moments later resolves
|
|
352
|
+
* `npm install -g @superblocksteam/cli@…` through public npm instead of the
|
|
353
|
+
* customer's configured private registry.
|
|
354
|
+
*
|
|
355
|
+
* Call-site ordering is intentionally NOT load-bearing: `TokenManager`
|
|
356
|
+
* retains the current token as state and `AiService` reads
|
|
357
|
+
* `tokenManager.getCurrentToken()` synchronously at construction time, so
|
|
358
|
+
* the prime call works whether it runs before or after consumer
|
|
359
|
+
* construction. The `tokenUpdated` event stream stays as the refresh
|
|
360
|
+
* channel for future rotations (hot-reload pushes, JWT renewal), so this
|
|
361
|
+
* seed is purely additive.
|
|
362
|
+
*/
|
|
363
|
+
export function primeTokenManagerWithInitialToken(tokenManager, token) {
|
|
364
|
+
if (token) {
|
|
365
|
+
tokenManager.updateToken(token);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
/** Decide how the startup catch handles an error: degrade (record, keep Vite up)
|
|
369
|
+
* for an app-install failure (the InitialInstallFailed marker), or exit for
|
|
370
|
+
* anything else (lock/sync/upgrade). Pure + unit-tested. Does NOT call process.exit. */
|
|
371
|
+
export function handleStartupError(error, status, logger) {
|
|
372
|
+
if (error instanceof InitialInstallFailed) {
|
|
373
|
+
status.serverErrors.push(error.serverError);
|
|
374
|
+
logger.error("[dev-server] initial dependency install failed; keeping dev server up", getErrorMeta(error));
|
|
375
|
+
devServerMetrics.recordInitialInstallFailure({
|
|
376
|
+
category: error.serverError.category,
|
|
377
|
+
npmErrorCode: error.serverError.npmErrorCode,
|
|
378
|
+
hasAnyRegistryConfigured: error.serverError.hasAnyRegistryConfigured,
|
|
379
|
+
});
|
|
380
|
+
return "degrade";
|
|
381
|
+
}
|
|
382
|
+
return "exit";
|
|
383
|
+
}
|
|
215
384
|
export async function dev(options) {
|
|
216
385
|
const { cwd, tokenConfig, devServerPort, skipSync, applicationConfig, autoUpgradeMode, tokenManager, authHotReloadServer, sdk, } = options;
|
|
386
|
+
// Seed the tokenManager with the initial CLI token so downstream consumers
|
|
387
|
+
// (AiService, AutoConnectingRpcClient, syncHomeNpmrc) have a usable
|
|
388
|
+
// bearer credential without waiting on the AuthHotReloadServer push
|
|
389
|
+
// refresh (which is disabled on live-edit pods). Order-independent: the
|
|
390
|
+
// manager retains state and AiService reads it synchronously during
|
|
391
|
+
// construction. See `primeTokenManagerWithInitialToken` for the
|
|
392
|
+
// cold-boot rationale.
|
|
393
|
+
primeTokenManagerWithInitialToken(tokenManager, tokenConfig.token);
|
|
217
394
|
// May be overridden by a pending snapshot restore
|
|
218
395
|
let { downloadFirst, uploadFirst } = options;
|
|
219
396
|
// Services that will be created
|
|
@@ -224,17 +401,40 @@ export async function dev(options) {
|
|
|
224
401
|
let snapshotManager;
|
|
225
402
|
let gitUserName;
|
|
226
403
|
let gitUserEmail;
|
|
227
|
-
// In-flight
|
|
228
|
-
//
|
|
229
|
-
//
|
|
404
|
+
// In-flight handles for the two background jobs we launch from sync.
|
|
405
|
+
// We keep them SEPARATE so an install rejection cannot swallow an upgrade
|
|
406
|
+
// rejection (or vice-versa) the way a single `Promise.all` would: that
|
|
407
|
+
// bundle settles on the FIRST rejection and silently absorbs the other's
|
|
408
|
+
// outcome, breaking APPS-4457's intent that auto-upgrade failures still
|
|
409
|
+
// exit (not degrade) and CLI-restart-on-upgrade still happens even if the
|
|
410
|
+
// app install fails.
|
|
411
|
+
//
|
|
412
|
+
// - packageUpgradePromise: library upgrades (and their `npm install`
|
|
413
|
+
// side-effects) from `checkVersionsAndWritePackageJson`. Rejection is
|
|
414
|
+
// OUT of scope for graceful degrade (APPS-4457) — `handleStartupError`
|
|
415
|
+
// routes it to `process.exit(1)`.
|
|
416
|
+
// - packageInstallPromise: the app's verification `npm install`.
|
|
417
|
+
// Rejection MAY be the `InitialInstallFailed` marker, which
|
|
418
|
+
// `handleStartupError` routes to the degrade path.
|
|
419
|
+
let packageUpgradePromise;
|
|
230
420
|
let packageInstallPromise;
|
|
231
421
|
const tracer = getTracer();
|
|
232
422
|
const logger = getLogger(options.logger);
|
|
233
|
-
// Joins the in-flight
|
|
234
|
-
//
|
|
235
|
-
//
|
|
236
|
-
|
|
237
|
-
|
|
423
|
+
// Joins the in-flight upgrade. Rejection propagates so the caller's step
|
|
424
|
+
// can abort cleanly (handleStartupError exits — auto-upgrade graceful
|
|
425
|
+
// degrade is OOS per APPS-4457).
|
|
426
|
+
const joinPackageUpgrade = async (reason) => {
|
|
427
|
+
if (!packageUpgradePromise) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
logger.info(`Waiting for background package upgrade (${reason})…`);
|
|
431
|
+
const promise = packageUpgradePromise;
|
|
432
|
+
packageUpgradePromise = undefined;
|
|
433
|
+
await promise;
|
|
434
|
+
};
|
|
435
|
+
// Joins the in-flight install. Rejection propagates so the caller's step
|
|
436
|
+
// can abort cleanly (handleStartupError degrades for InitialInstallFailed,
|
|
437
|
+
// exits otherwise).
|
|
238
438
|
const joinPackageInstall = async (reason) => {
|
|
239
439
|
if (!packageInstallPromise) {
|
|
240
440
|
return;
|
|
@@ -244,10 +444,34 @@ export async function dev(options) {
|
|
|
244
444
|
packageInstallPromise = undefined;
|
|
245
445
|
await promise;
|
|
246
446
|
};
|
|
447
|
+
// Settles the in-flight install WITHOUT throwing. Used on the CLI-restart
|
|
448
|
+
// path so an install failure doesn't preempt the restart (the new CLI's
|
|
449
|
+
// first boot re-runs install). Still waits for npm to finish so we don't
|
|
450
|
+
// SIGKILL it mid-rename.
|
|
451
|
+
const settlePackageInstall = async (reason) => {
|
|
452
|
+
if (!packageInstallPromise) {
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
logger.info(`Settling background package install (${reason})…`);
|
|
456
|
+
const promise = packageInstallPromise;
|
|
457
|
+
packageInstallPromise = undefined;
|
|
458
|
+
await Promise.allSettled([promise]);
|
|
459
|
+
};
|
|
460
|
+
// Joins upgrade THEN install. Upgrade failures surface first so
|
|
461
|
+
// handleStartupError exits (per APPS-4457) before an install rejection
|
|
462
|
+
// gets a chance to route to degrade.
|
|
463
|
+
const joinUpgradeThenInstall = async (reason) => {
|
|
464
|
+
await joinPackageUpgrade(reason);
|
|
465
|
+
await joinPackageInstall(reason);
|
|
466
|
+
};
|
|
247
467
|
const skipAutoUpgrade = autoUpgradeMode === DevServerAutoUpgradeMode.SKIP;
|
|
248
468
|
const skipCliUpgrade = skipAutoUpgrade ||
|
|
249
469
|
autoUpgradeMode === DevServerAutoUpgradeMode.SKIP_CLI_ONLY;
|
|
250
470
|
await tracer.startActiveSpan("devServerStartup", async (startupSpan) => {
|
|
471
|
+
// Mutable startup status surfaced to the browser via createDevServer's
|
|
472
|
+
// /_sb_connect + /_sb_status. The startup catch records an app-install
|
|
473
|
+
// failure here (degrade path) so Vite still starts and serves the error.
|
|
474
|
+
const devServerStatus = { serverErrors: [] };
|
|
251
475
|
try {
|
|
252
476
|
// Add check for node_modules
|
|
253
477
|
if (!fs.existsSync(path.join(cwd, "node_modules"))) {
|
|
@@ -536,15 +760,39 @@ export async function dev(options) {
|
|
|
536
760
|
else if (downloadFirst && isSynced) {
|
|
537
761
|
logger.info("[dev-startup] Skipping download, already in sync");
|
|
538
762
|
}
|
|
539
|
-
// Unconditional lockfile sanitation: strip
|
|
540
|
-
// `resolved` URLs from any lockfile on disk
|
|
541
|
-
// install runs next. The lockfile here is
|
|
542
|
-
// DBFS path (downloadFirst overwrite,
|
|
543
|
-
// import); npm honors `resolved` verbatim
|
|
544
|
-
// active registry. `integrity` is
|
|
545
|
-
// cross-registry tarball drift surfaces as
|
|
546
|
-
// there's no lockfile or no `resolved`
|
|
763
|
+
// Unconditional lockfile sanitation (APPS-4300): strip
|
|
764
|
+
// cross-registry `resolved` URLs from any lockfile on disk
|
|
765
|
+
// regardless of whether install runs next. The lockfile here is
|
|
766
|
+
// whatever survived the DBFS path (downloadFirst overwrite,
|
|
767
|
+
// prior boot, brownfield import); npm honors `resolved` verbatim
|
|
768
|
+
// and would bypass the active registry. `integrity` is
|
|
769
|
+
// preserved, so genuine cross-registry tarball drift surfaces as
|
|
770
|
+
// EINTEGRITY. No-op when there's no lockfile or no `resolved`
|
|
771
|
+
// entries.
|
|
547
772
|
await stripResolvedFromLockfile(cwd);
|
|
773
|
+
// Materialise `~/.superblocks/npmrc` from the server-fetched
|
|
774
|
+
// per-org npm registry config BEFORE the global Superblocks CLI
|
|
775
|
+
// auto-upgrade fires. With the file in place, the auto-upgrade's
|
|
776
|
+
// `npm install -g @superblocksteam/cli@…` resolves through the
|
|
777
|
+
// customer's private registry instead of `registry.npmjs.org`.
|
|
778
|
+
await tracer.startActiveSpan("syncHomeNpmrc", async (span) => {
|
|
779
|
+
try {
|
|
780
|
+
if (!aiService) {
|
|
781
|
+
logger.info("[home-npmrc] skipped: AiService unavailable at startup");
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
await syncHomeNpmrc({
|
|
785
|
+
npmRegistryClient: aiService.getNpmRegistryClient(),
|
|
786
|
+
logger,
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
catch (error) {
|
|
790
|
+
logger.warn("[home-npmrc] sync step failed unexpectedly; ~/.superblocks/npmrc left untouched", getErrorMeta(error));
|
|
791
|
+
}
|
|
792
|
+
finally {
|
|
793
|
+
span.end();
|
|
794
|
+
}
|
|
795
|
+
});
|
|
548
796
|
let hasCliUpdated = false;
|
|
549
797
|
let upgradePromises = [];
|
|
550
798
|
const forceUpgrade = options.autoUpgradeMode === DevServerAutoUpgradeMode.FORCE;
|
|
@@ -565,7 +813,10 @@ export async function dev(options) {
|
|
|
565
813
|
organizationId: currentUser.user.currentOrganizationId,
|
|
566
814
|
featureFlags: sdk.getFeatureFlagsForUser(currentUser),
|
|
567
815
|
};
|
|
568
|
-
const result = await checkVersionsAndWritePackageJson(lockService, applicationConfigWithTokenConfigAndUserInfo, forceUpgrade, skipCliUpgrade
|
|
816
|
+
const result = await checkVersionsAndWritePackageJson(lockService, applicationConfigWithTokenConfigAndUserInfo, forceUpgrade, skipCliUpgrade,
|
|
817
|
+
// Route the CLI-upgrade install's npm debug log into the
|
|
818
|
+
// same `<app>/.superblocks/logs` as the startup install.
|
|
819
|
+
cwd);
|
|
569
820
|
hasCliUpdated = result.cliUpdated;
|
|
570
821
|
upgradePromises = result.upgradePromises;
|
|
571
822
|
}
|
|
@@ -622,6 +873,33 @@ export async function dev(options) {
|
|
|
622
873
|
packageJsonInstallBaselineSnapshot: packageJsonSnapshotDiagnostic(packageJsonInstallBaselineSnapshot),
|
|
623
874
|
packageJsonSnapshotAfter: packageJsonSnapshotDiagnostic(packageJsonSnapshotAfter),
|
|
624
875
|
});
|
|
876
|
+
const installApplicationId = applicationConfig.id;
|
|
877
|
+
// Launch upgrades and app install as INDEPENDENT promises (not a
|
|
878
|
+
// single `Promise.all`) so each outcome is observed on its own
|
|
879
|
+
// join: upgrade failure exits (APPS-4457), install failure may
|
|
880
|
+
// degrade (InitialInstallFailed). A bundled `Promise.all` would
|
|
881
|
+
// settle on the first rejection and silently absorb the other's
|
|
882
|
+
// result. The `.catch` backstops convert "no join fired" cases
|
|
883
|
+
// into logged-then-handled rejections instead of unhandled ones.
|
|
884
|
+
if (upgradePromises.length > 0) {
|
|
885
|
+
logger.info("Starting package upgrade in background…");
|
|
886
|
+
const launchedUpgradeCount = upgradePromises.length;
|
|
887
|
+
packageUpgradePromise = tracer.startActiveSpan("packageUpgrades", async (span) => {
|
|
888
|
+
try {
|
|
889
|
+
await Promise.all(upgradePromises);
|
|
890
|
+
}
|
|
891
|
+
finally {
|
|
892
|
+
span.end();
|
|
893
|
+
}
|
|
894
|
+
});
|
|
895
|
+
packageUpgradePromise.catch((err) => {
|
|
896
|
+
logger.error(`Background package upgrade failed [errorId=DEV_SERVER_BG_UPGRADE_FAILED applicationId=${installApplicationId} cwd=${cwd} upgradePromiseCount=${launchedUpgradeCount}]`, getErrorMeta(err));
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
// Run the verification install when EITHER the package.json
|
|
900
|
+
// requires it, or upgrades just modified package.json/lockfile
|
|
901
|
+
// (re-syncing node_modules), or the caller forced it. Mirrors
|
|
902
|
+
// the original launch condition.
|
|
625
903
|
if (packageJsonRequiresInstall ||
|
|
626
904
|
upgradePromises.length > 0 ||
|
|
627
905
|
forcePackageInstall) {
|
|
@@ -634,29 +912,19 @@ export async function dev(options) {
|
|
|
634
912
|
logger.info("Starting package install in background…");
|
|
635
913
|
packageInstallPromise = tracer.startActiveSpan("installPackages", async (span) => {
|
|
636
914
|
try {
|
|
637
|
-
|
|
638
|
-
await Promise.all([
|
|
639
|
-
...upgradePromises,
|
|
640
|
-
installPackages(cwd, logger),
|
|
641
|
-
]);
|
|
915
|
+
await installPackages(cwd, logger, aiService?.getNpmRegistryClient());
|
|
642
916
|
}
|
|
643
917
|
finally {
|
|
644
918
|
span.end();
|
|
645
919
|
}
|
|
646
920
|
});
|
|
647
|
-
// Backstop: assigning `.catch` to a separate (discarded) promise
|
|
648
|
-
// keeps `packageInstallPromise` itself rejecting, so the joins
|
|
649
|
-
// can still observe and abort. Without this, an install that
|
|
650
|
-
// fails before any join fires becomes an unhandled rejection.
|
|
651
|
-
const installApplicationId = applicationConfig.id;
|
|
652
|
-
const installUpgradeCount = upgradePromises.length;
|
|
653
921
|
packageInstallPromise.catch((err) => {
|
|
654
922
|
logger.error(
|
|
655
923
|
// errorId is encoded into the message body because the
|
|
656
924
|
// logger.error contract limits structured attributes to
|
|
657
925
|
// `{ error: { kind, message, stack } }`. The id stays
|
|
658
926
|
// grep-able for Datadog/Sentry alert rules.
|
|
659
|
-
`Background package install failed [errorId=DEV_SERVER_BG_INSTALL_FAILED applicationId=${installApplicationId} cwd=${cwd}
|
|
927
|
+
`Background package install failed [errorId=DEV_SERVER_BG_INSTALL_FAILED applicationId=${installApplicationId} cwd=${cwd}]`, getErrorMeta(err));
|
|
660
928
|
});
|
|
661
929
|
}
|
|
662
930
|
else {
|
|
@@ -665,19 +933,49 @@ export async function dev(options) {
|
|
|
665
933
|
const shouldUploadPackageState = hasPackageChanged || forcePackageInstall;
|
|
666
934
|
if (shouldUploadPackageState || uploadFirst) {
|
|
667
935
|
// Upload serializes the post-install lockfile + node_modules
|
|
668
|
-
// tree to DBFS, so it must observe
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
936
|
+
// tree to DBFS, so it must observe quiesced upgrade+install.
|
|
937
|
+
// Upgrade first — its rejection exits before an install
|
|
938
|
+
// rejection can take the degrade path (APPS-4457).
|
|
939
|
+
//
|
|
940
|
+
// BUT: if a successful CLI upgrade is pending restart
|
|
941
|
+
// (`hasCliUpdated`), the restart branch below MUST win over
|
|
942
|
+
// the install-failure degrade path. Otherwise the outer
|
|
943
|
+
// sync/setup catch routes `InitialInstallFailed` to "degrade"
|
|
944
|
+
// and skips the restart entirely — masking a successful CLI
|
|
945
|
+
// upgrade. Catch ONLY the join (not the upload body) so a
|
|
946
|
+
// post-join upload failure still propagates normally.
|
|
947
|
+
let skipStartupUploadForCliRestart = false;
|
|
948
|
+
try {
|
|
949
|
+
await joinUpgradeThenInstall("before upload");
|
|
950
|
+
}
|
|
951
|
+
catch (joinError) {
|
|
952
|
+
if (hasCliUpdated &&
|
|
953
|
+
joinError instanceof InitialInstallFailed) {
|
|
954
|
+
logger.info("Initial package install failed before startup upload, but CLI was updated; skipping upload and letting the restart proceed");
|
|
955
|
+
skipStartupUploadForCliRestart = true;
|
|
956
|
+
}
|
|
957
|
+
else {
|
|
958
|
+
throw joinError;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
if (!skipStartupUploadForCliRestart) {
|
|
962
|
+
logger.info(`Uploading local files to branch '${activeDbfsBranchName}' on server before starting`);
|
|
963
|
+
await tracer.startActiveSpan("uploadFirstOrPackageChanged", async (span) => {
|
|
964
|
+
await syncService.uploadDirectory("cli:sdk");
|
|
965
|
+
await syncService.uploadDirectoryNowIfNeeded("cli:sdk");
|
|
966
|
+
span.end();
|
|
967
|
+
});
|
|
968
|
+
}
|
|
676
969
|
}
|
|
677
970
|
if (hasCliUpdated) {
|
|
678
|
-
//
|
|
679
|
-
// the next boot
|
|
680
|
-
|
|
971
|
+
// Restart must NOT be preempted by an app-install failure:
|
|
972
|
+
// the next boot re-runs install with the new CLI. Join the
|
|
973
|
+
// upgrade (it must succeed or we exit before restarting) and
|
|
974
|
+
// SETTLE the install (don't observe its rejection — but wait
|
|
975
|
+
// for npm to finish so the restart doesn't SIGKILL it
|
|
976
|
+
// mid-rename and leave a half-written lockfile).
|
|
977
|
+
await joinPackageUpgrade("before CLI restart");
|
|
978
|
+
await settlePackageInstall("before CLI restart");
|
|
681
979
|
try {
|
|
682
980
|
logger.info("Releasing lock before restarting the dev server");
|
|
683
981
|
await aiService?.removeIntegrationCache();
|
|
@@ -692,14 +990,20 @@ export async function dev(options) {
|
|
|
692
990
|
});
|
|
693
991
|
}
|
|
694
992
|
catch (error) {
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
await lockService?.shutdownAndExit();
|
|
993
|
+
if (handleStartupError(error, devServerStatus, logger) === "degrade") {
|
|
994
|
+
// app-install failure: do NOT exit — fall through to Vite startup below.
|
|
995
|
+
// upload + CLI-restart were already skipped (the rejecting join threw first).
|
|
699
996
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
997
|
+
else {
|
|
998
|
+
logger.error("[dev-server] Startup failed during sync/lock/setup (exiting with code 1)", getErrorMeta(error));
|
|
999
|
+
try {
|
|
1000
|
+
await aiService?.removeIntegrationCache();
|
|
1001
|
+
await lockService?.shutdownAndExit();
|
|
1002
|
+
}
|
|
1003
|
+
finally {
|
|
1004
|
+
// this is redundant, but it's here to make sure the lock service is shutdown and the process exits
|
|
1005
|
+
process.exit(1);
|
|
1006
|
+
}
|
|
703
1007
|
}
|
|
704
1008
|
}
|
|
705
1009
|
}
|
|
@@ -713,7 +1017,36 @@ export async function dev(options) {
|
|
|
713
1017
|
// cache embeds partial state that survives across reloads. Awaiting
|
|
714
1018
|
// here costs at most the install's remaining wall time — the upload
|
|
715
1019
|
// and CLI-restart joins above usually drain it first.
|
|
716
|
-
|
|
1020
|
+
// The "before upload" / "before CLI restart" joins above run inside the
|
|
1021
|
+
// sync/lock catch that degrades on `InitialInstallFailed`. But when the
|
|
1022
|
+
// install ran yet none of those joins fired (e.g.
|
|
1023
|
+
// `packageJsonRequiresInstall && !forcePackageInstall && !upload &&
|
|
1024
|
+
// !hasCliUpdated`), the install rejection first surfaces HERE — outside
|
|
1025
|
+
// that catch. Route it through the SAME origin gate so an app-install
|
|
1026
|
+
// failure still degrades (keep Vite up + record the error) instead of
|
|
1027
|
+
// escaping `dev()` to `process.exit(1)`. Non-install errors (including
|
|
1028
|
+
// any background upgrade rejection that escaped the prior joins) take
|
|
1029
|
+
// the same explicit exit path as the sync/lock catch above so the
|
|
1030
|
+
// process actually terminates (the startupSpan catch only rethrows,
|
|
1031
|
+
// and an unhandled rejection at that depth doesn't shut the lock
|
|
1032
|
+
// service down or guarantee `process.exit(1)`).
|
|
1033
|
+
try {
|
|
1034
|
+
await joinUpgradeThenInstall("before Vite startup");
|
|
1035
|
+
}
|
|
1036
|
+
catch (error) {
|
|
1037
|
+
if (handleStartupError(error, devServerStatus, logger) === "exit") {
|
|
1038
|
+
logger.error("[dev-server] Startup failed during pre-Vite install join (exiting with code 1)", getErrorMeta(error));
|
|
1039
|
+
try {
|
|
1040
|
+
await aiService?.removeIntegrationCache();
|
|
1041
|
+
await lockService?.shutdownAndExit();
|
|
1042
|
+
}
|
|
1043
|
+
finally {
|
|
1044
|
+
// Redundant with `shutdownAndExit`; here so a thrown shutdown
|
|
1045
|
+
// path can't leave the process hanging on a stuck handle.
|
|
1046
|
+
process.exit(1);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
717
1050
|
const activateRuntimeGitService = async () => {
|
|
718
1051
|
if (gitService) {
|
|
719
1052
|
const hasGit = await nodeFs.access(path.join(cwd, ".git")).then(() => true, () => false);
|
|
@@ -816,9 +1149,7 @@ export async function dev(options) {
|
|
|
816
1149
|
superblocksBaseUrl: tokenConfig.superblocksBaseUrl,
|
|
817
1150
|
existingServer: options.existingServer,
|
|
818
1151
|
warmActivationStart: options.warmActivationStart,
|
|
819
|
-
|
|
820
|
-
// CreateDevServerOptions directly so new required fields cause a
|
|
821
|
-
// compile error instead of silently passing undefined.
|
|
1152
|
+
devServerStatus,
|
|
822
1153
|
};
|
|
823
1154
|
const result = await createDevServer(createDevServerOptions);
|
|
824
1155
|
span.end();
|
|
@@ -833,13 +1164,16 @@ export async function dev(options) {
|
|
|
833
1164
|
logger.warn(`Error stopping auth hot-reload server: ${error}`);
|
|
834
1165
|
});
|
|
835
1166
|
}
|
|
836
|
-
// Drain any straggler install before tear-down. By this
|
|
837
|
-
// pre-Vite join has already run on the success path, so
|
|
838
|
-
//
|
|
1167
|
+
// Drain any straggler upgrade/install before tear-down. By this
|
|
1168
|
+
// point the pre-Vite join has already run on the success path, so
|
|
1169
|
+
// usually both promises are undefined and this is a no-op; if abort
|
|
839
1170
|
// races createDevServer (or fires during the inner sync block on
|
|
840
|
-
// some error path), draining here keeps the spawned npm
|
|
841
|
-
// being SIGKILL'd mid-rename. Errors only get logged — we are
|
|
842
|
-
// down anyway.
|
|
1171
|
+
// some error path), draining here keeps the spawned npm children
|
|
1172
|
+
// from being SIGKILL'd mid-rename. Errors only get logged — we are
|
|
1173
|
+
// tearing down anyway.
|
|
1174
|
+
joinPackageUpgrade("during abort").catch((error) => {
|
|
1175
|
+
logger.warn("Error draining background upgrade during abort", getErrorMeta(error));
|
|
1176
|
+
});
|
|
843
1177
|
joinPackageInstall("during abort").catch((error) => {
|
|
844
1178
|
logger.warn("Error draining background install during abort", getErrorMeta(error));
|
|
845
1179
|
});
|