@ember-data/model 4.10.0 → 4.12.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/addon/{-private/diff-array.ts → -private.js} +41 -13
  2. package/addon/-private.js.map +1 -0
  3. package/addon/has-many-a275fe0d.js +6283 -0
  4. package/addon/has-many-a275fe0d.js.map +1 -0
  5. package/addon/index.js +1 -0
  6. package/addon/index.js.map +1 -0
  7. package/addon-main.js +90 -0
  8. package/package.json +43 -15
  9. package/addon/-private/attr.js +0 -162
  10. package/addon/-private/belongs-to.js +0 -268
  11. package/addon/-private/debug/assert-polymorphic-type.js +0 -71
  12. package/addon/-private/deprecated-promise-proxy.ts +0 -76
  13. package/addon/-private/errors.ts +0 -425
  14. package/addon/-private/has-many.js +0 -280
  15. package/addon/-private/index.ts +0 -14
  16. package/addon/-private/legacy-data-fetch.js +0 -395
  17. package/addon/-private/legacy-data-utils.ts +0 -92
  18. package/addon/-private/legacy-relationships-support.ts +0 -774
  19. package/addon/-private/many-array.ts +0 -401
  20. package/addon/-private/model-for-mixin.ts +0 -38
  21. package/addon/-private/model.js +0 -2516
  22. package/addon/-private/notify-changes.ts +0 -72
  23. package/addon/-private/promise-belongs-to.ts +0 -73
  24. package/addon/-private/promise-many-array.ts +0 -425
  25. package/addon/-private/promise-proxy-base.js +0 -4
  26. package/addon/-private/record-state.ts +0 -468
  27. package/addon/-private/references/belongs-to.ts +0 -624
  28. package/addon/-private/references/has-many.ts +0 -669
  29. package/addon/-private/relationship-meta.ts +0 -98
  30. package/addon/-private/util.ts +0 -31
  31. package/addon/index.ts +0 -39
  32. package/blueprints/model/HELP.md +0 -26
  33. package/blueprints/model/files/__root__/__path__/__name__.js +0 -5
  34. package/blueprints/model/index.js +0 -158
  35. package/blueprints/model/native-files/__root__/__path__/__name__.js +0 -5
  36. package/blueprints/model-test/index.js +0 -33
  37. package/blueprints/model-test/mocha-files/__root__/__path__/__test__.js +0 -18
  38. package/blueprints/model-test/mocha-rfc-232-files/__root__/__path__/__test__.js +0 -15
  39. package/blueprints/model-test/qunit-files/__root__/__path__/__test__.js +0 -14
  40. package/index.js +0 -46
@@ -1,468 +0,0 @@
1
- import { assert } from '@ember/debug';
2
- import { dependentKeyCompat } from '@ember/object/compat';
3
- import { DEBUG } from '@glimmer/env';
4
- import { cached, tracked } from '@glimmer/tracking';
5
-
6
- import type Store from '@ember-data/store';
7
- import { storeFor } from '@ember-data/store';
8
- import { recordIdentifierFor } from '@ember-data/store/-private';
9
- import type { NotificationType } from '@ember-data/store/-private/managers/record-notification-manager';
10
- import type RequestCache from '@ember-data/store/-private/network/request-cache';
11
- import { addToTransaction, subscribe } from '@ember-data/tracking/-private';
12
- import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
13
- import type { RecordData } from '@ember-data/types/q/record-data';
14
-
15
- type Model = InstanceType<typeof import('./model')>;
16
-
17
- const SOURCE_POINTER_REGEXP = /^\/?data\/(attributes|relationships)\/(.*)/;
18
- const SOURCE_POINTER_PRIMARY_REGEXP = /^\/?data/;
19
- const PRIMARY_ATTRIBUTE_KEY = 'base';
20
- function isInvalidError(error) {
21
- return error && error.isAdapterError === true && error.code === 'InvalidError';
22
- }
23
-
24
- /**
25
- * Tag provides a cache for a getter
26
- * that recomputes only when a specific
27
- * tracked property that it manages is dirtied.
28
- *
29
- * This allows us to bust the cache for a value
30
- * that otherwise doesn't access anything tracked
31
- * as well as control the timing of that notification.
32
- *
33
- * @internal
34
- */
35
- class Tag {
36
- declare rev: number;
37
- declare isDirty: boolean;
38
- declare value: any;
39
- declare t: boolean;
40
-
41
- constructor() {
42
- this.rev = 1;
43
- this.isDirty = true;
44
- this.value = undefined;
45
- this.t = false;
46
- }
47
- @tracked ref = null;
48
-
49
- notify() {
50
- this.isDirty = true;
51
- addToTransaction(this);
52
- this.rev++;
53
- }
54
- consume(v) {
55
- this.isDirty = false;
56
- this.value = v; // set cached value
57
- }
58
- }
59
-
60
- const Tags = new WeakMap();
61
- function getTag(record, key) {
62
- let tags = Tags.get(record);
63
- if (!tags) {
64
- tags = Object.create(null);
65
- Tags.set(record, tags);
66
- }
67
- return (tags[key] = tags[key] || new Tag());
68
- }
69
-
70
- export function peekTag(record, key) {
71
- let tags = Tags.get(record);
72
- return tags && tags[key];
73
- }
74
-
75
- /**
76
- * A decorattor that caches a getter while
77
- * providing the ability to bust that cache
78
- * when we so choose in a way that notifies
79
- * glimmer's tracking system.
80
- *
81
- * @internal
82
- */
83
- export function tagged(_target, key, desc) {
84
- const getter = desc.get;
85
- const setter = desc.set;
86
- desc.get = function () {
87
- let tag = getTag(this, key);
88
- subscribe(tag);
89
-
90
- if (tag.isDirty) {
91
- tag.consume(getter.call(this));
92
- }
93
-
94
- return tag.value;
95
- };
96
- desc.set = function (v) {
97
- getTag(this, key); // ensure tag is setup in case we want to use it.
98
- // probably notify here but not yet.
99
- setter.call(this, v);
100
- };
101
- dependentKeyCompat(desc);
102
- return desc;
103
- }
104
-
105
- /**
106
- Historically EmberData managed a state machine
107
- for each record, the localState for which
108
- was reflected onto Model.
109
-
110
- This implements the flags and stateName for backwards compat
111
- with the state tree that used to be possible (listed below).
112
-
113
- stateName and dirtyType are candidates for deprecation.
114
-
115
- root
116
- empty
117
- deleted // hidden from stateName
118
- preloaded // hidden from stateName
119
-
120
- loading
121
- empty // hidden from stateName
122
- preloaded // hidden from stateName
123
-
124
- loaded
125
- saved
126
- updated
127
- uncommitted
128
- invalid
129
- inFlight
130
- created
131
- uncommitted
132
- invalid
133
- inFlight
134
-
135
- deleted
136
- saved
137
- new // hidden from stateName
138
- uncommitted
139
- invalid
140
- inFlight
141
-
142
- @internal
143
- */
144
- export default class RecordState {
145
- declare store: Store;
146
- declare identifier: StableRecordIdentifier;
147
- declare record: Model;
148
- declare rs: RequestCache;
149
-
150
- declare pendingCount: number;
151
- declare fulfilledCount: number;
152
- declare rejectedCount: number;
153
- declare recordData: RecordData;
154
- declare _errorRequests: any[];
155
- declare _lastError: any;
156
- declare handler: object;
157
-
158
- constructor(record: Model) {
159
- const store = storeFor(record)!;
160
- const identity = recordIdentifierFor(record);
161
-
162
- this.identifier = identity;
163
- this.record = record;
164
- this.recordData = store._instanceCache.getRecordData(identity);
165
-
166
- this.pendingCount = 0;
167
- this.fulfilledCount = 0;
168
- this.rejectedCount = 0;
169
- this._errorRequests = [];
170
- this._lastError = null;
171
-
172
- let requests = store.getRequestStateService();
173
- let notifications = store._notificationManager;
174
-
175
- const handleRequest = (req) => {
176
- if (req.type === 'mutation') {
177
- switch (req.state) {
178
- case 'pending':
179
- this.isSaving = true;
180
- break;
181
- case 'rejected':
182
- this.isSaving = false;
183
- this._lastError = req;
184
- if (!(req.response && isInvalidError(req.response.data))) {
185
- this._errorRequests.push(req);
186
- }
187
-
188
- notifyErrorsStateChanged(this);
189
- break;
190
- case 'fulfilled':
191
- this._errorRequests = [];
192
- this._lastError = null;
193
- this.isSaving = false;
194
- notifyErrorsStateChanged(this);
195
- break;
196
- }
197
- } else {
198
- switch (req.state) {
199
- case 'pending':
200
- this.pendingCount++;
201
- this.notify('isLoading');
202
- break;
203
- case 'rejected':
204
- this.pendingCount--;
205
- this._lastError = req;
206
- if (!(req.response && isInvalidError(req.response.data))) {
207
- this._errorRequests.push(req);
208
- }
209
- this.notify('isLoading');
210
- notifyErrorsStateChanged(this);
211
- break;
212
- case 'fulfilled':
213
- this.pendingCount--;
214
- this.fulfilledCount++;
215
- this.notify('isLoading');
216
- this.notify('isDirty');
217
- notifyErrorsStateChanged(this);
218
- this._errorRequests = [];
219
- this._lastError = null;
220
- break;
221
- }
222
- }
223
- };
224
-
225
- requests.subscribeForRecord(identity, handleRequest);
226
-
227
- // we instantiate lazily
228
- // so we grab anything we don't have yet
229
- if (!DEBUG) {
230
- const lastRequest = requests.getLastRequestForRecord(identity);
231
- if (lastRequest) {
232
- handleRequest(lastRequest);
233
- }
234
- }
235
-
236
- this.handler = notifications.subscribe(
237
- identity,
238
- (identifier: StableRecordIdentifier, type: NotificationType, key?: string) => {
239
- switch (type) {
240
- case 'state':
241
- this.notify('isNew');
242
- this.notify('isDeleted');
243
- this.notify('isDirty');
244
- break;
245
- case 'attributes':
246
- this.notify('isEmpty');
247
- this.notify('isDirty');
248
- break;
249
- case 'errors':
250
- this.updateInvalidErrors(this.record.errors);
251
- this.notify('isValid');
252
- break;
253
- }
254
- }
255
- );
256
- }
257
-
258
- destroy() {
259
- storeFor(this.record)!._notificationManager.unsubscribe(this.handler);
260
- }
261
-
262
- notify(key) {
263
- getTag(this, key).notify();
264
- }
265
-
266
- updateInvalidErrors(errors) {
267
- assert(
268
- `Expected the RecordData instance for ${this.identifier} to implement getErrors(identifier)`,
269
- typeof this.recordData.getErrors === 'function'
270
- );
271
- let jsonApiErrors = this.recordData.getErrors(this.identifier);
272
-
273
- errors.clear();
274
-
275
- for (let i = 0; i < jsonApiErrors.length; i++) {
276
- let error = jsonApiErrors[i];
277
-
278
- if (error.source && error.source.pointer) {
279
- let keyMatch = error.source.pointer.match(SOURCE_POINTER_REGEXP);
280
- let key: string | undefined;
281
-
282
- if (keyMatch) {
283
- key = keyMatch[2];
284
- } else if (error.source.pointer.search(SOURCE_POINTER_PRIMARY_REGEXP) !== -1) {
285
- key = PRIMARY_ATTRIBUTE_KEY;
286
- }
287
-
288
- if (key) {
289
- let errMsg = error.detail || error.title;
290
- errors.add(key, errMsg);
291
- }
292
- }
293
- }
294
- }
295
-
296
- cleanErrorRequests() {
297
- this.notify('isValid');
298
- this.notify('isError');
299
- this.notify('adapterError');
300
- this._errorRequests = [];
301
- this._lastError = null;
302
- }
303
-
304
- @tracked isSaving = false;
305
-
306
- @tagged
307
- get isLoading() {
308
- return !this.isLoaded && this.pendingCount > 0 && this.fulfilledCount === 0;
309
- }
310
-
311
- @tagged
312
- get isLoaded() {
313
- if (this.isNew) {
314
- return true;
315
- }
316
- return this.fulfilledCount > 0 || !this.isEmpty;
317
- }
318
-
319
- @tagged
320
- get isSaved() {
321
- let rd = this.recordData;
322
- if (this.isDeleted) {
323
- assert(`Expected RecordData to implement isDeletionCommitted()`, rd.isDeletionCommitted);
324
- return rd.isDeletionCommitted(this.identifier);
325
- }
326
- if (this.isNew || this.isEmpty || !this.isValid || this.isDirty || this.isLoading) {
327
- return false;
328
- }
329
- return true;
330
- }
331
-
332
- @tagged
333
- get isEmpty() {
334
- let rd = this.recordData;
335
- // TODO this is not actually an RFC'd concept. Determine the
336
- // correct heuristic to replace this with.
337
- assert(`Expected RecordData to implement isEmpty()`, rd.isEmpty);
338
- return !this.isNew && rd.isEmpty(this.identifier);
339
- }
340
-
341
- @tagged
342
- get isNew() {
343
- let rd = this.recordData;
344
- assert(`Expected RecordData to implement isNew()`, rd.isNew);
345
- return rd.isNew(this.identifier);
346
- }
347
-
348
- @tagged
349
- get isDeleted() {
350
- let rd = this.recordData;
351
- assert(`Expected RecordData to implement isDeleted()`, rd.isDeleted);
352
- return rd.isDeleted(this.identifier);
353
- }
354
-
355
- @tagged
356
- get isValid() {
357
- return this.record.errors.length === 0;
358
- }
359
-
360
- @tagged
361
- get isDirty() {
362
- let rd = this.recordData;
363
- if (rd.isDeletionCommitted(this.identifier) || (this.isDeleted && this.isNew)) {
364
- return false;
365
- }
366
- return this.isNew || rd.hasChangedAttrs(this.identifier);
367
- }
368
-
369
- @tagged
370
- get isError() {
371
- let errorReq = this._errorRequests[this._errorRequests.length - 1];
372
- if (!errorReq) {
373
- return false;
374
- } else {
375
- return true;
376
- }
377
- }
378
-
379
- @tagged
380
- get adapterError() {
381
- let request = this._lastError;
382
- if (!request) {
383
- return null;
384
- }
385
- return request.state === 'rejected' && request.response.data;
386
- }
387
-
388
- @cached
389
- get isPreloaded() {
390
- return !this.isEmpty && this.isLoading;
391
- }
392
-
393
- @cached
394
- get stateName() {
395
- // we might be empty while loading so check this first
396
- if (this.isLoading) {
397
- return 'root.loading';
398
-
399
- // got nothing yet or were unloaded
400
- } else if (this.isEmpty) {
401
- return 'root.empty';
402
-
403
- // deleted substates
404
- } else if (this.isDeleted) {
405
- if (this.isSaving) {
406
- return 'root.deleted.inFlight';
407
- } else if (this.isSaved) {
408
- // TODO ensure isSaved isn't true from previous requests
409
- return 'root.deleted.saved';
410
- } else if (!this.isValid) {
411
- return 'root.deleted.invalid';
412
- } else {
413
- return 'root.deleted.uncommitted';
414
- }
415
-
416
- // loaded.created substates
417
- } else if (this.isNew) {
418
- if (this.isSaving) {
419
- return 'root.loaded.created.inFlight';
420
- } else if (!this.isValid) {
421
- return 'root.loaded.created.invalid';
422
- }
423
- return 'root.loaded.created.uncommitted';
424
-
425
- // loaded.updated substates
426
- } else if (this.isSaving) {
427
- return 'root.loaded.updated.inFlight';
428
- } else if (!this.isValid) {
429
- return 'root.loaded.updated.invalid';
430
- } else if (this.isDirty) {
431
- return 'root.loaded.updated.uncommitted';
432
-
433
- // if nothing remains, we are loaded saved!
434
- } else {
435
- return 'root.loaded.saved';
436
- }
437
- }
438
-
439
- @cached
440
- get dirtyType() {
441
- // we might be empty while loading so check this first
442
- if (this.isLoading || this.isEmpty) {
443
- return '';
444
-
445
- // deleted substates
446
- } else if (this.isDeleted) {
447
- return 'deleted';
448
-
449
- // loaded.created substates
450
- } else if (this.isNew) {
451
- return 'created';
452
-
453
- // loaded.updated substates
454
- } else if (this.isSaving || !this.isValid || this.isDirty) {
455
- return 'updated';
456
-
457
- // if nothing remains, we are loaded saved!
458
- } else {
459
- return '';
460
- }
461
- }
462
- }
463
-
464
- function notifyErrorsStateChanged(state: RecordState) {
465
- state.notify('isValid');
466
- state.notify('isError');
467
- state.notify('adapterError');
468
- }