ngx-dynamic-entity 0.0.1 → 1.1.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.
- package/README.md +129 -0
- package/esm2022/lib/field-types/array-field.component.mjs +160 -0
- package/esm2022/lib/field-types/boolean-field.component.mjs +100 -0
- package/esm2022/lib/field-types/checkbox-field.component.mjs +25 -17
- package/esm2022/lib/field-types/currency-field.component.mjs +101 -0
- package/esm2022/lib/field-types/date-field.component.mjs +21 -13
- package/esm2022/lib/field-types/dropdown-field.component.mjs +120 -29
- package/esm2022/lib/field-types/email-field.component.mjs +101 -0
- package/esm2022/lib/field-types/entity-ref-field.component.mjs +96 -32
- package/esm2022/lib/field-types/file-field.component.mjs +156 -0
- package/esm2022/lib/field-types/group-field.component.mjs +89 -0
- package/esm2022/lib/field-types/image-field.component.mjs +168 -0
- package/esm2022/lib/field-types/month-year-field.component.mjs +151 -0
- package/esm2022/lib/field-types/multi-select-field.component.mjs +79 -25
- package/esm2022/lib/field-types/number-field.component.mjs +60 -17
- package/esm2022/lib/field-types/password-field.component.mjs +109 -0
- package/esm2022/lib/field-types/radio-field.component.mjs +152 -0
- package/esm2022/lib/field-types/text-field.component.mjs +58 -18
- package/esm2022/lib/field-types/textarea-field.component.mjs +26 -15
- package/esm2022/lib/form/dynamic-field/dynamic-field.component.mjs +44 -26
- package/esm2022/lib/form/dynamic-form.component.mjs +676 -69
- package/esm2022/lib/form/dynamic-record-form.component.mjs +494 -0
- package/esm2022/lib/providers/provide-field-types.mjs +72 -0
- package/esm2022/lib/providers/provide-ngx-dynamic-entity.mjs +4 -5
- package/esm2022/lib/services/cascade-data.service.mjs +129 -0
- package/esm2022/lib/services/config-source.service.mjs +67 -0
- package/esm2022/lib/services/entity-ref-cache.mjs +20 -0
- package/esm2022/lib/services/entity-ref-registry.service.mjs +3 -3
- package/esm2022/lib/services/entity-ref-selection.service.mjs +27 -0
- package/esm2022/lib/services/entity-reference.service.mjs +168 -0
- package/esm2022/lib/services/field-registry.service.mjs +35 -30
- package/esm2022/lib/services/file-upload.service.mjs +89 -0
- package/esm2022/lib/services/lookup-registry.service.mjs +246 -0
- package/esm2022/lib/services/rbac.service.mjs +5 -11
- package/esm2022/lib/services/rules-evaluation.service.mjs +27 -0
- package/esm2022/lib/services/validator-registry.service.mjs +35 -7
- package/esm2022/lib/tokens/injection-tokens.mjs +104 -7
- package/esm2022/public-api.mjs +31 -6
- package/fesm2022/ngx-dynamic-entity.mjs +4094 -759
- package/fesm2022/ngx-dynamic-entity.mjs.map +1 -1
- package/lib/field-types/array-field.component.d.ts +22 -0
- package/lib/field-types/boolean-field.component.d.ts +18 -0
- package/lib/field-types/checkbox-field.component.d.ts +3 -2
- package/lib/field-types/currency-field.component.d.ts +17 -0
- package/lib/field-types/date-field.component.d.ts +3 -2
- package/lib/field-types/dropdown-field.component.d.ts +24 -4
- package/lib/field-types/email-field.component.d.ts +15 -0
- package/lib/field-types/entity-ref-field.component.d.ts +24 -11
- package/lib/field-types/file-field.component.d.ts +25 -0
- package/lib/field-types/group-field.component.d.ts +17 -0
- package/lib/field-types/image-field.component.d.ts +30 -0
- package/lib/field-types/month-year-field.component.d.ts +28 -0
- package/lib/field-types/multi-select-field.component.d.ts +18 -4
- package/lib/field-types/number-field.component.d.ts +5 -2
- package/lib/field-types/password-field.component.d.ts +17 -0
- package/lib/field-types/radio-field.component.d.ts +33 -0
- package/lib/field-types/text-field.component.d.ts +5 -3
- package/lib/field-types/textarea-field.component.d.ts +4 -2
- package/lib/form/dynamic-field/dynamic-field.component.d.ts +20 -7
- package/lib/form/dynamic-form.component.d.ts +203 -24
- package/lib/form/dynamic-record-form.component.d.ts +180 -0
- package/lib/providers/provide-field-types.d.ts +24 -0
- package/lib/providers/provide-ngx-dynamic-entity.d.ts +19 -8
- package/lib/services/cascade-data.service.d.ts +45 -0
- package/lib/services/config-source.service.d.ts +14 -0
- package/lib/services/entity-ref-cache.d.ts +27 -0
- package/lib/services/entity-ref-registry.service.d.ts +4 -3
- package/lib/services/entity-ref-selection.service.d.ts +24 -0
- package/lib/services/entity-reference.service.d.ts +49 -0
- package/lib/services/field-registry.service.d.ts +17 -7
- package/lib/services/file-upload.service.d.ts +27 -0
- package/lib/services/lookup-registry.service.d.ts +94 -0
- package/lib/services/rbac.service.d.ts +4 -9
- package/lib/services/rules-evaluation.service.d.ts +17 -0
- package/lib/services/validator-registry.service.d.ts +5 -4
- package/lib/tokens/injection-tokens.d.ts +113 -8
- package/package.json +18 -8
- package/public-api.d.ts +21 -4
- package/esm2022/lib/services/config.service.mjs +0 -58
- package/esm2022/lib/services/version.service.mjs +0 -40
- package/esm2022/lib/table/dynamic-table.component.mjs +0 -160
- package/lib/services/config.service.d.ts +0 -25
- package/lib/services/version.service.d.ts +0 -26
- package/lib/table/dynamic-table.component.d.ts +0 -56
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { Injectable, inject } from '@angular/core';
|
|
2
|
+
import { buildReferenceCacheKey, entityKeyFromCacheKey, normalizeReferenceOptions, resolveLabel, valuesMatch, } from '@dynamic-entity/core';
|
|
3
|
+
import { ENTITY_REF_CACHE_STORE } from '../tokens/injection-tokens';
|
|
4
|
+
import { EntityRefRegistryService } from './entity-ref-registry.service';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
/** Label-cache field separator. Kept out of template literals so it stays visible in source. */
|
|
7
|
+
const LABEL_SEP = '|';
|
|
8
|
+
/**
|
|
9
|
+
* EntityReferenceService — the cache in front of consumer-registered option loaders.
|
|
10
|
+
*
|
|
11
|
+
* Three layers, matching the reference implementation:
|
|
12
|
+
* 1. **options** — resolved `ReferenceOption[]` per (entity, lang, displayFields, filters).
|
|
13
|
+
* 2. **in-flight** — the pending Promise, so N fields referencing one entity trigger one
|
|
14
|
+
* load rather than N. This is the layer that actually removes duplicate HTTP calls;
|
|
15
|
+
* an options cache alone does nothing for concurrent first loads.
|
|
16
|
+
* 3. **labels** — synchronous value → text, for rendering a stored reference without
|
|
17
|
+
* waiting on a load.
|
|
18
|
+
*
|
|
19
|
+
* Invalidation is per entity, not global. The reference could only `clearCache()` everything
|
|
20
|
+
* (issue #16), so saving one record re-fetched every entity's options.
|
|
21
|
+
*/
|
|
22
|
+
export class EntityReferenceService {
|
|
23
|
+
registry = inject(EntityRefRegistryService);
|
|
24
|
+
store = inject(ENTITY_REF_CACHE_STORE);
|
|
25
|
+
/** Loads in progress, keyed the same way as the options cache. */
|
|
26
|
+
inFlight = new Map();
|
|
27
|
+
/** `entityKey|lang|canonicalValue` -> display text. */
|
|
28
|
+
labels = new Map();
|
|
29
|
+
/**
|
|
30
|
+
* Options for an entity, from cache when present.
|
|
31
|
+
* Concurrent callers with the same key share one load.
|
|
32
|
+
*/
|
|
33
|
+
async load(entityKey, ctx = {}) {
|
|
34
|
+
const key = buildReferenceCacheKey(entityKey, {
|
|
35
|
+
lang: ctx.lang,
|
|
36
|
+
filters: ctx.filters,
|
|
37
|
+
displayFields: ctx.displayFields,
|
|
38
|
+
parentValue: ctx.parentValue,
|
|
39
|
+
});
|
|
40
|
+
const cached = this.store.get(key);
|
|
41
|
+
if (cached)
|
|
42
|
+
return cached;
|
|
43
|
+
const pending = this.inFlight.get(key);
|
|
44
|
+
if (pending)
|
|
45
|
+
return pending;
|
|
46
|
+
const loader = this.registry.resolve(entityKey);
|
|
47
|
+
if (!loader)
|
|
48
|
+
return [];
|
|
49
|
+
const load = this.invoke(loader, ctx)
|
|
50
|
+
.then(raw => {
|
|
51
|
+
// displayFields drive label building, which is why they are part of the cache key.
|
|
52
|
+
const options = normalizeReferenceOptions(raw, ctx.displayFields ? { enabled: true, displayFields: ctx.displayFields } : undefined, ctx.lang ?? 'en');
|
|
53
|
+
this.store.set(key, options);
|
|
54
|
+
this.rememberLabels(entityKey, ctx.lang ?? 'en', options);
|
|
55
|
+
return options;
|
|
56
|
+
});
|
|
57
|
+
// A failed load resolves to [] for the caller but is never written to the store, so a
|
|
58
|
+
// transient error is retried on the next call instead of poisoning the cache. Only the
|
|
59
|
+
// `.then` above caches, and a rejection skips it.
|
|
60
|
+
const guarded = load
|
|
61
|
+
.catch(() => [])
|
|
62
|
+
.finally(() => this.inFlight.delete(key));
|
|
63
|
+
this.inFlight.set(key, guarded);
|
|
64
|
+
return guarded;
|
|
65
|
+
}
|
|
66
|
+
/** Cached options without triggering a load. `undefined` when not cached. */
|
|
67
|
+
peek(entityKey, ctx = {}) {
|
|
68
|
+
return this.store.get(buildReferenceCacheKey(entityKey, {
|
|
69
|
+
lang: ctx.lang,
|
|
70
|
+
filters: ctx.filters,
|
|
71
|
+
displayFields: ctx.displayFields,
|
|
72
|
+
parentValue: ctx.parentValue,
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Display text for a stored reference value, synchronously.
|
|
77
|
+
* Returns `undefined` when the entity's options have never been loaded.
|
|
78
|
+
*/
|
|
79
|
+
labelFor(entityKey, value, lang = 'en') {
|
|
80
|
+
return this.labels.get(this.labelKey(entityKey, lang, value));
|
|
81
|
+
}
|
|
82
|
+
/** Drop everything cached for one entity, leaving other entities intact. */
|
|
83
|
+
invalidate(entityKey) {
|
|
84
|
+
for (const key of this.store.keys()) {
|
|
85
|
+
if (entityKeyFromCacheKey(key) === entityKey)
|
|
86
|
+
this.store.delete(key);
|
|
87
|
+
}
|
|
88
|
+
for (const key of [...this.inFlight.keys()]) {
|
|
89
|
+
if (entityKeyFromCacheKey(key) === entityKey)
|
|
90
|
+
this.inFlight.delete(key);
|
|
91
|
+
}
|
|
92
|
+
const prefix = `${entityKey}${LABEL_SEP}`;
|
|
93
|
+
for (const key of [...this.labels.keys()]) {
|
|
94
|
+
if (key.startsWith(prefix))
|
|
95
|
+
this.labels.delete(key);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
clear() {
|
|
99
|
+
this.store.clear();
|
|
100
|
+
this.inFlight.clear();
|
|
101
|
+
this.labels.clear();
|
|
102
|
+
}
|
|
103
|
+
rememberLabels(entityKey, lang, options) {
|
|
104
|
+
for (const option of options) {
|
|
105
|
+
this.labels.set(this.labelKey(entityKey, lang, option.value), option.label);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** Label-cache key. An object value collapses to its resolved text for the active language. */
|
|
109
|
+
labelKey(entityKey, lang, value) {
|
|
110
|
+
const canonical = value !== null && typeof value === 'object'
|
|
111
|
+
? resolveLabel(value, lang)
|
|
112
|
+
: String(value ?? '');
|
|
113
|
+
return [entityKey, lang, canonical].join(LABEL_SEP);
|
|
114
|
+
}
|
|
115
|
+
// ─── Loader result coercion (array | Promise | Observable) ──────────────────
|
|
116
|
+
invoke(loader, ctx) {
|
|
117
|
+
let result;
|
|
118
|
+
try {
|
|
119
|
+
result = loader(ctx);
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
// Reject rather than resolve empty: `load` must be able to tell failure from
|
|
123
|
+
// "no matches", or it would cache the failure.
|
|
124
|
+
return Promise.reject(err);
|
|
125
|
+
}
|
|
126
|
+
return this.toPromise(result);
|
|
127
|
+
}
|
|
128
|
+
toPromise(result) {
|
|
129
|
+
if (Array.isArray(result))
|
|
130
|
+
return Promise.resolve(result);
|
|
131
|
+
if (this.isSubscribable(result)) {
|
|
132
|
+
// First emission wins, so an Observable that stays open still resolves the load.
|
|
133
|
+
return new Promise((resolve, reject) => {
|
|
134
|
+
let settled = false;
|
|
135
|
+
const settle = (fn) => {
|
|
136
|
+
if (settled)
|
|
137
|
+
return;
|
|
138
|
+
settled = true;
|
|
139
|
+
fn();
|
|
140
|
+
};
|
|
141
|
+
result.subscribe({
|
|
142
|
+
next: value => settle(() => resolve(value ?? [])),
|
|
143
|
+
// An erroring stream is a failed load, not an empty one — see `invoke`.
|
|
144
|
+
error: err => settle(() => reject(err)),
|
|
145
|
+
// Completing without emitting is a legitimate empty result, and is cached.
|
|
146
|
+
complete: () => settle(() => resolve([])),
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
return Promise.resolve(result);
|
|
151
|
+
}
|
|
152
|
+
isSubscribable(value) {
|
|
153
|
+
return (typeof value === 'object' &&
|
|
154
|
+
value !== null &&
|
|
155
|
+
typeof value.subscribe === 'function');
|
|
156
|
+
}
|
|
157
|
+
/** Exposed for cascade filtering, which matches a parent value against option values. */
|
|
158
|
+
matches(a, b, lang = 'en') {
|
|
159
|
+
return valuesMatch(a, b, lang);
|
|
160
|
+
}
|
|
161
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EntityReferenceService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
162
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EntityReferenceService, providedIn: 'root' });
|
|
163
|
+
}
|
|
164
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EntityReferenceService, decorators: [{
|
|
165
|
+
type: Injectable,
|
|
166
|
+
args: [{ providedIn: 'root' }]
|
|
167
|
+
}] });
|
|
168
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW50aXR5LXJlZmVyZW5jZS5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9zZXJ2aWNlcy9lbnRpdHktcmVmZXJlbmNlLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFTbkQsT0FBTyxFQUNMLHNCQUFzQixFQUN0QixxQkFBcUIsRUFDckIseUJBQXlCLEVBQ3pCLFlBQVksRUFDWixXQUFXLEdBQ1osTUFBTSxzQkFBc0IsQ0FBQztBQUM5QixPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUNwRSxPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQzs7QUFFekUsZ0dBQWdHO0FBQ2hHLE1BQU0sU0FBUyxHQUFHLEdBQUcsQ0FBQztBQUV0Qjs7Ozs7Ozs7Ozs7OztHQWFHO0FBRUgsTUFBTSxPQUFPLHNCQUFzQjtJQUNoQixRQUFRLEdBQUcsTUFBTSxDQUFDLHdCQUF3QixDQUFDLENBQUM7SUFDNUMsS0FBSyxHQUFHLE1BQU0sQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO0lBRXhELGtFQUFrRTtJQUNqRCxRQUFRLEdBQUcsSUFBSSxHQUFHLEVBQXNDLENBQUM7SUFDMUUsdURBQXVEO0lBQ3RDLE1BQU0sR0FBRyxJQUFJLEdBQUcsRUFBa0IsQ0FBQztJQUVwRDs7O09BR0c7SUFDSCxLQUFLLENBQUMsSUFBSSxDQUFDLFNBQWlCLEVBQUUsTUFBOEIsRUFBRTtRQUM1RCxNQUFNLEdBQUcsR0FBRyxzQkFBc0IsQ0FBQyxTQUFTLEVBQUU7WUFDNUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxJQUFJO1lBQ2QsT0FBTyxFQUFFLEdBQUcsQ0FBQyxPQUFPO1lBQ3BCLGFBQWEsRUFBRSxHQUFHLENBQUMsYUFBYTtZQUNoQyxXQUFXLEVBQUUsR0FBRyxDQUFDLFdBQVc7U0FDN0Is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QUFFLENBQUMsT0FBTyxDQUFDLEtBQUssSUFBSSxFQUFFLENBQUMsQ0FBQztvQkFDakQsd0VBQXdFO29CQUN4RSxLQUFLLEVBQUUsR0FBRyxDQUFDLEVBQUUsQ0FBQyxNQUFNLENBQUMsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO29CQUN2QywyRUFBMkU7b0JBQzNFLFFBQVEsRUFBRSxHQUFHLEVBQUUsQ0FBQyxNQUFNLENBQUMsR0FBRyxFQUFFLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFDO2lCQUMxQyxDQUFDLENBQUM7WUFDTCxDQUFDLENBQUMsQ0FBQztRQUNMLENBQUM7UUFDRCxPQUFPLE9BQU8sQ0FBQyxPQUFPLENBQUMsTUFBb0MsQ0FBQyxDQUFDO0lBQy9ELENBQUM7SUFFTyxjQUFjLENBQUMsS0FBYztRQUNuQyxPQUFPLENBQ0wsT0FBTyxLQUFLLEtBQUssUUFBUTtZQUN6QixLQUFLLEtBQUssSUFBSTtZQUNkLE9BQVEsS0FBeUMsQ0FBQyxTQUFTLEtBQUssVUFBVSxDQUMzRSxDQUFDO0lBQ0osQ0FBQztJQUVELHlGQUF5RjtJQUN6RixPQUFPLENBQUMsQ0FBVSxFQUFFLENBQVUsRUFBRSxJQUFJLEdBQUcsSUFBSTtRQUN6QyxPQUFPLFdBQVcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ2pDLENBQUM7d0dBaEtVLHNCQUFzQjs0R0FBdEIsc0JBQXNCLGNBRFQsTUFBTTs7NEZBQ25CLHNCQUFzQjtrQkFEbEMsVUFBVTttQkFBQyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlLCBpbmplY3QgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB0eXBlIHtcbiAgRW50aXR5UmVmZXJlbmNlTG9hZGVyLFxuICBSYXdSZWZlcmVuY2VJdGVtLFxuICBSZWZlcmVuY2VMb2FkZXJDb250ZXh0LFxuICBSZWZlcmVuY2VMb2FkZXJSZXN1bHQsXG4gIFJlZmVyZW5jZU9wdGlvbixcbiAgU3Vic2NyaWJhYmxlLFxufSBmcm9tICdAZHluYW1pYy1lbnRpdHkvY29yZSc7XG5pbXBvcnQge1xuICBidWlsZFJlZmVyZW5jZUNhY2hlS2V5LFxuICBlbnRpdHlLZXlGcm9tQ2FjaGVLZXksXG4gIG5vcm1hbGl6ZVJlZmVyZW5jZU9wdGlvbnMsXG4gIHJlc29sdmVMYWJlbCxcbiAgdmFsdWVzTWF0Y2gsXG59IGZyb20gJ0BkeW5hbWljLWVudGl0eS9jb3JlJztcbmltcG9ydCB7IEVOVElUWV9SRUZfQ0FDSEVfU1RPUkUgfSBmcm9tICcuLi90b2tlbnMvaW5qZWN0aW9uLXRva2Vucyc7XG5pbXBvcnQgeyBFbnRpdHlSZWZSZWdpc3RyeVNlcnZpY2UgfSBmcm9tICcuL2VudGl0eS1yZWYtcmVnaXN0cnkuc2VydmljZSc7XG5cbi8qKiBMYWJlbC1jYWNoZSBmaWVsZCBzZXBhcmF0b3IuIEtlcHQgb3V0IG9mIHRlbXBsYXRlIGxpdGVyYWxzIHNvIGl0IHN0YXlzIHZpc2libGUgaW4gc291cmNlLiAqL1xuY29uc3QgTEFCRUxfU0VQID0gJ3wnO1xuXG4vKipcbiAqIEVudGl0eVJlZmVyZW5jZVNlcnZpY2Ug4oCUIHRoZSBjYWNoZSBpbiBmcm9udCBvZiBjb25zdW1lci1yZWdpc3RlcmVkIG9wdGlvbiBsb2FkZXJzLlxuICpcbiAqIFRocmVlIGxheWVycywgbWF0Y2hpbmcgdGhlIHJlZmVyZW5jZSBpbXBsZW1lbnRhdGlvbjpcbiAqICAxLiAqKm9wdGlvbnMqKiDigJQgcmVzb2x2ZWQgYFJlZmVyZW5jZU9wdGlvbltdYCBwZXIgKGVudGl0eSwgbGFuZywgZGlzcGxheUZpZWxkcywgZmlsdGVycykuXG4gKiAgMi4gKippbi1mbGlnaHQqKiDigJQgdGhlIHBlbmRpbmcgUHJvbWlzZSwgc28gTiBmaWVsZHMgcmVmZXJlbmNpbmcgb25lIGVudGl0eSB0cmlnZ2VyIG9uZVxuICogICAgIGxvYWQgcmF0aGVyIHRoYW4gTi4gVGhpcyBpcyB0aGUgbGF5ZXIgdGhhdCBhY3R1YWxseSByZW1vdmVzIGR1cGxpY2F0ZSBIVFRQIGNhbGxzO1xuICogICAgIGFuIG9wdGlvbnMgY2FjaGUgYWxvbmUgZG9lcyBub3RoaW5nIGZvciBjb25jdXJyZW50IGZpcnN0IGxvYWRzLlxuICogIDMuICoqbGFiZWxzKiog4oCUIHN5bmNocm9ub3VzIHZhbHVlIOKGkiB0ZXh0LCBmb3IgcmVuZGVyaW5nIGEgc3RvcmVkIHJlZmVyZW5jZSB3aXRob3V0XG4gKiAgICAgd2FpdGluZyBvbiBhIGxvYWQuXG4gKlxuICogSW52YWxpZGF0aW9uIGlzIHBlciBlbnRpdHksIG5vdCBnbG9iYWwuIFRoZSByZWZlcmVuY2UgY291bGQgb25seSBgY2xlYXJDYWNoZSgpYCBldmVyeXRoaW5nXG4gKiAoaXNzdWUgIzE2KSwgc28gc2F2aW5nIG9uZSByZWNvcmQgcmUtZmV0Y2hlZCBldmVyeSBlbnRpdHkncyBvcHRpb25zLlxuICovXG5ASW5qZWN0YWJsZSh7IHByb3ZpZGVkSW46ICdyb290JyB9KVxuZXhwb3J0IGNsYXNzIEVudGl0eVJlZmVyZW5jZVNlcnZpY2Uge1xuICBwcml2YXRlIHJlYWRvbmx5IHJlZ2lzdHJ5ID0gaW5qZWN0KEVudGl0eVJlZlJlZ2lzdHJ5U2VydmljZSk7XG4gIHByaXZhdGUgcmVhZG9ubHkgc3RvcmUgPSBpbmplY3QoRU5USVRZX1JFRl9DQUNIRV9TVE9SRSk7XG5cbiAgLyoqIExvYWRzIGluIHByb2dyZXNzLCBrZXllZCB0aGUgc2FtZSB3YXkgYXMgdGhlIG9wdGlvbnMgY2FjaGUuICovXG4gIHByaXZhdGUgcmVhZG9ubHkgaW5GbGlnaHQgPSBuZXcgTWFwPHN0cmluZywgUHJvbWlzZTxSZWZlcmVuY2VPcHRpb25bXT4+KCk7XG4gIC8qKiBgZW50aXR5S2V5fGxhbmd8Y2Fub25pY2FsVmFsdWVgIC0+IGRpc3BsYXkgdGV4dC4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSBsYWJlbHMgPSBuZXcgTWFwPHN0cmluZywgc3RyaW5nPigpO1xuXG4gIC8qKlxuICAgKiBPcHRpb25zIGZvciBhbiBlbnRpdHksIGZyb20gY2FjaGUgd2hlbiBwcmVzZW50LlxuICAgKiBDb25jdXJyZW50IGNhbGxlcnMgd2l0aCB0aGUgc2FtZSBrZXkgc2hhcmUgb25lIGxvYWQuXG4gICAqL1xuICBhc3luYyBsb2FkKGVudGl0eUtleTogc3RyaW5nLCBjdHg6IFJlZmVyZW5jZUxvYWRlckNvbnRleHQgPSB7fSk6IFByb21pc2U8UmVmZXJlbmNlT3B0aW9uW10+IHtcbiAgICBjb25zdCBrZXkgPSBidWlsZFJlZmVyZW5jZUNhY2hlS2V5KGVudGl0eUtleSwge1xuICAgICAgbGFuZzogY3R4LmxhbmcsXG4gICAgICBmaWx0ZXJzOiBjdHguZmlsdGVycyxcbiAgICAgIGRpc3BsYXlGaWVsZHM6IGN0eC5kaXNwbGF5RmllbGRzLFxuICAgICAgcGFyZW50VmFsdWU6IGN0eC5wYXJlbnRWYWx1ZSxcbiAgICB9KTtcblxuICAgIGNvbnN0IGNhY2hlZCA9IHRoaXMuc3RvcmUuZ2V0KGtleSk7XG4gICAgaWYgKGNhY2hlZCkgcmV0dXJuIGNhY2hlZDtcblxuICAgIGNvbnN0IHBlbmRpbmcgPSB0aGlzLmluRmxpZ2h0LmdldChrZXkpO1xuICAgIGlmIChwZW5kaW5nKSByZXR1cm4gcGVuZGluZztcblxuICAgIGNvbnN0IGxvYWRlciA9IHRoaXMucmVnaXN0cnkucmVzb2x2ZShlbnRpdHlLZXkpO1xuICAgIGlmICghbG9hZGVyKSByZXR1cm4gW107XG5cbiAgICBjb25zdCBsb2FkID0gdGhpcy5pbnZva2UobG9hZGVyLCBjdHgpXG4gICAgICAudGhlbihyYXcgPT4ge1xuICAgICAgICAvLyBkaXNwbGF5RmllbGRzIGRyaXZlIGxhYmVsIGJ1aWxkaW5nLCB3aGljaCBpcyB3aHkgdGhleSBhcmUgcGFydCBvZiB0aGUgY2FjaGUga2V5LlxuICAgICAgICBjb25zdCBvcHRpb25zID0gbm9ybWFsaXplUmVmZXJlbmNlT3B0aW9ucyhcbiAgICAgICAgICByYXcgYXMgUmF3UmVmZXJlbmNlSXRlbVtdLFxuICAgICAgICAgIGN0eC5kaXNwbGF5RmllbGRzID8geyBlbmFibGVkOiB0cnVlLCBkaXNwbGF5RmllbGRzOiBjdHguZGlzcGxheUZpZWxkcyB9IDogdW5kZWZpbmVkLFxuICAgICAgICAgIGN0eC5sYW5nID8/ICdlbicsXG4gICAgICAgICk7XG4gICAgICAgIHRoaXMuc3RvcmUuc2V0KGtleSwgb3B0aW9ucyk7XG4gICAgICAgIHRoaXMucmVtZW1iZXJMYWJlbHMoZW50aXR5S2V5LCBjdHgubGFuZyA/PyAnZW4nLCBvcHRpb25zKTtcbiAgICAgICAgcmV0dXJuIG9wdGlvbnM7XG4gICAgICB9KTtcblxuICAgIC8vIEEgZmFpbGVkIGxvYWQgcmVzb2x2ZXMgdG8gW10gZm9yIHRoZSBjYWxsZXIgYnV0IGlzIG5ldmVyIHdyaXR0ZW4gdG8gdGhlIHN0b3JlLCBzbyBhXG4gICAgLy8gdHJhbnNpZW50IGVycm9yIGlzIHJldHJpZWQgb24gdGhlIG5leHQgY2FsbCBpbnN0ZWFkIG9mIHBvaXNvbmluZyB0aGUgY2FjaGUuIE9ubHkgdGhlXG4gICAgLy8gYC50aGVuYCBhYm92ZSBjYWNoZXMsIGFuZCBhIHJlamVjdGlvbiBza2lwcyBpdC5cbiAgICBjb25zdCBndWFyZGVkID0gbG9hZFxuICAgICAgLmNhdGNoKCgpID0+IFtdIGFzIFJlZmVyZW5jZU9wdGlvbltdKVxuICAgICAgLmZpbmFsbHkoKCkgPT4gdGhpcy5pbkZsaWdodC5kZWxldGUoa2V5KSk7XG5cbiAgICB0aGlzLmluRmxpZ2h0LnNldChrZXksIGd1YXJkZWQpO1xuICAgIHJldHVybiBndWFyZGVkO1xuICB9XG5cbiAgLyoqIENhY2hlZCBvcHRpb25zIHdpdGhvdXQgdHJpZ2dlcmluZyBhIGxvYWQuIGB1bmRlZmluZWRgIHdoZW4gbm90IGNhY2hlZC4gKi9cbiAgcGVlayhlbnRpdHlLZXk6IHN0cmluZywgY3R4OiBSZWZlcmVuY2VMb2FkZXJDb250ZXh0ID0ge30pOiBSZWZlcmVuY2VPcHRpb25bXSB8IHVuZGVmaW5lZCB7XG4gICAgcmV0dXJuIHRoaXMuc3RvcmUuZ2V0KFxuICAgICAgYnVpbGRSZWZlcmVuY2VDYWNoZUtleShlbnRpdHlLZXksIHtcbiAgICAgICAgbGFuZzogY3R4LmxhbmcsXG4gICAgICAgIGZpbHRlcnM6IGN0eC5maWx0ZXJzLFxuICAgICAgICBkaXNwbGF5RmllbGRzOiBjdHguZGlzcGxheUZpZWxkcyxcbiAgICAgICAgcGFyZW50VmFsdWU6IGN0eC5wYXJlbnRWYWx1ZSxcbiAgICAgIH0pLFxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogRGlzcGxheSB0ZXh0IGZvciBhIHN0b3JlZCByZWZlcmVuY2UgdmFsdWUsIHN5bmNocm9ub3VzbHkuXG4gICAqIFJldHVybnMgYHVuZGVmaW5lZGAgd2hlbiB0aGUgZW50aXR5J3Mgb3B0aW9ucyBoYXZlIG5ldmVyIGJlZW4gbG9hZGVkLlxuICAgKi9cbiAgbGFiZWxGb3IoZW50aXR5S2V5OiBzdHJpbmcsIHZhbHVlOiB1bmtub3duLCBsYW5nID0gJ2VuJyk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gICAgcmV0dXJuIHRoaXMubGFiZWxzLmdldCh0aGlzLmxhYmVsS2V5KGVudGl0eUtleSwgbGFuZywgdmFsdWUpKTtcbiAgfVxuXG4gIC8qKiBEcm9wIGV2ZXJ5dGhpbmcgY2FjaGVkIGZvciBvbmUgZW50aXR5LCBsZWF2aW5nIG90aGVyIGVudGl0aWVzIGludGFjdC4gKi9cbiAgaW52YWxpZGF0ZShlbnRpdHlLZXk6IHN0cmluZyk6IHZvaWQge1xuICAgIGZvciAoY29uc3Qga2V5IG9mIHRoaXMuc3RvcmUua2V5cygpKSB7XG4gICAgICBpZiAoZW50aXR5S2V5RnJvbUNhY2hlS2V5KGtleSkgPT09IGVudGl0eUtleSkgdGhpcy5zdG9yZS5kZWxldGUoa2V5KTtcbiAgICB9XG4gICAgZm9yIChjb25zdCBrZXkgb2YgWy4uLnRoaXMuaW5GbGlnaHQua2V5cygpXSkge1xuICAgICAgaWYgKGVudGl0eUtleUZyb21DYWNoZUtleShrZXkpID09PSBlbnRpdHlLZXkpIHRoaXMuaW5GbGlnaHQuZGVsZXRlKGtleSk7XG4gICAgfVxuICAgIGNvbnN0IHByZWZpeCA9IGAke2VudGl0eUtleX0ke0xBQkVMX1NFUH1gO1xuICAgIGZvciAoY29uc3Qga2V5IG9mIFsuLi50aGlzLmxhYmVscy5rZXlzKCldKSB7XG4gICAgICBpZiAoa2V5LnN0YXJ0c1dpdGgocHJlZml4KSkgdGhpcy5sYWJlbHMuZGVsZXRlKGtleSk7XG4gICAgfVxuICB9XG5cbiAgY2xlYXIoKTogdm9pZCB7XG4gICAgdGhpcy5zdG9yZS5jbGVhcigpO1xuICAgIHRoaXMuaW5GbGlnaHQuY2xlYXIoKTtcbiAgICB0aGlzLmxhYmVscy5jbGVhcigpO1xuICB9XG5cbiAgcHJpdmF0ZSByZW1lbWJlckxhYmVscyhlbnRpdHlLZXk6IHN0cmluZywgbGFuZzogc3RyaW5nLCBvcHRpb25zOiBSZWZlcmVuY2VPcHRpb25bXSk6IHZvaWQge1xuICAgIGZvciAoY29uc3Qgb3B0aW9uIG9mIG9wdGlvbnMpIHtcbiAgICAgIHRoaXMubGFiZWxzLnNldCh0aGlzLmxhYmVsS2V5KGVudGl0eUtleSwgbGFuZywgb3B0aW9uLnZhbHVlKSwgb3B0aW9uLmxhYmVsKTtcbiAgICB9XG4gIH1cblxuICAvKiogTGFiZWwtY2FjaGUga2V5LiBBbiBvYmplY3QgdmFsdWUgY29sbGFwc2VzIHRvIGl0cyByZXNvbHZlZCB0ZXh0IGZvciB0aGUgYWN0aXZlIGxhbmd1YWdlLiAqL1xuICBwcml2YXRlIGxhYmVsS2V5KGVudGl0eUtleTogc3RyaW5nLCBsYW5nOiBzdHJpbmcsIHZhbHVlOiB1bmtub3duKTogc3RyaW5nIHtcbiAgICBjb25zdCBjYW5vbmljYWwgPVxuICAgICAgdmFsdWUgIT09IG51bGwgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0J1xuICAgICAgICA/IHJlc29sdmVMYWJlbCh2YWx1ZSBhcyBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+LCBsYW5nKVxuICAgICAgICA6IFN0cmluZyh2YWx1ZSA/PyAnJyk7XG4gICAgcmV0dXJuIFtlbnRpdHlLZXksIGxhbmcsIGNhbm9uaWNhbF0uam9pbihMQUJFTF9TRVApO1xuICB9XG5cbiAgLy8g4pSA4pSA4pSAIExvYWRlciByZXN1bHQgY29lcmNpb24gKGFycmF5IHwgUHJvbWlzZSB8IE9ic2VydmFibGUpIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIHByaXZhdGUgaW52b2tlKFxuICAgIGxvYWRlcjogRW50aXR5UmVmZXJlbmNlTG9hZGVyLFxuICAgIGN0eDogUmVmZXJlbmNlTG9hZGVyQ29udGV4dCxcbiAgKTogUHJvbWlzZTxSZWZlcmVuY2VPcHRpb25bXT4ge1xuICAgIGxldCByZXN1bHQ6IFJlZmVyZW5jZUxvYWRlclJlc3VsdDtcbiAgICB0cnkge1xuICAgICAgcmVzdWx0ID0gbG9hZGVyKGN0eCk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAvLyBSZWplY3QgcmF0aGVyIHRoYW4gcmVzb2x2ZSBlbXB0eTogYGxvYWRgIG11c3QgYmUgYWJsZSB0byB0ZWxsIGZhaWx1cmUgZnJvbVxuICAgICAgLy8gXCJubyBtYXRjaGVzXCIsIG9yIGl0IHdvdWxkIGNhY2hlIHRoZSBmYWlsdXJlLlxuICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KGVycik7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLnRvUHJvbWlzZShyZXN1bHQpO1xuICB9XG5cbiAgcHJpdmF0ZSB0b1Byb21pc2UocmVzdWx0OiBSZWZlcmVuY2VMb2FkZXJSZXN1bHQpOiBQcm9taXNlPFJlZmVyZW5jZU9wdGlvbltdPiB7XG4gICAgaWYgKEFycmF5LmlzQXJyYXkocmVzdWx0KSkgcmV0dXJuIFByb21pc2UucmVzb2x2ZShyZXN1bHQpO1xuICAgIGlmICh0aGlzLmlzU3Vic2NyaWJhYmxlKHJlc3VsdCkpIHtcbiAgICAgIC8vIEZpcnN0IGVtaXNzaW9uIHdpbnMsIHNvIGFuIE9ic2VydmFibGUgdGhhdCBzdGF5cyBvcGVuIHN0aWxsIHJlc29sdmVzIHRoZSBsb2FkLlxuICAgICAgcmV0dXJuIG5ldyBQcm9taXNlPFJlZmVyZW5jZU9wdGlvbltdPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgIGxldCBzZXR0bGVkID0gZmFsc2U7XG4gICAgICAgIGNvbnN0IHNldHRsZSA9IChmbjogKCkgPT4gdm9pZCkgPT4ge1xuICAgICAgICAgIGlmIChzZXR0bGVkKSByZXR1cm47XG4gICAgICAgICAgc2V0dGxlZCA9IHRydWU7XG4gICAgICAgICAgZm4oKTtcbiAgICAgICAgfTtcbiAgICAgICAgcmVzdWx0LnN1YnNjcmliZSh7XG4gICAgICAgICAgbmV4dDogdmFsdWUgPT4gc2V0dGxlKCgpID0+IHJlc29sdmUodmFsdWUgPz8gW10pKSxcbiAgICAgICAgICAvLyBBbiBlcnJvcmluZyBzdHJlYW0gaXMgYSBmYWlsZWQgbG9hZCwgbm90IGFuIGVtcHR5IG9uZSDigJQgc2VlIGBpbnZva2VgLlxuICAgICAgICAgIGVycm9yOiBlcnIgPT4gc2V0dGxlKCgpID0+IHJlamVjdChlcnIpKSxcbiAgICAgICAgICAvLyBDb21wbGV0aW5nIHdpdGhvdXQgZW1pdHRpbmcgaXMgYSBsZWdpdGltYXRlIGVtcHR5IHJlc3VsdCwgYW5kIGlzIGNhY2hlZC5cbiAgICAgICAgICBjb21wbGV0ZTogKCkgPT4gc2V0dGxlKCgpID0+IHJlc29sdmUoW10pKSxcbiAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICB9XG4gICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShyZXN1bHQgYXMgUHJvbWlzZTxSZWZlcmVuY2VPcHRpb25bXT4pO1xuICB9XG5cbiAgcHJpdmF0ZSBpc1N1YnNjcmliYWJsZSh2YWx1ZTogdW5rbm93bik6IHZhbHVlIGlzIFN1YnNjcmliYWJsZTxSZWZlcmVuY2VPcHRpb25bXT4ge1xuICAgIHJldHVybiAoXG4gICAgICB0eXBlb2YgdmFsdWUgPT09ICdvYmplY3QnICYmXG4gICAgICB2YWx1ZSAhPT0gbnVsbCAmJlxuICAgICAgdHlwZW9mICh2YWx1ZSBhcyBTdWJzY3JpYmFibGU8UmVmZXJlbmNlT3B0aW9uW10+KS5zdWJzY3JpYmUgPT09ICdmdW5jdGlvbidcbiAgICApO1xuICB9XG5cbiAgLyoqIEV4cG9zZWQgZm9yIGNhc2NhZGUgZmlsdGVyaW5nLCB3aGljaCBtYXRjaGVzIGEgcGFyZW50IHZhbHVlIGFnYWluc3Qgb3B0aW9uIHZhbHVlcy4gKi9cbiAgbWF0Y2hlcyhhOiB1bmtub3duLCBiOiB1bmtub3duLCBsYW5nID0gJ2VuJyk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB2YWx1ZXNNYXRjaChhLCBiLCBsYW5nKTtcbiAgfVxufVxuIl19
|
|
@@ -1,44 +1,49 @@
|
|
|
1
1
|
import { Injectable, inject } from '@angular/core';
|
|
2
|
-
import { FIELD_TYPE_REGISTRY } from '../tokens/injection-tokens';
|
|
3
|
-
// Built-in field components — pre-registered in this service
|
|
4
|
-
import { TextFieldComponent } from '../field-types/text-field.component';
|
|
5
|
-
import { TextareaFieldComponent } from '../field-types/textarea-field.component';
|
|
6
|
-
import { NumberFieldComponent } from '../field-types/number-field.component';
|
|
7
|
-
import { CheckboxFieldComponent } from '../field-types/checkbox-field.component';
|
|
8
|
-
import { DateFieldComponent } from '../field-types/date-field.component';
|
|
9
|
-
import { DropdownFieldComponent } from '../field-types/dropdown-field.component';
|
|
10
|
-
import { MultiSelectFieldComponent } from '../field-types/multi-select-field.component';
|
|
11
|
-
import { EntityRefFieldComponent } from '../field-types/entity-ref-field.component';
|
|
2
|
+
import { FIELD_TYPE_REGISTRY, FIELD_TYPE_SETS } from '../tokens/injection-tokens';
|
|
12
3
|
import * as i0 from "@angular/core";
|
|
13
4
|
/**
|
|
14
5
|
* FieldRegistryService — resolves field type strings to Angular component classes.
|
|
15
|
-
*
|
|
16
|
-
*
|
|
6
|
+
*
|
|
7
|
+
* **Tree-shaking contract:** this service holds *no* static imports of field components.
|
|
8
|
+
* Nothing is bundled until something registers it, which is what makes the built-ins
|
|
9
|
+
* opt-in. Register them with `provideBuiltInFieldTypes()` (all 19 keys) or a narrower
|
|
10
|
+
* subset via `provideFieldTypes({ ... })` — see `providers/provide-field-types.ts`.
|
|
11
|
+
*
|
|
12
|
+
* Resolution order: consumer token registry → runtime registrations → nothing.
|
|
17
13
|
*/
|
|
18
14
|
export class FieldRegistryService {
|
|
19
15
|
consumerRegistry = inject(FIELD_TYPE_REGISTRY, { optional: true }) ?? new Map();
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
16
|
+
dynamicRegistry = new Map();
|
|
17
|
+
constructor() {
|
|
18
|
+
// Sets contributed by provideFieldTypes()/provideBuiltInFieldTypes(), in declaration order.
|
|
19
|
+
for (const set of inject(FIELD_TYPE_SETS, { optional: true }) ?? []) {
|
|
20
|
+
this.registerAll(set);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** Register a field type component mapping at runtime. */
|
|
24
|
+
register(type, component) {
|
|
25
|
+
this.dynamicRegistry.set(type, component);
|
|
26
|
+
}
|
|
27
|
+
/** Register multiple field type component mappings at runtime. */
|
|
28
|
+
registerAll(map) {
|
|
29
|
+
for (const [type, comp] of Object.entries(map)) {
|
|
30
|
+
this.dynamicRegistry.set(type, comp);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
31
33
|
/**
|
|
32
34
|
* Resolve a field type string to a component class.
|
|
33
|
-
* Consumer registry takes
|
|
34
|
-
* Returns null if type is unknown — caller must handle gracefully.
|
|
35
|
+
* Consumer token registry takes priority over runtime registrations.
|
|
35
36
|
*/
|
|
36
37
|
resolve(fieldType) {
|
|
37
|
-
return this.consumerRegistry.get(fieldType) ?? this.
|
|
38
|
+
return this.consumerRegistry.get(fieldType) ?? this.dynamicRegistry.get(fieldType) ?? null;
|
|
38
39
|
}
|
|
39
|
-
/** Check if a field type has a registered component */
|
|
40
|
+
/** Check if a field type has a registered component. */
|
|
40
41
|
has(fieldType) {
|
|
41
|
-
return this.consumerRegistry.has(fieldType) || this.
|
|
42
|
+
return this.consumerRegistry.has(fieldType) || this.dynamicRegistry.has(fieldType);
|
|
43
|
+
}
|
|
44
|
+
/** Every registered field type key. Consumer overrides and runtime registrations combined. */
|
|
45
|
+
registeredTypes() {
|
|
46
|
+
return [...new Set([...this.dynamicRegistry.keys(), ...this.consumerRegistry.keys()])].sort();
|
|
42
47
|
}
|
|
43
48
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FieldRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
44
49
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FieldRegistryService, providedIn: 'root' });
|
|
@@ -46,5 +51,5 @@ export class FieldRegistryService {
|
|
|
46
51
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FieldRegistryService, decorators: [{
|
|
47
52
|
type: Injectable,
|
|
48
53
|
args: [{ providedIn: 'root' }]
|
|
49
|
-
}] });
|
|
50
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
54
|
+
}], ctorParameters: () => [] });
|
|
55
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmllbGQtcmVnaXN0cnkuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvc2VydmljZXMvZmllbGQtcmVnaXN0cnkuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFRLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN6RCxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsZUFBZSxFQUFFLE1BQU0sNEJBQTRCLENBQUM7O0FBRWxGOzs7Ozs7Ozs7R0FTRztBQUVILE1BQU0sT0FBTyxvQkFBb0I7SUFDZCxnQkFBZ0IsR0FDL0IsTUFBTSxDQUFDLG1CQUFtQixFQUFFLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxDQUFDLElBQUksSUFBSSxHQUFHLEVBQXFCLENBQUM7SUFDakUsZUFBZSxHQUFHLElBQUksR0FBRyxFQUFxQixDQUFDO0lBRWhFO1FBQ0UsNEZBQTRGO1FBQzVGLEtBQUssTUFBTSxHQUFHLElBQUksTUFBTSxDQUFDLGVBQWUsRUFBRSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDO1lBQ3BFLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDeEIsQ0FBQztJQUNILENBQUM7SUFFRCwwREFBMEQ7SUFDMUQsUUFBUSxDQUFDLElBQVksRUFBRSxTQUFvQjtRQUN6QyxJQUFJLENBQUMsZUFBZSxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsU0FBUyxDQUFDLENBQUM7SUFDNUMsQ0FBQztJQUVELGtFQUFrRTtJQUNsRSxXQUFXLENBQUMsR0FBOEI7UUFDeEMsS0FBSyxNQUFNLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUMvQyxJQUFJLENBQUMsZUFBZSxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDdkMsQ0FBQztJQUNILENBQUM7SUFFRDs7O09BR0c7SUFDSCxPQUFPLENBQUMsU0FBaUI7UUFDdkIsT0FBTyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxJQUFJLElBQUksQ0FBQyxlQUFlLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxJQUFJLElBQUksQ0FBQztJQUM3RixDQUFDO0lBRUQsd0RBQXdEO0lBQ3hELEdBQUcsQ0FBQyxTQUFpQjtRQUNuQixPQUFPLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLElBQUksSUFBSSxDQUFDLGVBQWUsQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDckYsQ0FBQztJQUVELDhGQUE4RjtJQUM5RixlQUFlO1FBQ2IsT0FBTyxDQUFDLEdBQUcsSUFBSSxHQUFHLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxlQUFlLENBQUMsSUFBSSxFQUFFLEVBQUUsR0FBRyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDaEcsQ0FBQzt3R0F4Q1Usb0JBQW9COzRHQUFwQixvQkFBb0IsY0FEUCxNQUFNOzs0RkFDbkIsb0JBQW9CO2tCQURoQyxVQUFVO21CQUFDLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUsIFR5cGUsIGluamVjdCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgRklFTERfVFlQRV9SRUdJU1RSWSwgRklFTERfVFlQRV9TRVRTIH0gZnJvbSAnLi4vdG9rZW5zL2luamVjdGlvbi10b2tlbnMnO1xuXG4vKipcbiAqIEZpZWxkUmVnaXN0cnlTZXJ2aWNlIOKAlCByZXNvbHZlcyBmaWVsZCB0eXBlIHN0cmluZ3MgdG8gQW5ndWxhciBjb21wb25lbnQgY2xhc3Nlcy5cbiAqXG4gKiAqKlRyZWUtc2hha2luZyBjb250cmFjdDoqKiB0aGlzIHNlcnZpY2UgaG9sZHMgKm5vKiBzdGF0aWMgaW1wb3J0cyBvZiBmaWVsZCBjb21wb25lbnRzLlxuICogTm90aGluZyBpcyBidW5kbGVkIHVudGlsIHNvbWV0aGluZyByZWdpc3RlcnMgaXQsIHdoaWNoIGlzIHdoYXQgbWFrZXMgdGhlIGJ1aWx0LWluc1xuICogb3B0LWluLiBSZWdpc3RlciB0aGVtIHdpdGggYHByb3ZpZGVCdWlsdEluRmllbGRUeXBlcygpYCAoYWxsIDE5IGtleXMpIG9yIGEgbmFycm93ZXJcbiAqIHN1YnNldCB2aWEgYHByb3ZpZGVGaWVsZFR5cGVzKHsgLi4uIH0pYCDigJQgc2VlIGBwcm92aWRlcnMvcHJvdmlkZS1maWVsZC10eXBlcy50c2AuXG4gKlxuICogUmVzb2x1dGlvbiBvcmRlcjogY29uc3VtZXIgdG9rZW4gcmVnaXN0cnkg4oaSIHJ1bnRpbWUgcmVnaXN0cmF0aW9ucyDihpIgbm90aGluZy5cbiAqL1xuQEluamVjdGFibGUoeyBwcm92aWRlZEluOiAncm9vdCcgfSlcbmV4cG9ydCBjbGFzcyBGaWVsZFJlZ2lzdHJ5U2VydmljZSB7XG4gIHByaXZhdGUgcmVhZG9ubHkgY29uc3VtZXJSZWdpc3RyeSA9XG4gICAgaW5qZWN0KEZJRUxEX1RZUEVfUkVHSVNUUlksIHsgb3B0aW9uYWw6IHRydWUgfSkgPz8gbmV3IE1hcDxzdHJpbmcsIFR5cGU8YW55Pj4oKTtcbiAgcHJpdmF0ZSByZWFkb25seSBkeW5hbWljUmVnaXN0cnkgPSBuZXcgTWFwPHN0cmluZywgVHlwZTxhbnk+PigpO1xuXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIC8vIFNldHMgY29udHJpYnV0ZWQgYnkgcHJvdmlkZUZpZWxkVHlwZXMoKS9wcm92aWRlQnVpbHRJbkZpZWxkVHlwZXMoKSwgaW4gZGVjbGFyYXRpb24gb3JkZXIuXG4gICAgZm9yIChjb25zdCBzZXQgb2YgaW5qZWN0KEZJRUxEX1RZUEVfU0VUUywgeyBvcHRpb25hbDogdHJ1ZSB9KSA/PyBbXSkge1xuICAgICAgdGhpcy5yZWdpc3RlckFsbChzZXQpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBSZWdpc3RlciBhIGZpZWxkIHR5cGUgY29tcG9uZW50IG1hcHBpbmcgYXQgcnVudGltZS4gKi9cbiAgcmVnaXN0ZXIodHlwZTogc3RyaW5nLCBjb21wb25lbnQ6IFR5cGU8YW55Pik6IHZvaWQge1xuICAgIHRoaXMuZHluYW1pY1JlZ2lzdHJ5LnNldCh0eXBlLCBjb21wb25lbnQpO1xuICB9XG5cbiAgLyoqIFJlZ2lzdGVyIG11bHRpcGxlIGZpZWxkIHR5cGUgY29tcG9uZW50IG1hcHBpbmdzIGF0IHJ1bnRpbWUuICovXG4gIHJlZ2lzdGVyQWxsKG1hcDogUmVjb3JkPHN0cmluZywgVHlwZTxhbnk+Pik6IHZvaWQge1xuICAgIGZvciAoY29uc3QgW3R5cGUsIGNvbXBdIG9mIE9iamVjdC5lbnRyaWVzKG1hcCkpIHtcbiAgICAgIHRoaXMuZHluYW1pY1JlZ2lzdHJ5LnNldCh0eXBlLCBjb21wKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVzb2x2ZSBhIGZpZWxkIHR5cGUgc3RyaW5nIHRvIGEgY29tcG9uZW50IGNsYXNzLlxuICAgKiBDb25zdW1lciB0b2tlbiByZWdpc3RyeSB0YWtlcyBwcmlvcml0eSBvdmVyIHJ1bnRpbWUgcmVnaXN0cmF0aW9ucy5cbiAgICovXG4gIHJlc29sdmUoZmllbGRUeXBlOiBzdHJpbmcpOiBUeXBlPGFueT4gfCBudWxsIHtcbiAgICByZXR1cm4gdGhpcy5jb25zdW1lclJlZ2lzdHJ5LmdldChmaWVsZFR5cGUpID8/IHRoaXMuZHluYW1pY1JlZ2lzdHJ5LmdldChmaWVsZFR5cGUpID8/IG51bGw7XG4gIH1cblxuICAvKiogQ2hlY2sgaWYgYSBmaWVsZCB0eXBlIGhhcyBhIHJlZ2lzdGVyZWQgY29tcG9uZW50LiAqL1xuICBoYXMoZmllbGRUeXBlOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5jb25zdW1lclJlZ2lzdHJ5LmhhcyhmaWVsZFR5cGUpIHx8IHRoaXMuZHluYW1pY1JlZ2lzdHJ5LmhhcyhmaWVsZFR5cGUpO1xuICB9XG5cbiAgLyoqIEV2ZXJ5IHJlZ2lzdGVyZWQgZmllbGQgdHlwZSBrZXkuIENvbnN1bWVyIG92ZXJyaWRlcyBhbmQgcnVudGltZSByZWdpc3RyYXRpb25zIGNvbWJpbmVkLiAqL1xuICByZWdpc3RlcmVkVHlwZXMoKTogc3RyaW5nW10ge1xuICAgIHJldHVybiBbLi4ubmV3IFNldChbLi4udGhpcy5keW5hbWljUmVnaXN0cnkua2V5cygpLCAuLi50aGlzLmNvbnN1bWVyUmVnaXN0cnkua2V5cygpXSldLnNvcnQoKTtcbiAgfVxufVxuIl19
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Injectable, inject } from '@angular/core';
|
|
2
|
+
import { UPLOAD_HANDLER } from '../tokens/injection-tokens';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/**
|
|
5
|
+
* FileUploadService — the single place image-field and file-field agree on how a
|
|
6
|
+
* selected `File` becomes a `FileRef`.
|
|
7
|
+
*
|
|
8
|
+
* With `UPLOAD_HANDLER` registered the file is persisted and stored as `{ url }`;
|
|
9
|
+
* without one it is stored as `{ file }` for the consumer to upload at submit time.
|
|
10
|
+
*/
|
|
11
|
+
export class FileUploadService {
|
|
12
|
+
handler = inject(UPLOAD_HANDLER, { optional: true });
|
|
13
|
+
get hasHandler() {
|
|
14
|
+
return !!this.handler;
|
|
15
|
+
}
|
|
16
|
+
/** Metadata common to both persisted and unpersisted refs. */
|
|
17
|
+
describe(file) {
|
|
18
|
+
return { name: file.name, size: file.size, mimeType: file.type };
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Turn a selected File into the value to store on the control.
|
|
22
|
+
* @throws when a registered handler rejects — callers surface it as a field error.
|
|
23
|
+
*/
|
|
24
|
+
async toFileRef(file) {
|
|
25
|
+
const meta = this.describe(file);
|
|
26
|
+
if (!this.handler)
|
|
27
|
+
return { ...meta, file };
|
|
28
|
+
const result = await this.normalize(this.handler(file));
|
|
29
|
+
return { ...meta, url: result.url, name: result.name ?? meta.name };
|
|
30
|
+
}
|
|
31
|
+
/** Object URL for previewing an unpersisted File. Callers must revoke it. */
|
|
32
|
+
previewUrlFor(ref) {
|
|
33
|
+
if (!ref)
|
|
34
|
+
return null;
|
|
35
|
+
if (ref.url)
|
|
36
|
+
return ref.url;
|
|
37
|
+
if (ref.file && typeof URL !== 'undefined' && typeof URL.createObjectURL === 'function') {
|
|
38
|
+
return URL.createObjectURL(ref.file);
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
revokePreviewUrl(url) {
|
|
43
|
+
if (!url || !url.startsWith('blob:'))
|
|
44
|
+
return;
|
|
45
|
+
if (typeof URL !== 'undefined' && typeof URL.revokeObjectURL === 'function') {
|
|
46
|
+
URL.revokeObjectURL(url);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
normalize(result) {
|
|
50
|
+
if (this.isSubscribable(result)) {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
let settled = false;
|
|
53
|
+
result.subscribe({
|
|
54
|
+
next: value => {
|
|
55
|
+
if (settled)
|
|
56
|
+
return;
|
|
57
|
+
settled = true;
|
|
58
|
+
resolve(value);
|
|
59
|
+
},
|
|
60
|
+
error: err => {
|
|
61
|
+
if (settled)
|
|
62
|
+
return;
|
|
63
|
+
settled = true;
|
|
64
|
+
reject(err);
|
|
65
|
+
},
|
|
66
|
+
complete: () => {
|
|
67
|
+
if (settled)
|
|
68
|
+
return;
|
|
69
|
+
settled = true;
|
|
70
|
+
reject(new Error('Upload handler completed without emitting a result.'));
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return Promise.resolve(result);
|
|
76
|
+
}
|
|
77
|
+
isSubscribable(value) {
|
|
78
|
+
return (typeof value === 'object' &&
|
|
79
|
+
value !== null &&
|
|
80
|
+
typeof value.subscribe === 'function');
|
|
81
|
+
}
|
|
82
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FileUploadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
83
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FileUploadService, providedIn: 'root' });
|
|
84
|
+
}
|
|
85
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FileUploadService, decorators: [{
|
|
86
|
+
type: Injectable,
|
|
87
|
+
args: [{ providedIn: 'root' }]
|
|
88
|
+
}] });
|
|
89
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZS11cGxvYWQuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvc2VydmljZXMvZmlsZS11cGxvYWQuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUVuRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sNEJBQTRCLENBQUM7O0FBRTVEOzs7Ozs7R0FNRztBQUVILE1BQU0sT0FBTyxpQkFBaUI7SUFDWCxPQUFPLEdBQUcsTUFBTSxDQUFDLGNBQWMsRUFBRSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBRXRFLElBQUksVUFBVTtRQUNaLE9BQU8sQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUM7SUFDeEIsQ0FBQztJQUVELDhEQUE4RDtJQUN0RCxRQUFRLENBQUMsSUFBVTtRQUN6QixPQUFPLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLEVBQUUsUUFBUSxFQUFFLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUNuRSxDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsS0FBSyxDQUFDLFNBQVMsQ0FBQyxJQUFVO1FBQ3hCLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDakMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPO1lBQUUsT0FBTyxFQUFFLEdBQUcsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDO1FBRTVDLE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDeEQsT0FBTyxFQUFFLEdBQUcsSUFBSSxFQUFFLEdBQUcsRUFBRSxNQUFNLENBQUMsR0FBRyxFQUFFLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSSxJQUFJLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUN0RSxDQUFDO0lBRUQsNkVBQTZFO0lBQzdFLGFBQWEsQ0FBQyxHQUErQjtRQUMzQyxJQUFJLENBQUMsR0FBRztZQUFFLE9BQU8sSUFBSSxDQUFDO1FBQ3RCLElBQUksR0FBRyxDQUFDLEdBQUc7WUFBRSxPQUFPLEdBQUcsQ0FBQyxHQUFHLENBQUM7UUFDNUIsSUFBSSxHQUFHLENBQUMsSUFBSSxJQUFJLE9BQU8sR0FBRyxLQUFLLFdBQVcsSUFBSSxPQUFPLEdBQUcsQ0FBQyxlQUFlLEtBQUssVUFBVSxFQUFFLENBQUM7WUFDeEYsT0FBTyxHQUFHLENBQUMsZUFBZSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN2QyxDQUFDO1FBQ0QsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQsZ0JBQWdCLENBQUMsR0FBa0I7UUFDakMsSUFBSSxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDO1lBQUUsT0FBTztRQUM3QyxJQUFJLE9BQU8sR0FBRyxLQUFLLFdBQVcsSUFBSSxPQUFPLEdBQUcsQ0FBQyxlQUFlLEtBQUssVUFBVSxFQUFFLENBQUM7WUFDNUUsR0FBRyxDQUFDLGVBQWUsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUMzQixDQUFDO0lBQ0gsQ0FBQztJQUVPLFNBQVMsQ0FDZixNQUF5RTtRQUV6RSxJQUFJLElBQUksQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztZQUNoQyxPQUFPLElBQUksT0FBTyxDQUFlLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFO2dCQUNuRCxJQUFJLE9BQU8sR0FBRyxLQUFLLENBQUM7Z0JBQ3BCLE1BQU0sQ0FBQyxTQUFTLENBQUM7b0JBQ2YsSUFBSSxFQUFFLEtBQUssQ0FBQyxFQUFFO3dCQUNaLElBQUksT0FBTzs0QkFBRSxPQUFPO3dCQUNwQixPQUFPLEdBQUcsSUFBSSxDQUFDO3dCQUNmLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQztvQkFDakIsQ0FBQztvQkFDRCxLQUFLLEVBQUUsR0FBRyxDQUFDLEVBQUU7d0JBQ1gsSUFBSSxPQUFPOzRCQUFFLE9BQU87d0JBQ3BCLE9BQU8sR0FBRyxJQUFJLENBQUM7d0JBQ2YsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO29CQUNkLENBQUM7b0JBQ0QsUUFBUSxFQUFFLEdBQUcsRUFBRTt3QkFDYixJQUFJLE9BQU87NEJBQUUsT0FBTzt3QkFDcEIsT0FBTyxHQUFHLElBQUksQ0FBQzt3QkFDZixNQUFNLENBQUMsSUFBSSxLQUFLLENBQUMscURBQXFELENBQUMsQ0FBQyxDQUFDO29CQUMzRSxDQUFDO2lCQUNGLENBQUMsQ0FBQztZQUNMLENBQUMsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUNELE9BQU8sT0FBTyxDQUFDLE9BQU8sQ0FBQyxNQUE4QyxDQUFDLENBQUM7SUFDekUsQ0FBQztJQUVPLGNBQWMsQ0FBQyxLQUFjO1FBQ25DLE9BQU8sQ0FDTCxPQUFPLEtBQUssS0FBSyxRQUFRO1lBQ3pCLEtBQUssS0FBSyxJQUFJO1lBQ2QsT0FBUSxLQUFvQyxDQUFDLFNBQVMsS0FBSyxVQUFVLENBQ3RFLENBQUM7SUFDSixDQUFDO3dHQTNFVSxpQkFBaUI7NEdBQWpCLGlCQUFpQixjQURKLE1BQU07OzRGQUNuQixpQkFBaUI7a0JBRDdCLFVBQVU7bUJBQUMsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSwgaW5qZWN0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgdHlwZSB7IEZpbGVSZWYsIFN1YnNjcmliYWJsZSwgVXBsb2FkUmVzdWx0IH0gZnJvbSAnQGR5bmFtaWMtZW50aXR5L2NvcmUnO1xuaW1wb3J0IHsgVVBMT0FEX0hBTkRMRVIgfSBmcm9tICcuLi90b2tlbnMvaW5qZWN0aW9uLXRva2Vucyc7XG5cbi8qKlxuICogRmlsZVVwbG9hZFNlcnZpY2Ug4oCUIHRoZSBzaW5nbGUgcGxhY2UgaW1hZ2UtZmllbGQgYW5kIGZpbGUtZmllbGQgYWdyZWUgb24gaG93IGFcbiAqIHNlbGVjdGVkIGBGaWxlYCBiZWNvbWVzIGEgYEZpbGVSZWZgLlxuICpcbiAqIFdpdGggYFVQTE9BRF9IQU5ETEVSYCByZWdpc3RlcmVkIHRoZSBmaWxlIGlzIHBlcnNpc3RlZCBhbmQgc3RvcmVkIGFzIGB7IHVybCB9YDtcbiAqIHdpdGhvdXQgb25lIGl0IGlzIHN0b3JlZCBhcyBgeyBmaWxlIH1gIGZvciB0aGUgY29uc3VtZXIgdG8gdXBsb2FkIGF0IHN1Ym1pdCB0aW1lLlxuICovXG5ASW5qZWN0YWJsZSh7IHByb3ZpZGVkSW46ICdyb290JyB9KVxuZXhwb3J0IGNsYXNzIEZpbGVVcGxvYWRTZXJ2aWNlIHtcbiAgcHJpdmF0ZSByZWFkb25seSBoYW5kbGVyID0gaW5qZWN0KFVQTE9BRF9IQU5ETEVSLCB7IG9wdGlvbmFsOiB0cnVlIH0pO1xuXG4gIGdldCBoYXNIYW5kbGVyKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAhIXRoaXMuaGFuZGxlcjtcbiAgfVxuXG4gIC8qKiBNZXRhZGF0YSBjb21tb24gdG8gYm90aCBwZXJzaXN0ZWQgYW5kIHVucGVyc2lzdGVkIHJlZnMuICovXG4gIHByaXZhdGUgZGVzY3JpYmUoZmlsZTogRmlsZSk6IEZpbGVSZWYge1xuICAgIHJldHVybiB7IG5hbWU6IGZpbGUubmFtZSwgc2l6ZTogZmlsZS5zaXplLCBtaW1lVHlwZTogZmlsZS50eXBlIH07XG4gIH1cblxuICAvKipcbiAgICogVHVybiBhIHNlbGVjdGVkIEZpbGUgaW50byB0aGUgdmFsdWUgdG8gc3RvcmUgb24gdGhlIGNvbnRyb2wuXG4gICAqIEB0aHJvd3Mgd2hlbiBhIHJlZ2lzdGVyZWQgaGFuZGxlciByZWplY3RzIOKAlCBjYWxsZXJzIHN1cmZhY2UgaXQgYXMgYSBmaWVsZCBlcnJvci5cbiAgICovXG4gIGFzeW5jIHRvRmlsZVJlZihmaWxlOiBGaWxlKTogUHJvbWlzZTxGaWxlUmVmPiB7XG4gICAgY29uc3QgbWV0YSA9IHRoaXMuZGVzY3JpYmUoZmlsZSk7XG4gICAgaWYgKCF0aGlzLmhhbmRsZXIpIHJldHVybiB7IC4uLm1ldGEsIGZpbGUgfTtcblxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMubm9ybWFsaXplKHRoaXMuaGFuZGxlcihmaWxlKSk7XG4gICAgcmV0dXJuIHsgLi4ubWV0YSwgdXJsOiByZXN1bHQudXJsLCBuYW1lOiByZXN1bHQubmFtZSA/PyBtZXRhLm5hbWUgfTtcbiAgfVxuXG4gIC8qKiBPYmplY3QgVVJMIGZvciBwcmV2aWV3aW5nIGFuIHVucGVyc2lzdGVkIEZpbGUuIENhbGxlcnMgbXVzdCByZXZva2UgaXQuICovXG4gIHByZXZpZXdVcmxGb3IocmVmOiBGaWxlUmVmIHwgbnVsbCB8IHVuZGVmaW5lZCk6IHN0cmluZyB8IG51bGwge1xuICAgIGlmICghcmVmKSByZXR1cm4gbnVsbDtcbiAgICBpZiAocmVmLnVybCkgcmV0dXJuIHJlZi51cmw7XG4gICAgaWYgKHJlZi5maWxlICYmIHR5cGVvZiBVUkwgIT09ICd1bmRlZmluZWQnICYmIHR5cGVvZiBVUkwuY3JlYXRlT2JqZWN0VVJMID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICByZXR1cm4gVVJMLmNyZWF0ZU9iamVjdFVSTChyZWYuZmlsZSk7XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgcmV2b2tlUHJldmlld1VybCh1cmw6IHN0cmluZyB8IG51bGwpOiB2b2lkIHtcbiAgICBpZiAoIXVybCB8fCAhdXJsLnN0YXJ0c1dpdGgoJ2Jsb2I6JykpIHJldHVybjtcbiAgICBpZiAodHlwZW9mIFVSTCAhPT0gJ3VuZGVmaW5lZCcgJiYgdHlwZW9mIFVSTC5yZXZva2VPYmplY3RVUkwgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgIFVSTC5yZXZva2VPYmplY3RVUkwodXJsKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIG5vcm1hbGl6ZShcbiAgICByZXN1bHQ6IFVwbG9hZFJlc3VsdCB8IFByb21pc2U8VXBsb2FkUmVzdWx0PiB8IFN1YnNjcmliYWJsZTxVcGxvYWRSZXN1bHQ+LFxuICApOiBQcm9taXNlPFVwbG9hZFJlc3VsdD4ge1xuICAgIGlmICh0aGlzLmlzU3Vic2NyaWJhYmxlKHJlc3VsdCkpIHtcbiAgICAgIHJldHVybiBuZXcgUHJvbWlzZTxVcGxvYWRSZXN1bHQ+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgbGV0IHNldHRsZWQgPSBmYWxzZTtcbiAgICAgICAgcmVzdWx0LnN1YnNjcmliZSh7XG4gICAgICAgICAgbmV4dDogdmFsdWUgPT4ge1xuICAgICAgICAgICAgaWYgKHNldHRsZWQpIHJldHVybjtcbiAgICAgICAgICAgIHNldHRsZWQgPSB0cnVlO1xuICAgICAgICAgICAgcmVzb2x2ZSh2YWx1ZSk7XG4gICAgICAgICAgfSxcbiAgICAgICAgICBlcnJvcjogZXJyID0+IHtcbiAgICAgICAgICAgIGlmIChzZXR0bGVkKSByZXR1cm47XG4gICAgICAgICAgICBzZXR0bGVkID0gdHJ1ZTtcbiAgICAgICAgICAgIHJlamVjdChlcnIpO1xuICAgICAgICAgIH0sXG4gICAgICAgICAgY29tcGxldGU6ICgpID0+IHtcbiAgICAgICAgICAgIGlmIChzZXR0bGVkKSByZXR1cm47XG4gICAgICAgICAgICBzZXR0bGVkID0gdHJ1ZTtcbiAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoJ1VwbG9hZCBoYW5kbGVyIGNvbXBsZXRlZCB3aXRob3V0IGVtaXR0aW5nIGEgcmVzdWx0LicpKTtcbiAgICAgICAgICB9LFxuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHJlc3VsdCBhcyBVcGxvYWRSZXN1bHQgfCBQcm9taXNlPFVwbG9hZFJlc3VsdD4pO1xuICB9XG5cbiAgcHJpdmF0ZSBpc1N1YnNjcmliYWJsZSh2YWx1ZTogdW5rbm93bik6IHZhbHVlIGlzIFN1YnNjcmliYWJsZTxVcGxvYWRSZXN1bHQ+IHtcbiAgICByZXR1cm4gKFxuICAgICAgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJlxuICAgICAgdmFsdWUgIT09IG51bGwgJiZcbiAgICAgIHR5cGVvZiAodmFsdWUgYXMgU3Vic2NyaWJhYmxlPFVwbG9hZFJlc3VsdD4pLnN1YnNjcmliZSA9PT0gJ2Z1bmN0aW9uJ1xuICAgICk7XG4gIH1cbn1cbiJdfQ==
|