-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
Copy pathindex.ts
303 lines (254 loc) · 10.7 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
import { getOwner } from '@ember/application';
import { assert } from '@ember/debug';
import type Store from '@ember-data/store';
import { recordIdentifierFor } from '@ember-data/store';
import { _deprecatingNormalize } from '@ember-data/store/-private';
import type { ObjectValue } from '@warp-drive/core-types/json/raw';
import { FetchManager, upgradeStore } from './-private';
import type { MinimumAdapterInterface } from './legacy-network-handler/minimum-adapter-interface';
import type {
MinimumSerializerInterface,
SerializerOptions,
} from './legacy-network-handler/minimum-serializer-interface';
export { LegacyNetworkHandler } from './legacy-network-handler/legacy-network-handler';
/**
* @module @ember-data/store
* @class Store
*/
export type LegacyStoreCompat = {
_fetchManager: FetchManager;
adapterFor(this: Store, modelName: string): MinimumAdapterInterface;
adapterFor(this: Store, modelName: string, _allowMissing: true): MinimumAdapterInterface | undefined;
serializerFor<K extends string>(modelName: K, _allowMissing?: boolean): MinimumSerializerInterface | null;
normalize(modelName: string, payload: ObjectValue): ObjectValue;
pushPayload(modelName: string, payload: ObjectValue): void;
serializeRecord(record: unknown, options?: SerializerOptions): unknown;
_adapterCache: Record<string, MinimumAdapterInterface & { store: Store }>;
_serializerCache: Record<string, MinimumSerializerInterface & { store: Store }>;
};
export type CompatStore = Store & LegacyStoreCompat;
/**
Returns an instance of the adapter for a given type. For
example, `adapterFor('person')` will return an instance of
the adapter located at `app/adapters/person.js`
If no `person` adapter is found, this method will look
for an `application` adapter (the default adapter for
your entire application).
@method adapterFor
@public
@param {String} modelName
@return Adapter
*/
export function adapterFor(this: Store, modelName: string): MinimumAdapterInterface;
export function adapterFor(this: Store, modelName: string, _allowMissing: true): MinimumAdapterInterface | undefined;
export function adapterFor(this: Store, modelName: string, _allowMissing?: true): MinimumAdapterInterface | undefined {
assert(
`Attempted to call store.adapterFor(), but the store instance has already been destroyed.`,
!(this.isDestroying || this.isDestroyed)
);
assert(`You need to pass a model name to the store's adapterFor method`, modelName);
assert(
`Passing classes to store.adapterFor has been removed. Please pass a dasherized string instead of ${modelName}`,
typeof modelName === 'string'
);
upgradeStore(this);
this._adapterCache =
this._adapterCache || (Object.create(null) as Record<string, MinimumAdapterInterface & { store: Store }>);
const normalizedModelName = _deprecatingNormalize(modelName);
const { _adapterCache } = this;
let adapter: (MinimumAdapterInterface & { store: Store }) | undefined = _adapterCache[normalizedModelName];
if (adapter) {
return adapter;
}
const owner = getOwner(this)!;
// name specific adapter
adapter = owner.lookup(`adapter:${normalizedModelName}`) as (MinimumAdapterInterface & { store: Store }) | undefined;
if (adapter !== undefined) {
_adapterCache[normalizedModelName] = adapter;
return adapter;
}
// no adapter found for the specific name, fallback and check for application adapter
adapter = _adapterCache.application || owner.lookup('adapter:application');
if (adapter !== undefined) {
_adapterCache[normalizedModelName] = adapter;
_adapterCache.application = adapter;
return adapter;
}
assert(
`No adapter was found for '${modelName}' and no 'application' adapter was found as a fallback.`,
_allowMissing
);
}
/**
Returns an instance of the serializer for a given type. For
example, `serializerFor('person')` will return an instance of
`App.PersonSerializer`.
If no `App.PersonSerializer` is found, this method will look
for an `App.ApplicationSerializer` (the default serializer for
your entire application).
If a serializer cannot be found on the adapter, it will fall back
to an instance of `JSONSerializer`.
@method serializerFor
@public
@param {String} modelName the record to serialize
@return {Serializer}
*/
export function serializerFor(this: Store, modelName: string): MinimumSerializerInterface | null {
assert(
`Attempted to call store.serializerFor(), but the store instance has already been destroyed.`,
!(this.isDestroying || this.isDestroyed)
);
assert(`You need to pass a model name to the store's serializerFor method`, modelName);
assert(
`Passing classes to store.serializerFor has been removed. Please pass a dasherized string instead of ${modelName}`,
typeof modelName === 'string'
);
upgradeStore(this);
this._serializerCache =
this._serializerCache || (Object.create(null) as Record<string, MinimumSerializerInterface & { store: Store }>);
const normalizedModelName = _deprecatingNormalize(modelName);
const { _serializerCache } = this;
let serializer: (MinimumSerializerInterface & { store: Store }) | undefined = _serializerCache[normalizedModelName];
if (serializer) {
return serializer;
}
// by name
const owner = getOwner(this)!;
serializer = owner.lookup(`serializer:${normalizedModelName}`) as
| (MinimumSerializerInterface & { store: Store })
| undefined;
if (serializer !== undefined) {
_serializerCache[normalizedModelName] = serializer;
return serializer;
}
// no serializer found for the specific model, fallback and check for application serializer
serializer = _serializerCache.application || owner.lookup('serializer:application');
if (serializer !== undefined) {
_serializerCache[normalizedModelName] = serializer;
_serializerCache.application = serializer;
return serializer;
}
return null;
}
/**
`normalize` converts a json payload into the normalized form that
[push](../methods/push?anchor=push) expects.
Example
```js
socket.on('message', function(message) {
let modelName = message.model;
let data = message.data;
store.push(store.normalize(modelName, data));
});
```
@method normalize
@public
@param {String} modelName The name of the model type for this payload
@param {Object} payload
@return {Object} The normalized payload
*/
// TODO @runspired @deprecate users should call normalize on the associated serializer directly
export function normalize(this: Store, modelName: string, payload: ObjectValue) {
upgradeStore(this);
assert(
`Attempted to call store.normalize(), but the store instance has already been destroyed.`,
!(this.isDestroying || this.isDestroyed)
);
assert(`You need to pass a model name to the store's normalize method`, modelName);
assert(
`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${typeof modelName}`,
typeof modelName === 'string'
);
const normalizedModelName = _deprecatingNormalize(modelName);
const serializer = this.serializerFor(normalizedModelName);
const schema = this.modelFor(normalizedModelName);
assert(
`You must define a normalize method in your serializer in order to call store.normalize`,
typeof serializer?.normalize === 'function'
);
return serializer.normalize(schema, payload);
}
/**
Push some raw data into the store.
This method can be used both to push in brand new
records, as well as to update existing records. You
can push in more than one type of object at once.
All objects should be in the format expected by the
serializer.
```app/serializers/application.js
import RESTSerializer from '@ember-data/serializer/rest';
export default class ApplicationSerializer extends RESTSerializer;
```
```js
let pushData = {
posts: [
{ id: 1, postTitle: "Great post", commentIds: [2] }
],
comments: [
{ id: 2, commentBody: "Insightful comment" }
]
}
store.pushPayload(pushData);
```
By default, the data will be deserialized using a default
serializer (the application serializer if it exists).
Alternatively, `pushPayload` will accept a model type which
will determine which serializer will process the payload.
```app/serializers/application.js
import RESTSerializer from '@ember-data/serializer/rest';
export default class ApplicationSerializer extends RESTSerializer;
```
```app/serializers/post.js
import JSONSerializer from '@ember-data/serializer/json';
export default JSONSerializer;
```
```js
store.pushPayload(pushData); // Will use the application serializer
store.pushPayload('post', pushData); // Will use the post serializer
```
@method pushPayload
@public
@param {String} modelName Optionally, a model type used to determine which serializer will be used
@param {Object} inputPayload
*/
// TODO @runspired @deprecate pushPayload in favor of looking up the serializer
export function pushPayload(this: Store, modelName: string, inputPayload: ObjectValue): void {
upgradeStore(this);
assert(
`Attempted to call store.pushPayload(), but the store instance has already been destroyed.`,
!(this.isDestroying || this.isDestroyed)
);
const payload: ObjectValue = inputPayload || (modelName as unknown as ObjectValue);
const normalizedModelName = inputPayload ? _deprecatingNormalize(modelName) : 'application';
const serializer = this.serializerFor(normalizedModelName);
assert(
`You cannot use 'store.pushPayload(<type>, <payload>)' unless the serializer for '${normalizedModelName}' defines 'pushPayload'`,
serializer && typeof serializer.pushPayload === 'function'
);
serializer.pushPayload(this, payload);
}
// TODO @runspired @deprecate records should implement their own serialization if desired
export function serializeRecord(this: Store, record: unknown, options?: SerializerOptions): unknown {
upgradeStore(this);
// TODO we used to check if the record was destroyed here
if (!this._fetchManager) {
this._fetchManager = new FetchManager(this);
}
return this._fetchManager.createSnapshot(recordIdentifierFor(record)).serialize(options);
}
export function cleanup(this: Store) {
upgradeStore(this);
// enqueue destruction of any adapters/serializers we have created
for (const adapterName in this._adapterCache) {
const adapter = this._adapterCache[adapterName];
if (typeof adapter.destroy === 'function') {
adapter.destroy();
}
}
for (const serializerName in this._serializerCache) {
const serializer = this._serializerCache[serializerName];
if (typeof serializer.destroy === 'function') {
serializer.destroy();
}
}
}