mindoodb 0.0.44 → 0.0.46
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/README.md +9 -0
- package/dist/appendonlystores/network/HttpTransport.d.ts.map +1 -1
- package/dist/appendonlystores/network/HttpTransport.js +4 -0
- package/dist/appendonlystores/network/HttpTransport.js.map +1 -1
- package/dist/browser/index.mjs +31 -0
- package/dist/core/BaseMindooDB.d.ts +74 -0
- package/dist/core/BaseMindooDB.d.ts.map +1 -1
- package/dist/core/BaseMindooDB.js +160 -0
- package/dist/core/BaseMindooDB.js.map +1 -1
- package/dist/core/BaseMindooTenant.d.ts +22 -0
- package/dist/core/BaseMindooTenant.d.ts.map +1 -1
- package/dist/core/BaseMindooTenant.js +57 -6
- package/dist/core/BaseMindooTenant.js.map +1 -1
- package/dist/core/BaseMindooTenantFactory.d.ts.map +1 -1
- package/dist/core/BaseMindooTenantFactory.js +31 -9
- package/dist/core/BaseMindooTenantFactory.js.map +1 -1
- package/dist/core/copy/attachments.d.ts +53 -0
- package/dist/core/copy/attachments.d.ts.map +1 -0
- package/dist/core/copy/attachments.js +186 -0
- package/dist/core/copy/attachments.js.map +1 -0
- package/dist/core/copy/copyDocument.d.ts +86 -0
- package/dist/core/copy/copyDocument.d.ts.map +1 -0
- package/dist/core/copy/copyDocument.js +369 -0
- package/dist/core/copy/copyDocument.js.map +1 -0
- package/dist/core/copy/copyDocuments.d.ts +57 -0
- package/dist/core/copy/copyDocuments.d.ts.map +1 -0
- package/dist/core/copy/copyDocuments.js +167 -0
- package/dist/core/copy/copyDocuments.js.map +1 -0
- package/dist/core/copy/entryRewrite.d.ts +49 -0
- package/dist/core/copy/entryRewrite.d.ts.map +1 -0
- package/dist/core/copy/entryRewrite.js +74 -0
- package/dist/core/copy/entryRewrite.js.map +1 -0
- package/dist/core/copy/feasibility.d.ts +73 -0
- package/dist/core/copy/feasibility.d.ts.map +1 -0
- package/dist/core/copy/feasibility.js +169 -0
- package/dist/core/copy/feasibility.js.map +1 -0
- package/dist/core/copy/flatten.d.ts +55 -0
- package/dist/core/copy/flatten.d.ts.map +1 -0
- package/dist/core/copy/flatten.js +155 -0
- package/dist/core/copy/flatten.js.map +1 -0
- package/dist/core/copy/host.d.ts +92 -0
- package/dist/core/copy/host.d.ts.map +1 -0
- package/dist/core/copy/host.js +16 -0
- package/dist/core/copy/host.js.map +1 -0
- package/dist/core/copy/preflight.d.ts +62 -0
- package/dist/core/copy/preflight.d.ts.map +1 -0
- package/dist/core/copy/preflight.js +98 -0
- package/dist/core/copy/preflight.js.map +1 -0
- package/dist/core/copy/provenance.d.ts +74 -0
- package/dist/core/copy/provenance.d.ts.map +1 -0
- package/dist/core/copy/provenance.js +91 -0
- package/dist/core/copy/provenance.js.map +1 -0
- package/dist/core/copy/types.d.ts +418 -0
- package/dist/core/copy/types.d.ts.map +1 -0
- package/dist/core/copy/types.js +21 -0
- package/dist/core/copy/types.js.map +1 -0
- package/dist/core/crypto/DomainStatement.d.ts +44 -0
- package/dist/core/crypto/DomainStatement.d.ts.map +1 -0
- package/dist/core/crypto/DomainStatement.js +71 -0
- package/dist/core/crypto/DomainStatement.js.map +1 -0
- package/dist/core/crypto/EntrySignature.d.ts +109 -15
- package/dist/core/crypto/EntrySignature.d.ts.map +1 -1
- package/dist/core/crypto/EntrySignature.js +83 -22
- package/dist/core/crypto/EntrySignature.js.map +1 -1
- package/dist/core/index.d.ts +21 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +54 -3
- package/dist/core/index.js.map +1 -1
- package/dist/core/timestamps/timestampClient.d.ts +88 -0
- package/dist/core/timestamps/timestampClient.d.ts.map +1 -0
- package/dist/core/timestamps/timestampClient.js +157 -0
- package/dist/core/timestamps/timestampClient.js.map +1 -0
- package/dist/core/types.d.ts +134 -4
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/node/server/MindooDBServer.d.ts +37 -0
- package/dist/node/server/MindooDBServer.d.ts.map +1 -1
- package/dist/node/server/MindooDBServer.js +157 -0
- package/dist/node/server/MindooDBServer.js.map +1 -1
- package/dist/node/server/config.d.ts.map +1 -1
- package/dist/node/server/config.js +16 -0
- package/dist/node/server/config.js.map +1 -1
- package/dist/node/server/timestampProxy.d.ts +129 -0
- package/dist/node/server/timestampProxy.d.ts.map +1 -0
- package/dist/node/server/timestampProxy.js +361 -0
- package/dist/node/server/timestampProxy.js.map +1 -0
- package/dist/node/server/types.d.ts +20 -0
- package/dist/node/server/types.d.ts.map +1 -1
- package/dist/node/server/types.js +12 -0
- package/dist/node/server/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attachment chunk copying.
|
|
3
|
+
*
|
|
4
|
+
* Chunk entry ids embed the document id (`<docId>_a_<fileUuid7>_<chunkUuid>`)
|
|
5
|
+
* and a document's payload points at them by id through
|
|
6
|
+
* `_attachments[].lastChunkId`. That forces two different strategies depending
|
|
7
|
+
* on whether the copy crosses a store boundary:
|
|
8
|
+
*
|
|
9
|
+
* - **Different store** (the normal case, including every shard): keep the
|
|
10
|
+
* chunk ids exactly as they are and only re-home the `docId` metadata field.
|
|
11
|
+
* Nothing can collide, because the target store has never seen those ids, and
|
|
12
|
+
* leaving them alone is what lets the document's change bytes replay
|
|
13
|
+
* unmodified — the payload's `lastChunkId` values still resolve.
|
|
14
|
+
*
|
|
15
|
+
* - **Same store, new document id** (duplicating a document in place): the ids
|
|
16
|
+
* must be re-prefixed. Reusing them is not an option: the stores implement an
|
|
17
|
+
* id write as remove-then-insert, so a verbatim copy would overwrite the
|
|
18
|
+
* source's own chunk metadata and re-point it at the copy. The copied history
|
|
19
|
+
* keeps referring to the source's chunks (still present in the same store,
|
|
20
|
+
* holding identical bytes, so attachment time travel still works), and one
|
|
21
|
+
* trailing change re-points the copy's current revision at the chunks it owns.
|
|
22
|
+
*
|
|
23
|
+
* @module
|
|
24
|
+
*/
|
|
25
|
+
import type { StoreEntryMetadata } from "../types";
|
|
26
|
+
import type { CopyEngineHost } from "./host";
|
|
27
|
+
import type { CopyContext } from "./feasibility";
|
|
28
|
+
import type { CopySigningKey, CopyProgressReporter } from "./copyDocument";
|
|
29
|
+
import type { CopyDocumentOptions, CopyFeasibility } from "./types";
|
|
30
|
+
/**
|
|
31
|
+
* The result of copying a document's attachment chunks, plus the deferred step
|
|
32
|
+
* that can only run once the document's own entries are in place.
|
|
33
|
+
*
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
export interface AttachmentCopyPlan {
|
|
37
|
+
/** True when the abort signal fired before the chunks were fully copied. */
|
|
38
|
+
cancelled: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Same-store copies only: append the one trailing change that re-points
|
|
41
|
+
* `_attachments[].lastChunkId` at the re-prefixed chunks. A no-op otherwise.
|
|
42
|
+
*/
|
|
43
|
+
finalize(signing?: CopySigningKey): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Copy every attachment chunk belonging to a document.
|
|
47
|
+
*
|
|
48
|
+
* Runs before the document's own entries so that the signed `attachmentRefs`
|
|
49
|
+
* snapshot each copied revision carries always describes chunks that already
|
|
50
|
+
* exist in the target.
|
|
51
|
+
*/
|
|
52
|
+
export declare function copyDocumentAttachments(source: CopyEngineHost, target: CopyEngineHost, context: CopyContext, feasibility: CopyFeasibility, options: CopyDocumentOptions, reporter: CopyProgressReporter, sourceDagEntries: StoreEntryMetadata[]): Promise<AttachmentCopyPlan>;
|
|
53
|
+
//# sourceMappingURL=attachments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachments.d.ts","sourceRoot":"","sources":["../../../src/core/copy/attachments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAMH,OAAO,KAAK,EAAc,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC3E,OAAO,KAAK,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAKpE;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,4EAA4E;IAC5E,SAAS,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD;AAQD;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,WAAW,EACpB,WAAW,EAAE,eAAe,EAC5B,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,oBAAoB,EAC9B,gBAAgB,EAAE,kBAAkB,EAAE,GACrC,OAAO,CAAC,kBAAkB,CAAC,CA8G7B"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Attachment chunk copying.
|
|
4
|
+
*
|
|
5
|
+
* Chunk entry ids embed the document id (`<docId>_a_<fileUuid7>_<chunkUuid>`)
|
|
6
|
+
* and a document's payload points at them by id through
|
|
7
|
+
* `_attachments[].lastChunkId`. That forces two different strategies depending
|
|
8
|
+
* on whether the copy crosses a store boundary:
|
|
9
|
+
*
|
|
10
|
+
* - **Different store** (the normal case, including every shard): keep the
|
|
11
|
+
* chunk ids exactly as they are and only re-home the `docId` metadata field.
|
|
12
|
+
* Nothing can collide, because the target store has never seen those ids, and
|
|
13
|
+
* leaving them alone is what lets the document's change bytes replay
|
|
14
|
+
* unmodified — the payload's `lastChunkId` values still resolve.
|
|
15
|
+
*
|
|
16
|
+
* - **Same store, new document id** (duplicating a document in place): the ids
|
|
17
|
+
* must be re-prefixed. Reusing them is not an option: the stores implement an
|
|
18
|
+
* id write as remove-then-insert, so a verbatim copy would overwrite the
|
|
19
|
+
* source's own chunk metadata and re-point it at the copy. The copied history
|
|
20
|
+
* keeps referring to the source's chunks (still present in the same store,
|
|
21
|
+
* holding identical bytes, so attachment time travel still works), and one
|
|
22
|
+
* trailing change re-points the copy's current revision at the chunks it owns.
|
|
23
|
+
*
|
|
24
|
+
* @module
|
|
25
|
+
*/
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.copyDocumentAttachments = copyDocumentAttachments;
|
|
28
|
+
const DocumentDagAnalysis_1 = require("../DocumentDagAnalysis");
|
|
29
|
+
const idGeneration_1 = require("../utils/idGeneration");
|
|
30
|
+
const timeSource_1 = require("../utils/timeSource");
|
|
31
|
+
const types_1 = require("../types");
|
|
32
|
+
const entryRewrite_1 = require("./entryRewrite");
|
|
33
|
+
/** Default entries written per `putEntries` call. */
|
|
34
|
+
const DEFAULT_BATCH_SIZE = 200;
|
|
35
|
+
/** A plan that does nothing, for documents without attachments. */
|
|
36
|
+
const NO_ATTACHMENTS = {
|
|
37
|
+
cancelled: false,
|
|
38
|
+
finalize: async () => { },
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Copy every attachment chunk belonging to a document.
|
|
42
|
+
*
|
|
43
|
+
* Runs before the document's own entries so that the signed `attachmentRefs`
|
|
44
|
+
* snapshot each copied revision carries always describes chunks that already
|
|
45
|
+
* exist in the target.
|
|
46
|
+
*/
|
|
47
|
+
async function copyDocumentAttachments(source, target, context, feasibility, options, reporter, sourceDagEntries) {
|
|
48
|
+
if (options.includeAttachments === false) {
|
|
49
|
+
return NO_ATTACHMENTS;
|
|
50
|
+
}
|
|
51
|
+
const sourceChunks = (await source.attachmentStore.findNewEntriesForDoc([], context.sourceDocId)).filter((entry) => entry.entryType === "attachment_chunk");
|
|
52
|
+
if (sourceChunks.length === 0) {
|
|
53
|
+
return NO_ATTACHMENTS;
|
|
54
|
+
}
|
|
55
|
+
// Only a same-store copy is forced to rename chunks; see the module comment.
|
|
56
|
+
const reprefixChunkIds = context.sameStore;
|
|
57
|
+
const chunkTargetDocId = context.targetDocId;
|
|
58
|
+
const remapChunkId = (chunkId) => reprefixChunkIds
|
|
59
|
+
? (0, entryRewrite_1.remapEntryId)(chunkId, context.sourceDocId, chunkTargetDocId)
|
|
60
|
+
: chunkId;
|
|
61
|
+
const ordered = (0, DocumentDagAnalysis_1.orderDagEntriesCausally)(sourceChunks);
|
|
62
|
+
const targetIds = ordered.map((entry) => remapChunkId(entry.id));
|
|
63
|
+
const alreadyPresent = new Set(await target.attachmentStore.hasEntries(targetIds));
|
|
64
|
+
const batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;
|
|
65
|
+
const signing = options.signingKeyPair && options.signingKeyPassword
|
|
66
|
+
? {
|
|
67
|
+
signingKeyPair: options.signingKeyPair,
|
|
68
|
+
signingKeyPassword: options.signingKeyPassword,
|
|
69
|
+
}
|
|
70
|
+
: undefined;
|
|
71
|
+
const copierPublicKey = signing?.signingKeyPair.publicKey ??
|
|
72
|
+
(await target.tenant.getCurrentUserId()).userSigningPublicKey;
|
|
73
|
+
const copyTime = (0, timeSource_1.semanticNow)();
|
|
74
|
+
const copiedAttachmentIds = new Set();
|
|
75
|
+
for (let offset = 0; offset < ordered.length; offset += batchSize) {
|
|
76
|
+
if (options.signal?.aborted) {
|
|
77
|
+
return { cancelled: true, finalize: async () => { } };
|
|
78
|
+
}
|
|
79
|
+
const slice = ordered.slice(offset, offset + batchSize);
|
|
80
|
+
const needed = slice.filter((_entry, index) => !alreadyPresent.has(targetIds[offset + index]));
|
|
81
|
+
if (needed.length === 0)
|
|
82
|
+
continue;
|
|
83
|
+
const fetched = await source.attachmentStore.getEntries(needed.map((entry) => entry.id));
|
|
84
|
+
const transformed = [];
|
|
85
|
+
for (const sourceEntry of fetched) {
|
|
86
|
+
transformed.push(feasibility.strategy === "graft"
|
|
87
|
+
? (0, entryRewrite_1.stripWitnessReceipt)({ ...sourceEntry })
|
|
88
|
+
: await replayChunk(source, target, context, feasibility, sourceEntry, {
|
|
89
|
+
id: remapChunkId(sourceEntry.id),
|
|
90
|
+
dependencyIds: sourceEntry.dependencyIds.map(remapChunkId),
|
|
91
|
+
createdAt: copyTime,
|
|
92
|
+
createdByPublicKey: copierPublicKey,
|
|
93
|
+
signing,
|
|
94
|
+
}));
|
|
95
|
+
if (sourceEntry.attachmentId) {
|
|
96
|
+
copiedAttachmentIds.add(sourceEntry.attachmentId);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (transformed.length > 0) {
|
|
100
|
+
await target.attachmentStore.putEntries(transformed);
|
|
101
|
+
reporter.copiedEntries += transformed.length;
|
|
102
|
+
for (const entry of transformed) {
|
|
103
|
+
reporter.copiedBytes += entry.encryptedSize;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
reporter.emit("transferring", `Copied ${transformed.length} attachment chunks of ${context.sourceDocId}`);
|
|
107
|
+
}
|
|
108
|
+
reporter.copiedAttachments += copiedAttachmentIds.size;
|
|
109
|
+
if (!reprefixChunkIds) {
|
|
110
|
+
return { cancelled: false, finalize: async () => { } };
|
|
111
|
+
}
|
|
112
|
+
// The re-prefixed chunks are in place but nothing points at them yet: the
|
|
113
|
+
// copied revisions still carry the source's chunk ids in their payloads. Look
|
|
114
|
+
// up the document's live attachment set from the newest signed refs snapshot
|
|
115
|
+
// (no decryption needed) and hand the re-pointing to the target database.
|
|
116
|
+
const liveRefs = latestAttachmentRefs(sourceDagEntries);
|
|
117
|
+
return {
|
|
118
|
+
cancelled: false,
|
|
119
|
+
finalize: async (finalizeSigning) => {
|
|
120
|
+
if (liveRefs.length === 0)
|
|
121
|
+
return;
|
|
122
|
+
const pointerRemap = new Map();
|
|
123
|
+
for (const ref of liveRefs) {
|
|
124
|
+
pointerRemap.set(ref.attachmentId, remapChunkId(ref.lastChunkId));
|
|
125
|
+
}
|
|
126
|
+
await target.remapAttachmentPointers(chunkTargetDocId, pointerRemap, finalizeSigning ?? signing);
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The document's current attachment set, read from the signed `attachmentRefs`
|
|
132
|
+
* snapshot on its newest revision.
|
|
133
|
+
*
|
|
134
|
+
* Every document revision carries the complete set as of that revision, so the
|
|
135
|
+
* newest one is authoritative — and reading it needs no decryption key.
|
|
136
|
+
*/
|
|
137
|
+
function latestAttachmentRefs(sourceDagEntries) {
|
|
138
|
+
let newest;
|
|
139
|
+
for (const entry of sourceDagEntries) {
|
|
140
|
+
if (!entry.attachmentRefs || entry.attachmentRefs.length === 0)
|
|
141
|
+
continue;
|
|
142
|
+
if (!newest || entry.createdAt > newest.createdAt) {
|
|
143
|
+
newest = entry;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return newest?.attachmentRefs ?? [];
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Re-home and re-sign one attachment chunk.
|
|
150
|
+
*
|
|
151
|
+
* Attachment payloads use a deterministic IV, so re-encrypting the same chunk
|
|
152
|
+
* under the same key reproduces the same ciphertext and `contentHash` — which
|
|
153
|
+
* is why a same-tenant copy into the same store deduplicates the bytes instead
|
|
154
|
+
* of storing them twice.
|
|
155
|
+
*/
|
|
156
|
+
async function replayChunk(source, target, context, feasibility, sourceEntry, rewrite) {
|
|
157
|
+
let encryptedData = sourceEntry.encryptedData;
|
|
158
|
+
let contentHash = sourceEntry.contentHash;
|
|
159
|
+
if (feasibility.requiresReEncryption) {
|
|
160
|
+
const plaintext = await source.decryptAttachmentPayload(sourceEntry.encryptedData, sourceEntry.decryptionKeyId);
|
|
161
|
+
encryptedData = await target.encryptAttachmentPayload(plaintext, context.targetDecryptionKeyId);
|
|
162
|
+
contentHash = await (0, idGeneration_1.computeContentHash)(encryptedData, target.tenant.getCryptoAdapter().getSubtle());
|
|
163
|
+
}
|
|
164
|
+
const signature = rewrite.signing
|
|
165
|
+
? await target.tenant.signPayloadWithKey(encryptedData, rewrite.signing.signingKeyPair, rewrite.signing.signingKeyPassword)
|
|
166
|
+
: await target.tenant.signPayload(encryptedData);
|
|
167
|
+
const entry = {
|
|
168
|
+
entryType: "attachment_chunk",
|
|
169
|
+
id: rewrite.id,
|
|
170
|
+
contentHash,
|
|
171
|
+
docId: context.targetDocId,
|
|
172
|
+
dependencyIds: rewrite.dependencyIds,
|
|
173
|
+
createdAt: rewrite.createdAt,
|
|
174
|
+
attachmentId: sourceEntry.attachmentId,
|
|
175
|
+
createdByPublicKey: rewrite.createdByPublicKey,
|
|
176
|
+
decryptionKeyId: context.targetDecryptionKeyId,
|
|
177
|
+
signature,
|
|
178
|
+
originalSize: sourceEntry.originalSize,
|
|
179
|
+
encryptedSize: encryptedData.length,
|
|
180
|
+
entryVersion: types_1.CURRENT_STORE_ENTRY_VERSION,
|
|
181
|
+
encryptedData,
|
|
182
|
+
};
|
|
183
|
+
entry.metadataSignature = await target.computeEntryMetadataSignature(entry, rewrite.signing);
|
|
184
|
+
return entry;
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=attachments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachments.js","sourceRoot":"","sources":["../../../src/core/copy/attachments.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The document copy engine.
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates the three strategies described in `docs/document-copy.md`:
|
|
5
|
+
* `flatten` (delegated to `flatten.ts`, which goes through the public document
|
|
6
|
+
* API), and the two history-preserving strategies implemented here.
|
|
7
|
+
*
|
|
8
|
+
* `graft` and `replay` share one pipeline — order the source revision graph
|
|
9
|
+
* causally, skip what the target already has, transform in batches, write — and
|
|
10
|
+
* differ only in the per-entry transform:
|
|
11
|
+
*
|
|
12
|
+
* - **graft** strips the witness receipt and changes nothing else, so the
|
|
13
|
+
* original authors' signatures remain valid.
|
|
14
|
+
* - **replay** re-homes each entry under the target document id and re-signs it
|
|
15
|
+
* as the copying user, recording the original author in a verifiable
|
|
16
|
+
* provenance record.
|
|
17
|
+
*
|
|
18
|
+
* @module
|
|
19
|
+
*/
|
|
20
|
+
import type { SigningKeyPair, StoreEntryMetadata } from "../types";
|
|
21
|
+
import type { CopyEngineHost } from "./host";
|
|
22
|
+
import { type CopyContext } from "./feasibility";
|
|
23
|
+
import type { CopyDocumentOptions, CopyDocumentResult, CopyFeasibility, CopyProgress, CopyProgressPhase } from "./types";
|
|
24
|
+
/**
|
|
25
|
+
* A resolved custom signing identity for a copy, or `undefined` to sign as the
|
|
26
|
+
* current user. Both fields are always present together.
|
|
27
|
+
*
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
export interface CopySigningKey {
|
|
31
|
+
signingKeyPair: SigningKeyPair;
|
|
32
|
+
signingKeyPassword: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Normalize the caller's signing options: a key pair is only usable together
|
|
36
|
+
* with its password, so a half-supplied pair is treated as absent.
|
|
37
|
+
*
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
40
|
+
export declare function resolveCopySigningKey(options: CopyDocumentOptions): CopySigningKey | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* The key the copy's entries will be signed with: the caller's service identity
|
|
43
|
+
* when supplied, the current user's otherwise.
|
|
44
|
+
*
|
|
45
|
+
* @internal
|
|
46
|
+
*/
|
|
47
|
+
export declare function resolveCopierPublicKey(target: CopyEngineHost, options: CopyDocumentOptions): Promise<string>;
|
|
48
|
+
/**
|
|
49
|
+
* Accumulates copy totals and emits {@link CopyProgress} events.
|
|
50
|
+
*
|
|
51
|
+
* Shared by the single-document and bulk paths so a bulk run reports one
|
|
52
|
+
* continuously rising total rather than restarting the counters per document.
|
|
53
|
+
*/
|
|
54
|
+
export declare class CopyProgressReporter {
|
|
55
|
+
private readonly onProgress?;
|
|
56
|
+
copiedEntries: number;
|
|
57
|
+
copiedBytes: number;
|
|
58
|
+
scannedEntries: number;
|
|
59
|
+
copiedAttachments: number;
|
|
60
|
+
totalSourceEntries?: number;
|
|
61
|
+
currentDocId?: string;
|
|
62
|
+
documentsCompleted?: number;
|
|
63
|
+
totalDocuments?: number;
|
|
64
|
+
constructor(onProgress?: ((progress: CopyProgress) => void) | undefined);
|
|
65
|
+
emit(phase: CopyProgressPhase, message: string): void;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Gather the source/target facts the strategy resolver needs, then resolve it.
|
|
69
|
+
*
|
|
70
|
+
* Shared by `copyDocument` and `canCopyDocument` so the answer a caller gets
|
|
71
|
+
* from the dry run is the one the real run will act on.
|
|
72
|
+
*/
|
|
73
|
+
export declare function prepareCopy(source: CopyEngineHost, target: CopyEngineHost, sourceDocId: string, options: CopyDocumentOptions, precomputedTargetDocId?: string): Promise<{
|
|
74
|
+
context: CopyContext;
|
|
75
|
+
feasibility: CopyFeasibility;
|
|
76
|
+
sourceEntries: StoreEntryMetadata[];
|
|
77
|
+
}>;
|
|
78
|
+
/** Dry run: report what a copy would do without writing anything. */
|
|
79
|
+
export declare function canCopyDocument(source: CopyEngineHost, target: CopyEngineHost, docId: string, options?: CopyDocumentOptions): Promise<CopyFeasibility>;
|
|
80
|
+
/**
|
|
81
|
+
* Copy one document into another database.
|
|
82
|
+
*
|
|
83
|
+
* @see `docs/document-copy.md` for the strategy matrix and caveats.
|
|
84
|
+
*/
|
|
85
|
+
export declare function copyDocument(source: CopyEngineHost, target: CopyEngineHost, docId: string, options?: CopyDocumentOptions, sharedReporter?: CopyProgressReporter): Promise<CopyDocumentResult>;
|
|
86
|
+
//# sourceMappingURL=copyDocument.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copyDocument.d.ts","sourceRoot":"","sources":["../../../src/core/copy/copyDocument.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAc,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC/E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,eAAe,CAAC;AAUvB,OAAO,KAAK,EACV,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAKjB;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,cAAc,EAAE,cAAc,CAAC;IAC/B,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,mBAAmB,GAC3B,cAAc,GAAG,SAAS,CAM5B;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,MAAM,CAAC,CAKjB;AAED;;;;;GAKG;AACH,qBAAa,oBAAoB;IAUnB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;IATxC,aAAa,SAAK;IAClB,WAAW,SAAK;IAChB,cAAc,SAAK;IACnB,iBAAiB,SAAK;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;gBAEK,UAAU,CAAC,GAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,aAAA;IAE1E,IAAI,CAAC,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;CActD;AAkCD;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,mBAAmB,EAC5B,sBAAsB,CAAC,EAAE,MAAM,GAC9B,OAAO,CAAC;IACT,OAAO,EAAE,WAAW,CAAC;IACrB,WAAW,EAAE,eAAe,CAAC;IAC7B,aAAa,EAAE,kBAAkB,EAAE,CAAC;CACrC,CAAC,CA8CD;AAED,qEAAqE;AACrE,wBAAsB,eAAe,CACnC,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,eAAe,CAAC,CAG1B;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,mBAAwB,EACjC,cAAc,CAAC,EAAE,oBAAoB,GACpC,OAAO,CAAC,kBAAkB,CAAC,CA2B7B"}
|