@chainlesschain/personal-data-hub 0.4.23 → 0.4.24
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/__tests__/adapters/bank-family.test.js +125 -0
- package/__tests__/adapters/car-mercedesme.test.js +74 -0
- package/__tests__/adapters/finance-dcep.test.js +74 -0
- package/__tests__/adapters/fitness-joyrun.test.js +82 -0
- package/__tests__/adapters/gov-12123.test.js +103 -0
- package/__tests__/adapters/music-qq.test.js +112 -0
- package/__tests__/adapters/reading-family.test.js +108 -0
- package/__tests__/adapters/travel-didi-consumer.test.js +66 -0
- package/__tests__/audio-ximalaya-snapshot.test.js +279 -0
- package/__tests__/fitness-keep-snapshot.test.js +224 -0
- package/__tests__/shopping-eleme-snapshot.test.js +454 -0
- package/__tests__/shopping-vipshop-snapshot.test.js +425 -0
- package/__tests__/shopping-xianyu-snapshot.test.js +451 -0
- package/__tests__/social-douban-snapshot.test.js +351 -0
- package/lib/adapter-guide.js +19 -1
- package/lib/adapters/_bank-base.js +405 -0
- package/lib/adapters/_reading-base.js +315 -0
- package/lib/adapters/audio-ximalaya/index.js +414 -0
- package/lib/adapters/bank-bankcomm/index.js +27 -0
- package/lib/adapters/bank-boc/index.js +26 -0
- package/lib/adapters/bank-cmbc/index.js +26 -0
- package/lib/adapters/bank-icbc/index.js +27 -0
- package/lib/adapters/car-mercedesme/index.js +225 -0
- package/lib/adapters/finance-dcep/index.js +302 -0
- package/lib/adapters/fitness-joyrun/index.js +295 -0
- package/lib/adapters/fitness-keep/index.js +343 -0
- package/lib/adapters/gov-12123/index.js +391 -0
- package/lib/adapters/music-qq/index.js +372 -0
- package/lib/adapters/reading-fanqie/index.js +61 -0
- package/lib/adapters/reading-qimao/index.js +61 -0
- package/lib/adapters/shopping-eleme/index.js +441 -0
- package/lib/adapters/shopping-vipshop/index.js +429 -0
- package/lib/adapters/shopping-xianyu/index.js +454 -0
- package/lib/adapters/social-douban/index.js +564 -0
- package/lib/adapters/travel-didi-consumer/index.js +148 -0
- package/lib/index.js +36 -0
- package/package.json +1 -1
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §12.1 Phase 13+ — 滴滴出行 consumer app (com.sdu.didi.psnger) ride adapter.
|
|
3
|
+
* Device-discovered gap (2026-06-15): distinct from travel-didi (滴滴企业版,
|
|
4
|
+
* com.didi.es.psngr). Same ride→car TravelRecord shape, so it REUSES
|
|
5
|
+
* travel-didi's order mapping helpers; only NAME + the consumer order-centre
|
|
6
|
+
* endpoint differ.
|
|
7
|
+
*
|
|
8
|
+
* BEST-EFFORT: api.udache.com / common.diditaxi.com.cn endpoints are FABRICATED
|
|
9
|
+
* placeholders (overridable via opts.ordersUrl, NOT field-verified — FAMILY-23
|
|
10
|
+
* playbook); snapshot/file-import is the reliable path; cookie path surfaces
|
|
11
|
+
* auth.unverified=true. sensitivity:"medium" (ride start/end addresses).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
"use strict";
|
|
15
|
+
|
|
16
|
+
const fs = require("node:fs");
|
|
17
|
+
const { normalizeTravelRecord } = require("../travel-base");
|
|
18
|
+
const { CookieAuth } = require("../shopping-base");
|
|
19
|
+
const { orderToRecord, extractOrders, parseRecords } = require("../travel-didi");
|
|
20
|
+
|
|
21
|
+
const NAME = "travel-didi-consumer";
|
|
22
|
+
const VERSION = "0.1.0";
|
|
23
|
+
|
|
24
|
+
// Best-effort 滴滴出行 (consumer) order-list endpoint. Overridable via opts.ordersUrl.
|
|
25
|
+
const DIDI_CONSUMER_ORDERS_URL = "https://api.udache.com/gulfstream/api/v1/order/list";
|
|
26
|
+
const DEFAULT_PAGE_SIZE = 20;
|
|
27
|
+
const DEFAULT_MAX_PAGES = 10;
|
|
28
|
+
|
|
29
|
+
class DidiConsumerAdapter {
|
|
30
|
+
constructor(opts = {}) {
|
|
31
|
+
this.account = opts.account || null;
|
|
32
|
+
this._dataPath = opts.dataPath || null;
|
|
33
|
+
this._cookieAuth =
|
|
34
|
+
opts.account && opts.account.cookies ? new CookieAuth({ platform: "didi", cookies: opts.account.cookies }) : null;
|
|
35
|
+
this._fetchFn = typeof opts.fetchFn === "function" ? opts.fetchFn : defaultFetch;
|
|
36
|
+
this._signProvider = typeof opts.signProvider === "function" ? opts.signProvider : null;
|
|
37
|
+
this._ordersUrl =
|
|
38
|
+
typeof opts.ordersUrl === "string" && opts.ordersUrl.length > 0 ? opts.ordersUrl : DIDI_CONSUMER_ORDERS_URL;
|
|
39
|
+
|
|
40
|
+
this.name = NAME;
|
|
41
|
+
this.version = VERSION;
|
|
42
|
+
this.capabilities = ["import:json", "sync:snapshot", "sync:cookie-api", "parse:didi-rides"];
|
|
43
|
+
this.extractMode = "file-import";
|
|
44
|
+
this.rateLimits = {};
|
|
45
|
+
this.dataDisclosure = {
|
|
46
|
+
fields: ["didi:orderId / fromAddress / toAddress / departTime / arriveTime / fare / carType"],
|
|
47
|
+
sensitivity: "medium",
|
|
48
|
+
legalGate: false,
|
|
49
|
+
};
|
|
50
|
+
this._deps = { fs };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async authenticate(ctx = {}) {
|
|
54
|
+
const filePath = (ctx && ctx.inputPath) || ctx.dataPath || this._dataPath;
|
|
55
|
+
if (filePath) {
|
|
56
|
+
try {
|
|
57
|
+
this._deps.fs.accessSync(filePath, this._deps.fs.constants.R_OK);
|
|
58
|
+
} catch (err) {
|
|
59
|
+
return { ok: false, reason: "INPUT_PATH_UNREADABLE", message: `not readable at ${filePath}: ${err.message}` };
|
|
60
|
+
}
|
|
61
|
+
return { ok: true, mode: "snapshot-file" };
|
|
62
|
+
}
|
|
63
|
+
if (this._cookieAuth) {
|
|
64
|
+
const ok = await this._cookieAuth.validate();
|
|
65
|
+
if (!ok) return { ok: false, reason: "INVALID_COOKIE", error: "cookies missing" };
|
|
66
|
+
return { ok: true, account: (this.account && this.account.phone) || null, mode: "cookie", unverified: true };
|
|
67
|
+
}
|
|
68
|
+
return { ok: true, account: null, mode: "ready" };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async healthCheck() {
|
|
72
|
+
if (this._cookieAuth) {
|
|
73
|
+
const r = await this.authenticate();
|
|
74
|
+
return r.ok ? { ok: true, lastChecked: Date.now(), unverified: true } : { ok: false, reason: r.reason, error: r.error };
|
|
75
|
+
}
|
|
76
|
+
return { ok: true, lastChecked: Date.now() };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async *sync(opts = {}) {
|
|
80
|
+
const dataPath = opts.inputPath || opts.dataPath || this._dataPath;
|
|
81
|
+
if (dataPath) {
|
|
82
|
+
if (!this._deps.fs.existsSync(dataPath)) return;
|
|
83
|
+
const text = this._deps.fs.readFileSync(dataPath, "utf-8");
|
|
84
|
+
let records;
|
|
85
|
+
try {
|
|
86
|
+
records = parseRecords(text);
|
|
87
|
+
} catch (err) {
|
|
88
|
+
throw new Error(`DidiConsumerAdapter: parse failed: ${err.message}`);
|
|
89
|
+
}
|
|
90
|
+
for (const r of records) {
|
|
91
|
+
// re-stamp vendor so dedup IDs don't collide with the enterprise adapter
|
|
92
|
+
yield { adapter: NAME, originalId: r.recordId, capturedAt: r.bookedAt || r.departureMs || Date.now(), payload: { record: r } };
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (this._cookieAuth) yield* this._syncViaCookie(opts);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async *_syncViaCookie(opts = {}) {
|
|
100
|
+
if (!(await this._cookieAuth.validate())) return;
|
|
101
|
+
const cookies = this._cookieAuth.toHeader();
|
|
102
|
+
const sinceMs = opts.sinceWatermark != null ? parseInt(String(opts.sinceWatermark), 10) || 0 : Date.now() - 365 * 24 * 3600_000;
|
|
103
|
+
const pageSize = Number.isFinite(opts.pageSize) ? opts.pageSize : DEFAULT_PAGE_SIZE;
|
|
104
|
+
const maxPages = Number.isInteger(opts.maxPages) && opts.maxPages > 0 ? opts.maxPages : DEFAULT_MAX_PAGES;
|
|
105
|
+
const limit = Number.isInteger(opts.limit) && opts.limit > 0 ? opts.limit : Infinity;
|
|
106
|
+
|
|
107
|
+
let emitted = 0;
|
|
108
|
+
let pageIndex = 1;
|
|
109
|
+
while (pageIndex <= maxPages) {
|
|
110
|
+
const query = { pageIndex, pageSize, ts: Date.now() };
|
|
111
|
+
let sign = null;
|
|
112
|
+
if (this._signProvider) sign = await this._signProvider({ url: this._ordersUrl, query, cookies });
|
|
113
|
+
const resp = await this._fetchFn({ url: this._ordersUrl, cookies, query, sign });
|
|
114
|
+
const rides = extractOrders(resp);
|
|
115
|
+
if (!rides.length) break;
|
|
116
|
+
let pageHasNew = false;
|
|
117
|
+
let reachedWatermark = false;
|
|
118
|
+
for (const raw of rides) {
|
|
119
|
+
const rec = orderToRecord(raw, { capturedVia: "cookie-api" });
|
|
120
|
+
if (!rec) continue;
|
|
121
|
+
const ts = rec.departureMs || rec.bookedAt || null;
|
|
122
|
+
if (ts && ts < sinceMs) {
|
|
123
|
+
reachedWatermark = true;
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
pageHasNew = true;
|
|
127
|
+
if (emitted >= limit) return;
|
|
128
|
+
yield { adapter: NAME, originalId: rec.recordId, capturedAt: ts || Date.now(), payload: { record: rec } };
|
|
129
|
+
emitted += 1;
|
|
130
|
+
}
|
|
131
|
+
if (reachedWatermark || !pageHasNew || rides.length < pageSize) break;
|
|
132
|
+
pageIndex += 1;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
normalize(raw) {
|
|
137
|
+
if (!raw || !raw.payload || !raw.payload.record) {
|
|
138
|
+
throw new Error("DidiConsumerAdapter.normalize: raw.payload.record missing");
|
|
139
|
+
}
|
|
140
|
+
return normalizeTravelRecord(raw.payload.record, { adapterName: NAME, adapterVersion: VERSION });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function defaultFetch(_opts) {
|
|
145
|
+
throw new Error("travel-didi-consumer: no fetchFn configured for cookie-api mode");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
module.exports = { DidiConsumerAdapter, NAME, VERSION };
|
package/lib/index.js
CHANGED
|
@@ -48,11 +48,15 @@ const shoppingBase = require("./adapters/shopping-base");
|
|
|
48
48
|
const { TaobaoAdapter } = require("./adapters/shopping-taobao");
|
|
49
49
|
const { JdAdapter } = require("./adapters/shopping-jd");
|
|
50
50
|
const { MeituanAdapter } = require("./adapters/shopping-meituan");
|
|
51
|
+
const { ElemeAdapter } = require("./adapters/shopping-eleme");
|
|
51
52
|
const { PinduoduoAdapter } = require("./adapters/shopping-pinduoduo");
|
|
52
53
|
const { DianpingAdapter } = require("./adapters/shopping-dianping");
|
|
54
|
+
const { XianyuAdapter } = require("./adapters/shopping-xianyu");
|
|
55
|
+
const { VipshopAdapter } = require("./adapters/shopping-vipshop");
|
|
53
56
|
const { BilibiliAdapter } = require("./adapters/social-bilibili");
|
|
54
57
|
const { WeiboAdapter } = require("./adapters/social-weibo");
|
|
55
58
|
const { ZhihuAdapter } = require("./adapters/social-zhihu");
|
|
59
|
+
const { DoubanAdapter } = require("./adapters/social-douban");
|
|
56
60
|
const { BossZhipinAdapter } = require("./adapters/recruit-boss");
|
|
57
61
|
const { CsdnAdapter } = require("./adapters/social-csdn");
|
|
58
62
|
const { DongchediAdapter } = require("./adapters/social-dongchedi");
|
|
@@ -73,6 +77,14 @@ const { QQPcAdapter } = require("./adapters/qq-pc");
|
|
|
73
77
|
const { AppleHealthAdapter } = require("./adapters/apple-health");
|
|
74
78
|
const { NeteaseMusicAdapter } = require("./adapters/netease-music");
|
|
75
79
|
const { KugouMusicAdapter } = require("./adapters/music-kugou");
|
|
80
|
+
const { QQMusicAdapter } = require("./adapters/music-qq");
|
|
81
|
+
const { XimalayaAdapter } = require("./adapters/audio-ximalaya");
|
|
82
|
+
const { FanqieReadingAdapter } = require("./adapters/reading-fanqie");
|
|
83
|
+
const { QimaoReadingAdapter } = require("./adapters/reading-qimao");
|
|
84
|
+
const { JoyrunAdapter } = require("./adapters/fitness-joyrun");
|
|
85
|
+
const { KeepAdapter } = require("./adapters/fitness-keep");
|
|
86
|
+
const { DidiConsumerAdapter } = require("./adapters/travel-didi-consumer");
|
|
87
|
+
const { MercedesMeAdapter } = require("./adapters/car-mercedesme");
|
|
76
88
|
const { IqiyiVideoAdapter } = require("./adapters/video-iqiyi");
|
|
77
89
|
const { TencentVideoAdapter } = require("./adapters/video-tencent");
|
|
78
90
|
const { XiguaVideoAdapter } = require("./adapters/video-xigua");
|
|
@@ -84,6 +96,12 @@ const { CamScannerDocAdapter } = require("./adapters/doc-camscanner");
|
|
|
84
96
|
const { IXiamenAdapter } = require("./adapters/gov-ixiamen");
|
|
85
97
|
const { MeiyouAdapter } = require("./adapters/health-meiyou");
|
|
86
98
|
const { TaxAdapter } = require("./adapters/gov-tax");
|
|
99
|
+
const { CmbcBankAdapter } = require("./adapters/bank-cmbc");
|
|
100
|
+
const { BocBankAdapter } = require("./adapters/bank-boc");
|
|
101
|
+
const { BankcommBankAdapter } = require("./adapters/bank-bankcomm");
|
|
102
|
+
const { IcbcBankAdapter } = require("./adapters/bank-icbc");
|
|
103
|
+
const { DcepAdapter } = require("./adapters/finance-dcep");
|
|
104
|
+
const { Tmri12123Adapter } = require("./adapters/gov-12123");
|
|
87
105
|
const { DingTalkPcAdapter } = require("./adapters/dingtalk-pc");
|
|
88
106
|
const { FeishuPcAdapter } = require("./adapters/feishu-pc");
|
|
89
107
|
const { WeWorkPcAdapter } = require("./adapters/wework-pc");
|
|
@@ -296,13 +314,17 @@ module.exports = {
|
|
|
296
314
|
TaobaoAdapter,
|
|
297
315
|
JdAdapter,
|
|
298
316
|
MeituanAdapter,
|
|
317
|
+
ElemeAdapter,
|
|
299
318
|
PinduoduoAdapter,
|
|
300
319
|
DianpingAdapter,
|
|
320
|
+
XianyuAdapter,
|
|
321
|
+
VipshopAdapter,
|
|
301
322
|
|
|
302
323
|
// Phase 13+ — long-tail social + messaging (借 sjqz parsers)
|
|
303
324
|
BilibiliAdapter,
|
|
304
325
|
WeiboAdapter,
|
|
305
326
|
ZhihuAdapter,
|
|
327
|
+
DoubanAdapter,
|
|
306
328
|
BossZhipinAdapter,
|
|
307
329
|
CsdnAdapter,
|
|
308
330
|
DongchediAdapter,
|
|
@@ -322,6 +344,14 @@ module.exports = {
|
|
|
322
344
|
AppleHealthAdapter,
|
|
323
345
|
NeteaseMusicAdapter,
|
|
324
346
|
KugouMusicAdapter,
|
|
347
|
+
QQMusicAdapter,
|
|
348
|
+
XimalayaAdapter,
|
|
349
|
+
FanqieReadingAdapter,
|
|
350
|
+
QimaoReadingAdapter,
|
|
351
|
+
JoyrunAdapter,
|
|
352
|
+
KeepAdapter,
|
|
353
|
+
DidiConsumerAdapter,
|
|
354
|
+
MercedesMeAdapter,
|
|
325
355
|
IqiyiVideoAdapter,
|
|
326
356
|
TencentVideoAdapter,
|
|
327
357
|
XiguaVideoAdapter,
|
|
@@ -333,6 +363,12 @@ module.exports = {
|
|
|
333
363
|
IXiamenAdapter,
|
|
334
364
|
MeiyouAdapter,
|
|
335
365
|
TaxAdapter,
|
|
366
|
+
CmbcBankAdapter,
|
|
367
|
+
BocBankAdapter,
|
|
368
|
+
BankcommBankAdapter,
|
|
369
|
+
IcbcBankAdapter,
|
|
370
|
+
DcepAdapter,
|
|
371
|
+
Tmri12123Adapter,
|
|
336
372
|
DingTalkPcAdapter,
|
|
337
373
|
FeishuPcAdapter,
|
|
338
374
|
WeWorkPcAdapter,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chainlesschain/personal-data-hub",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.24",
|
|
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",
|