json-api-models
Advanced tools
Comparing version
@@ -1,1 +0,1 @@ | ||
const e=class{constructor(e){Object.defineProperty(this,"type",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"id",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"attributes",{enumerable:!0,configurable:!0,writable:!0,value:{}}),Object.defineProperty(this,"relationships",{enumerable:!0,configurable:!0,writable:!0,value:{}}),Object.defineProperty(this,"meta",{enumerable:!0,configurable:!0,writable:!0,value:{}}),Object.defineProperty(this,"links",{enumerable:!0,configurable:!0,writable:!0,value:{}}),this.type=e.type,this.id=e.id,this.merge(e)}identifier(){return{id:this.id,type:this.type}}merge(e){this.links=e.links??this.links,this.meta=e.meta??this.meta,e.attributes&&Object.assign(this.attributes,e.attributes),e.relationships&&Object.entries(e.relationships).forEach((([e,t])=>{this.relationships[e]=this.relationships[e]||{},Object.assign(this.relationships[e],t)}))}};class t{constructor(e={}){Object.defineProperty(this,"models",{enumerable:!0,configurable:!0,writable:!0,value:e}),Object.defineProperty(this,"graph",{enumerable:!0,configurable:!0,writable:!0,value:{}})}find(e,t){return null===e?null:Array.isArray(e)?e.map((e=>this.find(e))):"object"==typeof e?this.find(e.type,e.id):t&&this.graph[e]?.[t]||null}findAll(e){return this.graph[e]?Object.keys(this.graph[e]).map((t=>this.graph[e][t])):[]}sync(e){return e.included?.map((e=>this.syncResource(e))),Array.isArray(e.data)?e.data.map((e=>this.syncResource(e))):e.data?this.syncResource(e.data):null}syncResource(e){const{type:t,id:i}=e;return this.graph[t]=this.graph[t]||{},this.graph[t][i]?this.graph[t][i].merge(e):this.graph[t][i]=this.createModel(e),this.graph[t][i]}createModel(t){const i=this.models[t.type]||e;return new Proxy(new i(t),{get:(e,t,i)=>{if("string"==typeof t){if(void 0!==e.attributes?.[t])return e.attributes[t];const i=e.relationships?.[t]?.data;if(void 0!==i)return Array.isArray(i),this.find(i)}return Reflect.get(e,t,i)}})}forget(e){delete this.graph[e.type][e.id]}reset(){this.graph={}}}export{e as Model,t as Store}; | ||
const e=class{constructor(e,t){Object.defineProperty(this,"store",{enumerable:!0,configurable:!0,writable:!0,value:t}),Object.defineProperty(this,"type",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"id",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"attributes",{enumerable:!0,configurable:!0,writable:!0,value:{}}),Object.defineProperty(this,"relationships",{enumerable:!0,configurable:!0,writable:!0,value:{}}),Object.defineProperty(this,"meta",{enumerable:!0,configurable:!0,writable:!0,value:{}}),Object.defineProperty(this,"links",{enumerable:!0,configurable:!0,writable:!0,value:{}}),this.type=e.type,this.id=e.id,this.merge(e)}identifier(){return{id:this.id,type:this.type}}merge(e){this.links=e.links??this.links,this.meta=e.meta??this.meta,e.attributes&&(Object.assign(this.attributes,e.attributes),Object.keys(e.attributes).forEach((e=>{Object.getOwnPropertyDescriptor(Object.getPrototypeOf(this),e)||Object.getOwnPropertyDescriptor(this,e)||Object.defineProperty(this,e,{get:()=>this.attributes[e],configurable:!0,enumerable:!0})}))),e.relationships&&Object.entries(e.relationships).forEach((([e,t])=>{this.relationships[e]=this.relationships[e]||{},Object.assign(this.relationships[e],t),Object.getOwnPropertyDescriptor(Object.getPrototypeOf(this),e)||Object.getOwnPropertyDescriptor(this,e)||Object.defineProperty(this,e,{get:()=>this.getRelationship(e),configurable:!0,enumerable:!0})}))}getRelationship(e){const t=this.relationships[e].data;return Array.isArray(t)||t?this.store.find(t):void 0}};class t{constructor(e={}){Object.defineProperty(this,"models",{enumerable:!0,configurable:!0,writable:!0,value:e}),Object.defineProperty(this,"graph",{enumerable:!0,configurable:!0,writable:!0,value:{}})}find(e,t){return null===e?null:Array.isArray(e)?e.map((e=>this.find(e))):"object"==typeof e?this.find(e.type,e.id):t&&this.graph[e]?.[t]||null}findAll(e){return this.graph[e]?Object.keys(this.graph[e]).map((t=>this.graph[e][t])):[]}sync(e){return e.included?.map((e=>this.syncResource(e))),Array.isArray(e.data)?e.data.map((e=>this.syncResource(e))):e.data?this.syncResource(e.data):null}syncResource(e){const{type:t,id:r}=e;return this.graph[t]=this.graph[t]||{},this.graph[t][r]?this.graph[t][r].merge(e):this.graph[t][r]=this.createModel(e),this.graph[t][r]}createModel(t){return new(this.models[t.type]||e)(t,this)}forget(e){delete this.graph[e.type][e.id]}reset(){this.graph={}}}export{e as Model,t as Store}; |
import { JsonApiResource, ModelForType, SchemaCollection } from './types'; | ||
type PartialJsonApiResource<T extends JsonApiResource> = { | ||
[P in keyof T]?: Partial<T[P]>; | ||
}; | ||
import { Store } from './store.ts'; | ||
declare class ModelBase<Schema extends JsonApiResource = JsonApiResource> { | ||
protected store: Store; | ||
type: Schema['type']; | ||
@@ -12,3 +11,3 @@ id: Schema['id']; | ||
links: Schema['links']; | ||
constructor(data: Schema); | ||
constructor(data: Schema, store: Store); | ||
/** | ||
@@ -24,6 +23,7 @@ * Make a resource identifier object for this model. | ||
*/ | ||
merge(data: PartialJsonApiResource<Schema>): void; | ||
merge(data: Omit<JsonApiResource<Schema['type']>, 'type' | 'id'>): void; | ||
private getRelationship; | ||
} | ||
type ProxiedModel<Schema extends JsonApiResource, Schemas extends Record<string, JsonApiResource>> = Schema & Schema['attributes'] & { | ||
[Property in keyof NonNullable<Schema['relationships']>]: NonNullable<NonNullable<Schema['relationships']>[Property]> extends { | ||
[Property in keyof NonNullable<Schema['relationships']>]?: NonNullable<NonNullable<Schema['relationships']>[Property]> extends { | ||
data?: infer Data; | ||
@@ -37,3 +37,3 @@ } ? Data extends { | ||
export type Model<Schema extends JsonApiResource = JsonApiResource, Schemas extends SchemaCollection = SchemaCollection> = JsonApiResource<Schema['type']> & ModelBase<Schema> & ProxiedModel<Schema, Schemas>; | ||
export declare const Model: new <Schema extends JsonApiResource = JsonApiResource, Schemas extends SchemaCollection = SchemaCollection>(data: JsonApiResource<Schema['type']>) => Model<Schema, Schemas>; | ||
export declare const Model: new <Schema extends JsonApiResource = JsonApiResource, Schemas extends SchemaCollection = SchemaCollection>(data: JsonApiResource<Schema['type']>, store: Store<Schemas>) => Model<Schema, Schemas>; | ||
export {}; |
import { Model } from './model.ts'; | ||
import { Store } from './store.ts'; | ||
export interface JsonApiDocument<Type extends string = string> { | ||
@@ -34,4 +35,4 @@ data: JsonApiResource<Type> | JsonApiResource<Type>[] | null; | ||
export type ModelMap<Schemas extends SchemaCollection = SchemaCollection> = { | ||
[Type in keyof Schemas & string]?: new (data: JsonApiResource<Type>) => Schemas[Type]; | ||
[Type in keyof Schemas & string]?: new (data: JsonApiResource<Type>, store: Store<Schemas>) => Schemas[Type]; | ||
}; | ||
export type ModelForType<Type extends string, Schemas> = Type extends keyof Schemas ? Schemas[Type] : Model<JsonApiResource<Type>>; | ||
export type ModelForType<Type extends string, Schemas extends SchemaCollection> = Type extends keyof Schemas ? Model<Schemas[Type], Schemas> : Model<JsonApiResource<Type>, Schemas>; |
{ | ||
"name": "json-api-models", | ||
"description": "A lightweight layer for working with JSON:API data.", | ||
"version": "0.2.0-beta.5", | ||
"version": "0.2.0-beta.6", | ||
"author": "Toby Zerner", | ||
@@ -6,0 +6,0 @@ "license": "MIT", |
import { JsonApiResource, ModelForType, SchemaCollection } from './types'; | ||
import { Store } from './store.ts'; | ||
type PartialJsonApiResource<T extends JsonApiResource> = { | ||
[P in keyof T]?: Partial<T[P]>; | ||
}; | ||
class ModelBase<Schema extends JsonApiResource = JsonApiResource> { | ||
@@ -15,3 +12,6 @@ public type: Schema['type']; | ||
constructor(data: Schema) { | ||
constructor( | ||
data: Schema, | ||
protected store: Store, | ||
) { | ||
this.type = data.type; | ||
@@ -36,3 +36,5 @@ this.id = data.id; | ||
*/ | ||
public merge(data: PartialJsonApiResource<Schema>): void { | ||
public merge( | ||
data: Omit<JsonApiResource<Schema['type']>, 'type' | 'id'>, | ||
): void { | ||
this.links = data.links ?? this.links; | ||
@@ -43,2 +45,20 @@ this.meta = data.meta ?? this.meta; | ||
Object.assign(this.attributes, data.attributes); | ||
Object.keys(data.attributes).forEach((name) => { | ||
if ( | ||
Object.getOwnPropertyDescriptor( | ||
Object.getPrototypeOf(this), | ||
name, | ||
) || | ||
Object.getOwnPropertyDescriptor(this, name) | ||
) { | ||
return; | ||
} | ||
Object.defineProperty(this, name, { | ||
get: () => this.attributes[name], | ||
configurable: true, | ||
enumerable: true, | ||
}); | ||
}); | ||
} | ||
@@ -50,3 +70,20 @@ | ||
this.relationships[name] = this.relationships[name] || {}; | ||
Object.assign(this.relationships[name], relationship); | ||
if ( | ||
Object.getOwnPropertyDescriptor( | ||
Object.getPrototypeOf(this), | ||
name, | ||
) || | ||
Object.getOwnPropertyDescriptor(this, name) | ||
) { | ||
return; | ||
} | ||
Object.defineProperty(this, name, { | ||
get: () => this.getRelationship(name), | ||
configurable: true, | ||
enumerable: true, | ||
}); | ||
}, | ||
@@ -56,2 +93,15 @@ ); | ||
} | ||
private getRelationship(name: string) { | ||
const data = this.relationships[name].data; | ||
// https://github.com/microsoft/TypeScript/issues/14107 | ||
if (Array.isArray(data)) { | ||
return this.store.find(data); | ||
} | ||
if (data) { | ||
return this.store.find(data); | ||
} | ||
} | ||
} | ||
@@ -64,3 +114,3 @@ | ||
Schema['attributes'] & { | ||
[Property in keyof NonNullable<Schema['relationships']>]: NonNullable< | ||
[Property in keyof NonNullable<Schema['relationships']>]?: NonNullable< | ||
NonNullable<Schema['relationships']>[Property] | ||
@@ -88,2 +138,3 @@ > extends { data?: infer Data } | ||
data: JsonApiResource<Schema['type']>, | ||
store: Store<Schemas>, | ||
) => Model<Schema, Schemas> = ModelBase as any; |
@@ -96,18 +96,3 @@ import { Model } from './model'; | ||
return new Proxy(new ModelClass(data), { | ||
get: (target, prop, receiver) => { | ||
if (typeof prop === 'string') { | ||
if (target.attributes?.[prop] !== undefined) { | ||
return target.attributes[prop]; | ||
} | ||
const data = target.relationships?.[prop]?.data; | ||
if (data !== undefined) { | ||
return Array.isArray(data) | ||
? this.find(data) | ||
: this.find(data); | ||
} | ||
} | ||
return Reflect.get(target, prop, receiver); | ||
}, | ||
}) as ModelForType<Type, Schemas>; | ||
return new ModelClass(data, this) as ModelForType<Type, Schemas>; | ||
} | ||
@@ -114,0 +99,0 @@ |
import { Model } from './model.ts'; | ||
import { Store } from './store.ts'; | ||
@@ -46,2 +47,3 @@ export interface JsonApiDocument<Type extends string = string> { | ||
data: JsonApiResource<Type>, | ||
store: Store<Schemas>, | ||
) => Schemas[Type]; | ||
@@ -52,3 +54,5 @@ }; | ||
Type extends string, | ||
Schemas, | ||
> = Type extends keyof Schemas ? Schemas[Type] : Model<JsonApiResource<Type>>; | ||
Schemas extends SchemaCollection, | ||
> = Type extends keyof Schemas | ||
? Model<Schemas[Type], Schemas> | ||
: Model<JsonApiResource<Type>, Schemas>; |
25126
6.91%354
10.97%