@iamjulianacosta/mobx-data 1.0.1 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{JsonApiSerializer-CKB02AgP.cjs → JsonApiSerializer-Bc4iQB0d.cjs} +2 -2
- package/dist/{JsonApiSerializer-CKB02AgP.cjs.map → JsonApiSerializer-Bc4iQB0d.cjs.map} +1 -1
- package/dist/{JsonApiSerializer-CC5HXp4b.js → JsonApiSerializer-wndq5a1n.js} +2 -2
- package/dist/{JsonApiSerializer-CC5HXp4b.js.map → JsonApiSerializer-wndq5a1n.js.map} +1 -1
- package/dist/{MemoryAdapter-D1cTyydm.cjs → MemoryAdapter-BTK2D64s.cjs} +2 -2
- package/dist/{MemoryAdapter-D1cTyydm.cjs.map → MemoryAdapter-BTK2D64s.cjs.map} +1 -1
- package/dist/{MemoryAdapter-Bx1e7ndV.js → MemoryAdapter-ni25N4H0.js} +2 -2
- package/dist/{MemoryAdapter-Bx1e7ndV.js.map → MemoryAdapter-ni25N4H0.js.map} +1 -1
- package/dist/{ODataAdapter-C4IHK4BK.js → ODataAdapter-DAja_jKM.js} +2 -2
- package/dist/{ODataAdapter-C4IHK4BK.js.map → ODataAdapter-DAja_jKM.js.map} +1 -1
- package/dist/{ODataAdapter-DyyF1sdA.cjs → ODataAdapter-lMifLyLD.cjs} +2 -2
- package/dist/{ODataAdapter-DyyF1sdA.cjs.map → ODataAdapter-lMifLyLD.cjs.map} +1 -1
- package/dist/RestAdapter-1V94stW-.cjs +2 -0
- package/dist/{RestAdapter-CJOwTsKK.cjs.map → RestAdapter-1V94stW-.cjs.map} +1 -1
- package/dist/{RestAdapter-B4aRvs4m.js → RestAdapter-CGWqOR_G.js} +32 -23
- package/dist/{RestAdapter-B4aRvs4m.js.map → RestAdapter-CGWqOR_G.js.map} +1 -1
- package/dist/Store-KvjmBTQ9.cjs +2 -0
- package/dist/Store-KvjmBTQ9.cjs.map +1 -0
- package/dist/{Store-CZ7Z-Nme.js → Store-mvrDLQEZ.js} +195 -150
- package/dist/Store-mvrDLQEZ.js.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-2lswvJ87.cjs +2 -0
- package/dist/cache-utils-2lswvJ87.cjs.map +1 -0
- package/dist/cache-utils-38Dqu4Qf.js +39 -0
- package/dist/cache-utils-38Dqu4Qf.js.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
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
|
+
: undefined;
|
|
665
|
+
if (ttl !== 0) {
|
|
666
|
+
this.cacheNormalizedDocument(doc, ttl);
|
|
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
|
+
: undefined;
|
|
700
|
+
if (ttl !== 0) {
|
|
701
|
+
this.cacheNormalizedDocument(doc, ttl);
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Store-BdwMrbDi.cjs","sources":["../src/store/IdentityMap.ts","../src/store/RecordArray.ts","../src/store/Store.ts"],"sourcesContent":["/**\n * Two-level observable map that serves as the store's record cache.\n *\n * Records are stored in per-type buckets:\n * ```\n * buckets: Map<modelName, Map<id, Model>>\n * ```\n *\n * The outer map is MobX-observable (shallow) so derived views like `peekAll`\n * react when new type buckets are added. Each inner bucket is an observable\n * map so computed properties that iterate records within a type react to\n * additions and deletions.\n *\n * All mutating methods (`set`, `delete`, `clear`) are MobX `action`s so they\n * batch observable updates correctly.\n */\n\nimport { makeObservable, observable, action } from 'mobx';\nimport type { Model } from '@mobx-data/model';\n\nexport class IdentityMap {\n /** @internal */\n readonly _buckets: Map<string, Map<string, Model>> = new Map();\n\n constructor() {\n makeObservable<this, '_buckets'>(this, {\n _buckets: observable.shallow,\n set: action,\n delete: action,\n clear: action,\n });\n }\n\n /**\n * Returns the bucket for `modelName`, optionally creating it when absent.\n * Internal helper — not part of the public API.\n */\n private bucket(modelName: string, create = false): Map<string, Model> | undefined {\n let existing = this._buckets.get(modelName);\n if (!existing && create) {\n existing = observable.map<string, Model>({}, { deep: false });\n this._buckets.set(modelName, existing);\n }\n return existing;\n }\n\n /** Adds or replaces the record with the given `id` under `modelName`. */\n set(modelName: string, id: string, record: Model): void {\n const bucket = this.bucket(modelName, true)!;\n bucket.set(id, record);\n }\n\n /**\n * Returns the record for `modelName` + `id`, or `null` when not found.\n */\n get(modelName: string, id: string): Model | null {\n return this.bucket(modelName)?.get(id) ?? null;\n }\n\n /** Returns `true` when a record exists for `modelName` + `id`. */\n has(modelName: string, id: string): boolean {\n return this.bucket(modelName)?.has(id) ?? false;\n }\n\n /**\n * Removes the record for `modelName` + `id`.\n * @returns `true` when the record existed and was deleted.\n */\n delete(modelName: string, id: string): boolean {\n return this.bucket(modelName)?.delete(id) ?? false;\n }\n\n /** Returns all records stored under `modelName` as an array. */\n all(modelName: string): Model[] {\n const bucket = this.bucket(modelName);\n if (!bucket) {\n return [];\n }\n return Array.from(bucket.values());\n }\n\n /**\n * Clears all records for `modelName`, or all records across all types when\n * `modelName` is omitted.\n */\n clear(modelName?: string): void {\n if (modelName) {\n this.bucket(modelName)?.clear();\n } else {\n for (const bucket of this._buckets.values()) {\n bucket.clear();\n }\n }\n }\n\n /**\n * Returns the number of records stored for `modelName`, or the total across\n * all types when `modelName` is omitted.\n */\n size(modelName?: string): number {\n if (modelName) {\n return this.bucket(modelName)?.size ?? 0;\n }\n let total = 0;\n for (const bucket of this._buckets.values()) {\n total += bucket.size;\n }\n return total;\n }\n}\n","/**\n * Observable array proxies for store query results.\n *\n * Two classes are provided:\n *\n * ## `RecordArray<T>`\n * A live, read-only view of all records of a given type in the store's\n * identity map. The contents are derived via an injected `source` function\n * so they stay in sync automatically with the identity map.\n *\n * Supports `update()` to trigger a background re-fetch if an `update` callback\n * is provided.\n *\n * ## `AdapterPopulatedRecordArray<T>`\n * A subclass used for the results of `store.query()`. Unlike `RecordArray`,\n * its contents are determined by the last adapter response (order preserved,\n * no auto-sync with new pushes).\n *\n * Adds `meta`, `links`, and `query` properties that are set by the store after\n * each adapter call.\n */\n\nimport { makeObservable, computed, observable } from 'mobx';\nimport type { Model } from '@mobx-data/model';\n\n/** Constructor options for `RecordArray`. */\nexport interface RecordArrayOptions<T extends Model> {\n /** Registered model name. */\n modelName: string;\n /** Function that returns the current record list (called on each access). */\n source: () => T[];\n /** Optional async callback invoked by `update()`. */\n update?: () => Promise<void>;\n /**\n * When `true`, the internal MobX computed retains its cached value even when\n * no observers are actively subscribed. Prevents expensive recomputation\n * for long-lived filtered views (e.g. `liveQuery` results).\n */\n keepAlive?: boolean;\n}\n\n/**\n * Live, read-only view of all records of a given type.\n *\n * The `source` function is called on each observable access so the array\n * always reflects the current store state. MobX tracks the `source` call\n * as part of `resolved`, so any computed prop that reads from this array\n * will re-run when the underlying identity map changes.\n */\nexport class RecordArray<T extends Model = Model> implements Iterable<T> {\n private readonly opts: RecordArrayOptions<T>;\n\n /** `true` while `update()` is in progress. */\n protected updating: boolean = false;\n\n constructor(opts: RecordArrayOptions<T>) {\n this.opts = opts;\n makeObservable<this, 'resolved' | 'updating'>(this, {\n resolved: opts.keepAlive ? computed({ keepAlive: true }) : computed,\n updating: observable,\n length: computed,\n modelName: computed,\n });\n }\n\n /** Current record list, derived from the injected `source` function. */\n protected get resolved(): T[] {\n return this.opts.source();\n }\n\n /** `true` while a background `update()` call is in progress. */\n get isLoading(): boolean {\n return this.updating;\n }\n\n /** Alias for `isLoading`. */\n get isUpdating(): boolean {\n return this.updating;\n }\n\n /** Number of records in the array. */\n get length(): number {\n return this.resolved.length;\n }\n\n /** The registered model name for the records in this array. */\n get modelName(): string {\n return this.opts.modelName;\n }\n\n /** Returns the record at `index`, or `undefined`. */\n at(index: number): T | undefined {\n return this.resolved[index];\n }\n\n /** Returns a plain array snapshot of all records. */\n toArray(): T[] {\n return [...this.resolved];\n }\n\n /** Maps over records. */\n map<R>(callback: (record: T, i: number) => R): R[] {\n return this.resolved.map(callback);\n }\n\n /** Filters records. */\n filter(predicate: (record: T, i: number) => boolean): T[] {\n return this.resolved.filter(predicate);\n }\n\n /** Iterates records. */\n forEach(callback: (record: T, i: number) => void): void {\n this.resolved.forEach(callback);\n }\n\n /** Returns `true` when `record` is in the array. */\n includes(record: T): boolean {\n return this.resolved.includes(record);\n }\n\n /**\n * Triggers the `update` callback (if any) to refresh the array from the\n * adapter. Sets `isLoading` while the request is in flight.\n */\n async update(): Promise<this> {\n if (!this.opts.update) {\n return this;\n }\n this.updating = true;\n try {\n await this.opts.update();\n } finally {\n this.updating = false;\n }\n return this;\n }\n\n [Symbol.iterator](): Iterator<T> {\n return this.resolved[Symbol.iterator]();\n }\n}\n\n/** Constructor options for `AdapterPopulatedRecordArray`. */\nexport interface AdapterPopulatedRecordArrayOptions<T extends Model>\n extends RecordArrayOptions<T> {\n /** Query parameters that produced this result set. */\n query: Record<string, unknown>;\n /** Server-side metadata (pagination, total counts, etc.). */\n meta?: Record<string, unknown>;\n /** Pagination or related links. */\n links?: Record<string, string>;\n}\n\n/**\n * Record array populated by an adapter query response.\n *\n * The store creates one of these for each `store.query()` call. Its contents\n * are driven by the id list returned from the last adapter call, not by live\n * identity map iteration.\n *\n * Call `.update()` to re-issue the original query and refresh the contents.\n */\nexport class AdapterPopulatedRecordArray<\n T extends Model = Model,\n> extends RecordArray<T> {\n private queryParams: Record<string, unknown>;\n\n private metaData: Record<string, unknown>;\n\n private linksData: Record<string, string>;\n\n constructor(opts: AdapterPopulatedRecordArrayOptions<T>) {\n super(opts);\n this.queryParams = opts.query;\n this.metaData = opts.meta ?? {};\n this.linksData = opts.links ?? {};\n makeObservable<this, 'metaData' | 'linksData'>(this, {\n metaData: observable.ref,\n linksData: observable.ref,\n meta: computed,\n links: computed,\n query: computed,\n });\n }\n\n /** Server-side metadata attached to the last response (e.g. pagination). */\n get meta(): Record<string, unknown> {\n return this.metaData;\n }\n\n /** Links attached to the last response. */\n get links(): Record<string, string> {\n return this.linksData;\n }\n\n /** The query parameters that produced this array. */\n get query(): Record<string, unknown> {\n return this.queryParams;\n }\n\n /** Called by the store to update `meta` after a successful query. */\n _setMeta(meta: Record<string, unknown>): void {\n this.metaData = meta;\n }\n\n /** Called by the store to update `links` after a successful query. */\n _setLinks(links: Record<string, string>): void {\n this.linksData = links;\n }\n}\n","/**\n * Central store — the top-level coordinator for the mobx-data runtime.\n *\n * `Store` orchestrates every aspect of the data layer:\n *\n * - **Identity Map** — a two-level `IdentityMap` keyed by `modelName → id`.\n * Records are merged in-place on repeated `push` calls so live references\n * always reflect the latest server state.\n *\n * - **Finding records** — `findRecord`, `findAll`, `query`, `queryRecord`\n * delegate to the registered adapter, then pass the raw response through the\n * registered serializer before pushing normalized data into the identity map.\n *\n * - **Saving records** — `saveRecord` calls `adapter.createRecord` or\n * `adapter.updateRecord` depending on `record.isNew`, then applies the\n * server response back onto the record via `_applyServerData`.\n *\n * - **Relationships** — `resolveRelationship` returns sync (`ManyArray`) or\n * async (`AsyncBelongsTo` / `AsyncHasMany`) proxy objects backed by the\n * store. `setRelationshipValue` updates `belongsTo` refs and keeps their\n * inverses in sync. `_hasManyAppend` / `_hasManyRemove` manage `hasMany`\n * refs and their inverses.\n *\n * - **Inverse tracking** — `addInverse` / `removeInverse` keep both sides of\n * a declared `inverse` relationship consistent when either side is mutated.\n *\n * ## Registration\n *\n * ```ts\n * store.registerAdapter('application', new RestAdapter());\n * store.registerSerializer('application', new JsonSerializer());\n * ```\n *\n * Adapters and serializers are looked up first by exact model name, then by\n * the special `'application'` fallback.\n */\n\nimport {\n singleton, inject, injectable,\n} from 'tsyringe';\nimport { runInAction, observable, computed, makeObservable } from 'mobx';\nimport {\n SchemaService,\n type RelationshipDef,\n type AttributeDef,\n} from '@mobx-data/schema';\nimport {\n Model,\n ManyArray,\n AsyncBelongsTo,\n AsyncHasMany,\n type RelationshipRef,\n type ModelStoreLike,\n type SaveOptions,\n} from '@mobx-data/model';\nimport { IdentityMap } from './IdentityMap.js';\nimport {\n RecordArray,\n AdapterPopulatedRecordArray,\n} from './RecordArray.js';\n\n/** Options forwarded from the Store to adapter fetch methods. */\nexport interface AdapterFetchOptions {\n include?: string;\n adapterOptions?: Record<string, unknown>;\n}\n\n/** Minimal adapter interface the Store depends on. */\nexport interface AdapterLike {\n findRecord(\n store: Store,\n modelName: string,\n id: string,\n snapshot: unknown,\n options?: AdapterFetchOptions,\n ): Promise<unknown>;\n findAll(\n store: Store,\n modelName: string,\n sinceToken: string | null,\n snapshotArray: unknown,\n options?: AdapterFetchOptions,\n ): Promise<unknown>;\n findMany?(\n store: Store,\n modelName: string,\n ids: string[],\n snapshots: unknown,\n ): Promise<unknown>;\n query(\n store: Store,\n modelName: string,\n query: Record<string, unknown>,\n recordArray: AdapterPopulatedRecordArray,\n ): Promise<unknown>;\n queryRecord(\n store: Store,\n modelName: string,\n query: Record<string, unknown>,\n ): Promise<unknown>;\n createRecord(\n store: Store,\n modelName: string,\n snapshot: unknown,\n ): Promise<unknown>;\n updateRecord(\n store: Store,\n modelName: string,\n snapshot: unknown,\n ): Promise<unknown>;\n patchRecord?(\n store: Store,\n modelName: string,\n snapshot: unknown,\n ): Promise<unknown>;\n deleteRecord(\n store: Store,\n modelName: string,\n snapshot: unknown,\n ): Promise<unknown>;\n /** When `true` the store coalesces multiple `findRecord` calls into one `findMany`. */\n coalesceFindRequests?: boolean;\n}\n\n/** Minimal serializer interface the Store depends on. */\nexport interface SerializerLike {\n normalize(\n store: Store,\n modelClass: unknown,\n payload: unknown,\n prop?: string,\n ): unknown;\n normalizeResponse(\n store: Store,\n modelClass: unknown,\n payload: unknown,\n id: string | null,\n requestType: string,\n ): unknown;\n serialize(snapshot: unknown, options?: Record<string, unknown>): unknown;\n extractErrors?(\n store: Store,\n modelClass: unknown,\n payload: unknown,\n id: string | null,\n ): Record<string, string[]>;\n}\n\n/** Options for `findRecord` and `findAll`. */\nexport interface FindOptions {\n /** When `true`, bypass the cache and always hit the network. */\n reload?: boolean;\n /** When `true`, return the cached value and refetch in the background. */\n backgroundReload?: boolean;\n /** Adapter-specific options forwarded to the adapter method. */\n adapterOptions?: Record<string, unknown>;\n /** Comma-separated relationship paths to include (e.g. `'author,comments'`). */\n include?: string;\n}\n\n/** Normalized resource shape consumed internally by the store. */\nexport interface NormalizedResource {\n type: string;\n id: string | null;\n attributes?: Record<string, unknown>;\n relationships?: Record<string, RelationshipRef>;\n}\n\n/** Normalized document containing primary data and optional side-loaded records. */\nexport interface NormalizedDocument {\n data: NormalizedResource | NormalizedResource[] | null;\n included?: NormalizedResource[];\n meta?: Record<string, unknown>;\n links?: Record<string, string>;\n}\n\n/** Internal snapshot interface created and consumed by the store. */\ninterface Snapshot {\n id: string | null;\n modelName: string;\n record: Model;\n attr(key: string): unknown;\n belongsTo(key: string, options?: { id?: boolean }): unknown;\n hasMany(key: string, options?: { ids?: boolean }): unknown;\n changedAttributes(): Record<string, [unknown, unknown]>;\n eachAttribute(fn: (key: string, meta: AttributeDef) => void): void;\n eachRelationship(fn: (key: string, meta: RelationshipDef) => void): void;\n}\n\n@singleton()\n@injectable()\nexport class Store implements ModelStoreLike {\n static refEquals(\n a: { type: string; id: string },\n b: { type: string; id: string },\n ): boolean {\n return a.id === b.id && a.type === b.type;\n }\n\n /** Schema registry used to look up model classes and their metadata. */\n readonly schema: SchemaService;\n\n /** Two-level identity map: `modelName → id → record`. */\n readonly identityMap: IdentityMap = new IdentityMap();\n\n private adapters: Map<string, AdapterLike> = new Map();\n\n private serializers: Map<string, SerializerLike> = new Map();\n\n /** Tracks unsaved (new) records by model name. */\n private newRecords: Map<string, Set<Model>> = new Map();\n\n /** Reverse index: record → modelName for O(1) untrackNewRecord. */\n private newRecordTypes: WeakMap<Model, string> = new WeakMap();\n\n /** Per-record cache of relationship proxy objects (ManyArray / AsyncBelongsTo / AsyncHasMany). */\n private relationshipCache: WeakMap<\n Model,\n Map<string, ManyArray | AsyncBelongsTo | AsyncHasMany>\n > = new WeakMap();\n\n /** Tracks new records that have been appended to a hasMany but not yet saved. */\n private pendingMembers: WeakMap<Model, Map<string, Set<Model>>> = new WeakMap();\n\n constructor(@inject(SchemaService) schema: SchemaService) {\n this.schema = schema;\n }\n\n // --- registration ---\n\n /** Registers an adapter for a given model name (or `'application'` as a fallback). */\n registerAdapter(modelName: string, adapter: AdapterLike): void {\n this.adapters.set(modelName, adapter);\n }\n\n /** Registers a serializer for a given model name (or `'application'` as a fallback). */\n registerSerializer(modelName: string, serializer: SerializerLike): void {\n this.serializers.set(modelName, serializer);\n }\n\n /**\n * Returns the adapter for `modelName`, falling back to `'application'`.\n * @throws when no adapter is registered.\n */\n adapterFor(modelName: string): AdapterLike {\n const adapter = this.adapters.get(modelName) ?? this.adapters.get('application');\n if (!adapter) {\n throw new Error(`No adapter registered for \"${modelName}\"`);\n }\n return adapter;\n }\n\n /**\n * Returns the serializer for `modelName`, falling back to `'application'`.\n * @throws when no serializer is registered.\n */\n serializerFor(modelName: string): SerializerLike {\n const serializer = this.serializers.get(modelName)\n ?? this.serializers.get('application');\n if (!serializer) {\n throw new Error(`No serializer registered for \"${modelName}\"`);\n }\n return serializer;\n }\n\n // --- creating ---\n\n /**\n * Creates a new (unsaved) record of the given type with optional initial data.\n * The record is tracked in `newRecords` until it is saved or rolled back.\n *\n * @throws when `modelName` has not been registered with `SchemaService`.\n */\n createRecord<T extends Model = Model>(\n modelName: string,\n data: Record<string, unknown> = {},\n ): T {\n if (!this.schema.doesTypeExist(modelName)) {\n throw new Error(`Unknown model type: \"${modelName}\"`);\n }\n const Klass = this.schema.modelFor(modelName) as unknown as new (\n opts: { id: null; data: Record<string, unknown>; store: Store },\n ) => Model;\n const record = new Klass({ id: null, data, store: this });\n this.trackNewRecord(modelName, record);\n return record as T;\n }\n\n private trackNewRecord(modelName: string, record: Model): void {\n let set = this.newRecords.get(modelName);\n if (!set) {\n set = new Set();\n this.newRecords.set(modelName, set);\n }\n set.add(record);\n this.newRecordTypes.set(record, modelName);\n }\n\n private untrackNewRecord(record: Model): void {\n const modelName = this.newRecordTypes.get(record);\n if (modelName) {\n this.newRecords.get(modelName)?.delete(record);\n this.newRecordTypes.delete(record);\n }\n }\n\n // --- peeking ---\n\n /**\n * Synchronously returns a record from the identity map, or `null` when not\n * found. Does not trigger a network request.\n */\n peekRecord<T extends Model = Model>(modelName: string, id: string): T | null {\n const key = id === null || id === undefined ? null : String(id);\n if (key === null) {\n return null;\n }\n return (this.identityMap.get(modelName, key) as T | null) ?? null;\n }\n\n /**\n * Returns a live `RecordArray` backed by the identity map for `modelName`.\n * New (unsaved) records are included at the end.\n * Does not trigger a network request.\n */\n peekAll<T extends Model = Model>(modelName: string): RecordArray<T> {\n return new RecordArray<T>({\n modelName,\n source: () => {\n const persisted = this.identityMap.all(modelName) as T[];\n const newRecordsForType = this.newRecords.get(modelName);\n if (!newRecordsForType || newRecordsForType.size === 0) {\n return persisted;\n }\n return [...persisted, ...(newRecordsForType as unknown as Set<T>)];\n },\n });\n }\n\n // --- push / normalize ---\n\n /**\n * Pushes a normalized document into the identity map.\n * Side-loaded (`included`) records are pushed first.\n *\n * @returns The primary record(s), or `null` for empty payloads.\n */\n push(doc: unknown): Model | Model[] | null {\n const document = doc as NormalizedDocument;\n if (document.included) {\n for (const resource of document.included) {\n this.pushResource(resource);\n }\n }\n if (document.data === null || document.data === undefined) {\n return null;\n }\n if (Array.isArray(document.data)) {\n return document.data.map((resource) => this.pushResource(resource));\n }\n return this.pushResource(document.data);\n }\n\n /**\n * Normalizes a raw payload via the registered serializer and pushes the\n * result. `modelName` is optional; when omitted the payload is pushed\n * directly without normalization.\n */\n pushPayload(modelNameOrPayload: string | unknown, payload?: unknown): void {\n let modelName: string | null;\n let body: unknown;\n if (typeof modelNameOrPayload === 'string') {\n modelName = modelNameOrPayload;\n body = payload;\n } else {\n modelName = null;\n body = modelNameOrPayload;\n }\n const normalized = modelName\n ? this.serializerFor(modelName).normalizeResponse(\n this,\n this.schema.modelFor(modelName),\n body,\n null,\n 'pushPayload',\n )\n : body;\n this.push(normalized);\n }\n\n /**\n * Normalizes a raw payload for `modelName` via the registered serializer\n * and returns the `NormalizedDocument` without pushing it.\n */\n normalize(modelName: string, payload: unknown): NormalizedDocument {\n return this.serializerFor(modelName).normalizeResponse(\n this,\n this.schema.modelFor(modelName),\n payload,\n null,\n 'normalize',\n ) as NormalizedDocument;\n }\n\n /**\n * Inserts or merges a single normalized resource into the identity map.\n * - Existing record → calls `_applyServerData` to merge attributes and\n * relationships in place (preserving the live reference).\n * - New record → instantiates via `Model.push` and sets it in the map.\n *\n * @throws when `type` has not been registered or `id` is `null`.\n */\n private pushResource(resource: NormalizedResource): Model {\n const { type, id } = resource;\n if (!this.schema.doesTypeExist(type)) {\n throw new Error(`Unknown model type: \"${type}\"`);\n }\n if (id === null) {\n throw new Error(`Cannot push a resource of type \"${type}\" without an id`);\n }\n const existing = this.identityMap.get(type, id);\n if (existing) {\n runInAction(() => {\n (existing as unknown as {\n _applyServerData(\n id: string | null,\n data: Record<string, unknown>,\n relationships?: Record<string, RelationshipRef>,\n ): void;\n })._applyServerData(null, resource.attributes ?? {}, resource.relationships);\n });\n this.trackInverseForResource(existing, resource);\n return existing;\n }\n const Klass = this.schema.modelFor(type) as unknown as new (\n opts: {\n id: string;\n data: Record<string, unknown>;\n relationships?: Record<string, RelationshipRef>;\n store: Store;\n },\n ) => Model;\n const record = Model.push.call(Klass as unknown as typeof Model, {\n id,\n data: resource.attributes ?? {},\n relationships: resource.relationships,\n store: this,\n }) as Model;\n this.identityMap.set(type, id, record);\n this.trackInverseForResource(record, resource);\n return record;\n }\n\n /**\n * After pushing a resource, updates the inverse side of every declared\n * inverse relationship so both sides stay consistent.\n */\n private trackInverseForResource(record: Model, resource: NormalizedResource): void {\n if (!resource.relationships) {\n return;\n }\n for (const [name, ref] of Object.entries(resource.relationships)) {\n const meta = this.schema.relationshipsDefinitionFor(record.modelName).get(name);\n if (!meta) {\n continue;\n }\n if (!meta.options.inverse) {\n continue;\n }\n if (!ref.data) {\n continue;\n }\n const items = Array.isArray(ref.data) ? ref.data : [ref.data];\n for (const item of items) {\n this.addInverse(item.type, item.id, meta.options.inverse, record);\n }\n }\n }\n\n /**\n * Adds `inverseRecord` to the inverse relationship on `targetType:targetId`.\n * No-ops when the target record is not in the identity map.\n */\n private addInverse(\n targetType: string,\n targetId: string,\n inverseName: string,\n inverseRecord: Model,\n ): void {\n const target = this.identityMap.get(targetType, targetId);\n if (!target) {\n return;\n }\n const targetRelationships = this.schema.relationshipsDefinitionFor(targetType);\n const inverseMeta = targetRelationships.get(inverseName);\n if (!inverseMeta) {\n return;\n }\n const existing = (target as unknown as {\n _getRelationshipRef(name: string): RelationshipRef | null;\n })._getRelationshipRef(inverseName);\n const inverseEntry = { type: inverseRecord.modelName, id: inverseRecord.id! };\n runInAction(() => {\n if (inverseMeta.kind === 'hasMany') {\n const currentData = existing?.data && Array.isArray(existing.data)\n ? existing.data : [];\n if (!currentData.some((reference) => Store.refEquals(reference, inverseEntry))) {\n (target as unknown as {\n _setRelationshipRef(name: string, ref: RelationshipRef): void;\n })._setRelationshipRef(inverseName, { data: [...currentData, inverseEntry] });\n }\n } else {\n (target as unknown as {\n _setRelationshipRef(name: string, ref: RelationshipRef): void;\n })._setRelationshipRef(inverseName, { data: inverseEntry });\n }\n });\n }\n\n /**\n * Removes `inverseRecord` from the inverse relationship on `targetType:targetId`.\n * No-ops when the target record is not in the identity map.\n */\n private removeInverse(\n targetType: string,\n targetId: string,\n inverseName: string,\n inverseRecord: Model,\n ): void {\n const target = this.identityMap.get(targetType, targetId);\n if (!target) {\n return;\n }\n const targetRelationships = this.schema.relationshipsDefinitionFor(targetType);\n const inverseMeta = targetRelationships.get(inverseName);\n if (!inverseMeta) {\n return;\n }\n const existing = (target as unknown as {\n _getRelationshipRef(name: string): RelationshipRef | null;\n })._getRelationshipRef(inverseName);\n runInAction(() => {\n if (inverseMeta.kind === 'hasMany') {\n const currentData = existing?.data && Array.isArray(existing.data)\n ? existing.data : [];\n const items = currentData.filter((reference) => !(\n reference.id === inverseRecord.id\n && reference.type === inverseRecord.modelName\n ));\n (target as unknown as {\n _setRelationshipRef(name: string, ref: RelationshipRef): void;\n })._setRelationshipRef(inverseName, { data: items });\n } else {\n (target as unknown as {\n _setRelationshipRef(name: string, ref: RelationshipRef): void;\n })._setRelationshipRef(inverseName, { data: null });\n }\n });\n }\n\n // --- unload ---\n\n /**\n * Removes a record from the identity map and clears its relationship cache.\n * Called by `record.unloadRecord()` and internally after `deleteRecord`.\n */\n unloadRecord(record: Model): void {\n if (record.id !== null) {\n this.identityMap.delete(record.modelName, record.id);\n }\n this.untrackNewRecord(record);\n this.relationshipCache.delete(record);\n }\n\n /**\n * Unloads all records for `modelName`, or all records across all types when\n * `modelName` is omitted.\n */\n unloadAll(modelName?: string): void {\n if (modelName) {\n for (const record of this.identityMap.all(modelName)) {\n this.relationshipCache.delete(record);\n }\n this.identityMap.clear(modelName);\n this.newRecords.get(modelName)?.clear();\n } else {\n this.identityMap.clear();\n this.newRecords.clear();\n }\n }\n\n // --- find ---\n\n /**\n * Finds a single record by id. Returns the cached record immediately when\n * `options.reload` is not set; otherwise re-fetches.\n *\n * When the adapter has `coalesceFindRequests: true` and `findMany` is\n * implemented, multiple concurrent `findRecord` calls for the same type\n * are batched into a single `findMany` network request.\n */\n async findRecord<T extends Model = Model>(\n modelName: string,\n id: string,\n options: FindOptions = {},\n ): Promise<T> {\n const cached = this.peekRecord<T>(modelName, id);\n if (cached && !options.reload && !options.include) {\n return cached;\n }\n const adapter = this.adapterFor(modelName);\n\n if (adapter.coalesceFindRequests && adapter.findMany && !options.include) {\n return this.scheduleCoalescedFind(modelName, id) as Promise<T>;\n }\n\n const snapshot = cached ? this.createSnapshot(cached) : this.createEmptySnapshot(modelName, id);\n const adapterOptions: AdapterFetchOptions | undefined = options.include\n ? { include: options.include, adapterOptions: options.adapterOptions }\n : options.adapterOptions ? { adapterOptions: options.adapterOptions } : undefined;\n const response = await adapter.findRecord(this, modelName, id, snapshot, adapterOptions);\n const doc = this.serializerFor(modelName).normalizeResponse(\n this,\n this.schema.modelFor(modelName),\n response,\n id,\n 'findRecord',\n ) as NormalizedDocument;\n const record = this.push(doc);\n return record as T;\n }\n\n /**\n * Fetches all records of `modelName` from the server and returns a\n * `RecordArray` backed by the identity map.\n */\n async findAll<T extends Model = Model>(\n modelName: string,\n options: FindOptions = {},\n ): Promise<RecordArray<T>> {\n const adapter = this.adapterFor(modelName);\n const adapterOptions: AdapterFetchOptions | undefined = options.include\n ? { include: options.include, adapterOptions: options.adapterOptions }\n : options.adapterOptions ? { adapterOptions: options.adapterOptions } : undefined;\n const response = await adapter.findAll(this, modelName, null, [], adapterOptions);\n const doc = this.serializerFor(modelName).normalizeResponse(\n this,\n this.schema.modelFor(modelName),\n response,\n null,\n 'findAll',\n ) as NormalizedDocument;\n this.push(doc);\n return this.peekAll<T>(modelName);\n }\n\n /**\n * Executes an adapter query and returns an `AdapterPopulatedRecordArray`\n * whose `update()` method re-issues the same query.\n */\n async query<T extends Model = Model>(\n modelName: string,\n params: Record<string, unknown>,\n ): Promise<AdapterPopulatedRecordArray<T>> {\n const ids: string[] = [];\n const array = new AdapterPopulatedRecordArray<T>({\n modelName,\n query: params,\n source: () => ids\n .map((id) => this.peekRecord<T>(modelName, id))\n .filter((r): r is T => r !== null),\n update: async () => {\n await this.runQuery(modelName, params, array, ids);\n },\n });\n await this.runQuery(modelName, params, array, ids);\n return array;\n }\n\n private async runQuery<T extends Model>(\n modelName: string,\n params: Record<string, unknown>,\n array: AdapterPopulatedRecordArray<T>,\n ids: string[],\n ): Promise<void> {\n const adapter = this.adapterFor(modelName);\n const response = await adapter.query(this, modelName, params, array);\n const doc = this.serializerFor(modelName).normalizeResponse(\n this,\n this.schema.modelFor(modelName),\n response,\n null,\n 'query',\n ) as NormalizedDocument;\n this.push(doc);\n ids.length = 0;\n if (Array.isArray(doc.data)) {\n for (const resource of doc.data) {\n if (resource.id) {\n ids.push(resource.id);\n }\n }\n }\n if (doc.meta) {\n array._setMeta(doc.meta);\n }\n if (doc.links) {\n array._setLinks(doc.links);\n }\n }\n\n /**\n * Executes an adapter query that returns at most one record.\n * Returns `null` when the adapter returns an empty payload.\n */\n async queryRecord<T extends Model = Model>(\n modelName: string,\n params: Record<string, unknown>,\n ): Promise<T | null> {\n const adapter = this.adapterFor(modelName);\n const response = await adapter.queryRecord(this, modelName, params);\n const doc = this.serializerFor(modelName).normalizeResponse(\n this,\n this.schema.modelFor(modelName),\n response,\n null,\n 'queryRecord',\n ) as NormalizedDocument;\n const result = this.push(doc);\n if (Array.isArray(result)) {\n return (result[0] ?? null) as T | null;\n }\n return (result as T | null) ?? null;\n }\n\n // --- save / delete / reload from Model ---\n\n /**\n * Persists a record to the server.\n * - New records → `adapter.createRecord` (POST)\n * - Existing dirty records → `adapter.updateRecord` (PUT) by default\n * - With `{ patch: true }` → `adapter.patchRecord` (PATCH, partial payload)\n *\n * After the response is received the server data is applied back to the\n * record via `_applyServerData` so it transitions to `saved`.\n */\n async saveRecord<T extends Model>(record: T, options: SaveOptions = {}): Promise<T> {\n const adapter = this.adapterFor(record.modelName);\n const snapshot = this.createSnapshot(record);\n const { isNew } = record;\n let response: unknown;\n if (isNew) {\n response = await adapter.createRecord(this, record.modelName, snapshot);\n } else if (options.patch && adapter.patchRecord) {\n response = await adapter.patchRecord(this, record.modelName, snapshot);\n } else {\n response = await adapter.updateRecord(this, record.modelName, snapshot);\n }\n const doc = this.serializerFor(record.modelName).normalizeResponse(\n this,\n this.schema.modelFor(record.modelName),\n response,\n record.id,\n isNew ? 'createRecord' : 'updateRecord',\n ) as NormalizedDocument;\n const data = doc.data as NormalizedResource | null;\n if (data) {\n const newId = data.id ?? record.id;\n runInAction(() => {\n (record as unknown as {\n _applyServerData(\n id: string | null,\n data: Record<string, unknown>,\n relationships?: Record<string, RelationshipRef>,\n ): void;\n })._applyServerData(newId, data.attributes ?? {}, data.relationships);\n });\n if (isNew && newId) {\n this.untrackNewRecord(record);\n this.identityMap.set(record.modelName, newId, record);\n }\n }\n if (doc.included) {\n for (const resource of doc.included) {\n this.pushResource(resource);\n }\n }\n return record;\n }\n\n /**\n * Issues a DELETE request and unloads the record from the identity map.\n */\n async deleteRecord<T extends Model>(record: T): Promise<T> {\n const adapter = this.adapterFor(record.modelName);\n const snapshot = this.createSnapshot(record);\n await adapter.deleteRecord(this, record.modelName, snapshot);\n this.unloadRecord(record);\n return record;\n }\n\n /**\n * Re-fetches a record from the server and merges the response into the\n * existing instance.\n */\n async reloadRecord<T extends Model>(record: T): Promise<T> {\n if (!record.id) {\n throw new Error('Cannot reload a record without an id');\n }\n const adapter = this.adapterFor(record.modelName);\n const snapshot = this.createSnapshot(record);\n const response = await adapter.findRecord(this, record.modelName, record.id, snapshot);\n const doc = this.serializerFor(record.modelName).normalizeResponse(\n this,\n this.schema.modelFor(record.modelName),\n response,\n record.id,\n 'findRecord',\n ) as NormalizedDocument;\n this.push(doc);\n return record;\n }\n\n // --- snapshot ---\n\n /**\n * Creates a `Snapshot` for a live record.\n * The snapshot reads directly from the record's internal state so it\n * reflects the current (possibly dirty) values.\n */\n createSnapshot(record: Model): Snapshot {\n const { modelName } = record;\n const attributes = this.schema.attributesDefinitionFor(modelName);\n const relationships = this.schema.relationshipsDefinitionFor(modelName);\n const internal = record as unknown as {\n _data: Record<string, unknown>;\n _getRelationshipRef(name: string): RelationshipRef | null;\n changedAttributes(): Record<string, [unknown, unknown]>;\n };\n return {\n id: record.id,\n modelName,\n record,\n attr: (key) => internal._data[key],\n belongsTo: (key, options) => {\n const ref = internal._getRelationshipRef(key);\n if (!ref?.data || Array.isArray(ref.data)) {\n return null;\n }\n if (options?.id) {\n return ref.data.id;\n }\n return this.peekRecord(ref.data.type, ref.data.id);\n },\n hasMany: (key, options) => {\n const ref = internal._getRelationshipRef(key);\n const items: Array<{ type: string; id: string }> = ref?.data\n && Array.isArray(ref.data) ? ref.data : [];\n if (options?.ids) {\n return items.map((item) => item.id);\n }\n return items\n .map((item) => this.peekRecord(item.type, item.id))\n .filter((resolvedRecord): resolvedRecord is Model => resolvedRecord !== null);\n },\n changedAttributes: () => internal.changedAttributes(),\n eachAttribute: (callback) => {\n for (const [key, meta] of attributes) {\n callback(key, meta);\n }\n },\n eachRelationship: (callback) => {\n for (const [key, meta] of relationships) {\n callback(key, meta);\n }\n },\n };\n }\n\n /**\n * Creates a placeholder `Snapshot` for a record that is not yet in the\n * identity map (used when fetching a record that isn't cached).\n */\n private createEmptySnapshot(modelName: string, id: string): Snapshot {\n const attributes = this.schema.attributesDefinitionFor(modelName);\n const relationships = this.schema.relationshipsDefinitionFor(modelName);\n return {\n id,\n modelName,\n record: null as unknown as Model,\n attr: () => undefined,\n belongsTo: () => null,\n hasMany: () => [],\n changedAttributes: () => ({}),\n eachAttribute: (callback) => {\n for (const [key, meta] of attributes) {\n callback(key, meta);\n }\n },\n eachRelationship: (callback) => {\n for (const [key, meta] of relationships) {\n callback(key, meta);\n }\n },\n };\n }\n\n // --- relationship resolution (called by Model) ---\n\n private getRelationshipCache(\n record: Model,\n name: string,\n ): ManyArray | AsyncBelongsTo | AsyncHasMany | undefined {\n return this.relationshipCache.get(record)?.get(name);\n }\n\n private setRelationshipCache(\n record: Model,\n name: string,\n value: ManyArray | AsyncBelongsTo | AsyncHasMany,\n ): void {\n let m = this.relationshipCache.get(record);\n if (!m) {\n m = new Map();\n this.relationshipCache.set(record, m);\n }\n m.set(name, value);\n }\n\n /**\n * Called by the `Model` relationship getter to resolve a relationship.\n *\n * - **Async** `belongsTo` → returns an `AsyncBelongsTo` wrapper.\n * - **Async** `hasMany` → returns an `AsyncHasMany` wrapper.\n * - **Sync** `belongsTo` → peeks the related record from the identity map.\n * - **Sync** `hasMany` → returns a `ManyArray` backed by the store.\n *\n * Results are cached per record + name so the same proxy is returned on\n * repeated accesses (important for MobX observability).\n */\n resolveRelationship(record: Model, name: string, meta: RelationshipDef): unknown {\n const isAsync = meta.options.async === true;\n const cached = this.getRelationshipCache(record, name);\n if (cached) {\n return cached;\n }\n const hostWithStore = {\n parent: record,\n name,\n meta,\n store: this as unknown as never,\n };\n\n if (isAsync) {\n if (meta.kind === 'belongsTo') {\n const wrapper = new AsyncBelongsTo(hostWithStore as never);\n this.setRelationshipCache(record, name, wrapper);\n return wrapper;\n }\n const wrapper = new AsyncHasMany(hostWithStore as never);\n this.setRelationshipCache(record, name, wrapper);\n return wrapper;\n }\n\n if (meta.kind === 'belongsTo') {\n const ref = (record as unknown as {\n _getRelationshipRef(name: string): RelationshipRef | null;\n })._getRelationshipRef(name);\n if (!ref?.data || Array.isArray(ref.data)) {\n return null;\n }\n return this.peekRecord(ref.data.type, ref.data.id);\n }\n const arr = new ManyArray(hostWithStore as never);\n this.setRelationshipCache(record, name, arr);\n return arr;\n }\n\n /**\n * Called by the `Model` `belongsTo` setter to update a relationship ref\n * and keep its inverse in sync.\n */\n setRelationshipValue(\n record: Model,\n name: string,\n meta: RelationshipDef,\n value: unknown,\n ): void {\n if (meta.kind !== 'belongsTo') {\n return;\n }\n const ref = (record as unknown as {\n _getRelationshipRef(name: string): RelationshipRef | null;\n })._getRelationshipRef(name);\n const prev = ref?.data && !Array.isArray(ref.data) ? ref.data : null;\n\n if (value === null || value === undefined) {\n runInAction(() => {\n (record as unknown as {\n _setRelationshipRef(name: string, ref: RelationshipRef): void;\n })._setRelationshipRef(name, { data: null });\n });\n if (prev && meta.options.inverse) {\n this.removeInverse(prev.type, prev.id, meta.options.inverse, record);\n }\n return;\n }\n\n const target = value as Model;\n const newRef = { type: target.modelName, id: target.id! };\n runInAction(() => {\n (record as unknown as {\n _setRelationshipRef(name: string, ref: RelationshipRef): void;\n })._setRelationshipRef(name, { data: newRef });\n });\n\n if (meta.options.inverse) {\n if (prev && !Store.refEquals(prev, newRef)) {\n this.removeInverse(prev.type, prev.id, meta.options.inverse, record);\n }\n this.addInverse(newRef.type, newRef.id, meta.options.inverse, record);\n }\n }\n\n // --- hooks used by ManyArray ---\n\n /** Returns the raw relationship ref stored on `record` for `name`. */\n _getRelationshipRefFor(record: Model, name: string): RelationshipRef | null {\n return (record as unknown as {\n _getRelationshipRef(n: string): RelationshipRef | null;\n })._getRelationshipRef(name);\n }\n\n /** Returns any pending (unsaved) members for a `hasMany` relationship. */\n _getPendingMembers(record: Model, name: string): Iterable<Model> {\n return this.pendingMembers.get(record)?.get(name) ?? [];\n }\n\n private addPendingMember(record: Model, name: string, value: Model): void {\n let byName = this.pendingMembers.get(record);\n if (!byName) {\n byName = new Map();\n this.pendingMembers.set(record, byName);\n }\n let set = byName.get(name);\n if (!set) {\n set = observable.set<Model>();\n byName.set(name, set);\n }\n set.add(value);\n }\n\n private removePendingMember(record: Model, name: string, value: Model): void {\n this.pendingMembers.get(record)?.get(name)?.delete(value);\n }\n\n /**\n * Appends `value` to the `hasMany` relationship ref on `record` and syncs\n * the inverse. Unsaved records (`value.id === null`) are tracked as\n * \"pending members\" until they are persisted.\n */\n _hasManyAppend(\n record: Model,\n name: string,\n meta: RelationshipDef,\n value: Model,\n ): void {\n if (value.id === null) {\n this.addPendingMember(record, name, value);\n if (meta.options.inverse) {\n const inverseMeta = this.schema\n .relationshipsDefinitionFor(value.modelName)\n .get(meta.options.inverse);\n if (inverseMeta?.kind === 'belongsTo') {\n runInAction(() => {\n (value as unknown as {\n _setRelationshipRef(n: string, r: RelationshipRef): void;\n })._setRelationshipRef(meta.options.inverse!, {\n data: { type: record.modelName, id: record.id! },\n });\n });\n }\n }\n return;\n }\n const ref = this._getRelationshipRefFor(record, name);\n const currentData = ref?.data && Array.isArray(ref.data) ? ref.data : [];\n const entry = { type: value.modelName, id: value.id };\n if (!currentData.some((reference) => Store.refEquals(reference, entry))) {\n runInAction(() => {\n (record as unknown as {\n _setRelationshipRef(n: string, r: RelationshipRef): void;\n })._setRelationshipRef(name, { data: [...currentData, entry] });\n });\n }\n if (meta.options.inverse) {\n this.addInverse(value.modelName, value.id, meta.options.inverse, record);\n }\n }\n\n /**\n * Removes `value` from the `hasMany` relationship ref on `record` and syncs\n * the inverse. Pending members are removed from the pending set.\n */\n _hasManyRemove(\n record: Model,\n name: string,\n meta: RelationshipDef,\n value: Model,\n ): void {\n if (value.id === null) {\n this.removePendingMember(record, name, value);\n return;\n }\n const ref = this._getRelationshipRefFor(record, name);\n const currentData = ref?.data && Array.isArray(ref.data) ? ref.data : [];\n const filtered = currentData.filter(\n (reference) => !(reference.id === value.id && reference.type === value.modelName),\n );\n runInAction(() => {\n (record as unknown as {\n _setRelationshipRef(n: string, r: RelationshipRef): void;\n })._setRelationshipRef(name, { data: filtered });\n });\n if (meta.options.inverse) {\n this.removeInverse(value.modelName, value.id, meta.options.inverse, record);\n }\n }\n\n // --- coalesceFindRequests ---\n\n private coalescePending: Map<string, Map<string, {\n resolve: (record: Model) => void;\n reject: (error: unknown) => void;\n }[]>> = new Map();\n\n private coalesceScheduled: Set<string> = new Set();\n\n private scheduleCoalescedFind(modelName: string, id: string): Promise<Model> {\n return new Promise((resolve, reject) => {\n let byId = this.coalescePending.get(modelName);\n if (!byId) {\n byId = new Map();\n this.coalescePending.set(modelName, byId);\n }\n let callbacks = byId.get(id);\n if (!callbacks) {\n callbacks = [];\n byId.set(id, callbacks);\n }\n callbacks.push({ resolve, reject });\n\n if (!this.coalesceScheduled.has(modelName)) {\n this.coalesceScheduled.add(modelName);\n queueMicrotask(() => this.flushCoalescedFind(modelName));\n }\n });\n }\n\n private async flushCoalescedFind(modelName: string): Promise<void> {\n this.coalesceScheduled.delete(modelName);\n const byId = this.coalescePending.get(modelName);\n if (!byId || byId.size === 0) {\n return;\n }\n const pendingEntries = new Map(byId);\n byId.clear();\n\n const ids = Array.from(pendingEntries.keys());\n const adapter = this.adapterFor(modelName);\n\n try {\n const snapshots = ids.map((id) => {\n const cached = this.peekRecord(modelName, id);\n return cached\n ? this.createSnapshot(cached)\n : this.createEmptySnapshot(modelName, id);\n });\n const response = await adapter.findMany!(this, modelName, ids, snapshots);\n const doc = this.serializerFor(modelName).normalizeResponse(\n this,\n this.schema.modelFor(modelName),\n response,\n null,\n 'findMany',\n ) as NormalizedDocument;\n this.push(doc);\n\n for (const id of ids) {\n const record = this.peekRecord(modelName, id);\n const callbacks = pendingEntries.get(id);\n if (callbacks) {\n for (const callback of callbacks) {\n if (record) {\n callback.resolve(record);\n } else {\n callback.reject(new Error(`Record not found after findMany: ${modelName}:${id}`));\n }\n }\n }\n }\n } catch (error) {\n for (const callbacks of pendingEntries.values()) {\n for (const callback of callbacks) {\n callback.reject(error);\n }\n }\n }\n }\n\n // --- liveQuery ---\n\n /**\n * Returns a reactive `RecordArray` that auto-updates whenever records matching\n * the predicate are added, removed, or mutated in the identity map.\n *\n * The underlying computed uses `keepAlive: true` so it remains cached even\n * without active MobX observers — useful for long-lived filtered views.\n *\n * @param modelName - The registered model type to query.\n * @param predicate - Filter function applied to each record of `modelName`.\n * @returns A live `RecordArray` containing only records that satisfy `predicate`.\n */\n liveQuery<T extends Model = Model>(\n modelName: string,\n predicate: (record: T) => boolean,\n ): RecordArray<T> {\n return new RecordArray<T>({\n modelName,\n keepAlive: true,\n source: () => {\n const all = this.identityMap.all(modelName) as T[];\n const newRecordsForType = this.newRecords.get(modelName);\n const combined = newRecordsForType && newRecordsForType.size > 0\n ? [...all, ...(newRecordsForType as unknown as Set<T>)]\n : all;\n return combined.filter(predicate);\n },\n });\n }\n\n // --- optimisticUpdate ---\n\n /**\n * Applies attribute changes to a record immediately (optimistically), then\n * executes `persistFn`. If `persistFn` throws, the record is automatically\n * rolled back to its state before the optimistic update.\n *\n * @param record - The record to update optimistically.\n * @param optimisticAttributes - Attributes to apply before persistence.\n * @param persistFn - Async function that persists the change (e.g. `record.save()`).\n * @returns The record on success.\n * @throws Re-throws the error from `persistFn` after rollback.\n */\n async optimisticUpdate<T extends Model>(\n record: T,\n optimisticAttributes: Partial<Record<string, unknown>>,\n persistFn: () => Promise<unknown>,\n ): Promise<T> {\n const internal = record as unknown as {\n _data: Record<string, unknown>;\n _savedData: Record<string, unknown>;\n };\n const backup = { ...internal._data };\n\n runInAction(() => {\n Object.assign(internal._data, optimisticAttributes);\n });\n\n try {\n await persistFn();\n return record;\n } catch (error) {\n runInAction(() => {\n for (const [key, value] of Object.entries(backup)) {\n internal._data[key] = value;\n }\n });\n throw error;\n }\n }\n\n // --- runInTransaction ---\n\n /**\n * Executes multiple store mutations as a single MobX action, guaranteeing\n * that observers (and therefore UI renders) react only once — after all\n * mutations have been applied.\n *\n * @param callback - Synchronous function containing one or more store mutations.\n */\n runInTransaction(callback: () => void): void {\n runInAction(callback);\n }\n\n // --- SSR: serialize / hydrate ---\n\n /**\n * Produces a JSON-serializable snapshot of all records in the identity map.\n * Designed for server-side rendering: serialize on the server, transfer as\n * JSON, then `hydrate()` on the client to restore the full store state\n * without network requests.\n *\n * @param options.exclude - Per-model-type list of attribute keys to omit\n * (e.g. `{ user: ['password', 'token'] }`) to prevent leaking sensitive\n * data in SSR payloads.\n * @returns A snapshot object safe to pass through `JSON.stringify`.\n */\n serialize(options: {\n exclude?: Record<string, string[]>;\n } = {}): { records: Record<string, Array<{ id: string; attributes: Record<string, unknown>; relationships?: Record<string, RelationshipRef> }>> } {\n const records: Record<string, Array<{ id: string; attributes: Record<string, unknown>; relationships?: Record<string, RelationshipRef> }>> = {};\n const allTypes = this.identityMap._buckets;\n for (const [modelName, bucket] of allTypes) {\n const excludeKeys = options.exclude?.[modelName];\n const items: Array<{ id: string; attributes: Record<string, unknown>; relationships?: Record<string, RelationshipRef> }> = [];\n for (const [id, record] of bucket) {\n const internal = record as unknown as {\n _data: Record<string, unknown>;\n _relationships: Map<string, RelationshipRef>;\n };\n let attributes: Record<string, unknown>;\n if (excludeKeys && excludeKeys.length > 0) {\n attributes = {};\n for (const [key, value] of Object.entries(internal._data)) {\n if (!excludeKeys.includes(key)) {\n attributes[key] = value;\n }\n }\n } else {\n attributes = { ...internal._data };\n }\n const entry: { id: string; attributes: Record<string, unknown>; relationships?: Record<string, RelationshipRef> } = {\n id,\n attributes,\n };\n if (internal._relationships && internal._relationships.size > 0) {\n const relationships: Record<string, RelationshipRef> = {};\n for (const [name, ref] of internal._relationships) {\n relationships[name] = ref;\n }\n entry.relationships = relationships;\n }\n items.push(entry);\n }\n if (items.length > 0) {\n records[modelName] = items;\n }\n }\n return { records };\n }\n\n /**\n * Restores records from a snapshot produced by `serialize()` into this store\n * instance. All records are pushed into the identity map in `loaded.saved`\n * state — no network requests are issued.\n *\n * @param snapshot - A snapshot object previously returned by `serialize()`.\n */\n hydrate(snapshot: { records: Record<string, Array<{ id: string; attributes: Record<string, unknown>; relationships?: Record<string, RelationshipRef> }>> }): void {\n runInAction(() => {\n for (const [modelName, items] of Object.entries(snapshot.records)) {\n for (const item of items) {\n this.pushResource({\n type: modelName,\n id: item.id,\n attributes: item.attributes,\n relationships: item.relationships,\n });\n }\n }\n });\n }\n\n /**\n * Factory method that creates a new `Store` and immediately hydrates it from\n * the given snapshot. Convenience for SSR client-side bootstrap.\n *\n * @param schema - SchemaService with all model types registered.\n * @param snapshot - A snapshot object previously returned by `serialize()`.\n * @returns A fully populated `Store` instance ready for use.\n */\n static hydrate(\n schema: SchemaService,\n snapshot: { records: Record<string, Array<{ id: string; attributes: Record<string, unknown>; relationships?: Record<string, RelationshipRef> }>> },\n ): Store {\n const store = new Store(schema);\n store.hydrate(snapshot);\n return store;\n }\n}\n"],"names":["IdentityMap","makeObservable","observable","action","modelName","create","existing","id","record","_a","bucket","total","RecordArray","opts","computed","index","callback","predicate","AdapterPopulatedRecordArray","meta","links","Store","schema","a","b","adapter","serializer","data","Klass","set","key","persisted","newRecordsForType","doc","document","resource","modelNameOrPayload","payload","body","normalized","type","runInAction","Model","name","ref","items","item","targetType","targetId","inverseName","inverseRecord","target","inverseMeta","inverseEntry","currentData","reference","options","cached","snapshot","adapterOptions","response","params","ids","array","r","result","isNew","newId","attributes","relationships","internal","resolvedRecord","value","m","isAsync","hostWithStore","wrapper","AsyncBelongsTo","AsyncHasMany","arr","ManyArray","prev","newRef","byName","_b","entry","filtered","resolve","reject","byId","callbacks","pendingEntries","snapshots","error","all","optimisticAttributes","persistFn","backup","records","allTypes","excludeKeys","store","__decorateClass","singleton","injectable","__decorateParam","SchemaService"],"mappings":"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"}
|