@instantdb/core 0.22.86-experimental.separate-attrs.20122276424.1 → 0.22.86-experimental.split-store.20183617880.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} +188 -210
- 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 +105 -76
- 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 +170 -70
- 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 +102 -71
- 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 +167 -70
- package/dist/esm/store.js.map +1 -1
- package/dist/standalone/index.js +1580 -1392
- package/dist/standalone/index.umd.cjs +3 -3
- package/package.json +2 -2
- package/src/Reactor.js +154 -77
- package/src/SyncTable.ts +85 -45
- package/src/{instaml.js → instaml.ts} +196 -95
- package/src/instaql.ts +88 -62
- package/src/reactorTypes.ts +32 -0
- package/src/store.ts +248 -90
- 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,72 @@ 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
|
+
// XXX: Better error here
|
|
166
|
+
extractLookup(attrsStore, fwdAttr!['reverse-identity']![1], eidB),
|
|
153
167
|
]
|
|
154
168
|
: [
|
|
155
169
|
'add-triple',
|
|
156
|
-
|
|
157
|
-
revAttr
|
|
158
|
-
|
|
170
|
+
// XXX: Better error here
|
|
171
|
+
extractLookup(attrsStore, revAttr!['forward-identity']![1], eidB),
|
|
172
|
+
revAttr?.id,
|
|
173
|
+
extractLookup(attrsStore, etype, eidA),
|
|
159
174
|
];
|
|
160
175
|
return txStep;
|
|
161
176
|
});
|
|
162
177
|
});
|
|
163
|
-
return withIdAttrForLookup(
|
|
178
|
+
return withIdAttrForLookup(attrsStore, etype, eidA, addTriples);
|
|
164
179
|
}
|
|
165
180
|
|
|
166
|
-
function expandUnlink({
|
|
181
|
+
function expandUnlink({ attrsStore }: Ctx, [etype, eidA, obj]) {
|
|
167
182
|
const retractTriples = Object.entries(obj).flatMap(([label, eidOrEids]) => {
|
|
168
183
|
const eids = Array.isArray(eidOrEids) ? eidOrEids : [eidOrEids];
|
|
169
|
-
const fwdAttr = getAttrByFwdIdentName(
|
|
170
|
-
const revAttr = getAttrByReverseIdentName(
|
|
184
|
+
const fwdAttr = getAttrByFwdIdentName(attrsStore, etype, label);
|
|
185
|
+
const revAttr = getAttrByReverseIdentName(attrsStore, etype, label);
|
|
171
186
|
return eids.map((eidB) => {
|
|
172
187
|
const txStep = fwdAttr
|
|
173
188
|
? [
|
|
174
189
|
'retract-triple',
|
|
175
|
-
extractLookup(
|
|
190
|
+
extractLookup(attrsStore, etype, eidA),
|
|
176
191
|
fwdAttr.id,
|
|
177
|
-
|
|
192
|
+
// XXX: Better error here
|
|
193
|
+
extractLookup(attrsStore, fwdAttr!['reverse-identity']![1], eidB),
|
|
178
194
|
]
|
|
179
195
|
: [
|
|
180
196
|
'retract-triple',
|
|
181
|
-
|
|
182
|
-
revAttr
|
|
183
|
-
|
|
197
|
+
// XXX: Better error here
|
|
198
|
+
extractLookup(attrsStore, revAttr!['forward-identity'][1], eidB),
|
|
199
|
+
revAttr!.id,
|
|
200
|
+
extractLookup(attrsStore, etype, eidA),
|
|
184
201
|
];
|
|
185
202
|
return txStep;
|
|
186
203
|
});
|
|
187
204
|
});
|
|
188
|
-
return withIdAttrForLookup(
|
|
205
|
+
return withIdAttrForLookup(attrsStore, etype, eidA, retractTriples);
|
|
189
206
|
}
|
|
190
207
|
|
|
191
|
-
function checkEntityExists(
|
|
208
|
+
function checkEntityExists(
|
|
209
|
+
stores: (Store | undefined)[] | undefined,
|
|
210
|
+
attrsStore: AttrsStore,
|
|
211
|
+
etype: string,
|
|
212
|
+
eid: string,
|
|
213
|
+
) {
|
|
192
214
|
if (Array.isArray(eid)) {
|
|
193
215
|
// lookup ref
|
|
194
216
|
const [entity_a, entity_v] = eid;
|
|
@@ -209,7 +231,7 @@ function checkEntityExists(stores, etype, eid) {
|
|
|
209
231
|
const av = store?.eav.get(eid);
|
|
210
232
|
if (av) {
|
|
211
233
|
for (const attr_id of av.keys()) {
|
|
212
|
-
if (
|
|
234
|
+
if (attrsStore.getAttr(attr_id)?.['forward-identity'][1] == etype) {
|
|
213
235
|
return true;
|
|
214
236
|
}
|
|
215
237
|
}
|
|
@@ -219,26 +241,34 @@ function checkEntityExists(stores, etype, eid) {
|
|
|
219
241
|
return false;
|
|
220
242
|
}
|
|
221
243
|
|
|
222
|
-
|
|
244
|
+
type Ctx = {
|
|
245
|
+
stores?: (Store | undefined)[];
|
|
246
|
+
attrsStore: AttrsStore;
|
|
247
|
+
schema?: Schema;
|
|
248
|
+
useDateObjects?: boolean | null;
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
function convertOpts({ stores, attrsStore }: Ctx, [etype, eid, obj_, opts]) {
|
|
223
252
|
return opts?.upsert === false
|
|
224
253
|
? { mode: 'update' }
|
|
225
254
|
: opts?.upsert === true
|
|
226
255
|
? null
|
|
227
|
-
: checkEntityExists(stores, etype, eid)
|
|
256
|
+
: checkEntityExists(stores, attrsStore, etype, eid)
|
|
228
257
|
? { mode: 'update' }
|
|
229
258
|
: null; // auto mode chooses between update and upsert, not update and create, just in case
|
|
230
259
|
}
|
|
231
260
|
|
|
232
|
-
function expandCreate(ctx, step) {
|
|
233
|
-
const {
|
|
261
|
+
function expandCreate(ctx: Ctx, step) {
|
|
262
|
+
const { attrsStore } = ctx;
|
|
234
263
|
const [etype, eid, obj_, opts] = step;
|
|
235
264
|
const obj = immutableRemoveUndefined(obj_);
|
|
236
|
-
const lookup = extractLookup(
|
|
265
|
+
const lookup = extractLookup(attrsStore, etype, eid);
|
|
237
266
|
// id first so that we don't clobber updates on the lookup field
|
|
238
267
|
const attrTuples = [['id', lookup]]
|
|
239
268
|
.concat(Object.entries(obj))
|
|
240
|
-
.map(([identName, value]) => {
|
|
241
|
-
|
|
269
|
+
.map(([identName, value]: [string, any]) => {
|
|
270
|
+
// XXX: missing attr?
|
|
271
|
+
const attr = getAttrByFwdIdentName(attrsStore, etype, identName)!;
|
|
242
272
|
|
|
243
273
|
if (attr['checked-data-type'] === 'date' && ctx.useDateObjects) {
|
|
244
274
|
value = coerceToDate(value);
|
|
@@ -249,17 +279,17 @@ function expandCreate(ctx, step) {
|
|
|
249
279
|
return attrTuples;
|
|
250
280
|
}
|
|
251
281
|
|
|
252
|
-
function expandUpdate(ctx, step) {
|
|
253
|
-
const {
|
|
282
|
+
function expandUpdate(ctx: Ctx, step) {
|
|
283
|
+
const { attrsStore } = ctx;
|
|
254
284
|
const [etype, eid, obj_, opts] = step;
|
|
255
285
|
const obj = immutableRemoveUndefined(obj_);
|
|
256
|
-
const lookup = extractLookup(
|
|
286
|
+
const lookup = extractLookup(attrsStore, etype, eid);
|
|
257
287
|
const serverOpts = convertOpts(ctx, [etype, lookup, obj_, opts]);
|
|
258
288
|
// id first so that we don't clobber updates on the lookup field
|
|
259
289
|
const attrTuples = [['id', lookup]]
|
|
260
290
|
.concat(Object.entries(obj))
|
|
261
|
-
.map(([identName, value]) => {
|
|
262
|
-
const attr = getAttrByFwdIdentName(
|
|
291
|
+
.map(([identName, value]: [string, any]) => {
|
|
292
|
+
const attr = getAttrByFwdIdentName(attrsStore, etype, identName)!;
|
|
263
293
|
|
|
264
294
|
if (attr['checked-data-type'] === 'date' && ctx.useDateObjects) {
|
|
265
295
|
value = coerceToDate(value);
|
|
@@ -276,19 +306,19 @@ function expandUpdate(ctx, step) {
|
|
|
276
306
|
return attrTuples;
|
|
277
307
|
}
|
|
278
308
|
|
|
279
|
-
function expandDelete({
|
|
280
|
-
const lookup = extractLookup(
|
|
309
|
+
function expandDelete({ attrsStore }: Ctx, [etype, eid]) {
|
|
310
|
+
const lookup = extractLookup(attrsStore, etype, eid);
|
|
281
311
|
return [['delete-entity', lookup, etype]];
|
|
282
312
|
}
|
|
283
313
|
|
|
284
|
-
function expandDeepMerge(ctx, step) {
|
|
285
|
-
const {
|
|
314
|
+
function expandDeepMerge(ctx: Ctx, step) {
|
|
315
|
+
const { attrsStore } = ctx;
|
|
286
316
|
const [etype, eid, obj_, opts] = step;
|
|
287
317
|
const obj = immutableRemoveUndefined(obj_);
|
|
288
|
-
const lookup = extractLookup(
|
|
318
|
+
const lookup = extractLookup(attrsStore, etype, eid);
|
|
289
319
|
const serverOpts = convertOpts(ctx, [etype, lookup, obj_, opts]);
|
|
290
320
|
const attrTuples = Object.entries(obj).map(([identName, value]) => {
|
|
291
|
-
const attr = getAttrByFwdIdentName(
|
|
321
|
+
const attr = getAttrByFwdIdentName(attrsStore, etype, identName)!;
|
|
292
322
|
return [
|
|
293
323
|
'deep-merge-triple',
|
|
294
324
|
lookup,
|
|
@@ -301,7 +331,7 @@ function expandDeepMerge(ctx, step) {
|
|
|
301
331
|
const idTuple = [
|
|
302
332
|
'add-triple',
|
|
303
333
|
lookup,
|
|
304
|
-
getAttrByFwdIdentName(
|
|
334
|
+
getAttrByFwdIdentName(attrsStore, etype, 'id')!.id,
|
|
305
335
|
lookup,
|
|
306
336
|
...(serverOpts ? [serverOpts] : []),
|
|
307
337
|
];
|
|
@@ -310,8 +340,8 @@ function expandDeepMerge(ctx, step) {
|
|
|
310
340
|
return [idTuple].concat(attrTuples);
|
|
311
341
|
}
|
|
312
342
|
|
|
313
|
-
function expandRuleParams({
|
|
314
|
-
const lookup = extractLookup(
|
|
343
|
+
function expandRuleParams({ attrsStore }: Ctx, [etype, eid, ruleParams]) {
|
|
344
|
+
const lookup = extractLookup(attrsStore, etype, eid);
|
|
315
345
|
return [['rule-params', lookup, etype, ruleParams]];
|
|
316
346
|
}
|
|
317
347
|
|
|
@@ -325,7 +355,7 @@ function removeIdFromArgs(step) {
|
|
|
325
355
|
return [op, etype, eid, newObj, ...(opts ? [opts] : [])];
|
|
326
356
|
}
|
|
327
357
|
|
|
328
|
-
function toTxSteps(ctx, step) {
|
|
358
|
+
function toTxSteps(ctx: Ctx, step) {
|
|
329
359
|
const [action, ...args] = removeIdFromArgs(step);
|
|
330
360
|
switch (action) {
|
|
331
361
|
case 'merge':
|
|
@@ -398,8 +428,12 @@ function createObjectAttr(schema, etype, label, props) {
|
|
|
398
428
|
};
|
|
399
429
|
}
|
|
400
430
|
|
|
401
|
-
|
|
402
|
-
|
|
431
|
+
type Link = LinkDef<any, any, any, any, any, any, any>;
|
|
432
|
+
type Schema = IContainEntitiesAndLinks<any, any>;
|
|
433
|
+
|
|
434
|
+
function findSchemaLink(schema: Schema, etype, label): Link | undefined {
|
|
435
|
+
const links: Link[] = Object.values(schema.links);
|
|
436
|
+
const found = links.find((x: Link) => {
|
|
403
437
|
return (
|
|
404
438
|
(x.forward.on === etype && x.forward.label === label) ||
|
|
405
439
|
(x.reverse.on === etype && x.reverse.label === label)
|
|
@@ -408,7 +442,7 @@ function findSchemaLink(schema, etype, label) {
|
|
|
408
442
|
return found;
|
|
409
443
|
}
|
|
410
444
|
|
|
411
|
-
function refPropsFromSchema(schema, etype, label) {
|
|
445
|
+
function refPropsFromSchema(schema: Schema, etype, label) {
|
|
412
446
|
const found = findSchemaLink(schema, etype, label);
|
|
413
447
|
if (!found) {
|
|
414
448
|
throw new Error(`Couldn't find the link ${etype}.${label} in your schema`);
|
|
@@ -424,18 +458,26 @@ function refPropsFromSchema(schema, etype, label) {
|
|
|
424
458
|
};
|
|
425
459
|
}
|
|
426
460
|
|
|
427
|
-
function createRefAttr(
|
|
461
|
+
function createRefAttr(
|
|
462
|
+
schema: Schema | undefined,
|
|
463
|
+
etype: string,
|
|
464
|
+
label: string,
|
|
465
|
+
props?: Partial<InstantDBAttr> | undefined,
|
|
466
|
+
): InstantDBAttr {
|
|
428
467
|
const schemaRefProps = schema
|
|
429
468
|
? refPropsFromSchema(schema, etype, label)
|
|
430
469
|
: null;
|
|
431
470
|
const attrId = uuid();
|
|
432
|
-
const fwdIdent = [uuid(), etype, label];
|
|
433
|
-
const revIdent = [uuid(), label, etype];
|
|
471
|
+
const fwdIdent: InstantDBIdent = [uuid(), etype, label];
|
|
472
|
+
const revIdent: InstantDBIdent = [uuid(), label, etype];
|
|
434
473
|
return {
|
|
435
474
|
id: attrId,
|
|
475
|
+
// @ts-ignore: ts thinks it's any[]
|
|
436
476
|
'forward-identity': fwdIdent,
|
|
477
|
+
// @ts-ignore: ts thinks it's any[]
|
|
437
478
|
'reverse-identity': revIdent,
|
|
438
479
|
'value-type': 'ref',
|
|
480
|
+
// @ts-ignore: ts thinks it's type string
|
|
439
481
|
cardinality: 'many',
|
|
440
482
|
'unique?': false,
|
|
441
483
|
'index?': false,
|
|
@@ -459,11 +501,14 @@ const SUPPORTS_LOOKUP_ACTIONS = new Set([
|
|
|
459
501
|
'ruleParams',
|
|
460
502
|
]);
|
|
461
503
|
|
|
462
|
-
const lookupProps = { 'unique?': true, 'index?': true };
|
|
463
|
-
const refLookupProps = {
|
|
504
|
+
const lookupProps: Partial<InstantDBAttr> = { 'unique?': true, 'index?': true };
|
|
505
|
+
const refLookupProps: Partial<InstantDBAttr> = {
|
|
506
|
+
...lookupProps,
|
|
507
|
+
cardinality: 'one',
|
|
508
|
+
};
|
|
464
509
|
|
|
465
510
|
function lookupPairsOfOp(op) {
|
|
466
|
-
const res = [];
|
|
511
|
+
const res: { etype: string; lookupPair: any; linkLabel?: string }[] = [];
|
|
467
512
|
const [action, etype, eid, obj] = op;
|
|
468
513
|
if (!SUPPORTS_LOOKUP_ACTIONS.has(action)) {
|
|
469
514
|
return res;
|
|
@@ -491,24 +536,72 @@ function lookupPairsOfOp(op) {
|
|
|
491
536
|
return res;
|
|
492
537
|
}
|
|
493
538
|
|
|
494
|
-
function createMissingAttrs(
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
539
|
+
function createMissingAttrs(
|
|
540
|
+
{ attrsStore, schema }: Ctx,
|
|
541
|
+
ops,
|
|
542
|
+
): [AttrsStore, TXStep[]] {
|
|
543
|
+
const addedIds = new Set();
|
|
544
|
+
const localAttrs: InstantDBAttr[] = [];
|
|
545
|
+
const addOps: TXStep[] = [];
|
|
546
|
+
|
|
547
|
+
function attrByFwdIdent(etype, label): InstantDBAttr | undefined {
|
|
548
|
+
return (
|
|
549
|
+
getAttrByFwdIdentName(attrsStore, etype, label) ||
|
|
550
|
+
localAttrs.find(
|
|
551
|
+
(x) =>
|
|
552
|
+
x['forward-identity'][1] === etype &&
|
|
553
|
+
x['forward-identity'][2] === label,
|
|
554
|
+
)
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function attrByRevIdent(etype, label): InstantDBAttr | undefined {
|
|
559
|
+
return (
|
|
560
|
+
getAttrByReverseIdentName(attrsStore, etype, label) ||
|
|
561
|
+
localAttrs.find(
|
|
562
|
+
(x) =>
|
|
563
|
+
x['reverse-identity']?.[1] === etype &&
|
|
564
|
+
x['reverse-identity']?.[2] === label,
|
|
565
|
+
)
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function addAttr(attr: InstantDBAttr) {
|
|
570
|
+
localAttrs.push(attr);
|
|
498
571
|
addOps.push(['add-attr', attr]);
|
|
499
572
|
addedIds.add(attr.id);
|
|
500
573
|
}
|
|
501
|
-
function addUnsynced(
|
|
502
|
-
|
|
574
|
+
function addUnsynced(
|
|
575
|
+
attr:
|
|
576
|
+
| (InstantDBAttr & { isUnsynced?: boolean })
|
|
577
|
+
| InstantDBAttr
|
|
578
|
+
| undefined,
|
|
579
|
+
) {
|
|
580
|
+
if (
|
|
581
|
+
attr &&
|
|
582
|
+
'isUnsynced' in attr &&
|
|
583
|
+
attr.isUnsynced &&
|
|
584
|
+
!addedIds.has(attr.id)
|
|
585
|
+
) {
|
|
586
|
+
localAttrs.push(attr);
|
|
503
587
|
addOps.push(['add-attr', attr]);
|
|
504
588
|
addedIds.add(attr.id);
|
|
505
589
|
}
|
|
506
590
|
}
|
|
507
591
|
|
|
592
|
+
function isRefLookupIdentLocal(etype: string, identName: string) {
|
|
593
|
+
return (
|
|
594
|
+
identName.indexOf('.') !== -1 &&
|
|
595
|
+
// attr names can have `.` in them, so use the attr we find with a `.`
|
|
596
|
+
// before assuming it's a ref lookup.
|
|
597
|
+
!attrByFwdIdent(etype, identName)
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
|
|
508
601
|
// Adds attrs needed for a ref lookup
|
|
509
602
|
function addForRef(etype, label) {
|
|
510
|
-
const fwdAttr =
|
|
511
|
-
const revAttr =
|
|
603
|
+
const fwdAttr = attrByFwdIdent(etype, label);
|
|
604
|
+
const revAttr = attrByRevIdent(etype, label);
|
|
512
605
|
addUnsynced(fwdAttr);
|
|
513
606
|
addUnsynced(revAttr);
|
|
514
607
|
if (!fwdAttr && !revAttr) {
|
|
@@ -530,18 +623,18 @@ function createMissingAttrs({ attrs: existingAttrs, schema }, ops) {
|
|
|
530
623
|
|
|
531
624
|
// Figure out the link etype so we can make sure we have the attrs
|
|
532
625
|
// for the link lookup
|
|
533
|
-
const fwdAttr =
|
|
534
|
-
const revAttr =
|
|
626
|
+
const fwdAttr = attrByFwdIdent(etype, linkLabel);
|
|
627
|
+
const revAttr = attrByRevIdent(etype, linkLabel);
|
|
535
628
|
addUnsynced(fwdAttr);
|
|
536
629
|
addUnsynced(revAttr);
|
|
537
630
|
const linkEtype =
|
|
538
631
|
fwdAttr?.['reverse-identity']?.[1] ||
|
|
539
632
|
revAttr?.['forward-identity']?.[1] ||
|
|
540
633
|
linkLabel;
|
|
541
|
-
if (
|
|
634
|
+
if (isRefLookupIdentLocal(linkEtype, identName)) {
|
|
542
635
|
addForRef(linkEtype, extractRefLookupFwdName(identName));
|
|
543
636
|
} else {
|
|
544
|
-
const attr =
|
|
637
|
+
const attr = attrByFwdIdent(linkEtype, identName);
|
|
545
638
|
if (!attr) {
|
|
546
639
|
addAttr(
|
|
547
640
|
createObjectAttr(schema, linkEtype, identName, lookupProps),
|
|
@@ -549,10 +642,10 @@ function createMissingAttrs({ attrs: existingAttrs, schema }, ops) {
|
|
|
549
642
|
}
|
|
550
643
|
addUnsynced(attr);
|
|
551
644
|
}
|
|
552
|
-
} else if (
|
|
645
|
+
} else if (isRefLookupIdentLocal(etype, identName)) {
|
|
553
646
|
addForRef(etype, extractRefLookupFwdName(identName));
|
|
554
647
|
} else {
|
|
555
|
-
const attr =
|
|
648
|
+
const attr = attrByFwdIdent(etype, identName);
|
|
556
649
|
if (!attr) {
|
|
557
650
|
addAttr(createObjectAttr(schema, etype, identName, lookupProps));
|
|
558
651
|
}
|
|
@@ -565,14 +658,14 @@ function createMissingAttrs({ attrs: existingAttrs, schema }, ops) {
|
|
|
565
658
|
for (const op of ops) {
|
|
566
659
|
const [action, etype, eid, obj] = op;
|
|
567
660
|
if (OBJ_ACTIONS.has(action)) {
|
|
568
|
-
const idAttr =
|
|
661
|
+
const idAttr = attrByFwdIdent(etype, 'id');
|
|
569
662
|
addUnsynced(idAttr);
|
|
570
663
|
if (!idAttr) {
|
|
571
664
|
addAttr(createObjectAttr(schema, etype, 'id', { 'unique?': true }));
|
|
572
665
|
}
|
|
573
666
|
|
|
574
667
|
for (const label of Object.keys(obj)) {
|
|
575
|
-
const fwdAttr =
|
|
668
|
+
const fwdAttr = attrByFwdIdent(etype, label);
|
|
576
669
|
addUnsynced(fwdAttr);
|
|
577
670
|
if (UPDATE_ACTIONS.has(action)) {
|
|
578
671
|
if (!fwdAttr) {
|
|
@@ -587,7 +680,7 @@ function createMissingAttrs({ attrs: existingAttrs, schema }, ops) {
|
|
|
587
680
|
}
|
|
588
681
|
}
|
|
589
682
|
if (REF_ACTIONS.has(action)) {
|
|
590
|
-
const revAttr =
|
|
683
|
+
const revAttr = attrByRevIdent(etype, label);
|
|
591
684
|
if (!fwdAttr && !revAttr) {
|
|
592
685
|
addAttr(createRefAttr(schema, etype, label));
|
|
593
686
|
}
|
|
@@ -596,14 +689,22 @@ function createMissingAttrs({ attrs: existingAttrs, schema }, ops) {
|
|
|
596
689
|
}
|
|
597
690
|
}
|
|
598
691
|
}
|
|
599
|
-
|
|
692
|
+
|
|
693
|
+
if (localAttrs.length) {
|
|
694
|
+
const nextAttrs = { ...attrsStore.attrs };
|
|
695
|
+
for (const attr of localAttrs) {
|
|
696
|
+
nextAttrs[attr.id] = attr;
|
|
697
|
+
}
|
|
698
|
+
return [new AttrsStoreClass(nextAttrs, attrsStore.linkIndex), addOps];
|
|
699
|
+
}
|
|
700
|
+
return [attrsStore, addOps];
|
|
600
701
|
}
|
|
601
702
|
|
|
602
|
-
export function transform(ctx, inputChunks) {
|
|
703
|
+
export function transform(ctx: Ctx, inputChunks) {
|
|
603
704
|
const chunks = Array.isArray(inputChunks) ? inputChunks : [inputChunks];
|
|
604
705
|
const ops = chunks.flatMap((tx) => getOps(tx));
|
|
605
706
|
const [newAttrs, addAttrTxSteps] = createMissingAttrs(ctx, ops);
|
|
606
|
-
const newCtx = { ...ctx,
|
|
707
|
+
const newCtx = { ...ctx, attrsStore: newAttrs };
|
|
607
708
|
const txSteps = ops.flatMap((op) => toTxSteps(newCtx, op));
|
|
608
709
|
return [...addAttrTxSteps, ...txSteps];
|
|
609
710
|
}
|