@zeroxsolutions/db 10.2.0 → 10.3.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,13 @@
1
+ ## 10.3.0 (2026-10-20)
2
+
3
+ ### 🚀 Features
4
+
5
+ - **db:** take a relation out of a read for a finder to load itself ([f57181e](https://github.com/zeroxsolutions/zeroxsolutions/commit/f57181e))
6
+
7
+ ### ❤️ Thank You
8
+
9
+ - Lương Văn Tú
10
+
1
11
  ## 10.2.0 (2026-10-20)
2
12
 
3
13
  ### 🚀 Features
package/README.md CHANGED
@@ -11,7 +11,8 @@ A shared database library providing base entities, repository patterns, and quer
11
11
  - Reads that name what they load: a `read` of columns and nested relations, a `filter` tree, an `orderBy` with the
12
12
  primary key appended, and a page window only when one is given
13
13
  - `READS` (`defineReads`), declared once per context: each table's order as a relation and its `virtual` filter and
14
- order names; `relationsOf` for the relationships a permit list names
14
+ order names; `relationsOf` for the relationships a permit list names; `withoutRelation` for a relation a finder
15
+ loads through one of its own
15
16
  - The filter vocabulary the wire toolkit is generic over: `filterOperatorSchema` / `FilterOperator` (15 operators),
16
17
  `filterValueSchema`, and typed refusals (`UnknownQueryField`, `UnknownReadName`)
17
18
  - Transaction support for all operations
@@ -34,13 +35,13 @@ Import from the subpath that matches your driver. **There is no root entry**: `p
34
35
  declares `"."`, but no `src/index.ts` exists for the build to emit it from, so
35
36
  `from '@zeroxsolutions/db'` does not resolve.
36
37
 
37
- | Export path | Description |
38
- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
39
- | `@zeroxsolutions/db/postgres` | PostgreSQL entity, repository, view repository, unit of work, outbox, and types |
40
- | `@zeroxsolutions/db/d1` | Cloudflare D1 (SQLite) entity, repository, unit of work, outbox, and types |
41
- | `@zeroxsolutions/db/utils` | `defineReads`, `relationsOf`, the read and order types, the filter-value schema + operator enum, the refusals, omit masks |
42
- | `@zeroxsolutions/db/testing` | A throwaway Postgres container carrying your schema, on the engine you name, plus the container timeout and a fake uuid v7 |
43
- | `@zeroxsolutions/db/containers` | The forced remove that tears down a container you started yourself |
38
+ | Export path | Description |
39
+ | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
40
+ | `@zeroxsolutions/db/postgres` | PostgreSQL entity, repository, view repository, unit of work, outbox, and types |
41
+ | `@zeroxsolutions/db/d1` | Cloudflare D1 (SQLite) entity, repository, unit of work, outbox, and types |
42
+ | `@zeroxsolutions/db/utils` | `defineReads`, `relationsOf`, `withoutRelation`, the read and order types, the filter-value schema + operator enum, the refusals, omit masks |
43
+ | `@zeroxsolutions/db/testing` | A throwaway Postgres container carrying your schema, on the engine you name, plus the container timeout and a fake uuid v7 |
44
+ | `@zeroxsolutions/db/containers` | The forced remove that tears down a container you started yourself |
44
45
 
45
46
  ---
46
47
 
@@ -417,6 +418,20 @@ them. A permit list carries it as `relationships`, `permitsFor` checks a relatio
417
418
  `findOptionsOf` in `@zeroxsolutions/jsonapi` finds an included relation's fieldset through it. It throws on a key that
418
419
  is no table export (a table's SQL name is not one).
419
420
 
421
+ ### withoutRelation
422
+
423
+ `withoutRelation(options, path)` answers the options with the relation at `path` taken out of their read, and every
424
+ relation the read named under it; the options are unchanged where the read names no such relation. A relation load has
425
+ no condition of its own, so a finder that may answer only some of a relation's rows, such as the caller's own, takes
426
+ the relation out and loads it through a finder that carries the condition:
427
+
428
+ ```ts
429
+ findOneVisibleById(id: PostId, memberId: MemberId, options?: FindOneOptions<typeof schema, 'postsTable'>) {
430
+ // ['review', 'appeals'] leaves the review and takes its appeals out
431
+ return this.findOne(eq(postsTable.id, id), withoutRelation(options, ['reviews']));
432
+ }
433
+ ```
434
+
420
435
  ### Reading the query off a request
421
436
 
422
437
  This package ships **no** query schema and knows no wire format. Parse the request with
@@ -3,4 +3,5 @@ export * from './omit.js';
3
3
  export { UnknownReadName, type ColumnName, type MatchOptions, type OrderEntry, type Read, type RelationRead, } from './read.js';
4
4
  export * from './reads.js';
5
5
  export * from './relationships.js';
6
+ export * from './without-relation.js';
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AACrB,cAAc,WAAW,CAAC;AAC1B,OAAO,EACL,eAAe,EACf,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,IAAI,EACT,KAAK,YAAY,GAClB,MAAM,WAAW,CAAC;AACnB,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AACrB,cAAc,WAAW,CAAC;AAC1B,OAAO,EACL,eAAe,EACf,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,IAAI,EACT,KAAK,YAAY,GAClB,MAAM,WAAW,CAAC;AACnB,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC"}
@@ -3,3 +3,4 @@ export * from './omit.js';
3
3
  export { UnknownReadName, } from './read.js';
4
4
  export * from './reads.js';
5
5
  export * from './relationships.js';
6
+ export * from './without-relation.js';
@@ -0,0 +1,11 @@
1
+ import type { Read } from './read.js';
2
+ /**
3
+ * `options` with the relation at `path` taken out of its read, and with it everything the read named under that
4
+ * relation; `options` unchanged where its read names no such relation. `['review', 'appeals']` takes the appeals out
5
+ * of the review and leaves the review. A finder takes a relation out this way where the rows it may answer are a
6
+ * subset no relation load can scope, and loads that relation through a finder of its own.
7
+ */
8
+ export declare function withoutRelation<O extends {
9
+ read?: Read;
10
+ }>(options: O | undefined, path: readonly [string, ...string[]]): O | undefined;
11
+ //# sourceMappingURL=without-relation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"without-relation.d.ts","sourceRoot":"","sources":["../../src/utils/without-relation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE,IAAI,CAAA;CAAE,EACvD,OAAO,EAAE,CAAC,GAAG,SAAS,EACtB,IAAI,EAAE,SAAS,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,GACnC,CAAC,GAAG,SAAS,CAGf"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * `options` with the relation at `path` taken out of its read, and with it everything the read named under that
3
+ * relation; `options` unchanged where its read names no such relation. `['review', 'appeals']` takes the appeals out
4
+ * of the review and leaves the review. A finder takes a relation out this way where the rows it may answer are a
5
+ * subset no relation load can scope, and loads that relation through a finder of its own.
6
+ */
7
+ export function withoutRelation(options, path) {
8
+ if (options?.read === undefined)
9
+ return options;
10
+ return { ...options, read: readWithout(options.read, path) };
11
+ }
12
+ function readWithout(read, path) {
13
+ const [name, ...below] = path;
14
+ const relation = read.relations?.[name];
15
+ if (relation === undefined)
16
+ return read;
17
+ const others = Object.fromEntries(Object.entries(read.relations ?? {}).filter(([key]) => key !== name));
18
+ const [next, ...rest] = below;
19
+ return {
20
+ ...read,
21
+ relations: next === undefined ? others : { ...others, [name]: readWithout(relation, [next, ...rest]) },
22
+ };
23
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeroxsolutions/db",
3
- "version": "10.2.0",
3
+ "version": "10.3.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",