@sentry/junior 0.116.1 → 0.117.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.
Files changed (67) hide show
  1. package/dist/{agent-hooks-GUAAS3XH.js → agent-hooks-5IK4LPLP.js} +8 -8
  2. package/dist/api/conversations/detail.d.ts +7 -0
  3. package/dist/api/conversations/event-list.d.ts +7 -0
  4. package/dist/api/conversations/events.d.ts +1 -1
  5. package/dist/api/schema/conversation.d.ts +28 -0
  6. package/dist/api/schema.js +1 -1
  7. package/dist/api.js +25 -12
  8. package/dist/app.js +32 -30
  9. package/dist/{catalog-runtime-WMPC4J4U.js → catalog-runtime-MTUYW3AA.js} +5 -5
  10. package/dist/chat/agent/prompt.d.ts +2 -0
  11. package/dist/chat/agent/request.d.ts +2 -1
  12. package/dist/chat/agent/resume.d.ts +2 -0
  13. package/dist/chat/agent/tools.d.ts +1 -0
  14. package/dist/chat/conversations/history.d.ts +22 -1
  15. package/dist/chat/conversations/projection.d.ts +6 -0
  16. package/dist/chat/mcp/client.d.ts +4 -0
  17. package/dist/chat/mcp/errors.d.ts +22 -1
  18. package/dist/chat/oauth-response.d.ts +21 -0
  19. package/dist/chat/plugins/prompt.d.ts +9 -0
  20. package/dist/chat/plugins/types.d.ts +2 -21
  21. package/dist/chat/sandbox/snapshot/install.d.ts +6 -0
  22. package/dist/chat/sandbox/snapshot/profile.d.ts +14 -0
  23. package/dist/chat/sandbox/snapshot/resolve.d.ts +22 -0
  24. package/dist/chat/sandbox/snapshot/span.d.ts +2 -0
  25. package/dist/chat/services/context-compaction-marker.d.ts +1 -0
  26. package/dist/chat/services/turn-session-record.d.ts +2 -0
  27. package/dist/chat/state/turn-session.d.ts +2 -0
  28. package/dist/chat/tools/query-conversation-events.d.ts +1 -1
  29. package/dist/chat/tools/sandbox/view-image.d.ts +11 -0
  30. package/dist/chat/tools/types.d.ts +7 -0
  31. package/dist/{chunk-RYFAVWP3.js → chunk-27KWTFP6.js} +33 -12
  32. package/dist/{chunk-N3C4XCRE.js → chunk-3GE3F4LT.js} +1 -1
  33. package/dist/{chunk-XA3VLFPK.js → chunk-5O3JDQDG.js} +154 -20
  34. package/dist/{chunk-CGXLCMWI.js → chunk-6G45D3YG.js} +114 -10
  35. package/dist/{chunk-F3SWGSPG.js → chunk-72Z3HNTQ.js} +1 -1
  36. package/dist/{chunk-DLLW5ITL.js → chunk-AKG4TZNH.js} +2 -2
  37. package/dist/{chunk-TC4ZEM2D.js → chunk-BFIRIZPH.js} +9 -0
  38. package/dist/{chunk-OY2PQQ4P.js → chunk-CU7HTGNI.js} +1 -1
  39. package/dist/{chunk-CY7NXM32.js → chunk-DS27ZBSP.js} +4 -2
  40. package/dist/{chunk-VBMMMVVQ.js → chunk-FAWBONPC.js} +404 -443
  41. package/dist/{chunk-7CSVG54J.js → chunk-KU6ALEJC.js} +1 -1
  42. package/dist/{chunk-H35BDAUS.js → chunk-LN765Y4N.js} +6 -6
  43. package/dist/{chunk-KNCELO2M.js → chunk-LUMEBOWC.js} +1 -1
  44. package/dist/{chunk-GDAXWEIJ.js → chunk-NHZEUHKH.js} +514 -344
  45. package/dist/{chunk-ZYHLNTC2.js → chunk-STUI6NIL.js} +78 -16
  46. package/dist/{chunk-2YYPXQNQ.js → chunk-W5JEQQEA.js} +1 -1
  47. package/dist/{chunk-JPKGW6L6.js → chunk-WEK3V5RI.js} +73 -32
  48. package/dist/{chunk-WLT72MRP.js → chunk-WUNPRM76.js} +1 -1
  49. package/dist/{chunk-FAEALXEH.js → chunk-YRWJ3KEU.js} +2 -2
  50. package/dist/{chunk-BFYVIOTS.js → chunk-ZRQUQJUV.js} +12 -2
  51. package/dist/cli/chat.js +21 -21
  52. package/dist/cli/check.js +6 -6
  53. package/dist/cli/main.js +1 -1
  54. package/dist/cli/plugins.js +11 -11
  55. package/dist/cli/{snapshot-warmup.js → snapshot-create.js} +17 -19
  56. package/dist/cli/upgrade.js +5 -5
  57. package/dist/{db-FH4R5ZQ4.js → db-J2ITKS63.js} +5 -5
  58. package/dist/handlers/oauth-html.d.ts +4 -2
  59. package/dist/instrumentation.js +2 -2
  60. package/dist/nitro.js +2 -2
  61. package/dist/{oauth-callback-server-C2SSJ3FQ.js → oauth-callback-server-NQYKBT24.js} +15 -15
  62. package/dist/{runner-NAALFIXM.js → runner-53V5GIQM.js} +10 -10
  63. package/dist/{skills-UN2JXYYF.js → skills-BU3ZMLVB.js} +6 -6
  64. package/dist/{validation-EEF4ROHR.js → validation-74EIKKE6.js} +6 -6
  65. package/dist/version.js +1 -1
  66. package/package.json +5 -5
  67. package/dist/chat/sandbox/runtime-dependency-snapshots.d.ts +0 -20
@@ -1,31 +1,138 @@
1
1
  import {
2
2
  SANDBOX_WORKSPACE_ROOT,
3
3
  getStateAdapter
4
- } from "./chunk-7CSVG54J.js";
4
+ } from "./chunk-KU6ALEJC.js";
5
5
  import {
6
6
  sleep
7
7
  } from "./chunk-4ZNGQH7C.js";
8
8
  import {
9
9
  toOptionalTrimmed
10
- } from "./chunk-CY7NXM32.js";
10
+ } from "./chunk-DS27ZBSP.js";
11
11
  import {
12
12
  pluginCatalogRuntime
13
- } from "./chunk-OY2PQQ4P.js";
13
+ } from "./chunk-CU7HTGNI.js";
14
14
  import {
15
15
  setSpanAttributes,
16
16
  setSpanStatus,
17
17
  withSpan
18
- } from "./chunk-F3SWGSPG.js";
18
+ } from "./chunk-72Z3HNTQ.js";
19
+
20
+ // src/chat/sandbox/snapshot/profile.ts
21
+ import { createHash } from "crypto";
19
22
 
20
23
  // src/chat/sandbox/runtime-dependencies.ts
21
24
  var GLOBAL_RUNTIME_DEPENDENCIES = [
22
25
  { type: "system", package: "docker" },
26
+ { type: "system", package: "spal-release" },
23
27
  { type: "system", package: "ripgrep" }
24
28
  ];
25
29
 
26
- // src/chat/sandbox/runtime-dependency-snapshots.ts
27
- import { createHash } from "crypto";
30
+ // src/chat/sandbox/snapshot/profile.ts
31
+ var VERSION = 1;
32
+ var DEFAULT_FLOATING_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
33
+ function isExactNpmVersion(version) {
34
+ return /^\d+\.\d+\.\d+(?:[-+][a-z0-9.]+)?$/i.test(version.trim());
35
+ }
36
+ function isFloating(dep) {
37
+ return dep.type === "npm" && !isExactNpmVersion(dep.version);
38
+ }
39
+ function mergeDependencies(dependencies2) {
40
+ const seen = /* @__PURE__ */ new Set();
41
+ const npmVersions = /* @__PURE__ */ new Map();
42
+ const merged = [];
43
+ for (const dependency of dependencies2) {
44
+ const key = dependency.type === "npm" ? `${dependency.type}:${dependency.package}:${dependency.version}` : "package" in dependency ? `${dependency.type}:package:${dependency.package}` : `${dependency.type}:url:${dependency.url}:${dependency.sha256}`;
45
+ if (seen.has(key)) {
46
+ continue;
47
+ }
48
+ seen.add(key);
49
+ if (dependency.type === "npm") {
50
+ const version = npmVersions.get(dependency.package);
51
+ if (version !== void 0 && version !== dependency.version) {
52
+ throw new Error(
53
+ `Conflicting runtime dependency versions for ${dependency.package}: ${version} and ${dependency.version}`
54
+ );
55
+ }
56
+ npmVersions.set(dependency.package, dependency.version);
57
+ }
58
+ merged.push(dependency);
59
+ }
60
+ return merged;
61
+ }
62
+ function floatingMaxAgeMs() {
63
+ const raw = process.env.SANDBOX_SNAPSHOT_FLOATING_MAX_AGE_MS;
64
+ if (!raw?.trim()) {
65
+ return DEFAULT_FLOATING_MAX_AGE_MS;
66
+ }
67
+ const parsed = Number.parseInt(raw, 10);
68
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_FLOATING_MAX_AGE_MS;
69
+ }
70
+ function create(runtime) {
71
+ const dependencies2 = mergeDependencies([
72
+ ...GLOBAL_RUNTIME_DEPENDENCIES,
73
+ ...pluginCatalogRuntime.getRuntimeDependencies()
74
+ ]);
75
+ const postinstall2 = pluginCatalogRuntime.getRuntimePostinstall();
76
+ if (dependencies2.length === 0 && postinstall2.length === 0) {
77
+ return null;
78
+ }
79
+ const rebuildEpoch = process.env.SANDBOX_SNAPSHOT_REBUILD_EPOCH?.trim() ?? "";
80
+ const floating = dependencies2.some((dependency) => isFloating(dependency)) || postinstall2.length > 0;
81
+ const hash2 = createHash("sha256").update(
82
+ JSON.stringify({
83
+ version: VERSION,
84
+ runtime,
85
+ rebuildEpoch,
86
+ dependencies: dependencies2,
87
+ postinstall: postinstall2
88
+ })
89
+ ).digest("hex");
90
+ return {
91
+ hash: hash2,
92
+ dependencyCount: dependencies2.length,
93
+ floating,
94
+ dependencies: dependencies2,
95
+ postinstall: postinstall2
96
+ };
97
+ }
98
+ function hash(runtime) {
99
+ return create(runtime)?.hash;
100
+ }
101
+ function isStale(profile, createdAtMs) {
102
+ if (!profile.floating) {
103
+ return false;
104
+ }
105
+ const maxAgeMs = floatingMaxAgeMs();
106
+ return maxAgeMs === 0 || Date.now() - createdAtMs > maxAgeMs;
107
+ }
108
+
109
+ // src/chat/sandbox/snapshot/resolve.ts
28
110
  import { Sandbox } from "@vercel/sandbox";
111
+ import { z } from "zod";
112
+
113
+ // src/chat/sandbox/credentials.ts
114
+ function getVercelSandboxCredentials() {
115
+ const token = toOptionalTrimmed(process.env.VERCEL_TOKEN);
116
+ const teamId = toOptionalTrimmed(process.env.VERCEL_TEAM_ID);
117
+ const projectId = toOptionalTrimmed(process.env.VERCEL_PROJECT_ID);
118
+ if (token && teamId && projectId) {
119
+ return { token, teamId, projectId };
120
+ }
121
+ return void 0;
122
+ }
123
+
124
+ // src/chat/sandbox/resources.ts
125
+ function getSandboxResources() {
126
+ const value = process.env.SANDBOX_VCPUS?.trim();
127
+ if (!value || !/^\d+$/.test(value)) {
128
+ return void 0;
129
+ }
130
+ const vcpus = Number(value);
131
+ if (!Number.isSafeInteger(vcpus) || vcpus <= 0) {
132
+ return void 0;
133
+ }
134
+ return { vcpus };
135
+ }
29
136
 
30
137
  // src/chat/sandbox/noninteractive-command.ts
31
138
  var NON_INTERACTIVE_ENV = {
@@ -96,15 +203,215 @@ async function runNonInteractiveCommand(sandbox, input) {
96
203
  return await sandbox.runCommand(command);
97
204
  }
98
205
 
99
- // src/chat/sandbox/credentials.ts
100
- function getVercelSandboxCredentials() {
101
- const token = toOptionalTrimmed(process.env.VERCEL_TOKEN);
102
- const teamId = toOptionalTrimmed(process.env.VERCEL_TEAM_ID);
103
- const projectId = toOptionalTrimmed(process.env.VERCEL_PROJECT_ID);
104
- if (token && teamId && projectId) {
105
- return { token, teamId, projectId };
206
+ // src/chat/sandbox/snapshot/span.ts
207
+ async function trace(name, op, attributes, callback) {
208
+ return await withSpan(name, op, {}, callback, attributes);
209
+ }
210
+
211
+ // src/chat/sandbox/snapshot/install.ts
212
+ async function runOrThrow(sandbox, params, label) {
213
+ const result = await runNonInteractiveCommand(sandbox, params);
214
+ if (result.exitCode === 0) {
215
+ return;
106
216
  }
107
- return void 0;
217
+ const detail = result.stderr.trim() || result.stdout.trim() || "command failed";
218
+ throw new Error(`${label} failed: ${detail}`);
219
+ }
220
+ async function tryRun(sandbox, params) {
221
+ const result = await runNonInteractiveCommand(sandbox, params);
222
+ return result.exitCode === 0;
223
+ }
224
+ async function installGh(sandbox) {
225
+ const installed = await tryRun(sandbox, {
226
+ cmd: "dnf",
227
+ args: ["install", "-y", "gh"],
228
+ sudo: true
229
+ });
230
+ if (installed) {
231
+ return;
232
+ }
233
+ const repoAdded = await tryRun(sandbox, {
234
+ cmd: "dnf",
235
+ args: [
236
+ "config-manager",
237
+ "addrepo",
238
+ "--from-repofile=https://cli.github.com/packages/rpm/gh-cli.repo"
239
+ ],
240
+ sudo: true
241
+ });
242
+ if (!repoAdded) {
243
+ await runOrThrow(
244
+ sandbox,
245
+ {
246
+ cmd: "dnf",
247
+ args: ["install", "-y", "dnf-command(config-manager)"],
248
+ sudo: true
249
+ },
250
+ "dnf install dnf-command(config-manager)"
251
+ );
252
+ await runOrThrow(
253
+ sandbox,
254
+ {
255
+ cmd: "dnf",
256
+ args: [
257
+ "config-manager",
258
+ "--add-repo",
259
+ "https://cli.github.com/packages/rpm/gh-cli.repo"
260
+ ],
261
+ sudo: true
262
+ },
263
+ "dnf config-manager --add-repo gh-cli.repo"
264
+ );
265
+ }
266
+ await runOrThrow(
267
+ sandbox,
268
+ {
269
+ cmd: "dnf",
270
+ args: ["install", "-y", "gh", "--repo", "gh-cli"],
271
+ sudo: true
272
+ },
273
+ "dnf install gh --repo gh-cli"
274
+ );
275
+ }
276
+ function filePath(url, sha256) {
277
+ let basename = "package.rpm";
278
+ try {
279
+ const candidate = new URL(url).pathname.split("/").filter(Boolean).at(-1);
280
+ if (candidate) {
281
+ basename = candidate;
282
+ }
283
+ } catch {
284
+ }
285
+ const sanitized = basename.replace(/[^a-zA-Z0-9._-]/g, "_");
286
+ return `/tmp/junior-runtime-${sha256.slice(0, 12)}-${sanitized}`;
287
+ }
288
+ async function installUrl(sandbox, dependency) {
289
+ const rpmPath = filePath(dependency.url, dependency.sha256);
290
+ await runOrThrow(
291
+ sandbox,
292
+ {
293
+ cmd: "curl",
294
+ args: ["-fsSL", dependency.url, "-o", rpmPath]
295
+ },
296
+ `curl download ${dependency.url}`
297
+ );
298
+ const checksum = await runNonInteractiveCommand(sandbox, {
299
+ cmd: "sha256sum",
300
+ args: [rpmPath]
301
+ });
302
+ const expected = dependency.sha256;
303
+ const actual = checksum.stdout.trim().split(/\s+/)[0]?.toLowerCase();
304
+ if (checksum.exitCode !== 0) {
305
+ const detail = checksum.stderr.trim() || checksum.stdout.trim() || "command failed";
306
+ throw new Error(`sha256sum failed: ${detail}`);
307
+ }
308
+ if (!actual) {
309
+ throw new Error("sha256sum produced empty output");
310
+ }
311
+ if (actual !== expected) {
312
+ throw new Error(
313
+ `checksum mismatch for ${dependency.url}: expected ${expected}, got ${actual}`
314
+ );
315
+ }
316
+ await runOrThrow(
317
+ sandbox,
318
+ {
319
+ cmd: "dnf",
320
+ args: ["install", "-y", rpmPath],
321
+ sudo: true
322
+ },
323
+ `dnf install ${dependency.url}`
324
+ );
325
+ }
326
+ async function dependencies(sandbox, values) {
327
+ const system = values.filter(
328
+ (dependency) => dependency.type === "system"
329
+ );
330
+ const npm = values.filter(
331
+ (dependency) => dependency.type === "npm"
332
+ ).map((dependency) => `${dependency.package}@${dependency.version}`);
333
+ if (system.length > 0) {
334
+ await trace(
335
+ "sandbox.snapshot.install_system",
336
+ "sandbox.snapshot.install.system",
337
+ {
338
+ "app.sandbox.snapshot.install.system_count": system.length
339
+ },
340
+ async () => {
341
+ for (const dependency of system) {
342
+ if ("url" in dependency) {
343
+ await installUrl(sandbox, dependency);
344
+ } else if (dependency.package === "gh") {
345
+ await installGh(sandbox);
346
+ } else {
347
+ await runOrThrow(
348
+ sandbox,
349
+ {
350
+ cmd: "dnf",
351
+ args: ["install", "-y", dependency.package],
352
+ sudo: true
353
+ },
354
+ `dnf install ${dependency.package}`
355
+ );
356
+ }
357
+ }
358
+ }
359
+ );
360
+ }
361
+ if (npm.length > 0) {
362
+ await trace(
363
+ "sandbox.snapshot.install_npm",
364
+ "sandbox.snapshot.install.npm",
365
+ {
366
+ "app.sandbox.snapshot.install.npm_count": npm.length
367
+ },
368
+ async () => {
369
+ await runOrThrow(
370
+ sandbox,
371
+ {
372
+ cmd: "npm",
373
+ args: [
374
+ "install",
375
+ "--global",
376
+ "--prefix",
377
+ `${SANDBOX_WORKSPACE_ROOT}/.junior`,
378
+ ...npm
379
+ ]
380
+ },
381
+ "npm install"
382
+ );
383
+ }
384
+ );
385
+ }
386
+ }
387
+ async function postinstall(sandbox, commands) {
388
+ if (commands.length === 0) {
389
+ return;
390
+ }
391
+ await trace(
392
+ "sandbox.snapshot.runtime_postinstall",
393
+ "sandbox.snapshot.runtime_postinstall",
394
+ {
395
+ "app.sandbox.snapshot.runtime_postinstall.count": commands.length
396
+ },
397
+ async () => {
398
+ for (const command of commands) {
399
+ const result = await runNonInteractiveCommand(sandbox, {
400
+ cmd: command.cmd,
401
+ args: command.args,
402
+ login: true,
403
+ pathPrefix: `${SANDBOX_WORKSPACE_ROOT}/.junior/bin:$PATH`,
404
+ ...command.sudo !== void 0 ? { sudo: command.sudo } : {}
405
+ });
406
+ if (result.exitCode !== 0) {
407
+ const detail = result.stderr.trim() || result.stdout.trim() || "command failed";
408
+ throw new Error(
409
+ `runtime-postinstall ${command.cmd} failed: ${detail}`
410
+ );
411
+ }
412
+ }
413
+ }
414
+ );
108
415
  }
109
416
 
110
417
  // src/chat/sandbox/workspace.ts
@@ -350,21 +657,21 @@ function bindSandboxFileSystem(fs, signal) {
350
657
  return fs;
351
658
  }
352
659
  return {
353
- readFile(filePath, options) {
660
+ readFile(filePath2, options) {
354
661
  signal.throwIfAborted();
355
- return fs.readFile(filePath, { ...options, signal });
662
+ return fs.readFile(filePath2, { ...options, signal });
356
663
  },
357
- writeFile(filePath, content, options) {
664
+ writeFile(filePath2, content, options) {
358
665
  signal.throwIfAborted();
359
- return fs.writeFile(filePath, content, { ...options, signal });
666
+ return fs.writeFile(filePath2, content, { ...options, signal });
360
667
  },
361
- readdir(filePath) {
668
+ readdir(filePath2) {
362
669
  signal.throwIfAborted();
363
- return fs.readdir(filePath, { signal });
670
+ return fs.readdir(filePath2, { signal });
364
671
  },
365
- stat(filePath) {
672
+ stat(filePath2) {
366
673
  signal.throwIfAborted();
367
- return fs.stat(filePath, { signal });
674
+ return fs.stat(filePath2, { signal });
368
675
  }
369
676
  };
370
677
  }
@@ -382,12 +689,12 @@ function createSandboxSession(sandbox, options) {
382
689
  }
383
690
  };
384
691
  const fs = {
385
- readFile: async (filePath, readOptions) => await run(async () => await fileSystem.readFile(filePath, readOptions)),
386
- writeFile: async (filePath, content, writeOptions) => await run(
387
- async () => await fileSystem.writeFile(filePath, content, writeOptions)
692
+ readFile: async (filePath2, readOptions) => await run(async () => await fileSystem.readFile(filePath2, readOptions)),
693
+ writeFile: async (filePath2, content, writeOptions) => await run(
694
+ async () => await fileSystem.writeFile(filePath2, content, writeOptions)
388
695
  ),
389
- readdir: async (filePath, readOptions) => await run(async () => await fileSystem.readdir(filePath, readOptions)),
390
- stat: async (filePath, statOptions) => await run(async () => await fileSystem.stat(filePath, statOptions))
696
+ readdir: async (filePath2, readOptions) => await run(async () => await fileSystem.readdir(filePath2, readOptions)),
697
+ stat: async (filePath2, statOptions) => await run(async () => await fileSystem.stat(filePath2, statOptions))
391
698
  };
392
699
  return {
393
700
  sandboxId: sandbox.name,
@@ -432,109 +739,43 @@ function createSandboxSession(sandbox, options) {
432
739
  };
433
740
  }
434
741
 
435
- // src/chat/sandbox/resources.ts
436
- function getSandboxResources() {
437
- const value = process.env.SANDBOX_VCPUS?.trim();
438
- if (!value || !/^\d+$/.test(value)) {
439
- return void 0;
440
- }
441
- const vcpus = Number(value);
442
- if (!Number.isSafeInteger(vcpus) || vcpus <= 0) {
443
- return void 0;
444
- }
445
- return { vcpus };
446
- }
447
-
448
- // src/chat/sandbox/runtime-dependency-snapshots.ts
742
+ // src/chat/sandbox/snapshot/resolve.ts
449
743
  var SNAPSHOT_CACHE_PREFIX = "junior:sandbox_snapshot_profile";
450
744
  var SNAPSHOT_LOCK_PREFIX = "junior:sandbox_snapshot_lock";
451
- var SNAPSHOT_PROFILE_VERSION = 2;
452
745
  var SNAPSHOT_CACHE_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
453
- var SNAPSHOT_BUILD_LOCK_TTL_MS = 10 * 60 * 1e3;
454
- var SNAPSHOT_WAIT_FOR_LOCK_MS = SNAPSHOT_BUILD_LOCK_TTL_MS + 30 * 1e3;
455
- var DEFAULT_FLOATING_DEP_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
456
- var RIPGREP_VERSION = "15.1.0";
457
- var RIPGREP_ARCHIVE_SHA256 = "1c9297be4a084eea7ecaedf93eb03d058d6faae29bbc57ecdaf5063921491599";
458
- var RIPGREP_ARCHIVE_URL = `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}/ripgrep-${RIPGREP_VERSION}-x86_64-unknown-linux-musl.tar.gz`;
746
+ var SNAPSHOT_BUILD_LOCK_BUFFER_MS = 30 * 1e3;
747
+ var SNAPSHOT_WAIT_FOR_LOCK_BUFFER_MS = 30 * 1e3;
748
+ var cachedSnapshotSchema = z.object({
749
+ profileHash: z.string(),
750
+ snapshotId: z.string(),
751
+ runtime: z.string(),
752
+ createdAtMs: z.number(),
753
+ dependencyCount: z.number()
754
+ }).strict();
459
755
  function profileCacheKey(profileHash) {
460
756
  return `${SNAPSHOT_CACHE_PREFIX}:${profileHash}`;
461
757
  }
462
758
  function profileLockKey(profileHash) {
463
759
  return `${SNAPSHOT_LOCK_PREFIX}:${profileHash}`;
464
760
  }
465
- function isExactNpmVersion(version) {
466
- return /^\d+\.\d+\.\d+(?:[-+][a-z0-9.]+)?$/i.test(version.trim());
467
- }
468
- function hasFloatingSelector(dep) {
469
- return dep.type === "npm" && !isExactNpmVersion(dep.version);
470
- }
471
- function parseFloatingDepMaxAgeMs() {
472
- const raw = process.env.SANDBOX_SNAPSHOT_FLOATING_MAX_AGE_MS;
473
- if (!raw?.trim()) {
474
- return DEFAULT_FLOATING_DEP_MAX_AGE_MS;
475
- }
476
- const parsed = Number.parseInt(raw, 10);
477
- if (!Number.isFinite(parsed) || parsed < 0) {
478
- return DEFAULT_FLOATING_DEP_MAX_AGE_MS;
479
- }
480
- return parsed;
481
- }
482
- function buildDependencyProfile(runtime) {
483
- const dependencies = [
484
- ...GLOBAL_RUNTIME_DEPENDENCIES,
485
- ...pluginCatalogRuntime.getRuntimeDependencies()
486
- ];
487
- const postinstall = pluginCatalogRuntime.getRuntimePostinstall();
488
- if (dependencies.length === 0 && postinstall.length === 0) {
761
+ async function getCachedSnapshot(profileHash) {
762
+ const state = getStateAdapter();
763
+ await state.connect();
764
+ const raw = await state.get(profileCacheKey(profileHash));
765
+ if (typeof raw !== "string") {
489
766
  return null;
490
767
  }
491
- const rebuildEpoch = process.env.SANDBOX_SNAPSHOT_REBUILD_EPOCH?.trim() ?? "";
492
- const hasFloatingVersions = dependencies.some((dep) => hasFloatingSelector(dep)) || postinstall.length > 0;
493
- const hashInput = JSON.stringify({
494
- version: SNAPSHOT_PROFILE_VERSION,
495
- runtime,
496
- rebuildEpoch,
497
- dependencies,
498
- postinstall
499
- });
500
- const profileHash = createHash("sha256").update(hashInput).digest("hex");
501
- return {
502
- profileHash,
503
- dependencyCount: dependencies.length,
504
- hasFloatingVersions,
505
- dependencies,
506
- postinstall
507
- };
508
- }
509
- function getRuntimeDependencyProfileHash(runtime) {
510
- return buildDependencyProfile(runtime)?.profileHash;
511
- }
512
- function shouldRebuildCachedSnapshot(profile, cached) {
513
- if (!profile.hasFloatingVersions) {
514
- return false;
515
- }
516
- const maxAgeMs = parseFloatingDepMaxAgeMs();
517
- if (maxAgeMs === 0) {
518
- return true;
519
- }
520
- return Date.now() - cached.createdAtMs > maxAgeMs;
521
- }
522
- async function getCachedSnapshot(profileHash) {
768
+ let value;
523
769
  try {
524
- const state = getStateAdapter();
525
- await state.connect();
526
- const raw = await state.get(profileCacheKey(profileHash));
527
- if (typeof raw !== "string") {
528
- return null;
529
- }
530
- const parsed = JSON.parse(raw);
531
- if (!parsed || typeof parsed !== "object" || typeof parsed.profileHash !== "string" || typeof parsed.snapshotId !== "string" || typeof parsed.runtime !== "string" || typeof parsed.createdAtMs !== "number" || typeof parsed.dependencyCount !== "number") {
532
- return null;
533
- }
534
- return parsed;
770
+ value = JSON.parse(raw);
535
771
  } catch {
536
- return null;
772
+ throw new Error(`Invalid cached sandbox snapshot for ${profileHash}`);
537
773
  }
774
+ const parsed = cachedSnapshotSchema.safeParse(value);
775
+ if (!parsed.success || parsed.data.profileHash !== profileHash) {
776
+ throw new Error(`Invalid cached sandbox snapshot for ${profileHash}`);
777
+ }
778
+ return parsed.data;
538
779
  }
539
780
  async function setCachedSnapshot(entry) {
540
781
  const state = getStateAdapter();
@@ -545,280 +786,13 @@ async function setCachedSnapshot(entry) {
545
786
  SNAPSHOT_CACHE_TTL_MS
546
787
  );
547
788
  }
548
- async function withSnapshotSpan(name, op, attributes, callback) {
549
- return await withSpan(name, op, {}, callback, attributes);
550
- }
551
- async function runOrThrow(sandbox, params, label) {
552
- const result = await runNonInteractiveCommand(sandbox, params);
553
- if (result.exitCode === 0) {
554
- return;
555
- }
556
- const stderr = result.stderr.trim();
557
- const stdout = result.stdout.trim();
558
- const detail = stderr || stdout || "command failed";
559
- throw new Error(`${label} failed: ${detail}`);
560
- }
561
- async function tryRun(sandbox, params) {
562
- const result = await runNonInteractiveCommand(sandbox, params);
563
- if (result.exitCode === 0) {
564
- return { ok: true };
565
- }
566
- const stderr = result.stderr.trim();
567
- const stdout = result.stdout.trim();
568
- return { ok: false, detail: stderr || stdout || "command failed" };
569
- }
570
- async function installGhCliViaDnf(sandbox) {
571
- const direct = await tryRun(sandbox, {
572
- cmd: "dnf",
573
- args: ["install", "-y", "gh"],
574
- sudo: true
575
- });
576
- if (direct.ok) {
577
- return;
578
- }
579
- const dnf5Repo = await tryRun(sandbox, {
580
- cmd: "dnf",
581
- args: [
582
- "config-manager",
583
- "addrepo",
584
- "--from-repofile=https://cli.github.com/packages/rpm/gh-cli.repo"
585
- ],
586
- sudo: true
587
- });
588
- if (!dnf5Repo.ok) {
589
- await runOrThrow(
590
- sandbox,
591
- {
592
- cmd: "dnf",
593
- args: ["install", "-y", "dnf-command(config-manager)"],
594
- sudo: true
595
- },
596
- "dnf install dnf-command(config-manager)"
597
- );
598
- await runOrThrow(
599
- sandbox,
600
- {
601
- cmd: "dnf",
602
- args: [
603
- "config-manager",
604
- "--add-repo",
605
- "https://cli.github.com/packages/rpm/gh-cli.repo"
606
- ],
607
- sudo: true
608
- },
609
- "dnf config-manager --add-repo gh-cli.repo"
610
- );
611
- }
612
- await runOrThrow(
613
- sandbox,
614
- {
615
- cmd: "dnf",
616
- args: ["install", "-y", "gh", "--repo", "gh-cli"],
617
- sudo: true
618
- },
619
- "dnf install gh --repo gh-cli"
620
- );
621
- }
622
- function runtimeDependencyFilePath(url, sha256) {
623
- let urlBasename = "package.rpm";
624
- try {
625
- const pathname = new URL(url).pathname;
626
- const segments = pathname.split("/").filter(Boolean);
627
- const candidate = segments[segments.length - 1];
628
- if (candidate) {
629
- urlBasename = candidate;
630
- }
631
- } catch {
632
- }
633
- const sanitizedBasename = urlBasename.replace(/[^a-zA-Z0-9._-]/g, "_");
634
- return `/tmp/junior-runtime-${sha256.slice(0, 12)}-${sanitizedBasename}`;
635
- }
636
- async function downloadVerifiedRuntimeDependency(sandbox, url, sha256) {
637
- const filePath = runtimeDependencyFilePath(url, sha256);
638
- await runOrThrow(
639
- sandbox,
640
- {
641
- cmd: "curl",
642
- args: ["-fsSL", url, "-o", filePath]
643
- },
644
- `curl download ${url}`
645
- );
646
- const checksumResult = await runNonInteractiveCommand(sandbox, {
647
- cmd: "sha256sum",
648
- args: [filePath]
649
- });
650
- const checksumStdout = checksumResult.stdout.trim();
651
- const checksumStderr = checksumResult.stderr.trim();
652
- if (checksumResult.exitCode !== 0) {
653
- throw new Error(
654
- `sha256sum failed: ${checksumStderr || checksumStdout || "command failed"}`
655
- );
656
- }
657
- const actualChecksum = checksumStdout.split(/\s+/)[0]?.toLowerCase();
658
- if (!actualChecksum) {
659
- throw new Error("sha256sum produced empty output");
660
- }
661
- if (actualChecksum !== sha256) {
662
- throw new Error(
663
- `checksum mismatch for ${url}: expected ${sha256}, got ${actualChecksum}`
664
- );
665
- }
666
- return filePath;
667
- }
668
- async function installRipgrep(sandbox) {
669
- const archivePath = await downloadVerifiedRuntimeDependency(
670
- sandbox,
671
- RIPGREP_ARCHIVE_URL,
672
- RIPGREP_ARCHIVE_SHA256
673
- );
674
- const extractedDir = `/tmp/ripgrep-${RIPGREP_VERSION}-x86_64-unknown-linux-musl`;
675
- await runOrThrow(
676
- sandbox,
677
- {
678
- cmd: "tar",
679
- args: ["-xzf", archivePath, "-C", "/tmp"]
680
- },
681
- "extract ripgrep"
682
- );
683
- await runOrThrow(
684
- sandbox,
685
- {
686
- cmd: "mkdir",
687
- args: ["-p", `${SANDBOX_WORKSPACE_ROOT}/.junior/bin`]
688
- },
689
- "create sandbox bin directory"
690
- );
691
- await runOrThrow(
692
- sandbox,
693
- {
694
- cmd: "install",
695
- args: [
696
- "-m",
697
- "0755",
698
- `${extractedDir}/rg`,
699
- `${SANDBOX_WORKSPACE_ROOT}/.junior/bin/rg`
700
- ]
701
- },
702
- "install ripgrep"
703
- );
704
- }
705
- async function installRuntimeDependencies(sandbox, deps) {
706
- const systemDeps = deps.filter(
707
- (dep) => dep.type === "system"
708
- );
709
- const npmPackages = deps.filter(
710
- (dep) => dep.type === "npm"
711
- ).map((dep) => `${dep.package}@${dep.version}`);
712
- if (systemDeps.length > 0) {
713
- await withSnapshotSpan(
714
- "sandbox.snapshot.install_system",
715
- "sandbox.snapshot.install.system",
716
- {
717
- "app.sandbox.snapshot.install.system_count": systemDeps.length
718
- },
719
- async () => {
720
- for (const dep of systemDeps) {
721
- if ("url" in dep) {
722
- const rpmPath = await downloadVerifiedRuntimeDependency(
723
- sandbox,
724
- dep.url,
725
- dep.sha256
726
- );
727
- await runOrThrow(
728
- sandbox,
729
- {
730
- cmd: "dnf",
731
- args: ["install", "-y", rpmPath],
732
- sudo: true
733
- },
734
- `dnf install ${dep.url}`
735
- );
736
- continue;
737
- }
738
- if (dep.package === "ripgrep") {
739
- await installRipgrep(sandbox);
740
- continue;
741
- }
742
- if (dep.package === "gh") {
743
- await installGhCliViaDnf(sandbox);
744
- continue;
745
- }
746
- await runOrThrow(
747
- sandbox,
748
- {
749
- cmd: "dnf",
750
- args: ["install", "-y", dep.package],
751
- sudo: true
752
- },
753
- `dnf install ${dep.package}`
754
- );
755
- }
756
- }
757
- );
758
- }
759
- if (npmPackages.length > 0) {
760
- await withSnapshotSpan(
761
- "sandbox.snapshot.install_npm",
762
- "sandbox.snapshot.install.npm",
763
- {
764
- "app.sandbox.snapshot.install.npm_count": npmPackages.length
765
- },
766
- async () => {
767
- await runOrThrow(
768
- sandbox,
769
- {
770
- cmd: "npm",
771
- args: [
772
- "install",
773
- "--global",
774
- "--prefix",
775
- `${SANDBOX_WORKSPACE_ROOT}/.junior`,
776
- ...npmPackages
777
- ]
778
- },
779
- "npm install"
780
- );
781
- }
782
- );
783
- }
784
- }
785
- async function runRuntimePostinstall(sandbox, commands) {
786
- if (commands.length === 0) {
787
- return;
788
- }
789
- await withSnapshotSpan(
790
- "sandbox.snapshot.runtime_postinstall",
791
- "sandbox.snapshot.runtime_postinstall",
792
- {
793
- "app.sandbox.snapshot.runtime_postinstall.count": commands.length
794
- },
795
- async () => {
796
- for (const command of commands) {
797
- const result = await runNonInteractiveCommand(sandbox, {
798
- cmd: command.cmd,
799
- args: command.args,
800
- login: true,
801
- pathPrefix: `${SANDBOX_WORKSPACE_ROOT}/.junior/bin:$PATH`,
802
- ...command.sudo !== void 0 ? { sudo: command.sudo } : {}
803
- });
804
- if (result.exitCode === 0) {
805
- continue;
806
- }
807
- const stderr = result.stderr.trim();
808
- const stdout = result.stdout.trim();
809
- const detail = stderr || stdout || "command failed";
810
- throw new Error(`runtime-postinstall ${command.cmd} failed: ${detail}`);
811
- }
812
- }
813
- );
814
- }
815
- async function createDependencySnapshot(profile, runtime, timeoutMs) {
816
- return await withSnapshotSpan(
789
+ async function build(value, runtime, timeoutMs) {
790
+ return await trace(
817
791
  "sandbox.snapshot.build",
818
792
  "sandbox.snapshot.build",
819
793
  {
820
794
  "app.sandbox.runtime": runtime,
821
- "app.sandbox.snapshot.dependency_count": profile.dependencyCount
795
+ "app.sandbox.snapshot.dependency_count": value.dependencyCount
822
796
  },
823
797
  async () => {
824
798
  const sandboxCredentials = getVercelSandboxCredentials();
@@ -832,13 +806,13 @@ async function createDependencySnapshot(profile, runtime, timeoutMs) {
832
806
  })
833
807
  );
834
808
  try {
835
- await installRuntimeDependencies(sandbox, profile.dependencies);
836
- await runRuntimePostinstall(sandbox, profile.postinstall);
837
- return await withSnapshotSpan(
809
+ await dependencies(sandbox, value.dependencies);
810
+ await postinstall(sandbox, value.postinstall);
811
+ return await trace(
838
812
  "sandbox.snapshot.capture",
839
813
  "sandbox.snapshot.capture",
840
814
  {
841
- "app.sandbox.snapshot.dependency_count": profile.dependencyCount
815
+ "app.sandbox.snapshot.dependency_count": value.dependencyCount
842
816
  },
843
817
  async () => {
844
818
  const snapshot = await sandbox.snapshot();
@@ -854,40 +828,35 @@ async function createDependencySnapshot(profile, runtime, timeoutMs) {
854
828
  }
855
829
  );
856
830
  }
857
- async function withBuildLock(profileHash, callback, canUseCachedSnapshot, hooks) {
831
+ async function withBuildLock(profileHash, timeoutMs, callback, canUseCachedSnapshot, onWaitingForLock) {
858
832
  const state = getStateAdapter();
859
833
  await state.connect();
860
834
  const lockKey = profileLockKey(profileHash);
861
- const tryAcquireLock = async () => await state.acquireLock(lockKey, SNAPSHOT_BUILD_LOCK_TTL_MS);
835
+ const lockTtlMs = timeoutMs + SNAPSHOT_BUILD_LOCK_BUFFER_MS;
836
+ const tryAcquireLock = async () => await state.acquireLock(lockKey, lockTtlMs);
862
837
  let lock = await tryAcquireLock();
863
838
  if (lock) {
864
839
  try {
865
- const result = await callback();
866
- return {
867
- snapshotId: result.snapshotId,
868
- source: result.source,
869
- waitedForLock: false
870
- };
840
+ return await callback();
871
841
  } finally {
872
842
  await state.releaseLock(lock);
873
843
  }
874
844
  }
875
- return await withSnapshotSpan(
845
+ return await trace(
876
846
  "sandbox.snapshot.lock_wait",
877
847
  "sandbox.snapshot.lock_wait",
878
848
  {
879
849
  "app.sandbox.snapshot.profile_hash": profileHash
880
850
  },
881
851
  async () => {
882
- await hooks?.onWaitingForLock?.();
883
- const waitUntil = Date.now() + SNAPSHOT_WAIT_FOR_LOCK_MS;
852
+ await onWaitingForLock?.();
853
+ const waitUntil = Date.now() + lockTtlMs + SNAPSHOT_WAIT_FOR_LOCK_BUFFER_MS;
884
854
  while (Date.now() < waitUntil) {
885
855
  const cached2 = await getCachedSnapshot(profileHash);
886
856
  if (cached2?.snapshotId && canUseCachedSnapshot(cached2)) {
887
857
  return {
888
858
  snapshotId: cached2.snapshotId,
889
- source: "wait_cache",
890
- waitedForLock: true
859
+ source: "cache_hit_after_lock_wait"
891
860
  };
892
861
  }
893
862
  lock = await tryAcquireLock();
@@ -896,8 +865,7 @@ async function withBuildLock(profileHash, callback, canUseCachedSnapshot, hooks)
896
865
  const result = await callback();
897
866
  return {
898
867
  snapshotId: result.snapshotId,
899
- source: result.source,
900
- waitedForLock: true
868
+ source: result.source === "built" ? "built" : "cache_hit_after_lock_wait"
901
869
  };
902
870
  } finally {
903
871
  await state.releaseLock(lock);
@@ -909,22 +877,18 @@ async function withBuildLock(profileHash, callback, canUseCachedSnapshot, hooks)
909
877
  if (cached?.snapshotId && canUseCachedSnapshot(cached)) {
910
878
  return {
911
879
  snapshotId: cached.snapshotId,
912
- source: "wait_cache",
913
- waitedForLock: true
880
+ source: "cache_hit_after_lock_wait"
914
881
  };
915
882
  }
916
883
  throw new Error("Timed out waiting for snapshot build lock");
917
884
  }
918
885
  );
919
886
  }
920
- function toResolveOutcome(forceRebuild, source, waitedForLock) {
887
+ function toResolveOutcome(forceRebuild, source) {
921
888
  if (source === "built") {
922
889
  return forceRebuild ? "forced_rebuild" : "rebuilt";
923
890
  }
924
- if (waitedForLock || source === "wait_cache") {
925
- return "cache_hit_after_lock_wait";
926
- }
927
- return "cache_hit";
891
+ return source;
928
892
  }
929
893
  function getRebuildReason(params) {
930
894
  if (params.forceRebuild) {
@@ -938,8 +902,8 @@ function getRebuildReason(params) {
938
902
  }
939
903
  return void 0;
940
904
  }
941
- async function resolveRuntimeDependencySnapshot(params) {
942
- return await withSnapshotSpan(
905
+ async function resolve(params) {
906
+ return await trace(
943
907
  "sandbox.snapshot.resolve",
944
908
  "sandbox.snapshot.resolve",
945
909
  {
@@ -948,25 +912,24 @@ async function resolveRuntimeDependencySnapshot(params) {
948
912
  },
949
913
  async () => {
950
914
  await params.onProgress?.("resolve_start");
951
- const resolveStartedAtMs = Date.now();
952
- const profile = buildDependencyProfile(params.runtime);
953
- if (!profile) {
915
+ const currentProfile = create(params.runtime);
916
+ if (!currentProfile) {
954
917
  return {
955
918
  dependencyCount: 0,
956
919
  cacheHit: false,
957
920
  resolveOutcome: "no_profile"
958
921
  };
959
922
  }
960
- const cached = await getCachedSnapshot(profile.profileHash);
923
+ const cached = await getCachedSnapshot(currentProfile.hash);
961
924
  const cachedNeedsRebuild = Boolean(
962
- cached?.snapshotId && shouldRebuildCachedSnapshot(profile, cached)
925
+ cached?.snapshotId && isStale(currentProfile, cached.createdAtMs)
963
926
  );
964
927
  if (!params.forceRebuild && cached?.snapshotId && !cachedNeedsRebuild) {
965
928
  await params.onProgress?.("cache_hit");
966
929
  return {
967
930
  snapshotId: cached.snapshotId,
968
- profileHash: profile.profileHash,
969
- dependencyCount: profile.dependencyCount,
931
+ profileHash: currentProfile.hash,
932
+ dependencyCount: currentProfile.dependencyCount,
970
933
  cacheHit: true,
971
934
  resolveOutcome: "cache_hit"
972
935
  };
@@ -982,60 +945,58 @@ async function resolveRuntimeDependencySnapshot(params) {
982
945
  if (params.staleSnapshotId) {
983
946
  return candidate.snapshotId !== params.staleSnapshotId;
984
947
  }
985
- return candidate.createdAtMs > resolveStartedAtMs;
948
+ return candidate.snapshotId !== cached?.snapshotId;
986
949
  }
987
- return !shouldRebuildCachedSnapshot(profile, candidate);
950
+ return !isStale(currentProfile, candidate.createdAtMs);
988
951
  };
989
952
  const lockResult = await withBuildLock(
990
- profile.profileHash,
953
+ currentProfile.hash,
954
+ params.timeoutMs,
991
955
  async () => {
992
- const latest = await getCachedSnapshot(profile.profileHash);
956
+ const latest = await getCachedSnapshot(currentProfile.hash);
993
957
  if (latest?.snapshotId && canUseCachedSnapshot(latest)) {
994
958
  await params.onProgress?.("cache_hit");
995
959
  return {
996
960
  snapshotId: latest.snapshotId,
997
- source: "callback_cache"
961
+ source: "cache_hit"
998
962
  };
999
963
  }
1000
964
  await params.onProgress?.("building_snapshot");
1001
- const nextSnapshotId = await createDependencySnapshot(
1002
- profile,
965
+ const nextSnapshotId = await build(
966
+ currentProfile,
1003
967
  params.runtime,
1004
968
  params.timeoutMs
1005
969
  );
1006
970
  await setCachedSnapshot({
1007
- profileHash: profile.profileHash,
971
+ profileHash: currentProfile.hash,
1008
972
  snapshotId: nextSnapshotId,
1009
973
  runtime: params.runtime,
1010
974
  createdAtMs: Date.now(),
1011
- dependencyCount: profile.dependencyCount
975
+ dependencyCount: currentProfile.dependencyCount
1012
976
  });
1013
977
  await params.onProgress?.("build_complete");
1014
978
  return { snapshotId: nextSnapshotId, source: "built" };
1015
979
  },
1016
980
  canUseCachedSnapshot,
1017
- {
1018
- onWaitingForLock: async () => {
1019
- await params.onProgress?.("waiting_for_lock");
1020
- }
981
+ async () => {
982
+ await params.onProgress?.("waiting_for_lock");
1021
983
  }
1022
984
  );
1023
985
  return {
1024
986
  snapshotId: lockResult.snapshotId,
1025
- profileHash: profile.profileHash,
1026
- dependencyCount: profile.dependencyCount,
987
+ profileHash: currentProfile.hash,
988
+ dependencyCount: currentProfile.dependencyCount,
1027
989
  cacheHit: lockResult.source !== "built",
1028
990
  resolveOutcome: toResolveOutcome(
1029
991
  Boolean(params.forceRebuild),
1030
- lockResult.source,
1031
- lockResult.waitedForLock
992
+ lockResult.source
1032
993
  ),
1033
994
  ...rebuildReason ? { rebuildReason } : {}
1034
995
  };
1035
996
  }
1036
997
  );
1037
998
  }
1038
- function isSnapshotMissingError(error) {
999
+ function isMissingError(error) {
1039
1000
  const searchable = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
1040
1001
  return searchable.includes("snapshot") && (searchable.includes("not found") || searchable.includes("unknown") || searchable.includes("404"));
1041
1002
  }
@@ -1052,10 +1013,10 @@ export {
1052
1013
  buildNonInteractiveShellScript,
1053
1014
  runNonInteractiveCommand,
1054
1015
  getSandboxResources,
1055
- GLOBAL_RUNTIME_DEPENDENCIES,
1016
+ create,
1017
+ hash,
1056
1018
  bindSandboxFileSystem,
1057
1019
  createSandboxSession,
1058
- getRuntimeDependencyProfileHash,
1059
- resolveRuntimeDependencySnapshot,
1060
- isSnapshotMissingError
1020
+ resolve,
1021
+ isMissingError
1061
1022
  };