@dxos/functions 0.8.3 → 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.
Files changed (172) hide show
  1. package/dist/lib/neutral/index.mjs +1229 -0
  2. package/dist/lib/neutral/index.mjs.map +7 -0
  3. package/dist/lib/neutral/meta.json +1 -0
  4. package/dist/types/src/errors.d.ts +121 -0
  5. package/dist/types/src/errors.d.ts.map +1 -0
  6. package/dist/types/src/example/fib.d.ts +7 -0
  7. package/dist/types/src/example/fib.d.ts.map +1 -0
  8. package/dist/types/src/example/forex-effect.d.ts +3 -0
  9. package/dist/types/src/example/forex-effect.d.ts.map +1 -0
  10. package/dist/types/src/example/index.d.ts +12 -0
  11. package/dist/types/src/example/index.d.ts.map +1 -0
  12. package/dist/types/src/example/reply.d.ts +3 -0
  13. package/dist/types/src/example/reply.d.ts.map +1 -0
  14. package/dist/types/src/example/sleep.d.ts +5 -0
  15. package/dist/types/src/example/sleep.d.ts.map +1 -0
  16. package/dist/types/src/index.d.ts +5 -6
  17. package/dist/types/src/index.d.ts.map +1 -1
  18. package/dist/types/src/operation-compatibility.test.d.ts +2 -0
  19. package/dist/types/src/operation-compatibility.test.d.ts.map +1 -0
  20. package/dist/types/src/protocol/functions-ai-http-client.d.ts +12 -0
  21. package/dist/types/src/protocol/functions-ai-http-client.d.ts.map +1 -0
  22. package/dist/types/src/protocol/index.d.ts +2 -0
  23. package/dist/types/src/protocol/index.d.ts.map +1 -0
  24. package/dist/types/src/protocol/protocol.d.ts +7 -0
  25. package/dist/types/src/protocol/protocol.d.ts.map +1 -0
  26. package/dist/types/src/protocol/protocol.test.d.ts +2 -0
  27. package/dist/types/src/protocol/protocol.test.d.ts.map +1 -0
  28. package/dist/types/src/sdk.d.ts +114 -0
  29. package/dist/types/src/sdk.d.ts.map +1 -0
  30. package/dist/types/src/services/credentials.d.ts +24 -4
  31. package/dist/types/src/services/credentials.d.ts.map +1 -1
  32. package/dist/types/src/services/event-logger.d.ts +74 -30
  33. package/dist/types/src/services/event-logger.d.ts.map +1 -1
  34. package/dist/types/src/services/function-invocation-service.d.ts +16 -0
  35. package/dist/types/src/services/function-invocation-service.d.ts.map +1 -0
  36. package/dist/types/src/services/index.d.ts +5 -6
  37. package/dist/types/src/services/index.d.ts.map +1 -1
  38. package/dist/types/src/services/queues.d.ts +35 -6
  39. package/dist/types/src/services/queues.d.ts.map +1 -1
  40. package/dist/types/src/services/tracing.d.ts +78 -5
  41. package/dist/types/src/services/tracing.d.ts.map +1 -1
  42. package/dist/types/src/types/Function.d.ts +52 -0
  43. package/dist/types/src/types/Function.d.ts.map +1 -0
  44. package/dist/types/src/types/Script.d.ts +21 -0
  45. package/dist/types/src/types/Script.d.ts.map +1 -0
  46. package/dist/types/src/types/Trigger.d.ts +121 -0
  47. package/dist/types/src/types/Trigger.d.ts.map +1 -0
  48. package/dist/types/src/types/TriggerEvent.d.ts +74 -0
  49. package/dist/types/src/types/TriggerEvent.d.ts.map +1 -0
  50. package/dist/types/src/types/index.d.ts +6 -0
  51. package/dist/types/src/types/index.d.ts.map +1 -0
  52. package/dist/types/src/types/url.d.ts +13 -0
  53. package/dist/types/src/types/url.d.ts.map +1 -0
  54. package/dist/types/tsconfig.tsbuildinfo +1 -1
  55. package/package.json +27 -65
  56. package/src/errors.ts +21 -0
  57. package/src/example/fib.ts +32 -0
  58. package/src/example/forex-effect.ts +40 -0
  59. package/src/example/index.ts +13 -0
  60. package/src/example/reply.ts +21 -0
  61. package/src/example/sleep.ts +24 -0
  62. package/src/index.ts +5 -8
  63. package/src/operation-compatibility.test.ts +185 -0
  64. package/src/protocol/functions-ai-http-client.ts +67 -0
  65. package/src/{executor → protocol}/index.ts +1 -1
  66. package/src/protocol/protocol.test.ts +59 -0
  67. package/src/protocol/protocol.ts +262 -0
  68. package/src/sdk.ts +289 -0
  69. package/src/services/credentials.ts +109 -5
  70. package/src/services/event-logger.ts +77 -37
  71. package/src/services/function-invocation-service.ts +37 -0
  72. package/src/services/index.ts +5 -6
  73. package/src/services/queues.ts +56 -11
  74. package/src/services/tracing.ts +151 -11
  75. package/src/types/Function.ts +82 -0
  76. package/src/types/Script.ts +34 -0
  77. package/src/types/Trigger.ts +143 -0
  78. package/src/types/TriggerEvent.ts +62 -0
  79. package/src/types/index.ts +9 -0
  80. package/src/types/url.ts +32 -0
  81. package/dist/lib/browser/bundler/index.mjs +0 -236
  82. package/dist/lib/browser/bundler/index.mjs.map +0 -7
  83. package/dist/lib/browser/chunk-WEFZUEL2.mjs +0 -300
  84. package/dist/lib/browser/chunk-WEFZUEL2.mjs.map +0 -7
  85. package/dist/lib/browser/edge/index.mjs +0 -69
  86. package/dist/lib/browser/edge/index.mjs.map +0 -7
  87. package/dist/lib/browser/index.mjs +0 -486
  88. package/dist/lib/browser/index.mjs.map +0 -7
  89. package/dist/lib/browser/meta.json +0 -1
  90. package/dist/lib/browser/testing/index.mjs +0 -28
  91. package/dist/lib/browser/testing/index.mjs.map +0 -7
  92. package/dist/lib/node/bundler/index.cjs +0 -260
  93. package/dist/lib/node/bundler/index.cjs.map +0 -7
  94. package/dist/lib/node/chunk-IJAE7FZK.cjs +0 -320
  95. package/dist/lib/node/chunk-IJAE7FZK.cjs.map +0 -7
  96. package/dist/lib/node/edge/index.cjs +0 -94
  97. package/dist/lib/node/edge/index.cjs.map +0 -7
  98. package/dist/lib/node/index.cjs +0 -522
  99. package/dist/lib/node/index.cjs.map +0 -7
  100. package/dist/lib/node/meta.json +0 -1
  101. package/dist/lib/node/testing/index.cjs +0 -43
  102. package/dist/lib/node/testing/index.cjs.map +0 -7
  103. package/dist/lib/node-esm/bundler/index.mjs +0 -238
  104. package/dist/lib/node-esm/bundler/index.mjs.map +0 -7
  105. package/dist/lib/node-esm/chunk-LIYPMWNQ.mjs +0 -302
  106. package/dist/lib/node-esm/chunk-LIYPMWNQ.mjs.map +0 -7
  107. package/dist/lib/node-esm/edge/index.mjs +0 -71
  108. package/dist/lib/node-esm/edge/index.mjs.map +0 -7
  109. package/dist/lib/node-esm/index.mjs +0 -487
  110. package/dist/lib/node-esm/index.mjs.map +0 -7
  111. package/dist/lib/node-esm/meta.json +0 -1
  112. package/dist/lib/node-esm/testing/index.mjs +0 -29
  113. package/dist/lib/node-esm/testing/index.mjs.map +0 -7
  114. package/dist/types/src/bundler/bundler.d.ts +0 -51
  115. package/dist/types/src/bundler/bundler.d.ts.map +0 -1
  116. package/dist/types/src/bundler/bundler.test.d.ts +0 -2
  117. package/dist/types/src/bundler/bundler.test.d.ts.map +0 -1
  118. package/dist/types/src/bundler/index.d.ts +0 -2
  119. package/dist/types/src/bundler/index.d.ts.map +0 -1
  120. package/dist/types/src/edge/functions.d.ts +0 -16
  121. package/dist/types/src/edge/functions.d.ts.map +0 -1
  122. package/dist/types/src/edge/index.d.ts +0 -2
  123. package/dist/types/src/edge/index.d.ts.map +0 -1
  124. package/dist/types/src/executor/executor.d.ts +0 -8
  125. package/dist/types/src/executor/executor.d.ts.map +0 -1
  126. package/dist/types/src/executor/index.d.ts +0 -2
  127. package/dist/types/src/executor/index.d.ts.map +0 -1
  128. package/dist/types/src/handler.d.ts +0 -64
  129. package/dist/types/src/handler.d.ts.map +0 -1
  130. package/dist/types/src/schema.d.ts +0 -38
  131. package/dist/types/src/schema.d.ts.map +0 -1
  132. package/dist/types/src/services/ai.d.ts +0 -12
  133. package/dist/types/src/services/ai.d.ts.map +0 -1
  134. package/dist/types/src/services/database.d.ts +0 -11
  135. package/dist/types/src/services/database.d.ts.map +0 -1
  136. package/dist/types/src/services/function-call-service.d.ts +0 -16
  137. package/dist/types/src/services/function-call-service.d.ts.map +0 -1
  138. package/dist/types/src/services/service-container.d.ts +0 -44
  139. package/dist/types/src/services/service-container.d.ts.map +0 -1
  140. package/dist/types/src/testing/index.d.ts +0 -2
  141. package/dist/types/src/testing/index.d.ts.map +0 -1
  142. package/dist/types/src/testing/logger.d.ts +0 -5
  143. package/dist/types/src/testing/logger.d.ts.map +0 -1
  144. package/dist/types/src/testing/services.d.ts +0 -13
  145. package/dist/types/src/testing/services.d.ts.map +0 -1
  146. package/dist/types/src/trace.d.ts +0 -124
  147. package/dist/types/src/trace.d.ts.map +0 -1
  148. package/dist/types/src/translations.d.ts +0 -12
  149. package/dist/types/src/translations.d.ts.map +0 -1
  150. package/dist/types/src/types.d.ts +0 -411
  151. package/dist/types/src/types.d.ts.map +0 -1
  152. package/dist/types/src/url.d.ts +0 -17
  153. package/dist/types/src/url.d.ts.map +0 -1
  154. package/src/bundler/bundler.test.ts +0 -59
  155. package/src/bundler/bundler.ts +0 -270
  156. package/src/bundler/index.ts +0 -5
  157. package/src/edge/functions.ts +0 -64
  158. package/src/edge/index.ts +0 -9
  159. package/src/executor/executor.ts +0 -47
  160. package/src/handler.ts +0 -113
  161. package/src/schema.ts +0 -57
  162. package/src/services/ai.ts +0 -32
  163. package/src/services/database.ts +0 -28
  164. package/src/services/function-call-service.ts +0 -64
  165. package/src/services/service-container.ts +0 -109
  166. package/src/testing/index.ts +0 -5
  167. package/src/testing/logger.ts +0 -16
  168. package/src/testing/services.ts +0 -32
  169. package/src/trace.ts +0 -180
  170. package/src/translations.ts +0 -20
  171. package/src/types.ts +0 -211
  172. package/src/url.ts +0 -52
@@ -1,270 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import { type BuildOptions } from 'esbuild';
6
- import { build, initialize, type BuildResult, type Plugin } from 'esbuild-wasm';
7
-
8
- import { subtleCrypto } from '@dxos/crypto';
9
- import { invariant } from '@dxos/invariant';
10
- import { log } from '@dxos/log';
11
-
12
- export type Import = {
13
- moduleUrl: string;
14
- defaultImport: boolean;
15
- namedImports: string[];
16
- };
17
-
18
- export type BundleOptions = {
19
- /**
20
- * Path to the source file on the local file system.
21
- * If provided, the path will be used instead of the `source` code.
22
- */
23
- path?: string;
24
-
25
- /**
26
- * Source code to bundle.
27
- * Required if `path` is not provided.
28
- */
29
- source?: string;
30
- };
31
-
32
- export type BundleResult = {
33
- timestamp: number;
34
- sourceHash?: Buffer;
35
- imports?: Import[];
36
- bundle?: string;
37
- error?: any;
38
- };
39
-
40
- export type BundlerOptions = {
41
- platform: BuildOptions['platform'];
42
- sandboxedModules: string[];
43
- remoteModules: Record<string, string>;
44
- };
45
-
46
- let initialized: Promise<void>;
47
- export const initializeBundler = async (options: { wasmUrl: string }) => {
48
- await (initialized ??= initialize({
49
- wasmURL: options.wasmUrl,
50
- }));
51
- };
52
-
53
- /**
54
- * ESBuild bundler.
55
- */
56
- export class Bundler {
57
- constructor(private readonly _options: BundlerOptions) {}
58
-
59
- async bundle({ path, source }: BundleOptions): Promise<BundleResult> {
60
- const { sandboxedModules: providedModules, ...options } = this._options;
61
-
62
- const createResult = async (result?: Partial<BundleResult>) => {
63
- return {
64
- timestamp: Date.now(),
65
- sourceHash: source ? Buffer.from(await subtleCrypto.digest('SHA-256', Buffer.from(source))) : undefined,
66
- ...result,
67
- };
68
- };
69
-
70
- if (this._options.platform === 'browser') {
71
- invariant(initialized, 'Compiler not initialized.');
72
- await initialized;
73
- }
74
-
75
- const imports = source ? analyzeSourceFileImports(source) : [];
76
-
77
- // https://esbuild.github.io/api/#build
78
- try {
79
- const result = await build({
80
- platform: options.platform,
81
- conditions: ['workerd', 'browser'],
82
- metafile: true,
83
- write: false,
84
- entryPoints: [path ?? 'memory:main.tsx'],
85
- bundle: true,
86
- format: 'esm',
87
- plugins: [
88
- {
89
- name: 'memory',
90
- setup: (build) => {
91
- build.onResolve({ filter: /^\.\/runtime\.js$/ }, ({ path }) => {
92
- return { path, external: true };
93
- });
94
-
95
- build.onResolve({ filter: /^dxos:functions$/ }, ({ path }) => {
96
- return { path: './runtime.js', external: true };
97
- });
98
-
99
- build.onResolve({ filter: /^memory:/ }, ({ path }) => {
100
- return { path: path.split(':')[1], namespace: 'memory' };
101
- });
102
-
103
- build.onLoad({ filter: /.*/, namespace: 'memory' }, ({ path }) => {
104
- if (path === 'main.tsx') {
105
- return {
106
- contents: source,
107
- loader: 'tsx',
108
- };
109
- }
110
- });
111
-
112
- for (const module of providedModules) {
113
- build.onResolve({ filter: new RegExp(`^${module}$`) }, ({ path }) => {
114
- return { path, namespace: 'injected-module' };
115
- });
116
- }
117
-
118
- build.onLoad({ filter: /.*/, namespace: 'injected-module' }, ({ path }) => {
119
- const namedImports = imports.find((entry) => entry.moduleIdentifier === path)?.namedImports ?? [];
120
- return {
121
- contents: `
122
- const { ${namedImports.join(',')} } = window.__DXOS_SANDBOX_MODULES__[${JSON.stringify(path)}];
123
- export { ${namedImports.join(',')} };
124
- export default window.__DXOS_SANDBOX_MODULES__[${JSON.stringify(path)}].default;
125
- `,
126
- loader: 'tsx',
127
- };
128
- });
129
- },
130
- },
131
- httpPlugin,
132
- ],
133
- });
134
-
135
- log('compile complete', result.metafile);
136
-
137
- return await createResult({
138
- imports: this.analyzeImports(result),
139
- bundle: result.outputFiles![0].text,
140
- });
141
- } catch (err) {
142
- return await createResult({ error: err });
143
- }
144
- }
145
-
146
- // TODO(dmaretskyi): In the future we can replace the compiler with SWC with plugins running in WASM.
147
- analyzeImports(result: BuildResult): Import[] {
148
- invariant(result.outputFiles);
149
-
150
- // TODO(dmaretskyi): Support import aliases and wildcard imports.
151
- const parsedImports = allMatches(IMPORT_REGEX, result.outputFiles[0].text);
152
- return Object.values(result.metafile!.outputs)[0].imports.map((entry): Import => {
153
- const namedImports: string[] = [];
154
-
155
- const parsedImport = parsedImports.find((capture) => capture?.[4] === entry.path);
156
- if (parsedImport?.[2]) {
157
- NAMED_IMPORTS_REGEX.lastIndex = 0;
158
- const namedImportsMatch = NAMED_IMPORTS_REGEX.exec(parsedImport[2]);
159
- if (namedImportsMatch) {
160
- namedImportsMatch[1].split(',').forEach((importName) => {
161
- namedImports.push(importName.trim());
162
- });
163
- }
164
- }
165
-
166
- return {
167
- moduleUrl: entry.path,
168
- defaultImport: !!parsedImport?.[1],
169
- namedImports,
170
- };
171
- });
172
- }
173
-
174
- analyzeSourceFileImports(code: string): {
175
- defaultImportName: string;
176
- namedImports: string[];
177
- wildcardImportName: string;
178
- moduleIdentifier: string;
179
- quotes: string;
180
- }[] {
181
- // TODO(dmaretskyi): Support import aliases and wildcard imports.
182
- const parsedImports = allMatches(IMPORT_REGEX, code);
183
- return parsedImports.map((capture) => {
184
- return {
185
- defaultImportName: capture[1],
186
- namedImports: capture[2]?.split(',').map((importName) => importName.trim()),
187
- wildcardImportName: capture[3],
188
- moduleIdentifier: capture[4],
189
- quotes: capture[5],
190
- };
191
- });
192
- }
193
- }
194
-
195
- // https://regex101.com/r/FEN5ks/1
196
- // https://stackoverflow.com/a/73265022
197
- // $1 = default import name (can be non-existent)
198
- // $2 = destructured exports (can be non-existent)
199
- // $3 = wildcard import name (can be non-existent)
200
- // $4 = module identifier
201
- // $5 = quotes used (either ' or ")
202
- const IMPORT_REGEX =
203
- /import(?:(?:(?:[ \n\t]+([^ *\n\t{},]+)[ \n\t]*(?:,|[ \n\t]+))?([ \n\t]*{(?:[ \n\t]*[^ \n\t"'{}]+[ \n\t]*,?)+})?[ \n\t]*)|[ \n\t]*\*[ \n\t]*as[ \n\t]+([^ \n\t{}]+)[ \n\t]+)from[ \n\t]*(?:['"])([^'"\n]+)(['"])/gm;
204
-
205
- const NAMED_IMPORTS_REGEX = /[ \n\t]*{((?:[ \n\t]*[^ \n\t"'{}]+[ \n\t]*,?)+)}[ \n\t]*/gm;
206
-
207
- const allMatches = (regex: RegExp, str: string) => {
208
- regex.lastIndex = 0;
209
-
210
- let match;
211
- const matches = [];
212
- while ((match = regex.exec(str))) {
213
- matches.push(match);
214
- }
215
-
216
- return matches;
217
- };
218
-
219
- type ParsedImport = {
220
- defaultImportName?: string;
221
- namedImports: string[];
222
- wildcardImportName?: string;
223
- moduleIdentifier: string;
224
- quotes: string;
225
- };
226
-
227
- const analyzeSourceFileImports = (code: string): ParsedImport[] => {
228
- // TODO(dmaretskyi): Support import aliases and wildcard imports.
229
- const parsedImports = allMatches(IMPORT_REGEX, code);
230
- return parsedImports.map((capture) => {
231
- return {
232
- defaultImportName: capture[1],
233
- namedImports: capture[2]
234
- ?.trim()
235
- .slice(1, -1)
236
- .split(',')
237
- .map((importName) => importName.trim()),
238
- wildcardImportName: capture[3],
239
- moduleIdentifier: capture[4],
240
- quotes: capture[5],
241
- };
242
- });
243
- };
244
-
245
- const httpPlugin: Plugin = {
246
- name: 'http',
247
- setup: (build) => {
248
- // Intercept import paths starting with "http:" and "https:" so esbuild doesn't attempt to map them to a file system location.
249
- // Tag them with the "http-url" namespace to associate them with this plugin.
250
- build.onResolve({ filter: /^https?:\/\// }, (args) => ({
251
- path: args.path,
252
- namespace: 'http-url',
253
- }));
254
-
255
- // We also want to intercept all import paths inside downloaded files and resolve them against the original URL.
256
- // All of these files will be in the "http-url" namespace.
257
- // Make sure to keep the newly resolved URL in the "http-url" namespace so imports inside it will also be resolved as URLs recursively.
258
- build.onResolve({ filter: /.*/, namespace: 'http-url' }, (args) => ({
259
- path: new URL(args.path, args.importer).toString(),
260
- namespace: 'http-url',
261
- }));
262
-
263
- // When a URL is loaded, we want to actually download the content from the internet.
264
- // This has just enough logic to be able to handle the example import from unpkg.com but in reality this would probably need to be more complex.
265
- build.onLoad({ filter: /.*/, namespace: 'http-url' }, async (args) => {
266
- const response = await fetch(args.path);
267
- return { contents: await response.text(), loader: 'jsx' };
268
- });
269
- },
270
- };
@@ -1,5 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- export * from './bundler';
@@ -1,64 +0,0 @@
1
- //
2
- // Copyright 2024 DXOS.org
3
- //
4
-
5
- import { type DID } from 'iso-did/types';
6
-
7
- import { type Client } from '@dxos/client';
8
- import { createEdgeIdentity } from '@dxos/client/edge';
9
- import { EdgeHttpClient } from '@dxos/edge-client';
10
- import { invariant } from '@dxos/invariant';
11
- import type { PublicKey } from '@dxos/keys';
12
- import { log } from '@dxos/log';
13
- import { type UploadFunctionResponseBody } from '@dxos/protocols';
14
-
15
- export type UploadWorkerArgs = {
16
- client: Client;
17
- source: string;
18
- version: string;
19
- name?: string;
20
- functionId?: string;
21
- ownerPublicKey: PublicKey;
22
- };
23
-
24
- export const uploadWorkerFunction = async ({
25
- client,
26
- version,
27
- source,
28
- name,
29
- functionId,
30
- ownerPublicKey,
31
- }: UploadWorkerArgs): Promise<UploadFunctionResponseBody> => {
32
- const edgeUrl = client.config.values.runtime?.services?.edge?.url;
33
- invariant(edgeUrl, 'Edge is not configured.');
34
- const edgeClient = new EdgeHttpClient(edgeUrl);
35
- const edgeIdentity = createEdgeIdentity(client);
36
- edgeClient.setIdentity(edgeIdentity);
37
- const response = await edgeClient.uploadFunction(
38
- { functionId },
39
- { name, version, script: source, ownerPublicKey: ownerPublicKey.toHex() },
40
- );
41
-
42
- // TODO(burdon): Edge service log.
43
- log.info('Uploaded', {
44
- identityKey: edgeIdentity.identityKey,
45
- functionId,
46
- name,
47
- source: source.length,
48
- response,
49
- });
50
-
51
- return response;
52
- };
53
-
54
- export const incrementSemverPatch = (version: string): string => {
55
- const [major, minor, patch] = version.split('.');
56
- const patchNum = Number(patch);
57
- invariant(!Number.isNaN(patchNum), `Unexpected function version format: ${version}`);
58
- return [major, minor, String(patchNum + 1)].join('.');
59
- };
60
-
61
- // TODO(burdon): Factor out.
62
- export const publicKeyToDid = (key: PublicKey): DID => {
63
- return `did:key:${key.toHex()}`;
64
- };
package/src/edge/index.ts DELETED
@@ -1,9 +0,0 @@
1
- //
2
- // Copyright 2024 DXOS.org
3
- //
4
-
5
- // NOTE: This module is separate because it uses the edge client.
6
- // This means it cannot be used on edge itself.
7
- // TODO(wittjosiah): Factor out.
8
-
9
- export * from './functions';
@@ -1,47 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import { Effect, Schema } from 'effect';
6
-
7
- import type { SpaceId } from '@dxos/client/echo';
8
-
9
- import type { FunctionContext, FunctionDefinition } from '../handler';
10
- import type { ServiceContainer } from '../services';
11
-
12
- export class FunctionExecutor {
13
- constructor(private readonly _services: ServiceContainer) {}
14
-
15
- // TODO(dmaretskyi): Invocation context: queue, space, etc...
16
- async invoke<F extends FunctionDefinition<any, any>>(
17
- fnDef: F,
18
- input: F extends FunctionDefinition<infer I, infer _O> ? I : never,
19
- ): Promise<F extends FunctionDefinition<infer _I, infer O> ? O : never> {
20
- // Assert input matches schema
21
- const assertInput = fnDef.inputSchema.pipe(Schema.asserts);
22
- (assertInput as any)(input);
23
-
24
- const context: FunctionContext = {
25
- getService: this._services.getService.bind(this._services),
26
- getSpace: async (_spaceId: SpaceId) => {
27
- throw new Error('Not available. Use the database service instead.');
28
- },
29
- space: undefined,
30
- get ai(): never {
31
- throw new Error('Not available. Use the ai service instead.');
32
- },
33
- };
34
-
35
- const result = await fnDef.handler({ context, data: input });
36
-
37
- // Assert output matches schema
38
- const assertOutput = fnDef.outputSchema?.pipe(Schema.asserts);
39
- (assertOutput as any)(result);
40
-
41
- if (Effect.isEffect(result)) {
42
- return Effect.runPromise(result as any);
43
- }
44
-
45
- return result as any;
46
- }
47
- }
package/src/handler.ts DELETED
@@ -1,113 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import { Schema, type Context, type Effect } from 'effect';
6
-
7
- import { type AiServiceClient } from '@dxos/ai';
8
- // import { type Space } from '@dxos/client/echo';
9
- import type { CoreDatabase, EchoDatabase } from '@dxos/echo-db';
10
- import { type HasId } from '@dxos/echo-schema';
11
- import { type SpaceId, type DXN } from '@dxos/keys';
12
- import { type QueryResult } from '@dxos/protocols';
13
-
14
- // TODO(burdon): Model after http request. Ref Lambda/OpenFaaS.
15
- // https://docs.aws.amazon.com/lambda/latest/dg/typescript-handler.html
16
- // https://www.serverless.com/framework/docs/providers/aws/guide/serverless.yml/#functions
17
- // https://www.npmjs.com/package/aws-lambda
18
-
19
- /**
20
- * Function handler.
21
- */
22
- export type FunctionHandler<TData = {}, TOutput = any> = (params: {
23
- /**
24
- * Services and context available to the function.
25
- */
26
- context: FunctionContext;
27
-
28
- /**
29
- * Data passed as the input to the function.
30
- * Must match the function's input schema.
31
- * This will be the payload from the trigger or other data passed into the function in a workflow.
32
- */
33
- data: TData;
34
- }) => TOutput | Promise<TOutput> | Effect.Effect<TOutput, any>;
35
-
36
- /**
37
- * Function context.
38
- */
39
- export interface FunctionContext {
40
- /**
41
- * Resolves a service available to the function.
42
- * @throws if the service is not available.
43
- */
44
- getService: <T extends Context.Tag<any, any>>(tag: T) => Context.Tag.Service<T>;
45
-
46
- getSpace: (spaceId: SpaceId) => Promise<SpaceAPI>;
47
-
48
- /**
49
- * Space from which the function was invoked.
50
- */
51
- space: SpaceAPI | undefined;
52
-
53
- ai: AiServiceClient;
54
- }
55
-
56
- export interface FunctionContextAi {
57
- // TODO(dmaretskyi): Refer to cloudflare AI docs for more comprehensive typedefs.
58
- run(model: string, inputs: any, options?: any): Promise<any>;
59
- }
60
-
61
- //
62
- // API.
63
- //
64
-
65
- // TODO(dmaretskyi): Temporary API to get the queues working.
66
- // TODO(dmaretskyi): To be replaced with integrating queues into echo.
67
- export interface QueuesAPI {
68
- queryQueue(queue: DXN, options?: {}): Promise<QueryResult>;
69
- insertIntoQueue(queue: DXN, objects: HasId[]): Promise<void>;
70
- }
71
-
72
- /**
73
- * Space interface available to functions.
74
- */
75
- export interface SpaceAPI {
76
- get id(): SpaceId;
77
- /**
78
- * @deprecated
79
- */
80
- get crud(): CoreDatabase;
81
- get db(): EchoDatabase;
82
- // TODO(dmaretskyi): Align with echo api --- queues.get(id).append(items);
83
- get queues(): QueuesAPI;
84
- }
85
-
86
- // TODO(wittjosiah): Queues are incompatible.
87
- const __assertFunctionSpaceIsCompatibleWithTheClientSpace = () => {
88
- // const _: SpaceAPI = {} as Space;
89
- };
90
-
91
- export type FunctionDefinition<T = {}, O = any> = {
92
- description?: string;
93
- inputSchema: Schema.Schema<T, any>;
94
- outputSchema?: Schema.Schema<O, any>;
95
- handler: FunctionHandler<T, O>;
96
- };
97
-
98
- // TODO(dmaretskyi): Bind input type to function handler.
99
- export const defineFunction = <T, O>(params: FunctionDefinition<T, O>): FunctionDefinition<T, O> => {
100
- if (!Schema.isSchema(params.inputSchema)) {
101
- throw new Error('Input schema must be a valid schema');
102
- }
103
- if (typeof params.handler !== 'function') {
104
- throw new Error('Handler must be a function');
105
- }
106
-
107
- return {
108
- description: params.description,
109
- inputSchema: params.inputSchema,
110
- outputSchema: params.outputSchema ?? Schema.Any,
111
- handler: params.handler,
112
- };
113
- };
package/src/schema.ts DELETED
@@ -1,57 +0,0 @@
1
- //
2
- // Copyright 2024 DXOS.org
3
- //
4
-
5
- import { Schema } from 'effect';
6
-
7
- import { Type } from '@dxos/echo';
8
- import { JsonSchemaType, LabelAnnotation, Ref } from '@dxos/echo-schema';
9
- import { DataType } from '@dxos/schema';
10
-
11
- /**
12
- * Source script.
13
- */
14
- export const ScriptType = Schema.Struct({
15
- name: Schema.optional(Schema.String),
16
- description: Schema.optional(Schema.String),
17
- // TODO(burdon): Change to hash of deployed content.
18
- // Whether source has changed since last deploy.
19
- changed: Schema.optional(Schema.Boolean),
20
- source: Ref(DataType.Text),
21
- }).pipe(
22
- Type.Obj({
23
- typename: 'dxos.org/type/Script',
24
- version: '0.1.0',
25
- }),
26
- LabelAnnotation.set(['name']),
27
- );
28
-
29
- export interface ScriptType extends Schema.Schema.Type<typeof ScriptType> {}
30
-
31
- /**
32
- * Function deployment.
33
- */
34
- export const FunctionType = Schema.Struct({
35
- // TODO(burdon): Rename to id/uri?
36
- name: Schema.NonEmptyString,
37
- version: Schema.String,
38
-
39
- description: Schema.optional(Schema.String),
40
-
41
- // Reference to a source script if it exists within ECHO.
42
- // TODO(burdon): Don't ref ScriptType directly (core).
43
- source: Schema.optional(Ref(ScriptType)),
44
-
45
- inputSchema: Schema.optional(JsonSchemaType),
46
- outputSchema: Schema.optional(JsonSchemaType),
47
-
48
- // Local binding to a function name.
49
- binding: Schema.optional(Schema.String),
50
- }).pipe(
51
- Type.Obj({
52
- typename: 'dxos.org/type/Function',
53
- version: '0.1.0',
54
- }),
55
- LabelAnnotation.set(['name']),
56
- );
57
- export interface FunctionType extends Schema.Schema.Type<typeof FunctionType> {}
@@ -1,32 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import { Context, Layer } from 'effect';
6
-
7
- import type { AiServiceClient } from '@dxos/ai';
8
-
9
- // TODO(burdon): Move to @dxos/ai.
10
- export class AiService extends Context.Tag('AiService')<
11
- AiService,
12
- {
13
- readonly client: AiServiceClient;
14
- }
15
- >() {
16
- static make = (client: AiServiceClient): Context.Tag.Service<AiService> => {
17
- return {
18
- get client() {
19
- return client;
20
- },
21
- };
22
- };
23
-
24
- static makeLayer = (client: AiServiceClient): Layer.Layer<AiService> =>
25
- Layer.succeed(AiService, AiService.make(client));
26
-
27
- static notAvailable = Layer.succeed(AiService, {
28
- get client(): AiServiceClient {
29
- throw new Error('AiService not available');
30
- },
31
- });
32
- }
@@ -1,28 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import { Context, Layer } from 'effect';
6
-
7
- import type { EchoDatabase } from '@dxos/echo-db';
8
-
9
- export class DatabaseService extends Context.Tag('DatabaseService')<
10
- DatabaseService,
11
- {
12
- readonly db: EchoDatabase;
13
- }
14
- >() {
15
- static notAvailable = Layer.succeed(DatabaseService, {
16
- get db(): EchoDatabase {
17
- throw new Error('Database not available');
18
- },
19
- });
20
-
21
- static make = (db: EchoDatabase): Context.Tag.Service<DatabaseService> => {
22
- return {
23
- get db() {
24
- return db;
25
- },
26
- };
27
- };
28
- }
@@ -1,64 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import { Context } from 'effect';
6
-
7
- import type { SpaceId } from '@dxos/keys';
8
-
9
- /**
10
- * Allows calling into other functions.
11
- */
12
- export class FunctionCallService extends Context.Tag('FunctionCallService')<
13
- FunctionCallService,
14
- {
15
- callFunction(deployedFunctionId: string, input: any, spaceId?: SpaceId): Promise<any>;
16
- }
17
- >() {
18
- static fromClient(baseUrl: string, spaceId: SpaceId): Context.Tag.Service<FunctionCallService> {
19
- return {
20
- callFunction: async (deployedFunctionId: string, input: any) => {
21
- const url = getInvocationUrl(deployedFunctionId, baseUrl, { spaceId });
22
- const result = await fetch(url, {
23
- method: 'POST',
24
- headers: { 'Content-Type': 'application/json' },
25
- body: JSON.stringify(input),
26
- });
27
- if (result.status >= 300 || result.status < 200) {
28
- throw new Error('Failed to invoke function', { cause: new Error(`HTTP error: ${await result.text()}`) });
29
- }
30
- return await result.json();
31
- },
32
- };
33
- }
34
-
35
- static mock = () => {
36
- return {
37
- callFunction: async (deployedFunctionId: string, input: any) => {
38
- return input;
39
- },
40
- };
41
- };
42
- }
43
-
44
- // TODO(dmaretskyi): Reconcile with `getInvocationUrl` in `@dxos/functions/edge`.
45
- const getInvocationUrl = (functionUrl: string, edgeUrl: string, options: InvocationOptions = {}) => {
46
- const baseUrl = new URL('functions/', edgeUrl);
47
-
48
- // Leading slashes cause the URL to be treated as an absolute path.
49
- const relativeUrl = functionUrl.replace(/^\//, '');
50
- const url = new URL(`./${relativeUrl}`, baseUrl.toString());
51
- options.spaceId && url.searchParams.set('spaceId', options.spaceId);
52
- options.subjectId && url.searchParams.set('subjectId', options.subjectId);
53
- url.protocol = isSecure(url.protocol) ? 'https' : 'http';
54
- return url.toString();
55
- };
56
-
57
- const isSecure = (protocol: string) => {
58
- return protocol === 'https:' || protocol === 'wss:';
59
- };
60
-
61
- type InvocationOptions = {
62
- spaceId?: SpaceId;
63
- subjectId?: string;
64
- };