@ecodev/natural 62.1.2 → 63.0.0

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 (183) hide show
  1. package/fesm2022/ecodev-natural-vanilla.mjs +1193 -0
  2. package/fesm2022/ecodev-natural-vanilla.mjs.map +1 -0
  3. package/fesm2022/ecodev-natural.mjs +436 -425
  4. package/fesm2022/ecodev-natural.mjs.map +1 -1
  5. package/lib/classes/network-activity.service.d.ts +54 -0
  6. package/lib/classes/validators.d.ts +1 -1
  7. package/lib/modules/columns-picker/columns-picker.component.d.ts +2 -2
  8. package/lib/modules/common/services/seo.provider.d.ts +2 -2
  9. package/lib/modules/dropdown-components/type-select/type-select.component.d.ts +1 -1
  10. package/lib/modules/file/abstract-file.d.ts +6 -3
  11. package/lib/modules/file/component/file.component.d.ts +2 -2
  12. package/lib/modules/file/file-drop.directive.d.ts +2 -3
  13. package/lib/modules/fixed-button-detail/fixed-button-detail.component.d.ts +2 -3
  14. package/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.component.d.ts +3 -3
  15. package/lib/modules/icon/icon.module.d.ts +2 -2
  16. package/lib/modules/panels/panels.service.d.ts +1 -2
  17. package/lib/modules/relations/relations.component.d.ts +3 -3
  18. package/lib/modules/search/dropdown-container/dropdown-container.component.d.ts +2 -3
  19. package/lib/modules/search/group/group.component.d.ts +2 -3
  20. package/lib/modules/search/input/input.component.d.ts +5 -5
  21. package/lib/modules/search/search/search.component.d.ts +2 -2
  22. package/lib/modules/select/abstract-select.component.d.ts +3 -3
  23. package/lib/modules/select/select/select.component.d.ts +1 -1
  24. package/lib/modules/sidenav/sidenav-container/sidenav-container.component.d.ts +1 -1
  25. package/lib/modules/table-button/table-button.component.d.ts +4 -2
  26. package/package.json +16 -14
  27. package/public-api.d.ts +1 -0
  28. package/src/lib/_natural.theme.scss +1 -2
  29. package/vanilla/index.d.ts +5 -0
  30. package/vanilla/package.json +3 -0
  31. package/vanilla/public-api.d.ts +11 -0
  32. package/vanilla/src/lib/classes/crypto.d.ts +8 -0
  33. package/vanilla/src/lib/classes/data-source.d.ts +32 -0
  34. package/vanilla/src/lib/classes/query-variable-manager-utils.d.ts +2 -0
  35. package/vanilla/src/lib/classes/query-variable-manager.d.ts +91 -0
  36. package/vanilla/src/lib/classes/signing.d.ts +7 -0
  37. package/vanilla/src/lib/classes/utility.d.ts +77 -0
  38. package/vanilla/src/lib/modules/search/classes/graphql-doctrine.types.d.ts +83 -0
  39. package/vanilla/src/lib/modules/search/classes/utils.d.ts +17 -0
  40. package/vanilla/src/lib/modules/search/types/dropdown-component.d.ts +20 -0
  41. package/vanilla/src/lib/modules/search/types/facet.d.ts +75 -0
  42. package/vanilla/src/lib/modules/search/types/values.d.ts +32 -0
  43. package/vanilla/src/lib/services/abstract-model.service.d.ts +244 -0
  44. package/vanilla/src/lib/services/debounce.service.d.ts +52 -0
  45. package/vanilla/src/lib/types/types.d.ts +100 -0
  46. package/esm2022/ecodev-natural.mjs +0 -5
  47. package/esm2022/lib/classes/abstract-detail.mjs +0 -229
  48. package/esm2022/lib/classes/abstract-editable-list.mjs +0 -99
  49. package/esm2022/lib/classes/abstract-list.mjs +0 -461
  50. package/esm2022/lib/classes/abstract-navigable-list.mjs +0 -133
  51. package/esm2022/lib/classes/apollo-utils.mjs +0 -59
  52. package/esm2022/lib/classes/crypto.mjs +0 -23
  53. package/esm2022/lib/classes/cumulative-changes.mjs +0 -50
  54. package/esm2022/lib/classes/data-source.mjs +0 -71
  55. package/esm2022/lib/classes/providers.mjs +0 -13
  56. package/esm2022/lib/classes/query-variable-manager-utils.mjs +0 -14
  57. package/esm2022/lib/classes/query-variable-manager.mjs +0 -172
  58. package/esm2022/lib/classes/rxjs.mjs +0 -54
  59. package/esm2022/lib/classes/signing.mjs +0 -38
  60. package/esm2022/lib/classes/tld.mjs +0 -1476
  61. package/esm2022/lib/classes/utility.mjs +0 -234
  62. package/esm2022/lib/classes/validators.mjs +0 -179
  63. package/esm2022/lib/directives/http-prefix.directive.mjs +0 -47
  64. package/esm2022/lib/modules/alert/alert.service.mjs +0 -53
  65. package/esm2022/lib/modules/alert/confirm.component.mjs +0 -16
  66. package/esm2022/lib/modules/alert/public-api.mjs +0 -6
  67. package/esm2022/lib/modules/avatar/component/avatar.component.mjs +0 -203
  68. package/esm2022/lib/modules/avatar/public-api.mjs +0 -6
  69. package/esm2022/lib/modules/avatar/service/avatar.service.mjs +0 -63
  70. package/esm2022/lib/modules/avatar/sources/gravatar.mjs +0 -29
  71. package/esm2022/lib/modules/avatar/sources/image.mjs +0 -13
  72. package/esm2022/lib/modules/avatar/sources/initials.mjs +0 -39
  73. package/esm2022/lib/modules/avatar/sources/source.mjs +0 -16
  74. package/esm2022/lib/modules/columns-picker/columns-picker.component.mjs +0 -145
  75. package/esm2022/lib/modules/columns-picker/public-api.mjs +0 -5
  76. package/esm2022/lib/modules/columns-picker/types.mjs +0 -2
  77. package/esm2022/lib/modules/common/directives/background-density.directive.mjs +0 -63
  78. package/esm2022/lib/modules/common/directives/linkable-tab.directive.mjs +0 -93
  79. package/esm2022/lib/modules/common/directives/src-density.directive.mjs +0 -72
  80. package/esm2022/lib/modules/common/pipes/capitalize.pipe.mjs +0 -24
  81. package/esm2022/lib/modules/common/pipes/ellipsis.pipe.mjs +0 -17
  82. package/esm2022/lib/modules/common/pipes/enum.pipe.mjs +0 -24
  83. package/esm2022/lib/modules/common/pipes/time-ago.pipe.mjs +0 -140
  84. package/esm2022/lib/modules/common/public-api.mjs +0 -14
  85. package/esm2022/lib/modules/common/services/memory-storage.mjs +0 -110
  86. package/esm2022/lib/modules/common/services/seo.provider.mjs +0 -23
  87. package/esm2022/lib/modules/common/services/seo.service.mjs +0 -235
  88. package/esm2022/lib/modules/detail-header/detail-header.component.mjs +0 -84
  89. package/esm2022/lib/modules/detail-header/public-api.mjs +0 -5
  90. package/esm2022/lib/modules/dialog-trigger/dialog-trigger.component.mjs +0 -72
  91. package/esm2022/lib/modules/dialog-trigger/public-api.mjs +0 -5
  92. package/esm2022/lib/modules/dropdown-components/abstract-association-select-component.directive.mjs +0 -100
  93. package/esm2022/lib/modules/dropdown-components/public-api.mjs +0 -14
  94. package/esm2022/lib/modules/dropdown-components/type-boolean/type-boolean.component.mjs +0 -39
  95. package/esm2022/lib/modules/dropdown-components/type-date/type-date.component.mjs +0 -173
  96. package/esm2022/lib/modules/dropdown-components/type-date-range/type-date-range.component.mjs +0 -134
  97. package/esm2022/lib/modules/dropdown-components/type-hierarchic-selector/type-hierarchic-selector.component.mjs +0 -80
  98. package/esm2022/lib/modules/dropdown-components/type-natural-select/type-natural-select.component.mjs +0 -48
  99. package/esm2022/lib/modules/dropdown-components/type-number/type-number.component.mjs +0 -110
  100. package/esm2022/lib/modules/dropdown-components/type-options/type-options.component.mjs +0 -64
  101. package/esm2022/lib/modules/dropdown-components/type-select/type-select.component.mjs +0 -175
  102. package/esm2022/lib/modules/dropdown-components/type-text/type-text.component.mjs +0 -62
  103. package/esm2022/lib/modules/dropdown-components/types.mjs +0 -41
  104. package/esm2022/lib/modules/dropdown-components/utils.mjs +0 -35
  105. package/esm2022/lib/modules/file/abstract-file.mjs +0 -230
  106. package/esm2022/lib/modules/file/component/file.component.mjs +0 -172
  107. package/esm2022/lib/modules/file/file-drop.directive.mjs +0 -111
  108. package/esm2022/lib/modules/file/file-select.directive.mjs +0 -26
  109. package/esm2022/lib/modules/file/file.service.mjs +0 -43
  110. package/esm2022/lib/modules/file/public-api.mjs +0 -9
  111. package/esm2022/lib/modules/file/types.mjs +0 -2
  112. package/esm2022/lib/modules/file/utils.mjs +0 -129
  113. package/esm2022/lib/modules/fixed-button/fixed-button.component.mjs +0 -30
  114. package/esm2022/lib/modules/fixed-button/public-api.mjs +0 -5
  115. package/esm2022/lib/modules/fixed-button-detail/fixed-button-detail.component.mjs +0 -56
  116. package/esm2022/lib/modules/fixed-button-detail/public-api.mjs +0 -5
  117. package/esm2022/lib/modules/hierarchic-selector/classes/flat-node.mjs +0 -18
  118. package/esm2022/lib/modules/hierarchic-selector/classes/hierarchic-configuration.mjs +0 -2
  119. package/esm2022/lib/modules/hierarchic-selector/classes/hierarchic-filters-configuration.mjs +0 -2
  120. package/esm2022/lib/modules/hierarchic-selector/classes/model-node.mjs +0 -14
  121. package/esm2022/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.component.mjs +0 -398
  122. package/esm2022/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.service.mjs +0 -243
  123. package/esm2022/lib/modules/hierarchic-selector/hierarchic-selector-dialog/hierarchic-selector-dialog.component.mjs +0 -38
  124. package/esm2022/lib/modules/hierarchic-selector/hierarchic-selector-dialog/hierarchic-selector-dialog.service.mjs +0 -22
  125. package/esm2022/lib/modules/hierarchic-selector/public-api.mjs +0 -10
  126. package/esm2022/lib/modules/icon/icon.directive.mjs +0 -96
  127. package/esm2022/lib/modules/icon/icon.module.mjs +0 -33
  128. package/esm2022/lib/modules/icon/public-api.mjs +0 -6
  129. package/esm2022/lib/modules/logger/error-handler.mjs +0 -87
  130. package/esm2022/lib/modules/logger/error.module.mjs +0 -22
  131. package/esm2022/lib/modules/logger/public-api.mjs +0 -6
  132. package/esm2022/lib/modules/matomo/matomo.service.mjs +0 -96
  133. package/esm2022/lib/modules/matomo/public-api.mjs +0 -5
  134. package/esm2022/lib/modules/panels/abstract-panel.mjs +0 -76
  135. package/esm2022/lib/modules/panels/fallback-if-no-opened-panels.urlmatcher.mjs +0 -12
  136. package/esm2022/lib/modules/panels/panels.component.mjs +0 -27
  137. package/esm2022/lib/modules/panels/panels.module.mjs +0 -10
  138. package/esm2022/lib/modules/panels/panels.service.mjs +0 -329
  139. package/esm2022/lib/modules/panels/panels.urlmatcher.mjs +0 -75
  140. package/esm2022/lib/modules/panels/public-api.mjs +0 -11
  141. package/esm2022/lib/modules/panels/types.mjs +0 -3
  142. package/esm2022/lib/modules/relations/public-api.mjs +0 -5
  143. package/esm2022/lib/modules/relations/relations.component.mjs +0 -254
  144. package/esm2022/lib/modules/search/classes/graphql-doctrine.mjs +0 -111
  145. package/esm2022/lib/modules/search/classes/graphql-doctrine.types.mjs +0 -14
  146. package/esm2022/lib/modules/search/classes/transformers.mjs +0 -142
  147. package/esm2022/lib/modules/search/classes/url.mjs +0 -53
  148. package/esm2022/lib/modules/search/classes/utils.mjs +0 -25
  149. package/esm2022/lib/modules/search/dropdown-container/dropdown-container-animations.mjs +0 -44
  150. package/esm2022/lib/modules/search/dropdown-container/dropdown-container.component.mjs +0 -87
  151. package/esm2022/lib/modules/search/dropdown-container/dropdown-ref.mjs +0 -24
  152. package/esm2022/lib/modules/search/dropdown-container/dropdown.service.mjs +0 -90
  153. package/esm2022/lib/modules/search/facet-selector/facet-selector.component.mjs +0 -45
  154. package/esm2022/lib/modules/search/group/group.component.mjs +0 -53
  155. package/esm2022/lib/modules/search/input/input.component.mjs +0 -365
  156. package/esm2022/lib/modules/search/public-api.mjs +0 -7
  157. package/esm2022/lib/modules/search/search/search.component.mjs +0 -102
  158. package/esm2022/lib/modules/search/types/dropdown-component.mjs +0 -2
  159. package/esm2022/lib/modules/search/types/facet.mjs +0 -2
  160. package/esm2022/lib/modules/search/types/values.mjs +0 -2
  161. package/esm2022/lib/modules/select/abstract-select.component.mjs +0 -232
  162. package/esm2022/lib/modules/select/public-api.mjs +0 -7
  163. package/esm2022/lib/modules/select/select/select.component.mjs +0 -310
  164. package/esm2022/lib/modules/select/select-enum/select-enum.component.mjs +0 -57
  165. package/esm2022/lib/modules/select/select-hierarchic/select-hierarchic.component.mjs +0 -155
  166. package/esm2022/lib/modules/sidenav/public-api.mjs +0 -9
  167. package/esm2022/lib/modules/sidenav/sidenav/sidenav.component.mjs +0 -15
  168. package/esm2022/lib/modules/sidenav/sidenav-container/sidenav-container.component.mjs +0 -90
  169. package/esm2022/lib/modules/sidenav/sidenav-content/sidenav-content.component.mjs +0 -11
  170. package/esm2022/lib/modules/sidenav/sidenav-stack.service.mjs +0 -50
  171. package/esm2022/lib/modules/sidenav/sidenav.service.mjs +0 -196
  172. package/esm2022/lib/modules/stamp/public-api.mjs +0 -5
  173. package/esm2022/lib/modules/stamp/stamp.component.mjs +0 -23
  174. package/esm2022/lib/modules/table-button/public-api.mjs +0 -5
  175. package/esm2022/lib/modules/table-button/table-button.component.mjs +0 -78
  176. package/esm2022/lib/services/abstract-model.service.mjs +0 -526
  177. package/esm2022/lib/services/debounce.service.mjs +0 -149
  178. package/esm2022/lib/services/enum.service.mjs +0 -64
  179. package/esm2022/lib/services/link-mutation.service.mjs +0 -154
  180. package/esm2022/lib/services/persistence.service.mjs +0 -115
  181. package/esm2022/lib/services/swiss-parsing-date-adapter.service.mjs +0 -63
  182. package/esm2022/lib/types/types.mjs +0 -2
  183. package/esm2022/public-api.mjs +0 -46
@@ -0,0 +1,1193 @@
1
+ import { pickBy, cloneDeep, uniq, groupBy, mergeWith, defaultsDeep, omit, merge, pick, defaults } from 'lodash-es';
2
+ import { BehaviorSubject, from, switchMap, ReplaySubject, Subject, debounceTime, raceWith, take, mergeMap, EMPTY, shareReplay, catchError, of, Observable, forkJoin, map, first, combineLatest } from 'rxjs';
3
+ import { Apollo, gql } from 'apollo-angular';
4
+ import { NetworkStatus } from '@apollo/client/core';
5
+ import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
6
+ import { takeWhile, map as map$1, switchMap as switchMap$1, filter, debounceTime as debounceTime$1, tap, shareReplay as shareReplay$1, startWith } from 'rxjs/operators';
7
+ import * as i0 from '@angular/core';
8
+ import { Injectable, inject } from '@angular/core';
9
+
10
+ function formatIsoDate(date) {
11
+ if (!date) {
12
+ return null;
13
+ }
14
+ const y = date.getFullYear();
15
+ const m = date.getMonth() + 1;
16
+ const d = date.getDate();
17
+ return y + '-' + (m < 10 ? '0' : '') + m + '-' + (d < 10 ? '0' : '') + d;
18
+ }
19
+ /**
20
+ * Format a date and time in a way that will preserve the local time zone.
21
+ * This allows the server side to know the day (without time) that was selected on client side.
22
+ *
23
+ * So something like: "2021-09-23T17:57:16+09:00"
24
+ */
25
+ function formatIsoDateTime(date) {
26
+ const timezoneOffsetInMinutes = date.getTimezoneOffset();
27
+ const timezoneOffsetInHours = -Math.trunc(timezoneOffsetInMinutes / 60); // UTC minus local time
28
+ const sign = timezoneOffsetInHours >= 0 ? '+' : '-';
29
+ const hoursLeadingZero = Math.abs(timezoneOffsetInHours) < 10 ? '0' : '';
30
+ const remainderMinutes = -(timezoneOffsetInMinutes % 60);
31
+ const minutesLeadingZero = Math.abs(remainderMinutes) < 10 ? '0' : '';
32
+ // It's a bit unfortunate that we need to construct a new Date instance,
33
+ // but we don't want the original Date instance to be modified
34
+ const correctedDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
35
+ correctedDate.setHours(date.getHours() + timezoneOffsetInHours);
36
+ const iso = correctedDate
37
+ .toISOString()
38
+ .replace(/\.\d{3}Z/, '')
39
+ .replace('Z', '');
40
+ return (iso +
41
+ sign +
42
+ hoursLeadingZero +
43
+ Math.abs(timezoneOffsetInHours).toString() +
44
+ ':' +
45
+ minutesLeadingZero +
46
+ remainderMinutes);
47
+ }
48
+ /**
49
+ * Relations to full objects are converted to their IDs only.
50
+ *
51
+ * So {user: {id: 123}} becomes {user: 123}
52
+ */
53
+ function relationsToIds(object) {
54
+ const newObj = {};
55
+ Object.keys(object).forEach(key => {
56
+ let value = object[key];
57
+ if (value === null || value === undefined) {
58
+ // noop
59
+ }
60
+ else if (hasId(value)) {
61
+ value = value.id;
62
+ }
63
+ else if (Array.isArray(value)) {
64
+ value = value.map((i) => (hasId(i) ? i.id : i));
65
+ }
66
+ else if (typeof value === 'object' && !(value instanceof File) && !(value instanceof Date)) {
67
+ value = pickBy(value, (v, k) => k !== '__typename'); // omit(value, ['__typename']) ?
68
+ }
69
+ newObj[key] = value;
70
+ });
71
+ return newObj;
72
+ }
73
+ function hasId(value) {
74
+ return !!value && typeof value === 'object' && 'id' in value && !!value.id;
75
+ }
76
+ /**
77
+ * Returns the plural form of the given name
78
+ *
79
+ * This is **not** necessarily valid english grammar. Its only purpose is for internal usage, not for humans.
80
+ *
81
+ * This **MUST** be kept in sync with `\Ecodev\Felix\Api\Plural:make()`.
82
+ *
83
+ * This is a bit performance-sensitive, so we should keep it fast and only cover cases that we actually need.
84
+ */
85
+ function makePlural(name) {
86
+ // Words ending in a y preceded by a vowel form their plurals by adding -s:
87
+ if (/[aeiou]y$/.exec(name)) {
88
+ return name + 's';
89
+ }
90
+ const plural = name + 's';
91
+ return plural.replace(/ys$/, 'ies').replace(/ss$/, 'ses').replace(/xs$/, 'xes');
92
+ }
93
+ /**
94
+ * Returns the string with the first letter as capital
95
+ */
96
+ function upperCaseFirstLetter(term) {
97
+ return term.charAt(0).toUpperCase() + term.slice(1);
98
+ }
99
+ /**
100
+ * Replace all attributes of first object with the ones provided by the second, but keeps the reference
101
+ */
102
+ function replaceObjectKeepingReference(obj, newObj) {
103
+ if (!obj || !newObj) {
104
+ return;
105
+ }
106
+ Object.keys(obj).forEach(key => {
107
+ delete obj[key];
108
+ });
109
+ Object.keys(newObj).forEach(key => {
110
+ obj[key] = newObj[key];
111
+ });
112
+ }
113
+ /**
114
+ * Get contrasted color for text in the slider thumb
115
+ * @param hexBgColor string in hexadecimals representing the background color
116
+ */
117
+ function getForegroundColor(hexBgColor) {
118
+ const rgb = hexToRgb(hexBgColor.slice(0, 7)); // splice remove alpha and consider only "visible" color at 100% alpha
119
+ const o = Math.round((rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000);
120
+ return o > 125 ? 'black' : 'white';
121
+ }
122
+ function hexToRgb(hex) {
123
+ // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
124
+ const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
125
+ hex = hex.replace(shorthandRegex, (m, r, g, b) => {
126
+ return r + r + g + g + b + b;
127
+ });
128
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
129
+ return result
130
+ ? {
131
+ r: parseInt(result[1], 16),
132
+ g: parseInt(result[2], 16),
133
+ b: parseInt(result[3], 16),
134
+ }
135
+ : {
136
+ r: 0,
137
+ g: 0,
138
+ b: 0,
139
+ };
140
+ }
141
+ /**
142
+ * Convert RGB color to hexadecimal color
143
+ *
144
+ * ```ts
145
+ * rgbToHex('rgb(255, 00, 255)'); // '#FF00FF'
146
+ * ```
147
+ */
148
+ function rgbToHex(rgb) {
149
+ const m = /^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/.exec(rgb);
150
+ if (!m) {
151
+ return rgb;
152
+ }
153
+ return '#' + [m[1], m[2], m[3]].map(x => parseInt(x).toString(16).toUpperCase().padStart(2, '0')).join('');
154
+ }
155
+ /**
156
+ * During lodash.mergeWith, overrides arrays
157
+ */
158
+ function mergeOverrideArray(destValue, source) {
159
+ if (Array.isArray(source)) {
160
+ return source;
161
+ }
162
+ }
163
+ /**
164
+ * Copy text to clipboard.
165
+ * Accepts line breaks `\n` as textarea do.
166
+ */
167
+ function copyToClipboard(document, text) {
168
+ const input = document.createElement('textarea');
169
+ document.body.append(input);
170
+ input.value = text;
171
+ input.select();
172
+ document.execCommand('copy');
173
+ document.body.removeChild(input);
174
+ }
175
+ function deepFreeze(o) {
176
+ Object.values(o).forEach(v => Object.isFrozen(v) || deepFreeze(v));
177
+ return Object.freeze(o);
178
+ }
179
+ /**
180
+ * Return a valid PaginationInput from whatever is available from data. Invalid properties/types will be dropped.
181
+ */
182
+ function validatePagination(data) {
183
+ if (!data || typeof data !== 'object' || Array.isArray(data)) {
184
+ return null;
185
+ }
186
+ const pagination = {};
187
+ if ('offset' in data && (data.offset === null || typeof data.offset === 'number')) {
188
+ pagination.offset = data.offset;
189
+ }
190
+ if ('pageIndex' in data && (data.pageIndex === null || typeof data.pageIndex === 'number')) {
191
+ pagination.pageIndex = data.pageIndex;
192
+ }
193
+ if ('pageSize' in data && (data.pageSize === null || typeof data.pageSize === 'number')) {
194
+ pagination.pageSize = data.pageSize;
195
+ }
196
+ return pagination;
197
+ }
198
+ /**
199
+ * Return a valid Sortings from whatever is available from data. Invalid properties/types will be dropped.
200
+ */
201
+ function validateSorting(data) {
202
+ if (!Array.isArray(data)) {
203
+ return null;
204
+ }
205
+ const result = [];
206
+ data.forEach(s => {
207
+ const r = validateOneSorting(s);
208
+ if (r) {
209
+ result.push(r);
210
+ }
211
+ });
212
+ return result;
213
+ }
214
+ function validateOneSorting(data) {
215
+ if (!data || typeof data !== 'object' || !('field' in data)) {
216
+ return null;
217
+ }
218
+ const sorting = { field: data.field };
219
+ if ('order' in data &&
220
+ (data.order === SortingOrder.ASC || data.order === SortingOrder.DESC || data.order === null)) {
221
+ sorting.order = data.order;
222
+ }
223
+ if ('nullAsHighest' in data && (data.nullAsHighest === null || typeof data.nullAsHighest === 'boolean')) {
224
+ sorting.nullAsHighest = data.nullAsHighest;
225
+ }
226
+ if ('emptyStringAsHighest' in data &&
227
+ (data.emptyStringAsHighest === null || typeof data.emptyStringAsHighest === 'boolean')) {
228
+ sorting.emptyStringAsHighest = data.emptyStringAsHighest;
229
+ }
230
+ return sorting;
231
+ }
232
+ /**
233
+ * Return valid columns from whatever is available from data. Invalid properties/types will be dropped.
234
+ */
235
+ function validateColumns(data) {
236
+ if (typeof data !== 'string') {
237
+ return null;
238
+ }
239
+ return data.split(',').filter(string => string);
240
+ }
241
+
242
+ // Basic; loosely typed structure for graphql-doctrine filters
243
+ // Logical operator to be used in conditions
244
+ var LogicalOperator;
245
+ (function (LogicalOperator) {
246
+ LogicalOperator["AND"] = "AND";
247
+ LogicalOperator["OR"] = "OR";
248
+ })(LogicalOperator || (LogicalOperator = {}));
249
+ // Join types to be used in DQL
250
+ var JoinType;
251
+ (function (JoinType) {
252
+ JoinType["innerJoin"] = "innerJoin";
253
+ JoinType["leftJoin"] = "leftJoin";
254
+ })(JoinType || (JoinType = {}));
255
+
256
+ function hasMixedGroupLogic(groups) {
257
+ // Complete lack of definition by fallback on AND operator
258
+ const completedGroups = cloneDeep(groups).map(group => {
259
+ if (!group.groupLogic) {
260
+ group.groupLogic = LogicalOperator.AND;
261
+ }
262
+ return group;
263
+ });
264
+ const groupLogics = uniq(Object.keys(groupBy(completedGroups.slice(1), 'groupLogic')));
265
+ return groupLogics.length > 1;
266
+ }
267
+
268
+ var SortingOrder;
269
+ (function (SortingOrder) {
270
+ SortingOrder["ASC"] = "ASC";
271
+ SortingOrder["DESC"] = "DESC";
272
+ })(SortingOrder || (SortingOrder = {}));
273
+ /**
274
+ * During lodash merge, concat arrays
275
+ */
276
+ function mergeConcatArray(destValue, source) {
277
+ if (Array.isArray(source)) {
278
+ if (destValue) {
279
+ return destValue.concat(source);
280
+ }
281
+ else {
282
+ return source;
283
+ }
284
+ }
285
+ }
286
+ /**
287
+ * Filter manager stores a set of channels that contain a variable object and exposes an observable "variables" that updates with the result
288
+ * of all channels merged together.
289
+ *
290
+ * A channel is supposed to be used by a given aspect of the GUI (pagination, sorting, search, others ?).
291
+ *
292
+ * ```ts
293
+ * const fm = new QueryVariablesManager();
294
+ * fm.merge('componentA-variables', {a : [1, 2, 3]});
295
+ * ```
296
+ *
297
+ * Variables attributes is a BehaviorSubject. That mean it's not mandatory to subscribe, we can just call getValue or value attributes on
298
+ * it :
299
+ *
300
+ * ```ts
301
+ * console.log(fm.variables.value); // {a : [1, 2, 3]}
302
+ * ```
303
+ *
304
+ * Set new variables for 'componentA-variables':
305
+ *
306
+ * ```ts
307
+ * fm.merge('componentA-variables', {a : [1, 2]});
308
+ * console.log(fm.variables.value); // {a : [1, 2, 3]}
309
+ * ```
310
+ *
311
+ * Set new variables for new channel:
312
+ *
313
+ * ```ts
314
+ * fm.merge('componentB-variables', {a : [3, 4]});
315
+ * console.log(fm.variables.value); // {a : [1, 2, 3, 4]}
316
+ * ```
317
+ */
318
+ class NaturalQueryVariablesManager {
319
+ variables = new BehaviorSubject(undefined);
320
+ channels = new Map();
321
+ constructor(queryVariablesManager) {
322
+ if (queryVariablesManager) {
323
+ this.channels = queryVariablesManager.getChannelsCopy();
324
+ this.updateVariables();
325
+ }
326
+ }
327
+ /**
328
+ * Set or override all the variables that may exist in the given channel
329
+ */
330
+ set(channelName, variables) {
331
+ // cloneDeep to change reference and prevent some interactions when merge
332
+ if (variables) {
333
+ this.channels.set(channelName, cloneDeep(variables));
334
+ }
335
+ else {
336
+ this.channels.delete(channelName);
337
+ }
338
+ this.updateVariables();
339
+ }
340
+ /**
341
+ * Return a deep clone of the variables for the given channel name.
342
+ *
343
+ * Avoid returning the same reference to prevent an attribute change, then another channel update that would
344
+ * used this changed attribute without having explicitly asked QueryVariablesManager to update it.
345
+ */
346
+ get(channelName) {
347
+ return cloneDeep(this.channels.get(channelName));
348
+ }
349
+ /**
350
+ * Merge variable into a channel, overriding arrays in same channel / key
351
+ */
352
+ merge(channelName, newVariables) {
353
+ const variables = this.channels.get(channelName);
354
+ if (variables) {
355
+ mergeWith(variables, cloneDeep(newVariables), mergeOverrideArray); // merge preserves references, cloneDeep prevent that
356
+ this.updateVariables();
357
+ }
358
+ else {
359
+ this.set(channelName, newVariables);
360
+ }
361
+ }
362
+ /**
363
+ * Apply default values to a channel
364
+ * Note : lodash defaults only defines values on destinations keys that are undefined
365
+ */
366
+ defaults(channelName, newVariables) {
367
+ const variables = this.channels.get(channelName);
368
+ if (variables) {
369
+ defaultsDeep(variables, newVariables);
370
+ this.updateVariables();
371
+ }
372
+ else {
373
+ this.set(channelName, newVariables);
374
+ }
375
+ }
376
+ getChannelsCopy() {
377
+ return new Map(this.channels);
378
+ }
379
+ /**
380
+ * Merge channels in a single object
381
+ * Arrays are concatenated
382
+ * Filter groups are combined smartly (see mergeGroupList)
383
+ */
384
+ updateVariables() {
385
+ const merged = {};
386
+ this.channels.forEach(channelVariables => {
387
+ if (channelVariables.filter) {
388
+ // Merge filter's groups first
389
+ const groups = this.mergeGroupList(merged.filter?.groups ? merged.filter.groups : [], channelVariables.filter.groups || []);
390
+ // Merge filter key (that contain groups)
391
+ if (groups?.length) {
392
+ if (merged.filter) {
393
+ merged.filter.groups = groups;
394
+ }
395
+ else {
396
+ merged.filter = { groups: groups };
397
+ }
398
+ }
399
+ else {
400
+ mergeWith(merged, { filter: channelVariables.filter }, mergeConcatArray);
401
+ }
402
+ }
403
+ // Merge other attributes than filter
404
+ mergeWith(merged, omit(channelVariables, 'filter'), mergeConcatArray);
405
+ });
406
+ this.variables.next(merged);
407
+ }
408
+ /**
409
+ * Cross merge two filters
410
+ * Only accepts groups with same groupLogic (ignores the first one, because there is no groupLogic in this one)
411
+ * @throws In case two non-empty lists of groups are given and at one of them mix groupLogic value, throws an error
412
+ */
413
+ mergeGroupList(groupsA, groupsB) {
414
+ if (groupsA.length === 0 && groupsB.length === 0) {
415
+ return []; // empty listings, return empty lists
416
+ }
417
+ if (groupsA.length === 0 && groupsB.length > 0) {
418
+ return groupsB; // One list is empty, return the one that is not
419
+ }
420
+ if (groupsB.length === 0 && groupsA.length > 0) {
421
+ return groupsA; // One list is empty, return the one that is not
422
+ }
423
+ const groups = [];
424
+ if (hasMixedGroupLogic(groupsA) || hasMixedGroupLogic(groupsB)) {
425
+ throw Error('QueryVariables groups contain mixed group logics');
426
+ }
427
+ groupsA.forEach(groupA => {
428
+ groupsB.forEach(groupB => {
429
+ groups.push(mergeWith(cloneDeep(groupA), groupB, mergeConcatArray));
430
+ });
431
+ });
432
+ return groups;
433
+ }
434
+ }
435
+
436
+ function bufferToHexa(hashBuffer) {
437
+ const hashArray = new Uint8Array(hashBuffer); // convert buffer to byte array
438
+ return hashArray.reduce((result, byte) => result + byte.toString(16).padStart(2, '0'), ''); // convert bytes to hex string
439
+ }
440
+ /**
441
+ * Thin wrapper around browsers' native SubtleCrypto for convenience of use
442
+ */
443
+ async function sha256(message) {
444
+ const msgUint8 = new TextEncoder().encode(message); // encode as (utf-8) Uint8Array
445
+ const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8); // hash the message
446
+ return bufferToHexa(hashBuffer);
447
+ }
448
+ /**
449
+ * Thin wrapper around browsers' native SubtleCrypto for convenience of use
450
+ */
451
+ async function hmacSha256(secret, payload) {
452
+ const encoder = new TextEncoder();
453
+ const algorithm = { name: 'HMAC', hash: 'SHA-256' };
454
+ const key = await crypto.subtle.importKey('raw', encoder.encode(secret), algorithm, false, ['sign']);
455
+ const signature = await crypto.subtle.sign(algorithm.name, key, encoder.encode(payload));
456
+ return bufferToHexa(signature);
457
+ }
458
+
459
+ function getOperations(req) {
460
+ if (req.body instanceof FormData) {
461
+ const operations = req.body.get('operations');
462
+ if (typeof operations !== 'string') {
463
+ throw new Error('Cannot sign a GraphQL query that is using FormData but that is missing the key `operations`');
464
+ }
465
+ return operations;
466
+ }
467
+ else {
468
+ return JSON.stringify(req.body);
469
+ }
470
+ }
471
+ /**
472
+ * Sign all HTTP POST requests that are GraphQL queries against `/graphql` endpoint with a custom signature.
473
+ *
474
+ * The server will validate the signature before executing the GraphQL query.
475
+ */
476
+ function graphqlQuerySigner(key) {
477
+ return (req, next) => {
478
+ const mustSign = req.method === 'POST' && /\/graphql(\?|$)/.exec(req.url);
479
+ if (!mustSign) {
480
+ return next(req);
481
+ }
482
+ const operations = getOperations(req);
483
+ const timestamp = Math.round(Date.now() / 1000);
484
+ const payload = timestamp + operations;
485
+ return from(hmacSha256(key, payload)).pipe(switchMap(hash => {
486
+ const header = `v1.${timestamp}.${hash}`;
487
+ const signedRequest = req.clone({
488
+ headers: req.headers.set('X-Signature', header),
489
+ });
490
+ return next(signedRequest);
491
+ }));
492
+ };
493
+ }
494
+
495
+ /**
496
+ * Debounce subscriptions to update mutations, with the possibility to cancel one, flush one, or flush all of them.
497
+ *
498
+ * `modelService` is also used to separate objects by their types. So User with ID 1 is not confused with Product with ID 1.
499
+ *
500
+ * `id` must be the ID of the object that will be updated.
501
+ */
502
+ class NaturalDebounceService {
503
+ /**
504
+ * Stores the debounced update function
505
+ */
506
+ allDebouncedUpdateCache = new Map();
507
+ /**
508
+ * Debounce the `modelService.updateNow()` mutation for a short time. If called multiple times with the same
509
+ * modelService and id, it will postpone the subscription to the mutation.
510
+ *
511
+ * All input variables for the same object (same service and ID) will be cumulated over time. So it is possible
512
+ * to update `field1`, then `field2`, and they will be batched into a single XHR including `field1` and `field2`.
513
+ *
514
+ * But it will always keep the same debouncing timeline.
515
+ */
516
+ debounce(modelService, id, object) {
517
+ const debouncedUpdateCache = this.getMap(modelService);
518
+ let debounced = debouncedUpdateCache.get(id);
519
+ if (debounced) {
520
+ debounced.object = {
521
+ ...debounced.object,
522
+ ...object,
523
+ };
524
+ }
525
+ else {
526
+ const debouncer = new ReplaySubject(1);
527
+ let wasCancelled = false;
528
+ const canceller = new Subject();
529
+ canceller.subscribe(() => {
530
+ wasCancelled = true;
531
+ debouncer.complete();
532
+ canceller.complete();
533
+ this.delete(modelService, id);
534
+ });
535
+ const flusher = new Subject();
536
+ debounced = {
537
+ object,
538
+ debouncer,
539
+ canceller,
540
+ flusher,
541
+ modelService: modelService,
542
+ result: debouncer.pipe(debounceTime(2000), // Wait 2 seconds...
543
+ raceWith(flusher), // ...unless flusher is triggered
544
+ take(1), mergeMap(() => {
545
+ this.delete(modelService, id);
546
+ if (wasCancelled || !debounced) {
547
+ return EMPTY;
548
+ }
549
+ return modelService.updateNow(debounced.object);
550
+ }), shareReplay()),
551
+ };
552
+ debouncedUpdateCache.set(id, debounced);
553
+ }
554
+ // Notify our debounced update each time we ask to update
555
+ debounced.debouncer.next();
556
+ // Return and observable that is updated when mutation is done
557
+ return debounced.result;
558
+ }
559
+ cancelOne(modelService, id) {
560
+ const debounced = this.allDebouncedUpdateCache.get(modelService)?.get(id);
561
+ debounced?.canceller.next();
562
+ }
563
+ /**
564
+ * Immediately execute the pending update, if any.
565
+ *
566
+ * It should typically be called before resolving the object, to mutate it before re-fetching it from server.
567
+ *
568
+ * The returned observable will complete when the update completes, even if it errors.
569
+ */
570
+ flushOne(modelService, id) {
571
+ const debounced = this.allDebouncedUpdateCache.get(modelService)?.get(id);
572
+ return this.internalFlush(debounced ? [debounced] : []);
573
+ }
574
+ /**
575
+ * Immediately execute all pending updates.
576
+ *
577
+ * It should typically be called before login out.
578
+ *
579
+ * The returned observable will complete when all updates complete, even if some of them error.
580
+ */
581
+ flush() {
582
+ const all = [];
583
+ this.allDebouncedUpdateCache.forEach(map => map.forEach(debounced => all.push(debounced)));
584
+ return this.internalFlush(all);
585
+ }
586
+ internalFlush(debounceds) {
587
+ const all = [];
588
+ const allFlusher = [];
589
+ debounceds.forEach(debounced => {
590
+ all.push(debounced.result.pipe(catchError(() => of(undefined))));
591
+ allFlusher.push(debounced.flusher);
592
+ });
593
+ if (!all.length) {
594
+ all.push(of(undefined));
595
+ }
596
+ return new Observable(subscriber => {
597
+ const subscription = forkJoin(all)
598
+ .pipe(map(() => undefined))
599
+ .subscribe(subscriber);
600
+ // Flush only after subscription process is finished
601
+ allFlusher.forEach(flusher => flusher.next());
602
+ return subscription;
603
+ });
604
+ }
605
+ /**
606
+ * Count of pending updates
607
+ */
608
+ get count() {
609
+ let count = 0;
610
+ this.allDebouncedUpdateCache.forEach(map => (count += map.size));
611
+ return count;
612
+ }
613
+ getMap(modelService) {
614
+ let debouncedUpdateCache = this.allDebouncedUpdateCache.get(modelService);
615
+ if (!debouncedUpdateCache) {
616
+ debouncedUpdateCache = new Map();
617
+ this.allDebouncedUpdateCache.set(modelService, debouncedUpdateCache);
618
+ }
619
+ return debouncedUpdateCache;
620
+ }
621
+ delete(modelService, id) {
622
+ const map = this.allDebouncedUpdateCache.get(modelService);
623
+ if (!map) {
624
+ return;
625
+ }
626
+ map.delete(id);
627
+ if (!map.size) {
628
+ this.allDebouncedUpdateCache.delete(modelService);
629
+ }
630
+ }
631
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: NaturalDebounceService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
632
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: NaturalDebounceService, providedIn: 'root' });
633
+ }
634
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: NaturalDebounceService, decorators: [{
635
+ type: Injectable,
636
+ args: [{
637
+ providedIn: 'root',
638
+ }]
639
+ }] });
640
+
641
+ /**
642
+ * Lookup a facet by its `name` and then by its `field`, or return null if not found
643
+ */
644
+ function getFacetFromSelection(facets, selection) {
645
+ if (!facets) {
646
+ return null;
647
+ }
648
+ return (facets.find(facet => facet.name != null && facet.name === selection.name) ||
649
+ facets.find(facet => facet.field === selection.field) ||
650
+ null);
651
+ }
652
+ /**
653
+ * Deep clone a literal via JSON serializing/unserializing
654
+ *
655
+ * It will **not** work with:
656
+ *
657
+ * - functions (will be removed)
658
+ * - `undefined` (will be removed)
659
+ * - cyclic references (will crash)
660
+ * - objects (will be converted to `{}`)
661
+ */
662
+ function deepClone(obj) {
663
+ return JSON.parse(JSON.stringify(obj));
664
+ }
665
+
666
+ class NaturalAbstractModelService {
667
+ name;
668
+ oneQuery;
669
+ allQuery;
670
+ createMutation;
671
+ updateMutation;
672
+ deleteMutation;
673
+ createName;
674
+ updateName;
675
+ deleteName;
676
+ /**
677
+ * Store the creation mutations that are pending
678
+ */
679
+ creatingCache = new Map();
680
+ apollo = inject(Apollo);
681
+ naturalDebounceService = inject(NaturalDebounceService);
682
+ plural;
683
+ /**
684
+ *
685
+ * @param name service and single object query name (eg. userForFront or user).
686
+ * @param oneQuery GraphQL query to fetch a single object from ID (eg. userForCrudQuery).
687
+ * @param allQuery GraphQL query to fetch a filtered list of objects (eg. usersForCrudQuery).
688
+ * @param createMutation GraphQL mutation to create an object.
689
+ * @param updateMutation GraphQL mutation to update an object.
690
+ * @param deleteMutation GraphQL mutation to delete a list of objects.
691
+ * @param plural list query name (eg. usersForFront or users).
692
+ * @param createName create object mutation name (eg. createUser).
693
+ * @param updateName update object mutation name (eg. updateUser).
694
+ * @param deleteName delete object mutation name (eg. deleteUsers).
695
+ */
696
+ constructor(name, oneQuery, allQuery, createMutation, updateMutation, deleteMutation, plural = null, createName = null, updateName = null, deleteName = null) {
697
+ this.name = name;
698
+ this.oneQuery = oneQuery;
699
+ this.allQuery = allQuery;
700
+ this.createMutation = createMutation;
701
+ this.updateMutation = updateMutation;
702
+ this.deleteMutation = deleteMutation;
703
+ this.createName = createName;
704
+ this.updateName = updateName;
705
+ this.deleteName = deleteName;
706
+ this.plural = plural ?? makePlural(this.name);
707
+ }
708
+ /**
709
+ * List of individual fields validators
710
+ */
711
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
712
+ getFormValidators(model) {
713
+ return {};
714
+ }
715
+ /**
716
+ * List of individual async fields validators
717
+ */
718
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
719
+ getFormAsyncValidators(model) {
720
+ return {};
721
+ }
722
+ /**
723
+ * List of grouped fields validators (like password + confirm password)
724
+ */
725
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
726
+ getFormGroupValidators(model) {
727
+ return [];
728
+ }
729
+ /**
730
+ * List of async group fields validators (like unique constraint on multiple columns)
731
+ */
732
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
733
+ getFormGroupAsyncValidators(model) {
734
+ return [];
735
+ }
736
+ getFormConfig(model) {
737
+ const values = { ...this.getDefaultForServer(), ...this.getFormExtraFieldDefaultValues() };
738
+ const validators = this.getFormValidators(model);
739
+ const asyncValidators = this.getFormAsyncValidators(model);
740
+ const controls = {};
741
+ const disabled = model.permissions ? !model.permissions.update : false;
742
+ if (model.id) {
743
+ controls.id = new UntypedFormControl({ value: model.id, disabled: true });
744
+ }
745
+ // Configure form for each field of model
746
+ for (const key of Object.keys(values)) {
747
+ const value = model[key] !== undefined ? model[key] : values[key];
748
+ const formState = {
749
+ value: value,
750
+ disabled: disabled,
751
+ };
752
+ const validator = typeof validators[key] !== 'undefined' ? validators[key] : null;
753
+ const asyncValidator = typeof asyncValidators[key] !== 'undefined' ? asyncValidators[key] : null;
754
+ controls[key] = new UntypedFormControl(formState, validator, asyncValidator);
755
+ }
756
+ // Configure form for extra validators that are not on a specific field
757
+ for (const key of Object.keys(validators)) {
758
+ if (!controls[key]) {
759
+ const formState = {
760
+ value: model[key] ? model[key] : null,
761
+ disabled: disabled,
762
+ };
763
+ controls[key] = new UntypedFormControl(formState, validators[key]);
764
+ }
765
+ }
766
+ for (const key of Object.keys(asyncValidators)) {
767
+ if (controls[key] && asyncValidators[key]) {
768
+ controls[key].setAsyncValidators(asyncValidators[key]);
769
+ }
770
+ else {
771
+ const formState = {
772
+ value: model[key] ? model[key] : null,
773
+ disabled: disabled,
774
+ };
775
+ controls[key] = new UntypedFormControl(formState, null, asyncValidators[key]);
776
+ }
777
+ }
778
+ return controls;
779
+ }
780
+ /**
781
+ * Create the final FormGroup for the object, including all validators
782
+ *
783
+ * This method should **not** be overridden, but instead `getFormConfig`,
784
+ * `getFormGroupValidators`, `getFormGroupAsyncValidators` might be.
785
+ */
786
+ getFormGroup(model) {
787
+ const formConfig = this.getFormConfig(deepClone(model));
788
+ return new UntypedFormGroup(formConfig, {
789
+ validators: this.getFormGroupValidators(model),
790
+ asyncValidators: this.getFormGroupAsyncValidators(model),
791
+ });
792
+ }
793
+ /**
794
+ * Get a single object
795
+ *
796
+ * If available it will emit object from cache immediately, then it
797
+ * will **always** fetch from network and then the observable will be completed.
798
+ *
799
+ * You must subscribe to start getting results (and fetch from network).
800
+ */
801
+ getOne(id) {
802
+ return this.prepareOneQuery(id, 'cache-and-network').pipe(takeWhile(result => result.networkStatus !== NetworkStatus.ready, true), map$1(result => result.data[this.name]));
803
+ }
804
+ /**
805
+ * Watch a single object
806
+ *
807
+ * If available it will emit object from cache immediately, then it
808
+ * will **always** fetch from network, and then keep watching the cache forever.
809
+ *
810
+ * You must subscribe to start getting results (and fetch from network).
811
+ *
812
+ * You **MUST** unsubscribe.
813
+ */
814
+ watchOne(id, fetchPolicy = 'cache-and-network') {
815
+ return this.prepareOneQuery(id, fetchPolicy).pipe(map$1(result => result.data[this.name]));
816
+ }
817
+ prepareOneQuery(id, fetchPolicy) {
818
+ this.throwIfObservable(id);
819
+ this.throwIfNotQuery(this.oneQuery);
820
+ return this.getVariablesForOne(id).pipe(switchMap$1(variables => {
821
+ this.throwIfNotQuery(this.oneQuery);
822
+ return this.apollo.watchQuery({
823
+ query: this.oneQuery,
824
+ variables: variables,
825
+ fetchPolicy: fetchPolicy,
826
+ nextFetchPolicy: 'cache-only',
827
+ }).valueChanges;
828
+ }), filter(result => !!result.data));
829
+ }
830
+ /**
831
+ * Get a collection of objects
832
+ *
833
+ * It will **always** fetch from network and then the observable will be completed.
834
+ * No cache is ever used, so it's slow but correct.
835
+ */
836
+ getAll(queryVariablesManager) {
837
+ this.throwIfNotQuery(this.allQuery);
838
+ return this.getPartialVariablesForAll().pipe(first(), switchMap$1(partialVariables => {
839
+ this.throwIfNotQuery(this.allQuery);
840
+ // Copy manager to prevent to apply internal variables to external QueryVariablesManager
841
+ const manager = new NaturalQueryVariablesManager(queryVariablesManager);
842
+ manager.merge('partial-variables', partialVariables);
843
+ return this.apollo.query({
844
+ query: this.allQuery,
845
+ variables: manager.variables.value,
846
+ fetchPolicy: 'network-only',
847
+ });
848
+ }), this.mapAll());
849
+ }
850
+ /**
851
+ * Get a collection of objects
852
+ *
853
+ * Every time the observable variables change, and they are not undefined,
854
+ * it will return result from cache, then it will **always** fetch from network,
855
+ * and then keep watching the cache forever.
856
+ *
857
+ * You must subscribe to start getting results (and fetch from network).
858
+ *
859
+ * You **MUST** unsubscribe.
860
+ */
861
+ watchAll(queryVariablesManager, fetchPolicy = 'cache-and-network') {
862
+ this.throwIfNotQuery(this.allQuery);
863
+ return combineLatest({
864
+ variables: queryVariablesManager.variables.pipe(
865
+ // Ignore very fast variable changes
866
+ debounceTime$1(20),
867
+ // Wait for variables to be defined to prevent duplicate query: with and without variables
868
+ // Null is accepted value for "no variables"
869
+ filter(variables => typeof variables !== 'undefined')),
870
+ partialVariables: this.getPartialVariablesForAll(),
871
+ }).pipe(switchMap$1(result => {
872
+ // Apply partial variables from service
873
+ // Copy manager to prevent to apply internal variables to external QueryVariablesManager
874
+ const manager = new NaturalQueryVariablesManager(queryVariablesManager);
875
+ manager.merge('partial-variables', result.partialVariables);
876
+ this.throwIfNotQuery(this.allQuery);
877
+ return this.apollo
878
+ .watchQuery({
879
+ query: this.allQuery,
880
+ variables: manager.variables.value,
881
+ fetchPolicy: fetchPolicy,
882
+ })
883
+ .valueChanges.pipe(catchError(() => EMPTY), filter(r => !!r.data), this.mapAll());
884
+ }));
885
+ }
886
+ /**
887
+ * This functions allow to quickly create or update objects.
888
+ *
889
+ * Manages a "creation is pending" status, and update when creation is ready.
890
+ * Uses regular update/updateNow and create methods.
891
+ * Used mainly when editing multiple objects in same controller (like in editable arrays)
892
+ */
893
+ createOrUpdate(object, now = false) {
894
+ this.throwIfObservable(object);
895
+ this.throwIfNotQuery(this.createMutation);
896
+ this.throwIfNotQuery(this.updateMutation);
897
+ // If creation is pending, listen to creation observable and when ready, fire update
898
+ const pendingCreation = this.creatingCache.get(object);
899
+ if (pendingCreation) {
900
+ return pendingCreation.pipe(switchMap$1(created => {
901
+ return this.update({
902
+ id: created.id,
903
+ ...object,
904
+ });
905
+ }));
906
+ }
907
+ // If object has Id, just save it
908
+ if ('id' in object && object.id) {
909
+ if (now) {
910
+ // used mainly for tests, because lodash debounced used in update() does not work fine with fakeAsync and tick()
911
+ return this.updateNow(object);
912
+ }
913
+ else {
914
+ return this.update(object);
915
+ }
916
+ }
917
+ // If object was not saving, and has no ID, create it
918
+ const creation = this.create(object).pipe(tap(() => {
919
+ this.creatingCache.delete(object); // remove from cache
920
+ }));
921
+ // stores creating observable in a cache replayable version of the observable,
922
+ // so several update() can subscribe to the same creation
923
+ this.creatingCache.set(object, creation.pipe(shareReplay$1()));
924
+ return creation;
925
+ }
926
+ /**
927
+ * Create an object in DB and then refetch the list of objects
928
+ */
929
+ create(object) {
930
+ this.throwIfObservable(object);
931
+ this.throwIfNotQuery(this.createMutation);
932
+ const variables = merge({}, { input: this.getInput(object, true) }, this.getPartialVariablesForCreation(object));
933
+ return this.apollo
934
+ .mutate({
935
+ mutation: this.createMutation,
936
+ variables: variables,
937
+ })
938
+ .pipe(map$1(result => {
939
+ this.apollo.client.reFetchObservableQueries();
940
+ return this.mapCreation(result);
941
+ }));
942
+ }
943
+ /**
944
+ * Update an object, after a short debounce
945
+ */
946
+ update(object) {
947
+ this.throwIfObservable(object);
948
+ this.throwIfNotQuery(this.updateMutation);
949
+ // Keep a single instance of the debounced update function
950
+ const id = object.id;
951
+ return this.naturalDebounceService.debounce(this, id, object);
952
+ }
953
+ /**
954
+ * Update an object immediately when subscribing
955
+ */
956
+ updateNow(object) {
957
+ this.throwIfObservable(object);
958
+ this.throwIfNotQuery(this.updateMutation);
959
+ const variables = merge({
960
+ id: object.id,
961
+ input: this.getInput(object, false),
962
+ }, this.getPartialVariablesForUpdate(object));
963
+ return this.apollo
964
+ .mutate({
965
+ mutation: this.updateMutation,
966
+ variables: variables,
967
+ })
968
+ .pipe(map$1(result => {
969
+ this.apollo.client.reFetchObservableQueries();
970
+ return this.mapUpdate(result);
971
+ }));
972
+ }
973
+ /**
974
+ * Delete objects and then refetch the list of objects
975
+ */
976
+ delete(objects) {
977
+ this.throwIfObservable(objects);
978
+ this.throwIfNotQuery(this.deleteMutation);
979
+ const ids = objects.map(o => {
980
+ // Cancel pending update
981
+ this.naturalDebounceService.cancelOne(this, o.id);
982
+ return o.id;
983
+ });
984
+ const variables = merge({
985
+ ids: ids,
986
+ }, this.getPartialVariablesForDelete(objects));
987
+ return this.apollo
988
+ .mutate({
989
+ mutation: this.deleteMutation,
990
+ variables: variables,
991
+ })
992
+ .pipe(
993
+ // Delay the observable until Apollo refetch is completed
994
+ switchMap$1(result => {
995
+ const mappedResult = this.mapDelete(result);
996
+ return from(this.apollo.client.reFetchObservableQueries()).pipe(map$1(() => mappedResult));
997
+ }));
998
+ }
999
+ /**
1000
+ * If the id is provided, resolves an observable model. The observable model will only be emitted after we are sure
1001
+ * that Apollo cache is fresh and warm. Then the component can subscribe to the observable model to get the model
1002
+ * immediately from Apollo cache and any subsequents future mutations that may happen to Apollo cache.
1003
+ *
1004
+ * Without id, returns default values, in order to show a creation form.
1005
+ */
1006
+ resolve(id) {
1007
+ if (id) {
1008
+ const onlyNetwork = this.watchOne(id, 'network-only').pipe(first());
1009
+ const onlyCache = this.watchOne(id, 'cache-first');
1010
+ // In theory, we can rely on Apollo Cache to return a result instantly. It is very fast indeed,
1011
+ // but it is still asynchronous, so there may be a very short time when we don't have the model
1012
+ // available. To fix that, we can rely on RxJS, which is able to emit synchronously the value we just
1013
+ // got from server. Once Apollo Client moves to RxJS (https://github.com/apollographql/apollo-feature-requests/issues/375),
1014
+ // we could try to remove `startWith()`.
1015
+ return onlyNetwork.pipe(map$1(firstValue => onlyCache.pipe(startWith(firstValue))));
1016
+ }
1017
+ else {
1018
+ return of(of(this.getDefaultForServer()));
1019
+ }
1020
+ }
1021
+ /**
1022
+ * Return an object that match the GraphQL input type.
1023
+ * It creates an object with manually filled data and add uncompleted data (like required attributes that can be empty strings)
1024
+ */
1025
+ getInput(object, forCreation) {
1026
+ // Convert relations to their IDs for mutation
1027
+ object = relationsToIds(object);
1028
+ // Pick only attributes that we can find in the empty object
1029
+ // In other words, prevent to select data that has unwanted attributes
1030
+ const emptyObject = this.getDefaultForServer();
1031
+ let input = pick(object, Object.keys(emptyObject));
1032
+ // Complete a potentially uncompleted object with default values
1033
+ if (forCreation) {
1034
+ input = defaults(input, emptyObject);
1035
+ }
1036
+ return input;
1037
+ }
1038
+ /**
1039
+ * Return the number of objects matching the query. It may never complete.
1040
+ *
1041
+ * This is used for the unique validator
1042
+ */
1043
+ count(queryVariablesManager) {
1044
+ const queryName = 'Count' + upperCaseFirstLetter(this.plural);
1045
+ const filterType = upperCaseFirstLetter(this.name) + 'Filter';
1046
+ const query = gql `
1047
+ query ${queryName} ($filter: ${filterType}) {
1048
+ count: ${this.plural} (filter: $filter, pagination: {pageSize: 0, pageIndex: 0}) {
1049
+ length
1050
+ }
1051
+ }`;
1052
+ return this.getPartialVariablesForAll().pipe(switchMap$1(partialVariables => {
1053
+ // Copy manager to prevent to apply internal variables to external QueryVariablesManager
1054
+ const manager = new NaturalQueryVariablesManager(queryVariablesManager);
1055
+ manager.merge('partial-variables', partialVariables);
1056
+ return this.apollo.query({
1057
+ query: query,
1058
+ variables: manager.variables.value,
1059
+ fetchPolicy: 'network-only',
1060
+ });
1061
+ }), map$1(result => result.data.count.length));
1062
+ }
1063
+ /**
1064
+ * Return empty object with some default values from server perspective
1065
+ *
1066
+ * This is typically useful when showing a form for creation
1067
+ */
1068
+ getDefaultForServer() {
1069
+ return {};
1070
+ }
1071
+ /**
1072
+ * You probably **should not** use this.
1073
+ *
1074
+ * If you are trying to *call* this method, instead you probably want to call `getDefaultForServer()` to get default
1075
+ * values for a model, or `getFormConfig()` to get a configured form that includes extra form fields.
1076
+ *
1077
+ * If you are trying to *override* this method, instead you probably want to override `getDefaultForServer()`.
1078
+ *
1079
+ * The only and **very rare** reason to override this method is if the client needs extra form fields that cannot be
1080
+ * accepted by the server (not part of `XXXInput` type) and that are strictly for the client form needs. In that case,
1081
+ * then you can return default values for those extra form fields, and the form returned by `getFormConfig()` will
1082
+ * include those extra fields.
1083
+ */
1084
+ getFormExtraFieldDefaultValues() {
1085
+ return {};
1086
+ }
1087
+ /**
1088
+ * This is used to extract only the array of fetched objects out of the entire fetched data
1089
+ */
1090
+ mapAll() {
1091
+ return map$1(result => result.data[this.plural]); // See https://github.com/apollographql/apollo-client/issues/5662
1092
+ }
1093
+ /**
1094
+ * This is used to extract only the created object out of the entire fetched data
1095
+ */
1096
+ mapCreation(result) {
1097
+ const name = this.createName ?? 'create' + upperCaseFirstLetter(this.name);
1098
+ return result.data[name]; // See https://github.com/apollographql/apollo-client/issues/5662
1099
+ }
1100
+ /**
1101
+ * This is used to extract only the updated object out of the entire fetched data
1102
+ */
1103
+ mapUpdate(result) {
1104
+ const name = this.updateName ?? 'update' + upperCaseFirstLetter(this.name);
1105
+ return result.data[name]; // See https://github.com/apollographql/apollo-client/issues/5662
1106
+ }
1107
+ /**
1108
+ * This is used to extract only flag when deleting an object
1109
+ */
1110
+ mapDelete(result) {
1111
+ const name = this.deleteName ?? 'delete' + upperCaseFirstLetter(this.plural);
1112
+ return result.data[name]; // See https://github.com/apollographql/apollo-client/issues/5662
1113
+ }
1114
+ /**
1115
+ * Returns additional variables to be used when getting a single object
1116
+ *
1117
+ * This is typically a site or state ID, and is needed to get appropriate access rights
1118
+ */
1119
+ getPartialVariablesForOne() {
1120
+ return of({});
1121
+ }
1122
+ /**
1123
+ * Returns additional variables to be used when getting multiple objects
1124
+ *
1125
+ * This is typically a site or state ID, but it could be something else to further filter the query
1126
+ */
1127
+ getPartialVariablesForAll() {
1128
+ return of({});
1129
+ }
1130
+ /**
1131
+ * Returns additional variables to be used when creating an object
1132
+ *
1133
+ * This is typically a site or state ID
1134
+ */
1135
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1136
+ getPartialVariablesForCreation(object) {
1137
+ return {};
1138
+ }
1139
+ /**
1140
+ * Returns additional variables to be used when updating an object
1141
+ *
1142
+ * This is typically a site or state ID
1143
+ */
1144
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1145
+ getPartialVariablesForUpdate(object) {
1146
+ return {};
1147
+ }
1148
+ /**
1149
+ * Return additional variables to be used when deleting an object
1150
+ *
1151
+ * This is typically a site or state ID
1152
+ */
1153
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1154
+ getPartialVariablesForDelete(objects) {
1155
+ return {};
1156
+ }
1157
+ /**
1158
+ * Throw exception to prevent executing queries with invalid variables
1159
+ */
1160
+ throwIfObservable(value) {
1161
+ if (value instanceof Observable) {
1162
+ throw new Error('Cannot use Observable as variables. Instead you should use .subscribe() to call the method with a real value');
1163
+ }
1164
+ }
1165
+ /**
1166
+ * Merge given ID with additional partial variables if there is any
1167
+ */
1168
+ getVariablesForOne(id) {
1169
+ return this.getPartialVariablesForOne().pipe(map$1(partialVariables => merge({}, { id: id }, partialVariables)));
1170
+ }
1171
+ /**
1172
+ * Throw exception to prevent executing null queries
1173
+ */
1174
+ throwIfNotQuery(query) {
1175
+ if (!query) {
1176
+ throw new Error('GraphQL query for this method was not configured in this service constructor');
1177
+ }
1178
+ }
1179
+ }
1180
+
1181
+ /**
1182
+ * **DO NOT MODIFY UNLESS STRICTLY REQUIRED FOR VANILLA**
1183
+ *
1184
+ * This is a minimal service specialized for Vanilla and any modification,
1185
+ * including adding `import` in this file, might break https://navigations.ichtus.club.
1186
+ */
1187
+
1188
+ /**
1189
+ * Generated bundle index. Do not edit.
1190
+ */
1191
+
1192
+ export { NaturalAbstractModelService, NaturalQueryVariablesManager, formatIsoDateTime, graphqlQuerySigner };
1193
+ //# sourceMappingURL=ecodev-natural-vanilla.mjs.map