├── .gitignore ├── .npmignore ├── src ├── index.ts ├── types.ts ├── PatchContext.ts ├── patch.ts ├── PatchInfo.ts └── Patcher.ts ├── tsconfig.json ├── pnpm-lock.yaml ├── package.json ├── tests └── index.js ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | node_modules 3 | tsconfig.json 4 | tsconfig.tsbuildinfo 5 | tests 6 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * JsPosed, a Javascript patching library inspired by Xposed 3 | * Copyright (c) 2022 Vendicated 4 | * Licensed under the Open Software License version 3.0 5 | */ 6 | 7 | export * from "./Patcher"; 8 | export * from "./PatchContext"; 9 | export * from "./PatchInfo"; 10 | export * from "./patch"; 11 | export * from "./types"; 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "allowSyntheticDefaultImports": true, 5 | "declaration": true, 6 | "esModuleInterop": false, 7 | "incremental": true, 8 | "module": "commonjs", 9 | "moduleResolution": "node", 10 | "noEmitOnError": true, 11 | "outDir": "dist", 12 | "strict": true, 13 | "target": "es6", 14 | "lib": ["esnext", "DOM"] 15 | }, 16 | "include": ["src/index.ts", "src/**/*.ts"] 17 | } 18 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: 5.3 2 | 3 | specifiers: 4 | '@types/node': ^17.0.23 5 | typescript: ^4.6.3 6 | 7 | devDependencies: 8 | '@types/node': 17.0.23 9 | typescript: 4.6.3 10 | 11 | packages: 12 | 13 | /@types/node/17.0.23: 14 | resolution: {integrity: sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==} 15 | dev: true 16 | 17 | /typescript/4.6.3: 18 | resolution: {integrity: sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==} 19 | engines: {node: '>=4.2.0'} 20 | hasBin: true 21 | dev: true 22 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * JsPosed, a Javascript patching library inspired by Xposed 3 | * Copyright (c) 2022 Vendicated 4 | * Licensed under the Open Software License version 3.0 5 | */ 6 | 7 | import { PatchContext } from "./PatchContext"; 8 | import { PatchInfo } from "./PatchInfo"; 9 | import { Patch } from "./patch"; 10 | 11 | export type Func = (...args: any[]) => any; 12 | export type PatchFn = (ctx: PatchContext, ...args: any[]) => void; 13 | export type InsteadFn = (ctx: PatchContext, ...args: any[]) => any; 14 | export type OnPatchError = (kind: "before" | "after", info: PatchInfo, err: any, patch: Patch) => void; 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsposed", 3 | "version": "1.0.4", 4 | "description": "Javascript patching library inspired by Xposed", 5 | "keywords": [ 6 | "Xposed", 7 | "javascript", 8 | "patching", 9 | "hook" 10 | ], 11 | "license": "OSL-3.0", 12 | "author": "Vendicated", 13 | "main": "dist/index.js", 14 | "types": "dist/index.d.ts", 15 | "directories": { 16 | "example": "example" 17 | }, 18 | "scripts": { 19 | "build": "tsc", 20 | "prepublish": "pnpm build", 21 | "test": "pnpm build && node tests" 22 | }, 23 | "devDependencies": { 24 | "@types/node": "^17.0.23", 25 | "typescript": "^4.6.3" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/PatchContext.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * JsPosed, a Javascript patching library inspired by Xposed 3 | * Copyright (c) 2022 Vendicated 4 | * Licensed under the Open Software License version 3.0 5 | */ 6 | 7 | export class PatchContext { 8 | private _result: any = null; 9 | private _error: any = null; 10 | _returnEarly = false; 11 | 12 | public constructor(public readonly thisObject: T, public args: any) { } 13 | 14 | /** 15 | * Get the result. Null in beforePatch 16 | */ 17 | public get result() { 18 | return this._result; 19 | } 20 | 21 | /** 22 | * Set the result. 23 | * If called in a beforePatch, this skips the original method 24 | */ 25 | public set result(result: any) { 26 | this._result = result; 27 | this._error = null; 28 | this._returnEarly = true; 29 | } 30 | 31 | /** 32 | * Get the error thrown by the original method, if any 33 | */ 34 | public get error() { 35 | return this._error; 36 | } 37 | 38 | /** 39 | * Set the error. The method will throw this. 40 | * If called in a beforePatch, this skips the original method 41 | */ 42 | public set error(err: any) { 43 | this._error = err; 44 | this._result = null; 45 | this._returnEarly = true; 46 | } 47 | 48 | /** 49 | * If error is not null, throw it. Otherwise, return the result 50 | */ 51 | public get resultOrError() { 52 | const error = this._error; 53 | if (error !== null) throw error; 54 | return this._result; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/patch.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * JsPosed, a Javascript patching library inspired by Xposed 3 | * Copyright (c) 2022 Vendicated 4 | * Licensed under the Open Software License version 3.0 5 | */ 6 | 7 | import { PatchContext } from "./PatchContext"; 8 | import { Patcher } from "./Patcher"; 9 | import { InsteadFn, PatchFn } from "./types"; 10 | 11 | export enum PatchPriority { 12 | MIN = 0, 13 | DEFAULT = 15, 14 | MAX = 30 15 | } 16 | 17 | function NOOP() { }; 18 | 19 | export class Patch { 20 | public before: PatchFn; 21 | public after: PatchFn; 22 | public priority: number; 23 | 24 | public constructor(data: Partial> & { instead?: InsteadFn; }) { 25 | this.priority = data.priority ?? PatchPriority.DEFAULT; 26 | if (this.priority < PatchPriority.MIN || this.priority > PatchPriority.MAX) { 27 | throw new Error("Priority must be between PatchPriority.MIN and PatchPriority.MAX"); 28 | } 29 | 30 | if (data.instead) { 31 | if (data.after || data.before) { 32 | throw new Error("Instead patches cannot specify before or after patches."); 33 | } 34 | 35 | const { instead } = data; 36 | this.before = (ctx: PatchContext) => { 37 | ctx.result = instead(ctx); 38 | }; 39 | this.after = NOOP; 40 | } else { 41 | this.before = data.before ?? NOOP; 42 | this.after = data.after ?? NOOP; 43 | } 44 | } 45 | } 46 | 47 | export class Unpatch { 48 | public constructor(private patcher: Patcher, public obj: T, public methodName: string, public patch: Patch) { } 49 | 50 | public unpatch() { 51 | this.patcher.unpatch(this.obj, this.methodName, this.patch); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * JsPosed, a Javascript patching library inspired by Xposed 3 | * Copyright (c) 2022 Vendicated 4 | * Licensed under the Open Software License version 3.0 5 | */ 6 | 7 | const assert = require("assert/strict"); 8 | const { Patcher, PatchPriority } = require(".."); 9 | 10 | const obj = { 11 | add(x, y) { 12 | return x + y; 13 | }, 14 | addAndMultiplyBy12(x, y) { 15 | let sum = x + y; 16 | sum *= 12; 17 | return sum; 18 | }, 19 | getAdd() { 20 | return this.add; 21 | } 22 | }; 23 | obj.add.someProp = "hello"; 24 | 25 | /** 26 | * @param {(patcher: Patcher) => void} task 27 | */ 28 | function stage(task) { 29 | const patcher = new Patcher(); 30 | task(patcher); 31 | patcher.unpatchAll(); 32 | } 33 | 34 | assert.equal(obj.add(1, 2), 3); 35 | 36 | // TEST INSTEAD 37 | stage(patcher => { 38 | patcher.instead(obj, "add", (_) => 42); 39 | assert.equal(obj.add(1, 2), 42); 40 | // TEST RESTORES PROPS 41 | assert.equal(obj.add.someProp, "hello"); 42 | }); 43 | 44 | // TEST PATCHING 45 | stage(patcher => { 46 | patcher.before(obj, "add", (param) => (param.args[0] = 42)); 47 | assert.equal(obj.add(1, 2), 44); 48 | patcher.after(obj, "add", (param) => (param.result *= 2)); 49 | assert.equal(obj.add(1, 2), 88); 50 | }); 51 | 52 | // SHOULD NOT BREAK 'this' 53 | stage(patcher => { 54 | patcher.before(obj, "getAdd", (_) => _); 55 | assert.equal(obj.getAdd(), obj.add); 56 | }); 57 | 58 | // TEST CALL ORIGINAL 59 | stage(patcher => { 60 | patcher.instead(obj, "add", (param) => 42); 61 | assert.equal(patcher.callOriginal(obj.add, obj, 1, 2), 3); 62 | }); 63 | 64 | // TEST UNPATCHALL 65 | stage(patcher => { 66 | patcher.instead(obj, "add", (param) => 42); 67 | patcher.unpatchAll(); 68 | assert.equal(obj.add(1, 2), 3); 69 | }); 70 | 71 | // TEST THROW 72 | stage(patcher => { 73 | patcher.after(obj, "add", (param) => (param.error = new Error("test"))); 74 | assert.throws(() => obj.add(1, 2), { message: "test" }); 75 | }); 76 | 77 | // TEST PRIORITY 78 | stage(patcher => { 79 | patcher.instead(obj, "add", (param) => 1); 80 | patcher.instead(obj, "add", (param) => 2, PatchPriority.MAX); 81 | patcher.instead(obj, "add", (param) => 3); 82 | assert.equal(obj.add(1, 2), 2); 83 | }); 84 | 85 | // TEST ERROR HANDLING 86 | stage(patcher => { 87 | let passedTest = false; 88 | 89 | const patcherWithCustomError = new Patcher("j", (kind, info, err) => { 90 | passedTest = true; 91 | assert(err instanceof Error); 92 | assert.equal(kind, "before"); 93 | assert.equal(info.methodName, "add"); 94 | assert.equal(info.targetObject, obj); 95 | assert.equal(info.patcher.name, "j"); 96 | }); 97 | 98 | patcherWithCustomError.before(obj, "add", param => { 99 | throw new Error(); 100 | }); 101 | 102 | obj.add(1, 2); 103 | 104 | assert(passedTest); 105 | assert.doesNotThrow(() => obj.add(1, 2)); 106 | 107 | patcherWithCustomError.unpatchAll(); 108 | }); 109 | -------------------------------------------------------------------------------- /src/PatchInfo.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * JsPosed, a Javascript patching library inspired by Xposed 3 | * Copyright (c) 2022 Vendicated 4 | * Licensed under the Open Software License version 3.0 5 | */ 6 | 7 | import { Patch } from "./patch"; 8 | import { PatchContext } from "./PatchContext"; 9 | import type { Patcher } from "./Patcher"; 10 | import { Func } from "./types"; 11 | 12 | export class PatchInfo { 13 | public patches = [] as Patch[]; 14 | 15 | public constructor( 16 | public readonly patcher: Patcher, 17 | public readonly targetObject: T, 18 | public readonly methodName: string, 19 | public readonly original: Func 20 | ) { } 21 | 22 | public get patchCount() { 23 | return this.patches.length; 24 | } 25 | 26 | public addPatch(patch: Patch) { 27 | if (this.patches.includes(patch)) return false; 28 | this.patches.push(patch); 29 | this.patches.sort((a, b) => b.priority - a.priority); 30 | return true; 31 | } 32 | 33 | public removePatch(patch: Patch) { 34 | const idx = this.patches.indexOf(patch); 35 | if (idx === -1) return false; 36 | this.patches.splice(idx, 1); 37 | return true; 38 | } 39 | 40 | public makeReplacementFunc() { 41 | const _this = this; 42 | return function (this: any, ...args: any[]) { 43 | return _this._callback(this, ...args); 44 | }; 45 | } 46 | 47 | private _callback(thisObject: any, ...args: any[]) { 48 | const { patches } = this; 49 | 50 | if (!patches.length) 51 | return this.original.call(thisObject, ...args); 52 | 53 | const ctx = new PatchContext(thisObject, args); 54 | 55 | let idx = 0; 56 | do { 57 | try { 58 | patches[idx].before(ctx, ...ctx.args); 59 | } catch (err: any) { 60 | this.patcher.handleError("before", this, err, patches[idx]); 61 | 62 | ctx.result = null; 63 | ctx._returnEarly = false; 64 | continue; 65 | } 66 | 67 | if (ctx._returnEarly) { 68 | idx++; 69 | break; 70 | } 71 | } while (++idx < patches.length); 72 | 73 | if (!ctx._returnEarly) { 74 | try { 75 | ctx.result = this.original.call(ctx.thisObject, ...ctx.args); 76 | } catch (err: any) { 77 | ctx.error = err; 78 | } 79 | } 80 | 81 | idx--; 82 | do { 83 | const lastResult = ctx.result; 84 | const lastError = ctx.error; 85 | 86 | try { 87 | patches[idx].after(ctx, ...ctx.args); 88 | } catch (err: any) { 89 | this.patcher.handleError("after", this, err, patches[idx]); 90 | 91 | if (lastError !== null) { 92 | ctx.error = lastError; 93 | } else { 94 | ctx.result = lastResult; 95 | } 96 | } 97 | } while (--idx >= 0); 98 | 99 | return ctx.resultOrError; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jsposed 2 | 3 | A javascript patching library inspired by Xposed 4 | 5 | ### Installing 6 | 7 | ```sh 8 | npm install jsposed 9 | 10 | pnpm add jsposed 11 | 12 | yarn add jsposed 13 | ``` 14 | 15 | ### Usage 16 | 17 | #### Basics 18 | Import the Patcher class from jsposed: 19 | ```js 20 | import { Patcher } from "jsposed"; 21 | ``` 22 | 23 | Create a new instance. The name is used for logging errors and optional (defaults to "JsPosed") 24 | ```js 25 | const patcher = new Patcher("MyPatcherName"); 26 | ``` 27 | 28 | The patcher will catch all errors thrown by patch callbacks and by default print them with a lot of info. 29 | If you want to use your own logger or do more with the errors, you may pass your own error callback: 30 | ```js 31 | const patcher = new Patcher("MyPatcher", (kind, info, error, patch) => { 32 | console.error("Oh no :( Very bad thing happened while patching", info.methodName); 33 | }) 34 | ``` 35 | 36 | #### Patch types 37 | There are three types of patches: 38 | - before: Runs **before** the original method and optionally may skip calling the original method by setting context.result (more info below) 39 | - instead: Runs **instead of** the original method. This will entirely skip the original method and requires you to return your own result if applicable 40 | - after: Runs **after** the original method and thus has access to the result returned by it 41 | 42 | You can either use the shortcut methods `patcher.before`, `patcher.instead` and `patcher.after`...: 43 | ```js 44 | patcher.before(window, "open", (context) => { 45 | console.log("before window.open", context.args[0]); 46 | context.args[0] = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"; 47 | }); 48 | 49 | patcher.after(window, "open", (context) => { 50 | console.log("after window.open", context.result) 51 | }) 52 | ``` 53 | 54 | ...or if you want to use both before and after more concisely, you may also create your own patch directly: 55 | ```js 56 | patcher.patch(window, "open", new Patch({ 57 | before(context) { }, 58 | after(context) { } 59 | })) 60 | ``` 61 | 62 | #### The Patch callback 63 | 64 | What all patches have in common is how their callback works: 65 | - The first argument is a context that contains a lot of info about the invoked function and allows you to alter the arguments and result (see table below) 66 | - For convenience and a nicer typescript experience, the arguments passed to the original function are also available as arg 2, 3, ..., n 67 | 68 | ##### PatchContext 69 | 70 | | Field | Description | in before patches | in after patches | 71 | |-------|-------------|--------|-------| 72 | | thisObject | The `this` value of this function call | - | - | 73 | | args | The arguments passed in this function call | Changing this array will change the arguments passed to the original | - | 74 | | result | The result (return value) of this function call | Always unset. Setting it to anything (including undefined) will skip the original function | Always set. Changing it will change what's eventually returned | 75 | | error | The error thrown by the original function | Always unset. Setting it to a non-nullish value will skip the original function and **throw** that value | Might be set if the original function threw an error. Setting it to a non-nullish value will **throw** that value. Resetting it to a nullish value will prevent errors from being thrown | 76 | | resultOrError | Throws context.error if non-nullish, otherwise returns context.result | Always unset | Always set 77 | 78 | ### Examples 79 | 80 | For more examples on real functions, check [tests/index.js](tests/index.js) 81 | 82 | ```js 83 | const { Patcher } = require("jsposed"); 84 | 85 | const patcher = new Patcher(); 86 | 87 | // Replace an argument before a method is called 88 | const unpatch = patcher.before(someObject, "someMethod", (context, arg1) => { 89 | context.args[0] = "Replace some arg"; 90 | }); 91 | 92 | // Remove that patch again 93 | unpatch.unpatch(); 94 | 95 | // Skip a method conditionally 96 | patcher.before(someObject, "slap", (context, person) => { 97 | if (person.name === "Tom") { 98 | // Hey now, who in their right mind would slap poor Tom 99 | 100 | // Lets skip this method 101 | context.result = null; // Or some other value, this will be returned to the caller 102 | 103 | // Or maybe you would want to throw an error? 104 | context.error = new Error("DO NOT THE TOM!!"); 105 | } 106 | }); 107 | 108 | // Replace a method entirely 109 | patcher.instead(someObject, "someMethod", (context) => { 110 | context.thisObject.doSomeOtherThing(); 111 | }) 112 | 113 | // Call the original method 114 | // If you really really want to slap Tom, then I guess go ahead... 115 | patcher.callOriginal(someObject.slap, someObject, "Tom"); 116 | 117 | // Now remove all patches again 118 | patcher.unpatchAll(); 119 | ``` 120 | -------------------------------------------------------------------------------- /src/Patcher.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * JsPosed, a Javascript patching library inspired by Xposed 3 | * Copyright (c) 2022 Vendicated 4 | * Licensed under the Open Software License version 3.0 5 | */ 6 | 7 | import { Patch, PatchPriority, Unpatch } from "./patch"; 8 | import { PatchInfo } from "./PatchInfo"; 9 | import { InsteadFn, OnPatchError, PatchFn } from "./types"; 10 | 11 | const patchInfoSym = Symbol.for("jsposed.patchInfo"); 12 | 13 | function getMethod(obj: any, methodName: String) { 14 | if (obj == null) 15 | throw new Error("obj may not be null or undefined"); 16 | 17 | const method = obj[methodName as any]; 18 | if (method == null) 19 | throw new Error("No such method: " + methodName); 20 | 21 | if (typeof method !== "function") 22 | throw new Error(methodName + " is not a function"); 23 | 24 | return method; 25 | } 26 | 27 | export class Patcher { 28 | /** 29 | * @param name A custom error for this patcher. This will be used for logging errors 30 | * @param handleError A custom error handler. If not specified, `console.error` will be used to print the following info: 31 | * - patcher name (defaults to "JsPosed") 32 | * - method name 33 | * - the caught error 34 | * - the patch callback that threw this error 35 | */ 36 | public constructor( 37 | public readonly name = "JsPosed", 38 | handleError?: OnPatchError 39 | ) { 40 | if (handleError) 41 | this.handleError = handleError; 42 | } 43 | 44 | public handleError(kind: "before" | "after", info: PatchInfo, err: any, patch: Patch) { 45 | console.error( 46 | `[Patcher<${this.name}>] Error in ${kind} patch of method "${info.methodName}"\n`, 47 | err, 48 | "\nFaulty Callback:", 49 | patch[kind] 50 | ); 51 | } 52 | 53 | private _unpatches = [] as Unpatch[]; 54 | 55 | /** 56 | * Call the original method, bypassing all patches 57 | * @param method The method to call 58 | * @param thisObject The `this` to call the method with 59 | * @param args The arguments to pass to the method 60 | * @returns Result of the method 61 | */ 62 | public callOriginal(method: (...args: any[]) => T, thisObject: any, ...args: any[]): T { 63 | if (typeof method !== "function") 64 | throw new Error("method must be a function"); 65 | 66 | const actual = (method[patchInfoSym as keyof typeof method] as PatchInfo)?.original ?? method; 67 | return actual.call(thisObject, ...args); 68 | } 69 | 70 | /** 71 | * Patch a method 72 | * @param obj Object holding the method 73 | * @param methodName Name of the method 74 | * @param patch Patch 75 | * @returns Unpatch 76 | */ 77 | public patch(obj: T, methodName: string, patch: Patch) { 78 | const method = getMethod(obj, methodName); 79 | let patchInfo = method[patchInfoSym] as PatchInfo; 80 | if (!patchInfo) { 81 | patchInfo = new PatchInfo(this, obj, methodName, method); 82 | 83 | const replacement = (obj as any)[methodName] = patchInfo.makeReplacementFunc(); 84 | Object.defineProperties(replacement, Object.getOwnPropertyDescriptors(method)); 85 | Object.defineProperty(replacement, patchInfoSym, { 86 | value: patchInfo, 87 | enumerable: false, 88 | writable: true, 89 | configurable: true 90 | }); 91 | } 92 | 93 | patchInfo.addPatch(patch); 94 | const unpatch = new Unpatch(this, obj, methodName, patch); 95 | this._unpatches.push(unpatch); 96 | return unpatch; 97 | } 98 | 99 | /** 100 | * Remove a patch 101 | * @param obj Object holding the method 102 | * @param methodName Method name 103 | * @param patch Patch to remove 104 | */ 105 | public unpatch(obj: T, methodName: string, patch: Patch) { 106 | const method = getMethod(obj, methodName); 107 | const patchInfo = method[patchInfoSym] as PatchInfo; 108 | if (patchInfo) { 109 | patchInfo.removePatch(patch); 110 | if (patchInfo.patchCount === 0) { 111 | (obj as any)[methodName] = patchInfo.original; 112 | } 113 | } 114 | } 115 | 116 | /** 117 | * Remove all patches 118 | */ 119 | public unpatchAll() { 120 | for (const unpatch of this._unpatches) { 121 | unpatch.unpatch(); 122 | } 123 | this._unpatches = []; 124 | } 125 | 126 | /** 127 | * Add a patch that will run before the original method 128 | * @param obj Object holding the method 129 | * @param methodName Method name 130 | * @param before Patch 131 | * @param priority Patch priority 132 | * @returns 133 | */ 134 | public before(obj: T, methodName: string, before: PatchFn, priority = PatchPriority.DEFAULT): Unpatch { 135 | return this.patch(obj, methodName, new Patch({ before, priority })); 136 | } 137 | 138 | /** 139 | * Add a patch that will run instead of the original method 140 | * @param obj Object holding the method 141 | * @param methodName Method name 142 | * @param instead Patch 143 | * @param priority Patch priority 144 | * @returns 145 | */ 146 | public instead(obj: T, methodName: string, instead: InsteadFn, priority = PatchPriority.DEFAULT) { 147 | return this.patch(obj, methodName, new Patch({ instead, priority })); 148 | } 149 | 150 | /** 151 | * Add a patch that will run after the original method 152 | * @param obj Object holding the method 153 | * @param methodName Method name 154 | * @param after Patch 155 | * @param priority Patch priority 156 | * @returns 157 | */ 158 | public after(obj: any, methodName: string, after: PatchFn, priority = PatchPriority.DEFAULT): Unpatch { 159 | return this.patch(obj, methodName, new Patch({ after, priority })); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Open Software License ("OSL") v. 3.0 2 | 3 | This Open Software License (the "License") applies to any original work of 4 | authorship (the "Original Work") whose owner (the "Licensor") has placed the 5 | following licensing notice adjacent to the copyright notice for the Original 6 | Work: 7 | 8 | Licensed under the Open Software License version 3.0 9 | 10 | 1) Grant of Copyright License. Licensor grants You a worldwide, royalty-free, 11 | non-exclusive, sublicensable license, for the duration of the copyright, to do 12 | the following: 13 | 14 | a) to reproduce the Original Work in copies, either alone or as part of a 15 | collective work; 16 | 17 | b) to translate, adapt, alter, transform, modify, or arrange the Original 18 | Work, thereby creating derivative works ("Derivative Works") based upon the 19 | Original Work; 20 | 21 | c) to distribute or communicate copies of the Original Work and Derivative 22 | Works to the public, with the proviso that copies of Original Work or 23 | Derivative Works that You distribute or communicate shall be licensed under 24 | this Open Software License; 25 | 26 | d) to perform the Original Work publicly; and 27 | 28 | e) to display the Original Work publicly. 29 | 30 | 2) Grant of Patent License. Licensor grants You a worldwide, royalty-free, 31 | non-exclusive, sublicensable license, under patent claims owned or controlled 32 | by the Licensor that are embodied in the Original Work as furnished by the 33 | Licensor, for the duration of the patents, to make, use, sell, offer for sale, 34 | have made, and import the Original Work and Derivative Works. 35 | 36 | 3) Grant of Source Code License. The term "Source Code" means the preferred 37 | form of the Original Work for making modifications to it and all available 38 | documentation describing how to modify the Original Work. Licensor agrees to 39 | provide a machine-readable copy of the Source Code of the Original Work along 40 | with each copy of the Original Work that Licensor distributes. Licensor 41 | reserves the right to satisfy this obligation by placing a machine-readable 42 | copy of the Source Code in an information repository reasonably calculated to 43 | permit inexpensive and convenient access by You for as long as Licensor 44 | continues to distribute the Original Work. 45 | 46 | 4) Exclusions From License Grant. Neither the names of Licensor, nor the names 47 | of any contributors to the Original Work, nor any of their trademarks or 48 | service marks, may be used to endorse or promote products derived from this 49 | Original Work without express prior permission of the Licensor. Except as 50 | expressly stated herein, nothing in this License grants any license to 51 | Licensor's trademarks, copyrights, patents, trade secrets or any other 52 | intellectual property. No patent license is granted to make, use, sell, offer 53 | for sale, have made, or import embodiments of any patent claims other than the 54 | licensed claims defined in Section 2. No license is granted to the trademarks 55 | of Licensor even if such marks are included in the Original Work. Nothing in 56 | this License shall be interpreted to prohibit Licensor from licensing under 57 | terms different from this License any Original Work that Licensor otherwise 58 | would have a right to license. 59 | 60 | 5) External Deployment. The term "External Deployment" means the use, 61 | distribution, or communication of the Original Work or Derivative Works in any 62 | way such that the Original Work or Derivative Works may be used by anyone 63 | other than You, whether those works are distributed or communicated to those 64 | persons or made available as an application intended for use over a network. 65 | As an express condition for the grants of license hereunder, You must treat 66 | any External Deployment by You of the Original Work or a Derivative Work as a 67 | distribution under section 1(c). 68 | 69 | 6) Attribution Rights. You must retain, in the Source Code of any Derivative 70 | Works that You create, all copyright, patent, or trademark notices from the 71 | Source Code of the Original Work, as well as any notices of licensing and any 72 | descriptive text identified therein as an "Attribution Notice." You must cause 73 | the Source Code for any Derivative Works that You create to carry a prominent 74 | Attribution Notice reasonably calculated to inform recipients that You have 75 | modified the Original Work. 76 | 77 | 7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that 78 | the copyright in and to the Original Work and the patent rights granted herein 79 | by Licensor are owned by the Licensor or are sublicensed to You under the 80 | terms of this License with the permission of the contributor(s) of those 81 | copyrights and patent rights. Except as expressly stated in the immediately 82 | preceding sentence, the Original Work is provided under this License on an "AS 83 | IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without 84 | limitation, the warranties of non-infringement, merchantability or fitness for 85 | a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK 86 | IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this 87 | License. No license to the Original Work is granted by this License except 88 | under this disclaimer. 89 | 90 | 8) Limitation of Liability. Under no circumstances and under no legal theory, 91 | whether in tort (including negligence), contract, or otherwise, shall the 92 | Licensor be liable to anyone for any indirect, special, incidental, or 93 | consequential damages of any character arising as a result of this License or 94 | the use of the Original Work including, without limitation, damages for loss 95 | of goodwill, work stoppage, computer failure or malfunction, or any and all 96 | other commercial damages or losses. This limitation of liability shall not 97 | apply to the extent applicable law prohibits such limitation. 98 | 99 | 9) Acceptance and Termination. If, at any time, You expressly assented to this 100 | License, that assent indicates your clear and irrevocable acceptance of this 101 | License and all of its terms and conditions. If You distribute or communicate 102 | copies of the Original Work or a Derivative Work, You must make a reasonable 103 | effort under the circumstances to obtain the express assent of recipients to 104 | the terms of this License. This License conditions your rights to undertake 105 | the activities listed in Section 1, including your right to create Derivative 106 | Works based upon the Original Work, and doing so without honoring these terms 107 | and conditions is prohibited by copyright law and international treaty. 108 | Nothing in this License is intended to affect copyright exceptions and 109 | limitations (including "fair use" or "fair dealing"). This License shall 110 | terminate immediately and You may no longer exercise any of the rights granted 111 | to You by this License upon your failure to honor the conditions in Section 112 | 1(c). 113 | 114 | 10) Termination for Patent Action. This License shall terminate automatically 115 | and You may no longer exercise any of the rights granted to You by this 116 | License as of the date You commence an action, including a cross-claim or 117 | counterclaim, against Licensor or any licensee alleging that the Original Work 118 | infringes a patent. This termination provision shall not apply for an action 119 | alleging patent infringement by combinations of the Original Work with other 120 | software or hardware. 121 | 122 | 11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this 123 | License may be brought only in the courts of a jurisdiction wherein the 124 | Licensor resides or in which Licensor conducts its primary business, and under 125 | the laws of that jurisdiction excluding its conflict-of-law provisions. The 126 | application of the United Nations Convention on Contracts for the 127 | International Sale of Goods is expressly excluded. Any use of the Original 128 | Work outside the scope of this License or after its termination shall be 129 | subject to the requirements and penalties of copyright or patent law in the 130 | appropriate jurisdiction. This section shall survive the termination of this 131 | License. 132 | 133 | 12) Attorneys' Fees. In any action to enforce the terms of this License or 134 | seeking damages relating thereto, the prevailing party shall be entitled to 135 | recover its costs and expenses, including, without limitation, reasonable 136 | attorneys' fees and costs incurred in connection with such action, including 137 | any appeal of such action. This section shall survive the termination of this 138 | License. 139 | 140 | 13) Miscellaneous. If any provision of this License is held to be 141 | unenforceable, such provision shall be reformed only to the extent necessary 142 | to make it enforceable. 143 | 144 | 14) Definition of "You" in This License. "You" throughout this License, 145 | whether in upper or lower case, means an individual or a legal entity 146 | exercising rights under, and complying with all of the terms of, this License. 147 | For legal entities, "You" includes any entity that controls, is controlled by, 148 | or is under common control with you. For purposes of this definition, 149 | "control" means (i) the power, direct or indirect, to cause the direction or 150 | management of such entity, whether by contract or otherwise, or (ii) ownership 151 | of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial 152 | ownership of such entity. 153 | 154 | 15) Right to Use. You may use the Original Work in all ways not otherwise 155 | restricted or conditioned by this License or by law, and Licensor promises not 156 | to interfere with or be responsible for such uses by You. 157 | 158 | 16) Modification of This License. This License is Copyright © 2005 Lawrence 159 | Rosen. Permission is granted to copy, distribute, or communicate this License 160 | without modification. Nothing in this License permits You to modify this 161 | License as applied to the Original Work or to Derivative Works. However, You 162 | may modify the text of this License and copy, distribute or communicate your 163 | modified version (the "Modified License") and apply it to other original works 164 | of authorship subject to the following conditions: (i) You may not indicate in 165 | any way that your Modified License is the "Open Software License" or "OSL" and 166 | you may not use those names in the name of your Modified License; (ii) You 167 | must replace the notice specified in the first paragraph above with the notice 168 | "Licensed under " or with a notice of your own 169 | that is not confusingly similar to the notice in this License; and (iii) You 170 | may not claim that your original works are open source software unless your 171 | Modified License has been approved by Open Source Initiative (OSI) and You 172 | comply with its license review and certification process. --------------------------------------------------------------------------------