pure-js-db 2.53.0 → 2.53.1

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.
@@ -53,6 +53,7 @@
53
53
  import { Database, Table, ColumnDef } from './database.js';
54
54
  import { MemoryKVAdapter } from '../storage/kv-adapter.js';
55
55
  import { computeCRC32, assertSafeSeq } from '../utils/wal-common.js';
56
+ import crypto from 'node:crypto';
56
57
 
57
58
  const WAL_KEY_PREFIX = 'db:wal';
58
59
  const CP_KEY_PREFIX = 'db:checkpoint';
@@ -98,6 +99,18 @@ function hexToBytes(hex) {
98
99
  return bytes;
99
100
  }
100
101
 
102
+ /**
103
+ * 计算十六进制字符串的 HMAC-SHA256(2026-10-31 渗透测试 M-03 修复)
104
+ * @param {string} valueHex - 待认证的十六进制内容
105
+ * @param {string} key - HMAC 密钥
106
+ * @returns {string} 十六进制 HMAC
107
+ */
108
+ function hmacHex(valueHex, key) {
109
+ return crypto.createHmac('sha256', Buffer.from(key, 'utf8'))
110
+ .update(valueHex, 'utf8')
111
+ .digest('hex');
112
+ }
113
+
101
114
  // ═══════════════════════════════════════════
102
115
  // 二进制 WAL 批次格式
103
116
  // ═══════════════════════════════════════════
@@ -143,9 +156,10 @@ const OP_TO_NAME = {
143
156
  /**
144
157
  * 将 WAL 条目数组序列化为二进制批次
145
158
  * @param {Object[]} entries
146
- * @returns {string} 十六进制字符串
159
+ * @param {string} [hmacKey] - 可选 HMAC 密钥;设置后返回 `${hex}.${hmac}` 后缀
160
+ * @returns {string} 十六进制字符串(配置 hmacKey 时含 `.hmac` 后缀)
147
161
  */
148
- function serializeBatch(entries) {
162
+ function serializeBatch(entries, hmacKey) {
149
163
  // P1-F 修复 v1.34.2:批次条目数 >65535 时旧格式(count 2B)会静默截断
150
164
  // 抛出错误而非截断,让调用方感知 bug(groupCommitMaxEntries 默认 64,不应触发)
151
165
  // 注意:上层 _flushBatch 已按 MAX_ENTRIES_PER_BATCH 拆分,正常不应触达此守卫。
@@ -238,7 +252,11 @@ function serializeBatch(entries) {
238
252
  const crc = crc32(crcBuf);
239
253
  fullView.setUint32(8, crc, false);
240
254
 
241
- return bytesToHex(fullBuf);
255
+ const hex = bytesToHex(fullBuf);
256
+ if (hmacKey) {
257
+ return `${hex}.${hmacHex(hex, hmacKey)}`;
258
+ }
259
+ return hex;
242
260
  }
243
261
 
244
262
  /**
@@ -246,11 +264,36 @@ function serializeBatch(entries) {
246
264
  * @param {string} hex
247
265
  * @param {number} batchSeq
248
266
  * @param {function} [reportWarn] - 可选,告警回调 ({ level, message, context })
267
+ * @param {string} [hmacKey] - 可选 HMAC 密钥;设置后校验批次 HMAC(不匹配返回 null)
249
268
  * @returns {{ seq: number, entries: Object[] }|null}
250
269
  */
251
- function deserializeBatch(hex, batchSeq, reportWarn) {
270
+ function deserializeBatch(hex, batchSeq, reportWarn, hmacKey) {
252
271
  try {
253
- const buf = hexToBytes(hex);
272
+ // 2026-10-31 渗透测试 M-03 修复:分离并校验 HMAC 后缀(`.hmacHex`)
273
+ let batchHex = hex;
274
+ let storedHmac = null;
275
+ const dotIdx = hex.indexOf('.');
276
+ if (dotIdx !== -1) {
277
+ batchHex = hex.slice(0, dotIdx);
278
+ storedHmac = hex.slice(dotIdx + 1);
279
+ }
280
+ if (hmacKey) {
281
+ if (storedHmac) {
282
+ const expectedHmac = hmacHex(batchHex, hmacKey);
283
+ if (expectedHmac !== storedHmac) {
284
+ const warnMsg = 'Batch HMAC mismatch(WAL 可能被篡改)';
285
+ if (reportWarn) reportWarn('warn', warnMsg);
286
+ else console.warn(`[WAL] ${warnMsg}`);
287
+ return null;
288
+ }
289
+ } else {
290
+ // 旧格式(无 HMAC 后缀):向后兼容放行,但提示未受认证完整性保护
291
+ const warnMsg = 'Batch 缺少 HMAC(旧格式 WAL,未受认证完整性保护)';
292
+ if (reportWarn) reportWarn('warn', warnMsg);
293
+ else console.warn(`[WAL] ${warnMsg}`);
294
+ }
295
+ }
296
+ const buf = hexToBytes(batchHex);
254
297
  if (buf.length < BATCH_HEADER_SIZE) {
255
298
  return null;
256
299
  }
@@ -350,6 +393,12 @@ export class WriteAheadLog {
350
393
  this._seqInitialized = false; // 懒初始化标记
351
394
  this._seq = 0;
352
395
 
396
+ // 2026-10-31 渗透测试 M-03 修复:可选 WAL/checkpoint 认证完整性密钥。
397
+ // 设置 hmacKey 后,新写入的二进制批次与 checkpoint 均附加 HMAC-SHA256;
398
+ // 恢复时校验 HMAC(不匹配则拒绝该批次/快照)。旧格式(无 HMAC)仍可读取
399
+ // (向后兼容),但会告警提示。
400
+ this._hmacKey = options.hmacKey || null;
401
+
353
402
  // ── 历史 checkpoint 保留策略(P3-#17) ──
354
403
  /** @type {boolean} 保留所有历史 checkpoint(时间旅行查询必需) */
355
404
  this._keepAllCps = options.keepAllCheckpoints === true;
@@ -572,7 +621,7 @@ export class WriteAheadLog {
572
621
  continue;
573
622
  }
574
623
 
575
- const result = deserializeBatch(raw, batchSeq, this._reportError.bind(this));
624
+ const result = deserializeBatch(raw, batchSeq, this._reportError.bind(this), this._hmacKey);
576
625
  if (!result || !result.entries) {
577
626
  continue;
578
627
  }
@@ -866,9 +915,16 @@ export class WriteAheadLog {
866
915
  const cpKey = `${CP_KEY_PREFIX}:${this.dbName}:${String(seq).padStart(16, '0')}`;
867
916
  const tmpKey = cpKey + ':tmp'; // 临时键,用于原子切换
868
917
 
918
+ // 2026-10-31 渗透测试 M-03 修复:配置 hmacKey 时为 checkpoint 附加 HMAC 认证
919
+ let cpJson = JSON.stringify(cpData);
920
+ if (this._hmacKey) {
921
+ const hmac = hmacHex(cpJson, this._hmacKey);
922
+ cpJson = JSON.stringify({ ...cpData, _hmac: hmac });
923
+ }
924
+
869
925
  try {
870
926
  // 阶段 1:写入临时 checkpoint
871
- await this.kv.set(tmpKey, JSON.stringify(cpData));
927
+ await this.kv.set(tmpKey, cpJson);
872
928
  if (typeof this.kv.flush === 'function') {
873
929
  await this.kv.flush();
874
930
  }
@@ -1128,7 +1184,7 @@ export class WriteAheadLog {
1128
1184
  for (let offset = 0; offset < entries.length; offset += chunkSize) {
1129
1185
  const chunk = entries.slice(offset, offset + chunkSize);
1130
1186
  const chunkStartSeq = startSeq + offset;
1131
- const hex = serializeBatch(chunk);
1187
+ const hex = serializeBatch(chunk, this._hmacKey);
1132
1188
  const key = `${WAL_KEY_PREFIX}:${this.dbName}:seq:${String(chunkStartSeq).padStart(16, '0')}`;
1133
1189
  await this.kv.set(key, hex);
1134
1190
  }
@@ -1268,10 +1324,10 @@ export class WriteAheadLog {
1268
1324
  continue;
1269
1325
  }
1270
1326
 
1271
- // 尝试二进制批次格式(十六进制字符串)
1272
- if (/^[0-9a-f]+$/i.test(raw) && raw.length > 20) {
1327
+ // 尝试二进制批次格式(十六进制字符串;2026-10-31 起可带 `.hmac` 认证后缀)
1328
+ if (/^[0-9a-f]+(\.[0-9a-f]+)?$/i.test(raw) && raw.length > 20) {
1273
1329
  const keySeq = parseInt(key.split(':').pop(), 10) || 0;
1274
- const result = deserializeBatch(raw, keySeq, this._reportError.bind(this));
1330
+ const result = deserializeBatch(raw, keySeq, this._reportError.bind(this), this._hmacKey);
1275
1331
  if (result && result.entries) {
1276
1332
  for (const entry of result.entries) {
1277
1333
  if (entry.seq > afterSeq) {
@@ -1331,6 +1387,20 @@ export class WriteAheadLog {
1331
1387
  return 0;
1332
1388
  }
1333
1389
 
1390
+ // 2026-10-31 渗透测试 M-03 修复:校验 checkpoint HMAC(若配置密钥)
1391
+ if (this._hmacKey) {
1392
+ if (cpData && typeof cpData === 'object' && '_hmac' in cpData) {
1393
+ const { _hmac, ...payloadObj } = cpData;
1394
+ const expectedHmac = hmacHex(JSON.stringify(payloadObj), this._hmacKey);
1395
+ if (expectedHmac !== _hmac) {
1396
+ this._reportError('warn', `_loadCheckpoint: checkpoint HMAC mismatch (可能被篡改), skipping "${latestKey}"`);
1397
+ return 0;
1398
+ }
1399
+ } else {
1400
+ this._reportError('warn', `_loadCheckpoint: checkpoint 缺少 HMAC(旧格式快照,未受认证完整性保护)`);
1401
+ }
1402
+ }
1403
+
1334
1404
  // 还原表
1335
1405
  for (const [tableName, tableMeta] of Object.entries(cpData.tables)) {
1336
1406
  const colDefs = tableMeta.columns.map(c =>
@@ -337,9 +337,50 @@ class Parser {
337
337
  export class GraphQLBridge {
338
338
  /**
339
339
  * @param {object} executor - Executor 实例
340
+ * @param {object} [options]
341
+ * @param {number} [options.maxDepth=8] - 最大查询嵌套深度(防深度炸弹)
342
+ * @param {number} [options.maxSelections=100] - 单请求最大字段/选择数(防字段数放大)
340
343
  */
341
- constructor(executor) {
344
+ constructor(executor, options = {}) {
342
345
  this.executor = executor;
346
+ this.maxDepth = Number.isFinite(options.maxDepth) ? options.maxDepth : 8;
347
+ this.maxSelections = Number.isFinite(options.maxSelections) ? options.maxSelections : 100;
348
+ }
349
+
350
+ /**
351
+ * 校验查询成本(2026-10-31 渗透测试 R7 修复)
352
+ *
353
+ * 递归遍历 AST,统计最大嵌套深度与总选择(字段)数。
354
+ * 超限直接抛错,防止深度嵌套/海量字段的解析与执行放大 DoS。
355
+ * @param {{operation: string, selections: Array}} doc - parseDocument 返回的 AST
356
+ * @private
357
+ */
358
+ _validateQueryCost(doc) {
359
+ let totalSelections = 0;
360
+ let maxDepth = 0;
361
+
362
+ const walk = (fields, depth) => {
363
+ if (depth > maxDepth) {
364
+ maxDepth = depth;
365
+ }
366
+ if (!fields || fields.length === 0) {
367
+ return;
368
+ }
369
+ for (const f of fields) {
370
+ totalSelections++;
371
+ if (totalSelections > this.maxSelections) {
372
+ throw new Error(`GraphQL 查询字段数超过上限 ${this.maxSelections}(防资源耗尽)`);
373
+ }
374
+ if (f.fields && f.fields.length > 0) {
375
+ walk(f.fields, depth + 1);
376
+ }
377
+ }
378
+ };
379
+
380
+ walk(doc.selections, 1);
381
+ if (maxDepth > this.maxDepth) {
382
+ throw new Error(`GraphQL 查询深度 ${maxDepth} 超过上限 ${this.maxDepth}(防嵌套 DoS)`);
383
+ }
343
384
  }
344
385
 
345
386
  /**
@@ -456,6 +497,9 @@ ${mutationFields.join('\n')}
456
497
  const parser = new Parser(tokens);
457
498
  const doc = parser.parseDocument();
458
499
 
500
+ // 2026-10-31 渗透测试 R7 修复:查询成本上限(深度/字段数)
501
+ this._validateQueryCost(doc);
502
+
459
503
  if (doc.operation === 'mutation') {
460
504
  return this._executeMutations(doc.selections);
461
505
  }
package/src/http-api.js CHANGED
@@ -207,6 +207,16 @@ function _containsSubquery(expr) {
207
207
  */
208
208
  function parseBody(req, limit = 1048576) {
209
209
  return new Promise((resolve, reject) => {
210
+ // 2026-10-31 渗透测试 H-03 修复:拒绝非 JSON Content-Type(消除跨站盲写 SQL 面),
211
+ // 与 http-server/server.js parseBody 行为对齐。
212
+ const contentType = (req.headers['content-type'] || '').toLowerCase();
213
+ if (contentType && !contentType.includes('application/json')) {
214
+ // 排空 body 但不销毁连接,让 handler 正常返回 400 错误响应
215
+ req.removeAllListeners('data');
216
+ req.resume();
217
+ reject(new Error('不支持的 Content-Type:仅接受 application/json'));
218
+ return;
219
+ }
210
220
  let body = '';
211
221
  let totalBytes = 0;
212
222
  req.on('data', chunk => {
@@ -229,6 +239,26 @@ function parseBody(req, limit = 1048576) {
229
239
  });
230
240
  }
231
241
 
242
+ /**
243
+ * 认证端点安全错误文案(2026-10-31 渗透测试 L-05 修复)
244
+ *
245
+ * AuthManager 已把所有可预期失败归一为安全文案(反用户枚举);
246
+ * 但非预期异常(数据库故障、TypeError 等)可能携带内部细节。
247
+ * 这里仅透出白名单内的业务文案,其余统一为通用信息(dev 模式保留细节便于排查)。
248
+ * @param {Error} err
249
+ * @returns {string}
250
+ */
251
+ function safeAuthError(err) {
252
+ const msg = err?.message || '';
253
+ const isDev = process.env.NODE_ENV === 'development';
254
+ if (msg === '注册失败' || msg === '用户名或密码错误' ||
255
+ msg === 'username 与 password 不能为空' || msg === '用户不存在' ||
256
+ msg === '旧密码错误') {
257
+ return msg;
258
+ }
259
+ return isDev ? msg : '认证服务异常,请稍后重试';
260
+ }
261
+
232
262
  /**
233
263
  * 统一错误响应(2026-08-14 M-06 修复:默认隐藏内部错误细节,防信息泄露)
234
264
  * - NODE_ENV=development 时保留 err.message 便于调试
@@ -924,7 +954,7 @@ export class HttpApiServer {
924
954
  res.end(JSON.stringify(result));
925
955
  } catch (err) {
926
956
  res.writeHead(400, { 'Content-Type': 'application/json' });
927
- res.end(JSON.stringify({ error: err.message }));
957
+ res.end(JSON.stringify({ error: safeAuthError(err) }));
928
958
  }
929
959
  }
930
960
 
@@ -955,7 +985,7 @@ export class HttpApiServer {
955
985
  }
956
986
  } catch (err) {
957
987
  res.writeHead(401, { 'Content-Type': 'application/json' });
958
- res.end(JSON.stringify({ error: err.message }));
988
+ res.end(JSON.stringify({ error: safeAuthError(err) }));
959
989
  }
960
990
  }
961
991
 
@@ -32,6 +32,7 @@ export { DBServer };
32
32
  * @param {string} [options.signingKey] - RSA 私钥 PEM(v1.12.0 数字签名)
33
33
  * @param {string} [options.verifyKey] - RSA 公钥 PEM(v1.12.0 签名验证)
34
34
  * @param {string} [options.auditFilePath] - 审计日志持久化文件路径
35
+ * @param {string} [options.anchorSigningKey] - 持久化审计 anchor HMAC 密钥(≥32 字节;配置 auditFilePath 时必填,M-5/fail-closed)
35
36
  * @param {boolean} [options.enableAuditOnStart=true] - 启动时自动启用审计日志
36
37
  * @param {string[]} [options.corsOrigins] - v1.34.6: CORS 允许的 Origin 白名单(null 或含 '*' 表示允许任意)
37
38
  * @param {boolean} [options.trustUserIdHeader=false] - v1.34.6: 是否信任 X-User-Id 头部作为身份来源(默认 false,仅受信反代场景开启)
@@ -79,6 +80,7 @@ export async function startHTTPServer(executor, options = {}) {
79
80
  signingKey: options.signingKey,
80
81
  verifyKey: options.verifyKey,
81
82
  auditFilePath: options.auditFilePath,
83
+ anchorSigningKey: options.anchorSigningKey,
82
84
  enableAuditOnStart: options.enableAuditOnStart,
83
85
  corsOrigins: options.corsOrigins,
84
86
  trustUserIdHeader: options.trustUserIdHeader,
@@ -304,6 +304,19 @@ export function parseBody(req, limit = DEFAULT_BODY_LIMIT) {
304
304
  const chunks = [];
305
305
  let totalBytes = 0;
306
306
 
307
+ // 2026-10-31 渗透测试 H-03 修复:拒绝非 JSON Content-Type。
308
+ // 浏览器跨站"简单请求"(text/plain 等)此前可带 JSON body 盲执行 SQL;
309
+ // 现在仅接受 application/json,浏览器跨站 application/json 会触发 CORS preflight
310
+ // (默认被 CORS 'null' 策略拒绝),从而消除 CSRF 盲写面。
311
+ const contentType = (req.headers['content-type'] || '').toLowerCase();
312
+ if (contentType && !contentType.includes('application/json')) {
313
+ // 排空 body 但不销毁连接,让 handler 正常返回 400 错误响应
314
+ req.removeAllListeners('data');
315
+ req.resume();
316
+ reject(new Error('不支持的 Content-Type:仅接受 application/json'));
317
+ return;
318
+ }
319
+
307
320
  req.on('data', chunk => {
308
321
  totalBytes += chunk.length;
309
322
  if (totalBytes > limit) {
@@ -657,14 +657,18 @@ export class WSServer {
657
657
 
658
658
  /**
659
659
  * Origin 校验(M-04 修复):配置白名单后,浏览器 Origin 必须精确匹配
660
+ * 2026-10-31 渗透测试 L-03 收紧扣紧:未配置白名单时(零认证回环模式),
661
+ * 仍拒绝携带 Origin 的浏览器连接(跨站 WebSocket 劫持面),仅放行无 Origin
662
+ * 的非浏览器客户端(curl/Node SDK)。
660
663
  * @param {string|null} origin
661
664
  * @returns {boolean}
662
665
  * @private
663
666
  */
664
667
  _checkOrigin(origin) {
665
668
  if (!this._allowedOrigins) {
666
- return true;
667
- } // 未配置白名单:放行
669
+ // 未配置白名单:仅允许无 Origin 的非浏览器客户端(浏览器必然携带 Origin)
670
+ return !origin;
671
+ }
668
672
  if (!origin) {
669
673
  return this._allowNoOrigin;
670
674
  } // 无 Origin:非浏览器客户端
@@ -292,6 +292,8 @@ export class ReplicaHttpClient {
292
292
  allowedPorts: [],
293
293
  };
294
294
  this._ssrfGuard = null;
295
+ // 2026-10-31 渗透测试 R6 修复:DNS 钉扎(可选,默认关闭)
296
+ this._pinDns = options.pinDns === true;
295
297
  }
296
298
 
297
299
  /**
@@ -347,11 +349,11 @@ export class ReplicaHttpClient {
347
349
  }
348
350
 
349
351
  async _request(method, path, request) {
350
- // v1.34.5: SSRF 防护校验主节点地址
352
+ // v1.34.5: SSRF 防护校验主节点地址(2026-10-31 M-04:升级为静态+DNS 深度校验)
351
353
  const fullUrl = `http://${this.masterHost}:${this.masterPort}${path}`;
352
354
  const guard = await this._getSSRFGuard();
353
355
  if (guard) {
354
- const check = guard.validate(fullUrl);
356
+ const check = await guard.validateDeep(fullUrl);
355
357
  if (!check.allowed) {
356
358
  throw new Error(`SSRF 阻止主节点请求: ${check.reason}`);
357
359
  }
@@ -362,6 +364,12 @@ export class ReplicaHttpClient {
362
364
  body = JSON.stringify(request);
363
365
  }
364
366
 
367
+ // 2026-10-31 渗透测试 R6 修复:DNS 钉扎(可选)——用解析 IP 直连,保留 Host 头
368
+ let pinned = null;
369
+ if (this._pinDns && guard) {
370
+ pinned = await guard.resolvePinnedHost(fullUrl);
371
+ }
372
+
365
373
  const headers = {
366
374
  'Content-Type': 'application/json',
367
375
  };
@@ -369,10 +377,14 @@ export class ReplicaHttpClient {
369
377
  if (this._authToken) {
370
378
  headers['Authorization'] = `Bearer ${this._authToken}`;
371
379
  }
380
+ // DNS 钉扎时保留原始 Host 头
381
+ if (pinned) {
382
+ headers.Host = pinned.hostHeader;
383
+ }
372
384
 
373
385
  const reqOpts = {
374
- hostname: this.masterHost,
375
- port: this.masterPort,
386
+ hostname: pinned ? pinned.ip : this.masterHost,
387
+ port: pinned ? pinned.port : this.masterPort,
376
388
  path,
377
389
  method,
378
390
  headers,
@@ -7,7 +7,7 @@
7
7
 
8
8
  <groupId>com.purejsdb</groupId>
9
9
  <artifactId>pure-client</artifactId>
10
- <version>2.53.0</version>
10
+ <version>2.53.1</version>
11
11
  <packaging>jar</packaging>
12
12
 
13
13
  <name>pure-js-db Java SDK</name>
@@ -62,7 +62,7 @@ import java.util.NoSuchElementException;
62
62
  public class PureClient {
63
63
 
64
64
  /** SDK 版本号。 */
65
- public static final String SDK_VERSION = "2.53.0";
65
+ public static final String SDK_VERSION = "2.53.1";
66
66
 
67
67
  /** 默认请求超时(秒)。 */
68
68
  public static final long DEFAULT_TIMEOUT_SECONDS = 30;
@@ -114,7 +114,7 @@ class PureClientTest {
114
114
  @Order(1)
115
115
  @DisplayName("J1: SDK 版本号应为 1.10.0")
116
116
  void testSdkVersion() {
117
- assertEquals("2.53.0", PureClient.SDK_VERSION,
117
+ assertEquals("2.53.1", PureClient.SDK_VERSION,
118
118
  "SDK 版本应为 1.10.0");
119
119
  }
120
120
 
@@ -2,7 +2,7 @@
2
2
  "name": "pure-js-db/php-sdk",
3
3
  "description": "PHP SDK for pure-js-db — pure JavaScript database engine, via HTTP RESTful API.",
4
4
  "type": "library",
5
- "version": "2.53.0",
5
+ "version": "2.53.1",
6
6
  "keywords": ["database", "sdk", "pure-js-db", "rest", "curl"],
7
7
  "license": "MIT",
8
8
  "require": {
@@ -34,7 +34,7 @@ namespace PureJSDB\Sdk;
34
34
  class PureClient
35
35
  {
36
36
  /** @var string SDK 版本号 */
37
- public const SDK_VERSION = '2.53.0';
37
+ public const SDK_VERSION = '2.53.1';
38
38
 
39
39
  /** @var float 默认请求超时(秒) */
40
40
  private const DEFAULT_TIMEOUT = 30.0;
@@ -35,7 +35,7 @@ import urllib.parse
35
35
  from typing import Any, Dict, Iterator, List, Optional, Union
36
36
 
37
37
 
38
- __version__ = "2.53.0"
38
+ __version__ = "2.53.1"
39
39
 
40
40
 
41
41
  class PureError(Exception):
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "pure-js-db"
3
- version = "2.53.0"
3
+ version = "2.53.1"
4
4
  edition = "2021"
5
5
  rust-version = "1.75"
6
6
  description = "Rust SDK for pure-js-db — HTTP RESTful API 客户端 + HNSWPQ 向量索引管理"
@@ -29,8 +29,8 @@ grep -E 'SDK 版本:\*\*[0-9]+\.[0-9]+\.[0-9]+\*\*' README.md # 期望: SDK
29
29
 
30
30
  | 文件 | 当前值 |
31
31
  |------|--------|
32
- | `pure-js-db/package.json` | `2.53.0` |
33
- | `pure-js-db/src/index.js` `VERSION` | `'2.53.0'` |
32
+ | `pure-js-db/package.json` | `2.53.1` |
33
+ | `pure-js-db/src/index.js` `VERSION` | `'2.53.1'` |
34
34
 
35
35
  ---
36
36
 
@@ -57,7 +57,7 @@ pub use models::{
57
57
  pub use transaction::Transaction;
58
58
 
59
59
  /// SDK 版本号
60
- pub const SDK_VERSION: &str = "2.53.0";
60
+ pub const SDK_VERSION: &str = "2.53.1";
61
61
 
62
62
  /// 默认 API 前缀
63
63
  pub const DEFAULT_API_PREFIX: &str = "/api/v1";