diff --git a/packages/core/src/database.ts b/packages/core/src/database.ts index 3145e5a..ff237d2 100644 --- a/packages/core/src/database.ts +++ b/packages/core/src/database.ts @@ -156,12 +156,11 @@ export class Database extends Servi ;(model.fields[key] = Field.parse('expr')).relation = relation if (def.target) { (relmodel.fields[def.target] ??= Field.parse('expr')).relation = inverse - disposables.unshift(() => delete relmodel.fields[def.target!]) } if (relation.type === 'oneToOne' || relation.type === 'manyToOne') { relation.fields.forEach((x, i) => { - if (!model.fields[x]) disposables.unshift(() => delete model.fields[x]) + if (!(x in model.fields)) disposables.unshift(() => delete model.fields[x]) model.fields[x] ??= { ...relmodel.fields[relation.references[i]] } as any if (!relation.required) { model.fields[x]!.nullable = true @@ -197,25 +196,15 @@ export class Database extends Servi if (Array.isArray(model.primary) || model.fields[model.primary]!.relation) { model.primary = deduplicate(makeArray(model.primary).map(key => model.fields[key]!.relation?.fields || key).flat()) } - model.unique = model.unique.map(keys => { - if (typeof keys === 'string') { - return (model.fields[keys]!.relation?.fields && disposables.unshift(() => model.unique.splice(model.unique.indexOf(keys), 1)), - model.fields[keys]!.relation?.fields || keys) - } else { - const pred = keys.some(key => model.fields[key]!.relation?.fields) - if (pred) { - const newKeys = keys.map(key => model.fields[key]!.relation?.fields || key).flat() - disposables.unshift(() => model.unique.splice(model.unique.indexOf(newKeys), 1)) - return newKeys - } else { - return keys - } - } - }) + model.unique = model.unique.map(keys => typeof keys === 'string' ? model.fields[keys]!.relation?.fields || keys + : keys.map(key => model.fields[key]!.relation?.fields || key).flat()) this.prepareTasks[name] = this.prepare(name) ;(this.ctx as Context).emit('model', name) - return this.ctx.effect(() => () => disposables.splice(0).forEach(dispose => dispose())) + return this.ctx.effect(() => () => { + disposables.splice(0).forEach(dispose => dispose()) + ;(this.ctx as Context).emit('model', name) + }) } private _parseField(field: any, transformers: Driver.Transformer[] = [], setInitial?: (value) => void, setField?: (value) => void): Type { @@ -321,7 +310,7 @@ export class Database extends Servi fields: Field.Extension, callback: Model.Migration, ) { - this.extend(name, fields, { callback }) + return this.extend(name, fields, { callback }) } select(table: Selection, query?: Query): Selection diff --git a/packages/core/src/model.ts b/packages/core/src/model.ts index d9af09c..359ca45 100644 --- a/packages/core/src/model.ts +++ b/packages/core/src/model.ts @@ -274,23 +274,18 @@ export class Model { extend(fields: Field.Extension, config?: Partial): () => void extend(fields = {}, config: Partial = {}) { - const { primary, autoInc, unique = [], indexes = [], foreign = {}, callback } = config - const disposables: (() => void)[] = [] + const { primary, autoInc, unique = [], indexes = [], foreign, callback } = config this.primary = primary || this.primary this.autoInc = autoInc || this.autoInc - unique.forEach(key => this.unique.includes(key) || (this.unique.push(key), disposables.push(() => this.unique.splice(this.unique.indexOf(key), 1)))) - indexes.map(x => this.parseIndex(x)).forEach(index => (this.indexes.some(ind => deepEqual(ind, index))) - || (this.indexes.push(index), disposables.push(() => this.indexes.splice(this.indexes.indexOf(index), 1)))) - Object.keys(foreign).forEach(key => Object.hasOwn(this.foreign, key) - || (this.foreign[key] = foreign[key], disposables.push(() => delete this.foreign[key]))) + unique.forEach(key => this.unique.includes(key) || this.unique.push(key)) + indexes.map(x => this.parseIndex(x)).forEach(index => (this.indexes.some(ind => deepEqual(ind, index))) || this.indexes.push(index)) + Object.assign(this.foreign, foreign) if (callback) this.migrations.set(callback, Object.keys(fields)) for (const key in fields) { - if (makeArray(this.primary).includes(key)) { - disposables.push(() => delete this.ctx?.get('model')?.tables[this.name]) - } + if (Object.keys(this.fields).includes(key)) throw new TypeError(`field "${key}" already exists in table "${this.name}"`) this.fields[key] = Field.parse(fields[key]) this.fields[key].deprecated = !!callback } @@ -304,12 +299,10 @@ export class Model { this.unique.forEach(index => this.checkIndex(index)) this.indexes.forEach(index => this.checkIndex(index)) - return () => { - for (const key in fields) { - delete this.fields[key] - } - if (callback) this.migrations.delete(callback) - disposables.splice(0).forEach(dispose => dispose()) + if (Object.keys(fields).some(key => makeArray(this.primary).includes(key))) { + return () => delete this.ctx?.get('model')?.tables[this.name] + } else { + return () => Object.keys(fields).forEach(key => delete this.fields[key]) } } diff --git a/packages/tests/src/index.ts b/packages/tests/src/index.ts index 6b183ec..e745cf2 100644 --- a/packages/tests/src/index.ts +++ b/packages/tests/src/index.ts @@ -20,7 +20,7 @@ type UnitOptions = (T extends (database: Database, options?: infer R) => any [K in keyof T as Exclude]?: false | UnitOptions } -type Unit = ((database: Database | ((arg: T) => Database), options: UnitOptions, fork?: boolean) => void) & { +type Unit = ((database: Database, options?: UnitOptions) => void) & { [K in keyof T as Exclude]: Unit } diff --git a/packages/tests/src/json.ts b/packages/tests/src/json.ts index a22cfa8..dbfedc6 100644 --- a/packages/tests/src/json.ts +++ b/packages/tests/src/json.ts @@ -38,7 +38,6 @@ interface Tables { function JsonTests(database: Database) { before(async () => { - console.log(Object.keys(database.tables)) database.extend('foo', { id: 'unsigned', value: 'integer', diff --git a/packages/tests/src/migration.ts b/packages/tests/src/migration.ts index 80bfd67..0109bdb 100644 --- a/packages/tests/src/migration.ts +++ b/packages/tests/src/migration.ts @@ -45,8 +45,6 @@ function MigrationTests(database: Database) { ]) database.extend('qux', { - id: 'unsigned', - text: 'string(64)', number: 'unsigned', }) @@ -73,6 +71,46 @@ function MigrationTests(database: Database) { ]) }) + it('alter disposable field', async () => { + Reflect.deleteProperty(database.tables, 'qux') + + database.extend('qux', { + id: 'unsigned', + text: 'string(64)', + }) + + await database.upsert('qux', [ + { id: 1, text: 'foo' }, + { id: 2, text: 'bar' }, + ]) + + await expect(database.get('qux', {})).to.eventually.deep.equal([ + { id: 1, text: 'foo' }, + { id: 2, text: 'bar' }, + ]) + + const dispose = database.extend('qux', { + number: 'unsigned', + }) + + await database.upsert('qux', [ + { id: 1, text: 'foo', number: 100 }, + { id: 2, text: 'bar', number: 200 }, + ]) + + await expect(database.get('qux', {})).to.eventually.deep.equal([ + { id: 1, text: 'foo', number: 100 }, + { id: 2, text: 'bar', number: 200 }, + ]) + + dispose() + + await expect(database.get('qux', {})).to.eventually.deep.equal([ + { id: 1, text: 'foo' }, + { id: 2, text: 'bar' }, + ]) + }) + it('should migrate field', async () => { Reflect.deleteProperty(database.tables, 'qux') @@ -174,10 +212,7 @@ function MigrationTests(database: Database) { { id: 2, number: 2 }, ]) - database.extend('qux', { - id: 'unsigned', - number: 'unsigned', - }, { + database.extend('qux', {}, { indexes: ['number'], })