@proto-kit/common 0.1.1-develop.165 → 0.1.1-develop.1665

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 (249) hide show
  1. package/dist/compiling/AtomicCompileHelper.d.ts +13 -0
  2. package/dist/compiling/AtomicCompileHelper.d.ts.map +1 -0
  3. package/dist/compiling/AtomicCompileHelper.js +40 -0
  4. package/dist/compiling/AtomicCompileHelper.js.map +1 -0
  5. package/dist/compiling/CompilableModule.d.ts +6 -0
  6. package/dist/compiling/CompilableModule.d.ts.map +1 -0
  7. package/dist/compiling/CompilableModule.js +2 -0
  8. package/dist/compiling/CompilableModule.js.map +1 -0
  9. package/dist/compiling/CompileRegistry.d.ts +26 -0
  10. package/dist/compiling/CompileRegistry.d.ts.map +1 -0
  11. package/dist/compiling/CompileRegistry.js +69 -0
  12. package/dist/compiling/CompileRegistry.js.map +1 -0
  13. package/dist/compiling/services/ChildVerificationKeyService.d.ts +10 -0
  14. package/dist/compiling/services/ChildVerificationKeyService.d.ts.map +1 -0
  15. package/dist/compiling/services/ChildVerificationKeyService.js +27 -0
  16. package/dist/compiling/services/ChildVerificationKeyService.js.map +1 -0
  17. package/dist/config/ChildContainerCreatable.d.ts +5 -0
  18. package/dist/config/ChildContainerCreatable.d.ts.map +1 -0
  19. package/dist/config/ChildContainerCreatable.js +2 -0
  20. package/dist/config/ChildContainerCreatable.js.map +1 -0
  21. package/dist/config/ChildContainerProvider.d.ts +5 -0
  22. package/dist/config/ChildContainerProvider.d.ts.map +1 -0
  23. package/dist/config/ChildContainerProvider.js +2 -0
  24. package/dist/config/ChildContainerProvider.js.map +1 -0
  25. package/dist/config/ConfigurableModule.d.ts +6 -2
  26. package/dist/config/ConfigurableModule.d.ts.map +1 -1
  27. package/dist/config/ConfigurableModule.js +5 -0
  28. package/dist/config/ConfigurableModule.js.map +1 -0
  29. package/dist/config/ModuleContainer.d.ts +70 -17
  30. package/dist/config/ModuleContainer.d.ts.map +1 -1
  31. package/dist/config/ModuleContainer.js +164 -22
  32. package/dist/config/ModuleContainer.js.map +1 -0
  33. package/dist/config/Startable.d.ts +4 -0
  34. package/dist/config/Startable.d.ts.map +1 -0
  35. package/dist/config/Startable.js +2 -0
  36. package/dist/config/Startable.js.map +1 -0
  37. package/dist/config/injectAlias.d.ts +18 -0
  38. package/dist/config/injectAlias.d.ts.map +1 -0
  39. package/dist/config/injectAlias.js +47 -0
  40. package/dist/config/injectAlias.js.map +1 -0
  41. package/dist/dependencyFactory/DependencyFactory.d.ts +29 -0
  42. package/dist/dependencyFactory/DependencyFactory.d.ts.map +1 -0
  43. package/dist/dependencyFactory/DependencyFactory.js +2 -0
  44. package/dist/dependencyFactory/DependencyFactory.js.map +1 -0
  45. package/dist/dummyVerificationKey.d.ts +3 -0
  46. package/dist/dummyVerificationKey.d.ts.map +1 -0
  47. package/dist/dummyVerificationKey.js +8 -0
  48. package/dist/dummyVerificationKey.js.map +1 -0
  49. package/dist/events/EventEmitter.d.ts +19 -0
  50. package/dist/events/EventEmitter.d.ts.map +1 -0
  51. package/dist/events/EventEmitter.js +35 -0
  52. package/dist/events/EventEmitter.js.map +1 -0
  53. package/dist/events/EventEmitterProxy.d.ts +18 -0
  54. package/dist/events/EventEmitterProxy.d.ts.map +1 -0
  55. package/dist/events/EventEmitterProxy.js +35 -0
  56. package/dist/events/EventEmitterProxy.js.map +1 -0
  57. package/dist/events/EventEmittingComponent.d.ts +9 -0
  58. package/dist/events/EventEmittingComponent.d.ts.map +1 -0
  59. package/dist/events/EventEmittingComponent.js +2 -0
  60. package/dist/events/EventEmittingComponent.js.map +1 -0
  61. package/dist/events/ReplayingSingleUseEventEmitter.d.ts +17 -0
  62. package/dist/events/ReplayingSingleUseEventEmitter.d.ts.map +1 -0
  63. package/dist/events/ReplayingSingleUseEventEmitter.js +34 -0
  64. package/dist/events/ReplayingSingleUseEventEmitter.js.map +1 -0
  65. package/dist/index.d.ts +24 -1
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +25 -2
  68. package/dist/index.js.map +1 -0
  69. package/dist/log.d.ts +37 -0
  70. package/dist/log.d.ts.map +1 -0
  71. package/dist/log.js +114 -0
  72. package/dist/log.js.map +1 -0
  73. package/dist/trees/lmt/AbstractLinkedMerkleTree.d.ts +288 -0
  74. package/dist/trees/lmt/AbstractLinkedMerkleTree.d.ts.map +1 -0
  75. package/dist/trees/lmt/AbstractLinkedMerkleTree.js +22 -0
  76. package/dist/trees/lmt/AbstractLinkedMerkleTree.js.map +1 -0
  77. package/dist/trees/lmt/InMemoryLinkedLeafStore.d.ts +21 -0
  78. package/dist/trees/lmt/InMemoryLinkedLeafStore.d.ts.map +1 -0
  79. package/dist/trees/lmt/InMemoryLinkedLeafStore.js +26 -0
  80. package/dist/trees/lmt/InMemoryLinkedLeafStore.js.map +1 -0
  81. package/dist/trees/lmt/LinkedLeafStore.d.ts +16 -0
  82. package/dist/trees/lmt/LinkedLeafStore.d.ts.map +1 -0
  83. package/dist/trees/lmt/LinkedLeafStore.js +2 -0
  84. package/dist/trees/lmt/LinkedLeafStore.js.map +1 -0
  85. package/dist/trees/lmt/LinkedMerkleTree.d.ts +11 -0
  86. package/dist/trees/lmt/LinkedMerkleTree.d.ts.map +1 -0
  87. package/dist/trees/lmt/LinkedMerkleTree.js +241 -0
  88. package/dist/trees/lmt/LinkedMerkleTree.js.map +1 -0
  89. package/dist/trees/lmt/LinkedMerkleTreeCircuitOps.d.ts +113 -0
  90. package/dist/trees/lmt/LinkedMerkleTreeCircuitOps.d.ts.map +1 -0
  91. package/dist/trees/lmt/LinkedMerkleTreeCircuitOps.js +113 -0
  92. package/dist/trees/lmt/LinkedMerkleTreeCircuitOps.js.map +1 -0
  93. package/dist/trees/lmt/LinkedMerkleTreeTypes.d.ts +74 -0
  94. package/dist/trees/lmt/LinkedMerkleTreeTypes.d.ts.map +1 -0
  95. package/dist/trees/lmt/LinkedMerkleTreeTypes.js +50 -0
  96. package/dist/trees/lmt/LinkedMerkleTreeTypes.js.map +1 -0
  97. package/dist/trees/sparse/InMemoryMerkleTreeStorage.d.ts +11 -0
  98. package/dist/trees/sparse/InMemoryMerkleTreeStorage.d.ts.map +1 -0
  99. package/dist/trees/sparse/InMemoryMerkleTreeStorage.js +13 -0
  100. package/dist/trees/sparse/InMemoryMerkleTreeStorage.js.map +1 -0
  101. package/dist/trees/sparse/MerkleTreeStore.d.ts +5 -0
  102. package/dist/trees/sparse/MerkleTreeStore.d.ts.map +1 -0
  103. package/dist/trees/sparse/MerkleTreeStore.js +2 -0
  104. package/dist/trees/sparse/MerkleTreeStore.js.map +1 -0
  105. package/dist/trees/sparse/MockAsyncMerkleStore.d.ts +9 -0
  106. package/dist/trees/sparse/MockAsyncMerkleStore.d.ts.map +1 -0
  107. package/dist/trees/sparse/MockAsyncMerkleStore.js +20 -0
  108. package/dist/trees/sparse/MockAsyncMerkleStore.js.map +1 -0
  109. package/dist/trees/sparse/RollupMerkleTree.d.ts +157 -0
  110. package/dist/trees/sparse/RollupMerkleTree.d.ts.map +1 -0
  111. package/dist/trees/sparse/RollupMerkleTree.js +272 -0
  112. package/dist/trees/sparse/RollupMerkleTree.js.map +1 -0
  113. package/dist/types.d.ts +16 -0
  114. package/dist/types.d.ts.map +1 -1
  115. package/dist/types.js +12 -1
  116. package/dist/types.js.map +1 -0
  117. package/dist/utils.d.ts +62 -0
  118. package/dist/utils.d.ts.map +1 -1
  119. package/dist/utils.js +155 -0
  120. package/dist/utils.js.map +1 -0
  121. package/dist/zkProgrammable/ProvableMethodExecutionContext.d.ts +4 -3
  122. package/dist/zkProgrammable/ProvableMethodExecutionContext.d.ts.map +1 -1
  123. package/dist/zkProgrammable/ProvableMethodExecutionContext.js +2 -3
  124. package/dist/zkProgrammable/ProvableMethodExecutionContext.js.map +1 -0
  125. package/dist/zkProgrammable/ZkProgrammable.d.ts +15 -7
  126. package/dist/zkProgrammable/ZkProgrammable.d.ts.map +1 -1
  127. package/dist/zkProgrammable/ZkProgrammable.js +34 -15
  128. package/dist/zkProgrammable/ZkProgrammable.js.map +1 -0
  129. package/dist/zkProgrammable/provableMethod.d.ts +8 -5
  130. package/dist/zkProgrammable/provableMethod.d.ts.map +1 -1
  131. package/dist/zkProgrammable/provableMethod.js +15 -13
  132. package/dist/zkProgrammable/provableMethod.js.map +1 -0
  133. package/jest.config.cjs +12 -1
  134. package/package.json +8 -7
  135. package/src/compiling/AtomicCompileHelper.ts +62 -0
  136. package/src/compiling/CompilableModule.ts +6 -0
  137. package/src/compiling/CompileRegistry.ts +79 -0
  138. package/src/compiling/services/ChildVerificationKeyService.ts +26 -0
  139. package/src/config/ChildContainerCreatable.ts +5 -0
  140. package/src/config/ChildContainerProvider.ts +5 -0
  141. package/src/config/ConfigurableModule.ts +15 -2
  142. package/src/config/ModuleContainer.ts +287 -46
  143. package/src/config/Startable.ts +3 -0
  144. package/src/config/injectAlias.ts +70 -0
  145. package/src/dependencyFactory/DependencyFactory.ts +57 -0
  146. package/src/dummyVerificationKey.ts +10 -0
  147. package/src/events/EventEmitter.ts +61 -0
  148. package/src/events/EventEmitterProxy.ts +81 -0
  149. package/src/events/EventEmittingComponent.ts +11 -0
  150. package/src/events/ReplayingSingleUseEventEmitter.ts +42 -0
  151. package/src/index.ts +24 -2
  152. package/src/log.ts +143 -0
  153. package/src/trees/lmt/AbstractLinkedMerkleTree.ts +102 -0
  154. package/src/trees/lmt/InMemoryLinkedLeafStore.ts +42 -0
  155. package/src/trees/lmt/LinkedLeafStore.ts +13 -0
  156. package/src/trees/lmt/LinkedMerkleTree.ts +335 -0
  157. package/src/trees/lmt/LinkedMerkleTreeCircuitOps.ts +188 -0
  158. package/src/trees/lmt/LinkedMerkleTreeTypes.ts +53 -0
  159. package/src/trees/sparse/InMemoryMerkleTreeStorage.ts +17 -0
  160. package/src/trees/sparse/MerkleTreeStore.ts +5 -0
  161. package/src/trees/sparse/MockAsyncMerkleStore.ts +30 -0
  162. package/src/trees/sparse/RollupMerkleTree.ts +427 -0
  163. package/src/types.ts +43 -2
  164. package/src/utils.ts +287 -0
  165. package/src/zkProgrammable/ProvableMethodExecutionContext.ts +6 -6
  166. package/src/zkProgrammable/ZkProgrammable.ts +61 -29
  167. package/src/zkProgrammable/provableMethod.ts +38 -21
  168. package/test/config/ContainerEvents.test.ts +65 -0
  169. package/test/config/ModuleContainer.test.ts +146 -15
  170. package/test/config/injectAlias.test.ts +28 -0
  171. package/test/trees/LinkedMerkleTree.test.ts +124 -0
  172. package/test/trees/LinkedMerkleTreeCircuitOps.test.ts +147 -0
  173. package/test/trees/MerkleTree.test.ts +220 -0
  174. package/test/tsconfig.json +5 -2
  175. package/test/zkProgrammable/ZkProgrammable.test.ts +135 -114
  176. package/tsconfig.json +1 -1
  177. package/dist/Constants.d.ts +0 -4
  178. package/dist/Constants.d.ts.map +0 -1
  179. package/dist/Constants.js +0 -3
  180. package/dist/config/ConfigurationAggregator.d.ts +0 -10
  181. package/dist/config/ConfigurationAggregator.d.ts.map +0 -1
  182. package/dist/config/ConfigurationAggregator.js +0 -35
  183. package/dist/config/ConfigurationReceiver.d.ts +0 -25
  184. package/dist/config/ConfigurationReceiver.d.ts.map +0 -1
  185. package/dist/config/ConfigurationReceiver.js +0 -36
  186. package/dist/config/types.d.ts +0 -2
  187. package/dist/config/types.d.ts.map +0 -1
  188. package/dist/config/types.js +0 -1
  189. package/dist/model/MethodPublicInput.d.ts +0 -51
  190. package/dist/model/MethodPublicInput.d.ts.map +0 -1
  191. package/dist/model/MethodPublicInput.js +0 -11
  192. package/dist/model/Option.d.ts +0 -89
  193. package/dist/model/Option.d.ts.map +0 -1
  194. package/dist/model/Option.js +0 -86
  195. package/dist/model/Path.d.ts +0 -31
  196. package/dist/model/Path.d.ts.map +0 -1
  197. package/dist/model/Path.js +0 -44
  198. package/dist/model/StateTransition.d.ts +0 -85
  199. package/dist/model/StateTransition.d.ts.map +0 -1
  200. package/dist/model/StateTransition.js +0 -58
  201. package/dist/model/StateTransitionProvableBatch.d.ts +0 -56
  202. package/dist/model/StateTransitionProvableBatch.d.ts.map +0 -1
  203. package/dist/model/StateTransitionProvableBatch.js +0 -20
  204. package/dist/prover/block/BlockProver.d.ts +0 -199
  205. package/dist/prover/block/BlockProver.d.ts.map +0 -1
  206. package/dist/prover/block/BlockProver.js +0 -119
  207. package/dist/prover/block/BlockScopedModule.d.ts +0 -3
  208. package/dist/prover/block/BlockScopedModule.d.ts.map +0 -1
  209. package/dist/prover/block/BlockScopedModule.js +0 -6
  210. package/dist/prover/statetransition/StateTransitionProver.d.ts +0 -92
  211. package/dist/prover/statetransition/StateTransitionProver.d.ts.map +0 -1
  212. package/dist/prover/statetransition/StateTransitionProver.js +0 -127
  213. package/dist/prover/statetransition/StateTransitionWitnessProvider.d.ts +0 -16
  214. package/dist/prover/statetransition/StateTransitionWitnessProvider.d.ts.map +0 -1
  215. package/dist/prover/statetransition/StateTransitionWitnessProvider.js +0 -17
  216. package/dist/src/model/Option.d.ts +0 -158
  217. package/dist/src/model/Option.d.ts.map +0 -1
  218. package/dist/src/model/Option.js +0 -53
  219. package/dist/src/model/Path.d.ts +0 -35
  220. package/dist/src/model/Path.d.ts.map +0 -1
  221. package/dist/src/model/Path.js +0 -51
  222. package/dist/src/model/StateTransition.d.ts +0 -201
  223. package/dist/src/model/StateTransition.d.ts.map +0 -1
  224. package/dist/src/model/StateTransition.js +0 -43
  225. package/dist/src/utils/PrefixedHashList.d.ts +0 -15
  226. package/dist/src/utils/PrefixedHashList.d.ts.map +0 -1
  227. package/dist/src/utils/PrefixedHashList.js +0 -28
  228. package/dist/src/utils/ProvableHashList.d.ts +0 -30
  229. package/dist/src/utils/ProvableHashList.d.ts.map +0 -1
  230. package/dist/src/utils/ProvableHashList.js +0 -43
  231. package/dist/utils/PrefixedHashList.d.ts +0 -14
  232. package/dist/utils/PrefixedHashList.d.ts.map +0 -1
  233. package/dist/utils/PrefixedHashList.js +0 -12
  234. package/dist/utils/PrefixedProvableHashList.d.ts +0 -8
  235. package/dist/utils/PrefixedProvableHashList.d.ts.map +0 -1
  236. package/dist/utils/PrefixedProvableHashList.js +0 -12
  237. package/dist/utils/ProvableHashList.d.ts +0 -26
  238. package/dist/utils/ProvableHashList.d.ts.map +0 -1
  239. package/dist/utils/ProvableHashList.js +0 -35
  240. package/dist/utils/Utils.d.ts +0 -22
  241. package/dist/utils/Utils.d.ts.map +0 -1
  242. package/dist/utils/Utils.js +0 -41
  243. package/dist/utils/merkletree/MemoryMerkleTreeStorage.d.ts +0 -26
  244. package/dist/utils/merkletree/MemoryMerkleTreeStorage.d.ts.map +0 -1
  245. package/dist/utils/merkletree/MemoryMerkleTreeStorage.js +0 -79
  246. package/dist/utils/merkletree/RollupMerkleTree.d.ts +0 -143
  247. package/dist/utils/merkletree/RollupMerkleTree.d.ts.map +0 -1
  248. package/dist/utils/merkletree/RollupMerkleTree.js +0 -246
  249. package/tsconfig.test.json +0 -9
@@ -1,11 +1,34 @@
1
1
  import "reflect-metadata";
2
2
 
3
- import { container, Frequency, InjectionToken, Lifecycle } from "tsyringe";
3
+ import {
4
+ DependencyContainer,
5
+ Frequency,
6
+ InjectionToken,
7
+ instanceCachingFactory,
8
+ isClassProvider,
9
+ isFactoryProvider,
10
+ isTokenProvider,
11
+ isValueProvider,
12
+ Lifecycle,
13
+ } from "tsyringe";
4
14
  import log from "loglevel";
5
-
6
- import { StringKeyOf, TypedClass } from "../types";
7
-
8
- import { Configurable, ConfigurableModule } from "./ConfigurableModule";
15
+ import merge from "lodash/merge";
16
+
17
+ import { MergeObjects, StringKeyOf, TypedClass } from "../types";
18
+ import {
19
+ DependencyFactory,
20
+ InferDependencies,
21
+ } from "../dependencyFactory/DependencyFactory";
22
+ import { EventEmitterProxy } from "../events/EventEmitterProxy";
23
+
24
+ import {
25
+ Configurable,
26
+ ConfigurableModule,
27
+ NoConfig,
28
+ } from "./ConfigurableModule";
29
+ import { ChildContainerProvider } from "./ChildContainerProvider";
30
+ import { ChildContainerCreatable } from "./ChildContainerCreatable";
31
+ import { getInjectAliases } from "./injectAlias";
9
32
 
10
33
  const errors = {
11
34
  configNotSetInContainer: (moduleName: string) =>
@@ -15,8 +38,7 @@ const errors = {
15
38
 
16
39
  onlyValidModuleNames: (moduleName: NonNullable<unknown>) =>
17
40
  new Error(
18
- // eslint-disable-next-line max-len
19
- // eslint-disable-next-line @typescript-eslint/no-base-to-string,@typescript-eslint/restrict-template-expressions
41
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
20
42
  `Only known module names are allowed, using unknown module name: ${moduleName}`
21
43
  ),
22
44
 
@@ -34,17 +56,31 @@ const errors = {
34
56
  attempting to inject a dependency that is not registered
35
57
  as a runtime module for this chain: ${name}`
36
58
  ),
59
+
60
+ dependencyContainerNotSet: (className: string) =>
61
+ new Error(
62
+ `DependencyContainer not set. Be sure to only call DI-related function in create() and not inside the constructor. (${className})`
63
+ ),
64
+
65
+ validModuleInstance: (moduleName: string, moduleTypeName: string) =>
66
+ new Error(
67
+ `Incompatible module instance ("${moduleName}" not instanceof ${moduleTypeName})`
68
+ ),
37
69
  };
38
70
 
39
71
  export const ModuleContainerErrors = errors;
40
72
 
73
+ export interface BaseModuleInstanceType
74
+ extends ChildContainerCreatable,
75
+ Configurable<unknown> {}
76
+
41
77
  // determines that a module should be configurable by default
42
- export type BaseModuleType = TypedClass<Configurable<unknown>>;
78
+ export type BaseModuleType = TypedClass<BaseModuleInstanceType>;
43
79
 
44
80
  // allows to specify what kind of modules can be passed into a container
45
81
  export interface ModulesRecord<
46
82
  // use the default configurable module type
47
- ModuleType extends BaseModuleType = BaseModuleType
83
+ ModuleType extends BaseModuleType = BaseModuleType,
48
84
  > {
49
85
  [name: string]: ModuleType;
50
86
  }
@@ -55,26 +91,58 @@ export type ModulesConfig<Modules extends ModulesRecord> = {
55
91
  [ConfigKey in StringKeyOf<Modules>]: InstanceType<
56
92
  Modules[ConfigKey]
57
93
  > extends Configurable<infer Config>
58
- ? Config
94
+ ? Config extends NoConfig
95
+ ? Config | undefined
96
+ : Config
59
97
  : never;
60
98
  };
61
99
 
62
100
  /**
63
- * Parameters required when creating a module container instance
101
+ * This type make any config partial (i.e. optional) up to the first level
102
+ * So { Module: { a: { b: string } } }
103
+ * will become
104
+ * { Module?: { a?: { b: string } } }
105
+ * Note that b does not become optional, as we don't want nested objects to
106
+ * become unreasonably partialized (for example Field).
64
107
  */
65
- export interface ModuleContainerDefinition<Modules extends ModulesRecord> {
66
- modules: Modules;
67
- // config is optional, as it may be provided by the parent/wrapper class
68
- config?: ModulesConfig<Modules>;
108
+ export type RecursivePartial<T> = {
109
+ [Key in keyof T]?: Partial<T[Key]>;
110
+ };
111
+
112
+ // Removes all keys with a "never" value from an object
113
+ export type FilterNeverValues<Type extends Record<string, unknown>> = {
114
+ [Key in keyof Type as Type[Key] extends never ? never : Key]: Type[Key];
115
+ };
116
+
117
+ export type DependenciesFromModules<Modules extends ModulesRecord> =
118
+ FilterNeverValues<{
119
+ [Key in keyof Modules]: Modules[Key] extends TypedClass<DependencyFactory>
120
+ ? InferDependencies<InstanceType<Modules[Key]>>
121
+ : never;
122
+ }>;
123
+
124
+ export type ResolvableModules<Modules extends ModulesRecord> = MergeObjects<
125
+ DependenciesFromModules<Modules>
126
+ > &
127
+ Modules;
128
+
129
+ export interface ModuleContainerLike {
130
+ get dependencyContainer(): DependencyContainer;
131
+
132
+ resolveOrFail<ModuleType>(
133
+ moduleName: string,
134
+ moduleType: TypedClass<ModuleType>
135
+ ): ModuleType;
69
136
  }
70
137
 
71
138
  /**
72
139
  * Reusable module container facilitating registration, resolution
73
140
  * configuration, decoration and validation of modules
74
141
  */
75
- export class ModuleContainer<
76
- Modules extends ModulesRecord
77
- > extends ConfigurableModule<unknown> {
142
+ export class ModuleContainer<Modules extends ModulesRecord>
143
+ extends ConfigurableModule<ModulesConfig<Modules>>
144
+ implements ModuleContainerLike
145
+ {
78
146
  /**
79
147
  * Determines how often are modules decorated upon resolution
80
148
  * from the tsyringe DI container
@@ -82,19 +150,19 @@ export class ModuleContainer<
82
150
  private static readonly moduleDecorationFrequency: Frequency = "Once";
83
151
 
84
152
  // DI container holding all the registered modules
85
- protected readonly container = container.createChildContainer();
153
+ private providedContainer?: DependencyContainer = undefined;
154
+
155
+ private eventEmitterProxy: EventEmitterProxy<Modules> | undefined = undefined;
86
156
 
87
- public constructor(public definition: ModuleContainerDefinition<Modules>) {
157
+ public constructor(public definition: Modules) {
88
158
  super();
89
- // register all provided modules when the container is created
90
- this.registerModules(definition.modules);
91
159
  }
92
160
 
93
161
  /**
94
162
  * @returns list of module names
95
163
  */
96
164
  public get moduleNames() {
97
- return Object.keys(this.definition.modules);
165
+ return Object.keys(this.definition);
98
166
  }
99
167
 
100
168
  /**
@@ -126,27 +194,49 @@ export class ModuleContainer<
126
194
  });
127
195
  }
128
196
 
197
+ protected get container(): DependencyContainer {
198
+ this.assertContainerInitialized(this.providedContainer);
199
+ return this.providedContainer;
200
+ }
201
+
129
202
  /**
130
203
  * Assert that the iterated `moduleName` is of ModuleName type,
131
204
  * otherwise it may be just string e.g. when modules are iterated over
132
205
  * using e.g. a for loop.
133
206
  */
134
207
  public assertIsValidModuleName(
135
- modules: Modules,
136
208
  moduleName: string
137
209
  ): asserts moduleName is StringKeyOf<Modules> {
138
- this.isValidModuleName(modules, moduleName);
210
+ if (!this.isValidModuleName(this.definition, moduleName)) {
211
+ throw errors.onlyValidModuleNames(moduleName);
212
+ }
139
213
  }
140
214
 
141
215
  public isValidModuleName(
142
216
  modules: Modules,
143
217
  moduleName: number | string | symbol
144
- ): asserts moduleName is StringKeyOf<Modules> {
145
- if (!Object.prototype.hasOwnProperty.call(modules, moduleName)) {
146
- throw errors.onlyValidModuleNames(moduleName);
218
+ ): moduleName is StringKeyOf<Modules> {
219
+ return Object.prototype.hasOwnProperty.call(modules, moduleName);
220
+ }
221
+
222
+ public assertContainerInitialized(
223
+ container: DependencyContainer | undefined
224
+ ): asserts container is DependencyContainer {
225
+ if (container === undefined) {
226
+ throw errors.dependencyContainerNotSet(this.constructor.name);
147
227
  }
148
228
  }
149
229
 
230
+ protected registerAliases(originalToken: string, clas: TypedClass<any>) {
231
+ const aliases = getInjectAliases(clas);
232
+
233
+ aliases.forEach((alias) =>
234
+ this.container.register(alias, {
235
+ useToken: originalToken,
236
+ })
237
+ );
238
+ }
239
+
150
240
  /**
151
241
  * Register modules into the current container, and registers
152
242
  * a respective resolution hook in order to decorate the module
@@ -155,27 +245,41 @@ export class ModuleContainer<
155
245
  * @param modules
156
246
  */
157
247
  protected registerModules(modules: Modules) {
158
- for (const moduleName in modules) {
248
+ Object.keys(modules).forEach((moduleName) => {
159
249
  if (Object.prototype.hasOwnProperty.call(modules, moduleName)) {
160
- this.assertIsValidModuleName(modules, moduleName);
250
+ this.assertIsValidModuleName(moduleName);
161
251
 
162
252
  log.debug(`Registering module: ${moduleName}`);
163
253
 
254
+ const useClass = modules[moduleName];
255
+
164
256
  this.container.register(
165
257
  moduleName,
166
- { useClass: modules[moduleName] },
258
+ { useClass },
167
259
  { lifecycle: Lifecycle.ContainerScoped }
168
260
  );
169
261
  this.onAfterModuleResolution(moduleName);
262
+
263
+ this.registerAliases(moduleName, useClass);
264
+
265
+ if (this.isDependencyFactory(useClass)) {
266
+ this.useDependencyFactory(useClass);
267
+ }
170
268
  }
269
+ });
270
+ }
271
+
272
+ public get events(): EventEmitterProxy<Modules> {
273
+ if (this.eventEmitterProxy === undefined) {
274
+ this.eventEmitterProxy = new EventEmitterProxy<Modules>(this);
171
275
  }
276
+ return this.eventEmitterProxy;
172
277
  }
173
278
 
174
279
  /**
175
280
  * Register a non-module value into the current container
176
281
  * @param modules
177
282
  */
178
- // eslint-disable-next-line no-warning-comments
179
283
  // TODO Rename to plural since object is param
180
284
  public registerValue<Value>(modules: Record<string, Value>) {
181
285
  Object.entries(modules).forEach(([moduleName, useValue]) => {
@@ -192,7 +296,25 @@ export class ModuleContainer<
192
296
  * @param config
193
297
  */
194
298
  public configure(config: ModulesConfig<Modules>) {
195
- this.definition.config = config;
299
+ this.config = config;
300
+ }
301
+
302
+ public configurePartial(config: RecursivePartial<ModulesConfig<Modules>>) {
303
+ this.config = merge<
304
+ ModulesConfig<Modules> | NoConfig,
305
+ RecursivePartial<ModulesConfig<Modules>>
306
+ >(this.currentConfig ?? {}, config);
307
+ }
308
+
309
+ public get config() {
310
+ return super.config;
311
+ }
312
+
313
+ public set config(config: ModulesConfig<Modules>) {
314
+ super.config = merge<
315
+ ModulesConfig<Modules> | NoConfig,
316
+ ModulesConfig<Modules>
317
+ >(this.currentConfig ?? {}, config);
196
318
  }
197
319
 
198
320
  /**
@@ -205,23 +327,30 @@ export class ModuleContainer<
205
327
  * @param moduleName
206
328
  * @returns
207
329
  */
208
- public resolve<ResolvableModuleName extends StringKeyOf<Modules>>(
209
- moduleName: ResolvableModuleName
210
- ): InstanceType<Modules[ResolvableModuleName]> {
211
- return this.container.resolve<InstanceType<Modules[ResolvableModuleName]>>(
212
- moduleName
213
- );
330
+ public resolve<KeyType extends StringKeyOf<ResolvableModules<Modules>>>(
331
+ moduleName: KeyType
332
+ ): InstanceType<ResolvableModules<Modules>[KeyType]> {
333
+ return this.container.resolve<
334
+ InstanceType<ResolvableModules<Modules>[KeyType]>
335
+ >(moduleName);
214
336
  }
215
337
 
216
338
  public resolveOrFail<ModuleType>(
217
339
  moduleName: string,
218
- moduleType: TypedClass<ModuleType>
340
+ moduleType?: TypedClass<ModuleType>
219
341
  ) {
342
+ if (!this.container.isRegistered(moduleName)) {
343
+ throw new Error(`Dependency with token ${moduleName} not registered`);
344
+ }
345
+
220
346
  const instance = this.container.resolve<ModuleType>(moduleName);
221
- const isValidModuleInstance = instance instanceof moduleType;
222
347
 
223
- if (!isValidModuleInstance) {
224
- throw new Error("Incompatible module instance");
348
+ if (moduleType !== undefined) {
349
+ const isValidModuleInstance = instance instanceof moduleType;
350
+
351
+ if (!isValidModuleInstance) {
352
+ throw errors.validModuleInstance(moduleName, moduleType.name);
353
+ }
225
354
  }
226
355
 
227
356
  return instance;
@@ -235,14 +364,96 @@ export class ModuleContainer<
235
364
  moduleName: StringKeyOf<Modules>,
236
365
  containedModule: InstanceType<Modules[StringKeyOf<Modules>]>
237
366
  ) {
238
- const config = this.definition.config?.[moduleName];
239
-
367
+ const config = super.config?.[moduleName];
240
368
  // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
241
369
  if (!config) {
242
370
  throw errors.configNotSetInContainer(moduleName.toString());
243
371
  }
244
372
 
245
- containedModule.config = config;
373
+ if (containedModule instanceof ModuleContainer) {
374
+ containedModule.configure(config);
375
+ } else {
376
+ containedModule.config = config;
377
+ }
378
+ }
379
+
380
+ private isDependencyFactory(type: any): type is DependencyFactory {
381
+ return "dependencies" in type;
382
+ }
383
+
384
+ /**
385
+ * Inject a set of dependencies using the given list of DependencyFactories
386
+ * This method should be called during startup
387
+ */
388
+ protected initializeDependencyFactories(factories: StringKeyOf<Modules>[]) {
389
+ factories.forEach((factoryName) => {
390
+ this.resolve(factoryName);
391
+ });
392
+ }
393
+
394
+ /**
395
+ * Retrieves all dependencies generated by a particular dependencyfactory
396
+ * and injects them inside this modulecontainer's DI container.
397
+ * This will be automatically called for every module, but can also be called
398
+ * explicitly to initialize an extra factory
399
+ * @param factory
400
+ * @private
401
+ */
402
+ protected useDependencyFactory(factory: DependencyFactory) {
403
+ const dependencies = factory.dependencies();
404
+
405
+ // eslint-disable-next-line sonarjs/cognitive-complexity
406
+ Object.entries(dependencies).forEach(([rawKey, declaration]) => {
407
+ const key = rawKey.charAt(0).toUpperCase() + rawKey.slice(1);
408
+
409
+ if (
410
+ !this.container.isRegistered(key) ||
411
+ declaration.forceOverwrite === true
412
+ ) {
413
+ if (
414
+ this.container.isRegistered(key) &&
415
+ (declaration?.forceOverwrite ?? false)
416
+ ) {
417
+ log.warn(
418
+ `You are trying to overwrite dependency ${key}, which is already registered. This is currently not supported. Try to define your dependency earlier.`
419
+ );
420
+ }
421
+
422
+ // Find correct provider type and call respective register
423
+ if (isValueProvider(declaration)) {
424
+ this.container.register(key, declaration);
425
+ } else if (isFactoryProvider(declaration)) {
426
+ // this enables us to have a singletoned factory
427
+ // that returns the same instance for each resolve
428
+ this.container.register(key, {
429
+ useFactory: instanceCachingFactory(declaration.useFactory),
430
+ });
431
+ } else if (isClassProvider(declaration)) {
432
+ this.container.register(key, declaration, {
433
+ lifecycle: Lifecycle.Singleton,
434
+ });
435
+ this.registerAliases(
436
+ key,
437
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
438
+ declaration.useClass as TypedClass<unknown>
439
+ );
440
+
441
+ // Register static dependencies
442
+ if (this.isDependencyFactory(declaration.useClass)) {
443
+ this.useDependencyFactory(declaration.useClass);
444
+ }
445
+ } else if (isTokenProvider(declaration)) {
446
+ this.container.register(key, declaration, {
447
+ lifecycle: Lifecycle.Singleton,
448
+ });
449
+ } else {
450
+ // Can never be reached
451
+ throw new Error("Above if-statement is exhaustive");
452
+ }
453
+ } else {
454
+ log.debug(`Dependency ${key} already registered, skipping`);
455
+ }
456
+ });
246
457
  }
247
458
 
248
459
  /**
@@ -258,8 +469,38 @@ export class ModuleContainer<
258
469
  throw errors.unableToDecorateModule(containedModuleName);
259
470
  }
260
471
  this.decorateModule(moduleName, containedModule);
472
+ containedModule.create(() => {
473
+ const container = this.container.createChildContainer();
474
+ container.reset();
475
+ return container;
476
+ });
477
+
478
+ if (this.isDependencyFactory(containedModule)) {
479
+ this.useDependencyFactory(containedModule);
480
+ }
261
481
  },
262
482
  { frequency: ModuleContainer.moduleDecorationFrequency }
263
483
  );
264
484
  }
485
+
486
+ /**
487
+ * This is a placeholder for individual modules to override.
488
+ * This method will be called whenever the underlying container fully
489
+ * initialized
490
+ */
491
+ public create(childContainerProvider: ChildContainerProvider): void {
492
+ this.providedContainer = childContainerProvider();
493
+
494
+ this.registerValue({
495
+ ChildContainerProvider: () => this.container.createChildContainer(),
496
+ });
497
+
498
+ // register all provided modules when the container is created
499
+ this.registerModules(this.definition);
500
+ this.container.register("ParentContainer", { useValue: this });
501
+ }
502
+
503
+ public get dependencyContainer(): DependencyContainer {
504
+ return this.container;
505
+ }
265
506
  }
@@ -0,0 +1,3 @@
1
+ export interface Startable {
2
+ start(): Promise<void>;
3
+ }
@@ -0,0 +1,70 @@
1
+ import { TypedClass } from "../types";
2
+
3
+ export const injectAliasMetadataKey = "protokit-inject-alias";
4
+
5
+ /**
6
+ * Attaches metadata to the class that the ModuleContainer can pick up
7
+ * and inject this class in the DI container under the specified aliases.
8
+ * This method supports inheritance, therefore also gets aliases defined
9
+ * on superclasses
10
+ */
11
+ export function injectAlias(aliases: string[]) {
12
+ return (target: TypedClass<unknown>) => {
13
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
14
+ const superAliases = Reflect.getMetadata(
15
+ injectAliasMetadataKey,
16
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
17
+ Object.getPrototypeOf(target)
18
+ ) as string[] | undefined;
19
+
20
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
21
+ const existingAliases = Reflect.getMetadata(
22
+ injectAliasMetadataKey,
23
+ target
24
+ ) as string[] | undefined;
25
+
26
+ let allAliases = aliases;
27
+
28
+ if (superAliases !== undefined) {
29
+ allAliases = allAliases.concat(superAliases);
30
+ }
31
+ if (existingAliases !== undefined) {
32
+ allAliases = allAliases.concat(existingAliases);
33
+ }
34
+
35
+ Reflect.defineMetadata(
36
+ injectAliasMetadataKey,
37
+ allAliases.filter(
38
+ (value, index, array) => array.indexOf(value) === index
39
+ ),
40
+ target
41
+ );
42
+ };
43
+ }
44
+
45
+ /**
46
+ * Marks the class to implement a certain interface T, while also attaching
47
+ * a DI-injection alias as metadata, that will be picked up by the ModuleContainer
48
+ * to allow resolving by that interface name
49
+ * @param name The name of the injection alias, convention is to use the same as the name of T
50
+ */
51
+ export function implement<T>(name: string) {
52
+ return (
53
+ /**
54
+ * Check if the target class extends RuntimeModule, while
55
+ * also providing static config presets
56
+ */
57
+ target: TypedClass<T>
58
+ ) => {
59
+ injectAlias([name])(target);
60
+ };
61
+ }
62
+
63
+ export function getInjectAliases(target: TypedClass<unknown>): string[] {
64
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
65
+ const aliases = Reflect.getMetadata(
66
+ injectAliasMetadataKey,
67
+ target
68
+ ) as string[];
69
+ return aliases ?? [];
70
+ }
@@ -0,0 +1,57 @@
1
+ import {
2
+ ClassProvider,
3
+ FactoryProvider,
4
+ TokenProvider,
5
+ ValueProvider,
6
+ } from "tsyringe";
7
+
8
+ import { TypedClass } from "../types";
9
+ import type { BaseModuleInstanceType } from "../config/ModuleContainer";
10
+
11
+ export type DependencyDeclaration<Dependency> =
12
+ | ClassProvider<Dependency>
13
+ | FactoryProvider<Dependency>
14
+ | TokenProvider<Dependency>
15
+ | ValueProvider<Dependency>;
16
+
17
+ export type DependencyRecord = Record<
18
+ string,
19
+ DependencyDeclaration<unknown> & { forceOverwrite?: boolean }
20
+ >;
21
+
22
+ /**
23
+ * This is an abstract class for creating DependencyFactories, a pattern
24
+ * to bundle multiple smaller services into one and register them into the
25
+ * injection context.
26
+ *
27
+ * This can for example be a StorageDependencyFactory that creates dependencies
28
+ * like StateService, MerkleWitnessService, etc. So in general, services that
29
+ * are not ConfigurableModules, but still are their own logical unit.
30
+ *
31
+ * DependencyFactories are designed to only be used statically for sets of
32
+ * deps that are necessary for the sequencer to work.
33
+ */
34
+ export interface DependencyFactory {
35
+ dependencies: () => DependencyRecord;
36
+ }
37
+
38
+ export type TypeFromDependencyDeclaration<
39
+ Declaration extends DependencyDeclaration<unknown>,
40
+ > =
41
+ Declaration extends DependencyDeclaration<infer Dependency>
42
+ ? Dependency
43
+ : never;
44
+
45
+ export type CapitalizeAny<Key extends string | number | symbol> =
46
+ Key extends string ? Capitalize<Key> : Key;
47
+
48
+ export type MapDependencyRecordToTypes<Record extends DependencyRecord> = {
49
+ [Key in keyof Record as CapitalizeAny<Key>]: TypedClass<
50
+ TypeFromDependencyDeclaration<Record[Key]>
51
+ >;
52
+ };
53
+
54
+ export type InferDependencies<Class extends BaseModuleInstanceType> =
55
+ Class extends DependencyFactory
56
+ ? MapDependencyRecordToTypes<ReturnType<Class["dependencies"]>>
57
+ : never;
@@ -0,0 +1,10 @@
1
+ import { Field, VerificationKey } from "o1js";
2
+
3
+ export function dummyVerificationKey() {
4
+ return new VerificationKey({
5
+ hash: Field(
6
+ "3392518251768960475377392625298437850623664973002200885669375116181514017494"
7
+ ),
8
+ data: "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",
9
+ });
10
+ }
@@ -0,0 +1,61 @@
1
+ import { EventsRecord } from "./EventEmittingComponent";
2
+
3
+ type ListenersHolder<Events extends EventsRecord> = {
4
+ [key in keyof Events]?: ((...args: Events[key]) => void)[];
5
+ };
6
+
7
+ export class EventEmitter<Events extends EventsRecord> {
8
+ protected readonly listeners: ListenersHolder<Events> = {};
9
+
10
+ protected readonly wildcardListeners: ((
11
+ event: keyof Events,
12
+ args: Events[keyof Events]
13
+ ) => void)[] = [];
14
+
15
+ public emit<Key extends keyof Events>(
16
+ event: Key,
17
+ ...parameters: Events[Key]
18
+ ) {
19
+ const listeners = this.listeners[event];
20
+ if (listeners !== undefined) {
21
+ listeners.forEach((listener) => {
22
+ listener(...parameters);
23
+ });
24
+ }
25
+ this.wildcardListeners.forEach((wildcardListener) => {
26
+ wildcardListener(event, parameters);
27
+ });
28
+ }
29
+
30
+ public onAll(listener: (event: keyof Events, args: unknown[]) => void): void {
31
+ this.wildcardListeners.push(listener);
32
+ }
33
+
34
+ public on<Key extends keyof Events>(
35
+ event: Key,
36
+ listener: (...args: Events[Key]) => void
37
+ ) {
38
+ (this.listeners[event] ??= []).push(listener);
39
+ }
40
+
41
+ /**
42
+ * Primitive .off() with identity comparison for now.
43
+ * Could be replaced by returning an id in .on() and using that.
44
+ */
45
+ public off<Key extends keyof Events>(
46
+ event: Key,
47
+ listener: (...args: Events[Key]) => void
48
+ ) {
49
+ const events = this.listeners[event];
50
+ if (events !== undefined) {
51
+ this.listeners[event] = events.filter(
52
+ (candidate) => candidate !== listener
53
+ );
54
+ }
55
+ }
56
+ }
57
+
58
+ export type EventListenable<Events extends EventsRecord> = Pick<
59
+ EventEmitter<Events>,
60
+ "on" | "onAll" | "off"
61
+ >;