@haverstack/record-adapter-do-sqlite 0.17.0 → 0.19.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,39 @@ 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
+ -- See docs/spec/journal.md.
75
+ CREATE TABLE IF NOT EXISTS journal (
76
+ record_id TEXT NOT NULL REFERENCES records(id),
77
+ seq INTEGER NOT NULL,
78
+ at INTEGER NOT NULL,
79
+ -- No 'purged': a purge destroys the log rather than appending to it,
80
+ -- so the constraint names exactly the kinds an entry can carry.
81
+ -- See docs/spec/journal.md \xA7 A hard delete destroys the journal.
82
+ kind TEXT NOT NULL CHECK (kind IN ('created', 'changed', 'deleted')),
83
+ ops TEXT NOT NULL CHECK (json_valid(ops)),
84
+ version INTEGER NOT NULL,
85
+ type_id TEXT NOT NULL,
86
+ parent_id TEXT,
87
+ -- NULL means the entry names no origin (the record did not move);
88
+ -- '' means it moved off the root. parent_id above needs no such
89
+ -- spelling: it is state, where this is a key that may be absent --
90
+ -- the same input/state split parentId follows everywhere.
91
+ previous_parent_id TEXT,
92
+ actor TEXT CHECK (actor IS NULL OR json_valid(actor)),
93
+ -- One tagged edit per association the write moved, each carrying the
94
+ -- state it displaced. See docs/spec/journal.md \xA7 The entry.
95
+ associations TEXT CHECK (associations IS NULL OR json_valid(associations)),
96
+ PRIMARY KEY (record_id, seq)
97
+ ) STRICT;
98
+
68
99
  CREATE TABLE IF NOT EXISTS types (
69
100
  id TEXT PRIMARY KEY,
70
101
  base_id TEXT NOT NULL,
@@ -635,12 +666,30 @@ var rowToVersion = (row) => {
635
666
  v.updatedVia = row.updated_via;
636
667
  if (row.parent_id != null)
637
668
  v.parentId = row.parent_id;
638
- if (row.associations != null)
639
- v.associations = JSON.parse(row.associations);
640
669
  if (row.permissions != null)
641
670
  v.permissions = JSON.parse(row.permissions);
642
671
  return v;
643
672
  };
673
+ var rowToJournalEntry = (row) => {
674
+ const e = {
675
+ seq: row.seq,
676
+ at: fromMs(row.at),
677
+ kind: row.kind,
678
+ ops: JSON.parse(row.ops),
679
+ version: row.version,
680
+ typeId: row.type_id
681
+ };
682
+ if (row.parent_id != null)
683
+ e.parentId = row.parent_id;
684
+ if (row.previous_parent_id != null) {
685
+ e.previousParentId = row.previous_parent_id === "" ? null : row.previous_parent_id;
686
+ }
687
+ if (row.actor != null)
688
+ e.actor = JSON.parse(row.actor);
689
+ if (row.associations != null)
690
+ e.associations = JSON.parse(row.associations);
691
+ return e;
692
+ };
644
693
 
645
694
  // ../sqlite-shared/dist/executor.js
646
695
  var isForeignKeyViolation = (err) => err instanceof Error && err.message.includes("FOREIGN KEY constraint failed");
@@ -679,7 +728,6 @@ var VERSION_COLUMNS = [
679
728
  "updated_by",
680
729
  "updated_via",
681
730
  "parent_id",
682
- "associations",
683
731
  "permissions"
684
732
  ];
685
733
  var versionRowValues = (version) => [
@@ -690,7 +738,6 @@ var versionRowValues = (version) => [
690
738
  version.updatedBy ?? null,
691
739
  version.updatedVia ?? null,
692
740
  version.parentId ?? null,
693
- version.associations ? JSON.stringify(version.associations) : null,
694
741
  version.permissions ? JSON.stringify(version.permissions) : null
695
742
  ];
696
743
  var indexedFieldKinds = (schema) => {
@@ -794,41 +841,51 @@ var SharedSqlRecordLogic = class {
794
841
  * A duplicate id hits the `records` PK — mapped to StackConflictError
795
842
  * instead of surfacing the raw engine exception, mirroring saveVersion's
796
843
  * collision mapping below.
844
+ *
845
+ * One transaction, because a record and the four tables that describe it
846
+ * are one fact. A half-applied create is worse than a failed one: the
847
+ * call raises, so the caller believes nothing landed, while the records
848
+ * row survives to fail their retry on the PK — and the row it leaves is
849
+ * invisible to `filter.search` and mis-ordered by `sort.contentField`
850
+ * until something writes it again.
797
851
  */
798
- async createRecord(record) {
799
- try {
800
- this.exec.run(`INSERT INTO records
852
+ async createRecord(record, opts = {}) {
853
+ this.exec.transaction(() => {
854
+ try {
855
+ this.exec.run(`INSERT INTO records
801
856
  (id, type_id, created_at, updated_at, content, version,
802
857
  parent_id, entity_id, app_id, principal_id, updated_by, updated_via,
803
858
  deleted_at, unlisted_at, permissions)
804
859
  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}"`);
860
+ record.id,
861
+ record.typeId,
862
+ toMs(record.createdAt),
863
+ toMs(record.updatedAt),
864
+ JSON.stringify(record.content),
865
+ record.version,
866
+ record.parentId ?? null,
867
+ record.entityId ?? null,
868
+ record.appId ?? null,
869
+ record.principalId ?? null,
870
+ record.updatedBy ?? null,
871
+ record.updatedVia ?? null,
872
+ record.deletedAt ? toMs(record.deletedAt) : null,
873
+ record.unlistedAt ? toMs(record.unlistedAt) : null,
874
+ record.permissions ? JSON.stringify(record.permissions) : null
875
+ ]);
876
+ } catch (err) {
877
+ if (isUniqueConstraintViolation(err)) {
878
+ throw new StackConflictError(`Record already exists: "${record.id}"`);
879
+ }
880
+ throw err;
824
881
  }
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);
882
+ if (record.associations?.length) {
883
+ this.insertAssociations(record.id, record.associations);
884
+ }
885
+ fts5Strategy.insert(this.exec, record.id, JSON.stringify(record.content));
886
+ this.syncContentIndex(record.id, record.typeId, record.content);
887
+ this.appendJournal(record.id, opts.journal);
888
+ });
832
889
  return record;
833
890
  }
834
891
  async getRecord(id) {
@@ -861,17 +918,40 @@ var SharedSqlRecordLogic = class {
861
918
  * replaced when it carries one, and the snapshot written alongside — so
862
919
  * one call is one version whatever it moved.
863
920
  *
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.
921
+ * `opts.bumpsVersion: false` is a change set that touches only
922
+ * `associations` — Stack computes this from which ops the set actually
923
+ * moves. Such a write skips the records-table UPDATE (and the snapshot)
924
+ * entirely: nothing on that row changes, so there is nothing to bump and
925
+ * no version to snapshot. `expectedVersion`, if given, is still
926
+ * re-checked synchronously inside the transaction — the CAS guard the
927
+ * bumping path gets from its UPDATE's WHERE clause, restated as a read
928
+ * because there is no UPDATE here to carry it. See
929
+ * docs/spec/versioning.md § Version history.
930
+ *
931
+ * The expectedVersion check for a bumping write is standalone rather than
932
+ * folded into the UPDATE alone, because a content change needs
933
+ * fts.remove() to run *before* the records-table content changes. The
934
+ * guard is still in the UPDATE's WHERE, so a writer that slipped in
935
+ * between the read and the write is caught there rather than overwriting.
869
936
  */
870
937
  async mutateRecord(id, changes, opts = {}) {
871
938
  const existing = await this.getRecord(id);
872
939
  if (!existing)
873
940
  throw new StackNotFoundError(`Record not found: "${id}"`);
874
941
  this.checkExpectedVersion(existing, opts.expectedVersion);
942
+ if (opts.bumpsVersion === false) {
943
+ this.exec.transaction(() => {
944
+ const current = this.readRecord(id);
945
+ if (!current)
946
+ throw new StackNotFoundError(`Record not found: "${id}"`);
947
+ this.checkExpectedVersion(current, opts.expectedVersion);
948
+ if (changes.associations !== void 0) {
949
+ this.replaceAssociations(id, changes.associations);
950
+ }
951
+ this.appendJournal(id, opts.journal);
952
+ });
953
+ return this.reread(id, "mutateRecord");
954
+ }
875
955
  const merged = changes.contentPatch ? applyMergePatch(existing.content, changes.contentPatch) : void 0;
876
956
  const sets = [];
877
957
  const values = [];
@@ -905,6 +985,7 @@ var SharedSqlRecordLogic = class {
905
985
  fts5Strategy.insert(this.exec, id, JSON.stringify(merged));
906
986
  this.syncContentIndex(id, existing.typeId, merged);
907
987
  }
988
+ this.appendJournal(id, opts.journal);
908
989
  });
909
990
  return this.reread(id, "mutateRecord");
910
991
  }
@@ -917,6 +998,7 @@ var SharedSqlRecordLogic = class {
917
998
  if (opts.snapshot)
918
999
  this.snapshotBeforeMutation(id, opts.snapshot);
919
1000
  this.versionedUpdate(id, ["deleted_at = ?"], [now], opts, now);
1001
+ this.appendJournal(id, opts.journal);
920
1002
  });
921
1003
  return this.reread(id, "deleteRecord");
922
1004
  }
@@ -940,6 +1022,7 @@ var SharedSqlRecordLogic = class {
940
1022
  fts5Strategy.remove(this.exec, id);
941
1023
  this.exec.run("DELETE FROM associations WHERE record_id = ?", [id]);
942
1024
  this.exec.run("DELETE FROM versions WHERE record_id = ?", [id]);
1025
+ this.exec.run("DELETE FROM journal WHERE record_id = ?", [id]);
943
1026
  this.exec.run("DELETE FROM content_index WHERE record_id = ?", [id]);
944
1027
  this.exec.run("DELETE FROM records WHERE id = ?", [id]);
945
1028
  return purged;
@@ -949,36 +1032,36 @@ var SharedSqlRecordLogic = class {
949
1032
  if (opts.snapshot)
950
1033
  this.snapshotBeforeMutation(id, opts.snapshot);
951
1034
  this.versionedUpdate(id, ["deleted_at = NULL"], [], opts);
1035
+ this.appendJournal(id, opts.journal);
952
1036
  });
953
1037
  return this.reread(id, "undelete");
954
1038
  }
955
1039
  async restoreVersion(id, version, opts = {}) {
956
1040
  const existing = await this.getRecord(id);
957
1041
  if (!existing)
958
- throw new Error(`Record not found: "${id}"`);
1042
+ throw new StackNotFoundError(`Record not found: "${id}"`);
959
1043
  this.checkExpectedVersion(existing, opts.expectedVersion);
960
1044
  const target = await this.getVersion(id, version);
961
1045
  if (!target)
962
- throw new Error(`Version not found: ${id}@${version}`);
1046
+ throw new StackNotFoundError(`Version not found: "${id}"@${version}`);
963
1047
  this.exec.transaction(() => {
964
1048
  if (opts.snapshot)
965
1049
  this.snapshotBeforeMutation(id, opts.snapshot);
966
1050
  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);
1051
+ this.appendJournal(id, opts.journal);
970
1052
  });
971
1053
  return this.reread(id, "restoreVersion");
972
1054
  }
973
1055
  async commitMigration(id, toTypeId, content, opts = {}) {
974
1056
  const existing = await this.getRecord(id);
975
1057
  if (!existing)
976
- throw new Error(`Record not found: "${id}"`);
1058
+ throw new StackNotFoundError(`Record not found: "${id}"`);
977
1059
  this.checkExpectedVersion(existing, opts.expectedVersion);
978
1060
  this.exec.transaction(() => {
979
1061
  if (opts.snapshot)
980
1062
  this.snapshotBeforeMutation(id, opts.snapshot);
981
1063
  this.rewriteContent(id, toTypeId, content, opts);
1064
+ this.appendJournal(id, opts.journal);
982
1065
  });
983
1066
  return this.reread(id, "commitMigration");
984
1067
  }
@@ -1121,6 +1204,59 @@ var SharedSqlRecordLogic = class {
1121
1204
  }
1122
1205
  }
1123
1206
  // -------------------------------------------------------
1207
+ // Change journal
1208
+ // -------------------------------------------------------
1209
+ /**
1210
+ * Append one entry, stamping the record-derived half off the row as it
1211
+ * now stands — so every caller must already sit inside the same
1212
+ * transaction as the write it describes, after that write has landed.
1213
+ *
1214
+ * `seq` is allocated here, from the log's own max, rather than computed
1215
+ * by the caller. That is what spares this table the collision healing
1216
+ * versions needs: nothing outside this statement ever holds a seq it
1217
+ * expects to be free. See docs/spec/journal.md § Ordering.
1218
+ */
1219
+ appendJournal(recordId, entry) {
1220
+ if (!entry)
1221
+ return;
1222
+ const row = this.exec.get("SELECT version, type_id, parent_id FROM records WHERE id = ?", [recordId]);
1223
+ if (!row)
1224
+ throw new StackNotFoundError(`Record not found: "${recordId}"`);
1225
+ this.exec.run(`INSERT INTO journal
1226
+ (record_id, seq, at, kind, ops, version, type_id, parent_id,
1227
+ previous_parent_id, actor, associations)
1228
+ VALUES (
1229
+ ?,
1230
+ (SELECT COALESCE(MAX(seq), 0) + 1 FROM journal WHERE record_id = ?),
1231
+ ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
1232
+ recordId,
1233
+ recordId,
1234
+ toMs(/* @__PURE__ */ new Date()),
1235
+ entry.kind,
1236
+ JSON.stringify(entry.ops),
1237
+ row.version,
1238
+ row.type_id,
1239
+ row.parent_id,
1240
+ // '' is the root; NULL is an entry that names no origin at all.
1241
+ entry.previousParentId === void 0 ? null : entry.previousParentId ?? "",
1242
+ entry.actor ? JSON.stringify(entry.actor) : null,
1243
+ entry.associations?.length ? JSON.stringify(entry.associations) : null
1244
+ ]);
1245
+ }
1246
+ async getJournal(id, query = {}) {
1247
+ if (!this.exec.get("SELECT 1 FROM records WHERE id = ?", [id])) {
1248
+ throw new StackNotFoundError(`Record not found: "${id}"`);
1249
+ }
1250
+ const conditions = ["record_id = ?"];
1251
+ const values = [id];
1252
+ if (query.sinceSeq !== void 0) {
1253
+ conditions.push("seq > ?");
1254
+ values.push(query.sinceSeq);
1255
+ }
1256
+ 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);
1257
+ return rows.map(rowToJournalEntry);
1258
+ }
1259
+ // -------------------------------------------------------
1124
1260
  // Types
1125
1261
  // -------------------------------------------------------
1126
1262
  async saveType(type) {
@@ -1150,20 +1286,27 @@ var SharedSqlRecordLogic = class {
1150
1286
  // -------------------------------------------------------
1151
1287
  // Associations
1152
1288
  // -------------------------------------------------------
1289
+ /**
1290
+ * Never bumps `version`/`updatedAt` and never snapshots — a set-add
1291
+ * composes correctly regardless of write order, so it needs neither OCC
1292
+ * nor rollback history. The records row is untouched entirely; only the
1293
+ * associations table changes. See docs/spec/versioning.md § Version
1294
+ * history.
1295
+ */
1153
1296
  async associate(recordId, association, opts = {}) {
1154
1297
  this.exec.transaction(() => {
1155
- if (opts.snapshot)
1156
- this.snapshotBeforeMutation(recordId, opts.snapshot);
1157
- this.versionedUpdate(recordId, [], [], opts);
1298
+ if (!this.readRecord(recordId))
1299
+ throw new StackNotFoundError(`Record not found: "${recordId}"`);
1158
1300
  this.insertAssociations(recordId, [association]);
1301
+ this.appendJournal(recordId, opts.journal);
1159
1302
  });
1160
1303
  return this.reread(recordId, "associate");
1161
1304
  }
1305
+ /** Never bumps `version`/`updatedAt` — see associate(). */
1162
1306
  async dissociate(recordId, association, opts = {}) {
1163
1307
  this.exec.transaction(() => {
1164
- if (opts.snapshot)
1165
- this.snapshotBeforeMutation(recordId, opts.snapshot);
1166
- this.versionedUpdate(recordId, [], [], opts);
1308
+ if (!this.readRecord(recordId))
1309
+ throw new StackNotFoundError(`Record not found: "${recordId}"`);
1167
1310
  this.exec.run(`DELETE FROM associations
1168
1311
  WHERE record_id = ?
1169
1312
  AND kind = ?
@@ -1173,6 +1316,7 @@ var SharedSqlRecordLogic = class {
1173
1316
  AND related_id = ?
1174
1317
  AND related_ns = ?
1175
1318
  AND related_stack = ?`, [recordId, association.kind, association.label, ...associationKeyColumns(association)]);
1319
+ this.appendJournal(recordId, opts.journal);
1176
1320
  });
1177
1321
  return this.reread(recordId, "dissociate");
1178
1322
  }
@@ -1299,8 +1443,8 @@ var SharedSqlRecordAdapter = class {
1299
1443
  // -------------------------------------------------------
1300
1444
  // Records
1301
1445
  // -------------------------------------------------------
1302
- createRecord(record) {
1303
- return this.record.createRecord(record);
1446
+ createRecord(record, opts) {
1447
+ return this.record.createRecord(record, opts);
1304
1448
  }
1305
1449
  getRecord(id) {
1306
1450
  return this.record.getRecord(id);
@@ -1339,6 +1483,12 @@ var SharedSqlRecordAdapter = class {
1339
1483
  return this.record.saveVersion(id, version);
1340
1484
  }
1341
1485
  // -------------------------------------------------------
1486
+ // Change journal
1487
+ // -------------------------------------------------------
1488
+ getJournal(id, query) {
1489
+ return this.record.getJournal(id, query);
1490
+ }
1491
+ // -------------------------------------------------------
1342
1492
  // Types
1343
1493
  // -------------------------------------------------------
1344
1494
  saveType(type) {