@shivaedev/effect-test-sql 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.2 - 2026-10-17
4
+
5
+ ### Changed
6
+
7
+ - Block `@shivaedev/effect-test-sql/internal/*` imports; use the public test isolation modules instead.
8
+
9
+ ## 0.5.1 - 2026-10-17
10
+
11
+ ### Fixed
12
+
13
+ - The `migrateTo(name: string | number)` helper returned by `migrationSteps` accepts zero-padded migration names as written, unpadded names and bare numeric ids. Unknown or ambiguous targets fail with a typed `MigrationError` naming the requested target.
14
+
3
15
  ## 0.5.0 - 2026-10-17
4
16
 
5
17
  ### Added
package/README.md CHANGED
@@ -112,9 +112,9 @@ Inside an isolated block, `PostgresTestDatabase.url` names that clone. Use it to
112
112
 
113
113
  ### Test a backfill against existing rows
114
114
 
115
- A backfill needs rows in the old schema. Prepare an empty template with the application's own `PostgresMigrationOptions`, then use `migrationSteps` inside `withIsolatedDatabase`. `migrateTo` takes the full migration name, including its numeric prefix and without a file extension. It commits migrations through that name; seed rows with the native SQL client, then yield `migrateToLatest` to apply the remaining migrations on the same database.
115
+ A backfill needs rows in the old schema. Prepare an empty template with the application's own `PostgresMigrationOptions`, then use `migrationSteps` inside `withIsolatedDatabase`. `migrateTo` accepts the migration name as written without a file extension (`"001_create_contacts"`), its unpadded form (`"1_create_contacts"`), or its bare numeric id (`1`, `"1"` or `"001"`). It commits migrations through that target; seed rows with the native SQL client, then yield `migrateToLatest` to apply the remaining migrations on the same database.
116
116
 
117
- This example uses an application loader whose `1_create_contacts` migration creates `contacts(id, email)`, `2_backfill_emails` creates `emails(contact_id, address)` and copies the contact email addresses into it, and `3_drop_email` drops `contacts.email`:
117
+ This example uses an application loader whose `001_create_contacts` migration creates `contacts(id, email)`, `002_backfill_emails` creates `emails(contact_id, address)` and copies the contact email addresses into it, and `003_drop_email` drops `contacts.email`:
118
118
 
119
119
  ```ts
120
120
  import { migrationSteps } from "@shivaedev/effect-test-sql/migrationSteps.ts";
@@ -135,7 +135,7 @@ const { effectApp: migrationTest } = makeEffectIt({
135
135
  });
136
136
 
137
137
  migrationTest("preserves contact emails in the new table", function* (steps) {
138
- yield* steps.migrateTo("1_create_contacts");
138
+ yield* steps.migrateTo("001_create_contacts");
139
139
  const sql = yield* SqlClient.SqlClient;
140
140
  yield* sql`insert into contacts (id, email) values (1, 'alice@example.test')`;
141
141
  yield* steps.migrateToLatest;
@@ -145,7 +145,7 @@ migrationTest("preserves contact emails in the new table", function* (steps) {
145
145
  });
146
146
  ```
147
147
 
148
- The helper resolves the loader once and uses `migratePostgres` for each step, preserving its ledger table and lock timeout. Unknown names fail with a typed `MigrationError` that names the requested migration. Moving behind an already applied migration and running inside a wrapping transaction also fail with `MigrationError`. Loader dependencies, including `Scope`, remain requirements of `migrationSteps`; its resources live until the caller's scope closes. Use the returned steps inside that database's scope. SQL and loader failures retain their failure channels.
148
+ The helper resolves the loader once and uses `migratePostgres` for each step, preserving its ledger table and lock timeout. Unknown or ambiguous targets fail with a typed `MigrationError` that names the requested target. Moving behind an already applied migration and running inside a wrapping transaction also fail with `MigrationError`. Loader dependencies, including `Scope`, remain requirements of `migrationSteps`; its resources live until the caller's scope closes. Use the returned steps inside that database's scope. SQL and loader failures retain their failure channels.
149
149
 
150
150
  For test-story, use the same Layer and `around: withIsolatedDatabase`, acquire `migrationSteps(appMigrations)` in the kit's `create`, and let schema and data traits call `migrateTo` and seed native SQL. A verb can yield `migrateToLatest` before the story asserts the backfilled rows.
151
151
 
@@ -2,7 +2,7 @@ import { Effect } from "effect";
2
2
  import { Migrator, SqlClient } from "effect/sql";
3
3
  import { type PostgresMigrationOptions } from "@shivaedev/effect-sql/migrations.ts";
4
4
  export declare const migrationSteps: <R>(migrations: PostgresMigrationOptions<R>) => Effect.Effect<{
5
- migrateTo: (name: string) => Effect.Effect<readonly (readonly [id: number, name: string])[], Migrator.MigrationError | import("effect/sql/SqlError").SqlError, never>;
5
+ migrateTo: (name: string | number) => Effect.Effect<readonly (readonly [id: number, name: string])[], Migrator.MigrationError | import("effect/sql/SqlError").SqlError, never>;
6
6
  migrateToLatest: Effect.Effect<readonly (readonly [id: number, name: string])[], Migrator.MigrationError | import("effect/sql/SqlError").SqlError, never>;
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,qCAAqC,CAAC;AAYrG,eAAO,MAAM,cAAc,GAAuD,CAAC;sBAuB/D,MAAM;;oDAQxB,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,qCAAqC,CAAC;AAYrG,eAAO,MAAM,cAAc,GAAuD,CAAC;sBAuB/D,MAAM,GAAG,MAAM;;oDAejC,CAAC"}
@@ -33,10 +33,16 @@ export const migrationSteps = Effect.fn("PostgresTest.migrationSteps")(function*
33
33
  }
34
34
  return {
35
35
  migrateTo: (name) => {
36
- const target = resolved.find(([id, label]) => `${id}_${label}` === name);
37
- return target === undefined
38
- ? invalid(`Migration "${name}" is not in the loader.`)
39
- : run(Effect.succeed(resolved.filter(([id]) => id <= target[0])), target[0]);
36
+ const match = /^(?<id>\d+)(?:_(?<label>.+))?$/u.exec(String(name))?.groups;
37
+ const matches = match === undefined ? [] : resolved.filter(([id, label]) => id === Number(match.id) && (match.label === undefined || label === match.label));
38
+ const [target] = matches;
39
+ if (target === undefined) {
40
+ return invalid(`Migration "${name}" is not in the loader.`);
41
+ }
42
+ if (matches.length > 1) {
43
+ return invalid(`Migration "${name}" is ambiguous in the loader.`);
44
+ }
45
+ return run(Effect.succeed(resolved.filter(([id]) => id <= target[0])), target[0]);
40
46
  },
41
47
  migrateToLatest: run(Effect.succeed(resolved)),
42
48
  };
@@ -1 +1 @@
1
- {"version":3,"file":"migrationSteps.js","sourceRoot":"","sources":["../../src/server/migrationSteps.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"migrationSteps.js","sourceRoot":"","sources":["../../src/server/migrationSteps.ts"],"names":[],"mappings":"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"}
package/docs/roadmap.md CHANGED
@@ -1,8 +1,12 @@
1
1
  # Roadmap
2
2
 
3
- - [x] Prepare migrations and optional fixtures once in a closed template database.
4
- - [x] Supply native PostgreSQL services on a shared database with per-test rollback.
5
- - [x] Compose the wrapping transaction with `makeEffectIt`, harness scopes and nested `transact` savepoints.
6
- - [x] Clone isolated databases for real commits and drop them after use.
7
- - [x] Prove the database lifecycle against PostgreSQL and the public contracts with compiler and packed-consumer checks.
8
- - [x] Prepare the approved initial 0.1.0 release metadata.
3
+ ## Built
4
+
5
+ - Prepare migrations and optional fixtures once in a closed template database, supply native PostgreSQL services on a shared database, and roll back ordinary tests. [PostgreSQL lifecycle tests](../src/server/postgres.test.ts) and [transaction tests](../src/server/transaction.spec.ts) prove preparation and rollback.
6
+ - Compose the wrapping transaction with `makeEffectIt`, harness scopes and nested `transact` savepoints. [Runner tests](../src/server/vitest.spec.ts) and [application-layer tests](../src/server/applicationLayers.spec.ts) prove the composition.
7
+ - Clone isolated databases for real commits and drop them after success, typed failure, defects or interruption. [Isolation tests](../src/server/isolated.spec.ts) prove these lifetimes and worker cleanup; [cleanup failure tests](../src/server/cleanupFailure.spec.ts) prove a failed drop becomes a defect carrying the SQL failure.
8
+ - Start with an empty schema using `prepare: false`, then use `migrationSteps` to stop at named migrations, seed old-schema data and finish the upgrade on the same database. [Migration-step tests](../src/server/migrationSteps.test.ts) prove committed backfills and rejection of unknown names and wrapping transactions; [lifecycle tests](../src/server/migrationStepsLifecycle.spec.ts) prove empty preparation, scoped loader resources, resume and rejection of backwards steps.
9
+ - Use `migrationUpgrade` to commit an old migration prefix and apply the remaining migrations, then compare live PostgreSQL columns with an Effect SQL model using `assertModelColumns`. [Upgrade tests](../src/server/migrationUpgrade.test.ts) prove committed old data, exact column types, drift detection and invalid-state rejection; [model-column tests](../src/server/assertModelColumns.test.ts) prove model field and type comparisons.
10
+ - Default to two pooled connections per database, allow `maxConnections` overrides and close administrative sessions between operations. [Connection-budget tests](../src/server/connectionBudget.spec.ts) measure twelve concurrent databases with default and explicit pool sizes; [idle-cleanup tests](../src/server/postgresIdleCleanup.spec.ts) prove idle workers release their connections.
11
+ - Preserve SQLSTATE `53300` on connection exhaustion and add connection-limit and worker-count guidance. [Connection-limit tests](../src/server/connectionLimit.spec.ts) prove setup and wrapper diagnostics against PostgreSQL and preserve other SQL failures' identity.
12
+ - Check public contracts with [compiler tests](../src/server/postgres.typecheck.test.ts), [migration-step compiler tests](../src/server/migrationSteps.typecheck.test.ts), [upgrade compiler tests](../src/server/migrationUpgrade.typecheck.test.ts) and the repository's packed-package consumer checks.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shivaedev/effect-test-sql",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "PostgreSQL rollback and real-commit test isolation for Effect",
5
5
  "homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/effect-test-sql#readme",
6
6
  "bugs": {
@@ -29,6 +29,7 @@
29
29
  "import": "./dist/server/*.js",
30
30
  "default": "./dist/server/*.js"
31
31
  },
32
+ "./internal/*": null,
32
33
  "./package.json": "./package.json"
33
34
  },
34
35
  "files": [
@@ -48,9 +49,9 @@
48
49
  "@effect/sql-pg": "4.0.4",
49
50
  "@effect/vitest": "4.0.4",
50
51
  "@shivaedev/effect-test": "0.5.0",
51
- "@shivaedev/quality": "0.22.0",
52
- "@shivaedev/test-story": "0.4.1",
53
- "@types/node": "24.19.1",
52
+ "@shivaedev/quality": "0.23.0",
53
+ "@shivaedev/test-story": "0.5.0",
54
+ "@types/node": "24.19.2",
54
55
  "@typescript/native": "npm:typescript@7.0.2",
55
56
  "effect": "4.0.4",
56
57
  "typescript": "npm:@typescript/typescript6@6.0.2",
@@ -35,11 +35,18 @@ export const migrationSteps = Effect.fn("PostgresTest.migrationSteps")(function*
35
35
  });
36
36
  }
37
37
  return {
38
- migrateTo: (name: string) => {
39
- const target = resolved.find(([id, label]) => `${id}_${label}` === name);
40
- return target === undefined
41
- ? invalid(`Migration "${name}" is not in the loader.`)
42
- : run(Effect.succeed(resolved.filter(([id]) => id <= target[0])), target[0]);
38
+ migrateTo: (name: string | number) => {
39
+ const match = /^(?<id>\d+)(?:_(?<label>.+))?$/u.exec(String(name))?.groups;
40
+ const matches =
41
+ match === undefined ? [] : resolved.filter(([id, label]) => id === Number(match.id) && (match.label === undefined || label === match.label));
42
+ const [target] = matches;
43
+ if (target === undefined) {
44
+ return invalid(`Migration "${name}" is not in the loader.`);
45
+ }
46
+ if (matches.length > 1) {
47
+ return invalid(`Migration "${name}" is ambiguous in the loader.`);
48
+ }
49
+ return run(Effect.succeed(resolved.filter(([id]) => id <= target[0])), target[0]);
43
50
  },
44
51
  migrateToLatest: run(Effect.succeed(resolved)),
45
52
  };