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