@zalkera/client 0.24.2 → 0.24.4

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.
@@ -188,7 +188,8 @@ function isInternalHost(host) {
188
188
  * 이미 적어 두었다.
189
189
  */
190
190
  function embeddedIPv4(host) {
191
- // `::` 는 **접두가 없는** 형태라 마지막에 둔다 앞에 두면 `::ffff:` 그것으로 먼저 삼킨다.
191
+ // `::` 는 **접두가 없는** 형태라 마지막에 둔다. (JS 정규식 교대는 백트래킹하므로 앞에 둬도
192
+ // 결과는 같다 — 실측으로 7개 프로브 전부 동일. 읽는 사람이 「좁은 것부터」로 읽게 두는 배치다.)
192
193
  const PREFIX = "(?:::ffff:|64:ff9b::|::)";
193
194
  const dotted = new RegExp(`^${PREFIX}(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})$`, "i").exec(host);
194
195
  if (dotted) return dotted[1];
@@ -1926,17 +1926,29 @@ function echoValue(value) {
1926
1926
  * 위치(`at position N` · `line L column C`)는 남긴다 — 고치는 사람이 어디를 볼지 알아야 한다.
1927
1927
  * 그 숫자는 내용이 아니다.
1928
1928
  */
1929
+ /**
1930
+ * JSON 의 **구조 문자**. 이것만 반려문에 실어도 내용이 새지 않는다 — 값에는 못 나오는 글자들이다
1931
+ * (따옴표 안이면 파싱이 안 깨지고, 깨졌다는 것은 그 자리가 구조라는 뜻이다).
1932
+ */
1933
+ const STRUCTURAL_JSON = new Set([",", ":", "{", "}", "[", "]", '"', "'", " ", "\t", "\n", "\r"]);
1934
+
1929
1935
  function parseWhere(error) {
1930
1936
  const raw = String(error?.message ?? "");
1931
1937
  const where = /(?:at position \d+(?:\s*\(line \d+ column \d+\))?|line \d+ column \d+)/.exec(raw);
1932
- // ⚠ **어느 글자에서 걸렸는지만** 남긴다. Node 글자를 따옴표로 싸서 주는데, 한 글자는
1933
- // 구조 정보(쉼표·중괄호)이지 내용이 아니다.**뒤**에 붙는 `..."조각"` 위험한 부분이다.
1934
- const token = /^Unexpected token ('.'|".")/.exec(raw);
1935
- const head = token
1936
- ? `예상하지 못한 글자 ${token[1]}`
1937
- : /Unexpected end of JSON input/.test(raw)
1938
- ? "내용이 도중에 끝났습니다"
1939
- : "형식이 올바르지 않습니다";
1938
+ // ⚠ **걸린 글자는 구조 문자일 때만 싣는다.** 「그 글자는 구조 정보이지 내용이 아니다」는
1939
+ // **거짓이다** 값이 따옴표 없이 적혀 있으면 글자는 **값의 글자**다(실측: 시크릿을
1940
+ // 비인용으로 두면 글자가 반려문에 실린다). 회당 한 글자라도 여기서 내보내지 않는다.
1941
+ // 구조 문자면 그대로 두는 이유: 「쉼표가 하나 더 있습니다」는 고치는 사람에게 실제로 쓸모가 있다.
1942
+ const token = /^Unexpected token ('(.)'|"(.)")/.exec(raw);
1943
+ const glyph = token ? (token[2] ?? token[3]) : null;
1944
+ const head =
1945
+ glyph !== null && STRUCTURAL_JSON.has(glyph)
1946
+ ? `예상하지 못한 글자 '${glyph}'`
1947
+ : glyph !== null
1948
+ ? "값이 따옴표 없이 적혀 있거나 표기가 잘못됐습니다"
1949
+ : /Unexpected end of JSON input/.test(raw)
1950
+ ? "내용이 도중에 끝났습니다"
1951
+ : "형식이 올바르지 않습니다";
1940
1952
  return where ? `${head} (${where[0]})` : head;
1941
1953
  }
1942
1954
 
@@ -2957,15 +2969,52 @@ function checkSectionCoverage() {
2957
2969
  const src = readSafe(rendererPath, "섹션 렌더러 계약 검사");
2958
2970
  if (src === null) return;
2959
2971
  const cases = new Set([...src.matchAll(/case\s+"([A-Z_]+)"/g)].map((m) => m[1]));
2960
- const missing = contract.map((c) => c.type).filter((t) => !cases.has(t));
2972
+
2973
+ // ⚠ **계약 전량이 아니라 「이 사이트가 실제로 쓰는 타입」을 잰다.**
2974
+ //
2975
+ // 전량을 재면 **계약에 타입이 하나 추가되는 순간** 그것을 안 그리는 기존 사이트가 전부
2976
+ // 관문에서 막힌다(`servingSink` 는 관문 모드에서 error 다). 계약이 약속한 「타입 추가는
2977
+ // 구 스토어프론트를 깨지 않는다」와 정면으로 어긋나고, 실제로 막히는 시점은 그 사이트가
2978
+ // 새 client 를 받는 때라 원인을 되짚기도 어렵다.
2979
+ //
2980
+ // 여기서 잡아야 하는 사고는 「콘솔에서 넣었는데 화면에 안 나온다」이고, 그것은 **콘텐츠가
2981
+ // 그 타입을 쓸 때만** 성립한다. 어휘 전량 커버는 팩 정본이 자기 시험으로 지킬 일이다.
2982
+ const known = new Set(contract.map((c) => c.type));
2983
+ const used = usedSectionTypes(repoRootDir).filter((t) => known.has(t));
2984
+ const missing = [...new Set(used)].filter((t) => !cases.has(t));
2961
2985
  if (missing.length > 0) {
2962
2986
  servingSink().push(
2963
- `[C2] SectionRenderer 가 계약의 ${missing.join("·")} 를 안 그린다 — 미지 타입은 조용히 스킵되므로 ` +
2964
- `콘솔에서 넣어도 화면에 안 나온다. case 를 추가하거나, 의도적 미지원이면 그 사유를 커밋에 남겨라.`,
2987
+ `[C2] SectionRenderer 가 콘텐츠가 쓰는 ${missing.join("·")} 를 안 그린다 — 미지 타입은 조용히 ` +
2988
+ `스킵되므로 콘솔에서 넣어도 화면에 안 나온다. case 를 추가하거나, 의도적 미지원이면 그 사유를 커밋에 남겨라.`,
2965
2989
  );
2966
2990
  }
2967
2991
  }
2968
2992
 
2993
+ /**
2994
+ * `content/pages/*.json` 이 실제로 쓰는 섹션 타입.
2995
+ *
2996
+ * 못 읽거나 깨진 파일은 **여기서 말하지 않는다** — N2·N4 가 그 일을 한다. 이 함수는 「무엇을
2997
+ * 쓰는가」만 답하고, 모르면 조용히 뺀다(모르는 것을 「쓴다」고 세면 없는 결함을 만든다).
2998
+ */
2999
+ function usedSectionTypes(repoRoot) {
3000
+ const out = [];
3001
+ for (const file of contentPageFiles(repoRoot)) {
3002
+ let page;
3003
+ try {
3004
+ page = JSON.parse(readFileSync(file, "utf8"));
3005
+ } catch {
3006
+ continue;
3007
+ }
3008
+ const sections = page?.sections;
3009
+ if (!Array.isArray(sections)) continue;
3010
+ for (const section of sections) {
3011
+ const type = section?.type;
3012
+ if (typeof type === "string" && type.trim() !== "") out.push(type.trim());
3013
+ }
3014
+ }
3015
+ return out;
3016
+ }
3017
+
2969
3018
  /*
2970
3019
  * ── N1~N5 : 콘텐츠 파일 계약 ────────────────────────────────────────────────
2971
3020
  *
package/package.json CHANGED
@@ -1,75 +1,75 @@
1
1
  {
2
- "name": "@zalkera/client",
3
- "version": "0.24.2",
4
- "description": "zalkera 헤드리스 CMS 공개 API 클라이언트 (테넌트 사이트용)",
5
- "license": "MIT",
6
- "author": "Credium Co., Ltd.",
7
- "type": "module",
8
- "publishConfig": {
9
- "access": "public"
2
+ "name": "@zalkera/client",
3
+ "version": "0.24.4",
4
+ "description": "zalkera 헤드리스 CMS 공개 API 클라이언트 (테넌트 사이트용)",
5
+ "license": "MIT",
6
+ "author": "Credium Co., Ltd.",
7
+ "type": "module",
8
+ "publishConfig": {
9
+ "access": "public"
10
+ },
11
+ "keywords": [
12
+ "zalkera",
13
+ "headless",
14
+ "commerce",
15
+ "cms",
16
+ "api-client",
17
+ "sdk"
18
+ ],
19
+ "homepage": "https://zalkera.com",
20
+ "files": [
21
+ "dist",
22
+ "llms.txt",
23
+ "contracts",
24
+ "bin/validate-storefront.mjs",
25
+ "bin/check-aeo-surfaces.mjs",
26
+ "lib",
27
+ "!dist/*.map"
28
+ ],
29
+ "main": "./dist/index.cjs",
30
+ "module": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "bin": {
33
+ "zalkera-aeo-check": "./bin/check-aeo-surfaces.mjs",
34
+ "zalkera-validate": "./bin/validate-storefront.mjs"
35
+ },
36
+ "exports": {
37
+ ".": {
38
+ "import": {
39
+ "types": "./dist/index.d.ts",
40
+ "default": "./dist/index.js"
41
+ },
42
+ "require": {
43
+ "types": "./dist/index.d.cts",
44
+ "default": "./dist/index.cjs"
45
+ }
10
46
  },
11
- "keywords": [
12
- "zalkera",
13
- "headless",
14
- "commerce",
15
- "cms",
16
- "api-client",
17
- "sdk"
18
- ],
19
- "homepage": "https://zalkera.com",
20
- "files": [
21
- "dist",
22
- "llms.txt",
23
- "contracts",
24
- "bin/validate-storefront.mjs",
25
- "bin/check-aeo-surfaces.mjs",
26
- "lib",
27
- "!dist/*.map"
28
- ],
29
- "main": "./dist/index.cjs",
30
- "module": "./dist/index.js",
31
- "types": "./dist/index.d.ts",
32
- "bin": {
33
- "zalkera-aeo-check": "./bin/check-aeo-surfaces.mjs",
34
- "zalkera-validate": "./bin/validate-storefront.mjs"
35
- },
36
- "exports": {
37
- ".": {
38
- "import": {
39
- "types": "./dist/index.d.ts",
40
- "default": "./dist/index.js"
41
- },
42
- "require": {
43
- "types": "./dist/index.d.cts",
44
- "default": "./dist/index.cjs"
45
- }
46
- },
47
- "./contracts/aeo-surface-guarantees.json": "./contracts/aeo-surface-guarantees.json",
48
- "./bin/check-aeo-surfaces.mjs": "./bin/check-aeo-surfaces.mjs",
49
- "./bin/validate-storefront.mjs": "./bin/validate-storefront.mjs",
50
- "./lib/site-crawl.mjs": "./lib/site-crawl.mjs"
51
- },
52
- "scripts": {
53
- "build": "tsup",
54
- "dev": "tsup --watch",
55
- "typecheck": "tsc --noEmit",
56
- "test": "vitest run",
57
- "test:watch": "vitest",
58
- "prepublishOnly": "npm run build && npm run check:tarball && npm run format:check && npm run typecheck && npm test && npm run check:published",
59
- "check:published": "node scripts/check-published-drift.mjs",
60
- "format": "prettier --write \"src/**/*.ts\" \"scripts/**/*.mjs\" \"bin/**/*.mjs\"",
61
- "format:check": "prettier --check \"src/**/*.ts\" \"scripts/**/*.mjs\" \"bin/**/*.mjs\"",
62
- "check:tarball": "node scripts/check-tarball.mjs"
63
- },
64
- "engines": {
65
- "node": ">=18"
66
- },
67
- "devDependencies": {
68
- "@types/node": "^22.10.2",
69
- "prettier": "^3.9.6",
70
- "tsup": "^8.3.5",
71
- "typescript": "^5.7.2",
72
- "vitest": "^2.1.8"
73
- },
74
- "sideEffects": false
47
+ "./contracts/aeo-surface-guarantees.json": "./contracts/aeo-surface-guarantees.json",
48
+ "./bin/check-aeo-surfaces.mjs": "./bin/check-aeo-surfaces.mjs",
49
+ "./bin/validate-storefront.mjs": "./bin/validate-storefront.mjs",
50
+ "./lib/site-crawl.mjs": "./lib/site-crawl.mjs"
51
+ },
52
+ "scripts": {
53
+ "build": "tsup",
54
+ "dev": "tsup --watch",
55
+ "typecheck": "tsc --noEmit",
56
+ "test": "vitest run",
57
+ "test:watch": "vitest",
58
+ "prepublishOnly": "npm run build && npm run check:tarball && npm run format:check && npm run typecheck && npm test && npm run check:published",
59
+ "check:published": "node scripts/check-published-drift.mjs",
60
+ "format": "prettier --write \"src/**/*.ts\" \"scripts/**/*.mjs\" \"bin/**/*.mjs\"",
61
+ "format:check": "prettier --check \"src/**/*.ts\" \"scripts/**/*.mjs\" \"bin/**/*.mjs\"",
62
+ "check:tarball": "node scripts/check-tarball.mjs"
63
+ },
64
+ "engines": {
65
+ "node": ">=18"
66
+ },
67
+ "devDependencies": {
68
+ "@types/node": "^22.10.2",
69
+ "prettier": "^3.9.6",
70
+ "tsup": "^8.3.5",
71
+ "typescript": "^5.7.2",
72
+ "vitest": "^2.1.8"
73
+ },
74
+ "sideEffects": false
75
75
  }