@things-factory/board-ai 10.1.24 β†’ 10.1.26

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 (107) hide show
  1. package/PLAN-styling-tools.md +1 -1
  2. package/dist-client/server/service/assistant.d.ts +5 -4
  3. package/dist-client/server/service/assistant.js +35 -35
  4. package/dist-client/server/service/assistant.js.map +1 -1
  5. package/dist-client/server/service/styling/clear-tools.d.ts +2 -2
  6. package/dist-client/server/service/styling/clear-tools.js.map +1 -1
  7. package/dist-client/server/service/styling/copy-tools.d.ts +3 -3
  8. package/dist-client/server/service/styling/copy-tools.js +1 -1
  9. package/dist-client/server/service/styling/copy-tools.js.map +1 -1
  10. package/dist-client/server/service/styling/effect-tools.d.ts +2 -2
  11. package/dist-client/server/service/styling/effect-tools.js.map +1 -1
  12. package/dist-client/server/service/styling/fill-tools.d.ts +3 -3
  13. package/dist-client/server/service/styling/fill-tools.js +1 -1
  14. package/dist-client/server/service/styling/fill-tools.js.map +1 -1
  15. package/dist-client/server/service/styling/material-tools.d.ts +2 -2
  16. package/dist-client/server/service/styling/material-tools.js.map +1 -1
  17. package/dist-client/server/service/styling/registry.d.ts +6 -6
  18. package/dist-client/server/service/styling/registry.js +2 -2
  19. package/dist-client/server/service/styling/registry.js.map +1 -1
  20. package/dist-client/server/service/styling/stroke-tools.d.ts +2 -2
  21. package/dist-client/server/service/styling/stroke-tools.js.map +1 -1
  22. package/dist-client/server/service/styling/text-tools.d.ts +2 -2
  23. package/dist-client/server/service/styling/text-tools.js.map +1 -1
  24. package/dist-client/server/service/types.d.ts +6 -148
  25. package/dist-client/server/service/types.js.map +1 -1
  26. package/dist-client/server/service/validation/board-model-schema.d.ts +262 -262
  27. package/dist-client/server/service/validation/board-model-schema.js +4 -4
  28. package/dist-client/server/service/validation/board-model-schema.js.map +1 -1
  29. package/dist-client/server/service/validation/tool-validation.js +5 -5
  30. package/dist-client/server/service/validation/tool-validation.js.map +1 -1
  31. package/dist-client/tsconfig.tsbuildinfo +1 -1
  32. package/dist-server/index.d.ts +2 -2
  33. package/dist-server/index.js +2 -2
  34. package/dist-server/index.js.map +1 -1
  35. package/dist-server/service/assistant.d.ts +5 -4
  36. package/dist-server/service/assistant.js +37 -37
  37. package/dist-server/service/assistant.js.map +1 -1
  38. package/dist-server/service/board-ai-resolver.js +2 -2
  39. package/dist-server/service/board-ai-resolver.js.map +1 -1
  40. package/dist-server/service/index.d.ts +0 -1
  41. package/dist-server/service/index.js +0 -1
  42. package/dist-server/service/index.js.map +1 -1
  43. package/dist-server/service/patch-entry/board-patch-subscription.js +1 -1
  44. package/dist-server/service/patch-entry/board-patch-subscription.js.map +1 -1
  45. package/dist-server/service/patch-entry/patch-entry.d.ts +1 -1
  46. package/dist-server/service/patch-entry/patch-entry.js +1 -1
  47. package/dist-server/service/patch-entry/patch-entry.js.map +1 -1
  48. package/dist-server/service/styling/clear-tools.d.ts +2 -2
  49. package/dist-server/service/styling/clear-tools.js.map +1 -1
  50. package/dist-server/service/styling/copy-tools.d.ts +3 -3
  51. package/dist-server/service/styling/copy-tools.js +1 -1
  52. package/dist-server/service/styling/copy-tools.js.map +1 -1
  53. package/dist-server/service/styling/effect-tools.d.ts +2 -2
  54. package/dist-server/service/styling/effect-tools.js.map +1 -1
  55. package/dist-server/service/styling/fill-tools.d.ts +3 -3
  56. package/dist-server/service/styling/fill-tools.js +1 -1
  57. package/dist-server/service/styling/fill-tools.js.map +1 -1
  58. package/dist-server/service/styling/material-tools.d.ts +2 -2
  59. package/dist-server/service/styling/material-tools.js.map +1 -1
  60. package/dist-server/service/styling/registry.d.ts +6 -6
  61. package/dist-server/service/styling/registry.js +2 -2
  62. package/dist-server/service/styling/registry.js.map +1 -1
  63. package/dist-server/service/styling/stroke-tools.d.ts +2 -2
  64. package/dist-server/service/styling/stroke-tools.js.map +1 -1
  65. package/dist-server/service/styling/text-tools.d.ts +2 -2
  66. package/dist-server/service/styling/text-tools.js.map +1 -1
  67. package/dist-server/service/types.d.ts +6 -148
  68. package/dist-server/service/types.js.map +1 -1
  69. package/dist-server/service/validation/board-model-schema.d.ts +262 -262
  70. package/dist-server/service/validation/board-model-schema.js +4 -4
  71. package/dist-server/service/validation/board-model-schema.js.map +1 -1
  72. package/dist-server/service/validation/tool-validation.js +5 -5
  73. package/dist-server/service/validation/tool-validation.js.map +1 -1
  74. package/dist-server/tsconfig.tsbuildinfo +1 -1
  75. package/logs/.5e5d741d8b7784a2fbad65eedc0fd46946aaf6f2-audit.json +5 -0
  76. package/logs/connections-2026-09-17-16.log +0 -0
  77. package/package.json +8 -7
  78. package/server/index.ts +2 -2
  79. package/server/service/assistant-integration.test.ts +44 -44
  80. package/server/service/assistant-llm-smoke.test.ts +5 -5
  81. package/server/service/assistant.test.ts +86 -86
  82. package/server/service/assistant.ts +41 -43
  83. package/server/service/board-ai-resolver.ts +2 -2
  84. package/server/service/index.ts +0 -1
  85. package/server/service/patch-entry/board-patch-subscription.ts +1 -1
  86. package/server/service/patch-entry/patch-entry.ts +2 -2
  87. package/server/service/styling/clear-tools.ts +2 -2
  88. package/server/service/styling/copy-tools.ts +3 -3
  89. package/server/service/styling/effect-tools.ts +2 -2
  90. package/server/service/styling/fill-tools.test.ts +1 -1
  91. package/server/service/styling/fill-tools.ts +4 -4
  92. package/server/service/styling/material-tools.ts +2 -2
  93. package/server/service/styling/registry.ts +6 -6
  94. package/server/service/styling/stroke-tools.test.ts +1 -1
  95. package/server/service/styling/stroke-tools.ts +3 -3
  96. package/server/service/styling/text-tools.ts +2 -2
  97. package/server/service/tool-exposure.test.ts +7 -7
  98. package/server/service/types.ts +9 -110
  99. package/server/service/validation/board-model-schema.test.ts +3 -3
  100. package/server/service/validation/board-model-schema.ts +4 -4
  101. package/server/service/validation/tool-validation.ts +5 -5
  102. package/dist-server/service/apply-patch.d.ts +0 -47
  103. package/dist-server/service/apply-patch.js +0 -369
  104. package/dist-server/service/apply-patch.js.map +0 -1
  105. package/server/service/apply-patch-drift.test.ts +0 -831
  106. package/server/service/apply-patch.test.ts +0 -757
  107. package/server/service/apply-patch.ts +0 -411
@@ -309,7 +309,7 @@ exports.ComponentSchema = ComponentBaseSchema.superRefine((component, ctx) => {
309
309
  * β€’ 식별: id, refid, type
310
310
  *
311
311
  * ⚠ `name` 은 things-scene model-layer κ°€ 인식 μ•ˆ 함 β€” Board *μ—”ν‹°ν‹°* 의 μ»¬λŸΌμ΄μ§€
312
- * scene MODEL 의 ν•„λ“œκ°€ μ•„λ‹˜. AI κ°€ modifyBoard 둜 name 을 보내도 죽은 ν•„λ“œ. 검증
312
+ * scene MODEL 의 ν•„λ“œκ°€ μ•„λ‹˜. AI κ°€ modifyScene 둜 name 을 보내도 죽은 ν•„λ“œ. 검증
313
313
  * μ‹œμ—λ„ 제거 / λ¬΄μ‹œ μ •μ±….
314
314
  *
315
315
  * `passthrough` 인 이유 β€” 3D 광원/카메라/sky λ“± ν‚€κ°€ λ„ˆλ¬΄ λ§Žμ•„ strict λ‘œλŠ” νšŒκ·€ μœ„ν—˜
@@ -354,14 +354,14 @@ exports.ComponentPatchSchema = zod_1.z
354
354
  })
355
355
  .passthrough(); // μœ„μΉ˜ / 크기 / μ»΄ν¬λ„ŒνŠΈ specific 톡과
356
356
  /**
357
- * modifyBoard op 의 patch β€” λ³΄λ“œ root partial.
357
+ * modifyScene op 의 patch β€” λ³΄λ“œ root partial.
358
358
  *
359
359
  * λͺ¨λ“  root ν•„λ“œ optional (BoardModelSchema 와 달리 width/height 도 optional).
360
360
  * passthrough 둜 μ•Œλ €μ§€μ§€ μ•Šμ€ root ν‚€ (3D atmosphere, sky, camera λ“±) 톡과.
361
361
  * λ‹€λ§Œ *μ•Œλ €μ§„ nested object* (fillStyle λ“±) λŠ” strict schema μž¬μ‚¬μš© β€” κ·Έ μ•ˆμ—
362
362
  * unknown key κ°€ λ“€μ–΄μ˜€λ©΄ κ±°λΆ€ (예: fillStyle μ•ˆ gradientStops 같은 stale 이름).
363
363
  *
364
- * AI κ°€ setFill λ“±μ—μ„œ ν•™μŠ΅ν•œ vocabulary λ₯Ό modifyBoard 둜 echo μ‹œν‚¬ λ•Œ stale
364
+ * AI κ°€ setFill λ“±μ—μ„œ ν•™μŠ΅ν•œ vocabulary λ₯Ό modifyScene 둜 echo μ‹œν‚¬ λ•Œ stale
365
365
  * key κ°€ fillStyle / shadow 같은 sub-object μ•ˆμ— 끼면 λ³Έ schema κ°€ catch.
366
366
  */
367
367
  exports.BoardPatchSchema = zod_1.z
@@ -399,7 +399,7 @@ function validateComponent(component) {
399
399
  return { valid: true };
400
400
  return { valid: false, errors: formatIssues(r.error) };
401
401
  }
402
- /** modifyBoard op 의 patch 검증. λ³΄λ“œ root partial. */
402
+ /** modifyScene op 의 patch 검증. λ³΄λ“œ root partial. */
403
403
  function validateBoardPatch(patch) {
404
404
  const r = exports.BoardPatchSchema.safeParse(patch);
405
405
  if (r.success)
@@ -1 +1 @@
1
- {"version":3,"file":"board-model-schema.js","sourceRoot":"","sources":["../../../server/service/validation/board-model-schema.ts"],"names":[],"mappings":";;;AAoaA,gDAIC;AAGD,wDAIC;AAGD,8CAIC;AAGD,gDAIC;AA7bD;;;;;;;;;;;;;;;GAeG;AACH,6BAAuB;AAEvB,oEAAoE;AAEpE;;;GAGG;AACH,MAAM,WAAW,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;AAE7C,oEAAoE;AACpE,EAAE;AACF,6BAA6B;AAC7B,2BAA2B;AAC3B,yGAAyG;AACzG,mDAAmD;AACnD,6BAA6B;AAE7B,MAAM,kBAAkB,GAAG,OAAC;KACzB,MAAM,CAAC;IACN,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAClC,KAAK,EAAE,WAAW;CACnB,CAAC;KACD,MAAM,EAAE,CAAA;AAEX,MAAM,uBAAuB,GAAG,OAAC;KAC9B,MAAM,CAAC;IACN,IAAI,EAAE,OAAC,CAAC,OAAO,CAAC,UAAU,CAAC;IAC3B,YAAY,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE;IACrD,UAAU,EAAE,OAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9C,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC5C,MAAM,EAAE,OAAC;SACN,IAAI,CAAC;QACJ,QAAQ;QACR,UAAU;QACV,aAAa;QACb,WAAW;QACX,cAAc;QACd,MAAM;QACN,OAAO;QACP,KAAK;QACL,QAAQ;KACT,CAAC;SACD,QAAQ,EAAE;CACd,CAAC;KACD,MAAM,EAAE,CAAA;AAEX,MAAM,sBAAsB,GAAG,OAAC;KAC7B,MAAM,CAAC;IACN,IAAI,EAAE,OAAC,CAAC,OAAO,CAAC,SAAS,CAAC;IAC1B,KAAK,EAAE,OAAC,CAAC,KAAK,CAAC,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IACzC,KAAK,EAAE,WAAW,CAAC,QAAQ,EAAE;IAC7B,UAAU,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAClC,QAAQ,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAChC,KAAK,EAAE,OAAC;SACL,IAAI,CAAC;QACJ,QAAQ;QACR,UAAU;QACV,aAAa;QACb,WAAW;QACX,cAAc;QACd,MAAM;QACN,OAAO;QACP,KAAK;QACL,QAAQ;KACT,CAAC;SACD,QAAQ,EAAE;IACb,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC5C,SAAS,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAClC,CAAC;KACD,MAAM,EAAE,CAAA;AAEX,+EAA+E;AAC/E,MAAM,mBAAmB,GAAG,OAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAA;AAE7D,QAAA,eAAe,GAAG,OAAC,CAAC,KAAK,CAAC;IACrC,WAAW,EAAE,gBAAgB;IAC7B,uBAAuB;IACvB,sBAAsB;IACtB,mBAAmB;IACnB,OAAC,CAAC,IAAI,EAAE;CACT,CAAC,CAAA;AAEF,oEAAoE;AACpE,EAAE;AACF,gFAAgF;AAChF,mFAAmF;AACnF,0DAA0D;AAE7C,QAAA,YAAY,GAAG,OAAC;KAC1B,MAAM,CAAC;IACN,KAAK,EAAE,WAAW,CAAC,QAAQ,EAAE;IAC7B,8BAA8B;IAC9B,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAClC,iEAAiE;IACjE,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1B,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CACvC,CAAC;KACD,MAAM,EAAE,CAAA;AAEX,oEAAoE;AACpE,EAAE;AACF,mEAAmE;AAEtD,QAAA,gBAAgB,GAAG,OAAC;KAC9B,MAAM,CAAC;IACN,MAAM,EAAE,OAAC;SACN,IAAI,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;SACrF,QAAQ,EAAE;IACb,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC9C,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC9C,QAAQ,EAAE,WAAW,CAAC,QAAQ,EAAE;IAChC,iBAAiB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACtD,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC5C,eAAe,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACpD,IAAI,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE;IACpD,UAAU,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAClC,aAAa,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CACtC,CAAC;KACD,MAAM,EAAE,CAAA;AAEX,oEAAoE;AACpE,EAAE;AACF,wFAAwF;AAE3E,QAAA,aAAa,GAAG,OAAC;KAC3B,MAAM,CAAC;IACN,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE;IAClB,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,MAAM,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC9B,SAAS,EAAE,OAAC,CAAC,KAAK,CAAC,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE;IACxD,KAAK,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAC9B,CAAC;KACD,WAAW,EAAE,CAAA,CAAC,wCAAwC;AAEzD,oEAAoE;AACpE,EAAE;AACF,iEAAiE;AAEjE,MAAM,cAAc,GAAG,OAAC,CAAC,IAAI,CAAC;IAC5B,OAAO;IACP,WAAW;IACX,YAAY;IACZ,MAAM;IACN,UAAU;IACV,WAAW;IACX,eAAe;IACf,mBAAmB;CACpB,CAAC,CAAA;AAEF;;;;;;;;;;;GAWG;AACH,SAAS,yBAAyB,CAAC,SAAc;IAC/C,IAAI,CAAC,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAE7D,4DAA4D;IAC5D,MAAM,MAAM,GAAG,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,SAAS,CAAC,GAAG,KAAK,QAAQ,CAAA;IAEtF,0CAA0C;IAC1C,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC,EAAE,KAAK,QAAQ,IAAI,OAAO,SAAS,CAAC,EAAE,KAAK,QAAQ,CAAA;IAEtF,iBAAiB;IACjB,MAAM,gBAAgB,GACpB,OAAO,SAAS,CAAC,EAAE,KAAK,QAAQ;QAChC,OAAO,SAAS,CAAC,EAAE,KAAK,QAAQ;QAChC,OAAO,SAAS,CAAC,EAAE,KAAK,QAAQ;QAChC,OAAO,SAAS,CAAC,EAAE,KAAK,QAAQ,CAAA;IAElC,8BAA8B;IAC9B,MAAM,OAAO,GAAG,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;IACnF,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAA;IAChF,MAAM,WAAW,GACf,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;QACpE,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA,CAAE,YAAY;IAEtF,6DAA6D;IAC7D,MAAM,YAAY,GAChB,SAAS,CAAC,IAAI,IAAI,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ;QACpD,SAAS,CAAC,EAAE,IAAI,OAAO,SAAS,CAAC,EAAE,KAAK,QAAQ,CAAA;IAElD,OAAO,MAAM,IAAI,SAAS,IAAI,gBAAgB,IAAI,OAAO,IAAI,SAAS,IAAI,WAAW,IAAI,YAAY,CAAA;AACvG,CAAC;AAED,+DAA+D;AAC/D,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAA;AACtD,CAAC;AAED,gDAAgD;AAChD,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAA;AACtD,CAAC;AAED,MAAM,mBAAmB,GAAG,OAAC;KAC1B,MAAM,CAAC;IACN,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,wBAAwB;IAC5D,qDAAqD;IACrD,iCAAiC;IACjC,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1B,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,UAAU;IACV,SAAS,EAAE,uBAAe,CAAC,QAAQ,EAAE;IACrC,WAAW,EAAE,WAAW,CAAC,QAAQ,EAAE,EAAE,mBAAmB;IACxD,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACvC,QAAQ,EAAE,cAAc,CAAC,QAAQ,EAAE;IACnC,OAAO,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE;IACvD,QAAQ,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE;IACxD,MAAM,EAAE,oBAAY,CAAC,QAAQ,EAAE;IAC/B,UAAU,EAAE,wBAAgB,CAAC,QAAQ,EAAE;IACvC,0BAA0B;IAC1B,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjC,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC1C,UAAU,EAAE,OAAC,CAAC,KAAK,CAAC,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE;IACxD,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,SAAS,EAAE,WAAW,CAAC,QAAQ,EAAE;IACjC,SAAS,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE;IACpF,YAAY,EAAE,OAAC;SACZ,IAAI,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;SACzE,QAAQ,EAAE;IACb,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC5C,uBAAuB;IACvB,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC1C,MAAM,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC9B,gBAAgB;IAChB,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1B,UAAU;IACV,QAAQ,EAAE,OAAC,CAAC,KAAK,CAAC,qBAAa,CAAC,CAAC,QAAQ,EAAE;CAC5C,CAAC;KACD,WAAW,EAAE,CAAA,CAAC,qEAAqE;AAEtF;;;;;;;;;;GAUG;AACU,QAAA,eAAe,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE;IAChF,IAAI,CAAC,yBAAyB,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1C,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,OAAC,CAAC,YAAY,CAAC,MAAM;YAC3B,OAAO,EACL,cAAc,SAAS,CAAC,IAAI,iJAAiJ;SAChL,CAAC,CAAA;IACJ,CAAC;IAED,IAAI,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QACjE,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,OAAC,CAAC,YAAY,CAAC,MAAM;YAC3B,IAAI,EAAE,CAAC,OAAO,CAAC;YACf,OAAO,EAAE,cAAc,SAAS,CAAC,IAAI,gEAAgE;SACtG,CAAC,CAAA;IACJ,CAAC;IAED,IAAI,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAChE,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,OAAC,CAAC,YAAY,CAAC,MAAM;YAC3B,IAAI,EAAE,CAAC,MAAM,CAAC;YACd,OAAO,EAAE,cAAc,SAAS,CAAC,IAAI,uDAAuD;SAC7F,CAAC,CAAA;IACJ,CAAC;AACH,CAAC,CAAC,CAAA;AAEF,oEAAoE;AAEpE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACU,QAAA,gBAAgB,GAAG,OAAC;KAC9B,MAAM,CAAC;IACN,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,SAAS,EAAE,uBAAe,CAAC,QAAQ,EAAE,EAAE,QAAQ;IAC/C,UAAU,EAAE,OAAC,CAAC,KAAK,CAAC,uBAAe,CAAC,CAAC,QAAQ,EAAE;CAChD,CAAC;KACD,WAAW,EAAE,CAAA,CAAC,+CAA+C;AAEhE,8DAA8D;AAE9D;;;GAGG;AACU,QAAA,oBAAoB,GAAG,OAAC;KAClC,MAAM,CAAC;IACN,SAAS,EAAE,uBAAe,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAChD,WAAW,EAAE,WAAW,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC9C,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACvC,QAAQ,EAAE,cAAc,CAAC,QAAQ,EAAE;IACnC,OAAO,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE;IACvD,QAAQ,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE;IACxD,MAAM,EAAE,oBAAY,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC1C,UAAU,EAAE,wBAAgB,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAClD,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjC,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC1C,UAAU,EAAE,OAAC,CAAC,KAAK,CAAC,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE;IACxD,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,SAAS,EAAE,WAAW,CAAC,QAAQ,EAAE;IACjC,SAAS,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE;IACpF,YAAY,EAAE,OAAC;SACZ,IAAI,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;SACzE,QAAQ,EAAE;IACb,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC5C,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC1C,MAAM,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAC/B,CAAC;KACD,WAAW,EAAE,CAAA,CAAC,6BAA6B;AAE9C;;;;;;;;;;GAUG;AACU,QAAA,gBAAgB,GAAG,OAAC;KAC9B,MAAM,CAAC;IACN,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACvC,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACxC,SAAS,EAAE,uBAAe,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAChD,UAAU,EAAE,OAAC,CAAC,KAAK,CAAC,uBAAe,CAAC,CAAC,QAAQ,EAAE;CAChD,CAAC;KACD,WAAW,EAAE,CAAA,CAAC,+DAA+D;AAShF,SAAS,YAAY,CAAC,KAAiB;IACrC,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;QAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAC,CAAC,CAAA;AACL,CAAC;AAED,sCAAsC;AACtC,SAAgB,kBAAkB,CAAC,KAAc;IAC/C,MAAM,CAAC,GAAG,wBAAgB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IAC3C,IAAI,CAAC,CAAC,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IACrC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAA;AACxD,CAAC;AAED,2DAA2D;AAC3D,SAAgB,sBAAsB,CAAC,KAAc;IACnD,MAAM,CAAC,GAAG,4BAAoB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IAC/C,IAAI,CAAC,CAAC,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IACrC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAA;AACxD,CAAC;AAED,oCAAoC;AACpC,SAAgB,iBAAiB,CAAC,SAAkB;IAClD,MAAM,CAAC,GAAG,uBAAe,CAAC,SAAS,CAAC,SAAS,CAAC,CAAA;IAC9C,IAAI,CAAC,CAAC,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IACrC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAA;AACxD,CAAC;AAED,kDAAkD;AAClD,SAAgB,kBAAkB,CAAC,KAAc;IAC/C,MAAM,CAAC,GAAG,wBAAgB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IAC3C,IAAI,CAAC,CAAC,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IACrC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAA;AACxD,CAAC","sourcesContent":["/**\n * BoardModel zod schema β€” μ˜μ† / patch 적용 boundary μ—μ„œ 데이터 무결성 검증.\n *\n * 핡심 μ •μ±…:\n * - things-scene 의 μ‹€μ œ drawer / setter κ΅¬ν˜„μ„ μ§„μ‹€λ‘œ λ΄„ (μΈν„°νŽ˜μ΄μŠ€ declaration X)\n * - Component schema λŠ” `.passthrough()` β€” μ•Œλ €μ§„ ν•„λ“œ 검증 + λ―Έμ§€ ν•„λ“œ 톡과\n * (μ‚¬μš©μž μ •μ˜ / 데이터 바인딩 / μ»΄ν¬λ„ŒνŠΈλ³„ specific 속성 ν—ˆμš©)\n * - κ·ΈλŸ¬λ‚˜ styling sub-objects (FillStyle / Shadow λ“±) λŠ” `.strict()` β€” unknown key\n * κ±°λΆ€. stale μž”μž¬ ν•„λ“œ (예: colorStops + colors 곡쑴) μžλ™ 발견.\n * - validation μ‹€νŒ¨ μ‹œ silent λˆ„λ½ X β€” caller κ°€ λͺ…μ‹œμ  μ—λŸ¬ / 둜그\n *\n * 적용 지점:\n * 1. server `UpdateBoard` mutation μ§„μž… β†’ DB μ˜μ† 직전 λ§ˆμ§€λ§‰ λ°©μ–΄μ„ \n * 2. server `BoardAIChat` mutation 응닡 직전 β€” AI κ°€ λ§Œλ“  patch 의 μ•ˆμ „μ„±\n * 3. (선택) client apply-patch 적용 ν›„ β€” λ©”λͺ¨λ¦¬ μƒνƒœ 무결성\n */\nimport { z } from 'zod'\n\n// ── Color ────────────────────────────────────────────────────────\n\n/**\n * 색상 β€” hex (#rgb / #rgba / #rrggbb / #rrggbbaa) / rgb / rgba / CSS named / 'transparent'.\n * μ •κ·œμ‹μ€ 느슨 β€” μ •ν™•ν•œ 검증은 normalizeColor κ°€, μ—¬κΈ°μ„  ν˜•μ‹λ§Œ.\n */\nconst ColorSchema = z.string().min(1).max(50)\n\n// ── FillStyle (state.fillStyle) ──────────────────────────────────\n//\n// μ‹€μ œ ν˜•μ‹ (drawer/fill.ts 검증):\n// - solid: 색상 string κ·ΈλŒ€λ‘œ\n// - gradient: { type: 'gradient', gradientType, colorStops: [{position, color}], rotation?, opacity? }\n// - pattern: { type: 'pattern', image, ...μ˜΅μ…˜λ“€ }\n// - none: undefined / null\n\nconst GradientStopSchema = z\n .object({\n position: z.number().min(0).max(1),\n color: ColorSchema\n })\n .strict()\n\nconst FillStyleGradientSchema = z\n .object({\n type: z.literal('gradient'),\n gradientType: z.enum(['linear', 'radial']).optional(),\n colorStops: z.array(GradientStopSchema).min(2),\n rotation: z.number().optional(),\n opacity: z.number().min(0).max(1).optional(),\n center: z\n .enum([\n 'center',\n 'left-top',\n 'left-bottom',\n 'right-top',\n 'right-bottom',\n 'left',\n 'right',\n 'top',\n 'bottom'\n ])\n .optional()\n })\n .strict()\n\nconst FillStylePatternSchema = z\n .object({\n type: z.literal('pattern'),\n image: z.union([z.string(), z.unknown()]),\n color: ColorSchema.optional(),\n fitPattern: z.boolean().optional(),\n noRepeat: z.boolean().optional(),\n align: z\n .enum([\n 'center',\n 'left-top',\n 'left-bottom',\n 'right-top',\n 'right-bottom',\n 'left',\n 'right',\n 'top',\n 'bottom'\n ])\n .optional(),\n offsetX: z.number().optional(),\n offsetY: z.number().optional(),\n width: z.number().optional(),\n height: z.number().optional(),\n opacity: z.number().min(0).max(1).optional(),\n guideOnly: z.boolean().optional()\n })\n .strict()\n\n/** 'none' type β€” fillStyle 제거 μ˜λ„ ν‘œν˜„ (state.fillStyle = {type:'none'} 도 ν—ˆμš©). */\nconst FillStyleNoneSchema = z.object({ type: z.literal('none') }).strict()\n\nexport const FillStyleSchema = z.union([\n ColorSchema, // solid: '#xxx'\n FillStyleGradientSchema,\n FillStylePatternSchema,\n FillStyleNoneSchema,\n z.null()\n])\n\n// ── Shadow (state.shadow) ────────────────────────────────────────\n//\n// Canonical (CSS box-shadow / Canvas API ν‘œμ€€): { color, offsetX, offsetY, blur }\n// Backwards-compat aliases (μ˜› 이름, drawer κ°€ fallback 으둜 읽음): left / top / blurSize.\n// μ˜μ†λœ μ˜› 데이터 ν˜Έν™˜μ„ μœ„ν•΄ 두 이름 λ‹€ schema 톡과 β€” μƒˆ μ½”λ“œλŠ” canonical μ‚¬μš©.\n\nexport const ShadowSchema = z\n .object({\n color: ColorSchema.optional(),\n // canonical (CSS / Canvas ν‘œμ€€)\n offsetX: z.number().optional(),\n offsetY: z.number().optional(),\n blur: z.number().min(0).optional(),\n // legacy aliases β€” μ˜› things-scene 데이터 ν˜Έν™˜μš©. μƒˆ μ½”λ“œλŠ” μœ„ canonical μ‚¬μš©.\n left: z.number().optional(),\n top: z.number().optional(),\n blurSize: z.number().min(0).optional()\n })\n .strict()\n\n// ── Material3D (state.material3d) ────────────────────────────────\n//\n// `things-scene/src/threed/material-3d.ts` 의 Material3D μΈν„°νŽ˜μ΄μŠ€μ™€ 일치.\n\nexport const Material3DSchema = z\n .object({\n preset: z\n .enum(['default', 'metal', 'glass', 'plastic', 'wood', 'ceramic', 'rubber', 'custom'])\n .optional(),\n metalness: z.number().min(0).max(1).optional(),\n roughness: z.number().min(0).max(1).optional(),\n emissive: ColorSchema.optional(),\n emissiveIntensity: z.number().min(0).max(5).optional(),\n opacity: z.number().min(0).max(1).optional(),\n envMapIntensity: z.number().min(0).max(3).optional(),\n side: z.enum(['double', 'front', 'back']).optional(),\n castShadow: z.boolean().optional(),\n receiveShadow: z.boolean().optional()\n })\n .strict()\n\n// ── DataMapping (state.mappings) ─────────────────────────────────\n//\n// 데이터 바인딩 β€” `interfaces/data.ts` Mapping. rule / target / accessor / converter / param.\n\nexport const MappingSchema = z\n .object({\n rule: z.string().optional(),\n target: z.string(),\n property: z.string().optional(),\n accessor: z.string().optional(),\n source: z.unknown().optional(),\n converter: z.union([z.string(), z.unknown()]).optional(),\n param: z.unknown().optional()\n })\n .passthrough() // mapping 의 자유 ν•„λ“œ (converter args λ“±) ν—ˆμš©\n\n// ── Component (one component in board.components) ────────────────\n//\n// 핡심 styling ν•„λ“œλŠ” λͺ…μ‹œ 검증, λ‚˜λ¨Έμ§€λŠ” passthrough β€” μ»΄ν¬λ„ŒνŠΈλ³„ specific 속성 자유.\n\nconst LineDashSchema = z.enum([\n 'solid',\n 'round-dot',\n 'square-dot',\n 'dash',\n 'dash-dot',\n 'long-dash',\n 'long-dash-dot',\n 'long-dash-dot-dot'\n])\n\n/**\n * μ»΄ν¬λ„ŒνŠΈκ°€ *μ–΄λ–€ ν˜•νƒœλ“ * μœ νš¨ν•œ geometry λ₯Ό κ°€μ‘ŒλŠ”μ§€ 확인.\n *\n * things-scene 의 μ»΄ν¬λ„ŒνŠΈ type λ³„λ‘œ geometry ν•„λ“œκ°€ 닀양함 (rect λŠ” box,\n * line 은 endpoints, polyline 은 vertices/path λ“±). type β†’ ν•„λ“œ 맀핑을 strict\n * ν•˜κ²Œ enumerate ν•˜λ©΄ μ‹ κ·œ type 등둝 μ‹œ schema 도 λ™μ‹œ μ—…λ°μ΄νŠΈ ν•„μš” β†’ forward\n * compatibility 손해.\n *\n * λŒ€μ‹  \"μ΅œμ†Œν•œ μ–΄λ–€ geometry ν˜•μ‹μ΄λΌλ„ μžˆμ–΄μ•Ό ν•œλ‹€\" λŠ” μ•½ν•œ 보μž₯ β€” 빈 μ»΄ν¬λ„ŒνŠΈ\n * (`{type:'rect'}` 만) λ˜λŠ” type 에 μ•ˆ λ§žλŠ” geometry ꡬ성 (`{type:'rect', cx:10, cy:20}`)\n * 같은 λͺ…λ°±ν•œ 였λ₯˜λ§Œ 차단. μ‹€μ œ λ Œλ”λ§ 정합성은 things-scene μžμ²΄κ°€ 검증.\n */\nfunction componentHasValidGeometry(component: any): boolean {\n if (!component || typeof component !== 'object') return false\n\n // Box geometry β€” left/top λ‘˜ λ‹€ μžˆμ–΄μ•Ό (width/height λŠ” optional)\n const hasBox = typeof component.left === 'number' && typeof component.top === 'number'\n\n // Circle/ellipse geometry β€” cx/cy λ‘˜ λ‹€ μžˆμ–΄μ•Ό\n const hasCircle = typeof component.cx === 'number' && typeof component.cy === 'number'\n\n // Line endpoints\n const hasLineEndpoints =\n typeof component.x1 === 'number' &&\n typeof component.y1 === 'number' &&\n typeof component.x2 === 'number' &&\n typeof component.y2 === 'number'\n\n // Path / points / vertices 계열\n const hasPath = typeof component.path === 'string' || Array.isArray(component.path)\n const hasPoints = Array.isArray(component.points) && component.points.length > 0\n const hasVertices =\n (Array.isArray(component.vertices) && component.vertices.length > 0) ||\n (Array.isArray(component.verticles) && component.verticles.length > 0) // legacy μ˜€νƒ€\n\n // line connector β€” startPoint / endPoint ν˜•νƒœ (anchor 기반 line)\n const hasConnector =\n component.from && typeof component.from === 'object' &&\n component.to && typeof component.to === 'object'\n\n return hasBox || hasCircle || hasLineEndpoints || hasPath || hasPoints || hasVertices || hasConnector\n}\n\n/** type 이 'gauge' λ˜λŠ” 'gauge-*' λ©΄ value ν•„λ“œ ν•„μš” (선택: min/max). */\nfunction isGaugeType(type: string): boolean {\n return type === 'gauge' || type.startsWith('gauge-')\n}\n\n/** type 이 'chart' λ˜λŠ” 'chart-*' λ©΄ data ν•„λ“œ ν•„μš”. */\nfunction isChartType(type: string): boolean {\n return type === 'chart' || type.startsWith('chart-')\n}\n\nconst ComponentBaseSchema = z\n .object({\n type: z.string().min(1),\n refid: z.number().int().optional(), // μ‹ κ·œ μ»΄ν¬λ„ŒνŠΈ μΆ”κ°€ μ‹œμ μ—” λ―Έν• λ‹Ή κ°€λŠ₯\n // μœ„μΉ˜ / 크기 β€” μ»΄ν¬λ„ŒνŠΈ νƒ€μž…μ— 따라 닀름. λ³Έ schema λŠ” optional 둜 두고\n // refinement κ°€ *μ–΄λ–€ ν˜•νƒœλ“ * μžˆλŠ”μ§€λ§Œ 검사.\n left: z.number().optional(),\n top: z.number().optional(),\n width: z.number().optional(),\n height: z.number().optional(),\n cx: z.number().optional(),\n cy: z.number().optional(),\n rx: z.number().optional(),\n ry: z.number().optional(),\n radius: z.number().optional(),\n x1: z.number().optional(),\n y1: z.number().optional(),\n x2: z.number().optional(),\n y2: z.number().optional(),\n rotation: z.number().optional(),\n // styling\n fillStyle: FillStyleSchema.optional(),\n strokeStyle: ColorSchema.optional(), // top-level string\n lineWidth: z.number().min(0).optional(),\n lineDash: LineDashSchema.optional(),\n lineCap: z.enum(['butt', 'round', 'square']).optional(),\n lineJoin: z.enum(['miter', 'round', 'bevel']).optional(),\n shadow: ShadowSchema.optional(),\n material3d: Material3DSchema.optional(),\n // text / font β€” top-level\n text: z.string().optional(),\n fontFamily: z.string().optional(),\n fontSize: z.number().positive().optional(),\n fontWeight: z.union([z.string(), z.number()]).optional(),\n fontStyle: z.string().optional(),\n fontColor: ColorSchema.optional(),\n textAlign: z.enum(['left', 'center', 'right', 'start', 'end', 'justify']).optional(),\n textBaseline: z\n .enum(['top', 'middle', 'bottom', 'hanging', 'alphabetic', 'ideographic'])\n .optional(),\n lineHeight: z.number().positive().optional(),\n // visibility / opacity\n alpha: z.number().min(0).max(1).optional(),\n hidden: z.boolean().optional(),\n // misc β€” 검증 μ•ˆ 함\n id: z.string().optional(),\n class: z.string().optional(),\n tag: z.string().optional(),\n // 데이터 바인딩\n mappings: z.array(MappingSchema).optional()\n })\n .passthrough() // μ»΄ν¬λ„ŒνŠΈλ³„ specific 속성 (vehicle wheels, gauge needle, points/path λ“±) ν—ˆμš©\n\n/**\n * Component schema β€” base μœ„μ— type-aware refinement 두 κ°€μ§€:\n *\n * 1. λͺ¨λ“  μ»΄ν¬λ„ŒνŠΈλŠ” *μ–΄λ–€ ν˜•νƒœλ“ * μœ νš¨ν•œ geometry κ°€ μžˆμ–΄μ•Ό\n * (left+top / cx+cy / x1y1x2y2 / path / points / vertices / from+to)\n * 2. gauge-* type 은 value ν•„μš”, chart-* type 은 data ν•„μš” (soft check β€”\n * 일뢀 prefab 이 미리 μ±„μ›Œμ£ΌλŠ” μΌ€μ΄μŠ€λ„ μžˆμ–΄ strict 거뢀보닀 미리 작기 λͺ©μ )\n *\n * 이 refinement 듀이 LLM 의 잘λͺ»λœ geometry (rect 에 cx/cy만, line 에 left/top만)\n * λ₯Ό μ˜μ† 직전 차단.\n */\nexport const ComponentSchema = ComponentBaseSchema.superRefine((component, ctx) => {\n if (!componentHasValidGeometry(component)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `Component '${component.type}' has no valid geometry β€” provide left+top (box), cx+cy (circle/ellipse), x1+y1+x2+y2 (line), path / points / vertices, or from+to (connector).`\n })\n }\n\n if (isGaugeType(component.type) && component.value === undefined) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['value'],\n message: `Component '${component.type}' requires 'value' (current sensor reading or display number).`\n })\n }\n\n if (isChartType(component.type) && component.data === undefined) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['data'],\n message: `Component '${component.type}' requires 'data' (chart datapoints array or object).`\n })\n }\n})\n\n// ── BoardModel (top-level) ───────────────────────────────────────\n\n/**\n * BoardModel β€” things-scene 의 model-layer (λ³΄λ“œ root).\n *\n * μΈμ‹λ˜λŠ” root 속성 (model-layer 의 NATURE.properties + μ‹€μ œ μ½”λ“œμ—μ„œ state.* 둜\n * μ°Έμ‘°λ˜λŠ” ν‚€):\n * β€’ Layout: width, height, fitMode, translate, scale\n * β€’ 2D λ°°κ²½: fillStyle\n * β€’ 3D Sky: sky, skyColor\n * β€’ 3D Tone: exposure\n * β€’ 3D Hemisphere: hemiSkyColor, hemiGroundColor, hemiIntensity\n * β€’ 3D Directional light: dirLightEnabled, dirLightColor, dirLightIntensity,\n * dirLightFollowCamera, dirLightAzimuth, dirLightElevation\n * β€’ 3D Shadow: dirShadowEnabled, dirShadowMapSize, dirShadowBias\n * β€’ 3D Camera: cameraX, cameraY, cameraZ, cameraAutoPlay, autoRotate, initialCameraView\n * β€’ 식별: id, refid, type\n *\n * ⚠ `name` 은 things-scene model-layer κ°€ 인식 μ•ˆ 함 β€” Board *μ—”ν‹°ν‹°* 의 μ»¬λŸΌμ΄μ§€\n * scene MODEL 의 ν•„λ“œκ°€ μ•„λ‹˜. AI κ°€ modifyBoard 둜 name 을 보내도 죽은 ν•„λ“œ. 검증\n * μ‹œμ—λ„ 제거 / λ¬΄μ‹œ μ •μ±….\n *\n * `passthrough` 인 이유 β€” 3D 광원/카메라/sky λ“± ν‚€κ°€ λ„ˆλ¬΄ λ§Žμ•„ strict λ‘œλŠ” νšŒκ·€ μœ„ν—˜\n * 큼. λŒ€μ‹  νŒ¨ν‚€μ§€ μ™ΈλΆ€ (operato λ“±) κ°€ μž„μ˜ root ν‚€ μΆ”κ°€ν•˜λŠ” κ±Έ 막지 μ•ŠμŒ.\n */\nexport const BoardModelSchema = z\n .object({\n width: z.number().positive(),\n height: z.number().positive(),\n fillStyle: FillStyleSchema.optional(), // λ³΄λ“œ λ°°κ²½\n components: z.array(ComponentSchema).optional()\n })\n .passthrough() // λ³΄λ“œ 레벨 자유 ν•„λ“œ (3D atmosphere / sky / camera λ“±)\n\n// ── Patch op schemas β€” modify op 의 patch κ°€ invalid 이면 κ±°λΆ€ ───\n\n/**\n * modify op 의 patch β€” μ»΄ν¬λ„ŒνŠΈ partial. λͺ¨λ“  ν•„λ“œ optional. styling sub-object\n * 만 strict ν•˜κ²Œ 검증해 stale key μž”μ‘΄ 차단.\n */\nexport const ComponentPatchSchema = z\n .object({\n fillStyle: FillStyleSchema.nullable().optional(),\n strokeStyle: ColorSchema.nullable().optional(),\n lineWidth: z.number().min(0).optional(),\n lineDash: LineDashSchema.optional(),\n lineCap: z.enum(['butt', 'round', 'square']).optional(),\n lineJoin: z.enum(['miter', 'round', 'bevel']).optional(),\n shadow: ShadowSchema.nullable().optional(),\n material3d: Material3DSchema.nullable().optional(),\n text: z.string().optional(),\n fontFamily: z.string().optional(),\n fontSize: z.number().positive().optional(),\n fontWeight: z.union([z.string(), z.number()]).optional(),\n fontStyle: z.string().optional(),\n fontColor: ColorSchema.optional(),\n textAlign: z.enum(['left', 'center', 'right', 'start', 'end', 'justify']).optional(),\n textBaseline: z\n .enum(['top', 'middle', 'bottom', 'hanging', 'alphabetic', 'ideographic'])\n .optional(),\n lineHeight: z.number().positive().optional(),\n alpha: z.number().min(0).max(1).optional(),\n hidden: z.boolean().optional()\n })\n .passthrough() // μœ„μΉ˜ / 크기 / μ»΄ν¬λ„ŒνŠΈ specific 톡과\n\n/**\n * modifyBoard op 의 patch β€” λ³΄λ“œ root partial.\n *\n * λͺ¨λ“  root ν•„λ“œ optional (BoardModelSchema 와 달리 width/height 도 optional).\n * passthrough 둜 μ•Œλ €μ§€μ§€ μ•Šμ€ root ν‚€ (3D atmosphere, sky, camera λ“±) 톡과.\n * λ‹€λ§Œ *μ•Œλ €μ§„ nested object* (fillStyle λ“±) λŠ” strict schema μž¬μ‚¬μš© β€” κ·Έ μ•ˆμ—\n * unknown key κ°€ λ“€μ–΄μ˜€λ©΄ κ±°λΆ€ (예: fillStyle μ•ˆ gradientStops 같은 stale 이름).\n *\n * AI κ°€ setFill λ“±μ—μ„œ ν•™μŠ΅ν•œ vocabulary λ₯Ό modifyBoard 둜 echo μ‹œν‚¬ λ•Œ stale\n * key κ°€ fillStyle / shadow 같은 sub-object μ•ˆμ— 끼면 λ³Έ schema κ°€ catch.\n */\nexport const BoardPatchSchema = z\n .object({\n width: z.number().positive().optional(),\n height: z.number().positive().optional(),\n fillStyle: FillStyleSchema.nullable().optional(),\n components: z.array(ComponentSchema).optional()\n })\n .passthrough() // root 의 자유 ν•„λ“œ (sky / exposure / hemi*/ dirLight* / camera* λ“±)\n\n// ── 검증 헬퍼 ────────────────────────────────────────────────────\n\nexport interface ValidationResult {\n valid: boolean\n errors?: Array<{ path: string; message: string }>\n}\n\nfunction formatIssues(error: z.ZodError): Array<{ path: string; message: string }> {\n return error.issues.map(issue => ({\n path: issue.path.join('.'),\n message: issue.message\n }))\n}\n\n/** BoardModel 전체 검증. μ˜μ† / λ‘œλ“œ μ‹œ μ‚¬μš©. */\nexport function validateBoardModel(model: unknown): ValidationResult {\n const r = BoardModelSchema.safeParse(model)\n if (r.success) return { valid: true }\n return { valid: false, errors: formatIssues(r.error) }\n}\n\n/** modify patch 검증. AI κ°€ λ§Œλ“  patch κ°€ styling spec 에 λ§žλŠ”μ§€. */\nexport function validateComponentPatch(patch: unknown): ValidationResult {\n const r = ComponentPatchSchema.safeParse(patch)\n if (r.success) return { valid: true }\n return { valid: false, errors: formatIssues(r.error) }\n}\n\n/** ν•œ μ»΄ν¬λ„ŒνŠΈ 검증. addComponent μ‹œ μ‚¬μš©. */\nexport function validateComponent(component: unknown): ValidationResult {\n const r = ComponentSchema.safeParse(component)\n if (r.success) return { valid: true }\n return { valid: false, errors: formatIssues(r.error) }\n}\n\n/** modifyBoard op 의 patch 검증. λ³΄λ“œ root partial. */\nexport function validateBoardPatch(patch: unknown): ValidationResult {\n const r = BoardPatchSchema.safeParse(patch)\n if (r.success) return { valid: true }\n return { valid: false, errors: formatIssues(r.error) }\n}\n"]}
1
+ {"version":3,"file":"board-model-schema.js","sourceRoot":"","sources":["../../../server/service/validation/board-model-schema.ts"],"names":[],"mappings":";;;AAoaA,gDAIC;AAGD,wDAIC;AAGD,8CAIC;AAGD,gDAIC;AA7bD;;;;;;;;;;;;;;;GAeG;AACH,6BAAuB;AAEvB,oEAAoE;AAEpE;;;GAGG;AACH,MAAM,WAAW,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;AAE7C,oEAAoE;AACpE,EAAE;AACF,6BAA6B;AAC7B,2BAA2B;AAC3B,yGAAyG;AACzG,mDAAmD;AACnD,6BAA6B;AAE7B,MAAM,kBAAkB,GAAG,OAAC;KACzB,MAAM,CAAC;IACN,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAClC,KAAK,EAAE,WAAW;CACnB,CAAC;KACD,MAAM,EAAE,CAAA;AAEX,MAAM,uBAAuB,GAAG,OAAC;KAC9B,MAAM,CAAC;IACN,IAAI,EAAE,OAAC,CAAC,OAAO,CAAC,UAAU,CAAC;IAC3B,YAAY,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE;IACrD,UAAU,EAAE,OAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9C,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC5C,MAAM,EAAE,OAAC;SACN,IAAI,CAAC;QACJ,QAAQ;QACR,UAAU;QACV,aAAa;QACb,WAAW;QACX,cAAc;QACd,MAAM;QACN,OAAO;QACP,KAAK;QACL,QAAQ;KACT,CAAC;SACD,QAAQ,EAAE;CACd,CAAC;KACD,MAAM,EAAE,CAAA;AAEX,MAAM,sBAAsB,GAAG,OAAC;KAC7B,MAAM,CAAC;IACN,IAAI,EAAE,OAAC,CAAC,OAAO,CAAC,SAAS,CAAC;IAC1B,KAAK,EAAE,OAAC,CAAC,KAAK,CAAC,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IACzC,KAAK,EAAE,WAAW,CAAC,QAAQ,EAAE;IAC7B,UAAU,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAClC,QAAQ,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAChC,KAAK,EAAE,OAAC;SACL,IAAI,CAAC;QACJ,QAAQ;QACR,UAAU;QACV,aAAa;QACb,WAAW;QACX,cAAc;QACd,MAAM;QACN,OAAO;QACP,KAAK;QACL,QAAQ;KACT,CAAC;SACD,QAAQ,EAAE;IACb,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC5C,SAAS,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAClC,CAAC;KACD,MAAM,EAAE,CAAA;AAEX,+EAA+E;AAC/E,MAAM,mBAAmB,GAAG,OAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAA;AAE7D,QAAA,eAAe,GAAG,OAAC,CAAC,KAAK,CAAC;IACrC,WAAW,EAAE,gBAAgB;IAC7B,uBAAuB;IACvB,sBAAsB;IACtB,mBAAmB;IACnB,OAAC,CAAC,IAAI,EAAE;CACT,CAAC,CAAA;AAEF,oEAAoE;AACpE,EAAE;AACF,gFAAgF;AAChF,mFAAmF;AACnF,0DAA0D;AAE7C,QAAA,YAAY,GAAG,OAAC;KAC1B,MAAM,CAAC;IACN,KAAK,EAAE,WAAW,CAAC,QAAQ,EAAE;IAC7B,8BAA8B;IAC9B,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAClC,iEAAiE;IACjE,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1B,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CACvC,CAAC;KACD,MAAM,EAAE,CAAA;AAEX,oEAAoE;AACpE,EAAE;AACF,mEAAmE;AAEtD,QAAA,gBAAgB,GAAG,OAAC;KAC9B,MAAM,CAAC;IACN,MAAM,EAAE,OAAC;SACN,IAAI,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;SACrF,QAAQ,EAAE;IACb,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC9C,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC9C,QAAQ,EAAE,WAAW,CAAC,QAAQ,EAAE;IAChC,iBAAiB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACtD,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC5C,eAAe,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACpD,IAAI,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE;IACpD,UAAU,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAClC,aAAa,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CACtC,CAAC;KACD,MAAM,EAAE,CAAA;AAEX,oEAAoE;AACpE,EAAE;AACF,wFAAwF;AAE3E,QAAA,aAAa,GAAG,OAAC;KAC3B,MAAM,CAAC;IACN,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE;IAClB,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,MAAM,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC9B,SAAS,EAAE,OAAC,CAAC,KAAK,CAAC,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE;IACxD,KAAK,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAC9B,CAAC;KACD,WAAW,EAAE,CAAA,CAAC,wCAAwC;AAEzD,oEAAoE;AACpE,EAAE;AACF,iEAAiE;AAEjE,MAAM,cAAc,GAAG,OAAC,CAAC,IAAI,CAAC;IAC5B,OAAO;IACP,WAAW;IACX,YAAY;IACZ,MAAM;IACN,UAAU;IACV,WAAW;IACX,eAAe;IACf,mBAAmB;CACpB,CAAC,CAAA;AAEF;;;;;;;;;;;GAWG;AACH,SAAS,yBAAyB,CAAC,SAAc;IAC/C,IAAI,CAAC,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAE7D,4DAA4D;IAC5D,MAAM,MAAM,GAAG,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,SAAS,CAAC,GAAG,KAAK,QAAQ,CAAA;IAEtF,0CAA0C;IAC1C,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC,EAAE,KAAK,QAAQ,IAAI,OAAO,SAAS,CAAC,EAAE,KAAK,QAAQ,CAAA;IAEtF,iBAAiB;IACjB,MAAM,gBAAgB,GACpB,OAAO,SAAS,CAAC,EAAE,KAAK,QAAQ;QAChC,OAAO,SAAS,CAAC,EAAE,KAAK,QAAQ;QAChC,OAAO,SAAS,CAAC,EAAE,KAAK,QAAQ;QAChC,OAAO,SAAS,CAAC,EAAE,KAAK,QAAQ,CAAA;IAElC,8BAA8B;IAC9B,MAAM,OAAO,GAAG,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;IACnF,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAA;IAChF,MAAM,WAAW,GACf,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;QACpE,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA,CAAE,YAAY;IAEtF,6DAA6D;IAC7D,MAAM,YAAY,GAChB,SAAS,CAAC,IAAI,IAAI,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ;QACpD,SAAS,CAAC,EAAE,IAAI,OAAO,SAAS,CAAC,EAAE,KAAK,QAAQ,CAAA;IAElD,OAAO,MAAM,IAAI,SAAS,IAAI,gBAAgB,IAAI,OAAO,IAAI,SAAS,IAAI,WAAW,IAAI,YAAY,CAAA;AACvG,CAAC;AAED,+DAA+D;AAC/D,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAA;AACtD,CAAC;AAED,gDAAgD;AAChD,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAA;AACtD,CAAC;AAED,MAAM,mBAAmB,GAAG,OAAC;KAC1B,MAAM,CAAC;IACN,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,wBAAwB;IAC5D,qDAAqD;IACrD,iCAAiC;IACjC,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1B,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,UAAU;IACV,SAAS,EAAE,uBAAe,CAAC,QAAQ,EAAE;IACrC,WAAW,EAAE,WAAW,CAAC,QAAQ,EAAE,EAAE,mBAAmB;IACxD,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACvC,QAAQ,EAAE,cAAc,CAAC,QAAQ,EAAE;IACnC,OAAO,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE;IACvD,QAAQ,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE;IACxD,MAAM,EAAE,oBAAY,CAAC,QAAQ,EAAE;IAC/B,UAAU,EAAE,wBAAgB,CAAC,QAAQ,EAAE;IACvC,0BAA0B;IAC1B,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjC,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC1C,UAAU,EAAE,OAAC,CAAC,KAAK,CAAC,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE;IACxD,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,SAAS,EAAE,WAAW,CAAC,QAAQ,EAAE;IACjC,SAAS,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE;IACpF,YAAY,EAAE,OAAC;SACZ,IAAI,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;SACzE,QAAQ,EAAE;IACb,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC5C,uBAAuB;IACvB,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC1C,MAAM,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC9B,gBAAgB;IAChB,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1B,UAAU;IACV,QAAQ,EAAE,OAAC,CAAC,KAAK,CAAC,qBAAa,CAAC,CAAC,QAAQ,EAAE;CAC5C,CAAC;KACD,WAAW,EAAE,CAAA,CAAC,qEAAqE;AAEtF;;;;;;;;;;GAUG;AACU,QAAA,eAAe,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE;IAChF,IAAI,CAAC,yBAAyB,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1C,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,OAAC,CAAC,YAAY,CAAC,MAAM;YAC3B,OAAO,EACL,cAAc,SAAS,CAAC,IAAI,iJAAiJ;SAChL,CAAC,CAAA;IACJ,CAAC;IAED,IAAI,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QACjE,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,OAAC,CAAC,YAAY,CAAC,MAAM;YAC3B,IAAI,EAAE,CAAC,OAAO,CAAC;YACf,OAAO,EAAE,cAAc,SAAS,CAAC,IAAI,gEAAgE;SACtG,CAAC,CAAA;IACJ,CAAC;IAED,IAAI,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAChE,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,OAAC,CAAC,YAAY,CAAC,MAAM;YAC3B,IAAI,EAAE,CAAC,MAAM,CAAC;YACd,OAAO,EAAE,cAAc,SAAS,CAAC,IAAI,uDAAuD;SAC7F,CAAC,CAAA;IACJ,CAAC;AACH,CAAC,CAAC,CAAA;AAEF,oEAAoE;AAEpE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACU,QAAA,gBAAgB,GAAG,OAAC;KAC9B,MAAM,CAAC;IACN,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,SAAS,EAAE,uBAAe,CAAC,QAAQ,EAAE,EAAE,QAAQ;IAC/C,UAAU,EAAE,OAAC,CAAC,KAAK,CAAC,uBAAe,CAAC,CAAC,QAAQ,EAAE;CAChD,CAAC;KACD,WAAW,EAAE,CAAA,CAAC,+CAA+C;AAEhE,8DAA8D;AAE9D;;;GAGG;AACU,QAAA,oBAAoB,GAAG,OAAC;KAClC,MAAM,CAAC;IACN,SAAS,EAAE,uBAAe,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAChD,WAAW,EAAE,WAAW,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC9C,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACvC,QAAQ,EAAE,cAAc,CAAC,QAAQ,EAAE;IACnC,OAAO,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE;IACvD,QAAQ,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE;IACxD,MAAM,EAAE,oBAAY,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC1C,UAAU,EAAE,wBAAgB,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAClD,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjC,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC1C,UAAU,EAAE,OAAC,CAAC,KAAK,CAAC,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE;IACxD,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,SAAS,EAAE,WAAW,CAAC,QAAQ,EAAE;IACjC,SAAS,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE;IACpF,YAAY,EAAE,OAAC;SACZ,IAAI,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;SACzE,QAAQ,EAAE;IACb,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC5C,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC1C,MAAM,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAC/B,CAAC;KACD,WAAW,EAAE,CAAA,CAAC,6BAA6B;AAE9C;;;;;;;;;;GAUG;AACU,QAAA,gBAAgB,GAAG,OAAC;KAC9B,MAAM,CAAC;IACN,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACvC,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACxC,SAAS,EAAE,uBAAe,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAChD,UAAU,EAAE,OAAC,CAAC,KAAK,CAAC,uBAAe,CAAC,CAAC,QAAQ,EAAE;CAChD,CAAC;KACD,WAAW,EAAE,CAAA,CAAC,+DAA+D;AAShF,SAAS,YAAY,CAAC,KAAiB;IACrC,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;QAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAC,CAAC,CAAA;AACL,CAAC;AAED,sCAAsC;AACtC,SAAgB,kBAAkB,CAAC,KAAc;IAC/C,MAAM,CAAC,GAAG,wBAAgB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IAC3C,IAAI,CAAC,CAAC,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IACrC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAA;AACxD,CAAC;AAED,2DAA2D;AAC3D,SAAgB,sBAAsB,CAAC,KAAc;IACnD,MAAM,CAAC,GAAG,4BAAoB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IAC/C,IAAI,CAAC,CAAC,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IACrC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAA;AACxD,CAAC;AAED,oCAAoC;AACpC,SAAgB,iBAAiB,CAAC,SAAkB;IAClD,MAAM,CAAC,GAAG,uBAAe,CAAC,SAAS,CAAC,SAAS,CAAC,CAAA;IAC9C,IAAI,CAAC,CAAC,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IACrC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAA;AACxD,CAAC;AAED,kDAAkD;AAClD,SAAgB,kBAAkB,CAAC,KAAc;IAC/C,MAAM,CAAC,GAAG,wBAAgB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IAC3C,IAAI,CAAC,CAAC,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IACrC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAA;AACxD,CAAC","sourcesContent":["/**\n * BoardModel zod schema β€” μ˜μ† / patch 적용 boundary μ—μ„œ 데이터 무결성 검증.\n *\n * 핡심 μ •μ±…:\n * - things-scene 의 μ‹€μ œ drawer / setter κ΅¬ν˜„μ„ μ§„μ‹€λ‘œ λ΄„ (μΈν„°νŽ˜μ΄μŠ€ declaration X)\n * - Component schema λŠ” `.passthrough()` β€” μ•Œλ €μ§„ ν•„λ“œ 검증 + λ―Έμ§€ ν•„λ“œ 톡과\n * (μ‚¬μš©μž μ •μ˜ / 데이터 바인딩 / μ»΄ν¬λ„ŒνŠΈλ³„ specific 속성 ν—ˆμš©)\n * - κ·ΈλŸ¬λ‚˜ styling sub-objects (FillStyle / Shadow λ“±) λŠ” `.strict()` β€” unknown key\n * κ±°λΆ€. stale μž”μž¬ ν•„λ“œ (예: colorStops + colors 곡쑴) μžλ™ 발견.\n * - validation μ‹€νŒ¨ μ‹œ silent λˆ„λ½ X β€” caller κ°€ λͺ…μ‹œμ  μ—λŸ¬ / 둜그\n *\n * 적용 지점:\n * 1. server `UpdateBoard` mutation μ§„μž… β†’ DB μ˜μ† 직전 λ§ˆμ§€λ§‰ λ°©μ–΄μ„ \n * 2. server `BoardAIChat` mutation 응닡 직전 β€” AI κ°€ λ§Œλ“  patch 의 μ•ˆμ „μ„±\n * 3. (선택) client apply-patch 적용 ν›„ β€” λ©”λͺ¨λ¦¬ μƒνƒœ 무결성\n */\nimport { z } from 'zod'\n\n// ── Color ────────────────────────────────────────────────────────\n\n/**\n * 색상 β€” hex (#rgb / #rgba / #rrggbb / #rrggbbaa) / rgb / rgba / CSS named / 'transparent'.\n * μ •κ·œμ‹μ€ 느슨 β€” μ •ν™•ν•œ 검증은 normalizeColor κ°€, μ—¬κΈ°μ„  ν˜•μ‹λ§Œ.\n */\nconst ColorSchema = z.string().min(1).max(50)\n\n// ── FillStyle (state.fillStyle) ──────────────────────────────────\n//\n// μ‹€μ œ ν˜•μ‹ (drawer/fill.ts 검증):\n// - solid: 색상 string κ·ΈλŒ€λ‘œ\n// - gradient: { type: 'gradient', gradientType, colorStops: [{position, color}], rotation?, opacity? }\n// - pattern: { type: 'pattern', image, ...μ˜΅μ…˜λ“€ }\n// - none: undefined / null\n\nconst GradientStopSchema = z\n .object({\n position: z.number().min(0).max(1),\n color: ColorSchema\n })\n .strict()\n\nconst FillStyleGradientSchema = z\n .object({\n type: z.literal('gradient'),\n gradientType: z.enum(['linear', 'radial']).optional(),\n colorStops: z.array(GradientStopSchema).min(2),\n rotation: z.number().optional(),\n opacity: z.number().min(0).max(1).optional(),\n center: z\n .enum([\n 'center',\n 'left-top',\n 'left-bottom',\n 'right-top',\n 'right-bottom',\n 'left',\n 'right',\n 'top',\n 'bottom'\n ])\n .optional()\n })\n .strict()\n\nconst FillStylePatternSchema = z\n .object({\n type: z.literal('pattern'),\n image: z.union([z.string(), z.unknown()]),\n color: ColorSchema.optional(),\n fitPattern: z.boolean().optional(),\n noRepeat: z.boolean().optional(),\n align: z\n .enum([\n 'center',\n 'left-top',\n 'left-bottom',\n 'right-top',\n 'right-bottom',\n 'left',\n 'right',\n 'top',\n 'bottom'\n ])\n .optional(),\n offsetX: z.number().optional(),\n offsetY: z.number().optional(),\n width: z.number().optional(),\n height: z.number().optional(),\n opacity: z.number().min(0).max(1).optional(),\n guideOnly: z.boolean().optional()\n })\n .strict()\n\n/** 'none' type β€” fillStyle 제거 μ˜λ„ ν‘œν˜„ (state.fillStyle = {type:'none'} 도 ν—ˆμš©). */\nconst FillStyleNoneSchema = z.object({ type: z.literal('none') }).strict()\n\nexport const FillStyleSchema = z.union([\n ColorSchema, // solid: '#xxx'\n FillStyleGradientSchema,\n FillStylePatternSchema,\n FillStyleNoneSchema,\n z.null()\n])\n\n// ── Shadow (state.shadow) ────────────────────────────────────────\n//\n// Canonical (CSS box-shadow / Canvas API ν‘œμ€€): { color, offsetX, offsetY, blur }\n// Backwards-compat aliases (μ˜› 이름, drawer κ°€ fallback 으둜 읽음): left / top / blurSize.\n// μ˜μ†λœ μ˜› 데이터 ν˜Έν™˜μ„ μœ„ν•΄ 두 이름 λ‹€ schema 톡과 β€” μƒˆ μ½”λ“œλŠ” canonical μ‚¬μš©.\n\nexport const ShadowSchema = z\n .object({\n color: ColorSchema.optional(),\n // canonical (CSS / Canvas ν‘œμ€€)\n offsetX: z.number().optional(),\n offsetY: z.number().optional(),\n blur: z.number().min(0).optional(),\n // legacy aliases β€” μ˜› things-scene 데이터 ν˜Έν™˜μš©. μƒˆ μ½”λ“œλŠ” μœ„ canonical μ‚¬μš©.\n left: z.number().optional(),\n top: z.number().optional(),\n blurSize: z.number().min(0).optional()\n })\n .strict()\n\n// ── Material3D (state.material3d) ────────────────────────────────\n//\n// `things-scene/src/threed/material-3d.ts` 의 Material3D μΈν„°νŽ˜μ΄μŠ€μ™€ 일치.\n\nexport const Material3DSchema = z\n .object({\n preset: z\n .enum(['default', 'metal', 'glass', 'plastic', 'wood', 'ceramic', 'rubber', 'custom'])\n .optional(),\n metalness: z.number().min(0).max(1).optional(),\n roughness: z.number().min(0).max(1).optional(),\n emissive: ColorSchema.optional(),\n emissiveIntensity: z.number().min(0).max(5).optional(),\n opacity: z.number().min(0).max(1).optional(),\n envMapIntensity: z.number().min(0).max(3).optional(),\n side: z.enum(['double', 'front', 'back']).optional(),\n castShadow: z.boolean().optional(),\n receiveShadow: z.boolean().optional()\n })\n .strict()\n\n// ── DataMapping (state.mappings) ─────────────────────────────────\n//\n// 데이터 바인딩 β€” `interfaces/data.ts` Mapping. rule / target / accessor / converter / param.\n\nexport const MappingSchema = z\n .object({\n rule: z.string().optional(),\n target: z.string(),\n property: z.string().optional(),\n accessor: z.string().optional(),\n source: z.unknown().optional(),\n converter: z.union([z.string(), z.unknown()]).optional(),\n param: z.unknown().optional()\n })\n .passthrough() // mapping 의 자유 ν•„λ“œ (converter args λ“±) ν—ˆμš©\n\n// ── Component (one component in board.components) ────────────────\n//\n// 핡심 styling ν•„λ“œλŠ” λͺ…μ‹œ 검증, λ‚˜λ¨Έμ§€λŠ” passthrough β€” μ»΄ν¬λ„ŒνŠΈλ³„ specific 속성 자유.\n\nconst LineDashSchema = z.enum([\n 'solid',\n 'round-dot',\n 'square-dot',\n 'dash',\n 'dash-dot',\n 'long-dash',\n 'long-dash-dot',\n 'long-dash-dot-dot'\n])\n\n/**\n * μ»΄ν¬λ„ŒνŠΈκ°€ *μ–΄λ–€ ν˜•νƒœλ“ * μœ νš¨ν•œ geometry λ₯Ό κ°€μ‘ŒλŠ”μ§€ 확인.\n *\n * things-scene 의 μ»΄ν¬λ„ŒνŠΈ type λ³„λ‘œ geometry ν•„λ“œκ°€ 닀양함 (rect λŠ” box,\n * line 은 endpoints, polyline 은 vertices/path λ“±). type β†’ ν•„λ“œ 맀핑을 strict\n * ν•˜κ²Œ enumerate ν•˜λ©΄ μ‹ κ·œ type 등둝 μ‹œ schema 도 λ™μ‹œ μ—…λ°μ΄νŠΈ ν•„μš” β†’ forward\n * compatibility 손해.\n *\n * λŒ€μ‹  \"μ΅œμ†Œν•œ μ–΄λ–€ geometry ν˜•μ‹μ΄λΌλ„ μžˆμ–΄μ•Ό ν•œλ‹€\" λŠ” μ•½ν•œ 보μž₯ β€” 빈 μ»΄ν¬λ„ŒνŠΈ\n * (`{type:'rect'}` 만) λ˜λŠ” type 에 μ•ˆ λ§žλŠ” geometry ꡬ성 (`{type:'rect', cx:10, cy:20}`)\n * 같은 λͺ…λ°±ν•œ 였λ₯˜λ§Œ 차단. μ‹€μ œ λ Œλ”λ§ 정합성은 things-scene μžμ²΄κ°€ 검증.\n */\nfunction componentHasValidGeometry(component: any): boolean {\n if (!component || typeof component !== 'object') return false\n\n // Box geometry β€” left/top λ‘˜ λ‹€ μžˆμ–΄μ•Ό (width/height λŠ” optional)\n const hasBox = typeof component.left === 'number' && typeof component.top === 'number'\n\n // Circle/ellipse geometry β€” cx/cy λ‘˜ λ‹€ μžˆμ–΄μ•Ό\n const hasCircle = typeof component.cx === 'number' && typeof component.cy === 'number'\n\n // Line endpoints\n const hasLineEndpoints =\n typeof component.x1 === 'number' &&\n typeof component.y1 === 'number' &&\n typeof component.x2 === 'number' &&\n typeof component.y2 === 'number'\n\n // Path / points / vertices 계열\n const hasPath = typeof component.path === 'string' || Array.isArray(component.path)\n const hasPoints = Array.isArray(component.points) && component.points.length > 0\n const hasVertices =\n (Array.isArray(component.vertices) && component.vertices.length > 0) ||\n (Array.isArray(component.verticles) && component.verticles.length > 0) // legacy μ˜€νƒ€\n\n // line connector β€” startPoint / endPoint ν˜•νƒœ (anchor 기반 line)\n const hasConnector =\n component.from && typeof component.from === 'object' &&\n component.to && typeof component.to === 'object'\n\n return hasBox || hasCircle || hasLineEndpoints || hasPath || hasPoints || hasVertices || hasConnector\n}\n\n/** type 이 'gauge' λ˜λŠ” 'gauge-*' λ©΄ value ν•„λ“œ ν•„μš” (선택: min/max). */\nfunction isGaugeType(type: string): boolean {\n return type === 'gauge' || type.startsWith('gauge-')\n}\n\n/** type 이 'chart' λ˜λŠ” 'chart-*' λ©΄ data ν•„λ“œ ν•„μš”. */\nfunction isChartType(type: string): boolean {\n return type === 'chart' || type.startsWith('chart-')\n}\n\nconst ComponentBaseSchema = z\n .object({\n type: z.string().min(1),\n refid: z.number().int().optional(), // μ‹ κ·œ μ»΄ν¬λ„ŒνŠΈ μΆ”κ°€ μ‹œμ μ—” λ―Έν• λ‹Ή κ°€λŠ₯\n // μœ„μΉ˜ / 크기 β€” μ»΄ν¬λ„ŒνŠΈ νƒ€μž…μ— 따라 닀름. λ³Έ schema λŠ” optional 둜 두고\n // refinement κ°€ *μ–΄λ–€ ν˜•νƒœλ“ * μžˆλŠ”μ§€λ§Œ 검사.\n left: z.number().optional(),\n top: z.number().optional(),\n width: z.number().optional(),\n height: z.number().optional(),\n cx: z.number().optional(),\n cy: z.number().optional(),\n rx: z.number().optional(),\n ry: z.number().optional(),\n radius: z.number().optional(),\n x1: z.number().optional(),\n y1: z.number().optional(),\n x2: z.number().optional(),\n y2: z.number().optional(),\n rotation: z.number().optional(),\n // styling\n fillStyle: FillStyleSchema.optional(),\n strokeStyle: ColorSchema.optional(), // top-level string\n lineWidth: z.number().min(0).optional(),\n lineDash: LineDashSchema.optional(),\n lineCap: z.enum(['butt', 'round', 'square']).optional(),\n lineJoin: z.enum(['miter', 'round', 'bevel']).optional(),\n shadow: ShadowSchema.optional(),\n material3d: Material3DSchema.optional(),\n // text / font β€” top-level\n text: z.string().optional(),\n fontFamily: z.string().optional(),\n fontSize: z.number().positive().optional(),\n fontWeight: z.union([z.string(), z.number()]).optional(),\n fontStyle: z.string().optional(),\n fontColor: ColorSchema.optional(),\n textAlign: z.enum(['left', 'center', 'right', 'start', 'end', 'justify']).optional(),\n textBaseline: z\n .enum(['top', 'middle', 'bottom', 'hanging', 'alphabetic', 'ideographic'])\n .optional(),\n lineHeight: z.number().positive().optional(),\n // visibility / opacity\n alpha: z.number().min(0).max(1).optional(),\n hidden: z.boolean().optional(),\n // misc β€” 검증 μ•ˆ 함\n id: z.string().optional(),\n class: z.string().optional(),\n tag: z.string().optional(),\n // 데이터 바인딩\n mappings: z.array(MappingSchema).optional()\n })\n .passthrough() // μ»΄ν¬λ„ŒνŠΈλ³„ specific 속성 (vehicle wheels, gauge needle, points/path λ“±) ν—ˆμš©\n\n/**\n * Component schema β€” base μœ„μ— type-aware refinement 두 κ°€μ§€:\n *\n * 1. λͺ¨λ“  μ»΄ν¬λ„ŒνŠΈλŠ” *μ–΄λ–€ ν˜•νƒœλ“ * μœ νš¨ν•œ geometry κ°€ μžˆμ–΄μ•Ό\n * (left+top / cx+cy / x1y1x2y2 / path / points / vertices / from+to)\n * 2. gauge-* type 은 value ν•„μš”, chart-* type 은 data ν•„μš” (soft check β€”\n * 일뢀 prefab 이 미리 μ±„μ›Œμ£ΌλŠ” μΌ€μ΄μŠ€λ„ μžˆμ–΄ strict 거뢀보닀 미리 작기 λͺ©μ )\n *\n * 이 refinement 듀이 LLM 의 잘λͺ»λœ geometry (rect 에 cx/cy만, line 에 left/top만)\n * λ₯Ό μ˜μ† 직전 차단.\n */\nexport const ComponentSchema = ComponentBaseSchema.superRefine((component, ctx) => {\n if (!componentHasValidGeometry(component)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `Component '${component.type}' has no valid geometry β€” provide left+top (box), cx+cy (circle/ellipse), x1+y1+x2+y2 (line), path / points / vertices, or from+to (connector).`\n })\n }\n\n if (isGaugeType(component.type) && component.value === undefined) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['value'],\n message: `Component '${component.type}' requires 'value' (current sensor reading or display number).`\n })\n }\n\n if (isChartType(component.type) && component.data === undefined) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['data'],\n message: `Component '${component.type}' requires 'data' (chart datapoints array or object).`\n })\n }\n})\n\n// ── BoardModel (top-level) ───────────────────────────────────────\n\n/**\n * BoardModel β€” things-scene 의 model-layer (λ³΄λ“œ root).\n *\n * μΈμ‹λ˜λŠ” root 속성 (model-layer 의 NATURE.properties + μ‹€μ œ μ½”λ“œμ—μ„œ state.* 둜\n * μ°Έμ‘°λ˜λŠ” ν‚€):\n * β€’ Layout: width, height, fitMode, translate, scale\n * β€’ 2D λ°°κ²½: fillStyle\n * β€’ 3D Sky: sky, skyColor\n * β€’ 3D Tone: exposure\n * β€’ 3D Hemisphere: hemiSkyColor, hemiGroundColor, hemiIntensity\n * β€’ 3D Directional light: dirLightEnabled, dirLightColor, dirLightIntensity,\n * dirLightFollowCamera, dirLightAzimuth, dirLightElevation\n * β€’ 3D Shadow: dirShadowEnabled, dirShadowMapSize, dirShadowBias\n * β€’ 3D Camera: cameraX, cameraY, cameraZ, cameraAutoPlay, autoRotate, initialCameraView\n * β€’ 식별: id, refid, type\n *\n * ⚠ `name` 은 things-scene model-layer κ°€ 인식 μ•ˆ 함 β€” Board *μ—”ν‹°ν‹°* 의 μ»¬λŸΌμ΄μ§€\n * scene MODEL 의 ν•„λ“œκ°€ μ•„λ‹˜. AI κ°€ modifyScene 둜 name 을 보내도 죽은 ν•„λ“œ. 검증\n * μ‹œμ—λ„ 제거 / λ¬΄μ‹œ μ •μ±….\n *\n * `passthrough` 인 이유 β€” 3D 광원/카메라/sky λ“± ν‚€κ°€ λ„ˆλ¬΄ λ§Žμ•„ strict λ‘œλŠ” νšŒκ·€ μœ„ν—˜\n * 큼. λŒ€μ‹  νŒ¨ν‚€μ§€ μ™ΈλΆ€ (operato λ“±) κ°€ μž„μ˜ root ν‚€ μΆ”κ°€ν•˜λŠ” κ±Έ 막지 μ•ŠμŒ.\n */\nexport const BoardModelSchema = z\n .object({\n width: z.number().positive(),\n height: z.number().positive(),\n fillStyle: FillStyleSchema.optional(), // λ³΄λ“œ λ°°κ²½\n components: z.array(ComponentSchema).optional()\n })\n .passthrough() // λ³΄λ“œ 레벨 자유 ν•„λ“œ (3D atmosphere / sky / camera λ“±)\n\n// ── Patch op schemas β€” modify op 의 patch κ°€ invalid 이면 κ±°λΆ€ ───\n\n/**\n * modify op 의 patch β€” μ»΄ν¬λ„ŒνŠΈ partial. λͺ¨λ“  ν•„λ“œ optional. styling sub-object\n * 만 strict ν•˜κ²Œ 검증해 stale key μž”μ‘΄ 차단.\n */\nexport const ComponentPatchSchema = z\n .object({\n fillStyle: FillStyleSchema.nullable().optional(),\n strokeStyle: ColorSchema.nullable().optional(),\n lineWidth: z.number().min(0).optional(),\n lineDash: LineDashSchema.optional(),\n lineCap: z.enum(['butt', 'round', 'square']).optional(),\n lineJoin: z.enum(['miter', 'round', 'bevel']).optional(),\n shadow: ShadowSchema.nullable().optional(),\n material3d: Material3DSchema.nullable().optional(),\n text: z.string().optional(),\n fontFamily: z.string().optional(),\n fontSize: z.number().positive().optional(),\n fontWeight: z.union([z.string(), z.number()]).optional(),\n fontStyle: z.string().optional(),\n fontColor: ColorSchema.optional(),\n textAlign: z.enum(['left', 'center', 'right', 'start', 'end', 'justify']).optional(),\n textBaseline: z\n .enum(['top', 'middle', 'bottom', 'hanging', 'alphabetic', 'ideographic'])\n .optional(),\n lineHeight: z.number().positive().optional(),\n alpha: z.number().min(0).max(1).optional(),\n hidden: z.boolean().optional()\n })\n .passthrough() // μœ„μΉ˜ / 크기 / μ»΄ν¬λ„ŒνŠΈ specific 톡과\n\n/**\n * modifyScene op 의 patch β€” λ³΄λ“œ root partial.\n *\n * λͺ¨λ“  root ν•„λ“œ optional (BoardModelSchema 와 달리 width/height 도 optional).\n * passthrough 둜 μ•Œλ €μ§€μ§€ μ•Šμ€ root ν‚€ (3D atmosphere, sky, camera λ“±) 톡과.\n * λ‹€λ§Œ *μ•Œλ €μ§„ nested object* (fillStyle λ“±) λŠ” strict schema μž¬μ‚¬μš© β€” κ·Έ μ•ˆμ—\n * unknown key κ°€ λ“€μ–΄μ˜€λ©΄ κ±°λΆ€ (예: fillStyle μ•ˆ gradientStops 같은 stale 이름).\n *\n * AI κ°€ setFill λ“±μ—μ„œ ν•™μŠ΅ν•œ vocabulary λ₯Ό modifyScene 둜 echo μ‹œν‚¬ λ•Œ stale\n * key κ°€ fillStyle / shadow 같은 sub-object μ•ˆμ— 끼면 λ³Έ schema κ°€ catch.\n */\nexport const BoardPatchSchema = z\n .object({\n width: z.number().positive().optional(),\n height: z.number().positive().optional(),\n fillStyle: FillStyleSchema.nullable().optional(),\n components: z.array(ComponentSchema).optional()\n })\n .passthrough() // root 의 자유 ν•„λ“œ (sky / exposure / hemi*/ dirLight* / camera* λ“±)\n\n// ── 검증 헬퍼 ────────────────────────────────────────────────────\n\nexport interface ValidationResult {\n valid: boolean\n errors?: Array<{ path: string; message: string }>\n}\n\nfunction formatIssues(error: z.ZodError): Array<{ path: string; message: string }> {\n return error.issues.map(issue => ({\n path: issue.path.join('.'),\n message: issue.message\n }))\n}\n\n/** BoardModel 전체 검증. μ˜μ† / λ‘œλ“œ μ‹œ μ‚¬μš©. */\nexport function validateBoardModel(model: unknown): ValidationResult {\n const r = BoardModelSchema.safeParse(model)\n if (r.success) return { valid: true }\n return { valid: false, errors: formatIssues(r.error) }\n}\n\n/** modify patch 검증. AI κ°€ λ§Œλ“  patch κ°€ styling spec 에 λ§žλŠ”μ§€. */\nexport function validateComponentPatch(patch: unknown): ValidationResult {\n const r = ComponentPatchSchema.safeParse(patch)\n if (r.success) return { valid: true }\n return { valid: false, errors: formatIssues(r.error) }\n}\n\n/** ν•œ μ»΄ν¬λ„ŒνŠΈ 검증. addComponent μ‹œ μ‚¬μš©. */\nexport function validateComponent(component: unknown): ValidationResult {\n const r = ComponentSchema.safeParse(component)\n if (r.success) return { valid: true }\n return { valid: false, errors: formatIssues(r.error) }\n}\n\n/** modifyScene op 의 patch 검증. λ³΄λ“œ root partial. */\nexport function validateBoardPatch(patch: unknown): ValidationResult {\n const r = BoardPatchSchema.safeParse(patch)\n if (r.success) return { valid: true }\n return { valid: false, errors: formatIssues(r.error) }\n}\n"]}
@@ -120,7 +120,7 @@ function validateWriteToolCall(tc, components = []) {
120
120
  }
121
121
  return { valid: true };
122
122
  }
123
- case 'modifyBoard': {
123
+ case 'modifyScene': {
124
124
  // λ³΄λ“œ root partial 검증 β€” fillStyle/shadow 같은 nested 의 strict schema κ°€
125
125
  // unknown key (e.g. fillStyle.gradientStops 같은 stale 이름) λ₯Ό κ±°λΆ€.
126
126
  // 이게 λ§ˆμ§€λ§‰ λ°©μ–΄μ„  β€” λ‹€λ₯Έ styling tool μ—μ„œ ν•™μŠ΅λœ vocabulary κ°€ leak 되면 catch.
@@ -132,7 +132,7 @@ function validateWriteToolCall(tc, components = []) {
132
132
  return {
133
133
  valid: false,
134
134
  errors: formatValidationErrors(v),
135
- suggestion: modifyBoardSuggestion()
135
+ suggestion: modifySceneSuggestion()
136
136
  };
137
137
  }
138
138
  return { valid: true };
@@ -147,7 +147,7 @@ function validateWriteToolCall(tc, components = []) {
147
147
  return {
148
148
  valid: false,
149
149
  errors: formatValidationErrors(v),
150
- suggestion: modifyBoardSuggestion()
150
+ suggestion: modifySceneSuggestion()
151
151
  };
152
152
  }
153
153
  return { valid: true };
@@ -157,8 +157,8 @@ function validateWriteToolCall(tc, components = []) {
157
157
  return { valid: true };
158
158
  }
159
159
  }
160
- function modifyBoardSuggestion() {
161
- return ('modifyBoard 의 patch λŠ” things-scene 의 model-layer κ°€ μΈμ‹ν•˜λŠ” root key 만 μ‚¬μš©. ' +
160
+ function modifySceneSuggestion() {
161
+ return ('modifyScene 의 patch λŠ” things-scene 의 model-layer κ°€ μΈμ‹ν•˜λŠ” root key 만 μ‚¬μš©. ' +
162
162
  'fillStyle / strokeStyle / shadow 같은 nested object 의 μ•ˆμͺ½ ν•„λ“œλŠ” canonical 이름 (colorStops λ“±) μ •ν™•νžˆ μ‚¬μš©. ' +
163
163
  'rect λ“± μžμ‹ μ»΄ν¬λ„ŒνŠΈ 변경은 modifyComponentByRefid 둜 뢄리.');
164
164
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tool-validation.js","sourceRoot":"","sources":["../../../server/service/validation/tool-validation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Write tool 의 사전 검증 β€” agentic loop 의 retry λ©”μ»€λ‹ˆμ¦˜.\n *\n * 흐름:\n * 1. LLM 이 write tool 호좜\n * 2. λ³Έ ν•¨μˆ˜κ°€ args 검증 (styling tool 의 build* ν•¨μˆ˜ λ˜λŠ” schema 직접)\n * 3. μ‹€νŒ¨ μ‹œ errors λ°˜ν™˜ β†’ caller κ°€ tool_result 에 error λ©”μ‹œμ§€ λ‹΄μ•„ LLM ν•œν…Œ\n * 4. LLM λ‹€μŒ turn μ—μ„œ μ •μ • ν›„ 재호좜 (자기 회볡)\n *\n * 이게 silent 데이터 손상을 λ§‰λŠ” λ§ˆμ§€λ§‰ λŠ₯동 λ°©μ–΄μ„ . 검증 ν†΅κ³Όν•œ args 만 λˆ„μ \n * β†’ patch 둜 λ³€ν™˜λ˜λ―€λ‘œ κΉ¨μ§„ 데이터가 μ˜μ†κΉŒμ§€ λͺ» 감.\n */\nimport type { AIToolCall } from '@things-factory/ai-client-base'\nimport {\n validateComponentPatch,\n validateComponent,\n validateBoardPatch,\n type ValidationResult\n} from './board-model-schema'\nimport { buildSetFillOps } from '../styling/fill-tools'\nimport { buildSetStrokeOps } from '../styling/stroke-tools'\nimport { buildSetTextOps, buildSetFontOps } from '../styling/text-tools'\nimport {\n buildSetShadowOps,\n buildSetOpacityOps,\n buildSetVisibleOps\n} from '../styling/effect-tools'\nimport {\n buildSetMaterialOps,\n buildSetShadowPropertiesOps\n} from '../styling/material-tools'\nimport { buildClearStyleOps } from '../styling/clear-tools'\nimport { buildCopyStyleOps } from '../styling/copy-tools'\n\nexport interface ToolValidationResult {\n /** 검증 톡과 β€” λˆ„μ  μ§„ν–‰ κ°€λŠ₯ */\n valid: boolean\n /** μ‹€νŒ¨ μ‹œ LLM ν•œν…Œ 보일 μ—λŸ¬ λ©”μ‹œμ§€ (자기 회볡 정보 포함) */\n errors?: string[]\n /** LLM ν•œν…Œ μ •μ • κ°€μ΄λ“œ β€” κ°€λŠ₯ν•œ 경우 μ–΄λ–€ ν˜•μ‹μ΄ λ§žλŠ”μ§€ */\n suggestion?: string\n}\n\n/**\n * write tool 의 args 사전 검증.\n *\n * μ •μ±…:\n * - styling tool: ν•΄λ‹Ή build* ν•¨μˆ˜κ°€ errors λ°˜ν™˜ β€” κ·ΈλŒ€λ‘œ LLM ν•œν…Œ\n * - modifyComponentByRefid: ComponentPatchSchema 둜 검증 (raw fallback μ˜μ—­)\n * - addComponent: ComponentSchema 둜 검증\n * - κ·Έ μ™Έ: κΈ°μ‘΄ dispatcher 의 자체 검증에 μœ„μž„ (μž¬μ‹œλ„ 보μž₯ X)\n */\nexport function validateWriteToolCall(\n tc: AIToolCall,\n components: ReadonlyArray<any> = []\n): ToolValidationResult {\n const args = tc.arguments ?? {}\n\n // Styling tool λ“€ β€” 각 build ν•¨μˆ˜κ°€ detailed errors λ°˜ν™˜\n switch (tc.name) {\n case 'setFill': {\n const r = buildSetFillOps(args as any)\n if (r.errors.length > 0)\n return {\n valid: false,\n errors: r.errors,\n suggestion: setFillSuggestion()\n }\n return { valid: true }\n }\n case 'setStroke': {\n const r = buildSetStrokeOps(args as any)\n if (r.errors.length > 0) return { valid: false, errors: r.errors }\n return { valid: true }\n }\n case 'setText': {\n const r = buildSetTextOps(args as any)\n if (r.errors.length > 0) return { valid: false, errors: r.errors }\n return { valid: true }\n }\n case 'setFont': {\n const r = buildSetFontOps(args as any)\n if (r.errors.length > 0) return { valid: false, errors: r.errors }\n return { valid: true }\n }\n case 'setShadow': {\n const r = buildSetShadowOps(args as any)\n if (r.errors.length > 0) return { valid: false, errors: r.errors }\n return { valid: true }\n }\n case 'setOpacity': {\n const r = buildSetOpacityOps(args as any)\n if (r.errors.length > 0) return { valid: false, errors: r.errors }\n return { valid: true }\n }\n case 'setVisible': {\n const r = buildSetVisibleOps(args as any)\n if (r.errors.length > 0) return { valid: false, errors: r.errors }\n return { valid: true }\n }\n case 'setMaterial': {\n const r = buildSetMaterialOps(args as any)\n if (r.errors.length > 0) return { valid: false, errors: r.errors }\n return { valid: true }\n }\n case 'setShadowProperties': {\n const r = buildSetShadowPropertiesOps(args as any)\n if (r.errors.length > 0) return { valid: false, errors: r.errors }\n return { valid: true }\n }\n case 'clearStyle': {\n const r = buildClearStyleOps(args as any)\n if (r.errors.length > 0) return { valid: false, errors: r.errors }\n return { valid: true }\n }\n case 'copyStyle': {\n const r = buildCopyStyleOps(args as any, components)\n if (r.errors.length > 0) return { valid: false, errors: r.errors }\n return { valid: true }\n }\n\n // raw fallback β€” schema 검증\n case 'modifyComponentByRefid': {\n if (typeof args.refid !== 'number') {\n return { valid: false, errors: ['refid (number) is required'] }\n }\n if (!args.patch || typeof args.patch !== 'object') {\n return { valid: false, errors: ['patch (object) is required'] }\n }\n const v = validateComponentPatch(args.patch)\n if (!v.valid) {\n return {\n valid: false,\n errors: formatValidationErrors(v),\n suggestion: modifyPatchSuggestion()\n }\n }\n return { valid: true }\n }\n case 'addComponent': {\n const v = validateComponent(args)\n if (!v.valid) {\n return {\n valid: false,\n errors: formatValidationErrors(v)\n }\n }\n return { valid: true }\n }\n case 'modifyBoard': {\n // λ³΄λ“œ root partial 검증 β€” fillStyle/shadow 같은 nested 의 strict schema κ°€\n // unknown key (e.g. fillStyle.gradientStops 같은 stale 이름) λ₯Ό κ±°λΆ€.\n // 이게 λ§ˆμ§€λ§‰ λ°©μ–΄μ„  β€” λ‹€λ₯Έ styling tool μ—μ„œ ν•™μŠ΅λœ vocabulary κ°€ leak 되면 catch.\n if (!args.patch || typeof args.patch !== 'object' || Array.isArray(args.patch)) {\n return { valid: false, errors: ['patch (object) is required'] }\n }\n const v = validateBoardPatch(args.patch)\n if (!v.valid) {\n return {\n valid: false,\n errors: formatValidationErrors(v),\n suggestion: modifyBoardSuggestion()\n }\n }\n return { valid: true }\n }\n case 'replaceBoard': {\n // λ³΄λ“œ 톡째 ꡐ체 β€” μ˜μ† 직전 κ°€μž₯ κ°•λ ₯ν•œ 검증 ν•„μš”. components κΉŒμ§€ ν•œ λ²ˆμ—\n // 검사. μžμ‹ μ»΄ν¬λ„ŒνŠΈμ˜ fillStyle.gradientStops 같은 stale 킀도 μ¦‰μ‹œ catch.\n // (BoardPatchSchema λŠ” component 도 array 둜 ν†΅κ³Όμ‹œν‚€λ˜ ComponentSchema κ°€\n // strict styling sub-objects 둜 stale 차단.)\n const v = validateBoardPatch(args)\n if (!v.valid) {\n return {\n valid: false,\n errors: formatValidationErrors(v),\n suggestion: modifyBoardSuggestion()\n }\n }\n return { valid: true }\n }\n\n // κ·Έ μ™Έ β€” κΈ°μ‘΄ dispatcher 검증에 μœ„μž„\n default:\n return { valid: true }\n }\n}\n\nfunction modifyBoardSuggestion(): string {\n return (\n 'modifyBoard 의 patch λŠ” things-scene 의 model-layer κ°€ μΈμ‹ν•˜λŠ” root key 만 μ‚¬μš©. ' +\n 'fillStyle / strokeStyle / shadow 같은 nested object 의 μ•ˆμͺ½ ν•„λ“œλŠ” canonical 이름 (colorStops λ“±) μ •ν™•νžˆ μ‚¬μš©. ' +\n 'rect λ“± μžμ‹ μ»΄ν¬λ„ŒνŠΈ 변경은 modifyComponentByRefid 둜 뢄리.'\n )\n}\n\nfunction formatValidationErrors(v: ValidationResult): string[] {\n if (v.valid || !v.errors) return []\n return v.errors.map(e => `${e.path || '<root>'}: ${e.message}`)\n}\n\n/**\n * setFill μ •μ • κ°€μ΄λ“œ β€” 자주 μ‹€μˆ˜ν•˜λŠ” νŒ¨ν„΄ + μ˜¬λ°”λ₯Έ μ˜ˆμ‹œ.\n */\nfunction setFillSuggestion(): string {\n return [\n 'Valid setFill patterns:',\n \" - Solid: { refids:[35], mode:'solid', color:'#ff0000' }\",\n \" - Gradient: { refids:[35], mode:'gradient', gradientType:'linear', \" +\n \"colorStops:[{position:0,color:'#ff0000'},{position:1,color:'#0000ff'}], rotation:90 }\",\n \" - Pattern: { refids:[35], mode:'pattern', image:'url' }\",\n \" - Partial color update: { refids:[35], color:'green' } // mode λ―Έμ§€μ •\",\n 'Notes:',\n ' - rotation 은 degrees 둜 μž…λ ₯ (μ„œλ²„κ°€ λΌλ””μ•ˆμœΌλ‘œ λ³€ν™˜ν•΄ fillStyle.rotation 에 μ €μž₯)',\n ' - colorStops λŠ” { position: 0..1, color: hex/rgb/named } 객체 λ°°μ—΄',\n ' - μ˜› 이름 (colors / stops / gradientStops) μ‚¬μš© κΈˆμ§€ β€” engine canonical 은 colorStops'\n ].join('\\n')\n}\n\nfunction modifyPatchSuggestion(): string {\n return [\n 'modifyComponentByRefid is the raw fallback β€” prefer specific styling tools (setFill/setStroke/setText/setFont/setShadow/setMaterial/setOpacity/setVisible) for any styling change.',\n 'If you must use raw modify:',\n ' - fillStyle string (solid) or {type:\"gradient\",gradientType,colorStops:[{position,color}]} or {type:\"pattern\",image,...}',\n ' - shadow {color, offsetX, offsetY, blur} (CSS box-shadow / Canvas ν‘œμ€€ 이름)',\n ' - lineDash STRING preset only (NOT number array)'\n ].join('\\n')\n}\n"]}
1
+ {"version":3,"file":"tool-validation.js","sourceRoot":"","sources":["../../../server/service/validation/tool-validation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Write tool 의 사전 검증 β€” agentic loop 의 retry λ©”μ»€λ‹ˆμ¦˜.\n *\n * 흐름:\n * 1. LLM 이 write tool 호좜\n * 2. λ³Έ ν•¨μˆ˜κ°€ args 검증 (styling tool 의 build* ν•¨μˆ˜ λ˜λŠ” schema 직접)\n * 3. μ‹€νŒ¨ μ‹œ errors λ°˜ν™˜ β†’ caller κ°€ tool_result 에 error λ©”μ‹œμ§€ λ‹΄μ•„ LLM ν•œν…Œ\n * 4. LLM λ‹€μŒ turn μ—μ„œ μ •μ • ν›„ 재호좜 (자기 회볡)\n *\n * 이게 silent 데이터 손상을 λ§‰λŠ” λ§ˆμ§€λ§‰ λŠ₯동 λ°©μ–΄μ„ . 검증 ν†΅κ³Όν•œ args 만 λˆ„μ \n * β†’ patch 둜 λ³€ν™˜λ˜λ―€λ‘œ κΉ¨μ§„ 데이터가 μ˜μ†κΉŒμ§€ λͺ» 감.\n */\nimport type { AIToolCall } from '@things-factory/ai-client-base'\nimport {\n validateComponentPatch,\n validateComponent,\n validateBoardPatch,\n type ValidationResult\n} from './board-model-schema'\nimport { buildSetFillOps } from '../styling/fill-tools'\nimport { buildSetStrokeOps } from '../styling/stroke-tools'\nimport { buildSetTextOps, buildSetFontOps } from '../styling/text-tools'\nimport {\n buildSetShadowOps,\n buildSetOpacityOps,\n buildSetVisibleOps\n} from '../styling/effect-tools'\nimport {\n buildSetMaterialOps,\n buildSetShadowPropertiesOps\n} from '../styling/material-tools'\nimport { buildClearStyleOps } from '../styling/clear-tools'\nimport { buildCopyStyleOps } from '../styling/copy-tools'\n\nexport interface ToolValidationResult {\n /** 검증 톡과 β€” λˆ„μ  μ§„ν–‰ κ°€λŠ₯ */\n valid: boolean\n /** μ‹€νŒ¨ μ‹œ LLM ν•œν…Œ 보일 μ—λŸ¬ λ©”μ‹œμ§€ (자기 회볡 정보 포함) */\n errors?: string[]\n /** LLM ν•œν…Œ μ •μ • κ°€μ΄λ“œ β€” κ°€λŠ₯ν•œ 경우 μ–΄λ–€ ν˜•μ‹μ΄ λ§žλŠ”μ§€ */\n suggestion?: string\n}\n\n/**\n * write tool 의 args 사전 검증.\n *\n * μ •μ±…:\n * - styling tool: ν•΄λ‹Ή build* ν•¨μˆ˜κ°€ errors λ°˜ν™˜ β€” κ·ΈλŒ€λ‘œ LLM ν•œν…Œ\n * - modifyComponentByRefid: ComponentPatchSchema 둜 검증 (raw fallback μ˜μ—­)\n * - addComponent: ComponentSchema 둜 검증\n * - κ·Έ μ™Έ: κΈ°μ‘΄ dispatcher 의 자체 검증에 μœ„μž„ (μž¬μ‹œλ„ 보μž₯ X)\n */\nexport function validateWriteToolCall(\n tc: AIToolCall,\n components: ReadonlyArray<any> = []\n): ToolValidationResult {\n const args = tc.arguments ?? {}\n\n // Styling tool λ“€ β€” 각 build ν•¨μˆ˜κ°€ detailed errors λ°˜ν™˜\n switch (tc.name) {\n case 'setFill': {\n const r = buildSetFillOps(args as any)\n if (r.errors.length > 0)\n return {\n valid: false,\n errors: r.errors,\n suggestion: setFillSuggestion()\n }\n return { valid: true }\n }\n case 'setStroke': {\n const r = buildSetStrokeOps(args as any)\n if (r.errors.length > 0) return { valid: false, errors: r.errors }\n return { valid: true }\n }\n case 'setText': {\n const r = buildSetTextOps(args as any)\n if (r.errors.length > 0) return { valid: false, errors: r.errors }\n return { valid: true }\n }\n case 'setFont': {\n const r = buildSetFontOps(args as any)\n if (r.errors.length > 0) return { valid: false, errors: r.errors }\n return { valid: true }\n }\n case 'setShadow': {\n const r = buildSetShadowOps(args as any)\n if (r.errors.length > 0) return { valid: false, errors: r.errors }\n return { valid: true }\n }\n case 'setOpacity': {\n const r = buildSetOpacityOps(args as any)\n if (r.errors.length > 0) return { valid: false, errors: r.errors }\n return { valid: true }\n }\n case 'setVisible': {\n const r = buildSetVisibleOps(args as any)\n if (r.errors.length > 0) return { valid: false, errors: r.errors }\n return { valid: true }\n }\n case 'setMaterial': {\n const r = buildSetMaterialOps(args as any)\n if (r.errors.length > 0) return { valid: false, errors: r.errors }\n return { valid: true }\n }\n case 'setShadowProperties': {\n const r = buildSetShadowPropertiesOps(args as any)\n if (r.errors.length > 0) return { valid: false, errors: r.errors }\n return { valid: true }\n }\n case 'clearStyle': {\n const r = buildClearStyleOps(args as any)\n if (r.errors.length > 0) return { valid: false, errors: r.errors }\n return { valid: true }\n }\n case 'copyStyle': {\n const r = buildCopyStyleOps(args as any, components)\n if (r.errors.length > 0) return { valid: false, errors: r.errors }\n return { valid: true }\n }\n\n // raw fallback β€” schema 검증\n case 'modifyComponentByRefid': {\n if (typeof args.refid !== 'number') {\n return { valid: false, errors: ['refid (number) is required'] }\n }\n if (!args.patch || typeof args.patch !== 'object') {\n return { valid: false, errors: ['patch (object) is required'] }\n }\n const v = validateComponentPatch(args.patch)\n if (!v.valid) {\n return {\n valid: false,\n errors: formatValidationErrors(v),\n suggestion: modifyPatchSuggestion()\n }\n }\n return { valid: true }\n }\n case 'addComponent': {\n const v = validateComponent(args)\n if (!v.valid) {\n return {\n valid: false,\n errors: formatValidationErrors(v)\n }\n }\n return { valid: true }\n }\n case 'modifyScene': {\n // λ³΄λ“œ root partial 검증 β€” fillStyle/shadow 같은 nested 의 strict schema κ°€\n // unknown key (e.g. fillStyle.gradientStops 같은 stale 이름) λ₯Ό κ±°λΆ€.\n // 이게 λ§ˆμ§€λ§‰ λ°©μ–΄μ„  β€” λ‹€λ₯Έ styling tool μ—μ„œ ν•™μŠ΅λœ vocabulary κ°€ leak 되면 catch.\n if (!args.patch || typeof args.patch !== 'object' || Array.isArray(args.patch)) {\n return { valid: false, errors: ['patch (object) is required'] }\n }\n const v = validateBoardPatch(args.patch)\n if (!v.valid) {\n return {\n valid: false,\n errors: formatValidationErrors(v),\n suggestion: modifySceneSuggestion()\n }\n }\n return { valid: true }\n }\n case 'replaceBoard': {\n // λ³΄λ“œ 톡째 ꡐ체 β€” μ˜μ† 직전 κ°€μž₯ κ°•λ ₯ν•œ 검증 ν•„μš”. components κΉŒμ§€ ν•œ λ²ˆμ—\n // 검사. μžμ‹ μ»΄ν¬λ„ŒνŠΈμ˜ fillStyle.gradientStops 같은 stale 킀도 μ¦‰μ‹œ catch.\n // (BoardPatchSchema λŠ” component 도 array 둜 ν†΅κ³Όμ‹œν‚€λ˜ ComponentSchema κ°€\n // strict styling sub-objects 둜 stale 차단.)\n const v = validateBoardPatch(args)\n if (!v.valid) {\n return {\n valid: false,\n errors: formatValidationErrors(v),\n suggestion: modifySceneSuggestion()\n }\n }\n return { valid: true }\n }\n\n // κ·Έ μ™Έ β€” κΈ°μ‘΄ dispatcher 검증에 μœ„μž„\n default:\n return { valid: true }\n }\n}\n\nfunction modifySceneSuggestion(): string {\n return (\n 'modifyScene 의 patch λŠ” things-scene 의 model-layer κ°€ μΈμ‹ν•˜λŠ” root key 만 μ‚¬μš©. ' +\n 'fillStyle / strokeStyle / shadow 같은 nested object 의 μ•ˆμͺ½ ν•„λ“œλŠ” canonical 이름 (colorStops λ“±) μ •ν™•νžˆ μ‚¬μš©. ' +\n 'rect λ“± μžμ‹ μ»΄ν¬λ„ŒνŠΈ 변경은 modifyComponentByRefid 둜 뢄리.'\n )\n}\n\nfunction formatValidationErrors(v: ValidationResult): string[] {\n if (v.valid || !v.errors) return []\n return v.errors.map(e => `${e.path || '<root>'}: ${e.message}`)\n}\n\n/**\n * setFill μ •μ • κ°€μ΄λ“œ β€” 자주 μ‹€μˆ˜ν•˜λŠ” νŒ¨ν„΄ + μ˜¬λ°”λ₯Έ μ˜ˆμ‹œ.\n */\nfunction setFillSuggestion(): string {\n return [\n 'Valid setFill patterns:',\n \" - Solid: { refids:[35], mode:'solid', color:'#ff0000' }\",\n \" - Gradient: { refids:[35], mode:'gradient', gradientType:'linear', \" +\n \"colorStops:[{position:0,color:'#ff0000'},{position:1,color:'#0000ff'}], rotation:90 }\",\n \" - Pattern: { refids:[35], mode:'pattern', image:'url' }\",\n \" - Partial color update: { refids:[35], color:'green' } // mode λ―Έμ§€μ •\",\n 'Notes:',\n ' - rotation 은 degrees 둜 μž…λ ₯ (μ„œλ²„κ°€ λΌλ””μ•ˆμœΌλ‘œ λ³€ν™˜ν•΄ fillStyle.rotation 에 μ €μž₯)',\n ' - colorStops λŠ” { position: 0..1, color: hex/rgb/named } 객체 λ°°μ—΄',\n ' - μ˜› 이름 (colors / stops / gradientStops) μ‚¬μš© κΈˆμ§€ β€” engine canonical 은 colorStops'\n ].join('\\n')\n}\n\nfunction modifyPatchSuggestion(): string {\n return [\n 'modifyComponentByRefid is the raw fallback β€” prefer specific styling tools (setFill/setStroke/setText/setFont/setShadow/setMaterial/setOpacity/setVisible) for any styling change.',\n 'If you must use raw modify:',\n ' - fillStyle string (solid) or {type:\"gradient\",gradientType,colorStops:[{position,color}]} or {type:\"pattern\",image,...}',\n ' - shadow {color, offsetX, offsetY, blur} (CSS box-shadow / Canvas ν‘œμ€€ 이름)',\n ' - lineDash STRING preset only (NOT number array)'\n ].join('\\n')\n}\n"]}