@shivaedev/effect-test-sql 0.5.0 → 0.5.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.1 - 2026-10-17
4
+
5
+ ### Fixed
6
+
7
+ - 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.
8
+
3
9
  ## 0.5.0 - 2026-10-17
4
10
 
5
11
  ### 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/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.1",
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": {
@@ -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
  };