@iamjulianacosta/mobx-data 1.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/dist/{JsonApiSerializer-CC5HXp4b.js → JsonApiSerializer-BLoE046A.js} +2 -2
- package/dist/{JsonApiSerializer-CC5HXp4b.js.map → JsonApiSerializer-BLoE046A.js.map} +1 -1
- package/dist/{JsonApiSerializer-CKB02AgP.cjs → JsonApiSerializer-DKemcyw-.cjs} +2 -2
- package/dist/{JsonApiSerializer-CKB02AgP.cjs.map → JsonApiSerializer-DKemcyw-.cjs.map} +1 -1
- package/dist/{MemoryAdapter-Bx1e7ndV.js → MemoryAdapter-Bp-BGHH3.js} +2 -2
- package/dist/{MemoryAdapter-Bx1e7ndV.js.map → MemoryAdapter-Bp-BGHH3.js.map} +1 -1
- package/dist/{MemoryAdapter-D1cTyydm.cjs → MemoryAdapter-DH-gzSSl.cjs} +2 -2
- package/dist/{MemoryAdapter-D1cTyydm.cjs.map → MemoryAdapter-DH-gzSSl.cjs.map} +1 -1
- package/dist/{ODataAdapter-DyyF1sdA.cjs → ODataAdapter-CrDFvBEZ.cjs} +2 -2
- package/dist/{ODataAdapter-DyyF1sdA.cjs.map → ODataAdapter-CrDFvBEZ.cjs.map} +1 -1
- package/dist/{ODataAdapter-C4IHK4BK.js → ODataAdapter-RQUjVTcf.js} +2 -2
- package/dist/{ODataAdapter-C4IHK4BK.js.map → ODataAdapter-RQUjVTcf.js.map} +1 -1
- package/dist/RestAdapter-CSoJg7D2.cjs +2 -0
- package/dist/{RestAdapter-CJOwTsKK.cjs.map → RestAdapter-CSoJg7D2.cjs.map} +1 -1
- package/dist/{RestAdapter-B4aRvs4m.js → RestAdapter-D6bGIHZT.js} +32 -23
- package/dist/{RestAdapter-B4aRvs4m.js.map → RestAdapter-D6bGIHZT.js.map} +1 -1
- package/dist/{Store-CZ7Z-Nme.js → Store-Bm5JivTc.js} +195 -150
- package/dist/Store-Bm5JivTc.js.map +1 -0
- package/dist/Store-DX9D0Mmy.cjs +2 -0
- package/dist/Store-DX9D0Mmy.cjs.map +1 -0
- package/dist/adapter/RestAdapter.d.ts.map +1 -1
- package/dist/adapter/index.cjs +1 -1
- package/dist/adapter/index.js +2 -2
- package/dist/cache/IndexedDBCache.d.ts +22 -0
- package/dist/cache/IndexedDBCache.d.ts.map +1 -0
- package/dist/cache/cache-utils.d.ts +4 -0
- package/dist/cache/cache-utils.d.ts.map +1 -0
- package/dist/cache/index.d.ts +4 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/types.d.ts +25 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/cache-utils-B2wFhisx.js +39 -0
- package/dist/cache-utils-B2wFhisx.js.map +1 -0
- package/dist/cache-utils-CSwsqOi3.cjs +2 -0
- package/dist/cache-utils-CSwsqOi3.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +144 -49
- package/dist/index.js.map +1 -1
- package/dist/json-api/index.cjs +1 -1
- package/dist/json-api/index.js +1 -1
- package/dist/odata/index.cjs +1 -1
- package/dist/odata/index.js +1 -1
- package/dist/store/Store.d.ts +6 -0
- package/dist/store/Store.d.ts.map +1 -1
- package/dist/store/index.cjs +1 -1
- package/dist/store/index.js +1 -1
- package/package.json +2 -1
- package/src/adapter/RestAdapter.ts +12 -2
- package/src/cache/IndexedDBCache.ts +171 -0
- package/src/cache/cache-utils.ts +57 -0
- package/src/cache/index.ts +12 -0
- package/src/cache/types.ts +33 -0
- package/src/index.ts +1 -0
- package/src/store/Store.ts +100 -0
- package/dist/RestAdapter-CJOwTsKK.cjs +0 -2
- package/dist/Store-BdwMrbDi.cjs +0 -2
- package/dist/Store-BdwMrbDi.cjs.map +0 -1
- package/dist/Store-CZ7Z-Nme.js.map +0 -1
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import type { RelationshipRef } from '@mobx-data/model';
|
|
2
|
+
import type { CacheEntryData, IndexedDBCacheOptions, CacheLike } from './types.js';
|
|
3
|
+
|
|
4
|
+
const STORE_NAME = 'cache-entries';
|
|
5
|
+
const DATABASE_VERSION = 1;
|
|
6
|
+
const DEFAULT_TTL = 3_600_000;
|
|
7
|
+
|
|
8
|
+
interface StoredEntry extends CacheEntryData {
|
|
9
|
+
key: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class IndexedDBCache implements CacheLike {
|
|
13
|
+
private _databaseName: string;
|
|
14
|
+
|
|
15
|
+
private _defaultTTL: number;
|
|
16
|
+
|
|
17
|
+
private _database: IDBDatabase | null = null;
|
|
18
|
+
|
|
19
|
+
private _openPromise: Promise<IDBDatabase> | null = null;
|
|
20
|
+
|
|
21
|
+
constructor(options: IndexedDBCacheOptions = {}) {
|
|
22
|
+
this._databaseName = options.databaseName ?? 'mobx-data-cache';
|
|
23
|
+
this._defaultTTL = options.defaultTTL ?? DEFAULT_TTL;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get defaultTTL(): number {
|
|
27
|
+
return this._defaultTTL;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private async open(): Promise<IDBDatabase> {
|
|
31
|
+
if (this._database) {
|
|
32
|
+
return this._database;
|
|
33
|
+
}
|
|
34
|
+
if (this._openPromise) {
|
|
35
|
+
return this._openPromise;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this._openPromise = new Promise<IDBDatabase>((resolve, reject) => {
|
|
39
|
+
const request = indexedDB.open(this._databaseName, DATABASE_VERSION);
|
|
40
|
+
request.onupgradeneeded = () => {
|
|
41
|
+
const database = request.result;
|
|
42
|
+
if (!database.objectStoreNames.contains(STORE_NAME)) {
|
|
43
|
+
const store = database.createObjectStore(STORE_NAME, {
|
|
44
|
+
keyPath: 'key',
|
|
45
|
+
});
|
|
46
|
+
store.createIndex('modelName', 'modelName', { unique: false });
|
|
47
|
+
store.createIndex('expiresAt', 'expiresAt', { unique: false });
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
request.onsuccess = () => {
|
|
51
|
+
this._database = request.result;
|
|
52
|
+
resolve(this._database);
|
|
53
|
+
};
|
|
54
|
+
request.onerror = () => {
|
|
55
|
+
this._openPromise = null;
|
|
56
|
+
reject(request.error);
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
return this._openPromise;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
static cacheKey(modelName: string, id: string): string {
|
|
63
|
+
return `${modelName}:${id}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async get(modelName: string, id: string): Promise<CacheEntryData | null> {
|
|
67
|
+
const database = await this.open();
|
|
68
|
+
return new Promise((resolve, reject) => {
|
|
69
|
+
const transaction = database.transaction(STORE_NAME, 'readonly');
|
|
70
|
+
const store = transaction.objectStore(STORE_NAME);
|
|
71
|
+
const request = store.get(IndexedDBCache.cacheKey(modelName, id));
|
|
72
|
+
request.onsuccess = () => {
|
|
73
|
+
const entry = request.result as StoredEntry | undefined;
|
|
74
|
+
if (!entry) {
|
|
75
|
+
resolve(null);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (Date.now() > entry.expiresAt) {
|
|
79
|
+
this.invalidate(modelName, id);
|
|
80
|
+
resolve(null);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
resolve(entry);
|
|
84
|
+
};
|
|
85
|
+
request.onerror = () => reject(request.error);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async set(
|
|
90
|
+
modelName: string,
|
|
91
|
+
id: string,
|
|
92
|
+
attributes: Record<string, unknown>,
|
|
93
|
+
options: {
|
|
94
|
+
relationships?: Record<string, RelationshipRef>;
|
|
95
|
+
ttl?: number;
|
|
96
|
+
} = {},
|
|
97
|
+
): Promise<void> {
|
|
98
|
+
const database = await this.open();
|
|
99
|
+
const now = Date.now();
|
|
100
|
+
const ttl = options.ttl ?? this._defaultTTL;
|
|
101
|
+
const entry: StoredEntry = {
|
|
102
|
+
key: IndexedDBCache.cacheKey(modelName, id),
|
|
103
|
+
modelName,
|
|
104
|
+
id,
|
|
105
|
+
attributes,
|
|
106
|
+
relationships: options.relationships,
|
|
107
|
+
cachedAt: now,
|
|
108
|
+
expiresAt: now + ttl,
|
|
109
|
+
};
|
|
110
|
+
return new Promise((resolve, reject) => {
|
|
111
|
+
const transaction = database.transaction(STORE_NAME, 'readwrite');
|
|
112
|
+
const store = transaction.objectStore(STORE_NAME);
|
|
113
|
+
const request = store.put(entry);
|
|
114
|
+
request.onsuccess = () => resolve();
|
|
115
|
+
request.onerror = () => reject(request.error);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async has(modelName: string, id: string): Promise<boolean> {
|
|
120
|
+
const entry = await this.get(modelName, id);
|
|
121
|
+
return entry !== null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async invalidate(modelName: string, id: string): Promise<void> {
|
|
125
|
+
const database = await this.open();
|
|
126
|
+
return new Promise((resolve, reject) => {
|
|
127
|
+
const transaction = database.transaction(STORE_NAME, 'readwrite');
|
|
128
|
+
const store = transaction.objectStore(STORE_NAME);
|
|
129
|
+
const request = store.delete(IndexedDBCache.cacheKey(modelName, id));
|
|
130
|
+
request.onsuccess = () => resolve();
|
|
131
|
+
request.onerror = () => reject(request.error);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async invalidateAll(modelName?: string): Promise<void> {
|
|
136
|
+
const database = await this.open();
|
|
137
|
+
if (!modelName) {
|
|
138
|
+
return new Promise((resolve, reject) => {
|
|
139
|
+
const transaction = database.transaction(STORE_NAME, 'readwrite');
|
|
140
|
+
const store = transaction.objectStore(STORE_NAME);
|
|
141
|
+
const request = store.clear();
|
|
142
|
+
request.onsuccess = () => resolve();
|
|
143
|
+
request.onerror = () => reject(request.error);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return new Promise((resolve, reject) => {
|
|
147
|
+
const transaction = database.transaction(STORE_NAME, 'readwrite');
|
|
148
|
+
const store = transaction.objectStore(STORE_NAME);
|
|
149
|
+
const index = store.index('modelName');
|
|
150
|
+
const cursorRequest = index.openCursor(IDBKeyRange.only(modelName));
|
|
151
|
+
cursorRequest.onsuccess = () => {
|
|
152
|
+
const cursor = cursorRequest.result;
|
|
153
|
+
if (cursor) {
|
|
154
|
+
cursor.delete();
|
|
155
|
+
cursor.continue();
|
|
156
|
+
} else {
|
|
157
|
+
resolve();
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
cursorRequest.onerror = () => reject(cursorRequest.error);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async close(): Promise<void> {
|
|
165
|
+
if (this._database) {
|
|
166
|
+
this._database.close();
|
|
167
|
+
this._database = null;
|
|
168
|
+
this._openPromise = null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { RESPONSE_HEADERS } from './types.js';
|
|
2
|
+
|
|
3
|
+
export function parseCacheTTLFromHeaders(
|
|
4
|
+
headers: Record<string, string>,
|
|
5
|
+
): number | null {
|
|
6
|
+
const cacheControl = headers['cache-control'];
|
|
7
|
+
if (cacheControl) {
|
|
8
|
+
if (/no-store/i.test(cacheControl) || /no-cache/i.test(cacheControl)) {
|
|
9
|
+
return 0;
|
|
10
|
+
}
|
|
11
|
+
const sMaxAge = /s-maxage=(\d+)/i.exec(cacheControl);
|
|
12
|
+
if (sMaxAge) {
|
|
13
|
+
return parseInt(sMaxAge[1]!, 10) * 1000;
|
|
14
|
+
}
|
|
15
|
+
const maxAge = /max-age=(\d+)/i.exec(cacheControl);
|
|
16
|
+
if (maxAge) {
|
|
17
|
+
return parseInt(maxAge[1]!, 10) * 1000;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const expires = headers['expires'];
|
|
22
|
+
if (expires) {
|
|
23
|
+
const expiresMilliseconds = new Date(expires).getTime();
|
|
24
|
+
if (!Number.isNaN(expiresMilliseconds)) {
|
|
25
|
+
return Math.max(0, expiresMilliseconds - Date.now());
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function extractResponseHeaders(
|
|
33
|
+
payload: unknown,
|
|
34
|
+
): Record<string, string> | null {
|
|
35
|
+
if (payload !== null && typeof payload === 'object') {
|
|
36
|
+
return (
|
|
37
|
+
(payload as Record<symbol, unknown>)[RESPONSE_HEADERS] as
|
|
38
|
+
| Record<string, string>
|
|
39
|
+
| undefined
|
|
40
|
+
) ?? null;
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function attachResponseHeaders(
|
|
46
|
+
payload: unknown,
|
|
47
|
+
headers: Record<string, string>,
|
|
48
|
+
): void {
|
|
49
|
+
if (payload !== null && typeof payload === 'object') {
|
|
50
|
+
Object.defineProperty(payload, RESPONSE_HEADERS, {
|
|
51
|
+
value: headers,
|
|
52
|
+
enumerable: false,
|
|
53
|
+
writable: false,
|
|
54
|
+
configurable: false,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { IndexedDBCache } from './IndexedDBCache.js';
|
|
2
|
+
export {
|
|
3
|
+
type CacheEntryData,
|
|
4
|
+
type IndexedDBCacheOptions,
|
|
5
|
+
type CacheLike,
|
|
6
|
+
RESPONSE_HEADERS,
|
|
7
|
+
} from './types.js';
|
|
8
|
+
export {
|
|
9
|
+
parseCacheTTLFromHeaders,
|
|
10
|
+
extractResponseHeaders,
|
|
11
|
+
attachResponseHeaders,
|
|
12
|
+
} from './cache-utils.js';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { RelationshipRef } from '@mobx-data/model';
|
|
2
|
+
|
|
3
|
+
export const RESPONSE_HEADERS: unique symbol = Symbol('response-headers');
|
|
4
|
+
|
|
5
|
+
export interface CacheEntryData {
|
|
6
|
+
modelName: string;
|
|
7
|
+
id: string;
|
|
8
|
+
attributes: Record<string, unknown>;
|
|
9
|
+
relationships?: Record<string, RelationshipRef>;
|
|
10
|
+
cachedAt: number;
|
|
11
|
+
expiresAt: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface IndexedDBCacheOptions {
|
|
15
|
+
databaseName?: string;
|
|
16
|
+
defaultTTL?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface CacheLike {
|
|
20
|
+
get(modelName: string, id: string): Promise<CacheEntryData | null>;
|
|
21
|
+
set(
|
|
22
|
+
modelName: string,
|
|
23
|
+
id: string,
|
|
24
|
+
attributes: Record<string, unknown>,
|
|
25
|
+
options?: {
|
|
26
|
+
relationships?: Record<string, RelationshipRef>;
|
|
27
|
+
ttl?: number;
|
|
28
|
+
},
|
|
29
|
+
): Promise<void>;
|
|
30
|
+
has(modelName: string, id: string): Promise<boolean>;
|
|
31
|
+
invalidate(modelName: string, id: string): Promise<void>;
|
|
32
|
+
invalidateAll(modelName?: string): Promise<void>;
|
|
33
|
+
}
|
package/src/index.ts
CHANGED
package/src/store/Store.ts
CHANGED
|
@@ -53,6 +53,11 @@ import {
|
|
|
53
53
|
type ModelStoreLike,
|
|
54
54
|
type SaveOptions,
|
|
55
55
|
} from '@mobx-data/model';
|
|
56
|
+
import type { CacheLike } from '../cache/types.js';
|
|
57
|
+
import {
|
|
58
|
+
extractResponseHeaders,
|
|
59
|
+
parseCacheTTLFromHeaders,
|
|
60
|
+
} from '../cache/cache-utils.js';
|
|
56
61
|
import { IdentityMap } from './IdentityMap.js';
|
|
57
62
|
import {
|
|
58
63
|
RecordArray,
|
|
@@ -222,6 +227,9 @@ export class Store implements ModelStoreLike {
|
|
|
222
227
|
/** Tracks new records that have been appended to a hasMany but not yet saved. */
|
|
223
228
|
private pendingMembers: WeakMap<Model, Map<string, Set<Model>>> = new WeakMap();
|
|
224
229
|
|
|
230
|
+
/** Optional persistent cache layer (e.g. IndexedDB). */
|
|
231
|
+
private _cache: CacheLike | null = null;
|
|
232
|
+
|
|
225
233
|
constructor(@inject(SchemaService) schema: SchemaService) {
|
|
226
234
|
this.schema = schema;
|
|
227
235
|
}
|
|
@@ -238,6 +246,11 @@ export class Store implements ModelStoreLike {
|
|
|
238
246
|
this.serializers.set(modelName, serializer);
|
|
239
247
|
}
|
|
240
248
|
|
|
249
|
+
/** Registers a persistent cache layer (e.g. IndexedDB) for offline-first reads. */
|
|
250
|
+
registerCache(cache: CacheLike): void {
|
|
251
|
+
this._cache = cache;
|
|
252
|
+
}
|
|
253
|
+
|
|
241
254
|
/**
|
|
242
255
|
* Returns the adapter for `modelName`, falling back to `'application'`.
|
|
243
256
|
* @throws when no adapter is registered.
|
|
@@ -608,6 +621,22 @@ export class Store implements ModelStoreLike {
|
|
|
608
621
|
if (cached && !options.reload && !options.include) {
|
|
609
622
|
return cached;
|
|
610
623
|
}
|
|
624
|
+
|
|
625
|
+
if (!options.reload && !options.include && this._cache) {
|
|
626
|
+
const cacheEntry = await this._cache.get(modelName, id);
|
|
627
|
+
if (cacheEntry) {
|
|
628
|
+
const record = this.push({
|
|
629
|
+
data: {
|
|
630
|
+
type: cacheEntry.modelName,
|
|
631
|
+
id: cacheEntry.id,
|
|
632
|
+
attributes: cacheEntry.attributes,
|
|
633
|
+
relationships: cacheEntry.relationships,
|
|
634
|
+
},
|
|
635
|
+
});
|
|
636
|
+
return record as T;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
611
640
|
const adapter = this.adapterFor(modelName);
|
|
612
641
|
|
|
613
642
|
if (adapter.coalesceFindRequests && adapter.findMany && !options.include) {
|
|
@@ -619,6 +648,7 @@ export class Store implements ModelStoreLike {
|
|
|
619
648
|
? { include: options.include, adapterOptions: options.adapterOptions }
|
|
620
649
|
: options.adapterOptions ? { adapterOptions: options.adapterOptions } : undefined;
|
|
621
650
|
const response = await adapter.findRecord(this, modelName, id, snapshot, adapterOptions);
|
|
651
|
+
const responseHeaders = extractResponseHeaders(response);
|
|
622
652
|
const doc = this.serializerFor(modelName).normalizeResponse(
|
|
623
653
|
this,
|
|
624
654
|
this.schema.modelFor(modelName),
|
|
@@ -627,6 +657,16 @@ export class Store implements ModelStoreLike {
|
|
|
627
657
|
'findRecord',
|
|
628
658
|
) as NormalizedDocument;
|
|
629
659
|
const record = this.push(doc);
|
|
660
|
+
|
|
661
|
+
if (this._cache) {
|
|
662
|
+
const ttl = responseHeaders
|
|
663
|
+
? parseCacheTTLFromHeaders(responseHeaders)
|
|
664
|
+
: null;
|
|
665
|
+
if (ttl !== 0) {
|
|
666
|
+
this.cacheNormalizedDocument(doc, ttl ?? undefined);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
630
670
|
return record as T;
|
|
631
671
|
}
|
|
632
672
|
|
|
@@ -643,6 +683,7 @@ export class Store implements ModelStoreLike {
|
|
|
643
683
|
? { include: options.include, adapterOptions: options.adapterOptions }
|
|
644
684
|
: options.adapterOptions ? { adapterOptions: options.adapterOptions } : undefined;
|
|
645
685
|
const response = await adapter.findAll(this, modelName, null, [], adapterOptions);
|
|
686
|
+
const responseHeaders = extractResponseHeaders(response);
|
|
646
687
|
const doc = this.serializerFor(modelName).normalizeResponse(
|
|
647
688
|
this,
|
|
648
689
|
this.schema.modelFor(modelName),
|
|
@@ -651,6 +692,16 @@ export class Store implements ModelStoreLike {
|
|
|
651
692
|
'findAll',
|
|
652
693
|
) as NormalizedDocument;
|
|
653
694
|
this.push(doc);
|
|
695
|
+
|
|
696
|
+
if (this._cache) {
|
|
697
|
+
const ttl = responseHeaders
|
|
698
|
+
? parseCacheTTLFromHeaders(responseHeaders)
|
|
699
|
+
: null;
|
|
700
|
+
if (ttl !== 0) {
|
|
701
|
+
this.cacheNormalizedDocument(doc, ttl ?? undefined);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
654
705
|
return this.peekAll<T>(modelName);
|
|
655
706
|
}
|
|
656
707
|
|
|
@@ -785,6 +836,22 @@ export class Store implements ModelStoreLike {
|
|
|
785
836
|
this.pushResource(resource);
|
|
786
837
|
}
|
|
787
838
|
}
|
|
839
|
+
|
|
840
|
+
if (this._cache && record.id) {
|
|
841
|
+
const internal = record as unknown as {
|
|
842
|
+
_data: Record<string, unknown>;
|
|
843
|
+
_relationships: Map<string, RelationshipRef>;
|
|
844
|
+
};
|
|
845
|
+
const relationships: Record<string, RelationshipRef> = {};
|
|
846
|
+
for (const [name, ref] of internal._relationships) {
|
|
847
|
+
relationships[name] = ref;
|
|
848
|
+
}
|
|
849
|
+
this._cache.set(record.modelName, record.id, { ...internal._data }, {
|
|
850
|
+
relationships: Object.keys(relationships).length > 0
|
|
851
|
+
? relationships : undefined,
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
|
|
788
855
|
return record;
|
|
789
856
|
}
|
|
790
857
|
|
|
@@ -795,6 +862,9 @@ export class Store implements ModelStoreLike {
|
|
|
795
862
|
const adapter = this.adapterFor(record.modelName);
|
|
796
863
|
const snapshot = this.createSnapshot(record);
|
|
797
864
|
await adapter.deleteRecord(this, record.modelName, snapshot);
|
|
865
|
+
if (this._cache && record.id) {
|
|
866
|
+
this._cache.invalidate(record.modelName, record.id);
|
|
867
|
+
}
|
|
798
868
|
this.unloadRecord(record);
|
|
799
869
|
return record;
|
|
800
870
|
}
|
|
@@ -1127,6 +1197,36 @@ export class Store implements ModelStoreLike {
|
|
|
1127
1197
|
}
|
|
1128
1198
|
}
|
|
1129
1199
|
|
|
1200
|
+
// --- persistent cache helpers ---
|
|
1201
|
+
|
|
1202
|
+
private cacheNormalizedDocument(
|
|
1203
|
+
doc: NormalizedDocument,
|
|
1204
|
+
ttl?: number,
|
|
1205
|
+
): void {
|
|
1206
|
+
if (!this._cache) {
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
const resources: NormalizedResource[] = [];
|
|
1210
|
+
if (doc.data) {
|
|
1211
|
+
if (Array.isArray(doc.data)) {
|
|
1212
|
+
resources.push(...doc.data);
|
|
1213
|
+
} else {
|
|
1214
|
+
resources.push(doc.data);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
if (doc.included) {
|
|
1218
|
+
resources.push(...doc.included);
|
|
1219
|
+
}
|
|
1220
|
+
for (const resource of resources) {
|
|
1221
|
+
if (resource.id) {
|
|
1222
|
+
this._cache.set(resource.type, resource.id, resource.attributes ?? {}, {
|
|
1223
|
+
relationships: resource.relationships,
|
|
1224
|
+
ttl,
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1130
1230
|
// --- coalesceFindRequests ---
|
|
1131
1231
|
|
|
1132
1232
|
private coalescePending: Map<string, Map<string, {
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";const p=require("tsyringe"),_=require("pluralize"),y=new RegExp("([\\p{Ll}\\d])(\\p{Lu})","gu"),F=new RegExp("(\\p{Lu})([\\p{Lu}][\\p{Ll}])","gu"),L=new RegExp("(\\d)\\p{Ll}|(\\p{L})\\d","u"),U=/[^\p{L}\d]+/giu,i="$1\0$2",l="";function d(n){let e=n.trim();e=e.replace(y,i).replace(F,i),e=e.replace(U,"\0");let r=0,t=e.length;for(;e.charAt(r)==="\0";)r++;if(r===t)return[];for(;e.charAt(t-1)==="\0";)t--;return e.slice(r,t).split(/\0/g)}function b(n){const e=d(n);for(let r=0;r<e.length;r++){const t=e[r],s=L.exec(t);if(s){const c=s.index+(s[1]??s[2]).length;e.splice(r,1,t.slice(0,c),t.slice(c))}}return e}function A(n,e){const[r,t,s]=f(n,e);return r+t.map(h(e==null?void 0:e.locale)).join((e==null?void 0:e.delimiter)??" ")+s}function T(n,e){const[r,t,s]=f(n,e),c=h(e==null?void 0:e.locale),a=m(e==null?void 0:e.locale),o=C(c,a);return r+t.map(o).join("")+s}function S(n,e){return A(n,{delimiter:"-",...e})}function h(n){return n===!1?e=>e.toLowerCase():e=>e.toLocaleLowerCase(n)}function m(n){return e=>e.toLocaleUpperCase(n)}function C(n,e){return(r,t)=>{const s=r[0];return(t>0&&s>="0"&&s<="9"?"_"+s:e(s))+n(r.slice(1))}}function f(n,e={}){const r=e.split??(e.separateNumbers?b:d),t=e.prefixCharacters??l,s=e.suffixCharacters??l;let c=0,a=n.length;for(;c<n.length;){const o=n.charAt(c);if(!t.includes(o))break;c++}for(;a>c;){const o=a-1,u=n.charAt(o);if(!s.includes(u))break;a=o}return[n.slice(0,c),r(n.slice(c,a)),n.slice(a)]}class R{constructor(){this.namespace="",this.host="",this.headers={},this.coalesceFindRequests=!1}patchRecord(e,r,t){return this.updateRecord(e,r,t)}pathForType(e){return _.plural(S(e))}_composeURL(e){const r=this.namespace.replace(/^\/+|\/+$/g,""),t=this.host?this.host.replace(/\/+$/,""):"",s=[];return t&&s.push(t),r&&s.push(r),s.push(e),t?s.join("/"):`/${s.filter(Boolean).join("/")}`}buildURL(e,r,t,s,c={}){switch(s){case"findRecord":return this.urlForFindRecord(r,e,t);case"findAll":return this.urlForFindAll(e,t??[]);case"findMany":return this.urlForFindMany(r??[],e,t??[]);case"query":return this.urlForQuery(c,e);case"queryRecord":return this.urlForQueryRecord(c,e);case"createRecord":return this.urlForCreateRecord(e,t);case"updateRecord":return this.urlForUpdateRecord(r,e,t);case"deleteRecord":return this.urlForDeleteRecord(r,e,t)}}urlForFindRecord(e,r,t){return this._composeURL(`${this.pathForType(r)}/${encodeURIComponent(e)}`)}urlForFindAll(e,r){return this._composeURL(this.pathForType(e))}urlForFindMany(e,r,t){return this._composeURL(this.pathForType(r))}urlForQuery(e,r){return this._composeURL(this.pathForType(r))}urlForQueryRecord(e,r){return this._composeURL(this.pathForType(r))}urlForCreateRecord(e,r){return this._composeURL(this.pathForType(e))}urlForUpdateRecord(e,r,t){return this._composeURL(`${this.pathForType(r)}/${encodeURIComponent(e)}`)}urlForDeleteRecord(e,r,t){return this._composeURL(`${this.pathForType(r)}/${encodeURIComponent(e)}`)}groupRecordsForFindMany(e,r){return[r]}shouldReloadRecord(e,r){return!1}shouldBackgroundReloadRecord(e,r){return!0}shouldReloadAll(e,r){return!1}shouldBackgroundReloadAll(e,r){return!0}}var E=Object.getOwnPropertyDescriptor,g=(n,e,r,t)=>{for(var s=t>1?void 0:t?E(e,r):e,c=n.length-1,a;c>=0;c--)(a=n[c])&&(s=a(s)||s);return s};exports.RestAdapter=class extends R{static serializeSnapshotToObject(e){const r={},s=e.record._data;if(s)for(const[c,a]of Object.entries(s))c==="__proto__"||c==="constructor"||c==="prototype"||(r[c]=a);return r}static toQueryString(e){const r=[];for(const[t,s]of Object.entries(e))s!=null&&r.push(`${encodeURIComponent(t)}=${encodeURIComponent(String(s))}`);return r.length>0?`?${r.join("&")}`:""}defaultHeaders(){return{Accept:"application/json",...this.headers}}mutationHeaders(){return{"Content-Type":"application/json",...this.defaultHeaders()}}async _fetchJSON(e,r){const t=await fetch(e,r);if(!t.ok){let c=null;try{c=await t.json()}catch{}throw Object.assign(new Error(`Request failed: ${t.status}`),{status:t.status,body:c})}if(t.status===204)return null;const s=await t.text();if(!s)return null;try{return JSON.parse(s)}catch{return s}}async findRecord(e,r,t,s,c){let a=this.buildURL(r,t,s,"findRecord");return c!=null&&c.include&&(a+=`${a.includes("?")?"&":"?"}include=${encodeURIComponent(c.include)}`),this._fetchJSON(a,{method:"GET",headers:this.defaultHeaders()})}async findAll(e,r,t,s,c){let a=this.buildURL(r,null,s,"findAll");return c!=null&&c.include&&(a+=`${a.includes("?")?"&":"?"}include=${encodeURIComponent(c.include)}`),this._fetchJSON(a,{method:"GET",headers:this.defaultHeaders()})}async findMany(e,r,t,s){const c=this.buildURL(r,t,s,"findMany"),a=c.includes("?")?"&":"?",o=`${c}${a}ids=${t.map(encodeURIComponent).join(",")}`;return this._fetchJSON(o,{method:"GET",headers:this.defaultHeaders()})}async query(e,r,t){const c=`${this.buildURL(r,null,null,"query",t)}${exports.RestAdapter.toQueryString(t)}`;return this._fetchJSON(c,{method:"GET",headers:this.defaultHeaders()})}async queryRecord(e,r,t){const c=`${this.buildURL(r,null,null,"queryRecord",t)}${exports.RestAdapter.toQueryString(t)}`;return this._fetchJSON(c,{method:"GET",headers:this.defaultHeaders()})}async createRecord(e,r,t){const s=this.buildURL(r,null,t,"createRecord");return this._fetchJSON(s,{method:"POST",headers:this.mutationHeaders(),body:JSON.stringify(exports.RestAdapter.serializeSnapshotToObject(t))})}async updateRecord(e,r,t){const s=this.buildURL(r,t.id,t,"updateRecord");return this._fetchJSON(s,{method:"PUT",headers:this.mutationHeaders(),body:JSON.stringify(exports.RestAdapter.serializeSnapshotToObject(t))})}async patchRecord(e,r,t){const s=this.buildURL(r,t.id,t,"updateRecord"),c=t.changedAttributes(),a={};for(const[o,[,u]]of Object.entries(c))a[o]=u;return this._fetchJSON(s,{method:"PATCH",headers:this.mutationHeaders(),body:JSON.stringify(a)})}async deleteRecord(e,r,t){const s=this.buildURL(r,t.id,t,"deleteRecord");return this._fetchJSON(s,{method:"DELETE",headers:this.defaultHeaders()})}};exports.RestAdapter=g([p.injectable()],exports.RestAdapter);exports.Adapter=R;exports.pascalCase=T;
|
|
2
|
-
//# sourceMappingURL=RestAdapter-CJOwTsKK.cjs.map
|
package/dist/Store-BdwMrbDi.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";const b=require("tsyringe"),c=require("mobx"),M=require("./SchemaService-Di_yjVzU.cjs"),y=require("./relationships-B55LBaCW.cjs");class k{constructor(){this._buckets=new Map,c.makeObservable(this,{_buckets:c.observable.shallow,set:c.action,delete:c.action,clear:c.action})}bucket(t,e=!1){let s=this._buckets.get(t);return!s&&e&&(s=c.observable.map({},{deep:!1}),this._buckets.set(t,s)),s}set(t,e,s){this.bucket(t,!0).set(e,s)}get(t,e){var s;return((s=this.bucket(t))==null?void 0:s.get(e))??null}has(t,e){var s;return((s=this.bucket(t))==null?void 0:s.has(e))??!1}delete(t,e){var s;return((s=this.bucket(t))==null?void 0:s.delete(e))??!1}all(t){const e=this.bucket(t);return e?Array.from(e.values()):[]}clear(t){var e;if(t)(e=this.bucket(t))==null||e.clear();else for(const s of this._buckets.values())s.clear()}size(t){var s;if(t)return((s=this.bucket(t))==null?void 0:s.size)??0;let e=0;for(const i of this._buckets.values())e+=i.size;return e}}class R{constructor(t){this.updating=!1,this.opts=t,c.makeObservable(this,{resolved:t.keepAlive?c.computed({keepAlive:!0}):c.computed,updating:c.observable,length:c.computed,modelName:c.computed})}get resolved(){return this.opts.source()}get isLoading(){return this.updating}get isUpdating(){return this.updating}get length(){return this.resolved.length}get modelName(){return this.opts.modelName}at(t){return this.resolved[t]}toArray(){return[...this.resolved]}map(t){return this.resolved.map(t)}filter(t){return this.resolved.filter(t)}forEach(t){this.resolved.forEach(t)}includes(t){return this.resolved.includes(t)}async update(){if(!this.opts.update)return this;this.updating=!0;try{await this.opts.update()}finally{this.updating=!1}return this}[Symbol.iterator](){return this.resolved[Symbol.iterator]()}}class A extends R{constructor(t){super(t),this.queryParams=t.query,this.metaData=t.meta??{},this.linksData=t.links??{},c.makeObservable(this,{metaData:c.observable.ref,linksData:c.observable.ref,meta:c.computed,links:c.computed,query:c.computed})}get meta(){return this.metaData}get links(){return this.linksData}get query(){return this.queryParams}_setMeta(t){this.metaData=t}_setLinks(t){this.linksData=t}}var F=Object.getOwnPropertyDescriptor,_=(u,t,e,s)=>{for(var i=s>1?void 0:s?F(t,e):t,n=u.length-1,a;n>=0;n--)(a=u[n])&&(i=a(i)||i);return i},m=(u,t)=>(e,s)=>t(e,s,u);exports.Store=class{constructor(t){this.identityMap=new k,this.adapters=new Map,this.serializers=new Map,this.newRecords=new Map,this.newRecordTypes=new WeakMap,this.relationshipCache=new WeakMap,this.pendingMembers=new WeakMap,this.coalescePending=new Map,this.coalesceScheduled=new Set,this.schema=t}static refEquals(t,e){return t.id===e.id&&t.type===e.type}registerAdapter(t,e){this.adapters.set(t,e)}registerSerializer(t,e){this.serializers.set(t,e)}adapterFor(t){const e=this.adapters.get(t)??this.adapters.get("application");if(!e)throw new Error(`No adapter registered for "${t}"`);return e}serializerFor(t){const e=this.serializers.get(t)??this.serializers.get("application");if(!e)throw new Error(`No serializer registered for "${t}"`);return e}createRecord(t,e={}){if(!this.schema.doesTypeExist(t))throw new Error(`Unknown model type: "${t}"`);const s=this.schema.modelFor(t),i=new s({id:null,data:e,store:this});return this.trackNewRecord(t,i),i}trackNewRecord(t,e){let s=this.newRecords.get(t);s||(s=new Set,this.newRecords.set(t,s)),s.add(e),this.newRecordTypes.set(e,t)}untrackNewRecord(t){var s;const e=this.newRecordTypes.get(t);e&&((s=this.newRecords.get(e))==null||s.delete(t),this.newRecordTypes.delete(t))}peekRecord(t,e){const s=e==null?null:String(e);return s===null?null:this.identityMap.get(t,s)??null}peekAll(t){return new R({modelName:t,source:()=>{const e=this.identityMap.all(t),s=this.newRecords.get(t);return!s||s.size===0?e:[...e,...s]}})}push(t){const e=t;if(e.included)for(const s of e.included)this.pushResource(s);return e.data===null||e.data===void 0?null:Array.isArray(e.data)?e.data.map(s=>this.pushResource(s)):this.pushResource(e.data)}pushPayload(t,e){let s,i;typeof t=="string"?(s=t,i=e):(s=null,i=t);const n=s?this.serializerFor(s).normalizeResponse(this,this.schema.modelFor(s),i,null,"pushPayload"):i;this.push(n)}normalize(t,e){return this.serializerFor(t).normalizeResponse(this,this.schema.modelFor(t),e,null,"normalize")}pushResource(t){const{type:e,id:s}=t;if(!this.schema.doesTypeExist(e))throw new Error(`Unknown model type: "${e}"`);if(s===null)throw new Error(`Cannot push a resource of type "${e}" without an id`);const i=this.identityMap.get(e,s);if(i)return c.runInAction(()=>{i._applyServerData(null,t.attributes??{},t.relationships)}),this.trackInverseForResource(i,t),i;const n=this.schema.modelFor(e),a=y.Model.push.call(n,{id:s,data:t.attributes??{},relationships:t.relationships,store:this});return this.identityMap.set(e,s,a),this.trackInverseForResource(a,t),a}trackInverseForResource(t,e){if(e.relationships)for(const[s,i]of Object.entries(e.relationships)){const n=this.schema.relationshipsDefinitionFor(t.modelName).get(s);if(!n||!n.options.inverse||!i.data)continue;const a=Array.isArray(i.data)?i.data:[i.data];for(const o of a)this.addInverse(o.type,o.id,n.options.inverse,t)}}addInverse(t,e,s,i){const n=this.identityMap.get(t,e);if(!n)return;const o=this.schema.relationshipsDefinitionFor(t).get(s);if(!o)return;const r=n._getRelationshipRef(s),h={type:i.modelName,id:i.id};c.runInAction(()=>{if(o.kind==="hasMany"){const l=r!=null&&r.data&&Array.isArray(r.data)?r.data:[];l.some(d=>exports.Store.refEquals(d,h))||n._setRelationshipRef(s,{data:[...l,h]})}else n._setRelationshipRef(s,{data:h})})}removeInverse(t,e,s,i){const n=this.identityMap.get(t,e);if(!n)return;const o=this.schema.relationshipsDefinitionFor(t).get(s);if(!o)return;const r=n._getRelationshipRef(s);c.runInAction(()=>{if(o.kind==="hasMany"){const l=(r!=null&&r.data&&Array.isArray(r.data)?r.data:[]).filter(d=>!(d.id===i.id&&d.type===i.modelName));n._setRelationshipRef(s,{data:l})}else n._setRelationshipRef(s,{data:null})})}unloadRecord(t){t.id!==null&&this.identityMap.delete(t.modelName,t.id),this.untrackNewRecord(t),this.relationshipCache.delete(t)}unloadAll(t){var e;if(t){for(const s of this.identityMap.all(t))this.relationshipCache.delete(s);this.identityMap.clear(t),(e=this.newRecords.get(t))==null||e.clear()}else this.identityMap.clear(),this.newRecords.clear()}async findRecord(t,e,s={}){const i=this.peekRecord(t,e);if(i&&!s.reload&&!s.include)return i;const n=this.adapterFor(t);if(n.coalesceFindRequests&&n.findMany&&!s.include)return this.scheduleCoalescedFind(t,e);const a=i?this.createSnapshot(i):this.createEmptySnapshot(t,e),o=s.include?{include:s.include,adapterOptions:s.adapterOptions}:s.adapterOptions?{adapterOptions:s.adapterOptions}:void 0,r=await n.findRecord(this,t,e,a,o),h=this.serializerFor(t).normalizeResponse(this,this.schema.modelFor(t),r,e,"findRecord");return this.push(h)}async findAll(t,e={}){const s=this.adapterFor(t),i=e.include?{include:e.include,adapterOptions:e.adapterOptions}:e.adapterOptions?{adapterOptions:e.adapterOptions}:void 0,n=await s.findAll(this,t,null,[],i),a=this.serializerFor(t).normalizeResponse(this,this.schema.modelFor(t),n,null,"findAll");return this.push(a),this.peekAll(t)}async query(t,e){const s=[],i=new A({modelName:t,query:e,source:()=>s.map(n=>this.peekRecord(t,n)).filter(n=>n!==null),update:async()=>{await this.runQuery(t,e,i,s)}});return await this.runQuery(t,e,i,s),i}async runQuery(t,e,s,i){const a=await this.adapterFor(t).query(this,t,e,s),o=this.serializerFor(t).normalizeResponse(this,this.schema.modelFor(t),a,null,"query");if(this.push(o),i.length=0,Array.isArray(o.data))for(const r of o.data)r.id&&i.push(r.id);o.meta&&s._setMeta(o.meta),o.links&&s._setLinks(o.links)}async queryRecord(t,e){const i=await this.adapterFor(t).queryRecord(this,t,e),n=this.serializerFor(t).normalizeResponse(this,this.schema.modelFor(t),i,null,"queryRecord"),a=this.push(n);return Array.isArray(a)?a[0]??null:a??null}async saveRecord(t,e={}){const s=this.adapterFor(t.modelName),i=this.createSnapshot(t),{isNew:n}=t;let a;n?a=await s.createRecord(this,t.modelName,i):e.patch&&s.patchRecord?a=await s.patchRecord(this,t.modelName,i):a=await s.updateRecord(this,t.modelName,i);const o=this.serializerFor(t.modelName).normalizeResponse(this,this.schema.modelFor(t.modelName),a,t.id,n?"createRecord":"updateRecord"),r=o.data;if(r){const h=r.id??t.id;c.runInAction(()=>{t._applyServerData(h,r.attributes??{},r.relationships)}),n&&h&&(this.untrackNewRecord(t),this.identityMap.set(t.modelName,h,t))}if(o.included)for(const h of o.included)this.pushResource(h);return t}async deleteRecord(t){const e=this.adapterFor(t.modelName),s=this.createSnapshot(t);return await e.deleteRecord(this,t.modelName,s),this.unloadRecord(t),t}async reloadRecord(t){if(!t.id)throw new Error("Cannot reload a record without an id");const e=this.adapterFor(t.modelName),s=this.createSnapshot(t),i=await e.findRecord(this,t.modelName,t.id,s),n=this.serializerFor(t.modelName).normalizeResponse(this,this.schema.modelFor(t.modelName),i,t.id,"findRecord");return this.push(n),t}createSnapshot(t){const{modelName:e}=t,s=this.schema.attributesDefinitionFor(e),i=this.schema.relationshipsDefinitionFor(e),n=t;return{id:t.id,modelName:e,record:t,attr:a=>n._data[a],belongsTo:(a,o)=>{const r=n._getRelationshipRef(a);return!(r!=null&&r.data)||Array.isArray(r.data)?null:o!=null&&o.id?r.data.id:this.peekRecord(r.data.type,r.data.id)},hasMany:(a,o)=>{const r=n._getRelationshipRef(a),h=r!=null&&r.data&&Array.isArray(r.data)?r.data:[];return o!=null&&o.ids?h.map(l=>l.id):h.map(l=>this.peekRecord(l.type,l.id)).filter(l=>l!==null)},changedAttributes:()=>n.changedAttributes(),eachAttribute:a=>{for(const[o,r]of s)a(o,r)},eachRelationship:a=>{for(const[o,r]of i)a(o,r)}}}createEmptySnapshot(t,e){const s=this.schema.attributesDefinitionFor(t),i=this.schema.relationshipsDefinitionFor(t);return{id:e,modelName:t,record:null,attr:()=>{},belongsTo:()=>null,hasMany:()=>[],changedAttributes:()=>({}),eachAttribute:n=>{for(const[a,o]of s)n(a,o)},eachRelationship:n=>{for(const[a,o]of i)n(a,o)}}}getRelationshipCache(t,e){var s;return(s=this.relationshipCache.get(t))==null?void 0:s.get(e)}setRelationshipCache(t,e,s){let i=this.relationshipCache.get(t);i||(i=new Map,this.relationshipCache.set(t,i)),i.set(e,s)}resolveRelationship(t,e,s){const i=s.options.async===!0,n=this.getRelationshipCache(t,e);if(n)return n;const a={parent:t,name:e,meta:s,store:this};if(i){if(s.kind==="belongsTo"){const h=new y.AsyncBelongsTo(a);return this.setRelationshipCache(t,e,h),h}const r=new y.AsyncHasMany(a);return this.setRelationshipCache(t,e,r),r}if(s.kind==="belongsTo"){const r=t._getRelationshipRef(e);return!(r!=null&&r.data)||Array.isArray(r.data)?null:this.peekRecord(r.data.type,r.data.id)}const o=new y.ManyArray(a);return this.setRelationshipCache(t,e,o),o}setRelationshipValue(t,e,s,i){if(s.kind!=="belongsTo")return;const n=t._getRelationshipRef(e),a=n!=null&&n.data&&!Array.isArray(n.data)?n.data:null;if(i==null){c.runInAction(()=>{t._setRelationshipRef(e,{data:null})}),a&&s.options.inverse&&this.removeInverse(a.type,a.id,s.options.inverse,t);return}const o=i,r={type:o.modelName,id:o.id};c.runInAction(()=>{t._setRelationshipRef(e,{data:r})}),s.options.inverse&&(a&&!exports.Store.refEquals(a,r)&&this.removeInverse(a.type,a.id,s.options.inverse,t),this.addInverse(r.type,r.id,s.options.inverse,t))}_getRelationshipRefFor(t,e){return t._getRelationshipRef(e)}_getPendingMembers(t,e){var s;return((s=this.pendingMembers.get(t))==null?void 0:s.get(e))??[]}addPendingMember(t,e,s){let i=this.pendingMembers.get(t);i||(i=new Map,this.pendingMembers.set(t,i));let n=i.get(e);n||(n=c.observable.set(),i.set(e,n)),n.add(s)}removePendingMember(t,e,s){var i,n;(n=(i=this.pendingMembers.get(t))==null?void 0:i.get(e))==null||n.delete(s)}_hasManyAppend(t,e,s,i){if(i.id===null){if(this.addPendingMember(t,e,i),s.options.inverse){const r=this.schema.relationshipsDefinitionFor(i.modelName).get(s.options.inverse);(r==null?void 0:r.kind)==="belongsTo"&&c.runInAction(()=>{i._setRelationshipRef(s.options.inverse,{data:{type:t.modelName,id:t.id}})})}return}const n=this._getRelationshipRefFor(t,e),a=n!=null&&n.data&&Array.isArray(n.data)?n.data:[],o={type:i.modelName,id:i.id};a.some(r=>exports.Store.refEquals(r,o))||c.runInAction(()=>{t._setRelationshipRef(e,{data:[...a,o]})}),s.options.inverse&&this.addInverse(i.modelName,i.id,s.options.inverse,t)}_hasManyRemove(t,e,s,i){if(i.id===null){this.removePendingMember(t,e,i);return}const n=this._getRelationshipRefFor(t,e),o=(n!=null&&n.data&&Array.isArray(n.data)?n.data:[]).filter(r=>!(r.id===i.id&&r.type===i.modelName));c.runInAction(()=>{t._setRelationshipRef(e,{data:o})}),s.options.inverse&&this.removeInverse(i.modelName,i.id,s.options.inverse,t)}scheduleCoalescedFind(t,e){return new Promise((s,i)=>{let n=this.coalescePending.get(t);n||(n=new Map,this.coalescePending.set(t,n));let a=n.get(e);a||(a=[],n.set(e,a)),a.push({resolve:s,reject:i}),this.coalesceScheduled.has(t)||(this.coalesceScheduled.add(t),queueMicrotask(()=>this.flushCoalescedFind(t)))})}async flushCoalescedFind(t){this.coalesceScheduled.delete(t);const e=this.coalescePending.get(t);if(!e||e.size===0)return;const s=new Map(e);e.clear();const i=Array.from(s.keys()),n=this.adapterFor(t);try{const a=i.map(h=>{const l=this.peekRecord(t,h);return l?this.createSnapshot(l):this.createEmptySnapshot(t,h)}),o=await n.findMany(this,t,i,a),r=this.serializerFor(t).normalizeResponse(this,this.schema.modelFor(t),o,null,"findMany");this.push(r);for(const h of i){const l=this.peekRecord(t,h),d=s.get(h);if(d)for(const p of d)l?p.resolve(l):p.reject(new Error(`Record not found after findMany: ${t}:${h}`))}}catch(a){for(const o of s.values())for(const r of o)r.reject(a)}}liveQuery(t,e){return new R({modelName:t,keepAlive:!0,source:()=>{const s=this.identityMap.all(t),i=this.newRecords.get(t);return(i&&i.size>0?[...s,...i]:s).filter(e)}})}async optimisticUpdate(t,e,s){const i=t,n={...i._data};c.runInAction(()=>{Object.assign(i._data,e)});try{return await s(),t}catch(a){throw c.runInAction(()=>{for(const[o,r]of Object.entries(n))i._data[o]=r}),a}}runInTransaction(t){c.runInAction(t)}serialize(t={}){var i;const e={},s=this.identityMap._buckets;for(const[n,a]of s){const o=(i=t.exclude)==null?void 0:i[n],r=[];for(const[h,l]of a){const d=l;let p;if(o&&o.length>0){p={};for(const[f,g]of Object.entries(d._data))o.includes(f)||(p[f]=g)}else p={...d._data};const w={id:h,attributes:p};if(d._relationships&&d._relationships.size>0){const f={};for(const[g,v]of d._relationships)f[g]=v;w.relationships=f}r.push(w)}r.length>0&&(e[n]=r)}return{records:e}}hydrate(t){c.runInAction(()=>{for(const[e,s]of Object.entries(t.records))for(const i of s)this.pushResource({type:e,id:i.id,attributes:i.attributes,relationships:i.relationships})})}static hydrate(t,e){const s=new exports.Store(t);return s.hydrate(e),s}};exports.Store=_([b.singleton(),b.injectable(),m(0,b.inject(M.SchemaService))],exports.Store);exports.AdapterPopulatedRecordArray=A;exports.IdentityMap=k;exports.RecordArray=R;
|
|
2
|
-
//# sourceMappingURL=Store-BdwMrbDi.cjs.map
|