@instantdb/core
Advanced tools
Comparing version 0.14.9 to 0.14.10
@@ -169,2 +169,19 @@ import Reactor from "./Reactor"; | ||
shutdown(): void; | ||
/** | ||
* Use this for one-off queries. | ||
* Returns local data if available, otherwise fetches from the server. | ||
* Because we want to avoid stale data, this method will throw an error | ||
* if the user is offline or there is no active connection to the server. | ||
* | ||
* @see https://instantdb.com/docs/instaql | ||
* | ||
* @example | ||
* | ||
* const resp = await db.queryOnce({ goals: {} }); | ||
* console.log(resp.data.goals) | ||
*/ | ||
queryOnce<Q extends Schema extends InstantGraph<any, any> ? InstaQLQueryParams<Schema> : Exactly<Query, Q>>(query: Q): Promise<{ | ||
data: QueryResponse<Q, Schema, WithCardinalityInference>; | ||
pageInfo: PageInfoResponse<Q>; | ||
}>; | ||
} | ||
@@ -334,3 +351,3 @@ /** | ||
declare function coerceQuery(o: any): any; | ||
export { init, init_experimental, _init_internal, id, tx, txInit, lookup, i, getOps, coerceQuery, weakHash, IndexedDBStorage, WindowNetworkListener, InstantCore as InstantClient, Auth, Storage, type IDatabase, type RoomSchemaShape, type Query, type QueryResponse, type InstantObject, type Exactly, type TransactionChunk, type AuthState, type User, type AuthToken, type TxChunk, type SubscriptionState, type LifecycleSubscriptionState, type PresenceOpts, type PresenceSlice, type PresenceResponse, type InstaQLQueryParams, type InstantQuery, type InstantQueryResult, type InstantSchema, type InstantEntity, type InstantSchemaDatabase, type AttrsDefs, type CardinalityKind, type DataAttrDef, type EntitiesDef, type EntitiesWithLinks, type EntityDef, type InstantGraph, type LinkAttrDef, type LinkDef, type LinksDef, type ResolveAttrs, type ValueTypes, }; | ||
export { init, init_experimental, _init_internal, id, tx, txInit, lookup, i, getOps, coerceQuery, weakHash, IndexedDBStorage, WindowNetworkListener, InstantCore as InstantClient, Auth, Storage, type IDatabase, type RoomSchemaShape, type Query, type QueryResponse, type PageInfoResponse, type InstantObject, type Exactly, type TransactionChunk, type AuthState, type User, type AuthToken, type TxChunk, type SubscriptionState, type LifecycleSubscriptionState, type PresenceOpts, type PresenceSlice, type PresenceResponse, type InstaQLQueryParams, type InstantQuery, type InstantQueryResult, type InstantSchema, type InstantEntity, type InstantSchemaDatabase, type AttrsDefs, type CardinalityKind, type DataAttrDef, type EntitiesDef, type EntitiesWithLinks, type EntityDef, type InstantGraph, type LinkAttrDef, type LinkDef, type LinksDef, type ResolveAttrs, type ValueTypes, }; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -203,2 +203,18 @@ "use strict"; | ||
} | ||
/** | ||
* Use this for one-off queries. | ||
* Returns local data if available, otherwise fetches from the server. | ||
* Because we want to avoid stale data, this method will throw an error | ||
* if the user is offline or there is no active connection to the server. | ||
* | ||
* @see https://instantdb.com/docs/instaql | ||
* | ||
* @example | ||
* | ||
* const resp = await db.queryOnce({ goals: {} }); | ||
* console.log(resp.data.goals) | ||
*/ | ||
queryOnce(query) { | ||
return this._reactor.queryOnce(query); | ||
} | ||
} | ||
@@ -205,0 +221,0 @@ exports.InstantClient = InstantCore; |
@@ -169,2 +169,19 @@ import Reactor from "./Reactor"; | ||
shutdown(): void; | ||
/** | ||
* Use this for one-off queries. | ||
* Returns local data if available, otherwise fetches from the server. | ||
* Because we want to avoid stale data, this method will throw an error | ||
* if the user is offline or there is no active connection to the server. | ||
* | ||
* @see https://instantdb.com/docs/instaql | ||
* | ||
* @example | ||
* | ||
* const resp = await db.queryOnce({ goals: {} }); | ||
* console.log(resp.data.goals) | ||
*/ | ||
queryOnce<Q extends Schema extends InstantGraph<any, any> ? InstaQLQueryParams<Schema> : Exactly<Query, Q>>(query: Q): Promise<{ | ||
data: QueryResponse<Q, Schema, WithCardinalityInference>; | ||
pageInfo: PageInfoResponse<Q>; | ||
}>; | ||
} | ||
@@ -334,3 +351,3 @@ /** | ||
declare function coerceQuery(o: any): any; | ||
export { init, init_experimental, _init_internal, id, tx, txInit, lookup, i, getOps, coerceQuery, weakHash, IndexedDBStorage, WindowNetworkListener, InstantCore as InstantClient, Auth, Storage, type IDatabase, type RoomSchemaShape, type Query, type QueryResponse, type InstantObject, type Exactly, type TransactionChunk, type AuthState, type User, type AuthToken, type TxChunk, type SubscriptionState, type LifecycleSubscriptionState, type PresenceOpts, type PresenceSlice, type PresenceResponse, type InstaQLQueryParams, type InstantQuery, type InstantQueryResult, type InstantSchema, type InstantEntity, type InstantSchemaDatabase, type AttrsDefs, type CardinalityKind, type DataAttrDef, type EntitiesDef, type EntitiesWithLinks, type EntityDef, type InstantGraph, type LinkAttrDef, type LinkDef, type LinksDef, type ResolveAttrs, type ValueTypes, }; | ||
export { init, init_experimental, _init_internal, id, tx, txInit, lookup, i, getOps, coerceQuery, weakHash, IndexedDBStorage, WindowNetworkListener, InstantCore as InstantClient, Auth, Storage, type IDatabase, type RoomSchemaShape, type Query, type QueryResponse, type PageInfoResponse, type InstantObject, type Exactly, type TransactionChunk, type AuthState, type User, type AuthToken, type TxChunk, type SubscriptionState, type LifecycleSubscriptionState, type PresenceOpts, type PresenceSlice, type PresenceResponse, type InstaQLQueryParams, type InstantQuery, type InstantQueryResult, type InstantSchema, type InstantEntity, type InstantSchemaDatabase, type AttrsDefs, type CardinalityKind, type DataAttrDef, type EntitiesDef, type EntitiesWithLinks, type EntityDef, type InstantGraph, type LinkAttrDef, type LinkDef, type LinksDef, type ResolveAttrs, type ValueTypes, }; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -184,2 +184,18 @@ import Reactor from "./Reactor"; | ||
} | ||
/** | ||
* Use this for one-off queries. | ||
* Returns local data if available, otherwise fetches from the server. | ||
* Because we want to avoid stale data, this method will throw an error | ||
* if the user is offline or there is no active connection to the server. | ||
* | ||
* @see https://instantdb.com/docs/instaql | ||
* | ||
* @example | ||
* | ||
* const resp = await db.queryOnce({ goals: {} }); | ||
* console.log(resp.data.goals) | ||
*/ | ||
queryOnce(query) { | ||
return this._reactor.queryOnce(query); | ||
} | ||
} | ||
@@ -186,0 +202,0 @@ /** |
@@ -14,3 +14,13 @@ /** | ||
pendingMutations: PersistedObject; | ||
queryCbs: {}; | ||
/** @type {Record<string, Array<{ q: any, cb: (data: any) => any }>>} */ | ||
queryCbs: Record<string, Array<{ | ||
q: any; | ||
cb: (data: any) => any; | ||
}>>; | ||
/** @type {Record<string, Array<{ q: any, eventId: string, dfd: Deferred }>>} */ | ||
queryOnceDfds: Record<string, Array<{ | ||
q: any; | ||
eventId: string; | ||
dfd: Deferred; | ||
}>>; | ||
authCbs: any[]; | ||
@@ -98,4 +108,6 @@ attrsCbs: any[]; | ||
_handleReceiveError(msg: any): void; | ||
notifyQueryOnceError(hash: any, eventId: any, e: any): void; | ||
_setAttrs(attrs: any): void; | ||
getPreviousResult: (q: any) => any; | ||
_startQuerySub(q: any, hash: any): string; | ||
/** | ||
@@ -112,2 +124,6 @@ * When a user subscribes to a query the following side effects occur: | ||
subscribeQuery(q: any, cb: any): () => void; | ||
queryOnce(q: any): Promise<any>; | ||
_completeQueryOnce(q: any, hash: any, dfd: any): void; | ||
_unsubQuery(q: any, hash: any, cb: any): void; | ||
_cleanupQuery(q: any, hash: any): void; | ||
_rewriteMutations(attrs: any, muts: any): any; | ||
@@ -119,3 +135,4 @@ optimisticAttrs(): any; | ||
notifyOne: (hash: any) => void; | ||
notifyQueryError: (hash: any, msg: any) => void; | ||
notifyOneQueryOnce: (hash: any) => void; | ||
notifyQueryError: (hash: any, error: any) => void; | ||
/** Re-compute all subscriptions */ | ||
@@ -281,4 +298,5 @@ notifyAll(): void; | ||
import { PersistedObject } from "./utils/PersistedObject"; | ||
import { Deferred } from "./utils/Deferred"; | ||
import IndexedDBStorage from "./IndexedDBStorage"; | ||
import WindowNetworkListener from "./WindowNetworkListener"; | ||
//# sourceMappingURL=Reactor.d.ts.map |
@@ -34,2 +34,3 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
}; | ||
const QUERY_ONCE_TIMEOUT = 5000; | ||
const WS_OPEN_STATUS = 1; | ||
@@ -82,3 +83,6 @@ const defaultConfig = { | ||
this.status = STATUS.CONNECTING; | ||
/** @type {Record<string, Array<{ q: any, cb: (data: any) => any }>>} */ | ||
this.queryCbs = {}; | ||
/** @type {Record<string, Array<{ q: any, eventId: string, dfd: Deferred }>>} */ | ||
this.queryOnceDfds = {}; | ||
this.authCbs = []; | ||
@@ -167,7 +171,5 @@ this.attrsCbs = []; | ||
this.notifyOne = (hash) => { | ||
var _a; | ||
const cbs = this.queryCbs[hash] || []; | ||
if (!cbs) | ||
return; | ||
const prevData = (_a = this._dataForQueryCache[hash]) === null || _a === void 0 ? void 0 : _a.data; | ||
var _a, _b; | ||
const cbs = (_a = this.queryCbs[hash]) !== null && _a !== void 0 ? _a : []; | ||
const prevData = (_b = this._dataForQueryCache[hash]) === null || _b === void 0 ? void 0 : _b.data; | ||
const data = this.dataForQuery(hash); | ||
@@ -178,7 +180,16 @@ if (!data) | ||
return; | ||
cbs.forEach((cb) => cb(data)); | ||
cbs.forEach((r) => r.cb(data)); | ||
}; | ||
this.notifyQueryError = (hash, msg) => { | ||
this.notifyOneQueryOnce = (hash) => { | ||
var _a; | ||
const dfds = (_a = this.queryOnceDfds[hash]) !== null && _a !== void 0 ? _a : []; | ||
const data = this.dataForQuery(hash); | ||
dfds.forEach((r) => { | ||
this._completeQueryOnce(r.q, hash, r.dfd); | ||
r.dfd.resolve(data); | ||
}); | ||
}; | ||
this.notifyQueryError = (hash, error) => { | ||
const cbs = this.queryCbs[hash] || []; | ||
cbs.forEach((cb) => cb({ error: msg })); | ||
cbs.forEach((r) => r.cb({ error })); | ||
}; | ||
@@ -405,2 +416,5 @@ /** Applies transactions locally and sends transact message to server */ | ||
break; | ||
case "add-query-exists": | ||
this.notifyOneQueryOnce(weakHash(msg.q)); | ||
break; | ||
case "add-query-ok": | ||
@@ -419,2 +433,3 @@ const { q, result, "processed-tx-id": addQueryTxId } = msg; | ||
this.notifyOne(hash); | ||
this.notifyOneQueryOnce(hash); | ||
break; | ||
@@ -526,2 +541,5 @@ case "refresh-ok": | ||
const prevMutation = this.pendingMutations.currentValue.get(eventId); | ||
const errorMessage = { | ||
message: msg.message || "Uh-oh, something went wrong. Ping Joe & Stopa.", | ||
}; | ||
if (prevMutation) { | ||
@@ -538,11 +556,10 @@ // This must be a transaction error | ||
if (q) { | ||
const hash = weakHash(q); | ||
// This must be a query error | ||
this.querySubs.set((prev) => { | ||
const hash = weakHash(q); | ||
delete prev[hash]; | ||
return prev; | ||
}); | ||
this.notifyQueryError(weakHash(q), { | ||
message: msg.message || "Uh-oh, something went wrong. Ping Joe & Stopa.", | ||
}); | ||
this.notifyQueryError(weakHash(q), errorMessage); | ||
this.notifyQueryOnceError(hash, eventId, errorMessage); | ||
return; | ||
@@ -559,5 +576,2 @@ } | ||
// We failed to init | ||
const errorMessage = { | ||
message: msg.message || "Uh-oh, something went wrong. Ping Joe & Stopa.", | ||
}; | ||
this._setStatus(STATUS.ERRORED, errorMessage); | ||
@@ -577,2 +591,9 @@ this.notifyAll(); | ||
} | ||
notifyQueryOnceError(hash, eventId, e) { | ||
var _a; | ||
const r = (_a = this.queryOnceDfds[hash]) === null || _a === void 0 ? void 0 : _a.find((r) => r.eventId === eventId); | ||
if (!r) | ||
return; | ||
r.dfd.reject(e); | ||
} | ||
_setAttrs(attrs) { | ||
@@ -585,2 +606,11 @@ this.attrs = attrs.reduce((acc, attr) => { | ||
} | ||
_startQuerySub(q, hash) { | ||
const eventId = uuid(); | ||
this.querySubs.set((prev) => { | ||
prev[hash] = prev[hash] || { q, result: null, eventId }; | ||
return prev; | ||
}); | ||
this._trySendAuthed(eventId, { op: "add-query", q }); | ||
return eventId; | ||
} | ||
/** | ||
@@ -597,11 +627,4 @@ * When a user subscribes to a query the following side effects occur: | ||
subscribeQuery(q, cb) { | ||
const eventId = uuid(); | ||
var _a; | ||
const hash = weakHash(q); | ||
this.queryCbs[hash] = this.queryCbs[hash] || []; | ||
this.queryCbs[hash].push(cb); | ||
this.querySubs.set((prev) => { | ||
prev[hash] = prev[hash] || { q, result: null, eventId }; | ||
return prev; | ||
}); | ||
this._trySendAuthed(eventId, { op: "add-query", q }); | ||
const prevResult = this.getPreviousResult(q); | ||
@@ -611,6 +634,45 @@ if (prevResult) { | ||
} | ||
this.queryCbs[hash] = (_a = this.queryCbs[hash]) !== null && _a !== void 0 ? _a : []; | ||
this.queryCbs[hash].push({ q, cb }); | ||
this._startQuerySub(q, hash); | ||
return () => { | ||
this.queryCbs[hash] = this.queryCbs[hash].filter((x) => x !== cb); | ||
this._unsubQuery(q, hash, cb); | ||
}; | ||
} | ||
queryOnce(q) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
var _a; | ||
if (!this._isOnline) { | ||
throw new Error("Offline: Cannot execute query because the device is offline."); | ||
} | ||
const hash = weakHash(q); | ||
const dfd = new Deferred(); | ||
const eventId = this._startQuerySub(q, hash); | ||
this.queryOnceDfds[hash] = (_a = this.queryOnceDfds[hash]) !== null && _a !== void 0 ? _a : []; | ||
this.queryOnceDfds[hash].push({ q, dfd, eventId }); | ||
setTimeout(() => dfd.reject(new Error("Query timed out")), QUERY_ONCE_TIMEOUT); | ||
return dfd.promise; | ||
}); | ||
} | ||
_completeQueryOnce(q, hash, dfd) { | ||
if (!this.queryOnceDfds[hash]) | ||
return; | ||
this.queryOnceDfds[hash] = this.queryOnceDfds[hash].filter((r) => r.dfd !== dfd); | ||
this._cleanupQuery(q, hash); | ||
} | ||
_unsubQuery(q, hash, cb) { | ||
if (!this.queryCbs[hash]) | ||
return; | ||
this.queryCbs[hash] = this.queryCbs[hash].filter((r) => r.cb !== cb); | ||
this._cleanupQuery(q, hash); | ||
} | ||
_cleanupQuery(q, hash) { | ||
var _a, _b; | ||
const hasListeners = ((_a = this.queryCbs[hash]) === null || _a === void 0 ? void 0 : _a.length) || ((_b = this.queryOnceDfds[hash]) === null || _b === void 0 ? void 0 : _b.length); | ||
if (hasListeners) | ||
return; | ||
delete this.queryCbs[hash]; | ||
delete this.queryOnceDfds[hash]; | ||
this._trySendAuthed(uuid(), { op: "remove-query", q }); | ||
} | ||
// When we `pushTx`, it's possible that we don't yet have `this.attrs` | ||
@@ -617,0 +679,0 @@ // This means that `tx-steps` in `pendingMutations` will include `add-attr` |
@@ -14,3 +14,13 @@ /** | ||
pendingMutations: PersistedObject; | ||
queryCbs: {}; | ||
/** @type {Record<string, Array<{ q: any, cb: (data: any) => any }>>} */ | ||
queryCbs: Record<string, Array<{ | ||
q: any; | ||
cb: (data: any) => any; | ||
}>>; | ||
/** @type {Record<string, Array<{ q: any, eventId: string, dfd: Deferred }>>} */ | ||
queryOnceDfds: Record<string, Array<{ | ||
q: any; | ||
eventId: string; | ||
dfd: Deferred; | ||
}>>; | ||
authCbs: any[]; | ||
@@ -98,4 +108,6 @@ attrsCbs: any[]; | ||
_handleReceiveError(msg: any): void; | ||
notifyQueryOnceError(hash: any, eventId: any, e: any): void; | ||
_setAttrs(attrs: any): void; | ||
getPreviousResult: (q: any) => any; | ||
_startQuerySub(q: any, hash: any): string; | ||
/** | ||
@@ -112,2 +124,6 @@ * When a user subscribes to a query the following side effects occur: | ||
subscribeQuery(q: any, cb: any): () => void; | ||
queryOnce(q: any): Promise<any>; | ||
_completeQueryOnce(q: any, hash: any, dfd: any): void; | ||
_unsubQuery(q: any, hash: any, cb: any): void; | ||
_cleanupQuery(q: any, hash: any): void; | ||
_rewriteMutations(attrs: any, muts: any): any; | ||
@@ -119,3 +135,4 @@ optimisticAttrs(): any; | ||
notifyOne: (hash: any) => void; | ||
notifyQueryError: (hash: any, msg: any) => void; | ||
notifyOneQueryOnce: (hash: any) => void; | ||
notifyQueryError: (hash: any, error: any) => void; | ||
/** Re-compute all subscriptions */ | ||
@@ -281,4 +298,5 @@ notifyAll(): void; | ||
import { PersistedObject } from "./utils/PersistedObject"; | ||
import { Deferred } from "./utils/Deferred"; | ||
import IndexedDBStorage from "./IndexedDBStorage"; | ||
import WindowNetworkListener from "./WindowNetworkListener"; | ||
//# sourceMappingURL=Reactor.d.ts.map |
@@ -62,2 +62,3 @@ "use strict"; | ||
}; | ||
const QUERY_ONCE_TIMEOUT = 5000; | ||
const WS_OPEN_STATUS = 1; | ||
@@ -110,3 +111,6 @@ const defaultConfig = { | ||
this.status = STATUS.CONNECTING; | ||
/** @type {Record<string, Array<{ q: any, cb: (data: any) => any }>>} */ | ||
this.queryCbs = {}; | ||
/** @type {Record<string, Array<{ q: any, eventId: string, dfd: Deferred }>>} */ | ||
this.queryOnceDfds = {}; | ||
this.authCbs = []; | ||
@@ -195,7 +199,5 @@ this.attrsCbs = []; | ||
this.notifyOne = (hash) => { | ||
var _a; | ||
const cbs = this.queryCbs[hash] || []; | ||
if (!cbs) | ||
return; | ||
const prevData = (_a = this._dataForQueryCache[hash]) === null || _a === void 0 ? void 0 : _a.data; | ||
var _a, _b; | ||
const cbs = (_a = this.queryCbs[hash]) !== null && _a !== void 0 ? _a : []; | ||
const prevData = (_b = this._dataForQueryCache[hash]) === null || _b === void 0 ? void 0 : _b.data; | ||
const data = this.dataForQuery(hash); | ||
@@ -206,7 +208,16 @@ if (!data) | ||
return; | ||
cbs.forEach((cb) => cb(data)); | ||
cbs.forEach((r) => r.cb(data)); | ||
}; | ||
this.notifyQueryError = (hash, msg) => { | ||
this.notifyOneQueryOnce = (hash) => { | ||
var _a; | ||
const dfds = (_a = this.queryOnceDfds[hash]) !== null && _a !== void 0 ? _a : []; | ||
const data = this.dataForQuery(hash); | ||
dfds.forEach((r) => { | ||
this._completeQueryOnce(r.q, hash, r.dfd); | ||
r.dfd.resolve(data); | ||
}); | ||
}; | ||
this.notifyQueryError = (hash, error) => { | ||
const cbs = this.queryCbs[hash] || []; | ||
cbs.forEach((cb) => cb({ error: msg })); | ||
cbs.forEach((r) => r.cb({ error })); | ||
}; | ||
@@ -433,2 +444,5 @@ /** Applies transactions locally and sends transact message to server */ | ||
break; | ||
case "add-query-exists": | ||
this.notifyOneQueryOnce((0, weakHash_1.default)(msg.q)); | ||
break; | ||
case "add-query-ok": | ||
@@ -447,2 +461,3 @@ const { q, result, "processed-tx-id": addQueryTxId } = msg; | ||
this.notifyOne(hash); | ||
this.notifyOneQueryOnce(hash); | ||
break; | ||
@@ -554,2 +569,5 @@ case "refresh-ok": | ||
const prevMutation = this.pendingMutations.currentValue.get(eventId); | ||
const errorMessage = { | ||
message: msg.message || "Uh-oh, something went wrong. Ping Joe & Stopa.", | ||
}; | ||
if (prevMutation) { | ||
@@ -566,11 +584,10 @@ // This must be a transaction error | ||
if (q) { | ||
const hash = (0, weakHash_1.default)(q); | ||
// This must be a query error | ||
this.querySubs.set((prev) => { | ||
const hash = (0, weakHash_1.default)(q); | ||
delete prev[hash]; | ||
return prev; | ||
}); | ||
this.notifyQueryError((0, weakHash_1.default)(q), { | ||
message: msg.message || "Uh-oh, something went wrong. Ping Joe & Stopa.", | ||
}); | ||
this.notifyQueryError((0, weakHash_1.default)(q), errorMessage); | ||
this.notifyQueryOnceError(hash, eventId, errorMessage); | ||
return; | ||
@@ -587,5 +604,2 @@ } | ||
// We failed to init | ||
const errorMessage = { | ||
message: msg.message || "Uh-oh, something went wrong. Ping Joe & Stopa.", | ||
}; | ||
this._setStatus(STATUS.ERRORED, errorMessage); | ||
@@ -605,2 +619,9 @@ this.notifyAll(); | ||
} | ||
notifyQueryOnceError(hash, eventId, e) { | ||
var _a; | ||
const r = (_a = this.queryOnceDfds[hash]) === null || _a === void 0 ? void 0 : _a.find((r) => r.eventId === eventId); | ||
if (!r) | ||
return; | ||
r.dfd.reject(e); | ||
} | ||
_setAttrs(attrs) { | ||
@@ -613,2 +634,11 @@ this.attrs = attrs.reduce((acc, attr) => { | ||
} | ||
_startQuerySub(q, hash) { | ||
const eventId = (0, uuid_1.default)(); | ||
this.querySubs.set((prev) => { | ||
prev[hash] = prev[hash] || { q, result: null, eventId }; | ||
return prev; | ||
}); | ||
this._trySendAuthed(eventId, { op: "add-query", q }); | ||
return eventId; | ||
} | ||
/** | ||
@@ -625,11 +655,4 @@ * When a user subscribes to a query the following side effects occur: | ||
subscribeQuery(q, cb) { | ||
const eventId = (0, uuid_1.default)(); | ||
var _a; | ||
const hash = (0, weakHash_1.default)(q); | ||
this.queryCbs[hash] = this.queryCbs[hash] || []; | ||
this.queryCbs[hash].push(cb); | ||
this.querySubs.set((prev) => { | ||
prev[hash] = prev[hash] || { q, result: null, eventId }; | ||
return prev; | ||
}); | ||
this._trySendAuthed(eventId, { op: "add-query", q }); | ||
const prevResult = this.getPreviousResult(q); | ||
@@ -639,6 +662,45 @@ if (prevResult) { | ||
} | ||
this.queryCbs[hash] = (_a = this.queryCbs[hash]) !== null && _a !== void 0 ? _a : []; | ||
this.queryCbs[hash].push({ q, cb }); | ||
this._startQuerySub(q, hash); | ||
return () => { | ||
this.queryCbs[hash] = this.queryCbs[hash].filter((x) => x !== cb); | ||
this._unsubQuery(q, hash, cb); | ||
}; | ||
} | ||
queryOnce(q) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
var _a; | ||
if (!this._isOnline) { | ||
throw new Error("Offline: Cannot execute query because the device is offline."); | ||
} | ||
const hash = (0, weakHash_1.default)(q); | ||
const dfd = new Deferred_1.Deferred(); | ||
const eventId = this._startQuerySub(q, hash); | ||
this.queryOnceDfds[hash] = (_a = this.queryOnceDfds[hash]) !== null && _a !== void 0 ? _a : []; | ||
this.queryOnceDfds[hash].push({ q, dfd, eventId }); | ||
setTimeout(() => dfd.reject(new Error("Query timed out")), QUERY_ONCE_TIMEOUT); | ||
return dfd.promise; | ||
}); | ||
} | ||
_completeQueryOnce(q, hash, dfd) { | ||
if (!this.queryOnceDfds[hash]) | ||
return; | ||
this.queryOnceDfds[hash] = this.queryOnceDfds[hash].filter((r) => r.dfd !== dfd); | ||
this._cleanupQuery(q, hash); | ||
} | ||
_unsubQuery(q, hash, cb) { | ||
if (!this.queryCbs[hash]) | ||
return; | ||
this.queryCbs[hash] = this.queryCbs[hash].filter((r) => r.cb !== cb); | ||
this._cleanupQuery(q, hash); | ||
} | ||
_cleanupQuery(q, hash) { | ||
var _a, _b; | ||
const hasListeners = ((_a = this.queryCbs[hash]) === null || _a === void 0 ? void 0 : _a.length) || ((_b = this.queryOnceDfds[hash]) === null || _b === void 0 ? void 0 : _b.length); | ||
if (hasListeners) | ||
return; | ||
delete this.queryCbs[hash]; | ||
delete this.queryOnceDfds[hash]; | ||
this._trySendAuthed((0, uuid_1.default)(), { op: "remove-query", q }); | ||
} | ||
// When we `pushTx`, it's possible that we don't yet have `this.attrs` | ||
@@ -645,0 +707,0 @@ // This means that `tx-steps` in `pendingMutations` will include `add-attr` |
{ | ||
"name": "@instantdb/core", | ||
"version": "v0.14.9", | ||
"version": "v0.14.10", | ||
"description": "Instant's core local abstraction", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -390,2 +390,28 @@ import Reactor from "./Reactor"; | ||
} | ||
/** | ||
* Use this for one-off queries. | ||
* Returns local data if available, otherwise fetches from the server. | ||
* Because we want to avoid stale data, this method will throw an error | ||
* if the user is offline or there is no active connection to the server. | ||
* | ||
* @see https://instantdb.com/docs/instaql | ||
* | ||
* @example | ||
* | ||
* const resp = await db.queryOnce({ goals: {} }); | ||
* console.log(resp.data.goals) | ||
*/ | ||
queryOnce< | ||
Q extends Schema extends InstantGraph<any, any> | ||
? InstaQLQueryParams<Schema> | ||
: Exactly<Query, Q>, | ||
>( | ||
query: Q, | ||
): Promise<{ | ||
data: QueryResponse<Q, Schema, WithCardinalityInference>; | ||
pageInfo: PageInfoResponse<Q>; | ||
}> { | ||
return this._reactor.queryOnce(query); | ||
} | ||
} | ||
@@ -617,2 +643,3 @@ | ||
type QueryResponse, | ||
type PageInfoResponse, | ||
type InstantObject, | ||
@@ -619,0 +646,0 @@ type Exactly, |
@@ -27,2 +27,4 @@ // @ts-check | ||
const QUERY_ONCE_TIMEOUT = 5000; | ||
const WS_OPEN_STATUS = 1; | ||
@@ -90,3 +92,6 @@ | ||
/** @type {Record<string, Array<{ q: any, cb: (data: any) => any }>>} */ | ||
queryCbs = {}; | ||
/** @type {Record<string, Array<{ q: any, eventId: string, dfd: Deferred }>>} */ | ||
queryOnceDfds = {}; | ||
authCbs = []; | ||
@@ -361,2 +366,5 @@ attrsCbs = []; | ||
break; | ||
case "add-query-exists": | ||
this.notifyOneQueryOnce(weakHash(msg.q)); | ||
break; | ||
case "add-query-ok": | ||
@@ -380,2 +388,3 @@ const { q, result, "processed-tx-id": addQueryTxId } = msg; | ||
this.notifyOne(hash); | ||
this.notifyOneQueryOnce(hash); | ||
break; | ||
@@ -503,2 +512,6 @@ case "refresh-ok": | ||
const prevMutation = this.pendingMutations.currentValue.get(eventId); | ||
const errorMessage = { | ||
message: msg.message || "Uh-oh, something went wrong. Ping Joe & Stopa.", | ||
}; | ||
if (prevMutation) { | ||
@@ -516,12 +529,11 @@ // This must be a transaction error | ||
if (q) { | ||
const hash = weakHash(q); | ||
// This must be a query error | ||
this.querySubs.set((prev) => { | ||
const hash = weakHash(q); | ||
delete prev[hash]; | ||
return prev; | ||
}); | ||
this.notifyQueryError(weakHash(q), { | ||
message: | ||
msg.message || "Uh-oh, something went wrong. Ping Joe & Stopa.", | ||
}); | ||
this.notifyQueryError(weakHash(q), errorMessage); | ||
this.notifyQueryOnceError(hash, eventId, errorMessage); | ||
return; | ||
@@ -542,6 +554,3 @@ } | ||
// We failed to init | ||
const errorMessage = { | ||
message: | ||
msg.message || "Uh-oh, something went wrong. Ping Joe & Stopa.", | ||
}; | ||
this._setStatus(STATUS.ERRORED, errorMessage); | ||
@@ -565,2 +574,8 @@ this.notifyAll(); | ||
notifyQueryOnceError(hash, eventId, e) { | ||
const r = this.queryOnceDfds[hash]?.find((r) => r.eventId === eventId); | ||
if (!r) return; | ||
r.dfd.reject(e); | ||
} | ||
_setAttrs(attrs) { | ||
@@ -583,2 +598,13 @@ this.attrs = attrs.reduce((acc, attr) => { | ||
_startQuerySub(q, hash) { | ||
const eventId = uuid(); | ||
this.querySubs.set((prev) => { | ||
prev[hash] = prev[hash] || { q, result: null, eventId }; | ||
return prev; | ||
}); | ||
this._trySendAuthed(eventId, { op: "add-query", q }); | ||
return eventId; | ||
} | ||
/** | ||
@@ -595,12 +621,4 @@ * When a user subscribes to a query the following side effects occur: | ||
subscribeQuery(q, cb) { | ||
const eventId = uuid(); | ||
const hash = weakHash(q); | ||
this.queryCbs[hash] = this.queryCbs[hash] || []; | ||
this.queryCbs[hash].push(cb); | ||
this.querySubs.set((prev) => { | ||
prev[hash] = prev[hash] || { q, result: null, eventId }; | ||
return prev; | ||
}); | ||
this._trySendAuthed(eventId, { op: "add-query", q }); | ||
const prevResult = this.getPreviousResult(q); | ||
@@ -610,7 +628,66 @@ if (prevResult) { | ||
} | ||
this.queryCbs[hash] = this.queryCbs[hash] ?? []; | ||
this.queryCbs[hash].push({ q, cb }); | ||
this._startQuerySub(q, hash); | ||
return () => { | ||
this.queryCbs[hash] = this.queryCbs[hash].filter((x) => x !== cb); | ||
this._unsubQuery(q, hash, cb); | ||
}; | ||
} | ||
async queryOnce(q) { | ||
if (!this._isOnline) { | ||
throw new Error( | ||
"Offline: Cannot execute query because the device is offline.", | ||
); | ||
} | ||
const hash = weakHash(q); | ||
const dfd = new Deferred(); | ||
const eventId = this._startQuerySub(q, hash); | ||
this.queryOnceDfds[hash] = this.queryOnceDfds[hash] ?? []; | ||
this.queryOnceDfds[hash].push({ q, dfd, eventId }); | ||
setTimeout( | ||
() => dfd.reject(new Error("Query timed out")), | ||
QUERY_ONCE_TIMEOUT, | ||
); | ||
return dfd.promise; | ||
} | ||
_completeQueryOnce(q, hash, dfd) { | ||
if (!this.queryOnceDfds[hash]) return; | ||
this.queryOnceDfds[hash] = this.queryOnceDfds[hash].filter( | ||
(r) => r.dfd !== dfd, | ||
); | ||
this._cleanupQuery(q, hash); | ||
} | ||
_unsubQuery(q, hash, cb) { | ||
if (!this.queryCbs[hash]) return; | ||
this.queryCbs[hash] = this.queryCbs[hash].filter((r) => r.cb !== cb); | ||
this._cleanupQuery(q, hash); | ||
} | ||
_cleanupQuery(q, hash) { | ||
const hasListeners = | ||
this.queryCbs[hash]?.length || this.queryOnceDfds[hash]?.length; | ||
if (hasListeners) return; | ||
delete this.queryCbs[hash]; | ||
delete this.queryOnceDfds[hash]; | ||
this._trySendAuthed(uuid(), { op: "remove-query", q }); | ||
} | ||
// When we `pushTx`, it's possible that we don't yet have `this.attrs` | ||
@@ -764,14 +841,25 @@ // This means that `tx-steps` in `pendingMutations` will include `add-attr` | ||
notifyOne = (hash) => { | ||
const cbs = this.queryCbs[hash] || []; | ||
if (!cbs) return; | ||
const cbs = this.queryCbs[hash] ?? []; | ||
const prevData = this._dataForQueryCache[hash]?.data; | ||
const data = this.dataForQuery(hash); | ||
if (!data) return; | ||
if (areObjectsDeepEqual(data, prevData)) return; | ||
cbs.forEach((cb) => cb(data)); | ||
cbs.forEach((r) => r.cb(data)); | ||
}; | ||
notifyQueryError = (hash, msg) => { | ||
notifyOneQueryOnce = (hash) => { | ||
const dfds = this.queryOnceDfds[hash] ?? []; | ||
const data = this.dataForQuery(hash); | ||
dfds.forEach((r) => { | ||
this._completeQueryOnce(r.q, hash, r.dfd); | ||
r.dfd.resolve(data); | ||
}); | ||
}; | ||
notifyQueryError = (hash, error) => { | ||
const cbs = this.queryCbs[hash] || []; | ||
cbs.forEach((cb) => cb({ error: msg })); | ||
cbs.forEach((r) => r.cb({ error })); | ||
}; | ||
@@ -778,0 +866,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
2379714
41868