@lowdefy/api 5.5.1 → 6.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.
- package/dist/context/createAuthorize.js +4 -1
- package/dist/context/resolveStrategyCaller.js +56 -0
- package/dist/index.js +16 -3
- package/dist/response/buildEndpointResult.js +33 -0
- package/dist/response/normalizeErrorSources.js +64 -0
- package/dist/response/omitErrorProps.js +51 -0
- package/dist/response/redactErrorResponse.js +30 -0
- package/dist/response/redactResponse.js +31 -0
- package/dist/routes/agent/authorizeAgent.js +39 -0
- package/dist/routes/agent/callAgent.js +7 -180
- package/dist/routes/agent/prepareAgent.js +173 -0
- package/dist/routes/auth/createLogger.js +2 -5
- package/dist/routes/auth/createPrefixedCookies.js +52 -0
- package/dist/routes/auth/{getNextAuthConfig.js → getAuthConfig.js} +27 -16
- package/dist/routes/auth/resolveCookies.js +37 -0
- package/dist/routes/auth/strategies/createAuthStrategies.js +77 -0
- package/dist/routes/auth/strategies/getAuthStrategies.js +31 -0
- package/dist/routes/endpoints/addStepResult.js +12 -2
- package/dist/routes/endpoints/authorizeApiEndpoint.js +8 -2
- package/dist/routes/endpoints/callEndpoint.js +32 -13
- package/dist/routes/endpoints/control/controlReject.js +3 -1
- package/dist/routes/endpoints/control/controlSetState.js +13 -2
- package/dist/routes/endpoints/control/controlThrow.js +4 -1
- package/dist/routes/endpoints/findSchedule.js +35 -0
- package/dist/routes/endpoints/forwardScheduledEndpoint.js +106 -0
- package/dist/routes/endpoints/getEndpointConfig.js +9 -3
- package/dist/routes/endpoints/getEnvironmentSchedules.js +29 -0
- package/dist/routes/endpoints/handleAgentCall.js +83 -0
- package/dist/routes/endpoints/handleEndpointCall.js +49 -1
- package/dist/routes/endpoints/handleRenderNotification.js +189 -0
- package/dist/routes/endpoints/handleValidateSchema.js +3 -1
- package/dist/routes/endpoints/isUnauthenticatedHuman.js +31 -0
- package/dist/routes/endpoints/resolveCronEnvironment.js +37 -0
- package/dist/routes/endpoints/runDetachedEndpoint.js +66 -0
- package/dist/routes/endpoints/runRoutine.js +15 -1
- package/dist/routes/endpoints/runScheduledEndpoint.js +104 -0
- package/dist/routes/endpoints/runWebhookEndpoint.js +83 -0
- package/dist/routes/endpoints/scheduleBackground.js +48 -0
- package/dist/routes/mcp/createMcpServer.js +160 -0
- package/dist/routes/notifications/derivePreview.js +30 -0
- package/dist/routes/notifications/getNotificationConfig.js +32 -0
- package/dist/routes/notifications/resolveNotificationLinks.js +72 -0
- package/dist/routes/notifications/resolveThemeLogo.js +35 -0
- package/dist/routes/page/dynamic/resolveDynamicContent.js +172 -0
- package/dist/routes/page/dynamic/unescapeOperators.js +36 -0
- package/dist/routes/page/dynamic/validateFragment.js +104 -0
- package/dist/routes/page/getPageConfig.js +16 -6
- package/dist/routes/request/callRequest.js +2 -1
- package/dist/routes/websocket/authorizeWebsocket.js +27 -0
- package/dist/routes/websocket/createChannelRegistry.js +269 -0
- package/dist/routes/websocket/createWebSocketConnection.js +131 -0
- package/dist/routes/websocket/getWebsocketConfig.js +30 -0
- package/dist/routes/websocket/getWebsocketResolver.js +33 -0
- package/dist/routes/websocket/prepareChannel.js +72 -0
- package/dist/test/testContext.js +10 -3
- package/package.json +12 -10
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import { type } from '@lowdefy/helpers';
|
|
16
|
+
// Wraps one client websocket connection: parses frames, dispatches
|
|
17
|
+
// subscribe/unsubscribe/publish to the channel registry, and answers every
|
|
18
|
+
// frame with an ack or an error so client actions never hang.
|
|
19
|
+
function createWebSocketConnection(context, { registry, send }) {
|
|
20
|
+
const { logger } = context;
|
|
21
|
+
const subscriber = {
|
|
22
|
+
id: context.rid,
|
|
23
|
+
subscriptions: new Map(),
|
|
24
|
+
send
|
|
25
|
+
};
|
|
26
|
+
function sendError({ message, requestId, websocketId }) {
|
|
27
|
+
send(JSON.stringify({
|
|
28
|
+
type: 'error',
|
|
29
|
+
websocketId,
|
|
30
|
+
requestId,
|
|
31
|
+
message
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
async function handleFrame(frame) {
|
|
35
|
+
const { payload, requestId, websocketId } = frame;
|
|
36
|
+
if (!type.isString(websocketId)) {
|
|
37
|
+
sendError({
|
|
38
|
+
message: 'Frame "websocketId" should be a string.',
|
|
39
|
+
requestId
|
|
40
|
+
});
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
switch(frame.type){
|
|
44
|
+
case 'subscribe':
|
|
45
|
+
await registry.subscribe(context, {
|
|
46
|
+
websocketId,
|
|
47
|
+
payload,
|
|
48
|
+
subscriber
|
|
49
|
+
});
|
|
50
|
+
send(JSON.stringify({
|
|
51
|
+
type: 'subscribed',
|
|
52
|
+
websocketId
|
|
53
|
+
}));
|
|
54
|
+
return;
|
|
55
|
+
case 'unsubscribe':
|
|
56
|
+
registry.unsubscribe({
|
|
57
|
+
websocketId,
|
|
58
|
+
subscriber
|
|
59
|
+
});
|
|
60
|
+
send(JSON.stringify({
|
|
61
|
+
type: 'unsubscribed',
|
|
62
|
+
websocketId
|
|
63
|
+
}));
|
|
64
|
+
return;
|
|
65
|
+
case 'publish':
|
|
66
|
+
await registry.publish(context, {
|
|
67
|
+
websocketId,
|
|
68
|
+
payload
|
|
69
|
+
});
|
|
70
|
+
send(JSON.stringify({
|
|
71
|
+
type: 'published',
|
|
72
|
+
websocketId,
|
|
73
|
+
requestId
|
|
74
|
+
}));
|
|
75
|
+
return;
|
|
76
|
+
default:
|
|
77
|
+
// Unknown frame types are ignored for forward compatibility.
|
|
78
|
+
logger.debug({
|
|
79
|
+
event: 'ws_unknown_frame',
|
|
80
|
+
frameType: frame.type
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function handleMessage(raw) {
|
|
85
|
+
let frame;
|
|
86
|
+
try {
|
|
87
|
+
frame = JSON.parse(raw);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
sendError({
|
|
90
|
+
message: 'Invalid frame — expected JSON.'
|
|
91
|
+
});
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (!type.isObject(frame)) {
|
|
95
|
+
sendError({
|
|
96
|
+
message: 'Invalid frame — expected an object.'
|
|
97
|
+
});
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
await handleFrame(frame);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
logger.debug({
|
|
104
|
+
err: error
|
|
105
|
+
}, error.message);
|
|
106
|
+
context.handleError(error);
|
|
107
|
+
sendError({
|
|
108
|
+
message: error.message,
|
|
109
|
+
requestId: frame.requestId,
|
|
110
|
+
websocketId: frame.websocketId
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function close() {
|
|
115
|
+
registry.unsubscribeAll({
|
|
116
|
+
subscriber
|
|
117
|
+
});
|
|
118
|
+
logger.debug({
|
|
119
|
+
event: 'ws_disconnect'
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
logger.debug({
|
|
123
|
+
event: 'ws_connect'
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
close,
|
|
127
|
+
handleMessage,
|
|
128
|
+
subscriber
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
export default createWebSocketConnection;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import { ConfigError } from '@lowdefy/errors';
|
|
16
|
+
async function getWebsocketConfig({ logger, readConfigFile }, { websocketId }) {
|
|
17
|
+
const websocketConfig = await readConfigFile(`websockets/${websocketId}.json`);
|
|
18
|
+
if (!websocketConfig) {
|
|
19
|
+
const err = new ConfigError(`Websocket "${websocketId}" does not exist.`);
|
|
20
|
+
logger.debug({
|
|
21
|
+
params: {
|
|
22
|
+
websocketId
|
|
23
|
+
},
|
|
24
|
+
err
|
|
25
|
+
}, err.message);
|
|
26
|
+
throw err;
|
|
27
|
+
}
|
|
28
|
+
return websocketConfig;
|
|
29
|
+
}
|
|
30
|
+
export default getWebsocketConfig;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import { ConfigError } from '@lowdefy/errors';
|
|
16
|
+
function getWebsocketResolver({ logger, websockets }, { websocketConfig }) {
|
|
17
|
+
const websocketResolver = (websockets ?? {})[websocketConfig.type];
|
|
18
|
+
if (!websocketResolver) {
|
|
19
|
+
const err = new ConfigError(`Websocket type "${websocketConfig.type}" can not be found.`, {
|
|
20
|
+
configKey: websocketConfig['~k']
|
|
21
|
+
});
|
|
22
|
+
logger.debug({
|
|
23
|
+
params: {
|
|
24
|
+
id: websocketConfig.websocketId,
|
|
25
|
+
type: websocketConfig.type
|
|
26
|
+
},
|
|
27
|
+
err
|
|
28
|
+
}, err.message);
|
|
29
|
+
throw err;
|
|
30
|
+
}
|
|
31
|
+
return websocketResolver;
|
|
32
|
+
}
|
|
33
|
+
export default getWebsocketResolver;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import { type } from '@lowdefy/helpers';
|
|
16
|
+
import authorizeWebsocket from './authorizeWebsocket.js';
|
|
17
|
+
import getConnection from '../connections/getConnection.js';
|
|
18
|
+
import getConnectionConfig from '../connections/getConnectionConfig.js';
|
|
19
|
+
import getWebsocketConfig from './getWebsocketConfig.js';
|
|
20
|
+
import getWebsocketResolver from './getWebsocketResolver.js';
|
|
21
|
+
import createEvaluateOperators from '../../context/createEvaluateOperators.js';
|
|
22
|
+
// Shared preparation for subscribe and publish frames: load config, authorize,
|
|
23
|
+
// resolve the websocket type, and evaluate server operators per subscription.
|
|
24
|
+
// Properties are evaluated with the subscriber's payload and user, so the same
|
|
25
|
+
// websocket definition can produce user- or filter-specific channels. The
|
|
26
|
+
// evaluated result is the channel identity — subscribers whose evaluation is
|
|
27
|
+
// identical share one running source.
|
|
28
|
+
async function prepareChannel(context, { websocketId, payload }) {
|
|
29
|
+
context.evaluateOperators = createEvaluateOperators(context);
|
|
30
|
+
const websocketConfig = await getWebsocketConfig(context, {
|
|
31
|
+
websocketId
|
|
32
|
+
});
|
|
33
|
+
authorizeWebsocket(context, {
|
|
34
|
+
websocketConfig
|
|
35
|
+
});
|
|
36
|
+
const websocketResolver = getWebsocketResolver(context, {
|
|
37
|
+
websocketConfig
|
|
38
|
+
});
|
|
39
|
+
let connectionProperties = null;
|
|
40
|
+
if (!type.isNone(websocketConfig.connectionId)) {
|
|
41
|
+
const connectionConfig = await getConnectionConfig(context, {
|
|
42
|
+
connectionId: websocketConfig.connectionId,
|
|
43
|
+
configKey: websocketConfig['~k']
|
|
44
|
+
});
|
|
45
|
+
// Validates the connection type exists — resolver lookup is on the
|
|
46
|
+
// websocket type itself, not nested on the connection.
|
|
47
|
+
getConnection(context, {
|
|
48
|
+
connectionConfig
|
|
49
|
+
});
|
|
50
|
+
connectionProperties = context.evaluateOperators({
|
|
51
|
+
input: connectionConfig.properties ?? {},
|
|
52
|
+
location: connectionConfig.connectionId,
|
|
53
|
+
payload,
|
|
54
|
+
state: {},
|
|
55
|
+
steps: {}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const properties = context.evaluateOperators({
|
|
59
|
+
input: websocketConfig.properties ?? {},
|
|
60
|
+
location: websocketConfig.websocketId,
|
|
61
|
+
payload,
|
|
62
|
+
state: {},
|
|
63
|
+
steps: {}
|
|
64
|
+
});
|
|
65
|
+
return {
|
|
66
|
+
connectionProperties,
|
|
67
|
+
properties,
|
|
68
|
+
websocketConfig,
|
|
69
|
+
websocketResolver
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export default prepareChannel;
|
package/dist/test/testContext.js
CHANGED
|
@@ -13,23 +13,29 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/ import createAuthorize from '../context/createAuthorize.js';
|
|
16
|
-
function testContext({ appMeta = {}, config = {}, connections = {}, headers = {}, logger = {
|
|
16
|
+
function testContext({ appMeta = {}, config = {}, configDirectory, connections = {}, headers = {}, logger = {
|
|
17
17
|
debug: ()=>{},
|
|
18
18
|
error: ()=>{},
|
|
19
19
|
info: ()=>{},
|
|
20
20
|
warn: ()=>{}
|
|
21
21
|
}, operators = {
|
|
22
22
|
_test: ()=>'test'
|
|
23
|
-
}, readConfigFile, secrets = {}, session } = {}) {
|
|
23
|
+
}, readConfigFile, secrets = {}, session, system } = {}) {
|
|
24
24
|
return {
|
|
25
25
|
appMeta,
|
|
26
26
|
authorize: createAuthorize({
|
|
27
|
-
session
|
|
27
|
+
session,
|
|
28
|
+
system
|
|
28
29
|
}),
|
|
29
30
|
config,
|
|
31
|
+
configDirectory,
|
|
30
32
|
connections,
|
|
33
|
+
// Mirrors the servers' createHandleError contract: the sink logs the error
|
|
34
|
+
// and marks it handled, which is what runRoutine's guard and the client's
|
|
35
|
+
// already-logged check both read.
|
|
31
36
|
handleError: async (error)=>{
|
|
32
37
|
logger.error(error);
|
|
38
|
+
error.handled = true;
|
|
33
39
|
},
|
|
34
40
|
headers,
|
|
35
41
|
logger,
|
|
@@ -38,6 +44,7 @@ function testContext({ appMeta = {}, config = {}, connections = {}, headers = {}
|
|
|
38
44
|
secrets,
|
|
39
45
|
session,
|
|
40
46
|
steps: {},
|
|
47
|
+
system,
|
|
41
48
|
user: session?.user
|
|
42
49
|
};
|
|
43
50
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lowdefy/api",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "",
|
|
6
6
|
"homepage": "https://lowdefy.com",
|
|
@@ -34,18 +34,20 @@
|
|
|
34
34
|
"dist/*"
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@lowdefy/ajv": "
|
|
38
|
-
"@lowdefy/
|
|
39
|
-
"@lowdefy/
|
|
40
|
-
"@lowdefy/
|
|
41
|
-
"@lowdefy/
|
|
42
|
-
"@lowdefy/
|
|
43
|
-
"@lowdefy/operators
|
|
37
|
+
"@lowdefy/ajv": "6.0.0",
|
|
38
|
+
"@lowdefy/build": "6.0.0",
|
|
39
|
+
"@lowdefy/errors": "6.0.0",
|
|
40
|
+
"@lowdefy/helpers": "6.0.0",
|
|
41
|
+
"@lowdefy/node-utils": "6.0.0",
|
|
42
|
+
"@lowdefy/nunjucks": "6.0.0",
|
|
43
|
+
"@lowdefy/operators": "6.0.0",
|
|
44
|
+
"@lowdefy/operators-js": "6.0.0",
|
|
45
|
+
"@modelcontextprotocol/sdk": "1.29.0"
|
|
44
46
|
},
|
|
45
47
|
"devDependencies": {
|
|
46
48
|
"@jest/globals": "28.1.3",
|
|
47
|
-
"@swc/cli": "0.8.
|
|
48
|
-
"@swc/core": "1.15.
|
|
49
|
+
"@swc/cli": "0.8.1",
|
|
50
|
+
"@swc/core": "1.15.32",
|
|
49
51
|
"@swc/jest": "0.2.39",
|
|
50
52
|
"jest": "28.1.3"
|
|
51
53
|
},
|