@lossless.org/client 1.18.1 → 1.18.2

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.
@@ -76,8 +76,8 @@ export const validateOrdinaryStoredDocument = (documentArg, policyArg, persisted
76
76
  if (policyArg.timestamps === 'managed' &&
77
77
  (typeof clone._createdAt !== 'string' || typeof clone._updatedAt !== 'string'))
78
78
  throw invalid();
79
- assertBsonWithoutMutation(clone, policyArg.assertStoredDocument, invalid);
79
+ assertBsonWithoutMutation(clone, plugins.mongodb.BSON.serialize(clone), (documentArg) => plugins.mongodb.BSON.serialize(documentArg), policyArg.assertStoredDocument, invalid);
80
80
  // The assertion must not retain authority to mutate the returned document.
81
81
  return plugins.mongodb.BSON.deserialize(plugins.mongodb.BSON.serialize(clone));
82
82
  };
83
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5vcmRpbmFyeXBlcnNpc3RlbmNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvbm9zcWxkYi9jbGFzc2VzLm9yZGluYXJ5cGVyc2lzdGVuY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxFQUFFLHlCQUF5QixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDckUsT0FBTyxFQUFFLHlCQUF5QixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFpQnZFLE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsaURBQWlELENBQUMsQ0FBQztBQUNuRixNQUFNLFdBQVcsR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLDhDQUE4QyxDQUFDLENBQUM7QUFFL0U7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxNQUFNLHlCQUF5QixHQUFHLENBQUMsUUFBYSxFQUFXLEVBQUUsQ0FDbEUsT0FBTyxDQUFDLFFBQVEsRUFBRSxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUM7QUFFbkM7OztHQUdHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sK0JBQStCLEdBQUcsQ0FBQyxPQUFlLEVBQUUsUUFBZ0IsRUFBVyxFQUFFLENBQzVGLE9BQU8sQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLFlBQVksQ0FBQyxLQUFLLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFBUSxFQUFFLFlBQVksQ0FBQztJQUMxRSxPQUFPLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxXQUFXLENBQUMsS0FBSyxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxXQUFXLENBQUMsQ0FBQztBQUUzRSxNQUFNLENBQUMsTUFBTSw0QkFBNEIsR0FBRyxDQUFDLFFBQWEsRUFBMEMsRUFBRTtJQUNwRyxNQUFNLE1BQU0sR0FBRyxRQUFRLEVBQUUsQ0FBQyxZQUFZLENBQTJDLENBQUM7SUFDbEYsSUFBSSxNQUFNLElB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
83
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5vcmRpbmFyeXBlcnNpc3RlbmNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvbm9zcWxkYi9jbGFzc2VzLm9yZGluYXJ5cGVyc2lzdGVuY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxFQUFFLHlCQUF5QixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDckUsT0FBTyxFQUFFLHlCQUF5QixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFpQnZFLE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsaURBQWlELENBQUMsQ0FBQztBQUNuRixNQUFNLFdBQVcsR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLDhDQUE4QyxDQUFDLENBQUM7QUFFL0U7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxNQUFNLHlCQUF5QixHQUFHLENBQUMsUUFBYSxFQUFXLEVBQUUsQ0FDbEUsT0FBTyxDQUFDLFFBQVEsRUFBRSxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUM7QUFFbkM7OztHQUdHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sK0JBQStCLEdBQUcsQ0FBQyxPQUFlLEVBQUUsUUFBZ0IsRUFBVyxFQUFFLENBQzVGLE9BQU8sQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLFlBQVksQ0FBQyxLQUFLLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFBUSxFQUFFLFlBQVksQ0FBQztJQUMxRSxPQUFPLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxXQUFXLENBQUMsS0FBSyxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxXQUFXLENBQUMsQ0FBQztBQUUzRSxNQUFNLENBQUMsTUFBTSw0QkFBNEIsR0FBRyxDQUFDLFFBQWEsRUFBMEMsRUFBRTtJQUNwRyxNQUFNLE1BQU0sR0FBRyxRQUFRLEVBQUUsQ0FBQyxZQUFZLENBQTJDLENBQUM7SUFDbEYsSUFBSSxNQUFNLElB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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lossless.org/client",
3
- "version": "1.18.1",
3
+ "version": "1.18.2",
4
4
  "private": false,
5
5
  "description": "One typed client for NoSQLDB, MongoDB, SQLDB, MariaDB, ClickHouse and S3 object storage.",
6
6
  "exports": {
@@ -81,15 +81,15 @@
81
81
  "@aws-sdk/client-s3": "^3.1039.0",
82
82
  "@clickhouse/client": "1.23.1",
83
83
  "@git.zone/tsbuild": "5.1.2",
84
- "@git.zone/tsbundle": "3.1.2",
84
+ "@git.zone/tsbundle": "3.1.3",
85
85
  "@git.zone/tsrun": "3.0.2",
86
- "@git.zone/tstest": "6.3.5",
86
+ "@git.zone/tstest": "7.1.0",
87
87
  "@lossless.org/nosqldb": "8.0.2",
88
88
  "@lossless.org/nosqldb-qualified": "npm:@lossless.org/nosqldb@10.7.2",
89
89
  "@lossless.org/objectstorage": "10.3.1",
90
90
  "@lossless.org/sqldb": "0.2.3",
91
91
  "@push.rocks/qenv": "^6.1.3",
92
- "@types/node": "26.6.2",
92
+ "@types/node": "26.6.3",
93
93
  "mariadb": "3.5.4",
94
94
  "mongodb-memory-server": "11.1.0"
95
95
  },
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@lossless.org/client',
6
- version: '1.18.1',
6
+ version: '1.18.2',
7
7
  description: 'One typed client for NoSQLDB, MongoDB, SQLDB, MariaDB, ClickHouse and S3 object storage.'
8
8
  }
@@ -1,12 +1,15 @@
1
- import * as plugins from './plugins.js';
2
-
3
- /** @internal Shared ordinary/exact assertion invariant. */
1
+ /**
2
+ * @internal Shared ordinary/exact assertion invariant. `beforeArg` is the
3
+ * document's BSON encoding before the assertion runs; `encodeArg` encodes it
4
+ * again afterwards, and any difference is a mutation.
5
+ */
4
6
  export const assertBsonWithoutMutation = (
5
7
  documentArg: object,
8
+ beforeArg: Uint8Array,
9
+ encodeArg: (documentArg: object) => Uint8Array,
6
10
  assertionArg: (valueArg: unknown) => void,
7
11
  errorArg: (reasonArg: 'assertion' | 'bson' | 'mutation', causeArg?: unknown) => Error,
8
12
  ): void => {
9
- const before = plugins.mongodb.BSON.serialize(documentArg);
10
13
  try {
11
14
  const result = assertionArg(documentArg) as unknown;
12
15
  if (result !== undefined) {
@@ -17,9 +20,9 @@ export const assertBsonWithoutMutation = (
17
20
  }
18
21
  } catch (causeArg) { throw errorArg('assertion', causeArg); }
19
22
  let after: Uint8Array;
20
- try { after = plugins.mongodb.BSON.serialize(documentArg); }
23
+ try { after = encodeArg(documentArg); }
21
24
  catch (causeArg) { throw errorArg('bson', causeArg); }
22
- if (before.length !== after.length || before.some((valueArg, indexArg) => valueArg !== after[indexArg])) {
25
+ if (Buffer.compare(beforeArg, after) !== 0) {
23
26
  throw errorArg('mutation');
24
27
  }
25
28
  };
@@ -261,22 +261,19 @@ interface IExactModelConstructor<
261
261
  prototype: TModel;
262
262
  }
263
263
 
264
+ /** A proven stored row: a fresh canonical decoding and its body's canonical bytes. */
265
+ interface IExactStoredRow<TPersisted extends object> {
266
+ document: TStoredDocument<TPersisted>;
267
+ bodyBytes: Uint8Array;
268
+ }
269
+
264
270
  const exactCollectionCache = new WeakMap<
265
271
  object,
266
272
  WeakMap<object, SmartdataExactCollection<any, any>>
267
273
  >();
268
274
 
269
- const bytesEqual = (leftArg: Uint8Array, rightArg: Uint8Array): boolean => {
270
- if (leftArg.byteLength !== rightArg.byteLength) {
271
- return false;
272
- }
273
- for (let index = 0; index < leftArg.byteLength; index++) {
274
- if (leftArg[index] !== rightArg[index]) {
275
- return false;
276
- }
277
- }
278
- return true;
279
- };
275
+ const bytesEqual = (leftArg: Uint8Array, rightArg: Uint8Array): boolean =>
276
+ Buffer.compare(leftArg, rightArg) === 0;
280
277
 
281
278
  const hasOwn = (documentArg: object, keyArg: PropertyKey): boolean =>
282
279
  Object.prototype.hasOwnProperty.call(documentArg, keyArg);
@@ -1150,21 +1147,39 @@ export class SmartdataExactCollection<
1150
1147
  );
1151
1148
  }
1152
1149
 
1153
- private canonicalizeDocument<TDocument extends object>(
1154
- documentArg: TDocument,
1155
- labelArg: string,
1150
+ /** The collection's one BSON encoder: every encoding of a row passes here. */
1151
+ private encodeBson(documentArg: object): Uint8Array {
1152
+ return plugins.mongodb.BSON.serialize(documentArg);
1153
+ }
1154
+
1155
+ /** The collection's one BSON decoder: every decoding of a row passes here. */
1156
+ private decodeBson(bytesArg: Uint8Array): plugins.mongodb.Document {
1157
+ return plugins.mongodb.BSON.deserialize(bytesArg);
1158
+ }
1159
+
1160
+ /** Decodes proven canonical bytes into a fresh canonical document. */
1161
+ private decodeCanonical<TDocument extends object>(
1162
+ bytesArg: Uint8Array,
1156
1163
  ): TDocument {
1157
- let canonicalInput: TDocument;
1164
+ return canonicalizeValue(this.decodeBson(bytesArg) as TDocument);
1165
+ }
1166
+
1167
+ /**
1168
+ * The exactness proof: the document's sorted encoding must decode and
1169
+ * encode again to the same bytes. Returns those canonical bytes and the
1170
+ * decoding the proof produced, which no caller has seen yet.
1171
+ */
1172
+ private canonicalizeDocument(
1173
+ documentArg: object,
1174
+ labelArg: string,
1175
+ ): { document: Record<string, unknown>; bytes: Uint8Array } {
1158
1176
  let serialized: Uint8Array;
1159
- let canonical: TDocument;
1177
+ let canonical: Record<string, unknown>;
1160
1178
  let reserialized: Uint8Array;
1161
1179
  try {
1162
- canonicalInput = canonicalizeValue(documentArg);
1163
- serialized = plugins.mongodb.BSON.serialize(canonicalInput);
1164
- canonical = canonicalizeValue(
1165
- plugins.mongodb.BSON.deserialize(serialized) as TDocument,
1166
- );
1167
- reserialized = plugins.mongodb.BSON.serialize(canonical);
1180
+ serialized = this.encodeBson(canonicalizeValue(documentArg));
1181
+ canonical = canonicalizeValue(this.decodeBson(serialized));
1182
+ reserialized = this.encodeBson(canonical);
1168
1183
  } catch (errorArg) {
1169
1184
  throw new SmartdataExactPersistenceError(
1170
1185
  'invalid_document',
@@ -1178,7 +1193,23 @@ export class SmartdataExactCollection<
1178
1193
  `${labelArg} is not byte-stable across a BSON roundtrip.`,
1179
1194
  );
1180
1195
  }
1181
- return canonical;
1196
+ return { document: canonical, bytes: serialized };
1197
+ }
1198
+
1199
+ /**
1200
+ * Whether a document encodes canonically to bytes an earlier proof
1201
+ * produced. Equal bytes carry that proof and its assertion over, so the
1202
+ * caller needs neither again; on a mismatch the caller runs the full proof
1203
+ * to report what is wrong.
1204
+ */
1205
+ private encodesAs(documentArg: object, provenBytesArg: Uint8Array): boolean {
1206
+ let bytes: Uint8Array;
1207
+ try {
1208
+ bytes = this.encodeBson(canonicalizeValue(documentArg));
1209
+ } catch {
1210
+ return false;
1211
+ }
1212
+ return bytesEqual(bytes, provenBytesArg);
1182
1213
  }
1183
1214
 
1184
1215
  private assertAllowedTopLevelKeys(
@@ -1209,29 +1240,47 @@ export class SmartdataExactCollection<
1209
1240
  }
1210
1241
  }
1211
1242
 
1212
- private canonicalizePersistedDocument(
1213
- documentArg: object,
1214
- ): TExactPersistedDocument<TPersisted> {
1243
+ /**
1244
+ * Proves an application body canonical and runs the assertion once, on the
1245
+ * proof's own decoding, which is never handed out. Returns the canonical
1246
+ * bytes of the body.
1247
+ */
1248
+ private provePersistedDocument(documentArg: object): Uint8Array {
1215
1249
  this.assertAllowedTopLevelKeys(documentArg, [], 'Persisted document');
1216
- const canonical = this.canonicalizeDocument(
1250
+ const { document, bytes } = this.canonicalizeDocument(
1217
1251
  documentArg,
1218
1252
  'Persisted document',
1219
1253
  );
1220
- this.assertWithoutMutation(canonical, 'Persisted document');
1221
- return canonical as TExactPersistedDocument<TPersisted>;
1254
+ this.assertAllowedTopLevelKeys(document, [], 'Persisted document');
1255
+ this.assertWithoutMutation(document, bytes, 'Persisted document');
1256
+ return bytes;
1222
1257
  }
1223
1258
 
1224
- private canonicalizeStoredDocument(
1259
+ /**
1260
+ * Proves a stored envelope canonical, checks its storage metadata and runs
1261
+ * the assertion once on its body. The body is the proof's own decoding with
1262
+ * the metadata removed, so the assertion never sees a document a caller
1263
+ * receives. Returns the canonical bytes of the envelope and of its body.
1264
+ */
1265
+ private proveStoredDocument(
1225
1266
  documentArg: object,
1226
- ): TStoredDocument<TPersisted> {
1267
+ ): { bytes: Uint8Array; bodyBytes: Uint8Array } {
1227
1268
  this.assertAllowedTopLevelKeys(
1228
1269
  documentArg,
1229
1270
  storageMetadataKeys,
1230
1271
  'Stored document',
1231
1272
  );
1232
- const canonical = this.canonicalizeDocument(documentArg, 'Stored document');
1233
- const revision = (canonical as Record<string, unknown>)._smartdataRevision;
1234
- if (!isMongoObjectId((canonical as Record<string, unknown>)._id)) {
1273
+ const { document, bytes } = this.canonicalizeDocument(
1274
+ documentArg,
1275
+ 'Stored document',
1276
+ );
1277
+ this.assertAllowedTopLevelKeys(
1278
+ document,
1279
+ storageMetadataKeys,
1280
+ 'Stored document',
1281
+ );
1282
+ const revision = document._smartdataRevision;
1283
+ if (!isMongoObjectId(document._id)) {
1235
1284
  throw new SmartdataExactPersistenceError(
1236
1285
  'invalid_document',
1237
1286
  'Stored document _id must be a MongoDB ObjectId.',
@@ -1246,15 +1295,55 @@ export class SmartdataExactCollection<
1246
1295
  'Stored document _smartdataRevision must be a nonempty string when present.',
1247
1296
  );
1248
1297
  }
1249
- this.getPersistedBody(canonical as TStoredDocument<TPersisted>);
1250
- return canonical as TStoredDocument<TPersisted>;
1298
+ for (const key of storageMetadataKeys) {
1299
+ delete document[key];
1300
+ }
1301
+ const bodyBytes = this.encodeBson(document);
1302
+ this.assertWithoutMutation(document, bodyBytes, 'Persisted document');
1303
+ return { bytes, bodyBytes };
1304
+ }
1305
+
1306
+ /** Proves a stored envelope and returns a fresh canonical decoding of it. */
1307
+ private readStoredDocument(documentArg: object): IExactStoredRow<TPersisted> {
1308
+ const { bytes, bodyBytes } = this.proveStoredDocument(documentArg);
1309
+ return {
1310
+ document: this.decodeCanonical<TStoredDocument<TPersisted>>(bytes),
1311
+ bodyBytes,
1312
+ };
1313
+ }
1314
+
1315
+ /**
1316
+ * The canonical envelope of a proven body. BSON encodes every element on
1317
+ * its own, so a proven body with inert metadata is canonical as a whole;
1318
+ * only the canonical key order has to be restored.
1319
+ */
1320
+ private composeStoredDocument(
1321
+ bodyArg: TExactPersistedDocument<TPersisted>,
1322
+ metadataArg: { _id: plugins.mongodb.ObjectId; _smartdataRevision?: string },
1323
+ ): TStoredDocument<TPersisted> {
1324
+ const fields = new Map<string, unknown>(Object.entries(bodyArg));
1325
+ fields.set('_id', metadataArg._id);
1326
+ if (metadataArg._smartdataRevision !== undefined) {
1327
+ fields.set('_smartdataRevision', metadataArg._smartdataRevision);
1328
+ }
1329
+ const envelope = Object.create(null) as Record<string, unknown>;
1330
+ for (const key of [...fields.keys()].sort()) {
1331
+ envelope[key] = fields.get(key);
1332
+ }
1333
+ return envelope as TStoredDocument<TPersisted>;
1251
1334
  }
1252
1335
 
1253
1336
  private assertWithoutMutation(
1254
1337
  documentArg: object,
1338
+ beforeArg: Uint8Array,
1255
1339
  labelArg: string,
1256
1340
  ): void {
1257
- assertBsonWithoutMutation(documentArg, this.options.assertDocument, (reasonArg, causeArg) =>
1341
+ assertBsonWithoutMutation(
1342
+ documentArg,
1343
+ beforeArg,
1344
+ (documentToEncodeArg) => this.encodeBson(documentToEncodeArg),
1345
+ this.options.assertDocument,
1346
+ (reasonArg, causeArg) =>
1258
1347
  new SmartdataExactPersistenceError('invalid_document', `${labelArg} ${
1259
1348
  reasonArg === 'assertion' ? 'assertion failed.' :
1260
1349
  reasonArg === 'bson' ? 'validator produced a non-BSON document.' :
@@ -1262,46 +1351,8 @@ export class SmartdataExactCollection<
1262
1351
  }`, causeArg === undefined ? undefined : { cause: causeArg }));
1263
1352
  }
1264
1353
 
1265
- private getPersistedBody(
1266
- storedDocumentArg: TStoredDocument<TPersisted>,
1267
- ): TExactPersistedDocument<TPersisted> {
1268
- const body = Object.create(null) as Record<string, unknown>;
1269
- for (const key of Object.keys(storedDocumentArg)) {
1270
- if (!reservedKeys.includes(key as TReservedKey)) {
1271
- body[key] = (storedDocumentArg as Record<string, unknown>)[key];
1272
- }
1273
- }
1274
- return this.canonicalizePersistedDocument(body);
1275
- }
1276
-
1277
- private persistedDocumentsEqual(
1278
- leftArg: TExactPersistedDocument<TPersisted>,
1279
- rightArg: TExactPersistedDocument<TPersisted>,
1280
- ): boolean {
1281
- return bytesEqual(
1282
- plugins.mongodb.BSON.serialize(leftArg),
1283
- plugins.mongodb.BSON.serialize(rightArg),
1284
- );
1285
- }
1286
-
1287
- private storedDocumentsEqual(
1288
- leftArg: TStoredDocument<TPersisted>,
1289
- rightArg: TStoredDocument<TPersisted>,
1290
- ): boolean {
1291
- if (!leftArg._id.equals(rightArg._id)) {
1292
- return false;
1293
- }
1294
- if (leftArg._smartdataRevision !== rightArg._smartdataRevision) {
1295
- return false;
1296
- }
1297
- return this.persistedDocumentsEqual(
1298
- this.getPersistedBody(leftArg),
1299
- this.getPersistedBody(rightArg),
1300
- );
1301
- }
1302
-
1303
1354
  private validateReconcileIdentity(
1304
- documentArg: TExactPersistedDocument<TPersisted>,
1355
+ documentArg: TExactPersistedDocument<TPersisted> | TStoredDocument<TPersisted>,
1305
1356
  ): string {
1306
1357
  const value = (documentArg as Record<string, unknown>)[this.reconcileBy];
1307
1358
  if (typeof value !== 'string' || value.trim().length === 0) {
@@ -1313,9 +1364,10 @@ export class SmartdataExactCollection<
1313
1364
  return value;
1314
1365
  }
1315
1366
 
1367
+ /** The persisted body a model serializes to, before any proof. */
1316
1368
  private async serializeModel(
1317
1369
  modelArg: TModel,
1318
- ): Promise<TExactPersistedDocument<TPersisted>> {
1370
+ ): Promise<Record<string, unknown>> {
1319
1371
  const serialized = (await modelArg.createSavableObject()) as Record<
1320
1372
  string,
1321
1373
  unknown
@@ -1353,12 +1405,13 @@ export class SmartdataExactCollection<
1353
1405
  body[key] = serialized[key];
1354
1406
  }
1355
1407
  }
1356
- return this.canonicalizePersistedDocument(body);
1408
+ return body;
1357
1409
  }
1358
1410
 
1411
+ /** Proves an insert input and returns the canonical bytes of its body. */
1359
1412
  private async createPersistedDocument(
1360
1413
  documentArg: TModel | TExactPersistedDocument<TPersisted>,
1361
- ): Promise<TExactPersistedDocument<TPersisted>> {
1414
+ ): Promise<Uint8Array> {
1362
1415
  if (typeof documentArg !== 'object' || documentArg === null) {
1363
1416
  throw new SmartdataExactPersistenceError(
1364
1417
  'invalid_document',
@@ -1366,9 +1419,9 @@ export class SmartdataExactCollection<
1366
1419
  );
1367
1420
  }
1368
1421
  if (documentArg instanceof this.modelConstructor) {
1369
- return this.serializeModel(documentArg);
1422
+ return this.provePersistedDocument(await this.serializeModel(documentArg));
1370
1423
  }
1371
- return this.canonicalizePersistedDocument(documentArg);
1424
+ return this.provePersistedDocument(documentArg);
1372
1425
  }
1373
1426
 
1374
1427
  private hydrateStoredDocument(
@@ -1383,12 +1436,12 @@ export class SmartdataExactCollection<
1383
1436
  identityArg: string,
1384
1437
  sessionArg: plugins.mongodb.ClientSession | undefined,
1385
1438
  deadlineArg: SmartdataOperationDeadline,
1386
- ): Promise<TStoredDocument<TPersisted> | null> {
1439
+ ): Promise<IExactStoredRow<TPersisted> | null> {
1387
1440
  const rawDocument = await this.collection.mongoDbCollection.findOne({
1388
1441
  [this.reconcileBy]: identityArg,
1389
1442
  }, { session: sessionArg, ...deadlineArg.commandOptions() });
1390
1443
  return rawDocument
1391
- ? this.canonicalizeStoredDocument(rawDocument)
1444
+ ? this.readStoredDocument(rawDocument)
1392
1445
  : null;
1393
1446
  }
1394
1447
 
@@ -1438,12 +1491,10 @@ export class SmartdataExactCollection<
1438
1491
  currentDescriptor.value,
1439
1492
  'Exact delete current',
1440
1493
  );
1441
- const current = this.canonicalizeStoredDocument(
1494
+ const { document: current } = this.readStoredDocument(
1442
1495
  currentSnapshot as object,
1443
1496
  );
1444
- const identity = this.validateReconcileIdentity(
1445
- this.getPersistedBody(current),
1446
- );
1497
+ const identity = this.validateReconcileIdentity(current);
1447
1498
  return { current, identity };
1448
1499
  }
1449
1500
 
@@ -1454,7 +1505,7 @@ export class SmartdataExactCollection<
1454
1505
  sessionArg: plugins.mongodb.ClientSession | undefined,
1455
1506
  deadlineArg: SmartdataOperationDeadline,
1456
1507
  ): Promise<TExactDeleteResult> {
1457
- let reconciled: TStoredDocument<TPersisted> | null;
1508
+ let reconciled: IExactStoredRow<TPersisted> | null;
1458
1509
  try {
1459
1510
  reconciled = await this.findByReconcileIdentity(identityArg, sessionArg, deadlineArg);
1460
1511
  } catch (errorArg) {
@@ -1466,7 +1517,7 @@ export class SmartdataExactCollection<
1466
1517
  causeArg,
1467
1518
  );
1468
1519
  }
1469
- if (reconciled && reconciled._id.equals(currentArg._id)) {
1520
+ if (reconciled && reconciled.document._id.equals(currentArg._id)) {
1470
1521
  return { status: 'concurrent_change', deletedCount: 0 };
1471
1522
  }
1472
1523
  throw this.ambiguousWriteError(
@@ -1480,21 +1531,21 @@ export class SmartdataExactCollection<
1480
1531
  optionsArg: IExactBoundedOptions = {},
1481
1532
  ): Promise<TExactInsertResult<TPersisted>> {
1482
1533
  return this.runOperation(optionsArg, async (sessionArg, deadlineArg) => {
1483
- const persistedDocument = await this.createPersistedDocument(documentArg);
1534
+ const persistedBytes = await this.createPersistedDocument(documentArg);
1535
+ const persistedDocument = this.decodeCanonical<
1536
+ TExactPersistedDocument<TPersisted>
1537
+ >(persistedBytes);
1484
1538
  const identity = this.validateReconcileIdentity(persistedDocument);
1485
- const expectedStoredDocument = this.canonicalizeStoredDocument({
1486
- _id: new plugins.mongodb.ObjectId(),
1487
- ...persistedDocument,
1488
- });
1539
+ const expectedStoredDocument = this.composeStoredDocument(
1540
+ persistedDocument,
1541
+ { _id: new plugins.mongodb.ObjectId() },
1542
+ );
1489
1543
  if (hasActiveTransaction(sessionArg)) {
1490
1544
  const existing = await this.findByReconcileIdentity(identity, sessionArg, deadlineArg);
1491
1545
  if (existing) {
1492
- return this.persistedDocumentsEqual(
1493
- this.getPersistedBody(existing),
1494
- persistedDocument,
1495
- )
1496
- ? { status: 'already_committed', document: existing }
1497
- : { status: 'conflict', document: existing };
1546
+ return bytesEqual(existing.bodyBytes, persistedBytes)
1547
+ ? { status: 'already_committed', document: existing.document }
1548
+ : { status: 'conflict', document: existing.document };
1498
1549
  }
1499
1550
  }
1500
1551
 
@@ -1522,7 +1573,7 @@ export class SmartdataExactCollection<
1522
1573
  if (deadlineArg.interrupted(errorArg)) {
1523
1574
  throw this.interruptedWriteError('Exact insert', errorArg);
1524
1575
  }
1525
- let reconciled: TStoredDocument<TPersisted> | null;
1576
+ let reconciled: IExactStoredRow<TPersisted> | null;
1526
1577
  try {
1527
1578
  reconciled = await this.findByReconcileIdentity(identity, sessionArg, deadlineArg);
1528
1579
  } catch (reconciliationError) {
@@ -1542,16 +1593,11 @@ export class SmartdataExactCollection<
1542
1593
  );
1543
1594
  }
1544
1595
  if (reconciled) {
1545
- if (
1546
- this.persistedDocumentsEqual(
1547
- this.getPersistedBody(reconciled),
1548
- persistedDocument,
1549
- )
1550
- ) {
1551
- return { status: 'already_committed', document: reconciled };
1596
+ if (bytesEqual(reconciled.bodyBytes, persistedBytes)) {
1597
+ return { status: 'already_committed', document: reconciled.document };
1552
1598
  }
1553
1599
  if (isDuplicateKeyError(errorArg)) {
1554
- return { status: 'conflict', document: reconciled };
1600
+ return { status: 'conflict', document: reconciled.document };
1555
1601
  }
1556
1602
  }
1557
1603
  if (isDuplicateKeyError(errorArg)) {
@@ -1574,7 +1620,7 @@ export class SmartdataExactCollection<
1574
1620
  result,
1575
1621
  );
1576
1622
  }
1577
- let reconciled: TStoredDocument<TPersisted> | null;
1623
+ let reconciled: IExactStoredRow<TPersisted> | null;
1578
1624
  try {
1579
1625
  reconciled = await this.findByReconcileIdentity(identity, sessionArg, deadlineArg);
1580
1626
  } catch (errorArg) {
@@ -1586,14 +1632,8 @@ export class SmartdataExactCollection<
1586
1632
  result,
1587
1633
  );
1588
1634
  }
1589
- if (
1590
- reconciled &&
1591
- this.persistedDocumentsEqual(
1592
- this.getPersistedBody(reconciled),
1593
- persistedDocument,
1594
- )
1595
- ) {
1596
- return { status: 'already_committed', document: reconciled };
1635
+ if (reconciled && bytesEqual(reconciled.bodyBytes, persistedBytes)) {
1636
+ return { status: 'already_committed', document: reconciled.document };
1597
1637
  }
1598
1638
  throw this.ambiguousWriteError(
1599
1639
  'Exact insert was not acknowledged and could not be reconciled.',
@@ -1607,19 +1647,15 @@ export class SmartdataExactCollection<
1607
1647
  public toPersisted(
1608
1648
  storedDocumentArg: TStoredDocument<TPersisted>,
1609
1649
  ): TExactPersistedDocument<TPersisted> {
1610
- const storedDocument = this.canonicalizeStoredDocument(storedDocumentArg);
1611
- const persistedDocument = this.getPersistedBody(storedDocument);
1612
- return plugins.mongodb.BSON.deserialize(
1613
- plugins.mongodb.BSON.serialize(persistedDocument),
1614
- ) as TExactPersistedDocument<TPersisted>;
1650
+ const { bodyBytes } = this.proveStoredDocument(storedDocumentArg);
1651
+ // A plain decoding: ordinary application objects, not canonical ones.
1652
+ return this.decodeBson(bodyBytes) as TExactPersistedDocument<TPersisted>;
1615
1653
  }
1616
1654
 
1617
1655
  public canonicalBytes(
1618
1656
  persistedDocumentArg: TExactPersistedDocument<TPersisted>,
1619
1657
  ): Uint8Array {
1620
- return plugins.mongodb.BSON.serialize(
1621
- this.canonicalizePersistedDocument(persistedDocumentArg),
1622
- );
1658
+ return this.provePersistedDocument(persistedDocumentArg);
1623
1659
  }
1624
1660
 
1625
1661
  public async findStoredOne(
@@ -1645,7 +1681,7 @@ export class SmartdataExactCollection<
1645
1681
  try {
1646
1682
  const rawDocument = await cursor.next();
1647
1683
  storedDocument = rawDocument
1648
- ? this.canonicalizeStoredDocument(rawDocument)
1684
+ ? this.readStoredDocument(rawDocument).document
1649
1685
  : null;
1650
1686
  } catch (errorArg) {
1651
1687
  try {
@@ -1689,7 +1725,7 @@ export class SmartdataExactCollection<
1689
1725
  }
1690
1726
  const rawDocuments = await cursor.toArray();
1691
1727
  storedDocuments = rawDocuments.map((documentArg) =>
1692
- this.canonicalizeStoredDocument(documentArg),
1728
+ this.readStoredDocument(documentArg).document,
1693
1729
  );
1694
1730
  } catch (errorArg) {
1695
1731
  try {
@@ -1744,12 +1780,14 @@ export class SmartdataExactCollection<
1744
1780
  sessionOptionsArg: IExactBoundedOptions = {},
1745
1781
  ): Promise<TExactTransitionResult<TPersisted>> {
1746
1782
  return this.runOperation(sessionOptionsArg, async (sessionArg, deadlineArg) => {
1747
- const current = this.canonicalizeStoredDocument(optionsArg.current);
1748
- const currentBody = this.getPersistedBody(current);
1749
- const currentIdentity = this.validateReconcileIdentity(currentBody);
1783
+ const { document: current, bodyBytes: currentBodyBytes } =
1784
+ this.readStoredDocument(optionsArg.current);
1785
+ const currentIdentity = this.validateReconcileIdentity(current);
1750
1786
  const model = this.hydrateStoredDocument(current);
1751
1787
  const untouchedBody = await this.serializeModel(model);
1752
- if (!this.persistedDocumentsEqual(currentBody, untouchedBody)) {
1788
+ if (!this.encodesAs(untouchedBody, currentBodyBytes)) {
1789
+ // A body that is not even canonical reports its own proof failure.
1790
+ this.provePersistedDocument(untouchedBody);
1753
1791
  throw new SmartdataExactPersistenceError(
1754
1792
  'noncanonical_document',
1755
1793
  'Stored document does not survive an untouched model serialization roundtrip.',
@@ -1769,7 +1807,9 @@ export class SmartdataExactCollection<
1769
1807
  );
1770
1808
  }
1771
1809
 
1772
- const candidateBody = await this.serializeModel(model);
1810
+ const candidateBody = this.decodeCanonical<
1811
+ TExactPersistedDocument<TPersisted>
1812
+ >(this.provePersistedDocument(await this.serializeModel(model)));
1773
1813
  const candidateIdentity = this.validateReconcileIdentity(candidateBody);
1774
1814
  if (candidateIdentity !== currentIdentity) {
1775
1815
  throw new SmartdataExactPersistenceError(
@@ -1778,11 +1818,11 @@ export class SmartdataExactCollection<
1778
1818
  );
1779
1819
  }
1780
1820
  const newRevision = globalThis.crypto.randomUUID();
1781
- const expectedStoredDocument = this.canonicalizeStoredDocument({
1821
+ const expectedStoredDocument = this.composeStoredDocument(candidateBody, {
1782
1822
  _id: current._id,
1783
- ...candidateBody,
1784
1823
  _smartdataRevision: newRevision,
1785
1824
  });
1825
+ const expectedBytes = this.encodeBson(expectedStoredDocument);
1786
1826
  const selector = current._smartdataRevision !== undefined
1787
1827
  ? { _id: current._id, _smartdataRevision: current._smartdataRevision }
1788
1828
  : { _id: current._id, _smartdataRevision: { $exists: false } };
@@ -1803,14 +1843,15 @@ export class SmartdataExactCollection<
1803
1843
  if (!postimage) {
1804
1844
  return { status: 'concurrent_change' };
1805
1845
  }
1806
- const canonicalPostimage = this.canonicalizeStoredDocument(postimage);
1807
- if (!this.storedDocumentsEqual(canonicalPostimage, expectedStoredDocument)) {
1846
+ if (!this.encodesAs(postimage, expectedBytes)) {
1847
+ // A postimage that is not even a valid stored document reports why.
1848
+ this.proveStoredDocument(postimage);
1808
1849
  throw new SmartdataExactPersistenceError(
1809
1850
  'ambiguous_write',
1810
1851
  'Exact transition returned a postimage different from the requested state.',
1811
1852
  );
1812
1853
  }
1813
- return { status: 'transitioned', document: canonicalPostimage };
1854
+ return { status: 'transitioned', document: expectedStoredDocument };
1814
1855
  } catch (errorArg) {
1815
1856
  if (isTransientTransactionError(errorArg, sessionArg)) {
1816
1857
  throw errorArg;
@@ -1833,13 +1874,11 @@ export class SmartdataExactCollection<
1833
1874
  _id: current._id,
1834
1875
  }, { session: sessionArg, ...deadlineArg.commandOptions() });
1835
1876
  if (reconciledRaw) {
1836
- const reconciled = this.canonicalizeStoredDocument(reconciledRaw);
1837
- if (
1838
- reconciled._smartdataRevision === newRevision &&
1839
- this.storedDocumentsEqual(reconciled, expectedStoredDocument)
1840
- ) {
1841
- return { status: 'transitioned', document: reconciled };
1877
+ if (this.encodesAs(reconciledRaw, expectedBytes)) {
1878
+ return { status: 'transitioned', document: expectedStoredDocument };
1842
1879
  }
1880
+ // A malformed document fails the reconciliation itself.
1881
+ this.proveStoredDocument(reconciledRaw);
1843
1882
  }
1844
1883
  } catch (reconciliationError) {
1845
1884
  if (isTransientTransactionError(reconciliationError, sessionArg)) {