diff --git a/.eslintrc.js b/.eslintrc.js index 7b88e098c51..94842dece0e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -215,7 +215,6 @@ module.exports = { 'ember-data-types/q/promise-proxies.ts', 'ember-data-types/q/minimum-serializer-interface.ts', 'ember-data-types/q/minimum-adapter-interface.ts', - 'ember-data-types/q/identifier.ts', 'ember-data-types/q/fetch-manager.ts', 'ember-data-types/q/ember-data-json-api.ts', '@types/@ember/polyfills/index.d.ts', diff --git a/ember-data-types/q/identifier.ts b/ember-data-types/q/identifier.ts index 49b35376685..f4625651c55 100644 --- a/ember-data-types/q/identifier.ts +++ b/ember-data-types/q/identifier.ts @@ -2,7 +2,7 @@ @module @ember-data/store */ -import { ImmutableRequestInfo } from '@ember-data/request/-private/types'; +import { ImmutableRequestInfo } from '@ember-data/request'; import { CACHE_OWNER, DEBUG_CLIENT_ORIGINATED, diff --git a/ember-data-types/q/record-data-schemas.ts b/ember-data-types/q/record-data-schemas.ts index 6ce8768d270..98df5e52b85 100644 --- a/ember-data-types/q/record-data-schemas.ts +++ b/ember-data-types/q/record-data-schemas.ts @@ -5,11 +5,6 @@ export interface RelationshipSchema { kind: 'belongsTo' | 'hasMany'; type: string; // related type - /** - * @internal - * @deprecated - */ - key: string; // TODO @runspired remove our uses // TODO @runspired should RFC be updated to make this optional? // TODO @runspired sohuld RFC be update to enforce async and inverse are set? else internals need to know // that meta came from @ember-data/model vs not from @ember-data/model as defaults should switch. @@ -29,18 +24,14 @@ export interface RelationshipSchema { export type RelationshipsSchema = Record; export interface AttributeSchema { - /** - * @internal - */ name: string; - - kind?: 'attribute'; + kind: 'attribute'; // TODO @runspired update RFC to make options optional options?: { [key: string]: unknown; }; - type?: string; // TODO @runspired update RFC to make type optional + type: string | null; } export type AttributesSchema = Record; diff --git a/packages/graph/src/-private/-edge-definition.ts b/packages/graph/src/-private/-edge-definition.ts index a88959d39a4..efe00dbf6c5 100644 --- a/packages/graph/src/-private/-edge-definition.ts +++ b/packages/graph/src/-private/-edge-definition.ts @@ -474,7 +474,7 @@ export function upgradeDefinition( // TODO we want to assert this but this breaks all of our shoddily written tests /* if (DEBUG) { - let inverseDoubleCheck = inverseMeta.type.inverseFor(inverseRelationshipName, store); + let inverseDoubleCheck = inverseFor(inverseRelationshipName, store); assert(`The ${inverseBaseModelName}:${inverseRelationshipName} relationship declares 'inverse: null', but it was resolved as the inverse for ${baseModelName}:${relationshipName}.`, inverseDoubleCheck); } diff --git a/packages/json-api/src/-private/cache.ts b/packages/json-api/src/-private/cache.ts index 03bfe816a5d..54adbb2b3c8 100644 --- a/packages/json-api/src/-private/cache.ts +++ b/packages/json-api/src/-private/cache.ts @@ -13,6 +13,7 @@ import type { ImplicitEdge } from '@ember-data/graph/-private/edges/implicit'; import type { ResourceEdge } from '@ember-data/graph/-private/edges/resource'; import type { Graph, GraphEdge } from '@ember-data/graph/-private/graph'; import type { StructuredDataDocument, StructuredDocument, StructuredErrorDocument } from '@ember-data/request'; +import Store from '@ember-data/store'; import { StoreRequestInfo } from '@ember-data/store/-private/cache-handler'; import type { IdentifierCache } from '@ember-data/store/-private/caches/identifier-cache'; import type { ResourceBlob } from '@ember-data/types/cache/aliases'; @@ -426,6 +427,22 @@ export default class JSONAPICache implements Cache { } }); } + + // @ts-expect-error we reach into private api here + const store: Store = this.__storeWrapper._store; + const attrs = this.__storeWrapper.getSchemaDefinitionService().attributesDefinitionFor(identifier); + Object.keys(attrs).forEach((key) => { + if (key in attributes && attributes[key] !== undefined) { + return; + } + const attr = attrs[key]!; + const defaultValue = getDefaultValue(attr, identifier, store); + + if (defaultValue !== undefined) { + attributes[key] = defaultValue; + } + }); + return { type, id, @@ -689,6 +706,7 @@ export default class JSONAPICache implements Cache { switch (kind) { case 'attribute': this.setAttr(identifier, name, propertyValue); + createOptions[name] = propertyValue; break; case 'belongsTo': this.mutate({ @@ -1026,7 +1044,9 @@ export default class JSONAPICache implements Cache { return cached.remoteAttrs[attr]; } else { const attrSchema = this.__storeWrapper.getSchemaDefinitionService().attributesDefinitionFor(identifier)[attr]; - return getDefaultValue(attrSchema?.options); + + // @ts-expect-error we reach into private API here + return getDefaultValue(attrSchema, identifier, this.__storeWrapper._store); } } @@ -1380,22 +1400,46 @@ function getRemoteState(rel) { return rel.remoteState; } -function getDefaultValue(options: { defaultValue?: unknown } | undefined) { - if (!options) { +function getDefaultValue( + schema: AttributeSchema | undefined, + identifier: StableRecordIdentifier, + store: Store +): unknown { + const options = schema?.options; + + if (!schema || (!options && !schema.type)) { return; } - if (typeof options.defaultValue === 'function') { + + // legacy support for defaultValues that are functions + if (typeof options?.defaultValue === 'function') { // If anyone opens an issue for args not working right, we'll restore + deprecate it via a Proxy // that lazily instantiates the record. We don't want to provide any args here // because in a non @ember-data/model world they don't make sense. return options.defaultValue(); - } else { + // legacy support for defaultValues that are primitives + } else if (options && 'defaultValue' in options) { let defaultValue = options.defaultValue; assert( `Non primitive defaultValues are not supported because they are shared between all instances. If you would like to use a complex object as a default value please provide a function that returns the complex object.`, typeof defaultValue !== 'object' || defaultValue === null ); return defaultValue; + + // new style transforms + } else if (schema.type) { + const transform = ( + store.schema as unknown as { + transforms?: Map< + string, + { defaultValue(options: Record | null, identifier: StableRecordIdentifier): unknown } + >; + } + ).transforms?.get(schema.type); + + if (transform?.defaultValue) { + return transform.defaultValue(options || null, identifier); + } } } diff --git a/packages/legacy-compat/src/legacy-network-handler/legacy-data-fetch.js b/packages/legacy-compat/src/legacy-network-handler/legacy-data-fetch.js index 94b61e413e4..67eadce1070 100644 --- a/packages/legacy-compat/src/legacy-network-handler/legacy-data-fetch.js +++ b/packages/legacy-compat/src/legacy-network-handler/legacy-data-fetch.js @@ -16,7 +16,7 @@ export function _findHasMany(adapter, store, identifier, link, relationship, opt promise = promise.then( (adapterPayload) => { assert( - `You made a 'findHasMany' request for a ${identifier.type}'s '${relationship.key}' relationship, using link '${link}' , but the adapter's response did not have any data`, + `You made a 'findHasMany' request for a ${identifier.type}'s '${relationship.name}' relationship, using link '${link}' , but the adapter's response did not have any data`, payloadIsNotBlank(adapterPayload) ); const modelClass = store.modelFor(relationship.type); @@ -115,7 +115,7 @@ function syncRelationshipDataFromLink(store, payload, parentIdentifier, relation id: parentIdentifier.id, type: parentIdentifier.type, relationships: { - [relationship.key]: relatedDataHash, + [relationship.name]: relatedDataHash, }, }; diff --git a/packages/legacy-compat/src/legacy-network-handler/legacy-network-handler.ts b/packages/legacy-compat/src/legacy-network-handler/legacy-network-handler.ts index 6f5ce631894..06b4438b84a 100644 --- a/packages/legacy-compat/src/legacy-network-handler/legacy-network-handler.ts +++ b/packages/legacy-compat/src/legacy-network-handler/legacy-network-handler.ts @@ -151,12 +151,12 @@ function findHasMany(context: StoreRequestContext): Promise { } // identifiers case - - const fetches = new Array>(identifiers!.length); + assert(`Expected an array of identifiers to fetch`, Array.isArray(identifiers)); + const fetches = new Array>(identifiers.length); const manager = store._fetchManager; - for (let i = 0; i < identifiers!.length; i++) { - let identifier = identifiers![i]; + for (let i = 0; i < identifiers.length; i++) { + let identifier = identifiers[i]; // TODO we probably can be lenient here and return from cache for the isNew case assertIdentifierHasId(identifier); fetches[i] = options.reload @@ -183,10 +183,10 @@ function saveRecord(context: StoreRequestContext): Promise { try { let payloadCopy: unknown = payload ? JSON.parse(JSON.stringify(payload)) : payload; // eslint-disable-next-line no-console - console.log(`EmberData | Payload - ${operation!}`, payloadCopy); + console.log(`EmberData | Payload - ${operation}`, payloadCopy); } catch (e) { // eslint-disable-next-line no-console - console.log(`EmberData | Payload - ${operation!}`, payload); + console.log(`EmberData | Payload - ${operation}`, payload); } } let result: SingleResourceDataDocument; diff --git a/packages/model/src/-private/attr.js b/packages/model/src/-private/attr.js index 151dc9d396a..d9b8a40af38 100644 --- a/packages/model/src/-private/attr.js +++ b/packages/model/src/-private/attr.js @@ -111,8 +111,10 @@ function attr(type, options) { let meta = { type: type, + kind: 'attribute', isAttribute: true, options: options, + key: null, }; return computed({ diff --git a/packages/model/src/-private/belongs-to.js b/packages/model/src/-private/belongs-to.js index d4f4545570a..91d9f251640 100644 --- a/packages/model/src/-private/belongs-to.js +++ b/packages/model/src/-private/belongs-to.js @@ -118,7 +118,7 @@ function belongsTo(modelName, options) { isRelationship: true, options: opts, kind: 'belongsTo', - name: 'Belongs To', + name: '', key: null, }; diff --git a/packages/model/src/-private/has-many.js b/packages/model/src/-private/has-many.js index 32e199c14ae..c5dc9565cb4 100644 --- a/packages/model/src/-private/has-many.js +++ b/packages/model/src/-private/has-many.js @@ -191,7 +191,7 @@ function hasMany(type, options) { options, isRelationship: true, kind: 'hasMany', - name: 'Has Many', + name: '', key: null, }; diff --git a/packages/model/src/-private/model.d.ts b/packages/model/src/-private/model.d.ts index 3263a5acff1..2bbf47f8add 100644 --- a/packages/model/src/-private/model.d.ts +++ b/packages/model/src/-private/model.d.ts @@ -51,7 +51,7 @@ class Model extends EmberObject { static relationshipsByName: Map; static eachAttribute(callback: (this: T, key: string, attribute: AttributeSchema) => void, binding?: T): void; static eachRelationship(callback: (this: T, key: string, relationship: RelationshipSchema) => void, binding?: T): void; - static eachTransformedAttribute(callback: (this: T, key: string, type?: string) => void, binding?: T): void; + static eachTransformedAttribute(callback: (this: T, key: string, type: string | null) => void, binding?: T): void; static toString(): string; static isModel: true; diff --git a/packages/model/src/-private/model.js b/packages/model/src/-private/model.js index faba35091f7..2d389cb92b5 100644 --- a/packages/model/src/-private/model.js +++ b/packages/model/src/-private/model.js @@ -75,7 +75,7 @@ function findPossibleInverses(type, inverseType, name, relationshipsSoFar) { * an expensive getter on first-access and thereafter * never recompute it. */ -function computeOnce(target, key, desc) { +function computeOnce(target, propertyName, desc) { const cache = new WeakMap(); let getter = desc.get; desc.get = function () { @@ -138,8 +138,8 @@ class Model extends EmberObject { this.setProperties(createProps); let notifications = store.notifications; - this.___private_notifications = notifications.subscribe(identity, (identifier, type, key) => { - notifyChanges(identifier, type, key, this, store); + this.___private_notifications = notifications.subscribe(identity, (identifier, type, field) => { + notifyChanges(identifier, type, field, this, store); }); } @@ -155,9 +155,9 @@ class Model extends EmberObject { // notify individual changes once the delete has been signaled, // this decision is left to model instances. - this.eachRelationship((key, meta) => { + this.eachRelationship((name, meta) => { if (meta.kind === 'belongsTo') { - this.notifyPropertyChange(key); + this.notifyPropertyChange(name); } }); LEGACY_SUPPORT.get(this)?.destroy(); @@ -633,12 +633,12 @@ class Model extends EmberObject { to trigger their flush. We wouldn't need to super in 4.0+ where sync observers are removed. */ - notifyPropertyChange(key) { - let tag = peekTag(this, key); + notifyPropertyChange(prop) { + let tag = peekTag(this, prop); if (tag) { tag.notify(); } - super.notifyPropertyChange(key); + super.notifyPropertyChange(prop); } /** @@ -761,10 +761,10 @@ class Model extends EmberObject { // and protects with a try...finally block // previously used begin...endPropertyChanges but this is private API changeProperties(() => { - let key; + let prop; for (let i = 0, length = keys.length; i < length; i++) { - key = keys[i]; - this.notifyPropertyChange(key); + prop = keys[i]; + this.notifyPropertyChange(prop); } }); } @@ -1131,7 +1131,7 @@ class Model extends EmberObject { The relationship descriptor argument is an object with the following properties. - - **key** String the name of this relationship on the Model + - **name** String the name of this relationship on the Model - **kind** String "hasMany" or "belongsTo" - **options** Object the original options hash passed when the relationship was declared - **parentType** Model the type of the Model that owns this relationship @@ -1174,8 +1174,8 @@ class Model extends EmberObject { return this.constructor.relationshipsByName.get(name); } - inverseFor(key) { - return this.constructor.inverseFor(key, storeFor(this)); + inverseFor(name) { + return this.constructor.inverseFor(name, storeFor(this)); } eachAttribute(callback, binding) { @@ -1588,7 +1588,7 @@ class Model extends EmberObject { }; this.eachComputedProperty((name, meta) => { - if (meta.isRelationship) { + if (meta.kind === 'hasMany' || meta.kind === 'belongsTo') { names[meta.kind].push(name); } }); @@ -1683,9 +1683,9 @@ class Model extends EmberObject { let relationshipsByName = Blog.relationshipsByName; relationshipsByName.users; - //=> { key: 'users', kind: 'hasMany', type: 'user', options: Object, isRelationship: true } + //=> { name: 'users', kind: 'hasMany', type: 'user', options: Object } relationshipsByName.owner; - //=> { key: 'owner', kind: 'belongsTo', type: 'user', options: Object, isRelationship: true } + //=> { name: 'owner', kind: 'belongsTo', type: 'user', options: Object } ``` @property relationshipsByName @@ -1705,10 +1705,10 @@ class Model extends EmberObject { let relationships = Object.keys(rels); for (let i = 0; i < relationships.length; i++) { - let key = relationships[i]; - let value = rels[key]; + let name = relationships[i]; + let value = rels[name]; - map.set(value.name || value.key, value); + map.set(value.name, value); } return map; @@ -1724,7 +1724,8 @@ class Model extends EmberObject { let relationships = Object.create(null); let modelName = this.modelName; this.eachComputedProperty((name, meta) => { - if (meta.isRelationship) { + if (meta.kind === 'hasMany' || meta.kind === 'belongsTo') { + // TODO deprecate key being here meta.key = name; meta.name = name; meta.parentModelName = modelName; @@ -1790,10 +1791,9 @@ class Model extends EmberObject { let map = new Map(); this.eachComputedProperty((name, meta) => { - // TODO end reliance on these booleans and stop leaking them in the spec - if (meta.isRelationship) { + if (meta.kind === 'hasMany' || meta.kind === 'belongsTo') { map.set(name, meta.kind); - } else if (meta.isAttribute) { + } else if (meta.kind === 'attribute') { map.set(name, 'attribute'); } }); @@ -1855,7 +1855,7 @@ class Model extends EmberObject { this.modelName ); - let knownKey = knownSide.key; + let knownKey = knownSide.name; let knownKind = knownSide.kind; let inverse = this.inverseFor(knownKey, store); // let key; @@ -1903,9 +1903,9 @@ class Model extends EmberObject { }); // prints: - // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"} - // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"} - // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"} + // firstName {type: "string", kind: 'attribute', options: Object, parentType: function, name: "firstName"} + // lastName {type: "string", kind: 'attribute', options: Object, parentType: function, name: "lastName"} + // birthday {type: "date", kind: 'attribute', options: Object, parentType: function, name: "birthday"} ``` @property attributes @@ -1924,13 +1924,15 @@ class Model extends EmberObject { let map = new Map(); this.eachComputedProperty((name, meta) => { - if (meta.isAttribute) { + if (meta.kind === 'attribute') { assert( "You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: attr('')` from " + this.toString(), name !== 'id' ); + // TODO deprecate key being here + meta.key = name; meta.name = name; map.set(name, meta); } @@ -1987,9 +1989,9 @@ class Model extends EmberObject { let map = new Map(); - this.eachAttribute((key, meta) => { + this.eachAttribute((name, meta) => { if (meta.type) { - map.set(key, meta.type); + map.set(name, meta.type); } }); @@ -2029,9 +2031,9 @@ class Model extends EmberObject { }); // prints: - // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"} - // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"} - // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"} + // firstName {type: "string", kind: 'attribute', options: Object, parentType: function, name: "firstName"} + // lastName {type: "string", kind: 'attribute', options: Object, parentType: function, name: "lastName"} + // birthday {type: "date", kind: 'attribute', options: Object, parentType: function, name: "birthday"} ``` @method eachAttribute diff --git a/packages/request/src/index.ts b/packages/request/src/index.ts index c6c3189e976..71efe9e9751 100644 --- a/packages/request/src/index.ts +++ b/packages/request/src/index.ts @@ -1,3 +1,3 @@ export { RequestManager as default } from './-private/manager'; export { createDeferred } from './-private/future'; -export type { Future, Handler, NextFn, RequestContext, RequestInfo, ResponseInfo, StructuredDocument, StructuredErrorDocument, StructuredDataDocument } from './-private/types'; +export type { Future, Handler, NextFn, RequestContext, ImmutableRequestInfo, RequestInfo, ResponseInfo, StructuredDocument, StructuredErrorDocument, StructuredDataDocument } from './-private/types'; diff --git a/packages/schema-record/package.json b/packages/schema-record/package.json index 194a5b1e27f..35ece72fc1f 100644 --- a/packages/schema-record/package.json +++ b/packages/schema-record/package.json @@ -14,9 +14,8 @@ "license": "MIT", "author": "", "scripts": { - "build:types": "tsc --build --force", "build:client": "rollup --config && babel ./addon --out-dir addon --plugins=../private-build-infra/src/transforms/babel-plugin-transform-ext.js", - "build": "pnpm build:client && pnpm build:types", + "build": "pnpm build:client", "start": "rollup --config --watch", "prepack": "pnpm build", "prepare": "pnpm build" diff --git a/packages/schema-record/src/index.ts b/packages/schema-record/src/index.ts index fc319b5baed..409a7a28d4c 100644 --- a/packages/schema-record/src/index.ts +++ b/packages/schema-record/src/index.ts @@ -1,6 +1,195 @@ -type Store = { peekRecord(identifier: StableRecordIdentifier): SchemaModel | unknown | null }; -type StableRecordIdentifier = { lid: string }; +import type Store from '@ember-data/store'; +import type { StableRecordIdentifier } from "@ember-data/types/q/identifier"; +import type { AttributeSchema, RelationshipSchema } from '@ember-data/types/q/record-data-schemas'; -export default class SchemaModel { - constructor(store: Store, identifier: StableRecordIdentifier) {} +export const Destroy = Symbol('Destroy'); +export const RecordStore = Symbol('Store'); +export const Identifier = Symbol('Identifier'); +export const Editable = Symbol('Editable'); + +export interface FieldSchema { + type: string | null; + name: string; + kind: 'attribute' | 'resource' | 'collection' | 'derived' | 'object' | 'array'; + options?: Record; +} + +type FieldSpec = { + // legacy support + attributes: Record; + relationships: Record; + // new support + fields: Map; +} + +export type Transform = { + serialize(value: PT, options: Record | null, record: SchemaRecord): T; + hydrate(value: T, options: Record | null, record: SchemaRecord): PT; + defaultValue?(options: Record | null, identifier: StableRecordIdentifier): T; +}; + +export class SchemaService { + declare schemas: Map; + declare transforms: Map; + + constructor() { + this.schemas = new Map(); + this.transforms = new Map(); + } + + registerTransform(type: string, transform: Transform): void { + this.transforms.set(type, transform); + } + + defineSchema(name: string, fields: FieldSchema[]): void { + const fieldSpec: FieldSpec = { + attributes: {}, + relationships: {}, + fields: new Map(), + }; + + fields.forEach((field) => { + fieldSpec.fields.set(field.name, field); + + if (field.kind === 'attribute') { + fieldSpec.attributes[field.name] = field as AttributeSchema; + } else if (field.kind === 'resource' || field.kind === 'collection') { + const relSchema = Object.assign({}, field, { + kind: field.kind === 'resource' ? 'belongsTo' : 'hasMany', + }) as unknown as RelationshipSchema; + fieldSpec.relationships[field.name] = relSchema; + } else { + throw new Error(`Unknown field kind ${field.kind}`); + } + }); + + this.schemas.set(name, fieldSpec); + } + + fields({ type }: { type: string }): FieldSpec['fields'] { + const schema = this.schemas.get(type); + + if (!schema) { + throw new Error(`No schema defined for ${type}`); + } + + return schema.fields; + } + + attributesDefinitionFor({ type }: { type: string }): FieldSpec['attributes'] { + const schema = this.schemas.get(type); + + if (!schema) { + throw new Error(`No schema defined for ${type}`); + } + + return schema.attributes; + } + + relationshipsDefinitionFor({ type }: { type: string }): FieldSpec['relationships'] { + const schema = this.schemas.get(type); + + if (!schema) { + throw new Error(`No schema defined for ${type}`); + } + + return schema.relationships; + } + + doesTypeExist(type: string): boolean { + return this.schemas.has(type); + } +} + +export class SchemaRecord { + declare [RecordStore]: Store; + declare [Identifier]: StableRecordIdentifier; + declare [Editable]: boolean; + + constructor(store: Store, identifier: StableRecordIdentifier, editable: boolean) { + this[RecordStore] = store; + this[Identifier] = identifier; + this[Editable] = editable; + + const schema = store.schema as unknown as SchemaService; + const cache = store.cache; + const fields = schema.fields(identifier); + + return new Proxy(this, { + get(target, prop) { + if (prop === Destroy) { + return target[Destroy]; + } + + if (prop === 'id') { + return identifier.id; + } + if (prop === '$type') { + return identifier.type; + } + const field = fields.get(prop as string); + if (!field) { + throw new Error(`No field named ${String(prop)} on ${identifier.type}`); + } + + if (field.kind === 'attribute') { + const rawValue = cache.getAttr(identifier, prop as string); + if (field.type === null) { + return rawValue; + } + const transform = schema.transforms.get(field.type); + if (!transform) { + throw new Error(`No '${field.type}' transform defined for use by ${identifier.type}.${String(prop)}`); + } + return transform.hydrate(rawValue, field.options ?? null, target); + } + + throw new Error(`Unknown field kind ${field.kind}`); + }, + set(target, prop, value) { + if (!target[Editable]) { + throw new Error(`Cannot set ${String(prop)} on ${identifier.type} because the record is not editable`); + } + + const field = fields.get(prop as string); + if (!field) { + throw new Error(`There is no field named ${String(prop)} on ${identifier.type}`); + } + + if (field.kind === 'attribute') { + if (field.type === null) { + cache.setAttr(identifier, prop as string, value); + return true; + } + const transform = schema.transforms.get(field.type); + + if (!transform) { + throw new Error(`No '${field.type}' transform defined for use by ${identifier.type}.${String(prop)}`); + } + + const rawValue = transform.serialize(value, field.options ?? null, target); + cache.setAttr(identifier, prop as string, rawValue); + return true; + } + + throw new Error(`Unknown field kind ${field.kind}`); + }, + }); + } + + [Destroy](): void {} +} + +export function instantiateRecord(store: Store, identifier: StableRecordIdentifier, createArgs?: Record): SchemaRecord { + if (createArgs) { + const editable = new SchemaRecord(store, identifier, true); + Object.assign(editable, createArgs); + return editable; + } + + return new SchemaRecord(store, identifier, false); +} + +export function teardownRecord(record: SchemaRecord): void { + record[Destroy](); } diff --git a/packages/schema-record/tsconfig.json b/packages/schema-record/tsconfig.json deleted file mode 100644 index 2ae51769cd7..00000000000 --- a/packages/schema-record/tsconfig.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "include": ["src/**/*"], - "compilerOptions": { - "target": "ES2022", - "module": "ES2020", - "moduleResolution": "node", - "skipLibCheck": true, - "declarationDir": "addon", - "emitDeclarationOnly": true, - "noEmit": false, - - // Enable faster builds - // but causes us to not rebuild properly - "incremental": false, - - "allowJs": false, - "checkJs": false, - - "alwaysStrict": true, - "strict": true, - "allowSyntheticDefaultImports": true, - - "noImplicitAny": true, - "noImplicitThis": true, - "strictBindCallApply": true, - "strictFunctionTypes": true, - "strictPropertyInitialization": true, - "allowUnreachableCode": false, - "allowUnusedLabels": false, - "noEmitOnError": false, - "strictNullChecks": true, - "noErrorTruncation": true, - "preserveConstEnums": false, - "experimentalDecorators": true, - "pretty": true, - - // Support generation of source maps. Note: you must *also* enable source - // maps in your `ember-cli-babel` config and/or `babel.config.js`. - "declaration": true, - "declarationMap": true, - "inlineSourceMap": true, - "inlineSources": true, - - "baseUrl": "src", - - "paths": { - "@ember-data/env": ["../../private-build-infra/virtual-packages/env.d.ts"], - } - } -} \ No newline at end of file diff --git a/packages/serializer/src/-private/embedded-records-mixin.js b/packages/serializer/src/-private/embedded-records-mixin.js index ee44876387d..d16a36de861 100644 --- a/packages/serializer/src/-private/embedded-records-mixin.js +++ b/packages/serializer/src/-private/embedded-records-mixin.js @@ -201,7 +201,7 @@ export default Mixin.create({ @param {Object} relationship */ serializeBelongsTo(snapshot, json, relationship) { - let attr = relationship.key; + let attr = relationship.name; if (this.noSerializeOptionSpecified(attr)) { this._super(snapshot, json, relationship); return; @@ -211,9 +211,9 @@ export default Mixin.create({ let embeddedSnapshot = snapshot.belongsTo(attr); if (includeIds) { let schema = this.store.modelFor(snapshot.modelName); - let serializedKey = this._getMappedKey(relationship.key, schema); - if (serializedKey === relationship.key && this.keyForRelationship) { - serializedKey = this.keyForRelationship(relationship.key, relationship.kind, 'serialize'); + let serializedKey = this._getMappedKey(relationship.name, schema); + if (serializedKey === relationship.name && this.keyForRelationship) { + serializedKey = this.keyForRelationship(relationship.name, relationship.kind, 'serialize'); } if (!embeddedSnapshot) { @@ -231,11 +231,11 @@ export default Mixin.create({ }, _serializeEmbeddedBelongsTo(snapshot, json, relationship) { - let embeddedSnapshot = snapshot.belongsTo(relationship.key); + let embeddedSnapshot = snapshot.belongsTo(relationship.name); let schema = this.store.modelFor(snapshot.modelName); - let serializedKey = this._getMappedKey(relationship.key, schema); - if (serializedKey === relationship.key && this.keyForRelationship) { - serializedKey = this.keyForRelationship(relationship.key, relationship.kind, 'serialize'); + let serializedKey = this._getMappedKey(relationship.name, schema); + if (serializedKey === relationship.name && this.keyForRelationship) { + serializedKey = this.keyForRelationship(relationship.name, relationship.kind, 'serialize'); } if (!embeddedSnapshot) { @@ -389,7 +389,7 @@ export default Mixin.create({ @param {Object} relationship */ serializeHasMany(snapshot, json, relationship) { - let attr = relationship.key; + let attr = relationship.name; if (this.noSerializeOptionSpecified(attr)) { this._super(snapshot, json, relationship); return; @@ -397,9 +397,9 @@ export default Mixin.create({ if (this.hasSerializeIdsOption(attr)) { let schema = this.store.modelFor(snapshot.modelName); - let serializedKey = this._getMappedKey(relationship.key, schema); - if (serializedKey === relationship.key && this.keyForRelationship) { - serializedKey = this.keyForRelationship(relationship.key, relationship.kind, 'serialize'); + let serializedKey = this._getMappedKey(relationship.name, schema); + if (serializedKey === relationship.name && this.keyForRelationship) { + serializedKey = this.keyForRelationship(relationship.name, relationship.kind, 'serialize'); } json[serializedKey] = snapshot.hasMany(attr, { ids: true }); @@ -421,8 +421,8 @@ export default Mixin.create({ TODO: Make the default in Ember-data 3.0?? */ _serializeHasManyAsIdsAndTypes(snapshot, json, relationship) { - let serializedKey = this.keyForAttribute(relationship.key, 'serialize'); - let hasMany = snapshot.hasMany(relationship.key); + let serializedKey = this.keyForAttribute(relationship.name, 'serialize'); + let hasMany = snapshot.hasMany(relationship.name); json[serializedKey] = A(hasMany).map(function (recordSnapshot) { // @@ -435,14 +435,14 @@ export default Mixin.create({ _serializeEmbeddedHasMany(snapshot, json, relationship) { let schema = this.store.modelFor(snapshot.modelName); - let serializedKey = this._getMappedKey(relationship.key, schema); - if (serializedKey === relationship.key && this.keyForRelationship) { - serializedKey = this.keyForRelationship(relationship.key, relationship.kind, 'serialize'); + let serializedKey = this._getMappedKey(relationship.name, schema); + if (serializedKey === relationship.name && this.keyForRelationship) { + serializedKey = this.keyForRelationship(relationship.name, relationship.kind, 'serialize'); } warn( `The embedded relationship '${serializedKey}' is undefined for '${snapshot.modelName}' with id '${snapshot.id}'. Please include it in your original payload.`, - typeof snapshot.hasMany(relationship.key) !== 'undefined', + typeof snapshot.hasMany(relationship.name) !== 'undefined', { id: 'ds.serializer.embedded-relationship-undefined' } ); @@ -453,7 +453,7 @@ export default Mixin.create({ Returns an array of embedded records serialized to JSON */ _generateSerializedHasMany(snapshot, relationship) { - let hasMany = snapshot.hasMany(relationship.key); + let hasMany = snapshot.hasMany(relationship.name); let manyArray = A(hasMany); let ret = new Array(manyArray.length); @@ -487,7 +487,7 @@ export default Mixin.create({ removeEmbeddedForeignKey(snapshot, embeddedSnapshot, relationship, json) { if (relationship.kind === 'belongsTo') { let schema = this.store.modelFor(snapshot.modelName); - let parentRecord = schema.inverseFor(relationship.key, this.store); + let parentRecord = schema.inverseFor(relationship.name, this.store); if (parentRecord) { let name = parentRecord.name; let embeddedSerializer = this.store.serializerFor(embeddedSnapshot.modelName); diff --git a/packages/serializer/src/json-api.js b/packages/serializer/src/json-api.js index dcb3effd86c..a935beccd1a 100644 --- a/packages/serializer/src/json-api.js +++ b/packages/serializer/src/json-api.js @@ -667,19 +667,19 @@ const JSONAPISerializer = JSONSerializer.extend({ }, serializeBelongsTo(snapshot, json, relationship) { - let key = relationship.key; + let name = relationship.name; - if (this._canSerialize(key)) { - let belongsTo = snapshot.belongsTo(key); + if (this._canSerialize(name)) { + let belongsTo = snapshot.belongsTo(name); let belongsToIsNotNew = belongsTo && !belongsTo.isNew; if (belongsTo === null || belongsToIsNotNew) { json.relationships = json.relationships || {}; let schema = this.store.modelFor(snapshot.modelName); - let payloadKey = this._getMappedKey(key, schema); - if (payloadKey === key) { - payloadKey = this.keyForRelationship(key, 'belongsTo', 'serialize'); + let payloadKey = this._getMappedKey(name, schema); + if (payloadKey === name) { + payloadKey = this.keyForRelationship(name, 'belongsTo', 'serialize'); } let data = null; @@ -698,17 +698,17 @@ const JSONAPISerializer = JSONSerializer.extend({ }, serializeHasMany(snapshot, json, relationship) { - let key = relationship.key; + let name = relationship.name; - if (this.shouldSerializeHasMany(snapshot, key, relationship)) { - let hasMany = snapshot.hasMany(key); + if (this.shouldSerializeHasMany(snapshot, name, relationship)) { + let hasMany = snapshot.hasMany(name); if (hasMany !== undefined) { json.relationships = json.relationships || {}; let schema = this.store.modelFor(snapshot.modelName); - let payloadKey = this._getMappedKey(key, schema); - if (payloadKey === key && this.keyForRelationship) { - payloadKey = this.keyForRelationship(key, 'hasMany', 'serialize'); + let payloadKey = this._getMappedKey(name, schema); + if (payloadKey === name && this.keyForRelationship) { + payloadKey = this.keyForRelationship(name, 'hasMany', 'serialize'); } // only serialize has many relationships that are not new diff --git a/packages/serializer/src/json.js b/packages/serializer/src/json.js index c65140f1d1e..c1a271fac66 100644 --- a/packages/serializer/src/json.js +++ b/packages/serializer/src/json.js @@ -1225,7 +1225,7 @@ const JSONSerializer = Serializer.extend({ export default class PostSerializer extends JSONSerializer { serializeBelongsTo(snapshot, json, relationship) { - let key = relationship.key; + let key = relationship.name; let belongsTo = snapshot.belongsTo(key); key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo", "serialize") : key; @@ -1242,17 +1242,17 @@ const JSONSerializer = Serializer.extend({ @param {Object} relationship */ serializeBelongsTo(snapshot, json, relationship) { - let key = relationship.key; + let name = relationship.name; - if (this._canSerialize(key)) { - let belongsToId = snapshot.belongsTo(key, { id: true }); + if (this._canSerialize(name)) { + let belongsToId = snapshot.belongsTo(name, { id: true }); // if provided, use the mapping provided by `attrs` in // the serializer let schema = this.store.modelFor(snapshot.modelName); - let payloadKey = this._getMappedKey(key, schema); - if (payloadKey === key && this.keyForRelationship) { - payloadKey = this.keyForRelationship(key, 'belongsTo', 'serialize'); + let payloadKey = this._getMappedKey(name, schema); + if (payloadKey === name && this.keyForRelationship) { + payloadKey = this.keyForRelationship(name, 'belongsTo', 'serialize'); } //Need to check whether the id is there for new&async records @@ -1279,7 +1279,7 @@ const JSONSerializer = Serializer.extend({ export default class PostSerializer extends JSONSerializer { serializeHasMany(snapshot, json, relationship) { - let key = relationship.key; + let key = relationship.name; if (key === 'comments') { return; } else { @@ -1296,17 +1296,17 @@ const JSONSerializer = Serializer.extend({ @param {Object} relationship */ serializeHasMany(snapshot, json, relationship) { - let key = relationship.key; + let name = relationship.name; - if (this.shouldSerializeHasMany(snapshot, key, relationship)) { - let hasMany = snapshot.hasMany(key, { ids: true }); + if (this.shouldSerializeHasMany(snapshot, name, relationship)) { + let hasMany = snapshot.hasMany(name, { ids: true }); if (hasMany !== undefined) { // if provided, use the mapping provided by `attrs` in // the serializer let schema = this.store.modelFor(snapshot.modelName); - let payloadKey = this._getMappedKey(key, schema); - if (payloadKey === key && this.keyForRelationship) { - payloadKey = this.keyForRelationship(key, 'hasMany', 'serialize'); + let payloadKey = this._getMappedKey(name, schema); + if (payloadKey === name && this.keyForRelationship) { + payloadKey = this.keyForRelationship(name, 'hasMany', 'serialize'); } json[payloadKey] = hasMany; @@ -1328,7 +1328,7 @@ const JSONSerializer = Serializer.extend({ export default class CommentSerializer extends JSONSerializer { serializePolymorphicType(snapshot, json, relationship) { - let key = relationship.key; + let key = relationship.name; let belongsTo = snapshot.belongsTo(key); key = this.keyForAttribute ? this.keyForAttribute(key, 'serialize') : key; diff --git a/packages/serializer/src/rest.js b/packages/serializer/src/rest.js index 264787097a3..4fd1d23b5d8 100644 --- a/packages/serializer/src/rest.js +++ b/packages/serializer/src/rest.js @@ -738,9 +738,9 @@ const RESTSerializer = JSONSerializer.extend({ @param {Object} relationship */ serializePolymorphicType(snapshot, json, relationship) { - let key = relationship.key; - let typeKey = this.keyForPolymorphicType(key, relationship.type, 'serialize'); - let belongsTo = snapshot.belongsTo(key); + let name = relationship.name; + let typeKey = this.keyForPolymorphicType(name, relationship.type, 'serialize'); + let belongsTo = snapshot.belongsTo(name); if (!belongsTo) { json[typeKey] = null; diff --git a/packages/store/src/-private/legacy-model-support/shim-model-class.ts b/packages/store/src/-private/legacy-model-support/shim-model-class.ts index 85dd3b29b31..c0a9fdf03a4 100644 --- a/packages/store/src/-private/legacy-model-support/shim-model-class.ts +++ b/packages/store/src/-private/legacy-model-support/shim-model-class.ts @@ -80,11 +80,11 @@ export default class ShimModelClass implements ModelSchema { }); } - eachTransformedAttribute(callback: (this: T | undefined, key: string, type?: string) => void, binding?: T) { + eachTransformedAttribute(callback: (this: T | undefined, key: string, type: string | null) => void, binding?: T) { const attrDefs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({ type: this.modelName }); Object.keys(attrDefs).forEach((key) => { if (attrDefs[key]!.type) { - callback.call(binding, key, attrDefs[key]!.type); + callback.call(binding, key, attrDefs[key].type); } }); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fc92fa05acd..d9a4505d9b1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3424,9 +3424,27 @@ importers: '@babel/runtime': specifier: ^7.23.1 version: 7.23.1 + '@ember-data/graph': + specifier: workspace:5.5.0-alpha.9 + version: file:packages/graph(@babel/core@7.23.0)(@ember-data/store@5.5.0-alpha.9) + '@ember-data/json-api': + specifier: workspace:5.5.0-alpha.9 + version: file:packages/json-api(@babel/core@7.23.0)(@ember-data/graph@5.5.0-alpha.9)(@ember-data/request-utils@packages+request-utils)(@ember-data/store@5.5.0-alpha.9)(ember-inflector@4.0.2) '@ember-data/private-build-infra': specifier: workspace:5.5.0-alpha.9 version: link:../../packages/private-build-infra + '@ember-data/request': + specifier: workspace:5.5.0-alpha.9 + version: file:packages/request(@babel/core@7.23.0) + '@ember-data/request-utils': + specifier: workspace:5.5.0-alpha.9 + version: link:../../packages/request-utils + '@ember-data/store': + specifier: workspace:5.5.0-alpha.9 + version: file:packages/store(@babel/core@7.23.0)(@ember-data/tracking@5.5.0-alpha.9)(@ember/string@3.1.1)(@glimmer/tracking@1.1.2)(ember-source@5.3.0) + '@ember-data/tracking': + specifier: workspace:5.5.0-alpha.9 + version: file:packages/tracking(@babel/core@7.23.0) '@ember-data/unpublished-test-infra': specifier: workspace:5.5.0-alpha.9 version: link:../../packages/unpublished-test-infra @@ -3518,6 +3536,16 @@ importers: specifier: ^5.88.2 version: 5.88.2 dependenciesMeta: + '@ember-data/graph': + injected: true + '@ember-data/json-api': + injected: true + '@ember-data/request': + injected: true + '@ember-data/store': + injected: true + '@ember-data/tracking': + injected: true '@warp-drive/schema-record': injected: true @@ -3731,6 +3759,15 @@ packages: semver: 6.3.1 dev: true + /@babel/generator@7.22.15: + resolution: {integrity: sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.22.19 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.19 + jsesc: 2.5.2 + /@babel/generator@7.23.0: resolution: {integrity: sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==} engines: {node: '>=6.9.0'} @@ -3744,13 +3781,13 @@ packages: resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.22.17 /@babel/helper-builder-binary-assignment-operator-visitor@7.22.15: resolution: {integrity: sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.22.19 /@babel/helper-compilation-targets@7.22.15: resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==} @@ -3770,11 +3807,11 @@ packages: dependencies: '@babel/core': 7.23.0(supports-color@8.1.1) '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.23.0 + '@babel/helper-environment-visitor': 7.22.5 + '@babel/helper-function-name': 7.22.5 '@babel/helper-member-expression-to-functions': 7.22.15 '@babel/helper-optimise-call-expression': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.0) + '@babel/helper-replace-supers': 7.22.9(@babel/core@7.23.0) '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 semver: 6.3.1 @@ -3808,6 +3845,17 @@ packages: resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} engines: {node: '>=6.9.0'} + /@babel/helper-environment-visitor@7.22.5: + resolution: {integrity: sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==} + engines: {node: '>=6.9.0'} + + /@babel/helper-function-name@7.22.5: + resolution: {integrity: sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/types': 7.22.19 + /@babel/helper-function-name@7.23.0: resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} engines: {node: '>=6.9.0'} @@ -3819,19 +3867,45 @@ packages: resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.22.19 /@babel/helper-member-expression-to-functions@7.22.15: resolution: {integrity: sha512-qLNsZbgrNh0fDQBCPocSL8guki1hcPvltGDv/NxvUoABwFq7GkKSu1nRXeJkVZc+wJvne2E0RKQz+2SQrz6eAA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.22.19 /@babel/helper-module-imports@7.22.15: resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.22.19 + + /@babel/helper-module-transforms@7.22.17(@babel/core@7.23.0): + resolution: {integrity: sha512-XouDDhQESrLHTpnBtCKExJdyY4gJCdrvH2Pyv8r8kovX2U8G0dRUOT45T9XlbLtuu9CLXP15eusnkprhoPV5iQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.0(supports-color@8.1.1) + '@babel/helper-environment-visitor': 7.22.5 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-validator-identifier': 7.22.15 + + /@babel/helper-module-transforms@7.22.20(@babel/core@7.23.0): + resolution: {integrity: sha512-dLT7JVWIUUxKOs1UnJUBR3S70YK+pKX6AbJgB2vMIvEkZkrfJDbYDJesnPshtKV4LhDOR3Oc5YULeDizRek+5A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.0(supports-color@8.1.1) + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-validator-identifier': 7.22.20 /@babel/helper-module-transforms@7.23.0(@babel/core@7.23.0): resolution: {integrity: sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==} @@ -3850,7 +3924,7 @@ packages: resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.22.19 /@babel/helper-plugin-utils@7.22.5: resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} @@ -3878,23 +3952,34 @@ packages: '@babel/helper-member-expression-to-functions': 7.22.15 '@babel/helper-optimise-call-expression': 7.22.5 + /@babel/helper-replace-supers@7.22.9(@babel/core@7.23.0): + resolution: {integrity: sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.0(supports-color@8.1.1) + '@babel/helper-environment-visitor': 7.22.5 + '@babel/helper-member-expression-to-functions': 7.22.15 + '@babel/helper-optimise-call-expression': 7.22.5 + /@babel/helper-simple-access@7.22.5: resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.22.19 /@babel/helper-skip-transparent-expression-wrappers@7.22.5: resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.22.19 /@babel/helper-split-export-declaration@7.22.6: resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.22.17 /@babel/helper-string-parser@7.22.5: resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} @@ -3916,9 +4001,9 @@ packages: resolution: {integrity: sha512-OnMhjWjuGYtdoO3FmsEFWvBStBAe2QOgwOLsLNDjN+aaiMD8InJk1/O3HSD8lkqTjCgg5YI34Tz15KNNA3p+nQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-function-name': 7.23.0 + '@babel/helper-function-name': 7.22.5 '@babel/template': 7.22.15 - '@babel/types': 7.23.0 + '@babel/types': 7.22.19 /@babel/helpers@7.23.1(supports-color@8.1.1): resolution: {integrity: sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==} @@ -3943,9 +4028,16 @@ packages: engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.22.19 dev: true + /@babel/parser@7.22.16: + resolution: {integrity: sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.22.19 + /@babel/parser@7.23.0: resolution: {integrity: sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==} engines: {node: '>=6.0.0'} @@ -3984,19 +4076,6 @@ packages: '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-proposal-decorators@7.22.15(@babel/core@7.23.0): - resolution: {integrity: sha512-kc0VvbbUyKelvzcKOSyQUSVVXS5pT3UhRB0e3c9An86MvLqs+gx0dN4asllrDluqSa3m9YyooXKGOFVomnyFkg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.0(supports-color@8.1.1) - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.0) - '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.0) - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/plugin-syntax-decorators': 7.22.10(@babel/core@7.23.0) - /@babel/plugin-proposal-decorators@7.23.0(@babel/core@7.23.0): resolution: {integrity: sha512-kYsT+f5ARWF6AdFmqoEEp+hpqxEB8vGmRWfw2aj78M2vTwS2uHW91EF58iFm1Z9U8Y/RrLu2XKJn46P9ca1b0w==} engines: {node: '>=6.9.0'} @@ -4009,7 +4088,6 @@ packages: '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.0) '@babel/helper-split-export-declaration': 7.22.6 '@babel/plugin-syntax-decorators': 7.22.10(@babel/core@7.23.0) - dev: true /@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.23.0): resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} @@ -4312,10 +4390,10 @@ packages: '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.23.0 + '@babel/helper-function-name': 7.22.5 '@babel/helper-optimise-call-expression': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.0) + '@babel/helper-replace-supers': 7.22.9(@babel/core@7.23.0) '@babel/helper-split-export-declaration': 7.22.6 globals: 11.12.0 @@ -4404,7 +4482,7 @@ packages: dependencies: '@babel/core': 7.23.0(supports-color@8.1.1) '@babel/helper-compilation-targets': 7.22.15 - '@babel/helper-function-name': 7.23.0 + '@babel/helper-function-name': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 /@babel/plugin-transform-json-strings@7.22.11(@babel/core@7.23.0): @@ -4452,7 +4530,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.23.0(supports-color@8.1.1) - '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.0) + '@babel/helper-module-transforms': 7.22.20(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 /@babel/plugin-transform-modules-commonjs@7.22.15(@babel/core@7.23.0): @@ -4462,7 +4540,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.23.0(supports-color@8.1.1) - '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.0) + '@babel/helper-module-transforms': 7.22.17(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-simple-access': 7.22.5 @@ -4476,6 +4554,7 @@ packages: '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-simple-access': 7.22.5 + dev: true /@babel/plugin-transform-modules-systemjs@7.22.11(@babel/core@7.23.0): resolution: {integrity: sha512-rIqHmHoMEOhI3VkVf5jQ15l539KrwhzqcBO6wdCNWPWc/JWt9ILNYNUssbRpeq0qWns8svuw8LnMNCvWBIJ8wA==} @@ -4485,7 +4564,7 @@ packages: dependencies: '@babel/core': 7.23.0(supports-color@8.1.1) '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.0) + '@babel/helper-module-transforms': 7.22.20(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-validator-identifier': 7.22.20 @@ -4496,7 +4575,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.23.0(supports-color@8.1.1) - '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.0) + '@babel/helper-module-transforms': 7.22.20(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.23.0): @@ -4559,7 +4638,7 @@ packages: dependencies: '@babel/core': 7.23.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.0) + '@babel/helper-replace-supers': 7.22.9(@babel/core@7.23.0) /@babel/plugin-transform-optional-catch-binding@7.22.11(@babel/core@7.23.0): resolution: {integrity: sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==} @@ -4808,7 +4887,7 @@ packages: '@babel/plugin-transform-logical-assignment-operators': 7.22.11(@babel/core@7.23.0) '@babel/plugin-transform-member-expression-literals': 7.22.5(@babel/core@7.23.0) '@babel/plugin-transform-modules-amd': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-modules-commonjs': 7.23.0(@babel/core@7.23.0) + '@babel/plugin-transform-modules-commonjs': 7.22.15(@babel/core@7.23.0) '@babel/plugin-transform-modules-systemjs': 7.22.11(@babel/core@7.23.0) '@babel/plugin-transform-modules-umd': 7.22.5(@babel/core@7.23.0) '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.23.0) @@ -4835,7 +4914,7 @@ packages: '@babel/plugin-transform-unicode-regex': 7.22.5(@babel/core@7.23.0) '@babel/plugin-transform-unicode-sets-regex': 7.22.5(@babel/core@7.23.0) '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.23.0) - '@babel/types': 7.23.0 + '@babel/types': 7.22.19 babel-plugin-polyfill-corejs2: 0.4.5(@babel/core@7.23.0) babel-plugin-polyfill-corejs3: 0.8.3(@babel/core@7.23.0) babel-plugin-polyfill-regenerator: 0.5.2(@babel/core@7.23.0) @@ -4941,7 +5020,7 @@ packages: dependencies: '@babel/core': 7.23.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.22.5 - '@babel/types': 7.23.0 + '@babel/types': 7.22.19 esutils: 2.0.3 /@babel/preset-typescript@7.23.0(@babel/core@7.23.0): @@ -4977,27 +5056,44 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.22.13 - '@babel/parser': 7.23.0 - '@babel/types': 7.23.0 + '@babel/parser': 7.22.16 + '@babel/types': 7.22.19 /@babel/traverse@7.22.15: resolution: {integrity: sha512-DdHPwvJY0sEeN4xJU5uRLmZjgMMDIvMPniLuYzUVXj/GGzysPl0/fwt44JBkyUIzGJPV8QgHMcQdQ34XFuKTYQ==} engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.22.13 - '@babel/generator': 7.23.0 + '@babel/generator': 7.22.15 '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.23.0 + '@babel/helper-function-name': 7.22.5 '@babel/helper-hoist-variables': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.23.0 - '@babel/types': 7.23.0 + '@babel/parser': 7.22.16 + '@babel/types': 7.22.19 debug: 4.3.4(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color dev: true + /@babel/traverse@7.22.20: + resolution: {integrity: sha512-eU260mPZbU7mZ0N+X10pxXhQFMGTeLb9eFS0mxehS8HZp9o1uSnFeWQuG1UPrlxgA7QoUzFhOnilHDp0AXCyHw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.22.13 + '@babel/generator': 7.22.15 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.22.5 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/parser': 7.22.16 + '@babel/types': 7.22.19 + debug: 4.3.4(supports-color@8.1.1) + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + /@babel/traverse@7.23.0(supports-color@8.1.1): resolution: {integrity: sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==} engines: {node: '>=6.9.0'} @@ -5023,6 +5119,14 @@ packages: '@babel/helper-validator-identifier': 7.22.15 to-fast-properties: 2.0.0 + /@babel/types@7.22.17: + resolution: {integrity: sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.22.5 + '@babel/helper-validator-identifier': 7.22.15 + to-fast-properties: 2.0.0 + /@babel/types@7.22.19: resolution: {integrity: sha512-P7LAw/LbojPzkgp5oznjE6tQEIWbp4PkkfrZDINTro9zgBRtI324/EYsiSI7lhPbpIQ+DCeR2NNmMWANGGfZsg==} engines: {node: '>=6.9.0'} @@ -9671,7 +9775,7 @@ packages: dependencies: '@babel/core': 7.23.0(supports-color@8.1.1) '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.23.0) - '@babel/plugin-proposal-decorators': 7.22.15(@babel/core@7.23.0) + '@babel/plugin-proposal-decorators': 7.23.0(@babel/core@7.23.0) '@babel/preset-env': 7.22.15(@babel/core@7.23.0) '@embroider/macros': 1.13.1(@babel/core@7.23.0) '@embroider/shared-internals': 2.4.0(supports-color@8.1.1) @@ -9748,7 +9852,7 @@ packages: '@babel/core': 7.23.0(supports-color@8.1.1) '@babel/helper-compilation-targets': 7.22.15 '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.23.0) - '@babel/plugin-proposal-decorators': 7.22.15(@babel/core@7.23.0) + '@babel/plugin-proposal-decorators': 7.23.0(@babel/core@7.23.0) '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.23.0) '@babel/plugin-proposal-private-property-in-object': 7.21.11(@babel/core@7.23.0) '@babel/plugin-transform-class-static-block': 7.22.11(@babel/core@7.23.0) @@ -10306,8 +10410,8 @@ packages: resolution: {integrity: sha512-89oVHVJwmLDvGvAUWgS87KpBoRhy3aZ6U0Ql6HOmU4TrPkyaa8pM0W81wj9cIwjYprcQtN9EwzZMHnq46+oUyw==} engines: {node: 8.* || 10.* || >= 12} dependencies: - '@babel/parser': 7.23.0 - '@babel/traverse': 7.23.0(supports-color@8.1.1) + '@babel/parser': 7.22.16 + '@babel/traverse': 7.22.20 recast: 0.18.10 transitivePeerDependencies: - supports-color @@ -17496,6 +17600,31 @@ packages: - '@glint/template' - supports-color + file:packages/json-api(@babel/core@7.23.0)(@ember-data/graph@5.5.0-alpha.9)(@ember-data/request-utils@packages+request-utils)(@ember-data/store@5.5.0-alpha.9)(ember-inflector@4.0.2): + resolution: {directory: packages/json-api, type: directory} + id: file:packages/json-api + name: '@ember-data/json-api' + engines: {node: '>= 18.*'} + peerDependencies: + '@ember-data/graph': workspace:5.5.0-alpha.9 + '@ember-data/request-utils': workspace:5.5.0-alpha.9 + '@ember-data/store': workspace:5.5.0-alpha.9 + ember-inflector: ^4.0.2 + dependencies: + '@ember-data/graph': file:packages/graph(@babel/core@7.23.0)(@ember-data/store@5.5.0-alpha.9) + '@ember-data/private-build-infra': file:packages/private-build-infra + '@ember-data/request-utils': link:packages/request-utils + '@ember-data/store': file:packages/store(@babel/core@7.23.0)(@ember-data/tracking@5.5.0-alpha.9)(@ember/string@3.1.1)(@glimmer/tracking@1.1.2)(ember-source@5.3.0) + '@ember/edition-utils': 1.2.0 + '@embroider/macros': 1.13.1(@babel/core@7.23.0) + ember-cli-babel: 8.1.0(@babel/core@7.23.0) + ember-inflector: 4.0.2(@babel/core@7.23.0) + transitivePeerDependencies: + - '@babel/core' + - '@glint/template' + - supports-color + dev: true + file:packages/json-api(@babel/core@7.23.0)(@ember-data/graph@packages+graph)(@ember-data/request-utils@5.5.0-alpha.9)(@ember-data/store@5.5.0-alpha.9)(ember-inflector@4.0.2): resolution: {directory: packages/json-api, type: directory} id: file:packages/json-api diff --git a/tests/json-api/tests/integration/cache/collection-data-documents-test.ts b/tests/json-api/tests/integration/cache/collection-data-documents-test.ts index 36c1fa8a6f9..0909cbbb501 100644 --- a/tests/json-api/tests/integration/cache/collection-data-documents-test.ts +++ b/tests/json-api/tests/integration/cache/collection-data-documents-test.ts @@ -215,13 +215,12 @@ module('Integration | @ember-data/json-api Cache.put()', new TestSchema<'user'>({ user: { attributes: { - name: { kind: 'attribute', name: 'name' }, + name: { kind: 'attribute', name: 'name', type: null }, }, relationships: { bestFriend: { kind: 'belongsTo', type: 'user', - key: 'bestFriend', name: 'bestFriend', options: { async: false, @@ -231,7 +230,6 @@ module('Integration | @ember-data/json-api Cache.put()', worstEnemy: { kind: 'belongsTo', type: 'user', - key: 'worstEnemy', name: 'worstEnemy', options: { async: false, @@ -241,7 +239,6 @@ module('Integration | @ember-data/json-api Cache.put()', friends: { kind: 'hasMany', type: 'user', - key: 'friends', name: 'friends', options: { async: false, diff --git a/tests/json-api/tests/integration/cache/resource-data-documents-test.ts b/tests/json-api/tests/integration/cache/resource-data-documents-test.ts index 985af2d41e7..e9ed6ecbd09 100644 --- a/tests/json-api/tests/integration/cache/resource-data-documents-test.ts +++ b/tests/json-api/tests/integration/cache/resource-data-documents-test.ts @@ -309,13 +309,12 @@ module('Integration | @ember-data/json-api Cache.put()', f new TestSchema<'user'>({ user: { attributes: { - name: { kind: 'attribute', name: 'name' }, + name: { kind: 'attribute', name: 'name', type: null }, }, relationships: { bestFriend: { kind: 'belongsTo', type: 'user', - key: 'bestFriend', name: 'bestFriend', options: { async: false, @@ -325,7 +324,6 @@ module('Integration | @ember-data/json-api Cache.put()', f worstEnemy: { kind: 'belongsTo', type: 'user', - key: 'worstEnemy', name: 'worstEnemy', options: { async: false, @@ -335,7 +333,6 @@ module('Integration | @ember-data/json-api Cache.put()', f friends: { kind: 'hasMany', type: 'user', - key: 'friends', name: 'friends', options: { async: false, diff --git a/tests/json-api/tests/integration/serialize-test.ts b/tests/json-api/tests/integration/serialize-test.ts index 7b6c2ffee28..9d5b5eebc81 100644 --- a/tests/json-api/tests/integration/serialize-test.ts +++ b/tests/json-api/tests/integration/serialize-test.ts @@ -74,14 +74,13 @@ module('Integration | @ember-data/json-api/request', function (hooks) { new TestSchema<'user'>({ user: { attributes: { - firstName: { kind: 'attribute', name: 'firstName' }, - lastName: { kind: 'attribute', name: 'lastName' }, + firstName: { kind: 'attribute', name: 'firstName', type: null }, + lastName: { kind: 'attribute', name: 'lastName', type: null }, }, relationships: { bestFriend: { kind: 'belongsTo', type: 'user', - key: 'bestFriend', name: 'bestFriend', options: { async: false, @@ -91,7 +90,6 @@ module('Integration | @ember-data/json-api/request', function (hooks) { worstEnemy: { kind: 'belongsTo', type: 'user', - key: 'worstEnemy', name: 'worstEnemy', options: { async: false, @@ -101,7 +99,6 @@ module('Integration | @ember-data/json-api/request', function (hooks) { friends: { kind: 'hasMany', type: 'user', - key: 'friends', name: 'friends', options: { async: false, diff --git a/tests/main/tests/integration/record-data/store-wrapper-test.ts b/tests/main/tests/integration/record-data/store-wrapper-test.ts index d3ff6b6a53e..74423008733 100644 --- a/tests/main/tests/integration/record-data/store-wrapper-test.ts +++ b/tests/main/tests/integration/record-data/store-wrapper-test.ts @@ -151,11 +151,13 @@ module('integration/store-wrapper - RecordData StoreWrapper tests', function (ho const store = owner.lookup('service:store') as unknown as Store; store.cache; - let houseAttrs = { + const houseAttrs = { name: { type: 'string', isAttribute: true, + kind: 'attribute' as const, options: {}, + key: 'name', name: 'name', }, }; @@ -166,11 +168,13 @@ module('integration/store-wrapper - RecordData StoreWrapper tests', function (ho 'can lookup attribute definitions for self' ); - let carAttrs = { + const carAttrs = { make: { type: 'string', isAttribute: true, + kind: 'attribute' as const, options: {}, + key: 'make', name: 'make', }, }; diff --git a/tests/main/tests/integration/serializers/json-serializer-test.js b/tests/main/tests/integration/serializers/json-serializer-test.js index 8fcfca270f8..1d759469d33 100644 --- a/tests/main/tests/integration/serializers/json-serializer-test.js +++ b/tests/main/tests/integration/serializers/json-serializer-test.js @@ -177,7 +177,7 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { let comment = store.createRecord('comment', { body: 'Omakase is delicious', post: post }); let json = {}; - serializer.serializeBelongsTo(comment._createSnapshot(), json, { key: 'post', options: {} }); + serializer.serializeBelongsTo(comment._createSnapshot(), json, comment.constructor.relationshipsByName.get('post')); assert.deepEqual(json, { post: '1' }); }); @@ -200,7 +200,7 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { let comment = store.createRecord('comment', { body: 'Omakase is delicious', post: null }); let json = {}; - serializer.serializeBelongsTo(comment._createSnapshot(), json, { key: 'post', options: {} }); + serializer.serializeBelongsTo(comment._createSnapshot(), json, comment.constructor.relationshipsByName.get('post')); assert.deepEqual( json, @@ -229,7 +229,7 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { let comment = store.createRecord('comment', { body: 'Omakase is delicious', post: null }); let json = {}; - serializer.serializeBelongsTo(comment._createSnapshot(), json, { key: 'post', options: {} }); + serializer.serializeBelongsTo(comment._createSnapshot(), json, comment.constructor.relationshipsByName.get('post')); assert.deepEqual( json, @@ -267,7 +267,9 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { let comment = store.createRecord('comment', { body: 'Omakase is delicious', post: post }); let json = {}; - store.serializerFor('post').serializeBelongsTo(comment._createSnapshot(), json, { key: 'post', options: {} }); + store + .serializerFor('post') + .serializeBelongsTo(comment._createSnapshot(), json, comment.constructor.relationshipsByName.get('post')); assert.deepEqual(json, { POST: '1', @@ -308,7 +310,9 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { let json = {}; - store.serializerFor('post').serializeHasMany(post._createSnapshot(), json, { key: 'comments', options: {} }); + store + .serializerFor('post') + .serializeHasMany(post._createSnapshot(), json, post.constructor.relationshipsByName.get('comments')); assert.deepEqual(json, { COMMENTS: ['1'], @@ -341,7 +345,9 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); let json = {}; - store.serializerFor('post').serializeHasMany(post._createSnapshot(), json, { key: 'comments', options: {} }); + store + .serializerFor('post') + .serializeHasMany(post._createSnapshot(), json, post.constructor.relationshipsByName.get('comments')); assert.notOk(hasOwn(json, 'comments'), 'Does not add the relationship key to json'); }); @@ -405,7 +411,7 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { } class Comment extends Model { @attr('string') body; - @belongsTo('post', { inverse: null, async: true }) post; + @belongsTo('post', { inverse: null, async: true, polymorphic: true }) post; } this.owner.register('model:post', Post); @@ -432,7 +438,7 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { store .serializerFor('comment') - .serializeBelongsTo(comment._createSnapshot(), {}, { key: 'post', options: { polymorphic: true } }); + .serializeBelongsTo(comment._createSnapshot(), {}, comment.constructor.relationshipsByName.get('post')); }); test('serializePolymorphicType async', function (assert) { @@ -443,7 +449,7 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { } class Comment extends Model { @attr('string') body; - @belongsTo('post', { inverse: null, async: true }) post; + @belongsTo('post', { inverse: null, async: true, polymorphic: true }) post; } this.owner.register('model:post', Post); @@ -464,7 +470,7 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { store .serializerFor('comment') - .serializeBelongsTo(comment._createSnapshot(), {}, { key: 'post', options: { async: true, polymorphic: true } }); + .serializeBelongsTo(comment._createSnapshot(), {}, comment.constructor.relationshipsByName.get('post')); }); test('normalizeResponse normalizes each record in the array', function (assert) { @@ -1217,10 +1223,9 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { let post = store.createRecord('post', { title: 'Kitties are omakase', id: '1' }); let favorite = store.createRecord('favorite', { post: post, id: '3' }); - store.serializerFor('favorite').serializeBelongsTo(favorite._createSnapshot(), json, { - key: 'post', - options: { polymorphic: true, async: true }, - }); + store + .serializerFor('favorite') + .serializeBelongsTo(favorite._createSnapshot(), json, favorite.constructor.relationshipsByName.get('post')); assert.deepEqual(json, expected, 'returned JSON is correct'); }); diff --git a/tests/main/tests/integration/serializers/rest-serializer-test.js b/tests/main/tests/integration/serializers/rest-serializer-test.js index c8f061aa43d..5683626ad25 100644 --- a/tests/main/tests/integration/serializers/rest-serializer-test.js +++ b/tests/main/tests/integration/serializers/rest-serializer-test.js @@ -515,10 +515,11 @@ module('integration/serializer/rest - RESTSerializer', function (hooks) { evilMinion: evilMinion, }); - serializer.serializeBelongsTo(doomsdayDevice._createSnapshot(), json, { - key: 'evilMinion', - options: { polymorphic: true, async: true }, - }); + serializer.serializeBelongsTo( + doomsdayDevice._createSnapshot(), + json, + doomsdayDevice.constructor.relationshipsByName.get('evilMinion') + ); assert.deepEqual(json, expected, 'returned JSON is correct'); }); @@ -541,10 +542,11 @@ module('integration/serializer/rest - RESTSerializer', function (hooks) { evilMinion: evilMinion, }); - serializer.serializeBelongsTo(doomsdayDevice._createSnapshot(), json, { - key: 'evilMinion', - options: { polymorphic: true, async: true }, - }); + serializer.serializeBelongsTo( + doomsdayDevice._createSnapshot(), + json, + doomsdayDevice.constructor.relationshipsByName.get('evilMinion') + ); assert.deepEqual(json, expected, 'returned JSON is correct'); }); diff --git a/tests/main/tests/unit/custom-class-support/custom-class-model-test.ts b/tests/main/tests/unit/custom-class-support/custom-class-model-test.ts index a475e64d306..a9c6a797dd6 100644 --- a/tests/main/tests/unit/custom-class-support/custom-class-model-test.ts +++ b/tests/main/tests/unit/custom-class-support/custom-class-model-test.ts @@ -110,7 +110,7 @@ module('unit/model - Custom Class Model', function (hooks: NestedHooks) { class CreationStore extends CustomStore { instantiateRecord(identifier: StableRecordIdentifier, createRecordArgs) { assert.strictEqual(identifier.type, 'person', 'Identifier type passed in correctly'); - assert.deepEqual(createRecordArgs, { otherProp: 'unk' }, 'createRecordArg passed in'); + assert.deepEqual(createRecordArgs, { name: 'chris', otherProp: 'unk' }, 'createRecordArg passed in'); returnValue = {}; return returnValue; } @@ -165,7 +165,6 @@ module('unit/model - Custom Class Model', function (hooks: NestedHooks) { async: false, }, name: 'boats', - key: 'boats', }, 'relationships def matches schem' ); @@ -177,7 +176,6 @@ module('unit/model - Custom Class Model', function (hooks: NestedHooks) { type: 'house', kind: 'belongsTo', options: { inverse: null, async: false }, - key: 'house', name: 'house', }, 'relationship def matches schem' @@ -234,7 +232,6 @@ module('unit/model - Custom Class Model', function (hooks: NestedHooks) { inverse: null, async: false, }, - key: 'boats', name: 'boats', }, house: { @@ -244,7 +241,6 @@ module('unit/model - Custom Class Model', function (hooks: NestedHooks) { inverse: null, async: false, }, - key: 'house', name: 'house', }, }; @@ -373,7 +369,6 @@ module('unit/model - Custom Class Model', function (hooks: NestedHooks) { inverse: null, async: true, }, - key: 'house', name: 'house', }, }; diff --git a/tests/main/tests/unit/debug-test.js b/tests/main/tests/unit/debug-test.js index d32801fb75d..d8bce394ed0 100644 --- a/tests/main/tests/unit/debug-test.js +++ b/tests/main/tests/unit/debug-test.js @@ -62,7 +62,6 @@ if (HAS_DEBUG_PACKAGE) { // .readOnly() // .meta({ // options: { inverse: null }, - // isRelationship: true, // kind: 'customRelationship', // name: 'posts', // type: 'post', diff --git a/tests/schema-record/app/models/user-setting.ts b/tests/schema-record/app/models/user-setting.ts deleted file mode 100644 index e8f3511d0a0..00000000000 --- a/tests/schema-record/app/models/user-setting.ts +++ /dev/null @@ -1,5 +0,0 @@ -import Model, { attr } from '@ember-data/model'; - -export default class UserSetting extends Model { - @attr declare name: string; -} diff --git a/tests/schema-record/app/services/store.ts b/tests/schema-record/app/services/store.ts index 41ac00034c8..399ed487c22 100644 --- a/tests/schema-record/app/services/store.ts +++ b/tests/schema-record/app/services/store.ts @@ -1,7 +1,7 @@ +import type { SchemaRecord } from '@warp-drive/schema-record'; +import { instantiateRecord, teardownRecord } from '@warp-drive/schema-record'; + import JSONAPICache from '@ember-data/json-api'; -import type Model from '@ember-data/model'; -import { instantiateRecord, teardownRecord } from '@ember-data/model'; -import { buildSchema, modelFor } from '@ember-data/model/hooks'; import RequestManager from '@ember-data/request'; import Fetch from '@ember-data/request/fetch'; import DataStore, { CacheHandler } from '@ember-data/store'; @@ -16,23 +16,17 @@ export default class Store extends DataStore { const manager = (this.requestManager = new RequestManager()); manager.use([Fetch]); manager.useCache(CacheHandler); - - this.registerSchema(buildSchema(this)); } createCache(capabilities: CacheCapabilitiesManager): Cache { return new JSONAPICache(capabilities); } - instantiateRecord(identifier: StableRecordIdentifier, createRecordArgs: { [key: string]: unknown }): unknown { - return instantiateRecord.call(this, identifier, createRecordArgs); - } - - teardownRecord(record: Model): void { - return teardownRecord.call(this, record); + instantiateRecord(identifier: StableRecordIdentifier, createArgs?: Record): SchemaRecord { + return instantiateRecord(this, identifier, createArgs); } - modelFor(type: string) { - return modelFor.call(this, type); + teardownRecord(record: SchemaRecord): void { + return teardownRecord(record); } } diff --git a/tests/schema-record/config/environment.js b/tests/schema-record/config/environment.js index 265be9f8b5e..7e28113b93b 100644 --- a/tests/schema-record/config/environment.js +++ b/tests/schema-record/config/environment.js @@ -2,7 +2,7 @@ module.exports = function (environment) { let ENV = { - modulePrefix: 'builders-test-app', + modulePrefix: 'schema-record-test-app', environment, rootURL: '/', locationType: 'history', diff --git a/tests/schema-record/package.json b/tests/schema-record/package.json index ff919195465..1e633ff1c8c 100644 --- a/tests/schema-record/package.json +++ b/tests/schema-record/package.json @@ -22,11 +22,32 @@ "dependenciesMeta": { "@warp-drive/schema-record": { "injected": true + }, + "@ember-data/store": { + "injected": true + }, + "@ember-data/request": { + "injected": true + }, + "@ember-data/tracking": { + "injected": true + }, + "@ember-data/json-api": { + "injected": true + }, + "@ember-data/graph": { + "injected": true } }, "devDependencies": { "@babel/core": "^7.23.0", "@babel/runtime": "^7.23.1", + "@ember-data/store": "workspace:5.5.0-alpha.9", + "@ember-data/request": "workspace:5.5.0-alpha.9", + "@ember-data/tracking": "workspace:5.5.0-alpha.9", + "@ember-data/json-api": "workspace:5.5.0-alpha.9", + "@ember-data/request-utils": "workspace:5.5.0-alpha.9", + "@ember-data/graph": "workspace:5.5.0-alpha.9", "@warp-drive/schema-record": "workspace:5.5.0-alpha.9", "@ember-data/private-build-infra": "workspace:5.5.0-alpha.9", "@ember-data/unpublished-test-infra": "workspace:5.5.0-alpha.9", diff --git a/tests/schema-record/tests/reads/basic-fields-test.ts b/tests/schema-record/tests/reads/basic-fields-test.ts new file mode 100644 index 00000000000..fac4ecc4c0e --- /dev/null +++ b/tests/schema-record/tests/reads/basic-fields-test.ts @@ -0,0 +1,171 @@ +import type { SchemaRecord, Transform } from '@warp-drive/schema-record'; +import { SchemaService } from '@warp-drive/schema-record'; +import { module, test } from 'qunit'; + +import { setupTest } from 'ember-qunit'; + +import type Store from '@ember-data/store'; +import { recordIdentifierFor } from '@ember-data/store'; +import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; +import type { JsonApiResource } from '@ember-data/types/q/record-data-json-api'; + +interface User { + id: string | null; + $type: 'user'; + name: string; + age: number; + netWorth: number; + coolometer: number; + rank: number; +} + +module('Reads | basic fields', function (hooks) { + setupTest(hooks); + + test('we can use simple fields with no `type`', function (assert) { + const store = this.owner.lookup('service:store') as Store; + const schema = new SchemaService(); + store.registerSchema(schema); + + schema.defineSchema('user', [ + { + name: 'name', + type: null, + kind: 'attribute', + }, + ]); + + const record = store.createRecord('user', { name: 'Rey Skybarker' }) as User; + + assert.strictEqual(record.id, null, 'id is accessible'); + assert.strictEqual(record.$type, 'user', '$type is accessible'); + + assert.strictEqual(record.name, 'Rey Skybarker', 'name is accessible'); + + try { + // @ts-expect-error intentionally accessing unknown field + record.lastName; + assert.ok(false, 'should error when accessing unknown field'); + } catch (e) { + assert.strictEqual( + (e as Error).message, + 'No field named lastName on user', + 'should error when accessing unknown field' + ); + } + }); + + test('we can use simple fields with a `type`', function (assert) { + const store = this.owner.lookup('service:store') as Store; + const schema = new SchemaService(); + store.registerSchema(schema); + + const FloatTransform: Transform = { + serialize(value: string | number, options: { precision?: number } | null, _record: SchemaRecord): string { + return typeof value === 'number' + ? value.toFixed(options?.precision ?? 3) + : Number(value).toFixed(options?.precision ?? 3); + }, + hydrate(value: string, _options: { precision?: number } | null, _record: SchemaRecord): number { + if (value === undefined || value === null) { + return 0; + } + return Number(value); + }, + defaultValue(_options: { precision?: number } | null, _identifier: StableRecordIdentifier): string { + const v = 0; + return v.toFixed(_options?.precision ?? 3); + }, + }; + + schema.registerTransform('float', FloatTransform); + + schema.defineSchema('user', [ + { + name: 'name', + type: null, + kind: 'attribute', + }, + { + name: 'lastName', + type: 'string', + kind: 'attribute', + }, + { + name: 'rank', + type: 'float', + kind: 'attribute', + options: { precision: 0 }, + }, + { + name: 'age', + type: 'float', + options: { precision: 0 }, + kind: 'attribute', + }, + { + name: 'netWorth', + type: 'float', + options: { precision: 2 }, + kind: 'attribute', + }, + { + name: 'coolometer', + type: 'float', + kind: 'attribute', + }, + ]); + + const record = store.createRecord('user', { + name: 'Rey Skybarker', + age: 42, + netWorth: 1_000_000.009, + coolometer: '100.0', + }) as User; + const identifier = recordIdentifierFor(record); + + assert.strictEqual(record.id, null, 'id is accessible'); + assert.strictEqual(record.$type, 'user', '$type is accessible'); + assert.strictEqual(record.name, 'Rey Skybarker', 'name is accessible'); + assert.strictEqual(record.age, 42, 'age is accessible'); + assert.strictEqual(record.netWorth, 1_000_000.01, 'netWorth is accessible'); + assert.strictEqual(record.coolometer, 100, 'coolometer is accessible'); + assert.strictEqual(record.rank, 0, 'rank is accessible'); + + try { + // @ts-expect-error intentionally have not typed the property on the record + record.lastName; + assert.ok(false, 'should error when accessing a field with an unknown transform'); + } catch (e) { + assert.strictEqual( + (e as Error).message, + `No 'string' transform defined for use by user.lastName`, + 'should error when accessing unknown field transform' + ); + } + + const resource = store.cache.peek(identifier) as JsonApiResource; + + assert.strictEqual(store.cache.getAttr(identifier, 'name'), 'Rey Skybarker', 'cache value for name is correct'); + assert.strictEqual(store.cache.getAttr(identifier, 'age'), '42', 'cache value for age is correct'); + assert.strictEqual( + store.cache.getAttr(identifier, 'netWorth'), + '1000000.01', + 'cache value for netWorth is correct' + ); + assert.strictEqual( + store.cache.getAttr(identifier, 'coolometer'), + '100.000', + 'cache value for coolometer is correct' + ); + assert.strictEqual(store.cache.getAttr(identifier, 'rank'), '0', 'cache value for rank is correct'); + + assert.strictEqual(resource.type, 'user', 'resource cache type is correct'); + assert.strictEqual(resource.id, null, 'resource cache id is correct'); + assert.strictEqual(resource.attributes?.name, 'Rey Skybarker', 'resource cache value for name is correct'); + assert.strictEqual(resource.attributes?.age, '42', 'resource cache value for age is correct'); + assert.strictEqual(resource.attributes?.netWorth, '1000000.01', 'cresource ache value for netWorth is correct'); + assert.strictEqual(resource.attributes?.coolometer, '100.000', 'resource cache value for coolometer is correct'); + assert.strictEqual(resource.attributes?.rank, '0', 'resource cache value for rank is correct'); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 5e5b6a2324d..60895b4e6cd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,7 +26,6 @@ "ember-data-types/q/promise-proxies.ts", "ember-data-types/q/minimum-serializer-interface.ts", "ember-data-types/q/minimum-adapter-interface.ts", - "ember-data-types/q/identifier.ts", "ember-data-types/q/fetch-manager.ts", "ember-data-types/q/ember-data-json-api.ts", "tests/graph/tests/integration/graph/polymorphism/implicit-keys-test.ts", diff --git a/tsconfig.root.json b/tsconfig.root.json index bf047b8f783..ce73c054bf9 100644 --- a/tsconfig.root.json +++ b/tsconfig.root.json @@ -53,6 +53,8 @@ "@warp-drive/holodeck/*": ["packages/holodeck/dist/*"], "@ember-data/request": ["packages/request/addon"], "@ember-data/request/*": ["packages/request/addon/*"], + "@warp-drive/schema-record": ["packages/schema-record/src"], + "@warp-drive/schema-record/*": ["packages/schema-record/src/*"], "@ember-data/request-utils": ["packages/request-utils/src"], "@ember-data/request-utils/*": ["packages/request-utils/src/*"], "@ember-data/rest": ["packages/rest/src"],