@lowdefy/api 5.5.1 → 5.6.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/index.js +5 -2
- 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 +16 -12
- package/dist/routes/endpoints/addStepResult.js +12 -2
- package/dist/routes/endpoints/callEndpoint.js +6 -10
- package/dist/routes/endpoints/control/controlSetState.js +13 -2
- package/dist/routes/endpoints/runRoutine.js +3 -1
- package/dist/routes/request/callRequest.js +2 -1
- package/dist/test/testContext.js +6 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
|
-
*/ import
|
|
15
|
+
*/ import buildEndpointResult from './response/buildEndpointResult.js';
|
|
16
|
+
import callAgent from './routes/agent/callAgent.js';
|
|
16
17
|
import callEndpoint from './routes/endpoints/callEndpoint.js';
|
|
17
18
|
import callRequest from './routes/request/callRequest.js';
|
|
18
19
|
import createApiContext from './context/createApiContext.js';
|
|
@@ -22,4 +23,6 @@ import getNextAuthConfig from './routes/auth/getNextAuthConfig.js';
|
|
|
22
23
|
import getPageConfig from './routes/page/getPageConfig.js';
|
|
23
24
|
import getRootConfig from './routes/rootConfig/getRootConfig.js';
|
|
24
25
|
import logClientError from './routes/log/logClientError.js';
|
|
25
|
-
|
|
26
|
+
import redactErrorResponse from './response/redactErrorResponse.js';
|
|
27
|
+
import redactResponse from './response/redactResponse.js';
|
|
28
|
+
export { buildEndpointResult, callAgent, callEndpoint, callRequest, createApiContext, createSessionCallback, getHomeAndMenus, getNextAuthConfig, getPageConfig, getRootConfig, logClientError, redactErrorResponse, redactResponse };
|
|
@@ -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 redactErrorResponse from './redactErrorResponse.js';
|
|
16
|
+
import redactResponse from './redactResponse.js';
|
|
17
|
+
// The wire object every endpoint route returns after running its routine. One
|
|
18
|
+
// function rather than a copy of the same return statement per route, so the
|
|
19
|
+
// `response` field cannot end up policed differently from the `error` field beside
|
|
20
|
+
// it - see redactResponse for why the response needs the policy at all.
|
|
21
|
+
function buildEndpointResult(context, { error, response, status }) {
|
|
22
|
+
const success = ![
|
|
23
|
+
'error',
|
|
24
|
+
'reject'
|
|
25
|
+
].includes(status);
|
|
26
|
+
return {
|
|
27
|
+
error: redactErrorResponse(context, error),
|
|
28
|
+
response: redactResponse(context, response),
|
|
29
|
+
status: success ? 'success' : status,
|
|
30
|
+
success
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export default buildEndpointResult;
|
|
@@ -0,0 +1,64 @@
|
|
|
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 path from 'path';
|
|
16
|
+
import { type } from '@lowdefy/helpers';
|
|
17
|
+
// extractErrorProps emits a nested error as a plain props object, not as a second
|
|
18
|
+
// '~e' wrapper, so an error node is recognised by its shape. Keying on the shape
|
|
19
|
+
// rather than on a map of where errors can appear is deliberate: re-deriving those
|
|
20
|
+
// positions is the mistake the walk-level omit exists to avoid, and this predicate
|
|
21
|
+
// still visits every position.
|
|
22
|
+
//
|
|
23
|
+
// The check matters because the payload also carries author-written data the policy
|
|
24
|
+
// deliberately preserves - a UserError's non-Error cause, its metaData - and a
|
|
25
|
+
// `source` key inside those belongs to the app, not to us.
|
|
26
|
+
function isErrorNode(value) {
|
|
27
|
+
return type.isString(value.name) && type.isString(value.message);
|
|
28
|
+
}
|
|
29
|
+
// A prefix slice rather than path.relative or a parse of a `path:line` shape:
|
|
30
|
+
// source is `${resolvedPath}:${lineNumber}` only when a line number resolved and
|
|
31
|
+
// the bare path otherwise, and removing a prefix never touches the suffix, so both
|
|
32
|
+
// forms work without knowing which this is. Applied at every error node carrying a
|
|
33
|
+
// source, not only the outermost, because a strip is a no-op when the prefix is
|
|
34
|
+
// absent. Mutates the freshly serialized payload in place - nothing else holds it.
|
|
35
|
+
function stripConfigDirectory(value, prefix) {
|
|
36
|
+
if (type.isArray(value)) {
|
|
37
|
+
value.forEach((item)=>stripConfigDirectory(item, prefix));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (!type.isObject(value)) return;
|
|
41
|
+
if (isErrorNode(value) && type.isString(value.source) && value.source.startsWith(prefix)) {
|
|
42
|
+
value.source = value.source.slice(prefix.length);
|
|
43
|
+
}
|
|
44
|
+
Object.values(value).forEach((child)=>stripConfigDirectory(child, prefix));
|
|
45
|
+
}
|
|
46
|
+
// Guarantees `source` reaches a client config-relative, never as an absolute server
|
|
47
|
+
// path. resolveConfigLocation makes it absolute whenever the context carries a
|
|
48
|
+
// configDirectory - server-dev and server-e2e do, and production happens not to
|
|
49
|
+
// today by omission rather than by invariant, which is the drift this closes.
|
|
50
|
+
//
|
|
51
|
+
// A rewrite rather than an omission, and it needs the context, so it runs as a pass
|
|
52
|
+
// over the serialized payload instead of inside the walk.
|
|
53
|
+
function normalizeErrorSources(context, payload) {
|
|
54
|
+
// errorHandler has an else branch for requests with no lowdefyContext, so a
|
|
55
|
+
// missing context is a supported call - it only means no source to normalise.
|
|
56
|
+
const configDirectory = context?.configDirectory;
|
|
57
|
+
if (type.isNone(configDirectory)) return payload;
|
|
58
|
+
// configDirectory is `LOWDEFY_DIRECTORY_CONFIG || process.cwd()` at every site
|
|
59
|
+
// that sets it, so it may be relative or carry a trailing separator, while
|
|
60
|
+
// resolveConfigLocation built source with path.resolve. Compare normalised.
|
|
61
|
+
stripConfigDirectory(payload, `${path.resolve(configDirectory)}${path.sep}`);
|
|
62
|
+
return payload;
|
|
63
|
+
}
|
|
64
|
+
export default normalizeErrorSources;
|
|
@@ -0,0 +1,51 @@
|
|
|
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 { UserError } from '@lowdefy/errors';
|
|
16
|
+
import { type } from '@lowdefy/helpers';
|
|
17
|
+
// The client-bound error policy: which fields of an error may cross the wire.
|
|
18
|
+
// Passed to serializer.serialize as `omitErrorProps`, so extractErrorProps applies
|
|
19
|
+
// it at EVERY error node the walk emits - the cause chain, an Error-valued own
|
|
20
|
+
// property, and an Error nested inside a plain object or array. The policy is
|
|
21
|
+
// stated against the emitter rather than against a response shape, so it cannot
|
|
22
|
+
// become depth-limited.
|
|
23
|
+
//
|
|
24
|
+
// `received` is not merely "may be sensitive": on the request path
|
|
25
|
+
// callRequestResolver sets it to the EVALUATED request properties, so a _secret
|
|
26
|
+
// resolved into a request header is in it. `stack` exposes server internals,
|
|
27
|
+
// including absolute node_modules paths. Both are unbounded runtime data nobody
|
|
28
|
+
// chose, in every environment - the full value stays in the server log, which is
|
|
29
|
+
// where a developer on their own machine reads it.
|
|
30
|
+
const ALWAYS_OMITTED = [
|
|
31
|
+
'received',
|
|
32
|
+
'stack'
|
|
33
|
+
];
|
|
34
|
+
const OMITTED_WITH_CAUSE = [
|
|
35
|
+
...ALWAYS_OMITTED,
|
|
36
|
+
'cause'
|
|
37
|
+
];
|
|
38
|
+
function omitErrorProps(error) {
|
|
39
|
+
// An Error cause is the trace the browser renders (name + message per level),
|
|
40
|
+
// so it is always kept - fields are taken from causes, causes are never pruned.
|
|
41
|
+
// A non-Error cause is internal server config: the whole endpoint routine, a
|
|
42
|
+
// control node, ajv errors. UserError is the exception, the one class whose
|
|
43
|
+
// payload the author wrote for the client.
|
|
44
|
+
//
|
|
45
|
+
// type.isError is `instanceof Error`, the same test extractErrorProps uses to
|
|
46
|
+
// decide which branch emits the cause - the two must agree.
|
|
47
|
+
if (type.isError(error.cause)) return ALWAYS_OMITTED;
|
|
48
|
+
if (error instanceof UserError) return ALWAYS_OMITTED;
|
|
49
|
+
return OMITTED_WITH_CAUSE;
|
|
50
|
+
}
|
|
51
|
+
export default omitErrorProps;
|
|
@@ -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 { serializer, type } from '@lowdefy/helpers';
|
|
16
|
+
import normalizeErrorSources from './normalizeErrorSources.js';
|
|
17
|
+
import omitErrorProps from './omitErrorProps.js';
|
|
18
|
+
// Owns the serialization as well as the policy, so the policy cannot be forgotten:
|
|
19
|
+
// no bare serializer.serialize(error) is left in response position to wrap. Every
|
|
20
|
+
// route returning an error to a caller - any status, any transport - goes through
|
|
21
|
+
// here or through buildEndpointResult.
|
|
22
|
+
function redactErrorResponse(context, error) {
|
|
23
|
+
// Endpoint routes serialize the error field on success too, where it is null.
|
|
24
|
+
// Passing that through unchanged keeps them from emitting an empty {'~e'}.
|
|
25
|
+
if (type.isNone(error)) return error;
|
|
26
|
+
return normalizeErrorSources(context, serializer.serialize(error, {
|
|
27
|
+
omitErrorProps
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
export default redactErrorResponse;
|
|
@@ -0,0 +1,31 @@
|
|
|
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 { serializer } from '@lowdefy/helpers';
|
|
16
|
+
import normalizeErrorSources from './normalizeErrorSources.js';
|
|
17
|
+
import omitErrorProps from './omitErrorProps.js';
|
|
18
|
+
// The response-value call shape, beside redactErrorResponse's error-only one.
|
|
19
|
+
// A response is not an error, but makeReplacer wraps any Error it meets anywhere
|
|
20
|
+
// in a value, so a response holding one is an error-serialization site too - the
|
|
21
|
+
// grep that enumerated those sites could not see them, which is why this exists
|
|
22
|
+
// as a function rather than as a rule to remember.
|
|
23
|
+
//
|
|
24
|
+
// Same policy as the error field, because it reaches the same audience: a browser
|
|
25
|
+
// for a request or endpoint body, a third party for cron and detached.
|
|
26
|
+
function redactResponse(context, response) {
|
|
27
|
+
return normalizeErrorSources(context, serializer.serialize(response, {
|
|
28
|
+
omitErrorProps
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
export default redactResponse;
|
|
@@ -0,0 +1,39 @@
|
|
|
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
|
+
import { translate } from '@lowdefy/helpers';
|
|
17
|
+
function authorizeAgent({ authorize, i18n, logger }, { agentConfig }) {
|
|
18
|
+
if (!authorize(agentConfig)) {
|
|
19
|
+
logger.debug({
|
|
20
|
+
event: 'debug_agent_authorize',
|
|
21
|
+
authorized: false,
|
|
22
|
+
auth_config: agentConfig.auth
|
|
23
|
+
});
|
|
24
|
+
// Same message as an unknown agentId so responses do not reveal which agents exist.
|
|
25
|
+
throw new ConfigError(translate({
|
|
26
|
+
key: 'agent.runtime.agentNotFound',
|
|
27
|
+
values: {
|
|
28
|
+
agentId: agentConfig.agentId
|
|
29
|
+
},
|
|
30
|
+
i18n
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
logger.debug({
|
|
34
|
+
event: 'debug_agent_authorize',
|
|
35
|
+
authorized: true,
|
|
36
|
+
auth_config: agentConfig.auth
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
export default authorizeAgent;
|
|
@@ -12,8 +12,10 @@
|
|
|
12
12
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
|
-
*/ import {
|
|
15
|
+
*/ import { type } from '@lowdefy/helpers';
|
|
16
|
+
import buildEndpointResult from '../../response/buildEndpointResult.js';
|
|
16
17
|
import createEvaluateOperators from '../../context/createEvaluateOperators.js';
|
|
18
|
+
import authorizeAgent from './authorizeAgent.js';
|
|
17
19
|
import authorizeApiEndpoint from '../endpoints/authorizeApiEndpoint.js';
|
|
18
20
|
import getEndpointConfig from '../endpoints/getEndpointConfig.js';
|
|
19
21
|
import runRoutine from '../endpoints/runRoutine.js';
|
|
@@ -33,6 +35,9 @@ async function callAgent(context, { agentId, pageId, messages, conversationId, u
|
|
|
33
35
|
const agentConfig = await getAgentConfig(context, {
|
|
34
36
|
agentId
|
|
35
37
|
});
|
|
38
|
+
authorizeAgent(context, {
|
|
39
|
+
agentConfig
|
|
40
|
+
});
|
|
36
41
|
const agentContext = {
|
|
37
42
|
conversationId: conversationId ?? undefined,
|
|
38
43
|
pageId,
|
|
@@ -102,16 +107,11 @@ async function callAgent(context, { agentId, pageId, messages, conversationId, u
|
|
|
102
107
|
const { error, response, status } = await runRoutine(context, routineContext, {
|
|
103
108
|
routine: endpointConfig.routine
|
|
104
109
|
});
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
error: serializer.serialize(error),
|
|
111
|
-
response: serializer.serialize(response),
|
|
112
|
-
status: success ? 'success' : status,
|
|
113
|
-
success
|
|
114
|
-
};
|
|
110
|
+
return buildEndpointResult(context, {
|
|
111
|
+
error,
|
|
112
|
+
response,
|
|
113
|
+
status
|
|
114
|
+
});
|
|
115
115
|
},
|
|
116
116
|
getEndpointConfig: async ({ endpointId })=>{
|
|
117
117
|
return getEndpointConfig(context, {
|
|
@@ -119,9 +119,13 @@ async function callAgent(context, { agentId, pageId, messages, conversationId, u
|
|
|
119
119
|
});
|
|
120
120
|
},
|
|
121
121
|
getAgentConfig: async ({ agentId })=>{
|
|
122
|
-
|
|
122
|
+
const subAgentConfig = await getAgentConfig(context, {
|
|
123
123
|
agentId
|
|
124
124
|
});
|
|
125
|
+
authorizeAgent(context, {
|
|
126
|
+
agentConfig: subAgentConfig
|
|
127
|
+
});
|
|
128
|
+
return subAgentConfig;
|
|
125
129
|
},
|
|
126
130
|
getConnectionForAgent: async ({ agentConfig: subAgentConfig })=>{
|
|
127
131
|
const subConnectionConfig = await getConnectionConfig(context, {
|
|
@@ -12,12 +12,22 @@
|
|
|
12
12
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
|
-
*/ import {
|
|
15
|
+
*/ import { ConfigError } from '@lowdefy/errors';
|
|
16
|
+
import { ReservedKeyError, set } from '@lowdefy/helpers';
|
|
16
17
|
function addStepResult(context, routineContext, { result, stepId }) {
|
|
17
18
|
const key = [
|
|
18
19
|
stepId,
|
|
19
20
|
...routineContext.arrayIndices
|
|
20
21
|
].join('.');
|
|
21
|
-
|
|
22
|
+
try {
|
|
23
|
+
set(routineContext.steps, key, result);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
if (error instanceof ReservedKeyError) {
|
|
26
|
+
throw new ConfigError(`Reserved step id "${error.segment}" cannot be used`, {
|
|
27
|
+
cause: error
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
22
32
|
}
|
|
23
33
|
export default addStepResult;
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/ import { serializer } from '@lowdefy/helpers';
|
|
16
16
|
import { ConfigError } from '@lowdefy/errors';
|
|
17
17
|
import authorizeApiEndpoint from './authorizeApiEndpoint.js';
|
|
18
|
+
import buildEndpointResult from '../../response/buildEndpointResult.js';
|
|
18
19
|
import createEvaluateOperators from '../../context/createEvaluateOperators.js';
|
|
19
20
|
import getEndpointConfig from './getEndpointConfig.js';
|
|
20
21
|
import runRoutine from './runRoutine.js';
|
|
@@ -59,15 +60,10 @@ async function callEndpoint(context, { blockId, endpointId, pageId, payload }) {
|
|
|
59
60
|
const { error, response, status } = await runRoutine(context, routineContext, {
|
|
60
61
|
routine: endpointConfig.routine
|
|
61
62
|
});
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
error: serializer.serialize(error),
|
|
68
|
-
response: serializer.serialize(response),
|
|
69
|
-
status: success ? 'success' : status,
|
|
70
|
-
success
|
|
71
|
-
};
|
|
63
|
+
return buildEndpointResult(context, {
|
|
64
|
+
error,
|
|
65
|
+
response,
|
|
66
|
+
status
|
|
67
|
+
});
|
|
72
68
|
}
|
|
73
69
|
export default callEndpoint;
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
|
-
*/ import {
|
|
15
|
+
*/ import { ConfigError } from '@lowdefy/errors';
|
|
16
|
+
import { ReservedKeyError, set } from '@lowdefy/helpers';
|
|
16
17
|
function controlSetState(context, routineContext, { control }) {
|
|
17
18
|
const { logger, evaluateOperators } = context;
|
|
18
19
|
const { items } = routineContext;
|
|
@@ -30,7 +31,17 @@ function controlSetState(context, routineContext, { control }) {
|
|
|
30
31
|
evaluated: evaluatedSetState
|
|
31
32
|
});
|
|
32
33
|
Object.entries(evaluatedSetState).forEach(([key, value])=>{
|
|
33
|
-
|
|
34
|
+
try {
|
|
35
|
+
set(routineContext.state, key, value);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if (error instanceof ReservedKeyError) {
|
|
38
|
+
throw new ConfigError(`Reserved key "${error.segment}" cannot be used in :set_state`, {
|
|
39
|
+
cause: error,
|
|
40
|
+
configKey: control['~k']
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
34
45
|
});
|
|
35
46
|
return {
|
|
36
47
|
status: 'continue'
|
|
@@ -68,9 +68,11 @@ async function runRoutine(context, routineContext, { routine }) {
|
|
|
68
68
|
error
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
|
+
// handleError sets error.handled once it has logged - it is the single sink
|
|
72
|
+
// that owns the flag, so a nested runRoutine re-throwing this error does not
|
|
73
|
+
// log it again.
|
|
71
74
|
if (!error.handled) {
|
|
72
75
|
await context.handleError(error);
|
|
73
|
-
error.handled = true;
|
|
74
76
|
}
|
|
75
77
|
return {
|
|
76
78
|
status: 'error',
|
|
@@ -24,6 +24,7 @@ import getRequestConfig from './getRequestConfig.js';
|
|
|
24
24
|
import getRequestResolver from './getRequestResolver.js';
|
|
25
25
|
import validateSchemas from './validateSchemas.js';
|
|
26
26
|
import createEvaluateOperators from '../../context/createEvaluateOperators.js';
|
|
27
|
+
import redactResponse from '../../response/redactResponse.js';
|
|
27
28
|
async function callRequest(context, { blockId, pageId, payload, requestId }) {
|
|
28
29
|
const { logger } = context;
|
|
29
30
|
context.blockId = blockId;
|
|
@@ -93,7 +94,7 @@ async function callRequest(context, { blockId, pageId, payload, requestId }) {
|
|
|
93
94
|
id: requestConfig.id,
|
|
94
95
|
success: true,
|
|
95
96
|
type: requestConfig.type,
|
|
96
|
-
response:
|
|
97
|
+
response: redactResponse(context, response)
|
|
97
98
|
};
|
|
98
99
|
}
|
|
99
100
|
export default callRequest;
|
package/dist/test/testContext.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
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: ()=>{},
|
|
@@ -27,9 +27,14 @@ function testContext({ appMeta = {}, config = {}, connections = {}, headers = {}
|
|
|
27
27
|
session
|
|
28
28
|
}),
|
|
29
29
|
config,
|
|
30
|
+
configDirectory,
|
|
30
31
|
connections,
|
|
32
|
+
// Mirrors the servers' createHandleError contract: the sink logs the error
|
|
33
|
+
// and marks it handled, which is what runRoutine's guard and the client's
|
|
34
|
+
// already-logged check both read.
|
|
31
35
|
handleError: async (error)=>{
|
|
32
36
|
logger.error(error);
|
|
37
|
+
error.handled = true;
|
|
33
38
|
},
|
|
34
39
|
headers,
|
|
35
40
|
logger,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lowdefy/api",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.6.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "",
|
|
6
6
|
"homepage": "https://lowdefy.com",
|
|
@@ -34,13 +34,13 @@
|
|
|
34
34
|
"dist/*"
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@lowdefy/ajv": "5.
|
|
38
|
-
"@lowdefy/errors": "5.
|
|
39
|
-
"@lowdefy/helpers": "5.
|
|
40
|
-
"@lowdefy/node-utils": "5.
|
|
41
|
-
"@lowdefy/nunjucks": "5.
|
|
42
|
-
"@lowdefy/operators": "5.
|
|
43
|
-
"@lowdefy/operators-js": "5.
|
|
37
|
+
"@lowdefy/ajv": "5.6.0",
|
|
38
|
+
"@lowdefy/errors": "5.6.0",
|
|
39
|
+
"@lowdefy/helpers": "5.6.0",
|
|
40
|
+
"@lowdefy/node-utils": "5.6.0",
|
|
41
|
+
"@lowdefy/nunjucks": "5.6.0",
|
|
42
|
+
"@lowdefy/operators": "5.6.0",
|
|
43
|
+
"@lowdefy/operators-js": "5.6.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@jest/globals": "28.1.3",
|