nodejs-store 1.0.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/src/schema.js ADDED
@@ -0,0 +1,106 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Schema 管理 — 薄适配层
5
+ *
6
+ * 职责(其余全部在 Rust core):
7
+ * 1. 把 JS schema 定义同步注册到 Rust core Registry(fn/asyncFn 以占位声明传递);
8
+ * 2. 同步 `fn` 计算列回调(core 经 FnRegistry 跨 FFI 回调);
9
+ * 3. 保留 asyncFn 原生函数映射(闭包无法跨 FFI,由 Host 在读路径尾处理执行);
10
+ * 4. 保留 Host 必需的元数据镜像(collection / idPrefix / indexes / relations),
11
+ * 供 ID 生成与索引创建使用。
12
+ */
13
+
14
+ const native = require('./core');
15
+
16
+ /** Rust core 注册表(全项目共享单例) */
17
+ const core = new native.Registry();
18
+
19
+ // Host 侧元数据镜像
20
+ const _schemas = Object.create(null);
21
+
22
+ // asyncFn 计算列回调映射(fnRef → 原生异步函数)
23
+ const _asyncFns = Object.create(null);
24
+
25
+ /** 生成可跨 FFI 的 schema 定义:fn/asyncFn → true 占位;函数型值剔除 */
26
+ function _toCoreDefn(defn) {
27
+ return JSON.parse(JSON.stringify(defn, (key, value) => {
28
+ if (typeof value === 'function') {
29
+ // fn/asyncFn 声明占位(core 按 `fn: true` 识别);函数型 default 无法跨 FFI,剔除
30
+ return key === 'fn' || key === 'asyncFn' ? true : undefined;
31
+ }
32
+ return value;
33
+ }));
34
+ }
35
+
36
+ /** 注册一个 schema(自动派生 `<Name>Deleted` 归档表镜像),返回 Host 侧元数据 */
37
+ function register(defn) {
38
+ core.register(_toCoreDefn(defn));
39
+
40
+ // 计算列回调:fn → core 回调桥;asyncFn → Host 侧映射
41
+ const computes = {};
42
+ for (const [key, val] of Object.entries(defn.computes || {})) {
43
+ const fnRef = val.fnRef || key;
44
+ if (val.fn) core.setFn(fnRef, val.fn);
45
+ if (val.asyncFn) _asyncFns[fnRef] = val.asyncFn;
46
+ computes[key] = { fnRef };
47
+ }
48
+
49
+ _schemas[defn.name] = {
50
+ name: defn.name,
51
+ collection: defn.collection || defn.name,
52
+ namespace: defn.namespace || null,
53
+ idPrefix: defn.idPrefix || '',
54
+ timestamps: defn.timestamps !== false,
55
+ fields: defn.fields || {},
56
+ relations: defn.relations || {},
57
+ computes,
58
+ indexes: defn.indexes || [],
59
+ datasource: defn.datasource || null,
60
+ read: defn.read,
61
+ write: defn.write,
62
+ };
63
+
64
+ // 归档表镜像(与 core register 的自动派生保持一致,供 Host 查询元数据)
65
+ if (!defn._isArchive && !defn.name.endsWith('Deleted')) {
66
+ register({
67
+ name: `${defn.name}Deleted`,
68
+ collection: `${defn.collection || defn.name}_deleted`,
69
+ idPrefix: '',
70
+ _isArchive: true,
71
+ fields: { ...(defn.fields || {}), deletedAt: { type: 'number' } },
72
+ indexes: defn.indexes || [],
73
+ // 归档表与原表同 (source, namespace)
74
+ datasource: defn.datasource || null,
75
+ namespace: defn.namespace || null,
76
+ });
77
+ }
78
+
79
+ return _schemas[defn.name];
80
+ }
81
+
82
+ /** 按名称获取 Host 侧元数据 */
83
+ function get(name) {
84
+ const s = _schemas[name];
85
+ if (!s) {
86
+ throw new Error(`Schema 未注册: ${name}`);
87
+ }
88
+ return s;
89
+ }
90
+
91
+ /** 检查 schema 是否已注册(core 侧判定,含归档表) */
92
+ function has(name) {
93
+ return core.has(name);
94
+ }
95
+
96
+ /** 所有已注册 schema 名称(core 侧,含归档表,按注册顺序) */
97
+ function list() {
98
+ return core.list();
99
+ }
100
+
101
+ /** 取 asyncFn 计算列实现(fnRef 缺省 = 计算列 key 名) */
102
+ function getAsyncFn(fnRef) {
103
+ return _asyncFns[fnRef];
104
+ }
105
+
106
+ module.exports = { core, register, get, has, list, getAsyncFn };
package/src/sync.js ADDED
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * schema 同步:introspect → core.schemaFromRows → core.mergeSchema(overlay) → register
5
+ *
6
+ * 纯编排(无 SQL 拼装、无 schema 推断):Host 只负责「取物理结构行」与「注册结果」,
7
+ * 映射与合并全在 core(铁律 1/6)。SQL 后端只 pull 结构,**绝不写 DDL 回库**。
8
+ */
9
+
10
+ const { core: _core, register } = require('./schema');
11
+ const introspect = require('./introspect');
12
+
13
+ /**
14
+ * 同步一个数据源的物理结构到 Registry。
15
+ *
16
+ * @param {object} opts
17
+ * @param {'mysql'|'postgres'|'sqlite'} opts.backend
18
+ * @param {object} opts.driver 驱动实例(建议只读账号)
19
+ * @param {object} [opts.introspectOptions] 透传给 introspection(如 PG 的 `schema`)
20
+ * @param {Array} [opts.overlay] 本地 overlay schemaJSON(权限/计算列/覆盖)
21
+ * @param {string} [opts.datasource] 绑定到该 schema 的数据源名(写入每个 def)
22
+ * @param {string} [opts.namespace] 连接内的库/schema 名(写入每个 def;缺省 = 连接默认)
23
+ * @param {boolean} [opts.registerDefs=true] 是否直接注册(false 时仅返回 defs)
24
+ * @returns {Promise<Array>} 合并后的 schemaJSON 数组
25
+ */
26
+ async function syncSchema({
27
+ backend,
28
+ driver,
29
+ introspectOptions,
30
+ overlay = [],
31
+ datasource = null,
32
+ namespace = null,
33
+ registerDefs = true,
34
+ }) {
35
+ const rows = await introspect.run(backend, driver, introspectOptions);
36
+ let defs = _core.schemaFromRows(rows, backend);
37
+ if (overlay && overlay.length) defs = _core.mergeSchema(defs, overlay);
38
+ if (datasource) defs = defs.map((d) => ({ ...d, datasource }));
39
+ if (namespace) defs = defs.map((d) => ({ ...d, namespace }));
40
+ if (registerDefs) for (const d of defs) register(d);
41
+ return defs;
42
+ }
43
+
44
+ module.exports = { syncSchema };