@antelopejs/interface-core 0.0.5 → 0.0.7
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/dist/proxies.js +6 -1
- package/dist/proxies.js.map +1 -1
- package/dist/tests/async-proxy-call.test.d.ts +1 -0
- package/dist/tests/async-proxy-call.test.js +35 -0
- package/dist/tests/async-proxy-call.test.js.map +1 -0
- package/docs/1.introduction.md +69 -0
- package/docs/2.proxies.md +207 -0
- package/docs/3.decorators.md +169 -0
- package/docs/4.metadata.md +107 -0
- package/docs/5.modules.md +180 -0
- package/docs/6.logging.md +106 -0
- package/docs/7.configuration.md +182 -0
- package/docs/8.runtime.md +60 -0
- package/package.json +8 -3
- package/skills/core-interface/SKILL.md +98 -0
package/dist/proxies.js
CHANGED
|
@@ -57,7 +57,12 @@ class AsyncProxy {
|
|
|
57
57
|
*/
|
|
58
58
|
call(...args) {
|
|
59
59
|
if (this.callback) {
|
|
60
|
-
|
|
60
|
+
try {
|
|
61
|
+
return Promise.resolve(this.callback(...args));
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
return Promise.reject(err);
|
|
65
|
+
}
|
|
61
66
|
}
|
|
62
67
|
if (internal_1.internal.testStubMode) {
|
|
63
68
|
return Promise.reject(new Error(STUB_NOT_IMPLEMENTED));
|
package/dist/proxies.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxies.js","sourceRoot":"","sources":["../src/proxies.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"proxies.js","sourceRoot":"","sources":["../src/proxies.ts"],"names":[],"mappings":";;;AA2RA,oDAQC;AAnSD,yCAAsC;AACtC,6DAA2D;AAE3D,MAAM,oBAAoB,GACxB,wEAAwE;IACxE,2DAA2D,CAAC;AAI9D;;;;;GAKG;AACH,MAAa,UAAU;IACb,QAAQ,CAAK;IACb,KAAK,GAIR,EAAE,CAAC;IAER;;;;;;;;OAQG;IACI,MAAM,CAAC,QAAW,EAAE,YAAsB;QAC/C,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,MAAM,GAAG,oBAAoB,EAAE,CAAC;YACtC,IAAI,MAAM,EAAE,CAAC;gBACX,mBAAQ,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACvC,CAAC;QACH,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE;gBAC/C,IAAI,CAAC;oBACH,OAAO,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;gBAC7B,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,CAAC,GAAG,CAAC,CAAC;gBACd,CAAC;YACH,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAED;;OAEG;IACI,MAAM;QACX,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACI,IAAI,CAAC,GAAG,IAAmB;QAChC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,IAAI,CAAC;gBACH,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;YACjD,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;QACD,IAAI,mBAAQ,CAAC,YAAY,EAAE,CAAC;YAC1B,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC,CAAC;QACzD,CAAC;QACD,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CACxC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAC3C,CAAC;IACJ,CAAC;CACF;AAhED,gCAgEC;AAMD;;;;;GAKG;AACH,MAAa,gBAAgB;IACnB,gBAAgB,CAAK;IACrB,kBAAkB,CAAwB;IAC1C,UAAU,GAAG,IAAI,GAAG,EAMzB,CAAC;IAEJ;;;;;;;OAOG;IACI,UAAU,CAAC,QAAW,EAAE,YAAsB;QACnD,IAAI,CAAC,gBAAgB,GAAG,QAAQ,CAAC;QACjC,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,MAAM,GAAG,oBAAoB,EAAE,CAAC;YACtC,IAAI,MAAM,EAAE,CAAC;gBACX,mBAAQ,CAAC,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC;QACD,KAAK,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAC7C,IAAI,CAAC;gBACH,QAAQ,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;YACxB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,wEAAwE;gBACxE,IAAI,mBAAQ,CAAC,mBAAmB,EAAE,CAAC;oBACjC,mBAAQ,CAAC,mBAAmB,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;gBACxC,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACI,YAAY,CAAC,QAA8B;QAChD,IAAI,CAAC,kBAAkB,GAAG,QAAQ,CAAC;IACrC,CAAC;IAED;;OAEG;IACI,MAAM;QACX,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;QAClC,IAAI,CAAC,kBAAkB,GAAG,SAAS,CAAC;IACtC,CAAC;IAED;;;;;;;OAOG;IACI,QAAQ,CAAC,EAAU,EAAE,GAAG,IAAc;QAC3C,IAAI,CAAC,IAAI,CAAC,gBAAgB,IAAI,mBAAQ,CAAC,YAAY,EAAE,CAAC;YACpD,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;QACxC,CAAC;QACD,MAAM,MAAM,GAAG,oBAAoB,EAAE,CAAC;QACtC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,IAAI,CAAC,gBAAgB,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED;;;;OAIG;IACI,UAAU,CAAC,EAAU;QAC1B,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YAC5B,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBAC5B,IAAI,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC;YAC9B,CAAC;YACD,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACI,gBAAgB,CAAC,GAAW;QACjC,KAAK,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAC/C,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;gBACnB,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;oBAC5B,IAAI,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC;gBAC9B,CAAC;gBACD,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;IACH,CAAC;CACF;AA3GD,4CA2GC;AAGD;;;;;GAKG;AACH,MAAa,UAAU;IACb,UAAU,GAGb,EAAE,CAAC;IAER;QACE,mBAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED;;;;OAIG;IACI,IAAI,CAAC,GAAG,IAAmB;QAChC,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACvC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QAChB,CAAC;IACH,CAAC;IAED;;;;OAIG;IACI,QAAQ,CAAC,IAAO;QACrB,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;YAC/D,OAAO;QACT,CAAC;QACD,MAAM,MAAM,GAAG,oBAAoB,EAAE,CAAC;QACtC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;IAED;;;;OAIG;IACI,UAAU,CAAC,EAAK;QACrB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;IACtE,CAAC;IAED;;;;;OAKG;IACI,gBAAgB,CAAC,GAAW;QACjC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC;IAC3E,CAAC;CACF;AApDD,gCAoDC;AAED,SAAS,gBAAgB,CAAC,UAAU,GAAG,CAAC;IACtC,MAAM,UAAU,GAAG,KAAK,CAAC,iBAAiB,CAAC;IAC3C,MAAM,QAAQ,GAAG,KAAK,CAAC,eAAe,CAAC;IACvC,KAAK,CAAC,eAAe,GAAG,QAAQ,CAAC;IACjC,KAAK,CAAC,iBAAiB,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC;IAC9C,MAAM,MAAM,GAAG,EAA8B,CAAC;IAC9C,KAAK,CAAC,iBAAiB,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAqC,CAAC;IAC3D,KAAK,CAAC,iBAAiB,GAAG,UAAU,CAAC;IACrC,KAAK,CAAC,eAAe,GAAG,QAAQ,CAAC;IACjC,OAAO,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,oBAAoB,CAAC,UAAU,GAAG,CAAC;IACjD,MAAM,KAAK,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAC3C,MAAM,WAAW,GAAG,IAAA,wCAAmB,EAAC,KAAK,CAAC,CAAC;IAC/C,IAAI,WAAW,CAAC,MAAM,EAAE,CAAC;QACvB,OAAO,WAAW,CAAC,MAAM,CAAC;IAC5B,CAAC;IACD,mBAAQ,CAAC,oBAAoB,EAAE,CAAC,KAAK,CAAC,CAAC;IACvC,OAAO,WAAW,CAAC,aAAa,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const chai_1 = require("chai");
|
|
4
|
+
const __1 = require("..");
|
|
5
|
+
describe("AsyncProxy call", () => {
|
|
6
|
+
it("rejects instead of throwing synchronously when the callback throws", async () => {
|
|
7
|
+
const proxy = new __1.AsyncProxy();
|
|
8
|
+
const boom = new Error("boom");
|
|
9
|
+
proxy.onCall(() => {
|
|
10
|
+
throw boom;
|
|
11
|
+
}, true);
|
|
12
|
+
// Must return a rejected promise, never throw synchronously: a sync throw
|
|
13
|
+
// here would propagate on this line and fail the test.
|
|
14
|
+
const result = proxy.call();
|
|
15
|
+
(0, chai_1.expect)(result).to.be.instanceOf(Promise);
|
|
16
|
+
const err = await result.then(() => chai_1.expect.fail("expected call() to reject"), (e) => e);
|
|
17
|
+
(0, chai_1.expect)(err).to.equal(boom);
|
|
18
|
+
});
|
|
19
|
+
it("resolves with the callback return value", async () => {
|
|
20
|
+
const proxy = new __1.AsyncProxy();
|
|
21
|
+
proxy.onCall(() => 42, true);
|
|
22
|
+
(0, chai_1.expect)(await proxy.call()).to.equal(42);
|
|
23
|
+
});
|
|
24
|
+
it("converts a queued call that throws on replay into a rejection", async () => {
|
|
25
|
+
const proxy = new __1.AsyncProxy();
|
|
26
|
+
const boom = new Error("queued boom");
|
|
27
|
+
const pending = proxy.call();
|
|
28
|
+
proxy.onCall(() => {
|
|
29
|
+
throw boom;
|
|
30
|
+
}, true);
|
|
31
|
+
const err = await pending.then(() => chai_1.expect.fail("expected queued call to reject"), (e) => e);
|
|
32
|
+
(0, chai_1.expect)(err).to.equal(boom);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=async-proxy-call.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-proxy-call.test.js","sourceRoot":"","sources":["../../src/tests/async-proxy-call.test.ts"],"names":[],"mappings":";;AAAA,+BAA8B;AAC9B,0BAAgC;AAEhC,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;QAClF,MAAM,KAAK,GAAG,IAAI,cAAU,EAAe,CAAC;QAC5C,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC;QAC/B,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE;YAChB,MAAM,IAAI,CAAC;QACb,CAAC,EAAE,IAAI,CAAC,CAAC;QAET,0EAA0E;QAC1E,uDAAuD;QACvD,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAEzC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAC3B,GAAG,EAAE,CAAC,aAAM,CAAC,IAAI,CAAC,2BAA2B,CAAC,EAC9C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CACT,CAAC;QACF,IAAA,aAAM,EAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;QACvD,MAAM,KAAK,GAAG,IAAI,cAAU,EAAgB,CAAC;QAC7C,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAE7B,IAAA,aAAM,EAAC,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;QAC7E,MAAM,KAAK,GAAG,IAAI,cAAU,EAAe,CAAC;QAC5C,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC;QAEtC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE;YAChB,MAAM,IAAI,CAAC;QACb,CAAC,EAAE,IAAI,CAAC,CAAC;QAET,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAC5B,GAAG,EAAE,CAAC,aAAM,CAAC,IAAI,CAAC,gCAAgC,CAAC,EACnD,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CACT,CAAC;QACF,IAAA,aAAM,EAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Interface Core Documentation
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`@antelopejs/interface-core` provides the foundational building blocks for the AntelopeJS interface system. It defines the core primitives that enable type-safe, module-aware communication between decoupled components.
|
|
6
|
+
|
|
7
|
+
The package exports several key categories of functionality:
|
|
8
|
+
|
|
9
|
+
- **Proxies** - `AsyncProxy`, `EventProxy`, and `RegisteringProxy` manage cross-module function calls, events, and registrations with automatic cleanup when modules are unloaded.
|
|
10
|
+
- **Interface functions** - `InterfaceFunction` and `ImplementInterface` wire up interface declarations to their concrete implementations.
|
|
11
|
+
- **Decorator factories** - Utilities for building type-safe TypeScript decorators that target classes, properties, methods, and parameters.
|
|
12
|
+
- **Metadata** - `GetMetadata` provides reflection-based metadata retrieval with inheritance support.
|
|
13
|
+
- **Module lifecycle** - Events and functions for managing module construction, startup, shutdown, and destruction.
|
|
14
|
+
- **Logging** - A structured, channel-based logging system with multiple severity levels.
|
|
15
|
+
- **Configuration** - TypeScript types and helpers for defining AntelopeJS project configurations.
|
|
16
|
+
|
|
17
|
+
## Import paths
|
|
18
|
+
|
|
19
|
+
The package exposes several entry points:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
// Main entry - proxies, InterfaceFunction, GetMetadata, ImplementInterface, GetInterfaceInstances
|
|
23
|
+
import { AsyncProxy, InterfaceFunction, GetMetadata } from "@antelopejs/interface-core";
|
|
24
|
+
|
|
25
|
+
// Decorator factories
|
|
26
|
+
import { MakeClassDecorator, MakeMethodDecorator } from "@antelopejs/interface-core/decorators";
|
|
27
|
+
|
|
28
|
+
// Module lifecycle events and management
|
|
29
|
+
import { Events, ListModules, LoadModule } from "@antelopejs/interface-core/modules";
|
|
30
|
+
|
|
31
|
+
// Logging system
|
|
32
|
+
import { Logging } from "@antelopejs/interface-core/logging";
|
|
33
|
+
|
|
34
|
+
// Configuration types and helpers
|
|
35
|
+
import { defineConfig } from "@antelopejs/interface-core/config";
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## How interfaces work
|
|
39
|
+
|
|
40
|
+
AntelopeJS interfaces act as contracts between modules. One module declares an interface using proxy objects and `InterfaceFunction`, while another module provides the implementation using `ImplementInterface`. The proxy layer handles queuing, lifecycle management, and automatic cleanup.
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
// Declaration side: define what the interface looks like
|
|
44
|
+
import { InterfaceFunction, EventProxy } from "@antelopejs/interface-core";
|
|
45
|
+
|
|
46
|
+
export const GetUser = InterfaceFunction<(id: string) => { name: string }>();
|
|
47
|
+
export const OnUserCreated = new EventProxy<(userId: string) => void>();
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
// Implementation side: provide the concrete behavior
|
|
52
|
+
import { ImplementInterface } from "@antelopejs/interface-core";
|
|
53
|
+
import * as UserInterface from "./declaration";
|
|
54
|
+
|
|
55
|
+
ImplementInterface(UserInterface, {
|
|
56
|
+
GetUser(id: string) {
|
|
57
|
+
return { name: "Alice" };
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The proxy system ensures that calls to `GetUser` before the implementation is attached are automatically queued and resolved once the implementation becomes available. When the implementing module is unloaded, the proxy detaches and resumes queuing.
|
|
63
|
+
|
|
64
|
+
## Next steps
|
|
65
|
+
|
|
66
|
+
- [Proxies](./2.proxies.md) - Deep dive into `AsyncProxy`, `EventProxy`, and `RegisteringProxy`
|
|
67
|
+
- [Decorators](./3.decorators.md) - Build type-safe decorator factories
|
|
68
|
+
- [Modules](./5.modules.md) - Understand module lifecycle events
|
|
69
|
+
- [Logging](./6.logging.md) - Use the structured logging system
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Proxies
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Proxies are the backbone of cross-module communication in AntelopeJS. They provide module-aware abstractions for function calls, event handling, and registration patterns. Each proxy type automatically tracks the module that attaches callbacks and detaches them when that module is unloaded, preventing memory leaks and dangling references.
|
|
6
|
+
|
|
7
|
+
## `AsyncProxy`
|
|
8
|
+
|
|
9
|
+
`AsyncProxy` wraps asynchronous function calls. It queues invocations when no callback is attached and flushes the queue once a callback becomes available.
|
|
10
|
+
|
|
11
|
+
### Create and use an `AsyncProxy`
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { AsyncProxy } from "@antelopejs/interface-core";
|
|
15
|
+
|
|
16
|
+
const proxy = new AsyncProxy<(name: string) => string>();
|
|
17
|
+
|
|
18
|
+
// Calls made before attachment are queued
|
|
19
|
+
const result = proxy.call("Alice"); // returns Promise<string>
|
|
20
|
+
|
|
21
|
+
// Attach an implementation - queued calls resolve immediately
|
|
22
|
+
proxy.onCall((name: string) => {
|
|
23
|
+
return `Hello, ${name}!`;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Subsequent calls execute directly
|
|
27
|
+
const greeting = await proxy.call("Bob"); // "Hello, Bob!"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### `onCall(callback, manualDetach?)`
|
|
31
|
+
|
|
32
|
+
Attaches a callback function to the proxy. The proxy automatically tracks the calling module and detaches the callback when that module is unloaded. Pass `manualDetach: true` to disable automatic cleanup.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
// Automatic cleanup (default) - detaches when the module unloads
|
|
36
|
+
proxy.onCall(myHandler);
|
|
37
|
+
|
|
38
|
+
// Manual cleanup - you are responsible for calling detach()
|
|
39
|
+
proxy.onCall(myHandler, true);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### `call(...args)`
|
|
43
|
+
|
|
44
|
+
Invokes the attached callback. If no callback is attached, the call returns a `Promise` that resolves once a callback is provided. In test stub mode, unattached calls reject with an error.
|
|
45
|
+
|
|
46
|
+
### `detach()`
|
|
47
|
+
|
|
48
|
+
Manually removes the attached callback. After detaching, subsequent calls are queued again.
|
|
49
|
+
|
|
50
|
+
## `InterfaceFunction`
|
|
51
|
+
|
|
52
|
+
`InterfaceFunction` is a convenience wrapper that creates an `AsyncProxy` and returns a callable function. This is the primary way to declare interface functions.
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { InterfaceFunction } from "@antelopejs/interface-core";
|
|
56
|
+
|
|
57
|
+
// Declare a typed interface function
|
|
58
|
+
const GetUser = InterfaceFunction<(id: string) => { name: string; email: string }>();
|
|
59
|
+
|
|
60
|
+
// Call it like a regular async function
|
|
61
|
+
const user = await GetUser("user-123");
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The returned function has a `.proxy` property that exposes the underlying `AsyncProxy` for direct access when needed.
|
|
65
|
+
|
|
66
|
+
## `EventProxy`
|
|
67
|
+
|
|
68
|
+
`EventProxy` manages a list of event handlers with module-aware automatic cleanup. Unlike `AsyncProxy`, it supports multiple registered handlers and does not queue emissions.
|
|
69
|
+
|
|
70
|
+
### Create and use an `EventProxy`
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { EventProxy } from "@antelopejs/interface-core";
|
|
74
|
+
|
|
75
|
+
const onUserCreated = new EventProxy<(userId: string, name: string) => void>();
|
|
76
|
+
|
|
77
|
+
// Register a handler
|
|
78
|
+
onUserCreated.register((userId, name) => {
|
|
79
|
+
console.log(`User created: ${name} (${userId})`);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Emit the event - all registered handlers are called
|
|
83
|
+
onUserCreated.emit("u-1", "Alice");
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### `register(func)`
|
|
87
|
+
|
|
88
|
+
Registers a handler for the event. Duplicate handlers (same function reference) are ignored. The proxy tracks the calling module and removes the handler when that module is unloaded.
|
|
89
|
+
|
|
90
|
+
### `emit(...args)`
|
|
91
|
+
|
|
92
|
+
Calls all registered handlers with the provided arguments.
|
|
93
|
+
|
|
94
|
+
### `unregister(func)`
|
|
95
|
+
|
|
96
|
+
Removes a specific handler by function reference.
|
|
97
|
+
|
|
98
|
+
## `RegisteringProxy`
|
|
99
|
+
|
|
100
|
+
`RegisteringProxy` manages a register/unregister pair. It tracks registered entries by a unique identifier and supports queuing registrations until the callbacks are attached.
|
|
101
|
+
|
|
102
|
+
### Create and use a `RegisteringProxy`
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import { RegisteringProxy } from "@antelopejs/interface-core";
|
|
106
|
+
|
|
107
|
+
const routeRegistry = new RegisteringProxy<(id: string, path: string, handler: Function) => void>();
|
|
108
|
+
|
|
109
|
+
// Register entries - queued if no callback is attached yet
|
|
110
|
+
routeRegistry.register("home", "/", homeHandler);
|
|
111
|
+
routeRegistry.register("about", "/about", aboutHandler);
|
|
112
|
+
|
|
113
|
+
// Attach callbacks - queued registrations replay immediately
|
|
114
|
+
routeRegistry.onRegister((id, path, handler) => {
|
|
115
|
+
router.addRoute(path, handler);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
routeRegistry.onUnregister((id) => {
|
|
119
|
+
router.removeRoute(id);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Unregister by ID
|
|
123
|
+
routeRegistry.unregister("about");
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### `onRegister(callback, manualDetach?)`
|
|
127
|
+
|
|
128
|
+
Attaches the register callback. Any previously registered entries replay through this callback immediately. Automatic module-aware detachment applies unless `manualDetach` is `true`.
|
|
129
|
+
|
|
130
|
+
### `onUnregister(callback)`
|
|
131
|
+
|
|
132
|
+
Attaches the unregister callback. This callback is detached at the same time as the register callback.
|
|
133
|
+
|
|
134
|
+
### `register(id, ...args)`
|
|
135
|
+
|
|
136
|
+
Registers an entry with the given identifier. If a register callback is attached, it executes immediately. Otherwise, the entry is stored and replayed when a callback is attached. The calling module is tracked for automatic cleanup.
|
|
137
|
+
|
|
138
|
+
### `unregister(id)`
|
|
139
|
+
|
|
140
|
+
Removes the entry with the given identifier and calls the unregister callback if one is attached.
|
|
141
|
+
|
|
142
|
+
### `detach()`
|
|
143
|
+
|
|
144
|
+
Manually removes both the register and unregister callbacks. Registered entries remain stored.
|
|
145
|
+
|
|
146
|
+
## `ImplementInterface`
|
|
147
|
+
|
|
148
|
+
`ImplementInterface` connects an interface declaration to its implementation. It iterates over the declaration object and wires up each proxy to the corresponding implementation function.
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import { InterfaceFunction, EventProxy, RegisteringProxy, ImplementInterface } from "@antelopejs/interface-core";
|
|
152
|
+
|
|
153
|
+
// Declaration
|
|
154
|
+
const GetItem = InterfaceFunction<(id: string) => { name: string }>();
|
|
155
|
+
const OnItemAdded = new EventProxy<(id: string) => void>();
|
|
156
|
+
const ItemRegistry = new RegisteringProxy<(id: string, data: any) => void>();
|
|
157
|
+
|
|
158
|
+
const ItemInterface = { GetItem, OnItemAdded, ItemRegistry };
|
|
159
|
+
|
|
160
|
+
// Implementation
|
|
161
|
+
ImplementInterface(ItemInterface, {
|
|
162
|
+
GetItem(id: string) {
|
|
163
|
+
return { name: `Item ${id}` };
|
|
164
|
+
},
|
|
165
|
+
ItemRegistry: {
|
|
166
|
+
register(id: string, data: any) {
|
|
167
|
+
store.set(id, data);
|
|
168
|
+
},
|
|
169
|
+
unregister(id: string) {
|
|
170
|
+
store.delete(id);
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`EventProxy` entries in the declaration are skipped during implementation wiring, as they are emitted from the declaring side, not implemented.
|
|
177
|
+
|
|
178
|
+
## `GetInterfaceInstances` and `GetInterfaceInstance`
|
|
179
|
+
|
|
180
|
+
These functions retrieve information about active interface connections for the current module.
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
import { GetInterfaceInstances, GetInterfaceInstance } from "@antelopejs/interface-core";
|
|
184
|
+
|
|
185
|
+
// Get all connections for an interface
|
|
186
|
+
const connections = GetInterfaceInstances("database");
|
|
187
|
+
|
|
188
|
+
// Get a specific connection by ID
|
|
189
|
+
const primary = GetInterfaceInstance("database", "primary");
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## `GetResponsibleModule`
|
|
193
|
+
|
|
194
|
+
`GetResponsibleModule` inspects the call stack to determine which module is responsible for the current execution. The proxy classes use this internally for automatic cleanup tracking.
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
import { GetResponsibleModule } from "@antelopejs/interface-core";
|
|
198
|
+
|
|
199
|
+
const moduleId = GetResponsibleModule();
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
> **Warning:** Calling `GetResponsibleModule` from within an async context (such as `setTimeout` or `setInterval`) breaks hot reloading. The system logs an error when this is detected.
|
|
203
|
+
|
|
204
|
+
## Next steps
|
|
205
|
+
|
|
206
|
+
- [Decorators](./3.decorators.md) - Build type-safe decorator factories
|
|
207
|
+
- [Metadata](./4.metadata.md) - Reflection-based metadata retrieval
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Decorators
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The `@antelopejs/interface-core/decorators` module provides factory functions for creating type-safe TypeScript decorators. These factories handle the boilerplate of splitting decorator arguments from factory arguments, producing reusable, parameterized decorators for classes, properties, methods, and parameters.
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import {
|
|
11
|
+
MakeClassDecorator,
|
|
12
|
+
MakePropertyDecorator,
|
|
13
|
+
MakeMethodDecorator,
|
|
14
|
+
MakeParameterDecorator,
|
|
15
|
+
} from "@antelopejs/interface-core/decorators";
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Types
|
|
19
|
+
|
|
20
|
+
The module exports several utility types used throughout the decorator system:
|
|
21
|
+
|
|
22
|
+
| Type | Description |
|
|
23
|
+
| -------------------- | ------------------------------------------------------------ |
|
|
24
|
+
| `Func<A, R>` | Generic function type with arguments `A` and return type `R` |
|
|
25
|
+
| `Class<T, A>` | Class constructor that creates instances of type `T` |
|
|
26
|
+
| `ClassDecorator<C>` | Decorator applied to class constructors |
|
|
27
|
+
| `PropertyDecorator` | Decorator applied to class properties |
|
|
28
|
+
| `MethodDecorator` | Decorator applied to class methods and accessors |
|
|
29
|
+
| `ParameterDecorator` | Decorator applied to method parameters |
|
|
30
|
+
|
|
31
|
+
## Single-target decorator factories
|
|
32
|
+
|
|
33
|
+
### `MakeClassDecorator`
|
|
34
|
+
|
|
35
|
+
Creates a decorator factory that targets classes. The handler receives the decorated class as its first argument, followed by any factory parameters.
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { MakeClassDecorator } from "@antelopejs/interface-core/decorators";
|
|
39
|
+
|
|
40
|
+
const Entity = MakeClassDecorator((target: Function, tableName: string) => {
|
|
41
|
+
Reflect.defineMetadata("table", tableName, target);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
@Entity("users")
|
|
45
|
+
class User {
|
|
46
|
+
name!: string;
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### `MakePropertyDecorator`
|
|
51
|
+
|
|
52
|
+
Creates a decorator factory that targets properties. The handler receives the target object and property key, followed by factory parameters.
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { MakePropertyDecorator } from "@antelopejs/interface-core/decorators";
|
|
56
|
+
|
|
57
|
+
const Column = MakePropertyDecorator((target: any, key: PropertyKey, columnName: string) => {
|
|
58
|
+
const columns = Reflect.getOwnMetadata("columns", target) || [];
|
|
59
|
+
columns.push({ key, columnName });
|
|
60
|
+
Reflect.defineMetadata("columns", columns, target);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
class User {
|
|
64
|
+
@Column("user_name")
|
|
65
|
+
name!: string;
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### `MakeMethodDecorator`
|
|
70
|
+
|
|
71
|
+
Creates a decorator factory that targets methods and accessors. The handler receives the target object, method key, and property descriptor, followed by factory parameters.
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { MakeMethodDecorator } from "@antelopejs/interface-core/decorators";
|
|
75
|
+
|
|
76
|
+
const Log = MakeMethodDecorator(
|
|
77
|
+
(target: any, key: PropertyKey, descriptor: PropertyDescriptor, level: string) => {
|
|
78
|
+
const original = descriptor.value;
|
|
79
|
+
descriptor.value = function (...args: any[]) {
|
|
80
|
+
console.log(`[${level}] Calling ${String(key)}`);
|
|
81
|
+
return original.apply(this, args);
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
class Service {
|
|
87
|
+
@Log("info")
|
|
88
|
+
process() {
|
|
89
|
+
// ...
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### `MakeParameterDecorator`
|
|
95
|
+
|
|
96
|
+
Creates a decorator factory that targets method parameters. The handler receives the target object, method key, and parameter index, followed by factory parameters.
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import { MakeParameterDecorator } from "@antelopejs/interface-core/decorators";
|
|
100
|
+
|
|
101
|
+
const Inject = MakeParameterDecorator(
|
|
102
|
+
(target: any, key: PropertyKey, index: number, token: string) => {
|
|
103
|
+
const injections = Reflect.getOwnMetadata("injections", target, key) || [];
|
|
104
|
+
injections[index] = token;
|
|
105
|
+
Reflect.defineMetadata("injections", injections, target, key);
|
|
106
|
+
},
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
class Controller {
|
|
110
|
+
handle(@Inject("db") db: any) {
|
|
111
|
+
// ...
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Multi-target decorator factories
|
|
117
|
+
|
|
118
|
+
For decorators that apply to multiple targets, the module provides combined factory functions. These detect the decorator context automatically based on the number and types of arguments received.
|
|
119
|
+
|
|
120
|
+
| Factory | Targets |
|
|
121
|
+
| -------------------------------------------------- | ----------------------------------------- |
|
|
122
|
+
| `MakePropertyAndClassDecorator` | Properties and classes |
|
|
123
|
+
| `MakeMethodAndClassDecorator` | Methods and classes |
|
|
124
|
+
| `MakeMethodAndPropertyDecorator` | Methods and properties |
|
|
125
|
+
| `MakeMethodAndPropertyAndClassDecorator` | Methods, properties, and classes |
|
|
126
|
+
| `MakeParameterAndClassDecorator` | Parameters and classes |
|
|
127
|
+
| `MakeParameterAndPropertyDecorator` | Parameters and properties |
|
|
128
|
+
| `MakeParameterAndPropertyAndClassDecorator` | Parameters, properties, and classes |
|
|
129
|
+
| `MakeParameterAndMethodDecorator` | Parameters and methods |
|
|
130
|
+
| `MakeParameterAndMethodAndClassDecorator` | Parameters, methods, and classes |
|
|
131
|
+
| `MakeParameterAndMethodAndPropertyDecorator` | Parameters, methods, and properties |
|
|
132
|
+
| `MakeParameterAndMethodAndPropertyAndClassDecorator`| Parameters, methods, properties, classes |
|
|
133
|
+
|
|
134
|
+
### Example: method and class decorator
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import { MakeMethodAndClassDecorator } from "@antelopejs/interface-core/decorators";
|
|
138
|
+
|
|
139
|
+
const Track = MakeMethodAndClassDecorator(
|
|
140
|
+
(target: any, key: PropertyKey | undefined, descriptor: PropertyDescriptor | undefined, category: string) => {
|
|
141
|
+
if (descriptor) {
|
|
142
|
+
// Applied to a method
|
|
143
|
+
const original = descriptor.value;
|
|
144
|
+
descriptor.value = function (...args: any[]) {
|
|
145
|
+
console.log(`[${category}] ${String(key)} called`);
|
|
146
|
+
return original.apply(this, args);
|
|
147
|
+
};
|
|
148
|
+
} else {
|
|
149
|
+
// Applied to a class
|
|
150
|
+
Reflect.defineMetadata("trackCategory", category, target);
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
@Track("api")
|
|
156
|
+
class ApiController {
|
|
157
|
+
@Track("endpoint")
|
|
158
|
+
getUsers() {
|
|
159
|
+
// ...
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
When applied to a class, the `key` and `descriptor` arguments are `undefined`. When applied to a method, all three arguments are provided.
|
|
165
|
+
|
|
166
|
+
## Next steps
|
|
167
|
+
|
|
168
|
+
- [Metadata](./4.metadata.md) - Reflection-based metadata with `GetMetadata`
|
|
169
|
+
- [Proxies](./2.proxies.md) - Module-aware proxy classes
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Metadata
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The `GetMetadata` function provides a reflection-based metadata system built on top of the `reflect-metadata` library. It retrieves or creates metadata instances associated with target objects, supporting inheritance through the prototype chain.
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { GetMetadata } from "@antelopejs/interface-core";
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## `GetMetadata`
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
function GetMetadata<T, U>(target: U, meta: Class<T, [U]> & { key: symbol }, inherit?: boolean): T
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Parameters
|
|
20
|
+
|
|
21
|
+
| Parameter | Type | Default | Description |
|
|
22
|
+
| --------- | -------------------------------- | ------- | ---------------------------------------------------- |
|
|
23
|
+
| `target` | `U` | - | The object to retrieve or create metadata for |
|
|
24
|
+
| `meta` | `Class<T, [U]> & { key: symbol }` | - | A metadata class with a static `key` symbol |
|
|
25
|
+
| `inherit` | `boolean` | `true` | Whether to inherit metadata from the prototype chain |
|
|
26
|
+
|
|
27
|
+
### Return value
|
|
28
|
+
|
|
29
|
+
Returns the metadata instance of type `T` associated with the target.
|
|
30
|
+
|
|
31
|
+
## Define a metadata class
|
|
32
|
+
|
|
33
|
+
A metadata class must have a static `key` property (a `Symbol`) and accept the target object as a constructor argument.
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
class RouteMetadata {
|
|
37
|
+
static key = Symbol("RouteMetadata");
|
|
38
|
+
|
|
39
|
+
public routes: Map<string, string> = new Map();
|
|
40
|
+
|
|
41
|
+
constructor(_target: any) {
|
|
42
|
+
// Initialize metadata for the target
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Retrieve metadata
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { GetMetadata } from "@antelopejs/interface-core";
|
|
51
|
+
|
|
52
|
+
class UserController {
|
|
53
|
+
getUser() {}
|
|
54
|
+
listUsers() {}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const meta = GetMetadata(UserController.prototype, RouteMetadata);
|
|
58
|
+
meta.routes.set("getUser", "/users/:id");
|
|
59
|
+
meta.routes.set("listUsers", "/users");
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Calling `GetMetadata` multiple times with the same target and metadata class returns the same instance. The metadata is stored on the target using `Reflect.defineMetadata`.
|
|
63
|
+
|
|
64
|
+
## Inheritance
|
|
65
|
+
|
|
66
|
+
When `inherit` is `true` (the default), `GetMetadata` walks the prototype chain to find parent metadata. If the metadata class defines an `inherit` method, that method is called with the parent metadata. Otherwise, properties from the parent are copied to the child metadata where they do not already exist.
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
class ControllerMeta {
|
|
70
|
+
static key = Symbol("ControllerMeta");
|
|
71
|
+
|
|
72
|
+
public middleware: string[] = [];
|
|
73
|
+
|
|
74
|
+
constructor(_target: any) {}
|
|
75
|
+
|
|
76
|
+
inherit(parent: ControllerMeta) {
|
|
77
|
+
this.middleware = [...parent.middleware];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
class BaseController {}
|
|
82
|
+
const baseMeta = GetMetadata(BaseController.prototype, ControllerMeta);
|
|
83
|
+
baseMeta.middleware.push("auth");
|
|
84
|
+
|
|
85
|
+
class AdminController extends BaseController {}
|
|
86
|
+
const adminMeta = GetMetadata(AdminController.prototype, ControllerMeta);
|
|
87
|
+
// adminMeta.middleware contains ["auth"] (inherited from BaseController)
|
|
88
|
+
|
|
89
|
+
adminMeta.middleware.push("adminOnly");
|
|
90
|
+
// adminMeta.middleware is now ["auth", "adminOnly"]
|
|
91
|
+
// baseMeta.middleware remains ["auth"]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Without a custom `inherit` method, properties are shallow-copied from parent to child using `Object.getOwnPropertyNames`, but only for keys that do not already exist on the child metadata instance.
|
|
95
|
+
|
|
96
|
+
## Disable inheritance
|
|
97
|
+
|
|
98
|
+
Pass `false` as the third argument to prevent prototype chain traversal:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
const meta = GetMetadata(target, RouteMetadata, false);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Next steps
|
|
105
|
+
|
|
106
|
+
- [Decorators](./3.decorators.md) - Combine metadata with decorator factories
|
|
107
|
+
- [Modules](./5.modules.md) - Module lifecycle events and management
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Modules
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The `@antelopejs/interface-core/modules` module provides lifecycle events and management functions for AntelopeJS modules. Modules transition through a defined lifecycle, and each transition emits an event that other modules can observe.
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import {
|
|
11
|
+
Events,
|
|
12
|
+
ListModules,
|
|
13
|
+
GetModuleInfo,
|
|
14
|
+
LoadModule,
|
|
15
|
+
StartModule,
|
|
16
|
+
StopModule,
|
|
17
|
+
DestroyModule,
|
|
18
|
+
ReloadModule,
|
|
19
|
+
} from "@antelopejs/interface-core/modules";
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Module lifecycle
|
|
23
|
+
|
|
24
|
+
A module moves through these states:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
loaded -> constructed -> active -> constructed -> loaded
|
|
28
|
+
(stopped) (destroyed)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
| State | Description |
|
|
32
|
+
| ------------- | ------------------------------------------------------------ |
|
|
33
|
+
| `loaded` | Module code is loaded but no instance exists |
|
|
34
|
+
| `constructed` | Module instance is created but not started |
|
|
35
|
+
| `active` | Module is fully started and providing services |
|
|
36
|
+
| `unknown` | Module status cannot be determined |
|
|
37
|
+
|
|
38
|
+
## Lifecycle events
|
|
39
|
+
|
|
40
|
+
The `Events` namespace exposes four `EventProxy` instances that fire during module lifecycle transitions.
|
|
41
|
+
|
|
42
|
+
### `Events.ModuleConstructed`
|
|
43
|
+
|
|
44
|
+
Fires after a module instance is created, before the module is started.
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { Events } from "@antelopejs/interface-core/modules";
|
|
48
|
+
|
|
49
|
+
Events.ModuleConstructed.register((moduleId: string) => {
|
|
50
|
+
console.log(`Module constructed: ${moduleId}`);
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### `Events.ModuleStarted`
|
|
55
|
+
|
|
56
|
+
Fires after a module has been started and is fully operational.
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
Events.ModuleStarted.register((moduleId: string) => {
|
|
60
|
+
console.log(`Module started: ${moduleId}`);
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### `Events.ModuleStopped`
|
|
65
|
+
|
|
66
|
+
Fires after a module has been stopped. The module instance still exists but is no longer active.
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
Events.ModuleStopped.register((moduleId: string) => {
|
|
70
|
+
console.log(`Module stopped: ${moduleId}`);
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### `Events.ModuleDestroyed`
|
|
75
|
+
|
|
76
|
+
Fires after a module instance has been destroyed and all its resources have been released. The system uses this event internally to clean up proxy attachments and event handlers associated with the destroyed module.
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
Events.ModuleDestroyed.register((moduleId: string) => {
|
|
80
|
+
console.log(`Module destroyed: ${moduleId}`);
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Management functions
|
|
85
|
+
|
|
86
|
+
These functions are declared as `InterfaceFunction` proxies. They are available once the core runtime provides their implementation.
|
|
87
|
+
|
|
88
|
+
### `ListModules`
|
|
89
|
+
|
|
90
|
+
Returns the identifiers of all loaded modules.
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
const modules = await ListModules();
|
|
94
|
+
// ["auth-module", "database-module", "api-module"]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### `GetModuleInfo`
|
|
98
|
+
|
|
99
|
+
Returns detailed information about a specific module, including its configuration, status, and file system path.
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import type { ModuleInfo } from "@antelopejs/interface-core/modules";
|
|
103
|
+
|
|
104
|
+
const info: ModuleInfo = await GetModuleInfo("auth-module");
|
|
105
|
+
// info.status -> "active"
|
|
106
|
+
// info.localPath -> "/path/to/auth-module"
|
|
107
|
+
// info.source -> { type: "package", ... }
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### `LoadModule`
|
|
111
|
+
|
|
112
|
+
Loads a new module with the given configuration. Set `autostart` to `true` to automatically start the module after loading.
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import type { ModuleDefinition } from "@antelopejs/interface-core/modules";
|
|
116
|
+
|
|
117
|
+
const definition: ModuleDefinition = {
|
|
118
|
+
source: { type: "package", package: "@my/module", version: "1.0.0" },
|
|
119
|
+
config: { key: "value" },
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
await LoadModule("my-module", definition, true);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### `StartModule`
|
|
126
|
+
|
|
127
|
+
Starts a loaded but inactive module.
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
await StartModule("my-module");
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### `StopModule`
|
|
134
|
+
|
|
135
|
+
Stops an active module. The module instance remains but stops providing services.
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
await StopModule("my-module");
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### `DestroyModule`
|
|
142
|
+
|
|
143
|
+
Destroys a stopped module instance. The module code remains loaded.
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
await DestroyModule("my-module");
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### `ReloadModule`
|
|
150
|
+
|
|
151
|
+
Stops, destroys, unloads, and reloads a module from its source. This is useful for applying updates without restarting the application.
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
await ReloadModule("my-module");
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## `ModuleDefinition`
|
|
158
|
+
|
|
159
|
+
The configuration object for defining a module.
|
|
160
|
+
|
|
161
|
+
| Property | Type | Description |
|
|
162
|
+
| ------------------ | --------------------------------- | -------------------------------------------------- |
|
|
163
|
+
| `source` | `{ type: string } & Record<...>` | Source location and loading mechanism |
|
|
164
|
+
| `config` | `unknown` | Optional configuration data for the module |
|
|
165
|
+
| `importOverrides` | `Record<string, string[]>` | Optional mapping of import paths to alternatives |
|
|
166
|
+
| `disabledExports` | `string[]` | Optional list of exports to hide from this module |
|
|
167
|
+
|
|
168
|
+
## `ModuleInfo`
|
|
169
|
+
|
|
170
|
+
Extends `ModuleDefinition` with runtime information.
|
|
171
|
+
|
|
172
|
+
| Property | Type | Description |
|
|
173
|
+
| ----------- | -------- | ---------------------------------------- |
|
|
174
|
+
| `status` | `string` | Current lifecycle state of the module |
|
|
175
|
+
| `localPath` | `string` | File system path where the module exists |
|
|
176
|
+
|
|
177
|
+
## Next steps
|
|
178
|
+
|
|
179
|
+
- [Logging](./6.logging.md) - Structured logging with channels and levels
|
|
180
|
+
- [Configuration](./7.configuration.md) - Project configuration types
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Logging
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The `@antelopejs/interface-core/logging` module provides a structured logging system with multiple severity levels and named channels. Log entries are emitted as events through an `EventProxy`, allowing any part of the application to listen for and process log output.
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Logging } from "@antelopejs/interface-core/logging";
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Severity levels
|
|
14
|
+
|
|
15
|
+
The `Logging.Level` enum defines the available severity levels:
|
|
16
|
+
|
|
17
|
+
| Level | Value | Purpose |
|
|
18
|
+
| ----------- | ----- | -------------------------------------------- |
|
|
19
|
+
| `ERROR` | 40 | Critical errors that may cause failure |
|
|
20
|
+
| `WARN` | 30 | Issues that do not prevent operation |
|
|
21
|
+
| `INFO` | 20 | General status updates and information |
|
|
22
|
+
| `DEBUG` | 10 | Detailed information for debugging |
|
|
23
|
+
| `TRACE` | 0 | Highly detailed tracing information |
|
|
24
|
+
| `NO_PREFIX` | -1 | Messages displayed without a level prefix |
|
|
25
|
+
|
|
26
|
+
## Log to the main channel
|
|
27
|
+
|
|
28
|
+
The `Logging` namespace exposes convenience functions that write to the `"main"` channel:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { Logging } from "@antelopejs/interface-core/logging";
|
|
32
|
+
|
|
33
|
+
Logging.Error("Database connection failed", error);
|
|
34
|
+
Logging.Warn("Cache miss for key:", cacheKey);
|
|
35
|
+
Logging.Info("Server started on port", port);
|
|
36
|
+
Logging.Debug("Request payload:", payload);
|
|
37
|
+
Logging.Trace("Entering function processItem");
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Each function accepts any number of arguments of any type.
|
|
41
|
+
|
|
42
|
+
## Use named channels
|
|
43
|
+
|
|
44
|
+
For categorized logging, create a `Logging.Channel` instance with a channel name:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { Logging } from "@antelopejs/interface-core/logging";
|
|
48
|
+
|
|
49
|
+
const dbLog = new Logging.Channel("database");
|
|
50
|
+
const httpLog = new Logging.Channel("http");
|
|
51
|
+
|
|
52
|
+
dbLog.Info("Connected to", dbHost);
|
|
53
|
+
dbLog.Error("Query failed:", query, error);
|
|
54
|
+
|
|
55
|
+
httpLog.Info("GET /api/users", statusCode);
|
|
56
|
+
httpLog.Debug("Response headers:", headers);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Channels provide the same methods as the main logging functions: `Error`, `Warn`, `Info`, `Debug`, and `Trace`.
|
|
60
|
+
|
|
61
|
+
## Write at a custom level
|
|
62
|
+
|
|
63
|
+
Use the `Write` function for logs at a custom severity level:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
// Static function on the Logging namespace
|
|
67
|
+
Logging.Write(25, "custom-channel", "Custom level message");
|
|
68
|
+
|
|
69
|
+
// Instance method on a channel
|
|
70
|
+
const channel = new Logging.Channel("metrics");
|
|
71
|
+
channel.Write(15, "Custom level within the metrics channel");
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Log entry structure
|
|
75
|
+
|
|
76
|
+
Each log entry emitted through the event system has the following structure:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
interface Log {
|
|
80
|
+
time: number; // Timestamp in milliseconds since epoch
|
|
81
|
+
channel: string; // Channel name (e.g., "main", "database")
|
|
82
|
+
levelId: number; // Numeric severity level
|
|
83
|
+
args: any[]; // The logged values
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Listen for log events
|
|
88
|
+
|
|
89
|
+
The logging system uses an `EventProxy` as its transport. Import the listener to register custom log handlers:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import eventLog from "@antelopejs/interface-core/logging/listener";
|
|
93
|
+
|
|
94
|
+
eventLog.register((log) => {
|
|
95
|
+
const date = new Date(log.time).toISOString();
|
|
96
|
+
const level = log.levelId >= 40 ? "ERROR" : log.levelId >= 30 ? "WARN" : "INFO";
|
|
97
|
+
console.log(`[${date}] [${level}] [${log.channel}]`, ...log.args);
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The listener is module-aware. Handlers registered by a module are automatically removed when that module is unloaded.
|
|
102
|
+
|
|
103
|
+
## Next steps
|
|
104
|
+
|
|
105
|
+
- [Configuration](./7.configuration.md) - Project configuration with logging settings
|
|
106
|
+
- [Proxies](./2.proxies.md) - Understand the `EventProxy` that powers the logging transport
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The `@antelopejs/interface-core/config` module exports TypeScript types and the `defineConfig` helper for defining AntelopeJS project configurations. These types describe module sources, logging settings, environment overrides, and test configurations.
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { defineConfig } from "@antelopejs/interface-core/config";
|
|
11
|
+
import type {
|
|
12
|
+
AntelopeConfig,
|
|
13
|
+
AntelopeModuleConfig,
|
|
14
|
+
AntelopeLogging,
|
|
15
|
+
ModuleSource,
|
|
16
|
+
ModuleSourceLocal,
|
|
17
|
+
ModuleSourceGit,
|
|
18
|
+
ModuleSourcePackage,
|
|
19
|
+
ModuleSourceLocalFolder,
|
|
20
|
+
} from "@antelopejs/interface-core/config";
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## `defineConfig`
|
|
24
|
+
|
|
25
|
+
The `defineConfig` function provides type-safe configuration definition. It accepts either a static configuration object or a function that receives a context and returns a configuration.
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { defineConfig } from "@antelopejs/interface-core/config";
|
|
29
|
+
|
|
30
|
+
// Static configuration
|
|
31
|
+
export default defineConfig({
|
|
32
|
+
name: "my-project",
|
|
33
|
+
modules: {
|
|
34
|
+
database: "@antelopejs/database",
|
|
35
|
+
auth: {
|
|
36
|
+
version: "1.0.0",
|
|
37
|
+
source: { type: "package", package: "@antelopejs/auth", version: "1.0.0" },
|
|
38
|
+
config: { secret: "my-secret" },
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
// Dynamic configuration based on environment
|
|
46
|
+
export default defineConfig((ctx) => {
|
|
47
|
+
return {
|
|
48
|
+
name: "my-project",
|
|
49
|
+
modules: {
|
|
50
|
+
database: {
|
|
51
|
+
source: { type: "package", package: "@antelopejs/database", version: "1.0.0" },
|
|
52
|
+
config: {
|
|
53
|
+
host: ctx.env === "production" ? "db.prod.internal" : "localhost",
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## `AntelopeConfig`
|
|
62
|
+
|
|
63
|
+
The root configuration object for an AntelopeJS project.
|
|
64
|
+
|
|
65
|
+
| Property | Type | Description |
|
|
66
|
+
| ---------------- | ----------------------------------------------- | ------------------------------------------- |
|
|
67
|
+
| `name` | `string` | Project name |
|
|
68
|
+
| `cacheFolder` | `string` | Optional custom cache directory |
|
|
69
|
+
| `modules` | `Record<string, string \| AntelopeModuleConfig>` | Module definitions (shorthand or full) |
|
|
70
|
+
| `logging` | `AntelopeLogging` | Optional logging configuration |
|
|
71
|
+
| `envOverrides` | `Record<string, string \| string[]>` | Optional environment variable overrides |
|
|
72
|
+
| `environments` | `Record<string, Partial<AntelopeConfig>>` | Optional per-environment config overrides |
|
|
73
|
+
| `test` | `AntelopeTestConfig` | Optional test configuration |
|
|
74
|
+
|
|
75
|
+
## Module sources
|
|
76
|
+
|
|
77
|
+
Each module source type specifies how to locate and load the module code.
|
|
78
|
+
|
|
79
|
+
### Local source
|
|
80
|
+
|
|
81
|
+
Load a module from a local file path:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
const config: AntelopeModuleConfig = {
|
|
85
|
+
source: {
|
|
86
|
+
type: "local",
|
|
87
|
+
path: "./modules/my-module",
|
|
88
|
+
main: "index.ts",
|
|
89
|
+
watchDir: "src",
|
|
90
|
+
installCommand: "pnpm install",
|
|
91
|
+
reloadCommand: "pnpm build",
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The optional `reloadCommand` runs instead of `installCommand` on hot reload, letting you configure a faster command chain (for example, skipping `pnpm install`).
|
|
97
|
+
|
|
98
|
+
### Git source
|
|
99
|
+
|
|
100
|
+
Load a module from a Git repository:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
const config: AntelopeModuleConfig = {
|
|
104
|
+
source: {
|
|
105
|
+
type: "git",
|
|
106
|
+
remote: "https://github.com/org/module.git",
|
|
107
|
+
branch: "main",
|
|
108
|
+
installCommand: "pnpm install",
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Package source
|
|
114
|
+
|
|
115
|
+
Load a module from an npm package:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
const config: AntelopeModuleConfig = {
|
|
119
|
+
source: {
|
|
120
|
+
type: "package",
|
|
121
|
+
package: "@antelopejs/database",
|
|
122
|
+
version: "1.0.0",
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Local folder source
|
|
128
|
+
|
|
129
|
+
Load a module from a local folder with optional file watching:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
const config: AntelopeModuleConfig = {
|
|
133
|
+
source: {
|
|
134
|
+
type: "local-folder",
|
|
135
|
+
path: "./packages/shared",
|
|
136
|
+
watchDir: ["src", "lib"],
|
|
137
|
+
installCommand: ["pnpm install", "pnpm build"],
|
|
138
|
+
reloadCommand: "pnpm build",
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Like the local source, `local-folder` also accepts an optional `reloadCommand` that runs in place of `installCommand` on hot reload.
|
|
144
|
+
|
|
145
|
+
## `AntelopeModuleConfig`
|
|
146
|
+
|
|
147
|
+
Full module configuration within the project config.
|
|
148
|
+
|
|
149
|
+
| Property | Type | Description |
|
|
150
|
+
| ----------------- | ------------------------------------------------- | ---------------------------------------- |
|
|
151
|
+
| `version` | `string` | Optional version constraint |
|
|
152
|
+
| `source` | `ModuleSource*` | Source definition (local, git, package, local-folder) |
|
|
153
|
+
| `config` | `unknown` | Optional runtime configuration |
|
|
154
|
+
| `importOverrides` | `ImportOverride[] \| Record<string, string>` | Optional import path redirections |
|
|
155
|
+
| `disabledExports` | `string[]` | Optional list of exports to disable |
|
|
156
|
+
|
|
157
|
+
## `AntelopeLogging`
|
|
158
|
+
|
|
159
|
+
Logging configuration within the project config.
|
|
160
|
+
|
|
161
|
+
| Property | Type | Description |
|
|
162
|
+
| ----------------- | --------------------------------- | ------------------------------------------ |
|
|
163
|
+
| `enabled` | `boolean` | Enable or disable logging |
|
|
164
|
+
| `moduleTracking` | `object` | Track module-level logging with includes/excludes |
|
|
165
|
+
| `channelFilter` | `Record<string, number \| string>` | Filter log output by channel and level |
|
|
166
|
+
| `formatter` | `Record<string, string>` | Custom formatters per channel |
|
|
167
|
+
| `dateFormat` | `string` | Date format string for log timestamps |
|
|
168
|
+
|
|
169
|
+
## `AntelopeTestConfig`
|
|
170
|
+
|
|
171
|
+
Test configuration for the project.
|
|
172
|
+
|
|
173
|
+
| Property | Type | Description |
|
|
174
|
+
| --------- | ------------ | --------------------------------------------------- |
|
|
175
|
+
| `folder` | `string` | Optional test folder path |
|
|
176
|
+
| `setup` | `Function` | Optional async setup function returning partial config |
|
|
177
|
+
| `cleanup` | `Function` | Optional async cleanup function |
|
|
178
|
+
|
|
179
|
+
## Next steps
|
|
180
|
+
|
|
181
|
+
- [Introduction](./1.introduction.md) - Return to the overview
|
|
182
|
+
- [Modules](./5.modules.md) - Module lifecycle management
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Runtime
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The `@antelopejs/interface-core/runtime` module exposes information about the runtime environment of the running project and a registry for development servers. The implementation is provided by the AntelopeJS core: `ajs project dev` reports development mode, while `ajs project start` and `ajs project run` report production mode.
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import {
|
|
11
|
+
GetRuntimeInfo,
|
|
12
|
+
RegisterDevServer,
|
|
13
|
+
DEV_REGISTRY_PATH,
|
|
14
|
+
} from "@antelopejs/interface-core/runtime";
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## `GetRuntimeInfo`
|
|
18
|
+
|
|
19
|
+
Retrieves information about the runtime environment of the running project.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
const info = await GetRuntimeInfo();
|
|
23
|
+
// { dev: true, projectPath: "/path/to/project", env: "default" }
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
| Field | Description |
|
|
27
|
+
| ------------- | ----------------------------------------------------------------- |
|
|
28
|
+
| `dev` | `true` when running under `ajs project dev`, `false` otherwise |
|
|
29
|
+
| `projectPath` | Absolute path to the root of the running project |
|
|
30
|
+
| `env` | Name of the active configuration environment |
|
|
31
|
+
|
|
32
|
+
## `RegisterDevServer`
|
|
33
|
+
|
|
34
|
+
Registers a development server and the endpoints it is listening on. Modules that bind network ports (such as an HTTP API) call this after a successful `listen()` so external tooling can discover the actual endpoints.
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
await RegisterDevServer("api", [
|
|
38
|
+
{ protocol: "http", host: "localhost", port: 5011 },
|
|
39
|
+
]);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
In development mode, the core merges the registration into the dev registry file and removes the file on shutdown. Outside development mode, the call is a no-op.
|
|
43
|
+
|
|
44
|
+
## Dev registry file
|
|
45
|
+
|
|
46
|
+
The dev registry file lives at `DEV_REGISTRY_PATH` (`.antelope/dev.json`) relative to the project root. Its shape is described by the `DevServerRegistry` type:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"pid": 12345,
|
|
51
|
+
"startedAt": "2026-06-12T10:00:00Z",
|
|
52
|
+
"servers": {
|
|
53
|
+
"api": {
|
|
54
|
+
"endpoints": [{ "protocol": "http", "host": "localhost", "port": 5011 }]
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The file is only valid while the process identified by `pid` exists. If that process is gone, the file is orphaned and must be ignored or overwritten.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antelopejs/interface-core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "AntelopeJS core interface primitives - proxies, decorators, interface functions, and logging",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"antelopejs",
|
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
"main": "dist/index.js",
|
|
17
17
|
"types": "dist/index.d.ts",
|
|
18
18
|
"files": [
|
|
19
|
-
"dist"
|
|
19
|
+
"dist",
|
|
20
|
+
"docs",
|
|
21
|
+
"skills"
|
|
20
22
|
],
|
|
21
23
|
"exports": {
|
|
22
24
|
".": {
|
|
@@ -79,7 +81,10 @@
|
|
|
79
81
|
"test": "pnpm run build && ajs module test ."
|
|
80
82
|
},
|
|
81
83
|
"antelopeJs": {
|
|
82
|
-
"test": "src/antelope.test.ts"
|
|
84
|
+
"test": "src/antelope.test.ts",
|
|
85
|
+
"skills": [
|
|
86
|
+
"./skills"
|
|
87
|
+
]
|
|
83
88
|
},
|
|
84
89
|
"dependencies": {
|
|
85
90
|
"reflect-metadata": "^0.2.2"
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: core-interface
|
|
3
|
+
description: Provides the AntelopeJS core interface primitives - AsyncProxy/EventProxy/RegisteringProxy, InterfaceFunction, ImplementInterface, GetMetadata, decorator factories, module lifecycle events, structured Logging, and defineConfig. Use when importing from "@antelopejs/interface-core" (or its /decorators, /modules, /proxies, /runtime, /config, /logging subpaths), when declaring or implementing an AntelopeJS interface, wiring cross-module calls or events, building custom decorators, subscribing to ModuleStarted/ModuleDestroyed, writing logs via Logging.Info/Error, or authoring antelope.config with defineConfig.
|
|
4
|
+
category: antelopejs-interface
|
|
5
|
+
tags: [antelopejs, interface, proxy, decorators, logging]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# @antelopejs/interface-core
|
|
9
|
+
|
|
10
|
+
Foundational primitives of the AntelopeJS interface system. An interface is a plain module of proxy
|
|
11
|
+
objects (the declaration); a provider attaches concrete behavior with `ImplementInterface`; consumers
|
|
12
|
+
call the declaration directly. All proxy calls cross module boundaries: they queue while no
|
|
13
|
+
implementation is attached and auto-detach when the providing module is unloaded. Decorator
|
|
14
|
+
factories, `GetMetadata`, and config types are ordinary consumer-side helpers (no proxy crossing).
|
|
15
|
+
|
|
16
|
+
## Import paths
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { InterfaceFunction, ImplementInterface, GetMetadata, GetInterfaceInstances, GetInterfaceInstance, AsyncProxy, EventProxy, RegisteringProxy, GetResponsibleModule } from "@antelopejs/interface-core";
|
|
20
|
+
import { MakeClassDecorator, MakeMethodDecorator, MakePropertyDecorator, MakeParameterDecorator } from "@antelopejs/interface-core/decorators"; // + many combined variants
|
|
21
|
+
import { Events, ListModules, GetModuleInfo, LoadModule, StartModule, StopModule, DestroyModule, ReloadModule } from "@antelopejs/interface-core/modules";
|
|
22
|
+
import { AsyncProxy, EventProxy, RegisteringProxy } from "@antelopejs/interface-core/proxies"; // same classes the root entry re-exports; pick one import site
|
|
23
|
+
import { GetRuntimeInfo, RegisterDevServer, DEV_REGISTRY_PATH } from "@antelopejs/interface-core/runtime";
|
|
24
|
+
import { defineConfig, type AntelopeConfig, type AntelopeModuleConfig } from "@antelopejs/interface-core/config";
|
|
25
|
+
import { Logging } from "@antelopejs/interface-core/logging";
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Declaring and implementing an interface
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
// declaration.ts - the interface contract
|
|
32
|
+
import { InterfaceFunction, EventProxy } from "@antelopejs/interface-core";
|
|
33
|
+
|
|
34
|
+
export const GetUser = InterfaceFunction<(id: string) => { name: string }>();
|
|
35
|
+
export const OnUserCreated = new EventProxy<(userId: string) => void>();
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
// provider module - attach the implementation
|
|
40
|
+
import { ImplementInterface } from "@antelopejs/interface-core";
|
|
41
|
+
import * as UserInterface from "./declaration";
|
|
42
|
+
|
|
43
|
+
ImplementInterface(UserInterface, {
|
|
44
|
+
GetUser(id) {
|
|
45
|
+
return { name: "Alice" };
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
// consumer - call the declaration; always returns a Promise
|
|
52
|
+
import { Logging } from "@antelopejs/interface-core/logging";
|
|
53
|
+
import { GetUser, OnUserCreated } from "./declaration";
|
|
54
|
+
|
|
55
|
+
const user = await GetUser("42");
|
|
56
|
+
OnUserCreated.register((userId) => Logging.Info("created", userId));
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
For a `RegisteringProxy` field, the implementation entry is an object:
|
|
60
|
+
`{ register: (id, ...args) => void, unregister: (id) => void }`. `EventProxy` fields are never
|
|
61
|
+
implemented; providers `emit` on them, consumers `register`/`unregister` handlers.
|
|
62
|
+
|
|
63
|
+
## Logging
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { Logging } from "@antelopejs/interface-core/logging";
|
|
67
|
+
|
|
68
|
+
Logging.Info("hello"); // main channel; also Error/Warn/Debug/Trace
|
|
69
|
+
const ch = new Logging.Channel("database"); // named channel
|
|
70
|
+
ch.Debug("query", { ms: 12 });
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Gotchas
|
|
74
|
+
|
|
75
|
+
- `InterfaceFunction` calls always return a `Promise`, even for sync implementations. Unimplemented
|
|
76
|
+
calls queue indefinitely (no timeout); an `await` before any provider attaches simply waits. In
|
|
77
|
+
`ajs module test` stub mode they reject instead with "Interface function called without
|
|
78
|
+
implementation in test environment. Ensure the required module is loaded in your test config.".
|
|
79
|
+
- Prefer the synchronous `ImplementInterface(decl, impl)`; the Promise-accepting overload is
|
|
80
|
+
deprecated. Implementations may be partial - unmatched keys keep queuing.
|
|
81
|
+
- Module attribution (`GetResponsibleModule`) works by call-stack analysis. Attach implementations
|
|
82
|
+
and register handlers synchronously during module load; doing it from a setTimeout/setInterval
|
|
83
|
+
callback logs "this will break hot reloading!", and other async contexts break attribution
|
|
84
|
+
silently - both defeat automatic cleanup on module unload.
|
|
85
|
+
- `RegisteringProxy` replays existing registrations to a newly attached provider (hot reload safe);
|
|
86
|
+
`EventProxy.register` deduplicates by function identity; both auto-clean handlers of destroyed
|
|
87
|
+
modules via `Events.ModuleDestroyed`.
|
|
88
|
+
- `GetMetadata` needs a metadata class with a static `key: symbol`; it inherits from the prototype
|
|
89
|
+
chain by default (pass `inherit = false` to disable). `reflect-metadata` is loaded by the main
|
|
90
|
+
entry point.
|
|
91
|
+
- Decorator factory callbacks split their arguments: decorator targets first (class / target+key /
|
|
92
|
+
target+key+descriptor), then the factory's own parameters.
|
|
93
|
+
|
|
94
|
+
## Deeper reference
|
|
95
|
+
|
|
96
|
+
See the shipped `.d.ts` files under `dist/` for exact signatures, and this package's `docs/`
|
|
97
|
+
chapters — Introduction, Proxies, Decorators, Metadata, Modules, Logging, Configuration,
|
|
98
|
+
Runtime — for the prose guides. Do not duplicate them here.
|