bcchapi 2.0.1 → 2.2.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.
package/CHANGELOG.md CHANGED
@@ -6,27 +6,83 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
6
6
 
7
7
  ---
8
8
 
9
- ## [2.0.1] - 2026-03-10
9
+ ## [2.2.0] - 2026-03-10
10
+
11
+
12
+ ### Added
13
+
14
+ - Add fillForward utility
15
+
10
16
 
11
17
  ### Changed
12
18
 
13
- - Updated README with full API reference and usage examples
14
- - Cleaned up CHANGELOG to start from v2.0.0
19
+ - Always run format before check in dev workflow
20
+ - Fix formatting
21
+ - Add check script and move release docs to CONTRIBUTING.md
22
+ - Update README for v2.1.0 caching and fix AGENTS.md checklists
23
+
15
24
 
16
25
  ---
26
+ ## [2.1.0] - 2026-03-10
17
27
 
18
- ## [2.0.0] - 2026-03-10
19
28
 
20
29
  ### Added
21
30
 
22
- - `bcchapi/client` typed HTTP client (`Client`, `ApiError`, `HttpError`) for the Banco Central de Chile REST API
23
- - `bcchapi/series` — curated `SERIES` constants covering exchange rates, prices, interest rates, money, national accounts, external sector, employment, public finances, and capital markets
24
- - `bcchapi/utils` — observation transform functions (`parseValue`, `filterValid`, `toNumbers`, `toMap`, `toArrays`, `parseObservationDate`, `formatQueryDate`) and statistics functions (`mean`, `stdDev`, `min`, `max`, `periodVariation`, `annualVariation`, `rollingMean`)
25
- - ESM-only output targeting Node.js >= 24
26
- - Full TypeScript declarations and source maps
27
- - npm provenance via GitHub Actions (SLSA)
31
+ - Add pluggable caching layer to Client (bcchapi/cache)
32
+
33
+
34
+ ### Changed
35
+
36
+ - Bump version to 2.1.0
37
+ - Release v2.1.0
38
+ - Fix formatting
39
+
40
+
41
+ ---
42
+ ## [2.0.1] - 2026-03-10
43
+
44
+
45
+ ### Changed
46
+
47
+ - Release v2.0.1
48
+ - Rewrite CHANGELOG for v2.0.0 initial release
49
+ - Write README for bcchapi v2
50
+
51
+
52
+ ---
53
+ ## [2.0.0] - 2026-03-10
54
+
55
+
56
+ ### Changed
57
+
58
+ - Rename package to bcchapi and bump to v2.0.0
59
+
60
+
61
+ ### Fixed
62
+
63
+ - Update repository URLs to bcchapi after GitHub rename
64
+
28
65
 
29
66
  ---
67
+ ## [0.1.0] - 2026-03-10
68
+
69
+
70
+ ### Added
71
+
72
+ - Wire up root src/index.ts re-exports and replace template test
73
+ - Add date, transform, and stats utility functions
74
+ - Add SERIES constants with curated well-known series IDs
75
+ - Add Client, ApiError, and HttpError with full test coverage
76
+
77
+
78
+ ### Changed
79
+
80
+ - Release v0.1.0
81
+
82
+
83
+ ### Fixed
84
+
85
+ - Set publishConfig access to public for npm provenance
86
+
30
87
 
31
- [2.0.1]: https://github.com/airarrazaval/bcchapi/compare/v2.0.0...v2.0.1
32
- [2.0.0]: https://github.com/airarrazaval/bcchapi/releases/tag/v2.0.0
88
+ ---
package/README.md CHANGED
@@ -31,14 +31,14 @@ const data = await client.getSeries(SERIES.PRICES.UF, {
31
31
  lastdate: '2024-12-31',
32
32
  });
33
33
 
34
- console.log(data.descripIng); // "Unidad de Fomento (UF)"
34
+ console.log(data.descripIng); // "Unidad de Fomento (UF)"
35
35
  console.log(data.observations); // [{ indexDateString, value, statusCode }, ...]
36
36
  ```
37
37
 
38
38
  ### Transform observations
39
39
 
40
40
  ```ts
41
- import { toNumbers, toArrays, filterValid, mean, rollingMean } from 'bcchapi/utils';
41
+ import { toNumbers, toArrays, filterValid, fillForward, mean, rollingMean } from 'bcchapi/utils';
42
42
 
43
43
  // Parse values to number | null (null for gaps)
44
44
  const values = toNumbers(data.observations); // [37000.12, null, 37050.45, ...]
@@ -49,6 +49,11 @@ const { dates, values } = toArrays(data.observations);
49
49
  // Filter out gap observations
50
50
  const valid = filterValid(data.observations);
51
51
 
52
+ // Fill gaps by carrying the last valid value forward (e.g. weekends, holidays)
53
+ // Throws if the first observation has no valid value — ensure the start date
54
+ // falls on a trading day.
55
+ const filled = fillForward(data.observations);
56
+
52
57
  // Summary statistics (gaps are ignored)
53
58
  const avg = mean(data.observations);
54
59
 
@@ -72,7 +77,40 @@ const yoy = annualVariation(cpi.observations, 12);
72
77
 
73
78
  ```ts
74
79
  const monthlySeries = await client.searchSeries('MONTHLY');
75
- console.log(monthlySeries.map(s => s.englishTitle));
80
+ console.log(monthlySeries.map((s) => s.englishTitle));
81
+ ```
82
+
83
+ ### Enable caching
84
+
85
+ ```ts
86
+ import { Client } from 'bcchapi/client';
87
+ import { MemoryCache } from 'bcchapi/cache';
88
+
89
+ const client = new Client({
90
+ user: 'me@example.com',
91
+ pass: 'secret',
92
+ cache: new MemoryCache({ defaultTtlMs: 60 * 60 * 1000 }), // 1 hour
93
+ });
94
+
95
+ // Repeated calls with the same arguments hit the cache — no HTTP request
96
+ const data = await client.getSeries(SERIES.PRICES.UF);
97
+ ```
98
+
99
+ Bring your own backend by implementing the `Cache` interface:
100
+
101
+ ```ts
102
+ import type { Cache } from 'bcchapi/cache';
103
+
104
+ const redisCache: Cache = {
105
+ get: (key) => {
106
+ /* ... */
107
+ },
108
+ set: (key, value, ttlMs) => {
109
+ /* ... */
110
+ },
111
+ };
112
+
113
+ const client = new Client({ user, pass, cache: redisCache });
76
114
  ```
77
115
 
78
116
  ### Error handling
@@ -102,21 +140,23 @@ try {
102
140
 
103
141
  #### `new Client(options)`
104
142
 
105
- | Option | Type | Description |
106
- | ------ | ---- | ----------- |
107
- | `user` | `string` | BCCH account email |
108
- | `pass` | `string` | BCCH account password |
109
- | `fetch` | `typeof fetch` | Custom fetch implementation (optional, useful for testing) |
143
+ | Option | Type | Description |
144
+ | ------------ | -------------- | --------------------------------------------------------------------------------- |
145
+ | `user` | `string` | BCCH account email |
146
+ | `pass` | `string` | BCCH account password |
147
+ | `fetch` | `typeof fetch` | Custom fetch implementation (optional, useful for testing) |
148
+ | `cache` | `Cache` | Cache backend (optional) — any object satisfying the `Cache` interface |
149
+ | `cacheTtlMs` | `number` | TTL in milliseconds passed to `cache.set` on every successful response (optional) |
110
150
 
111
151
  #### `client.getSeries(seriesId, options?)`
112
152
 
113
153
  Fetches observations for a single time series.
114
154
 
115
- | Parameter | Type | Description |
116
- | --------- | ---- | ----------- |
117
- | `seriesId` | `string` | BCCH series identifier |
155
+ | Parameter | Type | Description |
156
+ | ------------------- | -------- | ---------------------------------------------------------- |
157
+ | `seriesId` | `string` | BCCH series identifier |
118
158
  | `options.firstdate` | `string` | Start date `"YYYY-MM-DD"` (defaults to earliest available) |
119
- | `options.lastdate` | `string` | End date `"YYYY-MM-DD"` (defaults to most recent) |
159
+ | `options.lastdate` | `string` | End date `"YYYY-MM-DD"` (defaults to most recent) |
120
160
 
121
161
  Returns `Promise<SeriesData>`.
122
162
 
@@ -126,21 +166,42 @@ Returns metadata for all series with the given frequency. `frequency` is one of
126
166
 
127
167
  Returns `Promise<SeriesInfo[]>`.
128
168
 
169
+ ### `bcchapi/cache`
170
+
171
+ #### `Cache` interface
172
+
173
+ Minimal interface for plugging in any cache backend:
174
+
175
+ ```ts
176
+ interface Cache {
177
+ get(key: string): unknown;
178
+ set(key: string, value: unknown, ttlMs?: number): void;
179
+ }
180
+ ```
181
+
182
+ #### `new MemoryCache(options?)`
183
+
184
+ Built-in in-memory cache. Entries are evicted lazily on `get` — no background timers.
185
+
186
+ | Option | Type | Description |
187
+ | -------------- | -------- | ---------------------------------------------------------------- |
188
+ | `defaultTtlMs` | `number` | Default TTL in milliseconds. Omit for entries that never expire. |
189
+
129
190
  ### `bcchapi/series`
130
191
 
131
192
  `SERIES` is a nested `as const` object of curated series IDs grouped by domain:
132
193
 
133
- | Group | Description |
134
- | ----- | ----------- |
135
- | `SERIES.EXCHANGE_RATE` | USD/CLP and major currency pairs |
136
- | `SERIES.PRICES` | UF, UTM, IVP, CPI and components |
137
- | `SERIES.INTEREST_RATES` | MPR, BCP/BCU sovereign bonds, PDBC rates |
138
- | `SERIES.MONEY` | M1, M2, M3, bank loans by type |
139
- | `SERIES.NATIONAL_ACCOUNTS` | Imacec and components |
140
- | `SERIES.EXTERNAL_SECTOR` | Current account, exports, imports, FDI |
141
- | `SERIES.EMPLOYMENT` | Unemployment rate, labour force, employed/unemployed |
142
- | `SERIES.PUBLIC_FINANCES` | Government revenue, expenditure, fiscal balance |
143
- | `SERIES.CAPITAL_MARKET` | IPSA, stock market capitalisation |
194
+ | Group | Description |
195
+ | -------------------------- | ---------------------------------------------------- |
196
+ | `SERIES.EXCHANGE_RATE` | USD/CLP and major currency pairs |
197
+ | `SERIES.PRICES` | UF, UTM, IVP, CPI and components |
198
+ | `SERIES.INTEREST_RATES` | MPR, BCP/BCU sovereign bonds, PDBC rates |
199
+ | `SERIES.MONEY` | M1, M2, M3, bank loans by type |
200
+ | `SERIES.NATIONAL_ACCOUNTS` | Imacec and components |
201
+ | `SERIES.EXTERNAL_SECTOR` | Current account, exports, imports, FDI |
202
+ | `SERIES.EMPLOYMENT` | Unemployment rate, labour force, employed/unemployed |
203
+ | `SERIES.PUBLIC_FINANCES` | Government revenue, expenditure, fiscal balance |
204
+ | `SERIES.CAPITAL_MARKET` | IPSA, stock market capitalisation |
144
205
 
145
206
  Use [si3.bcentral.cl](https://si3.bcentral.cl/siete) or `client.searchSeries()` to discover additional series IDs beyond the curated set.
146
207
 
@@ -148,43 +209,44 @@ Use [si3.bcentral.cl](https://si3.bcentral.cl/siete) or `client.searchSeries()`
148
209
 
149
210
  #### Transform functions
150
211
 
151
- | Function | Description |
152
- | -------- | ----------- |
153
- | `parseValue(value)` | Parses a value string to `number \| null` (`null` for empty or non-numeric) |
154
- | `filterValid(observations)` | Returns only observations with parseable numeric values |
155
- | `toNumbers(observations)` | Maps observations to `Array<number \| null>` |
156
- | `toMap(observations)` | Returns a `Map<string, number \| null>` keyed by `indexDateString` |
157
- | `toArrays(observations)` | Returns `{ dates: Date[], values: Array<number \| null> }` |
158
- | `parseObservationDate(dateString)` | Parses `"DD-MM-YYYY"` to a UTC `Date` |
159
- | `formatQueryDate(date)` | Formats a `Date` to `"YYYY-MM-DD"` for use in `getSeries` options |
212
+ | Function | Description |
213
+ | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
214
+ | `parseValue(value)` | Parses a value string to `number \| null` (`null` for empty or non-numeric) |
215
+ | `filterValid(observations)` | Returns only observations with parseable numeric values |
216
+ | `toNumbers(observations)` | Maps observations to `Array<number \| null>` |
217
+ | `toMap(observations)` | Returns a `Map<string, number \| null>` keyed by `indexDateString` |
218
+ | `toArrays(observations)` | Returns `{ dates: Date[], values: Array<number \| null> }` |
219
+ | `fillForward(observations)` | Fills gap observations by carrying the last valid value forward. Throws if the first observation has no valid value. |
220
+ | `parseObservationDate(dateString)` | Parses `"DD-MM-YYYY"` to a UTC `Date` |
221
+ | `formatQueryDate(date)` | Formats a `Date` to `"YYYY-MM-DD"` for use in `getSeries` options |
160
222
 
161
223
  #### Statistics functions
162
224
 
163
225
  All stats functions operate on `Observation[]` and ignore gap values (empty or non-numeric).
164
226
 
165
- | Function | Description |
166
- | -------- | ----------- |
167
- | `mean(observations)` | Arithmetic mean |
168
- | `stdDev(observations)` | Sample standard deviation (Bessel's correction) |
169
- | `min(observations)` | Minimum value |
170
- | `max(observations)` | Maximum value |
171
- | `periodVariation(observations)` | Period-over-period fractional change (`value[i] / value[i-1] - 1`) |
172
- | `annualVariation(observations, periodsPerYear)` | Year-over-year fractional change (`value[i] / value[i - n] - 1`) |
173
- | `rollingMean(observations, window)` | Rolling mean over a fixed window; `null` if any value in window is a gap |
227
+ | Function | Description |
228
+ | ----------------------------------------------- | ------------------------------------------------------------------------ |
229
+ | `mean(observations)` | Arithmetic mean |
230
+ | `stdDev(observations)` | Sample standard deviation (Bessel's correction) |
231
+ | `min(observations)` | Minimum value |
232
+ | `max(observations)` | Maximum value |
233
+ | `periodVariation(observations)` | Period-over-period fractional change (`value[i] / value[i-1] - 1`) |
234
+ | `annualVariation(observations, periodsPerYear)` | Year-over-year fractional change (`value[i] / value[i - n] - 1`) |
235
+ | `rollingMean(observations, window)` | Rolling mean over a fixed window; `null` if any value in window is a gap |
174
236
 
175
- ## Releases
237
+ ## Terms of Use
176
238
 
177
- ```sh
178
- # 1. Regenerate CHANGELOG.md (auto-determines next version)
179
- npm run changelog
239
+ Use of the Banco Central de Chile API is subject to their [terms and conditions](https://si3.bcentral.cl/estadisticas/Principal1/Web_Services/index_BDE_TC.htm). Key points:
180
240
 
181
- # 2. Commit and bump version
182
- git add CHANGELOG.md && git commit -m "chore: release vX.Y.Z"
183
- npm version patch # or minor / major
241
+ - **Registration required** — you must register and accept the terms at [si3.bcentral.cl](https://si3.bcentral.cl/siete) to obtain credentials.
242
+ - **Rate limit** — maximum 5 simultaneous requests per second per account. The API does not support bulk or parallel fetching.
243
+ - **Attribution** any application or publication that uses data from this API must credit **Banco Central de Chile** as the original source.
184
244
 
185
- # 3. Push the publish workflow triggers automatically on the version tag
186
- git push --follow-tags
187
- ```
245
+ This library does not redistribute any data. All data is fetched directly from the official API at runtime by the consuming application.
246
+
247
+ ## Contributing
248
+
249
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
188
250
 
189
251
  ## License
190
252
 
@@ -0,0 +1,4 @@
1
+ export type { Cache } from './types.js';
2
+ export { MemoryCache } from './memory-cache.js';
3
+ export type { MemoryCacheOptions } from './memory-cache.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { MemoryCache } from './memory-cache.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,42 @@
1
+ import type { Cache } from './types.js';
2
+ /**
3
+ * Options for {@link MemoryCache}.
4
+ */
5
+ export interface MemoryCacheOptions {
6
+ /**
7
+ * Default time-to-live in milliseconds applied to every `set` call that
8
+ * does not provide its own `ttlMs`.
9
+ *
10
+ * When omitted, entries never expire unless a per-entry `ttlMs` is passed
11
+ * to `set`.
12
+ *
13
+ * @defaultValue `undefined` (no expiry)
14
+ */
15
+ defaultTtlMs?: number;
16
+ }
17
+ /**
18
+ * In-memory {@link Cache} implementation backed by a `Map`.
19
+ *
20
+ * Expired entries are evicted lazily on `get` — no background timers are
21
+ * created. Suitable for single-process use; does not persist across restarts.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * import { Client } from 'bcchapi/client';
26
+ * import { MemoryCache } from 'bcchapi/cache';
27
+ *
28
+ * const client = new Client({
29
+ * user: 'me@example.com',
30
+ * pass: 'secret',
31
+ * cache: new MemoryCache({ defaultTtlMs: 60 * 60 * 1000 }), // 1 hour
32
+ * });
33
+ * ```
34
+ */
35
+ export declare class MemoryCache implements Cache {
36
+ private readonly store;
37
+ private readonly defaultTtlMs;
38
+ constructor(options?: MemoryCacheOptions);
39
+ get(key: string): unknown;
40
+ set(key: string, value: unknown, ttlMs?: number): void;
41
+ }
42
+ //# sourceMappingURL=memory-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-cache.d.ts","sourceRoot":"","sources":["../../src/cache/memory-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAOxC;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,WAAY,YAAW,KAAK;IACvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4B;IAClD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqB;gBAEtC,OAAO,CAAC,EAAE,kBAAkB;IAIxC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAYzB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;CAKvD"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * In-memory {@link Cache} implementation backed by a `Map`.
3
+ *
4
+ * Expired entries are evicted lazily on `get` — no background timers are
5
+ * created. Suitable for single-process use; does not persist across restarts.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { Client } from 'bcchapi/client';
10
+ * import { MemoryCache } from 'bcchapi/cache';
11
+ *
12
+ * const client = new Client({
13
+ * user: 'me@example.com',
14
+ * pass: 'secret',
15
+ * cache: new MemoryCache({ defaultTtlMs: 60 * 60 * 1000 }), // 1 hour
16
+ * });
17
+ * ```
18
+ */
19
+ export class MemoryCache {
20
+ store = new Map();
21
+ defaultTtlMs;
22
+ constructor(options) {
23
+ this.defaultTtlMs = options?.defaultTtlMs;
24
+ }
25
+ get(key) {
26
+ const entry = this.store.get(key);
27
+ if (entry === undefined) {
28
+ return undefined;
29
+ }
30
+ if (entry.expiresAt !== null && Date.now() > entry.expiresAt) {
31
+ this.store.delete(key);
32
+ return undefined;
33
+ }
34
+ return entry.value;
35
+ }
36
+ set(key, value, ttlMs) {
37
+ const effectiveTtl = ttlMs ?? this.defaultTtlMs;
38
+ const expiresAt = effectiveTtl !== undefined ? Date.now() + effectiveTtl : null;
39
+ this.store.set(key, { value, expiresAt });
40
+ }
41
+ }
42
+ //# sourceMappingURL=memory-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-cache.js","sourceRoot":"","sources":["../../src/cache/memory-cache.ts"],"names":[],"mappings":"AAuBA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,WAAW;IACL,KAAK,GAAG,IAAI,GAAG,EAAiB,CAAC;IACjC,YAAY,CAAqB;IAElD,YAAY,OAA4B;QACtC,IAAI,CAAC,YAAY,GAAG,OAAO,EAAE,YAAY,CAAC;IAC5C,CAAC;IAED,GAAG,CAAC,GAAW;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;YAC7D,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,KAAc,EAAE,KAAc;QAC7C,MAAM,YAAY,GAAG,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC;QAChD,MAAM,SAAS,GAAG,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC;QAChF,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAC5C,CAAC;CACF"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * A minimal cache interface for storing and retrieving API responses.
3
+ *
4
+ * Implement this interface to plug in any caching backend (in-memory, Redis,
5
+ * SQLite, etc.) into {@link https://github.com/airarrazaval/bcchapi | Client}.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import type { Cache } from 'bcchapi/cache';
10
+ * import { createClient } from 'redis';
11
+ *
12
+ * const redis = await createClient().connect();
13
+ *
14
+ * const redisCache: Cache = {
15
+ * get: async (key) => {
16
+ * const raw = await redis.get(key);
17
+ * return raw === null ? undefined : JSON.parse(raw);
18
+ * },
19
+ * set: async (key, value, ttlMs) => {
20
+ * const opts = ttlMs !== undefined ? { PX: ttlMs } : {};
21
+ * await redis.set(key, JSON.stringify(value), opts);
22
+ * },
23
+ * };
24
+ * ```
25
+ */
26
+ export interface Cache {
27
+ /**
28
+ * Returns the cached value for `key`, or `undefined` on a miss.
29
+ *
30
+ * @param key - Cache key.
31
+ * @returns The stored value, or `undefined` if the key is absent or expired.
32
+ */
33
+ get(key: string): unknown;
34
+ /**
35
+ * Stores `value` under `key` with an optional time-to-live.
36
+ *
37
+ * @param key - Cache key.
38
+ * @param value - Value to store.
39
+ * @param ttlMs - Optional time-to-live in milliseconds. Implementations may
40
+ * ignore this if TTL is managed externally (e.g. Redis `EXPIRE`).
41
+ */
42
+ set(key: string, value: unknown, ttlMs?: number): void;
43
+ }
44
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/cache/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,WAAW,KAAK;IACpB;;;;;OAKG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAE1B;;;;;;;OAOG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/cache/types.ts"],"names":[],"mappings":""}
@@ -17,6 +17,8 @@ export declare class Client {
17
17
  private readonly user;
18
18
  private readonly pass;
19
19
  private readonly fetch;
20
+ private readonly cache;
21
+ private readonly cacheTtlMs;
20
22
  constructor(options: ClientOptions);
21
23
  /**
22
24
  * Fetches observations for a single time series.
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,aAAa,EACb,SAAS,EACT,gBAAgB,EAEhB,UAAU,EACV,UAAU,EACX,MAAM,YAAY,CAAC;AAyCpB;;;;;;;;;;;;;GAaG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA0B;gBAEpC,OAAO,EAAE,aAAa;IAMlC;;;;;;;;;;;;;;;;;;OAkBG;IACG,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IA+BlF;;;;;;;;;;;;;;OAcG;IACG,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAuB/D,OAAO,CAAC,UAAU;YAOJ,OAAO;CAgBtB"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,aAAa,EACb,SAAS,EACT,gBAAgB,EAEhB,UAAU,EACV,UAAU,EACX,MAAM,YAAY,CAAC;AAyCpB;;;;;;;;;;;;;GAaG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA0B;IAChD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;IAC1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;gBAEpC,OAAO,EAAE,aAAa;IAQlC;;;;;;;;;;;;;;;;;;OAkBG;IACG,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IA2ClF;;;;;;;;;;;;;;OAcG;IACG,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAmC/D,OAAO,CAAC,UAAU;YAOJ,OAAO;CAgBtB"}
@@ -18,10 +18,14 @@ export class Client {
18
18
  user;
19
19
  pass;
20
20
  fetch;
21
+ cache;
22
+ cacheTtlMs;
21
23
  constructor(options) {
22
24
  this.user = options.user;
23
25
  this.pass = options.pass;
24
26
  this.fetch = options.fetch ?? globalThis.fetch;
27
+ this.cache = options.cache;
28
+ this.cacheTtlMs = options.cacheTtlMs;
25
29
  }
26
30
  /**
27
31
  * Fetches observations for a single time series.
@@ -43,6 +47,13 @@ export class Client {
43
47
  * ```
44
48
  */
45
49
  async getSeries(seriesId, options) {
50
+ const cacheKey = `getSeries:${seriesId}:${options?.firstdate ?? ''}:${options?.lastdate ?? ''}`;
51
+ if (this.cache !== undefined) {
52
+ const cached = this.cache.get(cacheKey);
53
+ if (cached !== undefined) {
54
+ return cached;
55
+ }
56
+ }
46
57
  const params = this.baseParams();
47
58
  params.set('function', 'GetSeries');
48
59
  params.set('timeseries', seriesId);
@@ -61,12 +72,14 @@ export class Client {
61
72
  value: obs.value,
62
73
  statusCode: obs.statusCode,
63
74
  }));
64
- return {
75
+ const result = {
65
76
  seriesId: raw.Series.seriesId,
66
77
  descripEsp: raw.Series.descripEsp,
67
78
  descripIng: raw.Series.descripIng,
68
79
  observations,
69
80
  };
81
+ this.cache?.set(cacheKey, result, this.cacheTtlMs);
82
+ return result;
70
83
  }
71
84
  /**
72
85
  * Searches for available series filtered by observation frequency.
@@ -84,6 +97,13 @@ export class Client {
84
97
  * ```
85
98
  */
86
99
  async searchSeries(frequency) {
100
+ const cacheKey = `searchSeries:${frequency}`;
101
+ if (this.cache !== undefined) {
102
+ const cached = this.cache.get(cacheKey);
103
+ if (cached !== undefined) {
104
+ return cached;
105
+ }
106
+ }
87
107
  const params = this.baseParams();
88
108
  params.set('function', 'SearchSeries');
89
109
  params.set('frequency', frequency);
@@ -91,7 +111,7 @@ export class Client {
91
111
  if (raw.Codigo !== 0) {
92
112
  throw new ApiError(`BCCH API error: ${raw.Descripcion}`, raw.Codigo, raw.Descripcion);
93
113
  }
94
- return (raw.SeriesInfos ?? []).map((info) => ({
114
+ const result = (raw.SeriesInfos ?? []).map((info) => ({
95
115
  seriesId: info.seriesId,
96
116
  frequencyCode: info.frequencyCode,
97
117
  spanishTitle: info.spanishTitle,
@@ -101,6 +121,8 @@ export class Client {
101
121
  updatedAt: info.updatedAt,
102
122
  createdAt: info.createdAt,
103
123
  }));
124
+ this.cache?.set(cacheKey, result, this.cacheTtlMs);
125
+ return result;
104
126
  }
105
127
  baseParams() {
106
128
  const params = new URLSearchParams();
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
1
  export { Client } from './client.js';
2
2
  export { ApiError, HttpError } from './types.js';
3
- export type { ClientOptions, Frequency, GetSeriesOptions, Observation, SeriesData, SeriesInfo, } from './types.js';
3
+ export type { Cache, ClientOptions, Frequency, GetSeriesOptions, Observation, SeriesData, SeriesInfo, } from './types.js';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACjD,YAAY,EACV,aAAa,EACb,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,UAAU,EACV,UAAU,GACX,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACjD,YAAY,EACV,KAAK,EACL,aAAa,EACb,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,UAAU,EACV,UAAU,GACX,MAAM,YAAY,CAAC"}
@@ -1,3 +1,4 @@
1
+ import type { Cache } from '../cache/types.js';
1
2
  /**
2
3
  * Credentials and configuration for {@link Client}.
3
4
  */
@@ -15,7 +16,32 @@ export interface ClientOptions {
15
16
  * @defaultValue `globalThis.fetch`
16
17
  */
17
18
  fetch?: typeof globalThis.fetch;
19
+ /**
20
+ * Optional cache backend for storing API responses.
21
+ *
22
+ * Any object implementing the {@link Cache} interface is accepted — use
23
+ * `MemoryCache` from `bcchapi/cache` for a zero-config in-memory option,
24
+ * or provide your own adapter (Redis, SQLite, etc.).
25
+ *
26
+ * When omitted, every call makes a fresh HTTP request.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * import { MemoryCache } from 'bcchapi/cache';
31
+ * const client = new Client({ user, pass, cache: new MemoryCache() });
32
+ * ```
33
+ */
34
+ cache?: Cache;
35
+ /**
36
+ * Time-to-live in milliseconds passed to `cache.set` on every successful
37
+ * response. Applies to both `getSeries` and `searchSeries`.
38
+ *
39
+ * When omitted, `cache.set` is called without a TTL argument and expiry is
40
+ * determined by the cache implementation.
41
+ */
42
+ cacheTtlMs?: number;
18
43
  }
44
+ export type { Cache } from '../cache/types.js';
19
45
  /**
20
46
  * A single observation returned by the BCCH API.
21
47
  *
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IAEb,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,iDAAiD;IACjD,eAAe,EAAE,MAAM,CAAC;IAExB;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,CAAC;IAEjB,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IAEnB,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IAEnB,qDAAqD;IACrD,YAAY,EAAE,WAAW,EAAE,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,CAAC;IAEjB,oDAAoD;IACpD,aAAa,EAAE,MAAM,CAAC;IAEtB,+BAA+B;IAC/B,YAAY,EAAE,MAAM,CAAC;IAErB,+BAA+B;IAC/B,YAAY,EAAE,MAAM,CAAC;IAErB,wEAAwE;IACxE,gBAAgB,EAAE,MAAM,CAAC;IAEzB,uEAAuE;IACvE,eAAe,EAAE,MAAM,CAAC;IAExB,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAElB,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;AAMrE;;;;;;;;;;;;;GAaG;AACH,qBAAa,QAAS,SAAQ,KAAK;IACjC,qDAAqD;IACrD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,kDAAkD;IAClD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;gBAEjB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;CAMjE;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,SAAU,SAAQ,KAAK;IAClC,wBAAwB;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAK5C"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IAEb,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAEhC;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;IAEd;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,YAAY,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE/C;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,iDAAiD;IACjD,eAAe,EAAE,MAAM,CAAC;IAExB;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,CAAC;IAEjB,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IAEnB,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IAEnB,qDAAqD;IACrD,YAAY,EAAE,WAAW,EAAE,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,CAAC;IAEjB,oDAAoD;IACpD,aAAa,EAAE,MAAM,CAAC;IAEtB,+BAA+B;IAC/B,YAAY,EAAE,MAAM,CAAC;IAErB,+BAA+B;IAC/B,YAAY,EAAE,MAAM,CAAC;IAErB,wEAAwE;IACxE,gBAAgB,EAAE,MAAM,CAAC;IAEzB,uEAAuE;IACvE,eAAe,EAAE,MAAM,CAAC;IAExB,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAElB,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;AAMrE;;;;;;;;;;;;;GAaG;AACH,qBAAa,QAAS,SAAQ,KAAK;IACjC,qDAAqD;IACrD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,kDAAkD;IAClD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;gBAEjB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;CAMjE;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,SAAU,SAAQ,KAAK;IAClC,wBAAwB;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAK5C"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"AAoHA,8EAA8E;AAC9E,SAAS;AACT,8EAA8E;AAE9E;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IACjC,qDAAqD;IAC5C,MAAM,CAAS;IAExB,kDAAkD;IACzC,WAAW,CAAS;IAE7B,YAAY,OAAe,EAAE,MAAc,EAAE,WAAmB;QAC9D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;CACF;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,SAAU,SAAQ,KAAK;IAClC,wBAAwB;IACf,MAAM,CAAS;IAExB,YAAY,OAAe,EAAE,MAAc;QACzC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"AAkJA,8EAA8E;AAC9E,SAAS;AACT,8EAA8E;AAE9E;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IACjC,qDAAqD;IAC5C,MAAM,CAAS;IAExB,kDAAkD;IACzC,WAAW,CAAS;IAE7B,YAAY,OAAe,EAAE,MAAc,EAAE,WAAmB;QAC9D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;CACF;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,SAAU,SAAQ,KAAK;IAClC,wBAAwB;IACf,MAAM,CAAS;IAExB,YAAY,OAAe,EAAE,MAAc;QACzC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export { Client, ApiError, HttpError } from './client/index.js';
2
- export type { ClientOptions, Frequency, GetSeriesOptions, Observation, SeriesData, SeriesInfo, } from './client/index.js';
2
+ export type { Cache, ClientOptions, Frequency, GetSeriesOptions, Observation, SeriesData, SeriesInfo, } from './client/index.js';
3
+ export { MemoryCache } from './cache/index.js';
4
+ export type { MemoryCacheOptions } from './cache/index.js';
3
5
  export { SERIES } from './series/index.js';
4
- export { parseObservationDate, formatQueryDate, filterValid, parseValue, toArrays, toMap, toNumbers, annualVariation, max, mean, min, periodVariation, rollingMean, stdDev, } from './utils/index.js';
6
+ export { parseObservationDate, formatQueryDate, fillForward, filterValid, parseValue, toArrays, toMap, toNumbers, annualVariation, max, mean, min, periodVariation, rollingMean, stdDev, } from './utils/index.js';
5
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAChE,YAAY,EACV,aAAa,EACb,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,UAAU,EACV,UAAU,GACX,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,WAAW,EACX,UAAU,EACV,QAAQ,EACR,KAAK,EACL,SAAS,EACT,eAAe,EACf,GAAG,EACH,IAAI,EACJ,GAAG,EACH,eAAe,EACf,WAAW,EACX,MAAM,GACP,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAChE,YAAY,EACV,KAAK,EACL,aAAa,EACb,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,UAAU,EACV,UAAU,GACX,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3D,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,WAAW,EACX,WAAW,EACX,UAAU,EACV,QAAQ,EACR,KAAK,EACL,SAAS,EACT,eAAe,EACf,GAAG,EACH,IAAI,EACJ,GAAG,EACH,eAAe,EACf,WAAW,EACX,MAAM,GACP,MAAM,kBAAkB,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { Client, ApiError, HttpError } from './client/index.js';
2
+ export { MemoryCache } from './cache/index.js';
2
3
  export { SERIES } from './series/index.js';
3
- export { parseObservationDate, formatQueryDate, filterValid, parseValue, toArrays, toMap, toNumbers, annualVariation, max, mean, min, periodVariation, rollingMean, stdDev, } from './utils/index.js';
4
+ export { parseObservationDate, formatQueryDate, fillForward, filterValid, parseValue, toArrays, toMap, toNumbers, annualVariation, max, mean, min, periodVariation, rollingMean, stdDev, } from './utils/index.js';
4
5
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAUhE,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,WAAW,EACX,UAAU,EACV,QAAQ,EACR,KAAK,EACL,SAAS,EACT,eAAe,EACf,GAAG,EACH,IAAI,EACJ,GAAG,EACH,eAAe,EACf,WAAW,EACX,MAAM,GACP,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAWhE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,WAAW,EACX,WAAW,EACX,UAAU,EACV,QAAQ,EACR,KAAK,EACL,SAAS,EACT,eAAe,EACf,GAAG,EACH,IAAI,EACJ,GAAG,EACH,eAAe,EACf,WAAW,EACX,MAAM,GACP,MAAM,kBAAkB,CAAC"}
@@ -1,4 +1,4 @@
1
1
  export { formatQueryDate, parseObservationDate } from './dates.js';
2
- export { filterValid, parseValue, toArrays, toMap, toNumbers } from './transform.js';
2
+ export { fillForward, filterValid, parseValue, toArrays, toMap, toNumbers } from './transform.js';
3
3
  export { annualVariation, max, mean, min, periodVariation, rollingMean, stdDev } from './stats.js';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACrF,OAAO,EAAE,eAAe,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAClG,OAAO,EAAE,eAAe,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC"}
@@ -1,4 +1,4 @@
1
1
  export { formatQueryDate, parseObservationDate } from './dates.js';
2
- export { filterValid, parseValue, toArrays, toMap, toNumbers } from './transform.js';
2
+ export { fillForward, filterValid, parseValue, toArrays, toMap, toNumbers } from './transform.js';
3
3
  export { annualVariation, max, mean, min, periodVariation, rollingMean, stdDev } from './stats.js';
4
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACrF,OAAO,EAAE,eAAe,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAClG,OAAO,EAAE,eAAe,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC"}
@@ -1,4 +1,23 @@
1
1
  import type { Observation } from '../client/types.js';
2
+ /**
3
+ * Fills forward gap observations using the last valid value seen.
4
+ *
5
+ * Each gap (empty or non-numeric `value`) is replaced with the `value` string
6
+ * of the most recent valid observation. The original `indexDateString` and
7
+ * `statusCode` of each observation are preserved.
8
+ *
9
+ * @param observations - Array of observations to fill.
10
+ * @returns A new array of the same length with gaps filled forward.
11
+ * @throws {Error} When the first observation has no valid value. Ensure the
12
+ * start date falls on a trading day with an available observation.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * const filled = fillForward(data.observations);
17
+ * // Gap observations now carry the last known value forward
18
+ * ```
19
+ */
20
+ export declare function fillForward(observations: Observation[]): Observation[];
2
21
  /**
3
22
  * Parses a raw observation value string to a number, or `null` for gaps.
4
23
  *
@@ -1 +1 @@
1
- {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../src/utils/transform.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOvD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAEtE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAE3E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,KAAK,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAM7E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG;IACrD,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC9B,CAQA"}
1
+ {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../src/utils/transform.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAmBtE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOvD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAEtE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAE3E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,KAAK,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAM7E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG;IACrD,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC9B,CAQA"}
@@ -1,4 +1,38 @@
1
1
  import { parseObservationDate } from './dates.js';
2
+ /**
3
+ * Fills forward gap observations using the last valid value seen.
4
+ *
5
+ * Each gap (empty or non-numeric `value`) is replaced with the `value` string
6
+ * of the most recent valid observation. The original `indexDateString` and
7
+ * `statusCode` of each observation are preserved.
8
+ *
9
+ * @param observations - Array of observations to fill.
10
+ * @returns A new array of the same length with gaps filled forward.
11
+ * @throws {Error} When the first observation has no valid value. Ensure the
12
+ * start date falls on a trading day with an available observation.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * const filled = fillForward(data.observations);
17
+ * // Gap observations now carry the last known value forward
18
+ * ```
19
+ */
20
+ export function fillForward(observations) {
21
+ if (observations.length === 0) {
22
+ return [];
23
+ }
24
+ if (parseValue(observations[0].value) === null) {
25
+ throw new Error('fillForward: first observation has no valid value — ensure the start date falls on a trading day');
26
+ }
27
+ let lastValue = observations[0].value;
28
+ return observations.map((obs) => {
29
+ if (parseValue(obs.value) !== null) {
30
+ lastValue = obs.value;
31
+ return obs;
32
+ }
33
+ return { ...obs, value: lastValue };
34
+ });
35
+ }
2
36
  /**
3
37
  * Parses a raw observation value string to a number, or `null` for gaps.
4
38
  *
@@ -1 +1 @@
1
- {"version":3,"file":"transform.js","sourceRoot":"","sources":["../../src/utils/transform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAGlD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAC1B,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,WAAW,CAAC,YAA2B;IACrD,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;AACtE,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,SAAS,CAAC,YAA2B;IACnD,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,KAAK,CAAC,YAA2B;IAC/C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAyB,CAAC;IAChD,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,QAAQ,CAAC,YAA2B;IAIlD,MAAM,KAAK,GAAW,EAAE,CAAC;IACzB,MAAM,MAAM,GAAyB,EAAE,CAAC;IACxC,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC;QACtD,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAC3B,CAAC"}
1
+ {"version":3,"file":"transform.js","sourceRoot":"","sources":["../../src/utils/transform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAGlD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,WAAW,CAAC,YAA2B;IACrD,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CACb,kGAAkG,CACnG,CAAC;IACJ,CAAC;IAED,IAAI,SAAS,GAAG,YAAY,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC;IACvC,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QAC9B,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;YACnC,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC;YACtB,OAAO,GAAG,CAAC;QACb,CAAC;QACD,OAAO,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAC1B,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,WAAW,CAAC,YAA2B;IACrD,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;AACtE,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,SAAS,CAAC,YAA2B;IACnD,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,KAAK,CAAC,YAA2B;IAC/C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAyB,CAAC;IAChD,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,QAAQ,CAAC,YAA2B;IAIlD,MAAM,KAAK,GAAW,EAAE,CAAC;IACzB,MAAM,MAAM,GAAyB,EAAE,CAAC;IACxC,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC;QACtD,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAC3B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bcchapi",
3
- "version": "2.0.1",
3
+ "version": "2.2.0",
4
4
  "description": "Node.js wrapper for the Banco Central de Chile API. Features a fully typed API client and utility tools to streamline macroeconomic data integration.",
5
5
  "homepage": "https://github.com/airarrazaval/bcchapi#readme",
6
6
  "bugs": {
@@ -19,9 +19,6 @@
19
19
  ],
20
20
  "type": "module",
21
21
  "sideEffects": false,
22
- "publishConfig": {
23
- "access": "public"
24
- },
25
22
  "exports": {
26
23
  ".": {
27
24
  "types": "./dist/index.d.ts",
@@ -42,8 +39,16 @@
42
39
  "types": "./dist/utils/index.d.ts",
43
40
  "import": "./dist/utils/index.js",
44
41
  "default": "./dist/utils/index.js"
42
+ },
43
+ "./cache": {
44
+ "types": "./dist/cache/index.d.ts",
45
+ "import": "./dist/cache/index.js",
46
+ "default": "./dist/cache/index.js"
45
47
  }
46
48
  },
49
+ "publishConfig": {
50
+ "access": "public"
51
+ },
47
52
  "scripts": {
48
53
  "build": "tsc -p tsconfig.build.json",
49
54
  "dev": "node --import tsx/esm --test --watch \"tests/**/*.test.ts\"",
@@ -54,15 +59,16 @@
54
59
  "lint:fix": "oxlint --fix src tests",
55
60
  "format": "oxfmt",
56
61
  "format:check": "oxfmt --check",
62
+ "check": "npm run typecheck && npm run lint && npm test",
57
63
  "clean": "rm -rf dist docs",
58
64
  "docs": "typedoc",
59
65
  "changelog": "git-cliff --bump -o CHANGELOG.md",
60
- "prepublishOnly": "npm run typecheck && npm run lint && npm run clean && npm run build"
66
+ "prepublishOnly": "npm run check && npm run clean && npm run build"
61
67
  },
62
68
  "devDependencies": {
63
69
  "@types/node": "^24.12.0",
64
70
  "git-cliff": "^2.12.0",
65
- "oxfmt": "^0.36.0",
71
+ "oxfmt": "^0.37.0",
66
72
  "oxlint": "^1.51.0",
67
73
  "tsx": "^4.21.0",
68
74
  "typedoc": "^0.28.17",