@xnetjs/runtime 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts 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.8",
3
+ "version": "0.2.0",
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/core": "0.7.0",
32
- "@xnetjs/crypto": "0.7.0",
33
- "@xnetjs/data": "0.7.0",
34
- "@xnetjs/data-bridge": "0.7.0",
35
- "@xnetjs/history": "0.7.0",
36
- "@xnetjs/identity": "0.7.0",
37
- "@xnetjs/plugins": "0.7.0",
38
- "@xnetjs/storage": "0.7.0",
39
- "@xnetjs/sync": "0.7.0"
31
+ "@xnetjs/core": "0.8.0",
32
+ "@xnetjs/crypto": "0.8.0",
33
+ "@xnetjs/data": "0.8.0",
34
+ "@xnetjs/data-bridge": "0.8.0",
35
+ "@xnetjs/history": "0.8.0",
36
+ "@xnetjs/identity": "0.8.0",
37
+ "@xnetjs/plugins": "0.8.0",
38
+ "@xnetjs/storage": "0.8.0",
39
+ "@xnetjs/sync": "0.8.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "jsdom": "^26.0.0",