bps-mcp-server 0.16.4 → 0.18.0

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.
Files changed (55) hide show
  1. package/README.md +13 -5
  2. package/dist/client/bps-client.d.ts +10 -1
  3. package/dist/client/bps-client.d.ts.map +1 -1
  4. package/dist/client/bps-client.js +66 -13
  5. package/dist/client/bps-client.js.map +1 -1
  6. package/dist/client/request-scheduler.d.ts +29 -0
  7. package/dist/client/request-scheduler.d.ts.map +1 -0
  8. package/dist/client/request-scheduler.js +75 -0
  9. package/dist/client/request-scheduler.js.map +1 -0
  10. package/dist/config/kabkota-indicators.d.ts +30 -0
  11. package/dist/config/kabkota-indicators.d.ts.map +1 -0
  12. package/dist/config/kabkota-indicators.js +49 -0
  13. package/dist/config/kabkota-indicators.js.map +1 -0
  14. package/dist/server.d.ts.map +1 -1
  15. package/dist/server.js +34 -0
  16. package/dist/server.js.map +1 -1
  17. package/dist/services/data-formatter.d.ts +28 -0
  18. package/dist/services/data-formatter.d.ts.map +1 -1
  19. package/dist/services/data-formatter.js +74 -0
  20. package/dist/services/data-formatter.js.map +1 -1
  21. package/dist/services/intent-detector.d.ts.map +1 -1
  22. package/dist/services/intent-detector.js +1 -0
  23. package/dist/services/intent-detector.js.map +1 -1
  24. package/dist/services/kabkota-ranking.d.ts +61 -0
  25. package/dist/services/kabkota-ranking.d.ts.map +1 -0
  26. package/dist/services/kabkota-ranking.js +378 -0
  27. package/dist/services/kabkota-ranking.js.map +1 -0
  28. package/dist/services/period-resolver.d.ts +29 -0
  29. package/dist/services/period-resolver.d.ts.map +1 -0
  30. package/dist/services/period-resolver.js +132 -0
  31. package/dist/services/period-resolver.js.map +1 -0
  32. package/dist/services/region-series.d.ts +65 -0
  33. package/dist/services/region-series.d.ts.map +1 -0
  34. package/dist/services/region-series.js +396 -0
  35. package/dist/services/region-series.js.map +1 -0
  36. package/dist/services/variable-search.d.ts.map +1 -1
  37. package/dist/services/variable-search.js +2 -1
  38. package/dist/services/variable-search.js.map +1 -1
  39. package/dist/tools/analysis.tools.d.ts.map +1 -1
  40. package/dist/tools/analysis.tools.js +370 -285
  41. package/dist/tools/analysis.tools.js.map +1 -1
  42. package/dist/tools/dynamic-data.tools.d.ts.map +1 -1
  43. package/dist/tools/dynamic-data.tools.js +145 -32
  44. package/dist/tools/dynamic-data.tools.js.map +1 -1
  45. package/dist/tools/search.tools.d.ts.map +1 -1
  46. package/dist/tools/search.tools.js +14 -2
  47. package/dist/tools/search.tools.js.map +1 -1
  48. package/dist/tools/smart.tools.d.ts.map +1 -1
  49. package/dist/tools/smart.tools.js +49 -11
  50. package/dist/tools/smart.tools.js.map +1 -1
  51. package/dist/utils/error.d.ts +11 -1
  52. package/dist/utils/error.d.ts.map +1 -1
  53. package/dist/utils/error.js +50 -2
  54. package/dist/utils/error.js.map +1 -1
  55. package/package.json +1 -1
@@ -0,0 +1,378 @@
1
+ import { getPeriodId, getPeriodLabel, isNeutralPeriodLabel } from "../client/fields.js";
2
+ import { buildStructuredPayload } from "./data-formatter.js";
3
+ import { computeRelevanceScore, searchVariables } from "./variable-search.js";
4
+ import { findKabkotaIndicator } from "../config/kabkota-indicators.js";
5
+ import { logger } from "../utils/logger.js";
6
+ const DEFAULT_CONCURRENCY = 3;
7
+ /** Share of provinces that must publish a year before it is used for all. */
8
+ const COMMON_YEAR_COVERAGE = 0.6;
9
+ /** Kab/kota series older than this are reported as gaps, not ranked. */
10
+ const MAX_DATA_AGE_YEARS = 2;
11
+ export async function rankKabkota(client, resolver, options) {
12
+ const hint = findKabkotaIndicator(options.query);
13
+ const provinces = await resolveProvinces(resolver, options.provinces);
14
+ const missing = [];
15
+ // Provinces we know only publish province totals: report them as gaps
16
+ // instead of spending 3 requests each to rediscover that.
17
+ const included = [];
18
+ for (const province of provinces) {
19
+ const knownGap = hint?.unavailable[province.domain];
20
+ if (knownGap) {
21
+ missing.push({ province: province.name, reason: knownGap });
22
+ continue;
23
+ }
24
+ included.push(province);
25
+ }
26
+ const concurrency = Math.max(1, options.concurrency ?? DEFAULT_CONCURRENCY);
27
+ // 1. Which variable holds the kab/kota breakdown in each province.
28
+ const targets = [];
29
+ for (const province of included) {
30
+ const candidates = await candidateVariables(client, province, options.query, hint);
31
+ if (candidates.length === 0) {
32
+ missing.push({ province: province.name, reason: "tidak ada tabel kabupaten/kota yang cocok" });
33
+ continue;
34
+ }
35
+ targets.push({ province, candidates });
36
+ }
37
+ // 2. Pick one comparison year. Each BPS province releases Sakernas at its own
38
+ // pace; defaulting to "latest per province" mixes 2024/2025/2026 and makes
39
+ // the ranking incomparable (an agent then re-sweeps everything by hand to
40
+ // normalise the year — exactly what this tool must prevent).
41
+ const targetYear = options.year ??
42
+ (options.periodMode === "latest"
43
+ ? undefined
44
+ : await selectTargetYear(client, targets, concurrency));
45
+ // 3. Fetch and merge.
46
+ const collected = [];
47
+ for (let index = 0; index < targets.length; index += concurrency) {
48
+ const batch = targets.slice(index, index + concurrency);
49
+ const settled = await Promise.allSettled(batch.map((target) => fetchProvinceRows(client, target, options.query, targetYear)));
50
+ settled.forEach((outcome, position) => {
51
+ const target = batch[position];
52
+ if (outcome.status === "fulfilled") {
53
+ if (outcome.value.rows.length === 0) {
54
+ missing.push({
55
+ province: target.province.name,
56
+ reason: outcome.value.reason ?? "tidak ada tabel kabupaten/kota yang cocok",
57
+ });
58
+ }
59
+ else {
60
+ collected.push(...outcome.value.rows);
61
+ }
62
+ }
63
+ else {
64
+ const reason = outcome.reason instanceof Error ? outcome.reason.message : String(outcome.reason);
65
+ logger.debug(`rankKabkota: ${target.province.name} failed: ${reason}`);
66
+ missing.push({ province: target.province.name, reason: `gagal diambil (${reason.slice(0, 120)})` });
67
+ }
68
+ });
69
+ }
70
+ const sorted = collected.sort((a, b) => options.order === "highest" ? b.value - a.value : a.value - b.value);
71
+ const periods = [...new Set(sorted.map((row) => row.period))];
72
+ const series = [
73
+ ...new Set(sorted
74
+ .map((row) => row.series?.trim())
75
+ .filter((value) => !!value && !/^tidak ada$/i.test(value))
76
+ .map((value) => value.replace(/\s+/g, " "))),
77
+ ];
78
+ return {
79
+ rows: sorted.slice(0, Math.max(1, options.topN)),
80
+ totalRegions: sorted.length,
81
+ provincesCovered: new Set(collected.map((row) => row.domain)).size,
82
+ provincesConsidered: provinces.length,
83
+ periods,
84
+ series,
85
+ indicatorLabel: hint?.label ?? (collected[0]?.indicator ?? options.query),
86
+ missing,
87
+ targetYear,
88
+ yearRequested: options.year,
89
+ };
90
+ }
91
+ async function resolveProvinces(resolver, requested) {
92
+ await resolver.ensureLoaded();
93
+ const isProvince = (domain) => domain.length === 4 && domain.endsWith("00") && domain !== "0000";
94
+ const all = resolver
95
+ .getDomains()
96
+ .filter((domain) => isProvince(domain.domain_id))
97
+ .map((domain) => ({ domain: domain.domain_id, name: domain.domain_name }));
98
+ const unique = new Map(all.map((province) => [province.domain, province]));
99
+ if (!requested || requested.length === 0) {
100
+ return [...unique.values()];
101
+ }
102
+ const selected = [];
103
+ for (const query of requested) {
104
+ const resolved = await resolver.resolve(query);
105
+ if (resolved && unique.has(resolved.domainId)) {
106
+ selected.push(unique.get(resolved.domainId));
107
+ }
108
+ }
109
+ return selected;
110
+ }
111
+ /**
112
+ * Newest year that enough provinces actually publish. Using the newest *shared*
113
+ * year keeps the ranking apples-to-apples instead of mixing 2024/2025/2026.
114
+ */
115
+ async function selectTargetYear(client, targets, concurrency) {
116
+ if (targets.length === 0)
117
+ return undefined;
118
+ const yearCounts = new Map();
119
+ let reporting = 0;
120
+ for (let index = 0; index < targets.length; index += concurrency) {
121
+ const batch = targets.slice(index, index + concurrency);
122
+ const settled = await Promise.allSettled(batch.map(async (target) => {
123
+ const periods = await client.listPeriods(target.province.domain, Number(target.candidates[0].varId));
124
+ const years = new Set(periods
125
+ .map((period) => getPeriodLabel(period).match(/\d{4}/)?.[0])
126
+ .filter((year) => !!year));
127
+ return years;
128
+ }));
129
+ for (const outcome of settled) {
130
+ if (outcome.status !== "fulfilled" || outcome.value.size === 0)
131
+ continue;
132
+ reporting += 1;
133
+ for (const year of outcome.value) {
134
+ yearCounts.set(year, (yearCounts.get(year) ?? 0) + 1);
135
+ }
136
+ }
137
+ }
138
+ if (yearCounts.size === 0)
139
+ return undefined;
140
+ const threshold = Math.max(2, Math.ceil(reporting * COMMON_YEAR_COVERAGE));
141
+ const eligible = [...yearCounts.entries()]
142
+ .filter(([, count]) => count >= threshold)
143
+ .map(([year]) => year)
144
+ .sort();
145
+ if (eligible.length > 0)
146
+ return eligible[eligible.length - 1];
147
+ // No shared year: take the most common one rather than mixing all of them.
148
+ return [...yearCounts.entries()]
149
+ .sort((a, b) => b[1] - a[1] || b[0].localeCompare(a[0]))[0]?.[0];
150
+ }
151
+ async function fetchProvinceRows(client, target, query, year) {
152
+ const { province, candidates } = target;
153
+ for (const candidate of candidates) {
154
+ try {
155
+ const periodId = await findPeriodId(client, province.domain, candidate.varId, year);
156
+ if (!periodId) {
157
+ if (year)
158
+ return { rows: [], reason: `belum ada data ${year}` };
159
+ continue;
160
+ }
161
+ const response = await client.getDynamicData(province.domain, candidate.varId, periodId);
162
+ const payload = buildStructuredPayload(response, province.domain);
163
+ const rows = pickRegionRows(payload.rows, query, province);
164
+ if (rows.length >= 2) {
165
+ const staleYear = stalenessYear(rows);
166
+ if (staleYear !== null) {
167
+ return {
168
+ rows: [],
169
+ reason: `data terbaru ${staleYear} (lebih dari ${MAX_DATA_AGE_YEARS} tahun)`,
170
+ };
171
+ }
172
+ logger.debug(`rankKabkota: ${province.name} → var ${candidate.varId} (${rows.length} kab/kota)`);
173
+ return { rows };
174
+ }
175
+ }
176
+ catch (error) {
177
+ logger.debug(`rankKabkota: ${province.name} var ${candidate.varId} failed: ${error instanceof Error ? error.message : String(error)}`);
178
+ }
179
+ }
180
+ return { rows: [], reason: "tidak ada tabel kabupaten/kota yang cocok" };
181
+ }
182
+ /** Year of the newest row, or null when the whole series is recent enough. */
183
+ function stalenessYear(rows) {
184
+ const years = rows
185
+ .map((row) => Number(row.period.match(/\d{4}/)?.[0] ?? 0))
186
+ .filter((year) => year > 1900);
187
+ if (years.length === 0)
188
+ return null;
189
+ const newest = Math.max(...years);
190
+ return new Date().getFullYear() - newest > MAX_DATA_AGE_YEARS ? newest : null;
191
+ }
192
+ async function candidateVariables(client, province, query, hint) {
193
+ const hinted = hint?.variables[province.domain];
194
+ if (hinted) {
195
+ return [{ varId: hinted, title: hint.label }];
196
+ }
197
+ const discovered = await searchVariables(client, province.domain, query, {
198
+ maxSubjects: 6,
199
+ maxPagesPerSubject: 3,
200
+ maxCandidates: 12,
201
+ });
202
+ return discovered
203
+ .slice(0, 4)
204
+ .map((candidate) => ({ varId: String(candidate.var_id), title: candidate.title }));
205
+ }
206
+ /**
207
+ * Resolve the period to request. BPS requires `th`, so with no year we take the
208
+ * newest period the variable publishes (periods come back newest-first).
209
+ */
210
+ async function findPeriodId(client, domain, varId, year) {
211
+ const periods = await client.listPeriods(domain, Number(varId));
212
+ if (periods.length === 0)
213
+ return undefined;
214
+ if (!year)
215
+ return getPeriodId(periods[0]) || undefined;
216
+ const matching = periods.filter((period) => getPeriodLabel(period).includes(year));
217
+ if (matching.length === 0)
218
+ return undefined;
219
+ // Prefer the annual/August period when a year has several (Feb vs Aug).
220
+ const withAgustus = matching.find((period) => /agustus|tahunan/i.test(getPeriodLabel(period)));
221
+ return getPeriodId(withAgustus ?? matching[matching.length - 1]) || undefined;
222
+ }
223
+ /**
224
+ * Keep one value per kabupaten/kota, choosing the series that matches the query
225
+ * (e.g. TPT rather than TPAK when a table packs several indicators).
226
+ */
227
+ function pickRegionRows(rows, query, province) {
228
+ const regions = rows.filter((row) => isRegionRow(row, province));
229
+ if (regions.length === 0)
230
+ return [];
231
+ const series = selectSeries(regions, query);
232
+ let candidates = regions;
233
+ if (series !== null) {
234
+ const matching = regions.filter((row) => normalize(row.series) === series);
235
+ if (matching.length > 0)
236
+ candidates = matching;
237
+ }
238
+ const subperiod = selectSubperiod(candidates);
239
+ if (subperiod !== null) {
240
+ const matching = candidates.filter((row) => normalize(row.subperiod) === subperiod);
241
+ if (matching.length > 0)
242
+ candidates = matching;
243
+ }
244
+ const byRegion = new Map();
245
+ for (const row of candidates) {
246
+ const region = (row.category ?? "").replace(/^\d+\s*[-–]?\s*/, "").trim();
247
+ if (!region || byRegion.has(region))
248
+ continue;
249
+ if (typeof row.value !== "number" || !Number.isFinite(row.value))
250
+ continue;
251
+ const unit = row.unit ?? "";
252
+ // Guard against broken source rows (e.g. an unemployment count of 9.154
253
+ // published as a percentage). Percentages above 100 are data errors.
254
+ if (/persen|percent|%/i.test(unit) && (row.value < 0 || row.value > 100))
255
+ continue;
256
+ byRegion.set(region, {
257
+ region: titleCaseRegion(region),
258
+ province: province.name,
259
+ domain: province.domain,
260
+ value: row.value,
261
+ period: [row.period, isNeutralPeriodLabel(row.subperiod) ? undefined : row.subperiod]
262
+ .filter(Boolean)
263
+ .join(" "),
264
+ series: row.series?.trim(),
265
+ unit,
266
+ indicator: row.indicator,
267
+ });
268
+ }
269
+ return [...byRegion.values()];
270
+ }
271
+ function isRegionRow(row, province) {
272
+ const label = (row.category ?? "").trim();
273
+ if (!label)
274
+ return false;
275
+ if (/^(indonesia|nasional|total|jumlah)$/i.test(label))
276
+ return false;
277
+ if (/provinsi/i.test(label))
278
+ return false;
279
+ // Rows often carry the domain code: "7600 - Provinsi Sulawesi Barat",
280
+ // "3301 Kabupaten Cilacap". The province row starts with the province code.
281
+ if (label.startsWith(province.domain))
282
+ return false;
283
+ const stripped = normalize(label.replace(/^\d+\s*[-–]?\s*/, ""));
284
+ if (stripped === normalize(province.name))
285
+ return false;
286
+ return stripped.length > 1;
287
+ }
288
+ /** Pick the series (derived variable) that best matches the query. */
289
+ function selectSeries(rows, query) {
290
+ const labels = [
291
+ ...new Set(rows
292
+ .map((row) => row.series?.trim())
293
+ .filter((label) => !!label && !/^tidak ada$/i.test(label))),
294
+ ];
295
+ if (labels.length === 0)
296
+ return null;
297
+ if (labels.length === 1)
298
+ return normalize(labels[0]);
299
+ const scored = labels
300
+ .map((label) => {
301
+ let score = computeRelevanceScore(query, label);
302
+ if (/persentase|tingkat|rasio|%/i.test(label))
303
+ score += 15;
304
+ return { label, score };
305
+ })
306
+ .sort((a, b) => b.score - a.score);
307
+ return scored[0] && scored[0].score > 0 ? normalize(scored[0].label) : normalize(labels[0]);
308
+ }
309
+ /** Prefer the August/annual series when a table has several sub-periods. */
310
+ function selectSubperiod(rows) {
311
+ const labels = [...new Set(rows.map((row) => row.subperiod).filter((label) => !!label))];
312
+ if (labels.length <= 1)
313
+ return null;
314
+ const preference = [/^agustus$/i, /^tahunan$/i, /semester\s*2/i, /desember/i, /november/i];
315
+ for (const pattern of preference) {
316
+ const match = labels.find((label) => pattern.test(label.trim()));
317
+ if (match)
318
+ return normalize(match);
319
+ }
320
+ return normalize(labels[labels.length - 1]);
321
+ }
322
+ function normalize(value) {
323
+ return (value ?? "").trim().toLowerCase();
324
+ }
325
+ function titleCaseRegion(value) {
326
+ // Banten publishes "Kab Pandeglang"/"Kab Serang"; expand for readability.
327
+ const expanded = value.replace(/^kab\.?\s+/i, "Kabupaten ");
328
+ if (expanded !== expanded.toUpperCase())
329
+ return expanded;
330
+ return expanded
331
+ .toLowerCase()
332
+ .split(/\s+/)
333
+ .map((word) => (word.length > 2 ? word[0].toUpperCase() + word.slice(1) : word.toUpperCase()))
334
+ .join(" ");
335
+ }
336
+ export function formatKabkotaRanking(result, options) {
337
+ const lines = [];
338
+ const unit = result.rows[0]?.unit ? ` (${result.rows[0].unit})` : "";
339
+ const direction = options.order === "highest" ? "Tertinggi" : "Terendah";
340
+ const periodLabel = result.periods.length === 1
341
+ ? result.periods[0]
342
+ : result.periods.length === 0
343
+ ? "-"
344
+ : `${result.periods[0]} … ${result.periods[result.periods.length - 1]}`;
345
+ const periodHeader = result.targetYear
346
+ ? `${result.targetYear} (tahun yang sama untuk semua provinsi)`
347
+ : periodLabel;
348
+ lines.push(`## Ranking Kabupaten/Kota: ${result.indicatorLabel}${unit}`);
349
+ lines.push(`**Urutan:** ${direction} | **Periode:** ${periodHeader}`);
350
+ lines.push("");
351
+ lines.push("| # | Kabupaten/Kota | Provinsi | Nilai | Periode |");
352
+ lines.push("| --- | --- | --- | --- | --- |");
353
+ result.rows.forEach((row, index) => {
354
+ lines.push(`| ${index + 1} | ${row.region} | ${row.province} | ${row.value.toLocaleString("id-ID")} | ${row.period} |`);
355
+ });
356
+ lines.push("");
357
+ lines.push(`_Cakupan: ${result.totalRegions} kabupaten/kota dari ${result.provincesCovered} provinsi. ` +
358
+ `Menampilkan ${result.rows.length} teratas._`);
359
+ if (result.targetYear) {
360
+ lines.push(`_Angka di atas sudah diseragamkan ke tahun ${result.targetYear}; tidak perlu menyisir ulang tabel provinsi. ` +
361
+ `Untuk tahun lain: get_ranking(..., year="2025"). Untuk seri terbaru per provinsi: period_mode="latest"._`);
362
+ }
363
+ else if (result.periods.length > 1) {
364
+ lines.push(`_Catatan: periode tidak seragam (${result.periods.join(", ")}) karena period_mode="latest". ` +
365
+ `Sebutkan tahun per baris, atau panggil ulang dengan year="<tahun>" agar sebanding._`);
366
+ }
367
+ if (result.series.length > 1) {
368
+ lines.push(`_Seri yang dipakai: ${result.series[0]}; seri lain di tabel: ${result.series.slice(1).join(", ")}._`);
369
+ }
370
+ if (result.missing.length > 0) {
371
+ const listed = result.missing.slice(0, 12).map((item) => `${item.province} (${item.reason})`);
372
+ lines.push("");
373
+ lines.push(`**Provinsi tanpa data kabupaten/kota:** ${listed.join("; ")}${result.missing.length > 12 ? "; …" : ""}`);
374
+ lines.push(`_Untuk provinsi tersebut, TPT hanya tersedia level provinsi — gunakan get_ranking(level="provinsi") atau find_data._`);
375
+ }
376
+ return lines.join("\n");
377
+ }
378
+ //# sourceMappingURL=kabkota-ranking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kabkota-ranking.js","sourceRoot":"","sources":["../../src/services/kabkota-ranking.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { BpsClient } from "../client/bps-client.js";
2
+ export interface YearSpec {
3
+ /** True when the caller supplied anything at all. */
4
+ hasSpec: boolean;
5
+ /** Years requested, ascending. */
6
+ years: string[];
7
+ /** Tokens that are not years (assumed to be raw period IDs). */
8
+ periodIds: string[];
9
+ }
10
+ /** Parse "2021,2022", "2021;2022", "2021-2025", "2021 s.d. 2025" or raw IDs. */
11
+ export declare function parseYearSpec(spec?: string | null): YearSpec;
12
+ export interface ResolvedPeriodEntry {
13
+ id: string;
14
+ label: string;
15
+ year?: string;
16
+ }
17
+ export interface ResolvedPeriodsForSpec {
18
+ hasSpec: boolean;
19
+ entries: ResolvedPeriodEntry[];
20
+ missingYears: string[];
21
+ /** Years the variable actually has data for, ascending. */
22
+ availableYears: string[];
23
+ /** Up to 12 recent period labels, for error messages. */
24
+ availableLabels: string[];
25
+ }
26
+ export declare function resolvePeriodsForSpec(client: BpsClient, domain: string, varId: number, spec?: string | null): Promise<ResolvedPeriodsForSpec>;
27
+ /** Human readable note about years that had no data. */
28
+ export declare function describeMissingYears(missing: string[], available: string[]): string;
29
+ //# sourceMappingURL=period-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"period-resolver.d.ts","sourceRoot":"","sources":["../../src/services/period-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAazD,MAAM,WAAW,QAAQ;IACvB,qDAAqD;IACrD,OAAO,EAAE,OAAO,CAAC;IACjB,kCAAkC;IAClC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,gEAAgE;IAChE,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,QAAQ,CAsC5D;AAgBD,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,2DAA2D;IAC3D,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,yDAAyD;IACzD,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAgBD,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GACnB,OAAO,CAAC,sBAAsB,CAAC,CAqEjC;AAED,wDAAwD;AACxD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,CAMnF"}
@@ -0,0 +1,132 @@
1
+ import { getPeriodId, getPeriodLabel, isNeutralPeriodLabel } from "../client/fields.js";
2
+ /**
3
+ * BPS' WebAPI only accepts period *IDs* (`th`) — never years. Agents naturally
4
+ * pass "2021,2022" or "2021-2025", which the API silently answers with an empty
5
+ * dataset. This module maps human year specs to period IDs so no caller has to
6
+ * call `list_periods` first.
7
+ */
8
+ const YEAR_PATTERN = /^(19|20)\d{2}$/;
9
+ /** Parse "2021,2022", "2021;2022", "2021-2025", "2021 s.d. 2025" or raw IDs. */
10
+ export function parseYearSpec(spec) {
11
+ if (!spec || !spec.trim())
12
+ return { hasSpec: false, years: [], periodIds: [] };
13
+ // Normalize word-based ranges ("2021 s.d. 2023", "2021 sampai 2023") so the
14
+ // whitespace split below does not separate the endpoints.
15
+ const normalizedSpec = spec.replace(/((?:19|20)\d{2})\s*(?:s\.?\s*d\.?|sampai|hingga|to)\s*((?:19|20)\d{2})/gi, "$1-$2");
16
+ const tokens = normalizedSpec
17
+ .split(/[,;|]+|\s+/)
18
+ .map((token) => token.trim())
19
+ .filter(Boolean);
20
+ const years = new Set();
21
+ const periodIds = new Set();
22
+ for (const token of tokens) {
23
+ const range = token.match(/^((?:19|20)\d{2})\s*(?:-|–|—|s\.?\s*d\.?|sampai|hingga|to)\s*((?:19|20)\d{2})$/i);
24
+ if (range) {
25
+ const start = Number(range[1]);
26
+ const end = Number(range[2]);
27
+ if (Number.isFinite(start) && Number.isFinite(end) && end >= start && end - start <= 60) {
28
+ for (let year = start; year <= end; year++)
29
+ years.add(String(year));
30
+ }
31
+ continue;
32
+ }
33
+ if (YEAR_PATTERN.test(token)) {
34
+ years.add(token);
35
+ continue;
36
+ }
37
+ periodIds.add(token);
38
+ }
39
+ return { hasSpec: true, years: [...years].sort(), periodIds: [...periodIds] };
40
+ }
41
+ /**
42
+ * Prefer a comparable series across years: annual > first semester (Maret) >
43
+ * second semester (September) > anything else.
44
+ */
45
+ function periodPreference(label) {
46
+ // A bare year (or "Tahunan") is the annual series — the best comparable one.
47
+ if (/^\s*(19|20)\d{2}\s*$/.test(label))
48
+ return 5;
49
+ if (isNeutralPeriodLabel(label))
50
+ return 4;
51
+ if (/semester\s*1|maret|triwulan\s*(i{1}|1)\b|kwartal\s*1/i.test(label))
52
+ return 3;
53
+ if (/semester\s*2|september/i.test(label))
54
+ return 2;
55
+ if (/desember|desember|december/i.test(label))
56
+ return 1.5;
57
+ return 1;
58
+ }
59
+ async function listAllPeriodsSafe(client, domain, varId) {
60
+ const maybeAll = client.listAllPeriods;
61
+ if (typeof maybeAll === "function") {
62
+ return maybeAll.call(client, domain, varId);
63
+ }
64
+ return client.listPeriods(domain, varId);
65
+ }
66
+ export async function resolvePeriodsForSpec(client, domain, varId, spec) {
67
+ const parsed = parseYearSpec(spec);
68
+ const periods = await listAllPeriodsSafe(client, domain, varId);
69
+ const labels = periods
70
+ .map((period) => ({ id: getPeriodId(period), label: getPeriodLabel(period) }))
71
+ .filter((entry) => entry.id !== "");
72
+ const availableYears = [
73
+ ...new Set(labels
74
+ .map((entry) => entry.label.match(/\d{4}/)?.[0])
75
+ .filter((year) => Boolean(year))),
76
+ ].sort();
77
+ const availableLabels = [...labels]
78
+ .sort((a, b) => a.label.localeCompare(b.label, "id", { numeric: true }))
79
+ .reverse()
80
+ .slice(0, 12)
81
+ .map((entry) => entry.label);
82
+ if (!parsed.hasSpec) {
83
+ const latest = [...labels].sort((a, b) => a.label.localeCompare(b.label, "id", { numeric: true })).pop();
84
+ return {
85
+ hasSpec: false,
86
+ entries: latest ? [{ id: latest.id, label: latest.label, year: latest.label.match(/\d{4}/)?.[0] }] : [],
87
+ missingYears: [],
88
+ availableYears,
89
+ availableLabels,
90
+ };
91
+ }
92
+ const entries = [];
93
+ const seen = new Set();
94
+ for (const id of parsed.periodIds) {
95
+ const match = labels.find((entry) => entry.id === id);
96
+ if (match && !seen.has(match.id)) {
97
+ seen.add(match.id);
98
+ entries.push({ id: match.id, label: match.label, year: match.label.match(/\d{4}/)?.[0] });
99
+ }
100
+ }
101
+ const missingYears = [];
102
+ for (const year of parsed.years) {
103
+ const candidates = labels.filter((entry) => entry.label.includes(year));
104
+ if (candidates.length === 0) {
105
+ missingYears.push(year);
106
+ continue;
107
+ }
108
+ const best = [...candidates].sort((a, b) => periodPreference(b.label) - periodPreference(a.label) ||
109
+ a.label.localeCompare(b.label, "id", { numeric: true }))[0];
110
+ if (seen.has(best.id))
111
+ continue;
112
+ seen.add(best.id);
113
+ entries.push({ id: best.id, label: best.label, year });
114
+ }
115
+ return {
116
+ hasSpec: true,
117
+ entries,
118
+ missingYears,
119
+ availableYears,
120
+ availableLabels,
121
+ };
122
+ }
123
+ /** Human readable note about years that had no data. */
124
+ export function describeMissingYears(missing, available) {
125
+ if (missing.length === 0)
126
+ return "";
127
+ const availableNote = available.length > 0
128
+ ? ` Tahun yang tersedia: ${available.join(", ")}.`
129
+ : "";
130
+ return `Tahun ${missing.join(", ")} tidak tersedia untuk variabel ini.${availableNote}`;
131
+ }
132
+ //# sourceMappingURL=period-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"period-resolver.js","sourceRoot":"","sources":["../../src/services/period-resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import type { BpsClient } from "../client/bps-client.js";
2
+ import type { BpsDynamicDataResponse } from "../client/types.js";
3
+ import type { IPersistentStore } from "./persistent-store.js";
4
+ import { getPeriodId, getPeriodLabel } from "../client/fields.js";
5
+ /**
6
+ * Builds one comparable series (year → value) for a region.
7
+ *
8
+ * BPS provincial domains reuse national tables whose only row is "Indonesia",
9
+ * so "kemiskinan Jawa Timur" used to answer with the national number. Candidates
10
+ * are therefore accepted only when they actually contain a row for the region,
11
+ * and the candidate with the widest year coverage wins.
12
+ */
13
+ export declare function normalizeRegionLabel(label: string): string;
14
+ /** Pick the vervar id that represents the requested region. */
15
+ export declare function pickRegionVervarId(response: BpsDynamicDataResponse, domain: string, domainName?: string): string | null;
16
+ export declare function hasRegionRow(response: BpsDynamicDataResponse, domain: string, domainName?: string): boolean;
17
+ export interface SeriesPoint {
18
+ id: string;
19
+ label: string;
20
+ year: string;
21
+ value: number;
22
+ }
23
+ export interface RegionSeries {
24
+ region: string;
25
+ domain: string;
26
+ domainName: string;
27
+ varId: number | null;
28
+ varTitle: string;
29
+ unit?: string;
30
+ seriesLabel?: string;
31
+ points: SeriesPoint[];
32
+ missingYears: string[];
33
+ availableYears: string[];
34
+ notes: string[];
35
+ }
36
+ export interface BuildSeriesOptions {
37
+ years: string[];
38
+ /** How many candidate variables to try before giving up. */
39
+ maxCandidates?: number;
40
+ }
41
+ export declare function buildRegionSeries(client: BpsClient, store: IPersistentStore | null, query: string, region: string, domain: string, domainName: string, options: BuildSeriesOptions): Promise<RegionSeries>;
42
+ /** List all periods for a variable (used for diagnostics in error messages). */
43
+ export declare function listPeriodLabels(client: BpsClient, domain: string, varId: number): Promise<Array<{
44
+ id: string;
45
+ label: string;
46
+ }>>;
47
+ export { getPeriodId, getPeriodLabel };
48
+ export interface NationalBreakdown {
49
+ varId: number;
50
+ varTitle: string;
51
+ unit?: string;
52
+ seriesLabel?: string;
53
+ byRegion: Map<string, SeriesPoint[]>;
54
+ }
55
+ /**
56
+ * One national table (e.g. "Jumlah Penduduk Miskin Menurut Provinsi", var 185)
57
+ * already carries every province with a consistent unit and precision. Using it
58
+ * for a province comparison costs ONE request instead of one per province and
59
+ * avoids "Ribu Jiwa" vs "Juta Jiwa" mixes.
60
+ */
61
+ export declare function buildNationalBreakdown(client: BpsClient, query: string, regions: Array<{
62
+ name: string;
63
+ domainName: string;
64
+ }>, years: string[]): Promise<NationalBreakdown | null>;
65
+ //# sourceMappingURL=region-series.d.ts.map