ngx-lift 0.4.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 (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +56 -0
  3. package/esm2022/lib/const.mjs +5 -0
  4. package/esm2022/lib/models/async-state.model.mjs +2 -0
  5. package/esm2022/lib/models/index.mjs +2 -0
  6. package/esm2022/lib/operators/combine-latest-eager.operator.mjs +48 -0
  7. package/esm2022/lib/operators/create-async-state.operator.mjs +52 -0
  8. package/esm2022/lib/operators/distinct-on-change.operator.mjs +80 -0
  9. package/esm2022/lib/operators/index.mjs +8 -0
  10. package/esm2022/lib/operators/logger.operator.mjs +21 -0
  11. package/esm2022/lib/operators/poll.operator.mjs +32 -0
  12. package/esm2022/lib/operators/start-with-tap.operator.mjs +15 -0
  13. package/esm2022/lib/operators/switch-map-with-async-state.operator.mjs +33 -0
  14. package/esm2022/lib/pipes/array-join.pipe.mjs +21 -0
  15. package/esm2022/lib/pipes/byte-converter.pipe.mjs +159 -0
  16. package/esm2022/lib/pipes/index.mjs +5 -0
  17. package/esm2022/lib/pipes/is-https.pipe.mjs +18 -0
  18. package/esm2022/lib/pipes/mask.pipe.mjs +36 -0
  19. package/esm2022/lib/utils/form.util.mjs +56 -0
  20. package/esm2022/lib/utils/index.mjs +5 -0
  21. package/esm2022/lib/utils/is-empty.util.mjs +17 -0
  22. package/esm2022/lib/utils/is-equal.util.mjs +24 -0
  23. package/esm2022/lib/utils/pick-by.util.mjs +16 -0
  24. package/esm2022/lib/validators/index.mjs +3 -0
  25. package/esm2022/lib/validators/unique.validator.mjs +64 -0
  26. package/esm2022/lib/validators/url.validator.mjs +14 -0
  27. package/esm2022/ngx-lift.mjs +5 -0
  28. package/esm2022/public-api.mjs +10 -0
  29. package/fesm2022/ngx-lift.mjs +707 -0
  30. package/fesm2022/ngx-lift.mjs.map +1 -0
  31. package/index.d.ts +5 -0
  32. package/lib/const.d.ts +3 -0
  33. package/lib/models/async-state.model.d.ts +22 -0
  34. package/lib/models/index.d.ts +1 -0
  35. package/lib/operators/combine-latest-eager.operator.d.ts +7 -0
  36. package/lib/operators/create-async-state.operator.d.ts +49 -0
  37. package/lib/operators/distinct-on-change.operator.d.ts +55 -0
  38. package/lib/operators/index.d.ts +7 -0
  39. package/lib/operators/logger.operator.d.ts +11 -0
  40. package/lib/operators/poll.operator.d.ts +18 -0
  41. package/lib/operators/start-with-tap.operator.d.ts +10 -0
  42. package/lib/operators/switch-map-with-async-state.operator.d.ts +31 -0
  43. package/lib/pipes/array-join.pipe.d.ts +7 -0
  44. package/lib/pipes/byte-converter.pipe.d.ts +39 -0
  45. package/lib/pipes/index.d.ts +4 -0
  46. package/lib/pipes/is-https.pipe.d.ts +7 -0
  47. package/lib/pipes/mask.pipe.d.ts +19 -0
  48. package/lib/utils/form.util.d.ts +18 -0
  49. package/lib/utils/index.d.ts +4 -0
  50. package/lib/utils/is-empty.util.d.ts +6 -0
  51. package/lib/utils/is-equal.util.d.ts +7 -0
  52. package/lib/utils/pick-by.util.d.ts +7 -0
  53. package/lib/validators/index.d.ts +2 -0
  54. package/lib/validators/unique.validator.d.ts +18 -0
  55. package/lib/validators/url.validator.d.ts +3 -0
  56. package/package.json +28 -0
  57. package/public-api.d.ts +5 -0
@@ -0,0 +1,707 @@
1
+ import { startWith, Subject, combineLatest, tap, map, of, catchError, pipe, Observable, exhaustMap, share, EMPTY, timer, merge, switchMap } from 'rxjs';
2
+ import * as i0 from '@angular/core';
3
+ import { Pipe, LOCALE_ID, Inject } from '@angular/core';
4
+ import { Validators, FormArray } from '@angular/forms';
5
+
6
+ /**
7
+ * Combines multiple observables into a single observable emitting an array or dictionary
8
+ * of the latest values from each source observable.
9
+ * Adds startWith(null) for each Subject in combineLatest when the second parameter startWithNullForAll is false.
10
+ * When startWithNullForAll is true, each observable will startWith null.
11
+ *
12
+ * @template T - The type of the data in the observables.
13
+ *
14
+ * @param {Array<Observable<T>> | Record<string, Observable<T>>} sources -
15
+ * An array of observables or a dictionary of observables to be combined.
16
+ *
17
+ * @param {boolean} [startWithNullForAll=false] -
18
+ * Determines whether to start each observable with a `null` value.
19
+ *
20
+ * @returns {Observable<Array<T | null> | Record<string, T | null>>} -
21
+ * An observable emitting an array or dictionary of the latest values from each source observable.
22
+ *
23
+ * @throws {Error} -
24
+ * Throws an error if the provided argument is not an array of observables or a dictionary of observables.
25
+ */
26
+ function combineLatestEager(sources, startWithNullForAll = false) {
27
+ function observableMapper(observable) {
28
+ if (startWithNullForAll) {
29
+ return observable.pipe(startWith(null));
30
+ }
31
+ else {
32
+ // Check if observable is a Subject, if true, apply startWith(null)
33
+ return observable instanceof Subject ? observable.pipe(startWith(null)) : observable;
34
+ }
35
+ }
36
+ if (Array.isArray(sources)) {
37
+ // If sources is an array of observables
38
+ return combineLatest(sources.map(observableMapper));
39
+ }
40
+ else if (typeof sources === 'object' && sources !== null) {
41
+ // If sources is a dictionary of observables
42
+ const observables = {};
43
+ for (const [key, value] of Object.entries(sources)) {
44
+ observables[key] = observableMapper(value);
45
+ }
46
+ return combineLatest(observables);
47
+ }
48
+ else {
49
+ throw new Error(`Invalid argument type. Please provide an array of observables or a dictionary of observables. Received: ${typeof sources}`);
50
+ }
51
+ }
52
+
53
+ /**
54
+ * createAsyncState transforms an Observable of type T into an Observable of AsyncState<T>.
55
+ * AsyncState<T> represents the loading, error, and data states for asynchronous operations.
56
+ *
57
+ * @template T - The type of the data in the observable.
58
+ * @template E - The type of the error that can occur.
59
+ *
60
+ * @param {Partial<Observer<T>> | ((value: T) => void)} [observerOrNextForOrigin] -
61
+ * An optional parameter that can be a partial TapObserver<T> or a function to handle the next value or error in the original Observable.
62
+ *
63
+ * @returns {UnaryFunction<Observable<T>, Observable<AsyncState<T, E>>>} -
64
+ * A function that transforms an observable stream into an asynchronous state.
65
+ *
66
+ * @example
67
+ * Usage 1: Simple request
68
+ * data$ = this.shopService.products$.pipe(
69
+ * createAsyncState({
70
+ * next: res => console.log('Side effect if success: ' + res),
71
+ * error: error => console.error('Side effect if error: ' + error.message)
72
+ * })
73
+ * );
74
+ *
75
+ * Usage 2: Dependent requests
76
+ * data$ = firstCall$.pipe(
77
+ * switchMap(() => this.shopService.products$),
78
+ * createAsyncState()
79
+ * );
80
+ *
81
+ * Another implementation thought when refreshing the data: instead of startWith, `merge of` emit as the trigger
82
+ *
83
+ * subject.pipe(
84
+ * switchMap(() => merge(
85
+ * of({ loading: true, error: null, data: null }),
86
+ * this.service.apiCall().pipe(
87
+ * map(data => ({ loading: false, error: null, data })),
88
+ * tap({
89
+ * next: res => callback?.(res.data),
90
+ * error: err => errorCallback?.(err),
91
+ * }),
92
+ * catchError(error => of({ loading: false, error, data: null })),
93
+ * ),
94
+ * ))
95
+ * )
96
+ *
97
+ */
98
+ function createAsyncState(observerOrNextForOrigin) {
99
+ return pipe(tap(observerOrNextForOrigin), map((data) => ({ loading: false, error: null, data })), startWith({ loading: true, error: null, data: null }),
100
+ // retry(1), // if you want to add retry
101
+ catchError((error) => of({ loading: false, error, data: null })));
102
+ }
103
+
104
+ /**
105
+ * Creates an operator function for RxJS Observables that filters out consecutive
106
+ * values that are considered equal according to a provided comparator function,
107
+ * and invokes a callback when a distinct value is encountered.
108
+ *
109
+ * @template T - The type of elements emitted by the observable.
110
+ * @param {(previousValue: T, currentValue: T) => void} onChangeCallback
111
+ * A callback function that will be invoked when a distinct value is encountered.
112
+ * It receives the previous distinct value and the current value.
113
+ * @param {(previousValue: T, currentValue: T) => boolean} [comparator]
114
+ * A function that determines if two values are considered equal.
115
+ * Defaults to a function that performs strict equality (===) comparison.
116
+ * @returns {OperatorFunction<T, T>} - The RxJS operator function.
117
+ *
118
+ * @example
119
+ * Example 1:
120
+ * const source$ = new Observable<number>((observer) => {
121
+ * observer.next(1);
122
+ * observer.next(2);
123
+ * observer.next(2);
124
+ * observer.next(3);
125
+ * observer.next(3);
126
+ * observer.next(4);
127
+ * observer.next(5);
128
+ * observer.complete();
129
+ * });
130
+ *
131
+ * const distinctOnChange$ = source$.pipe(
132
+ * distinctOnChange(
133
+ * (prev, curr) => console.log(`Value changed from ${prev} to: ${curr}`),
134
+ * (prev, curr) => prev === curr,
135
+ * ),
136
+ * );
137
+ * distinctOnChange$.subscribe((res) => console.log(res));
138
+ *
139
+ *
140
+ * Example 2:
141
+ * distinctOnChange<RDEValue<OseInstance>[]>(
142
+ * () => {
143
+ * this.store.dispatch(
144
+ * addToast({
145
+ * toast: {
146
+ * type: ToastType.SUCCESS,
147
+ * title: this.l10nService.getMessage('STATUS_CHANGE'),
148
+ * description: this.l10nService.getMessage('STATUS_CHANGE_DESC'),
149
+ * },
150
+ * }),
151
+ * );
152
+ * },
153
+ * (prev, current) =>
154
+ * prev.every((prevInstance, index) => instanceComparator(prevInstance.entity, current[index].entity)),
155
+ * );
156
+ */
157
+ function distinctOnChange(onChangeCallback, comparator = (prev, curr) => prev === curr) {
158
+ return (source) => new Observable((subscriber) => {
159
+ let hasFirstValue = false;
160
+ let previousValue;
161
+ const subscription = source.subscribe({
162
+ next: (currentValue) => {
163
+ if (hasFirstValue) {
164
+ if (!comparator(previousValue, currentValue)) {
165
+ onChangeCallback(previousValue, currentValue);
166
+ previousValue = currentValue;
167
+ subscriber.next(currentValue);
168
+ }
169
+ }
170
+ else {
171
+ previousValue = currentValue;
172
+ hasFirstValue = true;
173
+ subscriber.next(currentValue);
174
+ }
175
+ },
176
+ error: (err) => subscriber.error(err),
177
+ complete: () => subscriber.complete(),
178
+ });
179
+ return () => subscription.unsubscribe();
180
+ });
181
+ }
182
+
183
+ // Map each LoggerType to its corresponding console function
184
+ const loggerFunctions = {
185
+ count: console.count.bind(console),
186
+ debug: console.debug.bind(console),
187
+ dir: console.dir.bind(console),
188
+ log: console.log.bind(console),
189
+ table: console.table.bind(console),
190
+ };
191
+ /**
192
+ * Logger operator for RxJS observables.
193
+ *
194
+ * @param loggerType The type of logger to be used: 'count', 'debug', 'dir', 'log', 'table'.
195
+ * Defaults to 'log' if not provided or if an unknown type is specified.
196
+ * @returns An RxJS operator function that logs values using the specified console function.
197
+ */
198
+ const logger = (loggerType = 'log') => pipe(tap((value) => {
199
+ const logFunction = loggerFunctions[loggerType] || console.log.bind(console);
200
+ logFunction(value);
201
+ }));
202
+
203
+ /**
204
+ * Polls the API at a specified interval, handling the triggering of requests and building parameters.
205
+ *
206
+ * @param {Object} options - The options object containing interval, apiCall, paramsBuilder, and trigger
207
+ * @param {number} options.interval - The interval at which to poll the API
208
+ * @param {(params: Record<string, unknown>) => Observable<Data>} options.apiCall - The function to call the API
209
+ * @param {(input: Input | null) => Record<string, unknown>} [options.paramsBuilder] - The function to build parameters based on input
210
+ * @param {Observable<Input>} [options.trigger] - The trigger observable for initiating requests
211
+ * @return {Observable<AsyncState<Data>>} An observable emitting the async state of the API data
212
+ */
213
+ function poll(options) {
214
+ let latestInput = null;
215
+ return merge(options.trigger || EMPTY, timer(0, options.interval)).pipe(exhaustMap((input) => {
216
+ let params = {};
217
+ const triggeredByObservable = typeof input !== 'number';
218
+ if (triggeredByObservable) {
219
+ latestInput = input;
220
+ }
221
+ if (options.paramsBuilder) {
222
+ params = options.paramsBuilder(latestInput);
223
+ }
224
+ const isFirstRequest = input === 0;
225
+ const shouldShowLoading = triggeredByObservable || isFirstRequest;
226
+ let observable = options.apiCall(params).pipe(map((data) => ({ loading: false, error: null, data })), catchError((error) => of({ loading: false, error, data: null })));
227
+ if (shouldShowLoading) {
228
+ observable = observable.pipe(startWith({ loading: true, error: null, data: null }));
229
+ }
230
+ return observable;
231
+ }), share());
232
+ }
233
+
234
+ /**
235
+ * Operator that taps into a callback before the source Observable starts emitting values.
236
+ *
237
+ * This operator is useful for triggering a side effect before the main Observable starts emitting.
238
+ *
239
+ * @param callback A function to be executed before the source Observable emits its first value.
240
+ * @returns An RxJS operator function that taps into the callback and then switchMaps to the source Observable.
241
+ */
242
+ function startWithTap(callback) {
243
+ return (source) => {
244
+ callback();
245
+ return source;
246
+ };
247
+ }
248
+
249
+ /**
250
+ * Custom RxJS operator that uses switchMap to handle asynchronous operations and
251
+ * transforms the emitted values into an AsyncState object.
252
+ *
253
+ * @template T - The type of data emitted by the observable returned by the project.
254
+ * @template K - The type of value emitted by the source observable.
255
+ * @template E - The type of error that can be encountered during the asynchronous operation.
256
+ *
257
+ * @param {function(K): Observable<T>} project - A function that takes a value emitted by the source
258
+ * observable and returns an observable representing an asynchronous operation.
259
+ *
260
+ * @returns {OperatorFunction<K, AsyncState<T, E>>} - An RxJS operator that transforms the source observable into
261
+ * an observable of AsyncState objects.
262
+ *
263
+ * @example
264
+ * // Usage of the switchMapWithAsyncState operator
265
+ * const source$ = new BehaviorSubject<number>(1);
266
+ *
267
+ * const asyncOperation = (value: number) => {
268
+ * return of(value * 2).pipe(delay(1000));
269
+ * };
270
+ *
271
+ * const result$ = source$.pipe(switchMapWithAsyncState(asyncOperation));
272
+ * result$.subscribe((state) => {
273
+ * console.log(state); // Outputs AsyncState objects with loading, data, and error properties.
274
+ * });
275
+ */
276
+ function switchMapWithAsyncState(project) {
277
+ return (source) => source.pipe(switchMap((value, index) => project(value, index).pipe(createAsyncState())));
278
+ }
279
+
280
+ class ArrayJoinPipe {
281
+ transform(value, separator = ',') {
282
+ if (Array.isArray(value)) {
283
+ return value.join(separator);
284
+ }
285
+ // For non-array cases or unexpected types, return the value as is
286
+ return value;
287
+ }
288
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: ArrayJoinPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
289
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "17.2.4", ngImport: i0, type: ArrayJoinPipe, isStandalone: true, name: "arrayJoin" }); }
290
+ }
291
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: ArrayJoinPipe, decorators: [{
292
+ type: Pipe,
293
+ args: [{
294
+ name: 'arrayJoin',
295
+ standalone: true,
296
+ }]
297
+ }] });
298
+
299
+ /**
300
+ * import { LOCALE_ID, NgModule } from '@angular/core';
301
+ * import { BrowserModule } from '@angular/platform-browser';
302
+ * import { AppComponent } from './app.component';
303
+ * import { registerLocaleData } from '@angular/common';
304
+ *
305
+ * import localeEn from '@angular/common/locales/en';
306
+ * import localeFr from '@angular/common/locales/fr';
307
+ *
308
+ * // Register locales
309
+ * registerLocaleData(localeEn);
310
+ * registerLocaleData(localeFr);
311
+ *
312
+ * @NgModule({
313
+ * declarations: [AppComponent],
314
+ * imports: [BrowserModule],
315
+ * providers: [
316
+ * {
317
+ * provide: LOCALE_ID,
318
+ * useFactory: () => {
319
+ * // Use the browser's language or a default language
320
+ * return navigator.language || 'en';
321
+ * },
322
+ * },
323
+ * ],
324
+ * bootstrap: [AppComponent],
325
+ * })
326
+ * export class AppModule {}
327
+ */
328
+ class ByteConverterPipe {
329
+ constructor(locale) {
330
+ this.locale = locale;
331
+ }
332
+ // If using navigator.language directly in the pipe, this approach directly uses the browser's language at the moment the ByteConverterPipe is constructed. If the user changes the language while using the application, it won't be automatically reflected. If dynamic language changes are a requirement, using the LOCALE_ID provider as demonstrated in the AppModule is a more Angular-centric approach.
333
+ // private locale: string;
334
+ // constructor() {
335
+ // // Use navigator.language as the default locale
336
+ // this.locale = navigator.language || 'en';
337
+ // }
338
+ transform(value) {
339
+ if (value === null || value === undefined || isNaN(value)) {
340
+ return '-';
341
+ }
342
+ const units = ['BYTE', 'KB', 'MB', 'GB', 'TB'];
343
+ let unitIndex = 0;
344
+ while (value >= 1024 && unitIndex < units.length - 1) {
345
+ value /= 1024;
346
+ unitIndex++;
347
+ }
348
+ const translationObject = translations[this.locale] || translations['en'];
349
+ const key = units[unitIndex];
350
+ return this.formatNumber(value) + ' ' + translationObject[key];
351
+ }
352
+ formatNumber(value) {
353
+ return new Intl.NumberFormat(this.locale, { maximumFractionDigits: 2 }).format(value);
354
+ }
355
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: ByteConverterPipe, deps: [{ token: LOCALE_ID }], target: i0.ɵɵFactoryTarget.Pipe }); }
356
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "17.2.4", ngImport: i0, type: ByteConverterPipe, isStandalone: true, name: "byteConverter" }); }
357
+ }
358
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: ByteConverterPipe, decorators: [{
359
+ type: Pipe,
360
+ args: [{
361
+ name: 'byteConverter',
362
+ standalone: true,
363
+ }]
364
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
365
+ type: Inject,
366
+ args: [LOCALE_ID]
367
+ }] }] });
368
+ const translations = {
369
+ en: {
370
+ BYTE: 'B',
371
+ KB: 'KB',
372
+ MB: 'MB',
373
+ GB: 'GB',
374
+ TB: 'TB',
375
+ },
376
+ 'en-US': {
377
+ // You can provide specific variations for en-US if needed
378
+ BYTE: 'B',
379
+ KB: 'KB',
380
+ MB: 'MB',
381
+ GB: 'GB',
382
+ TB: 'TB',
383
+ },
384
+ de: {
385
+ BYTE: 'B',
386
+ KB: 'KB',
387
+ MB: 'MB',
388
+ GB: 'GB',
389
+ TB: 'TB',
390
+ },
391
+ es: {
392
+ BYTE: 'B',
393
+ KB: 'KB',
394
+ MB: 'MB',
395
+ GB: 'GB',
396
+ TB: 'TB',
397
+ },
398
+ fr: {
399
+ BYTE: 'o',
400
+ KB: 'Ko',
401
+ MB: 'Mo',
402
+ GB: 'Go',
403
+ TB: 'To',
404
+ },
405
+ it: {
406
+ BYTE: 'B',
407
+ KB: 'KB',
408
+ MB: 'MB',
409
+ GB: 'GB',
410
+ TB: 'TB',
411
+ },
412
+ ja: {
413
+ BYTE: 'B',
414
+ KB: 'KB',
415
+ MB: 'MB',
416
+ GB: 'GB',
417
+ TB: 'TB',
418
+ },
419
+ ko: {
420
+ BYTE: 'B',
421
+ KB: 'KB',
422
+ MB: 'MB',
423
+ GB: 'GB',
424
+ TB: 'TB',
425
+ },
426
+ 'pt-BR': {
427
+ BYTE: 'B',
428
+ KB: 'KB',
429
+ MB: 'MB',
430
+ GB: 'GB',
431
+ TB: 'TB',
432
+ },
433
+ 'zh-CN': {
434
+ BYTE: '字节',
435
+ KB: '千字节',
436
+ MB: '兆字节',
437
+ GB: '千兆字节',
438
+ TB: '太字节',
439
+ },
440
+ 'zh-TW': {
441
+ BYTE: '位元組',
442
+ KB: '千位元組',
443
+ MB: '兆位元組',
444
+ GB: '千兆位元組',
445
+ TB: '太位元組',
446
+ },
447
+ ru: {
448
+ BYTE: 'Б',
449
+ KB: 'КБ',
450
+ MB: 'МБ',
451
+ GB: 'ГБ',
452
+ TB: 'ТБ',
453
+ },
454
+ };
455
+
456
+ // https://regex101.com/library/mX1xW0
457
+ const emailPattern = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/;
458
+ const urlPattern = /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/;
459
+ const httpsPattern = /^https:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/;
460
+
461
+ class IsHttpsPipe {
462
+ transform(value) {
463
+ return httpsPattern.test(value);
464
+ }
465
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: IsHttpsPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
466
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "17.2.4", ngImport: i0, type: IsHttpsPipe, isStandalone: true, name: "isHttps" }); }
467
+ }
468
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: IsHttpsPipe, decorators: [{
469
+ type: Pipe,
470
+ args: [{
471
+ name: 'isHttps',
472
+ standalone: true,
473
+ }]
474
+ }] });
475
+
476
+ const unmaskNumber = 6;
477
+ const maskChar = '*';
478
+ class MaskPipe {
479
+ /**
480
+ * Transforms the input string by masking characters based on the provided options.
481
+ *
482
+ * @param {string} value - The input string to be masked.
483
+ * @param {MaskOptions} [options={}] - Options for customizing the masking behavior.
484
+ * @returns {string} - The masked string.
485
+ */
486
+ transform(value, options = {}) {
487
+ const { unmaskedPrefixLength = unmaskNumber, unmaskedSuffixLength = unmaskNumber, masked = true } = options;
488
+ if (value.length <= unmaskedPrefixLength + unmaskedSuffixLength ||
489
+ unmaskedPrefixLength < 0 ||
490
+ unmaskedSuffixLength < 0 ||
491
+ !masked) {
492
+ return value;
493
+ }
494
+ return value
495
+ .split('')
496
+ .map((char, i) => (i < unmaskedPrefixLength || i > value.length - unmaskedSuffixLength - 1 ? char : maskChar))
497
+ .join('');
498
+ }
499
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: MaskPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
500
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "17.2.4", ngImport: i0, type: MaskPipe, isStandalone: true, name: "mask" }); }
501
+ }
502
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: MaskPipe, decorators: [{
503
+ type: Pipe,
504
+ args: [{
505
+ name: 'mask',
506
+ standalone: true,
507
+ }]
508
+ }] });
509
+
510
+ /**
511
+ * Provides a conditional validator that applies the specified validator functions only if the condition is met.
512
+ *
513
+ * @param condition A function that determines whether the validators should be applied.
514
+ * @param trueValidatorFn The validator function or an array of validator functions to be applied when the condition is true.
515
+ * @param falseValidatorFn Optional. The validator function or an array of validator functions to be applied when the condition is false.
516
+ * @returns A validator function that can be used with Angular Reactive Forms.
517
+ */
518
+ function ifValidator(condition, trueValidatorFn, falseValidatorFn) {
519
+ /**
520
+ * @param control The AbstractControl to validate.
521
+ * @returns Validation errors if the condition is met; otherwise, null.
522
+ */
523
+ return (control) => {
524
+ if (!trueValidatorFn || !condition(control)) {
525
+ return composeValidators(control, falseValidatorFn);
526
+ }
527
+ return composeValidators(control, trueValidatorFn);
528
+ };
529
+ }
530
+ /**
531
+ * Provides a conditional async validator that applies the specified async validator function only if the condition is met.
532
+ *
533
+ * @param condition A function that determines whether the async validator should be applied.
534
+ * @param validatorFn The async validator function to be applied conditionally.
535
+ * @returns An async validator function that can be used with Angular Reactive Forms.
536
+ */
537
+ function ifAsyncValidator(condition, validatorFn) {
538
+ /**
539
+ * @param control The AbstractControl to validate asynchronously.
540
+ * @returns An observable that emits validation errors if the condition is met; otherwise, emits null.
541
+ */
542
+ return (control) => {
543
+ if (!validatorFn || !condition(control)) {
544
+ return of(null);
545
+ }
546
+ return validatorFn(control);
547
+ };
548
+ }
549
+ /**
550
+ * Composes and applies the provided validators to the given AbstractControl.
551
+ *
552
+ * @param control The AbstractControl to validate.
553
+ * @param validatorFn The validator function or an array of validator functions to be applied.
554
+ * @returns Validation errors if the validators are applicable; otherwise, null.
555
+ */
556
+ function composeValidators(control, validatorFn) {
557
+ if (!validatorFn) {
558
+ return null;
559
+ }
560
+ const validatorFns = Array.isArray(validatorFn) ? validatorFn : [validatorFn];
561
+ return Validators.compose(validatorFns)?.(control) || null;
562
+ }
563
+
564
+ /**
565
+ * Check if a value is empty.
566
+ * @param {T | undefined | null} value - The value to check for emptiness.
567
+ * @returns {boolean} - Returns true if the value is empty, otherwise false.
568
+ */
569
+ function isEmpty(value) {
570
+ if (value == null)
571
+ return true;
572
+ if (typeof value === 'string' || Array.isArray(value)) {
573
+ return value.length === 0;
574
+ }
575
+ if (typeof value === 'object') {
576
+ return Object.keys(value).length === 0;
577
+ }
578
+ return false;
579
+ }
580
+
581
+ /**
582
+ * Check if two values are deeply equal.
583
+ * @param {T} value1 - The first value to compare.
584
+ * @param {T} value2 - The second value to compare.
585
+ * @returns {boolean} - Returns true if the values are deeply equal, otherwise false.
586
+ */
587
+ function isEqual(value1, value2) {
588
+ if (value1 === value2)
589
+ return true;
590
+ if (typeof value1 !== 'object' || typeof value2 !== 'object' || value1 === null || value2 === null) {
591
+ return false;
592
+ }
593
+ const keys1 = Object.keys(value1);
594
+ const keys2 = Object.keys(value2);
595
+ if (keys1.length !== keys2.length)
596
+ return false;
597
+ for (const key of keys1) {
598
+ if (!keys2.includes(key) || !isEqual(value1[key], value2[key])) {
599
+ return false;
600
+ }
601
+ }
602
+ return true;
603
+ }
604
+
605
+ /**
606
+ * Create an object composed of object properties that satisfy a given condition.
607
+ * @param {T} source - The object to pick properties from.
608
+ * @param {(value: T[keyof T], key: string) => boolean} predicate - The function invoked per property.
609
+ * @returns {Partial<T>} - Returns the new object.
610
+ */
611
+ function pickBy(source, predicate) {
612
+ const result = {};
613
+ for (const key in source) {
614
+ if (Object.prototype.hasOwnProperty.call(source, key) && predicate(source[key], key)) {
615
+ result[key] = source[key];
616
+ }
617
+ }
618
+ return result;
619
+ }
620
+
621
+ /**
622
+ * Validator for checking uniqueness across multiple fields in a FormArray or FormGroup.
623
+ *
624
+ * This validator can be applied to a FormArray or FormGroup containing the controls to be validated.
625
+ * It ensures that each control's value is unique among all other controls within the array or group.
626
+ */
627
+ class UniqueValidator {
628
+ /**
629
+ * Validator function to be attached to a FormArray or FormGroup.
630
+ *
631
+ * This validator checks for uniqueness of each control's value within the array or group.
632
+ *
633
+ * @param keySelector A function to select the key control for comparison (default is the control itself).
634
+ * @typeparam T The type of the control value.
635
+ */
636
+ static unique(keySelector = (control) => control) {
637
+ return (formArray) => {
638
+ if (!(formArray instanceof FormArray)) {
639
+ return null;
640
+ }
641
+ const targetControls = formArray.controls.map(keySelector);
642
+ const valueControlMap = new Map();
643
+ const invalidControls = [];
644
+ for (const control of targetControls) {
645
+ const value = control.value;
646
+ if (value == null || String(value) === '' || String(value) === 'NaN') {
647
+ continue;
648
+ }
649
+ const controlInMap = valueControlMap.get(value);
650
+ if (controlInMap) {
651
+ if (!invalidControls.includes(controlInMap)) {
652
+ invalidControls.push(controlInMap);
653
+ }
654
+ invalidControls.push(control);
655
+ }
656
+ else {
657
+ valueControlMap.set(value, control);
658
+ }
659
+ }
660
+ const notUniqueError = { notUnique: true };
661
+ // set errors manually for target controls
662
+ for (const control of targetControls) {
663
+ const errors = control.errors;
664
+ if (invalidControls.includes(control)) {
665
+ // set not unique error for invalid controls
666
+ control.setErrors(errors === null ? notUniqueError : { ...errors, ...notUniqueError });
667
+ }
668
+ else {
669
+ // remove not unique errors for valid controls
670
+ if (errors === null) {
671
+ control.setErrors(null);
672
+ }
673
+ else {
674
+ delete errors['notUnique'];
675
+ control.setErrors(Object.keys(errors).length > 0 ? errors : null);
676
+ }
677
+ }
678
+ }
679
+ return invalidControls.length > 0 ? notUniqueError : null;
680
+ };
681
+ }
682
+ }
683
+
684
+ function urlValidator(control) {
685
+ if (!urlPattern.test(control.value)) {
686
+ return { invalidUrl: true };
687
+ }
688
+ return null;
689
+ }
690
+ function httpsValidator(control) {
691
+ if (!httpsPattern.test(control.value)) {
692
+ return { invalidUrl: true };
693
+ }
694
+ return null;
695
+ }
696
+
697
+ /*
698
+ * Public API Surface of ngx-lift
699
+ */
700
+ // export * from './lib/signals';
701
+
702
+ /**
703
+ * Generated bundle index. Do not edit.
704
+ */
705
+
706
+ export { ArrayJoinPipe, ByteConverterPipe, IsHttpsPipe, MaskPipe, UniqueValidator, combineLatestEager, createAsyncState, distinctOnChange, httpsValidator, ifAsyncValidator, ifValidator, isEmpty, isEqual, logger, pickBy, poll, startWithTap, switchMapWithAsyncState, urlValidator };
707
+ //# sourceMappingURL=ngx-lift.mjs.map