@gokiteam/goki-dev 0.2.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.
Files changed (205) hide show
  1. package/README.md +478 -0
  2. package/bin/goki-dev.js +452 -0
  3. package/bin/mcp-server.js +16 -0
  4. package/bin/secrets-cli.js +302 -0
  5. package/cli/ComposeOverrideGenerator.js +226 -0
  6. package/cli/ComposeParser.js +73 -0
  7. package/cli/ConfigGenerator.js +304 -0
  8. package/cli/ConfigManager.js +46 -0
  9. package/cli/DatabaseManager.js +94 -0
  10. package/cli/DevToolsChecker.js +21 -0
  11. package/cli/DevToolsDir.js +66 -0
  12. package/cli/DevToolsManager.js +451 -0
  13. package/cli/DockerManager.js +138 -0
  14. package/cli/FunctionManager.js +95 -0
  15. package/cli/HttpProxyRewriter.js +91 -0
  16. package/cli/Logger.js +10 -0
  17. package/cli/McpConfigManager.js +123 -0
  18. package/cli/NgrokManager.js +431 -0
  19. package/cli/ProjectCLI.js +2322 -0
  20. package/cli/PubSubManager.js +129 -0
  21. package/cli/SnapshotManager.js +88 -0
  22. package/cli/UiFormatter.js +292 -0
  23. package/cli/WebhookUrlRewriter.js +32 -0
  24. package/cli/secrets/BiometricAuth.js +125 -0
  25. package/cli/secrets/SecretInjector.js +47 -0
  26. package/cli/secrets/SecretsConfig.js +141 -0
  27. package/cli/secrets/SecretsDoctor.js +384 -0
  28. package/cli/secrets/SecretsManager.js +255 -0
  29. package/client/dist/client.d.ts +332 -0
  30. package/client/dist/client.js +507 -0
  31. package/client/dist/helpers.d.ts +62 -0
  32. package/client/dist/helpers.js +122 -0
  33. package/client/dist/index.d.ts +59 -0
  34. package/client/dist/index.js +78 -0
  35. package/client/dist/package.json +1 -0
  36. package/client/dist/types.d.ts +280 -0
  37. package/client/dist/types.js +7 -0
  38. package/config.development +46 -0
  39. package/config.test +18 -0
  40. package/guidelines/CodingStyleGuideline.md +148 -0
  41. package/guidelines/CommentingGuideline.md +10 -0
  42. package/guidelines/HttpApiImplementationGuideline.md +137 -0
  43. package/guidelines/NamingGuideline.md +182 -0
  44. package/package.json +138 -0
  45. package/patterns/api/[collectionName]/Controllers.md +62 -0
  46. package/patterns/api/[collectionName]/Logic.md +154 -0
  47. package/patterns/api/[collectionName]/Permissions.md +81 -0
  48. package/patterns/api/[collectionName]/Router.md +83 -0
  49. package/patterns/api/[collectionName]/Schemas.md +197 -0
  50. package/patterns/configs/Patterns.md +7 -0
  51. package/patterns/enums/Patterns.md +24 -0
  52. package/patterns/errorHandling/Patterns.md +185 -0
  53. package/patterns/testing/Patterns.md +232 -0
  54. package/src/Server.js +238 -0
  55. package/src/api/dashboard/Controllers.js +9 -0
  56. package/src/api/dashboard/Logic.js +76 -0
  57. package/src/api/dashboard/Router.js +11 -0
  58. package/src/api/dashboard/Schemas.js +47 -0
  59. package/src/api/data/Controllers.js +26 -0
  60. package/src/api/data/Logic.js +188 -0
  61. package/src/api/data/Router.js +16 -0
  62. package/src/api/docker/Controllers.js +33 -0
  63. package/src/api/docker/Logic.js +268 -0
  64. package/src/api/docker/Router.js +15 -0
  65. package/src/api/docker/Schemas.js +80 -0
  66. package/src/api/docs/Controllers.js +15 -0
  67. package/src/api/docs/Logic.js +85 -0
  68. package/src/api/docs/Router.js +12 -0
  69. package/src/api/export/Controllers.js +30 -0
  70. package/src/api/export/Logic.js +143 -0
  71. package/src/api/export/Router.js +18 -0
  72. package/src/api/export/Schemas.js +104 -0
  73. package/src/api/firestore/Controllers.js +152 -0
  74. package/src/api/firestore/Logic.js +474 -0
  75. package/src/api/firestore/Router.js +23 -0
  76. package/src/api/functions/Controllers.js +261 -0
  77. package/src/api/functions/Logic.js +710 -0
  78. package/src/api/functions/Router.js +50 -0
  79. package/src/api/functions/Schemas.js +193 -0
  80. package/src/api/gateway/Controllers.js +72 -0
  81. package/src/api/gateway/Logic.js +74 -0
  82. package/src/api/gateway/Router.js +10 -0
  83. package/src/api/gateway/Schemas.js +19 -0
  84. package/src/api/health/Controllers.js +14 -0
  85. package/src/api/health/Logic.js +24 -0
  86. package/src/api/health/Router.js +12 -0
  87. package/src/api/httpTraffic/Controllers.js +29 -0
  88. package/src/api/httpTraffic/Logic.js +33 -0
  89. package/src/api/httpTraffic/Router.js +9 -0
  90. package/src/api/httpTraffic/Schemas.js +23 -0
  91. package/src/api/logging/Controllers.js +80 -0
  92. package/src/api/logging/Logic.js +461 -0
  93. package/src/api/logging/Router.js +24 -0
  94. package/src/api/logging/Schemas.js +43 -0
  95. package/src/api/mqtt/Controllers.js +17 -0
  96. package/src/api/mqtt/Logic.js +66 -0
  97. package/src/api/mqtt/Router.js +12 -0
  98. package/src/api/postgres/Controllers.js +97 -0
  99. package/src/api/postgres/Logic.js +221 -0
  100. package/src/api/postgres/Router.js +21 -0
  101. package/src/api/pubsub/Controllers.js +236 -0
  102. package/src/api/pubsub/Logic.js +732 -0
  103. package/src/api/pubsub/Router.js +41 -0
  104. package/src/api/pubsub/Schemas.js +355 -0
  105. package/src/api/redis/Controllers.js +63 -0
  106. package/src/api/redis/Logic.js +239 -0
  107. package/src/api/redis/Router.js +21 -0
  108. package/src/api/scheduler/Controllers.js +27 -0
  109. package/src/api/scheduler/Logic.js +49 -0
  110. package/src/api/scheduler/Router.js +16 -0
  111. package/src/api/services/Controllers.js +26 -0
  112. package/src/api/services/Logic.js +205 -0
  113. package/src/api/services/Router.js +14 -0
  114. package/src/api/services/Schemas.js +66 -0
  115. package/src/api/snapshots/Controllers.js +37 -0
  116. package/src/api/snapshots/Logic.js +797 -0
  117. package/src/api/snapshots/Router.js +15 -0
  118. package/src/api/snapshots/Schemas.js +23 -0
  119. package/src/api/webhooks/Controllers.js +49 -0
  120. package/src/api/webhooks/Logic.js +137 -0
  121. package/src/api/webhooks/Router.js +12 -0
  122. package/src/api/webhooks/Schemas.js +31 -0
  123. package/src/configs/Application.js +147 -0
  124. package/src/configs/Default.js +13 -0
  125. package/src/consumers/BlackboxLogsConsumer.js +235 -0
  126. package/src/consumers/DockerLogsConsumer.js +687 -0
  127. package/src/db/Tables.js +66 -0
  128. package/src/db/schemas/firestore.js +18 -0
  129. package/src/db/schemas/functions.js +65 -0
  130. package/src/db/schemas/httpTraffic.js +43 -0
  131. package/src/db/schemas/logging.js +74 -0
  132. package/src/db/schemas/migrations.js +64 -0
  133. package/src/db/schemas/mqtt.js +56 -0
  134. package/src/db/schemas/pubsub.js +90 -0
  135. package/src/db/schemas/pubsubRegistry.js +22 -0
  136. package/src/db/schemas/webhooks.js +28 -0
  137. package/src/emulation/awsiot/Controllers.js +91 -0
  138. package/src/emulation/awsiot/Logic.js +70 -0
  139. package/src/emulation/awsiot/Router.js +19 -0
  140. package/src/emulation/awsiot/Server.js +100 -0
  141. package/src/emulation/firestore/Server.js +136 -0
  142. package/src/emulation/logging/Controllers.js +212 -0
  143. package/src/emulation/logging/Logic.js +416 -0
  144. package/src/emulation/logging/Router.js +36 -0
  145. package/src/emulation/logging/Schemas.js +82 -0
  146. package/src/emulation/logging/Server.js +108 -0
  147. package/src/emulation/pubsub/Controllers.js +279 -0
  148. package/src/emulation/pubsub/DefaultTopics.js +162 -0
  149. package/src/emulation/pubsub/Logic.js +427 -0
  150. package/src/emulation/pubsub/README.md +309 -0
  151. package/src/emulation/pubsub/Router.js +33 -0
  152. package/src/emulation/pubsub/Server.js +104 -0
  153. package/src/emulation/pubsub/ShadowPoller.js +276 -0
  154. package/src/emulation/pubsub/ShadowSubscriptionManager.js +199 -0
  155. package/src/enums/ContainerNames.js +106 -0
  156. package/src/enums/ErrorReason.js +28 -0
  157. package/src/enums/FunctionStatuses.js +15 -0
  158. package/src/enums/FunctionTriggerTypes.js +15 -0
  159. package/src/enums/GatewayState.js +7 -0
  160. package/src/enums/ServiceNames.js +68 -0
  161. package/src/jobs/DatabaseMaintenance.js +184 -0
  162. package/src/jobs/MessageHistoryCleanup.js +152 -0
  163. package/src/mcp/ApiClient.js +25 -0
  164. package/src/mcp/Server.js +52 -0
  165. package/src/mcp/prompts/debugging.js +104 -0
  166. package/src/mcp/resources/platform.js +118 -0
  167. package/src/mcp/tools/data.js +84 -0
  168. package/src/mcp/tools/docker.js +166 -0
  169. package/src/mcp/tools/firestore.js +162 -0
  170. package/src/mcp/tools/functions.js +380 -0
  171. package/src/mcp/tools/httpTraffic.js +69 -0
  172. package/src/mcp/tools/logging.js +174 -0
  173. package/src/mcp/tools/mqtt.js +37 -0
  174. package/src/mcp/tools/postgres.js +130 -0
  175. package/src/mcp/tools/pubsub.js +316 -0
  176. package/src/mcp/tools/redis.js +146 -0
  177. package/src/mcp/tools/services.js +169 -0
  178. package/src/mcp/tools/snapshots.js +88 -0
  179. package/src/mcp/tools/webhooks.js +115 -0
  180. package/src/middleware/DevProxy.js +67 -0
  181. package/src/middleware/ErrorCatcher.js +35 -0
  182. package/src/middleware/HttpProxy.js +215 -0
  183. package/src/middleware/Reply.js +24 -0
  184. package/src/middleware/TraceId.js +9 -0
  185. package/src/middleware/WebhookProxy.js +234 -0
  186. package/src/protocols/mqtt/Broker.js +92 -0
  187. package/src/protocols/mqtt/Handlers.js +175 -0
  188. package/src/protocols/mqtt/PubSubBridge.js +162 -0
  189. package/src/protocols/mqtt/Server.js +116 -0
  190. package/src/runtime/FunctionRunner.js +179 -0
  191. package/src/services/AppGatewayService.js +582 -0
  192. package/src/singletons/FirestoreBroadcaster.js +367 -0
  193. package/src/singletons/FunctionTriggerDispatcher.js +456 -0
  194. package/src/singletons/FunctionsService.js +418 -0
  195. package/src/singletons/HttpProxy.js +224 -0
  196. package/src/singletons/LogBroadcaster.js +159 -0
  197. package/src/singletons/Logger.js +49 -0
  198. package/src/singletons/MemoryJsonStore.js +175 -0
  199. package/src/singletons/MessageBroadcaster.js +190 -0
  200. package/src/singletons/PostgresBroadcaster.js +367 -0
  201. package/src/singletons/PostgresClient.js +180 -0
  202. package/src/singletons/RedisClient.js +184 -0
  203. package/src/singletons/SqliteStore.js +480 -0
  204. package/src/singletons/TickService.js +151 -0
  205. package/src/singletons/WebhookProxy.js +223 -0
@@ -0,0 +1,427 @@
1
+ import { v4 as uuid } from 'uuid'
2
+ import { SqliteStore } from '../../singletons/SqliteStore.js'
3
+ import { MessageBroadcaster } from '../../singletons/MessageBroadcaster.js'
4
+
5
+ /**
6
+ * Extract sender from message attributes
7
+ * @param {Object} attributes - Message attributes
8
+ * @returns {string|null} - Sender identifier
9
+ */
10
+ const extractSender = attributes => {
11
+ if (!attributes) return null
12
+ return attributes.sender || attributes.source || attributes.userId || null
13
+ }
14
+
15
+ /**
16
+ * Check if a message has meaningful data worth storing in history.
17
+ * Filters out empty payloads like {} or empty strings that show as
18
+ * unknown items in the UI.
19
+ * @param {string} data - Base64-encoded message data
20
+ * @returns {boolean} - true if the message has meaningful content
21
+ */
22
+ const hasMeaningfulData = data => {
23
+ if (!data) return false
24
+ try {
25
+ const decoded = Buffer.from(data, 'base64').toString('utf-8')
26
+ const trimmed = decoded.trim()
27
+ if (!trimmed || trimmed === '{}' || trimmed === '""' || trimmed === 'null') return false
28
+ return true
29
+ } catch {
30
+ return !!data
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Create a new topic
36
+ */
37
+ export const CreateTopic = params => {
38
+ const { projectId, topicId } = params
39
+
40
+ const topicName = `projects/${projectId}/topics/${topicId}`
41
+
42
+ // Check if topic already exists
43
+ const existingTopic = SqliteStore.get('pubsub_topics', topicName, 'name')
44
+ if (existingTopic) {
45
+ return {
46
+ isSuccessful: false,
47
+ errorCode: 'ALREADY_EXISTS',
48
+ errorMessage: 'Topic already exists'
49
+ }
50
+ }
51
+
52
+ const topic = {
53
+ name: topicName,
54
+ labels: {},
55
+ messageStoragePolicy: {},
56
+ kmsKeyName: '',
57
+ schemaSettings: null,
58
+ satisfiesPzs: false,
59
+ messageRetentionDuration: '604800s'
60
+ }
61
+
62
+ SqliteStore.create('pubsub_topics', topic)
63
+
64
+ return {
65
+ isSuccessful: true,
66
+ topic
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Get a topic
72
+ */
73
+ export const GetTopic = params => {
74
+ const { projectId, topicId } = params
75
+ const topicName = `projects/${projectId}/topics/${topicId}`
76
+
77
+ const topic = SqliteStore.get('pubsub_topics', topicName, 'name')
78
+
79
+ if (!topic) {
80
+ return {
81
+ isSuccessful: false,
82
+ errorCode: 'NOT_FOUND',
83
+ errorMessage: 'Topic not found'
84
+ }
85
+ }
86
+
87
+ return {
88
+ isSuccessful: true,
89
+ topic
90
+ }
91
+ }
92
+
93
+ /**
94
+ * List topics in a project
95
+ */
96
+ export const ListTopics = params => {
97
+ const { projectId, pageSize = 50, pageToken } = params
98
+
99
+ const result = SqliteStore.list('pubsub_topics', {
100
+ filter: topic => topic.name.startsWith(`projects/${projectId}/topics/`),
101
+ limit: pageSize,
102
+ offset: pageToken ? parseInt(pageToken, 10) : 0
103
+ })
104
+
105
+ return {
106
+ isSuccessful: true,
107
+ topics: result.data,
108
+ nextPageToken: result.data.length === pageSize ? String(result.data.length) : undefined
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Delete a topic
114
+ */
115
+ export const DeleteTopic = params => {
116
+ const { projectId, topicId } = params
117
+ const topicName = `projects/${projectId}/topics/${topicId}`
118
+
119
+ try {
120
+ SqliteStore.delete('pubsub_topics', topicName, 'name')
121
+
122
+ // Also delete all subscriptions for this topic
123
+ const subscriptions = SqliteStore.find('pubsub_subscriptions', sub => sub.topic === topicName)
124
+ subscriptions.forEach(sub => {
125
+ SqliteStore.delete('pubsub_subscriptions', sub.name, 'name')
126
+ })
127
+
128
+ return {
129
+ isSuccessful: true
130
+ }
131
+ } catch (error) {
132
+ return {
133
+ isSuccessful: false,
134
+ errorCode: 'NOT_FOUND',
135
+ errorMessage: 'Topic not found'
136
+ }
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Publish messages to a topic
142
+ * Auto-creates the topic if it doesn't exist (dev-tools convenience feature)
143
+ */
144
+ export const PublishMessages = params => {
145
+ const { projectId, topicId, messages } = params
146
+ const topicName = `projects/${projectId}/topics/${topicId}`
147
+
148
+ // Auto-create topic if it doesn't exist (dev-tools convenience feature)
149
+ const existingTopic = SqliteStore.get('pubsub_topics', topicName, 'name')
150
+ if (!existingTopic) {
151
+ const topic = {
152
+ name: topicName,
153
+ labels: {},
154
+ messageStoragePolicy: {},
155
+ kmsKeyName: '',
156
+ schemaSettings: null,
157
+ satisfiesPzs: false,
158
+ messageRetentionDuration: '604800s'
159
+ }
160
+ SqliteStore.create('pubsub_topics', topic)
161
+ }
162
+
163
+ const messageIds = []
164
+ const publishTime = new Date().toISOString()
165
+
166
+ // Store each message
167
+ for (const message of messages) {
168
+ const messageId = uuid()
169
+ const storedMessage = {
170
+ id: messageId,
171
+ data: message.data,
172
+ attributes: message.attributes || {},
173
+ orderingKey: message.orderingKey || '',
174
+ publishTime,
175
+ topic: topicName,
176
+ ackId: null,
177
+ deliveryAttempt: 0
178
+ }
179
+
180
+ // Store in ephemeral messages table (for pull/ack)
181
+ SqliteStore.create('pubsub_messages', storedMessage)
182
+
183
+ // Only store messages with meaningful data in history
184
+ if (hasMeaningfulData(message.data)) {
185
+ const historyRecord = {
186
+ id: uuid(),
187
+ message_id: messageId,
188
+ data: message.data,
189
+ attributes: message.attributes || {},
190
+ ordering_key: message.orderingKey || '',
191
+ publish_time: publishTime,
192
+ topic: topicName,
193
+ sender: extractSender(message.attributes),
194
+ view_count: 0,
195
+ last_viewed_at: null
196
+ }
197
+ SqliteStore.create('pubsub_message_history', historyRecord)
198
+
199
+ // Broadcast to SSE clients
200
+ MessageBroadcaster.broadcast({
201
+ messageId,
202
+ topic: topicName,
203
+ data: message.data,
204
+ attributes: message.attributes || {},
205
+ publishTime,
206
+ sender: extractSender(message.attributes)
207
+ })
208
+ }
209
+
210
+ messageIds.push(messageId)
211
+ }
212
+
213
+ return {
214
+ isSuccessful: true,
215
+ messageIds
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Create a subscription
221
+ */
222
+ export const CreateSubscription = params => {
223
+ const { projectId, subscriptionId, topic, ackDeadlineSeconds = 10, pushConfig = {} } = params
224
+
225
+ const subscriptionName = `projects/${projectId}/subscriptions/${subscriptionId}`
226
+
227
+ // Check if subscription already exists
228
+ const existingSubscription = SqliteStore.get('pubsub_subscriptions', subscriptionName, 'name')
229
+ if (existingSubscription) {
230
+ return {
231
+ isSuccessful: false,
232
+ errorCode: 'ALREADY_EXISTS',
233
+ errorMessage: 'Subscription already exists'
234
+ }
235
+ }
236
+
237
+ // Validate that topic exists
238
+ const topicExists = SqliteStore.get('pubsub_topics', topic, 'name')
239
+ if (!topicExists) {
240
+ return {
241
+ isSuccessful: false,
242
+ errorCode: 'NOT_FOUND',
243
+ errorMessage: 'Topic not found'
244
+ }
245
+ }
246
+
247
+ const subscription = {
248
+ name: subscriptionName,
249
+ topic,
250
+ pushConfig,
251
+ ackDeadlineSeconds,
252
+ retainAckedMessages: false,
253
+ messageRetentionDuration: '604800s',
254
+ labels: {},
255
+ enableMessageOrdering: false,
256
+ expirationPolicy: {
257
+ ttl: '2678400s'
258
+ },
259
+ filter: '',
260
+ deadLetterPolicy: null,
261
+ retryPolicy: null,
262
+ detached: false,
263
+ enableExactlyOnceDelivery: false,
264
+ topicMessageRetentionDuration: '604800s',
265
+ state: 'ACTIVE'
266
+ }
267
+
268
+ SqliteStore.create('pubsub_subscriptions', subscription)
269
+
270
+ return {
271
+ isSuccessful: true,
272
+ subscription
273
+ }
274
+ }
275
+
276
+ /**
277
+ * Get a subscription
278
+ */
279
+ export const GetSubscription = params => {
280
+ const { projectId, subscriptionId } = params
281
+ const subscriptionName = `projects/${projectId}/subscriptions/${subscriptionId}`
282
+
283
+ const subscription = SqliteStore.get('pubsub_subscriptions', subscriptionName, 'name')
284
+
285
+ if (!subscription) {
286
+ return {
287
+ isSuccessful: false,
288
+ errorCode: 'NOT_FOUND',
289
+ errorMessage: 'Subscription not found'
290
+ }
291
+ }
292
+
293
+ return {
294
+ isSuccessful: true,
295
+ subscription
296
+ }
297
+ }
298
+
299
+ /**
300
+ * List subscriptions
301
+ */
302
+ export const ListSubscriptions = params => {
303
+ const { projectId, pageSize = 50, pageToken } = params
304
+
305
+ const result = SqliteStore.list('pubsub_subscriptions', {
306
+ filter: sub => sub.name.startsWith(`projects/${projectId}/subscriptions/`),
307
+ limit: pageSize,
308
+ offset: pageToken ? parseInt(pageToken, 10) : 0
309
+ })
310
+
311
+ return {
312
+ isSuccessful: true,
313
+ subscriptions: result.data,
314
+ nextPageToken: result.data.length === pageSize ? String(result.data.length) : undefined
315
+ }
316
+ }
317
+
318
+ /**
319
+ * Delete a subscription
320
+ */
321
+ export const DeleteSubscription = params => {
322
+ const { projectId, subscriptionId } = params
323
+ const subscriptionName = `projects/${projectId}/subscriptions/${subscriptionId}`
324
+
325
+ try {
326
+ SqliteStore.delete('pubsub_subscriptions', subscriptionName, 'name')
327
+ return {
328
+ isSuccessful: true
329
+ }
330
+ } catch (error) {
331
+ return {
332
+ isSuccessful: false,
333
+ errorCode: 'NOT_FOUND',
334
+ errorMessage: 'Subscription not found'
335
+ }
336
+ }
337
+ }
338
+
339
+ /**
340
+ * Pull messages from a subscription
341
+ */
342
+ export const PullMessages = params => {
343
+ const { projectId, subscriptionId, maxMessages = 100, returnImmediately = false } = params
344
+ const subscriptionName = `projects/${projectId}/subscriptions/${subscriptionId}`
345
+
346
+ // Check if subscription exists
347
+ const subscription = SqliteStore.get('pubsub_subscriptions', subscriptionName, 'name')
348
+ if (!subscription) {
349
+ return {
350
+ isSuccessful: false,
351
+ errorCode: 'NOT_FOUND',
352
+ errorMessage: 'Subscription not found'
353
+ }
354
+ }
355
+
356
+ // Get messages for this subscription's topic that haven't been acknowledged
357
+ const messages = SqliteStore.find('pubsub_messages', msg =>
358
+ msg.topic === subscription.topic && !msg.ackId
359
+ )
360
+
361
+ // Limit to maxMessages
362
+ const messagesToReturn = messages.slice(0, maxMessages)
363
+
364
+ // Generate ack IDs and mark messages as pending acknowledgment
365
+ const receivedMessages = messagesToReturn.map(msg => {
366
+ const ackId = uuid()
367
+
368
+ // Update message with ackId
369
+ SqliteStore.update('pubsub_messages', msg.id, {
370
+ ackId,
371
+ deliveryAttempt: msg.deliveryAttempt + 1,
372
+ lastDeliveryTime: new Date().toISOString()
373
+ })
374
+
375
+ return {
376
+ ackId,
377
+ message: {
378
+ data: msg.data,
379
+ attributes: msg.attributes,
380
+ messageId: msg.id,
381
+ publishTime: msg.publishTime,
382
+ orderingKey: msg.orderingKey
383
+ }
384
+ }
385
+ })
386
+
387
+ return {
388
+ isSuccessful: true,
389
+ receivedMessages
390
+ }
391
+ }
392
+
393
+ /**
394
+ * Acknowledge messages
395
+ */
396
+ export const AcknowledgeMessages = params => {
397
+ const { projectId, subscriptionId, ackIds } = params
398
+ const subscriptionName = `projects/${projectId}/subscriptions/${subscriptionId}`
399
+
400
+ // Check if subscription exists
401
+ const subscription = SqliteStore.get('pubsub_subscriptions', subscriptionName, 'name')
402
+ if (!subscription) {
403
+ return {
404
+ isSuccessful: false,
405
+ errorCode: 'NOT_FOUND',
406
+ errorMessage: 'Subscription not found'
407
+ }
408
+ }
409
+
410
+ // Delete messages with matching ackIds
411
+ const allMessages = SqliteStore.find('pubsub_messages', () => true)
412
+
413
+ for (const ackId of ackIds) {
414
+ const messageToAck = allMessages.find(msg => msg.ackId === ackId)
415
+ if (messageToAck) {
416
+ try {
417
+ SqliteStore.delete('pubsub_messages', messageToAck.id)
418
+ } catch (error) {
419
+ // Message might have already been deleted, ignore
420
+ }
421
+ }
422
+ }
423
+
424
+ return {
425
+ isSuccessful: true
426
+ }
427
+ }