@statezero/core 0.1.71 → 0.1.73

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.
@@ -22,6 +22,8 @@ export class SyncManager {
22
22
  debounceMs: number;
23
23
  maxWaitMs: number;
24
24
  batchStartTime: number | null;
25
+ syncQueue: PQueue<import("p-queue/dist/priority-queue").default, import("p-queue").QueueAddOptions>;
26
+ withTimeout(promise: any, ms: any): Promise<any>;
25
27
  /**
26
28
  * Initialize event handlers for all event receivers
27
29
  */
@@ -41,4 +43,5 @@ export class SyncManager {
41
43
  processMetrics(event: any): void;
42
44
  processModels(event: any): void;
43
45
  }
46
+ import PQueue from "p-queue";
44
47
  export const syncManager: SyncManager;
@@ -8,6 +8,7 @@ import { metricRegistry, MetricRegistry } from "./registries/metricRegistry";
8
8
  import { getModelClass, getConfig } from "../config";
9
9
  import { isNil } from "lodash-es";
10
10
  import { QuerysetStore } from "./stores/querysetStore";
11
+ import PQueue from "p-queue";
11
12
  export class EventPayload {
12
13
  constructor(data) {
13
14
  this.event = data.event;
@@ -85,6 +86,25 @@ export class SyncManager {
85
86
  this.debounceMs = 100; // Wait for rapid events to settle
86
87
  this.maxWaitMs = 2000; // Maximum time to hold events
87
88
  this.batchStartTime = null;
89
+ // SyncQueue
90
+ this.syncQueue = new PQueue({ concurrency: 1 });
91
+ }
92
+ withTimeout(promise, ms) {
93
+ // If no timeout specified, use 2x the periodic sync interval, or 30s as fallback
94
+ if (!ms) {
95
+ try {
96
+ const config = getConfig();
97
+ const intervalSeconds = config.periodicSyncIntervalSeconds;
98
+ ms = intervalSeconds ? intervalSeconds * 2000 : 30000; // 2x interval in ms, or 30s default
99
+ }
100
+ catch {
101
+ ms = 30000; // 30s fallback if no config
102
+ }
103
+ }
104
+ return Promise.race([
105
+ promise,
106
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`Sync timeout after ${ms}ms`)), ms)),
107
+ ]);
88
108
  }
89
109
  /**
90
110
  * Initialize event handlers for all event receivers
@@ -134,13 +154,13 @@ export class SyncManager {
134
154
  // Only sync if this store is actually being followed
135
155
  const isFollowed = this.isStoreFollowed(querysetRegistry, semanticKey);
136
156
  if (this.followAllQuerysets || isFollowed) {
137
- store.sync();
157
+ this.syncQueue.add(() => this.withTimeout(store.sync()));
138
158
  syncedCount++;
139
159
  }
140
160
  }
141
161
  }
142
162
  if (syncedCount > 0) {
143
- console.log(`[SyncManager] Periodic sync: ${syncedCount} stores synced`);
163
+ console.log(`[SyncManager] Periodic sync: ${syncedCount} stores pushed to the sync queue`);
144
164
  }
145
165
  }
146
166
  isStoreFollowed(registry, semanticKey) {
@@ -267,10 +287,7 @@ export class SyncManager {
267
287
  // Sync all relevant stores for this model
268
288
  console.log(`[SyncManager] Syncing ${storesToSync.length} queryset stores for ${representativeEvent.model}`);
269
289
  storesToSync.forEach((store) => {
270
- // Don't await - let them run in parallel
271
- store.sync().catch((error) => {
272
- console.error(`[SyncManager] Failed to sync queryset store:`, error);
273
- });
290
+ this.syncQueue.add(() => this.withTimeout(store.sync()));
274
291
  });
275
292
  }
276
293
  processMetrics(event) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statezero/core",
3
- "version": "0.1.71",
3
+ "version": "0.1.73",
4
4
  "type": "module",
5
5
  "module": "ESNext",
6
6
  "description": "The type-safe frontend client for StateZero - connect directly to your backend models with zero boilerplate",