@chainlesschain/personal-data-hub 0.3.1 → 0.3.7
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/email-adapter-snapshot.test.js +237 -0
- package/__tests__/adapters/email-adapter.test.js +1 -1
- package/__tests__/adapters/email-pdf-extractor.test.js +1 -1
- package/__tests__/adapters/email-retry-progress.test.js +1 -1
- package/__tests__/adapters/email-templates.test.js +1 -1
- package/__tests__/adapters/social-bilibili-adb-api-client.test.js +721 -0
- package/__tests__/adapters/social-bilibili-adb-chromium-cookies-reader.test.js +346 -0
- package/__tests__/adapters/social-bilibili-adb-collector.test.js +284 -0
- package/__tests__/adapters/social-bilibili-adb-cookies-extension.test.js +343 -0
- package/__tests__/adapters/social-bilibili-adb-snapshot-builder.test.js +296 -0
- package/__tests__/adapters/social-douyin-adb-collector.test.js +254 -0
- package/__tests__/adapters/social-douyin-adb-im-db-parser.test.js +304 -0
- package/__tests__/adapters/social-douyin-adb-snapshot-builder.test.js +216 -0
- package/__tests__/adapters/social-kuaishou-adb-api-client.test.js +432 -0
- package/__tests__/adapters/social-kuaishou-adb-collector.test.js +276 -0
- package/__tests__/adapters/social-kuaishou-adb-cookies-extension.test.js +141 -0
- package/__tests__/adapters/social-kuaishou-adb-snapshot-builder.test.js +178 -0
- package/__tests__/adapters/social-toutiao-adb-api-client.test.js +537 -0
- package/__tests__/adapters/social-toutiao-adb-collector.test.js +285 -0
- package/__tests__/adapters/social-toutiao-adb-cookies-extension.test.js +163 -0
- package/__tests__/adapters/social-toutiao-adb-snapshot-builder.test.js +196 -0
- package/__tests__/adapters/social-weibo-adb-api-client.test.js +362 -0
- package/__tests__/adapters/social-weibo-adb-collector.test.js +201 -0
- package/__tests__/adapters/social-weibo-adb-snapshot-builder.test.js +189 -0
- package/__tests__/adapters/social-xiaohongshu-adb-collector.test.js +207 -0
- package/__tests__/adapters/social-xiaohongshu-adb-sign-provider-injection.test.js +351 -0
- package/__tests__/adapters/social-xiaohongshu-adb-sign.test.js +130 -0
- package/__tests__/adapters/system-data-android.test.js +32 -1
- package/__tests__/longtail-adapters.test.js +15 -2
- package/__tests__/shopping-adapters.test.js +96 -0
- package/__tests__/sign-providers.test.js +62 -0
- package/__tests__/travel-adapters.test.js +66 -0
- package/__tests__/whatsapp-adapter.test.js +5 -2
- package/lib/adapters/browser-history-chrome/chrome-db-reader.js +11 -1
- package/lib/adapters/email-imap/email-adapter.js +224 -17
- package/lib/adapters/messaging-telegram/index.js +15 -12
- package/lib/adapters/messaging-whatsapp/index.js +15 -12
- package/lib/adapters/shopping-taobao/index.js +161 -21
- package/lib/adapters/social-bilibili-adb/api-client.js +555 -0
- package/lib/adapters/social-bilibili-adb/chromium-cookies-reader.js +296 -0
- package/lib/adapters/social-bilibili-adb/collector.js +190 -0
- package/lib/adapters/social-bilibili-adb/cookies-extension.js +250 -0
- package/lib/adapters/social-bilibili-adb/index.js +51 -0
- package/lib/adapters/social-bilibili-adb/snapshot-builder.js +197 -0
- package/lib/adapters/social-douyin/index.js +4 -0
- package/lib/adapters/social-douyin-adb/collector.js +165 -0
- package/lib/adapters/social-douyin-adb/db-extension.js +281 -0
- package/lib/adapters/social-douyin-adb/im-db-parser.js +287 -0
- package/lib/adapters/social-douyin-adb/index.js +57 -0
- package/lib/adapters/social-douyin-adb/snapshot-builder.js +174 -0
- package/lib/adapters/social-kuaishou-adb/api-client.js +397 -0
- package/lib/adapters/social-kuaishou-adb/collector.js +196 -0
- package/lib/adapters/social-kuaishou-adb/cookies-extension.js +261 -0
- package/lib/adapters/social-kuaishou-adb/index.js +53 -0
- package/lib/adapters/social-kuaishou-adb/snapshot-builder.js +145 -0
- package/lib/adapters/social-toutiao-adb/api-client.js +377 -0
- package/lib/adapters/social-toutiao-adb/collector.js +200 -0
- package/lib/adapters/social-toutiao-adb/cookies-extension.js +266 -0
- package/lib/adapters/social-toutiao-adb/index.js +52 -0
- package/lib/adapters/social-toutiao-adb/snapshot-builder.js +148 -0
- package/lib/adapters/social-weibo-adb/api-client.js +281 -0
- package/lib/adapters/social-weibo-adb/collector.js +169 -0
- package/lib/adapters/social-weibo-adb/cookies-extension.js +251 -0
- package/lib/adapters/social-weibo-adb/index.js +55 -0
- package/lib/adapters/social-weibo-adb/snapshot-builder.js +145 -0
- package/lib/adapters/social-xiaohongshu-adb/api-client.js +309 -0
- package/lib/adapters/social-xiaohongshu-adb/collector.js +209 -0
- package/lib/adapters/social-xiaohongshu-adb/cookies-extension.js +211 -0
- package/lib/adapters/social-xiaohongshu-adb/index.js +50 -0
- package/lib/adapters/social-xiaohongshu-adb/sign.js +90 -0
- package/lib/adapters/social-xiaohongshu-adb/snapshot-builder.js +126 -0
- package/lib/adapters/system-data-android/adapter.js +77 -3
- package/lib/adapters/travel-amap/index.js +16 -10
- package/lib/adapters/travel-ctrip/index.js +25 -9
- package/lib/adapters/vscode/vscode-reader.js +7 -1
- package/lib/sign-providers/index.js +20 -0
- package/lib/sign-providers/interface.js +82 -0
- package/lib/sign-providers/null-sign-provider.js +30 -0
- package/package.json +10 -1
|
@@ -18,28 +18,36 @@
|
|
|
18
18
|
|
|
19
19
|
"use strict";
|
|
20
20
|
|
|
21
|
+
const fs = require("node:fs");
|
|
21
22
|
const { normalizeOrderRecord, CookieAuth } = require("../shopping-base");
|
|
22
23
|
|
|
23
24
|
const NAME = "shopping-taobao";
|
|
24
|
-
const VERSION = "0.
|
|
25
|
+
const VERSION = "0.6.0"; // §2.4d snapshot mode for Android in-APK cc
|
|
26
|
+
const SNAPSHOT_SCHEMA_VERSION = 1;
|
|
27
|
+
|
|
28
|
+
const KIND_ORDER = "order";
|
|
29
|
+
const VALID_SNAPSHOT_KINDS = Object.freeze([KIND_ORDER]);
|
|
25
30
|
|
|
26
31
|
const TAOBAO_ORDERS_URL = "https://h5.m.taobao.com/mlapp/olist.html";
|
|
27
32
|
|
|
28
33
|
class TaobaoAdapter {
|
|
29
34
|
constructor(opts = {}) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
this.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
// §2.4d v0.2 — account.userId OPTIONAL (mirror shopping-jd/meituan/pinduoduo
|
|
36
|
+
// dual-mode). Snapshot mode is stateless; cookie mode requires it; checked
|
|
37
|
+
// at sync time, not construction. Earlier strict ctor blocked auto-register
|
|
38
|
+
// at boot → user-driven HTML import worked but JSON snapshot path didn't.
|
|
39
|
+
this.account = opts.account || null;
|
|
40
|
+
this._cookieAuth = opts.account
|
|
41
|
+
? new CookieAuth({
|
|
42
|
+
platform: "taobao",
|
|
43
|
+
cookies: opts.account.cookies || "",
|
|
44
|
+
})
|
|
45
|
+
: null;
|
|
38
46
|
this._fetchFn = typeof opts.fetchFn === "function" ? opts.fetchFn : defaultFetch;
|
|
39
47
|
|
|
40
48
|
this.name = NAME;
|
|
41
49
|
this.version = VERSION;
|
|
42
|
-
this.capabilities = ["sync:cookie-api", "parse:taobao-orders"];
|
|
50
|
+
this.capabilities = ["sync:snapshot", "sync:cookie-api", "parse:taobao-orders"];
|
|
43
51
|
this.extractMode = "web-api";
|
|
44
52
|
this.rateLimits = { perMinute: 6, perDay: 200 }; // respect Taobao风控
|
|
45
53
|
this.dataDisclosure = {
|
|
@@ -48,23 +56,132 @@ class TaobaoAdapter {
|
|
|
48
56
|
],
|
|
49
57
|
sensitivity: "high",
|
|
50
58
|
legalGate: false,
|
|
59
|
+
defaultInclude: { order: true },
|
|
51
60
|
};
|
|
61
|
+
|
|
62
|
+
// _deps injection seam — vi.mock fs doesn't intercept inlined CJS require.
|
|
63
|
+
this._deps = { fs };
|
|
52
64
|
}
|
|
53
65
|
|
|
54
|
-
async authenticate() {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
66
|
+
async authenticate(ctx = {}) {
|
|
67
|
+
if (ctx && typeof ctx.inputPath === "string" && ctx.inputPath.length > 0) {
|
|
68
|
+
try {
|
|
69
|
+
this._deps.fs.accessSync(ctx.inputPath, this._deps.fs.constants.R_OK);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
return {
|
|
72
|
+
ok: false,
|
|
73
|
+
reason: "INPUT_PATH_UNREADABLE",
|
|
74
|
+
message: `snapshot not readable at ${ctx.inputPath}: ${err.message}`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return { ok: true, mode: "snapshot-file" };
|
|
78
|
+
}
|
|
79
|
+
if (this._cookieAuth) {
|
|
80
|
+
const ok = await this._cookieAuth.validate();
|
|
81
|
+
if (!ok) return { ok: false, reason: "INVALID_COOKIE", error: "cookies missing or empty" };
|
|
82
|
+
if (!this.account || !this.account.userId) {
|
|
83
|
+
return { ok: false, reason: "NO_ACCOUNT_USERID", message: "cookie mode requires account.userId" };
|
|
84
|
+
}
|
|
85
|
+
return { ok: true, account: this.account.userId, mode: "cookie" };
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
ok: false,
|
|
89
|
+
reason: "NO_INPUT",
|
|
90
|
+
message: "TaobaoAdapter.authenticate: needs opts.inputPath (snapshot mode) OR opts.account.cookies (cookie mode)",
|
|
91
|
+
};
|
|
58
92
|
}
|
|
59
93
|
|
|
60
94
|
async healthCheck() {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
95
|
+
if (this._cookieAuth) {
|
|
96
|
+
const r = await this.authenticate();
|
|
97
|
+
return r.ok
|
|
98
|
+
? { ok: true, lastChecked: Date.now() }
|
|
99
|
+
: { ok: false, reason: r.reason, error: r.error };
|
|
100
|
+
}
|
|
101
|
+
return { ok: true, lastChecked: Date.now() };
|
|
65
102
|
}
|
|
66
103
|
|
|
67
104
|
async *sync(opts = {}) {
|
|
105
|
+
if (typeof opts.inputPath === "string" && opts.inputPath.length > 0) {
|
|
106
|
+
yield* this._syncViaSnapshot(opts);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (this._cookieAuth) {
|
|
110
|
+
yield* this._syncViaCookie(opts);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
throw new Error(
|
|
114
|
+
"TaobaoAdapter.sync: needs opts.inputPath (snapshot mode, Android in-APK cc) OR opts.account.cookies (cookie mode)",
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async *_syncViaSnapshot(opts) {
|
|
119
|
+
const raw = this._deps.fs.readFileSync(opts.inputPath, "utf-8");
|
|
120
|
+
let snapshot;
|
|
121
|
+
try {
|
|
122
|
+
snapshot = JSON.parse(raw);
|
|
123
|
+
} catch (err) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`shopping-taobao.sync: snapshot must be JSON (v0.3 will add HTML parsing for SAF-exported pages). Got parse error: ${err.message}`,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
if (
|
|
129
|
+
!snapshot ||
|
|
130
|
+
typeof snapshot !== "object" ||
|
|
131
|
+
snapshot.schemaVersion !== SNAPSHOT_SCHEMA_VERSION
|
|
132
|
+
) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
`shopping-taobao.sync: snapshot schemaVersion mismatch (got ${snapshot && snapshot.schemaVersion}, expected ${SNAPSHOT_SCHEMA_VERSION})`,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
const fallbackCapturedAt =
|
|
138
|
+
Number.isFinite(snapshot.snapshottedAt) && snapshot.snapshottedAt > 0
|
|
139
|
+
? Math.floor(snapshot.snapshottedAt)
|
|
140
|
+
: Date.now();
|
|
141
|
+
const account =
|
|
142
|
+
snapshot.account && typeof snapshot.account === "object"
|
|
143
|
+
? snapshot.account
|
|
144
|
+
: null;
|
|
145
|
+
const include = opts.include || {};
|
|
146
|
+
const limit =
|
|
147
|
+
Number.isInteger(opts.limit) && opts.limit > 0 ? opts.limit : Infinity;
|
|
148
|
+
|
|
149
|
+
const events = Array.isArray(snapshot.events) ? snapshot.events : [];
|
|
150
|
+
let emitted = 0;
|
|
151
|
+
for (const ev of events) {
|
|
152
|
+
if (emitted >= limit) return;
|
|
153
|
+
if (!ev || typeof ev !== "object") continue;
|
|
154
|
+
const kind = ev.kind;
|
|
155
|
+
if (!VALID_SNAPSHOT_KINDS.includes(kind)) continue;
|
|
156
|
+
if (include[kind] === false) continue;
|
|
157
|
+
|
|
158
|
+
const capturedAt =
|
|
159
|
+
parseTime(ev.capturedAt) ||
|
|
160
|
+
parseTime(ev.placedAt) ||
|
|
161
|
+
parseTime(ev.paidAt) ||
|
|
162
|
+
fallbackCapturedAt;
|
|
163
|
+
const id =
|
|
164
|
+
(typeof ev.id === "string" && ev.id.length > 0 && ev.id) ||
|
|
165
|
+
ev.orderId ||
|
|
166
|
+
null;
|
|
167
|
+
|
|
168
|
+
yield {
|
|
169
|
+
adapter: NAME,
|
|
170
|
+
kind,
|
|
171
|
+
originalId: stableOriginalId(kind, id),
|
|
172
|
+
capturedAt,
|
|
173
|
+
payload: { ...ev, account },
|
|
174
|
+
};
|
|
175
|
+
emitted += 1;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async *_syncViaCookie(opts = {}) {
|
|
180
|
+
if (!this.account || !this.account.userId) {
|
|
181
|
+
throw new Error(
|
|
182
|
+
"TaobaoAdapter._syncViaCookie: account.userId required (set via new TaobaoAdapter({ account: { userId } }))",
|
|
183
|
+
);
|
|
184
|
+
}
|
|
68
185
|
if (!(await this._cookieAuth.validate())) return;
|
|
69
186
|
const sinceMs = opts.sinceWatermark != null
|
|
70
187
|
? parseWatermarkMs(opts.sinceWatermark)
|
|
@@ -97,16 +214,39 @@ class TaobaoAdapter {
|
|
|
97
214
|
}
|
|
98
215
|
|
|
99
216
|
normalize(raw) {
|
|
100
|
-
if (!raw || !raw.payload
|
|
101
|
-
throw new Error("TaobaoAdapter.normalize: raw.payload
|
|
217
|
+
if (!raw || !raw.payload) {
|
|
218
|
+
throw new Error("TaobaoAdapter.normalize: raw.payload missing");
|
|
219
|
+
}
|
|
220
|
+
// Snapshot mode payload is the raw event spread + account; cookie mode
|
|
221
|
+
// wraps a normalized record under payload.record. Dispatch on shape.
|
|
222
|
+
if (raw.payload.record) {
|
|
223
|
+
return normalizeOrderRecord(raw.payload.record, {
|
|
224
|
+
adapterName: NAME,
|
|
225
|
+
adapterVersion: VERSION,
|
|
226
|
+
});
|
|
102
227
|
}
|
|
103
|
-
|
|
228
|
+
// Snapshot path: the Android collector ships records that already match
|
|
229
|
+
// the OrderRecord shape (vendorId/orderId/placedAt/...). Pass through.
|
|
230
|
+
return normalizeOrderRecord(raw.payload, {
|
|
104
231
|
adapterName: NAME,
|
|
105
232
|
adapterVersion: VERSION,
|
|
106
233
|
});
|
|
107
234
|
}
|
|
108
235
|
}
|
|
109
236
|
|
|
237
|
+
function parseTime(v) {
|
|
238
|
+
if (Number.isFinite(v) && v > 0) return v < 1e12 ? v * 1000 : v;
|
|
239
|
+
if (typeof v === "string") {
|
|
240
|
+
const t = Date.parse(v);
|
|
241
|
+
if (Number.isFinite(t)) return t;
|
|
242
|
+
}
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function stableOriginalId(kind, id) {
|
|
247
|
+
return id ? `taobao:${kind}:${id}` : `taobao:${kind}:unknown-${Date.now()}`;
|
|
248
|
+
}
|
|
249
|
+
|
|
110
250
|
function orderToRecord(o) {
|
|
111
251
|
if (!o || typeof o !== "object") return null;
|
|
112
252
|
const orderId = o.bizOrderId || o.orderId || o.id;
|