apcore-js 0.1.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/.claude/settings.local.json +11 -0
- package/.gitmessage +60 -0
- package/.pre-commit-config.yaml +28 -0
- package/CHANGELOG.md +47 -0
- package/CLAUDE.md +68 -0
- package/README.md +131 -0
- package/apcore-logo.svg +79 -0
- package/package.json +37 -0
- package/planning/acl-system/overview.md +54 -0
- package/planning/acl-system/plan.md +92 -0
- package/planning/acl-system/state.json +76 -0
- package/planning/acl-system/tasks/acl-core.md +226 -0
- package/planning/acl-system/tasks/acl-rule.md +92 -0
- package/planning/acl-system/tasks/conditional-rules.md +259 -0
- package/planning/acl-system/tasks/pattern-matching.md +152 -0
- package/planning/acl-system/tasks/yaml-loading.md +271 -0
- package/planning/core-executor/overview.md +53 -0
- package/planning/core-executor/plan.md +88 -0
- package/planning/core-executor/state.json +76 -0
- package/planning/core-executor/tasks/async-support.md +106 -0
- package/planning/core-executor/tasks/execution-pipeline.md +113 -0
- package/planning/core-executor/tasks/redaction.md +85 -0
- package/planning/core-executor/tasks/safety-checks.md +65 -0
- package/planning/core-executor/tasks/setup.md +75 -0
- package/planning/decorator-bindings/overview.md +62 -0
- package/planning/decorator-bindings/plan.md +104 -0
- package/planning/decorator-bindings/state.json +87 -0
- package/planning/decorator-bindings/tasks/binding-directory.md +79 -0
- package/planning/decorator-bindings/tasks/binding-loader.md +148 -0
- package/planning/decorator-bindings/tasks/explicit-schemas.md +85 -0
- package/planning/decorator-bindings/tasks/function-module.md +127 -0
- package/planning/decorator-bindings/tasks/module-factory.md +89 -0
- package/planning/decorator-bindings/tasks/schema-modes.md +142 -0
- package/planning/middleware-system/overview.md +48 -0
- package/planning/middleware-system/plan.md +102 -0
- package/planning/middleware-system/state.json +65 -0
- package/planning/middleware-system/tasks/adapters.md +170 -0
- package/planning/middleware-system/tasks/base.md +115 -0
- package/planning/middleware-system/tasks/logging-middleware.md +304 -0
- package/planning/middleware-system/tasks/manager.md +313 -0
- package/planning/observability/overview.md +53 -0
- package/planning/observability/plan.md +119 -0
- package/planning/observability/state.json +98 -0
- package/planning/observability/tasks/context-logger.md +201 -0
- package/planning/observability/tasks/exporters.md +121 -0
- package/planning/observability/tasks/metrics-collector.md +162 -0
- package/planning/observability/tasks/metrics-middleware.md +141 -0
- package/planning/observability/tasks/obs-logging-middleware.md +179 -0
- package/planning/observability/tasks/span-model.md +120 -0
- package/planning/observability/tasks/tracing-middleware.md +179 -0
- package/planning/overview.md +81 -0
- package/planning/registry-system/overview.md +57 -0
- package/planning/registry-system/plan.md +114 -0
- package/planning/registry-system/state.json +109 -0
- package/planning/registry-system/tasks/dependencies.md +157 -0
- package/planning/registry-system/tasks/entry-point.md +148 -0
- package/planning/registry-system/tasks/metadata.md +198 -0
- package/planning/registry-system/tasks/registry-core.md +323 -0
- package/planning/registry-system/tasks/scanner.md +172 -0
- package/planning/registry-system/tasks/schema-export.md +261 -0
- package/planning/registry-system/tasks/types.md +124 -0
- package/planning/registry-system/tasks/validation.md +177 -0
- package/planning/schema-system/overview.md +56 -0
- package/planning/schema-system/plan.md +121 -0
- package/planning/schema-system/state.json +98 -0
- package/planning/schema-system/tasks/exporter.md +153 -0
- package/planning/schema-system/tasks/loader.md +106 -0
- package/planning/schema-system/tasks/ref-resolver.md +133 -0
- package/planning/schema-system/tasks/strict-mode.md +140 -0
- package/planning/schema-system/tasks/typebox-generation.md +133 -0
- package/planning/schema-system/tasks/types-and-annotations.md +160 -0
- package/planning/schema-system/tasks/validator.md +149 -0
- package/src/acl.ts +188 -0
- package/src/bindings.ts +208 -0
- package/src/config.ts +24 -0
- package/src/context.ts +75 -0
- package/src/decorator.ts +110 -0
- package/src/errors.ts +369 -0
- package/src/executor.ts +348 -0
- package/src/index.ts +81 -0
- package/src/middleware/adapters.ts +54 -0
- package/src/middleware/base.ts +33 -0
- package/src/middleware/index.ts +6 -0
- package/src/middleware/logging.ts +103 -0
- package/src/middleware/manager.ts +105 -0
- package/src/module.ts +41 -0
- package/src/observability/context-logger.ts +201 -0
- package/src/observability/index.ts +4 -0
- package/src/observability/metrics.ts +212 -0
- package/src/observability/tracing.ts +187 -0
- package/src/registry/dependencies.ts +99 -0
- package/src/registry/entry-point.ts +64 -0
- package/src/registry/index.ts +8 -0
- package/src/registry/metadata.ts +111 -0
- package/src/registry/registry.ts +314 -0
- package/src/registry/scanner.ts +150 -0
- package/src/registry/schema-export.ts +177 -0
- package/src/registry/types.ts +32 -0
- package/src/registry/validation.ts +38 -0
- package/src/schema/annotations.ts +67 -0
- package/src/schema/exporter.ts +93 -0
- package/src/schema/index.ts +14 -0
- package/src/schema/loader.ts +270 -0
- package/src/schema/ref-resolver.ts +235 -0
- package/src/schema/strict.ts +128 -0
- package/src/schema/types.ts +73 -0
- package/src/schema/validator.ts +82 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/pattern.ts +30 -0
- package/tests/helpers.ts +30 -0
- package/tests/integration/test-acl-safety.test.ts +268 -0
- package/tests/integration/test-binding-executor.test.ts +194 -0
- package/tests/integration/test-e2e-flow.test.ts +117 -0
- package/tests/integration/test-error-propagation.test.ts +259 -0
- package/tests/integration/test-middleware-chain.test.ts +120 -0
- package/tests/integration/test-observability-integration.test.ts +438 -0
- package/tests/observability/test-context-logger.test.ts +123 -0
- package/tests/observability/test-metrics.test.ts +89 -0
- package/tests/observability/test-tracing.test.ts +131 -0
- package/tests/registry/test-dependencies.test.ts +70 -0
- package/tests/registry/test-entry-point.test.ts +133 -0
- package/tests/registry/test-metadata.test.ts +265 -0
- package/tests/registry/test-registry.test.ts +140 -0
- package/tests/registry/test-scanner.test.ts +257 -0
- package/tests/registry/test-schema-export.test.ts +224 -0
- package/tests/registry/test-validation.test.ts +75 -0
- package/tests/schema/test-loader.test.ts +97 -0
- package/tests/schema/test-ref-resolver.test.ts +105 -0
- package/tests/schema/test-strict.test.ts +139 -0
- package/tests/schema/test-validator.test.ts +64 -0
- package/tests/test-acl.test.ts +206 -0
- package/tests/test-bindings.test.ts +227 -0
- package/tests/test-config.test.ts +76 -0
- package/tests/test-context.test.ts +151 -0
- package/tests/test-decorator.test.ts +173 -0
- package/tests/test-errors.test.ts +204 -0
- package/tests/test-executor.test.ts +252 -0
- package/tests/test-middleware-manager.test.ts +185 -0
- package/tests/test-middleware.test.ts +86 -0
- package/tsconfig.build.json +8 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +18 -0
package/.gitmessage
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# <type>(<scope>): <subject>
|
|
2
|
+
#
|
|
3
|
+
# <body>
|
|
4
|
+
#
|
|
5
|
+
# <footer>
|
|
6
|
+
|
|
7
|
+
# Type must be one of the following:
|
|
8
|
+
# feat: A new feature
|
|
9
|
+
# fix: A bug fix
|
|
10
|
+
# docs: Documentation only changes
|
|
11
|
+
# style: Changes that do not affect the meaning of the code
|
|
12
|
+
# refactor: A code change that neither fixes a bug nor adds a feature
|
|
13
|
+
# perf: A code change that improves performance
|
|
14
|
+
# test: Adding missing tests or correcting existing tests
|
|
15
|
+
# chore: Changes to the build process or auxiliary tools
|
|
16
|
+
# ci: Changes to CI configuration files and scripts
|
|
17
|
+
|
|
18
|
+
# Scope is optional and can be anything specifying the place of the commit change.
|
|
19
|
+
# Examples: api, core, storage, cli, stdio, etc.
|
|
20
|
+
|
|
21
|
+
# Subject should be:
|
|
22
|
+
# - Use imperative, present tense: "change" not "changed" nor "changes"
|
|
23
|
+
# - Don't capitalize first letter
|
|
24
|
+
# - No dot (.) at the end
|
|
25
|
+
# - Maximum 72 characters
|
|
26
|
+
|
|
27
|
+
# Body should include:
|
|
28
|
+
# - Motivation for the change and contrast with previous behavior
|
|
29
|
+
# - What changed and why
|
|
30
|
+
# - Any breaking changes
|
|
31
|
+
|
|
32
|
+
# Footer should contain:
|
|
33
|
+
# - Breaking changes (start with BREAKING CHANGE:)
|
|
34
|
+
# - Issue references (Closes #123, Fixes #456)
|
|
35
|
+
|
|
36
|
+
# Examples:
|
|
37
|
+
# feat(stdio): add stdio executor for process execution
|
|
38
|
+
#
|
|
39
|
+
# Implement a new stdio executor that allows executing system commands
|
|
40
|
+
# and processes via stdin/stdout communication, similar to MCP stdio
|
|
41
|
+
# transport mode. This enables flexible task execution through shell
|
|
42
|
+
# commands and Python scripts.
|
|
43
|
+
#
|
|
44
|
+
# - Add StdioExecutor class with command execution support
|
|
45
|
+
# - Add system resource monitoring (CPU, memory, disk)
|
|
46
|
+
# - Support async process communication
|
|
47
|
+
# - Add comprehensive error handling and logging
|
|
48
|
+
#
|
|
49
|
+
# Closes #123
|
|
50
|
+
|
|
51
|
+
# refactor(core): extract shared types to core.types module
|
|
52
|
+
#
|
|
53
|
+
# Move common type definitions from various modules to a centralized
|
|
54
|
+
# core.types module to avoid circular dependencies and improve code
|
|
55
|
+
# organization.
|
|
56
|
+
#
|
|
57
|
+
# - Add TaskPreHook and TaskPostHook type aliases
|
|
58
|
+
# - Move TaskStatus enum to core.types
|
|
59
|
+
# - Update imports across affected modules
|
|
60
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
|
|
3
|
+
# apdev-js hooks
|
|
4
|
+
- repo: local
|
|
5
|
+
hooks:
|
|
6
|
+
- id: check-chars
|
|
7
|
+
name: apdev-js check-chars
|
|
8
|
+
entry: apdev-js check-chars
|
|
9
|
+
language: system
|
|
10
|
+
types_or: [text, ts, javascript]
|
|
11
|
+
|
|
12
|
+
- id: check-imports
|
|
13
|
+
name: apdev-js check-imports
|
|
14
|
+
entry: apdev-js check-imports
|
|
15
|
+
language: system
|
|
16
|
+
pass_filenames: false
|
|
17
|
+
always_run: true
|
|
18
|
+
|
|
19
|
+
# TypeScript type checking
|
|
20
|
+
- repo: local
|
|
21
|
+
hooks:
|
|
22
|
+
- id: typecheck
|
|
23
|
+
name: tsc --noEmit
|
|
24
|
+
entry: npx tsc --noEmit
|
|
25
|
+
language: system
|
|
26
|
+
pass_filenames: false
|
|
27
|
+
always_run: true
|
|
28
|
+
files: \.(ts|tsx)$
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-02-16
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Core executor** — 10-step async execution pipeline with timeout support via `Promise.race`
|
|
13
|
+
- **Context system** — Execution context with trace IDs, call chains, identity, and redacted inputs
|
|
14
|
+
- **Config** — Dot-path configuration accessor
|
|
15
|
+
- **Registry system**
|
|
16
|
+
- File-based module discovery (`scanExtensions`, `scanMultiRoot`)
|
|
17
|
+
- Dynamic entry point resolution with duck-type validation
|
|
18
|
+
- YAML metadata loading and merging (code values + YAML overrides)
|
|
19
|
+
- Dependency parsing with topological sort (Kahn's algorithm) and cycle detection
|
|
20
|
+
- ID map support for custom module IDs
|
|
21
|
+
- Schema export in JSON/YAML with strict and compact modes
|
|
22
|
+
- **FunctionModule** — Schema-driven module wrapper with TypeBox schemas
|
|
23
|
+
- **Binding loader** — YAML-based module registration with three schema modes (inline, external ref, permissive fallback)
|
|
24
|
+
- **ACL (Access Control List)**
|
|
25
|
+
- Pattern-based rules with glob matching
|
|
26
|
+
- Identity type and role-based conditions
|
|
27
|
+
- Call depth conditions
|
|
28
|
+
- Dynamic rule management (`addRule`, `removeRule`, `reload`)
|
|
29
|
+
- YAML configuration loading
|
|
30
|
+
- **Middleware system**
|
|
31
|
+
- Onion-model execution (before forward, after reverse)
|
|
32
|
+
- Error recovery via `onError` hooks
|
|
33
|
+
- `BeforeMiddleware` and `AfterMiddleware` adapters
|
|
34
|
+
- `LoggingMiddleware` for structured execution logging
|
|
35
|
+
- **Observability**
|
|
36
|
+
- **Tracing** — Span creation, `InMemoryExporter`, `StdoutExporter`, `TracingMiddleware` with sampling strategies (full, off, proportional, error_first)
|
|
37
|
+
- **Metrics** — `MetricsCollector` with counters, histograms, Prometheus text format export, `MetricsMiddleware`
|
|
38
|
+
- **Logging** — `ContextLogger` with JSON/text formats, level filtering, `_secret_` field redaction, `ObsLoggingMiddleware`
|
|
39
|
+
- **Schema system**
|
|
40
|
+
- JSON Schema to TypeBox conversion
|
|
41
|
+
- `$ref` resolution
|
|
42
|
+
- Schema validation
|
|
43
|
+
- Strict transforms (`additionalProperties: false`)
|
|
44
|
+
- LLM description injection and extension stripping
|
|
45
|
+
- **Error hierarchy** — 20+ typed error classes with error codes, details, trace IDs, and timestamps
|
|
46
|
+
- **Pattern matching** — Glob-style pattern matching for ACL rules and module targeting
|
|
47
|
+
- **Comprehensive test suite** — 385 tests across 29 test files
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# High-Quality Code Specification – Simplicity, Readability, and Maintainability First
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
The core of `apcore` is **task orchestration and execution specifications**. It provides a unified task orchestration framework that supports execution of multiple task types.
|
|
5
|
+
|
|
6
|
+
## Core Principles
|
|
7
|
+
- Prioritize **simplicity, readability, and maintainability** above all.
|
|
8
|
+
- Avoid premature abstraction, optimization, or over-engineering.
|
|
9
|
+
- Code should be understandable in ≤10 seconds; favor straightforward over clever.
|
|
10
|
+
- Always follow: Understand → Plan → Implement minimally → Test/Validate → Commit.
|
|
11
|
+
|
|
12
|
+
## TypeScript Code Quality
|
|
13
|
+
|
|
14
|
+
### Readability
|
|
15
|
+
- Use precise, full-word names (standard abbreviations only when conventional).
|
|
16
|
+
- Functions ≤50 lines, single responsibility, verb-named.
|
|
17
|
+
- Avoid obscure tricks, excessive generics, or unnecessary abstraction layers.
|
|
18
|
+
- Break complex logic into small, well-named helpers.
|
|
19
|
+
|
|
20
|
+
### Types (Mandatory)
|
|
21
|
+
- Full type annotations on all public APIs and function signatures.
|
|
22
|
+
- Avoid `any` except for dynamic/external data; prefer `unknown` with narrowing.
|
|
23
|
+
- Prefer `interface` for object shapes, `type` for unions/intersections.
|
|
24
|
+
- Use `readonly` for immutable data structures.
|
|
25
|
+
|
|
26
|
+
### Design
|
|
27
|
+
- Favor functional style + plain objects; minimize class inheritance.
|
|
28
|
+
- Composition > inheritance; use `interface` only for true contracts.
|
|
29
|
+
- No circular imports.
|
|
30
|
+
- Dependency injection for config, logging, external services, etc.
|
|
31
|
+
|
|
32
|
+
### Errors & Resources
|
|
33
|
+
- Explicit error handling; no bare `catch {}` that swallows errors silently.
|
|
34
|
+
- Use `try/finally` or equivalent cleanup patterns for resources.
|
|
35
|
+
- Validate/sanitize all public inputs.
|
|
36
|
+
|
|
37
|
+
### Logging
|
|
38
|
+
- Use `console.warn` for warnings in library code.
|
|
39
|
+
- Prefix log messages with `[apcore:<subsystem>]` for traceability.
|
|
40
|
+
- No `console.log` in production code paths.
|
|
41
|
+
|
|
42
|
+
### Testing
|
|
43
|
+
- Unit tests in `tests/`, ≥90% coverage on core logic.
|
|
44
|
+
- Test files named: `test-<unit>.test.ts`.
|
|
45
|
+
- Test cases named descriptively: `it('throws X when Y', ...)`.
|
|
46
|
+
- Never change production code without updating tests.
|
|
47
|
+
- Use `vitest` as test runner.
|
|
48
|
+
|
|
49
|
+
### Build & Checks
|
|
50
|
+
- After changes, always run:
|
|
51
|
+
- `npx tsc --noEmit` (type checking)
|
|
52
|
+
- `npx vitest run` (tests)
|
|
53
|
+
- Zero errors before commit.
|
|
54
|
+
|
|
55
|
+
### Module System
|
|
56
|
+
- ESM only (`"type": "module"` in package.json).
|
|
57
|
+
- All imports must use `.js` extension (NodeNext resolution).
|
|
58
|
+
- Use `@sinclair/typebox` for runtime schema definitions.
|
|
59
|
+
|
|
60
|
+
### Security & Performance
|
|
61
|
+
- Never hardcode secrets; use env/config.
|
|
62
|
+
- Validate/sanitize inputs at system boundaries.
|
|
63
|
+
- Avoid unjustified quadratic+ complexity in hot paths.
|
|
64
|
+
|
|
65
|
+
## General Guidelines
|
|
66
|
+
- English ONLY for comments, JSDoc, logs, errors, commit messages.
|
|
67
|
+
- Fully understand surrounding code before changes.
|
|
68
|
+
- Do not generate unnecessary documentation, examples, or stubs unless explicitly requested.
|
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="./apcore-logo.svg" alt="apcore logo" width="200"/>
|
|
3
|
+
</div>
|
|
4
|
+
|
|
5
|
+
# apcore
|
|
6
|
+
|
|
7
|
+
**AI-Perceivable Core** — A schema-driven module development framework for TypeScript.
|
|
8
|
+
|
|
9
|
+
apcore provides a unified task orchestration framework with schema validation, access control, middleware pipelines, and observability built in.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Schema-driven modules** — Define input/output schemas with TypeBox for runtime validation
|
|
14
|
+
- **Executor pipeline** — 10-step execution pipeline: context → safety checks → lookup → ACL → validation → middleware before → execute → output validation → middleware after → return
|
|
15
|
+
- **Registry system** — File-based module discovery with metadata, dependencies, and topological ordering
|
|
16
|
+
- **Binding loader** — YAML-based module registration for no-code integration
|
|
17
|
+
- **Access control (ACL)** — Pattern-based rules with identity types, roles, and call-depth conditions
|
|
18
|
+
- **Middleware** — Onion-model middleware with before/after/onError hooks and error recovery
|
|
19
|
+
- **Observability** — Tracing (spans + exporters), metrics (counters + histograms + Prometheus export), structured logging with redaction
|
|
20
|
+
- **Schema export** — JSON/YAML schema export with strict and compact modes
|
|
21
|
+
|
|
22
|
+
## Requirements
|
|
23
|
+
|
|
24
|
+
- Node.js >= 18.0.0
|
|
25
|
+
- TypeScript >= 5.5
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install apcore-js
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { Type } from '@sinclair/typebox';
|
|
37
|
+
import { FunctionModule, Registry, Executor } from 'apcore-js';
|
|
38
|
+
|
|
39
|
+
// Define a module
|
|
40
|
+
const greet = new FunctionModule({
|
|
41
|
+
execute: (inputs) => ({ greeting: `Hello, ${inputs.name}!` }),
|
|
42
|
+
moduleId: 'example.greet',
|
|
43
|
+
inputSchema: Type.Object({ name: Type.String() }),
|
|
44
|
+
outputSchema: Type.Object({ greeting: Type.String() }),
|
|
45
|
+
description: 'Greet a user',
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Register and execute
|
|
49
|
+
const registry = new Registry();
|
|
50
|
+
registry.register('example.greet', greet);
|
|
51
|
+
|
|
52
|
+
const executor = new Executor({ registry });
|
|
53
|
+
const result = await executor.call('example.greet', { name: 'World' });
|
|
54
|
+
// => { greeting: 'Hello, World!' }
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Architecture
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
src/
|
|
61
|
+
index.ts # Public API exports
|
|
62
|
+
executor.ts # 10-step execution pipeline
|
|
63
|
+
context.ts # Execution context and identity
|
|
64
|
+
config.ts # Dot-path configuration accessor
|
|
65
|
+
acl.ts # Access control with pattern matching
|
|
66
|
+
decorator.ts # FunctionModule class and helpers
|
|
67
|
+
bindings.ts # YAML binding loader
|
|
68
|
+
errors.ts # Error hierarchy (20+ typed errors)
|
|
69
|
+
module.ts # Module types and annotations
|
|
70
|
+
middleware/
|
|
71
|
+
base.ts # Middleware base class
|
|
72
|
+
manager.ts # MiddlewareManager (onion model)
|
|
73
|
+
adapters.ts # BeforeMiddleware, AfterMiddleware adapters
|
|
74
|
+
logging.ts # LoggingMiddleware
|
|
75
|
+
registry/
|
|
76
|
+
registry.ts # Registry with discover() pipeline
|
|
77
|
+
scanner.ts # File-based module discovery
|
|
78
|
+
entry-point.ts # Dynamic import and entry point resolution
|
|
79
|
+
metadata.ts # YAML metadata and ID map loading
|
|
80
|
+
dependencies.ts # Topological sort with cycle detection
|
|
81
|
+
validation.ts # Module duck-type validation
|
|
82
|
+
schema-export.ts # Schema export (JSON/YAML, strict/compact)
|
|
83
|
+
types.ts # Registry type definitions
|
|
84
|
+
schema/
|
|
85
|
+
loader.ts # JSON Schema to TypeBox conversion
|
|
86
|
+
validator.ts # Schema validation
|
|
87
|
+
exporter.ts # Schema serialization
|
|
88
|
+
ref-resolver.ts # $ref resolution
|
|
89
|
+
strict.ts # Strict schema transforms
|
|
90
|
+
types.ts # Schema type definitions
|
|
91
|
+
observability/
|
|
92
|
+
tracing.ts # Span, SpanExporter, TracingMiddleware
|
|
93
|
+
metrics.ts # MetricsCollector, MetricsMiddleware
|
|
94
|
+
context-logger.ts # ContextLogger, ObsLoggingMiddleware
|
|
95
|
+
utils/
|
|
96
|
+
pattern.ts # Glob-style pattern matching
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Development
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# Install dependencies
|
|
103
|
+
npm install
|
|
104
|
+
|
|
105
|
+
# Type check
|
|
106
|
+
npm run typecheck
|
|
107
|
+
|
|
108
|
+
# Run tests
|
|
109
|
+
npm test
|
|
110
|
+
|
|
111
|
+
# Run tests in watch mode
|
|
112
|
+
npm run test:watch
|
|
113
|
+
|
|
114
|
+
# Build
|
|
115
|
+
npm run build
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Testing
|
|
119
|
+
|
|
120
|
+
- Core executor pipeline
|
|
121
|
+
- Schema validation (strict mode, type coercion)
|
|
122
|
+
- Middleware chain (ordering, transforms, error recovery)
|
|
123
|
+
- ACL enforcement (patterns, conditions, identity types)
|
|
124
|
+
- Registry system (scanner, metadata, entry points, dependencies)
|
|
125
|
+
- Binding loader (YAML loading, target resolution, schema modes)
|
|
126
|
+
- Observability (tracing, metrics, structured logging)
|
|
127
|
+
- Integration tests (end-to-end flows, error propagation, safety checks)
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
MIT
|
package/apcore-logo.svg
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<svg width="512" height="512" viewBox="46 70 420 420" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<!-- apcore jellyfish logo - cartoon ocean mascot -->
|
|
3
|
+
|
|
4
|
+
<defs>
|
|
5
|
+
<!-- Bell radial gradient: bright center → deep indigo edge -->
|
|
6
|
+
<radialGradient id="bellGrad" cx="50%" cy="45%" r="50%">
|
|
7
|
+
<stop offset="0%" stop-color="#818CF8"/>
|
|
8
|
+
<stop offset="100%" stop-color="#4338CA"/>
|
|
9
|
+
</radialGradient>
|
|
10
|
+
|
|
11
|
+
<!-- Bioluminescence glow -->
|
|
12
|
+
<radialGradient id="glowGrad" cx="50%" cy="50%" r="50%">
|
|
13
|
+
<stop offset="0%" stop-color="#67E8F9" stop-opacity="0.4"/>
|
|
14
|
+
<stop offset="100%" stop-color="#67E8F9" stop-opacity="0"/>
|
|
15
|
+
</radialGradient>
|
|
16
|
+
|
|
17
|
+
<!-- Tentacle gradients (top solid → bottom fade) -->
|
|
18
|
+
<linearGradient id="tentGrad1" x1="0" y1="0" x2="0" y2="1">
|
|
19
|
+
<stop offset="0%" stop-color="#6366F1"/>
|
|
20
|
+
<stop offset="100%" stop-color="#4F46E5" stop-opacity="0"/>
|
|
21
|
+
</linearGradient>
|
|
22
|
+
<linearGradient id="tentGrad2" x1="0" y1="0" x2="0" y2="1">
|
|
23
|
+
<stop offset="0%" stop-color="#818CF8"/>
|
|
24
|
+
<stop offset="100%" stop-color="#6366F1" stop-opacity="0"/>
|
|
25
|
+
</linearGradient>
|
|
26
|
+
</defs>
|
|
27
|
+
|
|
28
|
+
<!-- Tentacles (behind bell) - 6 flowing tentacles -->
|
|
29
|
+
<!-- Outer left tentacle -->
|
|
30
|
+
<path d="M 175,300 C 160,350 145,390 155,430 Q 162,455 150,470"
|
|
31
|
+
stroke="url(#tentGrad1)" stroke-width="8" fill="none" stroke-linecap="round"/>
|
|
32
|
+
<!-- Inner left tentacle -->
|
|
33
|
+
<path d="M 210,310 C 200,360 188,400 195,445 Q 200,465 190,480"
|
|
34
|
+
stroke="url(#tentGrad2)" stroke-width="7" fill="none" stroke-linecap="round"/>
|
|
35
|
+
<!-- Center left tentacle -->
|
|
36
|
+
<path d="M 245,310 C 240,365 235,410 242,455 Q 248,475 240,490"
|
|
37
|
+
stroke="url(#tentGrad1)" stroke-width="6" fill="none" stroke-linecap="round"/>
|
|
38
|
+
<!-- Center right tentacle -->
|
|
39
|
+
<path d="M 267,310 C 272,365 277,410 270,455 Q 264,475 272,490"
|
|
40
|
+
stroke="url(#tentGrad1)" stroke-width="6" fill="none" stroke-linecap="round"/>
|
|
41
|
+
<!-- Inner right tentacle -->
|
|
42
|
+
<path d="M 302,310 C 312,360 324,400 317,445 Q 312,465 322,480"
|
|
43
|
+
stroke="url(#tentGrad2)" stroke-width="7" fill="none" stroke-linecap="round"/>
|
|
44
|
+
<!-- Outer right tentacle -->
|
|
45
|
+
<path d="M 337,300 C 352,350 367,390 357,430 Q 350,455 362,470"
|
|
46
|
+
stroke="url(#tentGrad1)" stroke-width="8" fill="none" stroke-linecap="round"/>
|
|
47
|
+
|
|
48
|
+
<!-- Bell (dome body) -->
|
|
49
|
+
<path d="M 130,290 C 130,150 190,80 256,80 C 322,80 382,150 382,290 Q 382,310 256,310 Q 130,310 130,290 Z"
|
|
50
|
+
fill="url(#bellGrad)"/>
|
|
51
|
+
|
|
52
|
+
<!-- Bell rim / ruffle -->
|
|
53
|
+
<path d="M 130,290 Q 150,318 175,298 Q 200,318 225,298 Q 250,318 268,298 Q 290,318 312,298 Q 335,318 355,298 Q 375,318 382,290"
|
|
54
|
+
fill="#4338CA" opacity="0.6"/>
|
|
55
|
+
|
|
56
|
+
<!-- Bioluminescence glow overlay -->
|
|
57
|
+
<ellipse cx="256" cy="210" rx="100" ry="90" fill="url(#glowGrad)"/>
|
|
58
|
+
|
|
59
|
+
<!-- Bell highlight (specular) -->
|
|
60
|
+
<ellipse cx="230" cy="160" rx="55" ry="35" fill="#A5B4FC" opacity="0.3"/>
|
|
61
|
+
|
|
62
|
+
<!-- Eyes -->
|
|
63
|
+
<ellipse cx="222" cy="215" rx="22" ry="24" fill="white"/>
|
|
64
|
+
<ellipse cx="290" cy="215" rx="22" ry="24" fill="white"/>
|
|
65
|
+
<!-- Pupils -->
|
|
66
|
+
<circle cx="228" cy="220" r="13" fill="#1E1B4B"/>
|
|
67
|
+
<circle cx="296" cy="220" r="13" fill="#1E1B4B"/>
|
|
68
|
+
<!-- Eye highlights -->
|
|
69
|
+
<circle cx="233" cy="212" r="6" fill="white"/>
|
|
70
|
+
<circle cx="301" cy="212" r="6" fill="white"/>
|
|
71
|
+
|
|
72
|
+
<!-- Smile -->
|
|
73
|
+
<path d="M 240,252 Q 256,268 272,252"
|
|
74
|
+
stroke="#312E81" stroke-width="3.5" fill="none" stroke-linecap="round"/>
|
|
75
|
+
|
|
76
|
+
<!-- Cheek blush -->
|
|
77
|
+
<ellipse cx="205" cy="242" rx="14" ry="9" fill="#C4B5FD" opacity="0.45"/>
|
|
78
|
+
<ellipse cx="307" cy="242" rx="14" ry="9" fill="#C4B5FD" opacity="0.45"/>
|
|
79
|
+
</svg>
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "apcore-js",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AI-Perceivable Core — schema-driven module development framework",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc -p tsconfig.build.json",
|
|
16
|
+
"typecheck": "tsc --noEmit",
|
|
17
|
+
"test": "vitest run",
|
|
18
|
+
"test:watch": "vitest",
|
|
19
|
+
"test:coverage": "vitest run --coverage"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18.0.0"
|
|
23
|
+
},
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@sinclair/typebox": "^0.34.0",
|
|
27
|
+
"js-yaml": "^4.1.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"typescript": "^5.5.0",
|
|
31
|
+
"@types/node": "^20.0.0",
|
|
32
|
+
"@types/js-yaml": "^4.0.9",
|
|
33
|
+
"apdev-js": "^0.1.1",
|
|
34
|
+
"vitest": "^2.0.0",
|
|
35
|
+
"@vitest/coverage-v8": "^2.0.0"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Feature: ACL System
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The ACL (Access Control List) system provides pattern-based access control for apcore module calls. It evaluates caller-to-target permissions using a first-match-wins strategy over an ordered rule list. Each rule specifies caller patterns, target patterns, an effect (allow/deny), and optional conditions (identity types, roles, call depth). The `ACL` class supports runtime rule mutation (`addRule`, `removeRule`), YAML-based configuration loading via a static `load()` factory, and live `reload()` for configuration hot-swapping. Pattern matching uses a wildcard algorithm (Algorithm A08) that supports `*` globs in module IDs.
|
|
6
|
+
|
|
7
|
+
## Scope
|
|
8
|
+
|
|
9
|
+
### Included
|
|
10
|
+
|
|
11
|
+
- `ACLRule` interface defining the structure of access control rules (callers, targets, effect, description, conditions)
|
|
12
|
+
- `ACL` class with `check()` for first-match-wins evaluation, `addRule()` for prepending rules, `removeRule()` for removal by caller/target match via `JSON.stringify` comparison
|
|
13
|
+
- Static `ACL.load()` factory for loading rules from YAML configuration files with strict validation
|
|
14
|
+
- `reload()` method for hot-reloading YAML-based configuration without reconstructing the ACL instance
|
|
15
|
+
- `matchPattern()` wildcard utility (Algorithm A08) for glob-style module ID matching
|
|
16
|
+
- Special caller tokens: `@external` (null caller substitution), `@system` (identity type check)
|
|
17
|
+
- Conditional rule evaluation: `identity_types`, `roles`, `max_call_depth` with AND logic
|
|
18
|
+
- Error types: `ACLDeniedError` (thrown by executor on denial), `ACLRuleError` (invalid rules/config), `ConfigNotFoundError` (missing YAML)
|
|
19
|
+
|
|
20
|
+
### Excluded
|
|
21
|
+
|
|
22
|
+
- Executor integration (the executor consumes `ACL.check()` as a dependency)
|
|
23
|
+
- Thread locking or concurrency guards (Node.js single-threaded event loop eliminates the need)
|
|
24
|
+
- Debug logging (identified gap vs. Python implementation; not included in current scope)
|
|
25
|
+
- Role or identity management (consumed from `Context.identity`)
|
|
26
|
+
|
|
27
|
+
## Technology Stack
|
|
28
|
+
|
|
29
|
+
- **TypeScript 5.5+** with strict mode
|
|
30
|
+
- **js-yaml** for YAML configuration parsing
|
|
31
|
+
- **Node.js >= 18.0.0** with ES Module support (`node:fs` for file I/O)
|
|
32
|
+
- **vitest** for unit and integration testing
|
|
33
|
+
|
|
34
|
+
## Task Execution Order
|
|
35
|
+
|
|
36
|
+
| # | Task File | Description | Status |
|
|
37
|
+
|---|-----------|-------------|--------|
|
|
38
|
+
| 1 | [acl-rule](./tasks/acl-rule.md) | ACLRule interface definition | completed |
|
|
39
|
+
| 2 | [acl-core](./tasks/acl-core.md) | ACL class with check(), addRule(), removeRule(), default effect | completed |
|
|
40
|
+
| 3 | [pattern-matching](./tasks/pattern-matching.md) | matchPattern() wildcard matching (Algorithm A08) | completed |
|
|
41
|
+
| 4 | [yaml-loading](./tasks/yaml-loading.md) | ACL.load() from YAML with strict validation, reload() support | completed |
|
|
42
|
+
| 5 | [conditional-rules](./tasks/conditional-rules.md) | _checkConditions() with identity_types, roles, max_call_depth | completed |
|
|
43
|
+
|
|
44
|
+
## Progress
|
|
45
|
+
|
|
46
|
+
| Total | Completed | In Progress | Pending |
|
|
47
|
+
|-------|-----------|-------------|---------|
|
|
48
|
+
| 5 | 5 | 0 | 0 |
|
|
49
|
+
|
|
50
|
+
## Reference Documents
|
|
51
|
+
|
|
52
|
+
- `src/acl.ts` -- ACL class and ACLRule interface (~188 lines)
|
|
53
|
+
- `src/utils/pattern.ts` -- matchPattern wildcard utility (~30 lines)
|
|
54
|
+
- `src/errors.ts` -- ACLDeniedError, ACLRuleError, ConfigNotFoundError
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Implementation Plan: ACL System
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Implement a pattern-based access control list that evaluates caller-to-target module permissions using first-match-wins semantics, supporting wildcard patterns, conditional rules, YAML-based configuration, and runtime rule mutation.
|
|
6
|
+
|
|
7
|
+
## Architecture Design
|
|
8
|
+
|
|
9
|
+
### Component Structure
|
|
10
|
+
|
|
11
|
+
- **ACL** (`acl.ts`, ~188 lines) -- Main access control class. Holds an ordered array of `ACLRule` objects and a `_defaultEffect` (default `'deny'`). Provides `check(callerId, targetId, context?)` for permission evaluation, `addRule(rule)` to prepend a rule, `removeRule(callers, targets)` to remove by JSON-stringified comparison, and `reload()` for hot-swapping YAML-loaded rules. Static `load(yamlPath)` factory parses and validates YAML configuration files.
|
|
12
|
+
|
|
13
|
+
- **ACLRule** (`acl.ts`) -- Interface defining a single access control rule: `callers` (string array of patterns), `targets` (string array of patterns), `effect` (`'allow'` or `'deny'`), `description` (string), and optional `conditions` record. Conditions are evaluated with AND logic across all present keys.
|
|
14
|
+
|
|
15
|
+
- **matchPattern** (`utils/pattern.ts`, ~30 lines) -- Standalone wildcard pattern matcher implementing Algorithm A08. Splits the pattern on `*` delimiters and verifies each segment appears in order within the module ID string. Handles edge cases: bare `*` matches everything, no `*` means exact match.
|
|
16
|
+
|
|
17
|
+
- **Error Types** (`errors.ts`) -- `ACLDeniedError` (raised by executor when `check()` returns `false`), `ACLRuleError` (invalid rule structure, bad YAML, missing keys), `ConfigNotFoundError` (YAML file does not exist). All extend `ModuleError` base class.
|
|
18
|
+
|
|
19
|
+
### Data Flow
|
|
20
|
+
|
|
21
|
+
The `check()` method evaluates permissions through this pipeline:
|
|
22
|
+
|
|
23
|
+
1. **Caller Normalization** -- `null` caller is replaced with `'@external'`
|
|
24
|
+
2. **Rule Snapshot** -- Shallow copy of `_rules` array (safe iteration)
|
|
25
|
+
3. **First-Match-Wins Scan** -- Iterate rules in order:
|
|
26
|
+
a. **Caller Pattern Match** -- `rule.callers.some(p => _matchPattern(p, caller, context))`
|
|
27
|
+
b. **Target Pattern Match** -- `rule.targets.some(p => _matchPattern(p, target, context))`
|
|
28
|
+
c. **Condition Evaluation** -- If `rule.conditions != null`, apply `_checkConditions()`
|
|
29
|
+
d. **Effect Return** -- If all checks pass, return `rule.effect === 'allow'`
|
|
30
|
+
4. **Default Effect** -- If no rule matches, return `_defaultEffect === 'allow'`
|
|
31
|
+
|
|
32
|
+
Pattern matching handles three special patterns:
|
|
33
|
+
- `'@external'` -- Exact match against the `@external` sentinel
|
|
34
|
+
- `'@system'` -- Checks `context.identity.type === 'system'`
|
|
35
|
+
- Wildcard patterns -- Delegated to `matchPattern()` from `utils/pattern.ts`
|
|
36
|
+
|
|
37
|
+
### Technical Choices and Rationale
|
|
38
|
+
|
|
39
|
+
- **First-match-wins over priority scores**: Simpler mental model; rule ordering in YAML is explicit and deterministic. No conflict resolution needed.
|
|
40
|
+
- **`JSON.stringify` for rule comparison in `removeRule()`**: Pragmatic approach for comparing string arrays without deep-equal dependencies. Suitable since callers/targets are always `string[]`.
|
|
41
|
+
- **No thread locking**: Node.js single-threaded event loop means no concurrent mutation of the rules array. The shallow copy in `check()` guards against mid-iteration `addRule`/`removeRule` calls from synchronous code, but no mutex is needed.
|
|
42
|
+
- **Synchronous YAML loading via `readFileSync`**: ACL configuration is loaded at startup or explicit reload. Synchronous I/O is appropriate for initialization-time config loading.
|
|
43
|
+
- **AND logic for conditions**: All present condition keys must pass for the rule to match. This provides a restrictive-by-default composition model (conditions narrow access rather than broaden it).
|
|
44
|
+
- **No debug logging**: Identified gap vs. Python implementation. The TypeScript ACL omits `debug` flag logging to keep the implementation lean. Can be added later via middleware or observability hooks.
|
|
45
|
+
|
|
46
|
+
## Task Breakdown
|
|
47
|
+
|
|
48
|
+
```mermaid
|
|
49
|
+
graph TD
|
|
50
|
+
T1[acl-rule] --> T2[acl-core]
|
|
51
|
+
T1 --> T5[conditional-rules]
|
|
52
|
+
T3[pattern-matching] --> T2
|
|
53
|
+
T2 --> T4[yaml-loading]
|
|
54
|
+
T5 --> T2
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
| Task ID | Title | Estimated Time | Dependencies |
|
|
58
|
+
|---------|-------|---------------|--------------|
|
|
59
|
+
| acl-rule | ACLRule interface definition | 1h | none |
|
|
60
|
+
| acl-core | ACL class with check, addRule, removeRule | 3h | acl-rule, pattern-matching, conditional-rules |
|
|
61
|
+
| pattern-matching | matchPattern() wildcard matching (Algorithm A08) | 2h | none |
|
|
62
|
+
| yaml-loading | ACL.load() from YAML, reload() support | 2h | acl-core |
|
|
63
|
+
| conditional-rules | _checkConditions() with identity_types, roles, max_call_depth | 2h | acl-rule |
|
|
64
|
+
|
|
65
|
+
## Risks and Considerations
|
|
66
|
+
|
|
67
|
+
- **Rule ordering sensitivity**: First-match-wins means a broadly-scoped rule placed early can shadow more specific rules. Documentation should emphasize that more specific rules should appear before general rules.
|
|
68
|
+
- **`JSON.stringify` ordering dependency**: `removeRule` relies on `JSON.stringify` producing identical output for identical arrays. This holds for simple `string[]` but would break for arrays containing objects with non-deterministic key order. Current usage is safe since callers/targets are always string arrays.
|
|
69
|
+
- **Synchronous file I/O in `load()`/`reload()`**: Blocks the event loop during YAML parsing. Acceptable for startup configuration but could be problematic if `reload()` is called frequently with large config files.
|
|
70
|
+
- **No validation of condition values at load time**: Condition values (e.g., `identity_types` expected as `string[]`) are validated at match time via type assertions, not at YAML load time. Malformed conditions will only surface when a rule is evaluated.
|
|
71
|
+
- **Debug flag exists but is unused**: The `debug` property is declared on the `ACL` class but no logging is wired up, representing a gap from the Python implementation.
|
|
72
|
+
|
|
73
|
+
## Acceptance Criteria
|
|
74
|
+
|
|
75
|
+
- [x] `ACLRule` interface defines callers, targets, effect, description, and optional conditions
|
|
76
|
+
- [x] `ACL.check()` implements first-match-wins evaluation returning `boolean`
|
|
77
|
+
- [x] Null caller is normalized to `'@external'`
|
|
78
|
+
- [x] `@system` pattern checks `context.identity.type === 'system'`
|
|
79
|
+
- [x] `matchPattern()` handles exact, wildcard, prefix, suffix, and multi-segment patterns
|
|
80
|
+
- [x] `ACL.load()` parses YAML, validates structure, and returns configured ACL instance
|
|
81
|
+
- [x] `reload()` re-reads YAML and replaces rules in-place; throws `ACLRuleError` if not loaded from YAML
|
|
82
|
+
- [x] `addRule()` prepends rules; `removeRule()` finds and removes by JSON-stringified caller/target match
|
|
83
|
+
- [x] Conditions (`identity_types`, `roles`, `max_call_depth`) are evaluated with AND logic
|
|
84
|
+
- [x] `ACLRuleError` thrown for invalid config structure; `ConfigNotFoundError` for missing YAML files
|
|
85
|
+
- [x] All tests pass with `vitest`; zero errors from `tsc --noEmit`
|
|
86
|
+
|
|
87
|
+
## References
|
|
88
|
+
|
|
89
|
+
- `src/acl.ts` -- ACL class and ACLRule interface
|
|
90
|
+
- `src/utils/pattern.ts` -- matchPattern wildcard utility
|
|
91
|
+
- `src/errors.ts` -- ACLDeniedError, ACLRuleError, ConfigNotFoundError
|
|
92
|
+
- `src/context.ts` -- Context and Identity types (consumed by ACL)
|