@zalkera/client 0.19.0 → 0.20.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
@@ -174,6 +174,7 @@ await zalkera.submitInquiry({name, email, subject, message}, {clientIp});
174
174
 
175
175
  - 대상: `submitInquiry`·`submitLead`·`recordPostView`.
176
176
  - 이유: 백엔드 IP 레이트리밋·조회 dedup이 이 값을 본다. 안 넘기면 테넌트 서버 IP로 뭉쳐 방문자 전원이 429.
177
+ - 주문 계열(`getOrder`/`cancelOrder`/`completeOrder`/`getShipment`/`startPayment`/`confirmPayment`)도 같다 — `OrderAccess.context`로 넘긴다. 게스트 주문 인가는 연락처 대입을 막으려고 **실패를 세는데**, 선언이 없으면 그 사이트의 게스트 전체가 한 IP로 묶인다(주문번호 축은 그와 무관하게 계속 선다).
177
178
 
178
179
  ```ts
179
180
  // route handler 안
@@ -210,7 +211,7 @@ await zalkera.submitInquiry(input, {clientIp: ip});
210
211
  | `code` | 기계 판독 에러코드(백엔드 `ErrorCode`). 구버전은 HTTP 사유구로 폴백. |
211
212
  | `validationErrors` | 400 시 필드별 메시지. |
212
213
  | `body` | 파싱된 원본 에러 본문. |
213
- | `isRateLimited` | `status === 429`. |
214
+ | `isRateLimited` | `status === 429`. 문의·리드 남발 또는 **게스트 주문 인가 실패 누적**. |
214
215
  | `isStorefrontKeyError` | `STOREFRONT_KEY_REQUIRED` 또는 `TENANT_MISMATCH`. |
215
216
 
216
217
  ```ts
@@ -63,8 +63,11 @@
63
63
  * inferred 선언 부재 + content/pages/*.json 이 실재 W (형상은 있는데 선언이 없다)
64
64
  * none 그 외(다른 선언값 · content 디렉터리 없음) – (스킵)
65
65
  *
66
- * N0 **은퇴한 선언값** `zalkera.content === "sections-db"` — 모드는 `none`(N1~N5 스킵)이지만 경고 한 줄을
67
- * 낸다. 그 선언을 든 레포는 얼굴의 정본이 어디에도 없다(어휘 rev 7 · memo144).
66
+ * N0 **`zalkera.content` 모르는 값** — 모드는 `none`(N1~N5 스킵)이지만 경고 한 줄을 낸다. 둘을 가른다:
67
+ * 은퇴한 값 `"sections-db"` — 그 선언을 든 레포는 얼굴의 정본이 어디에도 없다(어휘 rev 7 · memo144).
68
+ * ⑵ 그 밖의 미지 값(오탈자 `"sourse"` 류) — **스킵한다는 사실 자체를 말한다.** 유효값이 하나로 줄어
69
+ * 한 글자 오타가 N 규칙 전체를 조용히 끄는 스위치가 됐다(memo144 §심의-4 · 경고 ⑦).
70
+ * 어느 쪽도 error 가 아니다 — 남의 선언값을 우리가 해석해 막으면 그것이 어휘 강제다(memo125 요건 1).
68
71
  * N1 content/index.ts(매니페스트) 부재 — 정적 import 가 없으면 HMR 도 standalone 트레이싱도 없다.
69
72
  * N2 content/pages/*.json 파싱 실패 또는 최상위가 객체 아님.
70
73
  * N3 매니페스트와 파일의 어긋남 — 파일은 있는데 매니페스트에 없으면 **그 페이지는 존재하지 않는다**
@@ -495,6 +498,16 @@ const STYLE_MODE = detectStyleMode(root);
495
498
  * **은퇴한 선언값**(`"sections-db"`)은 3모드를 늘리지 않는다 — 판정은 그대로 `none` 이고 경고 한 줄을
496
499
  * 더한다(어휘 rev 7 · memo144). 섹션이 백엔드 DB 에도 살던 시절의 값인데 그 거처가 통째로 퇴역해서,
497
500
  * 그 선언을 든 레포는 **얼굴의 정본이 어디에도 없는 상태**다. 조용히 스킵하면 그 사실이 안 보인다.
501
+ *
502
+ * **미지 값**(`"sourse"` 같은 오탈자)도 같은 대접이다 — `none` 이되 **말은 한다**(memo144 §심의-4 부수 ·
503
+ * 경고 ⑦). 종전에는 이 자리가 침묵이었다: 유효값이 `"source"` 하나로 줄면서 오탈자 한 글자가 N1~N5
504
+ * 전체를 조용히 끄는 스위치가 됐고, `content/pages/*.json` 이 **실재해도** 선언이 없는 레포(`inferred`)
505
+ * 보다 낮은 감시를 받았다. 계약을 지키려던 손이 오타 때문에 계약 밖으로 떨어지는데 화면에 아무 말이
506
+ * 없는 것 — 그것이 조용한 실패다.
507
+ *
508
+ * ⚠ **경고까지다. error 도, 모드 승격도 아니다.** 우리 계약이 아닌 값을 적는 것은 자유이고(memo125
509
+ * 요건 1 — 어휘 강제 금지), 남의 `zalkera.content` 를 우리가 해석해 검사를 걸면 그것이 강제가 된다.
510
+ * 여기서 하는 일은 판정을 바꾸는 것이 아니라 **판정 결과를 보이게 하는 것**뿐이다.
498
511
  */
499
512
  function detectContentMode(srcDir) {
500
513
  const repoRoot = resolve(srcDir, "..");
@@ -525,7 +538,23 @@ function detectContentMode(srcDir) {
525
538
  );
526
539
  return "none";
527
540
  }
528
- if (declared !== undefined) return "none"; // 우리 계약이 아닌 선언값 — 이 레포에 콘텐츠 파일 계약이 없다
541
+ if (declared !== undefined) {
542
+ // 우리 계약이 아닌 선언값 — 이 레포에 콘텐츠 파일 계약이 없다. **다만 말은 한다**(위 KDoc).
543
+ // 콘텐츠 파일이 실재하면 오탈자일 가능성이 높으므로 그 사실을 함께 적는다 — 사람이 오타인지
544
+ // 의도인지 가를 재료다(우리가 대신 판정하지 않는다).
545
+ const shown = typeof declared === "string" ? `"${declared}"` : JSON.stringify(declared);
546
+ const pages = contentPageFiles(repoRoot).length;
547
+ warnings.push(
548
+ `[N0] package.json 의 \`zalkera.content\` 값 ${shown} 을 모릅니다 — 아는 값은 \`"source"\` 하나이고, ` +
549
+ `그래서 콘텐츠 파일 계약 검사(N1~N5)를 **건너뜁니다**` +
550
+ (pages > 0
551
+ ? `. 그런데 이 레포에는 \`content/pages/*.json\` 이 ${pages}개 있습니다 — 오탈자라면 ` +
552
+ `\`"source"\` 로 고치세요(고치면 N 규칙이 error 로 섭니다). 일부러 다른 값을 쓰신 것이면 ` +
553
+ `이 경고는 무시하십시오.`
554
+ : `. 일부러 다른 값을 쓰신 것이면 이 경고는 무시하십시오.`),
555
+ );
556
+ return "none";
557
+ }
529
558
  return contentPageFiles(repoRoot).length > 0 ? "inferred" : "none";
530
559
  }
531
560
 
package/dist/index.cjs CHANGED
@@ -25,7 +25,13 @@ var ZalkeraError = class _ZalkeraError extends Error {
25
25
  this.validationErrors = options.validationErrors ?? [];
26
26
  this.body = options.body ?? null;
27
27
  }
28
- /** IP 레이트리밋(429) — 문의 폼에서 "잠시 후 다시" 를 띄울 때 쓴다. */
28
+ /**
29
+ * 레이트리밋(429) — "잠시 후 다시" 를 띄울 때 쓴다.
30
+ *
31
+ * 두 갈래다: ⑴ 문의·리드 폼 남발(IP 축) ⑵ **게스트 주문 인가 실패 누적** — 주문번호+연락처로
32
+ * 여는 주문 조회·취소·구매확정·배송조회·결제세션은 연락처 대입을 막으려고 실패를 센다.
33
+ * 정상 조회는 세지 않으므로, 이 코드가 뜨면 연락처를 여러 번 틀렸거나 같은 주문에 시도가 몰린 것이다.
34
+ */
29
35
  get isRateLimited() {
30
36
  return this.status === 429;
31
37
  }
@@ -288,7 +294,8 @@ function accessInit(access, extra) {
288
294
  return {
289
295
  ...extra,
290
296
  bearer: access.accessToken,
291
- query: access.phone ? { phone: access.phone } : void 0
297
+ query: access.phone ? { phone: access.phone } : void 0,
298
+ context: access.context
292
299
  };
293
300
  }
294
301
  function isRedirectResponse(response) {