@shivaedev/test 0.9.0 → 0.10.1

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 (3) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +59 -61
  3. package/package.json +6 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.10.1 - 2026-10-30
4
+
5
+ ### Changed
6
+
7
+ - Require Effect 4.0.6 and align the Effect companion package peers and generated starter catalog to the same exact release. Install the 4.0.6 Effect stack together.
8
+
9
+ ### Fixed
10
+
11
+ - Register hosted RPC cache-control handling through Effect 4.0.6's request-scoped pre-response API so responses keep `private, no-store`.
12
+ - Accept native RPC's `application/x-ndjson` media type while preserving `application/ndjson` callers at the transport guard.
13
+ - Keep subscriptions open after their HTTP request body completes; closing the socket still cancels them.
14
+
15
+ ## 0.10.0 - 2026-10-27
16
+
17
+ ### Breaking
18
+
19
+ - Rename `@shivaedev/effect-test` to `@shivaedev/test`. Change dependency declarations and imports from `@shivaedev/effect-test/...` to `@shivaedev/test/...`; module paths inside the package are unchanged.
20
+
3
21
  ## 0.9.0 - 2026-10-25
4
22
 
5
23
  ### Added
package/README.md CHANGED
@@ -13,9 +13,9 @@ import * as TestClock from "effect/testing/TestClock";
13
13
  import { it } from "@shivaedev/test/it.ts";
14
14
 
15
15
  it.effect("advances the test's time", function* () {
16
- expect(yield* Clock.currentTimeMillis).toBe(0);
17
- yield* TestClock.adjust("1 second");
18
- expect(yield* Clock.currentTimeMillis).toBe(1000);
16
+ expect(yield* Clock.currentTimeMillis).toBe(0);
17
+ yield* TestClock.adjust("1 second");
18
+ expect(yield* Clock.currentTimeMillis).toBe(1000);
19
19
  });
20
20
  ```
21
21
 
@@ -32,7 +32,6 @@ A **Layer** describes how to acquire the fixture's services. `makeEffectIt` buil
32
32
  Keep the two lifetimes separate. The file owns the acquired Layer and its scope; its finalizers finish before Vitest reports the file complete. Each test makes its harness and runs its body in a separate scope. Harnesses, wrappers, generator bodies and `each` bodies may acquire resources through that Scope; their finalizers run on success, failure and interruption. Scope is available per test and is not added to the file Context. A shared service can retain mutable state across tests; a fresh harness alone does not reset it. Put application-state isolation in the fixture that owns that state, such as a database transaction wrapper.
33
33
 
34
34
  The work usually splits into two parts:
35
-
36
35
  1. Once per fixture, define its Layer, `makeHarness` and, when needed, an `around` wrapper in test support.
37
36
  2. In every test, write the generator and the assertions. For a test with no reusable fixture, use `it.effect` directly.
38
37
 
@@ -46,19 +45,20 @@ import { makeEffectIt } from "@shivaedev/test/makeEffectIt.ts";
46
45
  class Token extends Context.Service<Token, string>()("test/Token") {}
47
46
 
48
47
  const { effectApp } = makeEffectIt({
49
- layer: Layer.succeed(Token, "from-layer"),
50
- makeHarness: (context) => Effect.gen(function* () {
51
- return {
52
- name: context.task.name,
53
- token: yield* Token,
54
- };
55
- }),
48
+ layer: Layer.succeed(Token, "from-layer"),
49
+ makeHarness: (context) =>
50
+ Effect.gen(function* () {
51
+ return {
52
+ name: context.task.name,
53
+ token: yield* Token,
54
+ };
55
+ }),
56
56
  });
57
57
 
58
58
  effectApp("reads the fixture's services", function* (harness, context) {
59
- expect(harness.token).toBe("from-layer");
60
- expect(harness.name).toBe(context.task.name);
61
- expect(yield* Token).toBe("from-layer");
59
+ expect(harness.token).toBe("from-layer");
60
+ expect(harness.name).toBe(context.task.name);
61
+ expect(yield* Token).toBe("from-layer");
62
62
  });
63
63
  ```
64
64
 
@@ -72,12 +72,12 @@ import { Effect, Layer } from "effect";
72
72
  import { makeEffectIt } from "@shivaedev/test/makeEffectIt.ts";
73
73
 
74
74
  const { effectApp } = makeEffectIt({
75
- layer: Layer.empty,
76
- makeHarness: () => Effect.succeed({ shelf: 0 }),
75
+ layer: Layer.empty,
76
+ makeHarness: () => Effect.succeed({ shelf: 0 }),
77
77
  });
78
78
 
79
79
  effectApp("uses a harness without services", function* (harness) {
80
- expect(harness.shelf).toBe(0);
80
+ expect(harness.shelf).toBe(0);
81
81
  });
82
82
  ```
83
83
 
@@ -95,13 +95,13 @@ import { makeEffectIt } from "@shivaedev/test/makeEffectIt.ts";
95
95
  class Token extends Context.Service<Token, string>()("test/Token") {}
96
96
 
97
97
  const { effectApp } = makeEffectIt({
98
- around: (effect) => Effect.withSpan(effect, "test.token"),
99
- layer: Layer.succeed(Token, "from-layer"),
100
- makeHarness: () => Effect.service(Token),
98
+ around: (effect) => Effect.withSpan(effect, "test.token"),
99
+ layer: Layer.succeed(Token, "from-layer"),
100
+ makeHarness: () => Effect.service(Token),
101
101
  });
102
102
 
103
103
  effectApp("reads the harness", function* (token) {
104
- expect(token).toBe("from-layer");
104
+ expect(token).toBe("from-layer");
105
105
  });
106
106
  ```
107
107
 
@@ -117,16 +117,14 @@ import { Effect } from "effect";
117
117
  import { it } from "@shivaedev/test/it.ts";
118
118
 
119
119
  it.effect("reads an Effect result", function* ({ task }) {
120
- expect(task.name).toBe("reads an Effect result");
121
- expect(yield* Effect.succeed(3)).toBe(3);
120
+ expect(task.name).toBe("reads an Effect result");
121
+ expect(yield* Effect.succeed(3)).toBe(3);
122
122
  });
123
123
 
124
- it.effect("accepts an Effect-returning body", () =>
125
- Effect.sync(() => expect(3).toBe(3)),
126
- );
124
+ it.effect("accepts an Effect-returning body", () => Effect.sync(() => expect(3).toBe(3)));
127
125
 
128
126
  it.effect.each([1, 2])("passes the case %s", function* (count) {
129
- expect(yield* Effect.succeed(count)).toBe(count);
127
+ expect(yield* Effect.succeed(count)).toBe(count);
130
128
  });
131
129
  ```
132
130
 
@@ -136,8 +134,8 @@ For a fixture's `effectApp.each`, the arguments are the case, harness and contex
136
134
 
137
135
  ```ts
138
136
  effectApp.each(["alpha", "beta"])("checks %s", function* (item, harness, context) {
139
- expect(context.task.name).toContain(item);
140
- expect(harness.token).toBe("from-layer");
137
+ expect(context.task.name).toContain(item);
138
+ expect(harness.token).toBe("from-layer");
141
139
  });
142
140
  ```
143
141
 
@@ -148,9 +146,13 @@ This uses the token fixture from the first example. Case and harness types are i
148
146
  `it.effect` uses TestClock; `it.live` uses live time. A fixture defaults to `clock: "test"`. Its factory may choose `clock: "live"`, and a single test may override the factory in either direction:
149
147
 
150
148
  ```ts
151
- effectApp("reads live time", function* () {
152
- expect(yield* Clock.currentTimeMillis).toBeGreaterThan(1_000_000);
153
- }, { clock: "live" });
149
+ effectApp(
150
+ "reads live time",
151
+ function* () {
152
+ expect(yield* Clock.currentTimeMillis).toBeGreaterThan(1_000_000);
153
+ },
154
+ { clock: "live" },
155
+ );
154
156
  ```
155
157
 
156
158
  The override changes the per-test environment used by the wrapper, harness and body. A UTC `DateTime` also works as `clock`: it selects native TestClock and sets its initial instant before the wrapper or harness runs. `TestClock.adjust` advances that clock explicitly. For example, `{ clock: DateTime.makeUnsafe("2026-10-17T12:34:56.000Z") }` pins time to that UTC instant; import `DateTime` from `effect`. The same option is available on a fixture's factory.
@@ -169,15 +171,15 @@ Both test and live clock fixtures install a fresh native TestConsole for each te
169
171
  import { eventually } from "@shivaedev/test/eventually.ts";
170
172
 
171
173
  effectApp("reaches the selected time", function* () {
172
- const readyAt = yield* eventually(
173
- Effect.gen(function* () {
174
- const now = yield* Clock.currentTimeMillis;
175
- return now >= 50 ? now : yield* Effect.fail("too-early");
176
- }),
177
- { interval: "7 millis", times: 8 },
178
- );
179
-
180
- expect(readyAt).toBe(56);
174
+ const readyAt = yield* eventually(
175
+ Effect.gen(function* () {
176
+ const now = yield* Clock.currentTimeMillis;
177
+ return now >= 50 ? now : yield* Effect.fail("too-early");
178
+ }),
179
+ { interval: "7 millis", times: 8 },
180
+ );
181
+
182
+ expect(readyAt).toBe(56);
181
183
  });
182
184
  ```
183
185
 
@@ -187,17 +189,17 @@ A thrown assertion is a defect, so it escapes on the first attempt. Interruption
187
189
 
188
190
  ```ts
189
191
  effectApp("retries a readiness assertion", function* () {
190
- let attempts = 0;
192
+ let attempts = 0;
191
193
 
192
- yield* eventually(
193
- Effect.try(() => {
194
- attempts += 1;
195
- expect(attempts).toBe(3);
196
- }),
197
- { interval: "1 millis", times: 3 },
198
- );
194
+ yield* eventually(
195
+ Effect.try(() => {
196
+ attempts += 1;
197
+ expect(attempts).toBe(3);
198
+ }),
199
+ { interval: "1 millis", times: 3 },
200
+ );
199
201
 
200
- expect(attempts).toBe(3);
202
+ expect(attempts).toBe(3);
201
203
  });
202
204
  ```
203
205
 
@@ -219,25 +221,21 @@ The entire wrapped Effect, including readiness checks and retry delays, uses liv
219
221
  ### API
220
222
 
221
223
  Import the module that defines the name; the package has no root entry.
222
-
223
- | Module | Exports |
224
- | --- | --- |
225
- | `@shivaedev/test/it.ts` | `it`, with generator-aware `effect` and `live` testers. |
226
- | `@shivaedev/test/makeEffectIt.ts` | `makeEffectIt({ layer, makeHarness, around?, clock? })`, returning `{ effectApp }`. |
227
- | `@shivaedev/test/eventually.ts` | `eventually(effect, options?)` and `EventuallyOptions`. |
228
- | `@shivaedev/test/withLive.ts` | `withLive(effect)`, a live-time region under either clock. |
229
- | `@shivaedev/test/anyTestLayer.ts` | `AnyTestLayer` and `buildTestLayer(layer, scope)`, an Effect that builds the Layer's service Context in the supplied scope. |
230
- | `@shivaedev/test/types.ts` | `EffectClock`, `EffectTestOptions`, `EffectTestBody`, `EffectTest`, `EffectTester`, `MakeEffectItOptions`, `MakeEffectItResult`, `EffectItTest`, `EffectItTester` and `EffectIt`. |
224
+ - `@shivaedev/test/it.ts`: `it`, with generator-aware `effect` and `live` testers.
225
+ - `@shivaedev/test/makeEffectIt.ts`: `makeEffectIt({ layer, makeHarness, around?, clock? })`, returning `{ effectApp }`.
226
+ - `@shivaedev/test/eventually.ts`: `eventually(effect, options?)` and `EventuallyOptions`.
227
+ - `@shivaedev/test/withLive.ts`: `withLive(effect)`, a live-time region under either clock.
228
+ - `@shivaedev/test/anyTestLayer.ts`: `AnyTestLayer` and `buildTestLayer(layer, scope)`, an Effect that builds the Layer's service Context in the supplied scope.
229
+ - `@shivaedev/test/types.ts`: `EffectClock`, `EffectTestOptions`, `EffectTestBody`, `EffectTest`, `EffectTester`, `MakeEffectItOptions`, `MakeEffectItResult`, `EffectItTest`, `EffectItTester` and `EffectIt`.
231
230
 
232
231
  `eventually` waits `"10 millis"` between attempts by default; omitting `times` leaves retries unbounded.
233
232
 
234
233
  ### Install, setup and limits
235
234
 
236
235
  ```sh
237
- pnpm add --save-dev @shivaedev/test @effect/vitest@4.0.5 effect@4.0.5 vitest@5.0.3
236
+ pnpm add --save-dev @shivaedev/test @effect/vitest effect vitest
238
237
  ```
239
238
 
240
239
  The package declares `effect` as a peer and `@effect/vitest` as an optional peer. The Vitest runner modules require `@effect/vitest`; install it when using them. The package requires Node.js 24 or later. Use peer versions compatible with the package's `package.json`.
241
-
242
240
  - Define a fixture tester in test support and import it from Vitest tests. Calling `makeEffectIt` declares a Vitest fixture; it is not an application runtime factory.
243
241
  - An unbounded readiness poll can run until Vitest ends the test. Set a retry budget when the condition may never become true.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shivaedev/test",
3
- "version": "0.9.0",
3
+ "version": "0.10.1",
4
4
  "description": "Generic Effect Vitest runner with file-scoped Layers and TestClock",
5
5
  "keywords": [
6
6
  "effect"
@@ -44,16 +44,16 @@
44
44
  "README.md"
45
45
  ],
46
46
  "devDependencies": {
47
- "@effect/vitest": "4.0.5",
48
- "@shivaedev/quality": "0.41.1",
47
+ "@effect/vitest": "4.0.6",
48
+ "@shivaedev/quality": "0.45.0",
49
49
  "@types/node": "24.19.2",
50
- "effect": "4.0.5",
50
+ "effect": "4.0.6",
51
51
  "typescript": "7.0.2",
52
52
  "vitest": "5.0.5"
53
53
  },
54
54
  "peerDependencies": {
55
- "@effect/vitest": "4.0.5",
56
- "effect": "4.0.5"
55
+ "@effect/vitest": "4.0.6",
56
+ "effect": "4.0.6"
57
57
  },
58
58
  "peerDependenciesMeta": {
59
59
  "@effect/vitest": {