@ikas/code-components-mcp 1.4.0-beta.191 → 1.4.0-beta.192
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/framework.json +6 -2
- package/data/storefront-api.json +12 -166
- package/data/storefront-types.json +1 -13
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/data/framework.json
CHANGED
|
@@ -191,7 +191,7 @@
|
|
|
191
191
|
"common-pitfalls": {
|
|
192
192
|
"title": "Common Pitfalls",
|
|
193
193
|
"description": "Frequent mistakes LLMs and developers make when building ikas code components",
|
|
194
|
-
"content": "## Common Pitfalls\n\nThese are the most common mistakes when building ikas code components. Avoid them for correct, working code.\n\n### 1. Root Component Should NOT Use observer\n\nThe ikas runtime wraps root component renders in `autorun()`, making them automatically reactive. Wrapping a root export with `observer()` is redundant and misleading.\n\n**Wrong** — observer on root export:\n```tsx\nimport { observer } from \"@ikas/component-utils\";\nimport { cartStore } from \"@ikas/bp-storefront\";\n\nconst CartSection = observer(function CartSection({ title }: Props) {\n const items = cartStore.cart?.orderLineItems ?? [];\n return <section>{title}: {items.length} items</section>;\n});\nexport default CartSection;\n```\n\n**Correct** — plain named export:\n```tsx\nimport { cartStore } from \"@ikas/bp-storefront\";\n\nexport function CartSection({ title }: Props) {\n const items = cartStore.cart?.orderLineItems ?? [];\n return <section>{title}: {items.length} items</section>;\n}\n\nexport default CartSection;\n```\n\n### 2. Observer Sub-Component Naming\n\nWhen using `observer()` on **sub-components**, use a named function expression — not an arrow function — for proper DevTools display names.\n\n**Wrong** — arrow function loses display name:\n```tsx\nconst CartBadge = observer(() => {\n return <span>{cartStore.cart?.orderLineItems.length ?? 0}</span>;\n});\n```\n\n**Correct** — named function expression:\n```tsx\nconst CartBadge = observer(function CartBadge() {\n return <span>{cartStore.cart?.orderLineItems.length ?? 0}</span>;\n});\n```\n\n### 3. Mutation Semantics\n\nMany storefront functions (122+) return `void` and **mutate their arguments in place**. Do NOT try to capture a return value:\n\n```tsx\n// WRONG — selectVariantValue returns void, not a new product\nconst updated = selectVariantValue(product, value);\n\n// CORRECT — mutates product in place, observer re-renders automatically\nselectVariantValue(product, dvv.variantValue);\n```\n\nOther mutation functions: `initLoginForm()`, `setLoginFormEmail()`, `clearFilter()`, `selectFilterValue()`.\n\n### 4. CSS Scoping Limits\n\nOnly **class selectors** in `styles.css` are reliably scoped. Element selectors are NOT scoped:\n\n```css\n/* SAFE — scoped to your component */\n.product-card { padding: 16px; }\n.product-card .title { font-size: 18px; }\n\n/* UNSAFE — NOT reliably scoped, may affect other components */\ndiv { margin: 0; }\nh1 { font-size: 24px; }\n```\n\nAlways use class selectors for all styles.\n\n### 5. Prop Null Handling\n\nProps from the editor can be `undefined` when the store owner hasn't set them. Always use optional chaining:\n\n```tsx\n// WRONG — will crash if product is undefined\n<h1>{props.product.name}</h1>\n\n// CORRECT\n<h1>{props.product?.name}</h1>\n{props.heroImage && <img src={getDefaultSrc(props.heroImage)} />}\n```\n\n### 6. IkasProductImage vs IkasImage\n\n`variant.images` is `IkasProductImage[]`, NOT `IkasImage[]`. You must access the `.image` property to get the `IkasImage` needed by CDN helpers:\n\n```tsx\n// WRONG — productImage is IkasProductImage, not IkasImage\ngetDefaultSrc(productImage);\n\n// CORRECT — access .image to get IkasImage\ngetDefaultSrc(productImage.image);\n\n// Full pattern:\nconst images: IkasImage[] = variant.images\n .map((pi) => pi.image)\n .filter((img): img is IkasImage => img != null);\n```\n\n### 7. Type Assertion Pattern\n\nSome storefront functions have type inference gaps. Use `as unknown as` casts when needed — this is a known pattern:\n\n```tsx\nconst inStock = hasProductStock(product) as unknown as boolean;\nconst finalPrice = getProductVariantFormattedFinalPrice(variant) as unknown as string;\nconst canAddToCart = isAddToCartEnabled(product) as unknown as boolean;\n```\n\nThis applies to functions like `hasProductStock`, `hasProductVariantStock`, `isAddToCartEnabled`, `hasProductVariantDiscount`, `getProductVariantDiscountPercentage`, `getProductVariantFormattedFinalPrice`, `getProductVariantFormattedSellPrice`, `getProductVariantFormattedDiscountAmount`, and `getProductVariantFormattedCampaignPrice`.\n\nNote: `getProductVariantMainImage` returns `IkasProductImage | undefined` (NOT `IkasImage`). Access `.image` to get the `IkasImage` for CDN helpers like `getDefaultSrc()`.\n\n### 8. Store Data Null Safety\n\nStore data (`customerStore.customer`, `cartStore.cart`, `baseStore`) is `null` before initialization completes. Always guard access:\n\n```tsx\n// WRONG — crashes if customer is null\n<h1>{customerStore.customer.firstName}</h1>\n\n// CORRECT — null check first\nif (!customerStore.customer) return <div>Loading...</div>;\n<h1>{customerStore.customer.firstName}</h1>\n\n// Also correct — optional chaining\n<h1>{customerStore.customer?.firstName ?? \"Guest\"}</h1>\n```\n\nSame for `cartStore.cart` — always use `cartStore.cart?.orderLineItems ?? []`.\n\n### 9. ProductList/BlogList Data Access\n\n`productList.data` is the correct way to access products in a product list. Similarly, `blogList.data` for blogs. The display names `.products` / `.blogs` are only used by the blueprint editor — they do NOT exist at runtime:\n\n```tsx\n// CORRECT — use .data for both product lists and blog lists\nconst products = productList?.data ?? [];\nconst blogs = blogList?.data ?? [];\n\n// WRONG — .products / .blogs are display names, not actual fields\nconst products = productList?.products ?? [];\nconst blogs = blogList?.blogs ?? [];\n```\n\n### 10. Form Field Access Pattern\n\nForm fields are objects with `.value`, `.label`, `.hasError`, `.message`. Never access the field directly as a primitive:\n\n```tsx\n// WRONG — loginForm.email is a field object, not a string\n<input value={loginForm.email} />\n\n// CORRECT — access .value for the actual value\n<input value={loginForm.email.value} />\n{loginForm.email.hasError && <span>{loginForm.email.message}</span>}\n```\n\n### 11. Optional Chaining for Editor Props\n\nAll props from `ikas.config.json` can be `undefined` in the editor before the store owner sets them. Always use optional chaining and defaults:\n\n```tsx\n// WRONG — will crash in the editor\n<h1>{props.title}</h1>\n<img src={getDefaultSrc(props.image)} />\n{props.links.links.map(...)}\n\n// CORRECT — safe access with defaults\n<h1>{props.title ?? \"Default Title\"}</h1>\n{props.image && <img src={getDefaultSrc(props.image)} />}\n{(props.links?.links ?? []).map(...)}\n```\n\n### 12. Event Handler Typing\n\nPreact uses different event types than React. Use `(e: Event)` not `(e: React.ChangeEvent)`. Access values with casting. Preact uses `onInput` not `onChange` for text inputs:\n\n```tsx\n// WRONG — React patterns don't work in Preact\nonChange={(e: React.ChangeEvent<HTMLInputElement>) => setValue(e.target.value)}\n\n// CORRECT — Preact event handling\nonInput={(e: Event) => setValue((e.target as HTMLInputElement).value)}\n```\n\nFor select elements:\n```tsx\nonChange={(e: Event) => setOption((e.target as HTMLSelectElement).value)}\n```\n\n### 13. Function Parameter Order\n\nMany storefront functions take specific parameter orders. Always verify with `get_function_doc()` before using:\n\n```tsx\n// WRONG — submitLoginForm only takes the form, not the store\nsubmitLoginForm(customerStore, loginForm);\n\n// CORRECT\nsubmitLoginForm(loginForm);\n\n// WRONG — wrong parameter order for addItemToCart\naddItemToCart(product, variant, 1);\n\n// CORRECT — variant first, then product, then quantity\naddItemToCart(variant, product, 1);\n\n// WRONG — selectVariantValue takes product and variantValue\nselectVariantValue(variant, value);\n\n// CORRECT\nselectVariantValue(product, dvv.variantValue);\n```\n\nWhen in doubt, use the `get_function_doc(functionName)` MCP tool to check the exact signature.\n\n### 14. Missing backgroundColor Prop on Sections\n\nEvery section MUST include a `backgroundColor` COLOR prop so store owners can customize the section background. Without it, the section has a hardcoded background that cannot be changed in the editor.\n\n**Wrong** — section with no backgroundColor prop:\n```json\n// ikas.config.json\n{ \"id\": \"my-section\", \"type\": \"section\", \"props\": [\n { \"name\": \"title\", \"type\": \"TEXT\" }\n]}\n```\n```tsx\nexport function MySection({ title }: Props) {\n return <section className=\"my-section\"><h1>{title}</h1></section>;\n}\n```\n\n**Correct** — section with backgroundColor prop:\n```json\n// ikas.config.json\n{ \"id\": \"my-section\", \"type\": \"section\", \"props\": [\n { \"name\": \"title\", \"type\": \"TEXT\" },\n { \"name\": \"backgroundColor\", \"displayName\": \"Background Color\", \"type\": \"COLOR\", \"defaultValue\": \"#ffffff\" }\n]}\n```\n```tsx\nexport function MySection({ title, backgroundColor = \"#ffffff\" }: Props) {\n return (\n <section className=\"my-section\" style={backgroundColor ? { backgroundColor } : undefined}>\n <h1>{title}</h1>\n </section>\n );\n}\n```\n\nOptionally, also consider adding `textColor` COLOR props for text elements sitting directly on the section background, so they remain readable when the store owner changes the background color.\n\n### 15. Forgetting to Group Props for Complex Sections\n\nSections with 5+ props should use prop groups to organize the editor sidebar. Without groups, store owners see a long flat list of unrelated props.\n\n**Better** — organize related props into groups:\n```json\n{\n \"propGroups\": [\n { \"id\": \"content\", \"name\": \"Content\" },\n { \"id\": \"appearance\", \"name\": \"Appearance\" }\n ],\n \"props\": [\n { \"name\": \"heading\", \"groupId\": \"content\", ... },\n { \"name\": \"bgColor\", \"groupId\": \"appearance\", ... }\n ]\n}\n```\n\nSee `get_framework_guide(\"prop-groups\")` for full details.\n\n### 16. Hardcoded Static Text in Components\n\nAll user-visible text MUST be TEXT props — never hardcode strings in JSX. Hardcoded text cannot be translated for multilingual storefronts.\n\n**Wrong** — hardcoded strings:\n```tsx\nexport function LoginSection({ backgroundColor }: Props) {\n return (\n <section style={backgroundColor ? { backgroundColor } : undefined}>\n <h1>Sign In</h1>\n <button>{form.isSubmitting ? \"Signing in...\" : \"Sign In\"}</button>\n <p>Don't have an account? <a href=\"/register\">Create one</a></p>\n </section>\n );\n}\n```\n\n**Correct** — all text as TEXT props with defaultValues:\n```json\n// ikas.config.json\n{ \"props\": [\n { \"name\": \"title\", \"type\": \"TEXT\", \"defaultValue\": \"Sign In\", \"groupId\": \"texts\" },\n { \"name\": \"submitButtonText\", \"type\": \"TEXT\", \"defaultValue\": \"Sign In\", \"groupId\": \"texts\" },\n { \"name\": \"submittingButtonText\", \"type\": \"TEXT\", \"defaultValue\": \"Signing in...\", \"groupId\": \"texts\" },\n { \"name\": \"noAccountText\", \"type\": \"TEXT\", \"defaultValue\": \"Don't have an account?\", \"groupId\": \"texts\" },\n { \"name\": \"createAccountLinkText\", \"type\": \"TEXT\", \"defaultValue\": \"Create one\", \"groupId\": \"texts\" }\n],\n\"propGroups\": [{ \"id\": \"texts\", \"name\": \"Texts\" }]}\n```\n```tsx\nexport function LoginSection({ title = \"Sign In\", submitButtonText = \"Sign In\",\n submittingButtonText = \"Signing in...\", noAccountText = \"Don't have an account?\",\n createAccountLinkText = \"Create one\", backgroundColor }: Props) {\n return (\n <section style={backgroundColor ? { backgroundColor } : undefined}>\n <h1>{title}</h1>\n <button>{form.isSubmitting ? submittingButtonText : submitButtonText}</button>\n <p>{noAccountText} <a href=\"/register\">{createAccountLinkText}</a></p>\n </section>\n );\n}\n```\n\nFor button loading states, use two separate TEXT props (e.g., `submitButtonText` + `submittingButtonText`). Group text props under a \"Texts\" propGroup when the component has 5+ props.\n\n### 17. IkasOrderLineItemOption.values is an Array, Not a Scalar\n\n`IkasOrderLineItemOption` (the per-item options on cart/order line items) has a `.values` field that is an **array** of `IkasOrderLineItemOptionValue`, NOT a scalar `.value`. Each value in the array has its own `.name` / `.value` / `.formattedPrice`.\n\n```tsx\n// WRONG — there is no .value on IkasOrderLineItemOption\n<span>{option.value}</span>\n\n// CORRECT — .values is an array; map over it\n{option.values.map((v) => (\n <span key={v.name}>{v.name}: {v.value}</span>\n))}\n```\n\nThis is the most common confusion when rendering order summary lines or cart line item options. See `get_migration_guide(\"prop-runtime-shapes\")` for the broader catalog of order-shape gotchas.\n\n### 18. IkasOrderAdjustment Has No .formattedAmount — Use the Helper\n\n`IkasOrderAdjustment` does not expose a `.formattedAmount` property directly. Call the helper `getOrderAdjustmentFormattedAmount(adjustment)` from `@ikas/bp-storefront` — it returns the formatted currency string with the correct sign (negated for decrements).\n\n```tsx\nimport { getOrderAdjustmentFormattedAmount } from \"@ikas/bp-storefront\";\n\n// WRONG — .formattedAmount does not exist\n<td>{adjustment.formattedAmount}</td>\n\n// CORRECT\n<td>{getOrderAdjustmentFormattedAmount(adjustment)}</td>\n```\n\nSee `get_function_doc(\"getOrderAdjustmentFormattedAmount\")` for the exact signature.\n\n### 19. Old-System Property Patterns That Don't Exist in the New System\n\nSeveral `@ikas/storefront` properties look like they should still work but don't exist on the corresponding `@ikas/bp-storefront` types. Use the helper function instead. **If TypeScript reports TS2339 (\"Property X does not exist on type Y\") on a `.selectedVariant`, `.mainImage`, `.href`, or `.checkoutUrl` access, you're hitting one of these.**\n\n| Old pattern | New replacement |\n|---|---|\n| `product.selectedVariant` | `getSelectedProductVariant(product)` → `IkasProductVariant \\| undefined` |\n| `product.href` | `getProductHref(product)` (one of several `get*Href` helpers — see `navigation-patterns`) |\n| `variant.mainImage` | `getProductVariantMainImage(variant)` → `IkasProductImage`, then access `.image` for the `IkasImage` |\n| `cartStore.checkoutUrl` | `getCheckoutUrlFromCartStore(cartStore)` (handles auth-state branching internally) |\n\nThe variant-to-image chain is 4 hops (each carries metadata the next doesn't). See `get_framework_guide(\"image-handling\")` for the canonical recipe.\n\n### 20. Intl.NumberFormat for Currency Amounts\n\nNever format money with `Intl.NumberFormat` — it falls back to the browser locale and ignores the merchant's currency format settings, so its output clashes with platform-formatted prices on the same screen. For model-bound prices use the `get*FormattedPrice` helpers. For free-standing amounts (remaining amount to free shipping, gift wrap threshold, bulk discount limits, cart limit warnings) use `formatCurrency(amount, currencyCode, currencySymbol)` from `@ikas/bp-storefront`.\n\nNote: `Intl.DateTimeFormat(IkasStorefrontConfig.getCurrentLocale(), ...)` IS the correct pattern for dates — do not mirror it for money.\n\n```tsx\n// WRONG — browser-locale format (\"₺14.478,5\") next to platform prices (\"₺ 1,950.00\")\nconst remaining = new Intl.NumberFormat(undefined, { style: \"currency\", currency: \"TRY\" }).format(amount);\n\n// CORRECT — merchant currency format settings\nimport { formatCurrency } from \"@ikas/bp-storefront\";\n\nconst remaining = formatCurrency(amount, cart.currencyCode, cart.currencySymbol);\n```\n\nSee `get_function_doc(\"formatCurrency\")` for the exact signature.\n\n### Deep SDK imports (`@ikas/bp-storefront/dist/...`)\n\n**Wrong** — reaching into a private subpath when a root helper seems missing:\n```tsx\n// breaks at runtime: no window global for this subpath, and the build rejects it\nimport { apiSaveCart } from \"@ikas/bp-storefront/dist/functions/api/cart\";\n```\n\n**Correct** — use the public root export (coupon has dedicated form helpers):\n```tsx\nimport {\n customerStore,\n getCouponCodeForm,\n setCouponCodeFormCouponCode,\n submitCouponCodeForm,\n} from \"@ikas/bp-storefront\";\n```\n\nRule: every import from the storefront SDK must come from the BARE package name — never a `/dist/` or `/src/` subpath.",
|
|
194
|
+
"content": "## Common Pitfalls\n\nThese are the most common mistakes when building ikas code components. Avoid them for correct, working code.\n\n### 1. Root Component Should NOT Use observer\n\nThe ikas runtime wraps root component renders in `autorun()`, making them automatically reactive. Wrapping a root export with `observer()` is redundant and misleading.\n\n**Wrong** — observer on root export:\n```tsx\nimport { observer } from \"@ikas/component-utils\";\nimport { cartStore } from \"@ikas/bp-storefront\";\n\nconst CartSection = observer(function CartSection({ title }: Props) {\n const items = cartStore.cart?.orderLineItems ?? [];\n return <section>{title}: {items.length} items</section>;\n});\nexport default CartSection;\n```\n\n**Correct** — plain named export:\n```tsx\nimport { cartStore } from \"@ikas/bp-storefront\";\n\nexport function CartSection({ title }: Props) {\n const items = cartStore.cart?.orderLineItems ?? [];\n return <section>{title}: {items.length} items</section>;\n}\n\nexport default CartSection;\n```\n\n### 2. Observer Sub-Component Naming\n\nWhen using `observer()` on **sub-components**, use a named function expression — not an arrow function — for proper DevTools display names.\n\n**Wrong** — arrow function loses display name:\n```tsx\nconst CartBadge = observer(() => {\n return <span>{cartStore.cart?.orderLineItems.length ?? 0}</span>;\n});\n```\n\n**Correct** — named function expression:\n```tsx\nconst CartBadge = observer(function CartBadge() {\n return <span>{cartStore.cart?.orderLineItems.length ?? 0}</span>;\n});\n```\n\n### 3. Mutation Semantics\n\nMany storefront functions (122+) return `void` and **mutate their arguments in place**. Do NOT try to capture a return value:\n\n```tsx\n// WRONG — selectVariantValue returns void, not a new product\nconst updated = selectVariantValue(product, value);\n\n// CORRECT — mutates product in place, observer re-renders automatically\nselectVariantValue(product, dvv.variantValue);\n```\n\nOther mutation functions: `initLoginForm()`, `setLoginFormEmail()`, `clearFilter()`, `selectFilterValue()`.\n\n### 4. CSS Scoping Limits\n\nOnly **class selectors** in `styles.css` are reliably scoped. Element selectors are NOT scoped:\n\n```css\n/* SAFE — scoped to your component */\n.product-card { padding: 16px; }\n.product-card .title { font-size: 18px; }\n\n/* UNSAFE — NOT reliably scoped, may affect other components */\ndiv { margin: 0; }\nh1 { font-size: 24px; }\n```\n\nAlways use class selectors for all styles.\n\n### 5. Prop Null Handling\n\nProps from the editor can be `undefined` when the store owner hasn't set them. Always use optional chaining:\n\n```tsx\n// WRONG — will crash if product is undefined\n<h1>{props.product.name}</h1>\n\n// CORRECT\n<h1>{props.product?.name}</h1>\n{props.heroImage && <img src={getDefaultSrc(props.heroImage)} />}\n```\n\n### 6. IkasProductImage vs IkasImage\n\n`variant.images` is `IkasProductImage[]`, NOT `IkasImage[]`. You must access the `.image` property to get the `IkasImage` needed by CDN helpers:\n\n```tsx\n// WRONG — productImage is IkasProductImage, not IkasImage\ngetDefaultSrc(productImage);\n\n// CORRECT — access .image to get IkasImage\ngetDefaultSrc(productImage.image);\n\n// Full pattern:\nconst images: IkasImage[] = variant.images\n .map((pi) => pi.image)\n .filter((img): img is IkasImage => img != null);\n```\n\n### 7. Type Assertion Pattern\n\nSome storefront functions have type inference gaps. Use `as unknown as` casts when needed — this is a known pattern:\n\n```tsx\nconst inStock = hasProductStock(product) as unknown as boolean;\nconst finalPrice = getProductVariantFormattedFinalPrice(variant) as unknown as string;\nconst canAddToCart = isAddToCartEnabled(product) as unknown as boolean;\n```\n\nThis applies to functions like `hasProductStock`, `hasProductVariantStock`, `isAddToCartEnabled`, `hasProductVariantDiscount`, `getProductVariantDiscountPercentage`, `getProductVariantFormattedFinalPrice`, `getProductVariantFormattedSellPrice`, `getProductVariantFormattedDiscountAmount`, and `getProductVariantFormattedCampaignPrice`.\n\nNote: `getProductVariantMainImage` returns `IkasProductImage | undefined` (NOT `IkasImage`). Access `.image` to get the `IkasImage` for CDN helpers like `getDefaultSrc()`.\n\n### 8. Store Data Null Safety\n\nStore data (`customerStore.customer`, `cartStore.cart`, `baseStore`) is `null` before initialization completes. Always guard access:\n\n```tsx\n// WRONG — crashes if customer is null\n<h1>{customerStore.customer.firstName}</h1>\n\n// CORRECT — null check first\nif (!customerStore.customer) return <div>Loading...</div>;\n<h1>{customerStore.customer.firstName}</h1>\n\n// Also correct — optional chaining\n<h1>{customerStore.customer?.firstName ?? \"Guest\"}</h1>\n```\n\nSame for `cartStore.cart` — always use `cartStore.cart?.orderLineItems ?? []`.\n\n### 9. ProductList/BlogList Data Access\n\n`productList.data` is the correct way to access products in a product list. Similarly, `blogList.data` for blogs. The display names `.products` / `.blogs` are only used by the blueprint editor — they do NOT exist at runtime:\n\n```tsx\n// CORRECT — use .data for both product lists and blog lists\nconst products = productList?.data ?? [];\nconst blogs = blogList?.data ?? [];\n\n// WRONG — .products / .blogs are display names, not actual fields\nconst products = productList?.products ?? [];\nconst blogs = blogList?.blogs ?? [];\n```\n\n### 10. Form Field Access Pattern\n\nForm fields are objects with `.value`, `.label`, `.hasError`, `.message`. Never access the field directly as a primitive:\n\n```tsx\n// WRONG — loginForm.email is a field object, not a string\n<input value={loginForm.email} />\n\n// CORRECT — access .value for the actual value\n<input value={loginForm.email.value} />\n{loginForm.email.hasError && <span>{loginForm.email.message}</span>}\n```\n\n### 11. Optional Chaining for Editor Props\n\nAll props from `ikas.config.json` can be `undefined` in the editor before the store owner sets them. Always use optional chaining and defaults:\n\n```tsx\n// WRONG — will crash in the editor\n<h1>{props.title}</h1>\n<img src={getDefaultSrc(props.image)} />\n{props.links.links.map(...)}\n\n// CORRECT — safe access with defaults\n<h1>{props.title ?? \"Default Title\"}</h1>\n{props.image && <img src={getDefaultSrc(props.image)} />}\n{(props.links?.links ?? []).map(...)}\n```\n\n### 12. Event Handler Typing\n\nPreact uses different event types than React. Use `(e: Event)` not `(e: React.ChangeEvent)`. Access values with casting. Preact uses `onInput` not `onChange` for text inputs:\n\n```tsx\n// WRONG — React patterns don't work in Preact\nonChange={(e: React.ChangeEvent<HTMLInputElement>) => setValue(e.target.value)}\n\n// CORRECT — Preact event handling\nonInput={(e: Event) => setValue((e.target as HTMLInputElement).value)}\n```\n\nFor select elements:\n```tsx\nonChange={(e: Event) => setOption((e.target as HTMLSelectElement).value)}\n```\n\n### 13. Function Parameter Order\n\nMany storefront functions take specific parameter orders. Always verify with `get_function_doc()` before using:\n\n```tsx\n// WRONG — submitLoginForm only takes the form, not the store\nsubmitLoginForm(customerStore, loginForm);\n\n// CORRECT\nsubmitLoginForm(loginForm);\n\n// WRONG — wrong parameter order for addItemToCart\naddItemToCart(product, variant, 1);\n\n// CORRECT — variant first, then product, then quantity\naddItemToCart(variant, product, 1);\n\n// WRONG — selectVariantValue takes product and variantValue\nselectVariantValue(variant, value);\n\n// CORRECT\nselectVariantValue(product, dvv.variantValue);\n```\n\nWhen in doubt, use the `get_function_doc(functionName)` MCP tool to check the exact signature.\n\n### 14. Missing backgroundColor Prop on Sections\n\nEvery section MUST include a `backgroundColor` COLOR prop so store owners can customize the section background. Without it, the section has a hardcoded background that cannot be changed in the editor.\n\n**Wrong** — section with no backgroundColor prop:\n```json\n// ikas.config.json\n{ \"id\": \"my-section\", \"type\": \"section\", \"props\": [\n { \"name\": \"title\", \"type\": \"TEXT\" }\n]}\n```\n```tsx\nexport function MySection({ title }: Props) {\n return <section className=\"my-section\"><h1>{title}</h1></section>;\n}\n```\n\n**Correct** — section with backgroundColor prop:\n```json\n// ikas.config.json\n{ \"id\": \"my-section\", \"type\": \"section\", \"props\": [\n { \"name\": \"title\", \"type\": \"TEXT\" },\n { \"name\": \"backgroundColor\", \"displayName\": \"Background Color\", \"type\": \"COLOR\", \"defaultValue\": \"#ffffff\" }\n]}\n```\n```tsx\nexport function MySection({ title, backgroundColor = \"#ffffff\" }: Props) {\n return (\n <section className=\"my-section\" style={backgroundColor ? { backgroundColor } : undefined}>\n <h1>{title}</h1>\n </section>\n );\n}\n```\n\nOptionally, also consider adding `textColor` COLOR props for text elements sitting directly on the section background, so they remain readable when the store owner changes the background color.\n\n### 15. Forgetting to Group Props for Complex Sections\n\nSections with 5+ props should use prop groups to organize the editor sidebar. Without groups, store owners see a long flat list of unrelated props.\n\n**Better** — organize related props into groups:\n```json\n{\n \"propGroups\": [\n { \"id\": \"content\", \"name\": \"Content\" },\n { \"id\": \"appearance\", \"name\": \"Appearance\" }\n ],\n \"props\": [\n { \"name\": \"heading\", \"groupId\": \"content\", ... },\n { \"name\": \"bgColor\", \"groupId\": \"appearance\", ... }\n ]\n}\n```\n\nSee `get_framework_guide(\"prop-groups\")` for full details.\n\n### 16. Hardcoded Static Text in Components\n\nAll user-visible text MUST be TEXT props — never hardcode strings in JSX. Hardcoded text cannot be translated for multilingual storefronts.\n\n**Wrong** — hardcoded strings:\n```tsx\nexport function LoginSection({ backgroundColor }: Props) {\n return (\n <section style={backgroundColor ? { backgroundColor } : undefined}>\n <h1>Sign In</h1>\n <button>{form.isSubmitting ? \"Signing in...\" : \"Sign In\"}</button>\n <p>Don't have an account? <a href=\"/register\">Create one</a></p>\n </section>\n );\n}\n```\n\n**Correct** — all text as TEXT props with defaultValues:\n```json\n// ikas.config.json\n{ \"props\": [\n { \"name\": \"title\", \"type\": \"TEXT\", \"defaultValue\": \"Sign In\", \"groupId\": \"texts\" },\n { \"name\": \"submitButtonText\", \"type\": \"TEXT\", \"defaultValue\": \"Sign In\", \"groupId\": \"texts\" },\n { \"name\": \"submittingButtonText\", \"type\": \"TEXT\", \"defaultValue\": \"Signing in...\", \"groupId\": \"texts\" },\n { \"name\": \"noAccountText\", \"type\": \"TEXT\", \"defaultValue\": \"Don't have an account?\", \"groupId\": \"texts\" },\n { \"name\": \"createAccountLinkText\", \"type\": \"TEXT\", \"defaultValue\": \"Create one\", \"groupId\": \"texts\" }\n],\n\"propGroups\": [{ \"id\": \"texts\", \"name\": \"Texts\" }]}\n```\n```tsx\nexport function LoginSection({ title = \"Sign In\", submitButtonText = \"Sign In\",\n submittingButtonText = \"Signing in...\", noAccountText = \"Don't have an account?\",\n createAccountLinkText = \"Create one\", backgroundColor }: Props) {\n return (\n <section style={backgroundColor ? { backgroundColor } : undefined}>\n <h1>{title}</h1>\n <button>{form.isSubmitting ? submittingButtonText : submitButtonText}</button>\n <p>{noAccountText} <a href=\"/register\">{createAccountLinkText}</a></p>\n </section>\n );\n}\n```\n\nFor button loading states, use two separate TEXT props (e.g., `submitButtonText` + `submittingButtonText`). Group text props under a \"Texts\" propGroup when the component has 5+ props.\n\n### 17. IkasOrderLineItemOption.values is an Array, Not a Scalar\n\n`IkasOrderLineItemOption` (the per-item options on cart/order line items) has a `.values` field that is an **array** of `IkasOrderLineItemOptionValue`, NOT a scalar `.value`. Each value in the array has its own `.name` / `.value` / `.formattedPrice`.\n\n```tsx\n// WRONG — there is no .value on IkasOrderLineItemOption\n<span>{option.value}</span>\n\n// CORRECT — .values is an array; map over it\n{option.values.map((v) => (\n <span key={v.name}>{v.name}: {v.value}</span>\n))}\n```\n\nThis is the most common confusion when rendering order summary lines or cart line item options. See `get_migration_guide(\"prop-runtime-shapes\")` for the broader catalog of order-shape gotchas.\n\n### 18. IkasOrderAdjustment Has No .formattedAmount — Use the Helper\n\n`IkasOrderAdjustment` does not expose a `.formattedAmount` property directly. Call the helper `getOrderAdjustmentFormattedAmount(adjustment)` from `@ikas/bp-storefront` — it returns the formatted currency string with the correct sign (negated for decrements).\n\n```tsx\nimport { getOrderAdjustmentFormattedAmount } from \"@ikas/bp-storefront\";\n\n// WRONG — .formattedAmount does not exist\n<td>{adjustment.formattedAmount}</td>\n\n// CORRECT\n<td>{getOrderAdjustmentFormattedAmount(adjustment)}</td>\n```\n\nSee `get_function_doc(\"getOrderAdjustmentFormattedAmount\")` for the exact signature.\n\n### 19. Old-System Property Patterns That Don't Exist in the New System\n\nSeveral `@ikas/storefront` properties look like they should still work but don't exist on the corresponding `@ikas/bp-storefront` types. Use the helper function instead. **If TypeScript reports TS2339 (\"Property X does not exist on type Y\") on a `.selectedVariant`, `.mainImage`, `.href`, or `.checkoutUrl` access, you're hitting one of these.**\n\n| Old pattern | New replacement |\n|---|---|\n| `product.selectedVariant` | `getSelectedProductVariant(product)` → `IkasProductVariant \\| undefined` |\n| `product.href` | `getProductHref(product)` (one of several `get*Href` helpers — see `navigation-patterns`) |\n| `variant.mainImage` | `getProductVariantMainImage(variant)` → `IkasProductImage`, then access `.image` for the `IkasImage` |\n| `cartStore.checkoutUrl` | `getCheckoutUrlFromCartStore(cartStore)` (handles auth-state branching internally) |\n\nThe variant-to-image chain is 4 hops (each carries metadata the next doesn't). See `get_framework_guide(\"image-handling\")` for the canonical recipe.\n\n### 20. Intl.NumberFormat for Currency Amounts\n\nNever format money with `Intl.NumberFormat` — it falls back to the browser locale and ignores the merchant's currency format settings, so its output clashes with platform-formatted prices on the same screen. For model-bound prices use the `get*FormattedPrice` helpers. For free-standing amounts (remaining amount to free shipping, gift wrap threshold, bulk discount limits, cart limit warnings) use `formatCurrency(amount, currencyCode, currencySymbol)` from `@ikas/bp-storefront`.\n\nNote: `Intl.DateTimeFormat(IkasStorefrontConfig.getCurrentLocale(), ...)` IS the correct pattern for dates — do not mirror it for money.\n\n```tsx\n// WRONG — browser-locale format (\"₺14.478,5\") next to platform prices (\"₺ 1,950.00\")\nconst remaining = new Intl.NumberFormat(undefined, { style: \"currency\", currency: \"TRY\" }).format(amount);\n\n// CORRECT — merchant currency format settings\nimport { formatCurrency } from \"@ikas/bp-storefront\";\n\nconst remaining = formatCurrency(amount, cart.currencyCode, cart.currencySymbol);\n```\n\nSee `get_function_doc(\"formatCurrency\")` for the exact signature.\n\n### 21. Loops That Never Yield (Page Freeze)\n\nThree patterns re-queue themselves before the current task ends. The browser never gets to paint or handle input: the tab hard-freezes, and in the editor the whole editor freezes with it. The runtime now slows these loops down instead of freezing, and reports them (`get_runtime_warnings`), but the component is still wrong and still burns CPU.\n\n**Wrong** — MutationObserver that writes into the subtree it observes. Setting `textContent` emits a mutation record even when the value is unchanged:\n```tsx\nconst observer = new MutationObserver(() => {\n label.textContent = title; // re-triggers the observer, forever\n});\nobserver.observe(root, { childList: true, subtree: true });\n```\n\n**Correct** — write only when the value differs (or `disconnect()` around your own writes):\n```tsx\nconst observer = new MutationObserver(() => {\n if (label.textContent !== title) label.textContent = title;\n});\n```\n\n**Wrong** — `setState` on every render from `useLayoutEffect` (no dependency array). Unlike `useEffect`, layout effects run synchronously, so Preact re-renders without ever yielding:\n```tsx\nuseLayoutEffect(() => {\n setHeight(ref.current.offsetHeight);\n});\n```\n\n**Correct** — dependency array, and only set state when it changes:\n```tsx\nuseLayoutEffect(() => {\n const next = ref.current?.offsetHeight ?? 0;\n setHeight((prev) => (prev === next ? prev : next));\n}, [items.length]);\n```\n\n**Wrong** — a `queueMicrotask` / `await Promise.resolve()` chain used as a polling loop. Microtasks run before the next task, so this is a busy loop. The `await` form cannot be caught by the runtime at all — it freezes the page. Repeat with `setTimeout`, `requestAnimationFrame` or an observer instead.\n\n### Deep SDK imports (`@ikas/bp-storefront/dist/...`)\n\n**Wrong** — reaching into a private subpath when a root helper seems missing:\n```tsx\n// breaks at runtime: no window global for this subpath, and the build rejects it\nimport { apiSaveCart } from \"@ikas/bp-storefront/dist/functions/api/cart\";\n```\n\n**Correct** — use the public root export (coupon has dedicated form helpers):\n```tsx\nimport {\n customerStore,\n getCouponCodeForm,\n setCouponCodeFormCouponCode,\n submitCouponCodeForm,\n} from \"@ikas/bp-storefront\";\n```\n\nRule: every import from the storefront SDK must come from the BARE package name — never a `/dist/` or `/src/` subpath.",
|
|
195
195
|
"tags": [
|
|
196
196
|
"pitfalls",
|
|
197
197
|
"gotchas",
|
|
@@ -210,7 +210,11 @@
|
|
|
210
210
|
"currency",
|
|
211
211
|
"formatCurrency",
|
|
212
212
|
"NumberFormat",
|
|
213
|
-
"price-format"
|
|
213
|
+
"price-format",
|
|
214
|
+
"MutationObserver",
|
|
215
|
+
"useLayoutEffect",
|
|
216
|
+
"infinite-loop",
|
|
217
|
+
"freeze"
|
|
214
218
|
]
|
|
215
219
|
},
|
|
216
220
|
"sub-component-patterns": {
|
package/data/storefront-api.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-09-18T14:
|
|
2
|
+
"generatedAt": "2026-09-18T14:47:50.073Z",
|
|
3
3
|
"functions": [
|
|
4
4
|
{
|
|
5
5
|
"name": "apiListProductBrand",
|
|
@@ -13507,171 +13507,6 @@
|
|
|
13507
13507
|
"IkasCustomerStore"
|
|
13508
13508
|
]
|
|
13509
13509
|
},
|
|
13510
|
-
{
|
|
13511
|
-
"name": "milesSmilesBind",
|
|
13512
|
-
"signature": "async function milesSmilesBind(customerStore: IkasCustomerStore, redirect?: string | null): Promise<MilesSmilesBindResult>",
|
|
13513
|
-
"description": "Start the Turkish Airlines Miles&Smiles account bind flow for the logged-in customer.\nOpens the THY login page in a popup; once THY confirms, the browser is sent to the\nikas bind callback and lands back on `redirect` with `milesSmilesStatus` query params\n(see handleMilesSmilesBind). Binding is required before miles can be spent at checkout.",
|
|
13514
|
-
"params": [
|
|
13515
|
-
{
|
|
13516
|
-
"name": "customerStore",
|
|
13517
|
-
"description": "- The customer store instance (customer must be logged in)"
|
|
13518
|
-
},
|
|
13519
|
-
{
|
|
13520
|
-
"name": "redirect",
|
|
13521
|
-
"description": "- Relative store path to land on after bind, e.g. \"/account\" or \"/checkout?id=...&step=payment\""
|
|
13522
|
-
}
|
|
13523
|
-
],
|
|
13524
|
-
"returns": "`isSuccess: true` when the bind hand-off started; otherwise an `errorCode`\nsuch as \"AUTHORIZATION_REQUIRED\" or \"MILES_SMILES_LOGIN_CANCELLED_OR_FAILED\"",
|
|
13525
|
-
"returnType": "Promise<MilesSmilesBindResult>",
|
|
13526
|
-
"example": "```typescript\nimport { customerStore, milesSmilesBind } from \"@ikas/bp-storefront\";\n\nasync function onLinkClick() {\n const result = await milesSmilesBind(customerStore, \"/account\");\n if (!result.isSuccess) showToast(result.errorCode || \"UNEXPECTED_ERROR\");\n}\n```",
|
|
13527
|
-
"categories": [
|
|
13528
|
-
"Customer",
|
|
13529
|
-
"Miles&Smiles"
|
|
13530
|
-
],
|
|
13531
|
-
"related": [
|
|
13532
|
-
"handleMilesSmilesBind",
|
|
13533
|
-
"getMilesSmilesLink",
|
|
13534
|
-
"unlinkMilesSmiles",
|
|
13535
|
-
"getMilesSmilesDetails"
|
|
13536
|
-
],
|
|
13537
|
-
"isAsync": true,
|
|
13538
|
-
"isClass": false,
|
|
13539
|
-
"parameterTypes": [
|
|
13540
|
-
"IkasCustomerStore"
|
|
13541
|
-
]
|
|
13542
|
-
},
|
|
13543
|
-
{
|
|
13544
|
-
"name": "handleMilesSmilesBind",
|
|
13545
|
-
"signature": "function handleMilesSmilesBind(clear: boolean = true): HandleMilesSmilesBindReturnType | null",
|
|
13546
|
-
"description": "Read the Miles&Smiles bind result from the current URL after THY redirects back\n(`?milesSmilesStatus=success` or `?milesSmilesStatus=fail&milesSmilesError=CODE`).\nCall this on the landing page (account or checkout). The params are removed from the\nURL so a refresh does not repeat the same message.",
|
|
13547
|
-
"params": [
|
|
13548
|
-
{
|
|
13549
|
-
"name": "clear",
|
|
13550
|
-
"description": "- Remove the params from the URL with history.replaceState (default: true)"
|
|
13551
|
-
}
|
|
13552
|
-
],
|
|
13553
|
-
"returns": "The landing result, or null when the URL carries no bind result",
|
|
13554
|
-
"returnType": "HandleMilesSmilesBindReturnType | null",
|
|
13555
|
-
"example": "```typescript\nimport { handleMilesSmilesBind } from \"@ikas/bp-storefront\";\n\nuseEffect(() => {\n const result = handleMilesSmilesBind();\n if (result?.status === \"success\") showToast(\"Miles&Smiles account linked\");\n if (result?.status === \"fail\") showToast(result.errorCode || \"Bind failed\");\n}, []);\n```",
|
|
13556
|
-
"categories": [
|
|
13557
|
-
"Customer",
|
|
13558
|
-
"Miles&Smiles"
|
|
13559
|
-
],
|
|
13560
|
-
"related": [
|
|
13561
|
-
"milesSmilesBind",
|
|
13562
|
-
"getMilesSmilesLink"
|
|
13563
|
-
],
|
|
13564
|
-
"isAsync": false,
|
|
13565
|
-
"isClass": false
|
|
13566
|
-
},
|
|
13567
|
-
{
|
|
13568
|
-
"name": "getMilesSmilesLink",
|
|
13569
|
-
"signature": "async function getMilesSmilesLink(customerStore: IkasCustomerStore): Promise<APIResponse<ExternalAccountLink | null>>",
|
|
13570
|
-
"description": "Get the customer's durable Miles&Smiles link (TK membership number and link date).\nA link does not guarantee a live checkout session; when getMilesSmilesDetails returns\nMILES_SMILES_LOGIN_REQUIRED, run milesSmilesBind again.",
|
|
13571
|
-
"params": [
|
|
13572
|
-
{
|
|
13573
|
-
"name": "customerStore",
|
|
13574
|
-
"description": "- The customer store instance"
|
|
13575
|
-
}
|
|
13576
|
-
],
|
|
13577
|
-
"returns": "API response whose data is the link, or null when not linked",
|
|
13578
|
-
"returnType": "Promise<APIResponse<ExternalAccountLink | null>>",
|
|
13579
|
-
"example": "```typescript\nimport { customerStore, getMilesSmilesLink } from \"@ikas/bp-storefront\";\n\nconst response = await getMilesSmilesLink(customerStore);\nconst tkNumber = response.data?.externalId;\n```",
|
|
13580
|
-
"categories": [
|
|
13581
|
-
"Customer",
|
|
13582
|
-
"Miles&Smiles"
|
|
13583
|
-
],
|
|
13584
|
-
"related": [
|
|
13585
|
-
"milesSmilesBind",
|
|
13586
|
-
"unlinkMilesSmiles"
|
|
13587
|
-
],
|
|
13588
|
-
"isAsync": true,
|
|
13589
|
-
"isClass": false,
|
|
13590
|
-
"parameterTypes": [
|
|
13591
|
-
"IkasCustomerStore"
|
|
13592
|
-
]
|
|
13593
|
-
},
|
|
13594
|
-
{
|
|
13595
|
-
"name": "unlinkMilesSmiles",
|
|
13596
|
-
"signature": "async function unlinkMilesSmiles(customerStore: IkasCustomerStore): Promise<APIResponse<boolean>>",
|
|
13597
|
-
"description": "Remove the customer's Miles&Smiles link. Also clears the server-side checkout\nsession token, so miles cannot be spent until the customer binds again.",
|
|
13598
|
-
"params": [
|
|
13599
|
-
{
|
|
13600
|
-
"name": "customerStore",
|
|
13601
|
-
"description": "- The customer store instance"
|
|
13602
|
-
}
|
|
13603
|
-
],
|
|
13604
|
-
"returns": "API response whose data is true on success",
|
|
13605
|
-
"returnType": "Promise<APIResponse<boolean>>",
|
|
13606
|
-
"example": "```typescript\nimport { customerStore, unlinkMilesSmiles } from \"@ikas/bp-storefront\";\n\nconst response = await unlinkMilesSmiles(customerStore);\nif (response.isSuccess) showToast(\"Miles&Smiles link removed\");\n```",
|
|
13607
|
-
"categories": [
|
|
13608
|
-
"Customer",
|
|
13609
|
-
"Miles&Smiles"
|
|
13610
|
-
],
|
|
13611
|
-
"related": [
|
|
13612
|
-
"milesSmilesBind",
|
|
13613
|
-
"getMilesSmilesLink"
|
|
13614
|
-
],
|
|
13615
|
-
"isAsync": true,
|
|
13616
|
-
"isClass": false,
|
|
13617
|
-
"parameterTypes": [
|
|
13618
|
-
"IkasCustomerStore"
|
|
13619
|
-
]
|
|
13620
|
-
},
|
|
13621
|
-
{
|
|
13622
|
-
"name": "getMilesSmilesDetails",
|
|
13623
|
-
"signature": "async function getMilesSmilesDetails(customerStore: IkasCustomerStore): Promise<APIResponse<MilesSmilesDetailsResponse | undefined>>",
|
|
13624
|
-
"description": "Get the Miles&Smiles balance and member details for the payment step.\nRequires a recent bind; MILES_SMILES_LOGIN_REQUIRED means the short-lived session\nexpired and milesSmilesBind should be run again. MILES_SMILES_NOT_ENABLED and\nPAYMENT_GATEWAY_NOT_FOUND mean the Miles UI should be hidden.",
|
|
13625
|
-
"params": [
|
|
13626
|
-
{
|
|
13627
|
-
"name": "customerStore",
|
|
13628
|
-
"description": "- The customer store instance"
|
|
13629
|
-
}
|
|
13630
|
-
],
|
|
13631
|
-
"returns": "API response whose data holds availableMiles, memberId, firstName and lastName",
|
|
13632
|
-
"returnType": "Promise<APIResponse<MilesSmilesDetailsResponse | undefined>>",
|
|
13633
|
-
"example": "```typescript\nimport { customerStore, getMilesSmilesDetails } from \"@ikas/bp-storefront\";\n\nconst response = await getMilesSmilesDetails(customerStore);\nif (response.errorCodes.includes(\"MILES_SMILES_LOGIN_REQUIRED\")) {\n await milesSmilesBind(customerStore, window.location.pathname + window.location.search);\n} else if (response.data) {\n console.log(\"Available miles:\", response.data.availableMiles);\n}\n```",
|
|
13634
|
-
"categories": [
|
|
13635
|
-
"Customer",
|
|
13636
|
-
"Miles&Smiles"
|
|
13637
|
-
],
|
|
13638
|
-
"related": [
|
|
13639
|
-
"milesSmilesBind",
|
|
13640
|
-
"getMilesSmilesLink"
|
|
13641
|
-
],
|
|
13642
|
-
"isAsync": true,
|
|
13643
|
-
"isClass": false,
|
|
13644
|
-
"parameterTypes": [
|
|
13645
|
-
"IkasCustomerStore"
|
|
13646
|
-
]
|
|
13647
|
-
},
|
|
13648
|
-
{
|
|
13649
|
-
"name": "isMilesSmilesTransaction",
|
|
13650
|
-
"signature": "function isMilesSmilesTransaction(transaction: IkasOrderTransaction): boolean",
|
|
13651
|
-
"description": "Check whether an order transaction is the Miles&Smiles leg of a multi-tender payment.\nSuch rows are written on the synthetic MILES_SMILES gateway and are not refundable from the storefront.",
|
|
13652
|
-
"params": [
|
|
13653
|
-
{
|
|
13654
|
-
"name": "transaction",
|
|
13655
|
-
"description": "- The order transaction to check"
|
|
13656
|
-
}
|
|
13657
|
-
],
|
|
13658
|
-
"returns": "True when the transaction is a Miles&Smiles SALE",
|
|
13659
|
-
"returnType": "boolean",
|
|
13660
|
-
"example": "```typescript\nimport { isMilesSmilesTransaction } from \"@ikas/bp-storefront\";\n\nconst label = isMilesSmilesTransaction(tx) ? \"Miles&Smiles\" : tx.paymentGatewayName;\n```",
|
|
13661
|
-
"categories": [
|
|
13662
|
-
"Customer",
|
|
13663
|
-
"Orders",
|
|
13664
|
-
"Miles&Smiles"
|
|
13665
|
-
],
|
|
13666
|
-
"related": [
|
|
13667
|
-
"getOrderTransactions"
|
|
13668
|
-
],
|
|
13669
|
-
"isAsync": false,
|
|
13670
|
-
"isClass": false,
|
|
13671
|
-
"parameterTypes": [
|
|
13672
|
-
"IkasOrderTransaction"
|
|
13673
|
-
]
|
|
13674
|
-
},
|
|
13675
13510
|
{
|
|
13676
13511
|
"name": "deleteCustomerAddress",
|
|
13677
13512
|
"signature": "async function deleteCustomerAddress(customerStore: IkasCustomerStore, address: IkasCustomerAddress): Promise<boolean>",
|
|
@@ -15777,6 +15612,17 @@
|
|
|
15777
15612
|
"isAsync": false,
|
|
15778
15613
|
"isClass": false
|
|
15779
15614
|
},
|
|
15615
|
+
{
|
|
15616
|
+
"name": "createLoopGuard",
|
|
15617
|
+
"signature": "function createLoopGuard(win: GuardWindow, onTrip: (trip: LoopGuardTrip) => void): { pair: (ownerName?: string | undefined, ownerId?: string | undefined) => LoopGuardPair; renders: (options: PreactOptions) => void; }",
|
|
15618
|
+
"description": "",
|
|
15619
|
+
"params": [],
|
|
15620
|
+
"returnType": "{ pair: (ownerName?: string | undefined, ownerId?: string | undefined) => LoopGuardPair; renders: (options: PreactOptions) => void; }",
|
|
15621
|
+
"categories": [],
|
|
15622
|
+
"related": [],
|
|
15623
|
+
"isAsync": false,
|
|
15624
|
+
"isClass": false
|
|
15625
|
+
},
|
|
15780
15626
|
{
|
|
15781
15627
|
"name": "PayWithIkas",
|
|
15782
15628
|
"signature": "function PayWithIkas({ product, quantity = 1, className, style }: PayWithIkasProps): VNode<ClassAttributes<HTMLIFrameElement> & { ref: MutableRef<HTMLIFrameElement | null>; title: string; ... 4 more ...; style: { ...; }; }> | null",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-09-18T14:
|
|
2
|
+
"generatedAt": "2026-09-18T14:47:50.133Z",
|
|
3
3
|
"types": [
|
|
4
4
|
{
|
|
5
5
|
"name": "TextElementTagTypes",
|
|
@@ -8078,7 +8078,6 @@
|
|
|
8078
8078
|
"relatedFunctions": [
|
|
8079
8079
|
"getOrderTransactionFormattedAmount",
|
|
8080
8080
|
"getOrderTransactionPaymentMethodTranslation",
|
|
8081
|
-
"isMilesSmilesTransaction",
|
|
8082
8081
|
"getOrderTransactions"
|
|
8083
8082
|
]
|
|
8084
8083
|
},
|
|
@@ -8320,12 +8319,6 @@
|
|
|
8320
8319
|
"type": "string | null",
|
|
8321
8320
|
"optional": false
|
|
8322
8321
|
},
|
|
8323
|
-
{
|
|
8324
|
-
"name": "milesSmilesEnabled",
|
|
8325
|
-
"type": "boolean | null",
|
|
8326
|
-
"optional": true,
|
|
8327
|
-
"description": "True when Miles&Smiles can be redeemed on this (TK_PAY) gateway."
|
|
8328
|
-
},
|
|
8329
8322
|
{
|
|
8330
8323
|
"name": "name",
|
|
8331
8324
|
"type": "string",
|
|
@@ -12781,11 +12774,6 @@
|
|
|
12781
12774
|
"type": "string | null",
|
|
12782
12775
|
"optional": false
|
|
12783
12776
|
},
|
|
12784
|
-
{
|
|
12785
|
-
"name": "openAiPixelId",
|
|
12786
|
-
"type": "string | null",
|
|
12787
|
-
"optional": true
|
|
12788
|
-
},
|
|
12789
12777
|
{
|
|
12790
12778
|
"name": "token",
|
|
12791
12779
|
"type": "string | null",
|
package/dist/index.js
CHANGED
|
@@ -1969,7 +1969,7 @@ const server = new McpServer({
|
|
|
1969
1969
|
version: "0.1.0",
|
|
1970
1970
|
}, {
|
|
1971
1971
|
instructions: "Examples and section templates from this server are API reference only — reuse imports, function calls, and data-access patterns; create your own JSX structure, CSS class names, and visual design.\n\n" +
|
|
1972
|
-
"Live-editor actions (require `ikas theme dev` running with the editor connected): `list_editor_pages` → page ids; `list_imported_sections` → imported component ids; `import_section` → import a built component; `add_section_to_page` → place one section (`add_sections_to_page` places MANY at once and can set their props in the same call — fastest way to build a page); `list_page_sections` → the sections placed on a page (LEAN roster: per-placement `elementId`, `componentId`, `propCount`, which props are filled — NO schema/values, so it never truncates); `move_page_section` → reorder a section already placed on a page (by `direction` up/down or an absolute `to_index`) — the only way to change section order, since the editor UI offers it solely as a drag; `get_component_props` → a component's prop blueprint (types, ENUM valid `options`, COMPONENT_LIST `allowedComponentIds`) for any section OR child id; `get_section_values` → one section's current prop values (for read-modify-write); `get_page_by_type` → a theme page id by pageType (e.g. CATEGORY), to build PAGE links to entities (`create_page` adds that page if it does not exist yet, so you never guess a slug); `update_section_prop` → change a single prop value of a placed section (`update_page_sections` fills MANY sections/props of a page in ONE call — strongly prefer it to cut round-trips when filling content); `upload_image` → upload one image (file or URL) and get an image id (`upload_images` uploads many in one call — prefer it for several); `search_products` / `list_categories` / `list_brands` / `list_blogs` / `list_blog_categories` → find real entity ids for PRODUCT/CATEGORY/BRAND/BLOG/BLOG_CATEGORY prop values; `publish_theme` → publish the theme LIVE and get back the preview URL to review (guarded — needs `confirm:true`, and `confirm_production:true` for the main theme; only call it when the user explicitly asks to publish); `list_ready_made_pages` / `update_ready_made_page_prop` / `enable_ready_made_pages` → the ikas-rendered ready-made account/membership pages (login, register, my account, orders, …) — these are NOT sections and the section tools cannot edit them; use these three to enable the groups, read their prop schemas, and customize/localize them (logo, colors, texts). When the user asks to BUILD a login/register/password/my-account/orders/addresses page, FIRST offer these ready-made pages instead of assembling one from sections — they are fully functional out of the box and customizable (logo, colors, texts); build a custom section page only if the user prefers a custom design. Offer, don't flip: `enable_ready_made_pages` creates/removes pages, so get the user's explicit OK before calling it. For the full end-to-end process (placing a section and filling its content, with per-prop value shapes and COMPONENT_LIST rules), call `get_editor_workflow` first.\n\n" +
|
|
1972
|
+
"Live-editor actions (require `ikas theme dev` running with the editor connected): `list_editor_pages` → page ids; `list_imported_sections` → imported component ids; `import_section` → import a built component; `add_section_to_page` → place one section (`add_sections_to_page` places MANY at once and can set their props in the same call — fastest way to build a page); `list_page_sections` → the sections placed on a page (LEAN roster: per-placement `elementId`, `componentId`, `propCount`, which props are filled — NO schema/values, so it never truncates); `move_page_section` → reorder a section already placed on a page (by `direction` up/down or an absolute `to_index`) — the only way to change section order, since the editor UI offers it solely as a drag; `get_component_props` → a component's prop blueprint (types, ENUM valid `options`, COMPONENT_LIST `allowedComponentIds`) for any section OR child id; `get_section_values` → one section's current prop values (for read-modify-write); `get_page_by_type` → a theme page id by pageType (e.g. CATEGORY), to build PAGE links to entities (`create_page` adds that page if it does not exist yet, so you never guess a slug); `update_section_prop` → change a single prop value of a placed section (`update_page_sections` fills MANY sections/props of a page in ONE call — strongly prefer it to cut round-trips when filling content); `upload_image` → upload one image (file or URL) and get an image id (`upload_images` uploads many in one call — prefer it for several); `search_products` / `list_categories` / `list_brands` / `list_blogs` / `list_blog_categories` → find real entity ids for PRODUCT/CATEGORY/BRAND/BLOG/BLOG_CATEGORY prop values; `publish_theme` → publish the theme LIVE and get back the preview URL to review (guarded — needs `confirm:true`, and `confirm_production:true` for the main theme; only call it when the user explicitly asks to publish); `list_ready_made_pages` / `update_ready_made_page_prop` / `enable_ready_made_pages` → the ikas-rendered ready-made account/membership pages (login, register, my account, orders, …) — these are NOT sections and the section tools cannot edit them; use these three to enable the groups, read their prop schemas, and customize/localize them (logo, colors, texts). When the user asks to BUILD a login/register/password/my-account/orders/addresses page, FIRST offer these ready-made pages instead of assembling one from sections — they are fully functional out of the box and customizable (logo, colors, texts); build a custom section page only if the user prefers a custom design. Offer, don't flip: `enable_ready_made_pages` creates/removes pages, so get the user's explicit OK before calling it. For the full end-to-end process (placing a section and filling its content, with per-prop value shapes and COMPONENT_LIST rules), call `get_editor_workflow` first. RUNTIME WARNINGS: any live-editor tool result may carry a `runtimeWarnings` array — the editor canvas caught a code component misbehaving at runtime (e.g. a MutationObserver / queueMicrotask / useLayoutEffect infinite loop it had to slow down). It is unrelated to whether that call succeeded, and slowing down is damage control, not a fix: stop, fix the named component's source following the warning's `message`, then call `get_runtime_warnings` to confirm it cleared (a warning is dropped when its component is recompiled and re-raised if it is still broken).\n\n" +
|
|
1973
1973
|
"READY-MADE PAGES FIRST — HARD RULE for account/membership pages: USER-FACING NAME: name these in the USER'S OWN LANGUAGE — English: \"ikas's ready-made pages\", Turkish: \"ikas'ın hazır sayfaları\"; never mix the English term into a sentence in another language — the editor UI lists them as Membership Pages / My Account Pages; \"built-in\" is an internal term — keep it out of everything the user sees, including question/option labels. When the user asks for a login, register, forgot/recover-password, my-account, orders, order-detail, addresses, favorites or email-verification page, do NOT start coding a custom section page. STOP and ask the user FIRST whether to use ikas's ready-made version of that page (`list_ready_made_pages` shows it — fully functional out of the box, customized/localized via `update_ready_made_page_prop`, the whole group turned on with `enable_ready_made_pages`). Only build a custom section page AFTER the user explicitly chooses custom. When asking, list the ready-made option FIRST as the recommended default; the custom section option comes second. Never call `enable_ready_made_pages` without the user's OK — it creates/removes pages. Ready-made pages are born with ONLY their own ikas-rendered element: the theme's header/footer are NOT on them (the page draws its own top bar — logo, back-to-store link — and account nav). If the user wants the theme header/footer on a ready-made page, PLACE it there with add_section_to_page — sections can sit above/below that element — and suggest turning the page's own `showBrandBar` prop off to avoid double chrome.\n\n" +
|
|
1974
1974
|
"To CHANGE/EDIT a prop value (text, color, boolean, number, etc.) of a section that is already on a page, this IS supported: call `list_page_sections(page_id)` to get the placement `elementId` and the prop names/ids, then `update_section_prop(page_id, element_id, prop_name|prop_id, value)`. Do not assume prop editing is unavailable.\n\n" +
|
|
1975
1975
|
"TWO DISTINCT JOBS — do not confuse them, and do not jump to writing code for the second one:\n" +
|
|
@@ -4573,6 +4573,19 @@ server.tool("publish_theme", "Publish the current theme LIVE — the same action
|
|
|
4573
4573
|
];
|
|
4574
4574
|
return callEditorAction(project_root, args);
|
|
4575
4575
|
});
|
|
4576
|
+
// Tool: get_runtime_warnings
|
|
4577
|
+
server.tool("get_runtime_warnings", "List problems the editor canvas caught at RUNTIME in this project's code components — things a clean build and type-check cannot see. Today that is code that loops without ever yielding to the event loop — `observer-loop` (a MutationObserver whose callback mutates the subtree it observes), `microtask-loop` (a queueMicrotask chain that keeps queueing itself) and `render-loop` (setState on every render from a deps-less useLayoutEffect). The runtime slows such a loop down to one step per macrotask so the page stays responsive, but the component is still wrong, still burns CPU, and storefronts published before the guard shipped freeze on it. Each warning has `componentName`, `componentId` (a shared chunk id when the code was split out), `message` (cause + fix), `count` and `lastSeenAt`. Call it after placing or editing a section that touches the DOM imperatively (MutationObserver, manual textContent/innerHTML writes), and again after your fix: a warning is dropped when its component is recompiled and comes back if the component is still broken. The same list is attached as `runtimeWarnings` to every other live-editor tool result, so you may see it without asking. Answered by the dev server itself — needs `ikas theme dev` running, but no particular editor tab.", {
|
|
4578
|
+
project_root: z
|
|
4579
|
+
.string()
|
|
4580
|
+
.describe("Absolute path to the code-component project (where `node_modules/.bin/ikas-component` lives)."),
|
|
4581
|
+
port: z
|
|
4582
|
+
.number()
|
|
4583
|
+
.optional()
|
|
4584
|
+
.describe("Dev server WebSocket port (default 5201)."),
|
|
4585
|
+
}, async ({ project_root, port }) => {
|
|
4586
|
+
const args = ["runtime-warnings", ...(port ? ["--port", String(port)] : [])];
|
|
4587
|
+
return callEditorAction(project_root, args);
|
|
4588
|
+
});
|
|
4576
4589
|
// Tool: get_editor_workflow
|
|
4577
4590
|
const EDITOR_WORKFLOW_GUIDE = [
|
|
4578
4591
|
"# Editor workflow: placing sections and filling their content on a page",
|