apimo.js 1.0.5 → 1.1.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/dist/core/api.js CHANGED
@@ -1,12 +1,3 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import Bottleneck from 'bottleneck';
11
2
  import { merge } from 'merge-anything';
12
3
  import { z } from 'zod';
@@ -79,113 +70,99 @@ export class Apimo {
79
70
  const extendedInit = Object.assign(Object.assign({}, init), { headers: Object.assign({ Authorization: `Basic ${btoa(`${this.provider}:${this.token}`)}` }, init === null || init === void 0 ? void 0 : init.headers) });
80
71
  return this.limiter.schedule(() => fetch(input, extendedInit));
81
72
  }
82
- get(path, schema, options) {
83
- return __awaiter(this, void 0, void 0, function* () {
84
- const url = makeApiUrl(path, this.config, Object.assign({ culture: this.config.culture }, options));
85
- const { attempts, initialDelayMs, backoff } = this.config.retry;
86
- let lastError;
87
- for (let attempt = 1; attempt <= attempts; attempt++) {
88
- try {
89
- const response = yield this.fetch(url);
90
- if (!response.ok) {
91
- let responseBody;
92
- try {
93
- responseBody = yield response.json();
94
- }
95
- catch (_a) {
96
- // The body wasn't JSON — leave responseBody as undefined
97
- }
98
- throwForStatus(response.status, url.toString(), responseBody);
73
+ async get(path, schema, options) {
74
+ const url = makeApiUrl(path, this.config, Object.assign({ culture: this.config.culture }, options));
75
+ const { attempts, initialDelayMs, backoff } = this.config.retry;
76
+ let lastError;
77
+ for (let attempt = 1; attempt <= attempts; attempt++) {
78
+ try {
79
+ const response = await this.fetch(url);
80
+ if (!response.ok) {
81
+ let responseBody;
82
+ try {
83
+ responseBody = await response.json();
99
84
  }
100
- const json = yield response.json();
101
- const result = yield schema.safeParseAsync(json);
102
- if (!result.success) {
103
- throw new ApiResponseValidationError(url.toString(), result.error);
85
+ catch (_a) {
86
+ // The body wasn't JSON — leave responseBody as undefined
104
87
  }
105
- return result.data;
88
+ throwForStatus(response.status, url.toString(), responseBody);
106
89
  }
107
- catch (error) {
108
- lastError = error;
109
- const hasMoreAttempts = attempt < attempts;
110
- if (!isRetryable(error)) {
111
- // Non-transient errors (4xx, schema failures, etc.) — propagate immediately
112
- throw error;
113
- }
114
- if (!hasMoreAttempts) {
115
- break;
116
- }
117
- yield this.sleep(this.retryDelayMs(attempt, initialDelayMs, backoff));
90
+ const json = await response.json();
91
+ const result = await schema.safeParseAsync(json);
92
+ if (!result.success) {
93
+ throw new ApiResponseValidationError(url.toString(), result.error);
118
94
  }
95
+ return result.data;
119
96
  }
120
- throw new ApiRetryExhaustedError(attempts, lastError);
121
- });
97
+ catch (error) {
98
+ lastError = error;
99
+ const hasMoreAttempts = attempt < attempts;
100
+ if (!isRetryable(error)) {
101
+ // Non-transient errors (4xx, schema failures, etc.) — propagate immediately
102
+ throw error;
103
+ }
104
+ if (!hasMoreAttempts) {
105
+ break;
106
+ }
107
+ await this.sleep(this.retryDelayMs(attempt, initialDelayMs, backoff));
108
+ }
109
+ }
110
+ throw new ApiRetryExhaustedError(attempts, lastError);
122
111
  }
123
- fetchCatalogs() {
124
- return __awaiter(this, void 0, void 0, function* () {
125
- return this.get(['catalogs'], z.array(CatalogDefinitionSchema));
126
- });
112
+ async fetchCatalogs() {
113
+ return this.get(['catalogs'], z.array(CatalogDefinitionSchema));
127
114
  }
128
- populateCache(catalogName, culture, id) {
129
- return __awaiter(this, void 0, void 0, function* () {
130
- const catalog = yield this.fetchCatalog(catalogName, { culture });
131
- yield this.cache.setEntries(catalogName, culture, catalog);
132
- if (id !== undefined) {
133
- const queriedKey = catalog.find(({ id: entryId }) => entryId === id);
134
- return queriedKey
135
- ? {
136
- name: queriedKey.name,
137
- namePlural: queriedKey.name_plurial,
138
- }
139
- : null;
140
- }
141
- });
115
+ async populateCache(catalogName, culture, id) {
116
+ const catalog = await this.fetchCatalog(catalogName, { culture });
117
+ await this.cache.setEntries(catalogName, culture, catalog);
118
+ if (id !== undefined) {
119
+ const queriedKey = catalog.find(({ id: entryId }) => entryId === id);
120
+ return queriedKey
121
+ ? {
122
+ name: queriedKey.name,
123
+ namePlural: queriedKey.name_plurial,
124
+ }
125
+ : null;
126
+ }
142
127
  }
143
- getCatalogEntries(catalogName, options) {
144
- return __awaiter(this, void 0, void 0, function* () {
145
- var _a, _b, _c;
146
- try {
147
- return yield this.cache.getEntries(catalogName, (_a = options === null || options === void 0 ? void 0 : options.culture) !== null && _a !== void 0 ? _a : this.config.culture);
128
+ async getCatalogEntries(catalogName, options) {
129
+ var _a, _b, _c;
130
+ try {
131
+ return await this.cache.getEntries(catalogName, (_a = options === null || options === void 0 ? void 0 : options.culture) !== null && _a !== void 0 ? _a : this.config.culture);
132
+ }
133
+ catch (e) {
134
+ if (e instanceof CacheExpiredError) {
135
+ await this.populateCache(catalogName, (_b = options === null || options === void 0 ? void 0 : options.culture) !== null && _b !== void 0 ? _b : this.config.culture);
136
+ return this.cache.getEntries(catalogName, (_c = options === null || options === void 0 ? void 0 : options.culture) !== null && _c !== void 0 ? _c : this.config.culture);
148
137
  }
149
- catch (e) {
150
- if (e instanceof CacheExpiredError) {
151
- yield this.populateCache(catalogName, (_b = options === null || options === void 0 ? void 0 : options.culture) !== null && _b !== void 0 ? _b : this.config.culture);
152
- return this.cache.getEntries(catalogName, (_c = options === null || options === void 0 ? void 0 : options.culture) !== null && _c !== void 0 ? _c : this.config.culture);
153
- }
154
- else {
155
- throw e;
156
- }
138
+ else {
139
+ throw e;
157
140
  }
158
- });
141
+ }
159
142
  }
160
- fetchCatalog(catalogName, options) {
161
- return __awaiter(this, void 0, void 0, function* () {
162
- return this.get(['catalogs', catalogName], z.array(CatalogEntrySchema), options);
163
- });
143
+ async fetchCatalog(catalogName, options) {
144
+ return this.get(['catalogs', catalogName], z.array(CatalogEntrySchema), options);
164
145
  }
165
- fetchAgencies(options) {
166
- return __awaiter(this, void 0, void 0, function* () {
167
- var _a;
168
- return this.get(['agencies'], z.object({
169
- total_items: z.number(),
170
- agencies: getAgencySchema(this.getLocalizedCatalogTransformer((_a = options === null || options === void 0 ? void 0 : options.culture) !== null && _a !== void 0 ? _a : this.config.culture), this.config).array(),
171
- timestamp: z.number(),
172
- }));
173
- });
146
+ async fetchAgencies(options) {
147
+ var _a;
148
+ return this.get(['agencies'], z.object({
149
+ total_items: z.number(),
150
+ agencies: getAgencySchema(this.getLocalizedCatalogTransformer((_a = options === null || options === void 0 ? void 0 : options.culture) !== null && _a !== void 0 ? _a : this.config.culture), this.config).array(),
151
+ timestamp: z.number(),
152
+ }));
174
153
  }
175
- fetchProperties(agencyId, options) {
176
- return __awaiter(this, void 0, void 0, function* () {
177
- var _a;
178
- return this.get(['agencies', agencyId.toString(), 'properties'], z.object({
179
- total_items: z.number(),
180
- timestamp: z.number(),
181
- properties: getPropertySchema(this.getLocalizedCatalogTransformer((_a = options === null || options === void 0 ? void 0 : options.culture) !== null && _a !== void 0 ? _a : this.config.culture)).array(),
182
- }), options);
183
- });
154
+ async fetchProperties(agencyId, options) {
155
+ var _a;
156
+ return this.get(['agencies', agencyId.toString(), 'properties'], z.object({
157
+ total_items: z.number(),
158
+ timestamp: z.number(),
159
+ properties: getPropertySchema(this.getLocalizedCatalogTransformer((_a = options === null || options === void 0 ? void 0 : options.culture) !== null && _a !== void 0 ? _a : this.config.culture)).array(),
160
+ }), options);
184
161
  }
185
162
  /** Calculates the delay before the next retry attempt (1-based attempt index). */
186
163
  retryDelayMs(attempt, initialDelayMs, backoff) {
187
164
  switch (backoff) {
188
- case 'exponential': return initialDelayMs * Math.pow(2, (attempt - 1));
165
+ case 'exponential': return initialDelayMs * 2 ** (attempt - 1);
189
166
  case 'linear': return initialDelayMs * attempt;
190
167
  case 'fixed': return initialDelayMs;
191
168
  }
@@ -194,7 +171,7 @@ export class Apimo {
194
171
  return new Promise(resolve => setTimeout(resolve, ms));
195
172
  }
196
173
  getLocalizedCatalogTransformer(culture) {
197
- return (catalogName, id) => __awaiter(this, void 0, void 0, function* () {
174
+ return async (catalogName, id) => {
198
175
  if (!this.config.catalogs.transform.active) {
199
176
  return `${catalogName}.${id}`;
200
177
  }
@@ -202,21 +179,19 @@ export class Apimo {
202
179
  return this.config.catalogs.transform.transformFn(catalogName, culture, id);
203
180
  }
204
181
  return this.catalogTransformer(catalogName, culture, id);
205
- });
182
+ };
206
183
  }
207
- catalogTransformer(catalogName, culture, id) {
208
- return __awaiter(this, void 0, void 0, function* () {
209
- try {
210
- return yield this.cache.getEntry(catalogName, culture, id);
184
+ async catalogTransformer(catalogName, culture, id) {
185
+ try {
186
+ return await this.cache.getEntry(catalogName, culture, id);
187
+ }
188
+ catch (e) {
189
+ if (e instanceof CacheExpiredError) {
190
+ return await this.populateCache(catalogName, culture, id);
211
191
  }
212
- catch (e) {
213
- if (e instanceof CacheExpiredError) {
214
- return yield this.populateCache(catalogName, culture, id);
215
- }
216
- else {
217
- throw e;
218
- }
192
+ else {
193
+ throw e;
219
194
  }
220
- });
195
+ }
221
196
  }
222
197
  }
@@ -1,28 +1,13 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { CacheExpiredError } from './types';
11
2
  export class DummyCache {
12
3
  constructor() {
13
4
  }
14
- getEntries(_catalogName, _culture) {
15
- return __awaiter(this, void 0, void 0, function* () {
16
- throw new CacheExpiredError();
17
- });
5
+ async getEntries(_catalogName, _culture) {
6
+ throw new CacheExpiredError();
18
7
  }
19
- setEntries(_catalogName, _culture, _entries) {
20
- return __awaiter(this, void 0, void 0, function* () {
21
- });
8
+ async setEntries(_catalogName, _culture, _entries) {
22
9
  }
23
- getEntry(_catalogName, _culture, _id) {
24
- return __awaiter(this, void 0, void 0, function* () {
25
- throw new CacheExpiredError();
26
- });
10
+ async getEntry(_catalogName, _culture, _id) {
11
+ throw new CacheExpiredError();
27
12
  }
28
13
  }
@@ -1,12 +1,3 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { mkdirSync } from 'node:fs';
11
2
  import { readFile, writeFile } from 'node:fs/promises';
12
3
  import * as path from 'node:path';
@@ -20,59 +11,51 @@ export class FilesystemCache {
20
11
  this.cacheExpirationMs = (_b = settings === null || settings === void 0 ? void 0 : settings.cacheExpirationMs) !== null && _b !== void 0 ? _b : MS_IN_ONE_WEEK;
21
12
  mkdirSync(this.path, { recursive: true });
22
13
  }
23
- setEntries(catalogName, culture, entries) {
24
- return __awaiter(this, void 0, void 0, function* () {
25
- const filePath = this.getCacheFilePath(catalogName, culture);
26
- const formattedEntries = Object.fromEntries(entries.map(({ id, name, name_plurial }) => [id.toString(), {
27
- name,
28
- namePlural: name_plurial,
29
- }]));
30
- const dump = JSON.stringify({
31
- timestamp: Date.now(),
32
- cache: formattedEntries,
33
- });
34
- return writeFile(filePath, dump);
14
+ async setEntries(catalogName, culture, entries) {
15
+ const filePath = this.getCacheFilePath(catalogName, culture);
16
+ const formattedEntries = Object.fromEntries(entries.map(({ id, name, name_plurial }) => [id.toString(), {
17
+ name,
18
+ namePlural: name_plurial,
19
+ }]));
20
+ const dump = JSON.stringify({
21
+ timestamp: Date.now(),
22
+ cache: formattedEntries,
35
23
  });
24
+ return writeFile(filePath, dump);
36
25
  }
37
- readFileOrThrow(filePath) {
38
- return __awaiter(this, void 0, void 0, function* () {
39
- try {
40
- return yield readFile(filePath, 'utf-8');
41
- }
42
- catch (_a) {
43
- throw new CacheExpiredError();
44
- }
45
- });
26
+ async readFileOrThrow(filePath) {
27
+ try {
28
+ return await readFile(filePath, 'utf-8');
29
+ }
30
+ catch (_a) {
31
+ throw new CacheExpiredError();
32
+ }
46
33
  }
47
- getEntry(catalogName, culture, id) {
48
- return __awaiter(this, void 0, void 0, function* () {
49
- var _a;
50
- const filePath = this.getCacheFilePath(catalogName, culture);
51
- const data = yield this.readFileOrThrow(filePath);
52
- const parsed = JSON.parse(data);
53
- const currentTimestamp = Date.now();
54
- if (parsed.timestamp + this.cacheExpirationMs < currentTimestamp) {
55
- throw new CacheExpiredError();
56
- }
57
- return (_a = parsed.cache[id.toString()]) !== null && _a !== void 0 ? _a : null;
58
- });
34
+ async getEntry(catalogName, culture, id) {
35
+ var _a;
36
+ const filePath = this.getCacheFilePath(catalogName, culture);
37
+ const data = await this.readFileOrThrow(filePath);
38
+ const parsed = JSON.parse(data);
39
+ const currentTimestamp = Date.now();
40
+ if (parsed.timestamp + this.cacheExpirationMs < currentTimestamp) {
41
+ throw new CacheExpiredError();
42
+ }
43
+ return (_a = parsed.cache[id.toString()]) !== null && _a !== void 0 ? _a : null;
59
44
  }
60
- getEntries(catalogName, culture) {
61
- return __awaiter(this, void 0, void 0, function* () {
62
- const filePath = this.getCacheFilePath(catalogName, culture);
63
- const data = yield this.readFileOrThrow(filePath);
64
- const parsed = JSON.parse(data);
65
- const currentTimestamp = Date.now();
66
- if (parsed.timestamp + this.cacheExpirationMs < currentTimestamp) {
67
- throw new CacheExpiredError();
68
- }
69
- return Object.entries(parsed.cache).map(([id, entry]) => {
70
- var _a;
71
- return ({
72
- id: Number.parseInt(id, 10),
73
- name: (_a = entry === null || entry === void 0 ? void 0 : entry.name) !== null && _a !== void 0 ? _a : 'missing',
74
- name_plurial: entry === null || entry === void 0 ? void 0 : entry.namePlural,
75
- });
45
+ async getEntries(catalogName, culture) {
46
+ const filePath = this.getCacheFilePath(catalogName, culture);
47
+ const data = await this.readFileOrThrow(filePath);
48
+ const parsed = JSON.parse(data);
49
+ const currentTimestamp = Date.now();
50
+ if (parsed.timestamp + this.cacheExpirationMs < currentTimestamp) {
51
+ throw new CacheExpiredError();
52
+ }
53
+ return Object.entries(parsed.cache).map(([id, entry]) => {
54
+ var _a;
55
+ return ({
56
+ id: Number.parseInt(id, 10),
57
+ name: (_a = entry === null || entry === void 0 ? void 0 : entry.name) !== null && _a !== void 0 ? _a : 'missing',
58
+ name_plurial: entry === null || entry === void 0 ? void 0 : entry.namePlural,
76
59
  });
77
60
  });
78
61
  }
@@ -1,12 +1,3 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { CacheExpiredError } from './types';
11
2
  const MS_IN_ONE_WEEK = 7 * 24 * 60 * 60 * 1000;
12
3
  export class MemoryCache {
@@ -15,46 +6,40 @@ export class MemoryCache {
15
6
  this.cacheExpirationMs = (_a = settings === null || settings === void 0 ? void 0 : settings.cacheExpirationMs) !== null && _a !== void 0 ? _a : MS_IN_ONE_WEEK;
16
7
  this._MEMORY = new Map();
17
8
  }
18
- setEntries(catalogName, culture, entries) {
19
- return __awaiter(this, void 0, void 0, function* () {
20
- const memoryEntry = new Map(entries.map(({ id, name, name_plurial }) => [id, {
21
- name,
22
- namePlural: name_plurial,
23
- }]));
24
- this._MEMORY.set(this.getCacheKey(catalogName, culture), {
25
- timestamp: Date.now(),
26
- cache: memoryEntry,
27
- });
9
+ async setEntries(catalogName, culture, entries) {
10
+ const memoryEntry = new Map(entries.map(({ id, name, name_plurial }) => [id, {
11
+ name,
12
+ namePlural: name_plurial,
13
+ }]));
14
+ this._MEMORY.set(this.getCacheKey(catalogName, culture), {
15
+ timestamp: Date.now(),
16
+ cache: memoryEntry,
28
17
  });
29
18
  }
30
- getEntry(catalogName, culture, id) {
31
- return __awaiter(this, void 0, void 0, function* () {
32
- var _a;
33
- const memoryEntry = this._MEMORY.get(this.getCacheKey(catalogName, culture));
34
- if (!memoryEntry) {
35
- throw new CacheExpiredError();
36
- }
37
- if (memoryEntry.timestamp + this.cacheExpirationMs < Date.now()) {
38
- throw new CacheExpiredError();
39
- }
40
- return (_a = memoryEntry.cache.get(id)) !== null && _a !== void 0 ? _a : null;
41
- });
19
+ async getEntry(catalogName, culture, id) {
20
+ var _a;
21
+ const memoryEntry = this._MEMORY.get(this.getCacheKey(catalogName, culture));
22
+ if (!memoryEntry) {
23
+ throw new CacheExpiredError();
24
+ }
25
+ if (memoryEntry.timestamp + this.cacheExpirationMs < Date.now()) {
26
+ throw new CacheExpiredError();
27
+ }
28
+ return (_a = memoryEntry.cache.get(id)) !== null && _a !== void 0 ? _a : null;
42
29
  }
43
- getEntries(catalogName, culture) {
44
- return __awaiter(this, void 0, void 0, function* () {
45
- const memoryEntry = this._MEMORY.get(this.getCacheKey(catalogName, culture));
46
- if (!memoryEntry) {
47
- throw new CacheExpiredError();
48
- }
49
- if (memoryEntry.timestamp + this.cacheExpirationMs < Date.now()) {
50
- throw new CacheExpiredError();
51
- }
52
- return Array.from(memoryEntry.cache.entries()).map(([id, { name, namePlural }]) => ({
53
- id,
54
- name,
55
- name_plurial: namePlural,
56
- }));
57
- });
30
+ async getEntries(catalogName, culture) {
31
+ const memoryEntry = this._MEMORY.get(this.getCacheKey(catalogName, culture));
32
+ if (!memoryEntry) {
33
+ throw new CacheExpiredError();
34
+ }
35
+ if (memoryEntry.timestamp + this.cacheExpirationMs < Date.now()) {
36
+ throw new CacheExpiredError();
37
+ }
38
+ return Array.from(memoryEntry.cache.entries()).map(([id, { name, namePlural }]) => ({
39
+ id,
40
+ name,
41
+ name_plurial: namePlural,
42
+ }));
58
43
  }
59
44
  getCacheKey(catalogName, culture) {
60
45
  return `${catalogName}.${culture}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apimo.js",
3
- "version": "1.0.5",
3
+ "version": "1.1.0",
4
4
  "description": "A wrapper for the Apimo API with catalog caching for building custom Real Estate website using their technologies.",
5
5
  "author": "Vitaly Lysen <vitaly@lysen.dev> (https://lysen.dev)",
6
6
  "license": "MIT",