@byline/db-mysql 4.16.0 → 4.17.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/dist/database/schema/index.d.ts +41 -0
- package/dist/database/schema/index.js +14 -0
- package/dist/database/schema/schema-pins.test.node.js +22 -0
- package/dist/index.js +5 -0
- package/dist/lib/db-manager.d.ts +3 -0
- package/dist/lib/db-manager.js +3 -0
- package/dist/modules/storage/classify-error.d.ts +2 -2
- package/dist/modules/storage/classify-error.js +8 -2
- package/dist/modules/storage/classify-error.test.node.js +20 -8
- package/dist/modules/storage/singletons.d.ts +27 -0
- package/dist/modules/storage/singletons.js +70 -0
- package/dist/modules/storage/storage-commands.js +3 -3
- package/dist/modules/storage/storage-queries.d.ts +2 -1
- package/dist/modules/storage/storage-queries.js +3 -1
- package/package.json +8 -9
|
@@ -274,6 +274,47 @@ export declare const documents: import("drizzle-orm/mysql-core").MySqlTableWithC
|
|
|
274
274
|
};
|
|
275
275
|
dialect: 'mysql';
|
|
276
276
|
}>;
|
|
277
|
+
export declare const singletonDocuments: import("drizzle-orm/mysql-core").MySqlTableWithColumns<{
|
|
278
|
+
name: "byline_singleton_documents";
|
|
279
|
+
schema: undefined;
|
|
280
|
+
columns: {
|
|
281
|
+
collection_id: import("drizzle-orm/mysql-core").MySqlColumn<{
|
|
282
|
+
name: "collection_id";
|
|
283
|
+
tableName: "byline_singleton_documents";
|
|
284
|
+
dataType: "custom";
|
|
285
|
+
columnType: "MySqlCustomColumn";
|
|
286
|
+
data: string;
|
|
287
|
+
driverParam: string;
|
|
288
|
+
notNull: true;
|
|
289
|
+
hasDefault: false;
|
|
290
|
+
isPrimaryKey: true;
|
|
291
|
+
isAutoincrement: false;
|
|
292
|
+
hasRuntimeDefault: false;
|
|
293
|
+
enumValues: undefined;
|
|
294
|
+
baseColumn: never;
|
|
295
|
+
identity: undefined;
|
|
296
|
+
generated: undefined;
|
|
297
|
+
}, {}, {}>;
|
|
298
|
+
document_id: import("drizzle-orm/mysql-core").MySqlColumn<{
|
|
299
|
+
name: "document_id";
|
|
300
|
+
tableName: "byline_singleton_documents";
|
|
301
|
+
dataType: "custom";
|
|
302
|
+
columnType: "MySqlCustomColumn";
|
|
303
|
+
data: string;
|
|
304
|
+
driverParam: string;
|
|
305
|
+
notNull: true;
|
|
306
|
+
hasDefault: false;
|
|
307
|
+
isPrimaryKey: false;
|
|
308
|
+
isAutoincrement: false;
|
|
309
|
+
hasRuntimeDefault: false;
|
|
310
|
+
enumValues: undefined;
|
|
311
|
+
baseColumn: never;
|
|
312
|
+
identity: undefined;
|
|
313
|
+
generated: undefined;
|
|
314
|
+
}, {}, {}>;
|
|
315
|
+
};
|
|
316
|
+
dialect: 'mysql';
|
|
317
|
+
}>;
|
|
277
318
|
export declare const documentVersions: import("drizzle-orm/mysql-core").MySqlTableWithColumns<{
|
|
278
319
|
name: "byline_document_versions";
|
|
279
320
|
schema: undefined;
|
|
@@ -74,6 +74,20 @@ export const documents = mysqlTable('byline_documents', {
|
|
|
74
74
|
}).onDelete('cascade'),
|
|
75
75
|
index('idx_documents_collection').on(table.collection_id),
|
|
76
76
|
index('idx_documents_collection_order').on(table.collection_id, table.order_key),
|
|
77
|
+
unique('uq_documents_collection_id_id').on(table.collection_id, table.id),
|
|
78
|
+
]);
|
|
79
|
+
// Singleton slot → document mapping. The composite foreign key proves that
|
|
80
|
+
// the mapped document belongs to the registered collection while the primary
|
|
81
|
+
// key enforces zero-or-one materialised document per singleton slot.
|
|
82
|
+
export const singletonDocuments = mysqlTable('byline_singleton_documents', {
|
|
83
|
+
collection_id: uuidChar('collection_id').primaryKey(),
|
|
84
|
+
document_id: uuidChar('document_id').notNull().unique(),
|
|
85
|
+
}, (table) => [
|
|
86
|
+
foreignKey({
|
|
87
|
+
name: 'fk_singleton_documents_document',
|
|
88
|
+
columns: [table.collection_id, table.document_id],
|
|
89
|
+
foreignColumns: [documents.collection_id, documents.id],
|
|
90
|
+
}).onDelete('cascade'),
|
|
77
91
|
]);
|
|
78
92
|
// Document versions table
|
|
79
93
|
export const documentVersions = mysqlTable('byline_document_versions', {
|
|
@@ -463,3 +463,25 @@ describe('schema pins — scheduled publication', () => {
|
|
|
463
463
|
expect(expiry?.config.columns.map((column) => ('name' in column ? column.name : null))).toEqual(['execution_expires_at']);
|
|
464
464
|
});
|
|
465
465
|
});
|
|
466
|
+
describe('schema pins — singleton documents', () => {
|
|
467
|
+
it('pins the supporting document ownership key', () => {
|
|
468
|
+
const cfg = getTableConfig(coreSchema.documents);
|
|
469
|
+
const ownershipKey = cfg.uniqueConstraints.find((constraint) => constraint.name === 'uq_documents_collection_id_id');
|
|
470
|
+
expect(ownershipKey?.columns.map((column) => column.name)).toEqual(['collection_id', 'id']);
|
|
471
|
+
});
|
|
472
|
+
it('pins slot and document uniqueness plus collection ownership', () => {
|
|
473
|
+
const cfg = getTableConfig(coreSchema.singletonDocuments);
|
|
474
|
+
const collectionId = cfg.columns.find((column) => column.name === 'collection_id');
|
|
475
|
+
const documentId = cfg.columns.find((column) => column.name === 'document_id');
|
|
476
|
+
const ownershipForeignKey = cfg.foreignKeys.find((foreignKey) => foreignKey.getName() === 'fk_singleton_documents_document');
|
|
477
|
+
const reference = ownershipForeignKey?.reference();
|
|
478
|
+
expect(collectionId?.primary).toBe(true);
|
|
479
|
+
expect(documentId?.isUnique).toBe(true);
|
|
480
|
+
expect(reference?.columns.map((column) => column.name)).toEqual([
|
|
481
|
+
'collection_id',
|
|
482
|
+
'document_id',
|
|
483
|
+
]);
|
|
484
|
+
expect(reference?.foreignColumns.map((column) => column.name)).toEqual(['collection_id', 'id']);
|
|
485
|
+
expect(ownershipForeignKey?.onDelete).toBe('cascade');
|
|
486
|
+
});
|
|
487
|
+
});
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import { createAuditQueries } from './modules/audit/audit-queries.js';
|
|
|
15
15
|
import { createCounterCommands } from './modules/counters/counters-commands.js';
|
|
16
16
|
import { createSchedulerStore } from './modules/scheduler/scheduler-store.js';
|
|
17
17
|
import { classifyError } from './modules/storage/classify-error.js';
|
|
18
|
+
import { SingletonCommands, SingletonQueries } from './modules/storage/singletons.js';
|
|
18
19
|
import { createCommandBuilders } from './modules/storage/storage-commands.js';
|
|
19
20
|
import { createQueryBuilders } from './modules/storage/storage-queries.js';
|
|
20
21
|
export const mysqlAdapter = ({ connectionString, collections, defaultContentLocale, connectionLimit = 20, idleTimeout = 2000, connectTimeout = 30000, maxIdle, }) => {
|
|
@@ -87,6 +88,8 @@ export const mysqlAdapter = ({ connectionString, collections, defaultContentLoca
|
|
|
87
88
|
const auditCommands = createAuditCommands(dbManager);
|
|
88
89
|
const auditQueries = createAuditQueries(db);
|
|
89
90
|
const schedulerStore = createSchedulerStore(db);
|
|
91
|
+
const singletonCommands = new SingletonCommands(dbManager);
|
|
92
|
+
const singletonQueries = new SingletonQueries(dbManager);
|
|
90
93
|
// Boot check: run lazily on the pool's first physical connection rather
|
|
91
94
|
// than eagerly here, because `mysqlAdapter` is synchronous (mirroring
|
|
92
95
|
// `pgAdapter`) and cannot await a round trip before returning. mysql2
|
|
@@ -133,6 +136,7 @@ export const mysqlAdapter = ({ connectionString, collections, defaultContentLoca
|
|
|
133
136
|
// `auditCommands` fully implements `IAuditCommands` as of Task 11 —
|
|
134
137
|
// see `./modules/audit/audit-commands.js`.
|
|
135
138
|
audit: auditCommands,
|
|
139
|
+
singletons: singletonCommands,
|
|
136
140
|
},
|
|
137
141
|
queries: {
|
|
138
142
|
// `queryBuilders.collections` fully implements `ICollectionQueries`
|
|
@@ -144,6 +148,7 @@ export const mysqlAdapter = ({ connectionString, collections, defaultContentLoca
|
|
|
144
148
|
// `auditQueries` fully implements `IAuditQueries` as of Task 11 — see
|
|
145
149
|
// `./modules/audit/audit-queries.js`.
|
|
146
150
|
audit: auditQueries,
|
|
151
|
+
singletons: singletonQueries,
|
|
147
152
|
},
|
|
148
153
|
withTransaction: (fn) => txManager.withTransaction(fn),
|
|
149
154
|
scheduler: schedulerStore,
|
package/dist/lib/db-manager.d.ts
CHANGED
|
@@ -25,6 +25,8 @@ export interface DBManager {
|
|
|
25
25
|
* boundary is open in this async context, otherwise the pool.
|
|
26
26
|
*/
|
|
27
27
|
get(): DBExecutor;
|
|
28
|
+
/** Whether the current async context owns an ambient transaction. */
|
|
29
|
+
isInTransaction(): boolean;
|
|
28
30
|
}
|
|
29
31
|
export declare class DBManagerImpl implements DBManager {
|
|
30
32
|
private readonly dbPool;
|
|
@@ -32,6 +34,7 @@ export declare class DBManagerImpl implements DBManager {
|
|
|
32
34
|
dbPool: DBExecutor;
|
|
33
35
|
});
|
|
34
36
|
get(): DBExecutor;
|
|
37
|
+
isInTransaction(): boolean;
|
|
35
38
|
}
|
|
36
39
|
export interface TXManager {
|
|
37
40
|
/**
|
package/dist/lib/db-manager.js
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
import { type DbErrorClassification } from '@byline/core';
|
|
9
9
|
/**
|
|
10
10
|
* Classify a MySQL driver error. Walks a short `cause` chain (Drizzle's
|
|
11
|
-
* `DrizzleQueryError` → the underlying mysql2 error) looking for
|
|
12
|
-
*
|
|
11
|
+
* `DrizzleQueryError` → the underlying mysql2 error) looking for duplicate-key
|
|
12
|
+
* and foreign-key errors and returns the carried constraint or index name. Mirrors
|
|
13
13
|
* `packages/db-postgres/src/modules/storage/classify-error.ts`, whose
|
|
14
14
|
* pg-anatomy docblock this docblock intentionally parallels.
|
|
15
15
|
*
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
import { DbErrorCodes } from '@byline/core';
|
|
9
9
|
/**
|
|
10
10
|
* Classify a MySQL driver error. Walks a short `cause` chain (Drizzle's
|
|
11
|
-
* `DrizzleQueryError` → the underlying mysql2 error) looking for
|
|
12
|
-
*
|
|
11
|
+
* `DrizzleQueryError` → the underlying mysql2 error) looking for duplicate-key
|
|
12
|
+
* and foreign-key errors and returns the carried constraint or index name. Mirrors
|
|
13
13
|
* `packages/db-postgres/src/modules/storage/classify-error.ts`, whose
|
|
14
14
|
* pg-anatomy docblock this docblock intentionally parallels.
|
|
15
15
|
*
|
|
@@ -44,6 +44,12 @@ export function classifyError(err) {
|
|
|
44
44
|
: qualifiedName;
|
|
45
45
|
return { code: DbErrorCodes.UNIQUE_VIOLATION, constraint };
|
|
46
46
|
}
|
|
47
|
+
// InnoDB's table-level FK failures use 1451/1452 and carry a constraint
|
|
48
|
+
// name. Legacy 1216/1217 variants omit the name, so they remain UNKNOWN.
|
|
49
|
+
if (e.errno === 1451 || e.errno === 1452) {
|
|
50
|
+
const match = typeof e.message === 'string' ? e.message.match(/CONSTRAINT [`']([^`']+)[`']/) : null;
|
|
51
|
+
return { code: DbErrorCodes.FOREIGN_KEY_VIOLATION, constraint: match?.[1] };
|
|
52
|
+
}
|
|
47
53
|
e = e.cause;
|
|
48
54
|
}
|
|
49
55
|
return { code: DbErrorCodes.UNKNOWN };
|
|
@@ -46,13 +46,21 @@ describe('classifyError (mysql)', () => {
|
|
|
46
46
|
};
|
|
47
47
|
expect(classifyError(err).code).toBe('DB_UNIQUE_VIOLATION');
|
|
48
48
|
});
|
|
49
|
-
it('
|
|
49
|
+
it('classifies a foreign-key error with the constraint name', () => {
|
|
50
50
|
expect(classifyError({
|
|
51
51
|
code: 'ER_NO_REFERENCED_ROW_2',
|
|
52
52
|
errno: 1452,
|
|
53
53
|
sqlState: '23000',
|
|
54
|
-
message: 'Cannot add or update a child row: a foreign key constraint fails',
|
|
55
|
-
})).toEqual({
|
|
54
|
+
message: 'Cannot add or update a child row: a foreign key constraint fails (`db`.`table`, CONSTRAINT `fk_document_owner` FOREIGN KEY (`document_id`))',
|
|
55
|
+
})).toEqual({
|
|
56
|
+
code: 'DB_FOREIGN_KEY_VIOLATION',
|
|
57
|
+
constraint: 'fk_document_owner',
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
it('returns DB_UNKNOWN for an unrelated database error', () => {
|
|
61
|
+
expect(classifyError({ code: 'ER_DATA_TOO_LONG', errno: 1406 })).toEqual({
|
|
62
|
+
code: 'DB_UNKNOWN',
|
|
63
|
+
});
|
|
56
64
|
});
|
|
57
65
|
it('returns DB_UNKNOWN for a non-error value', () => {
|
|
58
66
|
expect(classifyError(undefined)).toEqual({ code: 'DB_UNKNOWN' });
|
|
@@ -79,11 +87,15 @@ runClassifyErrorContract([
|
|
|
79
87
|
message: "Duplicate entry 'some-uuid' for key 'byline_document_paths.idx_document_paths_collection_locale_path'",
|
|
80
88
|
},
|
|
81
89
|
},
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
90
|
+
foreignKeyViolationError: {
|
|
91
|
+
name: 'DrizzleQueryError',
|
|
92
|
+
cause: {
|
|
93
|
+
code: 'ER_NO_REFERENCED_ROW_2',
|
|
94
|
+
errno: 1452,
|
|
95
|
+
sqlState: '23000',
|
|
96
|
+
message: 'Cannot add or update a child row: a foreign key constraint fails (`db`.`table`, CONSTRAINT `fk_document_owner` FOREIGN KEY (`document_id`))',
|
|
97
|
+
},
|
|
87
98
|
},
|
|
99
|
+
unrelatedError: { code: 'ER_DATA_TOO_LONG', errno: 1406, sqlState: '22001' },
|
|
88
100
|
},
|
|
89
101
|
]);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import { type ISingletonCommands, type ISingletonQueries } from '@byline/core';
|
|
9
|
+
import type { DBManager } from '../../lib/db-manager.js';
|
|
10
|
+
export declare class SingletonCommands implements ISingletonCommands {
|
|
11
|
+
private dbManager;
|
|
12
|
+
constructor(dbManager: DBManager);
|
|
13
|
+
/**
|
|
14
|
+
* Resolve the executor per access so an enclosing `withTransaction`
|
|
15
|
+
* boundary is honoured through the adapter's ambient transaction manager.
|
|
16
|
+
*/
|
|
17
|
+
private get db();
|
|
18
|
+
lockSlot(collectionId: string): Promise<void>;
|
|
19
|
+
setMapping(collectionId: string, documentId: string): Promise<void>;
|
|
20
|
+
clearMapping(collectionId: string): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
export declare class SingletonQueries implements ISingletonQueries {
|
|
23
|
+
private dbManager;
|
|
24
|
+
constructor(dbManager: DBManager);
|
|
25
|
+
private get db();
|
|
26
|
+
getMappedDocumentId(collectionId: string): Promise<string | null>;
|
|
27
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import { ERR_DATABASE, ERR_NOT_FOUND, } from '@byline/core';
|
|
9
|
+
import { eq, sql } from 'drizzle-orm';
|
|
10
|
+
import { singletonDocuments } from '../../database/schema/index.js';
|
|
11
|
+
export class SingletonCommands {
|
|
12
|
+
dbManager;
|
|
13
|
+
constructor(dbManager) {
|
|
14
|
+
this.dbManager = dbManager;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolve the executor per access so an enclosing `withTransaction`
|
|
18
|
+
* boundary is honoured through the adapter's ambient transaction manager.
|
|
19
|
+
*/
|
|
20
|
+
get db() {
|
|
21
|
+
return this.dbManager.get();
|
|
22
|
+
}
|
|
23
|
+
async lockSlot(collectionId) {
|
|
24
|
+
if (!this.dbManager.isInTransaction()) {
|
|
25
|
+
throw ERR_DATABASE({
|
|
26
|
+
message: 'singleton slot locks require an active transaction',
|
|
27
|
+
details: { collectionId },
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const locked = await this.db.execute(sql `
|
|
31
|
+
SELECT id FROM byline_collections
|
|
32
|
+
WHERE id = ${collectionId}
|
|
33
|
+
FOR UPDATE
|
|
34
|
+
`);
|
|
35
|
+
const rows = locked[0];
|
|
36
|
+
if (rows.length === 0) {
|
|
37
|
+
throw ERR_NOT_FOUND({
|
|
38
|
+
message: 'singleton slot registration not found',
|
|
39
|
+
details: { collectionId },
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async setMapping(collectionId, documentId) {
|
|
44
|
+
await this.db
|
|
45
|
+
.insert(singletonDocuments)
|
|
46
|
+
.values({ collection_id: collectionId, document_id: documentId });
|
|
47
|
+
}
|
|
48
|
+
async clearMapping(collectionId) {
|
|
49
|
+
await this.db
|
|
50
|
+
.delete(singletonDocuments)
|
|
51
|
+
.where(eq(singletonDocuments.collection_id, collectionId));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export class SingletonQueries {
|
|
55
|
+
dbManager;
|
|
56
|
+
constructor(dbManager) {
|
|
57
|
+
this.dbManager = dbManager;
|
|
58
|
+
}
|
|
59
|
+
get db() {
|
|
60
|
+
return this.dbManager.get();
|
|
61
|
+
}
|
|
62
|
+
async getMappedDocumentId(collectionId) {
|
|
63
|
+
const rows = await this.db
|
|
64
|
+
.select({ documentId: singletonDocuments.document_id })
|
|
65
|
+
.from(singletonDocuments)
|
|
66
|
+
.where(eq(singletonDocuments.collection_id, collectionId))
|
|
67
|
+
.limit(1);
|
|
68
|
+
return rows[0]?.documentId ?? null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Copyright (c) Infonomic Company Limited
|
|
7
7
|
*/
|
|
8
|
-
import { DbErrorCodes, ERR_CONFLICT, ERR_NOT_FOUND, ERR_VALIDATION, flattenFieldSetData, generateKeyBetween, TREE_PLACEMENT_STALE_MARKER, } from '@byline/core';
|
|
8
|
+
import { DbErrorCodes, ERR_CONFLICT, ERR_NOT_FOUND, ERR_VALIDATION, flattenFieldSetData, generateKeyBetween, isSingleton, TREE_PLACEMENT_STALE_MARKER, } from '@byline/core';
|
|
9
9
|
import { and, desc, eq, inArray, ne, notInArray, sql } from 'drizzle-orm';
|
|
10
10
|
import { v7 as uuidv7 } from 'uuid';
|
|
11
11
|
import { booleanStore, collections, currentDocumentsView, datetimeStore, documentAvailableLocales, documentPaths, documentRelationships, documents, documentVersions, fileStore, jsonStore, metaStore, numericStore, relationStore, textStore, } from '../../database/schema/index.js';
|
|
@@ -49,8 +49,8 @@ export class CollectionCommands {
|
|
|
49
49
|
async create(path, config, opts) {
|
|
50
50
|
const id = uuidv7();
|
|
51
51
|
const version = opts?.version ?? 1;
|
|
52
|
-
const singular = config.labels.singular || path;
|
|
53
|
-
const plural = config.labels.plural || `${path}s`;
|
|
52
|
+
const singular = isSingleton(config) ? config.label : config.labels.singular || path;
|
|
53
|
+
const plural = isSingleton(config) ? config.label : config.labels.plural || `${path}s`;
|
|
54
54
|
// `created_at`/`updated_at` are written explicitly rather than left to
|
|
55
55
|
// the column's `DEFAULT CURRENT_TIMESTAMP(6)` — an earlier version of
|
|
56
56
|
// this method approximated them with a request-time `Date` instead
|
|
@@ -356,8 +356,9 @@ export declare class DocumentQueries implements IDocumentQueries {
|
|
|
356
356
|
/**
|
|
357
357
|
* getDocumentByVersion — fetches a specific version and reconstructs its fields.
|
|
358
358
|
*/
|
|
359
|
-
getDocumentByVersion({ document_version_id, locale, collection_id, filters, }: {
|
|
359
|
+
getDocumentByVersion({ document_version_id, document_id, locale, collection_id, filters, }: {
|
|
360
360
|
document_version_id: string;
|
|
361
|
+
document_id?: string;
|
|
361
362
|
locale?: string;
|
|
362
363
|
collection_id?: string;
|
|
363
364
|
filters?: DocumentFilter[];
|
|
@@ -652,10 +652,12 @@ export class DocumentQueries {
|
|
|
652
652
|
/**
|
|
653
653
|
* getDocumentByVersion — fetches a specific version and reconstructs its fields.
|
|
654
654
|
*/
|
|
655
|
-
async getDocumentByVersion({ document_version_id, locale = 'all', collection_id, filters, }) {
|
|
655
|
+
async getDocumentByVersion({ document_version_id, document_id, locale = 'all', collection_id, filters, }) {
|
|
656
656
|
const projectionLocale = locale === 'all' ? undefined : locale;
|
|
657
657
|
const filterLocale = projectionLocale ?? this.defaultContentLocale;
|
|
658
658
|
const conditions = [eq(documentVersions.id, document_version_id)];
|
|
659
|
+
if (document_id)
|
|
660
|
+
conditions.push(eq(documentVersions.document_id, document_id));
|
|
659
661
|
if (collection_id)
|
|
660
662
|
conditions.push(eq(documentVersions.collection_id, collection_id));
|
|
661
663
|
if (filters?.length) {
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@byline/db-mysql",
|
|
3
3
|
"private": false,
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
|
-
"version": "4.
|
|
5
|
+
"version": "4.17.0",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20.9.0"
|
|
8
8
|
},
|
|
@@ -48,26 +48,25 @@
|
|
|
48
48
|
],
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"drizzle-orm": "^0.45.2",
|
|
51
|
-
"mysql2": "^3.
|
|
51
|
+
"mysql2": "^3.24.2",
|
|
52
52
|
"uuid": "^14.0.2",
|
|
53
|
-
"@byline/admin": "4.
|
|
54
|
-
"@byline/core": "4.
|
|
53
|
+
"@byline/admin": "4.17.0",
|
|
54
|
+
"@byline/core": "4.17.0"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@biomejs/biome": "2.5.
|
|
58
|
-
"@types/node": "^26.
|
|
57
|
+
"@biomejs/biome": "2.5.10",
|
|
58
|
+
"@types/node": "^26.4.0",
|
|
59
59
|
"chokidar": "^5.0.0",
|
|
60
60
|
"chokidar-cli": "^3.0.0",
|
|
61
61
|
"dotenv": "^17.4.2",
|
|
62
62
|
"drizzle-kit": "^0.31.10",
|
|
63
63
|
"npm-run-all": "^4.1.5",
|
|
64
64
|
"tsc-alias": "^1.9.2",
|
|
65
|
-
"tsx": "^4.23.12",
|
|
66
65
|
"typescript": "^7.0.2",
|
|
67
66
|
"vitest": "^4.1.11",
|
|
68
|
-
"@byline/
|
|
67
|
+
"@byline/auth": "4.17.0",
|
|
69
68
|
"@byline/db-conformance": "0.0.10",
|
|
70
|
-
"@byline/
|
|
69
|
+
"@byline/client": "4.17.0"
|
|
71
70
|
},
|
|
72
71
|
"publishConfig": {
|
|
73
72
|
"access": "public",
|