@proto-kit/common 0.1.1-develop.339 → 0.1.1-develop.455

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 (61) hide show
  1. package/dist/config/ConfigurableModule.d.ts +2 -1
  2. package/dist/config/ConfigurableModule.d.ts.map +1 -1
  3. package/dist/config/ModuleContainer.d.ts +35 -11
  4. package/dist/config/ModuleContainer.d.ts.map +1 -1
  5. package/dist/config/ModuleContainer.js +86 -19
  6. package/dist/dependencyFactory/DependencyFactory.d.ts +13 -9
  7. package/dist/dependencyFactory/DependencyFactory.d.ts.map +1 -1
  8. package/dist/dependencyFactory/DependencyFactory.js +1 -97
  9. package/dist/events/EventEmitter.d.ts +14 -0
  10. package/dist/events/EventEmitter.d.ts.map +1 -0
  11. package/dist/events/EventEmitter.js +35 -0
  12. package/dist/events/EventEmitterProxy.d.ts +17 -0
  13. package/dist/events/EventEmitterProxy.d.ts.map +1 -0
  14. package/dist/events/EventEmitterProxy.js +21 -0
  15. package/dist/events/EventEmittingComponent.d.ts +6 -0
  16. package/dist/events/EventEmittingComponent.d.ts.map +1 -0
  17. package/dist/index.d.ts +7 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +7 -0
  20. package/dist/trees/InMemoryMerkleTreeStorage.d.ts +11 -0
  21. package/dist/trees/InMemoryMerkleTreeStorage.d.ts.map +1 -0
  22. package/dist/trees/InMemoryMerkleTreeStorage.js +12 -0
  23. package/dist/trees/MerkleTreeStore.d.ts +5 -0
  24. package/dist/trees/MerkleTreeStore.d.ts.map +1 -0
  25. package/dist/trees/MerkleTreeStore.js +1 -0
  26. package/dist/trees/MockAsyncMerkleStore.d.ts +9 -0
  27. package/dist/trees/MockAsyncMerkleStore.d.ts.map +1 -0
  28. package/dist/trees/MockAsyncMerkleStore.js +19 -0
  29. package/dist/trees/RollupMerkleTree.d.ts +126 -0
  30. package/dist/trees/RollupMerkleTree.d.ts.map +1 -0
  31. package/dist/trees/RollupMerkleTree.js +216 -0
  32. package/dist/types.d.ts +5 -0
  33. package/dist/types.d.ts.map +1 -1
  34. package/dist/zkProgrammable/ZkProgrammable.d.ts +1 -1
  35. package/dist/zkProgrammable/ZkProgrammable.d.ts.map +1 -1
  36. package/dist/zkProgrammable/ZkProgrammable.js +4 -4
  37. package/dist/zkProgrammable/provableMethod.d.ts +1 -1
  38. package/dist/zkProgrammable/provableMethod.d.ts.map +1 -1
  39. package/dist/zkProgrammable/provableMethod.js +2 -2
  40. package/package.json +2 -2
  41. package/src/config/ConfigurableModule.ts +3 -1
  42. package/src/config/ModuleContainer.ts +150 -30
  43. package/src/dependencyFactory/DependencyFactory.ts +25 -114
  44. package/src/events/EventEmitter.ts +58 -0
  45. package/src/events/EventEmitterProxy.ts +57 -0
  46. package/src/events/EventEmittingComponent.ts +7 -0
  47. package/src/index.ts +7 -0
  48. package/src/trees/InMemoryMerkleTreeStorage.ts +17 -0
  49. package/src/trees/MerkleTreeStore.ts +5 -0
  50. package/src/trees/MockAsyncMerkleStore.ts +29 -0
  51. package/src/trees/RollupMerkleTree.ts +354 -0
  52. package/src/trees/VirtualMerkleTreeStore.ts +21 -0
  53. package/src/types.ts +12 -0
  54. package/src/zkProgrammable/ZkProgrammable.ts +5 -4
  55. package/src/zkProgrammable/provableMethod.ts +2 -2
  56. package/test/config/ContainerEvents.test.ts +86 -0
  57. package/test/config/ModuleContainer.test.ts +111 -6
  58. package/test/trees/MerkleTree.test.ts +105 -0
  59. package/dist/config/ChildContainerStartable.d.ts +0 -5
  60. package/dist/config/ChildContainerStartable.d.ts.map +0 -1
  61. /package/dist/{config/ChildContainerStartable.js → events/EventEmittingComponent.js} +0 -0
@@ -0,0 +1,354 @@
1
+ /* eslint-disable id-length */
2
+ /* eslint-disable line-comment-position */
3
+ /* eslint-disable no-inline-comments */
4
+ /* eslint-disable @typescript-eslint/no-magic-numbers */
5
+ /* eslint-disable @typescript-eslint/method-signature-style */
6
+ import { Bool, Field, Poseidon, Provable, Struct } from "o1js";
7
+
8
+ import { range } from "../utils";
9
+ import { TypedClass } from "../types";
10
+
11
+ import { MerkleTreeStore } from "./MerkleTreeStore";
12
+ import { InMemoryMerkleTreeStorage } from "./InMemoryMerkleTreeStorage";
13
+
14
+ class StructTemplate extends Struct({
15
+ path: Provable.Array(Field, 0),
16
+ isLeft: Provable.Array(Bool, 0),
17
+ }) {}
18
+
19
+ export interface AbstractMerkleWitness extends StructTemplate {
20
+ height(): number;
21
+
22
+ /**
23
+ * Calculates a root depending on the leaf value.
24
+ * @param leaf Value of the leaf node that belongs to this Witness.
25
+ * @returns The calculated root.
26
+ */
27
+ calculateRoot(hash: Field): Field;
28
+
29
+ /**
30
+ * Calculates the index of the leaf node that belongs to this Witness.
31
+ * @returns Index of the leaf.
32
+ */
33
+ calculateIndex(): Field;
34
+
35
+ checkMembership(root: Field, key: Field, value: Field): Bool;
36
+
37
+ checkMembershipGetRoots(
38
+ root: Field,
39
+ key: Field,
40
+ value: Field
41
+ ): [Bool, Field, Field];
42
+
43
+ toShortenedEntries(): string[];
44
+ }
45
+
46
+ export interface AbstractMerkleTree {
47
+ store: MerkleTreeStore;
48
+ readonly leafCount: bigint;
49
+
50
+ assertIndexRange(index: bigint): void;
51
+
52
+ /**
53
+ * Returns a node which lives at a given index and level.
54
+ * @param level Level of the node.
55
+ * @param index Index of the node.
56
+ * @returns The data of the node.
57
+ */
58
+ getNode(level: number, index: bigint): Field;
59
+
60
+ /**
61
+ * Returns the root of the [Merkle Tree](https://en.wikipedia.org/wiki/Merkle_tree).
62
+ * @returns The root of the Merkle Tree.
63
+ */
64
+ getRoot(): Field;
65
+
66
+ /**
67
+ * Sets the value of a leaf node at a given index to a given value.
68
+ * @param index Position of the leaf node.
69
+ * @param leaf New value.
70
+ */
71
+ setLeaf(index: bigint, leaf: Field): void;
72
+
73
+ /**
74
+ * Returns the witness (also known as
75
+ * [Merkle Proof or Merkle Witness](https://computersciencewiki.org/index.php/Merkle_proof))
76
+ * for the leaf at the given index.
77
+ * @param index Position of the leaf node.
78
+ * @returns The witness that belongs to the leaf.
79
+ */
80
+ getWitness(index: bigint): AbstractMerkleWitness;
81
+
82
+ /**
83
+ * Fills all leaves of the tree.
84
+ * @param leaves Values to fill the leaves with.
85
+ */
86
+ fill(leaves: Field[]): void;
87
+ }
88
+
89
+ export interface AbstractMerkleTreeClass {
90
+ new (...args: any[]): AbstractMerkleTree;
91
+
92
+ WITNESS: TypedClass<AbstractMerkleWitness> & typeof StructTemplate;
93
+
94
+ HEIGHT: number;
95
+
96
+ EMPTY_ROOT: bigint;
97
+
98
+ get leafCount(): bigint;
99
+ }
100
+
101
+ /**
102
+ * A [Merkle Tree](https://en.wikipedia.org/wiki/Merkle_tree) is a binary tree in
103
+ * which every leaf is the cryptography hash of a piece of data,
104
+ * and every node is the hash of the concatenation of its two child nodes.
105
+ *
106
+ * A Merkle Tree allows developers to easily and securely verify
107
+ * the integrity of large amounts of data.
108
+ *
109
+ * Take a look at our [documentation](https://docs.minaprotocol.com/en/zkapps)
110
+ * on how to use Merkle Trees in combination with zkApps and
111
+ * zero knowledge programming!
112
+ *
113
+ * Levels are indexed from leaves (level 0) to root (level N - 1).
114
+ *
115
+ * This function takes a height as argument and returns a class
116
+ * that implements a merkletree with that specified height.
117
+ *
118
+ * It also holds the Witness class under tree.WITNESS
119
+ */
120
+ export function createMerkleTree(height: number): AbstractMerkleTreeClass {
121
+ /**
122
+ * The {@link BaseMerkleWitness} class defines a circuit-compatible base class
123
+ * for [Merkle Witness'](https://computersciencewiki.org/index.php/Merkle_proof).
124
+ */
125
+ class RollupMerkleWitness
126
+ extends Struct({
127
+ path: Provable.Array(Field, height - 1),
128
+ isLeft: Provable.Array(Bool, height - 1),
129
+ })
130
+ implements AbstractMerkleWitness
131
+ {
132
+ public static height = height;
133
+
134
+ public height(): number {
135
+ return RollupMerkleWitness.height;
136
+ }
137
+
138
+ /**
139
+ * Calculates a root depending on the leaf value.
140
+ * @param leaf Value of the leaf node that belongs to this Witness.
141
+ * @returns The calculated root.
142
+ */
143
+ public calculateRoot(hash: Field): Field {
144
+ const n = this.height();
145
+
146
+ for (let index = 1; index < n; ++index) {
147
+ const isLeft = this.isLeft[index - 1];
148
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
149
+ const [left, right] = maybeSwap(isLeft, hash, this.path[index - 1]);
150
+ hash = Poseidon.hash([left, right]);
151
+ }
152
+
153
+ return hash;
154
+ }
155
+
156
+ /**
157
+ * Calculates the index of the leaf node that belongs to this Witness.
158
+ * @returns Index of the leaf.
159
+ */
160
+ public calculateIndex(): Field {
161
+ let powerOfTwo = Field(1);
162
+ let index = Field(0);
163
+ const n = this.height();
164
+
165
+ // eslint-disable-next-line no-underscore-dangle
166
+ for (let index_ = 1; index_ < n; ++index_) {
167
+ index = Provable.if(
168
+ this.isLeft[index_ - 1],
169
+ index,
170
+ index.add(powerOfTwo)
171
+ );
172
+ powerOfTwo = powerOfTwo.mul(2);
173
+ }
174
+
175
+ return index;
176
+ }
177
+
178
+ public checkMembership(root: Field, key: Field, value: Field): Bool {
179
+ const calculatedRoot = this.calculateRoot(value);
180
+ const calculatedKey = this.calculateIndex();
181
+ // We don't have to range-check the key, because if it would be greater
182
+ // than leafCount, it would not match the computedKey
183
+ key.assertEquals(calculatedKey, "Keys of MerkleWitness does not match");
184
+ return root.equals(calculatedRoot);
185
+ }
186
+
187
+ public checkMembershipGetRoots(
188
+ root: Field,
189
+ key: Field,
190
+ value: Field
191
+ ): [Bool, Field, Field] {
192
+ const calculatedRoot = this.calculateRoot(value);
193
+ const calculatedKey = this.calculateIndex();
194
+ key.assertEquals(calculatedKey, "Keys of MerkleWitness does not match");
195
+ return [root.equals(calculatedRoot), root, calculatedRoot];
196
+ }
197
+
198
+ public toShortenedEntries() {
199
+ return range(0, 5)
200
+ .concat(range(this.height() - 4, this.height()))
201
+ .map((index) =>
202
+ [
203
+ this.path[index].toString(),
204
+ this.isLeft[index].toString(),
205
+ ].toString()
206
+ );
207
+ }
208
+ }
209
+
210
+ return class AbstractRollupMerkleTree implements AbstractMerkleTree {
211
+ public static HEIGHT = height;
212
+
213
+ public static EMPTY_ROOT = new AbstractRollupMerkleTree(
214
+ new InMemoryMerkleTreeStorage()
215
+ )
216
+ .getRoot()
217
+ .toBigInt();
218
+
219
+ public static get leafCount(): bigint {
220
+ return 2n ** BigInt(AbstractRollupMerkleTree.HEIGHT - 1);
221
+ }
222
+
223
+ public static WITNESS = RollupMerkleWitness;
224
+
225
+ // private in interface
226
+ readonly zeroes: bigint[];
227
+
228
+ readonly store: MerkleTreeStore;
229
+
230
+ public constructor(store: MerkleTreeStore) {
231
+ this.store = store;
232
+ this.zeroes = [0n];
233
+ for (let index = 1; index < AbstractRollupMerkleTree.HEIGHT; index += 1) {
234
+ const previousLevel = Field(this.zeroes[index - 1]);
235
+ this.zeroes.push(
236
+ Poseidon.hash([previousLevel, previousLevel]).toBigInt()
237
+ );
238
+ }
239
+ }
240
+
241
+ public assertIndexRange(index: bigint) {
242
+ if (index > this.leafCount) {
243
+ throw new Error("Index greater than maximum leaf number");
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Returns a node which lives at a given index and level.
249
+ * @param level Level of the node.
250
+ * @param index Index of the node.
251
+ * @returns The data of the node.
252
+ */
253
+ public getNode(level: number, index: bigint): Field {
254
+ this.assertIndexRange(index);
255
+ return Field(this.store.getNode(index, level) ?? this.zeroes[level]);
256
+ }
257
+
258
+ /**
259
+ * Returns the root of the [Merkle Tree](https://en.wikipedia.org/wiki/Merkle_tree).
260
+ * @returns The root of the Merkle Tree.
261
+ */
262
+ public getRoot(): Field {
263
+ return this.getNode(AbstractRollupMerkleTree.HEIGHT - 1, 0n);
264
+ }
265
+
266
+ // private in interface
267
+ private setNode(level: number, index: bigint, value: Field) {
268
+ this.store.setNode(index, level, value.toBigInt());
269
+ }
270
+
271
+ /**
272
+ * Sets the value of a leaf node at a given index to a given value.
273
+ * @param index Position of the leaf node.
274
+ * @param leaf New value.
275
+ */
276
+ public setLeaf(index: bigint, leaf: Field) {
277
+ this.assertIndexRange(index);
278
+
279
+ this.setNode(0, index, leaf);
280
+ let currentIndex = index;
281
+ for (let level = 1; level < AbstractRollupMerkleTree.HEIGHT; level += 1) {
282
+ currentIndex /= 2n;
283
+
284
+ const left = this.getNode(level - 1, currentIndex * 2n);
285
+ const right = this.getNode(level - 1, currentIndex * 2n + 1n);
286
+
287
+ this.setNode(level, currentIndex, Poseidon.hash([left, right]));
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Returns the witness (also known as
293
+ * [Merkle Proof or Merkle Witness](https://computersciencewiki.org/index.php/Merkle_proof))
294
+ * for the leaf at the given index.
295
+ * @param index Position of the leaf node.
296
+ * @returns The witness that belongs to the leaf.
297
+ */
298
+ public getWitness(index: bigint): RollupMerkleWitness {
299
+ this.assertIndexRange(index);
300
+
301
+ const path = [];
302
+ const isLefts = [];
303
+ for (
304
+ let level = 0;
305
+ level < AbstractRollupMerkleTree.HEIGHT - 1;
306
+ level += 1
307
+ ) {
308
+ const isLeft = index % 2n === 0n;
309
+ const sibling = this.getNode(level, isLeft ? index + 1n : index - 1n);
310
+ isLefts.push(Bool(isLeft));
311
+ path.push(sibling);
312
+ index /= 2n;
313
+ }
314
+ return new RollupMerkleWitness({
315
+ isLeft: isLefts,
316
+ path,
317
+ });
318
+ }
319
+
320
+ // eslint-disable-next-line no-warning-comments, max-len
321
+ // TODO: should this take an optional offset? should it fail if the array is too long?
322
+ /**
323
+ * Fills all leaves of the tree.
324
+ * @param leaves Values to fill the leaves with.
325
+ */
326
+ public fill(leaves: Field[]) {
327
+ leaves.forEach((value, index) => {
328
+ this.setLeaf(BigInt(index), value);
329
+ });
330
+ }
331
+
332
+ /**
333
+ * Returns the amount of leaf nodes.
334
+ * @returns Amount of leaf nodes.
335
+ */
336
+ public get leafCount(): bigint {
337
+ return AbstractRollupMerkleTree.leafCount;
338
+ }
339
+ };
340
+ }
341
+
342
+ export class RollupMerkleTree extends createMerkleTree(256) {}
343
+ export class RollupMerkleTreeWitness extends RollupMerkleTree.WITNESS {}
344
+
345
+ /**
346
+ * More efficient version of `maybeSwapBad` which
347
+ * reuses an intermediate variable
348
+ */
349
+ function maybeSwap(b: Bool, x: Field, y: Field): [Field, Field] {
350
+ const m = b.toField().mul(x.sub(y)); // b*(x - y)
351
+ const x1 = y.add(m); // y + b*(x - y)
352
+ const y2 = x.sub(m); // x - b*(x - y) = x + b*(y - x)
353
+ return [x1, y2];
354
+ }
@@ -0,0 +1,21 @@
1
+ import { MerkleTreeStore } from "./MerkleTreeStore";
2
+ import { InMemoryMerkleTreeStorage } from "./InMemoryMerkleTreeStorage";
3
+
4
+ /**
5
+ * A MemoryMerkleTreeStore that, if falls back to a parent store if it
6
+ * has no data
7
+ */
8
+ export class VirtualMerkleTreeStore extends InMemoryMerkleTreeStorage {
9
+ public constructor(private readonly parent: MerkleTreeStore) {
10
+ super()
11
+ }
12
+
13
+ public getNode(key: bigint, level: number): bigint | undefined {
14
+ return super.getNode(key, level) ?? this.parent.getNode(key, level);
15
+ }
16
+
17
+ public setNode(key: bigint, level: number, value: bigint): void {
18
+ super.setNode(key, level, value);
19
+ }
20
+
21
+ }
package/src/types.ts CHANGED
@@ -17,3 +17,15 @@ export type StringKeyOf<Target extends object> = Extract<keyof Target, string> &
17
17
  export type ArrayElement<ArrayType extends readonly unknown[]> =
18
18
  // eslint-disable-next-line putout/putout
19
19
  ArrayType extends readonly (infer ElementType)[] ? ElementType : never;
20
+
21
+ /**
22
+ * Transforms X | Y => X & Y
23
+ */
24
+ export type UnionToIntersection<Union> = (
25
+ Union extends any ? (x: Union) => void : never
26
+ ) extends (x: infer Intersection) => void
27
+ ? Intersection
28
+ : never;
29
+
30
+ export type MergeObjects<Input extends Record<string, unknown>> =
31
+ UnionToIntersection<Input[keyof Input]>;
@@ -1,7 +1,7 @@
1
1
  import { Experimental, FlexibleProvablePure, Proof } from "o1js";
2
2
  import { Memoize } from "typescript-memoize";
3
3
 
4
- import { mockProof } from "./provableMethod";
4
+ import { MOCK_PROOF } from "./provableMethod";
5
5
 
6
6
  const errors = {
7
7
  appChainNotSet: (name: string) =>
@@ -67,11 +67,12 @@ export function verifyToMockable<PublicInput, PublicOutput>(
67
67
  return verified;
68
68
  }
69
69
 
70
- return proof.proof === mockProof;
70
+ return proof.proof === MOCK_PROOF;
71
71
  };
72
72
  }
73
73
 
74
- export const mockVerificationKey = "mock-verification-key";
74
+ export const MOCK_VERIFICATION_KEY = "mock-verification-key";
75
+
75
76
  export function compileToMockable(
76
77
  compile: Compile,
77
78
  { areProofsEnabled }: AreProofsEnabled
@@ -82,7 +83,7 @@ export function compileToMockable(
82
83
  }
83
84
 
84
85
  return {
85
- verificationKey: mockVerificationKey,
86
+ verificationKey: MOCK_VERIFICATION_KEY,
86
87
  };
87
88
  };
88
89
  }
@@ -11,7 +11,7 @@ export type ArgumentTypes = O1JSPrimitive[] | Proof<unknown, unknown>[];
11
11
  // eslint-disable-next-line etc/prefer-interface
12
12
  export type DecoratedMethod = (...args: ArgumentTypes) => unknown;
13
13
 
14
- export const mockProof = "mock-proof";
14
+ export const MOCK_PROOF = "mock-proof";
15
15
 
16
16
  export function toProver(
17
17
  methodName: string,
@@ -31,7 +31,7 @@ export function toProver(
31
31
  const publicOutput = Reflect.apply(simulatedMethod, this, args);
32
32
 
33
33
  return new this.zkProgram.Proof({
34
- proof: mockProof,
34
+ proof: MOCK_PROOF,
35
35
 
36
36
  // eslint-disable-next-line no-warning-comments
37
37
  // TODO: provide undefined if public input is not used
@@ -0,0 +1,86 @@
1
+ import "reflect-metadata";
2
+ import {
3
+ BaseModuleInstanceType,
4
+ BaseModuleType,
5
+ ConfigurableModule,
6
+ EventEmitter,
7
+ EventEmittingComponent,
8
+ EventsRecord,
9
+ ModuleContainer,
10
+ ModulesRecord,
11
+ TypedClass,
12
+ } from "../../src";
13
+ import { injectable, container as tsyringeContainer } from "tsyringe";
14
+ import {
15
+ CastToEventsRecord,
16
+ ContainerEvents,
17
+ FlattenedContainerEvents,
18
+ FlattenObject,
19
+ } from "../../src/events/EventEmitterProxy";
20
+
21
+ class TestContainer<
22
+ Modules extends ModulesRecord
23
+ > extends ModuleContainer<Modules> {}
24
+
25
+ interface TestEvents extends EventsRecord {
26
+ test: [string];
27
+ }
28
+
29
+ @injectable()
30
+ class TestModule
31
+ extends ConfigurableModule<{}>
32
+ implements BaseModuleInstanceType, EventEmittingComponent<TestEvents>
33
+ {
34
+ events = new EventEmitter<TestEvents>();
35
+ }
36
+
37
+ interface TestEvents2 extends EventsRecord {
38
+ test2: [number];
39
+ }
40
+
41
+ class TestModule2
42
+ extends ConfigurableModule<{}>
43
+ implements BaseModuleInstanceType, EventEmittingComponent<TestEvents2>
44
+ {
45
+ events = new EventEmitter<TestEvents2>();
46
+ }
47
+
48
+ type X = {
49
+ test: TypedClass<TestModule>;
50
+ test2: TypedClass<TestModule2>;
51
+ };
52
+ type Y = FlattenObject<ContainerEvents<X>>;
53
+ type Z = CastToEventsRecord<FlattenedContainerEvents<X>>;
54
+ // const y: Y = {
55
+ // test: ["asd"],
56
+ // // test2: [2]
57
+ // }
58
+
59
+ describe("test event propagation", () => {
60
+ it("should propagate events up", () => {
61
+ expect.assertions(1);
62
+
63
+ const container = new TestContainer({
64
+ modules: {
65
+ test: TestModule,
66
+ test2: TestModule2,
67
+ },
68
+ });
69
+
70
+ container.configure({
71
+ test: {},
72
+ test2: {}
73
+ });
74
+
75
+ container.create(() => tsyringeContainer.createChildContainer());
76
+
77
+ const testvalue = "testString123";
78
+
79
+ container.events.on("test", (value: string) => {
80
+ expect(value).toStrictEqual(testvalue);
81
+ });
82
+
83
+ const module = container.resolve("test");
84
+ module.events.emit("test", testvalue);
85
+ });
86
+ });
@@ -1,11 +1,18 @@
1
1
  /* eslint-disable max-classes-per-file */
2
- import { ConfigurableModule } from "../../src/config/ConfigurableModule";
2
+ import "reflect-metadata";
3
+ import { container as tsyringeContainer, inject, injectable } from "tsyringe";
4
+
5
+ import {
6
+ ConfigurableModule,
7
+ NoConfig,
8
+ } from "../../src/config/ConfigurableModule";
3
9
  import {
4
- ModuleContainerErrors,
5
10
  ModuleContainer,
6
11
  ModulesRecord,
12
+ DependenciesFromModules,
7
13
  } from "../../src/config/ModuleContainer";
8
14
  import { TypedClass } from "../../src/types";
15
+ import { ChildContainerProvider, DependencyFactory, DependencyRecord } from "../../src";
9
16
 
10
17
  // module container will accept modules that extend this type
11
18
  class BaseTestModule<Config> extends ConfigurableModule<Config> {}
@@ -14,15 +21,43 @@ type TestModulesRecord = ModulesRecord<TypedClass<BaseTestModule<unknown>>>;
14
21
 
15
22
  interface TestModuleConfig {
16
23
  testConfigProperty: number;
24
+ testConfigProperty2?: number;
25
+ testConfigProperty3?: number;
26
+ }
27
+
28
+ @injectable()
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
+ }
17
37
  }
18
38
 
19
- class TestModule extends BaseTestModule<TestModuleConfig> {}
39
+ class TestModule
40
+ extends BaseTestModule<TestModuleConfig>
41
+ implements DependencyFactory
42
+ {
43
+ public dependencies() {
44
+ return {
45
+ dependencyModule1: {
46
+ useClass: ChildModule,
47
+ },
48
+ };
49
+ }
50
+ }
20
51
 
21
52
  interface OtherTestModuleConfig {
22
53
  otherTestConfigProperty: number;
23
54
  }
24
55
 
25
- class OtherTestModule extends BaseTestModule<OtherTestModuleConfig> {}
56
+ class OtherTestModule extends BaseTestModule<OtherTestModuleConfig> {
57
+ public x() {
58
+ return "";
59
+ }
60
+ }
26
61
 
27
62
  /**
28
63
  * Showcases a wrongly typed/defined module as
@@ -34,7 +69,21 @@ class WrongTestModule {}
34
69
 
35
70
  class TestModuleContainer<
36
71
  Modules extends TestModulesRecord
37
- > extends ModuleContainer<Modules> {}
72
+ > extends ModuleContainer<Modules> {
73
+ public create(childContainerProvider: ChildContainerProvider) {
74
+ super.create(childContainerProvider);
75
+ this.registerDependencyFactories(["TestModule" as any]);
76
+ }
77
+ }
78
+
79
+ type inferred = DependenciesFromModules<{
80
+ TestModule: typeof TestModule;
81
+ OtherTestModule: typeof OtherTestModule;
82
+ }>;
83
+
84
+ // const merged2T: merged2 = {
85
+ // dependencyModule1: ""
86
+ // }
38
87
 
39
88
  describe("moduleContainer", () => {
40
89
  let container: TestModuleContainer<{
@@ -54,12 +103,33 @@ describe("moduleContainer", () => {
54
103
  });
55
104
  });
56
105
 
106
+ it.only("should resolve dependency factory dependencies correctly", () => {
107
+ container.configure({
108
+ TestModule: {
109
+ testConfigProperty,
110
+ },
111
+
112
+ OtherTestModule: {
113
+ otherTestConfigProperty: testConfigProperty,
114
+ },
115
+ });
116
+
117
+ container.create(() => tsyringeContainer.createChildContainer());
118
+
119
+ const dm = container.resolve("dependencyModule1");
120
+
121
+ expect(dm.x()).toBe("dependency factory works");
122
+ expect(dm.testModule).toBeDefined();
123
+ });
124
+
57
125
  it("should throw on resolution, if config was not provided", () => {
58
126
  expect.assertions(1);
59
127
 
128
+ container.create(() => tsyringeContainer.createChildContainer());
129
+
60
130
  expect(() => {
61
131
  container.resolve("TestModule");
62
- }).toThrow(ModuleContainerErrors.configNotSetInContainer("TestModule"));
132
+ }).toThrow();
63
133
  });
64
134
 
65
135
  it("should resolve the registered module with the provided config", () => {
@@ -74,9 +144,44 @@ describe("moduleContainer", () => {
74
144
  otherTestConfigProperty: testConfigProperty,
75
145
  },
76
146
  });
147
+ container.create(() => tsyringeContainer.createChildContainer());
77
148
 
78
149
  const testModule = container.resolve("TestModule");
79
150
 
80
151
  expect(testModule.config.testConfigProperty).toBe(testConfigProperty);
152
+
153
+ const dependency = container.resolve("dependencyModule1");
154
+ dependency.x();
155
+ });
156
+
157
+ it("should stack configurations correctly", () => {
158
+ container.configure({
159
+ TestModule: {
160
+ testConfigProperty: 1,
161
+ },
162
+ OtherTestModule: {
163
+ otherTestConfigProperty: 4,
164
+ },
165
+ });
166
+
167
+ container.configurePartial({
168
+ TestModule: {
169
+ testConfigProperty2: 2,
170
+ },
171
+ });
172
+
173
+ container.configurePartial({
174
+ TestModule: {
175
+ testConfigProperty: 3,
176
+ },
177
+ });
178
+
179
+ container.create(() => tsyringeContainer.createChildContainer());
180
+
181
+ const config = container.resolve("TestModule").config;
182
+
183
+ expect(config.testConfigProperty).toBe(3);
184
+ expect(config.testConfigProperty2).toBe(2);
185
+ expect(config.testConfigProperty3).toBe(undefined);
81
186
  });
82
187
  });