@1api/engine 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/LICENSE +21 -0
- package/README.md +37 -0
- package/dist/data-service.d.ts +26 -0
- package/dist/data-service.d.ts.map +1 -0
- package/dist/data-service.js +64 -0
- package/dist/data-service.js.map +1 -0
- package/dist/dynamic-sql.d.ts +21 -0
- package/dist/dynamic-sql.d.ts.map +1 -0
- package/dist/dynamic-sql.js +208 -0
- package/dist/dynamic-sql.js.map +1 -0
- package/dist/engine.d.ts +23 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +48 -0
- package/dist/engine.js.map +1 -0
- package/dist/fs-utils.d.ts +8 -0
- package/dist/fs-utils.d.ts.map +1 -0
- package/dist/fs-utils.js +14 -0
- package/dist/fs-utils.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/metadata.d.ts +30 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +129 -0
- package/dist/metadata.js.map +1 -0
- package/dist/schema-ddl.d.ts +9 -0
- package/dist/schema-ddl.d.ts.map +1 -0
- package/dist/schema-ddl.js +47 -0
- package/dist/schema-ddl.js.map +1 -0
- package/dist/schema-reader.d.ts +10 -0
- package/dist/schema-reader.d.ts.map +1 -0
- package/dist/schema-reader.js +98 -0
- package/dist/schema-reader.js.map +1 -0
- package/dist/schema-service.d.ts +25 -0
- package/dist/schema-service.d.ts.map +1 -0
- package/dist/schema-service.js +72 -0
- package/dist/schema-service.js.map +1 -0
- package/dist/system-db.d.ts +24 -0
- package/dist/system-db.d.ts.map +1 -0
- package/dist/system-db.js +16 -0
- package/dist/system-db.js.map +1 -0
- package/dist/test-helpers.d.ts +15 -0
- package/dist/test-helpers.d.ts.map +1 -0
- package/dist/test-helpers.js +37 -0
- package/dist/test-helpers.js.map +1 -0
- package/package.json +61 -0
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yourtion <yourtion@gmail.com>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @1api/engine
|
|
2
|
+
|
|
3
|
+
> one-api 纯逻辑数据引擎。元数据驱动 + 动态 SQL,不依赖任何 HTTP/传输层。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @1api/engine
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 概览
|
|
12
|
+
|
|
13
|
+
`@1api/engine` 只接收 DB 句柄,提供 `data.*` / `schema.*` 方法。HTTP 模块、MCP 模块、CLI 都是它的「前端适配器」。
|
|
14
|
+
|
|
15
|
+
## 主要能力
|
|
16
|
+
|
|
17
|
+
- **数据操作**:`list` / `get` / `create` / `update` / `delete` / `incr` / `describe`
|
|
18
|
+
- **Schema 演进**:`createModel` / `addField` / `refresh`
|
|
19
|
+
- **元数据管理**:CRUD + 内存缓存 + 同步
|
|
20
|
+
- **动态 SQL 安全**:表名/列名经 `sql.id()` 包裹,值参数化,字段白名单校验
|
|
21
|
+
|
|
22
|
+
## 使用
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { initEngine } from '@1api/engine';
|
|
26
|
+
|
|
27
|
+
const engine = await initEngine({ businessDbPath, systemDbPath });
|
|
28
|
+
const rows = await engine.data.list('users', { limit: 10 });
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## internal 选项
|
|
32
|
+
|
|
33
|
+
`internal: true`(管理通道)仅跳过 `is_published`/`is_editable` 校验,不绕过白名单等注入防护。
|
|
34
|
+
|
|
35
|
+
## License
|
|
36
|
+
|
|
37
|
+
MIT
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ListOptions, type ListResult, type TableMeta } from '@1api/shared';
|
|
2
|
+
import { DynamicSql } from './dynamic-sql.js';
|
|
3
|
+
import { MetadataStore } from './metadata.js';
|
|
4
|
+
export interface DataServiceOptions {
|
|
5
|
+
/** true = 跳过 is_published/is_editable 校验(管理通道用)。默认 false。不绕过白名单等注入防护。 */
|
|
6
|
+
internal?: boolean;
|
|
7
|
+
}
|
|
8
|
+
type Row = Record<string, unknown>;
|
|
9
|
+
export declare class DataService {
|
|
10
|
+
private readonly store;
|
|
11
|
+
private readonly dyn;
|
|
12
|
+
constructor(store: MetadataStore, dyn: DynamicSql);
|
|
13
|
+
/** 取 meta + is_published 校验(对外通道)。internal 跳过。 */
|
|
14
|
+
private resolveForRead;
|
|
15
|
+
/** 取 meta + is_editable 校验(对外写通道)。internal 跳过。 */
|
|
16
|
+
private resolveForWrite;
|
|
17
|
+
describe(table: string, opts?: DataServiceOptions): Promise<TableMeta>;
|
|
18
|
+
list(table: string, options?: ListOptions, svcOpts?: DataServiceOptions): Promise<ListResult<Row>>;
|
|
19
|
+
get(table: string, pk: unknown, opts?: DataServiceOptions): Promise<Row | undefined>;
|
|
20
|
+
create(table: string, data: Record<string, unknown>, opts?: DataServiceOptions): Promise<Row>;
|
|
21
|
+
update(table: string, pk: unknown, patch: Record<string, unknown>, opts?: DataServiceOptions): Promise<void>;
|
|
22
|
+
delete(table: string, pk: unknown, opts?: DataServiceOptions): Promise<void>;
|
|
23
|
+
incr(table: string, pk: unknown, field: string, n: number, opts?: DataServiceOptions): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=data-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-service.d.ts","sourceRoot":"","sources":["../src/data-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AACzF,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEnC,qBAAa,WAAW;IAEpB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,GAAG;gBADH,KAAK,EAAE,aAAa,EACpB,GAAG,EAAE,UAAU;IAGlC,kDAAkD;YACpC,cAAc;IAO5B,kDAAkD;YACpC,eAAe;IAMvB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,SAAS,CAAC;IAQ1E,IAAI,CACR,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,WAAgB,EACzB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAKrB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,GAAG,GAAG,SAAS,CAAC;IAKxF,MAAM,CACV,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,GAAG,CAAC;IAQT,MAAM,CACV,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,OAAO,EACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,IAAI,CAAC;IAKV,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAKhF,IAAI,CACR,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,OAAO,EACX,KAAK,EAAE,MAAM,EACb,CAAC,EAAE,MAAM,EACT,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,IAAI,CAAC;CAIjB"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Errors } from '@1api/shared';
|
|
2
|
+
import { DynamicSql } from './dynamic-sql.js';
|
|
3
|
+
import { MetadataStore } from './metadata.js';
|
|
4
|
+
export class DataService {
|
|
5
|
+
store;
|
|
6
|
+
dyn;
|
|
7
|
+
constructor(store, dyn) {
|
|
8
|
+
this.store = store;
|
|
9
|
+
this.dyn = dyn;
|
|
10
|
+
}
|
|
11
|
+
/** 取 meta + is_published 校验(对外通道)。internal 跳过。 */
|
|
12
|
+
async resolveForRead(table, opts) {
|
|
13
|
+
const meta = await this.store.getMetaByName(table);
|
|
14
|
+
if (!meta)
|
|
15
|
+
throw Errors.tableNotFound(table);
|
|
16
|
+
if (!opts.internal && !meta.isPublished)
|
|
17
|
+
throw Errors.tableNotFound(table);
|
|
18
|
+
return meta;
|
|
19
|
+
}
|
|
20
|
+
/** 取 meta + is_editable 校验(对外写通道)。internal 跳过。 */
|
|
21
|
+
async resolveForWrite(table, opts) {
|
|
22
|
+
const meta = await this.resolveForRead(table, opts);
|
|
23
|
+
if (!opts.internal && !meta.isEditable)
|
|
24
|
+
throw Errors.tableNotEditable(table);
|
|
25
|
+
return meta;
|
|
26
|
+
}
|
|
27
|
+
async describe(table, opts = {}) {
|
|
28
|
+
// describe 仅需存在性(不强制 is_published,便于管理通道预览)
|
|
29
|
+
const meta = await this.store.getMetaByName(table);
|
|
30
|
+
if (!meta)
|
|
31
|
+
throw Errors.tableNotFound(table);
|
|
32
|
+
void opts;
|
|
33
|
+
return meta;
|
|
34
|
+
}
|
|
35
|
+
async list(table, options = {}, svcOpts = {}) {
|
|
36
|
+
const meta = await this.resolveForRead(table, svcOpts);
|
|
37
|
+
return this.dyn.selectList(meta, options);
|
|
38
|
+
}
|
|
39
|
+
async get(table, pk, opts = {}) {
|
|
40
|
+
const meta = await this.resolveForRead(table, opts);
|
|
41
|
+
return this.dyn.selectOne(meta, pk);
|
|
42
|
+
}
|
|
43
|
+
async create(table, data, opts = {}) {
|
|
44
|
+
const meta = await this.resolveForWrite(table, opts);
|
|
45
|
+
const id = await this.dyn.insertRow(meta, data);
|
|
46
|
+
const row = await this.dyn.selectOne(meta, id);
|
|
47
|
+
if (!row)
|
|
48
|
+
throw Errors.internalError('创建后回查失败');
|
|
49
|
+
return row;
|
|
50
|
+
}
|
|
51
|
+
async update(table, pk, patch, opts = {}) {
|
|
52
|
+
const meta = await this.resolveForWrite(table, opts);
|
|
53
|
+
await this.dyn.updateRow(meta, pk, patch);
|
|
54
|
+
}
|
|
55
|
+
async delete(table, pk, opts = {}) {
|
|
56
|
+
const meta = await this.resolveForWrite(table, opts);
|
|
57
|
+
await this.dyn.deleteRow(meta, pk);
|
|
58
|
+
}
|
|
59
|
+
async incr(table, pk, field, n, opts = {}) {
|
|
60
|
+
const meta = await this.resolveForWrite(table, opts);
|
|
61
|
+
await this.dyn.incrementField(meta, pk, field, n);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=data-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-service.js","sourceRoot":"","sources":["../src/data-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqD,MAAM,cAAc,CAAC;AACzF,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAS9C,MAAM,OAAO,WAAW;IAEH;IACA;IAFnB,YACmB,KAAoB,EACpB,GAAe;QADf,UAAK,GAAL,KAAK,CAAe;QACpB,QAAG,GAAH,GAAG,CAAY;IAC/B,CAAC;IAEJ,kDAAkD;IAC1C,KAAK,CAAC,cAAc,CAAC,KAAa,EAAE,IAAwB;QAClE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACnD,IAAI,CAAC,IAAI;YAAE,MAAM,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,MAAM,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC3E,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kDAAkD;IAC1C,KAAK,CAAC,eAAe,CAAC,KAAa,EAAE,IAAwB;QACnE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,MAAM,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAC7E,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,KAAa,EAAE,OAA2B,EAAE;QACzD,4CAA4C;QAC5C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACnD,IAAI,CAAC,IAAI;YAAE,MAAM,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC7C,KAAK,IAAI,CAAC;QACV,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,IAAI,CACR,KAAa,EACb,UAAuB,EAAE,EACzB,UAA8B,EAAE;QAEhC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACvD,OAAO,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAAa,EAAE,EAAW,EAAE,OAA2B,EAAE;QACjE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACpD,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,MAAM,CACV,KAAa,EACb,IAA6B,EAC7B,OAA2B,EAAE;QAE7B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACrD,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAChD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC/C,IAAI,CAAC,GAAG;YAAE,MAAM,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAChD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,KAAK,CAAC,MAAM,CACV,KAAa,EACb,EAAW,EACX,KAA8B,EAC9B,OAA2B,EAAE;QAE7B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACrD,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,EAAW,EAAE,OAA2B,EAAE;QACpE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACrD,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,IAAI,CACR,KAAa,EACb,EAAW,EACX,KAAa,EACb,CAAS,EACT,OAA2B,EAAE;QAE7B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACrD,MAAM,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IACpD,CAAC;CACF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Kysely } from 'kysely';
|
|
2
|
+
import { type FieldMeta, type ListOptions, type ListResult, type TableMeta } from '@1api/shared';
|
|
3
|
+
type Row = Record<string, unknown>;
|
|
4
|
+
/** 校验字段在 meta 白名单中 */
|
|
5
|
+
export declare function assertFieldExists(meta: TableMeta, name: string): FieldMeta;
|
|
6
|
+
/** datetime 值归一为 ISO8601;失败原样返回(业务库多格式适配) */
|
|
7
|
+
export declare function normalizeDatetime(value: unknown): unknown;
|
|
8
|
+
export declare class DynamicSql {
|
|
9
|
+
private readonly businessDb;
|
|
10
|
+
constructor(businessDb: Kysely<Record<string, never>>);
|
|
11
|
+
/** SELECT 列表 + WHERE + ORDER + LIMIT/OFFSET + COUNT */
|
|
12
|
+
selectList(meta: TableMeta, opts: ListOptions): Promise<ListResult<Row>>;
|
|
13
|
+
selectOne(meta: TableMeta, pk: unknown): Promise<Row | undefined>;
|
|
14
|
+
insertRow(meta: TableMeta, data: Record<string, unknown>): Promise<unknown>;
|
|
15
|
+
updateRow(meta: TableMeta, pk: unknown, patch: Record<string, unknown>): Promise<void>;
|
|
16
|
+
deleteRow(meta: TableMeta, pk: unknown): Promise<void>;
|
|
17
|
+
incrementField(meta: TableMeta, pk: unknown, field: string, n: number): Promise<void>;
|
|
18
|
+
private buildFilterClause;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=dynamic-sql.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dynamic-sql.d.ts","sourceRoot":"","sources":["../src/dynamic-sql.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAO,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAKL,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAEtB,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAMnC,sBAAsB;AACtB,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAI1E;AAsBD,6CAA6C;AAC7C,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAmBzD;AAsBD,qBAAa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAEtE,uDAAuD;IACjD,UAAU,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAiDxE,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,GAAG,GAAG,SAAS,CAAC;IAOjE,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IA+C3E,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBtF,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAMtD,cAAc,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY3F,OAAO,CAAC,iBAAiB;CAiC1B"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { sql } from 'kysely';
|
|
2
|
+
import { DEFAULT_PAGE, DEFAULT_SIZE, Errors, MAX_SIZE, } from '@1api/shared';
|
|
3
|
+
const TIMESTAMP_CREATED = 'created_at';
|
|
4
|
+
const TIMESTAMP_UPDATED = 'updated_at';
|
|
5
|
+
/** 校验字段在 meta 白名单中 */
|
|
6
|
+
export function assertFieldExists(meta, name) {
|
|
7
|
+
const f = meta.fields.find((x) => x.name === name);
|
|
8
|
+
if (!f)
|
|
9
|
+
throw Errors.validationFail(`字段不存在: ${name}`);
|
|
10
|
+
return f;
|
|
11
|
+
}
|
|
12
|
+
function parseOrder(meta, order) {
|
|
13
|
+
if (!order)
|
|
14
|
+
return [];
|
|
15
|
+
const out = [];
|
|
16
|
+
for (const part of order.split(',')) {
|
|
17
|
+
const seg = part.trim();
|
|
18
|
+
if (!seg)
|
|
19
|
+
continue;
|
|
20
|
+
const [field, dirRaw] = seg.split(':');
|
|
21
|
+
if (!field)
|
|
22
|
+
continue;
|
|
23
|
+
assertFieldExists(meta, field); // 白名单
|
|
24
|
+
const dir = (dirRaw ?? 'asc').toLowerCase() === 'desc' ? 'DESC' : 'ASC';
|
|
25
|
+
out.push({ field, dir });
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
/** datetime 值归一为 ISO8601;失败原样返回(业务库多格式适配) */
|
|
30
|
+
export function normalizeDatetime(value) {
|
|
31
|
+
if (typeof value !== 'string' || value === '')
|
|
32
|
+
return value;
|
|
33
|
+
// 3. 纯数字(Unix 秒/毫秒启发式:>1e12 视为毫秒)
|
|
34
|
+
if (/^\d+$/.test(value)) {
|
|
35
|
+
const n = Number(value);
|
|
36
|
+
const ms = n > 1e12 ? n : n * 1000;
|
|
37
|
+
const d = new Date(ms);
|
|
38
|
+
return Number.isNaN(d.getTime()) ? value : d.toISOString();
|
|
39
|
+
}
|
|
40
|
+
// 2. SQLite 原生 'YYYY-MM-DD HH:MM:SS' → 按 UTC 拼接 Z
|
|
41
|
+
if (/^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}/.test(value)) {
|
|
42
|
+
const iso = value.replace(' ', 'T');
|
|
43
|
+
const withTz = /[zZ]|[+-]\d{2}:?\d{2}$/.test(iso) ? iso : `${iso}Z`;
|
|
44
|
+
const d = new Date(withTz);
|
|
45
|
+
return Number.isNaN(d.getTime()) ? value : d.toISOString();
|
|
46
|
+
}
|
|
47
|
+
// 1. ISO8601(含 T/Z)或 Date 可解析
|
|
48
|
+
const d = new Date(value);
|
|
49
|
+
return Number.isNaN(d.getTime()) ? value : d.toISOString();
|
|
50
|
+
}
|
|
51
|
+
/** 对行中的 datetime 字段做归一 */
|
|
52
|
+
function transformRow(meta, row) {
|
|
53
|
+
for (const f of meta.fields) {
|
|
54
|
+
if (f.jsonType === 'datetime' && row[f.name] !== undefined) {
|
|
55
|
+
row[f.name] = normalizeDatetime(row[f.name]);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return row;
|
|
59
|
+
}
|
|
60
|
+
/** 双引号包裹标识符(支持非 snake_case) */
|
|
61
|
+
function col(name) {
|
|
62
|
+
return sql.id(name);
|
|
63
|
+
}
|
|
64
|
+
/** 是否含自动时间戳字段 */
|
|
65
|
+
function hasTimestamps(meta) {
|
|
66
|
+
return meta.fields.some((f) => f.name === TIMESTAMP_CREATED || f.name === TIMESTAMP_UPDATED);
|
|
67
|
+
}
|
|
68
|
+
export class DynamicSql {
|
|
69
|
+
businessDb;
|
|
70
|
+
constructor(businessDb) {
|
|
71
|
+
this.businessDb = businessDb;
|
|
72
|
+
}
|
|
73
|
+
/** SELECT 列表 + WHERE + ORDER + LIMIT/OFFSET + COUNT */
|
|
74
|
+
async selectList(meta, opts) {
|
|
75
|
+
const page = Math.max(opts.page ?? DEFAULT_PAGE, 1);
|
|
76
|
+
const size = Math.min(Math.max(opts.size ?? DEFAULT_SIZE, 1), MAX_SIZE);
|
|
77
|
+
const offset = (page - 1) * size;
|
|
78
|
+
const table = sql.id(meta.name);
|
|
79
|
+
const whereParts = [];
|
|
80
|
+
// filters
|
|
81
|
+
if (opts.filters) {
|
|
82
|
+
for (const [name, [op, value]] of Object.entries(opts.filters)) {
|
|
83
|
+
const field = assertFieldExists(meta, name);
|
|
84
|
+
whereParts.push(this.buildFilterClause(field, op, value));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// q:所有 TEXT/非主键字段 OR LIKE
|
|
88
|
+
if (opts.q) {
|
|
89
|
+
const qFields = meta.fields.filter((f) => f.sqliteType === 'TEXT' && !f.isPrimary);
|
|
90
|
+
if (qFields.length > 0) {
|
|
91
|
+
const likes = qFields.map((f) => sql `${col(f.name)} LIKE ${'%' + opts.q + '%'}`);
|
|
92
|
+
whereParts.push(sql.join(likes, sql.raw(' OR ')));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const whereClause = whereParts.length > 0 ? sql ` WHERE ${sql.join(whereParts, sql.raw(' AND '))}` : sql ``;
|
|
96
|
+
// COUNT
|
|
97
|
+
const countSql = sql `SELECT COUNT(*) AS total FROM ${table}${whereClause}`;
|
|
98
|
+
const countRes = await countSql.execute(this.businessDb);
|
|
99
|
+
const total = Number(countRes.rows[0]?.total ?? 0);
|
|
100
|
+
// DATA
|
|
101
|
+
const orderClauses = parseOrder(meta, opts.order);
|
|
102
|
+
const orderBy = orderClauses.length > 0
|
|
103
|
+
? sql ` ORDER BY ${sql.join(orderClauses.map((o) => sql `${col(o.field)} ${sql.raw(o.dir)}`), sql.raw(', '))}`
|
|
104
|
+
: sql ``;
|
|
105
|
+
const dataSql = sql `SELECT * FROM ${table}${whereClause}${orderBy} LIMIT ${size} OFFSET ${offset}`;
|
|
106
|
+
const dataRes = await dataSql.execute(this.businessDb);
|
|
107
|
+
const rows = dataRes.rows.map((r) => transformRow(meta, { ...r }));
|
|
108
|
+
return { rows, total, page, size };
|
|
109
|
+
}
|
|
110
|
+
async selectOne(meta, pk) {
|
|
111
|
+
const res = await sql `SELECT * FROM ${sql.id(meta.name)} WHERE ${col(meta.primaryKey)} = ${pk}`
|
|
112
|
+
.execute(this.businessDb);
|
|
113
|
+
const row = res.rows[0];
|
|
114
|
+
return row ? transformRow(meta, { ...row }) : undefined;
|
|
115
|
+
}
|
|
116
|
+
async insertRow(meta, data) {
|
|
117
|
+
// 只接受 meta 白名单内、且非只读时间戳(由本层填)的字段
|
|
118
|
+
const cols = Object.keys(data).filter((k) => meta.fields.some((f) => f.name === k) &&
|
|
119
|
+
k !== TIMESTAMP_CREATED &&
|
|
120
|
+
k !== TIMESTAMP_UPDATED);
|
|
121
|
+
if (cols.length === 0)
|
|
122
|
+
throw Errors.validationFail('无可写入字段');
|
|
123
|
+
// 时间戳约定:建表含 created_at/updated_at 时由 engine 填 Date.now()
|
|
124
|
+
if (hasTimestamps(meta)) {
|
|
125
|
+
const now = Date.now();
|
|
126
|
+
if (meta.fields.some((f) => f.name === TIMESTAMP_CREATED) &&
|
|
127
|
+
data[TIMESTAMP_CREATED] === undefined) {
|
|
128
|
+
cols.push(TIMESTAMP_CREATED);
|
|
129
|
+
data[TIMESTAMP_CREATED] = now;
|
|
130
|
+
}
|
|
131
|
+
if (meta.fields.some((f) => f.name === TIMESTAMP_UPDATED) &&
|
|
132
|
+
data[TIMESTAMP_UPDATED] === undefined) {
|
|
133
|
+
cols.push(TIMESTAMP_UPDATED);
|
|
134
|
+
data[TIMESTAMP_UPDATED] = now;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const colNodes = sql.join(cols.map((c) => col(c)), sql.raw(', '));
|
|
138
|
+
const valNodes = sql.join(cols.map((c) => sql `${data[c]}`), sql.raw(', '));
|
|
139
|
+
const res = await sql `INSERT INTO ${sql.id(meta.name)} (${colNodes}) VALUES (${valNodes}) RETURNING ${col(meta.primaryKey)}`.execute(this.businessDb);
|
|
140
|
+
const inserted = res.rows[0];
|
|
141
|
+
if (inserted && inserted[meta.primaryKey] !== undefined)
|
|
142
|
+
return inserted[meta.primaryKey];
|
|
143
|
+
// 兜底:last_insert_rowid()
|
|
144
|
+
const r = await sql `SELECT last_insert_rowid() AS id`.execute(this.businessDb);
|
|
145
|
+
return r.rows[0]?.id;
|
|
146
|
+
}
|
|
147
|
+
async updateRow(meta, pk, patch) {
|
|
148
|
+
// 只读字段忽略(如 created_at/updated_at 由本层管理);updated_at 由本层刷新
|
|
149
|
+
const sets = Object.keys(patch).filter((k) => {
|
|
150
|
+
const f = meta.fields.find((x) => x.name === k);
|
|
151
|
+
return f && !f.isReadonly;
|
|
152
|
+
});
|
|
153
|
+
if (sets.length === 0 && !meta.fields.some((f) => f.name === TIMESTAMP_UPDATED))
|
|
154
|
+
return;
|
|
155
|
+
const setNodes = sets.map((c) => sql `${col(c)} = ${patch[c]}`);
|
|
156
|
+
// 刷新 updated_at(SQLite 无 ON UPDATE,由 engine 负责)
|
|
157
|
+
if (meta.fields.some((f) => f.name === TIMESTAMP_UPDATED)) {
|
|
158
|
+
setNodes.push(sql `${col(TIMESTAMP_UPDATED)} = ${Date.now()}`);
|
|
159
|
+
}
|
|
160
|
+
if (setNodes.length === 0)
|
|
161
|
+
return;
|
|
162
|
+
const setNode = sql.join(setNodes, sql.raw(', '));
|
|
163
|
+
await sql `UPDATE ${sql.id(meta.name)} SET ${setNode} WHERE ${col(meta.primaryKey)} = ${pk}`.execute(this.businessDb);
|
|
164
|
+
}
|
|
165
|
+
async deleteRow(meta, pk) {
|
|
166
|
+
await sql `DELETE FROM ${sql.id(meta.name)} WHERE ${col(meta.primaryKey)} = ${pk}`.execute(this.businessDb);
|
|
167
|
+
}
|
|
168
|
+
async incrementField(meta, pk, field, n) {
|
|
169
|
+
const f = assertFieldExists(meta, field);
|
|
170
|
+
if (f.jsonType !== 'integer' && f.jsonType !== 'number') {
|
|
171
|
+
throw Errors.validationFail(`字段 ${field} 非数值类型,不可自增`);
|
|
172
|
+
}
|
|
173
|
+
await sql `UPDATE ${sql.id(meta.name)} SET ${col(field)} = ${col(field)} + ${n} WHERE ${col(meta.primaryKey)} = ${pk}`.execute(this.businessDb);
|
|
174
|
+
}
|
|
175
|
+
// —— 内部:过滤子句 ——
|
|
176
|
+
buildFilterClause(field, op, value) {
|
|
177
|
+
const name = col(field.name);
|
|
178
|
+
switch (op) {
|
|
179
|
+
case 'eq':
|
|
180
|
+
return sql `${name} = ${value}`;
|
|
181
|
+
case 'ne':
|
|
182
|
+
return sql `${name} <> ${value}`;
|
|
183
|
+
case 'gt':
|
|
184
|
+
return sql `${name} > ${value}`;
|
|
185
|
+
case 'gte':
|
|
186
|
+
return sql `${name} >= ${value}`;
|
|
187
|
+
case 'lt':
|
|
188
|
+
return sql `${name} < ${value}`;
|
|
189
|
+
case 'lte':
|
|
190
|
+
return sql `${name} <= ${value}`;
|
|
191
|
+
case 'like':
|
|
192
|
+
return sql `${name} LIKE ${value}`;
|
|
193
|
+
case 'in': {
|
|
194
|
+
const arr = String(value)
|
|
195
|
+
.split(',')
|
|
196
|
+
.map((s) => s.trim())
|
|
197
|
+
.filter((s) => s.length > 0);
|
|
198
|
+
if (arr.length === 0)
|
|
199
|
+
throw Errors.validationFail('in 操作符值非法');
|
|
200
|
+
const nodes = sql.join(arr.map((a) => sql `${a}`), sql.raw(', '));
|
|
201
|
+
return sql `${name} IN (${nodes})`;
|
|
202
|
+
}
|
|
203
|
+
default:
|
|
204
|
+
throw Errors.validationFail(`不支持的过滤操作符: ${op}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=dynamic-sql.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dynamic-sql.js","sourceRoot":"","sources":["../src/dynamic-sql.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import { Kysely } from 'kysely';
|
|
3
|
+
import { DataService } from './data-service.js';
|
|
4
|
+
import { MetadataStore } from './metadata.js';
|
|
5
|
+
import { SchemaService } from './schema-service.js';
|
|
6
|
+
import { type SystemDB } from './system-db.js';
|
|
7
|
+
export interface CreateEngineOptions {
|
|
8
|
+
businessDbPath: string;
|
|
9
|
+
systemDbPath: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class DataEngine {
|
|
12
|
+
readonly data: DataService;
|
|
13
|
+
readonly schema: SchemaService;
|
|
14
|
+
readonly metadata: MetadataStore;
|
|
15
|
+
/** 暴露原生业务库句柄(迁移/测试建表用) */
|
|
16
|
+
readonly businessRaw: Database.Database;
|
|
17
|
+
constructor(businessRaw: Database.Database, businessDb: Kysely<Record<string, never>>, systemDb: Kysely<SystemDB>);
|
|
18
|
+
}
|
|
19
|
+
/** 工厂:创建连接 + 系统库迁移 + 缓存预热 + 返回 DataEngine(带 destroy) */
|
|
20
|
+
export declare function createEngine(opts: CreateEngineOptions): Promise<DataEngine & {
|
|
21
|
+
destroy(): Promise<void>;
|
|
22
|
+
}>;
|
|
23
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,MAAM,EAAiB,MAAM,QAAQ,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAwB,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAGrE,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,UAAU;IACrB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,0BAA0B;IAC1B,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,QAAQ,CAAC;gBAGtC,WAAW,EAAE,QAAQ,CAAC,QAAQ,EAC9B,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,EACzC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;CAQ7B;AAED,wDAAwD;AACxD,wBAAsB,YAAY,CAChC,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,UAAU,GAAG;IAAE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CA0BpD"}
|
package/dist/engine.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import { Kysely, SqliteDialect } from 'kysely';
|
|
3
|
+
import { DataService } from './data-service.js';
|
|
4
|
+
import { DynamicSql } from './dynamic-sql.js';
|
|
5
|
+
import { MetadataStore } from './metadata.js';
|
|
6
|
+
import { SchemaService } from './schema-service.js';
|
|
7
|
+
import { SYSTEM_DB_MIGRATIONS } from './system-db.js';
|
|
8
|
+
import { ensureDbDir } from './fs-utils.js';
|
|
9
|
+
export class DataEngine {
|
|
10
|
+
data;
|
|
11
|
+
schema;
|
|
12
|
+
metadata;
|
|
13
|
+
/** 暴露原生业务库句柄(迁移/测试建表用) */
|
|
14
|
+
businessRaw;
|
|
15
|
+
constructor(businessRaw, businessDb, systemDb) {
|
|
16
|
+
this.businessRaw = businessRaw;
|
|
17
|
+
this.metadata = new MetadataStore(systemDb, businessRaw);
|
|
18
|
+
const dyn = new DynamicSql(businessDb);
|
|
19
|
+
this.data = new DataService(this.metadata, dyn);
|
|
20
|
+
this.schema = new SchemaService(businessRaw, this.metadata);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** 工厂:创建连接 + 系统库迁移 + 缓存预热 + 返回 DataEngine(带 destroy) */
|
|
24
|
+
export async function createEngine(opts) {
|
|
25
|
+
ensureDbDir(opts.businessDbPath);
|
|
26
|
+
ensureDbDir(opts.systemDbPath);
|
|
27
|
+
const businessRaw = new Database(opts.businessDbPath);
|
|
28
|
+
businessRaw.pragma('journal_mode = WAL');
|
|
29
|
+
const systemRaw = new Database(opts.systemDbPath);
|
|
30
|
+
// 系统库迁移(Phase 1 仅 table_meta)
|
|
31
|
+
for (const m of SYSTEM_DB_MIGRATIONS)
|
|
32
|
+
systemRaw.exec(m);
|
|
33
|
+
const businessDb = new Kysely({
|
|
34
|
+
dialect: new SqliteDialect({ database: businessRaw }),
|
|
35
|
+
});
|
|
36
|
+
const systemDb = new Kysely({
|
|
37
|
+
dialect: new SqliteDialect({ database: systemRaw }),
|
|
38
|
+
});
|
|
39
|
+
const engine = new DataEngine(businessRaw, businessDb, systemDb);
|
|
40
|
+
await engine.metadata.warmup(); // 缓存预热
|
|
41
|
+
return Object.assign(engine, {
|
|
42
|
+
async destroy() {
|
|
43
|
+
await businessDb.destroy();
|
|
44
|
+
await systemDb.destroy();
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAiB,MAAM,gBAAgB,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAO5C,MAAM,OAAO,UAAU;IACZ,IAAI,CAAc;IAClB,MAAM,CAAgB;IACtB,QAAQ,CAAgB;IACjC,0BAA0B;IACjB,WAAW,CAAoB;IAExC,YACE,WAA8B,EAC9B,UAAyC,EACzC,QAA0B;QAE1B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,QAAQ,GAAG,IAAI,aAAa,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QACzD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAChD,IAAI,CAAC,MAAM,GAAG,IAAI,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9D,CAAC;CACF;AAED,wDAAwD;AACxD,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,IAAyB;IAEzB,WAAW,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACjC,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC/B,MAAM,WAAW,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACtD,WAAW,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IACzC,MAAM,SAAS,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAElD,8BAA8B;IAC9B,KAAK,MAAM,CAAC,IAAI,oBAAoB;QAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAExD,MAAM,UAAU,GAAG,IAAI,MAAM,CAAwB;QACnD,OAAO,EAAE,IAAI,aAAa,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;KACtD,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAW;QACpC,OAAO,EAAE,IAAI,aAAa,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;KACpD,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,WAAW,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;IACjE,MAAM,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO;IAEvC,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE;QAC3B,KAAK,CAAC,OAAO;YACX,MAAM,UAAU,CAAC,OAAO,EAAE,CAAC;YAC3B,MAAM,QAAQ,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 确保数据库文件所在目录存在(递归创建)。
|
|
3
|
+
* better-sqlite3 打开不存在的目录下的 db 路径会抛
|
|
4
|
+
* "Cannot open database because the directory does not exist",
|
|
5
|
+
* 故在 new Database(path) 前调用本函数兜底。
|
|
6
|
+
*/
|
|
7
|
+
export declare function ensureDbDir(path: string): void;
|
|
8
|
+
//# sourceMappingURL=fs-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs-utils.d.ts","sourceRoot":"","sources":["../src/fs-utils.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAG9C"}
|
package/dist/fs-utils.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { mkdirSync } from 'node:fs';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* 确保数据库文件所在目录存在(递归创建)。
|
|
5
|
+
* better-sqlite3 打开不存在的目录下的 db 路径会抛
|
|
6
|
+
* "Cannot open database because the directory does not exist",
|
|
7
|
+
* 故在 new Database(path) 前调用本函数兜底。
|
|
8
|
+
*/
|
|
9
|
+
export function ensureDbDir(path) {
|
|
10
|
+
const dir = dirname(path);
|
|
11
|
+
if (dir)
|
|
12
|
+
mkdirSync(dir, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=fs-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs-utils.js","sourceRoot":"","sources":["../src/fs-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,IAAI,GAAG;QAAE,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC/C,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { DataEngine, createEngine } from './engine.js';
|
|
2
|
+
export type { CreateEngineOptions } from './engine.js';
|
|
3
|
+
export { SchemaService } from './schema-service.js';
|
|
4
|
+
export { DataService } from './data-service.js';
|
|
5
|
+
export type { DataServiceOptions } from './data-service.js';
|
|
6
|
+
export { MetadataStore } from './metadata.js';
|
|
7
|
+
export type { CreateMetaInput } from './metadata.js';
|
|
8
|
+
export { DynamicSql, assertFieldExists, normalizeDatetime } from './dynamic-sql.js';
|
|
9
|
+
export { readTableSchema, listTables } from './schema-reader.js';
|
|
10
|
+
export { createTable, addColumn, inputToField } from './schema-ddl.js';
|
|
11
|
+
export type { SystemDB, TableMetaRow } from './system-db.js';
|
|
12
|
+
export { SYSTEM_DB_MIGRATIONS } from './system-db.js';
|
|
13
|
+
export { ensureDbDir } from './fs-utils.js';
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACvD,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACpF,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACvE,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { DataEngine, createEngine } from './engine.js';
|
|
2
|
+
export { SchemaService } from './schema-service.js';
|
|
3
|
+
export { DataService } from './data-service.js';
|
|
4
|
+
export { MetadataStore } from './metadata.js';
|
|
5
|
+
export { DynamicSql, assertFieldExists, normalizeDatetime } from './dynamic-sql.js';
|
|
6
|
+
export { readTableSchema, listTables } from './schema-reader.js';
|
|
7
|
+
export { createTable, addColumn, inputToField } from './schema-ddl.js';
|
|
8
|
+
export { SYSTEM_DB_MIGRATIONS } from './system-db.js';
|
|
9
|
+
export { ensureDbDir } from './fs-utils.js';
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEvD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACpF,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
import { type Kysely } from 'kysely';
|
|
3
|
+
import { type FieldMeta, type TableMeta } from '@1api/shared';
|
|
4
|
+
import type { SystemDB } from './system-db.js';
|
|
5
|
+
export interface CreateMetaInput {
|
|
6
|
+
name: string;
|
|
7
|
+
label: string;
|
|
8
|
+
primaryKey: string;
|
|
9
|
+
fields: FieldMeta[];
|
|
10
|
+
note?: string | null;
|
|
11
|
+
isPublished?: boolean;
|
|
12
|
+
isEditable?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare class MetadataStore {
|
|
15
|
+
private readonly systemDb;
|
|
16
|
+
private readonly businessRaw;
|
|
17
|
+
private cache;
|
|
18
|
+
constructor(systemDb: Kysely<SystemDB>, businessRaw: Database.Database);
|
|
19
|
+
/** 预热:启动时加载全部 table_meta 到缓存 */
|
|
20
|
+
warmup(): Promise<void>;
|
|
21
|
+
listMetas(): Promise<TableMeta[]>;
|
|
22
|
+
getMeta(id: number): Promise<TableMeta | undefined>;
|
|
23
|
+
getMetaByName(name: string): Promise<TableMeta | undefined>;
|
|
24
|
+
createMeta(input: CreateMetaInput): Promise<TableMeta>;
|
|
25
|
+
updateMeta(id: number, patch: Partial<Pick<TableMeta, 'label' | 'note' | 'isPublished' | 'isEditable' | 'fields' | 'primaryKey'>>): Promise<void>;
|
|
26
|
+
deleteMeta(id: number): Promise<void>;
|
|
27
|
+
/** 重新反射业务库 → diff → 保留用户标记(label/isHidden/isReadonly/enumValues)→ 更新 fields */
|
|
28
|
+
refreshMeta(id: number): Promise<TableMeta>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AA8BD,qBAAa,aAAa;IAItB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAJ9B,OAAO,CAAC,KAAK,CAAgC;gBAG1B,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,EAC1B,WAAW,EAAE,QAAQ,CAAC,QAAQ;IAGjD,gCAAgC;IAC1B,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IASvB,SAAS,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAIjC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAKnD,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAI3D,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC;IAsBtD,UAAU,CACd,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,OAAO,CACZ,IAAI,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,aAAa,GAAG,YAAY,GAAG,QAAQ,GAAG,YAAY,CAAC,CAC3F,GACA,OAAO,CAAC,IAAI,CAAC;IAsBV,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM3C,+EAA+E;IACzE,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;CA0BlD"}
|