@zudojs/database 1.3.2 → 1.5.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.
Files changed (98) hide show
  1. package/README.md +83 -0
  2. package/dist/cache/cache.memory.d.ts +0 -1
  3. package/dist/cache/cache.memory.js +0 -1
  4. package/dist/cache/index.d.ts +0 -1
  5. package/dist/cache/index.js +0 -1
  6. package/dist/database/database.core.d.ts +20 -7
  7. package/dist/database/database.core.js +8 -6
  8. package/dist/database/index.d.ts +0 -1
  9. package/dist/database/index.js +0 -1
  10. package/dist/databaseClient/databaseClient.core.d.ts +30 -15
  11. package/dist/databaseClient/databaseClient.core.js +19 -3
  12. package/dist/databaseClient/databaseClient.errors.d.ts +19 -2
  13. package/dist/databaseClient/databaseClient.errors.js +8 -1
  14. package/dist/databaseClient/databaseClient.logger.d.ts +0 -1
  15. package/dist/databaseClient/databaseClient.logger.js +0 -1
  16. package/dist/databaseClient/databaseClient.type.d.ts +52 -0
  17. package/dist/databaseClient/databaseClient.type.js +11 -0
  18. package/dist/databaseClient/index.d.ts +2 -3
  19. package/dist/databaseClient/index.js +1 -2
  20. package/dist/databaseConnection/databaseConnection.manager.d.ts +0 -1
  21. package/dist/databaseConnection/databaseConnection.manager.js +0 -1
  22. package/dist/databaseConnection/index.d.ts +0 -1
  23. package/dist/databaseConnection/index.js +0 -1
  24. package/dist/databaseType/databaseType.type.d.ts +6 -5
  25. package/dist/databaseType/databaseType.type.js +0 -1
  26. package/dist/databaseType/index.d.ts +0 -1
  27. package/dist/databaseType/index.js +0 -1
  28. package/dist/health/health.check.d.ts +0 -1
  29. package/dist/health/health.check.js +0 -1
  30. package/dist/health/index.d.ts +0 -1
  31. package/dist/health/index.js +0 -1
  32. package/dist/index.d.ts +3 -4
  33. package/dist/index.js +2 -3
  34. package/dist/locks/index.d.ts +0 -1
  35. package/dist/locks/index.js +0 -1
  36. package/dist/locks/locks.core.d.ts +8 -7
  37. package/dist/locks/locks.core.js +2 -1
  38. package/dist/migration/index.d.ts +0 -1
  39. package/dist/migration/index.js +0 -1
  40. package/dist/migration/migration.dialect.d.ts +0 -1
  41. package/dist/migration/migration.dialect.js +0 -1
  42. package/dist/migration/migration.helpers.d.ts +4 -4
  43. package/dist/migration/migration.helpers.js +4 -1
  44. package/dist/migration/migration.runner.d.ts +16 -5
  45. package/dist/migration/migration.runner.js +69 -17
  46. package/dist/migration/migration.types.d.ts +20 -6
  47. package/dist/migration/migration.types.js +0 -1
  48. package/dist/pagination/index.d.ts +1 -1
  49. package/dist/pagination/index.js +1 -1
  50. package/dist/pagination/pagination.core.d.ts +0 -1
  51. package/dist/pagination/pagination.core.js +0 -1
  52. package/dist/pagination/pagination.cursorError.d.ts +0 -1
  53. package/dist/pagination/pagination.cursorError.js +0 -1
  54. package/dist/pagination/pagination.keyset.d.ts +12 -1
  55. package/dist/pagination/pagination.keyset.js +15 -3
  56. package/dist/pagination/pagination.keysetDirection.d.ts +0 -1
  57. package/dist/pagination/pagination.keysetDirection.js +0 -1
  58. package/dist/pagination/pagination.keysetValue.d.ts +37 -0
  59. package/dist/pagination/pagination.keysetValue.js +55 -0
  60. package/dist/queryBuilder/index.d.ts +0 -1
  61. package/dist/queryBuilder/index.js +0 -1
  62. package/dist/queryBuilder/queryBuilder.core.d.ts +0 -1
  63. package/dist/queryBuilder/queryBuilder.core.js +0 -1
  64. package/dist/queryBuilder/queryBuilder.factory.d.ts +0 -1
  65. package/dist/queryBuilder/queryBuilder.factory.js +0 -1
  66. package/dist/queryBuilder/queryBuilder.filter.d.ts +0 -1
  67. package/dist/queryBuilder/queryBuilder.filter.js +0 -1
  68. package/dist/queryBuilder/queryBuilder.prisma.d.ts +0 -1
  69. package/dist/queryBuilder/queryBuilder.prisma.js +0 -1
  70. package/dist/queryBuilder/queryBuilder.type.d.ts +0 -1
  71. package/dist/queryBuilder/queryBuilder.type.js +0 -1
  72. package/dist/relations/index.d.ts +0 -1
  73. package/dist/relations/index.js +0 -1
  74. package/dist/relations/relations.definition.d.ts +0 -1
  75. package/dist/relations/relations.definition.js +0 -1
  76. package/dist/repository/index.d.ts +1 -2
  77. package/dist/repository/index.js +0 -1
  78. package/dist/repository/repository.base.d.ts +27 -1
  79. package/dist/repository/repository.base.js +13 -2
  80. package/dist/repository/repository.delegate.d.ts +0 -1
  81. package/dist/repository/repository.delegate.js +0 -1
  82. package/dist/repository/repository.errors.d.ts +3 -1
  83. package/dist/repository/repository.errors.js +17 -2
  84. package/dist/seed/index.d.ts +0 -1
  85. package/dist/seed/index.js +0 -1
  86. package/dist/seed/seed.runner.d.ts +14 -12
  87. package/dist/seed/seed.runner.js +0 -1
  88. package/dist/transaction/index.d.ts +0 -1
  89. package/dist/transaction/index.js +0 -1
  90. package/dist/transaction/transaction.backoff.d.ts +0 -1
  91. package/dist/transaction/transaction.backoff.js +0 -1
  92. package/dist/transaction/transaction.core.d.ts +27 -11
  93. package/dist/transaction/transaction.core.js +11 -2
  94. package/dist/unitOfWork/index.d.ts +0 -1
  95. package/dist/unitOfWork/index.js +0 -1
  96. package/dist/unitOfWork/unitOfWork.core.d.ts +12 -11
  97. package/dist/unitOfWork/unitOfWork.core.js +0 -1
  98. package/package.json +4 -4
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Cursor value comparisons for keyset pagination.
3
+ *
4
+ * A `Date` is encoded into a cursor as its ISO string, which carries
5
+ * millisecond precision. PostgreSQL `timestamp`/`timestamptz` columns keep
6
+ * microseconds unless declared with precision `(3)`, and Prisma hands rows
7
+ * back as JavaScript `Date`s, so the exact stored value is not available
8
+ * to the cursor. Comparing `field = C` against such a column matched
9
+ * nothing and `field < C` skipped every row created in the same
10
+ * millisecond as the cursor row.
11
+ *
12
+ * A date cursor value is therefore compared as its millisecond bucket
13
+ * `[C, C + 1ms)`: rows outside the bucket are ordered by the strict
14
+ * comparison, and rows inside it fall through to the next sort field (the
15
+ * id tiebreaker `paginateCursor` appends). On millisecond-precision
16
+ * columns this is exactly the old `=`/`<`/`>` comparison.
17
+ */
18
+ /**
19
+ * Whether a cursor value was encoded from a `Date` by `createKeysetCursor`.
20
+ */
21
+ export declare function isDateCursorValue(value: unknown): value is string;
22
+ /**
23
+ * The ISO string one millisecond after a date cursor value: the exclusive
24
+ * upper bound of its millisecond bucket.
25
+ */
26
+ export declare function nextMillisecond(value: string): string;
27
+ /**
28
+ * The Prisma filter selecting rows that tie with a cursor value on a sort
29
+ * field: `{ equals }` for ordinary values, the millisecond bucket for dates.
30
+ */
31
+ export declare function keysetTieFilter(value: unknown): Readonly<Record<string, unknown>>;
32
+ /**
33
+ * The Prisma filter selecting rows strictly after (`"gt"`) or before
34
+ * (`"lt"`) a cursor value on a sort field. For a date the boundary is the
35
+ * whole millisecond bucket: `lt C` below it, `gte C + 1ms` above it.
36
+ */
37
+ export declare function keysetStrictFilter(value: unknown, comparison: "gt" | "lt"): Readonly<Record<string, unknown>>;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Cursor value comparisons for keyset pagination.
3
+ *
4
+ * A `Date` is encoded into a cursor as its ISO string, which carries
5
+ * millisecond precision. PostgreSQL `timestamp`/`timestamptz` columns keep
6
+ * microseconds unless declared with precision `(3)`, and Prisma hands rows
7
+ * back as JavaScript `Date`s, so the exact stored value is not available
8
+ * to the cursor. Comparing `field = C` against such a column matched
9
+ * nothing and `field < C` skipped every row created in the same
10
+ * millisecond as the cursor row.
11
+ *
12
+ * A date cursor value is therefore compared as its millisecond bucket
13
+ * `[C, C + 1ms)`: rows outside the bucket are ordered by the strict
14
+ * comparison, and rows inside it fall through to the next sort field (the
15
+ * id tiebreaker `paginateCursor` appends). On millisecond-precision
16
+ * columns this is exactly the old `=`/`<`/`>` comparison.
17
+ */
18
+ /** Exactly the shape `Date.prototype.toISOString` emits. */
19
+ const ISO_MILLISECOND_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
20
+ /**
21
+ * Whether a cursor value was encoded from a `Date` by `createKeysetCursor`.
22
+ */
23
+ export function isDateCursorValue(value) {
24
+ return (typeof value === "string" &&
25
+ ISO_MILLISECOND_PATTERN.test(value) &&
26
+ !Number.isNaN(Date.parse(value)));
27
+ }
28
+ /**
29
+ * The ISO string one millisecond after a date cursor value: the exclusive
30
+ * upper bound of its millisecond bucket.
31
+ */
32
+ export function nextMillisecond(value) {
33
+ return new Date(Date.parse(value) + 1).toISOString();
34
+ }
35
+ /**
36
+ * The Prisma filter selecting rows that tie with a cursor value on a sort
37
+ * field: `{ equals }` for ordinary values, the millisecond bucket for dates.
38
+ */
39
+ export function keysetTieFilter(value) {
40
+ if (isDateCursorValue(value)) {
41
+ return { gte: value, lt: nextMillisecond(value) };
42
+ }
43
+ return { equals: value };
44
+ }
45
+ /**
46
+ * The Prisma filter selecting rows strictly after (`"gt"`) or before
47
+ * (`"lt"`) a cursor value on a sort field. For a date the boundary is the
48
+ * whole millisecond bucket: `lt C` below it, `gte C + 1ms` above it.
49
+ */
50
+ export function keysetStrictFilter(value, comparison) {
51
+ if (isDateCursorValue(value) && comparison === "gt") {
52
+ return { gte: nextMillisecond(value) };
53
+ }
54
+ return { [comparison]: value };
55
+ }
@@ -8,4 +8,3 @@ export { createQueryBuilder } from "./queryBuilder.factory.js";
8
8
  export { toPrismaWhere, toPrismaArgs, toPrismaOrderBy, toPrismaSelect, toPrismaSkipTake, type PrismaWhere, type PrismaQueryArgs, type ToPrismaArgsOptions, } from "./queryBuilder.prisma.js";
9
9
  export type { QueryCondition, QueryFilter, QueryOperator, RelationOperator, QueryBuilderState, } from "./queryBuilder.type.js";
10
10
  export { equals, notEquals, inList, notInList, lessThan, lessThanOrEqual, greaterThan, greaterThanOrEqual, contains, startsWith, endsWith, isNull, isNotNull, and, or, not, condition, allOf, anyOf, fromObject, dateRange, oneOf, noneOf, optionalEquals, optionalContains, hasConditions, flattenAnd, cloneFilter, between, matchesPattern, isEmpty, isNotEmpty, dateOnly, isBefore, isAfter, isBetween, notCondition, relational, } from "./queryBuilder.filter.js";
11
- //# sourceMappingURL=index.d.ts.map
@@ -7,4 +7,3 @@ export { QueryBuilder } from "./queryBuilder.core.js";
7
7
  export { createQueryBuilder } from "./queryBuilder.factory.js";
8
8
  export { toPrismaWhere, toPrismaArgs, toPrismaOrderBy, toPrismaSelect, toPrismaSkipTake, } from "./queryBuilder.prisma.js";
9
9
  export { equals, notEquals, inList, notInList, lessThan, lessThanOrEqual, greaterThan, greaterThanOrEqual, contains, startsWith, endsWith, isNull, isNotNull, and, or, not, condition, allOf, anyOf, fromObject, dateRange, oneOf, noneOf, optionalEquals, optionalContains, hasConditions, flattenAnd, cloneFilter, between, matchesPattern, isEmpty, isNotEmpty, dateOnly, isBefore, isAfter, isBetween, notCondition, relational, } from "./queryBuilder.filter.js";
10
- //# sourceMappingURL=index.js.map
@@ -175,4 +175,3 @@ export declare class QueryBuilder<TField extends string = string> {
175
175
  */
176
176
  private addCondition;
177
177
  }
178
- //# sourceMappingURL=queryBuilder.core.d.ts.map
@@ -417,4 +417,3 @@ function deepFreeze(value) {
417
417
  }
418
418
  return value;
419
419
  }
420
- //# sourceMappingURL=queryBuilder.core.js.map
@@ -3,4 +3,3 @@ import { QueryBuilder } from "./queryBuilder.core.js";
3
3
  * Creates a new query builder.
4
4
  */
5
5
  export declare function createQueryBuilder<TField extends string = string>(): QueryBuilder<TField>;
6
- //# sourceMappingURL=queryBuilder.factory.d.ts.map
@@ -5,4 +5,3 @@ import { QueryBuilder } from "./queryBuilder.core.js";
5
5
  export function createQueryBuilder() {
6
6
  return new QueryBuilder();
7
7
  }
8
- //# sourceMappingURL=queryBuilder.factory.js.map
@@ -167,4 +167,3 @@ export declare function flattenAnd(filter: QueryFilter): QueryCondition[];
167
167
  */
168
168
  export declare function cloneFilter(filter: QueryFilter): QueryFilter;
169
169
  export declare function cloneFilter(filter?: QueryFilter): QueryFilter | undefined;
170
- //# sourceMappingURL=queryBuilder.filter.d.ts.map
@@ -494,4 +494,3 @@ function validateDate(value, name) {
494
494
  throw new TypeError(`Invalid ${name} date.`);
495
495
  }
496
496
  }
497
- //# sourceMappingURL=queryBuilder.filter.js.map
@@ -57,4 +57,3 @@ export declare function toPrismaSkipTake(pagination?: PaginationInput, offset?:
57
57
  readonly skip?: number;
58
58
  readonly take?: number;
59
59
  };
60
- //# sourceMappingURL=queryBuilder.prisma.d.ts.map
@@ -225,4 +225,3 @@ function validateFieldName(field) {
225
225
  throw new TypeError(`Invalid query field name "${String(field)}".`);
226
226
  }
227
227
  }
228
- //# sourceMappingURL=queryBuilder.prisma.js.map
@@ -45,4 +45,3 @@ export interface QueryBuilderState<TField extends string = string> {
45
45
  readonly select?: readonly TField[];
46
46
  readonly include?: readonly RelationInclude[];
47
47
  }
48
- //# sourceMappingURL=queryBuilder.type.d.ts.map
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=queryBuilder.type.js.map
@@ -4,4 +4,3 @@
4
4
  * Entity relation definitions and registry.
5
5
  */
6
6
  export { oneToOne, oneToMany, manyToOne, manyToMany, includeRelation, includeRelations, RelationRegistry, createRelationRegistry, validateRelation, validateInclude, toPrismaInclude, DEFAULT_INCLUDE_DEPTH, isRelationType, isCollectionRelation, isSingleRelation, type RelationDefinition, type RelationType, type RelationLoadOptions, type RelationInclude, type ToPrismaIncludeOptions, } from "./relations.definition.js";
7
- //# sourceMappingURL=index.d.ts.map
@@ -4,4 +4,3 @@
4
4
  * Entity relation definitions and registry.
5
5
  */
6
6
  export { oneToOne, oneToMany, manyToOne, manyToMany, includeRelation, includeRelations, RelationRegistry, createRelationRegistry, validateRelation, validateInclude, toPrismaInclude, DEFAULT_INCLUDE_DEPTH, isRelationType, isCollectionRelation, isSingleRelation, } from "./relations.definition.js";
7
- //# sourceMappingURL=index.js.map
@@ -181,4 +181,3 @@ export declare function isCollectionRelation(relation: RelationDefinition): bool
181
181
  * Returns whether the relation represents a single entity.
182
182
  */
183
183
  export declare function isSingleRelation(relation: RelationDefinition): boolean;
184
- //# sourceMappingURL=relations.definition.d.ts.map
@@ -379,4 +379,3 @@ function validateFieldName(field) {
379
379
  throw new TypeError(`Invalid relation field name "${String(field)}".`);
380
380
  }
381
381
  }
382
- //# sourceMappingURL=relations.definition.js.map
@@ -3,7 +3,6 @@
3
3
  *
4
4
  * Generic repository pattern with Prisma delegate support.
5
5
  */
6
- export { BaseRepository, type BaseRepositoryOptions, type SoftDeleteOptions, type CursorQueryOptions, type TransactionClientLike, } from "./repository.base.js";
6
+ export { BaseRepository, type BaseRepositoryOptions, type SoftDeleteOptions, type CreateCursorOptions, type CursorQueryOptions, type TransactionClientLike, } from "./repository.base.js";
7
7
  export { type RepositoryDelegate, type RepositoryDelegateOperations, } from "./repository.delegate.js";
8
8
  export { mapRepositoryError, isPrismaErrorLike, toDatabaseOperation, toErrorMetadata, createAbortError, createTimeoutError, type RepositoryOperation, type RepositoryErrorContext, type PrismaErrorLike, } from "./repository.errors.js";
9
- //# sourceMappingURL=index.d.ts.map
@@ -6,4 +6,3 @@
6
6
  export { BaseRepository, } from "./repository.base.js";
7
7
  export {} from "./repository.delegate.js";
8
8
  export { mapRepositoryError, isPrismaErrorLike, toDatabaseOperation, toErrorMetadata, createAbortError, createTimeoutError, } from "./repository.errors.js";
9
- //# sourceMappingURL=index.js.map
@@ -1,5 +1,6 @@
1
1
  import type { DatabaseOperationOptions, PaginatedResult, QueryOptions, Repository, SoftDeletableRepository, SortInput } from "../databaseType/databaseType.type.js";
2
2
  import { type CursorPaginatedResult } from "../pagination/pagination.core.js";
3
+ import { type KeysetDirection } from "../pagination/pagination.keysetDirection.js";
3
4
  import type { QueryBuilder } from "../queryBuilder/queryBuilder.core.js";
4
5
  import type { QueryBuilderState } from "../queryBuilder/queryBuilder.type.js";
5
6
  import type { RelationLoadOptions, RelationRegistry } from "../relations/relations.definition.js";
@@ -61,6 +62,22 @@ export interface CursorQueryOptions<TField extends string = string> extends Data
61
62
  */
62
63
  readonly sort?: readonly SortInput<TField>[];
63
64
  }
65
+ /**
66
+ * Options for {@link BaseRepository.createCursor}.
67
+ */
68
+ export interface CreateCursorOptions<TField extends string = string> {
69
+ /**
70
+ * The sort the page is fetched with, as passed to `paginateCursor`. The
71
+ * id field is appended as a tiebreaker when absent, exactly as
72
+ * `paginateCursor` does.
73
+ */
74
+ readonly sort?: readonly SortInput<TField>[];
75
+ /**
76
+ * `"forward"` (default) selects the rows after `row`; `"backward"` the
77
+ * rows before it (a `previousCursor`).
78
+ */
79
+ readonly direction?: KeysetDirection;
80
+ }
64
81
  /**
65
82
  * A Prisma transaction client (or any object exposing model delegates).
66
83
  */
@@ -133,6 +150,16 @@ export declare abstract class BaseRepository<TEntity, TId = string, TCreateInput
133
150
  * before any query runs.
134
151
  */
135
152
  paginateCursor<TField extends string = string>(filter?: TWhereInput, options?: CursorQueryOptions<TField>): Promise<CursorPaginatedResult<TEntity>>;
153
+ /**
154
+ * Builds a cursor positioned at `row` that `paginateCursor` accepts for
155
+ * the same `sort`: the id tiebreaker is appended and the cursor is
156
+ * signed with `cursorSecret` when configured.
157
+ *
158
+ * `createKeysetCursor(row, sort)` with the bare sort produces a cursor
159
+ * without the tiebreaker, which `paginateCursor` rejects as missing the
160
+ * id field.
161
+ */
162
+ createCursor<TField extends string = string>(row: TEntity, options?: CreateCursorOptions<TField>): string;
136
163
  /**
137
164
  * Finds entities from a query builder (or its built state), applying the
138
165
  * filter, sort, select, include and pagination it carries.
@@ -234,4 +261,3 @@ export declare abstract class BaseRepository<TEntity, TId = string, TCreateInput
234
261
  private buildCursorSort;
235
262
  private rebind;
236
263
  }
237
- //# sourceMappingURL=repository.base.d.ts.map
@@ -1,6 +1,6 @@
1
1
  import { DatabaseError } from "@zudojs/errors";
2
2
  import { createPaginationMeta, normalizeLimit, normalizePage, } from "../pagination/pagination.core.js";
3
- import { buildKeysetWhere, createKeysetPage, decodeKeysetCursor, } from "../pagination/pagination.keyset.js";
3
+ import { buildKeysetWhere, createKeysetCursor, createKeysetPage, decodeKeysetCursor, } from "../pagination/pagination.keyset.js";
4
4
  import { getKeysetDirection, keysetFetchSort, } from "../pagination/pagination.keysetDirection.js";
5
5
  import { toPrismaArgs, toPrismaOrderBy, } from "../queryBuilder/queryBuilder.prisma.js";
6
6
  import { toDelegateOperations, } from "./repository.delegate.js";
@@ -188,6 +188,18 @@ export class BaseRepository {
188
188
  direction,
189
189
  });
190
190
  }
191
+ /**
192
+ * Builds a cursor positioned at `row` that `paginateCursor` accepts for
193
+ * the same `sort`: the id tiebreaker is appended and the cursor is
194
+ * signed with `cursorSecret` when configured.
195
+ *
196
+ * `createKeysetCursor(row, sort)` with the bare sort produces a cursor
197
+ * without the tiebreaker, which `paginateCursor` rejects as missing the
198
+ * id field.
199
+ */
200
+ createCursor(row, options) {
201
+ return createKeysetCursor(row, this.buildCursorSort(options?.sort), this.cursorSecret, options?.direction ?? "forward");
202
+ }
191
203
  /**
192
204
  * Finds entities from a query builder (or its built state), applying the
193
205
  * filter, sort, select, include and pagination it carries.
@@ -536,4 +548,3 @@ function validateFieldName(field, name) {
536
548
  function lowerFirst(value) {
537
549
  return value.length === 0 ? value : value[0].toLowerCase() + value.slice(1);
538
550
  }
539
- //# sourceMappingURL=repository.base.js.map
@@ -90,4 +90,3 @@ export interface RepositoryDelegateOperations<TEntity, TId = string, TCreateInpu
90
90
  * model delegate accepts at runtime.
91
91
  */
92
92
  export declare function toDelegateOperations<TEntity, TId, TCreateInput, TUpdateInput, TWhereInput>(delegate: RepositoryDelegate<TEntity, TId, TCreateInput, TUpdateInput, TWhereInput>): RepositoryDelegateOperations<TEntity, TId, TCreateInput, TUpdateInput, TWhereInput>;
93
- //# sourceMappingURL=repository.delegate.d.ts.map
@@ -9,4 +9,3 @@
9
9
  export function toDelegateOperations(delegate) {
10
10
  return delegate;
11
11
  }
12
- //# sourceMappingURL=repository.delegate.js.map
@@ -40,6 +40,9 @@ export declare function toDatabaseOperation(operation: RepositoryOperation | str
40
40
  * - `P2034` serialization failure → `ERR_DATABASE_TRANSACTION` / 409 (retryable)
41
41
  * - `P2024` pool timeout → `ERR_DATABASE_TIMEOUT` / 503
42
42
  * - `P1xxx` connection failures → `ERR_DATABASE_CONNECTION` / 503
43
+ * - every other code `normalizeDatabaseError` knows (`P2000` value too
44
+ * long / 400, `P2004` constraint / 409, `P2011` null / 400, `P2014`,
45
+ * `P2015`, `P2018`, `P2028`) → the same status, code and exposure
43
46
  *
44
47
  * Existing `DatabaseError`s are returned unchanged.
45
48
  */
@@ -57,4 +60,3 @@ export declare function createTimeoutError(context: Omit<RepositoryErrorContext,
57
60
  * package accepts.
58
61
  */
59
62
  export declare function toErrorMetadata(value?: Readonly<Record<string, unknown>>): ErrorMetadata;
60
- //# sourceMappingURL=repository.errors.d.ts.map
@@ -1,5 +1,5 @@
1
1
  import { DatabaseError, DatabaseOperation, ErrorCode, } from "@zudojs/errors";
2
- import { isDatabaseErrorLike } from "../databaseClient/databaseClient.errors.js";
2
+ import { getPrismaCodeMapping, isDatabaseErrorLike, } from "../databaseClient/databaseClient.errors.js";
3
3
  const PRISMA_CODE_PATTERN = /^P\d{4}$/;
4
4
  /**
5
5
  * Determines whether a value looks like a Prisma known request error.
@@ -53,6 +53,9 @@ export function toDatabaseOperation(operation) {
53
53
  * - `P2034` serialization failure → `ERR_DATABASE_TRANSACTION` / 409 (retryable)
54
54
  * - `P2024` pool timeout → `ERR_DATABASE_TIMEOUT` / 503
55
55
  * - `P1xxx` connection failures → `ERR_DATABASE_CONNECTION` / 503
56
+ * - every other code `normalizeDatabaseError` knows (`P2000` value too
57
+ * long / 400, `P2004` constraint / 409, `P2011` null / 400, `P2014`,
58
+ * `P2015`, `P2018`, `P2028`) → the same status, code and exposure
56
59
  *
57
60
  * Existing `DatabaseError`s are returned unchanged.
58
61
  */
@@ -139,6 +142,16 @@ export function mapRepositoryError(error, context) {
139
142
  statusCode: 503,
140
143
  });
141
144
  }
145
+ const mapping = getPrismaCodeMapping(error.code);
146
+ if (mapping !== undefined) {
147
+ return new DatabaseError(`${context.model} ${context.operation} failed: ${lowerFirst(mapping.message)}`, {
148
+ ...options,
149
+ code: mapping.code,
150
+ statusCode: mapping.statusCode,
151
+ expose: mapping.expose,
152
+ metadata: { ...metadata, kind: mapping.kind },
153
+ });
154
+ }
142
155
  return new DatabaseError(`${context.model} ${context.operation} failed.`, {
143
156
  ...options,
144
157
  code: ErrorCode.DATABASE_QUERY,
@@ -222,6 +235,9 @@ function toMetadataValue(value) {
222
235
  }
223
236
  return String(value);
224
237
  }
238
+ function lowerFirst(value) {
239
+ return value.length === 0 ? value : value[0].toLowerCase() + value.slice(1);
240
+ }
225
241
  function extractTarget(meta) {
226
242
  const target = meta?.["target"];
227
243
  if (typeof target === "string") {
@@ -232,4 +248,3 @@ function extractTarget(meta) {
232
248
  }
233
249
  return undefined;
234
250
  }
235
- //# sourceMappingURL=repository.errors.js.map
@@ -4,4 +4,3 @@
4
4
  * Database seed runner with execution tracking (PostgreSQL).
5
5
  */
6
6
  export { SeedRunner, createSeedRunner, normalizeSeeds, validateSeed, DEFAULT_SEED_TABLE, DEFAULT_SEED_LOCK, type Seed, type SeedRecord, type SeedResult, type SeedStatus, type SeedRunnerOptions, } from "./seed.runner.js";
7
- //# sourceMappingURL=index.d.ts.map
@@ -4,4 +4,3 @@
4
4
  * Database seed runner with execution tracking (PostgreSQL).
5
5
  */
6
6
  export { SeedRunner, createSeedRunner, normalizeSeeds, validateSeed, DEFAULT_SEED_TABLE, DEFAULT_SEED_LOCK, } from "./seed.runner.js";
7
- //# sourceMappingURL=index.js.map
@@ -3,8 +3,11 @@ import { type SqlDialectName } from "../migration/migration.dialect.js";
3
3
  import type { RunnerTransactionOptions } from "../migration/migration.types.js";
4
4
  /**
5
5
  * Defines a database seed operation.
6
+ *
7
+ * `TTransaction` is the transaction client handed to `run` and `rollback`;
8
+ * {@link createSeedRunner} infers it from the database client.
6
9
  */
7
- export interface Seed {
10
+ export interface Seed<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext> {
8
11
  /**
9
12
  * Unique seed name.
10
13
  */
@@ -16,11 +19,11 @@ export interface Seed {
16
19
  /**
17
20
  * Executes the seed.
18
21
  */
19
- readonly run: (database: DatabaseTransactionContext) => Promise<void>;
22
+ readonly run: (database: TTransaction) => Promise<void>;
20
23
  /**
21
24
  * Optional cleanup operation.
22
25
  */
23
- readonly rollback?: (database: DatabaseTransactionContext) => Promise<void>;
26
+ readonly rollback?: (database: TTransaction) => Promise<void>;
24
27
  }
25
28
  /**
26
29
  * Persisted seed execution record.
@@ -45,8 +48,8 @@ export interface SeedResult {
45
48
  /**
46
49
  * Seed runner status.
47
50
  */
48
- export interface SeedStatus {
49
- readonly pending: readonly Seed[];
51
+ export interface SeedStatus<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext> {
52
+ readonly pending: readonly Seed<TTransaction>[];
50
53
  readonly applied: readonly SeedRecord[];
51
54
  }
52
55
  /**
@@ -91,7 +94,7 @@ export declare const DEFAULT_SEED_LOCK = "database:seeds";
91
94
  * lock before deciding what to execute, so two runners started together
92
95
  * never apply or revert the same seed twice.
93
96
  */
94
- export declare class SeedRunner {
97
+ export declare class SeedRunner<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext> {
95
98
  private readonly client;
96
99
  private readonly seeds;
97
100
  private readonly tableName;
@@ -99,11 +102,11 @@ export declare class SeedRunner {
99
102
  private readonly dialect;
100
103
  private readonly transactionOptions;
101
104
  private readonly perItemTransaction;
102
- constructor(client: DatabaseClient, seeds: readonly Seed[], options?: SeedRunnerOptions);
105
+ constructor(client: DatabaseClient<TTransaction>, seeds: readonly Seed<TTransaction>[], options?: SeedRunnerOptions);
103
106
  /**
104
107
  * Returns the seed runner status.
105
108
  */
106
- status(): Promise<SeedStatus>;
109
+ status(): Promise<SeedStatus<TTransaction>>;
107
110
  /**
108
111
  * Executes every pending seed in order.
109
112
  */
@@ -148,13 +151,12 @@ export declare class SeedRunner {
148
151
  /**
149
152
  * Creates a seed runner.
150
153
  */
151
- export declare function createSeedRunner(client: DatabaseClient, seeds: readonly Seed[], options?: SeedRunnerOptions): SeedRunner;
154
+ export declare function createSeedRunner<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext>(client: DatabaseClient<TTransaction>, seeds: readonly Seed<TTransaction>[], options?: SeedRunnerOptions): SeedRunner<TTransaction>;
152
155
  /**
153
156
  * Validates and sorts seed definitions (stable sort on `order`).
154
157
  */
155
- export declare function normalizeSeeds(seeds: readonly Seed[]): readonly Seed[];
158
+ export declare function normalizeSeeds<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext>(seeds: readonly Seed<TTransaction>[]): readonly Seed<TTransaction>[];
156
159
  /**
157
160
  * Validates one seed definition.
158
161
  */
159
- export declare function validateSeed(seed: Seed): void;
160
- //# sourceMappingURL=seed.runner.d.ts.map
162
+ export declare function validateSeed<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext>(seed: Seed<TTransaction>): void;
@@ -368,4 +368,3 @@ function validateSeedName(name) {
368
368
  throw new TypeError("Seed name cannot exceed 255 characters.");
369
369
  }
370
370
  }
371
- //# sourceMappingURL=seed.runner.js.map
@@ -4,4 +4,3 @@
4
4
  * Managed transaction execution with retry support.
5
5
  */
6
6
  export { TransactionManager, createTransactionManager, withTransaction, withTransactionRetry, createTransactionContext, createTransactionId, getTransactionContextFromError, isTransactionActive, isTransactionCommitted, isTransactionFailed, type TransactionStatus, type TransactionContext, type TransactionOutcome, type TransactionRetryOptions, type ManagedTransactionOptions, } from "./transaction.core.js";
7
- //# sourceMappingURL=index.d.ts.map
@@ -4,4 +4,3 @@
4
4
  * Managed transaction execution with retry support.
5
5
  */
6
6
  export { TransactionManager, createTransactionManager, withTransaction, withTransactionRetry, createTransactionContext, createTransactionId, getTransactionContextFromError, isTransactionActive, isTransactionCommitted, isTransactionFailed, } from "./transaction.core.js";
7
- //# sourceMappingURL=index.js.map
@@ -30,4 +30,3 @@ export interface RetryDelayOptions {
30
30
  * @returns A finite delay in `[0, min(maxRetryDelayMs, 2^31-1)]`.
31
31
  */
32
32
  export declare function computeRetryDelay(attempt: number, options: RetryDelayOptions): number;
33
- //# sourceMappingURL=transaction.backoff.d.ts.map
@@ -29,4 +29,3 @@ export function computeRetryDelay(attempt, options) {
29
29
  const random = options.random ?? Math.random;
30
30
  return Math.floor(random() * (capped + 1));
31
31
  }
32
- //# sourceMappingURL=transaction.backoff.js.map
@@ -5,7 +5,14 @@ import type { TransactionIsolationLevel, TransactionOptions } from "../databaseT
5
5
  */
6
6
  export type TransactionStatus = "idle" | "active" | "committed" | "rolled-back" | "failed";
7
7
  /**
8
- * Runtime transaction information.
8
+ * Runtime transaction information: the immutable status record
9
+ * `TransactionManager.run` reports in its `TransactionOutcome` and attaches
10
+ * to failures (see `getTransactionContextFromError`).
11
+ *
12
+ * Not the `TransactionContext` of `@zudojs/transactions`, which is the
13
+ * AsyncLocalStorage store its manager propagates transactions through.
14
+ * The two packages share the name but not the concept; import from the
15
+ * package whose manager you use.
9
16
  */
10
17
  export interface TransactionContext {
11
18
  readonly transactionId: string;
@@ -64,15 +71,18 @@ export interface TransactionRetryOptions extends ManagedTransactionOptions {
64
71
  export declare function createTransactionId(): string;
65
72
  /**
66
73
  * Manages transaction execution and lifecycle metadata.
74
+ *
75
+ * `TTransaction` is the transaction client handed to callbacks, taken from
76
+ * the {@link DatabaseClient} the manager wraps.
67
77
  */
68
- export declare class TransactionManager {
78
+ export declare class TransactionManager<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext> {
69
79
  private readonly client;
70
- constructor(client: DatabaseClient);
80
+ constructor(client: DatabaseClient<TTransaction>);
71
81
  /**
72
82
  * Executes a callback inside a managed transaction and returns its
73
83
  * result.
74
84
  */
75
- execute<TResult>(callback: (transaction: DatabaseTransactionContext, context: TransactionContext) => Promise<TResult>, options?: ManagedTransactionOptions): Promise<TResult>;
85
+ execute<TResult>(callback: (transaction: TTransaction, context: TransactionContext) => Promise<TResult>, options?: ManagedTransactionOptions): Promise<TResult>;
76
86
  /**
77
87
  * Executes a callback inside a managed transaction and returns the
78
88
  * result together with the final context (`status: "committed"`).
@@ -86,16 +96,16 @@ export declare class TransactionManager {
86
96
  * `NotFoundError` or `DomainError`) rolls the transaction back and is
87
97
  * rethrown as the same, unmodified instance.
88
98
  */
89
- run<TResult>(callback: (transaction: DatabaseTransactionContext, context: TransactionContext) => Promise<TResult>, options?: ManagedTransactionOptions): Promise<TransactionOutcome<TResult>>;
99
+ run<TResult>(callback: (transaction: TTransaction, context: TransactionContext) => Promise<TResult>, options?: ManagedTransactionOptions): Promise<TransactionOutcome<TResult>>;
90
100
  /**
91
101
  * Returns the database client used by the manager.
92
102
  */
93
- getClient(): DatabaseClient;
103
+ getClient(): DatabaseClient<TTransaction>;
94
104
  }
95
105
  /**
96
106
  * Creates a transaction manager.
97
107
  */
98
- export declare function createTransactionManager(client: DatabaseClient): TransactionManager;
108
+ export declare function createTransactionManager<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext>(client: DatabaseClient<TTransaction>): TransactionManager<TTransaction>;
99
109
  /**
100
110
  * Executes a managed database transaction.
101
111
  *
@@ -104,7 +114,7 @@ export declare function createTransactionManager(client: DatabaseClient): Transa
104
114
  * validation, not-found, ...) propagates unchanged; driver and database
105
115
  * failures and any other thrown value become a `DatabaseError`.
106
116
  */
107
- export declare function withTransaction<TResult>(client: DatabaseClient, callback: (transaction: DatabaseTransactionContext, context: TransactionContext) => Promise<TResult>, options?: ManagedTransactionOptions): Promise<TResult>;
117
+ export declare function withTransaction<TResult, TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext>(client: DatabaseClient<TTransaction>, callback: (transaction: TTransaction, context: TransactionContext) => Promise<TResult>, options?: ManagedTransactionOptions): Promise<TResult>;
108
118
  /**
109
119
  * Executes a transaction with retry support.
110
120
  *
@@ -112,9 +122,16 @@ export declare function withTransaction<TResult>(client: DatabaseClient, callbac
112
122
  * callback must be idempotent with respect to any side effects performed
113
123
  * outside the transaction client (for example, sending emails).
114
124
  */
115
- export declare function withTransactionRetry<TResult>(client: DatabaseClient, callback: (transaction: DatabaseTransactionContext, context: TransactionContext) => Promise<TResult>, options?: TransactionRetryOptions): Promise<TResult>;
125
+ export declare function withTransactionRetry<TResult, TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext>(client: DatabaseClient<TTransaction>, callback: (transaction: TTransaction, context: TransactionContext) => Promise<TResult>, options?: TransactionRetryOptions): Promise<TResult>;
116
126
  /**
117
- * Creates an immutable transaction context.
127
+ * Creates an immutable transaction status record (see
128
+ * {@link TransactionContext}), as `TransactionManager.run` does for every
129
+ * transaction it opens.
130
+ *
131
+ * Unrelated to `createTransactionContext` from `@zudojs/transactions`,
132
+ * which creates the AsyncLocalStorage propagation store that package's
133
+ * manager runs callbacks in. This one holds no transaction and propagates
134
+ * nothing; it is data for logging and error reporting.
118
135
  */
119
136
  export declare function createTransactionContext(options?: ManagedTransactionOptions): TransactionContext;
120
137
  /**
@@ -134,4 +151,3 @@ export declare function isTransactionCommitted(context: TransactionContext): boo
134
151
  * Determines whether a transaction failed.
135
152
  */
136
153
  export declare function isTransactionFailed(context: TransactionContext): boolean;
137
- //# sourceMappingURL=transaction.core.d.ts.map
@@ -12,6 +12,9 @@ export function createTransactionId() {
12
12
  }
13
13
  /**
14
14
  * Manages transaction execution and lifecycle metadata.
15
+ *
16
+ * `TTransaction` is the transaction client handed to callbacks, taken from
17
+ * the {@link DatabaseClient} the manager wraps.
15
18
  */
16
19
  export class TransactionManager {
17
20
  client;
@@ -120,7 +123,14 @@ export async function withTransactionRetry(client, callback, options = {}) {
120
123
  }
121
124
  }
122
125
  /**
123
- * Creates an immutable transaction context.
126
+ * Creates an immutable transaction status record (see
127
+ * {@link TransactionContext}), as `TransactionManager.run` does for every
128
+ * transaction it opens.
129
+ *
130
+ * Unrelated to `createTransactionContext` from `@zudojs/transactions`,
131
+ * which creates the AsyncLocalStorage propagation store that package's
132
+ * manager runs callbacks in. This one holds no transaction and propagates
133
+ * nothing; it is data for logging and error reporting.
124
134
  */
125
135
  export function createTransactionContext(options = {}) {
126
136
  return Object.freeze({
@@ -201,4 +211,3 @@ function sleep(milliseconds) {
201
211
  setTimeout(resolve, milliseconds);
202
212
  });
203
213
  }
204
- //# sourceMappingURL=transaction.core.js.map
@@ -4,4 +4,3 @@
4
4
  * Groups multiple repository operations into a single transaction.
5
5
  */
6
6
  export { DatabaseUnitOfWork, createUnitOfWork, executeUnitOfWork, type UnitOfWork, type UnitOfWorkOptions, } from "./unitOfWork.core.js";
7
- //# sourceMappingURL=index.d.ts.map
@@ -4,4 +4,3 @@
4
4
  * Groups multiple repository operations into a single transaction.
5
5
  */
6
6
  export { DatabaseUnitOfWork, createUnitOfWork, executeUnitOfWork, } from "./unitOfWork.core.js";
7
- //# sourceMappingURL=index.js.map