ngx-dynamic-entity 0.0.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +139 -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 +49 -41
- package/esm2022/lib/field-types/currency-field.component.mjs +101 -0
- package/esm2022/lib/field-types/date-field.component.mjs +47 -39
- package/esm2022/lib/field-types/dropdown-field.component.mjs +145 -54
- package/esm2022/lib/field-types/email-field.component.mjs +101 -0
- package/esm2022/lib/field-types/entity-ref-field.component.mjs +134 -70
- 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 +110 -56
- package/esm2022/lib/field-types/number-field.component.mjs +84 -41
- 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 +82 -42
- package/esm2022/lib/field-types/textarea-field.component.mjs +52 -41
- package/esm2022/lib/form/dynamic-field/dynamic-field.component.mjs +44 -26
- package/esm2022/lib/form/dynamic-form.component.mjs +501 -67
- package/esm2022/lib/form/dynamic-record-form.component.mjs +469 -0
- package/esm2022/lib/providers/provide-field-types.mjs +72 -0
- package/esm2022/lib/providers/provide-ngx-dynamic-entity.mjs +3 -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 +24 -7
- package/esm2022/lib/tokens/injection-tokens.mjs +88 -7
- package/esm2022/public-api.mjs +14 -5
- package/fesm2022/ngx-dynamic-entity.mjs +3985 -876
- 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 +111 -19
- package/lib/form/dynamic-record-form.component.d.ts +169 -0
- package/lib/providers/provide-field-types.d.ts +24 -0
- package/lib/providers/provide-ngx-dynamic-entity.d.ts +12 -7
- 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 +90 -7
- package/package.json +14 -3
- package/public-api.d.ts +11 -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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QUFFLENBQUMsT0FBTyxDQUFDLEtBQUssSUFBSSxFQUFFLENBQUMsQ0FBQztvQkFDakQsd0VBQXdFO29CQUN4RSxLQUFLLEVBQUUsR0FBRyxDQUFDLEVBQUUsQ0FBQyxNQUFNLENBQUMsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO29CQUN2QywyRUFBMkU7b0JBQzNFLFFBQVEsRUFBRSxHQUFHLEVBQUUsQ0FBQyxNQUFNLENBQUMsR0FBRyxFQUFFLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFDO2lCQUMxQyxDQUFDLENBQUM7WUFDTCxDQUFDLENBQUMsQ0FBQztRQUNMLENBQUM7UUFDRCxPQUFPLE9BQU8sQ0FBQyxPQUFPLENBQUMsTUFBb0MsQ0FBQyxDQUFDO0lBQy9ELENBQUM7SUFFTyxjQUFjLENBQUMsS0FBYztRQUNuQyxPQUFPLENBQ0wsT0FBTyxLQUFLLEtBQUssUUFBUTtZQUN6QixLQUFLLEtBQUssSUFBSTtZQUNkLE9BQVEsS0FBeUMsQ0FBQyxTQUFTLEtBQUssVUFBVSxDQUMzRSxDQUFDO0lBQ0osQ0FBQztJQUVELHlGQUF5RjtJQUN6RixPQUFPLENBQUMsQ0FBVSxFQUFFLENBQVUsRUFBRSxJQUFJLEdBQUcsSUFBSTtRQUN6QyxPQUFPLFdBQVcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ2pDLENBQUM7d0dBaEtVLHNCQUFzQjs0R0FBdEIsc0JBQXNCLGNBRFQsTUFBTTs7NEZBQ25CLHNCQUFzQjtrQkFEbEMsVUFBVTttQkFBQyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlLCBpbmplY3QgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHR5cGUge1xyXG4gIEVudGl0eVJlZmVyZW5jZUxvYWRlcixcclxuICBSYXdSZWZlcmVuY2VJdGVtLFxyXG4gIFJlZmVyZW5jZUxvYWRlckNvbnRleHQsXHJcbiAgUmVmZXJlbmNlTG9hZGVyUmVzdWx0LFxyXG4gIFJlZmVyZW5jZU9wdGlvbixcclxuICBTdWJzY3JpYmFibGUsXHJcbn0gZnJvbSAnQGR5bmFtaWMtZW50aXR5L2NvcmUnO1xyXG5pbXBvcnQge1xyXG4gIGJ1aWxkUmVmZXJlbmNlQ2FjaGVLZXksXHJcbiAgZW50aXR5S2V5RnJvbUNhY2hlS2V5LFxyXG4gIG5vcm1hbGl6ZVJlZmVyZW5jZU9wdGlvbnMsXHJcbiAgcmVzb2x2ZUxhYmVsLFxyXG4gIHZhbHVlc01hdGNoLFxyXG59IGZyb20gJ0BkeW5hbWljLWVudGl0eS9jb3JlJztcclxuaW1wb3J0IHsgRU5USVRZX1JFRl9DQUNIRV9TVE9SRSB9IGZyb20gJy4uL3Rva2Vucy9pbmplY3Rpb24tdG9rZW5zJztcclxuaW1wb3J0IHsgRW50aXR5UmVmUmVnaXN0cnlTZXJ2aWNlIH0gZnJvbSAnLi9lbnRpdHktcmVmLXJlZ2lzdHJ5LnNlcnZpY2UnO1xyXG5cclxuLyoqIExhYmVsLWNhY2hlIGZpZWxkIHNlcGFyYXRvci4gS2VwdCBvdXQgb2YgdGVtcGxhdGUgbGl0ZXJhbHMgc28gaXQgc3RheXMgdmlzaWJsZSBpbiBzb3VyY2UuICovXHJcbmNvbnN0IExBQkVMX1NFUCA9ICd8JztcclxuXHJcbi8qKlxyXG4gKiBFbnRpdHlSZWZlcmVuY2VTZXJ2aWNlIOKAlCB0aGUgY2FjaGUgaW4gZnJvbnQgb2YgY29uc3VtZXItcmVnaXN0ZXJlZCBvcHRpb24gbG9hZGVycy5cclxuICpcclxuICogVGhyZWUgbGF5ZXJzLCBtYXRjaGluZyB0aGUgcmVmZXJlbmNlIGltcGxlbWVudGF0aW9uOlxyXG4gKiAgMS4gKipvcHRpb25zKiog4oCUIHJlc29sdmVkIGBSZWZlcmVuY2VPcHRpb25bXWAgcGVyIChlbnRpdHksIGxhbmcsIGRpc3BsYXlGaWVsZHMsIGZpbHRlcnMpLlxyXG4gKiAgMi4gKippbi1mbGlnaHQqKiDigJQgdGhlIHBlbmRpbmcgUHJvbWlzZSwgc28gTiBmaWVsZHMgcmVmZXJlbmNpbmcgb25lIGVudGl0eSB0cmlnZ2VyIG9uZVxyXG4gKiAgICAgbG9hZCByYXRoZXIgdGhhbiBOLiBUaGlzIGlzIHRoZSBsYXllciB0aGF0IGFjdHVhbGx5IHJlbW92ZXMgZHVwbGljYXRlIEhUVFAgY2FsbHM7XHJcbiAqICAgICBhbiBvcHRpb25zIGNhY2hlIGFsb25lIGRvZXMgbm90aGluZyBmb3IgY29uY3VycmVudCBmaXJzdCBsb2Fkcy5cclxuICogIDMuICoqbGFiZWxzKiog4oCUIHN5bmNocm9ub3VzIHZhbHVlIOKGkiB0ZXh0LCBmb3IgcmVuZGVyaW5nIGEgc3RvcmVkIHJlZmVyZW5jZSB3aXRob3V0XHJcbiAqICAgICB3YWl0aW5nIG9uIGEgbG9hZC5cclxuICpcclxuICogSW52YWxpZGF0aW9uIGlzIHBlciBlbnRpdHksIG5vdCBnbG9iYWwuIFRoZSByZWZlcmVuY2UgY291bGQgb25seSBgY2xlYXJDYWNoZSgpYCBldmVyeXRoaW5nXHJcbiAqIChpc3N1ZSAjMTYpLCBzbyBzYXZpbmcgb25lIHJlY29yZCByZS1mZXRjaGVkIGV2ZXJ5IGVudGl0eSdzIG9wdGlvbnMuXHJcbiAqL1xyXG5ASW5qZWN0YWJsZSh7IHByb3ZpZGVkSW46ICdyb290JyB9KVxyXG5leHBvcnQgY2xhc3MgRW50aXR5UmVmZXJlbmNlU2VydmljZSB7XHJcbiAgcHJpdmF0ZSByZWFkb25seSByZWdpc3RyeSA9IGluamVjdChFbnRpdHlSZWZSZWdpc3RyeVNlcnZpY2UpO1xyXG4gIHByaXZhdGUgcmVhZG9ubHkgc3RvcmUgPSBpbmplY3QoRU5USVRZX1JFRl9DQUNIRV9TVE9SRSk7XHJcblxyXG4gIC8qKiBMb2FkcyBpbiBwcm9ncmVzcywga2V5ZWQgdGhlIHNhbWUgd2F5IGFzIHRoZSBvcHRpb25zIGNhY2hlLiAqL1xyXG4gIHByaXZhdGUgcmVhZG9ubHkgaW5GbGlnaHQgPSBuZXcgTWFwPHN0cmluZywgUHJvbWlzZTxSZWZlcmVuY2VPcHRpb25bXT4+KCk7XHJcbiAgLyoqIGBlbnRpdHlLZXl8bGFuZ3xjYW5vbmljYWxWYWx1ZWAgLT4gZGlzcGxheSB0ZXh0LiAqL1xyXG4gIHByaXZhdGUgcmVhZG9ubHkgbGFiZWxzID0gbmV3IE1hcDxzdHJpbmcsIHN0cmluZz4oKTtcclxuXHJcbiAgLyoqXHJcbiAgICogT3B0aW9ucyBmb3IgYW4gZW50aXR5LCBmcm9tIGNhY2hlIHdoZW4gcHJlc2VudC5cclxuICAgKiBDb25jdXJyZW50IGNhbGxlcnMgd2l0aCB0aGUgc2FtZSBrZXkgc2hhcmUgb25lIGxvYWQuXHJcbiAgICovXHJcbiAgYXN5bmMgbG9hZChlbnRpdHlLZXk6IHN0cmluZywgY3R4OiBSZWZlcmVuY2VMb2FkZXJDb250ZXh0ID0ge30pOiBQcm9taXNlPFJlZmVyZW5jZU9wdGlvbltdPiB7XHJcbiAgICBjb25zdCBrZXkgPSBidWlsZFJlZmVyZW5jZUNhY2hlS2V5KGVudGl0eUtleSwge1xyXG4gICAgICBsYW5nOiBjdHgubGFuZyxcclxuICAgICAgZmlsdGVyczogY3R4LmZpbHRlcnMsXHJcbiAgICAgIGRpc3BsYXlGaWVsZHM6IGN0eC5kaXNwbGF5RmllbGRzLFxyXG4gICAgICBwYXJlbnRWYWx1ZTogY3R4LnBhcmVudFZhbHVlLFxyXG4gICAgfSk7XHJcblxyXG4gICAgY29uc3QgY2FjaGVkID0gdGhpcy5zdG9yZS5nZXQoa2V5KTtcclxuICAgIGlmIChjYWNoZWQpIHJldHVybiBjYWNoZWQ7XHJcblxyXG4gICAgY29uc3QgcGVuZGluZyA9IHRoaXMuaW5GbGlnaHQuZ2V0KGtleSk7XHJcbiAgICBpZiAocGVuZGluZykgcmV0dXJuIHBlbmRpbmc7XHJcblxyXG4gICAgY29uc3QgbG9hZGVyID0gdGhpcy5yZWdpc3RyeS5yZXNvbHZlKGVudGl0eUtleSk7XHJcbiAgICBpZiAoIWxvYWRlcikgcmV0dXJuIFtdO1xyXG5cclxuICAgIGNvbnN0IGxvYWQgPSB0aGlzLmludm9rZShsb2FkZXIsIGN0eClcclxuICAgICAgLnRoZW4ocmF3ID0+IHtcclxuICAgICAgICAvLyBkaXNwbGF5RmllbGRzIGRyaXZlIGxhYmVsIGJ1aWxkaW5nLCB3aGljaCBpcyB3aHkgdGhleSBhcmUgcGFydCBvZiB0aGUgY2FjaGUga2V5LlxyXG4gICAgICAgIGNvbnN0IG9wdGlvbnMgPSBub3JtYWxpemVSZWZlcmVuY2VPcHRpb25zKFxyXG4gICAgICAgICAgcmF3IGFzIFJhd1JlZmVyZW5jZUl0ZW1bXSxcclxuICAgICAgICAgIGN0eC5kaXNwbGF5RmllbGRzID8geyBlbmFibGVkOiB0cnVlLCBkaXNwbGF5RmllbGRzOiBjdHguZGlzcGxheUZpZWxkcyB9IDogdW5kZWZpbmVkLFxyXG4gICAgICAgICAgY3R4LmxhbmcgPz8gJ2VuJyxcclxuICAgICAgICApO1xyXG4gICAgICAgIHRoaXMuc3RvcmUuc2V0KGtleSwgb3B0aW9ucyk7XHJcbiAgICAgICAgdGhpcy5yZW1lbWJlckxhYmVscyhlbnRpdHlLZXksIGN0eC5sYW5nID8/ICdlbicsIG9wdGlvbnMpO1xyXG4gICAgICAgIHJldHVybiBvcHRpb25zO1xyXG4gICAgICB9KTtcclxuXHJcbiAgICAvLyBBIGZhaWxlZCBsb2FkIHJlc29sdmVzIHRvIFtdIGZvciB0aGUgY2FsbGVyIGJ1dCBpcyBuZXZlciB3cml0dGVuIHRvIHRoZSBzdG9yZSwgc28gYVxyXG4gICAgLy8gdHJhbnNpZW50IGVycm9yIGlzIHJldHJpZWQgb24gdGhlIG5leHQgY2FsbCBpbnN0ZWFkIG9mIHBvaXNvbmluZyB0aGUgY2FjaGUuIE9ubHkgdGhlXHJcbiAgICAvLyBgLnRoZW5gIGFib3ZlIGNhY2hlcywgYW5kIGEgcmVqZWN0aW9uIHNraXBzIGl0LlxyXG4gICAgY29uc3QgZ3VhcmRlZCA9IGxvYWRcclxuICAgICAgLmNhdGNoKCgpID0+IFtdIGFzIFJlZmVyZW5jZU9wdGlvbltdKVxyXG4gICAgICAuZmluYWxseSgoKSA9PiB0aGlzLmluRmxpZ2h0LmRlbGV0ZShrZXkpKTtcclxuXHJcbiAgICB0aGlzLmluRmxpZ2h0LnNldChrZXksIGd1YXJkZWQpO1xyXG4gICAgcmV0dXJuIGd1YXJkZWQ7XHJcbiAgfVxyXG5cclxuICAvKiogQ2FjaGVkIG9wdGlvbnMgd2l0aG91dCB0cmlnZ2VyaW5nIGEgbG9hZC4gYHVuZGVmaW5lZGAgd2hlbiBub3QgY2FjaGVkLiAqL1xyXG4gIHBlZWsoZW50aXR5S2V5OiBzdHJpbmcsIGN0eDogUmVmZXJlbmNlTG9hZGVyQ29udGV4dCA9IHt9KTogUmVmZXJlbmNlT3B0aW9uW10gfCB1bmRlZmluZWQge1xyXG4gICAgcmV0dXJuIHRoaXMuc3RvcmUuZ2V0KFxyXG4gICAgICBidWlsZFJlZmVyZW5jZUNhY2hlS2V5KGVudGl0eUtleSwge1xyXG4gICAgICAgIGxhbmc6IGN0eC5sYW5nLFxyXG4gICAgICAgIGZpbHRlcnM6IGN0eC5maWx0ZXJzLFxyXG4gICAgICAgIGRpc3BsYXlGaWVsZHM6IGN0eC5kaXNwbGF5RmllbGRzLFxyXG4gICAgICAgIHBhcmVudFZhbHVlOiBjdHgucGFyZW50VmFsdWUsXHJcbiAgICAgIH0pLFxyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIERpc3BsYXkgdGV4dCBmb3IgYSBzdG9yZWQgcmVmZXJlbmNlIHZhbHVlLCBzeW5jaHJvbm91c2x5LlxyXG4gICAqIFJldHVybnMgYHVuZGVmaW5lZGAgd2hlbiB0aGUgZW50aXR5J3Mgb3B0aW9ucyBoYXZlIG5ldmVyIGJlZW4gbG9hZGVkLlxyXG4gICAqL1xyXG4gIGxhYmVsRm9yKGVudGl0eUtleTogc3RyaW5nLCB2YWx1ZTogdW5rbm93biwgbGFuZyA9ICdlbicpOiBzdHJpbmcgfCB1bmRlZmluZWQge1xyXG4gICAgcmV0dXJuIHRoaXMubGFiZWxzLmdldCh0aGlzLmxhYmVsS2V5KGVudGl0eUtleSwgbGFuZywgdmFsdWUpKTtcclxuICB9XHJcblxyXG4gIC8qKiBEcm9wIGV2ZXJ5dGhpbmcgY2FjaGVkIGZvciBvbmUgZW50aXR5LCBsZWF2aW5nIG90aGVyIGVudGl0aWVzIGludGFjdC4gKi9cclxuICBpbnZhbGlkYXRlKGVudGl0eUtleTogc3RyaW5nKTogdm9pZCB7XHJcbiAgICBmb3IgKGNvbnN0IGtleSBvZiB0aGlzLnN0b3JlLmtleXMoKSkge1xyXG4gICAgICBpZiAoZW50aXR5S2V5RnJvbUNhY2hlS2V5KGtleSkgPT09IGVudGl0eUtleSkgdGhpcy5zdG9yZS5kZWxldGUoa2V5KTtcclxuICAgIH1cclxuICAgIGZvciAoY29uc3Qga2V5IG9mIFsuLi50aGlzLmluRmxpZ2h0LmtleXMoKV0pIHtcclxuICAgICAgaWYgKGVudGl0eUtleUZyb21DYWNoZUtleShrZXkpID09PSBlbnRpdHlLZXkpIHRoaXMuaW5GbGlnaHQuZGVsZXRlKGtleSk7XHJcbiAgICB9XHJcbiAgICBjb25zdCBwcmVmaXggPSBgJHtlbnRpdHlLZXl9JHtMQUJFTF9TRVB9YDtcclxuICAgIGZvciAoY29uc3Qga2V5IG9mIFsuLi50aGlzLmxhYmVscy5rZXlzKCldKSB7XHJcbiAgICAgIGlmIChrZXkuc3RhcnRzV2l0aChwcmVmaXgpKSB0aGlzLmxhYmVscy5kZWxldGUoa2V5KTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIGNsZWFyKCk6IHZvaWQge1xyXG4gICAgdGhpcy5zdG9yZS5jbGVhcigpO1xyXG4gICAgdGhpcy5pbkZsaWdodC5jbGVhcigpO1xyXG4gICAgdGhpcy5sYWJlbHMuY2xlYXIoKTtcclxuICB9XHJcblxyXG4gIHByaXZhdGUgcmVtZW1iZXJMYWJlbHMoZW50aXR5S2V5OiBzdHJpbmcsIGxhbmc6IHN0cmluZywgb3B0aW9uczogUmVmZXJlbmNlT3B0aW9uW10pOiB2b2lkIHtcclxuICAgIGZvciAoY29uc3Qgb3B0aW9uIG9mIG9wdGlvbnMpIHtcclxuICAgICAgdGhpcy5sYWJlbHMuc2V0KHRoaXMubGFiZWxLZXkoZW50aXR5S2V5LCBsYW5nLCBvcHRpb24udmFsdWUpLCBvcHRpb24ubGFiZWwpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLyoqIExhYmVsLWNhY2hlIGtleS4gQW4gb2JqZWN0IHZhbHVlIGNvbGxhcHNlcyB0byBpdHMgcmVzb2x2ZWQgdGV4dCBmb3IgdGhlIGFjdGl2ZSBsYW5ndWFnZS4gKi9cclxuICBwcml2YXRlIGxhYmVsS2V5KGVudGl0eUtleTogc3RyaW5nLCBsYW5nOiBzdHJpbmcsIHZhbHVlOiB1bmtub3duKTogc3RyaW5nIHtcclxuICAgIGNvbnN0IGNhbm9uaWNhbCA9XHJcbiAgICAgIHZhbHVlICE9PSBudWxsICYmIHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCdcclxuICAgICAgICA/IHJlc29sdmVMYWJlbCh2YWx1ZSBhcyBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+LCBsYW5nKVxyXG4gICAgICAgIDogU3RyaW5nKHZhbHVlID8/ICcnKTtcclxuICAgIHJldHVybiBbZW50aXR5S2V5LCBsYW5nLCBjYW5vbmljYWxdLmpvaW4oTEFCRUxfU0VQKTtcclxuICB9XHJcblxyXG4gIC8vIOKUgOKUgOKUgCBMb2FkZXIgcmVzdWx0IGNvZXJjaW9uIChhcnJheSB8IFByb21pc2UgfCBPYnNlcnZhYmxlKSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcclxuXHJcbiAgcHJpdmF0ZSBpbnZva2UoXHJcbiAgICBsb2FkZXI6IEVudGl0eVJlZmVyZW5jZUxvYWRlcixcclxuICAgIGN0eDogUmVmZXJlbmNlTG9hZGVyQ29udGV4dCxcclxuICApOiBQcm9taXNlPFJlZmVyZW5jZU9wdGlvbltdPiB7XHJcbiAgICBsZXQgcmVzdWx0OiBSZWZlcmVuY2VMb2FkZXJSZXN1bHQ7XHJcbiAgICB0cnkge1xyXG4gICAgICByZXN1bHQgPSBsb2FkZXIoY3R4KTtcclxuICAgIH0gY2F0Y2ggKGVycikge1xyXG4gICAgICAvLyBSZWplY3QgcmF0aGVyIHRoYW4gcmVzb2x2ZSBlbXB0eTogYGxvYWRgIG11c3QgYmUgYWJsZSB0byB0ZWxsIGZhaWx1cmUgZnJvbVxyXG4gICAgICAvLyBcIm5vIG1hdGNoZXNcIiwgb3IgaXQgd291bGQgY2FjaGUgdGhlIGZhaWx1cmUuXHJcbiAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChlcnIpO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIHRoaXMudG9Qcm9taXNlKHJlc3VsdCk7XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIHRvUHJvbWlzZShyZXN1bHQ6IFJlZmVyZW5jZUxvYWRlclJlc3VsdCk6IFByb21pc2U8UmVmZXJlbmNlT3B0aW9uW10+IHtcclxuICAgIGlmIChBcnJheS5pc0FycmF5KHJlc3VsdCkpIHJldHVybiBQcm9taXNlLnJlc29sdmUocmVzdWx0KTtcclxuICAgIGlmICh0aGlzLmlzU3Vic2NyaWJhYmxlKHJlc3VsdCkpIHtcclxuICAgICAgLy8gRmlyc3QgZW1pc3Npb24gd2lucywgc28gYW4gT2JzZXJ2YWJsZSB0aGF0IHN0YXlzIG9wZW4gc3RpbGwgcmVzb2x2ZXMgdGhlIGxvYWQuXHJcbiAgICAgIHJldHVybiBuZXcgUHJvbWlzZTxSZWZlcmVuY2VPcHRpb25bXT4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgIGxldCBzZXR0bGVkID0gZmFsc2U7XHJcbiAgICAgICAgY29uc3Qgc2V0dGxlID0gKGZuOiAoKSA9PiB2b2lkKSA9PiB7XHJcbiAgICAgICAgICBpZiAoc2V0dGxlZCkgcmV0dXJuO1xyXG4gICAgICAgICAgc2V0dGxlZCA9IHRydWU7XHJcbiAgICAgICAgICBmbigpO1xyXG4gICAgICAgIH07XHJcbiAgICAgICAgcmVzdWx0LnN1YnNjcmliZSh7XHJcbiAgICAgICAgICBuZXh0OiB2YWx1ZSA9PiBzZXR0bGUoKCkgPT4gcmVzb2x2ZSh2YWx1ZSA/PyBbXSkpLFxyXG4gICAgICAgICAgLy8gQW4gZXJyb3Jpbmcgc3RyZWFtIGlzIGEgZmFpbGVkIGxvYWQsIG5vdCBhbiBlbXB0eSBvbmUg4oCUIHNlZSBgaW52b2tlYC5cclxuICAgICAgICAgIGVycm9yOiBlcnIgPT4gc2V0dGxlKCgpID0+IHJlamVjdChlcnIpKSxcclxuICAgICAgICAgIC8vIENvbXBsZXRpbmcgd2l0aG91dCBlbWl0dGluZyBpcyBhIGxlZ2l0aW1hdGUgZW1wdHkgcmVzdWx0LCBhbmQgaXMgY2FjaGVkLlxyXG4gICAgICAgICAgY29tcGxldGU6ICgpID0+IHNldHRsZSgoKSA9PiByZXNvbHZlKFtdKSksXHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShyZXN1bHQgYXMgUHJvbWlzZTxSZWZlcmVuY2VPcHRpb25bXT4pO1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBpc1N1YnNjcmliYWJsZSh2YWx1ZTogdW5rbm93bik6IHZhbHVlIGlzIFN1YnNjcmliYWJsZTxSZWZlcmVuY2VPcHRpb25bXT4ge1xyXG4gICAgcmV0dXJuIChcclxuICAgICAgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJlxyXG4gICAgICB2YWx1ZSAhPT0gbnVsbCAmJlxyXG4gICAgICB0eXBlb2YgKHZhbHVlIGFzIFN1YnNjcmliYWJsZTxSZWZlcmVuY2VPcHRpb25bXT4pLnN1YnNjcmliZSA9PT0gJ2Z1bmN0aW9uJ1xyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8qKiBFeHBvc2VkIGZvciBjYXNjYWRlIGZpbHRlcmluZywgd2hpY2ggbWF0Y2hlcyBhIHBhcmVudCB2YWx1ZSBhZ2FpbnN0IG9wdGlvbiB2YWx1ZXMuICovXHJcbiAgbWF0Y2hlcyhhOiB1bmtub3duLCBiOiB1bmtub3duLCBsYW5nID0gJ2VuJyk6IGJvb2xlYW4ge1xyXG4gICAgcmV0dXJuIHZhbHVlc01hdGNoKGEsIGIsIGxhbmcpO1xyXG4gIH1cclxufVxyXG4iXX0=
|
|
@@ -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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmllbGQtcmVnaXN0cnkuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvc2VydmljZXMvZmllbGQtcmVnaXN0cnkuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFRLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN6RCxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsZUFBZSxFQUFFLE1BQU0sNEJBQTRCLENBQUM7O0FBRWxGOzs7Ozs7Ozs7R0FTRztBQUVILE1BQU0sT0FBTyxvQkFBb0I7SUFDZCxnQkFBZ0IsR0FDL0IsTUFBTSxDQUFDLG1CQUFtQixFQUFFLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxDQUFDLElBQUksSUFBSSxHQUFHLEVBQXFCLENBQUM7SUFDakUsZUFBZSxHQUFHLElBQUksR0FBRyxFQUFxQixDQUFDO0lBRWhFO1FBQ0UsNEZBQTRGO1FBQzVGLEtBQUssTUFBTSxHQUFHLElBQUksTUFBTSxDQUFDLGVBQWUsRUFBRSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDO1lBQ3BFLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDeEIsQ0FBQztJQUNILENBQUM7SUFFRCwwREFBMEQ7SUFDMUQsUUFBUSxDQUFDLElBQVksRUFBRSxTQUFvQjtRQUN6QyxJQUFJLENBQUMsZUFBZSxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsU0FBUyxDQUFDLENBQUM7SUFDNUMsQ0FBQztJQUVELGtFQUFrRTtJQUNsRSxXQUFXLENBQUMsR0FBOEI7UUFDeEMsS0FBSyxNQUFNLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUMvQyxJQUFJLENBQUMsZUFBZSxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDdkMsQ0FBQztJQUNILENBQUM7SUFFRDs7O09BR0c7SUFDSCxPQUFPLENBQUMsU0FBaUI7UUFDdkIsT0FBTyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxJQUFJLElBQUksQ0FBQyxlQUFlLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxJQUFJLElBQUksQ0FBQztJQUM3RixDQUFDO0lBRUQsd0RBQXdEO0lBQ3hELEdBQUcsQ0FBQyxTQUFpQjtRQUNuQixPQUFPLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLElBQUksSUFBSSxDQUFDLGVBQWUsQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDckYsQ0FBQztJQUVELDhGQUE4RjtJQUM5RixlQUFlO1FBQ2IsT0FBTyxDQUFDLEdBQUcsSUFBSSxHQUFHLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxlQUFlLENBQUMsSUFBSSxFQUFFLEVBQUUsR0FBRyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDaEcsQ0FBQzt3R0F4Q1Usb0JBQW9COzRHQUFwQixvQkFBb0IsY0FEUCxNQUFNOzs0RkFDbkIsb0JBQW9CO2tCQURoQyxVQUFVO21CQUFDLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUsIFR5cGUsIGluamVjdCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQgeyBGSUVMRF9UWVBFX1JFR0lTVFJZLCBGSUVMRF9UWVBFX1NFVFMgfSBmcm9tICcuLi90b2tlbnMvaW5qZWN0aW9uLXRva2Vucyc7XHJcblxyXG4vKipcclxuICogRmllbGRSZWdpc3RyeVNlcnZpY2Ug4oCUIHJlc29sdmVzIGZpZWxkIHR5cGUgc3RyaW5ncyB0byBBbmd1bGFyIGNvbXBvbmVudCBjbGFzc2VzLlxyXG4gKlxyXG4gKiAqKlRyZWUtc2hha2luZyBjb250cmFjdDoqKiB0aGlzIHNlcnZpY2UgaG9sZHMgKm5vKiBzdGF0aWMgaW1wb3J0cyBvZiBmaWVsZCBjb21wb25lbnRzLlxyXG4gKiBOb3RoaW5nIGlzIGJ1bmRsZWQgdW50aWwgc29tZXRoaW5nIHJlZ2lzdGVycyBpdCwgd2hpY2ggaXMgd2hhdCBtYWtlcyB0aGUgYnVpbHQtaW5zXHJcbiAqIG9wdC1pbi4gUmVnaXN0ZXIgdGhlbSB3aXRoIGBwcm92aWRlQnVpbHRJbkZpZWxkVHlwZXMoKWAgKGFsbCAxOSBrZXlzKSBvciBhIG5hcnJvd2VyXHJcbiAqIHN1YnNldCB2aWEgYHByb3ZpZGVGaWVsZFR5cGVzKHsgLi4uIH0pYCDigJQgc2VlIGBwcm92aWRlcnMvcHJvdmlkZS1maWVsZC10eXBlcy50c2AuXHJcbiAqXHJcbiAqIFJlc29sdXRpb24gb3JkZXI6IGNvbnN1bWVyIHRva2VuIHJlZ2lzdHJ5IOKGkiBydW50aW1lIHJlZ2lzdHJhdGlvbnMg4oaSIG5vdGhpbmcuXHJcbiAqL1xyXG5ASW5qZWN0YWJsZSh7IHByb3ZpZGVkSW46ICdyb290JyB9KVxyXG5leHBvcnQgY2xhc3MgRmllbGRSZWdpc3RyeVNlcnZpY2Uge1xyXG4gIHByaXZhdGUgcmVhZG9ubHkgY29uc3VtZXJSZWdpc3RyeSA9XHJcbiAgICBpbmplY3QoRklFTERfVFlQRV9SRUdJU1RSWSwgeyBvcHRpb25hbDogdHJ1ZSB9KSA/PyBuZXcgTWFwPHN0cmluZywgVHlwZTxhbnk+PigpO1xyXG4gIHByaXZhdGUgcmVhZG9ubHkgZHluYW1pY1JlZ2lzdHJ5ID0gbmV3IE1hcDxzdHJpbmcsIFR5cGU8YW55Pj4oKTtcclxuXHJcbiAgY29uc3RydWN0b3IoKSB7XHJcbiAgICAvLyBTZXRzIGNvbnRyaWJ1dGVkIGJ5IHByb3ZpZGVGaWVsZFR5cGVzKCkvcHJvdmlkZUJ1aWx0SW5GaWVsZFR5cGVzKCksIGluIGRlY2xhcmF0aW9uIG9yZGVyLlxyXG4gICAgZm9yIChjb25zdCBzZXQgb2YgaW5qZWN0KEZJRUxEX1RZUEVfU0VUUywgeyBvcHRpb25hbDogdHJ1ZSB9KSA/PyBbXSkge1xyXG4gICAgICB0aGlzLnJlZ2lzdGVyQWxsKHNldCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKiogUmVnaXN0ZXIgYSBmaWVsZCB0eXBlIGNvbXBvbmVudCBtYXBwaW5nIGF0IHJ1bnRpbWUuICovXHJcbiAgcmVnaXN0ZXIodHlwZTogc3RyaW5nLCBjb21wb25lbnQ6IFR5cGU8YW55Pik6IHZvaWQge1xyXG4gICAgdGhpcy5keW5hbWljUmVnaXN0cnkuc2V0KHR5cGUsIGNvbXBvbmVudCk7XHJcbiAgfVxyXG5cclxuICAvKiogUmVnaXN0ZXIgbXVsdGlwbGUgZmllbGQgdHlwZSBjb21wb25lbnQgbWFwcGluZ3MgYXQgcnVudGltZS4gKi9cclxuICByZWdpc3RlckFsbChtYXA6IFJlY29yZDxzdHJpbmcsIFR5cGU8YW55Pj4pOiB2b2lkIHtcclxuICAgIGZvciAoY29uc3QgW3R5cGUsIGNvbXBdIG9mIE9iamVjdC5lbnRyaWVzKG1hcCkpIHtcclxuICAgICAgdGhpcy5keW5hbWljUmVnaXN0cnkuc2V0KHR5cGUsIGNvbXApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogUmVzb2x2ZSBhIGZpZWxkIHR5cGUgc3RyaW5nIHRvIGEgY29tcG9uZW50IGNsYXNzLlxyXG4gICAqIENvbnN1bWVyIHRva2VuIHJlZ2lzdHJ5IHRha2VzIHByaW9yaXR5IG92ZXIgcnVudGltZSByZWdpc3RyYXRpb25zLlxyXG4gICAqL1xyXG4gIHJlc29sdmUoZmllbGRUeXBlOiBzdHJpbmcpOiBUeXBlPGFueT4gfCBudWxsIHtcclxuICAgIHJldHVybiB0aGlzLmNvbnN1bWVyUmVnaXN0cnkuZ2V0KGZpZWxkVHlwZSkgPz8gdGhpcy5keW5hbWljUmVnaXN0cnkuZ2V0KGZpZWxkVHlwZSkgPz8gbnVsbDtcclxuICB9XHJcblxyXG4gIC8qKiBDaGVjayBpZiBhIGZpZWxkIHR5cGUgaGFzIGEgcmVnaXN0ZXJlZCBjb21wb25lbnQuICovXHJcbiAgaGFzKGZpZWxkVHlwZTogc3RyaW5nKTogYm9vbGVhbiB7XHJcbiAgICByZXR1cm4gdGhpcy5jb25zdW1lclJlZ2lzdHJ5LmhhcyhmaWVsZFR5cGUpIHx8IHRoaXMuZHluYW1pY1JlZ2lzdHJ5LmhhcyhmaWVsZFR5cGUpO1xyXG4gIH1cclxuXHJcbiAgLyoqIEV2ZXJ5IHJlZ2lzdGVyZWQgZmllbGQgdHlwZSBrZXkuIENvbnN1bWVyIG92ZXJyaWRlcyBhbmQgcnVudGltZSByZWdpc3RyYXRpb25zIGNvbWJpbmVkLiAqL1xyXG4gIHJlZ2lzdGVyZWRUeXBlcygpOiBzdHJpbmdbXSB7XHJcbiAgICByZXR1cm4gWy4uLm5ldyBTZXQoWy4uLnRoaXMuZHluYW1pY1JlZ2lzdHJ5LmtleXMoKSwgLi4udGhpcy5jb25zdW1lclJlZ2lzdHJ5LmtleXMoKV0pXS5zb3J0KCk7XHJcbiAgfVxyXG59XHJcbiJdfQ==
|
|
@@ -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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZS11cGxvYWQuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvc2VydmljZXMvZmlsZS11cGxvYWQuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUVuRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sNEJBQTRCLENBQUM7O0FBRTVEOzs7Ozs7R0FNRztBQUVILE1BQU0sT0FBTyxpQkFBaUI7SUFDWCxPQUFPLEdBQUcsTUFBTSxDQUFDLGNBQWMsRUFBRSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBRXRFLElBQUksVUFBVTtRQUNaLE9BQU8sQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUM7SUFDeEIsQ0FBQztJQUVELDhEQUE4RDtJQUN0RCxRQUFRLENBQUMsSUFBVTtRQUN6QixPQUFPLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLEVBQUUsUUFBUSxFQUFFLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUNuRSxDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsS0FBSyxDQUFDLFNBQVMsQ0FBQyxJQUFVO1FBQ3hCLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDakMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPO1lBQUUsT0FBTyxFQUFFLEdBQUcsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDO1FBRTVDLE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDeEQsT0FBTyxFQUFFLEdBQUcsSUFBSSxFQUFFLEdBQUcsRUFBRSxNQUFNLENBQUMsR0FBRyxFQUFFLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSSxJQUFJLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUN0RSxDQUFDO0lBRUQsNkVBQTZFO0lBQzdFLGFBQWEsQ0FBQyxHQUErQjtRQUMzQyxJQUFJLENBQUMsR0FBRztZQUFFLE9BQU8sSUFBSSxDQUFDO1FBQ3RCLElBQUksR0FBRyxDQUFDLEdBQUc7WUFBRSxPQUFPLEdBQUcsQ0FBQyxHQUFHLENBQUM7UUFDNUIsSUFBSSxHQUFHLENBQUMsSUFBSSxJQUFJLE9BQU8sR0FBRyxLQUFLLFdBQVcsSUFBSSxPQUFPLEdBQUcsQ0FBQyxlQUFlLEtBQUssVUFBVSxFQUFFLENBQUM7WUFDeEYsT0FBTyxHQUFHLENBQUMsZUFBZSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN2QyxDQUFDO1FBQ0QsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQsZ0JBQWdCLENBQUMsR0FBa0I7UUFDakMsSUFBSSxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDO1lBQUUsT0FBTztRQUM3QyxJQUFJLE9BQU8sR0FBRyxLQUFLLFdBQVcsSUFBSSxPQUFPLEdBQUcsQ0FBQyxlQUFlLEtBQUssVUFBVSxFQUFFLENBQUM7WUFDNUUsR0FBRyxDQUFDLGVBQWUsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUMzQixDQUFDO0lBQ0gsQ0FBQztJQUVPLFNBQVMsQ0FDZixNQUF5RTtRQUV6RSxJQUFJLElBQUksQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztZQUNoQyxPQUFPLElBQUksT0FBTyxDQUFlLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFO2dCQUNuRCxJQUFJLE9BQU8sR0FBRyxLQUFLLENBQUM7Z0JBQ3BCLE1BQU0sQ0FBQyxTQUFTLENBQUM7b0JBQ2YsSUFBSSxFQUFFLEtBQUssQ0FBQyxFQUFFO3dCQUNaLElBQUksT0FBTzs0QkFBRSxPQUFPO3dCQUNwQixPQUFPLEdBQUcsSUFBSSxDQUFDO3dCQUNmLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQztvQkFDakIsQ0FBQztvQkFDRCxLQUFLLEVBQUUsR0FBRyxDQUFDLEVBQUU7d0JBQ1gsSUFBSSxPQUFPOzRCQUFFLE9BQU87d0JBQ3BCLE9BQU8sR0FBRyxJQUFJLENBQUM7d0JBQ2YsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO29CQUNkLENBQUM7b0JBQ0QsUUFBUSxFQUFFLEdBQUcsRUFBRTt3QkFDYixJQUFJLE9BQU87NEJBQUUsT0FBTzt3QkFDcEIsT0FBTyxHQUFHLElBQUksQ0FBQzt3QkFDZixNQUFNLENBQUMsSUFBSSxLQUFLLENBQUMscURBQXFELENBQUMsQ0FBQyxDQUFDO29CQUMzRSxDQUFDO2lCQUNGLENBQUMsQ0FBQztZQUNMLENBQUMsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUNELE9BQU8sT0FBTyxDQUFDLE9BQU8sQ0FBQyxNQUE4QyxDQUFDLENBQUM7SUFDekUsQ0FBQztJQUVPLGNBQWMsQ0FBQyxLQUFjO1FBQ25DLE9BQU8sQ0FDTCxPQUFPLEtBQUssS0FBSyxRQUFRO1lBQ3pCLEtBQUssS0FBSyxJQUFJO1lBQ2QsT0FBUSxLQUFvQyxDQUFDLFNBQVMsS0FBSyxVQUFVLENBQ3RFLENBQUM7SUFDSixDQUFDO3dHQTNFVSxpQkFBaUI7NEdBQWpCLGlCQUFpQixjQURKLE1BQU07OzRGQUNuQixpQkFBaUI7a0JBRDdCLFVBQVU7bUJBQUMsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSwgaW5qZWN0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB0eXBlIHsgRmlsZVJlZiwgU3Vic2NyaWJhYmxlLCBVcGxvYWRSZXN1bHQgfSBmcm9tICdAZHluYW1pYy1lbnRpdHkvY29yZSc7XHJcbmltcG9ydCB7IFVQTE9BRF9IQU5ETEVSIH0gZnJvbSAnLi4vdG9rZW5zL2luamVjdGlvbi10b2tlbnMnO1xyXG5cclxuLyoqXHJcbiAqIEZpbGVVcGxvYWRTZXJ2aWNlIOKAlCB0aGUgc2luZ2xlIHBsYWNlIGltYWdlLWZpZWxkIGFuZCBmaWxlLWZpZWxkIGFncmVlIG9uIGhvdyBhXHJcbiAqIHNlbGVjdGVkIGBGaWxlYCBiZWNvbWVzIGEgYEZpbGVSZWZgLlxyXG4gKlxyXG4gKiBXaXRoIGBVUExPQURfSEFORExFUmAgcmVnaXN0ZXJlZCB0aGUgZmlsZSBpcyBwZXJzaXN0ZWQgYW5kIHN0b3JlZCBhcyBgeyB1cmwgfWA7XHJcbiAqIHdpdGhvdXQgb25lIGl0IGlzIHN0b3JlZCBhcyBgeyBmaWxlIH1gIGZvciB0aGUgY29uc3VtZXIgdG8gdXBsb2FkIGF0IHN1Ym1pdCB0aW1lLlxyXG4gKi9cclxuQEluamVjdGFibGUoeyBwcm92aWRlZEluOiAncm9vdCcgfSlcclxuZXhwb3J0IGNsYXNzIEZpbGVVcGxvYWRTZXJ2aWNlIHtcclxuICBwcml2YXRlIHJlYWRvbmx5IGhhbmRsZXIgPSBpbmplY3QoVVBMT0FEX0hBTkRMRVIsIHsgb3B0aW9uYWw6IHRydWUgfSk7XHJcblxyXG4gIGdldCBoYXNIYW5kbGVyKCk6IGJvb2xlYW4ge1xyXG4gICAgcmV0dXJuICEhdGhpcy5oYW5kbGVyO1xyXG4gIH1cclxuXHJcbiAgLyoqIE1ldGFkYXRhIGNvbW1vbiB0byBib3RoIHBlcnNpc3RlZCBhbmQgdW5wZXJzaXN0ZWQgcmVmcy4gKi9cclxuICBwcml2YXRlIGRlc2NyaWJlKGZpbGU6IEZpbGUpOiBGaWxlUmVmIHtcclxuICAgIHJldHVybiB7IG5hbWU6IGZpbGUubmFtZSwgc2l6ZTogZmlsZS5zaXplLCBtaW1lVHlwZTogZmlsZS50eXBlIH07XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBUdXJuIGEgc2VsZWN0ZWQgRmlsZSBpbnRvIHRoZSB2YWx1ZSB0byBzdG9yZSBvbiB0aGUgY29udHJvbC5cclxuICAgKiBAdGhyb3dzIHdoZW4gYSByZWdpc3RlcmVkIGhhbmRsZXIgcmVqZWN0cyDigJQgY2FsbGVycyBzdXJmYWNlIGl0IGFzIGEgZmllbGQgZXJyb3IuXHJcbiAgICovXHJcbiAgYXN5bmMgdG9GaWxlUmVmKGZpbGU6IEZpbGUpOiBQcm9taXNlPEZpbGVSZWY+IHtcclxuICAgIGNvbnN0IG1ldGEgPSB0aGlzLmRlc2NyaWJlKGZpbGUpO1xyXG4gICAgaWYgKCF0aGlzLmhhbmRsZXIpIHJldHVybiB7IC4uLm1ldGEsIGZpbGUgfTtcclxuXHJcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB0aGlzLm5vcm1hbGl6ZSh0aGlzLmhhbmRsZXIoZmlsZSkpO1xyXG4gICAgcmV0dXJuIHsgLi4ubWV0YSwgdXJsOiByZXN1bHQudXJsLCBuYW1lOiByZXN1bHQubmFtZSA/PyBtZXRhLm5hbWUgfTtcclxuICB9XHJcblxyXG4gIC8qKiBPYmplY3QgVVJMIGZvciBwcmV2aWV3aW5nIGFuIHVucGVyc2lzdGVkIEZpbGUuIENhbGxlcnMgbXVzdCByZXZva2UgaXQuICovXHJcbiAgcHJldmlld1VybEZvcihyZWY6IEZpbGVSZWYgfCBudWxsIHwgdW5kZWZpbmVkKTogc3RyaW5nIHwgbnVsbCB7XHJcbiAgICBpZiAoIXJlZikgcmV0dXJuIG51bGw7XHJcbiAgICBpZiAocmVmLnVybCkgcmV0dXJuIHJlZi51cmw7XHJcbiAgICBpZiAocmVmLmZpbGUgJiYgdHlwZW9mIFVSTCAhPT0gJ3VuZGVmaW5lZCcgJiYgdHlwZW9mIFVSTC5jcmVhdGVPYmplY3RVUkwgPT09ICdmdW5jdGlvbicpIHtcclxuICAgICAgcmV0dXJuIFVSTC5jcmVhdGVPYmplY3RVUkwocmVmLmZpbGUpO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIG51bGw7XHJcbiAgfVxyXG5cclxuICByZXZva2VQcmV2aWV3VXJsKHVybDogc3RyaW5nIHwgbnVsbCk6IHZvaWQge1xyXG4gICAgaWYgKCF1cmwgfHwgIXVybC5zdGFydHNXaXRoKCdibG9iOicpKSByZXR1cm47XHJcbiAgICBpZiAodHlwZW9mIFVSTCAhPT0gJ3VuZGVmaW5lZCcgJiYgdHlwZW9mIFVSTC5yZXZva2VPYmplY3RVUkwgPT09ICdmdW5jdGlvbicpIHtcclxuICAgICAgVVJMLnJldm9rZU9iamVjdFVSTCh1cmwpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBub3JtYWxpemUoXHJcbiAgICByZXN1bHQ6IFVwbG9hZFJlc3VsdCB8IFByb21pc2U8VXBsb2FkUmVzdWx0PiB8IFN1YnNjcmliYWJsZTxVcGxvYWRSZXN1bHQ+LFxyXG4gICk6IFByb21pc2U8VXBsb2FkUmVzdWx0PiB7XHJcbiAgICBpZiAodGhpcy5pc1N1YnNjcmliYWJsZShyZXN1bHQpKSB7XHJcbiAgICAgIHJldHVybiBuZXcgUHJvbWlzZTxVcGxvYWRSZXN1bHQ+KChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgICBsZXQgc2V0dGxlZCA9IGZhbHNlO1xyXG4gICAgICAgIHJlc3VsdC5zdWJzY3JpYmUoe1xyXG4gICAgICAgICAgbmV4dDogdmFsdWUgPT4ge1xyXG4gICAgICAgICAgICBpZiAoc2V0dGxlZCkgcmV0dXJuO1xyXG4gICAgICAgICAgICBzZXR0bGVkID0gdHJ1ZTtcclxuICAgICAgICAgICAgcmVzb2x2ZSh2YWx1ZSk7XHJcbiAgICAgICAgICB9LFxyXG4gICAgICAgICAgZXJyb3I6IGVyciA9PiB7XHJcbiAgICAgICAgICAgIGlmIChzZXR0bGVkKSByZXR1cm47XHJcbiAgICAgICAgICAgIHNldHRsZWQgPSB0cnVlO1xyXG4gICAgICAgICAgICByZWplY3QoZXJyKTtcclxuICAgICAgICAgIH0sXHJcbiAgICAgICAgICBjb21wbGV0ZTogKCkgPT4ge1xyXG4gICAgICAgICAgICBpZiAoc2V0dGxlZCkgcmV0dXJuO1xyXG4gICAgICAgICAgICBzZXR0bGVkID0gdHJ1ZTtcclxuICAgICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcignVXBsb2FkIGhhbmRsZXIgY29tcGxldGVkIHdpdGhvdXQgZW1pdHRpbmcgYSByZXN1bHQuJykpO1xyXG4gICAgICAgICAgfSxcclxuICAgICAgICB9KTtcclxuICAgICAgfSk7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHJlc3VsdCBhcyBVcGxvYWRSZXN1bHQgfCBQcm9taXNlPFVwbG9hZFJlc3VsdD4pO1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBpc1N1YnNjcmliYWJsZSh2YWx1ZTogdW5rbm93bik6IHZhbHVlIGlzIFN1YnNjcmliYWJsZTxVcGxvYWRSZXN1bHQ+IHtcclxuICAgIHJldHVybiAoXHJcbiAgICAgIHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcgJiZcclxuICAgICAgdmFsdWUgIT09IG51bGwgJiZcclxuICAgICAgdHlwZW9mICh2YWx1ZSBhcyBTdWJzY3JpYmFibGU8VXBsb2FkUmVzdWx0Pikuc3Vic2NyaWJlID09PSAnZnVuY3Rpb24nXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG4iXX0=
|