underpost 2.8.844 → 2.8.846

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +14 -2
  2. package/cli.md +4 -3
  3. package/docker-compose.yml +1 -1
  4. package/manifests/deployment/dd-template-development/deployment.yaml +2 -2
  5. package/package.json +1 -2
  6. package/src/cli/index.js +1 -7
  7. package/src/cli/run.js +7 -0
  8. package/src/client/Default.index.js +1 -1
  9. package/src/client/components/core/Chat.js +1 -1
  10. package/src/client/components/core/CommonJs.js +24 -22
  11. package/src/client/components/core/Content.js +1 -5
  12. package/src/client/components/core/Css.js +68 -4
  13. package/src/client/components/core/CssCore.js +5 -0
  14. package/src/client/components/core/Docs.js +9 -10
  15. package/src/client/components/core/DropDown.js +137 -82
  16. package/src/client/components/core/Modal.js +72 -48
  17. package/src/client/components/core/ObjectLayerEngine.js +638 -0
  18. package/src/client/components/core/Panel.js +156 -32
  19. package/src/client/components/core/PanelForm.js +12 -4
  20. package/src/client/components/core/Router.js +63 -2
  21. package/src/client/components/core/Translate.js +6 -2
  22. package/src/client/components/default/MenuDefault.js +27 -1
  23. package/src/client/public/default/android-chrome-144x144.png +0 -0
  24. package/src/client/public/default/android-chrome-192x192.png +0 -0
  25. package/src/client/public/default/android-chrome-256x256.png +0 -0
  26. package/src/client/public/default/android-chrome-36x36.png +0 -0
  27. package/src/client/public/default/android-chrome-48x48.png +0 -0
  28. package/src/client/public/default/android-chrome-72x72.png +0 -0
  29. package/src/client/public/default/android-chrome-96x96.png +0 -0
  30. package/src/client/public/default/apple-touch-icon-114x114-precomposed.png +0 -0
  31. package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
  32. package/src/client/public/default/apple-touch-icon-120x120-precomposed.png +0 -0
  33. package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
  34. package/src/client/public/default/apple-touch-icon-144x144-precomposed.png +0 -0
  35. package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
  36. package/src/client/public/default/apple-touch-icon-152x152-precomposed.png +0 -0
  37. package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
  38. package/src/client/public/default/apple-touch-icon-180x180-precomposed.png +0 -0
  39. package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
  40. package/src/client/public/default/apple-touch-icon-57x57-precomposed.png +0 -0
  41. package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
  42. package/src/client/public/default/apple-touch-icon-60x60-precomposed.png +0 -0
  43. package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
  44. package/src/client/public/default/apple-touch-icon-72x72-precomposed.png +0 -0
  45. package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
  46. package/src/client/public/default/apple-touch-icon-76x76-precomposed.png +0 -0
  47. package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
  48. package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
  49. package/src/client/public/default/apple-touch-icon.png +0 -0
  50. package/src/client/public/default/assets/background/dark.jpg +0 -0
  51. package/src/client/public/default/assets/logo/base-icon.png +0 -0
  52. package/src/client/public/default/assets/mailer/api-user-check.png +0 -0
  53. package/src/client/public/default/assets/mailer/api-user-invalid-token.png +0 -0
  54. package/src/client/public/default/assets/mailer/api-user-recover.png +0 -0
  55. package/src/client/public/default/favicon-16x16.png +0 -0
  56. package/src/client/public/default/favicon-32x32.png +0 -0
  57. package/src/client/public/default/favicon.ico +0 -0
  58. package/src/client/public/default/mstile-144x144.png +0 -0
  59. package/src/client/public/default/mstile-150x150.png +0 -0
  60. package/src/client/public/default/mstile-310x150.png +0 -0
  61. package/src/client/public/default/mstile-310x310.png +0 -0
  62. package/src/client/public/default/mstile-70x70.png +0 -0
  63. package/src/client/public/default/safari-pinned-tab.svg +24 -0
  64. package/src/client/ssr/body/DefaultSplashScreen.js +2 -2
  65. package/src/index.js +1 -1
  66. package/src/server/client-build-docs.js +205 -0
  67. package/src/server/client-build.js +15 -138
  68. package/src/server/conf.js +12 -5
@@ -3,6 +3,7 @@ import { LoadingAnimation } from '../core/LoadingAnimation.js';
3
3
  import { Validator } from '../core/Validator.js';
4
4
  import { Input } from '../core/Input.js';
5
5
  import { Responsive } from '../core/Responsive.js';
6
+ import { darkTheme, ThemeEvents } from './Css.js';
6
7
  import { append, getDataFromInputFile, getQueryParams, htmls, prepend, s } from './VanillaJs.js';
7
8
  import { BtnIcon } from './BtnIcon.js';
8
9
  import { Translate } from './Translate.js';
@@ -365,9 +366,7 @@ const Panel = {
365
366
  data: await getDataFromInputFile(file),
366
367
  },
367
368
  },
368
- aHrefOptions: {
369
- disable: true,
370
- },
369
+
371
370
  raw: true,
372
371
  })}
373
372
  <div class="in" style="overflow: hidden">${file.name}</div>`;
@@ -515,37 +514,24 @@ const Panel = {
515
514
  }
516
515
  }
517
516
 
517
+ // Add theme change handler
518
+ const themeChangeHandler = () => {
519
+ const styleElement = s(`.${idPanel}-styles`);
520
+ if (styleElement) {
521
+ styleElement.textContent = darkTheme
522
+ ? getDarkStyles(idPanel, scrollClassContainer)
523
+ : getLightStyles(idPanel, scrollClassContainer);
524
+ }
525
+ };
526
+
527
+ // Add theme change listener
528
+ ThemeEvents[`${idPanel}-theme`] = themeChangeHandler;
529
+
530
+ // Initial styles
531
+ setTimeout(ThemeEvents[`${idPanel}-theme`]);
532
+
518
533
  return html`
519
534
  <style>
520
- .${scrollClassContainer} {
521
- scroll-behavior: smooth;
522
- }
523
- .${idPanel}-form-container {
524
- padding-bottom: 20px;
525
- top: 0px;
526
- z-index: 1;
527
- overflow: auto;
528
- }
529
- .${idPanel}-form {
530
- max-width: 900px;
531
- }
532
- .${idPanel}-cell {
533
- min-height: 200px;
534
- }
535
- .${idPanel}-container {
536
- }
537
- .${idPanel} {
538
- margin: 10px;
539
- transition: 0.3s;
540
- border-radius: 10px;
541
- background: #f6f6f6;
542
- color: black;
543
- padding: 10px;
544
- min-height: 400px;
545
- }
546
- .${idPanel}:hover {
547
- background: #ffffff;
548
- }
549
535
  .${idPanel}-head {
550
536
  /* background: white; */
551
537
  margin-bottom: 10px;
@@ -610,6 +596,7 @@ const Panel = {
610
596
  font-size: 17px !important;
611
597
  }
612
598
  </style>
599
+ <style class="${idPanel}-styles"></style>
613
600
  <div class="${idPanel}-container">
614
601
  <div class="in modal ${idPanel}-form-container ${options.formContainerClass ? options.formContainerClass : ''}">
615
602
  <div class="in ${idPanel}-form-header">
@@ -643,4 +630,141 @@ const Panel = {
643
630
  },
644
631
  };
645
632
 
633
+ // Function to generate base styles
634
+ function getBaseStyles(idPanel, scrollClassContainer) {
635
+ return css`
636
+ .${scrollClassContainer} {
637
+ scroll-behavior: smooth;
638
+ }
639
+ .${idPanel}-form-container {
640
+ padding-bottom: 20px;
641
+ top: 0px;
642
+ z-index: 1;
643
+ overflow: auto;
644
+ }
645
+ .${idPanel}-form {
646
+ max-width: 900px;
647
+ }
648
+ .${idPanel}-cell {
649
+ min-height: 200px;
650
+ }
651
+ .${idPanel}-container {
652
+ }
653
+ .${idPanel} {
654
+ margin: 10px;
655
+ transition: 0.3s;
656
+ border-radius: 10px;
657
+ padding: 10px;
658
+ min-height: 400px;
659
+ }
660
+ .${idPanel}-head {
661
+ margin-bottom: 10px;
662
+ }
663
+ .img-${idPanel} {
664
+ width: 100%;
665
+ }
666
+ .${idPanel}-row {
667
+ padding: 5px;
668
+ margin: 5px;
669
+ font-size: 16px;
670
+ }
671
+ .${idPanel}-subtitle {
672
+ font-size: 17px;
673
+ margin-left: 20px;
674
+ top: -7px;
675
+ }
676
+ .${idPanel}-tags {
677
+ font-size: 17px;
678
+ margin-left: 10px;
679
+ top: -7px;
680
+ }
681
+ .${idPanel}-form-body {
682
+ transition: 0.3s;
683
+ }
684
+ .btn-${idPanel}-add {
685
+ padding: 10px;
686
+ font-size: 20px;
687
+ }
688
+ .${idPanel}-dropdown {
689
+ min-height: 100px;
690
+ }
691
+ `;
692
+ }
693
+
694
+ // Function to generate light theme styles
695
+ function getLightStyles(idPanel, scrollClassContainer) {
696
+ return css`
697
+ ${getBaseStyles(idPanel, scrollClassContainer)}
698
+
699
+ .${idPanel} {
700
+ background: #f6f6f6;
701
+ color: black;
702
+ }
703
+ .${idPanel}:hover {
704
+ background: #ffffff;
705
+ }
706
+ .${idPanel}-title {
707
+ color: rgba(109, 104, 255, 1);
708
+ font-size: 24px;
709
+ padding: 5px;
710
+ }
711
+ .a-title-${idPanel} {
712
+ color: rgba(109, 104, 255, 1);
713
+ }
714
+ .a-title-${idPanel}:hover {
715
+ color: #e89f4c;
716
+ }
717
+ .${idPanel}-row-pin-value {
718
+ font-size: 20px;
719
+ color: rgb(19 190 84);
720
+ }
721
+ .${idPanel}-btn-tool {
722
+ background: none !important;
723
+ color: #c4c4c4 !important;
724
+ }
725
+ .${idPanel}-btn-tool:hover {
726
+ color: #000000 !important;
727
+ font-size: 17px !important;
728
+ }
729
+ `;
730
+ }
731
+
732
+ // Function to generate dark theme styles
733
+ function getDarkStyles(idPanel, scrollClassContainer) {
734
+ return css`
735
+ ${getBaseStyles(idPanel, scrollClassContainer)}
736
+
737
+ .${idPanel} {
738
+ background: #2d2d2d;
739
+ color: #e0e0e0;
740
+ }
741
+ .${idPanel}:hover {
742
+ background: #3a3a3a;
743
+ }
744
+ .${idPanel}-title {
745
+ color: #8a85ff;
746
+ font-size: 24px;
747
+ padding: 5px;
748
+ }
749
+ .a-title-${idPanel} {
750
+ color: #8a85ff;
751
+ }
752
+ .a-title-${idPanel}:hover {
753
+ color: #ffb74d;
754
+ }
755
+ .${idPanel}-row-pin-value {
756
+ font-size: 20px;
757
+ color: #4caf50;
758
+ }
759
+ .${idPanel}-btn-tool {
760
+ background: none !important;
761
+ color: #666666 !important;
762
+ }
763
+ .${idPanel}-btn-tool:hover {
764
+ color: #ffffff !important;
765
+ font-size: 17px !important;
766
+ }
767
+ `;
768
+ }
769
+
646
770
  export { Panel };
@@ -15,7 +15,7 @@ import { getSrcFromFileData } from './Input.js';
15
15
  import { imageShimmer, renderCssAttr } from './Css.js';
16
16
  import { Translate } from './Translate.js';
17
17
  import { Modal } from './Modal.js';
18
- import { listenQueryPathInstance, setQueryPath } from './Router.js';
18
+ import { closeModalRouteChangeEvents, listenQueryPathInstance, renderTitle, setQueryPath } from './Router.js';
19
19
 
20
20
  const PanelForm = {
21
21
  Data: {},
@@ -425,15 +425,22 @@ const PanelForm = {
425
425
  });
426
426
  let firsUpdateEvent = false;
427
427
  let lastCid;
428
- console.warn('-------------> updatePanel 1');
428
+ let lastUserId;
429
+ closeModalRouteChangeEvents[idPanel] = (newPath) => {
430
+ if (newPath.split('?')[0] === '/' && PanelForm.Data[idPanel].data && PanelForm.Data[idPanel].data.length === 0) {
431
+ this.Data[idPanel].updatePanel();
432
+ }
433
+ };
429
434
  this.Data[idPanel].updatePanel = async () => {
430
- console.warn('-------------> updatePanel 2');
431
435
  const cid = getQueryParams().cid ? getQueryParams().cid : '';
432
- if (lastCid === cid) return;
436
+ const forceUpdate = lastUserId !== Elements.Data.user.main.model.user._id;
437
+ if (lastCid === cid && !forceUpdate) return;
438
+ lastUserId = newInstance(Elements.Data.user.main.model.user._id);
433
439
  lastCid = cid;
434
440
  if (options.route === 'home') Modal.homeCid = newInstance(cid);
435
441
  htmls(`.${options.parentIdModal ? 'html-' + options.parentIdModal : 'main-body'}`, await renderSrrPanelData());
436
442
  await getPanelData();
443
+ if (this.Data[idPanel].data.length === 1) renderTitle(this.Data[idPanel].data[0].title);
437
444
  htmls(
438
445
  `.${options.parentIdModal ? 'html-' + options.parentIdModal : 'main-body'}`,
439
446
  await panelRender({ data: this.Data[idPanel].data }),
@@ -454,6 +461,7 @@ const PanelForm = {
454
461
  if (!options.parentIdModal)
455
462
  Modal.Data['modal-menu'].onHome[idPanel] = async () => {
456
463
  lastCid = undefined;
464
+ lastUserId = undefined;
457
465
  setQueryPath({ path: options.route, queryPath: '' });
458
466
  await this.Data[idPanel].updatePanel();
459
467
  };
@@ -1,16 +1,20 @@
1
1
  import { titleFormatted } from './CommonJs.js';
2
2
  import { loggerFactory } from './Logger.js';
3
3
  import { getProxyPath, getQueryParams, htmls, s, setPath } from './VanillaJs.js';
4
+ import { Modal } from './Modal.js';
5
+ import { Worker } from './Worker.js';
4
6
 
5
7
  // Router
6
8
 
7
9
  const logger = loggerFactory(import.meta);
8
10
 
11
+ const renderTitle = (title, nameApp) => htmls('title', html`${title} | ${nameApp ?? Worker.RouterInstance.NameApp}`);
12
+
9
13
  const setDocTitle = (options = { Routes: () => {}, route: '', NameApp: '' }) => {
10
14
  const { Routes, route, NameApp } = options;
11
15
  let title = titleFormatted(Routes()[`/${route}`].title);
12
16
  if (Routes()[`/${route}`].upperCase) title = title.toUpperCase();
13
- htmls('title', html`${title} | ${NameApp}`);
17
+ renderTitle(title, NameApp);
14
18
  {
15
19
  const routeId = route === '' ? 'home' : route;
16
20
  if (s(`.main-btn-${routeId}`)) {
@@ -75,4 +79,61 @@ const listenQueryPathInstance = ({ id, routeId, event }, queryKey = 'cid') => {
75
79
  });
76
80
  };
77
81
 
78
- export { Router, setDocTitle, LoadRouter, RouterEvents, setQueryPath, listenQueryPathInstance };
82
+ const closeModalRouteChangeEvents = {};
83
+ const triggerCloseModalRouteChangeEvents = (newPath) => {
84
+ console.warn('[closeModalRouteChangeEvent]', newPath);
85
+ for (const event of Object.keys(closeModalRouteChangeEvents)) closeModalRouteChangeEvents[event](newPath);
86
+ };
87
+
88
+ const closeModalRouteChangeEvent = (options = {}) => {
89
+ const { route, RouterInstance, homeCid } = options;
90
+ if (!route) return;
91
+
92
+ let path = window.location.pathname;
93
+ if (path[path.length - 1] !== '/') path = `${path}/`;
94
+ let newPath = `${getProxyPath()}`;
95
+
96
+ if (path !== newPath) {
97
+ for (const subIdModal of Object.keys(Modal.Data).reverse()) {
98
+ if (Modal.Data[subIdModal]?.options?.route) {
99
+ newPath = `${newPath}${Modal.Data[subIdModal].options.route}`;
100
+ triggerCloseModalRouteChangeEvents(newPath);
101
+ setPath(newPath);
102
+ Modal.setTopModalCallback(subIdModal);
103
+ return setDocTitle({ ...RouterInstance, route: Modal.Data[subIdModal].options.route });
104
+ }
105
+ }
106
+ newPath = `${newPath}${homeCid ? `?cid=${homeCid}` : ''}`;
107
+ triggerCloseModalRouteChangeEvents(newPath);
108
+ setPath(newPath);
109
+ return setDocTitle({ ...RouterInstance, route: '' });
110
+ }
111
+ };
112
+
113
+ const handleModalViewRoute = (options = {}) => {
114
+ const { route, RouterInstance } = options;
115
+ if (!route) return;
116
+
117
+ let path = window.location.pathname;
118
+ if (path !== '/' && path[path.length - 1] === '/') path = path.slice(0, -1);
119
+ const proxyPath = getProxyPath();
120
+ const newPath = `${proxyPath}${route}`;
121
+
122
+ if (path !== newPath) {
123
+ setPath(newPath);
124
+ setDocTitle({ ...RouterInstance, route });
125
+ }
126
+ };
127
+
128
+ export {
129
+ Router,
130
+ setDocTitle,
131
+ LoadRouter,
132
+ RouterEvents,
133
+ setQueryPath,
134
+ listenQueryPathInstance,
135
+ closeModalRouteChangeEvent,
136
+ handleModalViewRoute,
137
+ closeModalRouteChangeEvents,
138
+ renderTitle,
139
+ };
@@ -60,10 +60,10 @@ const Translate = {
60
60
  this.Parse(language);
61
61
  if (s(`.action-btn-lang-render`)) htmls(`.action-btn-lang-render`, s('html').lang);
62
62
  },
63
- RenderSetting: async function () {
63
+ RenderSetting: async function (id) {
64
64
  return html` <div class="in section-mp">
65
65
  ${await DropDown.Render({
66
- id: 'settings-lang',
66
+ id: id ?? 'settings-lang',
67
67
  value: s('html').lang ? s('html').lang : 'en',
68
68
  label: html`${Translate.Render('lang')}`,
69
69
  data: ['en', 'es'].map((language) => {
@@ -512,6 +512,10 @@ const TranslateCore = {
512
512
  en: 'Your session has expired. Please log in again.',
513
513
  es: 'Su sesión ha expirado. Por favor, inicie sesión de nuevo.',
514
514
  };
515
+ Translate.Data['cron-management'] = {
516
+ en: 'Cron Management',
517
+ es: 'Gestion de cron jobs',
518
+ };
515
519
  },
516
520
  };
517
521
 
@@ -1,7 +1,7 @@
1
1
  import { Account } from '../core/Account.js';
2
2
  import { BtnIcon } from '../core/BtnIcon.js';
3
3
  import { getId, newInstance } from '../core/CommonJs.js';
4
- import { Css, Themes } from '../core/Css.js';
4
+ import { Css, darkTheme, extractBackgroundImageUrl, ThemeEvents, Themes } from '../core/Css.js';
5
5
  import { EventsUI } from '../core/EventsUI.js';
6
6
  import { LogIn } from '../core/LogIn.js';
7
7
  import { LogOut } from '../core/LogOut.js';
@@ -178,6 +178,32 @@ const MenuDefault = {
178
178
  htmlMainBody: options?.htmlMainBody ? options.htmlMainBody : undefined,
179
179
  });
180
180
 
181
+ ThemeEvents['ssr-background-image'] = () => {
182
+ if (darkTheme) {
183
+ const backgroundImage = `${getProxyPath()}assets/background/dark.jpg`;
184
+ htmls(
185
+ `.style-ssr-background-image`,
186
+ css`
187
+ .ssr-background-image {
188
+ background-image: url('${backgroundImage}');
189
+ }
190
+ `,
191
+ );
192
+ } else {
193
+ const backgroundImage = `${getProxyPath()}assets/background/white0-min.jpg`;
194
+ htmls(
195
+ `.style-ssr-background-image`,
196
+ css`
197
+ .ssr-background-image {
198
+ background-image: url('${backgroundImage}');
199
+ }
200
+ `,
201
+ );
202
+ }
203
+ };
204
+
205
+ setTimeout(ThemeEvents['ssr-background-image']);
206
+
181
207
  this.Data[id].sortable = new Sortable(s(`.menu-btn-container`), {
182
208
  animation: 150,
183
209
  group: `menu-sortable`,
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
3
+ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
4
+ <svg version="1.0" xmlns="http://www.w3.org/2000/svg"
5
+ width="269.000000pt" height="269.000000pt" viewBox="0 0 269.000000 269.000000"
6
+ preserveAspectRatio="xMidYMid meet">
7
+ <metadata>
8
+ Created by potrace 1.11, written by Peter Selinger 2001-2013
9
+ </metadata>
10
+ <g transform="translate(0.000000,269.000000) scale(0.100000,-0.100000)"
11
+ fill="#000000" stroke="none">
12
+ <path d="M980 2475 l0 -65 65 0 65 0 0 65 0 65 -65 0 -65 0 0 -65z"/>
13
+ <path d="M1700 2475 l0 -65 65 0 65 0 0 65 0 65 -65 0 -65 0 0 -65z"/>
14
+ <path d="M1110 2325 l0 -65 -85 0 -85 0 0 -140 0 -140 -60 0 -60 0 0 -160 0
15
+ -160 75 0 75 0 0 -100 0 -100 95 0 95 0 0 -40 0 -40 -240 0 -240 0 0 -445 0
16
+ -445 -65 0 -65 0 0 -135 0 -135 65 0 65 0 0 -55 0 -55 65 0 65 0 0 475 0 475
17
+ 85 0 85 0 0 -425 0 -425 425 0 425 0 0 425 0 425 85 0 85 0 0 -475 0 -475 65
18
+ 0 65 0 0 55 0 55 60 0 60 0 0 135 0 135 -60 0 -60 0 0 445 0 445 -240 0 -240
19
+ 0 0 35 0 35 95 0 95 0 0 105 0 105 75 0 75 0 0 160 0 160 -60 0 -60 0 0 140 0
20
+ 140 -85 0 -85 0 0 65 0 65 -75 0 -75 0 0 -65 0 -65 -145 0 -145 0 0 65 0 65
21
+ -75 0 -75 0 0 -65z m210 -505 l0 -110 -110 0 -110 0 0 110 0 110 110 0 110 0
22
+ 0 -110z m390 0 l0 -70 -70 0 -70 0 0 70 0 70 70 0 70 0 0 -70z"/>
23
+ </g>
24
+ </svg>
@@ -1,12 +1,12 @@
1
1
  SrrComponent = ({ backgroundImage, metadata }) => html`
2
2
  ${backgroundImage
3
- ? html`<style>
3
+ ? html`<style class="style-ssr-background-image">
4
4
  .ssr-background-image {
5
5
  background-image: url('${backgroundImage}');
6
6
  }
7
7
  </style>`
8
8
  : metadata?.themeColor
9
- ? html`<style>
9
+ ? html`<style class="style-ssr-background-image">
10
10
  .ssr-background-image {
11
11
  background: ${metadata.themeColor};
12
12
  }
package/src/index.js CHANGED
@@ -34,7 +34,7 @@ class Underpost {
34
34
  * @type {String}
35
35
  * @memberof Underpost
36
36
  */
37
- static version = 'v2.8.844';
37
+ static version = 'v2.8.846';
38
38
  /**
39
39
  * Repository cli API
40
40
  * @static