diff --git a/lib/src/decorators/ignore.ts b/lib/src/decorators/ignore.ts new file mode 100644 index 0000000..62a836a --- /dev/null +++ b/lib/src/decorators/ignore.ts @@ -0,0 +1,15 @@ +import { ModelConstructor } from "../interfaces"; + +export const IGNORABLE_FIELDS = new WeakMap>(); + +export function ignore(value: unknown, context: ClassFieldDecoratorContext) { + context.addInitializer(function () { + const ctor = this.constructor as ModelConstructor; + let set = IGNORABLE_FIELDS.get(ctor); + if (!set) { + set = new Set(); + IGNORABLE_FIELDS.set(ctor, set); + } + set.add(context.name as string); + }); +} diff --git a/lib/src/decorators/immutable.ts b/lib/src/decorators/immutable.ts new file mode 100644 index 0000000..d90f781 --- /dev/null +++ b/lib/src/decorators/immutable.ts @@ -0,0 +1,15 @@ +import { ModelConstructor } from "../interfaces"; + +export const IMMUTABLE_FIELDS = new WeakMap>(); + +export function immutable(value: unknown, context: ClassFieldDecoratorContext) { + context.addInitializer(function () { + const ctor = this.constructor as ModelConstructor; + let set = IMMUTABLE_FIELDS.get(ctor); + if (!set) { + set = new Set(); + IMMUTABLE_FIELDS.set(ctor, set); + } + set.add(context.name as string); + }); +} diff --git a/lib/src/decorators/index.ts b/lib/src/decorators/index.ts new file mode 100644 index 0000000..74308b9 --- /dev/null +++ b/lib/src/decorators/index.ts @@ -0,0 +1,3 @@ +export * from "./ignore"; +export * from "./immutable"; +export * from "./utils"; diff --git a/lib/src/decorators/property.ts b/lib/src/decorators/property.ts new file mode 100644 index 0000000..cf74376 --- /dev/null +++ b/lib/src/decorators/property.ts @@ -0,0 +1,11 @@ +export class PropertyMetadata { + get(object: any, property: string) { + console.warn(object, property); + } +} + +export function property() { + return function (target: object, property: string) { + console.warn(target, property); + }; +} diff --git a/lib/src/decorators/utils.ts b/lib/src/decorators/utils.ts new file mode 100644 index 0000000..93d3ced --- /dev/null +++ b/lib/src/decorators/utils.ts @@ -0,0 +1,17 @@ +import { IMMUTABLE_FIELDS } from "./immutable"; +import { IGNORABLE_FIELDS } from "./ignore"; + +export const getImmutableKeys = (obj: any): string[] => { + const ctor = obj.constructor; + return IMMUTABLE_FIELDS.has(ctor) ? Array.from(IMMUTABLE_FIELDS.get(ctor) as Set) : []; +}; + +export const isImmutableKey = (object: any, key: string) => getImmutableKeys(object).includes(key); + +export const getIgnorableKeys = (obj: any): string[] => { + const ctor = obj.constructor; + + return IGNORABLE_FIELDS.has(ctor) ? Array.from(IGNORABLE_FIELDS.get(ctor) as Set) : []; +}; + +export const isIgnorableKey = (object: any, key: string) => getIgnorableKeys(object).includes(key); diff --git a/lib/src/index.ts b/lib/src/index.ts index e69de29..ddfec6d 100644 --- a/lib/src/index.ts +++ b/lib/src/index.ts @@ -0,0 +1,3 @@ +export * from "./models"; +export * from "./decorators"; +export * from "./interfaces"; diff --git a/lib/src/interfaces/index.ts b/lib/src/interfaces/index.ts index e69de29..636af4d 100644 --- a/lib/src/interfaces/index.ts +++ b/lib/src/interfaces/index.ts @@ -0,0 +1,8 @@ +export type ModelConstructor = new (...args: any[]) => IModel; + +export type TypeOfModel = "collection"; + +export interface IModel { + readonly id: string; + readonly type: TypeOfModel; +} diff --git a/lib/src/models/collection.ts b/lib/src/models/collection.ts new file mode 100644 index 0000000..11f391a --- /dev/null +++ b/lib/src/models/collection.ts @@ -0,0 +1,75 @@ +import { ignore } from "../decorators/ignore"; + +export class Collection { + @ignore readonly id = crypto.randomUUID(); + private _store: Record = {}; + + get length() { + return Object.keys(this._store).length; + } + + constructor(args: T[] = []) { + args.forEach((item, index) => (this._store[index] = item)); + } + + *[Symbol.iterator]() { + const array = this.toArray(); + for (const item of array) { + yield item; + } + } + + push(...args: T[]) { + const diff: Record = {}; + args.forEach((item) => { + const index = this.length; + this._store[index] = item; + diff[index] = item; + }); + return { ...diff, length: this.length }; + } + + remove(item: T) { + const backup: Record = { ...this._store }; + + Object.keys(this._store).forEach((key) => { + const index = Number(key); + const itemOfIndex = this.get(index); + if (item !== itemOfIndex) return; + + Reflect.deleteProperty(this._store, Number(key)); + }); + + const diff: Record = {}; + const values = Object.values(this._store); + const newStore: Record = {}; + + for (let index = 0; index < values.length; index++) { + newStore[index] = values[index]; + + if (backup[index] !== newStore[index]) { + diff[index] = newStore[index]; + } + } + this._store = newStore; + + return { ...diff, length: this.length }; + } + + indexOf(item: T) { + return this.toArray().indexOf(item); + } + + get(index: number): T { + return this._store[index]; + } + + set(index: number, value: T) { + this._store[index] = value; + return { [index]: value, length: this.length }; + } + + toArray(): T[] { + return Object.keys(this._store).map((index) => this._store[index]); + } +} diff --git a/lib/src/models/index.ts b/lib/src/models/index.ts new file mode 100644 index 0000000..7c36a75 --- /dev/null +++ b/lib/src/models/index.ts @@ -0,0 +1 @@ +export * from "./collection"; diff --git a/lib/src/package.json b/lib/src/package.json index f576691..6873169 100644 --- a/lib/src/package.json +++ b/lib/src/package.json @@ -1,5 +1,5 @@ { - "name": "@gxc-solutions/lib", + "name": "@gxc-solutions/model-base", "version": "0.0.1", "main": "index.js", "author": "GXC Solutions",