@haverstack/record-adapter-sqlite 0.24.0 → 0.26.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,39 @@ 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
+ -- See docs/spec/journal.md.
82
+ CREATE TABLE IF NOT EXISTS journal (
83
+ record_id TEXT NOT NULL REFERENCES records(id),
84
+ seq INTEGER NOT NULL,
85
+ at INTEGER NOT NULL,
86
+ -- No 'purged': a purge destroys the log rather than appending to it,
87
+ -- so the constraint names exactly the kinds an entry can carry.
88
+ -- See docs/spec/journal.md \xA7 A hard delete destroys the journal.
89
+ kind TEXT NOT NULL CHECK (kind IN ('created', 'changed', 'deleted')),
90
+ ops TEXT NOT NULL CHECK (json_valid(ops)),
91
+ version INTEGER NOT NULL,
92
+ type_id TEXT NOT NULL,
93
+ parent_id TEXT,
94
+ -- NULL means the entry names no origin (the record did not move);
95
+ -- '' means it moved off the root. parent_id above needs no such
96
+ -- spelling: it is state, where this is a key that may be absent --
97
+ -- the same input/state split parentId follows everywhere.
98
+ previous_parent_id TEXT,
99
+ actor TEXT CHECK (actor IS NULL OR json_valid(actor)),
100
+ -- One tagged edit per association the write moved, each carrying the
101
+ -- state it displaced. See docs/spec/journal.md \xA7 The entry.
102
+ associations TEXT CHECK (associations IS NULL OR json_valid(associations)),
103
+ PRIMARY KEY (record_id, seq)
104
+ ) STRICT;
105
+
75
106
  CREATE TABLE IF NOT EXISTS types (
76
107
  id TEXT PRIMARY KEY,
77
108
  base_id TEXT NOT NULL,
@@ -655,12 +686,30 @@ var rowToVersion = (row) => {
655
686
  v.updatedVia = row.updated_via;
656
687
  if (row.parent_id != null)
657
688
  v.parentId = row.parent_id;
658
- if (row.associations != null)
659
- v.associations = JSON.parse(row.associations);
660
689
  if (row.permissions != null)
661
690
  v.permissions = JSON.parse(row.permissions);
662
691
  return v;
663
692
  };
693
+ var rowToJournalEntry = (row) => {
694
+ const e = {
695
+ seq: row.seq,
696
+ at: fromMs(row.at),
697
+ kind: row.kind,
698
+ ops: JSON.parse(row.ops),
699
+ version: row.version,
700
+ typeId: row.type_id
701
+ };
702
+ if (row.parent_id != null)
703
+ e.parentId = row.parent_id;
704
+ if (row.previous_parent_id != null) {
705
+ e.previousParentId = row.previous_parent_id === "" ? null : row.previous_parent_id;
706
+ }
707
+ if (row.actor != null)
708
+ e.actor = JSON.parse(row.actor);
709
+ if (row.associations != null)
710
+ e.associations = JSON.parse(row.associations);
711
+ return e;
712
+ };
664
713
 
665
714
  // ../sqlite-shared/dist/executor.js
666
715
  var isForeignKeyViolation = (err) => err instanceof Error && err.message.includes("FOREIGN KEY constraint failed");
@@ -705,7 +754,6 @@ var VERSION_COLUMNS = [
705
754
  "updated_by",
706
755
  "updated_via",
707
756
  "parent_id",
708
- "associations",
709
757
  "permissions"
710
758
  ];
711
759
  var versionRowValues = (version) => [
@@ -716,7 +764,6 @@ var versionRowValues = (version) => [
716
764
  version.updatedBy ?? null,
717
765
  version.updatedVia ?? null,
718
766
  version.parentId ?? null,
719
- version.associations ? JSON.stringify(version.associations) : null,
720
767
  version.permissions ? JSON.stringify(version.permissions) : null
721
768
  ];
722
769
  var indexedFieldKinds = (schema) => {
@@ -820,41 +867,51 @@ var SharedSqlRecordLogic = class {
820
867
  * A duplicate id hits the `records` PK — mapped to StackConflictError
821
868
  * instead of surfacing the raw engine exception, mirroring saveVersion's
822
869
  * collision mapping below.
870
+ *
871
+ * One transaction, because a record and the four tables that describe it
872
+ * are one fact. A half-applied create is worse than a failed one: the
873
+ * call raises, so the caller believes nothing landed, while the records
874
+ * row survives to fail their retry on the PK — and the row it leaves is
875
+ * invisible to `filter.search` and mis-ordered by `sort.contentField`
876
+ * until something writes it again.
823
877
  */
824
- async createRecord(record) {
825
- try {
826
- this.exec.run(`INSERT INTO records
878
+ async createRecord(record, opts = {}) {
879
+ this.exec.transaction(() => {
880
+ try {
881
+ this.exec.run(`INSERT INTO records
827
882
  (id, type_id, created_at, updated_at, content, version,
828
883
  parent_id, entity_id, app_id, principal_id, updated_by, updated_via,
829
884
  deleted_at, unlisted_at, permissions)
830
885
  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}"`);
886
+ record.id,
887
+ record.typeId,
888
+ toMs(record.createdAt),
889
+ toMs(record.updatedAt),
890
+ JSON.stringify(record.content),
891
+ record.version,
892
+ record.parentId ?? null,
893
+ record.entityId ?? null,
894
+ record.appId ?? null,
895
+ record.principalId ?? null,
896
+ record.updatedBy ?? null,
897
+ record.updatedVia ?? null,
898
+ record.deletedAt ? toMs(record.deletedAt) : null,
899
+ record.unlistedAt ? toMs(record.unlistedAt) : null,
900
+ record.permissions ? JSON.stringify(record.permissions) : null
901
+ ]);
902
+ } catch (err) {
903
+ if (isUniqueConstraintViolation(err)) {
904
+ throw new StackConflictError(`Record already exists: "${record.id}"`);
905
+ }
906
+ throw err;
850
907
  }
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);
908
+ if (record.associations?.length) {
909
+ this.insertAssociations(record.id, record.associations);
910
+ }
911
+ fts5Strategy.insert(this.exec, record.id, JSON.stringify(record.content));
912
+ this.syncContentIndex(record.id, record.typeId, record.content);
913
+ this.appendJournal(record.id, opts.journal);
914
+ });
858
915
  return record;
859
916
  }
860
917
  async getRecord(id) {
@@ -887,17 +944,40 @@ var SharedSqlRecordLogic = class {
887
944
  * replaced when it carries one, and the snapshot written alongside — so
888
945
  * one call is one version whatever it moved.
889
946
  *
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.
947
+ * `opts.bumpsVersion: false` is a change set that touches only
948
+ * `associations` — Stack computes this from which ops the set actually
949
+ * moves. Such a write skips the records-table UPDATE (and the snapshot)
950
+ * entirely: nothing on that row changes, so there is nothing to bump and
951
+ * no version to snapshot. `expectedVersion`, if given, is still
952
+ * re-checked synchronously inside the transaction — the CAS guard the
953
+ * bumping path gets from its UPDATE's WHERE clause, restated as a read
954
+ * because there is no UPDATE here to carry it. See
955
+ * docs/spec/versioning.md § Version history.
956
+ *
957
+ * The expectedVersion check for a bumping write is standalone rather than
958
+ * folded into the UPDATE alone, because a content change needs
959
+ * fts.remove() to run *before* the records-table content changes. The
960
+ * guard is still in the UPDATE's WHERE, so a writer that slipped in
961
+ * between the read and the write is caught there rather than overwriting.
895
962
  */
896
963
  async mutateRecord(id, changes, opts = {}) {
897
964
  const existing = await this.getRecord(id);
898
965
  if (!existing)
899
966
  throw new StackNotFoundError(`Record not found: "${id}"`);
900
967
  this.checkExpectedVersion(existing, opts.expectedVersion);
968
+ if (opts.bumpsVersion === false) {
969
+ this.exec.transaction(() => {
970
+ const current = this.readRecord(id);
971
+ if (!current)
972
+ throw new StackNotFoundError(`Record not found: "${id}"`);
973
+ this.checkExpectedVersion(current, opts.expectedVersion);
974
+ if (changes.associations !== void 0) {
975
+ this.replaceAssociations(id, changes.associations);
976
+ }
977
+ this.appendJournal(id, opts.journal);
978
+ });
979
+ return this.reread(id, "mutateRecord");
980
+ }
901
981
  const merged = changes.contentPatch ? applyMergePatch(existing.content, changes.contentPatch) : void 0;
902
982
  const sets = [];
903
983
  const values = [];
@@ -931,6 +1011,7 @@ var SharedSqlRecordLogic = class {
931
1011
  fts5Strategy.insert(this.exec, id, JSON.stringify(merged));
932
1012
  this.syncContentIndex(id, existing.typeId, merged);
933
1013
  }
1014
+ this.appendJournal(id, opts.journal);
934
1015
  });
935
1016
  return this.reread(id, "mutateRecord");
936
1017
  }
@@ -943,6 +1024,7 @@ var SharedSqlRecordLogic = class {
943
1024
  if (opts.snapshot)
944
1025
  this.snapshotBeforeMutation(id, opts.snapshot);
945
1026
  this.versionedUpdate(id, ["deleted_at = ?"], [now], opts, now);
1027
+ this.appendJournal(id, opts.journal);
946
1028
  });
947
1029
  return this.reread(id, "deleteRecord");
948
1030
  }
@@ -966,6 +1048,7 @@ var SharedSqlRecordLogic = class {
966
1048
  fts5Strategy.remove(this.exec, id);
967
1049
  this.exec.run("DELETE FROM associations WHERE record_id = ?", [id]);
968
1050
  this.exec.run("DELETE FROM versions WHERE record_id = ?", [id]);
1051
+ this.exec.run("DELETE FROM journal WHERE record_id = ?", [id]);
969
1052
  this.exec.run("DELETE FROM content_index WHERE record_id = ?", [id]);
970
1053
  this.exec.run("DELETE FROM records WHERE id = ?", [id]);
971
1054
  return purged;
@@ -975,36 +1058,36 @@ var SharedSqlRecordLogic = class {
975
1058
  if (opts.snapshot)
976
1059
  this.snapshotBeforeMutation(id, opts.snapshot);
977
1060
  this.versionedUpdate(id, ["deleted_at = NULL"], [], opts);
1061
+ this.appendJournal(id, opts.journal);
978
1062
  });
979
1063
  return this.reread(id, "undelete");
980
1064
  }
981
1065
  async restoreVersion(id, version, opts = {}) {
982
1066
  const existing = await this.getRecord(id);
983
1067
  if (!existing)
984
- throw new Error(`Record not found: "${id}"`);
1068
+ throw new StackNotFoundError(`Record not found: "${id}"`);
985
1069
  this.checkExpectedVersion(existing, opts.expectedVersion);
986
1070
  const target = await this.getVersion(id, version);
987
1071
  if (!target)
988
- throw new Error(`Version not found: ${id}@${version}`);
1072
+ throw new StackNotFoundError(`Version not found: "${id}"@${version}`);
989
1073
  this.exec.transaction(() => {
990
1074
  if (opts.snapshot)
991
1075
  this.snapshotBeforeMutation(id, opts.snapshot);
992
1076
  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);
1077
+ this.appendJournal(id, opts.journal);
996
1078
  });
997
1079
  return this.reread(id, "restoreVersion");
998
1080
  }
999
1081
  async commitMigration(id, toTypeId, content, opts = {}) {
1000
1082
  const existing = await this.getRecord(id);
1001
1083
  if (!existing)
1002
- throw new Error(`Record not found: "${id}"`);
1084
+ throw new StackNotFoundError(`Record not found: "${id}"`);
1003
1085
  this.checkExpectedVersion(existing, opts.expectedVersion);
1004
1086
  this.exec.transaction(() => {
1005
1087
  if (opts.snapshot)
1006
1088
  this.snapshotBeforeMutation(id, opts.snapshot);
1007
1089
  this.rewriteContent(id, toTypeId, content, opts);
1090
+ this.appendJournal(id, opts.journal);
1008
1091
  });
1009
1092
  return this.reread(id, "commitMigration");
1010
1093
  }
@@ -1147,6 +1230,59 @@ var SharedSqlRecordLogic = class {
1147
1230
  }
1148
1231
  }
1149
1232
  // -------------------------------------------------------
1233
+ // Change journal
1234
+ // -------------------------------------------------------
1235
+ /**
1236
+ * Append one entry, stamping the record-derived half off the row as it
1237
+ * now stands — so every caller must already sit inside the same
1238
+ * transaction as the write it describes, after that write has landed.
1239
+ *
1240
+ * `seq` is allocated here, from the log's own max, rather than computed
1241
+ * by the caller. That is what spares this table the collision healing
1242
+ * versions needs: nothing outside this statement ever holds a seq it
1243
+ * expects to be free. See docs/spec/journal.md § Ordering.
1244
+ */
1245
+ appendJournal(recordId, entry) {
1246
+ if (!entry)
1247
+ return;
1248
+ const row = this.exec.get("SELECT version, type_id, parent_id FROM records WHERE id = ?", [recordId]);
1249
+ if (!row)
1250
+ throw new StackNotFoundError(`Record not found: "${recordId}"`);
1251
+ this.exec.run(`INSERT INTO journal
1252
+ (record_id, seq, at, kind, ops, version, type_id, parent_id,
1253
+ previous_parent_id, actor, associations)
1254
+ VALUES (
1255
+ ?,
1256
+ (SELECT COALESCE(MAX(seq), 0) + 1 FROM journal WHERE record_id = ?),
1257
+ ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
1258
+ recordId,
1259
+ recordId,
1260
+ toMs(/* @__PURE__ */ new Date()),
1261
+ entry.kind,
1262
+ JSON.stringify(entry.ops),
1263
+ row.version,
1264
+ row.type_id,
1265
+ row.parent_id,
1266
+ // '' is the root; NULL is an entry that names no origin at all.
1267
+ entry.previousParentId === void 0 ? null : entry.previousParentId ?? "",
1268
+ entry.actor ? JSON.stringify(entry.actor) : null,
1269
+ entry.associations?.length ? JSON.stringify(entry.associations) : null
1270
+ ]);
1271
+ }
1272
+ async getJournal(id, query = {}) {
1273
+ if (!this.exec.get("SELECT 1 FROM records WHERE id = ?", [id])) {
1274
+ throw new StackNotFoundError(`Record not found: "${id}"`);
1275
+ }
1276
+ const conditions = ["record_id = ?"];
1277
+ const values = [id];
1278
+ if (query.sinceSeq !== void 0) {
1279
+ conditions.push("seq > ?");
1280
+ values.push(query.sinceSeq);
1281
+ }
1282
+ 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);
1283
+ return rows.map(rowToJournalEntry);
1284
+ }
1285
+ // -------------------------------------------------------
1150
1286
  // Types
1151
1287
  // -------------------------------------------------------
1152
1288
  async saveType(type) {
@@ -1176,20 +1312,27 @@ var SharedSqlRecordLogic = class {
1176
1312
  // -------------------------------------------------------
1177
1313
  // Associations
1178
1314
  // -------------------------------------------------------
1315
+ /**
1316
+ * Never bumps `version`/`updatedAt` and never snapshots — a set-add
1317
+ * composes correctly regardless of write order, so it needs neither OCC
1318
+ * nor rollback history. The records row is untouched entirely; only the
1319
+ * associations table changes. See docs/spec/versioning.md § Version
1320
+ * history.
1321
+ */
1179
1322
  async associate(recordId, association, opts = {}) {
1180
1323
  this.exec.transaction(() => {
1181
- if (opts.snapshot)
1182
- this.snapshotBeforeMutation(recordId, opts.snapshot);
1183
- this.versionedUpdate(recordId, [], [], opts);
1324
+ if (!this.readRecord(recordId))
1325
+ throw new StackNotFoundError(`Record not found: "${recordId}"`);
1184
1326
  this.insertAssociations(recordId, [association]);
1327
+ this.appendJournal(recordId, opts.journal);
1185
1328
  });
1186
1329
  return this.reread(recordId, "associate");
1187
1330
  }
1331
+ /** Never bumps `version`/`updatedAt` — see associate(). */
1188
1332
  async dissociate(recordId, association, opts = {}) {
1189
1333
  this.exec.transaction(() => {
1190
- if (opts.snapshot)
1191
- this.snapshotBeforeMutation(recordId, opts.snapshot);
1192
- this.versionedUpdate(recordId, [], [], opts);
1334
+ if (!this.readRecord(recordId))
1335
+ throw new StackNotFoundError(`Record not found: "${recordId}"`);
1193
1336
  this.exec.run(`DELETE FROM associations
1194
1337
  WHERE record_id = ?
1195
1338
  AND kind = ?
@@ -1199,6 +1342,7 @@ var SharedSqlRecordLogic = class {
1199
1342
  AND related_id = ?
1200
1343
  AND related_ns = ?
1201
1344
  AND related_stack = ?`, [recordId, association.kind, association.label, ...associationKeyColumns(association)]);
1345
+ this.appendJournal(recordId, opts.journal);
1202
1346
  });
1203
1347
  return this.reread(recordId, "dissociate");
1204
1348
  }
@@ -1325,8 +1469,8 @@ var SharedSqlRecordAdapter = class {
1325
1469
  // -------------------------------------------------------
1326
1470
  // Records
1327
1471
  // -------------------------------------------------------
1328
- createRecord(record) {
1329
- return this.record.createRecord(record);
1472
+ createRecord(record, opts) {
1473
+ return this.record.createRecord(record, opts);
1330
1474
  }
1331
1475
  getRecord(id) {
1332
1476
  return this.record.getRecord(id);
@@ -1365,6 +1509,12 @@ var SharedSqlRecordAdapter = class {
1365
1509
  return this.record.saveVersion(id, version);
1366
1510
  }
1367
1511
  // -------------------------------------------------------
1512
+ // Change journal
1513
+ // -------------------------------------------------------
1514
+ getJournal(id, query) {
1515
+ return this.record.getJournal(id, query);
1516
+ }
1517
+ // -------------------------------------------------------
1368
1518
  // Types
1369
1519
  // -------------------------------------------------------
1370
1520
  saveType(type) {