@haverstack/record-adapter-do-sqlite 0.17.0 → 0.18.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 } from '@haverstack/core';
1
+ import { StackRecord, JournalOptions, RecordChanges, RecordVersion, ActorOptions, TypeId, StackQuery, QueryResult, FileId, JournalQuery, RecordJournalEntry, StackType, Association } from '@haverstack/core';
2
2
  import { StackRecordAdapter, AdapterCapabilities } from '@haverstack/core/adapter';
3
3
 
4
4
  /**
@@ -64,47 +64,41 @@ declare abstract class SharedSqlRecordAdapter implements StackRecordAdapter {
64
64
  readonly timezone: string | undefined;
65
65
  private readonly record;
66
66
  protected constructor(exec: SqlExecutor, config: StackConfig);
67
- createRecord(record: StackRecord): Promise<StackRecord>;
67
+ createRecord(record: StackRecord, opts?: JournalOptions): Promise<StackRecord>;
68
68
  getRecord(id: string): Promise<StackRecord | null>;
69
69
  mutateRecord(id: string, changes: RecordChanges, opts?: {
70
70
  expectedVersion?: number;
71
71
  snapshot?: RecordVersion;
72
- } & ActorOptions): Promise<StackRecord>;
72
+ bumpsVersion?: boolean;
73
+ } & ActorOptions & JournalOptions): Promise<StackRecord>;
73
74
  deleteRecord(id: string, opts?: {
74
75
  hard?: boolean;
75
76
  expectedVersion?: number;
76
77
  snapshot?: RecordVersion;
77
- } & ActorOptions): Promise<StackRecord | null>;
78
+ } & ActorOptions & JournalOptions): Promise<StackRecord | null>;
78
79
  undeleteRecord(id: string, opts?: {
79
80
  expectedVersion?: number;
80
81
  snapshot?: RecordVersion;
81
- } & ActorOptions): Promise<StackRecord>;
82
+ } & ActorOptions & JournalOptions): Promise<StackRecord>;
82
83
  restoreVersion(id: string, version: number, opts?: {
83
84
  expectedVersion?: number;
84
85
  snapshot?: RecordVersion;
85
- } & {
86
- restoreAssociations?: boolean;
87
- } & ActorOptions): Promise<StackRecord>;
86
+ } & ActorOptions & JournalOptions): Promise<StackRecord>;
88
87
  commitMigration(id: string, toTypeId: TypeId, content: Record<string, unknown>, opts?: {
89
88
  expectedVersion?: number;
90
89
  snapshot?: RecordVersion;
91
- } & ActorOptions): Promise<StackRecord>;
90
+ } & ActorOptions & JournalOptions): Promise<StackRecord>;
92
91
  queryRecords(query: StackQuery): Promise<QueryResult>;
93
92
  deleteUnreferencedAttachmentRecords(fileId: FileId, metadataTypeIds: TypeId[]): Promise<StackRecord[]>;
94
93
  getVersions(id: string): Promise<RecordVersion[]>;
95
94
  getVersion(id: string, version: number): Promise<RecordVersion | null>;
96
95
  saveVersion(id: string, version: RecordVersion): Promise<void>;
96
+ getJournal(id: string, query?: JournalQuery): Promise<RecordJournalEntry[]>;
97
97
  saveType(type: StackType): Promise<void>;
98
98
  getType(id: TypeId): Promise<StackType | null>;
99
99
  listTypes(): Promise<StackType[]>;
100
- associate(recordId: string, association: Association, opts?: {
101
- expectedVersion?: number;
102
- snapshot?: RecordVersion;
103
- } & ActorOptions): Promise<StackRecord>;
104
- dissociate(recordId: string, association: Association, opts?: {
105
- expectedVersion?: number;
106
- snapshot?: RecordVersion;
107
- } & ActorOptions): Promise<StackRecord>;
100
+ associate(recordId: string, association: Association, opts?: JournalOptions): Promise<StackRecord>;
101
+ dissociate(recordId: string, association: Association, opts?: JournalOptions): Promise<StackRecord>;
108
102
  abstract flush(): Promise<void>;
109
103
  abstract close(): Promise<void>;
110
104
  }
package/dist/index.js CHANGED
@@ -50,6 +50,9 @@ var RECORD_SCHEMA_SQL = `
50
50
  -- parent_id holds the container the record sat in, NULL for the root --
51
51
  -- the same encoding records.parent_id uses, since a snapshot states
52
52
  -- where the record was rather than an instruction to apply.
53
+ -- No associations column: associate()/dissociate() don't bump version,
54
+ -- so no version ever snapshots the association set. See
55
+ -- docs/spec/versioning.md \xA7 Version history.
53
56
  CREATE TABLE IF NOT EXISTS versions (
54
57
  record_id TEXT NOT NULL REFERENCES records(id),
55
58
  version INTEGER NOT NULL,
@@ -60,11 +63,41 @@ var RECORD_SCHEMA_SQL = `
60
63
  updated_by TEXT,
61
64
  updated_via TEXT,
62
65
  parent_id TEXT,
63
- associations TEXT CHECK (associations IS NULL OR json_valid(associations)),
64
66
  permissions TEXT CHECK (permissions IS NULL OR json_valid(permissions)),
65
67
  PRIMARY KEY (record_id, version)
66
68
  ) STRICT;
67
69
 
70
+ -- The durable half of a change event: which aspects moved, who moved
71
+ -- them, and the association deltas nothing else retains. Envelope-level
72
+ -- by design \u2014 content lives on a versions row, and copying it here
73
+ -- would make the journal the larger of the two stores.
74
+ --
75
+ -- seq is dense from 1 per record and is the only ordering: at is wall
76
+ -- clock, and version stands still across an association change, so
77
+ -- neither orders the log alone. It is allocated inside the appending
78
+ -- write, which is why this table needs none of the collision healing
79
+ -- versions does. See docs/spec/versioning.md \xA7 The change journal.
80
+ CREATE TABLE IF NOT EXISTS journal (
81
+ record_id TEXT NOT NULL REFERENCES records(id),
82
+ seq INTEGER NOT NULL,
83
+ at INTEGER NOT NULL,
84
+ kind TEXT NOT NULL CHECK (kind IN ('created', 'changed', 'deleted', 'purged')),
85
+ ops TEXT NOT NULL CHECK (json_valid(ops)),
86
+ version INTEGER NOT NULL,
87
+ type_id TEXT NOT NULL,
88
+ parent_id TEXT,
89
+ -- NULL means the entry names no origin (the record did not move);
90
+ -- '' means it moved off the root. parent_id above needs no such
91
+ -- spelling: it is state, where this is a key that may be absent --
92
+ -- the same input/state split parentId follows everywhere.
93
+ previous_parent_id TEXT,
94
+ actor TEXT CHECK (actor IS NULL OR json_valid(actor)),
95
+ associations_added TEXT CHECK (associations_added IS NULL OR json_valid(associations_added)),
96
+ associations_removed TEXT CHECK (associations_removed IS NULL OR json_valid(associations_removed)),
97
+ associations_replaced TEXT CHECK (associations_replaced IS NULL OR json_valid(associations_replaced)),
98
+ PRIMARY KEY (record_id, seq)
99
+ ) STRICT;
100
+
68
101
  CREATE TABLE IF NOT EXISTS types (
69
102
  id TEXT PRIMARY KEY,
70
103
  base_id TEXT NOT NULL,
@@ -635,12 +668,37 @@ var rowToVersion = (row) => {
635
668
  v.updatedVia = row.updated_via;
636
669
  if (row.parent_id != null)
637
670
  v.parentId = row.parent_id;
638
- if (row.associations != null)
639
- v.associations = JSON.parse(row.associations);
640
671
  if (row.permissions != null)
641
672
  v.permissions = JSON.parse(row.permissions);
642
673
  return v;
643
674
  };
675
+ var rowToJournalEntry = (row) => {
676
+ const e = {
677
+ seq: row.seq,
678
+ at: fromMs(row.at),
679
+ kind: row.kind,
680
+ ops: JSON.parse(row.ops),
681
+ version: row.version,
682
+ typeId: row.type_id
683
+ };
684
+ if (row.parent_id != null)
685
+ e.parentId = row.parent_id;
686
+ if (row.previous_parent_id != null) {
687
+ e.previousParentId = row.previous_parent_id === "" ? null : row.previous_parent_id;
688
+ }
689
+ if (row.actor != null)
690
+ e.actor = JSON.parse(row.actor);
691
+ if (row.associations_added != null) {
692
+ e.associationsAdded = JSON.parse(row.associations_added);
693
+ }
694
+ if (row.associations_removed != null) {
695
+ e.associationsRemoved = JSON.parse(row.associations_removed);
696
+ }
697
+ if (row.associations_replaced != null) {
698
+ e.associationsReplaced = JSON.parse(row.associations_replaced);
699
+ }
700
+ return e;
701
+ };
644
702
 
645
703
  // ../sqlite-shared/dist/executor.js
646
704
  var isForeignKeyViolation = (err) => err instanceof Error && err.message.includes("FOREIGN KEY constraint failed");
@@ -679,7 +737,6 @@ var VERSION_COLUMNS = [
679
737
  "updated_by",
680
738
  "updated_via",
681
739
  "parent_id",
682
- "associations",
683
740
  "permissions"
684
741
  ];
685
742
  var versionRowValues = (version) => [
@@ -690,7 +747,6 @@ var versionRowValues = (version) => [
690
747
  version.updatedBy ?? null,
691
748
  version.updatedVia ?? null,
692
749
  version.parentId ?? null,
693
- version.associations ? JSON.stringify(version.associations) : null,
694
750
  version.permissions ? JSON.stringify(version.permissions) : null
695
751
  ];
696
752
  var indexedFieldKinds = (schema) => {
@@ -794,41 +850,51 @@ var SharedSqlRecordLogic = class {
794
850
  * A duplicate id hits the `records` PK — mapped to StackConflictError
795
851
  * instead of surfacing the raw engine exception, mirroring saveVersion's
796
852
  * collision mapping below.
853
+ *
854
+ * One transaction, because a record and the four tables that describe it
855
+ * are one fact. A half-applied create is worse than a failed one: the
856
+ * call raises, so the caller believes nothing landed, while the records
857
+ * row survives to fail their retry on the PK — and the row it leaves is
858
+ * invisible to `filter.search` and mis-ordered by `sort.contentField`
859
+ * until something writes it again.
797
860
  */
798
- async createRecord(record) {
799
- try {
800
- this.exec.run(`INSERT INTO records
861
+ async createRecord(record, opts = {}) {
862
+ this.exec.transaction(() => {
863
+ try {
864
+ this.exec.run(`INSERT INTO records
801
865
  (id, type_id, created_at, updated_at, content, version,
802
866
  parent_id, entity_id, app_id, principal_id, updated_by, updated_via,
803
867
  deleted_at, unlisted_at, permissions)
804
868
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
805
- record.id,
806
- record.typeId,
807
- toMs(record.createdAt),
808
- toMs(record.updatedAt),
809
- JSON.stringify(record.content),
810
- record.version,
811
- record.parentId ?? null,
812
- record.entityId ?? null,
813
- record.appId ?? null,
814
- record.principalId ?? null,
815
- record.updatedBy ?? null,
816
- record.updatedVia ?? null,
817
- record.deletedAt ? toMs(record.deletedAt) : null,
818
- record.unlistedAt ? toMs(record.unlistedAt) : null,
819
- record.permissions ? JSON.stringify(record.permissions) : null
820
- ]);
821
- } catch (err) {
822
- if (isUniqueConstraintViolation(err)) {
823
- throw new StackConflictError(`Record already exists: "${record.id}"`);
869
+ record.id,
870
+ record.typeId,
871
+ toMs(record.createdAt),
872
+ toMs(record.updatedAt),
873
+ JSON.stringify(record.content),
874
+ record.version,
875
+ record.parentId ?? null,
876
+ record.entityId ?? null,
877
+ record.appId ?? null,
878
+ record.principalId ?? null,
879
+ record.updatedBy ?? null,
880
+ record.updatedVia ?? null,
881
+ record.deletedAt ? toMs(record.deletedAt) : null,
882
+ record.unlistedAt ? toMs(record.unlistedAt) : null,
883
+ record.permissions ? JSON.stringify(record.permissions) : null
884
+ ]);
885
+ } catch (err) {
886
+ if (isUniqueConstraintViolation(err)) {
887
+ throw new StackConflictError(`Record already exists: "${record.id}"`);
888
+ }
889
+ throw err;
824
890
  }
825
- throw err;
826
- }
827
- if (record.associations?.length) {
828
- this.insertAssociations(record.id, record.associations);
829
- }
830
- fts5Strategy.insert(this.exec, record.id, JSON.stringify(record.content));
831
- this.syncContentIndex(record.id, record.typeId, record.content);
891
+ if (record.associations?.length) {
892
+ this.insertAssociations(record.id, record.associations);
893
+ }
894
+ fts5Strategy.insert(this.exec, record.id, JSON.stringify(record.content));
895
+ this.syncContentIndex(record.id, record.typeId, record.content);
896
+ this.appendJournal(record.id, opts.journal);
897
+ });
832
898
  return record;
833
899
  }
834
900
  async getRecord(id) {
@@ -861,17 +927,40 @@ var SharedSqlRecordLogic = class {
861
927
  * replaced when it carries one, and the snapshot written alongside — so
862
928
  * one call is one version whatever it moved.
863
929
  *
864
- * The expectedVersion check is standalone rather than folded into the
865
- * UPDATE alone, because a content change needs fts.remove() to run
866
- * *before* the records-table content changes. The guard is still in the
867
- * UPDATE's WHERE, so a writer that slipped in between the read and the
868
- * write is caught there rather than overwriting.
930
+ * `opts.bumpsVersion: false` is a change set that touches only
931
+ * `associations` — Stack computes this from which ops the set actually
932
+ * moves. Such a write skips the records-table UPDATE (and the snapshot)
933
+ * entirely: nothing on that row changes, so there is nothing to bump and
934
+ * no version to snapshot. `expectedVersion`, if given, is still
935
+ * re-checked synchronously inside the transaction — the CAS guard the
936
+ * bumping path gets from its UPDATE's WHERE clause, restated as a read
937
+ * because there is no UPDATE here to carry it. See
938
+ * docs/spec/versioning.md § Version history.
939
+ *
940
+ * The expectedVersion check for a bumping write is standalone rather than
941
+ * folded into the UPDATE alone, because a content change needs
942
+ * fts.remove() to run *before* the records-table content changes. The
943
+ * guard is still in the UPDATE's WHERE, so a writer that slipped in
944
+ * between the read and the write is caught there rather than overwriting.
869
945
  */
870
946
  async mutateRecord(id, changes, opts = {}) {
871
947
  const existing = await this.getRecord(id);
872
948
  if (!existing)
873
949
  throw new StackNotFoundError(`Record not found: "${id}"`);
874
950
  this.checkExpectedVersion(existing, opts.expectedVersion);
951
+ if (opts.bumpsVersion === false) {
952
+ this.exec.transaction(() => {
953
+ const current = this.readRecord(id);
954
+ if (!current)
955
+ throw new StackNotFoundError(`Record not found: "${id}"`);
956
+ this.checkExpectedVersion(current, opts.expectedVersion);
957
+ if (changes.associations !== void 0) {
958
+ this.replaceAssociations(id, changes.associations);
959
+ }
960
+ this.appendJournal(id, opts.journal);
961
+ });
962
+ return this.reread(id, "mutateRecord");
963
+ }
875
964
  const merged = changes.contentPatch ? applyMergePatch(existing.content, changes.contentPatch) : void 0;
876
965
  const sets = [];
877
966
  const values = [];
@@ -905,6 +994,7 @@ var SharedSqlRecordLogic = class {
905
994
  fts5Strategy.insert(this.exec, id, JSON.stringify(merged));
906
995
  this.syncContentIndex(id, existing.typeId, merged);
907
996
  }
997
+ this.appendJournal(id, opts.journal);
908
998
  });
909
999
  return this.reread(id, "mutateRecord");
910
1000
  }
@@ -917,6 +1007,7 @@ var SharedSqlRecordLogic = class {
917
1007
  if (opts.snapshot)
918
1008
  this.snapshotBeforeMutation(id, opts.snapshot);
919
1009
  this.versionedUpdate(id, ["deleted_at = ?"], [now], opts, now);
1010
+ this.appendJournal(id, opts.journal);
920
1011
  });
921
1012
  return this.reread(id, "deleteRecord");
922
1013
  }
@@ -940,6 +1031,7 @@ var SharedSqlRecordLogic = class {
940
1031
  fts5Strategy.remove(this.exec, id);
941
1032
  this.exec.run("DELETE FROM associations WHERE record_id = ?", [id]);
942
1033
  this.exec.run("DELETE FROM versions WHERE record_id = ?", [id]);
1034
+ this.exec.run("DELETE FROM journal WHERE record_id = ?", [id]);
943
1035
  this.exec.run("DELETE FROM content_index WHERE record_id = ?", [id]);
944
1036
  this.exec.run("DELETE FROM records WHERE id = ?", [id]);
945
1037
  return purged;
@@ -949,6 +1041,7 @@ var SharedSqlRecordLogic = class {
949
1041
  if (opts.snapshot)
950
1042
  this.snapshotBeforeMutation(id, opts.snapshot);
951
1043
  this.versionedUpdate(id, ["deleted_at = NULL"], [], opts);
1044
+ this.appendJournal(id, opts.journal);
952
1045
  });
953
1046
  return this.reread(id, "undelete");
954
1047
  }
@@ -964,9 +1057,7 @@ var SharedSqlRecordLogic = class {
964
1057
  if (opts.snapshot)
965
1058
  this.snapshotBeforeMutation(id, opts.snapshot);
966
1059
  this.rewriteContent(id, target.typeId, target.content, opts, ["parent_id = ?"], [target.parentId ?? null]);
967
- const applied = opts.restoreAssociations === false ? void 0 : target.associations;
968
- if (applied !== void 0)
969
- this.replaceAssociations(id, applied);
1060
+ this.appendJournal(id, opts.journal);
970
1061
  });
971
1062
  return this.reread(id, "restoreVersion");
972
1063
  }
@@ -979,6 +1070,7 @@ var SharedSqlRecordLogic = class {
979
1070
  if (opts.snapshot)
980
1071
  this.snapshotBeforeMutation(id, opts.snapshot);
981
1072
  this.rewriteContent(id, toTypeId, content, opts);
1073
+ this.appendJournal(id, opts.journal);
982
1074
  });
983
1075
  return this.reread(id, "commitMigration");
984
1076
  }
@@ -1121,6 +1213,59 @@ var SharedSqlRecordLogic = class {
1121
1213
  }
1122
1214
  }
1123
1215
  // -------------------------------------------------------
1216
+ // Change journal
1217
+ // -------------------------------------------------------
1218
+ /**
1219
+ * Append one entry, stamping the record-derived half off the row as it
1220
+ * now stands — so every caller must already sit inside the same
1221
+ * transaction as the write it describes, after that write has landed.
1222
+ *
1223
+ * `seq` is allocated here, from the log's own max, rather than computed
1224
+ * by the caller. That is what spares this table the collision healing
1225
+ * versions needs: nothing outside this statement ever holds a seq it
1226
+ * expects to be free. See docs/spec/versioning.md § The change journal.
1227
+ */
1228
+ appendJournal(recordId, entry) {
1229
+ if (!entry)
1230
+ return;
1231
+ const row = this.exec.get("SELECT version, type_id, parent_id FROM records WHERE id = ?", [recordId]);
1232
+ if (!row)
1233
+ throw new StackNotFoundError(`Record not found: "${recordId}"`);
1234
+ this.exec.run(`INSERT INTO journal
1235
+ (record_id, seq, at, kind, ops, version, type_id, parent_id,
1236
+ previous_parent_id, actor, associations_added, associations_removed,
1237
+ associations_replaced)
1238
+ VALUES (
1239
+ ?,
1240
+ (SELECT COALESCE(MAX(seq), 0) + 1 FROM journal WHERE record_id = ?),
1241
+ ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
1242
+ recordId,
1243
+ recordId,
1244
+ toMs(/* @__PURE__ */ new Date()),
1245
+ entry.kind,
1246
+ JSON.stringify(entry.ops),
1247
+ row.version,
1248
+ row.type_id,
1249
+ row.parent_id,
1250
+ // '' is the root; NULL is an entry that names no origin at all.
1251
+ entry.previousParentId === void 0 ? null : entry.previousParentId ?? "",
1252
+ entry.actor ? JSON.stringify(entry.actor) : null,
1253
+ entry.associationsAdded?.length ? JSON.stringify(entry.associationsAdded) : null,
1254
+ entry.associationsRemoved?.length ? JSON.stringify(entry.associationsRemoved) : null,
1255
+ entry.associationsReplaced?.length ? JSON.stringify(entry.associationsReplaced) : null
1256
+ ]);
1257
+ }
1258
+ async getJournal(id, query = {}) {
1259
+ const conditions = ["record_id = ?"];
1260
+ const values = [id];
1261
+ if (query.sinceSeq !== void 0) {
1262
+ conditions.push("seq > ?");
1263
+ values.push(query.sinceSeq);
1264
+ }
1265
+ 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);
1266
+ return rows.map(rowToJournalEntry);
1267
+ }
1268
+ // -------------------------------------------------------
1124
1269
  // Types
1125
1270
  // -------------------------------------------------------
1126
1271
  async saveType(type) {
@@ -1150,20 +1295,27 @@ var SharedSqlRecordLogic = class {
1150
1295
  // -------------------------------------------------------
1151
1296
  // Associations
1152
1297
  // -------------------------------------------------------
1298
+ /**
1299
+ * Never bumps `version`/`updatedAt` and never snapshots — a set-add
1300
+ * composes correctly regardless of write order, so it needs neither OCC
1301
+ * nor rollback history. The records row is untouched entirely; only the
1302
+ * associations table changes. See docs/spec/versioning.md § Version
1303
+ * history.
1304
+ */
1153
1305
  async associate(recordId, association, opts = {}) {
1154
1306
  this.exec.transaction(() => {
1155
- if (opts.snapshot)
1156
- this.snapshotBeforeMutation(recordId, opts.snapshot);
1157
- this.versionedUpdate(recordId, [], [], opts);
1307
+ if (!this.readRecord(recordId))
1308
+ throw new StackNotFoundError(`Record not found: "${recordId}"`);
1158
1309
  this.insertAssociations(recordId, [association]);
1310
+ this.appendJournal(recordId, opts.journal);
1159
1311
  });
1160
1312
  return this.reread(recordId, "associate");
1161
1313
  }
1314
+ /** Never bumps `version`/`updatedAt` — see associate(). */
1162
1315
  async dissociate(recordId, association, opts = {}) {
1163
1316
  this.exec.transaction(() => {
1164
- if (opts.snapshot)
1165
- this.snapshotBeforeMutation(recordId, opts.snapshot);
1166
- this.versionedUpdate(recordId, [], [], opts);
1317
+ if (!this.readRecord(recordId))
1318
+ throw new StackNotFoundError(`Record not found: "${recordId}"`);
1167
1319
  this.exec.run(`DELETE FROM associations
1168
1320
  WHERE record_id = ?
1169
1321
  AND kind = ?
@@ -1173,6 +1325,7 @@ var SharedSqlRecordLogic = class {
1173
1325
  AND related_id = ?
1174
1326
  AND related_ns = ?
1175
1327
  AND related_stack = ?`, [recordId, association.kind, association.label, ...associationKeyColumns(association)]);
1328
+ this.appendJournal(recordId, opts.journal);
1176
1329
  });
1177
1330
  return this.reread(recordId, "dissociate");
1178
1331
  }
@@ -1299,8 +1452,8 @@ var SharedSqlRecordAdapter = class {
1299
1452
  // -------------------------------------------------------
1300
1453
  // Records
1301
1454
  // -------------------------------------------------------
1302
- createRecord(record) {
1303
- return this.record.createRecord(record);
1455
+ createRecord(record, opts) {
1456
+ return this.record.createRecord(record, opts);
1304
1457
  }
1305
1458
  getRecord(id) {
1306
1459
  return this.record.getRecord(id);
@@ -1339,6 +1492,12 @@ var SharedSqlRecordAdapter = class {
1339
1492
  return this.record.saveVersion(id, version);
1340
1493
  }
1341
1494
  // -------------------------------------------------------
1495
+ // Change journal
1496
+ // -------------------------------------------------------
1497
+ getJournal(id, query) {
1498
+ return this.record.getJournal(id, query);
1499
+ }
1500
+ // -------------------------------------------------------
1342
1501
  // Types
1343
1502
  // -------------------------------------------------------
1344
1503
  saveType(type) {