@neocompose/cli 0.26.2 → 0.26.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.26.3] - 2026-08-10
4
+
5
+ ### Changed
6
+
7
+ - Show a phase-aware loading spinner during interactive `neo test` runs, then
8
+ render files, tests, failures, counts, and durations with Vitest-style
9
+ grouping, alignment, color, and emphasis. JSON reports now expose prepare,
10
+ selection, compilation, registration, execution, and finalization timings.
11
+ - Reuse matcher dispatch metadata, build assertion diagnostics only on failure,
12
+ and compare structured values directly instead of serializing them to JSON.
13
+ This keeps successful `expect`, `toBe`, `toEqual`, `toHaveLength`,
14
+ `toContain`, `toMatchObject`, and mock assertions on a smaller hot path.
15
+
3
16
  ## [0.26.2] - 2026-08-10
4
17
 
5
18
  ### Changed
package/dist/neo.mjs CHANGED
@@ -169,6 +169,9 @@ function warn(text) {
169
169
  function note(text) {
170
170
  console.log(color.dim(text));
171
171
  }
172
+ function pad(text, width) {
173
+ return text.length >= width ? text : text + " ".repeat(width - text.length);
174
+ }
172
175
  function paintChangeKind(kind) {
173
176
  const painter = CHANGE_KIND_COLOR[kind];
174
177
  return painter === void 0 ? kind : painter(kind);
@@ -46689,9 +46692,9 @@ ${namedArguments(
46689
46692
  `;
46690
46693
  }
46691
46694
  function namedArguments(entries, spaces, raw = /* @__PURE__ */ new Set()) {
46692
- const pad = " ".repeat(spaces);
46695
+ const pad2 = " ".repeat(spaces);
46693
46696
  return entries.map(
46694
- ([name, value]) => `${pad}${name}: ${raw.has(name) ? rawValue(value) : jsonValue2(value)},`
46697
+ ([name, value]) => `${pad2}${name}: ${raw.has(name) ? rawValue(value) : jsonValue2(value)},`
46695
46698
  ).join("\n");
46696
46699
  }
46697
46700
  function rawValue(value) {
@@ -57376,6 +57379,13 @@ var init_NeoScriptScope = __esm({
57376
57379
  setLocal(bindingId, value) {
57377
57380
  this.#bindings.set(bindingId, value);
57378
57381
  }
57382
+ bindInvocationEntry(bindingId, value) {
57383
+ this.#bindings.set(bindingId, value);
57384
+ }
57385
+ bindInvocationKeyAndEntry(keyBindingId, key, entryBindingId, entry) {
57386
+ this.#bindings.set(keyBindingId, key);
57387
+ this.#bindings.set(entryBindingId, entry);
57388
+ }
57379
57389
  resetInvocationLocals(parameterCount) {
57380
57390
  if (this.#bindings.size > parameterCount) this.#bindings.clear();
57381
57391
  if (this.#readonlyBindingErrors.size > 0) {
@@ -62423,6 +62433,211 @@ function evalDeclaredListIndex(info, scope, ctx) {
62423
62433
  }
62424
62434
  return Array.isArray(hit) ? [...hit] : [];
62425
62435
  }
62436
+ function prepareCollectionCallback(callback, parentScope, ctx, isList, returnContract, onPredicateMatch) {
62437
+ const metrics = ctx.__collectionCallbackPreparationMetrics;
62438
+ if (metrics !== void 0) metrics.bodyValidations += 1;
62439
+ const compilerRevision = callback.compilerRevision ?? 1;
62440
+ if (!Number.isSafeInteger(compilerRevision)) {
62441
+ throw new NSGetterRuntimeError(
62442
+ "Collection callback compiler revision must be a safe integer."
62443
+ );
62444
+ }
62445
+ if (compilerRevision < 1) {
62446
+ throw new NSGetterRuntimeError(
62447
+ "Collection callback compiler revision must be at least 1."
62448
+ );
62449
+ }
62450
+ if (compilerRevision > NEOSCRIPT_COMPILER_REVISION) {
62451
+ throw new NSGetterRuntimeError(
62452
+ `Collection callback compiler revision ${String(compilerRevision)} is newer than supported revision ${String(NEOSCRIPT_COMPILER_REVISION)}.`
62453
+ );
62454
+ }
62455
+ if (!isNSFunctionWithReturnType(callback)) {
62456
+ throw new NSGetterRuntimeError(
62457
+ `Collection callback body metadata is invalid for compiler revision ${String(compilerRevision)}.`
62458
+ );
62459
+ }
62460
+ const parameters = callback.parameters;
62461
+ if (parameters.length < 1) {
62462
+ throw new NSGetterRuntimeError(
62463
+ "Collection callback requires at least one parameter."
62464
+ );
62465
+ }
62466
+ if (parameters.length > 2) {
62467
+ throw new NSGetterRuntimeError(
62468
+ `Collection callback supports at most two parameters, but received ${String(parameters.length)}.`
62469
+ );
62470
+ }
62471
+ if (returnContract === "predicate") {
62472
+ if (callback.typeInfo.type !== 1 /* Bool */) {
62473
+ throw new NSGetterRuntimeError(
62474
+ "Collection predicate callback must declare a Bool return type."
62475
+ );
62476
+ }
62477
+ if (!callback.typeInfo.required) {
62478
+ throw new NSGetterRuntimeError(
62479
+ "Collection predicate callback must declare a required return type."
62480
+ );
62481
+ }
62482
+ }
62483
+ const parameterCount = parameters.length;
62484
+ const callbackScope = ctx.__collectionCallbackStrategy === "fresh" ? null : createChildScope(parentScope);
62485
+ const callbackOptions = evaluationOptions(ctx, false);
62486
+ const instructions = callback.instructions;
62487
+ const returnTypeInfo = callback.typeInfo;
62488
+ const requiresInvocationReset = callbackBodyMayAddInvocationLocals(
62489
+ instructions,
62490
+ new Set(parameters.map((parameter4) => parameter4.id))
62491
+ );
62492
+ let finishBody;
62493
+ if (returnContract === "predicate") {
62494
+ finishBody = (innerScope2) => {
62495
+ const result = evalInstructions(
62496
+ instructions,
62497
+ innerScope2,
62498
+ ctx,
62499
+ callbackOptions
62500
+ );
62501
+ rejectEscapedLoopTransfer(result, "collection callback");
62502
+ if (result.kind !== "return") {
62503
+ throw new NSGetterRuntimeError(
62504
+ "Collection callback ended without returning a value."
62505
+ );
62506
+ }
62507
+ const value = result.value;
62508
+ if (typeof value !== "boolean") {
62509
+ throw new NSGetterRuntimeError(
62510
+ "Collection predicate callback returned a value that does not match its required Bool contract."
62511
+ );
62512
+ }
62513
+ return value;
62514
+ };
62515
+ } else {
62516
+ finishBody = (innerScope2) => {
62517
+ const result = evalInstructions(
62518
+ instructions,
62519
+ innerScope2,
62520
+ ctx,
62521
+ callbackOptions
62522
+ );
62523
+ rejectEscapedLoopTransfer(result, "collection callback");
62524
+ if (result.kind !== "return") {
62525
+ throw new NSGetterRuntimeError(
62526
+ "Collection callback ended without returning a value."
62527
+ );
62528
+ }
62529
+ let value = result.value;
62530
+ if (returnTypeInfo.type === 20 /* Decimal */ && typeof value === "number") {
62531
+ value = coerceDecimalOperand(value, "collection callback return");
62532
+ }
62533
+ if (!runtimeValueMatchesType(value, returnTypeInfo, ctx)) {
62534
+ throw new NSGetterRuntimeError(
62535
+ "Collection projection callback returned a value that does not match its compiled return type."
62536
+ );
62537
+ }
62538
+ return value;
62539
+ };
62540
+ }
62541
+ if (metrics !== void 0) metrics.bindingPlanCreations += 1;
62542
+ if (ctx.__collectionCallbackStrategy === "fresh" || ctx.__collectionCallbackStrategy === "prepared") {
62543
+ return (entry, key, valueId) => {
62544
+ if (onPredicateMatch !== void 0) {
62545
+ consumeBudget(ctx, "workUnits", 1, "work unit");
62546
+ }
62547
+ const innerScope2 = callbackScope ?? createChildScope(parentScope);
62548
+ if (callbackScope !== null) {
62549
+ callbackScope.resetInvocationLocals(parameterCount);
62550
+ }
62551
+ if (parameters.length === 1) {
62552
+ innerScope2.setLocal(parameters[0].id, entry);
62553
+ } else if (parameters.length === 2) {
62554
+ innerScope2.setLocal(
62555
+ parameters[0].id,
62556
+ isList ? Number(key) : String(key)
62557
+ );
62558
+ innerScope2.setLocal(parameters[1].id, entry);
62559
+ }
62560
+ const value = finishBody(innerScope2);
62561
+ if (onPredicateMatch === void 0) return value;
62562
+ return value === true ? onPredicateMatch(entry, key, valueId) : 0 /* Continue */;
62563
+ };
62564
+ }
62565
+ const innerScope = callbackScope ?? createChildScope(parentScope);
62566
+ let bindValues;
62567
+ if (parameterCount === 1) {
62568
+ const entryParameterId = parameters[0].id;
62569
+ bindValues = (entry) => {
62570
+ innerScope.bindInvocationEntry(entryParameterId, entry);
62571
+ };
62572
+ } else {
62573
+ const keyParameterId = parameters[0].id;
62574
+ const entryParameterId = parameters[1].id;
62575
+ bindValues = (entry, key) => {
62576
+ innerScope.bindInvocationKeyAndEntry(
62577
+ keyParameterId,
62578
+ key,
62579
+ entryParameterId,
62580
+ entry
62581
+ );
62582
+ };
62583
+ }
62584
+ if (returnContract === "predicate") {
62585
+ if (onPredicateMatch === void 0) {
62586
+ throw new NSGetterRuntimeError(
62587
+ "Collection predicate preparation requires a match handler."
62588
+ );
62589
+ }
62590
+ if (requiresInvocationReset) {
62591
+ return (entry, key, valueId) => {
62592
+ consumeBudget(ctx, "workUnits", 1, "work unit");
62593
+ innerScope.resetInvocationLocals(parameterCount);
62594
+ bindValues(entry, key);
62595
+ return finishBody(innerScope) === true ? onPredicateMatch(entry, key, valueId) : 0 /* Continue */;
62596
+ };
62597
+ }
62598
+ return (entry, key, valueId) => {
62599
+ consumeBudget(ctx, "workUnits", 1, "work unit");
62600
+ bindValues(entry, key);
62601
+ return finishBody(innerScope) === true ? onPredicateMatch(entry, key, valueId) : 0 /* Continue */;
62602
+ };
62603
+ }
62604
+ if (requiresInvocationReset) {
62605
+ return (entry, key) => {
62606
+ innerScope.resetInvocationLocals(parameterCount);
62607
+ bindValues(entry, key);
62608
+ return finishBody(innerScope);
62609
+ };
62610
+ }
62611
+ return (entry, key) => {
62612
+ bindValues(entry, key);
62613
+ return finishBody(innerScope);
62614
+ };
62615
+ }
62616
+ function callbackBodyMayAddInvocationLocals(instructions, parameterIds) {
62617
+ return instructions.some((instruction) => {
62618
+ switch (instruction.type) {
62619
+ case "variable" /* variable */:
62620
+ return !parameterIds.has(instruction.variable.id);
62621
+ case "assign" /* assign */:
62622
+ return instruction.target.pointer.type === "variable" /* variable */ && !parameterIds.has(instruction.target.pointer.variableId);
62623
+ case "if" /* if */:
62624
+ case "for" /* for */:
62625
+ case "forEach" /* forEach */:
62626
+ case "switch" /* switch */:
62627
+ case "try" /* try */:
62628
+ return true;
62629
+ default:
62630
+ return false;
62631
+ }
62632
+ });
62633
+ }
62634
+ function isListCollection(collection) {
62635
+ if (Array.isArray(collection)) return true;
62636
+ if (typeof collection === "object" && collection !== null) return false;
62637
+ throw new NSGetterRuntimeError(
62638
+ "Collection callback receiver must be a present List or Dictionary value."
62639
+ );
62640
+ }
62426
62641
  function evalFunction(fn, scope, ctx) {
62427
62642
  switch (fn.type) {
62428
62643
  case "classConstructor" /* classConstructor */:
@@ -62609,34 +62824,15 @@ function evalFunction(fn, scope, ctx) {
62609
62824
  case "where" /* where */: {
62610
62825
  const c = evalPointer(fn.info.collectionPointer, scope, ctx);
62611
62826
  const innerFn = fn.info.function;
62612
- const isList = Array.isArray(c);
62827
+ const isList = isListCollection(c);
62613
62828
  const out = isList ? [] : {};
62614
- const useFreshCallbackScope = ctx.__collectionCallbackStrategy === "fresh";
62615
- const callbackScope = useFreshCallbackScope ? null : createChildScope(scope);
62616
- const callbackOptions = useFreshCallbackScope ? null : evaluationOptions(ctx, false);
62617
- const callbackParameterCount = innerFn.parameters.length === 1 || innerFn.parameters.length === 2 ? innerFn.parameters.length : 0;
62618
- iterateCollection(c, ctx, (entry, key, valueId) => {
62619
- consumeBudget(ctx, "workUnits", 1, "work unit");
62620
- const innerScope = callbackScope ?? pushParams(scope, innerFn.parameters, [key, entry], isList);
62621
- if (callbackScope !== null) {
62622
- callbackScope.resetInvocationLocals(callbackParameterCount);
62623
- if (callbackParameterCount === 1) {
62624
- callbackScope.setLocal(innerFn.parameters[0].id, entry);
62625
- } else if (callbackParameterCount === 2) {
62626
- callbackScope.setLocal(
62627
- innerFn.parameters[0].id,
62628
- isList ? Number(key) : String(key)
62629
- );
62630
- callbackScope.setLocal(innerFn.parameters[1].id, entry);
62631
- }
62632
- }
62633
- const result = evalInstructions(
62634
- innerFn.instructions,
62635
- innerScope,
62636
- ctx,
62637
- callbackOptions ?? evaluationOptions(ctx, false)
62638
- );
62639
- if (result.kind === "return" && result.value === true) {
62829
+ const callback = prepareCollectionCallback(
62830
+ innerFn,
62831
+ scope,
62832
+ ctx,
62833
+ isList,
62834
+ "predicate",
62835
+ (entry, key, valueId) => {
62640
62836
  consumeBudget(
62641
62837
  ctx,
62642
62838
  "producedCollectionEntries",
@@ -62648,53 +62844,38 @@ function evalFunction(fn, scope, ctx) {
62648
62844
  } else {
62649
62845
  out[String(key)] = valueId ?? entry;
62650
62846
  }
62847
+ return 0 /* Continue */;
62651
62848
  }
62652
- return 0 /* Continue */;
62653
- });
62849
+ );
62850
+ iterateCollection(c, ctx, callback);
62654
62851
  return out;
62655
62852
  }
62656
62853
  case "first" /* first */:
62657
62854
  case "firstOrDefault" /* firstOrDefault */: {
62658
62855
  const c = evalPointer(fn.info.collectionPointer, scope, ctx);
62659
62856
  const innerFn = fn.info.function ?? null;
62660
- const isList = Array.isArray(c);
62857
+ const isList = isListCollection(c);
62661
62858
  const sentinel = /* @__PURE__ */ Symbol("not-found");
62662
- const useFreshCallbackScope = ctx.__collectionCallbackStrategy === "fresh";
62663
- const callbackScope = innerFn === null || useFreshCallbackScope ? null : createChildScope(scope);
62664
- const callbackOptions = innerFn === null || useFreshCallbackScope ? null : evaluationOptions(ctx, false);
62665
- const callbackParameterCount = innerFn !== null && (innerFn.parameters.length === 1 || innerFn.parameters.length === 2) ? innerFn.parameters.length : 0;
62666
62859
  let found = sentinel;
62667
- iterateCollection(c, ctx, (entry, key) => {
62668
- if (innerFn === null) {
62860
+ if (innerFn === null) {
62861
+ iterateCollection(c, ctx, (entry) => {
62669
62862
  found = entry;
62670
62863
  return 1 /* Break */;
62671
- }
62672
- consumeBudget(ctx, "workUnits", 1, "work unit");
62673
- const innerScope = callbackScope ?? pushParams(scope, innerFn.parameters, [key, entry], isList);
62674
- if (callbackScope !== null) {
62675
- callbackScope.resetInvocationLocals(callbackParameterCount);
62676
- if (callbackParameterCount === 1) {
62677
- callbackScope.setLocal(innerFn.parameters[0].id, entry);
62678
- } else if (callbackParameterCount === 2) {
62679
- callbackScope.setLocal(
62680
- innerFn.parameters[0].id,
62681
- isList ? Number(key) : String(key)
62682
- );
62683
- callbackScope.setLocal(innerFn.parameters[1].id, entry);
62684
- }
62685
- }
62686
- const result = evalInstructions(
62687
- innerFn.instructions,
62688
- innerScope,
62864
+ });
62865
+ } else {
62866
+ const callback = prepareCollectionCallback(
62867
+ innerFn,
62868
+ scope,
62689
62869
  ctx,
62690
- callbackOptions ?? evaluationOptions(ctx, false)
62870
+ isList,
62871
+ "predicate",
62872
+ (entry) => {
62873
+ found = entry;
62874
+ return 1 /* Break */;
62875
+ }
62691
62876
  );
62692
- if (result.kind === "return" && result.value === true) {
62693
- found = entry;
62694
- return 1 /* Break */;
62695
- }
62696
- return 0 /* Continue */;
62697
- });
62877
+ iterateCollection(c, ctx, callback);
62878
+ }
62698
62879
  if (found !== sentinel) return found;
62699
62880
  if (fn.type === "first" /* first */) {
62700
62881
  throw new NSGetterRuntimeError(
@@ -62705,43 +62886,26 @@ function evalFunction(fn, scope, ctx) {
62705
62886
  }
62706
62887
  case "select" /* select */: {
62707
62888
  const c = evalPointer(fn.info.collectionPointer, scope, ctx);
62708
- const useFreshCallbackScope = ctx.__collectionCallbackStrategy === "fresh";
62709
- const callbackScope = useFreshCallbackScope ? null : createChildScope(scope);
62710
- const callbackOptions = useFreshCallbackScope ? null : evaluationOptions(ctx, false);
62711
62889
  const innerFn = fn.info.function;
62712
- const callbackParameterCount = innerFn.parameters.length === 1 || innerFn.parameters.length === 2 ? innerFn.parameters.length : 0;
62713
- const isList = Array.isArray(c);
62890
+ const isList = isListCollection(c);
62891
+ const callback = prepareCollectionCallback(
62892
+ innerFn,
62893
+ scope,
62894
+ ctx,
62895
+ isList,
62896
+ "projection"
62897
+ );
62714
62898
  const out = [];
62715
- iterateCollection(c, ctx, (entry, key) => {
62899
+ iterateCollection(c, ctx, (entry, key, valueId) => {
62716
62900
  consumeBudget(ctx, "workUnits", 1, "work unit");
62717
- const innerScope = callbackScope ?? pushParams(scope, innerFn.parameters, [key, entry], isList);
62718
- if (callbackScope !== null) {
62719
- callbackScope.resetInvocationLocals(callbackParameterCount);
62720
- if (callbackParameterCount === 1) {
62721
- callbackScope.setLocal(innerFn.parameters[0].id, entry);
62722
- } else if (callbackParameterCount === 2) {
62723
- callbackScope.setLocal(
62724
- innerFn.parameters[0].id,
62725
- isList ? Number(key) : String(key)
62726
- );
62727
- callbackScope.setLocal(innerFn.parameters[1].id, entry);
62728
- }
62729
- }
62730
- const result = evalInstructions(
62731
- innerFn.instructions,
62732
- innerScope,
62901
+ const value = callback(entry, key, valueId);
62902
+ consumeBudget(
62733
62903
  ctx,
62734
- callbackOptions ?? evaluationOptions(ctx, false)
62904
+ "producedCollectionEntries",
62905
+ 1,
62906
+ "produced collection entry"
62735
62907
  );
62736
- if (result.kind === "return") {
62737
- consumeBudget(
62738
- ctx,
62739
- "producedCollectionEntries",
62740
- 1,
62741
- "produced collection entry"
62742
- );
62743
- out.push(result.value);
62744
- }
62908
+ out.push(value);
62745
62909
  return 0 /* Continue */;
62746
62910
  });
62747
62911
  return out;
@@ -65294,17 +65458,6 @@ function iterateCollection(c, ctx, callback) {
65294
65458
  return callback(entry, key, valueId);
65295
65459
  });
65296
65460
  }
65297
- function pushParams(parent, parameters, positional, isList) {
65298
- const child = createChildScope(parent);
65299
- if (parameters.length === 1) {
65300
- child.setLocal(parameters[0].id, positional[1]);
65301
- } else if (parameters.length === 2) {
65302
- const first = isList ? Number(positional[0]) : String(positional[0]);
65303
- child.setLocal(parameters[0].id, first);
65304
- child.setLocal(parameters[1].id, positional[1]);
65305
- }
65306
- return child;
65307
- }
65308
65461
  var DELEGATE_LEXICAL_THIS, DELEGATE_LEXICAL_ROOT, NonCatchableNSGetterRuntimeError, NativeFunctionDelegateUnavailableError, CorruptNeoScriptIRError, NeoScriptResourceLimitError, NeoScriptWallClockTimeoutError, DEFAULT_NEO_SCRIPT_EXECUTION_BUDGET_LIMITS, liveListIndexesByProject, evaluatorOwnershipCachesByBase, MAX_CONSTRUCTION_DEPTH, MAX_LOOP_ITERATIONS, resolutionCacheByMembers, NO_SCHEMA_REVISION, LazyValueOverlay, READONLY_FOREACH_BINDING_ERROR, READONLY_CATCH_BINDING_ERROR;
65309
65462
  var init_evaluateNSGetter = __esm({
65310
65463
  "../src/view-models/neoscript-evaluator/evaluateNSGetter.ts"() {
@@ -65318,6 +65471,7 @@ var init_evaluateNSGetter = __esm({
65318
65471
  init_neoscript();
65319
65472
  init_NSGetterRuntimeError();
65320
65473
  init_value_row_owner_members();
65474
+ init_src();
65321
65475
  init_decimal();
65322
65476
  init_members();
65323
65477
  init_core();
@@ -102478,7 +102632,7 @@ var init_registry2 = __esm({
102478
102632
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
102479
102633
  formatVersion: 3,
102480
102634
  contractVersion: "3.9",
102481
- cliVersion: "0.26.2",
102635
+ cliVersion: "0.26.3",
102482
102636
  projectFileUploadBatchSize: 32,
102483
102637
  documentRecords: {
102484
102638
  member: {
@@ -103792,6 +103946,7 @@ import {
103792
103946
  resolve as resolve3,
103793
103947
  sep as sep5
103794
103948
  } from "node:path";
103949
+ import { isDeepStrictEqual } from "node:util";
103795
103950
  function isRecord10(value) {
103796
103951
  return typeof value === "object" && value !== null && !Array.isArray(value);
103797
103952
  }
@@ -103875,9 +104030,46 @@ function stableTestValue(value) {
103875
104030
  function display(value) {
103876
104031
  return stableTestValue(value);
103877
104032
  }
103878
- function assertMatcher(condition, expectationValue, message, expected, received = expectationValue.actual) {
103879
- if (condition === expectationValue.negated)
103880
- throw new NeoTestAssertionError(message, expected, received);
104033
+ function matcherFailureMessage(kind, expectationValue, expected, received, details) {
104034
+ const actual = expectationValue.actual;
104035
+ const negation = expectationValue.negated ? "not " : "";
104036
+ switch (kind) {
104037
+ case "toBe":
104038
+ return `Expected ${display(actual)} ${negation}to be ${display(expected)}.`;
104039
+ case "toEqual":
104040
+ return `Expected ${display(actual)} ${negation}to equal ${display(expected)}.`;
104041
+ case "toBeNull":
104042
+ return `Expected ${display(actual)} ${negation}to be null.`;
104043
+ case "boolean":
104044
+ return `Expected ${display(actual)} ${negation}to be ${String(expected)}.`;
104045
+ case "numeric":
104046
+ return `Expected ${display(actual)} ${negation}to satisfy numeric comparison with ${display(expected)}.`;
104047
+ case "toContain":
104048
+ return `Expected ${display(actual)} ${negation}to contain ${display(expected)}.`;
104049
+ case "toHaveLength":
104050
+ return `Expected length ${String(expected)}, received ${String(received)}.`;
104051
+ case "toMatchObject":
104052
+ return `Expected ${display(actual)} ${negation}to match ${display(expected)}.`;
104053
+ case "toThrow":
104054
+ return `Expected callback ${negation}to throw${details === void 0 ? "" : ` a message containing ${display(details)}`}.`;
104055
+ case "mock":
104056
+ return `Expected mock ${negation}to match recorded calls ${display(details)}.`;
104057
+ }
104058
+ }
104059
+ function assertMatcher(condition, expectationValue, messageKind, expected, received = expectationValue.actual, details) {
104060
+ if (condition === expectationValue.negated) {
104061
+ throw new NeoTestAssertionError(
104062
+ matcherFailureMessage(
104063
+ messageKind,
104064
+ expectationValue,
104065
+ expected,
104066
+ received,
104067
+ details
104068
+ ),
104069
+ expected,
104070
+ received
104071
+ );
104072
+ }
103881
104073
  }
103882
104074
  function errorMessage2(error) {
103883
104075
  return error instanceof Error ? error.message : String(error);
@@ -104426,8 +104618,7 @@ function mockResponse(environment, mock, selected2, call) {
104426
104618
  };
104427
104619
  }
104428
104620
  function partialObjectMatch(actual, expected) {
104429
- if (!isRecord10(expected))
104430
- return stableTestValue(actual) === stableTestValue(expected);
104621
+ if (!isRecord10(expected)) return isDeepStrictEqual(actual, expected);
104431
104622
  if (!isRecord10(actual)) return false;
104432
104623
  return Object.entries(expected).every(
104433
104624
  ([key, value]) => partialObjectMatch(actual[key], value)
@@ -104584,56 +104775,28 @@ function interceptTestCallCore(environment, call) {
104584
104775
  }
104585
104776
  return { handled: true, value: null };
104586
104777
  }
104587
- const matcherIds = /* @__PURE__ */ new Set([
104588
- NEO_TEST_IDS.toBe,
104589
- NEO_TEST_IDS.toEqual,
104590
- NEO_TEST_IDS.toBeNull,
104591
- NEO_TEST_IDS.toBeTrue,
104592
- NEO_TEST_IDS.toBeFalse,
104593
- NEO_TEST_IDS.toBeGreaterThan,
104594
- NEO_TEST_IDS.toBeGreaterThanOrEqual,
104595
- NEO_TEST_IDS.toBeLessThan,
104596
- NEO_TEST_IDS.toBeLessThanOrEqual,
104597
- NEO_TEST_IDS.toContain,
104598
- NEO_TEST_IDS.toHaveLength,
104599
- NEO_TEST_IDS.toMatchObject,
104600
- NEO_TEST_IDS.toThrow,
104601
- NEO_TEST_IDS.toHaveBeenCalled,
104602
- NEO_TEST_IDS.toHaveBeenCalledTimes,
104603
- NEO_TEST_IDS.toHaveBeenCalledWith
104604
- ]);
104605
- if (!matcherIds.has(call.memberId)) return { handled: false };
104778
+ if (!MATCHER_IDS.has(call.memberId)) return { handled: false };
104606
104779
  const expectationValue = readExpectation(call.receiver);
104607
104780
  const actual = expectationValue.actual;
104608
104781
  if (call.memberId === NEO_TEST_IDS.toBe) {
104609
104782
  assertMatcher(
104610
104783
  Object.is(actual, call.args[0]),
104611
104784
  expectationValue,
104612
- `Expected ${display(actual)} ${expectationValue.negated ? "not " : ""}to be ${display(call.args[0])}.`,
104785
+ "toBe",
104613
104786
  call.args[0]
104614
104787
  );
104615
104788
  } else if (call.memberId === NEO_TEST_IDS.toEqual) {
104616
104789
  assertMatcher(
104617
- stableTestValue(actual) === stableTestValue(call.args[0]),
104790
+ isDeepStrictEqual(actual, call.args[0]),
104618
104791
  expectationValue,
104619
- `Expected ${display(actual)} ${expectationValue.negated ? "not " : ""}to equal ${display(call.args[0])}.`,
104792
+ "toEqual",
104620
104793
  call.args[0]
104621
104794
  );
104622
104795
  } else if (call.memberId === NEO_TEST_IDS.toBeNull) {
104623
- assertMatcher(
104624
- actual === null,
104625
- expectationValue,
104626
- `Expected ${display(actual)} ${expectationValue.negated ? "not " : ""}to be null.`,
104627
- null
104628
- );
104796
+ assertMatcher(actual === null, expectationValue, "toBeNull", null);
104629
104797
  } else if (call.memberId === NEO_TEST_IDS.toBeTrue || call.memberId === NEO_TEST_IDS.toBeFalse) {
104630
104798
  const expected = call.memberId === NEO_TEST_IDS.toBeTrue;
104631
- assertMatcher(
104632
- actual === expected,
104633
- expectationValue,
104634
- `Expected ${display(actual)} ${expectationValue.negated ? "not " : ""}to be ${String(expected)}.`,
104635
- expected
104636
- );
104799
+ assertMatcher(actual === expected, expectationValue, "boolean", expected);
104637
104800
  } else if (call.memberId === NEO_TEST_IDS.toBeGreaterThan || call.memberId === NEO_TEST_IDS.toBeGreaterThanOrEqual || call.memberId === NEO_TEST_IDS.toBeLessThan || call.memberId === NEO_TEST_IDS.toBeLessThanOrEqual) {
104638
104801
  const left = Number(actual);
104639
104802
  const right = Number(call.args[0]);
@@ -104641,25 +104804,18 @@ function interceptTestCallCore(environment, call) {
104641
104804
  assertMatcher(
104642
104805
  Number.isFinite(left) && Number.isFinite(right) && matches,
104643
104806
  expectationValue,
104644
- `Expected ${display(actual)} ${expectationValue.negated ? "not " : ""}to satisfy numeric comparison with ${display(call.args[0])}.`,
104807
+ "numeric",
104645
104808
  call.args[0]
104646
104809
  );
104647
104810
  } else if (call.memberId === NEO_TEST_IDS.toContain) {
104648
- const contains2 = typeof actual === "string" ? actual.includes(String(call.args[0])) : Array.isArray(actual) && actual.some(
104649
- (entry) => stableTestValue(entry) === stableTestValue(call.args[0])
104650
- );
104651
- assertMatcher(
104652
- contains2,
104653
- expectationValue,
104654
- `Expected ${display(actual)} ${expectationValue.negated ? "not " : ""}to contain ${display(call.args[0])}.`,
104655
- call.args[0]
104656
- );
104811
+ const contains2 = typeof actual === "string" ? actual.includes(String(call.args[0])) : Array.isArray(actual) && actual.some((entry) => isDeepStrictEqual(entry, call.args[0]));
104812
+ assertMatcher(contains2, expectationValue, "toContain", call.args[0]);
104657
104813
  } else if (call.memberId === NEO_TEST_IDS.toHaveLength) {
104658
104814
  const length = typeof actual === "string" || Array.isArray(actual) ? actual.length : isRecord10(actual) ? Object.keys(actual).length : -1;
104659
104815
  assertMatcher(
104660
104816
  length === call.args[0],
104661
104817
  expectationValue,
104662
- `Expected length ${String(call.args[0])}, received ${String(length)}.`,
104818
+ "toHaveLength",
104663
104819
  call.args[0],
104664
104820
  length
104665
104821
  );
@@ -104667,7 +104823,7 @@ function interceptTestCallCore(environment, call) {
104667
104823
  assertMatcher(
104668
104824
  partialObjectMatch(actual, call.args[0]),
104669
104825
  expectationValue,
104670
- `Expected ${display(actual)} ${expectationValue.negated ? "not " : ""}to match ${display(call.args[0])}.`,
104826
+ "toMatchObject",
104671
104827
  call.args[0]
104672
104828
  );
104673
104829
  } else if (call.memberId === NEO_TEST_IDS.toThrow) {
@@ -104683,9 +104839,10 @@ function interceptTestCallCore(environment, call) {
104683
104839
  assertMatcher(
104684
104840
  matches,
104685
104841
  expectationValue,
104686
- `Expected callback ${expectationValue.negated ? "not " : ""}to throw${pattern === void 0 ? "" : ` a message containing ${display(pattern)}`}.`,
104842
+ "toThrow",
104687
104843
  pattern ?? "a catchable NeoScript error",
104688
- thrown === void 0 ? "no throw" : errorMessage2(thrown)
104844
+ thrown === void 0 ? "no throw" : errorMessage2(thrown),
104845
+ pattern
104689
104846
  );
104690
104847
  } else {
104691
104848
  const handle = readMockHandle(actual);
@@ -104695,15 +104852,14 @@ function interceptTestCallCore(environment, call) {
104695
104852
  "Expected value is not an active mock handle."
104696
104853
  );
104697
104854
  }
104698
- const matches = call.memberId === NEO_TEST_IDS.toHaveBeenCalled ? mock.calls.length > 0 : call.memberId === NEO_TEST_IDS.toHaveBeenCalledTimes ? mock.calls.length === call.args[0] : mock.calls.some(
104699
- (args) => stableTestValue(args) === stableTestValue(call.args)
104700
- );
104855
+ const matches = call.memberId === NEO_TEST_IDS.toHaveBeenCalled ? mock.calls.length > 0 : call.memberId === NEO_TEST_IDS.toHaveBeenCalledTimes ? mock.calls.length === call.args[0] : mock.calls.some((args) => isDeepStrictEqual(args, call.args));
104701
104856
  assertMatcher(
104702
104857
  matches,
104703
104858
  expectationValue,
104704
- `Expected mock ${expectationValue.negated ? "not " : ""}to match recorded calls ${display(mock.calls)}.`,
104859
+ "mock",
104705
104860
  call.memberId === NEO_TEST_IDS.toHaveBeenCalled ? "at least one call" : call.memberId === NEO_TEST_IDS.toHaveBeenCalledTimes ? call.args[0] : call.args,
104706
- call.memberId === NEO_TEST_IDS.toHaveBeenCalledTimes ? mock.calls.length : mock.calls
104861
+ call.memberId === NEO_TEST_IDS.toHaveBeenCalledTimes ? mock.calls.length : mock.calls,
104862
+ mock.calls
104707
104863
  );
104708
104864
  }
104709
104865
  return { handled: true, value: null };
@@ -104994,8 +105150,24 @@ function maintainNeoTestBuildCache(root, maxBytes = TEST_BUILD_CACHE_LIMIT_BYTES
104994
105150
  }
104995
105151
  }
104996
105152
  function formatTestDuration(durationMs) {
105153
+ if (durationMs >= 1e3) {
105154
+ const seconds = durationMs / 1e3;
105155
+ return `${seconds < 10 ? seconds.toFixed(2) : seconds.toFixed(1)}s`;
105156
+ }
104997
105157
  return `${durationMs < 10 ? durationMs.toFixed(2) : durationMs.toFixed(1)}ms`;
104998
105158
  }
105159
+ function paintTestDuration(durationMs) {
105160
+ const formatted = formatTestDuration(durationMs);
105161
+ return durationMs >= 300 ? color.yellow(formatted) : color.dim(formatted);
105162
+ }
105163
+ function formatStatusCounts(passed, failed, total, skipped = 0) {
105164
+ const parts = [
105165
+ failed > 0 ? color.red(`${failed} failed`) : null,
105166
+ passed > 0 ? color.green(`${passed} passed`) : null,
105167
+ skipped > 0 ? color.yellow(`${skipped} skipped`) : null
105168
+ ].filter((part) => part !== null);
105169
+ return `${parts.join(color.dim(" | "))} ${color.dim(`(${total})`)}`;
105170
+ }
104999
105171
  async function runTest(workspace, options, dependencies = {}) {
105000
105172
  const started = Date.now();
105001
105173
  const performanceStarted = performance.now();
@@ -105025,7 +105197,29 @@ async function runTest(workspace, options, dependencies = {}) {
105025
105197
  }
105026
105198
  };
105027
105199
  process.on("SIGINT", handleSigint);
105200
+ const phaseDurations = {
105201
+ prepare: 0,
105202
+ select: 0,
105203
+ compile: 0,
105204
+ register: 0,
105205
+ execute: 0,
105206
+ finalize: 0
105207
+ };
105028
105208
  let phase = "prepare";
105209
+ let phaseStarted = performanceStarted;
105210
+ const enterPhase = (next, label) => {
105211
+ const finished = performance.now();
105212
+ phaseDurations[phase] += finished - phaseStarted;
105213
+ phase = next;
105214
+ phaseStarted = finished;
105215
+ dependencies.progress?.update(label);
105216
+ };
105217
+ const finishPhase = () => {
105218
+ const finished = performance.now();
105219
+ phaseDurations[phase] += finished - phaseStarted;
105220
+ phaseStarted = finished;
105221
+ };
105222
+ dependencies.progress?.update("Preparing test project\u2026");
105029
105223
  try {
105030
105224
  let candidate = dependencies.prepareLocalCandidate === void 0 ? preparedHookCandidate(workspace) : null;
105031
105225
  let candidateInputFingerprint = null;
@@ -105037,7 +105231,11 @@ async function runTest(workspace, options, dependencies = {}) {
105037
105231
  candidate = cachedTestCandidate(workspace, candidateInputFingerprint);
105038
105232
  }
105039
105233
  if (candidate === null) {
105040
- const prepared = dependencies.prepareLocalCandidate === void 0 ? await prepareLocalCandidateV4(workspace) : await dependencies.prepareLocalCandidate(workspace);
105234
+ const prepared = dependencies.prepareLocalCandidate === void 0 ? await prepareLocalCandidateV4(workspace, {
105235
+ onPhase: (label) => {
105236
+ dependencies.progress?.update(label);
105237
+ }
105238
+ }) : await dependencies.prepareLocalCandidate(workspace);
105041
105239
  if (prepared.document === null) {
105042
105240
  throw new NeoTestReportWriteError(
105043
105241
  "Local test preparation did not produce a project document."
@@ -105069,7 +105267,7 @@ async function runTest(workspace, options, dependencies = {}) {
105069
105267
  rawDocument,
105070
105268
  candidate.documentSha256
105071
105269
  );
105072
- phase = "select";
105270
+ enterPhase("select", "Selecting test files\u2026");
105073
105271
  let selectedPaths;
105074
105272
  try {
105075
105273
  selectedPaths = selectedSpecPaths(workspace, options.selectors);
@@ -105084,7 +105282,7 @@ async function runTest(workspace, options, dependencies = {}) {
105084
105282
  "No .spec.neo files matched. Pass --passWithNoTests to treat this as success."
105085
105283
  );
105086
105284
  }
105087
- phase = "compile";
105285
+ enterPhase("compile", "Compiling test files\u2026");
105088
105286
  specs = selectedPaths.map(
105089
105287
  (path) => compileSpec(workspace, rawDocument, path, compilationHash)
105090
105288
  );
@@ -105098,7 +105296,7 @@ async function runTest(workspace, options, dependencies = {}) {
105098
105296
  );
105099
105297
  }
105100
105298
  }
105101
- phase = "register";
105299
+ enterPhase("register", "Registering tests\u2026");
105102
105300
  const registered = specs.map((spec) => registerSpec(spec, rawDocument));
105103
105301
  const selectedTests = registered.flatMap((entry) => entry.tests).filter((test) => pattern === null || pattern.test(test.fullName));
105104
105302
  for (const entry of registered) {
@@ -105115,7 +105313,7 @@ async function runTest(workspace, options, dependencies = {}) {
105115
105313
  );
105116
105314
  }
105117
105315
  const selectedIds = new Set(selectedTests.map((test) => test.id));
105118
- phase = "execute";
105316
+ enterPhase("execute", "Running tests\u2026");
105119
105317
  const timeoutMs = options.timeoutMs ?? workspace.config.test?.timeoutMs ?? 5e3;
105120
105318
  for (const entry of registered) {
105121
105319
  const executed = await executeRegisteredSpec(
@@ -105133,6 +105331,7 @@ async function runTest(workspace, options, dependencies = {}) {
105133
105331
  if (results.some((result) => result.status === "failed") || [...fileFailuresByPath.values()].some((failures) => failures.length > 0)) {
105134
105332
  exitCode = 1;
105135
105333
  }
105334
+ enterPhase("finalize", "Finalizing test report\u2026");
105136
105335
  const buildManifest = {
105137
105336
  version: 1,
105138
105337
  cliVersion: PROJECT_SCHEMA_CONTRACT.cliVersion,
@@ -105168,6 +105367,8 @@ async function runTest(workspace, options, dependencies = {}) {
105168
105367
  related: []
105169
105368
  });
105170
105369
  }
105370
+ finishPhase();
105371
+ dependencies.progress?.stop();
105171
105372
  process.off("SIGINT", handleSigint);
105172
105373
  const failed = results.filter((result) => result.status === "failed").length;
105173
105374
  const selectedTestTotal = [...selectedTestCountByFile.values()].reduce(
@@ -105221,6 +105422,14 @@ async function runTest(workspace, options, dependencies = {}) {
105221
105422
  sharedStartupDurationMs,
105222
105423
  testDurationMs
105223
105424
  },
105425
+ timings: {
105426
+ prepareMs: phaseDurations.prepare,
105427
+ selectMs: phaseDurations.select,
105428
+ compileMs: phaseDurations.compile,
105429
+ registerMs: phaseDurations.register,
105430
+ executeMs: phaseDurations.execute,
105431
+ finalizeMs: phaseDurations.finalize
105432
+ },
105224
105433
  testFiles,
105225
105434
  diagnostics,
105226
105435
  startedAt
@@ -105244,33 +105453,63 @@ async function runTest(workspace, options, dependencies = {}) {
105244
105453
  if (options.reporter === "json") {
105245
105454
  if (options.outputFile === null) process.stdout.write(serialized);
105246
105455
  } else {
105456
+ console.log("");
105457
+ console.log(
105458
+ ` ${color.bold("RUN")} ${color.dim(`neo ${PROJECT_SCHEMA_CONTRACT.cliVersion} ${workspace.root}`)}`
105459
+ );
105460
+ console.log("");
105247
105461
  console.log(
105248
- `\u21BB shared startup (${formatTestDuration(report.summary.sharedStartupDurationMs)})`
105462
+ ` ${color.cyan("\u21BB")} ${color.dim("shared startup")} ${paintTestDuration(report.summary.sharedStartupDurationMs)}`
105249
105463
  );
105250
- for (const result of results) {
105464
+ for (const file of testFiles) {
105465
+ const fileDurationMs = file.tests.reduce(
105466
+ (sum, test) => sum + test.durationMs,
105467
+ 0
105468
+ );
105469
+ const fileSymbol = file.status === "passed" ? color.green("\u2713") : file.status === "failed" ? color.red("\xD7") : color.yellow("!");
105470
+ const testLabel = `${file.tests.length} ${file.tests.length === 1 ? "test" : "tests"}`;
105251
105471
  console.log(
105252
- `${result.status === "passed" ? "\u2713" : "\u2717"} ${result.file} > ${result.name} (total ${formatTestDuration(result.durationMs)}; test ${formatTestDuration(result.testDurationMs)}; startup ${formatTestDuration(result.startupDurationMs)})`
105472
+ ` ${fileSymbol} ${color.bold(file.path)} ${color.dim(`(${testLabel})`)} ${paintTestDuration(fileDurationMs)}`
105253
105473
  );
105254
- for (const failure of result.failures)
105255
- console.log(` ${failure.message}`);
105256
- }
105257
- for (const file of testFiles) {
105474
+ for (const result of file.tests) {
105475
+ const resultSymbol = result.status === "passed" ? color.green("\u2713") : color.red("\xD7");
105476
+ console.log(
105477
+ ` ${resultSymbol} ${result.name} ${color.dim("total")} ${paintTestDuration(result.durationMs)} ${color.dim(`(test ${formatTestDuration(result.testDurationMs)}, startup ${formatTestDuration(result.startupDurationMs)})`)}`
105478
+ );
105479
+ for (const failure of result.failures) {
105480
+ console.log(` ${color.red(failure.message)}`);
105481
+ }
105482
+ }
105258
105483
  if (file.status === "interrupted") {
105259
105484
  console.log(
105260
- `! ${file.path} > interrupted before all selected tests ran`
105485
+ ` ${color.yellow("!")} interrupted before all selected tests ran`
105261
105486
  );
105262
105487
  }
105263
105488
  for (const failure of file.failures) {
105264
105489
  console.log(
105265
- `\u2717 ${file.path} > ${failure.frames[0]?.member ?? "suite hook"}`
105490
+ ` ${color.red("\xD7")} ${failure.frames[0]?.member ?? "suite hook"}`
105266
105491
  );
105267
- console.log(` ${failure.message}`);
105492
+ console.log(` ${color.red(failure.message)}`);
105268
105493
  }
105269
105494
  }
105270
- for (const diagnostic of diagnostics)
105271
- console.log(`\u2717 ${diagnostic.message}`);
105495
+ for (const diagnostic of diagnostics) {
105496
+ console.log(` ${color.red("\xD7")} ${diagnostic.message}`);
105497
+ }
105498
+ console.log("");
105499
+ const passedFiles = testFiles.filter(
105500
+ (file) => file.status === "passed"
105501
+ ).length;
105502
+ const failedFiles = testFiles.filter(
105503
+ (file) => file.status === "failed"
105504
+ ).length;
105505
+ console.log(
105506
+ ` ${color.bold(pad("Test Files", 11))} ${formatStatusCounts(passedFiles, failedFiles, report.summary.files)}`
105507
+ );
105508
+ console.log(
105509
+ ` ${color.bold(pad("Tests", 11))} ${formatStatusCounts(report.summary.passed, report.summary.failed, report.summary.tests, report.summary.skipped)}`
105510
+ );
105272
105511
  console.log(
105273
- `${report.summary.passed} passed, ${report.summary.failed} failed (total ${formatTestDuration(report.summary.durationMs)}; test ${formatTestDuration(report.summary.testDurationMs)}; startup ${formatTestDuration(report.summary.startupDurationMs)})`
105512
+ ` ${color.bold(pad("Duration", 11))} ${color.bold(`total ${formatTestDuration(report.summary.durationMs)}`)} ${color.dim(`(test ${formatTestDuration(report.summary.testDurationMs)}, startup ${formatTestDuration(report.summary.startupDurationMs)})`)}`
105274
105513
  );
105275
105514
  }
105276
105515
  if (!report.success) process.exitCode = exitCode;
@@ -105317,7 +105556,7 @@ async function inspectNeoTestCompilation(workspace, dependencies = {}) {
105317
105556
  errors
105318
105557
  };
105319
105558
  }
105320
- var TEST_BUILD_CACHE_LIMIT_BYTES, ABANDONED_TEMP_MAX_AGE_MS, TEST_CANDIDATE_CACHE_REVISION, NeoTestUsageError, NeoTestNoTestsError, NeoTestRegistrationError, NeoTestReportWriteError, NeoTestPreparedCandidateError, NeoTestInterruptedError, NeoTestAssertionError, ERROR_SOURCE_POSITIONS, SHARED_EVALUATOR_BASES, REGISTRATION_IDS;
105559
+ var TEST_BUILD_CACHE_LIMIT_BYTES, ABANDONED_TEMP_MAX_AGE_MS, TEST_CANDIDATE_CACHE_REVISION, NeoTestUsageError, NeoTestNoTestsError, NeoTestRegistrationError, NeoTestReportWriteError, NeoTestPreparedCandidateError, NeoTestInterruptedError, NeoTestAssertionError, ERROR_SOURCE_POSITIONS, SHARED_EVALUATOR_BASES, REGISTRATION_IDS, MATCHER_IDS;
105321
105560
  var init_test = __esm({
105322
105561
  "src/commands/test.ts"() {
105323
105562
  "use strict";
@@ -105330,6 +105569,7 @@ var init_test = __esm({
105330
105569
  init_push();
105331
105570
  init_registry2();
105332
105571
  init_push_hook();
105572
+ init_ui();
105333
105573
  TEST_BUILD_CACHE_LIMIT_BYTES = 512 * 1024 * 1024;
105334
105574
  ABANDONED_TEMP_MAX_AGE_MS = 60 * 60 * 1e3;
105335
105575
  TEST_CANDIDATE_CACHE_REVISION = 1;
@@ -105374,6 +105614,24 @@ var init_test = __esm({
105374
105614
  NEO_TEST_IDS.beforeEach,
105375
105615
  NEO_TEST_IDS.afterEach
105376
105616
  ]);
105617
+ MATCHER_IDS = /* @__PURE__ */ new Set([
105618
+ NEO_TEST_IDS.toBe,
105619
+ NEO_TEST_IDS.toEqual,
105620
+ NEO_TEST_IDS.toBeNull,
105621
+ NEO_TEST_IDS.toBeTrue,
105622
+ NEO_TEST_IDS.toBeFalse,
105623
+ NEO_TEST_IDS.toBeGreaterThan,
105624
+ NEO_TEST_IDS.toBeGreaterThanOrEqual,
105625
+ NEO_TEST_IDS.toBeLessThan,
105626
+ NEO_TEST_IDS.toBeLessThanOrEqual,
105627
+ NEO_TEST_IDS.toContain,
105628
+ NEO_TEST_IDS.toHaveLength,
105629
+ NEO_TEST_IDS.toMatchObject,
105630
+ NEO_TEST_IDS.toThrow,
105631
+ NEO_TEST_IDS.toHaveBeenCalled,
105632
+ NEO_TEST_IDS.toHaveBeenCalledTimes,
105633
+ NEO_TEST_IDS.toHaveBeenCalledWith
105634
+ ]);
105377
105635
  }
105378
105636
  });
105379
105637
 
@@ -108939,7 +109197,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
108939
109197
  async function main() {
108940
109198
  const args = parseArgs(process.argv.slice(2));
108941
109199
  if (args.command === "--version") {
108942
- console.log("0.26.2");
109200
+ console.log("0.26.3");
108943
109201
  return;
108944
109202
  }
108945
109203
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
@@ -109223,15 +109481,25 @@ async function main() {
109223
109481
  "--testTimeout must be a positive number of milliseconds."
109224
109482
  );
109225
109483
  }
109226
- const { runTest: runTest2 } = await Promise.resolve().then(() => (init_test(), test_exports));
109227
- await runTest2(workspace, {
109228
- selectors: args.positional,
109229
- testNamePattern: stringFlag(args, "testNamePattern"),
109230
- reporter: reporterValue,
109231
- outputFile: stringFlag(args, "outputFile") ?? stringFlag(args, "output-file"),
109232
- passWithNoTests: boolFlag(args, "passWithNoTests"),
109233
- ...timeoutMs === void 0 ? {} : { timeoutMs }
109234
- });
109484
+ const progress = reporterValue === "default" && process.stdout.isTTY === true ? spinner("Preparing test project\u2026") : null;
109485
+ try {
109486
+ const { runTest: runTest2 } = await Promise.resolve().then(() => (init_test(), test_exports));
109487
+ await runTest2(
109488
+ workspace,
109489
+ {
109490
+ selectors: args.positional,
109491
+ testNamePattern: stringFlag(args, "testNamePattern"),
109492
+ reporter: reporterValue,
109493
+ outputFile: stringFlag(args, "outputFile") ?? stringFlag(args, "output-file"),
109494
+ passWithNoTests: boolFlag(args, "passWithNoTests"),
109495
+ ...timeoutMs === void 0 ? {} : { timeoutMs }
109496
+ },
109497
+ progress === null ? {} : { progress }
109498
+ );
109499
+ } catch (error) {
109500
+ progress?.fail("Test run failed.");
109501
+ throw error;
109502
+ }
109235
109503
  return;
109236
109504
  }
109237
109505
  case "dev": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.26.2",
3
+ "version": "0.26.3",
4
4
  "description": "Neo Compose native project-source CLI with bidirectional sync.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,7 +9,7 @@ description: >-
9
9
  `@neocompose/cli` or `node cli/bin/neo.mjs` in the neo-compose repository.
10
10
  ---
11
11
 
12
- <!-- reviewed-through-cli: 0.26.2 -->
12
+ <!-- reviewed-through-cli: 0.26.3 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -83,7 +83,7 @@ wrappers.
83
83
  The marker near the top of `SKILL.md` must exactly match the package version:
84
84
 
85
85
  ```html
86
- <!-- reviewed-through-cli: 0.26.2 -->
86
+ <!-- reviewed-through-cli: 0.26.3 -->
87
87
  ```
88
88
 
89
89
  The quoted version above is checked too, so this instruction cannot go stale