@xnetjs/runtime 0.1.8 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +24 -1
- package/dist/index.js +40 -0
- package/package.json +10 -10
package/dist/index.d.ts
CHANGED
|
@@ -973,6 +973,14 @@ type NodeSyncResponse = {
|
|
|
973
973
|
* Listener for unknown change type events.
|
|
974
974
|
*/
|
|
975
975
|
type UnknownChangeTypeListener = (change: NodeChange, peerId: string) => void;
|
|
976
|
+
/**
|
|
977
|
+
* The hub is refusing further writes for a capacity reason (exploration 0291):
|
|
978
|
+
* `QUOTA_EXCEEDED` — this identity is over the hub's per-user cap (demo mode);
|
|
979
|
+
* `STORAGE_FULL` — the hub's volume is full and it is shedding writes.
|
|
980
|
+
* Local data is untouched; outbound sync pauses until the next reconnect.
|
|
981
|
+
*/
|
|
982
|
+
type SyncBlockedReason = 'QUOTA_EXCEEDED' | 'STORAGE_FULL';
|
|
983
|
+
type SyncBlockedListener = (reason: SyncBlockedReason, detail: string) => void;
|
|
976
984
|
declare class NodeStoreSyncProvider {
|
|
977
985
|
private store;
|
|
978
986
|
private room;
|
|
@@ -987,6 +995,7 @@ declare class NodeStoreSyncProvider {
|
|
|
987
995
|
private messageCleanup;
|
|
988
996
|
private storeCleanup;
|
|
989
997
|
private unknownChangeTypeListeners;
|
|
998
|
+
private syncBlockedListeners;
|
|
990
999
|
private sendQueue;
|
|
991
1000
|
private queuedHashes;
|
|
992
1001
|
private sendTimer;
|
|
@@ -1048,6 +1057,20 @@ declare class NodeStoreSyncProvider {
|
|
|
1048
1057
|
* counter so sparse one-off rejections never accumulate to a false trip.
|
|
1049
1058
|
*/
|
|
1050
1059
|
private recordStructuralRejection;
|
|
1060
|
+
/**
|
|
1061
|
+
* Halt outbound sync on a capacity rejection (exploration 0291). Unlike the
|
|
1062
|
+
* structural breaker, one rejection is enough: while the account is over
|
|
1063
|
+
* quota (or the hub's disk is full) every further change is rejected too, so
|
|
1064
|
+
* resending only floods the hub. Local data is untouched — the store keeps
|
|
1065
|
+
* accepting writes and the un-pushed changes replay on the next reconnect
|
|
1066
|
+
* (the demo hub's daily reset / freed disk clears the condition server-side).
|
|
1067
|
+
*/
|
|
1068
|
+
private recordCapacityRejection;
|
|
1069
|
+
/**
|
|
1070
|
+
* Subscribe to capacity-blocked events (hub over quota / disk full) so the
|
|
1071
|
+
* app can surface a "storage full" notice. Returns an unsubscribe function.
|
|
1072
|
+
*/
|
|
1073
|
+
onSyncBlocked(listener: SyncBlockedListener): () => void;
|
|
1051
1074
|
private requestSync;
|
|
1052
1075
|
private handleRemoteChange;
|
|
1053
1076
|
private handleSyncResponse;
|
|
@@ -1290,4 +1313,4 @@ declare function negotiateProtocolVersion(ours: readonly string[], theirs: reado
|
|
|
1290
1313
|
*/
|
|
1291
1314
|
declare function isProtocolCompatible(theirs: readonly string[]): boolean;
|
|
1292
1315
|
|
|
1293
|
-
export { type AdapterConformanceCheck, AdapterConformanceError, type AdapterConformanceResult, type BlobStoreForSync, type ConformanceClientFactory, type ConnectionManager, type ConnectionManagerConfig, type ConnectionStatus, type CreateXNetClientOptions, type HubConnection, type HubTransport, type InitialSyncManager, type InitialSyncMessage, type LiveQuery, type LiveQueryValue, METABRIDGE_ORIGIN, METABRIDGE_SEED_ORIGIN, type MetaBridge, type MultiHubConnectionManagerConfig, type MultiHubSyncManager, type MultiHubSyncManagerConfig, type NodePool, type NodePoolConfig, NodeStoreSyncProvider, type NodeSyncResponse, type OfflineQueue, type OfflineQueueConfig, type PlannedHub, type PoolEntryState, type ProgressListener, type QueueEntry, type Registry, type RegistryConfig, type RegistryStorage, type ReplicaTrust, type ReplicationDestinationSpec, type ReplicationScopeNode, type ScopedRoomHandle, type SerializedNodeChange, type SpaceReplicationPolicy, type SyncManager, type SyncManagerConfig, type SyncPhase, type SyncProgress, type SyncReconciliationOptions, type SyncReconciliationReport, type SyncStatus, type TrackedNode, WebSocketSyncProvider, type WebSocketSyncProviderOptions, XNET_AWARENESS_VERSION, XNET_DATA_MODEL_VERSION, XNET_PROTOCOL_VERSION, XNET_SCHEMA_VERSION, XNET_SUPPORTED_PROTOCOL_VERSIONS, XNET_SYNC_ENVELOPE_VERSION, XNET_UCAN_PROFILE, type XNetClient, type XNetClientBridgeMode, type XNetClientPluginOptions, type XNetClientRuntimePhase, type XNetClientRuntimeStatus, type XNetClientSyncOptions, type XNetClientTelemetry, type XNetClientUndoOptions, type XNetProtocolBundle, createConnectionManager, createInitialSyncManager, createMetaBridge, createMultiHubConnectionManager, createMultiHubSyncManager, createNodePool, createOfflineQueue, createRegistry, createSyncManager, createXNetClient, isProtocolCompatible, liveQuery, namespaceForNode, negotiateProtocolVersion, replicationConfigFromPolicies, runAdapterConformance, spaceNamespace, systemNamespace };
|
|
1316
|
+
export { type AdapterConformanceCheck, AdapterConformanceError, type AdapterConformanceResult, type BlobStoreForSync, type ConformanceClientFactory, type ConnectionManager, type ConnectionManagerConfig, type ConnectionStatus, type CreateXNetClientOptions, type HubConnection, type HubTransport, type InitialSyncManager, type InitialSyncMessage, type LiveQuery, type LiveQueryValue, METABRIDGE_ORIGIN, METABRIDGE_SEED_ORIGIN, type MetaBridge, type MultiHubConnectionManagerConfig, type MultiHubSyncManager, type MultiHubSyncManagerConfig, type NodePool, type NodePoolConfig, NodeStoreSyncProvider, type NodeSyncResponse, type OfflineQueue, type OfflineQueueConfig, type PlannedHub, type PoolEntryState, type ProgressListener, type QueueEntry, type Registry, type RegistryConfig, type RegistryStorage, type ReplicaTrust, type ReplicationDestinationSpec, type ReplicationScopeNode, type ScopedRoomHandle, type SerializedNodeChange, type SpaceReplicationPolicy, type SyncBlockedListener, type SyncBlockedReason, type SyncManager, type SyncManagerConfig, type SyncPhase, type SyncProgress, type SyncReconciliationOptions, type SyncReconciliationReport, type SyncStatus, type TrackedNode, WebSocketSyncProvider, type WebSocketSyncProviderOptions, XNET_AWARENESS_VERSION, XNET_DATA_MODEL_VERSION, XNET_PROTOCOL_VERSION, XNET_SCHEMA_VERSION, XNET_SUPPORTED_PROTOCOL_VERSIONS, XNET_SYNC_ENVELOPE_VERSION, XNET_UCAN_PROFILE, type XNetClient, type XNetClientBridgeMode, type XNetClientPluginOptions, type XNetClientRuntimePhase, type XNetClientRuntimeStatus, type XNetClientSyncOptions, type XNetClientTelemetry, type XNetClientUndoOptions, type XNetProtocolBundle, createConnectionManager, createInitialSyncManager, createMetaBridge, createMultiHubConnectionManager, createMultiHubSyncManager, createNodePool, createOfflineQueue, createRegistry, createSyncManager, createXNetClient, isProtocolCompatible, liveQuery, namespaceForNode, negotiateProtocolVersion, replicationConfigFromPolicies, runAdapterConformance, spaceNamespace, systemNamespace };
|
package/dist/index.js
CHANGED
|
@@ -901,6 +901,7 @@ function nowMs2() {
|
|
|
901
901
|
function yieldToEventLoop() {
|
|
902
902
|
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
903
903
|
}
|
|
904
|
+
var CAPACITY_REJECTION_CODES = /* @__PURE__ */ new Set(["QUOTA_EXCEEDED", "STORAGE_FULL"]);
|
|
904
905
|
var NodeStoreSyncProvider = class {
|
|
905
906
|
constructor(store, room) {
|
|
906
907
|
this.store = store;
|
|
@@ -917,6 +918,7 @@ var NodeStoreSyncProvider = class {
|
|
|
917
918
|
messageCleanup = null;
|
|
918
919
|
storeCleanup = null;
|
|
919
920
|
unknownChangeTypeListeners = /* @__PURE__ */ new Set();
|
|
921
|
+
syncBlockedListeners = /* @__PURE__ */ new Set();
|
|
920
922
|
// Throttled send queue.
|
|
921
923
|
sendQueue = [];
|
|
922
924
|
queuedHashes = /* @__PURE__ */ new Set();
|
|
@@ -1103,6 +1105,9 @@ var NodeStoreSyncProvider = class {
|
|
|
1103
1105
|
if (STRUCTURAL_REJECTION_CODES.has(code)) {
|
|
1104
1106
|
this.recordStructuralRejection(code, message);
|
|
1105
1107
|
}
|
|
1108
|
+
if (CAPACITY_REJECTION_CODES.has(code)) {
|
|
1109
|
+
this.recordCapacityRejection(code, message);
|
|
1110
|
+
}
|
|
1106
1111
|
return;
|
|
1107
1112
|
}
|
|
1108
1113
|
if (message.type !== "node-sync-response") return;
|
|
@@ -1130,6 +1135,41 @@ var NodeStoreSyncProvider = class {
|
|
|
1130
1135
|
`[NodeStoreSync] Pausing outbound sync after ${this.structuralRejections} consecutive "${code}" rejections. Local changes are valid but the hub keeps rejecting them \u2014 this usually means the hub is on an incompatible @xnetjs/sync build (protocol/hash skew). Outbound sync resumes on reconnect. Hub said: ${message.error ?? message.message ?? "(no detail)"}`
|
|
1131
1136
|
);
|
|
1132
1137
|
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Halt outbound sync on a capacity rejection (exploration 0291). Unlike the
|
|
1140
|
+
* structural breaker, one rejection is enough: while the account is over
|
|
1141
|
+
* quota (or the hub's disk is full) every further change is rejected too, so
|
|
1142
|
+
* resending only floods the hub. Local data is untouched — the store keeps
|
|
1143
|
+
* accepting writes and the un-pushed changes replay on the next reconnect
|
|
1144
|
+
* (the demo hub's daily reset / freed disk clears the condition server-side).
|
|
1145
|
+
*/
|
|
1146
|
+
recordCapacityRejection(reason, message) {
|
|
1147
|
+
const detail = String(message.error ?? message.message ?? "");
|
|
1148
|
+
if (!this.outboundHalted) {
|
|
1149
|
+
this.outboundHalted = true;
|
|
1150
|
+
this.clearSendQueue();
|
|
1151
|
+
console.error(
|
|
1152
|
+
reason === "QUOTA_EXCEEDED" ? `[NodeStoreSync] Hub storage limit reached \u2014 pausing outbound sync. Your data is safe locally; syncing resumes when space frees up (demo hubs reset daily). Hub said: ${detail}` : `[NodeStoreSync] Hub disk is full \u2014 pausing outbound sync. Your data is safe locally; syncing resumes automatically. Hub said: ${detail}`
|
|
1153
|
+
);
|
|
1154
|
+
}
|
|
1155
|
+
for (const listener of this.syncBlockedListeners) {
|
|
1156
|
+
try {
|
|
1157
|
+
listener(reason, detail);
|
|
1158
|
+
} catch (err) {
|
|
1159
|
+
console.error("Error in sync-blocked listener:", err);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
/**
|
|
1164
|
+
* Subscribe to capacity-blocked events (hub over quota / disk full) so the
|
|
1165
|
+
* app can surface a "storage full" notice. Returns an unsubscribe function.
|
|
1166
|
+
*/
|
|
1167
|
+
onSyncBlocked(listener) {
|
|
1168
|
+
this.syncBlockedListeners.add(listener);
|
|
1169
|
+
return () => {
|
|
1170
|
+
this.syncBlockedListeners.delete(listener);
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1133
1173
|
requestSync() {
|
|
1134
1174
|
if (!this.connection) return;
|
|
1135
1175
|
this.connection.sendRaw({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xnetjs/runtime",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Framework-agnostic xNet runtime: createXNetClient() and sync orchestration, usable from any framework, a CLI, a worker, or a Node service",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -28,15 +28,15 @@
|
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"y-protocols": "^1.0.6",
|
|
30
30
|
"yjs": "^13.6.24",
|
|
31
|
-
"@xnetjs/
|
|
32
|
-
"@xnetjs/
|
|
33
|
-
"@xnetjs/data": "0.
|
|
34
|
-
"@xnetjs/
|
|
35
|
-
"@xnetjs/
|
|
36
|
-
"@xnetjs/
|
|
37
|
-
"@xnetjs/
|
|
38
|
-
"@xnetjs/
|
|
39
|
-
"@xnetjs/
|
|
31
|
+
"@xnetjs/crypto": "0.9.0",
|
|
32
|
+
"@xnetjs/core": "0.9.0",
|
|
33
|
+
"@xnetjs/data-bridge": "0.9.0",
|
|
34
|
+
"@xnetjs/history": "0.9.0",
|
|
35
|
+
"@xnetjs/plugins": "0.9.0",
|
|
36
|
+
"@xnetjs/storage": "0.9.0",
|
|
37
|
+
"@xnetjs/sync": "0.9.0",
|
|
38
|
+
"@xnetjs/identity": "0.9.0",
|
|
39
|
+
"@xnetjs/data": "0.9.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"jsdom": "^26.0.0",
|