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.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,14 +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
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
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
|
+
);
|
|
915
1326
|
}
|
|
916
1327
|
/**
|
|
917
1328
|
* Create new category
|
|
@@ -980,12 +1391,22 @@ var CategoriesClient = class extends BaseClient {
|
|
|
980
1391
|
async searchCategories(query, params) {
|
|
981
1392
|
return this.http.get(`/categories/search`, { q: query, ...params });
|
|
982
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
|
+
}
|
|
983
1404
|
};
|
|
984
1405
|
|
|
985
1406
|
// src/client/webhooks-client.ts
|
|
986
1407
|
var WebhooksClient = class extends BaseClient {
|
|
987
|
-
constructor(http) {
|
|
988
|
-
super(http, "/api/v1");
|
|
1408
|
+
constructor(http, cache) {
|
|
1409
|
+
super(http, "/api/v1", cache);
|
|
989
1410
|
}
|
|
990
1411
|
/**
|
|
991
1412
|
* Get all webhooks
|
|
@@ -1082,8 +1503,8 @@ var WebhooksClient = class extends BaseClient {
|
|
|
1082
1503
|
|
|
1083
1504
|
// src/client/checkout-client.ts
|
|
1084
1505
|
var CheckoutClient = class extends BaseClient {
|
|
1085
|
-
constructor(http) {
|
|
1086
|
-
super(http, "/api/v1");
|
|
1506
|
+
constructor(http, cache) {
|
|
1507
|
+
super(http, "/api/v1", cache);
|
|
1087
1508
|
}
|
|
1088
1509
|
/**
|
|
1089
1510
|
* Get CSRF token for a specific site
|
|
@@ -1194,8 +1615,8 @@ var CheckoutClient = class extends BaseClient {
|
|
|
1194
1615
|
|
|
1195
1616
|
// src/client/contact-client.ts
|
|
1196
1617
|
var ContactClient = class extends BaseClient {
|
|
1197
|
-
constructor(http) {
|
|
1198
|
-
super(http, "/api/v1");
|
|
1618
|
+
constructor(http, cache) {
|
|
1619
|
+
super(http, "/api/v1", cache);
|
|
1199
1620
|
}
|
|
1200
1621
|
/**
|
|
1201
1622
|
* Build a contact endpoint scoped to a site (without /sites prefix)
|
|
@@ -1301,8 +1722,8 @@ var ContactClient = class extends BaseClient {
|
|
|
1301
1722
|
|
|
1302
1723
|
// src/client/newsletter-client.ts
|
|
1303
1724
|
var NewsletterClient = class extends BaseClient {
|
|
1304
|
-
constructor(http) {
|
|
1305
|
-
super(http, "/api/v1");
|
|
1725
|
+
constructor(http, cache) {
|
|
1726
|
+
super(http, "/api/v1", cache);
|
|
1306
1727
|
}
|
|
1307
1728
|
/**
|
|
1308
1729
|
* Build a newsletter endpoint scoped to a site (without /sites prefix)
|
|
@@ -1497,6 +1918,7 @@ var NewsletterClient = class extends BaseClient {
|
|
|
1497
1918
|
// src/perspect-api-client.ts
|
|
1498
1919
|
var PerspectApiClient = class {
|
|
1499
1920
|
http;
|
|
1921
|
+
cache;
|
|
1500
1922
|
// Service clients
|
|
1501
1923
|
auth;
|
|
1502
1924
|
content;
|
|
@@ -1514,17 +1936,18 @@ var PerspectApiClient = class {
|
|
|
1514
1936
|
throw new Error("baseUrl is required in PerspectApiConfig");
|
|
1515
1937
|
}
|
|
1516
1938
|
this.http = new HttpClient(config);
|
|
1517
|
-
this.
|
|
1518
|
-
this.
|
|
1519
|
-
this.
|
|
1520
|
-
this.
|
|
1521
|
-
this.
|
|
1522
|
-
this.
|
|
1523
|
-
this.
|
|
1524
|
-
this.
|
|
1525
|
-
this.
|
|
1526
|
-
this.
|
|
1527
|
-
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);
|
|
1528
1951
|
}
|
|
1529
1952
|
/**
|
|
1530
1953
|
* Update authentication token
|
|
@@ -2105,13 +2528,16 @@ async function createCheckoutSession(options) {
|
|
|
2105
2528
|
ApiKeysClient,
|
|
2106
2529
|
AuthClient,
|
|
2107
2530
|
BaseClient,
|
|
2531
|
+
CacheManager,
|
|
2108
2532
|
CategoriesClient,
|
|
2109
2533
|
CheckoutClient,
|
|
2110
2534
|
ContactClient,
|
|
2111
2535
|
ContentClient,
|
|
2112
2536
|
DEFAULT_IMAGE_SIZES,
|
|
2113
2537
|
HttpClient,
|
|
2538
|
+
InMemoryCacheAdapter,
|
|
2114
2539
|
NewsletterClient,
|
|
2540
|
+
NoopCacheAdapter,
|
|
2115
2541
|
OrganizationsClient,
|
|
2116
2542
|
PerspectApiClient,
|
|
2117
2543
|
ProductsClient,
|