├── .github
└── workflows
│ └── cd.yml
├── .gitignore
├── README.md
├── biome.json
├── graphql.config.yml
├── index.html
├── package.json
├── pnpm-lock.yaml
├── postcss.config.cjs
├── public
└── vite.svg
├── schema-storefront.graphql
├── src
├── App.tsx
├── assets
│ └── usage-stately-actors.mp4
├── commons
│ ├── entities.ts
│ └── http-errors.ts
├── components
│ └── Notifications.tsx
├── data
│ └── library-static-images-resources.data.ts
├── environment.ts
├── hooks
│ └── actor-ref-creator.hook.tsx
├── index.css
├── machines
│ ├── inspector
│ │ └── inspector.machine.tsx
│ ├── resource-picker
│ │ ├── resource-picker.context.ts
│ │ ├── resource-picker.machine.ts
│ │ └── resource-picker.view.tsx
│ └── root
│ │ ├── root.machine.context.tsx
│ │ ├── root.machine.ts
│ │ └── root.view.tsx
├── main.tsx
├── providers
│ └── polaris.provider.tsx
├── queries.ts
├── shopify.config.ts
├── utils.ts
└── vite-env.d.ts
├── tailwind.config.cjs
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
/.github/workflows/cd.yml:
--------------------------------------------------------------------------------
1 | # Simple workflow for deploying static content to GitHub Pages
2 | name: Deploy static content to Pages
3 |
4 | on:
5 | # Runs on pushes targeting the default branch
6 | push:
7 | branches: ['main']
8 |
9 | # Allows you to run this workflow manually from the Actions tab
10 | workflow_dispatch:
11 |
12 | # Sets the GITHUB_TOKEN permissions to allow deployment to GitHub Pages
13 | permissions:
14 | contents: read
15 | pages: write
16 | id-token: write
17 |
18 | # Allow one concurrent deployment
19 | concurrency:
20 | group: 'pages'
21 | cancel-in-progress: true
22 |
23 | jobs:
24 | # Single deploy job since we're just deploying
25 | deploy:
26 | environment:
27 | name: github-pages
28 | url: ${{ steps.deployment.outputs.page_url }}
29 | runs-on: ubuntu-latest
30 | steps:
31 | - name: "Install pnpm"
32 | uses: pnpm/action-setup@v4
33 | with:
34 | version: 9
35 | - name: Checkout
36 | uses: actions/checkout@v4
37 | - name: Set up Node
38 | uses: actions/setup-node@v4
39 | with:
40 | node-version: 22.x
41 | cache: 'pnpm'
42 | - name: Install dependencies
43 | run: pnpm install --frozen-lockfile
44 | - name: Build
45 | run: pnpm run build
46 | - name: Setup Pages
47 | uses: actions/configure-pages@v4
48 | - name: Upload artifact
49 | uses: actions/upload-pages-artifact@v3
50 | with:
51 | path: './dist'
52 | - name: Deploy to GitHub Pages
53 | id: deployment
54 | uses: actions/deploy-pages@v4
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Shopify Special Resource Picker
2 |
3 | A Polaris resource picker which can handle any resources, It's intended to explain the usage of Finite State Machines and the Actor Model using XState
4 |
5 | [Video](https://github.com/user-attachments/assets/8fc37aeb-7b32-4c03-bd5e-0eeed9e2a83a)
6 |
7 | You can check [the live demo](https://djang0dev.github.io/special-shopify-resource-picker/), don't forget to click on the inspector
8 |
9 | For more explanations about how I did this, you can check this [thread](https://x.com/djang0_dev/status/1870493077238083829)
10 |
11 | ## Logic
12 |
13 | We use the [Actor Model](https://stately.ai/docs/actor-model) for responsibility delegation:
14 |
15 | 
16 |
--------------------------------------------------------------------------------
/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
3 | "organizeImports": {
4 | "enabled": true
5 | },
6 | "files": {
7 | "maxSize": 2097152,
8 | "ignore": [
9 | "theme.d.ts",
10 | "documentation/plugins",
11 | "documentation/static",
12 | "libs/graphql-codegen",
13 | "libs/translations/src/i18n/**/*"
14 | ]
15 | },
16 | "formatter": {
17 | "enabled": true,
18 | "indentStyle": "space",
19 | "indentWidth": 2,
20 | "lineWidth": 100
21 | },
22 |
23 | "javascript": {
24 | "parser": {
25 | "unsafeParameterDecoratorsEnabled": true
26 | },
27 | "formatter": {
28 | "enabled": true,
29 | "jsxQuoteStyle": "double",
30 | "arrowParentheses": "always",
31 | "quoteStyle": "double",
32 | "semicolons": "asNeeded",
33 | "trailingCommas": "all",
34 | "attributePosition": "auto",
35 | "indentStyle": "space",
36 | "indentWidth": 2,
37 | "bracketSameLine": true,
38 | "quoteProperties": "asNeeded",
39 | "bracketSpacing": true,
40 | "lineWidth": 100
41 | }
42 | },
43 | "json": {
44 | "formatter": {
45 | "enabled": true
46 | },
47 | "parser": {
48 | "allowComments": false,
49 | "allowTrailingCommas": false
50 | }
51 | },
52 | "linter": {
53 | "enabled": true,
54 | "rules": {
55 | "recommended": true,
56 | "a11y": {
57 | "noAriaHiddenOnFocusable": "warn",
58 | "noAutofocus": "warn",
59 | "noBlankTarget": "error",
60 | "noNoninteractiveElementToInteractiveRole": "warn",
61 | "noNoninteractiveTabindex": "warn",
62 | "noPositiveTabindex": "warn",
63 | "noSvgWithoutTitle": "warn",
64 | "useAltText": "warn",
65 | "useButtonType": "warn",
66 | "useHtmlLang": "warn",
67 | "useIframeTitle": "warn",
68 | "useKeyWithClickEvents": "warn",
69 | "useKeyWithMouseEvents": "warn",
70 | "useValidAnchor": "warn"
71 | },
72 | "nursery": {},
73 | "complexity": {
74 | "useRegexLiterals": "error",
75 | "noExcessiveCognitiveComplexity": {
76 | "options": {
77 | "maxAllowedComplexity": 18
78 | },
79 | "level": "error"
80 | },
81 | "noBannedTypes": "warn",
82 | "noExtraBooleanCast": "warn",
83 | "noForEach": "off",
84 | "noStaticOnlyClass": "error",
85 | "noUselessCatch": "error",
86 | "noUselessConstructor": "error",
87 | "noUselessFragments": "off",
88 | "noUselessRename": "error",
89 | "noUselessSwitchCase": "error",
90 | "useArrowFunction": "error",
91 | "useLiteralKeys": "warn",
92 | "useOptionalChain": "error"
93 | },
94 | "correctness": {
95 | "noUndeclaredVariables": "warn",
96 | "noConstAssign": "error",
97 | "noChildrenProp": "warn",
98 | "noPrecisionLoss": "error",
99 | "useIsNan": "error",
100 | "noInnerDeclarations": "error",
101 | "noUnusedVariables": "error",
102 | "noUnusedImports": "error",
103 | "noConstantCondition": "error",
104 | "noEmptyPattern": "error",
105 | "noSwitchDeclarations": "error",
106 | "noUnreachable": "error",
107 | "noUnsafeOptionalChaining": "error",
108 | "useExhaustiveDependencies": "error",
109 | "useHookAtTopLevel": "error"
110 | },
111 | "performance": {
112 | "noAccumulatingSpread": "warn",
113 | "noDelete": "warn"
114 | },
115 | "security": {
116 | "noGlobalEval": "error",
117 | "noDangerouslySetInnerHtmlWithChildren": "error",
118 | "noDangerouslySetInnerHtml": "error"
119 | },
120 | "style": {
121 | "useWhile": "warn",
122 | "noVar": "error",
123 | "useImportType": "off",
124 | "noCommaOperator": "error",
125 | "noNonNullAssertion": "warn",
126 | "noParameterAssign": "warn",
127 | "noUnusedTemplateLiteral": "warn",
128 | "noUselessElse": "error",
129 | "useConst": "error",
130 | "useDefaultParameterLast": "warn",
131 | "useSelfClosingElements": "error",
132 | "useSingleVarDeclarator": "error",
133 | "useTemplate": "error"
134 | },
135 | "suspicious": {
136 | "noDuplicateCase": "error",
137 | "useIsArray": "warn",
138 | "noArrayIndexKey": "warn",
139 | "noAssignInExpressions": "error",
140 | "noAsyncPromiseExecutor": "error",
141 | "noConfusingVoidType": "warn",
142 | "noControlCharactersInRegex": "error",
143 | "noDoubleEquals": "error",
144 | "noDuplicateJsxProps": "error",
145 | "noDuplicateObjectKeys": "error",
146 | "noEmptyInterface": "error",
147 | "noExplicitAny": "warn",
148 | "noFallthroughSwitchClause": "error",
149 | "noGlobalIsNan": "error",
150 | "noImplicitAnyLet": "warn",
151 | "noPrototypeBuiltins": "error",
152 | "noRedeclare": "error",
153 | "noRedundantUseStrict": "error",
154 | "noSelfCompare": "error",
155 | "noShadowRestrictedNames": "warn",
156 | "useDefaultSwitchClauseLast": "error",
157 | "useValidTypeof": "error"
158 | }
159 | }
160 | },
161 | "vcs": {
162 | "clientKind": "git",
163 | "defaultBranch": "main",
164 | "enabled": true,
165 | "useIgnoreFile": true
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/graphql.config.yml:
--------------------------------------------------------------------------------
1 | projects:
2 | main:
3 | schema: "./schema-storefront.graphql"
4 | documents: "./src/**/*.{tsx,ts}"
5 | exclude: ["**/node_modules/**"]
6 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Special Shopify Resource Picker
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "demo-agnostic-resource-picker-xstate",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint .",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "graphql-request": "7.1.2",
14 | "@shopify/polaris-icons": "9.3.0",
15 | "remeda": "2.17.4",
16 | "@shopify/polaris": "13.9.1",
17 | "@xstate/react": "4.1.3",
18 | "mutative": "1.1.0",
19 | "react": "^18.3.1",
20 | "react-dom": "^18.3.1",
21 | "react-hot-toast": "2.4.1",
22 | "react-infinite-scroll-hook": "5.0.1",
23 | "xstate": "5.18.2",
24 | "zod": "3.23.8",
25 | "flat": "6.0.1",
26 | "set-value": "4.1.0",
27 | "@statelyai/inspect": "0.4.0"
28 | },
29 | "devDependencies": {
30 | "@types/set-value": "4.0.3",
31 | "@biomejs/biome": "1.9.4",
32 | "@types/react": "^18.3.12",
33 | "@types/react-dom": "^18.3.1",
34 | "@vitejs/plugin-react": "^4.3.4",
35 | "autoprefixer": "^10.4.20",
36 | "globals": "^15.12.0",
37 | "postcss": "^8.4.49",
38 | "tailwindcss": "^3.4.16",
39 | "typescript": "~5.6.2",
40 | "vite": "^6.0.1"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: '6.0'
2 |
3 | settings:
4 | autoInstallPeers: true
5 | excludeLinksFromLockfile: false
6 |
7 | dependencies:
8 | '@shopify/polaris':
9 | specifier: 13.9.1
10 | version: 13.9.1(react-dom@18.3.1)(react@18.3.1)
11 | '@shopify/polaris-icons':
12 | specifier: 9.3.0
13 | version: 9.3.0(react@18.3.1)
14 | '@statelyai/inspect':
15 | specifier: 0.4.0
16 | version: 0.4.0(ws@8.18.0)(xstate@5.18.2)
17 | '@xstate/react':
18 | specifier: 4.1.3
19 | version: 4.1.3(@types/react@18.3.16)(react@18.3.1)(xstate@5.18.2)
20 | flat:
21 | specifier: 6.0.1
22 | version: 6.0.1
23 | graphql-request:
24 | specifier: 7.1.2
25 | version: 7.1.2(graphql@16.9.0)
26 | mutative:
27 | specifier: 1.1.0
28 | version: 1.1.0
29 | react:
30 | specifier: ^18.3.1
31 | version: 18.3.1
32 | react-dom:
33 | specifier: ^18.3.1
34 | version: 18.3.1(react@18.3.1)
35 | react-hot-toast:
36 | specifier: 2.4.1
37 | version: 2.4.1(csstype@3.1.3)(react-dom@18.3.1)(react@18.3.1)
38 | react-infinite-scroll-hook:
39 | specifier: 5.0.1
40 | version: 5.0.1(react-dom@18.3.1)(react@18.3.1)
41 | remeda:
42 | specifier: 2.17.4
43 | version: 2.17.4
44 | set-value:
45 | specifier: 4.1.0
46 | version: 4.1.0
47 | xstate:
48 | specifier: 5.18.2
49 | version: 5.18.2
50 | zod:
51 | specifier: 3.23.8
52 | version: 3.23.8
53 |
54 | devDependencies:
55 | '@biomejs/biome':
56 | specifier: 1.9.4
57 | version: 1.9.4
58 | '@types/react':
59 | specifier: ^18.3.12
60 | version: 18.3.16
61 | '@types/react-dom':
62 | specifier: ^18.3.1
63 | version: 18.3.5(@types/react@18.3.16)
64 | '@types/set-value':
65 | specifier: 4.0.3
66 | version: 4.0.3
67 | '@vitejs/plugin-react':
68 | specifier: ^4.3.4
69 | version: 4.3.4(vite@6.0.3)
70 | autoprefixer:
71 | specifier: ^10.4.20
72 | version: 10.4.20(postcss@8.4.49)
73 | globals:
74 | specifier: ^15.12.0
75 | version: 15.13.0
76 | postcss:
77 | specifier: ^8.4.49
78 | version: 8.4.49
79 | tailwindcss:
80 | specifier: ^3.4.16
81 | version: 3.4.16
82 | typescript:
83 | specifier: ~5.6.2
84 | version: 5.6.3
85 | vite:
86 | specifier: ^6.0.1
87 | version: 6.0.3
88 |
89 | packages:
90 |
91 | /@alloc/quick-lru@5.2.0:
92 | resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
93 | engines: {node: '>=10'}
94 | dev: true
95 |
96 | /@ampproject/remapping@2.3.0:
97 | resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
98 | engines: {node: '>=6.0.0'}
99 | dependencies:
100 | '@jridgewell/gen-mapping': 0.3.8
101 | '@jridgewell/trace-mapping': 0.3.25
102 | dev: true
103 |
104 | /@babel/code-frame@7.26.2:
105 | resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==}
106 | engines: {node: '>=6.9.0'}
107 | dependencies:
108 | '@babel/helper-validator-identifier': 7.25.9
109 | js-tokens: 4.0.0
110 | picocolors: 1.1.1
111 | dev: true
112 |
113 | /@babel/compat-data@7.26.3:
114 | resolution: {integrity: sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==}
115 | engines: {node: '>=6.9.0'}
116 | dev: true
117 |
118 | /@babel/core@7.26.0:
119 | resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==}
120 | engines: {node: '>=6.9.0'}
121 | dependencies:
122 | '@ampproject/remapping': 2.3.0
123 | '@babel/code-frame': 7.26.2
124 | '@babel/generator': 7.26.3
125 | '@babel/helper-compilation-targets': 7.25.9
126 | '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0)
127 | '@babel/helpers': 7.26.0
128 | '@babel/parser': 7.26.3
129 | '@babel/template': 7.25.9
130 | '@babel/traverse': 7.26.4
131 | '@babel/types': 7.26.3
132 | convert-source-map: 2.0.0
133 | debug: 4.4.0
134 | gensync: 1.0.0-beta.2
135 | json5: 2.2.3
136 | semver: 6.3.1
137 | transitivePeerDependencies:
138 | - supports-color
139 | dev: true
140 |
141 | /@babel/generator@7.26.3:
142 | resolution: {integrity: sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==}
143 | engines: {node: '>=6.9.0'}
144 | dependencies:
145 | '@babel/parser': 7.26.3
146 | '@babel/types': 7.26.3
147 | '@jridgewell/gen-mapping': 0.3.8
148 | '@jridgewell/trace-mapping': 0.3.25
149 | jsesc: 3.1.0
150 | dev: true
151 |
152 | /@babel/helper-compilation-targets@7.25.9:
153 | resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==}
154 | engines: {node: '>=6.9.0'}
155 | dependencies:
156 | '@babel/compat-data': 7.26.3
157 | '@babel/helper-validator-option': 7.25.9
158 | browserslist: 4.24.2
159 | lru-cache: 5.1.1
160 | semver: 6.3.1
161 | dev: true
162 |
163 | /@babel/helper-module-imports@7.25.9:
164 | resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==}
165 | engines: {node: '>=6.9.0'}
166 | dependencies:
167 | '@babel/traverse': 7.26.4
168 | '@babel/types': 7.26.3
169 | transitivePeerDependencies:
170 | - supports-color
171 | dev: true
172 |
173 | /@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0):
174 | resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==}
175 | engines: {node: '>=6.9.0'}
176 | peerDependencies:
177 | '@babel/core': ^7.0.0
178 | dependencies:
179 | '@babel/core': 7.26.0
180 | '@babel/helper-module-imports': 7.25.9
181 | '@babel/helper-validator-identifier': 7.25.9
182 | '@babel/traverse': 7.26.4
183 | transitivePeerDependencies:
184 | - supports-color
185 | dev: true
186 |
187 | /@babel/helper-plugin-utils@7.25.9:
188 | resolution: {integrity: sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==}
189 | engines: {node: '>=6.9.0'}
190 | dev: true
191 |
192 | /@babel/helper-string-parser@7.25.9:
193 | resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==}
194 | engines: {node: '>=6.9.0'}
195 | dev: true
196 |
197 | /@babel/helper-validator-identifier@7.25.9:
198 | resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
199 | engines: {node: '>=6.9.0'}
200 | dev: true
201 |
202 | /@babel/helper-validator-option@7.25.9:
203 | resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==}
204 | engines: {node: '>=6.9.0'}
205 | dev: true
206 |
207 | /@babel/helpers@7.26.0:
208 | resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==}
209 | engines: {node: '>=6.9.0'}
210 | dependencies:
211 | '@babel/template': 7.25.9
212 | '@babel/types': 7.26.3
213 | dev: true
214 |
215 | /@babel/parser@7.26.3:
216 | resolution: {integrity: sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==}
217 | engines: {node: '>=6.0.0'}
218 | hasBin: true
219 | dependencies:
220 | '@babel/types': 7.26.3
221 | dev: true
222 |
223 | /@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.0):
224 | resolution: {integrity: sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==}
225 | engines: {node: '>=6.9.0'}
226 | peerDependencies:
227 | '@babel/core': ^7.0.0-0
228 | dependencies:
229 | '@babel/core': 7.26.0
230 | '@babel/helper-plugin-utils': 7.25.9
231 | dev: true
232 |
233 | /@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.26.0):
234 | resolution: {integrity: sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==}
235 | engines: {node: '>=6.9.0'}
236 | peerDependencies:
237 | '@babel/core': ^7.0.0-0
238 | dependencies:
239 | '@babel/core': 7.26.0
240 | '@babel/helper-plugin-utils': 7.25.9
241 | dev: true
242 |
243 | /@babel/runtime@7.26.0:
244 | resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==}
245 | engines: {node: '>=6.9.0'}
246 | dependencies:
247 | regenerator-runtime: 0.14.1
248 | dev: false
249 |
250 | /@babel/template@7.25.9:
251 | resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==}
252 | engines: {node: '>=6.9.0'}
253 | dependencies:
254 | '@babel/code-frame': 7.26.2
255 | '@babel/parser': 7.26.3
256 | '@babel/types': 7.26.3
257 | dev: true
258 |
259 | /@babel/traverse@7.26.4:
260 | resolution: {integrity: sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==}
261 | engines: {node: '>=6.9.0'}
262 | dependencies:
263 | '@babel/code-frame': 7.26.2
264 | '@babel/generator': 7.26.3
265 | '@babel/parser': 7.26.3
266 | '@babel/template': 7.25.9
267 | '@babel/types': 7.26.3
268 | debug: 4.4.0
269 | globals: 11.12.0
270 | transitivePeerDependencies:
271 | - supports-color
272 | dev: true
273 |
274 | /@babel/types@7.26.3:
275 | resolution: {integrity: sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==}
276 | engines: {node: '>=6.9.0'}
277 | dependencies:
278 | '@babel/helper-string-parser': 7.25.9
279 | '@babel/helper-validator-identifier': 7.25.9
280 | dev: true
281 |
282 | /@biomejs/biome@1.9.4:
283 | resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==}
284 | engines: {node: '>=14.21.3'}
285 | hasBin: true
286 | requiresBuild: true
287 | optionalDependencies:
288 | '@biomejs/cli-darwin-arm64': 1.9.4
289 | '@biomejs/cli-darwin-x64': 1.9.4
290 | '@biomejs/cli-linux-arm64': 1.9.4
291 | '@biomejs/cli-linux-arm64-musl': 1.9.4
292 | '@biomejs/cli-linux-x64': 1.9.4
293 | '@biomejs/cli-linux-x64-musl': 1.9.4
294 | '@biomejs/cli-win32-arm64': 1.9.4
295 | '@biomejs/cli-win32-x64': 1.9.4
296 | dev: true
297 |
298 | /@biomejs/cli-darwin-arm64@1.9.4:
299 | resolution: {integrity: sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==}
300 | engines: {node: '>=14.21.3'}
301 | cpu: [arm64]
302 | os: [darwin]
303 | requiresBuild: true
304 | dev: true
305 | optional: true
306 |
307 | /@biomejs/cli-darwin-x64@1.9.4:
308 | resolution: {integrity: sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==}
309 | engines: {node: '>=14.21.3'}
310 | cpu: [x64]
311 | os: [darwin]
312 | requiresBuild: true
313 | dev: true
314 | optional: true
315 |
316 | /@biomejs/cli-linux-arm64-musl@1.9.4:
317 | resolution: {integrity: sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==}
318 | engines: {node: '>=14.21.3'}
319 | cpu: [arm64]
320 | os: [linux]
321 | requiresBuild: true
322 | dev: true
323 | optional: true
324 |
325 | /@biomejs/cli-linux-arm64@1.9.4:
326 | resolution: {integrity: sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==}
327 | engines: {node: '>=14.21.3'}
328 | cpu: [arm64]
329 | os: [linux]
330 | requiresBuild: true
331 | dev: true
332 | optional: true
333 |
334 | /@biomejs/cli-linux-x64-musl@1.9.4:
335 | resolution: {integrity: sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==}
336 | engines: {node: '>=14.21.3'}
337 | cpu: [x64]
338 | os: [linux]
339 | requiresBuild: true
340 | dev: true
341 | optional: true
342 |
343 | /@biomejs/cli-linux-x64@1.9.4:
344 | resolution: {integrity: sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==}
345 | engines: {node: '>=14.21.3'}
346 | cpu: [x64]
347 | os: [linux]
348 | requiresBuild: true
349 | dev: true
350 | optional: true
351 |
352 | /@biomejs/cli-win32-arm64@1.9.4:
353 | resolution: {integrity: sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==}
354 | engines: {node: '>=14.21.3'}
355 | cpu: [arm64]
356 | os: [win32]
357 | requiresBuild: true
358 | dev: true
359 | optional: true
360 |
361 | /@biomejs/cli-win32-x64@1.9.4:
362 | resolution: {integrity: sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==}
363 | engines: {node: '>=14.21.3'}
364 | cpu: [x64]
365 | os: [win32]
366 | requiresBuild: true
367 | dev: true
368 | optional: true
369 |
370 | /@esbuild/aix-ppc64@0.24.0:
371 | resolution: {integrity: sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==}
372 | engines: {node: '>=18'}
373 | cpu: [ppc64]
374 | os: [aix]
375 | requiresBuild: true
376 | dev: true
377 | optional: true
378 |
379 | /@esbuild/android-arm64@0.24.0:
380 | resolution: {integrity: sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==}
381 | engines: {node: '>=18'}
382 | cpu: [arm64]
383 | os: [android]
384 | requiresBuild: true
385 | dev: true
386 | optional: true
387 |
388 | /@esbuild/android-arm@0.24.0:
389 | resolution: {integrity: sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==}
390 | engines: {node: '>=18'}
391 | cpu: [arm]
392 | os: [android]
393 | requiresBuild: true
394 | dev: true
395 | optional: true
396 |
397 | /@esbuild/android-x64@0.24.0:
398 | resolution: {integrity: sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==}
399 | engines: {node: '>=18'}
400 | cpu: [x64]
401 | os: [android]
402 | requiresBuild: true
403 | dev: true
404 | optional: true
405 |
406 | /@esbuild/darwin-arm64@0.24.0:
407 | resolution: {integrity: sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==}
408 | engines: {node: '>=18'}
409 | cpu: [arm64]
410 | os: [darwin]
411 | requiresBuild: true
412 | dev: true
413 | optional: true
414 |
415 | /@esbuild/darwin-x64@0.24.0:
416 | resolution: {integrity: sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==}
417 | engines: {node: '>=18'}
418 | cpu: [x64]
419 | os: [darwin]
420 | requiresBuild: true
421 | dev: true
422 | optional: true
423 |
424 | /@esbuild/freebsd-arm64@0.24.0:
425 | resolution: {integrity: sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==}
426 | engines: {node: '>=18'}
427 | cpu: [arm64]
428 | os: [freebsd]
429 | requiresBuild: true
430 | dev: true
431 | optional: true
432 |
433 | /@esbuild/freebsd-x64@0.24.0:
434 | resolution: {integrity: sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==}
435 | engines: {node: '>=18'}
436 | cpu: [x64]
437 | os: [freebsd]
438 | requiresBuild: true
439 | dev: true
440 | optional: true
441 |
442 | /@esbuild/linux-arm64@0.24.0:
443 | resolution: {integrity: sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==}
444 | engines: {node: '>=18'}
445 | cpu: [arm64]
446 | os: [linux]
447 | requiresBuild: true
448 | dev: true
449 | optional: true
450 |
451 | /@esbuild/linux-arm@0.24.0:
452 | resolution: {integrity: sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==}
453 | engines: {node: '>=18'}
454 | cpu: [arm]
455 | os: [linux]
456 | requiresBuild: true
457 | dev: true
458 | optional: true
459 |
460 | /@esbuild/linux-ia32@0.24.0:
461 | resolution: {integrity: sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==}
462 | engines: {node: '>=18'}
463 | cpu: [ia32]
464 | os: [linux]
465 | requiresBuild: true
466 | dev: true
467 | optional: true
468 |
469 | /@esbuild/linux-loong64@0.24.0:
470 | resolution: {integrity: sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==}
471 | engines: {node: '>=18'}
472 | cpu: [loong64]
473 | os: [linux]
474 | requiresBuild: true
475 | dev: true
476 | optional: true
477 |
478 | /@esbuild/linux-mips64el@0.24.0:
479 | resolution: {integrity: sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==}
480 | engines: {node: '>=18'}
481 | cpu: [mips64el]
482 | os: [linux]
483 | requiresBuild: true
484 | dev: true
485 | optional: true
486 |
487 | /@esbuild/linux-ppc64@0.24.0:
488 | resolution: {integrity: sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==}
489 | engines: {node: '>=18'}
490 | cpu: [ppc64]
491 | os: [linux]
492 | requiresBuild: true
493 | dev: true
494 | optional: true
495 |
496 | /@esbuild/linux-riscv64@0.24.0:
497 | resolution: {integrity: sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==}
498 | engines: {node: '>=18'}
499 | cpu: [riscv64]
500 | os: [linux]
501 | requiresBuild: true
502 | dev: true
503 | optional: true
504 |
505 | /@esbuild/linux-s390x@0.24.0:
506 | resolution: {integrity: sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==}
507 | engines: {node: '>=18'}
508 | cpu: [s390x]
509 | os: [linux]
510 | requiresBuild: true
511 | dev: true
512 | optional: true
513 |
514 | /@esbuild/linux-x64@0.24.0:
515 | resolution: {integrity: sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==}
516 | engines: {node: '>=18'}
517 | cpu: [x64]
518 | os: [linux]
519 | requiresBuild: true
520 | dev: true
521 | optional: true
522 |
523 | /@esbuild/netbsd-x64@0.24.0:
524 | resolution: {integrity: sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==}
525 | engines: {node: '>=18'}
526 | cpu: [x64]
527 | os: [netbsd]
528 | requiresBuild: true
529 | dev: true
530 | optional: true
531 |
532 | /@esbuild/openbsd-arm64@0.24.0:
533 | resolution: {integrity: sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==}
534 | engines: {node: '>=18'}
535 | cpu: [arm64]
536 | os: [openbsd]
537 | requiresBuild: true
538 | dev: true
539 | optional: true
540 |
541 | /@esbuild/openbsd-x64@0.24.0:
542 | resolution: {integrity: sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==}
543 | engines: {node: '>=18'}
544 | cpu: [x64]
545 | os: [openbsd]
546 | requiresBuild: true
547 | dev: true
548 | optional: true
549 |
550 | /@esbuild/sunos-x64@0.24.0:
551 | resolution: {integrity: sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==}
552 | engines: {node: '>=18'}
553 | cpu: [x64]
554 | os: [sunos]
555 | requiresBuild: true
556 | dev: true
557 | optional: true
558 |
559 | /@esbuild/win32-arm64@0.24.0:
560 | resolution: {integrity: sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==}
561 | engines: {node: '>=18'}
562 | cpu: [arm64]
563 | os: [win32]
564 | requiresBuild: true
565 | dev: true
566 | optional: true
567 |
568 | /@esbuild/win32-ia32@0.24.0:
569 | resolution: {integrity: sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==}
570 | engines: {node: '>=18'}
571 | cpu: [ia32]
572 | os: [win32]
573 | requiresBuild: true
574 | dev: true
575 | optional: true
576 |
577 | /@esbuild/win32-x64@0.24.0:
578 | resolution: {integrity: sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==}
579 | engines: {node: '>=18'}
580 | cpu: [x64]
581 | os: [win32]
582 | requiresBuild: true
583 | dev: true
584 | optional: true
585 |
586 | /@graphql-typed-document-node/core@3.2.0(graphql@16.9.0):
587 | resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==}
588 | peerDependencies:
589 | graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
590 | dependencies:
591 | graphql: 16.9.0
592 | dev: false
593 |
594 | /@isaacs/cliui@8.0.2:
595 | resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
596 | engines: {node: '>=12'}
597 | dependencies:
598 | string-width: 5.1.2
599 | string-width-cjs: /string-width@4.2.3
600 | strip-ansi: 7.1.0
601 | strip-ansi-cjs: /strip-ansi@6.0.1
602 | wrap-ansi: 8.1.0
603 | wrap-ansi-cjs: /wrap-ansi@7.0.0
604 | dev: true
605 |
606 | /@jridgewell/gen-mapping@0.3.8:
607 | resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==}
608 | engines: {node: '>=6.0.0'}
609 | dependencies:
610 | '@jridgewell/set-array': 1.2.1
611 | '@jridgewell/sourcemap-codec': 1.5.0
612 | '@jridgewell/trace-mapping': 0.3.25
613 | dev: true
614 |
615 | /@jridgewell/resolve-uri@3.1.2:
616 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
617 | engines: {node: '>=6.0.0'}
618 | dev: true
619 |
620 | /@jridgewell/set-array@1.2.1:
621 | resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
622 | engines: {node: '>=6.0.0'}
623 | dev: true
624 |
625 | /@jridgewell/sourcemap-codec@1.5.0:
626 | resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
627 | dev: true
628 |
629 | /@jridgewell/trace-mapping@0.3.25:
630 | resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
631 | dependencies:
632 | '@jridgewell/resolve-uri': 3.1.2
633 | '@jridgewell/sourcemap-codec': 1.5.0
634 | dev: true
635 |
636 | /@nodelib/fs.scandir@2.1.5:
637 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
638 | engines: {node: '>= 8'}
639 | dependencies:
640 | '@nodelib/fs.stat': 2.0.5
641 | run-parallel: 1.2.0
642 | dev: true
643 |
644 | /@nodelib/fs.stat@2.0.5:
645 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
646 | engines: {node: '>= 8'}
647 | dev: true
648 |
649 | /@nodelib/fs.walk@1.2.8:
650 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
651 | engines: {node: '>= 8'}
652 | dependencies:
653 | '@nodelib/fs.scandir': 2.1.5
654 | fastq: 1.17.1
655 | dev: true
656 |
657 | /@pkgjs/parseargs@0.11.0:
658 | resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
659 | engines: {node: '>=14'}
660 | requiresBuild: true
661 | dev: true
662 | optional: true
663 |
664 | /@rollup/rollup-android-arm-eabi@4.28.1:
665 | resolution: {integrity: sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ==}
666 | cpu: [arm]
667 | os: [android]
668 | requiresBuild: true
669 | dev: true
670 | optional: true
671 |
672 | /@rollup/rollup-android-arm64@4.28.1:
673 | resolution: {integrity: sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA==}
674 | cpu: [arm64]
675 | os: [android]
676 | requiresBuild: true
677 | dev: true
678 | optional: true
679 |
680 | /@rollup/rollup-darwin-arm64@4.28.1:
681 | resolution: {integrity: sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ==}
682 | cpu: [arm64]
683 | os: [darwin]
684 | requiresBuild: true
685 | dev: true
686 | optional: true
687 |
688 | /@rollup/rollup-darwin-x64@4.28.1:
689 | resolution: {integrity: sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ==}
690 | cpu: [x64]
691 | os: [darwin]
692 | requiresBuild: true
693 | dev: true
694 | optional: true
695 |
696 | /@rollup/rollup-freebsd-arm64@4.28.1:
697 | resolution: {integrity: sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA==}
698 | cpu: [arm64]
699 | os: [freebsd]
700 | requiresBuild: true
701 | dev: true
702 | optional: true
703 |
704 | /@rollup/rollup-freebsd-x64@4.28.1:
705 | resolution: {integrity: sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ==}
706 | cpu: [x64]
707 | os: [freebsd]
708 | requiresBuild: true
709 | dev: true
710 | optional: true
711 |
712 | /@rollup/rollup-linux-arm-gnueabihf@4.28.1:
713 | resolution: {integrity: sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA==}
714 | cpu: [arm]
715 | os: [linux]
716 | requiresBuild: true
717 | dev: true
718 | optional: true
719 |
720 | /@rollup/rollup-linux-arm-musleabihf@4.28.1:
721 | resolution: {integrity: sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg==}
722 | cpu: [arm]
723 | os: [linux]
724 | requiresBuild: true
725 | dev: true
726 | optional: true
727 |
728 | /@rollup/rollup-linux-arm64-gnu@4.28.1:
729 | resolution: {integrity: sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA==}
730 | cpu: [arm64]
731 | os: [linux]
732 | requiresBuild: true
733 | dev: true
734 | optional: true
735 |
736 | /@rollup/rollup-linux-arm64-musl@4.28.1:
737 | resolution: {integrity: sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A==}
738 | cpu: [arm64]
739 | os: [linux]
740 | requiresBuild: true
741 | dev: true
742 | optional: true
743 |
744 | /@rollup/rollup-linux-loongarch64-gnu@4.28.1:
745 | resolution: {integrity: sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA==}
746 | cpu: [loong64]
747 | os: [linux]
748 | requiresBuild: true
749 | dev: true
750 | optional: true
751 |
752 | /@rollup/rollup-linux-powerpc64le-gnu@4.28.1:
753 | resolution: {integrity: sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A==}
754 | cpu: [ppc64]
755 | os: [linux]
756 | requiresBuild: true
757 | dev: true
758 | optional: true
759 |
760 | /@rollup/rollup-linux-riscv64-gnu@4.28.1:
761 | resolution: {integrity: sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA==}
762 | cpu: [riscv64]
763 | os: [linux]
764 | requiresBuild: true
765 | dev: true
766 | optional: true
767 |
768 | /@rollup/rollup-linux-s390x-gnu@4.28.1:
769 | resolution: {integrity: sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg==}
770 | cpu: [s390x]
771 | os: [linux]
772 | requiresBuild: true
773 | dev: true
774 | optional: true
775 |
776 | /@rollup/rollup-linux-x64-gnu@4.28.1:
777 | resolution: {integrity: sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw==}
778 | cpu: [x64]
779 | os: [linux]
780 | requiresBuild: true
781 | dev: true
782 | optional: true
783 |
784 | /@rollup/rollup-linux-x64-musl@4.28.1:
785 | resolution: {integrity: sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g==}
786 | cpu: [x64]
787 | os: [linux]
788 | requiresBuild: true
789 | dev: true
790 | optional: true
791 |
792 | /@rollup/rollup-win32-arm64-msvc@4.28.1:
793 | resolution: {integrity: sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A==}
794 | cpu: [arm64]
795 | os: [win32]
796 | requiresBuild: true
797 | dev: true
798 | optional: true
799 |
800 | /@rollup/rollup-win32-ia32-msvc@4.28.1:
801 | resolution: {integrity: sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA==}
802 | cpu: [ia32]
803 | os: [win32]
804 | requiresBuild: true
805 | dev: true
806 | optional: true
807 |
808 | /@rollup/rollup-win32-x64-msvc@4.28.1:
809 | resolution: {integrity: sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA==}
810 | cpu: [x64]
811 | os: [win32]
812 | requiresBuild: true
813 | dev: true
814 | optional: true
815 |
816 | /@shopify/polaris-icons@9.3.0(react@18.3.1):
817 | resolution: {integrity: sha512-fnH5Lcd3WFZNjlxGYGNtnjeq3P2xonRV8vChW4PqBfxdKlY/GQ/3/rIuxHzIgmLL0zukeZUaqERUN0lwSU+Xmg==}
818 | engines: {node: '>=20.10.0'}
819 | peerDependencies:
820 | react: '*'
821 | dependencies:
822 | react: 18.3.1
823 | dev: false
824 |
825 | /@shopify/polaris-tokens@9.4.0:
826 | resolution: {integrity: sha512-jnCNxq9+XfWP9ijkvSsgQH5o3PeYgfWo4/zymoO+AeJvyngL+4LvlZ/nofvYj9DXLo74taTk05xui9qt22Zy2Q==}
827 | engines: {node: '>=20.10.0'}
828 | dependencies:
829 | deepmerge: 4.3.1
830 | dev: false
831 |
832 | /@shopify/polaris@13.9.1(react-dom@18.3.1)(react@18.3.1):
833 | resolution: {integrity: sha512-jSBEDP3LPZlPWJbtwTLdW72K1LS73ocB7x3VXzbi1DvpxUcmVp3K1YxhdKGEKmwHAkcBqIvZCrVNS2jcaTBmhQ==}
834 | engines: {node: '>=20.10.0'}
835 | peerDependencies:
836 | react: ^18.0.0
837 | react-dom: ^18.0.0
838 | dependencies:
839 | '@shopify/polaris-icons': 9.3.0(react@18.3.1)
840 | '@shopify/polaris-tokens': 9.4.0
841 | '@types/react': 18.3.16
842 | '@types/react-dom': 18.3.5(@types/react@18.3.16)
843 | '@types/react-transition-group': 4.4.12(@types/react@18.3.16)
844 | react: 18.3.1
845 | react-dom: 18.3.1(react@18.3.1)
846 | react-fast-compare: 3.2.2
847 | react-transition-group: 4.4.5(react-dom@18.3.1)(react@18.3.1)
848 | dev: false
849 |
850 | /@statelyai/inspect@0.4.0(ws@8.18.0)(xstate@5.18.2):
851 | resolution: {integrity: sha512-VxQldRlKYcu6rzLY83RSXVwMYexkH6hNx85B89YWYyXYWtNGaWHFCwV7a/Kz8FFPeUz8EKVAnyMOg2kNpn07wQ==}
852 | peerDependencies:
853 | xstate: ^5.5.1
854 | dependencies:
855 | fast-safe-stringify: 2.1.1
856 | isomorphic-ws: 5.0.0(ws@8.18.0)
857 | partysocket: 0.0.25
858 | safe-stable-stringify: 2.5.0
859 | superjson: 1.13.3
860 | uuid: 9.0.1
861 | xstate: 5.18.2
862 | transitivePeerDependencies:
863 | - ws
864 | dev: false
865 |
866 | /@types/babel__core@7.20.5:
867 | resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
868 | dependencies:
869 | '@babel/parser': 7.26.3
870 | '@babel/types': 7.26.3
871 | '@types/babel__generator': 7.6.8
872 | '@types/babel__template': 7.4.4
873 | '@types/babel__traverse': 7.20.6
874 | dev: true
875 |
876 | /@types/babel__generator@7.6.8:
877 | resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==}
878 | dependencies:
879 | '@babel/types': 7.26.3
880 | dev: true
881 |
882 | /@types/babel__template@7.4.4:
883 | resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
884 | dependencies:
885 | '@babel/parser': 7.26.3
886 | '@babel/types': 7.26.3
887 | dev: true
888 |
889 | /@types/babel__traverse@7.20.6:
890 | resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==}
891 | dependencies:
892 | '@babel/types': 7.26.3
893 | dev: true
894 |
895 | /@types/estree@1.0.6:
896 | resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
897 | dev: true
898 |
899 | /@types/prop-types@15.7.14:
900 | resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==}
901 |
902 | /@types/react-dom@18.3.5(@types/react@18.3.16):
903 | resolution: {integrity: sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==}
904 | peerDependencies:
905 | '@types/react': ^18.0.0
906 | dependencies:
907 | '@types/react': 18.3.16
908 |
909 | /@types/react-transition-group@4.4.12(@types/react@18.3.16):
910 | resolution: {integrity: sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==}
911 | peerDependencies:
912 | '@types/react': '*'
913 | dependencies:
914 | '@types/react': 18.3.16
915 | dev: false
916 |
917 | /@types/react@18.3.16:
918 | resolution: {integrity: sha512-oh8AMIC4Y2ciKufU8hnKgs+ufgbA/dhPTACaZPM86AbwX9QwnFtSoPWEeRUj8fge+v6kFt78BXcDhAU1SrrAsw==}
919 | dependencies:
920 | '@types/prop-types': 15.7.14
921 | csstype: 3.1.3
922 |
923 | /@types/set-value@4.0.3:
924 | resolution: {integrity: sha512-tSuUcLl6kMzI+l0gG7FZ04xbIcynxNIYgWFj91LPAvRcn7W3L1EveXNdVjqFDgAZPjY1qCOsm8Sb1C70SxAPHw==}
925 | dev: true
926 |
927 | /@vitejs/plugin-react@4.3.4(vite@6.0.3):
928 | resolution: {integrity: sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==}
929 | engines: {node: ^14.18.0 || >=16.0.0}
930 | peerDependencies:
931 | vite: ^4.2.0 || ^5.0.0 || ^6.0.0
932 | dependencies:
933 | '@babel/core': 7.26.0
934 | '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0)
935 | '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0)
936 | '@types/babel__core': 7.20.5
937 | react-refresh: 0.14.2
938 | vite: 6.0.3
939 | transitivePeerDependencies:
940 | - supports-color
941 | dev: true
942 |
943 | /@xstate/react@4.1.3(@types/react@18.3.16)(react@18.3.1)(xstate@5.18.2):
944 | resolution: {integrity: sha512-zhE+ZfrcCR87bu71Rkh5Z5ruZBivR/7uD/dkelzJqjQdI45IZc9DqTI8lL4Cg5+VN2p5k86KxDsusqW1kW11Tg==}
945 | peerDependencies:
946 | react: ^16.8.0 || ^17.0.0 || ^18.0.0
947 | xstate: ^5.18.2
948 | peerDependenciesMeta:
949 | xstate:
950 | optional: true
951 | dependencies:
952 | react: 18.3.1
953 | use-isomorphic-layout-effect: 1.2.0(@types/react@18.3.16)(react@18.3.1)
954 | use-sync-external-store: 1.4.0(react@18.3.1)
955 | xstate: 5.18.2
956 | transitivePeerDependencies:
957 | - '@types/react'
958 | dev: false
959 |
960 | /ansi-regex@5.0.1:
961 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
962 | engines: {node: '>=8'}
963 | dev: true
964 |
965 | /ansi-regex@6.1.0:
966 | resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
967 | engines: {node: '>=12'}
968 | dev: true
969 |
970 | /ansi-styles@4.3.0:
971 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
972 | engines: {node: '>=8'}
973 | dependencies:
974 | color-convert: 2.0.1
975 | dev: true
976 |
977 | /ansi-styles@6.2.1:
978 | resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
979 | engines: {node: '>=12'}
980 | dev: true
981 |
982 | /any-promise@1.3.0:
983 | resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
984 | dev: true
985 |
986 | /anymatch@3.1.3:
987 | resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
988 | engines: {node: '>= 8'}
989 | dependencies:
990 | normalize-path: 3.0.0
991 | picomatch: 2.3.1
992 | dev: true
993 |
994 | /arg@5.0.2:
995 | resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
996 | dev: true
997 |
998 | /autoprefixer@10.4.20(postcss@8.4.49):
999 | resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==}
1000 | engines: {node: ^10 || ^12 || >=14}
1001 | hasBin: true
1002 | peerDependencies:
1003 | postcss: ^8.1.0
1004 | dependencies:
1005 | browserslist: 4.24.2
1006 | caniuse-lite: 1.0.30001688
1007 | fraction.js: 4.3.7
1008 | normalize-range: 0.1.2
1009 | picocolors: 1.1.1
1010 | postcss: 8.4.49
1011 | postcss-value-parser: 4.2.0
1012 | dev: true
1013 |
1014 | /balanced-match@1.0.2:
1015 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
1016 | dev: true
1017 |
1018 | /binary-extensions@2.3.0:
1019 | resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
1020 | engines: {node: '>=8'}
1021 | dev: true
1022 |
1023 | /brace-expansion@2.0.1:
1024 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
1025 | dependencies:
1026 | balanced-match: 1.0.2
1027 | dev: true
1028 |
1029 | /braces@3.0.3:
1030 | resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
1031 | engines: {node: '>=8'}
1032 | dependencies:
1033 | fill-range: 7.1.1
1034 | dev: true
1035 |
1036 | /browserslist@4.24.2:
1037 | resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==}
1038 | engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
1039 | hasBin: true
1040 | dependencies:
1041 | caniuse-lite: 1.0.30001688
1042 | electron-to-chromium: 1.5.73
1043 | node-releases: 2.0.19
1044 | update-browserslist-db: 1.1.1(browserslist@4.24.2)
1045 | dev: true
1046 |
1047 | /camelcase-css@2.0.1:
1048 | resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
1049 | engines: {node: '>= 6'}
1050 | dev: true
1051 |
1052 | /caniuse-lite@1.0.30001688:
1053 | resolution: {integrity: sha512-Nmqpru91cuABu/DTCXbM2NSRHzM2uVHfPnhJ/1zEAJx/ILBRVmz3pzH4N7DZqbdG0gWClsCC05Oj0mJ/1AWMbA==}
1054 | dev: true
1055 |
1056 | /chokidar@3.6.0:
1057 | resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
1058 | engines: {node: '>= 8.10.0'}
1059 | dependencies:
1060 | anymatch: 3.1.3
1061 | braces: 3.0.3
1062 | glob-parent: 5.1.2
1063 | is-binary-path: 2.1.0
1064 | is-glob: 4.0.3
1065 | normalize-path: 3.0.0
1066 | readdirp: 3.6.0
1067 | optionalDependencies:
1068 | fsevents: 2.3.3
1069 | dev: true
1070 |
1071 | /color-convert@2.0.1:
1072 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
1073 | engines: {node: '>=7.0.0'}
1074 | dependencies:
1075 | color-name: 1.1.4
1076 | dev: true
1077 |
1078 | /color-name@1.1.4:
1079 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
1080 | dev: true
1081 |
1082 | /commander@4.1.1:
1083 | resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
1084 | engines: {node: '>= 6'}
1085 | dev: true
1086 |
1087 | /convert-source-map@2.0.0:
1088 | resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
1089 | dev: true
1090 |
1091 | /copy-anything@3.0.5:
1092 | resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
1093 | engines: {node: '>=12.13'}
1094 | dependencies:
1095 | is-what: 4.1.16
1096 | dev: false
1097 |
1098 | /cross-spawn@7.0.6:
1099 | resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
1100 | engines: {node: '>= 8'}
1101 | dependencies:
1102 | path-key: 3.1.1
1103 | shebang-command: 2.0.0
1104 | which: 2.0.2
1105 | dev: true
1106 |
1107 | /cssesc@3.0.0:
1108 | resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
1109 | engines: {node: '>=4'}
1110 | hasBin: true
1111 | dev: true
1112 |
1113 | /csstype@3.1.3:
1114 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
1115 |
1116 | /debug@4.4.0:
1117 | resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
1118 | engines: {node: '>=6.0'}
1119 | peerDependencies:
1120 | supports-color: '*'
1121 | peerDependenciesMeta:
1122 | supports-color:
1123 | optional: true
1124 | dependencies:
1125 | ms: 2.1.3
1126 | dev: true
1127 |
1128 | /deepmerge@4.3.1:
1129 | resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
1130 | engines: {node: '>=0.10.0'}
1131 | dev: false
1132 |
1133 | /didyoumean@1.2.2:
1134 | resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
1135 | dev: true
1136 |
1137 | /dlv@1.1.3:
1138 | resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
1139 | dev: true
1140 |
1141 | /dom-helpers@5.2.1:
1142 | resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
1143 | dependencies:
1144 | '@babel/runtime': 7.26.0
1145 | csstype: 3.1.3
1146 | dev: false
1147 |
1148 | /eastasianwidth@0.2.0:
1149 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
1150 | dev: true
1151 |
1152 | /electron-to-chromium@1.5.73:
1153 | resolution: {integrity: sha512-8wGNxG9tAG5KhGd3eeA0o6ixhiNdgr0DcHWm85XPCphwZgD1lIEoi6t3VERayWao7SF7AAZTw6oARGJeVjH8Kg==}
1154 | dev: true
1155 |
1156 | /emoji-regex@8.0.0:
1157 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
1158 | dev: true
1159 |
1160 | /emoji-regex@9.2.2:
1161 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
1162 | dev: true
1163 |
1164 | /esbuild@0.24.0:
1165 | resolution: {integrity: sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==}
1166 | engines: {node: '>=18'}
1167 | hasBin: true
1168 | requiresBuild: true
1169 | optionalDependencies:
1170 | '@esbuild/aix-ppc64': 0.24.0
1171 | '@esbuild/android-arm': 0.24.0
1172 | '@esbuild/android-arm64': 0.24.0
1173 | '@esbuild/android-x64': 0.24.0
1174 | '@esbuild/darwin-arm64': 0.24.0
1175 | '@esbuild/darwin-x64': 0.24.0
1176 | '@esbuild/freebsd-arm64': 0.24.0
1177 | '@esbuild/freebsd-x64': 0.24.0
1178 | '@esbuild/linux-arm': 0.24.0
1179 | '@esbuild/linux-arm64': 0.24.0
1180 | '@esbuild/linux-ia32': 0.24.0
1181 | '@esbuild/linux-loong64': 0.24.0
1182 | '@esbuild/linux-mips64el': 0.24.0
1183 | '@esbuild/linux-ppc64': 0.24.0
1184 | '@esbuild/linux-riscv64': 0.24.0
1185 | '@esbuild/linux-s390x': 0.24.0
1186 | '@esbuild/linux-x64': 0.24.0
1187 | '@esbuild/netbsd-x64': 0.24.0
1188 | '@esbuild/openbsd-arm64': 0.24.0
1189 | '@esbuild/openbsd-x64': 0.24.0
1190 | '@esbuild/sunos-x64': 0.24.0
1191 | '@esbuild/win32-arm64': 0.24.0
1192 | '@esbuild/win32-ia32': 0.24.0
1193 | '@esbuild/win32-x64': 0.24.0
1194 | dev: true
1195 |
1196 | /escalade@3.2.0:
1197 | resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
1198 | engines: {node: '>=6'}
1199 | dev: true
1200 |
1201 | /event-target-shim@6.0.2:
1202 | resolution: {integrity: sha512-8q3LsZjRezbFZ2PN+uP+Q7pnHUMmAOziU2vA2OwoFaKIXxlxl38IylhSSgUorWu/rf4er67w0ikBqjBFk/pomA==}
1203 | engines: {node: '>=10.13.0'}
1204 | dev: false
1205 |
1206 | /fast-glob@3.3.2:
1207 | resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
1208 | engines: {node: '>=8.6.0'}
1209 | dependencies:
1210 | '@nodelib/fs.stat': 2.0.5
1211 | '@nodelib/fs.walk': 1.2.8
1212 | glob-parent: 5.1.2
1213 | merge2: 1.4.1
1214 | micromatch: 4.0.8
1215 | dev: true
1216 |
1217 | /fast-safe-stringify@2.1.1:
1218 | resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
1219 | dev: false
1220 |
1221 | /fastq@1.17.1:
1222 | resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
1223 | dependencies:
1224 | reusify: 1.0.4
1225 | dev: true
1226 |
1227 | /fill-range@7.1.1:
1228 | resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
1229 | engines: {node: '>=8'}
1230 | dependencies:
1231 | to-regex-range: 5.0.1
1232 | dev: true
1233 |
1234 | /flat@6.0.1:
1235 | resolution: {integrity: sha512-/3FfIa8mbrg3xE7+wAhWeV+bd7L2Mof+xtZb5dRDKZ+wDvYJK4WDYeIOuOhre5Yv5aQObZrlbRmk3RTSiuQBtw==}
1236 | engines: {node: '>=18'}
1237 | hasBin: true
1238 | dev: false
1239 |
1240 | /foreground-child@3.3.0:
1241 | resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
1242 | engines: {node: '>=14'}
1243 | dependencies:
1244 | cross-spawn: 7.0.6
1245 | signal-exit: 4.1.0
1246 | dev: true
1247 |
1248 | /fraction.js@4.3.7:
1249 | resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
1250 | dev: true
1251 |
1252 | /fsevents@2.3.3:
1253 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
1254 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
1255 | os: [darwin]
1256 | requiresBuild: true
1257 | dev: true
1258 | optional: true
1259 |
1260 | /function-bind@1.1.2:
1261 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
1262 | dev: true
1263 |
1264 | /gensync@1.0.0-beta.2:
1265 | resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
1266 | engines: {node: '>=6.9.0'}
1267 | dev: true
1268 |
1269 | /glob-parent@5.1.2:
1270 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
1271 | engines: {node: '>= 6'}
1272 | dependencies:
1273 | is-glob: 4.0.3
1274 | dev: true
1275 |
1276 | /glob-parent@6.0.2:
1277 | resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
1278 | engines: {node: '>=10.13.0'}
1279 | dependencies:
1280 | is-glob: 4.0.3
1281 | dev: true
1282 |
1283 | /glob@10.4.5:
1284 | resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
1285 | hasBin: true
1286 | dependencies:
1287 | foreground-child: 3.3.0
1288 | jackspeak: 3.4.3
1289 | minimatch: 9.0.5
1290 | minipass: 7.1.2
1291 | package-json-from-dist: 1.0.1
1292 | path-scurry: 1.11.1
1293 | dev: true
1294 |
1295 | /globals@11.12.0:
1296 | resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
1297 | engines: {node: '>=4'}
1298 | dev: true
1299 |
1300 | /globals@15.13.0:
1301 | resolution: {integrity: sha512-49TewVEz0UxZjr1WYYsWpPrhyC/B/pA8Bq0fUmet2n+eR7yn0IvNzNaoBwnK6mdkzcN+se7Ez9zUgULTz2QH4g==}
1302 | engines: {node: '>=18'}
1303 | dev: true
1304 |
1305 | /goober@2.1.16(csstype@3.1.3):
1306 | resolution: {integrity: sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==}
1307 | peerDependencies:
1308 | csstype: ^3.0.10
1309 | dependencies:
1310 | csstype: 3.1.3
1311 | dev: false
1312 |
1313 | /graphql-request@7.1.2(graphql@16.9.0):
1314 | resolution: {integrity: sha512-+XE3iuC55C2di5ZUrB4pjgwe+nIQBuXVIK9J98wrVwojzDW3GMdSBZfxUk8l4j9TieIpjpggclxhNEU9ebGF8w==}
1315 | peerDependencies:
1316 | graphql: 14 - 16
1317 | dependencies:
1318 | '@graphql-typed-document-node/core': 3.2.0(graphql@16.9.0)
1319 | graphql: 16.9.0
1320 | dev: false
1321 |
1322 | /graphql@16.9.0:
1323 | resolution: {integrity: sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==}
1324 | engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
1325 | dev: false
1326 |
1327 | /hasown@2.0.2:
1328 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
1329 | engines: {node: '>= 0.4'}
1330 | dependencies:
1331 | function-bind: 1.1.2
1332 | dev: true
1333 |
1334 | /is-binary-path@2.1.0:
1335 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
1336 | engines: {node: '>=8'}
1337 | dependencies:
1338 | binary-extensions: 2.3.0
1339 | dev: true
1340 |
1341 | /is-core-module@2.15.1:
1342 | resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==}
1343 | engines: {node: '>= 0.4'}
1344 | dependencies:
1345 | hasown: 2.0.2
1346 | dev: true
1347 |
1348 | /is-extglob@2.1.1:
1349 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
1350 | engines: {node: '>=0.10.0'}
1351 | dev: true
1352 |
1353 | /is-fullwidth-code-point@3.0.0:
1354 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
1355 | engines: {node: '>=8'}
1356 | dev: true
1357 |
1358 | /is-glob@4.0.3:
1359 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
1360 | engines: {node: '>=0.10.0'}
1361 | dependencies:
1362 | is-extglob: 2.1.1
1363 | dev: true
1364 |
1365 | /is-number@7.0.0:
1366 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
1367 | engines: {node: '>=0.12.0'}
1368 | dev: true
1369 |
1370 | /is-plain-object@2.0.4:
1371 | resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==}
1372 | engines: {node: '>=0.10.0'}
1373 | dependencies:
1374 | isobject: 3.0.1
1375 | dev: false
1376 |
1377 | /is-primitive@3.0.1:
1378 | resolution: {integrity: sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w==}
1379 | engines: {node: '>=0.10.0'}
1380 | dev: false
1381 |
1382 | /is-what@4.1.16:
1383 | resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==}
1384 | engines: {node: '>=12.13'}
1385 | dev: false
1386 |
1387 | /isexe@2.0.0:
1388 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
1389 | dev: true
1390 |
1391 | /isobject@3.0.1:
1392 | resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==}
1393 | engines: {node: '>=0.10.0'}
1394 | dev: false
1395 |
1396 | /isomorphic-ws@5.0.0(ws@8.18.0):
1397 | resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==}
1398 | peerDependencies:
1399 | ws: '*'
1400 | dependencies:
1401 | ws: 8.18.0
1402 | dev: false
1403 |
1404 | /jackspeak@3.4.3:
1405 | resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
1406 | dependencies:
1407 | '@isaacs/cliui': 8.0.2
1408 | optionalDependencies:
1409 | '@pkgjs/parseargs': 0.11.0
1410 | dev: true
1411 |
1412 | /jiti@1.21.6:
1413 | resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==}
1414 | hasBin: true
1415 | dev: true
1416 |
1417 | /js-tokens@4.0.0:
1418 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
1419 |
1420 | /jsesc@3.1.0:
1421 | resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
1422 | engines: {node: '>=6'}
1423 | hasBin: true
1424 | dev: true
1425 |
1426 | /json5@2.2.3:
1427 | resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
1428 | engines: {node: '>=6'}
1429 | hasBin: true
1430 | dev: true
1431 |
1432 | /lilconfig@3.1.3:
1433 | resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
1434 | engines: {node: '>=14'}
1435 | dev: true
1436 |
1437 | /lines-and-columns@1.2.4:
1438 | resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
1439 | dev: true
1440 |
1441 | /loose-envify@1.4.0:
1442 | resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
1443 | hasBin: true
1444 | dependencies:
1445 | js-tokens: 4.0.0
1446 | dev: false
1447 |
1448 | /lru-cache@10.4.3:
1449 | resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
1450 | dev: true
1451 |
1452 | /lru-cache@5.1.1:
1453 | resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
1454 | dependencies:
1455 | yallist: 3.1.1
1456 | dev: true
1457 |
1458 | /merge2@1.4.1:
1459 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
1460 | engines: {node: '>= 8'}
1461 | dev: true
1462 |
1463 | /micromatch@4.0.8:
1464 | resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
1465 | engines: {node: '>=8.6'}
1466 | dependencies:
1467 | braces: 3.0.3
1468 | picomatch: 2.3.1
1469 | dev: true
1470 |
1471 | /minimatch@9.0.5:
1472 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
1473 | engines: {node: '>=16 || 14 >=14.17'}
1474 | dependencies:
1475 | brace-expansion: 2.0.1
1476 | dev: true
1477 |
1478 | /minipass@7.1.2:
1479 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
1480 | engines: {node: '>=16 || 14 >=14.17'}
1481 | dev: true
1482 |
1483 | /ms@2.1.3:
1484 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
1485 | dev: true
1486 |
1487 | /mutative@1.1.0:
1488 | resolution: {integrity: sha512-2PJADREjOusk3iJkD3rXV2YjAxTuaLxdfqtqTEt6vcY07LtEBR1seHuBHXWEIuscqRDGvbauYPs+A4Rj/KTczQ==}
1489 | engines: {node: '>=14.0'}
1490 | dev: false
1491 |
1492 | /mz@2.7.0:
1493 | resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
1494 | dependencies:
1495 | any-promise: 1.3.0
1496 | object-assign: 4.1.1
1497 | thenify-all: 1.6.0
1498 | dev: true
1499 |
1500 | /nanoid@3.3.8:
1501 | resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
1502 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
1503 | hasBin: true
1504 | dev: true
1505 |
1506 | /node-releases@2.0.19:
1507 | resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
1508 | dev: true
1509 |
1510 | /normalize-path@3.0.0:
1511 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
1512 | engines: {node: '>=0.10.0'}
1513 | dev: true
1514 |
1515 | /normalize-range@0.1.2:
1516 | resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
1517 | engines: {node: '>=0.10.0'}
1518 | dev: true
1519 |
1520 | /object-assign@4.1.1:
1521 | resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
1522 | engines: {node: '>=0.10.0'}
1523 |
1524 | /object-hash@3.0.0:
1525 | resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
1526 | engines: {node: '>= 6'}
1527 | dev: true
1528 |
1529 | /package-json-from-dist@1.0.1:
1530 | resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
1531 | dev: true
1532 |
1533 | /partysocket@0.0.25:
1534 | resolution: {integrity: sha512-1oCGA65fydX/FgdnsiBh68buOvfxuteoZVSb3Paci2kRp/7lhF0HyA8EDb5X/O6FxId1e+usPTQNRuzFEvkJbQ==}
1535 | dependencies:
1536 | event-target-shim: 6.0.2
1537 | dev: false
1538 |
1539 | /path-key@3.1.1:
1540 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
1541 | engines: {node: '>=8'}
1542 | dev: true
1543 |
1544 | /path-parse@1.0.7:
1545 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
1546 | dev: true
1547 |
1548 | /path-scurry@1.11.1:
1549 | resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
1550 | engines: {node: '>=16 || 14 >=14.18'}
1551 | dependencies:
1552 | lru-cache: 10.4.3
1553 | minipass: 7.1.2
1554 | dev: true
1555 |
1556 | /picocolors@1.1.1:
1557 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
1558 | dev: true
1559 |
1560 | /picomatch@2.3.1:
1561 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
1562 | engines: {node: '>=8.6'}
1563 | dev: true
1564 |
1565 | /pify@2.3.0:
1566 | resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
1567 | engines: {node: '>=0.10.0'}
1568 | dev: true
1569 |
1570 | /pirates@4.0.6:
1571 | resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
1572 | engines: {node: '>= 6'}
1573 | dev: true
1574 |
1575 | /postcss-import@15.1.0(postcss@8.4.49):
1576 | resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
1577 | engines: {node: '>=14.0.0'}
1578 | peerDependencies:
1579 | postcss: ^8.0.0
1580 | dependencies:
1581 | postcss: 8.4.49
1582 | postcss-value-parser: 4.2.0
1583 | read-cache: 1.0.0
1584 | resolve: 1.22.8
1585 | dev: true
1586 |
1587 | /postcss-js@4.0.1(postcss@8.4.49):
1588 | resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
1589 | engines: {node: ^12 || ^14 || >= 16}
1590 | peerDependencies:
1591 | postcss: ^8.4.21
1592 | dependencies:
1593 | camelcase-css: 2.0.1
1594 | postcss: 8.4.49
1595 | dev: true
1596 |
1597 | /postcss-load-config@4.0.2(postcss@8.4.49):
1598 | resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
1599 | engines: {node: '>= 14'}
1600 | peerDependencies:
1601 | postcss: '>=8.0.9'
1602 | ts-node: '>=9.0.0'
1603 | peerDependenciesMeta:
1604 | postcss:
1605 | optional: true
1606 | ts-node:
1607 | optional: true
1608 | dependencies:
1609 | lilconfig: 3.1.3
1610 | postcss: 8.4.49
1611 | yaml: 2.6.1
1612 | dev: true
1613 |
1614 | /postcss-nested@6.2.0(postcss@8.4.49):
1615 | resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==}
1616 | engines: {node: '>=12.0'}
1617 | peerDependencies:
1618 | postcss: ^8.2.14
1619 | dependencies:
1620 | postcss: 8.4.49
1621 | postcss-selector-parser: 6.1.2
1622 | dev: true
1623 |
1624 | /postcss-selector-parser@6.1.2:
1625 | resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
1626 | engines: {node: '>=4'}
1627 | dependencies:
1628 | cssesc: 3.0.0
1629 | util-deprecate: 1.0.2
1630 | dev: true
1631 |
1632 | /postcss-value-parser@4.2.0:
1633 | resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
1634 | dev: true
1635 |
1636 | /postcss@8.4.49:
1637 | resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==}
1638 | engines: {node: ^10 || ^12 || >=14}
1639 | dependencies:
1640 | nanoid: 3.3.8
1641 | picocolors: 1.1.1
1642 | source-map-js: 1.2.1
1643 | dev: true
1644 |
1645 | /prop-types@15.8.1:
1646 | resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
1647 | dependencies:
1648 | loose-envify: 1.4.0
1649 | object-assign: 4.1.1
1650 | react-is: 16.13.1
1651 | dev: false
1652 |
1653 | /queue-microtask@1.2.3:
1654 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
1655 | dev: true
1656 |
1657 | /react-dom@18.3.1(react@18.3.1):
1658 | resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==}
1659 | peerDependencies:
1660 | react: ^18.3.1
1661 | dependencies:
1662 | loose-envify: 1.4.0
1663 | react: 18.3.1
1664 | scheduler: 0.23.2
1665 | dev: false
1666 |
1667 | /react-fast-compare@3.2.2:
1668 | resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==}
1669 | dev: false
1670 |
1671 | /react-hot-toast@2.4.1(csstype@3.1.3)(react-dom@18.3.1)(react@18.3.1):
1672 | resolution: {integrity: sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==}
1673 | engines: {node: '>=10'}
1674 | peerDependencies:
1675 | react: '>=16'
1676 | react-dom: '>=16'
1677 | dependencies:
1678 | goober: 2.1.16(csstype@3.1.3)
1679 | react: 18.3.1
1680 | react-dom: 18.3.1(react@18.3.1)
1681 | transitivePeerDependencies:
1682 | - csstype
1683 | dev: false
1684 |
1685 | /react-infinite-scroll-hook@5.0.1(react-dom@18.3.1)(react@18.3.1):
1686 | resolution: {integrity: sha512-fn6+8BAZLQ9C1fvO5kPicGjDR2WHxK7rP4aaSWuaJkvtoJjYuudGJ9wjgPox7dghKm5Xj9cpKFycM86/wAJ3ig==}
1687 | peerDependencies:
1688 | react: '>=16.8.0'
1689 | react-dom: '>=16.8.0'
1690 | dependencies:
1691 | react: 18.3.1
1692 | react-dom: 18.3.1(react@18.3.1)
1693 | react-intersection-observer-hook: 3.0.1(react-dom@18.3.1)(react@18.3.1)
1694 | dev: false
1695 |
1696 | /react-intersection-observer-hook@3.0.1(react-dom@18.3.1)(react@18.3.1):
1697 | resolution: {integrity: sha512-nehzFUV+jVY49tc9FOm4yJHrmFFtCmsBZKiN1rUUB2675CMBf6kKu1ekuTqvCnmh4XG7RFP3bMyAevfqfV94xw==}
1698 | peerDependencies:
1699 | react: '>=16.8.0'
1700 | react-dom: '>=16.8.0'
1701 | dependencies:
1702 | react: 18.3.1
1703 | react-dom: 18.3.1(react@18.3.1)
1704 | dev: false
1705 |
1706 | /react-is@16.13.1:
1707 | resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
1708 | dev: false
1709 |
1710 | /react-refresh@0.14.2:
1711 | resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
1712 | engines: {node: '>=0.10.0'}
1713 | dev: true
1714 |
1715 | /react-transition-group@4.4.5(react-dom@18.3.1)(react@18.3.1):
1716 | resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
1717 | peerDependencies:
1718 | react: '>=16.6.0'
1719 | react-dom: '>=16.6.0'
1720 | dependencies:
1721 | '@babel/runtime': 7.26.0
1722 | dom-helpers: 5.2.1
1723 | loose-envify: 1.4.0
1724 | prop-types: 15.8.1
1725 | react: 18.3.1
1726 | react-dom: 18.3.1(react@18.3.1)
1727 | dev: false
1728 |
1729 | /react@18.3.1:
1730 | resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
1731 | engines: {node: '>=0.10.0'}
1732 | dependencies:
1733 | loose-envify: 1.4.0
1734 | dev: false
1735 |
1736 | /read-cache@1.0.0:
1737 | resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
1738 | dependencies:
1739 | pify: 2.3.0
1740 | dev: true
1741 |
1742 | /readdirp@3.6.0:
1743 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
1744 | engines: {node: '>=8.10.0'}
1745 | dependencies:
1746 | picomatch: 2.3.1
1747 | dev: true
1748 |
1749 | /regenerator-runtime@0.14.1:
1750 | resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
1751 | dev: false
1752 |
1753 | /remeda@2.17.4:
1754 | resolution: {integrity: sha512-pviU2Ag7Qx9mOCAKO4voxDx/scfLzdhp3v85qDO4xxntQsU76uE9sgrAAdK1ATn4zzaOJqCXYMMNRP+O9F4Wiw==}
1755 | dependencies:
1756 | type-fest: 4.30.1
1757 | dev: false
1758 |
1759 | /resolve@1.22.8:
1760 | resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
1761 | hasBin: true
1762 | dependencies:
1763 | is-core-module: 2.15.1
1764 | path-parse: 1.0.7
1765 | supports-preserve-symlinks-flag: 1.0.0
1766 | dev: true
1767 |
1768 | /reusify@1.0.4:
1769 | resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
1770 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
1771 | dev: true
1772 |
1773 | /rollup@4.28.1:
1774 | resolution: {integrity: sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg==}
1775 | engines: {node: '>=18.0.0', npm: '>=8.0.0'}
1776 | hasBin: true
1777 | dependencies:
1778 | '@types/estree': 1.0.6
1779 | optionalDependencies:
1780 | '@rollup/rollup-android-arm-eabi': 4.28.1
1781 | '@rollup/rollup-android-arm64': 4.28.1
1782 | '@rollup/rollup-darwin-arm64': 4.28.1
1783 | '@rollup/rollup-darwin-x64': 4.28.1
1784 | '@rollup/rollup-freebsd-arm64': 4.28.1
1785 | '@rollup/rollup-freebsd-x64': 4.28.1
1786 | '@rollup/rollup-linux-arm-gnueabihf': 4.28.1
1787 | '@rollup/rollup-linux-arm-musleabihf': 4.28.1
1788 | '@rollup/rollup-linux-arm64-gnu': 4.28.1
1789 | '@rollup/rollup-linux-arm64-musl': 4.28.1
1790 | '@rollup/rollup-linux-loongarch64-gnu': 4.28.1
1791 | '@rollup/rollup-linux-powerpc64le-gnu': 4.28.1
1792 | '@rollup/rollup-linux-riscv64-gnu': 4.28.1
1793 | '@rollup/rollup-linux-s390x-gnu': 4.28.1
1794 | '@rollup/rollup-linux-x64-gnu': 4.28.1
1795 | '@rollup/rollup-linux-x64-musl': 4.28.1
1796 | '@rollup/rollup-win32-arm64-msvc': 4.28.1
1797 | '@rollup/rollup-win32-ia32-msvc': 4.28.1
1798 | '@rollup/rollup-win32-x64-msvc': 4.28.1
1799 | fsevents: 2.3.3
1800 | dev: true
1801 |
1802 | /run-parallel@1.2.0:
1803 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
1804 | dependencies:
1805 | queue-microtask: 1.2.3
1806 | dev: true
1807 |
1808 | /safe-stable-stringify@2.5.0:
1809 | resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
1810 | engines: {node: '>=10'}
1811 | dev: false
1812 |
1813 | /scheduler@0.23.2:
1814 | resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
1815 | dependencies:
1816 | loose-envify: 1.4.0
1817 | dev: false
1818 |
1819 | /semver@6.3.1:
1820 | resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
1821 | hasBin: true
1822 | dev: true
1823 |
1824 | /set-value@4.1.0:
1825 | resolution: {integrity: sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw==}
1826 | engines: {node: '>=11.0'}
1827 | dependencies:
1828 | is-plain-object: 2.0.4
1829 | is-primitive: 3.0.1
1830 | dev: false
1831 |
1832 | /shebang-command@2.0.0:
1833 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
1834 | engines: {node: '>=8'}
1835 | dependencies:
1836 | shebang-regex: 3.0.0
1837 | dev: true
1838 |
1839 | /shebang-regex@3.0.0:
1840 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
1841 | engines: {node: '>=8'}
1842 | dev: true
1843 |
1844 | /signal-exit@4.1.0:
1845 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
1846 | engines: {node: '>=14'}
1847 | dev: true
1848 |
1849 | /source-map-js@1.2.1:
1850 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
1851 | engines: {node: '>=0.10.0'}
1852 | dev: true
1853 |
1854 | /string-width@4.2.3:
1855 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
1856 | engines: {node: '>=8'}
1857 | dependencies:
1858 | emoji-regex: 8.0.0
1859 | is-fullwidth-code-point: 3.0.0
1860 | strip-ansi: 6.0.1
1861 | dev: true
1862 |
1863 | /string-width@5.1.2:
1864 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
1865 | engines: {node: '>=12'}
1866 | dependencies:
1867 | eastasianwidth: 0.2.0
1868 | emoji-regex: 9.2.2
1869 | strip-ansi: 7.1.0
1870 | dev: true
1871 |
1872 | /strip-ansi@6.0.1:
1873 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
1874 | engines: {node: '>=8'}
1875 | dependencies:
1876 | ansi-regex: 5.0.1
1877 | dev: true
1878 |
1879 | /strip-ansi@7.1.0:
1880 | resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
1881 | engines: {node: '>=12'}
1882 | dependencies:
1883 | ansi-regex: 6.1.0
1884 | dev: true
1885 |
1886 | /sucrase@3.35.0:
1887 | resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
1888 | engines: {node: '>=16 || 14 >=14.17'}
1889 | hasBin: true
1890 | dependencies:
1891 | '@jridgewell/gen-mapping': 0.3.8
1892 | commander: 4.1.1
1893 | glob: 10.4.5
1894 | lines-and-columns: 1.2.4
1895 | mz: 2.7.0
1896 | pirates: 4.0.6
1897 | ts-interface-checker: 0.1.13
1898 | dev: true
1899 |
1900 | /superjson@1.13.3:
1901 | resolution: {integrity: sha512-mJiVjfd2vokfDxsQPOwJ/PtanO87LhpYY88ubI5dUB1Ab58Txbyje3+jpm+/83R/fevaq/107NNhtYBLuoTrFg==}
1902 | engines: {node: '>=10'}
1903 | dependencies:
1904 | copy-anything: 3.0.5
1905 | dev: false
1906 |
1907 | /supports-preserve-symlinks-flag@1.0.0:
1908 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
1909 | engines: {node: '>= 0.4'}
1910 | dev: true
1911 |
1912 | /tailwindcss@3.4.16:
1913 | resolution: {integrity: sha512-TI4Cyx7gDiZ6r44ewaJmt0o6BrMCT5aK5e0rmJ/G9Xq3w7CX/5VXl/zIPEJZFUK5VEqwByyhqNPycPlvcK4ZNw==}
1914 | engines: {node: '>=14.0.0'}
1915 | hasBin: true
1916 | dependencies:
1917 | '@alloc/quick-lru': 5.2.0
1918 | arg: 5.0.2
1919 | chokidar: 3.6.0
1920 | didyoumean: 1.2.2
1921 | dlv: 1.1.3
1922 | fast-glob: 3.3.2
1923 | glob-parent: 6.0.2
1924 | is-glob: 4.0.3
1925 | jiti: 1.21.6
1926 | lilconfig: 3.1.3
1927 | micromatch: 4.0.8
1928 | normalize-path: 3.0.0
1929 | object-hash: 3.0.0
1930 | picocolors: 1.1.1
1931 | postcss: 8.4.49
1932 | postcss-import: 15.1.0(postcss@8.4.49)
1933 | postcss-js: 4.0.1(postcss@8.4.49)
1934 | postcss-load-config: 4.0.2(postcss@8.4.49)
1935 | postcss-nested: 6.2.0(postcss@8.4.49)
1936 | postcss-selector-parser: 6.1.2
1937 | resolve: 1.22.8
1938 | sucrase: 3.35.0
1939 | transitivePeerDependencies:
1940 | - ts-node
1941 | dev: true
1942 |
1943 | /thenify-all@1.6.0:
1944 | resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
1945 | engines: {node: '>=0.8'}
1946 | dependencies:
1947 | thenify: 3.3.1
1948 | dev: true
1949 |
1950 | /thenify@3.3.1:
1951 | resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
1952 | dependencies:
1953 | any-promise: 1.3.0
1954 | dev: true
1955 |
1956 | /to-regex-range@5.0.1:
1957 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
1958 | engines: {node: '>=8.0'}
1959 | dependencies:
1960 | is-number: 7.0.0
1961 | dev: true
1962 |
1963 | /ts-interface-checker@0.1.13:
1964 | resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
1965 | dev: true
1966 |
1967 | /type-fest@4.30.1:
1968 | resolution: {integrity: sha512-ojFL7eDMX2NF0xMbDwPZJ8sb7ckqtlAi1GsmgsFXvErT9kFTk1r0DuQKvrCh73M6D4nngeHJmvogF9OluXs7Hw==}
1969 | engines: {node: '>=16'}
1970 | dev: false
1971 |
1972 | /typescript@5.6.3:
1973 | resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==}
1974 | engines: {node: '>=14.17'}
1975 | hasBin: true
1976 | dev: true
1977 |
1978 | /update-browserslist-db@1.1.1(browserslist@4.24.2):
1979 | resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==}
1980 | hasBin: true
1981 | peerDependencies:
1982 | browserslist: '>= 4.21.0'
1983 | dependencies:
1984 | browserslist: 4.24.2
1985 | escalade: 3.2.0
1986 | picocolors: 1.1.1
1987 | dev: true
1988 |
1989 | /use-isomorphic-layout-effect@1.2.0(@types/react@18.3.16)(react@18.3.1):
1990 | resolution: {integrity: sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==}
1991 | peerDependencies:
1992 | '@types/react': '*'
1993 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
1994 | peerDependenciesMeta:
1995 | '@types/react':
1996 | optional: true
1997 | dependencies:
1998 | '@types/react': 18.3.16
1999 | react: 18.3.1
2000 | dev: false
2001 |
2002 | /use-sync-external-store@1.4.0(react@18.3.1):
2003 | resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==}
2004 | peerDependencies:
2005 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
2006 | dependencies:
2007 | react: 18.3.1
2008 | dev: false
2009 |
2010 | /util-deprecate@1.0.2:
2011 | resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
2012 | dev: true
2013 |
2014 | /uuid@9.0.1:
2015 | resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
2016 | hasBin: true
2017 | dev: false
2018 |
2019 | /vite@6.0.3:
2020 | resolution: {integrity: sha512-Cmuo5P0ENTN6HxLSo6IHsjCLn/81Vgrp81oaiFFMRa8gGDj5xEjIcEpf2ZymZtZR8oU0P2JX5WuUp/rlXcHkAw==}
2021 | engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
2022 | hasBin: true
2023 | peerDependencies:
2024 | '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
2025 | jiti: '>=1.21.0'
2026 | less: '*'
2027 | lightningcss: ^1.21.0
2028 | sass: '*'
2029 | sass-embedded: '*'
2030 | stylus: '*'
2031 | sugarss: '*'
2032 | terser: ^5.16.0
2033 | tsx: ^4.8.1
2034 | yaml: ^2.4.2
2035 | peerDependenciesMeta:
2036 | '@types/node':
2037 | optional: true
2038 | jiti:
2039 | optional: true
2040 | less:
2041 | optional: true
2042 | lightningcss:
2043 | optional: true
2044 | sass:
2045 | optional: true
2046 | sass-embedded:
2047 | optional: true
2048 | stylus:
2049 | optional: true
2050 | sugarss:
2051 | optional: true
2052 | terser:
2053 | optional: true
2054 | tsx:
2055 | optional: true
2056 | yaml:
2057 | optional: true
2058 | dependencies:
2059 | esbuild: 0.24.0
2060 | postcss: 8.4.49
2061 | rollup: 4.28.1
2062 | optionalDependencies:
2063 | fsevents: 2.3.3
2064 | dev: true
2065 |
2066 | /which@2.0.2:
2067 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
2068 | engines: {node: '>= 8'}
2069 | hasBin: true
2070 | dependencies:
2071 | isexe: 2.0.0
2072 | dev: true
2073 |
2074 | /wrap-ansi@7.0.0:
2075 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
2076 | engines: {node: '>=10'}
2077 | dependencies:
2078 | ansi-styles: 4.3.0
2079 | string-width: 4.2.3
2080 | strip-ansi: 6.0.1
2081 | dev: true
2082 |
2083 | /wrap-ansi@8.1.0:
2084 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
2085 | engines: {node: '>=12'}
2086 | dependencies:
2087 | ansi-styles: 6.2.1
2088 | string-width: 5.1.2
2089 | strip-ansi: 7.1.0
2090 | dev: true
2091 |
2092 | /ws@8.18.0:
2093 | resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
2094 | engines: {node: '>=10.0.0'}
2095 | peerDependencies:
2096 | bufferutil: ^4.0.1
2097 | utf-8-validate: '>=5.0.2'
2098 | peerDependenciesMeta:
2099 | bufferutil:
2100 | optional: true
2101 | utf-8-validate:
2102 | optional: true
2103 | dev: false
2104 |
2105 | /xstate@5.18.2:
2106 | resolution: {integrity: sha512-hab5VOe29D0agy8/7dH1lGw+7kilRQyXwpaChoMu4fe6rDP+nsHYhDYKfS2O4iXE7myA98TW6qMEudj/8NXEkA==}
2107 | dev: false
2108 |
2109 | /yallist@3.1.1:
2110 | resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
2111 | dev: true
2112 |
2113 | /yaml@2.6.1:
2114 | resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==}
2115 | engines: {node: '>= 14'}
2116 | hasBin: true
2117 | dev: true
2118 |
2119 | /zod@3.23.8:
2120 | resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==}
2121 | dev: false
2122 |
--------------------------------------------------------------------------------
/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | }
6 | }
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Frame } from "@shopify/polaris"
2 |
3 | import { Notifications } from "./components/Notifications.tsx"
4 | import { AppExampleSandbox } from "./machines/inspector/inspector.machine.tsx"
5 | import { Root } from "./machines/root/root.view.tsx"
6 | import { PolarisProvider } from "./providers/polaris.provider"
7 |
8 | function App() {
9 | return (
10 |
11 |
12 |
13 |
14 |
15 |
16 | )
17 | }
18 |
19 | export default App
20 |
--------------------------------------------------------------------------------
/src/assets/usage-stately-actors.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djang0dev/special-shopify-resource-picker/9939766d06f071eb9bac20c11920be12b4b3a6f9/src/assets/usage-stately-actors.mp4
--------------------------------------------------------------------------------
/src/commons/entities.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod"
2 |
3 | export const PageInfo = z.object({
4 | hasNextPage: z.boolean(),
5 | hasPreviousPage: z.boolean(),
6 | startCursor: z.string().optional().nullable(),
7 | endCursor: z.string().optional().nullable(),
8 | })
9 |
10 | export type PageInfo = z.infer
11 |
12 | export interface Product {
13 | id: string
14 | title: string
15 | description: string
16 | featuredImage: {
17 | id: string
18 | url: string
19 | }
20 | variants: {
21 | edges: {
22 | node: {
23 | price: {
24 | amount: string
25 | currencyCode: string
26 | }
27 | }
28 | }[]
29 | }
30 | }
31 |
32 | export interface ProductsResponse {
33 | products: {
34 | pageInfo: PageInfo
35 | edges: {
36 | node: Product
37 | }[]
38 | }
39 | }
40 |
41 | interface CollectionImage {
42 | url: string
43 | }
44 |
45 | interface CollectionNode {
46 | id: string
47 | title: string
48 | description: string
49 | image: CollectionImage
50 | }
51 |
52 | interface CollectionEdge {
53 | node: CollectionNode
54 | }
55 |
56 | export interface CollectionQueryResponse {
57 | collections: {
58 | pageInfo: PageInfo
59 | edges: CollectionEdge[]
60 | }
61 | }
62 |
63 | export interface CollectionQueryVariables {
64 | first: number
65 | after?: string
66 | query?: string
67 | }
68 |
69 | export const zDate = z.union([z.string(), z.date()]).pipe(z.coerce.date())
70 | // Basic app local handled for now
71 | export const AppLocales = z.object({
72 | "en-US": z.string(),
73 | })
74 | export type AppLocales = z.infer
75 |
76 | // Related to resource picker
77 | export const LibraryQuery = z.object({
78 | query: z.string().nullable(),
79 | filters: z.object({
80 | tags: z.array(z.string()).nullable().default(null),
81 | }),
82 | // query: z.string(),
83 | endCursor: z
84 | .string()
85 | .nullable()
86 | .optional()
87 | .default(null)
88 | .transform((v) => v ?? null),
89 | first: z
90 | .string()
91 | .or(z.number())
92 | .optional()
93 | .default(10)
94 | .transform((v) => Number.parseInt(String(v))),
95 | })
96 | export type LibraryQuery = z.infer
97 |
98 | export const BaseResource = z.object({
99 | id: z.string(),
100 | mainImageSrc: z.string().nullable(),
101 | })
102 |
103 | export const LibraryStaticImage = z.object({
104 | resourceType: z.literal("libraryStaticImage"),
105 | data: z.object({
106 | src: z.string(),
107 | }),
108 | })
109 |
110 | export const LibraryResourceFilters = z.object({
111 | filters: z.object({
112 | tags: z.array(z.string()),
113 | }),
114 | })
115 |
116 | export type LibraryResourceFilters = z.infer
117 |
118 | export const LibraryResource = BaseResource.extend({
119 | title: AppLocales,
120 | resourceNamespace: z.literal("library"),
121 | description: AppLocales.nullable(),
122 | tags: z.array(z.string()).nullable(),
123 | allowedPlanSlugs: z.array(z.union([z.string(), z.literal("all")])),
124 | status: z.enum(["active", "draft"]),
125 | resource: z.discriminatedUnion("resourceType", [LibraryStaticImage]),
126 | createdAt: zDate,
127 | updatedAt: zDate,
128 | cursor: z.number(),
129 | })
130 | export type LibraryResourceTypeUnion = LibraryResource["resource"]["resourceType"]
131 |
132 | // export const LibraryResourceType = z.enum([
133 | // LibrarySizeChart.shape.resourceType.value,
134 | // LibraryStaticImage.shape.resourceType.value,
135 | // ])
136 |
137 | export const ShopifyResource = BaseResource.extend({
138 | resourceNamespace: z.literal("shopify"),
139 | // FIXME: redundancy with title title here and inside resource, this is shit
140 | title: z.string(),
141 | resource: z.discriminatedUnion("resourceType", [
142 | z.object({ resourceType: z.literal("product"), title: z.string() }),
143 | z.object({ resourceType: z.literal("collection"), title: z.string() }),
144 | ]),
145 | })
146 |
147 | export type ShopifyResource = z.infer
148 |
149 | export type ShopifyResourceTypeUnion = ShopifyResource["resource"]["resourceType"]
150 |
151 | export type LibraryResource = z.infer
152 |
153 | // Extract doesn't work like this Extract =
155 | LibraryResource extends { resource: infer R }
156 | ? R extends { resourceType: TResourceType }
157 | ? Omit & { resource: R }
158 | : never
159 | : never
160 |
161 | export const ShopifyQuery = z.object({
162 | query: z.string(),
163 | endCursor: z
164 | .string()
165 | .nullable()
166 | .optional()
167 | .default(null)
168 | .transform((v) => v ?? null),
169 | first: z
170 | .string()
171 | .or(z.number())
172 | .optional()
173 | .default(10)
174 | .transform((v) => Number.parseInt(String(v))),
175 | })
176 |
177 | export type ShopifyQuery = z.infer
178 | export type LibraryStaticImage = GetLibraryResource<"libraryStaticImage">
179 |
180 | export const Resource = z.discriminatedUnion("resourceNamespace", [
181 | LibraryResource,
182 | ShopifyResource,
183 | ])
184 | export type Resource = z.infer
185 |
186 | export type ResourceNamespace = Resource["resourceNamespace"]
187 | export type ResourceType = Resource["resource"]["resourceType"]
188 |
--------------------------------------------------------------------------------
/src/commons/http-errors.ts:
--------------------------------------------------------------------------------
1 | import toast from "react-hot-toast/headless"
2 | import type { ErrorActorEvent } from "xstate"
3 |
4 | export type HttpStatus =
5 | | 100
6 | | 101
7 | | 102
8 | | 103
9 | | 200
10 | | 201
11 | | 202
12 | | 203
13 | | 204
14 | | 205
15 | | 206
16 | | 207
17 | | 208
18 | | 226
19 | | 300
20 | | 301
21 | | 302
22 | | 303
23 | | 304
24 | | 305
25 | | 306
26 | | 307
27 | | 308
28 | | 400
29 | | 401
30 | | 402
31 | | 403
32 | | 404
33 | | 405
34 | | 406
35 | | 407
36 | | 408
37 | | 409
38 | | 410
39 | | 411
40 | | 412
41 | | 413
42 | | 414
43 | | 415
44 | | 416
45 | | 417
46 | | 418
47 | | 419
48 | | 421
49 | | 422
50 | | 423
51 | | 424
52 | | 425
53 | | 426
54 | | 428
55 | | 429
56 | | 431
57 | | 451
58 | | 500
59 | | 501
60 | | 502
61 | | 503
62 | | 504
63 | | 505
64 | | 506
65 | | 507
66 | | 508
67 | | 510
68 | | 511
69 |
70 | export interface AppHttpExceptionPayload {
71 | type?: TExceptionTypes
72 | code?: TExceptionCodes
73 | status?: number
74 | isOperational?: boolean
75 | message: string
76 | errorDetails?: { key: string; value: string }[]
77 | additionalInfo?: Record
78 | rawError: Error
79 | stackTrace?: string
80 | }
81 | export type AppHttpExceptionClientPayload = Omit<
82 | AppHttpExceptionPayload,
83 | "rawError" | "stack"
84 | >
85 |
86 | export interface SerializedHttpErrorClient {
87 | http: {
88 | method: string
89 | status: HttpStatus
90 | requestId: string
91 | path: string
92 | }
93 | error: AppHttpExceptionClientPayload
94 | metas: {
95 | dateISOString: string
96 | timestamp: number
97 | }
98 | }
99 |
100 | const isASerializedHttpErrorClient = (error: unknown): error is SerializedHttpErrorClient =>
101 | typeof error === "object" &&
102 | error !== null &&
103 | "http" in error &&
104 | "error" in error &&
105 | "metas" in error
106 | export const handleInvokeError = ({ event }: { event: ErrorActorEvent }): void => {
107 | console.error(event.error)
108 | const error = event.error ?? {}
109 | if (isASerializedHttpErrorClient(error)) {
110 | // @ts-expect-error TODO: Fix because not sure about error handling with client side error (not from the api)
111 | toast.error(error, { style: { maxHeight: "150px" } })
112 | return
113 | }
114 | toast.error(Reflect.get(error, "message") ?? "Something went wrong, please contact support.")
115 | }
116 |
--------------------------------------------------------------------------------
/src/components/Notifications.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-unnecessary-condition */
2 |
3 | import { Toast } from "@shopify/polaris"
4 | import toast, { useToaster } from "react-hot-toast/headless"
5 | import { handleErrorMessage } from "../utils"
6 |
7 | export const Notifications = () => {
8 | const { toasts, handlers } = useToaster()
9 | const { startPause, endPause } = handlers
10 | return (
11 |
12 | {toasts
13 | .filter((t) => t.visible)
14 | .map((t) => {
15 | if (!t) return null
16 | if (t.type === "error") {
17 | const errorMessage = handleErrorMessage(t.message)
18 | return (
19 |
20 |
26 |
27 | {errorMessage}
28 |
29 |
30 | }
31 | onDismiss={() => {
32 | toast.dismiss(t.id)
33 | }}
34 | />
35 |
36 | )
37 | }
38 | return (
39 |
40 | {
44 | toast.dismiss(t.id)
45 | }}
46 | />
47 |
48 | )
49 | })}
50 |
51 | )
52 | }
53 |
--------------------------------------------------------------------------------
/src/data/library-static-images-resources.data.ts:
--------------------------------------------------------------------------------
1 | import { LibraryStaticImage } from "../commons/entities.ts"
2 | import { getPaginatedLibraryResources } from "../utils.ts"
3 |
4 | export const libraryStaticImageResources = [
5 | {
6 | id: "ff21133c-c65c-4261-89c7-5cc11a068c10",
7 | cursor: 270,
8 | tags: ["bags", "no-arrows"],
9 | allowedPlanSlugs: ["all"],
10 | title: { "en-US": "Shopping Bag" },
11 | description: null,
12 | mainImageSrc:
13 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset137-na_bAHLww.svg?v=1717077352",
14 | status: "active",
15 | createdAt: new Date("2024-06-08 11:21:30.928078 +00:00"),
16 | updatedAt: new Date("2024-06-08 11:21:30.928078 +00:00"),
17 | resource: {
18 | resourceType: "libraryStaticImage",
19 | data: {
20 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset137-na_bAHLww.svg?v=1717077352",
21 | },
22 | },
23 | resourceNamespace: "library",
24 | },
25 | {
26 | id: "b5634ac2-772a-4373-841e-8d67537993cd",
27 | cursor: 271,
28 | tags: ["bags", "arrows"],
29 | allowedPlanSlugs: ["all"],
30 | title: { "en-US": "Shopping Bag" },
31 | description: null,
32 | mainImageSrc:
33 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset137_bAHLww.svg?v=1717077352",
34 | status: "active",
35 | createdAt: new Date("2024-06-08 11:21:30.928078 +00:00"),
36 | updatedAt: new Date("2024-06-08 11:21:30.928078 +00:00"),
37 | resource: {
38 | resourceType: "libraryStaticImage",
39 | data: {
40 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset137_bAHLww.svg?v=1717077352",
41 | },
42 | },
43 | resourceNamespace: "library",
44 | },
45 | {
46 | id: "db745256-9dfc-4212-b85a-82574c6e4915",
47 | cursor: 272,
48 | tags: ["bags", "no-arrows"],
49 | allowedPlanSlugs: ["all"],
50 | title: { "en-US": "Messenger Bag" },
51 | description: null,
52 | mainImageSrc:
53 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset138-na_VuTFud.svg?v=1717077352",
54 | status: "active",
55 | createdAt: new Date("2024-06-08 11:23:11.742576 +00:00"),
56 | updatedAt: new Date("2024-06-08 11:23:11.742576 +00:00"),
57 | resource: {
58 | resourceType: "libraryStaticImage",
59 | data: {
60 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset138-na_VuTFud.svg?v=1717077352",
61 | },
62 | },
63 | resourceNamespace: "library",
64 | },
65 | {
66 | id: "ad7cd813-52ea-4850-9725-af96dae2707d",
67 | cursor: 273,
68 | tags: ["bags", "arrows"],
69 | allowedPlanSlugs: ["all"],
70 | title: { "en-US": "Messenger Bag" },
71 | description: null,
72 | mainImageSrc:
73 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset138_VuTFud.svg?v=1717077352",
74 | status: "active",
75 | createdAt: new Date("2024-06-08 11:23:11.742576 +00:00"),
76 | updatedAt: new Date("2024-06-08 11:23:11.742576 +00:00"),
77 | resource: {
78 | resourceType: "libraryStaticImage",
79 | data: {
80 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset138_VuTFud.svg?v=1717077352",
81 | },
82 | },
83 | resourceNamespace: "library",
84 | },
85 | {
86 | id: "84ef61fd-5d32-4b41-a641-0e15fec736e8",
87 | cursor: 274,
88 | tags: ["bags", "no-arrows"],
89 | allowedPlanSlugs: ["all"],
90 | title: { "en-US": "Crescent Bag" },
91 | description: null,
92 | mainImageSrc:
93 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset139-na_GUFoVs.svg?v=1717077352",
94 | status: "active",
95 | createdAt: new Date("2024-06-08 11:23:45.936915 +00:00"),
96 | updatedAt: new Date("2024-06-08 11:23:45.936915 +00:00"),
97 | resourceNamespace: "library",
98 | resource: {
99 | resourceType: "libraryStaticImage",
100 | data: {
101 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset139-na_GUFoVs.svg?v=1717077352",
102 | },
103 | },
104 | },
105 | {
106 | id: "f0877977-8ae3-424a-a750-518317d86337",
107 | cursor: 275,
108 | tags: ["bags", "arrows"],
109 | allowedPlanSlugs: ["all"],
110 | title: { "en-US": "Crescent Bag" },
111 | description: null,
112 | mainImageSrc:
113 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset139_GUFoVs.svg?v=1717077352",
114 | status: "active",
115 | createdAt: new Date("2024-06-08 11:23:45.936915 +00:00"),
116 | updatedAt: new Date("2024-06-08 11:23:45.936915 +00:00"),
117 | resourceNamespace: "library",
118 | resource: {
119 | resourceType: "libraryStaticImage",
120 | data: {
121 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset139_GUFoVs.svg?v=1717077352",
122 | },
123 | },
124 | },
125 | {
126 | id: "b089f006-21c3-4363-ad6b-89af726b3c17",
127 | cursor: 276,
128 | tags: ["bags", "no-arrows"],
129 | allowedPlanSlugs: ["all"],
130 | title: { "en-US": "Tote Bag" },
131 | description: null,
132 | mainImageSrc:
133 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset140-na_qMvFUh.svg?v=1717077352",
134 | status: "active",
135 | createdAt: new Date("2024-06-08 11:25:48.677097 +00:00"),
136 | updatedAt: new Date("2024-06-08 11:25:48.677097 +00:00"),
137 | resourceNamespace: "library",
138 | resource: {
139 | resourceType: "libraryStaticImage",
140 | data: {
141 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset140-na_qMvFUh.svg?v=1717077352",
142 | },
143 | },
144 | },
145 | {
146 | id: "28de1041-4233-409b-9416-1d4f7d87c99b",
147 | cursor: 278,
148 | tags: ["bags", "no-arrows"],
149 | allowedPlanSlugs: ["all"],
150 | title: { "en-US": "Satchel Bag" },
151 | description: null,
152 | mainImageSrc:
153 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset141-na_fnidiJ.svg?v=1717077352",
154 | status: "active",
155 | createdAt: new Date("2024-06-08 11:26:32.911841 +00:00"),
156 | updatedAt: new Date("2024-06-08 11:26:32.911841 +00:00"),
157 | resourceNamespace: "library",
158 | resource: {
159 | resourceType: "libraryStaticImage",
160 | data: {
161 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset141-na_fnidiJ.svg?v=1717077352",
162 | },
163 | },
164 | },
165 | {
166 | id: "9591b0a9-4e60-4ab0-9c5e-7ca118ccc553",
167 | cursor: 279,
168 | tags: ["bags", "arrows"],
169 | allowedPlanSlugs: ["all"],
170 | title: { "en-US": "Satchel Bag" },
171 | description: null,
172 | mainImageSrc:
173 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset141_fnidiJ.svg?v=1717077352",
174 | status: "active",
175 | createdAt: new Date("2024-06-08 11:26:32.911841 +00:00"),
176 | updatedAt: new Date("2024-06-08 11:26:32.911841 +00:00"),
177 |
178 | resourceNamespace: "library",
179 | resource: {
180 | resourceType: "libraryStaticImage",
181 | data: {
182 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset141_fnidiJ.svg?v=1717077352",
183 | },
184 | },
185 | },
186 | {
187 | id: "0cff4d2a-bbbf-4430-9cf5-6acb97395262",
188 | cursor: 280,
189 | tags: ["bags", "no-arrows"],
190 | allowedPlanSlugs: ["all"],
191 | title: { "en-US": "Bucket Bag" },
192 | description: null,
193 | mainImageSrc:
194 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset142-na_czLUAk.svg?v=1717077352",
195 | status: "active",
196 | createdAt: new Date("2024-06-08 11:26:58.580202 +00:00"),
197 | updatedAt: new Date("2024-06-08 11:26:58.580202 +00:00"),
198 | resourceNamespace: "library",
199 | resource: {
200 | resourceType: "libraryStaticImage",
201 | data: {
202 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset142-na_czLUAk.svg?v=1717077352",
203 | },
204 | },
205 | },
206 | {
207 | id: "9d885eba-3199-4529-bf77-d1dd87b5fb83",
208 | cursor: 281,
209 | tags: ["bags", "arrows"],
210 | allowedPlanSlugs: ["all"],
211 | title: { "en-US": "Bucket Bag" },
212 | description: null,
213 | mainImageSrc:
214 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset142_czLUAk.svg?v=1717077352",
215 | status: "active",
216 | createdAt: new Date("2024-06-08 11:26:58.580202 +00:00"),
217 | updatedAt: new Date("2024-06-08 11:26:58.580202 +00:00"),
218 | resourceNamespace: "library",
219 | resource: {
220 | resourceType: "libraryStaticImage",
221 | data: {
222 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset142_czLUAk.svg?v=1717077352",
223 | },
224 | },
225 | },
226 | {
227 | id: "40d58f96-53c7-4039-9584-e8d9b0fe18b8",
228 | cursor: 282,
229 | tags: ["bags", "no-arrows"],
230 | allowedPlanSlugs: ["all"],
231 | title: { "en-US": "Foldover Clutch" },
232 | description: null,
233 | mainImageSrc:
234 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset143-na_xmWFgi.svg?v=1717077352",
235 | status: "active",
236 | createdAt: new Date("2024-06-08 11:27:30.257002 +00:00"),
237 | updatedAt: new Date("2024-06-08 11:27:30.257002 +00:00"),
238 | resourceNamespace: "library",
239 | resource: {
240 | resourceType: "libraryStaticImage",
241 | data: {
242 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset143-na_xmWFgi.svg?v=1717077352",
243 | },
244 | },
245 | },
246 | {
247 | id: "57fb20f5-1637-4b96-9e31-0465158b21cb",
248 | cursor: 283,
249 | tags: ["bags", "arrows"],
250 | allowedPlanSlugs: ["all"],
251 | title: { "en-US": "Foldover Clutch" },
252 | description: null,
253 | mainImageSrc:
254 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset143_xmWFgi.svg?v=1717077352",
255 | status: "active",
256 | createdAt: new Date("2024-06-08 11:27:30.257002 +00:00"),
257 | updatedAt: new Date("2024-06-08 11:27:30.257002 +00:00"),
258 | resourceNamespace: "library",
259 | resource: {
260 | resourceType: "libraryStaticImage",
261 | data: {
262 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset143_xmWFgi.svg?v=1717077352",
263 | },
264 | },
265 | },
266 | {
267 | id: "f61435af-3443-46a9-9347-3a72bceef608",
268 | cursor: 284,
269 | tags: ["tops", "no-arrows"],
270 | allowedPlanSlugs: ["all"],
271 | title: { "en-US": "Baseball Jersey" },
272 | description: null,
273 | mainImageSrc:
274 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset145-na_NtKRJi.svg?v=1717077352",
275 | status: "active",
276 | createdAt: new Date("2024-06-08 11:28:07.073194 +00:00"),
277 | updatedAt: new Date("2024-06-08 11:28:07.073194 +00:00"),
278 | resourceNamespace: "library",
279 | resource: {
280 | resourceType: "libraryStaticImage",
281 | data: {
282 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset145-na_NtKRJi.svg?v=1717077352",
283 | },
284 | },
285 | },
286 | {
287 | id: "f2aff9ac-cf31-427d-b4a2-75a015f64459",
288 | cursor: 285,
289 | tags: ["tops", "arrows"],
290 | allowedPlanSlugs: ["all"],
291 | title: { "en-US": "Baseball Jersey" },
292 | description: null,
293 | mainImageSrc:
294 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset145_NtKRJi.svg?v=1717077352",
295 | status: "active",
296 | createdAt: new Date("2024-06-08 11:28:07.073194 +00:00"),
297 | updatedAt: new Date("2024-06-08 11:28:07.073194 +00:00"),
298 | resourceNamespace: "library",
299 | resource: {
300 | resourceType: "libraryStaticImage",
301 | data: {
302 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset145_NtKRJi.svg?v=1717077352",
303 | },
304 | },
305 | },
306 | {
307 | id: "b2425b54-202f-4b29-b4f6-e37e20044ca7",
308 | cursor: 286,
309 | tags: ["tops", "no-arrows"],
310 | allowedPlanSlugs: ["all"],
311 | title: { "en-US": "Baseball Tee Female" },
312 | description: null,
313 | mainImageSrc:
314 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset146-na_PEbOTf.svg?v=1717077352",
315 | status: "active",
316 | createdAt: new Date("2024-06-08 11:28:30.829326 +00:00"),
317 | updatedAt: new Date("2024-06-08 11:28:30.829326 +00:00"),
318 | resourceNamespace: "library",
319 | resource: {
320 | resourceType: "libraryStaticImage",
321 | data: {
322 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset146-na_PEbOTf.svg?v=1717077352",
323 | },
324 | },
325 | },
326 | {
327 | id: "841ef24f-9458-4c66-8072-2e475181f50f",
328 | cursor: 287,
329 | tags: ["tops", "arrows"],
330 | allowedPlanSlugs: ["all"],
331 | title: { "en-US": "Baseball Tee Female" },
332 | description: null,
333 | mainImageSrc:
334 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset146_PEbOTf.svg?v=1717077352",
335 | status: "active",
336 | createdAt: new Date("2024-06-08 11:28:30.829326 +00:00"),
337 | updatedAt: new Date("2024-06-08 11:28:30.829326 +00:00"),
338 | resourceNamespace: "library",
339 | resource: {
340 | resourceType: "libraryStaticImage",
341 | data: {
342 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset146_PEbOTf.svg?v=1717077352",
343 | },
344 | },
345 | },
346 | {
347 | id: "9b05e0bf-ec8e-4ff5-a86d-f076f7c6b559",
348 | cursor: 288,
349 | tags: ["tops", "no-arrows"],
350 | allowedPlanSlugs: ["all"],
351 | title: { "en-US": "Baseball Tee Male" },
352 | description: null,
353 | mainImageSrc:
354 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset147-na_aDsSav.svg?v=1717077352",
355 | status: "active",
356 | createdAt: new Date("2024-06-08 11:28:54.898317 +00:00"),
357 | updatedAt: new Date("2024-06-08 11:28:54.898317 +00:00"),
358 | resourceNamespace: "library",
359 | resource: {
360 | resourceType: "libraryStaticImage",
361 | data: {
362 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset147-na_aDsSav.svg?v=1717077352",
363 | },
364 | },
365 | },
366 | {
367 | id: "81ffe268-e233-4c32-820e-aaf9fb69dd31",
368 | cursor: 289,
369 | tags: ["tops", "arrows"],
370 | allowedPlanSlugs: ["all"],
371 | title: { "en-US": "Baseball Tee Male" },
372 | description: null,
373 | mainImageSrc:
374 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset147_aDsSav.svg?v=1717077352",
375 | status: "active",
376 | createdAt: new Date("2024-06-08 11:28:54.898317 +00:00"),
377 | updatedAt: new Date("2024-06-08 11:28:54.898317 +00:00"),
378 | resourceNamespace: "library",
379 | resource: {
380 | resourceType: "libraryStaticImage",
381 | data: {
382 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset147_aDsSav.svg?v=1717077352",
383 | },
384 | },
385 | },
386 | {
387 | id: "71c6d04b-46c0-47da-9ba7-27abf525d6cc",
388 | cursor: 290,
389 | tags: ["tops", "no-arrows"],
390 | allowedPlanSlugs: ["all"],
391 | title: { "en-US": "Football Jersey" },
392 | description: null,
393 | mainImageSrc:
394 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset148-na_PjHNeD.svg?v=1717077352",
395 | status: "active",
396 | createdAt: new Date("2024-06-08 11:29:30.498990 +00:00"),
397 | updatedAt: new Date("2024-06-08 11:29:30.498990 +00:00"),
398 | resourceNamespace: "library",
399 | resource: {
400 | resourceType: "libraryStaticImage",
401 | data: {
402 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset148-na_PjHNeD.svg?v=1717077352",
403 | },
404 | },
405 | },
406 | {
407 | id: "47fda7d8-406a-4379-9100-c56998d27294",
408 | cursor: 291,
409 | tags: ["tops", "arrows"],
410 | allowedPlanSlugs: ["all"],
411 | title: { "en-US": "Football Jersey" },
412 | description: null,
413 | mainImageSrc:
414 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset148_PjHNeD.svg?v=1717077352",
415 | status: "active",
416 | createdAt: new Date("2024-06-08 11:29:30.498990 +00:00"),
417 | updatedAt: new Date("2024-06-08 11:29:30.498990 +00:00"),
418 | resourceNamespace: "library",
419 | resource: {
420 | resourceType: "libraryStaticImage",
421 | data: {
422 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset148_PjHNeD.svg?v=1717077352",
423 | },
424 | },
425 | },
426 | {
427 | id: "efba58c2-11e1-43de-83a3-4bc8f23aa003",
428 | cursor: 277,
429 | tags: ["bags", "arrows"],
430 | allowedPlanSlugs: ["all"],
431 | title: { "en-US": "Tote Bag" },
432 | description: null,
433 | mainImageSrc:
434 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset140_qMvFUh.svg?v=1717077352",
435 | status: "active",
436 | createdAt: new Date("2024-06-08 11:25:48.677097 +00:00"),
437 | updatedAt: new Date("2024-06-08 11:25:48.677097 +00:00"),
438 | resourceNamespace: "library",
439 | resource: {
440 | resourceType: "libraryStaticImage",
441 | data: {
442 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset140_qMvFUh.svg?v=1717077352",
443 | },
444 | },
445 | },
446 | {
447 | id: "3ef3ec86-96b7-4197-a97c-bdedd0f6118b",
448 | cursor: 294,
449 | tags: ["accessories", "no-arrows"],
450 | allowedPlanSlugs: ["all"],
451 | title: { "en-US": "Diamond Ring" },
452 | description: null,
453 | mainImageSrc:
454 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset164-na.svg?v=1717077352",
455 | status: "active",
456 | createdAt: new Date("2024-08-20 13:42:36.020377 +00:00"),
457 | updatedAt: new Date("2024-08-20 13:42:36.020377 +00:00"),
458 | resourceNamespace: "library",
459 | resource: {
460 | resourceType: "libraryStaticImage",
461 | data: {
462 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset164-na.svg?v=1717077352",
463 | },
464 | },
465 | },
466 | {
467 | id: "b0b4f528-d4ab-4082-8a24-e3b60aacac11",
468 | cursor: 295,
469 | tags: ["accessories", "arrows"],
470 | allowedPlanSlugs: ["all"],
471 | title: { "en-US": "Diamond Ring" },
472 | description: null,
473 | mainImageSrc:
474 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset164.svg?v=1717077352",
475 | status: "active",
476 | createdAt: new Date("2024-08-20 13:42:36.020377 +00:00"),
477 | updatedAt: new Date("2024-08-20 13:42:36.020377 +00:00"),
478 | resourceNamespace: "library",
479 | resource: {
480 | resourceType: "libraryStaticImage",
481 | data: {
482 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset164.svg?v=1717077352",
483 | },
484 | },
485 | },
486 | {
487 | id: "d2564ae3-9380-4d07-bb1c-0887388f76cd",
488 | cursor: 296,
489 | tags: ["accessories", "no-arrows"],
490 | allowedPlanSlugs: ["all"],
491 | title: { "en-US": "Ring" },
492 | description: null,
493 | mainImageSrc:
494 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset178-na.svg?v=1717077352",
495 | status: "active",
496 | createdAt: new Date("2024-08-20 13:42:47.812374 +00:00"),
497 | updatedAt: new Date("2024-08-20 13:42:47.812374 +00:00"),
498 | resourceNamespace: "library",
499 | resource: {
500 | resourceType: "libraryStaticImage",
501 | data: {
502 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset178-na.svg?v=1717077352",
503 | },
504 | },
505 | },
506 | {
507 | id: "4b42b5cc-4d7c-42f6-8796-4beda8d82815",
508 | cursor: 297,
509 | tags: ["accessories", "arrows"],
510 | allowedPlanSlugs: ["all"],
511 | title: { "en-US": "Ring" },
512 | description: null,
513 | mainImageSrc:
514 | "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset178.svg?v=1717077352",
515 | status: "active",
516 | createdAt: new Date("2024-08-20 13:42:47.812374 +00:00"),
517 | updatedAt: new Date("2024-08-20 13:42:47.812374 +00:00"),
518 | resourceNamespace: "library",
519 | resource: {
520 | resourceType: "libraryStaticImage",
521 | data: {
522 | src: "https://cdn.shopify.com/s/files/1/0873/5948/8306/files/Asset178.svg?v=1717077352",
523 | },
524 | },
525 | },
526 | ] satisfies LibraryStaticImage[]
527 |
528 | export const handlePaginatedLibraryStaticImageResources = getPaginatedLibraryResources({
529 | resources: libraryStaticImageResources,
530 | })
531 |
--------------------------------------------------------------------------------
/src/environment.ts:
--------------------------------------------------------------------------------
1 | // @ts-expect-error
2 | export const locale = window?.shopify?.config?.locale
3 | export const rootLocale = locale?.split("-").at(0) ?? "en"
4 | export const shopifyApiBaseUrl = "https://mock.shop/api"
5 |
--------------------------------------------------------------------------------
/src/hooks/actor-ref-creator.hook.tsx:
--------------------------------------------------------------------------------
1 | import { useSelector } from "@xstate/react"
2 | import type { ProviderProps } from "react"
3 | import { createContext, useContext } from "react"
4 | import type { ActorRef, SnapshotFrom } from "xstate"
5 |
6 | /**
7 | * @description allows to provide an actor context, and related hooks based on the actor ref
8 | */
9 | export const createActorReferenceContext = <
10 | // biome-ignore lint/suspicious/noExplicitAny:
11 | TMachineReference extends ActorRef,
12 | >() => {
13 | const ActorReferenceContext = createContext(
14 | undefined as unknown as TMachineReference,
15 | )
16 | const ActorRefProvider = ({ children, value }: ProviderProps) => (
17 | {children}
18 | )
19 |
20 | const useActorRefContext = () => {
21 | const context = useContext(ActorReferenceContext)
22 |
23 | if (context === undefined) {
24 | throw new Error("useActorRefContext must be used within a ActorReferenceProvider")
25 | }
26 | return context
27 | }
28 |
29 | const useActorRefSelector = (
30 | selector: (emitted: SnapshotFrom) => T,
31 | compare?: (a: T, b: T) => boolean,
32 | ) => {
33 | const context = useActorRefContext()
34 | // FIXME: fix types
35 | // @ts-expect-error - false positive with xstate/react got some regression while bumping xstate version
36 | return useSelector(context, selector, compare)
37 | }
38 |
39 | return {
40 | useActorRefContext,
41 | useActorRefSelector,
42 | ActorRefProvider,
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/src/machines/inspector/inspector.machine.tsx:
--------------------------------------------------------------------------------
1 | import { BlockStack, Box, Icon, InlineStack, Link, Page, Text } from "@shopify/polaris"
2 | import { CodeIcon, EmailIcon, EyeCheckMarkIcon, LinkIcon, ViewIcon } from "@shopify/polaris-icons"
3 | import { createBrowserInspector } from "@statelyai/inspect"
4 | import { useActor } from "@xstate/react"
5 | import type { FunctionComponent } from "react"
6 | import { type ActorOptions, type AnyActorLogic, assign, fromCallback, setup } from "xstate"
7 | import videoUrl from "../../assets/usage-stately-actors.mp4"
8 | type BrowserInspector = ReturnType
9 |
10 | type ExampleComponent = FunctionComponent<{
11 | actorOptions: ActorOptions | undefined
12 | }>
13 |
14 | // Thanks, Baptiste, for this machine! - https://x.com/BDevessier
15 | const inspectorLauncherMachine = setup({
16 | types: {
17 | context: {} as {
18 | updateId: number
19 | inspector: BrowserInspector | undefined
20 | },
21 | events: {} as { type: "inspector.open" } | { type: "inspector.closed" },
22 | },
23 | actors: {
24 | "Wait for inspector window to be closed": fromCallback(
25 | ({ input, sendBack }) => {
26 | const timerId = setInterval(() => {
27 | const isInspectorClosed = input.inspector.adapter.targetWindow!.closed === true
28 |
29 | if (isInspectorClosed === true) {
30 | sendBack({
31 | type: "inspector.closed",
32 | })
33 | }
34 | }, 1_000)
35 |
36 | return () => {
37 | clearInterval(timerId)
38 | }
39 | },
40 | ),
41 | },
42 | actions: {
43 | "Create inspector and assign to context": assign({
44 | inspector: () => createBrowserInspector(),
45 | }),
46 | "Increment update id in context": assign({
47 | updateId: ({ context }) => context.updateId + 1,
48 | }),
49 | },
50 | }).createMachine({
51 | /** @xstate-layout N4IgpgJg5mDOIC5QAoC2BDAxgCwJYDswBKAOkwBsB7WSAYgNgAcxMAXSgJxMufwG0ADAF1EoRtVytclfKJAAPRAEYALAE4SAJgDsAZhUrtAVgBsugUoAcJgDQgAnst2aSAt293aBJ7Wu2qAXwC7NCw8QlIeMHx6fCYWdi4KakhBESQQcVhJaVkMxQRNEw0lUx01FW8BTSK7RwRLJRIjdwtdJSKjNV0TIOCQfEoIODlQnAJiOSycmTkCgFpbB0RFoJCMcYiyKhoIKYkpWfzEFU065RMBEms1SwFLTVMHn17+sfDibl597MO80AKKiMLm0p00SiUJkadwERhU5wQSmcrh6Kku7SsKiRmj6ASAA */
52 | context: {
53 | updateId: 0,
54 | inspector: undefined,
55 | },
56 | initial: "closed",
57 | states: {
58 | closed: {
59 | on: {
60 | "inspector.open": {
61 | target: "open",
62 | actions: ["Create inspector and assign to context", "Increment update id in context"],
63 | },
64 | },
65 | },
66 | open: {
67 | invoke: {
68 | src: "Wait for inspector window to be closed",
69 | input: ({ context }) => {
70 | if (context.inspector === undefined) {
71 | throw new Error("Inspector must be defined in context")
72 | }
73 |
74 | return {
75 | inspector: context.inspector!,
76 | }
77 | },
78 | },
79 | on: {
80 | "inspector.closed": {
81 | target: "closed",
82 | },
83 | },
84 | },
85 | },
86 | })
87 |
88 | const InspectorSetter = ({
89 | inspector,
90 | Example,
91 | }: {
92 | inspector: BrowserInspector | undefined
93 | Example: ExampleComponent
94 | }) => {
95 | const actorOptions: ActorOptions | undefined =
96 | inspector === undefined
97 | ? undefined
98 | : {
99 | inspect: inspector.inspect as any,
100 | }
101 |
102 | return
103 | }
104 |
105 | const socials = [
106 | {
107 | name: "Website",
108 | url: "https://djang0.dev",
109 | icon: LinkIcon,
110 | },
111 | {
112 | name: "GitHub",
113 | url: "https://github.com/djang0dev",
114 | icon: CodeIcon,
115 | },
116 | {
117 | name: "Mail",
118 | url: "mailto:kinane@djang0.dev",
119 | icon: EmailIcon,
120 | },
121 | ] as const
122 |
123 | export const AppExampleSandbox = ({
124 | Example,
125 | pageTitle,
126 | }: { Example: ExampleComponent; pageTitle: string }) => {
127 | const [state, send] = useActor(inspectorLauncherMachine)
128 |
129 | const isClosed = state.matches("closed")
130 | return (
131 | {
136 | send({ type: "inspector.open" })
137 | },
138 | plain: true,
139 | outline: true,
140 | content: isClosed ? "Open Inspector" : "Visualizing",
141 | icon: isClosed ? ViewIcon : EyeCheckMarkIcon,
142 | disabled: !isClosed,
143 | helpText: isClosed ? (
144 | "Visualize the machines on Stately.ai"
145 | ) : (
146 |
147 | {/* biome-ignore lint/a11y/useMediaCaption: I don't have */}
148 |
149 |
150 | ),
151 | },
152 | ]}>
153 |
154 |
159 |
160 |
161 |
162 | Made by{" "}
163 |
164 | Kinane Brevet
165 | {" "}
166 | - {new Date().getFullYear()}
167 |
168 | •
169 |
170 | {socials.map((social) => (
171 |
177 |
178 |
179 | ))}
180 |
181 |
182 |
183 |
184 |
185 | )
186 | }
187 |
--------------------------------------------------------------------------------
/src/machines/resource-picker/resource-picker.context.ts:
--------------------------------------------------------------------------------
1 | import type { ActorRefFrom } from "xstate"
2 | import { createActorReferenceContext } from "../../hooks/actor-ref-creator.hook.tsx"
3 | import { resourcePickerMachineEditor } from "./resource-picker.machine.ts"
4 |
5 | export const {
6 | ActorRefProvider: ResourcePickerRefProvider,
7 | useActorRefContext: useResourcePickerRefContext,
8 | useActorRefSelector: useResourcePickerRefSelector,
9 | } = createActorReferenceContext>()
10 |
--------------------------------------------------------------------------------
/src/machines/resource-picker/resource-picker.machine.ts:
--------------------------------------------------------------------------------
1 | import type { ModalProps } from "@shopify/polaris"
2 | import { create } from "mutative"
3 | import { assertEvent, assign, fromPromise, sendParent, setup } from "xstate"
4 | import type {
5 | LibraryQuery,
6 | LibraryResourceFilters,
7 | LibraryResourceTypeUnion,
8 | PageInfo,
9 | Resource,
10 | ResourceNamespace,
11 | ResourceType,
12 | ShopifyQuery,
13 | ShopifyResourceTypeUnion,
14 | } from "../../commons/entities"
15 | import { handleInvokeError } from "../../commons/http-errors"
16 |
17 | export interface GetResourcePickerItemsCbOutput {
18 | pageInfo: PageInfo
19 | libraryItems: Resource[]
20 | }
21 | export type ResourcePickerItemsCb = (
22 | payload: DiscriminatedQueryUnion,
23 | ) => Promise
24 | export type DiscriminatedLibraryQuery = LibraryQuery & { queryType: "library" }
25 | export type DiscriminatedShopifyQuery = ShopifyQuery & { queryType: "shopify" }
26 |
27 | export type DiscriminatedQueryUnion = DiscriminatedLibraryQuery | DiscriminatedShopifyQuery
28 |
29 | export type ResourceSelectionType = "single" | "multiple"
30 | export interface ResourceSettings {
31 | resourceNamespace: ResourceNamespace
32 | resourceType: ResourceType
33 | selectionType: ResourceSelectionType
34 | }
35 |
36 | export type FiltersHandler = (
37 | resourceType: LibraryResourceTypeUnion,
38 | ) => Promise
39 |
40 | export type ResourceSettingsV2 = { selectionType: ResourceSelectionType } & (
41 | | {
42 | resourceNamespace: "library"
43 | resourceType: LibraryResourceTypeUnion
44 | queryHandler: ResourcePickerItemsCb
45 | filtersHandler: FiltersHandler
46 | }
47 | | {
48 | resourceNamespace: "shopify"
49 | resourceType: ShopifyResourceTypeUnion
50 | queryHandler: ResourcePickerItemsCb
51 | }
52 | )
53 |
54 | export const isShopifyQueryQuery = (
55 | payload: DiscriminatedQueryUnion["query"],
56 | ): payload is ShopifyQuery["query"] => {
57 | return typeof payload === "string"
58 | }
59 | export type ResourceUiMode = "resource-item" | "card"
60 |
61 | export interface UiSettings {
62 | modalSize: ModalProps["size"] | "medium"
63 | resourceUiMode: ResourceUiMode
64 | }
65 | export type ResourceSearchMachineInput = {
66 | uiSettings: UiSettings
67 | resourceSettings: ResourceSettingsV2
68 | }
69 |
70 | export const resourcePickerMachineEditor = setup({
71 | types: {
72 | input: {} as ResourceSearchMachineInput,
73 | context: {} as ResourceSearchMachineInput & {
74 | filters: Record | undefined
75 | selectedItems: Map
76 | currentQuery: DiscriminatedQueryUnion
77 | currentResources: Resource[]
78 | currentPageInfo: PageInfo
79 | },
80 | events: {} as
81 | | {
82 | type: "library.query.editQuery"
83 | payload: {
84 | query: string
85 | }
86 | }
87 | | {
88 | type: "rp.query.filters.edit"
89 | payload: {
90 | filterId: string
91 | value: string[]
92 | }
93 | }
94 | | {
95 | type: "library.query.filters.clearAll"
96 | }
97 | | {
98 | type: "open"
99 | payload: {
100 | selectedItems?: Map
101 | }
102 | }
103 | | {
104 | type: "close"
105 | }
106 | | {
107 | type: "library.item.select"
108 | payload: {
109 | itemId: string
110 | }
111 | }
112 | | {
113 | type: "library.item.unselect"
114 | payload: {
115 | itemId: string
116 | }
117 | }
118 | | {
119 | type: "library.items.loadMore"
120 | }
121 | | {
122 | type: "library.done"
123 | },
124 | },
125 |
126 | actors: {
127 | getLibraryItem: fromPromise(
128 | async ({
129 | input,
130 | }: {
131 | input: {
132 | query: DiscriminatedQueryUnion
133 | handler: ResourcePickerItemsCb
134 | }
135 | }) => {
136 | return input.handler(input.query)
137 | },
138 | ),
139 |
140 | getFilters: fromPromise(
141 | async ({
142 | input,
143 | }: {
144 | input: {
145 | handler: FiltersHandler
146 | resourceType: LibraryResourceTypeUnion
147 | }
148 | }) => {
149 | return input.handler(input.resourceType)
150 | },
151 | ),
152 | },
153 | actions: {
154 | injectSelectedItems: assign(({ event, context }) =>
155 | create(context, (draft) => {
156 | assertEvent(event, "open")
157 | draft.selectedItems = event.payload?.selectedItems ?? new Map()
158 | }),
159 | ),
160 | clearQueryEndCursor: assign(({ context }) =>
161 | create(context, (draft) => {
162 | draft.currentQuery.endCursor = null
163 | }),
164 | ),
165 | editSearch: assign(({ context, event }) => {
166 | assertEvent(event, "library.query.editQuery")
167 | return create(context, (draft) => {
168 | draft.currentQuery.query = event.payload.query
169 | })
170 | }),
171 | clearSelected: assign(({ context }) =>
172 | create(context, (draft) => {
173 | draft.selectedItems.clear()
174 | }),
175 | ),
176 | clearFilters: assign(({ event, context }) => {
177 | assertEvent(event, "library.query.filters.clearAll")
178 | return create(context, (draft) => {
179 | if (draft.currentQuery.queryType === "library") {
180 | for (const filterToken in draft.currentQuery.filters) {
181 | draft.currentQuery.filters[filterToken as keyof LibraryResourceFilters["filters"]] = []
182 | }
183 | }
184 | })
185 | }),
186 | editFilter: assign(({ event, context }) => {
187 | assertEvent(event, "rp.query.filters.edit")
188 | return create(context, (draft) => {
189 | if (draft.currentQuery.queryType === "library")
190 | draft.currentQuery.filters[
191 | event.payload.filterId as keyof LibraryResourceFilters["filters"]
192 | ] = event.payload.value
193 | })
194 | }),
195 | sendSelectedItemsToParent: sendParent(({ context, self }) => {
196 | return {
197 | type: "rp.items.selected",
198 | payload: {
199 | selectedItems: context.selectedItems,
200 | senderId: self.id,
201 | resourceSettings: context.resourceSettings,
202 | },
203 | }
204 | }),
205 | },
206 | }).createMachine({
207 | /** @xstate-layout N4IgpgJg5mDOIC5QCc4HsCuyDGYAKAltgNZjIB0AwgDZqyQDEaADmAHYDaADALqKjM6BAC4E0bfiAAeiAKwAmADQgAnogAcARnIAWAJwGAzADZjXQwHY9hvRYC+d5aliYc+IqQoB5VmwbZaem4+JBBBWBExCVCZBHlDLXJZLhSbWWNNK0NZZTUETXlbXWN1dRtNLgzNdR0HJ3QsXEISMnIfdioACzASBmDJcMjxSVj5At0dWUNNdOSa+Xkc1Q1F8gW9Masx6a5ZOpBnVyaPVva2Lp7iPs0QgSFRYZjEeS55CamZ4zmdBaW803U5Eq8jM+j0ZhMe0cBwabmanjavguvQ48luYXuURGiE0G3e01mXHmi1yz2s5AsILMXD0yU001s+0OjXcLW8SIAMmgAIYQABiBGowjIsAYEHEYHIBDYADc0KRyMy4Sd2R0ubyBUKRQhpXLsNyHpxeP1QoNDdi4uMdJMCV8iT8ScsEBZphSLGZZBZqpVsvZoUrjmzEWqefzBcLkKKyMg0BRmNQDQAzWMAW0VsMDCLO5HVYa1kZ1srQ+sNwRNdwi5qeCHSegpnvU8i0sk0xgSf0QFlKQPUsnBLqJpnkTIzrKznND0qgYolUqLCoDY9OE95U8LeoNUTLvAGmMeoFiFh02mBLfUGXienUFlJCGPbwWXzK1j09OsI5cLPhy5Dq7Y0+jWNyHjJNU3TT9lSDbNczXXVi03cRt3RM0sWrCwWyBYxFmqC8bGvW9klkchjCsI8dBIq83yheoIMzH9zgAUQgEQGGoAgACNkG5ZAVClYUU1gchaF5ABZWMwHLDFK1Qg8cWSLggWsCwj0pEibCUJ16WqJJPm9TRJlkBQPyOJdVUY5jhFYjiuJ48gAEcMDIXjIBEABFRyeMklD92kTtz0w7DzwKPCbyddQUmImkdF2UwuEmQpjK-FVg3MkRyAAZTAagelEf8GGQZh7I83jE3DEVyBc4QvL3aJZPyfQFJpSwVJBF0NlvLQdApcwCkMgwmwS-1R2-MzyCYtLMuy7BcunNjOO43iHKc8hSvzQSAjAbiAEFqGoarpJ82J9L0RqlJatT2qdaxAQqPRovBExLHSRLIPHDpxuEDKspyqcrPm2yRDANN6Cmqqd1NGqLQqcLyFsL5jEmXssI0vJNH07Rpmw91DEKSEXro0aPq+0HfrmmzeMBtMMDYEGcv2oZat8hBwqIqwSlpBHkfQ280fJNIaj7B6SPx0yUrGiziZ+vKyYW8hxTYCTwYrBmLUvbR5PMDnMmsDt8mqN53XSNStC9cwRZGsWicy7jsE6UnrNlpbbMq9ynPpqs6oqH5YZKd1SmUiwaRRztW10YEMi+GwbXN5LsytzacDtvKpFgYQDUlblEwjAAKeSAEp8uG2OkXjm2k6gd2ZKZ47TuanRVLa4PnRItYevIwoQXimOoJLiWtrYblqBUAAvMBJp+8Q+iVqSVerNWkhSTX0m17IefpYwKS+QP9PMYwOe7t7Us+-vB5HsfvumqJrmQyHqwqLDyFbLhMhMfR1FfHmaSIzHDNbWR9AWAfeiOZQxiVQOQAASmAYQyACBgBlGAGcCs5xygXEXHuv4IBgMlFAmBcCEHrngqWY009vKMyOrYG6XpXwtj3mCXW9JLBrHfovHGz8ZjURhLRUW0FQHiUgdA2B8DGApzTsKcgmcc4vC4AXRcFteGiX4bgoRCCICV0Op2V0p4cLBSvKFVG9dtAgkMtFBI1oiRAIoAAEQlFPG+B1yHPDbIYHS6R3QunWPhTS99yBaEWDUQOjZ6RQmhGwNAEA4CSDkSqXcDiLQAFo9C3niRvReaT0kpD9DREyFsaB0EgLE2edUSI8y7NoQ2LZwSvjip6SxKVCkeyZjYW8LwN6Ph0KUJshkjzDiGtw+RSJKDdBIA0qusQvF5C0ApDYr5wRvjGFwLJXCcnF0wZqCM8AIZxOrAkRqvY7rxAUOYXst4uwb3SP4qwvY4oJDqQo5i-5RkaPyOhBSmRcRvhdBkQwpzopJHkORCoshOo6EMIYO5vcRBPMcc6BIAUzy4T0beLCgITptkWLsZ+LZwV9JWRgo+ktL6PK2UUpmNRAQglpAoAyRhNA80KF1cld0db9UpBC96EtraJynNCqGQdfH1zUmMB6hgdAdWtL43qkw94VFubipK+LxZpRPkPUe49L4+TIVDeQR5fGvI+aC5+tg159lbsvAwfYEboXZecXM2DeV30CWsBsCgCivGBcYelHS1jwxqOpQOOLskKsPiAxR4DlH4LAA6z26E2kurGC8fxnrNJgtZvDF4BgEaMnla9YBdqlGCMjRAaNTNNiAl2PXQoWFyLTDFSm01xjPTKSJC2CxOaCbkBsQrEtox-7aAAfEbYNhBxr29sYrCbYtBlH0A4BwQA */
208 | id: "resourcePicker",
209 | initial: "Closed",
210 | context: ({ input }) => ({
211 | ...input,
212 | filters: undefined,
213 | selectedItems: new Map(),
214 | // TODO: Pagination with drizzle @see: https://orm.drizzle.team/learn/guides/limit-offset-pagination
215 | currentQuery:
216 | input.resourceSettings.resourceNamespace === "library"
217 | ? {
218 | queryType: "library",
219 | query: "",
220 | filters: {
221 | tags: [],
222 | },
223 | first: 10,
224 | endCursor: null,
225 | }
226 | : {
227 | queryType: "shopify",
228 | query: "",
229 | endCursor: null,
230 | first: 10,
231 | },
232 | currentResources: [],
233 | currentPageInfo: {
234 | endCursor: null,
235 | hasNextPage: true,
236 | hasPreviousPage: false,
237 | startCursor: "0",
238 | },
239 | }),
240 | states: {
241 | Closed: {
242 | on: {
243 | open: {
244 | actions: ["injectSelectedItems"],
245 | target: "Open",
246 | },
247 | },
248 | },
249 | Open: {
250 | on: {
251 | close: {
252 | target: "Closed",
253 | },
254 | },
255 |
256 | initial: "Check",
257 | states: {
258 | Check: {
259 | always: [
260 | {
261 | target: "LoadFilters",
262 | guard: ({ context }) => {
263 | return !context.filters && context.resourceSettings.resourceNamespace === "library"
264 | },
265 | },
266 | {
267 | target: "Edit",
268 | guard: ({ context }) => {
269 | return context.currentResources.length > 0
270 | },
271 | },
272 | {
273 | target: "Loading",
274 | },
275 | ],
276 | },
277 | LoadFilters: {
278 | invoke: {
279 | src: "getFilters",
280 | input: ({ context }) => {
281 | if (context.resourceSettings.resourceNamespace !== "library") {
282 | throw new Error("filters not implemented for shopify resources")
283 | }
284 | return {
285 | handler: context.resourceSettings.filtersHandler,
286 | resourceType: context.resourceSettings.resourceType,
287 | }
288 | },
289 | onError: {
290 | actions: handleInvokeError,
291 | target: "Edit",
292 | },
293 | onDone: {
294 | actions: assign(({ event, context }) =>
295 | create(context, (draft) => {
296 | draft.filters = event.output.filters
297 | }),
298 | ),
299 | target: "Check",
300 | },
301 | },
302 | },
303 | Loading: {
304 | invoke: {
305 | input: ({ context }) => ({
306 | handler: context.resourceSettings.queryHandler,
307 | query: context.currentQuery,
308 | }),
309 | src: "getLibraryItem",
310 | onDone: {
311 | actions: assign(({ event, context }) =>
312 | create(context, (draft) => {
313 | draft.currentResources = event.output.libraryItems
314 | draft.currentPageInfo = event.output.pageInfo
315 | draft.currentQuery.endCursor = event.output.pageInfo.endCursor ?? null
316 | }),
317 | ),
318 | target: "Edit",
319 | },
320 | onError: {
321 | actions: handleInvokeError,
322 | target: "Edit",
323 | },
324 | },
325 | },
326 | Edit: {
327 | initial: "Selecting",
328 | on: {
329 | "library.items.loadMore": {
330 | target: "LoadMore",
331 | },
332 | "library.query.editQuery": {
333 | actions: ["editSearch", "clearQueryEndCursor"],
334 | target: "#resourcePicker.Open.Edit.Searching",
335 | },
336 | },
337 | states: {
338 | Selecting: {
339 | on: {
340 | "rp.query.filters.edit": {
341 | actions: ["editFilter", "clearQueryEndCursor"],
342 | target: "#resourcePicker.Open.Loading",
343 | },
344 | "library.query.filters.clearAll": {
345 | actions: ["clearFilters", "clearQueryEndCursor"],
346 | target: "#resourcePicker.Open.Loading",
347 | },
348 | "library.item.select": {
349 | actions: assign(({ event, context }) => {
350 | const itemId = event.payload.itemId
351 | const item = context.currentResources.find((item) => item.id === itemId)
352 |
353 | if (!item) {
354 | throw new Error(`item ${itemId} not found in currentResources`)
355 | }
356 |
357 | return create(context, (draft) => {
358 | if (context.resourceSettings.selectionType === "single") {
359 | draft.selectedItems.clear()
360 | }
361 | draft.selectedItems.set(event.payload.itemId, item)
362 | })
363 | }),
364 | target: "AnalyzeSelection",
365 | },
366 | "library.item.unselect": {
367 | actions: assign(({ event, context }) =>
368 | create(context, (draft) => {
369 | draft.selectedItems.delete(event.payload.itemId)
370 | }),
371 | ),
372 | },
373 | "library.done": {
374 | target: "#resourcePicker.Done",
375 | },
376 | },
377 | },
378 | Searching: {
379 | on: {
380 | "library.query.editQuery": {
381 | actions: ["clearQueryEndCursor", "editSearch"],
382 | target: "Searching",
383 | reenter: true,
384 | },
385 | },
386 | after: {
387 | 5e2: {
388 | target: "#resourcePicker.Open.Loading",
389 | },
390 | },
391 | },
392 | AnalyzeSelection: {
393 | always: [
394 | {
395 | guard: ({ context }) => {
396 | return (
397 | // context.selectedItemIds.length === 1 &&
398 | context.resourceSettings.selectionType === "single"
399 | )
400 | },
401 |
402 | target: "#resourcePicker.Done",
403 | },
404 | {
405 | target: "Selecting",
406 | },
407 | ],
408 | },
409 | },
410 | },
411 | LoadMore: {
412 | initial: "Retrieve",
413 | states: {
414 | Retrieve: {
415 | invoke: {
416 | input: ({ context }) => ({
417 | handler: context.resourceSettings.queryHandler,
418 | query: context.currentQuery,
419 | }),
420 | src: "getLibraryItem",
421 | onDone: {
422 | actions: assign(({ event, context }) =>
423 | create(context, (draft) => {
424 | draft.currentResources.push(...event.output.libraryItems)
425 | draft.currentPageInfo = event.output.pageInfo
426 | draft.currentQuery.endCursor = event.output.pageInfo.endCursor ?? null
427 | }),
428 | ),
429 | target: "Retrieved",
430 | },
431 | },
432 | },
433 | Retrieved: {
434 | after: {
435 | 2e2: {
436 | target: "#resourcePicker.Open.Edit",
437 | },
438 | },
439 | },
440 | },
441 | },
442 | },
443 | },
444 | Done: {
445 | always: [
446 | {
447 | actions: ["sendSelectedItemsToParent", "clearSelected"],
448 | target: "#resourcePicker.Closed",
449 | },
450 | ],
451 | },
452 | },
453 | })
454 |
--------------------------------------------------------------------------------
/src/machines/resource-picker/resource-picker.view.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | type AppliedFilterInterface,
3 | BlockStack,
4 | Box,
5 | Checkbox,
6 | ChoiceList,
7 | Filters,
8 | Grid,
9 | Icon,
10 | InlineStack,
11 | Modal,
12 | Spinner,
13 | Tag,
14 | Text,
15 | TextField,
16 | Thumbnail,
17 | Tooltip,
18 | } from "@shopify/polaris"
19 | import { LockFilledIcon, SearchIcon } from "@shopify/polaris-icons"
20 | import React, { Fragment, type ReactElement, useMemo } from "react"
21 | import useInfiniteScroll from "react-infinite-scroll-hook"
22 | import * as R from "remeda"
23 | import type { ActorRefFrom } from "xstate"
24 | import { shopifyEditorConfig } from "../../shopify.config.ts"
25 | import { beautifyObjectName, beautifySlug, formatShopifyUrlImage } from "../../utils.ts"
26 |
27 | import { Resource } from "../../commons/entities.ts"
28 | import {
29 | ResourcePickerRefProvider,
30 | useResourcePickerRefContext,
31 | useResourcePickerRefSelector,
32 | } from "./resource-picker.context.ts"
33 | import type { ResourceUiMode, resourcePickerMachineEditor } from "./resource-picker.machine.ts"
34 |
35 | export const LibraryItem: React.FC<{
36 | resourceItem: Resource
37 | onSelect: (selected: boolean) => void
38 | isSelected: boolean
39 | lock?: {
40 | content: ReactElement
41 | }
42 | mode: ResourceUiMode
43 | }> = ({ resourceItem, onSelect, isSelected, lock, mode }) => {
44 | const resourceItemTitle =
45 | typeof resourceItem.title === "string" ? resourceItem.title : resourceItem.title["en-US"]
46 | const resourceItemDescription =
47 | resourceItem.resourceNamespace === "library"
48 | ? resourceItem.description == null
49 | ? ""
50 | : resourceItem.description["en-US"]
51 | : ""
52 | return (
53 |
54 |
136 |
137 | )
138 | }
139 |
140 | export const LibraryFilters = () => {
141 | const { resourceNamespace } = useResourcePickerRefSelector(
142 | (state) => state.context.resourceSettings,
143 | )
144 | const query = useResourcePickerRefSelector((state) => state.context.currentQuery.query)
145 | const queryFilters = useResourcePickerRefSelector((state) => {
146 | if (state.context.currentQuery.queryType === "library") {
147 | return state.context.currentQuery.filters
148 | }
149 | return undefined
150 | })
151 |
152 | const filtersData = useResourcePickerRefSelector((state) => state.context.filters)
153 | const { send } = useResourcePickerRefContext()
154 |
155 | const filters =
156 | resourceNamespace === "shopify"
157 | ? []
158 | : Object.entries(filtersData ?? {}).map(([filterId, filterData]) => {
159 | return {
160 | key: filterId,
161 | label: beautifyObjectName(filterId),
162 | filter: (
163 | {
170 | return { label: filterToken, value: filterToken }
171 | })
172 | }
173 | selected={queryFilters?.tags ?? []}
174 | onChange={(selected) => {
175 | send({
176 | type: "rp.query.filters.edit",
177 | payload: {
178 | filterId,
179 | value: selected,
180 | },
181 | })
182 | }}
183 | allowMultiple
184 | />
185 | ),
186 | shortcut: true,
187 | }
188 | })
189 |
190 | const appliedFilters = useMemo(() => {
191 | if (queryFilters && filtersData) {
192 | return Object.entries(filtersData).reduce((acc, [filterId]) => {
193 | const selectedFilterData = queryFilters[filterId as keyof typeof queryFilters]
194 | if (!selectedFilterData || selectedFilterData?.length === 0) return acc
195 |
196 | return [
197 | {
198 | key: filterId,
199 | label:
200 | R.isArray(selectedFilterData) && !R.isEmpty(selectedFilterData)
201 | ? `Selected: ${selectedFilterData.join(", ")}`
202 | : "",
203 | onRemove: () => {
204 | send({
205 | type: "rp.query.filters.edit",
206 | payload: {
207 | filterId: filterId,
208 | value: [],
209 | },
210 | })
211 | },
212 | },
213 | ]
214 | }, [] as AppliedFilterInterface[])
215 | }
216 | return []
217 | }, [queryFilters, send, filtersData])
218 |
219 | return (
220 |
221 |
222 |
223 | }
227 | value={query ?? ""}
228 | onChange={(value) => {
229 | send({
230 | type: "library.query.editQuery",
231 | payload: {
232 | query: value,
233 | },
234 | })
235 | }}
236 | />
237 |
238 | {}}
241 | onQueryClear={() => {}}
242 | onClearAll={() => {
243 | send({ type: "library.query.filters.clearAll" })
244 | }}
245 | hideQueryField={true}
246 | appliedFilters={appliedFilters}
247 | />
248 |
249 |
250 | )
251 | }
252 |
253 | export const ResourceItems = () => {
254 | const resourceItems = useResourcePickerRefSelector((state) => state.context.currentResources)
255 | const selectedItems = useResourcePickerRefSelector((state) => state.context.selectedItems)
256 | const uiSettings = useResourcePickerRefSelector((state) => state.context.uiSettings)
257 | const shopAssignedAppPlanSlug = "free"
258 | const { send } = useResourcePickerRefContext()
259 | return resourceItems.map((resourceItem) => {
260 | let lock
261 | if (
262 | resourceItem.resourceNamespace === "shopify" ||
263 | resourceItem.allowedPlanSlugs.includes(shopAssignedAppPlanSlug) ||
264 | resourceItem.allowedPlanSlugs.includes("all")
265 | ) {
266 | lock = undefined
267 | } else {
268 | lock = {
269 | content: (
270 |
276 |
280 |
281 |
282 | {/* FIXME: why does it refresh the whole page on redirect?*/}
283 |
284 | Upgrade
285 |
286 |
287 |
288 |
289 | ),
290 | }
291 | }
292 |
293 | return (
294 |
295 | {
301 | if (selected) {
302 | send({
303 | type: "library.item.select",
304 | payload: {
305 | itemId: resourceItem.id,
306 | },
307 | })
308 | return
309 | }
310 |
311 | send({
312 | type: "library.item.unselect",
313 | payload: {
314 | itemId: resourceItem.id,
315 | },
316 | })
317 | }}
318 | />
319 |
320 | )
321 | })
322 | }
323 | export const ResourcePickerMain = () => {
324 | const { send } = useResourcePickerRefContext()
325 | const isLoading = useResourcePickerRefSelector((state) => state.matches({ Open: "Loading" }))
326 | const isLoadMore = useResourcePickerRefSelector((state) =>
327 | state.matches({ Open: { LoadMore: "Retrieved" } }),
328 | )
329 |
330 | const uiSettings = useResourcePickerRefSelector((state) => state.context.uiSettings)
331 |
332 | const hasNextPage = useResourcePickerRefSelector(
333 | (state) => state.context.currentPageInfo.hasNextPage,
334 | )
335 |
336 | const [intersectionRef, { rootRef }] = useInfiniteScroll({
337 | loading: isLoading || isLoadMore,
338 | hasNextPage: hasNextPage,
339 |
340 | onLoadMore: () => {
341 | send({
342 | type: "library.items.loadMore",
343 | })
344 | },
345 | // disabled: !!error,
346 | rootMargin: "0px 0px 20px 0px",
347 | })
348 |
349 | if (isLoading) {
350 | return (
351 |
352 |
353 |
354 |
355 |
356 | )
357 | }
358 |
359 | return (
360 |
361 |
362 |
363 | {uiSettings.resourceUiMode === "card" && (
364 |
365 |
366 |
367 | )}
368 | {uiSettings.resourceUiMode === "resource-item" && (
369 |
370 |
371 |
372 |
373 |
374 | )}
375 |
376 |
377 | {(isLoadMore || hasNextPage) && (
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 | )}
386 |
387 | )
388 | }
389 |
390 | export const ResourcePicker = () => {
391 | const state = useResourcePickerRefSelector((state) => state.value)
392 | const resourceType = useResourcePickerRefSelector(
393 | (state) => state.context.resourceSettings.resourceType,
394 | )
395 |
396 | const uiSettings = useResourcePickerRefSelector((state) => state.context.uiSettings)
397 | const resourceSettings = useResourcePickerRefSelector((state) => state.context.resourceSettings)
398 | const { send } = useResourcePickerRefContext()
399 |
400 | return (
401 | {
405 | send({ type: "close" })
406 | }}
407 | primaryAction={
408 | resourceSettings.selectionType === "multiple"
409 | ? {
410 | content: "Select",
411 | onAction: () => {
412 | send({ type: "library.done" })
413 | },
414 | }
415 | : undefined
416 | }
417 | size={uiSettings.modalSize === "medium" ? undefined : uiSettings.modalSize}>
418 |
419 |
420 |
421 |
422 |
423 | )
424 | }
425 | export const ResourcePickerProvider: React.FC<{
426 | actorRef: ActorRefFrom
427 | }> = ({ actorRef }) => (
428 |
429 |
430 |
431 | )
432 |
--------------------------------------------------------------------------------
/src/machines/root/root.machine.context.tsx:
--------------------------------------------------------------------------------
1 | import type { ActorRefFrom } from "xstate"
2 | import { createActorReferenceContext } from "../../hooks/actor-ref-creator.hook.tsx"
3 | import { rootMachine } from "./root.machine.ts"
4 |
5 | export const {
6 | ActorRefProvider: RootProvider,
7 | useActorRefContext: useRootRef,
8 | useActorRefSelector: useRootSelector,
9 | } = createActorReferenceContext>()
10 |
--------------------------------------------------------------------------------
/src/machines/root/root.machine.ts:
--------------------------------------------------------------------------------
1 | import request from "graphql-request"
2 | import { create } from "mutative"
3 | import toast from "react-hot-toast/headless"
4 | import set from "set-value"
5 | import { ActorRefFrom, assertEvent, assign, enqueueActions, setup, stopChild } from "xstate"
6 | import {
7 | CollectionQueryResponse,
8 | LibraryResourceTypeUnion,
9 | ProductsResponse,
10 | Resource,
11 | ShopifyResourceTypeUnion,
12 | } from "../../commons/entities"
13 | import {
14 | handlePaginatedLibraryStaticImageResources,
15 | libraryStaticImageResources,
16 | } from "../../data/library-static-images-resources.data"
17 | import { getShopifyCollections, getShopifyProducts } from "../../queries"
18 | import { getRandomString } from "../../utils"
19 | import {
20 | ResourceSearchMachineInput,
21 | ResourceSelectionType,
22 | ResourceSettings,
23 | ResourceSettingsV2,
24 | UiSettings,
25 | resourcePickerMachineEditor,
26 | } from "../resource-picker/resource-picker.machine"
27 |
28 | const rootPartialInput = {
29 | libraryStaticImage: {
30 | resourceSettings: {
31 | resourceNamespace: "library",
32 | resourceType: "libraryStaticImage",
33 | filtersHandler: async () => {
34 | return {
35 | filters: { tags: [...new Set(libraryStaticImageResources.flatMap((i) => i.tags))] },
36 | }
37 | },
38 | queryHandler: async (payload) => {
39 | if (payload.queryType === "shopify") {
40 | throw new Error(
41 | `Invalid library query received: ${JSON.stringify(
42 | payload,
43 | )}, might be due if you use the Shopify query formatting instead of the library one`,
44 | )
45 | }
46 |
47 | const items = handlePaginatedLibraryStaticImageResources({
48 | query: payload.query,
49 | first: payload.first,
50 | filters: { tags: payload.filters.tags ?? [] },
51 | after: payload.endCursor ? Number(payload.endCursor) : undefined,
52 | })
53 |
54 | console.log({ items })
55 | return {
56 | libraryItems: items.data.map((i) => ({
57 | tags: i.tags,
58 | updatedAt: new Date(i.updatedAt),
59 | cursor: i.cursor,
60 | resourceNamespace: "library",
61 | status: i.status,
62 | allowedPlanSlugs: ["all"],
63 | id: i.id,
64 | createdAt: i.createdAt,
65 | description: i.description,
66 | mainImageSrc: i.mainImageSrc,
67 | title: i.title,
68 | resource: {
69 | resourceType: "libraryStaticImage",
70 | title: i.title,
71 | data: {
72 | src: i.mainImageSrc,
73 | },
74 | },
75 | })),
76 | pageInfo: {
77 | hasNextPage: items.pageInfo.hasNextPage,
78 | hasPreviousPage: items.pageInfo.hasPreviousPage,
79 | endCursor: String(items.pageInfo.endCursor) || null,
80 | },
81 | }
82 | },
83 | },
84 | },
85 | product: {
86 | resourceSettings: {
87 | resourceNamespace: "shopify",
88 | queryHandler: async (payload) => {
89 | if (payload.queryType === "library") {
90 | throw new Error(
91 | `Invalid library query received: ${JSON.stringify(
92 | payload,
93 | )}, might be due if you use the Shopify query formatting instead of the library one`,
94 | )
95 | }
96 |
97 | const data = (await request("https://mock.shop/api", getShopifyProducts, {
98 | after: payload.endCursor,
99 | first: payload.first,
100 | query: payload.query,
101 | })) as ProductsResponse
102 |
103 | return {
104 | libraryItems: data.products.edges.map((edge) => ({
105 | allowedPlanSlugs: ["all"],
106 | id: edge.node.id,
107 | resourceNamespace: "shopify",
108 | mainImageSrc: edge.node.featuredImage.url,
109 | resource: { resourceType: "product", title: edge.node.title },
110 | title: edge.node.title,
111 | })),
112 | pageInfo: data.products.pageInfo,
113 | }
114 | },
115 | resourceType: "product",
116 | },
117 | },
118 | collection: {
119 | resourceSettings: {
120 | resourceNamespace: "shopify",
121 | queryHandler: async (payload) => {
122 | if (payload.queryType === "library") {
123 | throw new Error(
124 | `Invalid library query received: ${JSON.stringify(
125 | payload,
126 | )}, might be due if you use the Shopify query formatting instead of the library one`,
127 | )
128 | }
129 |
130 | const data = (await request("https://mock.shop/api", getShopifyCollections, {
131 | after: payload.endCursor,
132 | first: payload.first,
133 | query: payload.query,
134 | })) as CollectionQueryResponse
135 |
136 | return {
137 | libraryItems: data.collections.edges.map((edge) => ({
138 | allowedPlanSlugs: ["all"],
139 | id: edge.node.id,
140 | resourceNamespace: "shopify",
141 | mainImageSrc: edge.node.image.url,
142 | resource: { resourceType: "product", title: edge.node.title },
143 | title: edge.node.title,
144 | })),
145 | pageInfo: data.collections.pageInfo,
146 | }
147 | },
148 | resourceType: "collection",
149 | },
150 | },
151 | } satisfies { [type: string]: { resourceSettings: Partial } }
152 |
153 | export interface SpawnForm {
154 | uiSettings: UiSettings
155 | resourceType: ShopifyResourceTypeUnion
156 | resourceSelectionType: ResourceSelectionType
157 | }
158 |
159 | export interface ResourcePickerValue {
160 | actorRef: ActorRefFrom
161 | latestSelectedItemIds: Set
162 | }
163 |
164 | export const rootMachine = setup({
165 | types: {
166 | events: {} as
167 | | {
168 | type: "rp.spawn"
169 | }
170 | | {
171 | type: "rp.open"
172 | payload: {
173 | id: string
174 | }
175 | }
176 | | {
177 | type: "rp.kill"
178 | payload: {
179 | id: string
180 | }
181 | }
182 | | {
183 | type: "rp.items.selected"
184 | payload: {
185 | senderId: string
186 | selectedItems: Map
187 | resourceSettings: ResourceSettings
188 | }
189 | }
190 | | {
191 | // TODO: better typesafety
192 | type: "rpSpawnForm.edit"
193 | payload: {
194 | key: string
195 | value: string
196 | }
197 | },
198 |
199 | context: {} as {
200 | spawnForm: {
201 | uiSettings: UiSettings
202 | resourceType: ShopifyResourceTypeUnion | LibraryResourceTypeUnion
203 | resourceSelectionType: ResourceSelectionType
204 | }
205 | resourcePickers: Map<
206 | string,
207 | {
208 | actorRef: ActorRefFrom
209 | latestSelectedItems: Map
210 | }
211 | >
212 | },
213 | },
214 | actions: {
215 | openResourcePicker: enqueueActions(({ context, enqueue, event }) => {
216 | assertEvent(event, "rp.open")
217 | const rp = context.resourcePickers.get(event.payload.id)
218 | if (!rp) return
219 |
220 | enqueue.sendTo(rp.actorRef, {
221 | type: "open",
222 | payload: {
223 | selectedItems: rp.latestSelectedItems,
224 | },
225 | })
226 | }),
227 | rpInsertLastSelectedItemsIds: assign(({ event, context }) => {
228 | assertEvent(event, "rp.items.selected")
229 | const id = event.payload.senderId
230 | return create(context, (draft) => {
231 | const resourcePicker = draft.resourcePickers.get(id)
232 | if (!resourcePicker) return
233 | resourcePicker.latestSelectedItems = event.payload.selectedItems
234 | })
235 | }),
236 |
237 | logContext: ({ context }) => {
238 | console.log({ context })
239 | },
240 |
241 | killResourcePicker: assign(({ event, context }) => {
242 | assertEvent(event, "rp.kill")
243 | const actorId = event.payload.id
244 | stopChild(actorId)
245 | return create(context, (draft) => {
246 | draft.resourcePickers.delete(actorId)
247 | })
248 | }),
249 |
250 | spawnResourcePicker: assign(({ context, event, spawn }) => {
251 | assertEvent(event, "rp.spawn")
252 | const systemId = getRandomString()
253 | const { spawnForm } = context
254 | // @ts-expect-error id wrong typing from xstate
255 | const actorRef = spawn(resourcePickerMachineEditor, {
256 | input: {
257 | resourceSettings: {
258 | ...rootPartialInput[spawnForm.resourceType]?.resourceSettings,
259 | selectionType: spawnForm.resourceSelectionType,
260 | } as ResourceSettingsV2,
261 | uiSettings: spawnForm.uiSettings,
262 | } satisfies ResourceSearchMachineInput,
263 |
264 | id: systemId,
265 | })
266 |
267 | return create(context, (draft) => {
268 | draft.resourcePickers.set(actorRef.id, {
269 | // @ts-expect-error id wrong typing from xstate
270 | actorRef: actorRef,
271 | latestSelectedItems: new Map(),
272 | })
273 | })
274 | }),
275 | editRpSpawnerForm: assign(({ context, event }) => {
276 | assertEvent(event, "rpSpawnForm.edit")
277 |
278 | console.log(event)
279 | return create(context, (draft) => {
280 | set(draft.spawnForm, event.payload.key, event.payload.value)
281 | })
282 | }),
283 | },
284 | }).createMachine({
285 | /** @xstate-layout N4IgpgJg5mDOIC5QCcD2qAuA6AlhANmAMTIAOAyqQIYDuAdgGKrIC2WkOGA2gAwC6iUKVSxOOVHUEgAHogBsADiwAmAMwLlATgCsAFh6bVygOwBGUwBoQAT3lms24wtOPTW4wdUBfL1bSZcAmIyXAwwFlgsWDBCAGMwiF4BJBBhUQxxSRTZBF1lK1sEPSVTM00eZR4KhSrlHz90bDxCElIsVFIwOiSpNLEJKRy5Yyw5OR5TOW1NDVNNeYLEPN0sYym5XUVjbQVNUx5depB-JqDWrABrHHx8HpS+jIHsxGMV5XXVOUMTExdFhFUEywqmmqj2r12zgUciOJ0CLRCsGo9DuQhE-SyoCGqhUpgUa20Lm0ql02hM-1KIx0qlMnzkykqxhMsMa8OCbSRtDoADVlKjUujHpiZIhtNpgWCFLpjPNPnptP9nA4qlVdOplNoDDwFD5fCA6KgIHApCdeoLMoNEABaOT-G1YFWOp1VYwsgLNMBm9IW54INzKLCaAnacZ4zSkjb-GWaQNS3T+kNyPHaXVeIA */
286 | id: "root",
287 | initial: "idle",
288 | context: {
289 | resourcePickers: new Map(),
290 | spawnForm: {
291 | uiSettings: {
292 | modalSize: "large",
293 | resourceUiMode: "resource-item",
294 | },
295 | resourceSelectionType: "multiple",
296 | resourceType: "product",
297 | },
298 | },
299 | states: {
300 | idle: {
301 | on: {
302 | "rpSpawnForm.edit": {
303 | actions: ["editRpSpawnerForm", "logContext"],
304 | },
305 | "rp.items.selected": {
306 | actions: [
307 | ({ event }) => {
308 | toast.success(
309 | `Root actor received the selected items ids from ${event.payload.senderId}`,
310 | )
311 | },
312 | "rpInsertLastSelectedItemsIds",
313 | ],
314 | },
315 | "rp.open": {
316 | actions: [
317 | "openResourcePicker",
318 | ({ event }) => {
319 | toast.success(`Resource picker ${event.payload.id} opened from root actor`)
320 | },
321 | ],
322 | },
323 | "rp.kill": {
324 | actions: [
325 | "killResourcePicker",
326 | ({ event }) => {
327 | toast.error(`Resource Picker ${event.payload.id} killed from root actor`)
328 | },
329 | ],
330 | },
331 |
332 | "rp.spawn": {
333 | actions: [
334 | "spawnResourcePicker",
335 | () => toast.success("Resource picker spawned from root actor"),
336 | ],
337 | },
338 | },
339 | },
340 | },
341 | })
342 |
--------------------------------------------------------------------------------
/src/machines/root/root.view.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | BlockStack,
3 | Box,
4 | Button,
5 | Card,
6 | ChoiceList,
7 | InlineStack,
8 | Layout,
9 | List,
10 | Select,
11 | Text,
12 | } from "@shopify/polaris"
13 | import { useActorRef } from "@xstate/react"
14 | import { Fragment, useMemo } from "react"
15 | import type { FC } from "react"
16 | import { ActorOptions, AnyActorLogic } from "xstate"
17 |
18 | import { getRandomNumberFromN } from "../../utils.ts"
19 | import { ResourcePickerProvider } from "../resource-picker/resource-picker.view.tsx"
20 | import { RootProvider, useRootRef, useRootSelector } from "./root.machine.context.tsx"
21 | import { ResourcePickerValue, rootMachine } from "./root.machine.ts"
22 |
23 | interface Props {
24 | actorOptions: ActorOptions | undefined
25 | }
26 | export const Root = ({ actorOptions }: Props) => {
27 | const rootActorRef = useActorRef(rootMachine, actorOptions)
28 | return (
29 |
30 |
31 |
32 | )
33 | }
34 |
35 | export const RootForm = () => {
36 | const spawnForm = useRootSelector((snapshot) => snapshot.context.spawnForm)
37 | const { send } = useRootRef()
38 | return (
39 |
40 |
41 | {
46 | const selected = v.at(0)
47 | if (!selected) return
48 | send({
49 | type: "rpSpawnForm.edit",
50 | payload: {
51 | key: "resourceType",
52 | value: selected,
53 | },
54 | })
55 | }}
56 | choices={[
57 | { label: "Product", value: "product" },
58 | { label: "Collection", value: "collection" },
59 | { label: "Static Image", value: "libraryStaticImage" },
60 | ]}
61 | />
62 |
152 |
153 | )
154 | }
155 |
156 | export const RootCard: FC<{ id: string } & ResourcePickerValue> = ({
157 | id,
158 | latestSelectedItemIds,
159 | actorRef,
160 | }) => {
161 | const { send } = useRootRef()
162 |
163 | const random = useMemo(() => getRandomNumberFromN(5), [])
164 | return (
165 |
166 |
180 |
181 |
182 |
183 |
184 | Resource Picker - {id}
185 |
186 |
187 |
188 | Latest Selected Ids
189 |
190 |
191 | {[...latestSelectedItemIds].map((id) => (
192 |
193 | {id}
194 |
195 | ))}
196 |
197 |
198 |
199 |
200 |
201 |
213 |
226 |
227 |
228 |
229 |
230 |
231 |
232 | )
233 | }
234 |
235 | export const RootMain = () => {
236 | const resourcePickers = useRootSelector((state) => state.context.resourcePickers)
237 | return (
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 | Root Control Center
246 |
247 |
248 |
249 |
250 | Selected Ids collected back from root
251 |
252 |
253 | {[...resourcePickers].map(([id, { latestSelectedItems, actorRef }]) => (
254 |
255 |
261 |
262 | ))}
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 | )
280 | }
281 |
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react'
2 | import { createRoot } from 'react-dom/client'
3 | import './index.css'
4 | import App from './App.tsx'
5 |
6 | createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/src/providers/polaris.provider.tsx:
--------------------------------------------------------------------------------
1 | import "@shopify/polaris/build/esm/styles.css"
2 |
3 | import { AppProvider } from "@shopify/polaris"
4 | import { type ReactNode, useLayoutEffect, useState } from "react"
5 | import { rootLocale } from "../environment.ts"
6 |
7 | // For now the app is translated in en and fr exclusively
8 | const languageMapper = new Map([
9 | ["fr", () => import("@shopify/polaris/locales/fr.json")] as const,
10 | ["en", () => import("@shopify/polaris/locales/en.json")] as const,
11 | ])
12 | export const PolarisProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
13 | const [translation, setTranslation] = useState(null)
14 |
15 | useLayoutEffect(() => {
16 | const loadTranslation = async () => {
17 | const l = languageMapper.get(rootLocale as any)
18 | if (l) {
19 | const result = await l()
20 | setTranslation(result)
21 | } else {
22 | const fallbackLanguageCallback = languageMapper.get("en")
23 | if (fallbackLanguageCallback) {
24 | const result = await fallbackLanguageCallback()
25 | setTranslation(result)
26 | }
27 | }
28 | }
29 |
30 | loadTranslation()
31 | }, [])
32 |
33 | if (!translation) {
34 | return null
35 | }
36 |
37 | return {children}
38 | }
39 |
--------------------------------------------------------------------------------
/src/queries.ts:
--------------------------------------------------------------------------------
1 | import { gql } from "graphql-request"
2 |
3 | export const getShopifyProducts = gql`
4 | query getShopifyProducts($first: Int!, $after: String, $query: String)
5 | {
6 | products(first: $first, after: $after, query: $query) {
7 | pageInfo {
8 | hasNextPage
9 | endCursor
10 | hasPreviousPage
11 | startCursor
12 | }
13 | edges {
14 | node {
15 | id
16 | title
17 | description
18 | featuredImage {
19 | id
20 | url
21 | }
22 | variants(first: 3) {
23 | edges {
24 | node {
25 | price {
26 | amount
27 | currencyCode
28 | }
29 | }
30 | }
31 | }
32 | }
33 | }
34 | }
35 | }
36 |
37 | `
38 |
39 | export const getShopifyCollections = gql`
40 | query getShopifyCollections($first: Int!, $after: String, $query: String)
41 | {
42 | collections(first: $first, after: $after, query: $query) {
43 | pageInfo {
44 | hasNextPage
45 | endCursor
46 | hasPreviousPage
47 | startCursor
48 | }
49 | edges {
50 | node {
51 | id
52 | title
53 | description
54 | image {
55 | url
56 | }
57 | }
58 | }
59 | }
60 | }
61 | `
62 |
--------------------------------------------------------------------------------
/src/shopify.config.ts:
--------------------------------------------------------------------------------
1 | export const shopifyEditorConfig = {
2 | fallbacks: {
3 | productImageURL:
4 | "https://cdn.shopify.com/s/files/1/0533/2089/files/placeholder-images-product-1.png",
5 | collectionImageURL:
6 | "https://cdn.shopify.com/s/files/1/0533/2089/files/placeholder-images-collection-1.png",
7 | imageURL: "https://cdn.shopify.com/s/files/1/0533/2089/files/placeholder-images-image.png?",
8 | },
9 | } as const
10 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import * as R from "remeda"
2 | import { LibraryResource, LibraryResourceFilters } from "./commons/entities"
3 |
4 | export const getRandomString = (length = 10) =>
5 | (Math.random() + 1).toString(36).substring(2, length + 2)
6 |
7 | export const getRandomNumberFromN = (N: number): number => {
8 | return Math.floor(Math.random() * (N + 1))
9 | }
10 |
11 | /**
12 | * Capitalize the first letter of a string.
13 | * @param string
14 | */
15 | export const cfl = (string: string) => string.charAt(0).toUpperCase() + string.slice(1)
16 |
17 | /**
18 | * Beautify a camelCase string.
19 | * e.g. "myString" -> "My String"
20 | */
21 | export const beautifyObjectName = (string: string) => {
22 | let output = string.replaceAll(/([A-Z])/g, " $1")
23 | output = output.charAt(0).toUpperCase() + output.slice(1)
24 | return output
25 | }
26 |
27 | export const beautifySlug = (string: string) => {
28 | return cfl(string.replaceAll("-", " "))
29 | }
30 |
31 | export const formatShopifyUrlImage = (
32 | url: string,
33 | options: {
34 | width?: number
35 | height?: number
36 | crop?: "bottom" | "center" | "left" | "right" | "top"
37 | scale?: 2 | 3
38 | },
39 | ): string => {
40 | const { width, height, crop, scale } = options
41 | if (width == null && height == null) return url
42 | let newUrl: string[] | string = url.split(/(\.[A-Za-z]+\?.*$)/)
43 | if (newUrl.length < 3) return url
44 |
45 | newUrl = `${newUrl[0]}_${width ?? ""}x${height ?? ""}${
46 | crop ? `_crop_${crop}` : ""
47 | }${scale ? `@${scale}x` : ""}${newUrl[1]}`
48 | return newUrl
49 | }
50 |
51 | export const getPaginatedItems = (
52 | items: T[],
53 | first: number,
54 | after?: number,
55 | ) => {
56 | const totalItems = items.length
57 |
58 | if (totalItems === 0) {
59 | return {
60 | data: [],
61 | pageInfo: {
62 | hasNextPage: false,
63 | hasPreviousPage: false,
64 | latestCursor: after || 0,
65 | },
66 | }
67 | }
68 |
69 | const startIndex = after ? items.findIndex((item) => item.cursor > after) : 0
70 |
71 | if (startIndex === -1) {
72 | return {
73 | data: [],
74 | pageInfo: {
75 | hasNextPage: false,
76 | hasPreviousPage: after !== undefined,
77 | latestCursor: after || 0,
78 | },
79 | }
80 | }
81 |
82 | const endIndex = Math.min(startIndex + first, totalItems)
83 | const data = items.slice(startIndex, endIndex)
84 |
85 | return {
86 | data,
87 | pageInfo: {
88 | hasNextPage: endIndex < totalItems,
89 | hasPreviousPage: startIndex > 0,
90 | endCursor: data.length > 0 ? data[data.length - 1].cursor : after || 0,
91 | },
92 | }
93 | }
94 |
95 | export const getPaginatedLibraryResources = ({
96 | resources,
97 | }: {
98 | resources: T[]
99 | }) => {
100 | const sortedResources = resources.sort((a, b) => a.cursor - b.cursor)
101 | return ({
102 | first,
103 | after,
104 | filters,
105 | query,
106 | }: {
107 | first: number
108 | after?: number
109 | filters: LibraryResourceFilters["filters"]
110 | query: string | null
111 | }) => {
112 | const lowercaseQuery = query?.toLowerCase()
113 | if (!sortedResources.length) {
114 | return {
115 | data: [],
116 | pageInfo: { hasNextPage: false, hasPreviousPage: false, latestCursor: after || 0 },
117 | }
118 | }
119 |
120 | const allFilteredResources = filters
121 | ? sortedResources.filter((r) => {
122 | return (
123 | R.keys(filters)
124 | .map((key) => {
125 | const f = filters[key]
126 | return f.length === 0 || f.every((el) => r[key]?.includes(el))
127 | })
128 | .every(Boolean) &&
129 | (lowercaseQuery ? r.title["en-US"].toLowerCase().includes(lowercaseQuery) : true)
130 | )
131 | })
132 | : sortedResources
133 |
134 | const totalAllFilteredResources = allFilteredResources.length
135 |
136 | if (!totalAllFilteredResources) {
137 | return {
138 | data: [],
139 | pageInfo: { hasNextPage: false, hasPreviousPage: false, latestCursor: after || 0 },
140 | }
141 | }
142 |
143 | const startIndex = after ? allFilteredResources.findIndex((r) => r.cursor > after) : 0
144 |
145 | if (startIndex === -1) {
146 | return {
147 | data: [],
148 | pageInfo: {
149 | hasNextPage: false,
150 | hasPreviousPage: after !== undefined,
151 | latestCursor: after || 0,
152 | },
153 | }
154 | }
155 |
156 | const endIndex = Math.min(startIndex + first, totalAllFilteredResources)
157 | const data = allFilteredResources.slice(startIndex, endIndex)
158 |
159 | const endCursor = data.length > 0 ? data[data.length - 1].cursor : after || 0
160 |
161 | return {
162 | data,
163 | pageInfo: {
164 | hasNextPage: endIndex < totalAllFilteredResources,
165 | hasPreviousPage: startIndex > 0,
166 | endCursor: endCursor,
167 | },
168 | }
169 | }
170 | }
171 |
172 | // Errors
173 | const defaultMessageError = "Something went wrong, this code is fked up"
174 | export const handleErrorMessage = (error: unknown): string => {
175 | if (typeof error === "string") return error
176 | if (typeof error === "object" && error != null) {
177 | // In case of HttpErrorClient
178 | if ("error" in error && Reflect.get(error, "error") != null) {
179 | return `${String(Reflect.get(error.error ?? {}, "message"))} type: ${String(
180 | Reflect.get(error.error ?? {}, "type"),
181 | )} code: ${String(Reflect.get(error.error ?? {}, "code"))}`
182 | }
183 | if ("message" in error) return String(Reflect.get(error, "message"))
184 | return defaultMessageError
185 | }
186 | return defaultMessageError
187 | }
188 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/tailwind.config.cjs:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: ["./src/**/*.{html,tsx,ts,jsx,js}"],
4 | theme: {
5 | extend: {},
6 | },
7 | plugins: [],
8 | }
--------------------------------------------------------------------------------
/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4 | "target": "ESNext",
5 | "useDefineForClassFields": true,
6 | "lib": ["ESNext", "DOM", "DOM.Iterable"],
7 | "module": "ESNext",
8 | "skipLibCheck": true,
9 |
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "isolatedModules": true,
13 | "moduleDetection": "force",
14 | "noEmit": true,
15 | "jsx": "react-jsx",
16 |
17 | "strict": true,
18 | "noUnusedLocals": true,
19 | "noUnusedParameters": true,
20 | "noFallthroughCasesInSwitch": true,
21 | "noUncheckedSideEffectImports": true
22 | },
23 | "include": ["src"]
24 | }
25 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | { "path": "./tsconfig.app.json" },
5 | { "path": "./tsconfig.node.json" }
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4 | "target": "ES2022",
5 | "lib": ["ES2023"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | "moduleResolution": "bundler",
10 | "allowImportingTsExtensions": true,
11 | "isolatedModules": true,
12 | "moduleDetection": "force",
13 | "noEmit": true,
14 |
15 | "strict": true,
16 | "noUnusedLocals": true,
17 | "noUnusedParameters": true,
18 | "noFallthroughCasesInSwitch": true,
19 | "noUncheckedSideEffectImports": true
20 | },
21 | "include": ["vite.config.ts"]
22 | }
23 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from "@vitejs/plugin-react"
2 | import { defineConfig } from "vite"
3 |
4 | // https://vite.dev/config/
5 | export default defineConfig({
6 | base: "/special-shopify-resource-picker/",
7 | plugins: [react()],
8 | })
9 |
--------------------------------------------------------------------------------