@01.software/sdk 0.48.0 → 0.49.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +64 -7
  3. package/dist/client.cjs +189 -26
  4. package/dist/client.cjs.map +1 -1
  5. package/dist/client.d.cts +7 -6
  6. package/dist/client.d.ts +7 -6
  7. package/dist/client.js +189 -26
  8. package/dist/client.js.map +1 -1
  9. package/dist/{collection-client-D3ExJU-7.d.ts → collection-client-DnDt6EQz.d.ts} +4 -4
  10. package/dist/{collection-client-CBup9yOx.d.cts → collection-client-XfGLPuTf.d.cts} +4 -4
  11. package/dist/{const-BXEi1Kix.d.cts → const-CBba4lDo.d.cts} +1 -1
  12. package/dist/{const-k9gHylYa.d.ts → const-DCNV5Kq7.d.ts} +1 -1
  13. package/dist/errors.cjs +63 -15
  14. package/dist/errors.cjs.map +1 -1
  15. package/dist/errors.d.cts +10 -1
  16. package/dist/errors.d.ts +10 -1
  17. package/dist/errors.js +63 -15
  18. package/dist/errors.js.map +1 -1
  19. package/dist/{index-BCM9CTL7.d.cts → index-CbKyoEzy.d.cts} +3 -3
  20. package/dist/{index-CHGGkcLj.d.ts → index-DUuIqUS8.d.ts} +3 -3
  21. package/dist/index.cjs +152 -48
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.cts +8 -8
  24. package/dist/index.d.ts +8 -8
  25. package/dist/index.js +152 -48
  26. package/dist/index.js.map +1 -1
  27. package/dist/{payload-types-B6AZQcB7.d.cts → payload-types-MKbx5MuJ.d.cts} +133 -3
  28. package/dist/{payload-types-B6AZQcB7.d.ts → payload-types-MKbx5MuJ.d.ts} +133 -3
  29. package/dist/query.cjs +21 -0
  30. package/dist/query.cjs.map +1 -1
  31. package/dist/query.d.cts +5 -5
  32. package/dist/query.d.ts +5 -5
  33. package/dist/query.js +21 -0
  34. package/dist/query.js.map +1 -1
  35. package/dist/realtime.d.cts +2 -2
  36. package/dist/realtime.d.ts +2 -2
  37. package/dist/server.cjs +189 -26
  38. package/dist/server.cjs.map +1 -1
  39. package/dist/server.d.cts +8 -7
  40. package/dist/server.d.ts +8 -7
  41. package/dist/server.js +189 -26
  42. package/dist/server.js.map +1 -1
  43. package/dist/storefront-cache.cjs +21 -0
  44. package/dist/storefront-cache.cjs.map +1 -1
  45. package/dist/storefront-cache.js +21 -0
  46. package/dist/storefront-cache.js.map +1 -1
  47. package/dist/{types-DbA5V3mt.d.ts → types-1UeOk1Uy.d.cts} +31 -6
  48. package/dist/{types-8us-_C-Q.d.cts → types-BKr_2avj.d.cts} +2 -2
  49. package/dist/{types-CM2je9Kw.d.cts → types-Du4s5anw.d.ts} +31 -6
  50. package/dist/{types-ByD2zii1.d.ts → types-L_nh-ED7.d.ts} +2 -2
  51. package/dist/ui/form.d.cts +1 -1
  52. package/dist/ui/form.d.ts +1 -1
  53. package/dist/ui/video.d.cts +1 -1
  54. package/dist/ui/video.d.ts +1 -1
  55. package/dist/webhook.d.cts +4 -4
  56. package/dist/webhook.d.ts +4 -4
  57. package/package.json +5 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @01.software/sdk
2
2
 
3
+ ## 0.49.0
4
+
5
+ ### Minor Changes
6
+
7
+ - aef44ec: Fix SDK error guards across independently bundled entrypoints and expose the guards from the client and server entries.
8
+ - 86f74d3: Expose immutable membership subscribe-admission snapshot fields in the generated SDK types.
9
+ - edc790e: BREAKING (0.x minor): Checkout `paymentState.activePaymentSession.provider` can
10
+ now be `portone`. Consumers using `@01.software/sdk` 0.45.0, 0.46.0, or 0.46.1
11
+ must upgrade to the 0.49.0 release produced by this minor changeset, or later,
12
+ before the server exposes managed PortOne sessions; otherwise
13
+ `checkouts.retrieve` and `checkouts.query` throw during strict response
14
+ validation. SDK 0.48.0's runtime decoder already accepts `portone`, but its
15
+ published TypeScript `CheckoutPaymentSessionRef` remains `provider: 'toss'`, so
16
+ 0.48.0 is not the fully compatible public-contract floor. `checkouts.create` is
17
+ not part of this break because the current producer does not return an active
18
+ PortOne session reference.
19
+
20
+ Keep custom bridge recovery on the server-only exact lookup.
21
+
22
+ - fa71a21: Add a fail-closed PortOne Standard V2 Toss Payments CARD browser action with a required redirect, PG-safe payment identity, and durable single-slot retry semantics.
23
+ - 5ea6348: Expose non-commerce API reasons through `SDKError.apiReason` and the family-aware `getSDKErrorReason` accessor while preserving strict commerce error typing.
24
+
25
+ ### Patch Changes
26
+
27
+ - cb2a742: Clarify that idempotent commerce mutation response fields do not provide exactly-once side-effect semantics.
28
+ - 15c1024: Synchronize generated Payload types for the internal payment-kernel result and owner-commit records without exposing those collections through a public SDK collection bucket.
29
+
3
30
  ## 0.48.0
4
31
 
5
32
  ### Minor Changes
package/README.md CHANGED
@@ -29,7 +29,7 @@ pnpm add @01.software/sdk
29
29
  import { createClient } from '@01.software/sdk'
30
30
 
31
31
  // Server-only entry - keep Secret Key code out of browser-facing imports
32
- import { createServerClient } from '@01.software/sdk/server'
32
+ import { createServerClient, isCommerceSDKError } from '@01.software/sdk/server'
33
33
 
34
34
  // Webhook only - webhook handlers
35
35
  import {
@@ -61,8 +61,8 @@ entry.
61
61
  | Import | Feature(s) | Install when used |
62
62
  | ----------------------------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- |
63
63
  | `@01.software/sdk` | browser-safe `createClient`, commerce helpers, collection helpers, types | none |
64
- | `@01.software/sdk/client` | browser-safe `createClient` entry | none |
65
- | `@01.software/sdk/server` | `createServerClient`, server-only collection, commerce, and preview APIs | none; keep `secretKey` code on the server |
64
+ | `@01.software/sdk/client` | browser-safe `createClient` entry and SDK error guards | none |
65
+ | `@01.software/sdk/server` | `createServerClient`, server-only APIs, and SDK error guards | none; keep `secretKey` code on the server |
66
66
  | `@01.software/sdk/errors` | SDK error classes and guards | none |
67
67
  | `@01.software/sdk/analytics` | browser analytics client and typed event helpers | none |
68
68
  | `@01.software/sdk/metadata` | SEO metadata extraction and generation helpers | none |
@@ -92,6 +92,9 @@ Migration quick reference:
92
92
  - `createClient` remains available from `@01.software/sdk` and
93
93
  `@01.software/sdk/client`.
94
94
  - `createServerClient` must be imported from `@01.software/sdk/server`.
95
+ - Error guards are available from the same `@01.software/sdk/client` or
96
+ `@01.software/sdk/server` entry as the client that throws; the root and
97
+ `@01.software/sdk/errors` guards also classify errors across entries.
95
98
  - React Query hooks and cache helpers must be imported from
96
99
  `@01.software/sdk/query`.
97
100
  - Product storefront cache resource helpers must be imported from
@@ -1325,10 +1328,8 @@ Treat a replayed Cart result as an operation receipt: never replace a newer
1325
1328
  cached Cart with a lower `revision`; retrieve when response ordering is unclear.
1326
1329
 
1327
1330
  ```typescript
1328
- import {
1329
- createCommerceIdempotencyKey,
1330
- isCommerceSDKError,
1331
- } from '@01.software/sdk'
1331
+ import { createCommerceIdempotencyKey } from '@01.software/sdk'
1332
+ import { isCommerceSDKError } from '@01.software/sdk/errors'
1332
1333
 
1333
1334
  const checkoutIdempotencyKey = createCommerceIdempotencyKey()
1334
1335
  const { checkout, checkoutToken } = await client.commerce.checkouts.create({
@@ -1369,6 +1370,24 @@ if (
1369
1370
  window.location.assign(portOneSession.nextAction.checkoutUrl)
1370
1371
  }
1371
1372
 
1373
+ // Standard V2 is a distinct Browser SDK capability, not a Hosted fallback.
1374
+ // Do not treat the SDK result or mobile redirect as payment finality.
1375
+ const standardPortOneSession =
1376
+ await client.commerce.paymentSessions.createPortOne({
1377
+ checkoutToken,
1378
+ provider: 'portone',
1379
+ providerOptions: { profile: 'standard_v2', paymentMethod: 'CARD' },
1380
+ idempotencyKey: createCommerceIdempotencyKey(),
1381
+ })
1382
+ if (
1383
+ standardPortOneSession.presentationStatus === 'actionable' &&
1384
+ standardPortOneSession.nextAction.type === 'portone_sdk'
1385
+ ) {
1386
+ // PortOne V2 requires its currency constant (`CURRENCY_KRW`), not ISO `KRW`.
1387
+ // await PortOne.requestPayment(standardPortOneSession.nextAction)
1388
+ // Always retrieve the session afterwards; Console lookup/webhook is authoritative.
1389
+ }
1390
+
1372
1391
  // Browser recovery requires the parent capability.
1373
1392
  await client.commerce.paymentSessions.retrieve({
1374
1393
  paymentSessionId: paymentSession.id,
@@ -1424,6 +1443,12 @@ authoritative API response or signed webhook, then report the exact captured
1424
1443
  identity and Money. The create response is intentionally limited to
1425
1444
  `{ id, provider, providerSessionId, money }`.
1426
1445
 
1446
+ Custom bridge sessions intentionally remain `null` in
1447
+ `checkout.paymentState.activePaymentSession`: Checkout exposes only managed
1448
+ adapters from its closed provider vocabulary. Persist the `providerSessionId`
1449
+ returned by `custom.create` and use `custom.retrieve` with that identity as the
1450
+ supported custom-lane settlement recovery path.
1451
+
1427
1452
  ```typescript
1428
1453
  const customSession = await server.commerce.paymentSessions.custom.create({
1429
1454
  provider: 'my-provider',
@@ -1491,6 +1516,38 @@ Canonical commerce error envelopes become `CommerceSDKError`. Branch on its
1491
1516
  stable `reason`, inspect `retryable`, and log `requestId`; a newer server reason
1492
1517
  is preserved as `reason: 'unknown_commerce_error'` plus `rawReason`.
1493
1518
 
1519
+ Non-commerce endpoints preserve their open-ended machine reason separately as
1520
+ `SDKError.apiReason`. Use `getSDKErrorReason(error)` when shared error handling
1521
+ must read either family: it returns the canonical commerce reason, the verbatim
1522
+ unknown commerce `rawReason`, or the non-commerce `apiReason`. Existing
1523
+ `details` payloads remain available for compatibility, but consumers no longer
1524
+ need to normalize `details.reason` and `details.body.reason` themselves.
1525
+
1526
+ #### Idempotent mutation results are not exactly-once receipts
1527
+
1528
+ No commerce mutation response field is a safe exactly-once gate for a
1529
+ non-idempotent side effect such as an operational alert, outbound webhook, or
1530
+ ledger entry. Use a consumer-owned durable claim keyed by the side effect's
1531
+ stable logical identity. The public webhook guide's
1532
+ [`processOnce`](https://docs.01.software/developers/webhooks) pattern uses a
1533
+ database unique key, queue idempotency store, or provider-safe send ledger.
1534
+
1535
+ `custom.confirm` returns `alreadyConfirmed: true` when the session was already
1536
+ confirmed at observation time. It does not attribute the transition to the
1537
+ current call. The field is absent from the initial successful confirmation, so
1538
+ code that only interprets the response shape must test
1539
+ `'alreadyConfirmed' in result`, not `result.alreadyConfirmed === false`. That
1540
+ presence check is still not a side-effect gate: when the initial commit succeeds
1541
+ but its response is lost, a retry returns `alreadyConfirmed: true` and would
1542
+ otherwise silently miss the transition.
1543
+
1544
+ Likewise, the `orders.cancelOrder` fields `cancelCommitted` and
1545
+ `alreadyCanceled` are domain effect dispositions, not transport replay markers.
1546
+ A same-key retry replays the cached initial 2xx body byte-for-byte and can
1547
+ therefore return the original `cancelCommitted: true` again. Neither field says
1548
+ whether the response was replayed or whether an external side effect has already
1549
+ run.
1550
+
1494
1551
  ### Commerce Orders (post-placement operations)
1495
1552
 
1496
1553
  Provider-paid `orders.create`, `orders.confirmPayment`, and ordinary
package/dist/client.cjs CHANGED
@@ -22,7 +22,22 @@ var client_exports = {};
22
22
  __export(client_exports, {
23
23
  Client: () => Client,
24
24
  ContentClient: () => ContentClient,
25
- createClient: () => createClient
25
+ createClient: () => createClient,
26
+ isApiError: () => isApiError,
27
+ isAuthError: () => isAuthError,
28
+ isCommerceSDKError: () => isCommerceSDKError,
29
+ isConfigError: () => isConfigError,
30
+ isConflictError: () => isConflictError,
31
+ isGoneError: () => isGoneError,
32
+ isNetworkError: () => isNetworkError,
33
+ isNotFoundError: () => isNotFoundError,
34
+ isPermissionError: () => isPermissionError,
35
+ isRateLimitError: () => isRateLimitError,
36
+ isSDKError: () => isSDKError,
37
+ isServiceUnavailableError: () => isServiceUnavailableError,
38
+ isTimeoutError: () => isTimeoutError,
39
+ isUsageLimitError: () => isUsageLimitError,
40
+ isValidationError: () => isValidationError
26
41
  });
27
42
  module.exports = __toCommonJS(client_exports);
28
43
 
@@ -30,6 +45,24 @@ module.exports = __toCommonJS(client_exports);
30
45
  var import_qs_esm = require("qs-esm");
31
46
 
32
47
  // src/core/errors.ts
48
+ var SDK_ERROR_BRAND = /* @__PURE__ */ Symbol.for("@01.software/sdk/SDKError");
49
+ var SDK_ERROR_TYPE_BRAND = /* @__PURE__ */ Symbol.for("@01.software/sdk/SDKError.type");
50
+ function defineSDKErrorBrand(error) {
51
+ Object.defineProperty(error, SDK_ERROR_BRAND, {
52
+ value: true,
53
+ enumerable: false,
54
+ configurable: false,
55
+ writable: false
56
+ });
57
+ }
58
+ function defineSDKErrorType(error, type) {
59
+ Object.defineProperty(error, SDK_ERROR_TYPE_BRAND, {
60
+ value: type,
61
+ enumerable: false,
62
+ configurable: false,
63
+ writable: false
64
+ });
65
+ }
33
66
  var SDKError = class extends Error {
34
67
  constructor(code, message, status, details, userMessage, suggestion, requestId) {
35
68
  super(message);
@@ -40,6 +73,7 @@ var SDKError = class extends Error {
40
73
  this.userMessage = userMessage;
41
74
  this.suggestion = suggestion;
42
75
  this.requestId = requestId;
76
+ defineSDKErrorBrand(this);
43
77
  if (Error.captureStackTrace) {
44
78
  Error.captureStackTrace(this, new.target);
45
79
  }
@@ -60,6 +94,7 @@ var SDKError = class extends Error {
60
94
  ...this.kind !== void 0 && { kind: this.kind },
61
95
  ...this.reason !== void 0 && { reason: this.reason },
62
96
  ...this.rawReason !== void 0 && { rawReason: this.rawReason },
97
+ ...this.apiReason !== void 0 && { apiReason: this.apiReason },
63
98
  ...this.retryable !== void 0 && { retryable: this.retryable },
64
99
  ...this.retryAfter !== void 0 && { retryAfter: this.retryAfter }
65
100
  };
@@ -233,8 +268,16 @@ function enhanceCommerceError(error, body, headerRequestId) {
233
268
  });
234
269
  return error;
235
270
  }
271
+ function enhanceNonCommerceError(error, reason) {
272
+ if (typeof reason === "string") Object.assign(error, { apiReason: reason });
273
+ return error;
274
+ }
275
+ function getSDKErrorReason(error) {
276
+ if (error.kind !== "commerce") return error.apiReason;
277
+ return error.reason === "unknown_commerce_error" ? error.rawReason : error.reason;
278
+ }
236
279
  function isCommerceSDKError(error) {
237
- if (!(error instanceof SDKError) || error.kind !== "commerce") return false;
280
+ if (!isSDKError(error) || error.kind !== "commerce") return false;
238
281
  if (typeof error.requestId !== "string" || typeof error.retryable !== "boolean" || error.retryAfter !== void 0 && (!Number.isSafeInteger(error.retryAfter) || error.retryAfter < 0) || typeof error.reason !== "string") {
239
282
  return false;
240
283
  }
@@ -244,12 +287,14 @@ var NetworkError = class extends SDKError {
244
287
  constructor(message, status, details, userMessage, suggestion) {
245
288
  super("NETWORK_ERROR", message, status, details, userMessage, suggestion);
246
289
  this.name = "NetworkError";
290
+ defineSDKErrorType(this, "NetworkError");
247
291
  }
248
292
  };
249
293
  var ValidationError = class extends SDKError {
250
294
  constructor(message, details, userMessage, suggestion, status = 400) {
251
295
  super("VALIDATION_ERROR", message, status, details, userMessage, suggestion);
252
296
  this.name = "ValidationError";
297
+ defineSDKErrorType(this, "ValidationError");
253
298
  }
254
299
  };
255
300
  var ApiError = class extends SDKError {
@@ -264,24 +309,28 @@ var ApiError = class extends SDKError {
264
309
  requestId
265
310
  );
266
311
  this.name = "ApiError";
312
+ defineSDKErrorType(this, "ApiError");
267
313
  }
268
314
  };
269
315
  var ConfigError = class extends SDKError {
270
316
  constructor(message, details, userMessage, suggestion) {
271
317
  super("CONFIG_ERROR", message, void 0, details, userMessage, suggestion);
272
318
  this.name = "ConfigError";
319
+ defineSDKErrorType(this, "ConfigError");
273
320
  }
274
321
  };
275
322
  var TimeoutError = class extends SDKError {
276
323
  constructor(message = "Request timed out.", details, userMessage, suggestion) {
277
324
  super("TIMEOUT_ERROR", message, 408, details, userMessage, suggestion);
278
325
  this.name = "TimeoutError";
326
+ defineSDKErrorType(this, "TimeoutError");
279
327
  }
280
328
  };
281
329
  var GoneError = class extends SDKError {
282
330
  constructor(message = "The requested resource is no longer available.", details, userMessage, suggestion) {
283
331
  super("GONE_ERROR", message, 410, details, userMessage, suggestion);
284
332
  this.name = "GoneError";
333
+ defineSDKErrorType(this, "GoneError");
285
334
  }
286
335
  };
287
336
  var ServiceUnavailableError = class extends SDKError {
@@ -296,6 +345,7 @@ var ServiceUnavailableError = class extends SDKError {
296
345
  );
297
346
  this.name = "ServiceUnavailableError";
298
347
  this.retryAfter = retryAfter;
348
+ defineSDKErrorType(this, "ServiceUnavailableError");
299
349
  }
300
350
  };
301
351
  var UsageLimitError = class extends SDKError {
@@ -303,6 +353,7 @@ var UsageLimitError = class extends SDKError {
303
353
  super("USAGE_LIMIT_ERROR", message, 429, details, userMessage, suggestion);
304
354
  this.name = "UsageLimitError";
305
355
  this.usage = usage;
356
+ defineSDKErrorType(this, "UsageLimitError");
306
357
  }
307
358
  toJSON() {
308
359
  return {
@@ -323,6 +374,7 @@ var AuthError = class extends SDKError {
323
374
  requestId
324
375
  );
325
376
  this.name = "AuthError";
377
+ defineSDKErrorType(this, "AuthError");
326
378
  }
327
379
  };
328
380
  var PermissionError = class extends SDKError {
@@ -337,6 +389,7 @@ var PermissionError = class extends SDKError {
337
389
  requestId
338
390
  );
339
391
  this.name = "PermissionError";
392
+ defineSDKErrorType(this, "PermissionError");
340
393
  }
341
394
  };
342
395
  var NotFoundError = class extends SDKError {
@@ -351,12 +404,14 @@ var NotFoundError = class extends SDKError {
351
404
  requestId
352
405
  );
353
406
  this.name = "NotFoundError";
407
+ defineSDKErrorType(this, "NotFoundError");
354
408
  }
355
409
  };
356
410
  var ConflictError = class extends SDKError {
357
411
  constructor(message, details, userMessage, suggestion, requestId) {
358
412
  super("conflict", message, 409, details, userMessage, suggestion, requestId);
359
413
  this.name = "ConflictError";
414
+ defineSDKErrorType(this, "ConflictError");
360
415
  }
361
416
  };
362
417
  var RateLimitError = class extends SDKError {
@@ -372,8 +427,64 @@ var RateLimitError = class extends SDKError {
372
427
  );
373
428
  this.name = "RateLimitError";
374
429
  this.retryAfter = retryAfter;
430
+ defineSDKErrorType(this, "RateLimitError");
375
431
  }
376
432
  };
433
+ function hasBrandedSDKErrorShape(error) {
434
+ if (error === null || typeof error !== "object") return false;
435
+ const candidate = error;
436
+ return candidate[SDK_ERROR_BRAND] === true && typeof candidate.name === "string" && typeof candidate.code === "string" && typeof candidate.message === "string" && (candidate.status === void 0 || typeof candidate.status === "number") && (candidate.userMessage === void 0 || typeof candidate.userMessage === "string") && (candidate.suggestion === void 0 || typeof candidate.suggestion === "string") && (candidate.requestId === void 0 || typeof candidate.requestId === "string") && (candidate.kind === void 0 || candidate.kind === "commerce") && (candidate.reason === void 0 || typeof candidate.reason === "string") && (candidate.rawReason === void 0 || typeof candidate.rawReason === "string") && (candidate.apiReason === void 0 || typeof candidate.apiReason === "string") && (candidate.retryable === void 0 || typeof candidate.retryable === "boolean") && (candidate.retryAfter === void 0 || typeof candidate.retryAfter === "number") && typeof candidate.getUserMessage === "function" && typeof candidate.toJSON === "function";
437
+ }
438
+ function hasSDKErrorType(error, type) {
439
+ return hasBrandedSDKErrorShape(error) && error[SDK_ERROR_TYPE_BRAND] === type;
440
+ }
441
+ function isSDKError(error) {
442
+ return error instanceof SDKError || hasBrandedSDKErrorShape(error);
443
+ }
444
+ function isNetworkError(error) {
445
+ return error instanceof NetworkError || hasSDKErrorType(error, "NetworkError");
446
+ }
447
+ function isValidationError(error) {
448
+ if (error instanceof ValidationError || hasSDKErrorType(error, "ValidationError")) {
449
+ return true;
450
+ }
451
+ if (error == null || typeof error !== "object") return false;
452
+ const candidate = error;
453
+ return candidate.name === "ValidationError" && candidate.code === "VALIDATION_ERROR" && typeof candidate.message === "string" && typeof candidate.getUserMessage === "function" && typeof candidate.toJSON === "function";
454
+ }
455
+ function isApiError(error) {
456
+ return error instanceof ApiError || hasSDKErrorType(error, "ApiError");
457
+ }
458
+ function isConfigError(error) {
459
+ return error instanceof ConfigError || hasSDKErrorType(error, "ConfigError");
460
+ }
461
+ function isTimeoutError(error) {
462
+ return error instanceof TimeoutError || hasSDKErrorType(error, "TimeoutError");
463
+ }
464
+ function isGoneError(error) {
465
+ return error instanceof GoneError || hasSDKErrorType(error, "GoneError");
466
+ }
467
+ function isServiceUnavailableError(error) {
468
+ return error instanceof ServiceUnavailableError || hasSDKErrorType(error, "ServiceUnavailableError");
469
+ }
470
+ function isUsageLimitError(error) {
471
+ return error instanceof UsageLimitError || hasSDKErrorType(error, "UsageLimitError");
472
+ }
473
+ function isAuthError(error) {
474
+ return error instanceof AuthError || hasSDKErrorType(error, "AuthError");
475
+ }
476
+ function isPermissionError(error) {
477
+ return error instanceof PermissionError || hasSDKErrorType(error, "PermissionError");
478
+ }
479
+ function isNotFoundError(error) {
480
+ return error instanceof NotFoundError || hasSDKErrorType(error, "NotFoundError");
481
+ }
482
+ function isConflictError(error) {
483
+ return error instanceof ConflictError || hasSDKErrorType(error, "ConflictError");
484
+ }
485
+ function isRateLimitError(error) {
486
+ return error instanceof RateLimitError || hasSDKErrorType(error, "RateLimitError");
487
+ }
377
488
  var createNetworkError = (message, status, details, userMessage, suggestion) => new NetworkError(message, status, details, userMessage, suggestion);
378
489
  var createValidationError = (message, details, userMessage, suggestion, status) => new ValidationError(message, details, userMessage, suggestion, status);
379
490
  var createApiError = (message, status, details, userMessage, suggestion, requestId) => new ApiError(message, status, details, userMessage, suggestion, requestId);
@@ -583,7 +694,7 @@ function mapResponseToError(status, parsed, details, requestId, retryAfter, erro
583
694
  if (retryAfter !== void 0) {
584
695
  Object.assign(withRequestId, { retryAfter });
585
696
  }
586
- return errorFamily === "commerce" ? enhanceCommerceError(withRequestId, parsed.body, requestId) : withRequestId;
697
+ return errorFamily === "commerce" ? enhanceCommerceError(withRequestId, parsed.body, requestId) : enhanceNonCommerceError(withRequestId, parsed.reason);
587
698
  };
588
699
  if (status === 400 || status === 422) {
589
700
  return finalize(
@@ -770,7 +881,7 @@ async function httpFetch(url, options) {
770
881
  ),
771
882
  requestId
772
883
  );
773
- throw errorFamily === "commerce" ? enhanceCommerceError(usageError, parsed.body, requestId) : usageError;
884
+ throw errorFamily === "commerce" ? enhanceCommerceError(usageError, parsed.body, requestId) : enhanceNonCommerceError(usageError, parsed.reason);
774
885
  }
775
886
  if (response.status === 401 && onUnauthorized && customerToken && !hasRetried401 && (commerceEnvelope?.reason ?? parsed.reason) === "token_expired") {
776
887
  hasRetried401 = true;
@@ -1217,7 +1328,7 @@ async function parseApiResponse(response, endpoint2, options = {}) {
1217
1328
  }
1218
1329
  const withRequestId = attachRequestId(error, requestId);
1219
1330
  if (retryAfter !== void 0) Object.assign(withRequestId, { retryAfter });
1220
- throw options.errorFamily === "commerce" ? enhanceCommerceError(withRequestId, data, requestId) : withRequestId;
1331
+ throw options.errorFamily === "commerce" ? enhanceCommerceError(withRequestId, data, requestId) : enhanceNonCommerceError(withRequestId, reason);
1221
1332
  }
1222
1333
  return data;
1223
1334
  }
@@ -1641,13 +1752,7 @@ function unwrapPayloadDoc(value) {
1641
1752
  return value;
1642
1753
  }
1643
1754
  function hasCanonicalNotFoundReason(error) {
1644
- const details = error.details;
1645
- if (typeof details !== "object" || details === null) return false;
1646
- if ("reason" in details && details.reason === "not_found") return true;
1647
- if (!("body" in details) || typeof details.body !== "object" || details.body === null) {
1648
- return false;
1649
- }
1650
- return "reason" in details.body && details.body.reason === "not_found";
1755
+ return getSDKErrorReason(error) === "not_found";
1651
1756
  }
1652
1757
  var CommunityClient = class extends RawCustomerScopedApi {
1653
1758
  constructor(options) {
@@ -3253,9 +3358,77 @@ function validateRequiredReconciliation(value, allowed, resource, path) {
3253
3358
  function validateNextAction(value, provider, resource, path) {
3254
3359
  const action = record(value, resource, path);
3255
3360
  if (provider === "portone") {
3361
+ if (action.type === "portone_sdk") {
3362
+ exact(
3363
+ action,
3364
+ [
3365
+ "type",
3366
+ "version",
3367
+ "storeId",
3368
+ "channelKey",
3369
+ "paymentId",
3370
+ "orderName",
3371
+ "totalAmount",
3372
+ "currency",
3373
+ "payMethod",
3374
+ "redirectUrl"
3375
+ ],
3376
+ [],
3377
+ resource,
3378
+ path
3379
+ );
3380
+ if (action.version !== "v2") fail(resource, `${path}.version`, "v2");
3381
+ for (const key of ["storeId", "channelKey"]) {
3382
+ boundedString(action[key], 255, resource, `${path}.${key}`);
3383
+ }
3384
+ const paymentId = boundedString(
3385
+ action.paymentId,
3386
+ 40,
3387
+ resource,
3388
+ `${path}.paymentId`
3389
+ );
3390
+ if (!/^[A-Za-z0-9]{6,40}$/.test(paymentId)) {
3391
+ fail(
3392
+ resource,
3393
+ `${path}.paymentId`,
3394
+ "6-40 ASCII alphanumeric characters"
3395
+ );
3396
+ }
3397
+ boundedString(
3398
+ action.orderName,
3399
+ MAX_COMMERCE_SHORT_TEXT_LENGTH,
3400
+ resource,
3401
+ `${path}.orderName`
3402
+ );
3403
+ positiveInteger(action.totalAmount, resource, `${path}.totalAmount`);
3404
+ if (action.currency !== "CURRENCY_KRW") {
3405
+ fail(resource, `${path}.currency`, "CURRENCY_KRW");
3406
+ }
3407
+ if (action.payMethod !== "CARD")
3408
+ fail(resource, `${path}.payMethod`, "CARD");
3409
+ const redirectUrl = boundedString(
3410
+ action.redirectUrl,
3411
+ 2048,
3412
+ resource,
3413
+ `${path}.redirectUrl`
3414
+ );
3415
+ try {
3416
+ const parsed = new URL(redirectUrl);
3417
+ const loopback = parsed.protocol === "http:" && (parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1");
3418
+ if (!loopback && parsed.protocol !== "https:" || parsed.username || parsed.password)
3419
+ throw new Error("unsafe redirect URL");
3420
+ } catch {
3421
+ fail(
3422
+ resource,
3423
+ `${path}.redirectUrl`,
3424
+ "HTTPS or loopback URL without embedded credentials"
3425
+ );
3426
+ }
3427
+ return;
3428
+ }
3256
3429
  exact(action, ["type", "checkoutUrl"], [], resource, path);
3257
3430
  if (action.type !== "portone_checkout") {
3258
- fail(resource, `${path}.type`, "portone_checkout");
3431
+ fail(resource, `${path}.type`, "portone_checkout | portone_sdk");
3259
3432
  }
3260
3433
  const checkoutUrl = boundedString(
3261
3434
  action.checkoutUrl,
@@ -5218,21 +5391,11 @@ var PRODUCT_DETAIL_UNAVAILABLE_REASONS = /* @__PURE__ */ new Set([
5218
5391
  "not_published",
5219
5392
  "feature_disabled"
5220
5393
  ]);
5221
- function isRecord(value) {
5222
- return typeof value === "object" && value !== null;
5223
- }
5224
- function readProductDetailUnavailableReason(value) {
5225
- if (!isRecord(value)) return void 0;
5226
- const directReason = value.reason ?? value.code;
5227
- if (typeof directReason === "string" && PRODUCT_DETAIL_UNAVAILABLE_REASONS.has(directReason)) {
5228
- return directReason;
5229
- }
5230
- return readProductDetailUnavailableReason(value.body);
5231
- }
5232
5394
  function productDetailResultFromError(error) {
5233
5395
  if (!(error instanceof SDKError) || error.status !== 404) return void 0;
5234
- const reason = readProductDetailUnavailableReason(error.details);
5235
- if (!reason) return void 0;
5396
+ const reason = getSDKErrorReason(error);
5397
+ if (!reason || !PRODUCT_DETAIL_UNAVAILABLE_REASONS.has(reason))
5398
+ return void 0;
5236
5399
  return { found: false, reason };
5237
5400
  }
5238
5401
  var MARKET_PRICE_QUERY_UNSUPPORTED_MESSAGE = "Market listing pages do not support base price filters or price sorts.";