@dxos/functions 0.5.3-main.37bbd91 → 0.5.3-main.3b535c7
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 +471 -825
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +461 -805
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/handler.d.ts +12 -33
- package/dist/types/src/handler.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +0 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/runtime/dev-server.d.ts +13 -16
- package/dist/types/src/runtime/dev-server.d.ts.map +1 -1
- package/dist/types/src/runtime/scheduler.d.ts +27 -13
- package/dist/types/src/runtime/scheduler.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +101 -143
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +15 -33
- package/schema/functions.json +104 -140
- package/src/handler.ts +31 -54
- package/src/index.ts +0 -2
- package/src/runtime/dev-server.ts +53 -104
- package/src/runtime/scheduler.test.ts +73 -56
- package/src/runtime/scheduler.ts +271 -87
- package/src/types.ts +32 -59
- package/dist/lib/browser/chunk-366QG6IX.mjs +0 -81
- package/dist/lib/browser/chunk-366QG6IX.mjs.map +0 -7
- package/dist/lib/browser/types.mjs +0 -12
- package/dist/lib/browser/types.mjs.map +0 -7
- package/dist/lib/node/chunk-3VSJ57ZZ.cjs +0 -97
- package/dist/lib/node/chunk-3VSJ57ZZ.cjs.map +0 -7
- package/dist/lib/node/types.cjs +0 -33
- package/dist/lib/node/types.cjs.map +0 -7
- package/dist/types/src/function/function-registry.d.ts +0 -24
- package/dist/types/src/function/function-registry.d.ts.map +0 -1
- package/dist/types/src/function/function-registry.test.d.ts +0 -2
- package/dist/types/src/function/function-registry.test.d.ts.map +0 -1
- package/dist/types/src/function/index.d.ts +0 -2
- package/dist/types/src/function/index.d.ts.map +0 -1
- package/dist/types/src/runtime/dev-server.test.d.ts +0 -2
- package/dist/types/src/runtime/dev-server.test.d.ts.map +0 -1
- package/dist/types/src/testing/functions-integration.test.d.ts +0 -2
- package/dist/types/src/testing/functions-integration.test.d.ts.map +0 -1
- package/dist/types/src/testing/index.d.ts +0 -4
- package/dist/types/src/testing/index.d.ts.map +0 -1
- package/dist/types/src/testing/setup.d.ts +0 -5
- package/dist/types/src/testing/setup.d.ts.map +0 -1
- package/dist/types/src/testing/test/handler.d.ts +0 -4
- package/dist/types/src/testing/test/handler.d.ts.map +0 -1
- package/dist/types/src/testing/test/index.d.ts +0 -3
- package/dist/types/src/testing/test/index.d.ts.map +0 -1
- package/dist/types/src/testing/types.d.ts +0 -9
- package/dist/types/src/testing/types.d.ts.map +0 -1
- package/dist/types/src/testing/util.d.ts +0 -3
- package/dist/types/src/testing/util.d.ts.map +0 -1
- package/dist/types/src/trigger/index.d.ts +0 -2
- package/dist/types/src/trigger/index.d.ts.map +0 -1
- package/dist/types/src/trigger/trigger-registry.d.ts +0 -40
- package/dist/types/src/trigger/trigger-registry.d.ts.map +0 -1
- package/dist/types/src/trigger/trigger-registry.test.d.ts +0 -2
- package/dist/types/src/trigger/trigger-registry.test.d.ts.map +0 -1
- package/dist/types/src/trigger/type/index.d.ts +0 -5
- package/dist/types/src/trigger/type/index.d.ts.map +0 -1
- package/dist/types/src/trigger/type/subscription-trigger.d.ts +0 -4
- package/dist/types/src/trigger/type/subscription-trigger.d.ts.map +0 -1
- package/dist/types/src/trigger/type/timer-trigger.d.ts +0 -4
- package/dist/types/src/trigger/type/timer-trigger.d.ts.map +0 -1
- package/dist/types/src/trigger/type/webhook-trigger.d.ts +0 -4
- package/dist/types/src/trigger/type/webhook-trigger.d.ts.map +0 -1
- package/dist/types/src/trigger/type/websocket-trigger.d.ts +0 -13
- package/dist/types/src/trigger/type/websocket-trigger.d.ts.map +0 -1
- 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/function/function-registry.test.ts +0 -105
- package/src/function/function-registry.ts +0 -90
- package/src/function/index.ts +0 -5
- package/src/runtime/dev-server.test.ts +0 -60
- package/src/testing/functions-integration.test.ts +0 -99
- package/src/testing/index.ts +0 -7
- package/src/testing/setup.ts +0 -45
- package/src/testing/test/handler.ts +0 -15
- package/src/testing/test/index.ts +0 -7
- package/src/testing/types.ts +0 -9
- package/src/testing/util.ts +0 -16
- package/src/trigger/index.ts +0 -5
- package/src/trigger/trigger-registry.test.ts +0 -255
- package/src/trigger/trigger-registry.ts +0 -189
- package/src/trigger/type/index.ts +0 -8
- package/src/trigger/type/subscription-trigger.ts +0 -80
- package/src/trigger/type/timer-trigger.ts +0 -44
- package/src/trigger/type/webhook-trigger.ts +0 -47
- package/src/trigger/type/websocket-trigger.ts +0 -91
- package/src/util.test.ts +0 -43
- package/src/util.ts +0 -48
package/schema/functions.json
CHANGED
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"type": "object",
|
|
4
|
-
"required": [
|
|
4
|
+
"required": [
|
|
5
|
+
"functions",
|
|
6
|
+
"triggers"
|
|
7
|
+
],
|
|
5
8
|
"properties": {
|
|
6
9
|
"functions": {
|
|
7
10
|
"type": "array",
|
|
8
11
|
"items": {
|
|
9
12
|
"type": "object",
|
|
10
13
|
"required": [
|
|
11
|
-
"
|
|
12
|
-
"
|
|
14
|
+
"id",
|
|
15
|
+
"name",
|
|
13
16
|
"handler"
|
|
14
17
|
],
|
|
15
18
|
"properties": {
|
|
16
|
-
"
|
|
19
|
+
"id": {
|
|
17
20
|
"type": "string",
|
|
18
21
|
"description": "a string",
|
|
19
22
|
"title": "string"
|
|
20
23
|
},
|
|
21
|
-
"
|
|
24
|
+
"name": {
|
|
22
25
|
"type": "string",
|
|
23
26
|
"description": "a string",
|
|
24
27
|
"title": "string"
|
|
@@ -42,165 +45,126 @@
|
|
|
42
45
|
"items": {
|
|
43
46
|
"type": "object",
|
|
44
47
|
"required": [
|
|
45
|
-
"function"
|
|
46
|
-
"spec"
|
|
48
|
+
"function"
|
|
47
49
|
],
|
|
48
50
|
"properties": {
|
|
49
51
|
"function": {
|
|
50
52
|
"type": "string",
|
|
51
|
-
"description": "Function URI.",
|
|
53
|
+
"description": "Function ID/URI.",
|
|
52
54
|
"title": "string"
|
|
53
55
|
},
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
+
"timer": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"required": [
|
|
59
|
+
"cron"
|
|
60
|
+
],
|
|
61
|
+
"properties": {
|
|
62
|
+
"cron": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"description": "a string",
|
|
65
|
+
"title": "string"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"additionalProperties": false
|
|
69
|
+
},
|
|
70
|
+
"webhook": {
|
|
71
|
+
"type": "object",
|
|
72
|
+
"required": [
|
|
73
|
+
"port"
|
|
74
|
+
],
|
|
75
|
+
"properties": {
|
|
76
|
+
"port": {
|
|
77
|
+
"type": "number",
|
|
78
|
+
"description": "a number",
|
|
79
|
+
"title": "number"
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"additionalProperties": false
|
|
56
83
|
},
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
|
|
84
|
+
"websocket": {
|
|
85
|
+
"type": "object",
|
|
86
|
+
"required": [
|
|
87
|
+
"url"
|
|
88
|
+
],
|
|
89
|
+
"properties": {
|
|
90
|
+
"url": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"description": "a string",
|
|
93
|
+
"title": "string"
|
|
94
|
+
},
|
|
95
|
+
"init": {
|
|
60
96
|
"type": "object",
|
|
61
|
-
"required": [
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
97
|
+
"required": [],
|
|
98
|
+
"properties": {},
|
|
99
|
+
"additionalProperties": {
|
|
100
|
+
"$id": "/schemas/any",
|
|
101
|
+
"title": "any"
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"additionalProperties": false
|
|
106
|
+
},
|
|
107
|
+
"subscription": {
|
|
108
|
+
"type": "object",
|
|
109
|
+
"required": [
|
|
110
|
+
"filter"
|
|
111
|
+
],
|
|
112
|
+
"properties": {
|
|
113
|
+
"spaceKey": {
|
|
114
|
+
"type": "string",
|
|
115
|
+
"description": "a string",
|
|
116
|
+
"title": "string"
|
|
117
|
+
},
|
|
118
|
+
"filter": {
|
|
119
|
+
"type": "array",
|
|
120
|
+
"items": {
|
|
121
|
+
"type": "object",
|
|
122
|
+
"required": [
|
|
123
|
+
"type"
|
|
124
|
+
],
|
|
125
|
+
"properties": {
|
|
126
|
+
"type": {
|
|
127
|
+
"type": "string",
|
|
128
|
+
"description": "a string",
|
|
129
|
+
"title": "string"
|
|
130
|
+
},
|
|
131
|
+
"props": {
|
|
132
|
+
"type": "object",
|
|
133
|
+
"required": [],
|
|
134
|
+
"properties": {},
|
|
135
|
+
"additionalProperties": {
|
|
136
|
+
"$id": "/schemas/any",
|
|
137
|
+
"title": "any"
|
|
138
|
+
}
|
|
139
|
+
}
|
|
68
140
|
},
|
|
69
|
-
"
|
|
70
|
-
|
|
71
|
-
"description": "a string",
|
|
72
|
-
"title": "string"
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
|
-
"additionalProperties": false
|
|
141
|
+
"additionalProperties": false
|
|
142
|
+
}
|
|
76
143
|
},
|
|
77
|
-
{
|
|
144
|
+
"options": {
|
|
78
145
|
"type": "object",
|
|
79
|
-
"required": [
|
|
80
|
-
"type",
|
|
81
|
-
"method"
|
|
82
|
-
],
|
|
146
|
+
"required": [],
|
|
83
147
|
"properties": {
|
|
84
|
-
"
|
|
85
|
-
"
|
|
148
|
+
"deep": {
|
|
149
|
+
"type": "boolean",
|
|
150
|
+
"description": "a boolean",
|
|
151
|
+
"title": "boolean"
|
|
86
152
|
},
|
|
87
|
-
"
|
|
88
|
-
"type": "string",
|
|
89
|
-
"description": "a string",
|
|
90
|
-
"title": "string"
|
|
91
|
-
},
|
|
92
|
-
"port": {
|
|
153
|
+
"delay": {
|
|
93
154
|
"type": "number",
|
|
94
155
|
"description": "a number",
|
|
95
156
|
"title": "number"
|
|
96
157
|
}
|
|
97
158
|
},
|
|
98
159
|
"additionalProperties": false
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
"type": "object",
|
|
102
|
-
"required": [
|
|
103
|
-
"type",
|
|
104
|
-
"url"
|
|
105
|
-
],
|
|
106
|
-
"properties": {
|
|
107
|
-
"type": {
|
|
108
|
-
"const": "websocket"
|
|
109
|
-
},
|
|
110
|
-
"url": {
|
|
111
|
-
"type": "string",
|
|
112
|
-
"description": "a string",
|
|
113
|
-
"title": "string"
|
|
114
|
-
},
|
|
115
|
-
"init": {
|
|
116
|
-
"type": "object",
|
|
117
|
-
"required": [],
|
|
118
|
-
"properties": {},
|
|
119
|
-
"additionalProperties": {
|
|
120
|
-
"$id": "/schemas/any",
|
|
121
|
-
"title": "any"
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
},
|
|
125
|
-
"additionalProperties": false
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
"type": "object",
|
|
129
|
-
"required": [
|
|
130
|
-
"type",
|
|
131
|
-
"filter"
|
|
132
|
-
],
|
|
133
|
-
"properties": {
|
|
134
|
-
"type": {
|
|
135
|
-
"const": "subscription"
|
|
136
|
-
},
|
|
137
|
-
"filter": {
|
|
138
|
-
"type": "array",
|
|
139
|
-
"items": {
|
|
140
|
-
"type": "object",
|
|
141
|
-
"required": [
|
|
142
|
-
"type"
|
|
143
|
-
],
|
|
144
|
-
"properties": {
|
|
145
|
-
"type": {
|
|
146
|
-
"type": "string",
|
|
147
|
-
"description": "a string",
|
|
148
|
-
"title": "string"
|
|
149
|
-
},
|
|
150
|
-
"props": {
|
|
151
|
-
"type": "object",
|
|
152
|
-
"required": [],
|
|
153
|
-
"properties": {},
|
|
154
|
-
"additionalProperties": {
|
|
155
|
-
"$id": "/schemas/any",
|
|
156
|
-
"title": "any"
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
},
|
|
160
|
-
"additionalProperties": false
|
|
161
|
-
}
|
|
162
|
-
},
|
|
163
|
-
"options": {
|
|
164
|
-
"type": "object",
|
|
165
|
-
"required": [],
|
|
166
|
-
"properties": {
|
|
167
|
-
"deep": {
|
|
168
|
-
"type": "boolean",
|
|
169
|
-
"description": "a boolean",
|
|
170
|
-
"title": "boolean"
|
|
171
|
-
},
|
|
172
|
-
"delay": {
|
|
173
|
-
"type": "number",
|
|
174
|
-
"description": "a number",
|
|
175
|
-
"title": "number"
|
|
176
|
-
}
|
|
177
|
-
},
|
|
178
|
-
"additionalProperties": false
|
|
179
|
-
}
|
|
180
|
-
},
|
|
181
|
-
"additionalProperties": false
|
|
182
160
|
}
|
|
183
|
-
|
|
161
|
+
},
|
|
162
|
+
"additionalProperties": false
|
|
184
163
|
}
|
|
185
164
|
},
|
|
186
165
|
"additionalProperties": false
|
|
187
166
|
}
|
|
188
167
|
}
|
|
189
168
|
},
|
|
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
|
-
}
|
|
169
|
+
"additionalProperties": false
|
|
206
170
|
}
|
package/src/handler.ts
CHANGED
|
@@ -8,61 +8,38 @@ import { type EchoReactiveObject } from '@dxos/echo-schema';
|
|
|
8
8
|
import { log } from '@dxos/log';
|
|
9
9
|
import { nonNullable } from '@dxos/util';
|
|
10
10
|
|
|
11
|
-
// TODO(burdon):
|
|
12
|
-
//
|
|
13
|
-
// https://www.serverless.com/framework/docs/providers/aws/guide/serverless.yml/#functions
|
|
11
|
+
// TODO(burdon): Context?
|
|
12
|
+
// Lambda-like function definitions.
|
|
13
|
+
// See: https://www.serverless.com/framework/docs/providers/aws/guide/serverless.yml/#functions
|
|
14
14
|
// https://www.npmjs.com/package/aws-lambda
|
|
15
|
+
// https://docs.aws.amazon.com/lambda/latest/dg/typescript-handler.html
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
context: FunctionContext;
|
|
21
|
-
event: FunctionEvent<TData, TMeta>;
|
|
22
|
-
response: FunctionResponse;
|
|
23
|
-
}) => Promise<FunctionResponse | void>;
|
|
17
|
+
// TODO(burdon): No response?
|
|
18
|
+
export interface Response {
|
|
19
|
+
status(code: number): Response;
|
|
20
|
+
}
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
* Function context.
|
|
27
|
-
*/
|
|
22
|
+
// TODO(burdon): Limit access to individual space?
|
|
28
23
|
export interface FunctionContext {
|
|
29
|
-
// TODO(burdon): Limit access to individual space.
|
|
30
24
|
client: Client;
|
|
31
|
-
// TODO(burdon): Replace with storage service abstraction.
|
|
32
25
|
dataDir?: string;
|
|
33
26
|
}
|
|
34
27
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Metadata from trigger.
|
|
44
|
-
*/
|
|
45
|
-
export type FunctionEventMeta<TMeta = {}> = {
|
|
46
|
-
meta: TMeta;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Function response.
|
|
51
|
-
*/
|
|
52
|
-
export interface FunctionResponse {
|
|
53
|
-
status(code: number): FunctionResponse;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
//
|
|
57
|
-
// Subscription utils.
|
|
58
|
-
//
|
|
28
|
+
// TODO(burdon): Model after http request. Ref Lambda/OpenFaaS.
|
|
29
|
+
// https://docs.aws.amazon.com/lambda/latest/dg/typescript-handler.html
|
|
30
|
+
export type FunctionHandler<T extends {}> = (params: {
|
|
31
|
+
event: T;
|
|
32
|
+
context: FunctionContext;
|
|
33
|
+
response: Response;
|
|
34
|
+
}) => Promise<Response | void>;
|
|
59
35
|
|
|
60
|
-
export type
|
|
61
|
-
|
|
36
|
+
export type FunctionSubscriptionEvent = {
|
|
37
|
+
space?: string; // TODO(burdon): Convert to PublicKey.
|
|
62
38
|
objects?: string[];
|
|
63
39
|
};
|
|
64
40
|
|
|
65
|
-
|
|
41
|
+
// TODO(burdon): ???
|
|
42
|
+
export type FunctionSubscriptionEvent2 = {
|
|
66
43
|
space?: Space;
|
|
67
44
|
objects?: EchoReactiveObject<any>[];
|
|
68
45
|
};
|
|
@@ -77,22 +54,22 @@ export type SubscriptionData = {
|
|
|
77
54
|
*
|
|
78
55
|
* NOTE: Get space key from devtools or `dx space list --json`
|
|
79
56
|
*/
|
|
80
|
-
export const subscriptionHandler =
|
|
81
|
-
handler: FunctionHandler<
|
|
82
|
-
): FunctionHandler<
|
|
83
|
-
return ({ event
|
|
57
|
+
export const subscriptionHandler = (
|
|
58
|
+
handler: FunctionHandler<FunctionSubscriptionEvent2>,
|
|
59
|
+
): FunctionHandler<FunctionSubscriptionEvent> => {
|
|
60
|
+
return ({ event, context, ...rest }) => {
|
|
84
61
|
const { client } = context;
|
|
85
|
-
const space =
|
|
86
|
-
const objects =
|
|
87
|
-
|
|
88
|
-
|
|
62
|
+
const space = event.space ? client.spaces.get(PublicKey.from(event.space)) : undefined;
|
|
63
|
+
const objects =
|
|
64
|
+
space &&
|
|
65
|
+
event.objects?.map<EchoReactiveObject<any> | undefined>((id) => space!.db.getObjectById(id)).filter(nonNullable);
|
|
89
66
|
|
|
90
|
-
if (!!
|
|
91
|
-
log.warn('invalid space', {
|
|
67
|
+
if (!!event.space && !space) {
|
|
68
|
+
log.warn('invalid space', { event });
|
|
92
69
|
} else {
|
|
93
70
|
log.info('handler', { space: space?.key.truncate(), objects: objects?.length });
|
|
94
71
|
}
|
|
95
72
|
|
|
96
|
-
return handler({ event: {
|
|
73
|
+
return handler({ event: { space, objects }, context, ...rest });
|
|
97
74
|
};
|
|
98
75
|
};
|
package/src/index.ts
CHANGED
|
@@ -7,19 +7,18 @@ import { getPort } from 'get-port-please';
|
|
|
7
7
|
import type http from 'http';
|
|
8
8
|
import { join } from 'node:path';
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { Trigger } from '@dxos/async';
|
|
11
11
|
import { type Client } from '@dxos/client';
|
|
12
|
-
import { Context } from '@dxos/context';
|
|
13
12
|
import { invariant } from '@dxos/invariant';
|
|
14
13
|
import { log } from '@dxos/log';
|
|
15
14
|
|
|
16
|
-
import { type
|
|
17
|
-
import { type
|
|
18
|
-
import { type FunctionDef } from '../types';
|
|
15
|
+
import { type FunctionContext, type FunctionHandler, type Response } from '../handler';
|
|
16
|
+
import { type FunctionDef, type FunctionManifest } from '../types';
|
|
19
17
|
|
|
20
18
|
export type DevServerOptions = {
|
|
21
|
-
baseDir: string;
|
|
22
19
|
port?: number;
|
|
20
|
+
directory: string;
|
|
21
|
+
manifest: FunctionManifest;
|
|
23
22
|
reload?: boolean;
|
|
24
23
|
dataDir?: string;
|
|
25
24
|
};
|
|
@@ -28,36 +27,20 @@ export type DevServerOptions = {
|
|
|
28
27
|
* Functions dev server provides a local HTTP server for testing functions.
|
|
29
28
|
*/
|
|
30
29
|
export class DevServer {
|
|
31
|
-
private _ctx = createContext();
|
|
32
|
-
|
|
33
30
|
// Function handlers indexed by name (URL path).
|
|
34
31
|
private readonly _handlers: Record<string, { def: FunctionDef; handler: FunctionHandler<any> }> = {};
|
|
35
32
|
|
|
36
33
|
private _server?: http.Server;
|
|
37
34
|
private _port?: number;
|
|
38
|
-
private
|
|
35
|
+
private _registrationId?: string;
|
|
39
36
|
private _proxy?: string;
|
|
40
37
|
private _seq = 0;
|
|
41
38
|
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
// prettier-ignore
|
|
44
40
|
constructor(
|
|
45
41
|
private readonly _client: Client,
|
|
46
|
-
private readonly _functionsRegistry: FunctionRegistry,
|
|
47
42
|
private readonly _options: DevServerOptions,
|
|
48
|
-
) {
|
|
49
|
-
this._functionsRegistry.registered.on(async ({ added }) => {
|
|
50
|
-
added.forEach((def) => this._load(def));
|
|
51
|
-
await this._safeUpdateRegistration();
|
|
52
|
-
log('new functions loaded', { added });
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
get stats() {
|
|
57
|
-
return {
|
|
58
|
-
seq: this._seq,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
43
|
+
) {}
|
|
61
44
|
|
|
62
45
|
get endpoint() {
|
|
63
46
|
invariant(this._port);
|
|
@@ -72,26 +55,30 @@ export class DevServer {
|
|
|
72
55
|
return Object.values(this._handlers);
|
|
73
56
|
}
|
|
74
57
|
|
|
75
|
-
async
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
58
|
+
async initialize() {
|
|
59
|
+
for (const def of this._options.manifest.functions) {
|
|
60
|
+
try {
|
|
61
|
+
await this._load(def);
|
|
62
|
+
} catch (err) {
|
|
63
|
+
log.error('parsing function (check manifest)', err);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
79
67
|
|
|
80
|
-
|
|
68
|
+
async start() {
|
|
81
69
|
const app = express();
|
|
82
70
|
app.use(express.json());
|
|
83
71
|
|
|
84
|
-
app.post('/:
|
|
85
|
-
const {
|
|
72
|
+
app.post('/:name', async (req, res) => {
|
|
73
|
+
const { name } = req.params;
|
|
86
74
|
try {
|
|
87
|
-
log.info('calling', {
|
|
75
|
+
log.info('calling', { name });
|
|
88
76
|
if (this._options.reload) {
|
|
89
|
-
const { def } = this._handlers[
|
|
77
|
+
const { def } = this._handlers[name];
|
|
90
78
|
await this._load(def, true);
|
|
91
79
|
}
|
|
92
80
|
|
|
93
|
-
|
|
94
|
-
res.statusCode = await this.invoke('/' + path, req.body);
|
|
81
|
+
res.statusCode = await this._invoke(name, req.body);
|
|
95
82
|
res.end();
|
|
96
83
|
} catch (err: any) {
|
|
97
84
|
log.catch(err);
|
|
@@ -107,110 +94,72 @@ export class DevServer {
|
|
|
107
94
|
// Register functions.
|
|
108
95
|
const { registrationId, endpoint } = await this._client.services.services.FunctionRegistryService!.register({
|
|
109
96
|
endpoint: this.endpoint,
|
|
97
|
+
functions: this.functions.map(({ def: { name } }) => ({ name })),
|
|
110
98
|
});
|
|
111
99
|
|
|
112
|
-
log.info('registered', { endpoint });
|
|
100
|
+
log.info('registered', { registrationId, endpoint });
|
|
101
|
+
this._registrationId = registrationId;
|
|
113
102
|
this._proxy = endpoint;
|
|
114
|
-
this._functionServiceRegistration = registrationId;
|
|
115
|
-
|
|
116
|
-
// Open after registration, so that it can be updated with the list of function definitions.
|
|
117
|
-
await this._functionsRegistry.open(this._ctx);
|
|
118
103
|
} catch (err: any) {
|
|
119
104
|
await this.stop();
|
|
120
105
|
throw new Error('FunctionRegistryService not available (check plugin is configured).');
|
|
121
106
|
}
|
|
122
|
-
|
|
123
|
-
log.info('started', { port: this._port });
|
|
124
107
|
}
|
|
125
108
|
|
|
126
109
|
async stop() {
|
|
127
|
-
invariant(this._server);
|
|
128
|
-
log.info('stopping...');
|
|
129
|
-
|
|
130
110
|
const trigger = new Trigger();
|
|
131
|
-
this._server
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
await this._client.services.services.FunctionRegistryService.unregister({
|
|
137
|
-
registrationId: this._functionServiceRegistration,
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
log.info('unregistered', { registrationId: this._functionServiceRegistration });
|
|
141
|
-
this._functionServiceRegistration = undefined;
|
|
142
|
-
this._proxy = undefined;
|
|
143
|
-
}
|
|
111
|
+
this._server?.close(async () => {
|
|
112
|
+
if (this._registrationId) {
|
|
113
|
+
await this._client.services.services.FunctionRegistryService!.unregister({
|
|
114
|
+
registrationId: this._registrationId,
|
|
115
|
+
});
|
|
144
116
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
117
|
+
log.info('unregistered', { registrationId: this._registrationId });
|
|
118
|
+
this._registrationId = undefined;
|
|
119
|
+
this._proxy = undefined;
|
|
148
120
|
}
|
|
121
|
+
|
|
122
|
+
trigger.wake();
|
|
149
123
|
});
|
|
150
124
|
|
|
151
125
|
await trigger.wait();
|
|
152
126
|
this._port = undefined;
|
|
153
127
|
this._server = undefined;
|
|
154
|
-
log.info('stopped');
|
|
155
128
|
}
|
|
156
129
|
|
|
157
130
|
/**
|
|
158
131
|
* Load function.
|
|
159
132
|
*/
|
|
160
|
-
private async _load(def: FunctionDef,
|
|
161
|
-
const {
|
|
162
|
-
const
|
|
163
|
-
log.info('loading', {
|
|
133
|
+
private async _load(def: FunctionDef, flush = false) {
|
|
134
|
+
const { id, name, handler } = def;
|
|
135
|
+
const path = join(this._options.directory, handler);
|
|
136
|
+
log.info('loading', { id });
|
|
164
137
|
|
|
165
138
|
// Remove from cache.
|
|
166
|
-
if (
|
|
139
|
+
if (flush) {
|
|
167
140
|
Object.keys(require.cache)
|
|
168
|
-
.filter((key) => key.startsWith(
|
|
169
|
-
.forEach((key) =>
|
|
170
|
-
delete require.cache[key];
|
|
171
|
-
});
|
|
141
|
+
.filter((key) => key.startsWith(path))
|
|
142
|
+
.forEach((key) => delete require.cache[key]);
|
|
172
143
|
}
|
|
173
144
|
|
|
174
|
-
// TODO(burdon): Import types.
|
|
175
145
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
176
|
-
const module = require(
|
|
146
|
+
const module = require(path);
|
|
177
147
|
if (typeof module.default !== 'function') {
|
|
178
|
-
throw new Error(`Handler must export default function: ${
|
|
148
|
+
throw new Error(`Handler must export default function: ${id}`);
|
|
179
149
|
}
|
|
180
150
|
|
|
181
|
-
this._handlers[
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
private async _safeUpdateRegistration(): Promise<void> {
|
|
185
|
-
invariant(this._functionServiceRegistration);
|
|
186
|
-
try {
|
|
187
|
-
await this._client.services.services.FunctionRegistryService!.updateRegistration({
|
|
188
|
-
registrationId: this._functionServiceRegistration,
|
|
189
|
-
functions: this.functions.map(({ def: { id, route } }) => ({ id, route })),
|
|
190
|
-
});
|
|
191
|
-
} catch (e) {
|
|
192
|
-
log.catch(e);
|
|
193
|
-
}
|
|
151
|
+
this._handlers[name] = { def, handler: module.default };
|
|
194
152
|
}
|
|
195
153
|
|
|
196
154
|
/**
|
|
197
|
-
* Invoke function.
|
|
155
|
+
* Invoke function handler.
|
|
198
156
|
*/
|
|
199
|
-
|
|
157
|
+
private async _invoke(name: string, event: any) {
|
|
200
158
|
const seq = ++this._seq;
|
|
201
159
|
const now = Date.now();
|
|
202
160
|
|
|
203
|
-
log.info('req', { seq,
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
log.info('res', { seq, path, statusCode, duration: Date.now() - now });
|
|
207
|
-
this.update.emit(statusCode);
|
|
208
|
-
return statusCode;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
private async _invoke(path: string, event: FunctionEvent) {
|
|
212
|
-
const { handler } = this._handlers[path] ?? {};
|
|
213
|
-
invariant(handler, `invalid path: ${path}`);
|
|
161
|
+
log.info('req', { seq, name });
|
|
162
|
+
const { handler } = this._handlers[name];
|
|
214
163
|
|
|
215
164
|
const context: FunctionContext = {
|
|
216
165
|
client: this._client,
|
|
@@ -218,7 +167,7 @@ export class DevServer {
|
|
|
218
167
|
};
|
|
219
168
|
|
|
220
169
|
let statusCode = 200;
|
|
221
|
-
const response:
|
|
170
|
+
const response: Response = {
|
|
222
171
|
status: (code: number) => {
|
|
223
172
|
statusCode = code;
|
|
224
173
|
return response;
|
|
@@ -226,8 +175,8 @@ export class DevServer {
|
|
|
226
175
|
};
|
|
227
176
|
|
|
228
177
|
await handler({ context, event, response });
|
|
178
|
+
log.info('res', { seq, name, statusCode, duration: Date.now() - now });
|
|
179
|
+
|
|
229
180
|
return statusCode;
|
|
230
181
|
}
|
|
231
182
|
}
|
|
232
|
-
|
|
233
|
-
const createContext = () => new Context({ name: 'DevServer' });
|