@statezero/core 0.2.3 → 0.2.5

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.
Files changed (54) hide show
  1. package/dist/adaptors/vue/composables.js +0 -12
  2. package/dist/cli/commands/syncModels.js +9 -0
  3. package/dist/core/eventReceivers.js +0 -15
  4. package/dist/flavours/django/model.d.ts +2 -1
  5. package/dist/flavours/django/model.js +9 -1
  6. package/dist/flavours/django/querySet.js +1 -0
  7. package/dist/models/backend1/django_app/comprehensivemodel.js +8 -0
  8. package/dist/models/backend1/django_app/custompkmodel.js +8 -0
  9. package/dist/models/backend1/django_app/dailyrate.d.ts +47 -0
  10. package/dist/models/backend1/django_app/dailyrate.js +71 -0
  11. package/dist/models/backend1/django_app/deepmodellevel1.js +8 -0
  12. package/dist/models/backend1/django_app/deepmodellevel2.js +8 -0
  13. package/dist/models/backend1/django_app/deepmodellevel3.js +8 -0
  14. package/dist/models/backend1/django_app/dummymodel.js +8 -0
  15. package/dist/models/backend1/django_app/dummyrelatedmodel.js +8 -0
  16. package/dist/models/backend1/django_app/filetest.js +8 -0
  17. package/dist/models/backend1/django_app/index.d.ts +2 -0
  18. package/dist/models/backend1/django_app/index.js +2 -0
  19. package/dist/models/backend1/django_app/modelwithcustompkrelation.js +8 -0
  20. package/dist/models/backend1/django_app/namefiltercustompkmodel.js +8 -0
  21. package/dist/models/backend1/django_app/order.js +8 -0
  22. package/dist/models/backend1/django_app/orderitem.js +8 -0
  23. package/dist/models/backend1/django_app/product.js +8 -0
  24. package/dist/models/backend1/django_app/productcategory.js +8 -0
  25. package/dist/models/backend1/django_app/rateplan.d.ts +44 -0
  26. package/dist/models/backend1/django_app/rateplan.js +69 -0
  27. package/dist/models/default/django_app/comprehensivemodel.js +8 -0
  28. package/dist/models/default/django_app/custompkmodel.js +8 -0
  29. package/dist/models/default/django_app/dailyrate.d.ts +47 -0
  30. package/dist/models/default/django_app/dailyrate.js +71 -0
  31. package/dist/models/default/django_app/deepmodellevel1.js +8 -0
  32. package/dist/models/default/django_app/deepmodellevel2.js +8 -0
  33. package/dist/models/default/django_app/deepmodellevel3.js +8 -0
  34. package/dist/models/default/django_app/dummymodel.js +8 -0
  35. package/dist/models/default/django_app/dummyrelatedmodel.js +8 -0
  36. package/dist/models/default/django_app/filetest.js +8 -0
  37. package/dist/models/default/django_app/index.d.ts +2 -0
  38. package/dist/models/default/django_app/index.js +2 -0
  39. package/dist/models/default/django_app/modelwithcustompkrelation.js +8 -0
  40. package/dist/models/default/django_app/namefiltercustompkmodel.js +8 -0
  41. package/dist/models/default/django_app/order.js +8 -0
  42. package/dist/models/default/django_app/orderitem.js +8 -0
  43. package/dist/models/default/django_app/product.js +8 -0
  44. package/dist/models/default/django_app/productcategory.js +8 -0
  45. package/dist/models/default/django_app/rateplan.d.ts +44 -0
  46. package/dist/models/default/django_app/rateplan.js +69 -0
  47. package/dist/syncEngine/stores/modelStore.js +2 -1
  48. package/dist/syncEngine/stores/querysetStore.d.ts +1 -1
  49. package/dist/syncEngine/stores/querysetStore.js +3 -10
  50. package/dist/syncEngine/sync.d.ts +0 -7
  51. package/dist/syncEngine/sync.js +2 -119
  52. package/package.json +1 -2
  53. package/dist/syncEngine/namespaceUtils.d.ts +0 -16
  54. package/dist/syncEngine/namespaceUtils.js +0 -32
@@ -0,0 +1,71 @@
1
+ /**
2
+ * This file was auto-generated. Do not make direct changes to the file.
3
+ */
4
+ import { Model, Manager, QuerySet, getModelClass } from '../../../../src';
5
+ import { wrapReactiveModel } from '../../../../src';
6
+ import schemaData from './dailyrate.schema.json';
7
+ /**
8
+ * Model-specific QuerySet implementation
9
+ */
10
+ export class DailyRateQuerySet extends QuerySet {
11
+ }
12
+ /**
13
+ * Model-specific Manager implementation
14
+ */
15
+ export class DailyRateManager extends Manager {
16
+ constructor(ModelClass) {
17
+ super(ModelClass, DailyRateQuerySet);
18
+ }
19
+ newQuerySet() {
20
+ return new DailyRateQuerySet(this.ModelClass);
21
+ }
22
+ }
23
+ /**
24
+ * Implementation of the DailyRate model
25
+ */
26
+ export class DailyRate extends Model {
27
+ constructor(data) {
28
+ DailyRate.validateFields(data);
29
+ super(data);
30
+ // Define getters and setters for all fields
31
+ this._defineProperties();
32
+ return wrapReactiveModel(this);
33
+ }
34
+ /**
35
+ * Define property getters and setters for all model fields
36
+ * @private
37
+ */
38
+ _defineProperties() {
39
+ // For each field, define a property that gets/sets from internal storage
40
+ DailyRate.fields.forEach(field => {
41
+ Object.defineProperty(this, field, {
42
+ get: function () {
43
+ return this.getField(field);
44
+ },
45
+ set: function (value) {
46
+ this.setField(field, value);
47
+ },
48
+ enumerable: true, // Make sure fields are enumerable for serialization
49
+ configurable: true
50
+ });
51
+ });
52
+ // Add a special read-only getter for the repr field
53
+ Object.defineProperty(this, 'repr', {
54
+ get: function () {
55
+ return this.getField('repr');
56
+ },
57
+ enumerable: true, // Make sure repr is enumerable
58
+ configurable: true
59
+ });
60
+ }
61
+ }
62
+ // Bind this model to its backend
63
+ DailyRate.configKey = 'default';
64
+ DailyRate.modelName = 'django_app.dailyrate';
65
+ DailyRate.primaryKeyField = 'id';
66
+ DailyRate.objects = new DailyRateManager(DailyRate);
67
+ DailyRate.fields = ['id', 'rate_plan', 'date', 'price', 'min_stay_arrival', 'min_stay_through', 'max_stay', 'closed_to_arrival', 'closed_to_departure', 'stop_sell'];
68
+ DailyRate.schema = schemaData;
69
+ DailyRate.relationshipFields = new Map([
70
+ ['rate_plan', { 'ModelClass': () => getModelClass('django_app.rateplan', 'default'), 'relationshipType': 'foreign-key' }]
71
+ ]);
@@ -49,6 +49,14 @@ export class DeepModelLevel1 extends Model {
49
49
  configurable: true
50
50
  });
51
51
  });
52
+ // Add a special read-only getter for the repr field
53
+ Object.defineProperty(this, 'repr', {
54
+ get: function () {
55
+ return this.getField('repr');
56
+ },
57
+ enumerable: true, // Make sure repr is enumerable
58
+ configurable: true
59
+ });
52
60
  }
53
61
  }
54
62
  // Bind this model to its backend
@@ -49,6 +49,14 @@ export class DeepModelLevel2 extends Model {
49
49
  configurable: true
50
50
  });
51
51
  });
52
+ // Add a special read-only getter for the repr field
53
+ Object.defineProperty(this, 'repr', {
54
+ get: function () {
55
+ return this.getField('repr');
56
+ },
57
+ enumerable: true, // Make sure repr is enumerable
58
+ configurable: true
59
+ });
52
60
  }
53
61
  }
54
62
  // Bind this model to its backend
@@ -49,6 +49,14 @@ export class DeepModelLevel3 extends Model {
49
49
  configurable: true
50
50
  });
51
51
  });
52
+ // Add a special read-only getter for the repr field
53
+ Object.defineProperty(this, 'repr', {
54
+ get: function () {
55
+ return this.getField('repr');
56
+ },
57
+ enumerable: true, // Make sure repr is enumerable
58
+ configurable: true
59
+ });
52
60
  }
53
61
  }
54
62
  // Bind this model to its backend
@@ -49,6 +49,14 @@ export class DummyModel extends Model {
49
49
  configurable: true
50
50
  });
51
51
  });
52
+ // Add a special read-only getter for the repr field
53
+ Object.defineProperty(this, 'repr', {
54
+ get: function () {
55
+ return this.getField('repr');
56
+ },
57
+ enumerable: true, // Make sure repr is enumerable
58
+ configurable: true
59
+ });
52
60
  }
53
61
  }
54
62
  // Bind this model to its backend
@@ -49,6 +49,14 @@ export class DummyRelatedModel extends Model {
49
49
  configurable: true
50
50
  });
51
51
  });
52
+ // Add a special read-only getter for the repr field
53
+ Object.defineProperty(this, 'repr', {
54
+ get: function () {
55
+ return this.getField('repr');
56
+ },
57
+ enumerable: true, // Make sure repr is enumerable
58
+ configurable: true
59
+ });
52
60
  }
53
61
  }
54
62
  // Bind this model to its backend
@@ -49,6 +49,14 @@ export class FileTest extends Model {
49
49
  configurable: true
50
50
  });
51
51
  });
52
+ // Add a special read-only getter for the repr field
53
+ Object.defineProperty(this, 'repr', {
54
+ get: function () {
55
+ return this.getField('repr');
56
+ },
57
+ enumerable: true, // Make sure repr is enumerable
58
+ configurable: true
59
+ });
52
60
  }
53
61
  }
54
62
  // Bind this model to its backend
@@ -12,3 +12,5 @@ export * from "./productcategory";
12
12
  export * from "./product";
13
13
  export * from "./order";
14
14
  export * from "./orderitem";
15
+ export * from "./rateplan";
16
+ export * from "./dailyrate";
@@ -12,3 +12,5 @@ export * from './productcategory';
12
12
  export * from './product';
13
13
  export * from './order';
14
14
  export * from './orderitem';
15
+ export * from './rateplan';
16
+ export * from './dailyrate';
@@ -49,6 +49,14 @@ export class ModelWithCustomPKRelation extends Model {
49
49
  configurable: true
50
50
  });
51
51
  });
52
+ // Add a special read-only getter for the repr field
53
+ Object.defineProperty(this, 'repr', {
54
+ get: function () {
55
+ return this.getField('repr');
56
+ },
57
+ enumerable: true, // Make sure repr is enumerable
58
+ configurable: true
59
+ });
52
60
  }
53
61
  }
54
62
  // Bind this model to its backend
@@ -49,6 +49,14 @@ export class NameFilterCustomPKModel extends Model {
49
49
  configurable: true
50
50
  });
51
51
  });
52
+ // Add a special read-only getter for the repr field
53
+ Object.defineProperty(this, 'repr', {
54
+ get: function () {
55
+ return this.getField('repr');
56
+ },
57
+ enumerable: true, // Make sure repr is enumerable
58
+ configurable: true
59
+ });
52
60
  }
53
61
  }
54
62
  // Bind this model to its backend
@@ -49,6 +49,14 @@ export class Order extends Model {
49
49
  configurable: true
50
50
  });
51
51
  });
52
+ // Add a special read-only getter for the repr field
53
+ Object.defineProperty(this, 'repr', {
54
+ get: function () {
55
+ return this.getField('repr');
56
+ },
57
+ enumerable: true, // Make sure repr is enumerable
58
+ configurable: true
59
+ });
52
60
  }
53
61
  }
54
62
  // Bind this model to its backend
@@ -49,6 +49,14 @@ export class OrderItem extends Model {
49
49
  configurable: true
50
50
  });
51
51
  });
52
+ // Add a special read-only getter for the repr field
53
+ Object.defineProperty(this, 'repr', {
54
+ get: function () {
55
+ return this.getField('repr');
56
+ },
57
+ enumerable: true, // Make sure repr is enumerable
58
+ configurable: true
59
+ });
52
60
  }
53
61
  }
54
62
  // Bind this model to its backend
@@ -49,6 +49,14 @@ export class Product extends Model {
49
49
  configurable: true
50
50
  });
51
51
  });
52
+ // Add a special read-only getter for the repr field
53
+ Object.defineProperty(this, 'repr', {
54
+ get: function () {
55
+ return this.getField('repr');
56
+ },
57
+ enumerable: true, // Make sure repr is enumerable
58
+ configurable: true
59
+ });
52
60
  }
53
61
  }
54
62
  // Bind this model to its backend
@@ -49,6 +49,14 @@ export class ProductCategory extends Model {
49
49
  configurable: true
50
50
  });
51
51
  });
52
+ // Add a special read-only getter for the repr field
53
+ Object.defineProperty(this, 'repr', {
54
+ get: function () {
55
+ return this.getField('repr');
56
+ },
57
+ enumerable: true, // Make sure repr is enumerable
58
+ configurable: true
59
+ });
52
60
  }
53
61
  }
54
62
  // Bind this model to its backend
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Model-specific QuerySet implementation
3
+ */
4
+ export class RatePlanQuerySet extends QuerySet<any> {
5
+ constructor(ModelClass: ModelConstructor, config?: {
6
+ nodes?: QueryNode[] | undefined;
7
+ orderBy?: {
8
+ field: string;
9
+ direction: "asc" | "desc";
10
+ }[] | undefined;
11
+ fields?: Set<string> | undefined;
12
+ aggregations?: Aggregation[] | undefined;
13
+ initialQueryset?: string | undefined;
14
+ serializerOptions?: SerializerOptions;
15
+ materialized?: boolean | undefined;
16
+ } | undefined, parent?: null);
17
+ }
18
+ /**
19
+ * Model-specific Manager implementation
20
+ */
21
+ export class RatePlanManager extends Manager {
22
+ constructor(ModelClass: any);
23
+ }
24
+ /**
25
+ * Implementation of the RatePlan model
26
+ */
27
+ export class RatePlan extends Model {
28
+ static configKey: string;
29
+ static modelName: string;
30
+ static primaryKeyField: string;
31
+ static objects: RatePlanManager;
32
+ static fields: string[];
33
+ static schema: any;
34
+ static relationshipFields: Map<any, any>;
35
+ constructor(data: any);
36
+ /**
37
+ * Define property getters and setters for all model fields
38
+ * @private
39
+ */
40
+ private _defineProperties;
41
+ }
42
+ import { QuerySet } from '../../../../src';
43
+ import { Manager } from '../../../../src';
44
+ import { Model } from '../../../../src';
@@ -0,0 +1,69 @@
1
+ /**
2
+ * This file was auto-generated. Do not make direct changes to the file.
3
+ */
4
+ import { Model, Manager, QuerySet, getModelClass } from '../../../../src';
5
+ import { wrapReactiveModel } from '../../../../src';
6
+ import schemaData from './rateplan.schema.json';
7
+ /**
8
+ * Model-specific QuerySet implementation
9
+ */
10
+ export class RatePlanQuerySet extends QuerySet {
11
+ }
12
+ /**
13
+ * Model-specific Manager implementation
14
+ */
15
+ export class RatePlanManager extends Manager {
16
+ constructor(ModelClass) {
17
+ super(ModelClass, RatePlanQuerySet);
18
+ }
19
+ newQuerySet() {
20
+ return new RatePlanQuerySet(this.ModelClass);
21
+ }
22
+ }
23
+ /**
24
+ * Implementation of the RatePlan model
25
+ */
26
+ export class RatePlan extends Model {
27
+ constructor(data) {
28
+ RatePlan.validateFields(data);
29
+ super(data);
30
+ // Define getters and setters for all fields
31
+ this._defineProperties();
32
+ return wrapReactiveModel(this);
33
+ }
34
+ /**
35
+ * Define property getters and setters for all model fields
36
+ * @private
37
+ */
38
+ _defineProperties() {
39
+ // For each field, define a property that gets/sets from internal storage
40
+ RatePlan.fields.forEach(field => {
41
+ Object.defineProperty(this, field, {
42
+ get: function () {
43
+ return this.getField(field);
44
+ },
45
+ set: function (value) {
46
+ this.setField(field, value);
47
+ },
48
+ enumerable: true, // Make sure fields are enumerable for serialization
49
+ configurable: true
50
+ });
51
+ });
52
+ // Add a special read-only getter for the repr field
53
+ Object.defineProperty(this, 'repr', {
54
+ get: function () {
55
+ return this.getField('repr');
56
+ },
57
+ enumerable: true, // Make sure repr is enumerable
58
+ configurable: true
59
+ });
60
+ }
61
+ }
62
+ // Bind this model to its backend
63
+ RatePlan.configKey = 'default';
64
+ RatePlan.modelName = 'django_app.rateplan';
65
+ RatePlan.primaryKeyField = 'id';
66
+ RatePlan.objects = new RatePlanManager(RatePlan);
67
+ RatePlan.fields = ['id', 'name'];
68
+ RatePlan.schema = schemaData;
69
+ RatePlan.relationshipFields = new Map([]);
@@ -133,7 +133,8 @@ export class ModelStore {
133
133
  }
134
134
  }
135
135
  if (item && typeof item.serialize === "function") {
136
- const serializedItem = item.serialize();
136
+ // Pass includeRepr=true to preserve repr field in cache
137
+ const serializedItem = item.serialize(true);
137
138
  serializedItem[pkField] = pk;
138
139
  nonTempPkItems.push(serializedItem);
139
140
  }
@@ -46,6 +46,6 @@ export class QuerysetStore {
46
46
  renderFromRoot(optimistic: boolean | undefined, rootStore: any): any[];
47
47
  renderFromData(optimistic?: boolean): any[];
48
48
  applyOperation(operation: any, currentPks: any): any;
49
- sync(options?: {}): Promise<void>;
49
+ sync(forceFromDb?: boolean): Promise<void>;
50
50
  }
51
51
  import { Cache } from '../cache/cache.js';
@@ -269,8 +269,7 @@ export class QuerysetStore {
269
269
  }
270
270
  return currentPks;
271
271
  }
272
- async sync(options = {}) {
273
- const { canonical_id, forceFromDb = false } = typeof options === 'boolean' ? { forceFromDb: options } : options;
272
+ async sync(forceFromDb = false) {
274
273
  const id = this.modelClass.modelName;
275
274
  if (this.isSyncing) {
276
275
  console.warn(`[QuerysetStore ${id}] Already syncing, request ignored.`);
@@ -293,16 +292,10 @@ export class QuerysetStore {
293
292
  this.isSyncing = true;
294
293
  console.log(`[${id}] Starting sync...`);
295
294
  try {
296
- // Build request with canonical_id
297
- const requestBody = {
295
+ const response = await this.fetchFn({
298
296
  ast: this.queryset.build(),
299
297
  modelClass: this.modelClass,
300
- };
301
- // Include canonical_id if provided (for cache sharing)
302
- if (canonical_id) {
303
- requestBody.canonical_id = canonical_id;
304
- }
305
- const response = await this.fetchFn(requestBody);
298
+ });
306
299
  const { data, included } = response;
307
300
  if (isNil(data)) {
308
301
  return;
@@ -5,7 +5,6 @@ export class EventPayload {
5
5
  operation_id: any;
6
6
  pk_field_name: any;
7
7
  configKey: any;
8
- canonical_id: any;
9
8
  instances: any;
10
9
  _cachedInstances: any;
11
10
  get modelClass(): Function | null;
@@ -24,22 +23,16 @@ export class SyncManager {
24
23
  maxWaitMs: number;
25
24
  batchStartTime: number | null;
26
25
  syncQueue: PQueue<import("p-queue/dist/priority-queue").default, import("p-queue").QueueAddOptions>;
27
- activeSubscriptions: Map<any, any>;
28
- currentCanonicalId: any;
29
26
  withTimeout(promise: any, ms: any): Promise<any>;
30
27
  /**
31
28
  * Initialize event handlers for all event receivers
32
29
  */
33
30
  initialize(): void;
34
- handleDisconnect(): void;
35
- handleReconnect(): void;
36
31
  startPeriodicSync(): void;
37
32
  syncStaleQuerysets(): void;
38
33
  pruneUnreferencedModels(): void;
39
34
  isStoreFollowed(registry: any, semanticKey: any): boolean;
40
35
  cleanup(): void;
41
- subscribeToNamespace(queryset: any): Promise<void>;
42
- unsubscribeFromNamespace(queryset: any): Promise<void>;
43
36
  followModel(registry: any, modelClass: any): void;
44
37
  unfollowModel(registry: any, modelClass: any): void;
45
38
  manageRegistry(registry: any): void;
@@ -16,7 +16,6 @@ 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;
20
19
  // Parse PK fields to numbers in instances
21
20
  this.instances = data.instances?.map(instance => {
22
21
  if (instance && this.pk_field_name && instance[this.pk_field_name] != null) {
@@ -51,10 +50,6 @@ export class SyncManager {
51
50
  this.handleEvent = (event) => {
52
51
  let payload = new EventPayload(event);
53
52
  let isLocalOperation = operationRegistry.has(payload.operation_id);
54
- // Store canonical_id for upcoming refetches
55
- if (event.canonical_id) {
56
- this.currentCanonicalId = event.canonical_id;
57
- }
58
53
  // Always process metrics immediately (they're lightweight)
59
54
  if (this.registries.has(MetricRegistry)) {
60
55
  this.processMetrics(payload);
@@ -102,9 +97,6 @@ export class SyncManager {
102
97
  this.batchStartTime = null;
103
98
  // SyncQueue
104
99
  this.syncQueue = new PQueue({ concurrency: 1 });
105
- // Namespace subscription tracking
106
- this.activeSubscriptions = new Map(); // semanticKey -> {queryset, subscribedAt}
107
- this.currentCanonicalId = null; // Store canonical_id from events
108
100
  }
109
101
  withTimeout(promise, ms) {
110
102
  // If no timeout specified, use 2x the periodic sync interval, or 30s as fallback
@@ -131,31 +123,15 @@ export class SyncManager {
131
123
  initializeAllEventReceivers();
132
124
  // Get all registered event receivers
133
125
  const eventReceivers = getAllEventReceivers();
134
- // Create event handler with connection lifecycle methods
135
- const eventHandlerWithLifecycle = this.handleEvent.bind(this);
136
- eventHandlerWithLifecycle.onReconnected = this.handleReconnect.bind(this);
137
- eventHandlerWithLifecycle.onDisconnected = this.handleDisconnect.bind(this);
138
126
  // Register the event handlers with each receiver
139
127
  eventReceivers.forEach((receiver, configKey) => {
140
128
  if (receiver) {
141
129
  // Model events go to handleEvent
142
- receiver.addModelEventHandler(eventHandlerWithLifecycle);
130
+ receiver.addModelEventHandler(this.handleEvent.bind(this));
143
131
  }
144
132
  });
145
133
  this.startPeriodicSync();
146
134
  }
147
- handleDisconnect() {
148
- // Clear local subscription state on disconnect
149
- this.activeSubscriptions.clear();
150
- console.log('[SyncManager] Cleared subscriptions on disconnect');
151
- }
152
- handleReconnect() {
153
- // Resubscribe to all active querysets after reconnect
154
- console.log('[SyncManager] Resubscribing to active querysets after reconnect');
155
- for (const queryset of this.followedQuerysets) {
156
- this.subscribeToNamespace(queryset);
157
- }
158
- }
159
135
  startPeriodicSync() {
160
136
  if (this.periodicSyncTimer)
161
137
  return;
@@ -231,97 +207,6 @@ export class SyncManager {
231
207
  this.maxWaitTimer = null;
232
208
  }
233
209
  }
234
- async subscribeToNamespace(queryset) {
235
- const key = queryset.semanticKey;
236
- // Skip if already subscribed
237
- if (this.activeSubscriptions.has(key))
238
- return;
239
- try {
240
- const config = getConfig();
241
- const backendConfig = config.backendConfigs[queryset.modelClass.configKey];
242
- if (!backendConfig) {
243
- console.error(`[SyncManager] Backend config not found for: ${queryset.modelClass.configKey}`);
244
- return;
245
- }
246
- const apiUrl = backendConfig.API_URL;
247
- // Get Pusher socket ID
248
- const eventReceiver = getEventReceiver(queryset.modelClass.configKey);
249
- const socketId = eventReceiver?.pusherClient?.connection?.socket_id;
250
- if (!socketId) {
251
- console.warn('[SyncManager] No socket_id available for subscription');
252
- return;
253
- }
254
- // Send subscription request to backend
255
- const response = await fetch(`${apiUrl}/namespaces/subscribe/`, {
256
- method: 'POST',
257
- headers: {
258
- 'Content-Type': 'application/json',
259
- },
260
- body: JSON.stringify({
261
- socket_id: socketId,
262
- model_name: queryset.modelClass.modelName,
263
- filter: queryset._filters // Backend extracts namespace from this
264
- })
265
- });
266
- const result = await response.json();
267
- if (!result.success) {
268
- throw new Error('Subscription failed');
269
- }
270
- // Subscribe to the namespace-specific Pusher channel
271
- const namespaceChannel = result.channel; // e.g., "django_app.Message:abc123hash"
272
- eventReceiver?.subscribe(namespaceChannel);
273
- this.activeSubscriptions.set(key, {
274
- queryset,
275
- subscribedAt: Date.now(),
276
- channel: namespaceChannel,
277
- namespace_hash: result.namespace_hash
278
- });
279
- console.log(`[SyncManager] Subscribed to namespace channel: ${namespaceChannel}`);
280
- }
281
- catch (error) {
282
- console.error('[SyncManager] Namespace subscription failed:', error);
283
- }
284
- }
285
- async unsubscribeFromNamespace(queryset) {
286
- const key = queryset.semanticKey;
287
- if (!this.activeSubscriptions.has(key))
288
- return;
289
- const subscription = this.activeSubscriptions.get(key);
290
- try {
291
- const config = getConfig();
292
- const backendConfig = config.backendConfigs[queryset.modelClass.configKey];
293
- if (!backendConfig) {
294
- console.error(`[SyncManager] Backend config not found for: ${queryset.modelClass.configKey}`);
295
- return;
296
- }
297
- const apiUrl = backendConfig.API_URL;
298
- const eventReceiver = getEventReceiver(queryset.modelClass.configKey);
299
- const socketId = eventReceiver?.pusherClient?.connection?.socket_id;
300
- if (!socketId)
301
- return;
302
- // Unsubscribe from the Pusher channel
303
- if (subscription.channel) {
304
- eventReceiver?.unsubscribe(subscription.channel);
305
- }
306
- // Notify backend to remove subscription
307
- await fetch(`${apiUrl}/namespaces/unsubscribe/`, {
308
- method: 'POST',
309
- headers: {
310
- 'Content-Type': 'application/json',
311
- },
312
- body: JSON.stringify({
313
- socket_id: socketId,
314
- model_name: queryset.modelClass.modelName,
315
- filter: queryset._filters
316
- })
317
- });
318
- this.activeSubscriptions.delete(key);
319
- console.log(`[SyncManager] Unsubscribed from namespace channel: ${subscription.channel}`);
320
- }
321
- catch (error) {
322
- console.error('[SyncManager] Namespace unsubscription failed:', error);
323
- }
324
- }
325
210
  followModel(registry, modelClass) {
326
211
  const models = this.followedModels.get(registry) || new Set();
327
212
  this.followedModels.set(registry, models);
@@ -423,9 +308,7 @@ export class SyncManager {
423
308
  // Sync all relevant stores for this model
424
309
  console.log(`[SyncManager] Syncing ${storesToSync.length} queryset stores for ${representativeEvent.model}`);
425
310
  storesToSync.forEach((store) => {
426
- this.syncQueue.add(() => this.withTimeout(store.sync({
427
- canonical_id: representativeEvent.canonical_id // Pass canonical_id
428
- })));
311
+ this.syncQueue.add(() => this.withTimeout(store.sync()));
429
312
  });
430
313
  }
431
314
  processMetrics(event) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statezero/core",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
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",
@@ -32,7 +32,6 @@
32
32
  "test:e2e": "vitest run --config=vitest.sequential.config.ts tests/e2e",
33
33
  "generate:test-apps": "ts-node scripts/generate-test-apps.js",
34
34
  "test:adaptors": "playwright test tests/adaptors",
35
- "test:integration": "playwright test --config=playwright.integration.config.ts",
36
35
  "test:coverage": "vitest run --coverage",
37
36
  "build": "tsc",
38
37
  "parse-queries": "node scripts/perfect-query-parser.js",