@velajs/vela 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 +6 -4
- package/dist/application.d.ts +2 -0
- package/dist/application.js +16 -0
- package/dist/cache/cache.decorators.d.ts +2 -0
- package/dist/cache/cache.decorators.js +4 -0
- package/dist/cache/cache.interceptor.d.ts +9 -0
- package/dist/cache/cache.interceptor.js +60 -0
- package/dist/cache/cache.module.d.ts +5 -0
- package/dist/cache/cache.module.js +50 -0
- package/dist/cache/cache.service.d.ts +9 -0
- package/dist/cache/cache.service.js +42 -0
- package/dist/cache/cache.store.d.ts +12 -0
- package/dist/cache/cache.store.js +51 -0
- package/dist/cache/cache.tokens.d.ts +6 -0
- package/dist/cache/cache.tokens.js +5 -0
- package/dist/cache/cache.types.d.ts +15 -0
- package/dist/cache/cache.types.js +1 -0
- package/dist/cache/index.d.ts +7 -0
- package/dist/cache/index.js +6 -0
- package/dist/cors/cors.module.d.ts +5 -0
- package/dist/cors/cors.module.js +45 -0
- package/dist/cors/cors.tokens.d.ts +3 -0
- package/dist/cors/cors.tokens.js +2 -0
- package/dist/cors/cors.types.d.ts +8 -0
- package/dist/cors/cors.types.js +1 -0
- package/dist/cors/index.d.ts +3 -0
- package/dist/cors/index.js +2 -0
- package/dist/event-emitter/event-emitter.decorators.d.ts +1 -0
- package/dist/event-emitter/event-emitter.decorators.js +11 -0
- package/dist/event-emitter/event-emitter.module.d.ts +2 -0
- package/dist/event-emitter/event-emitter.module.js +22 -0
- package/dist/event-emitter/event-emitter.service.d.ts +13 -0
- package/dist/event-emitter/event-emitter.service.js +92 -0
- package/dist/event-emitter/event-emitter.subscriber.d.ts +9 -0
- package/dist/event-emitter/event-emitter.subscriber.js +56 -0
- package/dist/event-emitter/event-emitter.tokens.d.ts +1 -0
- package/dist/event-emitter/event-emitter.tokens.js +1 -0
- package/dist/event-emitter/event-emitter.types.d.ts +5 -0
- package/dist/event-emitter/event-emitter.types.js +1 -0
- package/dist/event-emitter/index.d.ts +6 -0
- package/dist/event-emitter/index.js +5 -0
- package/dist/factory.js +4 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/schedule/index.d.ts +7 -0
- package/dist/schedule/index.js +5 -0
- package/dist/schedule/schedule.decorators.d.ts +2 -0
- package/dist/schedule/schedule.decorators.js +21 -0
- package/dist/schedule/schedule.executor.d.ts +11 -0
- package/dist/schedule/schedule.executor.js +54 -0
- package/dist/schedule/schedule.module.d.ts +5 -0
- package/dist/schedule/schedule.module.js +39 -0
- package/dist/schedule/schedule.registry.d.ts +23 -0
- package/dist/schedule/schedule.registry.js +78 -0
- package/dist/schedule/schedule.tokens.d.ts +5 -0
- package/dist/schedule/schedule.tokens.js +4 -0
- package/dist/schedule/schedule.types.d.ts +11 -0
- package/dist/schedule/schedule.types.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
# @velajs/vela
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@velajs/vela)
|
|
4
|
+
[](https://github.com/velajs/vela/actions/workflows/ci.yml)
|
|
5
|
+
[](https://github.com/velajs/vela/blob/main/LICENSE)
|
|
6
|
+
|
|
3
7
|
NestJS-compatible framework for edge runtimes, powered by [Hono](https://hono.dev).
|
|
4
8
|
|
|
5
9
|
## Install
|
|
6
10
|
|
|
7
11
|
```bash
|
|
8
|
-
|
|
9
|
-
# or
|
|
10
|
-
bun add @velajs/vela
|
|
12
|
+
pnpm add @velajs/vela
|
|
11
13
|
```
|
|
12
14
|
|
|
13
15
|
## Quick Start
|
|
@@ -74,7 +76,7 @@ No Node.js-specific APIs (`node:fs`, `Buffer`, `process`) are used.
|
|
|
74
76
|
## CRUD Module (Optional)
|
|
75
77
|
|
|
76
78
|
```bash
|
|
77
|
-
|
|
79
|
+
pnpm add @velajs/crud hono-crud @hono/zod-openapi zod
|
|
78
80
|
```
|
|
79
81
|
|
|
80
82
|
See [`@velajs/crud`](https://github.com/velajs/crud) for documentation.
|
package/dist/application.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Hono } from 'hono';
|
|
2
2
|
import type { Container } from './container/container';
|
|
3
3
|
import type { Token } from './container/types';
|
|
4
|
+
import type { CorsOptions } from './cors/cors.types';
|
|
4
5
|
import type { RouteManager } from './http/route.manager';
|
|
5
6
|
import type { FilterType, GuardType, InterceptorType, MiddlewareType, PipeType } from './registry/types';
|
|
6
7
|
export declare class VelaApplication {
|
|
@@ -36,6 +37,7 @@ export declare class VelaApplication {
|
|
|
36
37
|
useGlobalGuards(...guards: GuardType[]): this;
|
|
37
38
|
useGlobalInterceptors(...interceptors: InterceptorType[]): this;
|
|
38
39
|
useGlobalFilters(...filters: FilterType[]): this;
|
|
40
|
+
enableCors(options?: CorsOptions): this;
|
|
39
41
|
get<T>(token: Token<T>): T;
|
|
40
42
|
getHonoApp(): Hono;
|
|
41
43
|
callOnModuleInit(): Promise<void>;
|
package/dist/application.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { cors } from "hono/cors";
|
|
1
2
|
import { hasBeforeApplicationShutdown, hasOnApplicationBootstrap, hasOnApplicationShutdown, hasOnModuleDestroy, hasOnModuleInit } from "./lifecycle/index.js";
|
|
2
3
|
export class VelaApplication {
|
|
3
4
|
container;
|
|
@@ -68,6 +69,21 @@ export class VelaApplication {
|
|
|
68
69
|
this.routeManager.useGlobalFilters(...filters);
|
|
69
70
|
return this;
|
|
70
71
|
}
|
|
72
|
+
enableCors(options = {}) {
|
|
73
|
+
const corsMiddleware = cors({
|
|
74
|
+
origin: options.origin ?? '*',
|
|
75
|
+
allowMethods: options.allowMethods,
|
|
76
|
+
allowHeaders: options.allowHeaders,
|
|
77
|
+
exposeHeaders: options.exposeHeaders,
|
|
78
|
+
credentials: options.credentials,
|
|
79
|
+
maxAge: options.maxAge
|
|
80
|
+
});
|
|
81
|
+
const mw = {
|
|
82
|
+
use: (c, next)=>corsMiddleware(c, next)
|
|
83
|
+
};
|
|
84
|
+
this.routeManager.useGlobalMiddleware(mw);
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
71
87
|
get(token) {
|
|
72
88
|
return this.container.resolve(token);
|
|
73
89
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { SetMetadata } from "../pipeline/reflector.js";
|
|
2
|
+
import { CACHE_KEY_METADATA, CACHE_TTL_METADATA } from "./cache.tokens.js";
|
|
3
|
+
export const CacheKey = (key)=>SetMetadata(CACHE_KEY_METADATA, key);
|
|
4
|
+
export const CacheTTL = (seconds)=>SetMetadata(CACHE_TTL_METADATA, seconds);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ExecutionContext, CallHandler, NestInterceptor } from '../pipeline/types';
|
|
2
|
+
import type { CacheModuleOptions, CacheStore } from './cache.types';
|
|
3
|
+
export declare class CacheInterceptor implements NestInterceptor {
|
|
4
|
+
private cacheStore;
|
|
5
|
+
private options;
|
|
6
|
+
private reflector;
|
|
7
|
+
constructor(cacheStore: CacheStore, options: CacheModuleOptions);
|
|
8
|
+
intercept(context: ExecutionContext, next: CallHandler): Promise<unknown>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
}
|
|
7
|
+
function _ts_metadata(k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
}
|
|
10
|
+
function _ts_param(paramIndex, decorator) {
|
|
11
|
+
return function(target, key) {
|
|
12
|
+
decorator(target, key, paramIndex);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
import { Injectable, Inject } from "../container/decorators.js";
|
|
16
|
+
import { Reflector } from "../pipeline/reflector.js";
|
|
17
|
+
import { CACHE_MANAGER, CACHE_MODULE_OPTIONS, CACHE_KEY_METADATA, CACHE_TTL_METADATA } from "./cache.tokens.js";
|
|
18
|
+
export class CacheInterceptor {
|
|
19
|
+
cacheStore;
|
|
20
|
+
options;
|
|
21
|
+
reflector = new Reflector();
|
|
22
|
+
constructor(cacheStore, options){
|
|
23
|
+
this.cacheStore = cacheStore;
|
|
24
|
+
this.options = options;
|
|
25
|
+
}
|
|
26
|
+
async intercept(context, next) {
|
|
27
|
+
const request = context.getRequest();
|
|
28
|
+
// Only cache GET requests
|
|
29
|
+
if (request.method !== 'GET') {
|
|
30
|
+
return next.handle();
|
|
31
|
+
}
|
|
32
|
+
// Determine cache key
|
|
33
|
+
const customKey = this.reflector.getAllAndOverride(CACHE_KEY_METADATA, context);
|
|
34
|
+
const url = new URL(request.url);
|
|
35
|
+
const cacheKey = customKey ?? `cache:GET:${url.pathname}`;
|
|
36
|
+
// Check cache
|
|
37
|
+
const cached = this.cacheStore.get(cacheKey);
|
|
38
|
+
if (cached !== undefined) {
|
|
39
|
+
return cached;
|
|
40
|
+
}
|
|
41
|
+
// Execute handler
|
|
42
|
+
const result = await next.handle();
|
|
43
|
+
// Determine TTL
|
|
44
|
+
const customTtl = this.reflector.getAllAndOverride(CACHE_TTL_METADATA, context);
|
|
45
|
+
const ttl = customTtl ?? this.options.ttl ?? 5;
|
|
46
|
+
// Store in cache
|
|
47
|
+
this.cacheStore.set(cacheKey, result, ttl);
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
CacheInterceptor = _ts_decorate([
|
|
52
|
+
Injectable(),
|
|
53
|
+
_ts_param(0, Inject(CACHE_MANAGER)),
|
|
54
|
+
_ts_param(1, Inject(CACHE_MODULE_OPTIONS)),
|
|
55
|
+
_ts_metadata("design:type", Function),
|
|
56
|
+
_ts_metadata("design:paramtypes", [
|
|
57
|
+
typeof CacheStore === "undefined" ? Object : CacheStore,
|
|
58
|
+
typeof CacheModuleOptions === "undefined" ? Object : CacheModuleOptions
|
|
59
|
+
])
|
|
60
|
+
], CacheInterceptor);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { METADATA_KEYS } from "../constants.js";
|
|
2
|
+
import { defineMetadata } from "../metadata.js";
|
|
3
|
+
import { MetadataRegistry } from "../registry/metadata.registry.js";
|
|
4
|
+
import { APP_INTERCEPTOR } from "../pipeline/tokens.js";
|
|
5
|
+
import { CacheInterceptor } from "./cache.interceptor.js";
|
|
6
|
+
import { CacheService } from "./cache.service.js";
|
|
7
|
+
import { MemoryCacheStore } from "./cache.store.js";
|
|
8
|
+
import { CACHE_MANAGER, CACHE_MODULE_OPTIONS } from "./cache.tokens.js";
|
|
9
|
+
export class CacheModule {
|
|
10
|
+
static forRoot(options = {}) {
|
|
11
|
+
const { ttl = 5, max = 100, isGlobal = false } = options;
|
|
12
|
+
const store = new MemoryCacheStore(ttl, max);
|
|
13
|
+
const moduleClass = class CacheDynamicModule {
|
|
14
|
+
};
|
|
15
|
+
Object.defineProperty(moduleClass, 'name', {
|
|
16
|
+
value: 'CacheModule'
|
|
17
|
+
});
|
|
18
|
+
defineMetadata(METADATA_KEYS.MODULE, true, moduleClass);
|
|
19
|
+
MetadataRegistry.setModuleOptions(moduleClass, {
|
|
20
|
+
exports: [
|
|
21
|
+
CACHE_MANAGER,
|
|
22
|
+
CACHE_MODULE_OPTIONS,
|
|
23
|
+
CacheService,
|
|
24
|
+
CacheInterceptor
|
|
25
|
+
]
|
|
26
|
+
});
|
|
27
|
+
const providers = [
|
|
28
|
+
{
|
|
29
|
+
token: CACHE_MANAGER,
|
|
30
|
+
useValue: store
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
token: CACHE_MODULE_OPTIONS,
|
|
34
|
+
useValue: options
|
|
35
|
+
},
|
|
36
|
+
CacheService,
|
|
37
|
+
CacheInterceptor
|
|
38
|
+
];
|
|
39
|
+
if (isGlobal) {
|
|
40
|
+
providers.push({
|
|
41
|
+
token: APP_INTERCEPTOR,
|
|
42
|
+
useExisting: CacheInterceptor
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
module: moduleClass,
|
|
47
|
+
providers
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CacheStore } from './cache.types';
|
|
2
|
+
export declare class CacheService {
|
|
3
|
+
private store;
|
|
4
|
+
constructor(store: CacheStore);
|
|
5
|
+
get<T = unknown>(key: string): T | undefined;
|
|
6
|
+
set<T = unknown>(key: string, value: T, ttl?: number): void;
|
|
7
|
+
del(key: string): void;
|
|
8
|
+
clear(): void;
|
|
9
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
}
|
|
7
|
+
function _ts_metadata(k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
}
|
|
10
|
+
function _ts_param(paramIndex, decorator) {
|
|
11
|
+
return function(target, key) {
|
|
12
|
+
decorator(target, key, paramIndex);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
import { Injectable, Inject } from "../container/decorators.js";
|
|
16
|
+
import { CACHE_MANAGER } from "./cache.tokens.js";
|
|
17
|
+
export class CacheService {
|
|
18
|
+
store;
|
|
19
|
+
constructor(store){
|
|
20
|
+
this.store = store;
|
|
21
|
+
}
|
|
22
|
+
get(key) {
|
|
23
|
+
return this.store.get(key);
|
|
24
|
+
}
|
|
25
|
+
set(key, value, ttl) {
|
|
26
|
+
this.store.set(key, value, ttl);
|
|
27
|
+
}
|
|
28
|
+
del(key) {
|
|
29
|
+
this.store.del(key);
|
|
30
|
+
}
|
|
31
|
+
clear() {
|
|
32
|
+
this.store.clear();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
CacheService = _ts_decorate([
|
|
36
|
+
Injectable(),
|
|
37
|
+
_ts_param(0, Inject(CACHE_MANAGER)),
|
|
38
|
+
_ts_metadata("design:type", Function),
|
|
39
|
+
_ts_metadata("design:paramtypes", [
|
|
40
|
+
typeof CacheStore === "undefined" ? Object : CacheStore
|
|
41
|
+
])
|
|
42
|
+
], CacheService);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CacheStore } from './cache.types';
|
|
2
|
+
export declare class MemoryCacheStore implements CacheStore {
|
|
3
|
+
private store;
|
|
4
|
+
private defaultTtl;
|
|
5
|
+
private max;
|
|
6
|
+
constructor(ttl?: number, max?: number);
|
|
7
|
+
get<T = unknown>(key: string): T | undefined;
|
|
8
|
+
set<T = unknown>(key: string, value: T, ttl?: number): void;
|
|
9
|
+
del(key: string): void;
|
|
10
|
+
clear(): void;
|
|
11
|
+
private evict;
|
|
12
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export class MemoryCacheStore {
|
|
2
|
+
store = new Map();
|
|
3
|
+
defaultTtl;
|
|
4
|
+
max;
|
|
5
|
+
constructor(ttl = 5, max = 100){
|
|
6
|
+
this.defaultTtl = ttl;
|
|
7
|
+
this.max = max;
|
|
8
|
+
}
|
|
9
|
+
get(key) {
|
|
10
|
+
const entry = this.store.get(key);
|
|
11
|
+
if (!entry) return undefined;
|
|
12
|
+
if (Date.now() > entry.expiresAt) {
|
|
13
|
+
this.store.delete(key);
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
return entry.value;
|
|
17
|
+
}
|
|
18
|
+
set(key, value, ttl) {
|
|
19
|
+
// Evict if at capacity
|
|
20
|
+
if (!this.store.has(key) && this.store.size >= this.max) {
|
|
21
|
+
this.evict();
|
|
22
|
+
}
|
|
23
|
+
const effectiveTtl = ttl ?? this.defaultTtl;
|
|
24
|
+
this.store.set(key, {
|
|
25
|
+
value,
|
|
26
|
+
expiresAt: Date.now() + effectiveTtl * 1000
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
del(key) {
|
|
30
|
+
this.store.delete(key);
|
|
31
|
+
}
|
|
32
|
+
clear() {
|
|
33
|
+
this.store.clear();
|
|
34
|
+
}
|
|
35
|
+
evict() {
|
|
36
|
+
// First remove expired entries
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
for (const [key, entry] of this.store){
|
|
39
|
+
if (now > entry.expiresAt) {
|
|
40
|
+
this.store.delete(key);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// If still at capacity, remove oldest (first inserted)
|
|
44
|
+
if (this.store.size >= this.max) {
|
|
45
|
+
const firstKey = this.store.keys().next().value;
|
|
46
|
+
if (firstKey !== undefined) {
|
|
47
|
+
this.store.delete(firstKey);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { InjectionToken } from '../container/types';
|
|
2
|
+
import type { CacheStore, CacheModuleOptions } from './cache.types';
|
|
3
|
+
export declare const CACHE_MANAGER: InjectionToken<CacheStore>;
|
|
4
|
+
export declare const CACHE_MODULE_OPTIONS: InjectionToken<CacheModuleOptions>;
|
|
5
|
+
export declare const CACHE_KEY_METADATA = "vela:cache-key";
|
|
6
|
+
export declare const CACHE_TTL_METADATA = "vela:cache-ttl";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { InjectionToken } from "../container/types.js";
|
|
2
|
+
export const CACHE_MANAGER = new InjectionToken('CACHE_MANAGER');
|
|
3
|
+
export const CACHE_MODULE_OPTIONS = new InjectionToken('CACHE_MODULE_OPTIONS');
|
|
4
|
+
export const CACHE_KEY_METADATA = 'vela:cache-key';
|
|
5
|
+
export const CACHE_TTL_METADATA = 'vela:cache-ttl';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface CacheModuleOptions {
|
|
2
|
+
ttl?: number;
|
|
3
|
+
max?: number;
|
|
4
|
+
isGlobal?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface CacheStore {
|
|
7
|
+
get<T = unknown>(key: string): T | undefined;
|
|
8
|
+
set<T = unknown>(key: string, value: T, ttl?: number): void;
|
|
9
|
+
del(key: string): void;
|
|
10
|
+
clear(): void;
|
|
11
|
+
}
|
|
12
|
+
export interface CacheEntry<T = unknown> {
|
|
13
|
+
value: T;
|
|
14
|
+
expiresAt: number;
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { CacheModule } from './cache.module';
|
|
2
|
+
export { CacheService } from './cache.service';
|
|
3
|
+
export { CacheInterceptor } from './cache.interceptor';
|
|
4
|
+
export { MemoryCacheStore } from './cache.store';
|
|
5
|
+
export { CacheKey, CacheTTL } from './cache.decorators';
|
|
6
|
+
export { CACHE_MANAGER, CACHE_MODULE_OPTIONS, CACHE_KEY_METADATA, CACHE_TTL_METADATA } from './cache.tokens';
|
|
7
|
+
export type { CacheModuleOptions, CacheStore, CacheEntry } from './cache.types';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { CacheModule } from "./cache.module.js";
|
|
2
|
+
export { CacheService } from "./cache.service.js";
|
|
3
|
+
export { CacheInterceptor } from "./cache.interceptor.js";
|
|
4
|
+
export { MemoryCacheStore } from "./cache.store.js";
|
|
5
|
+
export { CacheKey, CacheTTL } from "./cache.decorators.js";
|
|
6
|
+
export { CACHE_MANAGER, CACHE_MODULE_OPTIONS, CACHE_KEY_METADATA, CACHE_TTL_METADATA } from "./cache.tokens.js";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { cors } from "hono/cors";
|
|
2
|
+
import { METADATA_KEYS } from "../constants.js";
|
|
3
|
+
import { defineMetadata } from "../metadata.js";
|
|
4
|
+
import { MetadataRegistry } from "../registry/metadata.registry.js";
|
|
5
|
+
import { APP_MIDDLEWARE } from "../pipeline/tokens.js";
|
|
6
|
+
import { CORS_OPTIONS } from "./cors.tokens.js";
|
|
7
|
+
export class CorsModule {
|
|
8
|
+
static forRoot(options = {}) {
|
|
9
|
+
const corsMiddleware = cors({
|
|
10
|
+
origin: options.origin ?? '*',
|
|
11
|
+
allowMethods: options.allowMethods,
|
|
12
|
+
allowHeaders: options.allowHeaders,
|
|
13
|
+
exposeHeaders: options.exposeHeaders,
|
|
14
|
+
credentials: options.credentials,
|
|
15
|
+
maxAge: options.maxAge
|
|
16
|
+
});
|
|
17
|
+
const middleware = {
|
|
18
|
+
use: (c, next)=>corsMiddleware(c, next)
|
|
19
|
+
};
|
|
20
|
+
const moduleClass = class CorsDynamicModule {
|
|
21
|
+
};
|
|
22
|
+
Object.defineProperty(moduleClass, 'name', {
|
|
23
|
+
value: 'CorsModule'
|
|
24
|
+
});
|
|
25
|
+
defineMetadata(METADATA_KEYS.MODULE, true, moduleClass);
|
|
26
|
+
MetadataRegistry.setModuleOptions(moduleClass, {
|
|
27
|
+
exports: [
|
|
28
|
+
CORS_OPTIONS
|
|
29
|
+
]
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
module: moduleClass,
|
|
33
|
+
providers: [
|
|
34
|
+
{
|
|
35
|
+
token: CORS_OPTIONS,
|
|
36
|
+
useValue: options
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
token: APP_MIDDLEWARE,
|
|
40
|
+
useValue: middleware
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function OnEvent(event: string): MethodDecorator;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ON_EVENT_METADATA } from "./event-emitter.tokens.js";
|
|
2
|
+
export function OnEvent(event) {
|
|
3
|
+
return (target, propertyKey)=>{
|
|
4
|
+
const existing = Reflect.getMetadata(ON_EVENT_METADATA, target.constructor) ?? [];
|
|
5
|
+
existing.push({
|
|
6
|
+
event,
|
|
7
|
+
methodName: String(propertyKey)
|
|
8
|
+
});
|
|
9
|
+
Reflect.defineMetadata(ON_EVENT_METADATA, existing, target.constructor);
|
|
10
|
+
};
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
}
|
|
7
|
+
import { Module } from "../module/index.js";
|
|
8
|
+
import { EventEmitter } from "./event-emitter.service.js";
|
|
9
|
+
import { EventEmitterSubscriber } from "./event-emitter.subscriber.js";
|
|
10
|
+
export class EventEmitterModule {
|
|
11
|
+
}
|
|
12
|
+
EventEmitterModule = _ts_decorate([
|
|
13
|
+
Module({
|
|
14
|
+
providers: [
|
|
15
|
+
EventEmitter,
|
|
16
|
+
EventEmitterSubscriber
|
|
17
|
+
],
|
|
18
|
+
exports: [
|
|
19
|
+
EventEmitter
|
|
20
|
+
]
|
|
21
|
+
})
|
|
22
|
+
], EventEmitterModule);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { EventHandler } from './event-emitter.types';
|
|
2
|
+
export declare class EventEmitter {
|
|
3
|
+
private listeners;
|
|
4
|
+
private onceListeners;
|
|
5
|
+
on(event: string, handler: EventHandler): this;
|
|
6
|
+
once(event: string, handler: EventHandler): this;
|
|
7
|
+
off(event: string, handler: EventHandler): this;
|
|
8
|
+
emit(event: string, ...args: unknown[]): Promise<void>;
|
|
9
|
+
removeAllListeners(event?: string): this;
|
|
10
|
+
listenerCount(event: string): number;
|
|
11
|
+
private getMatchingHandlers;
|
|
12
|
+
private matchPattern;
|
|
13
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
}
|
|
7
|
+
import { Injectable } from "../container/index.js";
|
|
8
|
+
export class EventEmitter {
|
|
9
|
+
listeners = new Map();
|
|
10
|
+
onceListeners = new Set();
|
|
11
|
+
on(event, handler) {
|
|
12
|
+
const handlers = this.listeners.get(event) ?? [];
|
|
13
|
+
handlers.push(handler);
|
|
14
|
+
this.listeners.set(event, handlers);
|
|
15
|
+
return this;
|
|
16
|
+
}
|
|
17
|
+
once(event, handler) {
|
|
18
|
+
this.onceListeners.add(handler);
|
|
19
|
+
return this.on(event, handler);
|
|
20
|
+
}
|
|
21
|
+
off(event, handler) {
|
|
22
|
+
const handlers = this.listeners.get(event);
|
|
23
|
+
if (handlers) {
|
|
24
|
+
const index = handlers.indexOf(handler);
|
|
25
|
+
if (index !== -1) handlers.splice(index, 1);
|
|
26
|
+
if (handlers.length === 0) this.listeners.delete(event);
|
|
27
|
+
}
|
|
28
|
+
this.onceListeners.delete(handler);
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
async emit(event, ...args) {
|
|
32
|
+
const handlers = this.getMatchingHandlers(event);
|
|
33
|
+
const toRemove = [];
|
|
34
|
+
await Promise.all(handlers.map(async ({ event: registeredEvent, handler })=>{
|
|
35
|
+
await handler(...args);
|
|
36
|
+
if (this.onceListeners.has(handler)) {
|
|
37
|
+
toRemove.push({
|
|
38
|
+
event: registeredEvent,
|
|
39
|
+
handler
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}));
|
|
43
|
+
for (const { event: registeredEvent, handler } of toRemove){
|
|
44
|
+
this.off(registeredEvent, handler);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
removeAllListeners(event) {
|
|
48
|
+
if (event) {
|
|
49
|
+
const handlers = this.listeners.get(event);
|
|
50
|
+
if (handlers) {
|
|
51
|
+
for (const h of handlers)this.onceListeners.delete(h);
|
|
52
|
+
}
|
|
53
|
+
this.listeners.delete(event);
|
|
54
|
+
} else {
|
|
55
|
+
this.listeners.clear();
|
|
56
|
+
this.onceListeners.clear();
|
|
57
|
+
}
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
listenerCount(event) {
|
|
61
|
+
return this.getMatchingHandlers(event).length;
|
|
62
|
+
}
|
|
63
|
+
getMatchingHandlers(event) {
|
|
64
|
+
const result = [];
|
|
65
|
+
for (const [pattern, handlers] of this.listeners){
|
|
66
|
+
if (this.matchPattern(pattern, event)) {
|
|
67
|
+
for (const handler of handlers){
|
|
68
|
+
result.push({
|
|
69
|
+
event: pattern,
|
|
70
|
+
handler
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
matchPattern(pattern, event) {
|
|
78
|
+
if (pattern === event) return true;
|
|
79
|
+
// Convert glob pattern to regex
|
|
80
|
+
// ** matches any depth (any number of segments)
|
|
81
|
+
// * matches single segment (no dots)
|
|
82
|
+
const regexStr = pattern.split('.').map((segment)=>{
|
|
83
|
+
if (segment === '**') return '.*';
|
|
84
|
+
if (segment === '*') return '[^.]+';
|
|
85
|
+
return segment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
86
|
+
}).join('\\.');
|
|
87
|
+
return new RegExp(`^${regexStr}$`).test(event);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
EventEmitter = _ts_decorate([
|
|
91
|
+
Injectable()
|
|
92
|
+
], EventEmitter);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Container } from '../container/container';
|
|
2
|
+
import type { OnApplicationBootstrap } from '../lifecycle/index';
|
|
3
|
+
import { EventEmitter } from './event-emitter.service';
|
|
4
|
+
export declare class EventEmitterSubscriber implements OnApplicationBootstrap {
|
|
5
|
+
private container;
|
|
6
|
+
private emitter;
|
|
7
|
+
constructor(container: Container, emitter: EventEmitter);
|
|
8
|
+
onApplicationBootstrap(): void;
|
|
9
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
}
|
|
7
|
+
function _ts_metadata(k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
}
|
|
10
|
+
function _ts_param(paramIndex, decorator) {
|
|
11
|
+
return function(target, key) {
|
|
12
|
+
decorator(target, key, paramIndex);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
import { Injectable, Inject } from "../container/index.js";
|
|
16
|
+
import { Container } from "../container/container.js";
|
|
17
|
+
import { EventEmitter } from "./event-emitter.service.js";
|
|
18
|
+
import { ON_EVENT_METADATA } from "./event-emitter.tokens.js";
|
|
19
|
+
export class EventEmitterSubscriber {
|
|
20
|
+
container;
|
|
21
|
+
emitter;
|
|
22
|
+
constructor(container, emitter){
|
|
23
|
+
this.container = container;
|
|
24
|
+
this.emitter = emitter;
|
|
25
|
+
}
|
|
26
|
+
onApplicationBootstrap() {
|
|
27
|
+
const tokens = this.container.getTokens();
|
|
28
|
+
for (const token of tokens){
|
|
29
|
+
// Skip non-class tokens
|
|
30
|
+
if (typeof token !== 'function') continue;
|
|
31
|
+
const metadata = Reflect.getMetadata(ON_EVENT_METADATA, token);
|
|
32
|
+
if (!metadata || metadata.length === 0) continue;
|
|
33
|
+
let instance;
|
|
34
|
+
try {
|
|
35
|
+
instance = this.container.resolve(token);
|
|
36
|
+
} catch {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
for (const { event, methodName } of metadata){
|
|
40
|
+
const method = instance[methodName];
|
|
41
|
+
if (typeof method === 'function') {
|
|
42
|
+
this.emitter.on(event, (...args)=>method.apply(instance, args));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
EventEmitterSubscriber = _ts_decorate([
|
|
49
|
+
Injectable(),
|
|
50
|
+
_ts_param(0, Inject(Container)),
|
|
51
|
+
_ts_metadata("design:type", Function),
|
|
52
|
+
_ts_metadata("design:paramtypes", [
|
|
53
|
+
typeof Container === "undefined" ? Object : Container,
|
|
54
|
+
typeof EventEmitter === "undefined" ? Object : EventEmitter
|
|
55
|
+
])
|
|
56
|
+
], EventEmitterSubscriber);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const ON_EVENT_METADATA = "vela:on-event";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const ON_EVENT_METADATA = 'vela:on-event';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { EventEmitterModule } from './event-emitter.module';
|
|
2
|
+
export { EventEmitter } from './event-emitter.service';
|
|
3
|
+
export { EventEmitterSubscriber } from './event-emitter.subscriber';
|
|
4
|
+
export { OnEvent } from './event-emitter.decorators';
|
|
5
|
+
export { ON_EVENT_METADATA } from './event-emitter.tokens';
|
|
6
|
+
export type { EventHandler, OnEventMetadata } from './event-emitter.types';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { EventEmitterModule } from "./event-emitter.module.js";
|
|
2
|
+
export { EventEmitter } from "./event-emitter.service.js";
|
|
3
|
+
export { EventEmitterSubscriber } from "./event-emitter.subscriber.js";
|
|
4
|
+
export { OnEvent } from "./event-emitter.decorators.js";
|
|
5
|
+
export { ON_EVENT_METADATA } from "./event-emitter.tokens.js";
|
package/dist/factory.js
CHANGED
|
@@ -7,6 +7,10 @@ import { APP_GUARD, APP_PIPE, APP_INTERCEPTOR, APP_FILTER, APP_MIDDLEWARE } from
|
|
|
7
7
|
export const VelaFactory = {
|
|
8
8
|
async create (rootModule) {
|
|
9
9
|
const container = new Container();
|
|
10
|
+
container.register({
|
|
11
|
+
token: Container,
|
|
12
|
+
useValue: container
|
|
13
|
+
});
|
|
10
14
|
const routeManager = new RouteManager(container);
|
|
11
15
|
ComponentManager.init(container);
|
|
12
16
|
const loader = new ModuleLoader(container, routeManager);
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,14 @@ export { Logger, LogLevel } from './services/index';
|
|
|
10
10
|
export type { LoggerService } from './services/index';
|
|
11
11
|
export { ConfigModule, ConfigService, CONFIG_OPTIONS } from './config/index';
|
|
12
12
|
export type { ConfigModuleOptions } from './config/index';
|
|
13
|
+
export { CorsModule, CORS_OPTIONS } from './cors/index';
|
|
14
|
+
export type { CorsOptions } from './cors/index';
|
|
15
|
+
export { CacheModule, CacheService, CacheInterceptor, MemoryCacheStore, CacheKey, CacheTTL, CACHE_MANAGER, CACHE_MODULE_OPTIONS, CACHE_KEY_METADATA, CACHE_TTL_METADATA, } from './cache/index';
|
|
16
|
+
export type { CacheModuleOptions, CacheStore, CacheEntry } from './cache/index';
|
|
17
|
+
export { EventEmitterModule, EventEmitter, EventEmitterSubscriber, OnEvent, ON_EVENT_METADATA, } from './event-emitter/index';
|
|
18
|
+
export type { EventHandler, OnEventMetadata } from './event-emitter/index';
|
|
19
|
+
export { ScheduleModule, ScheduleRegistry, ScheduleExecutor, Cron, Interval, SCHEDULE_MODULE_OPTIONS, CRON_METADATA, INTERVAL_METADATA, } from './schedule/index';
|
|
20
|
+
export type { RegisteredCronJob, RegisteredIntervalJob, CronMetadata, IntervalMetadata, ScheduleModuleOptions, } from './schedule/index';
|
|
13
21
|
export { Module } from './module/index';
|
|
14
22
|
export type { ModuleOptions, DynamicModule } from './module/index';
|
|
15
23
|
export { UseMiddleware, UseGuards, UsePipes, UseInterceptors, UseFilters, Catch, SetMetadata, Reflector, APP_GUARD, APP_PIPE, APP_INTERCEPTOR, APP_FILTER, APP_MIDDLEWARE, } from './pipeline/index';
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,14 @@ export { Controller, Version, Get, Post, Put, Patch, Delete, Options, Head, Sse,
|
|
|
13
13
|
export { Logger, LogLevel } from "./services/index.js";
|
|
14
14
|
// Config
|
|
15
15
|
export { ConfigModule, ConfigService, CONFIG_OPTIONS } from "./config/index.js";
|
|
16
|
+
// CORS
|
|
17
|
+
export { CorsModule, CORS_OPTIONS } from "./cors/index.js";
|
|
18
|
+
// Cache
|
|
19
|
+
export { CacheModule, CacheService, CacheInterceptor, MemoryCacheStore, CacheKey, CacheTTL, CACHE_MANAGER, CACHE_MODULE_OPTIONS, CACHE_KEY_METADATA, CACHE_TTL_METADATA } from "./cache/index.js";
|
|
20
|
+
// Event Emitter
|
|
21
|
+
export { EventEmitterModule, EventEmitter, EventEmitterSubscriber, OnEvent, ON_EVENT_METADATA } from "./event-emitter/index.js";
|
|
22
|
+
// Schedule
|
|
23
|
+
export { ScheduleModule, ScheduleRegistry, ScheduleExecutor, Cron, Interval, SCHEDULE_MODULE_OPTIONS, CRON_METADATA, INTERVAL_METADATA } from "./schedule/index.js";
|
|
16
24
|
// Module
|
|
17
25
|
export { Module } from "./module/index.js";
|
|
18
26
|
// Pipeline Decorators
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { ScheduleModule } from './schedule.module';
|
|
2
|
+
export { ScheduleRegistry } from './schedule.registry';
|
|
3
|
+
export type { RegisteredCronJob, RegisteredIntervalJob } from './schedule.registry';
|
|
4
|
+
export { ScheduleExecutor } from './schedule.executor';
|
|
5
|
+
export { Cron, Interval } from './schedule.decorators';
|
|
6
|
+
export { SCHEDULE_MODULE_OPTIONS, CRON_METADATA, INTERVAL_METADATA } from './schedule.tokens';
|
|
7
|
+
export type { CronMetadata, IntervalMetadata, ScheduleModuleOptions } from './schedule.types';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { ScheduleModule } from "./schedule.module.js";
|
|
2
|
+
export { ScheduleRegistry } from "./schedule.registry.js";
|
|
3
|
+
export { ScheduleExecutor } from "./schedule.executor.js";
|
|
4
|
+
export { Cron, Interval } from "./schedule.decorators.js";
|
|
5
|
+
export { SCHEDULE_MODULE_OPTIONS, CRON_METADATA, INTERVAL_METADATA } from "./schedule.tokens.js";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { CRON_METADATA, INTERVAL_METADATA } from "./schedule.tokens.js";
|
|
2
|
+
export function Cron(expression) {
|
|
3
|
+
return (target, propertyKey)=>{
|
|
4
|
+
const existing = Reflect.getMetadata(CRON_METADATA, target.constructor) ?? [];
|
|
5
|
+
existing.push({
|
|
6
|
+
expression,
|
|
7
|
+
methodName: String(propertyKey)
|
|
8
|
+
});
|
|
9
|
+
Reflect.defineMetadata(CRON_METADATA, existing, target.constructor);
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export function Interval(ms) {
|
|
13
|
+
return (target, propertyKey)=>{
|
|
14
|
+
const existing = Reflect.getMetadata(INTERVAL_METADATA, target.constructor) ?? [];
|
|
15
|
+
existing.push({
|
|
16
|
+
ms,
|
|
17
|
+
methodName: String(propertyKey)
|
|
18
|
+
});
|
|
19
|
+
Reflect.defineMetadata(INTERVAL_METADATA, existing, target.constructor);
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { OnApplicationBootstrap, OnModuleDestroy } from '../lifecycle/index';
|
|
2
|
+
import { ScheduleRegistry } from './schedule.registry';
|
|
3
|
+
export declare class ScheduleExecutor implements OnApplicationBootstrap, OnModuleDestroy {
|
|
4
|
+
private registry;
|
|
5
|
+
private timers;
|
|
6
|
+
private running;
|
|
7
|
+
constructor(registry: ScheduleRegistry);
|
|
8
|
+
onApplicationBootstrap(): void;
|
|
9
|
+
private scheduleNext;
|
|
10
|
+
onModuleDestroy(): void;
|
|
11
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
}
|
|
7
|
+
function _ts_metadata(k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
}
|
|
10
|
+
import { Injectable } from "../container/index.js";
|
|
11
|
+
import { ScheduleRegistry } from "./schedule.registry.js";
|
|
12
|
+
export class ScheduleExecutor {
|
|
13
|
+
registry;
|
|
14
|
+
timers = [];
|
|
15
|
+
running = true;
|
|
16
|
+
constructor(registry){
|
|
17
|
+
this.registry = registry;
|
|
18
|
+
}
|
|
19
|
+
onApplicationBootstrap() {
|
|
20
|
+
const jobs = this.registry.getIntervalJobs();
|
|
21
|
+
for (const job of jobs){
|
|
22
|
+
this.scheduleNext(job.instance, job.methodName, job.ms);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
scheduleNext(instance, methodName, ms) {
|
|
26
|
+
if (!this.running) return;
|
|
27
|
+
const timer = setTimeout(async ()=>{
|
|
28
|
+
try {
|
|
29
|
+
const method = instance[methodName];
|
|
30
|
+
if (typeof method === 'function') {
|
|
31
|
+
await method.call(instance);
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
// Swallow errors to keep the loop alive
|
|
35
|
+
}
|
|
36
|
+
this.scheduleNext(instance, methodName, ms);
|
|
37
|
+
}, ms);
|
|
38
|
+
this.timers.push(timer);
|
|
39
|
+
}
|
|
40
|
+
onModuleDestroy() {
|
|
41
|
+
this.running = false;
|
|
42
|
+
for (const timer of this.timers){
|
|
43
|
+
clearTimeout(timer);
|
|
44
|
+
}
|
|
45
|
+
this.timers = [];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
ScheduleExecutor = _ts_decorate([
|
|
49
|
+
Injectable(),
|
|
50
|
+
_ts_metadata("design:type", Function),
|
|
51
|
+
_ts_metadata("design:paramtypes", [
|
|
52
|
+
typeof ScheduleRegistry === "undefined" ? Object : ScheduleRegistry
|
|
53
|
+
])
|
|
54
|
+
], ScheduleExecutor);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { METADATA_KEYS } from "../constants.js";
|
|
2
|
+
import { defineMetadata } from "../metadata.js";
|
|
3
|
+
import { MetadataRegistry } from "../registry/metadata.registry.js";
|
|
4
|
+
import { ScheduleExecutor } from "./schedule.executor.js";
|
|
5
|
+
import { ScheduleRegistry } from "./schedule.registry.js";
|
|
6
|
+
import { SCHEDULE_MODULE_OPTIONS } from "./schedule.tokens.js";
|
|
7
|
+
export class ScheduleModule {
|
|
8
|
+
static forRoot(options = {}) {
|
|
9
|
+
const { enableTimers = false } = options;
|
|
10
|
+
const moduleClass = class ScheduleDynamicModule {
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(moduleClass, 'name', {
|
|
13
|
+
value: 'ScheduleModule'
|
|
14
|
+
});
|
|
15
|
+
defineMetadata(METADATA_KEYS.MODULE, true, moduleClass);
|
|
16
|
+
const providers = [
|
|
17
|
+
{
|
|
18
|
+
token: SCHEDULE_MODULE_OPTIONS,
|
|
19
|
+
useValue: options
|
|
20
|
+
},
|
|
21
|
+
ScheduleRegistry
|
|
22
|
+
];
|
|
23
|
+
const exports = [
|
|
24
|
+
SCHEDULE_MODULE_OPTIONS,
|
|
25
|
+
ScheduleRegistry
|
|
26
|
+
];
|
|
27
|
+
if (enableTimers) {
|
|
28
|
+
providers.push(ScheduleExecutor);
|
|
29
|
+
exports.push(ScheduleExecutor);
|
|
30
|
+
}
|
|
31
|
+
MetadataRegistry.setModuleOptions(moduleClass, {
|
|
32
|
+
exports
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
module: moduleClass,
|
|
36
|
+
providers
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Container } from '../container/container';
|
|
2
|
+
import type { OnApplicationBootstrap } from '../lifecycle/index';
|
|
3
|
+
export interface RegisteredCronJob {
|
|
4
|
+
expression: string;
|
|
5
|
+
methodName: string;
|
|
6
|
+
instance: unknown;
|
|
7
|
+
target: Function;
|
|
8
|
+
}
|
|
9
|
+
export interface RegisteredIntervalJob {
|
|
10
|
+
ms: number;
|
|
11
|
+
methodName: string;
|
|
12
|
+
instance: unknown;
|
|
13
|
+
target: Function;
|
|
14
|
+
}
|
|
15
|
+
export declare class ScheduleRegistry implements OnApplicationBootstrap {
|
|
16
|
+
private container;
|
|
17
|
+
private cronJobs;
|
|
18
|
+
private intervalJobs;
|
|
19
|
+
constructor(container: Container);
|
|
20
|
+
onApplicationBootstrap(): void;
|
|
21
|
+
getCronJobs(): RegisteredCronJob[];
|
|
22
|
+
getIntervalJobs(): RegisteredIntervalJob[];
|
|
23
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
}
|
|
7
|
+
function _ts_metadata(k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
}
|
|
10
|
+
function _ts_param(paramIndex, decorator) {
|
|
11
|
+
return function(target, key) {
|
|
12
|
+
decorator(target, key, paramIndex);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
import { Injectable, Inject } from "../container/index.js";
|
|
16
|
+
import { Container } from "../container/container.js";
|
|
17
|
+
import { CRON_METADATA, INTERVAL_METADATA } from "./schedule.tokens.js";
|
|
18
|
+
export class ScheduleRegistry {
|
|
19
|
+
container;
|
|
20
|
+
cronJobs = [];
|
|
21
|
+
intervalJobs = [];
|
|
22
|
+
constructor(container){
|
|
23
|
+
this.container = container;
|
|
24
|
+
}
|
|
25
|
+
onApplicationBootstrap() {
|
|
26
|
+
const tokens = this.container.getTokens();
|
|
27
|
+
for (const token of tokens){
|
|
28
|
+
if (typeof token !== 'function') continue;
|
|
29
|
+
const cronMeta = Reflect.getMetadata(CRON_METADATA, token);
|
|
30
|
+
const intervalMeta = Reflect.getMetadata(INTERVAL_METADATA, token);
|
|
31
|
+
if (!cronMeta && !intervalMeta) continue;
|
|
32
|
+
let instance;
|
|
33
|
+
try {
|
|
34
|
+
instance = this.container.resolve(token);
|
|
35
|
+
} catch {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (cronMeta) {
|
|
39
|
+
for (const { expression, methodName } of cronMeta){
|
|
40
|
+
this.cronJobs.push({
|
|
41
|
+
expression,
|
|
42
|
+
methodName,
|
|
43
|
+
instance,
|
|
44
|
+
target: token
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (intervalMeta) {
|
|
49
|
+
for (const { ms, methodName } of intervalMeta){
|
|
50
|
+
this.intervalJobs.push({
|
|
51
|
+
ms,
|
|
52
|
+
methodName,
|
|
53
|
+
instance,
|
|
54
|
+
target: token
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
getCronJobs() {
|
|
61
|
+
return [
|
|
62
|
+
...this.cronJobs
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
getIntervalJobs() {
|
|
66
|
+
return [
|
|
67
|
+
...this.intervalJobs
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
ScheduleRegistry = _ts_decorate([
|
|
72
|
+
Injectable(),
|
|
73
|
+
_ts_param(0, Inject(Container)),
|
|
74
|
+
_ts_metadata("design:type", Function),
|
|
75
|
+
_ts_metadata("design:paramtypes", [
|
|
76
|
+
typeof Container === "undefined" ? Object : Container
|
|
77
|
+
])
|
|
78
|
+
], ScheduleRegistry);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { InjectionToken } from '../container/types';
|
|
2
|
+
import type { ScheduleModuleOptions } from './schedule.types';
|
|
3
|
+
export declare const SCHEDULE_MODULE_OPTIONS: InjectionToken<ScheduleModuleOptions>;
|
|
4
|
+
export declare const CRON_METADATA = "vela:cron";
|
|
5
|
+
export declare const INTERVAL_METADATA = "vela:interval";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|