@chainlesschain/personal-data-hub 0.4.7 → 0.4.23
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/biz-tianyancha.test.js +159 -0
- package/__tests__/adapters/doc-baidu-netdisk.test.js +102 -0
- package/__tests__/adapters/doc-camscanner.test.js +147 -0
- package/__tests__/adapters/doc-platforms.test.js +177 -0
- package/__tests__/adapters/gov-ixiamen.test.js +150 -0
- package/__tests__/adapters/gov-tax.test.js +135 -0
- package/__tests__/adapters/health-meiyou.test.js +125 -0
- package/__tests__/adapters/music-kugou.test.js +187 -0
- package/__tests__/adapters/recruit-boss.test.js +180 -0
- package/__tests__/adapters/shopping-dianping.test.js +239 -0
- package/__tests__/adapters/social-csdn.test.js +175 -0
- package/__tests__/adapters/social-dongchedi.test.js +165 -0
- package/__tests__/adapters/social-zhihu.test.js +246 -0
- package/__tests__/adapters/travel-ctrip.test.js +175 -1
- package/__tests__/adapters/travel-didi.test.js +204 -0
- package/__tests__/adapters/travel-tongcheng.test.js +289 -0
- package/__tests__/adapters/video-platforms.test.js +152 -0
- package/__tests__/adapters/video-xigua.test.js +106 -0
- package/__tests__/adapters/wework-pc.test.js +124 -0
- package/lib/adapter-guide.js +25 -3
- package/lib/adapters/_document-base.js +370 -0
- package/lib/adapters/_video-base.js +331 -0
- package/lib/adapters/biz-tianyancha/index.js +348 -0
- package/lib/adapters/doc-baidu-netdisk/index.js +91 -0
- package/lib/adapters/doc-camscanner/index.js +102 -0
- package/lib/adapters/doc-tencent-docs/index.js +94 -0
- package/lib/adapters/doc-wps/index.js +77 -0
- package/lib/adapters/gov-ixiamen/index.js +380 -0
- package/lib/adapters/gov-tax/index.js +451 -0
- package/lib/adapters/health-meiyou/index.js +393 -0
- package/lib/adapters/music-kugou/index.js +418 -0
- package/lib/adapters/recruit-boss/index.js +442 -0
- package/lib/adapters/shopping-dianping/index.js +473 -0
- package/lib/adapters/social-csdn/index.js +444 -0
- package/lib/adapters/social-dongchedi/index.js +360 -0
- package/lib/adapters/social-zhihu/index.js +488 -0
- package/lib/adapters/travel-ctrip/index.js +255 -40
- package/lib/adapters/travel-didi/index.js +327 -0
- package/lib/adapters/travel-tongcheng/index.js +393 -0
- package/lib/adapters/video-iqiyi/index.js +75 -0
- package/lib/adapters/video-tencent/index.js +78 -0
- package/lib/adapters/video-xigua/index.js +68 -0
- package/lib/adapters/wework-pc/index.js +31 -0
- package/lib/index.js +40 -0
- package/package.json +1 -1
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §12.1 Phase 13+ ⭐⭐ — i 厦门 (com.xmgov.xmapp) adapter, "本地政务".
|
|
3
|
+
*
|
|
4
|
+
* ⚠️ BEST-EFFORT SCAFFOLD (user-requested). i 厦门 is a local-government
|
|
5
|
+
* super-app (社保 / 公积金 / 医保 / 政务办事 / 预约) behind real-name gov SSO.
|
|
6
|
+
* Unlike the document / shopping / travel adapters it has **no verifiable
|
|
7
|
+
* public API** — the cookie-api endpoint below is a FABRICATED placeholder
|
|
8
|
+
* (FAMILY-23 playbook: best-effort, overridable via opts.listUrl, NOT
|
|
9
|
+
* field-verified) and cannot actually authenticate without gov real-name
|
|
10
|
+
* login. The reliable path is therefore **snapshot mode** (the app / a manual
|
|
11
|
+
* export produces a JSON of the user's 办事记录). The cookie path is kept as a
|
|
12
|
+
* seam so it can be wired once a real device confirms the endpoint + sign.
|
|
13
|
+
*
|
|
14
|
+
* Personal footprint modelled: 政务办事记录 (government-service handling). Each
|
|
15
|
+
* record → an INTERACTION event ("办理: <服务名>") + a Topic for the service
|
|
16
|
+
* category (社保 / 公积金 / 医保 / ...) so the vault can group which kinds of
|
|
17
|
+
* civic services the user used. High sensitivity + legalGate (gov real-name
|
|
18
|
+
* data) — first collection requires explicit legal confirmation.
|
|
19
|
+
*
|
|
20
|
+
* 1. snapshot mode (opts.inputPath): JSON schemaVersion 1, stateless.
|
|
21
|
+
* 2. cookie-api mode (opts.account.cookies): best-effort, unverified —
|
|
22
|
+
* paginate the handle-list via injected fetchFn; signProvider seam for
|
|
23
|
+
* the gov gateway signature; best-effort unsigned when absent.
|
|
24
|
+
*
|
|
25
|
+
* Snapshot schema (schemaVersion 1):
|
|
26
|
+
* {
|
|
27
|
+
* "schemaVersion": 1, "snapshottedAt": <ms>,
|
|
28
|
+
* "account": { "userId": "...", "name": "..." },
|
|
29
|
+
* "events": [
|
|
30
|
+
* { "kind": "service", "id": "svc-<id>", "serviceId": "...",
|
|
31
|
+
* "serviceName": "城乡居民医保缴费", "category": "医保",
|
|
32
|
+
* "handledTime": <s|ms>, "status": "已办结", "dept": "厦门市医保局" }
|
|
33
|
+
* ]
|
|
34
|
+
* }
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
"use strict";
|
|
38
|
+
|
|
39
|
+
const fs = require("node:fs");
|
|
40
|
+
const { newId } = require("../../ids");
|
|
41
|
+
const { ENTITY_TYPES, EVENT_SUBTYPES, CAPTURED_BY } = require("../../constants");
|
|
42
|
+
const { CookieAuth } = require("../shopping-base");
|
|
43
|
+
|
|
44
|
+
const NAME = "gov-ixiamen";
|
|
45
|
+
const VERSION = "0.1.0";
|
|
46
|
+
const SNAPSHOT_SCHEMA_VERSION = 1;
|
|
47
|
+
|
|
48
|
+
const KIND_SERVICE = "service";
|
|
49
|
+
const VALID_SNAPSHOT_KINDS = Object.freeze([KIND_SERVICE]);
|
|
50
|
+
|
|
51
|
+
// FABRICATED best-effort handle-list endpoint — NOT field-verified.
|
|
52
|
+
// Overridable via opts.listUrl once a real device confirms the gov gateway.
|
|
53
|
+
const IXIAMEN_LIST_URL = "https://app.ixm.gov.cn/api/v1/handle/list";
|
|
54
|
+
const PAGE_SIZE = 20;
|
|
55
|
+
|
|
56
|
+
// Coarse service-category keyword map → grouping Topic name. Best-effort; the
|
|
57
|
+
// raw `category` (if present) always wins.
|
|
58
|
+
const CATEGORY_KEYWORDS = [
|
|
59
|
+
["社保", /社保|社会保险|养老保险|失业|工伤/],
|
|
60
|
+
["公积金", /公积金/],
|
|
61
|
+
["医保", /医保|医疗保险|门诊|住院报销/],
|
|
62
|
+
["户籍", /户籍|户口|身份证|居住证/],
|
|
63
|
+
["车驾管", /驾驶证|行驶证|车辆|违章|车驾管/],
|
|
64
|
+
["教育", /入学|学籍|教育|招生|学位/],
|
|
65
|
+
["民政", /婚姻|结婚|离婚|低保|殡葬|民政/],
|
|
66
|
+
["税务", /纳税|个税|税务|发票/],
|
|
67
|
+
["出行", /公交|地铁|出行|交通卡/],
|
|
68
|
+
["证照", /营业执照|证照|许可|备案/],
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
function inferCategory(name, explicit) {
|
|
72
|
+
if (typeof explicit === "string" && explicit.trim()) return explicit.trim();
|
|
73
|
+
const s = String(name || "");
|
|
74
|
+
for (const [cat, re] of CATEGORY_KEYWORDS) {
|
|
75
|
+
if (re.test(s)) return cat;
|
|
76
|
+
}
|
|
77
|
+
return "其他政务";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function parseTime(v) {
|
|
81
|
+
if (Number.isFinite(v)) return v > 1e12 ? v : v >= 1e9 ? v * 1000 : v;
|
|
82
|
+
if (typeof v === "string") {
|
|
83
|
+
if (/^\d+$/.test(v)) {
|
|
84
|
+
const n = parseInt(v, 10);
|
|
85
|
+
return n > 1e12 ? n : n >= 1e9 ? n * 1000 : n;
|
|
86
|
+
}
|
|
87
|
+
const t = Date.parse(v);
|
|
88
|
+
return Number.isFinite(t) ? t : null;
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function stableOriginalId(id) {
|
|
94
|
+
const safe =
|
|
95
|
+
(typeof id === "string" && id.length > 0 && id) ||
|
|
96
|
+
(typeof id === "number" && Number.isFinite(id) && String(id)) ||
|
|
97
|
+
`unknown-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
98
|
+
return `ixiamen:service:${safe}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Raw service record (snapshot or cookie shape) → canonical fields. */
|
|
102
|
+
function mapService(raw) {
|
|
103
|
+
if (!raw || typeof raw !== "object") return null;
|
|
104
|
+
const serviceId =
|
|
105
|
+
raw.serviceId || raw.service_id || raw.id || raw.bizId || raw.biz_id || raw.orderNo;
|
|
106
|
+
if (!serviceId) return null;
|
|
107
|
+
const serviceName =
|
|
108
|
+
raw.serviceName || raw.service_name || raw.name || raw.title || raw.itemName || "(未命名事项)";
|
|
109
|
+
return {
|
|
110
|
+
serviceId: String(serviceId),
|
|
111
|
+
serviceName: String(serviceName),
|
|
112
|
+
category: inferCategory(serviceName, raw.category || raw.categoryName || raw.type),
|
|
113
|
+
handledMs: parseTime(
|
|
114
|
+
raw.handledTime || raw.handle_time || raw.handledAt || raw.createTime || raw.create_time || raw.time,
|
|
115
|
+
),
|
|
116
|
+
status: raw.status || raw.statusName || raw.state || null,
|
|
117
|
+
dept: raw.dept || raw.deptName || raw.department || raw.org || raw.handleOrg || null,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function extractList(resp) {
|
|
122
|
+
if (!resp || typeof resp !== "object") return [];
|
|
123
|
+
if (Array.isArray(resp.list)) return resp.list;
|
|
124
|
+
if (Array.isArray(resp.data)) return resp.data;
|
|
125
|
+
const d = resp.data;
|
|
126
|
+
if (d && typeof d === "object") {
|
|
127
|
+
if (Array.isArray(d.list)) return d.list;
|
|
128
|
+
if (Array.isArray(d.records)) return d.records;
|
|
129
|
+
if (Array.isArray(d.result)) return d.result;
|
|
130
|
+
}
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
class IXiamenAdapter {
|
|
135
|
+
constructor(opts = {}) {
|
|
136
|
+
this.account = opts.account || null;
|
|
137
|
+
this._cookieAuth =
|
|
138
|
+
opts.account && opts.account.cookies
|
|
139
|
+
? new CookieAuth({ platform: "ixiamen", cookies: opts.account.cookies })
|
|
140
|
+
: null;
|
|
141
|
+
this._fetchFn = typeof opts.fetchFn === "function" ? opts.fetchFn : defaultFetch;
|
|
142
|
+
this._signProvider =
|
|
143
|
+
typeof opts.signProvider === "function" ? opts.signProvider : null;
|
|
144
|
+
this._listUrl =
|
|
145
|
+
typeof opts.listUrl === "string" && opts.listUrl.length > 0
|
|
146
|
+
? opts.listUrl
|
|
147
|
+
: IXIAMEN_LIST_URL;
|
|
148
|
+
|
|
149
|
+
this.name = NAME;
|
|
150
|
+
this.version = VERSION;
|
|
151
|
+
this.capabilities = ["sync:snapshot", "sync:cookie-api", "parse:ixiamen-service"];
|
|
152
|
+
this.extractMode = "web-api";
|
|
153
|
+
this.rateLimits = { perMinute: 6, perDay: 100 };
|
|
154
|
+
this.dataDisclosure = {
|
|
155
|
+
fields: ["ixiamen:service (serviceName / category / handledTime / status / dept)"],
|
|
156
|
+
// Gov real-name service records are sensitive personal data.
|
|
157
|
+
sensitivity: "high",
|
|
158
|
+
legalGate: true,
|
|
159
|
+
defaultInclude: { service: true },
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
this._deps = { fs };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async authenticate(ctx = {}) {
|
|
166
|
+
if (ctx && typeof ctx.inputPath === "string" && ctx.inputPath.length > 0) {
|
|
167
|
+
try {
|
|
168
|
+
this._deps.fs.accessSync(ctx.inputPath, this._deps.fs.constants.R_OK);
|
|
169
|
+
} catch (err) {
|
|
170
|
+
return {
|
|
171
|
+
ok: false,
|
|
172
|
+
reason: "INPUT_PATH_UNREADABLE",
|
|
173
|
+
message: `snapshot not readable at ${ctx.inputPath}: ${err.message}`,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
return { ok: true, mode: "snapshot-file" };
|
|
177
|
+
}
|
|
178
|
+
if (this._cookieAuth) {
|
|
179
|
+
const ok = await this._cookieAuth.validate();
|
|
180
|
+
if (!ok) return { ok: false, reason: "INVALID_COOKIE", error: "cookies missing" };
|
|
181
|
+
return {
|
|
182
|
+
ok: true,
|
|
183
|
+
account: (this.account && this.account.userId) || null,
|
|
184
|
+
mode: "cookie",
|
|
185
|
+
// Honest signal: live path is unverified for this gov source.
|
|
186
|
+
unverified: true,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
ok: false,
|
|
191
|
+
reason: "NO_INPUT",
|
|
192
|
+
message:
|
|
193
|
+
"gov-ixiamen.authenticate: needs opts.inputPath (snapshot mode) OR opts.account.cookies (cookie-api mode, best-effort/unverified)",
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async healthCheck() {
|
|
198
|
+
if (this._cookieAuth) {
|
|
199
|
+
const r = await this.authenticate();
|
|
200
|
+
return r.ok
|
|
201
|
+
? { ok: true, lastChecked: Date.now(), unverified: true }
|
|
202
|
+
: { ok: false, reason: r.reason, error: r.error };
|
|
203
|
+
}
|
|
204
|
+
return { ok: true, lastChecked: Date.now() };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async *sync(opts = {}) {
|
|
208
|
+
if (typeof opts.inputPath === "string" && opts.inputPath.length > 0) {
|
|
209
|
+
yield* this._syncViaSnapshot(opts);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (this._cookieAuth) {
|
|
213
|
+
yield* this._syncViaCookie(opts);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
throw new Error(
|
|
217
|
+
"gov-ixiamen.sync: needs opts.inputPath (snapshot mode) OR opts.account.cookies (cookie-api mode)",
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async *_syncViaSnapshot(opts) {
|
|
222
|
+
const raw = this._deps.fs.readFileSync(opts.inputPath, "utf-8");
|
|
223
|
+
const snapshot = JSON.parse(raw);
|
|
224
|
+
if (
|
|
225
|
+
!snapshot ||
|
|
226
|
+
typeof snapshot !== "object" ||
|
|
227
|
+
snapshot.schemaVersion !== SNAPSHOT_SCHEMA_VERSION
|
|
228
|
+
) {
|
|
229
|
+
throw new Error(
|
|
230
|
+
`gov-ixiamen.sync: snapshot schemaVersion mismatch (got ${snapshot && snapshot.schemaVersion}, expected ${SNAPSHOT_SCHEMA_VERSION})`,
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
const fallbackCapturedAt =
|
|
234
|
+
Number.isFinite(snapshot.snapshottedAt) && snapshot.snapshottedAt > 0
|
|
235
|
+
? Math.floor(snapshot.snapshottedAt)
|
|
236
|
+
: Date.now();
|
|
237
|
+
const account =
|
|
238
|
+
snapshot.account && typeof snapshot.account === "object" ? snapshot.account : null;
|
|
239
|
+
const include = opts.include || {};
|
|
240
|
+
const limit = Number.isInteger(opts.limit) && opts.limit > 0 ? opts.limit : Infinity;
|
|
241
|
+
|
|
242
|
+
const events = Array.isArray(snapshot.events) ? snapshot.events : [];
|
|
243
|
+
let emitted = 0;
|
|
244
|
+
for (const ev of events) {
|
|
245
|
+
if (emitted >= limit) return;
|
|
246
|
+
if (!ev || typeof ev !== "object") continue;
|
|
247
|
+
if (!VALID_SNAPSHOT_KINDS.includes(ev.kind)) continue;
|
|
248
|
+
if (include[ev.kind] === false) continue;
|
|
249
|
+
|
|
250
|
+
const rec = mapService(ev);
|
|
251
|
+
if (!rec) continue;
|
|
252
|
+
const capturedAt = parseTime(ev.capturedAt) || rec.handledMs || fallbackCapturedAt;
|
|
253
|
+
yield {
|
|
254
|
+
adapter: NAME,
|
|
255
|
+
kind: KIND_SERVICE,
|
|
256
|
+
originalId: stableOriginalId(rec.serviceId),
|
|
257
|
+
capturedAt,
|
|
258
|
+
payload: { record: rec, account },
|
|
259
|
+
};
|
|
260
|
+
emitted += 1;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async *_syncViaCookie(opts = {}) {
|
|
265
|
+
if (!(await this._cookieAuth.validate())) return;
|
|
266
|
+
const cookies = this._cookieAuth.toHeader();
|
|
267
|
+
const include = opts.include || {};
|
|
268
|
+
if (include[KIND_SERVICE] === false) return;
|
|
269
|
+
const sinceMs =
|
|
270
|
+
opts.sinceWatermark != null ? parseInt(String(opts.sinceWatermark), 10) || 0 : 0;
|
|
271
|
+
const limit = Number.isInteger(opts.limit) && opts.limit > 0 ? opts.limit : Infinity;
|
|
272
|
+
const maxPages =
|
|
273
|
+
Number.isInteger(opts.maxPages) && opts.maxPages > 0 ? opts.maxPages : 10;
|
|
274
|
+
|
|
275
|
+
let emitted = 0;
|
|
276
|
+
let page = 1;
|
|
277
|
+
while (page <= maxPages) {
|
|
278
|
+
const query = { page, size: PAGE_SIZE };
|
|
279
|
+
let sign = null;
|
|
280
|
+
if (this._signProvider) {
|
|
281
|
+
sign = await this._signProvider({ url: this._listUrl, query, cookies });
|
|
282
|
+
}
|
|
283
|
+
const resp = await this._fetchFn({ url: this._listUrl, cookies, query, sign });
|
|
284
|
+
const items = extractList(resp);
|
|
285
|
+
if (!items.length) break;
|
|
286
|
+
let reachedWatermark = false;
|
|
287
|
+
for (const it of items) {
|
|
288
|
+
const rec = mapService(it);
|
|
289
|
+
if (!rec) continue;
|
|
290
|
+
const ts = rec.handledMs || null;
|
|
291
|
+
if (sinceMs && ts && ts < sinceMs) {
|
|
292
|
+
reachedWatermark = true;
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
if (emitted >= limit) return;
|
|
296
|
+
yield {
|
|
297
|
+
adapter: NAME,
|
|
298
|
+
kind: KIND_SERVICE,
|
|
299
|
+
originalId: stableOriginalId(rec.serviceId),
|
|
300
|
+
capturedAt: ts || Date.now(),
|
|
301
|
+
payload: { record: rec, cookie: true },
|
|
302
|
+
};
|
|
303
|
+
emitted += 1;
|
|
304
|
+
}
|
|
305
|
+
if (reachedWatermark || items.length < PAGE_SIZE) break;
|
|
306
|
+
page += 1;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
normalize(raw) {
|
|
311
|
+
if (!raw || !raw.payload || !raw.payload.record) {
|
|
312
|
+
throw new Error("IXiamenAdapter.normalize: payload.record missing");
|
|
313
|
+
}
|
|
314
|
+
const rec = raw.payload.record;
|
|
315
|
+
const ingestedAt = Date.now();
|
|
316
|
+
const occurredAt = rec.handledMs || raw.capturedAt || ingestedAt;
|
|
317
|
+
const source = {
|
|
318
|
+
adapter: NAME,
|
|
319
|
+
adapterVersion: VERSION,
|
|
320
|
+
originalId: raw.originalId,
|
|
321
|
+
capturedAt: raw.capturedAt || occurredAt,
|
|
322
|
+
capturedBy: CAPTURED_BY.API,
|
|
323
|
+
};
|
|
324
|
+
const topicId = `topic-ixiamen-cat-${rec.category}`;
|
|
325
|
+
return {
|
|
326
|
+
events: [
|
|
327
|
+
{
|
|
328
|
+
id: newId(),
|
|
329
|
+
type: ENTITY_TYPES.EVENT,
|
|
330
|
+
subtype: EVENT_SUBTYPES.INTERACTION,
|
|
331
|
+
occurredAt,
|
|
332
|
+
actor: "person-self",
|
|
333
|
+
content: {
|
|
334
|
+
title: `办理: ${rec.serviceName}`.slice(0, 80),
|
|
335
|
+
text: rec.serviceName,
|
|
336
|
+
},
|
|
337
|
+
ingestedAt,
|
|
338
|
+
source,
|
|
339
|
+
extra: {
|
|
340
|
+
platform: "ixiamen",
|
|
341
|
+
serviceId: rec.serviceId,
|
|
342
|
+
category: rec.category,
|
|
343
|
+
status: rec.status || null,
|
|
344
|
+
dept: rec.dept || null,
|
|
345
|
+
topicRef: topicId,
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
],
|
|
349
|
+
persons: [],
|
|
350
|
+
places: [],
|
|
351
|
+
items: [],
|
|
352
|
+
topics: [
|
|
353
|
+
{
|
|
354
|
+
id: topicId,
|
|
355
|
+
type: ENTITY_TYPES.TOPIC,
|
|
356
|
+
name: rec.category,
|
|
357
|
+
ingestedAt,
|
|
358
|
+
source,
|
|
359
|
+
extra: { platform: "ixiamen", kind: "service-category" },
|
|
360
|
+
},
|
|
361
|
+
],
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async function defaultFetch(_opts) {
|
|
367
|
+
throw new Error("gov-ixiamen: no fetchFn configured for cookie-api mode");
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
module.exports = {
|
|
371
|
+
IXiamenAdapter,
|
|
372
|
+
mapService,
|
|
373
|
+
extractList,
|
|
374
|
+
inferCategory,
|
|
375
|
+
parseTime,
|
|
376
|
+
NAME,
|
|
377
|
+
VERSION,
|
|
378
|
+
SNAPSHOT_SCHEMA_VERSION,
|
|
379
|
+
VALID_SNAPSHOT_KINDS,
|
|
380
|
+
};
|