mindoodb 0.0.45 → 0.0.47

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 (92) 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 +75 -0
  7. package/dist/core/BaseMindooDB.d.ts.map +1 -1
  8. package/dist/core/BaseMindooDB.js +204 -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/DocumentDagAnalysis.d.ts +32 -0
  15. package/dist/core/DocumentDagAnalysis.d.ts.map +1 -1
  16. package/dist/core/DocumentDagAnalysis.js +46 -6
  17. package/dist/core/DocumentDagAnalysis.js.map +1 -1
  18. package/dist/core/copy/attachments.d.ts +53 -0
  19. package/dist/core/copy/attachments.d.ts.map +1 -0
  20. package/dist/core/copy/attachments.js +186 -0
  21. package/dist/core/copy/attachments.js.map +1 -0
  22. package/dist/core/copy/copyDocument.d.ts +86 -0
  23. package/dist/core/copy/copyDocument.d.ts.map +1 -0
  24. package/dist/core/copy/copyDocument.js +369 -0
  25. package/dist/core/copy/copyDocument.js.map +1 -0
  26. package/dist/core/copy/copyDocuments.d.ts +57 -0
  27. package/dist/core/copy/copyDocuments.d.ts.map +1 -0
  28. package/dist/core/copy/copyDocuments.js +167 -0
  29. package/dist/core/copy/copyDocuments.js.map +1 -0
  30. package/dist/core/copy/entryRewrite.d.ts +49 -0
  31. package/dist/core/copy/entryRewrite.d.ts.map +1 -0
  32. package/dist/core/copy/entryRewrite.js +74 -0
  33. package/dist/core/copy/entryRewrite.js.map +1 -0
  34. package/dist/core/copy/feasibility.d.ts +73 -0
  35. package/dist/core/copy/feasibility.d.ts.map +1 -0
  36. package/dist/core/copy/feasibility.js +169 -0
  37. package/dist/core/copy/feasibility.js.map +1 -0
  38. package/dist/core/copy/flatten.d.ts +55 -0
  39. package/dist/core/copy/flatten.d.ts.map +1 -0
  40. package/dist/core/copy/flatten.js +155 -0
  41. package/dist/core/copy/flatten.js.map +1 -0
  42. package/dist/core/copy/host.d.ts +92 -0
  43. package/dist/core/copy/host.d.ts.map +1 -0
  44. package/dist/core/copy/host.js +16 -0
  45. package/dist/core/copy/host.js.map +1 -0
  46. package/dist/core/copy/preflight.d.ts +62 -0
  47. package/dist/core/copy/preflight.d.ts.map +1 -0
  48. package/dist/core/copy/preflight.js +98 -0
  49. package/dist/core/copy/preflight.js.map +1 -0
  50. package/dist/core/copy/provenance.d.ts +74 -0
  51. package/dist/core/copy/provenance.d.ts.map +1 -0
  52. package/dist/core/copy/provenance.js +91 -0
  53. package/dist/core/copy/provenance.js.map +1 -0
  54. package/dist/core/copy/types.d.ts +418 -0
  55. package/dist/core/copy/types.d.ts.map +1 -0
  56. package/dist/core/copy/types.js +21 -0
  57. package/dist/core/copy/types.js.map +1 -0
  58. package/dist/core/crypto/DomainStatement.d.ts +44 -0
  59. package/dist/core/crypto/DomainStatement.d.ts.map +1 -0
  60. package/dist/core/crypto/DomainStatement.js +71 -0
  61. package/dist/core/crypto/DomainStatement.js.map +1 -0
  62. package/dist/core/crypto/EntrySignature.d.ts +109 -15
  63. package/dist/core/crypto/EntrySignature.d.ts.map +1 -1
  64. package/dist/core/crypto/EntrySignature.js +83 -22
  65. package/dist/core/crypto/EntrySignature.js.map +1 -1
  66. package/dist/core/index.d.ts +21 -0
  67. package/dist/core/index.d.ts.map +1 -1
  68. package/dist/core/index.js +54 -3
  69. package/dist/core/index.js.map +1 -1
  70. package/dist/core/timestamps/timestampClient.d.ts +88 -0
  71. package/dist/core/timestamps/timestampClient.d.ts.map +1 -0
  72. package/dist/core/timestamps/timestampClient.js +157 -0
  73. package/dist/core/timestamps/timestampClient.js.map +1 -0
  74. package/dist/core/types.d.ts +118 -1
  75. package/dist/core/types.d.ts.map +1 -1
  76. package/dist/core/types.js.map +1 -1
  77. package/dist/node/server/MindooDBServer.d.ts +37 -0
  78. package/dist/node/server/MindooDBServer.d.ts.map +1 -1
  79. package/dist/node/server/MindooDBServer.js +157 -0
  80. package/dist/node/server/MindooDBServer.js.map +1 -1
  81. package/dist/node/server/config.d.ts.map +1 -1
  82. package/dist/node/server/config.js +16 -0
  83. package/dist/node/server/config.js.map +1 -1
  84. package/dist/node/server/timestampProxy.d.ts +129 -0
  85. package/dist/node/server/timestampProxy.d.ts.map +1 -0
  86. package/dist/node/server/timestampProxy.js +361 -0
  87. package/dist/node/server/timestampProxy.js.map +1 -0
  88. package/dist/node/server/types.d.ts +20 -0
  89. package/dist/node/server/types.d.ts.map +1 -1
  90. package/dist/node/server/types.js +12 -0
  91. package/dist/node/server/types.js.map +1 -1
  92. package/package.json +1 -1
@@ -18,6 +18,8 @@ const idGeneration_1 = require("./utils/idGeneration");
18
18
  const timeSource_1 = require("./utils/timeSource");
19
19
  const errors_1 = require("./errors");
20
20
  const EntrySignature_1 = require("./crypto/EntrySignature");
21
+ const copyDocument_1 = require("./copy/copyDocument");
22
+ const copyDocuments_1 = require("./copy/copyDocuments");
21
23
  const materializationGuard_1 = require("./accesscontrol/materializationGuard");
22
24
  const Ed25519WitnessProvider_1 = require("./accesscontrol/timestamp/Ed25519WitnessProvider");
23
25
  const AccessDeniedError_1 = require("./accesscontrol/AccessDeniedError");
@@ -4278,6 +4280,7 @@ class BaseMindooDB {
4278
4280
  doc: wrappedDoc,
4279
4281
  changeCreatedAt: entryMetadata.createdAt,
4280
4282
  changeCreatedByPublicKey: entryMetadata.createdByPublicKey,
4283
+ changeDependencyIds: [...entryMetadata.dependencyIds],
4281
4284
  };
4282
4285
  }
4283
4286
  }
@@ -4881,6 +4884,49 @@ class BaseMindooDB {
4881
4884
  doc: this.wrapDocument(internalDoc),
4882
4885
  };
4883
4886
  }
4887
+ async materializeDocumentAtHeads(docId, headEntryIds) {
4888
+ const startedAt = Date.now();
4889
+ const allEntryMetadata = await this.scanAllMetadata(this.store, { docId });
4890
+ const plan = (0, DocumentDagAnalysis_1.computeHeadsMaterializationPlan)(docId, allEntryMetadata, headEntryIds);
4891
+ if (!plan) {
4892
+ this.performanceCallback?.onHistoryOperation?.({
4893
+ operation: "materializeDocumentAtHeads",
4894
+ docId,
4895
+ time: Date.now() - startedAt,
4896
+ scannedEntries: allEntryMetadata.length,
4897
+ returnedEntries: 0,
4898
+ bounded: true,
4899
+ });
4900
+ return null;
4901
+ }
4902
+ const metadataById = new Map(allEntryMetadata.map((entry) => [entry.id, entry]));
4903
+ const branchEntries = plan.branchEntryIds
4904
+ .map((entryId) => metadataById.get(entryId))
4905
+ .filter((entry) => entry !== undefined);
4906
+ const internalDoc = await this.materializeDocumentFromPlan(docId, allEntryMetadata, branchEntries, plan.snapshotEntryId, plan.entryIdsToApply, plan.headCreatedAt);
4907
+ this.performanceCallback?.onHistoryOperation?.({
4908
+ operation: "materializeDocumentAtHeads",
4909
+ docId,
4910
+ time: Date.now() - startedAt,
4911
+ scannedEntries: allEntryMetadata.length,
4912
+ returnedEntries: internalDoc ? 1 : 0,
4913
+ bounded: true,
4914
+ });
4915
+ if (!internalDoc) {
4916
+ return null;
4917
+ }
4918
+ return {
4919
+ docId,
4920
+ headEntryId: plan.headEntryIds[plan.headEntryIds.length - 1],
4921
+ headEntryIds: [...plan.headEntryIds],
4922
+ headCreatedAt: plan.headCreatedAt,
4923
+ headCreatedByPublicKey: plan.headCreatedByPublicKey,
4924
+ snapshotEntryId: plan.snapshotEntryId,
4925
+ entryIdsApplied: [...plan.entryIdsToApply],
4926
+ branchEntryIds: [...plan.branchEntryIds],
4927
+ doc: this.wrapDocument(internalDoc),
4928
+ };
4929
+ }
4884
4930
  async materializeDocumentBranchAtTimestamp(docId, timestamp, headEntryId) {
4885
4931
  const startedAt = Date.now();
4886
4932
  const resolvedTimestamp = this.resolveDagTimestamp(timestamp);
@@ -10545,6 +10591,164 @@ class BaseMindooDB {
10545
10591
  }
10546
10592
  };
10547
10593
  }
10594
+ // ==========================================================================
10595
+ // Document copy across databases (src/core/copy/, docs/document-copy.md)
10596
+ // ==========================================================================
10597
+ /**
10598
+ * The seam the copy engine reaches this database through.
10599
+ *
10600
+ * The engine lives in `src/core/copy/` rather than as more methods on this
10601
+ * already-large class, and needs a few things that are private here (entry
10602
+ * signing, the attachment-ref snapshot, the attachment crypto envelope). This
10603
+ * hands them over as one narrow object instead of widening their visibility.
10604
+ *
10605
+ * @internal Not part of the supported API; may change without notice.
10606
+ */
10607
+ getCopyEngineHost() {
10608
+ return {
10609
+ db: this,
10610
+ tenant: this.tenant,
10611
+ store: this.store,
10612
+ attachmentStore: this.getEffectiveAttachmentStore(),
10613
+ computeEntryMetadataSignature: (meta, signing) => this.computeEntryMetadataSignature(meta, signing),
10614
+ collectAttachmentRefs: (doc) => this.collectAttachmentRefs(doc),
10615
+ decryptAttachmentPayload: (payload, keyId) => this.tenant.decryptAttachmentPayload(payload, keyId),
10616
+ encryptAttachmentPayload: (payload, keyId) => this.tenant.encryptAttachmentPayload(payload, keyId),
10617
+ syncStoreChanges: () => this.syncStoreChanges(),
10618
+ evaluateWriteAccess: (op, signerKey, isAuthor) => this.evaluateClientWriteAccess({ op, signerKey, isAuthor }),
10619
+ hasWriteContentRules: async (op) => {
10620
+ const directory = await this.tenant.openDirectory();
10621
+ return (await directory.hasWriteContentRules?.(op, this.store.getId())) ?? false;
10622
+ },
10623
+ remapAttachmentPointers: (docId, lastChunkIdByAttachmentId, signing) => this.remapAttachmentPointers(docId, lastChunkIdByAttachmentId, signing),
10624
+ };
10625
+ }
10626
+ /** Obtain the copy seam from another database, with a clear error if absent. */
10627
+ resolveCopyHost(db, role) {
10628
+ const host = db.getCopyEngineHost?.();
10629
+ if (!host) {
10630
+ throw new Error(`The ${role} database does not support document copying (expected a BaseMindooDB instance).`);
10631
+ }
10632
+ return host;
10633
+ }
10634
+ /**
10635
+ * Re-point `_attachments[].lastChunkId` at a different set of chunks, as one
10636
+ * `doc_change`.
10637
+ *
10638
+ * Only used when a document is duplicated inside its own database: the copied
10639
+ * chunks had to be renamed to avoid colliding with the source's (see
10640
+ * `copy/attachments.ts`), so the copy's current revision has to be told where
10641
+ * its own chunks are. `changeDoc` deliberately offers no way to write
10642
+ * `_attachments` directly, hence this internal path.
10643
+ */
10644
+ async remapAttachmentPointers(docId, lastChunkIdByAttachmentId, signing) {
10645
+ if (lastChunkIdByAttachmentId.size === 0) {
10646
+ return;
10647
+ }
10648
+ const internalDoc = this.getCachedDocument(docId) ?? (await this.loadDocumentInternal(docId));
10649
+ if (!internalDoc) {
10650
+ throw new Error(`Cannot remap attachment pointers: document ${docId} was not found.`);
10651
+ }
10652
+ const now = (0, timeSource_1.semanticNow)();
10653
+ let changed = false;
10654
+ const newDoc = this.runChangeWithOutdatedDocRecovery(internalDoc, (doc) => automerge_adapter_1.Automerge.change(doc, { time: now }, (mutableDoc) => {
10655
+ const attachments = mutableDoc._attachments ?? [];
10656
+ for (const attachment of attachments) {
10657
+ const nextChunkId = lastChunkIdByAttachmentId.get(attachment.attachmentId);
10658
+ if (nextChunkId && attachment.lastChunkId !== nextChunkId) {
10659
+ attachment.lastChunkId = nextChunkId;
10660
+ changed = true;
10661
+ }
10662
+ }
10663
+ if (changed) {
10664
+ mutableDoc._lastModified = now;
10665
+ }
10666
+ }));
10667
+ if (!changed) {
10668
+ return;
10669
+ }
10670
+ const changeBytes = automerge_adapter_1.Automerge.getLastLocalChange(newDoc);
10671
+ if (!changeBytes) {
10672
+ throw new Error("Failed to get change bytes while remapping attachment pointers");
10673
+ }
10674
+ const entry = await this.buildDocChangeStoreEntry({
10675
+ internalDoc,
10676
+ changeBytes,
10677
+ now,
10678
+ useCustomKey: signing !== undefined,
10679
+ signingKeyPair: signing?.signingKeyPair,
10680
+ signingKeyPassword: signing?.signingKeyPassword,
10681
+ attachmentRefs: this.collectAttachmentRefs(newDoc),
10682
+ });
10683
+ await this.store.putEntries([entry]);
10684
+ this.registerAutomergeHashMapping(docId, automerge_adapter_1.Automerge.decodeChange(changeBytes).hash, entry.id);
10685
+ internalDoc.doc = newDoc;
10686
+ internalDoc.lastModified = now;
10687
+ internalDoc.awaitingWitness = (0, storeEntryTime_1.isProvisional)(entry) || (internalDoc.awaitingWitness ?? false);
10688
+ const versioned = (0, storeEntryTime_1.isVersioned)(entry) || internalDoc.awaitingWitness || (internalDoc.witnessed ?? false);
10689
+ internalDoc.witnessed = versioned && !internalDoc.awaitingWitness;
10690
+ await this.storeCachedDocument(internalDoc);
10691
+ this.updateIndex(docId, internalDoc.lastModified, internalDoc.isDeleted, internalDoc.decryptionKeyId, "visible", internalDoc.awaitingWitness, internalDoc.witnessed, true);
10692
+ this.markDocDirty(docId);
10693
+ }
10694
+ /**
10695
+ * Report what {@link copyDocumentTo} would do with these options — the chosen
10696
+ * strategy, whether the original authors' signatures would survive, whether
10697
+ * the copy would merge into an existing document — without writing anything.
10698
+ *
10699
+ * The returned `reasons` explain a refusal, and also explain why a permitted
10700
+ * copy has to re-author, so a caller can distinguish an unavoidable
10701
+ * re-authoring from an accidental one.
10702
+ *
10703
+ * @param docId The document to copy.
10704
+ * @param target The database the copy would be written to.
10705
+ * @param options The same options that would be passed to {@link copyDocumentTo}.
10706
+ */
10707
+ async canCopyDocumentTo(docId, target, options) {
10708
+ return (0, copyDocument_1.canCopyDocument)(this.getCopyEngineHost(), this.resolveCopyHost(target, "target"), docId, options);
10709
+ }
10710
+ /**
10711
+ * Copy one document into another database, either flattened to a single
10712
+ * revision or with its full history.
10713
+ *
10714
+ * See `docs/document-copy.md` for the strategy matrix. In short: the default
10715
+ * writes a flattened copy under a fresh id; `mode: "history"` carries the
10716
+ * whole revision graph; and adding `targetDocId: "same"` with
10717
+ * `authorship: "preserve"` (same tenant, same key, different database) keeps
10718
+ * the original signers, which is what makes sharding possible.
10719
+ *
10720
+ * @param docId The document to copy.
10721
+ * @param target The database to copy into.
10722
+ * @param options See {@link CopyDocumentOptions}; every default is documented there.
10723
+ * @return What was copied, including the target document id — returned even on
10724
+ * cancellation, so a resumed run can target the same id.
10725
+ */
10726
+ async copyDocumentTo(docId, target, options) {
10727
+ const targetHost = this.resolveCopyHost(target, "target");
10728
+ this.assertWritable("copyDocumentTo");
10729
+ return (0, copyDocument_1.copyDocument)(this.getCopyEngineHost(), targetHost, docId, options);
10730
+ }
10731
+ /**
10732
+ * Copy many documents into another database in one pass.
10733
+ *
10734
+ * With `mode: "history"`, `targetDocId: "same"` and `authorship: "preserve"`
10735
+ * this is the database sharding primitive: document ids, full history and the
10736
+ * original signers all survive the split, and nothing is decrypted or
10737
+ * re-signed along the way. Repeated runs are safe and transfer only the delta,
10738
+ * so a shard can proceed against a live database.
10739
+ *
10740
+ * Per-document failures do not abort the run; they are collected in
10741
+ * {@link CopyDocumentsResult.failed}.
10742
+ *
10743
+ * @param selector Which documents to copy — explicit ids, id prefixes, or both.
10744
+ * @param target The database to copy into.
10745
+ * @param options See {@link CopyDocumentsOptions}.
10746
+ */
10747
+ async copyDocumentsTo(selector, target, options) {
10748
+ const targetHost = this.resolveCopyHost(target, "target");
10749
+ this.assertWritable("copyDocumentsTo");
10750
+ return (0, copyDocuments_1.copyDocuments)(this.getCopyEngineHost(), targetHost, selector, options);
10751
+ }
10548
10752
  /**
10549
10753
  * Pull changes from a remote content-addressed store or another MindooDB instance.
10550
10754
  *