@proto-kit/common 0.1.1-develop.1086

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.
Files changed (96) hide show
  1. package/LICENSE.md +201 -0
  2. package/dist/config/ChildContainerCreatable.d.ts +5 -0
  3. package/dist/config/ChildContainerCreatable.d.ts.map +1 -0
  4. package/dist/config/ChildContainerCreatable.js +1 -0
  5. package/dist/config/ChildContainerProvider.d.ts +5 -0
  6. package/dist/config/ChildContainerProvider.d.ts.map +1 -0
  7. package/dist/config/ChildContainerProvider.js +1 -0
  8. package/dist/config/ConfigurableModule.d.ts +25 -0
  9. package/dist/config/ConfigurableModule.d.ts.map +1 -0
  10. package/dist/config/ConfigurableModule.js +23 -0
  11. package/dist/config/ModuleContainer.d.ts +161 -0
  12. package/dist/config/ModuleContainer.d.ts.map +1 -0
  13. package/dist/config/ModuleContainer.js +278 -0
  14. package/dist/dependencyFactory/DependencyFactory.d.ts +29 -0
  15. package/dist/dependencyFactory/DependencyFactory.d.ts.map +1 -0
  16. package/dist/dependencyFactory/DependencyFactory.js +1 -0
  17. package/dist/dependencyFactory/injectOptional.d.ts +16 -0
  18. package/dist/dependencyFactory/injectOptional.d.ts.map +1 -0
  19. package/dist/dependencyFactory/injectOptional.js +39 -0
  20. package/dist/events/EventEmitter.d.ts +19 -0
  21. package/dist/events/EventEmitter.d.ts.map +1 -0
  22. package/dist/events/EventEmitter.js +34 -0
  23. package/dist/events/EventEmitterProxy.d.ts +17 -0
  24. package/dist/events/EventEmitterProxy.d.ts.map +1 -0
  25. package/dist/events/EventEmitterProxy.js +23 -0
  26. package/dist/events/EventEmittingComponent.d.ts +6 -0
  27. package/dist/events/EventEmittingComponent.d.ts.map +1 -0
  28. package/dist/events/EventEmittingComponent.js +1 -0
  29. package/dist/index.d.ts +20 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +19 -0
  32. package/dist/log.d.ts +20 -0
  33. package/dist/log.d.ts.map +1 -0
  34. package/dist/log.js +75 -0
  35. package/dist/test/equalProvable.d.ts +20 -0
  36. package/dist/test/equalProvable.d.ts.map +1 -0
  37. package/dist/test/equalProvable.js +13 -0
  38. package/dist/trees/InMemoryMerkleTreeStorage.d.ts +11 -0
  39. package/dist/trees/InMemoryMerkleTreeStorage.d.ts.map +1 -0
  40. package/dist/trees/InMemoryMerkleTreeStorage.js +12 -0
  41. package/dist/trees/MerkleTreeStore.d.ts +5 -0
  42. package/dist/trees/MerkleTreeStore.d.ts.map +1 -0
  43. package/dist/trees/MerkleTreeStore.js +1 -0
  44. package/dist/trees/MockAsyncMerkleStore.d.ts +9 -0
  45. package/dist/trees/MockAsyncMerkleStore.d.ts.map +1 -0
  46. package/dist/trees/MockAsyncMerkleStore.js +19 -0
  47. package/dist/trees/RollupMerkleTree.d.ts +147 -0
  48. package/dist/trees/RollupMerkleTree.d.ts.map +1 -0
  49. package/dist/trees/RollupMerkleTree.js +217 -0
  50. package/dist/trees/VirtualMerkleTreeStore.d.ts +13 -0
  51. package/dist/trees/VirtualMerkleTreeStore.d.ts.map +1 -0
  52. package/dist/trees/VirtualMerkleTreeStore.js +17 -0
  53. package/dist/types.d.ts +26 -0
  54. package/dist/types.d.ts.map +1 -0
  55. package/dist/types.js +11 -0
  56. package/dist/utils.d.ts +35 -0
  57. package/dist/utils.d.ts.map +1 -0
  58. package/dist/utils.js +73 -0
  59. package/dist/zkProgrammable/ProvableMethodExecutionContext.d.ts +54 -0
  60. package/dist/zkProgrammable/ProvableMethodExecutionContext.d.ts.map +1 -0
  61. package/dist/zkProgrammable/ProvableMethodExecutionContext.js +96 -0
  62. package/dist/zkProgrammable/ZkProgrammable.d.ts +39 -0
  63. package/dist/zkProgrammable/ZkProgrammable.d.ts.map +1 -0
  64. package/dist/zkProgrammable/ZkProgrammable.js +67 -0
  65. package/dist/zkProgrammable/provableMethod.d.ts +19 -0
  66. package/dist/zkProgrammable/provableMethod.d.ts.map +1 -0
  67. package/dist/zkProgrammable/provableMethod.js +73 -0
  68. package/jest.config.cjs +1 -0
  69. package/package.json +34 -0
  70. package/src/config/ChildContainerCreatable.ts +5 -0
  71. package/src/config/ChildContainerProvider.ts +5 -0
  72. package/src/config/ConfigurableModule.ts +57 -0
  73. package/src/config/ModuleContainer.ts +472 -0
  74. package/src/dependencyFactory/DependencyFactory.ts +57 -0
  75. package/src/dependencyFactory/injectOptional.ts +41 -0
  76. package/src/events/EventEmitter.ts +61 -0
  77. package/src/events/EventEmitterProxy.ts +59 -0
  78. package/src/events/EventEmittingComponent.ts +7 -0
  79. package/src/index.ts +19 -0
  80. package/src/log.ts +97 -0
  81. package/src/trees/InMemoryMerkleTreeStorage.ts +17 -0
  82. package/src/trees/MerkleTreeStore.ts +5 -0
  83. package/src/trees/MockAsyncMerkleStore.ts +30 -0
  84. package/src/trees/RollupMerkleTree.ts +356 -0
  85. package/src/trees/VirtualMerkleTreeStore.ts +20 -0
  86. package/src/types.ts +49 -0
  87. package/src/utils.ts +149 -0
  88. package/src/zkProgrammable/ProvableMethodExecutionContext.ts +122 -0
  89. package/src/zkProgrammable/ZkProgrammable.ts +131 -0
  90. package/src/zkProgrammable/provableMethod.ts +123 -0
  91. package/test/config/ContainerEvents.test.ts +67 -0
  92. package/test/config/ModuleContainer.test.ts +172 -0
  93. package/test/trees/MerkleTree.test.ts +106 -0
  94. package/test/tsconfig.json +7 -0
  95. package/test/zkProgrammable/ZkProgrammable.test.ts +304 -0
  96. package/tsconfig.json +8 -0
package/src/utils.ts ADDED
@@ -0,0 +1,149 @@
1
+ import {
2
+ Field,
3
+ FlexibleProvablePure,
4
+ Poseidon,
5
+ DynamicProof,
6
+ Proof,
7
+ } from "o1js";
8
+
9
+ export function requireTrue(
10
+ condition: boolean,
11
+ errorOrFunction: Error | (() => Error)
12
+ ): void {
13
+ if (!condition) {
14
+ throw typeof errorOrFunction === "function"
15
+ ? errorOrFunction()
16
+ : errorOrFunction;
17
+ }
18
+ }
19
+
20
+ export function range(
21
+ startOrEnd: number,
22
+ endOrNothing?: number | undefined
23
+ ): number[] {
24
+ let end = endOrNothing;
25
+ let start = startOrEnd;
26
+ if (end === undefined) {
27
+ end = startOrEnd;
28
+ start = 0;
29
+ }
30
+ return Array.from({ length: end - start }, (ignored, index) => index + start);
31
+ }
32
+
33
+ export function reduceSequential<T, U>(
34
+ array: T[],
35
+ callbackfn: (
36
+ previousValue: U,
37
+ currentValue: T,
38
+ currentIndex: number,
39
+ array: T[]
40
+ ) => Promise<U>,
41
+ initialValue: U
42
+ ) {
43
+ return array.reduce<Promise<U>>(
44
+ async (previousPromise, current, index, arr) => {
45
+ const previous = await previousPromise;
46
+ return await callbackfn(previous, current, index, arr);
47
+ },
48
+ Promise.resolve(initialValue)
49
+ );
50
+ }
51
+
52
+ export function mapSequential<T, R>(
53
+ array: T[],
54
+ f: (element: T, index: number, array: T[]) => Promise<R>
55
+ ) {
56
+ return array.reduce<Promise<R[]>>(async (r, element, index, a) => {
57
+ const ret = await r;
58
+ const next = await f(element, index, a);
59
+ ret.push(next);
60
+ return ret;
61
+ }, Promise.resolve([]));
62
+ }
63
+
64
+ /**
65
+ * Computes a dummy value for the given value type.
66
+ *
67
+ * @param valueType - Value type to generate the dummy value for
68
+ * @returns Dummy value for the given value type
69
+ */
70
+ export function dummyValue<Value>(
71
+ valueType: FlexibleProvablePure<Value>
72
+ ): Value {
73
+ const length = valueType.sizeInFields();
74
+ const fields = Array.from({ length }, () => Field(0));
75
+
76
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
77
+ return valueType.fromFields(fields) as Value;
78
+ }
79
+
80
+ export function noop(): void {}
81
+
82
+ export interface ToFieldable {
83
+ toFields: () => Field[];
84
+ }
85
+
86
+ export interface ToFieldableStatic {
87
+ toFields: (value: unknown) => Field[];
88
+ }
89
+
90
+ export interface ToJSONableStatic {
91
+ toJSON: (value: unknown) => any;
92
+ }
93
+
94
+ // export interface ProofTypes {
95
+ // publicOutputType?: ToFieldableStatic;
96
+ // publicInputType?: ToFieldableStatic;
97
+ // }
98
+
99
+ export type ProofTypes =
100
+ | typeof Proof<unknown, unknown>
101
+ | typeof DynamicProof<unknown, unknown>;
102
+
103
+ export async function sleep(ms: number) {
104
+ await new Promise((resolve) => {
105
+ setTimeout(resolve, ms);
106
+ });
107
+ }
108
+
109
+ export function filterNonNull<Type>(value: Type | null): value is Type {
110
+ return value !== null;
111
+ }
112
+
113
+ export function filterNonUndefined<Type>(
114
+ value: Type | undefined
115
+ ): value is Type {
116
+ return value !== undefined;
117
+ }
118
+
119
+ const encoder = new TextEncoder();
120
+
121
+ // Copied from o1js binable.ts:317
122
+ export function prefixToField(prefix: string): Field {
123
+ const fieldSize = Field.sizeInBytes;
124
+ if (prefix.length >= fieldSize) throw Error("prefix too long");
125
+ const stringBytes = [...encoder.encode(prefix)];
126
+ return Field.fromBytes(
127
+ stringBytes.concat(Array(fieldSize - stringBytes.length).fill(0))
128
+ );
129
+ }
130
+
131
+ export function hashWithPrefix(prefix: string, input: Field[]) {
132
+ const salt = Poseidon.update(
133
+ [Field(0), Field(0), Field(0)],
134
+ [prefixToField(prefix)]
135
+ );
136
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
137
+ return Poseidon.update(salt as [Field, Field, Field], input)[0];
138
+ }
139
+
140
+ // end copy
141
+
142
+ export function expectDefined<T>(value: T | undefined): asserts value is T {
143
+ expect(value).toBeDefined();
144
+ }
145
+
146
+ type NonMethodKeys<Type> = {
147
+ [Key in keyof Type]: Type[Key] extends Function ? never : Key;
148
+ }[keyof Type];
149
+ export type NonMethods<Type> = Pick<Type, NonMethodKeys<Type>>;
@@ -0,0 +1,122 @@
1
+ import type { Proof } from "o1js";
2
+ import { singleton } from "tsyringe";
3
+ import uniqueId from "lodash/uniqueId";
4
+
5
+ import type { ArgumentTypes } from "./provableMethod";
6
+
7
+ const errors = {
8
+ moduleOrMethodNameNotSet: () => new Error("Module or method name not set"),
9
+
10
+ proverNotSet: (moduleName: string, methodName: string) =>
11
+ new Error(
12
+ `Prover not set for '${moduleName}.${methodName}', did you forget to decorate your method?`
13
+ ),
14
+ };
15
+
16
+ export class ProvableMethodExecutionResult {
17
+ public moduleName?: string;
18
+
19
+ public methodName?: string;
20
+
21
+ public args?: ArgumentTypes;
22
+
23
+ public prover?: () => Promise<Proof<unknown, unknown>>;
24
+
25
+ public async prove<
26
+ ProofType extends Proof<unknown, unknown>,
27
+ >(): Promise<ProofType> {
28
+ if (!this.prover) {
29
+ // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
30
+ if (!this.moduleName || !this.methodName) {
31
+ throw errors.moduleOrMethodNameNotSet();
32
+ }
33
+ throw errors.proverNotSet(this.moduleName, this.methodName);
34
+ }
35
+
36
+ // turn the prover result into the desired proof type
37
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
38
+ return (await this.prover()) as ProofType;
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Execution context used to wrap runtime module methods,
44
+ * allowing them to post relevant information (such as execution status)
45
+ * into the context without any unnecessary 'prop drilling'.
46
+ */
47
+ @singleton()
48
+ export class ProvableMethodExecutionContext {
49
+ public id = uniqueId();
50
+
51
+ public methods: string[] = [];
52
+
53
+ public result: ProvableMethodExecutionResult =
54
+ new ProvableMethodExecutionResult();
55
+
56
+ // TODO See if we should make this class generic, bc I think we can persist the type
57
+ /**
58
+ * Adds a method prover to the current execution context,
59
+ * which can be collected and ran asynchronously at a later point in time.
60
+ *
61
+ * @param prove - Prover function to be ran later,
62
+ * when the method execution needs to be proven
63
+ */
64
+ public setProver(prover: () => Promise<Proof<unknown, unknown>>) {
65
+ this.result.prover = prover;
66
+ }
67
+
68
+ /**
69
+ * Adds a method to the method execution stack, reseting the execution context
70
+ * in a case a new top-level (non nested) method call is made.
71
+ *
72
+ * @param methodName - Name of the method being captured in the context
73
+ */
74
+ public beforeMethod(
75
+ moduleName: string,
76
+ methodName: string,
77
+ args: ArgumentTypes
78
+ ) {
79
+ if (this.isFinished) {
80
+ this.clear();
81
+ this.result.moduleName = moduleName;
82
+ this.result.methodName = methodName;
83
+ this.result.args = args;
84
+ }
85
+
86
+ this.methods.push(methodName);
87
+ }
88
+
89
+ /**
90
+ * Removes the latest method from the execution context stack,
91
+ * keeping track of the amount of 'unfinished' methods. Allowing
92
+ * for the context to distinguish between top-level and nested method calls.
93
+ */
94
+ public afterMethod() {
95
+ this.methods.pop();
96
+ }
97
+
98
+ public get isTopLevel() {
99
+ return this.methods.length === 1;
100
+ }
101
+
102
+ public get isFinished() {
103
+ return this.methods.length === 0;
104
+ }
105
+
106
+ /**
107
+ * @returns - Current execution context state
108
+ */
109
+ public current() {
110
+ return {
111
+ isFinished: this.isFinished,
112
+ result: this.result,
113
+ };
114
+ }
115
+
116
+ /**
117
+ * Manually clears/resets the execution context
118
+ */
119
+ public clear() {
120
+ this.result = new ProvableMethodExecutionResult();
121
+ }
122
+ }
@@ -0,0 +1,131 @@
1
+ import { ZkProgram, FlexibleProvablePure, Proof, Field, Provable } from "o1js";
2
+ import { Memoize } from "typescript-memoize";
3
+
4
+ import { log } from "../log";
5
+
6
+ import { MOCK_PROOF } from "./provableMethod";
7
+
8
+ const errors = {
9
+ appChainNotSet: (name: string) =>
10
+ new Error(`Appchain was not injected for: ${name}`),
11
+ };
12
+
13
+ export interface CompileArtifact {
14
+ verificationKey: {
15
+ data: string;
16
+ hash: Field;
17
+ };
18
+ }
19
+
20
+ export interface AreProofsEnabled {
21
+ areProofsEnabled: boolean;
22
+ setProofsEnabled: (areProofsEnabled: boolean) => void;
23
+ }
24
+
25
+ export interface Verify<PublicInput, PublicOutput> {
26
+ (proof: Proof<PublicInput, PublicOutput>): Promise<boolean>;
27
+ }
28
+
29
+ export interface Compile {
30
+ (): Promise<CompileArtifact>;
31
+ }
32
+
33
+ export interface PlainZkProgram<PublicInput = undefined, PublicOutput = void> {
34
+ compile: Compile;
35
+ verify: Verify<PublicInput, PublicOutput>;
36
+ Proof: ReturnType<
37
+ typeof ZkProgram.Proof<
38
+ FlexibleProvablePure<PublicInput>,
39
+ FlexibleProvablePure<PublicOutput>
40
+ >
41
+ >;
42
+ methods: Record<
43
+ string,
44
+ | ((...args: any) => Promise<Proof<PublicInput, PublicOutput>>)
45
+ | ((
46
+ publicInput: PublicInput,
47
+ ...args: any
48
+ ) => Promise<Proof<PublicInput, PublicOutput>>)
49
+ >;
50
+ analyzeMethods: () => Promise<
51
+ Record<string, Awaited<ReturnType<typeof Provable.constraintSystem>>>
52
+ >;
53
+ }
54
+
55
+ export function verifyToMockable<PublicInput, PublicOutput>(
56
+ verify: Verify<PublicInput, PublicOutput>,
57
+ { areProofsEnabled }: AreProofsEnabled
58
+ ) {
59
+ return async (proof: Proof<PublicInput, PublicOutput>) => {
60
+ if (areProofsEnabled) {
61
+ let verified = false;
62
+
63
+ try {
64
+ verified = await verify(proof);
65
+ } catch (error: unknown) {
66
+ // silently fail verification
67
+ log.error(error);
68
+ verified = false;
69
+ }
70
+
71
+ return verified;
72
+ }
73
+
74
+ return proof.proof === MOCK_PROOF;
75
+ };
76
+ }
77
+
78
+ export const MOCK_VERIFICATION_KEY = {
79
+ data: "mock-verification-key",
80
+ hash: Field(0),
81
+ };
82
+
83
+ export function compileToMockable(
84
+ compile: Compile,
85
+ { areProofsEnabled }: AreProofsEnabled
86
+ ): () => Promise<CompileArtifact> {
87
+ return async () => {
88
+ if (areProofsEnabled) {
89
+ return await compile();
90
+ }
91
+
92
+ return {
93
+ verificationKey: MOCK_VERIFICATION_KEY,
94
+ };
95
+ };
96
+ }
97
+
98
+ export abstract class ZkProgrammable<
99
+ PublicInput = undefined,
100
+ PublicOutput = void,
101
+ > {
102
+ public abstract get appChain(): AreProofsEnabled | undefined;
103
+
104
+ public abstract zkProgramFactory(): PlainZkProgram<
105
+ PublicInput,
106
+ PublicOutput
107
+ >[];
108
+
109
+ @Memoize()
110
+ public get zkProgram(): PlainZkProgram<PublicInput, PublicOutput>[] {
111
+ const zkProgram = this.zkProgramFactory();
112
+
113
+ return zkProgram.map((bucket) => {
114
+ if (!this.appChain) {
115
+ throw errors.appChainNotSet(this.constructor.name);
116
+ }
117
+ return {
118
+ ...bucket,
119
+ verify: verifyToMockable(bucket.verify, this.appChain),
120
+ compile: compileToMockable(bucket.compile, this.appChain),
121
+ };
122
+ });
123
+ }
124
+ }
125
+
126
+ export interface WithZkProgrammable<
127
+ PublicInput = undefined,
128
+ PublicOutput = void,
129
+ > {
130
+ zkProgrammable: ZkProgrammable<PublicInput, PublicOutput>;
131
+ }
@@ -0,0 +1,123 @@
1
+ import { Proof, DynamicProof } from "o1js";
2
+ import { container } from "tsyringe";
3
+
4
+ import { ProvableMethodExecutionContext } from "./ProvableMethodExecutionContext";
5
+ import type { WithZkProgrammable, ZkProgrammable } from "./ZkProgrammable";
6
+
7
+ // Now, in o1js provable types are normal js objects, therefore any
8
+ export type O1JSPrimitive = object | string | boolean | number;
9
+ export type ArgumentTypes = (
10
+ | O1JSPrimitive
11
+ | Proof<unknown, unknown>
12
+ | DynamicProof<unknown, unknown>
13
+ )[];
14
+
15
+ export type DecoratedMethod = (...args: ArgumentTypes) => Promise<unknown>;
16
+
17
+ export const MOCK_PROOF = "mock-proof";
18
+
19
+ export function toProver(
20
+ methodName: string,
21
+ simulatedMethod: DecoratedMethod,
22
+ isFirstParameterPublicInput: boolean,
23
+ ...args: ArgumentTypes
24
+ ) {
25
+ return async function prover(this: ZkProgrammable<any, any>) {
26
+ const areProofsEnabled = this.appChain?.areProofsEnabled;
27
+ if (areProofsEnabled ?? false) {
28
+ for (const prog of this.zkProgram) {
29
+ if (Object.keys(prog.methods).includes(methodName)) {
30
+ const programProvableMethod = prog.methods[methodName];
31
+ // eslint-disable-next-line no-await-in-loop
32
+ return await Reflect.apply(programProvableMethod, this, args);
33
+ }
34
+ }
35
+ }
36
+
37
+ // create a mock proof by simulating method execution in JS
38
+ const publicOutput = await Reflect.apply(simulatedMethod, this, args);
39
+ const zkProgram =
40
+ this.zkProgram.find((prog) => {
41
+ return Object.keys(prog.methods).includes(methodName);
42
+ }) ?? this.zkProgram[0];
43
+
44
+ return new zkProgram.Proof({
45
+ proof: MOCK_PROOF,
46
+
47
+ // TODO: provide undefined if public input is not used
48
+ publicInput: isFirstParameterPublicInput ? args[0] : undefined,
49
+ publicOutput,
50
+
51
+ /**
52
+ * We set this to the max possible number, to avoid having
53
+ * to manually count in-circuit proof verifications
54
+ */
55
+ maxProofsVerified: 2,
56
+ });
57
+ };
58
+ }
59
+
60
+ /**
61
+ * Decorates a provable method on a 'prover class', depending on
62
+ * if proofs are enabled or not, either runs the respective zkProgram prover,
63
+ * or simulates the method execution and issues a mock proof.
64
+ *
65
+ * @param isFirstParameterPublicInput
66
+ * @param executionContext
67
+ * @returns
68
+ */
69
+ export function provableMethod(
70
+ isFirstParameterPublicInput = true,
71
+ executionContext: ProvableMethodExecutionContext = container.resolve<ProvableMethodExecutionContext>(
72
+ ProvableMethodExecutionContext
73
+ )
74
+ ) {
75
+ return <
76
+ Target extends WithZkProgrammable<any, any> | ZkProgrammable<any, any>,
77
+ >(
78
+ target: Target,
79
+ methodName: string,
80
+ descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise<any> | any>
81
+ ) => {
82
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
83
+ const simulatedMethod = descriptor.value as DecoratedMethod;
84
+
85
+ descriptor.value = async function value(
86
+ this: ZkProgrammable<unknown, unknown>,
87
+ ...args: ArgumentTypes
88
+ ) {
89
+ const prover = toProver(
90
+ methodName,
91
+ simulatedMethod,
92
+ isFirstParameterPublicInput,
93
+ ...args
94
+ );
95
+
96
+ executionContext.beforeMethod(this.constructor.name, methodName, args);
97
+
98
+ /**
99
+ * Check if the method is called at the top level,
100
+ * if yes then create a prover.
101
+ */
102
+ if (executionContext.isTopLevel) {
103
+ executionContext.setProver(prover.bind(this));
104
+ }
105
+
106
+ /**
107
+ * Regardless of if the method is called from the top level
108
+ * or not, execute its simulated (Javascript) version and
109
+ * return the result.
110
+ */
111
+ let result: unknown;
112
+ try {
113
+ result = Reflect.apply(simulatedMethod, this, args);
114
+ } finally {
115
+ executionContext.afterMethod();
116
+ }
117
+
118
+ return result;
119
+ };
120
+
121
+ return descriptor;
122
+ };
123
+ }
@@ -0,0 +1,67 @@
1
+ import "reflect-metadata";
2
+ import { injectable, container as tsyringeContainer } from "tsyringe";
3
+
4
+ import {
5
+ BaseModuleInstanceType,
6
+ ConfigurableModule,
7
+ EventEmitter,
8
+ EventEmittingComponent,
9
+ ModuleContainer,
10
+ ModulesRecord,
11
+ } from "../../src";
12
+
13
+ class TestContainer<
14
+ Modules extends ModulesRecord,
15
+ > extends ModuleContainer<Modules> {}
16
+
17
+ type TestEvents = {
18
+ test: [string];
19
+ };
20
+
21
+ @injectable()
22
+ class TestModule
23
+ extends ConfigurableModule<{}>
24
+ implements BaseModuleInstanceType, EventEmittingComponent<TestEvents>
25
+ {
26
+ events = new EventEmitter<TestEvents>();
27
+ }
28
+
29
+ type TestEvents2 = {
30
+ test2: [number];
31
+ };
32
+
33
+ class TestModule2
34
+ extends ConfigurableModule<{}>
35
+ implements BaseModuleInstanceType, EventEmittingComponent<TestEvents2>
36
+ {
37
+ events = new EventEmitter<TestEvents2>();
38
+ }
39
+
40
+ describe("test event propagation", () => {
41
+ it("should propagate events up", () => {
42
+ expect.assertions(1);
43
+
44
+ const container = new TestContainer({
45
+ modules: {
46
+ test: TestModule,
47
+ test2: TestModule2,
48
+ },
49
+ });
50
+
51
+ container.configure({
52
+ test: {},
53
+ test2: {},
54
+ });
55
+
56
+ container.create(() => tsyringeContainer.createChildContainer());
57
+
58
+ const testvalue = "testString123";
59
+
60
+ container.events.on("test", (value: string) => {
61
+ expect(value).toStrictEqual(testvalue);
62
+ });
63
+
64
+ const module = container.resolve("test");
65
+ module.events.emit("test", testvalue);
66
+ });
67
+ });