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