├── .github └── workflows │ └── publish.yml ├── .gitignore ├── README.md ├── build.ts ├── esbuild.js ├── example └── demo.ts ├── field-types.d.ts ├── package-lock.json ├── package.json ├── screenshots └── meta-object.png ├── src ├── index.ts ├── schemaHandler │ └── metaobject │ │ ├── MetaObjectDefinition.ts │ │ ├── client.ts │ │ ├── helper.ts │ │ └── index.ts └── types.ts └── tsconfig.json /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | release: 6 | description: 'major | minor | patch' 7 | required: true 8 | default: 'patch' 9 | type: choice 10 | options: 11 | - major 12 | - minor 13 | - patch 14 | jobs: 15 | publish-new-version: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout main 19 | uses: actions/checkout@v2 20 | - name: Use Node 21 | uses: actions/setup-node@v1 22 | with: 23 | node-version: '18' 24 | registry-url: https://registry.npmjs.org/ 25 | - name: Install dependencies 26 | run: yarn 27 | - name: Build 28 | run: yarn build 29 | - name: Publish New Version 30 | env: 31 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 32 | run: | 33 | yarn publish --access public -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [WIP] 🚧 🚀 Shopify MetaObject ORM 2 | This is a simple ORM for Shopify's MetaObject API. Which simplifies the process of creating, updating, and deleting metaobjects and its definitions. 3 | **_Note: This is still in development/alpha and not ready recommended for production use._** 4 | 5 | ### 🌟 Features 6 | - Migration Support: Migrate metaobject definitions to Shopify with ease. 7 | - Simplified MetaObject Management: Easily create, update, and delete Shopify metaobjects. 8 | - Efficient Schema Definitions: Define metaobject schemas with ease. 9 | - GraphQL Client Integration: Seamlessly works with Shopify's GraphQL client. 10 | - CRUD Operations Made Easy: Intuitive methods for all your CRUD needs. 11 | - Pagination Support: Supports pagination for listing metaobjects. 12 | 13 | ### Screenshots 14 | 15 | ![image](./screenshots/meta-object.png) 16 | 17 | 18 | ### 📖 Our Story 19 | Initially developed for internal use in our [apps](https://apps.shopify.com/partners/appzola), Shopify MetaObject ORM proved to be a game-changer in how we interacted with Shopify's MetaObject API. It not only enhanced our productivity but also brought an unmatched level of simplicity to complex tasks. 20 | 21 | The turning point came when [@blanklob](https://twitter.com/blanklob) [tweeted](https://twitter.com/blanklob/status/1729901729175515273) about the need for a simplified approach to managing Shopify's metaobjects. This tweet resonated with our experience, and we were inspired to contribute our solution to the community. 22 | 23 | A big shoutout to [@blanklob](https://twitter.com/blanklob) for the inspiration and to the vibrant Shopify developer community for their continuous support and feedback! 24 | 25 | 26 | 27 | ### 📦 Installation 28 | ```bash 29 | npm i shopify-orm 30 | ``` 31 | 32 | ### 🚀 Quick Start Guide 33 | 34 | Tune to [examples](./examples) for more examples. 35 | I used `bun` to run the examples. No typescript compilation is required. 36 | 37 | 38 | Create a graphql client using `@shopify/graphql-client` 39 | 40 | ```js 41 | import { createGraphQLClient } from "@shopify/graphql-client"; 42 | const client = createGraphQLClient({ 43 | url: "https://myshop.myshopify.com/admin/api/2023-10/graphql.json", 44 | headers: { 45 | "Content-Type": "application/json", 46 | "X-Shopify-Access-Token": "", 47 | }, 48 | retries: 1, 49 | }); 50 | ``` 51 | ### Import the ORM 52 | ```js 53 | import { metaobject, shopifyORM } from 'shopify-orm'; 54 | ``` 55 | 56 | Define your metaobject schema, In below example we are defining a `size_chart` metaobject with `name`, `config`, `products`, and `collections` fields. 57 | 58 | ```ts 59 | 60 | const sizeChart = metaobject( 61 | "size_chart", 62 | { 63 | name: { 64 | name: "name", 65 | type: "single_line_text_field", 66 | }, 67 | config: { 68 | name: "config", 69 | type: "json", 70 | }, 71 | products: { 72 | name: "products", 73 | type: "list.product_reference", 74 | }, 75 | collections: { 76 | name: "collections", 77 | type: "list.collection_reference", 78 | }, 79 | }, 80 | { 81 | displayNameKey: "name", 82 | capabilities: { 83 | publishable: { 84 | enabled: true, 85 | }, 86 | }, 87 | admin_access: "PUBLIC_READ_WRITE", 88 | access: { 89 | admin: "PUBLIC_READ_WRITE", 90 | storefront: "PUBLIC_READ", 91 | }, 92 | } 93 | ); 94 | ``` 95 | 96 | Create a db instance using the client 97 | 98 | ```ts 99 | const db = shopifyORM(client); 100 | ``` 101 | 102 | Create CRUD instances for your metaobjects 103 | 104 | ```ts 105 | const sizeChartSchema = db.metaobject(sizeChart); 106 | ``` 107 | 108 | ## Operations 109 | 110 | ### Migrate 111 | 112 | When you call migrate function, It will check the metaobject definition in shopify and update it if it is not matching with the local definition. If the metaobject is not present in shopify, It will create it. 113 | 114 | ```ts 115 | const resp = await sizeChartSchema.migrate(); 116 | ``` 117 | 118 | ### Create 119 | Create a new metaobject by passing data 120 | ```js 121 | const createdItems = await sizeChartSchema.create({ 122 | data: { 123 | name: "Jeans Size Chart", 124 | config: { 125 | size: ["S", "M", "L", "XL"], 126 | waist: ["28", "30", "32", "34"], 127 | }, 128 | products: ["gid://shopify/Product/8537198461244"], 129 | }, 130 | }); 131 | ``` 132 | 133 | ### List 134 | 135 | List items in `items` and `pageInfo` for additional information like `hasNextPage`, `hasPreviousPage`, `startCursor`, `endCursor`, `pageSize`. 136 | 137 | ```js 138 | const items = await sizeChartSchema.list({ 139 | first: 10, 140 | }); 141 | ``` 142 | 143 | ### Update 144 | Update metaobject by id and data 145 | ```js 146 | const item = await sizeChartSchema.update({ 147 | id: "gid://shopify/Metaobject/24441422140", 148 | data: { 149 | name: "Jeans Size Chart", 150 | config: { 151 | size: ["S", "M", "L", "XL"], 152 | waist: ["28", "30", "32", "34"], 153 | }, 154 | products: ["gid://shopify/Product/8537198461244"], 155 | }, 156 | }); 157 | ``` 158 | 159 | ### Get 160 | Get single item by id 161 | ```js 162 | const item = await sizeChartSchema.get({ 163 | id: "gid://shopify/Metaobject/24354980156", 164 | }); 165 | ``` 166 | 167 | ### Delete 168 | Delete single item by id 169 | ```js 170 | 171 | const deletedItem = await sizeChartSchema.delete({ 172 | id: "gid://shopify/Metaobject/24354980156", 173 | }); 174 | ``` 175 | 176 | ### 👥 Contributing 177 | We welcome contributions! Please see our contributing guidelines for more details. 178 | 179 | ### 📬 Feedback 180 | Your feedback is valuable to us. Please reach out with suggestions or issues. 181 | -------------------------------------------------------------------------------- /build.ts: -------------------------------------------------------------------------------- 1 | import {common, frontend} from '@shelf/esbuild-config'; 2 | 3 | // Call the functions with your options 4 | await common({ 5 | entryPoints: ['src/index.ts'], 6 | target: ['esnext', 'node16'], 7 | // ... 8 | }); -------------------------------------------------------------------------------- /esbuild.js: -------------------------------------------------------------------------------- 1 | const esbuild = require("esbuild"); 2 | const { nodeExternalsPlugin } = require("esbuild-node-externals"); 3 | esbuild 4 | .build({ 5 | entryPoints: ["./src/index.ts"], 6 | outfile: "dist/index.js", 7 | bundle: true, 8 | minify: true, 9 | treeShaking: true, 10 | platform: "node", 11 | format: "cjs", 12 | target: "node14", 13 | plugins: [nodeExternalsPlugin()], 14 | }) 15 | .catch(() => process.exit(1)); 16 | -------------------------------------------------------------------------------- /example/demo.ts: -------------------------------------------------------------------------------- 1 | import { createGraphQLClient } from '@shopify/graphql-client'; 2 | import { metaobject, shopifyORM } from '../src'; 3 | import type { OrmSchema } from '../types'; 4 | 5 | const client = createGraphQLClient({ 6 | url: 'https://.myshopify.com/admin/api/2023-10/graphql.json', 7 | headers: { 8 | 'Content-Type': 'application/json', 9 | 'X-Shopify-Access-Token': '', 10 | }, 11 | retries: 1 12 | }); 13 | 14 | const db = shopifyORM(client); 15 | const sizeChart: OrmSchema = metaobject('size_chart', { 16 | name: { 17 | name: 'name', 18 | type: 'single_line_text_field', 19 | }, 20 | config: { 21 | name: 'config', 22 | type: 'json' 23 | }, 24 | products: { 25 | name: 'products', 26 | type: 'list.product_reference', 27 | }, 28 | collections: { 29 | name: 'collections', 30 | type: 'list.collection_reference', 31 | } 32 | }, 33 | { 34 | displayNameKey: 'name', 35 | capabilities: { 36 | publishable: { 37 | enabled: true 38 | } 39 | }, 40 | admin_access: 'PUBLIC_READ_WRITE', 41 | access: { 42 | admin: 'PUBLIC_READ_WRITE', 43 | storefront: 'PUBLIC_READ' 44 | } 45 | }); 46 | async function main() { 47 | 48 | const sizeChartSchema = db.metaobject(sizeChart); 49 | const resp = await sizeChartSchema.migrate(); 50 | const createdItems = await sizeChartSchema.create({ 51 | data: { 52 | name: "Jeans Size Chart", 53 | config: { 54 | size: ["S", "M", "L", "XL"], 55 | waist: ["28", "30", "32", "34"], 56 | }, 57 | } 58 | }); 59 | console.log(createdItems); 60 | // return; 61 | const items = await sizeChartSchema.list({ 62 | first: 10 63 | }); 64 | 65 | console.log(items); 66 | 67 | 68 | } 69 | main(); -------------------------------------------------------------------------------- /field-types.d.ts: -------------------------------------------------------------------------------- 1 | 2 | type MetafieldReference = 3 | | CollectionReference 4 | | FileReference 5 | | MetaobjectReference 6 | | MixedReference 7 | | PageReference 8 | | ProductReference 9 | | VariantReference; 10 | type MetafieldListReference = 11 | | ListCollectionReference 12 | | ListColor 13 | | ListDate 14 | | ListDateTime 15 | | ListDimension 16 | | ListFileReference 17 | | ListMetaobjectReference 18 | | ListMixedReference 19 | | ListNumberInteger 20 | | ListNumberDecimal 21 | | ListPageReference 22 | | ListProductReference 23 | | ListRating 24 | | ListSingleLineTextField 25 | | ListURL 26 | | ListVariantReference 27 | | ListVolume 28 | | ListWeight; 29 | export type Metafield = 30 | | BooleanMetafield 31 | | ColorMetafield 32 | | DateMetafield 33 | | DateTimeMetafield 34 | | DimensionMetafield 35 | | JSONMetafield 36 | | MoneyMetafield 37 | | MultiLineTextField 38 | | NumberDecimalMetafield 39 | | NumberIntegerMetafield 40 | | RatingMetafield 41 | | RichTextField 42 | | SingleLineTextField 43 | | UrlMetafield 44 | | VolumeMetafield 45 | | WeightMetafield 46 | | MetafieldReference 47 | | MetafieldListReference; 48 | 49 | 50 | 51 | export interface BooleanMetafield { 52 | type: "boolean"; 53 | value: boolean; 54 | } 55 | 56 | export interface ColorMetafield { 57 | type: "color"; 58 | value: string; 59 | } 60 | 61 | export interface DateMetafield { 62 | type: "date"; 63 | value: string; 64 | } 65 | 66 | export interface DateTimeMetafield { 67 | type: "date_time"; 68 | value: string; 69 | } 70 | 71 | export interface DimensionMetafield { 72 | type: "dimension"; 73 | value: { 74 | value: number; 75 | unit: "in" | "ft" | "yd" | "mm" | "cm" | "m"; 76 | }; 77 | } 78 | 79 | export interface JSONMetafield { 80 | type: "json"; 81 | value: object | string | number | boolean | null; 82 | } 83 | 84 | export interface MoneyMetafield { 85 | type: "money"; 86 | value: { 87 | amount: string; 88 | currency_code: string; 89 | }; 90 | } 91 | 92 | export interface MultiLineTextField { 93 | type: "multi_line_text_field"; 94 | value: string; 95 | } 96 | 97 | export interface NumberDecimalMetafield { 98 | type: "number_decimal"; 99 | value: string; 100 | } 101 | 102 | export interface NumberIntegerMetafield { 103 | type: "number_integer"; 104 | value: number; 105 | } 106 | 107 | export interface RatingMetafield { 108 | type: "rating"; 109 | value: { 110 | value: string; 111 | scale_min: string; 112 | scale_max: string; 113 | }; 114 | } 115 | 116 | export interface RichTextField { 117 | type: "rich_text_field"; 118 | value: { 119 | type: "root"; 120 | children: any[]; // The structure here can be more detailed based on your needs 121 | }; 122 | } 123 | 124 | export interface SingleLineTextField { 125 | type: "single_line_text_field"; 126 | value: string; 127 | } 128 | 129 | export interface UrlMetafield { 130 | type: "url"; 131 | value: string; 132 | } 133 | 134 | export interface VolumeMetafield { 135 | type: "volume"; 136 | value: { 137 | value: number; 138 | unit: "ml" | "cl" | "l" | "m3" | "us_fl_oz" | "us_pt" | "us_qt" | "us_gal" | "imp_fl_oz" | "imp_pt" | "imp_qt" | "imp_gal"; 139 | }; 140 | } 141 | 142 | export interface WeightMetafield { 143 | type: "weight"; 144 | value: { 145 | value: number; 146 | unit: "oz" | "lb" | "g" | "kg"; 147 | }; 148 | } 149 | 150 | export interface CollectionReference { 151 | type: "collection_reference"; 152 | value: string; // e.g., "gid://shopify/Collection/1" 153 | } 154 | 155 | export interface FileReference { 156 | type: "file_reference"; 157 | value: string; // e.g., "gid://shopify/MediaImage/123" 158 | } 159 | 160 | export interface MetaobjectReference { 161 | type: "metaobject_reference"; 162 | value: string; // e.g., "gid://shopify/Metaobject/123" 163 | } 164 | 165 | export interface MixedReference { 166 | type: "mixed_reference"; 167 | value: string; // e.g., "gid://shopify/Metaobject/123" 168 | } 169 | 170 | export interface PageReference { 171 | type: "page_reference"; 172 | value: string; // e.g., "gid://shopify/OnlineStorePage/1" 173 | } 174 | 175 | export interface ProductReference { 176 | type: "product_reference"; 177 | value: string; // e.g., "gid://shopify/Product/1" 178 | } 179 | 180 | export interface VariantReference { 181 | type: "variant_reference"; 182 | value: string; // e.g., "gid://shopify/ProductVariant/1" 183 | } 184 | 185 | export interface ListCollectionReference { 186 | type: "list.collection_reference"; 187 | value: string[]; 188 | } 189 | 190 | export interface ListColor { 191 | type: "list.color"; 192 | value: string[]; 193 | } 194 | 195 | export interface ListDate { 196 | type: "list.date"; 197 | value: string[]; 198 | } 199 | 200 | export interface ListDateTime { 201 | type: "list.date_time"; 202 | value: string[]; 203 | } 204 | 205 | export interface Dimension { 206 | value: number; 207 | unit: "in" | "ft" | "yd" | "mm" | "cm" | "m"; 208 | } 209 | 210 | export interface ListDimension { 211 | type: "list.dimension"; 212 | value: Dimension[]; 213 | } 214 | 215 | export interface ListFileReference { 216 | type: "list.file_reference"; 217 | value: string[]; 218 | } 219 | 220 | export interface ListMetaobjectReference { 221 | type: "list.metaobject_reference"; 222 | value: string[]; 223 | } 224 | 225 | export interface ListMixedReference { 226 | type: "list.mixed_reference"; 227 | value: string[]; 228 | } 229 | 230 | export interface ListNumberInteger { 231 | type: "list.number_integer"; 232 | value: number[]; 233 | } 234 | 235 | export interface ListNumberDecimal { 236 | type: "list.number_decimal"; 237 | value: number[]; 238 | } 239 | 240 | export interface ListPageReference { 241 | type: "list.page_reference"; 242 | value: string[]; 243 | } 244 | 245 | export interface ListProductReference { 246 | type: "list.product_reference"; 247 | value: string[]; 248 | } 249 | 250 | export interface Rating { 251 | value: string; 252 | scale_min: string; 253 | scale_max: string; 254 | } 255 | 256 | export interface ListRating { 257 | type: "list.rating"; 258 | value: Rating[]; 259 | } 260 | 261 | export interface ListSingleLineTextField { 262 | type: "list.single_line_text_field"; 263 | value: string[]; 264 | } 265 | 266 | export interface ListURL { 267 | type: "list.url"; 268 | value: string[]; 269 | } 270 | 271 | export interface ListVariantReference { 272 | type: "list.variant_reference"; 273 | value: string[]; 274 | } 275 | 276 | export interface Volume { 277 | value: number; 278 | unit: "ml" | "cl" | "l" | "m3" | "us_fl_oz" | "us_pt" | "us_qt" | "us_gal" | "imp_fl_oz" | "imp_pt" | "imp_qt" | "imp_gal"; 279 | } 280 | 281 | export interface ListVolume { 282 | type: "list.volume"; 283 | value: Volume[]; 284 | } 285 | 286 | export interface Weight { 287 | value: number; 288 | unit: "oz" | "lb" | "g" | "kg"; 289 | } 290 | 291 | export interface ListWeight { 292 | type: "list.weight"; 293 | value: Weight[]; 294 | } 295 | export type AdminAccess = 296 | | "PRIVATE" 297 | | "MERCHANT_READ" 298 | | "MERCHANT_READ_WRITE" 299 | | "PUBLIC_READ" 300 | | "PUBLIC_READ_WRITE"; 301 | export interface AccessDefinition { 302 | admin: AdminAccess; 303 | storefront: "PUBLIC_READ" | 'NONE'; // You can add more types if needed 304 | } 305 | export interface PublishableCapabilities { 306 | enabled: boolean; 307 | } 308 | 309 | export interface CapabilitiesDefinition { 310 | publishable: PublishableCapabilities; 311 | } 312 | export type TextValidation = { 313 | type: 'minimum_length' | 'maximum_length' | 'regular_expression'; 314 | value: string; 315 | }; 316 | 317 | export type UrlValidation = { 318 | type: 'allowed_domains'; 319 | value: string; 320 | }; 321 | 322 | export type ChoiceValidation = { 323 | type: 'choices'; 324 | value: string; 325 | }; 326 | 327 | export type FileTypeValidation = { 328 | type: 'file_type_options'; 329 | value: ('Image' | 'Video'); 330 | }; 331 | 332 | export type NumericValidation = { 333 | type: 'maximum_precision' | 'minimum_integer' | 'maximum_integer' | 'minimum_decimal' | 'maximum_decimal'; 334 | value: string; 335 | }; 336 | 337 | export type DateValidation = { 338 | type: 'minimum_date' | 'maximum_date' | 'minimum_datetime' | 'maximum_datetime'; 339 | value: string; // ISO 8601 format 340 | }; 341 | 342 | export type WeightValidation = { 343 | type: 'minimum_weight' | 'maximum_weight'; 344 | value: string; 345 | }; 346 | 347 | export type VolumeValidation = { 348 | type: 'minimum_volume' | 'maximum_volume'; 349 | value: string; 350 | }; 351 | 352 | export type DimensionValidation = { 353 | type: 'minimum_dimension' | 'maximum_dimension'; 354 | value: string; 355 | }; 356 | 357 | export type ReferenceValidation = { 358 | type: 'metaobject_definition' | 'multiple_metaobject_definitions'; 359 | value: string; // array of metaobject definitions 360 | }; 361 | 362 | export type JsonValidation = { 363 | type: 'json_schema'; 364 | value: string; // You might want to use a specific schema export type if you have one defined 365 | }; 366 | 367 | export type Validation = 368 | TextValidation | UrlValidation | ChoiceValidation | FileTypeValidation | NumericValidation | 369 | DateValidation | WeightValidation | VolumeValidation | DimensionValidation | 370 | ReferenceValidation | JsonValidation; 371 | 372 | export interface MetaObjectDefinition { 373 | id?: string; // Keeping 'id' optional since it's not present in the provided object 374 | type: string; 375 | access: AccessDefinition; 376 | capabilities: CapabilitiesDefinition; 377 | fieldDefinitions: FieldDefinition[]; 378 | displayNameKey?: string; 379 | } 380 | export type FieldDefinitionChange = { 381 | create?: FieldDefinition; 382 | update?: FieldDefinition; 383 | delete?: { key: string }; 384 | }; 385 | export type SchemaChanges = { 386 | fieldDefinitions: FieldDefinitionChange[]; 387 | } 388 | export type MetaObjectDefinitionUpdateInput = { 389 | type?: string; 390 | access?: AccessDefinition; 391 | capabilities?: CapabilitiesDefinition; 392 | displayNameKey?: string; 393 | } & SchemaChanges 394 | 395 | export interface FieldDefinition { 396 | key: string; 397 | name: string; // Adding 'name' as it's present in the provided object 398 | type: Metafield["type"]; 399 | validations?: Validation[]; 400 | } -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shopify-orm", 3 | "version": "0.0.1", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "shopify-orm", 9 | "version": "0.0.1", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@shopify/graphql-client": "^0.8.0", 13 | "json-to-graphql-query": "^2.2.5" 14 | }, 15 | "devDependencies": { 16 | "@shelf/esbuild-config": "^0.2.0", 17 | "esbuild": "^0.19.8", 18 | "esbuild-node-externals": "^1.11.0" 19 | } 20 | }, 21 | "node_modules/@esbuild/android-arm": { 22 | "version": "0.19.8", 23 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.8.tgz", 24 | "integrity": "sha512-31E2lxlGM1KEfivQl8Yf5aYU/mflz9g06H6S15ITUFQueMFtFjESRMoDSkvMo8thYvLBax+VKTPlpnx+sPicOA==", 25 | "cpu": [ 26 | "arm" 27 | ], 28 | "dev": true, 29 | "optional": true, 30 | "os": [ 31 | "android" 32 | ], 33 | "engines": { 34 | "node": ">=12" 35 | } 36 | }, 37 | "node_modules/@esbuild/android-arm64": { 38 | "version": "0.19.8", 39 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.8.tgz", 40 | "integrity": "sha512-B8JbS61bEunhfx8kasogFENgQfr/dIp+ggYXwTqdbMAgGDhRa3AaPpQMuQU0rNxDLECj6FhDzk1cF9WHMVwrtA==", 41 | "cpu": [ 42 | "arm64" 43 | ], 44 | "dev": true, 45 | "optional": true, 46 | "os": [ 47 | "android" 48 | ], 49 | "engines": { 50 | "node": ">=12" 51 | } 52 | }, 53 | "node_modules/@esbuild/android-x64": { 54 | "version": "0.19.8", 55 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.8.tgz", 56 | "integrity": "sha512-rdqqYfRIn4jWOp+lzQttYMa2Xar3OK9Yt2fhOhzFXqg0rVWEfSclJvZq5fZslnz6ypHvVf3CT7qyf0A5pM682A==", 57 | "cpu": [ 58 | "x64" 59 | ], 60 | "dev": true, 61 | "optional": true, 62 | "os": [ 63 | "android" 64 | ], 65 | "engines": { 66 | "node": ">=12" 67 | } 68 | }, 69 | "node_modules/@esbuild/darwin-arm64": { 70 | "version": "0.19.8", 71 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.8.tgz", 72 | "integrity": "sha512-RQw9DemMbIq35Bprbboyf8SmOr4UXsRVxJ97LgB55VKKeJOOdvsIPy0nFyF2l8U+h4PtBx/1kRf0BelOYCiQcw==", 73 | "cpu": [ 74 | "arm64" 75 | ], 76 | "dev": true, 77 | "optional": true, 78 | "os": [ 79 | "darwin" 80 | ], 81 | "engines": { 82 | "node": ">=12" 83 | } 84 | }, 85 | "node_modules/@esbuild/darwin-x64": { 86 | "version": "0.19.8", 87 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.8.tgz", 88 | "integrity": "sha512-3sur80OT9YdeZwIVgERAysAbwncom7b4bCI2XKLjMfPymTud7e/oY4y+ci1XVp5TfQp/bppn7xLw1n/oSQY3/Q==", 89 | "cpu": [ 90 | "x64" 91 | ], 92 | "dev": true, 93 | "optional": true, 94 | "os": [ 95 | "darwin" 96 | ], 97 | "engines": { 98 | "node": ">=12" 99 | } 100 | }, 101 | "node_modules/@esbuild/freebsd-arm64": { 102 | "version": "0.19.8", 103 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.8.tgz", 104 | "integrity": "sha512-WAnPJSDattvS/XtPCTj1tPoTxERjcTpH6HsMr6ujTT+X6rylVe8ggxk8pVxzf5U1wh5sPODpawNicF5ta/9Tmw==", 105 | "cpu": [ 106 | "arm64" 107 | ], 108 | "dev": true, 109 | "optional": true, 110 | "os": [ 111 | "freebsd" 112 | ], 113 | "engines": { 114 | "node": ">=12" 115 | } 116 | }, 117 | "node_modules/@esbuild/freebsd-x64": { 118 | "version": "0.19.8", 119 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.8.tgz", 120 | "integrity": "sha512-ICvZyOplIjmmhjd6mxi+zxSdpPTKFfyPPQMQTK/w+8eNK6WV01AjIztJALDtwNNfFhfZLux0tZLC+U9nSyA5Zg==", 121 | "cpu": [ 122 | "x64" 123 | ], 124 | "dev": true, 125 | "optional": true, 126 | "os": [ 127 | "freebsd" 128 | ], 129 | "engines": { 130 | "node": ">=12" 131 | } 132 | }, 133 | "node_modules/@esbuild/linux-arm": { 134 | "version": "0.19.8", 135 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.8.tgz", 136 | "integrity": "sha512-H4vmI5PYqSvosPaTJuEppU9oz1dq2A7Mr2vyg5TF9Ga+3+MGgBdGzcyBP7qK9MrwFQZlvNyJrvz6GuCaj3OukQ==", 137 | "cpu": [ 138 | "arm" 139 | ], 140 | "dev": true, 141 | "optional": true, 142 | "os": [ 143 | "linux" 144 | ], 145 | "engines": { 146 | "node": ">=12" 147 | } 148 | }, 149 | "node_modules/@esbuild/linux-arm64": { 150 | "version": "0.19.8", 151 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.8.tgz", 152 | "integrity": "sha512-z1zMZivxDLHWnyGOctT9JP70h0beY54xDDDJt4VpTX+iwA77IFsE1vCXWmprajJGa+ZYSqkSbRQ4eyLCpCmiCQ==", 153 | "cpu": [ 154 | "arm64" 155 | ], 156 | "dev": true, 157 | "optional": true, 158 | "os": [ 159 | "linux" 160 | ], 161 | "engines": { 162 | "node": ">=12" 163 | } 164 | }, 165 | "node_modules/@esbuild/linux-ia32": { 166 | "version": "0.19.8", 167 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.8.tgz", 168 | "integrity": "sha512-1a8suQiFJmZz1khm/rDglOc8lavtzEMRo0v6WhPgxkrjcU0LkHj+TwBrALwoz/OtMExvsqbbMI0ChyelKabSvQ==", 169 | "cpu": [ 170 | "ia32" 171 | ], 172 | "dev": true, 173 | "optional": true, 174 | "os": [ 175 | "linux" 176 | ], 177 | "engines": { 178 | "node": ">=12" 179 | } 180 | }, 181 | "node_modules/@esbuild/linux-loong64": { 182 | "version": "0.19.8", 183 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.8.tgz", 184 | "integrity": "sha512-fHZWS2JJxnXt1uYJsDv9+b60WCc2RlvVAy1F76qOLtXRO+H4mjt3Tr6MJ5l7Q78X8KgCFudnTuiQRBhULUyBKQ==", 185 | "cpu": [ 186 | "loong64" 187 | ], 188 | "dev": true, 189 | "optional": true, 190 | "os": [ 191 | "linux" 192 | ], 193 | "engines": { 194 | "node": ">=12" 195 | } 196 | }, 197 | "node_modules/@esbuild/linux-mips64el": { 198 | "version": "0.19.8", 199 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.8.tgz", 200 | "integrity": "sha512-Wy/z0EL5qZYLX66dVnEg9riiwls5IYnziwuju2oUiuxVc+/edvqXa04qNtbrs0Ukatg5HEzqT94Zs7J207dN5Q==", 201 | "cpu": [ 202 | "mips64el" 203 | ], 204 | "dev": true, 205 | "optional": true, 206 | "os": [ 207 | "linux" 208 | ], 209 | "engines": { 210 | "node": ">=12" 211 | } 212 | }, 213 | "node_modules/@esbuild/linux-ppc64": { 214 | "version": "0.19.8", 215 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.8.tgz", 216 | "integrity": "sha512-ETaW6245wK23YIEufhMQ3HSeHO7NgsLx8gygBVldRHKhOlD1oNeNy/P67mIh1zPn2Hr2HLieQrt6tWrVwuqrxg==", 217 | "cpu": [ 218 | "ppc64" 219 | ], 220 | "dev": true, 221 | "optional": true, 222 | "os": [ 223 | "linux" 224 | ], 225 | "engines": { 226 | "node": ">=12" 227 | } 228 | }, 229 | "node_modules/@esbuild/linux-riscv64": { 230 | "version": "0.19.8", 231 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.8.tgz", 232 | "integrity": "sha512-T2DRQk55SgoleTP+DtPlMrxi/5r9AeFgkhkZ/B0ap99zmxtxdOixOMI570VjdRCs9pE4Wdkz7JYrsPvsl7eESg==", 233 | "cpu": [ 234 | "riscv64" 235 | ], 236 | "dev": true, 237 | "optional": true, 238 | "os": [ 239 | "linux" 240 | ], 241 | "engines": { 242 | "node": ">=12" 243 | } 244 | }, 245 | "node_modules/@esbuild/linux-s390x": { 246 | "version": "0.19.8", 247 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.8.tgz", 248 | "integrity": "sha512-NPxbdmmo3Bk7mbNeHmcCd7R7fptJaczPYBaELk6NcXxy7HLNyWwCyDJ/Xx+/YcNH7Im5dHdx9gZ5xIwyliQCbg==", 249 | "cpu": [ 250 | "s390x" 251 | ], 252 | "dev": true, 253 | "optional": true, 254 | "os": [ 255 | "linux" 256 | ], 257 | "engines": { 258 | "node": ">=12" 259 | } 260 | }, 261 | "node_modules/@esbuild/linux-x64": { 262 | "version": "0.19.8", 263 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.8.tgz", 264 | "integrity": "sha512-lytMAVOM3b1gPypL2TRmZ5rnXl7+6IIk8uB3eLsV1JwcizuolblXRrc5ShPrO9ls/b+RTp+E6gbsuLWHWi2zGg==", 265 | "cpu": [ 266 | "x64" 267 | ], 268 | "dev": true, 269 | "optional": true, 270 | "os": [ 271 | "linux" 272 | ], 273 | "engines": { 274 | "node": ">=12" 275 | } 276 | }, 277 | "node_modules/@esbuild/netbsd-x64": { 278 | "version": "0.19.8", 279 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.8.tgz", 280 | "integrity": "sha512-hvWVo2VsXz/8NVt1UhLzxwAfo5sioj92uo0bCfLibB0xlOmimU/DeAEsQILlBQvkhrGjamP0/el5HU76HAitGw==", 281 | "cpu": [ 282 | "x64" 283 | ], 284 | "dev": true, 285 | "optional": true, 286 | "os": [ 287 | "netbsd" 288 | ], 289 | "engines": { 290 | "node": ">=12" 291 | } 292 | }, 293 | "node_modules/@esbuild/openbsd-x64": { 294 | "version": "0.19.8", 295 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.8.tgz", 296 | "integrity": "sha512-/7Y7u77rdvmGTxR83PgaSvSBJCC2L3Kb1M/+dmSIvRvQPXXCuC97QAwMugBNG0yGcbEGfFBH7ojPzAOxfGNkwQ==", 297 | "cpu": [ 298 | "x64" 299 | ], 300 | "dev": true, 301 | "optional": true, 302 | "os": [ 303 | "openbsd" 304 | ], 305 | "engines": { 306 | "node": ">=12" 307 | } 308 | }, 309 | "node_modules/@esbuild/sunos-x64": { 310 | "version": "0.19.8", 311 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.8.tgz", 312 | "integrity": "sha512-9Lc4s7Oi98GqFA4HzA/W2JHIYfnXbUYgekUP/Sm4BG9sfLjyv6GKKHKKVs83SMicBF2JwAX6A1PuOLMqpD001w==", 313 | "cpu": [ 314 | "x64" 315 | ], 316 | "dev": true, 317 | "optional": true, 318 | "os": [ 319 | "sunos" 320 | ], 321 | "engines": { 322 | "node": ">=12" 323 | } 324 | }, 325 | "node_modules/@esbuild/win32-arm64": { 326 | "version": "0.19.8", 327 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.8.tgz", 328 | "integrity": "sha512-rq6WzBGjSzihI9deW3fC2Gqiak68+b7qo5/3kmB6Gvbh/NYPA0sJhrnp7wgV4bNwjqM+R2AApXGxMO7ZoGhIJg==", 329 | "cpu": [ 330 | "arm64" 331 | ], 332 | "dev": true, 333 | "optional": true, 334 | "os": [ 335 | "win32" 336 | ], 337 | "engines": { 338 | "node": ">=12" 339 | } 340 | }, 341 | "node_modules/@esbuild/win32-ia32": { 342 | "version": "0.19.8", 343 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.8.tgz", 344 | "integrity": "sha512-AIAbverbg5jMvJznYiGhrd3sumfwWs8572mIJL5NQjJa06P8KfCPWZQ0NwZbPQnbQi9OWSZhFVSUWjjIrn4hSw==", 345 | "cpu": [ 346 | "ia32" 347 | ], 348 | "dev": true, 349 | "optional": true, 350 | "os": [ 351 | "win32" 352 | ], 353 | "engines": { 354 | "node": ">=12" 355 | } 356 | }, 357 | "node_modules/@esbuild/win32-x64": { 358 | "version": "0.19.8", 359 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.8.tgz", 360 | "integrity": "sha512-bfZ0cQ1uZs2PqpulNL5j/3w+GDhP36k1K5c38QdQg+Swy51jFZWWeIkteNsufkQxp986wnqRRsb/bHbY1WQ7TA==", 361 | "cpu": [ 362 | "x64" 363 | ], 364 | "dev": true, 365 | "optional": true, 366 | "os": [ 367 | "win32" 368 | ], 369 | "engines": { 370 | "node": ">=12" 371 | } 372 | }, 373 | "node_modules/@shelf/esbuild-config": { 374 | "version": "0.2.0", 375 | "resolved": "https://registry.npmjs.org/@shelf/esbuild-config/-/esbuild-config-0.2.0.tgz", 376 | "integrity": "sha512-i6Wc9sq317kEmrN/bVnoHFqiBW7YKsOD21u9OJtfxwpNrVnCsOoxOivgQLWbN2mHgeQ9oYGn4zfjr6aDyYpDOg==", 377 | "dev": true, 378 | "dependencies": { 379 | "esbuild": "0.17.19", 380 | "esbuild-sass-plugin": "2.9.0" 381 | }, 382 | "engines": { 383 | "node": ">=16" 384 | } 385 | }, 386 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/android-arm": { 387 | "version": "0.17.19", 388 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", 389 | "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", 390 | "cpu": [ 391 | "arm" 392 | ], 393 | "dev": true, 394 | "optional": true, 395 | "os": [ 396 | "android" 397 | ], 398 | "engines": { 399 | "node": ">=12" 400 | } 401 | }, 402 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/android-arm64": { 403 | "version": "0.17.19", 404 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", 405 | "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", 406 | "cpu": [ 407 | "arm64" 408 | ], 409 | "dev": true, 410 | "optional": true, 411 | "os": [ 412 | "android" 413 | ], 414 | "engines": { 415 | "node": ">=12" 416 | } 417 | }, 418 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/android-x64": { 419 | "version": "0.17.19", 420 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", 421 | "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", 422 | "cpu": [ 423 | "x64" 424 | ], 425 | "dev": true, 426 | "optional": true, 427 | "os": [ 428 | "android" 429 | ], 430 | "engines": { 431 | "node": ">=12" 432 | } 433 | }, 434 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/darwin-arm64": { 435 | "version": "0.17.19", 436 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", 437 | "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", 438 | "cpu": [ 439 | "arm64" 440 | ], 441 | "dev": true, 442 | "optional": true, 443 | "os": [ 444 | "darwin" 445 | ], 446 | "engines": { 447 | "node": ">=12" 448 | } 449 | }, 450 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/darwin-x64": { 451 | "version": "0.17.19", 452 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", 453 | "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", 454 | "cpu": [ 455 | "x64" 456 | ], 457 | "dev": true, 458 | "optional": true, 459 | "os": [ 460 | "darwin" 461 | ], 462 | "engines": { 463 | "node": ">=12" 464 | } 465 | }, 466 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/freebsd-arm64": { 467 | "version": "0.17.19", 468 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", 469 | "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", 470 | "cpu": [ 471 | "arm64" 472 | ], 473 | "dev": true, 474 | "optional": true, 475 | "os": [ 476 | "freebsd" 477 | ], 478 | "engines": { 479 | "node": ">=12" 480 | } 481 | }, 482 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/freebsd-x64": { 483 | "version": "0.17.19", 484 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", 485 | "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", 486 | "cpu": [ 487 | "x64" 488 | ], 489 | "dev": true, 490 | "optional": true, 491 | "os": [ 492 | "freebsd" 493 | ], 494 | "engines": { 495 | "node": ">=12" 496 | } 497 | }, 498 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/linux-arm": { 499 | "version": "0.17.19", 500 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", 501 | "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", 502 | "cpu": [ 503 | "arm" 504 | ], 505 | "dev": true, 506 | "optional": true, 507 | "os": [ 508 | "linux" 509 | ], 510 | "engines": { 511 | "node": ">=12" 512 | } 513 | }, 514 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/linux-arm64": { 515 | "version": "0.17.19", 516 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", 517 | "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", 518 | "cpu": [ 519 | "arm64" 520 | ], 521 | "dev": true, 522 | "optional": true, 523 | "os": [ 524 | "linux" 525 | ], 526 | "engines": { 527 | "node": ">=12" 528 | } 529 | }, 530 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/linux-ia32": { 531 | "version": "0.17.19", 532 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", 533 | "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", 534 | "cpu": [ 535 | "ia32" 536 | ], 537 | "dev": true, 538 | "optional": true, 539 | "os": [ 540 | "linux" 541 | ], 542 | "engines": { 543 | "node": ">=12" 544 | } 545 | }, 546 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/linux-loong64": { 547 | "version": "0.17.19", 548 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", 549 | "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", 550 | "cpu": [ 551 | "loong64" 552 | ], 553 | "dev": true, 554 | "optional": true, 555 | "os": [ 556 | "linux" 557 | ], 558 | "engines": { 559 | "node": ">=12" 560 | } 561 | }, 562 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/linux-mips64el": { 563 | "version": "0.17.19", 564 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", 565 | "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", 566 | "cpu": [ 567 | "mips64el" 568 | ], 569 | "dev": true, 570 | "optional": true, 571 | "os": [ 572 | "linux" 573 | ], 574 | "engines": { 575 | "node": ">=12" 576 | } 577 | }, 578 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/linux-ppc64": { 579 | "version": "0.17.19", 580 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", 581 | "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", 582 | "cpu": [ 583 | "ppc64" 584 | ], 585 | "dev": true, 586 | "optional": true, 587 | "os": [ 588 | "linux" 589 | ], 590 | "engines": { 591 | "node": ">=12" 592 | } 593 | }, 594 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/linux-riscv64": { 595 | "version": "0.17.19", 596 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", 597 | "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", 598 | "cpu": [ 599 | "riscv64" 600 | ], 601 | "dev": true, 602 | "optional": true, 603 | "os": [ 604 | "linux" 605 | ], 606 | "engines": { 607 | "node": ">=12" 608 | } 609 | }, 610 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/linux-s390x": { 611 | "version": "0.17.19", 612 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", 613 | "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", 614 | "cpu": [ 615 | "s390x" 616 | ], 617 | "dev": true, 618 | "optional": true, 619 | "os": [ 620 | "linux" 621 | ], 622 | "engines": { 623 | "node": ">=12" 624 | } 625 | }, 626 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/linux-x64": { 627 | "version": "0.17.19", 628 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", 629 | "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", 630 | "cpu": [ 631 | "x64" 632 | ], 633 | "dev": true, 634 | "optional": true, 635 | "os": [ 636 | "linux" 637 | ], 638 | "engines": { 639 | "node": ">=12" 640 | } 641 | }, 642 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/netbsd-x64": { 643 | "version": "0.17.19", 644 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", 645 | "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", 646 | "cpu": [ 647 | "x64" 648 | ], 649 | "dev": true, 650 | "optional": true, 651 | "os": [ 652 | "netbsd" 653 | ], 654 | "engines": { 655 | "node": ">=12" 656 | } 657 | }, 658 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/openbsd-x64": { 659 | "version": "0.17.19", 660 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", 661 | "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", 662 | "cpu": [ 663 | "x64" 664 | ], 665 | "dev": true, 666 | "optional": true, 667 | "os": [ 668 | "openbsd" 669 | ], 670 | "engines": { 671 | "node": ">=12" 672 | } 673 | }, 674 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/sunos-x64": { 675 | "version": "0.17.19", 676 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", 677 | "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", 678 | "cpu": [ 679 | "x64" 680 | ], 681 | "dev": true, 682 | "optional": true, 683 | "os": [ 684 | "sunos" 685 | ], 686 | "engines": { 687 | "node": ">=12" 688 | } 689 | }, 690 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/win32-arm64": { 691 | "version": "0.17.19", 692 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", 693 | "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", 694 | "cpu": [ 695 | "arm64" 696 | ], 697 | "dev": true, 698 | "optional": true, 699 | "os": [ 700 | "win32" 701 | ], 702 | "engines": { 703 | "node": ">=12" 704 | } 705 | }, 706 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/win32-ia32": { 707 | "version": "0.17.19", 708 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", 709 | "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", 710 | "cpu": [ 711 | "ia32" 712 | ], 713 | "dev": true, 714 | "optional": true, 715 | "os": [ 716 | "win32" 717 | ], 718 | "engines": { 719 | "node": ">=12" 720 | } 721 | }, 722 | "node_modules/@shelf/esbuild-config/node_modules/@esbuild/win32-x64": { 723 | "version": "0.17.19", 724 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", 725 | "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", 726 | "cpu": [ 727 | "x64" 728 | ], 729 | "dev": true, 730 | "optional": true, 731 | "os": [ 732 | "win32" 733 | ], 734 | "engines": { 735 | "node": ">=12" 736 | } 737 | }, 738 | "node_modules/@shelf/esbuild-config/node_modules/esbuild": { 739 | "version": "0.17.19", 740 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", 741 | "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", 742 | "dev": true, 743 | "hasInstallScript": true, 744 | "bin": { 745 | "esbuild": "bin/esbuild" 746 | }, 747 | "engines": { 748 | "node": ">=12" 749 | }, 750 | "optionalDependencies": { 751 | "@esbuild/android-arm": "0.17.19", 752 | "@esbuild/android-arm64": "0.17.19", 753 | "@esbuild/android-x64": "0.17.19", 754 | "@esbuild/darwin-arm64": "0.17.19", 755 | "@esbuild/darwin-x64": "0.17.19", 756 | "@esbuild/freebsd-arm64": "0.17.19", 757 | "@esbuild/freebsd-x64": "0.17.19", 758 | "@esbuild/linux-arm": "0.17.19", 759 | "@esbuild/linux-arm64": "0.17.19", 760 | "@esbuild/linux-ia32": "0.17.19", 761 | "@esbuild/linux-loong64": "0.17.19", 762 | "@esbuild/linux-mips64el": "0.17.19", 763 | "@esbuild/linux-ppc64": "0.17.19", 764 | "@esbuild/linux-riscv64": "0.17.19", 765 | "@esbuild/linux-s390x": "0.17.19", 766 | "@esbuild/linux-x64": "0.17.19", 767 | "@esbuild/netbsd-x64": "0.17.19", 768 | "@esbuild/openbsd-x64": "0.17.19", 769 | "@esbuild/sunos-x64": "0.17.19", 770 | "@esbuild/win32-arm64": "0.17.19", 771 | "@esbuild/win32-ia32": "0.17.19", 772 | "@esbuild/win32-x64": "0.17.19" 773 | } 774 | }, 775 | "node_modules/@shelf/esbuild-config/node_modules/esbuild-sass-plugin": { 776 | "version": "2.9.0", 777 | "resolved": "https://registry.npmjs.org/esbuild-sass-plugin/-/esbuild-sass-plugin-2.9.0.tgz", 778 | "integrity": "sha512-D7c8Ub+C4RfaqhOoo9EEWprYmP5ZCmpmcodmYDtpvCfPZLgsSbLaLHPXdul4TUWUMH5eJ6POw+aes/s42ffwrA==", 779 | "dev": true, 780 | "dependencies": { 781 | "resolve": "^1.22.2", 782 | "sass": "^1.62.0" 783 | }, 784 | "peerDependencies": { 785 | "esbuild": "^0.17.17" 786 | } 787 | }, 788 | "node_modules/@shopify/graphql-client": { 789 | "version": "0.8.0", 790 | "resolved": "https://registry.npmjs.org/@shopify/graphql-client/-/graphql-client-0.8.0.tgz", 791 | "integrity": "sha512-YtEwYVUzV5n1fyQoc3Rsd5Y1F8OWP27dVe8/gQ3xNy7MLlRIOd3uT9/2LagUNM2cGw3tSrc8d35XG/cKFnCVyg==" 792 | }, 793 | "node_modules/anymatch": { 794 | "version": "3.1.3", 795 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 796 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 797 | "dev": true, 798 | "dependencies": { 799 | "normalize-path": "^3.0.0", 800 | "picomatch": "^2.0.4" 801 | }, 802 | "engines": { 803 | "node": ">= 8" 804 | } 805 | }, 806 | "node_modules/binary-extensions": { 807 | "version": "2.2.0", 808 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 809 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 810 | "dev": true, 811 | "engines": { 812 | "node": ">=8" 813 | } 814 | }, 815 | "node_modules/braces": { 816 | "version": "3.0.2", 817 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 818 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 819 | "dev": true, 820 | "dependencies": { 821 | "fill-range": "^7.0.1" 822 | }, 823 | "engines": { 824 | "node": ">=8" 825 | } 826 | }, 827 | "node_modules/chokidar": { 828 | "version": "3.5.3", 829 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 830 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 831 | "dev": true, 832 | "funding": [ 833 | { 834 | "type": "individual", 835 | "url": "https://paulmillr.com/funding/" 836 | } 837 | ], 838 | "dependencies": { 839 | "anymatch": "~3.1.2", 840 | "braces": "~3.0.2", 841 | "glob-parent": "~5.1.2", 842 | "is-binary-path": "~2.1.0", 843 | "is-glob": "~4.0.1", 844 | "normalize-path": "~3.0.0", 845 | "readdirp": "~3.6.0" 846 | }, 847 | "engines": { 848 | "node": ">= 8.10.0" 849 | }, 850 | "optionalDependencies": { 851 | "fsevents": "~2.3.2" 852 | } 853 | }, 854 | "node_modules/esbuild": { 855 | "version": "0.19.8", 856 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.8.tgz", 857 | "integrity": "sha512-l7iffQpT2OrZfH2rXIp7/FkmaeZM0vxbxN9KfiCwGYuZqzMg/JdvX26R31Zxn/Pxvsrg3Y9N6XTcnknqDyyv4w==", 858 | "dev": true, 859 | "hasInstallScript": true, 860 | "bin": { 861 | "esbuild": "bin/esbuild" 862 | }, 863 | "engines": { 864 | "node": ">=12" 865 | }, 866 | "optionalDependencies": { 867 | "@esbuild/android-arm": "0.19.8", 868 | "@esbuild/android-arm64": "0.19.8", 869 | "@esbuild/android-x64": "0.19.8", 870 | "@esbuild/darwin-arm64": "0.19.8", 871 | "@esbuild/darwin-x64": "0.19.8", 872 | "@esbuild/freebsd-arm64": "0.19.8", 873 | "@esbuild/freebsd-x64": "0.19.8", 874 | "@esbuild/linux-arm": "0.19.8", 875 | "@esbuild/linux-arm64": "0.19.8", 876 | "@esbuild/linux-ia32": "0.19.8", 877 | "@esbuild/linux-loong64": "0.19.8", 878 | "@esbuild/linux-mips64el": "0.19.8", 879 | "@esbuild/linux-ppc64": "0.19.8", 880 | "@esbuild/linux-riscv64": "0.19.8", 881 | "@esbuild/linux-s390x": "0.19.8", 882 | "@esbuild/linux-x64": "0.19.8", 883 | "@esbuild/netbsd-x64": "0.19.8", 884 | "@esbuild/openbsd-x64": "0.19.8", 885 | "@esbuild/sunos-x64": "0.19.8", 886 | "@esbuild/win32-arm64": "0.19.8", 887 | "@esbuild/win32-ia32": "0.19.8", 888 | "@esbuild/win32-x64": "0.19.8" 889 | } 890 | }, 891 | "node_modules/esbuild-node-externals": { 892 | "version": "1.11.0", 893 | "resolved": "https://registry.npmjs.org/esbuild-node-externals/-/esbuild-node-externals-1.11.0.tgz", 894 | "integrity": "sha512-ceISJR4U9ejE5ZShoD9HTHO0ClZ8Va7gkkBgejjYMo6/Kn7YDEYvt7YSiLgxor4xb47O7PTpgSoRNtRlV9WXKw==", 895 | "dev": true, 896 | "dependencies": { 897 | "find-up": "^5.0.0", 898 | "tslib": "^2.4.1" 899 | }, 900 | "engines": { 901 | "node": ">=12" 902 | }, 903 | "peerDependencies": { 904 | "esbuild": "0.12 - 0.19" 905 | } 906 | }, 907 | "node_modules/fill-range": { 908 | "version": "7.0.1", 909 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 910 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 911 | "dev": true, 912 | "dependencies": { 913 | "to-regex-range": "^5.0.1" 914 | }, 915 | "engines": { 916 | "node": ">=8" 917 | } 918 | }, 919 | "node_modules/find-up": { 920 | "version": "5.0.0", 921 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 922 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 923 | "dev": true, 924 | "dependencies": { 925 | "locate-path": "^6.0.0", 926 | "path-exists": "^4.0.0" 927 | }, 928 | "engines": { 929 | "node": ">=10" 930 | }, 931 | "funding": { 932 | "url": "https://github.com/sponsors/sindresorhus" 933 | } 934 | }, 935 | "node_modules/fsevents": { 936 | "version": "2.3.3", 937 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 938 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 939 | "dev": true, 940 | "hasInstallScript": true, 941 | "optional": true, 942 | "os": [ 943 | "darwin" 944 | ], 945 | "engines": { 946 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 947 | } 948 | }, 949 | "node_modules/function-bind": { 950 | "version": "1.1.2", 951 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 952 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 953 | "dev": true, 954 | "funding": { 955 | "url": "https://github.com/sponsors/ljharb" 956 | } 957 | }, 958 | "node_modules/glob-parent": { 959 | "version": "5.1.2", 960 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 961 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 962 | "dev": true, 963 | "dependencies": { 964 | "is-glob": "^4.0.1" 965 | }, 966 | "engines": { 967 | "node": ">= 6" 968 | } 969 | }, 970 | "node_modules/hasown": { 971 | "version": "2.0.0", 972 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", 973 | "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", 974 | "dev": true, 975 | "dependencies": { 976 | "function-bind": "^1.1.2" 977 | }, 978 | "engines": { 979 | "node": ">= 0.4" 980 | } 981 | }, 982 | "node_modules/immutable": { 983 | "version": "4.3.4", 984 | "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", 985 | "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==", 986 | "dev": true 987 | }, 988 | "node_modules/is-binary-path": { 989 | "version": "2.1.0", 990 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 991 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 992 | "dev": true, 993 | "dependencies": { 994 | "binary-extensions": "^2.0.0" 995 | }, 996 | "engines": { 997 | "node": ">=8" 998 | } 999 | }, 1000 | "node_modules/is-core-module": { 1001 | "version": "2.13.1", 1002 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", 1003 | "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", 1004 | "dev": true, 1005 | "dependencies": { 1006 | "hasown": "^2.0.0" 1007 | }, 1008 | "funding": { 1009 | "url": "https://github.com/sponsors/ljharb" 1010 | } 1011 | }, 1012 | "node_modules/is-extglob": { 1013 | "version": "2.1.1", 1014 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1015 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 1016 | "dev": true, 1017 | "engines": { 1018 | "node": ">=0.10.0" 1019 | } 1020 | }, 1021 | "node_modules/is-glob": { 1022 | "version": "4.0.3", 1023 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1024 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1025 | "dev": true, 1026 | "dependencies": { 1027 | "is-extglob": "^2.1.1" 1028 | }, 1029 | "engines": { 1030 | "node": ">=0.10.0" 1031 | } 1032 | }, 1033 | "node_modules/is-number": { 1034 | "version": "7.0.0", 1035 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1036 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1037 | "dev": true, 1038 | "engines": { 1039 | "node": ">=0.12.0" 1040 | } 1041 | }, 1042 | "node_modules/json-to-graphql-query": { 1043 | "version": "2.2.5", 1044 | "resolved": "https://registry.npmjs.org/json-to-graphql-query/-/json-to-graphql-query-2.2.5.tgz", 1045 | "integrity": "sha512-5Nom9inkIMrtY992LMBBG1Zaekrc10JaRhyZgprwHBVMDtRgllTvzl0oBbg13wJsVZoSoFNNMaeIVQs0P04vsA==" 1046 | }, 1047 | "node_modules/locate-path": { 1048 | "version": "6.0.0", 1049 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 1050 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 1051 | "dev": true, 1052 | "dependencies": { 1053 | "p-locate": "^5.0.0" 1054 | }, 1055 | "engines": { 1056 | "node": ">=10" 1057 | }, 1058 | "funding": { 1059 | "url": "https://github.com/sponsors/sindresorhus" 1060 | } 1061 | }, 1062 | "node_modules/normalize-path": { 1063 | "version": "3.0.0", 1064 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1065 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1066 | "dev": true, 1067 | "engines": { 1068 | "node": ">=0.10.0" 1069 | } 1070 | }, 1071 | "node_modules/p-limit": { 1072 | "version": "3.1.0", 1073 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 1074 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 1075 | "dev": true, 1076 | "dependencies": { 1077 | "yocto-queue": "^0.1.0" 1078 | }, 1079 | "engines": { 1080 | "node": ">=10" 1081 | }, 1082 | "funding": { 1083 | "url": "https://github.com/sponsors/sindresorhus" 1084 | } 1085 | }, 1086 | "node_modules/p-locate": { 1087 | "version": "5.0.0", 1088 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 1089 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1090 | "dev": true, 1091 | "dependencies": { 1092 | "p-limit": "^3.0.2" 1093 | }, 1094 | "engines": { 1095 | "node": ">=10" 1096 | }, 1097 | "funding": { 1098 | "url": "https://github.com/sponsors/sindresorhus" 1099 | } 1100 | }, 1101 | "node_modules/path-exists": { 1102 | "version": "4.0.0", 1103 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1104 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1105 | "dev": true, 1106 | "engines": { 1107 | "node": ">=8" 1108 | } 1109 | }, 1110 | "node_modules/path-parse": { 1111 | "version": "1.0.7", 1112 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 1113 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 1114 | "dev": true 1115 | }, 1116 | "node_modules/picomatch": { 1117 | "version": "2.3.1", 1118 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1119 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1120 | "dev": true, 1121 | "engines": { 1122 | "node": ">=8.6" 1123 | }, 1124 | "funding": { 1125 | "url": "https://github.com/sponsors/jonschlinkert" 1126 | } 1127 | }, 1128 | "node_modules/readdirp": { 1129 | "version": "3.6.0", 1130 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1131 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1132 | "dev": true, 1133 | "dependencies": { 1134 | "picomatch": "^2.2.1" 1135 | }, 1136 | "engines": { 1137 | "node": ">=8.10.0" 1138 | } 1139 | }, 1140 | "node_modules/resolve": { 1141 | "version": "1.22.8", 1142 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", 1143 | "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", 1144 | "dev": true, 1145 | "dependencies": { 1146 | "is-core-module": "^2.13.0", 1147 | "path-parse": "^1.0.7", 1148 | "supports-preserve-symlinks-flag": "^1.0.0" 1149 | }, 1150 | "bin": { 1151 | "resolve": "bin/resolve" 1152 | }, 1153 | "funding": { 1154 | "url": "https://github.com/sponsors/ljharb" 1155 | } 1156 | }, 1157 | "node_modules/sass": { 1158 | "version": "1.69.5", 1159 | "resolved": "https://registry.npmjs.org/sass/-/sass-1.69.5.tgz", 1160 | "integrity": "sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==", 1161 | "dev": true, 1162 | "dependencies": { 1163 | "chokidar": ">=3.0.0 <4.0.0", 1164 | "immutable": "^4.0.0", 1165 | "source-map-js": ">=0.6.2 <2.0.0" 1166 | }, 1167 | "bin": { 1168 | "sass": "sass.js" 1169 | }, 1170 | "engines": { 1171 | "node": ">=14.0.0" 1172 | } 1173 | }, 1174 | "node_modules/source-map-js": { 1175 | "version": "1.0.2", 1176 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 1177 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 1178 | "dev": true, 1179 | "engines": { 1180 | "node": ">=0.10.0" 1181 | } 1182 | }, 1183 | "node_modules/supports-preserve-symlinks-flag": { 1184 | "version": "1.0.0", 1185 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 1186 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 1187 | "dev": true, 1188 | "engines": { 1189 | "node": ">= 0.4" 1190 | }, 1191 | "funding": { 1192 | "url": "https://github.com/sponsors/ljharb" 1193 | } 1194 | }, 1195 | "node_modules/to-regex-range": { 1196 | "version": "5.0.1", 1197 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1198 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1199 | "dev": true, 1200 | "dependencies": { 1201 | "is-number": "^7.0.0" 1202 | }, 1203 | "engines": { 1204 | "node": ">=8.0" 1205 | } 1206 | }, 1207 | "node_modules/tslib": { 1208 | "version": "2.6.2", 1209 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", 1210 | "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", 1211 | "dev": true 1212 | }, 1213 | "node_modules/yocto-queue": { 1214 | "version": "0.1.0", 1215 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1216 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 1217 | "dev": true, 1218 | "engines": { 1219 | "node": ">=10" 1220 | }, 1221 | "funding": { 1222 | "url": "https://github.com/sponsors/sindresorhus" 1223 | } 1224 | } 1225 | } 1226 | } 1227 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shopify-orm", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "files": [ 7 | "dist" 8 | ], 9 | "scripts": { 10 | "build": "rm -rf dist && node esbuild.js" 11 | }, 12 | "keywords": [], 13 | "author": "neonninja22", 14 | "license": "ISC", 15 | "dependencies": { 16 | "@shopify/graphql-client": "^0.8.0", 17 | "json-to-graphql-query": "^2.2.5" 18 | }, 19 | "devDependencies": { 20 | "@shelf/esbuild-config": "^0.2.0", 21 | "esbuild": "^0.19.8", 22 | "esbuild-node-externals": "^1.11.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /screenshots/meta-object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ninja-Front/shopify-orm/404ee060f7548d04ec803ee62e08965472b94a6e/screenshots/meta-object.png -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import type { GraphQLClient } from "@shopify/graphql-client"; 2 | import type { Fields, RemoteTypeCrud, OrmSchema, OrmSchemaConfig } from "./types"; 3 | import { MetaObjectRemoteSchemaCrud } from "./schemaHandler/metaobject"; 4 | type ShopifyORM = { 5 | metaobject: (schema: OrmSchema) => RemoteTypeCrud; 6 | } 7 | export function shopifyORM(client: GraphQLClient): ShopifyORM { 8 | return { 9 | metaobject: (schema: OrmSchema) => { 10 | const handler = new MetaObjectRemoteSchemaCrud({ 11 | client, 12 | schema 13 | }); 14 | return handler; 15 | } 16 | } 17 | } 18 | type OrmSchemaWithColumns = OrmSchema; 19 | export function metaobject(name: string, fields: Fields, configs: OrmSchemaConfig): OrmSchemaWithColumns { 20 | return { 21 | ...configs, 22 | type: 'shopify_metaobject', 23 | name, 24 | fields 25 | } 26 | } -------------------------------------------------------------------------------- /src/schemaHandler/metaobject/MetaObjectDefinition.ts: -------------------------------------------------------------------------------- 1 | import type { ClientResponse, GraphQLClient } from '@shopify/graphql-client'; 2 | import type { MetaObjectDefinition, MetaObjectDefinitionUpdateInput } from '../../../field-types'; 3 | import { runQql } from './client'; 4 | 5 | const CHECK_EXISTING_META_OBJECT_DEFINITION = `#graphql 6 | query metaobjectDefinition($type: String = "") { 7 | metaobjectDefinitionByType(type: $type) { 8 | id 9 | type 10 | access { 11 | admin 12 | storefront 13 | } 14 | capabilities { 15 | publishable { 16 | enabled 17 | } 18 | } 19 | displayNameKey 20 | fieldDefinitions { 21 | key 22 | name 23 | type{ 24 | name 25 | # category 26 | } 27 | } 28 | } 29 | } 30 | `; 31 | type MetaObjectDefinitionResponse = { 32 | metaobjectDefinitionByType: MetaObjectDefinition 33 | } 34 | const CREATE_META_OBJECT_DEFINITION = `#graphql 35 | mutation CreateMetaObjectDefinition($definition: MetaobjectDefinitionCreateInput!) { 36 | metaobjectDefinitionCreate(definition: $definition) { 37 | metaobjectDefinition { 38 | id 39 | } 40 | userErrors { 41 | code 42 | elementIndex 43 | elementKey 44 | field 45 | message 46 | } 47 | } 48 | }`; 49 | type UserError = { 50 | code: string 51 | elementIndex: number 52 | elementKey: string 53 | field: string 54 | message: string 55 | }; 56 | type CreateResponse = { 57 | metaobjectDefinitionCreate: { 58 | metaobjectDefinition: { 59 | id: string 60 | } 61 | userErrors: UserError[] 62 | } 63 | 64 | } 65 | const UPDATE_META_OBJECT_DEFINITION = `#graphql 66 | mutation UpdateMetaObjectDefinition($definition: MetaobjectDefinitionUpdateInput!,$id:ID!) { 67 | metaobjectDefinitionUpdate(definition: $definition,id:$id) { 68 | metaobjectDefinition { 69 | id 70 | } 71 | userErrors { 72 | code 73 | elementIndex 74 | elementKey 75 | field 76 | message 77 | } 78 | } 79 | }`; 80 | type UpdateResponse = { 81 | metaobjectDefinitionUpdate: { 82 | metaobjectDefinition: { 83 | id: string 84 | } 85 | userErrors: UserError[] 86 | } 87 | 88 | 89 | } 90 | export function getMetaObjectDefinition( 91 | client: GraphQLClient, 92 | type: string 93 | ): Promise> { 94 | return runQql({ 95 | client, 96 | query: CHECK_EXISTING_META_OBJECT_DEFINITION, 97 | variables: { 98 | type 99 | } 100 | }); 101 | 102 | } 103 | export function createMetaObjectDefinition( 104 | client: GraphQLClient, 105 | metaObjectDefinition: MetaObjectDefinition 106 | ): Promise> { 107 | return runQql({ 108 | client, 109 | query: CREATE_META_OBJECT_DEFINITION, 110 | variables: { 111 | definition: metaObjectDefinition 112 | } 113 | }); 114 | 115 | 116 | } 117 | export function updateMetaObjectDefinition( 118 | client: GraphQLClient, 119 | { definition, id }: { definition: MetaObjectDefinitionUpdateInput, id: string } 120 | ): Promise> { 121 | return runQql({ 122 | client, 123 | query: UPDATE_META_OBJECT_DEFINITION, 124 | variables: { 125 | definition: definition, 126 | id 127 | } 128 | }); 129 | } -------------------------------------------------------------------------------- /src/schemaHandler/metaobject/client.ts: -------------------------------------------------------------------------------- 1 | import { ClientResponse } from "@shopify/graphql-client"; 2 | 3 | export async function runQql({ 4 | client, 5 | query, 6 | variables 7 | }): Promise> { 8 | const resp = await client.request(query, { 9 | variables 10 | }); 11 | 12 | if (resp.errors) { 13 | throw resp.errors; 14 | } 15 | else { 16 | return resp; 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /src/schemaHandler/metaobject/helper.ts: -------------------------------------------------------------------------------- 1 | import type { OrmSchema } from "../../types"; 2 | import type { FieldDefinition, MetaObjectDefinition, MetaObjectDefinitionUpdateInput, } from "../../../field-types"; 3 | import { Metafield } from "../../../field-types"; 4 | import { jsonToGraphQLQuery, VariableType } from 'json-to-graphql-query'; 5 | 6 | export function convertToMetaObjectDefinition(inputSchema: OrmSchema): MetaObjectDefinition { 7 | const fieldKeys = Object.keys(inputSchema.fields); 8 | return { 9 | capabilities: inputSchema.capabilities, 10 | type: inputSchema.name, 11 | access: inputSchema.access, 12 | displayNameKey: inputSchema?.displayNameKey, 13 | fieldDefinitions: fieldKeys.map((fieldKey: string) => { 14 | const field = inputSchema.fields[fieldKey]; 15 | return { 16 | key: fieldKey, 17 | name: field.name, 18 | type: field.type, 19 | } as FieldDefinition; 20 | }) 21 | }; 22 | } 23 | 24 | export function getMetaObjectFieldsQuery( 25 | schema: OrmSchema, 26 | ) { 27 | const fieldKeys = Object.keys(schema.fields); 28 | 29 | const fields = fieldKeys.reduce((acc, fieldKey) => { 30 | acc[fieldKey] = { 31 | __aliasFor: 'field', 32 | __args: { 33 | key: fieldKey 34 | }, 35 | value: true 36 | }; 37 | return acc; 38 | } 39 | , {} as any); 40 | return fields; 41 | } 42 | export type PageInfo = { 43 | endCursor: string; 44 | hasNextPage: boolean; 45 | hasPreviousPage: boolean; 46 | startCursor: string; 47 | } 48 | export function getMetaObjectsListQuery( 49 | schema: OrmSchema, 50 | ) { 51 | const fields = getMetaObjectFieldsQuery(schema); 52 | const query = jsonToGraphQLQuery({ 53 | query: { 54 | __name: 'metaobjects', 55 | __variables: { 56 | type: 'String!', 57 | first: 'Int!', 58 | after: 'String', 59 | }, 60 | metaobjects: { 61 | __args: { 62 | first: new VariableType('first'), 63 | type: new VariableType('type'), 64 | after: new VariableType('after'), 65 | }, 66 | edges: { 67 | node: { 68 | id: true, 69 | ...fields, 70 | } 71 | }, 72 | pageInfo: { 73 | endCursor: true, 74 | hasNextPage: true, 75 | hasPreviousPage: true, 76 | startCursor: true, 77 | } 78 | } 79 | } 80 | }, { 81 | pretty: true 82 | }); 83 | return query; 84 | } 85 | 86 | export function getMetaObjectItemQuery( 87 | schema: OrmSchema, 88 | ) { 89 | const fields = getMetaObjectFieldsQuery(schema); 90 | const query = jsonToGraphQLQuery({ 91 | query: { 92 | __name: 'metaobject', 93 | __variables: { 94 | id: 'ID!', 95 | }, 96 | metaobject: { 97 | __args: { 98 | id: new VariableType('id'), 99 | }, 100 | id: true, 101 | ...fields, 102 | } 103 | } 104 | }, { 105 | pretty: true 106 | }); 107 | return query; 108 | } 109 | 110 | export function getCreateMetaObjectMutation( 111 | schema: OrmSchema, 112 | ): string { 113 | const fields = getMetaObjectFieldsQuery(schema); 114 | const query = jsonToGraphQLQuery({ 115 | mutation: { 116 | __name: 'CreateMetaObject', 117 | __variables: { 118 | metaobject: 'MetaobjectCreateInput!', 119 | }, 120 | metaobjectCreate: { 121 | __args: { 122 | metaobject: new VariableType('metaobject'), 123 | }, 124 | metaobject: { 125 | id: true, 126 | ...fields, 127 | }, 128 | userErrors: { 129 | code: true, 130 | elementIndex: true, 131 | elementKey: true, 132 | field: true, 133 | message: true, 134 | }, 135 | }, 136 | }, 137 | }, 138 | { 139 | pretty: true 140 | }); 141 | return query; 142 | } 143 | export function getUpdateMetaObjectMutation( 144 | schema: OrmSchema, 145 | ): string { 146 | const fields = getMetaObjectFieldsQuery(schema); 147 | const query = jsonToGraphQLQuery({ 148 | mutation: { 149 | __name: 'UpdateMetaObject', 150 | __variables: { 151 | metaobject: 'MetaobjectUpdateInput!', 152 | id: 'ID!', 153 | }, 154 | metaobjectUpdate: { 155 | __args: { 156 | metaobject: new VariableType('metaobject'), 157 | id: new VariableType('id'), 158 | }, 159 | metaobject: { 160 | id: true, 161 | ...fields, 162 | }, 163 | userErrors: { 164 | code: true, 165 | elementIndex: true, 166 | elementKey: true, 167 | field: true, 168 | message: true, 169 | }, 170 | }, 171 | }, 172 | }, 173 | { 174 | pretty: true 175 | }); 176 | return query; 177 | } 178 | export const META_OBJECT_DELETE_MUTATION = `#graphql 179 | mutation metaobjectDelete($id: ID!) { 180 | metaobjectDelete(id: $id) { 181 | deletedId 182 | userErrors { 183 | code 184 | elementIndex 185 | elementKey 186 | field 187 | message 188 | } 189 | } 190 | }`; 191 | 192 | 193 | export function convertToMetaObjectInput( 194 | schema: OrmSchema, 195 | data: any 196 | ): { 197 | type: string; 198 | capabilities: any; 199 | } { 200 | const fieldsKeys = Object.keys(schema.fields); 201 | const fields = fieldsKeys.map((fieldKey: string) => { 202 | // const field = schema.fields[fieldKey]; 203 | const value = data[fieldKey]; 204 | if (value) 205 | return { 206 | key: fieldKey, 207 | value: typeof value === 'object' ? JSON.stringify(value) : value 208 | } as unknown as Metafield; 209 | return null; 210 | }); 211 | 212 | return { 213 | type: schema.name, 214 | fields 215 | }; 216 | } 217 | export function getSchemaDefaultValue( 218 | schema: MetaObjectSchemaType, 219 | ) { 220 | const fields = schema.fields.reduce((acc, field) => { 221 | acc[field.name] = field.defaultValue; 222 | return acc; 223 | } 224 | , {} as any); 225 | return fields; 226 | } 227 | 228 | const normalizeType = (type: string | { name: string }): string => 229 | typeof type === 'string' ? type : type.name; 230 | 231 | // Helper to compare non-array fields 232 | const compareFields = (field1: any, field2: any): boolean => { 233 | if (field1 && field2) { 234 | return JSON.stringify(field1) === JSON.stringify(field2); 235 | } 236 | return field1 === field2; 237 | }; 238 | 239 | export function findSchemaChanges(dbObject: MetaObjectDefinition, localObject: MetaObjectDefinition): MetaObjectDefinitionUpdateInput | null { 240 | const localFields = new Map(localObject.fieldDefinitions.map(field => [field.key ?? field.name, field])); 241 | const dbFields = new Map(dbObject.fieldDefinitions.map(field => [field.key ?? field.name, field])); 242 | let hasChanges = false; 243 | // const fieldDefinitions: Array = []; 244 | let changes: MetaObjectDefinitionUpdateInput = { 245 | 246 | fieldDefinitions: [] 247 | }; 248 | 249 | 250 | // Check for added, updated, or deleted fields 251 | localFields.forEach((localField, key) => { 252 | const dbField = dbFields.get(key); 253 | if (!dbField) { 254 | 255 | changes?.fieldDefinitions.push({ 256 | create: localField 257 | }); 258 | } else if (normalizeType(localField.name) !== normalizeType(dbField.name) || normalizeType(localField.type) !== normalizeType(dbField.type)) { 259 | changes?.fieldDefinitions.push({ update: localField }); 260 | } 261 | }); 262 | dbFields.forEach((_, key) => { 263 | if (!localFields.has(key)) { 264 | changes?.fieldDefinitions.push({ 265 | delete: { 266 | key 267 | } 268 | }); 269 | } 270 | }); 271 | // Check other properties like access, capabilities, displayNameKey, etc. 272 | ['access', 'capabilities', 'displayNameKey'].forEach(prop => { 273 | if (!compareFields(dbObject[prop as keyof MetaObjectDefinition], localObject[prop as keyof MetaObjectDefinition])) { 274 | hasChanges = true; 275 | changes[prop] = localObject[prop as keyof MetaObjectDefinition]; 276 | } 277 | }); 278 | if (changes?.fieldDefinitions.length > 0) { 279 | hasChanges = true; 280 | } 281 | if (hasChanges) { 282 | return changes; 283 | } 284 | return null; 285 | } 286 | const objectFields = ['list.product_reference', 'list.collection_reference']; 287 | export function convertToModel( 288 | schema: OrmSchema, 289 | data: any 290 | ) { 291 | const fields = Object.keys(data); 292 | let item = { 293 | id: data.id 294 | } 295 | fields.forEach((fieldKey: string) => { 296 | const field = schema.fields[fieldKey]; 297 | if (field) { 298 | item[fieldKey] = data[fieldKey]?.value; 299 | const isObject = field.type === 'json' || objectFields.includes(field.type); 300 | if (item[fieldKey] && isObject) { 301 | item[fieldKey] = JSON.parse(item[fieldKey]); 302 | } 303 | } 304 | }); 305 | return item; 306 | } 307 | -------------------------------------------------------------------------------- /src/schemaHandler/metaobject/index.ts: -------------------------------------------------------------------------------- 1 | import type { DbHandlerConfig, DbHandlerCreateCrudInput, DbHandlerDeleteCrudInput, DbHandlerGetCrudInput, DbHandlerListInput, DbHandlerUpdateCrudInput, DeleteResult, MigrateResult } from "../../types"; 2 | import { RemoteTypeCrud } from "../../types"; 3 | import { createMetaObjectDefinition, getMetaObjectDefinition, updateMetaObjectDefinition } from "./MetaObjectDefinition"; 4 | import { runQql } from "./client"; 5 | import type { PageInfo } from "./helper"; 6 | import { META_OBJECT_DELETE_MUTATION, convertToMetaObjectDefinition, convertToMetaObjectInput, convertToModel, findSchemaChanges, getCreateMetaObjectMutation, getMetaObjectItemQuery, getMetaObjectsListQuery, getUpdateMetaObjectMutation } from "./helper"; 7 | 8 | // complete this code 9 | export class MetaObjectRemoteSchemaCrud extends RemoteTypeCrud { 10 | 11 | constructor(config: DbHandlerConfig) { 12 | super(); 13 | this.config = config; 14 | } 15 | async migrate(): Promise { 16 | const metaObjectDefinition = convertToMetaObjectDefinition(this.config.schema); 17 | const remoteDefinition = await getMetaObjectDefinition(this.config.client, this.config.schema.name); 18 | const currentDefinition = remoteDefinition?.data?.metaobjectDefinitionByType; 19 | if (currentDefinition !== null && currentDefinition !== undefined) { 20 | const changes = findSchemaChanges(currentDefinition, metaObjectDefinition); 21 | if (changes !== null) { 22 | const updatedResponse = await updateMetaObjectDefinition(this.config.client, { 23 | definition: changes, 24 | id: currentDefinition.id 25 | }); 26 | if (updatedResponse?.data?.metaobjectDefinitionUpdate?.metaobjectDefinition?.id) { 27 | return { success: true, message: 'Definition Updated' }; 28 | } else { 29 | return { success: false, message: 'Definition Not Updated' }; 30 | } 31 | 32 | 33 | } else { 34 | return { success: true, message: 'Definition is up to date' }; 35 | } 36 | } else { 37 | const createdResponse = await createMetaObjectDefinition(this.config.client, metaObjectDefinition); 38 | if (createdResponse?.data?.metaobjectDefinitionCreate?.metaobjectDefinition?.id) { 39 | return { success: true, message: 'Definition Created' }; 40 | } else { 41 | return { success: false, message: 'Definition Not Created' }; 42 | } 43 | } 44 | 45 | } 46 | 47 | async get(input: DbHandlerGetCrudInput): Promise { 48 | 49 | const query = getMetaObjectItemQuery(this.config.schema); 50 | const response = await runQql({ 51 | client: this.config.client, 52 | query, 53 | variables: { 54 | id: input.id 55 | } 56 | }); 57 | const item = convertToModel(this.config.schema, response?.data?.metaobject); 58 | return item; 59 | 60 | } 61 | 62 | async list(input: DbHandlerListInput): Promise<{ 63 | items: Item[]; 64 | pageInfo: PageInfo; 65 | } | DbHandlerCrudError> { 66 | try { 67 | const query = getMetaObjectsListQuery(this.config.schema); 68 | const variables = { 69 | type: this.config.schema.name, 70 | first: input.first || 10, 71 | after: input.after 72 | } 73 | const response = await runQql({ 74 | client: this.config.client, 75 | query, 76 | variables 77 | }); 78 | const items = response?.data?.metaobjects?.edges.map((item: any) => { 79 | return convertToModel(this.config.schema, item?.node); 80 | }); 81 | return { 82 | items, 83 | pageInfo: response?.data?.metaobjects?.pageInfo 84 | 85 | }; 86 | 87 | } catch (error) { 88 | return { message: error.message, code: 'LIST_ERROR' }; 89 | } 90 | } 91 | async create(input: DbHandlerCreateCrudInput): Promise { 92 | const query = getCreateMetaObjectMutation(this.config.schema); 93 | const metaobject = convertToMetaObjectInput(this.config.schema, input.data); 94 | const createdItem = await runQql({ 95 | client: this.config.client, 96 | query, 97 | variables: { 98 | metaobject 99 | } 100 | }) 101 | // console.log(createdItem); 102 | 103 | if (createdItem?.data?.metaobjectCreate.userErrors?.length > 0) { 104 | throw createdItem?.data?.metaobjectCreate.userErrors; 105 | } 106 | 107 | return convertToModel(this.config.schema, createdItem?.data?.metaobjectCreate?.metaobject); 108 | 109 | } 110 | 111 | async update(input: DbHandlerUpdateCrudInput): Promise { 112 | const metaobject = convertToMetaObjectInput(this.config.schema, input.data); 113 | const query = getUpdateMetaObjectMutation(this.config.schema); 114 | 115 | const updateItem = await runQql({ 116 | client: this.config.client, 117 | query, 118 | variables: { 119 | id: input.id, 120 | metaobject: { 121 | fields: metaobject.fields 122 | } 123 | } 124 | }) 125 | 126 | return convertToModel(this.config.schema, updateItem?.data?.metaobjectUpdate?.metaobject); 127 | 128 | } 129 | 130 | // Implement the 'delete' method 131 | async delete(input: DbHandlerDeleteCrudInput): Promise { 132 | const response = await runQql({ 133 | client: this.config.client, 134 | query: META_OBJECT_DELETE_MUTATION, 135 | variables: { 136 | id: input.id 137 | } 138 | }); 139 | return { success: true, message: 'Item deleted successfully' }; 140 | 141 | } 142 | } -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import type { GraphQLClient } from "@shopify/graphql-client"; 2 | import type { CapabilitiesDefinition, Metafield } from "../field-types"; 3 | 4 | export type OrmRemoteType = 'shopify_metaobject' | 'payment_customization' | 'delivery_customization'; 5 | type AdminAccess = 6 | | "PRIVATE" 7 | | "MERCHANT_READ" 8 | | "MERCHANT_READ_WRITE" 9 | | "PUBLIC_READ" 10 | | "PUBLIC_READ_WRITE"; 11 | export interface AccessDefinition { 12 | admin: AdminAccess; 13 | storefront: "PUBLIC_READ" | 'NONE'; // You can add more types if needed 14 | } 15 | 16 | export type Field = { 17 | type: Metafield['type'] 18 | name: string; 19 | description?: string; 20 | defaultValue?: any; 21 | } 22 | export type Fields = Record; 23 | export type OrmSchemaConfig = { 24 | capabilities: CapabilitiesDefinition 25 | displayNameKey?: string; 26 | access: AccessDefinition; 27 | admin_access: AdminAccess; 28 | } 29 | export type OrmSchema = OrmSchemaConfig & { 30 | type: OrmRemoteType; 31 | name: string; 32 | fields: Fields; 33 | } 34 | export type DbHandlerCrudInput = { 35 | // schema: OrmSchema; 36 | } 37 | type IdOrHandle = { id: string } | { handle: string }; 38 | 39 | export type DbHandlerGetCrudInput = DbHandlerCrudInput & IdOrHandle; 40 | 41 | export type DbHandlerDeleteCrudInput = DbHandlerCrudInput & IdOrHandle; 42 | 43 | 44 | export type DbHandlerUpdateCrudInput = DbHandlerCrudInput & { 45 | data: any; 46 | } & IdOrHandle 47 | 48 | export type DbHandlerCreateCrudInput = DbHandlerCrudInput & { 49 | data: any; 50 | } 51 | 52 | export type DbHandlerCrudError = { 53 | message: string; 54 | code: string; 55 | } 56 | export type MigrateSuccess = { 57 | success: true; 58 | message: string; 59 | } 60 | export type MigrateError = { 61 | success: false; 62 | message: string; 63 | } 64 | export type MigrateResult = MigrateSuccess | MigrateError; 65 | export type DbHandlerConfig = { 66 | client: GraphQLClient; 67 | schema: OrmSchema; 68 | } 69 | export type DbHandlerListInput = { 70 | first?: number; 71 | after?: string; 72 | type?: string; 73 | } 74 | export type DeleteResult = { 75 | success: boolean; 76 | message: string; 77 | 78 | } 79 | export abstract class RemoteTypeCrud { 80 | abstract migrate(): Promise; 81 | abstract get(input: DbHandlerGetCrudInput): Promise; 82 | abstract list(input: DbHandlerListInput): Promise; 83 | abstract create(input: DbHandlerCreateCrudInput): Promise; 84 | abstract update(input: DbHandlerUpdateCrudInput): Promise; 85 | abstract delete(input: DbHandlerDeleteCrudInput): Promise; 86 | } 87 | export type RemoteDbHandler = Record> -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "compilerOptions": { 4 | "module": "esnext", 5 | "target": "esnext", 6 | "lib": ["dom", "dom.iterable", "esnext"], 7 | "declaration": true, 8 | "strict": false, 9 | "moduleResolution": "node", 10 | "jsx": "react", 11 | "skipLibCheck": true, 12 | "esModuleInterop": true, 13 | "emitDeclarationOnly": true, 14 | "outDir": "dist", 15 | "rootDir": "src" 16 | } 17 | } --------------------------------------------------------------------------------