@dxos/functions 0.5.3-main.79e0565 → 0.5.3-main.7e64682
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/chunk-CMMH5BA6.mjs +86 -0
- package/dist/lib/browser/chunk-CMMH5BA6.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +211 -204
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/types.mjs +14 -0
- package/dist/lib/browser/types.mjs.map +7 -0
- package/dist/lib/node/chunk-3C2FP4J4.cjs +103 -0
- package/dist/lib/node/chunk-3C2FP4J4.cjs.map +7 -0
- package/dist/lib/node/index.cjs +218 -203
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/types.cjs +35 -0
- package/dist/lib/node/types.cjs.map +7 -0
- package/dist/types/src/browser/index.d.ts +2 -0
- package/dist/types/src/browser/index.d.ts.map +1 -0
- package/dist/types/src/function/function-registry.d.ts.map +1 -1
- package/dist/types/src/handler.d.ts.map +1 -1
- package/dist/types/src/runtime/dev-server.d.ts.map +1 -1
- package/dist/types/src/runtime/scheduler.d.ts +1 -1
- package/dist/types/src/runtime/scheduler.d.ts.map +1 -1
- package/dist/types/src/testing/setup.d.ts.map +1 -1
- package/dist/types/src/trigger/trigger-registry.d.ts +2 -5
- package/dist/types/src/trigger/trigger-registry.d.ts.map +1 -1
- package/dist/types/src/trigger/type/subscription-trigger.d.ts.map +1 -1
- package/dist/types/src/trigger/type/timer-trigger.d.ts.map +1 -1
- package/dist/types/src/trigger/type/webhook-trigger.d.ts.map +1 -1
- package/dist/types/src/trigger/type/websocket-trigger.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +43 -33
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +30 -16
- package/schema/functions.json +5 -0
- package/src/browser/index.ts +5 -0
- package/src/function/function-registry.ts +5 -5
- package/src/runtime/dev-server.ts +4 -4
- package/src/runtime/scheduler.test.ts +14 -9
- package/src/runtime/scheduler.ts +14 -9
- package/src/testing/functions-integration.test.ts +1 -0
- package/src/testing/setup.ts +8 -10
- package/src/trigger/trigger-registry.test.ts +30 -13
- package/src/trigger/trigger-registry.ts +59 -37
- package/src/trigger/type/subscription-trigger.ts +13 -7
- package/src/trigger/type/timer-trigger.ts +4 -3
- package/src/trigger/type/webhook-trigger.ts +3 -2
- package/src/trigger/type/websocket-trigger.ts +4 -3
- package/src/types.ts +39 -30
- package/dist/types/src/util.d.ts +0 -15
- package/dist/types/src/util.d.ts.map +0 -1
- package/dist/types/src/util.test.d.ts +0 -2
- package/dist/types/src/util.test.d.ts.map +0 -1
- package/src/util.test.ts +0 -43
- package/src/util.ts +0 -48
|
@@ -6,7 +6,7 @@ import { S } from '@dxos/echo-schema';
|
|
|
6
6
|
* https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions
|
|
7
7
|
*/
|
|
8
8
|
export type FunctionTriggerType = 'subscription' | 'timer' | 'webhook' | 'websocket';
|
|
9
|
-
declare const SubscriptionTriggerSchema: S.struct<{
|
|
9
|
+
declare const SubscriptionTriggerSchema: S.mutable<S.struct<{
|
|
10
10
|
type: S.literal<["subscription"]>;
|
|
11
11
|
filter: S.array<S.struct<{
|
|
12
12
|
type: S.$string;
|
|
@@ -23,12 +23,12 @@ declare const SubscriptionTriggerSchema: S.struct<{
|
|
|
23
23
|
readonly deep?: boolean | undefined;
|
|
24
24
|
readonly delay?: number | undefined;
|
|
25
25
|
} | undefined, never>;
|
|
26
|
-
}
|
|
26
|
+
}>>;
|
|
27
27
|
export type SubscriptionTrigger = S.Schema.Type<typeof SubscriptionTriggerSchema>;
|
|
28
|
-
declare const TimerTriggerSchema: S.struct<{
|
|
28
|
+
declare const TimerTriggerSchema: S.mutable<S.struct<{
|
|
29
29
|
type: S.literal<["timer"]>;
|
|
30
30
|
cron: S.$string;
|
|
31
|
-
}
|
|
31
|
+
}>>;
|
|
32
32
|
export type TimerTrigger = S.Schema.Type<typeof TimerTriggerSchema>;
|
|
33
33
|
declare const WebhookTriggerSchema: S.mutable<S.struct<{
|
|
34
34
|
type: S.literal<["webhook"]>;
|
|
@@ -36,7 +36,7 @@ declare const WebhookTriggerSchema: S.mutable<S.struct<{
|
|
|
36
36
|
port: S.PropertySignature<"?:", number | undefined, never, "?:", number | undefined, never>;
|
|
37
37
|
}>>;
|
|
38
38
|
export type WebhookTrigger = S.Schema.Type<typeof WebhookTriggerSchema>;
|
|
39
|
-
declare const WebsocketTriggerSchema: S.struct<{
|
|
39
|
+
declare const WebsocketTriggerSchema: S.mutable<S.struct<{
|
|
40
40
|
type: S.literal<["websocket"]>;
|
|
41
41
|
url: S.$string;
|
|
42
42
|
init: S.PropertySignature<"?:", {
|
|
@@ -44,7 +44,7 @@ declare const WebsocketTriggerSchema: S.struct<{
|
|
|
44
44
|
} | undefined, never, "?:", {
|
|
45
45
|
readonly [x: string]: any;
|
|
46
46
|
} | undefined, never>;
|
|
47
|
-
}
|
|
47
|
+
}>>;
|
|
48
48
|
export type WebsocketTrigger = S.Schema.Type<typeof WebsocketTriggerSchema>;
|
|
49
49
|
export type TriggerSpec = TimerTrigger | WebhookTrigger | WebsocketTrigger | SubscriptionTrigger;
|
|
50
50
|
declare const FunctionDef_base: import("@dxos/echo-schema").EchoSchemaClass<{
|
|
@@ -62,30 +62,33 @@ export declare class FunctionDef extends FunctionDef_base {
|
|
|
62
62
|
}
|
|
63
63
|
declare const FunctionTrigger_base: import("@dxos/echo-schema").EchoSchemaClass<{
|
|
64
64
|
function: string;
|
|
65
|
-
|
|
65
|
+
enabled?: boolean | undefined;
|
|
66
|
+
meta?: {
|
|
67
|
+
[x: string]: any;
|
|
68
|
+
} | undefined;
|
|
66
69
|
spec: {
|
|
67
|
-
|
|
70
|
+
filter: readonly {
|
|
68
71
|
readonly type: string;
|
|
69
72
|
readonly props?: {
|
|
70
73
|
readonly [x: string]: any;
|
|
71
74
|
} | undefined;
|
|
72
75
|
}[];
|
|
73
|
-
|
|
74
|
-
|
|
76
|
+
type: "subscription";
|
|
77
|
+
options?: {
|
|
75
78
|
readonly deep?: boolean | undefined;
|
|
76
79
|
readonly delay?: number | undefined;
|
|
77
80
|
} | undefined;
|
|
78
81
|
} | {
|
|
79
|
-
|
|
80
|
-
|
|
82
|
+
type: "timer";
|
|
83
|
+
cron: string;
|
|
81
84
|
} | {
|
|
82
85
|
port?: number | undefined;
|
|
83
86
|
type: "webhook";
|
|
84
87
|
method: string;
|
|
85
88
|
} | {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
+
type: "websocket";
|
|
90
|
+
url: string;
|
|
91
|
+
init?: {
|
|
89
92
|
readonly [x: string]: any;
|
|
90
93
|
} | undefined;
|
|
91
94
|
};
|
|
@@ -125,30 +128,33 @@ export declare const FunctionManifestSchema: S.struct<{
|
|
|
125
128
|
})[] | undefined, never>;
|
|
126
129
|
triggers: S.PropertySignature<"?:", ({
|
|
127
130
|
function: string;
|
|
128
|
-
|
|
131
|
+
enabled?: boolean | undefined;
|
|
132
|
+
meta?: {
|
|
133
|
+
[x: string]: any;
|
|
134
|
+
} | undefined;
|
|
129
135
|
spec: {
|
|
130
|
-
|
|
136
|
+
filter: readonly {
|
|
131
137
|
readonly type: string;
|
|
132
138
|
readonly props?: {
|
|
133
139
|
readonly [x: string]: any;
|
|
134
140
|
} | undefined;
|
|
135
141
|
}[];
|
|
136
|
-
|
|
137
|
-
|
|
142
|
+
type: "subscription";
|
|
143
|
+
options?: {
|
|
138
144
|
readonly deep?: boolean | undefined;
|
|
139
145
|
readonly delay?: number | undefined;
|
|
140
146
|
} | undefined;
|
|
141
147
|
} | {
|
|
142
|
-
|
|
143
|
-
|
|
148
|
+
type: "timer";
|
|
149
|
+
cron: string;
|
|
144
150
|
} | {
|
|
145
151
|
port?: number | undefined;
|
|
146
152
|
type: "webhook";
|
|
147
153
|
method: string;
|
|
148
154
|
} | {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
155
|
+
type: "websocket";
|
|
156
|
+
url: string;
|
|
157
|
+
init?: {
|
|
152
158
|
readonly [x: string]: any;
|
|
153
159
|
} | undefined;
|
|
154
160
|
};
|
|
@@ -161,30 +167,33 @@ export declare const FunctionManifestSchema: S.struct<{
|
|
|
161
167
|
} | undefined;
|
|
162
168
|
})[] | undefined, never, "?:", ({
|
|
163
169
|
function: string;
|
|
164
|
-
|
|
170
|
+
enabled?: boolean | undefined;
|
|
171
|
+
meta?: {
|
|
172
|
+
[x: string]: any;
|
|
173
|
+
} | undefined;
|
|
165
174
|
spec: {
|
|
166
|
-
|
|
175
|
+
filter: readonly {
|
|
167
176
|
readonly type: string;
|
|
168
177
|
readonly props?: {
|
|
169
178
|
readonly [x: string]: any;
|
|
170
179
|
} | undefined;
|
|
171
180
|
}[];
|
|
172
|
-
|
|
173
|
-
|
|
181
|
+
type: "subscription";
|
|
182
|
+
options?: {
|
|
174
183
|
readonly deep?: boolean | undefined;
|
|
175
184
|
readonly delay?: number | undefined;
|
|
176
185
|
} | undefined;
|
|
177
186
|
} | {
|
|
178
|
-
|
|
179
|
-
|
|
187
|
+
type: "timer";
|
|
188
|
+
cron: string;
|
|
180
189
|
} | {
|
|
181
190
|
port?: number | undefined;
|
|
182
191
|
type: "webhook";
|
|
183
192
|
method: string;
|
|
184
193
|
} | {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
194
|
+
type: "websocket";
|
|
195
|
+
url: string;
|
|
196
|
+
init?: {
|
|
188
197
|
readonly [x: string]: any;
|
|
189
198
|
} | undefined;
|
|
190
199
|
};
|
|
@@ -198,5 +207,6 @@ export declare const FunctionManifestSchema: S.struct<{
|
|
|
198
207
|
})[] | undefined, never>;
|
|
199
208
|
}>;
|
|
200
209
|
export type FunctionManifest = S.Schema.Type<typeof FunctionManifestSchema>;
|
|
210
|
+
export declare const FUNCTION_SCHEMA: (typeof FunctionDef | typeof FunctionTrigger)[];
|
|
201
211
|
export {};
|
|
202
212
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAIA,OAAO,EAAa,CAAC,EAAe,MAAM,mBAAmB,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,cAAc,GAAG,OAAO,GAAG,SAAS,GAAG,WAAW,CAAC;AAErF,QAAA,MAAM,yBAAyB;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAIA,OAAO,EAAa,CAAC,EAAe,MAAM,mBAAmB,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,cAAc,GAAG,OAAO,GAAG,SAAS,GAAG,WAAW,CAAC;AAErF,QAAA,MAAM,yBAAyB;;;;;;;;;;;;;;;;;GAmB9B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAElF,QAAA,MAAM,kBAAkB;;;GAKvB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,QAAA,MAAM,oBAAoB;;;;GAOzB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE,QAAA,MAAM,sBAAsB;;;;;;;;GAM3B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAS5E,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,cAAc,GAAG,gBAAgB,GAAG,mBAAmB,CAAC;;;;;;;;;AAEjG;;GAEG;AACH,qBAAa,WAAY,SAAQ,gBAQ/B;CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEL,qBAAa,eAAgB,SAAQ,oBASnC;CAAG;AAEL;;GAEG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGjC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAG5E,eAAO,MAAM,eAAe,iDAAiC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/functions",
|
|
3
|
-
"version": "0.5.3-main.
|
|
4
|
-
"description": "Functions
|
|
3
|
+
"version": "0.5.3-main.7e64682",
|
|
4
|
+
"description": "Functions API and runtime.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
7
7
|
"license": "MIT",
|
|
@@ -10,10 +10,24 @@
|
|
|
10
10
|
".": {
|
|
11
11
|
"browser": "./dist/lib/browser/index.mjs",
|
|
12
12
|
"node": "./dist/lib/node/index.cjs",
|
|
13
|
-
"default": "./dist/lib/node/index.cjs"
|
|
13
|
+
"default": "./dist/lib/node/index.cjs",
|
|
14
|
+
"types": "./dist/types/src/index.d.ts"
|
|
15
|
+
},
|
|
16
|
+
"./types": {
|
|
17
|
+
"browser": "./dist/lib/browser/types.mjs",
|
|
18
|
+
"node": "./dist/lib/node/types.cjs",
|
|
19
|
+
"default": "./dist/lib/node/types.cjs",
|
|
20
|
+
"types": "./dist/types/src/types.d.ts"
|
|
14
21
|
}
|
|
15
22
|
},
|
|
16
23
|
"types": "dist/types/src/index.d.ts",
|
|
24
|
+
"typesVersions": {
|
|
25
|
+
"*": {
|
|
26
|
+
"types": [
|
|
27
|
+
"dist/types/src/types.d.ts"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
17
31
|
"files": [
|
|
18
32
|
"dist",
|
|
19
33
|
"schema",
|
|
@@ -26,23 +40,23 @@
|
|
|
26
40
|
"express": "^4.19.2",
|
|
27
41
|
"get-port-please": "^3.1.1",
|
|
28
42
|
"ws": "^8.14.2",
|
|
29
|
-
"@braneframe/types": "0.5.3-main.
|
|
30
|
-
"@dxos/async": "0.5.3-main.
|
|
31
|
-
"@dxos/client": "0.5.3-main.
|
|
32
|
-
"@dxos/context": "0.5.3-main.
|
|
33
|
-
"@dxos/echo-db": "0.5.3-main.
|
|
34
|
-
"@dxos/
|
|
35
|
-
"@dxos/
|
|
36
|
-
"@dxos/keys": "0.5.3-main.
|
|
37
|
-
"@dxos/
|
|
38
|
-
"@dxos/
|
|
39
|
-
"@dxos/
|
|
40
|
-
"@dxos/
|
|
43
|
+
"@braneframe/types": "0.5.3-main.7e64682",
|
|
44
|
+
"@dxos/async": "0.5.3-main.7e64682",
|
|
45
|
+
"@dxos/client": "0.5.3-main.7e64682",
|
|
46
|
+
"@dxos/context": "0.5.3-main.7e64682",
|
|
47
|
+
"@dxos/echo-db": "0.5.3-main.7e64682",
|
|
48
|
+
"@dxos/echo-schema": "0.5.3-main.7e64682",
|
|
49
|
+
"@dxos/invariant": "0.5.3-main.7e64682",
|
|
50
|
+
"@dxos/keys": "0.5.3-main.7e64682",
|
|
51
|
+
"@dxos/log": "0.5.3-main.7e64682",
|
|
52
|
+
"@dxos/protocols": "0.5.3-main.7e64682",
|
|
53
|
+
"@dxos/node-std": "0.5.3-main.7e64682",
|
|
54
|
+
"@dxos/util": "0.5.3-main.7e64682"
|
|
41
55
|
},
|
|
42
56
|
"devDependencies": {
|
|
43
57
|
"@types/express": "^4.17.17",
|
|
44
58
|
"@types/ws": "^7.4.0",
|
|
45
|
-
"@dxos/agent": "0.5.3-main.
|
|
59
|
+
"@dxos/agent": "0.5.3-main.7e64682"
|
|
46
60
|
},
|
|
47
61
|
"publishConfig": {
|
|
48
62
|
"access": "public"
|
package/schema/functions.json
CHANGED
|
@@ -8,10 +8,9 @@ import { create, Filter, type Space } from '@dxos/client/echo';
|
|
|
8
8
|
import { type Context, Resource } from '@dxos/context';
|
|
9
9
|
import { PublicKey } from '@dxos/keys';
|
|
10
10
|
import { log } from '@dxos/log';
|
|
11
|
-
import { ComplexMap } from '@dxos/util';
|
|
11
|
+
import { ComplexMap, diff } from '@dxos/util';
|
|
12
12
|
|
|
13
13
|
import { FunctionDef, type FunctionManifest } from '../types';
|
|
14
|
-
import { diff } from '../util';
|
|
15
14
|
|
|
16
15
|
export type FunctionsRegisteredEvent = {
|
|
17
16
|
space: Space;
|
|
@@ -46,13 +45,13 @@ export class FunctionRegistry extends Resource {
|
|
|
46
45
|
|
|
47
46
|
// Sync definitions.
|
|
48
47
|
const { objects: existing } = await space.db.query(Filter.schema(FunctionDef)).run();
|
|
49
|
-
const { added
|
|
50
|
-
added.forEach((def) => space.db.add(create(FunctionDef, def)));
|
|
48
|
+
const { added } = diff(existing, functions, (a, b) => a.uri === b.uri);
|
|
51
49
|
// TODO(burdon): Update existing templates.
|
|
52
|
-
|
|
50
|
+
added.forEach((def) => space.db.add(create(FunctionDef, def)));
|
|
53
51
|
}
|
|
54
52
|
|
|
55
53
|
protected override async _open(): Promise<void> {
|
|
54
|
+
log.info('opening...');
|
|
56
55
|
const spacesSubscription = this._client.spaces.subscribe(async (spaces) => {
|
|
57
56
|
for (const space of spaces) {
|
|
58
57
|
if (this._functionBySpaceKey.has(space.key)) {
|
|
@@ -85,6 +84,7 @@ export class FunctionRegistry extends Resource {
|
|
|
85
84
|
}
|
|
86
85
|
|
|
87
86
|
protected override async _close(_: Context): Promise<void> {
|
|
87
|
+
log.info('closing...');
|
|
88
88
|
this._functionBySpaceKey.clear();
|
|
89
89
|
}
|
|
90
90
|
}
|
|
@@ -46,6 +46,7 @@ export class DevServer {
|
|
|
46
46
|
private readonly _functionsRegistry: FunctionRegistry,
|
|
47
47
|
private readonly _options: DevServerOptions,
|
|
48
48
|
) {
|
|
49
|
+
// TODO(burdon): Add/remove listener in start/stop.
|
|
49
50
|
this._functionsRegistry.registered.on(async ({ added }) => {
|
|
50
51
|
added.forEach((def) => this._load(def));
|
|
51
52
|
await this._safeUpdateRegistration();
|
|
@@ -157,7 +158,7 @@ export class DevServer {
|
|
|
157
158
|
/**
|
|
158
159
|
* Load function.
|
|
159
160
|
*/
|
|
160
|
-
private async _load(def: FunctionDef, force
|
|
161
|
+
private async _load(def: FunctionDef, force?: boolean | undefined) {
|
|
161
162
|
const { uri, route, handler } = def;
|
|
162
163
|
const filePath = join(this._options.baseDir, handler);
|
|
163
164
|
log.info('loading', { uri, force });
|
|
@@ -188,8 +189,8 @@ export class DevServer {
|
|
|
188
189
|
registrationId: this._functionServiceRegistration,
|
|
189
190
|
functions: this.functions.map(({ def: { id, route } }) => ({ id, route })),
|
|
190
191
|
});
|
|
191
|
-
} catch (
|
|
192
|
-
log.catch(
|
|
192
|
+
} catch (err) {
|
|
193
|
+
log.catch(err);
|
|
193
194
|
}
|
|
194
195
|
}
|
|
195
196
|
|
|
@@ -211,7 +212,6 @@ export class DevServer {
|
|
|
211
212
|
private async _invoke(path: string, event: FunctionEvent) {
|
|
212
213
|
const { handler } = this._handlers[path] ?? {};
|
|
213
214
|
invariant(handler, `invalid path: ${path}`);
|
|
214
|
-
|
|
215
215
|
const context: FunctionContext = {
|
|
216
216
|
client: this._client,
|
|
217
217
|
dataDir: this._options.dataDir,
|
|
@@ -29,6 +29,15 @@ describe('scheduler', () => {
|
|
|
29
29
|
await testBuilder.destroy();
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
+
const createScheduler = (callback: SchedulerOptions['callback']) => {
|
|
33
|
+
const scheduler = new Scheduler(new FunctionRegistry(client), new TriggerRegistry(client), { callback });
|
|
34
|
+
after(async () => {
|
|
35
|
+
await scheduler.stop();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return scheduler;
|
|
39
|
+
};
|
|
40
|
+
|
|
32
41
|
test('timer', async () => {
|
|
33
42
|
const manifest: FunctionManifest = {
|
|
34
43
|
functions: [
|
|
@@ -41,6 +50,7 @@ describe('scheduler', () => {
|
|
|
41
50
|
triggers: [
|
|
42
51
|
{
|
|
43
52
|
function: 'example.com/function/test',
|
|
53
|
+
enabled: true,
|
|
44
54
|
spec: {
|
|
45
55
|
type: 'timer',
|
|
46
56
|
cron: '0/1 * * * * *', // Every 1s.
|
|
@@ -75,6 +85,7 @@ describe('scheduler', () => {
|
|
|
75
85
|
triggers: [
|
|
76
86
|
{
|
|
77
87
|
function: 'example.com/function/test',
|
|
88
|
+
enabled: true,
|
|
78
89
|
spec: {
|
|
79
90
|
type: 'webhook',
|
|
80
91
|
method: 'GET',
|
|
@@ -108,6 +119,7 @@ describe('scheduler', () => {
|
|
|
108
119
|
triggers: [
|
|
109
120
|
{
|
|
110
121
|
function: 'example.com/function/test',
|
|
122
|
+
enabled: true,
|
|
111
123
|
spec: {
|
|
112
124
|
type: 'websocket',
|
|
113
125
|
// url: 'https://hub.dxos.network/api/mailbox/test',
|
|
@@ -154,6 +166,7 @@ describe('scheduler', () => {
|
|
|
154
166
|
triggers: [
|
|
155
167
|
{
|
|
156
168
|
function: 'example.com/function/test',
|
|
169
|
+
enabled: true,
|
|
157
170
|
spec: {
|
|
158
171
|
type: 'subscription',
|
|
159
172
|
filter: [{ type: TestType.typename }],
|
|
@@ -165,7 +178,7 @@ describe('scheduler', () => {
|
|
|
165
178
|
let count = 0;
|
|
166
179
|
const done = new Trigger();
|
|
167
180
|
const scheduler = createScheduler(async () => {
|
|
168
|
-
if (++count ===
|
|
181
|
+
if (++count === 1) {
|
|
169
182
|
done.wake();
|
|
170
183
|
}
|
|
171
184
|
});
|
|
@@ -180,12 +193,4 @@ describe('scheduler', () => {
|
|
|
180
193
|
|
|
181
194
|
await done.wait();
|
|
182
195
|
});
|
|
183
|
-
|
|
184
|
-
const createScheduler = (callback: SchedulerOptions['callback']) => {
|
|
185
|
-
const scheduler = new Scheduler(new FunctionRegistry(client), new TriggerRegistry(client), { callback });
|
|
186
|
-
after(async () => {
|
|
187
|
-
await scheduler.stop();
|
|
188
|
-
});
|
|
189
|
-
return scheduler;
|
|
190
|
-
};
|
|
191
196
|
});
|
package/src/runtime/scheduler.ts
CHANGED
|
@@ -27,7 +27,7 @@ export type SchedulerOptions = {
|
|
|
27
27
|
export class Scheduler {
|
|
28
28
|
private _ctx = createContext();
|
|
29
29
|
|
|
30
|
-
private readonly
|
|
30
|
+
private readonly _functionUriToCallMutex = new Map<string, Mutex>();
|
|
31
31
|
|
|
32
32
|
constructor(
|
|
33
33
|
public readonly functions: FunctionRegistry,
|
|
@@ -72,23 +72,28 @@ export class Scheduler {
|
|
|
72
72
|
await Promise.all(mountTasks).catch(log.catch);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
private async activate(space: Space, functions: FunctionDef[],
|
|
76
|
-
const definition = functions.find((def) => def.uri ===
|
|
75
|
+
private async activate(space: Space, functions: FunctionDef[], trigger: FunctionTrigger) {
|
|
76
|
+
const definition = functions.find((def) => def.uri === trigger.function);
|
|
77
77
|
if (!definition) {
|
|
78
|
-
log.info('function is not found for trigger', {
|
|
78
|
+
log.info('function is not found for trigger', { trigger });
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
await this.triggers.activate(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
await this.triggers.activate(space, trigger, async (args) => {
|
|
83
|
+
const mutex = this._functionUriToCallMutex.get(definition.uri) ?? new Mutex();
|
|
84
|
+
this._functionUriToCallMutex.set(definition.uri, mutex);
|
|
85
|
+
|
|
86
|
+
log.info('function triggered, waiting for mutex', { uri: definition.uri });
|
|
87
|
+
return mutex.executeSynchronized(() => {
|
|
88
|
+
log.info('mutex acquired', { uri: definition.uri });
|
|
89
|
+
return this._execFunction(definition, trigger, {
|
|
90
|
+
meta: trigger.meta ?? {},
|
|
86
91
|
data: { ...args, spaceKey: space.key },
|
|
87
92
|
});
|
|
88
93
|
});
|
|
89
94
|
});
|
|
90
95
|
|
|
91
|
-
log('activated trigger', { space: space.key, trigger
|
|
96
|
+
log('activated trigger', { space: space.key, trigger });
|
|
92
97
|
}
|
|
93
98
|
|
|
94
99
|
private async _execFunction<TData, TMeta>(
|
package/src/testing/setup.ts
CHANGED
|
@@ -10,16 +10,16 @@ import { range } from '@dxos/util';
|
|
|
10
10
|
import { TestType } from './types';
|
|
11
11
|
import { FunctionDef, FunctionTrigger } from '../types';
|
|
12
12
|
|
|
13
|
-
// TODO(burdon):
|
|
13
|
+
// TODO(burdon): Extend/wrap TestBuilder.
|
|
14
14
|
|
|
15
15
|
export const createInitializedClients = async (testBuilder: TestBuilder, count: number = 1, config?: Config) => {
|
|
16
16
|
const clients = range(count).map(() => new Client({ config, services: testBuilder.createLocalClientServices() }));
|
|
17
|
-
testBuilder.ctx.onDispose(() => Promise.all(clients.map((
|
|
17
|
+
testBuilder.ctx.onDispose(() => Promise.all(clients.map((client) => client.destroy())));
|
|
18
18
|
return Promise.all(
|
|
19
19
|
clients.map(async (client, index) => {
|
|
20
20
|
await client.initialize();
|
|
21
21
|
await client.halo.createIdentity({ displayName: `Peer ${index}` });
|
|
22
|
-
client.addSchema(
|
|
22
|
+
client.addSchema(FunctionDef, FunctionTrigger, TestType);
|
|
23
23
|
return client;
|
|
24
24
|
}),
|
|
25
25
|
);
|
|
@@ -34,12 +34,10 @@ export const createFunctionRuntime = async (testBuilder: TestBuilder): Promise<C
|
|
|
34
34
|
},
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
-
const client =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
await functionsPlugin.open();
|
|
43
|
-
testBuilder.ctx.onDispose(() => functionsPlugin.close());
|
|
37
|
+
const [client] = await createInitializedClients(testBuilder, 1, config);
|
|
38
|
+
const plugin = new FunctionsPlugin();
|
|
39
|
+
await plugin.initialize({ client, clientServices: client.services });
|
|
40
|
+
await plugin.open();
|
|
41
|
+
testBuilder.ctx.onDispose(() => plugin.close());
|
|
44
42
|
return client;
|
|
45
43
|
};
|
|
@@ -31,6 +31,7 @@ const manifest: FunctionManifest = {
|
|
|
31
31
|
],
|
|
32
32
|
},
|
|
33
33
|
function: 'example.com/function/webhook-test',
|
|
34
|
+
enabled: true,
|
|
34
35
|
spec: {
|
|
35
36
|
type: 'webhook',
|
|
36
37
|
method: 'GET',
|
|
@@ -46,6 +47,7 @@ const manifest: FunctionManifest = {
|
|
|
46
47
|
],
|
|
47
48
|
},
|
|
48
49
|
function: 'example.com/function/subscription-test',
|
|
50
|
+
enabled: true,
|
|
49
51
|
spec: {
|
|
50
52
|
type: 'subscription',
|
|
51
53
|
filter: [
|
|
@@ -74,7 +76,7 @@ describe('trigger registry', () => {
|
|
|
74
76
|
|
|
75
77
|
describe('register', () => {
|
|
76
78
|
test('creates new triggers', async () => {
|
|
77
|
-
const client =
|
|
79
|
+
const [client] = await createInitializedClients(testBuilder);
|
|
78
80
|
const registry = createRegistry(client);
|
|
79
81
|
const space = await client.spaces.create();
|
|
80
82
|
await registry.register(space, manifest);
|
|
@@ -84,11 +86,24 @@ describe('trigger registry', () => {
|
|
|
84
86
|
const expected = manifest.triggers?.map((trigger) => trigger.function).sort();
|
|
85
87
|
expect(objects.map((object: FunctionTrigger) => object.function).sort()).to.deep.eq(expected);
|
|
86
88
|
});
|
|
89
|
+
|
|
90
|
+
test('set meta', () => {
|
|
91
|
+
const trigger = create(FunctionTrigger, {
|
|
92
|
+
function: 'example.com/function/webhook-test',
|
|
93
|
+
spec: {
|
|
94
|
+
type: 'webhook',
|
|
95
|
+
method: 'GET',
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
(trigger.meta ??= {}).test = 100;
|
|
100
|
+
expect(trigger.meta.test).to.eq(100);
|
|
101
|
+
});
|
|
87
102
|
});
|
|
88
103
|
|
|
89
104
|
describe('activate', () => {
|
|
90
105
|
test('invokes the provided callback', async () => {
|
|
91
|
-
const client =
|
|
106
|
+
const [client] = await createInitializedClients(testBuilder);
|
|
92
107
|
const space = await client.spaces.create();
|
|
93
108
|
const registry = createRegistry(client);
|
|
94
109
|
await registry.register(space, manifest);
|
|
@@ -98,7 +113,7 @@ describe('trigger registry', () => {
|
|
|
98
113
|
const callbackInvoked = new Trigger();
|
|
99
114
|
const { objects: allTriggers } = await space.db.query(Filter.schema(FunctionTrigger)).run();
|
|
100
115
|
const webhookTrigger = allTriggers.find((trigger: FunctionTrigger) => trigger.spec.type === 'webhook')!;
|
|
101
|
-
await registry.activate(
|
|
116
|
+
await registry.activate(space, webhookTrigger, async () => {
|
|
102
117
|
callbackInvoked.wake();
|
|
103
118
|
return 200;
|
|
104
119
|
});
|
|
@@ -108,7 +123,7 @@ describe('trigger registry', () => {
|
|
|
108
123
|
});
|
|
109
124
|
|
|
110
125
|
test('removes from inactive list', async () => {
|
|
111
|
-
const client =
|
|
126
|
+
const [client] = await createInitializedClients(testBuilder);
|
|
112
127
|
const space = await client.spaces.create();
|
|
113
128
|
const registry = createRegistry(client);
|
|
114
129
|
await registry.register(space, manifest);
|
|
@@ -116,17 +131,19 @@ describe('trigger registry', () => {
|
|
|
116
131
|
await waitForInactiveTriggers(registry, space);
|
|
117
132
|
|
|
118
133
|
const inactiveTrigger = registry.getInactiveTriggers(space)[0];
|
|
119
|
-
await registry.activate(
|
|
134
|
+
await registry.activate(space, inactiveTrigger, async () => 200);
|
|
120
135
|
|
|
121
136
|
const updatedInactiveList = registry.getInactiveTriggers(space);
|
|
122
137
|
expect(updatedInactiveList.find((trigger: FunctionTrigger) => trigger.function === inactiveTrigger.function)).to
|
|
123
138
|
.be.undefined;
|
|
124
139
|
});
|
|
140
|
+
|
|
141
|
+
// TODO(burdon): Test enable/disable trigger.
|
|
125
142
|
});
|
|
126
143
|
|
|
127
144
|
describe('deactivate', () => {
|
|
128
145
|
test('trigger object deletion deactivates a trigger', async () => {
|
|
129
|
-
const client =
|
|
146
|
+
const [client] = await createInitializedClients(testBuilder);
|
|
130
147
|
const space = await client.spaces.create();
|
|
131
148
|
const registry = createRegistry(client);
|
|
132
149
|
await registry.register(space, manifest);
|
|
@@ -136,7 +153,7 @@ describe('trigger registry', () => {
|
|
|
136
153
|
const { objects: allTriggers } = await space.db.query(Filter.schema(FunctionTrigger)).run();
|
|
137
154
|
const echoTrigger = allTriggers.find((trigger: FunctionTrigger) => trigger.spec.type === 'subscription')!;
|
|
138
155
|
let count = 0;
|
|
139
|
-
await registry.activate(
|
|
156
|
+
await registry.activate(space, echoTrigger, async () => {
|
|
140
157
|
count++;
|
|
141
158
|
return 200;
|
|
142
159
|
});
|
|
@@ -152,7 +169,7 @@ describe('trigger registry', () => {
|
|
|
152
169
|
});
|
|
153
170
|
|
|
154
171
|
test('registry closing deactivates a trigger', async () => {
|
|
155
|
-
const client =
|
|
172
|
+
const [client] = await createInitializedClients(testBuilder);
|
|
156
173
|
const space = await client.spaces.create();
|
|
157
174
|
const registry = createRegistry(client);
|
|
158
175
|
await registry.register(space, manifest);
|
|
@@ -162,7 +179,7 @@ describe('trigger registry', () => {
|
|
|
162
179
|
const { objects: allTriggers } = await space.db.query(Filter.schema(FunctionTrigger)).run();
|
|
163
180
|
const echoTrigger = allTriggers.find((trigger: FunctionTrigger) => trigger.spec.type === 'subscription')!;
|
|
164
181
|
let count = 0;
|
|
165
|
-
await registry.activate(
|
|
182
|
+
await registry.activate(space, echoTrigger, async () => {
|
|
166
183
|
count++;
|
|
167
184
|
return 200;
|
|
168
185
|
});
|
|
@@ -176,8 +193,8 @@ describe('trigger registry', () => {
|
|
|
176
193
|
});
|
|
177
194
|
|
|
178
195
|
describe('trigger events', () => {
|
|
179
|
-
test
|
|
180
|
-
const client =
|
|
196
|
+
test('event fired when all registered when opened', async () => {
|
|
197
|
+
const [client] = await createInitializedClients(testBuilder);
|
|
181
198
|
const registry = createRegistry(client);
|
|
182
199
|
const triggers = createTriggers(client.spaces.default, 3);
|
|
183
200
|
|
|
@@ -194,7 +211,7 @@ describe('trigger registry', () => {
|
|
|
194
211
|
});
|
|
195
212
|
|
|
196
213
|
test('event fired when a new trigger is added', async () => {
|
|
197
|
-
const client =
|
|
214
|
+
const [client] = await createInitializedClients(testBuilder);
|
|
198
215
|
const registry = createRegistry(client);
|
|
199
216
|
const space = await client.spaces.create();
|
|
200
217
|
|
|
@@ -210,7 +227,7 @@ describe('trigger registry', () => {
|
|
|
210
227
|
});
|
|
211
228
|
|
|
212
229
|
test('event fired when a new trigger is removed', async () => {
|
|
213
|
-
const client =
|
|
230
|
+
const [client] = await createInitializedClients(testBuilder);
|
|
214
231
|
const registry = createRegistry(client);
|
|
215
232
|
const space = await client.spaces.create();
|
|
216
233
|
const triggers = createTriggers(space, 3);
|