@candidstartup/event-sourced-spreadsheet-data 0.11.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/LICENSE +29 -0
- package/README.md +10 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +168 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023-2024, Tim Wiegand
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
[](https://github.com/TheCandidStartup/infinisheet/blob/main/README.md#typescript-semantic-versioning)
|
|
2
|
+
[](https://www.npmjs.com/package/@candidstartup/event-sourced-spreadsheet-data)
|
|
3
|
+
[](https://www.npmjs.com/package/@candidstartup/event-sourced-spreadsheet-data)
|
|
4
|
+
[](https://github.com/TheCandidStartup/infinisheet/actions/workflows/build.yml)
|
|
5
|
+
|
|
6
|
+
[GitHub](https://github.com/TheCandidStartup/infinisheet/tree/main/packages/event-sourced-spreadsheet-data) | [NPM](https://www.npmjs.com/package/@candidstartup/event-sourced-spreadsheet-data) | [API](https://www.thecandidstartup.org/infinisheet/modules/_candidstartup_event-sourced-spreadsheet-data.html)
|
|
7
|
+
|
|
8
|
+
# @candidstartup/event-sourced-spreadsheet-data
|
|
9
|
+
|
|
10
|
+
Event Sourced implementation of `SpreadsheetData`
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { LogEntry, CellValue, SpreadsheetData, EventLog, ItemOffsetMapping, Result, SpreadsheetDataError, ValidationError } from '@candidstartup/infinisheet-types';
|
|
2
|
+
|
|
3
|
+
interface SetCellValueAndFormatLogEntry extends LogEntry {
|
|
4
|
+
type: 'SetCellValueAndFormat';
|
|
5
|
+
row: number;
|
|
6
|
+
column: number;
|
|
7
|
+
value: CellValue;
|
|
8
|
+
format?: string | undefined;
|
|
9
|
+
}
|
|
10
|
+
type SpreadsheetLogEntry = SetCellValueAndFormatLogEntry;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Branding Enum. Used by {@link EventSourcedSnapshot} to ensure that
|
|
14
|
+
* you'll get a type error if you pass some random object where a `EventSourcedSnapshot`
|
|
15
|
+
* is expected.
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
declare enum _EventSourcedSnapshotBrand {
|
|
19
|
+
_DO_NOT_USE = ""
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Opaque type representing a {@link SimpleSpreadsheetData} snapshot. All the
|
|
23
|
+
* internal implementation details are hidden from the exported API.
|
|
24
|
+
*/
|
|
25
|
+
interface EventSourcedSnapshot {
|
|
26
|
+
/** @internal */
|
|
27
|
+
_brand: _EventSourcedSnapshotBrand;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Event sourced implementation of {@link SpreadsheetData}
|
|
31
|
+
*
|
|
32
|
+
*/
|
|
33
|
+
declare class EventSourcedSpreadsheetData implements SpreadsheetData<EventSourcedSnapshot> {
|
|
34
|
+
#private;
|
|
35
|
+
constructor(eventLog: EventLog<SpreadsheetLogEntry>);
|
|
36
|
+
subscribe(onDataChange: () => void): () => void;
|
|
37
|
+
getSnapshot(): EventSourcedSnapshot;
|
|
38
|
+
getRowCount(snapshot: EventSourcedSnapshot): number;
|
|
39
|
+
getRowItemOffsetMapping(_snapshot: EventSourcedSnapshot): ItemOffsetMapping;
|
|
40
|
+
getColumnCount(snapshot: EventSourcedSnapshot): number;
|
|
41
|
+
getColumnItemOffsetMapping(_snapshot: EventSourcedSnapshot): ItemOffsetMapping;
|
|
42
|
+
getCellValue(snapshot: EventSourcedSnapshot, row: number, column: number): CellValue;
|
|
43
|
+
getCellFormat(snapshot: EventSourcedSnapshot, row: number, column: number): string | undefined;
|
|
44
|
+
setCellValueAndFormat(row: number, column: number, value: CellValue, format: string | undefined): Result<void, SpreadsheetDataError>;
|
|
45
|
+
isValidCellValueAndFormat(_row: number, _column: number, _value: CellValue, _format: string | undefined): Result<void, ValidationError>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export { type EventSourcedSnapshot, EventSourcedSpreadsheetData, type SetCellValueAndFormatLogEntry, type SpreadsheetLogEntry, _EventSourcedSnapshotBrand };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { FixedSizeItemOffsetMapping, ok } from '@candidstartup/infinisheet-types';
|
|
2
|
+
|
|
3
|
+
const EVENT_LOG_CHECK_DELAY = 10000;
|
|
4
|
+
/**
|
|
5
|
+
* Branding Enum. Used by {@link EventSourcedSnapshot} to ensure that
|
|
6
|
+
* you'll get a type error if you pass some random object where a `EventSourcedSnapshot`
|
|
7
|
+
* is expected.
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
var _EventSourcedSnapshotBrand;
|
|
11
|
+
(function (_EventSourcedSnapshotBrand) {
|
|
12
|
+
_EventSourcedSnapshotBrand["_DO_NOT_USE"] = "";
|
|
13
|
+
})(_EventSourcedSnapshotBrand || (_EventSourcedSnapshotBrand = {}));
|
|
14
|
+
const rowItemOffsetMapping = new FixedSizeItemOffsetMapping(30);
|
|
15
|
+
const columnItemOffsetMapping = new FixedSizeItemOffsetMapping(100);
|
|
16
|
+
function asContent(snapshot) {
|
|
17
|
+
return snapshot;
|
|
18
|
+
}
|
|
19
|
+
function asSnapshot(snapshot) {
|
|
20
|
+
return snapshot;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Event sourced implementation of {@link SpreadsheetData}
|
|
24
|
+
*
|
|
25
|
+
*/
|
|
26
|
+
class EventSourcedSpreadsheetData {
|
|
27
|
+
constructor(eventLog) {
|
|
28
|
+
this.#intervalId = undefined;
|
|
29
|
+
this.#isInSyncLogs = false;
|
|
30
|
+
this.#eventLog = eventLog;
|
|
31
|
+
this.#listeners = [];
|
|
32
|
+
this.#content = {
|
|
33
|
+
endSequenceId: 0n,
|
|
34
|
+
logSegment: { startSequenceId: 0n, entries: [] },
|
|
35
|
+
isComplete: false,
|
|
36
|
+
rowCount: 0,
|
|
37
|
+
colCount: 0
|
|
38
|
+
};
|
|
39
|
+
this.#syncLogs();
|
|
40
|
+
}
|
|
41
|
+
subscribe(onDataChange) {
|
|
42
|
+
if (!this.#intervalId)
|
|
43
|
+
this.#intervalId = setInterval(() => { this.#syncLogs(); }, EVENT_LOG_CHECK_DELAY);
|
|
44
|
+
this.#listeners = [...this.#listeners, onDataChange];
|
|
45
|
+
return () => {
|
|
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
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
getSnapshot() {
|
|
54
|
+
return asSnapshot(this.#content);
|
|
55
|
+
}
|
|
56
|
+
getRowCount(snapshot) {
|
|
57
|
+
return asContent(snapshot).rowCount;
|
|
58
|
+
}
|
|
59
|
+
getRowItemOffsetMapping(_snapshot) {
|
|
60
|
+
return rowItemOffsetMapping;
|
|
61
|
+
}
|
|
62
|
+
getColumnCount(snapshot) {
|
|
63
|
+
return asContent(snapshot).colCount;
|
|
64
|
+
}
|
|
65
|
+
getColumnItemOffsetMapping(_snapshot) {
|
|
66
|
+
return columnItemOffsetMapping;
|
|
67
|
+
}
|
|
68
|
+
getCellValue(snapshot, row, column) {
|
|
69
|
+
const entry = this.#getCellValueAndFormatEntry(snapshot, row, column);
|
|
70
|
+
return entry?.value;
|
|
71
|
+
}
|
|
72
|
+
getCellFormat(snapshot, row, column) {
|
|
73
|
+
const entry = this.#getCellValueAndFormatEntry(snapshot, row, column);
|
|
74
|
+
return entry?.format;
|
|
75
|
+
}
|
|
76
|
+
setCellValueAndFormat(row, column, value, format) {
|
|
77
|
+
const curr = this.#content;
|
|
78
|
+
const result = this.#eventLog.addEntry({ type: 'SetCellValueAndFormat', row, column, value, format }, curr.endSequenceId);
|
|
79
|
+
result.andTee(() => {
|
|
80
|
+
if (this.#content == curr) {
|
|
81
|
+
// 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
|
+
curr.logSegment.entries.push({ type: 'SetCellValueAndFormat', row, column, value, format });
|
|
83
|
+
// Snapshot semantics preserved by treating EventSourcedSnapshot as an immutable data structure which is
|
|
84
|
+
// replaced with a modified copy on every update.
|
|
85
|
+
this.#content = {
|
|
86
|
+
endSequenceId: curr.endSequenceId + 1n,
|
|
87
|
+
logSegment: curr.logSegment,
|
|
88
|
+
isComplete: true,
|
|
89
|
+
rowCount: Math.max(curr.rowCount, row + 1),
|
|
90
|
+
colCount: Math.max(curr.colCount, column + 1)
|
|
91
|
+
};
|
|
92
|
+
this.#notifyListeners();
|
|
93
|
+
}
|
|
94
|
+
}).orElse((err) => { throw Error(err.message); });
|
|
95
|
+
// Oh no, this method needs to become async ...
|
|
96
|
+
return ok();
|
|
97
|
+
}
|
|
98
|
+
isValidCellValueAndFormat(_row, _column, _value, _format) {
|
|
99
|
+
return ok();
|
|
100
|
+
}
|
|
101
|
+
#notifyListeners() {
|
|
102
|
+
for (const listener of this.#listeners)
|
|
103
|
+
listener();
|
|
104
|
+
}
|
|
105
|
+
#getCellValueAndFormatEntry(snapshot, row, column) {
|
|
106
|
+
const content = asContent(snapshot);
|
|
107
|
+
const endIndex = Number(content.endSequenceId - content.logSegment.startSequenceId);
|
|
108
|
+
for (let i = endIndex - 1; i >= 0; i--) {
|
|
109
|
+
const entry = content.logSegment.entries[i];
|
|
110
|
+
if (entry.row == row && entry.column == column)
|
|
111
|
+
return entry;
|
|
112
|
+
}
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
#syncLogs() {
|
|
116
|
+
if (this.#isInSyncLogs)
|
|
117
|
+
return;
|
|
118
|
+
this.#syncLogsAsync().catch((reason) => { throw Error("Rejected promise from #syncLogsAsync", { cause: reason }); });
|
|
119
|
+
}
|
|
120
|
+
async #syncLogsAsync() {
|
|
121
|
+
this.#isInSyncLogs = true;
|
|
122
|
+
// Set up load of first batch of entries
|
|
123
|
+
const segment = this.#content.logSegment;
|
|
124
|
+
let isComplete = false;
|
|
125
|
+
while (!isComplete) {
|
|
126
|
+
const curr = this.#content;
|
|
127
|
+
const start = (segment.entries.length == 0) ? 'snapshot' : curr.endSequenceId;
|
|
128
|
+
const result = await this.#eventLog.query(start, 'end');
|
|
129
|
+
if (!result.isOk()) {
|
|
130
|
+
// Depending on error may need to retry (limited times, jitter and backoff), reload from scratch or panic
|
|
131
|
+
throw Error("Error querying log entries");
|
|
132
|
+
}
|
|
133
|
+
// Extend the current loaded segment.
|
|
134
|
+
// Once snapshots supported need to look out for new snapshot and start new segment
|
|
135
|
+
const value = result.value;
|
|
136
|
+
if (segment.entries.length == 0)
|
|
137
|
+
segment.startSequenceId = value.startSequenceId;
|
|
138
|
+
else if (curr.endSequenceId != value.startSequenceId)
|
|
139
|
+
throw Error(`Query returned start ${value.startSequenceId}, expected ${curr.endSequenceId}`);
|
|
140
|
+
isComplete = value.isComplete;
|
|
141
|
+
if (value.entries.length > 0) {
|
|
142
|
+
segment.entries.push(...value.entries);
|
|
143
|
+
// Create a new snapshot based on the new data
|
|
144
|
+
let rowCount = curr.rowCount;
|
|
145
|
+
let colCount = curr.colCount;
|
|
146
|
+
for (const entry of value.entries) {
|
|
147
|
+
rowCount = Math.max(rowCount, entry.row + 1);
|
|
148
|
+
colCount = Math.max(colCount, entry.column + 1);
|
|
149
|
+
}
|
|
150
|
+
this.#content = {
|
|
151
|
+
endSequenceId: value.endSequenceId,
|
|
152
|
+
logSegment: segment,
|
|
153
|
+
isComplete, rowCount, colCount
|
|
154
|
+
};
|
|
155
|
+
this.#notifyListeners();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
this.#isInSyncLogs = false;
|
|
159
|
+
}
|
|
160
|
+
#intervalId;
|
|
161
|
+
#isInSyncLogs;
|
|
162
|
+
#eventLog;
|
|
163
|
+
#listeners;
|
|
164
|
+
#content;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export { EventSourcedSpreadsheetData, _EventSourcedSnapshotBrand };
|
|
168
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@candidstartup/event-sourced-spreadsheet-data",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "0.11.0",
|
|
5
|
+
"description": "Event sourced implementation of SpreadsheetData",
|
|
6
|
+
"author": "Tim Wiegand <tim.wiegand@thecandidstartup.org>",
|
|
7
|
+
"license": "BSD-3-Clause",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/TheCandidStartup/infinisheet.git",
|
|
11
|
+
"directory": "packages/event-sourced-spreadsheet-data"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/TheCandidStartup/infinisheet/issues"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/TheCandidStartup/infinisheet/blob/main/packages/event-sourced-spreadsheet-data/README.md",
|
|
17
|
+
"keywords": [
|
|
18
|
+
"typescript",
|
|
19
|
+
"spreadsheet",
|
|
20
|
+
"infinisheet"
|
|
21
|
+
],
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"type": "module",
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"module": "./dist/index.js",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"import": "./dist/index.js"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"dev": "vite",
|
|
39
|
+
"clean": "rimraf {dist,temp}",
|
|
40
|
+
"prebuild": "npm run clean",
|
|
41
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
42
|
+
"build-tsc": "tsc -p tsconfig.build.json",
|
|
43
|
+
"build-rollup": "rollup -c ../rollup.config.mjs",
|
|
44
|
+
"build": "npm run build-rollup",
|
|
45
|
+
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
|
|
46
|
+
"devapi": "api-extractor run --local",
|
|
47
|
+
"prodapi": "api-extractor run",
|
|
48
|
+
"devbuild": "npm run build && npm run lint && npm run devapi",
|
|
49
|
+
"test": "vitest"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@candidstartup/infinisheet-types": "^0.11.0"
|
|
53
|
+
},
|
|
54
|
+
"gitHead": "0c72d04996502cb84f8720972bfda453e4989106"
|
|
55
|
+
}
|