OptionsNormalizer.spec.ts 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. import 'reflect-metadata';
  2. import { ServiceIdentifiers } from '../../../src/container/ServiceIdentifiers';
  3. import { assert } from 'chai';
  4. import { TInputOptions } from '../../../src/types/options/TInputOptions';
  5. import { IInversifyContainerFacade } from '../../../src/interfaces/container/IInversifyContainerFacade';
  6. import { IOptions } from '../../../src/interfaces/options/IOptions';
  7. import { IOptionsNormalizer } from '../../../src/interfaces/options/IOptionsNormalizer';
  8. import { StringArrayEncoding } from '../../../src/enums/node-transformers/string-array-transformers/StringArrayEncoding';
  9. import { DEFAULT_PRESET } from '../../../src/options/presets/Default';
  10. import { InversifyContainerFacade } from '../../../src/container/InversifyContainerFacade';
  11. /**
  12. * @param optionsPreset
  13. * @returns {IOptions}
  14. */
  15. function getNormalizedOptions (optionsPreset: TInputOptions): TInputOptions {
  16. const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade();
  17. inversifyContainerFacade.load('', '', optionsPreset);
  18. const options: IOptions = inversifyContainerFacade
  19. .get<IOptions>(ServiceIdentifiers.IOptions);
  20. const optionsNormalizer: IOptionsNormalizer = inversifyContainerFacade
  21. .get<IOptionsNormalizer>(ServiceIdentifiers.IOptionsNormalizer);
  22. return <TInputOptions>optionsNormalizer.normalize(options);
  23. }
  24. function getDefaultOptions(): TInputOptions {
  25. return {
  26. ...DEFAULT_PRESET,
  27. seed: 1 // set `seed` to the fixed value, to prevent a new seed for the each case
  28. };
  29. }
  30. describe('OptionsNormalizer', () => {
  31. describe('normalize', () => {
  32. let optionsPreset: TInputOptions,
  33. expectedOptionsPreset: TInputOptions;
  34. describe('controlFlowFlatteningThresholdRule', () => {
  35. before(() => {
  36. optionsPreset = getNormalizedOptions({
  37. ...getDefaultOptions(),
  38. controlFlowFlattening: true,
  39. controlFlowFlatteningThreshold: 0
  40. });
  41. expectedOptionsPreset = {
  42. ...getDefaultOptions(),
  43. controlFlowFlattening: false,
  44. controlFlowFlatteningThreshold: 0
  45. };
  46. });
  47. it('should normalize options preset', () => {
  48. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  49. });
  50. });
  51. describe('deadCodeInjectionRule', () => {
  52. before(() => {
  53. optionsPreset = getNormalizedOptions({
  54. ...getDefaultOptions(),
  55. deadCodeInjection: true,
  56. deadCodeInjectionThreshold: 0.4,
  57. stringArray: false,
  58. stringArrayThreshold: 0
  59. });
  60. expectedOptionsPreset = {
  61. ...getDefaultOptions(),
  62. deadCodeInjection: true,
  63. deadCodeInjectionThreshold: 0.4,
  64. stringArray: true,
  65. stringArrayThreshold: 0.75
  66. };
  67. });
  68. it('should normalize options preset', () => {
  69. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  70. });
  71. });
  72. describe('deadCodeInjectionRule', () => {
  73. describe('`stringArrayThreshold` option is empty', () => {
  74. before(() => {
  75. optionsPreset = getNormalizedOptions({
  76. ...getDefaultOptions(),
  77. deadCodeInjection: true,
  78. deadCodeInjectionThreshold: 0.4,
  79. stringArray: false,
  80. stringArrayThreshold: 0
  81. });
  82. expectedOptionsPreset = {
  83. ...getDefaultOptions(),
  84. deadCodeInjection: true,
  85. deadCodeInjectionThreshold: 0.4,
  86. stringArray: true,
  87. stringArrayThreshold: 0.75
  88. };
  89. });
  90. it('should normalize options preset', () => {
  91. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  92. });
  93. });
  94. describe('`stringArrayThreshold` option is not empty', () => {
  95. before(() => {
  96. optionsPreset = getNormalizedOptions({
  97. ...getDefaultOptions(),
  98. deadCodeInjection: true,
  99. deadCodeInjectionThreshold: 0.4,
  100. stringArray: false,
  101. stringArrayThreshold: 0.5
  102. });
  103. expectedOptionsPreset = {
  104. ...getDefaultOptions(),
  105. deadCodeInjection: true,
  106. deadCodeInjectionThreshold: 0.4,
  107. stringArray: true,
  108. stringArrayThreshold: 0.5
  109. };
  110. });
  111. it('should normalize options preset', () => {
  112. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  113. });
  114. });
  115. });
  116. describe('deadCodeInjectionThresholdRule', () => {
  117. before(() => {
  118. optionsPreset = getNormalizedOptions({
  119. ...getDefaultOptions(),
  120. deadCodeInjection: true,
  121. deadCodeInjectionThreshold: 0
  122. });
  123. expectedOptionsPreset = {
  124. ...getDefaultOptions(),
  125. deadCodeInjection: false,
  126. deadCodeInjectionThreshold: 0
  127. };
  128. });
  129. it('should normalize options preset', () => {
  130. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  131. });
  132. });
  133. describe('domainLockRule', () => {
  134. before(() => {
  135. optionsPreset = getNormalizedOptions({
  136. ...getDefaultOptions(),
  137. domainLock: [
  138. '//localhost:9000',
  139. 'https://google.ru/abc?cde=fgh'
  140. ]
  141. });
  142. expectedOptionsPreset = {
  143. ...getDefaultOptions(),
  144. domainLock: [
  145. 'localhost',
  146. 'google.ru'
  147. ]
  148. };
  149. });
  150. it('should normalize options preset', () => {
  151. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  152. });
  153. });
  154. describe('inputFileNameRule', () => {
  155. describe('Variant #1: extension isn\'t set', () => {
  156. before(() => {
  157. optionsPreset = getNormalizedOptions({
  158. ...getDefaultOptions(),
  159. inputFileName: 'foo'
  160. });
  161. expectedOptionsPreset = {
  162. ...getDefaultOptions(),
  163. inputFileName: 'foo.js'
  164. };
  165. });
  166. it('should normalize options preset', () => {
  167. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  168. });
  169. });
  170. describe('Variant #2: extension is set', () => {
  171. before(() => {
  172. optionsPreset = getNormalizedOptions({
  173. ...getDefaultOptions(),
  174. inputFileName: 'foo.js'
  175. });
  176. expectedOptionsPreset = {
  177. ...getDefaultOptions(),
  178. inputFileName: 'foo.js'
  179. };
  180. });
  181. it('should normalize options preset', () => {
  182. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  183. });
  184. });
  185. describe('Variant #3: extension in set with `.map` postfix', () => {
  186. before(() => {
  187. optionsPreset = getNormalizedOptions({
  188. ...getDefaultOptions(),
  189. inputFileName: 'foo.map.js'
  190. });
  191. expectedOptionsPreset = {
  192. ...getDefaultOptions(),
  193. inputFileName: 'foo.map.js'
  194. };
  195. });
  196. it('should normalize options preset', () => {
  197. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  198. });
  199. });
  200. describe('Variant #4: no file name', () => {
  201. before(() => {
  202. optionsPreset = getNormalizedOptions({
  203. ...getDefaultOptions(),
  204. inputFileName: ''
  205. });
  206. expectedOptionsPreset = {
  207. ...getDefaultOptions(),
  208. inputFileName: ''
  209. };
  210. });
  211. it('should normalize options preset', () => {
  212. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  213. });
  214. });
  215. });
  216. describe('identifierNamesCacheRule', () => {
  217. describe('Variant #1: all fields are exist with values', () => {
  218. before(() => {
  219. optionsPreset = getNormalizedOptions({
  220. ...getDefaultOptions(),
  221. identifierNamesCache: {
  222. globalIdentifiers: {
  223. foo: '_0x123456'
  224. },
  225. propertyIdentifiers: {
  226. bar: '_0x654321'
  227. }
  228. }
  229. });
  230. expectedOptionsPreset = {
  231. ...getDefaultOptions(),
  232. identifierNamesCache: {
  233. globalIdentifiers: {
  234. foo: '_0x123456'
  235. },
  236. propertyIdentifiers: {
  237. bar: '_0x654321'
  238. }
  239. }
  240. };
  241. });
  242. it('should not normalize options preset', () => {
  243. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  244. });
  245. });
  246. describe('Variant #2: some fields are exist with values', () => {
  247. before(() => {
  248. optionsPreset = getNormalizedOptions({
  249. ...getDefaultOptions(),
  250. identifierNamesCache: {
  251. globalIdentifiers: {
  252. foo: '_0x123456'
  253. },
  254. propertyIdentifiers: {}
  255. }
  256. });
  257. expectedOptionsPreset = {
  258. ...getDefaultOptions(),
  259. identifierNamesCache: {
  260. globalIdentifiers: {
  261. foo: '_0x123456'
  262. },
  263. propertyIdentifiers: {}
  264. }
  265. };
  266. });
  267. it('should not normalize options preset', () => {
  268. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  269. });
  270. });
  271. describe('Variant #3: all fields are exist with empty objects', () => {
  272. before(() => {
  273. optionsPreset = getNormalizedOptions({
  274. ...getDefaultOptions(),
  275. identifierNamesCache: {
  276. globalIdentifiers: {},
  277. propertyIdentifiers: {}
  278. }
  279. });
  280. expectedOptionsPreset = {
  281. ...getDefaultOptions(),
  282. identifierNamesCache: {
  283. globalIdentifiers: {},
  284. propertyIdentifiers: {}
  285. }
  286. };
  287. });
  288. it('should not normalize options preset', () => {
  289. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  290. });
  291. });
  292. describe('Variant #4: some fields are missing', () => {
  293. before(() => {
  294. optionsPreset = getNormalizedOptions({
  295. ...getDefaultOptions(),
  296. identifierNamesCache: {
  297. globalIdentifiers: {
  298. foo: '_0x123456'
  299. }
  300. }
  301. });
  302. expectedOptionsPreset = {
  303. ...getDefaultOptions(),
  304. identifierNamesCache: {
  305. globalIdentifiers: {
  306. foo: '_0x123456'
  307. },
  308. propertyIdentifiers: {}
  309. }
  310. };
  311. });
  312. it('should normalize options preset', () => {
  313. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  314. });
  315. });
  316. describe('Variant #5: all fields are missing', () => {
  317. before(() => {
  318. optionsPreset = getNormalizedOptions({
  319. ...getDefaultOptions(),
  320. identifierNamesCache: {}
  321. });
  322. expectedOptionsPreset = {
  323. ...getDefaultOptions(),
  324. identifierNamesCache: {
  325. globalIdentifiers: {},
  326. propertyIdentifiers: {}
  327. }
  328. };
  329. });
  330. it('should normalize options preset', () => {
  331. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  332. });
  333. });
  334. });
  335. describe('seedRule', () => {
  336. describe('Variant #1: seed value is string', () => {
  337. before(() => {
  338. optionsPreset = getNormalizedOptions({
  339. ...getDefaultOptions(),
  340. seed: 'abc'
  341. });
  342. expectedOptionsPreset = {
  343. ...getDefaultOptions(),
  344. seed: 'abc'
  345. };
  346. });
  347. it('should not normalize options preset', () => {
  348. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  349. });
  350. });
  351. describe('Variant #2: seed value is number', () => {
  352. before(() => {
  353. optionsPreset = getNormalizedOptions({
  354. ...getDefaultOptions(),
  355. seed: 123
  356. });
  357. expectedOptionsPreset = {
  358. ...getDefaultOptions(),
  359. seed: 123
  360. };
  361. });
  362. it('should normalize options preset', () => {
  363. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  364. });
  365. });
  366. describe('Variant #3: seed value is `0``', () => {
  367. let seedValue: number;
  368. before(() => {
  369. optionsPreset = getNormalizedOptions({
  370. ...getDefaultOptions(),
  371. seed: 0
  372. });
  373. seedValue = Number(optionsPreset.seed);
  374. });
  375. it('should normalize seed value', () => {
  376. assert.isAtLeast(seedValue, 0);
  377. assert.isBelow(seedValue, 999_999_999);
  378. });
  379. });
  380. });
  381. describe('selfDefendingRule', () => {
  382. before(() => {
  383. optionsPreset = getNormalizedOptions({
  384. ...getDefaultOptions(),
  385. selfDefending: true,
  386. compact: false
  387. });
  388. expectedOptionsPreset = {
  389. ...getDefaultOptions(),
  390. selfDefending: true,
  391. compact: true
  392. };
  393. });
  394. it('should normalize options preset', () => {
  395. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  396. });
  397. });
  398. describe('sourceMapBaseUrlRule', () => {
  399. describe('Variant #1: only source map base url', () => {
  400. before(() => {
  401. optionsPreset = getNormalizedOptions({
  402. ...getDefaultOptions(),
  403. sourceMapBaseUrl: 'http://localhost:9000',
  404. });
  405. expectedOptionsPreset = {
  406. ...getDefaultOptions(),
  407. sourceMapBaseUrl: ''
  408. };
  409. });
  410. it('should normalize options preset', () => {
  411. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  412. });
  413. });
  414. describe('Variant #2: source map base url with source map file name', () => {
  415. before(() => {
  416. optionsPreset = getNormalizedOptions({
  417. ...getDefaultOptions(),
  418. sourceMapBaseUrl: 'http://localhost:9000',
  419. sourceMapFileName: '/outputSourceMapName.map'
  420. });
  421. expectedOptionsPreset = {
  422. ...getDefaultOptions(),
  423. sourceMapBaseUrl: 'http://localhost:9000/',
  424. sourceMapFileName: 'outputSourceMapName.js.map'
  425. };
  426. });
  427. it('should normalize options preset', () => {
  428. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  429. });
  430. });
  431. });
  432. describe('sourceMapFileNameRule', () => {
  433. describe('Base filename without extension', () => {
  434. before(() => {
  435. optionsPreset = getNormalizedOptions({
  436. ...getDefaultOptions(),
  437. sourceMapBaseUrl: 'http://localhost:9000',
  438. sourceMapFileName: 'outputSourceMapName'
  439. });
  440. expectedOptionsPreset = {
  441. ...getDefaultOptions(),
  442. sourceMapBaseUrl: 'http://localhost:9000/',
  443. sourceMapFileName: 'outputSourceMapName.js.map'
  444. };
  445. });
  446. it('should normalize options preset', () => {
  447. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  448. });
  449. });
  450. describe('Slashes in file name', () => {
  451. before(() => {
  452. optionsPreset = getNormalizedOptions({
  453. ...getDefaultOptions(),
  454. sourceMapBaseUrl: 'http://localhost:9000',
  455. sourceMapFileName: '//outputSourceMapName'
  456. });
  457. expectedOptionsPreset = {
  458. ...getDefaultOptions(),
  459. sourceMapBaseUrl: 'http://localhost:9000/',
  460. sourceMapFileName: 'outputSourceMapName.js.map'
  461. };
  462. });
  463. it('should normalize options preset', () => {
  464. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  465. });
  466. });
  467. describe('`js` file extension in file name', () => {
  468. before(() => {
  469. optionsPreset = getNormalizedOptions({
  470. ...getDefaultOptions(),
  471. sourceMapBaseUrl: 'http://localhost:9000',
  472. sourceMapFileName: 'outputSourceMapName.js'
  473. });
  474. expectedOptionsPreset = {
  475. ...getDefaultOptions(),
  476. sourceMapBaseUrl: 'http://localhost:9000/',
  477. sourceMapFileName: 'outputSourceMapName.js.map'
  478. };
  479. });
  480. it('should normalize options preset', () => {
  481. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  482. });
  483. });
  484. describe('Non `js` file extension in file name', () => {
  485. before(() => {
  486. optionsPreset = getNormalizedOptions({
  487. ...getDefaultOptions(),
  488. sourceMapBaseUrl: 'http://localhost:9000',
  489. sourceMapFileName: 'outputSourceMapName.exe'
  490. });
  491. expectedOptionsPreset = {
  492. ...getDefaultOptions(),
  493. sourceMapBaseUrl: 'http://localhost:9000/',
  494. sourceMapFileName: 'outputSourceMapName.js.map'
  495. };
  496. });
  497. it('should normalize options preset', () => {
  498. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  499. });
  500. });
  501. describe('File hash in file name', () => {
  502. before(() => {
  503. optionsPreset = getNormalizedOptions({
  504. ...getDefaultOptions(),
  505. sourceMapBaseUrl: 'http://localhost:9000',
  506. sourceMapFileName: 'outputSourceMapName.7e2c49a622975ebd9b7e'
  507. });
  508. expectedOptionsPreset = {
  509. ...getDefaultOptions(),
  510. sourceMapBaseUrl: 'http://localhost:9000/',
  511. sourceMapFileName: 'outputSourceMapName.7e2c49a622975ebd9b7e.js.map'
  512. };
  513. });
  514. it('should normalize options preset', () => {
  515. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  516. });
  517. });
  518. describe('File hash and `js` file extension in file name #1', () => {
  519. before(() => {
  520. optionsPreset = getNormalizedOptions({
  521. ...getDefaultOptions(),
  522. sourceMapBaseUrl: 'http://localhost:9000',
  523. sourceMapFileName: 'outputSourceMapName.7e2c49a622975ebd9b7e.js'
  524. });
  525. expectedOptionsPreset = {
  526. ...getDefaultOptions(),
  527. sourceMapBaseUrl: 'http://localhost:9000/',
  528. sourceMapFileName: 'outputSourceMapName.7e2c49a622975ebd9b7e.js.map'
  529. };
  530. });
  531. it('should normalize options preset', () => {
  532. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  533. });
  534. });
  535. describe('File hash and non `js` file extension in file name', () => {
  536. before(() => {
  537. optionsPreset = getNormalizedOptions({
  538. ...getDefaultOptions(),
  539. sourceMapBaseUrl: 'http://localhost:9000',
  540. sourceMapFileName: 'outputSourceMapName.7e2c49a622975ebd9b7e.exe'
  541. });
  542. expectedOptionsPreset = {
  543. ...getDefaultOptions(),
  544. sourceMapBaseUrl: 'http://localhost:9000/',
  545. sourceMapFileName: 'outputSourceMapName.7e2c49a622975ebd9b7e.js.map'
  546. };
  547. });
  548. it('should normalize options preset', () => {
  549. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  550. });
  551. });
  552. });
  553. describe('splitStringsChunkLengthRule', () => {
  554. describe('`splitStringsChunkLengthRule` value is float number', () => {
  555. before(() => {
  556. optionsPreset = getNormalizedOptions({
  557. ...getDefaultOptions(),
  558. splitStrings: true,
  559. splitStringsChunkLength: 5.6
  560. });
  561. expectedOptionsPreset = {
  562. ...getDefaultOptions(),
  563. splitStrings: true,
  564. splitStringsChunkLength: 5
  565. };
  566. });
  567. it('should normalize options preset', () => {
  568. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  569. });
  570. });
  571. });
  572. describe('stringArrayRule', () => {
  573. before(() => {
  574. optionsPreset = getNormalizedOptions({
  575. ...getDefaultOptions(),
  576. shuffleStringArray: true,
  577. stringArray: false,
  578. stringArrayEncoding: [StringArrayEncoding.Rc4],
  579. stringArrayIndexShift: true,
  580. stringArrayWrappersChainedCalls: true,
  581. stringArrayWrappersCount: 5,
  582. stringArrayThreshold: 0.5,
  583. rotateStringArray: true
  584. });
  585. expectedOptionsPreset = {
  586. ...getDefaultOptions(),
  587. shuffleStringArray: false,
  588. stringArray: false,
  589. stringArrayEncoding: [StringArrayEncoding.None],
  590. stringArrayIndexShift: false,
  591. stringArrayWrappersChainedCalls: false,
  592. stringArrayWrappersCount: 0,
  593. stringArrayThreshold: 0,
  594. rotateStringArray: false
  595. };
  596. });
  597. it('should normalize options preset', () => {
  598. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  599. });
  600. });
  601. describe('stringArrayEncodingRule', () => {
  602. before(() => {
  603. optionsPreset = getNormalizedOptions({
  604. ...getDefaultOptions(),
  605. stringArrayEncoding: []
  606. });
  607. expectedOptionsPreset = {
  608. ...getDefaultOptions(),
  609. stringArrayEncoding: [
  610. StringArrayEncoding.None
  611. ]
  612. };
  613. });
  614. it('should normalize options preset', () => {
  615. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  616. });
  617. });
  618. describe('stringArrayWrappersChainedCallsRule', () => {
  619. before(() => {
  620. optionsPreset = getNormalizedOptions({
  621. ...getDefaultOptions(),
  622. stringArrayWrappersChainedCalls: true,
  623. stringArrayWrappersCount: 0
  624. });
  625. expectedOptionsPreset = {
  626. ...getDefaultOptions(),
  627. stringArrayWrappersChainedCalls: false,
  628. stringArrayWrappersCount: 0
  629. };
  630. });
  631. it('should normalize options preset', () => {
  632. assert.deepEqual(optionsPreset, expectedOptionsPreset);
  633. });
  634. });
  635. });
  636. });