eslint-plugin-zappie 0.0.0-stage → 0.1.2
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/CHANGELOG.md +13 -0
- package/LICENSE +21 -0
- package/README.md +49 -2
- package/dist/index.d.ts +59 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# eslint-plugin-zappie
|
|
2
|
+
|
|
3
|
+
## 0.1.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 45295a2: Make product cards ready to tap with route discovery, prefetch lint rules and measured storefront checks.
|
|
8
|
+
|
|
9
|
+
## 0.1.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- eeccfed: First release on npm. `zappie check` and `zappie verify` now work in any project that installs these as dev dependencies. `@zappie/verify` takes `@playwright/test` as a peer dependency.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zappie
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,50 @@
|
|
|
1
|
-
#
|
|
1
|
+
# eslint-plugin-zappie
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
ESLint rules for Zappie storefronts. They catch the mistakes that break a storefront quietly: pricing outside the SDK, calling Zappie around it, building your own sign-in or checkout, sending analytics elsewhere, or leaving out the markers `zappie verify` looks for.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm install --save-dev eslint-plugin-zappie eslint
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
ESLint 9 is a peer dependency. The plugin uses flat config.
|
|
10
|
+
|
|
11
|
+
## Use it
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
// eslint.config.js
|
|
15
|
+
import zappie from "eslint-plugin-zappie";
|
|
16
|
+
|
|
17
|
+
export default [...zappie.configs.recommended];
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`recommended` turns every rule on as an error, except `verify-markers`, which warns. `strict` makes that one an error too.
|
|
21
|
+
|
|
22
|
+
## Rules
|
|
23
|
+
|
|
24
|
+
| Rule | What it asks for |
|
|
25
|
+
| ---------------------------- | ------------------------------------------------------------- |
|
|
26
|
+
| `zappie/no-direct-api` | Call Zappie through the SDK. |
|
|
27
|
+
| `zappie/no-money-literals` | Use SDK prices and totals in your storefront. |
|
|
28
|
+
| `zappie/use-format-money` | Format SDK Money with `format.money()` or `<Money>`. Fixable. |
|
|
29
|
+
| `zappie/cart-via-sdk` | Keep your cart state in the SDK. |
|
|
30
|
+
| `zappie/checkout-via-sdk` | Open checkout through the Zappie SDK. |
|
|
31
|
+
| `zappie/no-custom-auth` | Sign customers in through the Zappie SDK. |
|
|
32
|
+
| `zappie/no-tracking-scripts` | Forward analytics through Zappie. |
|
|
33
|
+
| `zappie/overlay-primitive` | Use Overlay to keep dialogs and sheets in the URL. |
|
|
34
|
+
| `zappie/verify-markers` | Mark shopping actions so verify can find them. Fixable. |
|
|
35
|
+
|
|
36
|
+
Typed rules use TypeScript's project service when it's available, and fall back to syntax checks when it isn't.
|
|
37
|
+
|
|
38
|
+
The rules give early feedback. They aren't a security boundary: Zappie re-prices every order on the server and runs checkout on its own site.
|
|
39
|
+
|
|
40
|
+
## Docs
|
|
41
|
+
|
|
42
|
+
- [Lint rules](https://docs.zappie.ai/lint)
|
|
43
|
+
|
|
44
|
+
## Status
|
|
45
|
+
|
|
46
|
+
The plugin is at `0.x`. Rules can be added or tightened in a minor release while it settles. Each change is listed in `CHANGELOG.md`.
|
|
47
|
+
|
|
48
|
+
## License
|
|
49
|
+
|
|
50
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as _typescript_eslint_utils_ts_eslint from '@typescript-eslint/utils/ts-eslint';
|
|
2
|
+
import { TSESLint } from '@typescript-eslint/utils';
|
|
3
|
+
|
|
4
|
+
declare const cartViaSdk: _typescript_eslint_utils_ts_eslint.RuleModule<"cart", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
5
|
+
|
|
6
|
+
declare const checkoutViaSdk: _typescript_eslint_utils_ts_eslint.RuleModule<"checkout", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
7
|
+
|
|
8
|
+
declare const imagePriority: TSESLint.RuleModule<"mapped" | "routeLimit", [], unknown, TSESLint.RuleListener>;
|
|
9
|
+
|
|
10
|
+
declare const itemLinkPrefetch: _typescript_eslint_utils_ts_eslint.RuleModule<"prefetch", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
11
|
+
|
|
12
|
+
type Options = [{
|
|
13
|
+
serverFiles?: string[];
|
|
14
|
+
}];
|
|
15
|
+
declare const noCustomAuth: _typescript_eslint_utils_ts_eslint.RuleModule<"auth", Options, unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
16
|
+
|
|
17
|
+
type Message$1 = "direct" | "internal" | "storefront" | "checkout" | "customer";
|
|
18
|
+
declare const noDirectApi: _typescript_eslint_utils_ts_eslint.RuleModule<Message$1, [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
19
|
+
|
|
20
|
+
declare const noMoneyLiterals: _typescript_eslint_utils_ts_eslint.RuleModule<"arithmetic" | "literal" | "scale", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
21
|
+
|
|
22
|
+
declare const noTrackingScripts: _typescript_eslint_utils_ts_eslint.RuleModule<"tracking", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
23
|
+
|
|
24
|
+
declare const overlayPrimitive: _typescript_eslint_utils_ts_eslint.RuleModule<"overlay", [{
|
|
25
|
+
keys?: string[];
|
|
26
|
+
}], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
27
|
+
|
|
28
|
+
type Message = "currency" | "direct" | "minor" | "useAlias";
|
|
29
|
+
declare const useFormatMoney: _typescript_eslint_utils_ts_eslint.RuleModule<Message, [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
30
|
+
|
|
31
|
+
declare const verifyMarkers: TSESLint.RuleModule<"marker", [], unknown, TSESLint.RuleListener>;
|
|
32
|
+
|
|
33
|
+
type ZappieFlatConfig = Record<string, unknown>;
|
|
34
|
+
declare const plugin: {
|
|
35
|
+
meta: {
|
|
36
|
+
name: string;
|
|
37
|
+
version: string;
|
|
38
|
+
};
|
|
39
|
+
rules: {
|
|
40
|
+
"no-direct-api": _typescript_eslint_utils_ts_eslint.RuleModule<"direct" | "internal" | "storefront" | "checkout" | "customer", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
41
|
+
"no-tracking-scripts": _typescript_eslint_utils_ts_eslint.RuleModule<"tracking", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
42
|
+
"checkout-via-sdk": _typescript_eslint_utils_ts_eslint.RuleModule<"checkout", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
43
|
+
"no-custom-auth": _typescript_eslint_utils_ts_eslint.RuleModule<"auth", [{
|
|
44
|
+
serverFiles?: string[];
|
|
45
|
+
}], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
46
|
+
"no-money-literals": _typescript_eslint_utils_ts_eslint.RuleModule<"arithmetic" | "literal" | "scale", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
47
|
+
"use-format-money": _typescript_eslint_utils_ts_eslint.RuleModule<"direct" | "currency" | "minor" | "useAlias", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
48
|
+
"overlay-primitive": _typescript_eslint_utils_ts_eslint.RuleModule<"overlay", [{
|
|
49
|
+
keys?: string[];
|
|
50
|
+
}], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
51
|
+
"cart-via-sdk": _typescript_eslint_utils_ts_eslint.RuleModule<"cart", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
52
|
+
"verify-markers": _typescript_eslint_utils_ts_eslint.RuleModule<"marker", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
53
|
+
"item-link-prefetch": _typescript_eslint_utils_ts_eslint.RuleModule<"prefetch", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
54
|
+
"image-priority": _typescript_eslint_utils_ts_eslint.RuleModule<"mapped" | "routeLimit", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
55
|
+
};
|
|
56
|
+
configs: Record<string, ZappieFlatConfig[]>;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export { type ZappieFlatConfig, cartViaSdk, checkoutViaSdk, plugin as default, imagePriority, itemLinkPrefetch, noCustomAuth, noDirectApi, noMoneyLiterals, noTrackingScripts, overlayPrimitive, useFormatMoney, verifyMarkers };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import Re from"@typescript-eslint/parser";import se from"eslint-plugin-jsx-a11y";import Je from"eslint-plugin-react-hooks";import{ESLintUtils as oe}from"@typescript-eslint/utils";var p=oe.RuleCreator(e=>`https://docs.zappie.ai/lint/${e}`);var pe=/^(cart|basket|bag)(Items|Lines|Count|Total)?$/i,ce=/cart|basket|bag/i;function b(e){if(e.type==="Identifier")return e.name;if(e.type==="MemberExpression"&&e.property.type==="Identifier")return e.property.name}function me(e){return e.type==="Identifier"?e.name:e.type==="ArrayPattern"&&e.elements[0]?.type==="Identifier"?e.elements[0].name:void 0}function le(e){let r=b(e.callee)==="useReducer"?e.arguments[1]:e.arguments[0];return!!(r&&r.type!=="SpreadElement"&&(r.type==="ArrayExpression"||r.type==="Literal"&&typeof r.value=="number"))}var F=p({name:"cart-via-sdk",meta:{type:"problem",docs:{description:"Keep your cart state in the SDK."},schema:[],messages:{cart:"Use useCart(), useCartLine() or the SDK client cart. Keep cart state and storage in the SDK."}},defaultOptions:[],create(e){return{CallExpression(r){let i=b(r.callee),t=r.arguments[0];["getItem","setItem","removeItem","open"].includes(i??"")&&t?.type==="Literal"&&typeof t.value=="string"&&ce.test(t.value)&&e.report({node:r,messageId:"cart"})},VariableDeclarator(r){if(r.init?.type!=="CallExpression")return;let i=me(r.id),t=b(r.init.callee);i&&pe.test(i)&&["useState","useReducer","createContext"].includes(t??"")&&le(r.init)&&e.report({node:r,messageId:"cart"}),i&&/cart/i.test(i)&&["create","createStore","atom"].includes(t??"")&&e.report({node:r,messageId:"cart"})}}}});import{minimatch as ue}from"minimatch";var fe=/(?:api|accounts|checkout|edge)\.zappie\.ai|\/storefront\/v1|\/customer\/graphql/i,de=/googletagmanager\.com|google-analytics\.com|connect\.facebook\.net|analytics\.tiktok\.com|static\.hotjar\.com|cdn\.segment\.com|plausible\.io|clarity\.ms/i,O=/checkout\.paystack\.com|flutterwave|(?:^|\.)stripe\.com/i;function d(e){if(e?.type==="Literal"&&typeof e.value=="string")return e.value;if(e?.type==="TemplateLiteral")return e.quasis.map(r=>r.value.cooked??r.value.raw).join("")}function l(e,r){return e.attributes.find(i=>i.type==="JSXAttribute"&&i.name.type==="JSXIdentifier"&&i.name.name.toLowerCase()===r.toLowerCase())}function u(e){if(e?.value){if(e.value.type==="Literal")return typeof e.value.value=="string"?e.value.value:void 0;if(e.value.type==="JSXExpressionContainer")return d(e.value.expression)}}var R=e=>fe.test(e),J=e=>de.test(e),X=e=>e==="@zappie/sdk/internal"||e.startsWith("@zappie/sdk/internal/");function P(e,r){let i=e.replaceAll("\\","/");return r.some(t=>ue(i,t,{matchBase:!0}))}var ye=new Set(["cc-number","cc-exp","cc-csc","cc-name"]),D=/card.?number|cvv|cvc|expiry/i;function Se(e){return e.startsWith("@paystack/")||e==="react-paystack"||e==="flutterwave-react-v3"||e.startsWith("@stripe/")}var K=p({name:"checkout-via-sdk",meta:{type:"problem",docs:{description:"Open checkout through the Zappie SDK."},schema:[],messages:{checkout:"Use createCheckout().open() from @zappie/sdk/checkout. Let checkout collect payment details."}},defaultOptions:[],create(e){return{ImportDeclaration(r){Se(r.source.value)&&e.report({node:r.source,messageId:"checkout"})},JSXOpeningElement(r){let i=r.name.type==="JSXIdentifier"?r.name.name.toLowerCase():"",t=u(l(r,"autocomplete")),a=u(l(r,"name")),n=u(l(r,"id")),s=u(l(r,i==="iframe"?"src":"href")),o=i==="input"&&(t!==void 0&&ye.has(t.toLowerCase())||a!==void 0&&D.test(a)||n!==void 0&&D.test(n)),c=(i==="iframe"||i==="a")&&s!==void 0&&O.test(s);(o||c)&&e.report({node:r,messageId:"checkout"})}}}});function ge(e){let r=e;for(;r?.parent;){let i=r.parent;if((i.type==="ArrowFunctionExpression"||i.type==="FunctionExpression")&&i.parent?.type==="CallExpression"&&i.parent.callee.type==="MemberExpression"&&i.parent.callee.property.type==="Identifier"&&i.parent.callee.property.name==="map")return i;r=i}}function he(e,r){let i=e.getText(r.parent);if(/\.slice\(\s*0\s*,\s*1\s*\)\.map/.test(i))return!0;let t=r.params[1];return t?.type!=="Identifier"?!1:new RegExp(`${t.name}\\s*(?:={2,3}\\s*0|<\\s*1)`).test(e.getText(r))}var U=p({name:"image-priority",meta:{type:"problem",docs:{description:"Reserve priority for the first phone screen."},schema:[],messages:{mapped:"Remove priority from repeated images or limit it to the first.",routeLimit:"Keep no more than two priority images in a route."}},defaultOptions:[],create(e){let r=[];return{JSXOpeningElement(i){let t=l(i,"priority"),a=t?.value?.type==="JSXExpressionContainer"&&t.value.expression.type==="Literal"&&t.value.expression.value===!1;if(i.name.type!=="JSXIdentifier"||i.name.name!=="Image"||!t||a)return;r.push(i);let n=ge(i);n&&!he(e.sourceCode,n)&&e.report({node:t,messageId:"mapped"})},"Program:exit"(){if(/(?:^|\/)routes?\/[^/]+\.[jt]sx$/.test(e.filename.replaceAll("\\","/"))&&r.length>2)for(let i of r.slice(2))e.report({node:i,messageId:"routeLimit"})}}}});function Te(e){let r=e?.value?.type==="JSXExpressionContainer"?e.value.expression:void 0;if(r?.type==="CallExpression"&&r.callee.type==="Identifier"&&r.callee.name==="itemHref")return!0;let i=u(e);return i!==void 0&&/^\/item(?:\/|$)/.test(i)}var $=p({name:"item-link-prefetch",meta:{type:"problem",docs:{description:"Prefetch item routes before shoppers tap."},schema:[],messages:{prefetch:'Add prefetch="viewport" so this item route is ready to tap.'}},defaultOptions:[],create(e){let r=new Set;return{ImportDeclaration(i){if(["react-router","react-router-dom"].includes(i.source.value))for(let t of i.specifiers)t.type==="ImportSpecifier"&&t.imported.type==="Identifier"&&["Link","NavLink"].includes(t.imported.name)&&r.add(t.local.name)},JSXOpeningElement(i){i.name.type==="JSXIdentifier"&&r.has(i.name.name)&&Te(l(i,"to"))&&!l(i,"prefetch")&&e.report({node:i,messageId:"prefetch"})}}}});var Ee=["**/*.server.*","**/server/**"],I=/otp|verify.?code|access.?token|refresh.?token|id.?token/i,B=new Set(["localStorage","sessionStorage"]),xe=new Set(["window","globalThis","self"]);function V(e){return e.type==="Identifier"?B.has(e.name):e.type==="MemberExpression"&&!e.computed&&e.object.type==="Identifier"&&xe.has(e.object.name)&&e.property.type==="Identifier"&&B.has(e.property.name)}function ke(e){return!e.computed&&e.property.type==="Identifier"?e.property.name:d(e.property)}function ve(e){if(e==="jose"||e==="jsonwebtoken")return"server";if(e==="next-auth"||e.startsWith("next-auth/")||e.startsWith("@auth/")||e==="better-auth"||e.startsWith("better-auth/")||e==="lucia"||e.startsWith("@clerk/")||e==="firebase/auth"||e.startsWith("@supabase/auth-"))return"all"}var q=p({name:"no-custom-auth",meta:{type:"problem",docs:{description:"Sign customers in through the Zappie SDK."},schema:[{type:"object",properties:{serverFiles:{type:"array",items:{type:"string"},uniqueItems:!0}},additionalProperties:!1}],messages:{auth:"Use createCustomer().signIn() from @zappie/sdk/customer to sign customers in."}},defaultOptions:[{}],create(e,[r]){let i=P(e.filename,r.serverFiles??Ee);return{ImportDeclaration(t){let a=ve(t.source.value);(a==="all"||a==="server"&&!i)&&e.report({node:t.source,messageId:"auth"})},JSXOpeningElement(t){if(t.name.type!=="JSXIdentifier"||t.name.name.toLowerCase()!=="input")return;let a=u(l(t,"type")),n=u(l(t,"autocomplete"));(a?.toLowerCase()==="password"||n!==void 0&&["one-time-code","current-password","new-password"].includes(n.toLowerCase()))&&e.report({node:t,messageId:"auth"})},CallExpression(t){if(t.callee.type!=="MemberExpression"||!V(t.callee.object))return;let a=t.arguments[0],n=a?.type==="Identifier"?a.name:d(a??null);n&&I.test(n)&&e.report({node:t,messageId:"auth"})},AssignmentExpression(t){if(t.left.type==="MemberExpression"&&V(t.left.object)){let s=ke(t.left);s&&I.test(s)&&e.report({node:t,messageId:"auth"});return}let a=t.left.type==="MemberExpression"&&t.left.object.type==="Identifier"&&t.left.object.name==="document"&&t.left.property.type==="Identifier"&&t.left.property.name==="cookie",n=d(t.right);a&&n&&I.test(n)&&e.report({node:t,messageId:"auth"})}}}});function be(e){return/checkout\.zappie\.ai|\/checkout(?:\/|$)/i.test(e)?"checkout":/accounts\.zappie\.ai|\/customer\/graphql/i.test(e)?"customer":/\/storefront\/v1/i.test(e)?"storefront":"direct"}var Z=p({name:"no-direct-api",meta:{type:"problem",docs:{description:"Call Zappie through the SDK."},schema:[],messages:{direct:"Call the matching public @zappie/sdk client to reach this Zappie endpoint.",internal:"Use a public SDK entry. Only Zappie-owned packages can import @zappie/sdk/internal.",storefront:"Use createZappie(config).store or .menu to read storefront data.",checkout:"Use createCheckout().open() from @zappie/sdk/checkout.",customer:"Use createCustomer() from @zappie/sdk/customer."}},defaultOptions:[],create(e){let r=i=>{if(i.parent.type==="ImportDeclaration")return;let t=d(i);t&&R(t)&&e.report({node:i,messageId:be(t)})};return{Literal:r,TemplateLiteral:r,ImportDeclaration(i){X(i.source.value)&&e.report({node:i.source,messageId:"internal"})}}}});import{ESLintUtils as Ie}from"@typescript-eslint/utils";var Le=/[₦€£]\s?\d|\$\s?\d|NGN\s?\d|\d+(?:\.\d{2})?\s?(?:naira|NGN)/i;function g(e){return e.trim()!=="$1"&&Le.test(e)}function f(e){if(e.type==="MemberExpression"){if(!e.computed&&e.property.type==="Identifier")return e.property.name;if(e.computed&&e.property.type==="Literal")return typeof e.property.value=="string"?e.property.value:void 0}}var S=e=>f(e)==="amountMinor";function we(e){return e.type==="MemberExpression"&&S(e)?e.object:void 0}function y(e){if(!(e.type!=="BinaryExpression"||e.operator!=="/"||e.right.type!=="Literal"||e.right.value!==100))return we(e.left)}function h(e){try{return Ie.getParserServices(e,!0)}catch{return}}function Ce(e){return e.getName().includes("MoneyBrand")?(e.getDeclarations()??[]).some(r=>{let i=r.getSourceFile().fileName.replaceAll("\\","/");return i.endsWith("/packages/sdk/src/money.ts")||/\/node_modules\/@zappie\/sdk\/.*\.d\.ts$/.test(i)}):!1}function T(e,r){if(!e?.program)return!1;let i=e.program.getTypeChecker(),t=e.esTreeNodeToTSNodeMap.get(r),a=i.getTypeAtLocation(t);return(a.isUnionOrIntersection()?a.types:[a]).some(s=>s.getProperties().some(o=>Ce(o)))}function E(e){return e?.type!=="ObjectExpression"?!1:e.properties.some(r=>r.type==="Property"&&!r.computed&&(r.key.type==="Identifier"&&r.key.name==="style"||r.key.type==="Literal"&&r.key.value==="style")&&r.value.type==="Literal"&&r.value.value==="currency")}var W=p({name:"no-money-literals",meta:{type:"problem",docs:{description:"Use SDK prices and totals in your storefront."},schema:[],messages:{arithmetic:"Use cart.subtotal, line.lineSubtotal, quote.subtotal, quote.lines totals or quote.total. Let the SDK calculate amounts.",literal:"Render SDK Money with format.money() or <Money> instead of a hard-coded amount.",scale:"Use format.money() to display amountMinor. Currencies use 0, 2, 3 or 4 minor-unit digits."}},defaultOptions:[],create(e){let r=h(e),i=t=>S(t)||T(r,t);return{Literal(t){typeof t.value=="string"&&t.parent.type!=="ImportDeclaration"&&g(t.value)&&e.report({node:t,messageId:"literal"})},JSXText(t){g(t.value)&&e.report({node:t,messageId:"literal"})},BinaryExpression(t){["+","-","*","/"].includes(t.operator)&&(y(t)&&t.parent.type==="MemberExpression"&&t.parent.object===t&&f(t.parent)==="toFixed"||(i(t.left)||i(t.right))&&e.report({node:t,messageId:"arithmetic"}))},TemplateLiteral(t){for(let a of t.quasis)g(a.value.cooked??a.value.raw)&&e.report({node:a,messageId:"literal"})},CallExpression(t){t.callee.type==="MemberExpression"&&f(t.callee)==="toFixed"&&t.arguments[0]?.type==="Literal"&&t.arguments[0].value===2&&y(t.callee.object)&&e.report({node:t,messageId:"scale"})}}}});var Me=["react-ga4","react-facebook-pixel","@vercel/analytics","posthog-js"];function Ae(e){return Me.includes(e)||e.startsWith("@segment/")}var _=p({name:"no-tracking-scripts",meta:{type:"problem",docs:{description:"Forward analytics through Zappie."},schema:[],messages:{tracking:"Configure analytics.forward in zappie.config.ts to forward analytics. Remove the tracking script."}},defaultOptions:[],create(e){let r=i=>{if(i.parent.type==="ImportDeclaration")return;let t=d(i);t&&J(t)&&e.report({node:i,messageId:"tracking"})};return{Literal:r,TemplateLiteral:r,ImportDeclaration(i){Ae(i.source.value)&&e.report({node:i.source,messageId:"tracking"})},CallExpression(i){let t=i.callee,a=t.type==="Identifier"&&["gtag","fbq"].includes(t.name),n=t.type==="MemberExpression"&&!t.computed&&t.object.type==="Identifier"&&t.object.name==="dataLayer"&&t.property.type==="Identifier"&&t.property.name==="push";(a||n)&&e.report({node:i,messageId:"tracking"})}}}});var Ne=["modal","sheet","drawer","dialog","item","cart","cart_sheet","cart_edit","cart_clear","search","catalogue_search","catalogue_categories","product_note","modal_id"];function L(e){if(e.type==="Identifier")return e.name;if(e.type==="MemberExpression"&&e.property.type==="Identifier")return e.property.name}function H(e,r){return e.type==="Literal"&&typeof e.value=="string"?[...r].some(i=>new RegExp(`[?&]${i}=`).test(e.value)):e.type==="TemplateLiteral"?e.quasis.some(i=>H({...i,type:"Literal",value:i.value.raw},r)):e.type==="ObjectExpression"?e.properties.some(i=>i.type==="Property"&&(i.key.type==="Identifier"&&r.has(i.key.name)||i.key.type==="Literal"&&typeof i.key.value=="string"&&r.has(i.key.value))):!1}var G=p({name:"overlay-primitive",meta:{type:"problem",docs:{description:"Use Overlay to keep dialogs and sheets in the URL."},schema:[{type:"object",properties:{keys:{type:"array",items:{type:"string"}}},additionalProperties:!1}],messages:{overlay:"Use Overlay or useOverlay for dialogs and sheets in the URL. They handle Back, Forward and focus restoration."}},defaultOptions:[{}],create(e,[r]){let i=new Set([...Ne,...r.keys??[]]),t=new Set,a=n=>{let s=e.sourceCode.getText(n);return s.includes("useSearchParams(")||[...t].some(o=>new RegExp(`\\b${o}\\b`).test(s))};return{VariableDeclarator(n){n.init?.type==="CallExpression"&&L(n.init.callee)==="useSearchParams"?n.id.type==="Identifier"?t.add(n.id.name):n.id.type==="ArrayPattern"&&n.id.elements[0]?.type==="Identifier"&&t.add(n.id.elements[0].name):n.id.type==="Identifier"&&n.init&&a(n.init)&&t.add(n.id.name)},CallExpression(n){let s=L(n.callee);(s==="setSearchParams"||s==="navigate"||s==="pushState"||s==="replaceState"||n.callee.type==="CallExpression"&&L(n.callee.callee)==="useNavigate")&&n.arguments.some(c=>c.type!=="SpreadElement"&&H(c,i))&&e.report({node:n,messageId:"overlay"})},JSXOpeningElement(n){let s=n.name.type==="JSXIdentifier"&&n.name.name==="dialog",o=n.attributes.some(m=>m.type==="JSXAttribute"&&m.name.name==="role"&&m.value?.type==="Literal"&&m.value.value==="dialog"),c=n.attributes.find(m=>m.type==="JSXAttribute"&&m.name.name==="open");(s||o)&&c?.type==="JSXAttribute"&&c.value?.type==="JSXExpressionContainer"&&a(c.value.expression)&&e.report({node:n,messageId:"overlay"})}}}});function w(e){return e.body.find(r=>r.type==="ImportDeclaration"&&r.source.value==="@zappie/sdk")}function C(e){return e?.specifiers.find(r=>r.type==="ImportSpecifier"&&r.imported.type==="Identifier"&&r.imported.name==="format")}function je(e,r,i,t){let a=w(i);if(C(a)?.local.name===t)return;let n=t==="format"?"format":`format as ${t}`,s=a?.specifiers.filter(m=>m.type==="ImportSpecifier");if(a&&a.importKind!=="type"&&s?.length){let m=s.at(-1);if(m)return e.insertTextAfter(m,`, ${n}`)}let o=i.body[0],c=`import { ${n} } from "@zappie/sdk";
|
|
2
|
+
`;return o?e.insertTextBefore(o,c):e.insertTextAfter(i,c)}function M(e,r,i,t,a,n){let s=[e.replaceText(t,`${n}.money(${r.getText(a)})`)],o=je(e,r,i,n);return o&&s.push(o),s}function Q(e,r,i){if(C(w(r)))return"imported";let t=e.getScope(i);for(;t;){if(t.variables.some(a=>a.name==="format"))return"conflict";t=t.upper}return"available"}function Y(e){return C(w(e))?.local.name}function ee(e,r){let i=new Set,t=e.getScope(r);for(;t;){for(let a of t.variables)i.add(a.name);t=t.upper}for(let a of["zappieFormat","zappieMoneyFormat","formatMoneyValue"])if(!i.has(a))return a;return"zappieSdkFormat"}function te(e){let r=e.type==="NewExpression"||e.type==="CallExpression"?e.callee:void 0;return r?.type==="MemberExpression"&&r.object.type==="Identifier"&&r.object.name==="Intl"&&f(r)==="NumberFormat"}function ze(e){if(e.callee.type==="MemberExpression"){if(f(e.callee)==="toLocaleString")return y(e.callee.object);if(f(e.callee)==="format")return y(e.arguments[0]??e)}}function Fe(e){let r=e.parent,i=r.type==="MemberExpression"?r.parent:void 0;return!!(i?.type==="CallExpression"&&i.callee===r&&f(r)==="format"&&y(i.arguments[0]??i))}var re=p({name:"use-format-money",meta:{type:"problem",docs:{description:"Format SDK Money with format.money() or <Money>."},fixable:"code",hasSuggestions:!0,schema:[],messages:{currency:"Use format.money(). Intl currency defaults can differ from the API minor-unit digits.",direct:"Render SDK Money with format.money(value) or <Money value={value}>.",minor:"Use format.money(value). Dividing by 100 gives the wrong amount for currencies with 0, 3 or 4 minor-unit digits.",useAlias:"Import format with an unused alias and format this Money value."}},defaultOptions:[],create(e){let r=e.sourceCode,i=h(e),t,a=(s,o,c)=>{let m={node:s,messageId:"minor"};if(Q(r,t,s)==="conflict"){let k=ee(r,s);e.report({...m,suggest:[{messageId:"useAlias",fix:v=>M(v,r,t,o,c,k)}]});return}e.report({...m,fix:k=>{let v=Y(t)??"format";return M(k,r,t,o,c,v)}})},n=s=>{let o=ze(s);return o?(a(s,s,o),!0):!1};return{Program(s){t=s},NewExpression(s){te(s)&&E(s.arguments[1])&&!Fe(s)&&e.report({node:s,messageId:"currency"})},CallExpression(s){if(!n(s)){if(te(s)&&E(s.arguments[1])){e.report({node:s,messageId:"currency"});return}s.callee.type==="MemberExpression"&&f(s.callee)==="toLocaleString"&&s.arguments.some(E)&&e.report({node:s,messageId:"currency"})}},TemplateLiteral(s){for(let o of s.expressions){let c=y(o);c?a(o,o,c):S(o)&&e.report({node:o,messageId:"minor"})}},JSXExpressionContainer(s){s.parent.type==="JSXAttribute"||s.expression.type==="JSXEmptyExpression"||(y(s.expression)||S(s.expression)?e.report({node:s.expression,messageId:"minor"}):T(i,s.expression)&&e.report({node:s.expression,messageId:"direct"}))}}}});var A={add:{name:"data-zappie-action",text:'data-zappie-action="add-to-cart"'},checkout:{name:"data-zappie-checkout",text:"data-zappie-checkout"},signin:{name:"data-zappie-signin",text:"data-zappie-signin"}};function Oe(e,r){let i=r.value?e.getText(r.value):"";if(/\b(?:cart(?:\.actions)?\.add)\s*\(/.test(i))return"add";if(/\b(?:checkout|createCheckout\([^)]*\))\.open\s*\(/.test(i))return"checkout";if(/\b(?:auth|createCustomer\([^)]*\))\.signIn\s*\(/.test(i))return"signin"}var ie=p({name:"verify-markers",meta:{type:"suggestion",docs:{description:"Mark shopping actions so verify can find them."},fixable:"code",schema:[],messages:{marker:"Add {{marker}} so zappie verify can find this shopping action."}},defaultOptions:[],create(e){return{JSXOpeningElement(r){let i=r.attributes.find(a=>a.type==="JSXAttribute"&&a.name.name==="onClick");if(!i)return;let t=Oe(e.sourceCode,i);!t||r.attributes.some(a=>a.type==="JSXAttribute"&&a.name.name===A[t].name)||e.report({node:i,messageId:"marker",data:{marker:A[t].text},fix:a=>a.insertTextAfter(r.name,` ${A[t].text}`)})}}}});var ae={"no-direct-api":Z,"no-tracking-scripts":_,"checkout-via-sdk":K,"no-custom-auth":q,"no-money-literals":W,"use-format-money":re,"overlay-primitive":G,"cart-via-sdk":F,"verify-markers":ie,"item-link-prefetch":$,"image-priority":U},ne=Object.fromEntries(Object.keys(ae).map(e=>[`zappie/${e}`,e==="verify-markers"?"warn":"error"])),Xe={...ne,"zappie/verify-markers":"error"},z={meta:{name:"eslint-plugin-zappie",version:"0.0.0"},rules:ae,configs:{}},x={name:"zappie/rules",files:["**/*.{js,jsx,ts,tsx,mjs,cjs,mts,cts}"],languageOptions:{parser:Re},plugins:{zappie:z,"jsx-a11y":se,"react-hooks":Je},rules:{...se.flatConfigs.recommended.rules,"react-hooks/rules-of-hooks":"error",...ne}},N={name:"zappie/search-dialogs",files:["**/search/**/*.{jsx,tsx}","**/*search*.{jsx,tsx}"],rules:{"jsx-a11y/no-autofocus":"off"}},j={name:"zappie/typed",files:["**/*.{ts,tsx,mts,cts}"],languageOptions:{parserOptions:{projectService:!0}}};z.configs={recommended:[x,j,N],strict:[{...x,rules:{...x.rules,...Xe}},j,N],"zappie-internal":[x,j,N,{name:"zappie/internal-sdk",files:["packages/sdk/src/**/*.{ts,tsx}"],rules:{"zappie/no-direct-api":"off"}},{name:"zappie/internal-auth-ui",files:["packages/foundations/src/otp/**/*.{ts,tsx}"],rules:{"zappie/no-custom-auth":"off"}},{name:"zappie/internal-money",files:["packages/sdk/src/money.ts","packages/sdk/src/internal/money-math.ts","packages/sdk/src/cart/branch.ts"],rules:{"zappie/no-money-literals":"off","zappie/use-format-money":"off"}},{name:"zappie/internal-overlay",files:["packages/foundations/src/overlay/**/*.{ts,tsx}"],rules:{"zappie/overlay-primitive":"off"}},{name:"zappie/internal-overlay-tests",files:["themes/base/src/cart/cart-review.test.tsx","themes/base/src/cart/core-cart.test.tsx","themes/base/src/cart/fulfilment.test.tsx","themes/base/src/cart/theme-class.test.tsx"],rules:{"zappie/overlay-primitive":"off"}}]};var Xt=z;export{F as cartViaSdk,K as checkoutViaSdk,Xt as default,U as imagePriority,$ as itemLinkPrefetch,q as noCustomAuth,Z as noDirectApi,W as noMoneyLiterals,_ as noTrackingScripts,G as overlayPrimitive,re as useFormatMoney,ie as verifyMarkers};
|
|
3
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/create-rule.ts","../src/rules/cart-via-sdk.ts","../src/matchers.ts","../src/rules/checkout-via-sdk.ts","../src/rules/image-priority.ts","../src/rules/item-link-prefetch.ts","../src/rules/no-custom-auth.ts","../src/rules/no-direct-api.ts","../src/money-rule-utils.ts","../src/rules/no-money-literals.ts","../src/rules/no-tracking-scripts.ts","../src/rules/overlay-primitive.ts","../src/format-fix.ts","../src/rules/use-format-money.ts","../src/rules/verify-markers.ts"],"sourcesContent":["import parser from \"@typescript-eslint/parser\";\nimport jsxA11y from \"eslint-plugin-jsx-a11y\";\nimport reactHooks from \"eslint-plugin-react-hooks\";\nimport { cartViaSdk } from \"./rules/cart-via-sdk\";\nimport { checkoutViaSdk } from \"./rules/checkout-via-sdk\";\nimport { imagePriority } from \"./rules/image-priority\";\nimport { itemLinkPrefetch } from \"./rules/item-link-prefetch\";\nimport { noCustomAuth } from \"./rules/no-custom-auth\";\nimport { noDirectApi } from \"./rules/no-direct-api\";\nimport { noMoneyLiterals } from \"./rules/no-money-literals\";\nimport { noTrackingScripts } from \"./rules/no-tracking-scripts\";\nimport { overlayPrimitive } from \"./rules/overlay-primitive\";\nimport { useFormatMoney } from \"./rules/use-format-money\";\nimport { verifyMarkers } from \"./rules/verify-markers\";\n\nconst rules = {\n \"no-direct-api\": noDirectApi,\n \"no-tracking-scripts\": noTrackingScripts,\n \"checkout-via-sdk\": checkoutViaSdk,\n \"no-custom-auth\": noCustomAuth,\n \"no-money-literals\": noMoneyLiterals,\n \"use-format-money\": useFormatMoney,\n \"overlay-primitive\": overlayPrimitive,\n \"cart-via-sdk\": cartViaSdk,\n \"verify-markers\": verifyMarkers,\n \"item-link-prefetch\": itemLinkPrefetch,\n \"image-priority\": imagePriority,\n};\n\nexport type ZappieFlatConfig = Record<string, unknown>;\n\nconst enabled = Object.fromEntries(\n Object.keys(rules).map((name) => [\n `zappie/${name}`,\n name === \"verify-markers\" ? \"warn\" : \"error\",\n ]),\n);\nconst strictRules = { ...enabled, \"zappie/verify-markers\": \"error\" };\n\nconst plugin = {\n meta: { name: \"eslint-plugin-zappie\", version: \"0.0.0\" },\n rules,\n configs: {} as Record<string, ZappieFlatConfig[]>,\n};\n\nconst base = {\n name: \"zappie/rules\",\n files: [\"**/*.{js,jsx,ts,tsx,mjs,cjs,mts,cts}\"],\n languageOptions: { parser },\n plugins: { zappie: plugin, \"jsx-a11y\": jsxA11y, \"react-hooks\": reactHooks },\n rules: {\n ...jsxA11y.flatConfigs.recommended.rules,\n \"react-hooks/rules-of-hooks\": \"error\",\n ...enabled,\n },\n};\nconst searchDialogs = {\n name: \"zappie/search-dialogs\",\n files: [\"**/search/**/*.{jsx,tsx}\", \"**/*search*.{jsx,tsx}\"],\n rules: { \"jsx-a11y/no-autofocus\": \"off\" },\n};\n\n// JavaScript files (eslint.config.mjs among them) usually sit outside the\n// tsconfig, where the project service refuses to parse them. They get the\n// syntactic money checks only.\nconst typed = {\n name: \"zappie/typed\",\n files: [\"**/*.{ts,tsx,mts,cts}\"],\n languageOptions: { parserOptions: { projectService: true } },\n};\n\nplugin.configs = {\n recommended: [base, typed, searchDialogs],\n strict: [\n { ...base, rules: { ...base.rules, ...strictRules } },\n typed,\n searchDialogs,\n ],\n \"zappie-internal\": [\n base,\n typed,\n searchDialogs,\n {\n name: \"zappie/internal-sdk\",\n files: [\"packages/sdk/src/**/*.{ts,tsx}\"],\n rules: { \"zappie/no-direct-api\": \"off\" },\n },\n {\n name: \"zappie/internal-auth-ui\",\n files: [\"packages/foundations/src/otp/**/*.{ts,tsx}\"],\n rules: { \"zappie/no-custom-auth\": \"off\" },\n },\n {\n name: \"zappie/internal-money\",\n files: [\n \"packages/sdk/src/money.ts\",\n \"packages/sdk/src/internal/money-math.ts\",\n // Its own arithmetic: importing money-math here splits it out of the core bundle.\n \"packages/sdk/src/cart/branch.ts\",\n ],\n rules: {\n \"zappie/no-money-literals\": \"off\",\n \"zappie/use-format-money\": \"off\",\n },\n },\n {\n name: \"zappie/internal-overlay\",\n files: [\"packages/foundations/src/overlay/**/*.{ts,tsx}\"],\n rules: { \"zappie/overlay-primitive\": \"off\" },\n },\n {\n name: \"zappie/internal-overlay-tests\",\n files: [\n \"themes/base/src/cart/cart-review.test.tsx\",\n \"themes/base/src/cart/core-cart.test.tsx\",\n \"themes/base/src/cart/fulfilment.test.tsx\",\n \"themes/base/src/cart/theme-class.test.tsx\",\n ],\n rules: { \"zappie/overlay-primitive\": \"off\" },\n },\n ],\n};\n\nexport {\n cartViaSdk,\n checkoutViaSdk,\n imagePriority,\n itemLinkPrefetch,\n noCustomAuth,\n noDirectApi,\n noMoneyLiterals,\n noTrackingScripts,\n overlayPrimitive,\n useFormatMoney,\n verifyMarkers,\n};\nexport default plugin;\n","import { ESLintUtils } from \"@typescript-eslint/utils\";\n\nexport const createRule = ESLintUtils.RuleCreator(\n (name) => `https://docs.zappie.ai/lint/${name}`,\n);\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\n\nconst cartName = /^(cart|basket|bag)(Items|Lines|Count|Total)?$/i;\nconst storageKey = /cart|basket|bag/i;\n\nfunction calleeName(node: TSESTree.Node): string | undefined {\n if (node.type === \"Identifier\") {\n return node.name;\n }\n if (node.type === \"MemberExpression\" && node.property.type === \"Identifier\") {\n return node.property.name;\n }\n return undefined;\n}\n\nfunction variableName(node: TSESTree.BindingName): string | undefined {\n if (node.type === \"Identifier\") {\n return node.name;\n }\n return node.type === \"ArrayPattern\" && node.elements[0]?.type === \"Identifier\"\n ? node.elements[0].name\n : undefined;\n}\n\nfunction cartInitial(node: TSESTree.CallExpression): boolean {\n const value =\n calleeName(node.callee) === \"useReducer\"\n ? node.arguments[1]\n : node.arguments[0];\n return Boolean(\n value &&\n value.type !== \"SpreadElement\" &&\n (value.type === \"ArrayExpression\" ||\n (value.type === \"Literal\" && typeof value.value === \"number\")),\n );\n}\n\nexport const cartViaSdk = createRule<[], \"cart\">({\n name: \"cart-via-sdk\",\n meta: {\n type: \"problem\",\n docs: { description: \"Keep your cart state in the SDK.\" },\n schema: [],\n messages: {\n cart: \"Use useCart(), useCartLine() or the SDK client cart. Keep cart state and storage in the SDK.\",\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n CallExpression(node) {\n const method = calleeName(node.callee);\n const key = node.arguments[0];\n if (\n [\"getItem\", \"setItem\", \"removeItem\", \"open\"].includes(method ?? \"\") &&\n key?.type === \"Literal\" &&\n typeof key.value === \"string\" &&\n storageKey.test(key.value)\n ) {\n context.report({ node, messageId: \"cart\" });\n }\n },\n VariableDeclarator(node) {\n if (node.init?.type !== \"CallExpression\") {\n return;\n }\n const id = variableName(node.id);\n const call = calleeName(node.init.callee);\n if (\n id &&\n cartName.test(id) &&\n [\"useState\", \"useReducer\", \"createContext\"].includes(call ?? \"\") &&\n cartInitial(node.init)\n ) {\n context.report({ node, messageId: \"cart\" });\n }\n if (\n id &&\n /cart/i.test(id) &&\n [\"create\", \"createStore\", \"atom\"].includes(call ?? \"\")\n ) {\n context.report({ node, messageId: \"cart\" });\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { minimatch } from \"minimatch\";\n\nconst endpointPattern =\n /(?:api|accounts|checkout|edge)\\.zappie\\.ai|\\/storefront\\/v1|\\/customer\\/graphql/i;\nconst trackingPattern =\n /googletagmanager\\.com|google-analytics\\.com|connect\\.facebook\\.net|analytics\\.tiktok\\.com|static\\.hotjar\\.com|cdn\\.segment\\.com|plausible\\.io|clarity\\.ms/i;\n\nexport const paymentHostPattern =\n /checkout\\.paystack\\.com|flutterwave|(?:^|\\.)stripe\\.com/i;\n\nexport function staticText(node: TSESTree.Node | null): string | undefined {\n if (node?.type === \"Literal\" && typeof node.value === \"string\") {\n return node.value;\n }\n if (node?.type === \"TemplateLiteral\") {\n return node.quasis\n .map((part) => part.value.cooked ?? part.value.raw)\n .join(\"\");\n }\n return undefined;\n}\n\nexport function jsxAttribute(\n node: TSESTree.JSXOpeningElement,\n name: string,\n): TSESTree.JSXAttribute | undefined {\n return node.attributes.find(\n (attribute): attribute is TSESTree.JSXAttribute =>\n attribute.type === \"JSXAttribute\" &&\n attribute.name.type === \"JSXIdentifier\" &&\n attribute.name.name.toLowerCase() === name.toLowerCase(),\n );\n}\n\nexport function jsxValue(node?: TSESTree.JSXAttribute): string | undefined {\n if (!node?.value) {\n return undefined;\n }\n if (node.value.type === \"Literal\") {\n return typeof node.value.value === \"string\" ? node.value.value : undefined;\n }\n if (node.value.type === \"JSXExpressionContainer\") {\n return staticText(node.value.expression);\n }\n return undefined;\n}\n\nexport const isZappieEndpoint = (value: string) => endpointPattern.test(value);\nexport const isTrackingEndpoint = (value: string) =>\n trackingPattern.test(value);\nexport const isInternalSdkImport = (value: string) =>\n value === \"@zappie/sdk/internal\" || value.startsWith(\"@zappie/sdk/internal/\");\n\nexport function isServerFile(filename: string, patterns: string[]): boolean {\n const path = filename.replaceAll(\"\\\\\", \"/\");\n return patterns.some((pattern) =>\n minimatch(path, pattern, { matchBase: true }),\n );\n}\n","import { createRule } from \"../create-rule\";\nimport { jsxAttribute, jsxValue, paymentHostPattern } from \"../matchers\";\n\nconst autocomplete = new Set([\"cc-number\", \"cc-exp\", \"cc-csc\", \"cc-name\"]);\nconst paymentField = /card.?number|cvv|cvc|expiry/i;\n\nfunction providerImport(value: string): boolean {\n return (\n value.startsWith(\"@paystack/\") ||\n value === \"react-paystack\" ||\n value === \"flutterwave-react-v3\" ||\n value.startsWith(\"@stripe/\")\n );\n}\n\nexport const checkoutViaSdk = createRule<[], \"checkout\">({\n name: \"checkout-via-sdk\",\n meta: {\n type: \"problem\",\n docs: { description: \"Open checkout through the Zappie SDK.\" },\n schema: [],\n messages: {\n checkout:\n \"Use createCheckout().open() from @zappie/sdk/checkout. Let checkout collect payment details.\",\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n ImportDeclaration(node) {\n if (providerImport(node.source.value)) {\n context.report({ node: node.source, messageId: \"checkout\" });\n }\n },\n JSXOpeningElement(node) {\n const tag =\n node.name.type === \"JSXIdentifier\"\n ? node.name.name.toLowerCase()\n : \"\";\n const auto = jsxValue(jsxAttribute(node, \"autocomplete\"));\n const name = jsxValue(jsxAttribute(node, \"name\"));\n const id = jsxValue(jsxAttribute(node, \"id\"));\n const target = jsxValue(\n jsxAttribute(node, tag === \"iframe\" ? \"src\" : \"href\"),\n );\n const cardInput =\n tag === \"input\" &&\n ((auto !== undefined && autocomplete.has(auto.toLowerCase())) ||\n (name !== undefined && paymentField.test(name)) ||\n (id !== undefined && paymentField.test(id)));\n const providerTarget =\n (tag === \"iframe\" || tag === \"a\") &&\n target !== undefined &&\n paymentHostPattern.test(target);\n if (cardInput || providerTarget) {\n context.report({ node, messageId: \"checkout\" });\n }\n },\n };\n },\n});\n","import type { TSESLint, TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport { jsxAttribute } from \"../matchers\";\n\ntype Callback = TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression;\n\nfunction mappedBy(node: TSESTree.Node) {\n let current: TSESTree.Node | undefined = node;\n while (current?.parent) {\n const parent: TSESTree.Node = current.parent;\n if (\n (parent.type === \"ArrowFunctionExpression\" ||\n parent.type === \"FunctionExpression\") &&\n parent.parent?.type === \"CallExpression\" &&\n parent.parent.callee.type === \"MemberExpression\" &&\n parent.parent.callee.property.type === \"Identifier\" &&\n parent.parent.callee.property.name === \"map\"\n ) {\n return parent;\n }\n current = parent;\n }\n}\n\nfunction firstOnly(source: Readonly<TSESLint.SourceCode>, callback: Callback) {\n const text = source.getText(callback.parent);\n if (/\\.slice\\(\\s*0\\s*,\\s*1\\s*\\)\\.map/.test(text)) {\n return true;\n }\n const index = callback.params[1];\n if (index?.type !== \"Identifier\") {\n return false;\n }\n const first = new RegExp(`${index.name}\\\\s*(?:={2,3}\\\\s*0|<\\\\s*1)`);\n return first.test(source.getText(callback));\n}\n\nexport const imagePriority = createRule<[], \"mapped\" | \"routeLimit\">({\n name: \"image-priority\",\n meta: {\n type: \"problem\",\n docs: { description: \"Reserve priority for the first phone screen.\" },\n schema: [],\n messages: {\n mapped: \"Remove priority from repeated images or limit it to the first.\",\n routeLimit: \"Keep no more than two priority images in a route.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const images: TSESTree.JSXOpeningElement[] = [];\n return {\n JSXOpeningElement(node) {\n const priority = jsxAttribute(node, \"priority\");\n const disabled =\n priority?.value?.type === \"JSXExpressionContainer\" &&\n priority.value.expression.type === \"Literal\" &&\n priority.value.expression.value === false;\n if (\n node.name.type !== \"JSXIdentifier\" ||\n node.name.name !== \"Image\" ||\n !priority ||\n disabled\n ) {\n return;\n }\n images.push(node);\n const callback = mappedBy(node);\n if (callback && !firstOnly(context.sourceCode, callback)) {\n context.report({ node: priority, messageId: \"mapped\" });\n }\n },\n \"Program:exit\"() {\n if (\n /(?:^|\\/)routes?\\/[^/]+\\.[jt]sx$/.test(\n context.filename.replaceAll(\"\\\\\", \"/\"),\n ) &&\n images.length > 2\n ) {\n for (const node of images.slice(2)) {\n context.report({ node, messageId: \"routeLimit\" });\n }\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport { jsxAttribute, jsxValue } from \"../matchers\";\n\nfunction targetsItem(attribute?: TSESTree.JSXAttribute) {\n const expression =\n attribute?.value?.type === \"JSXExpressionContainer\"\n ? attribute.value.expression\n : undefined;\n if (\n expression?.type === \"CallExpression\" &&\n expression.callee.type === \"Identifier\" &&\n expression.callee.name === \"itemHref\"\n ) {\n return true;\n }\n const target = jsxValue(attribute);\n return target !== undefined && /^\\/item(?:\\/|$)/.test(target);\n}\n\nexport const itemLinkPrefetch = createRule<[], \"prefetch\">({\n name: \"item-link-prefetch\",\n meta: {\n type: \"problem\",\n docs: { description: \"Prefetch item routes before shoppers tap.\" },\n schema: [],\n messages: {\n prefetch: 'Add prefetch=\"viewport\" so this item route is ready to tap.',\n },\n },\n defaultOptions: [],\n create(context) {\n const links = new Set<string>();\n return {\n ImportDeclaration(node) {\n if (![\"react-router\", \"react-router-dom\"].includes(node.source.value)) {\n return;\n }\n for (const item of node.specifiers) {\n if (\n item.type === \"ImportSpecifier\" &&\n item.imported.type === \"Identifier\" &&\n [\"Link\", \"NavLink\"].includes(item.imported.name)\n ) {\n links.add(item.local.name);\n }\n }\n },\n JSXOpeningElement(node) {\n if (\n node.name.type === \"JSXIdentifier\" &&\n links.has(node.name.name) &&\n targetsItem(jsxAttribute(node, \"to\")) &&\n !jsxAttribute(node, \"prefetch\")\n ) {\n context.report({ node, messageId: \"prefetch\" });\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport { isServerFile, jsxAttribute, jsxValue, staticText } from \"../matchers\";\n\ntype Options = [{ serverFiles?: string[] }];\nconst defaults = [\"**/*.server.*\", \"**/server/**\"];\nconst authValue = /otp|verify.?code|access.?token|refresh.?token|id.?token/i;\nconst browserStorage = new Set([\"localStorage\", \"sessionStorage\"]);\nconst globals = new Set([\"window\", \"globalThis\", \"self\"]);\n\nfunction isStorage(node: TSESTree.Node): boolean {\n if (node.type === \"Identifier\") {\n return browserStorage.has(node.name);\n }\n return (\n node.type === \"MemberExpression\" &&\n !node.computed &&\n node.object.type === \"Identifier\" &&\n globals.has(node.object.name) &&\n node.property.type === \"Identifier\" &&\n browserStorage.has(node.property.name)\n );\n}\n\nfunction memberKey(node: TSESTree.MemberExpression): string | undefined {\n if (!node.computed && node.property.type === \"Identifier\") {\n return node.property.name;\n }\n return staticText(node.property);\n}\n\nfunction authImport(value: string): \"server\" | \"all\" | undefined {\n if (value === \"jose\" || value === \"jsonwebtoken\") {\n return \"server\";\n }\n if (\n value === \"next-auth\" ||\n value.startsWith(\"next-auth/\") ||\n value.startsWith(\"@auth/\") ||\n value === \"better-auth\" ||\n value.startsWith(\"better-auth/\") ||\n value === \"lucia\" ||\n value.startsWith(\"@clerk/\") ||\n value === \"firebase/auth\" ||\n value.startsWith(\"@supabase/auth-\")\n ) {\n return \"all\";\n }\n return undefined;\n}\n\nexport const noCustomAuth = createRule<Options, \"auth\">({\n name: \"no-custom-auth\",\n meta: {\n type: \"problem\",\n docs: {\n description: \"Sign customers in through the Zappie SDK.\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n serverFiles: {\n type: \"array\",\n items: { type: \"string\" },\n uniqueItems: true,\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n auth: \"Use createCustomer().signIn() from @zappie/sdk/customer to sign customers in.\",\n },\n },\n defaultOptions: [{}],\n create(context, [options]) {\n const server = isServerFile(\n context.filename,\n options.serverFiles ?? defaults,\n );\n return {\n ImportDeclaration(node) {\n const kind = authImport(node.source.value);\n if (kind === \"all\" || (kind === \"server\" && !server)) {\n context.report({ node: node.source, messageId: \"auth\" });\n }\n },\n JSXOpeningElement(node) {\n if (\n node.name.type !== \"JSXIdentifier\" ||\n node.name.name.toLowerCase() !== \"input\"\n ) {\n return;\n }\n const type = jsxValue(jsxAttribute(node, \"type\"));\n const auto = jsxValue(jsxAttribute(node, \"autocomplete\"));\n if (\n type?.toLowerCase() === \"password\" ||\n (auto !== undefined &&\n [\"one-time-code\", \"current-password\", \"new-password\"].includes(\n auto.toLowerCase(),\n ))\n ) {\n context.report({ node, messageId: \"auth\" });\n }\n },\n CallExpression(node) {\n if (\n node.callee.type !== \"MemberExpression\" ||\n !isStorage(node.callee.object)\n ) {\n return;\n }\n const first = node.arguments[0];\n const value =\n first?.type === \"Identifier\" ? first.name : staticText(first ?? null);\n if (value && authValue.test(value)) {\n context.report({ node, messageId: \"auth\" });\n }\n },\n AssignmentExpression(node) {\n if (\n node.left.type === \"MemberExpression\" &&\n isStorage(node.left.object)\n ) {\n const key = memberKey(node.left);\n if (key && authValue.test(key)) {\n context.report({ node, messageId: \"auth\" });\n }\n return;\n }\n const cookie =\n node.left.type === \"MemberExpression\" &&\n node.left.object.type === \"Identifier\" &&\n node.left.object.name === \"document\" &&\n node.left.property.type === \"Identifier\" &&\n node.left.property.name === \"cookie\";\n const value = staticText(node.right);\n if (cookie && value && authValue.test(value)) {\n context.report({ node, messageId: \"auth\" });\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport { isInternalSdkImport, isZappieEndpoint, staticText } from \"../matchers\";\n\ntype Message = \"direct\" | \"internal\" | \"storefront\" | \"checkout\" | \"customer\";\n\nfunction message(value: string): Message {\n if (/checkout\\.zappie\\.ai|\\/checkout(?:\\/|$)/i.test(value)) {\n return \"checkout\";\n }\n if (/accounts\\.zappie\\.ai|\\/customer\\/graphql/i.test(value)) {\n return \"customer\";\n }\n if (/\\/storefront\\/v1/i.test(value)) {\n return \"storefront\";\n }\n return \"direct\";\n}\n\nexport const noDirectApi = createRule<[], Message>({\n name: \"no-direct-api\",\n meta: {\n type: \"problem\",\n docs: { description: \"Call Zappie through the SDK.\" },\n schema: [],\n messages: {\n direct:\n \"Call the matching public @zappie/sdk client to reach this Zappie endpoint.\",\n internal:\n \"Use a public SDK entry. Only Zappie-owned packages can import @zappie/sdk/internal.\",\n storefront:\n \"Use createZappie(config).store or .menu to read storefront data.\",\n checkout: \"Use createCheckout().open() from @zappie/sdk/checkout.\",\n customer: \"Use createCustomer() from @zappie/sdk/customer.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const inspect = (node: TSESTree.Literal | TSESTree.TemplateLiteral) => {\n if (node.parent.type === \"ImportDeclaration\") {\n return;\n }\n const value = staticText(node);\n if (value && isZappieEndpoint(value)) {\n context.report({ node, messageId: message(value) });\n }\n };\n return {\n Literal: inspect,\n TemplateLiteral: inspect,\n ImportDeclaration(node) {\n if (isInternalSdkImport(node.source.value)) {\n context.report({ node: node.source, messageId: \"internal\" });\n }\n },\n };\n },\n});\n","import {\n ESLintUtils,\n type TSESLint,\n type TSESTree,\n} from \"@typescript-eslint/utils\";\nimport type ts from \"typescript\";\n\nconst moneyLiteralPattern =\n /[₦€£]\\s?\\d|\\$\\s?\\d|NGN\\s?\\d|\\d+(?:\\.\\d{2})?\\s?(?:naira|NGN)/i;\n\nexport function isMoneyLiteral(value: string): boolean {\n return value.trim() !== \"$1\" && moneyLiteralPattern.test(value);\n}\n\nexport function memberName(node: TSESTree.Node): string | undefined {\n if (node.type !== \"MemberExpression\") {\n return undefined;\n }\n if (!node.computed && node.property.type === \"Identifier\") {\n return node.property.name;\n }\n if (node.computed && node.property.type === \"Literal\") {\n return typeof node.property.value === \"string\"\n ? node.property.value\n : undefined;\n }\n return undefined;\n}\n\nexport const isAmountMinor = (node: TSESTree.Node) =>\n memberName(node) === \"amountMinor\";\n\nexport function amountOwner(\n node: TSESTree.Node,\n): TSESTree.Expression | undefined {\n return node.type === \"MemberExpression\" && isAmountMinor(node)\n ? node.object\n : undefined;\n}\n\nexport function dividedMinorOwner(\n node: TSESTree.Node,\n): TSESTree.Expression | undefined {\n if (\n node.type !== \"BinaryExpression\" ||\n node.operator !== \"/\" ||\n node.right.type !== \"Literal\" ||\n node.right.value !== 100\n ) {\n return undefined;\n }\n return amountOwner(node.left);\n}\n\ntype Services = ReturnType<typeof ESLintUtils.getParserServices>;\n\nexport function parserServices<\n MessageIds extends string,\n Options extends readonly unknown[],\n>(context: TSESLint.RuleContext<MessageIds, Options>): Services | undefined {\n try {\n return ESLintUtils.getParserServices(context, true);\n } catch {\n return undefined;\n }\n}\n\nfunction declaredBySdkMoney(symbol: ts.Symbol): boolean {\n if (!symbol.getName().includes(\"MoneyBrand\")) {\n return false;\n }\n return (symbol.getDeclarations() ?? []).some((declaration) => {\n const path = declaration.getSourceFile().fileName.replaceAll(\"\\\\\", \"/\");\n return (\n path.endsWith(\"/packages/sdk/src/money.ts\") ||\n /\\/node_modules\\/@zappie\\/sdk\\/.*\\.d\\.ts$/.test(path)\n );\n });\n}\n\nexport function isSdkMoney(\n services: Services | undefined,\n node: TSESTree.Node,\n): boolean {\n if (!services?.program) {\n return false;\n }\n const checker = services.program.getTypeChecker();\n const tsNode = services.esTreeNodeToTSNodeMap.get(node);\n const type = checker.getTypeAtLocation(tsNode);\n const types = type.isUnionOrIntersection() ? type.types : [type];\n return types.some((part) =>\n part.getProperties().some((property) => declaredBySdkMoney(property)),\n );\n}\n\nexport function currencyStyle(node: TSESTree.Node | undefined): boolean {\n if (node?.type !== \"ObjectExpression\") {\n return false;\n }\n return node.properties.some(\n (property) =>\n property.type === \"Property\" &&\n !property.computed &&\n ((property.key.type === \"Identifier\" && property.key.name === \"style\") ||\n (property.key.type === \"Literal\" && property.key.value === \"style\")) &&\n property.value.type === \"Literal\" &&\n property.value.value === \"currency\",\n );\n}\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport {\n dividedMinorOwner,\n isAmountMinor,\n isMoneyLiteral,\n isSdkMoney,\n memberName,\n parserServices,\n} from \"../money-rule-utils\";\n\nexport const noMoneyLiterals = createRule<\n [],\n \"arithmetic\" | \"literal\" | \"scale\"\n>({\n name: \"no-money-literals\",\n meta: {\n type: \"problem\",\n docs: {\n description: \"Use SDK prices and totals in your storefront.\",\n },\n schema: [],\n messages: {\n arithmetic:\n \"Use cart.subtotal, line.lineSubtotal, quote.subtotal, quote.lines totals or quote.total. Let the SDK calculate amounts.\",\n literal:\n \"Render SDK Money with format.money() or <Money> instead of a hard-coded amount.\",\n scale:\n \"Use format.money() to display amountMinor. Currencies use 0, 2, 3 or 4 minor-unit digits.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const services = parserServices(context);\n const arithmetic = (node: TSESTree.Node) =>\n isAmountMinor(node) || isSdkMoney(services, node);\n return {\n Literal(node) {\n if (\n typeof node.value === \"string\" &&\n node.parent.type !== \"ImportDeclaration\" &&\n isMoneyLiteral(node.value)\n ) {\n context.report({ node, messageId: \"literal\" });\n }\n },\n JSXText(node) {\n if (isMoneyLiteral(node.value)) {\n context.report({ node, messageId: \"literal\" });\n }\n },\n BinaryExpression(node) {\n if (![\"+\", \"-\", \"*\", \"/\"].includes(node.operator)) {\n return;\n }\n if (\n dividedMinorOwner(node) &&\n node.parent.type === \"MemberExpression\" &&\n node.parent.object === node &&\n memberName(node.parent) === \"toFixed\"\n ) {\n return;\n }\n if (arithmetic(node.left) || arithmetic(node.right)) {\n context.report({ node, messageId: \"arithmetic\" });\n }\n },\n TemplateLiteral(node) {\n for (const quasi of node.quasis) {\n if (isMoneyLiteral(quasi.value.cooked ?? quasi.value.raw)) {\n context.report({ node: quasi, messageId: \"literal\" });\n }\n }\n },\n CallExpression(node) {\n if (\n node.callee.type === \"MemberExpression\" &&\n memberName(node.callee) === \"toFixed\" &&\n node.arguments[0]?.type === \"Literal\" &&\n node.arguments[0].value === 2 &&\n dividedMinorOwner(node.callee.object)\n ) {\n context.report({ node, messageId: \"scale\" });\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport { isTrackingEndpoint, staticText } from \"../matchers\";\n\nconst imports = [\n \"react-ga4\",\n \"react-facebook-pixel\",\n \"@vercel/analytics\",\n \"posthog-js\",\n];\n\nfunction imported(value: string): boolean {\n return imports.includes(value) || value.startsWith(\"@segment/\");\n}\n\nexport const noTrackingScripts = createRule<[], \"tracking\">({\n name: \"no-tracking-scripts\",\n meta: {\n type: \"problem\",\n docs: { description: \"Forward analytics through Zappie.\" },\n schema: [],\n messages: {\n tracking:\n \"Configure analytics.forward in zappie.config.ts to forward analytics. Remove the tracking script.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const inspect = (node: TSESTree.Literal | TSESTree.TemplateLiteral) => {\n if (node.parent.type === \"ImportDeclaration\") {\n return;\n }\n const value = staticText(node);\n if (value && isTrackingEndpoint(value)) {\n context.report({ node, messageId: \"tracking\" });\n }\n };\n return {\n Literal: inspect,\n TemplateLiteral: inspect,\n ImportDeclaration(node) {\n if (imported(node.source.value)) {\n context.report({ node: node.source, messageId: \"tracking\" });\n }\n },\n CallExpression(node) {\n const callee = node.callee;\n const direct =\n callee.type === \"Identifier\" && [\"gtag\", \"fbq\"].includes(callee.name);\n const dataLayer =\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n callee.object.type === \"Identifier\" &&\n callee.object.name === \"dataLayer\" &&\n callee.property.type === \"Identifier\" &&\n callee.property.name === \"push\";\n if (direct || dataLayer) {\n context.report({ node, messageId: \"tracking\" });\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\n\nconst defaults = [\n \"modal\",\n \"sheet\",\n \"drawer\",\n \"dialog\",\n \"item\",\n \"cart\",\n \"cart_sheet\",\n \"cart_edit\",\n \"cart_clear\",\n \"search\",\n \"catalogue_search\",\n \"catalogue_categories\",\n \"product_note\",\n \"modal_id\",\n];\n\nfunction name(node: TSESTree.Node): string | undefined {\n if (node.type === \"Identifier\") {\n return node.name;\n }\n if (node.type === \"MemberExpression\" && node.property.type === \"Identifier\") {\n return node.property.name;\n }\n return undefined;\n}\n\nfunction carriesKey(node: TSESTree.Node, keys: Set<string>): boolean {\n if (node.type === \"Literal\" && typeof node.value === \"string\") {\n return [...keys].some((key) => new RegExp(`[?&]${key}=`).test(node.value));\n }\n if (node.type === \"TemplateLiteral\") {\n return node.quasis.some((part) =>\n carriesKey(\n { ...part, type: \"Literal\", value: part.value.raw } as never,\n keys,\n ),\n );\n }\n if (node.type === \"ObjectExpression\") {\n return node.properties.some(\n (property) =>\n property.type === \"Property\" &&\n ((property.key.type === \"Identifier\" && keys.has(property.key.name)) ||\n (property.key.type === \"Literal\" &&\n typeof property.key.value === \"string\" &&\n keys.has(property.key.value))),\n );\n }\n return false;\n}\n\nexport const overlayPrimitive = createRule<[{ keys?: string[] }], \"overlay\">({\n name: \"overlay-primitive\",\n meta: {\n type: \"problem\",\n docs: { description: \"Use Overlay to keep dialogs and sheets in the URL.\" },\n schema: [\n {\n type: \"object\",\n properties: { keys: { type: \"array\", items: { type: \"string\" } } },\n additionalProperties: false,\n },\n ],\n messages: {\n overlay:\n \"Use Overlay or useOverlay for dialogs and sheets in the URL. They handle Back, Forward and focus restoration.\",\n },\n },\n defaultOptions: [{}],\n create(context, [options]) {\n const keys = new Set([...defaults, ...(options.keys ?? [])]);\n const searchValues = new Set<string>();\n const derivedFromSearch = (node: TSESTree.Node) => {\n const text = context.sourceCode.getText(node);\n return (\n text.includes(\"useSearchParams(\") ||\n [...searchValues].some((value) =>\n new RegExp(`\\\\b${value}\\\\b`).test(text),\n )\n );\n };\n return {\n VariableDeclarator(node) {\n if (\n node.init?.type === \"CallExpression\" &&\n name(node.init.callee) === \"useSearchParams\"\n ) {\n if (node.id.type === \"Identifier\") {\n searchValues.add(node.id.name);\n } else if (\n node.id.type === \"ArrayPattern\" &&\n node.id.elements[0]?.type === \"Identifier\"\n ) {\n searchValues.add(node.id.elements[0].name);\n }\n } else if (\n node.id.type === \"Identifier\" &&\n node.init &&\n derivedFromSearch(node.init)\n ) {\n searchValues.add(node.id.name);\n }\n },\n CallExpression(node) {\n const callee = name(node.callee);\n const navigation =\n callee === \"setSearchParams\" ||\n callee === \"navigate\" ||\n callee === \"pushState\" ||\n callee === \"replaceState\" ||\n (node.callee.type === \"CallExpression\" &&\n name(node.callee.callee) === \"useNavigate\");\n if (\n navigation &&\n node.arguments.some(\n (argument) =>\n argument.type !== \"SpreadElement\" && carriesKey(argument, keys),\n )\n ) {\n context.report({ node, messageId: \"overlay\" });\n }\n },\n JSXOpeningElement(node) {\n const dialog =\n node.name.type === \"JSXIdentifier\" && node.name.name === \"dialog\";\n const role = node.attributes.some(\n (attribute) =>\n attribute.type === \"JSXAttribute\" &&\n attribute.name.name === \"role\" &&\n attribute.value?.type === \"Literal\" &&\n attribute.value.value === \"dialog\",\n );\n const open = node.attributes.find(\n (attribute) =>\n attribute.type === \"JSXAttribute\" && attribute.name.name === \"open\",\n );\n if (\n (dialog || role) &&\n open?.type === \"JSXAttribute\" &&\n open.value?.type === \"JSXExpressionContainer\" &&\n derivedFromSearch(open.value.expression)\n ) {\n context.report({ node, messageId: \"overlay\" });\n }\n },\n };\n },\n});\n","import type { TSESLint, TSESTree } from \"@typescript-eslint/utils\";\n\ntype Fixer = TSESLint.RuleFixer;\ntype Fix = TSESLint.RuleFix;\ntype Source = Readonly<TSESLint.SourceCode>;\n\nfunction sdkImport(\n program: TSESTree.Program,\n): TSESTree.ImportDeclaration | undefined {\n return program.body.find(\n (node): node is TSESTree.ImportDeclaration =>\n node.type === \"ImportDeclaration\" && node.source.value === \"@zappie/sdk\",\n );\n}\n\nfunction importedFormat(\n declaration: TSESTree.ImportDeclaration | undefined,\n): TSESTree.ImportSpecifier | undefined {\n return declaration?.specifiers.find(\n (specifier): specifier is TSESTree.ImportSpecifier =>\n specifier.type === \"ImportSpecifier\" &&\n specifier.imported.type === \"Identifier\" &&\n specifier.imported.name === \"format\",\n );\n}\n\nfunction importFix(\n fixer: Fixer,\n source: Source,\n program: TSESTree.Program,\n local: string,\n): Fix | undefined {\n const declaration = sdkImport(program);\n if (importedFormat(declaration)?.local.name === local) {\n return undefined;\n }\n const importedName = local === \"format\" ? \"format\" : `format as ${local}`;\n const named = declaration?.specifiers.filter(\n (specifier) => specifier.type === \"ImportSpecifier\",\n );\n if (declaration && declaration.importKind !== \"type\" && named?.length) {\n const last = named.at(-1);\n if (last) {\n return fixer.insertTextAfter(last, `, ${importedName}`);\n }\n }\n const first = program.body[0];\n const text = `import { ${importedName} } from \"@zappie/sdk\";\\n`;\n return first\n ? fixer.insertTextBefore(first, text)\n : fixer.insertTextAfter(program, text);\n}\n\nexport function formatFixes(\n fixer: Fixer,\n source: Source,\n program: TSESTree.Program,\n replace: TSESTree.Node,\n owner: TSESTree.Expression,\n local: string,\n): Fix[] {\n const fixes = [\n fixer.replaceText(replace, `${local}.money(${source.getText(owner)})`),\n ];\n const addition = importFix(fixer, source, program, local);\n if (addition) {\n fixes.push(addition);\n }\n return fixes;\n}\n\nexport function formatBinding(\n source: Source,\n program: TSESTree.Program,\n node: TSESTree.Node,\n): \"available\" | \"conflict\" | \"imported\" {\n if (importedFormat(sdkImport(program))) {\n return \"imported\";\n }\n let scope: TSESLint.Scope.Scope | null = source.getScope(node);\n while (scope) {\n if (scope.variables.some((variable) => variable.name === \"format\")) {\n return \"conflict\";\n }\n scope = scope.upper;\n }\n return \"available\";\n}\n\nexport function importedFormatLocal(\n program: TSESTree.Program,\n): string | undefined {\n return importedFormat(sdkImport(program))?.local.name;\n}\n\nexport function availableAlias(source: Source, node: TSESTree.Node): string {\n const names = new Set<string>();\n let scope: TSESLint.Scope.Scope | null = source.getScope(node);\n while (scope) {\n for (const variable of scope.variables) {\n names.add(variable.name);\n }\n scope = scope.upper;\n }\n for (const name of [\n \"zappieFormat\",\n \"zappieMoneyFormat\",\n \"formatMoneyValue\",\n ]) {\n if (!names.has(name)) {\n return name;\n }\n }\n return \"zappieSdkFormat\";\n}\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport {\n availableAlias,\n formatBinding,\n formatFixes,\n importedFormatLocal,\n} from \"../format-fix\";\nimport {\n currencyStyle,\n dividedMinorOwner,\n isAmountMinor,\n isSdkMoney,\n memberName,\n parserServices,\n} from \"../money-rule-utils\";\n\ntype Message = \"currency\" | \"direct\" | \"minor\" | \"useAlias\";\n\nfunction intlNumberFormat(node: TSESTree.Node): boolean {\n const callee =\n node.type === \"NewExpression\" || node.type === \"CallExpression\"\n ? node.callee\n : undefined;\n return Boolean(\n callee?.type === \"MemberExpression\" &&\n callee.object.type === \"Identifier\" &&\n callee.object.name === \"Intl\" &&\n memberName(callee) === \"NumberFormat\",\n );\n}\n\nfunction formattedMinor(node: TSESTree.CallExpression) {\n if (node.callee.type !== \"MemberExpression\") {\n return undefined;\n }\n if (memberName(node.callee) === \"toLocaleString\") {\n return dividedMinorOwner(node.callee.object);\n }\n if (memberName(node.callee) === \"format\") {\n return dividedMinorOwner(node.arguments[0] ?? node);\n }\n return undefined;\n}\n\nfunction formatsDividedMinor(node: TSESTree.NewExpression): boolean {\n const member = node.parent;\n const call = member.type === \"MemberExpression\" ? member.parent : undefined;\n return Boolean(\n call?.type === \"CallExpression\" &&\n call.callee === member &&\n memberName(member) === \"format\" &&\n dividedMinorOwner(call.arguments[0] ?? call),\n );\n}\n\nexport const useFormatMoney = createRule<[], Message>({\n name: \"use-format-money\",\n meta: {\n type: \"problem\",\n docs: { description: \"Format SDK Money with format.money() or <Money>.\" },\n fixable: \"code\",\n hasSuggestions: true,\n schema: [],\n messages: {\n currency:\n \"Use format.money(). Intl currency defaults can differ from the API minor-unit digits.\",\n direct:\n \"Render SDK Money with format.money(value) or <Money value={value}>.\",\n minor:\n \"Use format.money(value). Dividing by 100 gives the wrong amount for currencies with 0, 3 or 4 minor-unit digits.\",\n useAlias:\n \"Import format with an unused alias and format this Money value.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const source = context.sourceCode;\n const services = parserServices(context);\n let program: TSESTree.Program;\n const reportFix = (\n node: TSESTree.Node,\n replace: TSESTree.Node,\n owner: TSESTree.Expression,\n ) => {\n const data = { node, messageId: \"minor\" as const };\n const binding = formatBinding(source, program, node);\n if (binding === \"conflict\") {\n const alias = availableAlias(source, node);\n context.report({\n ...data,\n suggest: [\n {\n messageId: \"useAlias\",\n fix: (fixer) =>\n formatFixes(fixer, source, program, replace, owner, alias),\n },\n ],\n });\n return;\n }\n context.report({\n ...data,\n fix: (fixer) => {\n const local = importedFormatLocal(program) ?? \"format\";\n return formatFixes(fixer, source, program, replace, owner, local);\n },\n });\n };\n const inspectFormatter = (node: TSESTree.CallExpression) => {\n const owner = formattedMinor(node);\n if (owner) {\n reportFix(node, node, owner);\n return true;\n }\n return false;\n };\n return {\n Program(node) {\n program = node;\n },\n NewExpression(node) {\n if (\n intlNumberFormat(node) &&\n currencyStyle(node.arguments[1]) &&\n !formatsDividedMinor(node)\n ) {\n context.report({ node, messageId: \"currency\" });\n }\n },\n CallExpression(node) {\n if (inspectFormatter(node)) {\n return;\n }\n if (intlNumberFormat(node) && currencyStyle(node.arguments[1])) {\n context.report({ node, messageId: \"currency\" });\n return;\n }\n if (\n node.callee.type === \"MemberExpression\" &&\n memberName(node.callee) === \"toLocaleString\" &&\n node.arguments.some(currencyStyle)\n ) {\n context.report({ node, messageId: \"currency\" });\n }\n },\n TemplateLiteral(node) {\n for (const expression of node.expressions) {\n const owner = dividedMinorOwner(expression);\n if (owner) {\n reportFix(expression, expression, owner);\n } else if (isAmountMinor(expression)) {\n context.report({ node: expression, messageId: \"minor\" });\n }\n }\n },\n JSXExpressionContainer(node) {\n if (\n node.parent.type === \"JSXAttribute\" ||\n node.expression.type === \"JSXEmptyExpression\"\n ) {\n return;\n }\n if (\n dividedMinorOwner(node.expression) ||\n isAmountMinor(node.expression)\n ) {\n context.report({ node: node.expression, messageId: \"minor\" });\n } else if (isSdkMoney(services, node.expression)) {\n context.report({ node: node.expression, messageId: \"direct\" });\n }\n },\n };\n },\n});\n","import type { TSESLint, TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\n\ntype Kind = \"add\" | \"checkout\" | \"signin\";\nconst marker: Record<Kind, { name: string; text: string }> = {\n add: { name: \"data-zappie-action\", text: 'data-zappie-action=\"add-to-cart\"' },\n checkout: { name: \"data-zappie-checkout\", text: \"data-zappie-checkout\" },\n signin: { name: \"data-zappie-signin\", text: \"data-zappie-signin\" },\n};\n\nfunction handlerKind(\n source: Readonly<TSESLint.SourceCode>,\n node: TSESTree.JSXAttribute,\n): Kind | undefined {\n const text = node.value ? source.getText(node.value) : \"\";\n if (/\\b(?:cart(?:\\.actions)?\\.add)\\s*\\(/.test(text)) {\n return \"add\";\n }\n if (/\\b(?:checkout|createCheckout\\([^)]*\\))\\.open\\s*\\(/.test(text)) {\n return \"checkout\";\n }\n if (/\\b(?:auth|createCustomer\\([^)]*\\))\\.signIn\\s*\\(/.test(text)) {\n return \"signin\";\n }\n return undefined;\n}\n\nexport const verifyMarkers = createRule<[], \"marker\">({\n name: \"verify-markers\",\n meta: {\n type: \"suggestion\",\n docs: { description: \"Mark shopping actions so verify can find them.\" },\n fixable: \"code\",\n schema: [],\n messages: {\n marker: \"Add {{marker}} so zappie verify can find this shopping action.\",\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n JSXOpeningElement(node) {\n const click = node.attributes.find(\n (attribute): attribute is TSESTree.JSXAttribute =>\n attribute.type === \"JSXAttribute\" &&\n attribute.name.name === \"onClick\",\n );\n if (!click) {\n return;\n }\n const kind = handlerKind(context.sourceCode, click);\n if (\n !kind ||\n node.attributes.some(\n (attribute) =>\n attribute.type === \"JSXAttribute\" &&\n attribute.name.name === marker[kind].name,\n )\n ) {\n return;\n }\n context.report({\n node: click,\n messageId: \"marker\",\n data: { marker: marker[kind].text },\n fix: (fixer) =>\n fixer.insertTextAfter(node.name, ` ${marker[kind].text}`),\n });\n },\n };\n },\n});\n"],"mappings":"AAAA,OAAOA,OAAY,4BACnB,OAAOC,OAAa,yBACpB,OAAOC,OAAgB,4BCFvB,OAAS,eAAAC,OAAmB,2BAErB,IAAMC,EAAaD,GAAY,YACnCE,GAAS,+BAA+BA,CAAI,EAC/C,ECDA,IAAMC,GAAW,iDACXC,GAAa,mBAEnB,SAASC,EAAWC,EAAyC,CAC3D,GAAIA,EAAK,OAAS,aAChB,OAAOA,EAAK,KAEd,GAAIA,EAAK,OAAS,oBAAsBA,EAAK,SAAS,OAAS,aAC7D,OAAOA,EAAK,SAAS,IAGzB,CAEA,SAASC,GAAaD,EAAgD,CACpE,OAAIA,EAAK,OAAS,aACTA,EAAK,KAEPA,EAAK,OAAS,gBAAkBA,EAAK,SAAS,CAAC,GAAG,OAAS,aAC9DA,EAAK,SAAS,CAAC,EAAE,KACjB,MACN,CAEA,SAASE,GAAYF,EAAwC,CAC3D,IAAMG,EACJJ,EAAWC,EAAK,MAAM,IAAM,aACxBA,EAAK,UAAU,CAAC,EAChBA,EAAK,UAAU,CAAC,EACtB,MAAO,GACLG,GACAA,EAAM,OAAS,kBACdA,EAAM,OAAS,mBACbA,EAAM,OAAS,WAAa,OAAOA,EAAM,OAAU,UAE1D,CAEO,IAAMC,EAAaC,EAAuB,CAC/C,KAAM,eACN,KAAM,CACJ,KAAM,UACN,KAAM,CAAE,YAAa,kCAAmC,EACxD,OAAQ,CAAC,EACT,SAAU,CACR,KAAM,8FACR,CACF,EACA,eAAgB,CAAC,EACjB,OAAOC,EAAS,CACd,MAAO,CACL,eAAeN,EAAM,CACnB,IAAMO,EAASR,EAAWC,EAAK,MAAM,EAC/BQ,EAAMR,EAAK,UAAU,CAAC,EAE1B,CAAC,UAAW,UAAW,aAAc,MAAM,EAAE,SAASO,GAAU,EAAE,GAClEC,GAAK,OAAS,WACd,OAAOA,EAAI,OAAU,UACrBV,GAAW,KAAKU,EAAI,KAAK,GAEzBF,EAAQ,OAAO,CAAE,KAAAN,EAAM,UAAW,MAAO,CAAC,CAE9C,EACA,mBAAmBA,EAAM,CACvB,GAAIA,EAAK,MAAM,OAAS,iBACtB,OAEF,IAAMS,EAAKR,GAAaD,EAAK,EAAE,EACzBU,EAAOX,EAAWC,EAAK,KAAK,MAAM,EAEtCS,GACAZ,GAAS,KAAKY,CAAE,GAChB,CAAC,WAAY,aAAc,eAAe,EAAE,SAASC,GAAQ,EAAE,GAC/DR,GAAYF,EAAK,IAAI,GAErBM,EAAQ,OAAO,CAAE,KAAAN,EAAM,UAAW,MAAO,CAAC,EAG1CS,GACA,QAAQ,KAAKA,CAAE,GACf,CAAC,SAAU,cAAe,MAAM,EAAE,SAASC,GAAQ,EAAE,GAErDJ,EAAQ,OAAO,CAAE,KAAAN,EAAM,UAAW,MAAO,CAAC,CAE9C,CACF,CACF,CACF,CAAC,ECtFD,OAAS,aAAAW,OAAiB,YAE1B,IAAMC,GACJ,mFACIC,GACJ,6JAEWC,EACX,2DAEK,SAASC,EAAWC,EAAgD,CACzE,GAAIA,GAAM,OAAS,WAAa,OAAOA,EAAK,OAAU,SACpD,OAAOA,EAAK,MAEd,GAAIA,GAAM,OAAS,kBACjB,OAAOA,EAAK,OACT,IAAKC,GAASA,EAAK,MAAM,QAAUA,EAAK,MAAM,GAAG,EACjD,KAAK,EAAE,CAGd,CAEO,SAASC,EACdF,EACAG,EACmC,CACnC,OAAOH,EAAK,WAAW,KACpBI,GACCA,EAAU,OAAS,gBACnBA,EAAU,KAAK,OAAS,iBACxBA,EAAU,KAAK,KAAK,YAAY,IAAMD,EAAK,YAAY,CAC3D,CACF,CAEO,SAASE,EAASL,EAAkD,CACzE,GAAKA,GAAM,MAGX,IAAIA,EAAK,MAAM,OAAS,UACtB,OAAO,OAAOA,EAAK,MAAM,OAAU,SAAWA,EAAK,MAAM,MAAQ,OAEnE,GAAIA,EAAK,MAAM,OAAS,yBACtB,OAAOD,EAAWC,EAAK,MAAM,UAAU,EAG3C,CAEO,IAAMM,EAAoBC,GAAkBX,GAAgB,KAAKW,CAAK,EAChEC,EAAsBD,GACjCV,GAAgB,KAAKU,CAAK,EACfE,EAAuBF,GAClCA,IAAU,wBAA0BA,EAAM,WAAW,uBAAuB,EAEvE,SAASG,EAAaC,EAAkBC,EAA6B,CAC1E,IAAMC,EAAOF,EAAS,WAAW,KAAM,GAAG,EAC1C,OAAOC,EAAS,KAAME,GACpBnB,GAAUkB,EAAMC,EAAS,CAAE,UAAW,EAAK,CAAC,CAC9C,CACF,CCxDA,IAAMC,GAAe,IAAI,IAAI,CAAC,YAAa,SAAU,SAAU,SAAS,CAAC,EACnEC,EAAe,+BAErB,SAASC,GAAeC,EAAwB,CAC9C,OACEA,EAAM,WAAW,YAAY,GAC7BA,IAAU,kBACVA,IAAU,wBACVA,EAAM,WAAW,UAAU,CAE/B,CAEO,IAAMC,EAAiBC,EAA2B,CACvD,KAAM,mBACN,KAAM,CACJ,KAAM,UACN,KAAM,CAAE,YAAa,uCAAwC,EAC7D,OAAQ,CAAC,EACT,SAAU,CACR,SACE,8FACJ,CACF,EACA,eAAgB,CAAC,EACjB,OAAOC,EAAS,CACd,MAAO,CACL,kBAAkBC,EAAM,CAClBL,GAAeK,EAAK,OAAO,KAAK,GAClCD,EAAQ,OAAO,CAAE,KAAMC,EAAK,OAAQ,UAAW,UAAW,CAAC,CAE/D,EACA,kBAAkBA,EAAM,CACtB,IAAMC,EACJD,EAAK,KAAK,OAAS,gBACfA,EAAK,KAAK,KAAK,YAAY,EAC3B,GACAE,EAAOC,EAASC,EAAaJ,EAAM,cAAc,CAAC,EAClDK,EAAOF,EAASC,EAAaJ,EAAM,MAAM,CAAC,EAC1CM,EAAKH,EAASC,EAAaJ,EAAM,IAAI,CAAC,EACtCO,EAASJ,EACbC,EAAaJ,EAAMC,IAAQ,SAAW,MAAQ,MAAM,CACtD,EACMO,EACJP,IAAQ,UACNC,IAAS,QAAaT,GAAa,IAAIS,EAAK,YAAY,CAAC,GACxDG,IAAS,QAAaX,EAAa,KAAKW,CAAI,GAC5CC,IAAO,QAAaZ,EAAa,KAAKY,CAAE,GACvCG,GACHR,IAAQ,UAAYA,IAAQ,MAC7BM,IAAW,QACXG,EAAmB,KAAKH,CAAM,GAC5BC,GAAaC,IACfV,EAAQ,OAAO,CAAE,KAAAC,EAAM,UAAW,UAAW,CAAC,CAElD,CACF,CACF,CACF,CAAC,ECtDD,SAASW,GAASC,EAAqB,CACrC,IAAIC,EAAqCD,EACzC,KAAOC,GAAS,QAAQ,CACtB,IAAMC,EAAwBD,EAAQ,OACtC,IACGC,EAAO,OAAS,2BACfA,EAAO,OAAS,uBAClBA,EAAO,QAAQ,OAAS,kBACxBA,EAAO,OAAO,OAAO,OAAS,oBAC9BA,EAAO,OAAO,OAAO,SAAS,OAAS,cACvCA,EAAO,OAAO,OAAO,SAAS,OAAS,MAEvC,OAAOA,EAETD,EAAUC,CACZ,CACF,CAEA,SAASC,GAAUC,EAAuCC,EAAoB,CAC5E,IAAMC,EAAOF,EAAO,QAAQC,EAAS,MAAM,EAC3C,GAAI,kCAAkC,KAAKC,CAAI,EAC7C,MAAO,GAET,IAAMC,EAAQF,EAAS,OAAO,CAAC,EAC/B,OAAIE,GAAO,OAAS,aACX,GAEK,IAAI,OAAO,GAAGA,EAAM,IAAI,4BAA4B,EACrD,KAAKH,EAAO,QAAQC,CAAQ,CAAC,CAC5C,CAEO,IAAMG,EAAgBC,EAAwC,CACnE,KAAM,iBACN,KAAM,CACJ,KAAM,UACN,KAAM,CAAE,YAAa,8CAA+C,EACpE,OAAQ,CAAC,EACT,SAAU,CACR,OAAQ,iEACR,WAAY,mDACd,CACF,EACA,eAAgB,CAAC,EACjB,OAAOC,EAAS,CACd,IAAMC,EAAuC,CAAC,EAC9C,MAAO,CACL,kBAAkBX,EAAM,CACtB,IAAMY,EAAWC,EAAab,EAAM,UAAU,EACxCc,EACJF,GAAU,OAAO,OAAS,0BAC1BA,EAAS,MAAM,WAAW,OAAS,WACnCA,EAAS,MAAM,WAAW,QAAU,GACtC,GACEZ,EAAK,KAAK,OAAS,iBACnBA,EAAK,KAAK,OAAS,SACnB,CAACY,GACDE,EAEA,OAEFH,EAAO,KAAKX,CAAI,EAChB,IAAMK,EAAWN,GAASC,CAAI,EAC1BK,GAAY,CAACF,GAAUO,EAAQ,WAAYL,CAAQ,GACrDK,EAAQ,OAAO,CAAE,KAAME,EAAU,UAAW,QAAS,CAAC,CAE1D,EACA,gBAAiB,CACf,GACE,kCAAkC,KAChCF,EAAQ,SAAS,WAAW,KAAM,GAAG,CACvC,GACAC,EAAO,OAAS,EAEhB,QAAWX,KAAQW,EAAO,MAAM,CAAC,EAC/BD,EAAQ,OAAO,CAAE,KAAAV,EAAM,UAAW,YAAa,CAAC,CAGtD,CACF,CACF,CACF,CAAC,EClFD,SAASe,GAAYC,EAAmC,CACtD,IAAMC,EACJD,GAAW,OAAO,OAAS,yBACvBA,EAAU,MAAM,WAChB,OACN,GACEC,GAAY,OAAS,kBACrBA,EAAW,OAAO,OAAS,cAC3BA,EAAW,OAAO,OAAS,WAE3B,MAAO,GAET,IAAMC,EAASC,EAASH,CAAS,EACjC,OAAOE,IAAW,QAAa,kBAAkB,KAAKA,CAAM,CAC9D,CAEO,IAAME,EAAmBC,EAA2B,CACzD,KAAM,qBACN,KAAM,CACJ,KAAM,UACN,KAAM,CAAE,YAAa,2CAA4C,EACjE,OAAQ,CAAC,EACT,SAAU,CACR,SAAU,6DACZ,CACF,EACA,eAAgB,CAAC,EACjB,OAAOC,EAAS,CACd,IAAMC,EAAQ,IAAI,IAClB,MAAO,CACL,kBAAkBC,EAAM,CACtB,GAAK,CAAC,eAAgB,kBAAkB,EAAE,SAASA,EAAK,OAAO,KAAK,EAGpE,QAAWC,KAAQD,EAAK,WAEpBC,EAAK,OAAS,mBACdA,EAAK,SAAS,OAAS,cACvB,CAAC,OAAQ,SAAS,EAAE,SAASA,EAAK,SAAS,IAAI,GAE/CF,EAAM,IAAIE,EAAK,MAAM,IAAI,CAG/B,EACA,kBAAkBD,EAAM,CAEpBA,EAAK,KAAK,OAAS,iBACnBD,EAAM,IAAIC,EAAK,KAAK,IAAI,GACxBT,GAAYW,EAAaF,EAAM,IAAI,CAAC,GACpC,CAACE,EAAaF,EAAM,UAAU,GAE9BF,EAAQ,OAAO,CAAE,KAAAE,EAAM,UAAW,UAAW,CAAC,CAElD,CACF,CACF,CACF,CAAC,ECvDD,IAAMG,GAAW,CAAC,gBAAiB,cAAc,EAC3CC,EAAY,2DACZC,EAAiB,IAAI,IAAI,CAAC,eAAgB,gBAAgB,CAAC,EAC3DC,GAAU,IAAI,IAAI,CAAC,SAAU,aAAc,MAAM,CAAC,EAExD,SAASC,EAAUC,EAA8B,CAC/C,OAAIA,EAAK,OAAS,aACTH,EAAe,IAAIG,EAAK,IAAI,EAGnCA,EAAK,OAAS,oBACd,CAACA,EAAK,UACNA,EAAK,OAAO,OAAS,cACrBF,GAAQ,IAAIE,EAAK,OAAO,IAAI,GAC5BA,EAAK,SAAS,OAAS,cACvBH,EAAe,IAAIG,EAAK,SAAS,IAAI,CAEzC,CAEA,SAASC,GAAUD,EAAqD,CACtE,MAAI,CAACA,EAAK,UAAYA,EAAK,SAAS,OAAS,aACpCA,EAAK,SAAS,KAEhBE,EAAWF,EAAK,QAAQ,CACjC,CAEA,SAASG,GAAWC,EAA6C,CAC/D,GAAIA,IAAU,QAAUA,IAAU,eAChC,MAAO,SAET,GACEA,IAAU,aACVA,EAAM,WAAW,YAAY,GAC7BA,EAAM,WAAW,QAAQ,GACzBA,IAAU,eACVA,EAAM,WAAW,cAAc,GAC/BA,IAAU,SACVA,EAAM,WAAW,SAAS,GAC1BA,IAAU,iBACVA,EAAM,WAAW,iBAAiB,EAElC,MAAO,KAGX,CAEO,IAAMC,EAAeC,EAA4B,CACtD,KAAM,iBACN,KAAM,CACJ,KAAM,UACN,KAAM,CACJ,YAAa,2CACf,EACA,OAAQ,CACN,CACE,KAAM,SACN,WAAY,CACV,YAAa,CACX,KAAM,QACN,MAAO,CAAE,KAAM,QAAS,EACxB,YAAa,EACf,CACF,EACA,qBAAsB,EACxB,CACF,EACA,SAAU,CACR,KAAM,+EACR,CACF,EACA,eAAgB,CAAC,CAAC,CAAC,EACnB,OAAOC,EAAS,CAACC,CAAO,EAAG,CACzB,IAAMC,EAASC,EACbH,EAAQ,SACRC,EAAQ,aAAeb,EACzB,EACA,MAAO,CACL,kBAAkBK,EAAM,CACtB,IAAMW,EAAOR,GAAWH,EAAK,OAAO,KAAK,GACrCW,IAAS,OAAUA,IAAS,UAAY,CAACF,IAC3CF,EAAQ,OAAO,CAAE,KAAMP,EAAK,OAAQ,UAAW,MAAO,CAAC,CAE3D,EACA,kBAAkBA,EAAM,CACtB,GACEA,EAAK,KAAK,OAAS,iBACnBA,EAAK,KAAK,KAAK,YAAY,IAAM,QAEjC,OAEF,IAAMY,EAAOC,EAASC,EAAad,EAAM,MAAM,CAAC,EAC1Ce,EAAOF,EAASC,EAAad,EAAM,cAAc,CAAC,GAEtDY,GAAM,YAAY,IAAM,YACvBG,IAAS,QACR,CAAC,gBAAiB,mBAAoB,cAAc,EAAE,SACpDA,EAAK,YAAY,CACnB,IAEFR,EAAQ,OAAO,CAAE,KAAAP,EAAM,UAAW,MAAO,CAAC,CAE9C,EACA,eAAeA,EAAM,CACnB,GACEA,EAAK,OAAO,OAAS,oBACrB,CAACD,EAAUC,EAAK,OAAO,MAAM,EAE7B,OAEF,IAAMgB,EAAQhB,EAAK,UAAU,CAAC,EACxBI,EACJY,GAAO,OAAS,aAAeA,EAAM,KAAOd,EAAWc,GAAS,IAAI,EAClEZ,GAASR,EAAU,KAAKQ,CAAK,GAC/BG,EAAQ,OAAO,CAAE,KAAAP,EAAM,UAAW,MAAO,CAAC,CAE9C,EACA,qBAAqBA,EAAM,CACzB,GACEA,EAAK,KAAK,OAAS,oBACnBD,EAAUC,EAAK,KAAK,MAAM,EAC1B,CACA,IAAMiB,EAAMhB,GAAUD,EAAK,IAAI,EAC3BiB,GAAOrB,EAAU,KAAKqB,CAAG,GAC3BV,EAAQ,OAAO,CAAE,KAAAP,EAAM,UAAW,MAAO,CAAC,EAE5C,MACF,CACA,IAAMkB,EACJlB,EAAK,KAAK,OAAS,oBACnBA,EAAK,KAAK,OAAO,OAAS,cAC1BA,EAAK,KAAK,OAAO,OAAS,YAC1BA,EAAK,KAAK,SAAS,OAAS,cAC5BA,EAAK,KAAK,SAAS,OAAS,SACxBI,EAAQF,EAAWF,EAAK,KAAK,EAC/BkB,GAAUd,GAASR,EAAU,KAAKQ,CAAK,GACzCG,EAAQ,OAAO,CAAE,KAAAP,EAAM,UAAW,MAAO,CAAC,CAE9C,CACF,CACF,CACF,CAAC,EC3ID,SAASmB,GAAQC,EAAwB,CACvC,MAAI,2CAA2C,KAAKA,CAAK,EAChD,WAEL,4CAA4C,KAAKA,CAAK,EACjD,WAEL,oBAAoB,KAAKA,CAAK,EACzB,aAEF,QACT,CAEO,IAAMC,EAAcC,EAAwB,CACjD,KAAM,gBACN,KAAM,CACJ,KAAM,UACN,KAAM,CAAE,YAAa,8BAA+B,EACpD,OAAQ,CAAC,EACT,SAAU,CACR,OACE,6EACF,SACE,sFACF,WACE,mEACF,SAAU,yDACV,SAAU,iDACZ,CACF,EACA,eAAgB,CAAC,EACjB,OAAOC,EAAS,CACd,IAAMC,EAAWC,GAAsD,CACrE,GAAIA,EAAK,OAAO,OAAS,oBACvB,OAEF,IAAML,EAAQM,EAAWD,CAAI,EACzBL,GAASO,EAAiBP,CAAK,GACjCG,EAAQ,OAAO,CAAE,KAAAE,EAAM,UAAWN,GAAQC,CAAK,CAAE,CAAC,CAEtD,EACA,MAAO,CACL,QAASI,EACT,gBAAiBA,EACjB,kBAAkBC,EAAM,CAClBG,EAAoBH,EAAK,OAAO,KAAK,GACvCF,EAAQ,OAAO,CAAE,KAAME,EAAK,OAAQ,UAAW,UAAW,CAAC,CAE/D,CACF,CACF,CACF,CAAC,ECzDD,OACE,eAAAI,OAGK,2BAGP,IAAMC,GACJ,+DAEK,SAASC,EAAeC,EAAwB,CACrD,OAAOA,EAAM,KAAK,IAAM,MAAQF,GAAoB,KAAKE,CAAK,CAChE,CAEO,SAASC,EAAWC,EAAyC,CAClE,GAAIA,EAAK,OAAS,mBAGlB,IAAI,CAACA,EAAK,UAAYA,EAAK,SAAS,OAAS,aAC3C,OAAOA,EAAK,SAAS,KAEvB,GAAIA,EAAK,UAAYA,EAAK,SAAS,OAAS,UAC1C,OAAO,OAAOA,EAAK,SAAS,OAAU,SAClCA,EAAK,SAAS,MACd,OAGR,CAEO,IAAMC,EAAiBD,GAC5BD,EAAWC,CAAI,IAAM,cAEhB,SAASE,GACdF,EACiC,CACjC,OAAOA,EAAK,OAAS,oBAAsBC,EAAcD,CAAI,EACzDA,EAAK,OACL,MACN,CAEO,SAASG,EACdH,EACiC,CACjC,GACE,EAAAA,EAAK,OAAS,oBACdA,EAAK,WAAa,KAClBA,EAAK,MAAM,OAAS,WACpBA,EAAK,MAAM,QAAU,KAIvB,OAAOE,GAAYF,EAAK,IAAI,CAC9B,CAIO,SAASI,EAGdC,EAA0E,CAC1E,GAAI,CACF,OAAOV,GAAY,kBAAkBU,EAAS,EAAI,CACpD,MAAQ,CACN,MACF,CACF,CAEA,SAASC,GAAmBC,EAA4B,CACtD,OAAKA,EAAO,QAAQ,EAAE,SAAS,YAAY,GAGnCA,EAAO,gBAAgB,GAAK,CAAC,GAAG,KAAMC,GAAgB,CAC5D,IAAMC,EAAOD,EAAY,cAAc,EAAE,SAAS,WAAW,KAAM,GAAG,EACtE,OACEC,EAAK,SAAS,4BAA4B,GAC1C,2CAA2C,KAAKA,CAAI,CAExD,CAAC,EARQ,EASX,CAEO,SAASC,EACdC,EACAX,EACS,CACT,GAAI,CAACW,GAAU,QACb,MAAO,GAET,IAAMC,EAAUD,EAAS,QAAQ,eAAe,EAC1CE,EAASF,EAAS,sBAAsB,IAAIX,CAAI,EAChDc,EAAOF,EAAQ,kBAAkBC,CAAM,EAE7C,OADcC,EAAK,sBAAsB,EAAIA,EAAK,MAAQ,CAACA,CAAI,GAClD,KAAMC,GACjBA,EAAK,cAAc,EAAE,KAAMC,GAAaV,GAAmBU,CAAQ,CAAC,CACtE,CACF,CAEO,SAASC,EAAcjB,EAA0C,CACtE,OAAIA,GAAM,OAAS,mBACV,GAEFA,EAAK,WAAW,KACpBgB,GACCA,EAAS,OAAS,YAClB,CAACA,EAAS,WACRA,EAAS,IAAI,OAAS,cAAgBA,EAAS,IAAI,OAAS,SAC3DA,EAAS,IAAI,OAAS,WAAaA,EAAS,IAAI,QAAU,UAC7DA,EAAS,MAAM,OAAS,WACxBA,EAAS,MAAM,QAAU,UAC7B,CACF,CClGO,IAAME,EAAkBC,EAG7B,CACA,KAAM,oBACN,KAAM,CACJ,KAAM,UACN,KAAM,CACJ,YAAa,+CACf,EACA,OAAQ,CAAC,EACT,SAAU,CACR,WACE,0HACF,QACE,kFACF,MACE,2FACJ,CACF,EACA,eAAgB,CAAC,EACjB,OAAOC,EAAS,CACd,IAAMC,EAAWC,EAAeF,CAAO,EACjCG,EAAcC,GAClBC,EAAcD,CAAI,GAAKE,EAAWL,EAAUG,CAAI,EAClD,MAAO,CACL,QAAQA,EAAM,CAEV,OAAOA,EAAK,OAAU,UACtBA,EAAK,OAAO,OAAS,qBACrBG,EAAeH,EAAK,KAAK,GAEzBJ,EAAQ,OAAO,CAAE,KAAAI,EAAM,UAAW,SAAU,CAAC,CAEjD,EACA,QAAQA,EAAM,CACRG,EAAeH,EAAK,KAAK,GAC3BJ,EAAQ,OAAO,CAAE,KAAAI,EAAM,UAAW,SAAU,CAAC,CAEjD,EACA,iBAAiBA,EAAM,CAChB,CAAC,IAAK,IAAK,IAAK,GAAG,EAAE,SAASA,EAAK,QAAQ,IAI9CI,EAAkBJ,CAAI,GACtBA,EAAK,OAAO,OAAS,oBACrBA,EAAK,OAAO,SAAWA,GACvBK,EAAWL,EAAK,MAAM,IAAM,YAI1BD,EAAWC,EAAK,IAAI,GAAKD,EAAWC,EAAK,KAAK,IAChDJ,EAAQ,OAAO,CAAE,KAAAI,EAAM,UAAW,YAAa,CAAC,EAEpD,EACA,gBAAgBA,EAAM,CACpB,QAAWM,KAASN,EAAK,OACnBG,EAAeG,EAAM,MAAM,QAAUA,EAAM,MAAM,GAAG,GACtDV,EAAQ,OAAO,CAAE,KAAMU,EAAO,UAAW,SAAU,CAAC,CAG1D,EACA,eAAeN,EAAM,CAEjBA,EAAK,OAAO,OAAS,oBACrBK,EAAWL,EAAK,MAAM,IAAM,WAC5BA,EAAK,UAAU,CAAC,GAAG,OAAS,WAC5BA,EAAK,UAAU,CAAC,EAAE,QAAU,GAC5BI,EAAkBJ,EAAK,OAAO,MAAM,GAEpCJ,EAAQ,OAAO,CAAE,KAAAI,EAAM,UAAW,OAAQ,CAAC,CAE/C,CACF,CACF,CACF,CAAC,ECnFD,IAAMO,GAAU,CACd,YACA,uBACA,oBACA,YACF,EAEA,SAASC,GAASC,EAAwB,CACxC,OAAOF,GAAQ,SAASE,CAAK,GAAKA,EAAM,WAAW,WAAW,CAChE,CAEO,IAAMC,EAAoBC,EAA2B,CAC1D,KAAM,sBACN,KAAM,CACJ,KAAM,UACN,KAAM,CAAE,YAAa,mCAAoC,EACzD,OAAQ,CAAC,EACT,SAAU,CACR,SACE,mGACJ,CACF,EACA,eAAgB,CAAC,EACjB,OAAOC,EAAS,CACd,IAAMC,EAAWC,GAAsD,CACrE,GAAIA,EAAK,OAAO,OAAS,oBACvB,OAEF,IAAML,EAAQM,EAAWD,CAAI,EACzBL,GAASO,EAAmBP,CAAK,GACnCG,EAAQ,OAAO,CAAE,KAAAE,EAAM,UAAW,UAAW,CAAC,CAElD,EACA,MAAO,CACL,QAASD,EACT,gBAAiBA,EACjB,kBAAkBC,EAAM,CAClBN,GAASM,EAAK,OAAO,KAAK,GAC5BF,EAAQ,OAAO,CAAE,KAAME,EAAK,OAAQ,UAAW,UAAW,CAAC,CAE/D,EACA,eAAeA,EAAM,CACnB,IAAMG,EAASH,EAAK,OACdI,EACJD,EAAO,OAAS,cAAgB,CAAC,OAAQ,KAAK,EAAE,SAASA,EAAO,IAAI,EAChEE,EACJF,EAAO,OAAS,oBAChB,CAACA,EAAO,UACRA,EAAO,OAAO,OAAS,cACvBA,EAAO,OAAO,OAAS,aACvBA,EAAO,SAAS,OAAS,cACzBA,EAAO,SAAS,OAAS,QACvBC,GAAUC,IACZP,EAAQ,OAAO,CAAE,KAAAE,EAAM,UAAW,UAAW,CAAC,CAElD,CACF,CACF,CACF,CAAC,EC3DD,IAAMM,GAAW,CACf,QACA,QACA,SACA,SACA,OACA,OACA,aACA,YACA,aACA,SACA,mBACA,uBACA,eACA,UACF,EAEA,SAASC,EAAKC,EAAyC,CACrD,GAAIA,EAAK,OAAS,aAChB,OAAOA,EAAK,KAEd,GAAIA,EAAK,OAAS,oBAAsBA,EAAK,SAAS,OAAS,aAC7D,OAAOA,EAAK,SAAS,IAGzB,CAEA,SAASC,EAAWD,EAAqBE,EAA4B,CACnE,OAAIF,EAAK,OAAS,WAAa,OAAOA,EAAK,OAAU,SAC5C,CAAC,GAAGE,CAAI,EAAE,KAAMC,GAAQ,IAAI,OAAO,OAAOA,CAAG,GAAG,EAAE,KAAKH,EAAK,KAAK,CAAC,EAEvEA,EAAK,OAAS,kBACTA,EAAK,OAAO,KAAMI,GACvBH,EACE,CAAE,GAAGG,EAAM,KAAM,UAAW,MAAOA,EAAK,MAAM,GAAI,EAClDF,CACF,CACF,EAEEF,EAAK,OAAS,mBACTA,EAAK,WAAW,KACpBK,GACCA,EAAS,OAAS,aAChBA,EAAS,IAAI,OAAS,cAAgBH,EAAK,IAAIG,EAAS,IAAI,IAAI,GAC/DA,EAAS,IAAI,OAAS,WACrB,OAAOA,EAAS,IAAI,OAAU,UAC9BH,EAAK,IAAIG,EAAS,IAAI,KAAK,EACnC,EAEK,EACT,CAEO,IAAMC,EAAmBC,EAA6C,CAC3E,KAAM,oBACN,KAAM,CACJ,KAAM,UACN,KAAM,CAAE,YAAa,oDAAqD,EAC1E,OAAQ,CACN,CACE,KAAM,SACN,WAAY,CAAE,KAAM,CAAE,KAAM,QAAS,MAAO,CAAE,KAAM,QAAS,CAAE,CAAE,EACjE,qBAAsB,EACxB,CACF,EACA,SAAU,CACR,QACE,+GACJ,CACF,EACA,eAAgB,CAAC,CAAC,CAAC,EACnB,OAAOC,EAAS,CAACC,CAAO,EAAG,CACzB,IAAMP,EAAO,IAAI,IAAI,CAAC,GAAGJ,GAAU,GAAIW,EAAQ,MAAQ,CAAC,CAAE,CAAC,EACrDC,EAAe,IAAI,IACnBC,EAAqBX,GAAwB,CACjD,IAAMY,EAAOJ,EAAQ,WAAW,QAAQR,CAAI,EAC5C,OACEY,EAAK,SAAS,kBAAkB,GAChC,CAAC,GAAGF,CAAY,EAAE,KAAMG,GACtB,IAAI,OAAO,MAAMA,CAAK,KAAK,EAAE,KAAKD,CAAI,CACxC,CAEJ,EACA,MAAO,CACL,mBAAmBZ,EAAM,CAErBA,EAAK,MAAM,OAAS,kBACpBD,EAAKC,EAAK,KAAK,MAAM,IAAM,kBAEvBA,EAAK,GAAG,OAAS,aACnBU,EAAa,IAAIV,EAAK,GAAG,IAAI,EAE7BA,EAAK,GAAG,OAAS,gBACjBA,EAAK,GAAG,SAAS,CAAC,GAAG,OAAS,cAE9BU,EAAa,IAAIV,EAAK,GAAG,SAAS,CAAC,EAAE,IAAI,EAG3CA,EAAK,GAAG,OAAS,cACjBA,EAAK,MACLW,EAAkBX,EAAK,IAAI,GAE3BU,EAAa,IAAIV,EAAK,GAAG,IAAI,CAEjC,EACA,eAAeA,EAAM,CACnB,IAAMc,EAASf,EAAKC,EAAK,MAAM,GAE7Bc,IAAW,mBACXA,IAAW,YACXA,IAAW,aACXA,IAAW,gBACVd,EAAK,OAAO,OAAS,kBACpBD,EAAKC,EAAK,OAAO,MAAM,IAAM,gBAG/BA,EAAK,UAAU,KACZe,GACCA,EAAS,OAAS,iBAAmBd,EAAWc,EAAUb,CAAI,CAClE,GAEAM,EAAQ,OAAO,CAAE,KAAAR,EAAM,UAAW,SAAU,CAAC,CAEjD,EACA,kBAAkBA,EAAM,CACtB,IAAMgB,EACJhB,EAAK,KAAK,OAAS,iBAAmBA,EAAK,KAAK,OAAS,SACrDiB,EAAOjB,EAAK,WAAW,KAC1BkB,GACCA,EAAU,OAAS,gBACnBA,EAAU,KAAK,OAAS,QACxBA,EAAU,OAAO,OAAS,WAC1BA,EAAU,MAAM,QAAU,QAC9B,EACMC,EAAOnB,EAAK,WAAW,KAC1BkB,GACCA,EAAU,OAAS,gBAAkBA,EAAU,KAAK,OAAS,MACjE,GAEGF,GAAUC,IACXE,GAAM,OAAS,gBACfA,EAAK,OAAO,OAAS,0BACrBR,EAAkBQ,EAAK,MAAM,UAAU,GAEvCX,EAAQ,OAAO,CAAE,KAAAR,EAAM,UAAW,SAAU,CAAC,CAEjD,CACF,CACF,CACF,CAAC,ECjJD,SAASoB,EACPC,EACwC,CACxC,OAAOA,EAAQ,KAAK,KACjBC,GACCA,EAAK,OAAS,qBAAuBA,EAAK,OAAO,QAAU,aAC/D,CACF,CAEA,SAASC,EACPC,EACsC,CACtC,OAAOA,GAAa,WAAW,KAC5BC,GACCA,EAAU,OAAS,mBACnBA,EAAU,SAAS,OAAS,cAC5BA,EAAU,SAAS,OAAS,QAChC,CACF,CAEA,SAASC,GACPC,EACAC,EACAP,EACAQ,EACiB,CACjB,IAAML,EAAcJ,EAAUC,CAAO,EACrC,GAAIE,EAAeC,CAAW,GAAG,MAAM,OAASK,EAC9C,OAEF,IAAMC,EAAeD,IAAU,SAAW,SAAW,aAAaA,CAAK,GACjEE,EAAQP,GAAa,WAAW,OACnCC,GAAcA,EAAU,OAAS,iBACpC,EACA,GAAID,GAAeA,EAAY,aAAe,QAAUO,GAAO,OAAQ,CACrE,IAAMC,EAAOD,EAAM,GAAG,EAAE,EACxB,GAAIC,EACF,OAAOL,EAAM,gBAAgBK,EAAM,KAAKF,CAAY,EAAE,CAE1D,CACA,IAAMG,EAAQZ,EAAQ,KAAK,CAAC,EACtBa,EAAO,YAAYJ,CAAY;AAAA,EACrC,OAAOG,EACHN,EAAM,iBAAiBM,EAAOC,CAAI,EAClCP,EAAM,gBAAgBN,EAASa,CAAI,CACzC,CAEO,SAASC,EACdR,EACAC,EACAP,EACAe,EACAC,EACAR,EACO,CACP,IAAMS,EAAQ,CACZX,EAAM,YAAYS,EAAS,GAAGP,CAAK,UAAUD,EAAO,QAAQS,CAAK,CAAC,GAAG,CACvE,EACME,EAAWb,GAAUC,EAAOC,EAAQP,EAASQ,CAAK,EACxD,OAAIU,GACFD,EAAM,KAAKC,CAAQ,EAEdD,CACT,CAEO,SAASE,EACdZ,EACAP,EACAC,EACuC,CACvC,GAAIC,EAAeH,EAAUC,CAAO,CAAC,EACnC,MAAO,WAET,IAAIoB,EAAqCb,EAAO,SAASN,CAAI,EAC7D,KAAOmB,GAAO,CACZ,GAAIA,EAAM,UAAU,KAAMC,GAAaA,EAAS,OAAS,QAAQ,EAC/D,MAAO,WAETD,EAAQA,EAAM,KAChB,CACA,MAAO,WACT,CAEO,SAASE,EACdtB,EACoB,CACpB,OAAOE,EAAeH,EAAUC,CAAO,CAAC,GAAG,MAAM,IACnD,CAEO,SAASuB,GAAehB,EAAgBN,EAA6B,CAC1E,IAAMuB,EAAQ,IAAI,IACdJ,EAAqCb,EAAO,SAASN,CAAI,EAC7D,KAAOmB,GAAO,CACZ,QAAWC,KAAYD,EAAM,UAC3BI,EAAM,IAAIH,EAAS,IAAI,EAEzBD,EAAQA,EAAM,KAChB,CACA,QAAWK,IAAQ,CACjB,eACA,oBACA,kBACF,EACE,GAAI,CAACD,EAAM,IAAIC,CAAI,EACjB,OAAOA,EAGX,MAAO,iBACT,CC/FA,SAASC,GAAiBC,EAA8B,CACtD,IAAMC,EACJD,EAAK,OAAS,iBAAmBA,EAAK,OAAS,iBAC3CA,EAAK,OACL,OACN,OACEC,GAAQ,OAAS,oBACjBA,EAAO,OAAO,OAAS,cACvBA,EAAO,OAAO,OAAS,QACvBC,EAAWD,CAAM,IAAM,cAE3B,CAEA,SAASE,GAAeH,EAA+B,CACrD,GAAIA,EAAK,OAAO,OAAS,mBAGzB,IAAIE,EAAWF,EAAK,MAAM,IAAM,iBAC9B,OAAOI,EAAkBJ,EAAK,OAAO,MAAM,EAE7C,GAAIE,EAAWF,EAAK,MAAM,IAAM,SAC9B,OAAOI,EAAkBJ,EAAK,UAAU,CAAC,GAAKA,CAAI,EAGtD,CAEA,SAASK,GAAoBL,EAAuC,CAClE,IAAMM,EAASN,EAAK,OACdO,EAAOD,EAAO,OAAS,mBAAqBA,EAAO,OAAS,OAClE,MAAO,GACLC,GAAM,OAAS,kBACfA,EAAK,SAAWD,GAChBJ,EAAWI,CAAM,IAAM,UACvBF,EAAkBG,EAAK,UAAU,CAAC,GAAKA,CAAI,EAE/C,CAEO,IAAMC,GAAiBC,EAAwB,CACpD,KAAM,mBACN,KAAM,CACJ,KAAM,UACN,KAAM,CAAE,YAAa,kDAAmD,EACxE,QAAS,OACT,eAAgB,GAChB,OAAQ,CAAC,EACT,SAAU,CACR,SACE,wFACF,OACE,sEACF,MACE,mHACF,SACE,iEACJ,CACF,EACA,eAAgB,CAAC,EACjB,OAAOC,EAAS,CACd,IAAMC,EAASD,EAAQ,WACjBE,EAAWC,EAAeH,CAAO,EACnCI,EACEC,EAAY,CAChBf,EACAgB,EACAC,IACG,CACH,IAAMC,EAAO,CAAE,KAAAlB,EAAM,UAAW,OAAiB,EAEjD,GADgBmB,EAAcR,EAAQG,EAASd,CAAI,IACnC,WAAY,CAC1B,IAAMoB,EAAQC,GAAeV,EAAQX,CAAI,EACzCU,EAAQ,OAAO,CACb,GAAGQ,EACH,QAAS,CACP,CACE,UAAW,WACX,IAAMI,GACJC,EAAYD,EAAOX,EAAQG,EAASE,EAASC,EAAOG,CAAK,CAC7D,CACF,CACF,CAAC,EACD,MACF,CACAV,EAAQ,OAAO,CACb,GAAGQ,EACH,IAAMI,GAAU,CACd,IAAME,EAAQC,EAAoBX,CAAO,GAAK,SAC9C,OAAOS,EAAYD,EAAOX,EAAQG,EAASE,EAASC,EAAOO,CAAK,CAClE,CACF,CAAC,CACH,EACME,EAAoB1B,GAAkC,CAC1D,IAAMiB,EAAQd,GAAeH,CAAI,EACjC,OAAIiB,GACFF,EAAUf,EAAMA,EAAMiB,CAAK,EACpB,IAEF,EACT,EACA,MAAO,CACL,QAAQjB,EAAM,CACZc,EAAUd,CACZ,EACA,cAAcA,EAAM,CAEhBD,GAAiBC,CAAI,GACrB2B,EAAc3B,EAAK,UAAU,CAAC,CAAC,GAC/B,CAACK,GAAoBL,CAAI,GAEzBU,EAAQ,OAAO,CAAE,KAAAV,EAAM,UAAW,UAAW,CAAC,CAElD,EACA,eAAeA,EAAM,CACnB,GAAI,CAAA0B,EAAiB1B,CAAI,EAGzB,IAAID,GAAiBC,CAAI,GAAK2B,EAAc3B,EAAK,UAAU,CAAC,CAAC,EAAG,CAC9DU,EAAQ,OAAO,CAAE,KAAAV,EAAM,UAAW,UAAW,CAAC,EAC9C,MACF,CAEEA,EAAK,OAAO,OAAS,oBACrBE,EAAWF,EAAK,MAAM,IAAM,kBAC5BA,EAAK,UAAU,KAAK2B,CAAa,GAEjCjB,EAAQ,OAAO,CAAE,KAAAV,EAAM,UAAW,UAAW,CAAC,EAElD,EACA,gBAAgBA,EAAM,CACpB,QAAW4B,KAAc5B,EAAK,YAAa,CACzC,IAAMiB,EAAQb,EAAkBwB,CAAU,EACtCX,EACFF,EAAUa,EAAYA,EAAYX,CAAK,EAC9BY,EAAcD,CAAU,GACjClB,EAAQ,OAAO,CAAE,KAAMkB,EAAY,UAAW,OAAQ,CAAC,CAE3D,CACF,EACA,uBAAuB5B,EAAM,CAEzBA,EAAK,OAAO,OAAS,gBACrBA,EAAK,WAAW,OAAS,uBAKzBI,EAAkBJ,EAAK,UAAU,GACjC6B,EAAc7B,EAAK,UAAU,EAE7BU,EAAQ,OAAO,CAAE,KAAMV,EAAK,WAAY,UAAW,OAAQ,CAAC,EACnD8B,EAAWlB,EAAUZ,EAAK,UAAU,GAC7CU,EAAQ,OAAO,CAAE,KAAMV,EAAK,WAAY,UAAW,QAAS,CAAC,EAEjE,CACF,CACF,CACF,CAAC,EC1KD,IAAM+B,EAAuD,CAC3D,IAAK,CAAE,KAAM,qBAAsB,KAAM,kCAAmC,EAC5E,SAAU,CAAE,KAAM,uBAAwB,KAAM,sBAAuB,EACvE,OAAQ,CAAE,KAAM,qBAAsB,KAAM,oBAAqB,CACnE,EAEA,SAASC,GACPC,EACAC,EACkB,CAClB,IAAMC,EAAOD,EAAK,MAAQD,EAAO,QAAQC,EAAK,KAAK,EAAI,GACvD,GAAI,qCAAqC,KAAKC,CAAI,EAChD,MAAO,MAET,GAAI,oDAAoD,KAAKA,CAAI,EAC/D,MAAO,WAET,GAAI,kDAAkD,KAAKA,CAAI,EAC7D,MAAO,QAGX,CAEO,IAAMC,GAAgBC,EAAyB,CACpD,KAAM,iBACN,KAAM,CACJ,KAAM,aACN,KAAM,CAAE,YAAa,gDAAiD,EACtE,QAAS,OACT,OAAQ,CAAC,EACT,SAAU,CACR,OAAQ,gEACV,CACF,EACA,eAAgB,CAAC,EACjB,OAAOC,EAAS,CACd,MAAO,CACL,kBAAkBJ,EAAM,CACtB,IAAMK,EAAQL,EAAK,WAAW,KAC3BM,GACCA,EAAU,OAAS,gBACnBA,EAAU,KAAK,OAAS,SAC5B,EACA,GAAI,CAACD,EACH,OAEF,IAAME,EAAOT,GAAYM,EAAQ,WAAYC,CAAK,EAEhD,CAACE,GACDP,EAAK,WAAW,KACbM,GACCA,EAAU,OAAS,gBACnBA,EAAU,KAAK,OAAST,EAAOU,CAAI,EAAE,IACzC,GAIFH,EAAQ,OAAO,CACb,KAAMC,EACN,UAAW,SACX,KAAM,CAAE,OAAQR,EAAOU,CAAI,EAAE,IAAK,EAClC,IAAMC,GACJA,EAAM,gBAAgBR,EAAK,KAAM,IAAIH,EAAOU,CAAI,EAAE,IAAI,EAAE,CAC5D,CAAC,CACH,CACF,CACF,CACF,CAAC,EfxDD,IAAME,GAAQ,CACZ,gBAAiBC,EACjB,sBAAuBC,EACvB,mBAAoBC,EACpB,iBAAkBC,EAClB,oBAAqBC,EACrB,mBAAoBC,GACpB,oBAAqBC,EACrB,eAAgBC,EAChB,iBAAkBC,GAClB,qBAAsBC,EACtB,iBAAkBC,CACpB,EAIMC,GAAU,OAAO,YACrB,OAAO,KAAKZ,EAAK,EAAE,IAAKa,GAAS,CAC/B,UAAUA,CAAI,GACdA,IAAS,iBAAmB,OAAS,OACvC,CAAC,CACH,EACMC,GAAc,CAAE,GAAGF,GAAS,wBAAyB,OAAQ,EAE7DG,EAAS,CACb,KAAM,CAAE,KAAM,uBAAwB,QAAS,OAAQ,EACvD,MAAAf,GACA,QAAS,CAAC,CACZ,EAEMgB,EAAO,CACX,KAAM,eACN,MAAO,CAAC,sCAAsC,EAC9C,gBAAiB,CAAE,OAAAC,EAAO,EAC1B,QAAS,CAAE,OAAQF,EAAQ,WAAYG,GAAS,cAAeC,EAAW,EAC1E,MAAO,CACL,GAAGD,GAAQ,YAAY,YAAY,MACnC,6BAA8B,QAC9B,GAAGN,EACL,CACF,EACMQ,EAAgB,CACpB,KAAM,wBACN,MAAO,CAAC,2BAA4B,uBAAuB,EAC3D,MAAO,CAAE,wBAAyB,KAAM,CAC1C,EAKMC,EAAQ,CACZ,KAAM,eACN,MAAO,CAAC,uBAAuB,EAC/B,gBAAiB,CAAE,cAAe,CAAE,eAAgB,EAAK,CAAE,CAC7D,EAEAN,EAAO,QAAU,CACf,YAAa,CAACC,EAAMK,EAAOD,CAAa,EACxC,OAAQ,CACN,CAAE,GAAGJ,EAAM,MAAO,CAAE,GAAGA,EAAK,MAAO,GAAGF,EAAY,CAAE,EACpDO,EACAD,CACF,EACA,kBAAmB,CACjBJ,EACAK,EACAD,EACA,CACE,KAAM,sBACN,MAAO,CAAC,gCAAgC,EACxC,MAAO,CAAE,uBAAwB,KAAM,CACzC,EACA,CACE,KAAM,0BACN,MAAO,CAAC,4CAA4C,EACpD,MAAO,CAAE,wBAAyB,KAAM,CAC1C,EACA,CACE,KAAM,wBACN,MAAO,CACL,4BACA,0CAEA,iCACF,EACA,MAAO,CACL,2BAA4B,MAC5B,0BAA2B,KAC7B,CACF,EACA,CACE,KAAM,0BACN,MAAO,CAAC,gDAAgD,EACxD,MAAO,CAAE,2BAA4B,KAAM,CAC7C,EACA,CACE,KAAM,gCACN,MAAO,CACL,4CACA,0CACA,2CACA,2CACF,EACA,MAAO,CAAE,2BAA4B,KAAM,CAC7C,CACF,CACF,EAeA,IAAOE,GAAQC","names":["parser","jsxA11y","reactHooks","ESLintUtils","createRule","name","cartName","storageKey","calleeName","node","variableName","cartInitial","value","cartViaSdk","createRule","context","method","key","id","call","minimatch","endpointPattern","trackingPattern","paymentHostPattern","staticText","node","part","jsxAttribute","name","attribute","jsxValue","isZappieEndpoint","value","isTrackingEndpoint","isInternalSdkImport","isServerFile","filename","patterns","path","pattern","autocomplete","paymentField","providerImport","value","checkoutViaSdk","createRule","context","node","tag","auto","jsxValue","jsxAttribute","name","id","target","cardInput","providerTarget","paymentHostPattern","mappedBy","node","current","parent","firstOnly","source","callback","text","index","imagePriority","createRule","context","images","priority","jsxAttribute","disabled","targetsItem","attribute","expression","target","jsxValue","itemLinkPrefetch","createRule","context","links","node","item","jsxAttribute","defaults","authValue","browserStorage","globals","isStorage","node","memberKey","staticText","authImport","value","noCustomAuth","createRule","context","options","server","isServerFile","kind","type","jsxValue","jsxAttribute","auto","first","key","cookie","message","value","noDirectApi","createRule","context","inspect","node","staticText","isZappieEndpoint","isInternalSdkImport","ESLintUtils","moneyLiteralPattern","isMoneyLiteral","value","memberName","node","isAmountMinor","amountOwner","dividedMinorOwner","parserServices","context","declaredBySdkMoney","symbol","declaration","path","isSdkMoney","services","checker","tsNode","type","part","property","currencyStyle","noMoneyLiterals","createRule","context","services","parserServices","arithmetic","node","isAmountMinor","isSdkMoney","isMoneyLiteral","dividedMinorOwner","memberName","quasi","imports","imported","value","noTrackingScripts","createRule","context","inspect","node","staticText","isTrackingEndpoint","callee","direct","dataLayer","defaults","name","node","carriesKey","keys","key","part","property","overlayPrimitive","createRule","context","options","searchValues","derivedFromSearch","text","value","callee","argument","dialog","role","attribute","open","sdkImport","program","node","importedFormat","declaration","specifier","importFix","fixer","source","local","importedName","named","last","first","text","formatFixes","replace","owner","fixes","addition","formatBinding","scope","variable","importedFormatLocal","availableAlias","names","name","intlNumberFormat","node","callee","memberName","formattedMinor","dividedMinorOwner","formatsDividedMinor","member","call","useFormatMoney","createRule","context","source","services","parserServices","program","reportFix","replace","owner","data","formatBinding","alias","availableAlias","fixer","formatFixes","local","importedFormatLocal","inspectFormatter","currencyStyle","expression","isAmountMinor","isSdkMoney","marker","handlerKind","source","node","text","verifyMarkers","createRule","context","click","attribute","kind","fixer","rules","noDirectApi","noTrackingScripts","checkoutViaSdk","noCustomAuth","noMoneyLiterals","useFormatMoney","overlayPrimitive","cartViaSdk","verifyMarkers","itemLinkPrefetch","imagePriority","enabled","name","strictRules","plugin","base","parser","jsxA11y","reactHooks","searchDialogs","typed","src_default","plugin"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,52 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-zappie",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Flat ESLint rules for Zappie storefront projects.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/zappie/storefront.git",
|
|
9
|
+
"directory": "packages/eslint-plugin-zappie"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/zappie/storefront#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/zappie/storefront/issues"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"import": "./dist/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"CHANGELOG.md"
|
|
27
|
+
],
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"eslint": "^9.0.0"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@typescript-eslint/parser": "8.48.0",
|
|
33
|
+
"@typescript-eslint/utils": "8.48.0",
|
|
34
|
+
"eslint-plugin-jsx-a11y": "6.10.2",
|
|
35
|
+
"eslint-plugin-react-hooks": "7.0.1",
|
|
36
|
+
"minimatch": "^10.2.5"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/eslint-plugin-jsx-a11y": "^6.10.1",
|
|
40
|
+
"@typescript-eslint/rule-tester": "8.48.0"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public",
|
|
44
|
+
"provenance": false
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsup",
|
|
48
|
+
"docs": "pnpm build && node scripts/build-docs.mjs",
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"check-types": "tsc --noEmit -p ."
|
|
51
|
+
}
|
|
6
52
|
}
|