@optique/core 1.0.0-dev.1681 → 1.0.0-dev.1684

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/dist/facade.cjs CHANGED
@@ -12,6 +12,19 @@ const require_parser = require('./parser.cjs');
12
12
  const require_constructs = require('./constructs.cjs');
13
13
 
14
14
  //#region src/facade.ts
15
+ const SuppressedErrorCtor = typeof SuppressedError === "function" ? SuppressedError : (() => {
16
+ class SuppressedErrorPolyfill extends Error {
17
+ error;
18
+ suppressed;
19
+ constructor(error, suppressed, message$1) {
20
+ super(message$1);
21
+ this.name = "SuppressedError";
22
+ this.error = error;
23
+ this.suppressed = suppressed;
24
+ }
25
+ }
26
+ return SuppressedErrorPolyfill;
27
+ })();
15
28
  function finalizeParsedForContext(context, parsed) {
16
29
  return context.finalizeParsed != null ? context.finalizeParsed(parsed) : parsed;
17
30
  }
@@ -1216,57 +1229,104 @@ function disposeContextsSync(contexts) {
1216
1229
  * );
1217
1230
  * ```
1218
1231
  */
1232
+ async function runWithBody(parser, programName, contexts, args, options) {
1233
+ require_validate.validateContextIds(contexts);
1234
+ if (needsEarlyExit(args, options)) {
1235
+ if (parser.$mode === "async") return runParser(parser, programName, args, options);
1236
+ return Promise.resolve(runParser(parser, programName, args, options));
1237
+ }
1238
+ const ctxOptions = options.contextOptions;
1239
+ const { annotations: phase1Annotations, annotationsList: phase1AnnotationsList, hasDynamic: needsTwoPhase } = await collectPhase1Annotations(contexts, ctxOptions);
1240
+ if (!needsTwoPhase) {
1241
+ const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
1242
+ if (parser.$mode === "async") return runParser(augmentedParser, programName, args, options);
1243
+ return Promise.resolve(runParser(augmentedParser, programName, args, options));
1244
+ }
1245
+ const augmentedParser1 = injectAnnotationsIntoParser(parser, phase1Annotations);
1246
+ let firstPassResult;
1247
+ let firstPassDeferred;
1248
+ let firstPassDeferredKeys;
1249
+ let firstPassFailed = false;
1250
+ try {
1251
+ if (parser.$mode === "async") firstPassResult = await require_parser.parseAsync(augmentedParser1, args);
1252
+ else firstPassResult = require_parser.parseSync(augmentedParser1, args);
1253
+ if (typeof firstPassResult === "object" && firstPassResult !== null && "success" in firstPassResult) {
1254
+ const result = firstPassResult;
1255
+ if (result.success) {
1256
+ firstPassResult = result.value;
1257
+ firstPassDeferred = result.deferred;
1258
+ firstPassDeferredKeys = result.deferredKeys;
1259
+ } else firstPassFailed = true;
1260
+ }
1261
+ } catch {
1262
+ firstPassFailed = true;
1263
+ }
1264
+ if (firstPassFailed) {
1265
+ const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
1266
+ if (parser.$mode === "async") return runParser(augmentedParser, programName, args, options);
1267
+ return Promise.resolve(runParser(augmentedParser, programName, args, options));
1268
+ }
1269
+ const { annotationsList: phase2AnnotationsList } = await collectAnnotations(contexts, firstPassResult, ctxOptions, firstPassDeferred, firstPassDeferredKeys);
1270
+ const finalAnnotations = mergeTwoPhaseAnnotations(phase1AnnotationsList, phase2AnnotationsList);
1271
+ const augmentedParser2 = injectAnnotationsIntoParser(parser, finalAnnotations);
1272
+ if (parser.$mode === "async") return runParser(augmentedParser2, programName, args, options);
1273
+ return Promise.resolve(runParser(augmentedParser2, programName, args, options));
1274
+ }
1219
1275
  async function runWith(parser, programName, contexts, options) {
1220
1276
  const args = options?.args ?? [];
1221
1277
  if (contexts.length === 0) {
1222
1278
  if (parser.$mode === "async") return runParser(parser, programName, args, options);
1223
1279
  return Promise.resolve(runParser(parser, programName, args, options));
1224
1280
  }
1281
+ let result;
1282
+ let primaryError;
1283
+ let hasPrimaryError = false;
1284
+ try {
1285
+ result = await runWithBody(parser, programName, contexts, args, options);
1286
+ } catch (error) {
1287
+ hasPrimaryError = true;
1288
+ primaryError = error;
1289
+ }
1225
1290
  try {
1226
- require_validate.validateContextIds(contexts);
1227
- if (needsEarlyExit(args, options)) {
1228
- if (parser.$mode === "async") return runParser(parser, programName, args, options);
1229
- return Promise.resolve(runParser(parser, programName, args, options));
1230
- }
1231
- const ctxOptions = options?.contextOptions;
1232
- const { annotations: phase1Annotations, annotationsList: phase1AnnotationsList, hasDynamic: needsTwoPhase } = await collectPhase1Annotations(contexts, ctxOptions);
1233
- if (!needsTwoPhase) {
1234
- const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
1235
- if (parser.$mode === "async") return runParser(augmentedParser, programName, args, options);
1236
- return Promise.resolve(runParser(augmentedParser, programName, args, options));
1237
- }
1238
- const augmentedParser1 = injectAnnotationsIntoParser(parser, phase1Annotations);
1239
- let firstPassResult;
1240
- let firstPassDeferred;
1241
- let firstPassDeferredKeys;
1242
- let firstPassFailed = false;
1243
- try {
1244
- if (parser.$mode === "async") firstPassResult = await require_parser.parseAsync(augmentedParser1, args);
1245
- else firstPassResult = require_parser.parseSync(augmentedParser1, args);
1246
- if (typeof firstPassResult === "object" && firstPassResult !== null && "success" in firstPassResult) {
1247
- const result = firstPassResult;
1248
- if (result.success) {
1249
- firstPassResult = result.value;
1250
- firstPassDeferred = result.deferred;
1251
- firstPassDeferredKeys = result.deferredKeys;
1252
- } else firstPassFailed = true;
1253
- }
1254
- } catch {
1255
- firstPassFailed = true;
1256
- }
1257
- if (firstPassFailed) {
1258
- const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
1259
- if (parser.$mode === "async") return runParser(augmentedParser, programName, args, options);
1260
- return Promise.resolve(runParser(augmentedParser, programName, args, options));
1261
- }
1262
- const { annotationsList: phase2AnnotationsList } = await collectAnnotations(contexts, firstPassResult, ctxOptions, firstPassDeferred, firstPassDeferredKeys);
1263
- const finalAnnotations = mergeTwoPhaseAnnotations(phase1AnnotationsList, phase2AnnotationsList);
1264
- const augmentedParser2 = injectAnnotationsIntoParser(parser, finalAnnotations);
1265
- if (parser.$mode === "async") return runParser(augmentedParser2, programName, args, options);
1266
- return Promise.resolve(runParser(augmentedParser2, programName, args, options));
1267
- } finally {
1268
1291
  await disposeContexts(contexts);
1292
+ } catch (disposeError) {
1293
+ if (hasPrimaryError) throw new SuppressedErrorCtor(disposeError, primaryError, "An error was suppressed during context disposal.");
1294
+ throw disposeError;
1295
+ }
1296
+ if (hasPrimaryError) throw primaryError;
1297
+ return result;
1298
+ }
1299
+ /**
1300
+ * Body of {@link runWithSync}, extracted so that the caller can handle
1301
+ * disposal outside a `finally` block (avoiding `no-unsafe-finally` lint).
1302
+ */
1303
+ function runWithSyncBody(parser, programName, contexts, args, options) {
1304
+ require_validate.validateContextIds(contexts);
1305
+ if (needsEarlyExit(args, options)) return runParser(parser, programName, args, options);
1306
+ const ctxOptions = options.contextOptions;
1307
+ const { annotations: phase1Annotations, annotationsList: phase1AnnotationsList, hasDynamic: needsTwoPhase } = collectPhase1AnnotationsSync(contexts, ctxOptions);
1308
+ if (!needsTwoPhase) {
1309
+ const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
1310
+ return runParser(augmentedParser, programName, args, options);
1311
+ }
1312
+ const augmentedParser1 = injectAnnotationsIntoParser(parser, phase1Annotations);
1313
+ let firstPassResult;
1314
+ let firstPassDeferred;
1315
+ let firstPassDeferredKeys;
1316
+ try {
1317
+ const result = require_parser.parseSync(augmentedParser1, args);
1318
+ if (result.success) {
1319
+ firstPassResult = result.value;
1320
+ firstPassDeferred = result.deferred;
1321
+ firstPassDeferredKeys = result.deferredKeys;
1322
+ } else return runParser(augmentedParser1, programName, args, options);
1323
+ } catch {
1324
+ return runParser(augmentedParser1, programName, args, options);
1269
1325
  }
1326
+ const { annotationsList: phase2AnnotationsList } = collectAnnotationsSync(contexts, firstPassResult, ctxOptions, firstPassDeferred, firstPassDeferredKeys);
1327
+ const finalAnnotations = mergeTwoPhaseAnnotations(phase1AnnotationsList, phase2AnnotationsList);
1328
+ const augmentedParser2 = injectAnnotationsIntoParser(parser, finalAnnotations);
1329
+ return runParser(augmentedParser2, programName, args, options);
1270
1330
  }
1271
1331
  /**
1272
1332
  * Runs a synchronous parser with multiple source contexts.
@@ -1294,36 +1354,23 @@ function runWithSync(parser, programName, contexts, options) {
1294
1354
  if (parser.$mode !== "sync") throw new TypeError("Cannot use an async parser with runWithSync(). Use runWith() or runWithAsync() instead.");
1295
1355
  const args = options?.args ?? [];
1296
1356
  if (contexts.length === 0) return runParser(parser, programName, args, options);
1357
+ let result;
1358
+ let primaryError;
1359
+ let hasPrimaryError = false;
1360
+ try {
1361
+ result = runWithSyncBody(parser, programName, contexts, args, options);
1362
+ } catch (error) {
1363
+ hasPrimaryError = true;
1364
+ primaryError = error;
1365
+ }
1297
1366
  try {
1298
- require_validate.validateContextIds(contexts);
1299
- if (needsEarlyExit(args, options)) return runParser(parser, programName, args, options);
1300
- const ctxOptions = options?.contextOptions;
1301
- const { annotations: phase1Annotations, annotationsList: phase1AnnotationsList, hasDynamic: needsTwoPhase } = collectPhase1AnnotationsSync(contexts, ctxOptions);
1302
- if (!needsTwoPhase) {
1303
- const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
1304
- return runParser(augmentedParser, programName, args, options);
1305
- }
1306
- const augmentedParser1 = injectAnnotationsIntoParser(parser, phase1Annotations);
1307
- let firstPassResult;
1308
- let firstPassDeferred;
1309
- let firstPassDeferredKeys;
1310
- try {
1311
- const result = require_parser.parseSync(augmentedParser1, args);
1312
- if (result.success) {
1313
- firstPassResult = result.value;
1314
- firstPassDeferred = result.deferred;
1315
- firstPassDeferredKeys = result.deferredKeys;
1316
- } else return runParser(augmentedParser1, programName, args, options);
1317
- } catch {
1318
- return runParser(augmentedParser1, programName, args, options);
1319
- }
1320
- const { annotationsList: phase2AnnotationsList } = collectAnnotationsSync(contexts, firstPassResult, ctxOptions, firstPassDeferred, firstPassDeferredKeys);
1321
- const finalAnnotations = mergeTwoPhaseAnnotations(phase1AnnotationsList, phase2AnnotationsList);
1322
- const augmentedParser2 = injectAnnotationsIntoParser(parser, finalAnnotations);
1323
- return runParser(augmentedParser2, programName, args, options);
1324
- } finally {
1325
1367
  disposeContextsSync(contexts);
1368
+ } catch (disposeError) {
1369
+ if (hasPrimaryError) throw new SuppressedErrorCtor(disposeError, primaryError, "An error was suppressed during context disposal.");
1370
+ throw disposeError;
1326
1371
  }
1372
+ if (hasPrimaryError) throw primaryError;
1373
+ return result;
1327
1374
  }
1328
1375
  /**
1329
1376
  * Runs any parser asynchronously with multiple source contexts.
package/dist/facade.d.cts CHANGED
@@ -402,56 +402,6 @@ type ContextOptionsParam<TContexts extends readonly SourceContext<unknown>[], TV
402
402
  } : {
403
403
  readonly contextOptions: ExtractRequiredOptions<TContexts, TValue>;
404
404
  };
405
- /**
406
- * Runs a parser with multiple source contexts.
407
- *
408
- * This function automatically handles static and dynamic contexts with proper
409
- * priority. Earlier contexts in the array override later ones.
410
- *
411
- * The function uses a smart two-phase approach:
412
- *
413
- * 1. *Phase 1*: Collect annotations from all contexts (static contexts return
414
- * their data, dynamic contexts may return empty).
415
- * 2. *First parse*: Parse with Phase 1 annotations.
416
- * 3. *Phase 2*: Call `getAnnotations(parsed)` on all contexts with the first
417
- * parse result.
418
- * 4. *Second parse*: Parse again with merged annotations from both phases.
419
- *
420
- * If all contexts are static (no dynamic contexts), the second parse is skipped
421
- * for optimization.
422
- *
423
- * @template TParser The parser type.
424
- * @template THelp Return type when help is shown.
425
- * @template TError Return type when an error occurs.
426
- * @param parser The parser to execute.
427
- * @param programName Name of the program for help/error output.
428
- * @param contexts Source contexts to use (priority: earlier overrides later).
429
- * @param options Run options including args, help, version, etc.
430
- * @returns Promise that resolves to the parsed result.
431
- * @throws {TypeError} If two or more contexts share the same
432
- * {@link SourceContext.id}.
433
- * @since 0.10.0
434
- *
435
- * @example
436
- * ```typescript
437
- * import { runWith } from "@optique/core/facade";
438
- * import type { SourceContext } from "@optique/core/context";
439
- *
440
- * const envContext: SourceContext = {
441
- * id: Symbol.for("@myapp/env"),
442
- * getAnnotations() {
443
- * return { [Symbol.for("@myapp/env")]: process.env };
444
- * }
445
- * };
446
- *
447
- * const result = await runWith(
448
- * parser,
449
- * "myapp",
450
- * [envContext],
451
- * { args: process.argv.slice(2) }
452
- * );
453
- * ```
454
- */
455
405
  declare function runWith<TParser extends Parser<Mode, unknown, unknown>, TContexts extends readonly SourceContext<unknown>[], THelp = void, TError = never>(parser: TParser, programName: string, contexts: TContexts, options: RunWithOptions<THelp, TError> & ContextOptionsParam<TContexts, InferValue<TParser>>): Promise<InferValue<TParser>>;
456
406
  /**
457
407
  * Runs a synchronous parser with multiple source contexts.
package/dist/facade.d.ts CHANGED
@@ -402,56 +402,6 @@ type ContextOptionsParam<TContexts extends readonly SourceContext<unknown>[], TV
402
402
  } : {
403
403
  readonly contextOptions: ExtractRequiredOptions<TContexts, TValue>;
404
404
  };
405
- /**
406
- * Runs a parser with multiple source contexts.
407
- *
408
- * This function automatically handles static and dynamic contexts with proper
409
- * priority. Earlier contexts in the array override later ones.
410
- *
411
- * The function uses a smart two-phase approach:
412
- *
413
- * 1. *Phase 1*: Collect annotations from all contexts (static contexts return
414
- * their data, dynamic contexts may return empty).
415
- * 2. *First parse*: Parse with Phase 1 annotations.
416
- * 3. *Phase 2*: Call `getAnnotations(parsed)` on all contexts with the first
417
- * parse result.
418
- * 4. *Second parse*: Parse again with merged annotations from both phases.
419
- *
420
- * If all contexts are static (no dynamic contexts), the second parse is skipped
421
- * for optimization.
422
- *
423
- * @template TParser The parser type.
424
- * @template THelp Return type when help is shown.
425
- * @template TError Return type when an error occurs.
426
- * @param parser The parser to execute.
427
- * @param programName Name of the program for help/error output.
428
- * @param contexts Source contexts to use (priority: earlier overrides later).
429
- * @param options Run options including args, help, version, etc.
430
- * @returns Promise that resolves to the parsed result.
431
- * @throws {TypeError} If two or more contexts share the same
432
- * {@link SourceContext.id}.
433
- * @since 0.10.0
434
- *
435
- * @example
436
- * ```typescript
437
- * import { runWith } from "@optique/core/facade";
438
- * import type { SourceContext } from "@optique/core/context";
439
- *
440
- * const envContext: SourceContext = {
441
- * id: Symbol.for("@myapp/env"),
442
- * getAnnotations() {
443
- * return { [Symbol.for("@myapp/env")]: process.env };
444
- * }
445
- * };
446
- *
447
- * const result = await runWith(
448
- * parser,
449
- * "myapp",
450
- * [envContext],
451
- * { args: process.argv.slice(2) }
452
- * );
453
- * ```
454
- */
455
405
  declare function runWith<TParser extends Parser<Mode, unknown, unknown>, TContexts extends readonly SourceContext<unknown>[], THelp = void, TError = never>(parser: TParser, programName: string, contexts: TContexts, options: RunWithOptions<THelp, TError> & ContextOptionsParam<TContexts, InferValue<TParser>>): Promise<InferValue<TParser>>;
456
406
  /**
457
407
  * Runs a synchronous parser with multiple source contexts.
package/dist/facade.js CHANGED
@@ -12,6 +12,19 @@ import { getDocPage, parseAsync, parseSync, suggest, suggestAsync } from "./pars
12
12
  import { group, longestMatch, object } from "./constructs.js";
13
13
 
14
14
  //#region src/facade.ts
15
+ const SuppressedErrorCtor = typeof SuppressedError === "function" ? SuppressedError : (() => {
16
+ class SuppressedErrorPolyfill extends Error {
17
+ error;
18
+ suppressed;
19
+ constructor(error, suppressed, message$1) {
20
+ super(message$1);
21
+ this.name = "SuppressedError";
22
+ this.error = error;
23
+ this.suppressed = suppressed;
24
+ }
25
+ }
26
+ return SuppressedErrorPolyfill;
27
+ })();
15
28
  function finalizeParsedForContext(context, parsed) {
16
29
  return context.finalizeParsed != null ? context.finalizeParsed(parsed) : parsed;
17
30
  }
@@ -1216,57 +1229,104 @@ function disposeContextsSync(contexts) {
1216
1229
  * );
1217
1230
  * ```
1218
1231
  */
1232
+ async function runWithBody(parser, programName, contexts, args, options) {
1233
+ validateContextIds(contexts);
1234
+ if (needsEarlyExit(args, options)) {
1235
+ if (parser.$mode === "async") return runParser(parser, programName, args, options);
1236
+ return Promise.resolve(runParser(parser, programName, args, options));
1237
+ }
1238
+ const ctxOptions = options.contextOptions;
1239
+ const { annotations: phase1Annotations, annotationsList: phase1AnnotationsList, hasDynamic: needsTwoPhase } = await collectPhase1Annotations(contexts, ctxOptions);
1240
+ if (!needsTwoPhase) {
1241
+ const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
1242
+ if (parser.$mode === "async") return runParser(augmentedParser, programName, args, options);
1243
+ return Promise.resolve(runParser(augmentedParser, programName, args, options));
1244
+ }
1245
+ const augmentedParser1 = injectAnnotationsIntoParser(parser, phase1Annotations);
1246
+ let firstPassResult;
1247
+ let firstPassDeferred;
1248
+ let firstPassDeferredKeys;
1249
+ let firstPassFailed = false;
1250
+ try {
1251
+ if (parser.$mode === "async") firstPassResult = await parseAsync(augmentedParser1, args);
1252
+ else firstPassResult = parseSync(augmentedParser1, args);
1253
+ if (typeof firstPassResult === "object" && firstPassResult !== null && "success" in firstPassResult) {
1254
+ const result = firstPassResult;
1255
+ if (result.success) {
1256
+ firstPassResult = result.value;
1257
+ firstPassDeferred = result.deferred;
1258
+ firstPassDeferredKeys = result.deferredKeys;
1259
+ } else firstPassFailed = true;
1260
+ }
1261
+ } catch {
1262
+ firstPassFailed = true;
1263
+ }
1264
+ if (firstPassFailed) {
1265
+ const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
1266
+ if (parser.$mode === "async") return runParser(augmentedParser, programName, args, options);
1267
+ return Promise.resolve(runParser(augmentedParser, programName, args, options));
1268
+ }
1269
+ const { annotationsList: phase2AnnotationsList } = await collectAnnotations(contexts, firstPassResult, ctxOptions, firstPassDeferred, firstPassDeferredKeys);
1270
+ const finalAnnotations = mergeTwoPhaseAnnotations(phase1AnnotationsList, phase2AnnotationsList);
1271
+ const augmentedParser2 = injectAnnotationsIntoParser(parser, finalAnnotations);
1272
+ if (parser.$mode === "async") return runParser(augmentedParser2, programName, args, options);
1273
+ return Promise.resolve(runParser(augmentedParser2, programName, args, options));
1274
+ }
1219
1275
  async function runWith(parser, programName, contexts, options) {
1220
1276
  const args = options?.args ?? [];
1221
1277
  if (contexts.length === 0) {
1222
1278
  if (parser.$mode === "async") return runParser(parser, programName, args, options);
1223
1279
  return Promise.resolve(runParser(parser, programName, args, options));
1224
1280
  }
1281
+ let result;
1282
+ let primaryError;
1283
+ let hasPrimaryError = false;
1284
+ try {
1285
+ result = await runWithBody(parser, programName, contexts, args, options);
1286
+ } catch (error) {
1287
+ hasPrimaryError = true;
1288
+ primaryError = error;
1289
+ }
1225
1290
  try {
1226
- validateContextIds(contexts);
1227
- if (needsEarlyExit(args, options)) {
1228
- if (parser.$mode === "async") return runParser(parser, programName, args, options);
1229
- return Promise.resolve(runParser(parser, programName, args, options));
1230
- }
1231
- const ctxOptions = options?.contextOptions;
1232
- const { annotations: phase1Annotations, annotationsList: phase1AnnotationsList, hasDynamic: needsTwoPhase } = await collectPhase1Annotations(contexts, ctxOptions);
1233
- if (!needsTwoPhase) {
1234
- const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
1235
- if (parser.$mode === "async") return runParser(augmentedParser, programName, args, options);
1236
- return Promise.resolve(runParser(augmentedParser, programName, args, options));
1237
- }
1238
- const augmentedParser1 = injectAnnotationsIntoParser(parser, phase1Annotations);
1239
- let firstPassResult;
1240
- let firstPassDeferred;
1241
- let firstPassDeferredKeys;
1242
- let firstPassFailed = false;
1243
- try {
1244
- if (parser.$mode === "async") firstPassResult = await parseAsync(augmentedParser1, args);
1245
- else firstPassResult = parseSync(augmentedParser1, args);
1246
- if (typeof firstPassResult === "object" && firstPassResult !== null && "success" in firstPassResult) {
1247
- const result = firstPassResult;
1248
- if (result.success) {
1249
- firstPassResult = result.value;
1250
- firstPassDeferred = result.deferred;
1251
- firstPassDeferredKeys = result.deferredKeys;
1252
- } else firstPassFailed = true;
1253
- }
1254
- } catch {
1255
- firstPassFailed = true;
1256
- }
1257
- if (firstPassFailed) {
1258
- const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
1259
- if (parser.$mode === "async") return runParser(augmentedParser, programName, args, options);
1260
- return Promise.resolve(runParser(augmentedParser, programName, args, options));
1261
- }
1262
- const { annotationsList: phase2AnnotationsList } = await collectAnnotations(contexts, firstPassResult, ctxOptions, firstPassDeferred, firstPassDeferredKeys);
1263
- const finalAnnotations = mergeTwoPhaseAnnotations(phase1AnnotationsList, phase2AnnotationsList);
1264
- const augmentedParser2 = injectAnnotationsIntoParser(parser, finalAnnotations);
1265
- if (parser.$mode === "async") return runParser(augmentedParser2, programName, args, options);
1266
- return Promise.resolve(runParser(augmentedParser2, programName, args, options));
1267
- } finally {
1268
1291
  await disposeContexts(contexts);
1292
+ } catch (disposeError) {
1293
+ if (hasPrimaryError) throw new SuppressedErrorCtor(disposeError, primaryError, "An error was suppressed during context disposal.");
1294
+ throw disposeError;
1295
+ }
1296
+ if (hasPrimaryError) throw primaryError;
1297
+ return result;
1298
+ }
1299
+ /**
1300
+ * Body of {@link runWithSync}, extracted so that the caller can handle
1301
+ * disposal outside a `finally` block (avoiding `no-unsafe-finally` lint).
1302
+ */
1303
+ function runWithSyncBody(parser, programName, contexts, args, options) {
1304
+ validateContextIds(contexts);
1305
+ if (needsEarlyExit(args, options)) return runParser(parser, programName, args, options);
1306
+ const ctxOptions = options.contextOptions;
1307
+ const { annotations: phase1Annotations, annotationsList: phase1AnnotationsList, hasDynamic: needsTwoPhase } = collectPhase1AnnotationsSync(contexts, ctxOptions);
1308
+ if (!needsTwoPhase) {
1309
+ const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
1310
+ return runParser(augmentedParser, programName, args, options);
1311
+ }
1312
+ const augmentedParser1 = injectAnnotationsIntoParser(parser, phase1Annotations);
1313
+ let firstPassResult;
1314
+ let firstPassDeferred;
1315
+ let firstPassDeferredKeys;
1316
+ try {
1317
+ const result = parseSync(augmentedParser1, args);
1318
+ if (result.success) {
1319
+ firstPassResult = result.value;
1320
+ firstPassDeferred = result.deferred;
1321
+ firstPassDeferredKeys = result.deferredKeys;
1322
+ } else return runParser(augmentedParser1, programName, args, options);
1323
+ } catch {
1324
+ return runParser(augmentedParser1, programName, args, options);
1269
1325
  }
1326
+ const { annotationsList: phase2AnnotationsList } = collectAnnotationsSync(contexts, firstPassResult, ctxOptions, firstPassDeferred, firstPassDeferredKeys);
1327
+ const finalAnnotations = mergeTwoPhaseAnnotations(phase1AnnotationsList, phase2AnnotationsList);
1328
+ const augmentedParser2 = injectAnnotationsIntoParser(parser, finalAnnotations);
1329
+ return runParser(augmentedParser2, programName, args, options);
1270
1330
  }
1271
1331
  /**
1272
1332
  * Runs a synchronous parser with multiple source contexts.
@@ -1294,36 +1354,23 @@ function runWithSync(parser, programName, contexts, options) {
1294
1354
  if (parser.$mode !== "sync") throw new TypeError("Cannot use an async parser with runWithSync(). Use runWith() or runWithAsync() instead.");
1295
1355
  const args = options?.args ?? [];
1296
1356
  if (contexts.length === 0) return runParser(parser, programName, args, options);
1357
+ let result;
1358
+ let primaryError;
1359
+ let hasPrimaryError = false;
1360
+ try {
1361
+ result = runWithSyncBody(parser, programName, contexts, args, options);
1362
+ } catch (error) {
1363
+ hasPrimaryError = true;
1364
+ primaryError = error;
1365
+ }
1297
1366
  try {
1298
- validateContextIds(contexts);
1299
- if (needsEarlyExit(args, options)) return runParser(parser, programName, args, options);
1300
- const ctxOptions = options?.contextOptions;
1301
- const { annotations: phase1Annotations, annotationsList: phase1AnnotationsList, hasDynamic: needsTwoPhase } = collectPhase1AnnotationsSync(contexts, ctxOptions);
1302
- if (!needsTwoPhase) {
1303
- const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
1304
- return runParser(augmentedParser, programName, args, options);
1305
- }
1306
- const augmentedParser1 = injectAnnotationsIntoParser(parser, phase1Annotations);
1307
- let firstPassResult;
1308
- let firstPassDeferred;
1309
- let firstPassDeferredKeys;
1310
- try {
1311
- const result = parseSync(augmentedParser1, args);
1312
- if (result.success) {
1313
- firstPassResult = result.value;
1314
- firstPassDeferred = result.deferred;
1315
- firstPassDeferredKeys = result.deferredKeys;
1316
- } else return runParser(augmentedParser1, programName, args, options);
1317
- } catch {
1318
- return runParser(augmentedParser1, programName, args, options);
1319
- }
1320
- const { annotationsList: phase2AnnotationsList } = collectAnnotationsSync(contexts, firstPassResult, ctxOptions, firstPassDeferred, firstPassDeferredKeys);
1321
- const finalAnnotations = mergeTwoPhaseAnnotations(phase1AnnotationsList, phase2AnnotationsList);
1322
- const augmentedParser2 = injectAnnotationsIntoParser(parser, finalAnnotations);
1323
- return runParser(augmentedParser2, programName, args, options);
1324
- } finally {
1325
1367
  disposeContextsSync(contexts);
1368
+ } catch (disposeError) {
1369
+ if (hasPrimaryError) throw new SuppressedErrorCtor(disposeError, primaryError, "An error was suppressed during context disposal.");
1370
+ throw disposeError;
1326
1371
  }
1372
+ if (hasPrimaryError) throw primaryError;
1373
+ return result;
1327
1374
  }
1328
1375
  /**
1329
1376
  * Runs any parser asynchronously with multiple source contexts.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.0.0-dev.1681+85e1e6ac",
3
+ "version": "1.0.0-dev.1684+b4ee4c18",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",