@mastra/google-cloud-pubsub 1.1.2 → 1.1.3-alpha.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.js CHANGED
@@ -1,253 +1,198 @@
1
- import { PubSub as PubSub$1 } from '@google-cloud/pubsub';
2
- import { PubSub } from '@mastra/core/events';
3
-
4
- // src/index.ts
5
- var GoogleCloudPubSub = class extends PubSub {
6
- instanceId;
7
- pubsub;
8
- ackBuffer = {};
9
- activeSubscriptions = {};
10
- activeCbs = {};
11
- // `localOnly` publishes never touch Google Cloud — they are delivered to
12
- // same-process subscribers only. Tracks live callbacks per (normalized) topic
13
- // so we can fan out without going through PubSub.
14
- localCallbacks = /* @__PURE__ */ new Map();
15
- // Coalesces concurrent init() calls for the same subscription so racing
16
- // subscribers (e.g. a producer stream and a consumer observe on the same
17
- // run topic) share a single createTopic/createSubscription attempt.
18
- inFlightInit = {};
19
- // Tracks the actual anonymous message listener registered on each subscription,
20
- // so we can remove it cleanly on the final unsubscribe.
21
- messageListeners = {};
22
- constructor(config) {
23
- super();
24
- this.pubsub = new PubSub$1(config);
25
- this.instanceId = crypto.randomUUID();
26
- }
27
- getSubscriptionName(topic, group) {
28
- if (group) {
29
- return `${topic}-${group}`;
30
- }
31
- return `${topic}-${this.instanceId}`;
32
- }
33
- async ackMessage(topic, message) {
34
- try {
35
- const ackResponse = Promise.race([message.ackWithResponse(), new Promise((resolve) => setTimeout(resolve, 5e3))]);
36
- this.ackBuffer[topic + "-" + message.id] = ackResponse.catch(() => {
37
- });
38
- await ackResponse;
39
- delete this.ackBuffer[topic + "-" + message.id];
40
- } catch (e) {
41
- console.error("Error acking message", e);
42
- }
43
- }
44
- async init(topicName, group) {
45
- const subscriptionKey = group ? `${topicName}:${group}` : topicName;
46
- if (this.inFlightInit[subscriptionKey]) {
47
- return this.inFlightInit[subscriptionKey];
48
- }
49
- const subscriptionName = this.getSubscriptionName(topicName, group);
50
- const initPromise = (async () => {
51
- try {
52
- await this.pubsub.createTopic(topicName);
53
- } catch {
54
- }
55
- try {
56
- const [sub] = await this.pubsub.topic(topicName).createSubscription(subscriptionName, {
57
- enableMessageOrdering: true,
58
- enableExactlyOnceDelivery: topicName === "workflows" || !!group
59
- });
60
- this.activeSubscriptions[subscriptionKey] = sub;
61
- return sub;
62
- } catch (error) {
63
- const alreadyExists = error?.code === 6;
64
- if (alreadyExists || group) {
65
- try {
66
- const sub = this.pubsub.subscription(subscriptionName);
67
- this.activeSubscriptions[subscriptionKey] = sub;
68
- return sub;
69
- } catch {
70
- }
71
- }
72
- }
73
- return void 0;
74
- })().finally(() => {
75
- delete this.inFlightInit[subscriptionKey];
76
- });
77
- this.inFlightInit[subscriptionKey] = initPromise;
78
- return initPromise;
79
- }
80
- async destroy(topicName) {
81
- const subName = this.getSubscriptionName(topicName);
82
- delete this.activeSubscriptions[topicName];
83
- this.pubsub.subscription(subName).removeAllListeners();
84
- await this.pubsub.subscription(subName).close();
85
- await this.pubsub.subscription(subName).delete();
86
- await this.pubsub.topic(topicName).delete();
87
- }
88
- async publish(topicName, event, options) {
89
- if (topicName.startsWith("workflow.events.")) {
90
- const parts = topicName.split(".");
91
- if (parts[parts.length - 2] === "v2") {
92
- topicName = "workflow.events.v2";
93
- } else {
94
- topicName = "workflow.events.v1";
95
- }
96
- }
97
- if (options?.localOnly) {
98
- await this.deliverLocal(topicName, event);
99
- return;
100
- }
101
- let topic = this.pubsub.topic(topicName);
102
- try {
103
- await topic.publishMessage({
104
- data: Buffer.from(JSON.stringify(event)),
105
- orderingKey: "workflows"
106
- });
107
- } catch (e) {
108
- if (e.code === 5) {
109
- await this.pubsub.createTopic(topicName);
110
- await this.publish(topicName, event);
111
- } else {
112
- throw e;
113
- }
114
- }
115
- }
116
- async subscribe(topic, cb, options) {
117
- if (topic.startsWith("workflow.events.")) {
118
- const parts = topic.split(".");
119
- if (parts[parts.length - 2] === "v2") {
120
- topic = "workflow.events.v2";
121
- } else {
122
- topic = "workflow.events.v1";
123
- }
124
- }
125
- const group = options?.group;
126
- const subscriptionKey = group ? `${topic}:${group}` : topic;
127
- let localSet = this.localCallbacks.get(topic);
128
- if (!localSet) {
129
- localSet = /* @__PURE__ */ new Set();
130
- this.localCallbacks.set(topic, localSet);
131
- }
132
- localSet.add(cb);
133
- const subscription = this.activeSubscriptions[subscriptionKey] ?? await this.init(topic, group);
134
- if (!subscription) {
135
- throw new Error(`Failed to subscribe to topic: ${topic}`);
136
- }
137
- this.activeSubscriptions[subscriptionKey] = subscription;
138
- const activeCbs = this.activeCbs[subscriptionKey] ?? /* @__PURE__ */ new Set();
139
- activeCbs.add(cb);
140
- this.activeCbs[subscriptionKey] = activeCbs;
141
- if (subscription.isOpen) {
142
- return;
143
- }
144
- const messageListener = async (message) => {
145
- const event = JSON.parse(message.data.toString());
146
- event.id = message.id;
147
- event.createdAt = message.publishTime;
148
- event.deliveryAttempt = message.deliveryAttempt ?? 1;
149
- try {
150
- const activeCbs2 = this.activeCbs[subscriptionKey] ?? [];
151
- for (const cb2 of activeCbs2) {
152
- cb2(
153
- event,
154
- async () => {
155
- try {
156
- await this.ackMessage(subscriptionKey, message);
157
- } catch (e) {
158
- console.error("Error acking message", e);
159
- }
160
- },
161
- async () => {
162
- try {
163
- message.nack();
164
- } catch (e) {
165
- console.error("Error nacking message", e);
166
- }
167
- }
168
- );
169
- }
170
- } catch (error) {
171
- console.error("Error processing event", error);
172
- }
173
- };
174
- this.messageListeners[subscriptionKey] = messageListener;
175
- subscription.on("message", messageListener);
176
- subscription.on("error", async (error) => {
177
- console.error("subscription error", error);
178
- });
179
- }
180
- async unsubscribe(topic, cb) {
181
- if (topic.startsWith("workflow.events.")) {
182
- const parts = topic.split(".");
183
- if (parts[parts.length - 2] === "v2") {
184
- topic = "workflow.events.v2";
185
- } else {
186
- topic = "workflow.events.v1";
187
- }
188
- }
189
- const localSet = this.localCallbacks.get(topic);
190
- if (localSet?.delete(cb) && localSet.size === 0) {
191
- this.localCallbacks.delete(topic);
192
- }
193
- const keysToCheck = [topic];
194
- for (const key of Object.keys(this.activeCbs)) {
195
- if (key.startsWith(`${topic}:`) && !keysToCheck.includes(key)) {
196
- keysToCheck.push(key);
197
- }
198
- }
199
- for (const subscriptionKey of keysToCheck) {
200
- const activeCbs = this.activeCbs[subscriptionKey];
201
- if (activeCbs?.has(cb)) {
202
- activeCbs.delete(cb);
203
- if (activeCbs.size === 0) {
204
- const subscription = this.activeSubscriptions[subscriptionKey];
205
- const listener = this.messageListeners[subscriptionKey];
206
- if (subscription) {
207
- if (listener) subscription.removeListener("message", listener);
208
- await subscription.close();
209
- }
210
- delete this.activeSubscriptions[subscriptionKey];
211
- delete this.activeCbs[subscriptionKey];
212
- delete this.messageListeners[subscriptionKey];
213
- }
214
- return;
215
- }
216
- }
217
- }
218
- async flush() {
219
- await Promise.all(Object.values(this.ackBuffer));
220
- }
221
- /**
222
- * Fan a `localOnly` event out to in-process subscribers without going through
223
- * Google Cloud. The payload is delivered by reference, so live class instances
224
- * and functions on the event survive intact.
225
- */
226
- async deliverLocal(topicName, event) {
227
- const callbacks = this.localCallbacks.get(topicName);
228
- if (!callbacks || callbacks.size === 0) return;
229
- const localEvent = {
230
- ...event,
231
- id: crypto.randomUUID(),
232
- createdAt: /* @__PURE__ */ new Date(),
233
- deliveryAttempt: 1
234
- };
235
- for (const cb of [...callbacks]) {
236
- try {
237
- cb(
238
- localEvent,
239
- async () => {
240
- },
241
- async () => {
242
- }
243
- );
244
- } catch (error) {
245
- console.error("Error delivering local event", error);
246
- }
247
- }
248
- }
1
+ import { PubSub } from "@google-cloud/pubsub";
2
+ import { PubSub as PubSub$1 } from "@mastra/core/events";
3
+ //#region src/index.ts
4
+ var GoogleCloudPubSub = class extends PubSub$1 {
5
+ instanceId;
6
+ pubsub;
7
+ ackBuffer = {};
8
+ activeSubscriptions = {};
9
+ activeCbs = {};
10
+ localCallbacks = /* @__PURE__ */ new Map();
11
+ inFlightInit = {};
12
+ messageListeners = {};
13
+ constructor(config) {
14
+ super();
15
+ this.pubsub = new PubSub(config);
16
+ this.instanceId = crypto.randomUUID();
17
+ }
18
+ getSubscriptionName(topic, group) {
19
+ if (group) return `${topic}-${group}`;
20
+ return `${topic}-${this.instanceId}`;
21
+ }
22
+ async ackMessage(topic, message) {
23
+ try {
24
+ const ackResponse = Promise.race([message.ackWithResponse(), new Promise((resolve) => setTimeout(resolve, 5e3))]);
25
+ this.ackBuffer[topic + "-" + message.id] = ackResponse.catch(() => {});
26
+ await ackResponse;
27
+ delete this.ackBuffer[topic + "-" + message.id];
28
+ } catch (e) {
29
+ console.error("Error acking message", e);
30
+ }
31
+ }
32
+ async init(topicName, group) {
33
+ const subscriptionKey = group ? `${topicName}:${group}` : topicName;
34
+ if (this.inFlightInit[subscriptionKey]) return this.inFlightInit[subscriptionKey];
35
+ const subscriptionName = this.getSubscriptionName(topicName, group);
36
+ const initPromise = (async () => {
37
+ try {
38
+ await this.pubsub.createTopic(topicName);
39
+ } catch {}
40
+ try {
41
+ const [sub] = await this.pubsub.topic(topicName).createSubscription(subscriptionName, {
42
+ enableMessageOrdering: true,
43
+ enableExactlyOnceDelivery: topicName === "workflows" || !!group
44
+ });
45
+ this.activeSubscriptions[subscriptionKey] = sub;
46
+ return sub;
47
+ } catch (error) {
48
+ if (error?.code === 6 || group) try {
49
+ const sub = this.pubsub.subscription(subscriptionName);
50
+ this.activeSubscriptions[subscriptionKey] = sub;
51
+ return sub;
52
+ } catch {}
53
+ }
54
+ })().finally(() => {
55
+ delete this.inFlightInit[subscriptionKey];
56
+ });
57
+ this.inFlightInit[subscriptionKey] = initPromise;
58
+ return initPromise;
59
+ }
60
+ async destroy(topicName) {
61
+ const subName = this.getSubscriptionName(topicName);
62
+ delete this.activeSubscriptions[topicName];
63
+ this.pubsub.subscription(subName).removeAllListeners();
64
+ await this.pubsub.subscription(subName).close();
65
+ await this.pubsub.subscription(subName).delete();
66
+ await this.pubsub.topic(topicName).delete();
67
+ }
68
+ async publish(topicName, event, options) {
69
+ if (topicName.startsWith("workflow.events.")) {
70
+ const parts = topicName.split(".");
71
+ if (parts[parts.length - 2] === "v2") topicName = "workflow.events.v2";
72
+ else topicName = "workflow.events.v1";
73
+ }
74
+ if (options?.localOnly) {
75
+ await this.deliverLocal(topicName, event);
76
+ return;
77
+ }
78
+ let topic = this.pubsub.topic(topicName);
79
+ try {
80
+ await topic.publishMessage({
81
+ data: Buffer.from(JSON.stringify(event)),
82
+ orderingKey: "workflows"
83
+ });
84
+ } catch (e) {
85
+ if (e.code === 5) {
86
+ await this.pubsub.createTopic(topicName);
87
+ await this.publish(topicName, event);
88
+ } else throw e;
89
+ }
90
+ }
91
+ async subscribe(topic, cb, options) {
92
+ if (topic.startsWith("workflow.events.")) {
93
+ const parts = topic.split(".");
94
+ if (parts[parts.length - 2] === "v2") topic = "workflow.events.v2";
95
+ else topic = "workflow.events.v1";
96
+ }
97
+ const group = options?.group;
98
+ const subscriptionKey = group ? `${topic}:${group}` : topic;
99
+ let localSet = this.localCallbacks.get(topic);
100
+ if (!localSet) {
101
+ localSet = /* @__PURE__ */ new Set();
102
+ this.localCallbacks.set(topic, localSet);
103
+ }
104
+ localSet.add(cb);
105
+ const subscription = this.activeSubscriptions[subscriptionKey] ?? await this.init(topic, group);
106
+ if (!subscription) throw new Error(`Failed to subscribe to topic: ${topic}`);
107
+ this.activeSubscriptions[subscriptionKey] = subscription;
108
+ const activeCbs = this.activeCbs[subscriptionKey] ?? /* @__PURE__ */ new Set();
109
+ activeCbs.add(cb);
110
+ this.activeCbs[subscriptionKey] = activeCbs;
111
+ if (subscription.isOpen) return;
112
+ const messageListener = async (message) => {
113
+ const event = JSON.parse(message.data.toString());
114
+ event.id = message.id;
115
+ event.createdAt = message.publishTime;
116
+ event.deliveryAttempt = message.deliveryAttempt ?? 1;
117
+ try {
118
+ const activeCbs = this.activeCbs[subscriptionKey] ?? [];
119
+ for (const cb of activeCbs) cb(event, async () => {
120
+ try {
121
+ await this.ackMessage(subscriptionKey, message);
122
+ } catch (e) {
123
+ console.error("Error acking message", e);
124
+ }
125
+ }, async () => {
126
+ try {
127
+ message.nack();
128
+ } catch (e) {
129
+ console.error("Error nacking message", e);
130
+ }
131
+ });
132
+ } catch (error) {
133
+ console.error("Error processing event", error);
134
+ }
135
+ };
136
+ this.messageListeners[subscriptionKey] = messageListener;
137
+ subscription.on("message", messageListener);
138
+ subscription.on("error", async (error) => {
139
+ console.error("subscription error", error);
140
+ });
141
+ }
142
+ async unsubscribe(topic, cb) {
143
+ if (topic.startsWith("workflow.events.")) {
144
+ const parts = topic.split(".");
145
+ if (parts[parts.length - 2] === "v2") topic = "workflow.events.v2";
146
+ else topic = "workflow.events.v1";
147
+ }
148
+ const localSet = this.localCallbacks.get(topic);
149
+ if (localSet?.delete(cb) && localSet.size === 0) this.localCallbacks.delete(topic);
150
+ const keysToCheck = [topic];
151
+ for (const key of Object.keys(this.activeCbs)) if (key.startsWith(`${topic}:`) && !keysToCheck.includes(key)) keysToCheck.push(key);
152
+ for (const subscriptionKey of keysToCheck) {
153
+ const activeCbs = this.activeCbs[subscriptionKey];
154
+ if (activeCbs?.has(cb)) {
155
+ activeCbs.delete(cb);
156
+ if (activeCbs.size === 0) {
157
+ const subscription = this.activeSubscriptions[subscriptionKey];
158
+ const listener = this.messageListeners[subscriptionKey];
159
+ if (subscription) {
160
+ if (listener) subscription.removeListener("message", listener);
161
+ await subscription.close();
162
+ }
163
+ delete this.activeSubscriptions[subscriptionKey];
164
+ delete this.activeCbs[subscriptionKey];
165
+ delete this.messageListeners[subscriptionKey];
166
+ }
167
+ return;
168
+ }
169
+ }
170
+ }
171
+ async flush() {
172
+ await Promise.all(Object.values(this.ackBuffer));
173
+ }
174
+ /**
175
+ * Fan a `localOnly` event out to in-process subscribers without going through
176
+ * Google Cloud. The payload is delivered by reference, so live class instances
177
+ * and functions on the event survive intact.
178
+ */
179
+ async deliverLocal(topicName, event) {
180
+ const callbacks = this.localCallbacks.get(topicName);
181
+ if (!callbacks || callbacks.size === 0) return;
182
+ const localEvent = {
183
+ ...event,
184
+ id: crypto.randomUUID(),
185
+ createdAt: /* @__PURE__ */ new Date(),
186
+ deliveryAttempt: 1
187
+ };
188
+ for (const cb of [...callbacks]) try {
189
+ cb(localEvent, async () => {}, async () => {});
190
+ } catch (error) {
191
+ console.error("Error delivering local event", error);
192
+ }
193
+ }
249
194
  };
250
-
195
+ //#endregion
251
196
  export { GoogleCloudPubSub };
252
- //# sourceMappingURL=index.js.map
197
+
253
198
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["PubSubClient","activeCbs","cb"],"mappings":"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","file":"index.js","sourcesContent":["import { PubSub as PubSubClient } from '@google-cloud/pubsub';\nimport type { ClientConfig, Message, Subscription } from '@google-cloud/pubsub';\nimport { PubSub } from '@mastra/core/events';\nimport type { Event, EventCallback, SubscribeOptions } from '@mastra/core/events';\n\nexport class GoogleCloudPubSub extends PubSub {\n private instanceId: string;\n private pubsub: PubSubClient;\n private ackBuffer: Record<string, Promise<any>> = {};\n private activeSubscriptions: Record<string, Subscription> = {};\n private activeCbs: Record<string, Set<EventCallback>> = {};\n // `localOnly` publishes never touch Google Cloud — they are delivered to\n // same-process subscribers only. Tracks live callbacks per (normalized) topic\n // so we can fan out without going through PubSub.\n private localCallbacks: Map<string, Set<EventCallback>> = new Map();\n // Coalesces concurrent init() calls for the same subscription so racing\n // subscribers (e.g. a producer stream and a consumer observe on the same\n // run topic) share a single createTopic/createSubscription attempt.\n private inFlightInit: Record<string, Promise<Subscription | undefined>> = {};\n // Tracks the actual anonymous message listener registered on each subscription,\n // so we can remove it cleanly on the final unsubscribe.\n private messageListeners: Record<string, (message: Message) => void> = {};\n\n constructor(config: ClientConfig) {\n super();\n this.pubsub = new PubSubClient(config);\n this.instanceId = crypto.randomUUID();\n }\n\n getSubscriptionName(topic: string, group?: string) {\n if (group) {\n return `${topic}-${group}`;\n }\n return `${topic}-${this.instanceId}`;\n }\n\n async ackMessage(topic: string, message: Message) {\n try {\n const ackResponse = Promise.race([message.ackWithResponse(), new Promise(resolve => setTimeout(resolve, 5000))]);\n this.ackBuffer[topic + '-' + message.id] = ackResponse.catch(() => {});\n await ackResponse;\n delete this.ackBuffer[topic + '-' + message.id];\n } catch (e) {\n console.error('Error acking message', e);\n }\n }\n\n async init(topicName: string, group?: string): Promise<Subscription | undefined> {\n const subscriptionKey = group ? `${topicName}:${group}` : topicName;\n\n // Reuse an in-flight init so concurrent subscribers don't race to create the\n // same subscription. The promise is registered synchronously below (before any\n // await), so a second caller arriving during the create window reuses it.\n if (this.inFlightInit[subscriptionKey]) {\n return this.inFlightInit[subscriptionKey];\n }\n\n const subscriptionName = this.getSubscriptionName(topicName, group);\n const initPromise = (async (): Promise<Subscription | undefined> => {\n try {\n await this.pubsub.createTopic(topicName);\n } catch {\n // no-op\n }\n try {\n const [sub] = await this.pubsub.topic(topicName).createSubscription(subscriptionName, {\n enableMessageOrdering: true,\n enableExactlyOnceDelivery: topicName === 'workflows' || !!group,\n });\n this.activeSubscriptions[subscriptionKey] = sub;\n return sub;\n } catch (error) {\n // The subscription may already exist: created concurrently by a racing\n // subscriber (ALREADY_EXISTS / gRPC code 6), shared by another process via\n // a group, or surviving a previous process. In all of these cases attach to\n // the existing subscription instead of failing. Ungrouped subscriptions hit\n // this on the concurrent-create race, so we must not gate it on `group`.\n const alreadyExists = (error as { code?: number } | undefined)?.code === 6;\n if (alreadyExists || group) {\n try {\n const sub = this.pubsub.subscription(subscriptionName);\n this.activeSubscriptions[subscriptionKey] = sub;\n return sub;\n } catch {\n // no-op\n }\n }\n }\n return undefined;\n })().finally(() => {\n delete this.inFlightInit[subscriptionKey];\n });\n\n this.inFlightInit[subscriptionKey] = initPromise;\n return initPromise;\n }\n\n async destroy(topicName: string) {\n const subName = this.getSubscriptionName(topicName);\n delete this.activeSubscriptions[topicName];\n this.pubsub.subscription(subName).removeAllListeners();\n await this.pubsub.subscription(subName).close();\n await this.pubsub.subscription(subName).delete();\n await this.pubsub.topic(topicName).delete();\n }\n\n async publish(\n topicName: string,\n event: Omit<Event, 'id' | 'createdAt'>,\n options?: { localOnly?: boolean },\n ): Promise<void> {\n if (topicName.startsWith('workflow.events.')) {\n const parts = topicName.split('.');\n if (parts[parts.length - 2] === 'v2') {\n topicName = 'workflow.events.v2';\n } else {\n topicName = 'workflow.events.v1';\n }\n }\n\n // `localOnly` events stay entirely within the publishing process. They are\n // never serialized through Google Cloud, so live methods on payload values\n // (e.g. `MastraModelOutput.getFullOutput`) survive intact. The agent's\n // execution-workflow relies on this: the run result is delivered via\n // `workflows-finish` and includes the `MastraModelOutput` instance —\n // round-tripping it through Pub/Sub would strip its methods.\n if (options?.localOnly) {\n await this.deliverLocal(topicName, event);\n return;\n }\n\n let topic = this.pubsub.topic(topicName);\n\n try {\n await topic.publishMessage({\n data: Buffer.from(JSON.stringify(event)),\n orderingKey: 'workflows',\n });\n } catch (e: any) {\n if (e.code === 5) {\n await this.pubsub.createTopic(topicName);\n await this.publish(topicName, event);\n } else {\n throw e;\n }\n }\n }\n\n async subscribe(topic: string, cb: EventCallback, options?: SubscribeOptions): Promise<void> {\n if (topic.startsWith('workflow.events.')) {\n const parts = topic.split('.');\n if (parts[parts.length - 2] === 'v2') {\n topic = 'workflow.events.v2';\n } else {\n topic = 'workflow.events.v1';\n }\n }\n\n const group = options?.group;\n // Use a composite key when group is set so grouped and non-grouped subscriptions\n // on the same topic don't collide\n const subscriptionKey = group ? `${topic}:${group}` : topic;\n\n // Register callback for `localOnly` delivery. Local delivery bypasses Google\n // Cloud entirely so live class instances on the payload (e.g. Date, Map,\n // Error, MastraModelOutput) keep their prototypes.\n let localSet = this.localCallbacks.get(topic);\n if (!localSet) {\n localSet = new Set();\n this.localCallbacks.set(topic, localSet);\n }\n localSet.add(cb);\n\n // Update tracked callbacks\n const subscription = this.activeSubscriptions[subscriptionKey] ?? (await this.init(topic, group));\n if (!subscription) {\n throw new Error(`Failed to subscribe to topic: ${topic}`);\n }\n\n this.activeSubscriptions[subscriptionKey] = subscription;\n\n const activeCbs = this.activeCbs[subscriptionKey] ?? new Set();\n activeCbs.add(cb);\n this.activeCbs[subscriptionKey] = activeCbs;\n\n if (subscription.isOpen) {\n return;\n }\n\n const messageListener = async (message: Message) => {\n const event = JSON.parse(message.data.toString()) as Event;\n event.id = message.id;\n event.createdAt = message.publishTime;\n event.deliveryAttempt = message.deliveryAttempt ?? 1;\n\n try {\n const activeCbs = this.activeCbs[subscriptionKey] ?? [];\n for (const cb of activeCbs) {\n cb(\n event,\n async () => {\n try {\n await this.ackMessage(subscriptionKey, message);\n } catch (e) {\n console.error('Error acking message', e);\n }\n },\n async () => {\n try {\n message.nack();\n } catch (e) {\n console.error('Error nacking message', e);\n }\n },\n );\n }\n } catch (error) {\n console.error('Error processing event', error);\n }\n };\n\n this.messageListeners[subscriptionKey] = messageListener;\n subscription.on('message', messageListener);\n\n subscription.on('error', async error => {\n console.error('subscription error', error);\n });\n }\n\n async unsubscribe(topic: string, cb: EventCallback): Promise<void> {\n if (topic.startsWith('workflow.events.')) {\n const parts = topic.split('.');\n if (parts[parts.length - 2] === 'v2') {\n topic = 'workflow.events.v2';\n } else {\n topic = 'workflow.events.v1';\n }\n }\n\n // Drop from the local-delivery set; if nobody is left, tear down the bucket.\n const localSet = this.localCallbacks.get(topic);\n if (localSet?.delete(cb) && localSet.size === 0) {\n this.localCallbacks.delete(topic);\n }\n\n // Check both grouped and non-grouped subscription keys for this callback\n const keysToCheck = [topic];\n for (const key of Object.keys(this.activeCbs)) {\n if (key.startsWith(`${topic}:`) && !keysToCheck.includes(key)) {\n keysToCheck.push(key);\n }\n }\n\n for (const subscriptionKey of keysToCheck) {\n const activeCbs = this.activeCbs[subscriptionKey];\n if (activeCbs?.has(cb)) {\n activeCbs.delete(cb);\n\n if (activeCbs.size === 0) {\n const subscription = this.activeSubscriptions[subscriptionKey];\n const listener = this.messageListeners[subscriptionKey];\n if (subscription) {\n if (listener) subscription.removeListener('message', listener);\n await subscription.close();\n }\n delete this.activeSubscriptions[subscriptionKey];\n delete this.activeCbs[subscriptionKey];\n delete this.messageListeners[subscriptionKey];\n }\n return;\n }\n }\n }\n\n async flush(): Promise<void> {\n await Promise.all(Object.values(this.ackBuffer));\n }\n\n /**\n * Fan a `localOnly` event out to in-process subscribers without going through\n * Google Cloud. The payload is delivered by reference, so live class instances\n * and functions on the event survive intact.\n */\n private async deliverLocal(topicName: string, event: Omit<Event, 'id' | 'createdAt'>): Promise<void> {\n const callbacks = this.localCallbacks.get(topicName);\n if (!callbacks || callbacks.size === 0) return;\n\n const localEvent: Event = {\n ...event,\n id: crypto.randomUUID(),\n createdAt: new Date(),\n deliveryAttempt: 1,\n };\n\n for (const cb of [...callbacks]) {\n try {\n cb(\n localEvent,\n async () => {},\n async () => {},\n );\n } catch (error) {\n console.error('Error delivering local event', error);\n }\n }\n }\n}\n"]}
1
+ {"version":3,"file":"index.js","names":["PubSub","PubSubClient"],"sources":["../src/index.ts"],"sourcesContent":["import { PubSub as PubSubClient } from '@google-cloud/pubsub';\nimport type { ClientConfig, Message, Subscription } from '@google-cloud/pubsub';\nimport { PubSub } from '@mastra/core/events';\nimport type { Event, EventCallback, SubscribeOptions } from '@mastra/core/events';\n\nexport class GoogleCloudPubSub extends PubSub {\n private instanceId: string;\n private pubsub: PubSubClient;\n private ackBuffer: Record<string, Promise<any>> = {};\n private activeSubscriptions: Record<string, Subscription> = {};\n private activeCbs: Record<string, Set<EventCallback>> = {};\n // `localOnly` publishes never touch Google Cloud — they are delivered to\n // same-process subscribers only. Tracks live callbacks per (normalized) topic\n // so we can fan out without going through PubSub.\n private localCallbacks: Map<string, Set<EventCallback>> = new Map();\n // Coalesces concurrent init() calls for the same subscription so racing\n // subscribers (e.g. a producer stream and a consumer observe on the same\n // run topic) share a single createTopic/createSubscription attempt.\n private inFlightInit: Record<string, Promise<Subscription | undefined>> = {};\n // Tracks the actual anonymous message listener registered on each subscription,\n // so we can remove it cleanly on the final unsubscribe.\n private messageListeners: Record<string, (message: Message) => void> = {};\n\n constructor(config: ClientConfig) {\n super();\n this.pubsub = new PubSubClient(config);\n this.instanceId = crypto.randomUUID();\n }\n\n getSubscriptionName(topic: string, group?: string) {\n if (group) {\n return `${topic}-${group}`;\n }\n return `${topic}-${this.instanceId}`;\n }\n\n async ackMessage(topic: string, message: Message) {\n try {\n const ackResponse = Promise.race([message.ackWithResponse(), new Promise(resolve => setTimeout(resolve, 5000))]);\n this.ackBuffer[topic + '-' + message.id] = ackResponse.catch(() => {});\n await ackResponse;\n delete this.ackBuffer[topic + '-' + message.id];\n } catch (e) {\n console.error('Error acking message', e);\n }\n }\n\n async init(topicName: string, group?: string): Promise<Subscription | undefined> {\n const subscriptionKey = group ? `${topicName}:${group}` : topicName;\n\n // Reuse an in-flight init so concurrent subscribers don't race to create the\n // same subscription. The promise is registered synchronously below (before any\n // await), so a second caller arriving during the create window reuses it.\n if (this.inFlightInit[subscriptionKey]) {\n return this.inFlightInit[subscriptionKey];\n }\n\n const subscriptionName = this.getSubscriptionName(topicName, group);\n const initPromise = (async (): Promise<Subscription | undefined> => {\n try {\n await this.pubsub.createTopic(topicName);\n } catch {\n // no-op\n }\n try {\n const [sub] = await this.pubsub.topic(topicName).createSubscription(subscriptionName, {\n enableMessageOrdering: true,\n enableExactlyOnceDelivery: topicName === 'workflows' || !!group,\n });\n this.activeSubscriptions[subscriptionKey] = sub;\n return sub;\n } catch (error) {\n // The subscription may already exist: created concurrently by a racing\n // subscriber (ALREADY_EXISTS / gRPC code 6), shared by another process via\n // a group, or surviving a previous process. In all of these cases attach to\n // the existing subscription instead of failing. Ungrouped subscriptions hit\n // this on the concurrent-create race, so we must not gate it on `group`.\n const alreadyExists = (error as { code?: number } | undefined)?.code === 6;\n if (alreadyExists || group) {\n try {\n const sub = this.pubsub.subscription(subscriptionName);\n this.activeSubscriptions[subscriptionKey] = sub;\n return sub;\n } catch {\n // no-op\n }\n }\n }\n return undefined;\n })().finally(() => {\n delete this.inFlightInit[subscriptionKey];\n });\n\n this.inFlightInit[subscriptionKey] = initPromise;\n return initPromise;\n }\n\n async destroy(topicName: string) {\n const subName = this.getSubscriptionName(topicName);\n delete this.activeSubscriptions[topicName];\n this.pubsub.subscription(subName).removeAllListeners();\n await this.pubsub.subscription(subName).close();\n await this.pubsub.subscription(subName).delete();\n await this.pubsub.topic(topicName).delete();\n }\n\n async publish(\n topicName: string,\n event: Omit<Event, 'id' | 'createdAt'>,\n options?: { localOnly?: boolean },\n ): Promise<void> {\n if (topicName.startsWith('workflow.events.')) {\n const parts = topicName.split('.');\n if (parts[parts.length - 2] === 'v2') {\n topicName = 'workflow.events.v2';\n } else {\n topicName = 'workflow.events.v1';\n }\n }\n\n // `localOnly` events stay entirely within the publishing process. They are\n // never serialized through Google Cloud, so live methods on payload values\n // (e.g. `MastraModelOutput.getFullOutput`) survive intact. The agent's\n // execution-workflow relies on this: the run result is delivered via\n // `workflows-finish` and includes the `MastraModelOutput` instance —\n // round-tripping it through Pub/Sub would strip its methods.\n if (options?.localOnly) {\n await this.deliverLocal(topicName, event);\n return;\n }\n\n let topic = this.pubsub.topic(topicName);\n\n try {\n await topic.publishMessage({\n data: Buffer.from(JSON.stringify(event)),\n orderingKey: 'workflows',\n });\n } catch (e: any) {\n if (e.code === 5) {\n await this.pubsub.createTopic(topicName);\n await this.publish(topicName, event);\n } else {\n throw e;\n }\n }\n }\n\n async subscribe(topic: string, cb: EventCallback, options?: SubscribeOptions): Promise<void> {\n if (topic.startsWith('workflow.events.')) {\n const parts = topic.split('.');\n if (parts[parts.length - 2] === 'v2') {\n topic = 'workflow.events.v2';\n } else {\n topic = 'workflow.events.v1';\n }\n }\n\n const group = options?.group;\n // Use a composite key when group is set so grouped and non-grouped subscriptions\n // on the same topic don't collide\n const subscriptionKey = group ? `${topic}:${group}` : topic;\n\n // Register callback for `localOnly` delivery. Local delivery bypasses Google\n // Cloud entirely so live class instances on the payload (e.g. Date, Map,\n // Error, MastraModelOutput) keep their prototypes.\n let localSet = this.localCallbacks.get(topic);\n if (!localSet) {\n localSet = new Set();\n this.localCallbacks.set(topic, localSet);\n }\n localSet.add(cb);\n\n // Update tracked callbacks\n const subscription = this.activeSubscriptions[subscriptionKey] ?? (await this.init(topic, group));\n if (!subscription) {\n throw new Error(`Failed to subscribe to topic: ${topic}`);\n }\n\n this.activeSubscriptions[subscriptionKey] = subscription;\n\n const activeCbs = this.activeCbs[subscriptionKey] ?? new Set();\n activeCbs.add(cb);\n this.activeCbs[subscriptionKey] = activeCbs;\n\n if (subscription.isOpen) {\n return;\n }\n\n const messageListener = async (message: Message) => {\n const event = JSON.parse(message.data.toString()) as Event;\n event.id = message.id;\n event.createdAt = message.publishTime;\n event.deliveryAttempt = message.deliveryAttempt ?? 1;\n\n try {\n const activeCbs = this.activeCbs[subscriptionKey] ?? [];\n for (const cb of activeCbs) {\n cb(\n event,\n async () => {\n try {\n await this.ackMessage(subscriptionKey, message);\n } catch (e) {\n console.error('Error acking message', e);\n }\n },\n async () => {\n try {\n message.nack();\n } catch (e) {\n console.error('Error nacking message', e);\n }\n },\n );\n }\n } catch (error) {\n console.error('Error processing event', error);\n }\n };\n\n this.messageListeners[subscriptionKey] = messageListener;\n subscription.on('message', messageListener);\n\n subscription.on('error', async error => {\n console.error('subscription error', error);\n });\n }\n\n async unsubscribe(topic: string, cb: EventCallback): Promise<void> {\n if (topic.startsWith('workflow.events.')) {\n const parts = topic.split('.');\n if (parts[parts.length - 2] === 'v2') {\n topic = 'workflow.events.v2';\n } else {\n topic = 'workflow.events.v1';\n }\n }\n\n // Drop from the local-delivery set; if nobody is left, tear down the bucket.\n const localSet = this.localCallbacks.get(topic);\n if (localSet?.delete(cb) && localSet.size === 0) {\n this.localCallbacks.delete(topic);\n }\n\n // Check both grouped and non-grouped subscription keys for this callback\n const keysToCheck = [topic];\n for (const key of Object.keys(this.activeCbs)) {\n if (key.startsWith(`${topic}:`) && !keysToCheck.includes(key)) {\n keysToCheck.push(key);\n }\n }\n\n for (const subscriptionKey of keysToCheck) {\n const activeCbs = this.activeCbs[subscriptionKey];\n if (activeCbs?.has(cb)) {\n activeCbs.delete(cb);\n\n if (activeCbs.size === 0) {\n const subscription = this.activeSubscriptions[subscriptionKey];\n const listener = this.messageListeners[subscriptionKey];\n if (subscription) {\n if (listener) subscription.removeListener('message', listener);\n await subscription.close();\n }\n delete this.activeSubscriptions[subscriptionKey];\n delete this.activeCbs[subscriptionKey];\n delete this.messageListeners[subscriptionKey];\n }\n return;\n }\n }\n }\n\n async flush(): Promise<void> {\n await Promise.all(Object.values(this.ackBuffer));\n }\n\n /**\n * Fan a `localOnly` event out to in-process subscribers without going through\n * Google Cloud. The payload is delivered by reference, so live class instances\n * and functions on the event survive intact.\n */\n private async deliverLocal(topicName: string, event: Omit<Event, 'id' | 'createdAt'>): Promise<void> {\n const callbacks = this.localCallbacks.get(topicName);\n if (!callbacks || callbacks.size === 0) return;\n\n const localEvent: Event = {\n ...event,\n id: crypto.randomUUID(),\n createdAt: new Date(),\n deliveryAttempt: 1,\n };\n\n for (const cb of [...callbacks]) {\n try {\n cb(\n localEvent,\n async () => {},\n async () => {},\n );\n } catch (error) {\n console.error('Error delivering local event', error);\n }\n }\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "@mastra/google-cloud-pubsub",
3
- "version": "1.1.2",
3
+ "version": "1.1.3-alpha.0",
4
4
  "description": "Mastra Google Cloud PubSub integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "files": [
9
- "dist",
10
- "CHANGELOG.md"
9
+ "dist"
11
10
  ],
12
11
  "exports": {
13
12
  ".": {
@@ -30,22 +29,22 @@
30
29
  "zod": "^4.4.3"
31
30
  },
32
31
  "devDependencies": {
33
- "@ai-sdk/openai-v5": "npm:@ai-sdk/openai@2.0.110",
32
+ "@ai-sdk/openai-v5": "npm:@ai-sdk/openai@2.0.115",
34
33
  "@copilotkit/aimock": "^1.29.0",
35
34
  "@types/node": "22.20.1",
36
35
  "@vitest/coverage-v8": "4.1.10",
37
36
  "@vitest/ui": "4.1.10",
38
37
  "ai": "^4.3.19",
39
- "eslint": "^10.4.1",
40
- "tsup": "^8.5.1",
41
- "typescript": "^6.0.3",
38
+ "eslint": "^10.7.0",
39
+ "tsdown": "0.22.9",
40
+ "typescript": "^7.0.2",
42
41
  "vitest": "4.1.10",
43
- "@internal/ai-sdk-v5": "0.0.61",
44
- "@internal/lint": "0.0.114",
45
- "@internal/types-builder": "0.0.89",
46
- "@mastra/deployer": "1.51.0",
47
- "@mastra/core": "1.51.0",
48
- "@mastra/libsql": "1.16.0"
42
+ "@internal/ai-sdk-v5": "0.0.76",
43
+ "@internal/lint": "0.0.129",
44
+ "@internal/types-builder": "0.0.104",
45
+ "@mastra/core": "1.64.0-alpha.2",
46
+ "@mastra/libsql": "1.22.3-alpha.0",
47
+ "@mastra/deployer": "1.64.0-alpha.2"
49
48
  },
50
49
  "peerDependencies": {
51
50
  "@mastra/core": ">=1.13.2-0 <2.0.0-0"
@@ -63,9 +62,10 @@
63
62
  "node": ">=22.13.0"
64
63
  },
65
64
  "scripts": {
66
- "build": "tsup --silent --config tsup.config.ts",
67
- "build:watch": "tsup --watch --silent --config tsup.config.ts",
65
+ "build": "tsdown --silent --config tsdown.config.ts",
66
+ "build:watch": "tsdown --watch --silent --config tsdown.config.ts",
68
67
  "test": "vitest run --bail=1",
69
- "lint": "eslint ."
68
+ "lint": "oxlint . && eslint .",
69
+ "lint:fix": "oxlint --fix . && eslint --fix ."
70
70
  }
71
71
  }