@shivaedev/test-sql 0.10.2 → 0.12.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.
- package/CHANGELOG.md +36 -0
- package/README.md +84 -68
- package/dist/server/internal/databaseOwner.d.ts +3 -1
- package/dist/server/internal/databaseOwner.d.ts.map +1 -1
- package/dist/server/internal/databaseOwner.js +3 -3
- package/dist/server/internal/databaseOwner.js.map +1 -1
- package/dist/server/internal/databases.js +1 -1
- package/dist/server/internal/databases.js.map +1 -1
- package/dist/server/migrationSteps.d.ts +3 -3
- package/dist/server/migrationSteps.d.ts.map +1 -1
- package/dist/server/migrationSteps.js.map +1 -1
- package/dist/server/migrationUpgrade.d.ts +3 -3
- package/dist/server/migrationUpgrade.d.ts.map +1 -1
- package/dist/server/migrationUpgrade.js.map +1 -1
- package/dist/server/postgres.d.ts +2 -2
- package/dist/server/postgres.d.ts.map +1 -1
- package/dist/server/raceWithLock.d.ts +9 -0
- package/dist/server/raceWithLock.d.ts.map +1 -0
- package/dist/server/raceWithLock.js +28 -0
- package/dist/server/raceWithLock.js.map +1 -0
- package/package.json +13 -12
- package/src/server/internal/databaseOwner.ts +6 -3
- package/src/server/internal/databases.ts +1 -1
- package/src/server/migrationSteps.ts +1 -1
- package/src/server/migrationUpgrade.ts +3 -3
- package/src/server/postgres.ts +1 -1
- package/src/server/raceWithLock.ts +40 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.12.0 - 2026-10-30
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Export every engine module through the package's existing `.ts` wildcard, including modules under `internal/` in `test` and `vitest-rerun` and `server/internal/` in `test-story` and `test-sql`, so downstream kits can reuse them directly. Importing the rerun worker leaves test execution to its direct launch.
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Generate disposable PostgreSQL database names through Effect's Random service.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- SQL decodes migration ledger, release ownership and change-coverage rows before using them. Malformed rows fail with `SchemaError` instead of being treated as current, pending or a different owner.
|
|
16
|
+
- SQL generates worker identities and change-listener heartbeat tokens through Effect's `Random` service.
|
|
17
|
+
|
|
18
|
+
### Breaking
|
|
19
|
+
|
|
20
|
+
- Schema component failures now reach SQL migration and readiness callers, server startup and test-sql migration helpers with their original error, tag and cause, and log the component name. Handle the component's own error instead of a wrapping `MigrationError` with kind `Failed`. When annotating migration options with fallible components, supply their error type as the second generic parameter; the default describes components that cannot fail.
|
|
21
|
+
- Declare `WorkerRelease` through `defineService`. Consumers using the old class as a type must use `Context.Service.Identifier<typeof WorkerRelease>`; yielding the service and providing its value stay the same.
|
|
22
|
+
|
|
23
|
+
## 0.11.0 - 2026-10-30
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- `raceWithLock` holds a native PostgreSQL row, table or advisory lock until every competing call is queued, then releases the holder and returns each call's result or typed failure in input order.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- 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.
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- Register hosted RPC cache-control handling through Effect 4.0.6's request-scoped pre-response API so responses keep `private, no-store`.
|
|
36
|
+
- Accept native RPC's `application/x-ndjson` media type while preserving `application/ndjson` callers at the transport guard.
|
|
37
|
+
- Keep subscriptions open after their HTTP request body completes; closing the socket still cancels them.
|
|
38
|
+
|
|
3
39
|
## 0.10.2 - 2026-10-28
|
|
4
40
|
|
|
5
41
|
### Changed
|
package/README.md
CHANGED
|
@@ -8,11 +8,9 @@ Table cleanup couples tests to the order they run. A rollback test keeps its wri
|
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
10
|
effectApp("renames an item", function* () {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
{ name: "Renamed" },
|
|
15
|
-
]);
|
|
11
|
+
const sql = yield* SqlClient.SqlClient;
|
|
12
|
+
yield* sql`update items set name = 'Renamed' where id = 1`;
|
|
13
|
+
expect(yield* sql`select name from items where id = 1`).toEqual([{ name: "Renamed" }]);
|
|
16
14
|
});
|
|
17
15
|
```
|
|
18
16
|
|
|
@@ -37,27 +35,27 @@ import { makePostgresTestLayer } from "@shivaedev/test-sql/server/postgres.ts";
|
|
|
37
35
|
import { withTestTransaction } from "@shivaedev/test-sql/server/withTestTransaction.ts";
|
|
38
36
|
|
|
39
37
|
const layer = makePostgresTestLayer({
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
38
|
+
url: Redacted.make(Effect.runSync(Config.String("TEST_DATABASE_URL"))),
|
|
39
|
+
migrations: {
|
|
40
|
+
lockTimeout: "5 seconds",
|
|
41
|
+
loader: Migrator.fromRecord({
|
|
42
|
+
"1_create_items": Effect.gen(function* () {
|
|
43
|
+
const sql = yield* SqlClient.SqlClient;
|
|
44
|
+
yield* sql`create table items (id integer primary key, name text not null)`;
|
|
45
|
+
}),
|
|
46
|
+
}),
|
|
47
|
+
},
|
|
48
|
+
fixtures: Effect.gen(function* () {
|
|
49
|
+
const sql = yield* SqlClient.SqlClient;
|
|
50
|
+
yield* sql`insert into items values (1, 'Original')`;
|
|
51
|
+
}),
|
|
54
52
|
});
|
|
55
53
|
|
|
56
54
|
export const { effectApp } = makeEffectIt({
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
layer,
|
|
56
|
+
clock: "live",
|
|
57
|
+
around: withTestTransaction,
|
|
58
|
+
makeHarness: () => Effect.void,
|
|
61
59
|
});
|
|
62
60
|
```
|
|
63
61
|
|
|
@@ -69,13 +67,14 @@ Fixtures can also be a `PostgresTestLoader`: a callback from the migrated templa
|
|
|
69
67
|
import * as PgClient from "@effect/sql-pg/PgClient";
|
|
70
68
|
|
|
71
69
|
const layer = makePostgresTestLayer({
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
70
|
+
url,
|
|
71
|
+
migrations: appMigrations,
|
|
72
|
+
fixtures: (templateUrl) =>
|
|
73
|
+
Effect.gen(function* () {
|
|
74
|
+
const client = yield* PgClient.makeClient({ url: templateUrl });
|
|
75
|
+
yield* client`create table app_fixture (name text not null)`;
|
|
76
|
+
yield* client`insert into app_fixture values ('Original')`;
|
|
77
|
+
}),
|
|
79
78
|
});
|
|
80
79
|
```
|
|
81
80
|
|
|
@@ -85,8 +84,8 @@ Supply `initialize` instead of `migrations` when the application already owns it
|
|
|
85
84
|
|
|
86
85
|
```ts
|
|
87
86
|
const layer = makePostgresTestLayer({
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
url: testDatabaseUrl,
|
|
88
|
+
initialize: (templateUrl) => migrateDatabase(templateUrl),
|
|
90
89
|
});
|
|
91
90
|
```
|
|
92
91
|
|
|
@@ -98,9 +97,9 @@ Choose exactly one of `migrations` and `initialize`. The initializer is an Effec
|
|
|
98
97
|
|
|
99
98
|
```ts
|
|
100
99
|
const layer = makePostgresTestLayer({
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
...appClientOptions,
|
|
101
|
+
url: testDatabaseUrl,
|
|
102
|
+
migrations: appMigrations,
|
|
104
103
|
});
|
|
105
104
|
```
|
|
106
105
|
|
|
@@ -108,13 +107,13 @@ Declare the mapping once in `appClientOptions`, for example with `clientOptions(
|
|
|
108
107
|
|
|
109
108
|
### Budget PostgreSQL connections
|
|
110
109
|
|
|
111
|
-
Each database pool opens connections on demand, with a default maximum of
|
|
110
|
+
Each database pool opens connections on demand, with a default maximum of two: the shared database can use two, and each active per-test or per-story clone can use another two. Preparation uses the same limit and closes before tests start. An administrative connection exists only during a create or drop operation and closes as soon as that operation finishes; idle workers keep no administrative connection. Set `maxConnections` on `makePostgresTestLayer` to override the limit for the suite's preparation, shared and isolated pools:
|
|
112
111
|
|
|
113
112
|
```ts
|
|
114
113
|
const layer = makePostgresTestLayer({
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
url,
|
|
115
|
+
migrations,
|
|
116
|
+
maxConnections: 3,
|
|
118
117
|
});
|
|
119
118
|
```
|
|
120
119
|
|
|
@@ -142,10 +141,12 @@ An isolated block can live inside the rollback tester above:
|
|
|
142
141
|
import { withIsolatedDatabase } from "@shivaedev/test-sql/server/withIsolatedDatabase.ts";
|
|
143
142
|
|
|
144
143
|
effectApp("commits in its own database", function* () {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
144
|
+
yield* withIsolatedDatabase(
|
|
145
|
+
Effect.gen(function* () {
|
|
146
|
+
const sql = yield* SqlClient.SqlClient;
|
|
147
|
+
yield* sql`update items set name = 'Committed' where id = 1`;
|
|
148
|
+
}),
|
|
149
|
+
);
|
|
149
150
|
});
|
|
150
151
|
```
|
|
151
152
|
|
|
@@ -153,6 +154,23 @@ The clone starts from the template, including the original fixtures, even if the
|
|
|
153
154
|
|
|
154
155
|
Inside an isolated block, `PostgresTestDatabase.url` names that clone. Use it to acquire additional native PostgreSQL connections in the block's scope. Native SQL services are replaced in the block's Context; application services acquired earlier retain the client they captured. Build application service Layers inside the isolated block when they acquire SQL clients; use `Layer.fresh` when reusing a Layer definition already acquired by the worker. Keep servers and background fibers in that scope so they finish before the database is dropped. Separate databases isolate database contents; external services and server capacity need separate planning.
|
|
155
156
|
|
|
157
|
+
### Race stories
|
|
158
|
+
|
|
159
|
+
Use `raceWithLock` with a live story clock inside an isolated database, outside a wrapping transaction. Supply the native SQL Effect that takes a row, table or transaction advisory lock, and one Effect per competing call. The helper holds the lock in a separate transaction, starts the calls, and waits for PostgreSQL's blocker graph to contain every contender before releasing the holder. Keep other lock-taking work out of that database during the race, and allow at least `effects.length + 1` pool connections. Contenders keep their own transaction and service conventions.
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
import { raceWithLock } from "@shivaedev/test-sql/server/raceWithLock.ts";
|
|
163
|
+
|
|
164
|
+
const results = yield* raceWithLock({
|
|
165
|
+
lock: sql`lock table items in share mode`,
|
|
166
|
+
effects: [claimLastPlace(), claimLastPlace(), claimLastPlace()],
|
|
167
|
+
});
|
|
168
|
+
expect(results.filter(Result.isSuccess)).toHaveLength(1);
|
|
169
|
+
expect(results.filter(Result.isFailure).map((result) => result.failure)).toEqual(["full", "full"]);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`claimLastPlace` is the application's guarded write, failing with the typed `"full"` value when no place remains; `Result` is Effect's native `Result` module. Results stay in input order, including each call's typed failure. The holder rolls back by default, discarding any writes in `lock`; set `release: "commit"` when a contender must observe the holder's committed write. The default timeout is five seconds; `timeout` accepts an Effect duration. Defects and interruption retain their native failure kind, and the helper's scope owns the holder and competing fibers.
|
|
173
|
+
|
|
156
174
|
### Choose an empty database per story or trait
|
|
157
175
|
|
|
158
176
|
The default clone is migrated. A first-deploy or upgrade block can select the empty template from the same Layer:
|
|
@@ -180,26 +198,24 @@ import { withIsolatedDatabase } from "@shivaedev/test-sql/server/withIsolatedDat
|
|
|
180
198
|
import { appMigrations } from "./migrations.ts";
|
|
181
199
|
|
|
182
200
|
const migrationLayer = makePostgresTestLayer({
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
201
|
+
url,
|
|
202
|
+
migrations: appMigrations,
|
|
203
|
+
prepare: false,
|
|
186
204
|
});
|
|
187
205
|
|
|
188
206
|
const { effectApp: migrationTest } = makeEffectIt({
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
207
|
+
layer: migrationLayer,
|
|
208
|
+
clock: "live",
|
|
209
|
+
around: withIsolatedDatabase,
|
|
210
|
+
makeHarness: () => migrationSteps(appMigrations),
|
|
193
211
|
});
|
|
194
212
|
|
|
195
213
|
migrationTest("preserves contact emails in the new table", function* (steps) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
{ contact_id: 1, address: "alice@example.test" },
|
|
202
|
-
]);
|
|
214
|
+
yield* steps.migrateTo("001_create_contacts");
|
|
215
|
+
const sql = yield* SqlClient.SqlClient;
|
|
216
|
+
yield* sql`insert into contacts (id, email) values (1, 'alice@example.test')`;
|
|
217
|
+
yield* steps.migrateToLatest;
|
|
218
|
+
expect(yield* sql`select contact_id, address from emails`).toEqual([{ contact_id: 1, address: "alice@example.test" }]);
|
|
203
219
|
});
|
|
204
220
|
```
|
|
205
221
|
|
|
@@ -235,17 +251,17 @@ The assertion uses the SQL `Model`'s selected database fields, including generat
|
|
|
235
251
|
|
|
236
252
|
### API
|
|
237
253
|
|
|
238
|
-
Import each name from the module that defines it. The package has no root entry.
|
|
254
|
+
Import each name from the module that defines it. The package has no root entry. Every source module is available through its `.ts` subpath, including modules under `server/internal/`.
|
|
239
255
|
|
|
240
|
-
| Module
|
|
241
|
-
|
|
|
242
|
-
| `@shivaedev/test-sql/server/postgres.ts`
|
|
243
|
-
| `@shivaedev/test-sql/server/withTestTransaction.ts`
|
|
244
|
-
| `@shivaedev/test-sql/server/withIsolatedDatabase.ts` | `withIsolatedDatabase(effect, { template? }?)`
|
|
245
|
-
| `@shivaedev/test-sql/server/database.ts`
|
|
246
|
-
| `@shivaedev/test-sql/server/migrationSteps.ts`
|
|
247
|
-
| `@shivaedev/test-sql/server/migrationUpgrade.ts`
|
|
248
|
-
| `@shivaedev/test-sql/server/assertModelColumns.ts`
|
|
256
|
+
| Module | Exports |
|
|
257
|
+
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
258
|
+
| `@shivaedev/test-sql/server/postgres.ts` | `makePostgresTestLayer({ url, migrations?, initialize?, fixtures?, maxConnections?, prepare?, transformQueryNames?, transformResultNames? })`, `PostgresTestOptions`, `PostgresTestLoader` |
|
|
259
|
+
| `@shivaedev/test-sql/server/withTestTransaction.ts` | `withTestTransaction(effect)` |
|
|
260
|
+
| `@shivaedev/test-sql/server/withIsolatedDatabase.ts` | `withIsolatedDatabase(effect, { template? }?)` |
|
|
261
|
+
| `@shivaedev/test-sql/server/database.ts` | `PostgresTestDatabase`, `PostgresTestDatabaseService`, `PostgresTestServices`, `PostgresTestTemplate` |
|
|
262
|
+
| `@shivaedev/test-sql/server/migrationSteps.ts` | `migrationSteps(appMigrations)` returning `{ migrateTo(name), migrateToLatest }` |
|
|
263
|
+
| `@shivaedev/test-sql/server/migrationUpgrade.ts` | `migrationUpgrade({ migrations, upTo, table })`, `MigrationUpgradeOptions`, `PostgresColumn` |
|
|
264
|
+
| `@shivaedev/test-sql/server/assertModelColumns.ts` | `assertModelColumns(columns, model, types)`, `ModelColumnMismatch` |
|
|
249
265
|
|
|
250
266
|
`url` is a redacted PostgreSQL URL for an existing administrative database. `PostgresTestDatabase` supplies the current database's redacted `url`, an `awaitCleanup: Effect<void>` drain, a scoped `isolated` Effect that acquires a migrated clone's service Context, and `clone(template)` for an explicit `"empty"` or `"migrated"` choice. With `prepare: false`, both choices are empty. Prefer `withIsolatedDatabase` to acquire and release that Context around a test. Wrappers retain the body's result, failures and unmet service requirements, with native `SqlError` added for acquisition or transaction failures. Cleanup failures surface as SQL defects when the Layer closes. Drops that fail with connection exhaustion (SQLSTATE `53300`) retry every 100 ms of live time for up to 300 retries, even while a test clock is paused; if capacity does not return, the SQL defect remains visible. Startup sweeps databases left by dead processes on the same host and databases from the older unowned naming format. Databases owned by live processes or another host are retained, so local parallel workers keep their databases. Use the same host to reclaim its abandoned databases.
|
|
251
267
|
|
|
@@ -255,4 +271,4 @@ Import each name from the module that defines it. The package has no root entry.
|
|
|
255
271
|
pnpm add --save-dev @shivaedev/test-sql @shivaedev/test @effect/sql-pg @effect/vitest effect vitest
|
|
256
272
|
```
|
|
257
273
|
|
|
258
|
-
|
|
274
|
+
`@effect/sql-pg` and `effect` are peer dependencies; `@shivaedev/test` supplies the `makeEffectIt` runner the examples use. The package requires Node.js 24 or later. The PostgreSQL role must be able to create and drop databases; use a dedicated test server. PostgreSQL 13 or later supports the forced database drops used for cleanup. The migrations and fixtures must complete their work during preparation; their scoped resources are closed before cloning.
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
export declare const databaseId: Effect.Effect<string, never, never>;
|
|
3
|
+
export declare const databaseName: Effect.Effect<string, never, never>;
|
|
2
4
|
export declare function abandonedDatabase(name: string): boolean;
|
|
3
5
|
//# sourceMappingURL=databaseOwner.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"databaseOwner.d.ts","sourceRoot":"","sources":["../../../src/server/internal/databaseOwner.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"databaseOwner.d.ts","sourceRoot":"","sources":["../../../src/server/internal/databaseOwner.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AAIxC,eAAO,MAAM,UAAU,qCAEtB,CAAC;AAEF,eAAO,MAAM,YAAY,qCAA6E,CAAC;AAEvG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,WAc7C"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { hostname } from "node:os";
|
|
3
3
|
import process from "node:process";
|
|
4
|
+
import { Effect, Random } from "effect";
|
|
4
5
|
const host = createHash("sha256").update(hostname()).digest("hex").slice(0, 8);
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
}
|
|
6
|
+
export const databaseId = Effect.map(Effect.replicateEffect(Random.nextIntBetween(0, 0xff_ff_ff_ff), 4), (parts) => parts.map((part) => part.toString(16).padStart(8, "0")).join(""));
|
|
7
|
+
export const databaseName = Effect.map(databaseId, (id) => `effect_test_${host}_${process.pid}_${id}`);
|
|
8
8
|
export function abandonedDatabase(name) {
|
|
9
9
|
if (/^effect_test_[a-f0-9]{32}$/u.test(name)) {
|
|
10
10
|
return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"databaseOwner.js","sourceRoot":"","sources":["../../../src/server/internal/databaseOwner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,OAAO,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"databaseOwner.js","sourceRoot":"","sources":["../../../src/server/internal/databaseOwner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,OAAO,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAE/E,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,CAClH,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAChE,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,eAAe,IAAI,IAAI,OAAO,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,CAAC;AAEvG,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC7C,IAAI,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9C,OAAO,IAAI,CAAC;IACb,CAAC;IACD,MAAM,KAAK,GAAG,iEAAiE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3F,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,IAAI,EAAE,CAAC;QACnD,OAAO,KAAK,CAAC;IACd,CAAC;IACD,IAAI,CAAC;QACJ,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1C,OAAO,KAAK,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC;IAC5E,CAAC;AACF,CAAC"}
|
|
@@ -16,7 +16,7 @@ export const administer = Effect.fn("PostgresTest.administer")(function* (url, o
|
|
|
16
16
|
return yield* Effect.scoped(Effect.flatMap(PgClient.makeClient({ url }), operation).pipe(Effect.mapError(connectionLimitHint))).pipe(Effect.provide(Reactivity.layer));
|
|
17
17
|
});
|
|
18
18
|
export const database = Effect.fn("PostgresTest.database")(function* (url, release, template = "template0") {
|
|
19
|
-
const name = databaseName
|
|
19
|
+
const name = yield* databaseName;
|
|
20
20
|
const target = new URL(Redacted.value(url));
|
|
21
21
|
target.pathname = `/${name}`;
|
|
22
22
|
target.searchParams.delete("database");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"databases.js","sourceRoot":"","sources":["../../../src/server/internal/databases.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,KAAK,UAAU,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,UAAU,UAAU,CAAC,GAA8B,EAAE,cAAsB,EAAE,UAA6B;IAC/G,OAAO,KAAK,CAAC,aAAa,CACzB,MAAM,CAAC,GAAG,CACT,QAAQ,CAAC,IAAI,CAAC;QACb,cAAc;QACd,mBAAmB,EAAE,UAAU,CAAC,mBAAmB;QACnD,oBAAoB,EAAE,UAAU,CAAC,oBAAoB;QACrD,GAAG;KACH,CAAC,EACF,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAClG,CACD,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,EAAE,CAAC,yBAAyB,CAAC,CAAC,QAAQ,CAAC,EACvE,GAA8B,EAC9B,SAAmE;IAEnE,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,IAAI,CACnI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAChC,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,EAAE,CAAC,uBAAuB,CAAC,CAAC,QAAQ,CAAC,EACnE,GAA8B,EAC9B,OAAiD,EACjD,QAAQ,GAAG,WAAW;IAEtB,MAAM,IAAI,GAAG,
|
|
1
|
+
{"version":3,"file":"databases.js","sourceRoot":"","sources":["../../../src/server/internal/databases.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,KAAK,UAAU,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,UAAU,UAAU,CAAC,GAA8B,EAAE,cAAsB,EAAE,UAA6B;IAC/G,OAAO,KAAK,CAAC,aAAa,CACzB,MAAM,CAAC,GAAG,CACT,QAAQ,CAAC,IAAI,CAAC;QACb,cAAc;QACd,mBAAmB,EAAE,UAAU,CAAC,mBAAmB;QACnD,oBAAoB,EAAE,UAAU,CAAC,oBAAoB;QACrD,GAAG;KACH,CAAC,EACF,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAClG,CACD,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,EAAE,CAAC,yBAAyB,CAAC,CAAC,QAAQ,CAAC,EACvE,GAA8B,EAC9B,SAAmE;IAEnE,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,IAAI,CACnI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAChC,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,EAAE,CAAC,uBAAuB,CAAC,CAAC,QAAQ,CAAC,EACnE,GAA8B,EAC9B,OAAiD,EACjD,QAAQ,GAAG,WAAW;IAEtB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,YAAY,CAAC;IACjC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5C,MAAM,CAAC,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC;IAC7B,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACvC,KAAK,CAAC,CAAC,MAAM,CAAC,cAAc,CAC3B,UAAU,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAA,mBAAmB,KAAK,CAAC,IAAI,CAAC,aAAa,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,EAC7F,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CACnB,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC;AACxD,CAAC,CAAC,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import { Migrator, SqlClient } from "effect/sql";
|
|
3
3
|
import { type PostgresMigrationOptions } from "@shivaedev/sql/migrations.ts";
|
|
4
|
-
export declare const migrationSteps: <R>(migrations: PostgresMigrationOptions<R>) => Effect.Effect<{
|
|
5
|
-
migrateTo: (name: string | number) => Effect.Effect<readonly (readonly [id: number, name: string])[], Migrator.MigrationError | import("effect/sql/SqlError").SqlError, Exclude<R, R | SqlClient.SqlClient>>;
|
|
6
|
-
migrateToLatest: Effect.Effect<readonly (readonly [id: number, name: string])[], Migrator.MigrationError | import("effect/sql/SqlError").SqlError, Exclude<R, R | SqlClient.SqlClient>>;
|
|
4
|
+
export declare const migrationSteps: <R, E = never>(migrations: PostgresMigrationOptions<R, E>) => Effect.Effect<{
|
|
5
|
+
migrateTo: (name: string | number) => Effect.Effect<readonly (readonly [id: number, name: string])[], E | Migrator.MigrationError | import("effect/Schema").SchemaError | import("effect/sql/SqlError").SqlError, Exclude<R, R | SqlClient.SqlClient>>;
|
|
6
|
+
migrateToLatest: Effect.Effect<readonly (readonly [id: number, name: string])[], E | Migrator.MigrationError | import("effect/Schema").SchemaError | import("effect/sql/SqlError").SqlError, Exclude<R, R | SqlClient.SqlClient>>;
|
|
7
7
|
}, Migrator.MigrationError, R | SqlClient.SqlClient>;
|
|
8
8
|
//# sourceMappingURL=migrationSteps.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrationSteps.d.ts","sourceRoot":"","sources":["../../src/server/migrationSteps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAmB,KAAK,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAY9F,eAAO,MAAM,cAAc,GAAuD,CAAC;
|
|
1
|
+
{"version":3,"file":"migrationSteps.d.ts","sourceRoot":"","sources":["../../src/server/migrationSteps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAmB,KAAK,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAY9F,eAAO,MAAM,cAAc,GAAuD,CAAC,EAAE,CAAC;sBAwBlE,MAAM,GAAG,MAAM;;oDAejC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrationSteps.js","sourceRoot":"","sources":["../../src/server/migrationSteps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,eAAe,EAAiC,MAAM,8BAA8B,CAAC;AAE9F,SAAS,OAAO,CAAC,OAAe;IAC/B,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,kBAAkB,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,GAAwB;IAC/E,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,EAAE,CAAC;QACxE,OAAO,KAAK,CAAC,CAAC,OAAO,CAAC,0DAA0D,CAAC,CAAC;IACnF,CAAC;AACF,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,EAAE,CAAC,6BAA6B,CAAC,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"migrationSteps.js","sourceRoot":"","sources":["../../src/server/migrationSteps.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,10 +5,10 @@ export interface PostgresColumn {
|
|
|
5
5
|
readonly name: string;
|
|
6
6
|
readonly type: string;
|
|
7
7
|
}
|
|
8
|
-
export interface MigrationUpgradeOptions<R = never> {
|
|
9
|
-
readonly migrations: PostgresMigrationOptions<R>;
|
|
8
|
+
export interface MigrationUpgradeOptions<R = never, E = never> {
|
|
9
|
+
readonly migrations: PostgresMigrationOptions<R, E>;
|
|
10
10
|
readonly table: string;
|
|
11
11
|
readonly upTo: number;
|
|
12
12
|
}
|
|
13
|
-
export declare const migrationUpgrade: <R>(options: MigrationUpgradeOptions<R>) => Effect.Effect<readonly PostgresColumn[], Migrator.MigrationError | import("effect/sql/SqlError").SqlError, R | SqlClient.SqlClient>;
|
|
13
|
+
export declare const migrationUpgrade: <R, E = never>(options: MigrationUpgradeOptions<R, E>) => Effect.Effect<readonly PostgresColumn[], E | Migrator.MigrationError | import("effect/Schema").SchemaError | import("effect/sql/SqlError").SqlError, R | SqlClient.SqlClient>;
|
|
14
14
|
//# sourceMappingURL=migrationUpgrade.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrationUpgrade.d.ts","sourceRoot":"","sources":["../../src/server/migrationUpgrade.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAmB,KAAK,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAE9F,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,uBAAuB,CAAC,CAAC,GAAG,KAAK;
|
|
1
|
+
{"version":3,"file":"migrationUpgrade.d.ts","sourceRoot":"","sources":["../../src/server/migrationUpgrade.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAmB,KAAK,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAE9F,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,uBAAuB,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,KAAK;IAC5D,QAAQ,CAAC,UAAU,EAAE,wBAAwB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACpD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,gBAAgB,GAAyD,CAAC,EAAE,CAAC,kOA6BxF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrationUpgrade.js","sourceRoot":"","sources":["../../src/server/migrationUpgrade.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,eAAe,EAAiC,MAAM,8BAA8B,CAAC;AAa9F,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,EAAE,CAAC,+BAA+B,CAAC,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"migrationUpgrade.js","sourceRoot":"","sources":["../../src/server/migrationUpgrade.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,eAAe,EAAiC,MAAM,8BAA8B,CAAC;AAa9F,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,EAAE,CAAC,+BAA+B,CAAC,CAAC,QAAQ,CAAC,EAAgB,OAAsC;IACzI,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC;IACvC,SAAS,OAAO,CAAC,OAAe;QAC/B,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IAChF,CAAC;IACD,KAAK,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,8CAA8C,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7H,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,EAAE,CAAC;QACxE,OAAO,KAAK,CAAC,CAAC,OAAO,CAAC,mEAAmE,CAAC,CAAC;IAC5F,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,uBAAuB,CAAC;IACnE,MAAM,UAAU,GAAG,GAAG,CAAA,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,GAAG,CAAkC,sBAAsB,UAAU,iBAAiB,CAAC,gBAAgB,CAAC;IAChI,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,IAAI,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,GAAG,CAAA,4BAA4B,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC;QAC5E,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO,KAAK,CAAC,CAAC,OAAO,CAAC,6DAA6D,CAAC,CAAC;QACtF,CAAC;IACF,CAAC;IACD,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC;IAClD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,KAAK,CAAC,CAAC,OAAO,CAAC,aAAa,OAAO,CAAC,IAAI,wBAAwB,CAAC,CAAC;IAC1E,CAAC;IACD,KAAK,CAAC,CAAC,eAAe,CAAC,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;IACzH,KAAK,CAAC,CAAC,eAAe,CAAC,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACpF,OAAO,KAAK,CAAC,CAAC,GAAG,CAAgB;;;qBAGb,OAAO,CAAC,KAAK;mBACf,CAAC,gBAAgB,CAAC;AACrC,CAAC,CAAC,CAAC"}
|
|
@@ -11,12 +11,12 @@ interface PostgresTestCommonOptions<E, TFixtures> extends SqlNameTransforms {
|
|
|
11
11
|
readonly url: Redacted.Redacted<string>;
|
|
12
12
|
}
|
|
13
13
|
export type PostgresTestOptions<E = never, R = never, TFixtures = never> = PostgresTestCommonOptions<E, TFixtures> & ({
|
|
14
|
-
readonly migrations: PostgresMigrationOptions<R>;
|
|
14
|
+
readonly migrations: PostgresMigrationOptions<R, E>;
|
|
15
15
|
readonly initialize?: never;
|
|
16
16
|
} | {
|
|
17
17
|
readonly initialize: Effect.Effect<unknown, E, R | PostgresTestServices | Scope.Scope> | PostgresTestLoader<E, R>;
|
|
18
18
|
readonly migrations?: never;
|
|
19
19
|
});
|
|
20
|
-
export declare function makePostgresTestLayer<E = never, R = never, TFixtures = never>(options: PostgresTestOptions<E, R, TFixtures>): Layer.Layer<import("@effect/sql-pg/PgClient").PgClient | PostgresTestDatabase | Reactivity.Reactivity | import("effect/sql/SqlClient").SqlClient, E | import("effect/sql/Migrator").MigrationError | import("effect/sql/SqlError").SqlError, Exclude<Exclude<Exclude<Exclude<R, import("@effect/sql-pg/PgClient").PgClient | Reactivity.Reactivity | import("effect/sql/SqlClient").SqlClient>, Scope.Scope>, Scope.Scope>, Reactivity.Reactivity> | Exclude<Exclude<Exclude<Exclude<TFixtures, import("@effect/sql-pg/PgClient").PgClient | Reactivity.Reactivity | import("effect/sql/SqlClient").SqlClient>, Scope.Scope>, Scope.Scope>, Reactivity.Reactivity>>;
|
|
20
|
+
export declare function makePostgresTestLayer<E = never, R = never, TFixtures = never>(options: PostgresTestOptions<E, R, TFixtures>): Layer.Layer<import("@effect/sql-pg/PgClient").PgClient | PostgresTestDatabase | Reactivity.Reactivity | import("effect/sql/SqlClient").SqlClient, E | import("effect/sql/Migrator").MigrationError | import("effect/Schema").SchemaError | import("effect/sql/SqlError").SqlError, Exclude<Exclude<Exclude<Exclude<R, import("@effect/sql-pg/PgClient").PgClient | Reactivity.Reactivity | import("effect/sql/SqlClient").SqlClient>, Scope.Scope>, Scope.Scope>, Reactivity.Reactivity> | Exclude<Exclude<Exclude<Exclude<TFixtures, import("@effect/sql-pg/PgClient").PgClient | Reactivity.Reactivity | import("effect/sql/SqlClient").SqlClient>, Scope.Scope>, Scope.Scope>, Reactivity.Reactivity>>;
|
|
21
21
|
export {};
|
|
22
22
|
//# sourceMappingURL=postgres.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/server/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,MAAM,EAAE,KAAK,EAAE,KAAK,QAAQ,EAAE,KAAK,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC3E,OAAO,KAAK,UAAU,MAAM,8BAA8B,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAmB,KAAK,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAC9F,OAAO,EAAE,oBAAoB,EAAoC,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAKjH,MAAM,MAAM,kBAAkB,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,KAAK,IAAI,CACtD,GAAG,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,KAC1B,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,GAAG,oBAAoB,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;AAEvE,UAAU,yBAAyB,CAAC,CAAC,EAAE,SAAS,CAAE,SAAQ,iBAAiB;IAC1E,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,SAAS,GAAG,oBAAoB,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,kBAAkB,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACjI,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;CACxC;AAED,MAAM,MAAM,mBAAmB,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,KAAK,EAAE,SAAS,GAAG,KAAK,IAAI,yBAAyB,CAAC,CAAC,EAAE,SAAS,CAAC,GACjH,CACG;IAAE,QAAQ,CAAC,UAAU,EAAE,wBAAwB,CAAC,CAAC,CAAC,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/server/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,MAAM,EAAE,KAAK,EAAE,KAAK,QAAQ,EAAE,KAAK,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC3E,OAAO,KAAK,UAAU,MAAM,8BAA8B,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAmB,KAAK,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAC9F,OAAO,EAAE,oBAAoB,EAAoC,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAKjH,MAAM,MAAM,kBAAkB,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,KAAK,IAAI,CACtD,GAAG,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,KAC1B,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,GAAG,oBAAoB,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;AAEvE,UAAU,yBAAyB,CAAC,CAAC,EAAE,SAAS,CAAE,SAAQ,iBAAiB;IAC1E,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,SAAS,GAAG,oBAAoB,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,kBAAkB,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACjI,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;CACxC;AAED,MAAM,MAAM,mBAAmB,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,KAAK,EAAE,SAAS,GAAG,KAAK,IAAI,yBAAyB,CAAC,CAAC,EAAE,SAAS,CAAC,GACjH,CACG;IAAE,QAAQ,CAAC,UAAU,EAAE,wBAAwB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,CAAA;CAAE,GACpF;IACA,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,GAAG,oBAAoB,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAClH,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC;CAC3B,CACH,CAAC;AASH,wBAAgB,qBAAqB,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,KAAK,EAAE,SAAS,GAAG,KAAK,EAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,CAAC,6qBAmC3H"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Duration, Effect } from "effect";
|
|
2
|
+
import { SqlClient } from "effect/sql";
|
|
3
|
+
export declare function raceWithLock<A, E, R, TLockError, TLockServices>(options: {
|
|
4
|
+
readonly lock: Effect.Effect<unknown, TLockError, TLockServices>;
|
|
5
|
+
readonly effects: readonly Effect.Effect<A, E, R>[];
|
|
6
|
+
readonly timeout?: Duration.Input;
|
|
7
|
+
readonly release?: "commit" | "rollback";
|
|
8
|
+
}): Effect.Effect<import("effect/Result").Result<A, E>[], TLockError | import("effect/sql/SqlError").SqlError | import("effect/Cause").TimeoutError, import("effect/reactivity/Reactivity").Reactivity | SqlClient.SqlClient | Exclude<R, import("effect/Scope").Scope> | Exclude<Exclude<TLockServices, import("effect/Scope").Scope>, import("effect/Scope").Scope>>;
|
|
9
|
+
//# sourceMappingURL=raceWithLock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raceWithLock.d.ts","sourceRoot":"","sources":["../../src/server/raceWithLock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,EAAmB,MAAM,QAAQ,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAIvC,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,UAAU,EAAE,aAAa,EAAE,OAAO,EAAE;IACzE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC;IACjE,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IACpD,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC;IAClC,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;CACzC,sWA6BA"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Deferred, Effect, Fiber, Schedule } from "effect";
|
|
2
|
+
import { SqlClient } from "effect/sql";
|
|
3
|
+
import { transact } from "@shivaedev/sql/transact.ts";
|
|
4
|
+
import { withTestTransaction } from "#withTestTransaction.ts";
|
|
5
|
+
export function raceWithLock(options) {
|
|
6
|
+
return Effect.scoped(Effect.gen(function* () {
|
|
7
|
+
const sql = yield* SqlClient.SqlClient;
|
|
8
|
+
const ready = yield* Deferred.make();
|
|
9
|
+
const started = yield* Deferred.make();
|
|
10
|
+
const hold = Effect.gen(function* () {
|
|
11
|
+
yield* options.lock;
|
|
12
|
+
yield* Deferred.succeed(ready, undefined);
|
|
13
|
+
yield* Deferred.await(started);
|
|
14
|
+
yield* sql `select pg_stat_clear_snapshot()`.pipe(Effect.andThen(sql `with recursive blocked(pid) as (
|
|
15
|
+
select pid from pg_stat_activity where pg_backend_pid() = any(pg_blocking_pids(pid))
|
|
16
|
+
union
|
|
17
|
+
select activity.pid from pg_stat_activity activity join blocked on blocked.pid = any(pg_blocking_pids(activity.pid))
|
|
18
|
+
) select count(*)::integer as waiting from blocked`), Effect.repeat({ schedule: Schedule.spaced("10 millis"), while: ([row]) => row?.waiting !== options.effects.length }));
|
|
19
|
+
});
|
|
20
|
+
const holder = yield* (options.release === "commit" ? transact(Effect.scoped(hold), { onSqlError: (error) => error }) : withTestTransaction(hold)).pipe(Effect.forkScoped);
|
|
21
|
+
yield* Effect.raceFirst(Deferred.await(ready), Fiber.join(holder).pipe(Effect.andThen(Effect.never)));
|
|
22
|
+
const contenders = yield* Effect.forEach(options.effects, Effect.result, { concurrency: "unbounded" }).pipe(Effect.forkScoped);
|
|
23
|
+
yield* Deferred.succeed(started, undefined);
|
|
24
|
+
yield* Effect.raceFirst(Fiber.join(holder), Fiber.join(contenders).pipe(Effect.andThen(Effect.never)));
|
|
25
|
+
return yield* Fiber.join(contenders);
|
|
26
|
+
})).pipe(Effect.timeout(options.timeout ?? "5 seconds"));
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=raceWithLock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raceWithLock.js","sourceRoot":"","sources":["../../src/server/raceWithLock.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/test-sql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "PostgreSQL rollback and real-commit test isolation for Effect",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"effect"
|
|
@@ -31,8 +31,7 @@
|
|
|
31
31
|
"types": "./dist/*.d.ts",
|
|
32
32
|
"default": "./dist/*.js"
|
|
33
33
|
},
|
|
34
|
-
"./package.json": "./package.json"
|
|
35
|
-
"./server/internal/*": null
|
|
34
|
+
"./package.json": "./package.json"
|
|
36
35
|
},
|
|
37
36
|
"files": [
|
|
38
37
|
"dist",
|
|
@@ -44,23 +43,25 @@
|
|
|
44
43
|
"README.md"
|
|
45
44
|
],
|
|
46
45
|
"dependencies": {
|
|
47
|
-
"@shivaedev/sql": "0.
|
|
46
|
+
"@shivaedev/sql": "0.26.0"
|
|
48
47
|
},
|
|
49
48
|
"devDependencies": {
|
|
50
|
-
"@effect/sql-pg": "4.0.
|
|
51
|
-
"@effect/vitest": "4.0.
|
|
52
|
-
"@shivaedev/
|
|
53
|
-
"@shivaedev/
|
|
54
|
-
"@shivaedev/
|
|
49
|
+
"@effect/sql-pg": "4.0.6",
|
|
50
|
+
"@effect/vitest": "4.0.6",
|
|
51
|
+
"@shivaedev/jobs": "0.23.0",
|
|
52
|
+
"@shivaedev/quality": "0.46.0",
|
|
53
|
+
"@shivaedev/service": "0.6.0",
|
|
54
|
+
"@shivaedev/test": "0.11.0",
|
|
55
|
+
"@shivaedev/test-story": "0.25.0",
|
|
55
56
|
"@types/node": "24.19.2",
|
|
56
|
-
"effect": "4.0.
|
|
57
|
+
"effect": "4.0.6",
|
|
57
58
|
"pg-boss": "12.38.0",
|
|
58
59
|
"typescript": "7.0.2",
|
|
59
60
|
"vitest": "5.0.5"
|
|
60
61
|
},
|
|
61
62
|
"peerDependencies": {
|
|
62
|
-
"@effect/sql-pg": "4.0.
|
|
63
|
-
"effect": "4.0.
|
|
63
|
+
"@effect/sql-pg": "4.0.6",
|
|
64
|
+
"effect": "4.0.6"
|
|
64
65
|
},
|
|
65
66
|
"engines": {
|
|
66
67
|
"node": ">=24"
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { hostname } from "node:os";
|
|
3
3
|
import process from "node:process";
|
|
4
|
+
import { Effect, Random } from "effect";
|
|
4
5
|
|
|
5
6
|
const host = createHash("sha256").update(hostname()).digest("hex").slice(0, 8);
|
|
6
7
|
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
export const databaseId = Effect.map(Effect.replicateEffect(Random.nextIntBetween(0, 0xff_ff_ff_ff), 4), (parts) =>
|
|
9
|
+
parts.map((part) => part.toString(16).padStart(8, "0")).join(""),
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
export const databaseName = Effect.map(databaseId, (id) => `effect_test_${host}_${process.pid}_${id}`);
|
|
10
13
|
|
|
11
14
|
export function abandonedDatabase(name: string) {
|
|
12
15
|
if (/^effect_test_[a-f0-9]{32}$/u.test(name)) {
|
|
@@ -35,7 +35,7 @@ export const database = Effect.fn("PostgresTest.database")(function* (
|
|
|
35
35
|
release: (name: string) => Effect.Effect<unknown>,
|
|
36
36
|
template = "template0",
|
|
37
37
|
) {
|
|
38
|
-
const name = databaseName
|
|
38
|
+
const name = yield* databaseName;
|
|
39
39
|
const target = new URL(Redacted.value(url));
|
|
40
40
|
target.pathname = `/${name}`;
|
|
41
41
|
target.searchParams.delete("database");
|
|
@@ -12,7 +12,7 @@ const outsideTransaction = Effect.fnUntraced(function* (sql: SqlClient.SqlClient
|
|
|
12
12
|
}
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
-
export const migrationSteps = Effect.fn("PostgresTest.migrationSteps")(function* <R>(migrations: PostgresMigrationOptions<R>) {
|
|
15
|
+
export const migrationSteps = Effect.fn("PostgresTest.migrationSteps")(function* <R, E = never>(migrations: PostgresMigrationOptions<R, E>) {
|
|
16
16
|
const sql = yield* SqlClient.SqlClient;
|
|
17
17
|
const context = yield* Effect.context<R | SqlClient.SqlClient>();
|
|
18
18
|
yield* sql.onDialectOrElse({ orElse: () => invalid("Migration steps require PostgreSQL."), pg: () => Effect.void });
|
|
@@ -7,13 +7,13 @@ export interface PostgresColumn {
|
|
|
7
7
|
readonly type: string;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export interface MigrationUpgradeOptions<R = never> {
|
|
11
|
-
readonly migrations: PostgresMigrationOptions<R>;
|
|
10
|
+
export interface MigrationUpgradeOptions<R = never, E = never> {
|
|
11
|
+
readonly migrations: PostgresMigrationOptions<R, E>;
|
|
12
12
|
readonly table: string;
|
|
13
13
|
readonly upTo: number;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export const migrationUpgrade = Effect.fn("PostgresTest.migrationUpgrade")(function* <R>(options: MigrationUpgradeOptions<R>) {
|
|
16
|
+
export const migrationUpgrade = Effect.fn("PostgresTest.migrationUpgrade")(function* <R, E = never>(options: MigrationUpgradeOptions<R, E>) {
|
|
17
17
|
const sql = yield* SqlClient.SqlClient;
|
|
18
18
|
function invalid(message: string) {
|
|
19
19
|
return Effect.fail(new Migrator.MigrationError({ kind: "BadState", message }));
|
package/src/server/postgres.ts
CHANGED
|
@@ -20,7 +20,7 @@ interface PostgresTestCommonOptions<E, TFixtures> extends SqlNameTransforms {
|
|
|
20
20
|
|
|
21
21
|
export type PostgresTestOptions<E = never, R = never, TFixtures = never> = PostgresTestCommonOptions<E, TFixtures> &
|
|
22
22
|
(
|
|
23
|
-
| { readonly migrations: PostgresMigrationOptions<R>; readonly initialize?: never }
|
|
23
|
+
| { readonly migrations: PostgresMigrationOptions<R, E>; readonly initialize?: never }
|
|
24
24
|
| {
|
|
25
25
|
readonly initialize: Effect.Effect<unknown, E, R | PostgresTestServices | Scope.Scope> | PostgresTestLoader<E, R>;
|
|
26
26
|
readonly migrations?: never;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Deferred, type Duration, Effect, Fiber, Schedule } from "effect";
|
|
2
|
+
import { SqlClient } from "effect/sql";
|
|
3
|
+
import { transact } from "@shivaedev/sql/transact.ts";
|
|
4
|
+
import { withTestTransaction } from "#withTestTransaction.ts";
|
|
5
|
+
|
|
6
|
+
export function raceWithLock<A, E, R, TLockError, TLockServices>(options: {
|
|
7
|
+
readonly lock: Effect.Effect<unknown, TLockError, TLockServices>;
|
|
8
|
+
readonly effects: readonly Effect.Effect<A, E, R>[];
|
|
9
|
+
readonly timeout?: Duration.Input;
|
|
10
|
+
readonly release?: "commit" | "rollback";
|
|
11
|
+
}) {
|
|
12
|
+
return Effect.scoped(
|
|
13
|
+
Effect.gen(function* () {
|
|
14
|
+
const sql = yield* SqlClient.SqlClient;
|
|
15
|
+
const ready = yield* Deferred.make<void>();
|
|
16
|
+
const started = yield* Deferred.make<void>();
|
|
17
|
+
const hold = Effect.gen(function* () {
|
|
18
|
+
yield* options.lock;
|
|
19
|
+
yield* Deferred.succeed(ready, undefined);
|
|
20
|
+
yield* Deferred.await(started);
|
|
21
|
+
yield* sql`select pg_stat_clear_snapshot()`.pipe(
|
|
22
|
+
Effect.andThen(sql<{ waiting: number }>`with recursive blocked(pid) as (
|
|
23
|
+
select pid from pg_stat_activity where pg_backend_pid() = any(pg_blocking_pids(pid))
|
|
24
|
+
union
|
|
25
|
+
select activity.pid from pg_stat_activity activity join blocked on blocked.pid = any(pg_blocking_pids(activity.pid))
|
|
26
|
+
) select count(*)::integer as waiting from blocked`),
|
|
27
|
+
Effect.repeat({ schedule: Schedule.spaced("10 millis"), while: ([row]) => row?.waiting !== options.effects.length }),
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
const holder = yield* (
|
|
31
|
+
options.release === "commit" ? transact(Effect.scoped(hold), { onSqlError: (error) => error }) : withTestTransaction(hold)
|
|
32
|
+
).pipe(Effect.forkScoped);
|
|
33
|
+
yield* Effect.raceFirst(Deferred.await(ready), Fiber.join(holder).pipe(Effect.andThen(Effect.never)));
|
|
34
|
+
const contenders = yield* Effect.forEach(options.effects, Effect.result, { concurrency: "unbounded" }).pipe(Effect.forkScoped);
|
|
35
|
+
yield* Deferred.succeed(started, undefined);
|
|
36
|
+
yield* Effect.raceFirst(Fiber.join(holder), Fiber.join(contenders).pipe(Effect.andThen(Effect.never)));
|
|
37
|
+
return yield* Fiber.join(contenders);
|
|
38
|
+
}),
|
|
39
|
+
).pipe(Effect.timeout(options.timeout ?? "5 seconds"));
|
|
40
|
+
}
|