@dxos/edge-compute 0.0.0

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 (70) hide show
  1. package/LICENSE +105 -0
  2. package/dist/lib/neutral/bundler/index.mjs +213 -0
  3. package/dist/lib/neutral/bundler/index.mjs.map +7 -0
  4. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
  5. package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +7 -0
  6. package/dist/lib/neutral/chunk-RN6KVGN4.mjs +63 -0
  7. package/dist/lib/neutral/chunk-RN6KVGN4.mjs.map +7 -0
  8. package/dist/lib/neutral/index.mjs +343 -0
  9. package/dist/lib/neutral/index.mjs.map +7 -0
  10. package/dist/lib/neutral/meta.json +1 -0
  11. package/dist/lib/neutral/native/index.mjs +186 -0
  12. package/dist/lib/neutral/native/index.mjs.map +7 -0
  13. package/dist/lib/neutral/testing/index.mjs +41 -0
  14. package/dist/lib/neutral/testing/index.mjs.map +7 -0
  15. package/dist/types/src/EdgeOperationInvoker.d.ts +21 -0
  16. package/dist/types/src/EdgeOperationInvoker.d.ts.map +1 -0
  17. package/dist/types/src/EdgeProcessManager.d.ts +14 -0
  18. package/dist/types/src/EdgeProcessManager.d.ts.map +1 -0
  19. package/dist/types/src/EdgeTriggerManager.d.ts +14 -0
  20. package/dist/types/src/EdgeTriggerManager.d.ts.map +1 -0
  21. package/dist/types/src/FunctionsServiceClient.d.ts +65 -0
  22. package/dist/types/src/FunctionsServiceClient.d.ts.map +1 -0
  23. package/dist/types/src/bundler/bundler.d.ts +38 -0
  24. package/dist/types/src/bundler/bundler.d.ts.map +1 -0
  25. package/dist/types/src/bundler/bundler.test.d.ts +2 -0
  26. package/dist/types/src/bundler/bundler.test.d.ts.map +1 -0
  27. package/dist/types/src/bundler/index.d.ts +2 -0
  28. package/dist/types/src/bundler/index.d.ts.map +1 -0
  29. package/dist/types/src/bundler/plugins/embedded-vendored-packages.d.ts +3 -0
  30. package/dist/types/src/bundler/plugins/embedded-vendored-packages.d.ts.map +1 -0
  31. package/dist/types/src/bundler/plugins/http-plugin-esbuild.d.ts +34 -0
  32. package/dist/types/src/bundler/plugins/http-plugin-esbuild.d.ts.map +1 -0
  33. package/dist/types/src/bundler/plugins/index.d.ts +4 -0
  34. package/dist/types/src/bundler/plugins/index.d.ts.map +1 -0
  35. package/dist/types/src/bundler/plugins/r2-vendored-packages.d.ts +5 -0
  36. package/dist/types/src/bundler/plugins/r2-vendored-packages.d.ts.map +1 -0
  37. package/dist/types/src/edge-client.d.ts +39 -0
  38. package/dist/types/src/edge-client.d.ts.map +1 -0
  39. package/dist/types/src/index.d.ts +6 -0
  40. package/dist/types/src/index.d.ts.map +1 -0
  41. package/dist/types/src/native/bundler.d.ts +15 -0
  42. package/dist/types/src/native/bundler.d.ts.map +1 -0
  43. package/dist/types/src/native/index.d.ts +2 -0
  44. package/dist/types/src/native/index.d.ts.map +1 -0
  45. package/dist/types/src/testing/index.d.ts +2 -0
  46. package/dist/types/src/testing/index.d.ts.map +1 -0
  47. package/dist/types/src/testing/logger.d.ts +4 -0
  48. package/dist/types/src/testing/logger.d.ts.map +1 -0
  49. package/dist/types/src/testing/services.d.ts +53 -0
  50. package/dist/types/src/testing/services.d.ts.map +1 -0
  51. package/dist/types/tsconfig.tsbuildinfo +1 -0
  52. package/package.json +95 -0
  53. package/src/EdgeOperationInvoker.ts +65 -0
  54. package/src/EdgeProcessManager.ts +47 -0
  55. package/src/EdgeTriggerManager.ts +70 -0
  56. package/src/FunctionsServiceClient.ts +189 -0
  57. package/src/bundler/bundler.test.ts +108 -0
  58. package/src/bundler/bundler.ts +232 -0
  59. package/src/bundler/index.ts +5 -0
  60. package/src/bundler/plugins/embedded-vendored-packages.ts +39 -0
  61. package/src/bundler/plugins/http-plugin-esbuild.ts +77 -0
  62. package/src/bundler/plugins/index.ts +7 -0
  63. package/src/bundler/plugins/r2-vendored-packages.ts +35 -0
  64. package/src/edge-client.ts +156 -0
  65. package/src/index.ts +9 -0
  66. package/src/native/bundler.ts +231 -0
  67. package/src/native/index.ts +5 -0
  68. package/src/testing/index.ts +5 -0
  69. package/src/testing/logger.ts +14 -0
  70. package/src/testing/services.ts +95 -0
package/package.json ADDED
@@ -0,0 +1,95 @@
1
+ {
2
+ "name": "@dxos/edge-compute",
3
+ "version": "0.0.0",
4
+ "description": "EDGE compute: function deploy, scripts, and Edge implementations of Remote* runtime interfaces.",
5
+ "homepage": "https://dxos.org",
6
+ "bugs": "https://github.com/dxos/dxos/issues",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/dxos/dxos"
10
+ },
11
+ "license": "FSL-1.1-Apache-2.0",
12
+ "author": "info@dxos.org",
13
+ "sideEffects": false,
14
+ "type": "module",
15
+ "exports": {
16
+ ".": {
17
+ "source": "./src/index.ts",
18
+ "types": "./dist/types/src/index.d.ts",
19
+ "default": "./dist/lib/neutral/index.mjs"
20
+ },
21
+ "./testing": {
22
+ "source": "./src/testing/index.ts",
23
+ "types": "./dist/types/src/testing/index.d.ts",
24
+ "default": "./dist/lib/neutral/testing/index.mjs"
25
+ },
26
+ "./bundler": {
27
+ "source": "./src/bundler/index.ts",
28
+ "types": "./dist/types/src/bundler/index.d.ts",
29
+ "default": "./dist/lib/neutral/bundler/index.mjs"
30
+ },
31
+ "./native": {
32
+ "source": "./src/native/index.ts",
33
+ "types": "./dist/types/src/native/index.d.ts",
34
+ "default": "./dist/lib/neutral/native/index.mjs"
35
+ }
36
+ },
37
+ "types": "dist/types/src/index.d.ts",
38
+ "typesVersions": {
39
+ "*": {
40
+ "testing": [
41
+ "dist/types/src/testing/index.d.ts"
42
+ ],
43
+ "bundler": [
44
+ "dist/types/src/bundler/index.d.ts"
45
+ ],
46
+ "native": [
47
+ "dist/types/src/native/index.d.ts"
48
+ ]
49
+ }
50
+ },
51
+ "files": [
52
+ "dist/lib",
53
+ "dist/types",
54
+ "src"
55
+ ],
56
+ "dependencies": {
57
+ "@effect-atom/atom": "^0.5.3",
58
+ "@effect/platform": "0.96.2",
59
+ "effect": "3.21.4",
60
+ "esbuild": "0.28.1",
61
+ "esbuild-wasm": "^0.28.0",
62
+ "iso-did": "^1.6.0",
63
+ "@dxos/client": "0.10.0",
64
+ "@dxos/compute-runtime": "0.10.0",
65
+ "@dxos/echo": "0.10.0",
66
+ "@dxos/context": "0.10.0",
67
+ "@dxos/compute": "0.10.0",
68
+ "@dxos/crypto": "0.10.0",
69
+ "@dxos/echo-client": "0.10.0",
70
+ "@dxos/edge-client": "0.10.0",
71
+ "@dxos/effect": "0.10.0",
72
+ "@dxos/errors": "0.10.0",
73
+ "@dxos/functions-runtime-cloudflare": "0.10.0",
74
+ "@dxos/invariant": "0.10.0",
75
+ "@dxos/keys": "0.10.0",
76
+ "@dxos/node-std": "0.10.0",
77
+ "@dxos/util": "0.10.0",
78
+ "@dxos/protocols": "0.10.0",
79
+ "@dxos/log": "0.10.0"
80
+ },
81
+ "devDependencies": {
82
+ "@effect/vitest": "0.29.0",
83
+ "vitest": "4.1.10"
84
+ },
85
+ "peerDependencies": {
86
+ "effect": "3.21.4"
87
+ },
88
+ "publishConfig": {
89
+ "access": "public"
90
+ },
91
+ "scripts": {
92
+ "publish-runtime": "pnpm run secrets && node ./scripts/build-modules.mjs && node --env-file=.env ./scripts/upload-modules.mjs --env dev",
93
+ "secrets": "node ./scripts/secrets.mjs"
94
+ }
95
+ }
@@ -0,0 +1,65 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ // @import-as-namespace
6
+
7
+ import * as Effect from 'effect/Effect';
8
+ import * as Layer from 'effect/Layer';
9
+
10
+ import { type Client, ClientService } from '@dxos/client';
11
+ import { FunctionError } from '@dxos/compute';
12
+ import { RemoteOperationInvoker } from '@dxos/compute-runtime';
13
+ import { type Context as DxosContext } from '@dxos/context';
14
+ import { type SpaceId } from '@dxos/keys';
15
+
16
+ import { createEdgeClient } from './edge-client';
17
+
18
+ type EdgeClient = ReturnType<typeof createEdgeClient>;
19
+
20
+ const make = (getEdgeClient: () => EdgeClient, spaceId?: SpaceId): RemoteOperationInvoker.Invoker => ({
21
+ invoke: <I, O>(ctx: DxosContext, deployedId: string, input: I): Effect.Effect<O> =>
22
+ Effect.gen(function* () {
23
+ const cleanedId = deployedId.replace(/^\//, '');
24
+ return yield* Effect.promise(() =>
25
+ getEdgeClient().invokeFunction(ctx, { functionId: cleanedId, spaceId }, input),
26
+ ).pipe(Effect.mapError(FunctionError.wrap()), Effect.orDie);
27
+ }),
28
+ });
29
+
30
+ /**
31
+ * For tests: provide a pre-built edge client.
32
+ */
33
+ export const fromEdgeClient = (
34
+ edgeClient: EdgeClient,
35
+ spaceId?: SpaceId,
36
+ ): Layer.Layer<RemoteOperationInvoker.Service> =>
37
+ Layer.succeed(
38
+ RemoteOperationInvoker.Service,
39
+ make(() => edgeClient, spaceId),
40
+ );
41
+
42
+ /**
43
+ * Build from a `Client`, deferring edge-client creation until first invoke
44
+ * (identity may be absent at boot).
45
+ */
46
+ export const fromClient = (client: Client, spaceId?: SpaceId): Layer.Layer<RemoteOperationInvoker.Service> => {
47
+ let cached: EdgeClient | undefined;
48
+ return Layer.succeed(
49
+ RemoteOperationInvoker.Service,
50
+ make(() => (cached ??= createEdgeClient(client)), spaceId),
51
+ );
52
+ };
53
+
54
+ /**
55
+ * Build from the ambient `ClientService`.
56
+ */
57
+ export const layer = (spaceId?: SpaceId): Layer.Layer<RemoteOperationInvoker.Service, never, ClientService> =>
58
+ Layer.effect(
59
+ RemoteOperationInvoker.Service,
60
+ Effect.gen(function* () {
61
+ const client = yield* ClientService;
62
+ let cached: EdgeClient | undefined;
63
+ return make(() => (cached ??= createEdgeClient(client)), spaceId);
64
+ }),
65
+ );
@@ -0,0 +1,47 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ // @import-as-namespace
6
+
7
+ import { Atom, Registry } from '@effect-atom/atom';
8
+ import * as Effect from 'effect/Effect';
9
+ import * as Layer from 'effect/Layer';
10
+
11
+ import { type Client } from '@dxos/client';
12
+ import { type Process } from '@dxos/compute';
13
+ import { RemoteProcessManager } from '@dxos/compute-runtime';
14
+
15
+ /**
16
+ * EDGE implementation of {@link RemoteProcessManager.Service}.
17
+ *
18
+ * The EDGE worker exposes no process-tree endpoint yet (see Decision D3), so
19
+ * this currently returns an empty tree. Once an endpoint lands, poll it into
20
+ * `processTreeAtom` here.
21
+ */
22
+ const make = (): Layer.Layer<RemoteProcessManager.Service, never, Registry.AtomRegistry> =>
23
+ Layer.effect(
24
+ RemoteProcessManager.Service,
25
+ Effect.gen(function* () {
26
+ const registry = yield* Registry.AtomRegistry;
27
+ // TODO(edge): Populate from an EDGE process-tree endpoint once available.
28
+ const processTreeAtom = Atom.make<readonly Process.Info[]>([]);
29
+ registry.mount(processTreeAtom);
30
+ return {
31
+ processTree: Effect.sync(() => registry.get(processTreeAtom)),
32
+ processTreeAtom,
33
+ } satisfies RemoteProcessManager.Manager;
34
+ }),
35
+ );
36
+
37
+ /**
38
+ * Build from a `Client`. Currently ignores the client until an EDGE
39
+ * process-tree endpoint exists.
40
+ */
41
+ export const fromClient = (_client: Client): Layer.Layer<RemoteProcessManager.Service, never, Registry.AtomRegistry> =>
42
+ make();
43
+
44
+ /**
45
+ * Empty EDGE process manager (no endpoint yet).
46
+ */
47
+ export const layer: Layer.Layer<RemoteProcessManager.Service, never, Registry.AtomRegistry> = make();
@@ -0,0 +1,70 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ // @import-as-namespace
6
+
7
+ import { Atom, Registry } from '@effect-atom/atom';
8
+ import * as Effect from 'effect/Effect';
9
+ import * as Layer from 'effect/Layer';
10
+
11
+ import { type Client, ClientService } from '@dxos/client';
12
+ import { type Trigger } from '@dxos/compute';
13
+ import { RemoteTriggerManager } from '@dxos/compute-runtime';
14
+ import { type SpaceId } from '@dxos/keys';
15
+
16
+ /**
17
+ * EDGE implementation of {@link RemoteTriggerManager.Service}.
18
+ *
19
+ * Edge triggers are ECHO objects replicated into the local database, so the
20
+ * aggregate {@link TriggerMonitor} already surfaces them (marked
21
+ * `environment: 'edge'`) from that database view; the `triggers` atom here is
22
+ * therefore left empty to avoid double-counting. It exists as the seam through
23
+ * which EDGE dispatcher runtime status can be layered in once a mapping to
24
+ * {@link Trigger.State} is available.
25
+ *
26
+ * `invokeTrigger` is not yet supported over the wire (edge triggers run
27
+ * autonomously on the EDGE dispatcher).
28
+ */
29
+ const make = (_spaceId?: SpaceId): Layer.Layer<RemoteTriggerManager.Service, never, Registry.AtomRegistry> =>
30
+ Layer.effect(
31
+ RemoteTriggerManager.Service,
32
+ Effect.gen(function* () {
33
+ const registry = yield* Registry.AtomRegistry;
34
+ // TODO(edge): Poll `EdgeHttpClient.getTriggersDispatcherStatus` and enrich edge trigger state here.
35
+ const triggers = Atom.make<readonly Trigger.State[]>([]);
36
+ registry.mount(triggers);
37
+ return {
38
+ triggers,
39
+ invokeTrigger: () => Effect.die(new Error('Remote (EDGE) trigger invocation is not supported')),
40
+ } satisfies RemoteTriggerManager.Manager;
41
+ }),
42
+ );
43
+
44
+ /**
45
+ * Build from a `Client`.
46
+ */
47
+ export const fromClient = (
48
+ _client: Client,
49
+ spaceId?: SpaceId,
50
+ ): Layer.Layer<RemoteTriggerManager.Service, never, Registry.AtomRegistry> => make(spaceId);
51
+
52
+ /**
53
+ * Build from the ambient `ClientService`.
54
+ */
55
+ export const layer = (
56
+ spaceId?: SpaceId,
57
+ ): Layer.Layer<RemoteTriggerManager.Service, never, Registry.AtomRegistry | ClientService> =>
58
+ Layer.effect(
59
+ RemoteTriggerManager.Service,
60
+ Effect.gen(function* () {
61
+ yield* ClientService;
62
+ const registry = yield* Registry.AtomRegistry;
63
+ const triggers = Atom.make<readonly Trigger.State[]>([]);
64
+ registry.mount(triggers);
65
+ return {
66
+ triggers,
67
+ invokeTrigger: () => Effect.die(new Error('Remote (EDGE) trigger invocation is not supported')),
68
+ } satisfies RemoteTriggerManager.Manager;
69
+ }),
70
+ );
@@ -0,0 +1,189 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type Client } from '@dxos/client';
6
+ import { FunctionError, Operation } from '@dxos/compute';
7
+ import { FUNCTIONS_META_KEY } from '@dxos/compute-runtime';
8
+ import { FunctionServiceError } from '@dxos/compute-runtime';
9
+ import { type Context } from '@dxos/context';
10
+ import { Obj } from '@dxos/echo';
11
+ import { type EdgeHttpClient, type TriggersDispatcherStatus } from '@dxos/edge-client';
12
+ import { invariant } from '@dxos/invariant';
13
+ import { type EntityId, type SpaceId } from '@dxos/keys';
14
+ import { log } from '@dxos/log';
15
+ import { type FunctionRuntimeKind, type SerializedError } from '@dxos/protocols';
16
+ import { safeParseJson } from '@dxos/util';
17
+
18
+ import { createEdgeClient } from './edge-client';
19
+
20
+ // TODO(wittjosiah): Copied from @dxos/functions-simulator-cloudflare.
21
+ export type InvokeResult =
22
+ | {
23
+ _kind: 'success';
24
+ /**
25
+ * The output of the function.
26
+ */
27
+ result: unknown;
28
+ }
29
+ | {
30
+ _kind: 'error';
31
+ error: SerializedError;
32
+ };
33
+
34
+ export type FunctionDeployOptions = {
35
+ name?: string;
36
+ version: string;
37
+
38
+ /** Service-side function id in case this function is a re-upload of a previous version. */
39
+ functionId?: string;
40
+
41
+ /** Owner identity DID (`did:halo:…`). Must equal the authenticated presenter DID. */
42
+ ownerUri: string;
43
+
44
+ runtime?: FunctionRuntimeKind;
45
+
46
+ /** Path of the entry point file in the assets table. */
47
+ entryPoint: string;
48
+
49
+ assets: {
50
+ // TODO(dmaretskyi): Allow passing strings and setting mime-type.
51
+ [path: string]: Uint8Array;
52
+ };
53
+ };
54
+
55
+ export type FunctionInvokeOptions = {
56
+ /**
57
+ * Space in which the function is invoked.
58
+ * Binds the Database.Service injected into the function to this space.
59
+ * Without this, the function will not have access to any database.
60
+ */
61
+ spaceId?: SpaceId;
62
+
63
+ /**
64
+ * CPU time limit in milliseconds.
65
+ * See Cloudflare docs for details.
66
+ */
67
+ cpuTimeLimit?: number;
68
+
69
+ /**
70
+ * Maximum number of subrequests.
71
+ * See Cloudflare docs for details.
72
+ */
73
+ subrequestsLimit?: number;
74
+ };
75
+
76
+ export class FunctionsServiceClient {
77
+ static fromClient(client: Client) {
78
+ return new FunctionsServiceClient(createEdgeClient(client));
79
+ }
80
+
81
+ #edgeClient: EdgeHttpClient;
82
+
83
+ constructor(edgeClient: EdgeHttpClient) {
84
+ this.#edgeClient = edgeClient;
85
+ }
86
+
87
+ /**
88
+ * Deploys a function to the EDGE service.
89
+ */
90
+ async deploy(ctx: Context, request: FunctionDeployOptions): Promise<Operation.PersistentOperation> {
91
+ try {
92
+ invariant(
93
+ Object.keys(request.assets).every((path) => !path.startsWith('/')),
94
+ 'Asset paths must be relative',
95
+ );
96
+ const response = await this.#edgeClient.uploadFunction(
97
+ ctx,
98
+ { functionId: request.functionId },
99
+ {
100
+ name: request.name,
101
+ version: request.version,
102
+ ownerUri: request.ownerUri,
103
+ entryPoint: request.entryPoint,
104
+ assets: request.assets,
105
+ runtime: request.runtime,
106
+ },
107
+ { retry: { count: 3 }, auth: true },
108
+ );
109
+ log.verbose('deploy result', { ...response });
110
+
111
+ return Obj.make(Operation.PersistentOperation, {
112
+ [Obj.Meta]: {
113
+ keys: [{ source: FUNCTIONS_META_KEY, id: response.functionId }],
114
+ key: response.meta.key,
115
+ version: response.version,
116
+ },
117
+ name: response.meta.name ?? 'Unnamed function',
118
+ description: response.meta.description,
119
+ inputSchema: response.meta.inputSchema,
120
+ outputSchema: response.meta.outputSchema,
121
+ });
122
+ } catch (error) {
123
+ throw FunctionServiceError.wrap({ message: 'Failed to deploy function', ifTypeDiffers: true })(error);
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Queries the EDGE service for deployed functions.
129
+ */
130
+ // TODO(dmaretskyi): Add query filters.
131
+ async query(ctx: Context): Promise<Operation.PersistentOperation[]> {
132
+ try {
133
+ const response = await this.#edgeClient.listFunctions(ctx);
134
+ return response.uploadedFunctions.flatMap((record: any) => {
135
+ const latest = record.latestVersion ?? {};
136
+ const versionMeta = safeParseJson<any>(latest.versionMetaJSON);
137
+ if (!versionMeta) {
138
+ return [];
139
+ }
140
+ const fn = Obj.make(Operation.PersistentOperation, {
141
+ [Obj.Meta]: {
142
+ keys: [{ source: FUNCTIONS_META_KEY, id: record.id }],
143
+ key: versionMeta.key,
144
+ version: latest?.version ?? '0.0.0',
145
+ },
146
+ name: versionMeta.name ?? versionMeta.key ?? record.id,
147
+ updated: record?.updated !== undefined ? new Date(record.updated).toISOString() : undefined,
148
+ description: versionMeta.description,
149
+ inputSchema: versionMeta.inputSchema,
150
+ outputSchema: versionMeta.outputSchema,
151
+ });
152
+ return [fn];
153
+ });
154
+ } catch (error) {
155
+ throw FunctionServiceError.wrap({ message: 'Failed to query functions', ifTypeDiffers: true })(error);
156
+ }
157
+ }
158
+
159
+ async invoke(ctx: Context, func: Operation.PersistentOperation, input: unknown, options?: FunctionInvokeOptions) {
160
+ const functionId = Obj.getMeta(func).keys.find((key) => key.source === FUNCTIONS_META_KEY)?.id;
161
+ if (!functionId) {
162
+ throw new FunctionServiceError({ message: 'No identifier for the function at the EDGE service' });
163
+ }
164
+ // COMPAT: Previously functionId was a URL `/<guid>`. Now it's just the `<guid>`.
165
+ const cleanedId = functionId.replace(/^\//, '');
166
+ try {
167
+ return await this.#edgeClient.invokeFunction(
168
+ ctx,
169
+ {
170
+ functionId: cleanedId,
171
+ spaceId: options?.spaceId,
172
+ cpuTimeLimit: options?.cpuTimeLimit,
173
+ subrequestsLimit: options?.subrequestsLimit,
174
+ },
175
+ input,
176
+ );
177
+ } catch (error) {
178
+ throw FunctionError.wrap({ message: 'Failed to invoke function', ifTypeDiffers: true })(error);
179
+ }
180
+ }
181
+
182
+ async forceRunCronTrigger(ctx: Context, spaceId: SpaceId, triggerId: EntityId): Promise<InvokeResult> {
183
+ return (await this.#edgeClient.forceRunCronTrigger(ctx, spaceId, triggerId)) as InvokeResult;
184
+ }
185
+
186
+ async getTriggersDispatcherStatus(ctx: Context, spaceId: SpaceId): Promise<TriggersDispatcherStatus> {
187
+ return this.#edgeClient.getTriggersDispatcherStatus(ctx, spaceId);
188
+ }
189
+ }
@@ -0,0 +1,108 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ // @ts-ignore
6
+ import wasmUrl from 'esbuild-wasm/esbuild.wasm?url';
7
+ import { assert, beforeAll, describe, expect, test } from 'vitest';
8
+
9
+ import { isNode, trim } from '@dxos/util';
10
+
11
+ import { bundleFunction, initializeBundler } from './bundler';
12
+
13
+ // SKipped because requies CDN
14
+ describe.skip('Bundler', () => {
15
+ beforeAll(async () => {
16
+ if (!isNode()) {
17
+ await initializeBundler({ wasmUrl });
18
+ }
19
+ });
20
+
21
+ test('Basic', async () => {
22
+ const result = await bundleFunction({
23
+ source: `
24
+ export default function handler () {
25
+ return 100;
26
+ }
27
+ `,
28
+ });
29
+ assert(!('error' in result), 'error should not exist');
30
+ expect(Object.keys(result.assets)).toBeGreaterThan(1);
31
+ });
32
+
33
+ test('Import', async () => {
34
+ const result = await bundleFunction({
35
+ source: `
36
+ import { Filter } from './runtime.js';
37
+
38
+ export default function handler () {
39
+ return Filter.typename('org.dxos.type.example');
40
+ }
41
+ `,
42
+ });
43
+ assert(!('error' in result), 'error should not exist');
44
+ expect(Object.keys(result.assets)).toBeGreaterThan(1);
45
+ });
46
+
47
+ // TODO(dmaretskyi): Flaky on CI.
48
+ test.skip('HTTPS Import', async () => {
49
+ const result = await bundleFunction({
50
+ source: `
51
+ import { invariant } from 'https://esm.sh/@dxos/invariant';
52
+ invariant(true);
53
+ `,
54
+ });
55
+ assert(!('error' in result), 'error should not exist');
56
+ expect(Object.keys(result.assets)).toBeGreaterThan(1);
57
+ });
58
+
59
+ test('Error', async () => {
60
+ const result = await bundleFunction({
61
+ source: "import missing from './module'; export default () => missing();",
62
+ });
63
+ assert('error' in result, 'error should exist');
64
+ });
65
+
66
+ test('forex-effect', { timeout: 60_000 }, async () => {
67
+ const result = await bundleFunction({
68
+ source: trim`
69
+ //
70
+ // Copyright 2025 DXOS.org
71
+ //
72
+
73
+ import { defineFunction } from '@dxos/functions';
74
+ import { FetchHttpClient, HttpClient, HttpClientRequest } from '@effect/platform';
75
+ import * as Effect from 'effect/Effect';
76
+ import * as Schedule from 'effect/Schedule';
77
+ import * as Schema from 'effect/Schema';
78
+
79
+ export default defineFunction({
80
+ key: 'org.dxos.script.forex-effect',
81
+ name: 'Forex Effect',
82
+ description: 'Returns the exchange rate between two currencies.',
83
+
84
+ inputSchema: Schema.Struct({
85
+ from: Schema.String.annotations({ description: 'The source currency' }),
86
+ to: Schema.String.annotations({ description: 'The target currency' }),
87
+ }),
88
+
89
+ outputSchema: Schema.String.annotations({ description: 'The exchange rate between the two currencies' }),
90
+
91
+ handler: Effect.fnUntraced(function* ({ data: { from, to } }) {
92
+ const res = yield* HttpClientRequest.get(\`https://free.ratesdb.com/v1/rates?from=\${from}&to=\${to}\`).pipe(
93
+ HttpClient.execute,
94
+ Effect.flatMap((res: any) => res.json),
95
+ Effect.timeout('1 second'),
96
+ Effect.retry(Schedule.exponential(1_000).pipe(Schedule.compose(Schedule.recurs(3)))),
97
+ Effect.scoped,
98
+ );
99
+
100
+ return (res as any).data.rates[to].toString();
101
+ }, Effect.provide(FetchHttpClient.layer)),
102
+ });
103
+ `,
104
+ });
105
+ assert(!('error' in result), 'error should not exist');
106
+ expect(Object.keys(result.assets)).toBeGreaterThan(1);
107
+ });
108
+ });