{"id":19795028,"url":"https://github.com/engelmi/ts-aspect","last_synced_at":"2025-09-20T01:47:20.516Z","repository":{"id":44537736,"uuid":"327369619","full_name":"engelmi/ts-aspect","owner":"engelmi","description":"A simplistic library for Aspect Oriented Programming (AOP) in TypeScript. ","archived":false,"fork":false,"pushed_at":"2023-10-21T19:50:21.000Z","size":500,"stargazers_count":14,"open_issues_count":2,"forks_count":5,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-03-23T12:32:42.672Z","etag":null,"topics":["aspect-oriented-programming","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/ts-aspect","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/engelmi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-01-06T16:33:23.000Z","updated_at":"2024-07-22T11:32:07.514Z","dependencies_parsed_at":"2024-07-22T11:31:49.595Z","dependency_job_id":"07aeea83-b081-496e-8d48-3de9ca2b454a","html_url":"https://github.com/engelmi/ts-aspect","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/engelmi%2Fts-aspect","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/engelmi%2Fts-aspect/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/engelmi%2Fts-aspect/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/engelmi%2Fts-aspect/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/engelmi","download_url":"https://codeload.github.com/engelmi/ts-aspect/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251817795,"owners_count":21648807,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aspect-oriented-programming","typescript"],"created_at":"2024-11-12T07:15:16.038Z","updated_at":"2025-09-20T01:47:15.459Z","avatar_url":"https://github.com/engelmi.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ts-aspect\n\nA simplistic library for **A**spect **O**riented **P**rogramming (AOP) in TypeScript. Aspects can be injected on pointcuts via regular expressions for a class or object. \n\nOne application of AOP is the encapsulation of cross-cutting concerns, like logging, and keep the original business logic clean. Although a powerful tool, it should be used with care as it hides logic and complexity. \n\n![Build Status](https://github.com/engelmi/ts-aspect/actions/workflows/build.yml/badge.svg)\n\n## Installation\nTo get started, install `ts-aspect` with npm.\n```\nnpm install ts-aspect\n```\n\n## Using with NestJS\n`ts-aspect` can also be used together with `NestJS`. An example can be seen [here](https://github.com/engelmi/ts-aspect-nestjs-example). \n\n\n## Usage\nAn aspect can be injected to the `target` class instance or object via\n```javascript\nfunction addAspect(target: any, methodName: string, advice: Advice, aspect: Aspect): void\n```\nor\n```javascript\nfunction addAspectToPointcut(target: any, pointcut: string, advice: Advice, aspect: Aspect): void\n```\nThe `aspect` parameter is the actual behavior that extends the `target` code. When the `aspect` is about to be executed is defined by the `advice` parameter. Currently, the following advices are available:\n- Before\n- After\n- Around\n- AfterReturn\n- TryCatch\n- TryFinally\n\nFor example, the AfterReturn enables you to access the return value of the original function and execute additional logic on it (like logging). \nFinally, the `pointcut` parameter describes the where - so basically for which functions the `aspect` should be executed. For this a regular expression can be used. \n\nIf an `aspect` is called, it creates a new context. The context itself is defined as\n```javascript\nexport type AspectContext = {\n    target: any;            // injected object\n    methodName: string;     // the name of the injected function\n    functionParams: any[];  // parameters passed to the call of the injected function\n    returnValue: any;       // only set for the AfterReturn-Aspect\n    error: any;             // only set for the TryCatch-Aspect when an error is thrown\n};\n```\nAspects await the execution of asynchronous functions - and are asynchronous themselves in this case. This enables an aspect for the advices `Advice.After`, `Advice.AfterReturn` and at the second execution of `Advice.Around` to work with the resolved return value of the injected function. \n\nAlso, `ts-aspect` provides a method decorator to attach an aspect to a all instances of a class in a declarative manner:\n```javascript\nfunction UseAspect(advice: Advice, aspect: Aspect | (new () =\u003e Aspect)): MethodDecorator\n```\n\n## Example\nAssume the following aspect class which simply logs the current aspect context passed to it to the console: \n```javascript\nclass LogAspect implements Aspect{\n    function execute(ctx: AspectContext): void {\n        console.log(ctx);\n    }\n};\n```\n\nAlso, we create the following `Calculator` class: \n```javascript\nclass Calculator {\n    public add(a: number, b: number) {\n        return a + b;\n    }\n\n    public subtract(a: number, b: number) {\n        return a - b;\n    }\n\n    public divide(a: number, b: number) {\n        if(b === 0){\n            throw new Error('Division by zero!');\n        }\n        return a / b;\n    }\n\n    public multiply(a: number, b: number) {\n        return a * b;\n    }\n};\n```\n\n\nNow the `logArgsAspect` can be injected to an instance of `Calculator`. In the following example, the aspect is supposed to be executed before running the actual business logic: \n```javascript\nconst calculator = new Calculator();\naddAspectToPointcut(calculator, '.*', Advice.Before, new LogAspect());\n```\nBy defining the `pointcut` as `'.*'`, the `aspect` is executed when calling any of the functions of the respective `Calculator` instance. Therefore, a call to\n```javascript\ncalculator.add(1, 2);\n```\nshould output\n```javascript\n{\n  target: Calculator {},\n  methodName: 'add',\n  functionParams: [1, 2],\n  returnValue: null,\n  error: null\n}\n```\nAnd further calls to other functions like \n```javascript\ncalculator.subtract(1, 2);\ncalculator.divide(1, 2);\ncalculator.multiply(1, 2);\n```\nshould result in the same output (except for the changing `methodName`).\n\nAn aspect can also be applied in case an exception occurs in the target code: \n```javascript\nconst calculator = new Calculator();\naddAspect(calculator, 'divide', Advice.TryCatch, new LogAspect());\ncalculator.divide(1, 0);\n```\nIn this case, the `divide` function throws the division by zero exception. Due to `Advice.TryCatch` the error is being caught and control is handed over to the aspect, which logs the error as well as both input parameters of the divide function call.  \n**Note:** \nBecause the aspect does not rethrow the exception implicitly, the handling will stop here. Rethrowing the error in the aspect is necessary if it is supposed to be handled elsewhere. \n\n### UseAspect\nIn addition, aspects can be added to a all class instances in a declarative manner by using the decorator `UseAspect`. Based on the Calculator example above, lets add another LogAspect to the `add` method so that the result gets logged to the console as well: \n```javascript\nclass Calculator {\n    @UseAspect(Advice.AfterReturn, LogAspect)\n    public add(a: number, b: number) {\n        return a + b;\n    }\n    // ...\n}\n\nconst calculator = new Calculator();\ncalculator.add(1300, 37);\n```\nThe aspect passed to the decorator can be either a class which provides a constructor with no arguments or an instance of an aspect. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fengelmi%2Fts-aspect","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fengelmi%2Fts-aspect","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fengelmi%2Fts-aspect/lists"}