@powerhousedao/connect 6.0.0-dev.90 → 6.0.0-dev.91
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/context/index.js +112 -39
- package/dist/src/hooks/index.js +112 -39
- package/dist/src/main.js +112 -39
- package/dist/src/pages/index.js +112 -39
- package/dist/src/store/index.js +112 -39
- package/dist/src/utils/index.js +112 -39
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +12 -12
|
@@ -48598,6 +48598,7 @@ class GqlRequestChannel {
|
|
|
48598
48598
|
cursorStorage;
|
|
48599
48599
|
operationIndex;
|
|
48600
48600
|
pollTimer;
|
|
48601
|
+
abortController = new AbortController;
|
|
48601
48602
|
isShutdown;
|
|
48602
48603
|
failureCount;
|
|
48603
48604
|
lastSuccessUtcMs;
|
|
@@ -48678,6 +48679,7 @@ class GqlRequestChannel {
|
|
|
48678
48679
|
});
|
|
48679
48680
|
}
|
|
48680
48681
|
shutdown() {
|
|
48682
|
+
this.abortController.abort();
|
|
48681
48683
|
this.bufferedOutbox.flush();
|
|
48682
48684
|
this.isShutdown = true;
|
|
48683
48685
|
this.pollTimer.stop();
|
|
@@ -48705,7 +48707,7 @@ class GqlRequestChannel {
|
|
|
48705
48707
|
};
|
|
48706
48708
|
}
|
|
48707
48709
|
async init() {
|
|
48708
|
-
await this.touchRemoteChannel();
|
|
48710
|
+
const { ackOrdinal } = await this.touchRemoteChannel();
|
|
48709
48711
|
const cursors = await this.cursorStorage.list(this.remoteName);
|
|
48710
48712
|
const inboxOrdinal = cursors.find((c3) => c3.cursorType === "inbox")?.cursorOrdinal ?? 0;
|
|
48711
48713
|
const outboxOrdinal = cursors.find((c3) => c3.cursorType === "outbox")?.cursorOrdinal ?? 0;
|
|
@@ -48713,6 +48715,9 @@ class GqlRequestChannel {
|
|
|
48713
48715
|
this.outbox.init(outboxOrdinal);
|
|
48714
48716
|
this.lastPersistedInboxOrdinal = inboxOrdinal;
|
|
48715
48717
|
this.lastPersistedOutboxOrdinal = outboxOrdinal;
|
|
48718
|
+
if (ackOrdinal > 0) {
|
|
48719
|
+
trimMailboxFromAckOrdinal(this.outbox, ackOrdinal);
|
|
48720
|
+
}
|
|
48716
48721
|
this.pollTimer.setDelegate(() => this.poll());
|
|
48717
48722
|
this.pollTimer.start();
|
|
48718
48723
|
this.transitionConnectionState("connected");
|
|
@@ -48782,34 +48787,57 @@ class GqlRequestChannel {
|
|
|
48782
48787
|
this.deadLetter.add(...syncOps);
|
|
48783
48788
|
}
|
|
48784
48789
|
handlePollError(error3) {
|
|
48790
|
+
if (this.isShutdown)
|
|
48791
|
+
return true;
|
|
48785
48792
|
const err = error3 instanceof Error ? error3 : new Error(String(error3));
|
|
48786
48793
|
if (err.message.includes("Channel not found")) {
|
|
48787
48794
|
this.transitionConnectionState("reconnecting");
|
|
48788
48795
|
this.recoverFromChannelNotFound();
|
|
48789
48796
|
return true;
|
|
48790
48797
|
}
|
|
48798
|
+
const classification = this.classifyError(err);
|
|
48791
48799
|
this.failureCount++;
|
|
48792
48800
|
this.lastFailureUtcMs = Date.now();
|
|
48793
|
-
this.transitionConnectionState("error");
|
|
48794
48801
|
const channelError = new ChannelError("inbox", err);
|
|
48795
|
-
this.logger.error("GqlChannel poll error (@FailureCount): @Error", this.failureCount, channelError);
|
|
48802
|
+
this.logger.error("GqlChannel poll error (@FailureCount, @Classification): @Error", this.failureCount, classification, channelError);
|
|
48803
|
+
if (classification === "unrecoverable") {
|
|
48804
|
+
this.pollTimer.stop();
|
|
48805
|
+
this.transitionConnectionState("error");
|
|
48806
|
+
return true;
|
|
48807
|
+
}
|
|
48808
|
+
this.transitionConnectionState("error");
|
|
48796
48809
|
return false;
|
|
48797
48810
|
}
|
|
48798
48811
|
recoverFromChannelNotFound() {
|
|
48799
48812
|
this.logger.info("GqlChannel @ChannelId not found on remote, re-registering...", this.channelId);
|
|
48800
48813
|
this.pollTimer.stop();
|
|
48801
|
-
|
|
48802
|
-
|
|
48803
|
-
|
|
48804
|
-
this.
|
|
48805
|
-
|
|
48806
|
-
|
|
48807
|
-
|
|
48808
|
-
|
|
48809
|
-
|
|
48810
|
-
|
|
48811
|
-
|
|
48812
|
-
|
|
48814
|
+
const attemptRecovery = (attempt) => {
|
|
48815
|
+
if (this.isShutdown)
|
|
48816
|
+
return;
|
|
48817
|
+
this.touchRemoteChannel().then(({ ackOrdinal }) => {
|
|
48818
|
+
this.logger.info("GqlChannel @ChannelId re-registered successfully", this.channelId);
|
|
48819
|
+
this.failureCount = 0;
|
|
48820
|
+
if (ackOrdinal > 0) {
|
|
48821
|
+
trimMailboxFromAckOrdinal(this.outbox, ackOrdinal);
|
|
48822
|
+
}
|
|
48823
|
+
this.pollTimer.start();
|
|
48824
|
+
this.transitionConnectionState("connected");
|
|
48825
|
+
}).catch((recoveryError) => {
|
|
48826
|
+
const err = recoveryError instanceof Error ? recoveryError : new Error(String(recoveryError));
|
|
48827
|
+
const classification = this.classifyError(err);
|
|
48828
|
+
this.logger.error("GqlChannel @ChannelId recovery attempt @Attempt failed (@Classification): @Error", this.channelId, attempt, classification, recoveryError);
|
|
48829
|
+
this.failureCount++;
|
|
48830
|
+
this.lastFailureUtcMs = Date.now();
|
|
48831
|
+
if (classification === "unrecoverable") {
|
|
48832
|
+
this.transitionConnectionState("error");
|
|
48833
|
+
return;
|
|
48834
|
+
}
|
|
48835
|
+
this.transitionConnectionState("reconnecting");
|
|
48836
|
+
const delay = calculateBackoffDelay(attempt, this.config.retryBaseDelayMs, this.config.retryMaxDelayMs, Math.random());
|
|
48837
|
+
setTimeout(() => attemptRecovery(attempt + 1), delay);
|
|
48838
|
+
});
|
|
48839
|
+
};
|
|
48840
|
+
attemptRecovery(1);
|
|
48813
48841
|
}
|
|
48814
48842
|
async pollSyncEnvelopes(ackOrdinal, latestOrdinal) {
|
|
48815
48843
|
const query = `
|
|
@@ -48907,7 +48935,10 @@ class GqlRequestChannel {
|
|
|
48907
48935
|
} catch {}
|
|
48908
48936
|
const mutation = `
|
|
48909
48937
|
mutation TouchChannel($input: TouchChannelInput!) {
|
|
48910
|
-
touchChannel(input: $input)
|
|
48938
|
+
touchChannel(input: $input) {
|
|
48939
|
+
success
|
|
48940
|
+
ackOrdinal
|
|
48941
|
+
}
|
|
48911
48942
|
}
|
|
48912
48943
|
`;
|
|
48913
48944
|
const variables = {
|
|
@@ -48923,7 +48954,11 @@ class GqlRequestChannel {
|
|
|
48923
48954
|
sinceTimestampUtcMs
|
|
48924
48955
|
}
|
|
48925
48956
|
};
|
|
48926
|
-
await this.executeGraphQL(mutation, variables);
|
|
48957
|
+
const data = await this.executeGraphQL(mutation, variables);
|
|
48958
|
+
if (!data.touchChannel.success) {
|
|
48959
|
+
throw new GraphQLRequestError("touchChannel returned success=false", "graphql");
|
|
48960
|
+
}
|
|
48961
|
+
return { ackOrdinal: data.touchChannel.ackOrdinal };
|
|
48927
48962
|
}
|
|
48928
48963
|
attemptPush(syncOps) {
|
|
48929
48964
|
this.pushSyncOperations(syncOps).then(() => {
|
|
@@ -48933,8 +48968,11 @@ class GqlRequestChannel {
|
|
|
48933
48968
|
this.transitionConnectionState("connected");
|
|
48934
48969
|
}
|
|
48935
48970
|
}).catch((error3) => {
|
|
48971
|
+
if (this.isShutdown)
|
|
48972
|
+
return;
|
|
48936
48973
|
const err = error3 instanceof Error ? error3 : new Error(String(error3));
|
|
48937
|
-
|
|
48974
|
+
const classification = this.classifyError(err);
|
|
48975
|
+
if (classification === "recoverable") {
|
|
48938
48976
|
this.pushFailureCount++;
|
|
48939
48977
|
this.pushBlocked = true;
|
|
48940
48978
|
this.logger.error("GqlChannel push failed (attempt @FailureCount), will retry: @Error", this.pushFailureCount, err);
|
|
@@ -48968,12 +49006,26 @@ class GqlRequestChannel {
|
|
|
48968
49006
|
this.attemptPush([...allItems]);
|
|
48969
49007
|
}, delay);
|
|
48970
49008
|
}
|
|
48971
|
-
|
|
48972
|
-
if (error3
|
|
48973
|
-
return
|
|
48974
|
-
|
|
48975
|
-
|
|
48976
|
-
|
|
49009
|
+
classifyError(error3) {
|
|
49010
|
+
if (!(error3 instanceof GraphQLRequestError)) {
|
|
49011
|
+
return "recoverable";
|
|
49012
|
+
}
|
|
49013
|
+
switch (error3.category) {
|
|
49014
|
+
case "network":
|
|
49015
|
+
return "recoverable";
|
|
49016
|
+
case "http": {
|
|
49017
|
+
if (error3.statusCode !== undefined && error3.statusCode >= 500) {
|
|
49018
|
+
return "recoverable";
|
|
49019
|
+
}
|
|
49020
|
+
return "unrecoverable";
|
|
49021
|
+
}
|
|
49022
|
+
case "parse":
|
|
49023
|
+
return "recoverable";
|
|
49024
|
+
case "graphql":
|
|
49025
|
+
return "unrecoverable";
|
|
49026
|
+
case "missing-data":
|
|
49027
|
+
return "unrecoverable";
|
|
49028
|
+
}
|
|
48977
49029
|
}
|
|
48978
49030
|
async pushSyncOperations(syncOps) {
|
|
48979
49031
|
for (const syncOp of syncOps) {
|
|
@@ -49050,26 +49102,27 @@ class GqlRequestChannel {
|
|
|
49050
49102
|
body: JSON.stringify({
|
|
49051
49103
|
query,
|
|
49052
49104
|
variables
|
|
49053
|
-
})
|
|
49105
|
+
}),
|
|
49106
|
+
signal: this.abortController.signal
|
|
49054
49107
|
});
|
|
49055
49108
|
} catch (error3) {
|
|
49056
|
-
throw new
|
|
49109
|
+
throw new GraphQLRequestError(`GraphQL request failed: ${error3 instanceof Error ? error3.message : String(error3)}`, "network");
|
|
49057
49110
|
}
|
|
49058
49111
|
if (!response.ok) {
|
|
49059
|
-
throw new
|
|
49112
|
+
throw new GraphQLRequestError(`GraphQL request failed: ${response.status} ${response.statusText}`, "http", response.status);
|
|
49060
49113
|
}
|
|
49061
49114
|
let result;
|
|
49062
49115
|
try {
|
|
49063
49116
|
result = await response.json();
|
|
49064
49117
|
} catch (error3) {
|
|
49065
|
-
throw new
|
|
49118
|
+
throw new GraphQLRequestError(`Failed to parse GraphQL response: ${error3 instanceof Error ? error3.message : String(error3)}`, "parse");
|
|
49066
49119
|
}
|
|
49067
49120
|
this.logger.verbose("GQL response @channelId @operation status=@status data=@data errors=@errors", this.channelId, operationName, response.status, JSON.stringify(result.data), result.errors ? JSON.stringify(result.errors) : "none");
|
|
49068
49121
|
if (result.errors) {
|
|
49069
|
-
throw new
|
|
49122
|
+
throw new GraphQLRequestError(`GraphQL errors: ${JSON.stringify(result.errors, null, 2)}`, "graphql");
|
|
49070
49123
|
}
|
|
49071
49124
|
if (!result.data) {
|
|
49072
|
-
throw new
|
|
49125
|
+
throw new GraphQLRequestError("GraphQL response missing data field", "missing-data");
|
|
49073
49126
|
}
|
|
49074
49127
|
return result.data;
|
|
49075
49128
|
}
|
|
@@ -49989,6 +50042,7 @@ class SyncManager {
|
|
|
49989
50042
|
remotes;
|
|
49990
50043
|
awaiter;
|
|
49991
50044
|
syncAwaiter;
|
|
50045
|
+
abortController = new AbortController;
|
|
49992
50046
|
isShutdown;
|
|
49993
50047
|
eventUnsubscribe;
|
|
49994
50048
|
failedEventUnsubscribe;
|
|
@@ -50049,6 +50103,7 @@ class SyncManager {
|
|
|
50049
50103
|
}
|
|
50050
50104
|
shutdown() {
|
|
50051
50105
|
this.isShutdown = true;
|
|
50106
|
+
this.abortController.abort();
|
|
50052
50107
|
this.batchAggregator.clear();
|
|
50053
50108
|
if (this.eventUnsubscribe) {
|
|
50054
50109
|
this.eventUnsubscribe();
|
|
@@ -50241,6 +50296,8 @@ class SyncManager {
|
|
|
50241
50296
|
return Array.from(this.remotes.values()).filter((remote) => remote.collectionId === collectionId);
|
|
50242
50297
|
}
|
|
50243
50298
|
async processCompleteBatch(batch) {
|
|
50299
|
+
if (this.isShutdown)
|
|
50300
|
+
return;
|
|
50244
50301
|
const collectionIds = [
|
|
50245
50302
|
...new Set(Object.values(batch.collectionMemberships).flatMap((collections) => collections))
|
|
50246
50303
|
];
|
|
@@ -50284,8 +50341,10 @@ class SyncManager {
|
|
|
50284
50341
|
const operations = syncOp.operations.map((op) => op.operation);
|
|
50285
50342
|
let jobInfo;
|
|
50286
50343
|
try {
|
|
50287
|
-
jobInfo = await this.reactor.load(syncOp.documentId, syncOp.branch, operations,
|
|
50344
|
+
jobInfo = await this.reactor.load(syncOp.documentId, syncOp.branch, operations, this.abortController.signal, { sourceRemote: remote.name });
|
|
50288
50345
|
} catch (error3) {
|
|
50346
|
+
if (this.isShutdown)
|
|
50347
|
+
return;
|
|
50289
50348
|
const err = error3 instanceof Error ? error3 : new Error(String(error3));
|
|
50290
50349
|
this.logger.error("Failed to load operations from inbox (@remote, @documentId, @error)", remote.name, syncOp.documentId, err.message);
|
|
50291
50350
|
const channelError = new ChannelError("inbox", err);
|
|
@@ -50296,8 +50355,10 @@ class SyncManager {
|
|
|
50296
50355
|
}
|
|
50297
50356
|
let completedJobInfo;
|
|
50298
50357
|
try {
|
|
50299
|
-
completedJobInfo = await this.awaiter.waitForJob(jobInfo.id);
|
|
50358
|
+
completedJobInfo = await this.awaiter.waitForJob(jobInfo.id, this.abortController.signal);
|
|
50300
50359
|
} catch (error3) {
|
|
50360
|
+
if (this.isShutdown)
|
|
50361
|
+
return;
|
|
50301
50362
|
const err = error3 instanceof Error ? error3 : new Error(String(error3));
|
|
50302
50363
|
this.logger.error("Failed to wait for job completion (@remote, @documentId, @jobId, @error)", remote.name, syncOp.documentId, jobInfo.id, err.message);
|
|
50303
50364
|
const channelError = new ChannelError("inbox", err);
|
|
@@ -50332,10 +50393,10 @@ class SyncManager {
|
|
|
50332
50393
|
const request = { jobs };
|
|
50333
50394
|
let result;
|
|
50334
50395
|
try {
|
|
50335
|
-
result = await this.reactor.loadBatch(request,
|
|
50336
|
-
sourceRemote
|
|
50337
|
-
});
|
|
50396
|
+
result = await this.reactor.loadBatch(request, this.abortController.signal, { sourceRemote });
|
|
50338
50397
|
} catch (error3) {
|
|
50398
|
+
if (this.isShutdown)
|
|
50399
|
+
return;
|
|
50339
50400
|
for (const { remote, syncOp } of items) {
|
|
50340
50401
|
const err = error3 instanceof Error ? error3 : new Error(String(error3));
|
|
50341
50402
|
syncOp.failed(new ChannelError("inbox", err));
|
|
@@ -50356,8 +50417,10 @@ class SyncManager {
|
|
|
50356
50417
|
const jobInfo = result.jobs[syncOp.jobId];
|
|
50357
50418
|
let completedJobInfo;
|
|
50358
50419
|
try {
|
|
50359
|
-
completedJobInfo = await this.awaiter.waitForJob(jobInfo.id);
|
|
50420
|
+
completedJobInfo = await this.awaiter.waitForJob(jobInfo.id, this.abortController.signal);
|
|
50360
50421
|
} catch (error3) {
|
|
50422
|
+
if (this.isShutdown)
|
|
50423
|
+
continue;
|
|
50361
50424
|
const err = error3 instanceof Error ? error3 : new Error(String(error3));
|
|
50362
50425
|
syncOp.failed(new ChannelError("inbox", err));
|
|
50363
50426
|
remote.channel.deadLetter.add(syncOp);
|
|
@@ -50404,7 +50467,7 @@ class SyncManager {
|
|
|
50404
50467
|
remote.channel.outbox.add(...syncOps);
|
|
50405
50468
|
}
|
|
50406
50469
|
async getOperationsForRemote(remote, ackOrdinal) {
|
|
50407
|
-
const results = await this.operationIndex.find(remote.collectionId, ackOrdinal, { excludeSourceRemote: remote.name });
|
|
50470
|
+
const results = await this.operationIndex.find(remote.collectionId, ackOrdinal, { excludeSourceRemote: remote.name }, undefined, this.abortController.signal);
|
|
50408
50471
|
let operations = results.results.map((entry) => toOperationWithContext(entry));
|
|
50409
50472
|
const sinceTimestamp = remote.options.sinceTimestampUtcMs;
|
|
50410
50473
|
if (sinceTimestamp && sinceTimestamp !== "0") {
|
|
@@ -51727,7 +51790,7 @@ var __defProp2, __export2 = (target, all) => {
|
|
|
51727
51790
|
}
|
|
51728
51791
|
}, DocumentDeletedError, InvalidSignatureError, DowngradeNotSupportedError, DocumentNotFoundError, getNextIndexForScope = (document2, scope) => {
|
|
51729
51792
|
return document2.header.revision[scope] || 0;
|
|
51730
|
-
}, EventBusAggregateError, ReactorEventTypes, QueueEventTypes, ModuleNotFoundError, DuplicateModuleError, DuplicateManifestError, ManifestNotFoundError, DowngradeNotSupportedError2, MissingUpgradeTransitionError, InvalidUpgradeStepError, STRICT_ORDER_ACTION_TYPES, MAX_SKIP_THRESHOLD = 1000, documentScopeActions, DRIVE_DOCUMENT_TYPE = "powerhouse/document-drive", ProcessorManager, KyselyDocumentView, KyselyDocumentIndexer, DuplicateOperationError, RevisionMismatchError, exports_001_create_operation_table, exports_002_create_keyframe_table, exports_003_create_document_table, exports_004_create_document_relationship_table, exports_005_create_indexer_state_table, exports_006_create_document_snapshot_table, exports_007_create_slug_mapping_table, exports_008_create_view_state_table, exports_009_create_operation_index_tables, exports_010_create_sync_tables, exports_011_add_cursor_type_column, exports_012_add_source_remote_column, exports_013_create_sync_dead_letters_table, exports_014_create_processor_cursor_table, REACTOR_SCHEMA = "reactor", migrations, ChannelScheme, SyncOperationStatus, ChannelErrorSource, SyncEventTypes, MailboxAggregateError, ChannelError, SyncOperationAggregateError, DEFAULT_CONFIG, syncOpCounter = 0, getLatestAppliedOrdinal = (syncOps) => {
|
|
51793
|
+
}, EventBusAggregateError, ReactorEventTypes, QueueEventTypes, ModuleNotFoundError, DuplicateModuleError, DuplicateManifestError, ManifestNotFoundError, DowngradeNotSupportedError2, MissingUpgradeTransitionError, InvalidUpgradeStepError, STRICT_ORDER_ACTION_TYPES, MAX_SKIP_THRESHOLD = 1000, documentScopeActions, DRIVE_DOCUMENT_TYPE = "powerhouse/document-drive", ProcessorManager, KyselyDocumentView, KyselyDocumentIndexer, DuplicateOperationError, RevisionMismatchError, exports_001_create_operation_table, exports_002_create_keyframe_table, exports_003_create_document_table, exports_004_create_document_relationship_table, exports_005_create_indexer_state_table, exports_006_create_document_snapshot_table, exports_007_create_slug_mapping_table, exports_008_create_view_state_table, exports_009_create_operation_index_tables, exports_010_create_sync_tables, exports_011_add_cursor_type_column, exports_012_add_source_remote_column, exports_013_create_sync_dead_letters_table, exports_014_create_processor_cursor_table, REACTOR_SCHEMA = "reactor", migrations, ChannelScheme, SyncOperationStatus, ChannelErrorSource, SyncEventTypes, MailboxAggregateError, GraphQLRequestError, ChannelError, SyncOperationAggregateError, DEFAULT_CONFIG, syncOpCounter = 0, getLatestAppliedOrdinal = (syncOps) => {
|
|
51731
51794
|
let maxOrdinal = 0;
|
|
51732
51795
|
for (const syncOp of syncOps) {
|
|
51733
51796
|
if (syncOp.status === 2) {
|
|
@@ -56935,6 +56998,16 @@ var init_src = __esm(() => {
|
|
|
56935
56998
|
this.errors = errors2;
|
|
56936
56999
|
}
|
|
56937
57000
|
};
|
|
57001
|
+
GraphQLRequestError = class GraphQLRequestError extends Error {
|
|
57002
|
+
statusCode;
|
|
57003
|
+
category;
|
|
57004
|
+
constructor(message, category, statusCode) {
|
|
57005
|
+
super(message);
|
|
57006
|
+
this.name = "GraphQLRequestError";
|
|
57007
|
+
this.category = category;
|
|
57008
|
+
this.statusCode = statusCode;
|
|
57009
|
+
}
|
|
57010
|
+
};
|
|
56938
57011
|
ChannelError = class ChannelError extends Error {
|
|
56939
57012
|
source;
|
|
56940
57013
|
error;
|
|
@@ -93773,7 +93846,7 @@ var init_package = __esm(() => {
|
|
|
93773
93846
|
package_default = {
|
|
93774
93847
|
name: "@powerhousedao/connect",
|
|
93775
93848
|
productName: "Powerhouse-Connect",
|
|
93776
|
-
version: "6.0.0-dev.
|
|
93849
|
+
version: "6.0.0-dev.90",
|
|
93777
93850
|
description: "Powerhouse Connect",
|
|
93778
93851
|
main: "dist/index.html",
|
|
93779
93852
|
type: "module",
|
package/dist/src/hooks/index.js
CHANGED
|
@@ -48598,6 +48598,7 @@ class GqlRequestChannel {
|
|
|
48598
48598
|
cursorStorage;
|
|
48599
48599
|
operationIndex;
|
|
48600
48600
|
pollTimer;
|
|
48601
|
+
abortController = new AbortController;
|
|
48601
48602
|
isShutdown;
|
|
48602
48603
|
failureCount;
|
|
48603
48604
|
lastSuccessUtcMs;
|
|
@@ -48678,6 +48679,7 @@ class GqlRequestChannel {
|
|
|
48678
48679
|
});
|
|
48679
48680
|
}
|
|
48680
48681
|
shutdown() {
|
|
48682
|
+
this.abortController.abort();
|
|
48681
48683
|
this.bufferedOutbox.flush();
|
|
48682
48684
|
this.isShutdown = true;
|
|
48683
48685
|
this.pollTimer.stop();
|
|
@@ -48705,7 +48707,7 @@ class GqlRequestChannel {
|
|
|
48705
48707
|
};
|
|
48706
48708
|
}
|
|
48707
48709
|
async init() {
|
|
48708
|
-
await this.touchRemoteChannel();
|
|
48710
|
+
const { ackOrdinal } = await this.touchRemoteChannel();
|
|
48709
48711
|
const cursors = await this.cursorStorage.list(this.remoteName);
|
|
48710
48712
|
const inboxOrdinal = cursors.find((c3) => c3.cursorType === "inbox")?.cursorOrdinal ?? 0;
|
|
48711
48713
|
const outboxOrdinal = cursors.find((c3) => c3.cursorType === "outbox")?.cursorOrdinal ?? 0;
|
|
@@ -48713,6 +48715,9 @@ class GqlRequestChannel {
|
|
|
48713
48715
|
this.outbox.init(outboxOrdinal);
|
|
48714
48716
|
this.lastPersistedInboxOrdinal = inboxOrdinal;
|
|
48715
48717
|
this.lastPersistedOutboxOrdinal = outboxOrdinal;
|
|
48718
|
+
if (ackOrdinal > 0) {
|
|
48719
|
+
trimMailboxFromAckOrdinal(this.outbox, ackOrdinal);
|
|
48720
|
+
}
|
|
48716
48721
|
this.pollTimer.setDelegate(() => this.poll());
|
|
48717
48722
|
this.pollTimer.start();
|
|
48718
48723
|
this.transitionConnectionState("connected");
|
|
@@ -48782,34 +48787,57 @@ class GqlRequestChannel {
|
|
|
48782
48787
|
this.deadLetter.add(...syncOps);
|
|
48783
48788
|
}
|
|
48784
48789
|
handlePollError(error3) {
|
|
48790
|
+
if (this.isShutdown)
|
|
48791
|
+
return true;
|
|
48785
48792
|
const err = error3 instanceof Error ? error3 : new Error(String(error3));
|
|
48786
48793
|
if (err.message.includes("Channel not found")) {
|
|
48787
48794
|
this.transitionConnectionState("reconnecting");
|
|
48788
48795
|
this.recoverFromChannelNotFound();
|
|
48789
48796
|
return true;
|
|
48790
48797
|
}
|
|
48798
|
+
const classification = this.classifyError(err);
|
|
48791
48799
|
this.failureCount++;
|
|
48792
48800
|
this.lastFailureUtcMs = Date.now();
|
|
48793
|
-
this.transitionConnectionState("error");
|
|
48794
48801
|
const channelError = new ChannelError("inbox", err);
|
|
48795
|
-
this.logger.error("GqlChannel poll error (@FailureCount): @Error", this.failureCount, channelError);
|
|
48802
|
+
this.logger.error("GqlChannel poll error (@FailureCount, @Classification): @Error", this.failureCount, classification, channelError);
|
|
48803
|
+
if (classification === "unrecoverable") {
|
|
48804
|
+
this.pollTimer.stop();
|
|
48805
|
+
this.transitionConnectionState("error");
|
|
48806
|
+
return true;
|
|
48807
|
+
}
|
|
48808
|
+
this.transitionConnectionState("error");
|
|
48796
48809
|
return false;
|
|
48797
48810
|
}
|
|
48798
48811
|
recoverFromChannelNotFound() {
|
|
48799
48812
|
this.logger.info("GqlChannel @ChannelId not found on remote, re-registering...", this.channelId);
|
|
48800
48813
|
this.pollTimer.stop();
|
|
48801
|
-
|
|
48802
|
-
|
|
48803
|
-
|
|
48804
|
-
this.
|
|
48805
|
-
|
|
48806
|
-
|
|
48807
|
-
|
|
48808
|
-
|
|
48809
|
-
|
|
48810
|
-
|
|
48811
|
-
|
|
48812
|
-
|
|
48814
|
+
const attemptRecovery = (attempt) => {
|
|
48815
|
+
if (this.isShutdown)
|
|
48816
|
+
return;
|
|
48817
|
+
this.touchRemoteChannel().then(({ ackOrdinal }) => {
|
|
48818
|
+
this.logger.info("GqlChannel @ChannelId re-registered successfully", this.channelId);
|
|
48819
|
+
this.failureCount = 0;
|
|
48820
|
+
if (ackOrdinal > 0) {
|
|
48821
|
+
trimMailboxFromAckOrdinal(this.outbox, ackOrdinal);
|
|
48822
|
+
}
|
|
48823
|
+
this.pollTimer.start();
|
|
48824
|
+
this.transitionConnectionState("connected");
|
|
48825
|
+
}).catch((recoveryError) => {
|
|
48826
|
+
const err = recoveryError instanceof Error ? recoveryError : new Error(String(recoveryError));
|
|
48827
|
+
const classification = this.classifyError(err);
|
|
48828
|
+
this.logger.error("GqlChannel @ChannelId recovery attempt @Attempt failed (@Classification): @Error", this.channelId, attempt, classification, recoveryError);
|
|
48829
|
+
this.failureCount++;
|
|
48830
|
+
this.lastFailureUtcMs = Date.now();
|
|
48831
|
+
if (classification === "unrecoverable") {
|
|
48832
|
+
this.transitionConnectionState("error");
|
|
48833
|
+
return;
|
|
48834
|
+
}
|
|
48835
|
+
this.transitionConnectionState("reconnecting");
|
|
48836
|
+
const delay = calculateBackoffDelay(attempt, this.config.retryBaseDelayMs, this.config.retryMaxDelayMs, Math.random());
|
|
48837
|
+
setTimeout(() => attemptRecovery(attempt + 1), delay);
|
|
48838
|
+
});
|
|
48839
|
+
};
|
|
48840
|
+
attemptRecovery(1);
|
|
48813
48841
|
}
|
|
48814
48842
|
async pollSyncEnvelopes(ackOrdinal, latestOrdinal) {
|
|
48815
48843
|
const query = `
|
|
@@ -48907,7 +48935,10 @@ class GqlRequestChannel {
|
|
|
48907
48935
|
} catch {}
|
|
48908
48936
|
const mutation = `
|
|
48909
48937
|
mutation TouchChannel($input: TouchChannelInput!) {
|
|
48910
|
-
touchChannel(input: $input)
|
|
48938
|
+
touchChannel(input: $input) {
|
|
48939
|
+
success
|
|
48940
|
+
ackOrdinal
|
|
48941
|
+
}
|
|
48911
48942
|
}
|
|
48912
48943
|
`;
|
|
48913
48944
|
const variables = {
|
|
@@ -48923,7 +48954,11 @@ class GqlRequestChannel {
|
|
|
48923
48954
|
sinceTimestampUtcMs
|
|
48924
48955
|
}
|
|
48925
48956
|
};
|
|
48926
|
-
await this.executeGraphQL(mutation, variables);
|
|
48957
|
+
const data = await this.executeGraphQL(mutation, variables);
|
|
48958
|
+
if (!data.touchChannel.success) {
|
|
48959
|
+
throw new GraphQLRequestError("touchChannel returned success=false", "graphql");
|
|
48960
|
+
}
|
|
48961
|
+
return { ackOrdinal: data.touchChannel.ackOrdinal };
|
|
48927
48962
|
}
|
|
48928
48963
|
attemptPush(syncOps) {
|
|
48929
48964
|
this.pushSyncOperations(syncOps).then(() => {
|
|
@@ -48933,8 +48968,11 @@ class GqlRequestChannel {
|
|
|
48933
48968
|
this.transitionConnectionState("connected");
|
|
48934
48969
|
}
|
|
48935
48970
|
}).catch((error3) => {
|
|
48971
|
+
if (this.isShutdown)
|
|
48972
|
+
return;
|
|
48936
48973
|
const err = error3 instanceof Error ? error3 : new Error(String(error3));
|
|
48937
|
-
|
|
48974
|
+
const classification = this.classifyError(err);
|
|
48975
|
+
if (classification === "recoverable") {
|
|
48938
48976
|
this.pushFailureCount++;
|
|
48939
48977
|
this.pushBlocked = true;
|
|
48940
48978
|
this.logger.error("GqlChannel push failed (attempt @FailureCount), will retry: @Error", this.pushFailureCount, err);
|
|
@@ -48968,12 +49006,26 @@ class GqlRequestChannel {
|
|
|
48968
49006
|
this.attemptPush([...allItems]);
|
|
48969
49007
|
}, delay);
|
|
48970
49008
|
}
|
|
48971
|
-
|
|
48972
|
-
if (error3
|
|
48973
|
-
return
|
|
48974
|
-
|
|
48975
|
-
|
|
48976
|
-
|
|
49009
|
+
classifyError(error3) {
|
|
49010
|
+
if (!(error3 instanceof GraphQLRequestError)) {
|
|
49011
|
+
return "recoverable";
|
|
49012
|
+
}
|
|
49013
|
+
switch (error3.category) {
|
|
49014
|
+
case "network":
|
|
49015
|
+
return "recoverable";
|
|
49016
|
+
case "http": {
|
|
49017
|
+
if (error3.statusCode !== undefined && error3.statusCode >= 500) {
|
|
49018
|
+
return "recoverable";
|
|
49019
|
+
}
|
|
49020
|
+
return "unrecoverable";
|
|
49021
|
+
}
|
|
49022
|
+
case "parse":
|
|
49023
|
+
return "recoverable";
|
|
49024
|
+
case "graphql":
|
|
49025
|
+
return "unrecoverable";
|
|
49026
|
+
case "missing-data":
|
|
49027
|
+
return "unrecoverable";
|
|
49028
|
+
}
|
|
48977
49029
|
}
|
|
48978
49030
|
async pushSyncOperations(syncOps) {
|
|
48979
49031
|
for (const syncOp of syncOps) {
|
|
@@ -49050,26 +49102,27 @@ class GqlRequestChannel {
|
|
|
49050
49102
|
body: JSON.stringify({
|
|
49051
49103
|
query,
|
|
49052
49104
|
variables
|
|
49053
|
-
})
|
|
49105
|
+
}),
|
|
49106
|
+
signal: this.abortController.signal
|
|
49054
49107
|
});
|
|
49055
49108
|
} catch (error3) {
|
|
49056
|
-
throw new
|
|
49109
|
+
throw new GraphQLRequestError(`GraphQL request failed: ${error3 instanceof Error ? error3.message : String(error3)}`, "network");
|
|
49057
49110
|
}
|
|
49058
49111
|
if (!response.ok) {
|
|
49059
|
-
throw new
|
|
49112
|
+
throw new GraphQLRequestError(`GraphQL request failed: ${response.status} ${response.statusText}`, "http", response.status);
|
|
49060
49113
|
}
|
|
49061
49114
|
let result;
|
|
49062
49115
|
try {
|
|
49063
49116
|
result = await response.json();
|
|
49064
49117
|
} catch (error3) {
|
|
49065
|
-
throw new
|
|
49118
|
+
throw new GraphQLRequestError(`Failed to parse GraphQL response: ${error3 instanceof Error ? error3.message : String(error3)}`, "parse");
|
|
49066
49119
|
}
|
|
49067
49120
|
this.logger.verbose("GQL response @channelId @operation status=@status data=@data errors=@errors", this.channelId, operationName, response.status, JSON.stringify(result.data), result.errors ? JSON.stringify(result.errors) : "none");
|
|
49068
49121
|
if (result.errors) {
|
|
49069
|
-
throw new
|
|
49122
|
+
throw new GraphQLRequestError(`GraphQL errors: ${JSON.stringify(result.errors, null, 2)}`, "graphql");
|
|
49070
49123
|
}
|
|
49071
49124
|
if (!result.data) {
|
|
49072
|
-
throw new
|
|
49125
|
+
throw new GraphQLRequestError("GraphQL response missing data field", "missing-data");
|
|
49073
49126
|
}
|
|
49074
49127
|
return result.data;
|
|
49075
49128
|
}
|
|
@@ -49989,6 +50042,7 @@ class SyncManager {
|
|
|
49989
50042
|
remotes;
|
|
49990
50043
|
awaiter;
|
|
49991
50044
|
syncAwaiter;
|
|
50045
|
+
abortController = new AbortController;
|
|
49992
50046
|
isShutdown;
|
|
49993
50047
|
eventUnsubscribe;
|
|
49994
50048
|
failedEventUnsubscribe;
|
|
@@ -50049,6 +50103,7 @@ class SyncManager {
|
|
|
50049
50103
|
}
|
|
50050
50104
|
shutdown() {
|
|
50051
50105
|
this.isShutdown = true;
|
|
50106
|
+
this.abortController.abort();
|
|
50052
50107
|
this.batchAggregator.clear();
|
|
50053
50108
|
if (this.eventUnsubscribe) {
|
|
50054
50109
|
this.eventUnsubscribe();
|
|
@@ -50241,6 +50296,8 @@ class SyncManager {
|
|
|
50241
50296
|
return Array.from(this.remotes.values()).filter((remote) => remote.collectionId === collectionId);
|
|
50242
50297
|
}
|
|
50243
50298
|
async processCompleteBatch(batch) {
|
|
50299
|
+
if (this.isShutdown)
|
|
50300
|
+
return;
|
|
50244
50301
|
const collectionIds = [
|
|
50245
50302
|
...new Set(Object.values(batch.collectionMemberships).flatMap((collections) => collections))
|
|
50246
50303
|
];
|
|
@@ -50284,8 +50341,10 @@ class SyncManager {
|
|
|
50284
50341
|
const operations = syncOp.operations.map((op) => op.operation);
|
|
50285
50342
|
let jobInfo;
|
|
50286
50343
|
try {
|
|
50287
|
-
jobInfo = await this.reactor.load(syncOp.documentId, syncOp.branch, operations,
|
|
50344
|
+
jobInfo = await this.reactor.load(syncOp.documentId, syncOp.branch, operations, this.abortController.signal, { sourceRemote: remote.name });
|
|
50288
50345
|
} catch (error3) {
|
|
50346
|
+
if (this.isShutdown)
|
|
50347
|
+
return;
|
|
50289
50348
|
const err = error3 instanceof Error ? error3 : new Error(String(error3));
|
|
50290
50349
|
this.logger.error("Failed to load operations from inbox (@remote, @documentId, @error)", remote.name, syncOp.documentId, err.message);
|
|
50291
50350
|
const channelError = new ChannelError("inbox", err);
|
|
@@ -50296,8 +50355,10 @@ class SyncManager {
|
|
|
50296
50355
|
}
|
|
50297
50356
|
let completedJobInfo;
|
|
50298
50357
|
try {
|
|
50299
|
-
completedJobInfo = await this.awaiter.waitForJob(jobInfo.id);
|
|
50358
|
+
completedJobInfo = await this.awaiter.waitForJob(jobInfo.id, this.abortController.signal);
|
|
50300
50359
|
} catch (error3) {
|
|
50360
|
+
if (this.isShutdown)
|
|
50361
|
+
return;
|
|
50301
50362
|
const err = error3 instanceof Error ? error3 : new Error(String(error3));
|
|
50302
50363
|
this.logger.error("Failed to wait for job completion (@remote, @documentId, @jobId, @error)", remote.name, syncOp.documentId, jobInfo.id, err.message);
|
|
50303
50364
|
const channelError = new ChannelError("inbox", err);
|
|
@@ -50332,10 +50393,10 @@ class SyncManager {
|
|
|
50332
50393
|
const request = { jobs };
|
|
50333
50394
|
let result;
|
|
50334
50395
|
try {
|
|
50335
|
-
result = await this.reactor.loadBatch(request,
|
|
50336
|
-
sourceRemote
|
|
50337
|
-
});
|
|
50396
|
+
result = await this.reactor.loadBatch(request, this.abortController.signal, { sourceRemote });
|
|
50338
50397
|
} catch (error3) {
|
|
50398
|
+
if (this.isShutdown)
|
|
50399
|
+
return;
|
|
50339
50400
|
for (const { remote, syncOp } of items) {
|
|
50340
50401
|
const err = error3 instanceof Error ? error3 : new Error(String(error3));
|
|
50341
50402
|
syncOp.failed(new ChannelError("inbox", err));
|
|
@@ -50356,8 +50417,10 @@ class SyncManager {
|
|
|
50356
50417
|
const jobInfo = result.jobs[syncOp.jobId];
|
|
50357
50418
|
let completedJobInfo;
|
|
50358
50419
|
try {
|
|
50359
|
-
completedJobInfo = await this.awaiter.waitForJob(jobInfo.id);
|
|
50420
|
+
completedJobInfo = await this.awaiter.waitForJob(jobInfo.id, this.abortController.signal);
|
|
50360
50421
|
} catch (error3) {
|
|
50422
|
+
if (this.isShutdown)
|
|
50423
|
+
continue;
|
|
50361
50424
|
const err = error3 instanceof Error ? error3 : new Error(String(error3));
|
|
50362
50425
|
syncOp.failed(new ChannelError("inbox", err));
|
|
50363
50426
|
remote.channel.deadLetter.add(syncOp);
|
|
@@ -50404,7 +50467,7 @@ class SyncManager {
|
|
|
50404
50467
|
remote.channel.outbox.add(...syncOps);
|
|
50405
50468
|
}
|
|
50406
50469
|
async getOperationsForRemote(remote, ackOrdinal) {
|
|
50407
|
-
const results = await this.operationIndex.find(remote.collectionId, ackOrdinal, { excludeSourceRemote: remote.name });
|
|
50470
|
+
const results = await this.operationIndex.find(remote.collectionId, ackOrdinal, { excludeSourceRemote: remote.name }, undefined, this.abortController.signal);
|
|
50408
50471
|
let operations = results.results.map((entry) => toOperationWithContext(entry));
|
|
50409
50472
|
const sinceTimestamp = remote.options.sinceTimestampUtcMs;
|
|
50410
50473
|
if (sinceTimestamp && sinceTimestamp !== "0") {
|
|
@@ -51727,7 +51790,7 @@ var __defProp2, __export2 = (target, all) => {
|
|
|
51727
51790
|
}
|
|
51728
51791
|
}, DocumentDeletedError, InvalidSignatureError, DowngradeNotSupportedError, DocumentNotFoundError, getNextIndexForScope = (document2, scope) => {
|
|
51729
51792
|
return document2.header.revision[scope] || 0;
|
|
51730
|
-
}, EventBusAggregateError, ReactorEventTypes, QueueEventTypes, ModuleNotFoundError, DuplicateModuleError, DuplicateManifestError, ManifestNotFoundError, DowngradeNotSupportedError2, MissingUpgradeTransitionError, InvalidUpgradeStepError, STRICT_ORDER_ACTION_TYPES, MAX_SKIP_THRESHOLD = 1000, documentScopeActions, DRIVE_DOCUMENT_TYPE = "powerhouse/document-drive", ProcessorManager, KyselyDocumentView, KyselyDocumentIndexer, DuplicateOperationError, RevisionMismatchError, exports_001_create_operation_table, exports_002_create_keyframe_table, exports_003_create_document_table, exports_004_create_document_relationship_table, exports_005_create_indexer_state_table, exports_006_create_document_snapshot_table, exports_007_create_slug_mapping_table, exports_008_create_view_state_table, exports_009_create_operation_index_tables, exports_010_create_sync_tables, exports_011_add_cursor_type_column, exports_012_add_source_remote_column, exports_013_create_sync_dead_letters_table, exports_014_create_processor_cursor_table, REACTOR_SCHEMA = "reactor", migrations, ChannelScheme, SyncOperationStatus, ChannelErrorSource, SyncEventTypes, MailboxAggregateError, ChannelError, SyncOperationAggregateError, DEFAULT_CONFIG, syncOpCounter = 0, getLatestAppliedOrdinal = (syncOps) => {
|
|
51793
|
+
}, EventBusAggregateError, ReactorEventTypes, QueueEventTypes, ModuleNotFoundError, DuplicateModuleError, DuplicateManifestError, ManifestNotFoundError, DowngradeNotSupportedError2, MissingUpgradeTransitionError, InvalidUpgradeStepError, STRICT_ORDER_ACTION_TYPES, MAX_SKIP_THRESHOLD = 1000, documentScopeActions, DRIVE_DOCUMENT_TYPE = "powerhouse/document-drive", ProcessorManager, KyselyDocumentView, KyselyDocumentIndexer, DuplicateOperationError, RevisionMismatchError, exports_001_create_operation_table, exports_002_create_keyframe_table, exports_003_create_document_table, exports_004_create_document_relationship_table, exports_005_create_indexer_state_table, exports_006_create_document_snapshot_table, exports_007_create_slug_mapping_table, exports_008_create_view_state_table, exports_009_create_operation_index_tables, exports_010_create_sync_tables, exports_011_add_cursor_type_column, exports_012_add_source_remote_column, exports_013_create_sync_dead_letters_table, exports_014_create_processor_cursor_table, REACTOR_SCHEMA = "reactor", migrations, ChannelScheme, SyncOperationStatus, ChannelErrorSource, SyncEventTypes, MailboxAggregateError, GraphQLRequestError, ChannelError, SyncOperationAggregateError, DEFAULT_CONFIG, syncOpCounter = 0, getLatestAppliedOrdinal = (syncOps) => {
|
|
51731
51794
|
let maxOrdinal = 0;
|
|
51732
51795
|
for (const syncOp of syncOps) {
|
|
51733
51796
|
if (syncOp.status === 2) {
|
|
@@ -56935,6 +56998,16 @@ var init_src = __esm(() => {
|
|
|
56935
56998
|
this.errors = errors2;
|
|
56936
56999
|
}
|
|
56937
57000
|
};
|
|
57001
|
+
GraphQLRequestError = class GraphQLRequestError extends Error {
|
|
57002
|
+
statusCode;
|
|
57003
|
+
category;
|
|
57004
|
+
constructor(message, category, statusCode) {
|
|
57005
|
+
super(message);
|
|
57006
|
+
this.name = "GraphQLRequestError";
|
|
57007
|
+
this.category = category;
|
|
57008
|
+
this.statusCode = statusCode;
|
|
57009
|
+
}
|
|
57010
|
+
};
|
|
56938
57011
|
ChannelError = class ChannelError extends Error {
|
|
56939
57012
|
source;
|
|
56940
57013
|
error;
|
|
@@ -93773,7 +93846,7 @@ var init_package = __esm(() => {
|
|
|
93773
93846
|
package_default = {
|
|
93774
93847
|
name: "@powerhousedao/connect",
|
|
93775
93848
|
productName: "Powerhouse-Connect",
|
|
93776
|
-
version: "6.0.0-dev.
|
|
93849
|
+
version: "6.0.0-dev.90",
|
|
93777
93850
|
description: "Powerhouse Connect",
|
|
93778
93851
|
main: "dist/index.html",
|
|
93779
93852
|
type: "module",
|