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
@@ -1,87 +0,0 @@
1
- {
2
- "feature": "decorator-bindings",
3
- "created": "2026-02-16T00:00:00Z",
4
- "updated": "2026-02-16T00:00:00Z",
5
- "status": "completed",
6
- "execution_order": [
7
- "function-module",
8
- "module-factory",
9
- "explicit-schemas",
10
- "binding-loader",
11
- "binding-directory",
12
- "schema-modes"
13
- ],
14
- "progress": {
15
- "total_tasks": 6,
16
- "completed": 6,
17
- "in_progress": 0,
18
- "pending": 0
19
- },
20
- "tasks": [
21
- {
22
- "id": "function-module",
23
- "file": "tasks/function-module.md",
24
- "title": "FunctionModule Class with Execute, Schemas, and normalizeResult()",
25
- "status": "completed",
26
- "started_at": "2026-02-16T08:00:00Z",
27
- "completed_at": "2026-02-16T09:30:00Z",
28
- "assignee": null,
29
- "commits": []
30
- },
31
- {
32
- "id": "module-factory",
33
- "file": "tasks/module-factory.md",
34
- "title": "module() Factory Function with Options Object Pattern",
35
- "status": "completed",
36
- "started_at": "2026-02-16T09:30:00Z",
37
- "completed_at": "2026-02-16T11:00:00Z",
38
- "assignee": null,
39
- "commits": []
40
- },
41
- {
42
- "id": "explicit-schemas",
43
- "file": "tasks/explicit-schemas.md",
44
- "title": "Explicit TypeBox Schema Passing",
45
- "status": "completed",
46
- "started_at": "2026-02-16T11:00:00Z",
47
- "completed_at": "2026-02-16T11:45:00Z",
48
- "assignee": null,
49
- "commits": []
50
- },
51
- {
52
- "id": "binding-loader",
53
- "file": "tasks/binding-loader.md",
54
- "title": "BindingLoader with Async loadBindings() from YAML",
55
- "status": "completed",
56
- "started_at": "2026-02-16T11:45:00Z",
57
- "completed_at": "2026-02-16T14:30:00Z",
58
- "assignee": null,
59
- "commits": []
60
- },
61
- {
62
- "id": "binding-directory",
63
- "file": "tasks/binding-directory.md",
64
- "title": "loadBindingDir() for Directory Scanning of Binding YAML Files",
65
- "status": "completed",
66
- "started_at": "2026-02-16T14:30:00Z",
67
- "completed_at": "2026-02-16T16:00:00Z",
68
- "assignee": null,
69
- "commits": []
70
- },
71
- {
72
- "id": "schema-modes",
73
- "file": "tasks/schema-modes.md",
74
- "title": "Schema Resolution Modes: Inline, schema_ref, Permissive Fallback",
75
- "status": "completed",
76
- "started_at": "2026-02-16T16:00:00Z",
77
- "completed_at": "2026-02-16T18:30:00Z",
78
- "assignee": null,
79
- "commits": []
80
- }
81
- ],
82
- "metadata": {
83
- "source_doc": "planning/features/decorator-bindings.md",
84
- "created_by": "code-forge",
85
- "version": "1.0"
86
- }
87
- }
@@ -1,79 +0,0 @@
1
- # Task: loadBindingDir() for Directory Scanning of Binding YAML Files
2
-
3
- ## Goal
4
-
5
- Implement the `loadBindingDir()` method on `BindingLoader` that scans a directory for binding YAML files matching a glob pattern (default `*.binding.yaml`), loads each one via `loadBindings()`, and returns all created `FunctionModule` instances. This enables convention-based module registration where all binding files in a directory are automatically discovered and loaded.
6
-
7
- ## Files Involved
8
-
9
- - `src/bindings.ts` -- `BindingLoader.loadBindingDir()` method
10
- - `tests/test-bindings.test.ts` -- Unit tests for loadBindingDir()
11
-
12
- ## Steps
13
-
14
- ### 1. Write failing tests (TDD)
15
-
16
- Create tests for:
17
- - **loadBindingDir throws on nonexistent directory**: Throws `BindingFileInvalidError` with "Directory does not exist"
18
- - **loadBindingDir throws on file path (not directory)**: A path pointing to a file throws `BindingFileInvalidError`
19
- - **loadBindingDir returns empty array for empty directory**: Directory with no matching files returns `[]`
20
- - **loadBindingDir loads matching files in sorted order**: Files are processed alphabetically
21
- - **loadBindingDir respects custom pattern**: `pattern: '*.modules.yaml'` only matches files with that suffix
22
-
23
- ### 2. Implement loadBindingDir()
24
-
25
- ```typescript
26
- async loadBindingDir(
27
- dirPath: string,
28
- registry: Registry,
29
- pattern: string = '*.binding.yaml',
30
- ): Promise<FunctionModule[]> {
31
- if (!existsSync(dirPath) || !statSync(dirPath).isDirectory()) {
32
- throw new BindingFileInvalidError(dirPath, 'Directory does not exist');
33
- }
34
-
35
- const files = readdirSync(dirPath)
36
- .filter((f) => {
37
- // Simple glob matching: extract suffix after the wildcard
38
- const suffix = pattern.replace('*', '');
39
- return f.endsWith(suffix);
40
- })
41
- .sort();
42
-
43
- const results: FunctionModule[] = [];
44
- for (const f of files) {
45
- const fms = await this.loadBindings(join(dirPath, f), registry);
46
- results.push(...fms);
47
- }
48
- return results;
49
- }
50
- ```
51
-
52
- Key design decisions:
53
- - **Simple glob matching**: Only supports `*` prefix patterns (e.g., `*.binding.yaml`). The wildcard is stripped and the remaining suffix is used for `endsWith()` matching. This covers the primary use case without introducing a glob library dependency.
54
- - **Sorted file order**: Files are sorted alphabetically to ensure deterministic loading order across platforms.
55
- - **Sequential loading**: Files are loaded sequentially (not in parallel) to maintain deterministic registration order in the registry. This is acceptable since binding loading is a startup-time operation.
56
- - **Sync directory listing**: Uses `readdirSync` and `statSync` for directory operations (same rationale as `loadBindings`'s use of `readFileSync`).
57
-
58
- ### 3. Verify tests pass
59
-
60
- Run `npx vitest run tests/test-bindings.test.ts`.
61
-
62
- ## Acceptance Criteria
63
-
64
- - [x] `loadBindingDir()` scans directory for files matching the pattern
65
- - [x] Default pattern is `'*.binding.yaml'`
66
- - [x] Custom patterns like `'*.modules.yaml'` are supported
67
- - [x] Files are processed in sorted (alphabetical) order
68
- - [x] Each matching file is loaded via `loadBindings()` and results are aggregated
69
- - [x] Throws `BindingFileInvalidError` when directory does not exist
70
- - [x] Throws `BindingFileInvalidError` when path is a file, not a directory
71
- - [x] Returns empty array when no files match the pattern
72
-
73
- ## Dependencies
74
-
75
- - `binding-loader` -- Requires `BindingLoader` class with `loadBindings()` method
76
-
77
- ## Estimated Time
78
-
79
- 2 hours
@@ -1,148 +0,0 @@
1
- # Task: BindingLoader with Async loadBindings() from YAML
2
-
3
- ## Goal
4
-
5
- Implement the `BindingLoader` class that reads YAML binding configuration files, resolves target strings to callable functions via dynamic `import()`, wraps them in `FunctionModule` instances, and registers them with a `Registry`. This enables zero-code-modification module registration: existing functions can be exposed as apcore modules purely through YAML configuration.
6
-
7
- ## Files Involved
8
-
9
- - `src/bindings.ts` -- `BindingLoader` class with `loadBindings()`, `resolveTarget()`, `_createModuleFromBinding()`
10
- - `src/errors.ts` -- `BindingFileInvalidError`, `BindingInvalidTargetError`, `BindingModuleNotFoundError`, `BindingCallableNotFoundError`, `BindingNotCallableError`
11
- - `tests/test-bindings.test.ts` -- Unit tests for BindingLoader
12
-
13
- ## Steps
14
-
15
- ### 1. Write failing tests (TDD)
16
-
17
- Create tests for:
18
- - **BindingLoader instantiation**: `new BindingLoader()` creates a valid instance
19
- - **resolveTarget throws on missing colon**: `resolveTarget('no_colon')` throws `BindingInvalidTargetError`
20
- - **loadBindings throws on nonexistent file**: Throws `BindingFileInvalidError` with file path
21
- - **loadBindings throws on invalid YAML**: Malformed YAML throws `BindingFileInvalidError`
22
- - **loadBindings throws on missing bindings key**: YAML without `bindings` key throws `BindingFileInvalidError`
23
- - **loadBindings throws on non-array bindings**: `bindings: "not_a_list"` throws `BindingFileInvalidError`
24
- - **loadBindings throws on missing module_id**: Binding entry without `module_id` throws `BindingFileInvalidError`
25
- - **loadBindings throws on missing target**: Binding entry without `target` throws `BindingFileInvalidError`
26
-
27
- ### 2. Implement resolveTarget()
28
-
29
- ```typescript
30
- async resolveTarget(targetString: string): Promise<(...args: unknown[]) => unknown> {
31
- if (!targetString.includes(':')) {
32
- throw new BindingInvalidTargetError(targetString);
33
- }
34
-
35
- const [modulePath, callableName] = targetString.split(':', 2);
36
-
37
- let mod: Record<string, unknown>;
38
- try {
39
- mod = await import(modulePath);
40
- } catch (e) {
41
- throw new BindingModuleNotFoundError(modulePath);
42
- }
43
-
44
- // Handle Class.method syntax
45
- if (callableName.includes('.')) {
46
- const [className, methodName] = callableName.split('.', 2);
47
- const cls = mod[className];
48
- if (cls == null) throw new BindingCallableNotFoundError(className, modulePath);
49
- let instance: Record<string, unknown>;
50
- try {
51
- instance = new (cls as new () => Record<string, unknown>)();
52
- } catch {
53
- throw new BindingCallableNotFoundError(callableName, modulePath);
54
- }
55
- const method = instance[methodName];
56
- if (method == null) throw new BindingCallableNotFoundError(callableName, modulePath);
57
- if (typeof method !== 'function') throw new BindingNotCallableError(targetString);
58
- return method.bind(instance) as (...args: unknown[]) => unknown;
59
- }
60
-
61
- // Handle plain function export
62
- const result = mod[callableName];
63
- if (result == null) throw new BindingCallableNotFoundError(callableName, modulePath);
64
- if (typeof result !== 'function') throw new BindingNotCallableError(targetString);
65
- return result as (...args: unknown[]) => unknown;
66
- }
67
- ```
68
-
69
- ### 3. Implement loadBindings()
70
-
71
- ```typescript
72
- async loadBindings(filePath: string, registry: Registry): Promise<FunctionModule[]> {
73
- const bindingFileDir = dirname(filePath);
74
-
75
- let content: string;
76
- try {
77
- content = readFileSync(filePath, 'utf-8');
78
- } catch (e) {
79
- throw new BindingFileInvalidError(filePath, String(e));
80
- }
81
-
82
- let data: unknown;
83
- try {
84
- data = yaml.load(content);
85
- } catch (e) {
86
- throw new BindingFileInvalidError(filePath, `YAML parse error: ${e}`);
87
- }
88
-
89
- // Validate structure
90
- if (data === null || data === undefined) {
91
- throw new BindingFileInvalidError(filePath, 'File is empty');
92
- }
93
- const dataObj = data as Record<string, unknown>;
94
- if (!('bindings' in dataObj)) {
95
- throw new BindingFileInvalidError(filePath, "Missing 'bindings' key");
96
- }
97
- const bindings = dataObj['bindings'];
98
- if (!Array.isArray(bindings)) {
99
- throw new BindingFileInvalidError(filePath, "'bindings' must be a list");
100
- }
101
-
102
- const results: FunctionModule[] = [];
103
- for (const entry of bindings) {
104
- const entryObj = entry as Record<string, unknown>;
105
- if (!('module_id' in entryObj)) {
106
- throw new BindingFileInvalidError(filePath, "Binding entry missing 'module_id'");
107
- }
108
- if (!('target' in entryObj)) {
109
- throw new BindingFileInvalidError(filePath, "Binding entry missing 'target'");
110
- }
111
- const fm = await this._createModuleFromBinding(entryObj, bindingFileDir);
112
- registry.register(entryObj['module_id'] as string, fm);
113
- results.push(fm);
114
- }
115
-
116
- return results;
117
- }
118
- ```
119
-
120
- ### 4. Implement _createModuleFromBinding()
121
-
122
- Private method that resolves the target callable, builds schemas (delegated to schema-modes task), and constructs a `FunctionModule`.
123
-
124
- ### 5. Verify tests pass
125
-
126
- Run `npx vitest run tests/test-bindings.test.ts`.
127
-
128
- ## Acceptance Criteria
129
-
130
- - [x] `BindingLoader` class is instantiable with no constructor arguments
131
- - [x] `resolveTarget()` resolves `modulePath:funcName` to the exported function
132
- - [x] `resolveTarget()` resolves `modulePath:ClassName.methodName` to a bound method
133
- - [x] `resolveTarget()` throws `BindingInvalidTargetError` when target string has no colon
134
- - [x] `resolveTarget()` throws `BindingModuleNotFoundError` when module cannot be imported
135
- - [x] `resolveTarget()` throws `BindingCallableNotFoundError` when export is not found
136
- - [x] `resolveTarget()` throws `BindingNotCallableError` when export is not a function
137
- - [x] `loadBindings()` reads YAML, creates FunctionModules, and registers them
138
- - [x] `loadBindings()` throws `BindingFileInvalidError` for missing files, invalid YAML, missing keys
139
- - [x] Each binding entry requires `module_id` and `target` keys
140
-
141
- ## Dependencies
142
-
143
- - `explicit-schemas` -- Requires understanding of explicit schema passing for FunctionModule construction
144
- - `schema-system` (external) -- Consumes `jsonSchemaToTypeBox()` for JSON Schema conversion
145
-
146
- ## Estimated Time
147
-
148
- 4 hours
@@ -1,85 +0,0 @@
1
- # Task: Explicit TypeBox Schema Passing
2
-
3
- ## Goal
4
-
5
- Establish and enforce the pattern of explicit TypeBox schema passing for all module definitions. In Python's apcore, `@module` can introspect function signatures and type annotations at runtime to auto-generate schemas. TypeScript erases types at compile time, making this impossible. This task documents and validates the explicit schema requirement: every `FunctionModule` and `module()` call must receive `inputSchema` and `outputSchema` as TypeBox `TSchema` objects.
6
-
7
- ## Files Involved
8
-
9
- - `src/decorator.ts` -- `FunctionModule` constructor requiring `inputSchema`/`outputSchema` as `TSchema`
10
- - `tests/test-decorator.test.ts` -- Tests validating schema presence and TypeBox compatibility
11
-
12
- ## Steps
13
-
14
- ### 1. Write failing tests (TDD)
15
-
16
- Create tests for:
17
- - **FunctionModule requires inputSchema and outputSchema**: Constructing without schemas causes TypeScript compilation error (structural test via type assertions)
18
- - **Schemas are stored as readonly TSchema**: `fm.inputSchema` and `fm.outputSchema` are accessible and match the provided TypeBox schemas
19
- - **Complex schemas work**: Nested `Type.Object()` with optional fields, `Type.Array()`, `Type.Union()` all accepted as valid schemas
20
- - **Schema is not validated at construction**: FunctionModule stores schemas but does not validate inputs against them (validation is the executor's responsibility)
21
-
22
- ### 2. Validate TypeBox schema integration
23
-
24
- ```typescript
25
- import { Type, type TSchema } from '@sinclair/typebox';
26
-
27
- // Simple schemas
28
- const inputSchema = Type.Object({ name: Type.String() });
29
- const outputSchema = Type.Object({ greeting: Type.String() });
30
-
31
- // Complex schemas
32
- const complexInput = Type.Object({
33
- query: Type.String(),
34
- options: Type.Optional(Type.Object({
35
- limit: Type.Number(),
36
- offset: Type.Number(),
37
- })),
38
- tags: Type.Array(Type.String()),
39
- });
40
-
41
- const complexOutput = Type.Object({
42
- results: Type.Array(Type.Object({
43
- id: Type.String(),
44
- score: Type.Number(),
45
- })),
46
- total: Type.Integer(),
47
- });
48
-
49
- // Both simple and complex schemas are accepted by FunctionModule
50
- const fm = new FunctionModule({
51
- execute: (inputs) => ({ results: [], total: 0 }),
52
- moduleId: 'search.query',
53
- inputSchema: complexInput,
54
- outputSchema: complexOutput,
55
- });
56
- ```
57
-
58
- ### 3. Document the no-auto-schema rationale
59
-
60
- The TypeScript implementation intentionally omits the Python `auto_schema` mode because:
61
- - TypeScript types are erased at compile time; `typeof` at runtime only yields `"object"`, `"string"`, etc.
62
- - There is no equivalent of Python's `inspect.signature()` or `typing.get_type_hints()` for TypeScript
63
- - TypeBox provides a runtime-accessible schema representation that doubles as both a TypeScript type (via `Static<T>`) and a JSON Schema
64
- - Explicit schemas are self-documenting and enable IDE autocompletion via `Static<typeof inputSchema>`
65
-
66
- ### 4. Verify tests pass
67
-
68
- Run `npx vitest run tests/test-decorator.test.ts` and confirm schema-related tests pass.
69
-
70
- ## Acceptance Criteria
71
-
72
- - [x] `FunctionModule` constructor requires `inputSchema: TSchema` and `outputSchema: TSchema`
73
- - [x] TypeBox schemas of any complexity are accepted (`Type.Object`, `Type.Array`, `Type.Union`, `Type.Optional`, etc.)
74
- - [x] `fm.inputSchema` and `fm.outputSchema` are accessible as `readonly TSchema`
75
- - [x] No auto-schema mode exists (verified by absence of introspection code)
76
- - [x] `module()` factory also requires explicit `inputSchema` and `outputSchema`
77
- - [x] Complex nested schemas round-trip correctly through FunctionModule construction
78
-
79
- ## Dependencies
80
-
81
- - `function-module` -- Requires `FunctionModule` class with `inputSchema`/`outputSchema` fields
82
-
83
- ## Estimated Time
84
-
85
- 1 hour
@@ -1,127 +0,0 @@
1
- # Task: FunctionModule Class with Execute, Schemas, and normalizeResult()
2
-
3
- ## Goal
4
-
5
- Implement the `FunctionModule` class that wraps an execute function with explicit TypeBox `inputSchema`/`outputSchema`, metadata properties (description, documentation, tags, version, annotations, metadata, examples), and a `normalizeResult()` utility for standardizing module return values. Also implement `makeAutoId()` for generating valid module IDs from arbitrary strings.
6
-
7
- ## Files Involved
8
-
9
- - `src/decorator.ts` -- `FunctionModule` class, `normalizeResult()`, `makeAutoId()`
10
- - `tests/test-decorator.test.ts` -- Unit tests for FunctionModule, normalizeResult(), makeAutoId()
11
-
12
- ## Steps
13
-
14
- ### 1. Write failing tests (TDD)
15
-
16
- Create tests for:
17
- - **FunctionModule wraps execute**: Construct with execute function, moduleId, inputSchema, outputSchema; call `execute()` and verify result
18
- - **FunctionModule exposes properties**: Verify moduleId, description, documentation, tags, version are accessible
19
- - **FunctionModule defaults**: Verify sensible defaults (description="Module {id}", documentation=null, tags=null, version="1.0.0", annotations=null, metadata=null, examples=null)
20
- - **FunctionModule normalizes null return**: Execute function returning null produces `{}`
21
- - **normalizeResult(null)**: Returns `{}`
22
- - **normalizeResult(undefined)**: Returns `{}`
23
- - **normalizeResult(Record)**: Passes through unchanged
24
- - **normalizeResult(string)**: Returns `{ result: "hello" }`
25
- - **normalizeResult(number)**: Returns `{ result: 42 }`
26
- - **normalizeResult(boolean)**: Returns `{ result: true }`
27
- - **normalizeResult(array)**: Returns `{ result: [1, 2, 3] }`
28
- - **makeAutoId lowercases**: `makeAutoId('Hello World')` -> `'hello_world'`
29
- - **makeAutoId preserves dots**: `makeAutoId('my.module.name')` -> `'my.module.name'`
30
- - **makeAutoId prefixes digit-leading segments**: `makeAutoId('2fast.4you')` -> `'_2fast._4you'`
31
- - **makeAutoId no-op on valid IDs**: `makeAutoId('valid_id')` -> `'valid_id'`
32
-
33
- ### 2. Implement normalizeResult()
34
-
35
- ```typescript
36
- export function normalizeResult(result: unknown): Record<string, unknown> {
37
- if (result === null || result === undefined) return {};
38
- if (typeof result === 'object' && !Array.isArray(result)) return result as Record<string, unknown>;
39
- return { result };
40
- }
41
- ```
42
-
43
- ### 3. Implement makeAutoId()
44
-
45
- ```typescript
46
- export function makeAutoId(name: string): string {
47
- let raw = name.toLowerCase();
48
- raw = raw.replace(/[^a-z0-9_.]/g, '_');
49
- const segments = raw.split('.');
50
- return segments
51
- .map((s) => (s && s[0] >= '0' && s[0] <= '9' ? '_' + s : s))
52
- .join('.');
53
- }
54
- ```
55
-
56
- ### 4. Implement FunctionModule class
57
-
58
- ```typescript
59
- export class FunctionModule {
60
- readonly moduleId: string;
61
- readonly inputSchema: TSchema;
62
- readonly outputSchema: TSchema;
63
- readonly description: string;
64
- readonly documentation: string | null;
65
- readonly tags: string[] | null;
66
- readonly version: string;
67
- readonly annotations: ModuleAnnotations | null;
68
- readonly metadata: Record<string, unknown> | null;
69
- readonly examples: ModuleExample[] | null;
70
-
71
- private _executeFn: (inputs: Record<string, unknown>, context: Context) =>
72
- Promise<Record<string, unknown>> | Record<string, unknown>;
73
-
74
- constructor(options: {
75
- execute: (inputs: Record<string, unknown>, context: Context) =>
76
- Promise<Record<string, unknown>> | Record<string, unknown>;
77
- moduleId: string;
78
- inputSchema: TSchema;
79
- outputSchema: TSchema;
80
- description?: string;
81
- documentation?: string | null;
82
- tags?: string[] | null;
83
- version?: string;
84
- annotations?: ModuleAnnotations | null;
85
- metadata?: Record<string, unknown> | null;
86
- examples?: ModuleExample[] | null;
87
- }) {
88
- this.moduleId = options.moduleId;
89
- this.inputSchema = options.inputSchema;
90
- this.outputSchema = options.outputSchema;
91
- this.description = options.description ?? `Module ${options.moduleId}`;
92
- this.documentation = options.documentation ?? null;
93
- this.tags = options.tags ?? null;
94
- this.version = options.version ?? '1.0.0';
95
- this.annotations = options.annotations ?? null;
96
- this.metadata = options.metadata ?? null;
97
- this.examples = options.examples ?? null;
98
- this._executeFn = options.execute;
99
- }
100
-
101
- async execute(inputs: Record<string, unknown>, context: Context): Promise<Record<string, unknown>> {
102
- const result = await this._executeFn(inputs, context);
103
- return normalizeResult(result);
104
- }
105
- }
106
- ```
107
-
108
- ### 5. Verify tests pass
109
-
110
- Run `npx vitest run tests/test-decorator.test.ts` and confirm all FunctionModule, normalizeResult, and makeAutoId tests pass.
111
-
112
- ## Acceptance Criteria
113
-
114
- - [x] `FunctionModule` constructor accepts options object with execute, moduleId, inputSchema, outputSchema, and optional metadata
115
- - [x] `FunctionModule.execute()` calls wrapped function and passes result through `normalizeResult()`
116
- - [x] Default description is `"Module {moduleId}"`, version is `"1.0.0"`, others default to `null`
117
- - [x] `normalizeResult()` correctly handles null, undefined, Record, string, number, boolean, and array
118
- - [x] `makeAutoId()` lowercases, replaces invalid characters, preserves dots, prefixes digit-leading segments
119
- - [x] All fields are typed with `readonly` where appropriate
120
-
121
- ## Dependencies
122
-
123
- None -- this is the foundational data structure for the decorator-bindings module.
124
-
125
- ## Estimated Time
126
-
127
- 2 hours
@@ -1,89 +0,0 @@
1
- # Task: module() Factory Function with Options Object Pattern
2
-
3
- ## Goal
4
-
5
- Implement the `module()` factory function that creates a `FunctionModule` from an options object. This is the primary ergonomic API for defining apcore modules in TypeScript. Unlike Python's `@module` decorator, this is a plain factory function that returns a `FunctionModule` instance. It supports optional auto-registration with a `Registry` and auto-generates a module ID via `makeAutoId('anonymous')` when no `id` is provided.
6
-
7
- ## Files Involved
8
-
9
- - `src/decorator.ts` -- `module()` factory function
10
- - `tests/test-decorator.test.ts` -- Unit tests for module() factory
11
-
12
- ## Steps
13
-
14
- ### 1. Write failing tests (TDD)
15
-
16
- Create tests for:
17
- - **Creates FunctionModule with correct properties**: `module({ id: 'factory.test', inputSchema, outputSchema, execute })` returns `FunctionModule` with correct moduleId and description
18
- - **Generates auto ID when not provided**: `module({ inputSchema, outputSchema, execute })` creates module with moduleId `'anonymous'`
19
- - **Passes through optional fields**: documentation, tags, version, metadata are forwarded to FunctionModule
20
- - **Auto-registers with registry**: When `registry` option is provided, the module is registered via `registry.register()`
21
-
22
- ### 2. Implement module() factory
23
-
24
- ```typescript
25
- export function module(options: {
26
- id?: string;
27
- inputSchema: TSchema;
28
- outputSchema: TSchema;
29
- description?: string;
30
- documentation?: string | null;
31
- annotations?: ModuleAnnotations | null;
32
- tags?: string[] | null;
33
- version?: string;
34
- metadata?: Record<string, unknown> | null;
35
- examples?: ModuleExample[] | null;
36
- execute: (inputs: Record<string, unknown>, context: Context) =>
37
- Promise<Record<string, unknown>> | Record<string, unknown>;
38
- registry?: { register(moduleId: string, module: unknown): void } | null;
39
- }): FunctionModule {
40
- const moduleId = options.id ?? makeAutoId('anonymous');
41
-
42
- const fm = new FunctionModule({
43
- execute: options.execute,
44
- moduleId,
45
- inputSchema: options.inputSchema,
46
- outputSchema: options.outputSchema,
47
- description: options.description,
48
- documentation: options.documentation,
49
- tags: options.tags,
50
- version: options.version,
51
- annotations: options.annotations,
52
- metadata: options.metadata,
53
- examples: options.examples,
54
- });
55
-
56
- if (options.registry) {
57
- options.registry.register(fm.moduleId, fm);
58
- }
59
-
60
- return fm;
61
- }
62
- ```
63
-
64
- Key design decisions:
65
- - The `registry` option uses a structural type `{ register(moduleId: string, module: unknown): void }` rather than importing the `Registry` class, enabling loose coupling and easier testing
66
- - When `id` is omitted, `makeAutoId('anonymous')` produces `'anonymous'` (a valid ID)
67
- - All optional metadata fields are forwarded to `FunctionModule` without transformation
68
-
69
- ### 3. Verify tests pass
70
-
71
- Run `npx vitest run tests/test-decorator.test.ts` and confirm all module() factory tests pass.
72
-
73
- ## Acceptance Criteria
74
-
75
- - [x] `module()` returns a `FunctionModule` instance
76
- - [x] `module()` forwards all options to `FunctionModule` constructor
77
- - [x] `module({ id: 'x', ... })` creates module with `moduleId === 'x'`
78
- - [x] `module({ ... })` without `id` creates module with `moduleId === 'anonymous'`
79
- - [x] `module({ registry, ... })` auto-registers the module with the provided registry
80
- - [x] `module({ registry: null, ... })` does not attempt registration
81
- - [x] `registry` option uses structural typing for loose coupling
82
-
83
- ## Dependencies
84
-
85
- - `function-module` -- Requires `FunctionModule` class and `makeAutoId()`
86
-
87
- ## Estimated Time
88
-
89
- 2 hours