@byline/db-mysql 4.8.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/LICENSE +373 -0
- package/README.md +283 -0
- package/dist/database/schema/auth.d.ts +951 -0
- package/dist/database/schema/auth.js +226 -0
- package/dist/database/schema/common.d.ts +168 -0
- package/dist/database/schema/common.js +130 -0
- package/dist/database/schema/index.d.ts +3419 -0
- package/dist/database/schema/index.js +895 -0
- package/dist/database/schema/schema-pins.test.node.d.ts +52 -0
- package/dist/database/schema/schema-pins.test.node.js +398 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.js +151 -0
- package/dist/lib/boot-check.d.ts +22 -0
- package/dist/lib/boot-check.js +42 -0
- package/dist/lib/boot-check.test.node.d.ts +8 -0
- package/dist/lib/boot-check.test.node.js +46 -0
- package/dist/lib/db-manager.d.ts +54 -0
- package/dist/lib/db-manager.js +49 -0
- package/dist/lib/test-db.d.ts +37 -0
- package/dist/lib/test-db.js +111 -0
- package/dist/lib/test-helper.d.ts +33 -0
- package/dist/lib/test-helper.js +68 -0
- package/dist/modules/admin/admin-permissions-repository.d.ts +35 -0
- package/dist/modules/admin/admin-permissions-repository.js +101 -0
- package/dist/modules/admin/admin-preferences-repository.d.ts +62 -0
- package/dist/modules/admin/admin-preferences-repository.js +156 -0
- package/dist/modules/admin/admin-roles-repository.d.ts +11 -0
- package/dist/modules/admin/admin-roles-repository.js +209 -0
- package/dist/modules/admin/admin-store.d.ts +20 -0
- package/dist/modules/admin/admin-store.js +30 -0
- package/dist/modules/admin/admin-users-repository.d.ts +11 -0
- package/dist/modules/admin/admin-users-repository.js +303 -0
- package/dist/modules/admin/index.d.ts +27 -0
- package/dist/modules/admin/index.js +27 -0
- package/dist/modules/admin/refresh-tokens-repository.d.ts +34 -0
- package/dist/modules/admin/refresh-tokens-repository.js +160 -0
- package/dist/modules/audit/audit-commands.d.ts +29 -0
- package/dist/modules/audit/audit-commands.js +40 -0
- package/dist/modules/audit/audit-queries.d.ts +41 -0
- package/dist/modules/audit/audit-queries.js +169 -0
- package/dist/modules/counters/counters-commands.d.ts +54 -0
- package/dist/modules/counters/counters-commands.js +121 -0
- package/dist/modules/counters/tests/counters-concurrency.test.d.ts +8 -0
- package/dist/modules/counters/tests/counters-concurrency.test.js +111 -0
- package/dist/modules/storage/classify-error.d.ts +34 -0
- package/dist/modules/storage/classify-error.js +50 -0
- package/dist/modules/storage/classify-error.test.node.d.ts +8 -0
- package/dist/modules/storage/classify-error.test.node.js +89 -0
- package/dist/modules/storage/normalize-row.d.ts +55 -0
- package/dist/modules/storage/normalize-row.js +135 -0
- package/dist/modules/storage/normalize-row.test.node.d.ts +8 -0
- package/dist/modules/storage/normalize-row.test.node.js +89 -0
- package/dist/modules/storage/storage-commands.d.ts +443 -0
- package/dist/modules/storage/storage-commands.js +1263 -0
- package/dist/modules/storage/storage-insert.d.ts +21 -0
- package/dist/modules/storage/storage-insert.js +152 -0
- package/dist/modules/storage/storage-queries.d.ts +805 -0
- package/dist/modules/storage/storage-queries.js +1815 -0
- package/dist/modules/storage/storage-store-manifest.d.ts +77 -0
- package/dist/modules/storage/storage-store-manifest.js +168 -0
- package/dist/modules/storage/storage-utils.d.ts +49 -0
- package/dist/modules/storage/storage-utils.js +76 -0
- package/dist/modules/storage/tests/dialect-pins.integration.test.d.ts +8 -0
- package/dist/modules/storage/tests/dialect-pins.integration.test.js +266 -0
- package/dist/modules/storage/tests/storage-commands.test.d.ts +8 -0
- package/dist/modules/storage/tests/storage-commands.test.js +324 -0
- package/dist/modules/storage/tests/storage-document-paths.test.d.ts +8 -0
- package/dist/modules/storage/tests/storage-document-paths.test.js +214 -0
- package/dist/modules/storage/tests/storage-document-tree.test.d.ts +8 -0
- package/dist/modules/storage/tests/storage-document-tree.test.js +361 -0
- package/dist/modules/storage/tests/storage-queries.test.d.ts +8 -0
- package/dist/modules/storage/tests/storage-queries.test.js +685 -0
- package/dist/modules/storage/tests/storage-status-and-lifecycle.test.d.ts +8 -0
- package/dist/modules/storage/tests/storage-status-and-lifecycle.test.js +268 -0
- package/package.json +91 -0
|
@@ -0,0 +1,443 @@
|
|
|
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 { CollectionDefinition, ICollectionCommands, IDocumentCommands, TreeDeleteMutationResult, TreeMutationResult } from '@byline/core';
|
|
9
|
+
import type { DBManager } from '../../lib/db-manager.js';
|
|
10
|
+
/**
|
|
11
|
+
* CollectionCommands
|
|
12
|
+
*
|
|
13
|
+
* Mirrors `packages/db-postgres/src/modules/storage/storage-commands.ts`'s
|
|
14
|
+
* `CollectionCommands`. MySQL has no `RETURNING` clause, so every write
|
|
15
|
+
* constructs its return value in JS instead — collection ids are
|
|
16
|
+
* app-generated UUIDv7, so `create` already knows every value it needs
|
|
17
|
+
* without a round trip. `update` re-`SELECT`s because `patch` is partial
|
|
18
|
+
* and the caller expects the merged row back.
|
|
19
|
+
*/
|
|
20
|
+
export declare class CollectionCommands implements ICollectionCommands {
|
|
21
|
+
private dbManager;
|
|
22
|
+
constructor(dbManager: DBManager);
|
|
23
|
+
/**
|
|
24
|
+
* The executor for this call — the ambient transaction when a
|
|
25
|
+
* `withTransaction` boundary is open, otherwise the pool. Resolved per
|
|
26
|
+
* access so every `this.db.*` below transparently joins an enclosing
|
|
27
|
+
* transaction with no call-site change. See docs/03-architecture/03-transactions.md.
|
|
28
|
+
*/
|
|
29
|
+
private get db();
|
|
30
|
+
create(path: string, config: CollectionDefinition, opts?: {
|
|
31
|
+
version?: number;
|
|
32
|
+
schemaHash?: string;
|
|
33
|
+
}): Promise<{
|
|
34
|
+
id: string;
|
|
35
|
+
path: string;
|
|
36
|
+
singular: string;
|
|
37
|
+
plural: string;
|
|
38
|
+
config: CollectionDefinition;
|
|
39
|
+
version: number;
|
|
40
|
+
schema_hash: string | null;
|
|
41
|
+
created_at: Date;
|
|
42
|
+
updated_at: Date;
|
|
43
|
+
}[]>;
|
|
44
|
+
update(id: string, patch: {
|
|
45
|
+
config?: CollectionDefinition;
|
|
46
|
+
version?: number;
|
|
47
|
+
schemaHash?: string;
|
|
48
|
+
}): Promise<{
|
|
49
|
+
created_at: Date;
|
|
50
|
+
updated_at: Date;
|
|
51
|
+
id: string;
|
|
52
|
+
path: string;
|
|
53
|
+
singular: string;
|
|
54
|
+
plural: string;
|
|
55
|
+
config: unknown;
|
|
56
|
+
version: number;
|
|
57
|
+
schema_hash: string | null;
|
|
58
|
+
}[]>;
|
|
59
|
+
delete(id: string): Promise<import("drizzle-orm/mysql2").MySqlRawQueryResult>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* DocumentCommands
|
|
63
|
+
*
|
|
64
|
+
* Ported from `packages/db-postgres/src/modules/storage/storage-commands.ts`'s
|
|
65
|
+
* `DocumentCommands`. Task 9A landed `createDocumentVersion` and its private
|
|
66
|
+
* helpers; Task 9B (this task) ports the rest of the write surface —
|
|
67
|
+
* `updateDocumentPath`, `setDocumentAvailableLocales`, `setDocumentStatus`,
|
|
68
|
+
* `archivePublishedVersions`, `softDeleteDocument`, `deleteDocumentLocale`,
|
|
69
|
+
* `setOrderKey`, `placeTreeNode`, `removeFromTree`, and
|
|
70
|
+
* `promoteChildrenAndRemoveFromTree` — completing `IDocumentCommands`. The
|
|
71
|
+
* pg source's `reAnchorDocument` / `reAnchorDocuments` / `backfillVersionLocales`
|
|
72
|
+
* are deliberately NOT ported here: `@byline/db-conformance`'s own suites
|
|
73
|
+
* (`document-paths.ts`, `locale-fallback.ts`) document them as "Postgres-only
|
|
74
|
+
* maintenance operations documented as off the core `IDbAdapter` contract" —
|
|
75
|
+
* not something a conforming adapter is required to implement, and neither is
|
|
76
|
+
* a member of `IDocumentCommands`.
|
|
77
|
+
*/
|
|
78
|
+
export declare class DocumentCommands implements IDocumentCommands {
|
|
79
|
+
private dbManager;
|
|
80
|
+
private defaultContentLocale;
|
|
81
|
+
constructor(dbManager: DBManager, defaultContentLocale: string);
|
|
82
|
+
/**
|
|
83
|
+
* The executor for this call — the ambient transaction when a
|
|
84
|
+
* `withTransaction` boundary is open, otherwise the pool. Resolved per
|
|
85
|
+
* access so every `this.db.*` below transparently joins an enclosing
|
|
86
|
+
* transaction with no call-site change. See docs/03-architecture/03-transactions.md.
|
|
87
|
+
*/
|
|
88
|
+
private get db();
|
|
89
|
+
/**
|
|
90
|
+
* createDocumentVersion
|
|
91
|
+
*
|
|
92
|
+
* Creates a new document or a new version of an existing document. Ported
|
|
93
|
+
* from `packages/db-postgres/src/modules/storage/storage-commands.ts`'s
|
|
94
|
+
* `DocumentCommands.createDocumentVersion` — see spec §2's normative
|
|
95
|
+
* conversion table, applied at each site below:
|
|
96
|
+
*
|
|
97
|
+
* - `.returning()` → construct-in-JS. Every id here is app-generated
|
|
98
|
+
* UUIDv7 (minted before the insert), so the value is already known —
|
|
99
|
+
* no re-`SELECT` is needed anywhere in this method.
|
|
100
|
+
* - `onConflictDoUpdate` (path upsert) → NOT `.onDuplicateKeyUpdate()`
|
|
101
|
+
* (found live: MySQL's `ON DUPLICATE KEY UPDATE` has no per-constraint
|
|
102
|
+
* targeting the way pg's `onConflictDoUpdate({ target })` does, so it
|
|
103
|
+
* would silently absorb a genuine cross-document path conflict instead
|
|
104
|
+
* of erroring). `writeDocumentPath` below does the targeting itself —
|
|
105
|
+
* see its docblock.
|
|
106
|
+
* - `ON CONFLICT (document_version_id, field_path, locale) DO NOTHING`
|
|
107
|
+
* (7 sites, the per-locale carry-forward) → MySQL has no per-row
|
|
108
|
+
* `gen_random_uuid()` to call from an `INSERT … SELECT`, and ids must
|
|
109
|
+
* be app-generated UUIDv7 — never `INSERT IGNORE`, which would swallow
|
|
110
|
+
* unrelated errors, and never a DB-generated id. So each site becomes
|
|
111
|
+
* a typed `SELECT` of the previous version's carry-forward rows,
|
|
112
|
+
* followed by a JS-side UUIDv7-per-row bulk `INSERT … ON DUPLICATE KEY
|
|
113
|
+
* UPDATE id = id` (drizzle's own documented no-op idiom for "do
|
|
114
|
+
* nothing" on MySQL) — see `copyForwardStoreRows` below, which the 7
|
|
115
|
+
* call sites share.
|
|
116
|
+
* - `::uuid` casts → dropped (MySQL ids are plain `CHAR(36)`).
|
|
117
|
+
*
|
|
118
|
+
* @param params - Options for creating the document
|
|
119
|
+
* @returns The created document and the number of field values inserted
|
|
120
|
+
*/
|
|
121
|
+
createDocumentVersion(params: {
|
|
122
|
+
documentId?: string;
|
|
123
|
+
collectionId: string;
|
|
124
|
+
collectionVersion: number;
|
|
125
|
+
collectionConfig: CollectionDefinition;
|
|
126
|
+
action: string;
|
|
127
|
+
documentData: any;
|
|
128
|
+
path?: string;
|
|
129
|
+
availableLocales?: string[];
|
|
130
|
+
locale?: string;
|
|
131
|
+
status?: string;
|
|
132
|
+
createdBy?: string;
|
|
133
|
+
previousVersionId?: string;
|
|
134
|
+
orderKey?: string;
|
|
135
|
+
}): Promise<{
|
|
136
|
+
document: {
|
|
137
|
+
id: string;
|
|
138
|
+
document_id: string;
|
|
139
|
+
collection_id: string;
|
|
140
|
+
collection_version: number;
|
|
141
|
+
doc: null;
|
|
142
|
+
event_type: string;
|
|
143
|
+
status: string;
|
|
144
|
+
is_deleted: boolean;
|
|
145
|
+
created_by: string | null;
|
|
146
|
+
change_summary: null;
|
|
147
|
+
created_at: Date;
|
|
148
|
+
updated_at: Date;
|
|
149
|
+
};
|
|
150
|
+
fieldCount: number;
|
|
151
|
+
}>;
|
|
152
|
+
/**
|
|
153
|
+
* writeDocumentPath
|
|
154
|
+
*
|
|
155
|
+
* Upsert the `byline_document_paths` row for a (document, locale) pair. The
|
|
156
|
+
* path row is document-grain and sticky across versions — it lives under the
|
|
157
|
+
* document's `source_locale` (its data anchor), not the mutable global
|
|
158
|
+
* default.
|
|
159
|
+
*
|
|
160
|
+
* Real dialect divergence from pg, found live (a test that should have
|
|
161
|
+
* raised a conflict silently "succeeded" instead): pg's `onConflictDoUpdate`
|
|
162
|
+
* takes an explicit `target` — the (`document_id`, `locale`) unique index —
|
|
163
|
+
* so a collision on the *other* unique index
|
|
164
|
+
* (`idx_document_paths_collection_locale_path`, a genuine path conflict
|
|
165
|
+
* from a different document) is deliberately left untargeted and bubbles up
|
|
166
|
+
* as `23505`. MySQL's `ON DUPLICATE KEY UPDATE` has no per-constraint
|
|
167
|
+
* targeting — it fires for a collision on *any* unique/primary key on the
|
|
168
|
+
* table, so a naive `.onDuplicateKeyUpdate()` would silently rewrite
|
|
169
|
+
* whichever existing row the *other* document's path collided with instead
|
|
170
|
+
* of raising `ER_DUP_ENTRY`. So this does the targeting itself: try the
|
|
171
|
+
* `INSERT` first; if it fails on the *own-document* unique index
|
|
172
|
+
* (`unique_document_paths_document_locale`, the ordinary "this document is
|
|
173
|
+
* resaving its already-anchored path" case — detected via `classifyError`,
|
|
174
|
+
* the same cause-chain-walking classifier the lifecycle layer uses, since
|
|
175
|
+
* mysql2/Drizzle wraps the duplicate-key error rather than throwing it
|
|
176
|
+
* bare), fall through to an explicit `UPDATE` instead. Any other
|
|
177
|
+
* duplicate-key error — in particular the collection-scoped
|
|
178
|
+
* path-uniqueness index — is rethrown unchanged, so it still surfaces as
|
|
179
|
+
* `ER_DUP_ENTRY` for the lifecycle layer's own `classifyError`-based
|
|
180
|
+
* `ERR_PATH_CONFLICT` mapping.
|
|
181
|
+
*/
|
|
182
|
+
private writeDocumentPath;
|
|
183
|
+
/**
|
|
184
|
+
* writeDocumentAvailableLocales
|
|
185
|
+
*
|
|
186
|
+
* Replace a document's `byline_document_available_locales` rows wholesale —
|
|
187
|
+
* the editorial advertised-locale set. Document-grain and sticky across
|
|
188
|
+
* versions: `delete`-then-`insert`, deduplicated so a caller-supplied
|
|
189
|
+
* duplicate doesn't collide on the `(document_id, locale)` primary key. An
|
|
190
|
+
* empty array clears the set (advertise nothing).
|
|
191
|
+
*/
|
|
192
|
+
private writeDocumentAvailableLocales;
|
|
193
|
+
/**
|
|
194
|
+
* writeVersionLocaleLedger
|
|
195
|
+
*
|
|
196
|
+
* Compute and insert a version's `byline_document_version_locales` rows: a
|
|
197
|
+
* locale is recorded when it covers every localized field path the version's
|
|
198
|
+
* `sourceLocale` has (path-coverage), and a version with no localized content
|
|
199
|
+
* records a single `'all'` sentinel. Reads the version's persisted store rows,
|
|
200
|
+
* so callers must have written them first. `::uuid` casts are dropped
|
|
201
|
+
* (MySQL ids are plain `CHAR(36)`); the WITH/UNION/HAVING NOT EXISTS logic is
|
|
202
|
+
* otherwise unchanged MySQL 8+ syntax — but the WITH clause's *position* is a
|
|
203
|
+
* real dialect difference, found live (a syntax error, not assumed): Postgres
|
|
204
|
+
* accepts `WITH … INSERT INTO … SELECT …`, whereas MySQL requires the WITH
|
|
205
|
+
* clause *after* `INSERT INTO <table>` and immediately before the `SELECT`
|
|
206
|
+
* it modifies (confirmed against a live MySQL 9.7.1 server) — so the two
|
|
207
|
+
* clauses are transposed relative to the pg original. See
|
|
208
|
+
* docs/07-internationalization/index.md.
|
|
209
|
+
*/
|
|
210
|
+
private writeVersionLocaleLedger;
|
|
211
|
+
/**
|
|
212
|
+
* copyForwardStoreRows
|
|
213
|
+
*
|
|
214
|
+
* Carry forward one store table's rows for every locale except `'all'` and
|
|
215
|
+
* the version's active write locale, from `prevVersionId` to `newVersionId`.
|
|
216
|
+
* The MySQL counterpart of pg's `INSERT … SELECT gen_random_uuid(), … ON
|
|
217
|
+
* CONFLICT (document_version_id, field_path, locale) DO NOTHING`: MySQL has
|
|
218
|
+
* no per-row id generator usable from an `INSERT … SELECT`, and ids must be
|
|
219
|
+
* app-generated UUIDv7 (never DB-generated, never `INSERT IGNORE` — see the
|
|
220
|
+
* method doc on `createDocumentVersion`) — so this reads the candidate rows
|
|
221
|
+
* with a typed `SELECT`, mints a fresh UUIDv7 per row in JS, and bulk-inserts
|
|
222
|
+
* with `.onDuplicateKeyUpdate({ set: { id: sql\`id\` } })`, drizzle's own
|
|
223
|
+
* documented no-op idiom for "do nothing on conflict" on MySQL. One generic
|
|
224
|
+
* helper shared by all 7 call sites (pg keeps 7 near-identical raw-SQL
|
|
225
|
+
* blocks because it doesn't need this per-row JS step; MySQL does, so
|
|
226
|
+
* factoring it once here avoids seven copies of the same shape).
|
|
227
|
+
*
|
|
228
|
+
* `table` is threaded through as `AnyMySqlTable` and cast internally —
|
|
229
|
+
* the 7 store tables share the same `(id, document_version_id, …, locale,
|
|
230
|
+
* created_at, updated_at)` column shape (`baseStoreColumns` in the schema),
|
|
231
|
+
* but Drizzle's query builder generics don't unify cleanly across a union
|
|
232
|
+
* of distinct table types for `.from()` / `.insert()`, so the cast is
|
|
233
|
+
* confined to this one private helper rather than leaking into the 7 call
|
|
234
|
+
* sites, which stay fully typed.
|
|
235
|
+
*/
|
|
236
|
+
private copyForwardStoreRows;
|
|
237
|
+
/**
|
|
238
|
+
* updateDocumentPath
|
|
239
|
+
*
|
|
240
|
+
* Standalone, non-versioned write of a document's URL path. Backs the admin
|
|
241
|
+
* path widget's direct-write Save path: it edits `byline_document_paths`
|
|
242
|
+
* in-place (document-grain, sticky) **without** minting a new document
|
|
243
|
+
* version or touching workflow status. The path's document-grain nature means
|
|
244
|
+
* the change is immediate and applies across every version of the document.
|
|
245
|
+
*
|
|
246
|
+
* Second caller into `writeDocumentPath` (the first is `createDocumentVersion`
|
|
247
|
+
* step 2a) — see that method's docblock for the insert-then-catch-and-
|
|
248
|
+
* conditionally-update targeting it does in place of pg's
|
|
249
|
+
* `onConflictDoUpdate({ target })`.
|
|
250
|
+
*
|
|
251
|
+
* Source-locale enforcement and `ERR_PATH_CONFLICT` mapping live in the
|
|
252
|
+
* lifecycle service that calls this; the command itself only performs the
|
|
253
|
+
* upsert (and surfaces the raw `ER_DUP_ENTRY` for the service to translate
|
|
254
|
+
* via `classifyError`).
|
|
255
|
+
*/
|
|
256
|
+
updateDocumentPath(params: {
|
|
257
|
+
documentId: string;
|
|
258
|
+
collectionId: string;
|
|
259
|
+
locale: string;
|
|
260
|
+
path: string;
|
|
261
|
+
}): Promise<void>;
|
|
262
|
+
/**
|
|
263
|
+
* setDocumentAvailableLocales
|
|
264
|
+
*
|
|
265
|
+
* Standalone, non-versioned write of a document's editorial advertised-locale
|
|
266
|
+
* set. Backs the admin available-locales widget's direct-write Save path: it
|
|
267
|
+
* replaces `byline_document_available_locales` wholesale (document-grain)
|
|
268
|
+
* **without** minting a new document version or touching workflow status. The
|
|
269
|
+
* change is immediate and applies across every version of the document; the
|
|
270
|
+
* public advertised set remains the intersection with the resolved version's
|
|
271
|
+
* completeness ledger. See docs/07-internationalization/index.md.
|
|
272
|
+
*/
|
|
273
|
+
setDocumentAvailableLocales(params: {
|
|
274
|
+
documentId: string;
|
|
275
|
+
collectionId: string;
|
|
276
|
+
availableLocales: string[];
|
|
277
|
+
}): Promise<void>;
|
|
278
|
+
/**
|
|
279
|
+
* copyAllVersionStoreRows
|
|
280
|
+
*
|
|
281
|
+
* Copy every store row — all seven value-store tables (optionally excluding
|
|
282
|
+
* one locale) plus the locale-agnostic `byline_store_meta` identity rows
|
|
283
|
+
* (always copied wholesale, unfiltered — a block's identity is shared
|
|
284
|
+
* across locales) — from one document version to another, verbatim. New
|
|
285
|
+
* `id`s are minted per row (MySQL has no per-row `gen_random_uuid()`
|
|
286
|
+
* usable from an `INSERT … SELECT`, and ids must be app-generated UUIDv7 —
|
|
287
|
+
* see `copyForwardStoreRows` above); `created_at`/`updated_at` are left off
|
|
288
|
+
* the inserted row so the column default (`CURRENT_TIMESTAMP(6)`) supplies
|
|
289
|
+
* a fresh timestamp, matching pg's explicit `NOW(), NOW()`.
|
|
290
|
+
*
|
|
291
|
+
* The target version is assumed fresh (no existing rows), so — unlike
|
|
292
|
+
* `copyForwardStoreRows`, which may collide with rows `createDocumentVersion`
|
|
293
|
+
* already wrote in the same transaction — this performs a plain `INSERT`
|
|
294
|
+
* with no conflict handling. A collision here would mean the caller reused
|
|
295
|
+
* a non-fresh version id, which should fail loudly rather than being
|
|
296
|
+
* silently absorbed. Used by `deleteDocumentLocale` to snapshot the current
|
|
297
|
+
* version into the new one with the target locale's rows dropped.
|
|
298
|
+
*/
|
|
299
|
+
private copyAllVersionStoreRows;
|
|
300
|
+
/** One value-store table's share of `copyAllVersionStoreRows`. */
|
|
301
|
+
private copyVersionStoreRows;
|
|
302
|
+
/**
|
|
303
|
+
* deleteDocumentLocale
|
|
304
|
+
*
|
|
305
|
+
* Remove one content locale's data from a document by writing a **new
|
|
306
|
+
* immutable version** that carries forward every store row except the
|
|
307
|
+
* target locale's (the `'all'` rows and all other locales are kept). The
|
|
308
|
+
* prior version still holds the deleted locale, so the operation is
|
|
309
|
+
* recoverable via version restore, and a previously-published version keeps
|
|
310
|
+
* serving until the new version is published.
|
|
311
|
+
*
|
|
312
|
+
* The new version's status is supplied by the caller (the lifecycle service
|
|
313
|
+
* passes the workflow's default — a fresh draft, matching `copyToLocale`).
|
|
314
|
+
* The derived availability ledger is recomputed from the carried-forward
|
|
315
|
+
* rows, so the deleted locale drops out automatically. The default content
|
|
316
|
+
* locale (the document's anchor) must never be passed here — the lifecycle
|
|
317
|
+
* service enforces that.
|
|
318
|
+
*
|
|
319
|
+
* Defensively returns `null` when the document has no current version (the
|
|
320
|
+
* service validates existence first, so this is a guard).
|
|
321
|
+
*/
|
|
322
|
+
deleteDocumentLocale(params: {
|
|
323
|
+
documentId: string;
|
|
324
|
+
locale: string;
|
|
325
|
+
status?: string;
|
|
326
|
+
createdBy?: string;
|
|
327
|
+
}): Promise<{
|
|
328
|
+
newVersionId: string;
|
|
329
|
+
previousVersionId: string;
|
|
330
|
+
} | null>;
|
|
331
|
+
/**
|
|
332
|
+
* setDocumentStatus
|
|
333
|
+
*
|
|
334
|
+
* Mutate the status field on an existing document version row.
|
|
335
|
+
* This is the one case where we UPDATE a version in-place — status is
|
|
336
|
+
* lifecycle metadata, not content.
|
|
337
|
+
*/
|
|
338
|
+
setDocumentStatus(params: {
|
|
339
|
+
document_version_id: string;
|
|
340
|
+
status: string;
|
|
341
|
+
}): Promise<void>;
|
|
342
|
+
/**
|
|
343
|
+
* archivePublishedVersions
|
|
344
|
+
*
|
|
345
|
+
* Set ALL versions of a document that currently have `currentStatus`
|
|
346
|
+
* (defaults to 'published') to 'archived'. Optionally exclude a specific
|
|
347
|
+
* version so the caller can protect the version it is about to publish.
|
|
348
|
+
*
|
|
349
|
+
* Returns the number of rows updated. MySQL has no `RETURNING` and drizzle's
|
|
350
|
+
* mysql2 `update()` resolves to a `[ResultSetHeader, FieldPacket[]]` tuple
|
|
351
|
+
* rather than pg's driver result object — `affectedRows` lives on the first
|
|
352
|
+
* element (confirmed live against the test database), not `.rowCount`. See
|
|
353
|
+
* `affectedRowCount()` in `storage-utils.ts`, shared by every guarded write
|
|
354
|
+
* in this adapter (this method, `softDeleteDocument` below, and the admin
|
|
355
|
+
* repositories) for that cast.
|
|
356
|
+
*/
|
|
357
|
+
archivePublishedVersions(params: {
|
|
358
|
+
document_id: string;
|
|
359
|
+
currentStatus?: string;
|
|
360
|
+
excludeVersionId?: string;
|
|
361
|
+
}): Promise<number>;
|
|
362
|
+
/**
|
|
363
|
+
* softDeleteDocument
|
|
364
|
+
*
|
|
365
|
+
* Mark ALL versions of a document as deleted by setting `is_deleted = true`.
|
|
366
|
+
* The `current_documents` view filters these out, so the document disappears
|
|
367
|
+
* from listings without physically removing data.
|
|
368
|
+
*
|
|
369
|
+
* Returns the number of version rows marked as deleted.
|
|
370
|
+
*/
|
|
371
|
+
softDeleteDocument(params: {
|
|
372
|
+
document_id: string;
|
|
373
|
+
}): Promise<number>;
|
|
374
|
+
/**
|
|
375
|
+
* Write `order_key` on a single `byline_documents` row. Single-column
|
|
376
|
+
* metadata update — no new version row, no `documentVersions` touch.
|
|
377
|
+
* `updated_at` on the document row is bumped so list caches invalidate.
|
|
378
|
+
*/
|
|
379
|
+
setOrderKey(params: {
|
|
380
|
+
document_id: string;
|
|
381
|
+
order_key: string;
|
|
382
|
+
}): Promise<void>;
|
|
383
|
+
/**
|
|
384
|
+
* Serialize structural changes per collection and verify the collection
|
|
385
|
+
* exists. `FOR UPDATE` — no `FOR UPDATE OF` qualifier needed since this
|
|
386
|
+
* query selects from a single table (confirmed against a live MySQL 9.7.1
|
|
387
|
+
* server; MySQL 8.0.1+ supports `FOR UPDATE OF tbl_name` for the
|
|
388
|
+
* multi-table case used below in `lockDocumentCollection`).
|
|
389
|
+
*/
|
|
390
|
+
private lockTreeCollection;
|
|
391
|
+
/**
|
|
392
|
+
* Resolve a document's collection while locking only the collection row.
|
|
393
|
+
* `FOR UPDATE OF c` — confirmed live against a MySQL 9.7.1 server (MySQL
|
|
394
|
+
* 8.0.1+ supports the per-table lock qualifier; `::uuid` casts dropped).
|
|
395
|
+
*/
|
|
396
|
+
private lockDocumentCollection;
|
|
397
|
+
/** Read one ordered sibling group on the already collection-locked transaction. */
|
|
398
|
+
private treeGroup;
|
|
399
|
+
/** Read a node's placement from the already collection-locked transaction. */
|
|
400
|
+
private treePlacement;
|
|
401
|
+
/** Read a raw node-and-descendants set while the collection tree lock is held. */
|
|
402
|
+
private treeSubtreeIds;
|
|
403
|
+
/**
|
|
404
|
+
* placeTreeNode — see {@link IDocumentCommands.placeTreeNode}.
|
|
405
|
+
*
|
|
406
|
+
* Single transaction: same-collection guard → cycle guard → resolve the
|
|
407
|
+
* target sibling group's neighbour keys → mint a fractional key → upsert the
|
|
408
|
+
* edge row. Unversioned; touches only `byline_document_relationships`.
|
|
409
|
+
*
|
|
410
|
+
* The edge upsert uses a plain `.onDuplicateKeyUpdate()` — unlike
|
|
411
|
+
* `writeDocumentPath`, `byline_document_relationships` carries exactly one
|
|
412
|
+
* unique constraint (`uq_document_relationships_child`, on
|
|
413
|
+
* `child_document_id`), so MySQL's lack of per-constraint targeting is not
|
|
414
|
+
* an issue here: any duplicate-key collision on this table can only be that
|
|
415
|
+
* one index, so there is no ambiguity to guard against.
|
|
416
|
+
*/
|
|
417
|
+
placeTreeNode(params: {
|
|
418
|
+
collectionId: string;
|
|
419
|
+
documentId: string;
|
|
420
|
+
parentDocumentId: string | null;
|
|
421
|
+
beforeDocumentId?: string | null;
|
|
422
|
+
afterDocumentId?: string | null;
|
|
423
|
+
ifUnplaced?: boolean;
|
|
424
|
+
}): Promise<TreeMutationResult>;
|
|
425
|
+
/**
|
|
426
|
+
* removeFromTree — see {@link IDocumentCommands.removeFromTree}.
|
|
427
|
+
* Single-row delete; no-op when the node is already unplaced.
|
|
428
|
+
*/
|
|
429
|
+
removeFromTree(params: {
|
|
430
|
+
collectionId: string;
|
|
431
|
+
documentId: string;
|
|
432
|
+
includeSubtree?: boolean;
|
|
433
|
+
}): Promise<TreeMutationResult>;
|
|
434
|
+
/** Promote direct children to roots and remove the parent edge under one lock. */
|
|
435
|
+
promoteChildrenAndRemoveFromTree(params: {
|
|
436
|
+
collectionId: string;
|
|
437
|
+
documentId: string;
|
|
438
|
+
}): Promise<TreeDeleteMutationResult>;
|
|
439
|
+
}
|
|
440
|
+
export declare function createCommandBuilders(dbManager: DBManager, defaultContentLocale: string): {
|
|
441
|
+
collections: CollectionCommands;
|
|
442
|
+
documents: DocumentCommands;
|
|
443
|
+
};
|