@proto-kit/common 0.1.1-develop.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.
Files changed (150) hide show
  1. package/LICENSE.md +201 -0
  2. package/dist/compiling/AtomicCompileHelper.d.ts +13 -0
  3. package/dist/compiling/AtomicCompileHelper.d.ts.map +1 -0
  4. package/dist/compiling/AtomicCompileHelper.js +40 -0
  5. package/dist/compiling/AtomicCompileHelper.js.map +1 -0
  6. package/dist/compiling/CompilableModule.d.ts +6 -0
  7. package/dist/compiling/CompilableModule.d.ts.map +1 -0
  8. package/dist/compiling/CompilableModule.js +2 -0
  9. package/dist/compiling/CompilableModule.js.map +1 -0
  10. package/dist/compiling/CompileRegistry.d.ts +26 -0
  11. package/dist/compiling/CompileRegistry.d.ts.map +1 -0
  12. package/dist/compiling/CompileRegistry.js +68 -0
  13. package/dist/compiling/CompileRegistry.js.map +1 -0
  14. package/dist/compiling/services/ChildVerificationKeyService.d.ts +10 -0
  15. package/dist/compiling/services/ChildVerificationKeyService.d.ts.map +1 -0
  16. package/dist/compiling/services/ChildVerificationKeyService.js +27 -0
  17. package/dist/compiling/services/ChildVerificationKeyService.js.map +1 -0
  18. package/dist/config/ChildContainerCreatable.d.ts +5 -0
  19. package/dist/config/ChildContainerCreatable.d.ts.map +1 -0
  20. package/dist/config/ChildContainerCreatable.js +2 -0
  21. package/dist/config/ChildContainerCreatable.js.map +1 -0
  22. package/dist/config/ChildContainerProvider.d.ts +5 -0
  23. package/dist/config/ChildContainerProvider.d.ts.map +1 -0
  24. package/dist/config/ChildContainerProvider.js +2 -0
  25. package/dist/config/ChildContainerProvider.js.map +1 -0
  26. package/dist/config/ConfigurableModule.d.ts +25 -0
  27. package/dist/config/ConfigurableModule.d.ts.map +1 -0
  28. package/dist/config/ConfigurableModule.js +24 -0
  29. package/dist/config/ConfigurableModule.js.map +1 -0
  30. package/dist/config/ModuleContainer.d.ts +162 -0
  31. package/dist/config/ModuleContainer.d.ts.map +1 -0
  32. package/dist/config/ModuleContainer.js +289 -0
  33. package/dist/config/ModuleContainer.js.map +1 -0
  34. package/dist/config/injectAlias.d.ts +18 -0
  35. package/dist/config/injectAlias.d.ts.map +1 -0
  36. package/dist/config/injectAlias.js +47 -0
  37. package/dist/config/injectAlias.js.map +1 -0
  38. package/dist/dependencyFactory/DependencyFactory.d.ts +29 -0
  39. package/dist/dependencyFactory/DependencyFactory.d.ts.map +1 -0
  40. package/dist/dependencyFactory/DependencyFactory.js +2 -0
  41. package/dist/dependencyFactory/DependencyFactory.js.map +1 -0
  42. package/dist/dependencyFactory/injectOptional.d.ts +16 -0
  43. package/dist/dependencyFactory/injectOptional.d.ts.map +1 -0
  44. package/dist/dependencyFactory/injectOptional.js +40 -0
  45. package/dist/dependencyFactory/injectOptional.js.map +1 -0
  46. package/dist/dummyVerificationKey.d.ts +3 -0
  47. package/dist/dummyVerificationKey.d.ts.map +1 -0
  48. package/dist/dummyVerificationKey.js +8 -0
  49. package/dist/dummyVerificationKey.js.map +1 -0
  50. package/dist/events/EventEmitter.d.ts +19 -0
  51. package/dist/events/EventEmitter.d.ts.map +1 -0
  52. package/dist/events/EventEmitter.js +35 -0
  53. package/dist/events/EventEmitter.js.map +1 -0
  54. package/dist/events/EventEmitterProxy.d.ts +18 -0
  55. package/dist/events/EventEmitterProxy.d.ts.map +1 -0
  56. package/dist/events/EventEmitterProxy.js +35 -0
  57. package/dist/events/EventEmitterProxy.js.map +1 -0
  58. package/dist/events/EventEmittingComponent.d.ts +9 -0
  59. package/dist/events/EventEmittingComponent.d.ts.map +1 -0
  60. package/dist/events/EventEmittingComponent.js +2 -0
  61. package/dist/events/EventEmittingComponent.js.map +1 -0
  62. package/dist/events/ReplayingSingleUseEventEmitter.d.ts +17 -0
  63. package/dist/events/ReplayingSingleUseEventEmitter.d.ts.map +1 -0
  64. package/dist/events/ReplayingSingleUseEventEmitter.js +34 -0
  65. package/dist/events/ReplayingSingleUseEventEmitter.js.map +1 -0
  66. package/dist/index.d.ts +26 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +26 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/log.d.ts +37 -0
  71. package/dist/log.d.ts.map +1 -0
  72. package/dist/log.js +114 -0
  73. package/dist/log.js.map +1 -0
  74. package/dist/trees/InMemoryMerkleTreeStorage.d.ts +11 -0
  75. package/dist/trees/InMemoryMerkleTreeStorage.d.ts.map +1 -0
  76. package/dist/trees/InMemoryMerkleTreeStorage.js +13 -0
  77. package/dist/trees/InMemoryMerkleTreeStorage.js.map +1 -0
  78. package/dist/trees/MerkleTreeStore.d.ts +5 -0
  79. package/dist/trees/MerkleTreeStore.d.ts.map +1 -0
  80. package/dist/trees/MerkleTreeStore.js +2 -0
  81. package/dist/trees/MerkleTreeStore.js.map +1 -0
  82. package/dist/trees/MockAsyncMerkleStore.d.ts +9 -0
  83. package/dist/trees/MockAsyncMerkleStore.d.ts.map +1 -0
  84. package/dist/trees/MockAsyncMerkleStore.js +20 -0
  85. package/dist/trees/MockAsyncMerkleStore.js.map +1 -0
  86. package/dist/trees/RollupMerkleTree.d.ts +147 -0
  87. package/dist/trees/RollupMerkleTree.d.ts.map +1 -0
  88. package/dist/trees/RollupMerkleTree.js +218 -0
  89. package/dist/trees/RollupMerkleTree.js.map +1 -0
  90. package/dist/trees/VirtualMerkleTreeStore.d.ts +13 -0
  91. package/dist/trees/VirtualMerkleTreeStore.d.ts.map +1 -0
  92. package/dist/trees/VirtualMerkleTreeStore.js +18 -0
  93. package/dist/trees/VirtualMerkleTreeStore.js.map +1 -0
  94. package/dist/types.d.ts +27 -0
  95. package/dist/types.d.ts.map +1 -0
  96. package/dist/types.js +12 -0
  97. package/dist/types.js.map +1 -0
  98. package/dist/utils.d.ts +48 -0
  99. package/dist/utils.d.ts.map +1 -0
  100. package/dist/utils.js +113 -0
  101. package/dist/utils.js.map +1 -0
  102. package/dist/zkProgrammable/ProvableMethodExecutionContext.d.ts +54 -0
  103. package/dist/zkProgrammable/ProvableMethodExecutionContext.d.ts.map +1 -0
  104. package/dist/zkProgrammable/ProvableMethodExecutionContext.js +97 -0
  105. package/dist/zkProgrammable/ProvableMethodExecutionContext.js.map +1 -0
  106. package/dist/zkProgrammable/ZkProgrammable.d.ts +40 -0
  107. package/dist/zkProgrammable/ZkProgrammable.d.ts.map +1 -0
  108. package/dist/zkProgrammable/ZkProgrammable.js +79 -0
  109. package/dist/zkProgrammable/ZkProgrammable.js.map +1 -0
  110. package/dist/zkProgrammable/provableMethod.d.ts +19 -0
  111. package/dist/zkProgrammable/provableMethod.d.ts.map +1 -0
  112. package/dist/zkProgrammable/provableMethod.js +71 -0
  113. package/dist/zkProgrammable/provableMethod.js.map +1 -0
  114. package/jest.config.cjs +12 -0
  115. package/package.json +34 -0
  116. package/src/compiling/AtomicCompileHelper.ts +62 -0
  117. package/src/compiling/CompilableModule.ts +6 -0
  118. package/src/compiling/CompileRegistry.ts +78 -0
  119. package/src/compiling/services/ChildVerificationKeyService.ts +26 -0
  120. package/src/config/ChildContainerCreatable.ts +5 -0
  121. package/src/config/ChildContainerProvider.ts +5 -0
  122. package/src/config/ConfigurableModule.ts +57 -0
  123. package/src/config/ModuleContainer.ts +487 -0
  124. package/src/config/injectAlias.ts +70 -0
  125. package/src/dependencyFactory/DependencyFactory.ts +57 -0
  126. package/src/dependencyFactory/injectOptional.ts +41 -0
  127. package/src/dummyVerificationKey.ts +10 -0
  128. package/src/events/EventEmitter.ts +61 -0
  129. package/src/events/EventEmitterProxy.ts +83 -0
  130. package/src/events/EventEmittingComponent.ts +11 -0
  131. package/src/events/ReplayingSingleUseEventEmitter.ts +42 -0
  132. package/src/index.ts +25 -0
  133. package/src/log.ts +143 -0
  134. package/src/trees/InMemoryMerkleTreeStorage.ts +17 -0
  135. package/src/trees/MerkleTreeStore.ts +5 -0
  136. package/src/trees/MockAsyncMerkleStore.ts +30 -0
  137. package/src/trees/RollupMerkleTree.ts +356 -0
  138. package/src/trees/VirtualMerkleTreeStore.ts +20 -0
  139. package/src/types.ts +58 -0
  140. package/src/utils.ts +200 -0
  141. package/src/zkProgrammable/ProvableMethodExecutionContext.ts +122 -0
  142. package/src/zkProgrammable/ZkProgrammable.ts +151 -0
  143. package/src/zkProgrammable/provableMethod.ts +124 -0
  144. package/test/config/ContainerEvents.test.ts +67 -0
  145. package/test/config/ModuleContainer.test.ts +215 -0
  146. package/test/config/injectAlias.test.ts +28 -0
  147. package/test/trees/MerkleTree.test.ts +106 -0
  148. package/test/tsconfig.json +7 -0
  149. package/test/zkProgrammable/ZkProgrammable.test.ts +304 -0
  150. package/tsconfig.json +8 -0
@@ -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,151 @@
1
+ import { ZkProgram, FlexibleProvablePure, Proof, Field, Provable } from "o1js";
2
+ import { Memoize } from "typescript-memoize";
3
+
4
+ import { log } from "../log";
5
+ import { dummyVerificationKey } from "../dummyVerificationKey";
6
+ import { reduceSequential } from "../utils";
7
+ import type { CompileRegistry } from "../compiling/CompileRegistry";
8
+
9
+ import { MOCK_PROOF } from "./provableMethod";
10
+
11
+ const errors = {
12
+ areProofsEnabledNotSet: (name: string) =>
13
+ new Error(`AreProofsEnabled was not injected for: ${name}`),
14
+ };
15
+
16
+ export interface CompileArtifact {
17
+ verificationKey: {
18
+ data: string;
19
+ hash: Field;
20
+ };
21
+ }
22
+
23
+ export interface AreProofsEnabled {
24
+ areProofsEnabled: boolean;
25
+ setProofsEnabled: (areProofsEnabled: boolean) => void;
26
+ }
27
+
28
+ export interface Verify<PublicInput, PublicOutput> {
29
+ (proof: Proof<PublicInput, PublicOutput>): Promise<boolean>;
30
+ }
31
+
32
+ export interface Compile {
33
+ (): Promise<CompileArtifact>;
34
+ }
35
+
36
+ export interface PlainZkProgram<PublicInput = undefined, PublicOutput = void> {
37
+ name: string;
38
+ compile: Compile;
39
+ verify: Verify<PublicInput, PublicOutput>;
40
+ Proof: ReturnType<
41
+ typeof ZkProgram.Proof<
42
+ FlexibleProvablePure<PublicInput>,
43
+ FlexibleProvablePure<PublicOutput>
44
+ >
45
+ >;
46
+ methods: Record<
47
+ string,
48
+ | ((...args: any) => Promise<Proof<PublicInput, PublicOutput>>)
49
+ | ((
50
+ publicInput: PublicInput,
51
+ ...args: any
52
+ ) => Promise<Proof<PublicInput, PublicOutput>>)
53
+ >;
54
+ analyzeMethods: () => Promise<
55
+ Record<string, Awaited<ReturnType<typeof Provable.constraintSystem>>>
56
+ >;
57
+ }
58
+
59
+ export function verifyToMockable<PublicInput, PublicOutput>(
60
+ verify: Verify<PublicInput, PublicOutput>,
61
+ { areProofsEnabled }: AreProofsEnabled
62
+ ) {
63
+ return async (proof: Proof<PublicInput, PublicOutput>) => {
64
+ if (areProofsEnabled) {
65
+ let verified = false;
66
+
67
+ try {
68
+ verified = await verify(proof);
69
+ } catch (error: unknown) {
70
+ // silently fail verification
71
+ log.error(error);
72
+ verified = false;
73
+ }
74
+
75
+ return verified;
76
+ }
77
+
78
+ return proof.proof === MOCK_PROOF;
79
+ };
80
+ }
81
+
82
+ export const MOCK_VERIFICATION_KEY = dummyVerificationKey();
83
+
84
+ export function compileToMockable(
85
+ compile: Compile,
86
+ { areProofsEnabled }: AreProofsEnabled
87
+ ): () => Promise<CompileArtifact> {
88
+ return async () => {
89
+ if (areProofsEnabled) {
90
+ return await compile();
91
+ }
92
+
93
+ return {
94
+ verificationKey: MOCK_VERIFICATION_KEY,
95
+ };
96
+ };
97
+ }
98
+
99
+ export abstract class ZkProgrammable<
100
+ PublicInput = undefined,
101
+ PublicOutput = void,
102
+ > {
103
+ public abstract get areProofsEnabled(): AreProofsEnabled | undefined;
104
+
105
+ public abstract zkProgramFactory(): PlainZkProgram<
106
+ PublicInput,
107
+ PublicOutput
108
+ >[];
109
+
110
+ private zkProgramSingleton?: PlainZkProgram<PublicInput, PublicOutput>[];
111
+
112
+ @Memoize()
113
+ public get zkProgram(): PlainZkProgram<PublicInput, PublicOutput>[] {
114
+ if (this.zkProgramSingleton === undefined) {
115
+ this.zkProgramSingleton = this.zkProgramFactory();
116
+ }
117
+
118
+ return this.zkProgramSingleton.map((bucket) => {
119
+ if (!this.areProofsEnabled) {
120
+ throw errors.areProofsEnabledNotSet(this.constructor.name);
121
+ }
122
+ return {
123
+ ...bucket,
124
+ verify: verifyToMockable(bucket.verify, this.areProofsEnabled),
125
+ compile: compileToMockable(bucket.compile, this.areProofsEnabled),
126
+ };
127
+ });
128
+ }
129
+
130
+ public async compile(registry: CompileRegistry) {
131
+ return await reduceSequential(
132
+ this.zkProgram,
133
+ async (acc, program) => {
134
+ const result = await registry.compile(program);
135
+ return {
136
+ ...acc,
137
+ [program.name]: result,
138
+ };
139
+ },
140
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
141
+ {} as Record<string, CompileArtifact>
142
+ );
143
+ }
144
+ }
145
+
146
+ export interface WithZkProgrammable<
147
+ PublicInput = undefined,
148
+ PublicOutput = void,
149
+ > {
150
+ zkProgrammable: ZkProgrammable<PublicInput, PublicOutput>;
151
+ }
@@ -0,0 +1,124 @@
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
+ // (await Proof.dummy(Field(0), Field(0), 2)).proof as string;
19
+
20
+ export function toProver(
21
+ methodName: string,
22
+ simulatedMethod: DecoratedMethod,
23
+ isFirstParameterPublicInput: boolean,
24
+ ...args: ArgumentTypes
25
+ ) {
26
+ return async function prover(this: ZkProgrammable<any, any>) {
27
+ const { areProofsEnabled } = this.areProofsEnabled!;
28
+
29
+ const zkProgram = this.zkProgram.find((prog) =>
30
+ Object.keys(prog.methods).includes(methodName)
31
+ );
32
+
33
+ if (zkProgram === undefined) {
34
+ throw new Error("Correct ZkProgram not found");
35
+ }
36
+
37
+ if (areProofsEnabled) {
38
+ const programProvableMethod = zkProgram.methods[methodName];
39
+ return await Reflect.apply(programProvableMethod, this, args);
40
+ }
41
+
42
+ // create a mock proof by simulating method> execution in JS
43
+ const publicOutput = await Reflect.apply(simulatedMethod, this, args);
44
+
45
+ return new zkProgram.Proof({
46
+ proof: MOCK_PROOF,
47
+
48
+ // TODO: provide undefined if public input is not used
49
+ publicInput: isFirstParameterPublicInput ? args[0] : undefined,
50
+ publicOutput,
51
+
52
+ /**
53
+ * We set this to the max possible number, to avoid having
54
+ * to manually count in-circuit proof verifications
55
+ */
56
+ maxProofsVerified: 2,
57
+ });
58
+ };
59
+ }
60
+
61
+ /**
62
+ * Decorates a provable method on a 'prover class', depending on
63
+ * if proofs are enabled or not, either runs the respective zkProgram prover,
64
+ * or simulates the method execution and issues a mock proof.
65
+ *
66
+ * @param isFirstParameterPublicInput
67
+ * @param executionContext
68
+ * @returns
69
+ */
70
+ export function provableMethod(
71
+ isFirstParameterPublicInput = true,
72
+ executionContext: ProvableMethodExecutionContext = container.resolve<ProvableMethodExecutionContext>(
73
+ ProvableMethodExecutionContext
74
+ )
75
+ ) {
76
+ return <
77
+ Target extends WithZkProgrammable<any, any> | ZkProgrammable<any, any>,
78
+ >(
79
+ target: Target,
80
+ methodName: string,
81
+ descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise<any> | any>
82
+ ) => {
83
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
84
+ const simulatedMethod = descriptor.value as DecoratedMethod;
85
+
86
+ descriptor.value = async function value(
87
+ this: ZkProgrammable<unknown, unknown>,
88
+ ...args: ArgumentTypes
89
+ ) {
90
+ const prover = toProver(
91
+ methodName,
92
+ simulatedMethod,
93
+ isFirstParameterPublicInput,
94
+ ...args
95
+ );
96
+
97
+ executionContext.beforeMethod(this.constructor.name, methodName, args);
98
+
99
+ /**
100
+ * Check if the method is called at the top level,
101
+ * if yes then create a prover.
102
+ */
103
+ if (executionContext.isTopLevel) {
104
+ executionContext.setProver(prover.bind(this));
105
+ }
106
+
107
+ /**
108
+ * Regardless of if the method is called from the top level
109
+ * or not, execute its simulated (Javascript) version and
110
+ * return the result.
111
+ */
112
+ let result: unknown;
113
+ try {
114
+ result = Reflect.apply(simulatedMethod, this, args);
115
+ } finally {
116
+ executionContext.afterMethod();
117
+ }
118
+
119
+ return result;
120
+ };
121
+
122
+ return descriptor;
123
+ };
124
+ }
@@ -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
+ });
@@ -0,0 +1,215 @@
1
+ import "reflect-metadata";
2
+ import { container as tsyringeContainer, inject, injectable } from "tsyringe";
3
+
4
+ import {
5
+ ConfigurableModule,
6
+ NoConfig,
7
+ } from "../../src/config/ConfigurableModule";
8
+ import {
9
+ ModuleContainer,
10
+ ModulesRecord,
11
+ } from "../../src/config/ModuleContainer";
12
+ import { TypedClass } from "../../src/types";
13
+ import { DependencyFactory, expectDefined } from "../../src";
14
+ import { injectAlias } from "../../src/config/injectAlias";
15
+
16
+ // module container will accept modules that extend this type
17
+ class BaseTestModule<Config> extends ConfigurableModule<Config> {}
18
+
19
+ type TestModulesRecord = ModulesRecord<TypedClass<BaseTestModule<unknown>>>;
20
+
21
+ interface TestModuleConfig {
22
+ testConfigProperty: number;
23
+ testConfigProperty2?: number;
24
+ testConfigProperty3?: number;
25
+ }
26
+
27
+ @injectable()
28
+ @injectAlias(["child-alias", "multi-alias"])
29
+ class ChildModule extends BaseTestModule<NoConfig> {
30
+ public constructor(@inject("TestModule") public readonly testModule: any) {
31
+ super();
32
+ }
33
+
34
+ x() {
35
+ return "dependency factory works";
36
+ }
37
+ }
38
+
39
+ @injectAlias(["base-alias", "multi-alias"])
40
+ class TestModule
41
+ extends BaseTestModule<TestModuleConfig>
42
+ implements DependencyFactory
43
+ {
44
+ public dependencies() {
45
+ return {
46
+ dependencyModule1: {
47
+ useClass: ChildModule,
48
+ },
49
+ };
50
+ }
51
+ }
52
+
53
+ interface OtherTestModuleConfig {
54
+ otherTestConfigProperty: number;
55
+ }
56
+
57
+ class OtherTestModule extends BaseTestModule<OtherTestModuleConfig> {
58
+ public x() {
59
+ return "";
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Showcases a wrongly typed/defined module as
65
+ * per the TestModuleContainer requirements
66
+ */
67
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
68
+ class WrongTestModule {}
69
+
70
+ class TestModuleContainer<
71
+ Modules extends TestModulesRecord,
72
+ > extends ModuleContainer<Modules> {
73
+ public get dependencyContainer() {
74
+ return this.container;
75
+ }
76
+ }
77
+
78
+ describe("moduleContainer", () => {
79
+ let container: TestModuleContainer<{
80
+ TestModule: typeof TestModule;
81
+ OtherTestModule: typeof OtherTestModule;
82
+ }>;
83
+ const testConfigProperty = 0;
84
+
85
+ beforeEach(() => {
86
+ container = new TestModuleContainer({
87
+ modules: {
88
+ TestModule,
89
+ OtherTestModule,
90
+ // this module would not be assignable to TestModuleContainer
91
+ // WrongTestModule,
92
+ },
93
+ });
94
+ });
95
+
96
+ it("should resolve dependency factory dependencies correctly", () => {
97
+ container.configure({
98
+ TestModule: {
99
+ testConfigProperty,
100
+ },
101
+
102
+ OtherTestModule: {
103
+ otherTestConfigProperty: testConfigProperty,
104
+ },
105
+ });
106
+
107
+ container.create(() => tsyringeContainer.createChildContainer());
108
+
109
+ // Unfortunately we still need this so that the dependencies are registered
110
+ container.resolve("TestModule");
111
+ const dm = container.resolve("DependencyModule1");
112
+
113
+ expect(dm.x()).toBe("dependency factory works");
114
+ expect(dm.testModule).toBeDefined();
115
+ });
116
+
117
+ it("should throw on resolution, if config was not provided", () => {
118
+ expect.assertions(1);
119
+
120
+ container.create(() => tsyringeContainer.createChildContainer());
121
+
122
+ expect(() => {
123
+ container.resolve("TestModule");
124
+ }).toThrow();
125
+ });
126
+
127
+ it("should resolve the registered module with the provided config", () => {
128
+ expect.assertions(1);
129
+
130
+ container.configure({
131
+ TestModule: {
132
+ testConfigProperty,
133
+ },
134
+
135
+ OtherTestModule: {
136
+ otherTestConfigProperty: testConfigProperty,
137
+ },
138
+ });
139
+ container.create(() => tsyringeContainer.createChildContainer());
140
+
141
+ const testModule = container.resolve("TestModule");
142
+
143
+ expect(testModule.config.testConfigProperty).toBe(testConfigProperty);
144
+
145
+ const dependency = container.resolve("DependencyModule1");
146
+ dependency.x();
147
+ });
148
+
149
+ it("should stack configurations correctly", () => {
150
+ container.configure({
151
+ TestModule: {
152
+ testConfigProperty: 1,
153
+ },
154
+ OtherTestModule: {
155
+ otherTestConfigProperty: 4,
156
+ },
157
+ });
158
+
159
+ container.configurePartial({
160
+ TestModule: {
161
+ testConfigProperty2: 2,
162
+ },
163
+ });
164
+
165
+ container.configurePartial({
166
+ TestModule: {
167
+ testConfigProperty: 3,
168
+ },
169
+ });
170
+
171
+ container.create(() => tsyringeContainer.createChildContainer());
172
+
173
+ const { config } = container.resolve("TestModule");
174
+
175
+ expect(config.testConfigProperty).toBe(3);
176
+ expect(config.testConfigProperty2).toBe(2);
177
+ expect(config.testConfigProperty3).toBe(undefined);
178
+ });
179
+
180
+ it("should resolve dependencies correctly via alias", () => {
181
+ container.configure({
182
+ TestModule: {
183
+ testConfigProperty,
184
+ },
185
+
186
+ OtherTestModule: {
187
+ otherTestConfigProperty: testConfigProperty,
188
+ },
189
+ });
190
+
191
+ container.create(() => tsyringeContainer.createChildContainer());
192
+
193
+ // Unfortunately we still need this so that the dependencies are registered
194
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
195
+ const m1 = container.resolve("base-alias" as any);
196
+ const m2 = container.resolve("TestModule");
197
+
198
+ expectDefined(m1);
199
+ // Check if its the same reference
200
+ expect(m1).toBe(m2);
201
+
202
+ const dm1 = container.resolve("child-alias" as any) as ChildModule;
203
+ const dm2 = container.resolve("DependencyModule1");
204
+
205
+ expect(dm1.x()).toBe("dependency factory works");
206
+ expect(dm1.testModule).toBeDefined();
207
+ expect(dm1).toBe(dm2);
208
+
209
+ const multi =
210
+ container.dependencyContainer.resolveAll<BaseTestModule<unknown>>(
211
+ "multi-alias"
212
+ );
213
+ expect(multi).toHaveLength(2);
214
+ });
215
+ });
@@ -0,0 +1,28 @@
1
+ import "reflect-metadata";
2
+ import { getInjectAliases, injectAlias } from "../../src/config/injectAlias";
3
+
4
+ @injectAlias(["foo", "bar"])
5
+ class TestClass {}
6
+
7
+ @injectAlias(["ayy"])
8
+ class TestClass2 extends TestClass {}
9
+
10
+ describe("injectAlias metadata", () => {
11
+ it("set and retrieve", () => {
12
+ expect.assertions(2);
13
+
14
+ const aliases = getInjectAliases(TestClass);
15
+
16
+ expect(aliases).toHaveLength(2);
17
+ expect(aliases).toStrictEqual(["foo", "bar"]);
18
+ });
19
+
20
+ it("recursive", () => {
21
+ expect.assertions(2);
22
+
23
+ const aliases = getInjectAliases(TestClass2);
24
+
25
+ expect(aliases).toHaveLength(3);
26
+ expect(aliases).toStrictEqual(["ayy", "foo", "bar"]);
27
+ });
28
+ });