diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d8141b9..d33d9f62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Feature: - new life-cycle - `resolve`: derive after validation - `mapResponse`: custom response mapping +- add stack trace to plugin checksum Improvement: - lazy query reference diff --git a/example/a.ts b/example/a.ts index afbd12c2..01a8246c 100644 --- a/example/a.ts +++ b/example/a.ts @@ -1,7 +1,22 @@ import { Elysia, t } from '../src' +const a = new Elysia({ name: 'a', seed: 'awdawd' }).extends( + ({ onBeforeHandle }) => ({ + a(fn: () => void) { + onBeforeHandle(fn) + } + }) +) +const b = new Elysia({ name: 'b', seed: 'add' }).use(a).decorate('b', 'b') + const app = new Elysia() - .ws('/ws', { - message() {} + .use(a) + .use(b) + .get('/', () => 'Hello World', { + a() { + console.log('a') + } }) - // .listen(0) + .listen(3000) + +console.dir({ main: app.dependencies }, { depth: 10 }) diff --git a/src/index.ts b/src/index.ts index e922dd01..e0229cbf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -84,7 +84,8 @@ import type { BaseExtension, ExtensionToProperty, ExtensionManager, - MapResponse + MapResponse, + Checksum } from './types' import { t } from './type-system' @@ -119,7 +120,7 @@ export default class Elysia< Scoped extends boolean = false > { config: ElysiaConfig - private dependencies: Record = {} + private dependencies: Record = {} store: Decorators['store'] = {} private decorators = {} as Decorators['request'] @@ -180,6 +181,7 @@ export default class Elysia< private dynamicRouter = new Memoirist() private lazyLoadModules: Promise>[] = [] path: BasePath = '' as any + stack: string | undefined = undefined constructor(config?: Partial>) { this.config = { @@ -192,6 +194,9 @@ export default class Elysia< ...config, seed: config?.seed === undefined ? '' : config?.seed } as any + + if (config?.name || config?.seed !== undefined) + this.stack = new Error().stack } private add( @@ -315,8 +320,7 @@ export default class Elysia< ) } as any - const globalHook = Object.assign({}, this.event) - localHook = Object.assign({}, localHook) + const globalHook = this.event const loosePath = path.endsWith('/') ? path.slice(0, path.length - 1) @@ -335,6 +339,8 @@ export default class Elysia< fn?: MaybeArray ) => { if (typeof type === 'function' || Array.isArray(type)) { + if (!localHook[stackName]) localHook[stackName] = [] + if (Array.isArray(type)) localHook[stackName] = ( localHook[stackName] as unknown[] @@ -369,11 +375,11 @@ export default class Elysia< return } else { + if (!localHook[stackName]) localHook[stackName] = [] + if (!Array.isArray(fn)) { if (insert === 'before') { - ;(localHook[stackName] as any[]).unshift( - fn - ) + ;(localHook[stackName] as any[]).unshift(fn) } else { ;(localHook[stackName] as any[]).push(fn) } @@ -406,7 +412,13 @@ export default class Elysia< } for (const extension of this.extensions) - traceBackExtension(extension(manager), localHook as any) + traceBackExtension( + extension(manager), + localHook as any, + checksum( + this.config.name + JSON.stringify(this.config.seed) + ) + ) } const hooks = mergeHook(globalHook, localHook) @@ -1246,13 +1258,13 @@ export default class Elysia< > ) => NewElysia ): Elysia< - BasePath, - Decorators, - Definitions, - ParentSchema, - Extension, - Prettify - > + BasePath, + Decorators, + Definitions, + ParentSchema, + Extension, + Prettify + > /** * ### group @@ -1291,7 +1303,6 @@ export default class Elysia< const sandbox = (isSchema ? run! : schemaOrRun)(instance) this.decorators = mergeDeep(this.decorators, instance.decorators) - if (sandbox.event.request.length) this.event.request = [ ...this.event.request, @@ -1406,13 +1417,13 @@ export default class Elysia< > ) => NewElysia ): Elysia< - BasePath, - Decorators, - Definitions, - ParentSchema, - Extension, - Prettify -> + BasePath, + Decorators, + Definitions, + ParentSchema, + Extension, + Prettify + > /** * ### guard @@ -1800,12 +1811,18 @@ export default class Elysia< if ( this.dependencies[name].some( - (checksum) => current === checksum + ({ checksum }) => current === checksum ) ) return this - this.dependencies[name].push(current) + this.dependencies[name].push({ + name: plugin.config.name, + seed: plugin.config.seed, + checksum: current, + stack: plugin.stack, + dependencies: plugin.dependencies + }) } plugin.model(this.definitions.type as any) @@ -1839,7 +1856,7 @@ export default class Elysia< if ( !this.dependencies[name].some( - (checksum) => current === checksum + ({ checksum }) => current === checksum ) ) this.extensions.push(...plugin.extensions) @@ -1878,12 +1895,18 @@ export default class Elysia< if ( this.dependencies[name].some( - (checksum) => current === checksum + ({ checksum }) => current === checksum ) ) return this - this.dependencies[name].push(current) + this.dependencies[name].push({ + name: plugin.config.name, + seed: plugin.config.seed, + checksum: current, + stack: plugin.stack, + dependencies: plugin.dependencies + }) this.event = mergeLifeCycle( this.event, filterGlobalHook(plugin.event), diff --git a/src/types.ts b/src/types.ts index 5437bbf6..b49c599e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -696,6 +696,14 @@ export type BaseExtension = Record< BaseExtension | ((a: any) => unknown) > +export type Checksum = { + name?: string + seed?: unknown + checksum: number + stack?: string + dependencies?: Record +} + export type ExtensionToProperty = Prettify<{ [K in keyof T]: T[K] extends Function ? T[K] extends (a: infer Params) => any diff --git a/src/utils.ts b/src/utils.ts index 0734cab2..7a0b422d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -74,7 +74,7 @@ export const mergeCookie = ( skipKeys: ['properties'] }) -export const mergeObjectArray = (a: T | T[], b: T | T[]): T[] => { +export const mergeObjectArray = (a: T | T[], b: T | T[]): T[] => { // ! Must copy to remove side-effect const array = [...(Array.isArray(a) ? a : [a])] const checksums = [] @@ -280,7 +280,10 @@ export const getResponseSchemaValidator = ( 'additionalProperties' in schema === false // Inherits model maybe already compiled - record[+status] = Kind in schema ? compile(schema, Object.values(models)) : schema + record[+status] = + Kind in schema + ? compile(schema, Object.values(models)) + : schema } return undefined @@ -555,13 +558,21 @@ export const unsignCookie = async (input: string, secret: string | null) => { export const traceBackExtension = ( extension: BaseExtension, property: Record, + checksum: number | undefined, hooks = property ) => { for (const [key, value] of Object.entries(property)) { if (primitiveHooks.includes(key as any) || !(key in extension)) continue - if (typeof extension[key] === 'function') extension[key](value) - else if (typeof extension[key] === 'object') - traceBackExtension(extension[key], value as any, hooks) + if (typeof extension[key] === 'function') { + // Property is reference to the original object + // @ts-ignore + if (!property[key]?.$elysiaChecksum) { + // @ts-ignore + property[key].$elysiaChecksum = checksum + extension[key](value) + } + } else if (typeof extension[key] === 'object') + traceBackExtension(extension[key], value as any, checksum, hooks) } }