@celerity-sdk/core 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -3
- package/dist/index.cjs +111 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -2
- package/dist/index.d.ts +37 -2
- package/dist/index.js +110 -17
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
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,
|
|
@@ -1171,6 +1172,11 @@ var HandlerRegistry = class {
|
|
|
1171
1172
|
debug4("getHandler %s %s \u2192 %s", method, path, found ? "matched" : "not found");
|
|
1172
1173
|
return found;
|
|
1173
1174
|
}
|
|
1175
|
+
getHandlerById(id) {
|
|
1176
|
+
const found = this.handlers.find((h) => h.id !== void 0 && h.id === id);
|
|
1177
|
+
debug4("getHandlerById %s \u2192 %s", id, found ? "matched" : "not found");
|
|
1178
|
+
return found;
|
|
1179
|
+
}
|
|
1174
1180
|
getAllHandlers() {
|
|
1175
1181
|
return [
|
|
1176
1182
|
...this.handlers
|
|
@@ -1256,8 +1262,9 @@ var HandlerRegistry = class {
|
|
|
1256
1262
|
layers.unshift(validate(schemas));
|
|
1257
1263
|
}
|
|
1258
1264
|
}
|
|
1259
|
-
debug4("registerFunctionHandler: %s", meta.method && meta.path ? `${meta.method} ${meta.path}` : "(no route)");
|
|
1265
|
+
debug4("registerFunctionHandler: %s", definition.id ?? (meta.method && meta.path ? `${meta.method} ${meta.path}` : "(no route)"));
|
|
1260
1266
|
this.handlers.push({
|
|
1267
|
+
id: definition.id,
|
|
1261
1268
|
path: meta.path,
|
|
1262
1269
|
method: meta.method,
|
|
1263
1270
|
protectedBy: [],
|
|
@@ -1799,23 +1806,96 @@ function httpDelete(path, handlerOrOptions, maybeHandler) {
|
|
|
1799
1806
|
}
|
|
1800
1807
|
__name(httpDelete, "httpDelete");
|
|
1801
1808
|
|
|
1802
|
-
// src/
|
|
1809
|
+
// src/handlers/module-resolver.ts
|
|
1803
1810
|
var import_node_path = require("path");
|
|
1804
1811
|
var import_debug9 = __toESM(require("debug"), 1);
|
|
1805
|
-
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");
|
|
1806
1886
|
async function discoverModule(modulePath) {
|
|
1807
1887
|
const resolved = modulePath ?? process.env.CELERITY_MODULE_PATH;
|
|
1808
1888
|
if (!resolved) {
|
|
1809
1889
|
throw new Error("Cannot discover module: set CELERITY_MODULE_PATH environment variable or pass modulePath");
|
|
1810
1890
|
}
|
|
1811
|
-
const absolutePath = (0,
|
|
1812
|
-
|
|
1891
|
+
const absolutePath = (0, import_node_path2.resolve)(resolved);
|
|
1892
|
+
debug10("discoverModule: importing %s", absolutePath);
|
|
1813
1893
|
const imported = await import(absolutePath);
|
|
1814
1894
|
const rootModule = imported.default ?? findModuleExport(imported);
|
|
1815
1895
|
if (!rootModule || typeof rootModule !== "function") {
|
|
1816
1896
|
throw new Error(`No module class found in "${resolved}"`);
|
|
1817
1897
|
}
|
|
1818
|
-
|
|
1898
|
+
debug10("discoverModule: found %s", rootModule.name);
|
|
1819
1899
|
return rootModule;
|
|
1820
1900
|
}
|
|
1821
1901
|
__name(discoverModule, "discoverModule");
|
|
@@ -1871,24 +1951,37 @@ function mapToRuntimeResponse(response) {
|
|
|
1871
1951
|
__name(mapToRuntimeResponse, "mapToRuntimeResponse");
|
|
1872
1952
|
|
|
1873
1953
|
// src/bootstrap/runtime-entry.ts
|
|
1954
|
+
var import_node_path3 = require("path");
|
|
1874
1955
|
async function bootstrapForRuntime(modulePath, systemLayers) {
|
|
1875
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();
|
|
1876
1959
|
const rootModule = await discoverModule(modulePath);
|
|
1877
1960
|
const { container, registry } = await bootstrap(rootModule);
|
|
1961
|
+
function buildCallback(handler) {
|
|
1962
|
+
if (!handler) return null;
|
|
1963
|
+
return async (_err, request) => {
|
|
1964
|
+
const httpRequest = mapRuntimeRequest(request);
|
|
1965
|
+
const httpResponse = await executeHandlerPipeline(handler, httpRequest, {
|
|
1966
|
+
container,
|
|
1967
|
+
systemLayers: layers
|
|
1968
|
+
});
|
|
1969
|
+
return mapToRuntimeResponse(httpResponse);
|
|
1970
|
+
};
|
|
1971
|
+
}
|
|
1972
|
+
__name(buildCallback, "buildCallback");
|
|
1878
1973
|
return {
|
|
1879
1974
|
registry,
|
|
1880
1975
|
container,
|
|
1881
1976
|
createRouteCallback(path, method) {
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
return mapToRuntimeResponse(httpResponse);
|
|
1891
|
-
};
|
|
1977
|
+
return buildCallback(registry.getHandler(path, method));
|
|
1978
|
+
},
|
|
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;
|
|
1892
1985
|
}
|
|
1893
1986
|
};
|
|
1894
1987
|
}
|
|
@@ -1903,7 +1996,7 @@ async function startRuntime(options) {
|
|
|
1903
1996
|
const appConfig = app.setup();
|
|
1904
1997
|
const result = await bootstrapForRuntime();
|
|
1905
1998
|
for (const def of appConfig.api?.http?.handlers ?? []) {
|
|
1906
|
-
const callback = result.createRouteCallback(def.path, def.method);
|
|
1999
|
+
const callback = result.createRouteCallback(def.path, def.method) ?? await result.createRouteCallbackById(def.handler, def.location);
|
|
1907
2000
|
if (callback) {
|
|
1908
2001
|
app.registerHttpHandler(def.path, def.method, def.timeout, callback);
|
|
1909
2002
|
}
|
|
@@ -1994,6 +2087,7 @@ __name(startRuntime, "startRuntime");
|
|
|
1994
2087
|
mapToRuntimeResponse,
|
|
1995
2088
|
mockRequest,
|
|
1996
2089
|
registerModuleGraph,
|
|
2090
|
+
resolveHandlerByModuleRef,
|
|
1997
2091
|
runLayerPipeline,
|
|
1998
2092
|
startRuntime,
|
|
1999
2093
|
tokenToString,
|