@dxos/functions 0.8.4-main.7ace549 → 0.8.4-main.8360d9e660

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 (59) hide show
  1. package/README.md +4 -6
  2. package/dist/lib/{browser → neutral}/index.mjs +327 -114
  3. package/dist/lib/neutral/index.mjs.map +7 -0
  4. package/dist/lib/neutral/meta.json +1 -0
  5. package/dist/types/src/errors.d.ts +24 -32
  6. package/dist/types/src/errors.d.ts.map +1 -1
  7. package/dist/types/src/example/index.d.ts +5 -5
  8. package/dist/types/src/example/index.d.ts.map +1 -1
  9. package/dist/types/src/operation-compatibility.test.d.ts +2 -0
  10. package/dist/types/src/operation-compatibility.test.d.ts.map +1 -0
  11. package/dist/types/src/protocol/functions-ai-http-client.d.ts +12 -0
  12. package/dist/types/src/protocol/functions-ai-http-client.d.ts.map +1 -0
  13. package/dist/types/src/protocol/protocol.d.ts.map +1 -1
  14. package/dist/types/src/sdk.d.ts +26 -9
  15. package/dist/types/src/sdk.d.ts.map +1 -1
  16. package/dist/types/src/services/credentials.d.ts +6 -4
  17. package/dist/types/src/services/credentials.d.ts.map +1 -1
  18. package/dist/types/src/services/event-logger.d.ts +25 -31
  19. package/dist/types/src/services/event-logger.d.ts.map +1 -1
  20. package/dist/types/src/services/function-invocation-service.d.ts +5 -0
  21. package/dist/types/src/services/function-invocation-service.d.ts.map +1 -1
  22. package/dist/types/src/services/index.d.ts +0 -1
  23. package/dist/types/src/services/index.d.ts.map +1 -1
  24. package/dist/types/src/services/tracing.d.ts +37 -3
  25. package/dist/types/src/services/tracing.d.ts.map +1 -1
  26. package/dist/types/src/types/Function.d.ts +34 -45
  27. package/dist/types/src/types/Function.d.ts.map +1 -1
  28. package/dist/types/src/types/Script.d.ts +8 -15
  29. package/dist/types/src/types/Script.d.ts.map +1 -1
  30. package/dist/types/src/types/Trigger.d.ts +59 -78
  31. package/dist/types/src/types/Trigger.d.ts.map +1 -1
  32. package/dist/types/src/types/TriggerEvent.d.ts +44 -12
  33. package/dist/types/src/types/TriggerEvent.d.ts.map +1 -1
  34. package/dist/types/src/types/url.d.ts +6 -5
  35. package/dist/types/src/types/url.d.ts.map +1 -1
  36. package/dist/types/tsconfig.tsbuildinfo +1 -1
  37. package/package.json +23 -18
  38. package/src/errors.ts +4 -4
  39. package/src/example/forex-effect.ts +1 -1
  40. package/src/example/index.ts +8 -8
  41. package/src/operation-compatibility.test.ts +185 -0
  42. package/src/protocol/functions-ai-http-client.ts +67 -0
  43. package/src/protocol/protocol.ts +117 -15
  44. package/src/sdk.ts +73 -16
  45. package/src/services/credentials.ts +31 -15
  46. package/src/services/event-logger.ts +2 -2
  47. package/src/services/function-invocation-service.ts +14 -0
  48. package/src/services/index.ts +0 -2
  49. package/src/services/tracing.ts +63 -4
  50. package/src/types/Function.ts +16 -13
  51. package/src/types/Script.ts +9 -4
  52. package/src/types/Trigger.ts +30 -16
  53. package/src/types/TriggerEvent.ts +29 -29
  54. package/src/types/url.ts +6 -5
  55. package/dist/lib/browser/index.mjs.map +0 -7
  56. package/dist/lib/browser/meta.json +0 -1
  57. package/dist/lib/node-esm/index.mjs +0 -1031
  58. package/dist/lib/node-esm/index.mjs.map +0 -7
  59. package/dist/lib/node-esm/meta.json +0 -1
package/src/sdk.ts CHANGED
@@ -7,9 +7,10 @@ import * as Effect from 'effect/Effect';
7
7
  import * as Schema from 'effect/Schema';
8
8
 
9
9
  import { type AiService } from '@dxos/ai';
10
- import { Obj, Type } from '@dxos/echo';
11
- import { type DatabaseService } from '@dxos/echo-db';
10
+ import { type Feed, JsonSchema, Obj, type Type } from '@dxos/echo';
11
+ import { type Database } from '@dxos/echo';
12
12
  import { assertArgument, failedInvariant } from '@dxos/invariant';
13
+ import { Operation } from '@dxos/operation';
13
14
 
14
15
  import {
15
16
  type CredentialsService,
@@ -37,8 +38,10 @@ export type FunctionServices =
37
38
  | InvocationServices
38
39
  | AiService.AiService
39
40
  | CredentialsService
40
- | DatabaseService
41
+ | Database.Service
42
+ // TODO(wittjosiah): Remove QueueService — use Feed.Service instead.
41
43
  | QueueService
44
+ | Feed.Service
42
45
  | FunctionInvocationService;
43
46
 
44
47
  /**
@@ -67,26 +70,29 @@ export interface FunctionContext {
67
70
 
68
71
  const typeId = Symbol.for('@dxos/functions/FunctionDefinition');
69
72
 
70
- export type FunctionDefinition<T = any, O = any, S extends FunctionServices = FunctionServices> = {
73
+ /**
74
+ * Deployable function definition.
75
+ */
76
+ export type FunctionDefinition<TInput = any, TOutput = any, S extends FunctionServices = FunctionServices> = {
71
77
  [typeId]: true;
78
+
72
79
  key: string;
73
80
  name: string;
74
81
  description?: string;
75
- inputSchema: Schema.Schema<T, any>;
76
- outputSchema?: Schema.Schema<O, any>;
82
+ inputSchema: Schema.Schema<TInput, any>;
83
+ outputSchema?: Schema.Schema<TOutput, any>;
77
84
 
78
85
  /**
79
86
  * List of types the function uses.
80
87
  * This is used to ensure that the types are available when the function is executed.
81
88
  */
82
- types: readonly Type.Obj.Any[];
89
+ types: readonly Type.AnyEntity[];
83
90
 
84
91
  /**
85
92
  * Keys of the required services.
86
93
  */
87
94
  services: readonly string[];
88
95
 
89
- handler: FunctionHandler<T, O, S>;
90
96
  meta?: {
91
97
  /**
92
98
  * Tools that are projected from functions have this annotation.
@@ -99,6 +105,8 @@ export type FunctionDefinition<T = any, O = any, S extends FunctionServices = Fu
99
105
  */
100
106
  deployedFunctionId?: string;
101
107
  };
108
+
109
+ handler: FunctionHandler<TInput, TOutput, S>;
102
110
  };
103
111
 
104
112
  export declare namespace FunctionDefinition {
@@ -119,7 +127,8 @@ export type FunctionProps<T, O> = {
119
127
  * List of types the function uses.
120
128
  * This is used to ensure that the types are available when the function is executed.
121
129
  */
122
- types?: readonly Type.Obj.Any[];
130
+ types?: readonly Type.AnyEntity[];
131
+
123
132
  // TODO(dmaretskyi): This currently doesn't cause a compile-time error if the handler requests a service that is not specified
124
133
  services?: readonly Context.Tag<any, any>[];
125
134
 
@@ -176,7 +185,7 @@ export const defineFunction: {
176
185
  handler: handlerWithSpan,
177
186
  types: types ?? [],
178
187
  services: !services ? [] : getServiceKeys(services),
179
- } satisfies FunctionDefinition.Any;
188
+ };
180
189
  };
181
190
 
182
191
  const getServiceKeys = (services: readonly Context.Tag<any, any>[]) => {
@@ -184,11 +193,58 @@ const getServiceKeys = (services: readonly Context.Tag<any, any>[]) => {
184
193
  if (typeof tag.key === 'string') {
185
194
  return tag.key;
186
195
  }
187
- console.log(tag);
188
196
  failedInvariant();
189
197
  });
190
198
  };
191
199
 
200
+ /**
201
+ * Converts a FunctionDefinition to an OperationDefinition with handler.
202
+ * The function handler is adapted to the OperationHandler format.
203
+ *
204
+ * Note: FunctionDefinition stores service keys as strings, not Tag types,
205
+ * so we can't use Operation.withHandler's type inference here.
206
+ */
207
+ export const toOperation = <T, O, S extends FunctionServices = FunctionServices>(
208
+ functionDef: FunctionDefinition<T, O, S>,
209
+ ): Operation.Definition<T, O> & { handler: Operation.Handler<T, O, any, S> } => {
210
+ const op = Operation.make({
211
+ schema: {
212
+ input: functionDef.inputSchema,
213
+ output: functionDef.outputSchema ?? Schema.Any,
214
+ },
215
+ meta: {
216
+ key: functionDef.key,
217
+ name: functionDef.name,
218
+ description: functionDef.description,
219
+ },
220
+ });
221
+
222
+ // Adapt FunctionHandler signature to OperationHandler format.
223
+ // FunctionHandler expects { context, data }, OperationHandler expects just input.
224
+ const operationHandler: Operation.Handler<T, O, any, S> = (input: T) => {
225
+ const result = functionDef.handler({
226
+ context: {} as FunctionContext,
227
+ data: input,
228
+ });
229
+
230
+ // Convert Promise or plain value to Effect.
231
+ if (Effect.isEffect(result)) {
232
+ return result;
233
+ }
234
+ if (result instanceof Promise) {
235
+ return Effect.tryPromise(() => result);
236
+ }
237
+ return Effect.succeed(result as O);
238
+ };
239
+
240
+ // Manually attach handler since FunctionDefinition stores service keys as strings,
241
+ // not Tag types, so withHandler's type inference doesn't apply.
242
+ return {
243
+ ...op,
244
+ handler: operationHandler,
245
+ };
246
+ };
247
+
192
248
  export const FunctionDefinition = {
193
249
  make: defineFunction,
194
250
  isFunction: (value: unknown): value is FunctionDefinition.Any => {
@@ -202,6 +258,7 @@ export const FunctionDefinition = {
202
258
  assertArgument(Obj.instanceOf(Function.Function, functionObj), 'functionObj');
203
259
  return deserializeFunction(functionObj);
204
260
  },
261
+ toOperation,
205
262
  };
206
263
 
207
264
  export const serializeFunction = (functionDef: FunctionDefinition.Any): Function.Function => {
@@ -210,12 +267,12 @@ export const serializeFunction = (functionDef: FunctionDefinition.Any): Function
210
267
  name: functionDef.name,
211
268
  version: '0.1.0',
212
269
  description: functionDef.description,
213
- inputSchema: Type.toJsonSchema(functionDef.inputSchema),
214
- outputSchema: !functionDef.outputSchema ? undefined : Type.toJsonSchema(functionDef.outputSchema),
270
+ inputSchema: JsonSchema.toJsonSchema(functionDef.inputSchema),
271
+ outputSchema: !functionDef.outputSchema ? undefined : JsonSchema.toJsonSchema(functionDef.outputSchema),
215
272
  services: functionDef.services,
216
273
  });
217
274
  if (functionDef.meta?.deployedFunctionId) {
218
- setUserFunctionIdInMetadata(Obj.getMeta(fn), functionDef.meta.deployedFunctionId);
275
+ Obj.change(fn, (fn) => setUserFunctionIdInMetadata(Obj.getMeta(fn), functionDef.meta!.deployedFunctionId!));
219
276
  }
220
277
  return fn;
221
278
  };
@@ -227,8 +284,8 @@ export const deserializeFunction = (functionObj: Function.Function): FunctionDef
227
284
  key: functionObj.key ?? functionObj.name,
228
285
  name: functionObj.name,
229
286
  description: functionObj.description,
230
- inputSchema: !functionObj.inputSchema ? Schema.Unknown : Type.toEffectSchema(functionObj.inputSchema),
231
- outputSchema: !functionObj.outputSchema ? undefined : Type.toEffectSchema(functionObj.outputSchema),
287
+ inputSchema: !functionObj.inputSchema ? Schema.Unknown : JsonSchema.toEffectSchema(functionObj.inputSchema),
288
+ outputSchema: !functionObj.outputSchema ? undefined : JsonSchema.toEffectSchema(functionObj.outputSchema),
232
289
  // TODO(dmaretskyi): This should throw error.
233
290
  handler: () => {},
234
291
  services: functionObj.services ?? [],
@@ -11,7 +11,7 @@ import * as Layer from 'effect/Layer';
11
11
  import * as Redacted from 'effect/Redacted';
12
12
 
13
13
  import { Query } from '@dxos/echo';
14
- import { DatabaseService } from '@dxos/echo-db';
14
+ import { Database } from '@dxos/echo';
15
15
  import { AccessToken } from '@dxos/types';
16
16
 
17
17
  export type CredentialQuery = {
@@ -60,7 +60,12 @@ export class CredentialsService extends Context.Tag('@dxos/functions/Credentials
60
60
  static configuredLayer = (credentials: ServiceCredential[]) =>
61
61
  Layer.succeed(CredentialsService, new ConfiguredCredentialsService(credentials));
62
62
 
63
- static layerConfig = (credentials: { service: string; apiKey: Config.Config<Redacted.Redacted<string>> }[]) =>
63
+ static layerConfig = (
64
+ credentials: {
65
+ service: string;
66
+ apiKey: Config.Config<Redacted.Redacted<string>>;
67
+ }[],
68
+ ) =>
64
69
  Layer.effect(
65
70
  CredentialsService,
66
71
  Effect.gen(function* () {
@@ -77,20 +82,34 @@ export class CredentialsService extends Context.Tag('@dxos/functions/Credentials
77
82
  }),
78
83
  );
79
84
 
80
- static layerFromDatabase = () =>
85
+ static layerFromDatabase = ({ caching = false }: { caching?: boolean } = {}) =>
81
86
  Layer.effect(
82
87
  CredentialsService,
83
88
  Effect.gen(function* () {
84
- const dbService = yield* DatabaseService;
89
+ const dbService = yield* Database.Service;
90
+ const cache = new Map<string, ServiceCredential[]>();
91
+
85
92
  const queryCredentials = async (query: CredentialQuery): Promise<ServiceCredential[]> => {
86
- const { objects: accessTokens } = await dbService.db.query(Query.type(AccessToken.AccessToken)).run();
87
- return accessTokens
93
+ const cacheKey = JSON.stringify(query);
94
+ if (caching && cache.has(cacheKey)) {
95
+ return cache.get(cacheKey)!;
96
+ }
97
+
98
+ const accessTokens = await dbService.db.query(Query.type(AccessToken.AccessToken)).run();
99
+ const credentials = accessTokens
88
100
  .filter((accessToken) => accessToken.source === query.service)
89
101
  .map((accessToken) => ({
90
102
  service: accessToken.source,
91
103
  apiKey: accessToken.token,
92
104
  }));
105
+
106
+ if (caching) {
107
+ cache.set(cacheKey, credentials);
108
+ }
109
+
110
+ return credentials;
93
111
  };
112
+
94
113
  return {
95
114
  getCredential: async (query) => {
96
115
  const credentials = await queryCredentials(query);
@@ -131,13 +150,10 @@ export class ConfiguredCredentialsService implements Context.Tag.Service<Credent
131
150
  }
132
151
 
133
152
  /**
134
- * Maps the request to include the API key from the credential.
153
+ * Maps the request to include the given token in the Authorization header.
135
154
  */
136
- export const withAuthorization = (query: CredentialQuery, kind?: 'Bearer' | 'Basic') =>
137
- HttpClient.mapRequestEffect(
138
- Effect.fnUntraced(function* (request) {
139
- const key = yield* CredentialsService.getApiKey(query).pipe(Effect.map(Redacted.value));
140
- const authorization = kind ? `${kind} ${key}` : key;
141
- return HttpClientRequest.setHeader(request, 'Authorization', authorization);
142
- }),
143
- );
155
+ export const withAuthorization = (token: string, kind?: 'Bearer' | 'Basic') =>
156
+ HttpClient.mapRequest((request) => {
157
+ const authorization = kind ? `${kind} ${token}` : token;
158
+ return HttpClientRequest.setHeader(request, 'Authorization', authorization);
159
+ });
@@ -52,7 +52,7 @@ export type ComputeEventPayload = Schema.Schema.Type<typeof ComputeEventPayload>
52
52
 
53
53
  export const ComputeEvent = Schema.Struct({
54
54
  payload: ComputeEventPayload,
55
- }).pipe(Type.Obj({ typename: 'dxos.org/type/ComputeEvent', version: '0.1.0' }));
55
+ }).pipe(Type.object({ typename: 'org.dxos.type.compute-event', version: '0.1.0' }));
56
56
 
57
57
  /**
58
58
  * Logs event for the compute workflows.
@@ -75,7 +75,7 @@ export class ComputeEventLogger extends Context.Tag('@dxos/functions/ComputeEven
75
75
  const tracing = yield* TracingService;
76
76
  return {
77
77
  log: (event: ComputeEventPayload) => {
78
- tracing.write(Obj.make(ComputeEvent, { payload: event }));
78
+ tracing.write(Obj.make(ComputeEvent, { payload: event }), tracing.getTraceContext());
79
79
  },
80
80
  nodeId: undefined,
81
81
  };
@@ -3,7 +3,9 @@
3
3
  //
4
4
  import * as Context from 'effect/Context';
5
5
  import * as Effect from 'effect/Effect';
6
+ import * as Layer from 'effect/Layer';
6
7
 
8
+ import type { FunctionNotFoundError } from '../errors';
7
9
  import { type FunctionDefinition, type InvocationServices } from '../sdk';
8
10
 
9
11
  export class FunctionInvocationService extends Context.Tag('@dxos/functions/FunctionInvocationService')<
@@ -13,11 +15,23 @@ export class FunctionInvocationService extends Context.Tag('@dxos/functions/Func
13
15
  functionDef: FunctionDefinition<I, O, any>,
14
16
  input: I,
15
17
  ): Effect.Effect<O, never, InvocationServices>;
18
+
19
+ resolveFunction(key: string): Effect.Effect<FunctionDefinition.Any, FunctionNotFoundError>;
16
20
  }
17
21
  >() {
22
+ static layerNotAvailable = Layer.succeed(FunctionInvocationService, {
23
+ invokeFunction: () => Effect.die('FunctionInvocationService is not avaialble.'),
24
+ resolveFunction: () => Effect.die('FunctionInvocationService is not available.'),
25
+ });
26
+
18
27
  static invokeFunction = <I, O>(
19
28
  functionDef: FunctionDefinition<I, O, any>,
20
29
  input: I,
21
30
  ): Effect.Effect<O, never, FunctionInvocationService | InvocationServices> =>
22
31
  Effect.serviceFunctionEffect(FunctionInvocationService, (service) => service.invokeFunction)(functionDef, input);
32
+
33
+ static resolveFunction = (
34
+ key: string,
35
+ ): Effect.Effect<FunctionDefinition.Any, FunctionNotFoundError, FunctionInvocationService> =>
36
+ Effect.serviceFunctionEffect(FunctionInvocationService, (service) => service.resolveFunction)(key);
23
37
  }
@@ -2,8 +2,6 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- export { DatabaseService } from '@dxos/echo-db';
6
-
7
5
  export * from './credentials';
8
6
  export { ConfiguredCredentialsService, type ServiceCredential } from './credentials';
9
7
  export * from './event-logger';
@@ -7,10 +7,12 @@ import * as Effect from 'effect/Effect';
7
7
  import * as Layer from 'effect/Layer';
8
8
 
9
9
  import { AgentStatus } from '@dxos/ai';
10
- import { Obj } from '@dxos/echo';
11
- import { type ObjectId } from '@dxos/keys';
10
+ import { type DXN, Obj } from '@dxos/echo';
11
+ import { ObjectId } from '@dxos/keys';
12
12
  import { Message } from '@dxos/types';
13
13
 
14
+ import type { Trigger } from '../types';
15
+
14
16
  /**
15
17
  * Provides a way for compute primitives (functions, workflows, tools)
16
18
  * to emit an execution trace as a series of structured ECHO objects.
@@ -27,15 +29,35 @@ export class TracingService extends Context.Tag('@dxos/functions/TracingService'
27
29
  * Write an event to the tracing queue.
28
30
  * @param event - The event to write. Must be an a typed object.
29
31
  */
30
- write: (event: Obj.Any) => void;
32
+ write: (event: Obj.Unknown, traceContext: TracingService.TraceContext) => void;
33
+
34
+ traceInvocationStart({
35
+ payload,
36
+ target,
37
+ }: {
38
+ payload: TracingService.FunctionInvocationPayload;
39
+ target?: DXN;
40
+ }): Effect.Effect<TracingService.InvocationTraceData>;
41
+
42
+ traceInvocationEnd({
43
+ trace,
44
+ exception,
45
+ }: {
46
+ trace: TracingService.InvocationTraceData;
47
+ exception?: any;
48
+ }): Effect.Effect<void>;
31
49
  }
32
50
  >() {
33
51
  static noop: Context.Tag.Service<TracingService> = {
34
52
  getTraceContext: () => ({}),
35
53
  write: () => {},
54
+ traceInvocationStart: () =>
55
+ Effect.sync(() => ({ invocationId: ObjectId.random(), invocationTraceQueue: undefined })),
56
+ traceInvocationEnd: () => Effect.sync(() => {}),
36
57
  };
37
58
 
38
59
  static layerNoop: Layer.Layer<TracingService> = Layer.succeed(TracingService, TracingService.noop);
60
+
39
61
  /**
40
62
  * Creates a TracingService layer that emits events to the parent tracing service.
41
63
  */
@@ -46,12 +68,25 @@ export class TracingService extends Context.Tag('@dxos/functions/TracingService'
46
68
  const tracing = yield* TracingService;
47
69
  const context = mapContext(tracing.getTraceContext());
48
70
  return {
49
- write: (event) => tracing.write(event),
71
+ write: (event, context) => tracing.write(event, context),
50
72
  getTraceContext: () => context,
73
+ traceInvocationStart: () => Effect.die('Tracing invocation inside another invocation is not supported.'),
74
+ traceInvocationEnd: () => Effect.die('Tracing invocation inside another invocation is not supported.'),
51
75
  };
52
76
  }),
53
77
  );
54
78
 
79
+ /**
80
+ * Create sublayer to trace an invocation.
81
+ * @param data
82
+ * @returns
83
+ */
84
+ static layerInvocation = (data: TracingService.InvocationTraceData) =>
85
+ TracingService.layerSubframe((context) => ({
86
+ ...context,
87
+ currentInvocation: data,
88
+ }));
89
+
55
90
  /**
56
91
  * Emit the current human-readable execution status.
57
92
  */
@@ -66,6 +101,7 @@ export class TracingService extends Context.Tag('@dxos/functions/TracingService'
66
101
  created: new Date().toISOString(),
67
102
  ...data,
68
103
  }),
104
+ tracing.getTraceContext(),
69
105
  );
70
106
  });
71
107
 
@@ -82,12 +118,15 @@ export class TracingService extends Context.Tag('@dxos/functions/TracingService'
82
118
  ...data.properties,
83
119
  },
84
120
  }),
121
+ tracing.getTraceContext(),
85
122
  );
86
123
  });
87
124
  }
88
125
 
89
126
  export namespace TracingService {
90
127
  export interface TraceContext {
128
+ currentInvocation?: InvocationTraceData;
129
+
91
130
  /**
92
131
  * If this thread sprung from a tool call, this is the ID of the message containing the tool call.
93
132
  */
@@ -100,6 +139,26 @@ export namespace TracingService {
100
139
 
101
140
  debugInfo?: unknown;
102
141
  }
142
+
143
+ /**
144
+ * Trace data for a function/trigger invocation.
145
+ */
146
+ export interface InvocationTraceData {
147
+ invocationId: ObjectId;
148
+ invocationTraceQueue?: DXN.String;
149
+ }
150
+
151
+ /**
152
+ * Payload for a function/trigger invocation.
153
+ */
154
+ export interface FunctionInvocationPayload {
155
+ data?: any;
156
+ inputNodeId?: string;
157
+ trigger?: {
158
+ id: string;
159
+ kind: Trigger.Kind;
160
+ };
161
+ }
103
162
  }
104
163
 
105
164
  /**
@@ -4,7 +4,7 @@
4
4
 
5
5
  import * as Schema from 'effect/Schema';
6
6
 
7
- import { Annotation, JsonSchema, Obj, Type } from '@dxos/echo';
7
+ import { Annotation, JsonSchema, Obj, Ref, Type } from '@dxos/echo';
8
8
  import { SystemTypeAnnotation } from '@dxos/echo/internal';
9
9
 
10
10
  import { Script } from './Script';
@@ -36,7 +36,7 @@ export const Function = Schema.Struct({
36
36
 
37
37
  // Reference to a source script if it exists within ECHO.
38
38
  // TODO(burdon): Don't ref ScriptType directly (core).
39
- source: Schema.optional(Type.Ref(Script)),
39
+ source: Schema.optional(Ref.Ref(Script)),
40
40
 
41
41
  inputSchema: Schema.optional(JsonSchema.JsonSchema),
42
42
  outputSchema: Schema.optional(JsonSchema.JsonSchema),
@@ -50,11 +50,12 @@ export const Function = Schema.Struct({
50
50
  // Local binding to a function name.
51
51
  binding: Schema.optional(Schema.String),
52
52
  }).pipe(
53
- Type.Obj({
54
- typename: 'dxos.org/type/Function',
53
+ Type.object({
54
+ typename: 'org.dxos.type.function',
55
55
  version: '0.1.0',
56
56
  }),
57
57
  Annotation.LabelAnnotation.set(['name']),
58
+ Annotation.IconAnnotation.set({ icon: 'ph--function--regular', hue: 'blue' }),
58
59
  SystemTypeAnnotation.set(true),
59
60
  );
60
61
 
@@ -68,13 +69,15 @@ export const make = (props: Obj.MakeProps<typeof Function>) => Obj.make(Function
68
69
  * @param source - Source object to copy properties from.
69
70
  */
70
71
  export const setFrom = (target: Function, source: Function) => {
71
- target.key = source.key ?? target.key;
72
- target.name = source.name ?? target.name;
73
- target.version = source.version;
74
- target.description = source.description;
75
- target.updated = source.updated;
76
- // TODO(dmaretskyi): A workaround for an ECHO bug.
77
- target.inputSchema = source.inputSchema ? JSON.parse(JSON.stringify(source.inputSchema)) : undefined;
78
- target.outputSchema = source.outputSchema ? JSON.parse(JSON.stringify(source.outputSchema)) : undefined;
79
- Obj.getMeta(target).keys = JSON.parse(JSON.stringify(Obj.getMeta(source).keys));
72
+ Obj.change(target, (t) => {
73
+ t.key = source.key ?? target.key;
74
+ t.name = source.name ?? target.name;
75
+ t.version = source.version;
76
+ t.description = source.description;
77
+ t.updated = source.updated;
78
+ // TODO(dmaretskyi): A workaround for an ECHO bug.
79
+ t.inputSchema = source.inputSchema ? JSON.parse(JSON.stringify(source.inputSchema)) : undefined;
80
+ t.outputSchema = source.outputSchema ? JSON.parse(JSON.stringify(source.outputSchema)) : undefined;
81
+ Obj.getMeta(t).keys = JSON.parse(JSON.stringify(Obj.getMeta(source).keys));
82
+ });
80
83
  };
@@ -17,17 +17,22 @@ export const Script = Schema.Struct({
17
17
  // TODO(burdon): Change to hash of deployed content.
18
18
  // Whether source has changed since last deploy.
19
19
  changed: Schema.Boolean.pipe(FormInputAnnotation.set(false), Schema.optional),
20
- source: Type.Ref(Text.Text).pipe(FormInputAnnotation.set(false)),
20
+ source: Ref.Ref(Text.Text).pipe(FormInputAnnotation.set(false)),
21
21
  }).pipe(
22
- Type.Obj({
23
- typename: 'dxos.org/type/Script',
22
+ Type.object({
23
+ typename: 'org.dxos.type.script',
24
24
  version: '0.1.0',
25
25
  }),
26
26
  Annotation.LabelAnnotation.set(['name']),
27
+ Annotation.IconAnnotation.set({
28
+ icon: 'ph--code--regular',
29
+ hue: 'sky',
30
+ }),
27
31
  );
32
+
28
33
  export interface Script extends Schema.Schema.Type<typeof Script> {}
29
34
 
30
35
  type Props = Omit<Obj.MakeProps<typeof Script>, 'source'> & { source?: string };
31
36
 
32
- export const make = ({ source = '', ...props }: Props = {}) =>
37
+ export const make = ({ source = '', ...props }: Props = {}): Script =>
33
38
  Obj.make(Script, { ...props, source: Ref.make(Text.make(source)) });
@@ -5,9 +5,10 @@
5
5
  import * as Schema from 'effect/Schema';
6
6
  import * as SchemaAST from 'effect/SchemaAST';
7
7
 
8
- import { Obj, QueryAST, Type } from '@dxos/echo';
8
+ import { Annotation, Obj, QueryAST, Ref, Type } from '@dxos/echo';
9
9
  import { OptionsAnnotationId, SystemTypeAnnotation } from '@dxos/echo/internal';
10
10
  import { DXN } from '@dxos/keys';
11
+ import { Expando } from '@dxos/schema';
11
12
 
12
13
  /**
13
14
  * Type discriminator for TriggerType.
@@ -21,15 +22,16 @@ const kindLiteralAnnotations = { title: 'Kind' };
21
22
 
22
23
  export const EmailSpec = Schema.Struct({
23
24
  kind: Schema.Literal('email').annotations(kindLiteralAnnotations),
24
- }).pipe(Schema.mutable);
25
+ });
25
26
  export type EmailSpec = Schema.Schema.Type<typeof EmailSpec>;
26
27
 
28
+ // TODO(wittjosiah): Remove. Migrate to Subscription triggers once EDGE supports them for feed queries.
27
29
  export const QueueSpec = Schema.Struct({
28
30
  kind: Schema.Literal('queue').annotations(kindLiteralAnnotations),
29
31
 
30
32
  // TODO(dmaretskyi): Change to a reference.
31
33
  queue: DXN.Schema,
32
- }).pipe(Schema.mutable);
34
+ });
33
35
  export type QueueSpec = Schema.Schema.Type<typeof QueueSpec>;
34
36
 
35
37
  /**
@@ -40,7 +42,7 @@ export const SubscriptionSpec = Schema.Struct({
40
42
  query: Schema.Struct({
41
43
  raw: Schema.optional(Schema.String.annotations({ title: 'Query' })),
42
44
  ast: QueryAST.Query,
43
- }).pipe(Schema.mutable),
45
+ }),
44
46
  options: Schema.optional(
45
47
  Schema.Struct({
46
48
  // Watch changes to object (not just creation).
@@ -49,7 +51,7 @@ export const SubscriptionSpec = Schema.Struct({
49
51
  delay: Schema.optional(Schema.Number.annotations({ title: 'Delay' })),
50
52
  }).annotations({ title: 'Options' }),
51
53
  ),
52
- }).pipe(Schema.mutable);
54
+ });
53
55
  export type SubscriptionSpec = Schema.Schema.Type<typeof SubscriptionSpec>;
54
56
 
55
57
  /**
@@ -61,7 +63,7 @@ export const TimerSpec = Schema.Struct({
61
63
  title: 'Cron',
62
64
  [SchemaAST.ExamplesAnnotationId]: ['0 0 * * *'],
63
65
  }),
64
- }).pipe(Schema.mutable);
66
+ });
65
67
  export type TimerSpec = Schema.Schema.Type<typeof TimerSpec>;
66
68
 
67
69
  /**
@@ -80,7 +82,7 @@ export const WebhookSpec = Schema.Struct({
80
82
  title: 'Port',
81
83
  }),
82
84
  ),
83
- }).pipe(Schema.mutable);
85
+ });
84
86
  export type WebhookSpec = Schema.Schema.Type<typeof WebhookSpec>;
85
87
 
86
88
  /**
@@ -96,12 +98,12 @@ export type Spec = Schema.Schema.Type<typeof Spec>;
96
98
  * Function is invoked with the `payload` passed as input data.
97
99
  * The event that triggers the function is available in the function context.
98
100
  */
99
- const Trigger_ = Schema.Struct({
101
+ const TriggerSchema = Schema.Struct({
100
102
  /**
101
103
  * Function or workflow to invoke.
102
104
  */
103
105
  // TODO(dmaretskyi): Can be a Ref(FunctionType) or Ref(ComputeGraphType).
104
- function: Schema.optional(Type.Ref(Type.Expando).annotations({ title: 'Function' })),
106
+ function: Schema.optional(Ref.Ref(Expando.Expando).annotations({ title: 'Function' })),
105
107
 
106
108
  /**
107
109
  * Only used for workflowSchema.
@@ -110,31 +112,43 @@ const Trigger_ = Schema.Struct({
110
112
  */
111
113
  inputNodeId: Schema.optional(Schema.String.annotations({ title: 'Input Node ID' })),
112
114
 
115
+ // TODO(burdon): NO BOOLEAN PROPERTIES (enabld/disabled/paused, etc.)
116
+ // Need lint rule; or agent rule to require PR review for "boolean" key word.
113
117
  enabled: Schema.optional(Schema.Boolean.annotations({ title: 'Enabled' })),
114
118
 
115
119
  spec: Schema.optional(Spec),
116
120
 
121
+ concurrency: Schema.optional(
122
+ Schema.Number.annotations({
123
+ title: 'Concurrency',
124
+ default: 1,
125
+ description:
126
+ 'Maximum number of concurrent invocations of the trigger. For queue triggers, this will process queue items in parallel.',
127
+ }),
128
+ ),
129
+
117
130
  /**
118
131
  * Passed as the input data to the function.
119
132
  * Must match the function's input schema.
120
133
  *
121
134
  * @example
122
135
  * {
123
- * item: '{{$.trigger.event}}',
136
+ * item: '{{event.item}}',
124
137
  * instructions: 'Summarize and perform entity-extraction'
125
138
  * mailbox: { '/': 'dxn:echo:AAA:ZZZ' }
126
139
  * }
127
140
  */
128
- input: Schema.optional(Schema.mutable(Schema.Record({ key: Schema.String, value: Schema.Any }))),
141
+ input: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Any })),
129
142
  }).pipe(
130
- Type.Obj({
131
- typename: 'dxos.org/type/Trigger',
143
+ Type.object({
144
+ typename: 'org.dxos.type.trigger',
132
145
  version: '0.1.0',
133
146
  }),
147
+ Annotation.IconAnnotation.set({ icon: 'ph--lightning--regular', hue: 'yellow' }),
134
148
  SystemTypeAnnotation.set(true),
135
149
  );
136
- export interface Trigger extends Schema.Schema.Type<typeof Trigger_> {}
137
- export interface TriggerEncoded extends Schema.Schema.Encoded<typeof Trigger_> {}
138
- export const Trigger: Schema.Schema<Trigger, TriggerEncoded> = Trigger_;
150
+
151
+ export interface Trigger extends Schema.Schema.Type<typeof TriggerSchema> {}
152
+ export const Trigger: Type.Obj<Trigger> = TriggerSchema as any;
139
153
 
140
154
  export const make = (props: Obj.MakeProps<typeof Trigger>) => Obj.make(Trigger, props);