diff --git a/package.json b/package.json index 71f2b23..98bc24a 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "repository": "https://github.com/marcus13345/frigid.git", "devDependencies": { "@types/node": "^15.12.2", + "chai": "^4.3.4", "typescript": "^4.3.2", "yarn": "^1.22.10" }, diff --git a/src/Frigid.ts b/src/Frigid.ts new file mode 100644 index 0000000..a00ba5c --- /dev/null +++ b/src/Frigid.ts @@ -0,0 +1,33 @@ +import { existsSync, readFileSync, writeFileSync } from "fs"; +import Serializable from "./Serializable.js"; + +const PERSIST_LOCATION = Symbol('PERSIST_LOCATION'); +export const RESTORE = Symbol('RESTORE'); + +export default class Frigid extends Serializable { + static create(filename: string, ...args: any[]) { + if(existsSync(filename)) { + const instance = this.deserialize(readFileSync(filename)); + // TS is plain and simply wrong... symbols can be used to index object... + // @ts-ignore + instance[PERSIST_LOCATION] = filename; + instance[RESTORE](); + return instance; + } else { + const instance = new this(...args); + // again... TS is wrong... + // @ts-ignore + instance[PERSIST_LOCATION] = filename; + instance[RESTORE](); + instance.sync(); + return instance; + } + } + + sync() { + const data = this.serialize(); + // this is getting annoying... + // @ts-ignore + writeFileSync(this[PERSIST_LOCATION], data); + } +} \ No newline at end of file diff --git a/src/Serializable.ts b/src/Serializable.ts index cfc9ebf..740b111 100644 --- a/src/Serializable.ts +++ b/src/Serializable.ts @@ -1,5 +1,6 @@ // import { Ubjson } from '@shelacek/ubjson'; import { existsSync, readFileSync, writeFileSync } from 'fs'; +import { reverseLookup } from './reverseLookup.js'; export default class Serializable { @@ -14,26 +15,21 @@ export default class Serializable { static INSTANCE_DECLARATION = '$$INSTANCE_ID'; static INSTANCE_REFERENCE = '$$INSTANCE_REF'; - // things that need to be stored only at runtime - // are keyed with symbols to not interfere with - // user code. - static PERSIST_LOCATION = Symbol('PERSIST_LOCATION'); + static serializationDependencies(): any[] { + return []; + } toJson() { return JSON.stringify(this.toSerializableObject(), null, 2); } - static serializationDependencies(): any[] { - return []; - } - static fromJson(str: string) { return this.fromSerializableObject(JSON.parse(str)); } - // thisdoesnt operate recursively, it doesnt need to, because dependency - // resoltion isnt required. we simply declare the dependencies. - // so we never touch static serializationDependencies! + // this doesnt operate recursively, it doesnt need to, because + // dependency resoltion isnt required. we simply declare the + // dependencies. so we never touch serializationDependencies! toSerializableObject() { const instances: Map = new Map(); @@ -197,57 +193,4 @@ export default class Serializable { } } } - - async restore() {} - - static createFromDisk(filename: string, ...args: any[]) { - const filepath = createFilepath(filename); - if(existsSync(filepath)) { - const instance = this.deserialize(readFileSync(filepath)); - // TS is plain and simply wrong... symbols can be used to index object... - // @ts-ignore - instance[Serializable.PERSIST_LOCATION] = filepath; - instance?.restore(); - return instance; - } else { - const instance = new this(...args); - // again... TS is wrong... - // @ts-ignore - instance[Serializable.PERSIST_LOCATION] = filepath; - instance?.updateDisk(); - return instance; - } - } - - updateDisk(filepath?: string) { - // if it hasnt yet been written to disk... - // this can happen if the contrustor - // was called outside of createFromDisk - if(filepath) { - // see above... TS7053 is just _wrong_. incorrect. thats not how JS works. - // @ts-ignore - this[Serializable.PERSIST_LOCATION] = createFilepath(filepath); - } - const data = this.serialize(); - // this is getting annoying... - // @ts-ignore - writeFileSync(this[Serializable.PERSIST_LOCATION], data); - } -} - -function createFilepath(path: string) { - return `data/${path}`; -} - - -function reverseLookup(map: Map, value: V): K { - // console.log('searching for', value, 'in', map); - for(const [k, v] of map) { - if(v === value) { - // console.log('found in key', k); - return k; - } - } - // console.log(value, 'not found') - return null; } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 94fa023..ee3c06f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1,2 @@ -export {default as Serializable} from './Serializable.js'; \ No newline at end of file +export {default as Serializable} from './Serializable.js'; +export {default as Frigid, RESTORE} from './Frigid.js' \ No newline at end of file diff --git a/src/reverseLookup.ts b/src/reverseLookup.ts new file mode 100644 index 0000000..6176b61 --- /dev/null +++ b/src/reverseLookup.ts @@ -0,0 +1,11 @@ +export function reverseLookup(map: Map, value: V): K { + // console.log('searching for', value, 'in', map); + for (const [k, v] of map) { + if (v === value) { + // console.log('found in key', k); + return k; + } + } + // console.log(value, 'not found') + return null; +} diff --git a/test.state.json b/test.state.json new file mode 100644 index 0000000..0530746 --- /dev/null +++ b/test.state.json @@ -0,0 +1,5 @@ +{ + "$$INSTANCE_ID": 0, + "foo": "bar", + "$$CLASS_NAME": "Test" +} \ No newline at end of file diff --git a/test/Frigid.js b/test/Frigid.js new file mode 100644 index 0000000..868e5c1 --- /dev/null +++ b/test/Frigid.js @@ -0,0 +1,42 @@ +import {Frigid, RESTORE} from '../out/index.js'; +import { existsSync, readFileSync, unlinkSync } from 'fs'; +import { expect } from 'chai'; + +const trackingData = { + constructorCalls: 0, + restoreCalls: 0 +} + +class Test extends Frigid { + foo = 'bar'; + + constructor() { + super(); + trackingData.constructorCalls ++; + } + + [RESTORE]() { + trackingData.restoreCalls ++; + } +} + +const filepath = 'test.state.json'; + +if(existsSync(filepath)) { + unlinkSync(filepath) +} + +const test = Test.create(filepath) + +expect(test.sync.bind(test)).to.not.throw(); + +expect(existsSync(filepath)).to.be.true; +expect(readFileSync(filepath).toString()).to.not.be.empty; + +expect(trackingData.constructorCalls).to.equal(1); +expect(trackingData.restoreCalls).to.equal(1); + +const retest = Test.create(filepath); + +expect(trackingData.constructorCalls).to.equal(1); +expect(trackingData.restoreCalls).to.equal(2); \ No newline at end of file diff --git a/test/Serializable.js b/test/Serializable.js new file mode 100644 index 0000000..6fb2e52 --- /dev/null +++ b/test/Serializable.js @@ -0,0 +1,51 @@ +import { Serializable } from '../out/index.js' + +class Sub extends Serializable { + otherData = sharedObject; + root; + + static serializationDependencies() { + return [Root]; + } +} + +class Root extends Serializable{ + stuff = sharedObject; + child; + + static serializationDependencies() { + return [Sub]; + } + + test() { + return { + circular: this.child.root === this, + shared: this.stuff === this.child.otherData + } + } +} + +const sharedObject = {shared: 'data'} + +const root = new Root(); +const sub = new Sub(); + +root.child = sub; +sub.root = root; + +console.clear(); +console.log('#'.repeat(process.stdout.columns)); + +console.log(root); +const json = root.toJson(); +console.log(json); +const obj = Root.fromJson(json); +console.log(obj); +const tests = obj.test(); +console.log(tests); + +const passing = Object.values(tests).reduce((v, acc) => v && acc, true); +if(!passing) { + console.log('Some tests failed!'); + process.exit(1); +}; \ No newline at end of file diff --git a/test/index.js b/test/index.js index b859c66..09db8e3 100644 --- a/test/index.js +++ b/test/index.js @@ -1,57 +1,4 @@ -import { Serializable } from '../out/index.js' +import './Serializable.js'; +import './Frigid.js'; -class Sub extends Serializable { - otherData = sharedObject; - root; - - static serializationDependencies() { - return [Root]; - } -} - - - -class Root extends Serializable{ - stuff = sharedObject; - child; - - static serializationDependencies() { - return [Sub]; - } - - test() { - return { - circular: this.child.root === this, - shared: this.stuff === this.child.otherData - } - } -} - -const sharedObject = {shared: 'data'} - -const root = new Root(); -const sub = new Sub(); - -root.child = sub; -sub.root = root; - -console.clear(); -console.log('#'.repeat(process.stdout.columns)); - - - -console.log(root); -const json = root.toJson(); -console.log(json); -const obj = Root.fromJson(json); -console.log(obj); -const tests = obj.test(); -console.log(tests); - -const passing = Object.values(tests).reduce((v, acc) => v && acc, true); -if(!passing) { - console.log('Some tests failed!'); - process.exit(1); -} else { - console.log('All tests Passed!'); -} \ No newline at end of file +console.log('All tests passed!'); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 5b29763..37bae2c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6,6 +6,43 @@ version "15.12.2" resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.2.tgz#1f2b42c4be7156ff4a6f914b2fb03d05fa84e38d" +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + +chai@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49" + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + pathval "^1.1.1" + type-detect "^4.0.5" + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + dependencies: + type-detect "^4.0.0" + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + +type-detect@^4.0.0, type-detect@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + typescript@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805"