@01.software/sdk 0.21.0 → 0.22.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 CHANGED
@@ -18,9 +18,9 @@ pnpm add @01.software/sdk
18
18
  - React Query integration (both Client and ServerClient)
19
19
  - Mutation hooks (useCreate, useUpdate, useRemove) with automatic cache invalidation
20
20
  - Customer auth hooks (useCustomerMe, useCustomerLogin, etc.) with cache management
21
- - Automatic retry with exponential backoff (non-retryable: 401, 403, 404, 422)
21
+ - Automatic retry with exponential backoff (non-retryable: 400, 401, 403, 404, 409, 422)
22
22
  - Webhook handling with HMAC-SHA256 signature verification
23
- - Sub-path imports (`./webhook`, `./realtime`, `./ui/*`) for tree-shaking
23
+ - Sub-path imports (`./server`, `./webhook`, `./realtime`, `./ui/*`) for tree-shaking
24
24
  - Type-safe read-only `collections.from()` for Client (compile-time write prevention)
25
25
 
26
26
  ### Sub-path Imports
@@ -37,6 +37,9 @@ analytics.track('signup', { plan: 'pro', trial: false }) // custom event with op
37
37
  // Main entry - clients, query builder, hooks, utilities
38
38
  import { createClient, createServerClient } from '@01.software/sdk'
39
39
 
40
+ // Server-only entry - avoids importing browser Client APIs
41
+ import { createServerClient as createServerOnlyClient } from '@01.software/sdk/server'
42
+
40
43
  // Webhook only - webhook handlers
41
44
  import {
42
45
  handleWebhook,
@@ -78,7 +81,7 @@ const { docs } = await client.collections.from('products').find({
78
81
  ```typescript
79
82
  import { createServerClient } from '@01.software/sdk'
80
83
 
81
- const client = createServerClient({
84
+ const server = createServerClient({
82
85
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY,
83
86
  secretKey: process.env.SOFTWARE_SECRET_KEY, // sk01_... opaque API key from Console
84
87
  })
@@ -1,4 +1,4 @@
1
- import { C as Config } from './payload-types-D7lnu9By.cjs';
1
+ import { b as Config } from './payload-types-DeLBmtzd.js';
2
2
 
3
3
  /**
4
4
  * Collection type derived from Payload Config.
@@ -9,7 +9,7 @@ type Collection = keyof Config['collections'];
9
9
  * Internal collections that should not be exposed via SDK.
10
10
  * Includes Payload system collections and admin-only collections.
11
11
  */
12
- declare const INTERNAL_COLLECTIONS: readonly ["users", "payload-kv", "payload-locked-documents", "payload-preferences", "payload-migrations", "field-configs", "system-media", "track-assets", "audiences", "email-logs", "api-usage", "tenant-analytics-daily", "analytics-event-schemas", "subscriptions", "billing-history", "order-status-logs", "api-keys", "personal-access-tokens", "tenant-entitlements", "webhook-events", "webhook-deliveries", "audit-logs", "plans", "webhooks", "event-registrations"];
12
+ declare const INTERNAL_COLLECTIONS: readonly ["users", "payload-kv", "payload-locked-documents", "payload-preferences", "payload-migrations", "field-configs", "system-media", "track-assets", "audiences", "email-logs", "api-usage", "tenant-analytics-daily", "analytics-event-schemas", "subscriptions", "billing-history", "order-status-logs", "api-keys", "personal-access-tokens", "tenant-entitlements", "direct-upload-sessions", "webhook-events", "webhook-deliveries", "audit-logs", "plans", "webhooks", "event-registrations"];
13
13
  /**
14
14
  * Array of all public collection names for runtime use (e.g., Zod enum validation).
15
15
  * This is the single source of truth for which collections are publicly accessible via SDK.
@@ -1,4 +1,4 @@
1
- import { C as Config } from './payload-types-D7lnu9By.js';
1
+ import { b as Config } from './payload-types-DeLBmtzd.cjs';
2
2
 
3
3
  /**
4
4
  * Collection type derived from Payload Config.
@@ -9,7 +9,7 @@ type Collection = keyof Config['collections'];
9
9
  * Internal collections that should not be exposed via SDK.
10
10
  * Includes Payload system collections and admin-only collections.
11
11
  */
12
- declare const INTERNAL_COLLECTIONS: readonly ["users", "payload-kv", "payload-locked-documents", "payload-preferences", "payload-migrations", "field-configs", "system-media", "track-assets", "audiences", "email-logs", "api-usage", "tenant-analytics-daily", "analytics-event-schemas", "subscriptions", "billing-history", "order-status-logs", "api-keys", "personal-access-tokens", "tenant-entitlements", "webhook-events", "webhook-deliveries", "audit-logs", "plans", "webhooks", "event-registrations"];
12
+ declare const INTERNAL_COLLECTIONS: readonly ["users", "payload-kv", "payload-locked-documents", "payload-preferences", "payload-migrations", "field-configs", "system-media", "track-assets", "audiences", "email-logs", "api-usage", "tenant-analytics-daily", "analytics-event-schemas", "subscriptions", "billing-history", "order-status-logs", "api-keys", "personal-access-tokens", "tenant-entitlements", "direct-upload-sessions", "webhook-events", "webhook-deliveries", "audit-logs", "plans", "webhooks", "event-registrations"];
13
13
  /**
14
14
  * Array of all public collection names for runtime use (e.g., Zod enum validation).
15
15
  * This is the single source of truth for which collections are publicly accessible via SDK.
package/dist/index.cjs CHANGED
@@ -49,6 +49,7 @@ __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,
54
55
  ServerClient: () => ServerClient,
@@ -177,6 +178,46 @@ function resolveMetaImage(ref) {
177
178
  }
178
179
 
179
180
  // src/core/collection/query-builder.ts
181
+ var ReadOnlyCollectionQueryBuilder = class {
182
+ constructor(api, collection) {
183
+ this.api = api;
184
+ this.collection = collection;
185
+ }
186
+ async find(options) {
187
+ return this.api.requestFind(
188
+ `/api/${String(this.collection)}`,
189
+ options
190
+ );
191
+ }
192
+ async findById(id, options) {
193
+ return this.api.requestFindById(
194
+ `/api/${String(this.collection)}/${String(id)}`,
195
+ options
196
+ );
197
+ }
198
+ async count(options) {
199
+ return this.api.requestCount(
200
+ `/api/${String(this.collection)}/count`,
201
+ options
202
+ );
203
+ }
204
+ async findMetadata(options, metadataOptions) {
205
+ const { docs } = await this.find({ ...options, limit: 1, depth: 1 });
206
+ const doc = docs[0];
207
+ if (!doc) return null;
208
+ return generateMetadata(
209
+ extractSeo(doc),
210
+ metadataOptions
211
+ );
212
+ }
213
+ async findMetadataById(id, metadataOptions) {
214
+ const doc = await this.findById(id, { depth: 1 });
215
+ return generateMetadata(
216
+ extractSeo(doc),
217
+ metadataOptions
218
+ );
219
+ }
220
+ };
180
221
  var CollectionQueryBuilder = class {
181
222
  constructor(api, collection) {
182
223
  this.api = api;
@@ -350,8 +391,8 @@ var NetworkError = class extends SDKError {
350
391
  }
351
392
  };
352
393
  var ValidationError = class extends SDKError {
353
- constructor(message, details, userMessage, suggestion) {
354
- super("VALIDATION_ERROR", message, 400, details, userMessage, suggestion);
394
+ constructor(message, details, userMessage, suggestion, status = 400) {
395
+ super("VALIDATION_ERROR", message, status, details, userMessage, suggestion);
355
396
  this.name = "ValidationError";
356
397
  }
357
398
  };
@@ -480,7 +521,7 @@ function isRateLimitError(error) {
480
521
  return error instanceof RateLimitError;
481
522
  }
482
523
  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);
524
+ var createValidationError = (message, details, userMessage, suggestion, status) => new ValidationError(message, details, userMessage, suggestion, status);
484
525
  var createApiError = (message, status, details, userMessage, suggestion) => new ApiError(message, status, details, userMessage, suggestion);
485
526
  var createConfigError = (message, details, userMessage, suggestion) => new ConfigError(message, details, userMessage, suggestion);
486
527
  var createTimeoutError = (message, details, userMessage, suggestion) => new TimeoutError(message, details, userMessage, suggestion);
@@ -491,6 +532,16 @@ var createNotFoundError = (message, details, userMessage, suggestion, requestId)
491
532
  var createConflictError = (message, details, userMessage, suggestion, requestId) => new ConflictError(message, details, userMessage, suggestion, requestId);
492
533
  var createRateLimitError = (message, retryAfter, details, userMessage, suggestion, requestId) => new RateLimitError(message, retryAfter, details, userMessage, suggestion, requestId);
493
534
 
535
+ // src/core/internal/utils/credentials.ts
536
+ function requirePublishableKeyForSecret(apiName, publishableKey, secretKey) {
537
+ if (secretKey && !publishableKey) {
538
+ throw createConfigError(
539
+ `publishableKey is required for ${apiName} when secretKey is used. It is sent as X-Publishable-Key for tenant routing, rate limiting, and quota enforcement.`
540
+ );
541
+ }
542
+ return publishableKey ?? "";
543
+ }
544
+
494
545
  // src/core/client/types.ts
495
546
  function resolveApiUrl() {
496
547
  if (typeof process !== "undefined" && process.env) {
@@ -505,7 +556,7 @@ function resolveApiUrl() {
505
556
  // src/core/internal/utils/http.ts
506
557
  var DEFAULT_TIMEOUT = 3e4;
507
558
  var DEFAULT_RETRYABLE_STATUSES = [408, 429, 500, 502, 503, 504];
508
- var NON_RETRYABLE_STATUSES = [400, 401, 403, 404, 422];
559
+ var NON_RETRYABLE_STATUSES = [400, 401, 403, 404, 409, 422];
509
560
  var SAFE_METHODS = ["GET", "HEAD", "OPTIONS"];
510
561
  function debugLog(debug, type, message, data) {
511
562
  if (!debug) return;
@@ -588,6 +639,80 @@ function attachRequestId(err, id) {
588
639
  if (id) err.requestId = id;
589
640
  return err;
590
641
  }
642
+ function createHttpStatusError(status, parsed, details, requestId) {
643
+ const errorDetails = {
644
+ ...details,
645
+ ...parsed.errors && { errors: parsed.errors },
646
+ ...parsed.body && { body: parsed.body }
647
+ };
648
+ const suggestion = getErrorSuggestion(status);
649
+ if (status === 400 || status === 422) {
650
+ return attachRequestId(
651
+ createValidationError(
652
+ parsed.errorMessage,
653
+ errorDetails,
654
+ parsed.userMessage,
655
+ suggestion,
656
+ status
657
+ ),
658
+ requestId
659
+ );
660
+ }
661
+ if (status === 401) {
662
+ return attachRequestId(
663
+ createAuthError(
664
+ parsed.errorMessage,
665
+ errorDetails,
666
+ parsed.userMessage,
667
+ suggestion
668
+ ),
669
+ requestId
670
+ );
671
+ }
672
+ if (status === 403) {
673
+ return attachRequestId(
674
+ createPermissionError(
675
+ parsed.errorMessage,
676
+ errorDetails,
677
+ parsed.userMessage,
678
+ suggestion
679
+ ),
680
+ requestId
681
+ );
682
+ }
683
+ if (status === 404) {
684
+ return attachRequestId(
685
+ createNotFoundError(
686
+ parsed.errorMessage,
687
+ errorDetails,
688
+ parsed.userMessage,
689
+ suggestion
690
+ ),
691
+ requestId
692
+ );
693
+ }
694
+ if (status === 409) {
695
+ return attachRequestId(
696
+ createConflictError(
697
+ parsed.errorMessage,
698
+ errorDetails,
699
+ parsed.userMessage,
700
+ suggestion
701
+ ),
702
+ requestId
703
+ );
704
+ }
705
+ return attachRequestId(
706
+ createNetworkError(
707
+ parsed.errorMessage,
708
+ status,
709
+ errorDetails,
710
+ parsed.userMessage,
711
+ suggestion
712
+ ),
713
+ requestId
714
+ );
715
+ }
591
716
  async function httpFetch(url, options) {
592
717
  const {
593
718
  publishableKey,
@@ -692,14 +817,10 @@ async function httpFetch(url, options) {
692
817
  attempt: attempt + 1
693
818
  };
694
819
  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
- ),
820
+ throw createHttpStatusError(
821
+ response.status,
822
+ parsed,
823
+ details,
703
824
  requestId
704
825
  );
705
826
  }
@@ -789,7 +910,11 @@ async function httpFetch(url, options) {
789
910
  // src/core/collection/http-client.ts
790
911
  var HttpClient = class {
791
912
  constructor(publishableKey, secretKey, getCustomerToken, onUnauthorized, onRequestId) {
792
- this.publishableKey = publishableKey;
913
+ this.publishableKey = requirePublishableKeyForSecret(
914
+ "CollectionClient",
915
+ publishableKey,
916
+ secretKey
917
+ );
793
918
  this.secretKey = secretKey;
794
919
  this.getCustomerToken = getCustomerToken;
795
920
  this.onUnauthorized = onUnauthorized;
@@ -1062,6 +1187,35 @@ var CollectionClient = class extends HttpClient {
1062
1187
  return this.parseMutationResponse(response);
1063
1188
  }
1064
1189
  };
1190
+ var ReadOnlyCollectionClient = class extends HttpClient {
1191
+ from(collection) {
1192
+ return new ReadOnlyCollectionQueryBuilder(this, collection);
1193
+ }
1194
+ async requestFind(endpoint, options) {
1195
+ const url = this.buildUrl(endpoint, options);
1196
+ const response = await this.fetchWithTracking(url, {
1197
+ ...this.defaultOptions,
1198
+ method: "GET"
1199
+ });
1200
+ return this.parseFindResponse(response);
1201
+ }
1202
+ async requestFindById(endpoint, options) {
1203
+ const url = this.buildUrl(endpoint, options);
1204
+ const response = await this.fetchWithTracking(url, {
1205
+ ...this.defaultOptions,
1206
+ method: "GET"
1207
+ });
1208
+ return this.parseDocumentResponse(response);
1209
+ }
1210
+ async requestCount(endpoint, options) {
1211
+ const url = this.buildUrl(endpoint, options);
1212
+ const response = await this.fetchWithTracking(url, {
1213
+ ...this.defaultOptions,
1214
+ method: "GET"
1215
+ });
1216
+ return this.parseDocumentResponse(response);
1217
+ }
1218
+ };
1065
1219
 
1066
1220
  // src/core/collection/const.ts
1067
1221
  var INTERNAL_COLLECTIONS = [
@@ -1084,6 +1238,7 @@ var INTERNAL_COLLECTIONS = [
1084
1238
  "api-keys",
1085
1239
  "personal-access-tokens",
1086
1240
  "tenant-entitlements",
1241
+ "direct-upload-sessions",
1087
1242
  "webhook-events",
1088
1243
  "webhook-deliveries",
1089
1244
  "audit-logs",
@@ -1220,7 +1375,11 @@ async function parseApiResponse(response, endpoint) {
1220
1375
  // src/core/community/community-client.ts
1221
1376
  var CommunityClient = class {
1222
1377
  constructor(options) {
1223
- this.publishableKey = options.publishableKey ?? "";
1378
+ this.publishableKey = requirePublishableKeyForSecret(
1379
+ "CommunityClient",
1380
+ options.publishableKey,
1381
+ options.secretKey
1382
+ );
1224
1383
  this.secretKey = options.secretKey;
1225
1384
  this.customerToken = options.customerToken;
1226
1385
  this.onUnauthorized = options.onUnauthorized;
@@ -1390,7 +1549,11 @@ var BaseApi = class {
1390
1549
  if (!options.secretKey) {
1391
1550
  throw createConfigError(`secretKey is required for ${apiName}.`);
1392
1551
  }
1393
- this.publishableKey = options.publishableKey ?? "";
1552
+ this.publishableKey = requirePublishableKeyForSecret(
1553
+ apiName,
1554
+ options.publishableKey,
1555
+ options.secretKey
1556
+ );
1394
1557
  this.secretKey = options.secretKey;
1395
1558
  this.onRequestId = options.onRequestId;
1396
1559
  }
@@ -1684,7 +1847,11 @@ var CartApi = class {
1684
1847
  "Either secretKey or customerToken is required for CartApi."
1685
1848
  );
1686
1849
  }
1687
- this.publishableKey = options.publishableKey ?? "";
1850
+ this.publishableKey = requirePublishableKeyForSecret(
1851
+ "CartApi",
1852
+ options.publishableKey,
1853
+ options.secretKey
1854
+ );
1688
1855
  this.secretKey = options.secretKey;
1689
1856
  this.customerToken = options.customerToken;
1690
1857
  this.onUnauthorized = options.onUnauthorized;
@@ -1880,8 +2047,13 @@ var OrderApi = class extends BaseApi {
1880
2047
  // src/core/commerce/server-commerce-client.ts
1881
2048
  var ServerCommerceClient = class {
1882
2049
  constructor(options) {
2050
+ const publishableKey = requirePublishableKeyForSecret(
2051
+ "ServerCommerceClient",
2052
+ options.publishableKey,
2053
+ options.secretKey
2054
+ );
1883
2055
  const serverOptions = {
1884
- publishableKey: options.publishableKey,
2056
+ publishableKey,
1885
2057
  secretKey: options.secretKey,
1886
2058
  onRequestId: options.onRequestId
1887
2059
  };
@@ -2482,7 +2654,14 @@ var Client = class {
2482
2654
  onUnauthorized,
2483
2655
  onRequestId
2484
2656
  });
2485
- this.collections = new CollectionClient(
2657
+ const collectionClient = new CollectionClient(
2658
+ this.config.publishableKey,
2659
+ void 0,
2660
+ () => this.customer.auth.getToken(),
2661
+ onUnauthorized,
2662
+ onRequestId
2663
+ );
2664
+ this.collections = new ReadOnlyCollectionClient(
2486
2665
  this.config.publishableKey,
2487
2666
  void 0,
2488
2667
  () => this.customer.auth.getToken(),
@@ -2491,7 +2670,7 @@ var Client = class {
2491
2670
  );
2492
2671
  this.query = new QueryHooks(
2493
2672
  this.queryClient,
2494
- this.collections,
2673
+ collectionClient,
2495
2674
  this.customer.auth
2496
2675
  );
2497
2676
  }