@zudojs/database 0.0.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.
Files changed (105) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -0
  3. package/dist/cache/cache.memory.d.ts.map +1 -0
  4. package/dist/cache/cache.memory.js +226 -0
  5. package/dist/cache/cache.memory.js.map +1 -0
  6. package/dist/cache/index.d.ts +7 -0
  7. package/dist/cache/index.d.ts.map +1 -0
  8. package/dist/cache/index.js +7 -0
  9. package/dist/cache/index.js.map +1 -0
  10. package/dist/database/database.core.d.ts.map +1 -0
  11. package/dist/database/index.d.ts +7 -0
  12. package/dist/database/index.d.ts.map +1 -0
  13. package/dist/database/index.js +7 -0
  14. package/dist/database/index.js.map +1 -0
  15. package/dist/databaseClient/databaseClient.core.d.ts +42 -0
  16. package/dist/databaseClient/databaseClient.core.d.ts.map +1 -0
  17. package/dist/databaseClient/databaseClient.core.js +203 -0
  18. package/dist/databaseClient/databaseClient.core.js.map +1 -0
  19. package/dist/databaseClient/databaseClient.logger.d.ts +9 -0
  20. package/dist/databaseClient/databaseClient.logger.d.ts.map +1 -0
  21. package/dist/databaseClient/databaseClient.logger.js +27 -0
  22. package/dist/databaseClient/databaseClient.logger.js.map +1 -0
  23. package/dist/databaseClient/index.d.ts +7 -0
  24. package/dist/databaseClient/index.d.ts.map +1 -0
  25. package/dist/databaseClient/index.js +7 -0
  26. package/dist/databaseClient/index.js.map +1 -0
  27. package/dist/databaseConnection/databaseConnection.manager.d.ts.map +1 -0
  28. package/dist/databaseConnection/databaseConnection.manager.js +219 -0
  29. package/dist/databaseConnection/databaseConnection.manager.js.map +1 -0
  30. package/dist/databaseConnection/index.d.ts +7 -0
  31. package/dist/databaseConnection/index.d.ts.map +1 -0
  32. package/dist/databaseConnection/index.js +7 -0
  33. package/dist/databaseConnection/index.js.map +1 -0
  34. package/dist/databaseType/index.d.ts +7 -0
  35. package/dist/databaseType/index.d.ts.map +1 -0
  36. package/dist/databaseType/index.js +7 -0
  37. package/dist/databaseType/index.js.map +1 -0
  38. package/dist/health/health.check.d.ts.map +1 -0
  39. package/dist/health/health.check.js +159 -0
  40. package/dist/health/health.check.js.map +1 -0
  41. package/dist/health/index.d.ts +7 -0
  42. package/dist/health/index.d.ts.map +1 -0
  43. package/dist/health/index.js +7 -0
  44. package/dist/health/index.js.map +1 -0
  45. package/dist/index.d.ts +36 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +49 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/locks/index.d.ts +7 -0
  50. package/dist/locks/index.d.ts.map +1 -0
  51. package/dist/locks/index.js +7 -0
  52. package/dist/locks/index.js.map +1 -0
  53. package/dist/locks/locks.core.d.ts.map +1 -0
  54. package/dist/locks/locks.core.js +204 -0
  55. package/dist/locks/locks.core.js.map +1 -0
  56. package/dist/migration/index.d.ts +7 -0
  57. package/dist/migration/index.d.ts.map +1 -0
  58. package/dist/migration/index.js +7 -0
  59. package/dist/migration/index.js.map +1 -0
  60. package/dist/migration/migration.helpers.d.ts.map +1 -0
  61. package/dist/migration/migration.runner.d.ts.map +1 -0
  62. package/dist/migration/migration.runner.js +209 -0
  63. package/dist/migration/migration.runner.js.map +1 -0
  64. package/dist/migration/migration.types.d.ts.map +1 -0
  65. package/dist/pagination/index.d.ts +7 -0
  66. package/dist/pagination/index.d.ts.map +1 -0
  67. package/dist/pagination/index.js +7 -0
  68. package/dist/pagination/index.js.map +1 -0
  69. package/dist/queryBuilder/index.d.ts +10 -0
  70. package/dist/queryBuilder/index.d.ts.map +1 -0
  71. package/dist/queryBuilder/index.js +9 -0
  72. package/dist/queryBuilder/index.js.map +1 -0
  73. package/dist/relations/index.d.ts +7 -0
  74. package/dist/relations/index.d.ts.map +1 -0
  75. package/dist/relations/index.js +7 -0
  76. package/dist/relations/index.js.map +1 -0
  77. package/dist/repository/index.d.ts +7 -0
  78. package/dist/repository/index.d.ts.map +1 -0
  79. package/dist/repository/index.js +7 -0
  80. package/dist/repository/index.js.map +1 -0
  81. package/dist/repository/repository.base.d.ts.map +1 -0
  82. package/dist/repository/repository.base.js +248 -0
  83. package/dist/repository/repository.base.js.map +1 -0
  84. package/dist/seed/index.d.ts +7 -0
  85. package/dist/seed/index.d.ts.map +1 -0
  86. package/dist/seed/index.js +7 -0
  87. package/dist/seed/index.js.map +1 -0
  88. package/dist/seed/seed.runner.d.ts.map +1 -0
  89. package/dist/seed/seed.runner.js +402 -0
  90. package/dist/seed/seed.runner.js.map +1 -0
  91. package/dist/transaction/index.d.ts +7 -0
  92. package/dist/transaction/index.d.ts.map +1 -0
  93. package/dist/transaction/index.js +7 -0
  94. package/dist/transaction/index.js.map +1 -0
  95. package/dist/transaction/transaction.core.d.ts.map +1 -0
  96. package/dist/transaction/transaction.core.js +158 -0
  97. package/dist/transaction/transaction.core.js.map +1 -0
  98. package/dist/unitOfWork/index.d.ts +7 -0
  99. package/dist/unitOfWork/index.d.ts.map +1 -0
  100. package/dist/unitOfWork/index.js +7 -0
  101. package/dist/unitOfWork/index.js.map +1 -0
  102. package/dist/unitOfWork/unitOfWork.core.d.ts.map +1 -0
  103. package/dist/unitOfWork/unitOfWork.core.js +64 -0
  104. package/dist/unitOfWork/unitOfWork.core.js.map +1 -0
  105. package/package.json +66 -0
@@ -0,0 +1,248 @@
1
+ import { DatabaseError } from "@zudojs/errors";
2
+ /**
3
+ * Generic base repository implementation.
4
+ *
5
+ * Concrete repositories should extend this class and provide the
6
+ * appropriate Prisma delegate plus any domain-specific behavior.
7
+ */
8
+ export class BaseRepository {
9
+ delegate;
10
+ modelName;
11
+ constructor(delegate, options = {}) {
12
+ if (!delegate) {
13
+ throw new TypeError("A repository delegate is required.");
14
+ }
15
+ this.delegate = delegate;
16
+ this.modelName = options.modelName ?? "DatabaseEntity";
17
+ }
18
+ /**
19
+ * Finds an entity by its primary identifier.
20
+ */
21
+ async findById(id, options) {
22
+ this.validateId(id);
23
+ return this.execute("findById", () => this.delegate.findUnique({
24
+ where: {
25
+ id,
26
+ },
27
+ }), options);
28
+ }
29
+ /**
30
+ * Finds the first entity matching a filter.
31
+ */
32
+ async findOne(filter, options) {
33
+ this.validateFilter(filter);
34
+ return this.execute("findOne", () => this.delegate.findFirst({
35
+ where: filter,
36
+ }), options);
37
+ }
38
+ /**
39
+ * Finds all entities matching a filter.
40
+ */
41
+ async findMany(filter, options) {
42
+ if (filter !== undefined) {
43
+ this.validateFilter(filter);
44
+ }
45
+ return this.execute("findMany", () => this.delegate.findMany({
46
+ where: filter,
47
+ }), options);
48
+ }
49
+ /**
50
+ * Finds entities using pagination and sorting.
51
+ */
52
+ async findPaginated(filter, options) {
53
+ if (filter !== undefined) {
54
+ this.validateFilter(filter);
55
+ }
56
+ const pagination = options?.pagination;
57
+ const page = normalizePage(pagination);
58
+ const limit = normalizeLimit(pagination);
59
+ const skip = (page - 1) * limit;
60
+ const [data, total] = await Promise.all([
61
+ this.execute("findMany", () => this.delegate.findMany({
62
+ where: filter,
63
+ skip,
64
+ take: limit,
65
+ orderBy: this.buildOrderBy(options?.sort),
66
+ }), options),
67
+ this.count(filter, options),
68
+ ]);
69
+ return {
70
+ data,
71
+ meta: createPaginationMeta(page, limit, total),
72
+ };
73
+ }
74
+ /**
75
+ * Creates a new entity.
76
+ */
77
+ async create(input, options) {
78
+ if (input === undefined || input === null) {
79
+ throw new DatabaseError(`Cannot create ${this.modelName}: input is required.`);
80
+ }
81
+ return this.execute("create", () => this.delegate.create({
82
+ data: input,
83
+ }), options);
84
+ }
85
+ /**
86
+ * Updates an entity by its identifier.
87
+ */
88
+ async update(id, input, options) {
89
+ this.validateId(id);
90
+ if (input === undefined || input === null) {
91
+ throw new DatabaseError(`Cannot update ${this.modelName}: input is required.`);
92
+ }
93
+ return this.execute("update", () => this.delegate.update({
94
+ where: {
95
+ id,
96
+ },
97
+ data: input,
98
+ }), options);
99
+ }
100
+ /**
101
+ * Deletes an entity by its identifier.
102
+ */
103
+ async delete(id, options) {
104
+ this.validateId(id);
105
+ await this.execute("delete", () => this.delegate.delete({
106
+ where: {
107
+ id,
108
+ },
109
+ }), options);
110
+ }
111
+ /**
112
+ * Checks whether an entity exists.
113
+ */
114
+ async exists(filter, options) {
115
+ this.validateFilter(filter);
116
+ const entity = await this.execute("exists", () => this.delegate.findFirst({
117
+ where: filter,
118
+ }), options);
119
+ return entity !== null;
120
+ }
121
+ /**
122
+ * Counts entities matching a filter.
123
+ */
124
+ async count(filter, options) {
125
+ if (filter !== undefined) {
126
+ this.validateFilter(filter);
127
+ }
128
+ return this.execute("count", () => this.delegate.count({
129
+ where: filter,
130
+ }), options);
131
+ }
132
+ /**
133
+ * Updates an entity if it exists, otherwise creates it.
134
+ */
135
+ async upsert(where, create, update, options) {
136
+ if (typeof this.delegate.upsert !== "function") {
137
+ throw new DatabaseError(`Upsert is not supported by ${this.modelName}.`);
138
+ }
139
+ return this.execute("upsert", () => this.delegate.upsert({
140
+ where,
141
+ create,
142
+ update,
143
+ }), options);
144
+ }
145
+ /**
146
+ * Executes a repository operation and normalizes database failures.
147
+ */
148
+ async execute(operation, callback, options) {
149
+ if (options?.signal?.aborted) {
150
+ throw new DatabaseError(`${this.modelName} ${operation} was aborted.`);
151
+ }
152
+ const startedAt = Date.now();
153
+ try {
154
+ const promise = callback();
155
+ if (options?.timeoutMs && options.timeoutMs > 0) {
156
+ return await withTimeout(promise, options.timeoutMs, `${this.modelName} ${operation} timed out.`);
157
+ }
158
+ return await promise;
159
+ }
160
+ catch (error) {
161
+ if (error instanceof DatabaseError) {
162
+ throw error;
163
+ }
164
+ throw new DatabaseError(error instanceof Error
165
+ ? error.message
166
+ : `${this.modelName} ${operation} failed.`, {
167
+ cause: error,
168
+ metadata: {
169
+ model: this.modelName,
170
+ operation,
171
+ durationMs: Date.now() - startedAt,
172
+ ...(options?.metadata ?? {}),
173
+ },
174
+ });
175
+ }
176
+ }
177
+ /**
178
+ * Validates an entity identifier.
179
+ */
180
+ validateId(id) {
181
+ if (id === undefined ||
182
+ id === null ||
183
+ (typeof id === "string" && id.trim().length === 0)) {
184
+ throw new DatabaseError(`${this.modelName} identifier is required.`);
185
+ }
186
+ }
187
+ /**
188
+ * Validates a repository filter.
189
+ */
190
+ validateFilter(filter) {
191
+ if (filter === undefined || filter === null) {
192
+ throw new DatabaseError(`${this.modelName} filter is required.`);
193
+ }
194
+ }
195
+ /**
196
+ * Converts generic sort definitions into Prisma-compatible orderBy.
197
+ */
198
+ buildOrderBy(sort) {
199
+ if (!sort || sort.length === 0) {
200
+ return undefined;
201
+ }
202
+ return sort.map((entry) => ({
203
+ [entry.field]: entry.direction,
204
+ }));
205
+ }
206
+ }
207
+ /**
208
+ * Creates pagination metadata.
209
+ */
210
+ function createPaginationMeta(page, limit, total) {
211
+ const totalPages = total === 0 ? 0 : Math.ceil(total / limit);
212
+ return {
213
+ page,
214
+ limit,
215
+ total,
216
+ totalPages,
217
+ hasNextPage: totalPages > 0 && page < totalPages,
218
+ hasPreviousPage: page > 1 && totalPages > 0,
219
+ };
220
+ }
221
+ /**
222
+ * Normalizes a requested page number.
223
+ */
224
+ function normalizePage(input) {
225
+ return Math.max(1, Math.floor(input?.page ?? 1));
226
+ }
227
+ /**
228
+ * Normalizes a requested page size.
229
+ */
230
+ function normalizeLimit(input) {
231
+ return Math.min(100, Math.max(1, Math.floor(input?.limit ?? 20)));
232
+ }
233
+ /**
234
+ * Resolves an operation with a timeout.
235
+ */
236
+ async function withTimeout(promise, timeoutMs, message) {
237
+ let timer;
238
+ const timeout = new Promise((_, reject) => {
239
+ timer = setTimeout(() => reject(new DatabaseError(message)), timeoutMs);
240
+ });
241
+ try {
242
+ return await Promise.race([promise, timeout]);
243
+ }
244
+ finally {
245
+ clearTimeout(timer);
246
+ }
247
+ }
248
+ //# sourceMappingURL=repository.base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.base.js","sourceRoot":"","sources":["../../src/repository/repository.base.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @zudojs/database — Seeds
3
+ *
4
+ * Database seed runner with execution tracking.
5
+ */
6
+ export { SeedRunner, createSeedRunner, normalizeSeeds, validateSeed, DEFAULT_SEED_TABLE, DEFAULT_SEED_LOCK, type Seed, type SeedRecord, type SeedResult, type SeedRunnerOptions, } from "./seed.runner.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/seed/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,IAAI,EACT,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,iBAAiB,GACvB,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @zudojs/database — Seeds
3
+ *
4
+ * Database seed runner with execution tracking.
5
+ */
6
+ export { SeedRunner, createSeedRunner, normalizeSeeds, validateSeed, DEFAULT_SEED_TABLE, DEFAULT_SEED_LOCK, } from "./seed.runner.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/seed/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,GAKlB,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed.runner.d.ts","sourceRoot":"","sources":["../../src/seed/seed.runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,402 @@
1
+ import { DatabaseError } from "@zudojs/errors";
2
+ import { Prisma } from "@prisma/client";
3
+ /**
4
+ * Default seed tracking table.
5
+ */
6
+ export const DEFAULT_SEED_TABLE = "_seeds";
7
+ /**
8
+ * Default seed advisory lock.
9
+ */
10
+ export const DEFAULT_SEED_LOCK = "database:seeds";
11
+ /**
12
+ * Runs and tracks database seeds.
13
+ */
14
+ export class SeedRunner {
15
+ client;
16
+ seeds;
17
+ tableName;
18
+ lockKey;
19
+ constructor(client, seeds, options = {}) {
20
+ if (!client) {
21
+ throw new TypeError("A database client is required.");
22
+ }
23
+ this.client = client;
24
+ this.seeds = normalizeSeeds(seeds);
25
+ this.tableName = options.tableName ?? DEFAULT_SEED_TABLE;
26
+ this.lockKey = options.lockKey ?? DEFAULT_SEED_LOCK;
27
+ validateIdentifier(this.tableName, "seed table name");
28
+ validateLockKey(this.lockKey);
29
+ }
30
+ /**
31
+ * Returns the seed runner status.
32
+ */
33
+ async status() {
34
+ await this.ensureSeedTable();
35
+ const applied = await this.getAppliedSeeds();
36
+ const appliedNames = new Set(applied.map((seed) => seed.name));
37
+ const pending = this.seeds.filter((seed) => !appliedNames.has(seed.name));
38
+ return {
39
+ pending,
40
+ applied,
41
+ };
42
+ }
43
+ /**
44
+ * Executes every pending seed.
45
+ */
46
+ async run() {
47
+ await this.ensureSeedTable();
48
+ const applied = await this.getAppliedSeeds();
49
+ const appliedNames = new Set(applied.map((seed) => seed.name));
50
+ const pending = this.seeds.filter((seed) => !appliedNames.has(seed.name));
51
+ if (pending.length === 0) {
52
+ return {
53
+ applied: [],
54
+ skipped: applied,
55
+ };
56
+ }
57
+ const newlyApplied = [];
58
+ await this.client.transaction(async (transaction) => {
59
+ await this.acquireSeedLock(transaction);
60
+ for (const seed of pending) {
61
+ await this.executeSeed(transaction, seed);
62
+ const record = await this.recordSeed(transaction, seed);
63
+ newlyApplied.push(record);
64
+ }
65
+ });
66
+ return {
67
+ applied: newlyApplied,
68
+ skipped: applied,
69
+ };
70
+ }
71
+ /**
72
+ * Executes one named seed.
73
+ */
74
+ async runOne(name) {
75
+ validateSeedName(name);
76
+ await this.ensureSeedTable();
77
+ const seed = this.seeds.find((candidate) => candidate.name === name);
78
+ if (!seed) {
79
+ throw new DatabaseError(`Seed "${name}" is not registered.`);
80
+ }
81
+ const applied = await this.getAppliedSeeds();
82
+ const existing = applied.find((record) => record.name === name);
83
+ if (existing) {
84
+ return existing;
85
+ }
86
+ let result;
87
+ await this.client.transaction(async (transaction) => {
88
+ await this.acquireSeedLock(transaction);
89
+ const current = await this.getAppliedSeeds(transaction);
90
+ const alreadyApplied = current.find((record) => record.name === name);
91
+ if (alreadyApplied) {
92
+ result = alreadyApplied;
93
+ return;
94
+ }
95
+ await this.executeSeed(transaction, seed);
96
+ result = await this.recordSeed(transaction, seed);
97
+ });
98
+ if (!result) {
99
+ throw new DatabaseError(`Seed "${name}" did not produce an execution record.`);
100
+ }
101
+ return result;
102
+ }
103
+ /**
104
+ * Rolls back the most recently applied seed.
105
+ */
106
+ async rollback() {
107
+ await this.ensureSeedTable();
108
+ const applied = await this.getAppliedSeeds();
109
+ if (applied.length === 0) {
110
+ return null;
111
+ }
112
+ const latest = applied[applied.length - 1];
113
+ if (!latest) {
114
+ return null;
115
+ }
116
+ const seed = this.seeds.find((candidate) => candidate.name === latest.name);
117
+ if (!seed) {
118
+ throw new DatabaseError(`Seed "${latest.name}" is recorded as applied but is not registered.`);
119
+ }
120
+ if (!seed.rollback) {
121
+ throw new DatabaseError(`Seed "${seed.name}" does not define a rollback operation.`);
122
+ }
123
+ await this.client.transaction(async (transaction) => {
124
+ await this.acquireSeedLock(transaction);
125
+ await seed.rollback(transaction);
126
+ await this.deleteSeedRecord(transaction, seed.name);
127
+ });
128
+ return latest;
129
+ }
130
+ /**
131
+ * Rolls back every applied seed in reverse execution order.
132
+ */
133
+ async rollbackAll() {
134
+ await this.ensureSeedTable();
135
+ const applied = await this.getAppliedSeeds();
136
+ if (applied.length === 0) {
137
+ return [];
138
+ }
139
+ const rolledBack = [];
140
+ await this.client.transaction(async (transaction) => {
141
+ await this.acquireSeedLock(transaction);
142
+ for (let index = applied.length - 1; index >= 0; index -= 1) {
143
+ const record = applied[index];
144
+ if (!record) {
145
+ continue;
146
+ }
147
+ const seed = this.seeds.find((candidate) => candidate.name === record.name);
148
+ if (!seed) {
149
+ throw new DatabaseError(`Seed "${record.name}" is not registered.`);
150
+ }
151
+ if (!seed.rollback) {
152
+ throw new DatabaseError(`Seed "${seed.name}" does not define a rollback operation.`);
153
+ }
154
+ await seed.rollback(transaction);
155
+ await this.deleteSeedRecord(transaction, seed.name);
156
+ rolledBack.push(record);
157
+ }
158
+ });
159
+ return rolledBack;
160
+ }
161
+ /**
162
+ * Creates the seed tracking table if it does not exist.
163
+ */
164
+ async ensureSeedTable() {
165
+ const table = quoteIdentifier(this.tableName);
166
+ try {
167
+ await this.client.executeRaw(Prisma.sql `
168
+ CREATE TABLE IF NOT EXISTS ${table} (
169
+ "name" VARCHAR(255) PRIMARY KEY,
170
+ "applied_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
171
+ )
172
+ `);
173
+ }
174
+ catch (error) {
175
+ throw new DatabaseError("Failed to initialize the seed tracking table.", {
176
+ cause: error,
177
+ metadata: {
178
+ tableName: this.tableName,
179
+ },
180
+ });
181
+ }
182
+ }
183
+ /**
184
+ * Returns all applied seeds.
185
+ */
186
+ async getAppliedSeeds(transaction) {
187
+ const table = quoteIdentifier(this.tableName);
188
+ try {
189
+ let rows;
190
+ if (transaction) {
191
+ rows = await transaction.$queryRawUnsafe(`
192
+ SELECT
193
+ "name",
194
+ "applied_at"
195
+ FROM ${table}
196
+ ORDER BY "applied_at" ASC, "name" ASC
197
+ `);
198
+ }
199
+ else {
200
+ rows = await this.client.queryRaw(Prisma.sql `
201
+ SELECT
202
+ "name",
203
+ "applied_at"
204
+ FROM ${table}
205
+ ORDER BY "applied_at" ASC, "name" ASC
206
+ `);
207
+ }
208
+ return rows.map((row) => ({
209
+ name: row.name,
210
+ appliedAt: new Date(row.applied_at),
211
+ }));
212
+ }
213
+ catch (error) {
214
+ throw new DatabaseError("Failed to read seed execution history.", {
215
+ cause: error,
216
+ metadata: {
217
+ tableName: this.tableName,
218
+ },
219
+ });
220
+ }
221
+ }
222
+ /**
223
+ * Executes a seed.
224
+ */
225
+ async executeSeed(transaction, seed) {
226
+ try {
227
+ await seed.run(transaction);
228
+ }
229
+ catch (error) {
230
+ throw new DatabaseError(`Seed "${seed.name}" failed.`, {
231
+ cause: error,
232
+ metadata: {
233
+ name: seed.name,
234
+ order: seed.order ?? 0,
235
+ },
236
+ });
237
+ }
238
+ }
239
+ /**
240
+ * Records a successfully executed seed.
241
+ */
242
+ async recordSeed(transaction, seed) {
243
+ const table = quoteIdentifier(this.tableName);
244
+ try {
245
+ await transaction.$executeRawUnsafe(`
246
+ INSERT INTO ${table}
247
+ ("name")
248
+ VALUES
249
+ ($1)
250
+ `, seed.name);
251
+ return {
252
+ name: seed.name,
253
+ appliedAt: new Date(),
254
+ };
255
+ }
256
+ catch (error) {
257
+ throw new DatabaseError(`Failed to record seed "${seed.name}".`, {
258
+ cause: error,
259
+ metadata: {
260
+ name: seed.name,
261
+ },
262
+ });
263
+ }
264
+ }
265
+ /**
266
+ * Removes a seed execution record.
267
+ */
268
+ async deleteSeedRecord(transaction, name) {
269
+ const table = quoteIdentifier(this.tableName);
270
+ try {
271
+ await transaction.$executeRawUnsafe(`
272
+ DELETE FROM ${table}
273
+ WHERE "name" = $1
274
+ `, name);
275
+ }
276
+ catch (error) {
277
+ throw new DatabaseError(`Failed to remove seed record "${name}".`, {
278
+ cause: error,
279
+ metadata: {
280
+ name,
281
+ },
282
+ });
283
+ }
284
+ }
285
+ /**
286
+ * Acquires the advisory lock used for seed execution.
287
+ */
288
+ async acquireSeedLock(transaction) {
289
+ try {
290
+ const lock = hashLockKey(this.lockKey);
291
+ await transaction.$executeRaw `
292
+ SELECT pg_advisory_xact_lock(
293
+ ${lock}
294
+ )
295
+ `;
296
+ }
297
+ catch (error) {
298
+ throw new DatabaseError("Failed to acquire the database seed lock.", {
299
+ cause: error,
300
+ metadata: {
301
+ lockKey: this.lockKey,
302
+ },
303
+ });
304
+ }
305
+ }
306
+ }
307
+ /**
308
+ * Creates a seed runner.
309
+ */
310
+ export function createSeedRunner(client, seeds, options) {
311
+ return new SeedRunner(client, seeds, options);
312
+ }
313
+ /**
314
+ * Validates and sorts seed definitions.
315
+ */
316
+ export function normalizeSeeds(seeds) {
317
+ if (!Array.isArray(seeds)) {
318
+ throw new TypeError("Seeds must be an array.");
319
+ }
320
+ const normalized = seeds
321
+ .map((seed) => {
322
+ validateSeed(seed);
323
+ return Object.freeze({
324
+ ...seed,
325
+ });
326
+ })
327
+ .sort((first, second) => (first.order ?? 0) - (second.order ?? 0));
328
+ const names = new Set();
329
+ for (const seed of normalized) {
330
+ if (names.has(seed.name)) {
331
+ throw new TypeError(`Duplicate seed name: "${seed.name}".`);
332
+ }
333
+ names.add(seed.name);
334
+ }
335
+ return Object.freeze(normalized);
336
+ }
337
+ /**
338
+ * Validates one seed definition.
339
+ */
340
+ export function validateSeed(seed) {
341
+ if (!seed || typeof seed !== "object") {
342
+ throw new TypeError("A seed definition is required.");
343
+ }
344
+ validateSeedName(seed.name);
345
+ if (seed.order !== undefined && !Number.isInteger(seed.order)) {
346
+ throw new TypeError(`Seed "${seed.name}" has an invalid order.`);
347
+ }
348
+ if (typeof seed.run !== "function") {
349
+ throw new TypeError(`Seed "${seed.name}" requires a run function.`);
350
+ }
351
+ if (seed.rollback !== undefined && typeof seed.rollback !== "function") {
352
+ throw new TypeError(`Seed "${seed.name}" has an invalid rollback function.`);
353
+ }
354
+ }
355
+ /**
356
+ * Validates a seed name.
357
+ */
358
+ function validateSeedName(name) {
359
+ if (typeof name !== "string" || name.trim().length === 0) {
360
+ throw new TypeError("Seed name is required.");
361
+ }
362
+ if (name.length > 255) {
363
+ throw new TypeError("Seed name cannot exceed 255 characters.");
364
+ }
365
+ }
366
+ /**
367
+ * Validates an SQL identifier.
368
+ */
369
+ function validateIdentifier(identifier, name) {
370
+ if (typeof identifier !== "string" ||
371
+ !/^[A-Za-z_][A-Za-z0-9_]*$/.test(identifier)) {
372
+ throw new TypeError(`Invalid ${name}: "${identifier}".`);
373
+ }
374
+ }
375
+ /**
376
+ * Validates an advisory lock key.
377
+ */
378
+ function validateLockKey(lockKey) {
379
+ if (typeof lockKey !== "string" || lockKey.trim().length === 0) {
380
+ throw new TypeError("A database seed lock key is required.");
381
+ }
382
+ }
383
+ /**
384
+ * Quotes a validated SQL identifier.
385
+ */
386
+ function quoteIdentifier(identifier) {
387
+ validateIdentifier(identifier, "identifier");
388
+ return `"${identifier}"`;
389
+ }
390
+ /**
391
+ * Generates a deterministic signed 64-bit advisory lock key.
392
+ */
393
+ function hashLockKey(value) {
394
+ let hash = 1469598103934665603n;
395
+ const bytes = new TextEncoder().encode(value);
396
+ for (const byte of bytes) {
397
+ hash ^= BigInt(byte);
398
+ hash = BigInt.asIntN(64, hash * 1099511628211n);
399
+ }
400
+ return BigInt.asIntN(64, hash);
401
+ }
402
+ //# sourceMappingURL=seed.runner.js.map