Compare commits
6 Commits
v0.2.0
...
32f3fe5f52
| Author | SHA1 | Date | |
|---|---|---|---|
| 32f3fe5f52 | |||
|
abd2d6e840
|
|||
|
927a9081d4
|
|||
|
8f94cc915c
|
|||
|
3f114bb68d
|
|||
|
0846dbb758
|
@@ -4,38 +4,44 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- '**'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
detect-version-change:
|
detect-version-change:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
version_changed: ${{ steps.check.outputs.version_changed }}
|
version_changed: ${{ steps.set.outputs.version_changed }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Check if VERSION file changed
|
- name: Check if VERSION file changed
|
||||||
id: check
|
if: github.ref == 'refs/heads/main'
|
||||||
run: |
|
run: |
|
||||||
echo "🔍 Vergleich mit github.event.before:"
|
echo "🔍 Vergleich mit github.event.before:"
|
||||||
echo "Before: ${{ github.event.before }}"
|
echo "Before: ${{ github.event.before }}"
|
||||||
echo "After: ${{ github.sha }}"
|
echo "After: ${{ github.sha }}"
|
||||||
|
|
||||||
echo "📄 Changed files between before and after:"
|
echo "📄 Changed files between before and after:"
|
||||||
git diff --name-only ${{ github.event.before }} ${{ github.sha }} || echo "(diff failed)"
|
git diff --name-only ${{ github.event.before }} ${{ github.sha }} || echo "(diff failed)"
|
||||||
|
|
||||||
if git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep -q '^VERSION$'; then
|
if git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep -q '^VERSION$'; then
|
||||||
echo "✅ VERSION file was changed between before and after"
|
echo "✅ VERSION file was changed"
|
||||||
echo "version_changed=true" >> $GITHUB_OUTPUT
|
echo "VERSION_CHANGED=true" >> $GITHUB_ENV
|
||||||
else
|
else
|
||||||
echo "ℹ️ VERSION file not changed between before and after"
|
echo "ℹ️ VERSION file not changed"
|
||||||
echo "version_changed=false" >> $GITHUB_OUTPUT
|
echo "VERSION_CHANGED=false" >> $GITHUB_ENV
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
- name: Set output (always)
|
||||||
|
id: set
|
||||||
|
run: |
|
||||||
|
echo "version_changed=${VERSION_CHANGED:-false}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
changelog-only:
|
changelog-only:
|
||||||
needs: detect-version-change
|
needs: detect-version-change
|
||||||
if: needs.detect-version-change.outputs.version_changed == 'false'
|
if: github.ref != 'refs/heads/main' || needs.detect-version-change.outputs.version_changed == 'false'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -75,12 +81,12 @@ jobs:
|
|||||||
echo "No changes to commit"
|
echo "No changes to commit"
|
||||||
else
|
else
|
||||||
git commit -m "chore(changelog): update unreleased changelog"
|
git commit -m "chore(changelog): update unreleased changelog"
|
||||||
git push origin main
|
git push origin "${GITHUB_REF##refs/heads/}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
release:
|
release:
|
||||||
needs: detect-version-change
|
needs: detect-version-change
|
||||||
if: needs.detect-version-change.outputs.version_changed == 'true'
|
if: needs.detect-version-change.outputs.version_changed == 'true' && github.ref == 'refs/heads/main'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|||||||
@@ -6,19 +6,27 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
### 🚀 Features
|
### 🚀 Features
|
||||||
|
|
||||||
|
- *(interfaces)* Add pipeline executor interface - ([927a908](https://git.0xmax42.io/maxp/http-kernel/commit/927a9081d4f363202520d017eb424c7c097ced94))
|
||||||
|
- *(pipeline)* Add configuration and hooks for pipeline execution - ([3f114bb](https://git.0xmax42.io/maxp/http-kernel/commit/3f114bb68d94c48a53514752d57cb4f01adeaae3))
|
||||||
- *(workflows)* Add CI for Deno project tests - ([9d5db4f](https://git.0xmax42.io/maxp/http-kernel/commit/9d5db4f414cf961248f2b879f2b132b81a32cb92))
|
- *(workflows)* Add CI for Deno project tests - ([9d5db4f](https://git.0xmax42.io/maxp/http-kernel/commit/9d5db4f414cf961248f2b879f2b132b81a32cb92))
|
||||||
|
|
||||||
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
|
- *(workflows)* Ensure version detection output is always set - ([abd2d6e](https://git.0xmax42.io/maxp/http-kernel/commit/abd2d6e8402662f863d9974aaa0bc228a4777724))
|
||||||
|
|
||||||
### 🚜 Refactor
|
### 🚜 Refactor
|
||||||
|
|
||||||
- *(workflows)* Rename changelog file for consistency - ([b9d25f2](https://git.0xmax42.io/maxp/http-kernel/commit/b9d25f23fc6ad7696deee319024aa5b1af4d98c0))
|
- *(workflows)* Rename changelog file for consistency - ([b9d25f2](https://git.0xmax42.io/maxp/http-kernel/commit/b9d25f23fc6ad7696deee319024aa5b1af4d98c0))
|
||||||
|
|
||||||
### 📚 Documentation
|
### 📚 Documentation
|
||||||
|
|
||||||
|
- *(pipeline)* Add design plan for PipelineExecutor class - ([0846dbb](https://git.0xmax42.io/maxp/http-kernel/commit/0846dbb758ba788f969a381c56498920ee0f9562))
|
||||||
- Add README for HttpKernel project - ([a1ce306](https://git.0xmax42.io/maxp/http-kernel/commit/a1ce30627c68a3f869eb6a104308322af8596dc1))
|
- Add README for HttpKernel project - ([a1ce306](https://git.0xmax42.io/maxp/http-kernel/commit/a1ce30627c68a3f869eb6a104308322af8596dc1))
|
||||||
- Add MIT license file - ([5118a19](https://git.0xmax42.io/maxp/http-kernel/commit/5118a19aeaa1102591aa7fe093fdec1aa19dc7f5))
|
- Add MIT license file - ([5118a19](https://git.0xmax42.io/maxp/http-kernel/commit/5118a19aeaa1102591aa7fe093fdec1aa19dc7f5))
|
||||||
|
|
||||||
### ⚙️ Miscellaneous Tasks
|
### ⚙️ Miscellaneous Tasks
|
||||||
|
|
||||||
|
- *(workflows)* Refine branch handling in release process - ([8f94cc9](https://git.0xmax42.io/maxp/http-kernel/commit/8f94cc915c75a11efa1a8e3bdc51ffea9c2f19b5))
|
||||||
- *(workflows)* Update changelog file extension to .md and revert b9d25f23fc - ([a88b4d1](https://git.0xmax42.io/maxp/http-kernel/commit/a88b4d112f5c07664d41f6e9d03246307551f25d))
|
- *(workflows)* Update changelog file extension to .md and revert b9d25f23fc - ([a88b4d1](https://git.0xmax42.io/maxp/http-kernel/commit/a88b4d112f5c07664d41f6e9d03246307551f25d))
|
||||||
- Rename changelog and readme files to use .md extension - ([4f2b650](https://git.0xmax42.io/maxp/http-kernel/commit/4f2b65049f461ef377e7231905fd066cbc3c7fe0))
|
- Rename changelog and readme files to use .md extension - ([4f2b650](https://git.0xmax42.io/maxp/http-kernel/commit/4f2b65049f461ef377e7231905fd066cbc3c7fe0))
|
||||||
- *(workflows)* Update test workflow for http-kernel project - ([0311546](https://git.0xmax42.io/maxp/http-kernel/commit/03115464e0fb01b8ca00a2fdabde013d004ae8a2))
|
- *(workflows)* Update test workflow for http-kernel project - ([0311546](https://git.0xmax42.io/maxp/http-kernel/commit/03115464e0fb01b8ca00a2fdabde013d004ae8a2))
|
||||||
|
|||||||
72
docs/pipeline_executor_plan.md
Normal file
72
docs/pipeline_executor_plan.md
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# 🧩 Plan: `PipelineExecutor<TContext>`
|
||||||
|
|
||||||
|
## 🎯 Ziel
|
||||||
|
Eine eigenständige, testbare Klasse zur Ausführung einer Middleware- und Handler-Pipeline, die:
|
||||||
|
- Linear und sauber das `next()`-Verhalten abbildet
|
||||||
|
- Typvalidierung durchführt (`isMiddleware`, `isHandler`)
|
||||||
|
- Fehler behandelt und an konfigurierbare Handler weiterleitet
|
||||||
|
- Optionale Hooks zur Tracing-Integration bietet (z. B. für Zeitmessung, Logging)
|
||||||
|
- Am Ende eine dekorierte `Response` zurückliefert
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧩 Schnittstelle (API)
|
||||||
|
|
||||||
|
```ts
|
||||||
|
class PipelineExecutor<TContext extends IContext> {
|
||||||
|
constructor(cfg: IHttpKernelConfig<TContext>);
|
||||||
|
|
||||||
|
run(
|
||||||
|
ctx: TContext,
|
||||||
|
middleware: Middleware<TContext>[],
|
||||||
|
handler: Handler<TContext>,
|
||||||
|
hooks?: IPipelineHooks<TContext>, // optional
|
||||||
|
): Promise<Response>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🪝 Hook-Schnittstelle (`IPipelineHooks`)
|
||||||
|
|
||||||
|
```ts
|
||||||
|
interface IPipelineHooks<TContext> {
|
||||||
|
onPipelineStart?(ctx: TContext): void;
|
||||||
|
onStepStart?(name: string | undefined, ctx: TContext): void;
|
||||||
|
onStepEnd?(name: string | undefined, ctx: TContext, duration: number): void;
|
||||||
|
onPipelineEnd?(ctx: TContext, totalDuration: number): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `name` ist `undefined`, wenn keine `.name` am Handler/Middleware gesetzt ist
|
||||||
|
- Diese Hooks ermöglichen später Logging, Zeitmessung, Statistiken etc.
|
||||||
|
- Der `TraceManager` wird dieses Interface implementieren
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ Interne Aufgaben / Ablauf
|
||||||
|
|
||||||
|
1. `run(...)` beginnt mit Aufruf `onPipelineStart(ctx)`
|
||||||
|
2. Zeitmessung (`performance.now()`)
|
||||||
|
3. Dispatcher-Funktion führt jede Middleware mit `next()`-Kette aus
|
||||||
|
4. Vor jedem Aufruf: `onStepStart(name, ctx)`
|
||||||
|
5. Nach jedem Aufruf: `onStepEnd(name, ctx, duration)`
|
||||||
|
6. Nach letztem Handler: `onPipelineEnd(ctx, totalDuration)`
|
||||||
|
7. Ergebnis wird durch `cfg.decorateResponse(res, ctx)` geschickt
|
||||||
|
8. Im Fehlerfall: `cfg.httpErrorHandlers[500](ctx, error)`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Vorteile
|
||||||
|
|
||||||
|
- `HttpKernel` ist von Ausführungsdetails entkoppelt
|
||||||
|
- Tracing-/Logging-System kann ohne Invasivität angeschlossen werden
|
||||||
|
- Sehr gut testbar (z. B. Middleware-Mock + Hook-Aufrufe prüfen)
|
||||||
|
- Erweiterbar für Timeout, Async-Context, Abbruchlogik etc.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 Dateiname-Vorschlag
|
||||||
|
|
||||||
|
- `src/Core/PipelineExecutor.ts` oder
|
||||||
|
- `src/HttpKernel/PipelineExecutor.ts`
|
||||||
50
src/Interfaces/IPipelineExecutor.ts
Normal file
50
src/Interfaces/IPipelineExecutor.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { Handler, Middleware } from '../Types/mod.ts';
|
||||||
|
import { IContext } from './IContext.ts';
|
||||||
|
import { IPipelineExecutorConfig } from './IPipelineExecutorConfig.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor type for a class implementing the IPipelineExecutor interface.
|
||||||
|
*
|
||||||
|
* This can be used for dependency injection, factory-based initialization,
|
||||||
|
* or dynamic instantiation of pipeline executors.
|
||||||
|
*
|
||||||
|
* @template TContext - The extended context type passed through the pipeline.
|
||||||
|
*/
|
||||||
|
export interface PipelineExecutorFactory<TContext extends IContext = IContext> {
|
||||||
|
/**
|
||||||
|
* Creates a new instance of a pipeline executor.
|
||||||
|
*
|
||||||
|
* @param config - Configuration used to control error handling,
|
||||||
|
* response decoration and lifecycle hooks.
|
||||||
|
*/
|
||||||
|
new (
|
||||||
|
config: IPipelineExecutorConfig<TContext>,
|
||||||
|
): IPipelineExecutor<TContext>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the contract for executing a middleware and handler pipeline.
|
||||||
|
*
|
||||||
|
* The pipeline is responsible for:
|
||||||
|
* - Executing middleware in order with `next()` chaining
|
||||||
|
* - Invoking the final handler
|
||||||
|
* - Applying optional lifecycle hooks
|
||||||
|
* - Producing and decorating a Response
|
||||||
|
*
|
||||||
|
* @template TContext - The context type flowing through the pipeline.
|
||||||
|
*/
|
||||||
|
export interface IPipelineExecutor<TContext extends IContext = IContext> {
|
||||||
|
/**
|
||||||
|
* Executes the middleware pipeline and returns the final Response.
|
||||||
|
*
|
||||||
|
* @param ctx - The context object representing the current HTTP request state.
|
||||||
|
* @param middleware - An ordered array of middleware functions to be executed.
|
||||||
|
* @param handler - The final route handler to be called after all middleware.
|
||||||
|
* @returns A Promise resolving to the final HTTP Response.
|
||||||
|
*/
|
||||||
|
run(
|
||||||
|
ctx: TContext,
|
||||||
|
middleware: Middleware<TContext>[],
|
||||||
|
handler: Handler<TContext>,
|
||||||
|
): Promise<Response>;
|
||||||
|
}
|
||||||
33
src/Interfaces/IPipelineExecutorConfig.ts
Normal file
33
src/Interfaces/IPipelineExecutorConfig.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { ResponseDecorator } from '../Types/ResponseDecorator.ts';
|
||||||
|
import { IContext } from './IContext.ts';
|
||||||
|
import { IHttpErrorHandlers } from './IHttpErrorHandlers.ts';
|
||||||
|
import { IPipelineHooks } from './IPipelineHooks.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration object for the PipelineExecutor, defining how to handle
|
||||||
|
* errors, responses, and tracing hooks.
|
||||||
|
*
|
||||||
|
* This allows the execution logic to remain decoupled from kernel-level behavior
|
||||||
|
* while still supporting custom behavior injection.
|
||||||
|
*
|
||||||
|
* @template TContext - The context type propagated during pipeline execution.
|
||||||
|
*/
|
||||||
|
export interface IPipelineExecutorConfig<TContext extends IContext = IContext> {
|
||||||
|
/**
|
||||||
|
* Optional function used to transform or decorate the final Response object
|
||||||
|
* before it is returned to the client.
|
||||||
|
*/
|
||||||
|
decorateResponse?: ResponseDecorator<TContext>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional map of error handlers, keyed by HTTP status codes (e.g., 404, 500).
|
||||||
|
* These handlers are invoked if an error occurs during middleware or handler execution.
|
||||||
|
*/
|
||||||
|
errorHandlers?: IHttpErrorHandlers<TContext>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional hooks that allow tracing and lifecycle monitoring during pipeline execution.
|
||||||
|
* Each hook is called at a specific phase of the middleware/handler lifecycle.
|
||||||
|
*/
|
||||||
|
pipelineHooks?: IPipelineHooks<TContext>;
|
||||||
|
}
|
||||||
36
src/Interfaces/IPipelineHooks.ts
Normal file
36
src/Interfaces/IPipelineHooks.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import {
|
||||||
|
OnPipelineEnd,
|
||||||
|
OnPipelineStart,
|
||||||
|
OnStepEnd,
|
||||||
|
OnStepStart,
|
||||||
|
} from '../Types/mod.ts';
|
||||||
|
import { IContext } from './IContext.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of optional hook functions that can be triggered during pipeline execution.
|
||||||
|
* These hooks allow tracing, performance measurement, and logging to be integrated
|
||||||
|
* without altering middleware or handler logic.
|
||||||
|
*
|
||||||
|
* @template TContext - The custom context type used within the application.
|
||||||
|
*/
|
||||||
|
export interface IPipelineHooks<TContext extends IContext = IContext> {
|
||||||
|
/**
|
||||||
|
* Triggered once before any middleware or handler is executed.
|
||||||
|
*/
|
||||||
|
onPipelineStart?: OnPipelineStart<TContext>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered immediately before each middleware or handler runs.
|
||||||
|
*/
|
||||||
|
onStepStart?: OnStepStart<TContext>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered immediately after each middleware or handler has finished executing.
|
||||||
|
*/
|
||||||
|
onStepEnd?: OnStepEnd<TContext>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered after the entire pipeline completes execution.
|
||||||
|
*/
|
||||||
|
onPipelineEnd?: OnPipelineEnd<TContext>;
|
||||||
|
}
|
||||||
@@ -5,6 +5,12 @@ export type { IHttpErrorHandlers } from './IHttpErrorHandlers.ts';
|
|||||||
export type { IHttpKernel } from './IHttpKernel.ts';
|
export type { IHttpKernel } from './IHttpKernel.ts';
|
||||||
export type { IHttpKernelConfig } from './IHttpKernelConfig.ts';
|
export type { IHttpKernelConfig } from './IHttpKernelConfig.ts';
|
||||||
export type { IInternalRoute } from './IInternalRoute.ts';
|
export type { IInternalRoute } from './IInternalRoute.ts';
|
||||||
|
export type {
|
||||||
|
IPipelineExecutor,
|
||||||
|
PipelineExecutorFactory,
|
||||||
|
} from './IPipelineExecutor.ts';
|
||||||
|
export type { IPipelineExecutorConfig } from './IPipelineExecutorConfig.ts';
|
||||||
|
export type { IPipelineHooks } from './IPipelineHooks.ts';
|
||||||
export type { IRouteBuilder, IRouteBuilderFactory } from './IRouteBuilder.ts';
|
export type { IRouteBuilder, IRouteBuilderFactory } from './IRouteBuilder.ts';
|
||||||
export {
|
export {
|
||||||
isDynamicRouteDefinition,
|
isDynamicRouteDefinition,
|
||||||
|
|||||||
49
src/Types/PipelineHooks.ts
Normal file
49
src/Types/PipelineHooks.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { IContext } from '../Interfaces/mod.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback invoked when the middleware pipeline starts.
|
||||||
|
*
|
||||||
|
* @template TContext - The context type passed throughout the pipeline.
|
||||||
|
* @param ctx - The context object for the current request.
|
||||||
|
*/
|
||||||
|
export type OnPipelineStart<TContext extends IContext> = (
|
||||||
|
ctx: TContext,
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback invoked immediately before a middleware or handler is executed.
|
||||||
|
*
|
||||||
|
* @template TContext - The context type passed throughout the pipeline.
|
||||||
|
* @param name - Optional name of the current middleware or handler, if defined.
|
||||||
|
* @param ctx - The context object for the current request.
|
||||||
|
*/
|
||||||
|
export type OnStepStart<TContext extends IContext> = (
|
||||||
|
name: string | undefined,
|
||||||
|
ctx: TContext,
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback invoked immediately after a middleware or handler has completed.
|
||||||
|
*
|
||||||
|
* @template TContext - The context type passed throughout the pipeline.
|
||||||
|
* @param name - Optional name of the current middleware or handler, if defined.
|
||||||
|
* @param ctx - The context object for the current request.
|
||||||
|
* @param duration - Execution time in milliseconds.
|
||||||
|
*/
|
||||||
|
export type OnStepEnd<TContext extends IContext> = (
|
||||||
|
name: string | undefined,
|
||||||
|
ctx: TContext,
|
||||||
|
duration: number,
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback invoked after the entire pipeline has completed execution.
|
||||||
|
*
|
||||||
|
* @template TContext - The context type passed throughout the pipeline.
|
||||||
|
* @param ctx - The context object for the current request.
|
||||||
|
* @param totalDuration - Total execution time of the pipeline in milliseconds.
|
||||||
|
*/
|
||||||
|
export type OnPipelineEnd<TContext extends IContext> = (
|
||||||
|
ctx: TContext,
|
||||||
|
totalDuration: number,
|
||||||
|
) => void;
|
||||||
@@ -39,6 +39,12 @@ export type { HttpStatusCode } from './HttpStatusCode.ts';
|
|||||||
export { isMiddleware } from './Middleware.ts';
|
export { isMiddleware } from './Middleware.ts';
|
||||||
export type { Middleware } from './Middleware.ts';
|
export type { Middleware } from './Middleware.ts';
|
||||||
export type { Params } from './Params.ts';
|
export type { Params } from './Params.ts';
|
||||||
|
export type {
|
||||||
|
OnPipelineEnd,
|
||||||
|
OnPipelineStart,
|
||||||
|
OnStepEnd,
|
||||||
|
OnStepStart,
|
||||||
|
} from './PipelineHooks.ts';
|
||||||
export type { Query } from './Query.ts';
|
export type { Query } from './Query.ts';
|
||||||
export type { RegisterRoute } from './RegisterRoute.ts';
|
export type { RegisterRoute } from './RegisterRoute.ts';
|
||||||
export type { ResponseDecorator } from './ResponseDecorator.ts';
|
export type { ResponseDecorator } from './ResponseDecorator.ts';
|
||||||
|
|||||||
Reference in New Issue
Block a user