@haverstack/record-adapter-sqlite 0.26.0 → 0.28.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.js +119 -88
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -24,14 +24,23 @@ var RECORD_SCHEMA_SQL = `
|
|
|
24
24
|
updated_by TEXT,
|
|
25
25
|
updated_via TEXT,
|
|
26
26
|
deleted_at INTEGER,
|
|
27
|
-
unlisted_at INTEGER
|
|
28
|
-
permissions TEXT CHECK (permissions IS NULL OR json_valid(permissions))
|
|
27
|
+
unlisted_at INTEGER
|
|
29
28
|
) STRICT;
|
|
30
29
|
|
|
30
|
+
-- Every edge a record carries, authority and data alike: a permission
|
|
31
|
+
-- entry has element identity and a delta the same shape as a tag's, so
|
|
32
|
+
-- it earns the same table and the same durability tier. The permissions
|
|
33
|
+
-- and associations fields are projections over it, partitioned by
|
|
34
|
+
-- kind. See docs/spec/access-control.md \xA7 Record-level permissions.
|
|
35
|
+
--
|
|
31
36
|
-- A relationship's target is (related_scope, related_id) plus one
|
|
32
37
|
-- qualifier: related_stack for a record in another stack, related_ns for
|
|
33
38
|
-- a foreign namespace. All four are in the primary key, so two targets
|
|
34
|
-
-- differing only by namespace are two associations.
|
|
39
|
+
-- differing only by namespace are two associations. A permission's
|
|
40
|
+
-- grantee reuses them \u2014 related_scope 'entity' or 'group', related_id the
|
|
41
|
+
-- DID or group id \u2014 plus related_role, which only a group grantee sets
|
|
42
|
+
-- and which is in the key because member and admin name two different
|
|
43
|
+
-- sets of people.
|
|
35
44
|
--
|
|
36
45
|
-- attachment_record_id stays out of the primary key: it annotates a
|
|
37
46
|
-- reference rather than naming one, so re-pointing it updates the row an
|
|
@@ -43,22 +52,21 @@ var RECORD_SCHEMA_SQL = `
|
|
|
43
52
|
-- for the filter directions it cannot answer.
|
|
44
53
|
CREATE TABLE IF NOT EXISTS associations (
|
|
45
54
|
record_id TEXT NOT NULL REFERENCES records(id),
|
|
46
|
-
kind TEXT NOT NULL CHECK (kind IN ('tag', 'attachment', 'relationship')),
|
|
55
|
+
kind TEXT NOT NULL CHECK (kind IN ('tag', 'attachment', 'relationship', 'permission', 'anyone')),
|
|
47
56
|
label TEXT NOT NULL,
|
|
48
57
|
file_id TEXT NOT NULL DEFAULT '',
|
|
49
|
-
related_scope TEXT NOT NULL DEFAULT '' CHECK (related_scope IN ('', 'record', 'entity', 'external')),
|
|
58
|
+
related_scope TEXT NOT NULL DEFAULT '' CHECK (related_scope IN ('', 'record', 'entity', 'external', 'group')),
|
|
50
59
|
related_id TEXT NOT NULL DEFAULT '',
|
|
51
60
|
related_ns TEXT NOT NULL DEFAULT '',
|
|
52
61
|
related_stack TEXT NOT NULL DEFAULT '',
|
|
62
|
+
related_role TEXT NOT NULL DEFAULT '' CHECK (related_role IN ('', 'member', 'admin')),
|
|
53
63
|
attachment_record_id TEXT NOT NULL DEFAULT '',
|
|
54
|
-
PRIMARY KEY (record_id, kind, label, file_id, related_scope, related_id, related_ns, related_stack)
|
|
64
|
+
PRIMARY KEY (record_id, kind, label, file_id, related_scope, related_id, related_ns, related_stack, related_role)
|
|
55
65
|
) STRICT;
|
|
56
66
|
|
|
57
|
-
--
|
|
58
|
-
--
|
|
59
|
-
--
|
|
60
|
-
-- No associations column: associate()/dissociate() don't bump version,
|
|
61
|
-
-- so no version ever snapshots the association set. See
|
|
67
|
+
-- Content and the type it is read under. No parent_id, unlisted_at or
|
|
68
|
+
-- associations column: none of those aspects bumps version, so no
|
|
69
|
+
-- version ever snapshots one. See
|
|
62
70
|
-- docs/spec/versioning.md \xA7 Version history.
|
|
63
71
|
CREATE TABLE IF NOT EXISTS versions (
|
|
64
72
|
record_id TEXT NOT NULL REFERENCES records(id),
|
|
@@ -69,8 +77,6 @@ var RECORD_SCHEMA_SQL = `
|
|
|
69
77
|
entity_id TEXT,
|
|
70
78
|
updated_by TEXT,
|
|
71
79
|
updated_via TEXT,
|
|
72
|
-
parent_id TEXT,
|
|
73
|
-
permissions TEXT CHECK (permissions IS NULL OR json_valid(permissions)),
|
|
74
80
|
PRIMARY KEY (record_id, version)
|
|
75
81
|
) STRICT;
|
|
76
82
|
|
|
@@ -582,6 +588,10 @@ var buildQueryPlan = (query) => {
|
|
|
582
588
|
// ../sqlite-shared/dist/mappers.js
|
|
583
589
|
var toMs = (d) => d.getTime();
|
|
584
590
|
var fromMs = (ms) => new Date(ms);
|
|
591
|
+
var partitionAssociations = (associations) => ({
|
|
592
|
+
associations: associations.filter((a) => a.kind !== "permission" && a.kind !== "anyone"),
|
|
593
|
+
permissions: associations.filter((a) => a.kind === "permission" || a.kind === "anyone")
|
|
594
|
+
});
|
|
585
595
|
var rowToRecord = (row, associations) => {
|
|
586
596
|
const record = {
|
|
587
597
|
id: row.id,
|
|
@@ -607,16 +617,27 @@ var rowToRecord = (row, associations) => {
|
|
|
607
617
|
record.deletedAt = fromMs(row.deleted_at);
|
|
608
618
|
if (row.unlisted_at != null)
|
|
609
619
|
record.unlistedAt = fromMs(row.unlisted_at);
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
620
|
+
const partitioned = partitionAssociations(associations);
|
|
621
|
+
if (partitioned.associations.length)
|
|
622
|
+
record.associations = partitioned.associations;
|
|
623
|
+
if (partitioned.permissions.length)
|
|
624
|
+
record.permissions = partitioned.permissions;
|
|
614
625
|
return record;
|
|
615
626
|
};
|
|
616
627
|
var rowToAssociation = (row) => {
|
|
617
628
|
if (row.kind === "tag") {
|
|
618
629
|
return { kind: "tag", label: row.label };
|
|
619
630
|
}
|
|
631
|
+
if (row.kind === "anyone") {
|
|
632
|
+
return { kind: "anyone", label: "read" };
|
|
633
|
+
}
|
|
634
|
+
if (row.kind === "permission") {
|
|
635
|
+
return {
|
|
636
|
+
kind: "permission",
|
|
637
|
+
label: row.label,
|
|
638
|
+
grantee: rowToGrantee(row)
|
|
639
|
+
};
|
|
640
|
+
}
|
|
620
641
|
if (row.kind === "attachment") {
|
|
621
642
|
const attachmentRecordId = row.attachment_record_id;
|
|
622
643
|
return {
|
|
@@ -637,16 +658,25 @@ var rowToAssociation = (row) => {
|
|
|
637
658
|
};
|
|
638
659
|
var associationKeyColumns = (a) => {
|
|
639
660
|
if (a.kind === "attachment")
|
|
640
|
-
return [a.fileId, "", "", "", ""];
|
|
661
|
+
return [a.fileId, "", "", "", "", ""];
|
|
662
|
+
if (a.kind === "permission") {
|
|
663
|
+
const g = a.grantee;
|
|
664
|
+
return g.scope === "entity" ? ["", "entity", g.entityId, "", "", ""] : ["", "group", g.groupId, "", "", g.role];
|
|
665
|
+
}
|
|
641
666
|
if (a.kind !== "relationship")
|
|
642
|
-
return ["", "", "", "", ""];
|
|
667
|
+
return ["", "", "", "", "", ""];
|
|
643
668
|
const t = a.target;
|
|
644
669
|
if (t.scope === "entity")
|
|
645
|
-
return ["", "entity", t.entityId, "", ""];
|
|
670
|
+
return ["", "entity", t.entityId, "", "", ""];
|
|
646
671
|
if (t.scope === "external")
|
|
647
|
-
return ["", "external", t.id, t.ns, ""];
|
|
648
|
-
return ["", "record", t.recordId, "", t.stackUrl ?? ""];
|
|
672
|
+
return ["", "external", t.id, t.ns, "", ""];
|
|
673
|
+
return ["", "record", t.recordId, "", t.stackUrl ?? "", ""];
|
|
649
674
|
};
|
|
675
|
+
var rowToGrantee = (row) => row.related_scope === "group" ? {
|
|
676
|
+
scope: "group",
|
|
677
|
+
groupId: row.related_id,
|
|
678
|
+
role: row.related_role
|
|
679
|
+
} : { scope: "entity", entityId: row.related_id };
|
|
650
680
|
var rowToTarget = (row) => {
|
|
651
681
|
const id = row.related_id;
|
|
652
682
|
if (row.related_scope === "entity")
|
|
@@ -684,10 +714,6 @@ var rowToVersion = (row) => {
|
|
|
684
714
|
v.updatedBy = row.updated_by;
|
|
685
715
|
if (row.updated_via != null)
|
|
686
716
|
v.updatedVia = row.updated_via;
|
|
687
|
-
if (row.parent_id != null)
|
|
688
|
-
v.parentId = row.parent_id;
|
|
689
|
-
if (row.permissions != null)
|
|
690
|
-
v.permissions = JSON.parse(row.permissions);
|
|
691
717
|
return v;
|
|
692
718
|
};
|
|
693
719
|
var rowToJournalEntry = (row) => {
|
|
@@ -752,9 +778,7 @@ var VERSION_COLUMNS = [
|
|
|
752
778
|
"updated_at",
|
|
753
779
|
"entity_id",
|
|
754
780
|
"updated_by",
|
|
755
|
-
"updated_via"
|
|
756
|
-
"parent_id",
|
|
757
|
-
"permissions"
|
|
781
|
+
"updated_via"
|
|
758
782
|
];
|
|
759
783
|
var versionRowValues = (version) => [
|
|
760
784
|
version.typeId,
|
|
@@ -762,9 +786,7 @@ var versionRowValues = (version) => [
|
|
|
762
786
|
toMs(version.updatedAt),
|
|
763
787
|
version.entityId ?? null,
|
|
764
788
|
version.updatedBy ?? null,
|
|
765
|
-
version.updatedVia ?? null
|
|
766
|
-
version.parentId ?? null,
|
|
767
|
-
version.permissions ? JSON.stringify(version.permissions) : null
|
|
789
|
+
version.updatedVia ?? null
|
|
768
790
|
];
|
|
769
791
|
var indexedFieldKinds = (schema) => {
|
|
770
792
|
const kinds = /* @__PURE__ */ new Map();
|
|
@@ -775,7 +797,7 @@ var indexedFieldKinds = (schema) => {
|
|
|
775
797
|
}
|
|
776
798
|
return kinds;
|
|
777
799
|
};
|
|
778
|
-
var SharedSqlRecordLogic = class {
|
|
800
|
+
var SharedSqlRecordLogic = class _SharedSqlRecordLogic {
|
|
779
801
|
deps;
|
|
780
802
|
/**
|
|
781
803
|
* Per-typeId cache of the fields content_index covers and their declared
|
|
@@ -881,8 +903,8 @@ var SharedSqlRecordLogic = class {
|
|
|
881
903
|
this.exec.run(`INSERT INTO records
|
|
882
904
|
(id, type_id, created_at, updated_at, content, version,
|
|
883
905
|
parent_id, entity_id, app_id, principal_id, updated_by, updated_via,
|
|
884
|
-
deleted_at, unlisted_at
|
|
885
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
906
|
+
deleted_at, unlisted_at)
|
|
907
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
886
908
|
record.id,
|
|
887
909
|
record.typeId,
|
|
888
910
|
toMs(record.createdAt),
|
|
@@ -896,8 +918,7 @@ var SharedSqlRecordLogic = class {
|
|
|
896
918
|
record.updatedBy ?? null,
|
|
897
919
|
record.updatedVia ?? null,
|
|
898
920
|
record.deletedAt ? toMs(record.deletedAt) : null,
|
|
899
|
-
record.unlistedAt ? toMs(record.unlistedAt) : null
|
|
900
|
-
record.permissions ? JSON.stringify(record.permissions) : null
|
|
921
|
+
record.unlistedAt ? toMs(record.unlistedAt) : null
|
|
901
922
|
]);
|
|
902
923
|
} catch (err) {
|
|
903
924
|
if (isUniqueConstraintViolation(err)) {
|
|
@@ -905,9 +926,9 @@ var SharedSqlRecordLogic = class {
|
|
|
905
926
|
}
|
|
906
927
|
throw err;
|
|
907
928
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
929
|
+
const edges = [...record.associations ?? [], ...record.permissions ?? []];
|
|
930
|
+
if (edges.length)
|
|
931
|
+
this.insertAssociations(record.id, edges);
|
|
911
932
|
fts5Strategy.insert(this.exec, record.id, JSON.stringify(record.content));
|
|
912
933
|
this.syncContentIndex(record.id, record.typeId, record.content);
|
|
913
934
|
this.appendJournal(record.id, opts.journal);
|
|
@@ -945,13 +966,13 @@ var SharedSqlRecordLogic = class {
|
|
|
945
966
|
* one call is one version whatever it moved.
|
|
946
967
|
*
|
|
947
968
|
* `opts.bumpsVersion: false` is a change set that touches only
|
|
948
|
-
* `associations` — Stack computes this from
|
|
949
|
-
* moves. Such a write
|
|
950
|
-
*
|
|
951
|
-
*
|
|
952
|
-
*
|
|
953
|
-
*
|
|
954
|
-
*
|
|
969
|
+
* `associations`, `parentId` and/or `unlisted` — Stack computes this from
|
|
970
|
+
* which ops the set actually moves. Such a write still lands its columns,
|
|
971
|
+
* but leaves `version`, `updatedAt` and the actor stamps exactly as they
|
|
972
|
+
* stood and takes no snapshot. `expectedVersion`, if given, is re-checked
|
|
973
|
+
* synchronously inside the transaction — the CAS guard the bumping path
|
|
974
|
+
* gets from its UPDATE's WHERE clause, restated as a read because the
|
|
975
|
+
* UPDATE here carries no version predicate. See
|
|
955
976
|
* docs/spec/versioning.md § Version history.
|
|
956
977
|
*
|
|
957
978
|
* The expectedVersion check for a bumping write is standalone rather than
|
|
@@ -965,38 +986,35 @@ var SharedSqlRecordLogic = class {
|
|
|
965
986
|
if (!existing)
|
|
966
987
|
throw new StackNotFoundError(`Record not found: "${id}"`);
|
|
967
988
|
this.checkExpectedVersion(existing, opts.expectedVersion);
|
|
989
|
+
const now = toMs(/* @__PURE__ */ new Date());
|
|
990
|
+
const sets = [];
|
|
991
|
+
const values = [];
|
|
992
|
+
if (changes.parentId !== void 0) {
|
|
993
|
+
sets.push("parent_id = ?");
|
|
994
|
+
values.push(changes.parentId);
|
|
995
|
+
}
|
|
996
|
+
if (changes.unlisted !== void 0) {
|
|
997
|
+
sets.push("unlisted_at = ?");
|
|
998
|
+
values.push(changes.unlisted ? now : null);
|
|
999
|
+
}
|
|
968
1000
|
if (opts.bumpsVersion === false) {
|
|
969
1001
|
this.exec.transaction(() => {
|
|
970
1002
|
const current = this.readRecord(id);
|
|
971
1003
|
if (!current)
|
|
972
1004
|
throw new StackNotFoundError(`Record not found: "${id}"`);
|
|
973
1005
|
this.checkExpectedVersion(current, opts.expectedVersion);
|
|
974
|
-
if (
|
|
975
|
-
this.
|
|
1006
|
+
if (sets.length > 0) {
|
|
1007
|
+
this.exec.run(`UPDATE records SET ${sets.join(", ")} WHERE id = ?`, [...values, id]);
|
|
976
1008
|
}
|
|
1009
|
+
this.replaceAssociationHalves(id, changes);
|
|
977
1010
|
this.appendJournal(id, opts.journal);
|
|
978
1011
|
});
|
|
979
1012
|
return this.reread(id, "mutateRecord");
|
|
980
1013
|
}
|
|
981
1014
|
const merged = changes.contentPatch ? applyMergePatch(existing.content, changes.contentPatch) : void 0;
|
|
982
|
-
const sets = [];
|
|
983
|
-
const values = [];
|
|
984
1015
|
if (merged !== void 0) {
|
|
985
|
-
sets.
|
|
986
|
-
values.
|
|
987
|
-
}
|
|
988
|
-
if (changes.parentId !== void 0) {
|
|
989
|
-
sets.push("parent_id = ?");
|
|
990
|
-
values.push(changes.parentId);
|
|
991
|
-
}
|
|
992
|
-
if (changes.permissions !== void 0) {
|
|
993
|
-
sets.push("permissions = ?");
|
|
994
|
-
values.push(changes.permissions.length ? JSON.stringify(changes.permissions) : null);
|
|
995
|
-
}
|
|
996
|
-
const now = toMs(/* @__PURE__ */ new Date());
|
|
997
|
-
if (changes.unlisted !== void 0) {
|
|
998
|
-
sets.push("unlisted_at = ?");
|
|
999
|
-
values.push(changes.unlisted ? now : null);
|
|
1016
|
+
sets.unshift("content = ?");
|
|
1017
|
+
values.unshift(JSON.stringify(merged));
|
|
1000
1018
|
}
|
|
1001
1019
|
this.exec.transaction(() => {
|
|
1002
1020
|
if (opts.snapshot)
|
|
@@ -1004,9 +1022,7 @@ var SharedSqlRecordLogic = class {
|
|
|
1004
1022
|
if (merged !== void 0)
|
|
1005
1023
|
fts5Strategy.remove(this.exec, id);
|
|
1006
1024
|
this.versionedUpdate(id, sets, values, opts, now);
|
|
1007
|
-
|
|
1008
|
-
this.replaceAssociations(id, changes.associations);
|
|
1009
|
-
}
|
|
1025
|
+
this.replaceAssociationHalves(id, changes);
|
|
1010
1026
|
if (merged !== void 0) {
|
|
1011
1027
|
fts5Strategy.insert(this.exec, id, JSON.stringify(merged));
|
|
1012
1028
|
this.syncContentIndex(id, existing.typeId, merged);
|
|
@@ -1073,7 +1089,7 @@ var SharedSqlRecordLogic = class {
|
|
|
1073
1089
|
this.exec.transaction(() => {
|
|
1074
1090
|
if (opts.snapshot)
|
|
1075
1091
|
this.snapshotBeforeMutation(id, opts.snapshot);
|
|
1076
|
-
this.rewriteContent(id, target.typeId, target.content, opts
|
|
1092
|
+
this.rewriteContent(id, target.typeId, target.content, opts);
|
|
1077
1093
|
this.appendJournal(id, opts.journal);
|
|
1078
1094
|
});
|
|
1079
1095
|
return this.reread(id, "restoreVersion");
|
|
@@ -1093,8 +1109,7 @@ var SharedSqlRecordLogic = class {
|
|
|
1093
1109
|
}
|
|
1094
1110
|
/**
|
|
1095
1111
|
* Replace a record's type and content wholesale — what restoreVersion and
|
|
1096
|
-
* commitMigration each do, differing only in where the content came from
|
|
1097
|
-
* and whether the container moves with it.
|
|
1112
|
+
* commitMigration each do, differing only in where the content came from.
|
|
1098
1113
|
*
|
|
1099
1114
|
* The order is the constraint: fts5Strategy.remove() has to read the old
|
|
1100
1115
|
* content off the records row, so it runs before the UPDATE, and the
|
|
@@ -1102,10 +1117,10 @@ var SharedSqlRecordLogic = class {
|
|
|
1102
1117
|
* exec.transaction() — a failure between the two halves would otherwise
|
|
1103
1118
|
* leave the record unindexed.
|
|
1104
1119
|
*/
|
|
1105
|
-
rewriteContent(id, typeId, content, opts
|
|
1120
|
+
rewriteContent(id, typeId, content, opts) {
|
|
1106
1121
|
const json = JSON.stringify(content);
|
|
1107
1122
|
fts5Strategy.remove(this.exec, id);
|
|
1108
|
-
this.versionedUpdate(id, ["type_id = ?", "content = ?"
|
|
1123
|
+
this.versionedUpdate(id, ["type_id = ?", "content = ?"], [typeId, json], opts);
|
|
1109
1124
|
fts5Strategy.insert(this.exec, id, json);
|
|
1110
1125
|
this.syncContentIndex(id, typeId, content);
|
|
1111
1126
|
}
|
|
@@ -1313,11 +1328,9 @@ var SharedSqlRecordLogic = class {
|
|
|
1313
1328
|
// Associations
|
|
1314
1329
|
// -------------------------------------------------------
|
|
1315
1330
|
/**
|
|
1316
|
-
* Never bumps `version`/`updatedAt` and never snapshots —
|
|
1317
|
-
*
|
|
1318
|
-
*
|
|
1319
|
-
* associations table changes. See docs/spec/versioning.md § Version
|
|
1320
|
-
* history.
|
|
1331
|
+
* Never bumps `version`/`updatedAt` and never snapshots — see
|
|
1332
|
+
* StackRecordAdapter.associate(). The records row is untouched entirely;
|
|
1333
|
+
* only the associations table changes.
|
|
1321
1334
|
*/
|
|
1322
1335
|
async associate(recordId, association, opts = {}) {
|
|
1323
1336
|
this.exec.transaction(() => {
|
|
@@ -1341,19 +1354,37 @@ var SharedSqlRecordLogic = class {
|
|
|
1341
1354
|
AND related_scope = ?
|
|
1342
1355
|
AND related_id = ?
|
|
1343
1356
|
AND related_ns = ?
|
|
1344
|
-
AND related_stack =
|
|
1357
|
+
AND related_stack = ?
|
|
1358
|
+
AND related_role = ?`, [recordId, association.kind, association.label, ...associationKeyColumns(association)]);
|
|
1345
1359
|
this.appendJournal(recordId, opts.journal);
|
|
1346
1360
|
});
|
|
1347
1361
|
return this.reread(recordId, "dissociate");
|
|
1348
1362
|
}
|
|
1363
|
+
/** The kinds each change-set key replaces, and nothing else. */
|
|
1364
|
+
static DATA_KINDS = ["tag", "attachment", "relationship"];
|
|
1365
|
+
static AUTHORITY_KINDS = ["permission", "anyone"];
|
|
1366
|
+
/**
|
|
1367
|
+
* Apply a change set's two association keys, each replacing only within
|
|
1368
|
+
* its own half of the partition. That partition is the whole reason a
|
|
1369
|
+
* tag write cannot drop an ACL: the `associations` key deletes data
|
|
1370
|
+
* kinds alone, so the authority rows beside them are never in scope.
|
|
1371
|
+
* Caller-transactional, like every write below.
|
|
1372
|
+
* See docs/spec/access-control.md § Record-level permissions.
|
|
1373
|
+
*/
|
|
1374
|
+
replaceAssociationHalves(recordId, changes) {
|
|
1375
|
+
if (changes.associations !== void 0) {
|
|
1376
|
+
this.replaceAssociations(recordId, changes.associations, _SharedSqlRecordLogic.DATA_KINDS);
|
|
1377
|
+
}
|
|
1378
|
+
if (changes.permissions !== void 0) {
|
|
1379
|
+
this.replaceAssociations(recordId, changes.permissions, _SharedSqlRecordLogic.AUTHORITY_KINDS);
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1349
1382
|
/**
|
|
1350
|
-
* Swap a record's association set
|
|
1351
|
-
*
|
|
1352
|
-
* version snapshot's both carry. Caller-transactional, like every write
|
|
1353
|
-
* below.
|
|
1383
|
+
* Swap one half of a record's association set wholesale — the "replace,
|
|
1384
|
+
* don't merge" semantics a change set's association list carries.
|
|
1354
1385
|
*/
|
|
1355
|
-
replaceAssociations(recordId, associations) {
|
|
1356
|
-
this.exec.run(
|
|
1386
|
+
replaceAssociations(recordId, associations, kinds) {
|
|
1387
|
+
this.exec.run(`DELETE FROM associations WHERE record_id = ? AND kind IN (${kinds.map(() => "?").join(", ")})`, [recordId, ...kinds]);
|
|
1357
1388
|
if (associations.length)
|
|
1358
1389
|
this.insertAssociations(recordId, associations);
|
|
1359
1390
|
}
|
|
@@ -1372,8 +1403,8 @@ var SharedSqlRecordLogic = class {
|
|
|
1372
1403
|
try {
|
|
1373
1404
|
this.exec.run(`INSERT INTO associations
|
|
1374
1405
|
(record_id, kind, label, file_id, related_scope, related_id, related_ns,
|
|
1375
|
-
related_stack, attachment_record_id)
|
|
1376
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1406
|
+
related_stack, related_role, attachment_record_id)
|
|
1407
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1377
1408
|
ON CONFLICT DO UPDATE SET attachment_record_id = excluded.attachment_record_id`, [
|
|
1378
1409
|
recordId,
|
|
1379
1410
|
assoc.kind,
|