Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@instantdb/core

Package Overview
Dependencies
Maintainers
5
Versions
206
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@instantdb/core - npm Package Compare versions

Comparing version 0.11.3 to 0.11.4

__tests__/src/data/zeneca/attrs.json

4

__tests__/src/instaql.test.js
import { test, expect } from "vitest";
import zenecaAttrs from "./data/zenecaAttrs.json";
import zenecaTriples from "./data/zenecaTriples.json";
import zenecaAttrs from "./data/zeneca/attrs.json";
import zenecaTriples from "./data/zeneca/triples.json";
import { createStore, transact } from "../../src/store";

@@ -6,0 +6,0 @@ import query from "../../src/instaql";

@@ -8,4 +8,4 @@ // https://www.npmjs.com/package/fake-indexeddb

import Reactor from "../../src/Reactor";
import zenecaAttrs from "./data/zenecaAttrs.json";
import zenecaTriples from "./data/zenecaTriples.json";
import zenecaAttrs from "./data/zeneca/attrs.json";
import zenecaTriples from "./data/zeneca/triples.json";
import uuid from "../../src/utils/uuid";

@@ -81,2 +81,2 @@

]);
});
});

@@ -11,8 +11,3 @@ import { test } from "vitest";

name: i.string(),
email: i
.string()
.indexed()
.unique()
// this doesn't exist yet, but you get the idea
.clientValidate((s: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s)),
email: i.string().indexed().unique(),
bio: i.string().optional(),

@@ -34,32 +29,48 @@ // this is a convenient way to typecheck custom JSON fields

usersPosts: {
from: "users",
fromAttr: "posts",
fromHas: "many",
to: "posts",
toAttr: "author",
toHas: "one",
forward: {
on: "users",
has: "many",
label: "posts",
},
reverse: {
on: "posts",
has: "one",
label: "author",
},
},
postsComments: {
from: "posts",
fromAttr: "comments",
fromHas: "many",
to: "comments",
toAttr: "post",
toHas: "one",
forward: {
on: "posts",
has: "many",
label: "comments",
},
reverse: {
on: "comments",
has: "one",
label: "post",
},
},
friendships: {
from: "users",
fromAttr: "friends",
fromHas: "many",
to: "users",
toAttr: "_friends",
toHas: "many",
forward: {
on: "users",
has: "many",
label: "friends",
},
reverse: {
on: "users",
has: "many",
label: "_friends",
},
},
referrals: {
from: "users",
to: "users",
fromAttr: "referred",
toAttr: "referrer",
fromHas: "many",
toHas: "one",
forward: {
on: "users",
has: "many",
label: "referred",
},
reverse: {
on: "users",
has: "one",
label: "referrer",
},
},

@@ -83,20 +94,22 @@ },

type Graph = typeof graph;
type DemoQueryResult = i.InstaQLQueryResult<
Graph["entities"],
typeof demoQuery
>;
// Explore derived types
type Test1 = Graph["entities"]["users"]["links"]["_friends"]["entityName"];
type Test2 = Graph["entities"]["users"]["links"]["_friends"]["cardinality"];
// Demo time! Notice:
// Demo time!!! Notice:
// - everything is typed
// - links are resolved
// - links are resolved by label, deeply
// - only the links that were requested are present in the result
// - friends is an array
// - bio is optional (because `.optional()`)
// - referrer is NOT an array (because cardinality is 'one')
// - bio is optional
// - posts is not an array (because $first)
// - posts is not an array (because `$first`)
const queryResult: DemoQueryResult = null as any;
type DemoQueryResult = i.InstaQLQueryResult<
Graph["entities"],
typeof demoQuery
>;
queryResult?.users[0].friends[0]._friends[0].bio;
queryResult?.users[0].posts[0].author.junk;
queryResult?.users[0].posts.junk;
});
import { test, expect } from "vitest";
import zenecaAttrs from "./data/zenecaAttrs.json";
import zenecaTriples from "./data/zenecaTriples.json";
import oldStore from "./data/oldStore.json";
import zenecaAttrs from "./data/zeneca/attrs.json";
import zenecaTriples from "./data/zeneca/triples.json";
import {

@@ -9,3 +8,2 @@ createStore,

allMapValues,
upgradeStore,
} from "../../src/store";

@@ -12,0 +10,0 @@ import query from "../../src/instaql";

@@ -1,2 +0,1 @@

import { QueryResponse, PageInfoResponse } from "./queryTypes";
export type User = {

@@ -31,23 +30,2 @@ id: string;

};
/**
* @deprecated since 0.7.0
*/
export type QueryState<Q, Schema = {}> = {
isLoading: true;
error: undefined;
data: undefined;
pageInfo: undefined;
} | {
isLoading: false;
error: {
message: string;
};
data: undefined;
pageInfo: undefined;
} | {
isLoading: false;
error: undefined;
data: QueryResponse<Q, Schema>;
pageInfo: PageInfoResponse<Q>;
};
//# sourceMappingURL=clientTypes.d.ts.map

@@ -8,3 +8,3 @@ import Reactor from "./Reactor";

import { Query, QueryResponse, PageInfoResponse, Exactly, InstantObject } from "./queryTypes";
import { QueryState, AuthState, User, AuthResult } from "./clientTypes";
import { AuthState, User, AuthResult } from "./clientTypes";
import { PresenceOpts, PresenceResponse, PresenceSlice, RoomSchemaShape } from "./presence";

@@ -70,2 +70,3 @@ import * as i from "./schema";

auth: Auth;
storage: Storage;
constructor(reactor: Reactor<RoomSchema>);

@@ -284,4 +285,28 @@ /**

}
/**
* Functions to manage file storage.
*/
declare class Storage {
private db;
constructor(db: Reactor);
/**
* Uploads file at the provided path.
*
* @see https://docs.instantdb.com/docs/storage
* @example
* const [file] = e.target.files; // result of file input
* const url = await db.storage.put('photos/demo.png', file);
*/
put: (pathname: string, file: File) => Promise<any>;
/**
* Retrieves a download URL for the provided path.
*
* @see https://docs.instantdb.com/docs/storage
* @example
* const url = await db.storage.getDownloadUrl('photos/demo.png');
*/
getDownloadUrl: (pathname: string) => Promise<any>;
}
declare function coerceQuery(o: any): any;
export { init, id, tx, lookup, i, getOps, coerceQuery, weakHash, IndexedDBStorage, WindowNetworkListener, InstantCore as InstantClient, Auth, RoomSchemaShape, Query, QueryResponse, InstantObject, Exactly, TransactionChunk, QueryState, AuthState, User, AuthToken, EmptyChunk, SubscriptionState, LifecycleSubscriptionState, PresenceOpts, PresenceSlice, PresenceResponse, };
export { init, id, tx, lookup, i, getOps, coerceQuery, weakHash, IndexedDBStorage, WindowNetworkListener, InstantCore as InstantClient, Auth, Storage, RoomSchemaShape, Query, QueryResponse, InstantObject, Exactly, TransactionChunk, AuthState, User, AuthToken, EmptyChunk, SubscriptionState, LifecycleSubscriptionState, PresenceOpts, PresenceSlice, PresenceResponse, };
//# sourceMappingURL=index.d.ts.map

@@ -29,3 +29,3 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.Auth = exports.InstantClient = exports.WindowNetworkListener = exports.IndexedDBStorage = exports.weakHash = exports.getOps = exports.i = exports.lookup = exports.tx = exports.id = void 0;
exports.Storage = exports.Auth = exports.InstantClient = exports.WindowNetworkListener = exports.IndexedDBStorage = exports.weakHash = exports.getOps = exports.i = exports.lookup = exports.tx = exports.id = void 0;
exports.init = init;

@@ -114,2 +114,3 @@ exports.coerceQuery = coerceQuery;

this.auth = new Auth(this._reactor);
this.storage = new Storage(this._reactor);
}

@@ -353,2 +354,32 @@ /**

exports.Auth = Auth;
/**
* Functions to manage file storage.
*/
class Storage {
constructor(db) {
this.db = db;
/**
* Uploads file at the provided path.
*
* @see https://docs.instantdb.com/docs/storage
* @example
* const [file] = e.target.files; // result of file input
* const url = await db.storage.put('photos/demo.png', file);
*/
this.put = (pathname, file) => {
return this.db.upload(pathname, file);
};
/**
* Retrieves a download URL for the provided path.
*
* @see https://docs.instantdb.com/docs/storage
* @example
* const url = await db.storage.getDownloadUrl('photos/demo.png');
*/
this.getDownloadUrl = (pathname) => {
return this.db.getDownloadUrl(pathname);
};
}
}
exports.Storage = Storage;
// util

@@ -355,0 +386,0 @@ function coerceQuery(o) {

@@ -44,4 +44,11 @@ "use strict";

}
function getPrimaryKeyAttr(store, ns) {
const primary = Object.values(store.attrs).find((a) => { var _a; return a["primary?"] && ((_a = a["forward-identity"]) === null || _a === void 0 ? void 0 : _a[1]) === ns; });
if (primary) {
return primary;
}
return getAttrByFwdIdentName(store.attrs, ns, "id");
}
function idAttr(store, ns) {
const attr = getAttrByFwdIdentName(store.attrs, ns, "id");
const attr = getPrimaryKeyAttr(store, ns);
if (!attr) {

@@ -284,2 +291,10 @@ throw new AttrNotFoundError(`Could not find id attr for ${ns}`);

}
function determineOrder(form) {
var _a;
const orderOpts = (_a = form.$) === null || _a === void 0 ? void 0 : _a.order;
if (!orderOpts) {
return "asc";
}
return orderOpts[Object.keys(orderOpts)[0]] || "asc";
}
/**

@@ -299,3 +314,3 @@ * Given a query like:

function resolveObjects(store, { etype, level, form, join, pageInfo }) {
var _a, _b, _c, _d, _e, _g, _h, _j, _k;
var _a, _b, _c, _d, _e, _g, _h;
const limit = ((_a = form.$) === null || _a === void 0 ? void 0 : _a.limit) || ((_b = form.$) === null || _b === void 0 ? void 0 : _b.first) || ((_c = form.$) === null || _c === void 0 ? void 0 : _c.last);

@@ -311,3 +326,3 @@ const offset = (_d = form.$) === null || _d === void 0 ? void 0 : _d.offset;

const find = makeFind(makeVarImpl, etype, level);
const objs = runDataloadAndReturnObjects(store, etype, ((_k = (_j = form.$) === null || _j === void 0 ? void 0 : _j.order) === null || _k === void 0 ? void 0 : _k.serverCreatedAt) || "asc", pageInfo, { where, find });
const objs = runDataloadAndReturnObjects(store, etype, determineOrder(form), pageInfo, { where, find });
if (limit != null) {

@@ -314,0 +329,0 @@ const entries = Object.entries(objs);

@@ -1,2 +0,1 @@

import { QueryResponse, PageInfoResponse } from "./queryTypes";
export type User = {

@@ -31,23 +30,2 @@ id: string;

};
/**
* @deprecated since 0.7.0
*/
export type QueryState<Q, Schema = {}> = {
isLoading: true;
error: undefined;
data: undefined;
pageInfo: undefined;
} | {
isLoading: false;
error: {
message: string;
};
data: undefined;
pageInfo: undefined;
} | {
isLoading: false;
error: undefined;
data: QueryResponse<Q, Schema>;
pageInfo: PageInfoResponse<Q>;
};
//# sourceMappingURL=clientTypes.d.ts.map

@@ -8,3 +8,3 @@ import Reactor from "./Reactor";

import { Query, QueryResponse, PageInfoResponse, Exactly, InstantObject } from "./queryTypes";
import { QueryState, AuthState, User, AuthResult } from "./clientTypes";
import { AuthState, User, AuthResult } from "./clientTypes";
import { PresenceOpts, PresenceResponse, PresenceSlice, RoomSchemaShape } from "./presence";

@@ -70,2 +70,3 @@ import * as i from "./schema";

auth: Auth;
storage: Storage;
constructor(reactor: Reactor<RoomSchema>);

@@ -284,4 +285,28 @@ /**

}
/**
* Functions to manage file storage.
*/
declare class Storage {
private db;
constructor(db: Reactor);
/**
* Uploads file at the provided path.
*
* @see https://docs.instantdb.com/docs/storage
* @example
* const [file] = e.target.files; // result of file input
* const url = await db.storage.put('photos/demo.png', file);
*/
put: (pathname: string, file: File) => Promise<any>;
/**
* Retrieves a download URL for the provided path.
*
* @see https://docs.instantdb.com/docs/storage
* @example
* const url = await db.storage.getDownloadUrl('photos/demo.png');
*/
getDownloadUrl: (pathname: string) => Promise<any>;
}
declare function coerceQuery(o: any): any;
export { init, id, tx, lookup, i, getOps, coerceQuery, weakHash, IndexedDBStorage, WindowNetworkListener, InstantCore as InstantClient, Auth, RoomSchemaShape, Query, QueryResponse, InstantObject, Exactly, TransactionChunk, QueryState, AuthState, User, AuthToken, EmptyChunk, SubscriptionState, LifecycleSubscriptionState, PresenceOpts, PresenceSlice, PresenceResponse, };
export { init, id, tx, lookup, i, getOps, coerceQuery, weakHash, IndexedDBStorage, WindowNetworkListener, InstantCore as InstantClient, Auth, Storage, RoomSchemaShape, Query, QueryResponse, InstantObject, Exactly, TransactionChunk, AuthState, User, AuthToken, EmptyChunk, SubscriptionState, LifecycleSubscriptionState, PresenceOpts, PresenceSlice, PresenceResponse, };
//# sourceMappingURL=index.d.ts.map

@@ -74,2 +74,3 @@ import Reactor from "./Reactor";

this.auth = new Auth(this._reactor);
this.storage = new Storage(this._reactor);
}

@@ -311,2 +312,31 @@ /**

}
/**
* Functions to manage file storage.
*/
class Storage {
constructor(db) {
this.db = db;
/**
* Uploads file at the provided path.
*
* @see https://docs.instantdb.com/docs/storage
* @example
* const [file] = e.target.files; // result of file input
* const url = await db.storage.put('photos/demo.png', file);
*/
this.put = (pathname, file) => {
return this.db.upload(pathname, file);
};
/**
* Retrieves a download URL for the provided path.
*
* @see https://docs.instantdb.com/docs/storage
* @example
* const url = await db.storage.getDownloadUrl('photos/demo.png');
*/
this.getDownloadUrl = (pathname) => {
return this.db.getDownloadUrl(pathname);
};
}
}
// util

@@ -324,3 +354,3 @@ function coerceQuery(o) {

// util
getOps, coerceQuery, weakHash, IndexedDBStorage, WindowNetworkListener, InstantCore as InstantClient, Auth, };
getOps, coerceQuery, weakHash, IndexedDBStorage, WindowNetworkListener, InstantCore as InstantClient, Auth, Storage, };
//# sourceMappingURL=index.js.map

@@ -41,4 +41,11 @@ import { query as datalogQuery } from "./datalog";

}
function getPrimaryKeyAttr(store, ns) {
const primary = Object.values(store.attrs).find((a) => { var _a; return a["primary?"] && ((_a = a["forward-identity"]) === null || _a === void 0 ? void 0 : _a[1]) === ns; });
if (primary) {
return primary;
}
return getAttrByFwdIdentName(store.attrs, ns, "id");
}
function idAttr(store, ns) {
const attr = getAttrByFwdIdentName(store.attrs, ns, "id");
const attr = getPrimaryKeyAttr(store, ns);
if (!attr) {

@@ -281,2 +288,10 @@ throw new AttrNotFoundError(`Could not find id attr for ${ns}`);

}
function determineOrder(form) {
var _a;
const orderOpts = (_a = form.$) === null || _a === void 0 ? void 0 : _a.order;
if (!orderOpts) {
return "asc";
}
return orderOpts[Object.keys(orderOpts)[0]] || "asc";
}
/**

@@ -296,3 +311,3 @@ * Given a query like:

function resolveObjects(store, { etype, level, form, join, pageInfo }) {
var _a, _b, _c, _d, _e, _g, _h, _j, _k;
var _a, _b, _c, _d, _e, _g, _h;
const limit = ((_a = form.$) === null || _a === void 0 ? void 0 : _a.limit) || ((_b = form.$) === null || _b === void 0 ? void 0 : _b.first) || ((_c = form.$) === null || _c === void 0 ? void 0 : _c.last);

@@ -308,3 +323,3 @@ const offset = (_d = form.$) === null || _d === void 0 ? void 0 : _d.offset;

const find = makeFind(makeVarImpl, etype, level);
const objs = runDataloadAndReturnObjects(store, etype, ((_k = (_j = form.$) === null || _j === void 0 ? void 0 : _j.order) === null || _k === void 0 ? void 0 : _k.serverCreatedAt) || "asc", pageInfo, { where, find });
const objs = runDataloadAndReturnObjects(store, etype, determineOrder(form), pageInfo, { where, find });
if (limit != null) {

@@ -311,0 +326,0 @@ const entries = Object.entries(objs);

@@ -225,2 +225,4 @@ /**

_notifyBroadcastSubs(room: any, topic: any, msg: any): void;
upload(path: any, file: any): Promise<any>;
getDownloadUrl(path: any): Promise<any>;
}

@@ -227,0 +229,0 @@ import IndexedDBStorage from "./IndexedDBStorage";

@@ -20,6 +20,7 @@ // @ts-check

import * as authAPI from "./authAPI";
import * as StorageApi from "./StorageAPI";
import { buildPresenceSlice, hasPresenceResponseChanged } from "./presence";
import { Deferred } from "./utils/Deferred";
import { PersistedObject } from "./utils/PersistedObject";
import { extractTriples } from "./triples";
import { extractTriples } from "./model/instaqlResult";
import { areObjectsDeepEqual } from "./utils/object";

@@ -169,9 +170,7 @@ import { fromJSONWithMaps, toJSONWithMaps } from "./utils/json";

"refresh-token": (_a = resp.user) === null || _a === void 0 ? void 0 : _a["refresh_token"],
// Dangerous:
// If an admin token is provided for an app, we will
// skip all permission checks. Doing this for now
// to unblock query explorer. This option is not part
// of the standard `Config` type, as we don't want to expose
// this to users just yet. Perhaps in the future we _will_
// want this, but we want to sleep on it for a bit
// skip all permission checks. This is an advanced feature,
// to let users write internal tools
// This option is not exposed in `Config`, as it's
// not ready for prme time
"__admin-token": this.config.__adminToken,

@@ -204,3 +203,3 @@ });

this.config = Object.assign(Object.assign({}, defaultConfig), config);
// (XXX) This is to protect us against running
// This is to protect us against running
// server-side.

@@ -231,3 +230,2 @@ // Incidentally, window is defined in react-native

NetworkListener.listen((isOnline) => {
// (XXX):
// We do this because react native's NetInfo

@@ -300,3 +298,3 @@ // fires multiple online events.

this._flushPendingMessages();
// (XXX-EPH): set session-id, so we know
// (EPH): set session-id, so we know
// which item is us

@@ -399,6 +397,6 @@ this._sessionId = msg["session-id"];

var _a, _b;
// (XXX): Error handling is spaghetti right now.
const eventId = msg["client-event-id"];
const prevMutation = this.pendingMutations.currentValue.get(eventId);
if (prevMutation) { // This must be a transaction error
if (prevMutation) {
// This must be a transaction error
this.pendingMutations.set((prev) => {

@@ -419,8 +417,4 @@ prev.delete(eventId);

const q = msg.q || ((_a = msg["original-event"]) === null || _a === void 0 ? void 0 : _a.q);
if (q) { // This must be a query error
// (XXX) This is a hack.
// Imagine a user writes a malformed query.
// Right now, this query will never clean up.
// It will stay in querySubs, and get stored in local storage.
// This will at least clean it up and remove it from local storage.
if (q) {
// This must be a query error
this.querySubs.set((prev) => {

@@ -437,3 +431,4 @@ const hash = weakHash(q);

const isInitError = ((_b = msg["original-event"]) === null || _b === void 0 ? void 0 : _b.op) === "init";
if (isInitError) { // We failed to init
if (isInitError) {
// We failed to init
const errorMessage = {

@@ -446,3 +441,3 @@ message: msg.message || "Uh-oh, something went wrong. Ping Joe & Stopa.",

}
// We've caught some error which has no corresponding listener.
// We've caught some error which has no corresponding listener.
// Let's console.error to let the user know.

@@ -499,4 +494,2 @@ const errorObj = Object.assign({}, msg);

}
// (XXX)
// There is a race condition:
// When we `pushTx`, it's possible that we don't yet have `this.attrs`

@@ -554,4 +547,2 @@ // This means that `tx-steps` in `pendingMutations` will include `add-attr`

// Transact
// (XXX): We can probably get rid of this by applying these pending mutations
// to the commited store
optimisticAttrs() {

@@ -621,7 +612,5 @@ var _a;

this._trySend(eventId, mutation);
// (XXX): If a transaction is pending for over 3 seconds,
// If a transaction is pending for over 3 seconds,
// we want to unblock the UX, so mark it as pending
// and keep trying to process the transaction in the background
// TODO: add a global success/failure hook
// for mutations that exceed this timeout
window.setTimeout(() => {

@@ -634,3 +623,3 @@ this._finishTransaction(true, "pending", eventId);

}
// (XXX): If we are here, this means that we have sent this mutation, we are online
// If we are here, this means that we have sent this mutation, we are online
// but we have not received a response. If it's this long, something must be worng,

@@ -678,3 +667,7 @@ // so we error with a timeout.

return;
this._trySendAuthed(uuid(), { op: "set-presence", "room-id": roomId, data: user });
this._trySendAuthed(uuid(), {
op: "set-presence",
"room-id": roomId,
data: user,
});
});

@@ -1037,3 +1030,7 @@ }

const data = Object.assign(Object.assign({}, (_b = (_a = this._presence[roomId]) === null || _a === void 0 ? void 0 : _a.result) === null || _b === void 0 ? void 0 : _b.user), partialData);
this._trySendAuthed(uuid(), { op: "set-presence", "room-id": roomId, data });
this._trySendAuthed(uuid(), {
op: "set-presence",
"room-id": roomId,
data,
});
this._presence[roomId] = this._presence[roomId] || {};

@@ -1133,3 +1130,35 @@ this._presence[roomId].result = this._presence[roomId].result || {};

}
// --------
// Storage
upload(path, file) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const currentUser = yield this.getCurrentUser();
const refreshToken = (_a = currentUser === null || currentUser === void 0 ? void 0 : currentUser.user) === null || _a === void 0 ? void 0 : _a.refresh_token;
const fileName = path || file.name;
const url = yield StorageApi.getSignedUploadUrl({
apiURI: this.config.apiURI,
appId: this.config.appId,
fileName: fileName,
refreshToken: refreshToken,
});
const success = yield StorageApi.upload(url, file);
return this.getDownloadUrl(fileName);
});
}
getDownloadUrl(path) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const currentUser = yield this.getCurrentUser();
const refreshToken = (_a = currentUser === null || currentUser === void 0 ? void 0 : currentUser.user) === null || _a === void 0 ? void 0 : _a.refresh_token;
const url = yield StorageApi.getDownloadUrl({
apiURI: this.config.apiURI,
appId: this.config.appId,
path: path,
refreshToken: refreshToken,
});
return url;
});
}
}
//# sourceMappingURL=Reactor.js.map
export { graph, entity, string, number, boolean, json, any, InstaQLQueryResult, };
declare function graph<EntitiesWithoutLinks extends EntitiesDef, const Links extends LinksDef<any>>(appId: string, entities: EntitiesWithoutLinks, links: Links): InstantGraph<EntitiesWithLinks<EntitiesWithoutLinks, Links>, Links>;
declare function entity<A extends AttrsDefs>(attrs: A): EntityDef<A, {}>;
/**
* Your app's graph definition.
*
* @template EntitiesWithoutLinks - The type definition for entities without links.
* @template Links - The type definition for links.
* @param {string} appId - The ID of the application.
* @param {EntitiesWithoutLinks} entities - The entities without links.
* @param {Links} links - The links between entities.
* @returns {InstantGraph} - The new InstantGraph instance.
*/
declare function graph<EntitiesWithoutLinks extends EntitiesDef, const Links extends LinksDef<EntitiesWithoutLinks>>(appId: string, entities: EntitiesWithoutLinks, links: Links): InstantGraph<EntitiesWithLinks<EntitiesWithoutLinks, Links>, LinksDef<any>>;
/**
* Creates an entity definition with the given attributes.
*
* @template Attrs - The type of the attributes.
* @param {Attrs extends AttrsDefs} attrs - The attributes for the entity.
* @returns {EntityDef<Attrs, {}>} - The entity definition.
*/
declare function entity<Attrs extends AttrsDefs>(attrs: Attrs): EntityDef<Attrs, {}>;
declare function string(): DataAttrDef<string, true>;

@@ -20,3 +37,2 @@ declare function number(): DataAttrDef<number, true>;

unique: boolean;
clientValidator?: (value: ValueType) => boolean;
};

@@ -26,10 +42,8 @@ constructor(valueType: ValueTypes, required: IsRequired, config?: {

unique: boolean;
clientValidator?: (value: ValueType) => boolean;
});
optional(): DataAttrDef<ValueType, false>;
unique(): DataAttrDef<ValueType, IsRequired>;
indexed(): DataAttrDef<ValueType, IsRequired>;
clientValidate(clientValidator: (value: ValueType) => boolean): DataAttrDef<ValueType, IsRequired>;
unique(): DataAttrDef<unknown, IsRequired>;
indexed(): DataAttrDef<unknown, IsRequired>;
}
declare class InstantGraph<Entities extends EntitiesDef, Links extends LinksDef<EntitiesDef>> {
declare class InstantGraph<Entities extends EntitiesDef, Links extends LinksDef<Entities>> {
appId: string;

@@ -53,8 +67,12 @@ entities: Entities;

type LinkDef<Entities extends EntitiesDef, FwdEntity extends keyof Entities, FwdAttr extends string, FwdCardinality extends CardinalityKind, RevEntity extends keyof Entities, RevAttr extends string, RevCardinality extends CardinalityKind> = {
from: FwdEntity;
fromHas: FwdCardinality;
fromAttr: FwdAttr;
to: RevEntity;
toHas: RevCardinality;
toAttr: RevAttr;
forward: {
on: FwdEntity;
label: FwdAttr;
has: FwdCardinality;
};
reverse: {
on: RevEntity;
label: RevAttr;
has: RevCardinality;
};
};

@@ -68,3 +86,3 @@ type EntitiesWithLinks<Entities extends EntitiesDef, Links extends LinksDef<Entities>> = {

};
type EntityForwardLinksMap<EntityName extends keyof Entities, Entities extends EntitiesDef, Links extends LinksDef<Entities>, LinkIndexFwd = LinksIndexedByEntity<Entities, Links, "from", "fromAttr">> = EntityName extends keyof LinkIndexFwd ? {
type EntityForwardLinksMap<EntityName extends keyof Entities, Entities extends EntitiesDef, Links extends LinksDef<Entities>, LinkIndexFwd = LinksIndexedByEntity<Entities, Links, "reverse">> = EntityName extends keyof LinkIndexFwd ? {
[LinkName in keyof LinkIndexFwd[EntityName]]: LinkIndexFwd[EntityName][LinkName] extends LinkDef<Entities, any, any, infer Cardinality, infer RelatedEntityName, any, any> ? {

@@ -75,3 +93,3 @@ entityName: RelatedEntityName;

} : {};
type EntityReverseLinksMap<EntityName extends keyof Entities, Entities extends EntitiesDef, Links extends LinksDef<Entities>, RevLinkIndex = LinksIndexedByEntity<Entities, Links, "to", "toAttr">> = EntityName extends keyof RevLinkIndex ? {
type EntityReverseLinksMap<EntityName extends keyof Entities, Entities extends EntitiesDef, Links extends LinksDef<Entities>, RevLinkIndex = LinksIndexedByEntity<Entities, Links, "forward">> = EntityName extends keyof RevLinkIndex ? {
[LinkName in keyof RevLinkIndex[EntityName]]: RevLinkIndex[EntityName][LinkName] extends LinkDef<Entities, infer RelatedEntityName, any, any, any, any, infer Cardinality> ? {

@@ -82,5 +100,5 @@ entityName: RelatedEntityName;

} : {};
type LinksIndexedByEntity<Entities extends EntitiesDef, Links extends LinksDef<Entities>, Direction extends "from" | "to", DirectionAttrName extends "fromAttr" | "toAttr"> = {
type LinksIndexedByEntity<Entities extends EntitiesDef, Links extends LinksDef<Entities>, Direction extends "forward" | "reverse"> = {
[FwdEntity in keyof Entities]: {
[LinkName in keyof Links as Links[LinkName][Direction] extends FwdEntity ? Links[LinkName][DirectionAttrName] : never]: Links[LinkName] extends LinkDef<Entities, infer FwdEntity, infer FwdAttr, infer FwdCardinality, infer RevEntity, infer RevAttr, infer RevCardinality> ? LinkDef<Entities, FwdEntity, FwdAttr, FwdCardinality, RevEntity, RevAttr, RevCardinality> : never;
[LinkName in keyof Links as Links[LinkName][Direction]["on"] extends FwdEntity ? Links[LinkName][Direction]["label"] : never]: Links[LinkName] extends LinkDef<Entities, infer FwdEntity, infer FwdAttr, infer FwdCardinality, infer RevEntity, infer RevAttr, infer RevCardinality> ? LinkDef<Entities, FwdEntity, FwdAttr, FwdCardinality, RevEntity, RevAttr, RevCardinality> : never;
};

@@ -87,0 +105,0 @@ };

@@ -8,5 +8,28 @@ export {

// API
/**
* Your app's graph definition.
*
* @template EntitiesWithoutLinks - The type definition for entities without links.
* @template Links - The type definition for links.
* @param {string} appId - The ID of the application.
* @param {EntitiesWithoutLinks} entities - The entities without links.
* @param {Links} links - The links between entities.
* @returns {InstantGraph} - The new InstantGraph instance.
*/
function graph(appId, entities, links) {
return new InstantGraph(appId, enrichEntitiesWithLinks(entities, links), links);
return new InstantGraph(appId, enrichEntitiesWithLinks(entities, links),
// (XXX): LinksDef<any> stems from TypeScript’s inability to reconcile the
// type EntitiesWithLinks<EntitiesWithoutLinks, Links> with
// EntitiesWithoutLinks. TypeScript is strict about ensuring that types are
// correctly aligned and does not allow for substituting a type that might
// be broader or have additional properties.
links);
}
/**
* Creates an entity definition with the given attributes.
*
* @template Attrs - The type of the attributes.
* @param {Attrs extends AttrsDefs} attrs - The attributes for the entity.
* @returns {EntityDef<Attrs, {}>} - The entity definition.
*/
function entity(attrs) {

@@ -36,11 +59,11 @@ return { attrs, links: {} };

for (const linkDef of Object.values(links)) {
(_a = linksIndex.fwd)[_b = linkDef.from] || (_a[_b] = {});
(_c = linksIndex.rev)[_d = linkDef.to] || (_c[_d] = {});
linksIndex.fwd[linkDef.from][linkDef.fromAttr] = {
entityName: linkDef.to,
cardinality: linkDef.fromHas,
(_a = linksIndex.fwd)[_b = linkDef.forward.on] || (_a[_b] = {});
(_c = linksIndex.rev)[_d = linkDef.reverse.on] || (_c[_d] = {});
linksIndex.fwd[linkDef.forward.on][linkDef.forward.label] = {
entityName: linkDef.reverse.on,
cardinality: linkDef.forward.has,
};
linksIndex.rev[linkDef.to][linkDef.toAttr] = {
entityName: linkDef.from,
cardinality: linkDef.toHas,
linksIndex.rev[linkDef.reverse.on][linkDef.reverse.label] = {
entityName: linkDef.forward.on,
cardinality: linkDef.reverse.has,
};

@@ -75,5 +98,2 @@ }

}
clientValidate(clientValidator) {
return new DataAttrDef(this.valueType, this.required, Object.assign(Object.assign({}, this.config), { clientValidator }));
}
}

@@ -80,0 +100,0 @@ class InstantGraph {

@@ -133,6 +133,2 @@ import { produce, enableMapSet } from "immer";

if (!attr) {
// (XXX): Due to the way we're handling attrs, it's
// possible to enter a state where we receive a triple without an attr.
// See: https://github.com/jsventures/instant-local/pull/132 for details.
// For now, if we receive a command without an attr, we no-op.
return;

@@ -212,9 +208,4 @@ }

const attr = getAttr(store.attrs, aid);
// (XXX): Due to the way we're handling attrs, it's
// possible to enter a state where we receive a triple without an attr.
// See: https://github.com/jsventures/instant-local/pull/132 for details.
// For now, if we receive a command without an attr, we no-op.
if (!attr)
return;
// (XXX): We don't support merge for links
if (!isBlob(attr))

@@ -283,6 +274,2 @@ throw new Error("merge operation is not supported for links");

}
// (XXX):
// Making `deleteAttr` and `updateAttr` no-op.
// This is because right now it's really easy to get inconsistent
// attrs inside Reactor.
function deleteAttr(store, [id]) {

@@ -289,0 +276,0 @@ if (!store.attrs[id])

@@ -225,2 +225,4 @@ /**

_notifyBroadcastSubs(room: any, topic: any, msg: any): void;
upload(path: any, file: any): Promise<any>;
getDownloadUrl(path: any): Promise<any>;
}

@@ -227,0 +229,0 @@ import IndexedDBStorage from "./IndexedDBStorage";

@@ -48,6 +48,7 @@ "use strict";

const authAPI = __importStar(require("./authAPI"));
const StorageApi = __importStar(require("./StorageAPI"));
const presence_1 = require("./presence");
const Deferred_1 = require("./utils/Deferred");
const PersistedObject_1 = require("./utils/PersistedObject");
const triples_1 = require("./triples");
const instaqlResult_1 = require("./model/instaqlResult");
const object_1 = require("./utils/object");

@@ -197,9 +198,7 @@ const json_1 = require("./utils/json");

"refresh-token": (_a = resp.user) === null || _a === void 0 ? void 0 : _a["refresh_token"],
// Dangerous:
// If an admin token is provided for an app, we will
// skip all permission checks. Doing this for now
// to unblock query explorer. This option is not part
// of the standard `Config` type, as we don't want to expose
// this to users just yet. Perhaps in the future we _will_
// want this, but we want to sleep on it for a bit
// skip all permission checks. This is an advanced feature,
// to let users write internal tools
// This option is not exposed in `Config`, as it's
// not ready for prme time
"__admin-token": this.config.__adminToken,

@@ -232,3 +231,3 @@ });

this.config = Object.assign(Object.assign({}, defaultConfig), config);
// (XXX) This is to protect us against running
// This is to protect us against running
// server-side.

@@ -259,3 +258,2 @@ // Incidentally, window is defined in react-native

NetworkListener.listen((isOnline) => {
// (XXX):
// We do this because react native's NetInfo

@@ -328,3 +326,3 @@ // fires multiple online events.

this._flushPendingMessages();
// (XXX-EPH): set session-id, so we know
// (EPH): set session-id, so we know
// which item is us

@@ -339,3 +337,3 @@ this._sessionId = msg["session-id"];

const aggregate = (_d = (_c = result === null || result === void 0 ? void 0 : result[0]) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d["aggregate"];
const triples = (0, triples_1.extractTriples)(result);
const triples = (0, instaqlResult_1.extractTriples)(result);
const store = s.createStore(this.attrs, triples);

@@ -357,3 +355,3 @@ this.querySubs.set((prev) => {

const hash = (0, weakHash_1.default)(q);
const triples = (0, triples_1.extractTriples)(result);
const triples = (0, instaqlResult_1.extractTriples)(result);
const store = s.createStore(this.attrs, triples);

@@ -429,6 +427,6 @@ const pageInfo = (_b = (_a = result === null || result === void 0 ? void 0 : result[0]) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b["page-info"];

var _a, _b;
// (XXX): Error handling is spaghetti right now.
const eventId = msg["client-event-id"];
const prevMutation = this.pendingMutations.currentValue.get(eventId);
if (prevMutation) { // This must be a transaction error
if (prevMutation) {
// This must be a transaction error
this.pendingMutations.set((prev) => {

@@ -449,8 +447,4 @@ prev.delete(eventId);

const q = msg.q || ((_a = msg["original-event"]) === null || _a === void 0 ? void 0 : _a.q);
if (q) { // This must be a query error
// (XXX) This is a hack.
// Imagine a user writes a malformed query.
// Right now, this query will never clean up.
// It will stay in querySubs, and get stored in local storage.
// This will at least clean it up and remove it from local storage.
if (q) {
// This must be a query error
this.querySubs.set((prev) => {

@@ -467,3 +461,4 @@ const hash = (0, weakHash_1.default)(q);

const isInitError = ((_b = msg["original-event"]) === null || _b === void 0 ? void 0 : _b.op) === "init";
if (isInitError) { // We failed to init
if (isInitError) {
// We failed to init
const errorMessage = {

@@ -476,3 +471,3 @@ message: msg.message || "Uh-oh, something went wrong. Ping Joe & Stopa.",

}
// We've caught some error which has no corresponding listener.
// We've caught some error which has no corresponding listener.
// Let's console.error to let the user know.

@@ -529,4 +524,2 @@ const errorObj = Object.assign({}, msg);

}
// (XXX)
// There is a race condition:
// When we `pushTx`, it's possible that we don't yet have `this.attrs`

@@ -584,4 +577,2 @@ // This means that `tx-steps` in `pendingMutations` will include `add-attr`

// Transact
// (XXX): We can probably get rid of this by applying these pending mutations
// to the commited store
optimisticAttrs() {

@@ -651,7 +642,5 @@ var _a;

this._trySend(eventId, mutation);
// (XXX): If a transaction is pending for over 3 seconds,
// If a transaction is pending for over 3 seconds,
// we want to unblock the UX, so mark it as pending
// and keep trying to process the transaction in the background
// TODO: add a global success/failure hook
// for mutations that exceed this timeout
window.setTimeout(() => {

@@ -664,3 +653,3 @@ this._finishTransaction(true, "pending", eventId);

}
// (XXX): If we are here, this means that we have sent this mutation, we are online
// If we are here, this means that we have sent this mutation, we are online
// but we have not received a response. If it's this long, something must be worng,

@@ -708,3 +697,7 @@ // so we error with a timeout.

return;
this._trySendAuthed((0, uuid_1.default)(), { op: "set-presence", "room-id": roomId, data: user });
this._trySendAuthed((0, uuid_1.default)(), {
op: "set-presence",
"room-id": roomId,
data: user,
});
});

@@ -1067,3 +1060,7 @@ }

const data = Object.assign(Object.assign({}, (_b = (_a = this._presence[roomId]) === null || _a === void 0 ? void 0 : _a.result) === null || _b === void 0 ? void 0 : _b.user), partialData);
this._trySendAuthed((0, uuid_1.default)(), { op: "set-presence", "room-id": roomId, data });
this._trySendAuthed((0, uuid_1.default)(), {
op: "set-presence",
"room-id": roomId,
data,
});
this._presence[roomId] = this._presence[roomId] || {};

@@ -1163,4 +1160,36 @@ this._presence[roomId].result = this._presence[roomId].result || {};

}
// --------
// Storage
upload(path, file) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const currentUser = yield this.getCurrentUser();
const refreshToken = (_a = currentUser === null || currentUser === void 0 ? void 0 : currentUser.user) === null || _a === void 0 ? void 0 : _a.refresh_token;
const fileName = path || file.name;
const url = yield StorageApi.getSignedUploadUrl({
apiURI: this.config.apiURI,
appId: this.config.appId,
fileName: fileName,
refreshToken: refreshToken,
});
const success = yield StorageApi.upload(url, file);
return this.getDownloadUrl(fileName);
});
}
getDownloadUrl(path) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const currentUser = yield this.getCurrentUser();
const refreshToken = (_a = currentUser === null || currentUser === void 0 ? void 0 : currentUser.user) === null || _a === void 0 ? void 0 : _a.refresh_token;
const url = yield StorageApi.getDownloadUrl({
apiURI: this.config.apiURI,
appId: this.config.appId,
path: path,
refreshToken: refreshToken,
});
return url;
});
}
}
exports.default = Reactor;
//# sourceMappingURL=Reactor.js.map
export { graph, entity, string, number, boolean, json, any, InstaQLQueryResult, };
declare function graph<EntitiesWithoutLinks extends EntitiesDef, const Links extends LinksDef<any>>(appId: string, entities: EntitiesWithoutLinks, links: Links): InstantGraph<EntitiesWithLinks<EntitiesWithoutLinks, Links>, Links>;
declare function entity<A extends AttrsDefs>(attrs: A): EntityDef<A, {}>;
/**
* Your app's graph definition.
*
* @template EntitiesWithoutLinks - The type definition for entities without links.
* @template Links - The type definition for links.
* @param {string} appId - The ID of the application.
* @param {EntitiesWithoutLinks} entities - The entities without links.
* @param {Links} links - The links between entities.
* @returns {InstantGraph} - The new InstantGraph instance.
*/
declare function graph<EntitiesWithoutLinks extends EntitiesDef, const Links extends LinksDef<EntitiesWithoutLinks>>(appId: string, entities: EntitiesWithoutLinks, links: Links): InstantGraph<EntitiesWithLinks<EntitiesWithoutLinks, Links>, LinksDef<any>>;
/**
* Creates an entity definition with the given attributes.
*
* @template Attrs - The type of the attributes.
* @param {Attrs extends AttrsDefs} attrs - The attributes for the entity.
* @returns {EntityDef<Attrs, {}>} - The entity definition.
*/
declare function entity<Attrs extends AttrsDefs>(attrs: Attrs): EntityDef<Attrs, {}>;
declare function string(): DataAttrDef<string, true>;

@@ -20,3 +37,2 @@ declare function number(): DataAttrDef<number, true>;

unique: boolean;
clientValidator?: (value: ValueType) => boolean;
};

@@ -26,10 +42,8 @@ constructor(valueType: ValueTypes, required: IsRequired, config?: {

unique: boolean;
clientValidator?: (value: ValueType) => boolean;
});
optional(): DataAttrDef<ValueType, false>;
unique(): DataAttrDef<ValueType, IsRequired>;
indexed(): DataAttrDef<ValueType, IsRequired>;
clientValidate(clientValidator: (value: ValueType) => boolean): DataAttrDef<ValueType, IsRequired>;
unique(): DataAttrDef<unknown, IsRequired>;
indexed(): DataAttrDef<unknown, IsRequired>;
}
declare class InstantGraph<Entities extends EntitiesDef, Links extends LinksDef<EntitiesDef>> {
declare class InstantGraph<Entities extends EntitiesDef, Links extends LinksDef<Entities>> {
appId: string;

@@ -53,8 +67,12 @@ entities: Entities;

type LinkDef<Entities extends EntitiesDef, FwdEntity extends keyof Entities, FwdAttr extends string, FwdCardinality extends CardinalityKind, RevEntity extends keyof Entities, RevAttr extends string, RevCardinality extends CardinalityKind> = {
from: FwdEntity;
fromHas: FwdCardinality;
fromAttr: FwdAttr;
to: RevEntity;
toHas: RevCardinality;
toAttr: RevAttr;
forward: {
on: FwdEntity;
label: FwdAttr;
has: FwdCardinality;
};
reverse: {
on: RevEntity;
label: RevAttr;
has: RevCardinality;
};
};

@@ -68,3 +86,3 @@ type EntitiesWithLinks<Entities extends EntitiesDef, Links extends LinksDef<Entities>> = {

};
type EntityForwardLinksMap<EntityName extends keyof Entities, Entities extends EntitiesDef, Links extends LinksDef<Entities>, LinkIndexFwd = LinksIndexedByEntity<Entities, Links, "from", "fromAttr">> = EntityName extends keyof LinkIndexFwd ? {
type EntityForwardLinksMap<EntityName extends keyof Entities, Entities extends EntitiesDef, Links extends LinksDef<Entities>, LinkIndexFwd = LinksIndexedByEntity<Entities, Links, "reverse">> = EntityName extends keyof LinkIndexFwd ? {
[LinkName in keyof LinkIndexFwd[EntityName]]: LinkIndexFwd[EntityName][LinkName] extends LinkDef<Entities, any, any, infer Cardinality, infer RelatedEntityName, any, any> ? {

@@ -75,3 +93,3 @@ entityName: RelatedEntityName;

} : {};
type EntityReverseLinksMap<EntityName extends keyof Entities, Entities extends EntitiesDef, Links extends LinksDef<Entities>, RevLinkIndex = LinksIndexedByEntity<Entities, Links, "to", "toAttr">> = EntityName extends keyof RevLinkIndex ? {
type EntityReverseLinksMap<EntityName extends keyof Entities, Entities extends EntitiesDef, Links extends LinksDef<Entities>, RevLinkIndex = LinksIndexedByEntity<Entities, Links, "forward">> = EntityName extends keyof RevLinkIndex ? {
[LinkName in keyof RevLinkIndex[EntityName]]: RevLinkIndex[EntityName][LinkName] extends LinkDef<Entities, infer RelatedEntityName, any, any, any, any, infer Cardinality> ? {

@@ -82,5 +100,5 @@ entityName: RelatedEntityName;

} : {};
type LinksIndexedByEntity<Entities extends EntitiesDef, Links extends LinksDef<Entities>, Direction extends "from" | "to", DirectionAttrName extends "fromAttr" | "toAttr"> = {
type LinksIndexedByEntity<Entities extends EntitiesDef, Links extends LinksDef<Entities>, Direction extends "forward" | "reverse"> = {
[FwdEntity in keyof Entities]: {
[LinkName in keyof Links as Links[LinkName][Direction] extends FwdEntity ? Links[LinkName][DirectionAttrName] : never]: Links[LinkName] extends LinkDef<Entities, infer FwdEntity, infer FwdAttr, infer FwdCardinality, infer RevEntity, infer RevAttr, infer RevCardinality> ? LinkDef<Entities, FwdEntity, FwdAttr, FwdCardinality, RevEntity, RevAttr, RevCardinality> : never;
[LinkName in keyof Links as Links[LinkName][Direction]["on"] extends FwdEntity ? Links[LinkName][Direction]["label"] : never]: Links[LinkName] extends LinkDef<Entities, infer FwdEntity, infer FwdAttr, infer FwdCardinality, infer RevEntity, infer RevAttr, infer RevCardinality> ? LinkDef<Entities, FwdEntity, FwdAttr, FwdCardinality, RevEntity, RevAttr, RevCardinality> : never;
};

@@ -87,0 +105,0 @@ };

@@ -12,5 +12,28 @@ "use strict";

// API
/**
* Your app's graph definition.
*
* @template EntitiesWithoutLinks - The type definition for entities without links.
* @template Links - The type definition for links.
* @param {string} appId - The ID of the application.
* @param {EntitiesWithoutLinks} entities - The entities without links.
* @param {Links} links - The links between entities.
* @returns {InstantGraph} - The new InstantGraph instance.
*/
function graph(appId, entities, links) {
return new InstantGraph(appId, enrichEntitiesWithLinks(entities, links), links);
return new InstantGraph(appId, enrichEntitiesWithLinks(entities, links),
// (XXX): LinksDef<any> stems from TypeScript’s inability to reconcile the
// type EntitiesWithLinks<EntitiesWithoutLinks, Links> with
// EntitiesWithoutLinks. TypeScript is strict about ensuring that types are
// correctly aligned and does not allow for substituting a type that might
// be broader or have additional properties.
links);
}
/**
* Creates an entity definition with the given attributes.
*
* @template Attrs - The type of the attributes.
* @param {Attrs extends AttrsDefs} attrs - The attributes for the entity.
* @returns {EntityDef<Attrs, {}>} - The entity definition.
*/
function entity(attrs) {

@@ -40,11 +63,11 @@ return { attrs, links: {} };

for (const linkDef of Object.values(links)) {
(_a = linksIndex.fwd)[_b = linkDef.from] || (_a[_b] = {});
(_c = linksIndex.rev)[_d = linkDef.to] || (_c[_d] = {});
linksIndex.fwd[linkDef.from][linkDef.fromAttr] = {
entityName: linkDef.to,
cardinality: linkDef.fromHas,
(_a = linksIndex.fwd)[_b = linkDef.forward.on] || (_a[_b] = {});
(_c = linksIndex.rev)[_d = linkDef.reverse.on] || (_c[_d] = {});
linksIndex.fwd[linkDef.forward.on][linkDef.forward.label] = {
entityName: linkDef.reverse.on,
cardinality: linkDef.forward.has,
};
linksIndex.rev[linkDef.to][linkDef.toAttr] = {
entityName: linkDef.from,
cardinality: linkDef.toHas,
linksIndex.rev[linkDef.reverse.on][linkDef.reverse.label] = {
entityName: linkDef.forward.on,
cardinality: linkDef.reverse.has,
};

@@ -79,5 +102,2 @@ }

}
clientValidate(clientValidator) {
return new DataAttrDef(this.valueType, this.required, Object.assign(Object.assign({}, this.config), { clientValidator }));
}
}

@@ -84,0 +104,0 @@ class InstantGraph {

@@ -139,6 +139,2 @@ "use strict";

if (!attr) {
// (XXX): Due to the way we're handling attrs, it's
// possible to enter a state where we receive a triple without an attr.
// See: https://github.com/jsventures/instant-local/pull/132 for details.
// For now, if we receive a command without an attr, we no-op.
return;

@@ -218,9 +214,4 @@ }

const attr = getAttr(store.attrs, aid);
// (XXX): Due to the way we're handling attrs, it's
// possible to enter a state where we receive a triple without an attr.
// See: https://github.com/jsventures/instant-local/pull/132 for details.
// For now, if we receive a command without an attr, we no-op.
if (!attr)
return;
// (XXX): We don't support merge for links
if (!isBlob(attr))

@@ -289,6 +280,2 @@ throw new Error("merge operation is not supported for links");

}
// (XXX):
// Making `deleteAttr` and `updateAttr` no-op.
// This is because right now it's really easy to get inconsistent
// attrs inside Reactor.
function deleteAttr(store, [id]) {

@@ -295,0 +282,0 @@ if (!store.attrs[id])

{
"name": "@instantdb/core",
"version": "0.11.3",
"version": "0.11.4",
"description": "Instant's core local abstraction",

@@ -5,0 +5,0 @@ "main": "dist/index.js",

import { QueryResponse, PageInfoResponse } from "./queryTypes";
export type User = { id: string; email: string; refresh_token: string };
// ----
// React-related types
//
// (XXX): Right now there is some copy-pasta
// in `@instantdb/react` and `@instantdb/react-native`
//
// To reduce as much as we can, I went ahead and moved
// some of the shared types here.
//
// Eventually we may want a `react-shared` package, but
// this does enough for now.
export type AuthResult =

@@ -23,19 +12,1 @@ | { user: User | undefined; error: undefined }

| { isLoading: false; error: undefined; user: User | undefined };
/**
* @deprecated since 0.7.0
*/
export type QueryState<Q, Schema = {}> =
| { isLoading: true; error: undefined; data: undefined; pageInfo: undefined }
| {
isLoading: false;
error: { message: string };
data: undefined;
pageInfo: undefined;
}
| {
isLoading: false;
error: undefined;
data: QueryResponse<Q, Schema>;
pageInfo: PageInfoResponse<Q>;
};

@@ -14,3 +14,3 @@ import Reactor from "./Reactor";

} from "./queryTypes";
import { QueryState, AuthState, User, AuthResult } from "./clientTypes";
import { AuthState, User, AuthResult } from "./clientTypes";
import {

@@ -167,2 +167,3 @@ PresenceOpts,

public auth: Auth;
public storage: Storage;

@@ -172,2 +173,3 @@ constructor(reactor: Reactor<RoomSchema>) {

this.auth = new Auth(this._reactor);
this.storage = new Storage(this._reactor);
}

@@ -445,2 +447,32 @@

/**
* Functions to manage file storage.
*/
class Storage {
constructor(private db: Reactor) {}
/**
* Uploads file at the provided path.
*
* @see https://docs.instantdb.com/docs/storage
* @example
* const [file] = e.target.files; // result of file input
* const url = await db.storage.put('photos/demo.png', file);
*/
put = (pathname: string, file: File) => {
return this.db.upload(pathname, file);
};
/**
* Retrieves a download URL for the provided path.
*
* @see https://docs.instantdb.com/docs/storage
* @example
* const url = await db.storage.getDownloadUrl('photos/demo.png');
*/
getDownloadUrl = (pathname: string) => {
return this.db.getDownloadUrl(pathname);
};
}
// util

@@ -473,2 +505,3 @@

Auth,
Storage,

@@ -482,3 +515,2 @@ // types

TransactionChunk,
QueryState,
AuthState,

@@ -485,0 +517,0 @@ User,

@@ -49,4 +49,16 @@ import { query as datalogQuery } from "./datalog";

function getPrimaryKeyAttr(store, ns) {
const primary = Object.values(store.attrs).find(
(a) => a["primary?"] && a["forward-identity"]?.[1] === ns,
);
if (primary) {
return primary;
}
return getAttrByFwdIdentName(store.attrs, ns, "id");
}
function idAttr(store, ns) {
const attr = getAttrByFwdIdentName(store.attrs, ns, "id");
const attr = getPrimaryKeyAttr(store, ns);
if (!attr) {

@@ -374,2 +386,11 @@ throw new AttrNotFoundError(`Could not find id attr for ${ns}`);

function determineOrder(form) {
const orderOpts = form.$?.order;
if (!orderOpts) {
return "asc";
}
return orderOpts[Object.keys(orderOpts)[0]] || "asc";
}
/**

@@ -398,9 +419,10 @@ * Given a query like:

}
const where = withJoin(makeWhere(store, etype, level, form.$?.where), join);
const where = withJoin(makeWhere(store, etype, level, form.$?.where), join);
const find = makeFind(makeVarImpl, etype, level);
const objs = runDataloadAndReturnObjects(
store,
etype,
form.$?.order?.serverCreatedAt || "asc",
determineOrder(form),
pageInfo,

@@ -407,0 +429,0 @@ { where, find },

@@ -12,6 +12,7 @@ // @ts-check

import * as authAPI from "./authAPI";
import * as StorageApi from "./StorageAPI";
import { buildPresenceSlice, hasPresenceResponseChanged } from "./presence";
import { Deferred } from "./utils/Deferred";
import { PersistedObject } from "./utils/PersistedObject";
import { extractTriples } from "./triples";
import { extractTriples } from "./model/instaqlResult";
import { areObjectsDeepEqual } from "./utils/object";

@@ -78,3 +79,3 @@ import { fromJSONWithMaps, toJSONWithMaps } from "./utils/json";

this.config = { ...defaultConfig, ...config };
// (XXX) This is to protect us against running
// This is to protect us against running
// server-side.

@@ -110,3 +111,2 @@ // Incidentally, window is defined in react-native

NetworkListener.listen((isOnline) => {
// (XXX):
// We do this because react native's NetInfo

@@ -153,5 +153,5 @@ // fires multiple online events.

this.mutationDeferredStore.delete(clientId);
if (!dfd && !ok) {
if (!dfd && !ok) {
// console.erroring here, as there are no listeners to let know
console.error("Mutation failed", { status, clientId, ...errDetails })
console.error("Mutation failed", { status, clientId, ...errDetails });
}

@@ -258,3 +258,3 @@ if (!dfd) {

this._flushPendingMessages();
// (XXX-EPH): set session-id, so we know
// (EPH): set session-id, so we know
// which item is us

@@ -363,6 +363,6 @@ this._sessionId = msg["session-id"];

_handleReceiveError(msg) {
// (XXX): Error handling is spaghetti right now.
const eventId = msg["client-event-id"];
const prevMutation = this.pendingMutations.currentValue.get(eventId);
if (prevMutation) { // This must be a transaction error
if (prevMutation) {
// This must be a transaction error
this.pendingMutations.set((prev) => {

@@ -380,12 +380,8 @@ prev.delete(eventId);

this._finishTransaction(false, "error", eventId, errDetails);
return
return;
}
const q = msg.q || msg["original-event"]?.q;
if (q) { // This must be a query error
// (XXX) This is a hack.
// Imagine a user writes a malformed query.
// Right now, this query will never clean up.
// It will stay in querySubs, and get stored in local storage.
// This will at least clean it up and remove it from local storage.
const q = msg.q || msg["original-event"]?.q;
if (q) {
// This must be a query error
this.querySubs.set((prev) => {

@@ -397,17 +393,20 @@ const hash = weakHash(q);

this.notifyQueryError(weakHash(q), {
message: msg.message || "Uh-oh, something went wrong. Ping Joe & Stopa.",
message:
msg.message || "Uh-oh, something went wrong. Ping Joe & Stopa.",
});
return
return;
}
const isInitError = msg["original-event"]?.op === "init";
if (isInitError) { // We failed to init
if (isInitError) {
// We failed to init
const errorMessage = {
message: msg.message || "Uh-oh, something went wrong. Ping Joe & Stopa.",
message:
msg.message || "Uh-oh, something went wrong. Ping Joe & Stopa.",
};
this._setStatus(STATUS.ERRORED, errorMessage);
this.notifyAll();
return
return;
}
// We've caught some error which has no corresponding listener.
// We've caught some error which has no corresponding listener.
// Let's console.error to let the user know.

@@ -421,3 +420,3 @@ const errorObj = { ...msg };

"This error comes with some debugging information. Here it is: \n",
msg.hint
msg.hint,
);

@@ -472,4 +471,2 @@ }

// (XXX)
// There is a race condition:
// When we `pushTx`, it's possible that we don't yet have `this.attrs`

@@ -537,4 +534,2 @@ // This means that `tx-steps` in `pendingMutations` will include `add-attr`

// (XXX): We can probably get rid of this by applying these pending mutations
// to the commited store
optimisticAttrs() {

@@ -676,7 +671,5 @@ const pendingMutationSteps = [

// (XXX): If a transaction is pending for over 3 seconds,
// If a transaction is pending for over 3 seconds,
// we want to unblock the UX, so mark it as pending
// and keep trying to process the transaction in the background
// TODO: add a global success/failure hook
// for mutations that exceed this timeout
window.setTimeout(() => {

@@ -690,4 +683,4 @@ this._finishTransaction(true, "pending", eventId);

}
// (XXX): If we are here, this means that we have sent this mutation, we are online
// If we are here, this means that we have sent this mutation, we are online
// but we have not received a response. If it's this long, something must be worng,

@@ -703,3 +696,3 @@ // so we error with a timeout.

this._finishTransaction(false, "timeout", eventId);
console.error("mutation timed out", mut);

@@ -709,4 +702,2 @@ }

}
}

@@ -745,3 +736,7 @@

if (!user) return;
this._trySendAuthed(uuid(), { op: "set-presence", "room-id": roomId, data: user });
this._trySendAuthed(uuid(), {
op: "set-presence",
"room-id": roomId,
data: user,
});
});

@@ -759,3 +754,3 @@ }

if (this._ws.readyState !== WS_OPEN_STATUS) {
return
return;
}

@@ -773,9 +768,7 @@ this._ws.send(JSON.stringify({ "client-event-id": eventId, ...msg }));

"refresh-token": resp.user?.["refresh_token"],
// Dangerous:
// If an admin token is provided for an app, we will
// skip all permission checks. Doing this for now
// to unblock query explorer. This option is not part
// of the standard `Config` type, as we don't want to expose
// this to users just yet. Perhaps in the future we _will_
// want this, but we want to sleep on it for a bit
// skip all permission checks. This is an advanced feature,
// to let users write internal tools
// This option is not exposed in `Config`, as it's
// not ready for prme time
"__admin-token": this.config.__adminToken,

@@ -1184,3 +1177,7 @@ });

this._trySendAuthed(uuid(), { op: "set-presence", "room-id": roomId, data });
this._trySendAuthed(uuid(), {
op: "set-presence",
"room-id": roomId,
data,
});

@@ -1305,2 +1302,33 @@ this._presence[roomId] = this._presence[roomId] || {};

}
// --------
// Storage
async upload(path, file) {
const currentUser = await this.getCurrentUser();
const refreshToken = currentUser?.user?.refresh_token;
const fileName = path || file.name;
const url = await StorageApi.getSignedUploadUrl({
apiURI: this.config.apiURI,
appId: this.config.appId,
fileName: fileName,
refreshToken: refreshToken,
});
const success = await StorageApi.upload(url, file);
return this.getDownloadUrl(fileName);
}
async getDownloadUrl(path) {
const currentUser = await this.getCurrentUser();
const refreshToken = currentUser?.user?.refresh_token;
const url = await StorageApi.getDownloadUrl({
apiURI: this.config.apiURI,
appId: this.config.appId,
path: path,
refreshToken: refreshToken,
});
return url;
}
}

@@ -18,5 +18,15 @@ export {

/**
* Your app's graph definition.
*
* @template EntitiesWithoutLinks - The type definition for entities without links.
* @template Links - The type definition for links.
* @param {string} appId - The ID of the application.
* @param {EntitiesWithoutLinks} entities - The entities without links.
* @param {Links} links - The links between entities.
* @returns {InstantGraph} - The new InstantGraph instance.
*/
function graph<
EntitiesWithoutLinks extends EntitiesDef,
const Links extends LinksDef<any>,
const Links extends LinksDef<EntitiesWithoutLinks>,
>(appId: string, entities: EntitiesWithoutLinks, links: Links) {

@@ -26,7 +36,19 @@ return new InstantGraph(

enrichEntitiesWithLinks<EntitiesWithoutLinks, Links>(entities, links),
links,
// (XXX): LinksDef<any> stems from TypeScript’s inability to reconcile the
// type EntitiesWithLinks<EntitiesWithoutLinks, Links> with
// EntitiesWithoutLinks. TypeScript is strict about ensuring that types are
// correctly aligned and does not allow for substituting a type that might
// be broader or have additional properties.
links as LinksDef<any>,
);
}
function entity<A extends AttrsDefs>(attrs: A): EntityDef<A, {}> {
/**
* Creates an entity definition with the given attributes.
*
* @template Attrs - The type of the attributes.
* @param {Attrs extends AttrsDefs} attrs - The attributes for the entity.
* @returns {EntityDef<Attrs, {}>} - The entity definition.
*/
function entity<Attrs extends AttrsDefs>(attrs: Attrs): EntityDef<Attrs, {}> {
return { attrs, links: {} };

@@ -66,13 +88,13 @@ }

for (const linkDef of Object.values(links)) {
linksIndex.fwd[linkDef.from as string] ||= {};
linksIndex.rev[linkDef.to as string] ||= {};
linksIndex.fwd[linkDef.forward.on as string] ||= {};
linksIndex.rev[linkDef.reverse.on as string] ||= {};
linksIndex.fwd[linkDef.from as string][linkDef.fromAttr] = {
entityName: linkDef.to as string,
cardinality: linkDef.fromHas,
linksIndex.fwd[linkDef.forward.on as string][linkDef.forward.label] = {
entityName: linkDef.reverse.on as string,
cardinality: linkDef.forward.has,
};
linksIndex.rev[linkDef.to as string][linkDef.toAttr] = {
entityName: linkDef.from as string,
cardinality: linkDef.toHas,
linksIndex.rev[linkDef.reverse.on as string][linkDef.reverse.label] = {
entityName: linkDef.forward.on as string,
cardinality: linkDef.reverse.has,
};

@@ -111,3 +133,3 @@ }

unique: boolean;
clientValidator?: (value: ValueType) => boolean;
// clientValidator?: (value: ValueType) => boolean;
} = { indexed: false, unique: false },

@@ -134,8 +156,8 @@ ) {}

clientValidate(clientValidator: (value: ValueType) => boolean) {
return new DataAttrDef(this.valueType, this.required, {
...this.config,
clientValidator,
});
}
// clientValidate(clientValidator: (value: ValueType) => boolean) {
// return new DataAttrDef(this.valueType, this.required, {
// ...this.config,
// clientValidator,
// });
// }
}

@@ -145,3 +167,3 @@

Entities extends EntitiesDef,
Links extends LinksDef<EntitiesDef>,
Links extends LinksDef<Entities>,
> {

@@ -209,8 +231,12 @@ constructor(

> = {
from: FwdEntity;
fromHas: FwdCardinality;
fromAttr: FwdAttr;
to: RevEntity;
toHas: RevCardinality;
toAttr: RevAttr;
forward: {
on: FwdEntity;
label: FwdAttr;
has: FwdCardinality;
};
reverse: {
on: RevEntity;
label: RevAttr;
has: RevCardinality;
};
};

@@ -244,3 +270,3 @@

Links extends LinksDef<Entities>,
LinkIndexFwd = LinksIndexedByEntity<Entities, Links, "from", "fromAttr">,
LinkIndexFwd = LinksIndexedByEntity<Entities, Links, "reverse">,
> = EntityName extends keyof LinkIndexFwd

@@ -269,3 +295,3 @@ ? {

Links extends LinksDef<Entities>,
RevLinkIndex = LinksIndexedByEntity<Entities, Links, "to", "toAttr">,
RevLinkIndex = LinksIndexedByEntity<Entities, Links, "forward">,
> = EntityName extends keyof RevLinkIndex

@@ -293,8 +319,7 @@ ? {

Links extends LinksDef<Entities>,
Direction extends "from" | "to",
DirectionAttrName extends "fromAttr" | "toAttr",
Direction extends "forward" | "reverse",
> = {
[FwdEntity in keyof Entities]: {
[LinkName in keyof Links as Links[LinkName][Direction] extends FwdEntity
? Links[LinkName][DirectionAttrName]
[LinkName in keyof Links as Links[LinkName][Direction]["on"] extends FwdEntity
? Links[LinkName][Direction]["label"]
: never]: Links[LinkName] extends LinkDef<

@@ -301,0 +326,0 @@ Entities,

@@ -146,6 +146,2 @@ import { produce, enableMapSet } from "immer";

if (!attr) {
// (XXX): Due to the way we're handling attrs, it's
// possible to enter a state where we receive a triple without an attr.
// See: https://github.com/jsventures/instant-local/pull/132 for details.
// For now, if we receive a command without an attr, we no-op.
return;

@@ -232,9 +228,4 @@ }

// (XXX): Due to the way we're handling attrs, it's
// possible to enter a state where we receive a triple without an attr.
// See: https://github.com/jsventures/instant-local/pull/132 for details.
// For now, if we receive a command without an attr, we no-op.
if (!attr) return;
// (XXX): We don't support merge for links
if (!isBlob(attr))

@@ -313,6 +304,2 @@ throw new Error("merge operation is not supported for links");

// (XXX):
// Making `deleteAttr` and `updateAttr` no-op.
// This is because right now it's really easy to get inconsistent
// attrs inside Reactor.
function deleteAttr(store, [id]) {

@@ -319,0 +306,0 @@ if (!store.attrs[id]) return;

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 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 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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc