@encorejs/saaz 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +203 -0
  2. package/README.md +1 -0
  3. package/dist/back/BackMemoryAdapter.d.ts +18 -0
  4. package/dist/back/BackMemoryAdapter.d.ts.map +1 -0
  5. package/dist/back/BackMemoryAdapter.js +69 -0
  6. package/dist/back/BackStorage.d.ts +20 -0
  7. package/dist/back/BackStorage.d.ts.map +1 -0
  8. package/dist/back/BackStorage.js +22 -0
  9. package/dist/back/SaazBack.d.ts +60 -0
  10. package/dist/back/SaazBack.d.ts.map +1 -0
  11. package/dist/back/SaazBack.js +188 -0
  12. package/dist/front/FrontIdbAdapter.d.ts +15 -0
  13. package/dist/front/FrontIdbAdapter.d.ts.map +1 -0
  14. package/dist/front/FrontIdbAdapter.js +159 -0
  15. package/dist/front/FrontMemoryAdapter.d.ts +16 -0
  16. package/dist/front/FrontMemoryAdapter.d.ts.map +1 -0
  17. package/dist/front/FrontMemoryAdapter.js +156 -0
  18. package/dist/front/FrontStorage.d.ts +23 -0
  19. package/dist/front/FrontStorage.d.ts.map +1 -0
  20. package/dist/front/FrontStorage.js +85 -0
  21. package/dist/front/SaazFront.d.ts +113 -0
  22. package/dist/front/SaazFront.d.ts.map +1 -0
  23. package/dist/front/SaazFront.js +756 -0
  24. package/dist/index.d.ts +8 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +9295 -0
  27. package/dist/index.js.map +7 -0
  28. package/dist/index.test.d.ts +2 -0
  29. package/dist/index.test.d.ts.map +1 -0
  30. package/dist/index.test.js +166 -0
  31. package/dist/rogue.d.ts +63 -0
  32. package/dist/rogue.d.ts.map +1 -0
  33. package/dist/rogue.js +548 -0
  34. package/dist/rogue.test.d.ts +2 -0
  35. package/dist/rogue.test.d.ts.map +1 -0
  36. package/dist/rogue.test.js +248 -0
  37. package/dist/shared/GeneratorSpy.d.ts +4 -0
  38. package/dist/shared/GeneratorSpy.d.ts.map +1 -0
  39. package/dist/shared/GeneratorSpy.js +41 -0
  40. package/dist/shared/transactions.d.ts +4 -0
  41. package/dist/shared/transactions.d.ts.map +1 -0
  42. package/dist/shared/transactions.js +131 -0
  43. package/dist/shared/utils.d.ts +5 -0
  44. package/dist/shared/utils.d.ts.map +1 -0
  45. package/dist/shared/utils.js +21 -0
  46. package/dist/tsdoc-metadata.json +11 -0
  47. package/dist/types.d.ts +240 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +1 -0
  50. package/package.json +54 -0
@@ -0,0 +1,159 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { openDB } from 'idb';
11
+ export class FrontIDBAdapter {
12
+ constructor(_idbName, _currentSessionName) {
13
+ this._idbName = _idbName;
14
+ this._currentSessionName = _currentSessionName;
15
+ this._dbPromise = this._initializeDB();
16
+ }
17
+ _initializeDB() {
18
+ return __awaiter(this, void 0, void 0, function* () {
19
+ return openDB(this._idbName, 1, {
20
+ upgrade(db) {
21
+ const singularValueStore = db.createObjectStore('singularValues', {
22
+ keyPath: 'sessionAndKey',
23
+ });
24
+ singularValueStore.createIndex('session', 'session');
25
+ const listsStore = db.createObjectStore('lists', {
26
+ keyPath: 'sessionAndKeyAndId',
27
+ });
28
+ listsStore.createIndex('session', 'session');
29
+ listsStore.createIndex('sessionAndKey', 'sessionAndKey');
30
+ },
31
+ });
32
+ });
33
+ }
34
+ get ready() {
35
+ return this._dbPromise;
36
+ }
37
+ transaction(fn) {
38
+ return __awaiter(this, void 0, void 0, function* () {
39
+ const db = yield this.ready;
40
+ const tx = db.transaction(['singularValues', 'lists'], 'readwrite');
41
+ const t = new IDBTransaction(tx);
42
+ return yield fn(t);
43
+ });
44
+ }
45
+ get(key, session) {
46
+ return __awaiter(this, void 0, void 0, function* () {
47
+ return yield this.transaction(({ get }) => get(key, session));
48
+ });
49
+ }
50
+ getList(key, session) {
51
+ return __awaiter(this, void 0, void 0, function* () {
52
+ return yield this.transaction(({ getList }) => getList(key, session));
53
+ });
54
+ }
55
+ }
56
+ class IDBTransaction {
57
+ constructor(_tx) {
58
+ this._tx = _tx;
59
+ }
60
+ get(key, session) {
61
+ return __awaiter(this, void 0, void 0, function* () {
62
+ const sessionAndKey = `${session}/${key}`;
63
+ const store = this._tx.objectStore('singularValues');
64
+ const v = (yield store.get(sessionAndKey));
65
+ return v === null || v === void 0 ? void 0 : v.value;
66
+ });
67
+ }
68
+ getAll(key) {
69
+ return __awaiter(this, void 0, void 0, function* () {
70
+ const index = this._tx.objectStore('singularValues').index('session');
71
+ const all = yield index.getAll();
72
+ const record = {};
73
+ for (const row of all) {
74
+ record[row.session] = row.value;
75
+ }
76
+ return record;
77
+ });
78
+ }
79
+ set(key, value, session) {
80
+ return __awaiter(this, void 0, void 0, function* () {
81
+ const sessionAndKey = `${session}/${key}`;
82
+ const store = this._tx.objectStore('singularValues');
83
+ const s = {
84
+ sessionAndKey: sessionAndKey,
85
+ value,
86
+ session,
87
+ key,
88
+ };
89
+ yield store.put(s);
90
+ });
91
+ }
92
+ deleteSession(session) {
93
+ return __awaiter(this, void 0, void 0, function* () {
94
+ {
95
+ const store = this._tx.objectStore('singularValues');
96
+ const index = store.index('session');
97
+ const sessionIndex = yield index.getAllKeys(session);
98
+ for (const key of sessionIndex) {
99
+ yield store.delete(key);
100
+ }
101
+ }
102
+ const listsStore = this._tx.objectStore('lists');
103
+ const listsIndex = listsStore.index('session');
104
+ const listsSessionIndex = yield listsIndex.getAllKeys(session);
105
+ for (const key of listsSessionIndex) {
106
+ yield listsStore.delete(key);
107
+ }
108
+ });
109
+ }
110
+ pushToList(key, rows, session) {
111
+ return __awaiter(this, void 0, void 0, function* () {
112
+ const store = this._tx.objectStore('lists');
113
+ for (const row of rows) {
114
+ const listItem = {
115
+ key,
116
+ session,
117
+ value: row,
118
+ sessionAndKeyAndId: `${session}/${key}/${row.id}`,
119
+ sessionAndKey: `${session}/${key}`,
120
+ };
121
+ const existingItem = yield store.get(listItem.sessionAndKeyAndId);
122
+ if (existingItem) {
123
+ throw new Error(`Cannot push to list "${key}" because an entry with id "${row.id}" already exists`);
124
+ }
125
+ // Save the row with key and id properties
126
+ yield store.put(listItem);
127
+ }
128
+ });
129
+ }
130
+ getList(key, session) {
131
+ return __awaiter(this, void 0, void 0, function* () {
132
+ const store = this._tx.objectStore('lists');
133
+ const keyIndex = store.index('sessionAndKey');
134
+ const sessionAndKey = `${session}/${key}`;
135
+ const all = yield keyIndex.getAll(sessionAndKey);
136
+ return all.map((s) => s.value);
137
+ });
138
+ }
139
+ pluckFromList(key, ids, session) {
140
+ return __awaiter(this, void 0, void 0, function* () {
141
+ const store = this._tx.objectStore('lists');
142
+ const results = [];
143
+ for (const id of ids) {
144
+ const sessionAndKeyAndId = `${session}/${key}/${id}`;
145
+ const item = yield store.get(sessionAndKeyAndId);
146
+ if (item) {
147
+ results.push(item.value);
148
+ // IDB `delete` takes a key, not the record object (passing the object
149
+ // throws a DataError at runtime).
150
+ yield store.delete(sessionAndKeyAndId);
151
+ }
152
+ else {
153
+ results.push(undefined);
154
+ }
155
+ }
156
+ return results;
157
+ });
158
+ }
159
+ }
@@ -0,0 +1,16 @@
1
+ import type { FrontStorageAdapterTransaction } from '../types';
2
+ import type { FrontStorageAdapter } from '../types';
3
+ export declare class FrontMemoryAdapter implements FrontStorageAdapter {
4
+ private _state;
5
+ private _readyDeferred;
6
+ private _lastTransactionPromise;
7
+ export(): unknown;
8
+ constructor(state?: unknown);
9
+ get ready(): Promise<void>;
10
+ transaction<T>(fn: (opts: FrontStorageAdapterTransaction) => Promise<T>): Promise<T>;
11
+ get<T>(key: string, session: string): Promise<T | void>;
12
+ getList<T extends {
13
+ id: string;
14
+ }>(key: string, session: string): Promise<T[]>;
15
+ }
16
+ //# sourceMappingURL=FrontMemoryAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FrontMemoryAdapter.d.ts","sourceRoot":"","sources":["../../src/front/FrontMemoryAdapter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAkB,8BAA8B,EAAC,MAAM,UAAU,CAAA;AAC7E,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,UAAU,CAAA;AAGjD,qBAAa,kBAAmB,YAAW,mBAAmB;IAC5D,OAAO,CAAC,MAAM,CAWI;IAElB,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,uBAAuB,CAAmC;IAElE,MAAM,IAAI,OAAO;gBAIL,KAAK,CAAC,EAAE,OAAO;IAQ3B,IAAI,KAAK,kBAER;IAEK,WAAW,CAAC,CAAC,EACjB,EAAE,EAAE,CAAC,IAAI,EAAE,8BAA8B,KAAK,OAAO,CAAC,CAAC,CAAC,GACvD,OAAO,CAAC,CAAC,CAAC;IA0BP,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAIvD,OAAO,CAAC,CAAC,SAAS;QAAC,EAAE,EAAE,MAAM,CAAA;KAAC,EAClC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,CAAC,EAAE,CAAC;CAGhB"}
@@ -0,0 +1,156 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { createDraft, current, finishDraft } from 'immer';
11
+ import { defer } from '@encorejs/utils/defer';
12
+ export class FrontMemoryAdapter {
13
+ export() {
14
+ return this._state;
15
+ }
16
+ constructor(state) {
17
+ this._state = { sessions: {} };
18
+ this._readyDeferred = defer();
19
+ this._lastTransactionPromise = Promise.resolve();
20
+ if (state) {
21
+ this._state = state;
22
+ }
23
+ // nothing to initialize, so just resolve the promise
24
+ this._readyDeferred.resolve();
25
+ }
26
+ get ready() {
27
+ return this._readyDeferred.promise;
28
+ }
29
+ transaction(fn) {
30
+ return __awaiter(this, void 0, void 0, function* () {
31
+ const deferred = defer();
32
+ const oldTransactionPromise = this._lastTransactionPromise;
33
+ this._lastTransactionPromise = deferred.promise;
34
+ try {
35
+ yield oldTransactionPromise;
36
+ }
37
+ catch (err) {
38
+ // ignore the error. it'll be handled elsewhere
39
+ }
40
+ yield this.ready;
41
+ const originalState = this._state;
42
+ const draft = createDraft(originalState);
43
+ const t = new Transaction(draft);
44
+ try {
45
+ const result = yield fn(t);
46
+ this._state = finishDraft(draft);
47
+ return result;
48
+ }
49
+ catch (error) {
50
+ throw error;
51
+ }
52
+ finally {
53
+ deferred.resolve();
54
+ }
55
+ });
56
+ }
57
+ get(key, session) {
58
+ return __awaiter(this, void 0, void 0, function* () {
59
+ return yield this.transaction(({ get }) => get(key, session));
60
+ });
61
+ }
62
+ getList(key, session) {
63
+ return __awaiter(this, void 0, void 0, function* () {
64
+ return yield this.transaction(({ getList }) => getList(key, session));
65
+ });
66
+ }
67
+ }
68
+ class Transaction {
69
+ constructor(_draft) {
70
+ this._draft = _draft;
71
+ }
72
+ get(key, session) {
73
+ var _a, _b;
74
+ return __awaiter(this, void 0, void 0, function* () {
75
+ return (_b = current((_a = this._draft.sessions[session]) === null || _a === void 0 ? void 0 : _a.keyval)) === null || _b === void 0 ? void 0 : _b[key];
76
+ });
77
+ }
78
+ set(key, value, session) {
79
+ var _a;
80
+ var _b;
81
+ return __awaiter(this, void 0, void 0, function* () {
82
+ (_a = (_b = this._draft.sessions)[session]) !== null && _a !== void 0 ? _a : (_b[session] = { keyval: {}, lists: {} });
83
+ const s = this._draft.sessions[session];
84
+ s.keyval[key] = value;
85
+ });
86
+ }
87
+ getAll(key) {
88
+ return __awaiter(this, void 0, void 0, function* () {
89
+ const vals = {};
90
+ for (const [sessionId, v] of Object.entries(this._draft.sessions)) {
91
+ if (!v)
92
+ continue;
93
+ if (Object.hasOwn(v.keyval, key)) {
94
+ const value = v.keyval[key];
95
+ if (value === undefined)
96
+ continue;
97
+ vals[sessionId] = value;
98
+ }
99
+ }
100
+ return vals;
101
+ });
102
+ }
103
+ deleteSession(session) {
104
+ return __awaiter(this, void 0, void 0, function* () {
105
+ delete this._draft.sessions[session];
106
+ });
107
+ }
108
+ pushToList(key, rows, session) {
109
+ var _a;
110
+ var _b;
111
+ return __awaiter(this, void 0, void 0, function* () {
112
+ (_a = (_b = this._draft.sessions)[session]) !== null && _a !== void 0 ? _a : (_b[session] = { keyval: {}, lists: {} });
113
+ const s = this._draft.sessions[session];
114
+ if (!s.lists[key]) {
115
+ s.lists[key] = [];
116
+ }
117
+ const list = s.lists[key];
118
+ if (list.some((row) => rows.some((r) => r.id === row.id))) {
119
+ throw new Error(`Cannot push to list "${key}" because one or more rows already exist`);
120
+ }
121
+ list.push(...rows);
122
+ });
123
+ }
124
+ getList(key, session) {
125
+ return __awaiter(this, void 0, void 0, function* () {
126
+ const s = this._draft.sessions[session];
127
+ if (!(s === null || s === void 0 ? void 0 : s.lists[key])) {
128
+ return [];
129
+ }
130
+ return current(s === null || s === void 0 ? void 0 : s.lists)[key];
131
+ });
132
+ }
133
+ pluckFromList(key, ids, session) {
134
+ var _a;
135
+ var _b;
136
+ return __awaiter(this, void 0, void 0, function* () {
137
+ (_a = (_b = this._draft.sessions)[session]) !== null && _a !== void 0 ? _a : (_b[session] = { keyval: {}, lists: {} });
138
+ const s = this._draft.sessions[session];
139
+ if (!s.lists[key]) {
140
+ return [];
141
+ }
142
+ const list = s.lists[key];
143
+ return ids.map((id) => {
144
+ const index = list.findIndex((row) => row.id === id);
145
+ if (index === -1) {
146
+ return undefined;
147
+ }
148
+ else {
149
+ const row = list[index];
150
+ list.splice(index, 1);
151
+ return row;
152
+ }
153
+ });
154
+ });
155
+ }
156
+ }
@@ -0,0 +1,23 @@
1
+ import type { SessionState, FrontStorageAdapterTransaction, Transaction } from '../types';
2
+ import type { FrontStorageAdapter } from '../types';
3
+ export declare class FrontStorage {
4
+ readonly dbName: string;
5
+ private readonly _adapter;
6
+ constructor(dbName: string, _adapter: FrontStorageAdapter);
7
+ transaction<T>(fn: (opts: FrontStorageTransaction) => Promise<T>): Promise<T>;
8
+ }
9
+ declare class FrontStorageTransactionImpl {
10
+ private _adapterTransaction;
11
+ constructor(_adapterTransaction: FrontStorageAdapterTransaction);
12
+ setSessionState(s: SessionState<unknown>, session: string): Promise<void>;
13
+ getSessionState(session: string): Promise<SessionState<unknown> | undefined>;
14
+ deleteSession(session: string): Promise<void>;
15
+ getMostRecentlySyncedSessionState(): Promise<SessionState<unknown> | undefined>;
16
+ getOptimisticUpdates(session: string): Promise<Transaction[]>;
17
+ pluckOptimisticUpdates(transactions: Pick<Transaction, 'peerClock'>[], session: string): Promise<void>;
18
+ getAllExistingSessionIds(): Promise<string[]>;
19
+ pushOptimisticUpdates(transactions: Transaction[], session: string): Promise<void>;
20
+ }
21
+ export type FrontStorageTransaction = FrontStorageTransactionImpl;
22
+ export {};
23
+ //# sourceMappingURL=FrontStorage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FrontStorage.d.ts","sourceRoot":"","sources":["../../src/front/FrontStorage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,YAAY,EACZ,8BAA8B,EAC9B,WAAW,EACZ,MAAM,UAAU,CAAA;AACjB,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,UAAU,CAAA;AAEjD,qBAAa,YAAY;IAErB,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBADhB,MAAM,EAAE,MAAM,EACN,QAAQ,EAAE,mBAAmB;IAG1C,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,uBAAuB,KAAK,OAAO,CAAC,CAAC,CAAC;CAMvE;AAED,cAAM,2BAA2B;IACnB,OAAO,CAAC,mBAAmB;gBAAnB,mBAAmB,EAAE,8BAA8B;IAEjE,eAAe,CAAC,CAAC,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM;IAMzD,eAAe,CACnB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC;IAKvC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7C,iCAAiC,IAAI,OAAO,CAChD,YAAY,CAAC,OAAO,CAAC,GAAG,SAAS,CAClC;IAgBK,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAQ7D,sBAAsB,CAC1B,YAAY,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,EAC9C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC;IAQV,wBAAwB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAK7C,qBAAqB,CACzB,YAAY,EAAE,WAAW,EAAE,EAC3B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC;CAajB;AAED,MAAM,MAAM,uBAAuB,GAAG,2BAA2B,CAAA"}
@@ -0,0 +1,85 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ export class FrontStorage {
11
+ constructor(dbName, _adapter) {
12
+ this.dbName = dbName;
13
+ this._adapter = _adapter;
14
+ }
15
+ transaction(fn) {
16
+ return __awaiter(this, void 0, void 0, function* () {
17
+ return yield this._adapter.transaction((adapterOpts) => __awaiter(this, void 0, void 0, function* () {
18
+ const t = new FrontStorageTransactionImpl(adapterOpts);
19
+ return yield fn(t);
20
+ }));
21
+ });
22
+ }
23
+ }
24
+ class FrontStorageTransactionImpl {
25
+ constructor(_adapterTransaction) {
26
+ this._adapterTransaction = _adapterTransaction;
27
+ }
28
+ setSessionState(s, session) {
29
+ return __awaiter(this, void 0, void 0, function* () {
30
+ // TODO: serializing the state every time this changes probably wastes IO.
31
+ // better to create a writeahead log and compact it every once in a while
32
+ yield this._adapterTransaction.set('sessionState', s, session);
33
+ });
34
+ }
35
+ getSessionState(session) {
36
+ return __awaiter(this, void 0, void 0, function* () {
37
+ const v = this._adapterTransaction.get('sessionState', session);
38
+ return v;
39
+ });
40
+ }
41
+ deleteSession(session) {
42
+ return __awaiter(this, void 0, void 0, function* () {
43
+ yield this._adapterTransaction.deleteSession(session);
44
+ });
45
+ }
46
+ getMostRecentlySyncedSessionState() {
47
+ var _a;
48
+ return __awaiter(this, void 0, void 0, function* () {
49
+ const allSessionStates = this._adapterTransaction.getAll('sessionState');
50
+ let latest;
51
+ for (const [peerId, sessionState] of Object.entries(allSessionStates)) {
52
+ if (typeof sessionState.backendClock === 'number' &&
53
+ sessionState.backendClock > ((_a = latest === null || latest === void 0 ? void 0 : latest.backendClock) !== null && _a !== void 0 ? _a : -1)) {
54
+ latest = sessionState;
55
+ }
56
+ }
57
+ return latest;
58
+ });
59
+ }
60
+ getOptimisticUpdates(session) {
61
+ return __awaiter(this, void 0, void 0, function* () {
62
+ const s = yield this._adapterTransaction.getList('optimisticUpdates', session);
63
+ return s.map((t) => t.transaction);
64
+ });
65
+ }
66
+ pluckOptimisticUpdates(transactions, session) {
67
+ return __awaiter(this, void 0, void 0, function* () {
68
+ yield this._adapterTransaction.pluckFromList('optimisticUpdates', transactions.map(({ peerClock }) => '#' + peerClock), session);
69
+ });
70
+ }
71
+ getAllExistingSessionIds() {
72
+ return __awaiter(this, void 0, void 0, function* () {
73
+ const all = yield this._adapterTransaction.getAll('session');
74
+ return Object.keys(all);
75
+ });
76
+ }
77
+ pushOptimisticUpdates(transactions, session) {
78
+ return __awaiter(this, void 0, void 0, function* () {
79
+ yield this._adapterTransaction.pushToList('optimisticUpdates', transactions.map((t) => ({
80
+ id: '#' + t.peerClock,
81
+ transaction: t,
82
+ })), session);
83
+ });
84
+ }
85
+ }
@@ -0,0 +1,113 @@
1
+ import type { SessionState, SaazBackInterface, Schema, TempTransactionApi, ValidOpSnapshot } from '../types';
2
+ import type { ValidGenerators, EditorDefinitionToEditorInvocable } from '../types';
3
+ import type { OnDiskSnapshot } from '../types';
4
+ import type { FrontStorageAdapter } from '../types';
5
+ export declare class SaazFront<OpSnapshot extends ValidOpSnapshot, Editors extends {}, Generators extends ValidGenerators, CellShape extends {} = {}> {
6
+ /**
7
+ * Using an atom here so we can react to changes to its state
8
+ */
9
+ private readonly _atom;
10
+ /**
11
+ * The initial snapshot that was saved to disk, and provided as opts.initialSnapshot to the constructor
12
+ */
13
+ private readonly _diskSnapshot;
14
+ /**
15
+ * The peer id of this frontend. It is supposed to be unique per-tab, and globally. If there are more than
16
+ * one Saaz instance per tab, then each should have its own peer id. Better to generate this via UUID.
17
+ *
18
+ * Note that the backend will associate the peerId with the user's account to make sure the peerId cannot
19
+ * be stolen.
20
+ *
21
+ * Each user account may have many peerIds associated with it (the user may have multiple tabs, or multiple devices open at the same time).
22
+ *
23
+ * The backend will periodically garbage-collect the peerIds that have not been used for a while. In the off-chance that a peerId is garbage-collected
24
+ * on the backend but the frontend still has it, the frontend will generate a new peerId and use that instead.
25
+ */
26
+ private readonly _peerId;
27
+ /**
28
+ * The name of the database. This is used to namespace the keys in the storage adapter.
29
+ */
30
+ private _dbName;
31
+ /**
32
+ * The storage adapter that the frontend uses to store its state. The storage is meant to allow the user to work offline
33
+ * and be able to close the tab and reopen it later and continue working.
34
+ */
35
+ private _storage;
36
+ /**
37
+ * A promise that resolves when the frontend is ready to be used. This is the same as the promise returned by the ready getter.
38
+ */
39
+ private _initializedPromise;
40
+ /**
41
+ * The backend that the frontend communicates with. In environments where the backend and the frontend are in the same process,
42
+ * then a reference to SaazBack can be passed here. This is useful for testing.
43
+ *
44
+ * In environments where the backend and the frontend are in different processes (a client/server setup), an object that
45
+ * implements `SaazBackInterface` should be passed here. See `@encorejs/sync-server` for a TRPC-based implementation.
46
+ */
47
+ private _backend;
48
+ /**
49
+ * The schema includes the shape of the snapshot, a nested object of editors, and a shallow object of generator functions.
50
+ */
51
+ private readonly _schema;
52
+ /**
53
+ * A counter that is used to generate unique ids for temp transactions.
54
+ */
55
+ private _tempTransactionCounter;
56
+ /**
57
+ * A list of functions that will be called when the frontend is destroyed.
58
+ */
59
+ private _teardownCallbacks;
60
+ /**
61
+ * We use dataverse prisms to derive several values from the atom. This makes the reactive parts of the code easier to maintain.
62
+ */
63
+ private _prisms;
64
+ private _caches;
65
+ constructor(opts: {
66
+ schema: Schema<OpSnapshot, Editors, Generators, CellShape>;
67
+ backend: SaazBackInterface;
68
+ diskSnapshot?: OnDiskSnapshot<OpSnapshot>;
69
+ peerId?: string;
70
+ dbName: string;
71
+ storageAdapter: FrontStorageAdapter;
72
+ /**
73
+ * If true (default), the frontend will keep the prisms hot. This is a perf optimization
74
+ */
75
+ keepPrismsHot?: boolean;
76
+ });
77
+ private _init;
78
+ private _loadClosedSessions;
79
+ private _acquireLocksOnClosedSessions;
80
+ teardown(): void;
81
+ private _removeStalePeerPresenceStates;
82
+ private _reflectPresenceToBackend;
83
+ private _reflectUpdatesToBackend;
84
+ private _processBackendUpdate;
85
+ private _reflectBackendStateToStorage;
86
+ private _gcClosedSessions;
87
+ private _reflectOptimisticUpdatesToStorage;
88
+ private _cachedApplyTransactionToState;
89
+ _setSessionState(opts: SessionState<OpSnapshot>): void;
90
+ private _pullUpdatesFromBackend;
91
+ private _subscribeToBackend;
92
+ get state(): {
93
+ op: OpSnapshot;
94
+ cell: CellShape;
95
+ };
96
+ get isReady(): boolean;
97
+ get ready(): Promise<unknown>;
98
+ tx(editorFn?: (editors: EditorDefinitionToEditorInvocable<Editors>) => void, draftFn?: (cellDraft: CellShape) => void, undoable?: boolean): void;
99
+ tempTx(editorFn?: (editors: EditorDefinitionToEditorInvocable<Editors>) => void, draftFn?: (cellDraft: CellShape) => void, existingTempTransaction?: TempTransactionApi<Editors, CellShape>): TempTransactionApi<Editors, CellShape>;
100
+ private _setTempTransaction;
101
+ private _createTransaction;
102
+ waitForStorageSync(): Promise<void>;
103
+ private _pushOptimisticUpdate;
104
+ waitForBackendSync(): Promise<void>;
105
+ private _addToUndoStack;
106
+ undo(): void;
107
+ redo(): void;
108
+ subscribe(fn: (newState: {
109
+ op: OpSnapshot;
110
+ cell: CellShape;
111
+ }) => void): () => void;
112
+ }
113
+ //# sourceMappingURL=SaazFront.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SaazFront.d.ts","sourceRoot":"","sources":["../../src/front/SaazFront.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAIV,YAAY,EAEZ,iBAAiB,EACjB,MAAM,EAEN,kBAAkB,EAElB,eAAe,EAChB,MAAM,UAAU,CAAA;AACjB,OAAO,KAAK,EAAC,eAAe,EAAE,iCAAiC,EAAC,MAAM,UAAU,CAAA;AAChF,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,UAAU,CAAA;AAC5C,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,UAAU,CAAA;AAiEjD,qBAAa,SAAS,CACpB,UAAU,SAAS,eAAe,EAClC,OAAO,SAAS,EAAE,EAClB,UAAU,SAAS,eAAe,EAClC,SAAS,SAAS,EAAE,GAAG,EAAE;IAEzB;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAwC;IAC9D;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAmC;IACjE;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC;;OAEG;IACH,OAAO,CAAC,OAAO,CAAQ;IACvB;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAc;IAC9B;;OAEG;IACH,OAAO,CAAC,mBAAmB,CAAkB;IAE7C;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,CAAmB;IAEnC;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyC;IAEjE;;OAEG;IACH,OAAO,CAAC,uBAAuB,CAAY;IAE3C;;OAEG;IACH,OAAO,CAAC,kBAAkB,CAAqB;IAE/C;;OAEG;IACH,OAAO,CAAC,OAAO,CAyJd;IAED,OAAO,CAAC,OAAO,CASd;gBAEW,IAAI,EAAE;QAChB,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,CAAC,CAAA;QAC1D,OAAO,EAAE,iBAAiB,CAAA;QAC1B,YAAY,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAA;QACzC,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,MAAM,EAAE,MAAM,CAAA;QACd,cAAc,EAAE,mBAAmB,CAAA;QACnC;;WAEG;QACH,aAAa,CAAC,EAAE,OAAO,CAAA;KACxB;YAiDa,KAAK;YAsDL,mBAAmB;YAyBnB,6BAA6B;IAiD3C,QAAQ,IAAI,IAAI;IAOhB,OAAO,CAAC,8BAA8B;IAUtC,OAAO,CAAC,yBAAyB;IAwBjC,OAAO,CAAC,wBAAwB;IA+DhC,OAAO,CAAC,qBAAqB;IA6B7B,OAAO,CAAC,6BAA6B;IAqBrC,OAAO,CAAC,iBAAiB;IAqBzB,OAAO,CAAC,kCAAkC;IAuC1C,OAAO,CAAC,8BAA8B;IA6BtC,gBAAgB,CAAC,IAAI,EAAE,YAAY,CAAC,UAAU,CAAC;YAmEjC,uBAAuB;IAcrC,OAAO,CAAC,mBAAmB;IAuB3B,IAAI,KAAK,IAAI;QAAC,EAAE,EAAE,UAAU,CAAC;QAAC,IAAI,EAAE,SAAS,CAAA;KAAC,CAE7C;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC,CAE5B;IAED,EAAE,CACA,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,iCAAiC,CAAC,OAAO,CAAC,KAAK,IAAI,EACxE,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,EACxC,QAAQ,GAAE,OAAc,GACvB,IAAI;IAUP,MAAM,CACJ,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,iCAAiC,CAAC,OAAO,CAAC,KAAK,IAAI,EACxE,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,EACxC,uBAAuB,CAAC,EAAE,kBAAkB,CAAC,OAAO,EAAE,SAAS,CAAC,GAC/D,kBAAkB,CAAC,OAAO,EAAE,SAAS,CAAC;IAgFzC,OAAO,CAAC,mBAAmB;IA4B3B,OAAO,CAAC,kBAAkB;IA6HpB,kBAAkB;IAIxB,OAAO,CAAC,qBAAqB;IA6BvB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQzC,OAAO,CAAC,eAAe;IAuBvB,IAAI;IAyBJ,IAAI;IAyBJ,SAAS,CACP,EAAE,EAAE,CAAC,QAAQ,EAAE;QAAC,EAAE,EAAE,UAAU,CAAC;QAAC,IAAI,EAAE,SAAS,CAAA;KAAC,KAAK,IAAI,GACxD,MAAM,IAAI;CAWd"}