@chainlesschain/personal-data-hub 0.2.2 → 0.2.3

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.
@@ -0,0 +1,414 @@
1
+ /**
2
+ * §2.5b 地图三联 v0.2 — Tencent Map (腾讯地图) adapter, dual-mode (snapshot + sqlite).
3
+ *
4
+ * 新增本 adapter 把地图三联补齐 (amap / baidu-map / tencent-map)。两条路径
5
+ * 与 travel-baidu-map / travel-amap 同 pattern:
6
+ *
7
+ * 1. snapshot mode (opts.inputPath): in-APK Android cc reads a snapshot
8
+ * JSON produced by TencentMapLocalCollector (WebView cookie scrape on
9
+ * map.qq.com). Desktop-independent. Adapter stateless — account.
10
+ * deviceId OPTIONAL at construction.
11
+ *
12
+ * 2. sqlite mode (opts.dbPath, future device-pull): scaffold for completeness
13
+ * — table names are educated guess (sjqz/parsers does not yet have a
14
+ * tencent-map parser). Mode runs but trySelect tolerates missing tables.
15
+ * account.deviceId REQUIRED in this mode (checked at sync, not
16
+ * construction).
17
+ *
18
+ * Snapshot schema (mirrors TencentMapLocalCollector.SNAPSHOT_SCHEMA_VERSION):
19
+ *
20
+ * {
21
+ * "schemaVersion": 1,
22
+ * "snapshottedAt": <epoch-ms>,
23
+ * "vendor": "tencent-map",
24
+ * "account": { "uid": "...", "displayName": "..." },
25
+ * "events": [
26
+ * { "kind": "favourite", "id": "fav-<rid>", "capturedAt": <ms>,
27
+ * "name": "...", "address": "...", "lat": .., "lng": .., "category": "home|company|other" },
28
+ * { "kind": "search", "id": "search-<sid>","capturedAt": <ms>,
29
+ * "query": "...", "city": "..." },
30
+ * { "kind": "route", "id": "route-<rid>", "capturedAt": <ms>,
31
+ * "from": {...}, "to": {...}, "mode": "drive|walk|bus|bike|trip" }
32
+ * ]
33
+ * }
34
+ */
35
+
36
+ "use strict";
37
+
38
+ const fs = require("node:fs");
39
+ const { normalizeTravelRecord, parseChineseDateTime } = require("../travel-base");
40
+
41
+ const NAME = "travel-tencent-map";
42
+ const VERSION = "0.2.0";
43
+ const SNAPSHOT_SCHEMA_VERSION = 1;
44
+
45
+ const KIND_FAVOURITE = "favourite";
46
+ const KIND_SEARCH = "search";
47
+ const KIND_ROUTE = "route";
48
+ const VALID_SNAPSHOT_KINDS = Object.freeze([KIND_FAVOURITE, KIND_SEARCH, KIND_ROUTE]);
49
+
50
+ class TencentMapAdapter {
51
+ constructor(opts = {}) {
52
+ // §2.5b v0.2: account.deviceId OPTIONAL — snapshot mode is stateless.
53
+ // Sqlite mode requires it; checked at sync time.
54
+ this.account = opts.account || null;
55
+ this._dbPath = opts.dbPath || null;
56
+
57
+ this.name = NAME;
58
+ this.version = VERSION;
59
+ this.capabilities = [
60
+ "sync:snapshot",
61
+ "sync:sqlite",
62
+ "parse:tencent-map-favourite",
63
+ "parse:tencent-map-history",
64
+ ];
65
+ this.extractMode = "device-pull";
66
+ this.rateLimits = {};
67
+ this.dataDisclosure = {
68
+ fields: [
69
+ "tencent:account (uid / displayName, cookie scrape)",
70
+ "tencent:favourite (saved places — home / company / other)",
71
+ "tencent:search_history (queries, scaffold sqlite mode)",
72
+ "tencent:route_history (planned routes, scaffold sqlite mode)",
73
+ ],
74
+ sensitivity: "medium",
75
+ legalGate: false,
76
+ defaultInclude: {
77
+ favourite: true,
78
+ search: true,
79
+ route: true,
80
+ },
81
+ };
82
+
83
+ this._deps = {
84
+ fs,
85
+ dbDriverFactory: opts.dbDriverFactory || null,
86
+ };
87
+ }
88
+
89
+ async authenticate(ctx = {}) {
90
+ if (ctx && typeof ctx.inputPath === "string" && ctx.inputPath.length > 0) {
91
+ try {
92
+ this._deps.fs.accessSync(ctx.inputPath, this._deps.fs.constants.R_OK);
93
+ } catch (err) {
94
+ return {
95
+ ok: false,
96
+ reason: "INPUT_PATH_UNREADABLE",
97
+ message: `snapshot not readable at ${ctx.inputPath}: ${err.message}`,
98
+ };
99
+ }
100
+ return { ok: true, mode: "snapshot-file" };
101
+ }
102
+ if (this._dbPath || (ctx && typeof ctx.dbPath === "string")) {
103
+ if (!this.account || !this.account.deviceId) {
104
+ return {
105
+ ok: false,
106
+ reason: "NO_ACCOUNT_DEVICE_ID",
107
+ message: "travel-tencent-map.authenticate: sqlite mode requires account.deviceId",
108
+ };
109
+ }
110
+ return { ok: true, account: this.account.deviceId, mode: "sqlite" };
111
+ }
112
+ return {
113
+ ok: false,
114
+ reason: "NO_INPUT",
115
+ message:
116
+ "travel-tencent-map.authenticate: needs opts.inputPath (snapshot mode) OR opts.dbPath (sqlite mode)",
117
+ };
118
+ }
119
+
120
+ async healthCheck() {
121
+ return { ok: true, lastChecked: Date.now() };
122
+ }
123
+
124
+ async *sync(opts = {}) {
125
+ if (typeof opts.inputPath === "string" && opts.inputPath.length > 0) {
126
+ yield* this._syncViaSnapshot(opts);
127
+ return;
128
+ }
129
+ const dbPath = opts.dbPath || this._dbPath;
130
+ if (dbPath) {
131
+ yield* this._syncViaSqlite({ ...opts, dbPath });
132
+ return;
133
+ }
134
+ throw new Error(
135
+ "travel-tencent-map.sync: needs opts.inputPath (snapshot mode, Android in-APK cc) OR opts.dbPath (sqlite mode)",
136
+ );
137
+ }
138
+
139
+ async *_syncViaSnapshot(opts) {
140
+ const raw = this._deps.fs.readFileSync(opts.inputPath, "utf-8");
141
+ const snapshot = JSON.parse(raw);
142
+ if (
143
+ !snapshot ||
144
+ typeof snapshot !== "object" ||
145
+ snapshot.schemaVersion !== SNAPSHOT_SCHEMA_VERSION
146
+ ) {
147
+ throw new Error(
148
+ `travel-tencent-map.sync: snapshot schemaVersion mismatch (got ${snapshot && snapshot.schemaVersion}, expected ${SNAPSHOT_SCHEMA_VERSION})`,
149
+ );
150
+ }
151
+ const fallbackCapturedAt =
152
+ Number.isFinite(snapshot.snapshottedAt) && snapshot.snapshottedAt > 0
153
+ ? Math.floor(snapshot.snapshottedAt)
154
+ : Date.now();
155
+ const account =
156
+ snapshot.account && typeof snapshot.account === "object"
157
+ ? snapshot.account
158
+ : null;
159
+ const include = opts.include || {};
160
+ const limit =
161
+ Number.isInteger(opts.limit) && opts.limit > 0 ? opts.limit : Infinity;
162
+
163
+ const events = Array.isArray(snapshot.events) ? snapshot.events : [];
164
+ let emitted = 0;
165
+ for (const ev of events) {
166
+ if (emitted >= limit) return;
167
+ if (!ev || typeof ev !== "object") continue;
168
+ const kind = ev.kind;
169
+ if (!VALID_SNAPSHOT_KINDS.includes(kind)) continue;
170
+ if (include[kind] === false) continue;
171
+
172
+ const capturedAt =
173
+ parseTime(ev.capturedAt) ||
174
+ parseTime(ev.time) ||
175
+ fallbackCapturedAt;
176
+ const id =
177
+ (typeof ev.id === "string" && ev.id.length > 0 && ev.id) ||
178
+ ev.rid ||
179
+ null;
180
+
181
+ yield {
182
+ adapter: NAME,
183
+ kind,
184
+ originalId: stableOriginalId(kind, id),
185
+ capturedAt,
186
+ payload: { ...ev, account },
187
+ };
188
+ emitted += 1;
189
+ }
190
+ }
191
+
192
+ async *_syncViaSqlite(opts) {
193
+ if (!this.account || !this.account.deviceId) {
194
+ throw new Error(
195
+ "travel-tencent-map._syncViaSqlite: account.deviceId required (set via new TencentMapAdapter({ account: { deviceId } }))",
196
+ );
197
+ }
198
+ const dbPath = opts.dbPath;
199
+ if (!dbPath || !this._deps.fs.existsSync(dbPath)) return;
200
+ const Driver = this._deps.dbDriverFactory
201
+ ? this._deps.dbDriverFactory()
202
+ : require("better-sqlite3-multiple-ciphers");
203
+ const db = new Driver(dbPath, { readonly: true });
204
+
205
+ try {
206
+ // Tencent Map Android app table names (educated guess — sjqz has no
207
+ // parser yet; trySelect tolerates missing tables for forward-compat).
208
+ const routes =
209
+ trySelect(db, "SELECT * FROM route_history LIMIT 5000")
210
+ || trySelect(db, "SELECT * FROM tencent_route_history LIMIT 5000")
211
+ || [];
212
+ for (const r of routes) {
213
+ const rec = routeRowToRecord(r);
214
+ if (rec) {
215
+ yield {
216
+ adapter: NAME,
217
+ originalId: rec.recordId,
218
+ capturedAt: rec.bookedAt || Date.now(),
219
+ payload: { record: rec, kind: KIND_ROUTE },
220
+ };
221
+ }
222
+ }
223
+ const searches =
224
+ trySelect(db, "SELECT * FROM search_history LIMIT 5000")
225
+ || trySelect(db, "SELECT * FROM tencent_search_history LIMIT 5000")
226
+ || [];
227
+ for (const r of searches) {
228
+ const rec = searchRowToRecord(r);
229
+ if (rec) {
230
+ yield {
231
+ adapter: NAME,
232
+ originalId: rec.recordId,
233
+ capturedAt: rec.bookedAt || Date.now(),
234
+ payload: { record: rec, kind: KIND_SEARCH },
235
+ };
236
+ }
237
+ }
238
+ } finally {
239
+ try { db.close(); } catch (_e) { /* ignore */ }
240
+ }
241
+ }
242
+
243
+ normalize(raw) {
244
+ if (!raw || !raw.payload) {
245
+ throw new Error("TencentMapAdapter.normalize: payload missing");
246
+ }
247
+ const kind = raw.kind || raw.payload.kind;
248
+ const p = raw.payload;
249
+
250
+ if (p.record) {
251
+ return normalizeTravelRecord(p.record, {
252
+ adapterName: NAME,
253
+ adapterVersion: VERSION,
254
+ });
255
+ }
256
+ const rec = snapshotEventToRecord(kind, p, raw.originalId);
257
+ return normalizeTravelRecord(rec, {
258
+ adapterName: NAME,
259
+ adapterVersion: VERSION,
260
+ });
261
+ }
262
+ }
263
+
264
+ function stableOriginalId(kind, id) {
265
+ const stringified =
266
+ (typeof id === "string" && id.length > 0 && id) ||
267
+ (typeof id === "number" && Number.isFinite(id) && String(id)) ||
268
+ null;
269
+ const safe =
270
+ stringified ||
271
+ `unknown-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
272
+ return `tencent-map:${kind}:${safe}`;
273
+ }
274
+
275
+ function snapshotEventToRecord(kind, p, originalId) {
276
+ if (kind === KIND_FAVOURITE) {
277
+ return {
278
+ vendorId: "tencentmap",
279
+ recordId: originalId,
280
+ vehicleType: "visit",
281
+ to: {
282
+ name: p.name || p.address || null,
283
+ lat: numberOrNull(p.lat),
284
+ lng: numberOrNull(p.lng),
285
+ city: p.city || null,
286
+ },
287
+ departureMs: parseTime(p.capturedAt),
288
+ carrier: "腾讯地图",
289
+ extras: { category: p.category || null, kind: KIND_FAVOURITE },
290
+ };
291
+ }
292
+ if (kind === KIND_SEARCH) {
293
+ return {
294
+ vendorId: "tencentmap",
295
+ recordId: originalId,
296
+ vehicleType: "visit",
297
+ to: {
298
+ name: p.query || null,
299
+ lat: numberOrNull(p.lat),
300
+ lng: numberOrNull(p.lng),
301
+ city: p.city || null,
302
+ },
303
+ departureMs: parseTime(p.capturedAt),
304
+ carrier: "腾讯地图",
305
+ extras: { query: p.query || null, kind: KIND_SEARCH },
306
+ };
307
+ }
308
+ if (kind === KIND_ROUTE) {
309
+ return {
310
+ vendorId: "tencentmap",
311
+ recordId: originalId,
312
+ vehicleType: detectVehicle(p.mode),
313
+ from: p.from
314
+ ? { name: p.from.name || null, lat: numberOrNull(p.from.lat), lng: numberOrNull(p.from.lng) }
315
+ : undefined,
316
+ to: p.to
317
+ ? { name: p.to.name || null, lat: numberOrNull(p.to.lat), lng: numberOrNull(p.to.lng) }
318
+ : undefined,
319
+ departureMs: parseTime(p.capturedAt),
320
+ carrier: "腾讯地图",
321
+ extras: { mode: p.mode || null, kind: KIND_ROUTE },
322
+ };
323
+ }
324
+ return {
325
+ vendorId: "tencentmap",
326
+ recordId: originalId,
327
+ vehicleType: "visit",
328
+ carrier: "腾讯地图",
329
+ extras: { kind, raw: p },
330
+ };
331
+ }
332
+
333
+ function trySelect(db, sql) {
334
+ try {
335
+ return db.prepare(sql).all();
336
+ } catch (_e) {
337
+ return null;
338
+ }
339
+ }
340
+
341
+ function routeRowToRecord(row) {
342
+ if (!row) return null;
343
+ const id = row._id || row.id || row.uid;
344
+ if (!id) return null;
345
+ return {
346
+ vendorId: "tencentmap",
347
+ recordId: `route-${id}`,
348
+ vehicleType: detectVehicle(row.type || row.mode),
349
+ from: { name: row.start_name || row.from_name, lat: row.start_lat || null, lng: row.start_lng || null },
350
+ to: { name: row.end_name || row.to_name, lat: row.end_lat || null, lng: row.end_lng || null },
351
+ departureMs: numberOrParse(row.time || row.create_time),
352
+ carrier: "腾讯地图",
353
+ extras: { mode: row.type || row.mode },
354
+ };
355
+ }
356
+
357
+ function searchRowToRecord(row) {
358
+ if (!row) return null;
359
+ const id = row._id || row.id;
360
+ if (!id) return null;
361
+ return {
362
+ vendorId: "tencentmap",
363
+ recordId: `search-${id}`,
364
+ vehicleType: "visit",
365
+ to: { name: row.key || row.query || row.keyword, lat: row.lat || null, lng: row.lng || null, city: row.city },
366
+ departureMs: numberOrParse(row.time || row.create_time),
367
+ carrier: "腾讯地图",
368
+ extras: { query: row.key || row.query || row.keyword },
369
+ };
370
+ }
371
+
372
+ function detectVehicle(v) {
373
+ const s = String(v || "").toLowerCase();
374
+ if (s.includes("drive") || s.includes("car")) return "car";
375
+ if (s.includes("walk")) return "walk";
376
+ if (s.includes("bike") || s.includes("cycle")) return "bike";
377
+ if (s.includes("bus") || s.includes("transit")) return "bus";
378
+ return "trip";
379
+ }
380
+
381
+ function numberOrNull(v) {
382
+ if (Number.isFinite(v)) return v;
383
+ if (typeof v === "string" && /^-?\d+(\.\d+)?$/.test(v)) return parseFloat(v);
384
+ return null;
385
+ }
386
+
387
+ function parseTime(v) {
388
+ if (Number.isFinite(v)) return v > 1e12 ? v : v * 1000;
389
+ if (typeof v === "string") {
390
+ if (/^\d+$/.test(v)) {
391
+ const n = parseInt(v, 10);
392
+ return n > 1e12 ? n : n * 1000;
393
+ }
394
+ const parsed = parseChineseDateTime(v);
395
+ if (Number.isFinite(parsed)) return parsed;
396
+ const t = Date.parse(v);
397
+ return Number.isFinite(t) ? t : null;
398
+ }
399
+ return null;
400
+ }
401
+
402
+ function numberOrParse(v) {
403
+ if (Number.isFinite(v)) return v > 1e12 ? v : v * 1000;
404
+ if (typeof v === "string") {
405
+ if (/^\d+$/.test(v)) {
406
+ const n = parseInt(v, 10);
407
+ return n > 1e12 ? n : n * 1000;
408
+ }
409
+ return parseChineseDateTime(v);
410
+ }
411
+ return null;
412
+ }
413
+
414
+ module.exports = { TencentMapAdapter, NAME, VERSION, SNAPSHOT_SCHEMA_VERSION };
package/lib/index.js CHANGED
@@ -39,10 +39,12 @@ const { Train12306Adapter } = require("./adapters/travel-12306");
39
39
  const { CtripAdapter } = require("./adapters/travel-ctrip");
40
40
  const { AmapAdapter } = require("./adapters/travel-amap");
41
41
  const { BaiduMapAdapter } = require("./adapters/travel-baidu-map");
42
+ const { TencentMapAdapter } = require("./adapters/travel-tencent-map");
42
43
  const shoppingBase = require("./adapters/shopping-base");
43
44
  const { TaobaoAdapter } = require("./adapters/shopping-taobao");
44
45
  const { JdAdapter } = require("./adapters/shopping-jd");
45
46
  const { MeituanAdapter } = require("./adapters/shopping-meituan");
47
+ const { PinduoduoAdapter } = require("./adapters/shopping-pinduoduo");
46
48
  const { BilibiliAdapter } = require("./adapters/social-bilibili");
47
49
  const { WeiboAdapter } = require("./adapters/social-weibo");
48
50
  const { DouyinAdapter } = require("./adapters/social-douyin");
@@ -221,13 +223,14 @@ module.exports = {
221
223
  wxidToWeChatPersonId: wechatAdapter.wxidToWeChatPersonId,
222
224
  WECHAT_PRAGMA_PROFILES: wechatAdapter.WECHAT_PRAGMA_PROFILES,
223
225
 
224
- // Phase 9 — Travel four-pack
226
+ // Phase 9 + §2.5b 地图三联 — Travel five-pack
225
227
  normalizeTravelRecord: travelBase.normalizeTravelRecord,
226
228
  parseChineseDateTime: travelBase.parseChineseDateTime,
227
229
  Train12306Adapter,
228
230
  CtripAdapter,
229
231
  AmapAdapter,
230
232
  BaiduMapAdapter,
233
+ TencentMapAdapter,
231
234
 
232
235
  // Phase 7 — Shopping three-pack
233
236
  normalizeOrderRecord: shoppingBase.normalizeOrderRecord,
@@ -235,6 +238,7 @@ module.exports = {
235
238
  TaobaoAdapter,
236
239
  JdAdapter,
237
240
  MeituanAdapter,
241
+ PinduoduoAdapter,
238
242
 
239
243
  // Phase 13+ — long-tail social + messaging (借 sjqz parsers)
240
244
  BilibiliAdapter,
package/lib/vault.js CHANGED
@@ -44,19 +44,71 @@ function newGroupId() {
44
44
  return `mg-${r()}${r()}-${Date.now().toString(36)}`;
45
45
  }
46
46
 
47
+ /**
48
+ * Translate a bs3mc load-failure error into an actionable, user-readable
49
+ * message. Detects NODE_MODULE_VERSION mismatch (the single most common
50
+ * failure: Node 23/24/25 has no prebuild — bs3mc upstream only ships for
51
+ * Node LTS ABIs 108/115/127). See memory `node_23_native_dep_trap.md`.
52
+ *
53
+ * Pure function so it can be unit-tested without poisoning require cache.
54
+ *
55
+ * @param {Error|unknown} err Original throw from `require("better-sqlite3-multiple-ciphers")`.
56
+ * @param {string} [nodeVer] process.versions.node (override for tests).
57
+ * @returns {Error} Wrapped Error with `cause` and (when ABI-related) `code: "BS3MC_ABI_MISMATCH"`.
58
+ */
59
+ function formatDriverLoadError(err, nodeVer) {
60
+ const originalMsg = err && err.message ? err.message : String(err);
61
+ const runtimeNodeVer = nodeVer || process.versions.node;
62
+
63
+ const abiMatch = originalMsg.match(
64
+ /NODE_MODULE_VERSION\s+(\d+)[\s\S]+?requires\s+NODE_MODULE_VERSION\s+(\d+)/,
65
+ );
66
+ if (abiMatch) {
67
+ const compiledAbi = abiMatch[1];
68
+ const runtimeAbi = abiMatch[2];
69
+ const lines = [
70
+ "better-sqlite3-multiple-ciphers ABI mismatch — Node " +
71
+ runtimeNodeVer +
72
+ " has ABI " +
73
+ runtimeAbi +
74
+ " but bs3mc prebuild is ABI " +
75
+ compiledAbi +
76
+ ".",
77
+ "",
78
+ "修法(任选其一):",
79
+ " 1. 切 Node 22 LTS (推荐) — nvm-windows: `nvm install 22.12.0 && nvm use 22.12.0`",
80
+ " 2. 源码重编 — `npm rebuild better-sqlite3-multiple-ciphers --build-from-source`",
81
+ " (需要本机有 Visual Studio Build Tools / node-gyp toolchain,慢且不推荐)",
82
+ "",
83
+ "为什么 bs3mc 没 ABI " + runtimeAbi + " prebuild:",
84
+ " bs3mc 上游只 ship 主流 Node LTS 的 prebuild (ABI 108/115/127)。",
85
+ " Node 23/24/25 是 Current 系列,上游不给 prebuild。",
86
+ "",
87
+ "项目 engines.node 允许 >=22.12 是为了兼容未来 LTS,但实际推荐 22.x。",
88
+ ];
89
+ const wrapped = new Error(lines.join("\n"));
90
+ wrapped.cause = err;
91
+ wrapped.code = "BS3MC_ABI_MISMATCH";
92
+ return wrapped;
93
+ }
94
+
95
+ const wrapped = new Error(
96
+ "Failed to load better-sqlite3-multiple-ciphers. " +
97
+ "Install it as a workspace dep or pin the version in your package. " +
98
+ "Original error: " +
99
+ originalMsg,
100
+ );
101
+ wrapped.cause = err;
102
+ return wrapped;
103
+ }
104
+
47
105
  function loadDriver() {
48
106
  // Lazy require so consumers that only need schemas don't pay for the
49
107
  // native binding load. Errors surface here with a precise message.
50
108
  try {
51
109
  return require("better-sqlite3-multiple-ciphers");
52
110
  } catch (err) {
53
- const msg =
54
- "Failed to load better-sqlite3-multiple-ciphers. " +
55
- "Install it as a workspace dep or pin the version in your package. " +
56
- "Original error: " + (err && err.message ? err.message : String(err));
57
- const wrapped = new Error(msg);
58
- wrapped.cause = err;
59
- throw wrapped;
111
+ throw formatDriverLoadError(err);
60
112
  }
61
113
  }
62
114
 
@@ -1223,4 +1275,4 @@ class LocalVault {
1223
1275
  }
1224
1276
  }
1225
1277
 
1226
- module.exports = { LocalVault };
1278
+ module.exports = { LocalVault, _internal: { loadDriver, formatDriverLoadError } };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chainlesschain/personal-data-hub",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Personal Data Hub — UnifiedSchema + validators + KG ingest helpers for the data-back-to-the-individual middleware",
5
5
  "type": "commonjs",
6
6
  "main": "lib/index.js",
@@ -46,6 +46,7 @@
46
46
  "./adapters/travel-ctrip": "./lib/adapters/travel-ctrip/index.js",
47
47
  "./adapters/travel-amap": "./lib/adapters/travel-amap/index.js",
48
48
  "./adapters/travel-baidu-map": "./lib/adapters/travel-baidu-map/index.js",
49
+ "./adapters/travel-tencent-map": "./lib/adapters/travel-tencent-map/index.js",
49
50
  "./adapters/shopping-base": "./lib/adapters/shopping-base/index.js",
50
51
  "./adapters/shopping-taobao": "./lib/adapters/shopping-taobao/index.js",
51
52
  "./adapters/shopping-jd": "./lib/adapters/shopping-jd/index.js",