@01.software/sdk 0.21.0 → 0.23.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/index.cjs CHANGED
@@ -49,9 +49,14 @@ __export(src_exports, {
49
49
  ProductApi: () => ProductApi,
50
50
  QueryHooks: () => QueryHooks,
51
51
  RateLimitError: () => RateLimitError,
52
+ ReadOnlyCollectionClient: () => ReadOnlyCollectionClient,
52
53
  RealtimeConnection: () => RealtimeConnection,
53
54
  SDKError: () => SDKError,
55
+ SERVER_COLLECTIONS: () => SERVER_COLLECTIONS,
56
+ SERVER_ONLY_COLLECTIONS: () => SERVER_ONLY_COLLECTIONS,
54
57
  ServerClient: () => ServerClient,
58
+ ServerCollectionClient: () => ServerCollectionClient,
59
+ ServerCollectionQueryBuilder: () => ServerCollectionQueryBuilder,
55
60
  ServerCommerceClient: () => ServerCommerceClient,
56
61
  ServiceUnavailableError: () => ServiceUnavailableError,
57
62
  ShippingApi: () => ShippingApi,
@@ -177,6 +182,46 @@ function resolveMetaImage(ref) {
177
182
  }
178
183
 
179
184
  // src/core/collection/query-builder.ts
185
+ var ReadOnlyCollectionQueryBuilder = class {
186
+ constructor(api, collection) {
187
+ this.api = api;
188
+ this.collection = collection;
189
+ }
190
+ async find(options) {
191
+ return this.api.requestFind(
192
+ `/api/${String(this.collection)}`,
193
+ options
194
+ );
195
+ }
196
+ async findById(id, options) {
197
+ return this.api.requestFindById(
198
+ `/api/${String(this.collection)}/${String(id)}`,
199
+ options
200
+ );
201
+ }
202
+ async count(options) {
203
+ return this.api.requestCount(
204
+ `/api/${String(this.collection)}/count`,
205
+ options
206
+ );
207
+ }
208
+ async findMetadata(options, metadataOptions) {
209
+ const { docs } = await this.find({ ...options, limit: 1, depth: 1 });
210
+ const doc = docs[0];
211
+ if (!doc) return null;
212
+ return generateMetadata(
213
+ extractSeo(doc),
214
+ metadataOptions
215
+ );
216
+ }
217
+ async findMetadataById(id, metadataOptions) {
218
+ const doc = await this.findById(id, { depth: 1 });
219
+ return generateMetadata(
220
+ extractSeo(doc),
221
+ metadataOptions
222
+ );
223
+ }
224
+ };
180
225
  var CollectionQueryBuilder = class {
181
226
  constructor(api, collection) {
182
227
  this.api = api;
@@ -308,6 +353,8 @@ var CollectionQueryBuilder = class {
308
353
  );
309
354
  }
310
355
  };
356
+ var ServerCollectionQueryBuilder = class extends CollectionQueryBuilder {
357
+ };
311
358
 
312
359
  // src/core/collection/http-client.ts
313
360
  var import_qs_esm = require("qs-esm");
@@ -350,8 +397,8 @@ var NetworkError = class extends SDKError {
350
397
  }
351
398
  };
352
399
  var ValidationError = class extends SDKError {
353
- constructor(message, details, userMessage, suggestion) {
354
- super("VALIDATION_ERROR", message, 400, details, userMessage, suggestion);
400
+ constructor(message, details, userMessage, suggestion, status = 400) {
401
+ super("VALIDATION_ERROR", message, status, details, userMessage, suggestion);
355
402
  this.name = "ValidationError";
356
403
  }
357
404
  };
@@ -480,7 +527,7 @@ function isRateLimitError(error) {
480
527
  return error instanceof RateLimitError;
481
528
  }
482
529
  var createNetworkError = (message, status, details, userMessage, suggestion) => new NetworkError(message, status, details, userMessage, suggestion);
483
- var createValidationError = (message, details, userMessage, suggestion) => new ValidationError(message, details, userMessage, suggestion);
530
+ var createValidationError = (message, details, userMessage, suggestion, status) => new ValidationError(message, details, userMessage, suggestion, status);
484
531
  var createApiError = (message, status, details, userMessage, suggestion) => new ApiError(message, status, details, userMessage, suggestion);
485
532
  var createConfigError = (message, details, userMessage, suggestion) => new ConfigError(message, details, userMessage, suggestion);
486
533
  var createTimeoutError = (message, details, userMessage, suggestion) => new TimeoutError(message, details, userMessage, suggestion);
@@ -491,6 +538,16 @@ var createNotFoundError = (message, details, userMessage, suggestion, requestId)
491
538
  var createConflictError = (message, details, userMessage, suggestion, requestId) => new ConflictError(message, details, userMessage, suggestion, requestId);
492
539
  var createRateLimitError = (message, retryAfter, details, userMessage, suggestion, requestId) => new RateLimitError(message, retryAfter, details, userMessage, suggestion, requestId);
493
540
 
541
+ // src/core/internal/utils/credentials.ts
542
+ function requirePublishableKeyForSecret(apiName, publishableKey, secretKey) {
543
+ if (secretKey && !publishableKey) {
544
+ throw createConfigError(
545
+ `publishableKey is required for ${apiName} when secretKey is used. It is sent as X-Publishable-Key for tenant routing, rate limiting, and quota enforcement.`
546
+ );
547
+ }
548
+ return publishableKey ?? "";
549
+ }
550
+
494
551
  // src/core/client/types.ts
495
552
  function resolveApiUrl() {
496
553
  if (typeof process !== "undefined" && process.env) {
@@ -505,7 +562,7 @@ function resolveApiUrl() {
505
562
  // src/core/internal/utils/http.ts
506
563
  var DEFAULT_TIMEOUT = 3e4;
507
564
  var DEFAULT_RETRYABLE_STATUSES = [408, 429, 500, 502, 503, 504];
508
- var NON_RETRYABLE_STATUSES = [400, 401, 403, 404, 422];
565
+ var NON_RETRYABLE_STATUSES = [400, 401, 403, 404, 409, 422];
509
566
  var SAFE_METHODS = ["GET", "HEAD", "OPTIONS"];
510
567
  function debugLog(debug, type, message, data) {
511
568
  if (!debug) return;
@@ -588,6 +645,80 @@ function attachRequestId(err, id) {
588
645
  if (id) err.requestId = id;
589
646
  return err;
590
647
  }
648
+ function createHttpStatusError(status, parsed, details, requestId) {
649
+ const errorDetails = {
650
+ ...details,
651
+ ...parsed.errors && { errors: parsed.errors },
652
+ ...parsed.body && { body: parsed.body }
653
+ };
654
+ const suggestion = getErrorSuggestion(status);
655
+ if (status === 400 || status === 422) {
656
+ return attachRequestId(
657
+ createValidationError(
658
+ parsed.errorMessage,
659
+ errorDetails,
660
+ parsed.userMessage,
661
+ suggestion,
662
+ status
663
+ ),
664
+ requestId
665
+ );
666
+ }
667
+ if (status === 401) {
668
+ return attachRequestId(
669
+ createAuthError(
670
+ parsed.errorMessage,
671
+ errorDetails,
672
+ parsed.userMessage,
673
+ suggestion
674
+ ),
675
+ requestId
676
+ );
677
+ }
678
+ if (status === 403) {
679
+ return attachRequestId(
680
+ createPermissionError(
681
+ parsed.errorMessage,
682
+ errorDetails,
683
+ parsed.userMessage,
684
+ suggestion
685
+ ),
686
+ requestId
687
+ );
688
+ }
689
+ if (status === 404) {
690
+ return attachRequestId(
691
+ createNotFoundError(
692
+ parsed.errorMessage,
693
+ errorDetails,
694
+ parsed.userMessage,
695
+ suggestion
696
+ ),
697
+ requestId
698
+ );
699
+ }
700
+ if (status === 409) {
701
+ return attachRequestId(
702
+ createConflictError(
703
+ parsed.errorMessage,
704
+ errorDetails,
705
+ parsed.userMessage,
706
+ suggestion
707
+ ),
708
+ requestId
709
+ );
710
+ }
711
+ return attachRequestId(
712
+ createNetworkError(
713
+ parsed.errorMessage,
714
+ status,
715
+ errorDetails,
716
+ parsed.userMessage,
717
+ suggestion
718
+ ),
719
+ requestId
720
+ );
721
+ }
591
722
  async function httpFetch(url, options) {
592
723
  const {
593
724
  publishableKey,
@@ -692,14 +823,10 @@ async function httpFetch(url, options) {
692
823
  attempt: attempt + 1
693
824
  };
694
825
  if (NON_RETRYABLE_STATUSES.includes(response.status)) {
695
- throw attachRequestId(
696
- createNetworkError(
697
- parsed.errorMessage,
698
- response.status,
699
- { ...details, ...parsed.errors && { errors: parsed.errors } },
700
- parsed.userMessage,
701
- getErrorSuggestion(response.status)
702
- ),
826
+ throw createHttpStatusError(
827
+ response.status,
828
+ parsed,
829
+ details,
703
830
  requestId
704
831
  );
705
832
  }
@@ -789,7 +916,11 @@ async function httpFetch(url, options) {
789
916
  // src/core/collection/http-client.ts
790
917
  var HttpClient = class {
791
918
  constructor(publishableKey, secretKey, getCustomerToken, onUnauthorized, onRequestId) {
792
- this.publishableKey = publishableKey;
919
+ this.publishableKey = requirePublishableKeyForSecret(
920
+ "CollectionClient",
921
+ publishableKey,
922
+ secretKey
923
+ );
793
924
  this.secretKey = secretKey;
794
925
  this.getCustomerToken = getCustomerToken;
795
926
  this.onUnauthorized = onUnauthorized;
@@ -1062,6 +1193,40 @@ var CollectionClient = class extends HttpClient {
1062
1193
  return this.parseMutationResponse(response);
1063
1194
  }
1064
1195
  };
1196
+ var ServerCollectionClient = class extends CollectionClient {
1197
+ from(collection) {
1198
+ return new ServerCollectionQueryBuilder(this, collection);
1199
+ }
1200
+ };
1201
+ var ReadOnlyCollectionClient = class extends HttpClient {
1202
+ from(collection) {
1203
+ return new ReadOnlyCollectionQueryBuilder(this, collection);
1204
+ }
1205
+ async requestFind(endpoint, options) {
1206
+ const url = this.buildUrl(endpoint, options);
1207
+ const response = await this.fetchWithTracking(url, {
1208
+ ...this.defaultOptions,
1209
+ method: "GET"
1210
+ });
1211
+ return this.parseFindResponse(response);
1212
+ }
1213
+ async requestFindById(endpoint, options) {
1214
+ const url = this.buildUrl(endpoint, options);
1215
+ const response = await this.fetchWithTracking(url, {
1216
+ ...this.defaultOptions,
1217
+ method: "GET"
1218
+ });
1219
+ return this.parseDocumentResponse(response);
1220
+ }
1221
+ async requestCount(endpoint, options) {
1222
+ const url = this.buildUrl(endpoint, options);
1223
+ const response = await this.fetchWithTracking(url, {
1224
+ ...this.defaultOptions,
1225
+ method: "GET"
1226
+ });
1227
+ return this.parseDocumentResponse(response);
1228
+ }
1229
+ };
1065
1230
 
1066
1231
  // src/core/collection/const.ts
1067
1232
  var INTERNAL_COLLECTIONS = [
@@ -1084,6 +1249,7 @@ var INTERNAL_COLLECTIONS = [
1084
1249
  "api-keys",
1085
1250
  "personal-access-tokens",
1086
1251
  "tenant-entitlements",
1252
+ "direct-upload-sessions",
1087
1253
  "webhook-events",
1088
1254
  "webhook-deliveries",
1089
1255
  "audit-logs",
@@ -1113,8 +1279,8 @@ var COLLECTIONS = [
1113
1279
  "transactions",
1114
1280
  "customers",
1115
1281
  "customer-profiles",
1282
+ "customer-profile-lists",
1116
1283
  "customer-addresses",
1117
- "customer-groups",
1118
1284
  "carts",
1119
1285
  "cart-items",
1120
1286
  "discounts",
@@ -1170,6 +1336,11 @@ var COLLECTIONS = [
1170
1336
  "event-occurrences",
1171
1337
  "event-tags"
1172
1338
  ];
1339
+ var SERVER_ONLY_COLLECTIONS = ["customer-groups"];
1340
+ var SERVER_COLLECTIONS = [
1341
+ ...COLLECTIONS,
1342
+ ...SERVER_ONLY_COLLECTIONS
1343
+ ];
1173
1344
 
1174
1345
  // src/core/api/parse-response.ts
1175
1346
  async function parseApiResponse(response, endpoint) {
@@ -1220,7 +1391,11 @@ async function parseApiResponse(response, endpoint) {
1220
1391
  // src/core/community/community-client.ts
1221
1392
  var CommunityClient = class {
1222
1393
  constructor(options) {
1223
- this.publishableKey = options.publishableKey ?? "";
1394
+ this.publishableKey = requirePublishableKeyForSecret(
1395
+ "CommunityClient",
1396
+ options.publishableKey,
1397
+ options.secretKey
1398
+ );
1224
1399
  this.secretKey = options.secretKey;
1225
1400
  this.customerToken = options.customerToken;
1226
1401
  this.onUnauthorized = options.onUnauthorized;
@@ -1390,7 +1565,11 @@ var BaseApi = class {
1390
1565
  if (!options.secretKey) {
1391
1566
  throw createConfigError(`secretKey is required for ${apiName}.`);
1392
1567
  }
1393
- this.publishableKey = options.publishableKey ?? "";
1568
+ this.publishableKey = requirePublishableKeyForSecret(
1569
+ apiName,
1570
+ options.publishableKey,
1571
+ options.secretKey
1572
+ );
1394
1573
  this.secretKey = options.secretKey;
1395
1574
  this.onRequestId = options.onRequestId;
1396
1575
  }
@@ -1684,7 +1863,11 @@ var CartApi = class {
1684
1863
  "Either secretKey or customerToken is required for CartApi."
1685
1864
  );
1686
1865
  }
1687
- this.publishableKey = options.publishableKey ?? "";
1866
+ this.publishableKey = requirePublishableKeyForSecret(
1867
+ "CartApi",
1868
+ options.publishableKey,
1869
+ options.secretKey
1870
+ );
1688
1871
  this.secretKey = options.secretKey;
1689
1872
  this.customerToken = options.customerToken;
1690
1873
  this.onUnauthorized = options.onUnauthorized;
@@ -1880,8 +2063,13 @@ var OrderApi = class extends BaseApi {
1880
2063
  // src/core/commerce/server-commerce-client.ts
1881
2064
  var ServerCommerceClient = class {
1882
2065
  constructor(options) {
2066
+ const publishableKey = requirePublishableKeyForSecret(
2067
+ "ServerCommerceClient",
2068
+ options.publishableKey,
2069
+ options.secretKey
2070
+ );
1883
2071
  const serverOptions = {
1884
- publishableKey: options.publishableKey,
2072
+ publishableKey,
1885
2073
  secretKey: options.secretKey,
1886
2074
  onRequestId: options.onRequestId
1887
2075
  };
@@ -2482,7 +2670,14 @@ var Client = class {
2482
2670
  onUnauthorized,
2483
2671
  onRequestId
2484
2672
  });
2485
- this.collections = new CollectionClient(
2673
+ const collectionClient = new CollectionClient(
2674
+ this.config.publishableKey,
2675
+ void 0,
2676
+ () => this.customer.auth.getToken(),
2677
+ onUnauthorized,
2678
+ onRequestId
2679
+ );
2680
+ this.collections = new ReadOnlyCollectionClient(
2486
2681
  this.config.publishableKey,
2487
2682
  void 0,
2488
2683
  () => this.customer.auth.getToken(),
@@ -2491,7 +2686,7 @@ var Client = class {
2491
2686
  );
2492
2687
  this.query = new QueryHooks(
2493
2688
  this.queryClient,
2494
- this.collections,
2689
+ collectionClient,
2495
2690
  this.customer.auth
2496
2691
  );
2497
2692
  }
@@ -2546,7 +2741,7 @@ var ServerClient = class {
2546
2741
  unbanCustomer: moderationApi.unbanCustomer.bind(moderationApi)
2547
2742
  }
2548
2743
  });
2549
- this.collections = new CollectionClient(
2744
+ this.collections = new ServerCollectionClient(
2550
2745
  this.config.publishableKey,
2551
2746
  this.config.secretKey,
2552
2747
  void 0,
@@ -2868,12 +3063,24 @@ function getVariantPrimaryImage(variant) {
2868
3063
  if (!variant) return null;
2869
3064
  return extractEntityId(variant.thumbnail) ?? getFirstMediaId(variant.images);
2870
3065
  }
3066
+ function getFirstAvailableVariantPrimaryImage(variants) {
3067
+ const orderedVariants = [...variants].sort(compareVariantOrder);
3068
+ for (const variant of orderedVariants) {
3069
+ if (!isVariantAvailableForSale(variant)) continue;
3070
+ const image = getVariantPrimaryImage(variant);
3071
+ if (image != null) return image;
3072
+ }
3073
+ return null;
3074
+ }
2871
3075
  function normalizeOptionValue(value, fallbackOptionId) {
2872
3076
  return {
2873
3077
  id: String(value.id),
2874
3078
  optionId: getRelationID(value.option) ?? fallbackOptionId,
2875
3079
  label: value.value || value.slug || String(value.id),
2876
3080
  slug: value.slug ?? null,
3081
+ swatchColor: value.swatchColor ?? null,
3082
+ thumbnail: value.thumbnail ?? null,
3083
+ images: value.images ?? null,
2877
3084
  order: value._order ?? value["_product-option-values_values_order"] ?? ""
2878
3085
  };
2879
3086
  }
@@ -2902,7 +3109,10 @@ function buildProductOptionMatrix({
2902
3109
  const valuesById = /* @__PURE__ */ new Map();
2903
3110
  for (const rawValue of option.values?.docs ?? []) {
2904
3111
  if (!isProductOptionValueDoc(rawValue)) continue;
2905
- const normalizedValue = normalizeOptionValue(rawValue, String(option.id));
3112
+ const normalizedValue = normalizeOptionValue(
3113
+ rawValue,
3114
+ String(option.id)
3115
+ );
2906
3116
  valuesById.set(normalizedValue.id, normalizedValue);
2907
3117
  }
2908
3118
  return {
@@ -2914,7 +3124,9 @@ function buildProductOptionMatrix({
2914
3124
  )
2915
3125
  };
2916
3126
  }).sort((left, right) => compareOrder(left.order, right.order));
2917
- const optionById = new Map(normalizedOptions.map((option) => [option.id, option]));
3127
+ const optionById = new Map(
3128
+ normalizedOptions.map((option) => [option.id, option])
3129
+ );
2918
3130
  const valueById = /* @__PURE__ */ new Map();
2919
3131
  const valueToOptionId = /* @__PURE__ */ new Map();
2920
3132
  for (const option of normalizedOptions) {
@@ -2993,10 +3205,12 @@ function compareVariantOrder(a, b) {
2993
3205
  function isVariantAvailableForSale(variant) {
2994
3206
  if (variant.isActive === false) return false;
2995
3207
  if (variant.isUnlimited) return true;
2996
- return (variant.stock ?? 0) > 0;
3208
+ return (variant.stock ?? 0) - (variant.reservedStock ?? 0) > 0;
2997
3209
  }
2998
3210
  function getMinMax(values) {
2999
- const numbers = values.filter((value) => typeof value === "number");
3211
+ const numbers = values.filter(
3212
+ (value) => typeof value === "number"
3213
+ );
3000
3214
  if (numbers.length === 0) {
3001
3215
  return { min: null, max: null };
3002
3216
  }
@@ -3007,7 +3221,9 @@ function getMinMax(values) {
3007
3221
  }
3008
3222
  function buildProductListingProjection(product, variants) {
3009
3223
  const orderedVariants = [...variants].sort(compareVariantOrder);
3010
- const activeVariants = orderedVariants.filter((variant) => variant.isActive !== false);
3224
+ const activeVariants = orderedVariants.filter(
3225
+ (variant) => variant.isActive !== false
3226
+ );
3011
3227
  const availableVariants = activeVariants.filter(isVariantAvailableForSale);
3012
3228
  const selectionHintVariant = availableVariants[0] ?? activeVariants[0] ?? null;
3013
3229
  const { min: minPrice, max: maxPrice } = getMinMax(
@@ -3046,7 +3262,9 @@ function buildProductListingGroupsByOption(args) {
3046
3262
  return [];
3047
3263
  }
3048
3264
  const listingBase = buildProductListingProjection(void 0, variants);
3265
+ const optionValuePrimaryImage = extractEntityId(value.thumbnail) ?? getFirstMediaId(value.images);
3049
3266
  const productFallbackImage = extractEntityId(args.product?.thumbnail) ?? getFirstMediaId(args.product?.images);
3267
+ const groupPrimaryImage = getFirstAvailableVariantPrimaryImage(variants) ?? optionValuePrimaryImage ?? productFallbackImage;
3050
3268
  return [
3051
3269
  {
3052
3270
  optionId: primaryOption.id,
@@ -3054,12 +3272,15 @@ function buildProductListingGroupsByOption(args) {
3054
3272
  optionValueId: value.id,
3055
3273
  optionValueLabel: value.label,
3056
3274
  optionValueSlug: value.slug,
3275
+ optionValueSwatchColor: value.swatchColor ?? null,
3276
+ optionValueThumbnail: value.thumbnail ?? null,
3277
+ optionValueImages: value.images ?? null,
3057
3278
  variantIds: variants.map((variant) => String(variant.id)),
3058
3279
  variantCount: variants.length,
3059
3280
  variants,
3060
3281
  listing: {
3061
3282
  ...listingBase,
3062
- primaryImage: listingBase.primaryImage ?? productFallbackImage
3283
+ primaryImage: groupPrimaryImage
3063
3284
  }
3064
3285
  }
3065
3286
  ];