feat(core): adjust orm api (#7392)
Removed the `connect` and `disconnect` functions on the orm `DBAdapter`, making the ORM completely non-asynchronous.
This commit is contained in:
@@ -1,4 +0,0 @@
|
|||||||
import { createORMClientType } from '../core';
|
|
||||||
import { AFFiNE_DB_SCHEMA } from './schema';
|
|
||||||
|
|
||||||
export const ORMClient = createORMClientType(AFFiNE_DB_SCHEMA);
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import { ORMClient } from './client';
|
|
||||||
|
|
||||||
// The ORM hooks are used to define the transformers that will be applied on entities when they are loaded from the data providers.
|
|
||||||
// All transformers are doing in memory, none of the data under the hood will be changed.
|
|
||||||
//
|
|
||||||
// for example:
|
|
||||||
// data in providers: { color: 'red' }
|
|
||||||
// hook: { color: 'red' } => { color: '#FF0000' }
|
|
||||||
//
|
|
||||||
// ORMClient.defineHook(
|
|
||||||
// 'demo',
|
|
||||||
// 'deprecate color field and introduce colors filed',
|
|
||||||
// {
|
|
||||||
// deserialize(tag) {
|
|
||||||
// tag.color = stringToHex(tag.color)
|
|
||||||
// return tag;
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
|
|
||||||
export { ORMClient };
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import './hooks';
|
|
||||||
|
|
||||||
export { ORMClient } from './client';
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import type { DBSchemaBuilder } from '../core';
|
|
||||||
// import { f } from './core';
|
|
||||||
|
|
||||||
export const AFFiNE_DB_SCHEMA = {
|
|
||||||
// demo: {
|
|
||||||
// id: f.string().primaryKey().optional().default(nanoid),
|
|
||||||
// name: f.string(),
|
|
||||||
// // v1
|
|
||||||
// // color: f.string(),
|
|
||||||
// // v2, without data level breaking change
|
|
||||||
// /**
|
|
||||||
// * @deprecated use [colors]
|
|
||||||
// */
|
|
||||||
// color: f.string().optional(), // <= mark as optional since new created record might only have [colors] field
|
|
||||||
// colors: f.json<string[]>().optional(), // <= mark as optional since old records might only have [color] field
|
|
||||||
// },
|
|
||||||
} as const satisfies DBSchemaBuilder;
|
|
||||||
@@ -1,18 +1,12 @@
|
|||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import {
|
import { beforeEach, describe, expect, test as t, type TestAPI } from 'vitest';
|
||||||
afterEach,
|
|
||||||
beforeEach,
|
|
||||||
describe,
|
|
||||||
expect,
|
|
||||||
test as t,
|
|
||||||
type TestAPI,
|
|
||||||
} from 'vitest';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createORMClientType,
|
createORMClient,
|
||||||
type DBSchemaBuilder,
|
type DBSchemaBuilder,
|
||||||
f,
|
f,
|
||||||
MemoryORMAdapter,
|
MemoryORMAdapter,
|
||||||
|
type ORMClient,
|
||||||
Table,
|
Table,
|
||||||
} from '../';
|
} from '../';
|
||||||
|
|
||||||
@@ -24,18 +18,12 @@ const TEST_SCHEMA = {
|
|||||||
},
|
},
|
||||||
} satisfies DBSchemaBuilder;
|
} satisfies DBSchemaBuilder;
|
||||||
|
|
||||||
const Client = createORMClientType(TEST_SCHEMA);
|
|
||||||
type Context = {
|
type Context = {
|
||||||
client: InstanceType<typeof Client>;
|
client: ORMClient<typeof TEST_SCHEMA>;
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach<Context>(async t => {
|
beforeEach<Context>(async t => {
|
||||||
t.client = new Client(new MemoryORMAdapter());
|
t.client = createORMClient(TEST_SCHEMA, MemoryORMAdapter);
|
||||||
await t.client.connect();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach<Context>(async t => {
|
|
||||||
await t.client.disconnect();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const test = t as TestAPI<Context>;
|
const test = t as TestAPI<Context>;
|
||||||
|
|||||||
@@ -1,19 +1,13 @@
|
|||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import {
|
import { beforeEach, describe, expect, test as t, type TestAPI } from 'vitest';
|
||||||
afterEach,
|
|
||||||
beforeEach,
|
|
||||||
describe,
|
|
||||||
expect,
|
|
||||||
test as t,
|
|
||||||
type TestAPI,
|
|
||||||
} from 'vitest';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createORMClientType,
|
createORMClient,
|
||||||
type DBSchemaBuilder,
|
type DBSchemaBuilder,
|
||||||
type Entity,
|
type Entity,
|
||||||
f,
|
f,
|
||||||
MemoryORMAdapter,
|
MemoryORMAdapter,
|
||||||
|
type ORMClient,
|
||||||
} from '../';
|
} from '../';
|
||||||
|
|
||||||
const TEST_SCHEMA = {
|
const TEST_SCHEMA = {
|
||||||
@@ -29,30 +23,23 @@ const TEST_SCHEMA = {
|
|||||||
},
|
},
|
||||||
} satisfies DBSchemaBuilder;
|
} satisfies DBSchemaBuilder;
|
||||||
|
|
||||||
const Client = createORMClientType(TEST_SCHEMA);
|
|
||||||
|
|
||||||
// define the hooks
|
|
||||||
Client.defineHook('tags', 'migrate field `color` to field `colors`', {
|
|
||||||
deserialize(data) {
|
|
||||||
if (!data.colors && data.color) {
|
|
||||||
data.colors = [data.color];
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
type Context = {
|
type Context = {
|
||||||
client: InstanceType<typeof Client>;
|
client: ORMClient<typeof TEST_SCHEMA>;
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach<Context>(async t => {
|
beforeEach<Context>(async t => {
|
||||||
t.client = new Client(new MemoryORMAdapter());
|
t.client = createORMClient(TEST_SCHEMA, MemoryORMAdapter);
|
||||||
await t.client.connect();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach<Context>(async t => {
|
// define the hooks
|
||||||
await t.client.disconnect();
|
t.client.defineHook('tags', 'migrate field `color` to field `colors`', {
|
||||||
|
deserialize(data) {
|
||||||
|
if (!data.colors && data.color) {
|
||||||
|
data.colors = [data.color];
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const test = t as TestAPI<Context>;
|
const test = t as TestAPI<Context>;
|
||||||
|
|||||||
@@ -1,12 +1,21 @@
|
|||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { describe, expect, test } from 'vitest';
|
import { describe, expect, test } from 'vitest';
|
||||||
|
|
||||||
import { createORMClientType, f, MemoryORMAdapter } from '../';
|
import {
|
||||||
|
createORMClient,
|
||||||
|
type DBSchemaBuilder,
|
||||||
|
f,
|
||||||
|
MemoryORMAdapter,
|
||||||
|
} from '../';
|
||||||
|
|
||||||
|
function createClient<Schema extends DBSchemaBuilder>(schema: Schema) {
|
||||||
|
return createORMClient(schema, MemoryORMAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
describe('Schema validations', () => {
|
describe('Schema validations', () => {
|
||||||
test('primary key must be set', () => {
|
test('primary key must be set', () => {
|
||||||
expect(() =>
|
expect(() =>
|
||||||
createORMClientType({
|
createClient({
|
||||||
tags: {
|
tags: {
|
||||||
id: f.string(),
|
id: f.string(),
|
||||||
name: f.string(),
|
name: f.string(),
|
||||||
@@ -19,7 +28,7 @@ describe('Schema validations', () => {
|
|||||||
|
|
||||||
test('primary key must be unique', () => {
|
test('primary key must be unique', () => {
|
||||||
expect(() =>
|
expect(() =>
|
||||||
createORMClientType({
|
createClient({
|
||||||
tags: {
|
tags: {
|
||||||
id: f.string().primaryKey(),
|
id: f.string().primaryKey(),
|
||||||
name: f.string().primaryKey(),
|
name: f.string().primaryKey(),
|
||||||
@@ -32,7 +41,7 @@ describe('Schema validations', () => {
|
|||||||
|
|
||||||
test('primary key should not be optional without default value', () => {
|
test('primary key should not be optional without default value', () => {
|
||||||
expect(() =>
|
expect(() =>
|
||||||
createORMClientType({
|
createClient({
|
||||||
tags: {
|
tags: {
|
||||||
id: f.string().primaryKey().optional(),
|
id: f.string().primaryKey().optional(),
|
||||||
name: f.string(),
|
name: f.string(),
|
||||||
@@ -45,7 +54,7 @@ describe('Schema validations', () => {
|
|||||||
|
|
||||||
test('primary key can be optional with default value', async () => {
|
test('primary key can be optional with default value', async () => {
|
||||||
expect(() =>
|
expect(() =>
|
||||||
createORMClientType({
|
createClient({
|
||||||
tags: {
|
tags: {
|
||||||
id: f.string().primaryKey().optional().default(nanoid),
|
id: f.string().primaryKey().optional().default(nanoid),
|
||||||
name: f.string(),
|
name: f.string(),
|
||||||
@@ -56,20 +65,18 @@ describe('Schema validations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Entity validations', () => {
|
describe('Entity validations', () => {
|
||||||
const Client = createORMClientType({
|
function createTagsClient() {
|
||||||
tags: {
|
return createClient({
|
||||||
id: f.string().primaryKey().default(nanoid),
|
tags: {
|
||||||
name: f.string(),
|
id: f.string().primaryKey().default(nanoid),
|
||||||
color: f.string(),
|
name: f.string(),
|
||||||
},
|
color: f.string(),
|
||||||
});
|
},
|
||||||
|
});
|
||||||
function createClient() {
|
|
||||||
return new Client(new MemoryORMAdapter());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test('should not update primary key', () => {
|
test('should not update primary key', () => {
|
||||||
const client = createClient();
|
const client = createTagsClient();
|
||||||
|
|
||||||
const tag = client.tags.create({
|
const tag = client.tags.create({
|
||||||
name: 'tag',
|
name: 'tag',
|
||||||
@@ -83,7 +90,7 @@ describe('Entity validations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should throw when trying to create entity with missing required field', () => {
|
test('should throw when trying to create entity with missing required field', () => {
|
||||||
const client = createClient();
|
const client = createTagsClient();
|
||||||
|
|
||||||
// @ts-expect-error test
|
// @ts-expect-error test
|
||||||
expect(() => client.tags.create({ name: 'test' })).toThrow(
|
expect(() => client.tags.create({ name: 'test' })).toThrow(
|
||||||
@@ -92,7 +99,7 @@ describe('Entity validations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should throw when trying to create entity with extra field', () => {
|
test('should throw when trying to create entity with extra field', () => {
|
||||||
const client = createClient();
|
const client = createTagsClient();
|
||||||
|
|
||||||
expect(() =>
|
expect(() =>
|
||||||
// @ts-expect-error test
|
// @ts-expect-error test
|
||||||
@@ -101,34 +108,28 @@ describe('Entity validations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should throw when trying to create entity with unexpected field type', () => {
|
test('should throw when trying to create entity with unexpected field type', () => {
|
||||||
const client = createClient();
|
const client = createTagsClient();
|
||||||
|
|
||||||
expect(() =>
|
// @ts-expect-error test
|
||||||
// @ts-expect-error test
|
expect(() => client.tags.create({ name: 'test', color: 123 })).toThrow(
|
||||||
client.tags.create({ name: 'test', color: 123 })
|
|
||||||
).toThrow(
|
|
||||||
"[Table(tags)]: Field 'color' type mismatch. Expected type 'string' but got 'number'."
|
"[Table(tags)]: Field 'color' type mismatch. Expected type 'string' but got 'number'."
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(() =>
|
// @ts-expect-error test
|
||||||
// @ts-expect-error test
|
expect(() => client.tags.create({ name: 'test', color: [123] })).toThrow(
|
||||||
client.tags.create({ name: 'test', color: [123] })
|
|
||||||
).toThrow(
|
|
||||||
"[Table(tags)]: Field 'color' type mismatch. Expected type 'string' but got 'json'"
|
"[Table(tags)]: Field 'color' type mismatch. Expected type 'string' but got 'json'"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should be able to assign `null` to json field', () => {
|
test('should be able to assign `null` to json field', () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
const Client = createORMClientType({
|
const client = createClient({
|
||||||
tags: {
|
tags: {
|
||||||
id: f.string().primaryKey().default(nanoid),
|
id: f.string().primaryKey().default(nanoid),
|
||||||
info: f.json(),
|
info: f.json(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const client = new Client(new MemoryORMAdapter());
|
|
||||||
|
|
||||||
const tag = client.tags.create({ info: null });
|
const tag = client.tags.create({ info: null });
|
||||||
|
|
||||||
expect(tag.info).toBe(null);
|
expect(tag.info).toBe(null);
|
||||||
|
|||||||
@@ -14,9 +14,10 @@ import { DocEngine } from '../../../sync';
|
|||||||
import { MiniSyncServer } from '../../../sync/doc/__tests__/utils';
|
import { MiniSyncServer } from '../../../sync/doc/__tests__/utils';
|
||||||
import { MemoryStorage } from '../../../sync/doc/storage';
|
import { MemoryStorage } from '../../../sync/doc/storage';
|
||||||
import {
|
import {
|
||||||
createORMClientType,
|
createORMClient,
|
||||||
type DBSchemaBuilder,
|
type DBSchemaBuilder,
|
||||||
f,
|
f,
|
||||||
|
type ORMClient,
|
||||||
YjsDBAdapter,
|
YjsDBAdapter,
|
||||||
} from '../';
|
} from '../';
|
||||||
|
|
||||||
@@ -29,27 +30,14 @@ const TEST_SCHEMA = {
|
|||||||
},
|
},
|
||||||
} satisfies DBSchemaBuilder;
|
} satisfies DBSchemaBuilder;
|
||||||
|
|
||||||
const Client = createORMClientType(TEST_SCHEMA);
|
|
||||||
|
|
||||||
// define the hooks
|
|
||||||
Client.defineHook('tags', 'migrate field `color` to field `colors`', {
|
|
||||||
deserialize(data) {
|
|
||||||
if (!data.colors && data.color) {
|
|
||||||
data.colors = [data.color];
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
type Context = {
|
type Context = {
|
||||||
server: MiniSyncServer;
|
server: MiniSyncServer;
|
||||||
user1: {
|
user1: {
|
||||||
client: InstanceType<typeof Client>;
|
client: ORMClient<typeof TEST_SCHEMA>;
|
||||||
engine: DocEngine;
|
engine: DocEngine;
|
||||||
};
|
};
|
||||||
user2: {
|
user2: {
|
||||||
client: InstanceType<typeof Client>;
|
client: ORMClient<typeof TEST_SCHEMA>;
|
||||||
engine: DocEngine;
|
engine: DocEngine;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -60,16 +48,25 @@ function createEngine(server: MiniSyncServer) {
|
|||||||
|
|
||||||
async function createClient(server: MiniSyncServer, clientId: number) {
|
async function createClient(server: MiniSyncServer, clientId: number) {
|
||||||
const engine = createEngine(server);
|
const engine = createEngine(server);
|
||||||
const client = new Client(
|
const client = createORMClient(TEST_SCHEMA, YjsDBAdapter, {
|
||||||
new YjsDBAdapter({
|
getDoc(guid: string) {
|
||||||
getDoc(guid: string) {
|
const doc = new Doc({ guid });
|
||||||
const doc = new Doc({ guid });
|
doc.clientID = clientId;
|
||||||
doc.clientID = clientId;
|
engine.addDoc(doc);
|
||||||
engine.addDoc(doc);
|
return doc;
|
||||||
return doc;
|
},
|
||||||
},
|
});
|
||||||
})
|
|
||||||
);
|
// define the hooks
|
||||||
|
client.defineHook('tags', 'migrate field `color` to field `colors`', {
|
||||||
|
deserialize(data) {
|
||||||
|
if (!data.colors && data.color) {
|
||||||
|
data.colors = [data.color];
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
engine,
|
engine,
|
||||||
@@ -85,14 +82,10 @@ beforeEach<Context>(async t => {
|
|||||||
t.user2 = await createClient(t.server, 2);
|
t.user2 = await createClient(t.server, 2);
|
||||||
|
|
||||||
t.user1.engine.start();
|
t.user1.engine.start();
|
||||||
await t.user1.client.connect();
|
|
||||||
t.user2.engine.start();
|
t.user2.engine.start();
|
||||||
await t.user2.client.connect();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach<Context>(async t => {
|
afterEach<Context>(async t => {
|
||||||
t.user1.client.disconnect();
|
|
||||||
t.user2.client.disconnect();
|
|
||||||
t.user1.engine.stop();
|
t.user1.engine.stop();
|
||||||
t.user2.engine.stop();
|
t.user2.engine.stop();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,20 +1,14 @@
|
|||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import {
|
import { beforeEach, describe, expect, test as t, type TestAPI } from 'vitest';
|
||||||
afterEach,
|
|
||||||
beforeEach,
|
|
||||||
describe,
|
|
||||||
expect,
|
|
||||||
test as t,
|
|
||||||
type TestAPI,
|
|
||||||
} from 'vitest';
|
|
||||||
import { Doc } from 'yjs';
|
import { Doc } from 'yjs';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createORMClientType,
|
createORMClient,
|
||||||
type DBSchemaBuilder,
|
type DBSchemaBuilder,
|
||||||
type DocProvider,
|
type DocProvider,
|
||||||
type Entity,
|
type Entity,
|
||||||
f,
|
f,
|
||||||
|
type ORMClient,
|
||||||
Table,
|
Table,
|
||||||
YjsDBAdapter,
|
YjsDBAdapter,
|
||||||
} from '../';
|
} from '../';
|
||||||
@@ -33,18 +27,12 @@ const docProvider: DocProvider = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const Client = createORMClientType(TEST_SCHEMA);
|
|
||||||
type Context = {
|
type Context = {
|
||||||
client: InstanceType<typeof Client>;
|
client: ORMClient<typeof TEST_SCHEMA>;
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach<Context>(async t => {
|
beforeEach<Context>(async t => {
|
||||||
t.client = new Client(new YjsDBAdapter(docProvider));
|
t.client = createORMClient(TEST_SCHEMA, YjsDBAdapter, docProvider);
|
||||||
await t.client.connect();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach<Context>(async t => {
|
|
||||||
await t.client.disconnect();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const test = t as TestAPI<Context>;
|
const test = t as TestAPI<Context>;
|
||||||
@@ -223,15 +211,13 @@ describe('ORM entity CRUD', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('can not use reserved keyword as field name', () => {
|
test('can not use reserved keyword as field name', () => {
|
||||||
const Client = createORMClientType({
|
const schema = {
|
||||||
tags: {
|
tags: {
|
||||||
$$KEY: f.string().primaryKey().default(nanoid),
|
$$KEY: f.string().primaryKey().default(nanoid),
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
|
|
||||||
expect(() =>
|
expect(() => createORMClient(schema, YjsDBAdapter, docProvider)).toThrow(
|
||||||
new Client(new YjsDBAdapter(docProvider)).connect()
|
|
||||||
).rejects.toThrow(
|
|
||||||
"[Table(tags)]: Field '$$KEY' is reserved keyword and can't be used"
|
"[Table(tags)]: Field '$$KEY' is reserved keyword and can't be used"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,16 +1,7 @@
|
|||||||
import type { DBSchemaBuilder } from '../../schema';
|
|
||||||
import type { DBAdapter } from '../types';
|
import type { DBAdapter } from '../types';
|
||||||
import { MemoryTableAdapter } from './table';
|
import { MemoryTableAdapter } from './table';
|
||||||
|
|
||||||
export class MemoryORMAdapter implements DBAdapter {
|
export class MemoryORMAdapter implements DBAdapter {
|
||||||
connect(_db: DBSchemaBuilder): Promise<void> {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnect(_db: DBSchemaBuilder): Promise<void> {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
table(tableName: string) {
|
table(tableName: string) {
|
||||||
return new MemoryTableAdapter(tableName);
|
return new MemoryTableAdapter(tableName);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { DBSchemaBuilder, TableSchemaBuilder } from '../schema';
|
import type { TableSchemaBuilder } from '../schema';
|
||||||
|
|
||||||
export interface Key {
|
export interface Key {
|
||||||
toString(): string;
|
toString(): string;
|
||||||
@@ -21,8 +21,5 @@ export interface TableAdapter<K extends Key = any, T = unknown> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface DBAdapter {
|
export interface DBAdapter {
|
||||||
connect(db: DBSchemaBuilder): Promise<void>;
|
|
||||||
disconnect(db: DBSchemaBuilder): Promise<void>;
|
|
||||||
|
|
||||||
table(tableName: string): TableAdapter;
|
table(tableName: string): TableAdapter;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,25 +11,16 @@ export interface DocProvider {
|
|||||||
|
|
||||||
export class YjsDBAdapter implements DBAdapter {
|
export class YjsDBAdapter implements DBAdapter {
|
||||||
tables: Map<string, TableAdapter> = new Map();
|
tables: Map<string, TableAdapter> = new Map();
|
||||||
constructor(private readonly provider: DocProvider) {}
|
constructor(
|
||||||
|
db: DBSchemaBuilder,
|
||||||
connect(db: DBSchemaBuilder): Promise<void> {
|
private readonly provider: DocProvider
|
||||||
|
) {
|
||||||
for (const [tableName, table] of Object.entries(db)) {
|
for (const [tableName, table] of Object.entries(db)) {
|
||||||
validators.validateYjsTableSchema(tableName, table);
|
validators.validateYjsTableSchema(tableName, table);
|
||||||
const doc = this.provider.getDoc(tableName);
|
const doc = this.provider.getDoc(tableName);
|
||||||
|
|
||||||
this.tables.set(tableName, new YjsTableAdapter(tableName, doc));
|
this.tables.set(tableName, new YjsTableAdapter(tableName, doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnect(_db: DBSchemaBuilder): Promise<void> {
|
|
||||||
this.tables.forEach(table => {
|
|
||||||
table.dispose();
|
|
||||||
});
|
|
||||||
this.tables.clear();
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
table(tableName: string) {
|
table(tableName: string) {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { type DBAdapter, type Hook } from './adapters';
|
import { type DBAdapter, type Hook } from './adapters';
|
||||||
import type { DBSchemaBuilder } from './schema';
|
import type { DBSchemaBuilder } from './schema';
|
||||||
import { type CreateEntityInput, Table, type TableMap } from './table';
|
import { Table, type TableMap } from './table';
|
||||||
import { validators } from './validators';
|
import { validators } from './validators';
|
||||||
|
|
||||||
export class ORMClient {
|
class RawORMClient {
|
||||||
static hooksMap: Map<string, Hook<any>[]> = new Map();
|
hooksMap: Map<string, Hook<any>[]> = new Map();
|
||||||
private readonly tables = new Map<string, Table<any>>();
|
private readonly tables = new Map<string, Table<any>>();
|
||||||
constructor(
|
constructor(
|
||||||
protected readonly db: DBSchemaBuilder,
|
protected readonly db: DBSchemaBuilder,
|
||||||
@@ -17,7 +17,7 @@ export class ORMClient {
|
|||||||
if (!table) {
|
if (!table) {
|
||||||
table = new Table(this.adapter, tableName, {
|
table = new Table(this.adapter, tableName, {
|
||||||
schema: tableSchema,
|
schema: tableSchema,
|
||||||
hooks: ORMClient.hooksMap.get(tableName),
|
hooks: this.hooksMap.get(tableName),
|
||||||
});
|
});
|
||||||
this.tables.set(tableName, table);
|
this.tables.set(tableName, table);
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ export class ORMClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static defineHook(tableName: string, _desc: string, hook: Hook<any>) {
|
defineHook(tableName: string, _desc: string, hook: Hook<any>) {
|
||||||
let hooks = this.hooksMap.get(tableName);
|
let hooks = this.hooksMap.get(tableName);
|
||||||
if (!hooks) {
|
if (!hooks) {
|
||||||
hooks = [];
|
hooks = [];
|
||||||
@@ -36,48 +36,30 @@ export class ORMClient {
|
|||||||
|
|
||||||
hooks.push(hook);
|
hooks.push(hook);
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect() {
|
|
||||||
await this.adapter.connect(this.db);
|
|
||||||
}
|
|
||||||
|
|
||||||
async disconnect() {
|
|
||||||
await this.adapter.disconnect(this.db);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createORMClientType<Schema extends DBSchemaBuilder>(
|
export function createORMClient<
|
||||||
db: Schema
|
const Schema extends DBSchemaBuilder,
|
||||||
): ORMClientWithTablesClass<Schema> {
|
AdapterConstructor extends new (...args: any[]) => DBAdapter,
|
||||||
|
AdapterConstructorParams extends
|
||||||
|
any[] = ConstructorParameters<AdapterConstructor> extends [
|
||||||
|
DBSchemaBuilder,
|
||||||
|
...infer Args,
|
||||||
|
]
|
||||||
|
? Args
|
||||||
|
: never,
|
||||||
|
>(
|
||||||
|
db: Schema,
|
||||||
|
adapter: AdapterConstructor,
|
||||||
|
...args: AdapterConstructorParams
|
||||||
|
): ORMClient<Schema> {
|
||||||
Object.entries(db).forEach(([tableName, schema]) => {
|
Object.entries(db).forEach(([tableName, schema]) => {
|
||||||
validators.validateTableSchema(tableName, schema);
|
validators.validateTableSchema(tableName, schema);
|
||||||
});
|
});
|
||||||
|
|
||||||
class ORMClientWithTables extends ORMClient {
|
return new RawORMClient(db, new adapter(db, ...args)) as TableMap<Schema> &
|
||||||
constructor(adapter: DBAdapter) {
|
RawORMClient;
|
||||||
super(db, adapter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ORMClientWithTables as {
|
|
||||||
new (
|
|
||||||
...args: ConstructorParameters<typeof ORMClientWithTables>
|
|
||||||
): ORMClient & TableMap<Schema>;
|
|
||||||
|
|
||||||
defineHook<TableName extends keyof Schema>(
|
|
||||||
tableName: TableName,
|
|
||||||
desc: string,
|
|
||||||
hook: Hook<CreateEntityInput<Schema[TableName]>>
|
|
||||||
): void;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ORMClientWithTablesClass<Schema extends DBSchemaBuilder> = {
|
export type ORMClient<Schema extends DBSchemaBuilder> = RawORMClient &
|
||||||
new (adapter: DBAdapter): TableMap<Schema> & ORMClient;
|
TableMap<Schema>;
|
||||||
|
|
||||||
defineHook<TableName extends keyof Schema>(
|
|
||||||
tableName: TableName,
|
|
||||||
desc: string,
|
|
||||||
hook: Hook<CreateEntityInput<Schema[TableName]>>
|
|
||||||
): void;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export * from './affine';
|
|
||||||
Reference in New Issue
Block a user