|
1 | 1 | /** |
2 | | - * Keep a record's attachments across a slug rename. |
| 2 | + * Rename helpers — specs/behaviors/slug-handles.md: a rename changes the |
| 3 | + * handle and nothing else about the entity, so the record's attachments |
| 4 | + * move with it and any key or link pointing under its old path is |
| 5 | + * rewritten. |
3 | 6 | * |
4 | | - * A rename deletes the record at its old path and upserts it at the new one. |
5 | | - * gitsheets' delete also removes the record's attachment folder, so without |
6 | | - * this the attachments (avatars, featured images) are lost and the on-record |
7 | | - * key points at a path that no longer exists. specs/behaviors/slug-handles.md: |
8 | | - * a rename changes the handle and nothing else about the entity. |
9 | | - * |
10 | | - * Interim until gitsheets can move a record together with its attachments |
11 | | - * (plans/slug-rename-attachments.md). |
| 7 | + * The move itself is gitsheets' rename-on-upsert (≥ 2.8): upserting a |
| 8 | + * record annotated with its *current* path, when its fields now render a |
| 9 | + * different path, moves the record file and its attachment folder in one |
| 10 | + * step. |
12 | 11 | */ |
13 | | -import type { BlobHandle } from 'gitsheets'; |
14 | | - |
15 | | -interface AttachmentSheet<T> { |
16 | | - getAttachments(record: T | string): Promise<Record<string, BlobHandle> | null>; |
17 | | - setAttachments(record: T | string, attachments: Record<string, BlobHandle>): Promise<void>; |
18 | | -} |
| 12 | +import { RECORD_PATH_KEY } from 'gitsheets'; |
19 | 13 |
|
20 | | -/** Read the record's attachments before its old path is deleted. */ |
21 | | -export async function takeAttachments<T>( |
22 | | - sheet: AttachmentSheet<T>, |
23 | | - record: T, |
24 | | -): Promise<Record<string, BlobHandle> | null> { |
25 | | - const attachments = await sheet.getAttachments(record); |
26 | | - return attachments && Object.keys(attachments).length > 0 ? attachments : null; |
| 14 | +interface MovableSheet<T, O> { |
| 15 | + pathForRecord(record: T): Promise<string>; |
| 16 | + upsert(record: T, opts?: O): Promise<unknown>; |
27 | 17 | } |
28 | 18 |
|
29 | | -/** Re-attach to the record at its new path (blobs are reused by hash). */ |
30 | | -export async function restoreAttachments<T>( |
31 | | - sheet: AttachmentSheet<T>, |
32 | | - record: T, |
33 | | - attachments: Record<string, BlobHandle> | null, |
| 19 | +/** |
| 20 | + * Write `updated` in place of `existing`, moving the record (and its |
| 21 | + * attachments) when its path changed. The old path is rendered from |
| 22 | + * `existing` rather than read from its path annotation, which a record |
| 23 | + * re-parsed through Zod no longer carries. |
| 24 | + */ |
| 25 | +export async function upsertMoved<T extends object, O = never>( |
| 26 | + sheet: MovableSheet<T, O>, |
| 27 | + existing: T, |
| 28 | + updated: T, |
| 29 | + opts?: O, |
34 | 30 | ): Promise<void> { |
35 | | - if (attachments) await sheet.setAttachments(record, attachments); |
| 31 | + const oldPath = await sheet.pathForRecord(existing); |
| 32 | + await sheet.upsert(Object.assign({ ...updated }, { [RECORD_PATH_KEY]: oldPath }) as T, opts); |
36 | 33 | } |
37 | 34 |
|
38 | 35 | /** |
|
0 commit comments