Compare commits
3609 Commits
xen-api-v0
...
gab-docker
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
124fd97d38 | ||
|
|
7037b15cb3 | ||
|
|
9253718d5c | ||
|
|
99f2b42243 | ||
|
|
43104d908c | ||
|
|
fde65bcd6a | ||
|
|
26711456d5 | ||
|
|
73a0356e4f | ||
|
|
a4a828a71c | ||
|
|
3d9b9efc04 | ||
|
|
675405f7ac | ||
|
|
f8a3536a88 | ||
|
|
e527a13b50 | ||
|
|
3be03451f8 | ||
|
|
9fa15d9c84 | ||
|
|
9c3d39b4a7 | ||
|
|
28800f43ee | ||
|
|
5c0b29c51f | ||
|
|
62d9d0208b | ||
|
|
4bf871e52f | ||
|
|
103972808c | ||
|
|
dc65bb87b5 | ||
|
|
bfa0282ecc | ||
|
|
aa66ec0ccd | ||
|
|
18fe19c680 | ||
|
|
ab0e411ac0 | ||
|
|
79671e6d61 | ||
|
|
71ad9773da | ||
|
|
34ecc2bcbb | ||
|
|
53f4f265dc | ||
|
|
97624ef836 | ||
|
|
fb8d0ed924 | ||
|
|
fedbdba13d | ||
|
|
a281682f7a | ||
|
|
07e9f09692 | ||
|
|
29d6e590de | ||
|
|
3e351f8529 | ||
|
|
bfbfb9379a | ||
|
|
4f31b7007a | ||
|
|
fe0cc2ebb9 | ||
|
|
2fd6f521f8 | ||
|
|
ec00728112 | ||
|
|
7174c1edeb | ||
|
|
7bd27e7437 | ||
|
|
0a28e30003 | ||
|
|
246c793c28 | ||
|
|
5f0ea4d586 | ||
|
|
3c7d316b3c | ||
|
|
645c8f32e3 | ||
|
|
adc5e7d0c0 | ||
|
|
b9b74ab1ac | ||
|
|
64298c04f2 | ||
|
|
3dfb7db039 | ||
|
|
b64d8f5cbf | ||
|
|
c2e5225728 | ||
|
|
6c44a94bf4 | ||
|
|
a2d9310d0a | ||
|
|
05197b93ee | ||
|
|
448d115d49 | ||
|
|
ae993dff45 | ||
|
|
1bc4805f3d | ||
|
|
98fe8f3955 | ||
|
|
e902bcef67 | ||
|
|
cb2a6e43a8 | ||
|
|
b73a0992f8 | ||
|
|
d0b3d78639 | ||
|
|
e6b8939772 | ||
|
|
bc372a982c | ||
|
|
3ff8064f1b | ||
|
|
834459186d | ||
|
|
12220ad4cf | ||
|
|
f6fd1db1ef | ||
|
|
a1050882ae | ||
|
|
687df5ead4 | ||
|
|
b057881ad0 | ||
|
|
2b23550996 | ||
|
|
afeb20e589 | ||
|
|
d7794518a2 | ||
|
|
fee61a43e3 | ||
|
|
b201afd192 | ||
|
|
feef1f8b0a | ||
|
|
1a5e2fde4f | ||
|
|
609e957a55 | ||
|
|
5c18404174 | ||
|
|
866a1dd8ae | ||
|
|
3bfd6c6979 | ||
|
|
06564e9091 | ||
|
|
1702783cfb | ||
|
|
4ea0cbaa37 | ||
|
|
2246e065f7 | ||
|
|
29a38cdf1a | ||
|
|
960c569e86 | ||
|
|
fa183fc97e | ||
|
|
a1d63118c0 | ||
|
|
f95a20173c | ||
|
|
b82d0fadc3 | ||
|
|
0635b3316e | ||
|
|
113235aec3 | ||
|
|
3921401e96 | ||
|
|
2e514478a4 | ||
|
|
b3d53b230e | ||
|
|
45dcb914ba | ||
|
|
711087b686 | ||
|
|
b100a59d1d | ||
|
|
109b2b0055 | ||
|
|
9dda99eb20 | ||
|
|
fa0f75b474 | ||
|
|
2d93e0d4be | ||
|
|
fe6406336d | ||
|
|
1037d44089 | ||
|
|
a8c3669f43 | ||
|
|
d91753aa82 | ||
|
|
b548514d44 | ||
|
|
ba782d2698 | ||
|
|
0552dc23a5 | ||
|
|
574bbbf5ff | ||
|
|
df11a92cdb | ||
|
|
33ae59adf7 | ||
|
|
e0a115b41d | ||
|
|
f838d6c179 | ||
|
|
6c3229f517 | ||
|
|
6973928b1a | ||
|
|
a5daba2a4d | ||
|
|
40ef83416e | ||
|
|
8518146455 | ||
|
|
d58f563de5 | ||
|
|
ad2454adab | ||
|
|
1f32557743 | ||
|
|
e95aae2129 | ||
|
|
9176171f20 | ||
|
|
d4f2249a4d | ||
|
|
e0b4069c17 | ||
|
|
6b25a21151 | ||
|
|
716dc45d85 | ||
|
|
57850230c8 | ||
|
|
362d597031 | ||
|
|
e89b84b37b | ||
|
|
ae6f6bf536 | ||
|
|
6f765bdd6f | ||
|
|
1982c6e6e6 | ||
|
|
527dceb43f | ||
|
|
f5a3d68d07 | ||
|
|
6c904fbc96 | ||
|
|
295036a1e3 | ||
|
|
5601d61b49 | ||
|
|
1c35c1a61a | ||
|
|
4143014466 | ||
|
|
90fea69b7e | ||
|
|
625663d619 | ||
|
|
403afc7aaf | ||
|
|
d295524c3c | ||
|
|
5eb4294e70 | ||
|
|
90598522a6 | ||
|
|
519fa1bcf8 | ||
|
|
7b0e5afe37 | ||
|
|
0b6b3a47a2 | ||
|
|
75db810508 | ||
|
|
2f52c564f5 | ||
|
|
011d582b80 | ||
|
|
32d21b2308 | ||
|
|
45971ca622 | ||
|
|
f3a09f2dad | ||
|
|
552a9c7b9f | ||
|
|
ed34d9cbc0 | ||
|
|
187ee99931 | ||
|
|
ff78dd8f7c | ||
|
|
b0eadb8ea4 | ||
|
|
a95754715a | ||
|
|
18ece4b90c | ||
|
|
3862fb2664 | ||
|
|
72c69d791a | ||
|
|
d6192a4a7a | ||
|
|
0f824ffa70 | ||
|
|
f6c227e7f5 | ||
|
|
9d5bc8af6e | ||
|
|
9480079770 | ||
|
|
54fe9147ac | ||
|
|
b6a0477232 | ||
|
|
c60644c578 | ||
|
|
abdce94c5f | ||
|
|
d7dee04013 | ||
|
|
dfc62132b7 | ||
|
|
36f7f193aa | ||
|
|
ca4a82ec38 | ||
|
|
37aea1888d | ||
|
|
92f3b4ddd7 | ||
|
|
647995428c | ||
|
|
407e9c25f3 | ||
|
|
1612ab7335 | ||
|
|
b952c36210 | ||
|
|
96b5cb2c61 | ||
|
|
c5b3acfce2 | ||
|
|
20a01bf266 | ||
|
|
a33b88cf1c | ||
|
|
09a2f45ada | ||
|
|
83a7dd7ea1 | ||
|
|
afc1b6a5c0 | ||
|
|
7f4f860735 | ||
|
|
d789e3aa0d | ||
|
|
f5b91cd45d | ||
|
|
92ab4b3309 | ||
|
|
2c456e4c89 | ||
|
|
1460e63449 | ||
|
|
8291124c1f | ||
|
|
fc4d9accfd | ||
|
|
80969b785f | ||
|
|
3dfd7f1835 | ||
|
|
65daa39ebe | ||
|
|
5ad94504e3 | ||
|
|
4101bf3ba5 | ||
|
|
e9d52864ef | ||
|
|
aef2696426 | ||
|
|
94c755b102 | ||
|
|
279b457348 | ||
|
|
b5988bb8b7 | ||
|
|
f73b1d8b40 | ||
|
|
b2ccb07a95 | ||
|
|
9560cc4e33 | ||
|
|
e87c380556 | ||
|
|
b0846876f7 | ||
|
|
477ed67957 | ||
|
|
5acacd7e1e | ||
|
|
8d542fe9c0 | ||
|
|
b0cb249ae9 | ||
|
|
185509a0cf | ||
|
|
08298d3284 | ||
|
|
7a4cec5093 | ||
|
|
f44f5199c6 | ||
|
|
81abc091de | ||
|
|
7e4f4c445d | ||
|
|
5a673c1833 | ||
|
|
266231ae0f | ||
|
|
9e87a887cb | ||
|
|
12e98bfd31 | ||
|
|
249f124ba6 | ||
|
|
131643a91b | ||
|
|
df3df18690 | ||
|
|
5401d17610 | ||
|
|
90ea2284c6 | ||
|
|
a4c5792f9e | ||
|
|
5723598923 | ||
|
|
aa0b2ff93a | ||
|
|
be6233f12b | ||
|
|
17df749790 | ||
|
|
97f852f8e8 | ||
|
|
dc3446d61a | ||
|
|
c830a0b208 | ||
|
|
ff0307b68f | ||
|
|
1c3cad9235 | ||
|
|
ccafc15b66 | ||
|
|
a40d6b32e3 | ||
|
|
de1ee92fe7 | ||
|
|
c7227d2f50 | ||
|
|
b2cebbfaf4 | ||
|
|
30fbbc92ca | ||
|
|
d1b210cf16 | ||
|
|
9963568368 | ||
|
|
ffc3249b33 | ||
|
|
29826db81b | ||
|
|
5367a76db5 | ||
|
|
2512a00205 | ||
|
|
72a3a9f04f | ||
|
|
b566e0fd46 | ||
|
|
4621fb4e9b | ||
|
|
7f3d25964f | ||
|
|
4b3728e8d8 | ||
|
|
5218d6df1a | ||
|
|
94b2b8ec70 | ||
|
|
6d1086539e | ||
|
|
7f758bbb73 | ||
|
|
62b88200c3 | ||
|
|
ce42883268 | ||
|
|
6b60cfce4d | ||
|
|
aebb47ad38 | ||
|
|
41f5634b7a | ||
|
|
87ddb01122 | ||
|
|
6898eea45e | ||
|
|
ba2679d3d7 | ||
|
|
971cdaa44f | ||
|
|
005d3b5976 | ||
|
|
663403cb14 | ||
|
|
b341e38623 | ||
|
|
8246db30cb | ||
|
|
9c9c656620 | ||
|
|
f36be0d5e0 | ||
|
|
72090ea8ff | ||
|
|
8d64a0a232 | ||
|
|
35974a0a33 | ||
|
|
3023439028 | ||
|
|
77f4a09d74 | ||
|
|
0fc797f7d0 | ||
|
|
0b02c84e33 | ||
|
|
e03ff0a9be | ||
|
|
d91f1841c0 | ||
|
|
0effc9cfc1 | ||
|
|
f08cbb458d | ||
|
|
b8c9770d43 | ||
|
|
44ff5d0e4d | ||
|
|
ecb580a629 | ||
|
|
0623d837c1 | ||
|
|
f92d1ce4ac | ||
|
|
88f84069d6 | ||
|
|
b9b7081184 | ||
|
|
ce3e0817db | ||
|
|
55b65a8bf6 | ||
|
|
6767141661 | ||
|
|
2eb3b15930 | ||
|
|
b63c4a0d4f | ||
|
|
1269ddfeae | ||
|
|
afd47f5522 | ||
|
|
7ede6bdbce | ||
|
|
03b505e40e | ||
|
|
ed7ff1fad4 | ||
|
|
2dda1aecce | ||
|
|
720e363577 | ||
|
|
545a65521a | ||
|
|
0cf6f94677 | ||
|
|
14e205ab69 | ||
|
|
c3da87a40c | ||
|
|
5d93b05088 | ||
|
|
2cdd33cb7a | ||
|
|
dc909fdfb0 | ||
|
|
a43199b754 | ||
|
|
876211879f | ||
|
|
fe323b8fe5 | ||
|
|
b60f5d593b | ||
|
|
2d4317b681 | ||
|
|
caf0eb3762 | ||
|
|
c1aa7b9d8a | ||
|
|
6c6efd9cfb | ||
|
|
551670a8b9 | ||
|
|
ac75225e7d | ||
|
|
20dbbeb38e | ||
|
|
37dea9980e | ||
|
|
5cec2d4cb0 | ||
|
|
ed76fa5141 | ||
|
|
389a765825 | ||
|
|
3bad40095a | ||
|
|
1a51c66028 | ||
|
|
05161bd4df | ||
|
|
db1102750f | ||
|
|
42a974476f | ||
|
|
0dd91c1efe | ||
|
|
756d2fe4e7 | ||
|
|
61c64b49c7 | ||
|
|
c2eb68a31a | ||
|
|
f1a1b922c7 | ||
|
|
a2dcceb470 | ||
|
|
1d78fdd673 | ||
|
|
4a53749ca0 | ||
|
|
7f73ec52d6 | ||
|
|
4abb172976 | ||
|
|
c52e0a5531 | ||
|
|
0197758780 | ||
|
|
e2521b6688 | ||
|
|
13f19de1a0 | ||
|
|
5e589019d0 | ||
|
|
feaad13ac3 | ||
|
|
ab9428a9c4 | ||
|
|
c964a1471a | ||
|
|
424322f7b7 | ||
|
|
956a4f8b2a | ||
|
|
d87210e903 | ||
|
|
3d3b63a596 | ||
|
|
4f9636b4c3 | ||
|
|
74c8d56046 | ||
|
|
2d6b827fd2 | ||
|
|
f82eb8aeb4 | ||
|
|
f1ab62524c | ||
|
|
ce78d22bb8 | ||
|
|
99a1dbeae1 | ||
|
|
2a71e28253 | ||
|
|
46fe3be322 | ||
|
|
9da65b6c7c | ||
|
|
ad02700b51 | ||
|
|
8fd10bace7 | ||
|
|
9d09a3adf5 | ||
|
|
4350f0cd1a | ||
|
|
5dc993255c | ||
|
|
e9188a9864 | ||
|
|
42dd70c2f7 | ||
|
|
191c124130 | ||
|
|
2742f948c2 | ||
|
|
455a3ba677 | ||
|
|
1961da9aed | ||
|
|
e82d9d7a74 | ||
|
|
dfb3166bed | ||
|
|
5a54f7f302 | ||
|
|
6002a497fe | ||
|
|
3fa8b6332e | ||
|
|
1b521b87c5 | ||
|
|
8b7d2aab6b | ||
|
|
b0006f91f4 | ||
|
|
31aaa968ec | ||
|
|
facb4593f0 | ||
|
|
d1a30363b4 | ||
|
|
eac5347f32 | ||
|
|
2006665fe8 | ||
|
|
26a3862d61 | ||
|
|
2371109b6f | ||
|
|
243bffebbd | ||
|
|
e69ae7b0db | ||
|
|
5aff7b94d8 | ||
|
|
a65058ddd5 | ||
|
|
b1e81d84c6 | ||
|
|
96e60f7e4f | ||
|
|
5e59c617e8 | ||
|
|
69ad0ade6e | ||
|
|
37cdbc19ef | ||
|
|
6cbce81faa | ||
|
|
8c14906a60 | ||
|
|
62591e1f6f | ||
|
|
ea4a888c5e | ||
|
|
281a1cc549 | ||
|
|
d52dcd0708 | ||
|
|
d8e01b2867 | ||
|
|
dca3f39156 | ||
|
|
31e964fe0f | ||
|
|
39d973c43f | ||
|
|
55f921959d | ||
|
|
6598090662 | ||
|
|
d7f29e7363 | ||
|
|
82df6089c3 | ||
|
|
80cc66964e | ||
|
|
7883d38622 | ||
|
|
2cb5169b6d | ||
|
|
9ad2c07984 | ||
|
|
a9c1239149 | ||
|
|
cb1223f72e | ||
|
|
4dc7575d5b | ||
|
|
276d1ce60a | ||
|
|
58ab32a623 | ||
|
|
c1846e6ff3 | ||
|
|
826de17111 | ||
|
|
8a09ea8bc1 | ||
|
|
1297c925ad | ||
|
|
74d15e1a92 | ||
|
|
ae373c3e77 | ||
|
|
e9b90caa3a | ||
|
|
b89e77a6a4 | ||
|
|
61691ac46b | ||
|
|
512b96af24 | ||
|
|
d369593979 | ||
|
|
2f38e0564b | ||
|
|
5e8dd4e4bc | ||
|
|
8f9f1f566d | ||
|
|
d7870b8860 | ||
|
|
97fa23f890 | ||
|
|
f839887da8 | ||
|
|
15bfaa15ca | ||
|
|
4a3183ffa0 | ||
|
|
18d03a076b | ||
|
|
4bed4195ac | ||
|
|
a963878af5 | ||
|
|
d6c3dc87e0 | ||
|
|
5391a9a5ad | ||
|
|
b50e95802c | ||
|
|
75a9799e96 | ||
|
|
dbb9e4d60f | ||
|
|
d27b6bd49d | ||
|
|
c5d2726faa | ||
|
|
a2a98c490f | ||
|
|
e2dc1d98f1 | ||
|
|
658c26d3c9 | ||
|
|
612095789a | ||
|
|
7418d9f670 | ||
|
|
f344c58a62 | ||
|
|
36b94f745d | ||
|
|
08cdcf4112 | ||
|
|
76813737ef | ||
|
|
53d15d6a77 | ||
|
|
dd01b62b87 | ||
|
|
9fab15537b | ||
|
|
d87db05b2b | ||
|
|
f1f32c962c | ||
|
|
ad149740b1 | ||
|
|
9a4e938b91 | ||
|
|
a226760b07 | ||
|
|
a11450c3a7 | ||
|
|
e0cab4f937 | ||
|
|
468250f291 | ||
|
|
d04b93c17e | ||
|
|
911556a1aa | ||
|
|
c7d3230eef | ||
|
|
b63086bf09 | ||
|
|
a4118a5676 | ||
|
|
26e7e6467c | ||
|
|
1c9552fa58 | ||
|
|
9875cb5575 | ||
|
|
d1c6bb8829 | ||
|
|
ef7005a291 | ||
|
|
8068b83ffe | ||
|
|
f01a89710c | ||
|
|
38ced81ada | ||
|
|
9834632d59 | ||
|
|
bb4504dd50 | ||
|
|
8864c2f2db | ||
|
|
19208472e6 | ||
|
|
10c77ba3cc | ||
|
|
cd28fd4945 | ||
|
|
6778d6aa4a | ||
|
|
433851d771 | ||
|
|
d157fd3528 | ||
|
|
9150823c37 | ||
|
|
07c3a44441 | ||
|
|
051bbf9449 | ||
|
|
22ea1c0e2a | ||
|
|
6432a44860 | ||
|
|
493d861de3 | ||
|
|
82452e9616 | ||
|
|
2fbeaa618a | ||
|
|
6c08afaa0e | ||
|
|
af4cc1f574 | ||
|
|
2fb27b26cd | ||
|
|
11e09e1f87 | ||
|
|
9ccb5f8aa9 | ||
|
|
af87d6a0ea | ||
|
|
d847f45cb3 | ||
|
|
38c615609a | ||
|
|
144cc4b82f | ||
|
|
d24ab141e9 | ||
|
|
8505374fcf | ||
|
|
e53d961fc3 | ||
|
|
dc8ca7a8ee | ||
|
|
3d1b87d9dc | ||
|
|
01fa2af5cd | ||
|
|
20a89ca45a | ||
|
|
16ca2f8da9 | ||
|
|
30fe9764ad | ||
|
|
e246c8ee47 | ||
|
|
ba03a48498 | ||
|
|
b96dd0160a | ||
|
|
49890a09b7 | ||
|
|
dfce56cee8 | ||
|
|
a6fee2946a | ||
|
|
34c849ee89 | ||
|
|
c7192ed3bf | ||
|
|
4d3dc0c5f7 | ||
|
|
9ba4afa073 | ||
|
|
3ea4422d13 | ||
|
|
de2e314f7d | ||
|
|
2380fb42fe | ||
|
|
95b76076a3 | ||
|
|
b415d4c34c | ||
|
|
2d82b6dd6e | ||
|
|
16b1935f12 | ||
|
|
50ec614b2a | ||
|
|
9e11a0af6e | ||
|
|
0c3e42e0b9 | ||
|
|
36b31bb0b3 | ||
|
|
c03c41450b | ||
|
|
dfc2b5d88b | ||
|
|
87e3e3ffe3 | ||
|
|
dae37c6a50 | ||
|
|
c7df11cc6f | ||
|
|
87f1f208c3 | ||
|
|
ba8c5d740e | ||
|
|
c275d5d999 | ||
|
|
cfc53c9c94 | ||
|
|
87df917157 | ||
|
|
395d87d290 | ||
|
|
aff8ec08ad | ||
|
|
4d40b56d85 | ||
|
|
667d0724c3 | ||
|
|
a49395553a | ||
|
|
cce09bd9cc | ||
|
|
03a66e4690 | ||
|
|
fd752fee80 | ||
|
|
8a71f84733 | ||
|
|
9ef2c7da4c | ||
|
|
8975073416 | ||
|
|
d1c1378c9d | ||
|
|
7941284a1d | ||
|
|
af2d17b7a5 | ||
|
|
3ca2b01d9a | ||
|
|
67193a2ab7 | ||
|
|
9757aa36de | ||
|
|
29854a9f87 | ||
|
|
b12c179470 | ||
|
|
bbef15e4e4 | ||
|
|
c483929a0d | ||
|
|
1741f395dd | ||
|
|
0f29262797 | ||
|
|
31ed477b96 | ||
|
|
9e5de5413d | ||
|
|
0f297a81a4 | ||
|
|
89313def99 | ||
|
|
8e0be4edaf | ||
|
|
a8dfdfb922 | ||
|
|
f096024248 | ||
|
|
4f50f90213 | ||
|
|
4501902331 | ||
|
|
df19679dba | ||
|
|
9f5a2f67f9 | ||
|
|
2d5c406325 | ||
|
|
151b8a8940 | ||
|
|
cda027b94a | ||
|
|
ee2117abf6 | ||
|
|
6e7294d49f | ||
|
|
062e45f697 | ||
|
|
d18b39990d | ||
|
|
7387ac2411 | ||
|
|
4186592f9f | ||
|
|
6c9d5a72a6 | ||
|
|
83690a4dd4 | ||
|
|
c11e03ab26 | ||
|
|
c7d8709267 | ||
|
|
6579deffad | ||
|
|
e2739e7a4b | ||
|
|
c0d587f541 | ||
|
|
05a96ffc14 | ||
|
|
32a47444d7 | ||
|
|
9ff5de5f33 | ||
|
|
09badf33d0 | ||
|
|
1643d3637f | ||
|
|
b962e9ebe8 | ||
|
|
66f3528e10 | ||
|
|
a5e9f051a2 | ||
|
|
63bfb76516 | ||
|
|
f88f7d41aa | ||
|
|
877383ac85 | ||
|
|
dd5e11e835 | ||
|
|
3d43550ffe | ||
|
|
115bc8fa0a | ||
|
|
15c46e324c | ||
|
|
df38366066 | ||
|
|
28b13ccfff | ||
|
|
26a433ebbe | ||
|
|
1902595190 | ||
|
|
80146cfb58 | ||
|
|
03d2d6fc94 | ||
|
|
379e4d7596 | ||
|
|
9860bd770b | ||
|
|
2af5328a0f | ||
|
|
4084a44f83 | ||
|
|
ba7c7ddb23 | ||
|
|
2351e7b98c | ||
|
|
d353dc622c | ||
|
|
3ef6adfd02 | ||
|
|
5063a6982a | ||
|
|
0008f2845c | ||
|
|
a0994bc428 | ||
|
|
8fe0d97aec | ||
|
|
a8b3c02780 | ||
|
|
f3489fb57c | ||
|
|
434b5b375d | ||
|
|
445120f9f5 | ||
|
|
71b11f0d9c | ||
|
|
8297a9e0e7 | ||
|
|
4999672f2d | ||
|
|
70608ed7e9 | ||
|
|
a0836ebdd7 | ||
|
|
2b1edd1d4c | ||
|
|
42bb7cc973 | ||
|
|
8299c37bb7 | ||
|
|
7a2005c20c | ||
|
|
ae0eb9e66e | ||
|
|
052126613a | ||
|
|
7959657bd6 | ||
|
|
9f8bb376ea | ||
|
|
ee8e2fa906 | ||
|
|
33a380b173 | ||
|
|
6e5b6996fa | ||
|
|
6409dc276c | ||
|
|
98f7ce43e3 | ||
|
|
aa076e1d2d | ||
|
|
7a096d1b5c | ||
|
|
93b17ccddd | ||
|
|
68c118c3e5 | ||
|
|
c0b0ba433f | ||
|
|
d7d81431ef | ||
|
|
7451f45885 | ||
|
|
c9882001a9 | ||
|
|
837b06ef2b | ||
|
|
0e49150b8e | ||
|
|
0ec5f4bf68 | ||
|
|
601730d737 | ||
|
|
28eb4b21bd | ||
|
|
a5afe0bca1 | ||
|
|
ad5691dcb2 | ||
|
|
80974fa1dc | ||
|
|
78330a0e11 | ||
|
|
b6cff2d784 | ||
|
|
cae3555ca7 | ||
|
|
1f9cf458ec | ||
|
|
d9ead2d9f5 | ||
|
|
92660fd03e | ||
|
|
5393d847f0 | ||
|
|
231f09de12 | ||
|
|
b75ca2700b | ||
|
|
bae7ef9067 | ||
|
|
8ec8a3b4d9 | ||
|
|
5b7228ed69 | ||
|
|
b02bf90c8a | ||
|
|
7d3546734e | ||
|
|
030013eb5b | ||
|
|
da181345a6 | ||
|
|
30874b2206 | ||
|
|
2ed6b2dc87 | ||
|
|
41532f35d1 | ||
|
|
7a198a44cd | ||
|
|
77d615d15b | ||
|
|
c7bc397c85 | ||
|
|
38388cc297 | ||
|
|
a7b17b2b8c | ||
|
|
d93afc4648 | ||
|
|
24449e41bb | ||
|
|
df6f3ed165 | ||
|
|
ca5914dbfb | ||
|
|
3c3a1f8981 | ||
|
|
01810f35b2 | ||
|
|
5db4083414 | ||
|
|
8bf3a747f0 | ||
|
|
f0e817a8d9 | ||
|
|
b181c59698 | ||
|
|
cfa094f208 | ||
|
|
9ee5a8d089 | ||
|
|
819127da57 | ||
|
|
6e9659a797 | ||
|
|
07bd9cadd4 | ||
|
|
a1bcd35e26 | ||
|
|
1a741e18fd | ||
|
|
2e133dd0fb | ||
|
|
ecae554a78 | ||
|
|
4bed50b4ed | ||
|
|
c92b371d9e | ||
|
|
35e6bb30db | ||
|
|
1aaa123f47 | ||
|
|
a8c507a1df | ||
|
|
581e3c358f | ||
|
|
e4f1b8f2e0 | ||
|
|
29e8a7fd7e | ||
|
|
4af289c492 | ||
|
|
cd95793054 | ||
|
|
ab71578cf2 | ||
|
|
df07d4a393 | ||
|
|
2518395c03 | ||
|
|
50f3ab7798 | ||
|
|
2d01056ea9 | ||
|
|
f40fb3bab3 | ||
|
|
fe7c60654d | ||
|
|
728b640ff8 | ||
|
|
55c247e5d0 | ||
|
|
6be15b780a | ||
|
|
150c552ef9 | ||
|
|
7005c1f5e5 | ||
|
|
a66ae33d5d | ||
|
|
8ed8447665 | ||
|
|
e740719732 | ||
|
|
bfd9238f6d | ||
|
|
cca47a8149 | ||
|
|
3ecf099fe0 | ||
|
|
6f56dc0339 | ||
|
|
20108208d0 | ||
|
|
0706e6f4ff | ||
|
|
af85df611c | ||
|
|
3c1239cfb8 | ||
|
|
50d144bf93 | ||
|
|
9a5a03d032 | ||
|
|
854ae0f65e | ||
|
|
4fb34ffee9 | ||
|
|
bbf3dae37f | ||
|
|
e69f58eb86 | ||
|
|
c9475ddc65 | ||
|
|
31d085b6a1 | ||
|
|
173866236f | ||
|
|
b176780527 | ||
|
|
89c72fdbad | ||
|
|
7d6e832226 | ||
|
|
c024346475 | ||
|
|
95ec5929b4 | ||
|
|
1646c50a94 | ||
|
|
b1429e1df3 | ||
|
|
6da0aa376f | ||
|
|
1ab5503558 | ||
|
|
4b9db257fd | ||
|
|
96f83d92fc | ||
|
|
7c7ee7fb9b | ||
|
|
2bd3d57f8a | ||
|
|
3f1c41a4f7 | ||
|
|
ef819f4d53 | ||
|
|
23189ed8f9 | ||
|
|
6b8a345241 | ||
|
|
b3cc168571 | ||
|
|
00b740c549 | ||
|
|
472bececb1 | ||
|
|
4fe9a4eb59 | ||
|
|
d331cd934a | ||
|
|
f6e1b95711 | ||
|
|
edec412bc7 | ||
|
|
e142bacb67 | ||
|
|
915e4b66a3 | ||
|
|
ee47a361b1 | ||
|
|
3ab2dad19b | ||
|
|
635c6db83a | ||
|
|
d2a13f531a | ||
|
|
e44857c023 | ||
|
|
749cdd011b | ||
|
|
4fde005b7f | ||
|
|
0a975fc0cc | ||
|
|
f5b7c59203 | ||
|
|
c775559912 | ||
|
|
4b9116ed72 | ||
|
|
178501e252 | ||
|
|
c9391abfd9 | ||
|
|
f7a7d9e52d | ||
|
|
5a65087aeb | ||
|
|
3cfeb8f492 | ||
|
|
0498e2d679 | ||
|
|
19563d0b3c | ||
|
|
4cc1d98a42 | ||
|
|
cd408c1687 | ||
|
|
546859531b | ||
|
|
857e467672 | ||
|
|
5de5a80eee | ||
|
|
5f1333b2a0 | ||
|
|
5e9548edbc | ||
|
|
e42edf2164 | ||
|
|
7e8c524a43 | ||
|
|
8d6ba86118 | ||
|
|
dc27317d0b | ||
|
|
0b8032c7a0 | ||
|
|
d3e18a81aa | ||
|
|
4707573c57 | ||
|
|
218e2deef4 | ||
|
|
47afff3bab | ||
|
|
24ce400429 | ||
|
|
ceac341016 | ||
|
|
7830c89e66 | ||
|
|
fa6ca01de9 | ||
|
|
746bff55a8 | ||
|
|
ec81e90153 | ||
|
|
74c9d06d91 | ||
|
|
3cfbd8b70c | ||
|
|
046d9b80bb | ||
|
|
516fda2a3a | ||
|
|
bc5c103cec | ||
|
|
98f0c068ad | ||
|
|
28e0a5a3a3 | ||
|
|
5f9d6db761 | ||
|
|
9bec52074f | ||
|
|
dfa5009a9b | ||
|
|
fbb5c47358 | ||
|
|
f2ca67a7f4 | ||
|
|
3a9af92571 | ||
|
|
b26148bf62 | ||
|
|
7e27e0bda8 | ||
|
|
94df05566a | ||
|
|
efa46414fd | ||
|
|
311623d71a | ||
|
|
89894b0f08 | ||
|
|
ed590eeb41 | ||
|
|
8956902c52 | ||
|
|
720d9649eb | ||
|
|
ce95041821 | ||
|
|
9cd84ac834 | ||
|
|
eddd6880f2 | ||
|
|
e7f9111ab5 | ||
|
|
0eb2ee72a4 | ||
|
|
1aa4fac528 | ||
|
|
3f07f7ef41 | ||
|
|
886ff2cd70 | ||
|
|
ed09608952 | ||
|
|
6aa5ec6eb6 | ||
|
|
eee4981d4c | ||
|
|
ab2c0f905a | ||
|
|
4eca3d169e | ||
|
|
e69944eaaf | ||
|
|
bc987b2dda | ||
|
|
244b150385 | ||
|
|
3acbc08ec5 | ||
|
|
1ce7e5d8a4 | ||
|
|
7813fe232a | ||
|
|
1b273071b2 | ||
|
|
43236f25ad | ||
|
|
5f2b1acfea | ||
|
|
b820dcd73f | ||
|
|
0a412c0ef7 | ||
|
|
db75f8046c | ||
|
|
7bdd1a4a3a | ||
|
|
b7d39105e8 | ||
|
|
f8b5dbbba5 | ||
|
|
c99120bd24 | ||
|
|
b9ff3db9b0 | ||
|
|
8ce1b4bf71 | ||
|
|
0b41a2b132 | ||
|
|
575ed92593 | ||
|
|
d3fe37b879 | ||
|
|
8318cb991b | ||
|
|
9779fb1d29 | ||
|
|
43b4e8c924 | ||
|
|
756a206a9e | ||
|
|
324ff44886 | ||
|
|
04913cabba | ||
|
|
c67b550435 | ||
|
|
88bf60b8c2 | ||
|
|
08fd2beb52 | ||
|
|
a870813ac5 | ||
|
|
c31f0100cb | ||
|
|
bc7fc750f0 | ||
|
|
9cd1784667 | ||
|
|
fbc7baa9a4 | ||
|
|
a4e5cf3914 | ||
|
|
e14c177f39 | ||
|
|
b5b53c636c | ||
|
|
33b45d2eda | ||
|
|
4c9bb71626 | ||
|
|
e9aa88e637 | ||
|
|
e7c7c2ce3e | ||
|
|
686e7e9527 | ||
|
|
86e390f70f | ||
|
|
e04352a577 | ||
|
|
ee4e7620b5 | ||
|
|
975dc4f314 | ||
|
|
dfff96ace1 | ||
|
|
f01171bb8b | ||
|
|
8f248f8d14 | ||
|
|
ae815a80b1 | ||
|
|
925514b0a3 | ||
|
|
45639e5a5c | ||
|
|
17ab78f064 | ||
|
|
c43a3343d8 | ||
|
|
095e40d1dd | ||
|
|
a400ef823f | ||
|
|
7684fbd5ec | ||
|
|
cd64e8dc28 | ||
|
|
dbba918b79 | ||
|
|
88dbe35c4b | ||
|
|
4a61fedf97 | ||
|
|
110181679b | ||
|
|
b0a0092a73 | ||
|
|
a6a975ce40 | ||
|
|
a73eb82ac6 | ||
|
|
702cade0d7 | ||
|
|
367b86bc7a | ||
|
|
7c5e823805 | ||
|
|
0975863d98 | ||
|
|
6e6886a6ba | ||
|
|
8bf11d6f6a | ||
|
|
6c97ccafd5 | ||
|
|
72ff66ebde | ||
|
|
b3d0da7392 | ||
|
|
a1c0d82889 | ||
|
|
fbd708d1b5 | ||
|
|
710ce965a1 | ||
|
|
2ee1535095 | ||
|
|
471fbef6ef | ||
|
|
ab63709104 | ||
|
|
0924184358 | ||
|
|
6e38cf2bc1 | ||
|
|
2183422eb5 | ||
|
|
3842f5b16d | ||
|
|
1dad6b37ef | ||
|
|
7023db2264 | ||
|
|
bf194f5c85 | ||
|
|
bcdaa37f8a | ||
|
|
a5f8900d23 | ||
|
|
3c684c7f45 | ||
|
|
cd0f4baa15 | ||
|
|
58f3050344 | ||
|
|
ab110bb0df | ||
|
|
636025c1b9 | ||
|
|
c28fa78963 | ||
|
|
ecb66fb9f3 | ||
|
|
f8e4192d34 | ||
|
|
3d15cd57c4 | ||
|
|
2e633f0df4 | ||
|
|
833b814fd5 | ||
|
|
da52d905b6 | ||
|
|
a2054c24d8 | ||
|
|
599db80467 | ||
|
|
6075a3307e | ||
|
|
9af06996c0 | ||
|
|
72c386cdce | ||
|
|
f6d4771603 | ||
|
|
15d06c591e | ||
|
|
0a07765027 | ||
|
|
81e0d06604 | ||
|
|
809314e516 | ||
|
|
cbe37b2ccd | ||
|
|
14fcbb6b8b | ||
|
|
bac976144e | ||
|
|
7677620645 | ||
|
|
c8597bd363 | ||
|
|
899be12418 | ||
|
|
31975b6737 | ||
|
|
315549e588 | ||
|
|
457886d538 | ||
|
|
8d02b206e0 | ||
|
|
45e5b94b4f | ||
|
|
ec866b4a61 | ||
|
|
2e8e2522e5 | ||
|
|
96bd46c3a8 | ||
|
|
797ed9ac51 | ||
|
|
7089dcddff | ||
|
|
8ee93dfe9c | ||
|
|
88f160820f | ||
|
|
b61b3e75c1 | ||
|
|
a1bac51f7b | ||
|
|
fa986fb4cb | ||
|
|
aa27b3be64 | ||
|
|
97d94b7952 | ||
|
|
96eb793298 | ||
|
|
b4f15de7be | ||
|
|
ae5726b836 | ||
|
|
692e72a78a | ||
|
|
ff24364bb6 | ||
|
|
b60a1958b6 | ||
|
|
f6a2b505db | ||
|
|
38aacdbd7d | ||
|
|
089b877cc5 | ||
|
|
81e55dcf77 | ||
|
|
58dd44bf5d | ||
|
|
3aa6669fd9 | ||
|
|
c10601d905 | ||
|
|
e15be7ebd3 | ||
|
|
b465a91cd3 | ||
|
|
f304a46bea | ||
|
|
6756faa1cc | ||
|
|
73fd7c7d54 | ||
|
|
60eda9ec69 | ||
|
|
a979c29a15 | ||
|
|
8f25082917 | ||
|
|
9375b1c8bd | ||
|
|
422a22a767 | ||
|
|
249f638495 | ||
|
|
6cf5e10195 | ||
|
|
b78a946458 | ||
|
|
e8a5694d51 | ||
|
|
514fa72ee2 | ||
|
|
e9ca13aa12 | ||
|
|
57f1ec6716 | ||
|
|
02e32cc9b9 | ||
|
|
902abd5d94 | ||
|
|
53380802ec | ||
|
|
af5d8d02b6 | ||
|
|
7abba76f03 | ||
|
|
79b22057d9 | ||
|
|
366daef718 | ||
|
|
a5ff0ba799 | ||
|
|
c2c6febb88 | ||
|
|
f119c72a7f | ||
|
|
8aee897d23 | ||
|
|
729db5c662 | ||
|
|
61c46df7bf | ||
|
|
9b1a04338d | ||
|
|
d307134d22 | ||
|
|
5bc44363f9 | ||
|
|
68c4fac3ab | ||
|
|
6ad9245019 | ||
|
|
763cf771fb | ||
|
|
3160b08637 | ||
|
|
f8949958a3 | ||
|
|
8b7ac07d2d | ||
|
|
044df9adba | ||
|
|
040139f4cc | ||
|
|
7b73bb9df0 | ||
|
|
24c8370daa | ||
|
|
029c4921d7 | ||
|
|
3a74c71f1a | ||
|
|
6022a1bbaa | ||
|
|
4e88c993f7 | ||
|
|
c9a61f467c | ||
|
|
e6a5f42f63 | ||
|
|
a373823eea | ||
|
|
b5e010eac8 | ||
|
|
50ffe58655 | ||
|
|
07eb3b59b3 | ||
|
|
5177b5e142 | ||
|
|
3c984e21cd | ||
|
|
aa2b27e22b | ||
|
|
14a7f00c90 | ||
|
|
56f98601bd | ||
|
|
027a8c675e | ||
|
|
bdaba9a767 | ||
|
|
4e9090f60d | ||
|
|
73b445d371 | ||
|
|
75bfc283af | ||
|
|
727de19b89 | ||
|
|
5d605d1bd7 | ||
|
|
ffdd1dfd6f | ||
|
|
d45418eb29 | ||
|
|
6ccc9d1ade | ||
|
|
93069159dd | ||
|
|
8c4780131f | ||
|
|
02ae8bceda | ||
|
|
bb10bbc945 | ||
|
|
478d88e97f | ||
|
|
6fb397a729 | ||
|
|
18dae34778 | ||
|
|
243566e936 | ||
|
|
87f4fd675d | ||
|
|
dec6b59a9f | ||
|
|
e51baedf7f | ||
|
|
530da14e24 | ||
|
|
02da7c272f | ||
|
|
a07c5418e9 | ||
|
|
c080db814b | ||
|
|
3abe13c006 | ||
|
|
fb331c0a2c | ||
|
|
19ea78afc5 | ||
|
|
2096c782e3 | ||
|
|
79a6a8a10c | ||
|
|
5a933bad93 | ||
|
|
7e302fd1cb | ||
|
|
cf9f0da6e5 | ||
|
|
10ac23e265 | ||
|
|
dc2e1cba1f | ||
|
|
7bfd190c22 | ||
|
|
c3bafeb468 | ||
|
|
7bacd781cf | ||
|
|
ee005c3679 | ||
|
|
315f54497a | ||
|
|
e30233347b | ||
|
|
56d4a7f01e | ||
|
|
7c110eebd8 | ||
|
|
39394f8c09 | ||
|
|
3283130dfc | ||
|
|
3146a591d0 | ||
|
|
e478b1ec04 | ||
|
|
7bc4d14f46 | ||
|
|
f3eeeef389 | ||
|
|
8d69208197 | ||
|
|
2c689af1a9 | ||
|
|
cb2a34c765 | ||
|
|
465c8f9009 | ||
|
|
8ea4c1c1fd | ||
|
|
ba0f7df9e8 | ||
|
|
e47dd723b0 | ||
|
|
fca6e2f6bf | ||
|
|
faa7ba6f24 | ||
|
|
fc2dbbe3ee | ||
|
|
cc98b81825 | ||
|
|
eb4a7069d4 | ||
|
|
4f65d9214e | ||
|
|
4d3c8ee63c | ||
|
|
e41c1b826a | ||
|
|
644bb48135 | ||
|
|
c9809285f6 | ||
|
|
5704949f4d | ||
|
|
a19e00fbc0 | ||
|
|
470a9b3e27 | ||
|
|
ace31dc566 | ||
|
|
ed252276cb | ||
|
|
26d0ff3c9a | ||
|
|
ff806a3ff9 | ||
|
|
949b17dee6 | ||
|
|
b1fdc68623 | ||
|
|
f502facfd1 | ||
|
|
bf0a74d709 | ||
|
|
7296d98313 | ||
|
|
30568ced49 | ||
|
|
5e1284a9e0 | ||
|
|
27d2de872a | ||
|
|
03d6e3356b | ||
|
|
ca8baa62fb | ||
|
|
2f607357c6 | ||
|
|
2de80f7aff | ||
|
|
386058ed88 | ||
|
|
033fa9e067 | ||
|
|
e8104420b5 | ||
|
|
ae24b10da0 | ||
|
|
407b05b643 | ||
|
|
79bf8bc9f6 | ||
|
|
65d6dca52c | ||
|
|
66eeefbd7b | ||
|
|
c10bbcde00 | ||
|
|
fe69928bcc | ||
|
|
3ad8508ea5 | ||
|
|
1f1ae759e0 | ||
|
|
6e4bfe8f0f | ||
|
|
6276c48768 | ||
|
|
f6005baf1a | ||
|
|
b62fdbc6a6 | ||
|
|
bbd3d31b6a | ||
|
|
481ac92bf8 | ||
|
|
a2f2b50f57 | ||
|
|
bbab9d0f36 | ||
|
|
7f8190056d | ||
|
|
8f4737c5f1 | ||
|
|
c5adba3c97 | ||
|
|
d91eb9e396 | ||
|
|
1b47102d6c | ||
|
|
cd147f3fc5 | ||
|
|
c3acdc8cbd | ||
|
|
c3d755dc7b | ||
|
|
6f49c48bd4 | ||
|
|
446f390b3d | ||
|
|
966091593a | ||
|
|
d5f21bc27c | ||
|
|
8c3b452c0d | ||
|
|
9cacb92c2c | ||
|
|
7a1b56db87 | ||
|
|
56c3d70149 | ||
|
|
1ec8fcc73f | ||
|
|
060b16c5ca | ||
|
|
0acc52e3e9 | ||
|
|
a9c2c9b6ba | ||
|
|
5b2a6bc56b | ||
|
|
19c8693b62 | ||
|
|
c4720e1215 | ||
|
|
b6d4c8044c | ||
|
|
57dd6ebfba | ||
|
|
c75569f278 | ||
|
|
a8757f9074 | ||
|
|
f5c3bf72e5 | ||
|
|
d7ee13f98d | ||
|
|
1f47aa491d | ||
|
|
ffe430758e | ||
|
|
a4bb453401 | ||
|
|
5c8ebce9eb | ||
|
|
8b0cee5e6f | ||
|
|
e5f4f825b6 | ||
|
|
b179dc1d56 | ||
|
|
7281c9505d | ||
|
|
4db82f447d | ||
|
|
834da3d2b4 | ||
|
|
c6c3a33dcc | ||
|
|
fb720d9b05 | ||
|
|
547d318e55 | ||
|
|
cb5a2c18f2 | ||
|
|
e01ca3ad07 | ||
|
|
314d193f35 | ||
|
|
e0200bb730 | ||
|
|
2a3f4a6f97 | ||
|
|
88628bbdc0 | ||
|
|
cb7b695a72 | ||
|
|
ae549e2a88 | ||
|
|
7f9a970714 | ||
|
|
7661d3372d | ||
|
|
dbb4f34015 | ||
|
|
8f15a4c29d | ||
|
|
1b0a885ac3 | ||
|
|
f7195bad88 | ||
|
|
15630aee5e | ||
|
|
a950a1fe24 | ||
|
|
71b8e625fe | ||
|
|
e7391675fb | ||
|
|
84fdd3fe4b | ||
|
|
4dc4b635f2 | ||
|
|
ee0c6d7f8b | ||
|
|
a637af395d | ||
|
|
59fb612315 | ||
|
|
59b21c7a3e | ||
|
|
40f881c2ac | ||
|
|
1d069683ca | ||
|
|
de1d942b90 | ||
|
|
fc73971d63 | ||
|
|
eb238bf107 | ||
|
|
2412f8b1e2 | ||
|
|
0c87dee31c | ||
|
|
215146f663 | ||
|
|
9fe1069df0 | ||
|
|
d2c5b52bf1 | ||
|
|
12153a414d | ||
|
|
5ec1092a83 | ||
|
|
284169a2f2 | ||
|
|
838bfbb75f | ||
|
|
a448da77c9 | ||
|
|
268fb22d5f | ||
|
|
07cc4c853d | ||
|
|
c62d727cbe | ||
|
|
7ef89d5043 | ||
|
|
9ceba1d6e8 | ||
|
|
e2e453985f | ||
|
|
84dccd800f | ||
|
|
f9734d202b | ||
|
|
d3cb0f4672 | ||
|
|
c198bbb6fa | ||
|
|
c965a89509 | ||
|
|
47f9da2160 | ||
|
|
348a75adb4 | ||
|
|
332218a7f7 | ||
|
|
6d7a26d2b9 | ||
|
|
d19a748f0c | ||
|
|
9c83e70a28 | ||
|
|
abcabb736b | ||
|
|
0451aaeb5c | ||
|
|
880c45830c | ||
|
|
5fa16d2344 | ||
|
|
9e50b5dd83 | ||
|
|
29d8753574 | ||
|
|
f93e1e1695 | ||
|
|
0eaac8fd7a | ||
|
|
06c71154b9 | ||
|
|
0e8f314dd6 | ||
|
|
f53ec8968b | ||
|
|
919d118f21 | ||
|
|
216b759df1 | ||
|
|
01450db71e | ||
|
|
ed987e1610 | ||
|
|
2773591e1f | ||
|
|
a995276d1e | ||
|
|
ffb6a8fa3f | ||
|
|
0966efb7f2 | ||
|
|
4a0a708092 | ||
|
|
6bf3b6f3e0 | ||
|
|
8f197fe266 | ||
|
|
e1a3f680f2 | ||
|
|
e89cca7e90 | ||
|
|
5bb2767d62 | ||
|
|
95f029e0e7 | ||
|
|
fb21e4d585 | ||
|
|
633805cec9 | ||
|
|
b8801d7d2a | ||
|
|
a84fac1b6a | ||
|
|
a9de4ceb30 | ||
|
|
827b55d60c | ||
|
|
0e1fe76b46 | ||
|
|
097c9e8e12 | ||
|
|
266356cb20 | ||
|
|
6dba39a804 | ||
|
|
3ddafa7aca | ||
|
|
9d8e232684 | ||
|
|
bf83c269c4 | ||
|
|
54e47c98cc | ||
|
|
118f2594ea | ||
|
|
ab4fcd6ac4 | ||
|
|
ca6f345429 | ||
|
|
79b8e1b4e4 | ||
|
|
cafa1ffa14 | ||
|
|
ea10df8a92 | ||
|
|
85abc42100 | ||
|
|
4747eb4386 | ||
|
|
ad9cc900b8 | ||
|
|
6cd93a7bb0 | ||
|
|
3338a02afb | ||
|
|
31cfe82224 | ||
|
|
70a191336b | ||
|
|
030477454c | ||
|
|
2a078d1572 | ||
|
|
3c1f96bc69 | ||
|
|
7d30bdc148 | ||
|
|
5d42961761 | ||
|
|
f20d5cd8d3 | ||
|
|
f5111c0f41 | ||
|
|
f5473236d0 | ||
|
|
d3cb31f1a7 | ||
|
|
d5f5cdd27a | ||
|
|
656dc8fefc | ||
|
|
a505cd9567 | ||
|
|
f2a860b01a | ||
|
|
1a5b93de9c | ||
|
|
0f165b33a6 | ||
|
|
4f53555f09 | ||
|
|
175be44823 | ||
|
|
20a6428290 | ||
|
|
4b4bea5f3b | ||
|
|
c82f860334 | ||
|
|
b2a56c047c | ||
|
|
bc6afc3933 | ||
|
|
280e4b65c3 | ||
|
|
c6f22f4d75 | ||
|
|
4bed8eb86f | ||
|
|
c482f18572 | ||
|
|
d7668acd9b | ||
|
|
05b978c568 | ||
|
|
62e5ab6990 | ||
|
|
12216f1463 | ||
|
|
cbfa13a8b4 | ||
|
|
03ec0cab1e | ||
|
|
d7940292d0 | ||
|
|
9139c5e9d6 | ||
|
|
65e62018e6 | ||
|
|
138a3673ce | ||
|
|
096f443b56 | ||
|
|
b37f30393d | ||
|
|
f095a05c42 | ||
|
|
3d15a73f1b | ||
|
|
bbd571e311 | ||
|
|
a7c554f033 | ||
|
|
25b4532ce3 | ||
|
|
a304f50a6b | ||
|
|
e75f476965 | ||
|
|
1c31460d27 | ||
|
|
19db468bf0 | ||
|
|
5fe05578c4 | ||
|
|
956f5a56cf | ||
|
|
a3f589d740 | ||
|
|
beef09bb6d | ||
|
|
ff0a246c28 | ||
|
|
f1459a1a52 | ||
|
|
f3501acb64 | ||
|
|
2238c98e95 | ||
|
|
9658d43f1f | ||
|
|
1748a0c3e5 | ||
|
|
4463d81758 | ||
|
|
74221a4ab5 | ||
|
|
0d998ed342 | ||
|
|
7d5a01756e | ||
|
|
d66313406b | ||
|
|
d96a267191 | ||
|
|
5467583bb3 | ||
|
|
9a8138d07b | ||
|
|
36c290ffea | ||
|
|
3413bf9f64 | ||
|
|
3c352a3545 | ||
|
|
56e4847b6b | ||
|
|
033b671d0b | ||
|
|
51f013851d | ||
|
|
dafa4ced27 | ||
|
|
05fe154749 | ||
|
|
5ddceb4660 | ||
|
|
341a1b195c | ||
|
|
29c3d1f9a6 | ||
|
|
734d4fb92b | ||
|
|
057a1cbab6 | ||
|
|
d44509b2cd | ||
|
|
58cf69795a | ||
|
|
6d39512576 | ||
|
|
ec4dde86f5 | ||
|
|
1c91fb9dd5 | ||
|
|
cbd650c5ef | ||
|
|
c5a769cb29 | ||
|
|
00a7277377 | ||
|
|
b8c32d41f5 | ||
|
|
49c9fc79c7 | ||
|
|
1284a7708e | ||
|
|
0dd8d15a9a | ||
|
|
90f59e954a | ||
|
|
03d7ec55a7 | ||
|
|
1929b69145 | ||
|
|
fbf194e4be | ||
|
|
a20927343a | ||
|
|
3b465dc09e | ||
|
|
fb8ca00ad1 | ||
|
|
dd7dddaa2b | ||
|
|
f41903c2a1 | ||
|
|
9984b5882d | ||
|
|
9ff20bee5a | ||
|
|
53caa11bc4 | ||
|
|
f6ac08567c | ||
|
|
040c6375c0 | ||
|
|
a03266aaad | ||
|
|
3479064348 | ||
|
|
b02d823b30 | ||
|
|
a204b6fb3f | ||
|
|
c2450843a5 | ||
|
|
00beb6170e | ||
|
|
9f1a300d2a | ||
|
|
05aefa1d5c | ||
|
|
059843f030 | ||
|
|
e202dc9851 | ||
|
|
18ae664ba7 | ||
|
|
76b563fa88 | ||
|
|
2553f4c161 | ||
|
|
f35c865348 | ||
|
|
b873ba3a75 | ||
|
|
d49e388ea3 | ||
|
|
b931699175 | ||
|
|
55fd58efd8 | ||
|
|
773847e139 | ||
|
|
3a52944f21 | ||
|
|
cc9d741275 | ||
|
|
f0096cf0e2 | ||
|
|
1d673bf6ff | ||
|
|
d986f00b6a | ||
|
|
01c3ca4f37 | ||
|
|
497bd7dad5 | ||
|
|
1d6a0ae8f1 | ||
|
|
c5e6b5ec7a | ||
|
|
ca26b4b30d | ||
|
|
254558e9de | ||
|
|
da0cd0b99c | ||
|
|
2e49c685cc | ||
|
|
a64af4da7c | ||
|
|
68bb2fa7f0 | ||
|
|
8bc2710380 | ||
|
|
1691e7ad83 | ||
|
|
6c2cb31923 | ||
|
|
0c6d920682 | ||
|
|
a126b5b61b | ||
|
|
dadb16bb04 | ||
|
|
f29473ef4c | ||
|
|
84b3162bcd | ||
|
|
c7f1469e1f | ||
|
|
d1dfd93e15 | ||
|
|
4ef55b8d1f | ||
|
|
7da22094f3 | ||
|
|
cf45cb56ad | ||
|
|
df96898543 | ||
|
|
a58bf66dea | ||
|
|
0f1fc0cc79 | ||
|
|
dc41f60f52 | ||
|
|
3d21afb640 | ||
|
|
79c3667fd4 | ||
|
|
ab1549f60e | ||
|
|
5d32fa36ff | ||
|
|
8ac17ab6e3 | ||
|
|
2076141f47 | ||
|
|
6d0f479f81 | ||
|
|
f56a5a3de1 | ||
|
|
d0c34fd760 | ||
|
|
9e7afd67bc | ||
|
|
964810858b | ||
|
|
7a51361099 | ||
|
|
ec2e71a22f | ||
|
|
5b188f35b5 | ||
|
|
5683571577 | ||
|
|
db75568905 | ||
|
|
5517305973 | ||
|
|
57ef531be0 | ||
|
|
b590e29608 | ||
|
|
569d575a96 | ||
|
|
dd8bf3776e | ||
|
|
d4ea9c8892 | ||
|
|
793c6b4a5a | ||
|
|
917c9dabc7 | ||
|
|
1d1bf504de | ||
|
|
d0c07e1e97 | ||
|
|
dfff520259 | ||
|
|
bb928bbd73 | ||
|
|
f86ec98e05 | ||
|
|
48af5c7ed6 | ||
|
|
cfaf336597 | ||
|
|
b52345236d | ||
|
|
87ebaf62c1 | ||
|
|
c7721d6100 | ||
|
|
40a722a7ff | ||
|
|
d41fbb9216 | ||
|
|
8bee0925d0 | ||
|
|
b8edca53cb | ||
|
|
34a13dd293 | ||
|
|
f72e582a80 | ||
|
|
6da2865781 | ||
|
|
a0ea12cf6c | ||
|
|
317bfde574 | ||
|
|
5f53ebdf12 | ||
|
|
cb835b7b6a | ||
|
|
bf76787e49 | ||
|
|
15a4f7e273 | ||
|
|
dc3e5ffa4b | ||
|
|
b84c7cc2bb | ||
|
|
049717260d | ||
|
|
a50a96de82 | ||
|
|
8ff8c0d176 | ||
|
|
a29b63c7d1 | ||
|
|
a8400c77fb | ||
|
|
e1c40bd218 | ||
|
|
757224683f | ||
|
|
95d982f3f3 | ||
|
|
7bfdfe5e41 | ||
|
|
8888b1a89a | ||
|
|
c6ba48be10 | ||
|
|
f132c4b5d1 | ||
|
|
87f5a8f6f2 | ||
|
|
de500af30d | ||
|
|
8b5607ac89 | ||
|
|
22727f68c1 | ||
|
|
ba64f8e5b5 | ||
|
|
b3bde5857e | ||
|
|
6e36a21d18 | ||
|
|
968ebeb5a3 | ||
|
|
47e11652fb | ||
|
|
84019ed4e7 | ||
|
|
37befd89e7 | ||
|
|
aa4f1b834a | ||
|
|
e6f8fd9234 | ||
|
|
86904892f2 | ||
|
|
d176dd6533 | ||
|
|
283efe0eac | ||
|
|
0e361cb105 | ||
|
|
53aeb085ac | ||
|
|
cd8c618f08 | ||
|
|
18b74d9797 | ||
|
|
4008934bbb | ||
|
|
8ae432554e | ||
|
|
337b26176a | ||
|
|
2e643fce28 | ||
|
|
5edd271975 | ||
|
|
c219ea06bf | ||
|
|
ffacc0d8d0 | ||
|
|
70fff77a28 | ||
|
|
bcc52d586e | ||
|
|
521ded5079 | ||
|
|
73b6b59ec9 | ||
|
|
157c81b0e9 | ||
|
|
233096354c | ||
|
|
01ac23162f | ||
|
|
4e3628c6fb | ||
|
|
d6bea8aed8 | ||
|
|
a254097092 | ||
|
|
b2a3d224a5 | ||
|
|
b495c2b60b | ||
|
|
452f76cbef | ||
|
|
3a0690bfee | ||
|
|
29fd2ff5e9 | ||
|
|
a344b3b76d | ||
|
|
14cf955cb9 | ||
|
|
31193d5b40 | ||
|
|
d6dc63c491 | ||
|
|
263f693542 | ||
|
|
3f42199f8f | ||
|
|
251ccd2e38 | ||
|
|
82ccf5886e | ||
|
|
6acb1e3853 | ||
|
|
8c0238e98f | ||
|
|
e7779c3d55 | ||
|
|
bdb0ca836c | ||
|
|
53038a0372 | ||
|
|
1b0eb91d58 | ||
|
|
5814ba38ac | ||
|
|
b2ec0d288b | ||
|
|
5171378bea | ||
|
|
7f570c074b | ||
|
|
dac675143f | ||
|
|
72a5f0e220 | ||
|
|
375aaa8430 | ||
|
|
4c704a8a3a | ||
|
|
78c0f2c7e9 | ||
|
|
c262dd06e6 | ||
|
|
e0d6b501c7 | ||
|
|
efc3f45ef6 | ||
|
|
24d8ef25bb | ||
|
|
2aca775907 | ||
|
|
7aa10ef4be | ||
|
|
17ad622ce3 | ||
|
|
cc7431a092 | ||
|
|
4199d02d98 | ||
|
|
8c434760fb | ||
|
|
5f63b99dc8 | ||
|
|
edd0ae4c59 | ||
|
|
3944e6450d | ||
|
|
a8e5ad42ba | ||
|
|
d3bfb0b87b | ||
|
|
75e3e36aa8 | ||
|
|
9102b4aa1b | ||
|
|
e744d90dbb | ||
|
|
c38b957d7c | ||
|
|
282bb26da9 | ||
|
|
6b1c30157f | ||
|
|
e433251420 | ||
|
|
49ed9c7f7f | ||
|
|
5a5c0326b7 | ||
|
|
a25708be2b | ||
|
|
e8f2934534 | ||
|
|
37f8ac9da9 | ||
|
|
0ded95ce48 | ||
|
|
108e769833 | ||
|
|
5b2313ee56 | ||
|
|
368b84b7ff | ||
|
|
864946477b | ||
|
|
da67298b43 | ||
|
|
db5cb8b3a9 | ||
|
|
9643292be6 | ||
|
|
a651e34206 | ||
|
|
a4e7fd3209 | ||
|
|
d1113d40aa | ||
|
|
dcd834d3e4 | ||
|
|
c0be8a2c04 | ||
|
|
09182172cf | ||
|
|
56e903e359 | ||
|
|
9922d60e5b | ||
|
|
09ea42439e | ||
|
|
ce1acf1adc | ||
|
|
fe00badb0f | ||
|
|
2146d67dc2 | ||
|
|
6728768b3e | ||
|
|
48db3de08c | ||
|
|
b944364d1e | ||
|
|
39c2fbe8c3 | ||
|
|
c7ba640ecb | ||
|
|
f749f6be72 | ||
|
|
ccdd384c6e | ||
|
|
4061e2c149 | ||
|
|
e7b8461555 | ||
|
|
70d1537ecc | ||
|
|
cb37f85d8e | ||
|
|
9becf565a4 | ||
|
|
b1a4e5467d | ||
|
|
4bbe8488fc | ||
|
|
54a0d126b5 | ||
|
|
9b1fbf0fbf | ||
|
|
6f626974ac | ||
|
|
5c47beb1c4 | ||
|
|
b4fbe8df07 | ||
|
|
3cc9fd2782 | ||
|
|
eaecba7ec8 | ||
|
|
42a43be092 | ||
|
|
052aafd7cb | ||
|
|
4abae578f4 | ||
|
|
4132d96591 | ||
|
|
8e4c90129e | ||
|
|
31406927e6 | ||
|
|
303646efd3 | ||
|
|
9efc4f9113 | ||
|
|
31a5a42ec7 | ||
|
|
2d0ed3ec8a | ||
|
|
de288a008d | ||
|
|
3c5d73224a | ||
|
|
05f9c07836 | ||
|
|
a7ba6add39 | ||
|
|
479973bf06 | ||
|
|
854c9fe794 | ||
|
|
5a17c75fe4 | ||
|
|
4dc5eff252 | ||
|
|
7fe0d78154 | ||
|
|
2c709dc205 | ||
|
|
9353349a39 | ||
|
|
d3049b2bfa | ||
|
|
61cb2529bd | ||
|
|
e6c6e4395f | ||
|
|
959c955616 | ||
|
|
538253cdc1 | ||
|
|
b4c6594333 | ||
|
|
a7f5f8889c | ||
|
|
1c9b4cf552 | ||
|
|
ce09f487bd | ||
|
|
a5d1decf40 | ||
|
|
7024c7d598 | ||
|
|
8109253eeb | ||
|
|
b61f1e3803 | ||
|
|
db40f80be7 | ||
|
|
26eaf97032 | ||
|
|
da349374bf | ||
|
|
0ffa925fee | ||
|
|
082787c4cf | ||
|
|
be9b5332d9 | ||
|
|
97ae3ba7d3 | ||
|
|
d047f401c2 | ||
|
|
1e9e78223b | ||
|
|
6d5baebd08 | ||
|
|
4e758dbb85 | ||
|
|
40d943c620 | ||
|
|
e69b6c4dc8 | ||
|
|
23444f7083 | ||
|
|
8c077b96df | ||
|
|
4b1a055a88 | ||
|
|
b4ddcc1dec | ||
|
|
271d2e3abc | ||
|
|
37b6399398 | ||
|
|
ebf19b1506 | ||
|
|
e4dd773644 | ||
|
|
f9b3a1f293 | ||
|
|
7c9850ada8 | ||
|
|
9ef05b8afe | ||
|
|
efdd196441 | ||
|
|
6e780a3876 | ||
|
|
b475b265ae | ||
|
|
3bb7d2c294 | ||
|
|
594a148a39 | ||
|
|
779591db36 | ||
|
|
c002eeffb7 | ||
|
|
1dac973d70 | ||
|
|
f5024f0e75 | ||
|
|
cf320c08c5 | ||
|
|
8973c9550c | ||
|
|
bb671f0e93 | ||
|
|
a8774b5011 | ||
|
|
f092cd41bc | ||
|
|
b17ec9731a | ||
|
|
021810201b | ||
|
|
6038dc9c8a | ||
|
|
4df8c9610a | ||
|
|
6c12dd4f16 | ||
|
|
ad3b8fa59f | ||
|
|
cb52a8b51b | ||
|
|
22ba1302d2 | ||
|
|
7d04559921 | ||
|
|
e40e35d30c | ||
|
|
d1af9f236c | ||
|
|
45a0ff26c5 | ||
|
|
1fd330d7a4 | ||
|
|
09833f31cf | ||
|
|
20e7a036cf | ||
|
|
e6667c1782 | ||
|
|
657935eba5 | ||
|
|
67b905a757 | ||
|
|
55cede0434 | ||
|
|
c7677d6d1e | ||
|
|
d191ca54ad | ||
|
|
20f4c952fe | ||
|
|
0bd09896f3 | ||
|
|
60ecfbfb8e | ||
|
|
8921d78610 | ||
|
|
b243ff94e9 | ||
|
|
5f1c1278e3 | ||
|
|
fa56e594b1 | ||
|
|
c9b64927be | ||
|
|
3689cb2a99 | ||
|
|
3bb7541361 | ||
|
|
7b15aa5f83 | ||
|
|
690d3036db | ||
|
|
416e8d02a1 | ||
|
|
a968c2d2b7 | ||
|
|
b4787bf444 | ||
|
|
a4d90e8aff | ||
|
|
32d0606ee4 | ||
|
|
4541f7c758 | ||
|
|
65428d629c | ||
|
|
bdfd9cc617 | ||
|
|
6d324921a0 | ||
|
|
dcf0f5c5a3 | ||
|
|
d98f851a2c | ||
|
|
a95b102396 | ||
|
|
7e2fbbaae6 | ||
|
|
070e8b0b54 | ||
|
|
7b49a1296c | ||
|
|
1e278bde92 | ||
|
|
078f402819 | ||
|
|
52af565f77 | ||
|
|
853905e52f | ||
|
|
2e0e1d2aac | ||
|
|
7f33a62bb5 | ||
|
|
bdb59ea429 | ||
|
|
1c0ffe39f7 | ||
|
|
2fbfc97cca | ||
|
|
482299e765 | ||
|
|
54f4734847 | ||
|
|
0fb6cef577 | ||
|
|
7eec264961 | ||
|
|
aff874c68a | ||
|
|
27abee0850 | ||
|
|
bcfb19f7c5 | ||
|
|
306a8ce0df | ||
|
|
d9ea8d2c9c | ||
|
|
b479956bb2 | ||
|
|
b32dc0e450 | ||
|
|
5cca5d69af | ||
|
|
e0e89213d3 | ||
|
|
e246c19eb3 | ||
|
|
d282d8dd52 | ||
|
|
9601ad13ee | ||
|
|
b7603e109d | ||
|
|
066f54906b | ||
|
|
ea0aa9df70 | ||
|
|
0811da9014 | ||
|
|
d601290c46 | ||
|
|
64357aff55 | ||
|
|
a20a3311b5 | ||
|
|
ffce5d4bb5 | ||
|
|
cbfadc019a | ||
|
|
bf5427f3e8 | ||
|
|
4c27562650 | ||
|
|
e8d20532ba | ||
|
|
d928157569 | ||
|
|
872b05a7de | ||
|
|
6ea71ec6a2 | ||
|
|
139cb72209 | ||
|
|
855a15e696 | ||
|
|
eeebd3fc1b | ||
|
|
a4b209c654 | ||
|
|
43aad3d117 | ||
|
|
f2d4fdd4d2 | ||
|
|
a630106d80 | ||
|
|
c7acd455c5 | ||
|
|
555a9d4883 | ||
|
|
ec4ce0c70c | ||
|
|
edf275badc | ||
|
|
2e91285f02 | ||
|
|
ec69ba7e0e | ||
|
|
3804ca18cb | ||
|
|
9ea3222da8 | ||
|
|
df48524ca5 | ||
|
|
b3aff1162c | ||
|
|
891ca8a31b | ||
|
|
ba99ac8b17 | ||
|
|
1ff25943dc | ||
|
|
deb58e40d5 | ||
|
|
eab6eb8fab | ||
|
|
ff65367851 | ||
|
|
f16e29c63e | ||
|
|
cdfeb094b3 | ||
|
|
b63c5d2987 | ||
|
|
015309c882 | ||
|
|
20377e9c56 | ||
|
|
08857a6198 | ||
|
|
d9ce1b3a97 | ||
|
|
d166073b16 | ||
|
|
f858c196f4 | ||
|
|
57612eeced | ||
|
|
be2257153c | ||
|
|
d920a97f4f | ||
|
|
322f2a1728 | ||
|
|
cfe6b0d9ab | ||
|
|
e229deb238 | ||
|
|
8cdde947bc | ||
|
|
c1b3ddf87a | ||
|
|
27d97add1e | ||
|
|
3783724c40 | ||
|
|
67bc4ffe68 | ||
|
|
453bbfbbde | ||
|
|
ff463c4261 | ||
|
|
748b77ae7a | ||
|
|
58c1005657 | ||
|
|
9271eb61ac | ||
|
|
c82cee25a5 | ||
|
|
2e5dfa5845 | ||
|
|
693c07b927 | ||
|
|
71a6f70f46 | ||
|
|
2952b5a7ec | ||
|
|
baa5847949 | ||
|
|
b9ce0bd99d | ||
|
|
aac61d8120 | ||
|
|
1f6edfdbcc | ||
|
|
9d1ce7fadf | ||
|
|
fd560c351f | ||
|
|
b45556062d | ||
|
|
5be45599ed | ||
|
|
9b2533dbc9 | ||
|
|
ec1a4b1974 | ||
|
|
bb9fde17c9 | ||
|
|
8cb524080c | ||
|
|
171ec54781 | ||
|
|
5d9503b78c | ||
|
|
f56cb69c2e | ||
|
|
4eb9aa9ccb | ||
|
|
11801f306c | ||
|
|
95c2944f30 | ||
|
|
5bd4c54ab6 | ||
|
|
95d6d0a0fe | ||
|
|
7941be083a | ||
|
|
e36efaec08 | ||
|
|
637afdb540 | ||
|
|
dafdedef9a | ||
|
|
ce17ee2ae6 | ||
|
|
e74daa97d2 | ||
|
|
44d64d1b80 | ||
|
|
1a4731aa83 | ||
|
|
a75e1c52b7 | ||
|
|
1b97cb263c | ||
|
|
5c9a47b6b7 | ||
|
|
8a5fe86193 | ||
|
|
d9531e24a3 | ||
|
|
624f328269 | ||
|
|
a6f4e6771d | ||
|
|
a506c21b80 | ||
|
|
981193ed23 | ||
|
|
85a6204db2 | ||
|
|
b82aba1181 | ||
|
|
0a6dea2c79 | ||
|
|
69b6d75927 | ||
|
|
eff2d48cc5 | ||
|
|
ca5af2505c | ||
|
|
a958fe86d7 | ||
|
|
3ed488e10f | ||
|
|
dcc11f16b1 | ||
|
|
209706b70d | ||
|
|
1bc80eb485 | ||
|
|
9ab9e3fe46 | ||
|
|
d654c096ed | ||
|
|
f5d5884988 | ||
|
|
2c016204bf | ||
|
|
04fd625bde | ||
|
|
8455d4a49f | ||
|
|
a3960bb7c5 | ||
|
|
769262d60e | ||
|
|
942567586f | ||
|
|
ba6baaec0a | ||
|
|
a8ac6fc738 | ||
|
|
b027d3b1d6 | ||
|
|
71f9d268c9 | ||
|
|
2b91d4af99 | ||
|
|
0ec0e286ba | ||
|
|
258ae64568 | ||
|
|
90cafa126f | ||
|
|
43d31e285c | ||
|
|
57945e6751 | ||
|
|
fce56cbf4c | ||
|
|
7a13771198 | ||
|
|
819c798e99 | ||
|
|
8560ca0661 | ||
|
|
82cdfe7014 | ||
|
|
52642f5854 | ||
|
|
6c6f9f5a44 | ||
|
|
039ce15253 | ||
|
|
695a4c785c | ||
|
|
7d7f160159 | ||
|
|
b454b4dff1 | ||
|
|
e5d711dd28 | ||
|
|
10b127ca55 | ||
|
|
fb4dff4fca | ||
|
|
ef25b364ec | ||
|
|
9394db986d | ||
|
|
9226c6cac1 | ||
|
|
283193e992 | ||
|
|
72f8a6d220 | ||
|
|
f5e4fb49c3 | ||
|
|
3cd15c783c | ||
|
|
bf51ba860a | ||
|
|
6aa8515df4 | ||
|
|
3bf4ee35a1 | ||
|
|
e08c600740 | ||
|
|
f823690b44 | ||
|
|
350b0c1e3c | ||
|
|
b01a6124a9 | ||
|
|
b00652f9eb | ||
|
|
19159a203a | ||
|
|
be8c77af5a | ||
|
|
8bb7803d23 | ||
|
|
54a85a8dd0 | ||
|
|
6fd40c0a7c | ||
|
|
97dd423486 | ||
|
|
281d60df4f | ||
|
|
43933f4089 | ||
|
|
4f7e140737 | ||
|
|
2b6945a382 | ||
|
|
8a3ae59f77 | ||
|
|
db253875cc | ||
|
|
a8359dcb75 | ||
|
|
e5dac06d91 | ||
|
|
e9f82558ed | ||
|
|
26f5ef5e31 | ||
|
|
874e889b36 | ||
|
|
bece5f7083 | ||
|
|
2f535e6db1 | ||
|
|
61c3057060 | ||
|
|
063d7d5cc4 | ||
|
|
0e0211050b | ||
|
|
c8c7245da1 | ||
|
|
3e27e50bab | ||
|
|
6b9d3ed60e | ||
|
|
11a78111de | ||
|
|
2655421171 | ||
|
|
c6bc2ea485 | ||
|
|
289b7a3dbe | ||
|
|
70083c6dca | ||
|
|
3e25b92369 | ||
|
|
806eaaf14b | ||
|
|
fb3f2d46fa | ||
|
|
14d06fe754 | ||
|
|
752146028b | ||
|
|
6c6ae30ce5 | ||
|
|
b00750bfa3 | ||
|
|
55eac005a0 | ||
|
|
257524de18 | ||
|
|
d4f78056dd | ||
|
|
66c054f24b | ||
|
|
711b722118 | ||
|
|
26614b5f40 | ||
|
|
9240211f3e | ||
|
|
67d84d956e | ||
|
|
97b620f98f | ||
|
|
2f5c91a1e1 | ||
|
|
038dad834d | ||
|
|
b3cd265955 | ||
|
|
2c670bc838 | ||
|
|
30c2b8e192 | ||
|
|
a00d45522b | ||
|
|
525369e0ce | ||
|
|
ba413f3e8f | ||
|
|
4afebca77b | ||
|
|
d2eb92143d | ||
|
|
e01d3c64fe | ||
|
|
9f497c9c2c | ||
|
|
9aae154c4e | ||
|
|
339f012794 | ||
|
|
af500d7b7b | ||
|
|
16a71b3917 | ||
|
|
7dfa104f65 | ||
|
|
44a7b1761f | ||
|
|
22c8ea255c | ||
|
|
a1c10828d8 | ||
|
|
25d69d1bd7 | ||
|
|
a84961f8ba | ||
|
|
e17b6790b5 | ||
|
|
815aed52d3 | ||
|
|
a03581ccd3 | ||
|
|
c10f6e6c6a | ||
|
|
18abd0384f | ||
|
|
4292bdd7b4 | ||
|
|
1149648399 | ||
|
|
b6846eb21d | ||
|
|
d19546fcb4 | ||
|
|
6a1eb198d1 | ||
|
|
e4757d4345 | ||
|
|
3873a59a37 | ||
|
|
cf9f6c10d7 | ||
|
|
8bcd9debc2 | ||
|
|
510a159eee | ||
|
|
062fb3ba30 | ||
|
|
3bc477d21b | ||
|
|
79eb2feb2c | ||
|
|
1fa42a5753 | ||
|
|
2eaab408dd | ||
|
|
f7fd0d9121 | ||
|
|
3b7b776ac4 | ||
|
|
43abc8440b | ||
|
|
37515b5da9 | ||
|
|
2dec327013 | ||
|
|
8f4dae3134 | ||
|
|
a584daa92d | ||
|
|
43431aa9a0 | ||
|
|
f196d2abec | ||
|
|
4a6724f664 | ||
|
|
a960737207 | ||
|
|
da08bd7fff | ||
|
|
517430f23d | ||
|
|
48e82ac15b | ||
|
|
eead64ff71 | ||
|
|
9ac6db2f4c | ||
|
|
92cf6bb887 | ||
|
|
1d3978ce2f | ||
|
|
16c71da487 | ||
|
|
214dbafd62 | ||
|
|
89b162704c | ||
|
|
fbf906d97c | ||
|
|
7961ff0785 | ||
|
|
00e53f455b | ||
|
|
d1d4839a09 | ||
|
|
31b19725b7 | ||
|
|
a776eaf61a | ||
|
|
ae2a92d229 | ||
|
|
dedc4aa8b9 | ||
|
|
7a8ca2f068 | ||
|
|
fdf52a3d59 | ||
|
|
e0987059d3 | ||
|
|
ee7217c7c9 | ||
|
|
1027659f34 | ||
|
|
424a212cc3 | ||
|
|
949ddbdcd7 | ||
|
|
7fcfc306f9 | ||
|
|
a691e033eb | ||
|
|
b76f62d470 | ||
|
|
01a90a1694 | ||
|
|
97bcc7afb6 | ||
|
|
9fa0ec440d | ||
|
|
28559cde02 | ||
|
|
6970d48cc3 | ||
|
|
52801c5afc | ||
|
|
7797bce814 | ||
|
|
18762dc624 | ||
|
|
5a828a6465 | ||
|
|
eaa9f36478 | ||
|
|
2b63134bcf | ||
|
|
8dcff63aea | ||
|
|
c2777607be | ||
|
|
9ba2b18fdb | ||
|
|
4ebc10db6a | ||
|
|
610b6c7bb0 | ||
|
|
357333c4e4 | ||
|
|
723334a685 | ||
|
|
b2c218ff83 | ||
|
|
adabd6966d | ||
|
|
b3eb1270dd | ||
|
|
7659a195d3 | ||
|
|
8d2e23f4a8 | ||
|
|
539d7dab5d | ||
|
|
06d43cdb24 | ||
|
|
af7bcf19ab | ||
|
|
7ebeb37881 | ||
|
|
4911bbe3a2 | ||
|
|
e0b6ab3f8a | ||
|
|
8736c2cf9a | ||
|
|
d825c33b55 | ||
|
|
171ecaaf62 | ||
|
|
5e6d5d4eb0 | ||
|
|
3733a3c335 | ||
|
|
7fca6defd6 | ||
|
|
2a270b399e | ||
|
|
64109aee05 | ||
|
|
e1d9395128 | ||
|
|
32eec95c26 | ||
|
|
f41cca45aa | ||
|
|
48eeab974c | ||
|
|
eed44156ae | ||
|
|
1177d9bdd8 | ||
|
|
d151a94285 | ||
|
|
a7fe6453ee | ||
|
|
313eb136f4 | ||
|
|
98591ff83d | ||
|
|
0b9d78560b | ||
|
|
32a930e598 | ||
|
|
edd8512196 | ||
|
|
7a6aec34ae | ||
|
|
009a0c5703 | ||
|
|
a99086b6bd | ||
|
|
a186672447 | ||
|
|
0b8a7c0d09 | ||
|
|
1990bf3d7a | ||
|
|
ea74a7e401 | ||
|
|
bf12c3ff74 | ||
|
|
9d261aae76 | ||
|
|
3d8c8fd745 | ||
|
|
6ad7db522a | ||
|
|
385984b1d8 | ||
|
|
4f3d4b06b5 | ||
|
|
2291986e2c | ||
|
|
fc81cf4d70 | ||
|
|
fdeab86a87 | ||
|
|
3616b7a67b | ||
|
|
83ea57d825 | ||
|
|
24a69bcade | ||
|
|
58dc3244be | ||
|
|
61e580b992 | ||
|
|
1116530a6b | ||
|
|
8cfaabedeb | ||
|
|
66ba05dcd0 | ||
|
|
d1db616d1e | ||
|
|
aed09b152a | ||
|
|
f755365e23 | ||
|
|
ccd34c1610 | ||
|
|
f9104e6cc9 | ||
|
|
4bb702fe89 | ||
|
|
511a04dad5 | ||
|
|
f3527a44d7 | ||
|
|
fdbe84cb1e | ||
|
|
45fe70f0fa | ||
|
|
2aed2fd534 | ||
|
|
a523fa9733 | ||
|
|
0f42f032e4 | ||
|
|
4575b98fd5 | ||
|
|
3a0cc0d6f6 | ||
|
|
626e2fcb12 | ||
|
|
592feb54b7 | ||
|
|
9c6b63e7e4 | ||
|
|
4364a74b7a | ||
|
|
00f13102f8 | ||
|
|
3f17389871 | ||
|
|
726ba287b1 | ||
|
|
42ee29cb3c | ||
|
|
8a98b6b012 | ||
|
|
14ab694804 | ||
|
|
14b8cda543 | ||
|
|
4264e34ffd | ||
|
|
bd9bf55e43 | ||
|
|
7c802bbd33 | ||
|
|
9e37f3f586 | ||
|
|
1d4f5d068a | ||
|
|
5be5eb80e8 | ||
|
|
12c774a34a | ||
|
|
14c3fa4378 | ||
|
|
2f17420721 | ||
|
|
8d7f8d156f | ||
|
|
38248d8c35 | ||
|
|
edaae02892 | ||
|
|
846eff4984 | ||
|
|
481adf3a1e | ||
|
|
d622f7a65c | ||
|
|
a479501aef | ||
|
|
2456374e5a | ||
|
|
c77016ea44 | ||
|
|
6fd45a37e2 | ||
|
|
9be56d3ab8 | ||
|
|
24b264b6c9 | ||
|
|
7f9130470b | ||
|
|
b82aa1daa5 | ||
|
|
53cb325974 | ||
|
|
1256c320e3 | ||
|
|
15bc30a2d5 | ||
|
|
fc3bc8468f | ||
|
|
b4e068f630 | ||
|
|
08eef80673 | ||
|
|
152f73ebf0 | ||
|
|
38de5048bc | ||
|
|
c4d96fbc49 | ||
|
|
ff25d402c1 | ||
|
|
f957024605 | ||
|
|
006e54e2fd | ||
|
|
5f7bc58788 | ||
|
|
bdd93603aa | ||
|
|
8392a17cb2 | ||
|
|
5f7f0b777e | ||
|
|
3f574606d9 | ||
|
|
45f0f93895 | ||
|
|
af2710135b | ||
|
|
95ed6094fe | ||
|
|
6af8ce9eeb | ||
|
|
3ff37f00fe | ||
|
|
ed5b066cbe | ||
|
|
cec5593c70 | ||
|
|
04924884ad | ||
|
|
3ccf64fcd3 | ||
|
|
8eb7f9b91c | ||
|
|
f25c50c629 | ||
|
|
e524a1b865 | ||
|
|
ac15e3355e | ||
|
|
0930a37819 | ||
|
|
d62f91a9e6 | ||
|
|
2789ead999 | ||
|
|
f25fd267dd | ||
|
|
47999f1f72 | ||
|
|
095bbcd15c | ||
|
|
9177bb8451 | ||
|
|
119bf9b0ff | ||
|
|
015c6037c4 | ||
|
|
452a7e7445 | ||
|
|
407586e2d5 | ||
|
|
ffa431a3cd | ||
|
|
281a5ff991 | ||
|
|
92db9bd284 | ||
|
|
ea8f319f45 | ||
|
|
a11e9fe04e | ||
|
|
27367bd1fc | ||
|
|
c6f48ae054 | ||
|
|
7d6efe3694 | ||
|
|
f4aad05edc | ||
|
|
d8f7637ca0 | ||
|
|
f9a7bd199e | ||
|
|
68b7ed284a | ||
|
|
e782895cf5 | ||
|
|
a5935b40d5 | ||
|
|
035d2cb440 | ||
|
|
2a74a49995 | ||
|
|
902953a1fa | ||
|
|
1ffef91b7a | ||
|
|
3d13d9b0dc | ||
|
|
adcc5d5692 | ||
|
|
c49d70170e | ||
|
|
349a78a5bd | ||
|
|
48734c6896 | ||
|
|
0f60a3b24d | ||
|
|
d3a88011a6 | ||
|
|
9b6e4c605b | ||
|
|
7c91524111 | ||
|
|
e1573069e4 | ||
|
|
f2459c964b | ||
|
|
43aa0b815d | ||
|
|
0740630e05 | ||
|
|
c9244b2b13 | ||
|
|
0d398f867f | ||
|
|
b74ec2d7d3 | ||
|
|
26a295c8ed | ||
|
|
2a71d3d20c | ||
|
|
b79605b692 | ||
|
|
ea0fc68a53 | ||
|
|
1ca5c32de3 | ||
|
|
f51bcfa05a | ||
|
|
e1bf68ab38 | ||
|
|
99e03b7ce5 | ||
|
|
cd70d3ea46 | ||
|
|
d387227cef | ||
|
|
2f4530e426 | ||
|
|
4db181d8bf | ||
|
|
9a7a1cc752 | ||
|
|
59ca6c6708 | ||
|
|
fe7901ca7f | ||
|
|
9351b4a5bb | ||
|
|
dfdd0a0496 | ||
|
|
cda39ec256 | ||
|
|
3720a46ff3 | ||
|
|
7ea50ea41e | ||
|
|
60a696916b | ||
|
|
b6a255d96f | ||
|
|
44a0cce7f2 | ||
|
|
f580e0d26f | ||
|
|
6beefe86e2 | ||
|
|
cbada35788 | ||
|
|
44ff2f872d | ||
|
|
2198853662 | ||
|
|
4636109081 | ||
|
|
1c042778b6 | ||
|
|
34b5962eac | ||
|
|
fc7af59eb7 | ||
|
|
7e557ca059 | ||
|
|
1d0cea8ad0 | ||
|
|
5c901d7c1e | ||
|
|
1dffab0bb8 | ||
|
|
ae89e14ea2 | ||
|
|
908255060c | ||
|
|
88278d0041 | ||
|
|
86bfd91c9d | ||
|
|
0ee412ccb9 | ||
|
|
b8bd6ea820 | ||
|
|
98a1ab3033 | ||
|
|
e360f53a40 | ||
|
|
237ec38003 | ||
|
|
30ea1bbf87 | ||
|
|
0d0aef6014 | ||
|
|
1b7441715c | ||
|
|
e3223b6124 | ||
|
|
41fb06187b | ||
|
|
adf0e8ae3b | ||
|
|
42dd1efb41 | ||
|
|
b6a6694abf | ||
|
|
04f2f50d6d | ||
|
|
6d1048e5c5 | ||
|
|
fe722c8b31 | ||
|
|
0326ce1d85 | ||
|
|
183ddb68d3 | ||
|
|
d7fe1afc08 | ||
|
|
ae9aeaf5fd | ||
|
|
ec9476216f | ||
|
|
619f2ef119 | ||
|
|
52020abde8 | ||
|
|
1bd504d67e | ||
|
|
edc4414de4 | ||
|
|
c1d588264c | ||
|
|
94b84b75ad | ||
|
|
b72a4c5aa9 | ||
|
|
857a9f3efc | ||
|
|
ce53128657 | ||
|
|
d9211053ce | ||
|
|
e8316178a0 | ||
|
|
bf763d2cf4 | ||
|
|
eba5b34982 | ||
|
|
afb8b3dd6b | ||
|
|
c5fa94894b | ||
|
|
4137758caa | ||
|
|
3578d16e9e | ||
|
|
3ef263a5cc | ||
|
|
510460c966 | ||
|
|
f74ecc53ae | ||
|
|
c4121073ad | ||
|
|
9ded2641a7 | ||
|
|
295ca68d02 | ||
|
|
27f53f262b | ||
|
|
3fc16cb414 | ||
|
|
90db25d732 | ||
|
|
bbb359470e | ||
|
|
319652c7c7 | ||
|
|
c9c271fee8 | ||
|
|
ca0755e92b | ||
|
|
acd38597f6 | ||
|
|
f4a5a80f3c | ||
|
|
c45d00fee8 | ||
|
|
ffae59fa1c | ||
|
|
b697178f68 | ||
|
|
83ade5eecb | ||
|
|
6973b92c4a | ||
|
|
6261f8a778 | ||
|
|
6048493ac6 | ||
|
|
1cbd715235 | ||
|
|
703fcbccd6 | ||
|
|
2f9cbec07e | ||
|
|
9f0b22d3e9 | ||
|
|
ab5907c09c | ||
|
|
fae0b168f6 | ||
|
|
f18e98a63e | ||
|
|
3524886d5d | ||
|
|
fb44eea06c | ||
|
|
3ea4c757e6 | ||
|
|
cfb8d79049 | ||
|
|
1ea86da7af | ||
|
|
e289f2dba2 | ||
|
|
7f64cd1801 | ||
|
|
d4526e1ed2 | ||
|
|
34f42216c8 | ||
|
|
a26a24a8ad | ||
|
|
4530fd4164 | ||
|
|
9156b8f48c | ||
|
|
6212109fc1 | ||
|
|
c22a080e23 | ||
|
|
834a7109f9 | ||
|
|
7cbf32202d | ||
|
|
d0b9380dca | ||
|
|
13fd9be566 | ||
|
|
53a9aa6ad2 | ||
|
|
c2ce4aca1b | ||
|
|
567f6d7cc0 | ||
|
|
489c0b27f9 | ||
|
|
343f988584 | ||
|
|
7f676c56c8 | ||
|
|
3c0ca7026f | ||
|
|
2ceba11aa7 | ||
|
|
6db5e0b27c | ||
|
|
dfecb801db | ||
|
|
c10bfe3db2 | ||
|
|
20fb2c99bc | ||
|
|
b4a0b5c58b | ||
|
|
faa46c2a21 | ||
|
|
9691199ae8 | ||
|
|
f736381933 | ||
|
|
f44e5b3b7a | ||
|
|
6e24bf5f8c | ||
|
|
8fb43e31c5 | ||
|
|
0860c80e51 | ||
|
|
66fc25756f | ||
|
|
f008e240cd | ||
|
|
8d7e95d6e9 | ||
|
|
3e3ce543a8 | ||
|
|
6c447a82f1 | ||
|
|
64a0918ff1 | ||
|
|
9274223701 | ||
|
|
1368c18844 | ||
|
|
67a60a7557 | ||
|
|
3d5fd47748 | ||
|
|
b9a18807ae | ||
|
|
088c0b6321 | ||
|
|
ecee11a24c | ||
|
|
ec8df7ce57 | ||
|
|
4159fd2ffb | ||
|
|
1a1d21bbb3 | ||
|
|
be1045fed9 | ||
|
|
e43773c712 | ||
|
|
30d69dadbb | ||
|
|
b138438036 | ||
|
|
d649211330 | ||
|
|
6cf211a9ad | ||
|
|
3388e5e8a4 | ||
|
|
5d497a1908 | ||
|
|
c820646fb6 | ||
|
|
5870f6f734 | ||
|
|
6732150121 | ||
|
|
1dead8b080 | ||
|
|
d547aa8ebd | ||
|
|
1da889e420 | ||
|
|
5d0a308d1d | ||
|
|
f9886d52da | ||
|
|
4f8e48b7d4 | ||
|
|
258e07c2ca | ||
|
|
cc32c50665 | ||
|
|
ec1d91f73e | ||
|
|
eb2f429964 | ||
|
|
1ad067309d | ||
|
|
48ce7df43a | ||
|
|
6555e2c440 | ||
|
|
a05191e112 | ||
|
|
b8eeee1d5d | ||
|
|
4aa87f3fa5 | ||
|
|
40c37d923b | ||
|
|
5a5837b8ed | ||
|
|
1e0b521070 | ||
|
|
35ed58cc5e | ||
|
|
c4a1579197 | ||
|
|
e471706422 | ||
|
|
d78b7350b5 | ||
|
|
47b29d5a49 | ||
|
|
e5946a51d1 | ||
|
|
a88798cc22 | ||
|
|
6fbd32523a | ||
|
|
94b1cc2bdd | ||
|
|
0ed5c8f0ae | ||
|
|
5f883f552b | ||
|
|
9db99ab4a5 | ||
|
|
287214c2b2 | ||
|
|
317a020841 | ||
|
|
b50e3aec5f | ||
|
|
21a9e0e2a7 | ||
|
|
7775df8ef1 | ||
|
|
53f9b5d131 | ||
|
|
bf4d4a4742 | ||
|
|
0dbbe7104d | ||
|
|
561ef00680 | ||
|
|
85428fa72e | ||
|
|
29d0593b86 | ||
|
|
ac524dd799 | ||
|
|
aba8b764b6 | ||
|
|
a9050e0f41 | ||
|
|
15ef84e238 | ||
|
|
09096fef5b | ||
|
|
19b08e1019 | ||
|
|
06d67642dd | ||
|
|
ceb6c450c0 | ||
|
|
a745e42cf5 | ||
|
|
462d6a4450 | ||
|
|
a2e39c5e2e | ||
|
|
ec899be3b5 | ||
|
|
c79ebfdd0a | ||
|
|
cd95e6c552 | ||
|
|
f676145302 | ||
|
|
0847267069 | ||
|
|
6fe1da1587 | ||
|
|
7ab555d869 | ||
|
|
4a35e9e60d | ||
|
|
5e8dfdfd9b | ||
|
|
278a1b8ab3 | ||
|
|
6cb03dded1 | ||
|
|
4752ec1b67 | ||
|
|
e641371544 | ||
|
|
8d9a7e9af1 | ||
|
|
2b8e9bf887 | ||
|
|
a16c55c679 | ||
|
|
9a72c40149 | ||
|
|
8424fc4c19 | ||
|
|
9f29a047a7 | ||
|
|
af4904ce8d | ||
|
|
6f2a323063 | ||
|
|
1da8ecfaac | ||
|
|
1ba386bbd2 | ||
|
|
d00d791cda | ||
|
|
51aabd7b21 | ||
|
|
7840c3bdc8 | ||
|
|
07d13002b0 | ||
|
|
23abe2ba06 | ||
|
|
4d73821d21 | ||
|
|
f2687cf807 | ||
|
|
8abce0d4cf | ||
|
|
62ad3848c4 | ||
|
|
521e9969f1 | ||
|
|
5ee1ceced3 | ||
|
|
efffbafa42 | ||
|
|
899cec8814 | ||
|
|
55beb993e5 | ||
|
|
fba46a1e00 | ||
|
|
68c4d6f295 | ||
|
|
2ad07c018e | ||
|
|
c2418559f1 | ||
|
|
de0277eb3b | ||
|
|
b58802b495 | ||
|
|
898e20e8b2 | ||
|
|
75bf10d83d | ||
|
|
e8e58cc4a2 | ||
|
|
342d02c8a8 | ||
|
|
ae885eaddc | ||
|
|
b75bb4ed9f | ||
|
|
a171863591 | ||
|
|
bcc2286018 | ||
|
|
beb8b9723f | ||
|
|
ab3129b9c3 | ||
|
|
c061505bf8 | ||
|
|
171bbedcde | ||
|
|
ae655727c0 | ||
|
|
288b3783c3 | ||
|
|
320612c826 | ||
|
|
9b0a293d2b | ||
|
|
d5b6f27e97 | ||
|
|
cf8bd759e3 | ||
|
|
c2142c5cd9 | ||
|
|
eeef536803 | ||
|
|
ec51cf7606 | ||
|
|
ec83b76e46 | ||
|
|
6bc73f8f43 | ||
|
|
65127e04aa | ||
|
|
2726045fb0 | ||
|
|
d4046c3295 | ||
|
|
97d4f5583c | ||
|
|
96dc36cd16 | ||
|
|
ab2344ebfc | ||
|
|
4dd7d86ea8 | ||
|
|
6adfa618a3 | ||
|
|
855e896a5a | ||
|
|
2bacb0f073 | ||
|
|
399131a5a9 | ||
|
|
4c77be9e83 | ||
|
|
8ed5463f19 | ||
|
|
63739df903 | ||
|
|
3d72232bb9 | ||
|
|
d5ff811de7 | ||
|
|
f74f47965c | ||
|
|
79adb7225e | ||
|
|
11e79914ef | ||
|
|
9b34fc369b | ||
|
|
ae812806a1 | ||
|
|
956c7728bf | ||
|
|
6d5a5a46e4 | ||
|
|
b244126d60 | ||
|
|
2a7f8c5229 | ||
|
|
59fddf7c59 | ||
|
|
d21fd2a1ed | ||
|
|
6828a5d9a0 | ||
|
|
32960332b9 | ||
|
|
9ba77c9498 | ||
|
|
65f9a9dcc1 | ||
|
|
13315eec42 | ||
|
|
e43ccd155b | ||
|
|
e92960a413 | ||
|
|
e82eec0cd6 | ||
|
|
23b687c528 | ||
|
|
5044a814a1 | ||
|
|
3250b95f5e | ||
|
|
2a0934ec28 | ||
|
|
f1752abc5d | ||
|
|
e931ec74dd | ||
|
|
528d823c55 | ||
|
|
6475b58541 | ||
|
|
59e8b26015 | ||
|
|
0894c21296 | ||
|
|
4223bdd4ad | ||
|
|
c55ed42b2b | ||
|
|
7abc833ebe | ||
|
|
6abe399e36 | ||
|
|
555b1a16da | ||
|
|
52b956e677 | ||
|
|
0296cbe9e9 | ||
|
|
8315d7790a | ||
|
|
dd60d289ff | ||
|
|
8c61fd0bf7 | ||
|
|
814e08edd4 | ||
|
|
44956dff85 | ||
|
|
18685d061a | ||
|
|
692d5be166 | ||
|
|
60bdaef716 | ||
|
|
cb51f44a45 | ||
|
|
88f43a8124 | ||
|
|
7de7cdba60 | ||
|
|
ba140c60e3 | ||
|
|
95a5c7a001 | ||
|
|
b399da72d8 | ||
|
|
f9a10d8932 | ||
|
|
549ce6fbf9 | ||
|
|
e63d27a035 | ||
|
|
ab3621fe3c | ||
|
|
8b99f2ecbc | ||
|
|
433b309907 | ||
|
|
ac40cec138 | ||
|
|
93702ece48 | ||
|
|
1678474830 | ||
|
|
4260099c23 | ||
|
|
6139cb50bc | ||
|
|
49e334d726 | ||
|
|
6beded153b | ||
|
|
750308a16a | ||
|
|
57fa00b765 | ||
|
|
b57d4fdbec | ||
|
|
93aca81265 | ||
|
|
5c704e142e | ||
|
|
1a4a77066e | ||
|
|
a690b9d825 | ||
|
|
b70969fd03 | ||
|
|
daf5fff83f | ||
|
|
aef1f9b857 | ||
|
|
c1c8ea7df0 | ||
|
|
b75034b40c | ||
|
|
cb41a79b36 | ||
|
|
cedc20ce6a | ||
|
|
43bc1e9116 | ||
|
|
6fa1379c1e | ||
|
|
cb16438b1c | ||
|
|
e6c8d6cc7d | ||
|
|
f0e87e71ab | ||
|
|
cdd28b8e31 | ||
|
|
9bff564ace | ||
|
|
62c616c6af | ||
|
|
8a35f5d6ca | ||
|
|
e1b051324d | ||
|
|
a947c3152a | ||
|
|
cca43040d3 | ||
|
|
9b42657ca7 | ||
|
|
bb19c55c3a | ||
|
|
1ec6611410 | ||
|
|
218bd0ffc1 | ||
|
|
d649a22b80 | ||
|
|
0bffbbfe65 | ||
|
|
04a562372b | ||
|
|
11b08ce53a | ||
|
|
70f8f9679d | ||
|
|
4773f9ebf6 | ||
|
|
c1b6d1706a | ||
|
|
9f94b8f915 | ||
|
|
3abd97d0fb | ||
|
|
416a0687ee | ||
|
|
7056e20075 | ||
|
|
de4b158a44 | ||
|
|
c7f4648d5a | ||
|
|
a456be9d76 | ||
|
|
3befaac114 | ||
|
|
11616ee03b | ||
|
|
2a59feddb6 | ||
|
|
e2fa0aface | ||
|
|
6d4d954713 | ||
|
|
a4592ca425 | ||
|
|
700eae4cc6 | ||
|
|
2c9fe6f37d | ||
|
|
558ede11cf | ||
|
|
4c0a68ab0b | ||
|
|
d6b4931001 | ||
|
|
79ef01cb25 | ||
|
|
ea8735f390 | ||
|
|
7b70855e94 | ||
|
|
65f818b631 | ||
|
|
0a0baeaeab | ||
|
|
e011ed1f64 | ||
|
|
5142bf4338 | ||
|
|
e3532612ff | ||
|
|
d25e403233 | ||
|
|
8a5580eae5 | ||
|
|
cf1251ad7b | ||
|
|
4b1d0e8786 | ||
|
|
b6e99ce4a6 | ||
|
|
920def30d7 | ||
|
|
3839aa7419 | ||
|
|
8fde720f02 | ||
|
|
c6dfaa30b5 | ||
|
|
0d4975ba0f | ||
|
|
77325c98a6 | ||
|
|
01dc088a6f | ||
|
|
c20e9820fe | ||
|
|
d255c116dd | ||
|
|
2c1da3458a | ||
|
|
8017e42797 | ||
|
|
c162a7d3b1 | ||
|
|
d759d3dfee | ||
|
|
daecf4db14 | ||
|
|
1e8318598a | ||
|
|
53450d1160 | ||
|
|
c0049b3223 | ||
|
|
3ea9ce0270 | ||
|
|
bb18ffd9e7 | ||
|
|
1064c04ab8 | ||
|
|
2d84027a59 | ||
|
|
98680508d3 | ||
|
|
74cbec468f | ||
|
|
31e89b0868 | ||
|
|
c12a7a6319 | ||
|
|
82b0df6058 | ||
|
|
b6bd67b62f | ||
|
|
877226014d | ||
|
|
7572d306a0 | ||
|
|
f2f4573064 | ||
|
|
b1641edff6 | ||
|
|
b5147256e9 | ||
|
|
06f596adc6 | ||
|
|
1f3b54e0c4 | ||
|
|
2ddfbe8566 | ||
|
|
c61a118e4f | ||
|
|
d69e61a634 | ||
|
|
14f0cbaec6 | ||
|
|
b313eb14ee | ||
|
|
7b47e40244 | ||
|
|
b52204817d | ||
|
|
377552103e | ||
|
|
688b65ccde | ||
|
|
6cb4faf33d | ||
|
|
78b83bb901 | ||
|
|
9ff6f60b66 | ||
|
|
624e10ed15 | ||
|
|
19e10bbb53 | ||
|
|
cca945e05b | ||
|
|
21901f2a75 | ||
|
|
ef7f943eee | ||
|
|
ec1062f9f2 | ||
|
|
2f67ed3138 | ||
|
|
ce912db30e | ||
|
|
41d790f346 | ||
|
|
bf426e15ec | ||
|
|
e4403baeb9 | ||
|
|
61101b00a1 | ||
|
|
69f8ffcfeb | ||
|
|
6b8042291c | ||
|
|
ffc0c83b50 | ||
|
|
8ccd4c269a | ||
|
|
934ec86f93 | ||
|
|
23be38b5fa | ||
|
|
fe7f74e46b | ||
|
|
a3fd78f8e2 | ||
|
|
137bad6f7b | ||
|
|
17df6fc764 | ||
|
|
2e51c8a124 | ||
|
|
5588a46366 | ||
|
|
a8122f9add | ||
|
|
5568be91d2 | ||
|
|
a04bd6f93c | ||
|
|
56d63b10e4 | ||
|
|
2c97643b10 | ||
|
|
679f403648 | ||
|
|
d482c707f6 | ||
|
|
2ec9641783 | ||
|
|
dab1788a3b | ||
|
|
47bb79cce1 | ||
|
|
41dbc20be9 | ||
|
|
10a631ec96 | ||
|
|
830e5aed96 | ||
|
|
7db573885b | ||
|
|
a74d56ebc6 | ||
|
|
ff7d84297e | ||
|
|
3a76509fe9 | ||
|
|
ac4de9ab0f | ||
|
|
471f397418 | ||
|
|
73bbdf6d4e | ||
|
|
7f26aea585 | ||
|
|
1c767b709f | ||
|
|
0ced82c885 | ||
|
|
21dd195b0d | ||
|
|
6aa6cfba8e | ||
|
|
fd7d52d38b | ||
|
|
a47bb14364 | ||
|
|
d6e6fa5735 | ||
|
|
46da11a52e | ||
|
|
68e3dc21e4 | ||
|
|
7232cc45b4 | ||
|
|
be5a297248 | ||
|
|
257031b1bc | ||
|
|
c9db9fa17a | ||
|
|
13f961a422 | ||
|
|
3b38e0c4e1 | ||
|
|
07526efe61 | ||
|
|
8753c02adb | ||
|
|
6a0bbfa447 | ||
|
|
21faaeb33d | ||
|
|
0525fc5909 | ||
|
|
a1a53bb285 | ||
|
|
0c453c4415 | ||
|
|
d0406f9736 | ||
|
|
ba74b8603d | ||
|
|
c675a4d61d | ||
|
|
965c45bc70 | ||
|
|
139a22602a | ||
|
|
e0e4969198 | ||
|
|
08d69d95b3 | ||
|
|
4e6c507ba9 | ||
|
|
fd06374365 | ||
|
|
a07ebc636a | ||
|
|
4c151ac9aa | ||
|
|
05c425698f | ||
|
|
2a961979e6 | ||
|
|
211ede92cc | ||
|
|
256af03772 | ||
|
|
654fd5a4f9 | ||
|
|
541d90e49f | ||
|
|
974e7038e7 | ||
|
|
e2f5b30aa9 | ||
|
|
3483e7d9e0 | ||
|
|
56cb20a1af | ||
|
|
64929653dd | ||
|
|
c955da9bc6 | ||
|
|
291354fa8e | ||
|
|
905d736512 | ||
|
|
3406d6e2a9 | ||
|
|
fc10b5ffb9 | ||
|
|
f89c313166 | ||
|
|
7c734168d0 | ||
|
|
1e7bfec2ce | ||
|
|
1eb0603b4e | ||
|
|
4b32730ce8 | ||
|
|
ad083c1d9b | ||
|
|
b4f84c2de2 | ||
|
|
fc17443ce4 | ||
|
|
342ae06b21 | ||
|
|
093fb7f959 | ||
|
|
f6472424ad | ||
|
|
31ed3767c6 | ||
|
|
366acb65ea | ||
|
|
7c6946931b | ||
|
|
5d971433a5 | ||
|
|
05264b326b | ||
|
|
fdd5c6bfd8 | ||
|
|
42c3528c2f | ||
|
|
18640714f1 | ||
|
|
cda4d3399b | ||
|
|
4da8af6e69 | ||
|
|
b535565612 | ||
|
|
bef39b8a96 | ||
|
|
fb2502a031 | ||
|
|
0b90befda1 | ||
|
|
f9800f104a | ||
|
|
99134cc381 | ||
|
|
66ca08da6d | ||
|
|
5eb7ece6ba | ||
|
|
6b3d334e76 | ||
|
|
14f5fd8f73 | ||
|
|
5f73aee0df | ||
|
|
f8666ba367 | ||
|
|
9e80f76dd8 | ||
|
|
c76a5eaf67 | ||
|
|
cd378f0168 | ||
|
|
7d51ff0cf5 | ||
|
|
47819ea956 | ||
|
|
c7e3560c98 | ||
|
|
b24400b21d | ||
|
|
6c1d651687 | ||
|
|
e7757b53e7 | ||
|
|
a6d182e92d | ||
|
|
925eca1463 | ||
|
|
8b454f0d39 | ||
|
|
7c4d110353 | ||
|
|
6df55523b6 | ||
|
|
3ec6a24634 | ||
|
|
164b4218c4 | ||
|
|
56df8a6477 | ||
|
|
47a83b312d | ||
|
|
41a28ae088 | ||
|
|
436a8755ae | ||
|
|
960b179d95 | ||
|
|
0f0d0e1076 | ||
|
|
a8bd0d8075 | ||
|
|
986d3af685 | ||
|
|
1833f9ffdf | ||
|
|
30a6877f8a | ||
|
|
aaae2583c7 | ||
|
|
7f24afc2e7 | ||
|
|
0040923e12 | ||
|
|
844efb88d8 | ||
|
|
9efc3dd1fb | ||
|
|
67853bad8e | ||
|
|
faa8e1441a | ||
|
|
5c54611d1b | ||
|
|
dcf55e4385 | ||
|
|
2b0f1b6aab | ||
|
|
ae6cc8eea3 | ||
|
|
5279fa49a7 | ||
|
|
dcd8a62784 | ||
|
|
8c197b0e1a | ||
|
|
aed824b200 | ||
|
|
036b30212e | ||
|
|
3451ab3f50 | ||
|
|
0d0a92c2b1 | ||
|
|
aa19bc7bf5 | ||
|
|
347759b2e7 | ||
|
|
352230446c | ||
|
|
3eff8102e1 | ||
|
|
6693d845d9 | ||
|
|
4d79c462db | ||
|
|
c44ef6a1dc | ||
|
|
f0996fcfa7 | ||
|
|
54bc384d37 | ||
|
|
504fc1efe8 | ||
|
|
f4179b93fb | ||
|
|
564252c198 | ||
|
|
802a7a4463 | ||
|
|
3b3d6ba13c | ||
|
|
7350bf58e2 | ||
|
|
d37e29afc6 | ||
|
|
40de8c9e23 | ||
|
|
c81eac13c8 | ||
|
|
a6e1860f0d | ||
|
|
03eb2d81f0 | ||
|
|
171710b5e8 | ||
|
|
bed76429c2 | ||
|
|
d19f9b5062 | ||
|
|
38081d9822 | ||
|
|
54e278d3f7 | ||
|
|
181ed1b1a5 | ||
|
|
fb2d325ccb | ||
|
|
5f94a52537 | ||
|
|
c69b50c5d2 | ||
|
|
1c72f89178 | ||
|
|
14bd16da14 | ||
|
|
11a57f4618 | ||
|
|
57f35aff90 | ||
|
|
60e63a307f | ||
|
|
175e878ea6 | ||
|
|
5c960a3213 | ||
|
|
5dfb299e37 | ||
|
|
3890d4d9d1 | ||
|
|
77c62d6e7d | ||
|
|
ba54b53194 | ||
|
|
b4ef7352f2 | ||
|
|
1ce3368530 | ||
|
|
a4b32f3cb7 | ||
|
|
ee9cc05ae0 | ||
|
|
b8ccf2b0d6 | ||
|
|
886b499b94 | ||
|
|
07924d5621 | ||
|
|
43f3367ae4 | ||
|
|
454c73f42f | ||
|
|
041df698d5 | ||
|
|
97081f1219 | ||
|
|
f6792bf080 | ||
|
|
88635f31d6 | ||
|
|
abd0f115fc | ||
|
|
e9766c76c1 | ||
|
|
570506b324 | ||
|
|
11889880eb | ||
|
|
a86abde893 | ||
|
|
2cfe3360d8 | ||
|
|
60d75cb8ee | ||
|
|
68838e310a | ||
|
|
161de6cb7c | ||
|
|
af5a9b644b | ||
|
|
785426eab5 | ||
|
|
9267aef498 | ||
|
|
ae27a07578 | ||
|
|
131b2a35aa | ||
|
|
5a89601b24 | ||
|
|
2528bbc552 | ||
|
|
7c3a480003 | ||
|
|
80eac8443d | ||
|
|
a97234c48d | ||
|
|
53ea58c2f6 | ||
|
|
d867524c6b | ||
|
|
5edf9bde78 | ||
|
|
770ea55872 | ||
|
|
4eb0101c5b | ||
|
|
5d7af94abf | ||
|
|
b729b8f7c8 | ||
|
|
064e69d943 | ||
|
|
d880931951 | ||
|
|
f24741cd32 | ||
|
|
45c7017e83 | ||
|
|
7cfb891e6b | ||
|
|
fc8604e896 | ||
|
|
6b5e94103d | ||
|
|
aee4679ae5 | ||
|
|
2c2c930fce | ||
|
|
3f309e4db5 | ||
|
|
d26be402db | ||
|
|
a571e83005 | ||
|
|
10d5228eb2 | ||
|
|
7ed49b476f | ||
|
|
5396b90695 | ||
|
|
a6983d4e7b | ||
|
|
a3d1c76f67 | ||
|
|
15fab226b7 | ||
|
|
5a065d5a05 | ||
|
|
de81f3ffbb | ||
|
|
9103369cf6 | ||
|
|
7be36e6d0d | ||
|
|
a00e3e6f41 | ||
|
|
82ba02b4f3 | ||
|
|
d70ae6ebe3 | ||
|
|
f6c411a261 | ||
|
|
b606eaf9ee | ||
|
|
516edd1b09 | ||
|
|
e31c3b1f27 | ||
|
|
619818f968 | ||
|
|
79a80a1adf | ||
|
|
7cef48b995 | ||
|
|
7d3d1b1544 | ||
|
|
3f935f271d | ||
|
|
89935a1517 | ||
|
|
c67af4fb2f | ||
|
|
0b4adc36a0 | ||
|
|
44776b795f | ||
|
|
bec73a1c43 | ||
|
|
6ce35fdfa8 | ||
|
|
dabc2d0442 | ||
|
|
0527d3bc2b | ||
|
|
a7cfb71070 | ||
|
|
52003bedb4 | ||
|
|
a02fb8e739 | ||
|
|
60fad187a2 | ||
|
|
e8cd1e070f | ||
|
|
de6620be12 | ||
|
|
72dee73faa | ||
|
|
d8ce27907d | ||
|
|
3d8891d518 | ||
|
|
97742ccdc2 | ||
|
|
82fec86179 | ||
|
|
be83b53875 | ||
|
|
85fda0c18b | ||
|
|
a89f8fbd9c | ||
|
|
efdfa1f2f7 | ||
|
|
5bd61e3fb0 | ||
|
|
a45f83b646 | ||
|
|
16135b8e37 | ||
|
|
b011e8656f | ||
|
|
215432be6c | ||
|
|
d373760412 | ||
|
|
a1de04e285 | ||
|
|
23e16732fd | ||
|
|
5efac84b8b | ||
|
|
2cbc7b7d7d | ||
|
|
b1acbaecc2 | ||
|
|
6d61e8efff | ||
|
|
482e6b3cb3 | ||
|
|
445b13ec29 | ||
|
|
116af372dc | ||
|
|
970952783c | ||
|
|
e59cf13456 | ||
|
|
d0cfddce19 | ||
|
|
30b2a8dd8d | ||
|
|
b811ee7e7e | ||
|
|
ebe7f6784a | ||
|
|
e40792378f | ||
|
|
cc9c8fb891 | ||
|
|
ca06c4d403 | ||
|
|
c8aa058ede | ||
|
|
34169d685e | ||
|
|
d5a9d36815 | ||
|
|
c7aaeca530 | ||
|
|
863e4f0c19 | ||
|
|
0226e0553d | ||
|
|
02995d278f | ||
|
|
78a2104bcc | ||
|
|
4e9d143996 | ||
|
|
0811e5c765 | ||
|
|
b2cf2edd43 | ||
|
|
db493f6887 | ||
|
|
2cd0dec480 | ||
|
|
29024888fb | ||
|
|
dbcaab2bc1 | ||
|
|
28d445ae1c | ||
|
|
530360f859 | ||
|
|
738c55bad0 | ||
|
|
4b09bc85f5 | ||
|
|
5bc67d3570 | ||
|
|
f7ae6222b7 | ||
|
|
1e50dab093 | ||
|
|
d1935bf778 | ||
|
|
70a346d11e | ||
|
|
fd39a2063d | ||
|
|
682512fffe | ||
|
|
b13f91ec8d | ||
|
|
a140fc09ac | ||
|
|
f403a7e753 | ||
|
|
dfe5f412eb | ||
|
|
033d784c52 | ||
|
|
62c3fa13ca | ||
|
|
ce338cb6ca | ||
|
|
003eadc8fd | ||
|
|
8782151c5d | ||
|
|
b22c74c5a8 | ||
|
|
254fa36c01 | ||
|
|
a3e4253005 | ||
|
|
2388593b8a | ||
|
|
cdced63c1b | ||
|
|
45e1d1ecef | ||
|
|
f44447ce71 | ||
|
|
238e9cd8cc | ||
|
|
e171d8ed0e | ||
|
|
bd3399e04b | ||
|
|
2b4443f333 | ||
|
|
ab6548122f | ||
|
|
f81573d999 | ||
|
|
84ccebb858 | ||
|
|
530bc50e7c | ||
|
|
57e490fc23 | ||
|
|
61e902c094 | ||
|
|
8378ba77d6 | ||
|
|
c9e30b74e2 | ||
|
|
af944fd2e3 | ||
|
|
bcc0e76f1d | ||
|
|
95078d250a | ||
|
|
4b16a2c0c5 | ||
|
|
b8524732ce | ||
|
|
814fee4f47 | ||
|
|
d641d35d5c | ||
|
|
7464d95b57 | ||
|
|
8924a64622 | ||
|
|
3d6aa667fe | ||
|
|
147c3d2e7b | ||
|
|
ac298c3be3 | ||
|
|
e88848c44a | ||
|
|
cd518e3e4c | ||
|
|
114d521636 | ||
|
|
24d4fad394 | ||
|
|
6d8785e689 | ||
|
|
508cbf0a82 | ||
|
|
c83f56166d | ||
|
|
7199e1a214 | ||
|
|
85d55e97e7 | ||
|
|
cc2c71c076 | ||
|
|
9ca273b2c4 | ||
|
|
b85c2f35b6 | ||
|
|
fdd79885f9 | ||
|
|
b2eb970796 | ||
|
|
3ee9c1b550 | ||
|
|
2566c24753 | ||
|
|
49e1b0ba7e | ||
|
|
453c329f14 | ||
|
|
abad2944fb | ||
|
|
27193f38f3 | ||
|
|
d3dc94e210 | ||
|
|
6dad860635 | ||
|
|
0362ac8909 | ||
|
|
e7b79f83d1 | ||
|
|
62379c1e41 | ||
|
|
23b422e3df | ||
|
|
f8e6dee635 | ||
|
|
c8e9b287f4 | ||
|
|
c9412dbcd0 | ||
|
|
77222e9e6b | ||
|
|
2827544409 | ||
|
|
9d0f24eae1 | ||
|
|
db0a399da1 | ||
|
|
6e527947be | ||
|
|
e7051c1129 | ||
|
|
3196c7ca09 | ||
|
|
0e1e32d241 | ||
|
|
a34912fb0d | ||
|
|
c7c6e0e2ff | ||
|
|
1e529c995a | ||
|
|
7be1c7a47b | ||
|
|
b17380443b | ||
|
|
59e68682bd | ||
|
|
b7a92cfe92 | ||
|
|
5ebe27da49 | ||
|
|
42df6ba6fa | ||
|
|
8210fddfab | ||
|
|
f55ed273c5 | ||
|
|
d67e95af7b | ||
|
|
0b0f235252 | ||
|
|
36a5f52068 | ||
|
|
31266728f7 | ||
|
|
87d2096ed7 | ||
|
|
8c79ea4ce3 | ||
|
|
c73a4204cb | ||
|
|
0b3c2cc252 | ||
|
|
2bd3ca1d0b | ||
|
|
ce8649d991 | ||
|
|
9bd563b111 | ||
|
|
6ceb924a85 | ||
|
|
c2ef0ded43 | ||
|
|
6081a6f6db | ||
|
|
a0d92a0b1d | ||
|
|
3cf1f7ede2 | ||
|
|
5757afa1d8 | ||
|
|
86e9b9c1b8 | ||
|
|
1cdd1fa00e | ||
|
|
9d12759c68 | ||
|
|
d47f66548d | ||
|
|
594341fab6 | ||
|
|
4e88125cbe | ||
|
|
13237180a2 | ||
|
|
f64d7e0b6e | ||
|
|
040a6930a4 | ||
|
|
c54b9189a6 | ||
|
|
8882f1b019 | ||
|
|
ae6416c4d2 | ||
|
|
8faed87656 | ||
|
|
0983f05969 | ||
|
|
d43e2544a1 | ||
|
|
ca83d11ac8 | ||
|
|
1cdcdd9b5f | ||
|
|
cc7806e35b | ||
|
|
0ee48b6623 | ||
|
|
8c02e0efbd | ||
|
|
34d3ca82bc | ||
|
|
43822d3667 | ||
|
|
f4ac73b3b4 | ||
|
|
f084b6def9 | ||
|
|
a00d101ff7 | ||
|
|
9d5900d9b6 | ||
|
|
28fb4e8216 | ||
|
|
bec4dbe652 | ||
|
|
72cc14f508 | ||
|
|
d20941cc2c | ||
|
|
9cb8a05316 | ||
|
|
dccd799f6d | ||
|
|
b42b3d1b01 | ||
|
|
a40d6f772a | ||
|
|
6e9bfd18d9 | ||
|
|
3b92dd0139 | ||
|
|
564d53610a | ||
|
|
b4c7b8ac7f | ||
|
|
7acd90307b | ||
|
|
d3ec76c19f | ||
|
|
fb9425e503 | ||
|
|
688cb20674 | ||
|
|
c63be20bea | ||
|
|
df36633223 | ||
|
|
3597621d88 | ||
|
|
8387684839 | ||
|
|
f261f395f1 | ||
|
|
f27170ff0e | ||
|
|
d82c951db6 | ||
|
|
41ca853e03 | ||
|
|
d75580e11d | ||
|
|
a08d098265 | ||
|
|
a64960ddd0 | ||
|
|
875681b8ce | ||
|
|
a03dcbbf55 | ||
|
|
97cabbbc69 | ||
|
|
13725a9e21 | ||
|
|
f47df961f7 | ||
|
|
2f644d5eeb | ||
|
|
4b292bb78c | ||
|
|
804891cc81 | ||
|
|
d335e06371 | ||
|
|
477058ad23 | ||
|
|
eb3b68401d | ||
|
|
865d2df124 | ||
|
|
88160bae1d | ||
|
|
f581e93b88 | ||
|
|
876850a7a7 | ||
|
|
21a7cf7158 | ||
|
|
5edee4bae0 | ||
|
|
916ca5576a | ||
|
|
6c861bfd1f | ||
|
|
56961b55bd | ||
|
|
cdcd7154ba | ||
|
|
654a2ee870 | ||
|
|
903634073a | ||
|
|
0d4818feb6 | ||
|
|
d6aa40679b | ||
|
|
b7cc31c94d | ||
|
|
6860156b6f | ||
|
|
29486c9ce2 | ||
|
|
7cfa6a5da4 | ||
|
|
2563be472b | ||
|
|
7289e856d9 | ||
|
|
975de1954e | ||
|
|
95bcf0c080 | ||
|
|
f900a5ef4f | ||
|
|
7f1ab529ae | ||
|
|
49fc86e4b1 | ||
|
|
924aef84f1 | ||
|
|
96e6e2b72a | ||
|
|
71997d4e65 | ||
|
|
447f2f9506 | ||
|
|
79aef9024b | ||
|
|
fdf6f4fdf3 | ||
|
|
4d1eaaaade | ||
|
|
bdad6c0f6d | ||
|
|
ff1ca5d933 | ||
|
|
2cf4c494a4 | ||
|
|
95ac0a861a | ||
|
|
746c301f39 | ||
|
|
6455b12b58 | ||
|
|
485b8fe993 | ||
|
|
d7527f280c | ||
|
|
d57fa4375d | ||
|
|
d9e42c6625 | ||
|
|
28293d3fce | ||
|
|
d505401446 | ||
|
|
fafc24aeae | ||
|
|
f78ef0d208 | ||
|
|
8384cc3652 | ||
|
|
60aa18a229 | ||
|
|
3d64b42a89 | ||
|
|
b301997d4b | ||
|
|
ab34743250 | ||
|
|
bc14a1d167 | ||
|
|
2886ec116f | ||
|
|
c2beb2a5fa | ||
|
|
d6ac10f527 | ||
|
|
9dcd8a707a | ||
|
|
e1e97ef158 | ||
|
|
5d6b37f81a | ||
|
|
e1da08ba38 | ||
|
|
1dfb50fefd | ||
|
|
5c06ebc9c8 | ||
|
|
52a9270fb0 | ||
|
|
82247d7422 | ||
|
|
b34688043f | ||
|
|
ce4bcbd19d | ||
|
|
cde9a02c32 | ||
|
|
fe1da4ea12 | ||
|
|
a73306817b | ||
|
|
54e683d3d4 | ||
|
|
f49910ca82 | ||
|
|
4052f7f736 | ||
|
|
b47e097983 | ||
|
|
e44dbfb2a4 | ||
|
|
7d69dd9400 | ||
|
|
e6aae8fcfa | ||
|
|
da800b3391 | ||
|
|
3a574bcecc | ||
|
|
1bb0e234e7 | ||
|
|
b7e14ebf2a | ||
|
|
2af1207702 | ||
|
|
ecfed30e6e | ||
|
|
d06c3e3dd8 | ||
|
|
16b3fbeb16 | ||
|
|
0938804947 | ||
|
|
851bcf9816 | ||
|
|
9f6fc785bc | ||
|
|
56636bf5d4 | ||
|
|
3899a65167 | ||
|
|
628e53c1c3 | ||
|
|
0b689d99fa | ||
|
|
9fa424dd8d | ||
|
|
3e6f2eecfa | ||
|
|
cc655c8ba8 | ||
|
|
78aa0474ee | ||
|
|
9caefa2f49 | ||
|
|
478726fa3b | ||
|
|
f64917ec52 | ||
|
|
2bc25f91c4 | ||
|
|
623d7ffe2f | ||
|
|
07510b5099 | ||
|
|
9f21f9a7bc | ||
|
|
93da70709e | ||
|
|
00436e744a | ||
|
|
1e642fc512 | ||
|
|
6baef2450c | ||
|
|
600f34f85a | ||
|
|
6c0c6bc5c4 | ||
|
|
fcd62ed3cd | ||
|
|
cd0064d19c | ||
|
|
785f2e3a6d | ||
|
|
c2925f7c1e | ||
|
|
60814d8b58 | ||
|
|
2dec448f2c | ||
|
|
b71f4f6800 | ||
|
|
558083a916 | ||
|
|
d507ed9dff | ||
|
|
7ed0242662 | ||
|
|
d7b3d989d7 | ||
|
|
707b2f77f0 | ||
|
|
5ddbb76979 | ||
|
|
97b0fe62d4 | ||
|
|
8ac9b2cdc7 | ||
|
|
b4baa6cd7b | ||
|
|
bc4c1a13e6 | ||
|
|
d3ec303ade | ||
|
|
6cfc2a1ba6 | ||
|
|
e15cadc863 | ||
|
|
2f9284c263 | ||
|
|
2465852fd6 | ||
|
|
a9f48a0d50 | ||
|
|
4ed0035c67 | ||
|
|
b66f2dfb80 | ||
|
|
3cb155b129 | ||
|
|
df7efc04e2 | ||
|
|
a21a8457a4 | ||
|
|
1ab2cdeed3 | ||
|
|
020955f535 | ||
|
|
51f23a5f03 | ||
|
|
d024319441 | ||
|
|
f8f35938c0 | ||
|
|
2573ace368 | ||
|
|
6bf7269814 | ||
|
|
6695c7bf5e | ||
|
|
83c0281a33 | ||
|
|
44a83fd817 | ||
|
|
08ddfe0649 | ||
|
|
5ba170bf1f | ||
|
|
437b0b0240 | ||
|
|
8150d3110c | ||
|
|
312b33ae85 | ||
|
|
008eb995ed | ||
|
|
6d8848043c | ||
|
|
cf572c0cc5 | ||
|
|
18cfa7dd29 | ||
|
|
72cac2bbd6 | ||
|
|
48ffa28e0b | ||
|
|
2e6baeb95a | ||
|
|
3b5650dc1e | ||
|
|
3279728e4b | ||
|
|
fe0dcbacc5 | ||
|
|
5c48697eda | ||
|
|
7c5d90fe40 | ||
|
|
944dad6e36 | ||
|
|
6713d3ec66 | ||
|
|
6adadb2359 | ||
|
|
b01096876c | ||
|
|
60243d8517 | ||
|
|
94d0809380 | ||
|
|
e935dd9bad | ||
|
|
30aa2b83d0 | ||
|
|
0feea5b7a6 | ||
|
|
9eb27fdd5e | ||
|
|
6e4a64232a | ||
|
|
4bbedeeea9 | ||
|
|
b5c004e870 | ||
|
|
a0ef1ab4f4 | ||
|
|
c9172a11a8 | ||
|
|
a0feee912e | ||
|
|
8e42b7b891 | ||
|
|
147d7e773f | ||
|
|
759ab1c5ee | ||
|
|
4c1581d845 | ||
|
|
e1c6e4347a | ||
|
|
256f117bbf | ||
|
|
3b0acf82c7 | ||
|
|
3a12f3d6c7 | ||
|
|
335ac5a595 | ||
|
|
d0e2e97007 | ||
|
|
85e1baa2dc | ||
|
|
0c66c39211 | ||
|
|
250afa38ca | ||
|
|
b7e58eeb3f | ||
|
|
6f024d78a6 | ||
|
|
1e48096f36 | ||
|
|
ccf6a1bedb | ||
|
|
3639edb4db | ||
|
|
d3bbe0b3b6 | ||
|
|
e8ab101993 | ||
|
|
ef98b10063 | ||
|
|
84943e7fe6 | ||
|
|
d0fa5ff385 | ||
|
|
3609559ced | ||
|
|
950c780122 | ||
|
|
32b510ef40 | ||
|
|
4cc33ed29b | ||
|
|
d72906a6ba | ||
|
|
d577b51a86 | ||
|
|
63d4865427 | ||
|
|
1355477e37 | ||
|
|
d50e1b4e02 | ||
|
|
606ae41698 | ||
|
|
b6ee5ae779 | ||
|
|
aeb1b2c30f | ||
|
|
35ace281cc | ||
|
|
6cd056eee5 | ||
|
|
6c664bfaa7 | ||
|
|
8890d445dc | ||
|
|
7a7db1ea08 | ||
|
|
e585a3e5c4 | ||
|
|
7336032009 | ||
|
|
d29bc63b24 | ||
|
|
2a9bd1d4cb | ||
|
|
6578c14292 | ||
|
|
ceee93883f | ||
|
|
dae8fd2370 | ||
|
|
48f8322390 | ||
|
|
7df833bd9f | ||
|
|
2d639e191a | ||
|
|
db758c6806 | ||
|
|
6822e4ac0c | ||
|
|
14b1b07ecd | ||
|
|
3c71a20bb2 | ||
|
|
8f73619ba1 | ||
|
|
0ee6e5a35f | ||
|
|
22692757e6 | ||
|
|
ed9584270d | ||
|
|
5a5c35a1c9 | ||
|
|
1f842e4fe4 | ||
|
|
9275c4a6d6 | ||
|
|
9c7e61cbf3 | ||
|
|
69a6066fd8 | ||
|
|
47d2d09e50 | ||
|
|
da648e0a78 | ||
|
|
9e1c526d51 | ||
|
|
d81998f91c | ||
|
|
a717d9b8f3 | ||
|
|
31d1243a14 | ||
|
|
2424222964 | ||
|
|
370b245d65 | ||
|
|
c4dfcc27e3 | ||
|
|
dfa870a777 | ||
|
|
572375fff4 | ||
|
|
ed1caee9f8 | ||
|
|
6f7757c81b | ||
|
|
4c92965313 | ||
|
|
bbce96eb67 | ||
|
|
e3cb7bd4c7 | ||
|
|
79599bf831 | ||
|
|
1ab67bc225 | ||
|
|
37df213771 | ||
|
|
d48ffdb14f | ||
|
|
766cdc9f59 | ||
|
|
21a40c9d14 | ||
|
|
9275e9d006 | ||
|
|
ef9fe025e0 | ||
|
|
05694a8cda | ||
|
|
e6304cb028 | ||
|
|
b2d00784a4 | ||
|
|
ae31ebdc33 | ||
|
|
a2d50b380f | ||
|
|
654e8fd13f | ||
|
|
bcd44e4b2d | ||
|
|
5200793744 | ||
|
|
abcb29391c | ||
|
|
6a682dc143 | ||
|
|
d93d30537f | ||
|
|
377e88ff36 | ||
|
|
1733290c02 | ||
|
|
e702ccc48a | ||
|
|
ba729c493b | ||
|
|
1c55950b7e | ||
|
|
18c8282bac | ||
|
|
1d20456853 | ||
|
|
7e32d0ae10 | ||
|
|
5d33e45eae | ||
|
|
1590930ef9 | ||
|
|
8186d34f4e |
72
.eslintrc.js
72
.eslintrc.js
@@ -1,11 +1,7 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
extends: [
|
||||
'standard',
|
||||
'standard-jsx',
|
||||
'prettier',
|
||||
'prettier/standard',
|
||||
'prettier/react',
|
||||
],
|
||||
extends: ['plugin:eslint-comments/recommended', 'plugin:n/recommended', 'standard', 'standard-jsx', 'prettier'],
|
||||
globals: {
|
||||
__DEV__: true,
|
||||
$Dict: true,
|
||||
@@ -16,16 +12,62 @@ module.exports = {
|
||||
$PropertyType: true,
|
||||
$Shape: true,
|
||||
},
|
||||
parser: 'babel-eslint',
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
legacyDecorators: true,
|
||||
|
||||
overrides: [
|
||||
{
|
||||
files: ['cli.{,c,m}js', '*-cli.{,c,m}js', '**/*cli*/**/*.{,c,m}js'],
|
||||
rules: {
|
||||
'n/no-process-exit': 'off',
|
||||
'no-console': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.mjs'],
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.{spec,test}.{,c,m}js'],
|
||||
rules: {
|
||||
'n/no-unpublished-require': 'off',
|
||||
'n/no-unpublished-import': 'off',
|
||||
'n/no-unsupported-features/node-builtins': [
|
||||
'error',
|
||||
{
|
||||
version: '>=16',
|
||||
},
|
||||
],
|
||||
'n/no-unsupported-features/es-syntax': [
|
||||
'error',
|
||||
{
|
||||
version: '>=16',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
parserOptions: {
|
||||
ecmaVersion: 13,
|
||||
sourceType: 'script',
|
||||
},
|
||||
|
||||
rules: {
|
||||
'no-var': 'error',
|
||||
'node/no-extraneous-import': 'error',
|
||||
'node/no-extraneous-require': 'error',
|
||||
'prefer-const': 'error',
|
||||
// disabled because XAPI objects are using camel case
|
||||
camelcase: ['off'],
|
||||
|
||||
'react/jsx-handler-names': 'off',
|
||||
|
||||
// disabled because not always relevant, we might reconsider in the future
|
||||
//
|
||||
// enabled by https://github.com/standard/eslint-config-standard/commit/319b177750899d4525eb1210686f6aca96190b2f
|
||||
//
|
||||
// example: https://github.com/vatesfr/xen-orchestra/blob/31ed3767c67044ca445658eb6b560718972402f2/packages/xen-api/src/index.js#L156-L157
|
||||
'lines-between-class-members': 'off',
|
||||
|
||||
'no-console': ['error', { allow: ['warn', 'error'] }],
|
||||
|
||||
strict: 'error',
|
||||
},
|
||||
}
|
||||
|
||||
16
.flowconfig
16
.flowconfig
@@ -1,16 +0,0 @@
|
||||
[ignore]
|
||||
<PROJECT_ROOT>/node_modules/.*
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
|
||||
[lints]
|
||||
|
||||
[options]
|
||||
esproposal.decorators=ignore
|
||||
esproposal.optional_chaining=enable
|
||||
include_warnings=true
|
||||
module.use_strict=true
|
||||
|
||||
[strict]
|
||||
48
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
48
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: 'status: triaging :triangular_flag_on_post:, type: bug :bug:'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
1. ⚠️ **If you don't follow this template, the issue will be closed**.
|
||||
2. ⚠️ **If your issue can't be easily reproduced, please report it [on the forum first](https://xcp-ng.org/forum/category/12/xen-orchestra)**.
|
||||
|
||||
Are you using XOA or XO from the sources?
|
||||
|
||||
If XOA:
|
||||
|
||||
- which release channel? (`stable` vs `latest`)
|
||||
- please consider creating a support ticket in [your dedicated support area](https://xen-orchestra.com/#!/member/support)
|
||||
|
||||
If XO from the sources:
|
||||
|
||||
- Provide **your commit number**. If it's older than a week, we won't investigate
|
||||
- Don't forget to [read this first](https://xen-orchestra.com/docs/community.html)
|
||||
- As well as follow [this guide](https://xen-orchestra.com/docs/community.html#report-a-bug)
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Environment (please provide the following information):**
|
||||
|
||||
- Node: [e.g. 16.12.1]
|
||||
- hypervisor: [e.g. XCP-ng 8.2.0]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
19
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
19
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
12
.github/workflows/push.yml
vendored
Normal file
12
.github/workflows/push.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
name: CI
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Build docker image
|
||||
run: docker-compose -f docker/docker-compose.dev.yml build
|
||||
- name: Create the container and start the tests
|
||||
run: docker-compose -f docker/docker-compose.dev.yml up --exit-code-from xo
|
||||
16
.gitignore
vendored
16
.gitignore
vendored
@@ -1,23 +1,22 @@
|
||||
/_book/
|
||||
/coverage/
|
||||
/node_modules/
|
||||
/lerna-debug.log
|
||||
/lerna-debug.log.*
|
||||
|
||||
/@vates/*/dist/
|
||||
/@vates/*/node_modules/
|
||||
/@xen-orchestra/*/dist/
|
||||
/@xen-orchestra/*/node_modules/
|
||||
/packages/*/dist/
|
||||
/packages/*/node_modules/
|
||||
|
||||
/packages/vhd-cli/src/commands/index.js
|
||||
|
||||
/packages/xen-api/examples/node_modules/
|
||||
/packages/xen-api/plot.dat
|
||||
|
||||
/packages/xo-server/.xo-server.*
|
||||
/packages/xo-server/src/api/index.js
|
||||
/packages/xo-server/src/xapi/mixins/index.js
|
||||
/packages/xo-server/src/xo-mixins/index.js
|
||||
/packages/xo-server/src/api/index.mjs
|
||||
/packages/xo-server/src/xapi/mixins/index.mjs
|
||||
/packages/xo-server/src/xo-mixins/index.mjs
|
||||
|
||||
/packages/xo-server-auth-ldap/ldap.cache.conf
|
||||
|
||||
@@ -31,3 +30,8 @@ pnpm-debug.log.*
|
||||
yarn-error.log
|
||||
yarn-error.log.*
|
||||
.env
|
||||
|
||||
# code coverage
|
||||
.nyc_output/
|
||||
coverage/
|
||||
.turbo/
|
||||
|
||||
4
.husky/pre-commit
Executable file
4
.husky/pre-commit
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx lint-staged
|
||||
@@ -1,6 +1,11 @@
|
||||
module.exports = {
|
||||
arrowParens: 'avoid',
|
||||
jsxSingleQuote: true,
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
trailingComma: 'es5',
|
||||
|
||||
// 2020-11-24: Requested by nraynaud and approved by the rest of the team
|
||||
//
|
||||
// https://team.vates.fr/vates/pl/a1i8af1b9id7pgzm3jcg4toacy
|
||||
printWidth: 120,
|
||||
}
|
||||
|
||||
24
.travis.yml
24
.travis.yml
@@ -1,24 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
#- stable # disable for now due to an issue of indirect dep upath with Node 9
|
||||
- 8
|
||||
|
||||
# Use containers.
|
||||
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
|
||||
sudo: false
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- qemu-utils
|
||||
- blktap-utils
|
||||
- vmdk-stream-converter
|
||||
|
||||
before_install:
|
||||
- curl -o- -L https://yarnpkg.com/install.sh | bash
|
||||
- export PATH="$HOME/.yarn/bin:$PATH"
|
||||
|
||||
cache:
|
||||
yarn: true
|
||||
|
||||
script:
|
||||
- yarn run travis-tests
|
||||
35
@vates/async-each/.USAGE.md
Normal file
35
@vates/async-each/.USAGE.md
Normal file
@@ -0,0 +1,35 @@
|
||||
### `asyncEach(iterable, iteratee, [opts])`
|
||||
|
||||
Executes `iteratee` in order for each value yielded by `iterable`.
|
||||
|
||||
Returns a promise wich rejects as soon as a call to `iteratee` throws or a promise returned by it rejects, and which resolves when all promises returned by `iteratee` have resolved.
|
||||
|
||||
`iterable` must be an iterable or async iterable.
|
||||
|
||||
`iteratee` is called with the same `this` value as `asyncEach`, and with the following arguments:
|
||||
|
||||
- `value`: the value yielded by `iterable`
|
||||
- `index`: the 0-based index for this value
|
||||
- `iterable`: the iterable itself
|
||||
|
||||
`opts` is an object that can contains the following options:
|
||||
|
||||
- `concurrency`: a number which indicates the maximum number of parallel call to `iteratee`, defaults to `10`. The value `0` means no concurrency limit.
|
||||
- `signal`: an abort signal to stop the iteration
|
||||
- `stopOnError`: wether to stop iteration of first error, or wait for all calls to finish and throw an `AggregateError`, defaults to `true`
|
||||
|
||||
```js
|
||||
import { asyncEach } from '@vates/async-each'
|
||||
|
||||
const contents = []
|
||||
await asyncEach(
|
||||
['foo.txt', 'bar.txt', 'baz.txt'],
|
||||
async function (filename, i) {
|
||||
contents[i] = await readFile(filename)
|
||||
},
|
||||
{
|
||||
// reads two files at a time
|
||||
concurrency: 2,
|
||||
}
|
||||
)
|
||||
```
|
||||
1
@vates/async-each/.npmignore
Symbolic link
1
@vates/async-each/.npmignore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../scripts/npmignore
|
||||
68
@vates/async-each/README.md
Normal file
68
@vates/async-each/README.md
Normal file
@@ -0,0 +1,68 @@
|
||||
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
|
||||
|
||||
# @vates/async-each
|
||||
|
||||
[](https://npmjs.org/package/@vates/async-each)  [](https://bundlephobia.com/result?p=@vates/async-each) [](https://npmjs.org/package/@vates/async-each)
|
||||
|
||||
> Run async fn for each item in (async) iterable
|
||||
|
||||
## Install
|
||||
|
||||
Installation of the [npm package](https://npmjs.org/package/@vates/async-each):
|
||||
|
||||
```
|
||||
> npm install --save @vates/async-each
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### `asyncEach(iterable, iteratee, [opts])`
|
||||
|
||||
Executes `iteratee` in order for each value yielded by `iterable`.
|
||||
|
||||
Returns a promise wich rejects as soon as a call to `iteratee` throws or a promise returned by it rejects, and which resolves when all promises returned by `iteratee` have resolved.
|
||||
|
||||
`iterable` must be an iterable or async iterable.
|
||||
|
||||
`iteratee` is called with the same `this` value as `asyncEach`, and with the following arguments:
|
||||
|
||||
- `value`: the value yielded by `iterable`
|
||||
- `index`: the 0-based index for this value
|
||||
- `iterable`: the iterable itself
|
||||
|
||||
`opts` is an object that can contains the following options:
|
||||
|
||||
- `concurrency`: a number which indicates the maximum number of parallel call to `iteratee`, defaults to `10`. The value `0` means no concurrency limit.
|
||||
- `signal`: an abort signal to stop the iteration
|
||||
- `stopOnError`: wether to stop iteration of first error, or wait for all calls to finish and throw an `AggregateError`, defaults to `true`
|
||||
|
||||
```js
|
||||
import { asyncEach } from '@vates/async-each'
|
||||
|
||||
const contents = []
|
||||
await asyncEach(
|
||||
['foo.txt', 'bar.txt', 'baz.txt'],
|
||||
async function (filename, i) {
|
||||
contents[i] = await readFile(filename)
|
||||
},
|
||||
{
|
||||
// reads two files at a time
|
||||
concurrency: 2,
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions are _very_ welcomed, either on the documentation or on
|
||||
the code.
|
||||
|
||||
You may:
|
||||
|
||||
- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
|
||||
you've encountered;
|
||||
- fork and create a pull request.
|
||||
|
||||
## License
|
||||
|
||||
[ISC](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)
|
||||
108
@vates/async-each/index.js
Normal file
108
@vates/async-each/index.js
Normal file
@@ -0,0 +1,108 @@
|
||||
'use strict'
|
||||
|
||||
const noop = Function.prototype
|
||||
|
||||
class AggregateError extends Error {
|
||||
constructor(errors, message) {
|
||||
super(message)
|
||||
this.errors = errors
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template Item
|
||||
* @param {Iterable<Item>} iterable
|
||||
* @param {(item: Item, index: number, iterable: Iterable<Item>) => Promise<void>} iteratee
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
exports.asyncEach = function asyncEach(iterable, iteratee, { concurrency = 10, signal, stopOnError = true } = {}) {
|
||||
if (concurrency === 0) {
|
||||
concurrency = Infinity
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const it = (iterable[Symbol.iterator] || iterable[Symbol.asyncIterator]).call(iterable)
|
||||
const errors = []
|
||||
let running = 0
|
||||
let index = 0
|
||||
|
||||
let onAbort
|
||||
if (signal !== undefined) {
|
||||
onAbort = () => {
|
||||
onRejectedWrapper(new Error('asyncEach aborted'))
|
||||
}
|
||||
signal.addEventListener('abort', onAbort)
|
||||
}
|
||||
|
||||
const clean = () => {
|
||||
onFulfilled = onRejected = noop
|
||||
if (onAbort !== undefined) {
|
||||
signal.removeEventListener('abort', onAbort)
|
||||
}
|
||||
}
|
||||
|
||||
resolve = (resolve =>
|
||||
function resolveAndClean(value) {
|
||||
resolve(value)
|
||||
clean()
|
||||
})(resolve)
|
||||
reject = (reject =>
|
||||
function rejectAndClean(reason) {
|
||||
reject(reason)
|
||||
clean()
|
||||
})(reject)
|
||||
|
||||
let onFulfilled = value => {
|
||||
--running
|
||||
next()
|
||||
}
|
||||
const onFulfilledWrapper = value => onFulfilled(value)
|
||||
|
||||
let onRejected = stopOnError
|
||||
? reject
|
||||
: error => {
|
||||
--running
|
||||
errors.push(error)
|
||||
next()
|
||||
}
|
||||
const onRejectedWrapper = reason => onRejected(reason)
|
||||
|
||||
let nextIsRunning = false
|
||||
let next = async () => {
|
||||
if (nextIsRunning) {
|
||||
return
|
||||
}
|
||||
nextIsRunning = true
|
||||
if (running < concurrency) {
|
||||
const cursor = await it.next()
|
||||
if (cursor.done) {
|
||||
next = () => {
|
||||
if (running === 0) {
|
||||
if (errors.length === 0) {
|
||||
resolve()
|
||||
} else {
|
||||
reject(new AggregateError(errors))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
++running
|
||||
try {
|
||||
const result = iteratee.call(this, cursor.value, index++, iterable)
|
||||
let then
|
||||
if (result != null && typeof result === 'object' && typeof (then = result.then) === 'function') {
|
||||
then.call(result, onFulfilledWrapper, onRejectedWrapper)
|
||||
} else {
|
||||
onFulfilled(result)
|
||||
}
|
||||
} catch (error) {
|
||||
onRejected(error)
|
||||
}
|
||||
}
|
||||
nextIsRunning = false
|
||||
return next()
|
||||
}
|
||||
nextIsRunning = false
|
||||
}
|
||||
next()
|
||||
})
|
||||
}
|
||||
119
@vates/async-each/index.test.js
Normal file
119
@vates/async-each/index.test.js
Normal file
@@ -0,0 +1,119 @@
|
||||
'use strict'
|
||||
|
||||
const { describe, it, beforeEach } = require('test')
|
||||
const assert = require('assert').strict
|
||||
const { spy } = require('sinon')
|
||||
|
||||
const { asyncEach } = require('./')
|
||||
|
||||
const randomDelay = (max = 10) =>
|
||||
new Promise(resolve => {
|
||||
setTimeout(resolve, Math.floor(Math.random() * max + 1))
|
||||
})
|
||||
|
||||
const rejectionOf = p =>
|
||||
new Promise((resolve, reject) => {
|
||||
p.then(reject, resolve)
|
||||
})
|
||||
|
||||
describe('asyncEach', () => {
|
||||
const thisArg = 'qux'
|
||||
const values = ['foo', 'bar', 'baz']
|
||||
|
||||
Object.entries({
|
||||
'sync iterable': () => values,
|
||||
'async iterable': async function* () {
|
||||
for (const value of values) {
|
||||
await randomDelay()
|
||||
yield value
|
||||
}
|
||||
},
|
||||
}).forEach(([what, getIterable]) =>
|
||||
describe('with ' + what, () => {
|
||||
let iterable
|
||||
beforeEach(() => {
|
||||
iterable = getIterable()
|
||||
})
|
||||
|
||||
it('works', async () => {
|
||||
const iteratee = spy(async () => {})
|
||||
|
||||
await asyncEach.call(thisArg, iterable, iteratee, { concurrency: 1 })
|
||||
|
||||
assert.deepStrictEqual(
|
||||
iteratee.thisValues,
|
||||
Array.from(values, () => thisArg)
|
||||
)
|
||||
assert.deepStrictEqual(
|
||||
iteratee.args,
|
||||
Array.from(values, (value, index) => [value, index, iterable])
|
||||
)
|
||||
})
|
||||
;[1, 2, 4].forEach(concurrency => {
|
||||
it('respects a concurrency of ' + concurrency, async () => {
|
||||
let running = 0
|
||||
|
||||
await asyncEach(
|
||||
values,
|
||||
async () => {
|
||||
++running
|
||||
assert.deepStrictEqual(running <= concurrency, true)
|
||||
await randomDelay()
|
||||
--running
|
||||
},
|
||||
{ concurrency }
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('stops on first error when stopOnError is true', async () => {
|
||||
const tracker = new assert.CallTracker()
|
||||
|
||||
const error = new Error()
|
||||
const iteratee = tracker.calls((_, i) => {
|
||||
if (i === 1) {
|
||||
throw error
|
||||
}
|
||||
}, 2)
|
||||
assert.deepStrictEqual(
|
||||
await rejectionOf(asyncEach(iterable, iteratee, { concurrency: 1, stopOnError: true })),
|
||||
error
|
||||
)
|
||||
|
||||
tracker.verify()
|
||||
})
|
||||
|
||||
it('rejects AggregateError when stopOnError is false', async () => {
|
||||
const errors = []
|
||||
const iteratee = spy(() => {
|
||||
const error = new Error()
|
||||
errors.push(error)
|
||||
throw error
|
||||
})
|
||||
|
||||
const error = await rejectionOf(asyncEach(iterable, iteratee, { stopOnError: false }))
|
||||
assert.deepStrictEqual(error.errors, errors)
|
||||
assert.deepStrictEqual(
|
||||
iteratee.args,
|
||||
Array.from(values, (value, index) => [value, index, iterable])
|
||||
)
|
||||
})
|
||||
|
||||
it('can be interrupted with an AbortSignal', async () => {
|
||||
const tracker = new assert.CallTracker()
|
||||
|
||||
const ac = new AbortController()
|
||||
const iteratee = tracker.calls((_, i) => {
|
||||
if (i === 1) {
|
||||
ac.abort()
|
||||
}
|
||||
}, 2)
|
||||
await assert.rejects(asyncEach(iterable, iteratee, { concurrency: 1, signal: ac.signal }), {
|
||||
message: 'asyncEach aborted',
|
||||
})
|
||||
|
||||
tracker.verify()
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
40
@vates/async-each/package.json
Normal file
40
@vates/async-each/package.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "@vates/async-each",
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/async-each",
|
||||
"description": "Run async fn for each item in (async) iterable",
|
||||
"keywords": [
|
||||
"array",
|
||||
"async",
|
||||
"collection",
|
||||
"each",
|
||||
"for",
|
||||
"foreach",
|
||||
"iterable",
|
||||
"iterator"
|
||||
],
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"repository": {
|
||||
"directory": "@vates/async-each",
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Vates SAS",
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"license": "ISC",
|
||||
"version": "1.0.0",
|
||||
"engines": {
|
||||
"node": ">=8.10"
|
||||
},
|
||||
"scripts": {
|
||||
"postversion": "npm publish --access public",
|
||||
"test": "node--test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"sinon": "^15.0.1",
|
||||
"tap": "^16.3.0",
|
||||
"test": "^3.2.1"
|
||||
}
|
||||
}
|
||||
30
@vates/cached-dns.lookup/.USAGE.md
Normal file
30
@vates/cached-dns.lookup/.USAGE.md
Normal file
@@ -0,0 +1,30 @@
|
||||
Node does not cache queries to `dns.lookup`, which can lead application doing a lot of connections to have perf issues and to saturate Node threads pool.
|
||||
|
||||
This library attempts to mitigate these problems by providing a version of this function with a version short cache, applied on both errors and results.
|
||||
|
||||
> Limitation: `verbatim: false` option is not supported.
|
||||
|
||||
It has exactly the same API as the native method and can be used directly:
|
||||
|
||||
```js
|
||||
import { createCachedLookup } from '@vates/cached-dns.lookup'
|
||||
|
||||
const lookup = createCachedLookup()
|
||||
|
||||
lookup('example.net', { all: true, family: 0 }, (error, result) => {
|
||||
if (error != null) {
|
||||
return console.warn(error)
|
||||
}
|
||||
console.log(result)
|
||||
})
|
||||
```
|
||||
|
||||
Or it can be used to replace the native implementation and speed up the whole app:
|
||||
|
||||
```js
|
||||
// assign our cached implementation to dns.lookup
|
||||
const restore = createCachedLookup().patchGlobal()
|
||||
|
||||
// to restore the previous implementation
|
||||
restore()
|
||||
```
|
||||
1
@vates/cached-dns.lookup/.npmignore
Symbolic link
1
@vates/cached-dns.lookup/.npmignore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../scripts/npmignore
|
||||
63
@vates/cached-dns.lookup/README.md
Normal file
63
@vates/cached-dns.lookup/README.md
Normal file
@@ -0,0 +1,63 @@
|
||||
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
|
||||
|
||||
# @vates/cached-dns.lookup
|
||||
|
||||
[](https://npmjs.org/package/@vates/cached-dns.lookup)  [](https://bundlephobia.com/result?p=@vates/cached-dns.lookup) [](https://npmjs.org/package/@vates/cached-dns.lookup)
|
||||
|
||||
> Cached implementation of dns.lookup
|
||||
|
||||
## Install
|
||||
|
||||
Installation of the [npm package](https://npmjs.org/package/@vates/cached-dns.lookup):
|
||||
|
||||
```
|
||||
> npm install --save @vates/cached-dns.lookup
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Node does not cache queries to `dns.lookup`, which can lead application doing a lot of connections to have perf issues and to saturate Node threads pool.
|
||||
|
||||
This library attempts to mitigate these problems by providing a version of this function with a version short cache, applied on both errors and results.
|
||||
|
||||
> Limitation: `verbatim: false` option is not supported.
|
||||
|
||||
It has exactly the same API as the native method and can be used directly:
|
||||
|
||||
```js
|
||||
import { createCachedLookup } from '@vates/cached-dns.lookup'
|
||||
|
||||
const lookup = createCachedLookup()
|
||||
|
||||
lookup('example.net', { all: true, family: 0 }, (error, result) => {
|
||||
if (error != null) {
|
||||
return console.warn(error)
|
||||
}
|
||||
console.log(result)
|
||||
})
|
||||
```
|
||||
|
||||
Or it can be used to replace the native implementation and speed up the whole app:
|
||||
|
||||
```js
|
||||
// assign our cached implementation to dns.lookup
|
||||
const restore = createCachedLookup().patchGlobal()
|
||||
|
||||
// to restore the previous implementation
|
||||
restore()
|
||||
```
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions are _very_ welcomed, either on the documentation or on
|
||||
the code.
|
||||
|
||||
You may:
|
||||
|
||||
- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
|
||||
you've encountered;
|
||||
- fork and create a pull request.
|
||||
|
||||
## License
|
||||
|
||||
[ISC](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)
|
||||
72
@vates/cached-dns.lookup/index.js
Normal file
72
@vates/cached-dns.lookup/index.js
Normal file
@@ -0,0 +1,72 @@
|
||||
'use strict'
|
||||
|
||||
const assert = require('assert')
|
||||
const dns = require('dns')
|
||||
const LRU = require('lru-cache')
|
||||
|
||||
function reportResults(all, results, callback) {
|
||||
if (all) {
|
||||
callback(null, results)
|
||||
} else {
|
||||
const first = results[0]
|
||||
callback(null, first.address, first.family)
|
||||
}
|
||||
}
|
||||
|
||||
exports.createCachedLookup = function createCachedLookup({ lookup = dns.lookup } = {}) {
|
||||
const cache = new LRU({
|
||||
max: 500,
|
||||
|
||||
// 1 minute: long enough to be effective, short enough so there is no need to bother with DNS TTLs
|
||||
ttl: 60e3,
|
||||
})
|
||||
|
||||
function cachedLookup(hostname, options, callback) {
|
||||
let all = false
|
||||
let family = 0
|
||||
if (typeof options === 'function') {
|
||||
callback = options
|
||||
} else if (typeof options === 'number') {
|
||||
family = options
|
||||
} else if (options != null) {
|
||||
assert.notStrictEqual(options.verbatim, false, 'not supported by this implementation')
|
||||
;({ all = all, family = family } = options)
|
||||
}
|
||||
|
||||
// cache by family option because there will be an error if there is no
|
||||
// entries for the requestion family so we cannot easily cache all families
|
||||
// and filter on reporting back
|
||||
const key = hostname + '/' + family
|
||||
|
||||
const result = cache.get(key)
|
||||
if (result !== undefined) {
|
||||
setImmediate(reportResults, all, result, callback)
|
||||
} else {
|
||||
lookup(hostname, { all: true, family, verbatim: true }, function onLookup(error, results) {
|
||||
// errors are not cached because this will delay recovery after DNS/network issues
|
||||
//
|
||||
// there are no reliable way to detect if the error is real or simply
|
||||
// that there are no results for the requested hostname
|
||||
//
|
||||
// there should be much fewer errors than success, therefore it should
|
||||
// not be a big deal to not cache them
|
||||
if (error != null) {
|
||||
return callback(error)
|
||||
}
|
||||
|
||||
cache.set(key, results)
|
||||
reportResults(all, results, callback)
|
||||
})
|
||||
}
|
||||
}
|
||||
cachedLookup.patchGlobal = function patchGlobal() {
|
||||
const previous = dns.lookup
|
||||
dns.lookup = cachedLookup
|
||||
return function restoreGlobal() {
|
||||
assert.strictEqual(dns.lookup, cachedLookup)
|
||||
dns.lookup = previous
|
||||
}
|
||||
}
|
||||
|
||||
return cachedLookup
|
||||
}
|
||||
32
@vates/cached-dns.lookup/package.json
Normal file
32
@vates/cached-dns.lookup/package.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"dependencies": {
|
||||
"lru-cache": "^7.0.4"
|
||||
},
|
||||
"private": false,
|
||||
"name": "@vates/cached-dns.lookup",
|
||||
"description": "Cached implementation of dns.lookup",
|
||||
"keywords": [
|
||||
"cache",
|
||||
"dns",
|
||||
"lookup"
|
||||
],
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/cached-dns.lookup",
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"repository": {
|
||||
"directory": "@vates/cached-dns.lookup",
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Vates SAS",
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"license": "ISC",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"postversion": "npm publish --access public"
|
||||
}
|
||||
}
|
||||
13
@vates/coalesce-calls/.USAGE.md
Normal file
13
@vates/coalesce-calls/.USAGE.md
Normal file
@@ -0,0 +1,13 @@
|
||||
```js
|
||||
import { coalesceCalls } from '@vates/coalesce-calls'
|
||||
|
||||
const connect = coalesceCalls(async function () {
|
||||
// async operation
|
||||
})
|
||||
|
||||
connect()
|
||||
|
||||
// the previous promise result will be returned if the operation is not
|
||||
// complete yet
|
||||
connect()
|
||||
```
|
||||
1
@vates/coalesce-calls/.npmignore
Symbolic link
1
@vates/coalesce-calls/.npmignore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../scripts/npmignore
|
||||
46
@vates/coalesce-calls/README.md
Normal file
46
@vates/coalesce-calls/README.md
Normal file
@@ -0,0 +1,46 @@
|
||||
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
|
||||
|
||||
# @vates/coalesce-calls
|
||||
|
||||
[](https://npmjs.org/package/@vates/coalesce-calls)  [](https://bundlephobia.com/result?p=@vates/coalesce-calls) [](https://npmjs.org/package/@vates/coalesce-calls)
|
||||
|
||||
> Wraps an async function so that concurrent calls will be coalesced
|
||||
|
||||
## Install
|
||||
|
||||
Installation of the [npm package](https://npmjs.org/package/@vates/coalesce-calls):
|
||||
|
||||
```
|
||||
> npm install --save @vates/coalesce-calls
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import { coalesceCalls } from '@vates/coalesce-calls'
|
||||
|
||||
const connect = coalesceCalls(async function () {
|
||||
// async operation
|
||||
})
|
||||
|
||||
connect()
|
||||
|
||||
// the previous promise result will be returned if the operation is not
|
||||
// complete yet
|
||||
connect()
|
||||
```
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions are _very_ welcomed, either on the documentation or on
|
||||
the code.
|
||||
|
||||
You may:
|
||||
|
||||
- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
|
||||
you've encountered;
|
||||
- fork and create a pull request.
|
||||
|
||||
## License
|
||||
|
||||
[ISC](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)
|
||||
16
@vates/coalesce-calls/index.js
Normal file
16
@vates/coalesce-calls/index.js
Normal file
@@ -0,0 +1,16 @@
|
||||
'use strict'
|
||||
|
||||
exports.coalesceCalls = function (fn) {
|
||||
let promise
|
||||
const clean = () => {
|
||||
promise = undefined
|
||||
}
|
||||
return function () {
|
||||
if (promise !== undefined) {
|
||||
return promise
|
||||
}
|
||||
promise = fn.apply(this, arguments)
|
||||
promise.then(clean, clean)
|
||||
return promise
|
||||
}
|
||||
}
|
||||
36
@vates/coalesce-calls/index.test.js
Normal file
36
@vates/coalesce-calls/index.test.js
Normal file
@@ -0,0 +1,36 @@
|
||||
'use strict'
|
||||
|
||||
const { describe, it } = require('test')
|
||||
const assert = require('assert')
|
||||
|
||||
const { coalesceCalls } = require('./')
|
||||
|
||||
const pDefer = () => {
|
||||
const r = {}
|
||||
r.promise = new Promise((resolve, reject) => {
|
||||
r.reject = reject
|
||||
r.resolve = resolve
|
||||
})
|
||||
return r
|
||||
}
|
||||
|
||||
describe('coalesceCalls', () => {
|
||||
it('decorates an async function', async () => {
|
||||
const fn = coalesceCalls(promise => promise)
|
||||
|
||||
const defer1 = pDefer()
|
||||
const promise1 = fn(defer1.promise)
|
||||
const defer2 = pDefer()
|
||||
const promise2 = fn(defer2.promise)
|
||||
|
||||
defer1.resolve('foo')
|
||||
assert.strictEqual(await promise1, 'foo')
|
||||
assert.strictEqual(await promise2, 'foo')
|
||||
|
||||
const defer3 = pDefer()
|
||||
const promise3 = fn(defer3.promise)
|
||||
|
||||
defer3.resolve('bar')
|
||||
assert.strictEqual(await promise3, 'bar')
|
||||
})
|
||||
})
|
||||
39
@vates/coalesce-calls/package.json
Normal file
39
@vates/coalesce-calls/package.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "@vates/coalesce-calls",
|
||||
"description": "Wraps an async function so that concurrent calls will be coalesced",
|
||||
"keywords": [
|
||||
"async",
|
||||
"calls",
|
||||
"coalesce",
|
||||
"decorate",
|
||||
"decorator",
|
||||
"merge",
|
||||
"promise",
|
||||
"wrap",
|
||||
"wrapper"
|
||||
],
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/coalesce-calls",
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"repository": {
|
||||
"directory": "@vates/coalesce-calls",
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Vates SAS",
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"license": "ISC",
|
||||
"version": "0.1.0",
|
||||
"engines": {
|
||||
"node": ">=8.10"
|
||||
},
|
||||
"scripts": {
|
||||
"postversion": "npm publish --access public",
|
||||
"test": "node--test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"test": "^3.2.1"
|
||||
}
|
||||
}
|
||||
65
@vates/compose/.USAGE.md
Normal file
65
@vates/compose/.USAGE.md
Normal file
@@ -0,0 +1,65 @@
|
||||
```js
|
||||
import { compose } from '@vates/compose'
|
||||
|
||||
const add2 = x => x + 2
|
||||
const mul3 = x => x * 3
|
||||
|
||||
// const f = x => mul3(add2(x))
|
||||
const f = compose(add2, mul3)
|
||||
|
||||
console.log(f(5))
|
||||
// → 21
|
||||
```
|
||||
|
||||
> The call context (`this`) of the composed function is forwarded to all functions.
|
||||
|
||||
The first function is called with all arguments of the composed function:
|
||||
|
||||
```js
|
||||
const add = (x, y) => x + y
|
||||
const mul3 = x => x * 3
|
||||
|
||||
// const f = (x, y) => mul3(add(x, y))
|
||||
const f = compose(add, mul3)
|
||||
|
||||
console.log(f(4, 5))
|
||||
// → 27
|
||||
```
|
||||
|
||||
Functions may also be passed in an array:
|
||||
|
||||
```js
|
||||
const f = compose([add2, mul3])
|
||||
```
|
||||
|
||||
Options can be passed as first parameter:
|
||||
|
||||
```js
|
||||
const f = compose(
|
||||
{
|
||||
// compose async functions
|
||||
async: true,
|
||||
|
||||
// compose from right to left
|
||||
right: true,
|
||||
},
|
||||
[add2, mul3]
|
||||
)
|
||||
```
|
||||
|
||||
Functions can receive extra parameters:
|
||||
|
||||
```js
|
||||
const isIn = (value, min, max) => min <= value && value <= max
|
||||
|
||||
// Only compatible when `fns` is passed as an array!
|
||||
const f = compose([
|
||||
[add, 2],
|
||||
[isIn, 3, 10],
|
||||
])
|
||||
|
||||
console.log(f(1))
|
||||
// → true
|
||||
```
|
||||
|
||||
> Note: if the first function is defined with extra parameters, it will only receive the first value passed to the composed function, instead of all the parameters.
|
||||
1
@vates/compose/.npmignore
Symbolic link
1
@vates/compose/.npmignore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../scripts/npmignore
|
||||
98
@vates/compose/README.md
Normal file
98
@vates/compose/README.md
Normal file
@@ -0,0 +1,98 @@
|
||||
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
|
||||
|
||||
# @vates/compose
|
||||
|
||||
[](https://npmjs.org/package/@vates/compose)  [](https://bundlephobia.com/result?p=@vates/compose) [](https://npmjs.org/package/@vates/compose)
|
||||
|
||||
> Compose functions from left to right
|
||||
|
||||
## Install
|
||||
|
||||
Installation of the [npm package](https://npmjs.org/package/@vates/compose):
|
||||
|
||||
```
|
||||
> npm install --save @vates/compose
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import { compose } from '@vates/compose'
|
||||
|
||||
const add2 = x => x + 2
|
||||
const mul3 = x => x * 3
|
||||
|
||||
// const f = x => mul3(add2(x))
|
||||
const f = compose(add2, mul3)
|
||||
|
||||
console.log(f(5))
|
||||
// → 21
|
||||
```
|
||||
|
||||
> The call context (`this`) of the composed function is forwarded to all functions.
|
||||
|
||||
The first function is called with all arguments of the composed function:
|
||||
|
||||
```js
|
||||
const add = (x, y) => x + y
|
||||
const mul3 = x => x * 3
|
||||
|
||||
// const f = (x, y) => mul3(add(x, y))
|
||||
const f = compose(add, mul3)
|
||||
|
||||
console.log(f(4, 5))
|
||||
// → 27
|
||||
```
|
||||
|
||||
Functions may also be passed in an array:
|
||||
|
||||
```js
|
||||
const f = compose([add2, mul3])
|
||||
```
|
||||
|
||||
Options can be passed as first parameter:
|
||||
|
||||
```js
|
||||
const f = compose(
|
||||
{
|
||||
// compose async functions
|
||||
async: true,
|
||||
|
||||
// compose from right to left
|
||||
right: true,
|
||||
},
|
||||
[add2, mul3]
|
||||
)
|
||||
```
|
||||
|
||||
Functions can receive extra parameters:
|
||||
|
||||
```js
|
||||
const isIn = (value, min, max) => min <= value && value <= max
|
||||
|
||||
// Only compatible when `fns` is passed as an array!
|
||||
const f = compose([
|
||||
[add, 2],
|
||||
[isIn, 3, 10],
|
||||
])
|
||||
|
||||
console.log(f(1))
|
||||
// → true
|
||||
```
|
||||
|
||||
> Note: if the first function is defined with extra parameters, it will only receive the first value passed to the composed function, instead of all the parameters.
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions are _very_ welcomed, either on the documentation or on
|
||||
the code.
|
||||
|
||||
You may:
|
||||
|
||||
- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
|
||||
you've encountered;
|
||||
- fork and create a pull request.
|
||||
|
||||
## License
|
||||
|
||||
[ISC](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)
|
||||
66
@vates/compose/index.js
Normal file
66
@vates/compose/index.js
Normal file
@@ -0,0 +1,66 @@
|
||||
'use strict'
|
||||
|
||||
const defaultOpts = { async: false, right: false }
|
||||
|
||||
exports.compose = function compose(opts, fns) {
|
||||
if (Array.isArray(opts)) {
|
||||
fns = opts.slice() // don't mutate passed array
|
||||
opts = defaultOpts
|
||||
} else if (typeof opts === 'object') {
|
||||
opts = Object.assign({}, defaultOpts, opts)
|
||||
if (Array.isArray(fns)) {
|
||||
fns = fns.slice() // don't mutate passed array
|
||||
} else {
|
||||
fns = Array.prototype.slice.call(arguments, 1)
|
||||
}
|
||||
} else {
|
||||
fns = Array.from(arguments)
|
||||
opts = defaultOpts
|
||||
}
|
||||
|
||||
const n = fns.length
|
||||
if (n === 0) {
|
||||
throw new TypeError('at least one function must be passed')
|
||||
}
|
||||
|
||||
for (let i = 0; i < n; ++i) {
|
||||
const entry = fns[i]
|
||||
if (Array.isArray(entry)) {
|
||||
const fn = entry[0]
|
||||
const args = entry.slice()
|
||||
args[0] = undefined
|
||||
fns[i] = function composeWithArgs(value) {
|
||||
args[0] = value
|
||||
try {
|
||||
return fn.apply(this, args)
|
||||
} finally {
|
||||
args[0] = undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (n === 1) {
|
||||
return fns[0]
|
||||
}
|
||||
|
||||
if (opts.right) {
|
||||
fns.reverse()
|
||||
}
|
||||
|
||||
return opts.async
|
||||
? async function () {
|
||||
let value = await fns[0].apply(this, arguments)
|
||||
for (let i = 1; i < n; ++i) {
|
||||
value = await fns[i].call(this, value)
|
||||
}
|
||||
return value
|
||||
}
|
||||
: function () {
|
||||
let value = fns[0].apply(this, arguments)
|
||||
for (let i = 1; i < n; ++i) {
|
||||
value = fns[i].call(this, value)
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
66
@vates/compose/index.test.js
Normal file
66
@vates/compose/index.test.js
Normal file
@@ -0,0 +1,66 @@
|
||||
'use strict'
|
||||
|
||||
const { describe, it } = require('test')
|
||||
const assert = require('node:assert').strict
|
||||
|
||||
const { compose } = require('./')
|
||||
|
||||
const add2 = x => x + 2
|
||||
const mul3 = x => x * 3
|
||||
|
||||
describe('compose()', () => {
|
||||
it('throws when no functions is passed', () => {
|
||||
assert.throws(() => compose(), TypeError)
|
||||
assert.throws(() => compose([]), TypeError)
|
||||
})
|
||||
|
||||
it('applies from left to right', () => {
|
||||
assert.strictEqual(compose(add2, mul3)(5), 21)
|
||||
})
|
||||
|
||||
it('accepts functions in an array', () => {
|
||||
assert.strictEqual(compose([add2, mul3])(5), 21)
|
||||
})
|
||||
|
||||
it('can apply from right to left', () => {
|
||||
assert.strictEqual(compose({ right: true }, add2, mul3)(5), 17)
|
||||
})
|
||||
|
||||
it('accepts options with functions in an array', () => {
|
||||
assert.strictEqual(compose({ right: true }, [add2, mul3])(5), 17)
|
||||
})
|
||||
|
||||
it('can compose async functions', async () => {
|
||||
assert.strictEqual(
|
||||
await compose(
|
||||
{ async: true },
|
||||
async x => x + 2,
|
||||
async x => x * 3
|
||||
)(5),
|
||||
21
|
||||
)
|
||||
})
|
||||
|
||||
it('forwards all args to first function', () => {
|
||||
const expectedArgs = [Math.random(), Math.random()]
|
||||
compose(
|
||||
(...args) => {
|
||||
assert.deepEqual(args, expectedArgs)
|
||||
},
|
||||
// add a second function to avoid the one function special case
|
||||
Function.prototype
|
||||
)(...expectedArgs)
|
||||
})
|
||||
|
||||
it('forwards context to all functions', () => {
|
||||
const expectedThis = {}
|
||||
compose(
|
||||
function () {
|
||||
assert.strictEqual(this, expectedThis)
|
||||
},
|
||||
function () {
|
||||
assert.strictEqual(this, expectedThis)
|
||||
}
|
||||
).call(expectedThis)
|
||||
})
|
||||
})
|
||||
28
@vates/compose/package.json
Normal file
28
@vates/compose/package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "@vates/compose",
|
||||
"description": "Compose functions from left to right",
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/compose",
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"repository": {
|
||||
"directory": "@vates/compose",
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Vates SAS",
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"license": "ISC",
|
||||
"version": "2.1.0",
|
||||
"engines": {
|
||||
"node": ">=7.6"
|
||||
},
|
||||
"scripts": {
|
||||
"postversion": "npm publish --access public",
|
||||
"test": "node--test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"test": "^3.2.1"
|
||||
}
|
||||
}
|
||||
86
@vates/decorate-with/.USAGE.md
Normal file
86
@vates/decorate-with/.USAGE.md
Normal file
@@ -0,0 +1,86 @@
|
||||
### `decorateWith(fn, ...args)`
|
||||
|
||||
Creates a new ([legacy](https://babeljs.io/docs/en/babel-plugin-syntax-decorators#legacy)) method decorator from a function decorator, for instance, allows using Lodash's functions as decorators:
|
||||
|
||||
```js
|
||||
import { decorateWith } from '@vates/decorate-with'
|
||||
|
||||
class Foo {
|
||||
@decorateWith(lodash.debounce, 150)
|
||||
bar() {
|
||||
// body
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `decorateClass(class, map)`
|
||||
|
||||
Decorates a number of accessors and methods directly, without using the decorator syntax:
|
||||
|
||||
```js
|
||||
import { decorateClass } from '@vates/decorate-with'
|
||||
|
||||
class Foo {
|
||||
get bar() {
|
||||
// body
|
||||
}
|
||||
|
||||
set bar(value) {
|
||||
// body
|
||||
}
|
||||
|
||||
baz() {
|
||||
// body
|
||||
}
|
||||
}
|
||||
|
||||
decorateClass(Foo, {
|
||||
// getter and/or setter
|
||||
bar: {
|
||||
// without arguments
|
||||
get: lodash.memoize,
|
||||
|
||||
// with arguments
|
||||
set: [lodash.debounce, 150],
|
||||
},
|
||||
|
||||
// method (with or without arguments)
|
||||
baz: lodash.curry,
|
||||
})
|
||||
```
|
||||
|
||||
The decorated class is returned, so you can export it directly.
|
||||
|
||||
To apply multiple transforms to an accessor/method, you can either call `decorateClass` multiple times or use [`@vates/compose`](https://www.npmjs.com/package/@vates/compose):
|
||||
|
||||
```js
|
||||
decorateClass(Foo, {
|
||||
baz: compose([
|
||||
[lodash.debounce, 150]
|
||||
lodash.curry,
|
||||
])
|
||||
})
|
||||
```
|
||||
|
||||
### `perInstance(fn, ...args)`
|
||||
|
||||
Helper to decorate the method by instance instead of for the whole class.
|
||||
|
||||
This is often necessary for caching or deduplicating calls.
|
||||
|
||||
```js
|
||||
import { perInstance } from '@vates/decorateWith'
|
||||
|
||||
class Foo {
|
||||
@decorateWith(perInstance, lodash.memoize)
|
||||
bar() {
|
||||
// body
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Because it's a normal function, it can also be used with `decorateClass`, with `compose` or even by itself.
|
||||
|
||||
### `decorateMethodsWith(class, map)`
|
||||
|
||||
> Deprecated alias for [`decorateClass(class, map)`](#decorateclassclass-map).
|
||||
1
@vates/decorate-with/.npmignore
Symbolic link
1
@vates/decorate-with/.npmignore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../scripts/npmignore
|
||||
119
@vates/decorate-with/README.md
Normal file
119
@vates/decorate-with/README.md
Normal file
@@ -0,0 +1,119 @@
|
||||
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
|
||||
|
||||
# @vates/decorate-with
|
||||
|
||||
[](https://npmjs.org/package/@vates/decorate-with)  [](https://bundlephobia.com/result?p=@vates/decorate-with) [](https://npmjs.org/package/@vates/decorate-with)
|
||||
|
||||
> Creates a decorator from a function wrapper
|
||||
|
||||
## Install
|
||||
|
||||
Installation of the [npm package](https://npmjs.org/package/@vates/decorate-with):
|
||||
|
||||
```
|
||||
> npm install --save @vates/decorate-with
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### `decorateWith(fn, ...args)`
|
||||
|
||||
Creates a new ([legacy](https://babeljs.io/docs/en/babel-plugin-syntax-decorators#legacy)) method decorator from a function decorator, for instance, allows using Lodash's functions as decorators:
|
||||
|
||||
```js
|
||||
import { decorateWith } from '@vates/decorate-with'
|
||||
|
||||
class Foo {
|
||||
@decorateWith(lodash.debounce, 150)
|
||||
bar() {
|
||||
// body
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `decorateClass(class, map)`
|
||||
|
||||
Decorates a number of accessors and methods directly, without using the decorator syntax:
|
||||
|
||||
```js
|
||||
import { decorateClass } from '@vates/decorate-with'
|
||||
|
||||
class Foo {
|
||||
get bar() {
|
||||
// body
|
||||
}
|
||||
|
||||
set bar(value) {
|
||||
// body
|
||||
}
|
||||
|
||||
baz() {
|
||||
// body
|
||||
}
|
||||
}
|
||||
|
||||
decorateClass(Foo, {
|
||||
// getter and/or setter
|
||||
bar: {
|
||||
// without arguments
|
||||
get: lodash.memoize,
|
||||
|
||||
// with arguments
|
||||
set: [lodash.debounce, 150],
|
||||
},
|
||||
|
||||
// method (with or without arguments)
|
||||
baz: lodash.curry,
|
||||
})
|
||||
```
|
||||
|
||||
The decorated class is returned, so you can export it directly.
|
||||
|
||||
To apply multiple transforms to an accessor/method, you can either call `decorateClass` multiple times or use [`@vates/compose`](https://www.npmjs.com/package/@vates/compose):
|
||||
|
||||
```js
|
||||
decorateClass(Foo, {
|
||||
baz: compose([
|
||||
[lodash.debounce, 150]
|
||||
lodash.curry,
|
||||
])
|
||||
})
|
||||
```
|
||||
|
||||
### `perInstance(fn, ...args)`
|
||||
|
||||
Helper to decorate the method by instance instead of for the whole class.
|
||||
|
||||
This is often necessary for caching or deduplicating calls.
|
||||
|
||||
```js
|
||||
import { perInstance } from '@vates/decorateWith'
|
||||
|
||||
class Foo {
|
||||
@decorateWith(perInstance, lodash.memoize)
|
||||
bar() {
|
||||
// body
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Because it's a normal function, it can also be used with `decorateClass`, with `compose` or even by itself.
|
||||
|
||||
### `decorateMethodsWith(class, map)`
|
||||
|
||||
> Deprecated alias for [`decorateClass(class, map)`](#decorateclassclass-map).
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions are _very_ welcomed, either on the documentation or on
|
||||
the code.
|
||||
|
||||
You may:
|
||||
|
||||
- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
|
||||
you've encountered;
|
||||
- fork and create a pull request.
|
||||
|
||||
## License
|
||||
|
||||
[ISC](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)
|
||||
48
@vates/decorate-with/index.js
Normal file
48
@vates/decorate-with/index.js
Normal file
@@ -0,0 +1,48 @@
|
||||
'use strict'
|
||||
|
||||
exports.decorateWith = function decorateWith(fn, ...args) {
|
||||
return (target, name, descriptor) => ({
|
||||
...descriptor,
|
||||
value: fn(descriptor.value, ...args),
|
||||
})
|
||||
}
|
||||
|
||||
const { getOwnPropertyDescriptor, defineProperty } = Object
|
||||
|
||||
function applyDecorator(decorator, value) {
|
||||
return typeof decorator === 'function' ? decorator(value) : decorator[0](value, ...decorator.slice(1))
|
||||
}
|
||||
|
||||
exports.decorateClass = exports.decorateMethodsWith = function decorateClass(klass, map) {
|
||||
const { prototype } = klass
|
||||
for (const name of Object.keys(map)) {
|
||||
const decorator = map[name]
|
||||
const descriptor = getOwnPropertyDescriptor(prototype, name)
|
||||
if (typeof decorator === 'function' || Array.isArray(decorator)) {
|
||||
descriptor.value = applyDecorator(decorator, descriptor.value)
|
||||
} else {
|
||||
const { get, set } = decorator
|
||||
if (get !== undefined) {
|
||||
descriptor.get = applyDecorator(get, descriptor.get)
|
||||
}
|
||||
if (set !== undefined) {
|
||||
descriptor.set = applyDecorator(set, descriptor.set)
|
||||
}
|
||||
}
|
||||
|
||||
defineProperty(prototype, name, descriptor)
|
||||
}
|
||||
return klass
|
||||
}
|
||||
|
||||
exports.perInstance = function perInstance(fn, decorator, ...args) {
|
||||
const map = new WeakMap()
|
||||
return function () {
|
||||
let decorated = map.get(this)
|
||||
if (decorated === undefined) {
|
||||
decorated = decorator(fn, ...args)
|
||||
map.set(this, decorated)
|
||||
}
|
||||
return decorated.apply(this, arguments)
|
||||
}
|
||||
}
|
||||
152
@vates/decorate-with/index.test.js
Normal file
152
@vates/decorate-with/index.test.js
Normal file
@@ -0,0 +1,152 @@
|
||||
'use strict'
|
||||
|
||||
const assert = require('assert')
|
||||
const { describe, it } = require('test')
|
||||
|
||||
const { decorateClass, decorateWith, decorateMethodsWith, perInstance } = require('./')
|
||||
|
||||
const identity = _ => _
|
||||
|
||||
describe('decorateWith', () => {
|
||||
it('works', () => {
|
||||
const expectedArgs = [Math.random(), Math.random()]
|
||||
const expectedFn = Function.prototype
|
||||
const newFn = () => {}
|
||||
|
||||
const decorator = decorateWith(function wrapper(fn, ...args) {
|
||||
assert.deepStrictEqual(fn, expectedFn)
|
||||
assert.deepStrictEqual(args, expectedArgs)
|
||||
|
||||
return newFn
|
||||
}, ...expectedArgs)
|
||||
|
||||
const descriptor = {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: expectedFn,
|
||||
writable: true,
|
||||
}
|
||||
assert.deepStrictEqual(decorator({}, 'foo', descriptor), {
|
||||
...descriptor,
|
||||
value: newFn,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('decorateClass', () => {
|
||||
it('works', () => {
|
||||
class C {
|
||||
foo() {}
|
||||
bar() {}
|
||||
get baz() {}
|
||||
// eslint-disable-next-line accessor-pairs
|
||||
set qux(_) {}
|
||||
}
|
||||
|
||||
const expectedArgs = [Math.random(), Math.random()]
|
||||
const P = C.prototype
|
||||
|
||||
const descriptors = Object.getOwnPropertyDescriptors(P)
|
||||
|
||||
const newFoo = () => {}
|
||||
const newBar = () => {}
|
||||
const newGetBaz = () => {}
|
||||
const newSetQux = _ => {}
|
||||
|
||||
decorateClass(C, {
|
||||
foo(fn) {
|
||||
assert.strictEqual(arguments.length, 1)
|
||||
assert.strictEqual(fn, P.foo)
|
||||
return newFoo
|
||||
},
|
||||
bar: [
|
||||
function (fn, ...args) {
|
||||
assert.strictEqual(fn, P.bar)
|
||||
assert.deepStrictEqual(args, expectedArgs)
|
||||
return newBar
|
||||
},
|
||||
...expectedArgs,
|
||||
],
|
||||
baz: {
|
||||
get(fn) {
|
||||
assert.strictEqual(arguments.length, 1)
|
||||
assert.strictEqual(fn, descriptors.baz.get)
|
||||
return newGetBaz
|
||||
},
|
||||
},
|
||||
qux: {
|
||||
set: [
|
||||
function (fn, ...args) {
|
||||
assert.strictEqual(fn, descriptors.qux.set)
|
||||
assert.deepStrictEqual(args, expectedArgs)
|
||||
return newSetQux
|
||||
},
|
||||
...expectedArgs,
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
const newDescriptors = Object.getOwnPropertyDescriptors(P)
|
||||
assert.deepStrictEqual(newDescriptors.foo, { ...descriptors.foo, value: newFoo })
|
||||
assert.deepStrictEqual(newDescriptors.bar, { ...descriptors.bar, value: newBar })
|
||||
assert.deepStrictEqual(newDescriptors.baz, { ...descriptors.baz, get: newGetBaz })
|
||||
assert.deepStrictEqual(newDescriptors.qux, { ...descriptors.qux, set: newSetQux })
|
||||
})
|
||||
|
||||
it('throws if using an accessor decorator for a method', function () {
|
||||
assert.throws(() =>
|
||||
decorateClass(
|
||||
class {
|
||||
foo() {}
|
||||
},
|
||||
{ foo: { get: identity, set: identity } }
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
it('throws if using a method decorator for an accessor', function () {
|
||||
assert.throws(() =>
|
||||
decorateClass(
|
||||
class {
|
||||
get foo() {}
|
||||
},
|
||||
{ foo: identity }
|
||||
)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('decorateMethodsWith is an alias of decorateClass', function () {
|
||||
assert.strictEqual(decorateMethodsWith, decorateClass)
|
||||
})
|
||||
|
||||
describe('perInstance', () => {
|
||||
it('works', () => {
|
||||
let calls = 0
|
||||
|
||||
const expectedArgs = [Math.random(), Math.random()]
|
||||
const expectedFn = Function.prototype
|
||||
function wrapper(fn, ...args) {
|
||||
assert.strictEqual(fn, expectedFn)
|
||||
assert.deepStrictEqual(args, expectedArgs)
|
||||
const i = ++calls
|
||||
return () => i
|
||||
}
|
||||
|
||||
const wrapped = perInstance(expectedFn, wrapper, ...expectedArgs)
|
||||
|
||||
// decorator is not called before decorated called
|
||||
assert.strictEqual(calls, 0)
|
||||
|
||||
const o1 = {}
|
||||
const o2 = {}
|
||||
|
||||
assert.strictEqual(wrapped.call(o1), 1)
|
||||
|
||||
// the same decorated function is returned for the same instance
|
||||
assert.strictEqual(wrapped.call(o1), 1)
|
||||
|
||||
// a new decorated function is returned for another instance
|
||||
assert.strictEqual(wrapped.call(o2), 2)
|
||||
})
|
||||
})
|
||||
34
@vates/decorate-with/package.json
Normal file
34
@vates/decorate-with/package.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "@vates/decorate-with",
|
||||
"description": "Creates a decorator from a function wrapper",
|
||||
"keywords": [
|
||||
"apply",
|
||||
"decorator",
|
||||
"factory",
|
||||
"wrapper"
|
||||
],
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/decorate-with",
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"repository": {
|
||||
"directory": "@vates/decorate-with",
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Vates SAS",
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"license": "ISC",
|
||||
"version": "2.0.0",
|
||||
"engines": {
|
||||
"node": ">=8.10"
|
||||
},
|
||||
"scripts": {
|
||||
"postversion": "npm publish --access public",
|
||||
"test": "node--test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"test": "^3.2.1"
|
||||
}
|
||||
}
|
||||
56
@vates/disposable/.USAGE.md
Normal file
56
@vates/disposable/.USAGE.md
Normal file
@@ -0,0 +1,56 @@
|
||||
This library contains utilities for disposables as defined by the [`promise-toolbox` library](https://github.com/JsCommunity/promise-toolbox#resource-management).
|
||||
|
||||
### `deduped(fn, keyFn)`
|
||||
|
||||
Creates a new function that wraps `fn` and instead of creating a new disposables at each call, returns copies of the same one when `keyFn` returns the same keys.
|
||||
|
||||
Those copies contains the same value and can be disposed independently, the source disposable will only be disposed when all copies are disposed.
|
||||
|
||||
`keyFn` is called with the same context and arguments as the wrapping function and must returns an array of keys which will be used to identify which disposables should be grouped together.
|
||||
|
||||
```js
|
||||
import { deduped } from '@vates/disposable/deduped'
|
||||
|
||||
// the connection with the passed host will be established once at the first call, then, it will be shared with the next calls
|
||||
const getConnection = deduped(async function (host)) {
|
||||
const connection = new Connection(host)
|
||||
return new Disposabe(connection, () => connection.close())
|
||||
}, host => [host])
|
||||
```
|
||||
|
||||
### `debounceResource(disposable, delay)`
|
||||
|
||||
Creates a new disposable with the same value and with a delayed disposer.
|
||||
|
||||
On calling this disposer, the source disposable will be disposed when the `delay` is passed.
|
||||
|
||||
```js
|
||||
import { createDebounceResource } from '@vates/disposable/debounceResource'
|
||||
|
||||
const debounceResource = createDebounceResource()
|
||||
|
||||
// it will wait for 10 seconds before calling the disposer
|
||||
Disposable.use(debounceResource(getConnection(host), 10e3), connection => {})
|
||||
```
|
||||
|
||||
### `debounceResource.flushAll()`
|
||||
|
||||
Disposes all delayed disposers and cancels the delaying of the disposables that are in usage.
|
||||
|
||||
```js
|
||||
import { createDebounceResource } from '@vates/disposable/debounceResource'
|
||||
|
||||
const debounceResource = createDebounceResource()
|
||||
|
||||
const res1 = await debounceResource(res, 10e3)
|
||||
const res2 = await debounceResource(res, 10e3)
|
||||
const res3 = await debounceResource(res, 10e3)
|
||||
|
||||
rest1.dispose()
|
||||
rest2.dispose()
|
||||
// res3 is in usage
|
||||
|
||||
debounceResource.flushAll()
|
||||
// res1 and res2 are immediately disposed
|
||||
// res3 will be disposed immediately when its disposer will be called
|
||||
```
|
||||
1
@vates/disposable/.npmignore
Symbolic link
1
@vates/disposable/.npmignore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../scripts/npmignore
|
||||
89
@vates/disposable/README.md
Normal file
89
@vates/disposable/README.md
Normal file
@@ -0,0 +1,89 @@
|
||||
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
|
||||
|
||||
# @vates/disposable
|
||||
|
||||
[](https://npmjs.org/package/@vates/disposable)  [](https://bundlephobia.com/result?p=@vates/disposable) [](https://npmjs.org/package/@vates/disposable)
|
||||
|
||||
> Utilities for disposables
|
||||
|
||||
## Install
|
||||
|
||||
Installation of the [npm package](https://npmjs.org/package/@vates/disposable):
|
||||
|
||||
```
|
||||
> npm install --save @vates/disposable
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
This library contains utilities for disposables as defined by the [`promise-toolbox` library](https://github.com/JsCommunity/promise-toolbox#resource-management).
|
||||
|
||||
### `deduped(fn, keyFn)`
|
||||
|
||||
Creates a new function that wraps `fn` and instead of creating a new disposables at each call, returns copies of the same one when `keyFn` returns the same keys.
|
||||
|
||||
Those copies contains the same value and can be disposed independently, the source disposable will only be disposed when all copies are disposed.
|
||||
|
||||
`keyFn` is called with the same context and arguments as the wrapping function and must returns an array of keys which will be used to identify which disposables should be grouped together.
|
||||
|
||||
```js
|
||||
import { deduped } from '@vates/disposable/deduped'
|
||||
|
||||
// the connection with the passed host will be established once at the first call, then, it will be shared with the next calls
|
||||
const getConnection = deduped(async function (host)) {
|
||||
const connection = new Connection(host)
|
||||
return new Disposabe(connection, () => connection.close())
|
||||
}, host => [host])
|
||||
```
|
||||
|
||||
### `debounceResource(disposable, delay)`
|
||||
|
||||
Creates a new disposable with the same value and with a delayed disposer.
|
||||
|
||||
On calling this disposer, the source disposable will be disposed when the `delay` is passed.
|
||||
|
||||
```js
|
||||
import { createDebounceResource } from '@vates/disposable/debounceResource'
|
||||
|
||||
const debounceResource = createDebounceResource()
|
||||
|
||||
// it will wait for 10 seconds before calling the disposer
|
||||
Disposable.use(debounceResource(getConnection(host), 10e3), connection => {})
|
||||
```
|
||||
|
||||
### `debounceResource.flushAll()`
|
||||
|
||||
Disposes all delayed disposers and cancels the delaying of the disposables that are in usage.
|
||||
|
||||
```js
|
||||
import { createDebounceResource } from '@vates/disposable/debounceResource'
|
||||
|
||||
const debounceResource = createDebounceResource()
|
||||
|
||||
const res1 = await debounceResource(res, 10e3)
|
||||
const res2 = await debounceResource(res, 10e3)
|
||||
const res3 = await debounceResource(res, 10e3)
|
||||
|
||||
rest1.dispose()
|
||||
rest2.dispose()
|
||||
// res3 is in usage
|
||||
|
||||
debounceResource.flushAll()
|
||||
// res1 and res2 are immediately disposed
|
||||
// res3 will be disposed immediately when its disposer will be called
|
||||
```
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions are _very_ welcomed, either on the documentation or on
|
||||
the code.
|
||||
|
||||
You may:
|
||||
|
||||
- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
|
||||
you've encountered;
|
||||
- fork and create a pull request.
|
||||
|
||||
## License
|
||||
|
||||
[ISC](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)
|
||||
58
@vates/disposable/debounceResource.js
Normal file
58
@vates/disposable/debounceResource.js
Normal file
@@ -0,0 +1,58 @@
|
||||
'use strict'
|
||||
|
||||
const { asyncMap } = require('@xen-orchestra/async-map')
|
||||
const { createLogger } = require('@xen-orchestra/log')
|
||||
|
||||
const { warn } = createLogger('vates:disposable:debounceResource')
|
||||
|
||||
exports.createDebounceResource = () => {
|
||||
const flushers = new Set()
|
||||
async function debounceResource(pDisposable, delay = debounceResource.defaultDelay) {
|
||||
if (delay === 0) {
|
||||
return pDisposable
|
||||
}
|
||||
|
||||
const disposable = await pDisposable
|
||||
|
||||
let timeoutId
|
||||
const disposeWrapper = async () => {
|
||||
if (timeoutId !== undefined) {
|
||||
clearTimeout(timeoutId)
|
||||
timeoutId = undefined
|
||||
flushers.delete(flusher)
|
||||
|
||||
try {
|
||||
await disposable.dispose()
|
||||
} catch (error) {
|
||||
warn(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const flusher = () => {
|
||||
const shouldDisposeNow = timeoutId !== undefined
|
||||
if (shouldDisposeNow) {
|
||||
return disposeWrapper()
|
||||
} else {
|
||||
// will dispose ASAP
|
||||
delay = 0
|
||||
}
|
||||
}
|
||||
flushers.add(flusher)
|
||||
|
||||
return {
|
||||
dispose() {
|
||||
timeoutId = setTimeout(disposeWrapper, delay)
|
||||
},
|
||||
value: disposable.value,
|
||||
}
|
||||
}
|
||||
debounceResource.flushAll = () => {
|
||||
// iterate on a sync way in order to not remove a flusher added on processing flushers
|
||||
const promise = asyncMap(flushers, flush => flush())
|
||||
flushers.clear()
|
||||
return promise
|
||||
}
|
||||
|
||||
return debounceResource
|
||||
}
|
||||
32
@vates/disposable/debounceResource.test.js
Normal file
32
@vates/disposable/debounceResource.test.js
Normal file
@@ -0,0 +1,32 @@
|
||||
'use strict'
|
||||
|
||||
const { describe, it } = require('test')
|
||||
const { useFakeTimers, spy, assert } = require('sinon')
|
||||
|
||||
const { createDebounceResource } = require('./debounceResource')
|
||||
|
||||
const clock = useFakeTimers()
|
||||
|
||||
describe('debounceResource()', () => {
|
||||
it('calls the resource disposer after 10 seconds', async () => {
|
||||
const debounceResource = createDebounceResource()
|
||||
const delay = 10e3
|
||||
const dispose = spy()
|
||||
|
||||
const resource = await debounceResource(
|
||||
Promise.resolve({
|
||||
value: '',
|
||||
dispose,
|
||||
}),
|
||||
delay
|
||||
)
|
||||
|
||||
resource.dispose()
|
||||
|
||||
assert.notCalled(dispose)
|
||||
|
||||
clock.tick(delay)
|
||||
|
||||
assert.called(dispose)
|
||||
})
|
||||
})
|
||||
54
@vates/disposable/deduped.js
Normal file
54
@vates/disposable/deduped.js
Normal file
@@ -0,0 +1,54 @@
|
||||
'use strict'
|
||||
|
||||
const ensureArray = require('ensure-array')
|
||||
const { MultiKeyMap } = require('@vates/multi-key-map')
|
||||
|
||||
function State(factory) {
|
||||
this.factory = factory
|
||||
this.users = 0
|
||||
}
|
||||
|
||||
const call = fn => fn()
|
||||
|
||||
exports.deduped = (factory, keyFn = (...args) => args) =>
|
||||
(function () {
|
||||
const states = new MultiKeyMap()
|
||||
return function () {
|
||||
const keys = ensureArray(keyFn.apply(this, arguments))
|
||||
let state = states.get(keys)
|
||||
if (state === undefined) {
|
||||
const result = factory.apply(this, arguments)
|
||||
|
||||
const createFactory = disposable => {
|
||||
const wrapper = {
|
||||
dispose() {
|
||||
if (--state.users === 0) {
|
||||
states.delete(keys)
|
||||
return disposable.dispose()
|
||||
}
|
||||
},
|
||||
value: disposable.value,
|
||||
}
|
||||
|
||||
return () => {
|
||||
return wrapper
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof result.then !== 'function') {
|
||||
state = new State(createFactory(result))
|
||||
} else {
|
||||
result.catch(() => {
|
||||
states.delete(keys)
|
||||
})
|
||||
const pFactory = result.then(createFactory)
|
||||
state = new State(() => pFactory.then(call))
|
||||
}
|
||||
|
||||
states.set(keys, state)
|
||||
}
|
||||
|
||||
++state.users
|
||||
return state.factory()
|
||||
}
|
||||
})()
|
||||
79
@vates/disposable/deduped.test.js
Normal file
79
@vates/disposable/deduped.test.js
Normal file
@@ -0,0 +1,79 @@
|
||||
'use strict'
|
||||
|
||||
const { describe, it } = require('test')
|
||||
const { spy, assert } = require('sinon')
|
||||
|
||||
const { deduped } = require('./deduped')
|
||||
|
||||
describe('deduped()', () => {
|
||||
it('calls the resource function only once', async () => {
|
||||
const value = {}
|
||||
const getResource = spy(async () => ({
|
||||
value,
|
||||
dispose: Function.prototype,
|
||||
}))
|
||||
|
||||
const dedupedGetResource = deduped(getResource)
|
||||
|
||||
const { value: v1 } = await dedupedGetResource()
|
||||
const { value: v2 } = await dedupedGetResource()
|
||||
|
||||
assert.calledOnce(getResource)
|
||||
assert.match(v1, value)
|
||||
assert.match(v2, value)
|
||||
})
|
||||
|
||||
it('only disposes the source disposable when its all copies dispose', async () => {
|
||||
const dispose = spy()
|
||||
const getResource = async () => ({
|
||||
value: '',
|
||||
dispose,
|
||||
})
|
||||
|
||||
const dedupedGetResource = deduped(getResource)
|
||||
|
||||
const { dispose: d1 } = await dedupedGetResource()
|
||||
const { dispose: d2 } = await dedupedGetResource()
|
||||
|
||||
d1()
|
||||
|
||||
assert.notCalled(dispose)
|
||||
|
||||
d2()
|
||||
|
||||
assert.calledOnce(dispose)
|
||||
})
|
||||
|
||||
it('works with sync factory', () => {
|
||||
const value = {}
|
||||
const dispose = spy()
|
||||
const dedupedGetResource = deduped(() => ({ value, dispose }))
|
||||
|
||||
const d1 = dedupedGetResource()
|
||||
assert.match(d1.value, value)
|
||||
|
||||
const d2 = dedupedGetResource()
|
||||
assert.match(d2.value, value)
|
||||
|
||||
d1.dispose()
|
||||
|
||||
assert.notCalled(dispose)
|
||||
|
||||
d2.dispose()
|
||||
|
||||
assert.calledOnce(dispose)
|
||||
})
|
||||
|
||||
it('no race condition on dispose before async acquisition', async () => {
|
||||
const dispose = spy()
|
||||
const dedupedGetResource = deduped(async () => ({ value: 42, dispose }))
|
||||
|
||||
const d1 = await dedupedGetResource()
|
||||
|
||||
dedupedGetResource()
|
||||
|
||||
d1.dispose()
|
||||
|
||||
assert.notCalled(dispose)
|
||||
})
|
||||
})
|
||||
35
@vates/disposable/package.json
Normal file
35
@vates/disposable/package.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "@vates/disposable",
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/disposable",
|
||||
"description": "Utilities for disposables",
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"repository": {
|
||||
"directory": "@vates/disposable",
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Vates SAS",
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"license": "ISC",
|
||||
"version": "0.1.4",
|
||||
"engines": {
|
||||
"node": ">=8.10"
|
||||
},
|
||||
"scripts": {
|
||||
"postversion": "npm publish --access public",
|
||||
"test": "node--test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vates/multi-key-map": "^0.1.0",
|
||||
"@xen-orchestra/async-map": "^0.1.2",
|
||||
"@xen-orchestra/log": "^0.6.0",
|
||||
"ensure-array": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"sinon": "^15.0.1",
|
||||
"test": "^3.2.1"
|
||||
}
|
||||
}
|
||||
50
@vates/event-listeners-manager/.USAGE.md
Normal file
50
@vates/event-listeners-manager/.USAGE.md
Normal file
@@ -0,0 +1,50 @@
|
||||
> This library is compatible with Node's `EventEmitter` and web browsers' `EventTarget` APIs.
|
||||
|
||||
### API
|
||||
|
||||
```js
|
||||
import { EventListenersManager } from '@vates/event-listeners-manager'
|
||||
|
||||
const events = new EventListenersManager(emitter)
|
||||
|
||||
// adding listeners
|
||||
events.add('foo', onFoo).add('bar', onBar).on('baz', onBaz)
|
||||
|
||||
// removing a specific listener
|
||||
events.remove('foo', onFoo)
|
||||
|
||||
// removing all listeners for a specific event
|
||||
events.removeAll('foo')
|
||||
|
||||
// removing all listeners
|
||||
events.removeAll()
|
||||
```
|
||||
|
||||
### Typical use case
|
||||
|
||||
> Removing all listeners when no longer necessary.
|
||||
|
||||
Manually:
|
||||
|
||||
```js
|
||||
const onFoo = () => {}
|
||||
const onBar = () => {}
|
||||
const onBaz = () => {}
|
||||
emitter.on('foo', onFoo).on('bar', onBar).on('baz', onBaz)
|
||||
|
||||
// CODE LOGIC
|
||||
|
||||
emitter.off('foo', onFoo).off('bar', onBar).off('baz', onBaz)
|
||||
```
|
||||
|
||||
With this library:
|
||||
|
||||
```js
|
||||
const events = new EventListenersManager(emitter)
|
||||
|
||||
events.add('foo', () => {})).add('bar', () => {})).add('baz', () => {}))
|
||||
|
||||
// CODE LOGIC
|
||||
|
||||
events.removeAll()
|
||||
```
|
||||
1
@vates/event-listeners-manager/.npmignore
Symbolic link
1
@vates/event-listeners-manager/.npmignore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../scripts/npmignore
|
||||
81
@vates/event-listeners-manager/README.md
Normal file
81
@vates/event-listeners-manager/README.md
Normal file
@@ -0,0 +1,81 @@
|
||||
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
|
||||
|
||||
# @vates/event-listeners-manager
|
||||
|
||||
[](https://npmjs.org/package/@vates/event-listeners-manager)  [](https://bundlephobia.com/result?p=@vates/event-listeners-manager) [](https://npmjs.org/package/@vates/event-listeners-manager)
|
||||
|
||||
## Install
|
||||
|
||||
Installation of the [npm package](https://npmjs.org/package/@vates/event-listeners-manager):
|
||||
|
||||
```
|
||||
> npm install --save @vates/event-listeners-manager
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
> This library is compatible with Node's `EventEmitter` and web browsers' `EventTarget` APIs.
|
||||
|
||||
### API
|
||||
|
||||
```js
|
||||
import { EventListenersManager } from '@vates/event-listeners-manager'
|
||||
|
||||
const events = new EventListenersManager(emitter)
|
||||
|
||||
// adding listeners
|
||||
events.add('foo', onFoo).add('bar', onBar).on('baz', onBaz)
|
||||
|
||||
// removing a specific listener
|
||||
events.remove('foo', onFoo)
|
||||
|
||||
// removing all listeners for a specific event
|
||||
events.removeAll('foo')
|
||||
|
||||
// removing all listeners
|
||||
events.removeAll()
|
||||
```
|
||||
|
||||
### Typical use case
|
||||
|
||||
> Removing all listeners when no longer necessary.
|
||||
|
||||
Manually:
|
||||
|
||||
```js
|
||||
const onFoo = () => {}
|
||||
const onBar = () => {}
|
||||
const onBaz = () => {}
|
||||
emitter.on('foo', onFoo).on('bar', onBar).on('baz', onBaz)
|
||||
|
||||
// CODE LOGIC
|
||||
|
||||
emitter.off('foo', onFoo).off('bar', onBar).off('baz', onBaz)
|
||||
```
|
||||
|
||||
With this library:
|
||||
|
||||
```js
|
||||
const events = new EventListenersManager(emitter)
|
||||
|
||||
events.add('foo', () => {})).add('bar', () => {})).add('baz', () => {}))
|
||||
|
||||
// CODE LOGIC
|
||||
|
||||
events.removeAll()
|
||||
```
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions are _very_ welcomed, either on the documentation or on
|
||||
the code.
|
||||
|
||||
You may:
|
||||
|
||||
- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
|
||||
you've encountered;
|
||||
- fork and create a pull request.
|
||||
|
||||
## License
|
||||
|
||||
[ISC](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)
|
||||
56
@vates/event-listeners-manager/index.js
Normal file
56
@vates/event-listeners-manager/index.js
Normal file
@@ -0,0 +1,56 @@
|
||||
'use strict'
|
||||
|
||||
exports.EventListenersManager = class EventListenersManager {
|
||||
constructor(emitter) {
|
||||
this._listeners = new Map()
|
||||
|
||||
this._add = (emitter.addListener || emitter.addEventListener).bind(emitter)
|
||||
this._remove = (emitter.removeListener || emitter.removeEventListener).bind(emitter)
|
||||
}
|
||||
|
||||
add(type, listener) {
|
||||
let listeners = this._listeners.get(type)
|
||||
if (listeners === undefined) {
|
||||
listeners = new Set()
|
||||
this._listeners.set(type, listeners)
|
||||
}
|
||||
|
||||
// don't add the same listener multiple times (allowed on Node.js)
|
||||
if (!listeners.has(listener)) {
|
||||
listeners.add(listener)
|
||||
this._add(type, listener)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
remove(type, listener) {
|
||||
const allListeners = this._listeners
|
||||
const listeners = allListeners.get(type)
|
||||
if (listeners !== undefined && listeners.delete(listener)) {
|
||||
this._remove(type, listener)
|
||||
if (listeners.size === 0) {
|
||||
allListeners.delete(type)
|
||||
}
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
removeAll(type) {
|
||||
const allListeners = this._listeners
|
||||
const remove = this._remove
|
||||
const types = type !== undefined ? [type] : allListeners.keys()
|
||||
for (const type of types) {
|
||||
const listeners = allListeners.get(type)
|
||||
if (listeners !== undefined) {
|
||||
allListeners.delete(type)
|
||||
for (const listener of listeners) {
|
||||
remove(type, listener)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
||||
67
@vates/event-listeners-manager/index.spec.js
Normal file
67
@vates/event-listeners-manager/index.spec.js
Normal file
@@ -0,0 +1,67 @@
|
||||
'use strict'
|
||||
|
||||
const t = require('tap')
|
||||
const { EventEmitter } = require('events')
|
||||
|
||||
const { EventListenersManager } = require('./')
|
||||
|
||||
const noop = Function.prototype
|
||||
|
||||
// function spy (impl = Function.prototype) {
|
||||
// function spy() {
|
||||
// spy.calls.push([Array.from(arguments), this])
|
||||
// }
|
||||
// spy.calls = []
|
||||
// return spy
|
||||
// }
|
||||
|
||||
function assertListeners(t, event, listeners) {
|
||||
t.strictSame(t.context.ee.listeners(event), listeners)
|
||||
}
|
||||
|
||||
t.beforeEach(function (t) {
|
||||
t.context.ee = new EventEmitter()
|
||||
t.context.em = new EventListenersManager(t.context.ee)
|
||||
})
|
||||
|
||||
t.test('.add adds a listener', function (t) {
|
||||
t.context.em.add('foo', noop)
|
||||
|
||||
assertListeners(t, 'foo', [noop])
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('.add does not add a duplicate listener', function (t) {
|
||||
t.context.em.add('foo', noop).add('foo', noop)
|
||||
|
||||
assertListeners(t, 'foo', [noop])
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('.remove removes a listener', function (t) {
|
||||
t.context.em.add('foo', noop).remove('foo', noop)
|
||||
|
||||
assertListeners(t, 'foo', [])
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('.removeAll removes all listeners of a given type', function (t) {
|
||||
t.context.em.add('foo', noop).add('bar', noop).removeAll('foo')
|
||||
|
||||
assertListeners(t, 'foo', [])
|
||||
assertListeners(t, 'bar', [noop])
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
t.test('.removeAll removes all listeners', function (t) {
|
||||
t.context.em.add('foo', noop).add('bar', noop).removeAll()
|
||||
|
||||
assertListeners(t, 'foo', [])
|
||||
assertListeners(t, 'bar', [])
|
||||
|
||||
t.end()
|
||||
})
|
||||
46
@vates/event-listeners-manager/package.json
Normal file
46
@vates/event-listeners-manager/package.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"private": false,
|
||||
"name": "@vates/event-listeners-manager",
|
||||
"descriptions": "Easy way to clean up event listeners",
|
||||
"keywords": [
|
||||
"add",
|
||||
"addEventListener",
|
||||
"addListener",
|
||||
"browser",
|
||||
"clear",
|
||||
"DOM",
|
||||
"emitter",
|
||||
"event",
|
||||
"EventEmitter",
|
||||
"EventTarget",
|
||||
"management",
|
||||
"manager",
|
||||
"node",
|
||||
"remove",
|
||||
"removeEventListener",
|
||||
"removeListener"
|
||||
],
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/event-listeners-manager",
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"repository": {
|
||||
"directory": "@vates/event-listeners-manager",
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Vates SAS",
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"license": "ISC",
|
||||
"version": "1.0.1",
|
||||
"scripts": {
|
||||
"postversion": "npm publish --access public",
|
||||
"test": "tap --branches=72"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tap": "^16.2.0"
|
||||
}
|
||||
}
|
||||
1
@vates/fuse-vhd/.npmignore
Symbolic link
1
@vates/fuse-vhd/.npmignore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../scripts/npmignore
|
||||
66
@vates/fuse-vhd/index.js
Normal file
66
@vates/fuse-vhd/index.js
Normal file
@@ -0,0 +1,66 @@
|
||||
'use strict'
|
||||
|
||||
const LRU = require('lru-cache')
|
||||
const Fuse = require('fuse-native')
|
||||
const { VhdSynthetic } = require('vhd-lib')
|
||||
const { Disposable, fromCallback } = require('promise-toolbox')
|
||||
|
||||
// build a s stat object from https://github.com/fuse-friends/fuse-native/blob/master/test/fixtures/stat.js
|
||||
const stat = st => ({
|
||||
mtime: st.mtime || new Date(),
|
||||
atime: st.atime || new Date(),
|
||||
ctime: st.ctime || new Date(),
|
||||
size: st.size !== undefined ? st.size : 0,
|
||||
mode: st.mode === 'dir' ? 16877 : st.mode === 'file' ? 33188 : st.mode === 'link' ? 41453 : st.mode,
|
||||
uid: st.uid !== undefined ? st.uid : process.getuid(),
|
||||
gid: st.gid !== undefined ? st.gid : process.getgid(),
|
||||
})
|
||||
|
||||
exports.mount = Disposable.factory(async function* mount(handler, diskPath, mountDir) {
|
||||
const vhd = yield VhdSynthetic.fromVhdChain(handler, diskPath)
|
||||
|
||||
const cache = new LRU({
|
||||
max: 16, // each cached block is 2MB in size
|
||||
})
|
||||
await vhd.readBlockAllocationTable()
|
||||
const fuse = new Fuse(mountDir, {
|
||||
async readdir(path, cb) {
|
||||
if (path === '/') {
|
||||
return cb(null, ['vhd0'])
|
||||
}
|
||||
cb(Fuse.ENOENT)
|
||||
},
|
||||
async getattr(path, cb) {
|
||||
if (path === '/') {
|
||||
return cb(
|
||||
null,
|
||||
stat({
|
||||
mode: 'dir',
|
||||
size: 4096,
|
||||
})
|
||||
)
|
||||
}
|
||||
if (path === '/vhd0') {
|
||||
return cb(
|
||||
null,
|
||||
stat({
|
||||
mode: 'file',
|
||||
size: vhd.footer.currentSize,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
cb(Fuse.ENOENT)
|
||||
},
|
||||
read(path, fd, buf, len, pos, cb) {
|
||||
if (path === '/vhd0') {
|
||||
return vhd.readRawData(pos, len, cache, buf).then(cb)
|
||||
}
|
||||
throw new Error(`read file ${path} not exists`)
|
||||
},
|
||||
})
|
||||
return new Disposable(
|
||||
() => fromCallback(() => fuse.unmount()),
|
||||
fromCallback(() => fuse.mount())
|
||||
)
|
||||
})
|
||||
29
@vates/fuse-vhd/package.json
Normal file
29
@vates/fuse-vhd/package.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "@vates/fuse-vhd",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"private": false,
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/fuse-vhd",
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"repository": {
|
||||
"directory": "@vates/fuse-vhd",
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Vates SAS",
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"fuse-native": "^2.2.6",
|
||||
"lru-cache": "^7.14.0",
|
||||
"promise-toolbox": "^0.21.0",
|
||||
"vhd-lib": "^4.2.1"
|
||||
},
|
||||
"scripts": {
|
||||
"postversion": "npm publish --access public"
|
||||
}
|
||||
}
|
||||
20
@vates/multi-key-map/.USAGE.md
Normal file
20
@vates/multi-key-map/.USAGE.md
Normal file
@@ -0,0 +1,20 @@
|
||||
```js
|
||||
import { MultiKeyMap } from '@vates/multi-key-map'
|
||||
|
||||
const map = new MultiKeyMap()
|
||||
|
||||
const OBJ = {}
|
||||
map.set([], 0)
|
||||
map.set(['foo'], 1)
|
||||
map.set(['foo', 'bar'], 2)
|
||||
map.set(['bar', 'foo'], 3)
|
||||
map.set([OBJ], 4)
|
||||
map.set([{}], 5)
|
||||
|
||||
map.get([]) // 0
|
||||
map.get(['foo']) // 1
|
||||
map.get(['foo', 'bar']) // 2
|
||||
map.get(['bar', 'foo']) // 3
|
||||
map.get([OBJ]) // 4
|
||||
map.get([{}]) // undefined
|
||||
```
|
||||
1
@vates/multi-key-map/.npmignore
Symbolic link
1
@vates/multi-key-map/.npmignore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../scripts/npmignore
|
||||
53
@vates/multi-key-map/README.md
Normal file
53
@vates/multi-key-map/README.md
Normal file
@@ -0,0 +1,53 @@
|
||||
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
|
||||
|
||||
# @vates/multi-key-map
|
||||
|
||||
[](https://npmjs.org/package/@vates/multi-key-map)  [](https://bundlephobia.com/result?p=@vates/multi-key-map) [](https://npmjs.org/package/@vates/multi-key-map)
|
||||
|
||||
> Create map with values affected to multiple keys
|
||||
|
||||
## Install
|
||||
|
||||
Installation of the [npm package](https://npmjs.org/package/@vates/multi-key-map):
|
||||
|
||||
```
|
||||
> npm install --save @vates/multi-key-map
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import { MultiKeyMap } from '@vates/multi-key-map'
|
||||
|
||||
const map = new MultiKeyMap()
|
||||
|
||||
const OBJ = {}
|
||||
map.set([], 0)
|
||||
map.set(['foo'], 1)
|
||||
map.set(['foo', 'bar'], 2)
|
||||
map.set(['bar', 'foo'], 3)
|
||||
map.set([OBJ], 4)
|
||||
map.set([{}], 5)
|
||||
|
||||
map.get([]) // 0
|
||||
map.get(['foo']) // 1
|
||||
map.get(['foo', 'bar']) // 2
|
||||
map.get(['bar', 'foo']) // 3
|
||||
map.get([OBJ]) // 4
|
||||
map.get([{}]) // undefined
|
||||
```
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions are _very_ welcomed, either on the documentation or on
|
||||
the code.
|
||||
|
||||
You may:
|
||||
|
||||
- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
|
||||
you've encountered;
|
||||
- fork and create a pull request.
|
||||
|
||||
## License
|
||||
|
||||
[ISC](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)
|
||||
89
@vates/multi-key-map/index.js
Normal file
89
@vates/multi-key-map/index.js
Normal file
@@ -0,0 +1,89 @@
|
||||
'use strict'
|
||||
|
||||
class Node {
|
||||
constructor(value) {
|
||||
this.children = new Map()
|
||||
this.value = value
|
||||
}
|
||||
}
|
||||
|
||||
function del(node, i, keys) {
|
||||
if (i === keys.length) {
|
||||
if (node instanceof Node) {
|
||||
node.value = undefined
|
||||
return node
|
||||
}
|
||||
return
|
||||
}
|
||||
if (!(node instanceof Node)) {
|
||||
return node
|
||||
}
|
||||
const key = keys[i]
|
||||
const { children } = node
|
||||
const child = children.get(key)
|
||||
if (child === undefined) {
|
||||
return node
|
||||
}
|
||||
const newChild = del(child, i + 1, keys)
|
||||
if (newChild === undefined) {
|
||||
if (children.size === 1) {
|
||||
return node.value
|
||||
}
|
||||
children.delete(key)
|
||||
} else if (newChild !== child) {
|
||||
children.set(key, newChild)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
function get(node, i, keys) {
|
||||
return i === keys.length
|
||||
? node instanceof Node
|
||||
? node.value
|
||||
: node
|
||||
: node instanceof Node
|
||||
? get(node.children.get(keys[i]), i + 1, keys)
|
||||
: undefined
|
||||
}
|
||||
|
||||
function set(node, i, keys, value) {
|
||||
if (i === keys.length) {
|
||||
if (node instanceof Node) {
|
||||
node.value = value
|
||||
return node
|
||||
}
|
||||
return value
|
||||
}
|
||||
const key = keys[i]
|
||||
if (!(node instanceof Node)) {
|
||||
node = new Node(node)
|
||||
node.children.set(key, set(undefined, i + 1, keys, value))
|
||||
} else {
|
||||
const { children } = node
|
||||
const child = children.get(key)
|
||||
const newChild = set(child, i + 1, keys, value)
|
||||
if (newChild !== child) {
|
||||
children.set(key, newChild)
|
||||
}
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
exports.MultiKeyMap = class MultiKeyMap {
|
||||
constructor() {
|
||||
// each node is either a value or a Node if it contains children
|
||||
this._root = undefined
|
||||
}
|
||||
|
||||
delete(keys) {
|
||||
this._root = del(this._root, 0, keys)
|
||||
}
|
||||
|
||||
get(keys) {
|
||||
return get(this._root, 0, keys)
|
||||
}
|
||||
|
||||
set(keys, value) {
|
||||
this._root = set(this._root, 0, keys, value)
|
||||
}
|
||||
}
|
||||
37
@vates/multi-key-map/index.test.js
Normal file
37
@vates/multi-key-map/index.test.js
Normal file
@@ -0,0 +1,37 @@
|
||||
'use strict'
|
||||
|
||||
const { describe, it } = require('test')
|
||||
const assert = require('node:assert')
|
||||
|
||||
const { MultiKeyMap } = require('./')
|
||||
|
||||
describe('MultiKeyMap', () => {
|
||||
it('works', () => {
|
||||
const map = new MultiKeyMap()
|
||||
|
||||
const keys = [
|
||||
// null key
|
||||
[],
|
||||
// simple key
|
||||
['foo'],
|
||||
// composite key
|
||||
['foo', 'bar'],
|
||||
// reverse composite key
|
||||
['bar', 'foo'],
|
||||
]
|
||||
const values = keys.map(() => ({}))
|
||||
|
||||
// set all values first to make sure they are all stored and not only the
|
||||
// last one
|
||||
keys.forEach((key, i) => {
|
||||
map.set(key, values[i])
|
||||
})
|
||||
|
||||
keys.forEach((key, i) => {
|
||||
// copy the key to make sure the array itself is not the key
|
||||
assert.strictEqual(map.get(key.slice()), values[i])
|
||||
map.delete(key.slice())
|
||||
assert.strictEqual(map.get(key.slice()), undefined)
|
||||
})
|
||||
})
|
||||
})
|
||||
32
@vates/multi-key-map/package.json
Normal file
32
@vates/multi-key-map/package.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "@vates/multi-key-map",
|
||||
"description": "Create map with values affected to multiple keys",
|
||||
"keywords": [
|
||||
"cache",
|
||||
"map"
|
||||
],
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/multi-key-map",
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"repository": {
|
||||
"directory": "@vates/multi-key-map",
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Vates SAS",
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"license": "ISC",
|
||||
"version": "0.1.0",
|
||||
"engines": {
|
||||
"node": ">=8.10"
|
||||
},
|
||||
"scripts": {
|
||||
"postversion": "npm publish --access public",
|
||||
"test": "node--test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"test": "^3.2.1"
|
||||
}
|
||||
}
|
||||
16
@vates/nbd-client/.USAGE.md
Normal file
16
@vates/nbd-client/.USAGE.md
Normal file
@@ -0,0 +1,16 @@
|
||||
### `new NdbClient({address, exportname, secure = true, port = 10809})`
|
||||
|
||||
create a new nbd client
|
||||
|
||||
```js
|
||||
import NbdClient from '@vates/nbd-client'
|
||||
const client = new NbdClient({
|
||||
address: 'MY_NBD_HOST',
|
||||
exportname: 'MY_SECRET_EXPORT',
|
||||
cert: 'Server certificate', // optional, will use encrypted link if provided
|
||||
})
|
||||
|
||||
await client.connect()
|
||||
const block = await client.readBlock(blockIndex, BlockSize)
|
||||
await client.disconnect()
|
||||
```
|
||||
1
@vates/nbd-client/.npmignore
Symbolic link
1
@vates/nbd-client/.npmignore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../scripts/npmignore
|
||||
47
@vates/nbd-client/README.md
Normal file
47
@vates/nbd-client/README.md
Normal file
@@ -0,0 +1,47 @@
|
||||
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
|
||||
|
||||
# @vates/nbd-client
|
||||
|
||||
[](https://npmjs.org/package/@vates/nbd-client)  [](https://bundlephobia.com/result?p=@vates/nbd-client) [](https://npmjs.org/package/@vates/nbd-client)
|
||||
|
||||
## Install
|
||||
|
||||
Installation of the [npm package](https://npmjs.org/package/@vates/nbd-client):
|
||||
|
||||
```
|
||||
> npm install --save @vates/nbd-client
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### `new NdbClient({address, exportname, secure = true, port = 10809})`
|
||||
|
||||
create a new nbd client
|
||||
|
||||
```js
|
||||
import NbdClient from '@vates/nbd-client'
|
||||
const client = new NbdClient({
|
||||
address: 'MY_NBD_HOST',
|
||||
exportname: 'MY_SECRET_EXPORT',
|
||||
cert: 'Server certificate', // optional, will use encrypted link if provided
|
||||
})
|
||||
|
||||
await client.connect()
|
||||
const block = await client.readBlock(blockIndex, BlockSize)
|
||||
await client.disconnect()
|
||||
```
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions are _very_ welcomed, either on the documentation or on
|
||||
the code.
|
||||
|
||||
You may:
|
||||
|
||||
- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
|
||||
you've encountered;
|
||||
- fork and create a pull request.
|
||||
|
||||
## License
|
||||
|
||||
[ISC](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)
|
||||
42
@vates/nbd-client/constants.js
Normal file
42
@vates/nbd-client/constants.js
Normal file
@@ -0,0 +1,42 @@
|
||||
'use strict'
|
||||
exports.INIT_PASSWD = Buffer.from('NBDMAGIC') // "NBDMAGIC" ensure we're connected to a nbd server
|
||||
exports.OPTS_MAGIC = Buffer.from('IHAVEOPT') // "IHAVEOPT" start an option block
|
||||
exports.NBD_OPT_REPLY_MAGIC = 1100100111001001n // magic received during negociation
|
||||
exports.NBD_OPT_EXPORT_NAME = 1
|
||||
exports.NBD_OPT_ABORT = 2
|
||||
exports.NBD_OPT_LIST = 3
|
||||
exports.NBD_OPT_STARTTLS = 5
|
||||
exports.NBD_OPT_INFO = 6
|
||||
exports.NBD_OPT_GO = 7
|
||||
|
||||
exports.NBD_FLAG_HAS_FLAGS = 1 << 0
|
||||
exports.NBD_FLAG_READ_ONLY = 1 << 1
|
||||
exports.NBD_FLAG_SEND_FLUSH = 1 << 2
|
||||
exports.NBD_FLAG_SEND_FUA = 1 << 3
|
||||
exports.NBD_FLAG_ROTATIONAL = 1 << 4
|
||||
exports.NBD_FLAG_SEND_TRIM = 1 << 5
|
||||
|
||||
exports.NBD_FLAG_FIXED_NEWSTYLE = 1 << 0
|
||||
|
||||
exports.NBD_CMD_FLAG_FUA = 1 << 0
|
||||
exports.NBD_CMD_FLAG_NO_HOLE = 1 << 1
|
||||
exports.NBD_CMD_FLAG_DF = 1 << 2
|
||||
exports.NBD_CMD_FLAG_REQ_ONE = 1 << 3
|
||||
exports.NBD_CMD_FLAG_FAST_ZERO = 1 << 4
|
||||
|
||||
exports.NBD_CMD_READ = 0
|
||||
exports.NBD_CMD_WRITE = 1
|
||||
exports.NBD_CMD_DISC = 2
|
||||
exports.NBD_CMD_FLUSH = 3
|
||||
exports.NBD_CMD_TRIM = 4
|
||||
exports.NBD_CMD_CACHE = 5
|
||||
exports.NBD_CMD_WRITE_ZEROES = 6
|
||||
exports.NBD_CMD_BLOCK_STATUS = 7
|
||||
exports.NBD_CMD_RESIZE = 8
|
||||
|
||||
exports.NBD_REQUEST_MAGIC = 0x25609513 // magic number to create a new NBD request to send to the server
|
||||
exports.NBD_REPLY_MAGIC = 0x67446698 // magic number received from the server when reading response to a nbd request
|
||||
exports.NBD_REPLY_ACK = 1
|
||||
|
||||
exports.NBD_DEFAULT_PORT = 10809
|
||||
exports.NBD_DEFAULT_BLOCK_SIZE = 64 * 1024
|
||||
243
@vates/nbd-client/index.js
Normal file
243
@vates/nbd-client/index.js
Normal file
@@ -0,0 +1,243 @@
|
||||
'use strict'
|
||||
const assert = require('node:assert')
|
||||
const { Socket } = require('node:net')
|
||||
const { connect } = require('node:tls')
|
||||
const {
|
||||
INIT_PASSWD,
|
||||
NBD_CMD_READ,
|
||||
NBD_DEFAULT_BLOCK_SIZE,
|
||||
NBD_DEFAULT_PORT,
|
||||
NBD_FLAG_FIXED_NEWSTYLE,
|
||||
NBD_FLAG_HAS_FLAGS,
|
||||
NBD_OPT_EXPORT_NAME,
|
||||
NBD_OPT_REPLY_MAGIC,
|
||||
NBD_OPT_STARTTLS,
|
||||
NBD_REPLY_ACK,
|
||||
NBD_REPLY_MAGIC,
|
||||
NBD_REQUEST_MAGIC,
|
||||
OPTS_MAGIC,
|
||||
} = require('./constants.js')
|
||||
const { fromCallback } = require('promise-toolbox')
|
||||
const { readChunkStrict } = require('@vates/read-chunk')
|
||||
|
||||
// documentation is here : https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md
|
||||
|
||||
module.exports = class NbdClient {
|
||||
#serverAddress
|
||||
#serverCert
|
||||
#serverPort
|
||||
#serverSocket
|
||||
|
||||
#exportName
|
||||
#exportSize
|
||||
|
||||
// AFAIK, there is no guaranty the server answers in the same order as the queries
|
||||
// so we handle a backlog of command waiting for response and handle concurrency manually
|
||||
|
||||
#waitingForResponse // there is already a listenner waiting for a response
|
||||
#nextCommandQueryId = BigInt(0)
|
||||
#commandQueryBacklog // map of command waiting for an response queryId => { size/*in byte*/, resolve, reject}
|
||||
|
||||
constructor({ address, port = NBD_DEFAULT_PORT, exportname, cert }) {
|
||||
this.#serverAddress = address
|
||||
this.#serverPort = port
|
||||
this.#exportName = exportname
|
||||
this.#serverCert = cert
|
||||
}
|
||||
|
||||
get exportSize() {
|
||||
return this.#exportSize
|
||||
}
|
||||
|
||||
async #tlsConnect() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.#serverSocket = connect({
|
||||
socket: this.#serverSocket,
|
||||
rejectUnauthorized: false,
|
||||
cert: this.#serverCert,
|
||||
})
|
||||
this.#serverSocket.once('error', reject)
|
||||
this.#serverSocket.once('secureConnect', () => {
|
||||
this.#serverSocket.removeListener('error', reject)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// mandatory , at least to start the handshake
|
||||
async #unsecureConnect() {
|
||||
this.#serverSocket = new Socket()
|
||||
return new Promise((resolve, reject) => {
|
||||
this.#serverSocket.connect(this.#serverPort, this.#serverAddress)
|
||||
this.#serverSocket.once('error', reject)
|
||||
this.#serverSocket.once('connect', () => {
|
||||
this.#serverSocket.removeListener('error', reject)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async connect() {
|
||||
// first we connect to the serve without tls, and then we upgrade the connection
|
||||
// to tls during the handshake
|
||||
await this.#unsecureConnect()
|
||||
await this.#handshake()
|
||||
|
||||
// reset internal state if we reconnected a nbd client
|
||||
this.#commandQueryBacklog = new Map()
|
||||
this.#waitingForResponse = false
|
||||
}
|
||||
|
||||
async disconnect() {
|
||||
await this.#serverSocket.destroy()
|
||||
}
|
||||
|
||||
// we can use individual read/write from the socket here since there is no concurrency
|
||||
async #sendOption(option, buffer = Buffer.alloc(0)) {
|
||||
await this.#write(OPTS_MAGIC)
|
||||
await this.#writeInt32(option)
|
||||
await this.#writeInt32(buffer.length)
|
||||
await this.#write(buffer)
|
||||
assert.strictEqual(await this.#readInt64(), NBD_OPT_REPLY_MAGIC) // magic number everywhere
|
||||
assert.strictEqual(await this.#readInt32(), option) // the option passed
|
||||
assert.strictEqual(await this.#readInt32(), NBD_REPLY_ACK) // ACK
|
||||
const length = await this.#readInt32()
|
||||
assert.strictEqual(length, 0) // length
|
||||
}
|
||||
|
||||
// we can use individual read/write from the socket here since there is only one handshake at once, no concurrency
|
||||
async #handshake() {
|
||||
assert((await this.#read(8)).equals(INIT_PASSWD))
|
||||
assert((await this.#read(8)).equals(OPTS_MAGIC))
|
||||
const flagsBuffer = await this.#read(2)
|
||||
const flags = flagsBuffer.readInt16BE(0)
|
||||
assert.strictEqual(flags & NBD_FLAG_FIXED_NEWSTYLE, NBD_FLAG_FIXED_NEWSTYLE) // only FIXED_NEWSTYLE one is supported from the server options
|
||||
await this.#writeInt32(NBD_FLAG_FIXED_NEWSTYLE) // client also support NBD_FLAG_C_FIXED_NEWSTYLE
|
||||
|
||||
if (this.#serverCert !== undefined) {
|
||||
// upgrade socket to TLS if needed
|
||||
await this.#sendOption(NBD_OPT_STARTTLS)
|
||||
await this.#tlsConnect()
|
||||
}
|
||||
|
||||
// send export name we want to access.
|
||||
// it's implictly closing the negociation phase.
|
||||
await this.#write(OPTS_MAGIC)
|
||||
await this.#writeInt32(NBD_OPT_EXPORT_NAME)
|
||||
const exportNameBuffer = Buffer.from(this.#exportName)
|
||||
await this.#writeInt32(exportNameBuffer.length)
|
||||
await this.#write(exportNameBuffer)
|
||||
|
||||
// 8 (export size ) + 2 (flags) + 124 zero = 134
|
||||
// must read all to ensure nothing stays in the buffer
|
||||
const answer = await this.#read(134)
|
||||
this.#exportSize = answer.readBigUInt64BE(0)
|
||||
const transmissionFlags = answer.readInt16BE(8)
|
||||
assert.strictEqual(transmissionFlags & NBD_FLAG_HAS_FLAGS, NBD_FLAG_HAS_FLAGS, 'NBD_FLAG_HAS_FLAGS') // must always be 1 by the norm
|
||||
|
||||
// note : xapi server always send NBD_FLAG_READ_ONLY (3) as a flag
|
||||
}
|
||||
|
||||
#read(length) {
|
||||
return readChunkStrict(this.#serverSocket, length)
|
||||
}
|
||||
|
||||
#write(buffer) {
|
||||
return fromCallback.call(this.#serverSocket, 'write', buffer)
|
||||
}
|
||||
|
||||
async #readInt32() {
|
||||
const buffer = await this.#read(4)
|
||||
return buffer.readInt32BE(0)
|
||||
}
|
||||
|
||||
async #readInt64() {
|
||||
const buffer = await this.#read(8)
|
||||
return buffer.readBigUInt64BE(0)
|
||||
}
|
||||
|
||||
#writeInt32(int) {
|
||||
const buffer = Buffer.alloc(4)
|
||||
buffer.writeInt32BE(int)
|
||||
return this.#write(buffer)
|
||||
}
|
||||
|
||||
// when one read fail ,stop everything
|
||||
async #rejectAll(error) {
|
||||
this.#commandQueryBacklog.forEach(({ reject }) => {
|
||||
reject(error)
|
||||
})
|
||||
await this.disconnect()
|
||||
}
|
||||
|
||||
async #readBlockResponse() {
|
||||
// ensure at most one read occur in parallel
|
||||
if (this.#waitingForResponse) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
this.#waitingForResponse = true
|
||||
const magic = await this.#readInt32()
|
||||
|
||||
if (magic !== NBD_REPLY_MAGIC) {
|
||||
throw new Error(`magic number for block answer is wrong : ${magic} ${NBD_REPLY_MAGIC}`)
|
||||
}
|
||||
|
||||
const error = await this.#readInt32()
|
||||
if (error !== 0) {
|
||||
// @todo use error code from constants.mjs
|
||||
throw new Error(`GOT ERROR CODE : ${error}`)
|
||||
}
|
||||
|
||||
const blockQueryId = await this.#readInt64()
|
||||
const query = this.#commandQueryBacklog.get(blockQueryId)
|
||||
if (!query) {
|
||||
throw new Error(` no query associated with id ${blockQueryId}`)
|
||||
}
|
||||
this.#commandQueryBacklog.delete(blockQueryId)
|
||||
const data = await this.#read(query.size)
|
||||
query.resolve(data)
|
||||
this.#waitingForResponse = false
|
||||
if (this.#commandQueryBacklog.size > 0) {
|
||||
await this.#readBlockResponse()
|
||||
}
|
||||
} catch (error) {
|
||||
// reject all the promises
|
||||
// we don't need to call readBlockResponse on failure
|
||||
// since we will empty the backlog
|
||||
await this.#rejectAll(error)
|
||||
}
|
||||
}
|
||||
|
||||
async readBlock(index, size = NBD_DEFAULT_BLOCK_SIZE) {
|
||||
const queryId = this.#nextCommandQueryId
|
||||
this.#nextCommandQueryId++
|
||||
|
||||
// create and send command at once to ensure there is no concurrency issue
|
||||
const buffer = Buffer.alloc(28)
|
||||
buffer.writeInt32BE(NBD_REQUEST_MAGIC, 0) // it is a nbd request
|
||||
buffer.writeInt16BE(0, 4) // no command flags for a simple block read
|
||||
buffer.writeInt16BE(NBD_CMD_READ, 6) // we want to read a data block
|
||||
buffer.writeBigUInt64BE(queryId, 8)
|
||||
// byte offset in the raw disk
|
||||
buffer.writeBigUInt64BE(BigInt(index) * BigInt(size), 16)
|
||||
buffer.writeInt32BE(size, 24)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// this will handle one block response, but it can be another block
|
||||
// since server does not guaranty to handle query in order
|
||||
this.#commandQueryBacklog.set(queryId, {
|
||||
size,
|
||||
resolve,
|
||||
reject,
|
||||
})
|
||||
// really send the command to the server
|
||||
this.#write(buffer).catch(reject)
|
||||
|
||||
// #readBlockResponse never throws directly
|
||||
// but if it fails it will reject all the promises in the backlog
|
||||
this.#readBlockResponse()
|
||||
})
|
||||
}
|
||||
}
|
||||
76
@vates/nbd-client/nbdclient.spec.js
Normal file
76
@vates/nbd-client/nbdclient.spec.js
Normal file
@@ -0,0 +1,76 @@
|
||||
'use strict'
|
||||
const NbdClient = require('./index.js')
|
||||
const { spawn } = require('node:child_process')
|
||||
const fs = require('node:fs/promises')
|
||||
const { test } = require('tap')
|
||||
const tmp = require('tmp')
|
||||
const { pFromCallback } = require('promise-toolbox')
|
||||
const { asyncEach } = require('@vates/async-each')
|
||||
|
||||
const FILE_SIZE = 2 * 1024 * 1024
|
||||
|
||||
async function createTempFile(size) {
|
||||
const tmpPath = await pFromCallback(cb => tmp.file(cb))
|
||||
const data = Buffer.alloc(size, 0)
|
||||
for (let i = 0; i < size; i += 4) {
|
||||
data.writeUInt32BE(i, i)
|
||||
}
|
||||
await fs.writeFile(tmpPath, data)
|
||||
|
||||
return tmpPath
|
||||
}
|
||||
|
||||
test('it works with unsecured network', async tap => {
|
||||
const path = await createTempFile(FILE_SIZE)
|
||||
|
||||
const nbdServer = spawn(
|
||||
'nbdkit',
|
||||
[
|
||||
'file',
|
||||
path,
|
||||
'--newstyle', //
|
||||
'--exit-with-parent',
|
||||
'--read-only',
|
||||
'--export-name=MY_SECRET_EXPORT',
|
||||
],
|
||||
{
|
||||
stdio: ['inherit', 'inherit', 'inherit'],
|
||||
}
|
||||
)
|
||||
|
||||
const client = new NbdClient({
|
||||
address: 'localhost',
|
||||
exportname: 'MY_SECRET_EXPORT',
|
||||
secure: false,
|
||||
})
|
||||
|
||||
await client.connect()
|
||||
tap.equal(client.exportSize, BigInt(FILE_SIZE))
|
||||
const CHUNK_SIZE = 128 * 1024 // non default size
|
||||
const indexes = []
|
||||
for (let i = 0; i < FILE_SIZE / CHUNK_SIZE; i++) {
|
||||
indexes.push(i)
|
||||
}
|
||||
// read mutiple blocks in parallel
|
||||
await asyncEach(
|
||||
indexes,
|
||||
async i => {
|
||||
const block = await client.readBlock(i, CHUNK_SIZE)
|
||||
let blockOk = true
|
||||
let firstFail
|
||||
for (let j = 0; j < CHUNK_SIZE; j += 4) {
|
||||
const wanted = i * CHUNK_SIZE + j
|
||||
const found = block.readUInt32BE(j)
|
||||
blockOk = blockOk && found === wanted
|
||||
if (!blockOk && firstFail === undefined) {
|
||||
firstFail = j
|
||||
}
|
||||
}
|
||||
tap.ok(blockOk, `check block ${i} content`)
|
||||
},
|
||||
{ concurrency: 8 }
|
||||
)
|
||||
await client.disconnect()
|
||||
nbdServer.kill()
|
||||
await fs.unlink(path)
|
||||
})
|
||||
35
@vates/nbd-client/package.json
Normal file
35
@vates/nbd-client/package.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "@vates/nbd-client",
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/nbd-client",
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"repository": {
|
||||
"directory": "@vates/nbd-client",
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Vates SAS",
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"license": "ISC",
|
||||
"version": "1.0.0",
|
||||
"engines": {
|
||||
"node": ">=14.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vates/async-each": "^1.0.0",
|
||||
"@vates/read-chunk": "^1.0.1",
|
||||
"@xen-orchestra/async-map": "^0.1.2",
|
||||
"promise-toolbox": "^0.21.0",
|
||||
"xen-api": "^1.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tap": "^16.3.0",
|
||||
"tmp": "^0.2.1"
|
||||
},
|
||||
"scripts": {
|
||||
"postversion": "npm publish --access public",
|
||||
"test-integration": "tap *.spec.js"
|
||||
}
|
||||
}
|
||||
130
@vates/otp/.USAGE.md
Normal file
130
@vates/otp/.USAGE.md
Normal file
@@ -0,0 +1,130 @@
|
||||
### Usual workflow
|
||||
|
||||
> This section presents how this library should be used to implement a classic two factor authentification.
|
||||
|
||||
#### Setup
|
||||
|
||||
```js
|
||||
import { generateSecret, generateTotp } from '@vates/otp'
|
||||
import QrCode from 'qrcode'
|
||||
|
||||
// Generates a secret that will be shared by both the service and the user:
|
||||
const secret = generateSecret()
|
||||
|
||||
// Stores the secret in the service:
|
||||
await currentUser.saveOtpSecret(secret)
|
||||
|
||||
// Generates an URI to present to the user
|
||||
const uri = generateTotpUri({ secret })
|
||||
|
||||
// Generates the QR code from the URI to make it easily importable in Authy or Google Authenticator
|
||||
const qr = await QrCode.toDataURL(uri)
|
||||
```
|
||||
|
||||
#### Authentication
|
||||
|
||||
```js
|
||||
import { verifyTotp } from '@vates/otp'
|
||||
|
||||
// Verifies a `token` entered by the user against a `secret` generated during setup.
|
||||
if (await verifyTotp(token, { secret })) {
|
||||
console.log('authenticated!')
|
||||
}
|
||||
```
|
||||
|
||||
### API
|
||||
|
||||
#### Secret
|
||||
|
||||
```js
|
||||
import { generateSecret } from '@vates/otp'
|
||||
|
||||
const secret = generateSecret()
|
||||
// 'OJOKA65RY5FQQ2RYWVKD5Y3YG5CSHGYH'
|
||||
```
|
||||
|
||||
#### HOTP
|
||||
|
||||
> This is likely not what you want to use, see TOTP below instead.
|
||||
|
||||
```js
|
||||
import { generateHotp, generateHotpUri, verifyHotp } from '@vates/otp'
|
||||
|
||||
// a sequence number, see HOTP specification
|
||||
const counter = 0
|
||||
|
||||
// generate a token
|
||||
//
|
||||
// optional params:
|
||||
// - digits
|
||||
const token = await generateHotp({ counter, secret })
|
||||
// '239988'
|
||||
|
||||
// verify a token
|
||||
//
|
||||
// optional params:
|
||||
// - digits
|
||||
const isValid = await verifyHotp(token, { counter, secret })
|
||||
// true
|
||||
|
||||
// generate a URI than can be displayed as a QR code to be used with Authy or Google Authenticator
|
||||
//
|
||||
// optional params:
|
||||
// - digits
|
||||
const uri = generateHotpUri({ counter, label: 'account name', issuer: 'my app', secret })
|
||||
// 'otpauth://hotp/my%20app:account%20name?counter=0&issuer=my%20app&secret=OJOKA65RY5FQQ2RYWVKD5Y3YG5CSHGYH'
|
||||
```
|
||||
|
||||
Optional params and their default values:
|
||||
|
||||
- `digits = 6`: length of the token, avoid using it because not compatible with Google Authenticator
|
||||
|
||||
#### TOTP
|
||||
|
||||
```js
|
||||
import { generateTotp, generateTotpUri, verifyTotp } from '@vates/otp'
|
||||
|
||||
// generate a token
|
||||
//
|
||||
// optional params:
|
||||
// - digits
|
||||
// - period
|
||||
// - timestamp
|
||||
const token = await generateTotp({ secret })
|
||||
// '632869'
|
||||
|
||||
// verify a token
|
||||
//
|
||||
// optional params:
|
||||
// - digits
|
||||
// - period
|
||||
// - timestamp
|
||||
// - window
|
||||
const isValid = await verifyTotp(token, { secret })
|
||||
// true
|
||||
|
||||
// generate a URI than can be displayed as a QR code to be used with Authy or Google Authenticator
|
||||
//
|
||||
// optional params:
|
||||
// - digits
|
||||
// - period
|
||||
const uri = generateTotpUri({ label: 'account name', issuer: 'my app', secret })
|
||||
// 'otpauth://totp/my%20app:account%20name?issuer=my%20app&secret=OJOKA65RY5FQQ2RYWVKD5Y3YG5CSHGYH'
|
||||
```
|
||||
|
||||
Optional params and their default values:
|
||||
|
||||
- `digits = 6`: length of the token, avoid using it because not compatible with Google Authenticator
|
||||
- `period = 30`: number of seconds a token is valid
|
||||
- `timestamp = Date.now() / 1e3`: Unix timestamp, in seconds, when this token will be valid, default to now
|
||||
- `window = 1`: number of periods before and after `timestamp` for which the token is considered valid
|
||||
|
||||
#### Verification from URI
|
||||
|
||||
```js
|
||||
import { verifyFromUri } from '@vates/otp'
|
||||
|
||||
// Verify the token using all the information contained in the URI
|
||||
const isValid = await verifyFromUri(token, uri)
|
||||
// true
|
||||
```
|
||||
1
@vates/otp/.npmignore
Symbolic link
1
@vates/otp/.npmignore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../scripts/npmignore
|
||||
163
@vates/otp/README.md
Normal file
163
@vates/otp/README.md
Normal file
@@ -0,0 +1,163 @@
|
||||
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
|
||||
|
||||
# @vates/otp
|
||||
|
||||
[](https://npmjs.org/package/@vates/otp)  [](https://bundlephobia.com/result?p=@vates/otp) [](https://npmjs.org/package/@vates/otp)
|
||||
|
||||
> Minimal HTOP/TOTP implementation
|
||||
|
||||
## Install
|
||||
|
||||
Installation of the [npm package](https://npmjs.org/package/@vates/otp):
|
||||
|
||||
```
|
||||
> npm install --save @vates/otp
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Usual workflow
|
||||
|
||||
> This section presents how this library should be used to implement a classic two factor authentification.
|
||||
|
||||
#### Setup
|
||||
|
||||
```js
|
||||
import { generateSecret, generateTotp } from '@vates/otp'
|
||||
import QrCode from 'qrcode'
|
||||
|
||||
// Generates a secret that will be shared by both the service and the user:
|
||||
const secret = generateSecret()
|
||||
|
||||
// Stores the secret in the service:
|
||||
await currentUser.saveOtpSecret(secret)
|
||||
|
||||
// Generates an URI to present to the user
|
||||
const uri = generateTotpUri({ secret })
|
||||
|
||||
// Generates the QR code from the URI to make it easily importable in Authy or Google Authenticator
|
||||
const qr = await QrCode.toDataURL(uri)
|
||||
```
|
||||
|
||||
#### Authentication
|
||||
|
||||
```js
|
||||
import { verifyTotp } from '@vates/otp'
|
||||
|
||||
// Verifies a `token` entered by the user against a `secret` generated during setup.
|
||||
if (await verifyTotp(token, { secret })) {
|
||||
console.log('authenticated!')
|
||||
}
|
||||
```
|
||||
|
||||
### API
|
||||
|
||||
#### Secret
|
||||
|
||||
```js
|
||||
import { generateSecret } from '@vates/otp'
|
||||
|
||||
const secret = generateSecret()
|
||||
// 'OJOKA65RY5FQQ2RYWVKD5Y3YG5CSHGYH'
|
||||
```
|
||||
|
||||
#### HOTP
|
||||
|
||||
> This is likely not what you want to use, see TOTP below instead.
|
||||
|
||||
```js
|
||||
import { generateHotp, generateHotpUri, verifyHotp } from '@vates/otp'
|
||||
|
||||
// a sequence number, see HOTP specification
|
||||
const counter = 0
|
||||
|
||||
// generate a token
|
||||
//
|
||||
// optional params:
|
||||
// - digits
|
||||
const token = await generateHotp({ counter, secret })
|
||||
// '239988'
|
||||
|
||||
// verify a token
|
||||
//
|
||||
// optional params:
|
||||
// - digits
|
||||
const isValid = await verifyHotp(token, { counter, secret })
|
||||
// true
|
||||
|
||||
// generate a URI than can be displayed as a QR code to be used with Authy or Google Authenticator
|
||||
//
|
||||
// optional params:
|
||||
// - digits
|
||||
const uri = generateHotpUri({ counter, label: 'account name', issuer: 'my app', secret })
|
||||
// 'otpauth://hotp/my%20app:account%20name?counter=0&issuer=my%20app&secret=OJOKA65RY5FQQ2RYWVKD5Y3YG5CSHGYH'
|
||||
```
|
||||
|
||||
Optional params and their default values:
|
||||
|
||||
- `digits = 6`: length of the token, avoid using it because not compatible with Google Authenticator
|
||||
|
||||
#### TOTP
|
||||
|
||||
```js
|
||||
import { generateTotp, generateTotpUri, verifyTotp } from '@vates/otp'
|
||||
|
||||
// generate a token
|
||||
//
|
||||
// optional params:
|
||||
// - digits
|
||||
// - period
|
||||
// - timestamp
|
||||
const token = await generateTotp({ secret })
|
||||
// '632869'
|
||||
|
||||
// verify a token
|
||||
//
|
||||
// optional params:
|
||||
// - digits
|
||||
// - period
|
||||
// - timestamp
|
||||
// - window
|
||||
const isValid = await verifyTotp(token, { secret })
|
||||
// true
|
||||
|
||||
// generate a URI than can be displayed as a QR code to be used with Authy or Google Authenticator
|
||||
//
|
||||
// optional params:
|
||||
// - digits
|
||||
// - period
|
||||
const uri = generateTotpUri({ label: 'account name', issuer: 'my app', secret })
|
||||
// 'otpauth://totp/my%20app:account%20name?issuer=my%20app&secret=OJOKA65RY5FQQ2RYWVKD5Y3YG5CSHGYH'
|
||||
```
|
||||
|
||||
Optional params and their default values:
|
||||
|
||||
- `digits = 6`: length of the token, avoid using it because not compatible with Google Authenticator
|
||||
- `period = 30`: number of seconds a token is valid
|
||||
- `timestamp = Date.now() / 1e3`: Unix timestamp, in seconds, when this token will be valid, default to now
|
||||
- `window = 1`: number of periods before and after `timestamp` for which the token is considered valid
|
||||
|
||||
#### Verification from URI
|
||||
|
||||
```js
|
||||
import { verifyFromUri } from '@vates/otp'
|
||||
|
||||
// Verify the token using all the information contained in the URI
|
||||
const isValid = await verifyFromUri(token, uri)
|
||||
// true
|
||||
```
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions are _very_ welcomed, either on the documentation or on
|
||||
the code.
|
||||
|
||||
You may:
|
||||
|
||||
- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
|
||||
you've encountered;
|
||||
- fork and create a pull request.
|
||||
|
||||
## License
|
||||
|
||||
[ISC](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)
|
||||
111
@vates/otp/index.mjs
Normal file
111
@vates/otp/index.mjs
Normal file
@@ -0,0 +1,111 @@
|
||||
import { base32 } from 'rfc4648'
|
||||
import { webcrypto } from 'node:crypto'
|
||||
|
||||
const { subtle } = webcrypto
|
||||
|
||||
function assert(name, value) {
|
||||
if (!value) {
|
||||
throw new TypeError('invalid value for param ' + name)
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/google/google-authenticator/wiki/Key-Uri-Format
|
||||
function generateUri(protocol, label, params) {
|
||||
assert('label', typeof label === 'string')
|
||||
assert('secret', typeof params.secret === 'string')
|
||||
|
||||
let path = encodeURIComponent(label)
|
||||
|
||||
const { issuer } = params
|
||||
if (issuer !== undefined) {
|
||||
path = encodeURIComponent(issuer) + ':' + path
|
||||
}
|
||||
|
||||
const query = Object.entries(params)
|
||||
.filter(_ => _[1] !== undefined)
|
||||
.map(([key, value]) => key + '=' + encodeURIComponent(value))
|
||||
.join('&')
|
||||
|
||||
return `otpauth://${protocol}/${path}?${query}`
|
||||
}
|
||||
|
||||
export function generateSecret() {
|
||||
// https://www.rfc-editor.org/rfc/rfc4226 recommends 160 bits (i.e. 20 bytes)
|
||||
const data = new Uint8Array(20)
|
||||
webcrypto.getRandomValues(data)
|
||||
return base32.stringify(data, { pad: false })
|
||||
}
|
||||
|
||||
const DIGITS = 6
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc4226
|
||||
export async function generateHotp({ counter, digits = DIGITS, secret }) {
|
||||
const data = new Uint8Array(8)
|
||||
new DataView(data.buffer).setBigInt64(0, BigInt(counter), false)
|
||||
|
||||
const key = await subtle.importKey(
|
||||
'raw',
|
||||
base32.parse(secret, { loose: true }),
|
||||
{ name: 'HMAC', hash: 'SHA-1' },
|
||||
false,
|
||||
['sign', 'verify']
|
||||
)
|
||||
const digest = new DataView(await subtle.sign('HMAC', key, data))
|
||||
|
||||
const offset = digest.getUint8(digest.byteLength - 1) & 0xf
|
||||
const p = digest.getUint32(offset) & 0x7f_ff_ff_ff
|
||||
|
||||
return String(p % Math.pow(10, digits)).padStart(digits, '0')
|
||||
}
|
||||
|
||||
export function generateHotpUri({ counter, digits, issuer, label, secret }) {
|
||||
assert('counter', typeof counter === 'number')
|
||||
return generateUri('hotp', label, { counter, digits, issuer, secret })
|
||||
}
|
||||
|
||||
export async function verifyHotp(token, opts) {
|
||||
return token === (await generateHotp(opts))
|
||||
}
|
||||
|
||||
function totpCounter(period = 30, timestamp = Math.floor(Date.now() / 1e3)) {
|
||||
return Math.floor(timestamp / period)
|
||||
}
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc6238.html
|
||||
export async function generateTotp({ period, timestamp, ...opts }) {
|
||||
opts.counter = totpCounter(period, timestamp)
|
||||
return await generateHotp(opts)
|
||||
}
|
||||
|
||||
export function generateTotpUri({ digits, issuer, label, period, secret }) {
|
||||
return generateUri('totp', label, { digits, issuer, period, secret })
|
||||
}
|
||||
|
||||
export async function verifyTotp(token, { period, timestamp, window = 1, ...opts }) {
|
||||
const counter = totpCounter(period, timestamp)
|
||||
const end = counter + window
|
||||
opts.counter = counter - window
|
||||
while (opts.counter <= end) {
|
||||
if (token === (await generateHotp(opts))) {
|
||||
return true
|
||||
}
|
||||
opts.counter += 1
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export async function verifyFromUri(token, uri) {
|
||||
const url = new URL(uri)
|
||||
assert('protocol', url.protocol === 'otpauth:')
|
||||
|
||||
const { host } = url
|
||||
const opts = Object.fromEntries(url.searchParams.entries())
|
||||
if (host === 'hotp') {
|
||||
return await verifyHotp(token, opts)
|
||||
}
|
||||
if (host === 'totp') {
|
||||
return await verifyTotp(token, opts)
|
||||
}
|
||||
|
||||
assert('host', false)
|
||||
}
|
||||
112
@vates/otp/index.spec.mjs
Normal file
112
@vates/otp/index.spec.mjs
Normal file
@@ -0,0 +1,112 @@
|
||||
import { strict as assert } from 'node:assert'
|
||||
import { describe, it } from 'tap/mocha'
|
||||
|
||||
import {
|
||||
generateHotp,
|
||||
generateHotpUri,
|
||||
generateSecret,
|
||||
generateTotp,
|
||||
generateTotpUri,
|
||||
verifyHotp,
|
||||
verifyTotp,
|
||||
} from './index.mjs'
|
||||
|
||||
describe('generateSecret', function () {
|
||||
it('generates a string of 32 chars', async function () {
|
||||
const secret = generateSecret()
|
||||
assert.equal(typeof secret, 'string')
|
||||
assert.equal(secret.length, 32)
|
||||
})
|
||||
|
||||
it('generates a different secret at each call', async function () {
|
||||
assert.notEqual(generateSecret(), generateSecret())
|
||||
})
|
||||
})
|
||||
|
||||
describe('HOTP', function () {
|
||||
it('generate and verify valid tokens', async function () {
|
||||
for (const [token, opts] of Object.entries({
|
||||
382752: {
|
||||
counter: -3088,
|
||||
secret: 'PJYFSZ3JNVXVQMZXOB2EQYJSKB2HE6TB',
|
||||
},
|
||||
163376: {
|
||||
counter: 30598,
|
||||
secret: 'GBUDQZ3UKZZGIMRLNVYXA33GMFMEGQKN',
|
||||
},
|
||||
})) {
|
||||
assert.equal(await generateHotp(opts), token)
|
||||
assert(await verifyHotp(token, opts))
|
||||
}
|
||||
})
|
||||
|
||||
describe('generateHotpUri', function () {
|
||||
const opts = {
|
||||
counter: 59732,
|
||||
label: 'the label',
|
||||
secret: 'OGK45BBZAIGNGELHZPXYKN4GUVWWO6YX',
|
||||
}
|
||||
|
||||
Object.entries({
|
||||
'without optional params': [
|
||||
opts,
|
||||
'otpauth://hotp/the%20label?counter=59732&secret=OGK45BBZAIGNGELHZPXYKN4GUVWWO6YX',
|
||||
],
|
||||
'with issuer': [
|
||||
{ ...opts, issuer: 'the issuer' },
|
||||
'otpauth://hotp/the%20issuer:the%20label?counter=59732&issuer=the%20issuer&secret=OGK45BBZAIGNGELHZPXYKN4GUVWWO6YX',
|
||||
],
|
||||
'with digits': [
|
||||
{ ...opts, digits: 7 },
|
||||
'otpauth://hotp/the%20label?counter=59732&digits=7&secret=OGK45BBZAIGNGELHZPXYKN4GUVWWO6YX',
|
||||
],
|
||||
}).forEach(([title, [opts, uri]]) => {
|
||||
it(title, async function () {
|
||||
assert.strictEqual(generateHotpUri(opts), uri)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('TOTP', function () {
|
||||
Object.entries({
|
||||
'033702': {
|
||||
secret: 'PJYFSZ3JNVXVQMZXOB2EQYJSKB2HE6TB',
|
||||
timestamp: 1665416296,
|
||||
period: 30,
|
||||
},
|
||||
107250: {
|
||||
secret: 'GBUDQZ3UKZZGIMRLNVYXA33GMFMEGQKN',
|
||||
timestamp: 1665416674,
|
||||
period: 60,
|
||||
},
|
||||
}).forEach(([token, opts]) => {
|
||||
it('works', async function () {
|
||||
assert.equal(await generateTotp(opts), token)
|
||||
assert(await verifyTotp(token, opts))
|
||||
})
|
||||
})
|
||||
|
||||
describe('generateHotpUri', function () {
|
||||
const opts = {
|
||||
label: 'the label',
|
||||
secret: 'OGK45BBZAIGNGELHZPXYKN4GUVWWO6YX',
|
||||
}
|
||||
|
||||
Object.entries({
|
||||
'without optional params': [opts, 'otpauth://totp/the%20label?secret=OGK45BBZAIGNGELHZPXYKN4GUVWWO6YX'],
|
||||
'with issuer': [
|
||||
{ ...opts, issuer: 'the issuer' },
|
||||
'otpauth://totp/the%20issuer:the%20label?issuer=the%20issuer&secret=OGK45BBZAIGNGELHZPXYKN4GUVWWO6YX',
|
||||
],
|
||||
'with digits': [
|
||||
{ ...opts, digits: 7 },
|
||||
'otpauth://totp/the%20label?digits=7&secret=OGK45BBZAIGNGELHZPXYKN4GUVWWO6YX',
|
||||
],
|
||||
}).forEach(([title, [opts, uri]]) => {
|
||||
it(title, async function () {
|
||||
assert.strictEqual(generateTotpUri(opts), uri)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
39
@vates/otp/package.json
Normal file
39
@vates/otp/package.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "@vates/otp",
|
||||
"description": "Minimal HTOP/TOTP implementation",
|
||||
"keywords": [
|
||||
"2fa",
|
||||
"authenticator",
|
||||
"hotp",
|
||||
"otp",
|
||||
"totp"
|
||||
],
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/otp",
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"main": "index.mjs",
|
||||
"repository": {
|
||||
"directory": "@vates/otp",
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Vates SAS",
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"license": "ISC",
|
||||
"version": "1.0.0",
|
||||
"engines": {
|
||||
"node": ">=15"
|
||||
},
|
||||
"dependencies": {
|
||||
"rfc4648": "^1.5.2"
|
||||
},
|
||||
"scripts": {
|
||||
"postversion": "npm publish --access public",
|
||||
"test": "tap"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tap": "^16.3.0"
|
||||
}
|
||||
}
|
||||
14
@vates/parse-duration/.USAGE.md
Normal file
14
@vates/parse-duration/.USAGE.md
Normal file
@@ -0,0 +1,14 @@
|
||||
`ms` without magic: always parse a duration and throws if invalid.
|
||||
|
||||
```js
|
||||
import { parseDuration } from '@vates/parse-duration'
|
||||
|
||||
parseDuration('2 days')
|
||||
// 172800000
|
||||
|
||||
parseDuration(172800000)
|
||||
// 172800000
|
||||
|
||||
parseDuration(undefined)
|
||||
// throws TypeError('not a valid duration: undefined')
|
||||
```
|
||||
1
@vates/parse-duration/.npmignore
Symbolic link
1
@vates/parse-duration/.npmignore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../scripts/npmignore
|
||||
47
@vates/parse-duration/README.md
Normal file
47
@vates/parse-duration/README.md
Normal file
@@ -0,0 +1,47 @@
|
||||
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
|
||||
|
||||
# @vates/parse-duration
|
||||
|
||||
[](https://npmjs.org/package/@vates/parse-duration)  [](https://bundlephobia.com/result?p=@vates/parse-duration) [](https://npmjs.org/package/@vates/parse-duration)
|
||||
|
||||
> Small wrapper around ms to parse a duration
|
||||
|
||||
## Install
|
||||
|
||||
Installation of the [npm package](https://npmjs.org/package/@vates/parse-duration):
|
||||
|
||||
```
|
||||
> npm install --save @vates/parse-duration
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
`ms` without magic: always parse a duration and throws if invalid.
|
||||
|
||||
```js
|
||||
import { parseDuration } from '@vates/parse-duration'
|
||||
|
||||
parseDuration('2 days')
|
||||
// 172800000
|
||||
|
||||
parseDuration(172800000)
|
||||
// 172800000
|
||||
|
||||
parseDuration(undefined)
|
||||
// throws TypeError('not a valid duration: undefined')
|
||||
```
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions are _very_ welcomed, either on the documentation or on
|
||||
the code.
|
||||
|
||||
You may:
|
||||
|
||||
- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
|
||||
you've encountered;
|
||||
- fork and create a pull request.
|
||||
|
||||
## License
|
||||
|
||||
[ISC](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)
|
||||
14
@vates/parse-duration/index.js
Normal file
14
@vates/parse-duration/index.js
Normal file
@@ -0,0 +1,14 @@
|
||||
'use strict'
|
||||
|
||||
const ms = require('ms')
|
||||
|
||||
exports.parseDuration = value => {
|
||||
if (typeof value === 'number') {
|
||||
return value
|
||||
}
|
||||
const duration = ms(value)
|
||||
if (duration === undefined) {
|
||||
throw new TypeError(`not a valid duration: ${value}`)
|
||||
}
|
||||
return duration
|
||||
}
|
||||
32
@vates/parse-duration/package.json
Normal file
32
@vates/parse-duration/package.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "@vates/parse-duration",
|
||||
"description": "Small wrapper around ms to parse a duration",
|
||||
"keywords": [
|
||||
"duration",
|
||||
"ms",
|
||||
"parse"
|
||||
],
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/parse-duration",
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"repository": {
|
||||
"directory": "@vates/parse-duration",
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Vates SAS",
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"license": "ISC",
|
||||
"version": "0.1.1",
|
||||
"engines": {
|
||||
"node": ">=8.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"ms": "^2.1.2"
|
||||
},
|
||||
"scripts": {
|
||||
"postversion": "npm publish --access public"
|
||||
}
|
||||
}
|
||||
72
@vates/predicates/.USAGE.md
Normal file
72
@vates/predicates/.USAGE.md
Normal file
@@ -0,0 +1,72 @@
|
||||
`undefined` predicates are ignored and `undefined` is returned if all predicates are `undefined`, this permits the most efficient composition:
|
||||
|
||||
```js
|
||||
const compositePredicate = not(every(undefined, some(not(predicate2), undefined)))
|
||||
|
||||
// ends up as
|
||||
|
||||
const compositePredicate = predicate2
|
||||
```
|
||||
|
||||
Predicates can also be passed wrapped in an array:
|
||||
|
||||
```js
|
||||
const compositePredicate = every([predicate1, some([predicate2, predicate3])])
|
||||
```
|
||||
|
||||
`this` and all arguments are passed to the nested predicates.
|
||||
|
||||
### `every(predicates)`
|
||||
|
||||
> Returns a predicate that returns `true` iff every predicate returns `true`.
|
||||
|
||||
```js
|
||||
const isBetween3And7 = every(
|
||||
n => n >= 3,
|
||||
n => n <= 7
|
||||
)
|
||||
|
||||
isBetween3And10(0)
|
||||
// → false
|
||||
|
||||
isBetween3And10(5)
|
||||
// → true
|
||||
|
||||
isBetween3And10(10)
|
||||
// → false
|
||||
```
|
||||
|
||||
### `not(predicate)`
|
||||
|
||||
> Returns a predicate that returns the negation of the predicate.
|
||||
|
||||
```js
|
||||
const isEven = n => n % 2 === 0
|
||||
const isOdd = not(isEven)
|
||||
|
||||
isOdd(1)
|
||||
// true
|
||||
|
||||
isOdd(2)
|
||||
// false
|
||||
```
|
||||
|
||||
### `some(predicates)`
|
||||
|
||||
> Returns a predicate that returns `true` iff some predicate returns `true`.
|
||||
|
||||
```js
|
||||
const isAliceOrBob = some(
|
||||
name => name === 'Alice',
|
||||
name => name === 'Bob'
|
||||
)
|
||||
|
||||
isAliceOrBob('Alice')
|
||||
// → true
|
||||
|
||||
isAliceOrBob('Bob')
|
||||
// → true
|
||||
|
||||
isAliceOrBob('Oscar')
|
||||
// → false
|
||||
```
|
||||
1
@vates/predicates/.npmignore
Symbolic link
1
@vates/predicates/.npmignore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../scripts/npmignore
|
||||
105
@vates/predicates/README.md
Normal file
105
@vates/predicates/README.md
Normal file
@@ -0,0 +1,105 @@
|
||||
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
|
||||
|
||||
# @vates/predicates
|
||||
|
||||
[](https://npmjs.org/package/@vates/predicates)  [](https://bundlephobia.com/result?p=@vates/predicates) [](https://npmjs.org/package/@vates/predicates)
|
||||
|
||||
> Utilities to compose predicates
|
||||
|
||||
## Install
|
||||
|
||||
Installation of the [npm package](https://npmjs.org/package/@vates/predicates):
|
||||
|
||||
```
|
||||
> npm install --save @vates/predicates
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
`undefined` predicates are ignored and `undefined` is returned if all predicates are `undefined`, this permits the most efficient composition:
|
||||
|
||||
```js
|
||||
const compositePredicate = not(every(undefined, some(not(predicate2), undefined)))
|
||||
|
||||
// ends up as
|
||||
|
||||
const compositePredicate = predicate2
|
||||
```
|
||||
|
||||
Predicates can also be passed wrapped in an array:
|
||||
|
||||
```js
|
||||
const compositePredicate = every([predicate1, some([predicate2, predicate3])])
|
||||
```
|
||||
|
||||
`this` and all arguments are passed to the nested predicates.
|
||||
|
||||
### `every(predicates)`
|
||||
|
||||
> Returns a predicate that returns `true` iff every predicate returns `true`.
|
||||
|
||||
```js
|
||||
const isBetween3And7 = every(
|
||||
n => n >= 3,
|
||||
n => n <= 7
|
||||
)
|
||||
|
||||
isBetween3And10(0)
|
||||
// → false
|
||||
|
||||
isBetween3And10(5)
|
||||
// → true
|
||||
|
||||
isBetween3And10(10)
|
||||
// → false
|
||||
```
|
||||
|
||||
### `not(predicate)`
|
||||
|
||||
> Returns a predicate that returns the negation of the predicate.
|
||||
|
||||
```js
|
||||
const isEven = n => n % 2 === 0
|
||||
const isOdd = not(isEven)
|
||||
|
||||
isOdd(1)
|
||||
// true
|
||||
|
||||
isOdd(2)
|
||||
// false
|
||||
```
|
||||
|
||||
### `some(predicates)`
|
||||
|
||||
> Returns a predicate that returns `true` iff some predicate returns `true`.
|
||||
|
||||
```js
|
||||
const isAliceOrBob = some(
|
||||
name => name === 'Alice',
|
||||
name => name === 'Bob'
|
||||
)
|
||||
|
||||
isAliceOrBob('Alice')
|
||||
// → true
|
||||
|
||||
isAliceOrBob('Bob')
|
||||
// → true
|
||||
|
||||
isAliceOrBob('Oscar')
|
||||
// → false
|
||||
```
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions are _very_ welcomed, either on the documentation or on
|
||||
the code.
|
||||
|
||||
You may:
|
||||
|
||||
- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
|
||||
you've encountered;
|
||||
- fork and create a pull request.
|
||||
|
||||
## License
|
||||
|
||||
[ISC](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)
|
||||
87
@vates/predicates/index.js
Normal file
87
@vates/predicates/index.js
Normal file
@@ -0,0 +1,87 @@
|
||||
'use strict'
|
||||
|
||||
const {
|
||||
isArray,
|
||||
prototype: { filter },
|
||||
} = Array
|
||||
|
||||
class InvalidPredicate extends TypeError {
|
||||
constructor(value) {
|
||||
super('not a valid predicate')
|
||||
this.value = value
|
||||
}
|
||||
}
|
||||
|
||||
function isDefinedPredicate(value) {
|
||||
if (value === undefined) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (typeof value !== 'function') {
|
||||
throw new InvalidPredicate(value)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function handleArgs() {
|
||||
let predicates
|
||||
if (!(arguments.length === 1 && isArray((predicates = arguments[0])))) {
|
||||
predicates = arguments
|
||||
}
|
||||
return filter.call(predicates, isDefinedPredicate)
|
||||
}
|
||||
|
||||
exports.every = function every() {
|
||||
const predicates = handleArgs.apply(this, arguments)
|
||||
const n = predicates.length
|
||||
if (n === 0) {
|
||||
return
|
||||
}
|
||||
if (n === 1) {
|
||||
return predicates[0]
|
||||
}
|
||||
return function everyPredicate() {
|
||||
for (let i = 0; i < n; ++i) {
|
||||
if (!predicates[i].apply(this, arguments)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
const notPredicateTag = {}
|
||||
exports.not = function not(predicate) {
|
||||
if (isDefinedPredicate(predicate)) {
|
||||
if (predicate.tag === notPredicateTag) {
|
||||
return predicate.predicate
|
||||
}
|
||||
|
||||
function notPredicate() {
|
||||
return !predicate.apply(this, arguments)
|
||||
}
|
||||
notPredicate.predicate = predicate
|
||||
notPredicate.tag = notPredicateTag
|
||||
return notPredicate
|
||||
}
|
||||
}
|
||||
|
||||
exports.some = function some() {
|
||||
const predicates = handleArgs.apply(this, arguments)
|
||||
const n = predicates.length
|
||||
if (n === 0) {
|
||||
return
|
||||
}
|
||||
if (n === 1) {
|
||||
return predicates[0]
|
||||
}
|
||||
return function somePredicate() {
|
||||
for (let i = 0; i < n; ++i) {
|
||||
if (predicates[i].apply(this, arguments)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
85
@vates/predicates/index.spec.js
Normal file
85
@vates/predicates/index.spec.js
Normal file
@@ -0,0 +1,85 @@
|
||||
'use strict'
|
||||
|
||||
const assert = require('assert/strict')
|
||||
const { describe, it } = require('tap').mocha
|
||||
|
||||
const { every, not, some } = require('./')
|
||||
|
||||
const T = () => true
|
||||
const F = () => false
|
||||
|
||||
const testArgHandling = fn => {
|
||||
it('returns undefined if predicate is undefined', () => {
|
||||
assert.equal(fn(undefined), undefined)
|
||||
})
|
||||
|
||||
it('throws if it receives a non-predicate', () => {
|
||||
const error = new TypeError('not a valid predicate')
|
||||
error.value = 3
|
||||
assert.throws(() => fn(3), error)
|
||||
})
|
||||
}
|
||||
|
||||
const testArgsHandling = fn => {
|
||||
testArgHandling(fn)
|
||||
|
||||
it('returns the predicate if only a single one is passed', () => {
|
||||
assert.equal(fn(undefined, T), T)
|
||||
assert.equal(fn([undefined, T]), T)
|
||||
})
|
||||
|
||||
it('forwards this and arguments to predicates', () => {
|
||||
const thisArg = 'qux'
|
||||
const args = ['foo', 'bar', 'baz']
|
||||
const predicate = function () {
|
||||
assert.equal(this, thisArg)
|
||||
assert.deepEqual(Array.from(arguments), args)
|
||||
}
|
||||
fn(predicate, predicate).apply(thisArg, args)
|
||||
})
|
||||
}
|
||||
|
||||
const runTests = (fn, acceptMultiple, truthTable) =>
|
||||
it('works', () => {
|
||||
truthTable.forEach(([result, ...predicates]) => {
|
||||
if (acceptMultiple) {
|
||||
assert.equal(fn(predicates)(), result)
|
||||
} else {
|
||||
assert.equal(predicates.length, 1)
|
||||
}
|
||||
assert.equal(fn(...predicates)(), result)
|
||||
})
|
||||
})
|
||||
|
||||
describe('every', () => {
|
||||
testArgsHandling(every)
|
||||
runTests(every, true, [
|
||||
[true, T, T],
|
||||
[false, T, F],
|
||||
[false, F, T],
|
||||
[false, F, F],
|
||||
])
|
||||
})
|
||||
|
||||
describe('not', () => {
|
||||
testArgHandling(not)
|
||||
|
||||
it('returns the original predicate if negated twice', () => {
|
||||
assert.equal(not(not(T)), T)
|
||||
})
|
||||
|
||||
runTests(not, false, [
|
||||
[true, F],
|
||||
[false, T],
|
||||
])
|
||||
})
|
||||
|
||||
describe('some', () => {
|
||||
testArgsHandling(some)
|
||||
runTests(some, true, [
|
||||
[true, T, T],
|
||||
[true, T, F],
|
||||
[true, F, T],
|
||||
[false, F, F],
|
||||
])
|
||||
})
|
||||
40
@vates/predicates/package.json
Normal file
40
@vates/predicates/package.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "@vates/predicates",
|
||||
"description": "Utilities to compose predicates",
|
||||
"keywords": [
|
||||
"and",
|
||||
"combine",
|
||||
"compose",
|
||||
"every",
|
||||
"function",
|
||||
"functions",
|
||||
"or",
|
||||
"predicate",
|
||||
"predicates",
|
||||
"some"
|
||||
],
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/predicates",
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"repository": {
|
||||
"directory": "@vates/predicates",
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Vates SAS",
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"license": "ISC",
|
||||
"version": "1.1.0",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"scripts": {
|
||||
"postversion": "npm publish --access public",
|
||||
"test": "tap"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tap": "^16.0.1"
|
||||
}
|
||||
}
|
||||
26
@vates/read-chunk/.USAGE.md
Normal file
26
@vates/read-chunk/.USAGE.md
Normal file
@@ -0,0 +1,26 @@
|
||||
### `readChunk(stream, [size])`
|
||||
|
||||
- returns the next available chunk of data
|
||||
- like `stream.read()`, a number of bytes can be specified
|
||||
- returns with less data than expected if stream has ended
|
||||
- returns `null` if the stream has ended and no data has been read
|
||||
|
||||
```js
|
||||
import { readChunk } from '@vates/read-chunk'
|
||||
;(async () => {
|
||||
let chunk
|
||||
while ((chunk = await readChunk(stream, 1024)) !== null) {
|
||||
// do something with chunk
|
||||
}
|
||||
})()
|
||||
```
|
||||
|
||||
### `readChunkStrict(stream, [size])`
|
||||
|
||||
Similar behavior to `readChunk` but throws if the stream ended before the requested data could be read.
|
||||
|
||||
```js
|
||||
import { readChunkStrict } from '@vates/read-chunk'
|
||||
|
||||
const chunk = await readChunkStrict(stream, 1024)
|
||||
```
|
||||
1
@vates/read-chunk/.npmignore
Symbolic link
1
@vates/read-chunk/.npmignore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../scripts/npmignore
|
||||
59
@vates/read-chunk/README.md
Normal file
59
@vates/read-chunk/README.md
Normal file
@@ -0,0 +1,59 @@
|
||||
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
|
||||
|
||||
# @vates/read-chunk
|
||||
|
||||
[](https://npmjs.org/package/@vates/read-chunk)  [](https://bundlephobia.com/result?p=@vates/read-chunk) [](https://npmjs.org/package/@vates/read-chunk)
|
||||
|
||||
> Read a chunk of a Node stream
|
||||
|
||||
## Install
|
||||
|
||||
Installation of the [npm package](https://npmjs.org/package/@vates/read-chunk):
|
||||
|
||||
```
|
||||
> npm install --save @vates/read-chunk
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### `readChunk(stream, [size])`
|
||||
|
||||
- returns the next available chunk of data
|
||||
- like `stream.read()`, a number of bytes can be specified
|
||||
- returns with less data than expected if stream has ended
|
||||
- returns `null` if the stream has ended and no data has been read
|
||||
|
||||
```js
|
||||
import { readChunk } from '@vates/read-chunk'
|
||||
;(async () => {
|
||||
let chunk
|
||||
while ((chunk = await readChunk(stream, 1024)) !== null) {
|
||||
// do something with chunk
|
||||
}
|
||||
})()
|
||||
```
|
||||
|
||||
### `readChunkStrict(stream, [size])`
|
||||
|
||||
Similar behavior to `readChunk` but throws if the stream ended before the requested data could be read.
|
||||
|
||||
```js
|
||||
import { readChunkStrict } from '@vates/read-chunk'
|
||||
|
||||
const chunk = await readChunkStrict(stream, 1024)
|
||||
```
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions are _very_ welcomed, either on the documentation or on
|
||||
the code.
|
||||
|
||||
You may:
|
||||
|
||||
- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
|
||||
you've encountered;
|
||||
- fork and create a pull request.
|
||||
|
||||
## License
|
||||
|
||||
[ISC](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)
|
||||
53
@vates/read-chunk/index.js
Normal file
53
@vates/read-chunk/index.js
Normal file
@@ -0,0 +1,53 @@
|
||||
'use strict'
|
||||
|
||||
const readChunk = (stream, size) =>
|
||||
stream.closed || stream.readableEnded
|
||||
? Promise.resolve(null)
|
||||
: size === 0
|
||||
? Promise.resolve(Buffer.alloc(0))
|
||||
: new Promise((resolve, reject) => {
|
||||
function onEnd() {
|
||||
resolve(null)
|
||||
removeListeners()
|
||||
}
|
||||
function onError(error) {
|
||||
reject(error)
|
||||
removeListeners()
|
||||
}
|
||||
function onReadable() {
|
||||
const data = stream.read(size)
|
||||
if (data !== null) {
|
||||
resolve(data)
|
||||
removeListeners()
|
||||
}
|
||||
}
|
||||
function removeListeners() {
|
||||
stream.removeListener('end', onEnd)
|
||||
stream.removeListener('error', onError)
|
||||
stream.removeListener('readable', onReadable)
|
||||
}
|
||||
stream.on('end', onEnd)
|
||||
stream.on('error', onError)
|
||||
stream.on('readable', onReadable)
|
||||
onReadable()
|
||||
})
|
||||
exports.readChunk = readChunk
|
||||
|
||||
exports.readChunkStrict = async function readChunkStrict(stream, size) {
|
||||
const chunk = await readChunk(stream, size)
|
||||
if (chunk === null) {
|
||||
throw new Error('stream has ended without data')
|
||||
}
|
||||
|
||||
if (size !== undefined && chunk.length !== size) {
|
||||
const error = new Error('stream has ended with not enough data')
|
||||
Object.defineProperties(error, {
|
||||
chunk: {
|
||||
value: chunk,
|
||||
},
|
||||
})
|
||||
throw error
|
||||
}
|
||||
|
||||
return chunk
|
||||
}
|
||||
77
@vates/read-chunk/index.test.js
Normal file
77
@vates/read-chunk/index.test.js
Normal file
@@ -0,0 +1,77 @@
|
||||
'use strict'
|
||||
|
||||
const { describe, it } = require('test')
|
||||
const assert = require('node:assert').strict
|
||||
|
||||
const { Readable } = require('stream')
|
||||
|
||||
const { readChunk, readChunkStrict } = require('./')
|
||||
|
||||
const makeStream = it => Readable.from(it, { objectMode: false })
|
||||
makeStream.obj = Readable.from
|
||||
|
||||
describe('readChunk', () => {
|
||||
it('returns null if stream is empty', async () => {
|
||||
assert.strictEqual(await readChunk(makeStream([])), null)
|
||||
})
|
||||
|
||||
it('returns null if the stream is already ended', async () => {
|
||||
const stream = await makeStream([])
|
||||
await readChunk(stream)
|
||||
|
||||
assert.strictEqual(await readChunk(stream), null)
|
||||
})
|
||||
|
||||
describe('with binary stream', () => {
|
||||
it('returns the first chunk of data', async () => {
|
||||
assert.deepEqual(await readChunk(makeStream(['foo', 'bar'])), Buffer.from('foo'))
|
||||
})
|
||||
|
||||
it('returns a chunk of the specified size (smaller than first)', async () => {
|
||||
assert.deepEqual(await readChunk(makeStream(['foo', 'bar']), 2), Buffer.from('fo'))
|
||||
})
|
||||
|
||||
it('returns a chunk of the specified size (larger than first)', async () => {
|
||||
assert.deepEqual(await readChunk(makeStream(['foo', 'bar']), 4), Buffer.from('foob'))
|
||||
})
|
||||
|
||||
it('returns less data if stream ends', async () => {
|
||||
assert.deepEqual(await readChunk(makeStream(['foo', 'bar']), 10), Buffer.from('foobar'))
|
||||
})
|
||||
|
||||
it('returns an empty buffer if the specified size is 0', async () => {
|
||||
assert.deepEqual(await readChunk(makeStream(['foo', 'bar']), 0), Buffer.alloc(0))
|
||||
})
|
||||
})
|
||||
|
||||
describe('with object stream', () => {
|
||||
it('returns the first chunk of data verbatim', async () => {
|
||||
const chunks = [{}, {}]
|
||||
assert.strictEqual(await readChunk(makeStream.obj(chunks)), chunks[0])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
const rejectionOf = promise =>
|
||||
promise.then(
|
||||
value => {
|
||||
throw value
|
||||
},
|
||||
error => error
|
||||
)
|
||||
|
||||
describe('readChunkStrict', function () {
|
||||
it('throws if stream is empty', async () => {
|
||||
const error = await rejectionOf(readChunkStrict(makeStream([])))
|
||||
assert(error instanceof Error)
|
||||
assert.strictEqual(error.message, 'stream has ended without data')
|
||||
assert.strictEqual(error.chunk, undefined)
|
||||
})
|
||||
|
||||
it('throws if stream ends with not enough data', async () => {
|
||||
const error = await rejectionOf(readChunkStrict(makeStream(['foo', 'bar']), 10))
|
||||
assert(error instanceof Error)
|
||||
assert.strictEqual(error.message, 'stream has ended with not enough data')
|
||||
assert.deepEqual(error.chunk, Buffer.from('foobar'))
|
||||
})
|
||||
})
|
||||
37
@vates/read-chunk/package.json
Normal file
37
@vates/read-chunk/package.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "@vates/read-chunk",
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/read-chunk",
|
||||
"description": "Read a chunk of a Node stream",
|
||||
"license": "ISC",
|
||||
"keywords": [
|
||||
"async",
|
||||
"chunk",
|
||||
"data",
|
||||
"node",
|
||||
"promise",
|
||||
"read",
|
||||
"stream"
|
||||
],
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"repository": {
|
||||
"directory": "@vates/read-chunk",
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"version": "1.0.1",
|
||||
"engines": {
|
||||
"node": ">=8.10"
|
||||
},
|
||||
"scripts": {
|
||||
"postversion": "npm publish --access public",
|
||||
"test": "node--test"
|
||||
},
|
||||
"author": {
|
||||
"name": "Vates SAS",
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"devDependencies": {
|
||||
"test": "^3.2.1"
|
||||
}
|
||||
}
|
||||
54
@vates/task/.USAGE.md
Normal file
54
@vates/task/.USAGE.md
Normal file
@@ -0,0 +1,54 @@
|
||||
```js
|
||||
import { Task } from '@vates/task'
|
||||
|
||||
const task = new Task({
|
||||
name: 'my task',
|
||||
|
||||
// if defined, a new detached task is created
|
||||
//
|
||||
// if not defined and created inside an existing task, the new task is considered a subtask
|
||||
onProgress(event) {
|
||||
// this function is called each time this task or one of it's subtasks change state
|
||||
const { id, timestamp, type } = event
|
||||
if (type === 'start') {
|
||||
const { name, parentId } = event
|
||||
} else if (type === 'end') {
|
||||
const { result, status } = event
|
||||
} else if (type === 'info' || type === 'warning') {
|
||||
const { data, message } = event
|
||||
} else if (type === 'property') {
|
||||
const { name, value } = event
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
// this field is settable once before being observed
|
||||
task.id
|
||||
|
||||
task.status
|
||||
await task.abort()
|
||||
|
||||
// if fn rejects, the task will be marked as failed
|
||||
const result = await task.runInside(fn)
|
||||
|
||||
// if fn rejects, the task will be marked as failed
|
||||
// if fn resolves, the task will be marked as succeeded
|
||||
const result = await task.run(fn)
|
||||
|
||||
// the abort signal of the current task if any, otherwise is `undefined`
|
||||
Task.abortSignal
|
||||
|
||||
// sends an info on the current task if any, otherwise does nothing
|
||||
Task.info(message, data)
|
||||
|
||||
// sends an info on the current task if any, otherwise does nothing
|
||||
Task.warning(message, data)
|
||||
|
||||
// attaches a property to the current task if any, otherwise does nothing
|
||||
//
|
||||
// the latest value takes precedence
|
||||
//
|
||||
// examples:
|
||||
// - progress
|
||||
Task.set(property, value)
|
||||
```
|
||||
1
@vates/task/.npmignore
Symbolic link
1
@vates/task/.npmignore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../scripts/npmignore
|
||||
85
@vates/task/README.md
Normal file
85
@vates/task/README.md
Normal file
@@ -0,0 +1,85 @@
|
||||
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
|
||||
|
||||
# @vates/task
|
||||
|
||||
[](https://npmjs.org/package/@vates/task)  [](https://bundlephobia.com/result?p=@vates/task) [](https://npmjs.org/package/@vates/task)
|
||||
|
||||
## Install
|
||||
|
||||
Installation of the [npm package](https://npmjs.org/package/@vates/task):
|
||||
|
||||
```
|
||||
> npm install --save @vates/task
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import { Task } from '@vates/task'
|
||||
|
||||
const task = new Task({
|
||||
name: 'my task',
|
||||
|
||||
// if defined, a new detached task is created
|
||||
//
|
||||
// if not defined and created inside an existing task, the new task is considered a subtask
|
||||
onProgress(event) {
|
||||
// this function is called each time this task or one of it's subtasks change state
|
||||
const { id, timestamp, type } = event
|
||||
if (type === 'start') {
|
||||
const { name, parentId } = event
|
||||
} else if (type === 'end') {
|
||||
const { result, status } = event
|
||||
} else if (type === 'info' || type === 'warning') {
|
||||
const { data, message } = event
|
||||
} else if (type === 'property') {
|
||||
const { name, value } = event
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
// this field is settable once before being observed
|
||||
task.id
|
||||
|
||||
task.status
|
||||
await task.abort()
|
||||
|
||||
// if fn rejects, the task will be marked as failed
|
||||
const result = await task.runInside(fn)
|
||||
|
||||
// if fn rejects, the task will be marked as failed
|
||||
// if fn resolves, the task will be marked as succeeded
|
||||
const result = await task.run(fn)
|
||||
|
||||
// the abort signal of the current task if any, otherwise is `undefined`
|
||||
Task.abortSignal
|
||||
|
||||
// sends an info on the current task if any, otherwise does nothing
|
||||
Task.info(message, data)
|
||||
|
||||
// sends an info on the current task if any, otherwise does nothing
|
||||
Task.warning(message, data)
|
||||
|
||||
// attaches a property to the current task if any, otherwise does nothing
|
||||
//
|
||||
// the latest value takes precedence
|
||||
//
|
||||
// examples:
|
||||
// - progress
|
||||
Task.set(property, value)
|
||||
```
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions are _very_ welcomed, either on the documentation or on
|
||||
the code.
|
||||
|
||||
You may:
|
||||
|
||||
- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
|
||||
you've encountered;
|
||||
- fork and create a pull request.
|
||||
|
||||
## License
|
||||
|
||||
[ISC](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)
|
||||
184
@vates/task/index.js
Normal file
184
@vates/task/index.js
Normal file
@@ -0,0 +1,184 @@
|
||||
'use strict'
|
||||
|
||||
const assert = require('node:assert').strict
|
||||
const { AsyncLocalStorage } = require('node:async_hooks')
|
||||
|
||||
// define a read-only, non-enumerable, non-configurable property
|
||||
function define(object, property, value) {
|
||||
Object.defineProperty(object, property, { value })
|
||||
}
|
||||
|
||||
const noop = Function.prototype
|
||||
|
||||
const ABORTED = 'aborted'
|
||||
const ABORTING = 'aborting'
|
||||
const FAILURE = 'failure'
|
||||
const PENDING = 'pending'
|
||||
const SUCCESS = 'success'
|
||||
exports.STATUS = { ABORTED, ABORTING, FAILURE, PENDING, SUCCESS }
|
||||
|
||||
const asyncStorage = new AsyncLocalStorage()
|
||||
const getTask = () => asyncStorage.getStore()
|
||||
|
||||
exports.Task = class Task {
|
||||
static get abortSignal() {
|
||||
const task = getTask()
|
||||
if (task !== undefined) {
|
||||
return task.#abortController.signal
|
||||
}
|
||||
}
|
||||
|
||||
static info(message, data) {
|
||||
const task = getTask()
|
||||
if (task !== undefined) {
|
||||
task.#emit('info', { data, message })
|
||||
}
|
||||
}
|
||||
|
||||
static run(opts, fn) {
|
||||
return new this(opts).run(fn)
|
||||
}
|
||||
|
||||
static set(name, value) {
|
||||
const task = getTask()
|
||||
if (task !== undefined) {
|
||||
task.#emit('property', { name, value })
|
||||
}
|
||||
}
|
||||
|
||||
static warning(message, data) {
|
||||
const task = getTask()
|
||||
if (task !== undefined) {
|
||||
task.#emit('warning', { data, message })
|
||||
}
|
||||
}
|
||||
|
||||
static wrap(opts, fn) {
|
||||
// compatibility with @decorateWith
|
||||
if (typeof fn !== 'function') {
|
||||
;[fn, opts] = [opts, fn]
|
||||
}
|
||||
|
||||
return function taskRun() {
|
||||
return Task.run(typeof opts === 'function' ? opts.apply(this, arguments) : opts, () => fn.apply(this, arguments))
|
||||
}
|
||||
}
|
||||
|
||||
#abortController = new AbortController()
|
||||
#onProgress
|
||||
#parent
|
||||
|
||||
get id() {
|
||||
return (this.id = Math.random().toString(36).slice(2))
|
||||
}
|
||||
set id(value) {
|
||||
define(this, 'id', value)
|
||||
}
|
||||
|
||||
#startData
|
||||
|
||||
#status = PENDING
|
||||
get status() {
|
||||
return this.#status
|
||||
}
|
||||
|
||||
constructor({ name, onProgress }) {
|
||||
this.#startData = { name }
|
||||
|
||||
if (onProgress !== undefined) {
|
||||
this.#onProgress = onProgress
|
||||
} else {
|
||||
const parent = getTask()
|
||||
if (parent !== undefined) {
|
||||
this.#parent = parent
|
||||
|
||||
const { signal } = parent.#abortController
|
||||
signal.addEventListener('abort', () => {
|
||||
this.#abortController.abort(signal.reason)
|
||||
})
|
||||
|
||||
this.#onProgress = parent.#onProgress
|
||||
this.#startData.parentId = parent.id
|
||||
} else {
|
||||
this.#onProgress = noop
|
||||
}
|
||||
}
|
||||
|
||||
const { signal } = this.#abortController
|
||||
signal.addEventListener('abort', () => {
|
||||
if (this.status === PENDING) {
|
||||
this.#status = this.#running ? ABORTING : ABORTED
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
abort(reason) {
|
||||
this.#abortController.abort(reason)
|
||||
}
|
||||
|
||||
#emit(type, data) {
|
||||
data.id = this.id
|
||||
data.timestamp = Date.now()
|
||||
data.type = type
|
||||
this.#onProgress(data)
|
||||
}
|
||||
|
||||
#handleMaybeAbortion(result) {
|
||||
if (this.status === ABORTING) {
|
||||
this.#status = ABORTED
|
||||
this.#emit('end', { status: ABORTED, result })
|
||||
return true
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async run(fn) {
|
||||
const result = await this.runInside(fn)
|
||||
if (this.status === PENDING) {
|
||||
this.#status = SUCCESS
|
||||
this.#emit('end', { status: SUCCESS, result })
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
#running = false
|
||||
async runInside(fn) {
|
||||
assert.equal(this.status, PENDING)
|
||||
assert.equal(this.#running, false)
|
||||
this.#running = true
|
||||
|
||||
const startData = this.#startData
|
||||
if (startData !== undefined) {
|
||||
this.#startData = undefined
|
||||
this.#emit('start', startData)
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await asyncStorage.run(this, fn)
|
||||
this.#handleMaybeAbortion(result)
|
||||
this.#running = false
|
||||
return result
|
||||
} catch (result) {
|
||||
if (!this.#handleMaybeAbortion(result)) {
|
||||
this.#status = FAILURE
|
||||
this.#emit('end', { status: FAILURE, result })
|
||||
}
|
||||
throw result
|
||||
}
|
||||
}
|
||||
|
||||
wrap(fn) {
|
||||
const task = this
|
||||
return function taskRun() {
|
||||
return task.run(() => fn.apply(this, arguments))
|
||||
}
|
||||
}
|
||||
|
||||
wrapInside(fn) {
|
||||
const task = this
|
||||
return function taskRunInside() {
|
||||
return task.runInside(() => fn.apply(this, arguments))
|
||||
}
|
||||
}
|
||||
}
|
||||
23
@vates/task/package.json
Normal file
23
@vates/task/package.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "@vates/task",
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/task",
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"repository": {
|
||||
"directory": "@vates/task",
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Vates SAS",
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"license": "ISC",
|
||||
"version": "0.0.1",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"scripts": {
|
||||
"postversion": "npm publish --access public"
|
||||
}
|
||||
}
|
||||
26
@vates/toggle-scripts/.USAGE.md
Normal file
26
@vates/toggle-scripts/.USAGE.md
Normal file
@@ -0,0 +1,26 @@
|
||||
```
|
||||
Usage: toggle-scripts options...
|
||||
|
||||
Easily enable/disable scripts in package.json
|
||||
|
||||
Options
|
||||
+<script> Enable the script <script>, ie remove the prefix `_`
|
||||
-<script> Disable the script <script>, ie prefix it with `_`
|
||||
|
||||
Examples
|
||||
toggle-scripts +postinstall +preuninstall
|
||||
toggle-scripts -postinstall -preuninstall
|
||||
```
|
||||
|
||||
For example, if you want `postinstall` hook only in dev:
|
||||
|
||||
```json
|
||||
// package.json
|
||||
{
|
||||
"scripts": {
|
||||
"postinstall": "<some dev only command>",
|
||||
"prepublishOnly": "toggle-scripts -postinstall",
|
||||
"postpublish": "toggle-scripts +postinstall"
|
||||
}
|
||||
}
|
||||
```
|
||||
1
@vates/toggle-scripts/.npmignore
Symbolic link
1
@vates/toggle-scripts/.npmignore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../scripts/npmignore
|
||||
59
@vates/toggle-scripts/README.md
Normal file
59
@vates/toggle-scripts/README.md
Normal file
@@ -0,0 +1,59 @@
|
||||
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
|
||||
|
||||
# @vates/toggle-scripts
|
||||
|
||||
[](https://npmjs.org/package/@vates/toggle-scripts)  [](https://bundlephobia.com/result?p=@vates/toggle-scripts) [](https://npmjs.org/package/@vates/toggle-scripts)
|
||||
|
||||
> Easily enable/disable scripts in package.json
|
||||
|
||||
## Install
|
||||
|
||||
Installation of the [npm package](https://npmjs.org/package/@vates/toggle-scripts):
|
||||
|
||||
```
|
||||
> npm install --save @vates/toggle-scripts
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
Usage: toggle-scripts options...
|
||||
|
||||
Easily enable/disable scripts in package.json
|
||||
|
||||
Options
|
||||
+<script> Enable the script <script>, ie remove the prefix `_`
|
||||
-<script> Disable the script <script>, ie prefix it with `_`
|
||||
|
||||
Examples
|
||||
toggle-scripts +postinstall +preuninstall
|
||||
toggle-scripts -postinstall -preuninstall
|
||||
```
|
||||
|
||||
For example, if you want `postinstall` hook only in dev:
|
||||
|
||||
```json
|
||||
// package.json
|
||||
{
|
||||
"scripts": {
|
||||
"postinstall": "<some dev only command>",
|
||||
"prepublishOnly": "toggle-scripts -postinstall",
|
||||
"postpublish": "toggle-scripts +postinstall"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions are _very_ welcomed, either on the documentation or on
|
||||
the code.
|
||||
|
||||
You may:
|
||||
|
||||
- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
|
||||
you've encountered;
|
||||
- fork and create a pull request.
|
||||
|
||||
## License
|
||||
|
||||
[ISC](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)
|
||||
63
@vates/toggle-scripts/index.js
Executable file
63
@vates/toggle-scripts/index.js
Executable file
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
|
||||
const mapKeys = (object, iteratee) => {
|
||||
const result = {}
|
||||
for (const key of Object.keys(object)) {
|
||||
result[iteratee(key, object)] = object[key]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const args = process.argv.slice(2)
|
||||
if (args.length === 0) {
|
||||
const { description, name, version } = require('./package.json')
|
||||
const bin = 'toggle-scripts'
|
||||
process.stdout.write(`Usage: ${bin} options...
|
||||
|
||||
${description}
|
||||
|
||||
Options
|
||||
+<script> Enable the script <script>, ie remove the prefix \`_\`
|
||||
-<script> Disable the script <script>, ie prefix it with \`_\`
|
||||
|
||||
Examples
|
||||
${bin} +postinstall +preuninstall
|
||||
${bin} -postinstall -preuninstall
|
||||
|
||||
${name} v${version}
|
||||
`)
|
||||
// eslint-disable-next-line n/no-process-exit
|
||||
process.exit()
|
||||
}
|
||||
|
||||
const plan = { __proto__: null }
|
||||
for (const arg of args) {
|
||||
const action = arg[0]
|
||||
const script = arg.slice(1)
|
||||
|
||||
if (action === '+') {
|
||||
plan['_' + script] = script
|
||||
} else if (action === '-') {
|
||||
plan[script] = '_' + script
|
||||
} else {
|
||||
throw new Error('invalid param: ' + arg)
|
||||
}
|
||||
}
|
||||
|
||||
const pkgPath = process.env.npm_package_json || './package.json'
|
||||
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
|
||||
pkg.scripts = mapKeys(pkg.scripts, (name, scripts) => {
|
||||
const newName = plan[name]
|
||||
if (newName === undefined) {
|
||||
return name
|
||||
}
|
||||
if (newName in scripts) {
|
||||
throw new Error('script already defined: ' + name)
|
||||
}
|
||||
return newName
|
||||
})
|
||||
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user