@statezero/core 0.2.44 → 0.2.46

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.
@@ -5,3 +5,4 @@
5
5
  * @returns {Object} Components registry for LayoutRenderer
6
6
  */
7
7
  export function createDefaultComponents(options?: Object): Object;
8
+ export { AlertElement, LabelElement, DividerElement, DisplayElement, GroupElement, TabsElement, ErrorBlock };
@@ -4,13 +4,14 @@
4
4
  * These are minimal, unstyled implementations that follow the contracts.
5
5
  * Users can use these as-is or as reference for custom implementations.
6
6
  */
7
- export { default as AlertElement } from './AlertElement.js';
8
- export { default as LabelElement } from './LabelElement.js';
9
- export { default as DividerElement } from './DividerElement.js';
10
- export { default as DisplayElement } from './DisplayElement.js';
11
- export { default as GroupElement } from './GroupElement.js';
12
- export { default as TabsElement } from './TabsElement.js';
13
- export { default as ErrorBlock } from './ErrorBlock.js';
7
+ import AlertElement from './AlertElement.js';
8
+ import LabelElement from './LabelElement.js';
9
+ import DividerElement from './DividerElement.js';
10
+ import DisplayElement from './DisplayElement.js';
11
+ import GroupElement from './GroupElement.js';
12
+ import TabsElement from './TabsElement.js';
13
+ import ErrorBlock from './ErrorBlock.js';
14
+ export { AlertElement, LabelElement, DividerElement, DisplayElement, GroupElement, TabsElement, ErrorBlock };
14
15
  /**
15
16
  * Create a default components registry (without Control - user must provide)
16
17
  *
@@ -66,7 +66,7 @@ export class QuerysetStoreRegistry {
66
66
  * @param {string} operationId - Unique ID for this sync operation (for coordination)
67
67
  * @param {Set} dbSyncedKeys - Set of semanticKeys that are dbSynced (followedQuerysets)
68
68
  */
69
- groupSync(queryset: Object, operationId: string, dbSyncedKeys: Set<any>): Promise<void>;
69
+ groupSync(queryset: Object, operationId: string, dbSyncedKeys: Set<any>, canonical_id?: null): Promise<void>;
70
70
  }
71
71
  export const querysetStoreRegistry: QuerysetStoreRegistry;
72
72
  import { QuerysetStoreGraph } from './querysetStoreGraph.js';
@@ -287,7 +287,7 @@ export class QuerysetStoreRegistry {
287
287
  * @param {string} operationId - Unique ID for this sync operation (for coordination)
288
288
  * @param {Set} dbSyncedKeys - Set of semanticKeys that are dbSynced (followedQuerysets)
289
289
  */
290
- async groupSync(queryset, operationId, dbSyncedKeys) {
290
+ async groupSync(queryset, operationId, dbSyncedKeys, canonical_id = null) {
291
291
  if (isNil(queryset))
292
292
  return;
293
293
  const semanticKey = queryset.semanticKey;
@@ -319,7 +319,7 @@ export class QuerysetStoreRegistry {
319
319
  }
320
320
  if (iAmRoot) {
321
321
  // I'm the root - sync from DB (store handles everything)
322
- await store.sync();
322
+ await store.sync(canonical_id);
323
323
  cached.pks = store.groundTruthPks;
324
324
  cached.resolve();
325
325
  }
@@ -349,7 +349,7 @@ export class QuerysetStoreRegistry {
349
349
  // Fallback to direct sync on timeout or if root data is missing
350
350
  if (timedOut || !cached.pks) {
351
351
  console.warn(`[groupSync] Falling back to direct sync for: ${semanticKey.substring(0, 60)}`);
352
- await store.sync();
352
+ await store.sync(canonical_id);
353
353
  return;
354
354
  }
355
355
  // Filter from cached root data
@@ -55,6 +55,6 @@ export class QuerysetStore {
55
55
  * Sync this queryset with the database.
56
56
  * Fetches from DB and sets ground truth.
57
57
  */
58
- sync(): Promise<void>;
58
+ sync(canonical_id?: null): Promise<void>;
59
59
  }
60
60
  import { Cache } from '../cache/cache.js';
@@ -258,7 +258,7 @@ export class QuerysetStore {
258
258
  * Sync this queryset with the database.
259
259
  * Fetches from DB and sets ground truth.
260
260
  */
261
- async sync() {
261
+ async sync(canonical_id = null) {
262
262
  const id = this.modelClass.modelName;
263
263
  if (this.isSyncing) {
264
264
  console.warn(`[QuerysetStore ${id}] Already syncing, request ignored.`);
@@ -270,6 +270,7 @@ export class QuerysetStore {
270
270
  const response = await this.fetchFn({
271
271
  ast: this.queryset.build(),
272
272
  modelClass: this.modelClass,
273
+ canonical_id,
273
274
  });
274
275
  const { data, included } = response;
275
276
  if (!isNil(data)) {
@@ -5,6 +5,8 @@ export class EventPayload {
5
5
  operation_id: any;
6
6
  pk_field_name: any;
7
7
  configKey: any;
8
+ canonical_id: any;
9
+ server_ts_ms: any;
8
10
  instances: any;
9
11
  _cachedInstances: any;
10
12
  get modelClass(): Function | null;
@@ -16,6 +16,8 @@ export class EventPayload {
16
16
  this.operation_id = data.operation_id;
17
17
  this.pk_field_name = data.pk_field_name;
18
18
  this.configKey = data.configKey;
19
+ this.canonical_id = data.canonical_id || null;
20
+ this.server_ts_ms = data.server_ts_ms || null;
19
21
  // Parse PK fields to numbers in instances
20
22
  this.instances = data.instances?.map(instance => {
21
23
  if (instance && this.pk_field_name && instance[this.pk_field_name] != null) {
@@ -345,6 +347,9 @@ export class SyncManager {
345
347
  }
346
348
  if (storesToSync.length === 0)
347
349
  return;
350
+ // Pick canonical_id from the latest event by server_ts_ms
351
+ const latestEvent = allEvents.reduce((latest, evt) => (evt.server_ts_ms || 0) > (latest.server_ts_ms || 0) ? evt : latest, allEvents[0]);
352
+ const canonical_id = latestEvent.canonical_id;
348
353
  // Sync all relevant stores for this model
349
354
  console.log(`[SyncManager] Syncing ${storesToSync.length} queryset stores for ${representativeEvent.model}`);
350
355
  // Generate operationId for this batch - querysets in same chain will coordinate
@@ -356,7 +361,7 @@ export class SyncManager {
356
361
  .map(qs => qs.semanticKey)
357
362
  .filter(key => registry._stores.has(key)));
358
363
  // Run all groupSync calls in parallel - they coordinate via shared promise cache
359
- Promise.all(storesToSync.map(store => registry.groupSync(store.queryset, operationId, dbSyncedKeys)));
364
+ Promise.all(storesToSync.map(store => registry.groupSync(store.queryset, operationId, dbSyncedKeys, canonical_id)));
360
365
  }
361
366
  processMetrics(event) {
362
367
  const registry = this.registries.get(MetricRegistry);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statezero/core",
3
- "version": "0.2.44",
3
+ "version": "0.2.46",
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",