@dxos/functions-testing 0.8.4-main.d05673bc65 → 0.8.4-main.d9fc60f731
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/LICENSE +102 -5
- package/README.md +1 -1
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +11 -6
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/types/src/edge-routine.test.d.ts +2 -0
- package/dist/types/src/edge-routine.test.d.ts.map +1 -0
- package/dist/types/src/functions/definitions.d.ts +13 -0
- package/dist/types/src/functions/definitions.d.ts.map +1 -0
- package/dist/types/src/functions/fib.d.ts +3 -2
- package/dist/types/src/functions/fib.d.ts.map +1 -1
- package/dist/types/src/functions/forex-effect.d.ts +3 -2
- package/dist/types/src/functions/forex-effect.d.ts.map +1 -1
- package/dist/types/src/functions/query.d.ts +3 -1
- package/dist/types/src/functions/query.d.ts.map +1 -1
- package/dist/types/src/functions/reply.d.ts +2 -1
- package/dist/types/src/functions/reply.d.ts.map +1 -1
- package/dist/types/src/functions/sleep.d.ts +3 -2
- package/dist/types/src/functions/sleep.d.ts.map +1 -1
- package/dist/types/src/testing/util.d.ts +2 -2
- package/dist/types/src/testing/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +20 -20
- package/src/bundle.test.ts +0 -1
- package/src/cpu-limit.test.ts +20 -18
- package/src/edge-routine.test.ts +110 -0
- package/src/functions/definitions.ts +62 -0
- package/src/functions/fib.ts +15 -24
- package/src/functions/forex-effect.ts +30 -25
- package/src/functions/query.ts +12 -14
- package/src/functions/reply.ts +11 -13
- package/src/functions/sleep.ts +9 -16
- package/src/testing/util.ts +11 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/functions-testing",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.d9fc60f731",
|
|
4
4
|
"description": "Functions tests",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "https://github.com/dxos/dxos"
|
|
10
10
|
},
|
|
11
|
-
"license": "
|
|
11
|
+
"license": "FSL-1.1-Apache-2.0",
|
|
12
12
|
"author": "info@dxos.org",
|
|
13
13
|
"sideEffects": true,
|
|
14
14
|
"type": "module",
|
|
@@ -25,29 +25,29 @@
|
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
27
|
"types": "dist/types/src/index.d.ts",
|
|
28
|
-
"typesVersions": {
|
|
29
|
-
"*": {
|
|
30
|
-
"testing": [
|
|
31
|
-
"dist/types/src/testing/index.d.ts"
|
|
32
|
-
]
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
28
|
"files": [
|
|
36
29
|
"dist",
|
|
37
30
|
"src"
|
|
38
31
|
],
|
|
39
32
|
"dependencies": {
|
|
40
|
-
"@effect/platform": "0.
|
|
41
|
-
"@dxos/async": "0.8.4-main.
|
|
42
|
-
"@dxos/
|
|
43
|
-
"@dxos/
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/
|
|
46
|
-
"@dxos/
|
|
47
|
-
"@dxos/
|
|
48
|
-
"@dxos/
|
|
49
|
-
"@dxos/
|
|
50
|
-
"@dxos/
|
|
33
|
+
"@effect/platform": "0.96.1",
|
|
34
|
+
"@dxos/async": "0.8.4-main.d9fc60f731",
|
|
35
|
+
"@dxos/assistant-toolkit": "0.8.4-main.d9fc60f731",
|
|
36
|
+
"@dxos/client": "0.8.4-main.d9fc60f731",
|
|
37
|
+
"@dxos/compute": "0.8.4-main.d9fc60f731",
|
|
38
|
+
"@dxos/echo": "0.8.4-main.d9fc60f731",
|
|
39
|
+
"@dxos/config": "0.8.4-main.d9fc60f731",
|
|
40
|
+
"@dxos/context": "0.8.4-main.d9fc60f731",
|
|
41
|
+
"@dxos/invariant": "0.8.4-main.d9fc60f731",
|
|
42
|
+
"@dxos/functions": "0.8.4-main.d9fc60f731",
|
|
43
|
+
"@dxos/log": "0.8.4-main.d9fc60f731",
|
|
44
|
+
"@dxos/functions-runtime": "0.8.4-main.d9fc60f731",
|
|
45
|
+
"@dxos/functions-runtime-cloudflare": "0.8.4-main.d9fc60f731",
|
|
46
|
+
"@dxos/keys": "0.8.4-main.d9fc60f731",
|
|
47
|
+
"@dxos/util": "0.8.4-main.d9fc60f731",
|
|
48
|
+
"@dxos/protocols": "0.8.4-main.d9fc60f731",
|
|
49
|
+
"@dxos/operation": "0.8.4-main.d9fc60f731",
|
|
50
|
+
"@dxos/schema": "0.8.4-main.d9fc60f731"
|
|
51
51
|
},
|
|
52
52
|
"publishConfig": {
|
|
53
53
|
"access": "public"
|
package/src/bundle.test.ts
CHANGED
package/src/cpu-limit.test.ts
CHANGED
|
@@ -4,17 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
import { describe, test } from 'vitest';
|
|
6
6
|
|
|
7
|
+
import { Trigger } from '@dxos/compute';
|
|
7
8
|
import { configPreset } from '@dxos/config';
|
|
9
|
+
import { Context } from '@dxos/context';
|
|
8
10
|
import { Obj, Ref } from '@dxos/echo';
|
|
9
|
-
import { Trigger } from '@dxos/functions';
|
|
10
11
|
import { FunctionRuntimeKind } from '@dxos/protocols';
|
|
11
12
|
|
|
12
13
|
import { deployFunction, observeInvocations, setup, sync } from './testing';
|
|
13
14
|
|
|
14
15
|
const FIB_FUNCTION_PATH = new URL('./functions/fib.ts', import.meta.url).pathname;
|
|
15
16
|
|
|
16
|
-
// Test suite to explore who CPU time limits influence function execution
|
|
17
|
-
describe
|
|
17
|
+
// Test suite to explore who CPU time limits influence function execution.
|
|
18
|
+
describe('CPU limit', { tags: ['functions-e2e'] }, () => {
|
|
18
19
|
const config = configPreset({ edge: 'dev' });
|
|
19
20
|
|
|
20
21
|
test('invoke directly', { timeout: 120_000 }, async ({ expect }) => {
|
|
@@ -26,6 +27,7 @@ describe.runIf(process.env.DX_TEST_TAGS?.includes('functions-e2e'))('CPU limit',
|
|
|
26
27
|
FunctionRuntimeKind.enums.WORKERS_FOR_PLATFORMS,
|
|
27
28
|
);
|
|
28
29
|
const result = await functionsServiceClient.invoke(
|
|
30
|
+
Context.default(),
|
|
29
31
|
func,
|
|
30
32
|
{
|
|
31
33
|
iterations: 100,
|
|
@@ -49,12 +51,12 @@ describe.runIf(process.env.DX_TEST_TAGS?.includes('functions-e2e'))('CPU limit',
|
|
|
49
51
|
Obj.make(Trigger.Trigger, {
|
|
50
52
|
enabled: true,
|
|
51
53
|
function: Ref.make(func),
|
|
52
|
-
spec:
|
|
54
|
+
spec: Trigger.specTimer('* */30 * * * *'),
|
|
53
55
|
input: { iterations: 100 },
|
|
54
56
|
}),
|
|
55
57
|
);
|
|
56
58
|
await sync(space);
|
|
57
|
-
const result = await functionsServiceClient.forceRunCronTrigger(space.id, trigger.id);
|
|
59
|
+
const result = await functionsServiceClient.forceRunCronTrigger(Context.default(), space.id, trigger.id);
|
|
58
60
|
console.log(result);
|
|
59
61
|
});
|
|
60
62
|
|
|
@@ -70,27 +72,27 @@ describe.runIf(process.env.DX_TEST_TAGS?.includes('functions-e2e'))('CPU limit',
|
|
|
70
72
|
Obj.make(Trigger.Trigger, {
|
|
71
73
|
enabled: true,
|
|
72
74
|
function: Ref.make(func),
|
|
73
|
-
spec:
|
|
75
|
+
spec: Trigger.specTimer('* */30 * * * *'),
|
|
74
76
|
input: { iterations: 1_000_000_000 },
|
|
75
77
|
}),
|
|
76
78
|
);
|
|
77
79
|
await sync(space);
|
|
78
80
|
{
|
|
79
|
-
const result = await functionsServiceClient.forceRunCronTrigger(space.id, trigger.id);
|
|
81
|
+
const result = await functionsServiceClient.forceRunCronTrigger(Context.default(), space.id, trigger.id);
|
|
80
82
|
console.log(result);
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
{
|
|
84
|
-
const result = await functionsServiceClient.forceRunCronTrigger(space.id, trigger.id);
|
|
86
|
+
const result = await functionsServiceClient.forceRunCronTrigger(Context.default(), space.id, trigger.id);
|
|
85
87
|
console.log(result);
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
{
|
|
89
|
-
Obj.
|
|
90
|
-
|
|
91
|
+
Obj.update(trigger, (trigger) => {
|
|
92
|
+
trigger.input!.iterations = 100;
|
|
91
93
|
});
|
|
92
94
|
await sync(space);
|
|
93
|
-
const result = await functionsServiceClient.forceRunCronTrigger(space.id, trigger.id);
|
|
95
|
+
const result = await functionsServiceClient.forceRunCronTrigger(Context.default(), space.id, trigger.id);
|
|
94
96
|
console.log(result);
|
|
95
97
|
}
|
|
96
98
|
});
|
|
@@ -107,7 +109,7 @@ describe.runIf(process.env.DX_TEST_TAGS?.includes('functions-e2e'))('CPU limit',
|
|
|
107
109
|
Obj.make(Trigger.Trigger, {
|
|
108
110
|
enabled: true,
|
|
109
111
|
function: Ref.make(func),
|
|
110
|
-
spec:
|
|
112
|
+
spec: Trigger.specTimer('* * * * * *'),
|
|
111
113
|
input: { iterations: 1_000_000 },
|
|
112
114
|
}),
|
|
113
115
|
);
|
|
@@ -115,7 +117,7 @@ describe.runIf(process.env.DX_TEST_TAGS?.includes('functions-e2e'))('CPU limit',
|
|
|
115
117
|
await observeInvocations(space, 100);
|
|
116
118
|
});
|
|
117
119
|
|
|
118
|
-
test
|
|
120
|
+
test('break CPU limit through natural exection', { timeout: 520_000 }, async ({ expect }) => {
|
|
119
121
|
const { client, space, functionsServiceClient } = await setup(config);
|
|
120
122
|
const func = await deployFunction(
|
|
121
123
|
space,
|
|
@@ -127,21 +129,21 @@ describe.runIf(process.env.DX_TEST_TAGS?.includes('functions-e2e'))('CPU limit',
|
|
|
127
129
|
Obj.make(Trigger.Trigger, {
|
|
128
130
|
enabled: true,
|
|
129
131
|
function: Ref.make(func),
|
|
130
|
-
spec:
|
|
132
|
+
spec: Trigger.specTimer('* * * * * *'),
|
|
131
133
|
input: { iterations: 100 },
|
|
132
134
|
}),
|
|
133
135
|
);
|
|
134
136
|
await sync(space);
|
|
135
137
|
await observeInvocations(space, 5);
|
|
136
138
|
|
|
137
|
-
Obj.
|
|
138
|
-
|
|
139
|
+
Obj.update(trigger, (trigger) => {
|
|
140
|
+
trigger.input!.iterations = 1_000_000_000;
|
|
139
141
|
});
|
|
140
142
|
await sync(space);
|
|
141
143
|
await observeInvocations(space, 10);
|
|
142
144
|
|
|
143
|
-
Obj.
|
|
144
|
-
|
|
145
|
+
Obj.update(trigger, (trigger) => {
|
|
146
|
+
trigger.input!.iterations = 100;
|
|
145
147
|
});
|
|
146
148
|
await sync(space);
|
|
147
149
|
await observeInvocations(space, 1_000);
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
|
+
import { describe, test } from 'vitest';
|
|
7
|
+
|
|
8
|
+
import { AgentPrompt, DatabaseBlueprint, Chat } from '@dxos/assistant-toolkit';
|
|
9
|
+
import { Client } from '@dxos/client';
|
|
10
|
+
import { Blueprint, Operation, Routine, Trigger } from '@dxos/compute';
|
|
11
|
+
import { configPreset } from '@dxos/config';
|
|
12
|
+
import { Context } from '@dxos/context';
|
|
13
|
+
import { Feed, Obj, Ref, Type } from '@dxos/echo';
|
|
14
|
+
import { TestSchema } from '@dxos/echo/testing';
|
|
15
|
+
import { dbg, log } from '@dxos/log';
|
|
16
|
+
import { ErrorCodec } from '@dxos/protocols';
|
|
17
|
+
import { EdgeReplicationSetting } from '@dxos/protocols/proto/dxos/echo/metadata';
|
|
18
|
+
import { Text } from '@dxos/schema';
|
|
19
|
+
import { trim } from '@dxos/util';
|
|
20
|
+
|
|
21
|
+
import { sync } from './testing';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Cron trigger on EDGE runs {@link AgentPrompt} for a {@link Routine} that uses the Database
|
|
25
|
+
* blueprint Query tool against replicated ECHO documents.
|
|
26
|
+
*
|
|
27
|
+
* Prereq: local EDGE (`configPreset({ edge: 'local' })` → `http://localhost:8787`) with LLM
|
|
28
|
+
* available (API key and/or memo replay under `conversationsCache/`).
|
|
29
|
+
*/
|
|
30
|
+
describe('Edge routine', { tags: ['functions-e2e'] }, () => {
|
|
31
|
+
const config = configPreset({ edge: 'local' });
|
|
32
|
+
|
|
33
|
+
test('timer trigger runs routine with database blueprint on edge', { timeout: 420_000 }, async ({ expect }) => {
|
|
34
|
+
await using client = await new Client({
|
|
35
|
+
config,
|
|
36
|
+
types: [
|
|
37
|
+
Operation.PersistentOperation,
|
|
38
|
+
Trigger.Trigger,
|
|
39
|
+
Routine.Routine,
|
|
40
|
+
Blueprint.Blueprint,
|
|
41
|
+
Feed.Feed,
|
|
42
|
+
Text.Text,
|
|
43
|
+
Chat.Chat,
|
|
44
|
+
TestSchema.Organization,
|
|
45
|
+
],
|
|
46
|
+
}).initialize();
|
|
47
|
+
|
|
48
|
+
await client.halo.createIdentity();
|
|
49
|
+
const space = await client.spaces.create();
|
|
50
|
+
await space.waitUntilReady();
|
|
51
|
+
|
|
52
|
+
await space.internal.setEdgeReplicationPreference(EdgeReplicationSetting.ENABLED);
|
|
53
|
+
|
|
54
|
+
Obj.update(space.properties, (properties) => {
|
|
55
|
+
properties.computeEnvironment = 'edge';
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
space.db.add(Obj.make(TestSchema.Organization, { name: 'Acme Corp' }));
|
|
59
|
+
space.db.add(Obj.make(TestSchema.Organization, { name: 'Globex Industries' }));
|
|
60
|
+
space.db.add(Obj.make(TestSchema.Organization, { name: 'Initech' }));
|
|
61
|
+
|
|
62
|
+
const databaseBlueprint = space.db.add(DatabaseBlueprint.make());
|
|
63
|
+
|
|
64
|
+
const routine = space.db.add(
|
|
65
|
+
Routine.make({
|
|
66
|
+
name: 'edge-e2e-count-orgs-db-blueprint',
|
|
67
|
+
instructions: trim`
|
|
68
|
+
You have access to the Database blueprint tools.
|
|
69
|
+
Use the Query tool exactly once with typename "${Type.getTypename(TestSchema.Organization)}" and no other arguments.
|
|
70
|
+
Then call completeJob with the output object { "count": <number of rows returned by Query> }.
|
|
71
|
+
If you are unable to query -- fail.
|
|
72
|
+
Do not list schemas first.
|
|
73
|
+
`,
|
|
74
|
+
input: Schema.Struct({}),
|
|
75
|
+
output: Schema.Struct({
|
|
76
|
+
count: Schema.Number,
|
|
77
|
+
}),
|
|
78
|
+
blueprints: [Ref.make(databaseBlueprint)],
|
|
79
|
+
context: [],
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
const fn = Operation.serialize(AgentPrompt);
|
|
83
|
+
dbg(Obj.toJSON(fn));
|
|
84
|
+
|
|
85
|
+
const trigger = space.db.add(
|
|
86
|
+
Obj.make(Trigger.Trigger, {
|
|
87
|
+
enabled: true,
|
|
88
|
+
function: Ref.make(fn),
|
|
89
|
+
spec: Trigger.specTimer('* * * * * *'),
|
|
90
|
+
input: {
|
|
91
|
+
prompt: Ref.make(routine),
|
|
92
|
+
input: {},
|
|
93
|
+
model: '@anthropic/claude-haiku-4-5',
|
|
94
|
+
},
|
|
95
|
+
}),
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
await sync(space);
|
|
99
|
+
|
|
100
|
+
log('trigger created and synced');
|
|
101
|
+
log.break();
|
|
102
|
+
|
|
103
|
+
const runResult: any = await client.edge.http.forceRunCronTrigger(Context.default(), space.id, trigger.id);
|
|
104
|
+
if (runResult._kind === 'error') {
|
|
105
|
+
throw ErrorCodec.decode(runResult.error);
|
|
106
|
+
}
|
|
107
|
+
log('trigger ran', { runResult });
|
|
108
|
+
expect(runResult.result.count).toBe(3);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
|
+
|
|
7
|
+
import { Operation } from '@dxos/compute';
|
|
8
|
+
import { Database } from '@dxos/echo';
|
|
9
|
+
import { DXN } from '@dxos/keys';
|
|
10
|
+
|
|
11
|
+
export const Fibonacci = Operation.make({
|
|
12
|
+
meta: {
|
|
13
|
+
key: DXN.make('org.example.function.fib'),
|
|
14
|
+
name: 'Fibonacci',
|
|
15
|
+
description: 'Function that calculates a Fibonacci number',
|
|
16
|
+
},
|
|
17
|
+
input: Schema.Struct({
|
|
18
|
+
iterations: Schema.optional(Schema.Number).annotations({
|
|
19
|
+
description: 'Number of iterations',
|
|
20
|
+
default: 100_000,
|
|
21
|
+
}),
|
|
22
|
+
}),
|
|
23
|
+
output: Schema.Struct({
|
|
24
|
+
result: Schema.String,
|
|
25
|
+
}),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export const Reply = Operation.make({
|
|
29
|
+
meta: {
|
|
30
|
+
key: DXN.make('org.example.function.reply'),
|
|
31
|
+
name: 'Reply',
|
|
32
|
+
description: 'Function that echoes the input',
|
|
33
|
+
},
|
|
34
|
+
input: Schema.Any,
|
|
35
|
+
output: Schema.Any,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export const Sleep = Operation.make({
|
|
39
|
+
meta: {
|
|
40
|
+
key: DXN.make('org.example.function.sleep'),
|
|
41
|
+
name: 'Sleep',
|
|
42
|
+
description: 'Function that sleeps for a given amount of time',
|
|
43
|
+
},
|
|
44
|
+
input: Schema.Struct({
|
|
45
|
+
duration: Schema.optional(Schema.Number).annotations({
|
|
46
|
+
description: 'Milliseconds to sleep',
|
|
47
|
+
default: 100_000,
|
|
48
|
+
}),
|
|
49
|
+
}),
|
|
50
|
+
output: Schema.Void,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export const QueryDb = Operation.make({
|
|
54
|
+
meta: {
|
|
55
|
+
key: DXN.make('org.example.function.query'),
|
|
56
|
+
name: 'Query',
|
|
57
|
+
description: 'Queries the database',
|
|
58
|
+
},
|
|
59
|
+
input: Schema.Any,
|
|
60
|
+
output: Schema.Any,
|
|
61
|
+
services: [Database.Service],
|
|
62
|
+
});
|
package/src/functions/fib.ts
CHANGED
|
@@ -3,30 +3,21 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import * as Effect from 'effect/Effect';
|
|
6
|
-
import * as Schema from 'effect/Schema';
|
|
7
6
|
|
|
8
|
-
import {
|
|
7
|
+
import { Operation } from '@dxos/compute';
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
import { Fibonacci } from './definitions';
|
|
10
|
+
|
|
11
|
+
export default Fibonacci.pipe(
|
|
12
|
+
Operation.withHandler(
|
|
13
|
+
Effect.fn(function* ({ iterations = 100_000 }) {
|
|
14
|
+
let a = 0n;
|
|
15
|
+
let b = 1n;
|
|
16
|
+
for (let i = 0; i < iterations; i++) {
|
|
17
|
+
a += b;
|
|
18
|
+
b = a - b;
|
|
19
|
+
}
|
|
20
|
+
return { result: a.toString() };
|
|
18
21
|
}),
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
result: Schema.String,
|
|
22
|
-
}),
|
|
23
|
-
handler: Effect.fn(function* ({ data: { iterations = 100_000 } }) {
|
|
24
|
-
let a = 0n;
|
|
25
|
-
let b = 1n;
|
|
26
|
-
for (let i = 0; i < iterations; i++) {
|
|
27
|
-
a += b;
|
|
28
|
-
b = a - b;
|
|
29
|
-
}
|
|
30
|
-
return { result: a.toString() };
|
|
31
|
-
}),
|
|
32
|
-
});
|
|
22
|
+
),
|
|
23
|
+
);
|
|
@@ -10,35 +10,40 @@ import * as Effect from 'effect/Effect';
|
|
|
10
10
|
import * as Schedule from 'effect/Schedule';
|
|
11
11
|
import * as Schema from 'effect/Schema';
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import { Operation } from '@dxos/compute';
|
|
14
|
+
import { DXN } from '@dxos/keys';
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
const ForexEffect = Operation.make({
|
|
17
|
+
meta: {
|
|
18
|
+
key: DXN.make('org.dxos.script.forexEffect'),
|
|
19
|
+
name: 'Forex Effect',
|
|
20
|
+
description: 'Returns the exchange rate between two currencies.',
|
|
21
|
+
},
|
|
22
|
+
input: Schema.Struct({
|
|
21
23
|
from: Schema.String.annotations({ description: 'The source currency' }),
|
|
22
24
|
to: Schema.String.annotations({ description: 'The target currency' }),
|
|
23
25
|
}),
|
|
26
|
+
output: Schema.String.annotations({ description: 'The exchange rate between the two currencies' }),
|
|
27
|
+
});
|
|
24
28
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
export default ForexEffect.pipe(
|
|
30
|
+
Operation.withHandler(
|
|
31
|
+
Effect.fnUntraced(function* ({ from, to }) {
|
|
32
|
+
const res = yield* HttpClientRequest.get(`https://free.ratesdb.com/v1/rates?from=${from}&to=${to}`).pipe(
|
|
33
|
+
HttpClient.execute,
|
|
34
|
+
Effect.flatMap(
|
|
35
|
+
HttpClientResponse.schemaBodyJson(
|
|
36
|
+
Schema.Struct({
|
|
37
|
+
data: Schema.Struct({ rates: Schema.Record({ key: Schema.String, value: Schema.Number }) }),
|
|
38
|
+
}),
|
|
39
|
+
),
|
|
35
40
|
),
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
);
|
|
41
|
+
Effect.timeout('1 second'),
|
|
42
|
+
Effect.retry(Schedule.exponential(1_000).pipe(Schedule.compose(Schedule.recurs(3)))),
|
|
43
|
+
Effect.scoped,
|
|
44
|
+
);
|
|
41
45
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
return res.data.rates[to].toString();
|
|
47
|
+
}, Effect.provide(FetchHttpClient.layer)),
|
|
48
|
+
),
|
|
49
|
+
);
|
package/src/functions/query.ts
CHANGED
|
@@ -3,19 +3,17 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import * as Effect from 'effect/Effect';
|
|
6
|
-
import * as Schema from 'effect/Schema';
|
|
7
6
|
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
7
|
+
import { Operation } from '@dxos/compute';
|
|
8
|
+
import { Database, Filter, Query as EchoQuery } from '@dxos/echo';
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
});
|
|
10
|
+
import { QueryDb } from './definitions';
|
|
11
|
+
|
|
12
|
+
export default QueryDb.pipe(
|
|
13
|
+
Operation.withHandler(
|
|
14
|
+
Effect.fn(function* (data) {
|
|
15
|
+
const results = yield* Database.runQuery(EchoQuery.select(Filter.everything()));
|
|
16
|
+
return { count: results.length };
|
|
17
|
+
}),
|
|
18
|
+
),
|
|
19
|
+
);
|
package/src/functions/reply.ts
CHANGED
|
@@ -4,18 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
import * as Console from 'effect/Console';
|
|
6
6
|
import * as Effect from 'effect/Effect';
|
|
7
|
-
import * as Schema from 'effect/Schema';
|
|
8
7
|
|
|
9
|
-
import {
|
|
8
|
+
import { Operation } from '@dxos/compute';
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
});
|
|
10
|
+
import { Reply } from './definitions';
|
|
11
|
+
|
|
12
|
+
export default Reply.pipe(
|
|
13
|
+
Operation.withHandler(
|
|
14
|
+
Effect.fn(function* (data) {
|
|
15
|
+
yield* Console.log('reply', { data });
|
|
16
|
+
return data;
|
|
17
|
+
}),
|
|
18
|
+
),
|
|
19
|
+
);
|
package/src/functions/sleep.ts
CHANGED
|
@@ -3,22 +3,15 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import * as Effect from 'effect/Effect';
|
|
6
|
-
import * as Schema from 'effect/Schema';
|
|
7
6
|
|
|
8
|
-
import {
|
|
7
|
+
import { Operation } from '@dxos/compute';
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
description: 'Milliseconds to sleep',
|
|
17
|
-
default: 100_000,
|
|
9
|
+
import { Sleep } from './definitions';
|
|
10
|
+
|
|
11
|
+
export default Sleep.pipe(
|
|
12
|
+
Operation.withHandler(
|
|
13
|
+
Effect.fn(function* ({ duration = 100_000 }) {
|
|
14
|
+
yield* Effect.sleep(duration);
|
|
18
15
|
}),
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
handler: Effect.fn(function* ({ data: { duration = 100_000 } }) {
|
|
22
|
-
yield* Effect.sleep(duration);
|
|
23
|
-
}),
|
|
24
|
-
});
|
|
16
|
+
),
|
|
17
|
+
);
|
package/src/testing/util.ts
CHANGED
|
@@ -7,9 +7,9 @@ import fs from 'node:fs';
|
|
|
7
7
|
import { sleep } from '@dxos/async';
|
|
8
8
|
import { Client, type Config } from '@dxos/client';
|
|
9
9
|
import { type Space } from '@dxos/client/echo';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
10
|
+
import { Operation, Trigger } from '@dxos/compute';
|
|
11
|
+
import { Context } from '@dxos/context';
|
|
12
|
+
import { Feed, Obj } from '@dxos/echo';
|
|
13
13
|
import { InvocationTraceEndEvent, InvocationTraceStartEvent } from '@dxos/functions-runtime';
|
|
14
14
|
import { FunctionsServiceClient } from '@dxos/functions-runtime/edge';
|
|
15
15
|
import { bundleFunction } from '@dxos/functions-runtime/native';
|
|
@@ -27,7 +27,7 @@ export const writeBundle = (path: string, bundle: BundleResult) => {
|
|
|
27
27
|
export const setup = async (config: Config) => {
|
|
28
28
|
const client = await new Client({
|
|
29
29
|
config,
|
|
30
|
-
types: [
|
|
30
|
+
types: [Operation.PersistentOperation, Trigger.Trigger],
|
|
31
31
|
}).initialize();
|
|
32
32
|
await client.halo.createIdentity();
|
|
33
33
|
|
|
@@ -53,12 +53,12 @@ export const deployFunction = async (
|
|
|
53
53
|
functionsServiceClient: FunctionsServiceClient,
|
|
54
54
|
entryPoint: string,
|
|
55
55
|
runtime: FunctionRuntimeKind,
|
|
56
|
-
) => {
|
|
56
|
+
): Promise<Operation.PersistentOperation> => {
|
|
57
57
|
const artifact = await bundleFunction({
|
|
58
58
|
entryPoint,
|
|
59
59
|
verbose: true,
|
|
60
60
|
});
|
|
61
|
-
const func = await functionsServiceClient.deploy({
|
|
61
|
+
const func = await functionsServiceClient.deploy(Context.default(), {
|
|
62
62
|
version: '0.0.1',
|
|
63
63
|
ownerPublicKey: space.key,
|
|
64
64
|
entryPoint: artifact.entryPoint,
|
|
@@ -83,8 +83,11 @@ export const observeInvocations = async (space: Space, maxCount: number | null)
|
|
|
83
83
|
let count = 0;
|
|
84
84
|
while (true) {
|
|
85
85
|
try {
|
|
86
|
-
const
|
|
87
|
-
|
|
86
|
+
const traceFeed = space.properties.invocationTraceFeed?.target;
|
|
87
|
+
const traceFeedDXN = traceFeed ? Feed.getQueueUri(traceFeed) : undefined;
|
|
88
|
+
const invocations = traceFeedDXN
|
|
89
|
+
? ((await space.queues.get(traceFeedDXN, { subspaceTag: traceFeed!.namespace }).queryObjects()) ?? [])
|
|
90
|
+
: [];
|
|
88
91
|
|
|
89
92
|
for (const invocation of invocations) {
|
|
90
93
|
if (Obj.instanceOf(InvocationTraceStartEvent, invocation)) {
|