@objectstack/client 4.0.2 → 4.0.3

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.mjs CHANGED
@@ -2,6 +2,135 @@
2
2
  import { isFilterAST } from "@objectstack/spec/data";
3
3
  import { createLogger } from "@objectstack/core";
4
4
 
5
+ // src/realtime-api.ts
6
+ var RealtimeAPI = class {
7
+ constructor(baseUrl, token) {
8
+ this.subscriptions = /* @__PURE__ */ new Map();
9
+ this.eventBuffer = [];
10
+ this._baseUrl = baseUrl;
11
+ this._token = token;
12
+ }
13
+ /**
14
+ * Subscribe to metadata events
15
+ * Returns an unsubscribe function
16
+ */
17
+ subscribeMetadata(type, callback, options) {
18
+ const subscriptionId = `metadata-${type}-${Date.now()}`;
19
+ this.subscriptions.set(subscriptionId, {
20
+ filter: {
21
+ type,
22
+ packageId: options?.packageId,
23
+ eventTypes: [
24
+ `metadata.${type}.created`,
25
+ `metadata.${type}.updated`,
26
+ `metadata.${type}.deleted`
27
+ ]
28
+ },
29
+ handler: (event) => {
30
+ if (event.type.startsWith("metadata.")) {
31
+ callback(event);
32
+ }
33
+ }
34
+ });
35
+ this.startPolling();
36
+ return () => {
37
+ this.subscriptions.delete(subscriptionId);
38
+ if (this.subscriptions.size === 0) {
39
+ this.stopPolling();
40
+ }
41
+ };
42
+ }
43
+ /**
44
+ * Subscribe to data record events
45
+ * Returns an unsubscribe function
46
+ */
47
+ subscribeData(object, callback, options) {
48
+ const subscriptionId = `data-${object}-${Date.now()}`;
49
+ this.subscriptions.set(subscriptionId, {
50
+ filter: {
51
+ type: object,
52
+ recordId: options?.recordId,
53
+ eventTypes: [
54
+ "data.record.created",
55
+ "data.record.updated",
56
+ "data.record.deleted"
57
+ ]
58
+ },
59
+ handler: (event) => {
60
+ if (event.type.startsWith("data.") && event.object === object) {
61
+ if (!options?.recordId || event.payload?.recordId === options.recordId) {
62
+ callback(event);
63
+ }
64
+ }
65
+ }
66
+ });
67
+ this.startPolling();
68
+ return () => {
69
+ this.subscriptions.delete(subscriptionId);
70
+ if (this.subscriptions.size === 0) {
71
+ this.stopPolling();
72
+ }
73
+ };
74
+ }
75
+ /**
76
+ * Emit an event to all matching subscriptions (client-side only)
77
+ * This is used for in-process event delivery
78
+ */
79
+ emitEvent(event) {
80
+ for (const sub of this.subscriptions.values()) {
81
+ const matchesType = !sub.filter.type || event.type.includes(sub.filter.type) || event.object === sub.filter.type;
82
+ const matchesEventType = !sub.filter.eventTypes?.length || sub.filter.eventTypes.includes(event.type);
83
+ const matchesPackage = !sub.filter.packageId || event.payload?.packageId === sub.filter.packageId;
84
+ if (matchesType && matchesEventType && matchesPackage) {
85
+ try {
86
+ sub.handler(event);
87
+ } catch (error) {
88
+ console.error("Error in realtime event handler:", error);
89
+ }
90
+ }
91
+ }
92
+ }
93
+ /**
94
+ * Start polling for events (fallback mechanism)
95
+ * In production, this would be replaced with WebSocket/SSE
96
+ */
97
+ startPolling() {
98
+ if (this.pollInterval) return;
99
+ this.pollInterval = setInterval(() => {
100
+ while (this.eventBuffer.length > 0) {
101
+ const event = this.eventBuffer.shift();
102
+ if (event) {
103
+ this.emitEvent(event);
104
+ }
105
+ }
106
+ }, 2e3);
107
+ }
108
+ /**
109
+ * Stop polling for events
110
+ */
111
+ stopPolling() {
112
+ if (this.pollInterval) {
113
+ clearInterval(this.pollInterval);
114
+ this.pollInterval = void 0;
115
+ }
116
+ }
117
+ /**
118
+ * Internal method to buffer events from server
119
+ * This would be called by WebSocket/SSE handlers in production
120
+ */
121
+ _bufferEvent(event) {
122
+ this.eventBuffer.push(event);
123
+ }
124
+ /**
125
+ * Disconnect and clean up all subscriptions
126
+ */
127
+ disconnect() {
128
+ this.stopPolling();
129
+ this.subscriptions.clear();
130
+ this.eventBuffer = [];
131
+ }
132
+ };
133
+
5
134
  // src/query-builder.ts
6
135
  var FilterBuilder = class {
7
136
  constructor() {
@@ -1472,6 +1601,7 @@ var ObjectStackClient = class {
1472
1601
  level: config.debug ? "debug" : "info",
1473
1602
  format: "pretty"
1474
1603
  });
1604
+ this.realtimeAPI = new RealtimeAPI(this.baseUrl, this.token);
1475
1605
  this.logger.debug("ObjectStack client created", { baseUrl: this.baseUrl });
1476
1606
  }
1477
1607
  /**
@@ -1542,6 +1672,13 @@ var ObjectStackClient = class {
1542
1672
  }
1543
1673
  return result;
1544
1674
  }
1675
+ /**
1676
+ * Event Subscription API
1677
+ * Provides real-time event subscriptions for metadata and data changes
1678
+ */
1679
+ get events() {
1680
+ return this.realtimeAPI;
1681
+ }
1545
1682
  /**
1546
1683
  * Private Helpers
1547
1684
  */
@@ -1645,6 +1782,7 @@ export {
1645
1782
  FilterBuilder,
1646
1783
  ObjectStackClient,
1647
1784
  QueryBuilder,
1785
+ RealtimeAPI,
1648
1786
  createFilter,
1649
1787
  createQuery
1650
1788
  };