@dxos/functions 0.8.4-main.84f28bd → 0.8.4-main.8baae0fced
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +102 -5
- package/README.md +5 -7
- package/dist/lib/neutral/index.mjs +602 -0
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/types/src/index.d.ts +3 -6
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/protocol/functions-ai-http-client.d.ts +12 -0
- package/dist/types/src/protocol/functions-ai-http-client.d.ts.map +1 -0
- package/dist/types/src/protocol/functions-ai-http-client.test.d.ts +2 -0
- package/dist/types/src/protocol/functions-ai-http-client.test.d.ts.map +1 -0
- package/dist/types/src/protocol/index.d.ts +2 -0
- package/dist/types/src/protocol/index.d.ts.map +1 -0
- package/dist/types/src/protocol/protocol.d.ts +24 -0
- package/dist/types/src/protocol/protocol.d.ts.map +1 -0
- package/dist/types/src/protocol/protocol.test.d.ts +2 -0
- package/dist/types/src/protocol/protocol.test.d.ts.map +1 -0
- package/dist/types/src/sdk.d.ts +10 -0
- package/dist/types/src/sdk.d.ts.map +1 -0
- package/dist/types/src/services/credentials.d.ts +24 -27
- package/dist/types/src/services/credentials.d.ts.map +1 -1
- package/dist/types/src/services/function-invocation-service.d.ts +18 -0
- package/dist/types/src/services/function-invocation-service.d.ts.map +1 -0
- package/dist/types/src/services/index.d.ts +3 -8
- package/dist/types/src/services/index.d.ts.map +1 -1
- package/dist/types/src/services/queues.d.ts +1 -17
- package/dist/types/src/services/queues.d.ts.map +1 -1
- package/dist/types/src/services/tracing.d.ts +4 -14
- package/dist/types/src/services/tracing.d.ts.map +1 -1
- package/dist/types/src/types/index.d.ts +2 -0
- package/dist/types/src/types/index.d.ts.map +1 -0
- package/dist/types/src/types/url.d.ts +13 -0
- package/dist/types/src/types/url.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +29 -69
- package/src/index.ts +3 -8
- package/src/protocol/functions-ai-http-client.test.ts +105 -0
- package/src/protocol/functions-ai-http-client.ts +141 -0
- package/src/{executor → protocol}/index.ts +1 -1
- package/src/protocol/protocol.test.ts +58 -0
- package/src/protocol/protocol.ts +455 -0
- package/src/sdk.ts +31 -0
- package/src/services/credentials.ts +95 -35
- package/src/services/function-invocation-service.ts +36 -0
- package/src/services/index.ts +3 -8
- package/src/services/queues.ts +1 -38
- package/src/services/tracing.ts +4 -23
- package/src/{testing → types}/index.ts +1 -1
- package/src/types/url.ts +32 -0
- package/dist/lib/browser/bundler/index.mjs +0 -247
- package/dist/lib/browser/bundler/index.mjs.map +0 -7
- package/dist/lib/browser/chunk-54U464M4.mjs +0 -360
- package/dist/lib/browser/chunk-54U464M4.mjs.map +0 -7
- package/dist/lib/browser/edge/index.mjs +0 -69
- package/dist/lib/browser/edge/index.mjs.map +0 -7
- package/dist/lib/browser/index.mjs +0 -492
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/testing/index.mjs +0 -79
- package/dist/lib/browser/testing/index.mjs.map +0 -7
- package/dist/lib/node-esm/bundler/index.mjs +0 -249
- package/dist/lib/node-esm/bundler/index.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-XDSX35BS.mjs +0 -362
- package/dist/lib/node-esm/chunk-XDSX35BS.mjs.map +0 -7
- package/dist/lib/node-esm/edge/index.mjs +0 -71
- package/dist/lib/node-esm/edge/index.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -493
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/testing/index.mjs +0 -80
- package/dist/lib/node-esm/testing/index.mjs.map +0 -7
- package/dist/types/src/bundler/bundler.d.ts +0 -50
- package/dist/types/src/bundler/bundler.d.ts.map +0 -1
- package/dist/types/src/bundler/bundler.test.d.ts +0 -2
- package/dist/types/src/bundler/bundler.test.d.ts.map +0 -1
- package/dist/types/src/bundler/index.d.ts +0 -2
- package/dist/types/src/bundler/index.d.ts.map +0 -1
- package/dist/types/src/edge/functions.d.ts +0 -16
- package/dist/types/src/edge/functions.d.ts.map +0 -1
- package/dist/types/src/edge/index.d.ts +0 -2
- package/dist/types/src/edge/index.d.ts.map +0 -1
- package/dist/types/src/executor/executor.d.ts +0 -8
- package/dist/types/src/executor/executor.d.ts.map +0 -1
- package/dist/types/src/executor/index.d.ts +0 -2
- package/dist/types/src/executor/index.d.ts.map +0 -1
- package/dist/types/src/handler.d.ts +0 -61
- package/dist/types/src/handler.d.ts.map +0 -1
- package/dist/types/src/schema.d.ts +0 -38
- package/dist/types/src/schema.d.ts.map +0 -1
- package/dist/types/src/services/ai.d.ts +0 -12
- package/dist/types/src/services/ai.d.ts.map +0 -1
- package/dist/types/src/services/database.d.ts +0 -15
- package/dist/types/src/services/database.d.ts.map +0 -1
- package/dist/types/src/services/event-logger.d.ts +0 -37
- package/dist/types/src/services/event-logger.d.ts.map +0 -1
- package/dist/types/src/services/function-call-service.d.ts +0 -16
- package/dist/types/src/services/function-call-service.d.ts.map +0 -1
- package/dist/types/src/services/service-container.d.ts +0 -46
- package/dist/types/src/services/service-container.d.ts.map +0 -1
- package/dist/types/src/services/tool-resolver.d.ts +0 -14
- package/dist/types/src/services/tool-resolver.d.ts.map +0 -1
- package/dist/types/src/testing/index.d.ts +0 -2
- package/dist/types/src/testing/index.d.ts.map +0 -1
- package/dist/types/src/testing/logger.d.ts +0 -5
- package/dist/types/src/testing/logger.d.ts.map +0 -1
- package/dist/types/src/testing/services.d.ts +0 -71
- package/dist/types/src/testing/services.d.ts.map +0 -1
- package/dist/types/src/trace.d.ts +0 -124
- package/dist/types/src/trace.d.ts.map +0 -1
- package/dist/types/src/translations.d.ts +0 -12
- package/dist/types/src/translations.d.ts.map +0 -1
- package/dist/types/src/types.d.ts +0 -411
- package/dist/types/src/types.d.ts.map +0 -1
- package/dist/types/src/url.d.ts +0 -17
- package/dist/types/src/url.d.ts.map +0 -1
- package/src/bundler/bundler.test.ts +0 -59
- package/src/bundler/bundler.ts +0 -292
- package/src/bundler/index.ts +0 -5
- package/src/edge/functions.ts +0 -64
- package/src/edge/index.ts +0 -9
- package/src/executor/executor.ts +0 -54
- package/src/handler.ts +0 -113
- package/src/schema.ts +0 -57
- package/src/services/ai.ts +0 -32
- package/src/services/database.ts +0 -50
- package/src/services/event-logger.ts +0 -87
- package/src/services/function-call-service.ts +0 -64
- package/src/services/service-container.ts +0 -127
- package/src/services/tool-resolver.ts +0 -31
- package/src/testing/logger.ts +0 -16
- package/src/testing/services.ts +0 -174
- package/src/trace.ts +0 -180
- package/src/translations.ts +0 -20
- package/src/types.ts +0 -211
- package/src/url.ts +0 -52
package/src/bundler/bundler.ts
DELETED
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2023 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { FetchHttpClient, HttpClient } from '@effect/platform';
|
|
6
|
-
import { Duration, Effect, pipe, Schedule } from 'effect';
|
|
7
|
-
import { type BuildOptions, type Loader, build, initialize, type BuildResult, type Plugin } from 'esbuild-wasm';
|
|
8
|
-
|
|
9
|
-
import { subtleCrypto } from '@dxos/crypto';
|
|
10
|
-
import { runAndForwardErrors } from '@dxos/effect';
|
|
11
|
-
import { invariant } from '@dxos/invariant';
|
|
12
|
-
import { log } from '@dxos/log';
|
|
13
|
-
|
|
14
|
-
export type Import = {
|
|
15
|
-
moduleUrl: string;
|
|
16
|
-
defaultImport: boolean;
|
|
17
|
-
namedImports: string[];
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export type BundleOptions = {
|
|
21
|
-
/**
|
|
22
|
-
* Path to the source file on the local file system.
|
|
23
|
-
* If provided, the path will be used instead of the `source` code.
|
|
24
|
-
*/
|
|
25
|
-
path?: string;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Source code to bundle.
|
|
29
|
-
* Required if `path` is not provided.
|
|
30
|
-
*/
|
|
31
|
-
source?: string;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export type BundleResult = {
|
|
35
|
-
timestamp: number;
|
|
36
|
-
sourceHash?: Buffer;
|
|
37
|
-
imports?: Import[];
|
|
38
|
-
bundle?: string;
|
|
39
|
-
error?: any;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export type BundlerOptions = {
|
|
43
|
-
platform: BuildOptions['platform'];
|
|
44
|
-
sandboxedModules: string[];
|
|
45
|
-
remoteModules: Record<string, string>;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
let initialized: Promise<void>;
|
|
49
|
-
export const initializeBundler = async (options: { wasmUrl: string }) => {
|
|
50
|
-
await (initialized ??= initialize({
|
|
51
|
-
wasmURL: options.wasmUrl,
|
|
52
|
-
}));
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* ESBuild bundler.
|
|
57
|
-
*/
|
|
58
|
-
export class Bundler {
|
|
59
|
-
constructor(private readonly _options: BundlerOptions) {}
|
|
60
|
-
|
|
61
|
-
async bundle({ path, source }: BundleOptions): Promise<BundleResult> {
|
|
62
|
-
const { sandboxedModules: providedModules, ...options } = this._options;
|
|
63
|
-
|
|
64
|
-
const createResult = async (result?: Partial<BundleResult>) => {
|
|
65
|
-
return {
|
|
66
|
-
timestamp: Date.now(),
|
|
67
|
-
sourceHash: source ? Buffer.from(await subtleCrypto.digest('SHA-256', Buffer.from(source))) : undefined,
|
|
68
|
-
...result,
|
|
69
|
-
};
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
if (this._options.platform === 'browser') {
|
|
73
|
-
invariant(initialized, 'Compiler not initialized.');
|
|
74
|
-
await initialized;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const imports = source ? analyzeSourceFileImports(source) : [];
|
|
78
|
-
|
|
79
|
-
// https://esbuild.github.io/api/#build
|
|
80
|
-
try {
|
|
81
|
-
const result = await build({
|
|
82
|
-
platform: options.platform,
|
|
83
|
-
conditions: ['workerd', 'browser'],
|
|
84
|
-
metafile: true,
|
|
85
|
-
write: false,
|
|
86
|
-
entryPoints: [path ?? 'memory:main.tsx'],
|
|
87
|
-
bundle: true,
|
|
88
|
-
format: 'esm',
|
|
89
|
-
plugins: [
|
|
90
|
-
{
|
|
91
|
-
name: 'memory',
|
|
92
|
-
setup: (build) => {
|
|
93
|
-
build.onResolve({ filter: /^\.\/runtime\.js$/ }, ({ path }) => {
|
|
94
|
-
return { path, external: true };
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
build.onResolve({ filter: /^dxos:functions$/ }, ({ path }) => {
|
|
98
|
-
return { path: './runtime.js', external: true };
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
build.onResolve({ filter: /^memory:/ }, ({ path }) => {
|
|
102
|
-
return { path: path.split(':')[1], namespace: 'memory' };
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
build.onLoad({ filter: /.*/, namespace: 'memory' }, ({ path }) => {
|
|
106
|
-
if (path === 'main.tsx') {
|
|
107
|
-
return {
|
|
108
|
-
contents: source,
|
|
109
|
-
loader: 'tsx',
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
for (const module of providedModules) {
|
|
115
|
-
build.onResolve({ filter: new RegExp(`^${module}$`) }, ({ path }) => {
|
|
116
|
-
return { path, namespace: 'injected-module' };
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
build.onLoad({ filter: /.*/, namespace: 'injected-module' }, ({ path }) => {
|
|
121
|
-
const namedImports = imports.find((entry) => entry.moduleIdentifier === path)?.namedImports ?? [];
|
|
122
|
-
return {
|
|
123
|
-
contents: `
|
|
124
|
-
const { ${namedImports.join(',')} } = window.__DXOS_SANDBOX_MODULES__[${JSON.stringify(path)}];
|
|
125
|
-
export { ${namedImports.join(',')} };
|
|
126
|
-
export default window.__DXOS_SANDBOX_MODULES__[${JSON.stringify(path)}].default;
|
|
127
|
-
`,
|
|
128
|
-
loader: 'tsx',
|
|
129
|
-
};
|
|
130
|
-
});
|
|
131
|
-
},
|
|
132
|
-
},
|
|
133
|
-
httpPlugin,
|
|
134
|
-
],
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
log('compile complete', result.metafile);
|
|
138
|
-
|
|
139
|
-
return await createResult({
|
|
140
|
-
imports: this.analyzeImports(result),
|
|
141
|
-
bundle: result.outputFiles![0].text,
|
|
142
|
-
});
|
|
143
|
-
} catch (err) {
|
|
144
|
-
return await createResult({ error: err });
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// TODO(dmaretskyi): In the future we can replace the compiler with SWC with plugins running in WASM.
|
|
149
|
-
analyzeImports(result: BuildResult): Import[] {
|
|
150
|
-
invariant(result.outputFiles);
|
|
151
|
-
|
|
152
|
-
// TODO(dmaretskyi): Support import aliases and wildcard imports.
|
|
153
|
-
const parsedImports = allMatches(IMPORT_REGEX, result.outputFiles[0].text);
|
|
154
|
-
return Object.values(result.metafile!.outputs)[0].imports.map((entry): Import => {
|
|
155
|
-
const namedImports: string[] = [];
|
|
156
|
-
|
|
157
|
-
const parsedImport = parsedImports.find((capture) => capture?.[4] === entry.path);
|
|
158
|
-
if (parsedImport?.[2]) {
|
|
159
|
-
NAMED_IMPORTS_REGEX.lastIndex = 0;
|
|
160
|
-
const namedImportsMatch = NAMED_IMPORTS_REGEX.exec(parsedImport[2]);
|
|
161
|
-
if (namedImportsMatch) {
|
|
162
|
-
namedImportsMatch[1].split(',').forEach((importName) => {
|
|
163
|
-
namedImports.push(importName.trim());
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return {
|
|
169
|
-
moduleUrl: entry.path,
|
|
170
|
-
defaultImport: !!parsedImport?.[1],
|
|
171
|
-
namedImports,
|
|
172
|
-
};
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
analyzeSourceFileImports(code: string): {
|
|
177
|
-
defaultImportName: string;
|
|
178
|
-
namedImports: string[];
|
|
179
|
-
wildcardImportName: string;
|
|
180
|
-
moduleIdentifier: string;
|
|
181
|
-
quotes: string;
|
|
182
|
-
}[] {
|
|
183
|
-
// TODO(dmaretskyi): Support import aliases and wildcard imports.
|
|
184
|
-
const parsedImports = allMatches(IMPORT_REGEX, code);
|
|
185
|
-
return parsedImports.map((capture) => {
|
|
186
|
-
return {
|
|
187
|
-
defaultImportName: capture[1],
|
|
188
|
-
namedImports: capture[2]?.split(',').map((importName) => importName.trim()),
|
|
189
|
-
wildcardImportName: capture[3],
|
|
190
|
-
moduleIdentifier: capture[4],
|
|
191
|
-
quotes: capture[5],
|
|
192
|
-
};
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// https://regex101.com/r/FEN5ks/1
|
|
198
|
-
// https://stackoverflow.com/a/73265022
|
|
199
|
-
// $1 = default import name (can be non-existent)
|
|
200
|
-
// $2 = destructured exports (can be non-existent)
|
|
201
|
-
// $3 = wildcard import name (can be non-existent)
|
|
202
|
-
// $4 = module identifier
|
|
203
|
-
// $5 = quotes used (either ' or ")
|
|
204
|
-
const IMPORT_REGEX =
|
|
205
|
-
/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;
|
|
206
|
-
|
|
207
|
-
const NAMED_IMPORTS_REGEX = /[ \n\t]*{((?:[ \n\t]*[^ \n\t"'{}]+[ \n\t]*,?)+)}[ \n\t]*/gm;
|
|
208
|
-
|
|
209
|
-
const allMatches = (regex: RegExp, str: string) => {
|
|
210
|
-
regex.lastIndex = 0;
|
|
211
|
-
|
|
212
|
-
let match;
|
|
213
|
-
const matches = [];
|
|
214
|
-
while ((match = regex.exec(str))) {
|
|
215
|
-
matches.push(match);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return matches;
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
type ParsedImport = {
|
|
222
|
-
defaultImportName?: string;
|
|
223
|
-
namedImports: string[];
|
|
224
|
-
wildcardImportName?: string;
|
|
225
|
-
moduleIdentifier: string;
|
|
226
|
-
quotes: string;
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
const analyzeSourceFileImports = (code: string): ParsedImport[] => {
|
|
230
|
-
// TODO(dmaretskyi): Support import aliases and wildcard imports.
|
|
231
|
-
const parsedImports = allMatches(IMPORT_REGEX, code);
|
|
232
|
-
return parsedImports.map((capture) => {
|
|
233
|
-
return {
|
|
234
|
-
defaultImportName: capture[1],
|
|
235
|
-
namedImports: capture[2]
|
|
236
|
-
?.trim()
|
|
237
|
-
.slice(1, -1)
|
|
238
|
-
.split(',')
|
|
239
|
-
.map((importName) => importName.trim()),
|
|
240
|
-
wildcardImportName: capture[3],
|
|
241
|
-
moduleIdentifier: capture[4],
|
|
242
|
-
quotes: capture[5],
|
|
243
|
-
};
|
|
244
|
-
});
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
const MAX_RETRIES = 5;
|
|
248
|
-
const INITIAL_DELAY = 1_000;
|
|
249
|
-
|
|
250
|
-
const httpPlugin: Plugin = {
|
|
251
|
-
name: 'http',
|
|
252
|
-
setup: (build) => {
|
|
253
|
-
// Intercept import paths starting with "http:" and "https:" so esbuild doesn't attempt to map them to a file system location.
|
|
254
|
-
// Tag them with the "http-url" namespace to associate them with this plugin.
|
|
255
|
-
build.onResolve({ filter: /^https?:\/\// }, (args) => ({
|
|
256
|
-
path: args.path,
|
|
257
|
-
namespace: 'http-url',
|
|
258
|
-
}));
|
|
259
|
-
|
|
260
|
-
// We also want to intercept all import paths inside downloaded files and resolve them against the original URL.
|
|
261
|
-
// All of these files will be in the "http-url" namespace.
|
|
262
|
-
// Make sure to keep the newly resolved URL in the "http-url" namespace so imports inside it will also be resolved as URLs recursively.
|
|
263
|
-
build.onResolve({ filter: /.*/, namespace: 'http-url' }, (args) => ({
|
|
264
|
-
path: new URL(args.path, args.importer).toString(),
|
|
265
|
-
namespace: 'http-url',
|
|
266
|
-
}));
|
|
267
|
-
|
|
268
|
-
// When a URL is loaded, we want to actually download the content from the internet.
|
|
269
|
-
// 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.
|
|
270
|
-
build.onLoad({ filter: /.*/, namespace: 'http-url' }, async (args) => {
|
|
271
|
-
return Effect.gen(function* () {
|
|
272
|
-
const response = yield* HttpClient.get(args.path);
|
|
273
|
-
if (response.status !== 200) {
|
|
274
|
-
throw new Error(`failed to fetch: ${response.status}`);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
const text = yield* response.text;
|
|
278
|
-
return { contents: text, loader: 'jsx' as Loader };
|
|
279
|
-
}).pipe(
|
|
280
|
-
Effect.retry(
|
|
281
|
-
pipe(
|
|
282
|
-
Schedule.exponential(Duration.millis(INITIAL_DELAY)),
|
|
283
|
-
Schedule.jittered,
|
|
284
|
-
Schedule.intersect(Schedule.recurs(MAX_RETRIES - 1)),
|
|
285
|
-
),
|
|
286
|
-
),
|
|
287
|
-
Effect.provide(FetchHttpClient.layer),
|
|
288
|
-
runAndForwardErrors,
|
|
289
|
-
);
|
|
290
|
-
});
|
|
291
|
-
},
|
|
292
|
-
};
|
package/src/bundler/index.ts
DELETED
package/src/edge/functions.ts
DELETED
|
@@ -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
package/src/executor/executor.ts
DELETED
|
@@ -1,54 +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
|
-
import { runAndForwardErrors } from '@dxos/effect';
|
|
9
|
-
|
|
10
|
-
import type { FunctionContext, FunctionDefinition } from '../handler';
|
|
11
|
-
import type { ServiceContainer, Services } from '../services';
|
|
12
|
-
|
|
13
|
-
export class FunctionExecutor {
|
|
14
|
-
constructor(private readonly _services: ServiceContainer) {}
|
|
15
|
-
|
|
16
|
-
// TODO(dmaretskyi): Invocation context: queue, space, etc...
|
|
17
|
-
async invoke<F extends FunctionDefinition<any, any>>(
|
|
18
|
-
fnDef: F,
|
|
19
|
-
input: F extends FunctionDefinition<infer I, infer _O> ? I : never,
|
|
20
|
-
): Promise<F extends FunctionDefinition<infer _I, infer O> ? O : never> {
|
|
21
|
-
// Assert input matches schema
|
|
22
|
-
const assertInput = fnDef.inputSchema.pipe(Schema.asserts);
|
|
23
|
-
(assertInput as any)(input);
|
|
24
|
-
|
|
25
|
-
const context: FunctionContext = {
|
|
26
|
-
getService: this._services.getService.bind(this._services),
|
|
27
|
-
getSpace: async (_spaceId: SpaceId) => {
|
|
28
|
-
throw new Error('Not available. Use the database service instead.');
|
|
29
|
-
},
|
|
30
|
-
space: undefined,
|
|
31
|
-
get ai(): never {
|
|
32
|
-
throw new Error('Not available. Use the ai service instead.');
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const result = fnDef.handler({ context, data: input });
|
|
37
|
-
|
|
38
|
-
let data: unknown;
|
|
39
|
-
if (Effect.isEffect(result)) {
|
|
40
|
-
data = await (result as Effect.Effect<unknown, unknown, Services>).pipe(
|
|
41
|
-
Effect.provide(this._services.createLayer()),
|
|
42
|
-
runAndForwardErrors,
|
|
43
|
-
);
|
|
44
|
-
} else {
|
|
45
|
-
data = await result;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Assert output matches schema
|
|
49
|
-
const assertOutput = fnDef.outputSchema?.pipe(Schema.asserts);
|
|
50
|
-
(assertOutput as any)(data);
|
|
51
|
-
|
|
52
|
-
return data as any;
|
|
53
|
-
}
|
|
54
|
-
}
|
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 { 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
|
-
import type { Services } from './services';
|
|
15
|
-
|
|
16
|
-
// TODO(burdon): Model after http request. Ref Lambda/OpenFaaS.
|
|
17
|
-
// https://docs.aws.amazon.com/lambda/latest/dg/typescript-handler.html
|
|
18
|
-
// https://www.serverless.com/framework/docs/providers/aws/guide/serverless.yml/#functions
|
|
19
|
-
// https://www.npmjs.com/package/aws-lambda
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Function handler.
|
|
23
|
-
*/
|
|
24
|
-
export type FunctionHandler<TData = {}, TOutput = any> = (params: {
|
|
25
|
-
/**
|
|
26
|
-
* Services and context available to the function.
|
|
27
|
-
*/
|
|
28
|
-
context: FunctionContext;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Data passed as the input to the function.
|
|
32
|
-
* Must match the function's input schema.
|
|
33
|
-
* This will be the payload from the trigger or other data passed into the function in a workflow.
|
|
34
|
-
*/
|
|
35
|
-
data: TData;
|
|
36
|
-
}) => TOutput | Promise<TOutput> | Effect.Effect<TOutput, any, Services>;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Function context.
|
|
40
|
-
*/
|
|
41
|
-
export interface FunctionContext {
|
|
42
|
-
/**
|
|
43
|
-
* Resolves a service available to the function.
|
|
44
|
-
* @throws if the service is not available.
|
|
45
|
-
*/
|
|
46
|
-
getService: <T extends Context.Tag<any, any>>(tag: T) => Context.Tag.Service<T>;
|
|
47
|
-
|
|
48
|
-
getSpace: (spaceId: SpaceId) => Promise<SpaceAPI>;
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Space from which the function was invoked.
|
|
52
|
-
*/
|
|
53
|
-
space: SpaceAPI | undefined;
|
|
54
|
-
|
|
55
|
-
ai: AiServiceClient;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export interface FunctionContextAi {
|
|
59
|
-
// TODO(dmaretskyi): Refer to cloudflare AI docs for more comprehensive typedefs.
|
|
60
|
-
run(model: string, inputs: any, options?: any): Promise<any>;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
//
|
|
64
|
-
// API.
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
// TODO(dmaretskyi): Temporary API to get the queues working.
|
|
68
|
-
// TODO(dmaretskyi): To be replaced with integrating queues into echo.
|
|
69
|
-
export interface QueuesAPI {
|
|
70
|
-
queryQueue(queue: DXN, options?: {}): Promise<QueryResult>;
|
|
71
|
-
insertIntoQueue(queue: DXN, objects: HasId[]): Promise<void>;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Space interface available to functions.
|
|
76
|
-
*/
|
|
77
|
-
export interface SpaceAPI {
|
|
78
|
-
get id(): SpaceId;
|
|
79
|
-
|
|
80
|
-
get db(): EchoDatabase;
|
|
81
|
-
|
|
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> {}
|
package/src/services/ai.ts
DELETED
|
@@ -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
|
-
}
|
package/src/services/database.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { Context, Effect, Layer } from 'effect';
|
|
6
|
-
|
|
7
|
-
import type { Obj, Ref, Relation } from '@dxos/echo';
|
|
8
|
-
import type { EchoDatabase } from '@dxos/echo-db';
|
|
9
|
-
import type { DXN } from '@dxos/keys';
|
|
10
|
-
|
|
11
|
-
export class DatabaseService extends Context.Tag('DatabaseService')<
|
|
12
|
-
DatabaseService,
|
|
13
|
-
{
|
|
14
|
-
readonly db: EchoDatabase;
|
|
15
|
-
}
|
|
16
|
-
>() {
|
|
17
|
-
static notAvailable = Layer.succeed(DatabaseService, {
|
|
18
|
-
get db(): EchoDatabase {
|
|
19
|
-
throw new Error('Database not available');
|
|
20
|
-
},
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
static make = (db: EchoDatabase): Context.Tag.Service<DatabaseService> => {
|
|
24
|
-
return {
|
|
25
|
-
get db() {
|
|
26
|
-
return db;
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
static resolve: (dxn: DXN) => Effect.Effect<Obj.Any | Relation.Any, Error, DatabaseService> = Effect.fn(
|
|
32
|
-
function* (dxn) {
|
|
33
|
-
const { db } = yield* DatabaseService;
|
|
34
|
-
return yield* Effect.tryPromise({
|
|
35
|
-
try: () =>
|
|
36
|
-
db.graph.createRefResolver({ context: { space: db.spaceId } }).resolve(dxn) as Promise<
|
|
37
|
-
Obj.Any | Relation.Any
|
|
38
|
-
>,
|
|
39
|
-
catch: (error) => error as Error,
|
|
40
|
-
});
|
|
41
|
-
},
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
static loadRef: <T>(ref: Ref.Ref<T>) => Effect.Effect<T, Error, never> = Effect.fn(function* (ref) {
|
|
45
|
-
return yield* Effect.tryPromise({
|
|
46
|
-
try: () => ref.load(),
|
|
47
|
-
catch: (error) => error as Error,
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
}
|