New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

replicache

Package Overview
Dependencies
Maintainers
3
Versions
106
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

replicache - npm Package Compare versions

Comparing version 0.0.6 to 0.0.7

out/wasm/debug/package.json

4

out.cjs/mod.d.ts
export { default } from './replicache.js';
export { TransactionClosedError } from './transaction-closed-error.js';
export { REPMHTTPInvoker } from './repm-invoker.js';
export { REPMHTTPInvoker, REPMWASMInvoker } from './repm-invoker.js';
export type { Mutator } from './replicache.js';

@@ -8,4 +8,4 @@ export type { ScanId } from './scan-id.js';

export type { DatabaseInfo } from './database-info.js';
export type { REPMInvoke } from './repm-invoker.js';
export type { REPMInvoke, Invoker } from './repm-invoker.js';
export type { ReadTransaction, WriteTransaction } from './transactions.js';
export type { ScanResult } from './scan-iterator.js';

@@ -9,1 +9,2 @@ "use strict";

Object.defineProperty(exports, "REPMHTTPInvoker", { enumerable: true, get: function () { return repm_invoker_js_1.REPMHTTPInvoker; } });
Object.defineProperty(exports, "REPMWASMInvoker", { enumerable: true, get: function () { return repm_invoker_js_1.REPMWASMInvoker; } });
import type { JSONValue } from './json.js';
import type { ScanOptions } from './scan-options.js';
import type { DatabaseInfo } from './database-info.js';
import type { REPMInvoke } from './repm-invoker.js';
import { Invoker, REPMInvoke } from './repm-invoker.js';
import { ScanResult } from './scan-iterator.js';

@@ -21,3 +21,3 @@ import type { ReadTransaction, WriteTransaction } from './transactions.js';

private readonly _name;
private readonly _repmInvoke;
private readonly _repmInvoker;
private _closed;

@@ -30,3 +30,3 @@ private _online;

private readonly _subscriptions;
protected _syncInterval: number | null;
private _syncInterval;
protected _timerId: ReturnType<typeof setTimeout> | 0;

@@ -40,3 +40,3 @@ onSync: ((syncing: boolean) => void) | null;

getDataLayerAuth: (() => MaybePromise<string | null | undefined>) | null | undefined;
constructor({ batchURL, dataLayerAuth, diffServerAuth, diffServerURL, name, repmInvoke, }: {
constructor({ batchURL, dataLayerAuth, diffServerAuth, diffServerURL, name, repmInvoker, syncInterval, }: {
batchURL?: string;

@@ -47,3 +47,4 @@ dataLayerAuth?: string;

name?: string;
repmInvoke: REPMInvoke;
repmInvoker: Invoker;
syncInterval?: number | null;
});

@@ -63,2 +64,3 @@ /**

}): Promise<void>;
get isWASM(): boolean;
get online(): boolean;

@@ -73,2 +75,3 @@ get closed(): boolean;

private _scheduleSync;
private _clearTimer;
close(): Promise<void>;

@@ -112,3 +115,3 @@ private _getRoot;

* If an error occurs in the `body` the `onError` function is called if
* present.
* present. Otherwise, the error is thrown.
*/

@@ -161,3 +164,3 @@ subscribe<R, E>(body: (tx: ReadTransaction) => Promise<R>, { onData, onError, onDone, }: {

export declare class ReplicacheTest extends Replicache {
static new({ batchURL, dataLayerAuth, diffServerAuth, diffServerURL, name, repmInvoke, }: {
static new({ batchURL, dataLayerAuth, diffServerAuth, diffServerURL, name, repmInvoker, }: {
diffServerURL: string;

@@ -168,6 +171,4 @@ batchURL?: string;

name?: string;
repmInvoke: REPMInvoke;
repmInvoker: Invoker;
}): Promise<ReplicacheTest>;
/** @override */
protected _syncInterval: number | null;
beginSync(): Promise<BeginSyncResult>;

@@ -174,0 +175,0 @@ maybeEndSync(beginSyncResult: BeginSyncResult): Promise<void>;

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReplicacheTest = exports.httpStatusUnauthorized = void 0;
const repm_invoker_js_1 = require("./repm-invoker.js");
const transactions_js_1 = require("./transactions.js");

@@ -8,3 +9,3 @@ const scan_iterator_js_1 = require("./scan-iterator.js");

class Replicache {
constructor({ batchURL = '', dataLayerAuth = '', diffServerAuth = '', diffServerURL, name = 'default', repmInvoke, }) {
constructor({ batchURL = '', dataLayerAuth = '', diffServerAuth = '', diffServerURL, name = 'default', repmInvoker = new repm_invoker_js_1.REPMWASMInvoker(), syncInterval = 60000, }) {
this._closed = false;

@@ -17,3 +18,2 @@ this._online = true;

this._subscriptions = new Set();
this._syncInterval = 60000;
// NodeJS has a non standard setTimeout function :'(

@@ -30,3 +30,3 @@ this._timerId = 0;

await this._opened;
return await this._repmInvoke(this._name, rpc, args);
return await this._repmInvoker.invoke(this._name, rpc, args);
};

@@ -38,3 +38,4 @@ this._batchURL = batchURL;

this._name = name;
this._repmInvoke = repmInvoke;
this._repmInvoker = repmInvoker;
this._syncInterval = syncInterval;
this._open();

@@ -50,3 +51,3 @@ }

async _open() {
this._opened = this._repmInvoke(this._name, 'open');
this._opened = this._repmInvoker.invoke(this._name, 'open');
this._root = this._getRoot();

@@ -64,2 +65,5 @@ await this._root;

}
get isWASM() {
return this._repmInvoker.isWASM || false;
}
get online() {

@@ -79,6 +83,3 @@ return this._online;

set syncInterval(duration) {
if (this._timerId !== 0) {
clearTimeout(this._timerId);
this._timerId = 0;
}
this._clearTimer();
this._syncInterval = duration;

@@ -92,2 +93,8 @@ this._scheduleSync();

}
_clearTimer() {
if (this._timerId !== 0) {
clearTimeout(this._timerId);
this._timerId = 0;
}
}
async close() {

@@ -97,3 +104,3 @@ var _a;

const p = this._invoke('close');
// Clear timer
this._clearTimer();
// Clear subscriptions

@@ -135,7 +142,7 @@ for (const subscription of this._subscriptions) {

let tx;
return new scan_iterator_js_1.ScanResult(prefix, start, this._invoke, async () => {
return new scan_iterator_js_1.ScanResult(this.isWASM, prefix, start, this._invoke, async () => {
if (tx) {
return tx;
}
tx = new transactions_js_1.ReadTransactionImpl(this._invoke);
tx = new transactions_js_1.ReadTransactionImpl(this.isWASM, this._invoke);
await tx.open({});

@@ -148,3 +155,6 @@ return tx;

const beginSyncResult = await this._beginSync();
if (beginSyncResult.syncHead !== '00000000000000000000000000000000') {
// TODO(repc-switchover)
// replicache-client sends all zeros for null sync,
// repc sends empty string.
if (beginSyncResult.syncHead.replace(/0/g, '') !== '') {
await this._maybeEndSync(beginSyncResult);

@@ -174,2 +184,3 @@ }

let reauth = false;
// TODO:(repc-switchover): checkStatus only used by replicache-client.
function checkStatus(data, serverName) {

@@ -184,3 +195,3 @@ const { httpStatusCode, errorMessage } = data;

}
const { batchPushInfo } = syncInfo;
const { batchPushInfo } = { ...syncInfo };
if (batchPushInfo) {

@@ -195,3 +206,6 @@ checkStatus(batchPushInfo, 'batch');

}
checkStatus(syncInfo.clientViewInfo, 'client view');
const { clientViewInfo } = { ...syncInfo };
if (clientViewInfo) {
checkStatus(syncInfo.clientViewInfo, 'client view');
}
if (reauth && this.getDataLayerAuth) {

@@ -258,6 +272,3 @@ const dataLayerAuth = await this.getDataLayerAuth();

}
if (this._timerId !== 0) {
clearTimeout(this._timerId);
this._timerId = 0;
}
this._clearTimer();
this._fireOnSync(true);

@@ -312,3 +323,3 @@ try {

* If an error occurs in the `body` the `onError` function is called if
* present.
* present. Otherwise, the error is thrown.
*/

@@ -319,3 +330,2 @@ subscribe(body, { onData, onError, onDone, }) {

(async () => {
var _a;
try {

@@ -326,3 +336,8 @@ const res = await this.query(s.body);

catch (ex) {
(_a = s.onError) === null || _a === void 0 ? void 0 : _a.call(s, ex);
if (s.onError) {
s.onError(ex);
}
else {
throw ex;
}
}

@@ -340,3 +355,3 @@ })();

async query(body) {
const tx = new transactions_js_1.ReadTransactionImpl(this._invoke);
const tx = new transactions_js_1.ReadTransactionImpl(this.isWASM, this._invoke);
await tx.open({});

@@ -393,3 +408,3 @@ try {

let result;
const tx = new transactions_js_1.WriteTransactionImpl(this._invoke);
const tx = new transactions_js_1.WriteTransactionImpl(this.isWASM, this._invoke);
await tx.open(actualInvokeArgs);

@@ -420,8 +435,3 @@ try {

class ReplicacheTest extends Replicache {
constructor() {
super(...arguments);
/** @override */
this._syncInterval = null;
}
static async new({ batchURL, dataLayerAuth, diffServerAuth, diffServerURL, name = '', repmInvoke, }) {
static async new({ batchURL, dataLayerAuth, diffServerAuth, diffServerURL, name = '', repmInvoker, }) {
const rep = new ReplicacheTest({

@@ -433,6 +443,6 @@ batchURL,

name,
repmInvoke,
repmInvoker,
syncInterval: null,
});
await rep._opened;
// await this._root;
return rep;

@@ -439,0 +449,0 @@ }

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {

@@ -13,3 +32,3 @@ return (mod && mod.__esModule) ? mod : { "default": mod };

const replicache_js_1 = require("./replicache.js");
const mod_js_1 = require("./mod.js");
const mod_js_1 = __importStar(require("./mod.js"));
const scan_iterator_js_1 = require("./scan-iterator.js");

@@ -131,3 +150,5 @@ let rep = null;

name,
repmInvoke: invoke,
repmInvoker: {
invoke,
},
});

@@ -592,1 +613,87 @@ }

});
test('syncInterval in constructor', async () => {
await useReplay('syncInterval in constructor');
const rep = new mod_js_1.default({
syncInterval: 12.34,
repmInvoker: { invoke },
diffServerURL: 'xxx',
});
expect(rep.syncInterval).toBe(12.34);
await rep.close();
});
test('closeTransaction after rep.scan', async () => {
await useReplay('closeTransaction after rep.scan');
invoke = jest.fn(invoke);
rep = await replicacheForTesting('test5');
const add = rep.register('add-data', addData);
await add({
'a/0': 0,
'a/1': 1,
});
const mockInvoke = invoke;
mockInvoke.mockClear();
function expectCalls(log) {
expect(log).toEqual(log);
const rpcs = mockInvoke.mock.calls.map(([, rpc]) => rpc);
expect(rpcs).toEqual(['openTransaction', 'scan', 'closeTransaction']);
}
const it = rep.scan();
const log = [];
for await (const v of it) {
log.push(v);
}
expectCalls([0, 1]);
// One more time with return in loop...
log.length = 0;
mockInvoke.mockClear();
await (async () => {
if (!rep) {
fail();
}
const it = rep.scan();
for await (const v of it) {
log.push(v);
return;
}
})();
expectCalls([0]);
// ... and with a break.
log.length = 0;
mockInvoke.mockClear();
{
const it = rep.scan();
for await (const v of it) {
log.push(v);
break;
}
}
expectCalls([0]);
// ... and with a throw.
log.length = 0;
mockInvoke.mockClear();
await expect((async () => {
if (!rep) {
fail();
}
const it = rep.scan();
for await (const v of it) {
log.push(v);
throw 'hi!';
}
})()).rejects.toBe('hi!');
expectCalls([0]);
// ... and with a throw.
log.length = 0;
mockInvoke.mockClear();
await expect((async () => {
if (!rep) {
fail();
}
const it = rep.scan();
for await (const v of it) {
log.push(v);
throw 'hi!';
}
})()).rejects.toBe('hi!');
expectCalls([0]);
});

@@ -5,2 +5,6 @@ import type { JSONValue, ToJSON } from './json.js';

import type { DatabaseInfo } from './database-info.js';
export declare type Invoker = {
readonly invoke: REPMInvoke;
readonly isWASM?: boolean;
};
export interface Invoke {

@@ -21,2 +25,9 @@ <Rpc extends keyof InvokeMapNoArgs>(rpc: Rpc): Promise<InvokeMapNoArgs[Rpc]>;

}
export declare class REPMWASMInvoker {
private readonly _inited;
private _dispatch?;
readonly isWASM = true;
constructor(wasm_module?: any);
invoke: REPMInvoke;
}
declare type GetRequest = TransactionRequest & {

@@ -39,5 +50,7 @@ key: string;

export declare type ScanRequest = TransactionRequest & ScanOptions & {
limit?: number;
opts?: ScanOptions;
};
export declare type ScanResponse = ScanItem[];
export declare type ScanResponse = ScanItem[] | {
items: ScanItem[];
};
declare type PutRequest = TransactionRequest & {

@@ -44,0 +57,0 @@ key: string;

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.REPMHTTPInvoker = void 0;
exports.REPMWASMInvoker = exports.REPMHTTPInvoker = void 0;
class REPMHTTPInvoker {

@@ -23,1 +42,23 @@ constructor(url) {

exports.REPMHTTPInvoker = REPMHTTPInvoker;
class REPMWASMInvoker {
constructor(wasm_module) {
this.isWASM = true;
this.invoke = async (dbName, rpc, args = {}) => {
console.debug(">", dbName, rpc, args);
await this._inited;
const json = await this._dispatch(dbName, rpc, JSON.stringify(args)); // eslint-disable-line @typescript-eslint/no-non-null-assertion
const ret = json == '' ? null : JSON.parse(json);
console.debug("<", dbName, rpc, ret);
return ret;
};
this._inited = (async () => {
// TODO: Have to import dynamically to hide this from Jest.
// Jest cannot parse the es6 behind this import, I don't know why.
// TODO: We need to have some way to switch between debug and release.
const { default: init, dispatch } = await Promise.resolve().then(() => __importStar(require('./wasm/debug/replicache_client.js')));
this._dispatch = dispatch;
return init(wasm_module);
})();
}
}
exports.REPMWASMInvoker = REPMWASMInvoker;

@@ -17,2 +17,3 @@ import type { ScanBound } from './scan-bound.js';

export declare class ScanIterator<V> implements AsyncIterableIterator<V> {
private readonly _isWASM;
private readonly _scanItems;

@@ -29,3 +30,3 @@ private _current;

private readonly _invoke;
constructor(kind: ScanIterableKind, prefix: string, start: ScanBound | undefined, invoke: Invoke, getTransaction: () => Promise<IdCloser> | IdCloser, shouldCloseTranscation: boolean);
constructor(isWASM: boolean, kind: ScanIterableKind, prefix: string, start: ScanBound | undefined, invoke: Invoke, getTransaction: () => Promise<IdCloser> | IdCloser, shouldCloseTranscation: boolean);
[Symbol.asyncIterator](): AsyncIterableIterator<V>;

@@ -38,4 +39,5 @@ private _ensureTransaction;

export declare class ScanResult implements AsyncIterable<JSONValue> {
private readonly _isWASM;
private readonly _args;
constructor(...args: [string, ScanBound | undefined, Invoke, () => Promise<IdCloser> | IdCloser, boolean]);
constructor(isWASM: boolean, ...args: [string, ScanBound | undefined, Invoke, () => Promise<IdCloser> | IdCloser, boolean]);
[Symbol.asyncIterator](): AsyncIterableIterator<JSONValue>;

@@ -42,0 +44,0 @@ values(): AsyncIterableIterator<JSONValue>;

@@ -5,3 +5,4 @@ "use strict";

const transactions_js_1 = require("./transactions.js");
exports.scanPageSize = 100;
const defaultScanSize = 500;
exports.scanPageSize = defaultScanSize;
function setScanPageSizeForTesting(n) {

@@ -12,3 +13,3 @@ exports.scanPageSize = n;

function restoreScanPageSizeForTesting() {
exports.scanPageSize = 100;
exports.scanPageSize = defaultScanSize;
}

@@ -20,3 +21,3 @@ exports.restoreScanPageSizeForTesting = restoreScanPageSizeForTesting;

class ScanIterator {
constructor(kind, prefix, start, invoke, getTransaction, shouldCloseTranscation) {
constructor(isWASM, kind, prefix, start, invoke, getTransaction, shouldCloseTranscation) {
this._scanItems = [];

@@ -27,2 +28,3 @@ this._current = 0;

this._transaction = undefined;
this._isWASM = isWASM;
this._kind = kind;

@@ -55,3 +57,3 @@ this._prefix = prefix;

if (!this._moreItemsToLoad) {
return { done: true, value: undefined };
return this.return();
}

@@ -102,11 +104,23 @@ this._loadPromise = this._load();

}
const scanItems = await this._invoke('scan', {
transactionId: this._transaction.id,
const opts = {
prefix: this._prefix,
start,
limit: exports.scanPageSize,
});
};
const args = {
transactionId: this._transaction.id,
...(this._isWASM ? { opts } : opts),
};
const response = await this._invoke('scan', args);
// TODO(repc-switchover): only the !array path is needed for repc.
const scanItems = Array.isArray(response) ? response : response.items;
if (scanItems.length !== exports.scanPageSize) {
this._moreItemsToLoad = false;
}
if (this._isWASM) {
for (const item of scanItems) {
// Temporarily circument the readonly-ness of item.value to parse.
item.value = JSON.parse(item.value); // eslint-disable-line @typescript-eslint/no-explicit-any
}
}
this._scanItems.push(...scanItems);

@@ -117,3 +131,4 @@ }

class ScanResult {
constructor(...args) {
constructor(isWASM, ...args) {
this._isWASM = isWASM;
this._args = args;

@@ -125,14 +140,14 @@ }

values() {
return this._newIterator('value');
return this._newIterator(this._isWASM, 'value');
}
keys() {
return this._newIterator('key');
return this._newIterator(this._isWASM, 'key');
}
entries() {
return this._newIterator('entry');
return this._newIterator(this._isWASM, 'entry');
}
_newIterator(kind) {
return new ScanIterator(kind, ...this._args);
_newIterator(isWASM, kind) {
return new ScanIterator(isWASM, kind, ...this._args);
}
}
exports.ScanResult = ScanResult;

@@ -5,2 +5,3 @@ import type { ScanBound } from './scan-bound.js';

start?: ScanBound;
limit?: number;
};

@@ -34,5 +34,6 @@ import type { JSONValue, ToJSON } from './json.js';

private _transactionId;
protected readonly _isWASM: boolean;
protected readonly _invoke: Invoke;
protected _closed: boolean;
constructor(invoke: Invoke);
constructor(isWASM: boolean, invoke: Invoke);
get(key: string): Promise<JSONValue | undefined>;

@@ -39,0 +40,0 @@ has(key: string): Promise<boolean>;

@@ -13,5 +13,6 @@ "use strict";

class ReadTransactionImpl {
constructor(invoke) {
constructor(isWASM, invoke) {
this._transactionId = -1;
this._closed = false;
this._isWASM = isWASM;
this._invoke = invoke;

@@ -28,3 +29,3 @@ }

}
return result.value;
return this._isWASM ? JSON.parse(result.value) : result.value;
}

@@ -40,3 +41,3 @@ async has(key) {

scan({ prefix = '', start } = {}) {
return new scan_iterator_js_1.ScanResult(prefix, start, this._invoke, () => this, false);
return new scan_iterator_js_1.ScanResult(this._isWASM, prefix, start, this._invoke, () => this, false);
}

@@ -72,3 +73,3 @@ get id() {

key,
value,
value: this._isWASM ? JSON.stringify(value) : value,
});

@@ -75,0 +76,0 @@ }

export { default } from './replicache.js';
export { TransactionClosedError } from './transaction-closed-error.js';
export { REPMHTTPInvoker } from './repm-invoker.js';
export { REPMHTTPInvoker, REPMWASMInvoker } from './repm-invoker.js';
export type { Mutator } from './replicache.js';

@@ -8,4 +8,4 @@ export type { ScanId } from './scan-id.js';

export type { DatabaseInfo } from './database-info.js';
export type { REPMInvoke } from './repm-invoker.js';
export type { REPMInvoke, Invoker } from './repm-invoker.js';
export type { ReadTransaction, WriteTransaction } from './transactions.js';
export type { ScanResult } from './scan-iterator.js';
export { default } from './replicache.js';
export { TransactionClosedError } from './transaction-closed-error.js';
export { REPMHTTPInvoker } from './repm-invoker.js';
export { REPMHTTPInvoker, REPMWASMInvoker } from './repm-invoker.js';
import type { JSONValue } from './json.js';
import type { ScanOptions } from './scan-options.js';
import type { DatabaseInfo } from './database-info.js';
import type { REPMInvoke } from './repm-invoker.js';
import { Invoker, REPMInvoke } from './repm-invoker.js';
import { ScanResult } from './scan-iterator.js';

@@ -21,3 +21,3 @@ import type { ReadTransaction, WriteTransaction } from './transactions.js';

private readonly _name;
private readonly _repmInvoke;
private readonly _repmInvoker;
private _closed;

@@ -30,3 +30,3 @@ private _online;

private readonly _subscriptions;
protected _syncInterval: number | null;
private _syncInterval;
protected _timerId: ReturnType<typeof setTimeout> | 0;

@@ -40,3 +40,3 @@ onSync: ((syncing: boolean) => void) | null;

getDataLayerAuth: (() => MaybePromise<string | null | undefined>) | null | undefined;
constructor({ batchURL, dataLayerAuth, diffServerAuth, diffServerURL, name, repmInvoke, }: {
constructor({ batchURL, dataLayerAuth, diffServerAuth, diffServerURL, name, repmInvoker, syncInterval, }: {
batchURL?: string;

@@ -47,3 +47,4 @@ dataLayerAuth?: string;

name?: string;
repmInvoke: REPMInvoke;
repmInvoker: Invoker;
syncInterval?: number | null;
});

@@ -63,2 +64,3 @@ /**

}): Promise<void>;
get isWASM(): boolean;
get online(): boolean;

@@ -73,2 +75,3 @@ get closed(): boolean;

private _scheduleSync;
private _clearTimer;
close(): Promise<void>;

@@ -112,3 +115,3 @@ private _getRoot;

* If an error occurs in the `body` the `onError` function is called if
* present.
* present. Otherwise, the error is thrown.
*/

@@ -161,3 +164,3 @@ subscribe<R, E>(body: (tx: ReadTransaction) => Promise<R>, { onData, onError, onDone, }: {

export declare class ReplicacheTest extends Replicache {
static new({ batchURL, dataLayerAuth, diffServerAuth, diffServerURL, name, repmInvoke, }: {
static new({ batchURL, dataLayerAuth, diffServerAuth, diffServerURL, name, repmInvoker, }: {
diffServerURL: string;

@@ -168,6 +171,4 @@ batchURL?: string;

name?: string;
repmInvoke: REPMInvoke;
repmInvoker: Invoker;
}): Promise<ReplicacheTest>;
/** @override */
protected _syncInterval: number | null;
beginSync(): Promise<BeginSyncResult>;

@@ -174,0 +175,0 @@ maybeEndSync(beginSyncResult: BeginSyncResult): Promise<void>;

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

import { REPMWASMInvoker, } from './repm-invoker.js';
import { ReadTransactionImpl, WriteTransactionImpl } from './transactions.js';

@@ -5,3 +6,3 @@ import { ScanResult } from './scan-iterator.js';

export default class Replicache {
constructor({ batchURL = '', dataLayerAuth = '', diffServerAuth = '', diffServerURL, name = 'default', repmInvoke, }) {
constructor({ batchURL = '', dataLayerAuth = '', diffServerAuth = '', diffServerURL, name = 'default', repmInvoker = new REPMWASMInvoker(), syncInterval = 60000, }) {
this._closed = false;

@@ -14,3 +15,2 @@ this._online = true;

this._subscriptions = new Set();
this._syncInterval = 60000;
// NodeJS has a non standard setTimeout function :'(

@@ -27,3 +27,3 @@ this._timerId = 0;

await this._opened;
return await this._repmInvoke(this._name, rpc, args);
return await this._repmInvoker.invoke(this._name, rpc, args);
};

@@ -35,3 +35,4 @@ this._batchURL = batchURL;

this._name = name;
this._repmInvoke = repmInvoke;
this._repmInvoker = repmInvoker;
this._syncInterval = syncInterval;
this._open();

@@ -47,3 +48,3 @@ }

async _open() {
this._opened = this._repmInvoke(this._name, 'open');
this._opened = this._repmInvoker.invoke(this._name, 'open');
this._root = this._getRoot();

@@ -61,2 +62,5 @@ await this._root;

}
get isWASM() {
return this._repmInvoker.isWASM || false;
}
get online() {

@@ -76,6 +80,3 @@ return this._online;

set syncInterval(duration) {
if (this._timerId !== 0) {
clearTimeout(this._timerId);
this._timerId = 0;
}
this._clearTimer();
this._syncInterval = duration;

@@ -89,2 +90,8 @@ this._scheduleSync();

}
_clearTimer() {
if (this._timerId !== 0) {
clearTimeout(this._timerId);
this._timerId = 0;
}
}
async close() {

@@ -94,3 +101,3 @@ var _a;

const p = this._invoke('close');
// Clear timer
this._clearTimer();
// Clear subscriptions

@@ -132,7 +139,7 @@ for (const subscription of this._subscriptions) {

let tx;
return new ScanResult(prefix, start, this._invoke, async () => {
return new ScanResult(this.isWASM, prefix, start, this._invoke, async () => {
if (tx) {
return tx;
}
tx = new ReadTransactionImpl(this._invoke);
tx = new ReadTransactionImpl(this.isWASM, this._invoke);
await tx.open({});

@@ -145,3 +152,6 @@ return tx;

const beginSyncResult = await this._beginSync();
if (beginSyncResult.syncHead !== '00000000000000000000000000000000') {
// TODO(repc-switchover)
// replicache-client sends all zeros for null sync,
// repc sends empty string.
if (beginSyncResult.syncHead.replace(/0/g, '') !== '') {
await this._maybeEndSync(beginSyncResult);

@@ -171,2 +181,3 @@ }

let reauth = false;
// TODO:(repc-switchover): checkStatus only used by replicache-client.
function checkStatus(data, serverName) {

@@ -181,3 +192,3 @@ const { httpStatusCode, errorMessage } = data;

}
const { batchPushInfo } = syncInfo;
const { batchPushInfo } = { ...syncInfo };
if (batchPushInfo) {

@@ -192,3 +203,6 @@ checkStatus(batchPushInfo, 'batch');

}
checkStatus(syncInfo.clientViewInfo, 'client view');
const { clientViewInfo } = { ...syncInfo };
if (clientViewInfo) {
checkStatus(syncInfo.clientViewInfo, 'client view');
}
if (reauth && this.getDataLayerAuth) {

@@ -255,6 +269,3 @@ const dataLayerAuth = await this.getDataLayerAuth();

}
if (this._timerId !== 0) {
clearTimeout(this._timerId);
this._timerId = 0;
}
this._clearTimer();
this._fireOnSync(true);

@@ -309,3 +320,3 @@ try {

* If an error occurs in the `body` the `onError` function is called if
* present.
* present. Otherwise, the error is thrown.
*/

@@ -316,3 +327,2 @@ subscribe(body, { onData, onError, onDone, }) {

(async () => {
var _a;
try {

@@ -323,3 +333,8 @@ const res = await this.query(s.body);

catch (ex) {
(_a = s.onError) === null || _a === void 0 ? void 0 : _a.call(s, ex);
if (s.onError) {
s.onError(ex);
}
else {
throw ex;
}
}

@@ -337,3 +352,3 @@ })();

async query(body) {
const tx = new ReadTransactionImpl(this._invoke);
const tx = new ReadTransactionImpl(this.isWASM, this._invoke);
await tx.open({});

@@ -390,3 +405,3 @@ try {

let result;
const tx = new WriteTransactionImpl(this._invoke);
const tx = new WriteTransactionImpl(this.isWASM, this._invoke);
await tx.open(actualInvokeArgs);

@@ -416,8 +431,3 @@ try {

export class ReplicacheTest extends Replicache {
constructor() {
super(...arguments);
/** @override */
this._syncInterval = null;
}
static async new({ batchURL, dataLayerAuth, diffServerAuth, diffServerURL, name = '', repmInvoke, }) {
static async new({ batchURL, dataLayerAuth, diffServerAuth, diffServerURL, name = '', repmInvoker, }) {
const rep = new ReplicacheTest({

@@ -429,6 +439,6 @@ batchURL,

name,
repmInvoke,
repmInvoker,
syncInterval: null,
});
await rep._opened;
// await this._root;
return rep;

@@ -435,0 +445,0 @@ }

@@ -8,3 +8,3 @@ var _a;

import { ReplicacheTest, httpStatusUnauthorized } from './replicache.js';
import { REPMHTTPInvoker, TransactionClosedError } from './mod.js';
import Replicache, { REPMHTTPInvoker, TransactionClosedError, } from './mod.js';
import { restoreScanPageSizeForTesting, setScanPageSizeForTesting, } from './scan-iterator.js';

@@ -126,3 +126,5 @@ let rep = null;

name,
repmInvoke: invoke,
repmInvoker: {
invoke,
},
});

@@ -587,1 +589,87 @@ }

});
test('syncInterval in constructor', async () => {
await useReplay('syncInterval in constructor');
const rep = new Replicache({
syncInterval: 12.34,
repmInvoker: { invoke },
diffServerURL: 'xxx',
});
expect(rep.syncInterval).toBe(12.34);
await rep.close();
});
test('closeTransaction after rep.scan', async () => {
await useReplay('closeTransaction after rep.scan');
invoke = jest.fn(invoke);
rep = await replicacheForTesting('test5');
const add = rep.register('add-data', addData);
await add({
'a/0': 0,
'a/1': 1,
});
const mockInvoke = invoke;
mockInvoke.mockClear();
function expectCalls(log) {
expect(log).toEqual(log);
const rpcs = mockInvoke.mock.calls.map(([, rpc]) => rpc);
expect(rpcs).toEqual(['openTransaction', 'scan', 'closeTransaction']);
}
const it = rep.scan();
const log = [];
for await (const v of it) {
log.push(v);
}
expectCalls([0, 1]);
// One more time with return in loop...
log.length = 0;
mockInvoke.mockClear();
await (async () => {
if (!rep) {
fail();
}
const it = rep.scan();
for await (const v of it) {
log.push(v);
return;
}
})();
expectCalls([0]);
// ... and with a break.
log.length = 0;
mockInvoke.mockClear();
{
const it = rep.scan();
for await (const v of it) {
log.push(v);
break;
}
}
expectCalls([0]);
// ... and with a throw.
log.length = 0;
mockInvoke.mockClear();
await expect((async () => {
if (!rep) {
fail();
}
const it = rep.scan();
for await (const v of it) {
log.push(v);
throw 'hi!';
}
})()).rejects.toBe('hi!');
expectCalls([0]);
// ... and with a throw.
log.length = 0;
mockInvoke.mockClear();
await expect((async () => {
if (!rep) {
fail();
}
const it = rep.scan();
for await (const v of it) {
log.push(v);
throw 'hi!';
}
})()).rejects.toBe('hi!');
expectCalls([0]);
});

@@ -5,2 +5,6 @@ import type { JSONValue, ToJSON } from './json.js';

import type { DatabaseInfo } from './database-info.js';
export declare type Invoker = {
readonly invoke: REPMInvoke;
readonly isWASM?: boolean;
};
export interface Invoke {

@@ -21,2 +25,9 @@ <Rpc extends keyof InvokeMapNoArgs>(rpc: Rpc): Promise<InvokeMapNoArgs[Rpc]>;

}
export declare class REPMWASMInvoker {
private readonly _inited;
private _dispatch?;
readonly isWASM = true;
constructor(wasm_module?: any);
invoke: REPMInvoke;
}
declare type GetRequest = TransactionRequest & {

@@ -39,5 +50,7 @@ key: string;

export declare type ScanRequest = TransactionRequest & ScanOptions & {
limit?: number;
opts?: ScanOptions;
};
export declare type ScanResponse = ScanItem[];
export declare type ScanResponse = ScanItem[] | {
items: ScanItem[];
};
declare type PutRequest = TransactionRequest & {

@@ -44,0 +57,0 @@ key: string;

@@ -19,1 +19,22 @@ export class REPMHTTPInvoker {

}
export class REPMWASMInvoker {
constructor(wasm_module) {
this.isWASM = true;
this.invoke = async (dbName, rpc, args = {}) => {
console.debug(">", dbName, rpc, args);
await this._inited;
const json = await this._dispatch(dbName, rpc, JSON.stringify(args)); // eslint-disable-line @typescript-eslint/no-non-null-assertion
const ret = json == '' ? null : JSON.parse(json);
console.debug("<", dbName, rpc, ret);
return ret;
};
this._inited = (async () => {
// TODO: Have to import dynamically to hide this from Jest.
// Jest cannot parse the es6 behind this import, I don't know why.
// TODO: We need to have some way to switch between debug and release.
const { default: init, dispatch } = await import('./wasm/debug/replicache_client.js');
this._dispatch = dispatch;
return init(wasm_module);
})();
}
}

@@ -17,2 +17,3 @@ import type { ScanBound } from './scan-bound.js';

export declare class ScanIterator<V> implements AsyncIterableIterator<V> {
private readonly _isWASM;
private readonly _scanItems;

@@ -29,3 +30,3 @@ private _current;

private readonly _invoke;
constructor(kind: ScanIterableKind, prefix: string, start: ScanBound | undefined, invoke: Invoke, getTransaction: () => Promise<IdCloser> | IdCloser, shouldCloseTranscation: boolean);
constructor(isWASM: boolean, kind: ScanIterableKind, prefix: string, start: ScanBound | undefined, invoke: Invoke, getTransaction: () => Promise<IdCloser> | IdCloser, shouldCloseTranscation: boolean);
[Symbol.asyncIterator](): AsyncIterableIterator<V>;

@@ -38,4 +39,5 @@ private _ensureTransaction;

export declare class ScanResult implements AsyncIterable<JSONValue> {
private readonly _isWASM;
private readonly _args;
constructor(...args: [string, ScanBound | undefined, Invoke, () => Promise<IdCloser> | IdCloser, boolean]);
constructor(isWASM: boolean, ...args: [string, ScanBound | undefined, Invoke, () => Promise<IdCloser> | IdCloser, boolean]);
[Symbol.asyncIterator](): AsyncIterableIterator<JSONValue>;

@@ -42,0 +44,0 @@ values(): AsyncIterableIterator<JSONValue>;

import { throwIfClosed } from './transactions.js';
export let scanPageSize = 100;
const defaultScanSize = 500;
export let scanPageSize = defaultScanSize;
export function setScanPageSizeForTesting(n) {

@@ -7,3 +8,3 @@ scanPageSize = n;

export function restoreScanPageSizeForTesting() {
scanPageSize = 100;
scanPageSize = defaultScanSize;
}

@@ -14,3 +15,3 @@ /**

export class ScanIterator {
constructor(kind, prefix, start, invoke, getTransaction, shouldCloseTranscation) {
constructor(isWASM, kind, prefix, start, invoke, getTransaction, shouldCloseTranscation) {
this._scanItems = [];

@@ -21,2 +22,3 @@ this._current = 0;

this._transaction = undefined;
this._isWASM = isWASM;
this._kind = kind;

@@ -49,3 +51,3 @@ this._prefix = prefix;

if (!this._moreItemsToLoad) {
return { done: true, value: undefined };
return this.return();
}

@@ -96,11 +98,23 @@ this._loadPromise = this._load();

}
const scanItems = await this._invoke('scan', {
transactionId: this._transaction.id,
const opts = {
prefix: this._prefix,
start,
limit: scanPageSize,
});
};
const args = {
transactionId: this._transaction.id,
...(this._isWASM ? { opts } : opts),
};
const response = await this._invoke('scan', args);
// TODO(repc-switchover): only the !array path is needed for repc.
const scanItems = Array.isArray(response) ? response : response.items;
if (scanItems.length !== scanPageSize) {
this._moreItemsToLoad = false;
}
if (this._isWASM) {
for (const item of scanItems) {
// Temporarily circument the readonly-ness of item.value to parse.
item.value = JSON.parse(item.value); // eslint-disable-line @typescript-eslint/no-explicit-any
}
}
this._scanItems.push(...scanItems);

@@ -110,3 +124,4 @@ }

export class ScanResult {
constructor(...args) {
constructor(isWASM, ...args) {
this._isWASM = isWASM;
this._args = args;

@@ -118,13 +133,13 @@ }

values() {
return this._newIterator('value');
return this._newIterator(this._isWASM, 'value');
}
keys() {
return this._newIterator('key');
return this._newIterator(this._isWASM, 'key');
}
entries() {
return this._newIterator('entry');
return this._newIterator(this._isWASM, 'entry');
}
_newIterator(kind) {
return new ScanIterator(kind, ...this._args);
_newIterator(isWASM, kind) {
return new ScanIterator(isWASM, kind, ...this._args);
}
}

@@ -5,2 +5,3 @@ import type { ScanBound } from './scan-bound.js';

start?: ScanBound;
limit?: number;
};

@@ -34,5 +34,6 @@ import type { JSONValue, ToJSON } from './json.js';

private _transactionId;
protected readonly _isWASM: boolean;
protected readonly _invoke: Invoke;
protected _closed: boolean;
constructor(invoke: Invoke);
constructor(isWASM: boolean, invoke: Invoke);
get(key: string): Promise<JSONValue | undefined>;

@@ -39,0 +40,0 @@ has(key: string): Promise<boolean>;

@@ -9,5 +9,6 @@ import { ScanResult } from './scan-iterator.js';

export class ReadTransactionImpl {
constructor(invoke) {
constructor(isWASM, invoke) {
this._transactionId = -1;
this._closed = false;
this._isWASM = isWASM;
this._invoke = invoke;

@@ -24,3 +25,3 @@ }

}
return result.value;
return this._isWASM ? JSON.parse(result.value) : result.value;
}

@@ -36,3 +37,3 @@ async has(key) {

scan({ prefix = '', start } = {}) {
return new ScanResult(prefix, start, this._invoke, () => this, false);
return new ScanResult(this._isWASM, prefix, start, this._invoke, () => this, false);
}

@@ -67,3 +68,3 @@ get id() {

key,
value,
value: this._isWASM ? JSON.stringify(value) : value,
});

@@ -70,0 +71,0 @@ }

{
"name": "replicache",
"description": "Offline-First for Every Application",
"version": "0.0.6",
"version": "0.0.7",
"repository": "github:rocicorp/replicache-sdk-js",

@@ -12,8 +12,8 @@ "scripts": {

"doc": "typedoc --name Replicache --mode library --exclude node_modules --exclude src/*.test.ts --excludeNotExported --excludePrivate --excludeProtected --out docs --excludeExternals src/mod.ts",
"build": "tsc",
"build": "tsc && rm -rf out/wasm && mkdir out/wasm && cp -R src/wasm/* out/wasm",
"build:watch": "tsc --watch",
"build:cjs": "tsc --outDir out.cjs --module CommonJS",
"build:cjs": "tsc --outDir out.cjs --module CommonJS && rm -rf out/wasm && mkdir out/wasm && cp -R src/wasm/* out/wasm",
"start:test-server": "bin/test-server --no-fake-time",
"start:diff-server": "bin/diff-server --db=/tmp/diffs serve",
"postinstall": "tool/build.sh",
"postinstall": "tool/get-deps.sh",
"prepublishOnly": "tool/validate-binaries-for-publish.js && npm run lint && npm run test && rm -rf out && npm run build && rm -rf out.cjs && npm run build:cjs"

@@ -40,2 +40,3 @@ },

"main": "out.cjs/mod.js",
"types": "out/mod.js",
"dependencies": {},

@@ -45,4 +46,4 @@ "files": [

"out.cjs",
"tool/build.sh"
"tool/get-deps.sh"
]
}
}

@@ -7,6 +7,10 @@ # Replicache JS SDK

## Installation
## 👋 Quickstart
You can install Replicache JS SDK from npm.
This tutorial walks through creating a basic offline-first todo app with [Replicache](https://replicache.dev/). If you have any problems or questions, please [join us on Slack](https://join.slack.com/t/rocicorp/shared_invite/zt-h8ygwu8j-RVniv5XsBps0Q9oJXdMyoA). We'd be happy to help.
**Note:** This document assumes you already know what Replicache is, why you might need it, and broadly how it works. If that's not true check out the [design document](https://github.com/rocicorp/replicache/blob/master/design.md) for a detailed deep-dive.
## 🏃‍♂️ Install
```

@@ -16,73 +20,79 @@ npm install replicache

## Get Binaries
## 🚴🏿‍♀️ Instantiate
The binaries are downloaded when you do `npm install`. If for some reason you
need to redownload these you can manually run `tool/build.sh`. Do this again
whenever you update the SDK.
Replicache ships with both ES6 and CommonJS modules. For simplicity, these examples use ES6.
## Run `test-server`
```html
<script type='module'>
import Replicache from './node_modules/replicache/out/mod.js';
Currently, the JavaScript SDK relies on a native local server that implements
the guts of the sync protocol on the client side. This is temporary and will be
removed.
var rep = new Replicache({
// URL of the diff server to use. The diff server periodically fetches
// the "client view" from your service and forwards any delta to the
// client. You can use our hosted diff server (as here) or a local diff
// server, which is useful during development. See
// https://github.com/rocicorp/replicache#server-side for more
// information on setting up your client view.
diffServerURL: 'https://serve.replicache.dev/pull',
For now, you must have this server running whenever you are working with the
SDK:
// Auth token for the diff server, if any.
diffServerAuth: '1',
// URL of your service's Replicache batch endpoint. Replicache
// will send batches of mutations here for application.
batchURL: 'https://replicache-sample-todo.now.sh/serve/replicache-batch',
// Auth token for your client view and batch endpoints, if any.
dataLayerAuth: '2',
});
</script>
```
mkdir ~/.repm
npx test-server --storage-dir=$HOME/.repm
```
## Start your Data Layer
## 🚗 Render UI
See [Replicache Server
Setup](https://github.com/rocicorp/replicache#server-side) for server-side
instructions.
Use `subscribe()` to open standing queries. Replicache fires `onData` whenever the result of the query changes, either because of local changes or sync.
For the rest of these instructions we will assume your data layer is running on
`localhost:3000`.
```js
rep.subscribe(async tx => {
return await toArray(tx.scan({ prefix: '/todo/' }));
}, {
onData: result => {
// Using lit-html, but the principle is the same in any UI framework.
// See https://github.com/rocicorp/replicache-sdk-js/tree/master/sample/cal
// for an example using React.
const toggle = complete => html`<td><input type=checkbox .checked=${complete}></td>`;
const title = text => html`<td>${text}</td>`;
const row = todo => html`<tr>${toggle(todo.complete)}${title(todo.text)}</tr>`;
render(html`<table>${result.map(row)}</table>`, document.body);
},
});
```
## Start Diff-Server
## 🏎 Mutate Data
In production, your app will talk to the production Replicache diff-server at
https://serve.replicache.dev/.
Register client-side *mutators* using `register()`.
During development, that server can't reach your workstation, so we provide a
development instance to work against instead. Leave this running in a tab:
Mutators run completely locally, without waiting on the server — online, offline, whatever! A record of the mutation is queued and sent to your service's batch endpoint when possible.
```bash
# The --client-view flag should point to the Client View endpoint
# on your development data layer.
npx diff-server --client-view="http://localhost:3000/replicache-client-view"
```
Replicache also invokes mutators itself, during sync, to replay unacknowledged changes on top of newly received server state.
## Including the JS
```js
const updateTodo = rep.register('updateTodo', async (tx, { id, complete }) => {
const key = `/todo/${id}`;
const todo = await tx.get(key);
todo.complete = complete;
await tx.put(key, todo);
});
It is recommended to use ES modules (but we also include CommonJS for backwards
compat).
```js
import Replicache, {REPMHTTPInvoker} from 'replicache';
const handleCheckbox = async (id, e) => {
await updateTodo({ id, complete: e.srcElement.checked });
}
```
To use `Replicache` you currently have to tell it how to invoke the
**Rep**licache Client API **M**odule (REPM). This implementation detail will be hidden/removed in the future.
## 🚀 Next Steps
```js
const diffServerURL = 'https://serve.replicache.dev/pull';
const diffServerAuth = '<your diff-server account ID>';
const batchURL = 'https://youservice.com/replicache-batch';
const dataLayerAuth = '<your data-layer auth token>';
const repmInvoker = new REPMHTTPInvoker('http://localhost:7002');
const repmInvoke = repmInvoker.invoke;
const replicache = new Replicache({
diffServerURL,
diffServerAuth,
batchURL,
dataLayerAuth,
repmInvoke,
});
const value = await replicache.query(tx => tx.get('/hello'));
console.log(value);
```
That's it! You've built a fully-functioning offline-first todo app against our sample backend. What will you do next?
* [Learn how to build your own backend integration](https://github.com/rocicorp/replicache#server-side)
* [Check out the richer React/Babel/GCal sample](https://github.com/rocicorp/replicache-sdk-js/tree/master/sample/cal)
* [Browse the full JS documentation](https://replicache-sdk-js.now.sh/)
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