@dxos/functions-runtime-cloudflare 0.8.4-main.66e292d
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 +8 -0
- package/README.md +47 -0
- package/dist/lib/browser/index.mjs +481 -0
- package/dist/lib/browser/index.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -0
- package/dist/lib/node-esm/index.mjs +483 -0
- package/dist/lib/node-esm/index.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -0
- package/dist/types/src/functions-client.d.ts +32 -0
- package/dist/types/src/functions-client.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +3 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/internal/adapter.d.ts +12 -0
- package/dist/types/src/internal/adapter.d.ts.map +1 -0
- package/dist/types/src/internal/data-service-impl.d.ts +25 -0
- package/dist/types/src/internal/data-service-impl.d.ts.map +1 -0
- package/dist/types/src/internal/index.d.ts +2 -0
- package/dist/types/src/internal/index.d.ts.map +1 -0
- package/dist/types/src/internal/query-service-impl.d.ts +18 -0
- package/dist/types/src/internal/query-service-impl.d.ts.map +1 -0
- package/dist/types/src/internal/queue-service-impl.d.ts +12 -0
- package/dist/types/src/internal/queue-service-impl.d.ts.map +1 -0
- package/dist/types/src/internal/service-container.d.ts +25 -0
- package/dist/types/src/internal/service-container.d.ts.map +1 -0
- package/dist/types/src/queues-api.d.ts +22 -0
- package/dist/types/src/queues-api.d.ts.map +1 -0
- package/dist/types/src/space-proxy.d.ts +25 -0
- package/dist/types/src/space-proxy.d.ts.map +1 -0
- package/dist/types/src/types.d.ts +31 -0
- package/dist/types/src/types.d.ts.map +1 -0
- package/dist/types/src/wrap-handler-for-cloudflare.d.ts +6 -0
- package/dist/types/src/wrap-handler-for-cloudflare.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +47 -0
- package/src/functions-client.ts +81 -0
- package/src/index.ts +6 -0
- package/src/internal/adapter.ts +48 -0
- package/src/internal/data-service-impl.ts +93 -0
- package/src/internal/index.ts +5 -0
- package/src/internal/query-service-impl.ts +91 -0
- package/src/internal/queue-service-impl.ts +23 -0
- package/src/internal/service-container.ts +54 -0
- package/src/queues-api.ts +38 -0
- package/src/space-proxy.ts +65 -0
- package/src/types.ts +40 -0
- package/src/wrap-handler-for-cloudflare.ts +132 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import type { JsonSchemaType } from '@dxos/echo/internal';
|
|
6
|
+
import { invariant } from '@dxos/invariant';
|
|
7
|
+
import { SpaceId } from '@dxos/keys';
|
|
8
|
+
import { log } from '@dxos/log';
|
|
9
|
+
import { EdgeResponse } from '@dxos/protocols';
|
|
10
|
+
import type { EdgeFunctionEnv, FunctionProtocol } from '@dxos/protocols';
|
|
11
|
+
|
|
12
|
+
import { ServiceContainer } from './internal';
|
|
13
|
+
import { FUNCTION_ROUTE_HEADER, type FunctionMetadata, FunctionRouteValue } from './types';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Wraps a user function in a Cloudflare-compatible handler.
|
|
17
|
+
*/
|
|
18
|
+
export const wrapHandlerForCloudflare = (func: FunctionProtocol.Func): ExportedHandlerFetchHandler<any> => {
|
|
19
|
+
return async (request: Request, env: EdgeFunctionEnv.Env): Promise<Response> => {
|
|
20
|
+
// TODO(dmaretskyi): Should theÓ scope name reflect the function name?
|
|
21
|
+
// TODO(mykola): Wrap in withCleanAutomergeWasmState;
|
|
22
|
+
// TODO(mykola): Wrap in withNewExecutionContext;
|
|
23
|
+
// Meta route is used to get the input schema of the function by the functions service.
|
|
24
|
+
if (request.headers.get(FUNCTION_ROUTE_HEADER) === FunctionRouteValue.Meta) {
|
|
25
|
+
log.info('>>> meta', { func });
|
|
26
|
+
return handleFunctionMetaCall(func, request);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const spaceId = new URL(request.url).searchParams.get('spaceId');
|
|
31
|
+
if (spaceId) {
|
|
32
|
+
if (!SpaceId.isValid(spaceId)) {
|
|
33
|
+
return new Response('Invalid spaceId', { status: 400 });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const serviceContainer = new ServiceContainer({}, env.DATA_SERVICE, env.QUEUE_SERVICE);
|
|
38
|
+
const context = await createFunctionContext({
|
|
39
|
+
serviceContainer,
|
|
40
|
+
contextSpaceId: spaceId as SpaceId | undefined,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return EdgeResponse.success(await invokeFunction(func, context, request));
|
|
44
|
+
} catch (error: any) {
|
|
45
|
+
log.error('error invoking function', { error, stack: error.stack });
|
|
46
|
+
return EdgeResponse.failure({
|
|
47
|
+
message: error?.message ?? 'Internal error',
|
|
48
|
+
error,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const invokeFunction = async (func: FunctionProtocol.Func, context: FunctionProtocol.Context, request: Request) => {
|
|
55
|
+
// TODO(dmaretskyi): For some reason requests get wrapped like this.
|
|
56
|
+
const { data } = await decodeRequest(request);
|
|
57
|
+
|
|
58
|
+
return func.handler({
|
|
59
|
+
context,
|
|
60
|
+
data,
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const decodeRequest = async (request: Request) => {
|
|
65
|
+
const {
|
|
66
|
+
data: { bodyText, ...rest },
|
|
67
|
+
trigger,
|
|
68
|
+
} = (await request.json()) as any;
|
|
69
|
+
|
|
70
|
+
if (!bodyText) {
|
|
71
|
+
return { data: rest, trigger };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Webhook passed body as bodyText. Use it as function input if a well-formatted JSON
|
|
75
|
+
// TODO: better trigger input mapping
|
|
76
|
+
try {
|
|
77
|
+
const data = JSON.parse(bodyText);
|
|
78
|
+
return { data, trigger: { ...trigger, ...rest } };
|
|
79
|
+
} catch (err) {
|
|
80
|
+
log.catch(err);
|
|
81
|
+
return { data: { bodyText, ...rest } };
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const handleFunctionMetaCall = (functionDefinition: FunctionProtocol.Func, request: Request): Response => {
|
|
86
|
+
const response: FunctionMetadata = {
|
|
87
|
+
key: functionDefinition.meta.key,
|
|
88
|
+
name: functionDefinition.meta.name,
|
|
89
|
+
description: functionDefinition.meta.description,
|
|
90
|
+
inputSchema: functionDefinition.meta.inputSchema as JsonSchemaType | undefined,
|
|
91
|
+
outputSchema: functionDefinition.meta.outputSchema as JsonSchemaType | undefined,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
return new Response(JSON.stringify(response), {
|
|
95
|
+
headers: {
|
|
96
|
+
'Content-Type': 'application/json',
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const createFunctionContext = async ({
|
|
102
|
+
serviceContainer,
|
|
103
|
+
contextSpaceId,
|
|
104
|
+
}: {
|
|
105
|
+
serviceContainer: ServiceContainer;
|
|
106
|
+
contextSpaceId: SpaceId | undefined;
|
|
107
|
+
}): Promise<FunctionProtocol.Context> => {
|
|
108
|
+
const { dataService, queryService, queueService } = await serviceContainer.createServices();
|
|
109
|
+
|
|
110
|
+
let spaceKey: string | undefined;
|
|
111
|
+
let rootUrl: string | undefined;
|
|
112
|
+
if (contextSpaceId) {
|
|
113
|
+
const meta = await serviceContainer.getSpaceMeta(contextSpaceId);
|
|
114
|
+
if (!meta) {
|
|
115
|
+
throw new Error(`Space not found: ${contextSpaceId}`);
|
|
116
|
+
}
|
|
117
|
+
spaceKey = meta.spaceKey;
|
|
118
|
+
invariant(!meta.rootDocumentId.startsWith('automerge:'));
|
|
119
|
+
rootUrl = `automerge:${meta.rootDocumentId}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
services: {
|
|
124
|
+
dataService,
|
|
125
|
+
queryService,
|
|
126
|
+
queueService,
|
|
127
|
+
},
|
|
128
|
+
spaceId: contextSpaceId,
|
|
129
|
+
spaceKey,
|
|
130
|
+
spaceRootUrl: rootUrl,
|
|
131
|
+
} as any; // TODO(dmaretskyi): Link and fix before merging
|
|
132
|
+
};
|