@ethlete/query 5.16.0 → 5.16.1
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.
- package/CHANGELOG.md +6 -0
- package/esm2022/ethlete-query.mjs +5 -0
- package/esm2022/index.mjs +20 -0
- package/esm2022/lib/auth/auth-provider.types.mjs +2 -0
- package/esm2022/lib/auth/auth-provider.utils.mjs +25 -0
- package/esm2022/lib/auth/basic-auth-provider.mjs +15 -0
- package/esm2022/lib/auth/bearer-auth-provider.mjs +168 -0
- package/esm2022/lib/auth/custom-header-auth-provider.mjs +12 -0
- package/esm2022/lib/auth/index.mjs +6 -0
- package/esm2022/lib/class-validator/class-validator.types.mjs +2 -0
- package/esm2022/lib/class-validator/class-validator.utils.mjs +4 -0
- package/esm2022/lib/class-validator/index.mjs +3 -0
- package/esm2022/lib/devtools/components/query-devtools/index.mjs +2 -0
- package/esm2022/lib/devtools/components/query-devtools/query-devtools.component.mjs +297 -0
- package/esm2022/lib/devtools/pipes/index.mjs +2 -0
- package/esm2022/lib/devtools/pipes/query-short-name.pipe.mjs +24 -0
- package/esm2022/lib/devtools/utils/index.mjs +2 -0
- package/esm2022/lib/devtools/utils/query-client.utils.mjs +13 -0
- package/esm2022/lib/directives/index.mjs +4 -0
- package/esm2022/lib/directives/infinity-query-trigger.directive.mjs +56 -0
- package/esm2022/lib/directives/infinity-query.directive.mjs +147 -0
- package/esm2022/lib/directives/query.directive.mjs +123 -0
- package/esm2022/lib/entity/entity-store.mjs +127 -0
- package/esm2022/lib/entity/entity.types.mjs +2 -0
- package/esm2022/lib/entity/entity.utils.mjs +29 -0
- package/esm2022/lib/entity/index.mjs +4 -0
- package/esm2022/lib/gql/gql.mjs +23 -0
- package/esm2022/lib/gql/index.mjs +2 -0
- package/esm2022/lib/infinite-query/index.mjs +4 -0
- package/esm2022/lib/infinite-query/infinity-query.mjs +171 -0
- package/esm2022/lib/infinite-query/infinity-query.types.mjs +2 -0
- package/esm2022/lib/infinite-query/infinity-query.utils.mjs +3 -0
- package/esm2022/lib/logger/index.mjs +2 -0
- package/esm2022/lib/logger/logger.mjs +16 -0
- package/esm2022/lib/query/index.mjs +4 -0
- package/esm2022/lib/query/query.mjs +428 -0
- package/esm2022/lib/query/query.types.mjs +2 -0
- package/esm2022/lib/query/query.utils.mjs +253 -0
- package/esm2022/lib/query-client/index.mjs +4 -0
- package/esm2022/lib/query-client/query-client.mjs +80 -0
- package/esm2022/lib/query-client/query-client.types.mjs +2 -0
- package/esm2022/lib/query-client/query-client.utils.mjs +20 -0
- package/esm2022/lib/query-creator/index.mjs +3 -0
- package/esm2022/lib/query-creator/query-creator.mjs +55 -0
- package/esm2022/lib/query-creator/query-creator.types.mjs +2 -0
- package/esm2022/lib/query-form/index.mjs +4 -0
- package/esm2022/lib/query-form/query-form.mjs +379 -0
- package/esm2022/lib/query-form/query-form.types.mjs +2 -0
- package/esm2022/lib/query-form/query-form.utils.mjs +97 -0
- package/esm2022/lib/query-store/index.mjs +2 -0
- package/esm2022/lib/query-store/query-store.mjs +144 -0
- package/esm2022/lib/request/index.mjs +4 -0
- package/esm2022/lib/request/request.mjs +196 -0
- package/esm2022/lib/request/request.types.mjs +2 -0
- package/esm2022/lib/request/request.util.mjs +252 -0
- package/esm2022/lib/symfony/index.mjs +3 -0
- package/esm2022/lib/symfony/symfony.types.mjs +2 -0
- package/esm2022/lib/symfony/symfony.utils.mjs +31 -0
- package/esm2022/lib/utils/data.utils.mjs +138 -0
- package/esm2022/lib/utils/deep-freeze.util.mjs +17 -0
- package/esm2022/lib/utils/def.util.mjs +8 -0
- package/esm2022/lib/utils/index.mjs +4 -0
- package/fesm2022/ethlete-query.mjs +3287 -0
- package/fesm2022/ethlete-query.mjs.map +1 -0
- package/{src/index.ts → index.d.ts} +0 -2
- package/lib/auth/auth-provider.types.d.ts +133 -0
- package/lib/auth/auth-provider.utils.d.ts +9 -0
- package/lib/auth/basic-auth-provider.d.ts +10 -0
- package/lib/auth/bearer-auth-provider.d.ts +23 -0
- package/lib/auth/custom-header-auth-provider.d.ts +9 -0
- package/{src/lib/class-validator/class-validator.types.ts → lib/class-validator/class-validator.types.d.ts} +3 -4
- package/lib/class-validator/class-validator.utils.d.ts +2 -0
- package/lib/devtools/components/query-devtools/query-devtools.component.d.ts +151 -0
- package/lib/devtools/pipes/query-short-name.pipe.d.ts +8 -0
- package/lib/devtools/utils/query-client.utils.d.ts +12 -0
- package/lib/directives/infinity-query-trigger.directive.d.ts +15 -0
- package/lib/directives/infinity-query.directive.d.ts +49 -0
- package/lib/directives/query.directive.d.ts +63 -0
- package/lib/entity/entity-store.d.ts +25 -0
- package/lib/entity/entity.types.d.ts +25 -0
- package/lib/entity/entity.utils.d.ts +12 -0
- package/lib/gql/gql.d.ts +6 -0
- package/lib/infinite-query/infinity-query.d.ts +29 -0
- package/lib/infinite-query/infinity-query.types.d.ts +104 -0
- package/lib/infinite-query/infinity-query.utils.d.ts +4 -0
- package/lib/logger/logger.d.ts +12 -0
- package/lib/query/query.d.ts +60 -0
- package/lib/query/query.types.d.ts +388 -0
- package/lib/query/query.utils.d.ts +91 -0
- package/lib/query-client/query-client.d.ts +29 -0
- package/lib/query-client/query-client.types.d.ts +82 -0
- package/lib/query-client/query-client.utils.d.ts +4 -0
- package/lib/query-creator/query-creator.d.ts +20 -0
- package/lib/query-creator/query-creator.types.d.ts +13 -0
- package/lib/query-form/query-form.d.ts +91 -0
- package/lib/query-form/query-form.types.d.ts +145 -0
- package/lib/query-form/query-form.utils.d.ts +13 -0
- package/lib/query-store/query-store.d.ts +31 -0
- package/lib/request/request.d.ts +3 -0
- package/lib/request/request.types.d.ts +126 -0
- package/lib/request/request.util.d.ts +92 -0
- package/lib/symfony/symfony.types.d.ts +19 -0
- package/lib/symfony/symfony.utils.d.ts +6 -0
- package/lib/utils/data.utils.d.ts +59 -0
- package/lib/utils/deep-freeze.util.d.ts +1 -0
- package/{src/lib/utils/def.util.ts → lib/utils/def.util.d.ts} +1 -1
- package/package.json +19 -2
- package/.eslintrc.json +0 -40
- package/jest.config.ts +0 -23
- package/ng-package.json +0 -8
- package/project.json +0 -37
- package/src/lib/auth/auth-provider.types.ts +0 -160
- package/src/lib/auth/auth-provider.utils.ts +0 -39
- package/src/lib/auth/basic-auth-provider.ts +0 -17
- package/src/lib/auth/bearer-auth-provider.ts +0 -243
- package/src/lib/auth/custom-header-auth-provider.ts +0 -13
- package/src/lib/class-validator/class-validator.utils.ts +0 -5
- package/src/lib/devtools/components/query-devtools/query-devtools.component.html +0 -402
- package/src/lib/devtools/components/query-devtools/query-devtools.component.scss +0 -360
- package/src/lib/devtools/components/query-devtools/query-devtools.component.ts +0 -427
- package/src/lib/devtools/pipes/query-short-name.pipe.ts +0 -24
- package/src/lib/devtools/utils/query-client.utils.ts +0 -25
- package/src/lib/directives/infinity-query-trigger.directive.ts +0 -58
- package/src/lib/directives/infinity-query.directive.ts +0 -208
- package/src/lib/directives/query.directive.ts +0 -201
- package/src/lib/entity/entity-store.ts +0 -180
- package/src/lib/entity/entity.types.ts +0 -34
- package/src/lib/entity/entity.utils.ts +0 -51
- package/src/lib/gql/gql.ts +0 -22
- package/src/lib/infinite-query/infinity-query.ts +0 -245
- package/src/lib/infinite-query/infinity-query.types.ts +0 -149
- package/src/lib/infinite-query/infinity-query.utils.ts +0 -14
- package/src/lib/logger/logger.ts +0 -30
- package/src/lib/query/query.ts +0 -631
- package/src/lib/query/query.types.ts +0 -532
- package/src/lib/query/query.utils.spec.ts +0 -92
- package/src/lib/query/query.utils.ts +0 -450
- package/src/lib/query-client/query-client.spec.ts +0 -77
- package/src/lib/query-client/query-client.ts +0 -195
- package/src/lib/query-client/query-client.types.ts +0 -101
- package/src/lib/query-client/query-client.utils.spec.ts +0 -61
- package/src/lib/query-client/query-client.utils.ts +0 -28
- package/src/lib/query-creator/query-creator.ts +0 -103
- package/src/lib/query-creator/query-creator.types.ts +0 -74
- package/src/lib/query-form/query-form.ts +0 -526
- package/src/lib/query-form/query-form.types.ts +0 -169
- package/src/lib/query-form/query-form.utils.ts +0 -116
- package/src/lib/query-store/query-store.ts +0 -192
- package/src/lib/request/request.ts +0 -253
- package/src/lib/request/request.types.ts +0 -164
- package/src/lib/request/request.util.spec.ts +0 -177
- package/src/lib/request/request.util.ts +0 -399
- package/src/lib/stories/angular-example-page.mdx +0 -85
- package/src/lib/stories/authentication-page.mdx +0 -35
- package/src/lib/stories/changelog-page.mdx +0 -9
- package/src/lib/stories/client-page.mdx +0 -100
- package/src/lib/stories/queries-page.mdx +0 -117
- package/src/lib/symfony/symfony.types.ts +0 -21
- package/src/lib/symfony/symfony.utils.ts +0 -43
- package/src/lib/utils/data.utils.ts +0 -280
- package/src/lib/utils/deep-freeze.util.ts +0 -21
- package/src/test-setup.ts +0 -1
- package/tsconfig.json +0 -29
- package/tsconfig.lib.json +0 -12
- package/tsconfig.lib.prod.json +0 -10
- package/tsconfig.spec.json +0 -11
- /package/{src/lib/auth/index.ts → lib/auth/index.d.ts} +0 -0
- /package/{src/lib/class-validator/index.ts → lib/class-validator/index.d.ts} +0 -0
- /package/{src/lib/devtools/components/query-devtools/index.ts → lib/devtools/components/query-devtools/index.d.ts} +0 -0
- /package/{src/lib/devtools/pipes/index.ts → lib/devtools/pipes/index.d.ts} +0 -0
- /package/{src/lib/devtools/utils/index.ts → lib/devtools/utils/index.d.ts} +0 -0
- /package/{src/lib/directives/index.ts → lib/directives/index.d.ts} +0 -0
- /package/{src/lib/entity/index.ts → lib/entity/index.d.ts} +0 -0
- /package/{src/lib/gql/index.ts → lib/gql/index.d.ts} +0 -0
- /package/{src/lib/infinite-query/index.ts → lib/infinite-query/index.d.ts} +0 -0
- /package/{src/lib/logger/index.ts → lib/logger/index.d.ts} +0 -0
- /package/{src/lib/query/index.ts → lib/query/index.d.ts} +0 -0
- /package/{src/lib/query-client/index.ts → lib/query-client/index.d.ts} +0 -0
- /package/{src/lib/query-creator/index.ts → lib/query-creator/index.d.ts} +0 -0
- /package/{src/lib/query-form/index.ts → lib/query-form/index.d.ts} +0 -0
- /package/{src/lib/query-store/index.ts → lib/query-store/index.d.ts} +0 -0
- /package/{src/lib/request/index.ts → lib/request/index.d.ts} +0 -0
- /package/{src/lib/symfony/index.ts → lib/symfony/index.d.ts} +0 -0
- /package/{src/lib/utils/index.ts → lib/utils/index.d.ts} +0 -0
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import {
|
|
3
|
-
ChangeDetectorRef,
|
|
4
|
-
Directive,
|
|
5
|
-
ErrorHandler,
|
|
6
|
-
inject,
|
|
7
|
-
InjectionToken,
|
|
8
|
-
Input,
|
|
9
|
-
TemplateRef,
|
|
10
|
-
ViewContainerRef,
|
|
11
|
-
} from '@angular/core';
|
|
12
|
-
import { toSignal } from '@angular/core/rxjs-interop';
|
|
13
|
-
import { createDestroy, DelayableDirective } from '@ethlete/core';
|
|
14
|
-
import { BehaviorSubject, combineLatest, Subject, takeUntil, tap, withLatestFrom } from 'rxjs';
|
|
15
|
-
import { InfinityQuery, InfinityQueryConfig, InfinityQueryOf } from '../infinite-query';
|
|
16
|
-
import {
|
|
17
|
-
BaseArguments,
|
|
18
|
-
isQueryStateFailure,
|
|
19
|
-
isQueryStateLoading,
|
|
20
|
-
isQueryStateSuccess,
|
|
21
|
-
switchQueryState,
|
|
22
|
-
} from '../query';
|
|
23
|
-
import { AnyQueryCreator, ConstructQuery } from '../query-creator';
|
|
24
|
-
import { RequestError } from '../request';
|
|
25
|
-
|
|
26
|
-
interface InfinityQueryContext<
|
|
27
|
-
Q extends InfinityQueryConfig<AnyQueryCreator, BaseArguments | undefined, any, unknown[]>,
|
|
28
|
-
> {
|
|
29
|
-
$implicit: Q['response']['arrayType'] | null;
|
|
30
|
-
etInfinityQuery: Q['response']['arrayType'] | null;
|
|
31
|
-
loading: boolean;
|
|
32
|
-
error: RequestError<unknown> | null;
|
|
33
|
-
|
|
34
|
-
isFirstLoad: boolean;
|
|
35
|
-
canLoadMore: boolean;
|
|
36
|
-
currentPage: number | null;
|
|
37
|
-
currentCalculatedPage: number | null;
|
|
38
|
-
totalPages: number | null;
|
|
39
|
-
itemsPerPage: number | null;
|
|
40
|
-
|
|
41
|
-
currentQuery: ConstructQuery<Q['queryCreator']> | null;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export const INFINITY_QUERY_TOKEN = new InjectionToken<InfinityQueryDirective<any>>('INFINITY_QUERY_TOKEN');
|
|
45
|
-
|
|
46
|
-
@Directive({
|
|
47
|
-
// eslint-disable-next-line @angular-eslint/directive-selector
|
|
48
|
-
selector: '[etInfinityQuery]',
|
|
49
|
-
exportAs: 'etInfinityQuery',
|
|
50
|
-
standalone: true,
|
|
51
|
-
providers: [{ provide: INFINITY_QUERY_TOKEN, useExisting: InfinityQueryDirective }],
|
|
52
|
-
hostDirectives: [DelayableDirective],
|
|
53
|
-
})
|
|
54
|
-
export class InfinityQueryDirective<
|
|
55
|
-
Q extends InfinityQueryConfig<AnyQueryCreator, BaseArguments | undefined, any, unknown[]>,
|
|
56
|
-
> {
|
|
57
|
-
private readonly _queryConfigChanged$ = new Subject<boolean>();
|
|
58
|
-
private readonly _viewContext: InfinityQueryContext<Q> = {
|
|
59
|
-
$implicit: null,
|
|
60
|
-
etInfinityQuery: null,
|
|
61
|
-
loading: false,
|
|
62
|
-
error: null,
|
|
63
|
-
|
|
64
|
-
isFirstLoad: false,
|
|
65
|
-
canLoadMore: false,
|
|
66
|
-
currentPage: null,
|
|
67
|
-
currentCalculatedPage: null,
|
|
68
|
-
totalPages: null,
|
|
69
|
-
itemsPerPage: null,
|
|
70
|
-
|
|
71
|
-
currentQuery: null,
|
|
72
|
-
};
|
|
73
|
-
private _infinityQueryInstance: InfinityQueryOf<Q> | null = null;
|
|
74
|
-
|
|
75
|
-
private readonly _destroy$ = createDestroy();
|
|
76
|
-
private readonly _cdr = inject(ChangeDetectorRef);
|
|
77
|
-
private readonly _viewContainerRef = inject(ViewContainerRef);
|
|
78
|
-
private readonly _mainTemplateRef = inject(TemplateRef<InfinityQueryContext<Q>>);
|
|
79
|
-
private readonly _errorHandler = inject(ErrorHandler);
|
|
80
|
-
private readonly _delayable = inject(DelayableDirective, { host: true });
|
|
81
|
-
|
|
82
|
-
private readonly _data$ = new BehaviorSubject<Q['response']['arrayType']>([]);
|
|
83
|
-
|
|
84
|
-
@Input('etInfinityQuery')
|
|
85
|
-
get infinityQuery(): Q {
|
|
86
|
-
return this._infinityQuery;
|
|
87
|
-
}
|
|
88
|
-
set infinityQuery(v: Q) {
|
|
89
|
-
this._cleanup();
|
|
90
|
-
|
|
91
|
-
this._infinityQuery = v;
|
|
92
|
-
this._infinityQueryInstance = this._setupInfinityQuery(v);
|
|
93
|
-
this._infinityQueryInstance.nextPage();
|
|
94
|
-
}
|
|
95
|
-
private _infinityQuery!: Q;
|
|
96
|
-
|
|
97
|
-
get context() {
|
|
98
|
-
return this._viewContext;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
get instance() {
|
|
102
|
-
return this._infinityQueryInstance;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
get data$() {
|
|
106
|
-
return this._data$.asObservable();
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
data = toSignal(this._data$, { requireSync: true });
|
|
110
|
-
|
|
111
|
-
static ngTemplateContextGuard<
|
|
112
|
-
Q extends InfinityQueryConfig<AnyQueryCreator, BaseArguments | undefined, any, unknown[]>,
|
|
113
|
-
>(dir: InfinityQueryDirective<Q>, ctx: unknown): ctx is InfinityQueryContext<Q> {
|
|
114
|
-
return true;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
constructor() {
|
|
118
|
-
this._viewContainerRef.createEmbeddedView(this._mainTemplateRef, this._viewContext);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
private _setupInfinityQuery(config: Q) {
|
|
122
|
-
const instance = new InfinityQuery(config, this._destroy$) as InfinityQueryOf<Q>;
|
|
123
|
-
|
|
124
|
-
combineLatest([
|
|
125
|
-
instance.currentQuery$.pipe(switchQueryState(), withLatestFrom(instance.currentQuery$)),
|
|
126
|
-
this._delayable.isDelayed$,
|
|
127
|
-
instance.data$,
|
|
128
|
-
])
|
|
129
|
-
.pipe(
|
|
130
|
-
tap(([[state, currentQuery], isDelayed, infinityArray]) => {
|
|
131
|
-
this._viewContext.currentPage = instance.currentPage;
|
|
132
|
-
this._viewContext.totalPages = instance.totalPages;
|
|
133
|
-
this._viewContext.itemsPerPage = instance.itemsPerPage;
|
|
134
|
-
this._viewContext.canLoadMore =
|
|
135
|
-
(instance.totalPages !== null &&
|
|
136
|
-
instance.currentPage !== null &&
|
|
137
|
-
instance.totalPages > instance.currentPage) ||
|
|
138
|
-
false;
|
|
139
|
-
this._viewContext.currentCalculatedPage = instance.currentCalculatedPage;
|
|
140
|
-
this._viewContext.currentQuery = currentQuery;
|
|
141
|
-
|
|
142
|
-
if (isQueryStateLoading(state) || isDelayed || !infinityArray) {
|
|
143
|
-
this._viewContext.loading = state ? state.meta.triggeredVia !== 'poll' : true;
|
|
144
|
-
this._viewContext.error = null;
|
|
145
|
-
this._viewContext.isFirstLoad = this.context.etInfinityQuery === null;
|
|
146
|
-
} else if (isQueryStateFailure(state)) {
|
|
147
|
-
this._viewContext.loading = false;
|
|
148
|
-
this._viewContext.error = state.error;
|
|
149
|
-
this._viewContext.isFirstLoad = false;
|
|
150
|
-
this._errorHandler.handleError(state.error);
|
|
151
|
-
} else if (isQueryStateSuccess(state)) {
|
|
152
|
-
this._viewContext.loading = false;
|
|
153
|
-
this._viewContext.error = null;
|
|
154
|
-
this._viewContext.isFirstLoad = false;
|
|
155
|
-
this._viewContext.etInfinityQuery = this._viewContext.$implicit =
|
|
156
|
-
infinityArray as Q['response']['arrayType'];
|
|
157
|
-
this._data$.next(infinityArray);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
this._cdr.markForCheck();
|
|
161
|
-
}),
|
|
162
|
-
takeUntil(this._destroy$),
|
|
163
|
-
takeUntil(this._queryConfigChanged$),
|
|
164
|
-
)
|
|
165
|
-
.subscribe();
|
|
166
|
-
|
|
167
|
-
return instance;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
loadNextPage() {
|
|
171
|
-
if (!this._infinityQueryInstance) {
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (this._viewContext.loading) {
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
this._infinityQueryInstance.nextPage();
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
reset(newConfig?: Omit<Q, 'queryCreator' | 'response'>) {
|
|
183
|
-
if (!this._infinityQueryInstance) {
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
this._infinityQueryInstance.reset(newConfig as any);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
private _cleanup() {
|
|
191
|
-
this._queryConfigChanged$.next(true);
|
|
192
|
-
|
|
193
|
-
this._viewContext.loading = false;
|
|
194
|
-
this._viewContext.error = null;
|
|
195
|
-
this._viewContext.etInfinityQuery = null;
|
|
196
|
-
this._viewContext.$implicit = null;
|
|
197
|
-
|
|
198
|
-
this._viewContext.isFirstLoad = false;
|
|
199
|
-
this._viewContext.canLoadMore = false;
|
|
200
|
-
this._viewContext.currentPage = null;
|
|
201
|
-
this._viewContext.itemsPerPage = null;
|
|
202
|
-
this._viewContext.totalPages = null;
|
|
203
|
-
|
|
204
|
-
this._viewContext.currentQuery = null;
|
|
205
|
-
|
|
206
|
-
this._data$.next([]);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ChangeDetectorRef,
|
|
3
|
-
Directive,
|
|
4
|
-
ErrorHandler,
|
|
5
|
-
Input,
|
|
6
|
-
OnDestroy,
|
|
7
|
-
OnInit,
|
|
8
|
-
TemplateRef,
|
|
9
|
-
ViewContainerRef,
|
|
10
|
-
inject,
|
|
11
|
-
} from '@angular/core';
|
|
12
|
-
import { Subscription, tap } from 'rxjs';
|
|
13
|
-
import {
|
|
14
|
-
AnyQuery,
|
|
15
|
-
AnyQueryCollection,
|
|
16
|
-
QueryCollectionKeysOf,
|
|
17
|
-
QueryOf,
|
|
18
|
-
QueryState,
|
|
19
|
-
extractQuery,
|
|
20
|
-
isQueryCollection,
|
|
21
|
-
isQueryStateCancelled,
|
|
22
|
-
isQueryStateFailure,
|
|
23
|
-
isQueryStateLoading,
|
|
24
|
-
isQueryStatePrepared,
|
|
25
|
-
isQueryStateSuccess,
|
|
26
|
-
} from '../query';
|
|
27
|
-
import { QueryDataOf } from '../query-creator';
|
|
28
|
-
import { RequestError, RequestProgress } from '../request';
|
|
29
|
-
|
|
30
|
-
interface QueryContext<Q extends AnyQuery | AnyQueryCollection | null> {
|
|
31
|
-
/**
|
|
32
|
-
* The queries's response data.
|
|
33
|
-
*/
|
|
34
|
-
$implicit: QueryDataOf<QueryOf<Q>> | null;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* The queries's response data.
|
|
38
|
-
*/
|
|
39
|
-
etQuery: QueryDataOf<QueryOf<Q>> | null;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* The query used inside this directive. Useful if for instance the query gets unwrapped via async pipe inside the directive.
|
|
43
|
-
*/
|
|
44
|
-
query: QueryOf<Q> | null;
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Is true when the query is triggered by user interaction.
|
|
48
|
-
*/
|
|
49
|
-
loading: boolean;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Is true when the query is triggered by either polling or auto refresh event.
|
|
53
|
-
*/
|
|
54
|
-
refreshing: boolean;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* The query's progress state.
|
|
58
|
-
*/
|
|
59
|
-
progress: RequestProgress | null;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* The query's error state.
|
|
63
|
-
*/
|
|
64
|
-
error: RequestError<unknown> | null;
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* The query's scope (only available when the query is a collection)
|
|
68
|
-
*/
|
|
69
|
-
scope: QueryCollectionKeysOf<Q> | null;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
@Directive({
|
|
73
|
-
// eslint-disable-next-line @angular-eslint/directive-selector
|
|
74
|
-
selector: '[etQuery]',
|
|
75
|
-
standalone: true,
|
|
76
|
-
})
|
|
77
|
-
export class QueryDirective<Q extends AnyQuery | AnyQueryCollection | null> implements OnInit, OnDestroy {
|
|
78
|
-
private _mainTemplateRef = inject<TemplateRef<QueryContext<Q>>>(TemplateRef);
|
|
79
|
-
private _viewContainerRef = inject(ViewContainerRef);
|
|
80
|
-
private _errorHandler = inject(ErrorHandler);
|
|
81
|
-
private _cdr = inject(ChangeDetectorRef);
|
|
82
|
-
|
|
83
|
-
private _isMainViewCreated = false;
|
|
84
|
-
private _subscription: Subscription | null = null;
|
|
85
|
-
|
|
86
|
-
private readonly _viewContext: QueryContext<Q> = {
|
|
87
|
-
$implicit: null,
|
|
88
|
-
etQuery: null,
|
|
89
|
-
loading: false,
|
|
90
|
-
refreshing: false,
|
|
91
|
-
error: null,
|
|
92
|
-
progress: null,
|
|
93
|
-
scope: null,
|
|
94
|
-
query: null,
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
@Input('etQuery')
|
|
98
|
-
get query(): Q {
|
|
99
|
-
return this._query;
|
|
100
|
-
}
|
|
101
|
-
set query(v: Q) {
|
|
102
|
-
this._query = v;
|
|
103
|
-
|
|
104
|
-
this._subscribeToQuery();
|
|
105
|
-
}
|
|
106
|
-
private _query!: Q;
|
|
107
|
-
|
|
108
|
-
@Input('etQueryCache')
|
|
109
|
-
get cache(): boolean {
|
|
110
|
-
return this._cache;
|
|
111
|
-
}
|
|
112
|
-
set cache(v: boolean) {
|
|
113
|
-
this._cache = v;
|
|
114
|
-
}
|
|
115
|
-
private _cache = false;
|
|
116
|
-
|
|
117
|
-
static ngTemplateContextGuard<Q extends AnyQuery | AnyQueryCollection | null>(
|
|
118
|
-
dir: QueryDirective<Q>,
|
|
119
|
-
ctx: unknown,
|
|
120
|
-
): ctx is QueryContext<Q> {
|
|
121
|
-
return true;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
ngOnInit(): void {
|
|
125
|
-
this._renderMainView();
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
ngOnDestroy(): void {
|
|
129
|
-
this._subscription?.unsubscribe();
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
private _subscribeToQuery(): void {
|
|
133
|
-
this._subscription?.unsubscribe();
|
|
134
|
-
this._subscription = null;
|
|
135
|
-
|
|
136
|
-
const query = extractQuery(this.query);
|
|
137
|
-
|
|
138
|
-
if (!query) {
|
|
139
|
-
this._viewContext.$implicit = null;
|
|
140
|
-
this._viewContext.etQuery = null;
|
|
141
|
-
this._viewContext.loading = false;
|
|
142
|
-
this._viewContext.refreshing = false;
|
|
143
|
-
this._viewContext.error = null;
|
|
144
|
-
this._viewContext.progress = null;
|
|
145
|
-
this._viewContext.scope = null;
|
|
146
|
-
this._viewContext.query = null;
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (isQueryStatePrepared(query.rawState) || isQueryStateCancelled(query.rawState)) {
|
|
151
|
-
query.execute();
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const sub = query.state$.pipe(tap((state) => this._updateView(state))).subscribe();
|
|
155
|
-
|
|
156
|
-
this._viewContext.scope = isQueryCollection(query) ? (query.type as QueryCollectionKeysOf<Q>) : null;
|
|
157
|
-
this._viewContext.query = query as QueryOf<Q>;
|
|
158
|
-
|
|
159
|
-
this._subscription = sub;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
private _updateView(state: QueryState) {
|
|
163
|
-
if (isQueryStateLoading(state)) {
|
|
164
|
-
this._viewContext.progress = state.progress ?? null;
|
|
165
|
-
this._viewContext.refreshing = state.meta.triggeredVia === 'auto' || state.meta.triggeredVia === 'poll';
|
|
166
|
-
|
|
167
|
-
if (!this._viewContext.refreshing) {
|
|
168
|
-
this._viewContext.loading = true;
|
|
169
|
-
}
|
|
170
|
-
} else {
|
|
171
|
-
this._viewContext.loading = false;
|
|
172
|
-
this._viewContext.refreshing = false;
|
|
173
|
-
this._viewContext.progress = null;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (isQueryStateSuccess(state)) {
|
|
177
|
-
this._viewContext.etQuery = state.response as QueryDataOf<QueryOf<Q>>;
|
|
178
|
-
this._viewContext.$implicit = state.response as QueryDataOf<QueryOf<Q>>;
|
|
179
|
-
} else if (!this.cache) {
|
|
180
|
-
this._viewContext.etQuery = null;
|
|
181
|
-
this._viewContext.$implicit = null;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (isQueryStateFailure(state)) {
|
|
185
|
-
this._viewContext.error = state.error;
|
|
186
|
-
|
|
187
|
-
this._errorHandler.handleError(state.error);
|
|
188
|
-
} else {
|
|
189
|
-
this._viewContext.error = null;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
this._cdr.markForCheck();
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
private _renderMainView(): void {
|
|
196
|
-
if (!this._isMainViewCreated) {
|
|
197
|
-
this._isMainViewCreated = true;
|
|
198
|
-
this._viewContainerRef.createEmbeddedView(this._mainTemplateRef, this._viewContext);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import { Observable, Subject, debounceTime, filter, map, shareReplay, startWith } from 'rxjs';
|
|
2
|
-
import { EntityKey, EntityStoreConfig } from './entity.types';
|
|
3
|
-
|
|
4
|
-
class EntityStoreError extends Error {
|
|
5
|
-
constructor(
|
|
6
|
-
message: string,
|
|
7
|
-
public detail?: unknown,
|
|
8
|
-
) {
|
|
9
|
-
super(message);
|
|
10
|
-
this.name = 'EntityStoreError';
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function flatten<T>(arr: T[][]): T[] {
|
|
15
|
-
return arr.reduce((acc, val) => acc.concat(val), []);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export class EntityStore<T> {
|
|
19
|
-
/**
|
|
20
|
-
* @internal
|
|
21
|
-
*/
|
|
22
|
-
readonly _dictionary = new Map<EntityKey, T>();
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* @internal
|
|
26
|
-
*/
|
|
27
|
-
readonly _change$ = new Subject<EntityKey | EntityKey[]>();
|
|
28
|
-
|
|
29
|
-
constructor(private readonly _config: EntityStoreConfig) {}
|
|
30
|
-
|
|
31
|
-
select(key: EntityKey): Observable<T | null>;
|
|
32
|
-
select(keys: EntityKey[]): Observable<T[]>;
|
|
33
|
-
select(keys: EntityKey[][]): Observable<T[]>;
|
|
34
|
-
select(keyOrKeys: EntityKey | EntityKey[] | EntityKey[][]): Observable<T | null> | Observable<T[]> {
|
|
35
|
-
if (Array.isArray(keyOrKeys)) {
|
|
36
|
-
const _keys = this._normalizeKeys(keyOrKeys);
|
|
37
|
-
return this._selectMany(_keys as EntityKey[]);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return this._select(keyOrKeys);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* **Note:** Prefer `select` over `selectWhere` when possible.
|
|
45
|
-
*/
|
|
46
|
-
selectWhere(predicate: (value: T) => boolean): Observable<T[]> {
|
|
47
|
-
return this._change$.pipe(
|
|
48
|
-
startWith(null),
|
|
49
|
-
debounceTime(0),
|
|
50
|
-
map(() => Array.from(this._dictionary.values()).filter(predicate)),
|
|
51
|
-
shareReplay({ bufferSize: 1, refCount: true }),
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
set(key: EntityKey, value: T): void;
|
|
56
|
-
set(keys: EntityKey[], values: T[]): void;
|
|
57
|
-
set(keys: EntityKey[][], values: T[][]): void;
|
|
58
|
-
set(keyOrKeys: EntityKey | EntityKey[] | EntityKey[][], valueOrValues: T | T[] | T[][]) {
|
|
59
|
-
const _keys = this._normalizeKeys(keyOrKeys);
|
|
60
|
-
|
|
61
|
-
if (Array.isArray(_keys)) {
|
|
62
|
-
if (!Array.isArray(valueOrValues)) {
|
|
63
|
-
throw new EntityStoreError('When setting multiple keys, values must be an array', {
|
|
64
|
-
keyOrValues: _keys,
|
|
65
|
-
valueOrValues,
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const _values = (Array.isArray(valueOrValues[0]) ? flatten(valueOrValues as T[][]) : valueOrValues) as T[];
|
|
70
|
-
|
|
71
|
-
for (let i = 0; i < _keys.length; i++) {
|
|
72
|
-
const k = _keys[i];
|
|
73
|
-
const v = _values[i];
|
|
74
|
-
|
|
75
|
-
if (k === undefined || v === undefined) continue;
|
|
76
|
-
|
|
77
|
-
this._set(k, v);
|
|
78
|
-
}
|
|
79
|
-
} else {
|
|
80
|
-
this._set(_keys, valueOrValues as T);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
this._change$.next(_keys);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
remove(key: EntityKey): void;
|
|
87
|
-
remove(keys: EntityKey[]): void;
|
|
88
|
-
remove(keys: EntityKey[][]): void;
|
|
89
|
-
remove(keyOrKeys: EntityKey | EntityKey[] | EntityKey[][]) {
|
|
90
|
-
const _keys = this._normalizeKeys(keyOrKeys);
|
|
91
|
-
|
|
92
|
-
if (Array.isArray(_keys)) {
|
|
93
|
-
for (const key of _keys) {
|
|
94
|
-
this._remove(key);
|
|
95
|
-
}
|
|
96
|
-
} else {
|
|
97
|
-
this._remove(_keys);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
this._change$.next(_keys);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
logState() {
|
|
104
|
-
console.log('EntityStore:', this._config.name, {
|
|
105
|
-
dictionary: this._dictionary,
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
private _select(key: EntityKey): Observable<T | null> {
|
|
110
|
-
if (this._config.logActions) {
|
|
111
|
-
console.log('EntityStore: select', this._config.name, key);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return this._change$.pipe(
|
|
115
|
-
startWith(null),
|
|
116
|
-
filter((v) => {
|
|
117
|
-
if (v === null) {
|
|
118
|
-
return true;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (Array.isArray(v)) {
|
|
122
|
-
return v.includes(key);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return v === key;
|
|
126
|
-
}),
|
|
127
|
-
map(() => this._dictionary.get(key) ?? null),
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
private _selectMany(keys: EntityKey[]): Observable<T[]> {
|
|
132
|
-
if (this._config.logActions) {
|
|
133
|
-
console.log('EntityStore: selectMany', this._config.name, keys);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return this._change$.pipe(
|
|
137
|
-
startWith(null),
|
|
138
|
-
filter((v) => {
|
|
139
|
-
if (v === null) {
|
|
140
|
-
return true;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (Array.isArray(v)) {
|
|
144
|
-
return keys.some((key) => v.includes(key));
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return keys.includes(v);
|
|
148
|
-
}),
|
|
149
|
-
map(() => keys.map((key) => this._dictionary.get(key)).filter((value): value is T => value !== undefined)),
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
private _set(key: EntityKey, value: T) {
|
|
154
|
-
if (this._config.logActions) {
|
|
155
|
-
console.log('EntityStore: set', this._config.name, key, value);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
this._dictionary.set(key, value);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
private _remove(key: EntityKey) {
|
|
162
|
-
if (this._config.logActions) {
|
|
163
|
-
console.log('EntityStore: remove', this._config.name, key);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
this._dictionary.delete(key);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
private _normalizeKeys(keys: EntityKey | EntityKey[] | EntityKey[][]): EntityKey | EntityKey[] {
|
|
170
|
-
if (Array.isArray(keys)) {
|
|
171
|
-
if (Array.isArray(keys[0])) {
|
|
172
|
-
return flatten(keys as EntityKey[][]);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return keys as EntityKey[];
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return keys;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
export type EntityKey = string | number;
|
|
2
|
-
|
|
3
|
-
export interface EntityStoreConfig {
|
|
4
|
-
name: string;
|
|
5
|
-
logActions?: boolean;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface InsertFromConfig<
|
|
9
|
-
OriginStoreType,
|
|
10
|
-
OriginKeys extends keyof NonNullable<OriginStoreType>,
|
|
11
|
-
IdFn extends (value: NonNullable<OriginStoreType>) => EntityKey | EntityKey[],
|
|
12
|
-
> {
|
|
13
|
-
/**
|
|
14
|
-
* The property key of which the value should be replaced.
|
|
15
|
-
*/
|
|
16
|
-
for: OriginKeys;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* A function that returns the id of the entity. Can also return an array of ids.
|
|
20
|
-
*/
|
|
21
|
-
id: IdFn;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface RemoveFromConfig<T> {
|
|
25
|
-
/**
|
|
26
|
-
* The where function to select the entities to remove.
|
|
27
|
-
*/
|
|
28
|
-
where: (entity: T) => boolean;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* A function that returns the id of the entity.
|
|
32
|
-
*/
|
|
33
|
-
id: (entity: T) => EntityKey;
|
|
34
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { Paginated } from '@ethlete/types';
|
|
2
|
-
import { map, of, switchMap, take, tap } from 'rxjs';
|
|
3
|
-
import { EntityStore } from './entity-store';
|
|
4
|
-
import { EntityKey, InsertFromConfig, RemoveFromConfig } from './entity.types';
|
|
5
|
-
|
|
6
|
-
export function mapToPaginated<T>(response: Paginated<T>) {
|
|
7
|
-
return map((items) => ({ ...response, items }) as Paginated<T>);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// FIXME: This breaks if SubStoreType is an array
|
|
11
|
-
/**
|
|
12
|
-
* Use an other store to represent a property of the current entity.
|
|
13
|
-
*/
|
|
14
|
-
export const insertFrom = <
|
|
15
|
-
OriginStoreType,
|
|
16
|
-
OriginKeys extends keyof NonNullable<OriginStoreType>,
|
|
17
|
-
SubStoreType extends NonNullable<OriginStoreType>[OriginKeys] extends Array<infer U>
|
|
18
|
-
? U
|
|
19
|
-
: NonNullable<OriginStoreType>[OriginKeys],
|
|
20
|
-
IdFn extends (value: NonNullable<OriginStoreType>) => EntityKey | EntityKey[],
|
|
21
|
-
ComputedEntityType extends ReturnType<IdFn> extends Array<unknown>
|
|
22
|
-
? OriginStoreType
|
|
23
|
-
: Omit<OriginStoreType, OriginKeys> & { [K in OriginKeys]: SubStoreType | null },
|
|
24
|
-
>(
|
|
25
|
-
store: EntityStore<SubStoreType>,
|
|
26
|
-
{ for: key, id }: InsertFromConfig<OriginStoreType, OriginKeys, IdFn>,
|
|
27
|
-
) =>
|
|
28
|
-
switchMap((value: OriginStoreType) => {
|
|
29
|
-
if (!value) return of(null);
|
|
30
|
-
|
|
31
|
-
const ids = id(value);
|
|
32
|
-
|
|
33
|
-
if (Array.isArray(ids)) {
|
|
34
|
-
return store.select(ids).pipe(map((data) => ({ ...value, [key]: data }) as ComputedEntityType));
|
|
35
|
-
} else {
|
|
36
|
-
return store.select(ids).pipe(map((data) => ({ ...value, [key]: data }) as ComputedEntityType));
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Remove entities from a store based on a where function.
|
|
42
|
-
*/
|
|
43
|
-
export const removeFrom = <T>(store: EntityStore<T>, { where, id }: RemoveFromConfig<T>) => {
|
|
44
|
-
store
|
|
45
|
-
.selectWhere(where)
|
|
46
|
-
.pipe(
|
|
47
|
-
tap((entities) => store.remove(entities.map(id))),
|
|
48
|
-
take(1),
|
|
49
|
-
)
|
|
50
|
-
.subscribe();
|
|
51
|
-
};
|
package/src/lib/gql/gql.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
const getOpName = /(query|mutation) ?([\w\d-_]+)? ?\(.*?\)? \{/;
|
|
2
|
-
|
|
3
|
-
export const transformGql = (str: string) => {
|
|
4
|
-
str = Array.isArray(str) ? str.join('') : str;
|
|
5
|
-
const name = getOpName.exec(str);
|
|
6
|
-
return function (variables: Record<string, unknown> | null | undefined) {
|
|
7
|
-
const data: Record<string, unknown> = { query: str };
|
|
8
|
-
if (variables) data['variables'] = JSON.stringify(variables);
|
|
9
|
-
if (name && name.length) {
|
|
10
|
-
const operationName = name[2];
|
|
11
|
-
if (operationName) data['operationName'] = name[2];
|
|
12
|
-
}
|
|
13
|
-
return data as { query: string; variables?: string; operationName?: string };
|
|
14
|
-
};
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export const gql = (strings: TemplateStringsArray, ...values: string[]) => {
|
|
18
|
-
const str = strings.reduce((acc, cur, i) => {
|
|
19
|
-
return acc + cur + (values[i] || '');
|
|
20
|
-
}, '');
|
|
21
|
-
return str;
|
|
22
|
-
};
|