@instantdb/core 0.22.96-experimental.add-posthog-frontend.20386914944.1 → 0.22.96
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/serializeSchema.test.ts +123 -0
- package/dist/commonjs/Reactor.d.ts +13 -1
- package/dist/commonjs/Reactor.d.ts.map +1 -1
- package/dist/commonjs/Reactor.js +72 -5
- package/dist/commonjs/Reactor.js.map +1 -1
- package/dist/commonjs/SyncTable.d.ts.map +1 -1
- package/dist/commonjs/SyncTable.js +7 -6
- 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 +66 -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 +228 -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/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 +5 -4
- package/dist/commonjs/reactorTypes.d.ts.map +1 -1
- package/dist/commonjs/reactorTypes.js.map +1 -1
- package/dist/esm/Reactor.d.ts +13 -1
- package/dist/esm/Reactor.d.ts.map +1 -1
- package/dist/esm/Reactor.js +72 -5
- package/dist/esm/Reactor.js.map +1 -1
- package/dist/esm/SyncTable.d.ts.map +1 -1
- package/dist/esm/SyncTable.js +7 -6
- 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 +62 -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 +188 -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/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 +5 -4
- package/dist/esm/reactorTypes.d.ts.map +1 -1
- package/dist/esm/reactorTypes.js.map +1 -1
- package/dist/standalone/index.js +2735 -2400
- package/dist/standalone/index.umd.cjs +3 -3
- package/package.json +2 -2
- package/src/Reactor.js +83 -6
- package/src/SyncTable.ts +18 -14
- package/src/createRouteHandler.ts +63 -0
- package/src/framework.ts +318 -0
- package/src/index.ts +9 -0
- package/src/parseSchemaFromJSON.ts +176 -0
- package/src/reactorTypes.ts +5 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@instantdb/core",
|
|
3
|
-
"version": "0.22.96
|
|
3
|
+
"version": "0.22.96",
|
|
4
4
|
"description": "Instant's core local abstraction",
|
|
5
5
|
"homepage": "https://github.com/instantdb/instant/tree/main/client/packages/core",
|
|
6
6
|
"repository": {
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"mutative": "^1.0.10",
|
|
55
55
|
"uuid": "^11.1.0",
|
|
56
|
-
"@instantdb/version": "0.22.96
|
|
56
|
+
"@instantdb/version": "0.22.96"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"test": "vitest",
|
package/src/Reactor.js
CHANGED
|
@@ -50,6 +50,7 @@ const STATUS = {
|
|
|
50
50
|
const QUERY_ONCE_TIMEOUT = 30_000;
|
|
51
51
|
const PENDING_TX_CLEANUP_TIMEOUT = 30_000;
|
|
52
52
|
const PENDING_MUTATION_CLEANUP_THRESHOLD = 200;
|
|
53
|
+
const ONE_MIN_MS = 1_000 * 60;
|
|
53
54
|
|
|
54
55
|
const defaultConfig = {
|
|
55
56
|
apiURI: 'https://api.instantdb.com',
|
|
@@ -348,7 +349,14 @@ export default class Reactor {
|
|
|
348
349
|
this._oauthCallbackResponse = this._oauthLoginInit();
|
|
349
350
|
|
|
350
351
|
// kick off a request to cache it
|
|
351
|
-
this.getCurrentUser()
|
|
352
|
+
this.getCurrentUser().then((userInfo) => {
|
|
353
|
+
this.syncUserToEndpoint(userInfo.user);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
setInterval(async () => {
|
|
357
|
+
const currentUser = await this.getCurrentUser();
|
|
358
|
+
this.syncUserToEndpoint(currentUser.user);
|
|
359
|
+
}, ONE_MIN_MS);
|
|
352
360
|
|
|
353
361
|
NetworkListener.getIsOnline().then((isOnline) => {
|
|
354
362
|
this._isOnline = isOnline;
|
|
@@ -549,6 +557,43 @@ export default class Reactor {
|
|
|
549
557
|
}
|
|
550
558
|
}
|
|
551
559
|
|
|
560
|
+
/**
|
|
561
|
+
* Does the same thing as add-query-ok
|
|
562
|
+
* but called as a result of receiving query info from ssr
|
|
563
|
+
* @param {any} q
|
|
564
|
+
* @param {{ triples: any; pageInfo: any; }} result
|
|
565
|
+
* @param {boolean} enableCardinalityInference
|
|
566
|
+
*/
|
|
567
|
+
_addQueryData(q, result, enableCardinalityInference) {
|
|
568
|
+
if (!this.attrs) {
|
|
569
|
+
throw new Error('Attrs in reactor have not been set');
|
|
570
|
+
}
|
|
571
|
+
const queryHash = weakHash(q);
|
|
572
|
+
const attrsStore = this.ensureAttrs();
|
|
573
|
+
const store = s.createStore(
|
|
574
|
+
this.attrs,
|
|
575
|
+
result.triples,
|
|
576
|
+
enableCardinalityInference,
|
|
577
|
+
this.config.useDateObjects,
|
|
578
|
+
);
|
|
579
|
+
this.querySubs.updateInPlace((prev) => {
|
|
580
|
+
prev[queryHash] = {
|
|
581
|
+
result: {
|
|
582
|
+
store,
|
|
583
|
+
attrsStore,
|
|
584
|
+
pageInfo: result.pageInfo,
|
|
585
|
+
processedTxId: undefined,
|
|
586
|
+
isExternal: true,
|
|
587
|
+
},
|
|
588
|
+
q,
|
|
589
|
+
};
|
|
590
|
+
});
|
|
591
|
+
this._cleanupPendingMutationsQueries();
|
|
592
|
+
this.notifyOne(queryHash);
|
|
593
|
+
this.notifyOneQueryOnce(queryHash);
|
|
594
|
+
this._cleanupPendingMutationsTimeout();
|
|
595
|
+
}
|
|
596
|
+
|
|
552
597
|
_handleReceive(connId, msg) {
|
|
553
598
|
// opt-out, enabled by default if schema
|
|
554
599
|
const enableCardinalityInference =
|
|
@@ -1221,7 +1266,7 @@ export default class Reactor {
|
|
|
1221
1266
|
}
|
|
1222
1267
|
|
|
1223
1268
|
/** Runs instaql on a query and a store */
|
|
1224
|
-
dataForQuery(hash) {
|
|
1269
|
+
dataForQuery(hash, applyOptimistic = true) {
|
|
1225
1270
|
const errorMessage = this._errorMessage;
|
|
1226
1271
|
if (errorMessage) {
|
|
1227
1272
|
return { error: errorMessage };
|
|
@@ -1245,15 +1290,26 @@ export default class Reactor {
|
|
|
1245
1290
|
return cached;
|
|
1246
1291
|
}
|
|
1247
1292
|
|
|
1248
|
-
|
|
1293
|
+
let store = result.store;
|
|
1294
|
+
let attrsStore = result.attrsStore;
|
|
1295
|
+
const { pageInfo, aggregate, processedTxId } = result;
|
|
1249
1296
|
const mutations = this._rewriteMutationsSorted(
|
|
1250
1297
|
attrsStore,
|
|
1251
1298
|
pendingMutations,
|
|
1252
1299
|
);
|
|
1253
|
-
|
|
1254
|
-
this._applyOptimisticUpdates(
|
|
1300
|
+
if (applyOptimistic) {
|
|
1301
|
+
const optimisticResult = this._applyOptimisticUpdates(
|
|
1302
|
+
store,
|
|
1303
|
+
attrsStore,
|
|
1304
|
+
mutations,
|
|
1305
|
+
processedTxId,
|
|
1306
|
+
);
|
|
1307
|
+
|
|
1308
|
+
store = optimisticResult.store;
|
|
1309
|
+
attrsStore = optimisticResult.attrsStore;
|
|
1310
|
+
}
|
|
1255
1311
|
const resp = instaql(
|
|
1256
|
-
{ store:
|
|
1312
|
+
{ store: store, attrsStore: attrsStore, pageInfo, aggregate },
|
|
1257
1313
|
q,
|
|
1258
1314
|
);
|
|
1259
1315
|
|
|
@@ -1989,7 +2045,28 @@ export default class Reactor {
|
|
|
1989
2045
|
}
|
|
1990
2046
|
}
|
|
1991
2047
|
|
|
2048
|
+
async syncUserToEndpoint(user) {
|
|
2049
|
+
if (!this.config.firstPartyPath) return;
|
|
2050
|
+
try {
|
|
2051
|
+
fetch(this.config.firstPartyPath + '/', {
|
|
2052
|
+
method: 'POST',
|
|
2053
|
+
body: JSON.stringify({
|
|
2054
|
+
type: 'sync-user',
|
|
2055
|
+
appId: this.config.appId,
|
|
2056
|
+
user: user,
|
|
2057
|
+
}),
|
|
2058
|
+
headers: {
|
|
2059
|
+
'Content-Type': 'application/json',
|
|
2060
|
+
},
|
|
2061
|
+
});
|
|
2062
|
+
} catch (error) {
|
|
2063
|
+
this._log.error('Error syncing user with external endpoint', error);
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
|
|
1992
2067
|
updateUser(newUser) {
|
|
2068
|
+
this.syncUserToEndpoint(newUser);
|
|
2069
|
+
|
|
1993
2070
|
const newV = { error: undefined, user: newUser };
|
|
1994
2071
|
this._currentUserCached = { isLoading: false, ...newV };
|
|
1995
2072
|
this._dataForQueryCache = {};
|
package/src/SyncTable.ts
CHANGED
|
@@ -199,20 +199,18 @@ function applyChangesToStore(
|
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
type ChangedFieldsOfChanges = {
|
|
203
|
+
[eid: string]: { [field: string]: { oldValue: unknown; newValue: unknown } };
|
|
204
|
+
};
|
|
205
|
+
|
|
202
206
|
function changedFieldsOfChanges(
|
|
203
207
|
store: s.Store,
|
|
204
208
|
attrsStore: s.AttrsStore,
|
|
205
209
|
changes: SyncUpdateTriplesMsg['txes'][number]['changes'],
|
|
206
|
-
): {
|
|
207
|
-
[eid: string]: SyncTransaction<
|
|
208
|
-
any,
|
|
209
|
-
any,
|
|
210
|
-
any
|
|
211
|
-
>['updated'][number]['changedFields'];
|
|
212
|
-
} {
|
|
210
|
+
): ChangedFieldsOfChanges {
|
|
213
211
|
// This will be more complicated when we include links, we can either add a
|
|
214
212
|
// changedLinks field or we can have something like 'bookshelves.title`
|
|
215
|
-
const changedFields = {};
|
|
213
|
+
const changedFields: ChangedFieldsOfChanges = {};
|
|
216
214
|
for (const { action, triple } of changes) {
|
|
217
215
|
const [e, a, v] = triple;
|
|
218
216
|
const field = attrsStore.getAttr(a)?.['forward-identity']?.[2];
|
|
@@ -222,7 +220,6 @@ function changedFieldsOfChanges(
|
|
|
222
220
|
changedFields[e] = fields;
|
|
223
221
|
|
|
224
222
|
const oldNew = fields[field] ?? {};
|
|
225
|
-
fields[field] = oldNew;
|
|
226
223
|
|
|
227
224
|
switch (action) {
|
|
228
225
|
case 'added':
|
|
@@ -235,12 +232,15 @@ function changedFieldsOfChanges(
|
|
|
235
232
|
}
|
|
236
233
|
break;
|
|
237
234
|
}
|
|
235
|
+
|
|
236
|
+
fields[field] = oldNew;
|
|
238
237
|
}
|
|
239
238
|
|
|
240
|
-
for (const
|
|
241
|
-
const { oldValue, newValue }
|
|
242
|
-
|
|
243
|
-
|
|
239
|
+
for (const [_eid, fields] of Object.entries(changedFields)) {
|
|
240
|
+
for (const [k, { oldValue, newValue }] of Object.entries(fields)) {
|
|
241
|
+
if (oldValue === newValue) {
|
|
242
|
+
delete fields[k];
|
|
243
|
+
}
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
return changedFields;
|
|
@@ -783,7 +783,11 @@ export class SyncTable {
|
|
|
783
783
|
updated.push({
|
|
784
784
|
oldEntity: ent.entity,
|
|
785
785
|
newEntity: entity,
|
|
786
|
-
changedFields: changedFields || {}
|
|
786
|
+
changedFields: (changedFields || {}) as SyncTransaction<
|
|
787
|
+
any,
|
|
788
|
+
any,
|
|
789
|
+
any
|
|
790
|
+
>['updated'][number]['changedFields'],
|
|
787
791
|
});
|
|
788
792
|
ent.entity = entity;
|
|
789
793
|
} else {
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { User } from './clientTypes.js';
|
|
2
|
+
|
|
3
|
+
type CreateRouteHandlerConfig = {
|
|
4
|
+
appId: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
function createUserSyncResponse(
|
|
8
|
+
config: CreateRouteHandlerConfig,
|
|
9
|
+
user: User | null,
|
|
10
|
+
) {
|
|
11
|
+
if (user && user.refresh_token) {
|
|
12
|
+
return new Response(JSON.stringify({ ok: true }), {
|
|
13
|
+
headers: {
|
|
14
|
+
'Content-Type': 'application/json',
|
|
15
|
+
// 7 day expiry
|
|
16
|
+
'Set-Cookie': `instant_user_${config.appId}=${JSON.stringify(user)}; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=604800`,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
} else {
|
|
20
|
+
return new Response(JSON.stringify({ ok: true }), {
|
|
21
|
+
headers: {
|
|
22
|
+
'Content-Type': 'application/json',
|
|
23
|
+
// remove the cookie (some browsers)
|
|
24
|
+
'Set-Cookie': `instant_user_${config.appId}=; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=-1`,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function errorResponse(status: number, message: string) {
|
|
31
|
+
return new Response(JSON.stringify({ ok: false, error: message }), {
|
|
32
|
+
status,
|
|
33
|
+
headers: { 'Content-Type': 'application/json' },
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const createInstantRouteHandler = (config: CreateRouteHandlerConfig) => {
|
|
38
|
+
return {
|
|
39
|
+
POST: async (req: Request) => {
|
|
40
|
+
let body: { type?: string; appId?: string; user?: User | null };
|
|
41
|
+
try {
|
|
42
|
+
body = await req.json();
|
|
43
|
+
} catch {
|
|
44
|
+
return errorResponse(400, 'Invalid JSON body');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!body.type) {
|
|
48
|
+
return errorResponse(400, 'Missing "type" field');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (body.appId !== config.appId) {
|
|
52
|
+
return errorResponse(403, 'App ID mismatch');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
switch (body.type) {
|
|
56
|
+
case 'sync-user':
|
|
57
|
+
return createUserSyncResponse(config, body.user ?? null);
|
|
58
|
+
default:
|
|
59
|
+
return errorResponse(400, `Unknown type: ${body.type}`);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
};
|
package/src/framework.ts
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
// The FrameworkClient class is a mini version of a query store that allows making queries on both the frontend and backend
|
|
2
|
+
// you can register queries, await their results and serialize them over a server/client boundary.
|
|
3
|
+
// The class is generic so that it can be a good starting off point to make other ssr adapters.
|
|
4
|
+
import {
|
|
5
|
+
coerceQuery,
|
|
6
|
+
InstantCoreDatabase,
|
|
7
|
+
InstantDBAttr,
|
|
8
|
+
weakHash,
|
|
9
|
+
} from './index.ts';
|
|
10
|
+
import * as s from './store.js';
|
|
11
|
+
import instaql from './instaql.js';
|
|
12
|
+
import { RuleParams } from './schemaTypes.ts';
|
|
13
|
+
import { createLinkIndex } from './utils/linkIndex.ts';
|
|
14
|
+
|
|
15
|
+
export const isServer = typeof window === 'undefined' || 'Deno' in globalThis;
|
|
16
|
+
|
|
17
|
+
export type FrameworkConfig = {
|
|
18
|
+
token?: string | null;
|
|
19
|
+
db: InstantCoreDatabase<any, any>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// represents an eventual result from running a query
|
|
23
|
+
// either via ssr or by using the existing websocket connection.
|
|
24
|
+
type QueryPromise =
|
|
25
|
+
| {
|
|
26
|
+
type: 'http';
|
|
27
|
+
triples: any;
|
|
28
|
+
attrs: any;
|
|
29
|
+
queryHash: any;
|
|
30
|
+
query: any;
|
|
31
|
+
pageInfo?: any;
|
|
32
|
+
}
|
|
33
|
+
| {
|
|
34
|
+
type: 'session';
|
|
35
|
+
queryResult: any;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export class FrameworkClient {
|
|
39
|
+
private params: FrameworkConfig;
|
|
40
|
+
private db: InstantCoreDatabase<any, any>;
|
|
41
|
+
|
|
42
|
+
// stores all of the query promises so that ssr can read them
|
|
43
|
+
// and send the relevant results alongside the html that resulted in the query resolving
|
|
44
|
+
public resultMap: Map<
|
|
45
|
+
string,
|
|
46
|
+
{
|
|
47
|
+
status: 'pending' | 'success' | 'error';
|
|
48
|
+
type: 'http' | 'session';
|
|
49
|
+
promise?: Promise<QueryPromise> | null;
|
|
50
|
+
data?: any;
|
|
51
|
+
error?: any;
|
|
52
|
+
}
|
|
53
|
+
> = new Map();
|
|
54
|
+
|
|
55
|
+
private queryResolvedCallbacks: ((result: {
|
|
56
|
+
triples: any;
|
|
57
|
+
attrs: any;
|
|
58
|
+
queryHash: any;
|
|
59
|
+
query: any;
|
|
60
|
+
pageInfo?: any;
|
|
61
|
+
}) => void)[] = [];
|
|
62
|
+
|
|
63
|
+
constructor(params: FrameworkConfig) {
|
|
64
|
+
this.params = params;
|
|
65
|
+
this.db = params.db;
|
|
66
|
+
this.resultMap = new Map<
|
|
67
|
+
string,
|
|
68
|
+
{
|
|
69
|
+
type: 'http' | 'session';
|
|
70
|
+
status: 'pending' | 'success' | 'error';
|
|
71
|
+
promise?: Promise<QueryPromise>;
|
|
72
|
+
data?: any;
|
|
73
|
+
error?: any;
|
|
74
|
+
}
|
|
75
|
+
>();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public subscribe = (
|
|
79
|
+
callback: (result: {
|
|
80
|
+
triples: any;
|
|
81
|
+
attrs: any;
|
|
82
|
+
queryHash: string;
|
|
83
|
+
pageInfo?: any;
|
|
84
|
+
}) => void,
|
|
85
|
+
) => {
|
|
86
|
+
this.queryResolvedCallbacks.push(callback);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Runs on the client when ssr gets html script tags
|
|
90
|
+
public addQueryResult = (queryKey: string, value: any) => {
|
|
91
|
+
this.resultMap.set(queryKey, {
|
|
92
|
+
type: value.type,
|
|
93
|
+
status: 'success',
|
|
94
|
+
data: value,
|
|
95
|
+
promise: null,
|
|
96
|
+
error: null,
|
|
97
|
+
});
|
|
98
|
+
// send the result to the client
|
|
99
|
+
if (!isServer) {
|
|
100
|
+
// make sure the attrs are there to create stores
|
|
101
|
+
if (!this.db._reactor.attrs) {
|
|
102
|
+
this.db._reactor._setAttrs(value.attrs);
|
|
103
|
+
}
|
|
104
|
+
this.db._reactor._addQueryData(
|
|
105
|
+
value.query,
|
|
106
|
+
value,
|
|
107
|
+
!!this.db._reactor.config.schema,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// creates an entry in the results map
|
|
113
|
+
// and returns the same thing added to the map
|
|
114
|
+
public query = (
|
|
115
|
+
_query: any,
|
|
116
|
+
opts?: {
|
|
117
|
+
ruleParams: RuleParams;
|
|
118
|
+
},
|
|
119
|
+
): {
|
|
120
|
+
type: 'http' | 'session';
|
|
121
|
+
status: 'pending' | 'success' | 'error';
|
|
122
|
+
promise?: Promise<QueryPromise>;
|
|
123
|
+
data?: any;
|
|
124
|
+
error?: any;
|
|
125
|
+
} => {
|
|
126
|
+
const { hash, query } = this.hashQuery(_query, opts);
|
|
127
|
+
|
|
128
|
+
if (this.db._reactor.status === 'authenticated') {
|
|
129
|
+
const promise = this.db.queryOnce(_query, opts);
|
|
130
|
+
let entry = {
|
|
131
|
+
status: 'pending' as 'pending' | 'success' | 'error',
|
|
132
|
+
type: 'session' as 'http' | 'session',
|
|
133
|
+
data: undefined as any,
|
|
134
|
+
error: undefined as any,
|
|
135
|
+
promise: promise as any,
|
|
136
|
+
};
|
|
137
|
+
promise.then((result) => {
|
|
138
|
+
entry.status = 'success';
|
|
139
|
+
entry.data = result;
|
|
140
|
+
entry.promise = null;
|
|
141
|
+
});
|
|
142
|
+
promise.catch((error) => {
|
|
143
|
+
entry.status = 'error';
|
|
144
|
+
entry.error = error;
|
|
145
|
+
entry.promise = null;
|
|
146
|
+
});
|
|
147
|
+
this.resultMap.set(hash, entry);
|
|
148
|
+
return entry as any;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const promise = this.getTriplesAndAttrsForQuery(query);
|
|
152
|
+
let entry = {
|
|
153
|
+
status: 'pending' as 'pending' | 'success' | 'error',
|
|
154
|
+
type: 'http' as 'http' | 'session',
|
|
155
|
+
data: undefined as any,
|
|
156
|
+
error: undefined as any,
|
|
157
|
+
promise: promise as any,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
promise.then((result) => {
|
|
161
|
+
entry.status = 'success';
|
|
162
|
+
entry.data = result;
|
|
163
|
+
entry.promise = null;
|
|
164
|
+
});
|
|
165
|
+
promise.catch((error) => {
|
|
166
|
+
entry.status = 'error';
|
|
167
|
+
entry.error = error;
|
|
168
|
+
entry.promise = null;
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
promise.then((result) => {
|
|
172
|
+
this.queryResolvedCallbacks.forEach((callback) => {
|
|
173
|
+
callback({
|
|
174
|
+
queryHash: hash,
|
|
175
|
+
query: query,
|
|
176
|
+
attrs: result.attrs,
|
|
177
|
+
triples: result.triples,
|
|
178
|
+
pageInfo: result.pageInfo,
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
this.resultMap.set(hash, entry);
|
|
184
|
+
return entry;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
public getExistingResultForQuery = (
|
|
188
|
+
_query: any,
|
|
189
|
+
opts?: {
|
|
190
|
+
ruleParams: RuleParams;
|
|
191
|
+
},
|
|
192
|
+
) => {
|
|
193
|
+
const { hash } = this.hashQuery(_query, opts);
|
|
194
|
+
return this.resultMap.get(hash);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// creates a query result from a set of triples, query, and attrs
|
|
198
|
+
// can be run server side or client side
|
|
199
|
+
public completeIsomorphic = (
|
|
200
|
+
query: any,
|
|
201
|
+
triples: any[],
|
|
202
|
+
attrs: InstantDBAttr[],
|
|
203
|
+
pageInfo?: any,
|
|
204
|
+
) => {
|
|
205
|
+
const attrMap = {};
|
|
206
|
+
attrs.forEach((attr) => {
|
|
207
|
+
attrMap[attr.id] = attr;
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const enableCardinalityInference =
|
|
211
|
+
Boolean(this.db?._reactor?.config?.schema) &&
|
|
212
|
+
('cardinalityInference' in this.db?._reactor?.config
|
|
213
|
+
? Boolean(this.db?._reactor.config?.cardinalityInference)
|
|
214
|
+
: true);
|
|
215
|
+
|
|
216
|
+
const attrsStore = new s.AttrsStoreClass(
|
|
217
|
+
attrs.reduce((acc, attr) => {
|
|
218
|
+
acc[attr.id] = attr;
|
|
219
|
+
return acc;
|
|
220
|
+
}, {}),
|
|
221
|
+
createLinkIndex(this.db?._reactor.config.schema),
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const store = s.createStore(
|
|
225
|
+
attrsStore,
|
|
226
|
+
triples,
|
|
227
|
+
enableCardinalityInference,
|
|
228
|
+
this.params.db._reactor.config.useDateObjects || false,
|
|
229
|
+
);
|
|
230
|
+
const resp = instaql(
|
|
231
|
+
{
|
|
232
|
+
store: store,
|
|
233
|
+
attrsStore: attrsStore,
|
|
234
|
+
pageInfo: pageInfo,
|
|
235
|
+
aggregate: undefined,
|
|
236
|
+
},
|
|
237
|
+
query,
|
|
238
|
+
);
|
|
239
|
+
return resp;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
public hashQuery = (
|
|
243
|
+
_query: any,
|
|
244
|
+
opts?: {
|
|
245
|
+
ruleParams: RuleParams;
|
|
246
|
+
},
|
|
247
|
+
): { hash: string; query: any } => {
|
|
248
|
+
if (_query && opts && 'ruleParams' in opts) {
|
|
249
|
+
_query = { $$ruleParams: opts['ruleParams'], ..._query };
|
|
250
|
+
}
|
|
251
|
+
const query = _query ? coerceQuery(_query) : null;
|
|
252
|
+
return { hash: weakHash(query), query: query };
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// Run by the server to get triples and attrs
|
|
256
|
+
public getTriplesAndAttrsForQuery = async (
|
|
257
|
+
query: any,
|
|
258
|
+
): Promise<{
|
|
259
|
+
triples: any[];
|
|
260
|
+
attrs: InstantDBAttr[];
|
|
261
|
+
query: any;
|
|
262
|
+
queryHash: string;
|
|
263
|
+
type: 'http';
|
|
264
|
+
pageInfo?: any;
|
|
265
|
+
}> => {
|
|
266
|
+
try {
|
|
267
|
+
const response = await fetch(
|
|
268
|
+
`${this.db._reactor.config.apiURI}/runtime/framework/query`,
|
|
269
|
+
{
|
|
270
|
+
method: 'POST',
|
|
271
|
+
headers: {
|
|
272
|
+
'app-id': this.params.db._reactor.config.appId,
|
|
273
|
+
'Content-Type': 'application/json',
|
|
274
|
+
Authorization: this.params.token
|
|
275
|
+
? `Bearer ${this.params.token}`
|
|
276
|
+
: undefined,
|
|
277
|
+
} as Record<string, string>,
|
|
278
|
+
body: JSON.stringify({
|
|
279
|
+
query: query,
|
|
280
|
+
}),
|
|
281
|
+
},
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
if (!response.ok) {
|
|
285
|
+
throw new Error('Error getting triples from server');
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const data = await response.json();
|
|
289
|
+
|
|
290
|
+
const attrs = data?.attrs;
|
|
291
|
+
if (!attrs) {
|
|
292
|
+
throw new Error('No attrs');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// TODO: make safer
|
|
296
|
+
const triples =
|
|
297
|
+
data.result?.[0].data?.['datalog-result']?.['join-rows'][0];
|
|
298
|
+
|
|
299
|
+
const pageInfo = data.result?.[0]?.data?.['page-info'];
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
attrs,
|
|
303
|
+
triples,
|
|
304
|
+
type: 'http',
|
|
305
|
+
queryHash: this.hashQuery(query).hash,
|
|
306
|
+
query,
|
|
307
|
+
pageInfo,
|
|
308
|
+
};
|
|
309
|
+
} catch (err: any) {
|
|
310
|
+
const errWithMessage = new Error(
|
|
311
|
+
'Error getting triples from framework client',
|
|
312
|
+
);
|
|
313
|
+
// @ts-expect-error pre es2022
|
|
314
|
+
errWithMessage.cause = err;
|
|
315
|
+
throw errWithMessage;
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
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
|
+
firstPartyPath?: string;
|
|
156
161
|
apiURI?: string;
|
|
157
162
|
devtool?: boolean | DevtoolConfig;
|
|
158
163
|
verbose?: boolean;
|
|
@@ -903,7 +908,9 @@ export {
|
|
|
903
908
|
validateQuery,
|
|
904
909
|
QueryValidationError,
|
|
905
910
|
validateTransactions,
|
|
911
|
+
parseSchemaFromJSON,
|
|
906
912
|
TransactionValidationError,
|
|
913
|
+
FrameworkClient,
|
|
907
914
|
|
|
908
915
|
// error
|
|
909
916
|
InstantAPIError,
|
|
@@ -1019,6 +1026,7 @@ export {
|
|
|
1019
1026
|
|
|
1020
1027
|
// SSE
|
|
1021
1028
|
type EventSourceType,
|
|
1029
|
+
type FrameworkConfig,
|
|
1022
1030
|
|
|
1023
1031
|
// sync table types
|
|
1024
1032
|
type SyncTableCallback,
|
|
@@ -1035,4 +1043,5 @@ export {
|
|
|
1035
1043
|
// storage (e.g. indexeddb) interface
|
|
1036
1044
|
StorageInterface,
|
|
1037
1045
|
type StorageInterfaceStoreName,
|
|
1046
|
+
createInstantRouteHandler,
|
|
1038
1047
|
};
|