@zeroxsolutions/db 9.3.5 → 9.4.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 CHANGED
@@ -1,3 +1,14 @@
1
+ ## 9.4.0 (2026-10-14)
2
+
3
+ ### 🚀 Features
4
+
5
+ - **db:** export the forced container remove from its own subpath ([fade0ea](https://github.com/zeroxsolutions/zeroxsolutions/commit/fade0ea))
6
+
7
+ ### ❤️ Thank You
8
+
9
+ - Claude Opus 5.5
10
+ - Lương Văn Tú
11
+
1
12
  ## 9.3.5 (2026-10-12)
2
13
 
3
14
  ### 🩹 Fixes
package/README.md CHANGED
@@ -39,6 +39,7 @@ declares `"."`, but no `src/index.ts` exists for the build to emit it from, so
39
39
  | `@zeroxsolutions/db/d1` | Cloudflare D1 (SQLite) entity, repository, unit of work, outbox, and types |
40
40
  | `@zeroxsolutions/db/utils` | Filter and order builders, `TableQuery` and the relational-read builder, the field-whitelist guard, the filter-value schema + operator enum, omit masks, pagination constants |
41
41
  | `@zeroxsolutions/db/testing` | A throwaway Postgres container carrying your schema, on the engine you name, plus the container timeout and a fake uuid v7 |
42
+ | `@zeroxsolutions/db/containers` | The forced remove that tears down a container you started yourself |
42
43
 
43
44
  ---
44
45
 
@@ -525,6 +526,25 @@ typecheck failure rather than a suite that quietly passed on a major you do not
525
526
  | `CONTAINER_TIMEOUT_MS` | 180s, for the hook that starts a container: an image pull on a cold machine outruns vitest's 5s default |
526
527
  | `fakeUuidV7` | Random bits in a v7 layout: it passes a v7 check, and is not time-ordered like a minted id |
527
528
 
529
+ ### A container you started yourself
530
+
531
+ A global setup that starts its own container, to migrate a template or several databases, tears it down
532
+ with `removeContainer` from `@zeroxsolutions/db/containers`, not with the container's `stop()`:
533
+
534
+ ```ts
535
+ import { removeContainer } from '@zeroxsolutions/db/containers';
536
+
537
+ const container = await new PostgreSqlContainer(image).start();
538
+ // ...
539
+ return () => removeContainer(container);
540
+ ```
541
+
542
+ `stop()` sends a stop and then a separate remove, and on a loaded host the daemon answers that remove
543
+ with 409 "container is running", failing a suite whose every case passed. `removeContainer` is one forced
544
+ remove, volumes included, and it is what `startDisposablePostgres`'s `stop()` calls. It rejects for a
545
+ container the daemon no longer knows, so a teardown that runs twice shows. This subpath imports nothing
546
+ but `@testcontainers/postgresql`, so an e2e project that installs no `drizzle-kit` can use it.
547
+
528
548
  ---
529
549
 
530
550
  ## Building
@@ -0,0 +1,2 @@
1
+ export * from './remove-container.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/containers/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1 @@
1
+ export * from './remove-container.js';
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Removes `container` and its volumes in one forced call, which the daemon carries out as a kill and a
3
+ * removal together. The container's own stop sends a stop and then a remove, and on a loaded host that
4
+ * remove answered 409 "container is running", failing a suite whose every case had passed
5
+ * (testcontainers@12.1.0, Docker Desktop 29).
6
+ * @param container - a container started through `@testcontainers/postgresql`.
7
+ * @throws whatever the daemon answers, including that it knows no such container, so a second teardown
8
+ * of the same container shows.
9
+ */
10
+ export declare function removeContainer(container: {
11
+ getId(): string;
12
+ }): Promise<void>;
13
+ //# sourceMappingURL=remove-container.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remove-container.d.ts","sourceRoot":"","sources":["../../src/containers/remove-container.ts"],"names":[],"mappings":"AAOA;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE;IAAE,KAAK,IAAI,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAGnF"}
@@ -0,0 +1,24 @@
1
+ import { createRequire } from 'node:module';
2
+ /**
3
+ * Removes `container` and its volumes in one forced call, which the daemon carries out as a kill and a
4
+ * removal together. The container's own stop sends a stop and then a remove, and on a loaded host that
5
+ * remove answered 409 "container is running", failing a suite whose every case had passed
6
+ * (testcontainers@12.1.0, Docker Desktop 29).
7
+ * @param container - a container started through `@testcontainers/postgresql`.
8
+ * @throws whatever the daemon answers, including that it knows no such container, so a second teardown
9
+ * of the same container shows.
10
+ */
11
+ export async function removeContainer(container) {
12
+ const runtime = await containerRuntime();
13
+ await runtime.container.getById(container.getId()).remove({ force: true, v: true });
14
+ }
15
+ /**
16
+ * The runtime client of the testcontainers that `@testcontainers/postgresql` itself runs on. It is
17
+ * resolved from that package rather than declared here, so a consumer installs nothing beyond the peer it
18
+ * already has, and the client is the one the container was started through.
19
+ */
20
+ function containerRuntime() {
21
+ const fromPostgres = createRequire(createRequire(import.meta.url).resolve('@testcontainers/postgresql'));
22
+ const { getContainerRuntimeClient } = fromPostgres('testcontainers');
23
+ return getContainerRuntimeClient();
24
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"disposable-postgres.d.ts","sourceRoot":"","sources":["../../src/testing/disposable-postgres.ts"],"names":[],"mappings":"AAGA,OAAO,EAAW,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAG3E,2FAA2F;AAC3F,eAAO,MAAM,oBAAoB,SAAU,CAAC;AAE5C,8FAA8F;AAC9F,MAAM,WAAW,kBAAkB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACzE,EAAE,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAChC;;;OAGG;IACH,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAsB,uBAAuB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnF,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAqBtC"}
1
+ {"version":3,"file":"disposable-postgres.d.ts","sourceRoot":"","sources":["../../src/testing/disposable-postgres.ts"],"names":[],"mappings":"AAEA,OAAO,EAAW,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAI3E,2FAA2F;AAC3F,eAAO,MAAM,oBAAoB,SAAU,CAAC;AAE5C,8FAA8F;AAC9F,MAAM,WAAW,kBAAkB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACzE,EAAE,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAChC;;;OAGG;IACH,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAsB,uBAAuB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnF,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAiBtC"}
@@ -1,8 +1,8 @@
1
- import { createRequire } from 'node:module';
2
1
  import { PostgreSqlContainer } from '@testcontainers/postgresql';
3
2
  import { generateDrizzleJson, generateMigration } from 'drizzle-kit/api';
4
3
  import { drizzle } from 'drizzle-orm/postgres-js';
5
4
  import postgres from 'postgres';
5
+ import { removeContainer } from '../containers/index.js';
6
6
  /** Pulling the image on a cold machine costs far more than vitest's 5s default timeout. */
7
7
  export const CONTAINER_TIMEOUT_MS = 180_000;
8
8
  /**
@@ -27,24 +27,10 @@ export async function startDisposablePostgres(schema, image) {
27
27
  connectionString,
28
28
  async stop() {
29
29
  await client.end();
30
- // One forced remove, which the daemon carries out as a kill and a removal together. The container's
31
- // own stop sends a stop and then a remove, and on a loaded host that remove answered 409 "container is
32
- // running", failing a suite whose every case had passed (testcontainers@12.1.0, Docker Desktop 29).
33
- const runtime = await containerRuntime();
34
- await runtime.container.getById(container.getId()).remove({ force: true, v: true });
30
+ await removeContainer(container);
35
31
  },
36
32
  };
37
33
  }
38
- /**
39
- * The runtime client of the testcontainers that `@testcontainers/postgresql` itself runs on. It is
40
- * resolved from that package rather than declared here, so a consumer installs nothing beyond the peer it
41
- * already has, and the client is the one the container was started through.
42
- */
43
- function containerRuntime() {
44
- const fromPostgres = createRequire(createRequire(import.meta.url).resolve('@testcontainers/postgresql'));
45
- const { getContainerRuntimeClient } = fromPostgres('testcontainers');
46
- return getContainerRuntimeClient();
47
- }
48
34
  /**
49
35
  * Prints a server WARNING as one line and drops every lower severity. Left to its default, postgres.js
50
36
  * prints each notice as a raw object, and a full-text search for a stop word alone raises two per
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeroxsolutions/db",
3
- "version": "9.3.5",
3
+ "version": "9.4.0",
4
4
  "description": "Generic Drizzle toolkit - the id column and audit timestamps a table takes, a base repository, the unit-of-work and transactional outbox impls, and the filter/sort/pagination query helpers, for PostgreSQL (postgres-js) and Cloudflare D1; ships no domain schema. Shared across ZeroXSolutions services.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -32,6 +32,11 @@
32
32
  "types": "./dist/testing/index.d.ts",
33
33
  "import": "./dist/testing/index.js",
34
34
  "default": "./dist/testing/index.js"
35
+ },
36
+ "./containers": {
37
+ "types": "./dist/containers/index.d.ts",
38
+ "import": "./dist/containers/index.js",
39
+ "default": "./dist/containers/index.js"
35
40
  }
36
41
  },
37
42
  "dependencies": {