@celerity-sdk/core 0.2.1 → 0.3.1
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/README.md +30 -3
- package/dist/index.cjs +90 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -3
- package/dist/index.d.ts +33 -3
- package/dist/index.js +89 -8
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @celerity-sdk/core
|
|
2
2
|
|
|
3
|
-
Core SDK for building Celerity applications
|
|
3
|
+
Core SDK for building Celerity applications - decorators, dependency injection, layers, guards, handler adapters, and the application factory.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -101,12 +101,27 @@ class OrdersHandler {
|
|
|
101
101
|
import { CelerityFactory } from "@celerity-sdk/core";
|
|
102
102
|
|
|
103
103
|
const app = await CelerityFactory.create(AppModule);
|
|
104
|
-
// Auto-detects platform from
|
|
104
|
+
// Auto-detects platform from CELERITY_PLATFORM env var
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
+
## Handler Resolution
|
|
108
|
+
|
|
109
|
+
When a handler is invoked by ID (e.g. from a blueprint's `spec.handler` field), the SDK resolves it using a multi-step strategy:
|
|
110
|
+
|
|
111
|
+
1. **Direct registry ID match** - looks up the handler by its explicit `id` (set via decorator or `createHttpHandler`).
|
|
112
|
+
2. **Module resolution fallback** - if the direct lookup fails, the ID is treated as a module reference and dynamically imported:
|
|
113
|
+
- `"handlers.hello"` - named export `hello` from module `handlers`
|
|
114
|
+
- `"handlers"` - default export from module `handlers`
|
|
115
|
+
- `"app.module"` - tries named export split first (`module: "app"`, `export: "module"`), then falls back to default export from module `app.module`
|
|
116
|
+
3. **Path/method routing** - if both ID-based lookups fail, falls back to matching the incoming request's HTTP method and path against the registry.
|
|
117
|
+
|
|
118
|
+
Module resolution matches the imported function against the registry by reference (`===`). Once matched, the handler ID is assigned and subsequent invocations use the direct lookup without repeated imports.
|
|
119
|
+
|
|
120
|
+
This is primarily relevant for blueprint-first function handlers where `spec.handler` references like `"handlers.hello"` map to exported functions that have no routing information in code.
|
|
121
|
+
|
|
107
122
|
## Guards
|
|
108
123
|
|
|
109
|
-
Guards are declarative
|
|
124
|
+
Guards are declarative. They annotate handlers with protection requirements but do not execute in the Node.js process. Guard enforcement happens at the Rust runtime layer (containers) or API Gateway (serverless).
|
|
110
125
|
|
|
111
126
|
## Testing
|
|
112
127
|
|
|
@@ -117,6 +132,18 @@ const app = new TestingApplication(AppModule);
|
|
|
117
132
|
const response = await app.handle(mockRequest({ method: "GET", path: "/orders/1" }));
|
|
118
133
|
```
|
|
119
134
|
|
|
135
|
+
## Advanced Exports
|
|
136
|
+
|
|
137
|
+
The following exports are available for adapter authors building custom serverless or runtime adapters:
|
|
138
|
+
|
|
139
|
+
| Export | Purpose |
|
|
140
|
+
|---|---|
|
|
141
|
+
| `resolveHandlerByModuleRef(id, registry, baseDir)` | Resolve a handler ID as a module reference via dynamic import |
|
|
142
|
+
| `executeHandlerPipeline(handler, request, options)` | Execute the full layer + handler pipeline |
|
|
143
|
+
| `HandlerRegistry` | Handler registry class with route and ID-based lookups |
|
|
144
|
+
| `bootstrapForRuntime(modulePath?, systemLayers?)` | Bootstrap for the Celerity runtime host |
|
|
145
|
+
| `mapRuntimeRequest` / `mapToRuntimeResponse` | Runtime request/response mappers |
|
|
146
|
+
|
|
120
147
|
## Part of the Celerity Framework
|
|
121
148
|
|
|
122
149
|
See [celerityframework.io](https://celerityframework.io) for full documentation.
|
package/dist/index.cjs
CHANGED
|
@@ -112,6 +112,7 @@ __export(index_exports, {
|
|
|
112
112
|
mapToRuntimeResponse: () => mapToRuntimeResponse,
|
|
113
113
|
mockRequest: () => mockRequest,
|
|
114
114
|
registerModuleGraph: () => registerModuleGraph,
|
|
115
|
+
resolveHandlerByModuleRef: () => resolveHandlerByModuleRef,
|
|
115
116
|
runLayerPipeline: () => runLayerPipeline,
|
|
116
117
|
startRuntime: () => startRuntime,
|
|
117
118
|
tokenToString: () => tokenToString,
|
|
@@ -1805,23 +1806,96 @@ function httpDelete(path, handlerOrOptions, maybeHandler) {
|
|
|
1805
1806
|
}
|
|
1806
1807
|
__name(httpDelete, "httpDelete");
|
|
1807
1808
|
|
|
1808
|
-
// src/
|
|
1809
|
+
// src/handlers/module-resolver.ts
|
|
1809
1810
|
var import_node_path = require("path");
|
|
1810
1811
|
var import_debug9 = __toESM(require("debug"), 1);
|
|
1811
|
-
var debug9 = (0, import_debug9.default)("celerity:core:
|
|
1812
|
+
var debug9 = (0, import_debug9.default)("celerity:core:module-resolver");
|
|
1813
|
+
async function resolveHandlerByModuleRef(handlerId, registry, baseDir) {
|
|
1814
|
+
const lastDot = handlerId.lastIndexOf(".");
|
|
1815
|
+
if (lastDot > 0) {
|
|
1816
|
+
const moduleName = handlerId.slice(0, lastDot);
|
|
1817
|
+
const exportName = handlerId.slice(lastDot + 1);
|
|
1818
|
+
const result = await tryResolveExport(baseDir, moduleName, exportName, handlerId, registry);
|
|
1819
|
+
if (result) return result;
|
|
1820
|
+
}
|
|
1821
|
+
return tryResolveExport(baseDir, handlerId, "default", handlerId, registry);
|
|
1822
|
+
}
|
|
1823
|
+
__name(resolveHandlerByModuleRef, "resolveHandlerByModuleRef");
|
|
1824
|
+
async function tryResolveExport(baseDir, moduleName, exportName, handlerId, registry) {
|
|
1825
|
+
const handlerModulePath = (0, import_node_path.resolve)(baseDir, moduleName);
|
|
1826
|
+
let mod;
|
|
1827
|
+
try {
|
|
1828
|
+
mod = await import(handlerModulePath);
|
|
1829
|
+
} catch {
|
|
1830
|
+
try {
|
|
1831
|
+
mod = await import(`${handlerModulePath}.js`);
|
|
1832
|
+
} catch {
|
|
1833
|
+
return null;
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
const exported = mod[exportName];
|
|
1837
|
+
if (!exported) return null;
|
|
1838
|
+
const isFnDef = typeof exported === "object" && exported !== null && exported.__celerity_handler;
|
|
1839
|
+
const handlerFn = isFnDef ? exported.handler : exported;
|
|
1840
|
+
if (typeof handlerFn !== "function") return null;
|
|
1841
|
+
const match = registry.getAllHandlers().find((h) => h.handlerFn === handlerFn);
|
|
1842
|
+
if (match) {
|
|
1843
|
+
match.id = handlerId;
|
|
1844
|
+
debug9("matched '%s' to registry handler", handlerId);
|
|
1845
|
+
return match;
|
|
1846
|
+
}
|
|
1847
|
+
debug9("'%s' not in registry, wrapping directly", handlerId);
|
|
1848
|
+
return buildResolvedFromExport(handlerId, handlerFn, isFnDef ? exported : null);
|
|
1849
|
+
}
|
|
1850
|
+
__name(tryResolveExport, "tryResolveExport");
|
|
1851
|
+
function buildResolvedFromExport(handlerId, handlerFn, fnDef) {
|
|
1852
|
+
if (fnDef) {
|
|
1853
|
+
const meta = fnDef.metadata;
|
|
1854
|
+
return {
|
|
1855
|
+
id: handlerId,
|
|
1856
|
+
protectedBy: [],
|
|
1857
|
+
layers: [
|
|
1858
|
+
...meta.layers ?? []
|
|
1859
|
+
],
|
|
1860
|
+
isPublic: false,
|
|
1861
|
+
paramMetadata: [],
|
|
1862
|
+
customMetadata: meta.customMetadata ?? {},
|
|
1863
|
+
handlerFn,
|
|
1864
|
+
isFunctionHandler: true,
|
|
1865
|
+
injectTokens: meta.inject ?? []
|
|
1866
|
+
};
|
|
1867
|
+
}
|
|
1868
|
+
return {
|
|
1869
|
+
id: handlerId,
|
|
1870
|
+
protectedBy: [],
|
|
1871
|
+
layers: [],
|
|
1872
|
+
isPublic: false,
|
|
1873
|
+
paramMetadata: [],
|
|
1874
|
+
customMetadata: {},
|
|
1875
|
+
handlerFn,
|
|
1876
|
+
isFunctionHandler: true,
|
|
1877
|
+
injectTokens: []
|
|
1878
|
+
};
|
|
1879
|
+
}
|
|
1880
|
+
__name(buildResolvedFromExport, "buildResolvedFromExport");
|
|
1881
|
+
|
|
1882
|
+
// src/bootstrap/discovery.ts
|
|
1883
|
+
var import_node_path2 = require("path");
|
|
1884
|
+
var import_debug10 = __toESM(require("debug"), 1);
|
|
1885
|
+
var debug10 = (0, import_debug10.default)("celerity:core:bootstrap");
|
|
1812
1886
|
async function discoverModule(modulePath) {
|
|
1813
1887
|
const resolved = modulePath ?? process.env.CELERITY_MODULE_PATH;
|
|
1814
1888
|
if (!resolved) {
|
|
1815
1889
|
throw new Error("Cannot discover module: set CELERITY_MODULE_PATH environment variable or pass modulePath");
|
|
1816
1890
|
}
|
|
1817
|
-
const absolutePath = (0,
|
|
1818
|
-
|
|
1891
|
+
const absolutePath = (0, import_node_path2.resolve)(resolved);
|
|
1892
|
+
debug10("discoverModule: importing %s", absolutePath);
|
|
1819
1893
|
const imported = await import(absolutePath);
|
|
1820
1894
|
const rootModule = imported.default ?? findModuleExport(imported);
|
|
1821
1895
|
if (!rootModule || typeof rootModule !== "function") {
|
|
1822
1896
|
throw new Error(`No module class found in "${resolved}"`);
|
|
1823
1897
|
}
|
|
1824
|
-
|
|
1898
|
+
debug10("discoverModule: found %s", rootModule.name);
|
|
1825
1899
|
return rootModule;
|
|
1826
1900
|
}
|
|
1827
1901
|
__name(discoverModule, "discoverModule");
|
|
@@ -1877,8 +1951,11 @@ function mapToRuntimeResponse(response) {
|
|
|
1877
1951
|
__name(mapToRuntimeResponse, "mapToRuntimeResponse");
|
|
1878
1952
|
|
|
1879
1953
|
// src/bootstrap/runtime-entry.ts
|
|
1954
|
+
var import_node_path3 = require("path");
|
|
1880
1955
|
async function bootstrapForRuntime(modulePath, systemLayers) {
|
|
1881
1956
|
const layers = systemLayers ?? await createDefaultSystemLayers();
|
|
1957
|
+
const resolvedModulePath = modulePath ?? process.env.CELERITY_MODULE_PATH;
|
|
1958
|
+
const moduleDir = resolvedModulePath ? (0, import_node_path3.dirname)((0, import_node_path3.resolve)(resolvedModulePath)) : process.cwd();
|
|
1882
1959
|
const rootModule = await discoverModule(modulePath);
|
|
1883
1960
|
const { container, registry } = await bootstrap(rootModule);
|
|
1884
1961
|
function buildCallback(handler) {
|
|
@@ -1899,8 +1976,12 @@ async function bootstrapForRuntime(modulePath, systemLayers) {
|
|
|
1899
1976
|
createRouteCallback(path, method) {
|
|
1900
1977
|
return buildCallback(registry.getHandler(path, method));
|
|
1901
1978
|
},
|
|
1902
|
-
createRouteCallbackById(handlerId) {
|
|
1903
|
-
|
|
1979
|
+
async createRouteCallbackById(handlerId, codeLocation) {
|
|
1980
|
+
const fromRegistry = registry.getHandlerById(handlerId);
|
|
1981
|
+
if (fromRegistry) return buildCallback(fromRegistry);
|
|
1982
|
+
const baseDir = codeLocation ? (0, import_node_path3.resolve)(codeLocation) : moduleDir;
|
|
1983
|
+
const resolved = await resolveHandlerByModuleRef(handlerId, registry, baseDir);
|
|
1984
|
+
return resolved ? buildCallback(resolved) : null;
|
|
1904
1985
|
}
|
|
1905
1986
|
};
|
|
1906
1987
|
}
|
|
@@ -1915,7 +1996,7 @@ async function startRuntime(options) {
|
|
|
1915
1996
|
const appConfig = app.setup();
|
|
1916
1997
|
const result = await bootstrapForRuntime();
|
|
1917
1998
|
for (const def of appConfig.api?.http?.handlers ?? []) {
|
|
1918
|
-
const callback = result.createRouteCallback(def.path, def.method) ?? result.createRouteCallbackById(def.handler);
|
|
1999
|
+
const callback = result.createRouteCallback(def.path, def.method) ?? await result.createRouteCallbackById(def.handler, def.location);
|
|
1919
2000
|
if (callback) {
|
|
1920
2001
|
app.registerHttpHandler(def.path, def.method, def.timeout, callback);
|
|
1921
2002
|
}
|
|
@@ -2006,6 +2087,7 @@ __name(startRuntime, "startRuntime");
|
|
|
2006
2087
|
mapToRuntimeResponse,
|
|
2007
2088
|
mockRequest,
|
|
2008
2089
|
registerModuleGraph,
|
|
2090
|
+
resolveHandlerByModuleRef,
|
|
2009
2091
|
runLayerPipeline,
|
|
2010
2092
|
startRuntime,
|
|
2011
2093
|
tokenToString,
|