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