@zalkera/client 0.25.0 → 0.26.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.
@@ -2997,12 +2997,19 @@ function checkSectionCoverage() {
2997
2997
  * 못 읽거나 깨진 파일은 **여기서 말하지 않는다** — N2·N4 가 그 일을 한다. 이 함수는 「무엇을
2998
2998
  * 쓰는가」만 답하고, 모르면 조용히 뺀다(모르는 것을 「쓴다」고 세면 없는 결함을 만든다).
2999
2999
  */
3000
- function usedSectionTypes(repoRoot) {
3000
+ function usedSectionTypes(repoRoot, what = "콘텐츠 섹션 타입 수집") {
3001
3001
  const out = [];
3002
3002
  for (const file of contentPageFiles(repoRoot)) {
3003
+ // ⚠ **맨 `readFileSync` 로 되돌리지 마라.** 이 자리는 상한(`MAX_READ_BYTES`)도 봉쇄
3004
+ // (`containedPath`)도 안 받는 읽기였다. 종전엔 `C2` 하나만 여기 닿았고 그것은 우리
3005
+ // 렌더러 파일이 있는 트리에서만 도는데, 지금은 선언 없는 **업로드 레포**가 그대로 닿는다.
3006
+ // 실측: 59MB 콘텐츠 파일을 구판은 `EFBIG` 로 **거부**했고 이 자리는 읽고 파싱까지 했다.
3007
+ // 페이지마다 도는 순회라 총량 상한도 없다.
3008
+ const text = readSafe(file, what);
3009
+ if (text === null) continue;
3003
3010
  let page;
3004
3011
  try {
3005
- page = JSON.parse(readFileSync(file, "utf8"));
3012
+ page = JSON.parse(text);
3006
3013
  } catch {
3007
3014
  continue;
3008
3015
  }
@@ -4423,6 +4430,82 @@ checkManualCarrier(); // W-LLMS — zip 이 나르는 명세가 설치본과 같
4423
4430
  if (!singletonFound)
4424
4431
  warnings.push(`[W1] createZalkeraClient 싱글턴을 찾지 못했습니다 — 서버 사이드 호출 패턴이 있는지 확인하세요.`);
4425
4432
 
4433
+ /**
4434
+ * **선언은 없는데 우리 계약 어휘를 쓰고 있다** — 셋째 사실이다.
4435
+ *
4436
+ * 두 모드 판정은 사실을 둘로만 갈라 왔다: 「선언했다」(계약 모드)와 「선언 안 했다」(BYO — 정상 형상).
4437
+ * 그 사이에 셋째가 있다. `content/pages/*.json` 이 `SECTION_CONTRACT` 의 타입을 적고 있으면 그 레포는
4438
+ * **우리 어휘를 쓰는 중**이다 — 그 문자열은 우리가 발행한 식별자라 남이 우연히 쓰지 않는다.
4439
+ *
4440
+ * 이 자리가 왜 구멍이었나. 선언 두 줄을 지우면 S·N·D 축이 **error 에서 warning 으로 내려가 rc 가
4441
+ * 0** 이 된다(실측: 오류 5 → 0). 고쳐진 것은 없다 — 깨진 이미지는 그대로 나가고 미지 타입 섹션은
4442
+ * 그대로 화면에서 사라진다. 검사기를 고치거나 지울 필요도 없이 **가장 싼 우회**가 여기 있었다.
4443
+ *
4444
+ * 그런데 일괄 격상은 답이 아니다. 선언 부재는 BYO 의 **정상 형상**이라, 그것만으로 error 를 세우면
4445
+ * 우리 계약을 자처한 적 없는 레포를 기계가 벌한다(「출처 자유의 부정」). 그래서 **선언이 아니라
4446
+ * 코드가 하는 일**을 증거로 삼는다 — 어휘를 쓰면 우리 계약 위에 있다.
4447
+ *
4448
+ * 처분은 error(rc=1)가 아니라 **rc=7(못 잼)** 이다. 우리가 아는 것은 「어느 잣대를 댈지 모르겠다」
4449
+ * 까지이지 「위반이다」가 아니다. 이 검사기는 그 칸을 이미 갖고 있다(모르는 선언값 `EDECL`).
4450
+ * 고치는 값도 싸다 — 선언 두 줄이면 다음 실행이 진짜 판정을 낸다.
4451
+ *
4452
+ * **관문에서만 선다.** 로컬 `validate` 는 경고로 끝난다 — 남의 개발 루프를 끊지 않는다.
4453
+ *
4454
+ * ## 이 판정이 **안 잡는 것** (심의 실측 — 잡는 척하지 않는다)
4455
+ *
4456
+ * 증거가 「콘텐츠 파일이 우리 어휘를 쓴다」 하나뿐이라, 그 증거를 없애면 조용해진다.
4457
+ *
4458
+ * - **어휘를 자기 이름으로 바꾸면** 빠져나간다(rc=0). 렌더러까지 같이 고쳐야 하는 기계적 작업이고,
4459
+ * 그러고 나면 다른 error 도 안 선다 — `N4`(계약에 없는 타입)는 `inferred` 에서 경고이고 `C2` 는
4460
+ * 쓰는 타입을 계약과 교집합으로 걸러서 함께 조용해진다.
4461
+ * - **콘텐츠를 `content/pages/` 밖으로 옮기면** 빠져나간다. `CONTENT_MODE` 가 `none` 이 되어 N 축이
4462
+ * 통째로 스킵된다. `content/pages/<slug>/index.json` 도 같다 — [contentPageFiles] 는 재귀하지 않는다.
4463
+ * 이 축은 이 판정 이전부터 그랬다.
4464
+ * - **가짜 계약을 반입하면**(`"@zalkera/client": "file:./vendor/fake"` 에 비지 않은 `SECTION_CONTRACT`)
4465
+ * 빠져나간다. 비어 있으면 [warnIfContractMissing] 이 관문에서 rc=7 로 세우지만, **비지 않았는데
4466
+ * 내용이 다른** 것은 못 가른다.
4467
+ * - **배송 디렉터리 이름이 `src`** 면 [resolveRepoRoot] 가 한 칸 위를 루트로 잡아 콘텐츠가 안 보인다.
4468
+ * 서빙 관문은 `/build` 로 복사해 띄우므로 그 경로로는 닿지 않는다(`sandbox/build.sh`).
4469
+ *
4470
+ * 반대 방향의 한계도 적는다. 어휘의 절반(`HERO`·`FAQ_LIST`·`TESTIMONIALS`·`LEAD_CTA`)은 업계 통용어라
4471
+ * **우리와 무관한 레포가 우연히 겹칠 수 있다.** 그때 이 판정은 「선언하라」고만 하고 위반을 주장하지
4472
+ * 않는다 — 처분을 error 가 아니라 rc=7 로 둔 이유가 그것이다.
4473
+ *
4474
+ * 이 축들을 더 조이려면 증거를 넓혀야 하는데(예: `@zalkera/client` 의존 존재를 OR), 그러면 관문이
4475
+ * 부르는 검사기가 곧 그 의존이라 **선언이 사실상 필수**가 된다. 그것은 기계가 아니라 계약의 결정이다.
4476
+ */
4477
+ function usesContractVocabulary(repoRoot) {
4478
+ const known = sectionContractMap();
4479
+ // 계약을 못 읽으면 판정하지 않는다(BYO 레포를 막지 않는다). 그 형상은 `warnIfContractMissing`
4480
+ // 이 관문에서 따로 rc=7 로 세운다 — 여기서 두 번 외치지 않는다.
4481
+ if (known === null) return false;
4482
+ return usedSectionTypes(repoRoot, "선언 부재 판정(섹션 어휘 대조)").some((t) => known.has(t));
4483
+ }
4484
+
4485
+ if (CONTENT_MODE === "inferred" && usesContractVocabulary(repoRootDir)) {
4486
+ // ⚠ **콘텐츠축에만 건다.** 증거가 콘텐츠 어휘라 그것이 말해 주는 것은 「이 레포가 우리 콘텐츠
4487
+ // 계약 위에 있다」까지다. 스타일축까지 같이 세우면 **콘텐츠 계약은 따르되 스타일은 자기 것을
4488
+ // 쓰는** 레포가 빠져나갈 길이 없어진다 — `styling` 을 빼면 여기서 서고, `tailwind-tokens` 로
4489
+ // 적으면 S1~S4 가 error 로 서고, 다른 값을 적으면 EDECL 로 선다. 세 길이 다 막힌 자리였다.
4490
+ // 그 형상이 곧 풀소스 BYO 이고, 이 판정의 전제(「선언 부재는 BYO 의 정상 형상」)와도 어긋난다.
4491
+ const pkg = join(repoRootDir, "package.json");
4492
+ // 어느 파일을 고치라는 말인지 정확해야 한다. 워크스페이스 하위 트리에는 자기 package.json 이
4493
+ // 없을 수 있고, 그때 이 경로를 그대로 찍으면 **없는 파일을 고치라는 지시**가 된다.
4494
+ const where = fileExists(pkg)
4495
+ ? "package.json"
4496
+ : `package.json(${relative(process.cwd(), repoRootDir) || "."} 에 없습니다)`;
4497
+ // ⚠ **처방을 `what` 안에 담는다.** 관문 출력은 「이 소스를 만든 개발자에게 아래 줄을 그대로
4498
+ // 전달하십시오」로 시작하는데, 넘겨받는 그 줄에 무엇을 하라는 말이 없으면 전달이 무의미하다.
4499
+ // 처방이 더 필요한 쪽은 로컬 루프가 아니라 **업로드한 서드파티**다.
4500
+ const what =
4501
+ `콘텐츠가 SECTION_CONTRACT 의 섹션 타입을 쓰는데 ${where} 에 zalkera 선언이 없어 ` +
4502
+ `콘텐츠(N) 규칙군을 경고로만 돌렸습니다 — 어느 잣대를 댈지 정하지 못했습니다. ` +
4503
+ `\`"zalkera": {"content": "source"}\` 를 선언하십시오 — 우리 계약을 따르는 것이 아니면 ` +
4504
+ `콘텐츠를 \`content/pages/\` 밖에 두십시오`;
4505
+ if (GATE_MODE) markUnmeasured(pkg, "EDECL", what);
4506
+ else warnings.push(`[N0] ${what}`);
4507
+ }
4508
+
4426
4509
  console.log(
4427
4510
  `스타일 규약 모드: ${STYLE_MODE}` +
4428
4511
  (STYLE_MODE === "declared"
package/package.json CHANGED
@@ -1,75 +1,75 @@
1
1
  {
2
- "name": "@zalkera/client",
3
- "version": "0.25.0",
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
- }
2
+ "name": "@zalkera/client",
3
+ "version": "0.26.0",
4
+ "description": "zalkera 헤드리스 CMS 공개 API 클라이언트 (테넌트 사이트용)",
5
+ "license": "MIT",
6
+ "author": "Credium Co., Ltd.",
7
+ "type": "module",
8
+ "publishConfig": {
9
+ "access": "public"
46
10
  },
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
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
75
75
  }