@sdcorejs/angular 21.1.2 → 21.1.4
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 +127 -807
- package/fesm2022/sdcorejs-angular-components-anchor.mjs +14 -14
- package/fesm2022/sdcorejs-angular-components-anchor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-audit-diff.mjs +332 -0
- package/fesm2022/sdcorejs-angular-components-audit-diff.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-badge.mjs +1 -0
- package/fesm2022/sdcorejs-angular-components-badge.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-breadcrumb.mjs +121 -0
- package/fesm2022/sdcorejs-angular-components-breadcrumb.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-data-state.mjs +68 -0
- package/fesm2022/sdcorejs-angular-components-data-state.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-document-builder.mjs +18 -23
- package/fesm2022/sdcorejs-angular-components-document-builder.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-editor.mjs +6 -4
- package/fesm2022/sdcorejs-angular-components-editor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-form-generic.mjs +633 -886
- package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-import-excel.mjs +26 -41
- package/fesm2022/sdcorejs-angular-components-import-excel.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-job-progress.mjs +97 -0
- package/fesm2022/sdcorejs-angular-components-job-progress.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-mini-editor.mjs +10 -20
- package/fesm2022/sdcorejs-angular-components-mini-editor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-modal.mjs +61 -4
- package/fesm2022/sdcorejs-angular-components-modal.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-preview.mjs +1438 -218
- package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-query-bar.mjs +9 -17
- package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-query-builder.mjs +1 -1
- package/fesm2022/sdcorejs-angular-components-query-builder.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +39 -2
- package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tab-router.mjs +76 -52
- package/fesm2022/sdcorejs-angular-components-tab-router.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tab.mjs +48 -4
- package/fesm2022/sdcorejs-angular-components-tab.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-table.mjs +123 -89
- package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tree.mjs +188 -18
- package/fesm2022/sdcorejs-angular-components-tree.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-upload-file.mjs +17 -23
- package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components.mjs +4 -0
- package/fesm2022/sdcorejs-angular-components.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-directives.mjs +29 -41
- package/fesm2022/sdcorejs-angular-directives.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +9 -8
- package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-checkbox.mjs +20 -71
- package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +8 -4
- package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-chip.mjs +8 -4
- package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-date-range.mjs +18 -14
- package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-date.mjs +48 -70
- package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-datetime.mjs +62 -528
- package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs +384 -0
- package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-input-color.mjs +5 -3
- package/fesm2022/sdcorejs-angular-forms-input-color.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input-number.mjs +16 -11
- package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input.mjs +297 -59
- package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-models.mjs +155 -2
- package/fesm2022/sdcorejs-angular-forms-models.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-radio.mjs +44 -66
- package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-select.mjs +8 -21
- package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-switch.mjs +19 -64
- package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-textarea.mjs +8 -6
- package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-time-range.mjs +277 -0
- package/fesm2022/sdcorejs-angular-forms-time-range.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-time.mjs +307 -0
- package/fesm2022/sdcorejs-angular-forms-time.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-tree-select.mjs +210 -0
- package/fesm2022/sdcorejs-angular-forms-tree-select.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms.mjs +73 -4
- package/fesm2022/sdcorejs-angular-forms.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-i18n.mjs +355 -10
- package/fesm2022/sdcorejs-angular-i18n.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-interceptors.mjs +12 -17
- package/fesm2022/sdcorejs-angular-interceptors.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-icon.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-layout.mjs +861 -80
- package/fesm2022/sdcorejs-angular-modules-layout.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules.mjs +0 -1
- package/fesm2022/sdcorejs-angular-modules.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-pipes.mjs +5 -7
- package/fesm2022/sdcorejs-angular-pipes.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-api.mjs +407 -137
- package/fesm2022/sdcorejs-angular-services-api.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-cache.mjs +515 -105
- package/fesm2022/sdcorejs-angular-services-cache.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-confirm.mjs +5 -7
- package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-docx.mjs +7 -10
- package/fesm2022/sdcorejs-angular-services-docx.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-loading.mjs +340 -66
- package/fesm2022/sdcorejs-angular-services-loading.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-notify.mjs +7 -10
- package/fesm2022/sdcorejs-angular-services-notify.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-persistence.mjs +899 -0
- package/fesm2022/sdcorejs-angular-services-persistence.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services-storage.mjs +324 -122
- package/fesm2022/sdcorejs-angular-services-storage.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-task.mjs +389 -0
- package/fesm2022/sdcorejs-angular-services-task.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services-unsaved-changes.mjs +255 -0
- package/fesm2022/sdcorejs-angular-services-unsaved-changes.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services-viewport.mjs +76 -0
- package/fesm2022/sdcorejs-angular-services-viewport.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services.mjs +4 -0
- package/fesm2022/sdcorejs-angular-services.mjs.map +1 -1
- package/package.json +50 -5
- package/types/sdcorejs-angular-components-audit-diff.d.ts +74 -0
- package/types/sdcorejs-angular-components-badge.d.ts +1 -1
- package/types/sdcorejs-angular-components-breadcrumb.d.ts +53 -0
- package/types/sdcorejs-angular-components-data-state.d.ts +44 -0
- package/types/sdcorejs-angular-components-document-builder.d.ts +5 -5
- package/types/sdcorejs-angular-components-form-generic.d.ts +89 -103
- package/types/sdcorejs-angular-components-import-excel.d.ts +4 -14
- package/types/sdcorejs-angular-components-job-progress.d.ts +38 -0
- package/types/sdcorejs-angular-components-mini-editor.d.ts +9 -9
- package/types/sdcorejs-angular-components-modal.d.ts +7 -1
- package/types/sdcorejs-angular-components-preview.d.ts +181 -28
- package/types/sdcorejs-angular-components-side-drawer.d.ts +7 -1
- package/types/sdcorejs-angular-components-tab.d.ts +9 -2
- package/types/sdcorejs-angular-components-table.d.ts +8 -7
- package/types/sdcorejs-angular-components-tree.d.ts +24 -2
- package/types/sdcorejs-angular-components-upload-file.d.ts +5 -4
- package/types/sdcorejs-angular-components.d.ts +4 -0
- package/types/sdcorejs-angular-directives.d.ts +10 -8
- package/types/sdcorejs-angular-forms-autocomplete.d.ts +3 -4
- package/types/sdcorejs-angular-forms-checkbox.d.ts +4 -8
- package/types/sdcorejs-angular-forms-date-range.d.ts +6 -7
- package/types/sdcorejs-angular-forms-date.d.ts +3 -3
- package/types/sdcorejs-angular-forms-datetime.d.ts +3 -3
- package/types/sdcorejs-angular-forms-entity-picker.d.ts +128 -0
- package/types/sdcorejs-angular-forms-input-color.d.ts +3 -1
- package/types/sdcorejs-angular-forms-input-number.d.ts +4 -2
- package/types/sdcorejs-angular-forms-input.d.ts +76 -11
- package/types/sdcorejs-angular-forms-models.d.ts +86 -3
- package/types/sdcorejs-angular-forms-radio.d.ts +12 -8
- package/types/sdcorejs-angular-forms-switch.d.ts +4 -7
- package/types/sdcorejs-angular-forms-time-range.d.ts +98 -0
- package/types/sdcorejs-angular-forms-time.d.ts +117 -0
- package/types/sdcorejs-angular-forms-tree-select.d.ts +76 -0
- package/types/sdcorejs-angular-forms.d.ts +15 -5
- package/types/sdcorejs-angular-i18n.d.ts +68 -2
- package/types/sdcorejs-angular-interceptors.d.ts +5 -6
- package/types/sdcorejs-angular-modules-icon.d.ts +2 -3
- package/types/sdcorejs-angular-modules-layout.d.ts +288 -15
- package/types/sdcorejs-angular-modules.d.ts +0 -1
- package/types/sdcorejs-angular-pipes.d.ts +2 -2
- package/types/sdcorejs-angular-services-api.d.ts +68 -24
- package/types/sdcorejs-angular-services-cache.d.ts +52 -16
- package/types/sdcorejs-angular-services-confirm.d.ts +3 -2
- package/types/sdcorejs-angular-services-docx.d.ts +2 -3
- package/types/sdcorejs-angular-services-loading.d.ts +16 -8
- package/types/sdcorejs-angular-services-persistence.d.ts +159 -0
- package/types/sdcorejs-angular-services-storage.d.ts +26 -8
- package/types/sdcorejs-angular-services-task.d.ts +104 -0
- package/types/sdcorejs-angular-services-unsaved-changes.d.ts +83 -0
- package/types/sdcorejs-angular-services-viewport.d.ts +42 -0
- package/types/sdcorejs-angular-services.d.ts +4 -0
- package/fesm2022/sdcorejs-angular-modules-authom.mjs +0 -360
- package/fesm2022/sdcorejs-angular-modules-authom.mjs.map +0 -1
- package/types/sdcorejs-angular-modules-authom.d.ts +0 -70
|
@@ -1,155 +1,565 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { InjectionToken, inject, DestroyRef, Injectable } from '@angular/core';
|
|
3
|
+
import { SD_PERSISTENCE_STORAGE_ADAPTER, SdGraphSerializer, SdGraphIdentityCanonicalizer, canonicalizeSdPersistenceValue, buildSdPersistenceKey, digestSdPersistenceKey, readSdPersistenceStorageItem, parseSdPersistenceEnvelope, stringifySdPersistenceValueEnvelope, stringifySdPersistenceTombstoneEnvelope } from '@sdcorejs/angular/services/persistence';
|
|
4
4
|
import { Utilities } from '@sdcorejs/utils/fns';
|
|
5
5
|
import { BehaviorSubject } from 'rxjs';
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
const SD_CACHE_CONFIG = new InjectionToken('sd-cache.configuration');
|
|
8
|
+
/** @deprecated Prefer globally safe unknown callbacks on `ISdCacheConfiguration`. */
|
|
9
|
+
function adaptLegacySdCacheCallbacks(callbacks) {
|
|
10
|
+
return {
|
|
11
|
+
set: callbacks.set
|
|
12
|
+
? (key, value, option) => {
|
|
13
|
+
if (!callbacks.matches(key, option))
|
|
14
|
+
return;
|
|
15
|
+
if (!callbacks.isValue(value.data))
|
|
16
|
+
throw new TypeError(`Legacy cache value did not match key ${key}`);
|
|
17
|
+
return callbacks.set?.(key, { data: value.data, createdOn: new Date(value.createdOn) }, option);
|
|
18
|
+
}
|
|
19
|
+
: undefined,
|
|
20
|
+
get: callbacks.get
|
|
21
|
+
? async (key, option) => {
|
|
22
|
+
if (!callbacks.matches(key, option))
|
|
23
|
+
return undefined;
|
|
24
|
+
const value = await callbacks.get?.(key, option);
|
|
25
|
+
if (!value)
|
|
26
|
+
return undefined;
|
|
27
|
+
if (!callbacks.isValue(value.data))
|
|
28
|
+
throw new TypeError(`Legacy cache value did not match key ${key}`);
|
|
29
|
+
return { data: value.data, createdOn: new Date(value.createdOn) };
|
|
30
|
+
}
|
|
31
|
+
: undefined,
|
|
32
|
+
remove: callbacks.remove
|
|
33
|
+
? (key, option) => {
|
|
34
|
+
if (!callbacks.matches(key, option))
|
|
35
|
+
return;
|
|
36
|
+
return callbacks.remove?.(key, option);
|
|
37
|
+
}
|
|
38
|
+
: undefined,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
7
41
|
|
|
8
42
|
class SdCacheService {
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
|
|
43
|
+
#configuration = inject(SD_CACHE_CONFIG, { optional: true });
|
|
44
|
+
#adapter = inject(SD_PERSISTENCE_STORAGE_ADAPTER);
|
|
45
|
+
#destroyRef = inject(DestroyRef);
|
|
46
|
+
#defaultSerializer = new SdGraphSerializer();
|
|
47
|
+
#defaultIdentityCanonicalizer = new SdGraphIdentityCanonicalizer();
|
|
48
|
+
#states = new Map();
|
|
49
|
+
#remoteQueues = new Map();
|
|
50
|
+
#memoryEntries = new Map();
|
|
51
|
+
#failedOwners = new Map();
|
|
52
|
+
#maxFailedOwners = 256;
|
|
53
|
+
#destroyed = false;
|
|
54
|
+
constructor() {
|
|
55
|
+
this.#destroyRef.onDestroy(() => {
|
|
56
|
+
this.#destroyed = true;
|
|
57
|
+
for (const state of this.#states.values()) {
|
|
58
|
+
state.disposed = true;
|
|
59
|
+
state.generation += 1;
|
|
60
|
+
state.entry = undefined;
|
|
61
|
+
state.inFlight = undefined;
|
|
62
|
+
for (const handle of state.handles)
|
|
63
|
+
handle.complete();
|
|
64
|
+
state.handles.clear();
|
|
65
|
+
}
|
|
66
|
+
this.#states.clear();
|
|
67
|
+
this.#remoteQueues.clear();
|
|
68
|
+
this.#memoryEntries.clear();
|
|
69
|
+
this.#failedOwners.clear();
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
create(key, option = {}) {
|
|
73
|
+
if (this.#destroyed)
|
|
74
|
+
throw new Error('Cache service has been destroyed');
|
|
13
75
|
if (!key)
|
|
14
76
|
throw new Error('Key is required');
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
if (
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
77
|
+
const normalized = this.#normalize(key, option);
|
|
78
|
+
const state = this.#getOrCreateState(normalized);
|
|
79
|
+
if (Object.prototype.hasOwnProperty.call(option, 'default')) {
|
|
80
|
+
return this.#createDefaultHandle(state, normalized, this.#cloneAs(normalized.serializer, option.default));
|
|
81
|
+
}
|
|
82
|
+
return this.#createOptionalHandle(state, normalized);
|
|
83
|
+
}
|
|
84
|
+
#getOrCreateState(normalized) {
|
|
85
|
+
const existing = this.#states.get(normalized.ownerDigest);
|
|
86
|
+
if (existing)
|
|
87
|
+
return existing;
|
|
88
|
+
const state = {
|
|
89
|
+
hydrated: false,
|
|
90
|
+
handles: new Set(),
|
|
91
|
+
generation: 0,
|
|
92
|
+
disposed: false,
|
|
93
|
+
};
|
|
94
|
+
this.#states.set(normalized.ownerDigest, state);
|
|
95
|
+
this.#hydrateState(state, normalized);
|
|
96
|
+
return state;
|
|
97
|
+
}
|
|
98
|
+
#createOptionalHandle(state, normalized) {
|
|
99
|
+
const initial = this.#readOptional(state, normalized);
|
|
100
|
+
const subject = new BehaviorSubject(initial);
|
|
101
|
+
const facade = {
|
|
102
|
+
emit: (present, value) => subject.next(present ? this.#cloneAs(normalized.serializer, value) : undefined),
|
|
103
|
+
complete: () => subject.complete(),
|
|
104
|
+
};
|
|
105
|
+
const operations = this.#attachHandle(state, normalized, facade, () => this.#readOptional(state, normalized));
|
|
106
|
+
return { ...operations, observer: subject.asObservable() };
|
|
107
|
+
}
|
|
108
|
+
#createDefaultHandle(state, normalized, defaultSnapshot) {
|
|
109
|
+
const read = () => this.#readWithDefault(state, normalized, defaultSnapshot);
|
|
110
|
+
const subject = new BehaviorSubject(read());
|
|
111
|
+
const facade = {
|
|
112
|
+
emit: (present, value) => subject.next(present ? this.#cloneAs(normalized.serializer, value) : this.#cloneAs(normalized.serializer, defaultSnapshot)),
|
|
113
|
+
complete: () => subject.complete(),
|
|
114
|
+
};
|
|
115
|
+
const operations = this.#attachHandle(state, normalized, facade, read);
|
|
116
|
+
return { ...operations, get: read, observer: subject.asObservable() };
|
|
117
|
+
}
|
|
118
|
+
#attachHandle(state, normalized, facade, read) {
|
|
119
|
+
state.handles.add(facade);
|
|
120
|
+
let active = true;
|
|
121
|
+
const assertActive = () => {
|
|
122
|
+
if (!active || state.disposed || this.#destroyed)
|
|
123
|
+
throw new Error('Cache handle has been destroyed');
|
|
124
|
+
};
|
|
23
125
|
const get = () => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
126
|
+
assertActive();
|
|
127
|
+
return read();
|
|
128
|
+
};
|
|
129
|
+
const snapshot = () => {
|
|
130
|
+
assertActive();
|
|
131
|
+
const entry = this.#getValidEntry(state, normalized);
|
|
132
|
+
return entry === undefined ? { present: false } : { present: true, value: this.#cloneAs(normalized.serializer, entry.data) };
|
|
27
133
|
};
|
|
28
134
|
const set = (data) => {
|
|
29
|
-
|
|
30
|
-
|
|
135
|
+
assertActive();
|
|
136
|
+
this.#setEntry(state, normalized, data, true);
|
|
137
|
+
this.#broadcast(state);
|
|
31
138
|
};
|
|
32
139
|
const has = () => {
|
|
33
|
-
|
|
140
|
+
assertActive();
|
|
141
|
+
return this.#getValidEntry(state, normalized) !== undefined;
|
|
34
142
|
};
|
|
35
143
|
const remove = () => {
|
|
36
|
-
|
|
37
|
-
|
|
144
|
+
assertActive();
|
|
145
|
+
this.#removeEntry(state, normalized, true);
|
|
146
|
+
this.#broadcast(state);
|
|
147
|
+
};
|
|
148
|
+
const releaseFacade = () => {
|
|
149
|
+
if (!active)
|
|
150
|
+
return;
|
|
151
|
+
active = false;
|
|
152
|
+
state.handles.delete(facade);
|
|
153
|
+
facade.complete();
|
|
154
|
+
if (state.handles.size > 0)
|
|
155
|
+
return;
|
|
156
|
+
state.disposed = true;
|
|
157
|
+
state.generation += 1;
|
|
158
|
+
state.entry = undefined;
|
|
159
|
+
state.inFlight = undefined;
|
|
160
|
+
this.#states.delete(normalized.ownerDigest);
|
|
38
161
|
};
|
|
162
|
+
const release = () => releaseFacade();
|
|
39
163
|
const destroy = () => {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
164
|
+
if (!active)
|
|
165
|
+
return;
|
|
166
|
+
releaseFacade();
|
|
167
|
+
if (normalized.area !== 'memory')
|
|
168
|
+
return;
|
|
169
|
+
this.#memoryEntries.delete(normalized.ownerDigest);
|
|
170
|
+
if (state.disposed)
|
|
171
|
+
return;
|
|
172
|
+
state.entry = undefined;
|
|
173
|
+
state.hydrated = true;
|
|
174
|
+
this.#broadcast(state);
|
|
43
175
|
};
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (has()) {
|
|
48
|
-
return get();
|
|
176
|
+
const load = (callback) => {
|
|
177
|
+
try {
|
|
178
|
+
assertActive();
|
|
49
179
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
// Chỉ lưu nếu kết quả hợp lệ (khác undefined/null)
|
|
53
|
-
if (result !== undefined && result !== null) {
|
|
54
|
-
set(result);
|
|
180
|
+
catch (error) {
|
|
181
|
+
return Promise.reject(error);
|
|
55
182
|
}
|
|
56
|
-
|
|
183
|
+
const entry = this.#getValidEntry(state, normalized);
|
|
184
|
+
if (entry)
|
|
185
|
+
return Promise.resolve(this.#cloneAs(normalized.serializer, entry.data));
|
|
186
|
+
const generation = state.generation;
|
|
187
|
+
if (state.inFlight?.generation === generation) {
|
|
188
|
+
return state.inFlight.promise.then(value => this.#cloneAs(normalized.serializer, value));
|
|
189
|
+
}
|
|
190
|
+
const promise = this.#loadMiss(state, normalized, callback, generation);
|
|
191
|
+
state.inFlight = { generation, promise };
|
|
192
|
+
void promise
|
|
193
|
+
.finally(() => {
|
|
194
|
+
if (state.inFlight?.promise === promise)
|
|
195
|
+
state.inFlight = undefined;
|
|
196
|
+
})
|
|
197
|
+
.catch(() => undefined);
|
|
198
|
+
return promise;
|
|
57
199
|
};
|
|
200
|
+
return { get, snapshot, set, has, remove, release, destroy, load };
|
|
201
|
+
}
|
|
202
|
+
async #loadMiss(state, normalized, callback, generation) {
|
|
203
|
+
const remote = await this.#readRemote(normalized);
|
|
204
|
+
if (remote) {
|
|
205
|
+
if (!state.disposed && state.generation === generation) {
|
|
206
|
+
state.entry = this.#cloneEntry(remote, normalized.serializer);
|
|
207
|
+
state.hydrated = true;
|
|
208
|
+
state.generation += 1;
|
|
209
|
+
this.#writePersistent(normalized, state.entry);
|
|
210
|
+
this.#broadcast(state);
|
|
211
|
+
}
|
|
212
|
+
return this.#cloneAs(normalized.serializer, remote.data);
|
|
213
|
+
}
|
|
214
|
+
const result = await callback();
|
|
215
|
+
if (result !== null && result !== undefined && !state.disposed && state.generation === generation) {
|
|
216
|
+
this.#setEntry(state, normalized, result, true);
|
|
217
|
+
this.#broadcast(state);
|
|
218
|
+
}
|
|
219
|
+
if (result === null || result === undefined)
|
|
220
|
+
return result;
|
|
221
|
+
return this.#cloneAs(normalized.serializer, result);
|
|
222
|
+
}
|
|
223
|
+
#normalize(key, option) {
|
|
224
|
+
const serializer = option.serializer ?? this.#configuration?.serializer ?? this.#defaultSerializer;
|
|
225
|
+
const identityCanonicalizer = option.identityCanonicalizer ?? this.#configuration?.identityCanonicalizer ?? this.#defaultIdentityCanonicalizer;
|
|
226
|
+
const canonicalKey = canonicalizeSdPersistenceValue(identityCanonicalizer, key, 'cache key');
|
|
227
|
+
const legacyStorageKey = this.#legacyStorageKey(key);
|
|
228
|
+
const namespace = option.namespace ?? this.#configuration?.namespace;
|
|
229
|
+
const version = option.version ?? this.#configuration?.version;
|
|
230
|
+
const area = option.type ?? 'memory';
|
|
231
|
+
const ttlMs = this.#normalizeTtl(option);
|
|
232
|
+
const serializerFormat = this.#serializerFormat(serializer);
|
|
233
|
+
const hasArgs = Object.prototype.hasOwnProperty.call(option, 'args');
|
|
234
|
+
const hasDefault = Object.prototype.hasOwnProperty.call(option, 'default');
|
|
235
|
+
const fields = [
|
|
236
|
+
{ tag: 'area', value: canonicalizeSdPersistenceValue(identityCanonicalizer, area, 'cache area') },
|
|
237
|
+
{ tag: 'namespace', value: this.#optionalIdentity(identityCanonicalizer, namespace !== undefined, namespace, 'cache namespace') },
|
|
238
|
+
{ tag: 'version', value: this.#optionalIdentity(identityCanonicalizer, version !== undefined, version, 'cache version') },
|
|
239
|
+
{ tag: 'ttlMs', value: this.#optionalIdentity(identityCanonicalizer, ttlMs !== undefined, ttlMs, 'cache TTL') },
|
|
240
|
+
{ tag: 'args', value: this.#optionalIdentity(identityCanonicalizer, hasArgs, option.args, 'cache args') },
|
|
241
|
+
{ tag: 'default', value: this.#optionalIdentity(identityCanonicalizer, hasDefault, option.default, 'cache default') },
|
|
242
|
+
{ tag: 'serializer', value: serializerFormat },
|
|
243
|
+
{ tag: 'identityCanonicalizer', value: this.#identityCanonicalizerFormat(identityCanonicalizer) },
|
|
244
|
+
];
|
|
245
|
+
const identity = buildSdPersistenceKey(canonicalKey, fields);
|
|
246
|
+
const identityDigest = digestSdPersistenceKey(identity);
|
|
247
|
+
const storageKey = this.#convertKey(`sdcorejs.cache@1:${identityDigest}`);
|
|
248
|
+
const ownerDigest = digestSdPersistenceKey(buildSdPersistenceKey(storageKey, [{ tag: 'area', value: area }]));
|
|
58
249
|
return {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
250
|
+
publicOption: option,
|
|
251
|
+
area,
|
|
252
|
+
storageKey,
|
|
253
|
+
legacyStorageKey,
|
|
254
|
+
ownerDigest,
|
|
255
|
+
identityDigest,
|
|
256
|
+
ttlMs,
|
|
257
|
+
serializer,
|
|
66
258
|
};
|
|
67
259
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
#
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const storage = this.#getStorage(option);
|
|
80
|
-
if (storage) {
|
|
81
|
-
const raw = storage.getItem(key);
|
|
82
|
-
if (raw) {
|
|
83
|
-
try {
|
|
84
|
-
const parsed = JSON.parse(raw);
|
|
85
|
-
entry = {
|
|
86
|
-
data: parsed.data,
|
|
87
|
-
createdOn: new Date(parsed.createdOn),
|
|
88
|
-
};
|
|
89
|
-
this.#memoryCache.set(key, entry);
|
|
90
|
-
}
|
|
91
|
-
catch (e) {
|
|
92
|
-
this.#internalRemove(key, option);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
260
|
+
#convertKey(baseKey) {
|
|
261
|
+
try {
|
|
262
|
+
return this.#configuration?.convertKey?.(baseKey) ?? baseKey;
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
return baseKey;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
#legacyStorageKey(key) {
|
|
269
|
+
try {
|
|
270
|
+
return this.#convertKey(Utilities.hash({ key }));
|
|
96
271
|
}
|
|
97
|
-
|
|
272
|
+
catch {
|
|
273
|
+
return undefined;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
#normalizeTtl(option) {
|
|
277
|
+
const candidate = option.ttlMs ?? (option.hours === undefined ? undefined : option.hours * 3_600_000);
|
|
278
|
+
if (candidate === undefined || !Number.isFinite(candidate) || candidate <= 0)
|
|
279
|
+
return undefined;
|
|
280
|
+
return Math.min(candidate, Number.MAX_SAFE_INTEGER);
|
|
281
|
+
}
|
|
282
|
+
#optionalIdentity(canonicalizer, present, value, label) {
|
|
283
|
+
return present ? `1${canonicalizeSdPersistenceValue(canonicalizer, value, label)}` : '0';
|
|
284
|
+
}
|
|
285
|
+
#identityCanonicalizerFormat(canonicalizer) {
|
|
286
|
+
if (typeof canonicalizer.format !== 'string' || canonicalizer.format.length === 0) {
|
|
287
|
+
throw new Error('Persistence identity canonicalizer format must be a non-empty string');
|
|
288
|
+
}
|
|
289
|
+
return canonicalizer.format;
|
|
290
|
+
}
|
|
291
|
+
#serializerFormat(serializer) {
|
|
292
|
+
if (typeof serializer.format !== 'string' || serializer.format.length === 0) {
|
|
293
|
+
throw new Error('Persistence serializer format must be a non-empty string');
|
|
294
|
+
}
|
|
295
|
+
return serializer.format;
|
|
296
|
+
}
|
|
297
|
+
#readOptional(state, normalized) {
|
|
298
|
+
const entry = this.#getValidEntry(state, normalized);
|
|
299
|
+
return entry ? this.#cloneAs(normalized.serializer, entry.data) : undefined;
|
|
300
|
+
}
|
|
301
|
+
#readWithDefault(state, normalized, defaultSnapshot) {
|
|
302
|
+
const entry = this.#getValidEntry(state, normalized);
|
|
303
|
+
return entry ? this.#cloneAs(normalized.serializer, entry.data) : this.#cloneAs(normalized.serializer, defaultSnapshot);
|
|
304
|
+
}
|
|
305
|
+
#getValidEntry(state, normalized) {
|
|
306
|
+
if (!state.hydrated) {
|
|
307
|
+
this.#hydrateState(state, normalized);
|
|
308
|
+
}
|
|
309
|
+
if (!state.entry)
|
|
310
|
+
return undefined;
|
|
311
|
+
if (this.#isExpired(state.entry, normalized.ttlMs)) {
|
|
312
|
+
this.#removeEntry(state, normalized, true);
|
|
313
|
+
this.#broadcast(state);
|
|
98
314
|
return undefined;
|
|
99
|
-
if (option?.hours && entry.createdOn) {
|
|
100
|
-
const expiredOn = DateUtilities.addHours(entry.createdOn, option.hours);
|
|
101
|
-
if (expiredOn && expiredOn < new Date()) {
|
|
102
|
-
this.#internalRemove(key, option);
|
|
103
|
-
return undefined;
|
|
104
|
-
}
|
|
105
315
|
}
|
|
106
|
-
return
|
|
316
|
+
return state.entry;
|
|
317
|
+
}
|
|
318
|
+
#isExpired(entry, ttlMs) {
|
|
319
|
+
return ttlMs !== undefined && entry.createdOn.getTime() + ttlMs <= Date.now();
|
|
107
320
|
}
|
|
108
|
-
#
|
|
109
|
-
|
|
110
|
-
|
|
321
|
+
#hydrateState(state, normalized) {
|
|
322
|
+
const read = this.#readEntry(normalized);
|
|
323
|
+
state.entry = read.status === 'found' ? read.entry : undefined;
|
|
324
|
+
state.hydrated = read.status !== 'unavailable';
|
|
325
|
+
}
|
|
326
|
+
#setEntry(state, normalized, data, notifyRemote) {
|
|
111
327
|
const entry = {
|
|
112
|
-
data:
|
|
328
|
+
data: normalized.serializer.clone(data),
|
|
113
329
|
createdOn: new Date(),
|
|
114
330
|
};
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
331
|
+
state.entry = entry;
|
|
332
|
+
state.hydrated = true;
|
|
333
|
+
state.generation += 1;
|
|
334
|
+
this.#writePersistent(normalized, entry);
|
|
335
|
+
if (notifyRemote)
|
|
336
|
+
this.#safeRemoteSet(normalized, entry);
|
|
337
|
+
}
|
|
338
|
+
#removeEntry(state, normalized, notifyRemote) {
|
|
339
|
+
state.entry = undefined;
|
|
340
|
+
state.hydrated = true;
|
|
341
|
+
state.generation += 1;
|
|
342
|
+
if (normalized.area !== 'memory')
|
|
343
|
+
this.#writeTombstone(normalized);
|
|
344
|
+
if (notifyRemote)
|
|
345
|
+
void this.#safeRemoteRemove(normalized);
|
|
346
|
+
}
|
|
347
|
+
#broadcast(state) {
|
|
348
|
+
const present = state.entry !== undefined;
|
|
349
|
+
const value = state.entry?.data;
|
|
350
|
+
for (const handle of state.handles)
|
|
351
|
+
handle.emit(present, value);
|
|
352
|
+
}
|
|
353
|
+
#readEntry(normalized) {
|
|
354
|
+
if (normalized.area === 'memory') {
|
|
355
|
+
const entry = this.#memoryEntries.get(normalized.ownerDigest);
|
|
356
|
+
return entry ? { status: 'found', entry: this.#cloneEntry(entry, normalized.serializer) } : { status: 'absent' };
|
|
357
|
+
}
|
|
358
|
+
if (this.#failedOwners.has(normalized.ownerDigest))
|
|
359
|
+
return { status: 'unavailable' };
|
|
360
|
+
const primary = readSdPersistenceStorageItem(this.#adapter, normalized.area, normalized.storageKey);
|
|
361
|
+
if (primary.status === 'found') {
|
|
362
|
+
let envelope;
|
|
363
|
+
try {
|
|
364
|
+
envelope = parseSdPersistenceEnvelope(primary.value, normalized.identityDigest, normalized.serializer.format);
|
|
365
|
+
}
|
|
366
|
+
catch {
|
|
367
|
+
this.#writeTombstone(normalized);
|
|
368
|
+
return { status: 'absent' };
|
|
369
|
+
}
|
|
370
|
+
if (envelope?.kind === 'tombstone')
|
|
371
|
+
return { status: 'absent' };
|
|
372
|
+
const entry = envelope?.kind === 'value' ? this.#parseCurrentEntry(envelope.payload, normalized.serializer) : undefined;
|
|
373
|
+
if (entry)
|
|
374
|
+
return { status: 'found', entry };
|
|
375
|
+
this.#writeTombstone(normalized);
|
|
376
|
+
return { status: 'absent' };
|
|
377
|
+
}
|
|
378
|
+
if (primary.status === 'unavailable')
|
|
379
|
+
return { status: 'unavailable' };
|
|
380
|
+
if (normalized.legacyStorageKey === undefined)
|
|
381
|
+
return { status: 'absent' };
|
|
382
|
+
const legacyRead = readSdPersistenceStorageItem(this.#adapter, normalized.area, normalized.legacyStorageKey);
|
|
383
|
+
if (legacyRead.status === 'unavailable')
|
|
384
|
+
return { status: 'unavailable' };
|
|
385
|
+
if (legacyRead.status === 'absent')
|
|
386
|
+
return { status: 'absent' };
|
|
387
|
+
const legacy = this.#parseLegacy(legacyRead.value);
|
|
388
|
+
if (!legacy) {
|
|
389
|
+
this.#writeTombstone(normalized);
|
|
390
|
+
return { status: 'absent' };
|
|
391
|
+
}
|
|
392
|
+
this.#writePersistent(normalized, legacy);
|
|
393
|
+
return { status: 'found', entry: legacy };
|
|
394
|
+
}
|
|
395
|
+
#parseCurrentEntry(payload, serializer) {
|
|
396
|
+
try {
|
|
397
|
+
return this.#validateEntry(serializer.parse(payload));
|
|
398
|
+
}
|
|
399
|
+
catch {
|
|
400
|
+
return undefined;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
#parseLegacy(raw) {
|
|
404
|
+
try {
|
|
405
|
+
const parsed = JSON.parse(raw);
|
|
406
|
+
if (!this.#hasExactEntryKeys(parsed))
|
|
407
|
+
return undefined;
|
|
408
|
+
const createdOn = this.#toValidDate(parsed['createdOn']);
|
|
409
|
+
return createdOn ? { data: parsed['data'], createdOn } : undefined;
|
|
410
|
+
}
|
|
411
|
+
catch {
|
|
412
|
+
return undefined;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
#validateEntry(value) {
|
|
416
|
+
if (!this.#hasExactEntryKeys(value))
|
|
417
|
+
return undefined;
|
|
418
|
+
const createdOn = this.#toValidDate(value['createdOn']);
|
|
419
|
+
return createdOn ? { data: value['data'], createdOn } : undefined;
|
|
420
|
+
}
|
|
421
|
+
#toValidDate(value) {
|
|
422
|
+
const date = value instanceof Date ? value : typeof value === 'string' || typeof value === 'number' ? new Date(value) : undefined;
|
|
423
|
+
return date && Number.isFinite(date.getTime()) ? date : undefined;
|
|
424
|
+
}
|
|
425
|
+
#writePersistent(normalized, entry) {
|
|
426
|
+
if (normalized.area === 'memory') {
|
|
427
|
+
this.#memoryEntries.set(normalized.ownerDigest, this.#cloneEntry(entry, normalized.serializer));
|
|
428
|
+
return true;
|
|
429
|
+
}
|
|
430
|
+
try {
|
|
431
|
+
const payload = normalized.serializer.stringify(entry);
|
|
432
|
+
if (this.#adapter.setItem(normalized.area, normalized.storageKey, stringifySdPersistenceValueEnvelope(normalized.identityDigest, normalized.serializer.format, payload))) {
|
|
433
|
+
this.#clearFailedOwner(normalized.ownerDigest);
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
catch {
|
|
438
|
+
// Fall through to canonical quarantine.
|
|
439
|
+
}
|
|
440
|
+
this.#safeAdapterRemove(normalized.area, normalized.storageKey);
|
|
441
|
+
if (this.#tryPersistTombstone(normalized)) {
|
|
442
|
+
this.#clearFailedOwner(normalized.ownerDigest);
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
this.#recordFailedOwner(normalized.ownerDigest);
|
|
446
|
+
return false;
|
|
447
|
+
}
|
|
448
|
+
#readRemote(normalized) {
|
|
449
|
+
return this.#enqueueRemote(normalized, async () => {
|
|
450
|
+
const get = this.#configuration?.get;
|
|
451
|
+
if (!get)
|
|
452
|
+
return undefined;
|
|
453
|
+
const entry = this.#validateEntry(await get(normalized.storageKey, normalized.publicOption));
|
|
454
|
+
if (!entry || !this.#isExpired(entry, normalized.ttlMs))
|
|
455
|
+
return entry;
|
|
456
|
+
const remove = this.#configuration?.remove;
|
|
457
|
+
if (remove)
|
|
458
|
+
await remove(normalized.storageKey, normalized.publicOption);
|
|
459
|
+
return undefined;
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
#safeRemoteSet(normalized, entry) {
|
|
463
|
+
const set = this.#configuration?.set;
|
|
464
|
+
if (!set)
|
|
465
|
+
return;
|
|
466
|
+
void this.#enqueueRemote(normalized, () => set(normalized.storageKey, this.#cloneEntry(entry, normalized.serializer), normalized.publicOption));
|
|
467
|
+
}
|
|
468
|
+
#safeRemoteRemove(normalized) {
|
|
469
|
+
const remove = this.#configuration?.remove;
|
|
470
|
+
if (!remove)
|
|
471
|
+
return Promise.resolve();
|
|
472
|
+
return this.#enqueueRemote(normalized, () => remove(normalized.storageKey, normalized.publicOption));
|
|
473
|
+
}
|
|
474
|
+
#enqueueRemote(normalized, operation) {
|
|
475
|
+
const previous = this.#remoteQueues.get(normalized.ownerDigest) ?? Promise.resolve();
|
|
476
|
+
const result = previous.then(async () => {
|
|
118
477
|
try {
|
|
119
|
-
|
|
478
|
+
return await operation();
|
|
120
479
|
}
|
|
121
|
-
catch
|
|
122
|
-
|
|
480
|
+
catch {
|
|
481
|
+
// A failed remote operation cannot poison later work for this identity.
|
|
482
|
+
return undefined;
|
|
123
483
|
}
|
|
484
|
+
});
|
|
485
|
+
const tail = result.then(() => undefined);
|
|
486
|
+
this.#remoteQueues.set(normalized.ownerDigest, tail);
|
|
487
|
+
void tail.then(() => {
|
|
488
|
+
if (this.#remoteQueues.get(normalized.ownerDigest) === tail)
|
|
489
|
+
this.#remoteQueues.delete(normalized.ownerDigest);
|
|
490
|
+
});
|
|
491
|
+
return result;
|
|
492
|
+
}
|
|
493
|
+
#safeAdapterRemove(area, key) {
|
|
494
|
+
try {
|
|
495
|
+
return this.#adapter.removeItem(area, key);
|
|
124
496
|
}
|
|
497
|
+
catch {
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
#writeTombstone(normalized) {
|
|
502
|
+
if (normalized.area === 'memory') {
|
|
503
|
+
this.#memoryEntries.delete(normalized.ownerDigest);
|
|
504
|
+
return true;
|
|
505
|
+
}
|
|
506
|
+
if (this.#tryPersistTombstone(normalized)) {
|
|
507
|
+
this.#clearFailedOwner(normalized.ownerDigest);
|
|
508
|
+
return true;
|
|
509
|
+
}
|
|
510
|
+
this.#safeAdapterRemove(normalized.area, normalized.storageKey);
|
|
511
|
+
this.#recordFailedOwner(normalized.ownerDigest);
|
|
512
|
+
return false;
|
|
125
513
|
}
|
|
126
|
-
#
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
514
|
+
#tryPersistTombstone(normalized) {
|
|
515
|
+
if (normalized.area === 'memory')
|
|
516
|
+
return false;
|
|
517
|
+
try {
|
|
518
|
+
return this.#adapter.setItem(normalized.area, normalized.storageKey, stringifySdPersistenceTombstoneEnvelope(normalized.identityDigest, normalized.serializer.format));
|
|
519
|
+
}
|
|
520
|
+
catch {
|
|
521
|
+
return false;
|
|
131
522
|
}
|
|
132
523
|
}
|
|
133
|
-
#
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
524
|
+
#clearFailedOwner(ownerDigest) {
|
|
525
|
+
this.#failedOwners.delete(ownerDigest);
|
|
526
|
+
}
|
|
527
|
+
#recordFailedOwner(ownerDigest) {
|
|
528
|
+
this.#failedOwners.delete(ownerDigest);
|
|
529
|
+
this.#failedOwners.set(ownerDigest, true);
|
|
530
|
+
if (this.#failedOwners.size <= this.#maxFailedOwners)
|
|
531
|
+
return;
|
|
532
|
+
const oldest = this.#failedOwners.keys().next().value;
|
|
533
|
+
if (oldest !== undefined)
|
|
534
|
+
this.#failedOwners.delete(oldest);
|
|
535
|
+
}
|
|
536
|
+
#cloneEntry(entry, serializer) {
|
|
537
|
+
return { data: serializer.clone(entry.data), createdOn: new Date(entry.createdOn) };
|
|
538
|
+
}
|
|
539
|
+
#cloneAs(serializer, value) {
|
|
540
|
+
const entry = serializer.parse(serializer.stringify({ data: value, createdOn: new Date(0) }));
|
|
541
|
+
return entry.data;
|
|
542
|
+
}
|
|
543
|
+
#isRecord(value) {
|
|
544
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
545
|
+
}
|
|
546
|
+
#hasExactEntryKeys(value) {
|
|
547
|
+
return (this.#isRecord(value) &&
|
|
548
|
+
Reflect.ownKeys(value).length === 2 &&
|
|
549
|
+
Object.prototype.hasOwnProperty.call(value, 'data') &&
|
|
550
|
+
Object.prototype.hasOwnProperty.call(value, 'createdOn'));
|
|
137
551
|
}
|
|
138
552
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdCacheService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
139
553
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdCacheService, providedIn: 'root' });
|
|
140
554
|
}
|
|
141
555
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdCacheService, decorators: [{
|
|
142
556
|
type: Injectable,
|
|
143
|
-
args: [{
|
|
144
|
-
providedIn: 'root',
|
|
145
|
-
}]
|
|
557
|
+
args: [{ providedIn: 'root' }]
|
|
146
558
|
}], ctorParameters: () => [] });
|
|
147
559
|
|
|
148
|
-
const SD_CACHE_CONFIG = new InjectionToken('sd-cache.configuration');
|
|
149
|
-
|
|
150
560
|
/**
|
|
151
561
|
* Generated bundle index. Do not edit.
|
|
152
562
|
*/
|
|
153
563
|
|
|
154
|
-
export { SD_CACHE_CONFIG, SdCacheService };
|
|
564
|
+
export { SD_CACHE_CONFIG, SdCacheService, adaptLegacySdCacheCallbacks };
|
|
155
565
|
//# sourceMappingURL=sdcorejs-angular-services-cache.mjs.map
|