@dxos/functions 0.8.4-main.a4bbb77 → 0.8.4-main.abd8ff62ef

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 (181) hide show
  1. package/README.md +4 -6
  2. package/dist/lib/neutral/index.mjs +475 -0
  3. package/dist/lib/neutral/index.mjs.map +7 -0
  4. package/dist/lib/neutral/meta.json +1 -0
  5. package/dist/types/src/index.d.ts +3 -9
  6. package/dist/types/src/index.d.ts.map +1 -1
  7. package/dist/types/src/protocol/functions-ai-http-client.d.ts +12 -0
  8. package/dist/types/src/protocol/functions-ai-http-client.d.ts.map +1 -0
  9. package/dist/types/src/protocol/index.d.ts +2 -0
  10. package/dist/types/src/protocol/index.d.ts.map +1 -0
  11. package/dist/types/src/protocol/protocol.d.ts +7 -0
  12. package/dist/types/src/protocol/protocol.d.ts.map +1 -0
  13. package/dist/types/src/protocol/protocol.test.d.ts +2 -0
  14. package/dist/types/src/protocol/protocol.test.d.ts.map +1 -0
  15. package/dist/types/src/sdk.d.ts +10 -0
  16. package/dist/types/src/sdk.d.ts.map +1 -0
  17. package/dist/types/src/services/credentials.d.ts +22 -39
  18. package/dist/types/src/services/credentials.d.ts.map +1 -1
  19. package/dist/types/src/services/function-invocation-service.d.ts +9 -20
  20. package/dist/types/src/services/function-invocation-service.d.ts.map +1 -1
  21. package/dist/types/src/services/index.d.ts +1 -6
  22. package/dist/types/src/services/index.d.ts.map +1 -1
  23. package/dist/types/src/services/queues.d.ts +1 -44
  24. package/dist/types/src/services/queues.d.ts.map +1 -1
  25. package/dist/types/src/services/tracing.d.ts +2 -54
  26. package/dist/types/src/services/tracing.d.ts.map +1 -1
  27. package/dist/types/src/types/index.d.ts +2 -0
  28. package/dist/types/src/types/index.d.ts.map +1 -0
  29. package/dist/types/src/types/url.d.ts +13 -0
  30. package/dist/types/src/types/url.d.ts.map +1 -0
  31. package/dist/types/tsconfig.tsbuildinfo +1 -1
  32. package/package.json +25 -72
  33. package/src/index.ts +3 -9
  34. package/src/protocol/functions-ai-http-client.ts +67 -0
  35. package/src/{executor → protocol}/index.ts +1 -1
  36. package/src/protocol/protocol.test.ts +58 -0
  37. package/src/protocol/protocol.ts +292 -0
  38. package/src/sdk.ts +29 -0
  39. package/src/services/credentials.ts +89 -112
  40. package/src/services/function-invocation-service.ts +22 -71
  41. package/src/services/index.ts +1 -6
  42. package/src/services/queues.ts +1 -78
  43. package/src/services/tracing.ts +1 -134
  44. package/src/types/index.ts +5 -0
  45. package/src/types/url.ts +32 -0
  46. package/dist/lib/browser/bundler/index.mjs +0 -265
  47. package/dist/lib/browser/bundler/index.mjs.map +0 -7
  48. package/dist/lib/browser/chunk-C2Z7LCJ2.mjs +0 -649
  49. package/dist/lib/browser/chunk-C2Z7LCJ2.mjs.map +0 -7
  50. package/dist/lib/browser/chunk-J5LGTIGS.mjs +0 -10
  51. package/dist/lib/browser/chunk-J5LGTIGS.mjs.map +0 -7
  52. package/dist/lib/browser/edge/index.mjs +0 -83
  53. package/dist/lib/browser/edge/index.mjs.map +0 -7
  54. package/dist/lib/browser/index.mjs +0 -1366
  55. package/dist/lib/browser/index.mjs.map +0 -7
  56. package/dist/lib/browser/meta.json +0 -1
  57. package/dist/lib/browser/testing/index.mjs +0 -129
  58. package/dist/lib/browser/testing/index.mjs.map +0 -7
  59. package/dist/lib/node-esm/bundler/index.mjs +0 -266
  60. package/dist/lib/node-esm/bundler/index.mjs.map +0 -7
  61. package/dist/lib/node-esm/chunk-AH3AZM2U.mjs +0 -651
  62. package/dist/lib/node-esm/chunk-AH3AZM2U.mjs.map +0 -7
  63. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
  64. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +0 -7
  65. package/dist/lib/node-esm/edge/index.mjs +0 -84
  66. package/dist/lib/node-esm/edge/index.mjs.map +0 -7
  67. package/dist/lib/node-esm/index.mjs +0 -1367
  68. package/dist/lib/node-esm/index.mjs.map +0 -7
  69. package/dist/lib/node-esm/meta.json +0 -1
  70. package/dist/lib/node-esm/testing/index.mjs +0 -130
  71. package/dist/lib/node-esm/testing/index.mjs.map +0 -7
  72. package/dist/types/src/bundler/bundler.d.ts +0 -49
  73. package/dist/types/src/bundler/bundler.d.ts.map +0 -1
  74. package/dist/types/src/bundler/bundler.test.d.ts +0 -2
  75. package/dist/types/src/bundler/bundler.test.d.ts.map +0 -1
  76. package/dist/types/src/bundler/index.d.ts +0 -2
  77. package/dist/types/src/bundler/index.d.ts.map +0 -1
  78. package/dist/types/src/edge/functions.d.ts +0 -17
  79. package/dist/types/src/edge/functions.d.ts.map +0 -1
  80. package/dist/types/src/edge/index.d.ts +0 -2
  81. package/dist/types/src/edge/index.d.ts.map +0 -1
  82. package/dist/types/src/errors.d.ts +0 -137
  83. package/dist/types/src/errors.d.ts.map +0 -1
  84. package/dist/types/src/examples/fib.d.ts +0 -7
  85. package/dist/types/src/examples/fib.d.ts.map +0 -1
  86. package/dist/types/src/examples/index.d.ts +0 -4
  87. package/dist/types/src/examples/index.d.ts.map +0 -1
  88. package/dist/types/src/examples/reply.d.ts +0 -3
  89. package/dist/types/src/examples/reply.d.ts.map +0 -1
  90. package/dist/types/src/examples/sleep.d.ts +0 -5
  91. package/dist/types/src/examples/sleep.d.ts.map +0 -1
  92. package/dist/types/src/executor/executor.d.ts +0 -14
  93. package/dist/types/src/executor/executor.d.ts.map +0 -1
  94. package/dist/types/src/executor/index.d.ts +0 -2
  95. package/dist/types/src/executor/index.d.ts.map +0 -1
  96. package/dist/types/src/handler.d.ts +0 -106
  97. package/dist/types/src/handler.d.ts.map +0 -1
  98. package/dist/types/src/schema.d.ts +0 -43
  99. package/dist/types/src/schema.d.ts.map +0 -1
  100. package/dist/types/src/services/database.d.ts +0 -63
  101. package/dist/types/src/services/database.d.ts.map +0 -1
  102. package/dist/types/src/services/event-logger.d.ts +0 -72
  103. package/dist/types/src/services/event-logger.d.ts.map +0 -1
  104. package/dist/types/src/services/function-invocation-service.test.d.ts +0 -2
  105. package/dist/types/src/services/function-invocation-service.test.d.ts.map +0 -1
  106. package/dist/types/src/services/local-function-execution.d.ts +0 -32
  107. package/dist/types/src/services/local-function-execution.d.ts.map +0 -1
  108. package/dist/types/src/services/remote-function-execution-service.d.ts +0 -20
  109. package/dist/types/src/services/remote-function-execution-service.d.ts.map +0 -1
  110. package/dist/types/src/services/service-container.d.ts +0 -56
  111. package/dist/types/src/services/service-container.d.ts.map +0 -1
  112. package/dist/types/src/services/service-registry.d.ts +0 -29
  113. package/dist/types/src/services/service-registry.d.ts.map +0 -1
  114. package/dist/types/src/services/service-registry.test.d.ts +0 -2
  115. package/dist/types/src/services/service-registry.test.d.ts.map +0 -1
  116. package/dist/types/src/testing/index.d.ts +0 -3
  117. package/dist/types/src/testing/index.d.ts.map +0 -1
  118. package/dist/types/src/testing/layer.d.ts +0 -17
  119. package/dist/types/src/testing/layer.d.ts.map +0 -1
  120. package/dist/types/src/testing/logger.d.ts +0 -5
  121. package/dist/types/src/testing/logger.d.ts.map +0 -1
  122. package/dist/types/src/testing/persist-database.test.d.ts +0 -2
  123. package/dist/types/src/testing/persist-database.test.d.ts.map +0 -1
  124. package/dist/types/src/testing/services.d.ts +0 -59
  125. package/dist/types/src/testing/services.d.ts.map +0 -1
  126. package/dist/types/src/trace.d.ts +0 -122
  127. package/dist/types/src/trace.d.ts.map +0 -1
  128. package/dist/types/src/translations.d.ts +0 -12
  129. package/dist/types/src/translations.d.ts.map +0 -1
  130. package/dist/types/src/triggers/index.d.ts +0 -4
  131. package/dist/types/src/triggers/index.d.ts.map +0 -1
  132. package/dist/types/src/triggers/input-builder.d.ts +0 -3
  133. package/dist/types/src/triggers/input-builder.d.ts.map +0 -1
  134. package/dist/types/src/triggers/invocation-tracer.d.ts +0 -35
  135. package/dist/types/src/triggers/invocation-tracer.d.ts.map +0 -1
  136. package/dist/types/src/triggers/trigger-dispatcher.d.ts +0 -74
  137. package/dist/types/src/triggers/trigger-dispatcher.d.ts.map +0 -1
  138. package/dist/types/src/triggers/trigger-dispatcher.test.d.ts +0 -2
  139. package/dist/types/src/triggers/trigger-dispatcher.test.d.ts.map +0 -1
  140. package/dist/types/src/triggers/trigger-state-store.d.ts +0 -27
  141. package/dist/types/src/triggers/trigger-state-store.d.ts.map +0 -1
  142. package/dist/types/src/types.d.ts +0 -221
  143. package/dist/types/src/types.d.ts.map +0 -1
  144. package/dist/types/src/url.d.ts +0 -21
  145. package/dist/types/src/url.d.ts.map +0 -1
  146. package/src/bundler/bundler.test.ts +0 -58
  147. package/src/bundler/bundler.ts +0 -291
  148. package/src/bundler/index.ts +0 -5
  149. package/src/edge/functions.ts +0 -67
  150. package/src/edge/index.ts +0 -9
  151. package/src/errors.ts +0 -21
  152. package/src/examples/fib.ts +0 -31
  153. package/src/examples/index.ts +0 -7
  154. package/src/examples/reply.ts +0 -19
  155. package/src/examples/sleep.ts +0 -23
  156. package/src/executor/executor.ts +0 -57
  157. package/src/handler.ts +0 -222
  158. package/src/schema.ts +0 -68
  159. package/src/services/database.ts +0 -171
  160. package/src/services/event-logger.ts +0 -118
  161. package/src/services/function-invocation-service.test.ts +0 -79
  162. package/src/services/local-function-execution.ts +0 -150
  163. package/src/services/remote-function-execution-service.ts +0 -61
  164. package/src/services/service-container.ts +0 -114
  165. package/src/services/service-registry.test.ts +0 -42
  166. package/src/services/service-registry.ts +0 -59
  167. package/src/testing/index.ts +0 -6
  168. package/src/testing/layer.ts +0 -112
  169. package/src/testing/logger.ts +0 -16
  170. package/src/testing/persist-database.test.ts +0 -87
  171. package/src/testing/services.ts +0 -115
  172. package/src/trace.ts +0 -178
  173. package/src/translations.ts +0 -20
  174. package/src/triggers/index.ts +0 -7
  175. package/src/triggers/input-builder.ts +0 -35
  176. package/src/triggers/invocation-tracer.ts +0 -99
  177. package/src/triggers/trigger-dispatcher.test.ts +0 -651
  178. package/src/triggers/trigger-dispatcher.ts +0 -522
  179. package/src/triggers/trigger-state-store.ts +0 -60
  180. package/src/types.ts +0 -214
  181. package/src/url.ts +0 -55
@@ -1,651 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import { FetchHttpClient } from '@effect/platform';
6
- import { describe, it } from '@effect/vitest';
7
- import { Duration, Effect, Exit, Layer, pipe } from 'effect';
8
-
9
- import { AiService } from '@dxos/ai';
10
- import { Filter, Obj, Query, Ref } from '@dxos/echo';
11
- import { invariant } from '@dxos/invariant';
12
- import { DataType } from '@dxos/schema';
13
-
14
- import { default as reply } from '../examples/reply';
15
- import { serializeFunction } from '../handler';
16
- import { FunctionType } from '../schema';
17
- import {
18
- ComputeEventLogger,
19
- CredentialsService,
20
- DatabaseService,
21
- FunctionInvocationService,
22
- QueueService,
23
- TracingService,
24
- } from '../services';
25
- import { TestDatabaseLayer } from '../testing';
26
- import { FunctionTrigger } from '../types';
27
-
28
- import { InvocationTracer } from './invocation-tracer';
29
- import { TriggerDispatcher } from './trigger-dispatcher';
30
- import { TriggerStateStore } from './trigger-state-store';
31
-
32
- const TestLayer = pipe(
33
- Layer.mergeAll(ComputeEventLogger.layerFromTracing, InvocationTracer.layerTest, TriggerStateStore.layerMemory),
34
- Layer.provideMerge(
35
- Layer.mergeAll(
36
- AiService.notAvailable,
37
- CredentialsService.layerConfig([]),
38
- FunctionInvocationService.layerTestMocked({ functions: [reply] }).pipe(
39
- Layer.provideMerge(ComputeEventLogger.layerFromTracing),
40
- Layer.provideMerge(TracingService.layerLogInfo()),
41
- ),
42
- FetchHttpClient.layer,
43
- TestDatabaseLayer({
44
- types: [FunctionType, FunctionTrigger, DataType.Person, DataType.Task],
45
- }),
46
- ),
47
- ),
48
- );
49
-
50
- const TestTriggerDispatcherLayer = Layer.provideMerge(
51
- TriggerDispatcher.layer({ timeControl: 'manual', startingTime: new Date('2025-09-05T15:01:00.000Z') }),
52
- TestLayer,
53
- );
54
-
55
- describe('TriggerDispatcher', () => {
56
- describe('Time Control', () => {
57
- it.effect(
58
- 'should get current time based on time control',
59
- Effect.fnUntraced(function* ({ expect }) {
60
- const dispatcher = yield* TriggerDispatcher;
61
-
62
- const initialTime = dispatcher.getCurrentTime();
63
-
64
- // Advance time by 1 hour
65
- yield* dispatcher.advanceTime(Duration.hours(1));
66
-
67
- const newTime = dispatcher.getCurrentTime();
68
- const timeDiff = newTime.getTime() - initialTime.getTime();
69
-
70
- expect(timeDiff).toBe(Duration.toMillis(Duration.hours(1)));
71
- }, Effect.provide(TestTriggerDispatcherLayer)),
72
- );
73
- });
74
-
75
- describe('Manual Invocation', () => {
76
- it.effect(
77
- 'should manually invoke trigger',
78
- Effect.fnUntraced(function* ({ expect }) {
79
- const functionObj = serializeFunction(reply);
80
- yield* DatabaseService.add(functionObj);
81
- const trigger = Obj.make(FunctionTrigger, {
82
- function: Ref.make(functionObj),
83
- enabled: true,
84
- spec: {
85
- kind: 'timer',
86
- cron: '*/5 * * * *',
87
- },
88
- });
89
- yield* DatabaseService.add(trigger);
90
- const dispatcher = yield* TriggerDispatcher;
91
- const { result } = yield* dispatcher.invokeTrigger({
92
- trigger,
93
- event: { tick: 0 },
94
- });
95
-
96
- expect(result).toEqual(Exit.succeed({ tick: 0 }));
97
- }, Effect.provide(TestTriggerDispatcherLayer)),
98
- );
99
- });
100
-
101
- describe('Timer Triggers', () => {
102
- it.effect(
103
- 'should invoke scheduled timer triggers',
104
- Effect.fnUntraced(function* ({ expect }) {
105
- const functionObj = serializeFunction(reply);
106
- yield* DatabaseService.add(functionObj);
107
- const trigger = Obj.make(FunctionTrigger, {
108
- function: Ref.make(functionObj),
109
- enabled: true,
110
- spec: {
111
- kind: 'timer',
112
- cron: '* * * * *', // Every minute - should trigger immediately
113
- },
114
- });
115
- yield* DatabaseService.add(trigger);
116
-
117
- const dispatcher = yield* TriggerDispatcher;
118
- yield* dispatcher.refreshTriggers();
119
-
120
- // Manually invoke the trigger
121
- yield* dispatcher.advanceTime(Duration.minutes(1));
122
- const results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['timer'] });
123
-
124
- // Should have executed successfully
125
- expect(results.length).toBe(1);
126
- expect(results[0].triggerId).toBe(trigger.id);
127
- expect(Exit.isSuccess(results[0].result)).toBe(true);
128
- }, Effect.provide(TestTriggerDispatcherLayer)),
129
- );
130
-
131
- it.effect(
132
- 'should handle disabled triggers',
133
- Effect.fnUntraced(function* ({ expect }) {
134
- const functionObj = serializeFunction(reply);
135
- yield* DatabaseService.add(functionObj);
136
-
137
- const enabledTrigger = Obj.make(FunctionTrigger, {
138
- function: Ref.make(functionObj),
139
- enabled: true,
140
- spec: {
141
- kind: 'timer',
142
- cron: '* * * * *',
143
- },
144
- });
145
-
146
- const disabledTrigger = Obj.make(FunctionTrigger, {
147
- function: Ref.make(functionObj),
148
- enabled: false,
149
- spec: {
150
- kind: 'timer',
151
- cron: '* * * * *',
152
- },
153
- });
154
-
155
- yield* DatabaseService.add(enabledTrigger);
156
- yield* DatabaseService.add(disabledTrigger);
157
-
158
- const dispatcher = yield* TriggerDispatcher;
159
- yield* dispatcher.refreshTriggers();
160
-
161
- // Manually test invocation of enabled vs disabled
162
- yield* dispatcher.advanceTime(Duration.minutes(1));
163
- const results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['timer'] });
164
-
165
- // Enabled should succeed
166
- expect(results.length).toBe(1);
167
- expect(results[0].triggerId).toBe(enabledTrigger.id);
168
- expect(Exit.isSuccess(results[0].result)).toBe(true);
169
- }, Effect.provide(TestTriggerDispatcherLayer)),
170
- );
171
-
172
- it.effect(
173
- 'cron triggers are invoked periodically on schedule',
174
- Effect.fnUntraced(function* ({ expect }) {
175
- const functionObj = serializeFunction(reply);
176
- yield* DatabaseService.add(functionObj);
177
-
178
- // cron every 5 minutes
179
- const trigger = Obj.make(FunctionTrigger, {
180
- function: Ref.make(functionObj),
181
- enabled: true,
182
- spec: {
183
- kind: 'timer',
184
- cron: '*/5 * * * *',
185
- },
186
- });
187
- yield* DatabaseService.add(trigger);
188
-
189
- // now = 15:01
190
- const dispatcher = yield* TriggerDispatcher;
191
- yield* dispatcher.refreshTriggers(); // next execution = 15:05
192
-
193
- // advance 1 minute; now = 15:02 -- trigger should not be invoked
194
- yield* dispatcher.advanceTime(Duration.minutes(1));
195
- let results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['timer'] });
196
- expect(results.length).toBe(0);
197
-
198
- // advance 4 more minutes; now = 15:06 -- trigger should be invoked
199
- yield* dispatcher.advanceTime(Duration.minutes(4));
200
- results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['timer'] });
201
- expect(results.length).toBe(1);
202
-
203
- // advance 2 more minutes; now = 15:08 -- trigger should not be invoked
204
- yield* dispatcher.advanceTime(Duration.minutes(2));
205
- results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['timer'] });
206
- expect(results.length).toBe(0);
207
-
208
- // advance 3 more minutes; now = 15:11 -- trigger should be invoked
209
- yield* dispatcher.advanceTime(Duration.minutes(3));
210
- results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['timer'] });
211
- expect(results.length).toBe(1);
212
- }, Effect.provide(TestTriggerDispatcherLayer)),
213
- );
214
- });
215
-
216
- describe('Dynamic Trigger Management', () => {
217
- it.effect(
218
- 'should handle trigger updates dynamically',
219
- Effect.fnUntraced(function* ({ expect }) {
220
- const dispatcher = yield* TriggerDispatcher;
221
- yield* dispatcher.refreshTriggers();
222
-
223
- // Initially no triggers in database
224
-
225
- // Add a trigger dynamically
226
- const functionObj = serializeFunction(reply);
227
- yield* DatabaseService.add(functionObj);
228
- const trigger = Obj.make(FunctionTrigger, {
229
- function: Ref.make(functionObj),
230
- enabled: true,
231
- spec: {
232
- kind: 'timer',
233
- cron: '* * * * *', // Every minute
234
- },
235
- });
236
- yield* DatabaseService.add(trigger);
237
-
238
- // Can invoke the trigger
239
- const result = yield* dispatcher.invokeTrigger({ trigger, event: { tick: 0 } });
240
- expect(Exit.isSuccess(result.result)).toBe(true);
241
- }, Effect.provide(TestTriggerDispatcherLayer)),
242
- );
243
- });
244
-
245
- describe('Cron Patterns', () => {
246
- it.effect(
247
- 'should support Effect cron expressions',
248
- Effect.fnUntraced(function* ({ expect }) {
249
- const functionObj = serializeFunction(reply);
250
- yield* DatabaseService.add(functionObj);
251
-
252
- const validPatterns = [
253
- '* * * * *', // Every minute
254
- '0 * * * *', // Every hour
255
- '0 0 * * *', // Daily
256
- '0 0 * * 1', // Every Monday
257
- '0 9-17 * * *', // Every hour from 9 AM to 5 PM
258
- ];
259
-
260
- const dispatcher = yield* TriggerDispatcher;
261
-
262
- // Test that valid patterns can be invoked
263
- for (const cron of validPatterns) {
264
- const trigger = Obj.make(FunctionTrigger, {
265
- function: Ref.make(functionObj),
266
- enabled: true,
267
- spec: {
268
- kind: 'timer',
269
- cron,
270
- },
271
- });
272
- yield* DatabaseService.add(trigger);
273
-
274
- const result = yield* dispatcher.invokeTrigger({ trigger, event: { tick: 0 } });
275
- expect(Exit.isSuccess(result.result)).toBe(true);
276
- }
277
- }, Effect.provide(TestTriggerDispatcherLayer)),
278
- );
279
-
280
- it.effect(
281
- 'should handle invalid cron expressions gracefully',
282
- Effect.fnUntraced(function* ({ expect }) {
283
- const functionObj = serializeFunction(reply);
284
- yield* DatabaseService.add(functionObj);
285
-
286
- // Test with an invalid pattern
287
- const trigger = Obj.make(FunctionTrigger, {
288
- function: Ref.make(functionObj),
289
- enabled: true,
290
- spec: {
291
- kind: 'timer',
292
- cron: 'invalid-cron',
293
- },
294
- });
295
- yield* DatabaseService.add(trigger);
296
-
297
- const dispatcher = yield* TriggerDispatcher;
298
- yield* dispatcher.refreshTriggers();
299
-
300
- // Can still invoke manually even with invalid cron
301
- const result = yield* dispatcher.invokeScheduledTriggers({ kinds: ['timer'] });
302
- expect(result.length).toBe(0);
303
- }, Effect.provide(TestTriggerDispatcherLayer)),
304
- );
305
- });
306
-
307
- describe('Natural Time Control', () => {
308
- it.effect(
309
- 'should start and stop dispatcher',
310
- Effect.fnUntraced(
311
- function* () {
312
- const dispatcher = yield* TriggerDispatcher;
313
- yield* dispatcher.start();
314
- yield* dispatcher.stop();
315
- },
316
- Effect.provide(Layer.provideMerge(TriggerDispatcher.layer({ timeControl: 'natural' }), TestLayer)),
317
- ),
318
- );
319
- });
320
-
321
- describe('Queue Triggers', () => {
322
- it.effect(
323
- 'should invoke scheduled queue triggers',
324
- Effect.fnUntraced(function* ({ expect }) {
325
- const queue = yield* QueueService.createQueue();
326
- const functionObj = serializeFunction(reply);
327
- yield* DatabaseService.add(functionObj);
328
- const trigger = Obj.make(FunctionTrigger, {
329
- function: Ref.make(functionObj),
330
- enabled: true,
331
- spec: {
332
- kind: 'queue',
333
- queue: queue.dxn.toString(),
334
- },
335
- });
336
- yield* DatabaseService.add(trigger);
337
- yield* QueueService.append(queue, [
338
- Obj.make(DataType.Person, {
339
- fullName: 'John Doe',
340
- }),
341
- ]);
342
-
343
- const dispatcher = yield* TriggerDispatcher;
344
- const results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['queue'] });
345
- expect(results.length).toBe(1);
346
- expect(results[0].triggerId).toBe(trigger.id);
347
- expect(Exit.isSuccess(results[0].result)).toBe(true);
348
- }, Effect.provide(TestTriggerDispatcherLayer)),
349
- );
350
-
351
- it.effect(
352
- 'triggers are invoked one by one',
353
- Effect.fnUntraced(function* ({ expect }) {
354
- const queue = yield* QueueService.createQueue();
355
- const functionObj = serializeFunction(reply);
356
- yield* DatabaseService.add(functionObj);
357
- const trigger = Obj.make(FunctionTrigger, {
358
- function: Ref.make(functionObj),
359
- enabled: true,
360
- spec: {
361
- kind: 'queue',
362
- queue: queue.dxn.toString(),
363
- },
364
- });
365
- yield* DatabaseService.add(trigger);
366
- yield* QueueService.append(queue, [
367
- Obj.make(DataType.Person, {
368
- fullName: 'John Doe',
369
- }),
370
- Obj.make(DataType.Person, {
371
- fullName: 'Jane Smith',
372
- }),
373
- ]);
374
-
375
- const dispatcher = yield* TriggerDispatcher;
376
-
377
- {
378
- const results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['queue'] });
379
- expect(results.length).toBe(1);
380
- expect(results[0].triggerId).toBe(trigger.id);
381
- expect(Exit.isSuccess(results[0].result)).toBe(true);
382
- }
383
-
384
- {
385
- const results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['queue'] });
386
- expect(results.length).toBe(1);
387
- expect(results[0].triggerId).toBe(trigger.id);
388
- expect(Exit.isSuccess(results[0].result)).toBe(true);
389
- }
390
-
391
- {
392
- const results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['queue'] });
393
- expect(results.length).toBe(0);
394
- }
395
- }, Effect.provide(TestTriggerDispatcherLayer)),
396
- );
397
-
398
- it.effect(
399
- 'builds input from pattern',
400
- Effect.fnUntraced(function* ({ expect }) {
401
- const queue = yield* QueueService.createQueue();
402
- const functionObj = serializeFunction(reply);
403
- yield* DatabaseService.add(functionObj);
404
- const trigger = Obj.make(FunctionTrigger, {
405
- function: Ref.make(functionObj),
406
- enabled: true,
407
- spec: {
408
- kind: 'queue',
409
- queue: queue.dxn.toString(),
410
- },
411
- input: {
412
- instructions: 'Please process the queue item.',
413
- input: '{{event.item}}',
414
- triggerId: '{{trigger.id}}',
415
- },
416
- });
417
- yield* DatabaseService.add(trigger);
418
- const person = Obj.make(DataType.Person, {
419
- fullName: 'John Doe',
420
- });
421
- yield* QueueService.append(queue, [person]);
422
-
423
- const dispatcher = yield* TriggerDispatcher;
424
- const results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['queue'] });
425
- expect(results.length).toBe(1);
426
- expect(results[0].triggerId).toBe(trigger.id);
427
- const exit = results[0].result;
428
- invariant(Exit.isSuccess(exit));
429
- expect(exit.value).to.deep.include({
430
- instructions: 'Please process the queue item.',
431
- input: {
432
- id: person.id,
433
- fullName: 'John Doe',
434
- },
435
- triggerId: trigger.id,
436
- });
437
- }, Effect.provide(TestTriggerDispatcherLayer)),
438
- );
439
- });
440
-
441
- describe('Database Triggers (Subscription)', () => {
442
- it.effect(
443
- 'should invoke triggers on object creation',
444
- Effect.fnUntraced(function* ({ expect }) {
445
- const functionObj = serializeFunction(reply);
446
- yield* DatabaseService.add(functionObj);
447
-
448
- // Create a subscription trigger that watches for DataType.Person objects
449
- const trigger = Obj.make(FunctionTrigger, {
450
- function: Ref.make(functionObj),
451
- enabled: true,
452
- spec: {
453
- kind: 'subscription',
454
- query: Query.select(Filter.type(DataType.Person)).ast,
455
- },
456
- });
457
- yield* DatabaseService.add(trigger);
458
-
459
- const dispatcher = yield* TriggerDispatcher;
460
- yield* dispatcher.refreshTriggers();
461
-
462
- // Create a new Person object - this should trigger the subscription
463
- const person = Obj.make(DataType.Person, {
464
- fullName: 'Alice Smith',
465
- });
466
- yield* DatabaseService.add(person);
467
-
468
- // Invoke scheduled triggers to check if subscription fires
469
- const results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['subscription'] });
470
-
471
- // Should have triggered for the new person
472
- expect(results.length).toBe(1);
473
- expect(results[0].triggerId).toBe(trigger.id);
474
- expect(Exit.isSuccess(results[0].result)).toBe(true);
475
- }, Effect.provide(TestTriggerDispatcherLayer)),
476
- );
477
-
478
- it.effect(
479
- 'should invoke triggers on object updates',
480
- Effect.fnUntraced(function* ({ expect }) {
481
- const functionObj = serializeFunction(reply);
482
- yield* DatabaseService.add(functionObj);
483
-
484
- // Create a person object first
485
- const person = Obj.make(DataType.Person, {
486
- fullName: 'Bob Jones',
487
- });
488
- yield* DatabaseService.add(person);
489
-
490
- // Create a subscription trigger
491
- const trigger = Obj.make(FunctionTrigger, {
492
- function: Ref.make(functionObj),
493
- enabled: true,
494
- spec: {
495
- kind: 'subscription',
496
- query: Query.select(Filter.type(DataType.Person)).ast,
497
- },
498
- });
499
- yield* DatabaseService.add(trigger);
500
-
501
- const dispatcher = yield* TriggerDispatcher;
502
- yield* dispatcher.refreshTriggers();
503
-
504
- // Initial check - should trigger for existing object
505
- let results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['subscription'] });
506
- expect(results.length).toBe(1);
507
-
508
- // Update the person object
509
- person.fullName = 'Robert Jones';
510
- yield* DatabaseService.flush();
511
-
512
- // Should trigger again for the update
513
- results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['subscription'] });
514
- expect(results.length).toBe(1);
515
- expect(results[0].triggerId).toBe(trigger.id);
516
- expect(Exit.isSuccess(results[0].result)).toBe(true);
517
- }, Effect.provide(TestTriggerDispatcherLayer)),
518
- );
519
-
520
- it.effect(
521
- 'should not invoke triggers for unchanged objects',
522
- Effect.fnUntraced(function* ({ expect }) {
523
- const functionObj = serializeFunction(reply);
524
- yield* DatabaseService.add(functionObj);
525
-
526
- // Create a subscription trigger first
527
- const trigger = Obj.make(FunctionTrigger, {
528
- function: Ref.make(functionObj),
529
- enabled: true,
530
- spec: {
531
- kind: 'subscription',
532
- query: Query.select(Filter.type(DataType.Person)).ast,
533
- },
534
- });
535
- yield* DatabaseService.add(trigger);
536
-
537
- const dispatcher = yield* TriggerDispatcher;
538
- yield* dispatcher.refreshTriggers();
539
-
540
- // Create a person object
541
- const person = Obj.make(DataType.Person, {
542
- fullName: 'Charlie Brown',
543
- });
544
- yield* DatabaseService.add(person);
545
-
546
- // First invocation - should trigger for new object
547
- let results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['subscription'] });
548
- expect(results.length).toBe(1);
549
-
550
- // Second invocation without any changes - should not trigger
551
- results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['subscription'] });
552
- expect(results.length).toBe(0);
553
-
554
- // Update the object
555
- person.fullName = 'Charles Brown';
556
- yield* DatabaseService.flush();
557
-
558
- // Third invocation - should trigger for the update
559
- results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['subscription'] });
560
- expect(results.length).toBe(1);
561
-
562
- // Fourth invocation without changes - should not trigger
563
- results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['subscription'] });
564
- expect(results.length).toBe(0);
565
- }, Effect.provide(TestTriggerDispatcherLayer)),
566
- );
567
-
568
- it.effect(
569
- 'should handle multiple object types with filters',
570
- Effect.fnUntraced(function* ({ expect }) {
571
- const functionObj = serializeFunction(reply);
572
- yield* DatabaseService.add(functionObj);
573
-
574
- // Create a subscription trigger that only watches for DataType.Task objects
575
- const trigger = Obj.make(FunctionTrigger, {
576
- function: Ref.make(functionObj),
577
- enabled: true,
578
- spec: {
579
- kind: 'subscription',
580
- query: Query.select(Filter.type(DataType.Task)).ast,
581
- },
582
- });
583
- yield* DatabaseService.add(trigger);
584
-
585
- const dispatcher = yield* TriggerDispatcher;
586
- yield* dispatcher.refreshTriggers();
587
-
588
- // Create a Person object - should NOT trigger
589
- const person = Obj.make(DataType.Person, {
590
- fullName: 'David Wilson',
591
- });
592
- yield* DatabaseService.add(person);
593
-
594
- let results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['subscription'] });
595
- expect(results.length).toBe(0);
596
-
597
- // Create a Task object - should trigger
598
- const task = Obj.make(DataType.Task, {
599
- title: 'Important task',
600
- });
601
- yield* DatabaseService.add(task);
602
-
603
- results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['subscription'] });
604
- expect(results.length).toBe(1);
605
- expect(results[0].triggerId).toBe(trigger.id);
606
- expect(Exit.isSuccess(results[0].result)).toBe(true);
607
- }, Effect.provide(TestTriggerDispatcherLayer)),
608
- );
609
-
610
- it.effect(
611
- 'should pass correct event data to function',
612
- Effect.fnUntraced(function* ({ expect }) {
613
- const functionObj = serializeFunction(reply);
614
- yield* DatabaseService.add(functionObj);
615
-
616
- const person = Obj.make(DataType.Person, {
617
- fullName: 'Eva Martinez',
618
- });
619
- yield* DatabaseService.add(person);
620
-
621
- // Create a subscription trigger with input pattern
622
- const trigger = Obj.make(FunctionTrigger, {
623
- function: Ref.make(functionObj),
624
- enabled: true,
625
- spec: {
626
- kind: 'subscription',
627
- query: Query.select(Filter.type(DataType.Person)).ast,
628
- },
629
- input: {
630
- objectId: '{{event.changedObjectId}}',
631
- changeType: '{{event.type}}',
632
- triggerId: '{{trigger.id}}',
633
- },
634
- });
635
- yield* DatabaseService.add(trigger);
636
-
637
- const dispatcher = yield* TriggerDispatcher;
638
- const results = yield* dispatcher.invokeScheduledTriggers({ kinds: ['subscription'] });
639
-
640
- expect(results.length).toBe(1);
641
- const exit = results[0].result;
642
- invariant(Exit.isSuccess(exit));
643
- expect(exit.value).to.deep.include({
644
- objectId: person.id,
645
- changeType: 'unknown', // TODO: This should be 'create' or 'update'
646
- triggerId: trigger.id,
647
- });
648
- }, Effect.provide(TestTriggerDispatcherLayer)),
649
- );
650
- });
651
- });