@candidstartup/event-sourced-spreadsheet-data 0.11.0 → 0.12.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.
- package/dist/index.d.ts +23 -5
- package/dist/index.js +89 -51
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
|
-
import { LogEntry, CellValue, SpreadsheetData, EventLog, ItemOffsetMapping,
|
|
1
|
+
import { LogEntry, CellValue, SpreadsheetData, EventLog, Result, StorageError, ItemOffsetMapping, ResultAsync, SpreadsheetDataError, ValidationError } from '@candidstartup/infinisheet-types';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Log entry that captures change from calling {@link SpreadsheetData.setCellValueAndFormat}
|
|
5
|
+
*/
|
|
3
6
|
interface SetCellValueAndFormatLogEntry extends LogEntry {
|
|
4
7
|
type: 'SetCellValueAndFormat';
|
|
8
|
+
/** Row index of cell being modified */
|
|
5
9
|
row: number;
|
|
10
|
+
/** Column index of cell being modified */
|
|
6
11
|
column: number;
|
|
12
|
+
/** Value of cell being modified */
|
|
7
13
|
value: CellValue;
|
|
14
|
+
/** Format of cell being modified */
|
|
8
15
|
format?: string | undefined;
|
|
9
16
|
}
|
|
17
|
+
/** Union of all types of Spreadsheet related log entries */
|
|
10
18
|
type SpreadsheetLogEntry = SetCellValueAndFormatLogEntry;
|
|
11
19
|
|
|
12
20
|
/**
|
|
@@ -19,7 +27,7 @@ declare enum _EventSourcedSnapshotBrand {
|
|
|
19
27
|
_DO_NOT_USE = ""
|
|
20
28
|
}
|
|
21
29
|
/**
|
|
22
|
-
* Opaque type representing
|
|
30
|
+
* Opaque type representing an {@link EventSourcedSpreadsheetData} snapshot. All the
|
|
23
31
|
* internal implementation details are hidden from the exported API.
|
|
24
32
|
*/
|
|
25
33
|
interface EventSourcedSnapshot {
|
|
@@ -31,18 +39,28 @@ interface EventSourcedSnapshot {
|
|
|
31
39
|
*
|
|
32
40
|
*/
|
|
33
41
|
declare class EventSourcedSpreadsheetData implements SpreadsheetData<EventSourcedSnapshot> {
|
|
34
|
-
#private;
|
|
35
42
|
constructor(eventLog: EventLog<SpreadsheetLogEntry>);
|
|
36
43
|
subscribe(onDataChange: () => void): () => void;
|
|
37
44
|
getSnapshot(): EventSourcedSnapshot;
|
|
45
|
+
getLoadStatus(snapshot: EventSourcedSnapshot): Result<boolean, StorageError>;
|
|
38
46
|
getRowCount(snapshot: EventSourcedSnapshot): number;
|
|
39
47
|
getRowItemOffsetMapping(_snapshot: EventSourcedSnapshot): ItemOffsetMapping;
|
|
40
48
|
getColumnCount(snapshot: EventSourcedSnapshot): number;
|
|
41
49
|
getColumnItemOffsetMapping(_snapshot: EventSourcedSnapshot): ItemOffsetMapping;
|
|
42
50
|
getCellValue(snapshot: EventSourcedSnapshot, row: number, column: number): CellValue;
|
|
43
51
|
getCellFormat(snapshot: EventSourcedSnapshot, row: number, column: number): string | undefined;
|
|
44
|
-
setCellValueAndFormat(row: number, column: number, value: CellValue, format: string | undefined):
|
|
52
|
+
setCellValueAndFormat(row: number, column: number, value: CellValue, format: string | undefined): ResultAsync<void, SpreadsheetDataError>;
|
|
45
53
|
isValidCellValueAndFormat(_row: number, _column: number, _value: CellValue, _format: string | undefined): Result<void, ValidationError>;
|
|
54
|
+
private notifyListeners;
|
|
55
|
+
private getCellValueAndFormatEntry;
|
|
56
|
+
private syncLogs;
|
|
57
|
+
private syncLogsAsync;
|
|
58
|
+
private intervalId;
|
|
59
|
+
private isInSyncLogs;
|
|
60
|
+
private eventLog;
|
|
61
|
+
private listeners;
|
|
62
|
+
private content;
|
|
46
63
|
}
|
|
47
64
|
|
|
48
|
-
export {
|
|
65
|
+
export { EventSourcedSpreadsheetData, _EventSourcedSnapshotBrand };
|
|
66
|
+
export type { EventSourcedSnapshot, SetCellValueAndFormatLogEntry, SpreadsheetLogEntry };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FixedSizeItemOffsetMapping, ok } from '@candidstartup/infinisheet-types';
|
|
1
|
+
import { FixedSizeItemOffsetMapping, ok, storageError, err } from '@candidstartup/infinisheet-types';
|
|
2
2
|
|
|
3
3
|
const EVENT_LOG_CHECK_DELAY = 10000;
|
|
4
4
|
/**
|
|
@@ -25,33 +25,36 @@ function asSnapshot(snapshot) {
|
|
|
25
25
|
*/
|
|
26
26
|
class EventSourcedSpreadsheetData {
|
|
27
27
|
constructor(eventLog) {
|
|
28
|
-
this
|
|
29
|
-
this
|
|
30
|
-
this
|
|
31
|
-
this
|
|
32
|
-
this
|
|
28
|
+
this.intervalId = undefined;
|
|
29
|
+
this.isInSyncLogs = false;
|
|
30
|
+
this.eventLog = eventLog;
|
|
31
|
+
this.listeners = [];
|
|
32
|
+
this.content = {
|
|
33
33
|
endSequenceId: 0n,
|
|
34
34
|
logSegment: { startSequenceId: 0n, entries: [] },
|
|
35
|
-
|
|
35
|
+
loadStatus: ok(false),
|
|
36
36
|
rowCount: 0,
|
|
37
37
|
colCount: 0
|
|
38
38
|
};
|
|
39
|
-
this
|
|
39
|
+
this.syncLogs();
|
|
40
40
|
}
|
|
41
41
|
subscribe(onDataChange) {
|
|
42
|
-
if (!this
|
|
43
|
-
this
|
|
44
|
-
this
|
|
42
|
+
if (!this.intervalId)
|
|
43
|
+
this.intervalId = setInterval(() => { this.syncLogs(); }, EVENT_LOG_CHECK_DELAY);
|
|
44
|
+
this.listeners = [...this.listeners, onDataChange];
|
|
45
45
|
return () => {
|
|
46
|
-
this
|
|
47
|
-
if (this
|
|
48
|
-
clearInterval(this
|
|
49
|
-
this
|
|
46
|
+
this.listeners = this.listeners.filter(l => l !== onDataChange);
|
|
47
|
+
if (this.listeners.length == 0 && this.intervalId !== undefined) {
|
|
48
|
+
clearInterval(this.intervalId);
|
|
49
|
+
this.intervalId = undefined;
|
|
50
50
|
}
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
getSnapshot() {
|
|
54
|
-
return asSnapshot(this
|
|
54
|
+
return asSnapshot(this.content);
|
|
55
|
+
}
|
|
56
|
+
getLoadStatus(snapshot) {
|
|
57
|
+
return asContent(snapshot).loadStatus;
|
|
55
58
|
}
|
|
56
59
|
getRowCount(snapshot) {
|
|
57
60
|
return asContent(snapshot).rowCount;
|
|
@@ -66,43 +69,54 @@ class EventSourcedSpreadsheetData {
|
|
|
66
69
|
return columnItemOffsetMapping;
|
|
67
70
|
}
|
|
68
71
|
getCellValue(snapshot, row, column) {
|
|
69
|
-
const entry = this
|
|
72
|
+
const entry = this.getCellValueAndFormatEntry(snapshot, row, column);
|
|
70
73
|
return entry?.value;
|
|
71
74
|
}
|
|
72
75
|
getCellFormat(snapshot, row, column) {
|
|
73
|
-
const entry = this
|
|
76
|
+
const entry = this.getCellValueAndFormatEntry(snapshot, row, column);
|
|
74
77
|
return entry?.format;
|
|
75
78
|
}
|
|
76
79
|
setCellValueAndFormat(row, column, value, format) {
|
|
77
|
-
const curr = this
|
|
78
|
-
const result = this
|
|
79
|
-
result.andTee(() => {
|
|
80
|
-
if (this
|
|
80
|
+
const curr = this.content;
|
|
81
|
+
const result = this.eventLog.addEntry({ type: 'SetCellValueAndFormat', row, column, value, format }, curr.endSequenceId);
|
|
82
|
+
return result.andTee(() => {
|
|
83
|
+
if (this.content == curr) {
|
|
81
84
|
// Nothing else has updated local copy (no async load has snuck in), so safe to do it myself avoiding round trip with event log
|
|
82
85
|
curr.logSegment.entries.push({ type: 'SetCellValueAndFormat', row, column, value, format });
|
|
83
86
|
// Snapshot semantics preserved by treating EventSourcedSnapshot as an immutable data structure which is
|
|
84
87
|
// replaced with a modified copy on every update.
|
|
85
|
-
this
|
|
88
|
+
this.content = {
|
|
86
89
|
endSequenceId: curr.endSequenceId + 1n,
|
|
87
90
|
logSegment: curr.logSegment,
|
|
88
|
-
|
|
91
|
+
loadStatus: ok(true),
|
|
89
92
|
rowCount: Math.max(curr.rowCount, row + 1),
|
|
90
93
|
colCount: Math.max(curr.colCount, column + 1)
|
|
91
94
|
};
|
|
92
|
-
this
|
|
95
|
+
this.notifyListeners();
|
|
93
96
|
}
|
|
94
|
-
}).
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
}).mapErr((err) => {
|
|
98
|
+
switch (err.type) {
|
|
99
|
+
case 'ConflictError':
|
|
100
|
+
if (this.content == curr) {
|
|
101
|
+
// Out of date wrt to event log, nothing else has updated content since then, so set
|
|
102
|
+
// status for in progress load and trigger sync.
|
|
103
|
+
this.content = { ...curr, loadStatus: ok(false) };
|
|
104
|
+
this.syncLogs();
|
|
105
|
+
}
|
|
106
|
+
return storageError("Client out of sync", 409);
|
|
107
|
+
case 'StorageError':
|
|
108
|
+
return err;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
97
111
|
}
|
|
98
112
|
isValidCellValueAndFormat(_row, _column, _value, _format) {
|
|
99
113
|
return ok();
|
|
100
114
|
}
|
|
101
|
-
|
|
102
|
-
for (const listener of this
|
|
115
|
+
notifyListeners() {
|
|
116
|
+
for (const listener of this.listeners)
|
|
103
117
|
listener();
|
|
104
118
|
}
|
|
105
|
-
|
|
119
|
+
getCellValueAndFormatEntry(snapshot, row, column) {
|
|
106
120
|
const content = asContent(snapshot);
|
|
107
121
|
const endIndex = Number(content.endSequenceId - content.logSegment.startSequenceId);
|
|
108
122
|
for (let i = endIndex - 1; i >= 0; i--) {
|
|
@@ -112,32 +126,50 @@ class EventSourcedSpreadsheetData {
|
|
|
112
126
|
}
|
|
113
127
|
return undefined;
|
|
114
128
|
}
|
|
115
|
-
|
|
116
|
-
if (this
|
|
129
|
+
syncLogs() {
|
|
130
|
+
if (this.isInSyncLogs)
|
|
117
131
|
return;
|
|
118
|
-
this
|
|
132
|
+
this.syncLogsAsync().catch((reason) => { throw Error("Rejected promise from syncLogsAsync", { cause: reason }); });
|
|
119
133
|
}
|
|
120
|
-
async
|
|
121
|
-
this
|
|
134
|
+
async syncLogsAsync() {
|
|
135
|
+
this.isInSyncLogs = true;
|
|
122
136
|
// Set up load of first batch of entries
|
|
123
|
-
const segment = this
|
|
137
|
+
const segment = this.content.logSegment;
|
|
124
138
|
let isComplete = false;
|
|
125
139
|
while (!isComplete) {
|
|
126
|
-
const curr = this
|
|
140
|
+
const curr = this.content;
|
|
127
141
|
const start = (segment.entries.length == 0) ? 'snapshot' : curr.endSequenceId;
|
|
128
|
-
const result = await this
|
|
142
|
+
const result = await this.eventLog.query(start, 'end');
|
|
143
|
+
if (curr != this.content) {
|
|
144
|
+
// Must have had setCellValueAndFormat complete successfully and update content to match
|
|
145
|
+
// Query result no longer relevant
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
129
148
|
if (!result.isOk()) {
|
|
130
|
-
|
|
131
|
-
|
|
149
|
+
if (result.error.type == 'InfinisheetRangeError') {
|
|
150
|
+
// Once we have proper snapshot system would expect this if client gets too far behind, for
|
|
151
|
+
// now shouldn't happen.
|
|
152
|
+
throw Error("Query resulted in range error, reload from scratch?", { cause: result.error });
|
|
153
|
+
}
|
|
154
|
+
// Could do some immediate retries of intermittent errors (limited times, jitter and backoff).
|
|
155
|
+
// For now wait for interval timer to try another sync
|
|
156
|
+
// For persistent failures should stop interval timer and have some mechanism for user to trigger
|
|
157
|
+
// manual retry.
|
|
158
|
+
this.content = { ...curr, loadStatus: err(result.error) };
|
|
159
|
+
this.notifyListeners();
|
|
160
|
+
break;
|
|
132
161
|
}
|
|
133
162
|
// Extend the current loaded segment.
|
|
134
163
|
// Once snapshots supported need to look out for new snapshot and start new segment
|
|
135
164
|
const value = result.value;
|
|
136
165
|
if (segment.entries.length == 0)
|
|
137
166
|
segment.startSequenceId = value.startSequenceId;
|
|
138
|
-
else if (curr.endSequenceId != value.startSequenceId)
|
|
167
|
+
else if (curr.endSequenceId != value.startSequenceId) {
|
|
168
|
+
// Shouldn't happen unless we have buggy event log implementation
|
|
139
169
|
throw Error(`Query returned start ${value.startSequenceId}, expected ${curr.endSequenceId}`);
|
|
170
|
+
}
|
|
140
171
|
isComplete = value.isComplete;
|
|
172
|
+
// Don't create new snapshot if nothing has changed
|
|
141
173
|
if (value.entries.length > 0) {
|
|
142
174
|
segment.entries.push(...value.entries);
|
|
143
175
|
// Create a new snapshot based on the new data
|
|
@@ -147,21 +179,27 @@ class EventSourcedSpreadsheetData {
|
|
|
147
179
|
rowCount = Math.max(rowCount, entry.row + 1);
|
|
148
180
|
colCount = Math.max(colCount, entry.column + 1);
|
|
149
181
|
}
|
|
150
|
-
this
|
|
182
|
+
this.content = {
|
|
151
183
|
endSequenceId: value.endSequenceId,
|
|
152
184
|
logSegment: segment,
|
|
153
|
-
isComplete,
|
|
185
|
+
loadStatus: ok(isComplete),
|
|
186
|
+
rowCount, colCount
|
|
154
187
|
};
|
|
155
|
-
this
|
|
188
|
+
this.notifyListeners();
|
|
189
|
+
}
|
|
190
|
+
else if (curr.loadStatus.isErr() || curr.loadStatus.value != isComplete) {
|
|
191
|
+
// Careful, even if no entries returned, loadStatus may have changed
|
|
192
|
+
this.content = { ...curr, loadStatus: ok(isComplete) };
|
|
193
|
+
this.notifyListeners();
|
|
156
194
|
}
|
|
157
195
|
}
|
|
158
|
-
this
|
|
196
|
+
this.isInSyncLogs = false;
|
|
159
197
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
198
|
+
intervalId;
|
|
199
|
+
isInSyncLogs;
|
|
200
|
+
eventLog;
|
|
201
|
+
listeners;
|
|
202
|
+
content;
|
|
165
203
|
}
|
|
166
204
|
|
|
167
205
|
export { EventSourcedSpreadsheetData, _EventSourcedSnapshotBrand };
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/EventSourcedSpreadsheetData.ts"],"sourcesContent":["import type { CellValue, SpreadsheetData, ItemOffsetMapping, Result, \n SpreadsheetDataError, ValidationError, SequenceId, BlobId, EventLog } from \"@candidstartup/infinisheet-types\";\nimport { FixedSizeItemOffsetMapping, ok } from \"@candidstartup/infinisheet-types\";\n\nimport type { SpreadsheetLogEntry, SetCellValueAndFormatLogEntry } from \"./SpreadsheetLogEntry\";\n\nconst EVENT_LOG_CHECK_DELAY = 10000;\n\ninterface LogSegment {\n startSequenceId: SequenceId;\n entries: SpreadsheetLogEntry[];\n snapshot?: BlobId | undefined;\n}\n\ninterface EventSourcedSnapshotContent {\n endSequenceId: SequenceId;\n logSegment: LogSegment;\n isComplete: boolean;\n rowCount: number;\n colCount: number;\n}\n\n/** \n * Branding Enum. Used by {@link EventSourcedSnapshot} to ensure that\n * you'll get a type error if you pass some random object where a `EventSourcedSnapshot`\n * is expected.\n * @internal\n */\nexport enum _EventSourcedSnapshotBrand { _DO_NOT_USE=\"\" };\n\n/**\n * Opaque type representing a {@link SimpleSpreadsheetData} snapshot. All the\n * internal implementation details are hidden from the exported API.\n */\nexport interface EventSourcedSnapshot {\n /** @internal */\n _brand: _EventSourcedSnapshotBrand;\n}\n\nconst rowItemOffsetMapping = new FixedSizeItemOffsetMapping(30);\nconst columnItemOffsetMapping = new FixedSizeItemOffsetMapping(100);\n\nfunction asContent(snapshot: EventSourcedSnapshot) {\n return snapshot as unknown as EventSourcedSnapshotContent;\n}\n\nfunction asSnapshot(snapshot: EventSourcedSnapshotContent) {\n return snapshot as unknown as EventSourcedSnapshot;\n}\n\n/**\n * Event sourced implementation of {@link SpreadsheetData}\n *\n */\nexport class EventSourcedSpreadsheetData implements SpreadsheetData<EventSourcedSnapshot> {\n constructor (eventLog: EventLog<SpreadsheetLogEntry>) {\n this.#intervalId = undefined;\n this.#isInSyncLogs = false;\n this.#eventLog = eventLog;\n this.#listeners = [];\n this.#content = {\n endSequenceId: 0n,\n logSegment: { startSequenceId: 0n, entries: [] },\n isComplete: false,\n rowCount: 0,\n colCount: 0\n }\n\n this.#syncLogs();\n }\n\n subscribe(onDataChange: () => void): () => void {\n if (!this.#intervalId)\n this.#intervalId = setInterval(() => { this.#syncLogs() }, EVENT_LOG_CHECK_DELAY);\n this.#listeners = [...this.#listeners, onDataChange];\n return () => {\n this.#listeners = this.#listeners.filter(l => l !== onDataChange);\n if (this.#listeners.length == 0 && this.#intervalId !== undefined) {\n clearInterval(this.#intervalId);\n this.#intervalId = undefined;\n }\n }\n }\n\n getSnapshot(): EventSourcedSnapshot {\n return asSnapshot(this.#content);\n }\n\n getRowCount(snapshot: EventSourcedSnapshot): number {\n return asContent(snapshot).rowCount;\n }\n\n getRowItemOffsetMapping(_snapshot: EventSourcedSnapshot): ItemOffsetMapping {\n return rowItemOffsetMapping;\n }\n\n getColumnCount(snapshot: EventSourcedSnapshot): number {\n return asContent(snapshot).colCount;\n }\n\n getColumnItemOffsetMapping(_snapshot: EventSourcedSnapshot): ItemOffsetMapping {\n return columnItemOffsetMapping\n }\n\n getCellValue(snapshot: EventSourcedSnapshot, row: number, column: number): CellValue {\n const entry = this.#getCellValueAndFormatEntry(snapshot, row, column);\n return entry?.value;\n }\n\n getCellFormat(snapshot: EventSourcedSnapshot, row: number, column: number): string | undefined {\n const entry = this.#getCellValueAndFormatEntry(snapshot, row, column);\n return entry?.format;\n }\n\n setCellValueAndFormat(row: number, column: number, value: CellValue, format: string | undefined): Result<void,SpreadsheetDataError> {\n const curr = this.#content;\n\n const result = this.#eventLog.addEntry({ type: 'SetCellValueAndFormat', row, column, value, format}, curr.endSequenceId);\n result.andTee(() => {\n if (this.#content == curr) {\n // Nothing else has updated local copy (no async load has snuck in), so safe to do it myself avoiding round trip with event log\n curr.logSegment.entries.push({ type: 'SetCellValueAndFormat', row, column, value, format});\n\n // Snapshot semantics preserved by treating EventSourcedSnapshot as an immutable data structure which is \n // replaced with a modified copy on every update.\n this.#content = {\n endSequenceId: curr.endSequenceId + 1n,\n logSegment: curr.logSegment,\n isComplete: true,\n rowCount: Math.max(curr.rowCount, row+1),\n colCount: Math.max(curr.colCount, column+1)\n }\n\n this.#notifyListeners();\n }\n }).orElse((err) => { throw Error(err.message); });\n\n // Oh no, this method needs to become async ...\n return ok();\n }\n\n isValidCellValueAndFormat(_row: number, _column: number, _value: CellValue, _format: string | undefined): Result<void,ValidationError> {\n return ok(); \n }\n\n #notifyListeners() {\n for (const listener of this.#listeners)\n listener();\n }\n\n #getCellValueAndFormatEntry(snapshot: EventSourcedSnapshot, row: number, column: number): SetCellValueAndFormatLogEntry | undefined {\n const content = asContent(snapshot);\n const endIndex = Number(content.endSequenceId-content.logSegment.startSequenceId);\n for (let i = endIndex-1; i >= 0; i --) {\n const entry = content.logSegment.entries[i]!;\n if (entry.row == row && entry.column == column)\n return entry;\n }\n return undefined;\n }\n\n #syncLogs(): void {\n if (this.#isInSyncLogs)\n return;\n\n this.#syncLogsAsync().catch((reason) => { throw Error(\"Rejected promise from #syncLogsAsync\", { cause: reason }) });\n }\n\n async #syncLogsAsync(): Promise<void> {\n this.#isInSyncLogs = true;\n\n // Set up load of first batch of entries\n const segment = this.#content.logSegment;\n let isComplete = false;\n\n while (!isComplete) {\n const curr = this.#content;\n const start = (segment.entries.length == 0) ? 'snapshot' : curr.endSequenceId;\n const result = await this.#eventLog.query(start, 'end');\n\n if (!result.isOk()) {\n // Depending on error may need to retry (limited times, jitter and backoff), reload from scratch or panic\n throw Error(\"Error querying log entries\");\n }\n\n // Extend the current loaded segment.\n // Once snapshots supported need to look out for new snapshot and start new segment\n const value = result.value;\n if (segment.entries.length == 0)\n segment.startSequenceId = value.startSequenceId;\n else if (curr.endSequenceId != value.startSequenceId)\n throw Error(`Query returned start ${value.startSequenceId}, expected ${curr.endSequenceId}`);\n isComplete = value.isComplete;\n\n if (value.entries.length > 0) {\n segment.entries.push(...value.entries);\n\n // Create a new snapshot based on the new data\n let rowCount = curr.rowCount;\n let colCount = curr.colCount;\n for (const entry of value.entries) {\n rowCount = Math.max(rowCount, entry.row+1);\n colCount = Math.max(colCount, entry.column+1);\n }\n\n this.#content = {\n endSequenceId: value.endSequenceId,\n logSegment: segment,\n isComplete, rowCount, colCount\n }\n\n this.#notifyListeners();\n }\n }\n\n this.#isInSyncLogs = false;\n }\n\n #intervalId: ReturnType<typeof setInterval> | undefined;\n #isInSyncLogs: boolean;\n #eventLog: EventLog<SpreadsheetLogEntry>;\n #listeners: (() => void)[];\n #content: EventSourcedSnapshotContent;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/EventSourcedSpreadsheetData.ts"],"sourcesContent":["import type { CellValue, SpreadsheetData, ItemOffsetMapping, Result, ResultAsync, StorageError, \n SpreadsheetDataError, ValidationError, SequenceId, BlobId, EventLog } from \"@candidstartup/infinisheet-types\";\nimport { FixedSizeItemOffsetMapping, ok, err, storageError } from \"@candidstartup/infinisheet-types\";\n\nimport type { SpreadsheetLogEntry, SetCellValueAndFormatLogEntry } from \"./SpreadsheetLogEntry\";\n\nconst EVENT_LOG_CHECK_DELAY = 10000;\n\ninterface LogSegment {\n startSequenceId: SequenceId;\n entries: SpreadsheetLogEntry[];\n snapshot?: BlobId | undefined;\n}\n\ninterface EventSourcedSnapshotContent {\n endSequenceId: SequenceId;\n logSegment: LogSegment;\n loadStatus: Result<boolean,StorageError>;\n rowCount: number;\n colCount: number;\n}\n\n/** \n * Branding Enum. Used by {@link EventSourcedSnapshot} to ensure that\n * you'll get a type error if you pass some random object where a `EventSourcedSnapshot`\n * is expected.\n * @internal\n */\nexport enum _EventSourcedSnapshotBrand { _DO_NOT_USE=\"\" };\n\n/**\n * Opaque type representing an {@link EventSourcedSpreadsheetData} snapshot. All the\n * internal implementation details are hidden from the exported API.\n */\nexport interface EventSourcedSnapshot {\n /** @internal */\n _brand: _EventSourcedSnapshotBrand;\n}\n\nconst rowItemOffsetMapping = new FixedSizeItemOffsetMapping(30);\nconst columnItemOffsetMapping = new FixedSizeItemOffsetMapping(100);\n\nfunction asContent(snapshot: EventSourcedSnapshot) {\n return snapshot as unknown as EventSourcedSnapshotContent;\n}\n\nfunction asSnapshot(snapshot: EventSourcedSnapshotContent) {\n return snapshot as unknown as EventSourcedSnapshot;\n}\n\n/**\n * Event sourced implementation of {@link SpreadsheetData}\n *\n */\nexport class EventSourcedSpreadsheetData implements SpreadsheetData<EventSourcedSnapshot> {\n constructor (eventLog: EventLog<SpreadsheetLogEntry>) {\n this.intervalId = undefined;\n this.isInSyncLogs = false;\n this.eventLog = eventLog;\n this.listeners = [];\n this.content = {\n endSequenceId: 0n,\n logSegment: { startSequenceId: 0n, entries: [] },\n loadStatus: ok(false),\n rowCount: 0,\n colCount: 0\n }\n\n this.syncLogs();\n }\n\n subscribe(onDataChange: () => void): () => void {\n if (!this.intervalId)\n this.intervalId = setInterval(() => { this.syncLogs() }, EVENT_LOG_CHECK_DELAY);\n this.listeners = [...this.listeners, onDataChange];\n return () => {\n this.listeners = this.listeners.filter(l => l !== onDataChange);\n if (this.listeners.length == 0 && this.intervalId !== undefined) {\n clearInterval(this.intervalId);\n this.intervalId = undefined;\n }\n }\n }\n\n getSnapshot(): EventSourcedSnapshot {\n return asSnapshot(this.content);\n }\n\n getLoadStatus(snapshot: EventSourcedSnapshot): Result<boolean,StorageError> {\n return asContent(snapshot).loadStatus;\n }\n\n getRowCount(snapshot: EventSourcedSnapshot): number {\n return asContent(snapshot).rowCount;\n }\n\n getRowItemOffsetMapping(_snapshot: EventSourcedSnapshot): ItemOffsetMapping {\n return rowItemOffsetMapping;\n }\n\n getColumnCount(snapshot: EventSourcedSnapshot): number {\n return asContent(snapshot).colCount;\n }\n\n getColumnItemOffsetMapping(_snapshot: EventSourcedSnapshot): ItemOffsetMapping {\n return columnItemOffsetMapping\n }\n\n getCellValue(snapshot: EventSourcedSnapshot, row: number, column: number): CellValue {\n const entry = this.getCellValueAndFormatEntry(snapshot, row, column);\n return entry?.value;\n }\n\n getCellFormat(snapshot: EventSourcedSnapshot, row: number, column: number): string | undefined {\n const entry = this.getCellValueAndFormatEntry(snapshot, row, column);\n return entry?.format;\n }\n\n setCellValueAndFormat(row: number, column: number, value: CellValue, format: string | undefined): ResultAsync<void,SpreadsheetDataError> {\n const curr = this.content;\n\n const result = this.eventLog.addEntry({ type: 'SetCellValueAndFormat', row, column, value, format}, curr.endSequenceId);\n return result.andTee(() => {\n if (this.content == curr) {\n // Nothing else has updated local copy (no async load has snuck in), so safe to do it myself avoiding round trip with event log\n curr.logSegment.entries.push({ type: 'SetCellValueAndFormat', row, column, value, format});\n\n // Snapshot semantics preserved by treating EventSourcedSnapshot as an immutable data structure which is \n // replaced with a modified copy on every update.\n this.content = {\n endSequenceId: curr.endSequenceId + 1n,\n logSegment: curr.logSegment,\n loadStatus: ok(true),\n rowCount: Math.max(curr.rowCount, row+1),\n colCount: Math.max(curr.colCount, column+1)\n }\n\n this.notifyListeners();\n }\n }).mapErr((err): SpreadsheetDataError => {\n switch (err.type) {\n case 'ConflictError':\n if (this.content == curr) {\n // Out of date wrt to event log, nothing else has updated content since then, so set\n // status for in progress load and trigger sync.\n this.content = { ...curr, loadStatus: ok(false) }\n this.syncLogs();\n }\n return storageError(\"Client out of sync\", 409);\n case 'StorageError': \n return err;\n }\n });\n }\n\n isValidCellValueAndFormat(_row: number, _column: number, _value: CellValue, _format: string | undefined): Result<void,ValidationError> {\n return ok(); \n }\n\n private notifyListeners() {\n for (const listener of this.listeners)\n listener();\n }\n\n private getCellValueAndFormatEntry(snapshot: EventSourcedSnapshot, row: number, column: number): SetCellValueAndFormatLogEntry | undefined {\n const content = asContent(snapshot);\n const endIndex = Number(content.endSequenceId-content.logSegment.startSequenceId);\n for (let i = endIndex-1; i >= 0; i --) {\n const entry = content.logSegment.entries[i]!;\n if (entry.row == row && entry.column == column)\n return entry;\n }\n return undefined;\n }\n\n private syncLogs(): void {\n if (this.isInSyncLogs)\n return;\n\n this.syncLogsAsync().catch((reason) => { throw Error(\"Rejected promise from syncLogsAsync\", { cause: reason }) });\n }\n\n private async syncLogsAsync(): Promise<void> {\n this.isInSyncLogs = true;\n\n // Set up load of first batch of entries\n const segment = this.content.logSegment;\n let isComplete = false;\n\n while (!isComplete) {\n const curr = this.content;\n const start = (segment.entries.length == 0) ? 'snapshot' : curr.endSequenceId;\n const result = await this.eventLog.query(start, 'end');\n\n if (curr != this.content) {\n // Must have had setCellValueAndFormat complete successfully and update content to match\n // Query result no longer relevant\n break;\n }\n\n if (!result.isOk()) {\n if (result.error.type == 'InfinisheetRangeError') {\n // Once we have proper snapshot system would expect this if client gets too far behind, for\n // now shouldn't happen.\n throw Error(\"Query resulted in range error, reload from scratch?\", { cause: result.error });\n }\n\n // Could do some immediate retries of intermittent errors (limited times, jitter and backoff).\n // For now wait for interval timer to try another sync\n // For persistent failures should stop interval timer and have some mechanism for user to trigger\n // manual retry. \n this.content = { ...curr, loadStatus: err(result.error)};\n this.notifyListeners();\n break;\n }\n\n // Extend the current loaded segment.\n // Once snapshots supported need to look out for new snapshot and start new segment\n const value = result.value;\n if (segment.entries.length == 0)\n segment.startSequenceId = value.startSequenceId;\n else if (curr.endSequenceId != value.startSequenceId) {\n // Shouldn't happen unless we have buggy event log implementation\n throw Error(`Query returned start ${value.startSequenceId}, expected ${curr.endSequenceId}`);\n }\n isComplete = value.isComplete;\n\n // Don't create new snapshot if nothing has changed\n if (value.entries.length > 0) {\n segment.entries.push(...value.entries);\n\n // Create a new snapshot based on the new data\n let rowCount = curr.rowCount;\n let colCount = curr.colCount;\n for (const entry of value.entries) {\n rowCount = Math.max(rowCount, entry.row+1);\n colCount = Math.max(colCount, entry.column+1);\n }\n\n this.content = {\n endSequenceId: value.endSequenceId,\n logSegment: segment,\n loadStatus: ok(isComplete),\n rowCount, colCount\n }\n\n this.notifyListeners();\n } else if (curr.loadStatus.isErr() || curr.loadStatus.value != isComplete) {\n // Careful, even if no entries returned, loadStatus may have changed\n this.content = { ...curr, loadStatus: ok(isComplete) }\n this.notifyListeners();\n }\n }\n\n this.isInSyncLogs = false;\n }\n\n private intervalId: ReturnType<typeof setInterval> | undefined;\n private isInSyncLogs: boolean;\n private eventLog: EventLog<SpreadsheetLogEntry>;\n private listeners: (() => void)[];\n private content: EventSourcedSnapshotContent;\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@candidstartup/event-sourced-spreadsheet-data",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.12.0",
|
|
5
5
|
"description": "Event sourced implementation of SpreadsheetData",
|
|
6
6
|
"author": "Tim Wiegand <tim.wiegand@thecandidstartup.org>",
|
|
7
7
|
"license": "BSD-3-Clause",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"test": "vitest"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@candidstartup/infinisheet-types": "^0.
|
|
52
|
+
"@candidstartup/infinisheet-types": "^0.12.0"
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "03b639807d367f0c167dc5b6653b3935412cbde8"
|
|
55
55
|
}
|