@dxos/functions-testing 0.8.4-main.1068cf700f
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 +8 -0
- package/README.md +21 -0
- package/dist/lib/node-esm/index.mjs +2 -0
- package/dist/lib/node-esm/index.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -0
- package/dist/lib/node-esm/testing/index.mjs +114 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/types/src/bundle.test.d.ts +2 -0
- package/dist/types/src/bundle.test.d.ts.map +1 -0
- package/dist/types/src/cpu-limit.test.d.ts +2 -0
- package/dist/types/src/cpu-limit.test.d.ts.map +1 -0
- package/dist/types/src/functions/fib.d.ts +7 -0
- package/dist/types/src/functions/fib.d.ts.map +1 -0
- package/dist/types/src/functions/forex-effect.d.ts +6 -0
- package/dist/types/src/functions/forex-effect.d.ts.map +1 -0
- package/dist/types/src/functions/query.d.ts +3 -0
- package/dist/types/src/functions/query.d.ts.map +1 -0
- package/dist/types/src/functions/reply.d.ts +3 -0
- package/dist/types/src/functions/reply.d.ts.map +1 -0
- package/dist/types/src/functions/sleep.d.ts +5 -0
- package/dist/types/src/functions/sleep.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +2 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/util.d.ts +16 -0
- package/dist/types/src/testing/util.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +56 -0
- package/src/bundle.test.ts +21 -0
- package/src/cpu-limit.test.ts +149 -0
- package/src/functions/fib.ts +32 -0
- package/src/functions/forex-effect.ts +44 -0
- package/src/functions/query.ts +21 -0
- package/src/functions/reply.ts +21 -0
- package/src/functions/sleep.ts +24 -0
- package/src/index.ts +5 -0
- package/src/testing/index.ts +5 -0
- package/src/testing/util.ts +129 -0
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dxos/functions-testing",
|
|
3
|
+
"version": "0.8.4-main.1068cf700f",
|
|
4
|
+
"description": "Functions tests",
|
|
5
|
+
"homepage": "https://dxos.org",
|
|
6
|
+
"bugs": "https://github.com/dxos/dxos/issues",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/dxos/dxos"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"author": "info@dxos.org",
|
|
13
|
+
"sideEffects": true,
|
|
14
|
+
"type": "module",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/types/src/index.d.ts",
|
|
18
|
+
"browser": "./dist/lib/browser/index.mjs",
|
|
19
|
+
"node": "./dist/lib/node-esm/index.mjs"
|
|
20
|
+
},
|
|
21
|
+
"./testing": {
|
|
22
|
+
"types": "./dist/types/src/testing/index.d.ts",
|
|
23
|
+
"browser": "./dist/lib/browser/testing/index.mjs",
|
|
24
|
+
"node": "./dist/lib/node-esm/testing/index.mjs"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
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
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"src"
|
|
38
|
+
],
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@effect/platform": "0.94.4",
|
|
41
|
+
"@dxos/async": "0.8.4-main.1068cf700f",
|
|
42
|
+
"@dxos/client": "0.8.4-main.1068cf700f",
|
|
43
|
+
"@dxos/config": "0.8.4-main.1068cf700f",
|
|
44
|
+
"@dxos/echo": "0.8.4-main.1068cf700f",
|
|
45
|
+
"@dxos/functions": "0.8.4-main.1068cf700f",
|
|
46
|
+
"@dxos/functions-runtime": "0.8.4-main.1068cf700f",
|
|
47
|
+
"@dxos/functions-runtime-cloudflare": "0.8.4-main.1068cf700f",
|
|
48
|
+
"@dxos/invariant": "0.8.4-main.1068cf700f",
|
|
49
|
+
"@dxos/log": "0.8.4-main.1068cf700f",
|
|
50
|
+
"@dxos/protocols": "0.8.4-main.1068cf700f"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
},
|
|
55
|
+
"beast": {}
|
|
56
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
|
|
7
|
+
import { describe, it } from 'vitest';
|
|
8
|
+
|
|
9
|
+
import { bundleFunction } from '@dxos/functions-runtime/native';
|
|
10
|
+
|
|
11
|
+
import { writeBundle } from './testing';
|
|
12
|
+
|
|
13
|
+
describe('bundle', () => {
|
|
14
|
+
it('should bundle', async () => {
|
|
15
|
+
const result = await bundleFunction({
|
|
16
|
+
entryPoint: new URL('./functions/reply.ts', import.meta.url).pathname,
|
|
17
|
+
});
|
|
18
|
+
writeBundle('./dist/test/bundle-reply', result);
|
|
19
|
+
console.log(join('dist/test/bundle-reply', result.entryPoint));
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { describe, test } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { configPreset } from '@dxos/config';
|
|
8
|
+
import { Obj, Ref } from '@dxos/echo';
|
|
9
|
+
import { Trigger } from '@dxos/functions';
|
|
10
|
+
import { FunctionRuntimeKind } from '@dxos/protocols';
|
|
11
|
+
|
|
12
|
+
import { deployFunction, observeInvocations, setup, sync } from './testing';
|
|
13
|
+
|
|
14
|
+
const FIB_FUNCTION_PATH = new URL('./functions/fib.ts', import.meta.url).pathname;
|
|
15
|
+
|
|
16
|
+
// Test suite to explore who CPU time limits influence function execution
|
|
17
|
+
describe.runIf(process.env.DX_TEST_TAGS?.includes('functions-e2e'))('CPU limit', () => {
|
|
18
|
+
const config = configPreset({ edge: 'dev' });
|
|
19
|
+
|
|
20
|
+
test('invoke directly', { timeout: 120_000 }, async ({ expect }) => {
|
|
21
|
+
const { client, space, functionsServiceClient } = await setup(config);
|
|
22
|
+
const func = await deployFunction(
|
|
23
|
+
space,
|
|
24
|
+
functionsServiceClient,
|
|
25
|
+
FIB_FUNCTION_PATH,
|
|
26
|
+
FunctionRuntimeKind.enums.WORKERS_FOR_PLATFORMS,
|
|
27
|
+
);
|
|
28
|
+
const result = await functionsServiceClient.invoke(
|
|
29
|
+
func,
|
|
30
|
+
{
|
|
31
|
+
iterations: 100,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
spaceId: space.id,
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
console.log(result);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('force-trigger', { timeout: 120_000 }, async ({ expect }) => {
|
|
41
|
+
const { client, space, functionsServiceClient } = await setup(config);
|
|
42
|
+
const func = await deployFunction(
|
|
43
|
+
space,
|
|
44
|
+
functionsServiceClient,
|
|
45
|
+
FIB_FUNCTION_PATH,
|
|
46
|
+
FunctionRuntimeKind.enums.WORKERS_FOR_PLATFORMS,
|
|
47
|
+
);
|
|
48
|
+
const trigger = space.db.add(
|
|
49
|
+
Obj.make(Trigger.Trigger, {
|
|
50
|
+
enabled: true,
|
|
51
|
+
function: Ref.make(func),
|
|
52
|
+
spec: { kind: 'timer', cron: '* */30 * * * *' },
|
|
53
|
+
input: { iterations: 100 },
|
|
54
|
+
}),
|
|
55
|
+
);
|
|
56
|
+
await sync(space);
|
|
57
|
+
const result = await functionsServiceClient.forceRunCronTrigger(space.id, trigger.id);
|
|
58
|
+
console.log(result);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('break CPU limit', { timeout: 120_000 }, async ({ expect }) => {
|
|
62
|
+
const { client, space, functionsServiceClient } = await setup(config);
|
|
63
|
+
const func = await deployFunction(
|
|
64
|
+
space,
|
|
65
|
+
functionsServiceClient,
|
|
66
|
+
FIB_FUNCTION_PATH,
|
|
67
|
+
FunctionRuntimeKind.enums.WORKERS_FOR_PLATFORMS,
|
|
68
|
+
);
|
|
69
|
+
const trigger = space.db.add(
|
|
70
|
+
Obj.make(Trigger.Trigger, {
|
|
71
|
+
enabled: true,
|
|
72
|
+
function: Ref.make(func),
|
|
73
|
+
spec: { kind: 'timer', cron: '* */30 * * * *' },
|
|
74
|
+
input: { iterations: 1_000_000_000 },
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
77
|
+
await sync(space);
|
|
78
|
+
{
|
|
79
|
+
const result = await functionsServiceClient.forceRunCronTrigger(space.id, trigger.id);
|
|
80
|
+
console.log(result);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
{
|
|
84
|
+
const result = await functionsServiceClient.forceRunCronTrigger(space.id, trigger.id);
|
|
85
|
+
console.log(result);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
{
|
|
89
|
+
Obj.change(trigger, (t) => {
|
|
90
|
+
t.input!.iterations = 100;
|
|
91
|
+
});
|
|
92
|
+
await sync(space);
|
|
93
|
+
const result = await functionsServiceClient.forceRunCronTrigger(space.id, trigger.id);
|
|
94
|
+
console.log(result);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('observe invocations', { timeout: 520_000 }, async ({ expect }) => {
|
|
99
|
+
const { client, space, functionsServiceClient } = await setup(config);
|
|
100
|
+
const func = await deployFunction(
|
|
101
|
+
space,
|
|
102
|
+
functionsServiceClient,
|
|
103
|
+
FIB_FUNCTION_PATH,
|
|
104
|
+
FunctionRuntimeKind.enums.WORKERS_FOR_PLATFORMS,
|
|
105
|
+
);
|
|
106
|
+
const trigger = space.db.add(
|
|
107
|
+
Obj.make(Trigger.Trigger, {
|
|
108
|
+
enabled: true,
|
|
109
|
+
function: Ref.make(func),
|
|
110
|
+
spec: { kind: 'timer', cron: '* * * * * *' },
|
|
111
|
+
input: { iterations: 1_000_000 },
|
|
112
|
+
}),
|
|
113
|
+
);
|
|
114
|
+
await sync(space);
|
|
115
|
+
await observeInvocations(space, 100);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test.only('break CPU limit through natural exection', { timeout: 520_000 }, async ({ expect }) => {
|
|
119
|
+
const { client, space, functionsServiceClient } = await setup(config);
|
|
120
|
+
const func = await deployFunction(
|
|
121
|
+
space,
|
|
122
|
+
functionsServiceClient,
|
|
123
|
+
FIB_FUNCTION_PATH,
|
|
124
|
+
FunctionRuntimeKind.enums.WORKERS_FOR_PLATFORMS,
|
|
125
|
+
);
|
|
126
|
+
const trigger = space.db.add(
|
|
127
|
+
Obj.make(Trigger.Trigger, {
|
|
128
|
+
enabled: true,
|
|
129
|
+
function: Ref.make(func),
|
|
130
|
+
spec: { kind: 'timer', cron: '* * * * * *' },
|
|
131
|
+
input: { iterations: 100 },
|
|
132
|
+
}),
|
|
133
|
+
);
|
|
134
|
+
await sync(space);
|
|
135
|
+
await observeInvocations(space, 5);
|
|
136
|
+
|
|
137
|
+
Obj.change(trigger, (t) => {
|
|
138
|
+
t.input!.iterations = 1_000_000_000;
|
|
139
|
+
});
|
|
140
|
+
await sync(space);
|
|
141
|
+
await observeInvocations(space, 10);
|
|
142
|
+
|
|
143
|
+
Obj.change(trigger, (t) => {
|
|
144
|
+
t.input!.iterations = 100;
|
|
145
|
+
});
|
|
146
|
+
await sync(space);
|
|
147
|
+
await observeInvocations(space, 1_000);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
7
|
+
|
|
8
|
+
import { defineFunction } from '@dxos/functions';
|
|
9
|
+
|
|
10
|
+
export default defineFunction({
|
|
11
|
+
key: 'example.org/function/fib',
|
|
12
|
+
name: 'Fibonacci',
|
|
13
|
+
description: 'Function that calculates a Fibonacci number',
|
|
14
|
+
inputSchema: Schema.Struct({
|
|
15
|
+
iterations: Schema.optional(Schema.Number).annotations({
|
|
16
|
+
description: 'Number of iterations',
|
|
17
|
+
default: 100_000,
|
|
18
|
+
}),
|
|
19
|
+
}),
|
|
20
|
+
outputSchema: Schema.Struct({
|
|
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
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as FetchHttpClient from '@effect/platform/FetchHttpClient';
|
|
6
|
+
import * as HttpClient from '@effect/platform/HttpClient';
|
|
7
|
+
import * as HttpClientRequest from '@effect/platform/HttpClientRequest';
|
|
8
|
+
import * as HttpClientResponse from '@effect/platform/HttpClientResponse';
|
|
9
|
+
import * as Effect from 'effect/Effect';
|
|
10
|
+
import * as Schedule from 'effect/Schedule';
|
|
11
|
+
import * as Schema from 'effect/Schema';
|
|
12
|
+
|
|
13
|
+
import { defineFunction } from '@dxos/functions';
|
|
14
|
+
|
|
15
|
+
export default defineFunction({
|
|
16
|
+
key: 'dxos.org/script/forex-effect',
|
|
17
|
+
name: 'Forex Effect',
|
|
18
|
+
description: 'Returns the exchange rate between two currencies.',
|
|
19
|
+
|
|
20
|
+
inputSchema: Schema.Struct({
|
|
21
|
+
from: Schema.String.annotations({ description: 'The source currency' }),
|
|
22
|
+
to: Schema.String.annotations({ description: 'The target currency' }),
|
|
23
|
+
}),
|
|
24
|
+
|
|
25
|
+
outputSchema: Schema.String.annotations({ description: 'The exchange rate between the two currencies' }),
|
|
26
|
+
|
|
27
|
+
handler: Effect.fnUntraced(function* ({ data: { from, to } }: any) {
|
|
28
|
+
const res = yield* HttpClientRequest.get(`https://free.ratesdb.com/v1/rates?from=${from}&to=${to}`).pipe(
|
|
29
|
+
HttpClient.execute,
|
|
30
|
+
Effect.flatMap(
|
|
31
|
+
HttpClientResponse.schemaBodyJson(
|
|
32
|
+
Schema.Struct({
|
|
33
|
+
data: Schema.Struct({ rates: Schema.Record({ key: Schema.String, value: Schema.Number }) }),
|
|
34
|
+
}),
|
|
35
|
+
),
|
|
36
|
+
),
|
|
37
|
+
Effect.timeout('1 second'),
|
|
38
|
+
Effect.retry(Schedule.exponential(1_000).pipe(Schedule.compose(Schedule.recurs(3)))),
|
|
39
|
+
Effect.scoped,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
return res.data.rates[to].toString();
|
|
43
|
+
}, Effect.provide(FetchHttpClient.layer)),
|
|
44
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
7
|
+
|
|
8
|
+
import { Database, Filter, Query } from '@dxos/echo';
|
|
9
|
+
import { defineFunction } from '@dxos/functions';
|
|
10
|
+
|
|
11
|
+
export default defineFunction({
|
|
12
|
+
key: 'example.org/function/query',
|
|
13
|
+
name: 'Query',
|
|
14
|
+
description: 'Queries the database',
|
|
15
|
+
inputSchema: Schema.Any,
|
|
16
|
+
outputSchema: Schema.Any,
|
|
17
|
+
handler: Effect.fn(function* ({ data }) {
|
|
18
|
+
const results = yield* Database.runQuery(Query.select(Filter.everything()));
|
|
19
|
+
return { count: results.length };
|
|
20
|
+
}),
|
|
21
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Console from 'effect/Console';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
import * as Schema from 'effect/Schema';
|
|
8
|
+
|
|
9
|
+
import { defineFunction } from '@dxos/functions';
|
|
10
|
+
|
|
11
|
+
export default defineFunction({
|
|
12
|
+
key: 'example.org/function/reply',
|
|
13
|
+
name: 'Reply',
|
|
14
|
+
description: 'Function that echoes the input',
|
|
15
|
+
inputSchema: Schema.Any,
|
|
16
|
+
outputSchema: Schema.Any,
|
|
17
|
+
handler: Effect.fn(function* ({ data }) {
|
|
18
|
+
yield* Console.log('reply', { data });
|
|
19
|
+
return data;
|
|
20
|
+
}),
|
|
21
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
7
|
+
|
|
8
|
+
import { defineFunction } from '@dxos/functions';
|
|
9
|
+
|
|
10
|
+
export default defineFunction({
|
|
11
|
+
key: 'example.org/function/sleep',
|
|
12
|
+
name: 'Sleep',
|
|
13
|
+
description: 'Function that sleeps for a given amount of time',
|
|
14
|
+
inputSchema: Schema.Struct({
|
|
15
|
+
duration: Schema.optional(Schema.Number).annotations({
|
|
16
|
+
description: 'Milliseconds to sleep',
|
|
17
|
+
default: 100_000,
|
|
18
|
+
}),
|
|
19
|
+
}),
|
|
20
|
+
outputSchema: Schema.Void,
|
|
21
|
+
handler: Effect.fn(function* ({ data: { duration = 100_000 } }) {
|
|
22
|
+
yield* Effect.sleep(duration);
|
|
23
|
+
}),
|
|
24
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
|
|
7
|
+
import { sleep } from '@dxos/async';
|
|
8
|
+
import { Client, type Config } from '@dxos/client';
|
|
9
|
+
import { type Space } from '@dxos/client/echo';
|
|
10
|
+
import { Filter, Obj, Query } from '@dxos/echo';
|
|
11
|
+
import { Function } from '@dxos/functions';
|
|
12
|
+
import { Trigger } from '@dxos/functions';
|
|
13
|
+
import { InvocationTraceEndEvent, InvocationTraceStartEvent } from '@dxos/functions-runtime';
|
|
14
|
+
import { FunctionsServiceClient } from '@dxos/functions-runtime/edge';
|
|
15
|
+
import { bundleFunction } from '@dxos/functions-runtime/native';
|
|
16
|
+
import type { BundleResult } from '@dxos/functions-runtime/native';
|
|
17
|
+
import { ErrorCodec, type FunctionRuntimeKind } from '@dxos/protocols';
|
|
18
|
+
import { EdgeReplicationSetting } from '@dxos/protocols/proto/dxos/echo/metadata';
|
|
19
|
+
|
|
20
|
+
export const writeBundle = (path: string, bundle: BundleResult) => {
|
|
21
|
+
fs.mkdirSync(path, { recursive: true });
|
|
22
|
+
Object.entries(bundle.assets).forEach(([name, content]) => {
|
|
23
|
+
fs.writeFileSync(path + '/' + name, content);
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const setup = async (config: Config) => {
|
|
28
|
+
const client = await new Client({
|
|
29
|
+
config,
|
|
30
|
+
types: [Function.Function, Trigger.Trigger],
|
|
31
|
+
}).initialize();
|
|
32
|
+
await client.halo.createIdentity();
|
|
33
|
+
|
|
34
|
+
const space = await client.spaces.create();
|
|
35
|
+
await space.waitUntilReady();
|
|
36
|
+
await space.internal.setEdgeReplicationPreference(EdgeReplicationSetting.ENABLED);
|
|
37
|
+
|
|
38
|
+
const functionsServiceClient = FunctionsServiceClient.fromClient(client);
|
|
39
|
+
|
|
40
|
+
return { client, space, functionsServiceClient };
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const sync = async (space: Space) => {
|
|
44
|
+
await space.db.flush({ indexes: true });
|
|
45
|
+
await space.internal.syncToEdge({
|
|
46
|
+
onProgress: (state) =>
|
|
47
|
+
console.log(state ? `${state.unsyncedDocumentCount} documents syncing...` : 'connecting to edge...'),
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const deployFunction = async (
|
|
52
|
+
space: Space,
|
|
53
|
+
functionsServiceClient: FunctionsServiceClient,
|
|
54
|
+
entryPoint: string,
|
|
55
|
+
runtime: FunctionRuntimeKind,
|
|
56
|
+
) => {
|
|
57
|
+
const artifact = await bundleFunction({
|
|
58
|
+
entryPoint,
|
|
59
|
+
verbose: true,
|
|
60
|
+
});
|
|
61
|
+
const func = await functionsServiceClient.deploy({
|
|
62
|
+
version: '0.0.1',
|
|
63
|
+
ownerPublicKey: space.key,
|
|
64
|
+
entryPoint: artifact.entryPoint,
|
|
65
|
+
assets: artifact.assets,
|
|
66
|
+
runtime,
|
|
67
|
+
});
|
|
68
|
+
space.db.add(func);
|
|
69
|
+
|
|
70
|
+
return func;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const observeInvocations = async (space: Space, maxCount: number | null) => {
|
|
74
|
+
let initialCount = null;
|
|
75
|
+
const invocationData = new Map<
|
|
76
|
+
string,
|
|
77
|
+
{
|
|
78
|
+
count: number;
|
|
79
|
+
begin: InvocationTraceStartEvent;
|
|
80
|
+
end?: InvocationTraceEndEvent;
|
|
81
|
+
}
|
|
82
|
+
>();
|
|
83
|
+
let count = 0;
|
|
84
|
+
while (true) {
|
|
85
|
+
try {
|
|
86
|
+
const invocations =
|
|
87
|
+
(await space.properties.invocationTraceQueue?.target!.query(Query.select(Filter.everything())).run()) ?? [];
|
|
88
|
+
|
|
89
|
+
for (const invocation of invocations) {
|
|
90
|
+
if (Obj.instanceOf(InvocationTraceStartEvent, invocation)) {
|
|
91
|
+
if (invocationData.has(invocation.invocationId)) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
invocationData.set(invocation.invocationId, {
|
|
95
|
+
count: count++,
|
|
96
|
+
begin: invocation,
|
|
97
|
+
});
|
|
98
|
+
console.log(
|
|
99
|
+
`${count.toString().padStart(3, ' ')}: BEGIN ${invocation.runtime ?? '(unknown runtime)'} ${JSON.stringify(invocation.input)}`,
|
|
100
|
+
);
|
|
101
|
+
} else if (Obj.instanceOf(InvocationTraceEndEvent, invocation)) {
|
|
102
|
+
const data = invocationData.get(invocation.invocationId);
|
|
103
|
+
if (!data || !!data.end) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
data.end = invocation;
|
|
107
|
+
|
|
108
|
+
const outcome = data.end.outcome;
|
|
109
|
+
console.log(
|
|
110
|
+
`${data.count.toString().padStart(3, ' ')}: END outcome=${outcome} duration=${data.end.timestamp - data.begin.timestamp}`,
|
|
111
|
+
);
|
|
112
|
+
if (outcome === 'failure' && data.end.error) {
|
|
113
|
+
console.error(ErrorCodec.decode(data.end.error));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (initialCount === null) {
|
|
118
|
+
initialCount = invocations.length;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (maxCount !== null && invocationData.size >= maxCount + initialCount) {
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
} catch (err) {
|
|
125
|
+
console.error(err);
|
|
126
|
+
}
|
|
127
|
+
await sleep(1_000);
|
|
128
|
+
}
|
|
129
|
+
};
|