vestjs-runtime 1.6.0 → 1.7.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 (136) hide show
  1. package/IsolateSerializer/package.json +8 -12
  2. package/README.md +1 -3
  3. package/dist/cjs/IsolateSerializer.development.js +135 -0
  4. package/dist/cjs/IsolateSerializer.development.js.map +1 -0
  5. package/dist/cjs/IsolateSerializer.js +6 -0
  6. package/dist/cjs/IsolateSerializer.production.js +2 -0
  7. package/dist/cjs/IsolateSerializer.production.js.map +1 -0
  8. package/dist/cjs/package.json +1 -0
  9. package/dist/cjs/test-utils.development.js +61 -0
  10. package/dist/cjs/test-utils.development.js.map +1 -0
  11. package/dist/cjs/test-utils.js +6 -0
  12. package/dist/cjs/test-utils.production.js +2 -0
  13. package/dist/cjs/test-utils.production.js.map +1 -0
  14. package/dist/cjs/vestjs-runtime.development.js +686 -0
  15. package/dist/cjs/vestjs-runtime.development.js.map +1 -0
  16. package/dist/cjs/vestjs-runtime.js +6 -0
  17. package/dist/cjs/vestjs-runtime.production.js +2 -0
  18. package/dist/cjs/vestjs-runtime.production.js.map +1 -0
  19. package/dist/es/IsolateSerializer.development.js +133 -0
  20. package/dist/es/IsolateSerializer.development.js.map +1 -0
  21. package/dist/es/IsolateSerializer.production.js +2 -0
  22. package/dist/es/IsolateSerializer.production.js.map +1 -0
  23. package/dist/es/package.json +1 -0
  24. package/dist/es/test-utils.development.js +59 -0
  25. package/dist/es/test-utils.development.js.map +1 -0
  26. package/dist/es/test-utils.production.js +2 -0
  27. package/dist/es/test-utils.production.js.map +1 -0
  28. package/dist/es/vestjs-runtime.development.js +675 -0
  29. package/dist/es/vestjs-runtime.development.js.map +1 -0
  30. package/dist/es/vestjs-runtime.production.js +2 -0
  31. package/dist/es/vestjs-runtime.production.js.map +1 -0
  32. package/dist/umd/IsolateSerializer.development.js +138 -0
  33. package/dist/umd/IsolateSerializer.development.js.map +1 -0
  34. package/dist/umd/IsolateSerializer.production.js +2 -0
  35. package/dist/umd/IsolateSerializer.production.js.map +1 -0
  36. package/dist/umd/test-utils.development.js +67 -0
  37. package/dist/umd/test-utils.development.js.map +1 -0
  38. package/dist/umd/test-utils.production.js +2 -0
  39. package/dist/umd/test-utils.production.js.map +1 -0
  40. package/dist/umd/vestjs-runtime.development.js +688 -0
  41. package/dist/umd/vestjs-runtime.development.js.map +1 -0
  42. package/dist/umd/vestjs-runtime.production.js +2 -0
  43. package/dist/umd/vestjs-runtime.production.js.map +1 -0
  44. package/package.json +87 -42
  45. package/test-utils/package.json +8 -12
  46. package/types/IsolateSerializer.d.ts +42 -0
  47. package/types/IsolateSerializer.d.ts.map +1 -0
  48. package/types/test-utils.d.ts +37 -0
  49. package/types/test-utils.d.ts.map +1 -0
  50. package/types/vestjs-runtime.d.ts +257 -351
  51. package/types/vestjs-runtime.d.ts.map +1 -0
  52. package/vitest.config.ts +17 -9
  53. package/dist/IsolateKeys-B21aPuBk.mjs +0 -23
  54. package/dist/IsolateKeys-B21aPuBk.mjs.map +0 -1
  55. package/dist/IsolateKeys-CCvALpZC.cjs +0 -35
  56. package/dist/IsolateKeys-CCvALpZC.cjs.map +0 -1
  57. package/dist/IsolateSerializer-B1hE3gmT.mjs +0 -1004
  58. package/dist/IsolateSerializer-B1hE3gmT.mjs.map +0 -1
  59. package/dist/IsolateSerializer-pbEf5gB2.cjs +0 -1121
  60. package/dist/IsolateSerializer-pbEf5gB2.cjs.map +0 -1
  61. package/dist/chunk-CLMFDpHK.mjs +0 -18
  62. package/dist/exports/IsolateSerializer.cjs +0 -4
  63. package/dist/exports/IsolateSerializer.mjs +0 -4
  64. package/dist/exports/test-utils.cjs +0 -21
  65. package/dist/exports/test-utils.cjs.map +0 -1
  66. package/dist/exports/test-utils.mjs +0 -21
  67. package/dist/exports/test-utils.mjs.map +0 -1
  68. package/dist/vestjs-runtime.cjs +0 -153
  69. package/dist/vestjs-runtime.cjs.map +0 -1
  70. package/dist/vestjs-runtime.mjs +0 -117
  71. package/dist/vestjs-runtime.mjs.map +0 -1
  72. package/docs/IsolateRegistry.docs.md +0 -146
  73. package/docs/Isolates.md +0 -97
  74. package/src/Bus.ts +0 -46
  75. package/src/Isolate/Isolate.ts +0 -163
  76. package/src/Isolate/IsolateFocused.ts +0 -93
  77. package/src/Isolate/IsolateIndexer.ts +0 -42
  78. package/src/Isolate/IsolateInspector.ts +0 -93
  79. package/src/Isolate/IsolateKeys.ts +0 -18
  80. package/src/Isolate/IsolateMutator.ts +0 -165
  81. package/src/Isolate/IsolateRegistry.ts +0 -176
  82. package/src/Isolate/IsolateReorderable.ts +0 -11
  83. package/src/Isolate/IsolateSelectors.ts +0 -25
  84. package/src/Isolate/IsolateStateMachine.ts +0 -30
  85. package/src/Isolate/IsolateStatus.ts +0 -8
  86. package/src/Isolate/IsolateTransient.ts +0 -27
  87. package/src/Isolate/IsolateTypes.ts +0 -33
  88. package/src/Isolate/__tests__/Isolate.test.ts +0 -123
  89. package/src/Isolate/__tests__/IsolateFocused.test.ts +0 -199
  90. package/src/Isolate/__tests__/IsolateInspector.test.ts +0 -136
  91. package/src/Isolate/__tests__/IsolateMutator.test.ts +0 -164
  92. package/src/Isolate/__tests__/IsolatePropagation.test.ts +0 -170
  93. package/src/Isolate/__tests__/IsolateReorderable.test.ts +0 -111
  94. package/src/Isolate/__tests__/IsolateSelectors.test.ts +0 -72
  95. package/src/Isolate/__tests__/IsolateStatus.test.ts +0 -44
  96. package/src/Isolate/__tests__/IsolateTransient.test.ts +0 -58
  97. package/src/Isolate/__tests__/__snapshots__/asyncIsolate.test.ts.snap +0 -71
  98. package/src/Isolate/__tests__/asyncIsolate.test.ts +0 -85
  99. package/src/IsolateWalker.ts +0 -359
  100. package/src/Orchestrator/RuntimeStates.ts +0 -4
  101. package/src/Reconciler.ts +0 -178
  102. package/src/RuntimeEvents.ts +0 -9
  103. package/src/VestRuntime.ts +0 -421
  104. package/src/__tests__/Bus.test.ts +0 -57
  105. package/src/__tests__/IsolateWalker.iterative.test.ts +0 -77
  106. package/src/__tests__/IsolateWalker.test.ts +0 -418
  107. package/src/__tests__/Reconciler.test.ts +0 -193
  108. package/src/__tests__/Reconciler.transient.test.ts +0 -166
  109. package/src/__tests__/VestRuntime.test.ts +0 -212
  110. package/src/__tests__/VestRuntimeStateMachine.test.ts +0 -36
  111. package/src/__tests__/vestjs-runtime.test.ts +0 -19
  112. package/src/errors/ErrorStrings.ts +0 -6
  113. package/src/exports/IsolateSerializer.ts +0 -131
  114. package/src/exports/__tests__/IsolateSerializer.test.ts +0 -334
  115. package/src/exports/__tests__/IsolateSerializer.transient.test.ts +0 -101
  116. package/src/exports/__tests__/__snapshots__/IsolateSerializer.test.ts.snap +0 -5
  117. package/src/exports/test-utils.ts +0 -17
  118. package/src/vestjs-runtime.ts +0 -28
  119. package/types/Isolate-DChR7h5K.d.mts +0 -58
  120. package/types/Isolate-DChR7h5K.d.mts.map +0 -1
  121. package/types/Isolate-HYIh82M8.d.cts +0 -58
  122. package/types/Isolate-HYIh82M8.d.cts.map +0 -1
  123. package/types/IsolateSerializer-BCg01Px5.d.mts +0 -13
  124. package/types/IsolateSerializer-BCg01Px5.d.mts.map +0 -1
  125. package/types/IsolateSerializer-CQpP6A4m.d.cts +0 -13
  126. package/types/IsolateSerializer-CQpP6A4m.d.cts.map +0 -1
  127. package/types/exports/IsolateSerializer.d.cts +0 -3
  128. package/types/exports/IsolateSerializer.d.mts +0 -3
  129. package/types/exports/test-utils.d.cts +0 -7
  130. package/types/exports/test-utils.d.cts.map +0 -1
  131. package/types/exports/test-utils.d.mts +0 -7
  132. package/types/exports/test-utils.d.mts.map +0 -1
  133. package/types/vestjs-runtime.d.cts +0 -372
  134. package/types/vestjs-runtime.d.cts.map +0 -1
  135. package/types/vestjs-runtime.d.mts +0 -370
  136. package/types/vestjs-runtime.d.mts.map +0 -1
@@ -1,146 +0,0 @@
1
- # Isolate Registry Architecture & API
2
-
3
- The Isolate Registry is a high-performance, O(1) indexing system that tracks isolates based on configurable predicates and keys. It eliminates the need for expensive tree traversals when calculating suite results or checking test statuses.
4
-
5
- ## 🏗️ Architecture
6
-
7
- The Registry is divided into two layers:
8
-
9
- 1. **Generic Engine (`vestjs-runtime/IsolateRegistry`)**: A predicated indexing engine with **configurable key selectors**. It manages maps of sets on the root isolate and is completely agnostic to both the predicates and key extraction logic.
10
- 2. **Vest Configuration (`vest/TestRegistry`)**: Defines the categories (failed, pending, etc.) and provides **both predicates and key selectors** (e.g., extracting `fieldName` from test data).
11
-
12
- ### Key Configuration (`RegistryCategoryConfig`)
13
-
14
- Each category is defined by:
15
-
16
- ```typescript
17
- type RegistryCategoryConfig = {
18
- predicate: (isolate: TIsolate) => boolean; // What isolates match this category?
19
- getKey: (isolate: TIsolate) => string; // How to group them (e.g., by fieldName)?
20
- };
21
- ```
22
-
23
- This decoupling allows `vestjs-runtime` to remain **domain-agnostic** while Vest defines how to extract keys from its test data.
24
-
25
- ## 🏗️ Structure & Location
26
-
27
- The registry is stateful and is stored directly on the **Root Isolate** of a suite.
28
-
29
- ### Storage Location
30
-
31
- Each category is stored in the `root.data` object with a `registry_` prefix:
32
-
33
- - `root.data.registry_failed`
34
- - `root.data.registry_pending`
35
- - ...and so on.
36
-
37
- ### Shape
38
-
39
- Each registry entry is a `RegistryIndex`:
40
-
41
- ```typescript
42
- type RegistryIndex = Map<string, Set<TIsolate>>;
43
- ```
44
-
45
- - **Key**: Determined by the `getKey` function in the category config (e.g., `fieldName` for tests, or `_generic` for null keys).
46
- - **Value**: A `Set` containing all isolates currently matching that category.
47
-
48
- ---
49
-
50
- ## 🔄 Update Lifecycle
51
-
52
- The registry is **reactive**. It re-evaluates its state whenever relevant events occur.
53
-
54
- ### Flowchart
55
-
56
- ```text
57
- [ Isolate Event ]
58
- |
59
- ( Enter / Status Change )
60
- |
61
- v
62
- useUpdateRegistry
63
- |
64
- +---------v-------------------+
65
- | Iterates all Predicates |
66
- +---------|-------------------+
67
- |
68
- +---------v-------------------+
69
- | For each Category: |
70
- | [Match?] --Yes--> [Add] |
71
- | |-------No---> [Remove] |
72
- +---------|-------------------+
73
- |
74
- v
75
- Root.data["registry_<category>"]
76
- ```
77
-
78
- ### When do updates happen?
79
-
80
- 1. **Initial Registration (`ISOLATED_ENTER`)**: When a node is first encountered.
81
- 2. **Status Changes (`setStatus`)**: When a test moves between states (e.g. Pending -> Failed).
82
- 3. **Cross-Suite Reconciliation**: When a tree is reprocessed or merged.
83
- 4. **Field Removal**: When `REMOVE_FIELD` is emitted, the registry cleans up all indexed categories for a specific field.
84
-
85
- ---
86
-
87
- ## 🛠️ APIs
88
-
89
- ### `IsolateCategory` (Vest specific)
90
-
91
- - `failed`: Tests with error severity.
92
- - `omitted`: Tests that were not run.
93
- - `passing`: Tests that passed.
94
- - `pending`: Tests currently in progress.
95
- - `tested`: Tests that have finished running.
96
- - `valid`: Tests that are either passing or warnings.
97
- - `warning`: Tests with warning severity.
98
-
99
- ### Core Engine (`vestjs-runtime/IsolateRegistry`)
100
-
101
- #### `useUpdateRegistry(isolate, predicates)`
102
-
103
- ```typescript
104
- useUpdateRegistry(
105
- isolate: TIsolate,
106
- predicates: Record<string, RegistryCategoryConfig>
107
- ): void
108
- ```
109
-
110
- Evaluates all predicates and updates the registry. For each category:
111
-
112
- - Calls `config.predicate(isolate)` to check if it matches
113
- - Calls `config.getKey(isolate)` to determine the index key
114
- - Adds to or removes from the appropriate registry index
115
-
116
- #### `useGetFromRegistry(category, key?)`
117
-
118
- ```typescript
119
- useGetFromRegistry(category: string, key?: string): Set<TIsolate>
120
- ```
121
-
122
- Retrieves isolates for a category. If `key` is provided (e.g., a fieldName), returns only isolates indexed under that key. Otherwise, returns all isolates in the category.
123
-
124
- #### `useHasFromRegistry(category, key?)`
125
-
126
- ```typescript
127
- useHasFromRegistry(category: string, key?: string): boolean
128
- ```
129
-
130
- Returns `true` if any isolates exist for the given criteria.
131
-
132
- #### `useRemoveFieldFromRegistry(key)`
133
-
134
- ```typescript
135
- useRemoveFieldFromRegistry(key: string): void
136
- ```
137
-
138
- Removes all entries for a specific key (e.g., fieldName) from all registry categories.
139
-
140
- #### `useClearRegistry(root)`
141
-
142
- ```typescript
143
- useClearRegistry(root: TIsolate): void
144
- ```
145
-
146
- Clears all registry indices from the root isolate. Used during tree reprocessing.
package/docs/Isolates.md DELETED
@@ -1,97 +0,0 @@
1
- # Isolates Architecture
2
-
3
- ## What is an Isolate?
4
-
5
- In the `@vestjs-runtime`, an "Isolate" is the fundamental building block of the state and execution tree. Every piece of business logic or structural grouping—whether it's a test, a suite, a group, or a focus context—is represented as an Isolate.
6
-
7
- The term "isolate" stems from its purpose: creating an isolated context where functionality runs safely. When traversing the tree of isolates, each context is maintained correctly, preventing sibling states from polluting one another.
8
-
9
- Every Isolate contains at minimum:
10
-
11
- - `Type`: A string defining the nature of the Isolate (e.g. `'Test'`, `'Suite'`, `'Group'`).
12
- - `Parent` / `Children`: Linking to construct the overall state tree structure.
13
- - `Data`: Any metadata the Isolate requires to execute properly.
14
- - `Status`: Current state of resolution (e.g. `PENDING`, `DONE`).
15
-
16
- ---
17
-
18
- ## Types of Isolates
19
-
20
- Isolates behave differently depending on their internal configurations and traits:
21
-
22
- ### 1. Stateful Isolates (Default)
23
-
24
- Most isolates created with `Isolate.create()` are stateful. This means:
25
-
26
- - They persist in the internal history tree.
27
- - They are subject to reconciliation (comparing current vs previous states between executions).
28
- - They have a predefined order in the tree matching their execution sequence, which prevents unpredictable side-effects between runs.
29
- - **Example:** A `VestTest` or a `Group`. The runtime tracks how many tests successfully ran before.
30
-
31
- ### 2. Transient Isolates
32
-
33
- Transient isolates act as "structural" or "control flow" isolates but don't hold state between runs.
34
-
35
- Characteristics of transient isolates:
36
-
37
- 1. They **do not** persist in the history tree.
38
- 2. They **are not** reconciled with previous runs.
39
- 3. They **do not** appear in the serialized suite dump (no `.dump()` presence).
40
- 4. They **do not interfere** with the indexing of siblings. Since they are transient, the runtime reconciler effectively skips them when aligning sibling child indices.
41
-
42
- These are primarily used as invisible "modifiers" that dictate logic over their children but hold no inherent measurable business-state of their own.
43
-
44
- - **Example:** `IsolateFocused` (used for `.only` and `.skip`).
45
-
46
- ---
47
-
48
- ## Focus Capabilities (`IsolateFocused`)
49
-
50
- `IsolateFocused` is a specialized implementation of a Transient Isolate used to manage the `only` and `skip` execution boundaries.
51
-
52
- Because they are transient, testing an `only()` block will not incorrectly disrupt the index sequence of test components evaluated below it. It is strictly used as an execution-context layer.
53
-
54
- ### How Focus Checking Works
55
-
56
- Focus checking uses a **two-tier approach**: explicit matching and implicit exclusion.
57
-
58
- #### 1. Explicit Focus Matching (`findClosest` — sibling-aware)
59
-
60
- `Walker.findClosest` traverses UP through ancestor levels, and at each level searches the **children** (siblings) for a matching `IsolateFocused` node. Because `only()` and `skip()` create transient isolates that are **siblings** of the tests they affect (not ancestors), the search must start from the test object itself to correctly find focus rules at the same tree level.
61
-
62
- **Important:** Starting from `useIsolate()` (the parent context) would be one level too high and would miss sibling focus isolates entirely. This is why the vest-level `useIsExcludedByField` passes the specific `testObject` to `findClosest`, while the runtime-level `useIsFocusedOut` starts from the current isolate context.
63
-
64
- If an explicit focus match is found:
65
-
66
- - **Skip-focused:** The test is excluded immediately.
67
- - **Only-focused:** The test is NOT excluded (it matched the only rule).
68
-
69
- #### 2. Implicit ONLY Identification (`hasImplicitOnly` — centralized registry)
70
-
71
- When no explicit focus match is found for a test, the runtime checks whether any ancestor scope contains an `ONLY` rule — meaning any test _not_ explicitly matched should be excluded.
72
-
73
- To keep this check **O(Depth)** performant — instead of O(N) traversing thousands of elements per check — the runtime maintains a centralized `implicitOnlyNodes` Set on the `StateRef` context. When an `IsolateFocused` node with `FocusModes.ONLY` is created, its **parent** is registered in this Set via `useSetNextIsolateChild`.
74
-
75
- When checking implicit exclusion, `hasImplicitOnly()` simply walks up the current node's ancestors and queries `implicitOnlyNodes.has(current)` at each level. This approach:
76
-
77
- - **Separates concerns:** The Isolate data model remains pure — no focus-specific flags on AST nodes.
78
- - **Is O(Depth):** Only walks the ancestor chain, not the full tree.
79
- - **Is centralized:** All implicit-only state lives on the runtime's `StateRef`, not scattered across nodes.
80
-
81
- ### Interaction with `include()`
82
-
83
- At the vest application layer, `useIsExcludedByField` combines both tiers:
84
-
85
- 1. Explicit focus match via `useClosestMatchingFocus(testObject, fieldName)`.
86
- 2. If no match, falls back to `VestRuntime.hasImplicitOnly()`.
87
- 3. If implicitly excluded, checks the `inclusion` map — `include()` rules can override implicit exclusion but **not** explicit `skip()`.
88
-
89
- ---
90
-
91
- ## The Reconciler Core
92
-
93
- All isolates (except Transient ones) pass through the underlying Reconciler matching system. The reconciler evaluates:
94
-
95
- 1. Is there an active existing node from the previous history tree in this slot?
96
- 2. If yes: Can it be reused? (Bypassing execution to save processing).
97
- 3. If no: The execution runs anew and is marked as new runtime history.
package/src/Bus.ts DELETED
@@ -1,46 +0,0 @@
1
- import { BusType, isNullish } from 'vest-utils';
2
-
3
- import { persist, useX } from './VestRuntime';
4
- import { RuntimeEvents } from './RuntimeEvents';
5
-
6
- export function useBus<
7
- E extends Record<string, any> = RuntimeEvents,
8
- >(): BusType<E> {
9
- return useX().stateRef.Bus as unknown as BusType<E>;
10
- }
11
-
12
- /*
13
- Returns an emitter, but it also has a shortcut for emitting an event immediately
14
- by passing an event name.
15
- */
16
-
17
- export function useEmit<
18
- E extends Record<string, any> = RuntimeEvents,
19
- T extends keyof E = keyof E,
20
- >(
21
- event: T,
22
- ...args: E[T] extends void ? [payload?: E[T]] : [payload: E[T]]
23
- ): void;
24
- export function useEmit<_E extends Record<string, any> = RuntimeEvents>(): (
25
- event: string,
26
- data?: any,
27
- ) => void;
28
- export function useEmit(event?: string, data?: any): any {
29
- const emit = useBus().emit;
30
-
31
- if (!isNullish(event)) {
32
- // @ts-ignore - We're allowing any event name here because the bus might be extended
33
- emit(event, data);
34
- }
35
-
36
- return persist(emit);
37
- }
38
-
39
- export function usePrepareEmitter<
40
- E extends Record<string, any> = RuntimeEvents,
41
- T extends keyof E = keyof E,
42
- >(event: T): (arg: E[T]) => void {
43
- const emit = useEmit<E>();
44
-
45
- return (arg: E[T]) => emit(event as string, arg);
46
- }
@@ -1,163 +0,0 @@
1
- import { CB, Maybe, Nullable, isNotNullish, isPromise } from 'vest-utils';
2
-
3
- import { useEmit } from '../Bus';
4
- import { Reconciler } from '../Reconciler';
5
- import * as VestRuntime from '../VestRuntime';
6
-
7
- import { IsolateKeys } from './IsolateKeys';
8
- import { IsolateMutator } from './IsolateMutator';
9
- import { IsolateStatus } from './IsolateStatus';
10
- import type { IsolateKey, IsolatePayload, TIsolate } from './IsolateTypes';
11
-
12
- export { IsolateKey, TIsolate };
13
-
14
- export class Isolate {
15
- // eslint-disable-next-line max-statements
16
- static create<Payload extends IsolatePayload>(
17
- type: string,
18
- callback: CB,
19
- payload: Maybe<Payload> = undefined,
20
- key?: IsolateKey,
21
- ): TIsolate<Payload> {
22
- const parent = VestRuntime.useIsolate();
23
-
24
- const newCreatedNode = IsolateMutator.setParent(
25
- baseIsolate(type, payload, key),
26
- parent,
27
- );
28
-
29
- const nextIsolateChild = Reconciler.reconcile(newCreatedNode);
30
-
31
- const localHistoryNode = VestRuntime.useHistoryIsolateAtCurrentPosition();
32
-
33
- const shouldRunNew = Object.is(nextIsolateChild, newCreatedNode);
34
-
35
- if (parent) {
36
- // We are within an isolate context. This means that
37
- // we need to set the new node to be the child of this parent node.
38
- VestRuntime.useSetNextIsolateChild(nextIsolateChild);
39
- }
40
-
41
- let output;
42
-
43
- if (shouldRunNew) {
44
- output = useRunAsNew(localHistoryNode, newCreatedNode, callback);
45
- } else {
46
- const emit = useEmit();
47
- output = nextIsolateChild.output;
48
- emit('ISOLATE_RECONCILED', nextIsolateChild);
49
- }
50
-
51
- IsolateMutator.saveOutput(nextIsolateChild, output);
52
-
53
- if (!parent) {
54
- // We're exiting the node, and there is no parent. This means
55
- // that we're at the top level and this node should be set
56
- // as the new root of the history tree.
57
- VestRuntime.useSetHistoryRoot(nextIsolateChild);
58
- }
59
-
60
- return nextIsolateChild as TIsolate<Payload>;
61
- }
62
-
63
- static isIsolate(node: any): node is TIsolate {
64
- return isNotNullish(node) && node[IsolateKeys.Type];
65
- }
66
- }
67
-
68
- /**
69
- * Creates a new child isolate context where the local history node is the current history node, thus advancing the history cursor.
70
- * Runs the callback function and returns its output.
71
- * @param localHistoryNode The local history node.
72
- * @param current The current isolate.
73
- * @param callback The callback function to execute.
74
- * @returns The output of the callback function.
75
- */
76
- function useRunAsNew<Callback extends CB = CB>(
77
- localHistoryNode: Nullable<TIsolate>,
78
- current: TIsolate,
79
- callback: CB,
80
- ): ReturnType<Callback> {
81
- const runtimeRoot = VestRuntime.useRuntimeRoot();
82
-
83
- // We're creating a new child isolate context where the local history node
84
- // is the current history node, thus advancing the history cursor.
85
- const output = VestRuntime.Run(
86
- {
87
- historyNode: localHistoryNode,
88
- runtimeNode: current,
89
- ...(!runtimeRoot && { runtimeRoot: current }),
90
- },
91
- () => useRunAsNewCallback(current, callback),
92
- );
93
-
94
- current.output = output;
95
- return output;
96
- }
97
-
98
- function useRunAsNewCallback(current: TIsolate, callback: CB): any {
99
- const emit = useEmit();
100
- emit('ISOLATE_ENTER', current);
101
- const output = callback(current);
102
-
103
- if (isPromise(output)) {
104
- emit('ISOLATE_PENDING', current);
105
- IsolateMutator.setPending(current);
106
- output.then(
107
- VestRuntime.persist(iso => {
108
- if (Isolate.isIsolate(iso)) {
109
- IsolateMutator.addChild(current, iso);
110
- }
111
-
112
- IsolateMutator.setDone(current);
113
- emit('ASYNC_ISOLATE_DONE', current);
114
- }),
115
- VestRuntime.persist(() => {
116
- IsolateMutator.setDone(current);
117
- emit('ASYNC_ISOLATE_DONE', current);
118
- }),
119
- );
120
- } else {
121
- IsolateMutator.setDone(current);
122
- }
123
-
124
- return output;
125
- }
126
-
127
- class IsolateInstance implements TIsolate {
128
- [IsolateKeys.Type]: string;
129
- children: Nullable<TIsolate[]> = null;
130
- [IsolateKeys.Keys]: Nullable<Record<string, TIsolate>> = null;
131
- [IsolateKeys.Parent]: Nullable<TIsolate> = null;
132
- output: any = null;
133
- key: IsolateKey = null;
134
- [IsolateKeys.AllowReorder]: Maybe<boolean> = undefined;
135
- [IsolateKeys.Transient]: Maybe<boolean> = undefined;
136
- [IsolateKeys.Status]: IsolateStatus = IsolateStatus.INITIAL;
137
- [IsolateKeys.AbortController]: Nullable<AbortController> = null;
138
- [IsolateKeys.Data]: Maybe<any>;
139
-
140
- constructor(
141
- type: string,
142
- payload: Maybe<IsolatePayload> = undefined,
143
- key: IsolateKey = null,
144
- ) {
145
- this[IsolateKeys.Type] = type;
146
- this.key = key;
147
- const { allowReorder, transient, status, ...data } = payload ?? {};
148
- this[IsolateKeys.AllowReorder] = allowReorder;
149
- this[IsolateKeys.Transient] = transient;
150
- if (status) {
151
- this[IsolateKeys.Status] = status;
152
- }
153
- this[IsolateKeys.Data] = data;
154
- }
155
- }
156
-
157
- function baseIsolate(
158
- type: string,
159
- payload: Maybe<IsolatePayload> = undefined,
160
- key: IsolateKey = null,
161
- ): TIsolate {
162
- return new IsolateInstance(type, payload, key);
163
- }
@@ -1,93 +0,0 @@
1
- import {
2
- asArray,
3
- Maybe,
4
- Nullish,
5
- OneOrMoreOf,
6
- noop,
7
- isNotEmpty,
8
- isStringValue,
9
- } from 'vest-utils';
10
-
11
- import { IsolateTransient } from './IsolateTransient';
12
- import { TIsolate } from './Isolate';
13
- import { IsolateKeys } from './IsolateKeys';
14
-
15
- export const VestIsolateTypeFocused = 'Focused';
16
-
17
- export enum FocusModes {
18
- SKIP = 'skip',
19
- ONLY = 'only',
20
- }
21
-
22
- export type FocusMatchExclusion = Maybe<OneOrMoreOf<string>>;
23
-
24
- export type TIsolateFocused = TIsolate<IsolateFocusedPayload>;
25
-
26
- type IsolateFocusedPayload = {
27
- focusMode: FocusModes;
28
- match: FocusMatchExclusion;
29
- matchAll: boolean;
30
- };
31
-
32
- /**
33
- * Creates a focused isolate.
34
- * Focused isolates are transient because they only affect the current run
35
- * and do not need to be preserved in history or appearing in the suite result.
36
- */
37
- export function IsolateFocused(
38
- focusMode: FocusModes,
39
- match?: true | FocusMatchExclusion,
40
- ): TIsolateFocused | undefined {
41
- const matchedFields = asArray(match).filter(isStringValue).filter(isNotEmpty);
42
-
43
- const matchAll = match === true;
44
-
45
- // If there are no fields to match and matchAll is false,
46
- // skip creating the isolate entirely — it would be a no-op.
47
- if (!isNotEmpty(matchedFields) && !matchAll) {
48
- return undefined;
49
- }
50
-
51
- return IsolateTransient<IsolateFocusedPayload>(noop, VestIsolateTypeFocused, {
52
- focusMode,
53
- match: matchedFields,
54
- matchAll,
55
- });
56
- }
57
-
58
- export class FocusSelectors {
59
- static isSkipFocused(
60
- focus: Nullish<TIsolateFocused>,
61
- fieldName?: string,
62
- ): boolean {
63
- if (!focus) return false;
64
- const data = focus.data;
65
- if (!data || data.focusMode !== FocusModes.SKIP) return false;
66
- if (data.matchAll) return true;
67
- return hasFocus(focus, fieldName);
68
- }
69
- static isOnlyFocused(
70
- focus: Nullish<TIsolateFocused>,
71
- fieldName?: string,
72
- ): boolean {
73
- if (!focus) return false;
74
- const data = focus.data;
75
- if (!data || data.focusMode !== FocusModes.ONLY) return false;
76
- if (data.matchAll) return true;
77
- return hasFocus(focus, fieldName);
78
- }
79
-
80
- static isIsolateFocused(isolate: TIsolate): isolate is TIsolateFocused {
81
- return isolate[IsolateKeys.Type] === VestIsolateTypeFocused;
82
- }
83
- }
84
-
85
- function hasFocus(
86
- focus: Nullish<TIsolateFocused>,
87
- fieldName?: string,
88
- ): boolean {
89
- const match = asArray(focus?.data?.match);
90
- if (!match.length) return false;
91
-
92
- return !fieldName || match.includes(fieldName as string);
93
- }
@@ -1,42 +0,0 @@
1
- import { isNullish, Nullable } from 'vest-utils';
2
-
3
- import { TIsolate } from './IsolateTypes';
4
-
5
- export function createHistoryIndex(
6
- children: Nullable<TIsolate[]>,
7
- ): Map<string, TIsolate | TIsolate[]> {
8
- const index = new Map<string, TIsolate | TIsolate[]>();
9
-
10
- if (!children) {
11
- return index;
12
- }
13
-
14
- for (const child of children) {
15
- addToIndex(index, child);
16
- }
17
-
18
- return index;
19
- }
20
-
21
- function addToIndex(
22
- index: Map<string, TIsolate | TIsolate[]>,
23
- isolate: TIsolate,
24
- ): void {
25
- const { key } = isolate;
26
-
27
- if (isNullish(key)) {
28
- return;
29
- }
30
-
31
- const existing = index.get(key);
32
-
33
- if (existing) {
34
- if (Array.isArray(existing)) {
35
- existing.push(isolate);
36
- } else {
37
- index.set(key, [existing, isolate]);
38
- }
39
- } else {
40
- index.set(key, isolate);
41
- }
42
- }
@@ -1,93 +0,0 @@
1
- import { Nullable, isNotNullish, isNullish } from 'vest-utils';
2
-
3
- import { TIsolate } from './Isolate';
4
- import { IsolateStatus } from './IsolateStatus';
5
-
6
- export class IsolateInspector {
7
- static at(isolate: Nullable<TIsolate>, at: number): Nullable<TIsolate> {
8
- if (isNullish(isolate)) {
9
- return null;
10
- }
11
- return isolate.children?.[at] ?? null;
12
- }
13
-
14
- static cursor(isolate: Nullable<TIsolate>): number {
15
- if (isNullish(isolate)) {
16
- return 0;
17
- }
18
- return isolate.children?.length ?? 0;
19
- }
20
-
21
- static canReorder<I extends TIsolate>(isolate: Nullable<I>): boolean {
22
- if (isNullish(isolate)) {
23
- return false;
24
- }
25
-
26
- return IsolateInspector.allowsReorder(isolate.parent);
27
- }
28
-
29
- static allowsReorder<I extends Record<any, any>>(
30
- isolate: Nullable<I>,
31
- ): boolean {
32
- return isolate?.allowReorder === true;
33
- }
34
-
35
- static usesKey(isolate: Nullable<TIsolate>): boolean {
36
- if (isNullish(isolate)) {
37
- return false;
38
- }
39
- return isNotNullish(isolate.key);
40
- }
41
-
42
- static getChildByKey(
43
- isolate: Nullable<TIsolate>,
44
- key: string,
45
- ): Nullable<TIsolate> {
46
- if (isNullish(isolate)) {
47
- return null;
48
- }
49
- return isolate.keys?.[key] ?? null;
50
- }
51
-
52
- static getStatus(isolate: Nullable<TIsolate>): IsolateStatus {
53
- if (isNullish(isolate)) {
54
- return IsolateStatus.INITIAL;
55
- }
56
- return isolate.status ?? IsolateStatus.INITIAL;
57
- }
58
-
59
- static statusEquals(
60
- isolate: Nullable<TIsolate>,
61
- status: IsolateStatus,
62
- ): boolean {
63
- return IsolateInspector.getStatus(isolate) === status;
64
- }
65
-
66
- static isPending(isolate: Nullable<TIsolate>): boolean {
67
- return IsolateInspector.statusEquals(isolate, IsolateStatus.PENDING);
68
- }
69
-
70
- static isHasPending(isolate: Nullable<TIsolate>): boolean {
71
- return IsolateInspector.statusEquals(isolate, IsolateStatus.HAS_PENDING);
72
- }
73
-
74
- static hasPending(isolate: Nullable<TIsolate>): boolean {
75
- return (
76
- IsolateInspector.isPending(isolate) ||
77
- IsolateInspector.isHasPending(isolate)
78
- );
79
- }
80
-
81
- static hasActiveChildren(isolate: Nullable<TIsolate>): boolean {
82
- if (isNullish(isolate) || isNullish(isolate.children)) {
83
- return false;
84
- }
85
-
86
- // Check if ANY immediate child is currently PENDING or HAS_PENDING
87
- return isolate.children.some(child => IsolateInspector.hasPending(child));
88
- }
89
-
90
- static getParent(isolate: Nullable<TIsolate>): Nullable<TIsolate> {
91
- return isolate?.parent ?? null;
92
- }
93
- }
@@ -1,18 +0,0 @@
1
- export enum IsolateKeys {
2
- Type = '$type',
3
- Keys = 'keys',
4
- Key = 'key',
5
- Parent = 'parent',
6
- Data = 'data',
7
- AllowReorder = 'allowReorder',
8
- Transient = 'transient',
9
- Status = 'status',
10
- AbortController = 'abortController',
11
- Children = 'children',
12
- }
13
-
14
- export const ExcludedFromDump = new Set([
15
- IsolateKeys.AbortController,
16
- IsolateKeys.Parent,
17
- IsolateKeys.Keys,
18
- ]);