mindoodb 0.0.79 → 0.0.80
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 +1 -0
- package/dist/browser/index.mjs +5 -0
- package/dist/core/BaseMindooDB.d.ts +16 -6
- package/dist/core/BaseMindooDB.d.ts.map +1 -1
- package/dist/core/BaseMindooDB.js +108 -38
- package/dist/core/BaseMindooDB.js.map +1 -1
- package/dist/core/changeAttribution.d.ts +66 -0
- package/dist/core/changeAttribution.d.ts.map +1 -0
- package/dist/core/changeAttribution.js +109 -0
- package/dist/core/changeAttribution.js.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/types.d.ts +58 -5
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/userkeys/sealedTypes.d.ts +2 -0
- package/dist/core/userkeys/sealedTypes.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@ exports.BaseMindooDB = exports.CUSTOM_DOC_ID_INITIAL_CHANGE_BYTES = void 0;
|
|
|
7
7
|
const automerge_adapter_1 = require("./automerge-adapter");
|
|
8
8
|
const values_1 = require("./values");
|
|
9
9
|
const storeEntryTime_1 = require("./storeEntryTime");
|
|
10
|
+
const changeAttribution_1 = require("./changeAttribution");
|
|
10
11
|
const types_1 = require("./types");
|
|
11
12
|
const types_2 = require("./types");
|
|
12
13
|
const types_3 = require("./appendonlystores/types");
|
|
@@ -4054,6 +4055,7 @@ class BaseMindooDB {
|
|
|
4054
4055
|
}
|
|
4055
4056
|
this.validateIdPrefixOption("createDocuments", options);
|
|
4056
4057
|
this.validateAssumeUniqueIdOption("createDocuments", options);
|
|
4058
|
+
(0, changeAttribution_1.encodeChangeAttribution)(options.attribution);
|
|
4057
4059
|
}
|
|
4058
4060
|
const results = new Array(inputs.length).fill(null);
|
|
4059
4061
|
const prepared = [];
|
|
@@ -4080,7 +4082,7 @@ class BaseMindooDB {
|
|
|
4080
4082
|
for (const [key, value] of valueEntries) {
|
|
4081
4083
|
data[key] = value;
|
|
4082
4084
|
}
|
|
4083
|
-
}, options.signingKeyPair, options.signingKeyPassword, options.bypassAccessControlPrecheck);
|
|
4085
|
+
}, options.signingKeyPair, options.signingKeyPassword, options.bypassAccessControlPrecheck, undefined, undefined, options.attribution);
|
|
4084
4086
|
}
|
|
4085
4087
|
results[index] = existingDoc;
|
|
4086
4088
|
continue;
|
|
@@ -4197,6 +4199,9 @@ class BaseMindooDB {
|
|
|
4197
4199
|
// of concurrent same-id creates is then not a concern and the create can
|
|
4198
4200
|
// follow the generated-ID path (initialValues baked into doc_create).
|
|
4199
4201
|
const useDeterministicSeed = useCustomDocId && !options.assumeUniqueId;
|
|
4202
|
+
// Validated before anything is written. The deterministic seed change of a
|
|
4203
|
+
// convergent custom id is fixed bytes and cannot carry it.
|
|
4204
|
+
const changeMessage = (0, changeAttribution_1.encodeChangeAttribution)(options.attribution);
|
|
4200
4205
|
// Sanitize caller-provided initial values: internal/reserved fields (those
|
|
4201
4206
|
// starting with "_", e.g. "_attachments") are managed by MindooDB and must
|
|
4202
4207
|
// not be seeded by callers. `undefined` values are treated as "not set"
|
|
@@ -4244,7 +4249,7 @@ class BaseMindooDB {
|
|
|
4244
4249
|
if (existing) {
|
|
4245
4250
|
if (existing.isDeleted) {
|
|
4246
4251
|
this.logger.debug(`Document ${docId} exists as a tombstone; undeleting existing custom-id document`);
|
|
4247
|
-
await this.undeleteDocInternal(docId, signingKeyPair, signingKeyPassword);
|
|
4252
|
+
await this.undeleteDocInternal(docId, signingKeyPair, signingKeyPassword, undefined, options.attribution);
|
|
4248
4253
|
const undeleted = await this.loadDocumentInternal(docId);
|
|
4249
4254
|
if (!undeleted || undeleted.isDeleted) {
|
|
4250
4255
|
throw new Error(`Document ${docId} could not be undeleted`);
|
|
@@ -4287,7 +4292,7 @@ class BaseMindooDB {
|
|
|
4287
4292
|
this.logger.debug(`Creating initial Automerge change for document ${docId}`);
|
|
4288
4293
|
const initialDoc = automerge_adapter_1.Automerge.init();
|
|
4289
4294
|
try {
|
|
4290
|
-
newDoc = automerge_adapter_1.Automerge.change(initialDoc, (doc) => {
|
|
4295
|
+
newDoc = automerge_adapter_1.Automerge.change(initialDoc, changeMessage ? { message: changeMessage } : {}, (doc) => {
|
|
4291
4296
|
// Store metadata in the document payload
|
|
4292
4297
|
// We need to modify the document to ensure a change is created
|
|
4293
4298
|
doc._attachments = [];
|
|
@@ -4463,6 +4468,7 @@ class BaseMindooDB {
|
|
|
4463
4468
|
// out of the deterministic seed, so initialValues fold into doc_create
|
|
4464
4469
|
// and the document costs a single store entry.
|
|
4465
4470
|
const useDeterministicSeed = useCustomDocId && !options.assumeUniqueId;
|
|
4471
|
+
const changeMessage = (0, changeAttribution_1.encodeChangeAttribution)(options.attribution);
|
|
4466
4472
|
if (useDeterministicSeed && options.recipients !== undefined) {
|
|
4467
4473
|
throw new Error("createDocuments: recipients is not supported together with a convergent custom id; pass assumeUniqueId");
|
|
4468
4474
|
}
|
|
@@ -4497,7 +4503,7 @@ class BaseMindooDB {
|
|
|
4497
4503
|
}
|
|
4498
4504
|
else {
|
|
4499
4505
|
const initialDoc = automerge_adapter_1.Automerge.init();
|
|
4500
|
-
seedDoc = automerge_adapter_1.Automerge.change(initialDoc, (doc) => {
|
|
4506
|
+
seedDoc = automerge_adapter_1.Automerge.change(initialDoc, changeMessage ? { message: changeMessage } : {}, (doc) => {
|
|
4501
4507
|
doc._attachments = [];
|
|
4502
4508
|
if (sealedCreate) {
|
|
4503
4509
|
(0, recipients_1.applyEncryptForCreate)(doc, sealedCreate.encryptFor);
|
|
@@ -4521,7 +4527,7 @@ class BaseMindooDB {
|
|
|
4521
4527
|
let finalDoc = seedDoc;
|
|
4522
4528
|
let valueChangeBytes = null;
|
|
4523
4529
|
if (useDeterministicSeed && initialValueEntries.length > 0) {
|
|
4524
|
-
finalDoc = automerge_adapter_1.Automerge.change(seedDoc, { time: now }, (doc) => {
|
|
4530
|
+
finalDoc = automerge_adapter_1.Automerge.change(seedDoc, { time: now, message: changeMessage }, (doc) => {
|
|
4525
4531
|
for (const [key, value] of initialValueEntries) {
|
|
4526
4532
|
this.assignHydrated(doc, key, value);
|
|
4527
4533
|
}
|
|
@@ -4957,12 +4963,14 @@ class BaseMindooDB {
|
|
|
4957
4963
|
};
|
|
4958
4964
|
// Wrap and yield (including lifecycle terminal entries)
|
|
4959
4965
|
const wrappedDoc = this.wrapDocument(internalDoc);
|
|
4966
|
+
const attribution = this.attributionFromChangePayload(entryMetadata.entryType, decryptedPayload);
|
|
4960
4967
|
yield {
|
|
4961
4968
|
changeEntryId: entryMetadata.id,
|
|
4962
4969
|
doc: wrappedDoc,
|
|
4963
4970
|
changeCreatedAt: entryMetadata.createdAt,
|
|
4964
4971
|
changeCreatedByPublicKey: entryMetadata.createdByPublicKey,
|
|
4965
4972
|
changeDependencyIds: [...entryMetadata.dependencyIds],
|
|
4973
|
+
...(attribution ? { attribution } : {}),
|
|
4966
4974
|
};
|
|
4967
4975
|
}
|
|
4968
4976
|
}
|
|
@@ -5427,6 +5435,40 @@ class BaseMindooDB {
|
|
|
5427
5435
|
this.revisionFeedDocCache.delete(oldest);
|
|
5428
5436
|
}
|
|
5429
5437
|
}
|
|
5438
|
+
/**
|
|
5439
|
+
* The agent attribution carried by a document change payload (the Automerge
|
|
5440
|
+
* change message), if any. Snapshots and undecodable payloads yield none.
|
|
5441
|
+
*/
|
|
5442
|
+
attributionFromChangePayload(entryType, payload) {
|
|
5443
|
+
if (entryType !== "doc_create" &&
|
|
5444
|
+
entryType !== "doc_change" &&
|
|
5445
|
+
entryType !== "doc_delete" &&
|
|
5446
|
+
entryType !== "doc_undelete") {
|
|
5447
|
+
return undefined;
|
|
5448
|
+
}
|
|
5449
|
+
try {
|
|
5450
|
+
return (0, changeAttribution_1.decodeChangeAttribution)(automerge_adapter_1.Automerge.decodeChange(payload).message);
|
|
5451
|
+
}
|
|
5452
|
+
catch {
|
|
5453
|
+
return undefined;
|
|
5454
|
+
}
|
|
5455
|
+
}
|
|
5456
|
+
/**
|
|
5457
|
+
* Verify, decrypt and read the attribution of one stored entry. Entries with
|
|
5458
|
+
* an invalid signature or a payload this replica cannot decrypt yield none.
|
|
5459
|
+
*/
|
|
5460
|
+
async readStoreEntryAttribution(entry) {
|
|
5461
|
+
try {
|
|
5462
|
+
if (!(await this.tenant.verifyEntrySignature(entry, entry.encryptedData))) {
|
|
5463
|
+
return undefined;
|
|
5464
|
+
}
|
|
5465
|
+
const payload = await this.tenant.decryptPayload(entry.encryptedData, entry.decryptionKeyId);
|
|
5466
|
+
return this.attributionFromChangePayload(entry.entryType, payload);
|
|
5467
|
+
}
|
|
5468
|
+
catch {
|
|
5469
|
+
return undefined;
|
|
5470
|
+
}
|
|
5471
|
+
}
|
|
5430
5472
|
async getDocumentHistoryPage(docId, options) {
|
|
5431
5473
|
const startedAt = Date.now();
|
|
5432
5474
|
const limit = Math.max(1, Math.floor(options?.limit ?? 100));
|
|
@@ -5456,6 +5498,21 @@ class BaseMindooDB {
|
|
|
5456
5498
|
// order for same-millisecond entries.
|
|
5457
5499
|
isDeleted: this.computeIsDeletedFromMetadata(relevantEntries.slice(0, offset + sliceIndex + 1)),
|
|
5458
5500
|
}));
|
|
5501
|
+
if (options?.includeAttribution && slice.length > 0) {
|
|
5502
|
+
// Opt-in: costs one load + signature check + decrypt per returned entry,
|
|
5503
|
+
// still bounded by the page size.
|
|
5504
|
+
await this.ingestSealedKeyFromEntries(slice);
|
|
5505
|
+
const loaded = await this.store.getEntries(slice.map((entry) => entry.id));
|
|
5506
|
+
const loadedById = new Map(loaded.map((entry) => [entry.id, entry]));
|
|
5507
|
+
for (const pageEntry of entries) {
|
|
5508
|
+
const stored = loadedById.get(pageEntry.entryId);
|
|
5509
|
+
const attribution = stored
|
|
5510
|
+
? await this.readStoreEntryAttribution(stored)
|
|
5511
|
+
: undefined;
|
|
5512
|
+
if (attribution)
|
|
5513
|
+
pageEntry.attribution = attribution;
|
|
5514
|
+
}
|
|
5515
|
+
}
|
|
5459
5516
|
const nextOffset = offset + entries.length;
|
|
5460
5517
|
const hasMore = nextOffset < relevantEntries.length;
|
|
5461
5518
|
const result = {
|
|
@@ -7146,7 +7203,7 @@ class BaseMindooDB {
|
|
|
7146
7203
|
return docIds;
|
|
7147
7204
|
}
|
|
7148
7205
|
async deleteDocument(docId, options = {}) {
|
|
7149
|
-
return this.deleteDocInternal(docId, options.signingKeyPair, options.signingKeyPassword, options.bypassAccessControlPrecheck);
|
|
7206
|
+
return this.deleteDocInternal(docId, options.signingKeyPair, options.signingKeyPassword, options.bypassAccessControlPrecheck, options.attribution);
|
|
7150
7207
|
}
|
|
7151
7208
|
/**
|
|
7152
7209
|
* Bulk-delete documents in a single append-only store write.
|
|
@@ -7167,6 +7224,7 @@ class BaseMindooDB {
|
|
|
7167
7224
|
if (docIds.length === 0) {
|
|
7168
7225
|
return;
|
|
7169
7226
|
}
|
|
7227
|
+
const changeMessage = (0, changeAttribution_1.encodeChangeAttribution)(options.attribution);
|
|
7170
7228
|
// One gate call per document id: inside `userdirectory` the applicable rule
|
|
7171
7229
|
// depends on the id (personal documents are owned by their creator), so a
|
|
7172
7230
|
// single batch-wide check would deny an owner deleting their own data.
|
|
@@ -7203,7 +7261,7 @@ class BaseMindooDB {
|
|
|
7203
7261
|
this.logger.debug(`Skipping bulk delete for ${docId} — not found or already deleted`);
|
|
7204
7262
|
continue;
|
|
7205
7263
|
}
|
|
7206
|
-
preparedByIndex[index] = await this.prepareLifecycleEntry(internalDoc, "doc_delete", options.signingKeyPair, options.signingKeyPassword, options.bypassAccessControlPrecheck);
|
|
7264
|
+
preparedByIndex[index] = await this.prepareLifecycleEntry(internalDoc, "doc_delete", options.signingKeyPair, options.signingKeyPassword, options.bypassAccessControlPrecheck, changeMessage);
|
|
7207
7265
|
}
|
|
7208
7266
|
}));
|
|
7209
7267
|
const prepared = preparedByIndex.filter((p) => p !== null);
|
|
@@ -7223,7 +7281,7 @@ class BaseMindooDB {
|
|
|
7223
7281
|
return this.deleteDocument(docId, { signingKeyPair, signingKeyPassword });
|
|
7224
7282
|
}
|
|
7225
7283
|
async undeleteDocument(docId, options = {}) {
|
|
7226
|
-
return this.undeleteDocInternal(docId, options.signingKeyPair, options.signingKeyPassword, options.bypassAccessControlPrecheck);
|
|
7284
|
+
return this.undeleteDocInternal(docId, options.signingKeyPair, options.signingKeyPassword, options.bypassAccessControlPrecheck, options.attribution);
|
|
7227
7285
|
}
|
|
7228
7286
|
async assertLifecycleMutationAllowed(op, signingKeyPair, signingKeyPassword, documentUsernameHash, docId) {
|
|
7229
7287
|
this.assertWritable("document lifecycle mutation");
|
|
@@ -7241,8 +7299,8 @@ class BaseMindooDB {
|
|
|
7241
7299
|
* change that advances `_lastModified`, wraps it in the requested lifecycle
|
|
7242
7300
|
* StoreEntry type, signs/encrypts it, and updates local caches/indexes.
|
|
7243
7301
|
*/
|
|
7244
|
-
async writeLifecycleEntry(internalDoc, entryType, signingKeyPair, signingKeyPassword, bypassPrecheck) {
|
|
7245
|
-
const prepared = await this.prepareLifecycleEntry(internalDoc, entryType, signingKeyPair, signingKeyPassword, bypassPrecheck);
|
|
7302
|
+
async writeLifecycleEntry(internalDoc, entryType, signingKeyPair, signingKeyPassword, bypassPrecheck, attribution) {
|
|
7303
|
+
const prepared = await this.prepareLifecycleEntry(internalDoc, entryType, signingKeyPair, signingKeyPassword, bypassPrecheck, (0, changeAttribution_1.encodeChangeAttribution)(attribution));
|
|
7246
7304
|
await this.putLocalEntries([prepared.fullEntry]);
|
|
7247
7305
|
await this.applyLifecycleEntryLocally(prepared, { markDirty: true });
|
|
7248
7306
|
}
|
|
@@ -7252,7 +7310,7 @@ class BaseMindooDB {
|
|
|
7252
7310
|
* returned `fullEntry` (single or batched `putEntries`) and then call
|
|
7253
7311
|
* {@link applyLifecycleEntryLocally} to update caches and indexes.
|
|
7254
7312
|
*/
|
|
7255
|
-
async prepareLifecycleEntry(internalDoc, entryType, signingKeyPair, signingKeyPassword, bypassPrecheck) {
|
|
7313
|
+
async prepareLifecycleEntry(internalDoc, entryType, signingKeyPair, signingKeyPassword, bypassPrecheck, changeMessage) {
|
|
7256
7314
|
// Delete and undelete are encoded as normal Automerge changes so the DAG
|
|
7257
7315
|
// keeps causal ancestry, while the StoreEntry type carries lifecycle intent.
|
|
7258
7316
|
const useCustomSigningKey = signingKeyPair !== undefined && signingKeyPassword !== undefined;
|
|
@@ -7272,7 +7330,7 @@ class BaseMindooDB {
|
|
|
7272
7330
|
// `emptyChange`, which always advances the heads with a fresh hash while
|
|
7273
7331
|
// keeping causal ancestry intact.
|
|
7274
7332
|
const headsBefore = automerge_adapter_1.Automerge.getHeads(internalDoc.doc);
|
|
7275
|
-
let newDoc = this.runChangeWithOutdatedDocRecovery(internalDoc, (doc) => automerge_adapter_1.Automerge.change(doc, { time: now }, (mutableDoc) => {
|
|
7333
|
+
let newDoc = this.runChangeWithOutdatedDocRecovery(internalDoc, (doc) => automerge_adapter_1.Automerge.change(doc, { time: now, message: changeMessage }, (mutableDoc) => {
|
|
7276
7334
|
if (!Array.isArray(mutableDoc._attachments)) {
|
|
7277
7335
|
mutableDoc._attachments = [];
|
|
7278
7336
|
}
|
|
@@ -7281,6 +7339,7 @@ class BaseMindooDB {
|
|
|
7281
7339
|
if (automerge_adapter_1.Automerge.getHeads(newDoc).every((head) => headsBefore.includes(head))) {
|
|
7282
7340
|
newDoc = automerge_adapter_1.Automerge.emptyChange(newDoc, {
|
|
7283
7341
|
time: now,
|
|
7342
|
+
message: changeMessage,
|
|
7284
7343
|
});
|
|
7285
7344
|
}
|
|
7286
7345
|
const changeBytes = automerge_adapter_1.Automerge.getLastLocalChange(newDoc);
|
|
@@ -7407,7 +7466,8 @@ class BaseMindooDB {
|
|
|
7407
7466
|
* Handles both regular deletion (signed by current user) and
|
|
7408
7467
|
* deletion with a custom signing key (e.g., for directory operations).
|
|
7409
7468
|
*/
|
|
7410
|
-
async deleteDocInternal(docId, signingKeyPair, signingKeyPassword, bypassPrecheck) {
|
|
7469
|
+
async deleteDocInternal(docId, signingKeyPair, signingKeyPassword, bypassPrecheck, attribution) {
|
|
7470
|
+
(0, changeAttribution_1.encodeChangeAttribution)(attribution);
|
|
7411
7471
|
const useCustomSigningKey = signingKeyPair !== undefined && signingKeyPassword !== undefined;
|
|
7412
7472
|
this.logger.debug(`Deleting document ${docId}${useCustomSigningKey ? " using custom signing key" : ""}`);
|
|
7413
7473
|
await this.assertLifecycleMutationAllowed("doc_delete", signingKeyPair, signingKeyPassword, undefined, docId);
|
|
@@ -7416,10 +7476,11 @@ class BaseMindooDB {
|
|
|
7416
7476
|
if (!internalDoc || internalDoc.isDeleted) {
|
|
7417
7477
|
throw new Error(`Document ${docId} not found or already deleted`);
|
|
7418
7478
|
}
|
|
7419
|
-
await this.writeLifecycleEntry(internalDoc, "doc_delete", signingKeyPair, signingKeyPassword, bypassPrecheck);
|
|
7479
|
+
await this.writeLifecycleEntry(internalDoc, "doc_delete", signingKeyPair, signingKeyPassword, bypassPrecheck, attribution);
|
|
7420
7480
|
this.logger.info(`Document ${docId} deleted successfully`);
|
|
7421
7481
|
}
|
|
7422
|
-
async undeleteDocInternal(docId, signingKeyPair, signingKeyPassword, bypassPrecheck) {
|
|
7482
|
+
async undeleteDocInternal(docId, signingKeyPair, signingKeyPassword, bypassPrecheck, attribution) {
|
|
7483
|
+
(0, changeAttribution_1.encodeChangeAttribution)(attribution);
|
|
7423
7484
|
const useCustomSigningKey = signingKeyPair !== undefined && signingKeyPassword !== undefined;
|
|
7424
7485
|
this.logger.debug(`Undeleting document ${docId}${useCustomSigningKey ? " using custom signing key" : ""}`);
|
|
7425
7486
|
const internalDoc = await this.loadDocumentInternal(docId);
|
|
@@ -7431,11 +7492,11 @@ class BaseMindooDB {
|
|
|
7431
7492
|
this.logger.debug(`Document ${docId} is already alive; undelete is a no-op`);
|
|
7432
7493
|
return;
|
|
7433
7494
|
}
|
|
7434
|
-
await this.writeLifecycleEntry(internalDoc, "doc_undelete", signingKeyPair, signingKeyPassword, bypassPrecheck);
|
|
7495
|
+
await this.writeLifecycleEntry(internalDoc, "doc_undelete", signingKeyPair, signingKeyPassword, bypassPrecheck, attribution);
|
|
7435
7496
|
this.logger.info(`Document ${docId} undeleted successfully`);
|
|
7436
7497
|
}
|
|
7437
7498
|
async changeDoc(doc, changeFunc, options = {}) {
|
|
7438
|
-
return this.changeDocInternal(doc, changeFunc, options.signingKeyPair, options.signingKeyPassword, options.bypassAccessControlPrecheck);
|
|
7499
|
+
return this.changeDocInternal(doc, changeFunc, options.signingKeyPair, options.signingKeyPassword, options.bypassAccessControlPrecheck, undefined, undefined, options.attribution);
|
|
7439
7500
|
}
|
|
7440
7501
|
async changeDocWithSigningKey(doc, changeFunc, signingKeyPair, signingKeyPassword) {
|
|
7441
7502
|
return this.changeDoc(doc, changeFunc, {
|
|
@@ -7443,8 +7504,9 @@ class BaseMindooDB {
|
|
|
7443
7504
|
signingKeyPassword,
|
|
7444
7505
|
});
|
|
7445
7506
|
}
|
|
7446
|
-
async applyTextPatch(doc, patch) {
|
|
7507
|
+
async applyTextPatch(doc, patch, options = {}) {
|
|
7447
7508
|
this.assertWritable("applyTextPatch");
|
|
7509
|
+
const changeMessage = (0, changeAttribution_1.encodeChangeAttribution)(options.attribution);
|
|
7448
7510
|
const docId = doc.getId();
|
|
7449
7511
|
this.logger.debug(`===== applyTextPatch called for document ${docId} =====`);
|
|
7450
7512
|
let internalDoc = this.getCachedDocument(docId);
|
|
@@ -7473,10 +7535,10 @@ class BaseMindooDB {
|
|
|
7473
7535
|
try {
|
|
7474
7536
|
newDoc = this.runChangeWithOutdatedDocRecovery(internalDoc, (doc) => {
|
|
7475
7537
|
if (baseHeads && baseHeads.length > 0) {
|
|
7476
|
-
const result = automerge_adapter_1.Automerge.changeAt(doc, baseHeads, applyEdits);
|
|
7538
|
+
const result = automerge_adapter_1.Automerge.changeAt(doc, baseHeads, { message: changeMessage }, applyEdits);
|
|
7477
7539
|
return result.newDoc;
|
|
7478
7540
|
}
|
|
7479
|
-
return automerge_adapter_1.Automerge.change(doc, applyEdits);
|
|
7541
|
+
return automerge_adapter_1.Automerge.change(doc, { message: changeMessage }, applyEdits);
|
|
7480
7542
|
});
|
|
7481
7543
|
if (this.isDebugEnabled())
|
|
7482
7544
|
this.logger.debug(`Successfully applied text patch, new document heads: ${JSON.stringify(automerge_adapter_1.Automerge.getHeads(newDoc))}`);
|
|
@@ -7500,8 +7562,9 @@ class BaseMindooDB {
|
|
|
7500
7562
|
data: wrapped.getData(),
|
|
7501
7563
|
};
|
|
7502
7564
|
}
|
|
7503
|
-
async applyRichTextPatch(doc, patch) {
|
|
7565
|
+
async applyRichTextPatch(doc, patch, options = {}) {
|
|
7504
7566
|
this.assertWritable("applyRichTextPatch");
|
|
7567
|
+
const changeMessage = (0, changeAttribution_1.encodeChangeAttribution)(options.attribution);
|
|
7505
7568
|
const docId = doc.getId();
|
|
7506
7569
|
this.logger.debug(`===== applyRichTextPatch called for document ${docId} =====`);
|
|
7507
7570
|
let internalDoc = this.getCachedDocument(docId);
|
|
@@ -7541,9 +7604,9 @@ class BaseMindooDB {
|
|
|
7541
7604
|
try {
|
|
7542
7605
|
newDoc = this.runChangeWithOutdatedDocRecovery(internalDoc, (doc) => {
|
|
7543
7606
|
if (patch.baseHeads && patch.baseHeads.length > 0) {
|
|
7544
|
-
return this.changeOnForkAt(doc, patch.baseHeads, applySpans);
|
|
7607
|
+
return this.changeOnForkAt(doc, patch.baseHeads, applySpans, changeMessage);
|
|
7545
7608
|
}
|
|
7546
|
-
return automerge_adapter_1.Automerge.change(doc, applySpans);
|
|
7609
|
+
return automerge_adapter_1.Automerge.change(doc, { message: changeMessage }, applySpans);
|
|
7547
7610
|
});
|
|
7548
7611
|
if (this.isDebugEnabled())
|
|
7549
7612
|
this.logger.debug(`Successfully applied rich-text patch, new document heads: ${JSON.stringify(automerge_adapter_1.Automerge.getHeads(newDoc))}`);
|
|
@@ -7567,8 +7630,9 @@ class BaseMindooDB {
|
|
|
7567
7630
|
data: wrapped.getData(),
|
|
7568
7631
|
};
|
|
7569
7632
|
}
|
|
7570
|
-
async applyDocumentUpdate(doc, update) {
|
|
7633
|
+
async applyDocumentUpdate(doc, update, options = {}) {
|
|
7571
7634
|
this.assertWritable("applyDocumentUpdate");
|
|
7635
|
+
const changeMessage = (0, changeAttribution_1.encodeChangeAttribution)(options.attribution);
|
|
7572
7636
|
const docId = doc.getId();
|
|
7573
7637
|
let internalDoc = this.getCachedDocument(docId);
|
|
7574
7638
|
if (!internalDoc) {
|
|
@@ -7621,8 +7685,8 @@ class BaseMindooDB {
|
|
|
7621
7685
|
let newDoc;
|
|
7622
7686
|
try {
|
|
7623
7687
|
newDoc = this.runChangeWithOutdatedDocRecovery(internalDoc, (current) => baseHeads
|
|
7624
|
-
? this.changeOnForkAt(current, baseHeads, applyAll)
|
|
7625
|
-
: automerge_adapter_1.Automerge.change(current, applyAll));
|
|
7688
|
+
? this.changeOnForkAt(current, baseHeads, applyAll, changeMessage)
|
|
7689
|
+
: automerge_adapter_1.Automerge.change(current, { message: changeMessage }, applyAll));
|
|
7626
7690
|
}
|
|
7627
7691
|
catch (error) {
|
|
7628
7692
|
this.logger.error(`Error applying document update for document ${docId}:`, error);
|
|
@@ -7681,8 +7745,9 @@ class BaseMindooDB {
|
|
|
7681
7745
|
}
|
|
7682
7746
|
}
|
|
7683
7747
|
}
|
|
7684
|
-
async applyRichTextStepsPatch(doc, patch) {
|
|
7748
|
+
async applyRichTextStepsPatch(doc, patch, options = {}) {
|
|
7685
7749
|
this.assertWritable("applyRichTextStepsPatch");
|
|
7750
|
+
const changeMessage = (0, changeAttribution_1.encodeChangeAttribution)(options.attribution);
|
|
7686
7751
|
const docId = doc.getId();
|
|
7687
7752
|
this.logger.debug(`===== applyRichTextStepsPatch called for document ${docId} =====`);
|
|
7688
7753
|
let internalDoc = this.getCachedDocument(docId);
|
|
@@ -7734,10 +7799,10 @@ class BaseMindooDB {
|
|
|
7734
7799
|
try {
|
|
7735
7800
|
newDoc = this.runChangeWithOutdatedDocRecovery(internalDoc, (doc) => {
|
|
7736
7801
|
if (patch.baseHeads && patch.baseHeads.length > 0) {
|
|
7737
|
-
const result = automerge_adapter_1.Automerge.changeAt(doc, patch.baseHeads, applySteps);
|
|
7802
|
+
const result = automerge_adapter_1.Automerge.changeAt(doc, patch.baseHeads, { message: changeMessage }, applySteps);
|
|
7738
7803
|
return result.newDoc;
|
|
7739
7804
|
}
|
|
7740
|
-
return automerge_adapter_1.Automerge.change(doc, applySteps);
|
|
7805
|
+
return automerge_adapter_1.Automerge.change(doc, { message: changeMessage }, applySteps);
|
|
7741
7806
|
});
|
|
7742
7807
|
if (this.isDebugEnabled())
|
|
7743
7808
|
this.logger.debug(`Successfully applied rich-text steps, new document heads: ${JSON.stringify(automerge_adapter_1.Automerge.getHeads(newDoc))}`);
|
|
@@ -7977,8 +8042,9 @@ class BaseMindooDB {
|
|
|
7977
8042
|
}
|
|
7978
8043
|
return internalDoc;
|
|
7979
8044
|
}
|
|
7980
|
-
async applyJsonPatch(doc, patch) {
|
|
8045
|
+
async applyJsonPatch(doc, patch, options = {}) {
|
|
7981
8046
|
this.assertWritable("applyJsonPatch");
|
|
8047
|
+
const changeMessage = (0, changeAttribution_1.encodeChangeAttribution)(options.attribution);
|
|
7982
8048
|
const docId = doc.getId();
|
|
7983
8049
|
this.logger.debug(`===== applyJsonPatch called for document ${docId} =====`);
|
|
7984
8050
|
let internalDoc = this.getCachedDocument(docId);
|
|
@@ -8003,10 +8069,10 @@ class BaseMindooDB {
|
|
|
8003
8069
|
try {
|
|
8004
8070
|
newDoc = this.runChangeWithOutdatedDocRecovery(internalDoc, (doc) => {
|
|
8005
8071
|
if (patch.baseHeads && patch.baseHeads.length > 0) {
|
|
8006
|
-
const result = automerge_adapter_1.Automerge.changeAt(doc, patch.baseHeads, applyPatch);
|
|
8072
|
+
const result = automerge_adapter_1.Automerge.changeAt(doc, patch.baseHeads, { message: changeMessage }, applyPatch);
|
|
8007
8073
|
return result.newDoc;
|
|
8008
8074
|
}
|
|
8009
|
-
return automerge_adapter_1.Automerge.change(doc, applyPatch);
|
|
8075
|
+
return automerge_adapter_1.Automerge.change(doc, { message: changeMessage }, applyPatch);
|
|
8010
8076
|
});
|
|
8011
8077
|
if (this.isDebugEnabled())
|
|
8012
8078
|
this.logger.debug(`Successfully applied JSON patch, new document heads: ${JSON.stringify(automerge_adapter_1.Automerge.getHeads(newDoc))}`);
|
|
@@ -8030,8 +8096,9 @@ class BaseMindooDB {
|
|
|
8030
8096
|
data: wrapped.getData(),
|
|
8031
8097
|
};
|
|
8032
8098
|
}
|
|
8033
|
-
async changeDocInternal(doc, changeFunc, signingKeyPair, signingKeyPassword, bypassPrecheck, restoredAttachments = [], internalMutation) {
|
|
8099
|
+
async changeDocInternal(doc, changeFunc, signingKeyPair, signingKeyPassword, bypassPrecheck, restoredAttachments = [], internalMutation, attribution) {
|
|
8034
8100
|
this.assertWritable("changeDoc");
|
|
8101
|
+
const changeMessage = (0, changeAttribution_1.encodeChangeAttribution)(attribution);
|
|
8035
8102
|
const docId = doc.getId();
|
|
8036
8103
|
if ((signingKeyPair !== undefined) !== (signingKeyPassword !== undefined)) {
|
|
8037
8104
|
throw new Error("changeDoc: signingKeyPair and signingKeyPassword must be provided together");
|
|
@@ -8326,7 +8393,7 @@ class BaseMindooDB {
|
|
|
8326
8393
|
const headsBeforeChange = automerge_adapter_1.Automerge.getHeads(internalDoc.doc);
|
|
8327
8394
|
let newDoc;
|
|
8328
8395
|
try {
|
|
8329
|
-
newDoc = this.runChangeWithOutdatedDocRecovery(internalDoc, (doc) => automerge_adapter_1.Automerge.change(doc, (automergeDoc) => {
|
|
8396
|
+
newDoc = this.runChangeWithOutdatedDocRecovery(internalDoc, (doc) => automerge_adapter_1.Automerge.change(doc, { message: changeMessage }, (automergeDoc) => {
|
|
8330
8397
|
// Apply all pending changes (sets/updates)
|
|
8331
8398
|
for (const [key, value] of pendingChanges) {
|
|
8332
8399
|
this.assignHydrated(automergeDoc, key, this.hydrateTypedValues(value));
|
|
@@ -9113,17 +9180,17 @@ class BaseMindooDB {
|
|
|
9113
9180
|
* after `baseHeads` (its next sequence number is taken then); in that case a
|
|
9114
9181
|
* fresh actor writes the one change.
|
|
9115
9182
|
*/
|
|
9116
|
-
changeOnForkAt(doc, baseHeads, apply) {
|
|
9183
|
+
changeOnForkAt(doc, baseHeads, apply, changeMessage) {
|
|
9117
9184
|
const current = automerge_adapter_1.Automerge.getHeads(doc);
|
|
9118
9185
|
if (current.length === baseHeads.length &&
|
|
9119
9186
|
current.every((head) => baseHeads.includes(head))) {
|
|
9120
|
-
return automerge_adapter_1.Automerge.change(doc, apply);
|
|
9187
|
+
return automerge_adapter_1.Automerge.change(doc, { message: changeMessage }, apply);
|
|
9121
9188
|
}
|
|
9122
9189
|
const base = automerge_adapter_1.Automerge.view(doc, baseHeads);
|
|
9123
9190
|
const actor = automerge_adapter_1.Automerge.getActorId(doc);
|
|
9124
9191
|
const ownChangeSince = automerge_adapter_1.Automerge.getChanges(base, doc).some((change) => automerge_adapter_1.Automerge.decodeChange(change).actor === actor);
|
|
9125
9192
|
const fork = automerge_adapter_1.Automerge.clone(base, ownChangeSince ? undefined : { actor });
|
|
9126
|
-
const changed = automerge_adapter_1.Automerge.change(fork, apply);
|
|
9193
|
+
const changed = automerge_adapter_1.Automerge.change(fork, { message: changeMessage }, apply);
|
|
9127
9194
|
return automerge_adapter_1.Automerge.merge(doc, changed);
|
|
9128
9195
|
}
|
|
9129
9196
|
runChangeWithOutdatedDocRecovery(internalDoc, apply) {
|
|
@@ -9874,6 +9941,7 @@ class BaseMindooDB {
|
|
|
9874
9941
|
message: typeof decodedChange.message === "string"
|
|
9875
9942
|
? decodedChange.message
|
|
9876
9943
|
: null,
|
|
9944
|
+
attribution: (0, changeAttribution_1.decodeChangeAttribution)(typeof decodedChange.message === "string" ? decodedChange.message : null),
|
|
9877
9945
|
dependencyHashes: Array.isArray(decodedChange.deps)
|
|
9878
9946
|
? decodedChange.deps.filter((dep) => typeof dep === "string")
|
|
9879
9947
|
: [],
|
|
@@ -11078,6 +11146,7 @@ class BaseMindooDB {
|
|
|
11078
11146
|
}
|
|
11079
11147
|
async mutateRecipients(doc, mode, specs, options) {
|
|
11080
11148
|
this.assertWritable("changeDoc");
|
|
11149
|
+
const changeMessage = (0, changeAttribution_1.encodeChangeAttribution)(options?.attribution);
|
|
11081
11150
|
if (!(0, sealedTypes_1.isSealedKeyId)(doc.getDecryptionKeyId())) {
|
|
11082
11151
|
throw new Error("Recipient mutators require a document created with `recipients`");
|
|
11083
11152
|
}
|
|
@@ -11195,7 +11264,7 @@ class BaseMindooDB {
|
|
|
11195
11264
|
});
|
|
11196
11265
|
tenant.rememberSealedGenerations(internalDoc.decryptionKeyId, generations);
|
|
11197
11266
|
const headsBeforeChange = automerge_adapter_1.Automerge.getHeads(internalDoc.doc);
|
|
11198
|
-
const newDoc = automerge_adapter_1.Automerge.change(internalDoc.doc, (draft) => {
|
|
11267
|
+
const newDoc = automerge_adapter_1.Automerge.change(internalDoc.doc, { message: changeMessage }, (draft) => {
|
|
11199
11268
|
if (addedTargets.length > 0) {
|
|
11200
11269
|
const addMap = {};
|
|
11201
11270
|
for (const target of addedTargets) {
|
|
@@ -11235,6 +11304,7 @@ class BaseMindooDB {
|
|
|
11235
11304
|
signingKeyPair: options.signingKeyPair,
|
|
11236
11305
|
signingKeyPassword: options.signingKeyPassword,
|
|
11237
11306
|
bypassAccessControlPrecheck: options.bypassAccessControlPrecheck,
|
|
11307
|
+
attribution: options.attribution,
|
|
11238
11308
|
});
|
|
11239
11309
|
}
|
|
11240
11310
|
const wrapped = this.wrapDocument(internalDoc);
|