@haverstack/record-adapter-sqlite 0.24.0 → 0.25.0

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/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { StackRecord, RecordChanges, RecordVersion, ActorOptions, TypeId, StackQuery, QueryResult, FileId, StackType, Association, TokenSession } from '@haverstack/core';
1
+ import { StackRecord, JournalOptions, RecordChanges, RecordVersion, ActorOptions, TypeId, StackQuery, QueryResult, FileId, JournalQuery, RecordJournalEntry, StackType, Association, TokenSession } from '@haverstack/core';
2
2
  import { StackRecordAdapter, AdapterCapabilities } from '@haverstack/core/adapter';
3
3
  import { StackTokenStore, TokenInfo } from '@haverstack/core/wire';
4
4
 
@@ -65,47 +65,41 @@ declare abstract class SharedSqlRecordAdapter implements StackRecordAdapter {
65
65
  readonly timezone: string | undefined;
66
66
  private readonly record;
67
67
  protected constructor(exec: SqlExecutor, config: StackConfig);
68
- createRecord(record: StackRecord): Promise<StackRecord>;
68
+ createRecord(record: StackRecord, opts?: JournalOptions): Promise<StackRecord>;
69
69
  getRecord(id: string): Promise<StackRecord | null>;
70
70
  mutateRecord(id: string, changes: RecordChanges, opts?: {
71
71
  expectedVersion?: number;
72
72
  snapshot?: RecordVersion;
73
- } & ActorOptions): Promise<StackRecord>;
73
+ bumpsVersion?: boolean;
74
+ } & ActorOptions & JournalOptions): Promise<StackRecord>;
74
75
  deleteRecord(id: string, opts?: {
75
76
  hard?: boolean;
76
77
  expectedVersion?: number;
77
78
  snapshot?: RecordVersion;
78
- } & ActorOptions): Promise<StackRecord | null>;
79
+ } & ActorOptions & JournalOptions): Promise<StackRecord | null>;
79
80
  undeleteRecord(id: string, opts?: {
80
81
  expectedVersion?: number;
81
82
  snapshot?: RecordVersion;
82
- } & ActorOptions): Promise<StackRecord>;
83
+ } & ActorOptions & JournalOptions): Promise<StackRecord>;
83
84
  restoreVersion(id: string, version: number, opts?: {
84
85
  expectedVersion?: number;
85
86
  snapshot?: RecordVersion;
86
- } & {
87
- restoreAssociations?: boolean;
88
- } & ActorOptions): Promise<StackRecord>;
87
+ } & ActorOptions & JournalOptions): Promise<StackRecord>;
89
88
  commitMigration(id: string, toTypeId: TypeId, content: Record<string, unknown>, opts?: {
90
89
  expectedVersion?: number;
91
90
  snapshot?: RecordVersion;
92
- } & ActorOptions): Promise<StackRecord>;
91
+ } & ActorOptions & JournalOptions): Promise<StackRecord>;
93
92
  queryRecords(query: StackQuery): Promise<QueryResult>;
94
93
  deleteUnreferencedAttachmentRecords(fileId: FileId, metadataTypeIds: TypeId[]): Promise<StackRecord[]>;
95
94
  getVersions(id: string): Promise<RecordVersion[]>;
96
95
  getVersion(id: string, version: number): Promise<RecordVersion | null>;
97
96
  saveVersion(id: string, version: RecordVersion): Promise<void>;
97
+ getJournal(id: string, query?: JournalQuery): Promise<RecordJournalEntry[]>;
98
98
  saveType(type: StackType): Promise<void>;
99
99
  getType(id: TypeId): Promise<StackType | null>;
100
100
  listTypes(): Promise<StackType[]>;
101
- associate(recordId: string, association: Association, opts?: {
102
- expectedVersion?: number;
103
- snapshot?: RecordVersion;
104
- } & ActorOptions): Promise<StackRecord>;
105
- dissociate(recordId: string, association: Association, opts?: {
106
- expectedVersion?: number;
107
- snapshot?: RecordVersion;
108
- } & ActorOptions): Promise<StackRecord>;
101
+ associate(recordId: string, association: Association, opts?: JournalOptions): Promise<StackRecord>;
102
+ dissociate(recordId: string, association: Association, opts?: JournalOptions): Promise<StackRecord>;
109
103
  abstract flush(): Promise<void>;
110
104
  abstract close(): Promise<void>;
111
105
  }
package/dist/index.js CHANGED
@@ -57,6 +57,9 @@ var RECORD_SCHEMA_SQL = `
57
57
  -- parent_id holds the container the record sat in, NULL for the root --
58
58
  -- the same encoding records.parent_id uses, since a snapshot states
59
59
  -- where the record was rather than an instruction to apply.
60
+ -- No associations column: associate()/dissociate() don't bump version,
61
+ -- so no version ever snapshots the association set. See
62
+ -- docs/spec/versioning.md \xA7 Version history.
60
63
  CREATE TABLE IF NOT EXISTS versions (
61
64
  record_id TEXT NOT NULL REFERENCES records(id),
62
65
  version INTEGER NOT NULL,
@@ -67,11 +70,41 @@ var RECORD_SCHEMA_SQL = `
67
70
  updated_by TEXT,
68
71
  updated_via TEXT,
69
72
  parent_id TEXT,
70
- associations TEXT CHECK (associations IS NULL OR json_valid(associations)),
71
73
  permissions TEXT CHECK (permissions IS NULL OR json_valid(permissions)),
72
74
  PRIMARY KEY (record_id, version)
73
75
  ) STRICT;
74
76
 
77
+ -- The durable half of a change event: which aspects moved, who moved
78
+ -- them, and the association deltas nothing else retains. Envelope-level
79
+ -- by design \u2014 content lives on a versions row, and copying it here
80
+ -- would make the journal the larger of the two stores.
81
+ --
82
+ -- seq is dense from 1 per record and is the only ordering: at is wall
83
+ -- clock, and version stands still across an association change, so
84
+ -- neither orders the log alone. It is allocated inside the appending
85
+ -- write, which is why this table needs none of the collision healing
86
+ -- versions does. See docs/spec/versioning.md \xA7 The change journal.
87
+ CREATE TABLE IF NOT EXISTS journal (
88
+ record_id TEXT NOT NULL REFERENCES records(id),
89
+ seq INTEGER NOT NULL,
90
+ at INTEGER NOT NULL,
91
+ kind TEXT NOT NULL CHECK (kind IN ('created', 'changed', 'deleted', 'purged')),
92
+ ops TEXT NOT NULL CHECK (json_valid(ops)),
93
+ version INTEGER NOT NULL,
94
+ type_id TEXT NOT NULL,
95
+ parent_id TEXT,
96
+ -- NULL means the entry names no origin (the record did not move);
97
+ -- '' means it moved off the root. parent_id above needs no such
98
+ -- spelling: it is state, where this is a key that may be absent --
99
+ -- the same input/state split parentId follows everywhere.
100
+ previous_parent_id TEXT,
101
+ actor TEXT CHECK (actor IS NULL OR json_valid(actor)),
102
+ associations_added TEXT CHECK (associations_added IS NULL OR json_valid(associations_added)),
103
+ associations_removed TEXT CHECK (associations_removed IS NULL OR json_valid(associations_removed)),
104
+ associations_replaced TEXT CHECK (associations_replaced IS NULL OR json_valid(associations_replaced)),
105
+ PRIMARY KEY (record_id, seq)
106
+ ) STRICT;
107
+
75
108
  CREATE TABLE IF NOT EXISTS types (
76
109
  id TEXT PRIMARY KEY,
77
110
  base_id TEXT NOT NULL,
@@ -655,12 +688,37 @@ var rowToVersion = (row) => {
655
688
  v.updatedVia = row.updated_via;
656
689
  if (row.parent_id != null)
657
690
  v.parentId = row.parent_id;
658
- if (row.associations != null)
659
- v.associations = JSON.parse(row.associations);
660
691
  if (row.permissions != null)
661
692
  v.permissions = JSON.parse(row.permissions);
662
693
  return v;
663
694
  };
695
+ var rowToJournalEntry = (row) => {
696
+ const e = {
697
+ seq: row.seq,
698
+ at: fromMs(row.at),
699
+ kind: row.kind,
700
+ ops: JSON.parse(row.ops),
701
+ version: row.version,
702
+ typeId: row.type_id
703
+ };
704
+ if (row.parent_id != null)
705
+ e.parentId = row.parent_id;
706
+ if (row.previous_parent_id != null) {
707
+ e.previousParentId = row.previous_parent_id === "" ? null : row.previous_parent_id;
708
+ }
709
+ if (row.actor != null)
710
+ e.actor = JSON.parse(row.actor);
711
+ if (row.associations_added != null) {
712
+ e.associationsAdded = JSON.parse(row.associations_added);
713
+ }
714
+ if (row.associations_removed != null) {
715
+ e.associationsRemoved = JSON.parse(row.associations_removed);
716
+ }
717
+ if (row.associations_replaced != null) {
718
+ e.associationsReplaced = JSON.parse(row.associations_replaced);
719
+ }
720
+ return e;
721
+ };
664
722
 
665
723
  // ../sqlite-shared/dist/executor.js
666
724
  var isForeignKeyViolation = (err) => err instanceof Error && err.message.includes("FOREIGN KEY constraint failed");
@@ -705,7 +763,6 @@ var VERSION_COLUMNS = [
705
763
  "updated_by",
706
764
  "updated_via",
707
765
  "parent_id",
708
- "associations",
709
766
  "permissions"
710
767
  ];
711
768
  var versionRowValues = (version) => [
@@ -716,7 +773,6 @@ var versionRowValues = (version) => [
716
773
  version.updatedBy ?? null,
717
774
  version.updatedVia ?? null,
718
775
  version.parentId ?? null,
719
- version.associations ? JSON.stringify(version.associations) : null,
720
776
  version.permissions ? JSON.stringify(version.permissions) : null
721
777
  ];
722
778
  var indexedFieldKinds = (schema) => {
@@ -820,41 +876,51 @@ var SharedSqlRecordLogic = class {
820
876
  * A duplicate id hits the `records` PK — mapped to StackConflictError
821
877
  * instead of surfacing the raw engine exception, mirroring saveVersion's
822
878
  * collision mapping below.
879
+ *
880
+ * One transaction, because a record and the four tables that describe it
881
+ * are one fact. A half-applied create is worse than a failed one: the
882
+ * call raises, so the caller believes nothing landed, while the records
883
+ * row survives to fail their retry on the PK — and the row it leaves is
884
+ * invisible to `filter.search` and mis-ordered by `sort.contentField`
885
+ * until something writes it again.
823
886
  */
824
- async createRecord(record) {
825
- try {
826
- this.exec.run(`INSERT INTO records
887
+ async createRecord(record, opts = {}) {
888
+ this.exec.transaction(() => {
889
+ try {
890
+ this.exec.run(`INSERT INTO records
827
891
  (id, type_id, created_at, updated_at, content, version,
828
892
  parent_id, entity_id, app_id, principal_id, updated_by, updated_via,
829
893
  deleted_at, unlisted_at, permissions)
830
894
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
831
- record.id,
832
- record.typeId,
833
- toMs(record.createdAt),
834
- toMs(record.updatedAt),
835
- JSON.stringify(record.content),
836
- record.version,
837
- record.parentId ?? null,
838
- record.entityId ?? null,
839
- record.appId ?? null,
840
- record.principalId ?? null,
841
- record.updatedBy ?? null,
842
- record.updatedVia ?? null,
843
- record.deletedAt ? toMs(record.deletedAt) : null,
844
- record.unlistedAt ? toMs(record.unlistedAt) : null,
845
- record.permissions ? JSON.stringify(record.permissions) : null
846
- ]);
847
- } catch (err) {
848
- if (isUniqueConstraintViolation(err)) {
849
- throw new StackConflictError(`Record already exists: "${record.id}"`);
895
+ record.id,
896
+ record.typeId,
897
+ toMs(record.createdAt),
898
+ toMs(record.updatedAt),
899
+ JSON.stringify(record.content),
900
+ record.version,
901
+ record.parentId ?? null,
902
+ record.entityId ?? null,
903
+ record.appId ?? null,
904
+ record.principalId ?? null,
905
+ record.updatedBy ?? null,
906
+ record.updatedVia ?? null,
907
+ record.deletedAt ? toMs(record.deletedAt) : null,
908
+ record.unlistedAt ? toMs(record.unlistedAt) : null,
909
+ record.permissions ? JSON.stringify(record.permissions) : null
910
+ ]);
911
+ } catch (err) {
912
+ if (isUniqueConstraintViolation(err)) {
913
+ throw new StackConflictError(`Record already exists: "${record.id}"`);
914
+ }
915
+ throw err;
850
916
  }
851
- throw err;
852
- }
853
- if (record.associations?.length) {
854
- this.insertAssociations(record.id, record.associations);
855
- }
856
- fts5Strategy.insert(this.exec, record.id, JSON.stringify(record.content));
857
- this.syncContentIndex(record.id, record.typeId, record.content);
917
+ if (record.associations?.length) {
918
+ this.insertAssociations(record.id, record.associations);
919
+ }
920
+ fts5Strategy.insert(this.exec, record.id, JSON.stringify(record.content));
921
+ this.syncContentIndex(record.id, record.typeId, record.content);
922
+ this.appendJournal(record.id, opts.journal);
923
+ });
858
924
  return record;
859
925
  }
860
926
  async getRecord(id) {
@@ -887,17 +953,40 @@ var SharedSqlRecordLogic = class {
887
953
  * replaced when it carries one, and the snapshot written alongside — so
888
954
  * one call is one version whatever it moved.
889
955
  *
890
- * The expectedVersion check is standalone rather than folded into the
891
- * UPDATE alone, because a content change needs fts.remove() to run
892
- * *before* the records-table content changes. The guard is still in the
893
- * UPDATE's WHERE, so a writer that slipped in between the read and the
894
- * write is caught there rather than overwriting.
956
+ * `opts.bumpsVersion: false` is a change set that touches only
957
+ * `associations` — Stack computes this from which ops the set actually
958
+ * moves. Such a write skips the records-table UPDATE (and the snapshot)
959
+ * entirely: nothing on that row changes, so there is nothing to bump and
960
+ * no version to snapshot. `expectedVersion`, if given, is still
961
+ * re-checked synchronously inside the transaction — the CAS guard the
962
+ * bumping path gets from its UPDATE's WHERE clause, restated as a read
963
+ * because there is no UPDATE here to carry it. See
964
+ * docs/spec/versioning.md § Version history.
965
+ *
966
+ * The expectedVersion check for a bumping write is standalone rather than
967
+ * folded into the UPDATE alone, because a content change needs
968
+ * fts.remove() to run *before* the records-table content changes. The
969
+ * guard is still in the UPDATE's WHERE, so a writer that slipped in
970
+ * between the read and the write is caught there rather than overwriting.
895
971
  */
896
972
  async mutateRecord(id, changes, opts = {}) {
897
973
  const existing = await this.getRecord(id);
898
974
  if (!existing)
899
975
  throw new StackNotFoundError(`Record not found: "${id}"`);
900
976
  this.checkExpectedVersion(existing, opts.expectedVersion);
977
+ if (opts.bumpsVersion === false) {
978
+ this.exec.transaction(() => {
979
+ const current = this.readRecord(id);
980
+ if (!current)
981
+ throw new StackNotFoundError(`Record not found: "${id}"`);
982
+ this.checkExpectedVersion(current, opts.expectedVersion);
983
+ if (changes.associations !== void 0) {
984
+ this.replaceAssociations(id, changes.associations);
985
+ }
986
+ this.appendJournal(id, opts.journal);
987
+ });
988
+ return this.reread(id, "mutateRecord");
989
+ }
901
990
  const merged = changes.contentPatch ? applyMergePatch(existing.content, changes.contentPatch) : void 0;
902
991
  const sets = [];
903
992
  const values = [];
@@ -931,6 +1020,7 @@ var SharedSqlRecordLogic = class {
931
1020
  fts5Strategy.insert(this.exec, id, JSON.stringify(merged));
932
1021
  this.syncContentIndex(id, existing.typeId, merged);
933
1022
  }
1023
+ this.appendJournal(id, opts.journal);
934
1024
  });
935
1025
  return this.reread(id, "mutateRecord");
936
1026
  }
@@ -943,6 +1033,7 @@ var SharedSqlRecordLogic = class {
943
1033
  if (opts.snapshot)
944
1034
  this.snapshotBeforeMutation(id, opts.snapshot);
945
1035
  this.versionedUpdate(id, ["deleted_at = ?"], [now], opts, now);
1036
+ this.appendJournal(id, opts.journal);
946
1037
  });
947
1038
  return this.reread(id, "deleteRecord");
948
1039
  }
@@ -966,6 +1057,7 @@ var SharedSqlRecordLogic = class {
966
1057
  fts5Strategy.remove(this.exec, id);
967
1058
  this.exec.run("DELETE FROM associations WHERE record_id = ?", [id]);
968
1059
  this.exec.run("DELETE FROM versions WHERE record_id = ?", [id]);
1060
+ this.exec.run("DELETE FROM journal WHERE record_id = ?", [id]);
969
1061
  this.exec.run("DELETE FROM content_index WHERE record_id = ?", [id]);
970
1062
  this.exec.run("DELETE FROM records WHERE id = ?", [id]);
971
1063
  return purged;
@@ -975,6 +1067,7 @@ var SharedSqlRecordLogic = class {
975
1067
  if (opts.snapshot)
976
1068
  this.snapshotBeforeMutation(id, opts.snapshot);
977
1069
  this.versionedUpdate(id, ["deleted_at = NULL"], [], opts);
1070
+ this.appendJournal(id, opts.journal);
978
1071
  });
979
1072
  return this.reread(id, "undelete");
980
1073
  }
@@ -990,9 +1083,7 @@ var SharedSqlRecordLogic = class {
990
1083
  if (opts.snapshot)
991
1084
  this.snapshotBeforeMutation(id, opts.snapshot);
992
1085
  this.rewriteContent(id, target.typeId, target.content, opts, ["parent_id = ?"], [target.parentId ?? null]);
993
- const applied = opts.restoreAssociations === false ? void 0 : target.associations;
994
- if (applied !== void 0)
995
- this.replaceAssociations(id, applied);
1086
+ this.appendJournal(id, opts.journal);
996
1087
  });
997
1088
  return this.reread(id, "restoreVersion");
998
1089
  }
@@ -1005,6 +1096,7 @@ var SharedSqlRecordLogic = class {
1005
1096
  if (opts.snapshot)
1006
1097
  this.snapshotBeforeMutation(id, opts.snapshot);
1007
1098
  this.rewriteContent(id, toTypeId, content, opts);
1099
+ this.appendJournal(id, opts.journal);
1008
1100
  });
1009
1101
  return this.reread(id, "commitMigration");
1010
1102
  }
@@ -1147,6 +1239,59 @@ var SharedSqlRecordLogic = class {
1147
1239
  }
1148
1240
  }
1149
1241
  // -------------------------------------------------------
1242
+ // Change journal
1243
+ // -------------------------------------------------------
1244
+ /**
1245
+ * Append one entry, stamping the record-derived half off the row as it
1246
+ * now stands — so every caller must already sit inside the same
1247
+ * transaction as the write it describes, after that write has landed.
1248
+ *
1249
+ * `seq` is allocated here, from the log's own max, rather than computed
1250
+ * by the caller. That is what spares this table the collision healing
1251
+ * versions needs: nothing outside this statement ever holds a seq it
1252
+ * expects to be free. See docs/spec/versioning.md § The change journal.
1253
+ */
1254
+ appendJournal(recordId, entry) {
1255
+ if (!entry)
1256
+ return;
1257
+ const row = this.exec.get("SELECT version, type_id, parent_id FROM records WHERE id = ?", [recordId]);
1258
+ if (!row)
1259
+ throw new StackNotFoundError(`Record not found: "${recordId}"`);
1260
+ this.exec.run(`INSERT INTO journal
1261
+ (record_id, seq, at, kind, ops, version, type_id, parent_id,
1262
+ previous_parent_id, actor, associations_added, associations_removed,
1263
+ associations_replaced)
1264
+ VALUES (
1265
+ ?,
1266
+ (SELECT COALESCE(MAX(seq), 0) + 1 FROM journal WHERE record_id = ?),
1267
+ ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
1268
+ recordId,
1269
+ recordId,
1270
+ toMs(/* @__PURE__ */ new Date()),
1271
+ entry.kind,
1272
+ JSON.stringify(entry.ops),
1273
+ row.version,
1274
+ row.type_id,
1275
+ row.parent_id,
1276
+ // '' is the root; NULL is an entry that names no origin at all.
1277
+ entry.previousParentId === void 0 ? null : entry.previousParentId ?? "",
1278
+ entry.actor ? JSON.stringify(entry.actor) : null,
1279
+ entry.associationsAdded?.length ? JSON.stringify(entry.associationsAdded) : null,
1280
+ entry.associationsRemoved?.length ? JSON.stringify(entry.associationsRemoved) : null,
1281
+ entry.associationsReplaced?.length ? JSON.stringify(entry.associationsReplaced) : null
1282
+ ]);
1283
+ }
1284
+ async getJournal(id, query = {}) {
1285
+ const conditions = ["record_id = ?"];
1286
+ const values = [id];
1287
+ if (query.sinceSeq !== void 0) {
1288
+ conditions.push("seq > ?");
1289
+ values.push(query.sinceSeq);
1290
+ }
1291
+ const rows = this.exec.all(`SELECT * FROM journal WHERE ${conditions.join(" AND ")} ORDER BY seq ASC${query.limit !== void 0 ? " LIMIT ?" : ""}`, query.limit !== void 0 ? [...values, query.limit] : values);
1292
+ return rows.map(rowToJournalEntry);
1293
+ }
1294
+ // -------------------------------------------------------
1150
1295
  // Types
1151
1296
  // -------------------------------------------------------
1152
1297
  async saveType(type) {
@@ -1176,20 +1321,27 @@ var SharedSqlRecordLogic = class {
1176
1321
  // -------------------------------------------------------
1177
1322
  // Associations
1178
1323
  // -------------------------------------------------------
1324
+ /**
1325
+ * Never bumps `version`/`updatedAt` and never snapshots — a set-add
1326
+ * composes correctly regardless of write order, so it needs neither OCC
1327
+ * nor rollback history. The records row is untouched entirely; only the
1328
+ * associations table changes. See docs/spec/versioning.md § Version
1329
+ * history.
1330
+ */
1179
1331
  async associate(recordId, association, opts = {}) {
1180
1332
  this.exec.transaction(() => {
1181
- if (opts.snapshot)
1182
- this.snapshotBeforeMutation(recordId, opts.snapshot);
1183
- this.versionedUpdate(recordId, [], [], opts);
1333
+ if (!this.readRecord(recordId))
1334
+ throw new StackNotFoundError(`Record not found: "${recordId}"`);
1184
1335
  this.insertAssociations(recordId, [association]);
1336
+ this.appendJournal(recordId, opts.journal);
1185
1337
  });
1186
1338
  return this.reread(recordId, "associate");
1187
1339
  }
1340
+ /** Never bumps `version`/`updatedAt` — see associate(). */
1188
1341
  async dissociate(recordId, association, opts = {}) {
1189
1342
  this.exec.transaction(() => {
1190
- if (opts.snapshot)
1191
- this.snapshotBeforeMutation(recordId, opts.snapshot);
1192
- this.versionedUpdate(recordId, [], [], opts);
1343
+ if (!this.readRecord(recordId))
1344
+ throw new StackNotFoundError(`Record not found: "${recordId}"`);
1193
1345
  this.exec.run(`DELETE FROM associations
1194
1346
  WHERE record_id = ?
1195
1347
  AND kind = ?
@@ -1199,6 +1351,7 @@ var SharedSqlRecordLogic = class {
1199
1351
  AND related_id = ?
1200
1352
  AND related_ns = ?
1201
1353
  AND related_stack = ?`, [recordId, association.kind, association.label, ...associationKeyColumns(association)]);
1354
+ this.appendJournal(recordId, opts.journal);
1202
1355
  });
1203
1356
  return this.reread(recordId, "dissociate");
1204
1357
  }
@@ -1325,8 +1478,8 @@ var SharedSqlRecordAdapter = class {
1325
1478
  // -------------------------------------------------------
1326
1479
  // Records
1327
1480
  // -------------------------------------------------------
1328
- createRecord(record) {
1329
- return this.record.createRecord(record);
1481
+ createRecord(record, opts) {
1482
+ return this.record.createRecord(record, opts);
1330
1483
  }
1331
1484
  getRecord(id) {
1332
1485
  return this.record.getRecord(id);
@@ -1365,6 +1518,12 @@ var SharedSqlRecordAdapter = class {
1365
1518
  return this.record.saveVersion(id, version);
1366
1519
  }
1367
1520
  // -------------------------------------------------------
1521
+ // Change journal
1522
+ // -------------------------------------------------------
1523
+ getJournal(id, query) {
1524
+ return this.record.getJournal(id, query);
1525
+ }
1526
+ // -------------------------------------------------------
1368
1527
  // Types
1369
1528
  // -------------------------------------------------------
1370
1529
  saveType(type) {