perspectapi-ts-sdk 1.5.1 → 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 +497 -67
- package/dist/index.mjs +494 -67
- 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 -6
- package/src/client/checkout-client.ts +4 -3
- 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.js
CHANGED
|
@@ -23,13 +23,16 @@ __export(index_exports, {
|
|
|
23
23
|
ApiKeysClient: () => ApiKeysClient,
|
|
24
24
|
AuthClient: () => AuthClient,
|
|
25
25
|
BaseClient: () => BaseClient,
|
|
26
|
+
CacheManager: () => CacheManager,
|
|
26
27
|
CategoriesClient: () => CategoriesClient,
|
|
27
28
|
CheckoutClient: () => CheckoutClient,
|
|
28
29
|
ContactClient: () => ContactClient,
|
|
29
30
|
ContentClient: () => ContentClient,
|
|
30
31
|
DEFAULT_IMAGE_SIZES: () => DEFAULT_IMAGE_SIZES,
|
|
31
32
|
HttpClient: () => HttpClient,
|
|
33
|
+
InMemoryCacheAdapter: () => InMemoryCacheAdapter,
|
|
32
34
|
NewsletterClient: () => NewsletterClient,
|
|
35
|
+
NoopCacheAdapter: () => NoopCacheAdapter,
|
|
33
36
|
OrganizationsClient: () => OrganizationsClient,
|
|
34
37
|
PerspectApiClient: () => PerspectApiClient,
|
|
35
38
|
ProductsClient: () => ProductsClient,
|
|
@@ -263,13 +266,273 @@ function createApiError(error) {
|
|
|
263
266
|
};
|
|
264
267
|
}
|
|
265
268
|
|
|
269
|
+
// src/cache/in-memory-adapter.ts
|
|
270
|
+
var InMemoryCacheAdapter = class {
|
|
271
|
+
store = /* @__PURE__ */ new Map();
|
|
272
|
+
async get(key) {
|
|
273
|
+
const entry = this.store.get(key);
|
|
274
|
+
if (!entry) {
|
|
275
|
+
return void 0;
|
|
276
|
+
}
|
|
277
|
+
if (entry.expiresAt && entry.expiresAt <= Date.now()) {
|
|
278
|
+
this.store.delete(key);
|
|
279
|
+
return void 0;
|
|
280
|
+
}
|
|
281
|
+
return entry.value;
|
|
282
|
+
}
|
|
283
|
+
async set(key, value, options) {
|
|
284
|
+
const expiresAt = options?.ttlSeconds && options.ttlSeconds > 0 ? Date.now() + options.ttlSeconds * 1e3 : void 0;
|
|
285
|
+
this.store.set(key, { value, expiresAt });
|
|
286
|
+
}
|
|
287
|
+
async delete(key) {
|
|
288
|
+
this.store.delete(key);
|
|
289
|
+
}
|
|
290
|
+
async deleteMany(keys) {
|
|
291
|
+
keys.forEach((key) => this.store.delete(key));
|
|
292
|
+
}
|
|
293
|
+
async clear() {
|
|
294
|
+
this.store.clear();
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
// src/cache/noop-adapter.ts
|
|
299
|
+
var NoopCacheAdapter = class {
|
|
300
|
+
async get() {
|
|
301
|
+
return void 0;
|
|
302
|
+
}
|
|
303
|
+
async set() {
|
|
304
|
+
}
|
|
305
|
+
async delete() {
|
|
306
|
+
}
|
|
307
|
+
async deleteMany() {
|
|
308
|
+
}
|
|
309
|
+
async clear() {
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
// src/cache/cache-manager.ts
|
|
314
|
+
var TAG_PREFIX = "__tag__";
|
|
315
|
+
var CacheManager = class {
|
|
316
|
+
adapter;
|
|
317
|
+
defaultTtlSeconds;
|
|
318
|
+
keyPrefix;
|
|
319
|
+
enabled;
|
|
320
|
+
constructor(config) {
|
|
321
|
+
const defaultOptions = {
|
|
322
|
+
defaultTtlSeconds: 300,
|
|
323
|
+
keyPrefix: "perspectapi"
|
|
324
|
+
};
|
|
325
|
+
const mergedConfig = {
|
|
326
|
+
...defaultOptions,
|
|
327
|
+
...config
|
|
328
|
+
};
|
|
329
|
+
if (config && config.enabled === false) {
|
|
330
|
+
this.enabled = false;
|
|
331
|
+
this.adapter = new NoopCacheAdapter();
|
|
332
|
+
} else if (config && config.adapter) {
|
|
333
|
+
this.enabled = true;
|
|
334
|
+
this.adapter = config.adapter;
|
|
335
|
+
} else if (config) {
|
|
336
|
+
this.enabled = true;
|
|
337
|
+
this.adapter = new InMemoryCacheAdapter();
|
|
338
|
+
} else {
|
|
339
|
+
this.enabled = false;
|
|
340
|
+
this.adapter = new NoopCacheAdapter();
|
|
341
|
+
}
|
|
342
|
+
this.defaultTtlSeconds = mergedConfig.defaultTtlSeconds ?? 300;
|
|
343
|
+
this.keyPrefix = mergedConfig.keyPrefix ?? "perspectapi";
|
|
344
|
+
}
|
|
345
|
+
isEnabled() {
|
|
346
|
+
return this.enabled;
|
|
347
|
+
}
|
|
348
|
+
getKeyPrefix() {
|
|
349
|
+
return this.keyPrefix;
|
|
350
|
+
}
|
|
351
|
+
buildKey(parts) {
|
|
352
|
+
const normalized = parts.flatMap((part) => this.normalizeKeyPart(part)).filter((part) => part !== void 0 && part !== null && part !== "");
|
|
353
|
+
return normalized.join(":");
|
|
354
|
+
}
|
|
355
|
+
async getOrSet(key, resolveValue, policy) {
|
|
356
|
+
if (!this.enabled || policy?.skipCache) {
|
|
357
|
+
const value2 = await resolveValue();
|
|
358
|
+
if (this.enabled && !policy?.skipCache && policy?.ttlSeconds !== 0) {
|
|
359
|
+
await this.set(key, value2, policy);
|
|
360
|
+
}
|
|
361
|
+
return value2;
|
|
362
|
+
}
|
|
363
|
+
const namespacedKey = this.namespacedKey(key);
|
|
364
|
+
const cachedRaw = await this.adapter.get(namespacedKey);
|
|
365
|
+
if (cachedRaw) {
|
|
366
|
+
const entry = this.deserialize(cachedRaw);
|
|
367
|
+
if (!entry.expiresAt || entry.expiresAt > Date.now()) {
|
|
368
|
+
return entry.value;
|
|
369
|
+
}
|
|
370
|
+
await this.adapter.delete(namespacedKey);
|
|
371
|
+
if (entry.tags?.length) {
|
|
372
|
+
await this.removeKeyFromTags(namespacedKey, entry.tags);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
const value = await resolveValue();
|
|
376
|
+
await this.set(key, value, policy);
|
|
377
|
+
return value;
|
|
378
|
+
}
|
|
379
|
+
async set(key, value, options) {
|
|
380
|
+
if (!this.enabled || options?.ttlSeconds === 0) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
const namespacedKey = this.namespacedKey(key);
|
|
384
|
+
const ttlSeconds = options?.ttlSeconds ?? this.defaultTtlSeconds;
|
|
385
|
+
const entry = {
|
|
386
|
+
value,
|
|
387
|
+
expiresAt: ttlSeconds > 0 ? Date.now() + ttlSeconds * 1e3 : void 0,
|
|
388
|
+
tags: options?.tags,
|
|
389
|
+
metadata: options?.metadata
|
|
390
|
+
};
|
|
391
|
+
await this.adapter.set(namespacedKey, this.serialize(entry), {
|
|
392
|
+
ttlSeconds: ttlSeconds > 0 ? ttlSeconds : void 0
|
|
393
|
+
});
|
|
394
|
+
if (options?.tags?.length) {
|
|
395
|
+
await this.registerKeyTags(namespacedKey, options.tags);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
async delete(key) {
|
|
399
|
+
if (!this.enabled) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const namespacedKey = this.namespacedKey(key);
|
|
403
|
+
await this.adapter.delete(namespacedKey);
|
|
404
|
+
}
|
|
405
|
+
async invalidate(options) {
|
|
406
|
+
if (!this.enabled) {
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
if (options.keys?.length) {
|
|
410
|
+
const namespacedKeys = options.keys.map((key) => this.namespacedKey(key));
|
|
411
|
+
if (this.adapter.deleteMany) {
|
|
412
|
+
await this.adapter.deleteMany(namespacedKeys);
|
|
413
|
+
} else {
|
|
414
|
+
await Promise.all(namespacedKeys.map((key) => this.adapter.delete(key)));
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (options.tags?.length) {
|
|
418
|
+
await Promise.all(
|
|
419
|
+
options.tags.map(async (tag) => {
|
|
420
|
+
const tagKey = this.tagKey(tag);
|
|
421
|
+
const payload = await this.adapter.get(tagKey);
|
|
422
|
+
if (!payload) {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
const keys = this.deserializeTagSet(payload);
|
|
426
|
+
if (keys.length) {
|
|
427
|
+
if (this.adapter.deleteMany) {
|
|
428
|
+
await this.adapter.deleteMany(keys);
|
|
429
|
+
} else {
|
|
430
|
+
await Promise.all(keys.map((key) => this.adapter.delete(key)));
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
await this.adapter.delete(tagKey);
|
|
434
|
+
})
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
namespacedKey(key) {
|
|
439
|
+
return `${this.keyPrefix}:${key}`;
|
|
440
|
+
}
|
|
441
|
+
tagKey(tag) {
|
|
442
|
+
return this.namespacedKey(`${TAG_PREFIX}:${tag}`);
|
|
443
|
+
}
|
|
444
|
+
serialize(entry) {
|
|
445
|
+
return JSON.stringify(entry);
|
|
446
|
+
}
|
|
447
|
+
deserialize(payload) {
|
|
448
|
+
try {
|
|
449
|
+
return JSON.parse(payload);
|
|
450
|
+
} catch {
|
|
451
|
+
return { value: payload };
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
deserializeTagSet(payload) {
|
|
455
|
+
try {
|
|
456
|
+
const parsed = JSON.parse(payload);
|
|
457
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
458
|
+
} catch {
|
|
459
|
+
return [];
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
async registerKeyTags(namespacedKey, tags) {
|
|
463
|
+
await Promise.all(
|
|
464
|
+
tags.map(async (tag) => {
|
|
465
|
+
const tagKey = this.tagKey(tag);
|
|
466
|
+
const existingPayload = await this.adapter.get(tagKey);
|
|
467
|
+
const keys = existingPayload ? this.deserializeTagSet(existingPayload) : [];
|
|
468
|
+
if (!keys.includes(namespacedKey)) {
|
|
469
|
+
keys.push(namespacedKey);
|
|
470
|
+
await this.adapter.set(tagKey, JSON.stringify(keys));
|
|
471
|
+
}
|
|
472
|
+
})
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
async removeKeyFromTags(namespacedKey, tags) {
|
|
476
|
+
await Promise.all(
|
|
477
|
+
tags.map(async (tag) => {
|
|
478
|
+
const tagKey = this.tagKey(tag);
|
|
479
|
+
const existingPayload = await this.adapter.get(tagKey);
|
|
480
|
+
if (!existingPayload) {
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
const keys = this.deserializeTagSet(existingPayload);
|
|
484
|
+
const updated = keys.filter((key) => key !== namespacedKey);
|
|
485
|
+
if (updated.length === 0) {
|
|
486
|
+
await this.adapter.delete(tagKey);
|
|
487
|
+
} else if (updated.length !== keys.length) {
|
|
488
|
+
await this.adapter.set(tagKey, JSON.stringify(updated));
|
|
489
|
+
}
|
|
490
|
+
})
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
normalizeKeyPart(part) {
|
|
494
|
+
if (part === void 0 || part === null || part === "") {
|
|
495
|
+
return [];
|
|
496
|
+
}
|
|
497
|
+
if (Array.isArray(part)) {
|
|
498
|
+
return part.flatMap((item) => this.normalizeKeyPart(item));
|
|
499
|
+
}
|
|
500
|
+
if (typeof part === "object") {
|
|
501
|
+
return [this.normalizeObject(part)];
|
|
502
|
+
}
|
|
503
|
+
return [String(part)];
|
|
504
|
+
}
|
|
505
|
+
normalizeObject(input) {
|
|
506
|
+
const sortedKeys = Object.keys(input).sort();
|
|
507
|
+
const normalized = {};
|
|
508
|
+
for (const key of sortedKeys) {
|
|
509
|
+
const value = input[key];
|
|
510
|
+
if (value === void 0) {
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
514
|
+
normalized[key] = JSON.parse(this.normalizeObject(value));
|
|
515
|
+
} else if (Array.isArray(value)) {
|
|
516
|
+
normalized[key] = value.map(
|
|
517
|
+
(item) => typeof item === "object" && item !== null ? JSON.parse(this.normalizeObject(item)) : item
|
|
518
|
+
);
|
|
519
|
+
} else {
|
|
520
|
+
normalized[key] = value;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return JSON.stringify(normalized);
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
|
|
266
527
|
// src/client/base-client.ts
|
|
267
528
|
var BaseClient = class {
|
|
268
529
|
http;
|
|
269
530
|
basePath;
|
|
270
|
-
|
|
531
|
+
cache;
|
|
532
|
+
constructor(http, basePath, cache) {
|
|
271
533
|
this.http = http;
|
|
272
534
|
this.basePath = basePath;
|
|
535
|
+
this.cache = cache && cache.isEnabled() ? cache : void 0;
|
|
273
536
|
}
|
|
274
537
|
/**
|
|
275
538
|
* Build a site-scoped endpoint relative to the API base path
|
|
@@ -329,12 +592,71 @@ var BaseClient = class {
|
|
|
329
592
|
async delete(endpoint, csrfToken) {
|
|
330
593
|
return this.http.delete(this.buildPath(endpoint), { csrfToken });
|
|
331
594
|
}
|
|
595
|
+
/**
|
|
596
|
+
* Fetch a GET endpoint with optional caching support.
|
|
597
|
+
*/
|
|
598
|
+
async fetchWithCache(endpoint, params, tags, policy, fetcher) {
|
|
599
|
+
if (!this.cache) {
|
|
600
|
+
return fetcher();
|
|
601
|
+
}
|
|
602
|
+
const cacheKey = this.buildCacheKey(endpoint, params);
|
|
603
|
+
const combinedPolicy = policy ? { ...policy, tags: this.mergeTags(tags, policy.tags) } : { tags };
|
|
604
|
+
return this.cache.getOrSet(cacheKey, fetcher, combinedPolicy);
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Invalidate cache entries by keys or tags.
|
|
608
|
+
*/
|
|
609
|
+
async invalidateCache(options) {
|
|
610
|
+
if (!this.cache) {
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
await this.cache.invalidate(options);
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Build a consistent cache key for an endpoint + params combination.
|
|
617
|
+
*/
|
|
618
|
+
buildCacheKey(endpoint, params) {
|
|
619
|
+
const sanitizedEndpoint = endpoint.replace(/^\//, "");
|
|
620
|
+
const baseSegment = this.basePath.replace(/^\//, "");
|
|
621
|
+
const parts = [baseSegment, sanitizedEndpoint];
|
|
622
|
+
if (params && Object.keys(params).length > 0) {
|
|
623
|
+
parts.push(this.sortObject(params));
|
|
624
|
+
}
|
|
625
|
+
if (this.cache) {
|
|
626
|
+
return this.cache.buildKey(parts);
|
|
627
|
+
}
|
|
628
|
+
return parts.map((part) => typeof part === "string" ? part : JSON.stringify(part)).join(":");
|
|
629
|
+
}
|
|
630
|
+
mergeTags(defaultTags, overrideTags) {
|
|
631
|
+
const combined = /* @__PURE__ */ new Set();
|
|
632
|
+
defaultTags.forEach((tag) => combined.add(tag));
|
|
633
|
+
overrideTags?.forEach((tag) => combined.add(tag));
|
|
634
|
+
return Array.from(combined.values());
|
|
635
|
+
}
|
|
636
|
+
sortObject(input) {
|
|
637
|
+
const sortedKeys = Object.keys(input).sort();
|
|
638
|
+
const result = {};
|
|
639
|
+
for (const key of sortedKeys) {
|
|
640
|
+
const value = input[key];
|
|
641
|
+
if (value === void 0) continue;
|
|
642
|
+
if (Array.isArray(value)) {
|
|
643
|
+
result[key] = value.map(
|
|
644
|
+
(item) => typeof item === "object" && item !== null ? this.sortObject(item) : item
|
|
645
|
+
);
|
|
646
|
+
} else if (value && typeof value === "object") {
|
|
647
|
+
result[key] = this.sortObject(value);
|
|
648
|
+
} else {
|
|
649
|
+
result[key] = value;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
return result;
|
|
653
|
+
}
|
|
332
654
|
};
|
|
333
655
|
|
|
334
656
|
// src/client/auth-client.ts
|
|
335
657
|
var AuthClient = class extends BaseClient {
|
|
336
|
-
constructor(http) {
|
|
337
|
-
super(http, "/api/v1");
|
|
658
|
+
constructor(http, cache) {
|
|
659
|
+
super(http, "/api/v1", cache);
|
|
338
660
|
}
|
|
339
661
|
/**
|
|
340
662
|
* Get CSRF token
|
|
@@ -395,28 +717,50 @@ var AuthClient = class extends BaseClient {
|
|
|
395
717
|
|
|
396
718
|
// src/client/content-client.ts
|
|
397
719
|
var ContentClient = class extends BaseClient {
|
|
398
|
-
constructor(http) {
|
|
399
|
-
super(http, "/api/v1");
|
|
720
|
+
constructor(http, cache) {
|
|
721
|
+
super(http, "/api/v1", cache);
|
|
400
722
|
}
|
|
401
723
|
/**
|
|
402
724
|
* Get all content with pagination and filtering for a site
|
|
403
725
|
*/
|
|
404
|
-
async getContent(siteName, params) {
|
|
405
|
-
|
|
726
|
+
async getContent(siteName, params, cachePolicy) {
|
|
727
|
+
const endpoint = this.siteScopedEndpoint(siteName);
|
|
728
|
+
const path = this.buildPath(endpoint);
|
|
729
|
+
return this.fetchWithCache(
|
|
730
|
+
endpoint,
|
|
731
|
+
params,
|
|
732
|
+
this.buildContentTags(siteName),
|
|
733
|
+
cachePolicy,
|
|
734
|
+
() => this.http.get(path, params)
|
|
735
|
+
);
|
|
406
736
|
}
|
|
407
737
|
/**
|
|
408
738
|
* Get content by ID
|
|
409
739
|
*/
|
|
410
|
-
async getContentById(id) {
|
|
411
|
-
|
|
740
|
+
async getContentById(id, cachePolicy) {
|
|
741
|
+
const endpoint = `/content/${id}`;
|
|
742
|
+
const path = this.buildPath(endpoint);
|
|
743
|
+
return this.fetchWithCache(
|
|
744
|
+
endpoint,
|
|
745
|
+
void 0,
|
|
746
|
+
this.buildContentTags(void 0, void 0, id),
|
|
747
|
+
cachePolicy,
|
|
748
|
+
() => this.http.get(path)
|
|
749
|
+
);
|
|
412
750
|
}
|
|
413
751
|
/**
|
|
414
752
|
* Get content by slug for a site
|
|
415
753
|
*/
|
|
416
|
-
async getContentBySlug(siteName, slug) {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
754
|
+
async getContentBySlug(siteName, slug, cachePolicy) {
|
|
755
|
+
const endpoint = this.siteScopedEndpoint(siteName, `/slug/${encodeURIComponent(slug)}`);
|
|
756
|
+
const path = this.buildPath(endpoint);
|
|
757
|
+
return this.fetchWithCache(
|
|
758
|
+
endpoint,
|
|
759
|
+
void 0,
|
|
760
|
+
this.buildContentTags(siteName, slug),
|
|
761
|
+
cachePolicy,
|
|
762
|
+
() => this.http.get(path)
|
|
763
|
+
);
|
|
420
764
|
}
|
|
421
765
|
/**
|
|
422
766
|
* Create new content
|
|
@@ -478,12 +822,25 @@ var ContentClient = class extends BaseClient {
|
|
|
478
822
|
async duplicateContent(id) {
|
|
479
823
|
return this.create(`/content/${id}/duplicate`, {});
|
|
480
824
|
}
|
|
825
|
+
buildContentTags(siteName, slug, id) {
|
|
826
|
+
const tags = /* @__PURE__ */ new Set(["content"]);
|
|
827
|
+
if (siteName) {
|
|
828
|
+
tags.add(`content:site:${siteName}`);
|
|
829
|
+
}
|
|
830
|
+
if (slug) {
|
|
831
|
+
tags.add(`content:slug:${siteName}:${slug}`);
|
|
832
|
+
}
|
|
833
|
+
if (typeof id === "number") {
|
|
834
|
+
tags.add(`content:id:${id}`);
|
|
835
|
+
}
|
|
836
|
+
return Array.from(tags.values());
|
|
837
|
+
}
|
|
481
838
|
};
|
|
482
839
|
|
|
483
840
|
// src/client/api-keys-client.ts
|
|
484
841
|
var ApiKeysClient = class extends BaseClient {
|
|
485
|
-
constructor(http) {
|
|
486
|
-
super(http, "/api/v1");
|
|
842
|
+
constructor(http, cache) {
|
|
843
|
+
super(http, "/api/v1", cache);
|
|
487
844
|
}
|
|
488
845
|
/**
|
|
489
846
|
* Get all API keys
|
|
@@ -549,8 +906,8 @@ var ApiKeysClient = class extends BaseClient {
|
|
|
549
906
|
|
|
550
907
|
// src/client/organizations-client.ts
|
|
551
908
|
var OrganizationsClient = class extends BaseClient {
|
|
552
|
-
constructor(http) {
|
|
553
|
-
super(http, "/api/v1");
|
|
909
|
+
constructor(http, cache) {
|
|
910
|
+
super(http, "/api/v1", cache);
|
|
554
911
|
}
|
|
555
912
|
/**
|
|
556
913
|
* Get all organizations
|
|
@@ -622,8 +979,8 @@ var OrganizationsClient = class extends BaseClient {
|
|
|
622
979
|
|
|
623
980
|
// src/client/sites-client.ts
|
|
624
981
|
var SitesClient = class extends BaseClient {
|
|
625
|
-
constructor(http) {
|
|
626
|
-
super(http, "/api/v1");
|
|
982
|
+
constructor(http, cache) {
|
|
983
|
+
super(http, "/api/v1", cache);
|
|
627
984
|
}
|
|
628
985
|
/**
|
|
629
986
|
* Get all sites
|
|
@@ -719,13 +1076,13 @@ var SitesClient = class extends BaseClient {
|
|
|
719
1076
|
|
|
720
1077
|
// src/client/products-client.ts
|
|
721
1078
|
var ProductsClient = class extends BaseClient {
|
|
722
|
-
constructor(http) {
|
|
723
|
-
super(http, "/api/v1");
|
|
1079
|
+
constructor(http, cache) {
|
|
1080
|
+
super(http, "/api/v1", cache);
|
|
724
1081
|
}
|
|
725
1082
|
/**
|
|
726
1083
|
* Get all products for a site
|
|
727
1084
|
*/
|
|
728
|
-
async getProducts(siteName, params) {
|
|
1085
|
+
async getProducts(siteName, params, cachePolicy) {
|
|
729
1086
|
const normalizeList = (value) => {
|
|
730
1087
|
if (value === void 0 || value === null) {
|
|
731
1088
|
return void 0;
|
|
@@ -749,30 +1106,61 @@ var ProductsClient = class extends BaseClient {
|
|
|
749
1106
|
delete normalizedParams.category_id;
|
|
750
1107
|
}
|
|
751
1108
|
}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
1109
|
+
const endpoint = this.siteScopedEndpoint(siteName, "/products", { includeSitesSegment: false });
|
|
1110
|
+
const path = this.buildPath(endpoint);
|
|
1111
|
+
return this.fetchWithCache(
|
|
1112
|
+
endpoint,
|
|
1113
|
+
normalizedParams,
|
|
1114
|
+
this.buildProductTags(siteName, ["products:list"]),
|
|
1115
|
+
cachePolicy,
|
|
1116
|
+
() => this.http.get(path, normalizedParams)
|
|
755
1117
|
);
|
|
756
1118
|
}
|
|
757
1119
|
/**
|
|
758
1120
|
* Get product by ID
|
|
759
1121
|
*/
|
|
760
|
-
async getProductById(id) {
|
|
761
|
-
|
|
1122
|
+
async getProductById(id, cachePolicy) {
|
|
1123
|
+
const endpoint = `/products/${id}`;
|
|
1124
|
+
const path = this.buildPath(endpoint);
|
|
1125
|
+
return this.fetchWithCache(
|
|
1126
|
+
endpoint,
|
|
1127
|
+
void 0,
|
|
1128
|
+
this.buildProductTags(void 0, [`products:id:${id}`]),
|
|
1129
|
+
cachePolicy,
|
|
1130
|
+
() => this.http.get(path)
|
|
1131
|
+
);
|
|
762
1132
|
}
|
|
763
1133
|
/**
|
|
764
1134
|
* Get product by SKU
|
|
765
1135
|
*/
|
|
766
|
-
async getProductBySku(sku) {
|
|
767
|
-
|
|
1136
|
+
async getProductBySku(sku, cachePolicy) {
|
|
1137
|
+
const endpoint = `/products/sku/${encodeURIComponent(sku)}`;
|
|
1138
|
+
const path = this.buildPath(endpoint);
|
|
1139
|
+
return this.fetchWithCache(
|
|
1140
|
+
endpoint,
|
|
1141
|
+
void 0,
|
|
1142
|
+
this.buildProductTags(void 0, [`products:sku:${sku.toLowerCase()}`]),
|
|
1143
|
+
cachePolicy,
|
|
1144
|
+
() => this.http.get(path)
|
|
1145
|
+
);
|
|
768
1146
|
}
|
|
769
1147
|
/**
|
|
770
1148
|
* Get product by slug and site name
|
|
771
1149
|
*/
|
|
772
|
-
async getProductBySlug(siteName, slug) {
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
1150
|
+
async getProductBySlug(siteName, slug, cachePolicy) {
|
|
1151
|
+
const endpoint = this.siteScopedEndpoint(
|
|
1152
|
+
siteName,
|
|
1153
|
+
`/products/slug/${encodeURIComponent(slug)}`,
|
|
1154
|
+
{ includeSitesSegment: false }
|
|
1155
|
+
);
|
|
1156
|
+
const path = this.buildPath(endpoint);
|
|
1157
|
+
return this.fetchWithCache(
|
|
1158
|
+
endpoint,
|
|
1159
|
+
void 0,
|
|
1160
|
+
this.buildProductTags(siteName, [`products:slug:${siteName}:${slug}`]),
|
|
1161
|
+
cachePolicy,
|
|
1162
|
+
() => this.http.get(path)
|
|
1163
|
+
);
|
|
776
1164
|
}
|
|
777
1165
|
/**
|
|
778
1166
|
* Create new product
|
|
@@ -861,27 +1249,44 @@ var ProductsClient = class extends BaseClient {
|
|
|
861
1249
|
/**
|
|
862
1250
|
* Get products by category slug
|
|
863
1251
|
*/
|
|
864
|
-
async getProductsByCategorySlug(siteName, categorySlug, params) {
|
|
1252
|
+
async getProductsByCategorySlug(siteName, categorySlug, params, cachePolicy) {
|
|
865
1253
|
const queryParams = params ? {
|
|
866
1254
|
limit: params.limit,
|
|
867
1255
|
offset: params.page ? (params.page - 1) * (params.limit || 20) : void 0,
|
|
868
1256
|
published: params.published,
|
|
869
1257
|
search: params.search
|
|
870
1258
|
} : void 0;
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
1259
|
+
const endpoint = this.siteScopedEndpoint(
|
|
1260
|
+
siteName,
|
|
1261
|
+
`/products/category/${encodeURIComponent(categorySlug)}`,
|
|
1262
|
+
{ includeSitesSegment: false }
|
|
1263
|
+
);
|
|
1264
|
+
const path = this.buildPath(endpoint);
|
|
1265
|
+
return this.fetchWithCache(
|
|
1266
|
+
endpoint,
|
|
1267
|
+
queryParams,
|
|
1268
|
+
this.buildProductTags(siteName, [
|
|
1269
|
+
"products:category",
|
|
1270
|
+
`products:category:${siteName}:${categorySlug}`
|
|
1271
|
+
]),
|
|
1272
|
+
cachePolicy,
|
|
1273
|
+
() => this.http.get(path, queryParams)
|
|
1274
|
+
);
|
|
1275
|
+
}
|
|
1276
|
+
buildProductTags(siteName, extraTags = []) {
|
|
1277
|
+
const tags = /* @__PURE__ */ new Set(["products"]);
|
|
1278
|
+
if (siteName) {
|
|
1279
|
+
tags.add(`products:site:${siteName}`);
|
|
1280
|
+
}
|
|
1281
|
+
extraTags.filter(Boolean).forEach((tag) => tags.add(tag));
|
|
1282
|
+
return Array.from(tags.values());
|
|
878
1283
|
}
|
|
879
1284
|
};
|
|
880
1285
|
|
|
881
1286
|
// src/client/categories-client.ts
|
|
882
1287
|
var CategoriesClient = class extends BaseClient {
|
|
883
|
-
constructor(http) {
|
|
884
|
-
super(http, "/api/v1");
|
|
1288
|
+
constructor(http, cache) {
|
|
1289
|
+
super(http, "/api/v1", cache);
|
|
885
1290
|
}
|
|
886
1291
|
/**
|
|
887
1292
|
* Get all categories
|
|
@@ -904,10 +1309,20 @@ var CategoriesClient = class extends BaseClient {
|
|
|
904
1309
|
/**
|
|
905
1310
|
* Get product category by slug (for products)
|
|
906
1311
|
*/
|
|
907
|
-
async getProductCategoryBySlug(siteName, slug) {
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
1312
|
+
async getProductCategoryBySlug(siteName, slug, cachePolicy) {
|
|
1313
|
+
const endpoint = this.siteScopedEndpoint(
|
|
1314
|
+
siteName,
|
|
1315
|
+
`/product_category/slug/${encodeURIComponent(slug)}`,
|
|
1316
|
+
{ includeSitesSegment: false }
|
|
1317
|
+
);
|
|
1318
|
+
const path = this.buildPath(endpoint);
|
|
1319
|
+
return this.fetchWithCache(
|
|
1320
|
+
endpoint,
|
|
1321
|
+
void 0,
|
|
1322
|
+
this.buildCategoryTags(siteName, slug),
|
|
1323
|
+
cachePolicy,
|
|
1324
|
+
() => this.http.get(path)
|
|
1325
|
+
);
|
|
911
1326
|
}
|
|
912
1327
|
/**
|
|
913
1328
|
* Create new category
|
|
@@ -976,12 +1391,22 @@ var CategoriesClient = class extends BaseClient {
|
|
|
976
1391
|
async searchCategories(query, params) {
|
|
977
1392
|
return this.http.get(`/categories/search`, { q: query, ...params });
|
|
978
1393
|
}
|
|
1394
|
+
buildCategoryTags(siteName, slug) {
|
|
1395
|
+
const tags = /* @__PURE__ */ new Set(["categories"]);
|
|
1396
|
+
if (siteName) {
|
|
1397
|
+
tags.add(`categories:site:${siteName}`);
|
|
1398
|
+
}
|
|
1399
|
+
if (slug) {
|
|
1400
|
+
tags.add(`categories:product:${siteName}:${slug}`);
|
|
1401
|
+
}
|
|
1402
|
+
return Array.from(tags.values());
|
|
1403
|
+
}
|
|
979
1404
|
};
|
|
980
1405
|
|
|
981
1406
|
// src/client/webhooks-client.ts
|
|
982
1407
|
var WebhooksClient = class extends BaseClient {
|
|
983
|
-
constructor(http) {
|
|
984
|
-
super(http, "/api/v1");
|
|
1408
|
+
constructor(http, cache) {
|
|
1409
|
+
super(http, "/api/v1", cache);
|
|
985
1410
|
}
|
|
986
1411
|
/**
|
|
987
1412
|
* Get all webhooks
|
|
@@ -1078,8 +1503,8 @@ var WebhooksClient = class extends BaseClient {
|
|
|
1078
1503
|
|
|
1079
1504
|
// src/client/checkout-client.ts
|
|
1080
1505
|
var CheckoutClient = class extends BaseClient {
|
|
1081
|
-
constructor(http) {
|
|
1082
|
-
super(http, "/api/v1");
|
|
1506
|
+
constructor(http, cache) {
|
|
1507
|
+
super(http, "/api/v1", cache);
|
|
1083
1508
|
}
|
|
1084
1509
|
/**
|
|
1085
1510
|
* Get CSRF token for a specific site
|
|
@@ -1105,7 +1530,7 @@ var CheckoutClient = class extends BaseClient {
|
|
|
1105
1530
|
token = csrfToken2;
|
|
1106
1531
|
}
|
|
1107
1532
|
return this.http.request(this.buildPath(
|
|
1108
|
-
this.siteScopedEndpoint(siteName, "/checkout/create-session")
|
|
1533
|
+
this.siteScopedEndpoint(siteName, "/checkout/create-session", { includeSitesSegment: false })
|
|
1109
1534
|
), {
|
|
1110
1535
|
method: "POST",
|
|
1111
1536
|
body: data,
|
|
@@ -1190,8 +1615,8 @@ var CheckoutClient = class extends BaseClient {
|
|
|
1190
1615
|
|
|
1191
1616
|
// src/client/contact-client.ts
|
|
1192
1617
|
var ContactClient = class extends BaseClient {
|
|
1193
|
-
constructor(http) {
|
|
1194
|
-
super(http, "/api/v1");
|
|
1618
|
+
constructor(http, cache) {
|
|
1619
|
+
super(http, "/api/v1", cache);
|
|
1195
1620
|
}
|
|
1196
1621
|
/**
|
|
1197
1622
|
* Build a contact endpoint scoped to a site (without /sites prefix)
|
|
@@ -1297,8 +1722,8 @@ var ContactClient = class extends BaseClient {
|
|
|
1297
1722
|
|
|
1298
1723
|
// src/client/newsletter-client.ts
|
|
1299
1724
|
var NewsletterClient = class extends BaseClient {
|
|
1300
|
-
constructor(http) {
|
|
1301
|
-
super(http, "/api/v1");
|
|
1725
|
+
constructor(http, cache) {
|
|
1726
|
+
super(http, "/api/v1", cache);
|
|
1302
1727
|
}
|
|
1303
1728
|
/**
|
|
1304
1729
|
* Build a newsletter endpoint scoped to a site (without /sites prefix)
|
|
@@ -1493,6 +1918,7 @@ var NewsletterClient = class extends BaseClient {
|
|
|
1493
1918
|
// src/perspect-api-client.ts
|
|
1494
1919
|
var PerspectApiClient = class {
|
|
1495
1920
|
http;
|
|
1921
|
+
cache;
|
|
1496
1922
|
// Service clients
|
|
1497
1923
|
auth;
|
|
1498
1924
|
content;
|
|
@@ -1510,17 +1936,18 @@ var PerspectApiClient = class {
|
|
|
1510
1936
|
throw new Error("baseUrl is required in PerspectApiConfig");
|
|
1511
1937
|
}
|
|
1512
1938
|
this.http = new HttpClient(config);
|
|
1513
|
-
this.
|
|
1514
|
-
this.
|
|
1515
|
-
this.
|
|
1516
|
-
this.
|
|
1517
|
-
this.
|
|
1518
|
-
this.
|
|
1519
|
-
this.
|
|
1520
|
-
this.
|
|
1521
|
-
this.
|
|
1522
|
-
this.
|
|
1523
|
-
this.
|
|
1939
|
+
this.cache = new CacheManager(config.cache);
|
|
1940
|
+
this.auth = new AuthClient(this.http, this.cache);
|
|
1941
|
+
this.content = new ContentClient(this.http, this.cache);
|
|
1942
|
+
this.apiKeys = new ApiKeysClient(this.http, this.cache);
|
|
1943
|
+
this.organizations = new OrganizationsClient(this.http, this.cache);
|
|
1944
|
+
this.sites = new SitesClient(this.http, this.cache);
|
|
1945
|
+
this.products = new ProductsClient(this.http, this.cache);
|
|
1946
|
+
this.categories = new CategoriesClient(this.http, this.cache);
|
|
1947
|
+
this.webhooks = new WebhooksClient(this.http, this.cache);
|
|
1948
|
+
this.checkout = new CheckoutClient(this.http, this.cache);
|
|
1949
|
+
this.contact = new ContactClient(this.http, this.cache);
|
|
1950
|
+
this.newsletter = new NewsletterClient(this.http, this.cache);
|
|
1524
1951
|
}
|
|
1525
1952
|
/**
|
|
1526
1953
|
* Update authentication token
|
|
@@ -2101,13 +2528,16 @@ async function createCheckoutSession(options) {
|
|
|
2101
2528
|
ApiKeysClient,
|
|
2102
2529
|
AuthClient,
|
|
2103
2530
|
BaseClient,
|
|
2531
|
+
CacheManager,
|
|
2104
2532
|
CategoriesClient,
|
|
2105
2533
|
CheckoutClient,
|
|
2106
2534
|
ContactClient,
|
|
2107
2535
|
ContentClient,
|
|
2108
2536
|
DEFAULT_IMAGE_SIZES,
|
|
2109
2537
|
HttpClient,
|
|
2538
|
+
InMemoryCacheAdapter,
|
|
2110
2539
|
NewsletterClient,
|
|
2540
|
+
NoopCacheAdapter,
|
|
2111
2541
|
OrganizationsClient,
|
|
2112
2542
|
PerspectApiClient,
|
|
2113
2543
|
ProductsClient,
|