apcore-js 0.5.0 → 0.7.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/README.md +1 -1
- package/dist/acl.d.ts +27 -0
- package/dist/acl.d.ts.map +1 -0
- package/dist/acl.js +175 -0
- package/dist/acl.js.map +1 -0
- package/dist/approval.d.ts +85 -0
- package/dist/approval.d.ts.map +1 -0
- package/dist/approval.js +73 -0
- package/dist/approval.js.map +1 -0
- package/dist/async-task.d.ts +90 -0
- package/dist/async-task.d.ts.map +1 -0
- package/dist/async-task.js +215 -0
- package/dist/async-task.js.map +1 -0
- package/dist/bindings.d.ts +12 -0
- package/dist/bindings.d.ts.map +1 -0
- package/dist/bindings.js +185 -0
- package/dist/bindings.js.map +1 -0
- package/dist/cancel.d.ts +14 -0
- package/dist/cancel.d.ts.map +1 -0
- package/dist/cancel.js +27 -0
- package/dist/cancel.js.map +1 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +23 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +50 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +87 -0
- package/dist/context.js.map +1 -0
- package/dist/decorator.d.ts +57 -0
- package/dist/decorator.d.ts.map +1 -0
- package/dist/decorator.js +74 -0
- package/dist/decorator.js.map +1 -0
- package/dist/errors.d.ts +204 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +364 -0
- package/dist/errors.js.map +1 -0
- package/dist/executor.d.ts +82 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +489 -0
- package/dist/executor.js.map +1 -0
- package/dist/extensions.d.ts +58 -0
- package/dist/extensions.d.ts.map +1 -0
- package/dist/extensions.js +239 -0
- package/dist/extensions.js.map +1 -0
- package/{src/index.ts → dist/index.d.ts} +6 -63
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/adapters.d.ts +18 -0
- package/dist/middleware/adapters.d.ts.map +1 -0
- package/dist/middleware/adapters.js +25 -0
- package/dist/middleware/adapters.js.map +1 -0
- package/dist/middleware/base.d.ts +10 -0
- package/dist/middleware/base.d.ts.map +1 -0
- package/dist/middleware/base.js +15 -0
- package/dist/middleware/base.js.map +1 -0
- package/{src/middleware/index.ts → dist/middleware/index.d.ts} +1 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +5 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/logging.d.ts +25 -0
- package/dist/middleware/logging.d.ts.map +1 -0
- package/dist/middleware/logging.js +64 -0
- package/dist/middleware/logging.js.map +1 -0
- package/dist/middleware/manager.d.ts +21 -0
- package/dist/middleware/manager.d.ts.map +1 -0
- package/dist/middleware/manager.js +77 -0
- package/dist/middleware/manager.js.map +1 -0
- package/dist/module.d.ts +31 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +12 -0
- package/dist/module.js.map +1 -0
- package/dist/observability/context-logger.d.ts +54 -0
- package/dist/observability/context-logger.d.ts.map +1 -0
- package/dist/observability/context-logger.js +151 -0
- package/dist/observability/context-logger.js.map +1 -0
- package/{src/observability/index.ts → dist/observability/index.d.ts} +1 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +4 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/metrics.d.ts +30 -0
- package/dist/observability/metrics.d.ts.map +1 -0
- package/dist/observability/metrics.js +177 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/observability/tracing.d.ts +62 -0
- package/dist/observability/tracing.d.ts.map +1 -0
- package/dist/observability/tracing.js +184 -0
- package/dist/observability/tracing.js.map +1 -0
- package/dist/registry/dependencies.d.ts +6 -0
- package/dist/registry/dependencies.d.ts.map +1 -0
- package/dist/registry/dependencies.js +83 -0
- package/dist/registry/dependencies.js.map +1 -0
- package/dist/registry/entry-point.d.ts +6 -0
- package/dist/registry/entry-point.d.ts.map +1 -0
- package/dist/registry/entry-point.js +55 -0
- package/dist/registry/entry-point.js.map +1 -0
- package/{src/registry/index.ts → dist/registry/index.d.ts} +1 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +8 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/metadata.d.ts +9 -0
- package/dist/registry/metadata.d.ts.map +1 -0
- package/dist/registry/metadata.js +105 -0
- package/dist/registry/metadata.js.map +1 -0
- package/dist/registry/registry.d.ts +102 -0
- package/dist/registry/registry.d.ts.map +1 -0
- package/dist/registry/registry.js +534 -0
- package/dist/registry/registry.js.map +1 -0
- package/dist/registry/scanner.d.ts +7 -0
- package/dist/registry/scanner.d.ts.map +1 -0
- package/dist/registry/scanner.js +164 -0
- package/dist/registry/scanner.js.map +1 -0
- package/dist/registry/schema-export.d.ts +9 -0
- package/dist/registry/schema-export.d.ts.map +1 -0
- package/dist/registry/schema-export.js +132 -0
- package/dist/registry/schema-export.js.map +1 -0
- package/dist/registry/types.d.ts +29 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/types.js +5 -0
- package/dist/registry/types.js.map +1 -0
- package/dist/registry/validation.d.ts +9 -0
- package/dist/registry/validation.d.ts.map +1 -0
- package/dist/registry/validation.js +33 -0
- package/dist/registry/validation.js.map +1 -0
- package/dist/schema/annotations.d.ts +8 -0
- package/dist/schema/annotations.d.ts.map +1 -0
- package/dist/schema/annotations.js +52 -0
- package/dist/schema/annotations.js.map +1 -0
- package/dist/schema/exporter.d.ts +13 -0
- package/dist/schema/exporter.d.ts.map +1 -0
- package/dist/schema/exporter.js +71 -0
- package/dist/schema/exporter.js.map +1 -0
- package/dist/schema/index.d.ts +9 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/{src/schema/index.ts → dist/schema/index.js} +1 -7
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/loader.d.ts +30 -0
- package/dist/schema/loader.d.ts.map +1 -0
- package/dist/schema/loader.js +260 -0
- package/dist/schema/loader.js.map +1 -0
- package/dist/schema/ref-resolver.d.ts +19 -0
- package/dist/schema/ref-resolver.d.ts.map +1 -0
- package/dist/schema/ref-resolver.js +212 -0
- package/dist/schema/ref-resolver.js.map +1 -0
- package/dist/schema/strict.d.ts +7 -0
- package/dist/schema/strict.d.ts.map +1 -0
- package/dist/schema/strict.js +127 -0
- package/dist/schema/strict.js.map +1 -0
- package/dist/schema/types.d.ts +53 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +31 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/schema/validator.d.ts +16 -0
- package/dist/schema/validator.d.ts.map +1 -0
- package/dist/schema/validator.js +71 -0
- package/dist/schema/validator.js.map +1 -0
- package/dist/trace-context.d.ts +35 -0
- package/dist/trace-context.d.ts.map +1 -0
- package/dist/trace-context.js +86 -0
- package/dist/trace-context.js.map +1 -0
- package/dist/utils/index.d.ts +11 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +32 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/pattern.d.ts +5 -0
- package/dist/utils/pattern.d.ts.map +1 -0
- package/dist/utils/pattern.js +31 -0
- package/dist/utils/pattern.js.map +1 -0
- package/package.json +24 -3
- package/.claude/settings.local.json +0 -12
- package/.github/workflows/ci.yml +0 -39
- package/.gitmessage +0 -60
- package/.pre-commit-config.yaml +0 -28
- package/CHANGELOG.md +0 -214
- package/CLAUDE.md +0 -68
- package/apcore-logo.svg +0 -79
- package/planning/acl-system/overview.md +0 -54
- package/planning/acl-system/plan.md +0 -92
- package/planning/acl-system/state.json +0 -76
- package/planning/acl-system/tasks/acl-core.md +0 -226
- package/planning/acl-system/tasks/acl-rule.md +0 -92
- package/planning/acl-system/tasks/conditional-rules.md +0 -259
- package/planning/acl-system/tasks/pattern-matching.md +0 -152
- package/planning/acl-system/tasks/yaml-loading.md +0 -271
- package/planning/core-executor/overview.md +0 -53
- package/planning/core-executor/plan.md +0 -88
- package/planning/core-executor/state.json +0 -76
- package/planning/core-executor/tasks/async-support.md +0 -106
- package/planning/core-executor/tasks/execution-pipeline.md +0 -113
- package/planning/core-executor/tasks/redaction.md +0 -85
- package/planning/core-executor/tasks/safety-checks.md +0 -65
- package/planning/core-executor/tasks/setup.md +0 -75
- package/planning/decorator-bindings/overview.md +0 -62
- package/planning/decorator-bindings/plan.md +0 -104
- package/planning/decorator-bindings/state.json +0 -87
- package/planning/decorator-bindings/tasks/binding-directory.md +0 -79
- package/planning/decorator-bindings/tasks/binding-loader.md +0 -148
- package/planning/decorator-bindings/tasks/explicit-schemas.md +0 -85
- package/planning/decorator-bindings/tasks/function-module.md +0 -127
- package/planning/decorator-bindings/tasks/module-factory.md +0 -89
- package/planning/decorator-bindings/tasks/schema-modes.md +0 -142
- package/planning/middleware-system/overview.md +0 -48
- package/planning/middleware-system/plan.md +0 -102
- package/planning/middleware-system/state.json +0 -65
- package/planning/middleware-system/tasks/adapters.md +0 -170
- package/planning/middleware-system/tasks/base.md +0 -115
- package/planning/middleware-system/tasks/logging-middleware.md +0 -304
- package/planning/middleware-system/tasks/manager.md +0 -313
- package/planning/observability/overview.md +0 -53
- package/planning/observability/plan.md +0 -119
- package/planning/observability/state.json +0 -98
- package/planning/observability/tasks/context-logger.md +0 -201
- package/planning/observability/tasks/exporters.md +0 -121
- package/planning/observability/tasks/metrics-collector.md +0 -162
- package/planning/observability/tasks/metrics-middleware.md +0 -141
- package/planning/observability/tasks/obs-logging-middleware.md +0 -179
- package/planning/observability/tasks/span-model.md +0 -120
- package/planning/observability/tasks/tracing-middleware.md +0 -179
- package/planning/overview.md +0 -81
- package/planning/registry-system/overview.md +0 -57
- package/planning/registry-system/plan.md +0 -114
- package/planning/registry-system/state.json +0 -109
- package/planning/registry-system/tasks/dependencies.md +0 -157
- package/planning/registry-system/tasks/entry-point.md +0 -148
- package/planning/registry-system/tasks/metadata.md +0 -198
- package/planning/registry-system/tasks/registry-core.md +0 -323
- package/planning/registry-system/tasks/scanner.md +0 -172
- package/planning/registry-system/tasks/schema-export.md +0 -261
- package/planning/registry-system/tasks/types.md +0 -124
- package/planning/registry-system/tasks/validation.md +0 -177
- package/planning/schema-system/overview.md +0 -56
- package/planning/schema-system/plan.md +0 -121
- package/planning/schema-system/state.json +0 -98
- package/planning/schema-system/tasks/exporter.md +0 -153
- package/planning/schema-system/tasks/loader.md +0 -106
- package/planning/schema-system/tasks/ref-resolver.md +0 -133
- package/planning/schema-system/tasks/strict-mode.md +0 -140
- package/planning/schema-system/tasks/typebox-generation.md +0 -133
- package/planning/schema-system/tasks/types-and-annotations.md +0 -160
- package/planning/schema-system/tasks/validator.md +0 -149
- package/src/acl.ts +0 -200
- package/src/async-task.ts +0 -267
- package/src/bindings.ts +0 -207
- package/src/cancel.ts +0 -32
- package/src/config.ts +0 -24
- package/src/context.ts +0 -160
- package/src/decorator.ts +0 -110
- package/src/errors.ts +0 -429
- package/src/executor.ts +0 -493
- package/src/extensions.ts +0 -265
- package/src/middleware/adapters.ts +0 -54
- package/src/middleware/base.ts +0 -33
- package/src/middleware/logging.ts +0 -103
- package/src/middleware/manager.ts +0 -105
- package/src/module.ts +0 -43
- package/src/observability/context-logger.ts +0 -203
- package/src/observability/metrics.ts +0 -214
- package/src/observability/tracing.ts +0 -252
- package/src/registry/dependencies.ts +0 -99
- package/src/registry/entry-point.ts +0 -64
- package/src/registry/metadata.ts +0 -111
- package/src/registry/registry.ts +0 -580
- package/src/registry/scanner.ts +0 -168
- package/src/registry/schema-export.ts +0 -181
- package/src/registry/types.ts +0 -32
- package/src/registry/validation.ts +0 -38
- package/src/schema/annotations.ts +0 -68
- package/src/schema/exporter.ts +0 -90
- package/src/schema/loader.ts +0 -273
- package/src/schema/ref-resolver.ts +0 -244
- package/src/schema/strict.ts +0 -136
- package/src/schema/types.ts +0 -73
- package/src/schema/validator.ts +0 -82
- package/src/trace-context.ts +0 -102
- package/src/utils/index.ts +0 -5
- package/src/utils/pattern.ts +0 -30
- package/tests/async-task.test.ts +0 -335
- package/tests/helpers.ts +0 -30
- package/tests/integration/test-acl-safety.test.ts +0 -269
- package/tests/integration/test-binding-executor.test.ts +0 -194
- package/tests/integration/test-e2e-flow.test.ts +0 -117
- package/tests/integration/test-error-propagation.test.ts +0 -259
- package/tests/integration/test-middleware-chain.test.ts +0 -120
- package/tests/integration/test-observability-integration.test.ts +0 -438
- package/tests/observability/test-context-logger.test.ts +0 -123
- package/tests/observability/test-metrics.test.ts +0 -186
- package/tests/observability/test-tracing.test.ts +0 -303
- package/tests/registry/test-dependencies.test.ts +0 -70
- package/tests/registry/test-entry-point.test.ts +0 -133
- package/tests/registry/test-metadata.test.ts +0 -265
- package/tests/registry/test-registry.test.ts +0 -1397
- package/tests/registry/test-scanner.test.ts +0 -257
- package/tests/registry/test-schema-export.test.ts +0 -355
- package/tests/registry/test-validation.test.ts +0 -75
- package/tests/schema/test-annotations.test.ts +0 -137
- package/tests/schema/test-exporter.test.ts +0 -172
- package/tests/schema/test-loader.test.ts +0 -461
- package/tests/schema/test-ref-resolver.test.ts +0 -530
- package/tests/schema/test-strict.test.ts +0 -348
- package/tests/schema/test-validator.test.ts +0 -64
- package/tests/test-acl.test.ts +0 -423
- package/tests/test-bindings.test.ts +0 -227
- package/tests/test-cancel.test.ts +0 -71
- package/tests/test-config.test.ts +0 -76
- package/tests/test-context.test.ts +0 -266
- package/tests/test-decorator.test.ts +0 -173
- package/tests/test-errors.test.ts +0 -647
- package/tests/test-executor-stream.test.ts +0 -208
- package/tests/test-executor.test.ts +0 -252
- package/tests/test-extensions.test.ts +0 -310
- package/tests/test-logging-middleware.test.ts +0 -150
- package/tests/test-middleware-manager.test.ts +0 -185
- package/tests/test-middleware.test.ts +0 -86
- package/tests/test-trace-context.test.ts +0 -251
- package/tests/utils/test-pattern.test.ts +0 -109
- package/tsconfig.build.json +0 -8
- package/tsconfig.json +0 -20
- package/vitest.config.ts +0 -18
package/src/executor.ts
DELETED
|
@@ -1,493 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Executor and related utilities for apcore.
|
|
3
|
-
*
|
|
4
|
-
* Async-only execution pipeline. Python's call() + call_async() merge into one async call().
|
|
5
|
-
* Timeout uses Promise.race instead of threading.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { TSchema } from '@sinclair/typebox';
|
|
9
|
-
import { Value } from '@sinclair/typebox/value';
|
|
10
|
-
import type { ACL } from './acl.js';
|
|
11
|
-
import type { Config } from './config.js';
|
|
12
|
-
import { Context } from './context.js';
|
|
13
|
-
import { ExecutionCancelledError } from './cancel.js';
|
|
14
|
-
import {
|
|
15
|
-
ACLDeniedError,
|
|
16
|
-
CallDepthExceededError,
|
|
17
|
-
CallFrequencyExceededError,
|
|
18
|
-
CircularCallError,
|
|
19
|
-
InvalidInputError,
|
|
20
|
-
ModuleNotFoundError,
|
|
21
|
-
ModuleTimeoutError,
|
|
22
|
-
SchemaValidationError,
|
|
23
|
-
} from './errors.js';
|
|
24
|
-
import { AfterMiddleware, BeforeMiddleware, Middleware } from './middleware/index.js';
|
|
25
|
-
import { MiddlewareChainError, MiddlewareManager } from './middleware/manager.js';
|
|
26
|
-
import type { ValidationResult } from './module.js';
|
|
27
|
-
import type { Registry } from './registry/registry.js';
|
|
28
|
-
|
|
29
|
-
export const REDACTED_VALUE: string = '***REDACTED***';
|
|
30
|
-
|
|
31
|
-
export function redactSensitive(
|
|
32
|
-
data: Record<string, unknown>,
|
|
33
|
-
schemaDict: Record<string, unknown>,
|
|
34
|
-
): Record<string, unknown> {
|
|
35
|
-
const redacted = JSON.parse(JSON.stringify(data));
|
|
36
|
-
redactFields(redacted, schemaDict);
|
|
37
|
-
redactSecretPrefix(redacted);
|
|
38
|
-
return redacted;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function redactFields(data: Record<string, unknown>, schemaDict: Record<string, unknown>): void {
|
|
42
|
-
const properties = schemaDict['properties'] as Record<string, Record<string, unknown>> | undefined;
|
|
43
|
-
if (!properties) return;
|
|
44
|
-
|
|
45
|
-
for (const [fieldName, fieldSchema] of Object.entries(properties)) {
|
|
46
|
-
if (!(fieldName in data)) continue;
|
|
47
|
-
const value = data[fieldName];
|
|
48
|
-
|
|
49
|
-
if (fieldSchema['x-sensitive'] === true) {
|
|
50
|
-
if (value !== null && value !== undefined) {
|
|
51
|
-
data[fieldName] = REDACTED_VALUE;
|
|
52
|
-
}
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (fieldSchema['type'] === 'object' && 'properties' in fieldSchema && typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
57
|
-
redactFields(value as Record<string, unknown>, fieldSchema);
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (fieldSchema['type'] === 'array' && 'items' in fieldSchema && Array.isArray(value)) {
|
|
62
|
-
const itemsSchema = fieldSchema['items'] as Record<string, unknown>;
|
|
63
|
-
if (itemsSchema['x-sensitive'] === true) {
|
|
64
|
-
for (let i = 0; i < value.length; i++) {
|
|
65
|
-
if (value[i] !== null && value[i] !== undefined) {
|
|
66
|
-
value[i] = REDACTED_VALUE;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
} else if (itemsSchema['type'] === 'object' && 'properties' in itemsSchema) {
|
|
70
|
-
for (const item of value) {
|
|
71
|
-
if (typeof item === 'object' && item !== null) {
|
|
72
|
-
redactFields(item as Record<string, unknown>, itemsSchema);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function redactSecretPrefix(data: Record<string, unknown>): void {
|
|
81
|
-
for (const key of Object.keys(data)) {
|
|
82
|
-
const value = data[key];
|
|
83
|
-
if (key.startsWith('_secret_') && value !== null && value !== undefined) {
|
|
84
|
-
data[key] = REDACTED_VALUE;
|
|
85
|
-
} else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
86
|
-
redactSecretPrefix(value as Record<string, unknown>);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export class Executor {
|
|
92
|
-
private _registry: Registry;
|
|
93
|
-
private _middlewareManager: MiddlewareManager;
|
|
94
|
-
private _acl: ACL | null;
|
|
95
|
-
private _config: Config | null;
|
|
96
|
-
private _defaultTimeout: number;
|
|
97
|
-
private _maxCallDepth: number;
|
|
98
|
-
private _maxModuleRepeat: number;
|
|
99
|
-
|
|
100
|
-
constructor(options: {
|
|
101
|
-
registry: Registry;
|
|
102
|
-
middlewares?: Middleware[] | null;
|
|
103
|
-
acl?: ACL | null;
|
|
104
|
-
config?: Config | null;
|
|
105
|
-
}) {
|
|
106
|
-
this._registry = options.registry;
|
|
107
|
-
this._middlewareManager = new MiddlewareManager();
|
|
108
|
-
this._acl = options.acl ?? null;
|
|
109
|
-
this._config = options.config ?? null;
|
|
110
|
-
|
|
111
|
-
if (options.middlewares) {
|
|
112
|
-
for (const mw of options.middlewares) {
|
|
113
|
-
this._middlewareManager.add(mw);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (this._config !== null) {
|
|
118
|
-
this._defaultTimeout = (this._config.get('executor.default_timeout') as number) ?? 30000;
|
|
119
|
-
this._maxCallDepth = (this._config.get('executor.max_call_depth') as number) ?? 32;
|
|
120
|
-
this._maxModuleRepeat = (this._config.get('executor.max_module_repeat') as number) ?? 3;
|
|
121
|
-
} else {
|
|
122
|
-
this._defaultTimeout = 30000;
|
|
123
|
-
this._maxCallDepth = 32;
|
|
124
|
-
this._maxModuleRepeat = 3;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
get registry(): Registry {
|
|
129
|
-
return this._registry;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
get middlewares(): Middleware[] {
|
|
133
|
-
return this._middlewareManager.snapshot();
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/** Set the access control provider. */
|
|
137
|
-
setAcl(acl: ACL): void {
|
|
138
|
-
this._acl = acl;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
use(middleware: Middleware): Executor {
|
|
142
|
-
this._middlewareManager.add(middleware);
|
|
143
|
-
return this;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
useBefore(callback: (moduleId: string, inputs: Record<string, unknown>, context: Context) => Record<string, unknown> | null): Executor {
|
|
147
|
-
this._middlewareManager.add(new BeforeMiddleware(callback));
|
|
148
|
-
return this;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
useAfter(callback: (moduleId: string, inputs: Record<string, unknown>, output: Record<string, unknown>, context: Context) => Record<string, unknown> | null): Executor {
|
|
152
|
-
this._middlewareManager.add(new AfterMiddleware(callback));
|
|
153
|
-
return this;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
remove(middleware: Middleware): boolean {
|
|
157
|
-
return this._middlewareManager.remove(middleware);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
async call(
|
|
161
|
-
moduleId: string,
|
|
162
|
-
inputs?: Record<string, unknown> | null,
|
|
163
|
-
context?: Context | null,
|
|
164
|
-
): Promise<Record<string, unknown>> {
|
|
165
|
-
let effectiveInputs = inputs ?? {};
|
|
166
|
-
const ctx = this._createContext(moduleId, context);
|
|
167
|
-
this._checkSafety(moduleId, ctx);
|
|
168
|
-
|
|
169
|
-
const mod = this._lookupModule(moduleId);
|
|
170
|
-
this._checkAcl(moduleId, ctx);
|
|
171
|
-
|
|
172
|
-
effectiveInputs = this._validateInputs(mod, effectiveInputs, ctx);
|
|
173
|
-
|
|
174
|
-
return this._executeWithMiddleware(mod, moduleId, effectiveInputs, ctx);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Alias for call(). Provided for compatibility with MCP bridge packages
|
|
179
|
-
* that may call callAsync() by convention.
|
|
180
|
-
*/
|
|
181
|
-
async callAsync(
|
|
182
|
-
moduleId: string,
|
|
183
|
-
inputs?: Record<string, unknown> | null,
|
|
184
|
-
context?: Context | null,
|
|
185
|
-
): Promise<Record<string, unknown>> {
|
|
186
|
-
return this.call(moduleId, inputs, context);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Streaming execution pipeline. If the module exposes a stream() async generator,
|
|
191
|
-
* yields each chunk. Otherwise falls back to call() and yields a single chunk.
|
|
192
|
-
*
|
|
193
|
-
* Pipeline: context -> safety -> lookup -> ACL -> validate inputs -> before-middleware
|
|
194
|
-
* -> stream (or fallback to execute) -> validate accumulated output -> after-middleware
|
|
195
|
-
*
|
|
196
|
-
* Note: In the streaming path, after-middleware runs on the accumulated output for
|
|
197
|
-
* validation/side-effects but its return value is not yielded since chunks were already
|
|
198
|
-
* emitted. In the non-streaming fallback, after-middleware can transform the output.
|
|
199
|
-
*/
|
|
200
|
-
async *stream(
|
|
201
|
-
moduleId: string,
|
|
202
|
-
inputs?: Record<string, unknown> | null,
|
|
203
|
-
context?: Context | null,
|
|
204
|
-
): AsyncGenerator<Record<string, unknown>> {
|
|
205
|
-
let effectiveInputs = inputs ?? {};
|
|
206
|
-
const ctx = this._createContext(moduleId, context);
|
|
207
|
-
this._checkSafety(moduleId, ctx);
|
|
208
|
-
|
|
209
|
-
const mod = this._lookupModule(moduleId);
|
|
210
|
-
this._checkAcl(moduleId, ctx);
|
|
211
|
-
|
|
212
|
-
effectiveInputs = this._validateInputs(mod, effectiveInputs, ctx);
|
|
213
|
-
|
|
214
|
-
yield* this._streamWithMiddleware(mod, moduleId, effectiveInputs, ctx);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
private async *_streamWithMiddleware(
|
|
218
|
-
mod: Record<string, unknown>,
|
|
219
|
-
moduleId: string,
|
|
220
|
-
inputs: Record<string, unknown>,
|
|
221
|
-
ctx: Context,
|
|
222
|
-
): AsyncGenerator<Record<string, unknown>> {
|
|
223
|
-
let effectiveInputs = inputs;
|
|
224
|
-
let executedMiddlewares: Middleware[] = [];
|
|
225
|
-
|
|
226
|
-
try {
|
|
227
|
-
try {
|
|
228
|
-
[effectiveInputs, executedMiddlewares] = this._middlewareManager.executeBefore(moduleId, effectiveInputs, ctx);
|
|
229
|
-
} catch (e) {
|
|
230
|
-
if (e instanceof MiddlewareChainError) {
|
|
231
|
-
executedMiddlewares = e.executedMiddlewares;
|
|
232
|
-
const recovery = this._middlewareManager.executeOnError(
|
|
233
|
-
moduleId, effectiveInputs, e.original, ctx, executedMiddlewares,
|
|
234
|
-
);
|
|
235
|
-
if (recovery !== null) {
|
|
236
|
-
yield recovery;
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
executedMiddlewares = [];
|
|
240
|
-
throw e.original;
|
|
241
|
-
}
|
|
242
|
-
throw e;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Cancel check before execution
|
|
246
|
-
if (ctx.cancelToken !== null) {
|
|
247
|
-
ctx.cancelToken.check();
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const streamFn = mod['stream'] as
|
|
251
|
-
| ((inputs: Record<string, unknown>, context: Context) => AsyncGenerator<Record<string, unknown>>)
|
|
252
|
-
| undefined;
|
|
253
|
-
|
|
254
|
-
if (typeof streamFn === 'function') {
|
|
255
|
-
// Module has a stream() method: iterate and yield each chunk
|
|
256
|
-
let accumulated: Record<string, unknown> = {};
|
|
257
|
-
for await (const chunk of streamFn.call(mod, effectiveInputs, ctx)) {
|
|
258
|
-
accumulated = { ...accumulated, ...chunk };
|
|
259
|
-
yield chunk;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Validate accumulated output against output schema
|
|
263
|
-
this._validateOutput(mod, accumulated);
|
|
264
|
-
|
|
265
|
-
// Run after-middleware on the accumulated result
|
|
266
|
-
this._middlewareManager.executeAfter(moduleId, effectiveInputs, accumulated, ctx);
|
|
267
|
-
} else {
|
|
268
|
-
// Fallback: execute normally and yield single chunk
|
|
269
|
-
let output = await this._executeWithTimeout(mod, moduleId, effectiveInputs, ctx);
|
|
270
|
-
this._validateOutput(mod, output);
|
|
271
|
-
output = this._middlewareManager.executeAfter(moduleId, effectiveInputs, output, ctx);
|
|
272
|
-
yield output;
|
|
273
|
-
}
|
|
274
|
-
} catch (exc) {
|
|
275
|
-
if (exc instanceof ExecutionCancelledError) throw exc;
|
|
276
|
-
if (executedMiddlewares.length > 0) {
|
|
277
|
-
const recovery = this._middlewareManager.executeOnError(
|
|
278
|
-
moduleId, effectiveInputs, exc as Error, ctx, executedMiddlewares,
|
|
279
|
-
);
|
|
280
|
-
if (recovery !== null) {
|
|
281
|
-
yield recovery;
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
throw exc;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
private _createContext(moduleId: string, context?: Context | null): Context {
|
|
290
|
-
if (context == null) {
|
|
291
|
-
return Context.create(this).child(moduleId);
|
|
292
|
-
}
|
|
293
|
-
return context.child(moduleId);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
private _lookupModule(moduleId: string): Record<string, unknown> {
|
|
297
|
-
const module = this._registry.get(moduleId);
|
|
298
|
-
if (module === null) {
|
|
299
|
-
throw new ModuleNotFoundError(moduleId);
|
|
300
|
-
}
|
|
301
|
-
return module as Record<string, unknown>;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
private _checkAcl(moduleId: string, ctx: Context): void {
|
|
305
|
-
if (this._acl !== null) {
|
|
306
|
-
const allowed = this._acl.check(ctx.callerId, moduleId, ctx);
|
|
307
|
-
if (!allowed) {
|
|
308
|
-
throw new ACLDeniedError(ctx.callerId, moduleId);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
private _validateInputs(
|
|
314
|
-
mod: Record<string, unknown>,
|
|
315
|
-
inputs: Record<string, unknown>,
|
|
316
|
-
ctx: Context,
|
|
317
|
-
): Record<string, unknown> {
|
|
318
|
-
const inputSchema = mod['inputSchema'] as TSchema | undefined;
|
|
319
|
-
if (inputSchema == null) return inputs;
|
|
320
|
-
|
|
321
|
-
this._validateSchema(inputSchema, inputs, 'Input');
|
|
322
|
-
ctx.redactedInputs = redactSensitive(
|
|
323
|
-
inputs,
|
|
324
|
-
inputSchema as unknown as Record<string, unknown>,
|
|
325
|
-
);
|
|
326
|
-
return inputs;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
private _validateSchema(
|
|
330
|
-
schema: TSchema,
|
|
331
|
-
data: Record<string, unknown>,
|
|
332
|
-
direction: string,
|
|
333
|
-
): void {
|
|
334
|
-
if (Value.Check(schema, data)) return;
|
|
335
|
-
|
|
336
|
-
const errors: Array<Record<string, unknown>> = [];
|
|
337
|
-
for (const error of Value.Errors(schema, data)) {
|
|
338
|
-
errors.push({
|
|
339
|
-
field: error.path || '/',
|
|
340
|
-
code: String(error.type),
|
|
341
|
-
message: error.message,
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
throw new SchemaValidationError(`${direction} validation failed`, errors);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
private async _executeWithMiddleware(
|
|
348
|
-
mod: Record<string, unknown>,
|
|
349
|
-
moduleId: string,
|
|
350
|
-
inputs: Record<string, unknown>,
|
|
351
|
-
ctx: Context,
|
|
352
|
-
): Promise<Record<string, unknown>> {
|
|
353
|
-
let effectiveInputs = inputs;
|
|
354
|
-
let executedMiddlewares: Middleware[] = [];
|
|
355
|
-
|
|
356
|
-
try {
|
|
357
|
-
try {
|
|
358
|
-
[effectiveInputs, executedMiddlewares] = this._middlewareManager.executeBefore(moduleId, effectiveInputs, ctx);
|
|
359
|
-
} catch (e) {
|
|
360
|
-
if (e instanceof MiddlewareChainError) {
|
|
361
|
-
executedMiddlewares = e.executedMiddlewares;
|
|
362
|
-
const recovery = this._middlewareManager.executeOnError(
|
|
363
|
-
moduleId, effectiveInputs, e.original, ctx, executedMiddlewares,
|
|
364
|
-
);
|
|
365
|
-
if (recovery !== null) return recovery;
|
|
366
|
-
executedMiddlewares = [];
|
|
367
|
-
throw e.original;
|
|
368
|
-
}
|
|
369
|
-
throw e;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// Cancel check before execution
|
|
373
|
-
if (ctx.cancelToken !== null) {
|
|
374
|
-
ctx.cancelToken.check();
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
let output = await this._executeWithTimeout(mod, moduleId, effectiveInputs, ctx);
|
|
378
|
-
|
|
379
|
-
this._validateOutput(mod, output);
|
|
380
|
-
|
|
381
|
-
output = this._middlewareManager.executeAfter(moduleId, effectiveInputs, output, ctx);
|
|
382
|
-
return output;
|
|
383
|
-
} catch (exc) {
|
|
384
|
-
if (exc instanceof ExecutionCancelledError) throw exc;
|
|
385
|
-
if (executedMiddlewares.length > 0) {
|
|
386
|
-
const recovery = this._middlewareManager.executeOnError(
|
|
387
|
-
moduleId, effectiveInputs, exc as Error, ctx, executedMiddlewares,
|
|
388
|
-
);
|
|
389
|
-
if (recovery !== null) return recovery;
|
|
390
|
-
}
|
|
391
|
-
throw exc;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
private _validateOutput(mod: Record<string, unknown>, output: Record<string, unknown>): void {
|
|
396
|
-
const outputSchema = mod['outputSchema'] as TSchema | undefined;
|
|
397
|
-
if (outputSchema != null) {
|
|
398
|
-
this._validateSchema(outputSchema, output, 'Output');
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
validate(moduleId: string, inputs: Record<string, unknown>): ValidationResult {
|
|
403
|
-
const module = this._registry.get(moduleId);
|
|
404
|
-
if (module === null) {
|
|
405
|
-
throw new ModuleNotFoundError(moduleId);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
const mod = module as Record<string, unknown>;
|
|
409
|
-
const inputSchema = mod['inputSchema'] as TSchema | undefined;
|
|
410
|
-
|
|
411
|
-
if (inputSchema == null) {
|
|
412
|
-
return { valid: true, errors: [] };
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
if (Value.Check(inputSchema, inputs)) {
|
|
416
|
-
return { valid: true, errors: [] };
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
const errors: Array<Record<string, string>> = [];
|
|
420
|
-
for (const error of Value.Errors(inputSchema, inputs)) {
|
|
421
|
-
errors.push({
|
|
422
|
-
field: error.path || '/',
|
|
423
|
-
code: String(error.type),
|
|
424
|
-
message: error.message,
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
return { valid: false, errors };
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
private _checkSafety(moduleId: string, ctx: Context): void {
|
|
431
|
-
const callChain = ctx.callChain;
|
|
432
|
-
|
|
433
|
-
// Depth check
|
|
434
|
-
if (callChain.length > this._maxCallDepth) {
|
|
435
|
-
throw new CallDepthExceededError(callChain.length, this._maxCallDepth, [...callChain]);
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
// Circular detection (strict cycles of length >= 2)
|
|
439
|
-
const priorChain = callChain.slice(0, -1);
|
|
440
|
-
const lastIdx = priorChain.lastIndexOf(moduleId);
|
|
441
|
-
if (lastIdx !== -1) {
|
|
442
|
-
const subsequence = priorChain.slice(lastIdx + 1);
|
|
443
|
-
if (subsequence.length > 0) {
|
|
444
|
-
throw new CircularCallError(moduleId, [...callChain]);
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// Frequency check
|
|
449
|
-
const count = callChain.filter((id) => id === moduleId).length;
|
|
450
|
-
if (count > this._maxModuleRepeat) {
|
|
451
|
-
throw new CallFrequencyExceededError(moduleId, count, this._maxModuleRepeat, [...callChain]);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
private async _executeWithTimeout(
|
|
456
|
-
mod: Record<string, unknown>,
|
|
457
|
-
moduleId: string,
|
|
458
|
-
inputs: Record<string, unknown>,
|
|
459
|
-
ctx: Context,
|
|
460
|
-
): Promise<Record<string, unknown>> {
|
|
461
|
-
const timeoutMs = this._defaultTimeout;
|
|
462
|
-
|
|
463
|
-
if (timeoutMs < 0) {
|
|
464
|
-
throw new InvalidInputError(`Negative timeout: ${timeoutMs}ms`);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
const executeFn = mod['execute'];
|
|
468
|
-
if (typeof executeFn !== 'function') {
|
|
469
|
-
throw new InvalidInputError(`Module '${moduleId}' has no execute method`);
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
const executionPromise = Promise.resolve(
|
|
473
|
-
(executeFn as (inputs: Record<string, unknown>, context: Context) => Promise<Record<string, unknown>> | Record<string, unknown>)
|
|
474
|
-
.call(mod, inputs, ctx),
|
|
475
|
-
);
|
|
476
|
-
|
|
477
|
-
if (timeoutMs === 0) {
|
|
478
|
-
console.warn('[apcore:executor] Timeout is 0, timeout limit disabled');
|
|
479
|
-
return executionPromise;
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
let timer: ReturnType<typeof setTimeout>;
|
|
483
|
-
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
484
|
-
timer = setTimeout(() => {
|
|
485
|
-
reject(new ModuleTimeoutError(moduleId, timeoutMs));
|
|
486
|
-
}, timeoutMs);
|
|
487
|
-
});
|
|
488
|
-
|
|
489
|
-
return Promise.race([executionPromise, timeoutPromise]).finally(() => {
|
|
490
|
-
clearTimeout(timer);
|
|
491
|
-
});
|
|
492
|
-
}
|
|
493
|
-
}
|