@byline/core 3.11.2 → 3.12.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/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@byline/core",
3
3
  "private": false,
4
4
  "license": "MPL-2.0",
5
- "version": "3.11.2",
5
+ "version": "3.12.0",
6
6
  "engines": {
7
7
  "node": ">=20.9.0"
8
8
  },
@@ -79,7 +79,7 @@
79
79
  "sharp": "^0.34.5",
80
80
  "uuid": "^14.0.0",
81
81
  "zod": "^4.4.3",
82
- "@byline/auth": "3.11.2"
82
+ "@byline/auth": "3.12.0"
83
83
  },
84
84
  "devDependencies": {
85
85
  "@biomejs/biome": "2.4.15",
@@ -1,513 +0,0 @@
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
- /**
9
- * Document lifecycle service.
10
- *
11
- * Orchestrates CRUD operations and workflow transitions, invoking collection
12
- * hooks at the appropriate points. Sits between the API route layer and the
13
- * storage adapter (`IDbAdapter`) so that every operation path — POST, PUT,
14
- * PATCH, status change, unpublish — goes through a single set of hooks.
15
- *
16
- * Hook invocations run **outside** the storage transaction. They are suitable
17
- * for logging, cache invalidation, webhooks, and similar side-effects.
18
- *
19
- * This module depends only on `@byline/core` types and utilities — it has no
20
- * dependency on any specific database adapter.
21
- */
22
- import type { RequestContext } from '@byline/auth';
23
- import { type CollectionDefinition, type IDbAdapter, type IStorageProvider } from '../@types/index.js';
24
- import { type SlugifierFn } from '../utils/slugify.js';
25
- import type { BylineLogger } from '../lib/logger.js';
26
- import type { DocumentPatch } from '../patches/index.js';
27
- /**
28
- * The shared context every lifecycle function requires. Built once per
29
- * request by the API route layer and passed through.
30
- */
31
- export interface DocumentLifecycleContext {
32
- /** The database adapter returned by `getServerConfig().db`. */
33
- db: IDbAdapter;
34
- /** The resolved `CollectionDefinition` (includes `hooks`). */
35
- definition: CollectionDefinition;
36
- /** The database-level collection row ID. */
37
- collectionId: string;
38
- /**
39
- * The collection's current schema version. Stamped onto every
40
- * `documentVersions` row written during the lifecycle call so that
41
- * Phase-2 in-memory migration can later resolve each document against
42
- * the shape it was authored under. Callers resolve this from the core
43
- * registry (`core.getCollectionRecord(path).version`).
44
- */
45
- collectionVersion: number;
46
- /** The collection `path` string (e.g. `'docs'`, `'news'`). */
47
- collectionPath: string;
48
- /**
49
- * Storage provider for this collection. Required when the collection
50
- * has any upload-capable image/file field, so that the original files
51
- * and their persisted variants can be cleaned up on document deletion.
52
- *
53
- * Resolved by the route layer as:
54
- * `field.upload?.storage ?? serverConfig.storage`
55
- *
56
- * Optional — callers whose collections have no upload-capable fields
57
- * are unaffected.
58
- */
59
- storage?: IStorageProvider;
60
- /** Structured logger instance. Provided via the DI registry. */
61
- logger: BylineLogger;
62
- /**
63
- * The default content locale (e.g. `'en'`). Used to anchor `path`
64
- * derivation: the slugifier always runs against the default-locale
65
- * source value, and creating a brand-new document in any other locale
66
- * is rejected.
67
- *
68
- * Sourced by callers from `ServerConfig.i18n.content.defaultLocale`.
69
- */
70
- defaultLocale: string;
71
- /**
72
- * Installation slugifier. When omitted, the lifecycle falls back to
73
- * the default `slugify` exported from `@byline/core`.
74
- */
75
- slugifier?: SlugifierFn;
76
- /**
77
- * Request-scoped context carrying the authenticated actor, request id,
78
- * and related per-request metadata.
79
- *
80
- * Plumbing only in Phase 0 of the auth roadmap — present on the context
81
- * so every lifecycle service can accept and forward it, but no ability
82
- * assertions are performed yet. Phase 4 turns enforcement on: lifecycle
83
- * entry points will call `context.requestContext?.actor?.assertAbility(...)`
84
- * before any storage mutation.
85
- *
86
- * Optional so that internal-tooling callers (seed scripts, migration
87
- * tools) continue to compile. Production write paths always supply it
88
- * — `assertActorCanPerform` runs at every lifecycle entry and rejects
89
- * a missing context.
90
- *
91
- * See docs/AUTHN-AUTHZ.md.
92
- */
93
- requestContext?: RequestContext;
94
- }
95
- export interface CreateDocumentResult {
96
- documentId: string;
97
- documentVersionId: string;
98
- }
99
- export interface UpdateDocumentResult {
100
- documentId: string;
101
- documentVersionId: string;
102
- }
103
- export interface UpdateDocumentWithPatchesResult {
104
- documentId: string;
105
- documentVersionId: string;
106
- }
107
- export interface UpdateDocumentSystemFieldsResult {
108
- documentId: string;
109
- /** The path actually written, or `undefined` when no path write occurred. */
110
- path?: string;
111
- /** Whether the advertised-locale set was rewritten this call. */
112
- availableLocalesWritten: boolean;
113
- }
114
- export interface ChangeStatusResult {
115
- previousStatus: string;
116
- newStatus: string;
117
- }
118
- export interface UnpublishResult {
119
- archivedCount: number;
120
- }
121
- export interface DeleteDocumentResult {
122
- deletedVersionCount: number;
123
- }
124
- export interface RestoreVersionResult {
125
- documentId: string;
126
- documentVersionId: string;
127
- sourceVersionId: string;
128
- }
129
- export interface CopyToLocaleResult {
130
- documentId: string;
131
- documentVersionId: string;
132
- /** Source locale read for the copy. */
133
- sourceLocale: string;
134
- /** Target locale into which the source's localized leaves were written. */
135
- targetLocale: string;
136
- /**
137
- * Number of localized field values copied from source to target. Useful
138
- * for UI toasts ("Copied 4 fields from EN to FR"). A zero result means
139
- * the source had no localized content to copy into the target under the
140
- * chosen merge rule (e.g. `overwrite: false` and target was already
141
- * fully populated).
142
- */
143
- fieldsUpdated: number;
144
- }
145
- export interface DeleteLocaleResult {
146
- documentId: string;
147
- /** The newly-created version that omits the deleted locale's content. */
148
- documentVersionId: string;
149
- /** The content locale that was removed. */
150
- locale: string;
151
- }
152
- export interface DuplicateDocumentResult {
153
- /** The newly-created document's id. */
154
- documentId: string;
155
- /** The newly-created version id (every duplicate starts at version 1). */
156
- documentVersionId: string;
157
- /** The id of the document this duplicate was cloned from. */
158
- sourceDocumentId: string;
159
- /**
160
- * Final `path` written into `byline_document_paths` for the new document.
161
- * Surfaced in the result so the UI can include it in success toasts /
162
- * navigate to it directly.
163
- */
164
- newPath: string;
165
- /**
166
- * `true` when the candidate path collided with an existing row and the
167
- * lifecycle retried with a short-UUID suffix. UIs can surface a hint that
168
- * the auto-generated path is uglier than usual so the editor knows to
169
- * adjust it via the path widget.
170
- */
171
- pathRetried: boolean;
172
- }
173
- /**
174
- * Create a new document.
175
- *
176
- * Flow:
177
- * 1. Default-locale enforcement: reject if `params.locale` is anything
178
- * other than the configured default content locale (a brand-new
179
- * document's canonical `path` lives in the default locale).
180
- * 2. `normaliseDateFields(data)`
181
- * 3. `hooks.beforeCreate({ data, collectionPath })`
182
- * 4. Resolve `path` — explicit `params.path` → derive via `useAsPath`
183
- * → UUID fallback.
184
- * 5. `db.commands.documents.createDocumentVersion(...)` (action = 'create')
185
- * 6. `hooks.afterCreate({ data, collectionPath, documentId, documentVersionId })`
186
- */
187
- export declare function createDocument(ctx: DocumentLifecycleContext, params: {
188
- data: Record<string, any>;
189
- locale?: string;
190
- status?: string;
191
- /**
192
- * Explicit, user-supplied path (e.g. from the admin sidebar widget
193
- * or an SDK caller importing legacy content). When omitted, the
194
- * lifecycle derives the value from `definition.useAsPath`.
195
- */
196
- path?: string;
197
- /**
198
- * The editorial advertised-locale set (from the admin available-locales
199
- * sidebar widget). Document-grain and sticky like `path`: passed straight
200
- * to the storage primitive, which replaces the document's rows wholesale.
201
- * `undefined` writes nothing (a new document starts with an empty set —
202
- * the safe opt-in default); `[]` clears it. See docs/I18N.md.
203
- */
204
- availableLocales?: string[];
205
- }): Promise<CreateDocumentResult>;
206
- /**
207
- * Update a document via full replacement (PUT semantics).
208
- *
209
- * Unlike the previous implementation, this now fetches the current version
210
- * from storage to provide a real `originalData` to hooks.
211
- *
212
- * Flow:
213
- * 1. Fetch current document via `getDocumentById({ reconstruct: true })`
214
- * 2. `normaliseDateFields(data)`
215
- * 3. `hooks.beforeUpdate({ data, originalData, collectionPath })`
216
- * 4. `db.commands.documents.createDocumentVersion(...)` (action = 'update')
217
- * 5. `hooks.afterUpdate({ data, originalData, collectionPath, documentId, documentVersionId })`
218
- */
219
- export declare function updateDocument(ctx: DocumentLifecycleContext, params: {
220
- documentId: string;
221
- data: Record<string, any>;
222
- locale?: string;
223
- /**
224
- * Explicit path override. When omitted, the previous version's path
225
- * carries forward unchanged (sticky). The lifecycle never re-derives
226
- * `path` from the source field on update — that is an explicit user
227
- * action driven by the admin path widget.
228
- */
229
- path?: string;
230
- /**
231
- * The editorial advertised-locale set. `undefined` leaves the existing
232
- * set untouched (sticky — document-grain, like `path`); an explicit array
233
- * (empty included) replaces it wholesale. Driven by the admin
234
- * available-locales sidebar widget. See docs/I18N.md.
235
- */
236
- availableLocales?: string[];
237
- }): Promise<UpdateDocumentResult>;
238
- /**
239
- * Update a document via patch application.
240
- *
241
- * Flow:
242
- * 1. Fetch current document via `getDocumentById({ reconstruct: true })`
243
- * 2. Optimistic concurrency check on `documentVersionId`
244
- * 3. `applyPatches(definition, originalData, patches)` → `nextData`
245
- * 4. `normaliseDateFields(nextData)`
246
- * 5. `hooks.beforeUpdate({ data: nextData, originalData, collectionPath })`
247
- * 6. `db.commands.documents.createDocumentVersion(...)` (action = 'update')
248
- * 7. `hooks.afterUpdate({ data: nextData, originalData, collectionPath, documentId, documentVersionId })`
249
- *
250
- * @throws {BylineError} ERR_CONFLICT if the supplied `documentVersionId` does not match the current version.
251
- * @throws {BylineError} ERR_PATCH_FAILED if `applyPatches` fails.
252
- */
253
- export declare function updateDocumentWithPatches(ctx: DocumentLifecycleContext, params: {
254
- documentId: string;
255
- patches: DocumentPatch[];
256
- /** Client-supplied version ID for optimistic concurrency. */
257
- documentVersionId?: string;
258
- locale?: string;
259
- /**
260
- * Explicit path override (typically supplied alongside patches when
261
- * the admin path widget has been edited). When omitted, sticky from
262
- * the previous version.
263
- */
264
- path?: string;
265
- /**
266
- * The editorial advertised-locale set (typically supplied alongside
267
- * patches when the admin available-locales widget has been edited).
268
- * `undefined` leaves the existing set untouched (sticky); an explicit
269
- * array replaces it wholesale. See docs/I18N.md.
270
- */
271
- availableLocales?: string[];
272
- }): Promise<UpdateDocumentWithPatchesResult>;
273
- /**
274
- * Write a document's system-managed, document-grain fields — `path` and the
275
- * editorial `availableLocales` set — **without** minting a new version or
276
- * touching workflow status.
277
- *
278
- * These fields are document-grain (they live in `byline_document_paths` and
279
- * `byline_document_available_locales`, keyed by logical document, sticky across
280
- * versions), so a workflow status change would falsely imply the edit is gated
281
- * behind publish. It is not: the write is immediate and applies across every
282
- * version. This service backs the admin path / available-locales widgets'
283
- * direct-write Save (the `direct-write` and `both` dirty-reason cases). The
284
- * public *advertised* set remains the intersection of `availableLocales` with
285
- * the resolved version's completeness ledger. See docs/I18N.md.
286
- *
287
- * Flow:
288
- * 1. `assertActorCanPerform('update')` — same auth gate as content writes.
289
- * 2. Fetch the document to resolve its `source_locale` anchor + current path.
290
- * 3. Path (when supplied): `resolvePathForUpdate` enforces the source-locale
291
- * rule (translation-locale path edits are dropped with a warn); a real
292
- * change is written via `updateDocumentPath`, mapping the unique-constraint
293
- * violation to `ERR_PATH_CONFLICT`.
294
- * 4. `availableLocales` (when supplied): rewritten wholesale via
295
- * `setDocumentAvailableLocales`.
296
- *
297
- * No content hooks fire — these are not content writes. Accountability for
298
- * these mutations is the job of the (planned) document-grain audit log.
299
- *
300
- * @throws {BylineError} ERR_NOT_FOUND if the document does not exist.
301
- * @throws {BylineError} ERR_PATH_CONFLICT if the path is already in use.
302
- */
303
- export declare function updateDocumentSystemFields(ctx: DocumentLifecycleContext, params: {
304
- documentId: string;
305
- locale?: string;
306
- /**
307
- * Explicit path override from the path widget. `null` / empty / omitted
308
- * means "no path write" (the existing row stays sticky). A non-empty
309
- * string is written when the request locale is the document's source
310
- * locale; on a translation locale it is dropped with a warn.
311
- */
312
- path?: string | null;
313
- /**
314
- * The editorial advertised-locale set from the available-locales widget.
315
- * `undefined` means "no advertised-locale write"; an explicit array — `[]`
316
- * included — replaces the set wholesale.
317
- */
318
- availableLocales?: string[];
319
- }): Promise<UpdateDocumentSystemFieldsResult>;
320
- /**
321
- * Change a document's workflow status.
322
- *
323
- * Flow:
324
- * 1. Fetch current document metadata
325
- * 2. Validate transition via `validateStatusTransition()`
326
- * 3. `hooks.beforeStatusChange({ documentId, documentVersionId, collectionPath, previousStatus, nextStatus })`
327
- * 4. `db.commands.documents.setDocumentStatus(...)` — in-place mutation
328
- * 5. Auto-archive: if transitioning to `'published'`, archive other published versions
329
- * 6. `hooks.afterStatusChange({ documentId, documentVersionId, collectionPath, previousStatus, nextStatus })`
330
- */
331
- export declare function changeDocumentStatus(ctx: DocumentLifecycleContext, params: {
332
- documentId: string;
333
- nextStatus: string;
334
- }): Promise<ChangeStatusResult>;
335
- /**
336
- * Unpublish a document by archiving its published version(s).
337
- *
338
- * Flow:
339
- * 1. `hooks.beforeUnpublish({ documentId, collectionPath })`
340
- * 2. `db.commands.documents.archivePublishedVersions(...)`
341
- * 3. `hooks.afterUnpublish({ documentId, collectionPath, archivedCount })`
342
- */
343
- export declare function unpublishDocument(ctx: DocumentLifecycleContext, params: {
344
- documentId: string;
345
- }): Promise<UnpublishResult>;
346
- /**
347
- * Restore a historical document version as the new current version.
348
- *
349
- * Reads the source version with `locale: 'all'` so the entire multi-locale
350
- * field tree (with `_id` / `_type` meta inlined onto blocks and array items
351
- * by `reconstructFromUnifiedRows`) is captured. That tree is then re-emitted
352
- * through `createDocumentVersion` with `locale: 'all'`, which produces a
353
- * fresh version row with a new UUIDv7 id and the latest `created_at`. The
354
- * `current_documents` view (`ROW_NUMBER() OVER PARTITION BY document_id
355
- * ORDER BY created_at DESC`) automatically promotes the new row to current.
356
- *
357
- * Status is hard-defaulted to the workflow's first status — restoring an
358
- * old `published` version must never silently re-publish content. The user
359
- * runs the restored draft through the normal workflow.
360
- *
361
- * `path` is sticky from the previous current version (not from the source),
362
- * matching the semantics of `updateDocument`. A path change made between
363
- * the source and now should not be undone by the restore.
364
- *
365
- * Auth reuses the `update` ability — restore is conceptually an edit
366
- * against an existing document.
367
- *
368
- * Hooks: fires `beforeUpdate` / `afterUpdate` with a `restore: { sourceVersionId }`
369
- * field on the context. Userland hooks that need to react differently
370
- * (e.g. tag the audit entry, skip search re-index) can branch on its
371
- * presence.
372
- *
373
- * Flow:
374
- * 1. Auth: `update` ability.
375
- * 2. Read source version with `locale: 'all'`.
376
- * 3. Validate source belongs to `documentId` (defence against forged
377
- * cross-document version ids).
378
- * 4. Read current version metadata; reject if the source IS already the
379
- * current version (nothing to restore).
380
- * 5. Read current document with reconstruction for hook `originalData`.
381
- * 6. `hooks.beforeUpdate({ data, originalData, collectionPath, restore })`
382
- * 7. `db.commands.documents.createDocumentVersion(...)` with
383
- * `action: 'restore'`, `locale: 'all'`, sticky path, default status.
384
- * 8. `hooks.afterUpdate({ ..., restore })`
385
- */
386
- export declare function restoreDocumentVersion(ctx: DocumentLifecycleContext, params: {
387
- documentId: string;
388
- sourceVersionId: string;
389
- }): Promise<RestoreVersionResult>;
390
- /**
391
- * Soft-delete a document.
392
- *
393
- * Marks all versions of the document as deleted (`is_deleted = true`). The
394
- * `current_documents` view automatically filters deleted rows, so the
395
- * document disappears from all list / page queries without physically
396
- * removing data.
397
- *
398
- * When the collection has any upload-capable image/file field and
399
- * `ctx.storage` is provided, every original file and persisted variant
400
- * across those fields is also removed from storage after the DB
401
- * soft-delete succeeds. Variant paths are read from the field value's
402
- * `variants` array (no re-derivation from `upload.sizes`), so cleanup
403
- * stays correct even if the size set changed between upload and delete.
404
- * File cleanup failures are logged but are non-fatal.
405
- *
406
- * Flow:
407
- * 1. Fetch current document (reconstruct when upload-capable fields exist)
408
- * 2. `hooks.beforeDelete({ documentId, collectionPath })`
409
- * 3. `db.commands.documents.softDeleteDocument({ document_id })`
410
- * 4. Storage file + variant cleanup (skipped when no upload fields, non-fatal)
411
- * 5. `hooks.afterDelete({ documentId, collectionPath })`
412
- */
413
- export declare function deleteDocument(ctx: DocumentLifecycleContext, params: {
414
- documentId: string;
415
- }): Promise<DeleteDocumentResult>;
416
- /**
417
- * Duplicate a document, cloning all of its locales into a brand-new
418
- * document atomically.
419
- *
420
- * Flow:
421
- * 1. `assertActorCanPerform('create')` — duplicating is a create at the
422
- * ability level. The source must be readable (any RBAC scoping the
423
- * caller has applies via the storage read).
424
- * 2. Fetch the source with `locale: 'all'` so a single read carries the
425
- * full multi-locale tree forward.
426
- * 3. Deep-clone the source fields; strip block / array-item `_id` meta
427
- * so the new doc gets fresh identities.
428
- * 4. Append `" (copy)"` to the `useAsTitle` field's value(s).
429
- * 5. Derive a candidate path from the default-locale suffixed title.
430
- * 6. `hooks.beforeCreate({ data, collectionPath, duplicate })`.
431
- * 7. `db.commands.documents.createDocumentVersion(...)` with `locale:
432
- * 'all'`, `action: 'create'`, no `documentId` → fresh document_id.
433
- * On `ERR_PATH_CONFLICT` retry once with the candidate path plus a
434
- * 4-char UUID suffix; bounded to two attempts, no existence
435
- * pre-check, no TOCTOU race.
436
- * 8. `hooks.afterCreate({ data, collectionPath, documentId,
437
- * documentVersionId, duplicate })`.
438
- *
439
- * The write is atomic at the storage layer — a partial duplicate is
440
- * structurally impossible. Editors are expected to rename both the
441
- * title and the system path after the operation; the UI surfaces a
442
- * confirmation modal that calls this out.
443
- */
444
- export declare function duplicateDocument(ctx: DocumentLifecycleContext, params: {
445
- sourceDocumentId: string;
446
- }): Promise<DuplicateDocumentResult>;
447
- /**
448
- * Copy a document's content from one locale into another, in place on
449
- * the same document.
450
- *
451
- * Reads the source and target locales separately (the storage layer
452
- * resolves localized fields to flat single-locale shapes when given a
453
- * specific `resolveLocale`). A schema-aware merge walker decides, leaf
454
- * by leaf, whether to take the source's value or keep the target's,
455
- * driven by the `overwrite` flag. The merged tree is written via
456
- * `createDocumentVersion({ action: 'copy_to_locale', locale: target })`
457
- * — the existing cross-locale carry-forward in the storage primitive
458
- * preserves every *other* locale's rows untouched.
459
- *
460
- * Non-localized fields are never altered by this operation: they live
461
- * on `locale: 'all'` rows and the merge walker passes the target's
462
- * value through so the write does not blank them.
463
- *
464
- * Path is sticky and lives on default-locale only; this operation never
465
- * touches `byline_document_paths`. Status resets to the workflow
466
- * default — translations land as drafts.
467
- *
468
- * Flow:
469
- * 1. `assertActorCanPerform('update')` — same gate as a translation save.
470
- * 2. Reject if `sourceLocale === targetLocale`.
471
- * 3. Fetch source via `getDocumentById({ locale: sourceLocale })`.
472
- * 4. Fetch target via `getDocumentById({ locale: targetLocale })`.
473
- * 5. `mergeLocaleData(definition.fields, source.fields, target.fields, overwrite)`.
474
- * 6. `hooks.beforeUpdate({ data, originalData, collectionPath, copyToLocale })`.
475
- * 7. `createDocumentVersion({ documentId, action: 'copy_to_locale',
476
- * locale: targetLocale, documentData, previousVersionId, status })`.
477
- * 8. `hooks.afterUpdate({ ..., copyToLocale })`.
478
- */
479
- export declare function copyToLocale(ctx: DocumentLifecycleContext, params: {
480
- documentId: string;
481
- sourceLocale: string;
482
- targetLocale: string;
483
- overwrite: boolean;
484
- }): Promise<CopyToLocaleResult>;
485
- /**
486
- * Remove one content locale's data from a document, in place on the same
487
- * document, by writing a new immutable version that omits that locale's
488
- * store rows (every other locale and all non-localized `'all'` rows are
489
- * carried forward by the storage primitive).
490
- *
491
- * The default content locale is the document's anchor (path + source_locale)
492
- * and can never be removed — rejected up front. The new version lands as the
493
- * workflow's default status (a fresh draft), exactly like `copyToLocale`: the
494
- * previously-published version keeps serving — including the locale being
495
- * removed — until the new version is reviewed and published. The deletion is
496
- * recoverable: the prior version still holds the locale, so restoring it
497
- * brings the content back.
498
- *
499
- * Flow:
500
- * 1. `assertActorCanPerform('update')` — removing a translation is an edit.
501
- * 2. Reject `locale === defaultLocale`.
502
- * 3. Read the document in the target locale (validates existence; supplies
503
- * `originalData` for hooks and the availability set for the presence
504
- * check).
505
- * 4. Reject when the locale has no content to delete.
506
- * 5. `hooks.beforeUpdate({ …, deleteLocale: { locale } })`.
507
- * 6. `db.commands.documents.deleteDocumentLocale({ …, status: default })`.
508
- * 7. `hooks.afterUpdate({ …, deleteLocale: { locale } })`.
509
- */
510
- export declare function deleteLocale(ctx: DocumentLifecycleContext, params: {
511
- documentId: string;
512
- locale: string;
513
- }): Promise<DeleteLocaleResult>;