@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.
Files changed (106) hide show
  1. package/dist/lib/browser/bundler/index.mjs +10 -6
  2. package/dist/lib/browser/bundler/index.mjs.map +3 -3
  3. package/dist/lib/browser/{chunk-D2XO7XXY.mjs → chunk-LKYT2SAL.mjs} +176 -122
  4. package/dist/lib/browser/chunk-LKYT2SAL.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +319 -199
  6. package/dist/lib/browser/index.mjs.map +4 -4
  7. package/dist/lib/browser/meta.json +1 -1
  8. package/dist/lib/browser/testing/index.mjs +11 -9
  9. package/dist/lib/browser/testing/index.mjs.map +3 -3
  10. package/dist/lib/node-esm/bundler/index.mjs +10 -6
  11. package/dist/lib/node-esm/bundler/index.mjs.map +3 -3
  12. package/dist/lib/node-esm/{chunk-Z4CJ62WS.mjs → chunk-NAQIKLZB.mjs} +176 -122
  13. package/dist/lib/node-esm/chunk-NAQIKLZB.mjs.map +7 -0
  14. package/dist/lib/node-esm/index.mjs +319 -199
  15. package/dist/lib/node-esm/index.mjs.map +4 -4
  16. package/dist/lib/node-esm/meta.json +1 -1
  17. package/dist/lib/node-esm/testing/index.mjs +11 -9
  18. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  19. package/dist/types/src/bundler/bundler.d.ts.map +1 -1
  20. package/dist/types/src/errors.d.ts +16 -16
  21. package/dist/types/src/examples/fib.d.ts.map +1 -1
  22. package/dist/types/src/examples/reply.d.ts.map +1 -1
  23. package/dist/types/src/examples/sleep.d.ts.map +1 -1
  24. package/dist/types/src/executor/executor.d.ts +3 -0
  25. package/dist/types/src/executor/executor.d.ts.map +1 -1
  26. package/dist/types/src/handler.d.ts +19 -4
  27. package/dist/types/src/handler.d.ts.map +1 -1
  28. package/dist/types/src/schema.d.ts +5 -5
  29. package/dist/types/src/schema.d.ts.map +1 -1
  30. package/dist/types/src/services/credentials.d.ts +6 -2
  31. package/dist/types/src/services/credentials.d.ts.map +1 -1
  32. package/dist/types/src/services/database.d.ts +6 -2
  33. package/dist/types/src/services/database.d.ts.map +1 -1
  34. package/dist/types/src/services/event-logger.d.ts +4 -1
  35. package/dist/types/src/services/event-logger.d.ts.map +1 -1
  36. package/dist/types/src/services/function-invocation-service.d.ts +28 -0
  37. package/dist/types/src/services/function-invocation-service.d.ts.map +1 -0
  38. package/dist/types/src/services/function-invocation-service.test.d.ts +2 -0
  39. package/dist/types/src/services/function-invocation-service.test.d.ts.map +1 -0
  40. package/dist/types/src/services/index.d.ts +4 -3
  41. package/dist/types/src/services/index.d.ts.map +1 -1
  42. package/dist/types/src/services/local-function-execution.d.ts +18 -9
  43. package/dist/types/src/services/local-function-execution.d.ts.map +1 -1
  44. package/dist/types/src/services/queues.d.ts +3 -1
  45. package/dist/types/src/services/queues.d.ts.map +1 -1
  46. package/dist/types/src/services/remote-function-execution-service.d.ts +11 -4
  47. package/dist/types/src/services/remote-function-execution-service.d.ts.map +1 -1
  48. package/dist/types/src/services/service-container.d.ts +2 -1
  49. package/dist/types/src/services/service-container.d.ts.map +1 -1
  50. package/dist/types/src/services/service-registry.d.ts +3 -1
  51. package/dist/types/src/services/service-registry.d.ts.map +1 -1
  52. package/dist/types/src/services/tracing.d.ts +5 -3
  53. package/dist/types/src/services/tracing.d.ts.map +1 -1
  54. package/dist/types/src/testing/layer.d.ts +6 -3
  55. package/dist/types/src/testing/layer.d.ts.map +1 -1
  56. package/dist/types/src/testing/logger.d.ts +1 -1
  57. package/dist/types/src/testing/logger.d.ts.map +1 -1
  58. package/dist/types/src/testing/services.d.ts +1 -1
  59. package/dist/types/src/testing/services.d.ts.map +1 -1
  60. package/dist/types/src/trace.d.ts +1 -1
  61. package/dist/types/src/trace.d.ts.map +1 -1
  62. package/dist/types/src/triggers/invocation-tracer.d.ts +3 -1
  63. package/dist/types/src/triggers/invocation-tracer.d.ts.map +1 -1
  64. package/dist/types/src/triggers/trigger-dispatcher.d.ts +7 -4
  65. package/dist/types/src/triggers/trigger-dispatcher.d.ts.map +1 -1
  66. package/dist/types/src/triggers/trigger-state-store.d.ts +5 -4
  67. package/dist/types/src/triggers/trigger-state-store.d.ts.map +1 -1
  68. package/dist/types/src/types.d.ts +49 -30
  69. package/dist/types/src/types.d.ts.map +1 -1
  70. package/dist/types/src/url.d.ts +1 -1
  71. package/dist/types/src/url.d.ts.map +1 -1
  72. package/dist/types/tsconfig.tsbuildinfo +1 -1
  73. package/package.json +27 -36
  74. package/src/bundler/bundler.ts +7 -3
  75. package/src/examples/fib.ts +4 -2
  76. package/src/examples/reply.ts +5 -2
  77. package/src/examples/sleep.ts +4 -2
  78. package/src/executor/executor.ts +5 -1
  79. package/src/handler.ts +33 -9
  80. package/src/schema.ts +5 -2
  81. package/src/services/credentials.ts +9 -2
  82. package/src/services/database.ts +6 -2
  83. package/src/services/event-logger.ts +4 -1
  84. package/src/services/function-invocation-service.test.ts +81 -0
  85. package/src/services/function-invocation-service.ts +84 -0
  86. package/src/services/index.ts +4 -3
  87. package/src/services/local-function-execution.ts +65 -26
  88. package/src/services/queues.ts +3 -1
  89. package/src/services/remote-function-execution-service.ts +38 -21
  90. package/src/services/service-container.ts +2 -1
  91. package/src/services/service-registry.test.ts +4 -1
  92. package/src/services/service-registry.ts +7 -3
  93. package/src/services/tracing.ts +17 -14
  94. package/src/testing/layer.ts +10 -7
  95. package/src/testing/logger.ts +2 -1
  96. package/src/testing/persist-database.test.ts +4 -4
  97. package/src/testing/services.ts +1 -1
  98. package/src/trace.ts +2 -2
  99. package/src/triggers/invocation-tracer.ts +3 -1
  100. package/src/triggers/trigger-dispatcher.test.ts +29 -16
  101. package/src/triggers/trigger-dispatcher.ts +29 -8
  102. package/src/triggers/trigger-state-store.ts +6 -5
  103. package/src/types.ts +30 -12
  104. package/src/url.ts +1 -1
  105. package/dist/lib/browser/chunk-D2XO7XXY.mjs.map +0 -7
  106. package/dist/lib/node-esm/chunk-Z4CJ62WS.mjs.map +0 -7
@@ -2,40 +2,67 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Context, Effect, Layer, Schema } from 'effect';
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
 
10
+ import { AiService } from '@dxos/ai';
7
11
  import { todo } from '@dxos/debug';
12
+ import { log } from '@dxos/log';
8
13
 
9
14
  import { FunctionError, FunctionNotFoundError } from '../errors';
10
15
  import type { FunctionContext, FunctionDefinition } from '../handler';
11
16
 
12
- import type { Services } from './service-container';
17
+ import { CredentialsService } from './credentials';
18
+ import { DatabaseService } from './database';
19
+ import { type ComputeEventLogger } from './event-logger';
20
+ import { QueueService } from './queues';
21
+ import { RemoteFunctionExecutionService } from './remote-function-execution-service';
22
+ import { type Services } from './service-container';
23
+ import { type TracingService } from './tracing';
24
+
25
+ /**
26
+ * Services that are provided at the function call site.
27
+ */
28
+ export type InvocationServices = TracingService | ComputeEventLogger;
13
29
 
14
30
  export class LocalFunctionExecutionService extends Context.Tag('@dxos/functions/LocalFunctionExecutionService')<
15
31
  LocalFunctionExecutionService,
16
32
  {
17
33
  // TODO(dmaretskyi): This should take function id instead of the definition object.
18
34
  // TODO(dmaretskyi): Services should be satisfied from environment rather then bubbled up.
19
- invokeFunction(functionDef: FunctionDefinition<any, any>, input: unknown): Effect.Effect<unknown, never, Services>;
35
+ invokeFunction<I, O>(functionDef: FunctionDefinition<I, O>, input: I): Effect.Effect<O, never, InvocationServices>;
20
36
  }
21
37
  >() {
22
- /**
23
- * @deprecated Use layerLive instead.
24
- */
25
- static layer = Layer.succeed(LocalFunctionExecutionService, {
26
- invokeFunction: (functionDef, input) => invokeFunction(functionDef, input),
27
- });
28
-
29
38
  static layerLive = Layer.effect(
30
39
  LocalFunctionExecutionService,
31
40
  Effect.gen(function* () {
41
+ // TODO(dmaretskyi): Use `yield* Effect.context()`;
32
42
  const resolver = yield* FunctionImplementationResolver;
43
+ const ai = yield* AiService.AiService;
44
+ const credentials = yield* CredentialsService;
45
+ const database = yield* DatabaseService;
46
+ const queues = yield* QueueService;
47
+ // TODO(mykola): Delete, should not be required for local execution.
48
+ const functionCallService = yield* RemoteFunctionExecutionService;
33
49
  return {
34
- invokeFunction: Effect.fn('invokeFunction')(function* (functionDef, input) {
35
- // TODO(dmaretskyi): Better error types
36
- const resolved = yield* resolver.resolveFunctionImplementation(functionDef).pipe(Effect.orDie);
37
- return yield* invokeFunction(resolved, input);
38
- }),
50
+ // TODO(dmaretskyi): Better error types.
51
+ invokeFunction: <I, O>(
52
+ functionDef: FunctionDefinition<I, O>,
53
+ input: I,
54
+ ): Effect.Effect<O, never, InvocationServices> =>
55
+ Effect.gen(function* () {
56
+ const resolved = yield* resolver.resolveFunctionImplementation(functionDef).pipe(Effect.orDie);
57
+ const output = yield* invokeFunction(resolved, input);
58
+ return output as O;
59
+ }).pipe(
60
+ Effect.provideService(AiService.AiService, ai),
61
+ Effect.provideService(CredentialsService, credentials),
62
+ Effect.provideService(DatabaseService, database),
63
+ Effect.provideService(QueueService, queues),
64
+ Effect.provideService(RemoteFunctionExecutionService, functionCallService),
65
+ ),
39
66
  };
40
67
  }),
41
68
  );
@@ -52,9 +79,13 @@ const invokeFunction = (
52
79
  input: any,
53
80
  ): Effect.Effect<unknown, never, Services> =>
54
81
  Effect.gen(function* () {
55
- // Assert input matches schema
56
- const assertInput = functionDef.inputSchema.pipe(Schema.asserts);
57
- (assertInput as any)(input);
82
+ // Assert input matches schema.
83
+ try {
84
+ const assertInput = functionDef.inputSchema.pipe(Schema.asserts);
85
+ (assertInput as any)(input);
86
+ } catch (e) {
87
+ throw new FunctionError({ message: 'Invalid function input', context: { name: functionDef.name }, cause: e });
88
+ }
58
89
 
59
90
  const context: FunctionContext = {
60
91
  space: undefined,
@@ -64,6 +95,8 @@ const invokeFunction = (
64
95
  },
65
96
  };
66
97
 
98
+ log.info('invoking function', { name: functionDef.name, input });
99
+
67
100
  // TODO(dmaretskyi): This should be delegated to a function invoker service.
68
101
  const data = yield* Effect.gen(function* () {
69
102
  const result = functionDef.handler({ context, data: input });
@@ -86,9 +119,15 @@ const invokeFunction = (
86
119
  ),
87
120
  );
88
121
 
89
- // Assert output matches schema
90
- const assertOutput = functionDef.outputSchema?.pipe(Schema.asserts);
91
- (assertOutput as any)(data);
122
+ log.info('completed', { function: functionDef.name, input, data });
123
+
124
+ // Assert output matches schema.
125
+ try {
126
+ const assertOutput = functionDef.outputSchema?.pipe(Schema.asserts);
127
+ (assertOutput as any)(data);
128
+ } catch (e) {
129
+ throw new FunctionError({ message: 'Invalid function output', context: { name: functionDef.name }, cause: e });
130
+ }
92
131
 
93
132
  return data;
94
133
  }).pipe(Effect.withSpan('invokeFunction', { attributes: { name: functionDef.name } }));
@@ -96,15 +135,15 @@ const invokeFunction = (
96
135
  export class FunctionImplementationResolver extends Context.Tag('@dxos/functions/FunctionImplementationResolver')<
97
136
  FunctionImplementationResolver,
98
137
  {
99
- resolveFunctionImplementation(
100
- functionDef: FunctionDefinition<any, any>,
101
- ): Effect.Effect<FunctionDefinition<any, any>, FunctionNotFoundError>;
138
+ resolveFunctionImplementation<I, O>(
139
+ functionDef: FunctionDefinition<I, O>,
140
+ ): Effect.Effect<FunctionDefinition<I, O>, FunctionNotFoundError>;
102
141
  }
103
142
  >() {
104
143
  static layerTest = ({ functions }: { functions: FunctionDefinition<any, any>[] }) =>
105
144
  Layer.succeed(FunctionImplementationResolver, {
106
- resolveFunctionImplementation: (functionDef) => {
107
- const resolved = functions.find((f) => f.name === functionDef.name);
145
+ resolveFunctionImplementation: <I, O>(functionDef: FunctionDefinition<I, O>) => {
146
+ const resolved = functions.find((f) => f.key === functionDef.key);
108
147
  if (!resolved) {
109
148
  return Effect.fail(new FunctionNotFoundError(functionDef.name));
110
149
  }
@@ -2,7 +2,9 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Context, Effect, Layer } from 'effect';
5
+ import * as Context from 'effect/Context';
6
+ import * as Effect from 'effect/Effect';
7
+ import * as Layer from 'effect/Layer';
6
8
 
7
9
  import type { Obj, Relation } from '@dxos/echo';
8
10
  import type { Queue, QueueAPI, QueueFactory } from '@dxos/echo-db';
@@ -2,10 +2,13 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Context, Layer } from 'effect';
5
+ import * as Context from 'effect/Context';
6
+ import * as Effect from 'effect/Effect';
7
+ import * as Layer from 'effect/Layer';
6
8
 
7
9
  import type { SpaceId } from '@dxos/keys';
8
10
 
11
+ import { FunctionError } from '../errors';
9
12
  import { getInvocationUrl } from '../url';
10
13
 
11
14
  /**
@@ -14,33 +17,47 @@ import { getInvocationUrl } from '../url';
14
17
  export class RemoteFunctionExecutionService extends Context.Tag('@dxos/functions/RemoteFunctionExecutionService')<
15
18
  RemoteFunctionExecutionService,
16
19
  {
17
- callFunction(deployedFunctionId: string, input: any, spaceId?: SpaceId): Promise<any>;
20
+ callFunction<I, O>(deployedFunctionId: string, input: I): Effect.Effect<O>;
18
21
  }
19
22
  >() {
20
- static fromClient(baseUrl: string, spaceId: SpaceId): Context.Tag.Service<RemoteFunctionExecutionService> {
21
- return {
22
- callFunction: async (deployedFunctionId: string, input: any) => {
23
- const url = getInvocationUrl(deployedFunctionId, baseUrl, { spaceId });
24
- const result = await fetch(url, {
25
- method: 'POST',
26
- headers: { 'Content-Type': 'application/json' },
27
- body: JSON.stringify(input),
28
- });
29
- if (result.status >= 300 || result.status < 200) {
30
- throw new Error('Failed to invoke function', { cause: new Error(`HTTP error: ${await result.text()}`) });
31
- }
32
- return await result.json();
33
- },
34
- };
23
+ /**
24
+ * @param baseUrl URL of the EDGE server.
25
+ * @param spaceId - The space ID to invoke the function in. If not provided, the function will be without space context.
26
+ * @returns
27
+ */
28
+ static fromClient(baseUrl: string, spaceId?: SpaceId): Layer.Layer<RemoteFunctionExecutionService> {
29
+ return Layer.succeed(RemoteFunctionExecutionService, {
30
+ callFunction: <I, O>(deployedFunctionId: string, input: I): Effect.Effect<O> =>
31
+ Effect.gen(function* () {
32
+ const url = getInvocationUrl(deployedFunctionId, baseUrl, { spaceId });
33
+ const result = yield* Effect.promise(() =>
34
+ fetch(url, {
35
+ method: 'POST',
36
+ headers: { 'Content-Type': 'application/json' },
37
+ body: JSON.stringify(input),
38
+ }),
39
+ );
40
+ if (result.status >= 300 || result.status < 200) {
41
+ const text = yield* Effect.promise(() => result.text());
42
+ return yield* Effect.die(
43
+ new FunctionError({
44
+ message: 'Failed to invoke function',
45
+ cause: new Error(`HTTP error: ${text}`),
46
+ }),
47
+ );
48
+ }
49
+ const data = (yield* Effect.promise(() => result.json())) as O;
50
+ return data;
51
+ }),
52
+ });
35
53
  }
36
54
 
37
55
  static mock = (): Context.Tag.Service<RemoteFunctionExecutionService> => {
38
56
  return {
39
- callFunction: async (deployedFunctionId: string, input: any) => {
40
- return input;
41
- },
57
+ callFunction: <I, O>(deployedFunctionId: string, input: I): Effect.Effect<O> =>
58
+ Effect.succeed(input as unknown as O),
42
59
  };
43
60
  };
44
61
 
45
- static mockLayer = Layer.succeed(RemoteFunctionExecutionService, RemoteFunctionExecutionService.mock());
62
+ static layerMock = Layer.succeed(RemoteFunctionExecutionService, RemoteFunctionExecutionService.mock());
46
63
  }
@@ -2,7 +2,8 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { type Context, Layer } from 'effect';
5
+ import type * as Context from 'effect/Context';
6
+ import * as Layer from 'effect/Layer';
6
7
 
7
8
  import { AiService } from '@dxos/ai';
8
9
  import { entries } from '@dxos/util';
@@ -3,7 +3,10 @@
3
3
  //
4
4
 
5
5
  import { describe, it } from '@effect/vitest';
6
- import { Context, Effect, Layer, Option } from 'effect';
6
+ import * as Context from 'effect/Context';
7
+ import * as Effect from 'effect/Effect';
8
+ import * as Layer from 'effect/Layer';
9
+ import * as Option from 'effect/Option';
7
10
 
8
11
  import { ServiceRegistry } from './service-registry';
9
12
 
@@ -2,7 +2,10 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Context, Effect, type Option, flow } from 'effect';
5
+ import * as Context from 'effect/Context';
6
+ import * as Effect from 'effect/Effect';
7
+ import * as Function from 'effect/Function';
8
+ import type * as Option from 'effect/Option';
6
9
 
7
10
  import { ServiceNotAvailableError } from '../errors';
8
11
 
@@ -40,7 +43,8 @@ export class ServiceRegistry extends Context.Tag('@dxos/functions/ServiceRegistr
40
43
  E | ServiceNotAvailableError,
41
44
  Exclude<R, { [K in keyof Tags]: Context.Tag.Identifier<Tags[K]> }[number]> | ServiceRegistry
42
45
  >;
43
- } = (...tags) => (flow as any)(...tags.map((tag) => Effect.provideServiceEffect(tag, ServiceRegistry.resolve(tag))));
46
+ } = (...tags) =>
47
+ (Function.flow as any)(...tags.map((tag) => Effect.provideServiceEffect(tag, ServiceRegistry.resolve(tag))));
44
48
 
45
49
  static provideOrDie: {
46
50
  <Tags extends [Context.Tag<any, any>, ...Context.Tag<any, any>[]]>(
@@ -53,7 +57,7 @@ export class ServiceRegistry extends Context.Tag('@dxos/functions/ServiceRegistr
53
57
  Exclude<R, { [K in keyof Tags]: Context.Tag.Identifier<Tags[K]> }[number]> | ServiceRegistry
54
58
  >;
55
59
  } = (...tags) =>
56
- (flow as any)(
60
+ (Function.flow as any)(
57
61
  ...tags.map((tag) => Effect.provideServiceEffect(tag, ServiceRegistry.resolve(tag).pipe(Effect.orDie))),
58
62
  );
59
63
  }
@@ -2,12 +2,14 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Context, Effect, Layer } from 'effect';
5
+ import * as Context from 'effect/Context';
6
+ import * as Effect from 'effect/Effect';
7
+ import * as Layer from 'effect/Layer';
6
8
 
7
9
  import { AgentStatus } from '@dxos/ai';
8
10
  import { Obj } from '@dxos/echo';
11
+ import type { ObjectId } from '@dxos/echo/internal';
9
12
  import type { Queue } from '@dxos/echo-db';
10
- import type { ObjectId } from '@dxos/echo-schema';
11
13
  import { log } from '@dxos/log';
12
14
  import { DataType } from '@dxos/schema';
13
15
 
@@ -36,7 +38,6 @@ export class TracingService extends Context.Tag('@dxos/functions/TracingService'
36
38
 
37
39
  static console: Context.Tag.Service<TracingService> = {
38
40
  write: (event) => {
39
- // eslint-disable-next-line no-console
40
41
  console.log(event);
41
42
  },
42
43
  getTraceContext: () => ({}),
@@ -89,17 +90,19 @@ export class TracingService extends Context.Tag('@dxos/functions/TracingService'
89
90
  /**
90
91
  * Emit the current human-readable execution status.
91
92
  */
92
- static emitStatus: (data: Obj.MakeProps<typeof AgentStatus>) => Effect.Effect<void, never, TracingService> =
93
- Effect.fnUntraced(function* (data) {
94
- const tracing = yield* TracingService;
95
- tracing.write(
96
- Obj.make(AgentStatus, {
97
- parentMessage: tracing.getTraceContext().parentMessage,
98
- toolCallId: tracing.getTraceContext().toolCallId,
99
- ...data,
100
- }),
101
- );
102
- });
93
+ static emitStatus: (
94
+ data: Omit<Obj.MakeProps<typeof AgentStatus>, 'created'>,
95
+ ) => Effect.Effect<void, never, TracingService> = Effect.fnUntraced(function* (data) {
96
+ const tracing = yield* TracingService;
97
+ tracing.write(
98
+ Obj.make(AgentStatus, {
99
+ parentMessage: tracing.getTraceContext().parentMessage,
100
+ toolCallId: tracing.getTraceContext().toolCallId,
101
+ created: new Date().toISOString(),
102
+ ...data,
103
+ }),
104
+ );
105
+ });
103
106
 
104
107
  static emitConverationMessage: (
105
108
  data: Obj.MakeProps<typeof DataType.Message>,
@@ -2,13 +2,15 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import type { Schema } from 'effect';
6
- import { Context, Effect, Layer } from 'effect';
5
+ import * as Context from 'effect/Context';
6
+ import * as Effect from 'effect/Effect';
7
+ import * as Layer from 'effect/Layer';
8
+ import type * as Schema from 'effect/Schema';
7
9
 
8
10
  import type { EchoDatabaseImpl, QueueFactory } from '@dxos/echo-db';
9
11
  import { EchoTestBuilder } from '@dxos/echo-db/testing';
10
12
  import type { EchoHostIndexingConfig } from '@dxos/echo-pipeline';
11
- import { accuireReleaseResource } from '@dxos/effect';
13
+ import { acquireReleaseResource } from '@dxos/effect';
12
14
  import { PublicKey } from '@dxos/keys';
13
15
  import type { LevelDB } from '@dxos/kv-store';
14
16
  import { createTestLevel } from '@dxos/kv-store/testing';
@@ -16,7 +18,7 @@ import { log } from '@dxos/log';
16
18
 
17
19
  import { DatabaseService, QueueService } from '../services';
18
20
 
19
- const testBuilder = accuireReleaseResource(() => new EchoTestBuilder());
21
+ const testBuilder = acquireReleaseResource(() => new EchoTestBuilder());
20
22
 
21
23
  export const testStoragePath = ({ name = PublicKey.random().toHex() }: { name?: string }) => {
22
24
  return `/tmp/dxos-${name}`;
@@ -25,11 +27,12 @@ export const testStoragePath = ({ name = PublicKey.random().toHex() }: { name?:
25
27
  export type TestDatabaseOptions = {
26
28
  indexing?: Partial<EchoHostIndexingConfig>;
27
29
  types?: Schema.Schema.AnyNoContext[];
30
+ spaceKey?: PublicKey;
28
31
  storagePath?: string;
29
32
  onInit?: () => Effect.Effect<void, never, DatabaseService | QueueService>;
30
33
  };
31
34
 
32
- export const TestDatabaseLayer = ({ indexing, types, storagePath, onInit }: TestDatabaseOptions = {}) =>
35
+ export const TestDatabaseLayer = ({ indexing, types, spaceKey, storagePath, onInit }: TestDatabaseOptions = {}) =>
33
36
  Layer.scopedContext(
34
37
  Effect.gen(function* () {
35
38
  const builder = yield* testBuilder;
@@ -59,7 +62,7 @@ export const TestDatabaseLayer = ({ indexing, types, storagePath, onInit }: Test
59
62
  });
60
63
  log('starting persistant test db', { storagePath, testMetadata });
61
64
  if (!testMetadata) {
62
- const key = PublicKey.random();
65
+ const key = spaceKey ?? PublicKey.random();
63
66
  db = yield* Effect.promise(() => peer.createDatabase(key));
64
67
  queues = peer.client.constructQueueFactory(db.spaceId);
65
68
 
@@ -80,7 +83,7 @@ export const TestDatabaseLayer = ({ indexing, types, storagePath, onInit }: Test
80
83
  queues = peer.client.constructQueueFactory(db.spaceId);
81
84
  }
82
85
  } else {
83
- db = yield* Effect.promise(() => peer.createDatabase());
86
+ db = yield* Effect.promise(() => peer.createDatabase(spaceKey));
84
87
  queues = peer.client.constructQueueFactory(db.spaceId);
85
88
  if (onInit) {
86
89
  yield* onInit().pipe(
@@ -2,7 +2,8 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { type Context, Effect } from 'effect';
5
+ import type * as Context from 'effect/Context';
6
+ import * as Effect from 'effect/Effect';
6
7
 
7
8
  import { LogLevel } from '@dxos/log';
8
9
 
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { describe, expect, it } from '@effect/vitest';
6
- import { Effect } from 'effect';
6
+ import * as Effect from 'effect/Effect';
7
7
 
8
8
  import { Filter, Obj, Query, Type } from '@dxos/echo';
9
9
  import { DataType } from '@dxos/schema';
@@ -15,7 +15,7 @@ import { TestDatabaseLayer, testStoragePath } from './layer';
15
15
  describe('TestDatabaseLayer', { timeout: 600_000 }, () => {
16
16
  it.effect(
17
17
  'persist database to disk',
18
- Effect.fnUntraced(function* ({ expect: _ }) {
18
+ Effect.fnUntraced(function* (_) {
19
19
  const DbLayer = TestDatabaseLayer({
20
20
  storagePath: testStoragePath({ name: `feed-test-${Date.now()}` }),
21
21
  });
@@ -34,7 +34,7 @@ describe('TestDatabaseLayer', { timeout: 600_000 }, () => {
34
34
 
35
35
  it.effect(
36
36
  'reload database -- save index before restart',
37
- Effect.fnUntraced(function* ({ expect: _ }) {
37
+ Effect.fnUntraced(function* (_) {
38
38
  const NUM_OBJECTS = 500;
39
39
  const DbLayer = TestDatabaseLayer({
40
40
  types: [DataType.Person],
@@ -58,7 +58,7 @@ describe('TestDatabaseLayer', { timeout: 600_000 }, () => {
58
58
  it.effect.skip(
59
59
  'reload database -- save index before restart [manual]',
60
60
  Effect.fnUntraced(
61
- function* ({ expect: _ }) {
61
+ function* (_) {
62
62
  const NUM_OBJECTS = 500;
63
63
 
64
64
  {
@@ -2,7 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { type Context } from 'effect';
5
+ import type * as Context from 'effect/Context';
6
6
 
7
7
  import type { Space } from '@dxos/client/echo';
8
8
  import type { EchoDatabase, QueueFactory } from '@dxos/echo-db';
package/src/trace.ts CHANGED
@@ -2,11 +2,11 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Schema } from 'effect';
5
+ import * as Schema from 'effect/Schema';
6
6
 
7
7
  import { type Ref, Type } from '@dxos/echo';
8
+ import { ObjectId } from '@dxos/echo/internal';
8
9
  import { Queue } from '@dxos/echo-db';
9
- import { ObjectId } from '@dxos/echo-schema';
10
10
  import { log } from '@dxos/log';
11
11
 
12
12
  import { FunctionTrigger } from './types';
@@ -2,7 +2,9 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Context, Effect, Layer } from 'effect';
5
+ import * as Context from 'effect/Context';
6
+ import * as Effect from 'effect/Effect';
7
+ import * as Layer from 'effect/Layer';
6
8
 
7
9
  import { Obj, Ref } from '@dxos/echo';
8
10
  import { type Queue } from '@dxos/echo-db';
@@ -2,9 +2,13 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { FetchHttpClient } from '@effect/platform';
5
+ import * as FetchHttpClient from '@effect/platform/FetchHttpClient';
6
6
  import { describe, it } from '@effect/vitest';
7
- import { Duration, Effect, Exit, Layer, pipe } from 'effect';
7
+ import * as Duration from 'effect/Duration';
8
+ import * as Effect from 'effect/Effect';
9
+ import * as Exit from 'effect/Exit';
10
+ import * as Function from 'effect/Function';
11
+ import * as Layer from 'effect/Layer';
8
12
 
9
13
  import { AiService } from '@dxos/ai';
10
14
  import { Filter, Obj, Query, Ref } from '@dxos/echo';
@@ -18,11 +22,10 @@ import {
18
22
  ComputeEventLogger,
19
23
  CredentialsService,
20
24
  DatabaseService,
25
+ FunctionInvocationService,
21
26
  QueueService,
22
- RemoteFunctionExecutionService,
23
27
  TracingService,
24
28
  } from '../services';
25
- import { FunctionImplementationResolver, LocalFunctionExecutionService } from '../services/local-function-execution';
26
29
  import { TestDatabaseLayer } from '../testing';
27
30
  import { FunctionTrigger } from '../types';
28
31
 
@@ -30,22 +33,22 @@ import { InvocationTracer } from './invocation-tracer';
30
33
  import { TriggerDispatcher } from './trigger-dispatcher';
31
34
  import { TriggerStateStore } from './trigger-state-store';
32
35
 
33
- const TestLayer = pipe(
36
+ const TestLayer = Function.pipe(
34
37
  Layer.mergeAll(ComputeEventLogger.layerFromTracing, InvocationTracer.layerTest, TriggerStateStore.layerMemory),
35
38
  Layer.provideMerge(
36
39
  Layer.mergeAll(
37
40
  AiService.notAvailable,
41
+ CredentialsService.layerConfig([]),
42
+ FunctionInvocationService.layerTestMocked({ functions: [reply] }).pipe(
43
+ Layer.provideMerge(ComputeEventLogger.layerFromTracing),
44
+ Layer.provideMerge(TracingService.layerLogInfo()),
45
+ ),
46
+ FetchHttpClient.layer,
38
47
  TestDatabaseLayer({
39
48
  types: [FunctionType, FunctionTrigger, DataType.Person, DataType.Task],
40
49
  }),
41
- CredentialsService.layerConfig([]),
42
- LocalFunctionExecutionService.layerLive,
43
- RemoteFunctionExecutionService.mockLayer,
44
- TracingService.layerLogInfo(),
45
- FetchHttpClient.layer,
46
50
  ),
47
51
  ),
48
- Layer.provideMerge(FunctionImplementationResolver.layerTest({ functions: [reply] })),
49
52
  );
50
53
 
51
54
  const TestTriggerDispatcherLayer = Layer.provideMerge(
@@ -452,7 +455,9 @@ describe('TriggerDispatcher', () => {
452
455
  enabled: true,
453
456
  spec: {
454
457
  kind: 'subscription',
455
- query: Query.select(Filter.type(DataType.Person)).ast,
458
+ query: {
459
+ ast: Query.select(Filter.type(DataType.Person)).ast,
460
+ },
456
461
  },
457
462
  });
458
463
  yield* DatabaseService.add(trigger);
@@ -494,7 +499,9 @@ describe('TriggerDispatcher', () => {
494
499
  enabled: true,
495
500
  spec: {
496
501
  kind: 'subscription',
497
- query: Query.select(Filter.type(DataType.Person)).ast,
502
+ query: {
503
+ ast: Query.select(Filter.type(DataType.Person)).ast,
504
+ },
498
505
  },
499
506
  });
500
507
  yield* DatabaseService.add(trigger);
@@ -530,7 +537,9 @@ describe('TriggerDispatcher', () => {
530
537
  enabled: true,
531
538
  spec: {
532
539
  kind: 'subscription',
533
- query: Query.select(Filter.type(DataType.Person)).ast,
540
+ query: {
541
+ ast: Query.select(Filter.type(DataType.Person)).ast,
542
+ },
534
543
  },
535
544
  });
536
545
  yield* DatabaseService.add(trigger);
@@ -578,7 +587,9 @@ describe('TriggerDispatcher', () => {
578
587
  enabled: true,
579
588
  spec: {
580
589
  kind: 'subscription',
581
- query: Query.select(Filter.type(DataType.Task)).ast,
590
+ query: {
591
+ ast: Query.select(Filter.type(DataType.Task)).ast,
592
+ },
582
593
  },
583
594
  });
584
595
  yield* DatabaseService.add(trigger);
@@ -625,7 +636,9 @@ describe('TriggerDispatcher', () => {
625
636
  enabled: true,
626
637
  spec: {
627
638
  kind: 'subscription',
628
- query: Query.select(Filter.type(DataType.Person)).ast,
639
+ query: {
640
+ ast: Query.select(Filter.type(DataType.Person)).ast,
641
+ },
629
642
  },
630
643
  input: {
631
644
  objectId: '{{event.changedObjectId}}',