@instantdb/core 0.22.88-experimental.version-bump-0-22-88.20244772799.1 → 0.22.89-experimental.drewh-ssr.20277611943.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/serializeSchema.test.ts +123 -0
- package/__tests__/src/{store.test.js → store.test.ts} +215 -212
- package/dist/commonjs/Reactor.d.ts +36 -7
- package/dist/commonjs/Reactor.d.ts.map +1 -1
- package/dist/commonjs/Reactor.js +176 -47
- 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/createRouteHandler.d.ts +8 -0
- package/dist/commonjs/createRouteHandler.d.ts.map +1 -0
- package/dist/commonjs/createRouteHandler.js +57 -0
- package/dist/commonjs/createRouteHandler.js.map +1 -0
- package/dist/commonjs/framework.d.ts +77 -0
- package/dist/commonjs/framework.d.ts.map +1 -0
- package/dist/commonjs/framework.js +209 -0
- package/dist/commonjs/framework.js.map +1 -0
- package/dist/commonjs/index.d.ts +5 -1
- package/dist/commonjs/index.d.ts.map +1 -1
- package/dist/commonjs/index.js +7 -1
- package/dist/commonjs/index.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/parseSchemaFromJSON.d.ts +3 -0
- package/dist/commonjs/parseSchemaFromJSON.d.ts.map +1 -0
- package/dist/commonjs/parseSchemaFromJSON.js +148 -0
- package/dist/commonjs/parseSchemaFromJSON.js.map +1 -0
- package/dist/commonjs/reactorTypes.d.ts +30 -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 +36 -7
- package/dist/esm/Reactor.d.ts.map +1 -1
- package/dist/esm/Reactor.js +177 -48
- 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/createRouteHandler.d.ts +8 -0
- package/dist/esm/createRouteHandler.d.ts.map +1 -0
- package/dist/esm/createRouteHandler.js +53 -0
- package/dist/esm/createRouteHandler.js.map +1 -0
- package/dist/esm/framework.d.ts +77 -0
- package/dist/esm/framework.d.ts.map +1 -0
- package/dist/esm/framework.js +169 -0
- package/dist/esm/framework.js.map +1 -0
- package/dist/esm/index.d.ts +5 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +5 -2
- package/dist/esm/index.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/parseSchemaFromJSON.d.ts +3 -0
- package/dist/esm/parseSchemaFromJSON.d.ts.map +1 -0
- package/dist/esm/parseSchemaFromJSON.js +144 -0
- package/dist/esm/parseSchemaFromJSON.js.map +1 -0
- package/dist/esm/reactorTypes.d.ts +30 -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 +2899 -2389
- package/dist/standalone/index.umd.cjs +3 -3
- package/package.json +2 -2
- package/src/Reactor.js +232 -77
- package/src/SyncTable.ts +85 -45
- package/src/createRouteHandler.ts +44 -0
- package/src/framework.ts +294 -0
- package/src/index.ts +9 -0
- package/src/{instaml.js → instaml.ts} +201 -96
- package/src/instaql.ts +88 -62
- package/src/parseSchemaFromJSON.ts +176 -0
- package/src/reactorTypes.ts +33 -0
- package/src/store.ts +257 -101
- package/__tests__/src/instaql.bench.js +0 -29
package/src/SyncTable.ts
CHANGED
|
@@ -16,12 +16,12 @@ type SubState = {
|
|
|
16
16
|
|
|
17
17
|
type SubEntity = {
|
|
18
18
|
entity: any;
|
|
19
|
-
store:
|
|
19
|
+
store: s.Store;
|
|
20
20
|
serverCreatedAt: number;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
type SubValues = {
|
|
24
|
-
|
|
24
|
+
attrsStore: s.AttrsStore;
|
|
25
25
|
entities: Array<SubEntity>;
|
|
26
26
|
};
|
|
27
27
|
|
|
@@ -38,8 +38,21 @@ type Sub = {
|
|
|
38
38
|
updatedAt: number;
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
+
type SubEntityInStorage = {
|
|
42
|
+
entity: any;
|
|
43
|
+
store: s.StoreJson;
|
|
44
|
+
serverCreatedAt: number;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
type SubValuesInStorage = {
|
|
48
|
+
attrsStore: s.AttrsStoreJson;
|
|
49
|
+
entities: Array<SubEntityInStorage>;
|
|
50
|
+
};
|
|
51
|
+
|
|
41
52
|
// We could make a better type for this if we had a return type for s.toJSON
|
|
42
|
-
type SubInStorage = Sub
|
|
53
|
+
type SubInStorage = Omit<Sub, 'values'> & {
|
|
54
|
+
values: SubValuesInStorage;
|
|
55
|
+
};
|
|
43
56
|
|
|
44
57
|
type StartMsg = {
|
|
45
58
|
op: 'start-sync';
|
|
@@ -96,36 +109,38 @@ type Config = { useDateObjects: boolean };
|
|
|
96
109
|
function syncSubFromStorage(sub: SubInStorage, useDateObjects: boolean): Sub {
|
|
97
110
|
const values = sub.values;
|
|
98
111
|
if (values) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
e
|
|
102
|
-
|
|
112
|
+
const attrsStore = s.attrsStoreFromJSON(values.attrsStore, null);
|
|
113
|
+
if (attrsStore) {
|
|
114
|
+
for (const e of values.entities || []) {
|
|
115
|
+
e.store.useDateObjects = useDateObjects;
|
|
116
|
+
(e as unknown as SubEntity).store = s.fromJSON(attrsStore, e.store);
|
|
117
|
+
}
|
|
118
|
+
(values as unknown as SubValues).attrsStore = attrsStore;
|
|
103
119
|
}
|
|
104
120
|
}
|
|
105
121
|
|
|
106
|
-
return sub;
|
|
122
|
+
return sub as unknown as Sub;
|
|
107
123
|
}
|
|
108
124
|
|
|
109
125
|
function syncSubToStorage(_k: string, sub: Sub): SubInStorage {
|
|
110
|
-
if (sub.values
|
|
111
|
-
const entities:
|
|
126
|
+
if (sub.values) {
|
|
127
|
+
const entities: SubEntityInStorage[] = [];
|
|
112
128
|
for (const e of sub.values?.entities) {
|
|
113
129
|
const store = s.toJSON(e.store);
|
|
114
|
-
// We'll store the attrs once on values, and put the
|
|
115
|
-
// attrs back into the store on hydration
|
|
116
|
-
// @ts-ignore: ts doesn't want us to delete a non-optional
|
|
117
|
-
delete store['attrs'];
|
|
118
130
|
entities.push({ ...e, store });
|
|
119
131
|
}
|
|
120
|
-
return {
|
|
132
|
+
return {
|
|
133
|
+
...sub,
|
|
134
|
+
values: { attrsStore: sub.values.attrsStore.toJSON(), entities },
|
|
135
|
+
};
|
|
121
136
|
} else {
|
|
122
|
-
return sub;
|
|
137
|
+
return sub as unknown as SubInStorage;
|
|
123
138
|
}
|
|
124
139
|
}
|
|
125
140
|
|
|
126
141
|
function onMergeSub(
|
|
127
142
|
_key: string,
|
|
128
|
-
storageSub:
|
|
143
|
+
storageSub: Sub,
|
|
129
144
|
inMemorySub: Sub | null,
|
|
130
145
|
): Sub {
|
|
131
146
|
const storageTxId = storageSub?.state?.txId;
|
|
@@ -142,13 +157,21 @@ function onMergeSub(
|
|
|
142
157
|
return storageSub || inMemorySub;
|
|
143
158
|
}
|
|
144
159
|
|
|
145
|
-
function queryEntity(sub: Sub, store:
|
|
146
|
-
const res = instaql(
|
|
160
|
+
function queryEntity(sub: Sub, store: s.Store, attrsStore: s.AttrsStore) {
|
|
161
|
+
const res = instaql(
|
|
162
|
+
{ store, attrsStore, pageInfo: null, aggregate: null },
|
|
163
|
+
sub.query,
|
|
164
|
+
);
|
|
147
165
|
return res.data[sub.table][0];
|
|
148
166
|
}
|
|
149
167
|
|
|
150
|
-
function getServerCreatedAt(
|
|
151
|
-
|
|
168
|
+
function getServerCreatedAt(
|
|
169
|
+
sub: Sub,
|
|
170
|
+
store: s.Store,
|
|
171
|
+
attrsStore: s.AttrsStore,
|
|
172
|
+
entityId: string,
|
|
173
|
+
): number {
|
|
174
|
+
const aid = s.getAttrByFwdIdentName(attrsStore, sub.table, 'id')?.id;
|
|
152
175
|
if (!aid) {
|
|
153
176
|
return -1;
|
|
154
177
|
}
|
|
@@ -160,23 +183,25 @@ function getServerCreatedAt(sub: Sub, store: any, entityId: string): number {
|
|
|
160
183
|
}
|
|
161
184
|
|
|
162
185
|
function applyChangesToStore(
|
|
163
|
-
store:
|
|
186
|
+
store: s.Store,
|
|
187
|
+
attrsStore: s.AttrsStore,
|
|
164
188
|
changes: SyncUpdateTriplesMsg['txes'][number]['changes'],
|
|
165
189
|
): void {
|
|
166
190
|
for (const { action, triple } of changes) {
|
|
167
191
|
switch (action) {
|
|
168
192
|
case 'added':
|
|
169
|
-
s.addTriple(store, triple);
|
|
193
|
+
s.addTriple(store, attrsStore, triple);
|
|
170
194
|
break;
|
|
171
195
|
case 'removed':
|
|
172
|
-
s.retractTriple(store, triple);
|
|
196
|
+
s.retractTriple(store, attrsStore, triple);
|
|
173
197
|
break;
|
|
174
198
|
}
|
|
175
199
|
}
|
|
176
200
|
}
|
|
177
201
|
|
|
178
202
|
function changedFieldsOfChanges(
|
|
179
|
-
store:
|
|
203
|
+
store: s.Store,
|
|
204
|
+
attrsStore: s.AttrsStore,
|
|
180
205
|
changes: SyncUpdateTriplesMsg['txes'][number]['changes'],
|
|
181
206
|
): {
|
|
182
207
|
[eid: string]: SyncTransaction<
|
|
@@ -190,7 +215,7 @@ function changedFieldsOfChanges(
|
|
|
190
215
|
const changedFields = {};
|
|
191
216
|
for (const { action, triple } of changes) {
|
|
192
217
|
const [e, a, v] = triple;
|
|
193
|
-
const field =
|
|
218
|
+
const field = attrsStore.getAttr(a)?.['forward-identity']?.[2];
|
|
194
219
|
if (!field) continue;
|
|
195
220
|
|
|
196
221
|
const fields = changedFields[e] ?? {};
|
|
@@ -225,17 +250,19 @@ function subData(sub: Sub, entities: NonNullable<Sub['values']>['entities']) {
|
|
|
225
250
|
return { [sub.table]: entities.map((e) => e.entity) };
|
|
226
251
|
}
|
|
227
252
|
|
|
253
|
+
type CreateStore = (triples: Triple[]) => s.Store;
|
|
254
|
+
|
|
228
255
|
// Updates the sub order field type if it hasn't been set
|
|
229
256
|
// and returns the type. We have to wait until the attrs
|
|
230
257
|
// are loaded before we can determine the type.
|
|
231
|
-
function orderFieldTypeMutative(sub: Sub,
|
|
258
|
+
function orderFieldTypeMutative(sub: Sub, getAttrs: () => s.AttrsStore) {
|
|
232
259
|
if (sub.orderFieldType) {
|
|
233
260
|
return sub.orderFieldType;
|
|
234
261
|
}
|
|
235
262
|
const orderFieldType =
|
|
236
263
|
sub.orderField === 'serverCreatedAt'
|
|
237
264
|
? 'number'
|
|
238
|
-
: s.getAttrByFwdIdentName(
|
|
265
|
+
: s.getAttrByFwdIdentName(getAttrs(), sub.table, sub.orderField)?.[
|
|
239
266
|
'checked-data-type'
|
|
240
267
|
];
|
|
241
268
|
|
|
@@ -414,19 +441,22 @@ export class SyncTable {
|
|
|
414
441
|
private config: Config;
|
|
415
442
|
private idToHash: { [subscriptionId: string]: string } = {};
|
|
416
443
|
private log: Logger;
|
|
417
|
-
private createStore:
|
|
444
|
+
private createStore: CreateStore;
|
|
445
|
+
private getAttrs: () => s.AttrsStore;
|
|
418
446
|
|
|
419
447
|
constructor(
|
|
420
448
|
trySend: TrySend,
|
|
421
449
|
storage: StorageInterface,
|
|
422
450
|
config: Config,
|
|
423
451
|
log: Logger,
|
|
424
|
-
createStore:
|
|
452
|
+
createStore: CreateStore,
|
|
453
|
+
getAttrs: () => s.AttrsStore,
|
|
425
454
|
) {
|
|
426
455
|
this.trySend = trySend;
|
|
427
456
|
this.config = config;
|
|
428
457
|
this.log = log;
|
|
429
458
|
this.createStore = createStore;
|
|
459
|
+
this.getAttrs = getAttrs;
|
|
430
460
|
|
|
431
461
|
this.subs = new PersistedObject<string, Sub, SubInStorage>({
|
|
432
462
|
persister: storage,
|
|
@@ -624,19 +654,23 @@ export class SyncTable {
|
|
|
624
654
|
|
|
625
655
|
const values: SubValues = sub.values ?? {
|
|
626
656
|
entities: [],
|
|
627
|
-
|
|
657
|
+
attrsStore: this.getAttrs(),
|
|
628
658
|
};
|
|
629
659
|
sub.values = values;
|
|
630
660
|
const entities = values.entities;
|
|
631
661
|
|
|
632
662
|
for (const entRows of joinRows) {
|
|
633
663
|
const store = this.createStore(entRows);
|
|
634
|
-
|
|
635
|
-
const entity = queryEntity(sub, store);
|
|
664
|
+
const entity = queryEntity(sub, store, values.attrsStore);
|
|
636
665
|
entities.push({
|
|
637
666
|
store,
|
|
638
667
|
entity,
|
|
639
|
-
serverCreatedAt: getServerCreatedAt(
|
|
668
|
+
serverCreatedAt: getServerCreatedAt(
|
|
669
|
+
sub,
|
|
670
|
+
store,
|
|
671
|
+
values.attrsStore,
|
|
672
|
+
entity.id,
|
|
673
|
+
),
|
|
640
674
|
});
|
|
641
675
|
batch.push(entity);
|
|
642
676
|
}
|
|
@@ -727,7 +761,7 @@ export class SyncTable {
|
|
|
727
761
|
|
|
728
762
|
const values: SubValues = sub.values ?? {
|
|
729
763
|
entities: [],
|
|
730
|
-
|
|
764
|
+
attrsStore: this.getAttrs(),
|
|
731
765
|
};
|
|
732
766
|
const entities = values.entities;
|
|
733
767
|
sub.values = values;
|
|
@@ -738,11 +772,13 @@ export class SyncTable {
|
|
|
738
772
|
for (let i = 0; i < entities.length; i++) {
|
|
739
773
|
const ent = entities[i];
|
|
740
774
|
if (s.hasEntity(ent.store, eid)) {
|
|
741
|
-
applyChangesToStore(ent.store, changes);
|
|
742
|
-
const entity = queryEntity(sub, ent.store);
|
|
743
|
-
const changedFields = changedFieldsOfChanges(
|
|
744
|
-
|
|
745
|
-
|
|
775
|
+
applyChangesToStore(ent.store, values.attrsStore, changes);
|
|
776
|
+
const entity = queryEntity(sub, ent.store, values.attrsStore);
|
|
777
|
+
const changedFields = changedFieldsOfChanges(
|
|
778
|
+
ent.store,
|
|
779
|
+
values.attrsStore,
|
|
780
|
+
changes,
|
|
781
|
+
)[eid];
|
|
746
782
|
if (entity) {
|
|
747
783
|
updated.push({
|
|
748
784
|
oldEntity: ent.entity,
|
|
@@ -763,9 +799,8 @@ export class SyncTable {
|
|
|
763
799
|
// If we have anything left in byEid, then this must be a new entity we don't know about
|
|
764
800
|
for (const [_eid, changes] of Object.entries(byEid)) {
|
|
765
801
|
const store = this.createStore([]);
|
|
766
|
-
values.
|
|
767
|
-
|
|
768
|
-
const entity = queryEntity(sub, store);
|
|
802
|
+
applyChangesToStore(store, values.attrsStore, changes);
|
|
803
|
+
const entity = queryEntity(sub, store, values.attrsStore);
|
|
769
804
|
if (!entity) {
|
|
770
805
|
this.log.error('No entity found after applying change', {
|
|
771
806
|
sub,
|
|
@@ -777,7 +812,12 @@ export class SyncTable {
|
|
|
777
812
|
entities.push({
|
|
778
813
|
store,
|
|
779
814
|
entity,
|
|
780
|
-
serverCreatedAt: getServerCreatedAt(
|
|
815
|
+
serverCreatedAt: getServerCreatedAt(
|
|
816
|
+
sub,
|
|
817
|
+
store,
|
|
818
|
+
values.attrsStore,
|
|
819
|
+
entity.id,
|
|
820
|
+
),
|
|
781
821
|
});
|
|
782
822
|
added.push(entity);
|
|
783
823
|
}
|
|
@@ -789,7 +829,7 @@ export class SyncTable {
|
|
|
789
829
|
entities.splice(idx, 1);
|
|
790
830
|
}
|
|
791
831
|
|
|
792
|
-
const orderFieldType = orderFieldTypeMutative(sub, this.
|
|
832
|
+
const orderFieldType = orderFieldTypeMutative(sub, this.getAttrs);
|
|
793
833
|
|
|
794
834
|
sortEntitiesInPlace(sub, orderFieldType!, entities);
|
|
795
835
|
this.notifyCbs(hash, {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const createInstantRouteHandler = (config: {
|
|
2
|
+
appId: string;
|
|
3
|
+
apiURI?: string;
|
|
4
|
+
}) => {
|
|
5
|
+
async function handleUserSync(req: Request) {
|
|
6
|
+
const body = await req.json();
|
|
7
|
+
if (body.user && body.user.refresh_token) {
|
|
8
|
+
return new Response('sync', {
|
|
9
|
+
headers: {
|
|
10
|
+
// 7 day expiry
|
|
11
|
+
'Set-Cookie': `instant_user=${JSON.stringify(body.user)}; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=604800`,
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
} else {
|
|
15
|
+
return new Response('sync', {
|
|
16
|
+
headers: {
|
|
17
|
+
// remove the cookie (some browsers)
|
|
18
|
+
'Set-Cookie': `instant_user=; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=-1`,
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
GET: async (_req: Request) => {
|
|
26
|
+
return new Response('Method not allowed', {
|
|
27
|
+
status: 405,
|
|
28
|
+
statusText: 'Method Not Allowed',
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
POST: async (req: Request) => {
|
|
32
|
+
const url = new URL(req.url);
|
|
33
|
+
const pathname = url.pathname;
|
|
34
|
+
const route = pathname.split('/')[pathname.split('/').length - 1];
|
|
35
|
+
switch (route) {
|
|
36
|
+
case 'sync-auth':
|
|
37
|
+
return await handleUserSync(req);
|
|
38
|
+
}
|
|
39
|
+
return new Response('Route not found', {
|
|
40
|
+
status: 404,
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
};
|
package/src/framework.ts
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import {
|
|
2
|
+
coerceQuery,
|
|
3
|
+
InstantCoreDatabase,
|
|
4
|
+
InstantDBAttr,
|
|
5
|
+
weakHash,
|
|
6
|
+
} from './index.ts';
|
|
7
|
+
import * as s from './store.js';
|
|
8
|
+
import instaql from './instaql.js';
|
|
9
|
+
import { RuleParams } from './schemaTypes.ts';
|
|
10
|
+
import { createLinkIndex } from './utils/linkIndex.ts';
|
|
11
|
+
|
|
12
|
+
export const isServer = typeof window === 'undefined' || 'Deno' in globalThis;
|
|
13
|
+
|
|
14
|
+
export type FrameworkConfig = {
|
|
15
|
+
token?: string | null;
|
|
16
|
+
db: InstantCoreDatabase<any, any>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type QueryPromise =
|
|
20
|
+
| {
|
|
21
|
+
type: 'http';
|
|
22
|
+
triples: any;
|
|
23
|
+
attrs: any;
|
|
24
|
+
queryHash: any;
|
|
25
|
+
query: any;
|
|
26
|
+
pageInfo?: any;
|
|
27
|
+
}
|
|
28
|
+
| {
|
|
29
|
+
type: 'session';
|
|
30
|
+
queryResult: any;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export class FrameworkClient {
|
|
34
|
+
private params: FrameworkConfig;
|
|
35
|
+
private db: InstantCoreDatabase<any, any>;
|
|
36
|
+
public resultMap: Map<
|
|
37
|
+
string,
|
|
38
|
+
{
|
|
39
|
+
status: 'pending' | 'success' | 'error';
|
|
40
|
+
type: 'http' | 'session';
|
|
41
|
+
promise?: Promise<QueryPromise> | null;
|
|
42
|
+
data?: any;
|
|
43
|
+
error?: any;
|
|
44
|
+
}
|
|
45
|
+
> = new Map();
|
|
46
|
+
|
|
47
|
+
private queryResolvedCallbacks: ((result: {
|
|
48
|
+
triples: any;
|
|
49
|
+
attrs: any;
|
|
50
|
+
queryHash: any;
|
|
51
|
+
query: any;
|
|
52
|
+
pageInfo?: any;
|
|
53
|
+
}) => void)[] = [];
|
|
54
|
+
|
|
55
|
+
constructor(params: FrameworkConfig) {
|
|
56
|
+
this.params = params;
|
|
57
|
+
this.db = params.db;
|
|
58
|
+
this.resultMap = new Map<
|
|
59
|
+
string,
|
|
60
|
+
{
|
|
61
|
+
type: 'http' | 'session';
|
|
62
|
+
status: 'pending' | 'success' | 'error';
|
|
63
|
+
promise?: Promise<QueryPromise>;
|
|
64
|
+
data?: any;
|
|
65
|
+
error?: any;
|
|
66
|
+
}
|
|
67
|
+
>();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public subscribe = (
|
|
71
|
+
callback: (result: {
|
|
72
|
+
triples: any;
|
|
73
|
+
attrs: any;
|
|
74
|
+
queryHash: string;
|
|
75
|
+
pageInfo?: any;
|
|
76
|
+
}) => void,
|
|
77
|
+
) => {
|
|
78
|
+
this.queryResolvedCallbacks.push(callback);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Runs on the client when ssr gets html script tags
|
|
82
|
+
public addQueryResult = (queryKey: string, value: any) => {
|
|
83
|
+
this.resultMap.set(queryKey, {
|
|
84
|
+
type: value.type,
|
|
85
|
+
status: 'success',
|
|
86
|
+
data: value,
|
|
87
|
+
promise: null,
|
|
88
|
+
error: null,
|
|
89
|
+
});
|
|
90
|
+
// send the result to the client
|
|
91
|
+
if (!isServer) {
|
|
92
|
+
// make sure the attrs are there to create stores
|
|
93
|
+
if (!this.db._reactor.attrs) {
|
|
94
|
+
this.db._reactor._setAttrs(value.attrs);
|
|
95
|
+
}
|
|
96
|
+
this.db._reactor._addQueryData(
|
|
97
|
+
value.query,
|
|
98
|
+
value,
|
|
99
|
+
!!this.db._reactor.config.schema,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
public query = (
|
|
105
|
+
_query: any,
|
|
106
|
+
opts?: {
|
|
107
|
+
ruleParams: RuleParams;
|
|
108
|
+
},
|
|
109
|
+
): {
|
|
110
|
+
type: 'http' | 'session';
|
|
111
|
+
status: 'pending' | 'success' | 'error';
|
|
112
|
+
promise?: Promise<QueryPromise>;
|
|
113
|
+
data?: any;
|
|
114
|
+
error?: any;
|
|
115
|
+
} => {
|
|
116
|
+
const { hash, query } = this.hashQuery(_query, opts);
|
|
117
|
+
|
|
118
|
+
if (this.db._reactor.status === 'authenticated') {
|
|
119
|
+
const promise = this.db.queryOnce(_query, opts);
|
|
120
|
+
let entry = {
|
|
121
|
+
status: 'pending' as 'pending' | 'success' | 'error',
|
|
122
|
+
type: 'session' as 'http' | 'session',
|
|
123
|
+
data: undefined as any,
|
|
124
|
+
error: undefined as any,
|
|
125
|
+
promise: promise as any,
|
|
126
|
+
};
|
|
127
|
+
promise.then((result) => {
|
|
128
|
+
entry.status = 'success';
|
|
129
|
+
entry.data = result;
|
|
130
|
+
entry.promise = null;
|
|
131
|
+
});
|
|
132
|
+
promise.catch((error) => {
|
|
133
|
+
entry.status = 'error';
|
|
134
|
+
entry.error = error;
|
|
135
|
+
entry.promise = null;
|
|
136
|
+
});
|
|
137
|
+
return entry as any;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const promise = this.getTriplesAndAttrsForQuery(query);
|
|
141
|
+
let entry = {
|
|
142
|
+
status: 'pending' as 'pending' | 'success' | 'error',
|
|
143
|
+
type: 'http' as 'http' | 'session',
|
|
144
|
+
data: undefined as any,
|
|
145
|
+
error: undefined as any,
|
|
146
|
+
promise: promise as any,
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
promise.then((result) => {
|
|
150
|
+
entry.status = 'success';
|
|
151
|
+
entry.data = result;
|
|
152
|
+
entry.promise = null;
|
|
153
|
+
});
|
|
154
|
+
promise.catch((error) => {
|
|
155
|
+
entry.status = 'error';
|
|
156
|
+
entry.error = error;
|
|
157
|
+
entry.promise = null;
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
promise.then((result) => {
|
|
161
|
+
this.queryResolvedCallbacks.forEach((callback) => {
|
|
162
|
+
callback({
|
|
163
|
+
queryHash: hash,
|
|
164
|
+
query: query,
|
|
165
|
+
attrs: result.attrs,
|
|
166
|
+
triples: result.triples,
|
|
167
|
+
pageInfo: result.pageInfo,
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
this.resultMap.set(hash, entry);
|
|
173
|
+
return entry;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
public getExistingResultForQuery = (
|
|
177
|
+
_query: any,
|
|
178
|
+
opts?: {
|
|
179
|
+
ruleParams: RuleParams;
|
|
180
|
+
},
|
|
181
|
+
) => {
|
|
182
|
+
const { hash } = this.hashQuery(_query, opts);
|
|
183
|
+
return this.resultMap.get(hash);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
public completeIsomorphic = (
|
|
187
|
+
query: any,
|
|
188
|
+
triples: any[],
|
|
189
|
+
attrs: InstantDBAttr[],
|
|
190
|
+
pageInfo?: any,
|
|
191
|
+
) => {
|
|
192
|
+
const attrMap = {};
|
|
193
|
+
attrs.forEach((attr) => {
|
|
194
|
+
attrMap[attr.id] = attr;
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const enableCardinalityInference =
|
|
198
|
+
Boolean(this.db?._reactor?.config?.schema) &&
|
|
199
|
+
('cardinalityInference' in this.db?._reactor?.config
|
|
200
|
+
? Boolean(this.db?._reactor.config?.cardinalityInference)
|
|
201
|
+
: true);
|
|
202
|
+
|
|
203
|
+
const attrsStore = new s.AttrsStoreClass(
|
|
204
|
+
attrs.reduce((acc, attr) => {
|
|
205
|
+
acc[attr.id] = attr;
|
|
206
|
+
return acc;
|
|
207
|
+
}, {}),
|
|
208
|
+
createLinkIndex(this.db?._reactor.config.schema),
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
const store = s.createStore(
|
|
212
|
+
attrsStore,
|
|
213
|
+
triples,
|
|
214
|
+
enableCardinalityInference,
|
|
215
|
+
this.params.db._reactor.config.useDateObjects || false,
|
|
216
|
+
);
|
|
217
|
+
const resp = instaql(
|
|
218
|
+
{
|
|
219
|
+
store: store,
|
|
220
|
+
attrsStore: attrsStore,
|
|
221
|
+
pageInfo: pageInfo,
|
|
222
|
+
aggregate: undefined,
|
|
223
|
+
},
|
|
224
|
+
query,
|
|
225
|
+
);
|
|
226
|
+
return resp;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
public hashQuery = (
|
|
230
|
+
_query: any,
|
|
231
|
+
opts?: {
|
|
232
|
+
ruleParams: RuleParams;
|
|
233
|
+
},
|
|
234
|
+
): { hash: string; query: any } => {
|
|
235
|
+
if (_query && opts && 'ruleParams' in opts) {
|
|
236
|
+
_query = { $$ruleParams: opts['ruleParams'], ..._query };
|
|
237
|
+
}
|
|
238
|
+
const query = _query ? coerceQuery(_query) : null;
|
|
239
|
+
return { hash: weakHash(query), query: query };
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
public getTriplesAndAttrsForQuery = async (
|
|
243
|
+
query: any,
|
|
244
|
+
): Promise<{
|
|
245
|
+
triples: any[];
|
|
246
|
+
attrs: InstantDBAttr[];
|
|
247
|
+
query: any;
|
|
248
|
+
queryHash: string;
|
|
249
|
+
type: 'http';
|
|
250
|
+
pageInfo?: any;
|
|
251
|
+
}> => {
|
|
252
|
+
const response = await fetch(
|
|
253
|
+
`${this.db._reactor.config.apiURI}/runtime/framework/query`,
|
|
254
|
+
{
|
|
255
|
+
method: 'POST',
|
|
256
|
+
headers: {
|
|
257
|
+
'app-id': this.params.db._reactor.config.appId,
|
|
258
|
+
'Content-Type': 'application/json',
|
|
259
|
+
Authorization: this.params.token
|
|
260
|
+
? `Bearer ${this.params.token}`
|
|
261
|
+
: undefined,
|
|
262
|
+
} as Record<string, string>,
|
|
263
|
+
body: JSON.stringify({
|
|
264
|
+
query: query,
|
|
265
|
+
}),
|
|
266
|
+
},
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
if (!response.ok) {
|
|
270
|
+
throw new Error('Error getting triples from server');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const data = await response.json();
|
|
274
|
+
|
|
275
|
+
const attrs = data?.attrs;
|
|
276
|
+
if (!attrs) {
|
|
277
|
+
throw new Error('No attrs');
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// TODO: make safer
|
|
281
|
+
const triples = data.result?.[0].data?.['datalog-result']?.['join-rows'][0];
|
|
282
|
+
|
|
283
|
+
const pageInfo = data.result?.[0]?.data?.['page-info'];
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
attrs,
|
|
287
|
+
triples,
|
|
288
|
+
type: 'http',
|
|
289
|
+
queryHash: this.hashQuery(query).hash,
|
|
290
|
+
query,
|
|
291
|
+
pageInfo,
|
|
292
|
+
};
|
|
293
|
+
};
|
|
294
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -20,10 +20,13 @@ import {
|
|
|
20
20
|
validateTransactions,
|
|
21
21
|
TransactionValidationError,
|
|
22
22
|
} from './transactionValidation.ts';
|
|
23
|
+
|
|
23
24
|
import {
|
|
24
25
|
StorageInterface,
|
|
25
26
|
type StorageInterfaceStoreName,
|
|
26
27
|
} from './utils/PersistedObject.ts';
|
|
28
|
+
import { createInstantRouteHandler } from './createRouteHandler.ts';
|
|
29
|
+
import { parseSchemaFromJSON } from './parseSchemaFromJSON.ts';
|
|
27
30
|
|
|
28
31
|
import type {
|
|
29
32
|
PresenceOpts,
|
|
@@ -103,6 +106,7 @@ import type {
|
|
|
103
106
|
} from './schemaTypes.ts';
|
|
104
107
|
import type { InstantRules } from './rulesTypes.ts';
|
|
105
108
|
import type { UploadFileResponse, DeleteFileResponse } from './StorageAPI.ts';
|
|
109
|
+
import { FrameworkClient, type FrameworkConfig } from './framework.ts';
|
|
106
110
|
|
|
107
111
|
import type {
|
|
108
112
|
ExchangeCodeForTokenParams,
|
|
@@ -153,6 +157,7 @@ export type InstantConfig<
|
|
|
153
157
|
appId: string;
|
|
154
158
|
schema?: S;
|
|
155
159
|
websocketURI?: string;
|
|
160
|
+
cookieEndpoint?: string;
|
|
156
161
|
apiURI?: string;
|
|
157
162
|
devtool?: boolean | DevtoolConfig;
|
|
158
163
|
verbose?: boolean;
|
|
@@ -902,7 +907,9 @@ export {
|
|
|
902
907
|
validateQuery,
|
|
903
908
|
QueryValidationError,
|
|
904
909
|
validateTransactions,
|
|
910
|
+
parseSchemaFromJSON,
|
|
905
911
|
TransactionValidationError,
|
|
912
|
+
FrameworkClient,
|
|
906
913
|
|
|
907
914
|
// error
|
|
908
915
|
InstantAPIError,
|
|
@@ -1018,6 +1025,7 @@ export {
|
|
|
1018
1025
|
|
|
1019
1026
|
// SSE
|
|
1020
1027
|
type EventSourceType,
|
|
1028
|
+
type FrameworkConfig,
|
|
1021
1029
|
|
|
1022
1030
|
// sync table types
|
|
1023
1031
|
type SyncTableCallback,
|
|
@@ -1034,4 +1042,5 @@ export {
|
|
|
1034
1042
|
// storage (e.g. indexeddb) interface
|
|
1035
1043
|
StorageInterface,
|
|
1036
1044
|
type StorageInterfaceStoreName,
|
|
1045
|
+
createInstantRouteHandler,
|
|
1037
1046
|
};
|