@instantdb/core 0.22.88 → 0.22.89-experimental.drewh-fix-export.20277749804.1
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/__tests__/src/Reactor.test.js +18 -11
- package/__tests__/src/{datalog.test.js → datalog.test.ts} +17 -5
- package/__tests__/src/{instaml.test.js → instaml.test.ts} +183 -119
- package/__tests__/src/instaql.bench.ts +34 -0
- package/__tests__/src/{instaql.test.js → instaql.test.ts} +342 -455
- package/__tests__/src/instaqlInference.test.js +13 -9
- package/__tests__/src/{store.test.js → store.test.ts} +215 -212
- package/dist/commonjs/Reactor.d.ts +23 -6
- package/dist/commonjs/Reactor.d.ts.map +1 -1
- package/dist/commonjs/Reactor.js +110 -45
- package/dist/commonjs/Reactor.js.map +1 -1
- package/dist/commonjs/SyncTable.d.ts +4 -1
- package/dist/commonjs/SyncTable.d.ts.map +1 -1
- package/dist/commonjs/SyncTable.js +35 -37
- package/dist/commonjs/SyncTable.js.map +1 -1
- package/dist/commonjs/instaml.d.ts +17 -4
- package/dist/commonjs/instaml.d.ts.map +1 -1
- package/dist/commonjs/instaml.js +115 -82
- package/dist/commonjs/instaml.js.map +1 -1
- package/dist/commonjs/instaql.d.ts +4 -3
- package/dist/commonjs/instaql.d.ts.map +1 -1
- package/dist/commonjs/instaql.js +65 -63
- package/dist/commonjs/instaql.js.map +1 -1
- package/dist/commonjs/reactorTypes.d.ts +29 -0
- package/dist/commonjs/reactorTypes.d.ts.map +1 -0
- package/dist/commonjs/reactorTypes.js +3 -0
- package/dist/commonjs/reactorTypes.js.map +1 -0
- package/dist/commonjs/store.d.ts +67 -25
- package/dist/commonjs/store.d.ts.map +1 -1
- package/dist/commonjs/store.js +177 -81
- package/dist/commonjs/store.js.map +1 -1
- package/dist/esm/Reactor.d.ts +23 -6
- package/dist/esm/Reactor.d.ts.map +1 -1
- package/dist/esm/Reactor.js +111 -46
- package/dist/esm/Reactor.js.map +1 -1
- package/dist/esm/SyncTable.d.ts +4 -1
- package/dist/esm/SyncTable.d.ts.map +1 -1
- package/dist/esm/SyncTable.js +35 -37
- package/dist/esm/SyncTable.js.map +1 -1
- package/dist/esm/instaml.d.ts +17 -4
- package/dist/esm/instaml.d.ts.map +1 -1
- package/dist/esm/instaml.js +112 -77
- package/dist/esm/instaml.js.map +1 -1
- package/dist/esm/instaql.d.ts +4 -3
- package/dist/esm/instaql.d.ts.map +1 -1
- package/dist/esm/instaql.js +65 -63
- package/dist/esm/instaql.js.map +1 -1
- package/dist/esm/reactorTypes.d.ts +29 -0
- package/dist/esm/reactorTypes.d.ts.map +1 -0
- package/dist/esm/reactorTypes.js +2 -0
- package/dist/esm/reactorTypes.js.map +1 -0
- package/dist/esm/store.d.ts +67 -25
- package/dist/esm/store.d.ts.map +1 -1
- package/dist/esm/store.js +174 -81
- package/dist/esm/store.js.map +1 -1
- package/dist/standalone/index.js +1605 -1415
- package/dist/standalone/index.umd.cjs +3 -3
- package/package.json +2 -2
- package/src/Reactor.js +152 -75
- package/src/SyncTable.ts +85 -45
- package/src/{instaml.js → instaml.ts} +201 -96
- package/src/instaql.ts +88 -62
- package/src/reactorTypes.ts +32 -0
- package/src/store.ts +257 -101
- package/__tests__/src/instaql.bench.js +0 -29
|
@@ -1,13 +1,33 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
allMapValues,
|
|
3
|
+
AttrsStore,
|
|
4
|
+
AttrsStoreClass,
|
|
5
|
+
getAttrByFwdIdentName,
|
|
6
|
+
getAttrByReverseIdentName,
|
|
7
|
+
Store,
|
|
8
|
+
} from './store.ts';
|
|
2
9
|
import { getOps, isLookup, parseLookup } from './instatx.ts';
|
|
3
10
|
import { immutableRemoveUndefined } from './utils/object.js';
|
|
4
11
|
import { coerceToDate } from './utils/dates.ts';
|
|
5
12
|
import uuid from './utils/uuid.ts';
|
|
13
|
+
import {
|
|
14
|
+
EntitiesWithLinks,
|
|
15
|
+
IContainEntitiesAndLinks,
|
|
16
|
+
LinkDef,
|
|
17
|
+
} from './schemaTypes.ts';
|
|
18
|
+
import { InstantDBAttr, InstantDBIdent } from './attrTypes.ts';
|
|
19
|
+
|
|
20
|
+
export type AttrMapping = {
|
|
21
|
+
attrIdMap: Record<string, string>;
|
|
22
|
+
refSwapAttrIds: Set<string>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type TXStep = any[];
|
|
6
26
|
|
|
7
27
|
// Rewrites optimistic attrs with the attrs we get back from the server.
|
|
8
|
-
export function rewriteStep(attrMapping, txStep) {
|
|
28
|
+
export function rewriteStep(attrMapping: AttrMapping, txStep: TXStep): TXStep {
|
|
9
29
|
const { attrIdMap, refSwapAttrIds } = attrMapping;
|
|
10
|
-
const rewritten = [];
|
|
30
|
+
const rewritten: TXStep = [];
|
|
11
31
|
for (const part of txStep) {
|
|
12
32
|
const newValue = attrIdMap[part];
|
|
13
33
|
|
|
@@ -35,22 +55,6 @@ export function rewriteStep(attrMapping, txStep) {
|
|
|
35
55
|
return rewritten;
|
|
36
56
|
}
|
|
37
57
|
|
|
38
|
-
export function getAttrByFwdIdentName(attrs, inputEtype, inputIdentName) {
|
|
39
|
-
return Object.values(attrs).find((attr) => {
|
|
40
|
-
const [_id, etype, label] = attr['forward-identity'];
|
|
41
|
-
return etype === inputEtype && label === inputIdentName;
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function getAttrByReverseIdentName(attrs, inputEtype, inputIdentName) {
|
|
46
|
-
return Object.values(attrs).find((attr) => {
|
|
47
|
-
const revIdent = attr['reverse-identity'];
|
|
48
|
-
if (!revIdent) return false;
|
|
49
|
-
const [_id, etype, label] = revIdent;
|
|
50
|
-
return etype === inputEtype && label === inputIdentName;
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
|
|
54
58
|
function explodeLookupRef(eid) {
|
|
55
59
|
if (Array.isArray(eid)) {
|
|
56
60
|
return eid;
|
|
@@ -64,7 +68,7 @@ function explodeLookupRef(eid) {
|
|
|
64
68
|
return entries[0];
|
|
65
69
|
}
|
|
66
70
|
|
|
67
|
-
function isRefLookupIdent(attrs, etype, identName) {
|
|
71
|
+
function isRefLookupIdent(attrs: AttrsStore, etype: string, identName: string) {
|
|
68
72
|
return (
|
|
69
73
|
identName.indexOf('.') !== -1 &&
|
|
70
74
|
// attr names can have `.` in them, so use the attr we find with a `.`
|
|
@@ -82,7 +86,11 @@ function extractRefLookupFwdName(identName) {
|
|
|
82
86
|
return fwdName;
|
|
83
87
|
}
|
|
84
88
|
|
|
85
|
-
function lookupIdentToAttr(
|
|
89
|
+
function lookupIdentToAttr(
|
|
90
|
+
attrs: AttrsStore,
|
|
91
|
+
etype: string,
|
|
92
|
+
identName: string,
|
|
93
|
+
) {
|
|
86
94
|
if (!isRefLookupIdent(attrs, etype, identName)) {
|
|
87
95
|
return getAttrByFwdIdentName(attrs, etype, identName);
|
|
88
96
|
}
|
|
@@ -109,7 +117,7 @@ function lookupPairOfEid(eid) {
|
|
|
109
117
|
: explodeLookupRef(eid);
|
|
110
118
|
}
|
|
111
119
|
|
|
112
|
-
function extractLookup(attrs, etype, eid) {
|
|
120
|
+
function extractLookup(attrs: AttrsStore, etype: string, eid: string) {
|
|
113
121
|
const lookupPair = lookupPairOfEid(eid);
|
|
114
122
|
|
|
115
123
|
if (lookupPair === null) {
|
|
@@ -124,7 +132,12 @@ function extractLookup(attrs, etype, eid) {
|
|
|
124
132
|
return [attr.id, value];
|
|
125
133
|
}
|
|
126
134
|
|
|
127
|
-
function withIdAttrForLookup(
|
|
135
|
+
function withIdAttrForLookup(
|
|
136
|
+
attrs: AttrsStore,
|
|
137
|
+
etype: string,
|
|
138
|
+
eidA: string,
|
|
139
|
+
txSteps: TXStep[],
|
|
140
|
+
) {
|
|
128
141
|
const lookup = extractLookup(attrs, etype, eidA);
|
|
129
142
|
if (!Array.isArray(lookup)) {
|
|
130
143
|
return txSteps;
|
|
@@ -132,63 +145,76 @@ function withIdAttrForLookup(attrs, etype, eidA, txSteps) {
|
|
|
132
145
|
const idTuple = [
|
|
133
146
|
'add-triple',
|
|
134
147
|
lookup,
|
|
135
|
-
getAttrByFwdIdentName(attrs, etype, 'id')
|
|
148
|
+
getAttrByFwdIdentName(attrs, etype, 'id')?.id,
|
|
136
149
|
lookup,
|
|
137
150
|
];
|
|
138
151
|
return [idTuple].concat(txSteps);
|
|
139
152
|
}
|
|
140
153
|
|
|
141
|
-
function expandLink({
|
|
154
|
+
function expandLink({ attrsStore }: Ctx, [etype, eidA, obj]) {
|
|
142
155
|
const addTriples = Object.entries(obj).flatMap(([label, eidOrEids]) => {
|
|
143
156
|
const eids = Array.isArray(eidOrEids) ? eidOrEids : [eidOrEids];
|
|
144
|
-
const fwdAttr = getAttrByFwdIdentName(
|
|
145
|
-
const revAttr = getAttrByReverseIdentName(
|
|
157
|
+
const fwdAttr = getAttrByFwdIdentName(attrsStore, etype, label);
|
|
158
|
+
const revAttr = getAttrByReverseIdentName(attrsStore, etype, label);
|
|
146
159
|
return eids.map((eidB) => {
|
|
147
160
|
const txStep = fwdAttr
|
|
148
161
|
? [
|
|
149
162
|
'add-triple',
|
|
150
|
-
extractLookup(
|
|
163
|
+
extractLookup(attrsStore, etype, eidA),
|
|
151
164
|
fwdAttr.id,
|
|
152
|
-
|
|
165
|
+
// Uses `!` because if we get here, we should have created the attr if it doesn't
|
|
166
|
+
// already exist
|
|
167
|
+
extractLookup(attrsStore, fwdAttr['reverse-identity']![1], eidB),
|
|
153
168
|
]
|
|
154
169
|
: [
|
|
155
170
|
'add-triple',
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
extractLookup(
|
|
171
|
+
// Uses `!` because if we get here, we should have created the attr if it doesn't
|
|
172
|
+
// already exist
|
|
173
|
+
extractLookup(attrsStore, revAttr!['forward-identity']![1], eidB),
|
|
174
|
+
revAttr?.id,
|
|
175
|
+
extractLookup(attrsStore, etype, eidA),
|
|
159
176
|
];
|
|
160
177
|
return txStep;
|
|
161
178
|
});
|
|
162
179
|
});
|
|
163
|
-
return withIdAttrForLookup(
|
|
180
|
+
return withIdAttrForLookup(attrsStore, etype, eidA, addTriples);
|
|
164
181
|
}
|
|
165
182
|
|
|
166
|
-
function expandUnlink({
|
|
183
|
+
function expandUnlink({ attrsStore }: Ctx, [etype, eidA, obj]) {
|
|
167
184
|
const retractTriples = Object.entries(obj).flatMap(([label, eidOrEids]) => {
|
|
168
185
|
const eids = Array.isArray(eidOrEids) ? eidOrEids : [eidOrEids];
|
|
169
|
-
const fwdAttr = getAttrByFwdIdentName(
|
|
170
|
-
const revAttr = getAttrByReverseIdentName(
|
|
186
|
+
const fwdAttr = getAttrByFwdIdentName(attrsStore, etype, label);
|
|
187
|
+
const revAttr = getAttrByReverseIdentName(attrsStore, etype, label);
|
|
171
188
|
return eids.map((eidB) => {
|
|
172
189
|
const txStep = fwdAttr
|
|
173
190
|
? [
|
|
174
191
|
'retract-triple',
|
|
175
|
-
extractLookup(
|
|
192
|
+
extractLookup(attrsStore, etype, eidA),
|
|
176
193
|
fwdAttr.id,
|
|
177
|
-
|
|
194
|
+
// Uses `!` because if we get here, we should have created the attr if it doesn't
|
|
195
|
+
// already exist
|
|
196
|
+
extractLookup(attrsStore, fwdAttr!['reverse-identity']![1], eidB),
|
|
178
197
|
]
|
|
179
198
|
: [
|
|
180
199
|
'retract-triple',
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
extractLookup(
|
|
200
|
+
// Uses `!` because if we get here, we should have created the attr if it doesn't
|
|
201
|
+
// already exist
|
|
202
|
+
extractLookup(attrsStore, revAttr!['forward-identity'][1], eidB),
|
|
203
|
+
revAttr!.id,
|
|
204
|
+
extractLookup(attrsStore, etype, eidA),
|
|
184
205
|
];
|
|
185
206
|
return txStep;
|
|
186
207
|
});
|
|
187
208
|
});
|
|
188
|
-
return withIdAttrForLookup(
|
|
209
|
+
return withIdAttrForLookup(attrsStore, etype, eidA, retractTriples);
|
|
189
210
|
}
|
|
190
211
|
|
|
191
|
-
function checkEntityExists(
|
|
212
|
+
function checkEntityExists(
|
|
213
|
+
stores: (Store | undefined)[] | undefined,
|
|
214
|
+
attrsStore: AttrsStore,
|
|
215
|
+
etype: string,
|
|
216
|
+
eid: string,
|
|
217
|
+
) {
|
|
192
218
|
if (Array.isArray(eid)) {
|
|
193
219
|
// lookup ref
|
|
194
220
|
const [entity_a, entity_v] = eid;
|
|
@@ -196,7 +222,7 @@ function checkEntityExists(stores, etype, eid) {
|
|
|
196
222
|
const ev = store?.aev.get(entity_a);
|
|
197
223
|
if (ev) {
|
|
198
224
|
// This would be a lot more efficient with a ave index
|
|
199
|
-
for (const [
|
|
225
|
+
for (const [_e, _a, v] of allMapValues(ev, 2)) {
|
|
200
226
|
if (v === entity_v) {
|
|
201
227
|
return true;
|
|
202
228
|
}
|
|
@@ -209,7 +235,7 @@ function checkEntityExists(stores, etype, eid) {
|
|
|
209
235
|
const av = store?.eav.get(eid);
|
|
210
236
|
if (av) {
|
|
211
237
|
for (const attr_id of av.keys()) {
|
|
212
|
-
if (
|
|
238
|
+
if (attrsStore.getAttr(attr_id)?.['forward-identity'][1] == etype) {
|
|
213
239
|
return true;
|
|
214
240
|
}
|
|
215
241
|
}
|
|
@@ -219,26 +245,34 @@ function checkEntityExists(stores, etype, eid) {
|
|
|
219
245
|
return false;
|
|
220
246
|
}
|
|
221
247
|
|
|
222
|
-
|
|
248
|
+
type Ctx = {
|
|
249
|
+
stores?: (Store | undefined)[];
|
|
250
|
+
attrsStore: AttrsStore;
|
|
251
|
+
schema?: Schema;
|
|
252
|
+
useDateObjects?: boolean | null;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
function convertOpts({ stores, attrsStore }: Ctx, [etype, eid, obj_, opts]) {
|
|
223
256
|
return opts?.upsert === false
|
|
224
257
|
? { mode: 'update' }
|
|
225
258
|
: opts?.upsert === true
|
|
226
259
|
? null
|
|
227
|
-
: checkEntityExists(stores, etype, eid)
|
|
260
|
+
: checkEntityExists(stores, attrsStore, etype, eid)
|
|
228
261
|
? { mode: 'update' }
|
|
229
262
|
: null; // auto mode chooses between update and upsert, not update and create, just in case
|
|
230
263
|
}
|
|
231
264
|
|
|
232
|
-
function expandCreate(ctx, step) {
|
|
233
|
-
const {
|
|
265
|
+
function expandCreate(ctx: Ctx, step) {
|
|
266
|
+
const { attrsStore } = ctx;
|
|
234
267
|
const [etype, eid, obj_, opts] = step;
|
|
235
268
|
const obj = immutableRemoveUndefined(obj_);
|
|
236
|
-
const lookup = extractLookup(
|
|
269
|
+
const lookup = extractLookup(attrsStore, etype, eid);
|
|
237
270
|
// id first so that we don't clobber updates on the lookup field
|
|
238
271
|
const attrTuples = [['id', lookup]]
|
|
239
272
|
.concat(Object.entries(obj))
|
|
240
|
-
.map(([identName, value]) => {
|
|
241
|
-
|
|
273
|
+
.map(([identName, value]: [string, any]) => {
|
|
274
|
+
// Uses `!` because we should have optimistically created the attr if it doesn't exist
|
|
275
|
+
const attr = getAttrByFwdIdentName(attrsStore, etype, identName)!;
|
|
242
276
|
|
|
243
277
|
if (attr['checked-data-type'] === 'date' && ctx.useDateObjects) {
|
|
244
278
|
value = coerceToDate(value);
|
|
@@ -249,17 +283,17 @@ function expandCreate(ctx, step) {
|
|
|
249
283
|
return attrTuples;
|
|
250
284
|
}
|
|
251
285
|
|
|
252
|
-
function expandUpdate(ctx, step) {
|
|
253
|
-
const {
|
|
286
|
+
function expandUpdate(ctx: Ctx, step) {
|
|
287
|
+
const { attrsStore } = ctx;
|
|
254
288
|
const [etype, eid, obj_, opts] = step;
|
|
255
289
|
const obj = immutableRemoveUndefined(obj_);
|
|
256
|
-
const lookup = extractLookup(
|
|
290
|
+
const lookup = extractLookup(attrsStore, etype, eid);
|
|
257
291
|
const serverOpts = convertOpts(ctx, [etype, lookup, obj_, opts]);
|
|
258
292
|
// id first so that we don't clobber updates on the lookup field
|
|
259
293
|
const attrTuples = [['id', lookup]]
|
|
260
294
|
.concat(Object.entries(obj))
|
|
261
|
-
.map(([identName, value]) => {
|
|
262
|
-
const attr = getAttrByFwdIdentName(
|
|
295
|
+
.map(([identName, value]: [string, any]) => {
|
|
296
|
+
const attr = getAttrByFwdIdentName(attrsStore, etype, identName)!;
|
|
263
297
|
|
|
264
298
|
if (attr['checked-data-type'] === 'date' && ctx.useDateObjects) {
|
|
265
299
|
value = coerceToDate(value);
|
|
@@ -276,19 +310,19 @@ function expandUpdate(ctx, step) {
|
|
|
276
310
|
return attrTuples;
|
|
277
311
|
}
|
|
278
312
|
|
|
279
|
-
function expandDelete({
|
|
280
|
-
const lookup = extractLookup(
|
|
313
|
+
function expandDelete({ attrsStore }: Ctx, [etype, eid]) {
|
|
314
|
+
const lookup = extractLookup(attrsStore, etype, eid);
|
|
281
315
|
return [['delete-entity', lookup, etype]];
|
|
282
316
|
}
|
|
283
317
|
|
|
284
|
-
function expandDeepMerge(ctx, step) {
|
|
285
|
-
const {
|
|
318
|
+
function expandDeepMerge(ctx: Ctx, step) {
|
|
319
|
+
const { attrsStore } = ctx;
|
|
286
320
|
const [etype, eid, obj_, opts] = step;
|
|
287
321
|
const obj = immutableRemoveUndefined(obj_);
|
|
288
|
-
const lookup = extractLookup(
|
|
322
|
+
const lookup = extractLookup(attrsStore, etype, eid);
|
|
289
323
|
const serverOpts = convertOpts(ctx, [etype, lookup, obj_, opts]);
|
|
290
324
|
const attrTuples = Object.entries(obj).map(([identName, value]) => {
|
|
291
|
-
const attr = getAttrByFwdIdentName(
|
|
325
|
+
const attr = getAttrByFwdIdentName(attrsStore, etype, identName)!;
|
|
292
326
|
return [
|
|
293
327
|
'deep-merge-triple',
|
|
294
328
|
lookup,
|
|
@@ -301,7 +335,7 @@ function expandDeepMerge(ctx, step) {
|
|
|
301
335
|
const idTuple = [
|
|
302
336
|
'add-triple',
|
|
303
337
|
lookup,
|
|
304
|
-
getAttrByFwdIdentName(
|
|
338
|
+
getAttrByFwdIdentName(attrsStore, etype, 'id')!.id,
|
|
305
339
|
lookup,
|
|
306
340
|
...(serverOpts ? [serverOpts] : []),
|
|
307
341
|
];
|
|
@@ -310,8 +344,8 @@ function expandDeepMerge(ctx, step) {
|
|
|
310
344
|
return [idTuple].concat(attrTuples);
|
|
311
345
|
}
|
|
312
346
|
|
|
313
|
-
function expandRuleParams({
|
|
314
|
-
const lookup = extractLookup(
|
|
347
|
+
function expandRuleParams({ attrsStore }: Ctx, [etype, eid, ruleParams]) {
|
|
348
|
+
const lookup = extractLookup(attrsStore, etype, eid);
|
|
315
349
|
return [['rule-params', lookup, etype, ruleParams]];
|
|
316
350
|
}
|
|
317
351
|
|
|
@@ -325,7 +359,7 @@ function removeIdFromArgs(step) {
|
|
|
325
359
|
return [op, etype, eid, newObj, ...(opts ? [opts] : [])];
|
|
326
360
|
}
|
|
327
361
|
|
|
328
|
-
function toTxSteps(ctx, step) {
|
|
362
|
+
function toTxSteps(ctx: Ctx, step) {
|
|
329
363
|
const [action, ...args] = removeIdFromArgs(step);
|
|
330
364
|
switch (action) {
|
|
331
365
|
case 'merge':
|
|
@@ -398,8 +432,12 @@ function createObjectAttr(schema, etype, label, props) {
|
|
|
398
432
|
};
|
|
399
433
|
}
|
|
400
434
|
|
|
401
|
-
|
|
402
|
-
|
|
435
|
+
type Link = LinkDef<any, any, any, any, any, any, any>;
|
|
436
|
+
type Schema = IContainEntitiesAndLinks<any, any>;
|
|
437
|
+
|
|
438
|
+
function findSchemaLink(schema: Schema, etype, label): Link | undefined {
|
|
439
|
+
const links: Link[] = Object.values(schema.links);
|
|
440
|
+
const found = links.find((x: Link) => {
|
|
403
441
|
return (
|
|
404
442
|
(x.forward.on === etype && x.forward.label === label) ||
|
|
405
443
|
(x.reverse.on === etype && x.reverse.label === label)
|
|
@@ -408,7 +446,7 @@ function findSchemaLink(schema, etype, label) {
|
|
|
408
446
|
return found;
|
|
409
447
|
}
|
|
410
448
|
|
|
411
|
-
function refPropsFromSchema(schema, etype, label) {
|
|
449
|
+
function refPropsFromSchema(schema: Schema, etype, label) {
|
|
412
450
|
const found = findSchemaLink(schema, etype, label);
|
|
413
451
|
if (!found) {
|
|
414
452
|
throw new Error(`Couldn't find the link ${etype}.${label} in your schema`);
|
|
@@ -424,18 +462,26 @@ function refPropsFromSchema(schema, etype, label) {
|
|
|
424
462
|
};
|
|
425
463
|
}
|
|
426
464
|
|
|
427
|
-
function createRefAttr(
|
|
465
|
+
function createRefAttr(
|
|
466
|
+
schema: Schema | undefined,
|
|
467
|
+
etype: string,
|
|
468
|
+
label: string,
|
|
469
|
+
props?: Partial<InstantDBAttr> | undefined,
|
|
470
|
+
): InstantDBAttr {
|
|
428
471
|
const schemaRefProps = schema
|
|
429
472
|
? refPropsFromSchema(schema, etype, label)
|
|
430
473
|
: null;
|
|
431
474
|
const attrId = uuid();
|
|
432
|
-
const fwdIdent = [uuid(), etype, label];
|
|
433
|
-
const revIdent = [uuid(), label, etype];
|
|
475
|
+
const fwdIdent: InstantDBIdent = [uuid(), etype, label];
|
|
476
|
+
const revIdent: InstantDBIdent = [uuid(), label, etype];
|
|
434
477
|
return {
|
|
435
478
|
id: attrId,
|
|
479
|
+
// @ts-ignore: ts thinks it's any[]
|
|
436
480
|
'forward-identity': fwdIdent,
|
|
481
|
+
// @ts-ignore: ts thinks it's any[]
|
|
437
482
|
'reverse-identity': revIdent,
|
|
438
483
|
'value-type': 'ref',
|
|
484
|
+
// @ts-ignore: ts thinks it's type string
|
|
439
485
|
cardinality: 'many',
|
|
440
486
|
'unique?': false,
|
|
441
487
|
'index?': false,
|
|
@@ -459,11 +505,14 @@ const SUPPORTS_LOOKUP_ACTIONS = new Set([
|
|
|
459
505
|
'ruleParams',
|
|
460
506
|
]);
|
|
461
507
|
|
|
462
|
-
const lookupProps = { 'unique?': true, 'index?': true };
|
|
463
|
-
const refLookupProps = {
|
|
508
|
+
const lookupProps: Partial<InstantDBAttr> = { 'unique?': true, 'index?': true };
|
|
509
|
+
const refLookupProps: Partial<InstantDBAttr> = {
|
|
510
|
+
...lookupProps,
|
|
511
|
+
cardinality: 'one',
|
|
512
|
+
};
|
|
464
513
|
|
|
465
514
|
function lookupPairsOfOp(op) {
|
|
466
|
-
const res = [];
|
|
515
|
+
const res: { etype: string; lookupPair: any; linkLabel?: string }[] = [];
|
|
467
516
|
const [action, etype, eid, obj] = op;
|
|
468
517
|
if (!SUPPORTS_LOOKUP_ACTIONS.has(action)) {
|
|
469
518
|
return res;
|
|
@@ -491,24 +540,72 @@ function lookupPairsOfOp(op) {
|
|
|
491
540
|
return res;
|
|
492
541
|
}
|
|
493
542
|
|
|
494
|
-
function createMissingAttrs(
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
543
|
+
function createMissingAttrs(
|
|
544
|
+
{ attrsStore, schema }: Ctx,
|
|
545
|
+
ops,
|
|
546
|
+
): [AttrsStore, TXStep[]] {
|
|
547
|
+
const addedIds = new Set();
|
|
548
|
+
const localAttrs: InstantDBAttr[] = [];
|
|
549
|
+
const addOps: TXStep[] = [];
|
|
550
|
+
|
|
551
|
+
function attrByFwdIdent(etype, label): InstantDBAttr | undefined {
|
|
552
|
+
return (
|
|
553
|
+
getAttrByFwdIdentName(attrsStore, etype, label) ||
|
|
554
|
+
localAttrs.find(
|
|
555
|
+
(x) =>
|
|
556
|
+
x['forward-identity'][1] === etype &&
|
|
557
|
+
x['forward-identity'][2] === label,
|
|
558
|
+
)
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function attrByRevIdent(etype, label): InstantDBAttr | undefined {
|
|
563
|
+
return (
|
|
564
|
+
getAttrByReverseIdentName(attrsStore, etype, label) ||
|
|
565
|
+
localAttrs.find(
|
|
566
|
+
(x) =>
|
|
567
|
+
x['reverse-identity']?.[1] === etype &&
|
|
568
|
+
x['reverse-identity']?.[2] === label,
|
|
569
|
+
)
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
function addAttr(attr: InstantDBAttr) {
|
|
574
|
+
localAttrs.push(attr);
|
|
498
575
|
addOps.push(['add-attr', attr]);
|
|
499
576
|
addedIds.add(attr.id);
|
|
500
577
|
}
|
|
501
|
-
function addUnsynced(
|
|
502
|
-
|
|
578
|
+
function addUnsynced(
|
|
579
|
+
attr:
|
|
580
|
+
| (InstantDBAttr & { isUnsynced?: boolean })
|
|
581
|
+
| InstantDBAttr
|
|
582
|
+
| undefined,
|
|
583
|
+
) {
|
|
584
|
+
if (
|
|
585
|
+
attr &&
|
|
586
|
+
'isUnsynced' in attr &&
|
|
587
|
+
attr.isUnsynced &&
|
|
588
|
+
!addedIds.has(attr.id)
|
|
589
|
+
) {
|
|
590
|
+
localAttrs.push(attr);
|
|
503
591
|
addOps.push(['add-attr', attr]);
|
|
504
592
|
addedIds.add(attr.id);
|
|
505
593
|
}
|
|
506
594
|
}
|
|
507
595
|
|
|
596
|
+
function isRefLookupIdentLocal(etype: string, identName: string) {
|
|
597
|
+
return (
|
|
598
|
+
identName.indexOf('.') !== -1 &&
|
|
599
|
+
// attr names can have `.` in them, so use the attr we find with a `.`
|
|
600
|
+
// before assuming it's a ref lookup.
|
|
601
|
+
!attrByFwdIdent(etype, identName)
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
|
|
508
605
|
// Adds attrs needed for a ref lookup
|
|
509
606
|
function addForRef(etype, label) {
|
|
510
|
-
const fwdAttr =
|
|
511
|
-
const revAttr =
|
|
607
|
+
const fwdAttr = attrByFwdIdent(etype, label);
|
|
608
|
+
const revAttr = attrByRevIdent(etype, label);
|
|
512
609
|
addUnsynced(fwdAttr);
|
|
513
610
|
addUnsynced(revAttr);
|
|
514
611
|
if (!fwdAttr && !revAttr) {
|
|
@@ -530,18 +627,18 @@ function createMissingAttrs({ attrs: existingAttrs, schema }, ops) {
|
|
|
530
627
|
|
|
531
628
|
// Figure out the link etype so we can make sure we have the attrs
|
|
532
629
|
// for the link lookup
|
|
533
|
-
const fwdAttr =
|
|
534
|
-
const revAttr =
|
|
630
|
+
const fwdAttr = attrByFwdIdent(etype, linkLabel);
|
|
631
|
+
const revAttr = attrByRevIdent(etype, linkLabel);
|
|
535
632
|
addUnsynced(fwdAttr);
|
|
536
633
|
addUnsynced(revAttr);
|
|
537
634
|
const linkEtype =
|
|
538
635
|
fwdAttr?.['reverse-identity']?.[1] ||
|
|
539
636
|
revAttr?.['forward-identity']?.[1] ||
|
|
540
637
|
linkLabel;
|
|
541
|
-
if (
|
|
638
|
+
if (isRefLookupIdentLocal(linkEtype, identName)) {
|
|
542
639
|
addForRef(linkEtype, extractRefLookupFwdName(identName));
|
|
543
640
|
} else {
|
|
544
|
-
const attr =
|
|
641
|
+
const attr = attrByFwdIdent(linkEtype, identName);
|
|
545
642
|
if (!attr) {
|
|
546
643
|
addAttr(
|
|
547
644
|
createObjectAttr(schema, linkEtype, identName, lookupProps),
|
|
@@ -549,10 +646,10 @@ function createMissingAttrs({ attrs: existingAttrs, schema }, ops) {
|
|
|
549
646
|
}
|
|
550
647
|
addUnsynced(attr);
|
|
551
648
|
}
|
|
552
|
-
} else if (
|
|
649
|
+
} else if (isRefLookupIdentLocal(etype, identName)) {
|
|
553
650
|
addForRef(etype, extractRefLookupFwdName(identName));
|
|
554
651
|
} else {
|
|
555
|
-
const attr =
|
|
652
|
+
const attr = attrByFwdIdent(etype, identName);
|
|
556
653
|
if (!attr) {
|
|
557
654
|
addAttr(createObjectAttr(schema, etype, identName, lookupProps));
|
|
558
655
|
}
|
|
@@ -565,14 +662,14 @@ function createMissingAttrs({ attrs: existingAttrs, schema }, ops) {
|
|
|
565
662
|
for (const op of ops) {
|
|
566
663
|
const [action, etype, eid, obj] = op;
|
|
567
664
|
if (OBJ_ACTIONS.has(action)) {
|
|
568
|
-
const idAttr =
|
|
665
|
+
const idAttr = attrByFwdIdent(etype, 'id');
|
|
569
666
|
addUnsynced(idAttr);
|
|
570
667
|
if (!idAttr) {
|
|
571
668
|
addAttr(createObjectAttr(schema, etype, 'id', { 'unique?': true }));
|
|
572
669
|
}
|
|
573
670
|
|
|
574
671
|
for (const label of Object.keys(obj)) {
|
|
575
|
-
const fwdAttr =
|
|
672
|
+
const fwdAttr = attrByFwdIdent(etype, label);
|
|
576
673
|
addUnsynced(fwdAttr);
|
|
577
674
|
if (UPDATE_ACTIONS.has(action)) {
|
|
578
675
|
if (!fwdAttr) {
|
|
@@ -587,7 +684,7 @@ function createMissingAttrs({ attrs: existingAttrs, schema }, ops) {
|
|
|
587
684
|
}
|
|
588
685
|
}
|
|
589
686
|
if (REF_ACTIONS.has(action)) {
|
|
590
|
-
const revAttr =
|
|
687
|
+
const revAttr = attrByRevIdent(etype, label);
|
|
591
688
|
if (!fwdAttr && !revAttr) {
|
|
592
689
|
addAttr(createRefAttr(schema, etype, label));
|
|
593
690
|
}
|
|
@@ -596,14 +693,22 @@ function createMissingAttrs({ attrs: existingAttrs, schema }, ops) {
|
|
|
596
693
|
}
|
|
597
694
|
}
|
|
598
695
|
}
|
|
599
|
-
|
|
696
|
+
|
|
697
|
+
if (localAttrs.length) {
|
|
698
|
+
const nextAttrs = { ...attrsStore.attrs };
|
|
699
|
+
for (const attr of localAttrs) {
|
|
700
|
+
nextAttrs[attr.id] = attr;
|
|
701
|
+
}
|
|
702
|
+
return [new AttrsStoreClass(nextAttrs, attrsStore.linkIndex), addOps];
|
|
703
|
+
}
|
|
704
|
+
return [attrsStore, addOps];
|
|
600
705
|
}
|
|
601
706
|
|
|
602
|
-
export function transform(ctx, inputChunks) {
|
|
707
|
+
export function transform(ctx: Ctx, inputChunks) {
|
|
603
708
|
const chunks = Array.isArray(inputChunks) ? inputChunks : [inputChunks];
|
|
604
709
|
const ops = chunks.flatMap((tx) => getOps(tx));
|
|
605
710
|
const [newAttrs, addAttrTxSteps] = createMissingAttrs(ctx, ops);
|
|
606
|
-
const newCtx = { ...ctx,
|
|
711
|
+
const newCtx = { ...ctx, attrsStore: newAttrs };
|
|
607
712
|
const txSteps = ops.flatMap((op) => toTxSteps(newCtx, op));
|
|
608
713
|
return [...addAttrTxSteps, ...txSteps];
|
|
609
714
|
}
|