@dxos/functions 0.8.4-main.e098934 → 0.8.4-main.ead640a
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/bundler/index.mjs +10 -6
- package/dist/lib/browser/bundler/index.mjs.map +3 -3
- package/dist/lib/browser/{chunk-D2XO7XXY.mjs → chunk-LKYT2SAL.mjs} +176 -122
- package/dist/lib/browser/chunk-LKYT2SAL.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +319 -199
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +11 -9
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/bundler/index.mjs +10 -6
- package/dist/lib/node-esm/bundler/index.mjs.map +3 -3
- package/dist/lib/node-esm/{chunk-Z4CJ62WS.mjs → chunk-NAQIKLZB.mjs} +176 -122
- package/dist/lib/node-esm/chunk-NAQIKLZB.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +319 -199
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +11 -9
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/types/src/bundler/bundler.d.ts.map +1 -1
- package/dist/types/src/errors.d.ts +16 -16
- package/dist/types/src/examples/fib.d.ts.map +1 -1
- package/dist/types/src/examples/reply.d.ts.map +1 -1
- package/dist/types/src/examples/sleep.d.ts.map +1 -1
- package/dist/types/src/executor/executor.d.ts +3 -0
- package/dist/types/src/executor/executor.d.ts.map +1 -1
- package/dist/types/src/handler.d.ts +19 -4
- package/dist/types/src/handler.d.ts.map +1 -1
- package/dist/types/src/schema.d.ts +5 -5
- package/dist/types/src/schema.d.ts.map +1 -1
- package/dist/types/src/services/credentials.d.ts +6 -2
- package/dist/types/src/services/credentials.d.ts.map +1 -1
- package/dist/types/src/services/database.d.ts +6 -2
- package/dist/types/src/services/database.d.ts.map +1 -1
- package/dist/types/src/services/event-logger.d.ts +4 -1
- package/dist/types/src/services/event-logger.d.ts.map +1 -1
- package/dist/types/src/services/function-invocation-service.d.ts +28 -0
- package/dist/types/src/services/function-invocation-service.d.ts.map +1 -0
- package/dist/types/src/services/function-invocation-service.test.d.ts +2 -0
- package/dist/types/src/services/function-invocation-service.test.d.ts.map +1 -0
- package/dist/types/src/services/index.d.ts +4 -3
- package/dist/types/src/services/index.d.ts.map +1 -1
- package/dist/types/src/services/local-function-execution.d.ts +18 -9
- package/dist/types/src/services/local-function-execution.d.ts.map +1 -1
- package/dist/types/src/services/queues.d.ts +3 -1
- package/dist/types/src/services/queues.d.ts.map +1 -1
- package/dist/types/src/services/remote-function-execution-service.d.ts +11 -4
- package/dist/types/src/services/remote-function-execution-service.d.ts.map +1 -1
- package/dist/types/src/services/service-container.d.ts +2 -1
- package/dist/types/src/services/service-container.d.ts.map +1 -1
- package/dist/types/src/services/service-registry.d.ts +3 -1
- package/dist/types/src/services/service-registry.d.ts.map +1 -1
- package/dist/types/src/services/tracing.d.ts +5 -3
- package/dist/types/src/services/tracing.d.ts.map +1 -1
- package/dist/types/src/testing/layer.d.ts +6 -3
- package/dist/types/src/testing/layer.d.ts.map +1 -1
- package/dist/types/src/testing/logger.d.ts +1 -1
- package/dist/types/src/testing/logger.d.ts.map +1 -1
- package/dist/types/src/testing/services.d.ts +1 -1
- package/dist/types/src/testing/services.d.ts.map +1 -1
- package/dist/types/src/trace.d.ts +1 -1
- package/dist/types/src/trace.d.ts.map +1 -1
- package/dist/types/src/triggers/invocation-tracer.d.ts +3 -1
- package/dist/types/src/triggers/invocation-tracer.d.ts.map +1 -1
- package/dist/types/src/triggers/trigger-dispatcher.d.ts +7 -4
- package/dist/types/src/triggers/trigger-dispatcher.d.ts.map +1 -1
- package/dist/types/src/triggers/trigger-state-store.d.ts +5 -4
- package/dist/types/src/triggers/trigger-state-store.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +49 -30
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/src/url.d.ts +1 -1
- package/dist/types/src/url.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +27 -36
- package/src/bundler/bundler.ts +7 -3
- package/src/examples/fib.ts +4 -2
- package/src/examples/reply.ts +5 -2
- package/src/examples/sleep.ts +4 -2
- package/src/executor/executor.ts +5 -1
- package/src/handler.ts +33 -9
- package/src/schema.ts +5 -2
- package/src/services/credentials.ts +9 -2
- package/src/services/database.ts +6 -2
- package/src/services/event-logger.ts +4 -1
- package/src/services/function-invocation-service.test.ts +81 -0
- package/src/services/function-invocation-service.ts +84 -0
- package/src/services/index.ts +4 -3
- package/src/services/local-function-execution.ts +65 -26
- package/src/services/queues.ts +3 -1
- package/src/services/remote-function-execution-service.ts +38 -21
- package/src/services/service-container.ts +2 -1
- package/src/services/service-registry.test.ts +4 -1
- package/src/services/service-registry.ts +7 -3
- package/src/services/tracing.ts +17 -14
- package/src/testing/layer.ts +10 -7
- package/src/testing/logger.ts +2 -1
- package/src/testing/persist-database.test.ts +4 -4
- package/src/testing/services.ts +1 -1
- package/src/trace.ts +2 -2
- package/src/triggers/invocation-tracer.ts +3 -1
- package/src/triggers/trigger-dispatcher.test.ts +29 -16
- package/src/triggers/trigger-dispatcher.ts +29 -8
- package/src/triggers/trigger-state-store.ts +6 -5
- package/src/types.ts +30 -12
- package/src/url.ts +1 -1
- package/dist/lib/browser/chunk-D2XO7XXY.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-Z4CJ62WS.mjs.map +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/functions",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.ead640a",
|
|
4
4
|
"description": "Functions API and runtime.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -51,46 +51,37 @@
|
|
|
51
51
|
"src"
|
|
52
52
|
],
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@effect/platform": "0.
|
|
55
|
-
"
|
|
56
|
-
"cron": "^3.1.6",
|
|
57
|
-
"cron-schedule": "^5.0.4",
|
|
58
|
-
"effect": "3.17.7",
|
|
54
|
+
"@effect/platform": "0.92.1",
|
|
55
|
+
"effect": "3.18.3",
|
|
59
56
|
"esbuild-wasm": "^0.16.14",
|
|
60
|
-
"express": "^4.19.2",
|
|
61
|
-
"get-port-please": "^3.1.1",
|
|
62
57
|
"i18next": "^24.2.1",
|
|
63
58
|
"iso-did": "^1.6.0",
|
|
64
|
-
"
|
|
65
|
-
"@dxos/
|
|
66
|
-
"@dxos/
|
|
67
|
-
"@dxos/
|
|
68
|
-
"@dxos/
|
|
69
|
-
"@dxos/
|
|
70
|
-
"@dxos/
|
|
71
|
-
"@dxos/
|
|
72
|
-
"@dxos/echo-db": "0.8.4-main.
|
|
73
|
-
"@dxos/echo-
|
|
74
|
-
"@dxos/
|
|
75
|
-
"@dxos/
|
|
76
|
-
"@dxos/
|
|
77
|
-
"@dxos/
|
|
78
|
-
"@dxos/
|
|
79
|
-
"@dxos/
|
|
80
|
-
"@dxos/
|
|
81
|
-
"@dxos/
|
|
82
|
-
"@dxos/
|
|
83
|
-
"@dxos/
|
|
84
|
-
"@dxos/
|
|
85
|
-
"@dxos/
|
|
86
|
-
"@dxos/protocols": "0.8.4-main.
|
|
87
|
-
"@dxos/schema": "0.8.4-main.e098934",
|
|
88
|
-
"@dxos/util": "0.8.4-main.e098934"
|
|
59
|
+
"@dxos/ai": "0.8.4-main.ead640a",
|
|
60
|
+
"@dxos/async": "0.8.4-main.ead640a",
|
|
61
|
+
"@dxos/crypto": "0.8.4-main.ead640a",
|
|
62
|
+
"@dxos/client": "0.8.4-main.ead640a",
|
|
63
|
+
"@dxos/context": "0.8.4-main.ead640a",
|
|
64
|
+
"@dxos/debug": "0.8.4-main.ead640a",
|
|
65
|
+
"@dxos/echo": "0.8.4-main.ead640a",
|
|
66
|
+
"@dxos/echo-pipeline": "0.8.4-main.ead640a",
|
|
67
|
+
"@dxos/echo-db": "0.8.4-main.ead640a",
|
|
68
|
+
"@dxos/echo-protocol": "0.8.4-main.ead640a",
|
|
69
|
+
"@dxos/client-protocol": "0.8.4-main.ead640a",
|
|
70
|
+
"@dxos/edge-client": "0.8.4-main.ead640a",
|
|
71
|
+
"@dxos/invariant": "0.8.4-main.ead640a",
|
|
72
|
+
"@dxos/kv-store": "0.8.4-main.ead640a",
|
|
73
|
+
"@dxos/keys": "0.8.4-main.ead640a",
|
|
74
|
+
"@dxos/live-object": "0.8.4-main.ead640a",
|
|
75
|
+
"@dxos/effect": "0.8.4-main.ead640a",
|
|
76
|
+
"@dxos/errors": "0.8.4-main.ead640a",
|
|
77
|
+
"@dxos/node-std": "0.8.4-main.ead640a",
|
|
78
|
+
"@dxos/log": "0.8.4-main.ead640a",
|
|
79
|
+
"@dxos/schema": "0.8.4-main.ead640a",
|
|
80
|
+
"@dxos/util": "0.8.4-main.ead640a",
|
|
81
|
+
"@dxos/protocols": "0.8.4-main.ead640a"
|
|
89
82
|
},
|
|
90
83
|
"devDependencies": {
|
|
91
|
-
"@
|
|
92
|
-
"@types/ws": "^7.4.0",
|
|
93
|
-
"@dxos/agent": "0.8.4-main.e098934"
|
|
84
|
+
"@dxos/agent": "0.8.4-main.ead640a"
|
|
94
85
|
},
|
|
95
86
|
"publishConfig": {
|
|
96
87
|
"access": "public"
|
package/src/bundler/bundler.ts
CHANGED
|
@@ -2,8 +2,12 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import * as FetchHttpClient from '@effect/platform/FetchHttpClient';
|
|
6
|
+
import * as HttpClient from '@effect/platform/HttpClient';
|
|
7
|
+
import * as Duration from 'effect/Duration';
|
|
8
|
+
import * as Effect from 'effect/Effect';
|
|
9
|
+
import * as Function from 'effect/Function';
|
|
10
|
+
import * as Schedule from 'effect/Schedule';
|
|
7
11
|
import { type BuildOptions, type BuildResult, type Loader, type Plugin, build, initialize } from 'esbuild-wasm';
|
|
8
12
|
|
|
9
13
|
import { subtleCrypto } from '@dxos/crypto';
|
|
@@ -277,7 +281,7 @@ const httpPlugin: Plugin = {
|
|
|
277
281
|
return { contents: text, loader: 'jsx' as Loader };
|
|
278
282
|
}).pipe(
|
|
279
283
|
Effect.retry(
|
|
280
|
-
pipe(
|
|
284
|
+
Function.pipe(
|
|
281
285
|
Schedule.exponential(Duration.millis(INITIAL_DELAY)),
|
|
282
286
|
Schedule.jittered,
|
|
283
287
|
Schedule.intersect(Schedule.recurs(MAX_RETRIES - 1)),
|
package/src/examples/fib.ts
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
6
7
|
|
|
7
8
|
import { defineFunction } from '../handler';
|
|
8
9
|
|
|
9
10
|
export default defineFunction({
|
|
10
|
-
|
|
11
|
+
key: 'example.org/function/fib',
|
|
12
|
+
name: 'Fibonacci',
|
|
11
13
|
description: 'Function that calculates a Fibonacci number',
|
|
12
14
|
inputSchema: Schema.Struct({
|
|
13
15
|
iterations: Schema.optional(Schema.Number).annotations({
|
package/src/examples/reply.ts
CHANGED
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Console from 'effect/Console';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
import * as Schema from 'effect/Schema';
|
|
6
8
|
|
|
7
9
|
import { defineFunction } from '../handler';
|
|
8
10
|
|
|
9
11
|
export default defineFunction({
|
|
10
|
-
|
|
12
|
+
key: 'example.org/function/reply',
|
|
13
|
+
name: 'Reply',
|
|
11
14
|
description: 'Function that echoes the input',
|
|
12
15
|
inputSchema: Schema.Any,
|
|
13
16
|
outputSchema: Schema.Any,
|
package/src/examples/sleep.ts
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
6
7
|
|
|
7
8
|
import { defineFunction } from '../handler';
|
|
8
9
|
|
|
9
10
|
export default defineFunction({
|
|
10
|
-
|
|
11
|
+
key: 'example.org/function/sleep',
|
|
12
|
+
name: 'Sleep',
|
|
11
13
|
description: 'Function that sleeps for a given amount of time',
|
|
12
14
|
inputSchema: Schema.Struct({
|
|
13
15
|
duration: Schema.optional(Schema.Number).annotations({
|
package/src/executor/executor.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
6
7
|
|
|
7
8
|
import { type SpaceId } from '@dxos/client/echo';
|
|
8
9
|
import { runAndForwardErrors } from '@dxos/effect';
|
|
@@ -10,6 +11,9 @@ import { runAndForwardErrors } from '@dxos/effect';
|
|
|
10
11
|
import type { FunctionContext, FunctionDefinition } from '../handler';
|
|
11
12
|
import type { ServiceContainer, Services } from '../services';
|
|
12
13
|
|
|
14
|
+
/**
|
|
15
|
+
* @deprecated Use `FunctionInvocationService`
|
|
16
|
+
*/
|
|
13
17
|
export class FunctionExecutor {
|
|
14
18
|
constructor(private readonly _services: ServiceContainer) {}
|
|
15
19
|
|
package/src/handler.ts
CHANGED
|
@@ -2,17 +2,21 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import type * as Context from 'effect/Context';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
import * as Schema from 'effect/Schema';
|
|
8
|
+
import type * as Types from 'effect/Types';
|
|
6
9
|
|
|
7
10
|
import { Obj, Type } from '@dxos/echo';
|
|
11
|
+
import { type HasId } from '@dxos/echo/internal';
|
|
8
12
|
import { type EchoDatabase } from '@dxos/echo-db';
|
|
9
|
-
import { type HasId } from '@dxos/echo-schema';
|
|
10
13
|
import { assertArgument } from '@dxos/invariant';
|
|
11
14
|
import { type DXN, type SpaceId } from '@dxos/keys';
|
|
12
15
|
import { type QueryResult } from '@dxos/protocols';
|
|
13
16
|
|
|
14
17
|
import { FunctionType } from './schema';
|
|
15
18
|
import { type Services } from './services';
|
|
19
|
+
import { getUserFunctionIdInMetadata, setUserFunctionIdInMetadata } from './url';
|
|
16
20
|
|
|
17
21
|
// TODO(burdon): Model after http request. Ref Lambda/OpenFaaS.
|
|
18
22
|
// https://docs.aws.amazon.com/lambda/latest/dg/typescript-handler.html
|
|
@@ -90,19 +94,30 @@ const typeId = Symbol.for('@dxos/functions/FunctionDefinition');
|
|
|
90
94
|
|
|
91
95
|
export type FunctionDefinition<T = any, O = any> = {
|
|
92
96
|
[typeId]: true;
|
|
93
|
-
// TODO(dmaretskyi): Use `key` for FQN and `name` for human-readable-name.
|
|
94
97
|
key: string;
|
|
95
98
|
name: string;
|
|
96
99
|
description?: string;
|
|
97
100
|
inputSchema: Schema.Schema<T, any>;
|
|
98
101
|
outputSchema?: Schema.Schema<O, any>;
|
|
99
102
|
handler: FunctionHandler<T, O>;
|
|
103
|
+
meta?: {
|
|
104
|
+
/**
|
|
105
|
+
* Tools that are projected from functions have this annotation.
|
|
106
|
+
*
|
|
107
|
+
* deployedFunctionId:
|
|
108
|
+
* - Backend deployment ID assigned by the EDGE function service (typically a UUID).
|
|
109
|
+
* - Used for remote invocation via `FunctionInvocationService` → `RemoteFunctionExecutionService`.
|
|
110
|
+
* - Persisted on the corresponding ECHO `FunctionType` object's metadata under the
|
|
111
|
+
* `FUNCTIONS_META_KEY` and retrieved with `getUserFunctionIdInMetadata`.
|
|
112
|
+
*/
|
|
113
|
+
deployedFunctionId?: string;
|
|
114
|
+
};
|
|
100
115
|
};
|
|
101
116
|
|
|
117
|
+
// TODO(dmaretskyi): Output type doesn't get typechecked.
|
|
102
118
|
export const defineFunction: {
|
|
103
119
|
<I, O>(params: {
|
|
104
|
-
|
|
105
|
-
key?: string;
|
|
120
|
+
key: string;
|
|
106
121
|
name: string;
|
|
107
122
|
description?: string;
|
|
108
123
|
inputSchema: Schema.Schema<I, any>;
|
|
@@ -148,7 +163,7 @@ export const defineFunction: {
|
|
|
148
163
|
|
|
149
164
|
return {
|
|
150
165
|
[typeId]: true,
|
|
151
|
-
key
|
|
166
|
+
key,
|
|
152
167
|
name,
|
|
153
168
|
description,
|
|
154
169
|
inputSchema,
|
|
@@ -177,15 +192,20 @@ export declare namespace FunctionDefinition {
|
|
|
177
192
|
export type Output<T extends FunctionDefinition> = T extends FunctionDefinition<any, infer O> ? O : never;
|
|
178
193
|
}
|
|
179
194
|
|
|
180
|
-
export const serializeFunction = (functionDef: FunctionDefinition<any, any>): FunctionType =>
|
|
181
|
-
Obj.make(FunctionType, {
|
|
182
|
-
key: functionDef.
|
|
195
|
+
export const serializeFunction = (functionDef: FunctionDefinition<any, any>): FunctionType => {
|
|
196
|
+
const fn = Obj.make(FunctionType, {
|
|
197
|
+
key: functionDef.key,
|
|
183
198
|
name: functionDef.name,
|
|
184
199
|
version: '0.1.0',
|
|
185
200
|
description: functionDef.description,
|
|
186
201
|
inputSchema: Type.toJsonSchema(functionDef.inputSchema),
|
|
187
202
|
outputSchema: !functionDef.outputSchema ? undefined : Type.toJsonSchema(functionDef.outputSchema),
|
|
188
203
|
});
|
|
204
|
+
if (functionDef.meta?.deployedFunctionId) {
|
|
205
|
+
setUserFunctionIdInMetadata(Obj.getMeta(fn), functionDef.meta.deployedFunctionId);
|
|
206
|
+
}
|
|
207
|
+
return fn;
|
|
208
|
+
};
|
|
189
209
|
|
|
190
210
|
export const deserializeFunction = (functionObj: FunctionType): FunctionDefinition<unknown, unknown> => {
|
|
191
211
|
return {
|
|
@@ -196,6 +216,10 @@ export const deserializeFunction = (functionObj: FunctionType): FunctionDefiniti
|
|
|
196
216
|
description: functionObj.description,
|
|
197
217
|
inputSchema: !functionObj.inputSchema ? Schema.Unknown : Type.toEffectSchema(functionObj.inputSchema),
|
|
198
218
|
outputSchema: !functionObj.outputSchema ? undefined : Type.toEffectSchema(functionObj.outputSchema),
|
|
219
|
+
// TODO(dmaretskyi): This should throw error.
|
|
199
220
|
handler: () => {},
|
|
221
|
+
meta: {
|
|
222
|
+
deployedFunctionId: getUserFunctionIdInMetadata(Obj.getMeta(functionObj)),
|
|
223
|
+
},
|
|
200
224
|
};
|
|
201
225
|
};
|
package/src/schema.ts
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
6
|
|
|
7
7
|
import { Type } from '@dxos/echo';
|
|
8
|
-
import { JsonSchemaType, LabelAnnotation, Ref } from '@dxos/echo
|
|
8
|
+
import { JsonSchemaType, LabelAnnotation, Ref } from '@dxos/echo/internal';
|
|
9
9
|
import { DataType } from '@dxos/schema';
|
|
10
10
|
|
|
11
|
+
// TODO(burdon): Create namespace and remove Type suffix.
|
|
12
|
+
|
|
11
13
|
/**
|
|
12
14
|
* Source script.
|
|
13
15
|
*/
|
|
@@ -65,4 +67,5 @@ export const FunctionType = Schema.Struct({
|
|
|
65
67
|
}),
|
|
66
68
|
LabelAnnotation.set(['name']),
|
|
67
69
|
);
|
|
70
|
+
|
|
68
71
|
export interface FunctionType extends Schema.Schema.Type<typeof FunctionType> {}
|
|
@@ -2,8 +2,13 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import * as HttpClient from '@effect/platform/HttpClient';
|
|
6
|
+
import * as HttpClientRequest from '@effect/platform/HttpClientRequest';
|
|
7
|
+
import type * as Config from 'effect/Config';
|
|
8
|
+
import * as Context from 'effect/Context';
|
|
9
|
+
import * as Effect from 'effect/Effect';
|
|
10
|
+
import * as Layer from 'effect/Layer';
|
|
11
|
+
import * as Redacted from 'effect/Redacted';
|
|
7
12
|
|
|
8
13
|
import { Query } from '@dxos/echo';
|
|
9
14
|
import { DataType } from '@dxos/schema';
|
|
@@ -93,6 +98,7 @@ export class CredentialsService extends Context.Tag('@dxos/functions/Credentials
|
|
|
93
98
|
if (credentials.length === 0) {
|
|
94
99
|
throw new Error(`Credential not found for service: ${query.service}`);
|
|
95
100
|
}
|
|
101
|
+
|
|
96
102
|
return credentials[0];
|
|
97
103
|
},
|
|
98
104
|
queryCredentials: async (query) => {
|
|
@@ -120,6 +126,7 @@ export class ConfiguredCredentialsService implements Context.Tag.Service<Credent
|
|
|
120
126
|
if (!credential) {
|
|
121
127
|
throw new Error(`Credential not found for service: ${query.service}`);
|
|
122
128
|
}
|
|
129
|
+
|
|
123
130
|
return credential;
|
|
124
131
|
}
|
|
125
132
|
}
|
package/src/services/database.ts
CHANGED
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Context from 'effect/Context';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
import * as Layer from 'effect/Layer';
|
|
8
|
+
import * as Option from 'effect/Option';
|
|
9
|
+
import type * as Schema from 'effect/Schema';
|
|
6
10
|
|
|
7
11
|
import {
|
|
8
12
|
type Filter,
|
|
@@ -14,9 +18,9 @@ import {
|
|
|
14
18
|
type Relation,
|
|
15
19
|
type Type,
|
|
16
20
|
} from '@dxos/echo';
|
|
21
|
+
import type { EchoSchema } from '@dxos/echo/internal';
|
|
17
22
|
import type { EchoDatabase, FlushOptions, OneShotQueryResult, QueryResult, SchemaRegistryQuery } from '@dxos/echo-db';
|
|
18
23
|
import type { SchemaRegistryPreparedQuery } from '@dxos/echo-db';
|
|
19
|
-
import type { EchoSchema } from '@dxos/echo-schema';
|
|
20
24
|
import { promiseWithCauseCapture } from '@dxos/effect';
|
|
21
25
|
import { invariant } from '@dxos/invariant';
|
|
22
26
|
import type { DXN } from '@dxos/keys';
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Context from 'effect/Context';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
import * as Layer from 'effect/Layer';
|
|
8
|
+
import * as Schema from 'effect/Schema';
|
|
6
9
|
|
|
7
10
|
import { Obj, Type } from '@dxos/echo';
|
|
8
11
|
import { invariant } from '@dxos/invariant';
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { describe, expect, it } from '@effect/vitest';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
import * as Layer from 'effect/Layer';
|
|
8
|
+
import * as Schema from 'effect/Schema';
|
|
9
|
+
|
|
10
|
+
import { AiService } from '@dxos/ai';
|
|
11
|
+
|
|
12
|
+
import { defineFunction } from '../handler';
|
|
13
|
+
import { TestDatabaseLayer } from '../testing';
|
|
14
|
+
|
|
15
|
+
import { FunctionInvocationService } from './function-invocation-service';
|
|
16
|
+
import { FunctionImplementationResolver } from './local-function-execution';
|
|
17
|
+
|
|
18
|
+
const TestLayer = Layer.mergeAll(AiService.model('@anthropic/claude-opus-4-0')).pipe(
|
|
19
|
+
Layer.provideMerge(
|
|
20
|
+
Layer.mergeAll(
|
|
21
|
+
TestDatabaseLayer({
|
|
22
|
+
indexing: { vector: true },
|
|
23
|
+
types: [],
|
|
24
|
+
}),
|
|
25
|
+
FunctionInvocationService.layer,
|
|
26
|
+
),
|
|
27
|
+
),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
describe('FunctionInvocationService', () => {
|
|
31
|
+
it(
|
|
32
|
+
'should be defined',
|
|
33
|
+
Effect.fnUntraced(function* () {
|
|
34
|
+
const service = yield* FunctionInvocationService;
|
|
35
|
+
expect(service).toBeDefined();
|
|
36
|
+
}, Effect.provide(TestLayer)),
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
it(
|
|
40
|
+
'routes to local when implementation is available',
|
|
41
|
+
Effect.fnUntraced(function* () {
|
|
42
|
+
const add = defineFunction({
|
|
43
|
+
key: 'example.org/function/add',
|
|
44
|
+
name: 'add',
|
|
45
|
+
inputSchema: Schema.Struct({ a: Schema.Number, b: Schema.Number }),
|
|
46
|
+
outputSchema: Schema.Number,
|
|
47
|
+
handler: ({ data }) => data.a + data.b,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const layer = TestLayer.pipe(Layer.provideMerge(FunctionImplementationResolver.layerTest({ functions: [add] })));
|
|
51
|
+
|
|
52
|
+
const result = yield* Effect.gen(function* () {
|
|
53
|
+
return yield* FunctionInvocationService.invokeFunction(add, { a: 2, b: 3 });
|
|
54
|
+
}).pipe(Effect.provide(layer));
|
|
55
|
+
|
|
56
|
+
expect(result).toEqual(5);
|
|
57
|
+
}),
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
it(
|
|
61
|
+
'routes to remote when no local implementation is found',
|
|
62
|
+
Effect.fnUntraced(function* () {
|
|
63
|
+
// This function is not deployed, so mock layer will be used.
|
|
64
|
+
const echo = defineFunction({
|
|
65
|
+
key: 'example.org/function/echo',
|
|
66
|
+
name: 'function-that-is-deployed',
|
|
67
|
+
inputSchema: Schema.Unknown,
|
|
68
|
+
outputSchema: Schema.Unknown,
|
|
69
|
+
handler: () => {},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// No resolver provided → resolveFunctionImplementation will fail → remote path is used.
|
|
73
|
+
const result = yield* Effect.gen(function* () {
|
|
74
|
+
return yield* FunctionInvocationService.invokeFunction(echo, { hello: 'world' });
|
|
75
|
+
}).pipe(Effect.provide(TestLayer));
|
|
76
|
+
|
|
77
|
+
// RemoteFunctionExecutionService.mock echos input back.
|
|
78
|
+
expect(result).toEqual({ hello: 'world', resolved: 'remote' });
|
|
79
|
+
}),
|
|
80
|
+
);
|
|
81
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
import * as Context from 'effect/Context';
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import * as Layer from 'effect/Layer';
|
|
7
|
+
|
|
8
|
+
import { AiService } from '@dxos/ai';
|
|
9
|
+
|
|
10
|
+
import { type FunctionDefinition } from '../handler';
|
|
11
|
+
|
|
12
|
+
import { CredentialsService } from './credentials';
|
|
13
|
+
import { DatabaseService } from './database';
|
|
14
|
+
import {
|
|
15
|
+
FunctionImplementationResolver,
|
|
16
|
+
type InvocationServices,
|
|
17
|
+
LocalFunctionExecutionService,
|
|
18
|
+
} from './local-function-execution';
|
|
19
|
+
import { QueueService } from './queues';
|
|
20
|
+
import { RemoteFunctionExecutionService } from './remote-function-execution-service';
|
|
21
|
+
|
|
22
|
+
export class FunctionInvocationService extends Context.Tag('@dxos/functions/FunctionInvocationService')<
|
|
23
|
+
FunctionInvocationService,
|
|
24
|
+
{
|
|
25
|
+
invokeFunction<I, O>(functionDef: FunctionDefinition<I, O>, input: I): Effect.Effect<O, never, InvocationServices>;
|
|
26
|
+
}
|
|
27
|
+
>() {
|
|
28
|
+
static invokeFunction = Effect.serviceFunctionEffect(FunctionInvocationService, (_) => _.invokeFunction);
|
|
29
|
+
|
|
30
|
+
static layer = Layer.effect(
|
|
31
|
+
FunctionInvocationService,
|
|
32
|
+
Effect.gen(function* () {
|
|
33
|
+
const localExecutioner = yield* LocalFunctionExecutionService;
|
|
34
|
+
const remoteExecutioner = yield* RemoteFunctionExecutionService;
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
invokeFunction: <I, O>(
|
|
38
|
+
functionDef: FunctionDefinition<I, O>,
|
|
39
|
+
input: I,
|
|
40
|
+
): Effect.Effect<O, never, InvocationServices> =>
|
|
41
|
+
Effect.gen(function* () {
|
|
42
|
+
if (functionDef.meta?.deployedFunctionId) {
|
|
43
|
+
return yield* remoteExecutioner.callFunction<I, O>(functionDef.meta.deployedFunctionId, input);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return yield* localExecutioner.invokeFunction(functionDef, input);
|
|
47
|
+
}),
|
|
48
|
+
} satisfies Context.Tag.Service<FunctionInvocationService>;
|
|
49
|
+
}),
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// TODO(dmaretskyi): Don't provide `FunctionImplementationResolver`.
|
|
53
|
+
static layerTest = ({
|
|
54
|
+
functions = [],
|
|
55
|
+
}: {
|
|
56
|
+
functions?: FunctionDefinition<any, any>[];
|
|
57
|
+
} = {}): Layer.Layer<
|
|
58
|
+
FunctionInvocationService,
|
|
59
|
+
never,
|
|
60
|
+
AiService.AiService | CredentialsService | DatabaseService | QueueService
|
|
61
|
+
> =>
|
|
62
|
+
FunctionInvocationService.layer.pipe(
|
|
63
|
+
Layer.provide(LocalFunctionExecutionService.layerLive),
|
|
64
|
+
Layer.provide(FunctionImplementationResolver.layerTest({ functions })),
|
|
65
|
+
Layer.provide(RemoteFunctionExecutionService.layerMock),
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// TODO(dmaretskyi): This shouldn't default to all services being not available.
|
|
69
|
+
// TODO(dmaretskyi): Don't provide `FunctionImplementationResolver`.
|
|
70
|
+
/**
|
|
71
|
+
* @deprecated Use {@link layerTest} instead.
|
|
72
|
+
*/
|
|
73
|
+
static layerTestMocked = ({
|
|
74
|
+
functions,
|
|
75
|
+
}: {
|
|
76
|
+
functions: FunctionDefinition<any, any>[];
|
|
77
|
+
}): Layer.Layer<FunctionInvocationService> =>
|
|
78
|
+
FunctionInvocationService.layerTest({ functions }).pipe(
|
|
79
|
+
Layer.provide(AiService.notAvailable),
|
|
80
|
+
Layer.provide(CredentialsService.configuredLayer([])),
|
|
81
|
+
Layer.provide(DatabaseService.notAvailable),
|
|
82
|
+
Layer.provide(QueueService.notAvailable),
|
|
83
|
+
);
|
|
84
|
+
}
|
package/src/services/index.ts
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
export * from './credentials';
|
|
5
6
|
export * from './database';
|
|
7
|
+
export * from './event-logger';
|
|
8
|
+
export * from './function-invocation-service';
|
|
9
|
+
export * from './local-function-execution';
|
|
6
10
|
export * from './queues';
|
|
7
11
|
export * from './service-container';
|
|
8
|
-
export * from './credentials';
|
|
9
12
|
export * from './tracing';
|
|
10
|
-
export * from './event-logger';
|
|
11
13
|
export * from './remote-function-execution-service';
|
|
12
|
-
export * from './local-function-execution';
|