@powerhousedao/reactor 6.0.0-dev.69 → 6.0.0-dev.77
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/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +19998 -62
- package/dist/src/sync/index.d.ts +1 -1
- package/dist/src/sync/index.d.ts.map +1 -1
- package/dist/src/sync/sync-manager.d.ts.map +1 -1
- package/dist/src/sync/types.d.ts +11 -0
- package/dist/src/sync/types.d.ts.map +1 -1
- package/package.json +17 -10
- package/dist/src/actions/index.js +0 -76
- package/dist/src/actions/index.js.map +0 -1
- package/dist/src/cache/buffer/ring-buffer.js +0 -69
- package/dist/src/cache/buffer/ring-buffer.js.map +0 -1
- package/dist/src/cache/collection-membership-cache.js +0 -33
- package/dist/src/cache/collection-membership-cache.js.map +0 -1
- package/dist/src/cache/document-meta-cache-types.js +0 -2
- package/dist/src/cache/document-meta-cache-types.js.map +0 -1
- package/dist/src/cache/document-meta-cache.js +0 -129
- package/dist/src/cache/document-meta-cache.js.map +0 -1
- package/dist/src/cache/index.js +0 -2
- package/dist/src/cache/index.js.map +0 -1
- package/dist/src/cache/kysely-operation-index.js +0 -345
- package/dist/src/cache/kysely-operation-index.js.map +0 -1
- package/dist/src/cache/kysely-write-cache.js +0 -411
- package/dist/src/cache/kysely-write-cache.js.map +0 -1
- package/dist/src/cache/lru/lru-tracker.js +0 -96
- package/dist/src/cache/lru/lru-tracker.js.map +0 -1
- package/dist/src/cache/operation-index-types.js +0 -4
- package/dist/src/cache/operation-index-types.js.map +0 -1
- package/dist/src/cache/write/interfaces.js +0 -2
- package/dist/src/cache/write/interfaces.js.map +0 -1
- package/dist/src/cache/write-cache-types.js +0 -2
- package/dist/src/cache/write-cache-types.js.map +0 -1
- package/dist/src/client/reactor-client.js +0 -497
- package/dist/src/client/reactor-client.js.map +0 -1
- package/dist/src/client/types.js +0 -14
- package/dist/src/client/types.js.map +0 -1
- package/dist/src/core/reactor-builder.js +0 -306
- package/dist/src/core/reactor-builder.js.map +0 -1
- package/dist/src/core/reactor-client-builder.js +0 -132
- package/dist/src/core/reactor-client-builder.js.map +0 -1
- package/dist/src/core/reactor.js +0 -640
- package/dist/src/core/reactor.js.map +0 -1
- package/dist/src/core/types.js +0 -2
- package/dist/src/core/types.js.map +0 -1
- package/dist/src/core/utils.js +0 -225
- package/dist/src/core/utils.js.map +0 -1
- package/dist/src/events/event-bus.js +0 -53
- package/dist/src/events/event-bus.js.map +0 -1
- package/dist/src/events/interfaces.js +0 -2
- package/dist/src/events/interfaces.js.map +0 -1
- package/dist/src/events/types.js +0 -30
- package/dist/src/events/types.js.map +0 -1
- package/dist/src/executor/document-action-handler.js +0 -356
- package/dist/src/executor/document-action-handler.js.map +0 -1
- package/dist/src/executor/interfaces.js +0 -2
- package/dist/src/executor/interfaces.js.map +0 -1
- package/dist/src/executor/signature-verifier.js +0 -70
- package/dist/src/executor/signature-verifier.js.map +0 -1
- package/dist/src/executor/simple-job-executor-manager.js +0 -345
- package/dist/src/executor/simple-job-executor-manager.js.map +0 -1
- package/dist/src/executor/simple-job-executor.js +0 -423
- package/dist/src/executor/simple-job-executor.js.map +0 -1
- package/dist/src/executor/types.js +0 -11
- package/dist/src/executor/types.js.map +0 -1
- package/dist/src/executor/util.js +0 -230
- package/dist/src/executor/util.js.map +0 -1
- package/dist/src/index.js.map +0 -1
- package/dist/src/job-tracker/in-memory-job-tracker.js +0 -114
- package/dist/src/job-tracker/in-memory-job-tracker.js.map +0 -1
- package/dist/src/job-tracker/index.js +0 -2
- package/dist/src/job-tracker/index.js.map +0 -1
- package/dist/src/job-tracker/interfaces.js +0 -2
- package/dist/src/job-tracker/interfaces.js.map +0 -1
- package/dist/src/logging/console.js +0 -2
- package/dist/src/logging/console.js.map +0 -1
- package/dist/src/logging/types.js +0 -2
- package/dist/src/logging/types.js.map +0 -1
- package/dist/src/processors/index.js +0 -2
- package/dist/src/processors/index.js.map +0 -1
- package/dist/src/processors/processor-manager.js +0 -165
- package/dist/src/processors/processor-manager.js.map +0 -1
- package/dist/src/processors/relational/types.js +0 -2
- package/dist/src/processors/relational/types.js.map +0 -1
- package/dist/src/processors/relational/utils.js +0 -2
- package/dist/src/processors/relational/utils.js.map +0 -1
- package/dist/src/processors/utils.js +0 -59
- package/dist/src/processors/utils.js.map +0 -1
- package/dist/src/queue/interfaces.js +0 -2
- package/dist/src/queue/interfaces.js.map +0 -1
- package/dist/src/queue/job-execution-handle.js +0 -71
- package/dist/src/queue/job-execution-handle.js.map +0 -1
- package/dist/src/queue/queue.js +0 -493
- package/dist/src/queue/queue.js.map +0 -1
- package/dist/src/queue/types.js +0 -19
- package/dist/src/queue/types.js.map +0 -1
- package/dist/src/read-models/base-read-model.js +0 -143
- package/dist/src/read-models/base-read-model.js.map +0 -1
- package/dist/src/read-models/coordinator.js +0 -72
- package/dist/src/read-models/coordinator.js.map +0 -1
- package/dist/src/read-models/document-view.js +0 -457
- package/dist/src/read-models/document-view.js.map +0 -1
- package/dist/src/read-models/interfaces.js +0 -2
- package/dist/src/read-models/interfaces.js.map +0 -1
- package/dist/src/read-models/types.js +0 -2
- package/dist/src/read-models/types.js.map +0 -1
- package/dist/src/registry/document-model-resolver.js +0 -81
- package/dist/src/registry/document-model-resolver.js.map +0 -1
- package/dist/src/registry/implementation.js +0 -226
- package/dist/src/registry/implementation.js.map +0 -1
- package/dist/src/registry/index.js +0 -3
- package/dist/src/registry/index.js.map +0 -1
- package/dist/src/registry/interfaces.js +0 -2
- package/dist/src/registry/interfaces.js.map +0 -1
- package/dist/src/shared/awaiter.js +0 -123
- package/dist/src/shared/awaiter.js.map +0 -1
- package/dist/src/shared/collect-all-pages.js +0 -17
- package/dist/src/shared/collect-all-pages.js.map +0 -1
- package/dist/src/shared/consistency-tracker.js +0 -123
- package/dist/src/shared/consistency-tracker.js.map +0 -1
- package/dist/src/shared/drive-url.js +0 -17
- package/dist/src/shared/drive-url.js.map +0 -1
- package/dist/src/shared/errors.js +0 -93
- package/dist/src/shared/errors.js.map +0 -1
- package/dist/src/shared/factories.js +0 -41
- package/dist/src/shared/factories.js.map +0 -1
- package/dist/src/shared/types.js +0 -38
- package/dist/src/shared/types.js.map +0 -1
- package/dist/src/shared/utils.js +0 -8
- package/dist/src/shared/utils.js.map +0 -1
- package/dist/src/signer/passthrough-signer.js +0 -17
- package/dist/src/signer/passthrough-signer.js.map +0 -1
- package/dist/src/signer/types.js +0 -2
- package/dist/src/signer/types.js.map +0 -1
- package/dist/src/storage/index.js +0 -3
- package/dist/src/storage/index.js.map +0 -1
- package/dist/src/storage/interfaces.js +0 -29
- package/dist/src/storage/interfaces.js.map +0 -1
- package/dist/src/storage/kysely/document-indexer.js +0 -421
- package/dist/src/storage/kysely/document-indexer.js.map +0 -1
- package/dist/src/storage/kysely/keyframe-store.js +0 -64
- package/dist/src/storage/kysely/keyframe-store.js.map +0 -1
- package/dist/src/storage/kysely/store.js +0 -264
- package/dist/src/storage/kysely/store.js.map +0 -1
- package/dist/src/storage/kysely/sync-cursor-storage.js +0 -97
- package/dist/src/storage/kysely/sync-cursor-storage.js.map +0 -1
- package/dist/src/storage/kysely/sync-dead-letter-storage.js +0 -110
- package/dist/src/storage/kysely/sync-dead-letter-storage.js.map +0 -1
- package/dist/src/storage/kysely/sync-remote-storage.js +0 -133
- package/dist/src/storage/kysely/sync-remote-storage.js.map +0 -1
- package/dist/src/storage/kysely/types.js +0 -2
- package/dist/src/storage/kysely/types.js.map +0 -1
- package/dist/src/storage/migrations/001_create_operation_table.js +0 -41
- package/dist/src/storage/migrations/001_create_operation_table.js.map +0 -1
- package/dist/src/storage/migrations/002_create_keyframe_table.js +0 -27
- package/dist/src/storage/migrations/002_create_keyframe_table.js.map +0 -1
- package/dist/src/storage/migrations/003_create_document_table.js +0 -10
- package/dist/src/storage/migrations/003_create_document_table.js.map +0 -1
- package/dist/src/storage/migrations/004_create_document_relationship_table.js +0 -35
- package/dist/src/storage/migrations/004_create_document_relationship_table.js.map +0 -1
- package/dist/src/storage/migrations/005_create_indexer_state_table.js +0 -10
- package/dist/src/storage/migrations/005_create_indexer_state_table.js.map +0 -1
- package/dist/src/storage/migrations/006_create_document_snapshot_table.js +0 -49
- package/dist/src/storage/migrations/006_create_document_snapshot_table.js.map +0 -1
- package/dist/src/storage/migrations/007_create_slug_mapping_table.js +0 -24
- package/dist/src/storage/migrations/007_create_slug_mapping_table.js.map +0 -1
- package/dist/src/storage/migrations/008_create_view_state_table.js +0 -10
- package/dist/src/storage/migrations/008_create_view_state_table.js.map +0 -1
- package/dist/src/storage/migrations/009_create_operation_index_tables.js +0 -50
- package/dist/src/storage/migrations/009_create_operation_index_tables.js.map +0 -1
- package/dist/src/storage/migrations/010_create_sync_tables.js +0 -43
- package/dist/src/storage/migrations/010_create_sync_tables.js.map +0 -1
- package/dist/src/storage/migrations/011_add_cursor_type_column.js +0 -29
- package/dist/src/storage/migrations/011_add_cursor_type_column.js.map +0 -1
- package/dist/src/storage/migrations/012_add_source_remote_column.js +0 -7
- package/dist/src/storage/migrations/012_add_source_remote_column.js.map +0 -1
- package/dist/src/storage/migrations/013_create_sync_dead_letters_table.js +0 -24
- package/dist/src/storage/migrations/013_create_sync_dead_letters_table.js.map +0 -1
- package/dist/src/storage/migrations/index.js +0 -3
- package/dist/src/storage/migrations/index.js.map +0 -1
- package/dist/src/storage/migrations/migrator.js +0 -84
- package/dist/src/storage/migrations/migrator.js.map +0 -1
- package/dist/src/storage/migrations/run-migrations.js +0 -58
- package/dist/src/storage/migrations/run-migrations.js.map +0 -1
- package/dist/src/storage/migrations/types.js +0 -2
- package/dist/src/storage/migrations/types.js.map +0 -1
- package/dist/src/storage/txn.js +0 -42
- package/dist/src/storage/txn.js.map +0 -1
- package/dist/src/subs/default-error-handler.js +0 -27
- package/dist/src/subs/default-error-handler.js.map +0 -1
- package/dist/src/subs/react-subscription-manager.js +0 -185
- package/dist/src/subs/react-subscription-manager.js.map +0 -1
- package/dist/src/subs/subscription-notification-read-model.js +0 -62
- package/dist/src/subs/subscription-notification-read-model.js.map +0 -1
- package/dist/src/subs/types.js +0 -2
- package/dist/src/subs/types.js.map +0 -1
- package/dist/src/sync/batch-aggregator.js +0 -94
- package/dist/src/sync/batch-aggregator.js.map +0 -1
- package/dist/src/sync/buffered-mailbox.js +0 -164
- package/dist/src/sync/buffered-mailbox.js.map +0 -1
- package/dist/src/sync/channels/gql-req-channel.js +0 -548
- package/dist/src/sync/channels/gql-req-channel.js.map +0 -1
- package/dist/src/sync/channels/gql-request-channel-factory.js +0 -105
- package/dist/src/sync/channels/gql-request-channel-factory.js.map +0 -1
- package/dist/src/sync/channels/gql-res-channel.js +0 -79
- package/dist/src/sync/channels/gql-res-channel.js.map +0 -1
- package/dist/src/sync/channels/gql-response-channel-factory.js +0 -14
- package/dist/src/sync/channels/gql-response-channel-factory.js.map +0 -1
- package/dist/src/sync/channels/index.js +0 -8
- package/dist/src/sync/channels/index.js.map +0 -1
- package/dist/src/sync/channels/interval-poll-timer.js +0 -123
- package/dist/src/sync/channels/interval-poll-timer.js.map +0 -1
- package/dist/src/sync/channels/poll-timer.js +0 -2
- package/dist/src/sync/channels/poll-timer.js.map +0 -1
- package/dist/src/sync/channels/utils.js +0 -161
- package/dist/src/sync/channels/utils.js.map +0 -1
- package/dist/src/sync/errors.js +0 -17
- package/dist/src/sync/errors.js.map +0 -1
- package/dist/src/sync/index.js +0 -11
- package/dist/src/sync/index.js.map +0 -1
- package/dist/src/sync/interfaces.js +0 -2
- package/dist/src/sync/interfaces.js.map +0 -1
- package/dist/src/sync/mailbox.js +0 -142
- package/dist/src/sync/mailbox.js.map +0 -1
- package/dist/src/sync/sync-awaiter.js +0 -124
- package/dist/src/sync/sync-awaiter.js.map +0 -1
- package/dist/src/sync/sync-builder.js +0 -52
- package/dist/src/sync/sync-builder.js.map +0 -1
- package/dist/src/sync/sync-manager.js +0 -447
- package/dist/src/sync/sync-manager.js.map +0 -1
- package/dist/src/sync/sync-operation.js +0 -70
- package/dist/src/sync/sync-operation.js.map +0 -1
- package/dist/src/sync/sync-status-tracker.js +0 -137
- package/dist/src/sync/sync-status-tracker.js.map +0 -1
- package/dist/src/sync/types.js +0 -31
- package/dist/src/sync/types.js.map +0 -1
- package/dist/src/sync/utils.js +0 -283
- package/dist/src/sync/utils.js.map +0 -1
- package/dist/src/utils/reshuffle.js +0 -91
- package/dist/src/utils/reshuffle.js.map +0 -1
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Base class for read models that provides catch-up/rewind functionality.
|
|
3
|
-
* Handles initialization, state tracking via ViewState table, and consistency tracking.
|
|
4
|
-
* Subclasses should override indexOperations() with their specific indexing logic.
|
|
5
|
-
*/
|
|
6
|
-
export class BaseReadModel {
|
|
7
|
-
db;
|
|
8
|
-
operationIndex;
|
|
9
|
-
writeCache;
|
|
10
|
-
consistencyTracker;
|
|
11
|
-
readModelId;
|
|
12
|
-
lastOrdinal = 0;
|
|
13
|
-
constructor(db, operationIndex, writeCache, consistencyTracker, readModelId) {
|
|
14
|
-
this.db = db;
|
|
15
|
-
this.operationIndex = operationIndex;
|
|
16
|
-
this.writeCache = writeCache;
|
|
17
|
-
this.consistencyTracker = consistencyTracker;
|
|
18
|
-
this.readModelId = readModelId;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Initializes the read model by loading state and catching up on missed operations.
|
|
22
|
-
*/
|
|
23
|
-
async init() {
|
|
24
|
-
const viewState = await this.loadState();
|
|
25
|
-
if (viewState !== undefined) {
|
|
26
|
-
this.lastOrdinal = viewState;
|
|
27
|
-
const missedOperations = await this.operationIndex.getSinceOrdinal(this.lastOrdinal);
|
|
28
|
-
if (missedOperations.results.length > 0) {
|
|
29
|
-
const opsWithState = await this.rebuildStateForOperations(missedOperations.results);
|
|
30
|
-
await this.indexOperations(opsWithState);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
await this.initializeState();
|
|
35
|
-
const allOperations = await this.operationIndex.getSinceOrdinal(0);
|
|
36
|
-
if (allOperations.results.length > 0) {
|
|
37
|
-
const opsWithState = await this.rebuildStateForOperations(allOperations.results);
|
|
38
|
-
await this.indexOperations(opsWithState);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Indexes operations into the read model.
|
|
44
|
-
* Subclasses should override this method to implement their specific indexing logic.
|
|
45
|
-
* The overriding method should call saveState() and updateConsistencyTracker() at the end.
|
|
46
|
-
*/
|
|
47
|
-
async indexOperations(items) {
|
|
48
|
-
if (items.length === 0)
|
|
49
|
-
return;
|
|
50
|
-
await this.db.transaction().execute(async (trx) => {
|
|
51
|
-
await this.saveState(trx, items);
|
|
52
|
-
});
|
|
53
|
-
this.updateConsistencyTracker(items);
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Waits for the read model to reach the specified consistency level.
|
|
57
|
-
*/
|
|
58
|
-
async waitForConsistency(token, timeoutMs, signal) {
|
|
59
|
-
if (token.coordinates.length === 0) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
await this.consistencyTracker.waitFor(token.coordinates, timeoutMs, signal);
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Rebuilds document state for each operation using the write cache.
|
|
66
|
-
*/
|
|
67
|
-
async rebuildStateForOperations(operations) {
|
|
68
|
-
const result = [];
|
|
69
|
-
for (const op of operations) {
|
|
70
|
-
const { documentId, scope, branch } = op.context;
|
|
71
|
-
const targetRevision = op.operation.index;
|
|
72
|
-
const document = await this.writeCache.getState(documentId, scope, branch, targetRevision);
|
|
73
|
-
result.push({
|
|
74
|
-
operation: op.operation,
|
|
75
|
-
context: {
|
|
76
|
-
...op.context,
|
|
77
|
-
resultingState: JSON.stringify(document),
|
|
78
|
-
},
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
return result;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Loads the last processed ordinal from the ViewState table.
|
|
85
|
-
* Returns undefined if no state exists for this read model.
|
|
86
|
-
*/
|
|
87
|
-
async loadState() {
|
|
88
|
-
const viewStateDb = this.db;
|
|
89
|
-
const row = await viewStateDb
|
|
90
|
-
.selectFrom("ViewState")
|
|
91
|
-
.select("lastOrdinal")
|
|
92
|
-
.where("readModelId", "=", this.readModelId)
|
|
93
|
-
.executeTakeFirst();
|
|
94
|
-
return row?.lastOrdinal;
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Initializes the ViewState row for this read model.
|
|
98
|
-
*/
|
|
99
|
-
async initializeState() {
|
|
100
|
-
const viewStateDb = this.db;
|
|
101
|
-
await viewStateDb
|
|
102
|
-
.insertInto("ViewState")
|
|
103
|
-
.values({
|
|
104
|
-
readModelId: this.readModelId,
|
|
105
|
-
lastOrdinal: 0,
|
|
106
|
-
})
|
|
107
|
-
.execute();
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Saves the last processed ordinal to the ViewState table.
|
|
111
|
-
* Should be called at the end of indexOperations() within a transaction.
|
|
112
|
-
*/
|
|
113
|
-
async saveState(trx, items) {
|
|
114
|
-
const maxOrdinal = Math.max(...items.map((item) => item.context.ordinal));
|
|
115
|
-
this.lastOrdinal = maxOrdinal;
|
|
116
|
-
await trx
|
|
117
|
-
.updateTable("ViewState")
|
|
118
|
-
.set({
|
|
119
|
-
lastOrdinal: maxOrdinal,
|
|
120
|
-
lastOperationTimestamp: new Date(),
|
|
121
|
-
})
|
|
122
|
-
.where("readModelId", "=", this.readModelId)
|
|
123
|
-
.execute();
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Updates the consistency tracker with the processed operations.
|
|
127
|
-
* Should be called at the end of indexOperations() after the transaction commits.
|
|
128
|
-
*/
|
|
129
|
-
updateConsistencyTracker(items) {
|
|
130
|
-
const coordinates = [];
|
|
131
|
-
for (let i = 0; i < items.length; i++) {
|
|
132
|
-
const item = items[i];
|
|
133
|
-
coordinates.push({
|
|
134
|
-
documentId: item.context.documentId,
|
|
135
|
-
scope: item.context.scope,
|
|
136
|
-
branch: item.context.branch,
|
|
137
|
-
operationIndex: item.operation.index,
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
this.consistencyTracker.update(coordinates);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
//# sourceMappingURL=base-read-model.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"base-read-model.js","sourceRoot":"","sources":["../../../src/read-models/base-read-model.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { ReactorEventTypes, } from "../events/types.js";
|
|
2
|
-
/**
|
|
3
|
-
* Coordinates read model synchronization by listening to operation write events
|
|
4
|
-
* and updating all registered read models in parallel.
|
|
5
|
-
*
|
|
6
|
-
* This coordinator is responsible for:
|
|
7
|
-
* - Subscribing to OPERATION_WRITTEN events from the event bus
|
|
8
|
-
* - Distributing operation updates to all registered read models
|
|
9
|
-
* - Managing the lifecycle of read model subscriptions
|
|
10
|
-
*
|
|
11
|
-
* Read models are updated asynchronously and in parallel to avoid blocking
|
|
12
|
-
* the write path. Errors in read model updates are propagated through the
|
|
13
|
-
* event bus but do not affect the write operation success.
|
|
14
|
-
*/
|
|
15
|
-
export class ReadModelCoordinator {
|
|
16
|
-
eventBus;
|
|
17
|
-
preReady;
|
|
18
|
-
postReady;
|
|
19
|
-
unsubscribe;
|
|
20
|
-
isRunning = false;
|
|
21
|
-
constructor(eventBus, preReady, postReady) {
|
|
22
|
-
this.eventBus = eventBus;
|
|
23
|
-
this.preReady = preReady;
|
|
24
|
-
this.postReady = postReady;
|
|
25
|
-
//
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Start listening for operation events and updating read models.
|
|
29
|
-
* Can be called multiple times safely (subsequent calls are no-ops).
|
|
30
|
-
*/
|
|
31
|
-
start() {
|
|
32
|
-
if (this.isRunning) {
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
// Subscribe to WRITE_READY events
|
|
36
|
-
this.unsubscribe = this.eventBus.subscribe(ReactorEventTypes.JOB_WRITE_READY, async (type, event) => {
|
|
37
|
-
await this.handleWriteReady(event);
|
|
38
|
-
});
|
|
39
|
-
this.isRunning = true;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Stop listening and clean up subscriptions.
|
|
43
|
-
* Can be called multiple times safely (subsequent calls are no-ops).
|
|
44
|
-
*/
|
|
45
|
-
stop() {
|
|
46
|
-
if (!this.isRunning) {
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
if (this.unsubscribe) {
|
|
50
|
-
this.unsubscribe();
|
|
51
|
-
this.unsubscribe = undefined;
|
|
52
|
-
}
|
|
53
|
-
this.isRunning = false;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Handle write ready events by updating all read models in parallel.
|
|
57
|
-
* Errors from individual read models are collected and re-thrown as an aggregate.
|
|
58
|
-
*/
|
|
59
|
-
async handleWriteReady(event) {
|
|
60
|
-
// Index into pre-ready read models in parallel
|
|
61
|
-
await Promise.all(this.preReady.map((readModel) => readModel.indexOperations(event.operations)));
|
|
62
|
-
// Emit READ_READY event after all pre-ready read models have completed
|
|
63
|
-
const readyEvent = {
|
|
64
|
-
jobId: event.jobId,
|
|
65
|
-
operations: event.operations,
|
|
66
|
-
};
|
|
67
|
-
await this.eventBus.emit(ReactorEventTypes.JOB_READ_READY, readyEvent);
|
|
68
|
-
// Process post-ready read models (e.g., subscription notifications)
|
|
69
|
-
await Promise.all(this.postReady.map((readModel) => readModel.indexOperations(event.operations)));
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
//# sourceMappingURL=coordinator.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"coordinator.js","sourceRoot":"","sources":["../../../src/read-models/coordinator.ts"],"names":[],"mappings":"AACA,OAAO,EACL,iBAAiB,GAIlB,MAAM,oBAAoB,CAAC;AAG5B;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,oBAAoB;IAKrB;IACQ;IACA;IANV,WAAW,CAAe;IAC1B,SAAS,GAAG,KAAK,CAAC;IAE1B,YACU,QAAmB,EACX,QAAsB,EACtB,SAAuB;QAF/B,aAAQ,GAAR,QAAQ,CAAW;QACX,aAAQ,GAAR,QAAQ,CAAc;QACtB,cAAS,GAAT,SAAS,CAAc;QAEvC,EAAE;IACJ,CAAC;IAED;;;OAGG;IACH,KAAK;QACH,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,OAAO;QACT,CAAC;QAED,kCAAkC;QAClC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CACxC,iBAAiB,CAAC,eAAe,EACjC,KAAK,EAAE,IAAI,EAAE,KAAyB,EAAE,EAAE;YACxC,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC,CACF,CAAC;QAEF,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IACxB,CAAC;IAED;;;OAGG;IACH,IAAI;QACF,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,OAAO;QACT,CAAC;QAED,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;QAC/B,CAAC;QAED,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;IACzB,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,gBAAgB,CAAC,KAAyB;QACtD,+CAA+C;QAC/C,MAAM,OAAO,CAAC,GAAG,CACf,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAC9B,SAAS,CAAC,eAAe,CAAC,KAAK,CAAC,UAAU,CAAC,CAC5C,CACF,CAAC;QAEF,uEAAuE;QACvE,MAAM,UAAU,GAAsB;YACpC,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,UAAU,EAAE,KAAK,CAAC,UAAU;SAC7B,CAAC;QACF,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QAEvE,oEAAoE;QACpE,MAAM,OAAO,CAAC,GAAG,CACf,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAC/B,SAAS,CAAC,eAAe,CAAC,KAAK,CAAC,UAAU,CAAC,CAC5C,CACF,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -1,457 +0,0 @@
|
|
|
1
|
-
import { v4 as uuidv4 } from "uuid";
|
|
2
|
-
import { BaseReadModel } from "./base-read-model.js";
|
|
3
|
-
export class KyselyDocumentView extends BaseReadModel {
|
|
4
|
-
operationStore;
|
|
5
|
-
_db;
|
|
6
|
-
constructor(db, operationStore, operationIndex, writeCache, consistencyTracker) {
|
|
7
|
-
super(db, operationIndex, writeCache, consistencyTracker, "document-view");
|
|
8
|
-
this.operationStore = operationStore;
|
|
9
|
-
this._db = db;
|
|
10
|
-
}
|
|
11
|
-
async indexOperations(items) {
|
|
12
|
-
if (items.length === 0)
|
|
13
|
-
return;
|
|
14
|
-
await this._db.transaction().execute(async (trx) => {
|
|
15
|
-
for (const item of items) {
|
|
16
|
-
const { operation, context } = item;
|
|
17
|
-
const { documentId, scope, branch, documentType, resultingState } = context;
|
|
18
|
-
const { index, hash } = operation;
|
|
19
|
-
if (!resultingState) {
|
|
20
|
-
throw new Error(`Missing resultingState in context for operation ${operation.id || "unknown"}. ` +
|
|
21
|
-
`IDocumentView requires resultingState from upstream - it does not rebuild documents.`);
|
|
22
|
-
}
|
|
23
|
-
let fullState = {};
|
|
24
|
-
try {
|
|
25
|
-
fullState = JSON.parse(resultingState);
|
|
26
|
-
}
|
|
27
|
-
catch (error) {
|
|
28
|
-
throw new Error(`Failed to parse resultingState for operation ${operation.id || "unknown"}: ${error instanceof Error ? error.message : String(error)}`);
|
|
29
|
-
}
|
|
30
|
-
const operationType = operation.action.type;
|
|
31
|
-
let scopesToIndex;
|
|
32
|
-
if (operationType === "CREATE_DOCUMENT") {
|
|
33
|
-
scopesToIndex = Object.entries(fullState).filter(([key]) => key === "header" || key === "document" || key === "auth");
|
|
34
|
-
}
|
|
35
|
-
else if (operationType === "UPGRADE_DOCUMENT") {
|
|
36
|
-
const scopeStatesToIndex = [];
|
|
37
|
-
for (const [scopeName, scopeState] of Object.entries(fullState)) {
|
|
38
|
-
if (scopeName === "header") {
|
|
39
|
-
scopeStatesToIndex.push([scopeName, scopeState]);
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
if (scopeName === scope) {
|
|
43
|
-
scopeStatesToIndex.push([scopeName, scopeState]);
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
const existingSnapshot = await trx
|
|
47
|
-
.selectFrom("DocumentSnapshot")
|
|
48
|
-
.select("scope")
|
|
49
|
-
.where("documentId", "=", documentId)
|
|
50
|
-
.where("scope", "=", scopeName)
|
|
51
|
-
.where("branch", "=", branch)
|
|
52
|
-
.executeTakeFirst();
|
|
53
|
-
if (!existingSnapshot) {
|
|
54
|
-
scopeStatesToIndex.push([scopeName, scopeState]);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
scopesToIndex = scopeStatesToIndex;
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
scopesToIndex = [];
|
|
61
|
-
if (fullState.header !== undefined) {
|
|
62
|
-
scopesToIndex.push(["header", fullState.header]);
|
|
63
|
-
}
|
|
64
|
-
if (fullState[scope] !== undefined) {
|
|
65
|
-
scopesToIndex.push([scope, fullState[scope]]);
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
scopesToIndex.push([scope, {}]);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
for (const [scopeName, scopeState] of scopesToIndex) {
|
|
72
|
-
const existingSnapshot = await trx
|
|
73
|
-
.selectFrom("DocumentSnapshot")
|
|
74
|
-
.selectAll()
|
|
75
|
-
.where("documentId", "=", documentId)
|
|
76
|
-
.where("scope", "=", scopeName)
|
|
77
|
-
.where("branch", "=", branch)
|
|
78
|
-
.executeTakeFirst();
|
|
79
|
-
const newState = typeof scopeState === "object" && scopeState !== null
|
|
80
|
-
? scopeState
|
|
81
|
-
: {};
|
|
82
|
-
let slug = existingSnapshot?.slug ?? null;
|
|
83
|
-
let name = existingSnapshot?.name ?? null;
|
|
84
|
-
if (scopeName === "header") {
|
|
85
|
-
const headerSlug = newState.slug;
|
|
86
|
-
const headerName = newState.name;
|
|
87
|
-
if (typeof headerSlug === "string") {
|
|
88
|
-
slug = headerSlug;
|
|
89
|
-
}
|
|
90
|
-
if (typeof headerName === "string") {
|
|
91
|
-
name = headerName;
|
|
92
|
-
}
|
|
93
|
-
if (slug && slug !== documentId) {
|
|
94
|
-
await trx
|
|
95
|
-
.insertInto("SlugMapping")
|
|
96
|
-
.values({
|
|
97
|
-
slug,
|
|
98
|
-
documentId,
|
|
99
|
-
scope: scopeName,
|
|
100
|
-
branch,
|
|
101
|
-
})
|
|
102
|
-
.onConflict((oc) => oc.column("slug").doUpdateSet({
|
|
103
|
-
documentId,
|
|
104
|
-
scope: scopeName,
|
|
105
|
-
branch,
|
|
106
|
-
}))
|
|
107
|
-
.execute();
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
if (existingSnapshot) {
|
|
111
|
-
await trx
|
|
112
|
-
.updateTable("DocumentSnapshot")
|
|
113
|
-
.set({
|
|
114
|
-
lastOperationIndex: index,
|
|
115
|
-
lastOperationHash: hash,
|
|
116
|
-
lastUpdatedAt: new Date(),
|
|
117
|
-
snapshotVersion: existingSnapshot.snapshotVersion + 1,
|
|
118
|
-
content: newState,
|
|
119
|
-
slug,
|
|
120
|
-
name,
|
|
121
|
-
})
|
|
122
|
-
.where("documentId", "=", documentId)
|
|
123
|
-
.where("scope", "=", scopeName)
|
|
124
|
-
.where("branch", "=", branch)
|
|
125
|
-
.execute();
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
128
|
-
const snapshot = {
|
|
129
|
-
id: uuidv4(),
|
|
130
|
-
documentId,
|
|
131
|
-
slug,
|
|
132
|
-
name,
|
|
133
|
-
scope: scopeName,
|
|
134
|
-
branch,
|
|
135
|
-
content: newState,
|
|
136
|
-
documentType,
|
|
137
|
-
lastOperationIndex: index,
|
|
138
|
-
lastOperationHash: hash,
|
|
139
|
-
identifiers: null,
|
|
140
|
-
metadata: null,
|
|
141
|
-
deletedAt: null,
|
|
142
|
-
};
|
|
143
|
-
await trx.insertInto("DocumentSnapshot").values(snapshot).execute();
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
await this.saveState(trx, items);
|
|
148
|
-
});
|
|
149
|
-
this.updateConsistencyTracker(items);
|
|
150
|
-
}
|
|
151
|
-
async exists(documentIds, consistencyToken, signal) {
|
|
152
|
-
if (consistencyToken) {
|
|
153
|
-
await this.waitForConsistency(consistencyToken, undefined, signal);
|
|
154
|
-
}
|
|
155
|
-
if (signal?.aborted) {
|
|
156
|
-
throw new Error("Operation aborted");
|
|
157
|
-
}
|
|
158
|
-
if (documentIds.length === 0) {
|
|
159
|
-
return [];
|
|
160
|
-
}
|
|
161
|
-
const snapshots = await this._db
|
|
162
|
-
.selectFrom("DocumentSnapshot")
|
|
163
|
-
.select(["documentId"])
|
|
164
|
-
.where("documentId", "in", documentIds)
|
|
165
|
-
.where("isDeleted", "=", false)
|
|
166
|
-
.distinct()
|
|
167
|
-
.execute();
|
|
168
|
-
const existingIds = new Set(snapshots.map((s) => s.documentId));
|
|
169
|
-
return documentIds.map((id) => existingIds.has(id));
|
|
170
|
-
}
|
|
171
|
-
async get(documentId, view, consistencyToken, signal) {
|
|
172
|
-
if (consistencyToken) {
|
|
173
|
-
await this.waitForConsistency(consistencyToken, undefined, signal);
|
|
174
|
-
}
|
|
175
|
-
if (signal?.aborted) {
|
|
176
|
-
throw new Error("Operation aborted");
|
|
177
|
-
}
|
|
178
|
-
const branch = view?.branch || "main";
|
|
179
|
-
let scopesToQuery;
|
|
180
|
-
if (view?.scopes && view.scopes.length > 0) {
|
|
181
|
-
scopesToQuery = [...new Set(["header", "document", ...view.scopes])];
|
|
182
|
-
}
|
|
183
|
-
else {
|
|
184
|
-
scopesToQuery = [];
|
|
185
|
-
}
|
|
186
|
-
let query = this._db
|
|
187
|
-
.selectFrom("DocumentSnapshot")
|
|
188
|
-
.selectAll()
|
|
189
|
-
.where("documentId", "=", documentId)
|
|
190
|
-
.where("branch", "=", branch)
|
|
191
|
-
.where("isDeleted", "=", false);
|
|
192
|
-
if (scopesToQuery.length > 0) {
|
|
193
|
-
query = query.where("scope", "in", scopesToQuery);
|
|
194
|
-
}
|
|
195
|
-
const snapshots = await query.execute();
|
|
196
|
-
if (snapshots.length === 0) {
|
|
197
|
-
throw new Error(`Document not found: ${documentId}`);
|
|
198
|
-
}
|
|
199
|
-
if (signal?.aborted) {
|
|
200
|
-
throw new Error("Operation aborted");
|
|
201
|
-
}
|
|
202
|
-
const headerSnapshot = snapshots.find((s) => s.scope === "header");
|
|
203
|
-
if (!headerSnapshot) {
|
|
204
|
-
throw new Error(`Document header not found: ${documentId}`);
|
|
205
|
-
}
|
|
206
|
-
const header = headerSnapshot.content;
|
|
207
|
-
const revisions = await this.operationStore.getRevisions(documentId, branch, signal);
|
|
208
|
-
header.revision = revisions.revision;
|
|
209
|
-
header.lastModifiedAtUtcIso = revisions.latestTimestamp;
|
|
210
|
-
const state = {};
|
|
211
|
-
for (const snapshot of snapshots) {
|
|
212
|
-
if (snapshot.scope === "header") {
|
|
213
|
-
continue;
|
|
214
|
-
}
|
|
215
|
-
state[snapshot.scope] = snapshot.content;
|
|
216
|
-
}
|
|
217
|
-
const document = {
|
|
218
|
-
header,
|
|
219
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
220
|
-
state: state,
|
|
221
|
-
operations: {},
|
|
222
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
223
|
-
initialState: state,
|
|
224
|
-
clipboard: [],
|
|
225
|
-
};
|
|
226
|
-
return document;
|
|
227
|
-
}
|
|
228
|
-
async getMany(documentIds, view, consistencyToken, signal) {
|
|
229
|
-
if (documentIds.length === 0) {
|
|
230
|
-
return [];
|
|
231
|
-
}
|
|
232
|
-
if (consistencyToken) {
|
|
233
|
-
await this.waitForConsistency(consistencyToken, undefined, signal);
|
|
234
|
-
}
|
|
235
|
-
if (signal?.aborted) {
|
|
236
|
-
throw new Error("Operation aborted");
|
|
237
|
-
}
|
|
238
|
-
const branch = view?.branch || "main";
|
|
239
|
-
let scopesToQuery;
|
|
240
|
-
if (view?.scopes && view.scopes.length > 0) {
|
|
241
|
-
scopesToQuery = [...new Set(["header", "document", ...view.scopes])];
|
|
242
|
-
}
|
|
243
|
-
else {
|
|
244
|
-
scopesToQuery = [];
|
|
245
|
-
}
|
|
246
|
-
let query = this._db
|
|
247
|
-
.selectFrom("DocumentSnapshot")
|
|
248
|
-
.selectAll()
|
|
249
|
-
.where("documentId", "in", documentIds)
|
|
250
|
-
.where("branch", "=", branch)
|
|
251
|
-
.where("isDeleted", "=", false);
|
|
252
|
-
if (scopesToQuery.length > 0) {
|
|
253
|
-
query = query.where("scope", "in", scopesToQuery);
|
|
254
|
-
}
|
|
255
|
-
const allSnapshots = await query.execute();
|
|
256
|
-
if (signal?.aborted) {
|
|
257
|
-
throw new Error("Operation aborted");
|
|
258
|
-
}
|
|
259
|
-
const snapshotsByDocId = new Map();
|
|
260
|
-
for (const snapshot of allSnapshots) {
|
|
261
|
-
const existing = snapshotsByDocId.get(snapshot.documentId) || [];
|
|
262
|
-
existing.push(snapshot);
|
|
263
|
-
snapshotsByDocId.set(snapshot.documentId, existing);
|
|
264
|
-
}
|
|
265
|
-
const foundDocumentIds = [...snapshotsByDocId.keys()];
|
|
266
|
-
const revisionResults = await Promise.all(foundDocumentIds.map((docId) => this.operationStore.getRevisions(docId, branch, signal)));
|
|
267
|
-
const revisionsByDocId = new Map();
|
|
268
|
-
for (let i = 0; i < foundDocumentIds.length; i++) {
|
|
269
|
-
revisionsByDocId.set(foundDocumentIds[i], revisionResults[i]);
|
|
270
|
-
}
|
|
271
|
-
if (signal?.aborted) {
|
|
272
|
-
throw new Error("Operation aborted");
|
|
273
|
-
}
|
|
274
|
-
const documents = [];
|
|
275
|
-
for (const documentId of documentIds) {
|
|
276
|
-
const snapshots = snapshotsByDocId.get(documentId);
|
|
277
|
-
if (!snapshots || snapshots.length === 0) {
|
|
278
|
-
continue;
|
|
279
|
-
}
|
|
280
|
-
const headerSnapshot = snapshots.find((s) => s.scope === "header");
|
|
281
|
-
if (!headerSnapshot) {
|
|
282
|
-
continue;
|
|
283
|
-
}
|
|
284
|
-
const header = headerSnapshot.content;
|
|
285
|
-
const revisions = revisionsByDocId.get(documentId);
|
|
286
|
-
if (revisions) {
|
|
287
|
-
header.revision = revisions.revision;
|
|
288
|
-
header.lastModifiedAtUtcIso = revisions.latestTimestamp;
|
|
289
|
-
}
|
|
290
|
-
const state = {};
|
|
291
|
-
for (const snapshot of snapshots) {
|
|
292
|
-
if (snapshot.scope === "header") {
|
|
293
|
-
continue;
|
|
294
|
-
}
|
|
295
|
-
state[snapshot.scope] = snapshot.content;
|
|
296
|
-
}
|
|
297
|
-
const document = {
|
|
298
|
-
header,
|
|
299
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
300
|
-
state: state,
|
|
301
|
-
operations: {},
|
|
302
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
303
|
-
initialState: state,
|
|
304
|
-
clipboard: [],
|
|
305
|
-
};
|
|
306
|
-
documents.push(document);
|
|
307
|
-
}
|
|
308
|
-
return documents;
|
|
309
|
-
}
|
|
310
|
-
async getByIdOrSlug(identifier, view, consistencyToken, signal) {
|
|
311
|
-
const documentId = await this.resolveIdOrSlug(identifier, view, consistencyToken, signal);
|
|
312
|
-
return this.get(documentId, view, undefined, signal);
|
|
313
|
-
}
|
|
314
|
-
async findByType(type, view, paging, consistencyToken, signal) {
|
|
315
|
-
if (consistencyToken) {
|
|
316
|
-
await this.waitForConsistency(consistencyToken, undefined, signal);
|
|
317
|
-
}
|
|
318
|
-
if (signal?.aborted) {
|
|
319
|
-
throw new Error("Operation aborted");
|
|
320
|
-
}
|
|
321
|
-
const branch = view?.branch || "main";
|
|
322
|
-
const startIndex = paging?.cursor ? parseInt(paging.cursor) : 0;
|
|
323
|
-
const limit = paging?.limit || 100;
|
|
324
|
-
const documents = [];
|
|
325
|
-
const processedDocumentIds = new Set();
|
|
326
|
-
const allDocumentIds = [];
|
|
327
|
-
const snapshots = await this._db
|
|
328
|
-
.selectFrom("DocumentSnapshot")
|
|
329
|
-
.selectAll()
|
|
330
|
-
.where("documentType", "=", type)
|
|
331
|
-
.where("branch", "=", branch)
|
|
332
|
-
.where("isDeleted", "=", false)
|
|
333
|
-
.orderBy("lastUpdatedAt", "desc")
|
|
334
|
-
.execute();
|
|
335
|
-
if (signal?.aborted) {
|
|
336
|
-
throw new Error("Operation aborted");
|
|
337
|
-
}
|
|
338
|
-
for (const snapshot of snapshots) {
|
|
339
|
-
if (processedDocumentIds.has(snapshot.documentId)) {
|
|
340
|
-
continue;
|
|
341
|
-
}
|
|
342
|
-
processedDocumentIds.add(snapshot.documentId);
|
|
343
|
-
allDocumentIds.push(snapshot.documentId);
|
|
344
|
-
}
|
|
345
|
-
const docsToFetch = allDocumentIds.slice(startIndex, startIndex + limit);
|
|
346
|
-
for (const documentId of docsToFetch) {
|
|
347
|
-
if (signal?.aborted) {
|
|
348
|
-
throw new Error("Operation aborted");
|
|
349
|
-
}
|
|
350
|
-
try {
|
|
351
|
-
const document = await this.get(documentId, view, undefined, signal);
|
|
352
|
-
documents.push(document);
|
|
353
|
-
}
|
|
354
|
-
catch {
|
|
355
|
-
continue;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
const hasMore = allDocumentIds.length > startIndex + limit;
|
|
359
|
-
const nextCursor = hasMore ? String(startIndex + limit) : undefined;
|
|
360
|
-
return {
|
|
361
|
-
results: documents,
|
|
362
|
-
options: paging || { cursor: "0", limit: 100 },
|
|
363
|
-
nextCursor,
|
|
364
|
-
totalCount: allDocumentIds.length,
|
|
365
|
-
next: hasMore
|
|
366
|
-
? () => this.findByType(type, view, { cursor: nextCursor, limit }, consistencyToken, signal)
|
|
367
|
-
: undefined,
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
async resolveSlug(slug,
|
|
371
|
-
// TODO: this should only be branch
|
|
372
|
-
view, consistencyToken, signal) {
|
|
373
|
-
if (consistencyToken) {
|
|
374
|
-
await this.waitForConsistency(consistencyToken, undefined, signal);
|
|
375
|
-
}
|
|
376
|
-
if (signal?.aborted) {
|
|
377
|
-
throw new Error("Operation aborted");
|
|
378
|
-
}
|
|
379
|
-
const branch = view?.branch || "main";
|
|
380
|
-
const mapping = await this._db
|
|
381
|
-
.selectFrom("SlugMapping")
|
|
382
|
-
.select("documentId")
|
|
383
|
-
.where("slug", "=", slug)
|
|
384
|
-
.where("branch", "=", branch)
|
|
385
|
-
.executeTakeFirst();
|
|
386
|
-
if (!mapping) {
|
|
387
|
-
return undefined;
|
|
388
|
-
}
|
|
389
|
-
if (signal?.aborted) {
|
|
390
|
-
throw new Error("Operation aborted");
|
|
391
|
-
}
|
|
392
|
-
if (view?.scopes && view.scopes.length > 0) {
|
|
393
|
-
const scopeCheck = await this._db
|
|
394
|
-
.selectFrom("DocumentSnapshot")
|
|
395
|
-
.select("scope")
|
|
396
|
-
.where("documentId", "=", mapping.documentId)
|
|
397
|
-
.where("branch", "=", branch)
|
|
398
|
-
.where("scope", "in", view.scopes)
|
|
399
|
-
.where("isDeleted", "=", false)
|
|
400
|
-
.executeTakeFirst();
|
|
401
|
-
if (!scopeCheck) {
|
|
402
|
-
return undefined;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
return mapping.documentId;
|
|
406
|
-
}
|
|
407
|
-
// TODO: fix
|
|
408
|
-
async resolveSlugs(slugs,
|
|
409
|
-
// TODO: this should only be branch
|
|
410
|
-
view, consistencyToken, signal) {
|
|
411
|
-
const ids = await Promise.all(slugs.map((slug) => this.resolveSlug(slug, view, consistencyToken, signal)));
|
|
412
|
-
return ids.filter((id) => id !== undefined);
|
|
413
|
-
}
|
|
414
|
-
async resolveIdOrSlug(identifier,
|
|
415
|
-
// TODO: this should only be branch
|
|
416
|
-
view, consistencyToken, signal) {
|
|
417
|
-
if (consistencyToken) {
|
|
418
|
-
await this.waitForConsistency(consistencyToken, undefined, signal);
|
|
419
|
-
}
|
|
420
|
-
if (signal?.aborted) {
|
|
421
|
-
throw new Error("Operation aborted");
|
|
422
|
-
}
|
|
423
|
-
const branch = view?.branch || "main";
|
|
424
|
-
const idCheckPromise = this._db
|
|
425
|
-
.selectFrom("DocumentSnapshot")
|
|
426
|
-
.select("documentId")
|
|
427
|
-
.where("documentId", "=", identifier)
|
|
428
|
-
.where("branch", "=", branch)
|
|
429
|
-
.where("isDeleted", "=", false)
|
|
430
|
-
.executeTakeFirst();
|
|
431
|
-
const slugCheckPromise = this._db
|
|
432
|
-
.selectFrom("SlugMapping")
|
|
433
|
-
.select("documentId")
|
|
434
|
-
.where("slug", "=", identifier)
|
|
435
|
-
.where("branch", "=", branch)
|
|
436
|
-
.executeTakeFirst();
|
|
437
|
-
const [idMatch, slugMatch] = await Promise.all([
|
|
438
|
-
idCheckPromise,
|
|
439
|
-
slugCheckPromise,
|
|
440
|
-
]);
|
|
441
|
-
if (signal?.aborted) {
|
|
442
|
-
throw new Error("Operation aborted");
|
|
443
|
-
}
|
|
444
|
-
const idMatchDocId = idMatch?.documentId;
|
|
445
|
-
const slugMatchDocId = slugMatch?.documentId;
|
|
446
|
-
if (idMatchDocId && slugMatchDocId && idMatchDocId !== slugMatchDocId) {
|
|
447
|
-
throw new Error(`Ambiguous identifier "${identifier}": matches both document ID "${idMatchDocId}" and slug for document ID "${slugMatchDocId}". ` +
|
|
448
|
-
`Please use get() for ID or resolveSlug() + get() for slug to be explicit.`);
|
|
449
|
-
}
|
|
450
|
-
const resolvedDocumentId = idMatchDocId || slugMatchDocId;
|
|
451
|
-
if (!resolvedDocumentId) {
|
|
452
|
-
throw new Error(`Document not found: ${identifier}`);
|
|
453
|
-
}
|
|
454
|
-
return resolvedDocumentId;
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
//# sourceMappingURL=document-view.js.map
|