perspectapi-ts-sdk 1.5.2 → 2.0.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/README.md +66 -0
- package/dist/index.d.mts +154 -22
- package/dist/index.d.ts +154 -22
- package/dist/index.js +496 -70
- package/dist/index.mjs +493 -70
- package/package.json +1 -1
- package/src/cache/cache-manager.ts +285 -0
- package/src/cache/in-memory-adapter.ts +49 -0
- package/src/cache/noop-adapter.ts +27 -0
- package/src/cache/types.ts +78 -0
- package/src/client/api-keys-client.ts +3 -2
- package/src/client/auth-client.ts +3 -2
- package/src/client/base-client.ts +88 -1
- package/src/client/categories-client.ts +34 -10
- package/src/client/checkout-client.ts +3 -2
- package/src/client/contact-client.ts +3 -2
- package/src/client/content-client.ts +59 -10
- package/src/client/newsletter-client.ts +4 -3
- package/src/client/organizations-client.ts +3 -2
- package/src/client/products-client.ts +115 -22
- package/src/client/sites-client.ts +3 -2
- package/src/client/webhooks-client.ts +3 -2
- package/src/index.ts +5 -0
- package/src/perspect-api-client.ts +14 -11
- package/src/types/index.ts +6 -0
package/dist/index.mjs
CHANGED
|
@@ -205,13 +205,273 @@ function createApiError(error) {
|
|
|
205
205
|
};
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
// src/cache/in-memory-adapter.ts
|
|
209
|
+
var InMemoryCacheAdapter = class {
|
|
210
|
+
store = /* @__PURE__ */ new Map();
|
|
211
|
+
async get(key) {
|
|
212
|
+
const entry = this.store.get(key);
|
|
213
|
+
if (!entry) {
|
|
214
|
+
return void 0;
|
|
215
|
+
}
|
|
216
|
+
if (entry.expiresAt && entry.expiresAt <= Date.now()) {
|
|
217
|
+
this.store.delete(key);
|
|
218
|
+
return void 0;
|
|
219
|
+
}
|
|
220
|
+
return entry.value;
|
|
221
|
+
}
|
|
222
|
+
async set(key, value, options) {
|
|
223
|
+
const expiresAt = options?.ttlSeconds && options.ttlSeconds > 0 ? Date.now() + options.ttlSeconds * 1e3 : void 0;
|
|
224
|
+
this.store.set(key, { value, expiresAt });
|
|
225
|
+
}
|
|
226
|
+
async delete(key) {
|
|
227
|
+
this.store.delete(key);
|
|
228
|
+
}
|
|
229
|
+
async deleteMany(keys) {
|
|
230
|
+
keys.forEach((key) => this.store.delete(key));
|
|
231
|
+
}
|
|
232
|
+
async clear() {
|
|
233
|
+
this.store.clear();
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// src/cache/noop-adapter.ts
|
|
238
|
+
var NoopCacheAdapter = class {
|
|
239
|
+
async get() {
|
|
240
|
+
return void 0;
|
|
241
|
+
}
|
|
242
|
+
async set() {
|
|
243
|
+
}
|
|
244
|
+
async delete() {
|
|
245
|
+
}
|
|
246
|
+
async deleteMany() {
|
|
247
|
+
}
|
|
248
|
+
async clear() {
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// src/cache/cache-manager.ts
|
|
253
|
+
var TAG_PREFIX = "__tag__";
|
|
254
|
+
var CacheManager = class {
|
|
255
|
+
adapter;
|
|
256
|
+
defaultTtlSeconds;
|
|
257
|
+
keyPrefix;
|
|
258
|
+
enabled;
|
|
259
|
+
constructor(config) {
|
|
260
|
+
const defaultOptions = {
|
|
261
|
+
defaultTtlSeconds: 300,
|
|
262
|
+
keyPrefix: "perspectapi"
|
|
263
|
+
};
|
|
264
|
+
const mergedConfig = {
|
|
265
|
+
...defaultOptions,
|
|
266
|
+
...config
|
|
267
|
+
};
|
|
268
|
+
if (config && config.enabled === false) {
|
|
269
|
+
this.enabled = false;
|
|
270
|
+
this.adapter = new NoopCacheAdapter();
|
|
271
|
+
} else if (config && config.adapter) {
|
|
272
|
+
this.enabled = true;
|
|
273
|
+
this.adapter = config.adapter;
|
|
274
|
+
} else if (config) {
|
|
275
|
+
this.enabled = true;
|
|
276
|
+
this.adapter = new InMemoryCacheAdapter();
|
|
277
|
+
} else {
|
|
278
|
+
this.enabled = false;
|
|
279
|
+
this.adapter = new NoopCacheAdapter();
|
|
280
|
+
}
|
|
281
|
+
this.defaultTtlSeconds = mergedConfig.defaultTtlSeconds ?? 300;
|
|
282
|
+
this.keyPrefix = mergedConfig.keyPrefix ?? "perspectapi";
|
|
283
|
+
}
|
|
284
|
+
isEnabled() {
|
|
285
|
+
return this.enabled;
|
|
286
|
+
}
|
|
287
|
+
getKeyPrefix() {
|
|
288
|
+
return this.keyPrefix;
|
|
289
|
+
}
|
|
290
|
+
buildKey(parts) {
|
|
291
|
+
const normalized = parts.flatMap((part) => this.normalizeKeyPart(part)).filter((part) => part !== void 0 && part !== null && part !== "");
|
|
292
|
+
return normalized.join(":");
|
|
293
|
+
}
|
|
294
|
+
async getOrSet(key, resolveValue, policy) {
|
|
295
|
+
if (!this.enabled || policy?.skipCache) {
|
|
296
|
+
const value2 = await resolveValue();
|
|
297
|
+
if (this.enabled && !policy?.skipCache && policy?.ttlSeconds !== 0) {
|
|
298
|
+
await this.set(key, value2, policy);
|
|
299
|
+
}
|
|
300
|
+
return value2;
|
|
301
|
+
}
|
|
302
|
+
const namespacedKey = this.namespacedKey(key);
|
|
303
|
+
const cachedRaw = await this.adapter.get(namespacedKey);
|
|
304
|
+
if (cachedRaw) {
|
|
305
|
+
const entry = this.deserialize(cachedRaw);
|
|
306
|
+
if (!entry.expiresAt || entry.expiresAt > Date.now()) {
|
|
307
|
+
return entry.value;
|
|
308
|
+
}
|
|
309
|
+
await this.adapter.delete(namespacedKey);
|
|
310
|
+
if (entry.tags?.length) {
|
|
311
|
+
await this.removeKeyFromTags(namespacedKey, entry.tags);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
const value = await resolveValue();
|
|
315
|
+
await this.set(key, value, policy);
|
|
316
|
+
return value;
|
|
317
|
+
}
|
|
318
|
+
async set(key, value, options) {
|
|
319
|
+
if (!this.enabled || options?.ttlSeconds === 0) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const namespacedKey = this.namespacedKey(key);
|
|
323
|
+
const ttlSeconds = options?.ttlSeconds ?? this.defaultTtlSeconds;
|
|
324
|
+
const entry = {
|
|
325
|
+
value,
|
|
326
|
+
expiresAt: ttlSeconds > 0 ? Date.now() + ttlSeconds * 1e3 : void 0,
|
|
327
|
+
tags: options?.tags,
|
|
328
|
+
metadata: options?.metadata
|
|
329
|
+
};
|
|
330
|
+
await this.adapter.set(namespacedKey, this.serialize(entry), {
|
|
331
|
+
ttlSeconds: ttlSeconds > 0 ? ttlSeconds : void 0
|
|
332
|
+
});
|
|
333
|
+
if (options?.tags?.length) {
|
|
334
|
+
await this.registerKeyTags(namespacedKey, options.tags);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
async delete(key) {
|
|
338
|
+
if (!this.enabled) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const namespacedKey = this.namespacedKey(key);
|
|
342
|
+
await this.adapter.delete(namespacedKey);
|
|
343
|
+
}
|
|
344
|
+
async invalidate(options) {
|
|
345
|
+
if (!this.enabled) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
if (options.keys?.length) {
|
|
349
|
+
const namespacedKeys = options.keys.map((key) => this.namespacedKey(key));
|
|
350
|
+
if (this.adapter.deleteMany) {
|
|
351
|
+
await this.adapter.deleteMany(namespacedKeys);
|
|
352
|
+
} else {
|
|
353
|
+
await Promise.all(namespacedKeys.map((key) => this.adapter.delete(key)));
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (options.tags?.length) {
|
|
357
|
+
await Promise.all(
|
|
358
|
+
options.tags.map(async (tag) => {
|
|
359
|
+
const tagKey = this.tagKey(tag);
|
|
360
|
+
const payload = await this.adapter.get(tagKey);
|
|
361
|
+
if (!payload) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
const keys = this.deserializeTagSet(payload);
|
|
365
|
+
if (keys.length) {
|
|
366
|
+
if (this.adapter.deleteMany) {
|
|
367
|
+
await this.adapter.deleteMany(keys);
|
|
368
|
+
} else {
|
|
369
|
+
await Promise.all(keys.map((key) => this.adapter.delete(key)));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
await this.adapter.delete(tagKey);
|
|
373
|
+
})
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
namespacedKey(key) {
|
|
378
|
+
return `${this.keyPrefix}:${key}`;
|
|
379
|
+
}
|
|
380
|
+
tagKey(tag) {
|
|
381
|
+
return this.namespacedKey(`${TAG_PREFIX}:${tag}`);
|
|
382
|
+
}
|
|
383
|
+
serialize(entry) {
|
|
384
|
+
return JSON.stringify(entry);
|
|
385
|
+
}
|
|
386
|
+
deserialize(payload) {
|
|
387
|
+
try {
|
|
388
|
+
return JSON.parse(payload);
|
|
389
|
+
} catch {
|
|
390
|
+
return { value: payload };
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
deserializeTagSet(payload) {
|
|
394
|
+
try {
|
|
395
|
+
const parsed = JSON.parse(payload);
|
|
396
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
397
|
+
} catch {
|
|
398
|
+
return [];
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
async registerKeyTags(namespacedKey, tags) {
|
|
402
|
+
await Promise.all(
|
|
403
|
+
tags.map(async (tag) => {
|
|
404
|
+
const tagKey = this.tagKey(tag);
|
|
405
|
+
const existingPayload = await this.adapter.get(tagKey);
|
|
406
|
+
const keys = existingPayload ? this.deserializeTagSet(existingPayload) : [];
|
|
407
|
+
if (!keys.includes(namespacedKey)) {
|
|
408
|
+
keys.push(namespacedKey);
|
|
409
|
+
await this.adapter.set(tagKey, JSON.stringify(keys));
|
|
410
|
+
}
|
|
411
|
+
})
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
async removeKeyFromTags(namespacedKey, tags) {
|
|
415
|
+
await Promise.all(
|
|
416
|
+
tags.map(async (tag) => {
|
|
417
|
+
const tagKey = this.tagKey(tag);
|
|
418
|
+
const existingPayload = await this.adapter.get(tagKey);
|
|
419
|
+
if (!existingPayload) {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
const keys = this.deserializeTagSet(existingPayload);
|
|
423
|
+
const updated = keys.filter((key) => key !== namespacedKey);
|
|
424
|
+
if (updated.length === 0) {
|
|
425
|
+
await this.adapter.delete(tagKey);
|
|
426
|
+
} else if (updated.length !== keys.length) {
|
|
427
|
+
await this.adapter.set(tagKey, JSON.stringify(updated));
|
|
428
|
+
}
|
|
429
|
+
})
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
normalizeKeyPart(part) {
|
|
433
|
+
if (part === void 0 || part === null || part === "") {
|
|
434
|
+
return [];
|
|
435
|
+
}
|
|
436
|
+
if (Array.isArray(part)) {
|
|
437
|
+
return part.flatMap((item) => this.normalizeKeyPart(item));
|
|
438
|
+
}
|
|
439
|
+
if (typeof part === "object") {
|
|
440
|
+
return [this.normalizeObject(part)];
|
|
441
|
+
}
|
|
442
|
+
return [String(part)];
|
|
443
|
+
}
|
|
444
|
+
normalizeObject(input) {
|
|
445
|
+
const sortedKeys = Object.keys(input).sort();
|
|
446
|
+
const normalized = {};
|
|
447
|
+
for (const key of sortedKeys) {
|
|
448
|
+
const value = input[key];
|
|
449
|
+
if (value === void 0) {
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
453
|
+
normalized[key] = JSON.parse(this.normalizeObject(value));
|
|
454
|
+
} else if (Array.isArray(value)) {
|
|
455
|
+
normalized[key] = value.map(
|
|
456
|
+
(item) => typeof item === "object" && item !== null ? JSON.parse(this.normalizeObject(item)) : item
|
|
457
|
+
);
|
|
458
|
+
} else {
|
|
459
|
+
normalized[key] = value;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return JSON.stringify(normalized);
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
|
|
208
466
|
// src/client/base-client.ts
|
|
209
467
|
var BaseClient = class {
|
|
210
468
|
http;
|
|
211
469
|
basePath;
|
|
212
|
-
|
|
470
|
+
cache;
|
|
471
|
+
constructor(http, basePath, cache) {
|
|
213
472
|
this.http = http;
|
|
214
473
|
this.basePath = basePath;
|
|
474
|
+
this.cache = cache && cache.isEnabled() ? cache : void 0;
|
|
215
475
|
}
|
|
216
476
|
/**
|
|
217
477
|
* Build a site-scoped endpoint relative to the API base path
|
|
@@ -271,12 +531,71 @@ var BaseClient = class {
|
|
|
271
531
|
async delete(endpoint, csrfToken) {
|
|
272
532
|
return this.http.delete(this.buildPath(endpoint), { csrfToken });
|
|
273
533
|
}
|
|
534
|
+
/**
|
|
535
|
+
* Fetch a GET endpoint with optional caching support.
|
|
536
|
+
*/
|
|
537
|
+
async fetchWithCache(endpoint, params, tags, policy, fetcher) {
|
|
538
|
+
if (!this.cache) {
|
|
539
|
+
return fetcher();
|
|
540
|
+
}
|
|
541
|
+
const cacheKey = this.buildCacheKey(endpoint, params);
|
|
542
|
+
const combinedPolicy = policy ? { ...policy, tags: this.mergeTags(tags, policy.tags) } : { tags };
|
|
543
|
+
return this.cache.getOrSet(cacheKey, fetcher, combinedPolicy);
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Invalidate cache entries by keys or tags.
|
|
547
|
+
*/
|
|
548
|
+
async invalidateCache(options) {
|
|
549
|
+
if (!this.cache) {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
await this.cache.invalidate(options);
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Build a consistent cache key for an endpoint + params combination.
|
|
556
|
+
*/
|
|
557
|
+
buildCacheKey(endpoint, params) {
|
|
558
|
+
const sanitizedEndpoint = endpoint.replace(/^\//, "");
|
|
559
|
+
const baseSegment = this.basePath.replace(/^\//, "");
|
|
560
|
+
const parts = [baseSegment, sanitizedEndpoint];
|
|
561
|
+
if (params && Object.keys(params).length > 0) {
|
|
562
|
+
parts.push(this.sortObject(params));
|
|
563
|
+
}
|
|
564
|
+
if (this.cache) {
|
|
565
|
+
return this.cache.buildKey(parts);
|
|
566
|
+
}
|
|
567
|
+
return parts.map((part) => typeof part === "string" ? part : JSON.stringify(part)).join(":");
|
|
568
|
+
}
|
|
569
|
+
mergeTags(defaultTags, overrideTags) {
|
|
570
|
+
const combined = /* @__PURE__ */ new Set();
|
|
571
|
+
defaultTags.forEach((tag) => combined.add(tag));
|
|
572
|
+
overrideTags?.forEach((tag) => combined.add(tag));
|
|
573
|
+
return Array.from(combined.values());
|
|
574
|
+
}
|
|
575
|
+
sortObject(input) {
|
|
576
|
+
const sortedKeys = Object.keys(input).sort();
|
|
577
|
+
const result = {};
|
|
578
|
+
for (const key of sortedKeys) {
|
|
579
|
+
const value = input[key];
|
|
580
|
+
if (value === void 0) continue;
|
|
581
|
+
if (Array.isArray(value)) {
|
|
582
|
+
result[key] = value.map(
|
|
583
|
+
(item) => typeof item === "object" && item !== null ? this.sortObject(item) : item
|
|
584
|
+
);
|
|
585
|
+
} else if (value && typeof value === "object") {
|
|
586
|
+
result[key] = this.sortObject(value);
|
|
587
|
+
} else {
|
|
588
|
+
result[key] = value;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return result;
|
|
592
|
+
}
|
|
274
593
|
};
|
|
275
594
|
|
|
276
595
|
// src/client/auth-client.ts
|
|
277
596
|
var AuthClient = class extends BaseClient {
|
|
278
|
-
constructor(http) {
|
|
279
|
-
super(http, "/api/v1");
|
|
597
|
+
constructor(http, cache) {
|
|
598
|
+
super(http, "/api/v1", cache);
|
|
280
599
|
}
|
|
281
600
|
/**
|
|
282
601
|
* Get CSRF token
|
|
@@ -337,28 +656,50 @@ var AuthClient = class extends BaseClient {
|
|
|
337
656
|
|
|
338
657
|
// src/client/content-client.ts
|
|
339
658
|
var ContentClient = class extends BaseClient {
|
|
340
|
-
constructor(http) {
|
|
341
|
-
super(http, "/api/v1");
|
|
659
|
+
constructor(http, cache) {
|
|
660
|
+
super(http, "/api/v1", cache);
|
|
342
661
|
}
|
|
343
662
|
/**
|
|
344
663
|
* Get all content with pagination and filtering for a site
|
|
345
664
|
*/
|
|
346
|
-
async getContent(siteName, params) {
|
|
347
|
-
|
|
665
|
+
async getContent(siteName, params, cachePolicy) {
|
|
666
|
+
const endpoint = this.siteScopedEndpoint(siteName);
|
|
667
|
+
const path = this.buildPath(endpoint);
|
|
668
|
+
return this.fetchWithCache(
|
|
669
|
+
endpoint,
|
|
670
|
+
params,
|
|
671
|
+
this.buildContentTags(siteName),
|
|
672
|
+
cachePolicy,
|
|
673
|
+
() => this.http.get(path, params)
|
|
674
|
+
);
|
|
348
675
|
}
|
|
349
676
|
/**
|
|
350
677
|
* Get content by ID
|
|
351
678
|
*/
|
|
352
|
-
async getContentById(id) {
|
|
353
|
-
|
|
679
|
+
async getContentById(id, cachePolicy) {
|
|
680
|
+
const endpoint = `/content/${id}`;
|
|
681
|
+
const path = this.buildPath(endpoint);
|
|
682
|
+
return this.fetchWithCache(
|
|
683
|
+
endpoint,
|
|
684
|
+
void 0,
|
|
685
|
+
this.buildContentTags(void 0, void 0, id),
|
|
686
|
+
cachePolicy,
|
|
687
|
+
() => this.http.get(path)
|
|
688
|
+
);
|
|
354
689
|
}
|
|
355
690
|
/**
|
|
356
691
|
* Get content by slug for a site
|
|
357
692
|
*/
|
|
358
|
-
async getContentBySlug(siteName, slug) {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
693
|
+
async getContentBySlug(siteName, slug, cachePolicy) {
|
|
694
|
+
const endpoint = this.siteScopedEndpoint(siteName, `/slug/${encodeURIComponent(slug)}`);
|
|
695
|
+
const path = this.buildPath(endpoint);
|
|
696
|
+
return this.fetchWithCache(
|
|
697
|
+
endpoint,
|
|
698
|
+
void 0,
|
|
699
|
+
this.buildContentTags(siteName, slug),
|
|
700
|
+
cachePolicy,
|
|
701
|
+
() => this.http.get(path)
|
|
702
|
+
);
|
|
362
703
|
}
|
|
363
704
|
/**
|
|
364
705
|
* Create new content
|
|
@@ -420,12 +761,25 @@ var ContentClient = class extends BaseClient {
|
|
|
420
761
|
async duplicateContent(id) {
|
|
421
762
|
return this.create(`/content/${id}/duplicate`, {});
|
|
422
763
|
}
|
|
764
|
+
buildContentTags(siteName, slug, id) {
|
|
765
|
+
const tags = /* @__PURE__ */ new Set(["content"]);
|
|
766
|
+
if (siteName) {
|
|
767
|
+
tags.add(`content:site:${siteName}`);
|
|
768
|
+
}
|
|
769
|
+
if (slug) {
|
|
770
|
+
tags.add(`content:slug:${siteName}:${slug}`);
|
|
771
|
+
}
|
|
772
|
+
if (typeof id === "number") {
|
|
773
|
+
tags.add(`content:id:${id}`);
|
|
774
|
+
}
|
|
775
|
+
return Array.from(tags.values());
|
|
776
|
+
}
|
|
423
777
|
};
|
|
424
778
|
|
|
425
779
|
// src/client/api-keys-client.ts
|
|
426
780
|
var ApiKeysClient = class extends BaseClient {
|
|
427
|
-
constructor(http) {
|
|
428
|
-
super(http, "/api/v1");
|
|
781
|
+
constructor(http, cache) {
|
|
782
|
+
super(http, "/api/v1", cache);
|
|
429
783
|
}
|
|
430
784
|
/**
|
|
431
785
|
* Get all API keys
|
|
@@ -491,8 +845,8 @@ var ApiKeysClient = class extends BaseClient {
|
|
|
491
845
|
|
|
492
846
|
// src/client/organizations-client.ts
|
|
493
847
|
var OrganizationsClient = class extends BaseClient {
|
|
494
|
-
constructor(http) {
|
|
495
|
-
super(http, "/api/v1");
|
|
848
|
+
constructor(http, cache) {
|
|
849
|
+
super(http, "/api/v1", cache);
|
|
496
850
|
}
|
|
497
851
|
/**
|
|
498
852
|
* Get all organizations
|
|
@@ -564,8 +918,8 @@ var OrganizationsClient = class extends BaseClient {
|
|
|
564
918
|
|
|
565
919
|
// src/client/sites-client.ts
|
|
566
920
|
var SitesClient = class extends BaseClient {
|
|
567
|
-
constructor(http) {
|
|
568
|
-
super(http, "/api/v1");
|
|
921
|
+
constructor(http, cache) {
|
|
922
|
+
super(http, "/api/v1", cache);
|
|
569
923
|
}
|
|
570
924
|
/**
|
|
571
925
|
* Get all sites
|
|
@@ -661,13 +1015,13 @@ var SitesClient = class extends BaseClient {
|
|
|
661
1015
|
|
|
662
1016
|
// src/client/products-client.ts
|
|
663
1017
|
var ProductsClient = class extends BaseClient {
|
|
664
|
-
constructor(http) {
|
|
665
|
-
super(http, "/api/v1");
|
|
1018
|
+
constructor(http, cache) {
|
|
1019
|
+
super(http, "/api/v1", cache);
|
|
666
1020
|
}
|
|
667
1021
|
/**
|
|
668
1022
|
* Get all products for a site
|
|
669
1023
|
*/
|
|
670
|
-
async getProducts(siteName, params) {
|
|
1024
|
+
async getProducts(siteName, params, cachePolicy) {
|
|
671
1025
|
const normalizeList = (value) => {
|
|
672
1026
|
if (value === void 0 || value === null) {
|
|
673
1027
|
return void 0;
|
|
@@ -691,30 +1045,61 @@ var ProductsClient = class extends BaseClient {
|
|
|
691
1045
|
delete normalizedParams.category_id;
|
|
692
1046
|
}
|
|
693
1047
|
}
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
1048
|
+
const endpoint = this.siteScopedEndpoint(siteName, "/products", { includeSitesSegment: false });
|
|
1049
|
+
const path = this.buildPath(endpoint);
|
|
1050
|
+
return this.fetchWithCache(
|
|
1051
|
+
endpoint,
|
|
1052
|
+
normalizedParams,
|
|
1053
|
+
this.buildProductTags(siteName, ["products:list"]),
|
|
1054
|
+
cachePolicy,
|
|
1055
|
+
() => this.http.get(path, normalizedParams)
|
|
697
1056
|
);
|
|
698
1057
|
}
|
|
699
1058
|
/**
|
|
700
1059
|
* Get product by ID
|
|
701
1060
|
*/
|
|
702
|
-
async getProductById(id) {
|
|
703
|
-
|
|
1061
|
+
async getProductById(id, cachePolicy) {
|
|
1062
|
+
const endpoint = `/products/${id}`;
|
|
1063
|
+
const path = this.buildPath(endpoint);
|
|
1064
|
+
return this.fetchWithCache(
|
|
1065
|
+
endpoint,
|
|
1066
|
+
void 0,
|
|
1067
|
+
this.buildProductTags(void 0, [`products:id:${id}`]),
|
|
1068
|
+
cachePolicy,
|
|
1069
|
+
() => this.http.get(path)
|
|
1070
|
+
);
|
|
704
1071
|
}
|
|
705
1072
|
/**
|
|
706
1073
|
* Get product by SKU
|
|
707
1074
|
*/
|
|
708
|
-
async getProductBySku(sku) {
|
|
709
|
-
|
|
1075
|
+
async getProductBySku(sku, cachePolicy) {
|
|
1076
|
+
const endpoint = `/products/sku/${encodeURIComponent(sku)}`;
|
|
1077
|
+
const path = this.buildPath(endpoint);
|
|
1078
|
+
return this.fetchWithCache(
|
|
1079
|
+
endpoint,
|
|
1080
|
+
void 0,
|
|
1081
|
+
this.buildProductTags(void 0, [`products:sku:${sku.toLowerCase()}`]),
|
|
1082
|
+
cachePolicy,
|
|
1083
|
+
() => this.http.get(path)
|
|
1084
|
+
);
|
|
710
1085
|
}
|
|
711
1086
|
/**
|
|
712
1087
|
* Get product by slug and site name
|
|
713
1088
|
*/
|
|
714
|
-
async getProductBySlug(siteName, slug) {
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
1089
|
+
async getProductBySlug(siteName, slug, cachePolicy) {
|
|
1090
|
+
const endpoint = this.siteScopedEndpoint(
|
|
1091
|
+
siteName,
|
|
1092
|
+
`/products/slug/${encodeURIComponent(slug)}`,
|
|
1093
|
+
{ includeSitesSegment: false }
|
|
1094
|
+
);
|
|
1095
|
+
const path = this.buildPath(endpoint);
|
|
1096
|
+
return this.fetchWithCache(
|
|
1097
|
+
endpoint,
|
|
1098
|
+
void 0,
|
|
1099
|
+
this.buildProductTags(siteName, [`products:slug:${siteName}:${slug}`]),
|
|
1100
|
+
cachePolicy,
|
|
1101
|
+
() => this.http.get(path)
|
|
1102
|
+
);
|
|
718
1103
|
}
|
|
719
1104
|
/**
|
|
720
1105
|
* Create new product
|
|
@@ -803,27 +1188,44 @@ var ProductsClient = class extends BaseClient {
|
|
|
803
1188
|
/**
|
|
804
1189
|
* Get products by category slug
|
|
805
1190
|
*/
|
|
806
|
-
async getProductsByCategorySlug(siteName, categorySlug, params) {
|
|
1191
|
+
async getProductsByCategorySlug(siteName, categorySlug, params, cachePolicy) {
|
|
807
1192
|
const queryParams = params ? {
|
|
808
1193
|
limit: params.limit,
|
|
809
1194
|
offset: params.page ? (params.page - 1) * (params.limit || 20) : void 0,
|
|
810
1195
|
published: params.published,
|
|
811
1196
|
search: params.search
|
|
812
1197
|
} : void 0;
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
1198
|
+
const endpoint = this.siteScopedEndpoint(
|
|
1199
|
+
siteName,
|
|
1200
|
+
`/products/category/${encodeURIComponent(categorySlug)}`,
|
|
1201
|
+
{ includeSitesSegment: false }
|
|
1202
|
+
);
|
|
1203
|
+
const path = this.buildPath(endpoint);
|
|
1204
|
+
return this.fetchWithCache(
|
|
1205
|
+
endpoint,
|
|
1206
|
+
queryParams,
|
|
1207
|
+
this.buildProductTags(siteName, [
|
|
1208
|
+
"products:category",
|
|
1209
|
+
`products:category:${siteName}:${categorySlug}`
|
|
1210
|
+
]),
|
|
1211
|
+
cachePolicy,
|
|
1212
|
+
() => this.http.get(path, queryParams)
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1215
|
+
buildProductTags(siteName, extraTags = []) {
|
|
1216
|
+
const tags = /* @__PURE__ */ new Set(["products"]);
|
|
1217
|
+
if (siteName) {
|
|
1218
|
+
tags.add(`products:site:${siteName}`);
|
|
1219
|
+
}
|
|
1220
|
+
extraTags.filter(Boolean).forEach((tag) => tags.add(tag));
|
|
1221
|
+
return Array.from(tags.values());
|
|
820
1222
|
}
|
|
821
1223
|
};
|
|
822
1224
|
|
|
823
1225
|
// src/client/categories-client.ts
|
|
824
1226
|
var CategoriesClient = class extends BaseClient {
|
|
825
|
-
constructor(http) {
|
|
826
|
-
super(http, "/api/v1");
|
|
1227
|
+
constructor(http, cache) {
|
|
1228
|
+
super(http, "/api/v1", cache);
|
|
827
1229
|
}
|
|
828
1230
|
/**
|
|
829
1231
|
* Get all categories
|
|
@@ -846,14 +1248,20 @@ var CategoriesClient = class extends BaseClient {
|
|
|
846
1248
|
/**
|
|
847
1249
|
* Get product category by slug (for products)
|
|
848
1250
|
*/
|
|
849
|
-
async getProductCategoryBySlug(siteName, slug) {
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
1251
|
+
async getProductCategoryBySlug(siteName, slug, cachePolicy) {
|
|
1252
|
+
const endpoint = this.siteScopedEndpoint(
|
|
1253
|
+
siteName,
|
|
1254
|
+
`/product_category/slug/${encodeURIComponent(slug)}`,
|
|
1255
|
+
{ includeSitesSegment: false }
|
|
1256
|
+
);
|
|
1257
|
+
const path = this.buildPath(endpoint);
|
|
1258
|
+
return this.fetchWithCache(
|
|
1259
|
+
endpoint,
|
|
1260
|
+
void 0,
|
|
1261
|
+
this.buildCategoryTags(siteName, slug),
|
|
1262
|
+
cachePolicy,
|
|
1263
|
+
() => this.http.get(path)
|
|
1264
|
+
);
|
|
857
1265
|
}
|
|
858
1266
|
/**
|
|
859
1267
|
* Create new category
|
|
@@ -922,12 +1330,22 @@ var CategoriesClient = class extends BaseClient {
|
|
|
922
1330
|
async searchCategories(query, params) {
|
|
923
1331
|
return this.http.get(`/categories/search`, { q: query, ...params });
|
|
924
1332
|
}
|
|
1333
|
+
buildCategoryTags(siteName, slug) {
|
|
1334
|
+
const tags = /* @__PURE__ */ new Set(["categories"]);
|
|
1335
|
+
if (siteName) {
|
|
1336
|
+
tags.add(`categories:site:${siteName}`);
|
|
1337
|
+
}
|
|
1338
|
+
if (slug) {
|
|
1339
|
+
tags.add(`categories:product:${siteName}:${slug}`);
|
|
1340
|
+
}
|
|
1341
|
+
return Array.from(tags.values());
|
|
1342
|
+
}
|
|
925
1343
|
};
|
|
926
1344
|
|
|
927
1345
|
// src/client/webhooks-client.ts
|
|
928
1346
|
var WebhooksClient = class extends BaseClient {
|
|
929
|
-
constructor(http) {
|
|
930
|
-
super(http, "/api/v1");
|
|
1347
|
+
constructor(http, cache) {
|
|
1348
|
+
super(http, "/api/v1", cache);
|
|
931
1349
|
}
|
|
932
1350
|
/**
|
|
933
1351
|
* Get all webhooks
|
|
@@ -1024,8 +1442,8 @@ var WebhooksClient = class extends BaseClient {
|
|
|
1024
1442
|
|
|
1025
1443
|
// src/client/checkout-client.ts
|
|
1026
1444
|
var CheckoutClient = class extends BaseClient {
|
|
1027
|
-
constructor(http) {
|
|
1028
|
-
super(http, "/api/v1");
|
|
1445
|
+
constructor(http, cache) {
|
|
1446
|
+
super(http, "/api/v1", cache);
|
|
1029
1447
|
}
|
|
1030
1448
|
/**
|
|
1031
1449
|
* Get CSRF token for a specific site
|
|
@@ -1136,8 +1554,8 @@ var CheckoutClient = class extends BaseClient {
|
|
|
1136
1554
|
|
|
1137
1555
|
// src/client/contact-client.ts
|
|
1138
1556
|
var ContactClient = class extends BaseClient {
|
|
1139
|
-
constructor(http) {
|
|
1140
|
-
super(http, "/api/v1");
|
|
1557
|
+
constructor(http, cache) {
|
|
1558
|
+
super(http, "/api/v1", cache);
|
|
1141
1559
|
}
|
|
1142
1560
|
/**
|
|
1143
1561
|
* Build a contact endpoint scoped to a site (without /sites prefix)
|
|
@@ -1243,8 +1661,8 @@ var ContactClient = class extends BaseClient {
|
|
|
1243
1661
|
|
|
1244
1662
|
// src/client/newsletter-client.ts
|
|
1245
1663
|
var NewsletterClient = class extends BaseClient {
|
|
1246
|
-
constructor(http) {
|
|
1247
|
-
super(http, "/api/v1");
|
|
1664
|
+
constructor(http, cache) {
|
|
1665
|
+
super(http, "/api/v1", cache);
|
|
1248
1666
|
}
|
|
1249
1667
|
/**
|
|
1250
1668
|
* Build a newsletter endpoint scoped to a site (without /sites prefix)
|
|
@@ -1439,6 +1857,7 @@ var NewsletterClient = class extends BaseClient {
|
|
|
1439
1857
|
// src/perspect-api-client.ts
|
|
1440
1858
|
var PerspectApiClient = class {
|
|
1441
1859
|
http;
|
|
1860
|
+
cache;
|
|
1442
1861
|
// Service clients
|
|
1443
1862
|
auth;
|
|
1444
1863
|
content;
|
|
@@ -1456,17 +1875,18 @@ var PerspectApiClient = class {
|
|
|
1456
1875
|
throw new Error("baseUrl is required in PerspectApiConfig");
|
|
1457
1876
|
}
|
|
1458
1877
|
this.http = new HttpClient(config);
|
|
1459
|
-
this.
|
|
1460
|
-
this.
|
|
1461
|
-
this.
|
|
1462
|
-
this.
|
|
1463
|
-
this.
|
|
1464
|
-
this.
|
|
1465
|
-
this.
|
|
1466
|
-
this.
|
|
1467
|
-
this.
|
|
1468
|
-
this.
|
|
1469
|
-
this.
|
|
1878
|
+
this.cache = new CacheManager(config.cache);
|
|
1879
|
+
this.auth = new AuthClient(this.http, this.cache);
|
|
1880
|
+
this.content = new ContentClient(this.http, this.cache);
|
|
1881
|
+
this.apiKeys = new ApiKeysClient(this.http, this.cache);
|
|
1882
|
+
this.organizations = new OrganizationsClient(this.http, this.cache);
|
|
1883
|
+
this.sites = new SitesClient(this.http, this.cache);
|
|
1884
|
+
this.products = new ProductsClient(this.http, this.cache);
|
|
1885
|
+
this.categories = new CategoriesClient(this.http, this.cache);
|
|
1886
|
+
this.webhooks = new WebhooksClient(this.http, this.cache);
|
|
1887
|
+
this.checkout = new CheckoutClient(this.http, this.cache);
|
|
1888
|
+
this.contact = new ContactClient(this.http, this.cache);
|
|
1889
|
+
this.newsletter = new NewsletterClient(this.http, this.cache);
|
|
1470
1890
|
}
|
|
1471
1891
|
/**
|
|
1472
1892
|
* Update authentication token
|
|
@@ -2046,13 +2466,16 @@ export {
|
|
|
2046
2466
|
ApiKeysClient,
|
|
2047
2467
|
AuthClient,
|
|
2048
2468
|
BaseClient,
|
|
2469
|
+
CacheManager,
|
|
2049
2470
|
CategoriesClient,
|
|
2050
2471
|
CheckoutClient,
|
|
2051
2472
|
ContactClient,
|
|
2052
2473
|
ContentClient,
|
|
2053
2474
|
DEFAULT_IMAGE_SIZES,
|
|
2054
2475
|
HttpClient,
|
|
2476
|
+
InMemoryCacheAdapter,
|
|
2055
2477
|
NewsletterClient,
|
|
2478
|
+
NoopCacheAdapter,
|
|
2056
2479
|
OrganizationsClient,
|
|
2057
2480
|
PerspectApiClient,
|
|
2058
2481
|
ProductsClient,
|