@dxos/functions 0.5.3-main.cb47aab → 0.5.3-main.d28cf09
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-HRU7VDYL.mjs +87 -0
- package/dist/lib/browser/chunk-HRU7VDYL.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +325 -308
- 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-R5JUDLSN.cjs +104 -0
- package/dist/lib/node/chunk-R5JUDLSN.cjs.map +7 -0
- package/dist/lib/node/index.cjs +332 -309
- 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/{registry → function}/function-registry.d.ts +4 -4
- package/dist/types/src/function/function-registry.d.ts.map +1 -0
- package/dist/types/src/function/function-registry.test.d.ts.map +1 -0
- package/dist/types/src/function/index.d.ts.map +1 -0
- package/dist/types/src/handler.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/runtime/dev-server.d.ts +1 -1
- package/dist/types/src/runtime/dev-server.d.ts.map +1 -1
- package/dist/types/src/runtime/scheduler.d.ts +2 -1
- package/dist/types/src/runtime/scheduler.d.ts.map +1 -1
- 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/types.d.ts +67 -49
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +31 -18
- package/schema/functions.json +18 -9
- package/src/browser/index.ts +5 -0
- package/src/{registry → function}/function-registry.test.ts +10 -10
- package/src/function/function-registry.ts +90 -0
- package/src/index.ts +1 -1
- package/src/runtime/dev-server.test.ts +2 -2
- package/src/runtime/dev-server.ts +5 -6
- package/src/runtime/scheduler.test.ts +1 -1
- package/src/runtime/scheduler.ts +21 -8
- package/src/testing/functions-integration.test.ts +1 -1
- package/src/testing/setup.ts +1 -1
- package/src/trigger/trigger-registry.test.ts +60 -34
- package/src/trigger/trigger-registry.ts +37 -12
- package/src/trigger/type/subscription-trigger.ts +17 -10
- package/src/types.ts +48 -36
- package/dist/types/src/registry/function-registry.d.ts.map +0 -1
- package/dist/types/src/registry/function-registry.test.d.ts.map +0 -1
- package/dist/types/src/registry/index.d.ts.map +0 -1
- package/src/registry/function-registry.ts +0 -84
- /package/dist/types/src/{registry → function}/function-registry.test.d.ts +0 -0
- /package/dist/types/src/{registry → function}/index.d.ts +0 -0
- /package/src/{registry → function}/index.ts +0 -0
|
@@ -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,32 +62,31 @@ 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
|
-
|
|
66
|
-
|
|
67
|
-
} | undefined;
|
|
65
|
+
enabled?: boolean | undefined;
|
|
66
|
+
meta?: object | undefined;
|
|
68
67
|
spec: {
|
|
69
|
-
|
|
68
|
+
filter: readonly {
|
|
70
69
|
readonly type: string;
|
|
71
70
|
readonly props?: {
|
|
72
71
|
readonly [x: string]: any;
|
|
73
72
|
} | undefined;
|
|
74
73
|
}[];
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
type: "subscription";
|
|
75
|
+
options?: {
|
|
77
76
|
readonly deep?: boolean | undefined;
|
|
78
77
|
readonly delay?: number | undefined;
|
|
79
78
|
} | undefined;
|
|
80
79
|
} | {
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
type: "timer";
|
|
81
|
+
cron: string;
|
|
83
82
|
} | {
|
|
84
83
|
port?: number | undefined;
|
|
85
84
|
type: "webhook";
|
|
86
85
|
method: string;
|
|
87
86
|
} | {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
type: "websocket";
|
|
88
|
+
url: string;
|
|
89
|
+
init?: {
|
|
91
90
|
readonly [x: string]: any;
|
|
92
91
|
} | undefined;
|
|
93
92
|
};
|
|
@@ -100,89 +99,108 @@ export declare class FunctionTrigger extends FunctionTrigger_base {
|
|
|
100
99
|
* Function manifest file.
|
|
101
100
|
*/
|
|
102
101
|
export declare const FunctionManifestSchema: S.struct<{
|
|
103
|
-
functions: S.PropertySignature<"?:",
|
|
102
|
+
functions: S.PropertySignature<"?:", ({
|
|
104
103
|
handler: string;
|
|
105
104
|
uri: string;
|
|
106
105
|
description?: string | undefined;
|
|
107
106
|
route: string;
|
|
108
107
|
} & {
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
"@meta"?: {
|
|
109
|
+
readonly keys: {
|
|
110
|
+
readonly id: string;
|
|
111
|
+
readonly source: string;
|
|
112
|
+
}[];
|
|
113
|
+
} | undefined;
|
|
114
|
+
})[] | undefined, never, "?:", ({
|
|
111
115
|
handler: string;
|
|
112
116
|
uri: string;
|
|
113
117
|
description?: string | undefined;
|
|
114
118
|
route: string;
|
|
115
119
|
} & {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
readonly [x: string]: any;
|
|
120
|
+
"@meta"?: {
|
|
121
|
+
readonly keys: {
|
|
122
|
+
readonly id: string;
|
|
123
|
+
readonly source: string;
|
|
124
|
+
}[];
|
|
122
125
|
} | undefined;
|
|
126
|
+
})[] | undefined, never>;
|
|
127
|
+
triggers: S.PropertySignature<"?:", ({
|
|
128
|
+
function: string;
|
|
129
|
+
enabled?: boolean | undefined;
|
|
130
|
+
meta?: object | undefined;
|
|
123
131
|
spec: {
|
|
124
|
-
|
|
132
|
+
filter: readonly {
|
|
125
133
|
readonly type: string;
|
|
126
134
|
readonly props?: {
|
|
127
135
|
readonly [x: string]: any;
|
|
128
136
|
} | undefined;
|
|
129
137
|
}[];
|
|
130
|
-
|
|
131
|
-
|
|
138
|
+
type: "subscription";
|
|
139
|
+
options?: {
|
|
132
140
|
readonly deep?: boolean | undefined;
|
|
133
141
|
readonly delay?: number | undefined;
|
|
134
142
|
} | undefined;
|
|
135
143
|
} | {
|
|
136
|
-
|
|
137
|
-
|
|
144
|
+
type: "timer";
|
|
145
|
+
cron: string;
|
|
138
146
|
} | {
|
|
139
147
|
port?: number | undefined;
|
|
140
148
|
type: "webhook";
|
|
141
149
|
method: string;
|
|
142
150
|
} | {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
151
|
+
type: "websocket";
|
|
152
|
+
url: string;
|
|
153
|
+
init?: {
|
|
146
154
|
readonly [x: string]: any;
|
|
147
155
|
} | undefined;
|
|
148
156
|
};
|
|
149
157
|
} & {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
158
|
+
"@meta"?: {
|
|
159
|
+
readonly keys: {
|
|
160
|
+
readonly id: string;
|
|
161
|
+
readonly source: string;
|
|
162
|
+
}[];
|
|
155
163
|
} | undefined;
|
|
164
|
+
})[] | undefined, never, "?:", ({
|
|
165
|
+
function: string;
|
|
166
|
+
enabled?: boolean | undefined;
|
|
167
|
+
meta?: object | undefined;
|
|
156
168
|
spec: {
|
|
157
|
-
|
|
169
|
+
filter: readonly {
|
|
158
170
|
readonly type: string;
|
|
159
171
|
readonly props?: {
|
|
160
172
|
readonly [x: string]: any;
|
|
161
173
|
} | undefined;
|
|
162
174
|
}[];
|
|
163
|
-
|
|
164
|
-
|
|
175
|
+
type: "subscription";
|
|
176
|
+
options?: {
|
|
165
177
|
readonly deep?: boolean | undefined;
|
|
166
178
|
readonly delay?: number | undefined;
|
|
167
179
|
} | undefined;
|
|
168
180
|
} | {
|
|
169
|
-
|
|
170
|
-
|
|
181
|
+
type: "timer";
|
|
182
|
+
cron: string;
|
|
171
183
|
} | {
|
|
172
184
|
port?: number | undefined;
|
|
173
185
|
type: "webhook";
|
|
174
186
|
method: string;
|
|
175
187
|
} | {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
188
|
+
type: "websocket";
|
|
189
|
+
url: string;
|
|
190
|
+
init?: {
|
|
179
191
|
readonly [x: string]: any;
|
|
180
192
|
} | undefined;
|
|
181
193
|
};
|
|
182
194
|
} & {
|
|
183
|
-
|
|
184
|
-
|
|
195
|
+
"@meta"?: {
|
|
196
|
+
readonly keys: {
|
|
197
|
+
readonly id: string;
|
|
198
|
+
readonly source: string;
|
|
199
|
+
}[];
|
|
200
|
+
} | undefined;
|
|
201
|
+
})[] | undefined, never>;
|
|
185
202
|
}>;
|
|
186
203
|
export type FunctionManifest = S.Schema.Type<typeof FunctionManifestSchema>;
|
|
204
|
+
export declare const FUNCTION_SCHEMA: (typeof FunctionDef | typeof FunctionTrigger)[];
|
|
187
205
|
export {};
|
|
188
206
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAIA,OAAO,
|
|
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,gBAS/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.d28cf09",
|
|
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,29 +40,28 @@
|
|
|
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/
|
|
32
|
-
"@dxos/
|
|
33
|
-
"@dxos/echo-
|
|
34
|
-
"@dxos/echo-
|
|
35
|
-
"@dxos/
|
|
36
|
-
"@dxos/
|
|
37
|
-
"@dxos/log": "0.5.3-main.
|
|
38
|
-
"@dxos/node-std": "0.5.3-main.
|
|
39
|
-
"@dxos/
|
|
40
|
-
"@dxos/
|
|
43
|
+
"@braneframe/types": "0.5.3-main.d28cf09",
|
|
44
|
+
"@dxos/async": "0.5.3-main.d28cf09",
|
|
45
|
+
"@dxos/context": "0.5.3-main.d28cf09",
|
|
46
|
+
"@dxos/client": "0.5.3-main.d28cf09",
|
|
47
|
+
"@dxos/echo-schema": "0.5.3-main.d28cf09",
|
|
48
|
+
"@dxos/echo-db": "0.5.3-main.d28cf09",
|
|
49
|
+
"@dxos/keys": "0.5.3-main.d28cf09",
|
|
50
|
+
"@dxos/invariant": "0.5.3-main.d28cf09",
|
|
51
|
+
"@dxos/log": "0.5.3-main.d28cf09",
|
|
52
|
+
"@dxos/node-std": "0.5.3-main.d28cf09",
|
|
53
|
+
"@dxos/util": "0.5.3-main.d28cf09",
|
|
54
|
+
"@dxos/protocols": "0.5.3-main.d28cf09"
|
|
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.d28cf09"
|
|
46
60
|
},
|
|
47
61
|
"publishConfig": {
|
|
48
62
|
"access": "public"
|
|
49
63
|
},
|
|
50
64
|
"scripts": {
|
|
51
|
-
"gen-schema": "ts-node ./tools/schema.ts"
|
|
52
|
-
"prebuild": "pnpm gen-schema"
|
|
65
|
+
"gen-schema": "ts-node ./tools/schema.ts"
|
|
53
66
|
}
|
|
54
67
|
}
|
package/schema/functions.json
CHANGED
|
@@ -48,17 +48,11 @@
|
|
|
48
48
|
"properties": {
|
|
49
49
|
"function": {
|
|
50
50
|
"type": "string",
|
|
51
|
-
"description": "Function
|
|
51
|
+
"description": "Function URI.",
|
|
52
52
|
"title": "string"
|
|
53
53
|
},
|
|
54
54
|
"meta": {
|
|
55
|
-
"
|
|
56
|
-
"required": [],
|
|
57
|
-
"properties": {},
|
|
58
|
-
"additionalProperties": {
|
|
59
|
-
"$id": "/schemas/any",
|
|
60
|
-
"title": "any"
|
|
61
|
-
}
|
|
55
|
+
"$ref": "#/$defs/object"
|
|
62
56
|
},
|
|
63
57
|
"spec": {
|
|
64
58
|
"anyOf": [
|
|
@@ -193,5 +187,20 @@
|
|
|
193
187
|
}
|
|
194
188
|
}
|
|
195
189
|
},
|
|
196
|
-
"additionalProperties": false
|
|
190
|
+
"additionalProperties": false,
|
|
191
|
+
"$defs": {
|
|
192
|
+
"object": {
|
|
193
|
+
"$id": "/schemas/object",
|
|
194
|
+
"oneOf": [
|
|
195
|
+
{
|
|
196
|
+
"type": "object"
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"type": "array"
|
|
200
|
+
}
|
|
201
|
+
],
|
|
202
|
+
"description": "an object in the TypeScript meaning, i.e. the `object` type",
|
|
203
|
+
"title": "object"
|
|
204
|
+
}
|
|
205
|
+
}
|
|
197
206
|
}
|
|
@@ -44,7 +44,7 @@ describe('function registry', () => {
|
|
|
44
44
|
const client = (await createInitializedClients(testBuilder))[0];
|
|
45
45
|
const registry = createRegistry(client);
|
|
46
46
|
const space = await client.spaces.create();
|
|
47
|
-
await registry.register(space, testManifest);
|
|
47
|
+
await registry.register(space, testManifest.functions);
|
|
48
48
|
const { objects: definitions } = await space.db.query(Filter.schema(FunctionDef)).run();
|
|
49
49
|
expect(definitions.length).to.eq(1);
|
|
50
50
|
expect(definitions[0].uri).to.eq(testManifest.functions?.[0]?.uri);
|
|
@@ -54,8 +54,8 @@ describe('function registry', () => {
|
|
|
54
54
|
const client = (await createInitializedClients(testBuilder))[0];
|
|
55
55
|
const registry = createRegistry(client);
|
|
56
56
|
const space = await client.spaces.create();
|
|
57
|
-
const existing = space.db.add(create(FunctionDef,
|
|
58
|
-
await registry.register(space, testManifest);
|
|
57
|
+
const existing = space.db.add(create(FunctionDef, testManifest.functions![0]));
|
|
58
|
+
await registry.register(space, testManifest.functions);
|
|
59
59
|
const { objects: definitions } = await space.db.query(Filter.schema(FunctionDef)).run();
|
|
60
60
|
expect(definitions.length).to.eq(1);
|
|
61
61
|
expect(definitions[0].uri).to.eq(existing.uri);
|
|
@@ -67,12 +67,12 @@ describe('function registry', () => {
|
|
|
67
67
|
const client = (await createInitializedClients(testBuilder))[0];
|
|
68
68
|
const registry = createRegistry(client);
|
|
69
69
|
const space = await client.spaces.create();
|
|
70
|
-
const definitions = range(3, () => create(FunctionDef,
|
|
70
|
+
const definitions = range(3, () => create(FunctionDef, testManifest.functions![0]));
|
|
71
71
|
definitions.forEach((def) => space.db.add(def));
|
|
72
72
|
|
|
73
73
|
const functionRegistered = new Trigger<FunctionDef[]>();
|
|
74
|
-
registry.
|
|
75
|
-
functionRegistered.wake(fn.
|
|
74
|
+
registry.registered.on((fn) => {
|
|
75
|
+
functionRegistered.wake(fn.added);
|
|
76
76
|
});
|
|
77
77
|
void registry.open(ctx);
|
|
78
78
|
const functions = await functionRegistered.wait();
|
|
@@ -86,12 +86,12 @@ describe('function registry', () => {
|
|
|
86
86
|
const space = await client.spaces.create();
|
|
87
87
|
|
|
88
88
|
const functionRegistered = new Trigger<FunctionDef>();
|
|
89
|
-
registry.
|
|
90
|
-
expect(fn.
|
|
91
|
-
functionRegistered.wake(fn.
|
|
89
|
+
registry.registered.on((fn) => {
|
|
90
|
+
expect(fn.added.length).to.eq(1);
|
|
91
|
+
functionRegistered.wake(fn.added[0]);
|
|
92
92
|
});
|
|
93
93
|
await registry.open(ctx);
|
|
94
|
-
await registry.register(space, testManifest);
|
|
94
|
+
await registry.register(space, testManifest.functions);
|
|
95
95
|
const registered = await functionRegistered.wait();
|
|
96
96
|
expect(registered.uri).to.eq(testManifest.functions![0].uri);
|
|
97
97
|
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Event } from '@dxos/async';
|
|
6
|
+
import { type Client } from '@dxos/client';
|
|
7
|
+
import { create, Filter, type Space } from '@dxos/client/echo';
|
|
8
|
+
import { type Context, Resource } from '@dxos/context';
|
|
9
|
+
import { PublicKey } from '@dxos/keys';
|
|
10
|
+
import { log } from '@dxos/log';
|
|
11
|
+
import { ComplexMap, diff } from '@dxos/util';
|
|
12
|
+
|
|
13
|
+
import { FunctionDef, type FunctionManifest } from '../types';
|
|
14
|
+
|
|
15
|
+
export type FunctionsRegisteredEvent = {
|
|
16
|
+
space: Space;
|
|
17
|
+
added: FunctionDef[];
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export class FunctionRegistry extends Resource {
|
|
21
|
+
private readonly _functionBySpaceKey = new ComplexMap<PublicKey, FunctionDef[]>(PublicKey.hash);
|
|
22
|
+
|
|
23
|
+
public readonly registered = new Event<FunctionsRegisteredEvent>();
|
|
24
|
+
|
|
25
|
+
constructor(private readonly _client: Client) {
|
|
26
|
+
super();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public getFunctions(space: Space): FunctionDef[] {
|
|
30
|
+
return this._functionBySpaceKey.get(space.key) ?? [];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Loads function definitions from the manifest into the space.
|
|
35
|
+
* We first load all the definitions from the space to deduplicate by functionId.
|
|
36
|
+
*/
|
|
37
|
+
public async register(space: Space, functions: FunctionManifest['functions']): Promise<void> {
|
|
38
|
+
log('register', { space: space.key, functions: functions?.length ?? 0 });
|
|
39
|
+
if (!functions?.length) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (!space.db.graph.runtimeSchemaRegistry.hasSchema(FunctionDef)) {
|
|
43
|
+
space.db.graph.runtimeSchemaRegistry.registerSchema(FunctionDef);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Sync definitions.
|
|
47
|
+
const { objects: existing } = await space.db.query(Filter.schema(FunctionDef)).run();
|
|
48
|
+
const { added } = diff(existing, functions, (a, b) => a.uri === b.uri);
|
|
49
|
+
// TODO(burdon): Update existing templates.
|
|
50
|
+
added.forEach((def) => space.db.add(create(FunctionDef, def)));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
protected override async _open(): Promise<void> {
|
|
54
|
+
log.info('opening...');
|
|
55
|
+
const spacesSubscription = this._client.spaces.subscribe(async (spaces) => {
|
|
56
|
+
for (const space of spaces) {
|
|
57
|
+
if (this._functionBySpaceKey.has(space.key)) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const registered: FunctionDef[] = [];
|
|
62
|
+
this._functionBySpaceKey.set(space.key, registered);
|
|
63
|
+
await space.waitUntilReady();
|
|
64
|
+
if (this._ctx.disposed) {
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Subscribe to updates.
|
|
69
|
+
this._ctx.onDispose(
|
|
70
|
+
space.db.query(Filter.schema(FunctionDef)).subscribe(({ objects }) => {
|
|
71
|
+
const { added } = diff(registered, objects, (a, b) => a.uri === b.uri);
|
|
72
|
+
// TODO(burdon): Update and remove.
|
|
73
|
+
if (added.length > 0) {
|
|
74
|
+
registered.push(...added);
|
|
75
|
+
this.registered.emit({ space, added });
|
|
76
|
+
}
|
|
77
|
+
}),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// TODO(burdon): API: Normalize unsubscribe methods.
|
|
83
|
+
this._ctx.onDispose(() => spacesSubscription.unsubscribe());
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
protected override async _close(_: Context): Promise<void> {
|
|
87
|
+
log.info('closing...');
|
|
88
|
+
this._functionBySpaceKey.clear();
|
|
89
|
+
}
|
|
90
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { TestBuilder } from '@dxos/client/testing';
|
|
|
11
11
|
import { describe, test } from '@dxos/test';
|
|
12
12
|
|
|
13
13
|
import { DevServer } from './dev-server';
|
|
14
|
-
import { FunctionRegistry } from '../
|
|
14
|
+
import { FunctionRegistry } from '../function';
|
|
15
15
|
import { createFunctionRuntime } from '../testing';
|
|
16
16
|
import { type FunctionManifest } from '../types';
|
|
17
17
|
|
|
@@ -44,7 +44,7 @@ describe('dev server', () => {
|
|
|
44
44
|
baseDir: path.join(__dirname, '../testing'),
|
|
45
45
|
});
|
|
46
46
|
const space = await client.spaces.create();
|
|
47
|
-
await registry.register(space, manifest);
|
|
47
|
+
await registry.register(space, manifest.functions);
|
|
48
48
|
await server.start();
|
|
49
49
|
|
|
50
50
|
// TODO(burdon): Doesn't shut down cleanly.
|
|
@@ -13,8 +13,8 @@ import { Context } from '@dxos/context';
|
|
|
13
13
|
import { invariant } from '@dxos/invariant';
|
|
14
14
|
import { log } from '@dxos/log';
|
|
15
15
|
|
|
16
|
+
import { type FunctionRegistry } from '../function';
|
|
16
17
|
import { type FunctionContext, type FunctionEvent, type FunctionHandler, type FunctionResponse } from '../handler';
|
|
17
|
-
import { type FunctionRegistry } from '../registry';
|
|
18
18
|
import { type FunctionDef } from '../types';
|
|
19
19
|
|
|
20
20
|
export type DevServerOptions = {
|
|
@@ -41,16 +41,15 @@ export class DevServer {
|
|
|
41
41
|
|
|
42
42
|
public readonly update = new Event<number>();
|
|
43
43
|
|
|
44
|
-
// prettier-ignore
|
|
45
44
|
constructor(
|
|
46
45
|
private readonly _client: Client,
|
|
47
46
|
private readonly _functionsRegistry: FunctionRegistry,
|
|
48
47
|
private readonly _options: DevServerOptions,
|
|
49
48
|
) {
|
|
50
|
-
this._functionsRegistry.
|
|
51
|
-
|
|
49
|
+
this._functionsRegistry.registered.on(async ({ added }) => {
|
|
50
|
+
added.forEach((def) => this._load(def));
|
|
52
51
|
await this._safeUpdateRegistration();
|
|
53
|
-
log('new functions loaded', {
|
|
52
|
+
log('new functions loaded', { added });
|
|
54
53
|
});
|
|
55
54
|
}
|
|
56
55
|
|
|
@@ -158,7 +157,7 @@ export class DevServer {
|
|
|
158
157
|
/**
|
|
159
158
|
* Load function.
|
|
160
159
|
*/
|
|
161
|
-
private async _load(def: FunctionDef, force
|
|
160
|
+
private async _load(def: FunctionDef, force?: boolean | undefined) {
|
|
162
161
|
const { uri, route, handler } = def;
|
|
163
162
|
const filePath = join(this._options.baseDir, handler);
|
|
164
163
|
log.info('loading', { uri, force });
|
|
@@ -12,7 +12,7 @@ import { create } from '@dxos/echo-schema';
|
|
|
12
12
|
import { describe, test } from '@dxos/test';
|
|
13
13
|
|
|
14
14
|
import { Scheduler, type SchedulerOptions } from './scheduler';
|
|
15
|
-
import { FunctionRegistry } from '../
|
|
15
|
+
import { FunctionRegistry } from '../function';
|
|
16
16
|
import { createInitializedClients, TestType, triggerWebhook } from '../testing';
|
|
17
17
|
import { TriggerRegistry } from '../trigger';
|
|
18
18
|
import { type FunctionManifest } from '../types';
|
package/src/runtime/scheduler.ts
CHANGED
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
|
|
7
|
+
import { Mutex } from '@dxos/async';
|
|
7
8
|
import { type Space } from '@dxos/client/echo';
|
|
8
9
|
import { Context } from '@dxos/context';
|
|
9
10
|
import { log } from '@dxos/log';
|
|
10
11
|
|
|
12
|
+
import { type FunctionRegistry } from '../function';
|
|
11
13
|
import { type FunctionEventMeta } from '../handler';
|
|
12
|
-
import { type FunctionRegistry } from '../registry';
|
|
13
14
|
import { type TriggerRegistry } from '../trigger';
|
|
14
15
|
import { type FunctionDef, type FunctionManifest, type FunctionTrigger } from '../types';
|
|
15
16
|
|
|
@@ -26,13 +27,15 @@ export type SchedulerOptions = {
|
|
|
26
27
|
export class Scheduler {
|
|
27
28
|
private _ctx = createContext();
|
|
28
29
|
|
|
30
|
+
private readonly _functionUriToCallMutex = new Map<string, Mutex>();
|
|
31
|
+
|
|
29
32
|
constructor(
|
|
30
33
|
public readonly functions: FunctionRegistry,
|
|
31
34
|
public readonly triggers: TriggerRegistry,
|
|
32
35
|
private readonly _options: SchedulerOptions = {},
|
|
33
36
|
) {
|
|
34
|
-
this.functions.
|
|
35
|
-
await this._safeActivateTriggers(space, this.triggers.getInactiveTriggers(space),
|
|
37
|
+
this.functions.registered.on(async ({ space, added }) => {
|
|
38
|
+
await this._safeActivateTriggers(space, this.triggers.getInactiveTriggers(space), added);
|
|
36
39
|
});
|
|
37
40
|
this.triggers.registered.on(async ({ space, triggers }) => {
|
|
38
41
|
await this._safeActivateTriggers(space, triggers, this.functions.getFunctions(space));
|
|
@@ -52,8 +55,9 @@ export class Scheduler {
|
|
|
52
55
|
await this.triggers.close();
|
|
53
56
|
}
|
|
54
57
|
|
|
58
|
+
// TODO(burdon): Remove and update registries directly.
|
|
55
59
|
public async register(space: Space, manifest: FunctionManifest) {
|
|
56
|
-
await this.functions.register(space, manifest);
|
|
60
|
+
await this.functions.register(space, manifest.functions);
|
|
57
61
|
await this.triggers.register(space, manifest);
|
|
58
62
|
}
|
|
59
63
|
|
|
@@ -76,16 +80,25 @@ export class Scheduler {
|
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
await this.triggers.activate({ space }, fnTrigger, async (args) => {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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, fnTrigger, {
|
|
90
|
+
meta: fnTrigger.meta,
|
|
91
|
+
data: { ...args, spaceKey: space.key },
|
|
92
|
+
});
|
|
82
93
|
});
|
|
83
94
|
});
|
|
95
|
+
|
|
84
96
|
log('activated trigger', { space: space.key, trigger: fnTrigger });
|
|
85
97
|
}
|
|
86
98
|
|
|
87
99
|
private async _execFunction<TData, TMeta>(
|
|
88
100
|
def: FunctionDef,
|
|
101
|
+
trigger: FunctionTrigger,
|
|
89
102
|
{ data, meta }: { data: TData; meta?: TMeta },
|
|
90
103
|
): Promise<number> {
|
|
91
104
|
let status = 0;
|
|
@@ -97,7 +110,7 @@ export class Scheduler {
|
|
|
97
110
|
if (endpoint) {
|
|
98
111
|
// TODO(burdon): Move out of scheduler (generalize as callback).
|
|
99
112
|
const url = path.join(endpoint, def.route);
|
|
100
|
-
log.info('exec', { function: def.uri, url });
|
|
113
|
+
log.info('exec', { function: def.uri, url, triggerType: trigger.spec.type });
|
|
101
114
|
const response = await fetch(url, {
|
|
102
115
|
method: 'POST',
|
|
103
116
|
headers: {
|