apcore-js 0.14.1 → 0.15.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 (37) hide show
  1. package/README.md +136 -1
  2. package/dist/config.d.ts +74 -3
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js +422 -12
  5. package/dist/config.js.map +1 -1
  6. package/dist/error-formatter.d.ts +37 -0
  7. package/dist/error-formatter.d.ts.map +1 -0
  8. package/dist/error-formatter.js +51 -0
  9. package/dist/error-formatter.js.map +1 -0
  10. package/dist/errors.d.ts +30 -0
  11. package/dist/errors.d.ts.map +1 -1
  12. package/dist/errors.js +48 -0
  13. package/dist/errors.js.map +1 -1
  14. package/dist/events/subscribers.d.ts +1 -1
  15. package/dist/events/subscribers.d.ts.map +1 -1
  16. package/dist/events/subscribers.js +4 -4
  17. package/dist/events/subscribers.js.map +1 -1
  18. package/dist/generated/version.d.ts +1 -1
  19. package/dist/generated/version.js +1 -1
  20. package/dist/index.d.ts +4 -2
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +4 -2
  23. package/dist/index.js.map +1 -1
  24. package/dist/middleware/platform-notify.d.ts.map +1 -1
  25. package/dist/middleware/platform-notify.js +4 -2
  26. package/dist/middleware/platform-notify.js.map +1 -1
  27. package/dist/sys-modules/control.d.ts.map +1 -1
  28. package/dist/sys-modules/control.js +15 -2
  29. package/dist/sys-modules/control.js.map +1 -1
  30. package/dist/sys-modules/registration.d.ts +1 -1
  31. package/dist/sys-modules/registration.d.ts.map +1 -1
  32. package/dist/sys-modules/registration.js +40 -9
  33. package/dist/sys-modules/registration.js.map +1 -1
  34. package/dist/sys-modules/toggle.d.ts.map +1 -1
  35. package/dist/sys-modules/toggle.js +6 -2
  36. package/dist/sys-modules/toggle.js.map +1 -1
  37. package/package.json +1 -1
package/README.md CHANGED
@@ -4,6 +4,10 @@
4
4
 
5
5
  # apcore
6
6
 
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Node_18+-blue.svg)](https://github.com/aiperceivable/apcore-typescript)
8
+ [![License](https://img.shields.io/badge/license-Apache%202.0-green.svg)](https://opensource.org/licenses/Apache-2.0)
9
+ [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/12294/badge)](https://www.bestpractices.dev/projects/12294)
10
+
7
11
  **AI-Perceivable Core**
8
12
 
9
13
  > **Build once, invoke by Code or AI.**
@@ -24,6 +28,7 @@ apcore is an AI-Perceivable module standard that makes every interface naturally
24
28
  - **Observability** — Tracing (spans + exporters), metrics (counters + histograms + Prometheus export), structured logging with redaction
25
29
  - **Schema export** — JSON/YAML schema export with strict and compact modes
26
30
  - **Caching & pagination annotations** — `cacheable`, `cacheTtl`, `cacheKeyFields` for result caching; `paginated`, `paginationStyle` for paginated modules
31
+ - **Config Bus** — Namespace-based configuration registry with typed access, env prefix dispatch, hot-reload, and external config mounting (`Config.registerNamespace()`, `config.namespace()`, `config.bind<T>()`, `config.mount()`)
27
32
 
28
33
  ## Documentation
29
34
 
@@ -100,11 +105,141 @@ const result = await executor.call('example.greet', { name: 'World' });
100
105
  | `Registry` | Module storage — discover, register, get, list, watch |
101
106
  | `Executor` | Execution engine — call with middleware pipeline, ACL, approval |
102
107
  | `Context` | Request context — trace ID, identity, call chain, cancel token |
103
- | `Config` | Configuration — load from YAML, get/set values |
108
+ | `Config` | Configuration — load from YAML, namespace bus, get/set/bind values |
104
109
  | `ACL` | Access control — rule-based caller/target authorization |
105
110
  | `Middleware` | Pipeline hooks — before/after/onError interception |
106
111
  | `EventEmitter` | Event system — subscribe, emit, flush |
107
112
 
113
+ ## Configuration
114
+
115
+ ### Config Bus
116
+
117
+ `Config` acts as an ecosystem-level Config Bus. Any package can register a namespace with optional JSON Schema validation, environment variable prefix, and defaults.
118
+
119
+ ```typescript
120
+ import { Config } from 'apcore-js';
121
+
122
+ // Register a namespace (class-level, shared across all Config instances)
123
+ Config.registerNamespace('myPlugin', {
124
+ envPrefix: 'MY_PLUGIN',
125
+ defaults: { timeout: 5000, retries: 3 },
126
+ schema: {
127
+ type: 'object',
128
+ properties: {
129
+ timeout: { type: 'number' },
130
+ retries: { type: 'number' },
131
+ },
132
+ },
133
+ });
134
+
135
+ const config = Config.load('apcore.yaml');
136
+
137
+ // Dot-path access with namespace resolution
138
+ const timeout = config.get('myPlugin.timeout'); // 5000 (or env override)
139
+
140
+ // Full namespace subtree
141
+ const pluginConfig = config.namespace('myPlugin');
142
+
143
+ // Typed access — pass a class constructor; its constructor receives the namespace dict
144
+ class MyPluginConfig {
145
+ timeout: number;
146
+ retries: number;
147
+ constructor(data: Record<string, unknown>) {
148
+ this.timeout = (data['timeout'] as number) ?? 5000;
149
+ this.retries = (data['retries'] as number) ?? 3;
150
+ }
151
+ }
152
+ const typed = config.bind('myPlugin', MyPluginConfig);
153
+
154
+ // Mount an external config source (e.g. an existing config file)
155
+ config.mount('myPlugin', { fromFile: './my-plugin.yaml' });
156
+ // Or from an in-memory object:
157
+ config.mount('myPlugin', { fromDict: { timeout: 10000 } });
158
+
159
+ // Introspect registered namespaces
160
+ const names = Config.registeredNamespaces(); // string[]
161
+ ```
162
+
163
+ ### Environment Variable Overrides
164
+
165
+ Merge priority (highest wins): **environment variables > config file > namespace defaults**.
166
+
167
+ Two prefix conventions are supported:
168
+
169
+ | Convention | Applies to | Example |
170
+ |------------|------------|---------|
171
+ | `APCORE_` + `KEY_PATH` (single `_` → `.`) | Legacy flat keys | `APCORE_EXECUTOR_DEFAULT_TIMEOUT=5000` |
172
+ | `APCORE__` + namespace prefix (double `__`) | apcore sub-package namespaces | `APCORE__OBSERVABILITY_TRACING_ENABLED=true` |
173
+
174
+ apcore pre-registers the following namespaces and env prefixes:
175
+
176
+ | Namespace | Env prefix | Wraps |
177
+ |-----------|-----------|-------|
178
+ | `observability` | `APCORE__OBSERVABILITY` | `apcore.observability.*` keys |
179
+ | `sysModules` | `APCORE__SYS` | `apcore.sys_modules.*` keys |
180
+
181
+ Third-party packages should use their own prefix (e.g. `APCORE__MCP` for apcore-mcp) to avoid collisions.
182
+
183
+ ### Hot Reload
184
+
185
+ `config.reload()` re-reads the source YAML, re-detects legacy/namespace mode, re-applies all namespace defaults and env overrides, re-validates, and re-reads any mounted files.
186
+
187
+ ```typescript
188
+ const config = Config.load('apcore.yaml');
189
+ // ... runtime config change on disk ...
190
+ config.reload(); // picks up all changes
191
+ ```
192
+
193
+ ### YAML File Format
194
+
195
+ Configuration files support two modes. **Legacy mode** (no `apcore:` key) is fully backward compatible. **Namespace mode** is activated when an `apcore:` top-level key is present; each namespace occupies its own top-level section. The `_config` reserved namespace controls validation behavior.
196
+
197
+ ```yaml
198
+ # Namespace mode
199
+ apcore:
200
+ version: "0.15.0"
201
+
202
+ _config:
203
+ strict: true
204
+
205
+ observability:
206
+ tracing:
207
+ enabled: true
208
+ samplingRate: 1.0
209
+
210
+ myPlugin:
211
+ timeout: 10000
212
+ retries: 5
213
+ ```
214
+
215
+ ### Error Codes
216
+
217
+ New error codes added in v0.15.0:
218
+
219
+ | Code | Description |
220
+ |------|-------------|
221
+ | `CONFIG_NAMESPACE_DUPLICATE` | `Config.registerNamespace()` called with an already-registered name |
222
+ | `CONFIG_NAMESPACE_RESERVED` | `Config.registerNamespace()` called with a reserved name (e.g. `_config`) |
223
+ | `CONFIG_ENV_PREFIX_CONFLICT` | Two namespaces declare the same `envPrefix` |
224
+ | `CONFIG_MOUNT_ERROR` | `config.mount()` cannot read or parse the external source |
225
+ | `CONFIG_BIND_ERROR` | `config.bind<T>()` or `config.getTyped<T>()` type guard fails |
226
+ | `ERROR_FORMATTER_DUPLICATE` | `ErrorFormatterRegistry.register()` called for an already-registered surface |
227
+
228
+ ### Event Type Canonical Names
229
+
230
+ apcore 0.15.0 resolves two event-type collisions. Canonical dot-namespaced names should be used in new code; legacy short-form names remain emitted as aliases during the transition period.
231
+
232
+ | Legacy name (alias) | Canonical name | Meaning |
233
+ |---------------------|----------------|---------|
234
+ | `"module_health_changed"` | `"apcore.module.toggled"` | Module enabled/disabled toggle |
235
+ | `"module_health_changed"` | `"apcore.health.recovered"` | Error-rate recovery after spike |
236
+ | `"config_changed"` | `"apcore.config.updated"` | Config key updated at runtime |
237
+ | `"config_changed"` | `"apcore.module.reloaded"` | Module reloaded from disk |
238
+
239
+ Naming convention: `apcore.*` is reserved for core events. Adapter packages use their own prefix (`apcore-mcp.*`, `apcore-a2a.*`, `apcore-cli.*`).
240
+
241
+ ---
242
+
108
243
  ## Examples
109
244
 
110
245
  The `examples/` directory contains runnable demos:
package/dist/config.d.ts CHANGED
@@ -1,21 +1,60 @@
1
1
  /**
2
2
  * Configuration loading, validation, and environment variable overrides (Algorithm A12).
3
+ * Supports legacy mode (flat YAML) and namespace mode (apcore top-level key).
3
4
  */
5
+ /**
6
+ * Search for a config file in the standard discovery order (§9.14).
7
+ * Returns the path of the first found file, or null if none found.
8
+ */
9
+ export declare function discoverConfigFile(): string | null;
4
10
  /**
5
11
  * Configuration system with YAML loading, env overrides, and validation.
6
12
  *
7
- * Merge priority (highest wins): environment variables > config file > defaults.
13
+ * Merge priority (highest wins): environment variables > mount data > config file > namespace defaults > defaults.
14
+ *
15
+ * Two modes:
16
+ * - Legacy mode: top-level YAML has no "apcore" key. Backward compatible.
17
+ * - Namespace mode: top-level YAML has "apcore" key. Enables namespace features.
8
18
  *
9
19
  * Backward compatible: `new Config(data)` still works for in-memory configuration.
10
20
  */
11
21
  export declare class Config {
12
22
  private _data;
13
23
  private _yamlPath;
24
+ private _mode;
25
+ private _mounts;
14
26
  constructor(data?: Record<string, unknown>);
27
+ /**
28
+ * Register a namespace globally.
29
+ *
30
+ * Throws:
31
+ * - ConfigNamespaceReservedError if name is in the reserved set.
32
+ * - ConfigNamespaceDuplicateError if name is already registered.
33
+ * - ConfigEnvPrefixConflictError if envPrefix is already used or matches the
34
+ * APCORE_[A-Z0-9] pattern.
35
+ */
36
+ static registerNamespace(options: {
37
+ name: string;
38
+ schema?: object | string | null;
39
+ envPrefix?: string | null;
40
+ defaults?: Record<string, unknown> | null;
41
+ }): void;
42
+ /**
43
+ * Return a snapshot of all registered namespaces.
44
+ */
45
+ static registeredNamespaces(): Array<{
46
+ name: string;
47
+ envPrefix: string | null;
48
+ hasSchema: boolean;
49
+ }>;
15
50
  /**
16
51
  * Load configuration from a YAML file with env overrides.
52
+ *
53
+ * Auto-detects mode:
54
+ * - Namespace mode: top-level "apcore" key present.
55
+ * - Legacy mode: otherwise (backward compatible).
17
56
  */
18
- static load(yamlPath: string, options?: {
57
+ static load(yamlPath?: string, options?: {
19
58
  validate?: boolean;
20
59
  }): Config;
21
60
  /** Create a Config from default values with env overrides applied. */
@@ -26,16 +65,48 @@ export declare class Config {
26
65
  set(key: string, value: unknown): void;
27
66
  /** Return a deep copy of the raw config data. */
28
67
  get data(): Record<string, unknown>;
68
+ /** Return the detected mode: 'legacy' or 'namespace'. */
69
+ get mode(): 'legacy' | 'namespace';
70
+ /**
71
+ * Attach external config data to a namespace.
72
+ *
73
+ * Exactly one of fromFile or fromDict must be provided.
74
+ * Throws ConfigMountError if namespace is "_config" or file not found.
75
+ */
76
+ mount(namespace: string, options: {
77
+ fromFile?: string;
78
+ fromDict?: Record<string, unknown>;
79
+ }): void;
80
+ /**
81
+ * Return a deep copy of a namespace subtree.
82
+ */
83
+ namespace(name: string): Record<string, unknown>;
84
+ /**
85
+ * Typed get with coercion and validation.
86
+ * Applies the coerce function to the raw value and returns the result.
87
+ * Throws ConfigError if the raw value is undefined.
88
+ */
89
+ getTyped<T>(path: string, coerce: (v: unknown) => T): T;
90
+ /**
91
+ * Deserialize a namespace subtree into a class instance.
92
+ * The schema constructor receives the namespace data as a plain object.
93
+ * Throws ConfigBindError if instantiation fails.
94
+ */
95
+ bind<T>(namespace: string, schema: new (data: Record<string, unknown>) => T): T;
29
96
  /**
30
97
  * Validate the configuration per Algorithm A12.
31
98
  *
32
- * Checks required fields, type constraints, and semantic rules.
99
+ * In legacy mode: checks required fields, type constraints, and semantic rules.
100
+ * In namespace mode (A12-NS): validates data.apcore; throws on unknown namespaces
101
+ * if strict mode is enabled via data._config.strict.
33
102
  * Collects all errors before raising.
34
103
  */
35
104
  validate(): void;
105
+ private _validateNamespaceMode;
36
106
  /**
37
107
  * Re-read configuration from the original YAML file.
38
108
  * Only works if the Config was created via Config.load().
109
+ * In namespace mode, re-applies namespace defaults, env overrides, and mount data.
39
110
  */
40
111
  reload(): void;
41
112
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAsKH;;;;;;GAMG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,SAAS,CAAuB;gBAE5B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAI1C;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,MAAM;IAoCvE,sEAAsE;IACtE,MAAM,CAAC,YAAY,IAAI,MAAM;IAK7B,iDAAiD;IACjD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,OAAO,GAAG,OAAO;IAIjD,iDAAiD;IACjD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAItC,iDAAiD;IACjD,IAAI,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAElC;IAED;;;;;OAKG;IACH,QAAQ,IAAI,IAAI;IA2BhB;;;OAGG;IACH,MAAM,IAAI,IAAI;CAOf"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAiRH;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,GAAG,IAAI,CA2BlD;AAMD;;;;;;;;;;GAUG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,OAAO,CAAmD;gBAEtD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAQ1C;;;;;;;;OAQG;IACH,MAAM,CAAC,iBAAiB,CAAC,OAAO,EAAE;QAChC,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;QAChC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;KAC3C,GAAG,IAAI;IAqBR;;OAEG;IACH,MAAM,CAAC,oBAAoB,IAAI,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IAYpG;;;;;;OAMG;IACH,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,MAAM;IA4ExE,sEAAsE;IACtE,MAAM,CAAC,YAAY,IAAI,MAAM;IAS7B,iDAAiD;IACjD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,OAAO,GAAG,OAAO;IAYjD,iDAAiD;IACjD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAoBtC,iDAAiD;IACjD,IAAI,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAElC;IAED,yDAAyD;IACzD,IAAI,IAAI,IAAI,QAAQ,GAAG,WAAW,CAEjC;IAED;;;;;OAKG;IACH,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAAG,IAAI;IAmDlG;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAQhD;;;;OAIG;IACH,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,CAAC,GAAG,CAAC;IAQvD;;;;OAIG;IACH,IAAI,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC;IAS/E;;;;;;;OAOG;IACH,QAAQ,IAAI,IAAI;IAgChB,OAAO,CAAC,sBAAsB;IAuC9B;;;;OAIG;IACH,MAAM,IAAI,IAAI;CAoBf"}