mindoodb 0.0.45 → 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.
Files changed (88) hide show
  1. package/README.md +9 -0
  2. package/dist/appendonlystores/network/HttpTransport.d.ts.map +1 -1
  3. package/dist/appendonlystores/network/HttpTransport.js +4 -0
  4. package/dist/appendonlystores/network/HttpTransport.js.map +1 -1
  5. package/dist/browser/index.mjs +31 -0
  6. package/dist/core/BaseMindooDB.d.ts +74 -0
  7. package/dist/core/BaseMindooDB.d.ts.map +1 -1
  8. package/dist/core/BaseMindooDB.js +160 -0
  9. package/dist/core/BaseMindooDB.js.map +1 -1
  10. package/dist/core/BaseMindooTenant.d.ts +22 -0
  11. package/dist/core/BaseMindooTenant.d.ts.map +1 -1
  12. package/dist/core/BaseMindooTenant.js +38 -0
  13. package/dist/core/BaseMindooTenant.js.map +1 -1
  14. package/dist/core/copy/attachments.d.ts +53 -0
  15. package/dist/core/copy/attachments.d.ts.map +1 -0
  16. package/dist/core/copy/attachments.js +186 -0
  17. package/dist/core/copy/attachments.js.map +1 -0
  18. package/dist/core/copy/copyDocument.d.ts +86 -0
  19. package/dist/core/copy/copyDocument.d.ts.map +1 -0
  20. package/dist/core/copy/copyDocument.js +369 -0
  21. package/dist/core/copy/copyDocument.js.map +1 -0
  22. package/dist/core/copy/copyDocuments.d.ts +57 -0
  23. package/dist/core/copy/copyDocuments.d.ts.map +1 -0
  24. package/dist/core/copy/copyDocuments.js +167 -0
  25. package/dist/core/copy/copyDocuments.js.map +1 -0
  26. package/dist/core/copy/entryRewrite.d.ts +49 -0
  27. package/dist/core/copy/entryRewrite.d.ts.map +1 -0
  28. package/dist/core/copy/entryRewrite.js +74 -0
  29. package/dist/core/copy/entryRewrite.js.map +1 -0
  30. package/dist/core/copy/feasibility.d.ts +73 -0
  31. package/dist/core/copy/feasibility.d.ts.map +1 -0
  32. package/dist/core/copy/feasibility.js +169 -0
  33. package/dist/core/copy/feasibility.js.map +1 -0
  34. package/dist/core/copy/flatten.d.ts +55 -0
  35. package/dist/core/copy/flatten.d.ts.map +1 -0
  36. package/dist/core/copy/flatten.js +155 -0
  37. package/dist/core/copy/flatten.js.map +1 -0
  38. package/dist/core/copy/host.d.ts +92 -0
  39. package/dist/core/copy/host.d.ts.map +1 -0
  40. package/dist/core/copy/host.js +16 -0
  41. package/dist/core/copy/host.js.map +1 -0
  42. package/dist/core/copy/preflight.d.ts +62 -0
  43. package/dist/core/copy/preflight.d.ts.map +1 -0
  44. package/dist/core/copy/preflight.js +98 -0
  45. package/dist/core/copy/preflight.js.map +1 -0
  46. package/dist/core/copy/provenance.d.ts +74 -0
  47. package/dist/core/copy/provenance.d.ts.map +1 -0
  48. package/dist/core/copy/provenance.js +91 -0
  49. package/dist/core/copy/provenance.js.map +1 -0
  50. package/dist/core/copy/types.d.ts +418 -0
  51. package/dist/core/copy/types.d.ts.map +1 -0
  52. package/dist/core/copy/types.js +21 -0
  53. package/dist/core/copy/types.js.map +1 -0
  54. package/dist/core/crypto/DomainStatement.d.ts +44 -0
  55. package/dist/core/crypto/DomainStatement.d.ts.map +1 -0
  56. package/dist/core/crypto/DomainStatement.js +71 -0
  57. package/dist/core/crypto/DomainStatement.js.map +1 -0
  58. package/dist/core/crypto/EntrySignature.d.ts +109 -15
  59. package/dist/core/crypto/EntrySignature.d.ts.map +1 -1
  60. package/dist/core/crypto/EntrySignature.js +83 -22
  61. package/dist/core/crypto/EntrySignature.js.map +1 -1
  62. package/dist/core/index.d.ts +21 -0
  63. package/dist/core/index.d.ts.map +1 -1
  64. package/dist/core/index.js +54 -3
  65. package/dist/core/index.js.map +1 -1
  66. package/dist/core/timestamps/timestampClient.d.ts +88 -0
  67. package/dist/core/timestamps/timestampClient.d.ts.map +1 -0
  68. package/dist/core/timestamps/timestampClient.js +157 -0
  69. package/dist/core/timestamps/timestampClient.js.map +1 -0
  70. package/dist/core/types.d.ts +90 -0
  71. package/dist/core/types.d.ts.map +1 -1
  72. package/dist/core/types.js.map +1 -1
  73. package/dist/node/server/MindooDBServer.d.ts +37 -0
  74. package/dist/node/server/MindooDBServer.d.ts.map +1 -1
  75. package/dist/node/server/MindooDBServer.js +157 -0
  76. package/dist/node/server/MindooDBServer.js.map +1 -1
  77. package/dist/node/server/config.d.ts.map +1 -1
  78. package/dist/node/server/config.js +16 -0
  79. package/dist/node/server/config.js.map +1 -1
  80. package/dist/node/server/timestampProxy.d.ts +129 -0
  81. package/dist/node/server/timestampProxy.d.ts.map +1 -0
  82. package/dist/node/server/timestampProxy.js +361 -0
  83. package/dist/node/server/timestampProxy.js.map +1 -0
  84. package/dist/node/server/types.d.ts +20 -0
  85. package/dist/node/server/types.d.ts.map +1 -1
  86. package/dist/node/server/types.js +12 -0
  87. package/dist/node/server/types.js.map +1 -1
  88. package/package.json +1 -1
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ /**
3
+ * Bulk document copy — and, with `mode: "history"` plus `targetDocId: "same"`
4
+ * plus `authorship: "preserve"`, the database sharding primitive.
5
+ *
6
+ * The per-document API is the wrong shape for moving thousands of documents:
7
+ * each call would scan the source store's metadata again. This path selects the
8
+ * documents in one pass and then reuses the per-document engine, sharing one
9
+ * progress reporter so the totals accumulate across the whole run.
10
+ *
11
+ * Errors are per-document rather than fatal: the run continues and reports what
12
+ * failed, mirroring how `pushChangesTo` reports `rejectedEntries`. Because every
13
+ * produced entry id is deterministic, re-running after a failure or a
14
+ * cancellation transfers only what is still missing.
15
+ *
16
+ * @module
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.copyDocuments = copyDocuments;
20
+ exports.buildDocHistoryPurgeRequest = buildDocHistoryPurgeRequest;
21
+ const idGeneration_1 = require("../utils/idGeneration");
22
+ const feasibility_1 = require("./feasibility");
23
+ const copyDocument_1 = require("./copyDocument");
24
+ /** How many entries one metadata scan page pulls when a cursor scan is available. */
25
+ const SCAN_PAGE_SIZE = 1000;
26
+ /**
27
+ * Collect the source document ids a selector matches.
28
+ *
29
+ * Prefix selection reads store metadata only — never a payload — which is what
30
+ * keeps a shard keyless. Only the ids are retained, so the working set stays
31
+ * proportional to the document count rather than the entry count even on a
32
+ * store with millions of entries.
33
+ */
34
+ async function selectDocIds(source, selector) {
35
+ const selected = new Set(selector.docIds ?? []);
36
+ const prefixes = selector.idPrefix === undefined
37
+ ? []
38
+ : Array.isArray(selector.idPrefix)
39
+ ? selector.idPrefix
40
+ : [selector.idPrefix];
41
+ if (prefixes.length > 0) {
42
+ const matches = (docId) => prefixes.some((prefix) => (0, idGeneration_1.matchesDocIdPrefix)(docId, prefix));
43
+ const store = source.store;
44
+ if (store.scanEntriesSince) {
45
+ let cursor = null;
46
+ for (;;) {
47
+ const page = await store.scanEntriesSince(cursor, SCAN_PAGE_SIZE);
48
+ for (const entry of page.entries) {
49
+ if (matches(entry.docId))
50
+ selected.add(entry.docId);
51
+ }
52
+ if (!page.hasMore || !page.nextCursor)
53
+ break;
54
+ cursor = page.nextCursor;
55
+ }
56
+ }
57
+ else {
58
+ for (const entry of await store.findNewEntries([])) {
59
+ if (matches(entry.docId))
60
+ selected.add(entry.docId);
61
+ }
62
+ }
63
+ }
64
+ return [...selected].sort();
65
+ }
66
+ /** Copy many documents into another database in one pass. */
67
+ async function copyDocuments(source, target, selector, options = {}) {
68
+ if ((selector.docIds === undefined || selector.docIds.length === 0) &&
69
+ selector.idPrefix === undefined) {
70
+ throw new Error("copyDocumentsTo requires a selector with at least one of docIds or idPrefix.");
71
+ }
72
+ for (const [role, host] of [
73
+ ["source", source],
74
+ ["target", target],
75
+ ]) {
76
+ if (host.store.getId() === feasibility_1.DIRECTORY_DB_ID) {
77
+ throw new Error(`The tenant directory database cannot be used as a copy ${role}.`);
78
+ }
79
+ }
80
+ const reporter = new copyDocument_1.CopyProgressReporter(options.onProgress);
81
+ reporter.emit("preparing", "Selecting documents to copy");
82
+ const docIds = await selectDocIds(source, selector);
83
+ reporter.totalDocuments = docIds.length;
84
+ reporter.documentsCompleted = 0;
85
+ reporter.emit("planning", `Selected ${docIds.length} documents to copy`);
86
+ const documents = [];
87
+ const failed = [];
88
+ let cancelled = false;
89
+ for (const docId of docIds) {
90
+ if (options.signal?.aborted) {
91
+ cancelled = true;
92
+ break;
93
+ }
94
+ try {
95
+ const result = await (0, copyDocument_1.copyDocument)(source, target, docId, { ...options, targetDocId: options.targetDocId ?? "new" }, reporter);
96
+ documents.push(result);
97
+ if (result.cancelled) {
98
+ cancelled = true;
99
+ break;
100
+ }
101
+ }
102
+ catch (error) {
103
+ failed.push({
104
+ docId,
105
+ error: error instanceof Error ? error.message : String(error),
106
+ });
107
+ }
108
+ reporter.documentsCompleted = (reporter.documentsCompleted ?? 0) + 1;
109
+ }
110
+ reporter.currentDocId = undefined;
111
+ reporter.emit("complete", `Copied ${documents.length} of ${docIds.length} documents` +
112
+ (failed.length > 0 ? `, ${failed.length} failed` : ""));
113
+ return {
114
+ documents,
115
+ copiedDocIds: documents
116
+ .filter((result) => !result.cancelled)
117
+ .map((result) => result.sourceDocId),
118
+ failed,
119
+ copiedEntries: reporter.copiedEntries,
120
+ copiedBytes: reporter.copiedBytes,
121
+ copiedAttachments: reporter.copiedAttachments,
122
+ cancelled,
123
+ };
124
+ }
125
+ /**
126
+ * Turn a completed shard into the purge request that reclaims the source.
127
+ *
128
+ * No new purge machinery is involved: this only shapes the result for the
129
+ * existing pipeline. The admin publishes the returned request into the
130
+ * directory database, and the server's `executePendingPurges` denylists each
131
+ * document id before deleting it, so a stale replica cannot re-push into the
132
+ * gap.
133
+ *
134
+ * Two ordering rules matter and are not enforceable here:
135
+ *
136
+ * 1. Publish only after the shard target has been pushed **and witnessed**.
137
+ * Copied entries are provisional until they are re-witnessed, and a purge is
138
+ * irreversible.
139
+ * 2. `dbId` must name the **source** database. The denylist is keyed by
140
+ * database id, which is precisely why purging the source leaves the shard
141
+ * target — holding the same document ids under a different database id —
142
+ * untouched.
143
+ *
144
+ * For a very large shard, split {@link CopyDocumentsResult.copiedDocIds} across
145
+ * several requests: the id list lives inside a single directory document, and
146
+ * idempotency is tracked per `requestId`.
147
+ */
148
+ function buildDocHistoryPurgeRequest(result, options) {
149
+ if (result.copiedDocIds.length === 0) {
150
+ throw new Error("Refusing to build a purge request from a copy that moved no documents.");
151
+ }
152
+ if (result.failed.length > 0) {
153
+ throw new Error(`Refusing to build a purge request: ${result.failed.length} document(s) failed to copy ` +
154
+ `(${result.failed.map((failure) => failure.docId).join(", ")}). ` +
155
+ "Re-run the copy until it completes cleanly, or build the request from a filtered id list.");
156
+ }
157
+ return {
158
+ v: 1,
159
+ tenantId: options.tenantId,
160
+ requestId: options.requestId,
161
+ dbId: options.dbId,
162
+ docIds: result.copiedDocIds,
163
+ reason: options.reason,
164
+ preparedByPublicKey: options.preparedByPublicKey,
165
+ };
166
+ }
167
+ //# sourceMappingURL=copyDocuments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copyDocuments.js","sourceRoot":"","sources":["../../../src/core/copy/copyDocuments.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Pure entry-rewriting helpers shared by the graft and replay strategies.
3
+ *
4
+ * @internal
5
+ * @module
6
+ */
7
+ import type { StoreEntry, StoreEntryMetadata, StoreEntryType } from "../types";
8
+ /**
9
+ * The entry types that make up a document's revision graph. `doc_snapshot` is
10
+ * included deliberately: after a dense sync it can be the only entry a peer
11
+ * holds for a document, so skipping it would silently drop content.
12
+ */
13
+ export declare const DOCUMENT_DAG_ENTRY_TYPES: ReadonlySet<StoreEntryType>;
14
+ /** True when the entry is part of the document revision graph. */
15
+ export declare function isDocumentDagEntry(entry: StoreEntryMetadata): boolean;
16
+ /**
17
+ * Re-point an entry id from one document to another.
18
+ *
19
+ * Every entry id — document (`<docId>_d_<depsFingerprint>_<automergeHash>`) and
20
+ * attachment chunk (`<docId>_a_<fileUuid7>_<chunkUuid>`) alike — begins with the
21
+ * document id followed by `_`, so re-homing an entry is a prefix swap.
22
+ *
23
+ * This is exact rather than a shortcut, and it is what makes a replay cheap:
24
+ * the deps fingerprint is `SHA-256` over the sorted *Automerge* dependency
25
+ * hashes, which a copy never changes, and the Automerge hash is a property of
26
+ * the change bytes, which a copy reuses verbatim. So the re-derived id is
27
+ * identical to what `generateDocEntryId()` would return under the new document
28
+ * id — without decrypting or decoding anything — and it is deterministic, which
29
+ * is what lets an interrupted copy resume by simply running again.
30
+ *
31
+ * Ids that do not carry the source prefix (a dependency on a foreign entry)
32
+ * are returned unchanged.
33
+ */
34
+ export declare function remapEntryId(entryId: string, sourceDocId: string, targetDocId: string): string;
35
+ /**
36
+ * Remove the server's witness receipt from a copied entry.
37
+ *
38
+ * The receipt signature binds the database id (see `crypto/WitnessReceipt.ts`),
39
+ * so carrying it into a different database would fail verification and
40
+ * quarantine the entry as `invalid_witness_receipt`. Stripped entries are
41
+ * provisional until the target database is pushed and re-witnessed.
42
+ *
43
+ * `receiptOrder` goes too: it is a per-store sequence the receiving store
44
+ * assigns, meaningless in another one.
45
+ */
46
+ export declare function stripWitnessReceipt<T extends StoreEntryMetadata>(entry: T): T;
47
+ /** Sum the encrypted payload sizes of a batch, for progress and result totals. */
48
+ export declare function sumEncryptedSize(entries: StoreEntry[]): number;
49
+ //# sourceMappingURL=entryRewrite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entryRewrite.d.ts","sourceRoot":"","sources":["../../../src/core/copy/entryRewrite.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/E;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,WAAW,CAAC,cAAc,CAM/D,CAAC;AAEH,kEAAkE;AAClE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAErE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,MAAM,CAIR;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,kBAAkB,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAS7E;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,CAE9D"}
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ /**
3
+ * Pure entry-rewriting helpers shared by the graft and replay strategies.
4
+ *
5
+ * @internal
6
+ * @module
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.DOCUMENT_DAG_ENTRY_TYPES = void 0;
10
+ exports.isDocumentDagEntry = isDocumentDagEntry;
11
+ exports.remapEntryId = remapEntryId;
12
+ exports.stripWitnessReceipt = stripWitnessReceipt;
13
+ exports.sumEncryptedSize = sumEncryptedSize;
14
+ /**
15
+ * The entry types that make up a document's revision graph. `doc_snapshot` is
16
+ * included deliberately: after a dense sync it can be the only entry a peer
17
+ * holds for a document, so skipping it would silently drop content.
18
+ */
19
+ exports.DOCUMENT_DAG_ENTRY_TYPES = new Set([
20
+ "doc_create",
21
+ "doc_change",
22
+ "doc_delete",
23
+ "doc_undelete",
24
+ "doc_snapshot",
25
+ ]);
26
+ /** True when the entry is part of the document revision graph. */
27
+ function isDocumentDagEntry(entry) {
28
+ return exports.DOCUMENT_DAG_ENTRY_TYPES.has(entry.entryType);
29
+ }
30
+ /**
31
+ * Re-point an entry id from one document to another.
32
+ *
33
+ * Every entry id — document (`<docId>_d_<depsFingerprint>_<automergeHash>`) and
34
+ * attachment chunk (`<docId>_a_<fileUuid7>_<chunkUuid>`) alike — begins with the
35
+ * document id followed by `_`, so re-homing an entry is a prefix swap.
36
+ *
37
+ * This is exact rather than a shortcut, and it is what makes a replay cheap:
38
+ * the deps fingerprint is `SHA-256` over the sorted *Automerge* dependency
39
+ * hashes, which a copy never changes, and the Automerge hash is a property of
40
+ * the change bytes, which a copy reuses verbatim. So the re-derived id is
41
+ * identical to what `generateDocEntryId()` would return under the new document
42
+ * id — without decrypting or decoding anything — and it is deterministic, which
43
+ * is what lets an interrupted copy resume by simply running again.
44
+ *
45
+ * Ids that do not carry the source prefix (a dependency on a foreign entry)
46
+ * are returned unchanged.
47
+ */
48
+ function remapEntryId(entryId, sourceDocId, targetDocId) {
49
+ if (sourceDocId === targetDocId)
50
+ return entryId;
51
+ if (!entryId.startsWith(`${sourceDocId}_`))
52
+ return entryId;
53
+ return `${targetDocId}${entryId.slice(sourceDocId.length)}`;
54
+ }
55
+ /**
56
+ * Remove the server's witness receipt from a copied entry.
57
+ *
58
+ * The receipt signature binds the database id (see `crypto/WitnessReceipt.ts`),
59
+ * so carrying it into a different database would fail verification and
60
+ * quarantine the entry as `invalid_witness_receipt`. Stripped entries are
61
+ * provisional until the target database is pushed and re-witnessed.
62
+ *
63
+ * `receiptOrder` goes too: it is a per-store sequence the receiving store
64
+ * assigns, meaningless in another one.
65
+ */
66
+ function stripWitnessReceipt(entry) {
67
+ const { receivedAt: _receivedAt, receivedByPublicKey: _receivedByPublicKey, receivedDateSignature: _receivedDateSignature, receiptOrder: _receiptOrder, ...rest } = entry;
68
+ return rest;
69
+ }
70
+ /** Sum the encrypted payload sizes of a batch, for progress and result totals. */
71
+ function sumEncryptedSize(entries) {
72
+ return entries.reduce((total, entry) => total + entry.encryptedSize, 0);
73
+ }
74
+ //# sourceMappingURL=entryRewrite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entryRewrite.js","sourceRoot":"","sources":["../../../src/core/copy/entryRewrite.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAkBH,gDAEC;AAoBD,oCAQC;AAaD,kDASC;AAGD,4CAEC;AAvED;;;;GAIG;AACU,QAAA,wBAAwB,GAAgC,IAAI,GAAG,CAAiB;IAC3F,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,cAAc;IACd,cAAc;CACf,CAAC,CAAC;AAEH,kEAAkE;AAClE,SAAgB,kBAAkB,CAAC,KAAyB;IAC1D,OAAO,gCAAwB,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;AACvD,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAgB,YAAY,CAC1B,OAAe,EACf,WAAmB,EACnB,WAAmB;IAEnB,IAAI,WAAW,KAAK,WAAW;QAAE,OAAO,OAAO,CAAC;IAChD,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,WAAW,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC;IAC3D,OAAO,GAAG,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;AAC9D,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,mBAAmB,CAA+B,KAAQ;IACxE,MAAM,EACJ,UAAU,EAAE,WAAW,EACvB,mBAAmB,EAAE,oBAAoB,EACzC,qBAAqB,EAAE,sBAAsB,EAC7C,YAAY,EAAE,aAAa,EAC3B,GAAG,IAAI,EACR,GAAG,KAAK,CAAC;IACV,OAAO,IAAS,CAAC;AACnB,CAAC;AAED,kFAAkF;AAClF,SAAgB,gBAAgB,CAAC,OAAqB;IACpD,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;AAC1E,CAAC"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Pure strategy resolution for document copies.
3
+ *
4
+ * Everything here is a side-effect-free function of a {@link CopyContext} plus
5
+ * the caller's {@link CopyDocumentOptions}, which keeps the "can I do this, and
6
+ * what would it cost me" question answerable without touching a store, and
7
+ * keeps the rules unit-testable in isolation from the copy engine.
8
+ *
9
+ * @module
10
+ */
11
+ import type { CopyDocumentOptions, CopyFeasibility } from "./types";
12
+ /** The tenant directory database id. Never a valid copy source or target. */
13
+ export declare const DIRECTORY_DB_ID = "directory";
14
+ /**
15
+ * Everything the resolver needs to know about the source and target, gathered
16
+ * by the copy engine before any work starts.
17
+ *
18
+ * @internal
19
+ */
20
+ export interface CopyContext {
21
+ /** Tenant owning the source database. */
22
+ sourceTenantId: string;
23
+ /** Tenant owning the target database. */
24
+ targetTenantId: string;
25
+ /** Source database (store) id. */
26
+ sourceDbId: string;
27
+ /** Target database (store) id. */
28
+ targetDbId: string;
29
+ /** The document id being read from the source. */
30
+ sourceDocId: string;
31
+ /** The document id that will be written in the target, already resolved. */
32
+ targetDocId: string;
33
+ /** The `decryptionKeyId` the source document's entries currently use. */
34
+ sourceDecryptionKeyId: string;
35
+ /** The `decryptionKeyId` the copy will be written under, already resolved. */
36
+ targetDecryptionKeyId: string;
37
+ /** True when source and target document entries live in the same store. */
38
+ sameStore: boolean;
39
+ /** True when the target store already holds an entry for {@link targetDocId}. */
40
+ targetHasDocId: boolean;
41
+ }
42
+ /**
43
+ * Resolve the effective target `decryptionKeyId` for a copy.
44
+ *
45
+ * Within one tenant the source document's own key is kept, so a copy stays
46
+ * readable by exactly the same people and — crucially — stays eligible for
47
+ * authorship preservation. Across tenants the source key id is meaningless
48
+ * (key ids resolve tenant-scoped), so the copy falls back to the target
49
+ * tenant's `"default"` key.
50
+ */
51
+ export declare function resolveTargetDecryptionKeyId(requested: string | undefined, sourceDecryptionKeyId: string, sameTenant: boolean): string;
52
+ /**
53
+ * Decide what a copy would do, and whether it can run at all.
54
+ *
55
+ * The rules, in order:
56
+ *
57
+ * 1. Neither side may be the tenant directory database.
58
+ * 2. `flatten` mode always resolves to the flatten strategy and never preserves
59
+ * authorship (there is no original entry left to preserve).
60
+ * 3. `history` mode preserves authorship only in the exact configuration where
61
+ * the original `metadataSignature` remains valid: same tenant, same document
62
+ * id, same key, different database. Anything else re-authors.
63
+ * 4. Asking for `authorship: "preserve"` outside that configuration is refused
64
+ * rather than silently downgraded, because a caller who asked for it is
65
+ * relying on the signatures being real.
66
+ */
67
+ export declare function resolveCopyFeasibility(context: CopyContext, options?: CopyDocumentOptions): CopyFeasibility;
68
+ /**
69
+ * Turn an unusable {@link CopyFeasibility} into the error thrown by
70
+ * `copyDocumentTo` / `copyDocumentsTo`.
71
+ */
72
+ export declare function copyFeasibilityError(feasibility: CopyFeasibility): Error;
73
+ //# sourceMappingURL=feasibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feasibility.d.ts","sourceRoot":"","sources":["../../../src/core/copy/feasibility.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EACV,mBAAmB,EACnB,eAAe,EAIhB,MAAM,SAAS,CAAC;AAEjB,6EAA6E;AAC7E,eAAO,MAAM,eAAe,cAAc,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,yCAAyC;IACzC,cAAc,EAAE,MAAM,CAAC;IACvB,yCAAyC;IACzC,cAAc,EAAE,MAAM,CAAC;IACvB,kCAAkC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,WAAW,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,qBAAqB,EAAE,MAAM,CAAC;IAC9B,8EAA8E;IAC9E,qBAAqB,EAAE,MAAM,CAAC;IAC9B,2EAA2E;IAC3E,SAAS,EAAE,OAAO,CAAC;IACnB,iFAAiF;IACjF,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAC1C,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,qBAAqB,EAAE,MAAM,EAC7B,UAAU,EAAE,OAAO,GAClB,MAAM,CAGR;AAUD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,WAAW,EACpB,OAAO,GAAE,mBAAwB,GAChC,eAAe,CAgJjB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,eAAe,GAAG,KAAK,CAKxE"}
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ /**
3
+ * Pure strategy resolution for document copies.
4
+ *
5
+ * Everything here is a side-effect-free function of a {@link CopyContext} plus
6
+ * the caller's {@link CopyDocumentOptions}, which keeps the "can I do this, and
7
+ * what would it cost me" question answerable without touching a store, and
8
+ * keeps the rules unit-testable in isolation from the copy engine.
9
+ *
10
+ * @module
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.DIRECTORY_DB_ID = void 0;
14
+ exports.resolveTargetDecryptionKeyId = resolveTargetDecryptionKeyId;
15
+ exports.resolveCopyFeasibility = resolveCopyFeasibility;
16
+ exports.copyFeasibilityError = copyFeasibilityError;
17
+ /** The tenant directory database id. Never a valid copy source or target. */
18
+ exports.DIRECTORY_DB_ID = "directory";
19
+ /**
20
+ * Resolve the effective target `decryptionKeyId` for a copy.
21
+ *
22
+ * Within one tenant the source document's own key is kept, so a copy stays
23
+ * readable by exactly the same people and — crucially — stays eligible for
24
+ * authorship preservation. Across tenants the source key id is meaningless
25
+ * (key ids resolve tenant-scoped), so the copy falls back to the target
26
+ * tenant's `"default"` key.
27
+ */
28
+ function resolveTargetDecryptionKeyId(requested, sourceDecryptionKeyId, sameTenant) {
29
+ if (requested)
30
+ return requested;
31
+ return sameTenant ? sourceDecryptionKeyId : "default";
32
+ }
33
+ /** Build a reason record with a stable code and a human-readable message. */
34
+ function reason(code, message) {
35
+ return { code, message };
36
+ }
37
+ /**
38
+ * Decide what a copy would do, and whether it can run at all.
39
+ *
40
+ * The rules, in order:
41
+ *
42
+ * 1. Neither side may be the tenant directory database.
43
+ * 2. `flatten` mode always resolves to the flatten strategy and never preserves
44
+ * authorship (there is no original entry left to preserve).
45
+ * 3. `history` mode preserves authorship only in the exact configuration where
46
+ * the original `metadataSignature` remains valid: same tenant, same document
47
+ * id, same key, different database. Anything else re-authors.
48
+ * 4. Asking for `authorship: "preserve"` outside that configuration is refused
49
+ * rather than silently downgraded, because a caller who asked for it is
50
+ * relying on the signatures being real.
51
+ */
52
+ function resolveCopyFeasibility(context, options = {}) {
53
+ const mode = options.mode ?? "flatten";
54
+ const authorship = options.authorship ?? "reauthor";
55
+ const wantsPreserve = authorship === "preserve";
56
+ const sameTenant = context.sourceTenantId === context.targetTenantId;
57
+ const sameDocId = context.sourceDocId === context.targetDocId;
58
+ const sameKey = context.sourceDecryptionKeyId === context.targetDecryptionKeyId;
59
+ // Key ids resolve per tenant, so an identical id across tenants still names a
60
+ // different key and still forces re-encryption.
61
+ const requiresReEncryption = !sameTenant || !sameKey;
62
+ const reasons = [];
63
+ const isDirectory = (dbId) => dbId === exports.DIRECTORY_DB_ID;
64
+ if (isDirectory(context.sourceDbId) || isDirectory(context.targetDbId)) {
65
+ reasons.push(reason("directory_database", "The tenant directory database cannot be used as a copy source or target."));
66
+ return {
67
+ allowed: false,
68
+ strategy: mode === "flatten" ? "flatten" : "replay",
69
+ authorshipPreserved: false,
70
+ willMergeIntoExisting: false,
71
+ sameStore: context.sameStore,
72
+ requiresReEncryption,
73
+ reasons,
74
+ };
75
+ }
76
+ if (context.sameStore && sameDocId) {
77
+ reasons.push(reason("same_database_same_doc_id", `Source and target are the same database (${context.sourceDbId}) and the ` +
78
+ `target document id is unchanged (${context.targetDocId}), so there is ` +
79
+ "nothing to copy. Use targetDocId 'new' to duplicate in place."));
80
+ return {
81
+ allowed: false,
82
+ strategy: mode === "flatten" ? "flatten" : "replay",
83
+ authorshipPreserved: false,
84
+ willMergeIntoExisting: true,
85
+ sameStore: true,
86
+ requiresReEncryption,
87
+ reasons,
88
+ };
89
+ }
90
+ if (mode === "flatten") {
91
+ if (wantsPreserve) {
92
+ reasons.push(reason("flatten_mode", "Flatten mode writes a single new change, so there is no original " +
93
+ "entry whose authorship could be preserved. Use mode 'history' with " +
94
+ "authorship 'preserve'."));
95
+ return {
96
+ allowed: false,
97
+ strategy: "flatten",
98
+ authorshipPreserved: false,
99
+ willMergeIntoExisting: context.targetHasDocId,
100
+ sameStore: context.sameStore,
101
+ requiresReEncryption,
102
+ reasons,
103
+ };
104
+ }
105
+ return {
106
+ allowed: true,
107
+ strategy: "flatten",
108
+ authorshipPreserved: false,
109
+ willMergeIntoExisting: context.targetHasDocId,
110
+ sameStore: context.sameStore,
111
+ requiresReEncryption,
112
+ reasons,
113
+ };
114
+ }
115
+ // mode === "history": collect every condition that blocks preservation.
116
+ if (!sameTenant) {
117
+ reasons.push(reason("different_tenant", `Source tenant '${context.sourceTenantId}' differs from target tenant ` +
118
+ `'${context.targetTenantId}'. The payload must be re-encrypted under the ` +
119
+ "target tenant key, and the source authors' keys are not in the target " +
120
+ "tenant's directory, so their signatures could not be verified there."));
121
+ }
122
+ if (!sameDocId) {
123
+ reasons.push(reason("different_doc_id", `The copy lands under document id '${context.targetDocId}' instead of ` +
124
+ `'${context.sourceDocId}'. Entry ids embed the document id and the ` +
125
+ "author's metadataSignature binds both, so the original signatures " +
126
+ "cannot carry over. Use targetDocId 'same' to preserve authorship."));
127
+ }
128
+ if (!sameKey) {
129
+ reasons.push(reason("different_key", `The copy is encrypted under key '${context.targetDecryptionKeyId}' instead ` +
130
+ `of '${context.sourceDecryptionKeyId}'. Re-encryption changes contentHash, ` +
131
+ "which the author's metadataSignature binds."));
132
+ }
133
+ const canPreserve = reasons.length === 0;
134
+ if (wantsPreserve && !canPreserve) {
135
+ return {
136
+ allowed: false,
137
+ strategy: "replay",
138
+ authorshipPreserved: false,
139
+ willMergeIntoExisting: context.targetHasDocId,
140
+ sameStore: context.sameStore,
141
+ requiresReEncryption,
142
+ reasons,
143
+ };
144
+ }
145
+ const strategy = wantsPreserve ? "graft" : "replay";
146
+ return {
147
+ allowed: true,
148
+ strategy,
149
+ authorshipPreserved: strategy === "graft",
150
+ willMergeIntoExisting: context.targetHasDocId,
151
+ sameStore: context.sameStore,
152
+ requiresReEncryption,
153
+ // A successful graft has nothing to explain. A replay lists what would have
154
+ // had to be true for authorship to survive, so callers can tell an
155
+ // unavoidable re-author from an accidental one.
156
+ reasons: strategy === "graft" ? [] : reasons,
157
+ };
158
+ }
159
+ /**
160
+ * Turn an unusable {@link CopyFeasibility} into the error thrown by
161
+ * `copyDocumentTo` / `copyDocumentsTo`.
162
+ */
163
+ function copyFeasibilityError(feasibility) {
164
+ const detail = feasibility.reasons
165
+ .map((entry) => `[${entry.code}] ${entry.message}`)
166
+ .join(" ");
167
+ return new Error(`Document copy is not possible with the requested options. ${detail}`);
168
+ }
169
+ //# sourceMappingURL=feasibility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feasibility.js","sourceRoot":"","sources":["../../../src/core/copy/feasibility.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The flatten copy strategy.
3
+ *
4
+ * Reads the source document's state — at its head, or as of
5
+ * {@link CopyDocumentOptions.atTimestamp} — and writes it into the target as a
6
+ * brand-new document with a single revision. History, original authors and the
7
+ * CRDT structure are all deliberately left behind; what arrives is a clean
8
+ * document that happens to have the same content.
9
+ *
10
+ * Unlike the two history strategies this one goes entirely through the public
11
+ * document API, which is what makes it work regardless of tenant, key or
12
+ * document id: `createDocument` + one `changeDoc`, with attachments re-uploaded
13
+ * through `addAttachmentStream` so they get fresh chunk ids in the target.
14
+ *
15
+ * **Caveat worth knowing:** materializing an Automerge document to plain JS
16
+ * collapses its text fields into ordinary strings. A flattened copy therefore
17
+ * loses the character-level text CRDT structure, and concurrent edits to those
18
+ * fields merge as whole-value conflicts rather than per-character. That is
19
+ * usually the right trade for a fresh document, but it is the reason
20
+ * `mode: "history"` exists.
21
+ *
22
+ * @module
23
+ */
24
+ import type { CopyEngineHost } from "./host";
25
+ import type { CopyContext } from "./feasibility";
26
+ import type { CopyProgressReporter } from "./copyDocument";
27
+ import type { CopyDocumentOptions, CopyDocumentResult } from "./types";
28
+ /**
29
+ * Document-level provenance for a flattened copy.
30
+ *
31
+ * Per-change provenance would be meaningless here — the copy has exactly one
32
+ * change, authored by the copying user, that corresponds to no single source
33
+ * revision — so the origin is recorded once in the payload instead, which also
34
+ * keeps flatten free of any store-format concern.
35
+ */
36
+ export interface FlattenedDocumentProvenance {
37
+ /** When the copy ran (epoch millis). */
38
+ copiedAt: number;
39
+ /** Tenant the content came from. */
40
+ sourceTenantId: string;
41
+ /** Database the content came from. */
42
+ sourceDbId: string;
43
+ /** Document the content came from. */
44
+ sourceDocId: string;
45
+ /**
46
+ * The point in the source's history that was captured, when the caller asked
47
+ * for a specific one via {@link CopyDocumentOptions.atTimestamp}.
48
+ */
49
+ sourceAtTimestamp?: number;
50
+ /** Signing key of the user who made the copy. */
51
+ copiedByPublicKey: string;
52
+ }
53
+ /** Flatten one document into the target database. */
54
+ export declare function flattenDocument(source: CopyEngineHost, target: CopyEngineHost, context: CopyContext, options: CopyDocumentOptions, reporter: CopyProgressReporter): Promise<CopyDocumentResult>;
55
+ //# sourceMappingURL=flatten.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flatten.d.ts","sourceRoot":"","sources":["../../../src/core/copy/flatten.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAEvE;;;;;;;GAOG;AACH,MAAM,WAAW,2BAA2B;IAC1C,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iDAAiD;IACjD,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAqBD,qDAAqD;AACrD,wBAAsB,eAAe,CACnC,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,oBAAoB,GAC7B,OAAO,CAAC,kBAAkB,CAAC,CAyI7B"}