diff --git a/package.json b/package.json index 2ed83cc..2691edc 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "prepublishOnly": "pnpm run build && pnpm run test && pnpm run lint" }, "dependencies": { + "core-js": "^3.46.0", "graphql": "^16.11.0" }, "devDependencies": { @@ -38,7 +39,7 @@ "vitest": "^3.2.4" }, "engines": { - "node": ">=14.0.0" + "node": ">=20.0.0" }, "author": "lopert", "license": "MIT", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aa72b32..d4de8fd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + core-js: + specifier: ^3.46.0 + version: 3.46.0 graphql: specifier: ^16.11.0 version: 16.11.0 @@ -17,7 +20,7 @@ importers: version: 50.0.0(@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(@typescript-eslint/utils@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(prettier@3.6.2)(typescript@5.9.3) '@types/node': specifier: ^22.18.6 - version: 22.18.10 + version: 22.18.13 '@typescript-eslint/eslint-plugin': specifier: ^8.46.2 version: 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) @@ -35,7 +38,7 @@ importers: version: 5.9.3 vitest: specifier: ^3.2.4 - version: 3.2.4(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1) + version: 3.2.4(@types/node@22.18.13)(jiti@2.6.1)(yaml@2.8.1) test-app: {} @@ -43,11 +46,11 @@ importers: dependencies: '@shopify/shopify_function': specifier: 2.0.0 - version: 2.0.0(@types/node@22.18.10)(graphql-sock@1.0.1(graphql@16.11.0)) + version: 2.0.0(@types/node@22.18.13)(graphql-sock@1.0.1(graphql@16.11.0)) devDependencies: vitest: specifier: 3.2.4 - version: 3.2.4(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1) + version: 3.2.4(@types/node@22.18.13)(jiti@2.6.1)(yaml@2.8.1) test-app/extensions/cart-validation-js/tests: dependencies: @@ -56,7 +59,7 @@ importers: version: link:../../../.. vitest: specifier: ^3.2.4 - version: 3.2.4(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1) + version: 3.2.4(@types/node@22.18.13)(jiti@2.6.1)(yaml@2.8.1) test-app/extensions/discount-function/tests: dependencies: @@ -65,7 +68,7 @@ importers: version: link:../../../.. vitest: specifier: ^3.2.4 - version: 3.2.4(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1) + version: 3.2.4(@types/node@22.18.13)(jiti@2.6.1)(yaml@2.8.1) packages: @@ -83,16 +86,16 @@ packages: resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.28.4': - resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} + '@babel/compat-data@7.28.5': + resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.4': - resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} + '@babel/core@7.28.5': + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} engines: {node: '>=6.9.0'} - '@babel/generator@7.28.3': - resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@7.27.2': @@ -121,8 +124,8 @@ packages: resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.27.1': - resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} '@babel/helper-validator-option@7.27.1': @@ -133,8 +136,8 @@ packages: resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.4': - resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} engines: {node: '>=6.0.0'} hasBin: true @@ -152,12 +155,12 @@ packages: resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.4': - resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.4': - resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} '@emnapi/core@1.6.0': @@ -343,8 +346,8 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.12.1': - resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} '@eslint/config-array@0.21.1': @@ -690,113 +693,113 @@ packages: '@repeaterjs/repeater@3.0.6': resolution: {integrity: sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==} - '@rollup/rollup-android-arm-eabi@4.52.4': - resolution: {integrity: sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==} + '@rollup/rollup-android-arm-eabi@4.52.5': + resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.52.4': - resolution: {integrity: sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==} + '@rollup/rollup-android-arm64@4.52.5': + resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.52.4': - resolution: {integrity: sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==} + '@rollup/rollup-darwin-arm64@4.52.5': + resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.52.4': - resolution: {integrity: sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==} + '@rollup/rollup-darwin-x64@4.52.5': + resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.52.4': - resolution: {integrity: sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==} + '@rollup/rollup-freebsd-arm64@4.52.5': + resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.52.4': - resolution: {integrity: sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==} + '@rollup/rollup-freebsd-x64@4.52.5': + resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.52.4': - resolution: {integrity: sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': + resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.52.4': - resolution: {integrity: sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==} + '@rollup/rollup-linux-arm-musleabihf@4.52.5': + resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.52.4': - resolution: {integrity: sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==} + '@rollup/rollup-linux-arm64-gnu@4.52.5': + resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.52.4': - resolution: {integrity: sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==} + '@rollup/rollup-linux-arm64-musl@4.52.5': + resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.52.4': - resolution: {integrity: sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==} + '@rollup/rollup-linux-loong64-gnu@4.52.5': + resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.52.4': - resolution: {integrity: sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==} + '@rollup/rollup-linux-ppc64-gnu@4.52.5': + resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.52.4': - resolution: {integrity: sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==} + '@rollup/rollup-linux-riscv64-gnu@4.52.5': + resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.52.4': - resolution: {integrity: sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==} + '@rollup/rollup-linux-riscv64-musl@4.52.5': + resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.52.4': - resolution: {integrity: sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==} + '@rollup/rollup-linux-s390x-gnu@4.52.5': + resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.52.4': - resolution: {integrity: sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==} + '@rollup/rollup-linux-x64-gnu@4.52.5': + resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.52.4': - resolution: {integrity: sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==} + '@rollup/rollup-linux-x64-musl@4.52.5': + resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.52.4': - resolution: {integrity: sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==} + '@rollup/rollup-openharmony-arm64@4.52.5': + resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.52.4': - resolution: {integrity: sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==} + '@rollup/rollup-win32-arm64-msvc@4.52.5': + resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.52.4': - resolution: {integrity: sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==} + '@rollup/rollup-win32-ia32-msvc@4.52.5': + resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.52.4': - resolution: {integrity: sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==} + '@rollup/rollup-win32-x64-gnu@4.52.5': + resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.52.4': - resolution: {integrity: sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==} + '@rollup/rollup-win32-x64-msvc@4.52.5': + resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==} cpu: [x64] os: [win32] @@ -808,8 +811,8 @@ packages: '@shopify/shopify_function@2.0.0': resolution: {integrity: sha512-i00I0hkmYzD5TkoSwxk81FP0tKaG1M0bbgftF4nigi2NRvFwR5PJy6r1/kcB9w2zBx9xKPG/Z8C4cc2TanFf5Q==} - '@theguild/federation-composition@0.20.1': - resolution: {integrity: sha512-lwYYKCeHmstOtbMtzxC0BQKWsUPYbEVRVdJ3EqR4jSpcF4gvNf3MOJv6yuvq6QsKqgYZURKRBszmg7VEDoi5Aw==} + '@theguild/federation-composition@0.20.2': + resolution: {integrity: sha512-QI4iSdrc4JvCWnMb1QbiHnEpdD33KGdiU66qfWOcM8ENebRGHkGjXDnUrVJ8F9g1dmCRMTNfn2NFGqTcDpeYXw==} engines: {node: '>=18'} peerDependencies: graphql: ^16.0.0 @@ -817,8 +820,8 @@ packages: '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} - '@types/chai@5.2.2': - resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} @@ -832,8 +835,8 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@22.18.10': - resolution: {integrity: sha512-anNG/V/Efn/YZY4pRzbACnKxNKoBng2VTFydVu8RRs5hQjikP8CQfaeAV59VFSCzKNp90mXiVXW2QzV56rwMrg==} + '@types/node@22.18.13': + resolution: {integrity: sha512-Bo45YKIjnmFtv6I1TuC8AaHBbqXtIo+Om5fE4QiU1Tj8QR/qt+8O3BAtOimG5IFmwaWiPmB3Mv3jtYzBA4Us2A==} '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} @@ -1153,8 +1156,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.8.18: - resolution: {integrity: sha512-UYmTpOBwgPScZpS4A+YbapwWuBwasxvO/2IOHArSsAhL/+ZdmATBXTex3t+l2hXwLVYK382ibr/nKoY9GKe86w==} + baseline-browser-mapping@2.8.21: + resolution: {integrity: sha512-JU0h5APyQNsHOlAM7HnQnPToSDQoEBZqzu/YBlqDnEeymPnZDREeXJA3KBMQee+dKteAxZ2AtvQEvVYdZf241Q==} hasBin: true bl@4.1.0: @@ -1170,8 +1173,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.26.3: - resolution: {integrity: sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==} + browserslist@4.27.0: + resolution: {integrity: sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1286,6 +1289,9 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + core-js@3.46.0: + resolution: {integrity: sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==} + cosmiconfig@8.3.6: resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} engines: {node: '>=14'} @@ -1339,15 +1345,6 @@ packages: supports-color: optional: true - debug@4.4.1: - resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -1406,8 +1403,8 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - electron-to-chromium@1.5.237: - resolution: {integrity: sha512-icUt1NvfhGLar5lSWH3tHNzablaA5js3HVHacQimfP8ViEBOQv+L7DKEuHdbTZ0SKCO1ogTJTIL1Gwk9S6Qvcg==} + electron-to-chromium@1.5.243: + resolution: {integrity: sha512-ZCphxFW3Q1TVhcgS9blfut1PX8lusVi2SvXQgmEEnK4TCmE1JhH2JkjJN+DNt0pJJwfBri5AROBnz2b/C+YU9g==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -2216,8 +2213,8 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - magic-string@0.30.19: - resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} map-cache@0.2.2: resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} @@ -2298,8 +2295,8 @@ packages: node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - node-releases@2.0.25: - resolution: {integrity: sha512-4auku8B/vw5psvTiiN9j1dAOsXvMoGqJuKJcR+dTdqiXEK20mMTk1UEo3HS16LeGQsVG6+qKTPM9u/qQ2LqATA==} + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} normalize-path@2.1.1: resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} @@ -2525,8 +2522,8 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rollup@4.52.4: - resolution: {integrity: sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==} + rollup@4.52.5: + resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2850,8 +2847,8 @@ packages: unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} - update-browserslist-db@1.1.3: - resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + update-browserslist-db@1.1.4: + resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -2880,8 +2877,8 @@ packages: engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite@7.1.10: - resolution: {integrity: sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA==} + vite@7.1.12: + resolution: {integrity: sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -3046,8 +3043,8 @@ snapshots: '@ardatan/relay-compiler@12.0.3(graphql@16.11.0)': dependencies: - '@babel/generator': 7.28.3 - '@babel/parser': 7.28.4 + '@babel/generator': 7.28.5 + '@babel/parser': 7.28.5 '@babel/runtime': 7.28.4 chalk: 4.1.2 fb-watchman: 2.0.2 @@ -3068,23 +3065,23 @@ snapshots: '@babel/code-frame@7.27.1': dependencies: - '@babel/helper-validator-identifier': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.28.4': {} + '@babel/compat-data@7.28.5': {} - '@babel/core@7.28.4': + '@babel/core@7.28.5': dependencies: '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.3 + '@babel/generator': 7.28.5 '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) '@babel/helpers': 7.28.4 - '@babel/parser': 7.28.4 + '@babel/parser': 7.28.5 '@babel/template': 7.27.2 - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3 @@ -3094,19 +3091,19 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.28.3': + '@babel/generator@7.28.5': dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.28.4 + '@babel/compat-data': 7.28.5 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.26.3 + browserslist: 4.27.0 lru-cache: 5.1.1 semver: 6.3.1 @@ -3114,17 +3111,17 @@ snapshots: '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-module-imports': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -3132,22 +3129,22 @@ snapshots: '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.27.1': {} + '@babel/helper-validator-identifier@7.28.5': {} '@babel/helper-validator-option@7.27.1': {} '@babel/helpers@7.28.4': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 - '@babel/parser@7.28.4': + '@babel/parser@7.28.5': dependencies: - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 - '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 '@babel/runtime@7.28.4': {} @@ -3155,25 +3152,25 @@ snapshots: '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 - '@babel/traverse@7.28.4': + '@babel/traverse@7.28.5': dependencies: '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.3 + '@babel/generator': 7.28.5 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.4 + '@babel/parser': 7.28.5 '@babel/template': 7.27.2 - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 debug: 4.4.3 transitivePeerDependencies: - supports-color - '@babel/types@7.28.4': + '@babel/types@7.28.5': dependencies: '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 '@emnapi/core@1.6.0': dependencies: @@ -3291,7 +3288,7 @@ snapshots: eslint: 9.38.0(jiti@2.6.1) eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.12.1': {} + '@eslint-community/regexpp@4.12.2': {} '@eslint/config-array@0.21.1': dependencies: @@ -3340,23 +3337,23 @@ snapshots: graphql: 16.11.0 tslib: 2.6.3 - '@graphql-codegen/cli@5.0.5(@types/node@22.18.10)(graphql-sock@1.0.1(graphql@16.11.0))(graphql@16.11.0)(typescript@5.8.3)': + '@graphql-codegen/cli@5.0.5(@types/node@22.18.13)(graphql-sock@1.0.1(graphql@16.11.0))(graphql@16.11.0)(typescript@5.8.3)': dependencies: - '@babel/generator': 7.28.3 + '@babel/generator': 7.28.5 '@babel/template': 7.27.2 - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 '@graphql-codegen/client-preset': 4.8.3(graphql-sock@1.0.1(graphql@16.11.0))(graphql@16.11.0) '@graphql-codegen/core': 4.0.2(graphql@16.11.0) '@graphql-codegen/plugin-helpers': 5.1.1(graphql@16.11.0) '@graphql-tools/apollo-engine-loader': 8.0.22(graphql@16.11.0) '@graphql-tools/code-file-loader': 8.1.22(graphql@16.11.0) '@graphql-tools/git-loader': 8.0.26(graphql@16.11.0) - '@graphql-tools/github-loader': 8.0.22(@types/node@22.18.10)(graphql@16.11.0) + '@graphql-tools/github-loader': 8.0.22(@types/node@22.18.13)(graphql@16.11.0) '@graphql-tools/graphql-file-loader': 8.1.2(graphql@16.11.0) '@graphql-tools/json-file-loader': 8.0.20(graphql@16.11.0) '@graphql-tools/load': 8.1.2(graphql@16.11.0) - '@graphql-tools/prisma-loader': 8.0.17(@types/node@22.18.10)(graphql@16.11.0) - '@graphql-tools/url-loader': 8.0.16(@types/node@22.18.10)(graphql@16.11.0) + '@graphql-tools/prisma-loader': 8.0.17(@types/node@22.18.13)(graphql@16.11.0) + '@graphql-tools/url-loader': 8.0.16(@types/node@22.18.13)(graphql@16.11.0) '@graphql-tools/utils': 10.9.1(graphql@16.11.0) '@whatwg-node/fetch': 0.10.11 chalk: 4.1.2 @@ -3364,8 +3361,8 @@ snapshots: debounce: 1.2.1 detect-indent: 6.1.0 graphql: 16.11.0 - graphql-config: 5.1.3(@types/node@22.18.10)(graphql@16.11.0)(typescript@5.8.3) - inquirer: 8.2.7(@types/node@22.18.10) + graphql-config: 5.1.3(@types/node@22.18.13)(graphql@16.11.0)(typescript@5.8.3) + inquirer: 8.2.7(@types/node@22.18.13) is-glob: 4.0.3 jiti: 1.21.7 json-to-pretty-yaml: 1.2.2 @@ -3583,7 +3580,7 @@ snapshots: - bufferutil - utf-8-validate - '@graphql-tools/executor-http@1.3.3(@types/node@22.18.10)(graphql@16.11.0)': + '@graphql-tools/executor-http@1.3.3(@types/node@22.18.13)(graphql@16.11.0)': dependencies: '@graphql-hive/signal': 1.0.0 '@graphql-tools/executor-common': 0.0.4(graphql@16.11.0) @@ -3593,7 +3590,7 @@ snapshots: '@whatwg-node/fetch': 0.10.11 '@whatwg-node/promise-helpers': 1.3.2 graphql: 16.11.0 - meros: 1.3.2(@types/node@22.18.10) + meros: 1.3.2(@types/node@22.18.13) tslib: 2.8.1 transitivePeerDependencies: - '@types/node' @@ -3632,9 +3629,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@graphql-tools/github-loader@8.0.22(@types/node@22.18.10)(graphql@16.11.0)': + '@graphql-tools/github-loader@8.0.22(@types/node@22.18.13)(graphql@16.11.0)': dependencies: - '@graphql-tools/executor-http': 1.3.3(@types/node@22.18.10)(graphql@16.11.0) + '@graphql-tools/executor-http': 1.3.3(@types/node@22.18.13)(graphql@16.11.0) '@graphql-tools/graphql-tag-pluck': 8.3.21(graphql@16.11.0) '@graphql-tools/utils': 10.9.1(graphql@16.11.0) '@whatwg-node/fetch': 0.10.11 @@ -3659,11 +3656,11 @@ snapshots: '@graphql-tools/graphql-tag-pluck@8.3.21(graphql@16.11.0)': dependencies: - '@babel/core': 7.28.4 - '@babel/parser': 7.28.4 - '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.28.4) - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/core': 7.28.5 + '@babel/parser': 7.28.5 + '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.28.5) + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 '@graphql-tools/utils': 10.9.1(graphql@16.11.0) graphql: 16.11.0 tslib: 2.8.1 @@ -3673,7 +3670,7 @@ snapshots: '@graphql-tools/import@7.1.2(graphql@16.11.0)': dependencies: '@graphql-tools/utils': 10.9.1(graphql@16.11.0) - '@theguild/federation-composition': 0.20.1(graphql@16.11.0) + '@theguild/federation-composition': 0.20.2(graphql@16.11.0) graphql: 16.11.0 resolve-from: 5.0.0 tslib: 2.8.1 @@ -3707,9 +3704,9 @@ snapshots: graphql: 16.11.0 tslib: 2.6.3 - '@graphql-tools/prisma-loader@8.0.17(@types/node@22.18.10)(graphql@16.11.0)': + '@graphql-tools/prisma-loader@8.0.17(@types/node@22.18.13)(graphql@16.11.0)': dependencies: - '@graphql-tools/url-loader': 8.0.16(@types/node@22.18.10)(graphql@16.11.0) + '@graphql-tools/url-loader': 8.0.16(@types/node@22.18.13)(graphql@16.11.0) '@graphql-tools/utils': 10.9.1(graphql@16.11.0) '@types/js-yaml': 4.0.9 '@whatwg-node/fetch': 0.10.11 @@ -3749,11 +3746,11 @@ snapshots: graphql: 16.11.0 tslib: 2.8.1 - '@graphql-tools/url-loader@8.0.16(@types/node@22.18.10)(graphql@16.11.0)': + '@graphql-tools/url-loader@8.0.16(@types/node@22.18.13)(graphql@16.11.0)': dependencies: '@ardatan/sync-fetch': 0.0.1 '@graphql-tools/executor-graphql-ws': 1.3.7(graphql@16.11.0) - '@graphql-tools/executor-http': 1.3.3(@types/node@22.18.10)(graphql@16.11.0) + '@graphql-tools/executor-http': 1.3.3(@types/node@22.18.13)(graphql@16.11.0) '@graphql-tools/executor-legacy-ws': 1.1.19(graphql@16.11.0) '@graphql-tools/utils': 10.9.1(graphql@16.11.0) '@graphql-tools/wrap': 10.1.4(graphql@16.11.0) @@ -3803,12 +3800,12 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@inquirer/external-editor@1.0.2(@types/node@22.18.10)': + '@inquirer/external-editor@1.0.2(@types/node@22.18.13)': dependencies: chardet: 2.1.0 iconv-lite: 0.7.0 optionalDependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.13 '@jridgewell/gen-mapping@0.3.13': dependencies: @@ -3852,70 +3849,70 @@ snapshots: '@repeaterjs/repeater@3.0.6': {} - '@rollup/rollup-android-arm-eabi@4.52.4': + '@rollup/rollup-android-arm-eabi@4.52.5': optional: true - '@rollup/rollup-android-arm64@4.52.4': + '@rollup/rollup-android-arm64@4.52.5': optional: true - '@rollup/rollup-darwin-arm64@4.52.4': + '@rollup/rollup-darwin-arm64@4.52.5': optional: true - '@rollup/rollup-darwin-x64@4.52.4': + '@rollup/rollup-darwin-x64@4.52.5': optional: true - '@rollup/rollup-freebsd-arm64@4.52.4': + '@rollup/rollup-freebsd-arm64@4.52.5': optional: true - '@rollup/rollup-freebsd-x64@4.52.4': + '@rollup/rollup-freebsd-x64@4.52.5': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.52.4': + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.52.4': + '@rollup/rollup-linux-arm-musleabihf@4.52.5': optional: true - '@rollup/rollup-linux-arm64-gnu@4.52.4': + '@rollup/rollup-linux-arm64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-arm64-musl@4.52.4': + '@rollup/rollup-linux-arm64-musl@4.52.5': optional: true - '@rollup/rollup-linux-loong64-gnu@4.52.4': + '@rollup/rollup-linux-loong64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.52.4': + '@rollup/rollup-linux-ppc64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.52.4': + '@rollup/rollup-linux-riscv64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-riscv64-musl@4.52.4': + '@rollup/rollup-linux-riscv64-musl@4.52.5': optional: true - '@rollup/rollup-linux-s390x-gnu@4.52.4': + '@rollup/rollup-linux-s390x-gnu@4.52.5': optional: true - '@rollup/rollup-linux-x64-gnu@4.52.4': + '@rollup/rollup-linux-x64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-x64-musl@4.52.4': + '@rollup/rollup-linux-x64-musl@4.52.5': optional: true - '@rollup/rollup-openharmony-arm64@4.52.4': + '@rollup/rollup-openharmony-arm64@4.52.5': optional: true - '@rollup/rollup-win32-arm64-msvc@4.52.4': + '@rollup/rollup-win32-arm64-msvc@4.52.5': optional: true - '@rollup/rollup-win32-ia32-msvc@4.52.4': + '@rollup/rollup-win32-ia32-msvc@4.52.5': optional: true - '@rollup/rollup-win32-x64-gnu@4.52.4': + '@rollup/rollup-win32-x64-gnu@4.52.5': optional: true - '@rollup/rollup-win32-x64-msvc@4.52.4': + '@rollup/rollup-win32-x64-msvc@4.52.5': optional: true '@shopify/eslint-plugin@50.0.0(@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(@typescript-eslint/utils@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(prettier@3.6.2)(typescript@5.9.3)': @@ -3956,14 +3953,14 @@ snapshots: - supports-color - typescript - '@shopify/shopify_function@2.0.0(@types/node@22.18.10)(graphql-sock@1.0.1(graphql@16.11.0))': + '@shopify/shopify_function@2.0.0(@types/node@22.18.13)(graphql-sock@1.0.1(graphql@16.11.0))': dependencies: - '@graphql-codegen/cli': 5.0.5(@types/node@22.18.10)(graphql-sock@1.0.1(graphql@16.11.0))(graphql@16.11.0)(typescript@5.8.3) + '@graphql-codegen/cli': 5.0.5(@types/node@22.18.13)(graphql-sock@1.0.1(graphql@16.11.0))(graphql@16.11.0)(typescript@5.8.3) '@graphql-codegen/typescript': 4.1.6(graphql@16.11.0) '@graphql-codegen/typescript-operations': 4.6.0(graphql-sock@1.0.1(graphql@16.11.0))(graphql@16.11.0) - '@graphql-tools/url-loader': 8.0.16(@types/node@22.18.10)(graphql@16.11.0) + '@graphql-tools/url-loader': 8.0.16(@types/node@22.18.13)(graphql@16.11.0) graphql: 16.11.0 - graphql-config: 5.1.3(@types/node@22.18.10)(graphql@16.11.0)(typescript@5.8.3) + graphql-config: 5.1.3(@types/node@22.18.13)(graphql@16.11.0)(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: - '@parcel/watcher' @@ -3976,10 +3973,10 @@ snapshots: - supports-color - utf-8-validate - '@theguild/federation-composition@0.20.1(graphql@16.11.0)': + '@theguild/federation-composition@0.20.2(graphql@16.11.0)': dependencies: constant-case: 3.0.4 - debug: 4.4.1 + debug: 4.4.3 graphql: 16.11.0 json5: 2.2.3 lodash.sortby: 4.7.0 @@ -3991,9 +3988,10 @@ snapshots: tslib: 2.8.1 optional: true - '@types/chai@5.2.2': + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 '@types/deep-eql@4.0.2': {} @@ -4003,17 +4001,17 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/node@22.18.10': + '@types/node@22.18.13': dependencies: undici-types: 6.21.0 '@types/ws@8.18.1': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.13 '@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@eslint-community/regexpp': 4.12.1 + '@eslint-community/regexpp': 4.12.2 '@typescript-eslint/parser': 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.46.2 '@typescript-eslint/type-utils': 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) @@ -4165,19 +4163,19 @@ snapshots: '@vitest/expect@3.2.4': dependencies: - '@types/chai': 5.2.2 + '@types/chai': 5.2.3 '@vitest/spy': 3.2.4 '@vitest/utils': 3.2.4 chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.1.10(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1))': + '@vitest/mocker@3.2.4(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 - magic-string: 0.30.19 + magic-string: 0.30.21 optionalDependencies: - vite: 7.1.10(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1) + vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(yaml@2.8.1) '@vitest/pretty-format@3.2.4': dependencies: @@ -4192,7 +4190,7 @@ snapshots: '@vitest/snapshot@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 - magic-string: 0.30.19 + magic-string: 0.30.21 pathe: 2.0.3 '@vitest/spy@3.2.4': @@ -4347,7 +4345,7 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.8.18: {} + baseline-browser-mapping@2.8.21: {} bl@4.1.0: dependencies: @@ -4368,13 +4366,13 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.26.3: + browserslist@4.27.0: dependencies: - baseline-browser-mapping: 2.8.18 + baseline-browser-mapping: 2.8.21 caniuse-lite: 1.0.30001751 - electron-to-chromium: 1.5.237 - node-releases: 2.0.25 - update-browserslist-db: 1.1.3(browserslist@4.26.3) + electron-to-chromium: 1.5.243 + node-releases: 2.0.27 + update-browserslist-db: 1.1.4(browserslist@4.27.0) bser@2.1.1: dependencies: @@ -4509,6 +4507,8 @@ snapshots: convert-source-map@2.0.0: {} + core-js@3.46.0: {} + cosmiconfig@8.3.6(typescript@5.8.3): dependencies: import-fresh: 3.3.1 @@ -4564,10 +4564,6 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.4.1: - dependencies: - ms: 2.1.3 - debug@4.4.3: dependencies: ms: 2.1.3 @@ -4619,7 +4615,7 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - electron-to-chromium@1.5.237: {} + electron-to-chromium@1.5.243: {} emoji-regex@8.0.0: {} @@ -4816,7 +4812,7 @@ snapshots: eslint-plugin-es-x@7.8.0(eslint@9.38.0(jiti@2.6.1)): dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.38.0(jiti@2.6.1)) - '@eslint-community/regexpp': 4.12.1 + '@eslint-community/regexpp': 4.12.2 eslint: 9.38.0(jiti@2.6.1) eslint-compat-utils: 0.5.1(eslint@9.38.0(jiti@2.6.1)) @@ -4947,7 +4943,7 @@ snapshots: eslint@9.38.0(jiti@2.6.1): dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.38.0(jiti@2.6.1)) - '@eslint-community/regexpp': 4.12.1 + '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.21.1 '@eslint/config-helpers': 0.4.1 '@eslint/core': 0.16.0 @@ -5172,13 +5168,13 @@ snapshots: graphemer@1.4.0: {} - graphql-config@5.1.3(@types/node@22.18.10)(graphql@16.11.0)(typescript@5.8.3): + graphql-config@5.1.3(@types/node@22.18.13)(graphql@16.11.0)(typescript@5.8.3): dependencies: '@graphql-tools/graphql-file-loader': 8.1.2(graphql@16.11.0) '@graphql-tools/json-file-loader': 8.0.20(graphql@16.11.0) '@graphql-tools/load': 8.1.2(graphql@16.11.0) '@graphql-tools/merge': 9.1.1(graphql@16.11.0) - '@graphql-tools/url-loader': 8.0.16(@types/node@22.18.10)(graphql@16.11.0) + '@graphql-tools/url-loader': 8.0.16(@types/node@22.18.13)(graphql@16.11.0) '@graphql-tools/utils': 10.9.1(graphql@16.11.0) cosmiconfig: 8.3.6(typescript@5.8.3) graphql: 16.11.0 @@ -5283,9 +5279,9 @@ snapshots: inherits@2.0.4: {} - inquirer@8.2.7(@types/node@22.18.10): + inquirer@8.2.7(@types/node@22.18.13): dependencies: - '@inquirer/external-editor': 1.0.2(@types/node@22.18.10) + '@inquirer/external-editor': 1.0.2(@types/node@22.18.13) ansi-escapes: 4.3.2 chalk: 4.1.2 cli-cursor: 3.1.0 @@ -5579,7 +5575,7 @@ snapshots: dependencies: yallist: 3.1.1 - magic-string@0.30.19: + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -5589,9 +5585,9 @@ snapshots: merge2@1.4.1: {} - meros@1.3.2(@types/node@22.18.10): + meros@1.3.2(@types/node@22.18.13): optionalDependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.13 micromatch@4.0.8: dependencies: @@ -5637,7 +5633,7 @@ snapshots: node-int64@0.4.0: {} - node-releases@2.0.25: {} + node-releases@2.0.27: {} normalize-path@2.1.1: dependencies: @@ -5881,32 +5877,32 @@ snapshots: rfdc@1.4.1: {} - rollup@4.52.4: + rollup@4.52.5: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.52.4 - '@rollup/rollup-android-arm64': 4.52.4 - '@rollup/rollup-darwin-arm64': 4.52.4 - '@rollup/rollup-darwin-x64': 4.52.4 - '@rollup/rollup-freebsd-arm64': 4.52.4 - '@rollup/rollup-freebsd-x64': 4.52.4 - '@rollup/rollup-linux-arm-gnueabihf': 4.52.4 - '@rollup/rollup-linux-arm-musleabihf': 4.52.4 - '@rollup/rollup-linux-arm64-gnu': 4.52.4 - '@rollup/rollup-linux-arm64-musl': 4.52.4 - '@rollup/rollup-linux-loong64-gnu': 4.52.4 - '@rollup/rollup-linux-ppc64-gnu': 4.52.4 - '@rollup/rollup-linux-riscv64-gnu': 4.52.4 - '@rollup/rollup-linux-riscv64-musl': 4.52.4 - '@rollup/rollup-linux-s390x-gnu': 4.52.4 - '@rollup/rollup-linux-x64-gnu': 4.52.4 - '@rollup/rollup-linux-x64-musl': 4.52.4 - '@rollup/rollup-openharmony-arm64': 4.52.4 - '@rollup/rollup-win32-arm64-msvc': 4.52.4 - '@rollup/rollup-win32-ia32-msvc': 4.52.4 - '@rollup/rollup-win32-x64-gnu': 4.52.4 - '@rollup/rollup-win32-x64-msvc': 4.52.4 + '@rollup/rollup-android-arm-eabi': 4.52.5 + '@rollup/rollup-android-arm64': 4.52.5 + '@rollup/rollup-darwin-arm64': 4.52.5 + '@rollup/rollup-darwin-x64': 4.52.5 + '@rollup/rollup-freebsd-arm64': 4.52.5 + '@rollup/rollup-freebsd-x64': 4.52.5 + '@rollup/rollup-linux-arm-gnueabihf': 4.52.5 + '@rollup/rollup-linux-arm-musleabihf': 4.52.5 + '@rollup/rollup-linux-arm64-gnu': 4.52.5 + '@rollup/rollup-linux-arm64-musl': 4.52.5 + '@rollup/rollup-linux-loong64-gnu': 4.52.5 + '@rollup/rollup-linux-ppc64-gnu': 4.52.5 + '@rollup/rollup-linux-riscv64-gnu': 4.52.5 + '@rollup/rollup-linux-riscv64-musl': 4.52.5 + '@rollup/rollup-linux-s390x-gnu': 4.52.5 + '@rollup/rollup-linux-x64-gnu': 4.52.5 + '@rollup/rollup-linux-x64-musl': 4.52.5 + '@rollup/rollup-openharmony-arm64': 4.52.5 + '@rollup/rollup-win32-arm64-msvc': 4.52.5 + '@rollup/rollup-win32-ia32-msvc': 4.52.5 + '@rollup/rollup-win32-x64-gnu': 4.52.5 + '@rollup/rollup-win32-x64-msvc': 4.52.5 fsevents: 2.3.3 run-async@2.4.1: {} @@ -6289,9 +6285,9 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - update-browserslist-db@1.1.3(browserslist@4.26.3): + update-browserslist-db@1.1.4(browserslist@4.27.0): dependencies: - browserslist: 4.26.3 + browserslist: 4.27.0 escalade: 3.2.0 picocolors: 1.1.1 @@ -6313,13 +6309,13 @@ snapshots: value-or-promise@1.0.12: {} - vite-node@3.2.4(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1): + vite-node@3.2.4(@types/node@22.18.13)(jiti@2.6.1)(yaml@2.8.1): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.10(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1) + vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti @@ -6334,25 +6330,25 @@ snapshots: - tsx - yaml - vite@7.1.10(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1): + vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(yaml@2.8.1): dependencies: esbuild: 0.25.11 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.52.4 + rollup: 4.52.5 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.13 fsevents: 2.3.3 jiti: 2.6.1 yaml: 2.8.1 - vitest@3.2.4(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1): + vitest@3.2.4(@types/node@22.18.13)(jiti@2.6.1)(yaml@2.8.1): dependencies: - '@types/chai': 5.2.2 + '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.10(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -6361,7 +6357,7 @@ snapshots: chai: 5.3.3 debug: 4.4.3 expect-type: 1.2.2 - magic-string: 0.30.19 + magic-string: 0.30.21 pathe: 2.0.3 picomatch: 4.0.3 std-env: 3.10.0 @@ -6370,11 +6366,11 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.10(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1) - vite-node: 3.2.4(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1) + vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@22.18.13)(jiti@2.6.1)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.13 transitivePeerDependencies: - jiti - less diff --git a/src/methods/validate-fixture-input.ts b/src/methods/validate-fixture-input.ts index c551274..c5a2921 100644 --- a/src/methods/validate-fixture-input.ts +++ b/src/methods/validate-fixture-input.ts @@ -1,15 +1,24 @@ -import { visit, DocumentNode, Kind } from "graphql"; -import { TypeInfo, visitWithTypeInfo, coerceInputValue } from "graphql"; +import "core-js/actual/set/intersection.js"; +import "core-js/actual/set/symmetric-difference.js"; import { + coerceInputValue, + DocumentNode, + getNamedType, + getNullableType, + GraphQLList, + GraphQLNamedType, + GraphQLSchema, + isAbstractType, isInputType, + isLeafType, isListType, isNullableType, - GraphQLSchema, - GraphQLList, - getNullableType, - isAbstractType, isObjectType, - getNamedType, + Kind, + TypeInfo, + visit, + visitWithTypeInfo, + BREAK, } from "graphql"; import { inlineNamedFragmentSpreads } from "../utils/inline-named-fragment-spreads.js"; @@ -37,10 +46,34 @@ export function validateFixtureInput( const inlineFragmentSpreadsAst = inlineNamedFragmentSpreads(queryAST); const typeInfo = new TypeInfo(schema); const valueStack: any[][] = [[value]]; + // based on field depth + const typeStack: (GraphQLNamedType | undefined)[] = []; + // based on selection set depth + const possibleTypesStack: Set[] = [new Set([schema.getQueryType()!.name])]; + const typenameResponseKeyStack: (string | undefined)[] = []; + const errors: string[] = []; + visit( inlineFragmentSpreadsAst, visitWithTypeInfo(typeInfo, { + InlineFragment: { + enter(node) { + let possibleTypes = new Set(possibleTypesStack[possibleTypesStack.length - 1]); + if (node.typeCondition !== null && node.typeCondition !== undefined) { + const namedType = schema.getType(node.typeCondition.name.value); + if (namedType && isAbstractType(namedType)) { + possibleTypes = possibleTypes.intersection(new Set(schema.getPossibleTypes(namedType).map(type => type.name))); + } else if (namedType && isObjectType(namedType)) { + possibleTypes = new Set([namedType.name]); + } + } + possibleTypesStack.push(possibleTypes); + }, + leave() { + possibleTypesStack.pop(); + }, + }, Field: { enter(node) { const currentValues = valueStack[valueStack.length - 1]; @@ -49,14 +82,24 @@ export function validateFixtureInput( const responseKey = node.alias?.value || node.name.value; const fieldDefinition = typeInfo.getFieldDef(); - const fieldType = fieldDefinition?.type; + if (fieldDefinition === undefined || fieldDefinition === null) { + errors.push(`Cannot validate ${responseKey}: missing field definition`); + return BREAK; + } + const fieldType = fieldDefinition.type; for (const currentValue of currentValues) { const valueForResponseKey = currentValue[responseKey]; // Field is missing from fixture if (valueForResponseKey === undefined) { - errors.push(`Missing expected fixture data for ${responseKey}`); + const typenameResponseKey = typenameResponseKeyStack[typenameResponseKeyStack.length - 1]; + const parentFieldType = typeStack[typeStack.length - 1]!; + const possibleTypes = possibleTypesStack[possibleTypesStack.length - 1]; + if (isValueExpectedForType(currentValue, parentFieldType, possibleTypes, schema, typenameResponseKey)) { + errors.push(`Missing expected fixture data for ${responseKey}`); + } + } // Scalars and Enums (including wrapped types) else if (isInputType(fieldType)) { @@ -100,10 +143,6 @@ export function validateFixtureInput( } } // Objects - validate and add to traversal stack - // Note: Abstract types (unions/interfaces) are handled in a limited way. - // We add them to the traversal stack but don't use __typename to discriminate - // between concrete types. This works for simple cases where all items are the - // same type, but doesn't support mixed-type arrays (see skipped test). else if (isObjectType(unwrappedFieldType) || isAbstractType(unwrappedFieldType)) { if (valueForResponseKey === null) { errors.push(`Expected object for ${responseKey}, but got null`); @@ -124,14 +163,49 @@ export function validateFixtureInput( } } + const namedType = getNamedType(fieldType); + let possibleTypes: string[] = []; + if (isLeafType(namedType)) { + // do nothing + } else if (isAbstractType(namedType)) { + possibleTypes = schema.getPossibleTypes(namedType).map(type => type.name); + } else if (isObjectType(namedType)) { + possibleTypes = [namedType.name]; + } + + possibleTypesStack.push(new Set(possibleTypes)); + typeStack.push(getNamedType(fieldType)); + valueStack.push(nestedValues); }, leave() { valueStack.pop(); + typeStack.pop(); + possibleTypesStack.pop(); }, }, SelectionSet: { - enter(node) { + enter(node, _key, parent) { + // Look ahead to find __typename field and track its response key + const typenameField = node.selections.find( + (selection) => + selection.kind === Kind.FIELD && + selection.name.value === "__typename" + ); + + let typenameResponseKey: string | undefined; + if (typenameField?.kind === Kind.FIELD) { + typenameResponseKey = typenameField.alias?.value || "__typename"; + } else if (parent && 'kind' in parent && parent.kind === Kind.INLINE_FRAGMENT) { + // Inside an inline fragment without __typename - inherit from parent SelectionSet + typenameResponseKey = typenameResponseKeyStack[typenameResponseKeyStack.length - 1]; + } else { + // Field SelectionSet or root level - don't inherit (new object context) + typenameResponseKey = undefined; + } + + typenameResponseKeyStack.push(typenameResponseKey); + if (isAbstractType(getNamedType(typeInfo.getType()))) { const hasTypename = node.selections.some( (selection) => @@ -149,9 +223,13 @@ export function validateFixtureInput( errors.push( `Missing __typename field for abstract type ${getNamedType(typeInfo.getType())?.name}` ); + return BREAK; } } }, + leave() { + typenameResponseKeyStack.pop(); + }, }, }) ); @@ -219,4 +297,59 @@ function processNestedArrays( } return { values: result, errors }; -} \ No newline at end of file +} + +/** + * Determines if a fixture value is expected + * + * @param fixtureValue - The fixture value to check + * @param parentFieldType - The type returned by the parent field (e.g., InterfaceImplementersUnion from `interfaceImplementers` field) + * @param possibleTypes - Set of possible type names after inline fragment narrowing (e.g., {"InterfaceImplementer1"} after `...on HasDescription`) + * @param schema - The GraphQL schema to resolve possible types for abstract types + * @param typenameKey - The response key for the __typename field (supports aliases like `type: __typename`) + * @returns True if the value is expected (field should be present), false otherwise (field can be skipped) + * + * @remarks + * When __typename is selected: + * - Checks if value's __typename is in the possibleTypes set + * + * When __typename is NOT selected: + * - Compares parent field's possible types vs current possibleTypes + * - If sets differ (narrowing occurred), empty objects {} are valid (return false) + * - Non-empty objects conservatively expect all fields (return true) + * - Since typeStack only tracks fields (not inline fragments), parentFieldType is the original + * field's type, enabling correct narrowing detection at any nesting depth + */ +function isValueExpectedForType( + fixtureValue: any, + parentFieldType: GraphQLNamedType, + possibleTypes: Set, + schema: GraphQLSchema, + typenameKey?: string +): boolean { + if (!typenameKey) { + let parentFieldPossibleTypes: string[] = []; + if (isAbstractType(parentFieldType)) { + parentFieldPossibleTypes = schema.getPossibleTypes(parentFieldType).map(type => type.name); + } else { + parentFieldPossibleTypes = [parentFieldType.name]; + } + + const parentFieldPossibleTypesSet = new Set(parentFieldPossibleTypes); + const difference = parentFieldPossibleTypesSet.symmetricDifference(possibleTypes); + + if (difference.size > 0 && Object.keys(fixtureValue).length === 0) { + return false; + } + + return true; // Otherwise, expect all values + } + + const valueTypename = fixtureValue[typenameKey]; + if (!valueTypename) { + // No __typename in value - can't discriminate, so expect it + return true; + } + + return possibleTypes.has(valueTypename); +} diff --git a/test/fixtures/test-schema.graphql b/test/fixtures/test-schema.graphql index 32b6987..7f1c6e3 100644 --- a/test/fixtures/test-schema.graphql +++ b/test/fixtures/test-schema.graphql @@ -15,10 +15,75 @@ type DataContainer { searchResults: [SearchResult!]! itemMatrix: [[Item]] metadataCube: [[[Metadata]]] + products: [Product!]! + purchasable: Purchasable + interfaceImplementers: [InterfaceImplementersUnion!]! + nested: [NestedOuter!]! } union SearchResult = Item | Metadata +union Product = PhysicalProduct | DigitalProduct | GiftCard + +interface Purchasable { + price: Int! + currency: String! +} + +interface HasId { + id: ID! +} + +interface HasName { + name: String! +} + +interface HasDescription { + description: String! +} + +type InterfaceImplementer1 implements HasId & HasName & HasDescription { + id: ID! + name: String! + description: String! +} + +type InterfaceImplementer2 implements HasId & HasName { + id: ID! + name: String! +} + +type InterfaceImplementer3 implements HasId { + id: ID! +} + +type NoInterfacesImplemented { + value: String +} + +union InterfaceImplementersUnion = InterfaceImplementer1 | InterfaceImplementer2 | InterfaceImplementer3 | NoInterfacesImplemented + +union NestedOuter = NestedOuterA | NestedOuterB + +type NestedOuterA { + id: ID + inner: [NestedInner!]! +} + +type NestedOuterB { + email: String +} + +union NestedInner = NestedInnerA | NestedInnerB + +type NestedInnerA { + name: String! +} + +type NestedInnerB { + value: String! +} + type Item { id: ID count: Int! @@ -78,4 +143,23 @@ input HttpRequest { method: String! headers: String body: String +} + +type PhysicalProduct implements Purchasable { + price: Int! + currency: String! + weight: Int! + sku: String! +} + +type DigitalProduct implements Purchasable { + price: Int! + currency: String! + downloadUrl: String! + fileSize: Int! +} + +type GiftCard { + code: String! + balance: Int! } \ No newline at end of file diff --git a/test/methods/validate-fixture-input.test.ts b/test/methods/validate-fixture-input.test.ts index 51b4d5f..df6bf24 100644 --- a/test/methods/validate-fixture-input.test.ts +++ b/test/methods/validate-fixture-input.test.ts @@ -129,11 +129,7 @@ describe("validateFixtureInput", () => { expect(result.errors).toHaveLength(0); }); - // This test is skipped because the validator doesn't yet support unions where - // different items in the array can be different types. Currently, it expects - // all fields from all inline fragments to be present in every item, instead of - // filtering by __typename. - it.skip("handles inline fragments with multiple types in union", () => { + it("handles inline fragments with multiple types in union", () => { const queryAST = parse(` query { data { @@ -174,6 +170,522 @@ describe("validateFixtureInput", () => { expect(result.errors).toHaveLength(0); }); + it("handles aliased __typename in inline fragments", () => { + const queryAST = parse(` + query { + data { + searchResults { + type: __typename + ... on Item { + id + count + } + ... on Metadata { + email + phone + } + } + } + } + `); + + const fixtureInput = { + data: { + searchResults: [ + { + type: "Item", + id: "gid://test/Item/1", + count: 5 + }, + { + type: "Metadata", + email: "test@example.com", + phone: "555-0001" + } + ] + } + }; + + const result = validateFixtureInput(queryAST, schema, fixtureInput); + + expect(result.errors).toHaveLength(0); + }); + + it("should not inherit typename key across field boundaries", () => { + const queryAST = parse(` + query { + data { + nested { + outerType: __typename + ... on NestedOuterA { + id + inner { + ... on NestedInnerA { + name + } + ... on NestedInnerB { + value + } + } + } + } + } + } + `); + + const fixtureInput = { + data: { + nested: [ + { + outerType: "NestedOuterA", + id: "1", + inner: [ + { + name: "Inner name" + // No __typename - query doesn't select it for inner + } + ] + } + ] + } + }; + + const result = validateFixtureInput(queryAST, schema, fixtureInput); + + // - inner SelectionSet has typenameResponseKey = undefined (doesn't inherit "outerType") + // - Detects missing __typename and BREAKs early + expect(result.errors).toHaveLength(1); + expect(result.errors[0]).toBe("Missing __typename field for abstract type NestedInner"); + }); + + it("handles nested unions with typename at each level", () => { + // Same structure as previous test, but WITH __typename at inner level + const queryAST = parse(` + query { + data { + nested { + outerType: __typename + ... on NestedOuterA { + id + inner { + __typename + ... on NestedInnerA { + name + } + ... on NestedInnerB { + value + } + } + } + } + } + } + `); + + const fixtureInput = { + data: { + nested: [ + { + outerType: "NestedOuterA", + id: "1", + inner: [ + { + __typename: "NestedInnerA", + name: "Inner name" + } + ] + } + ] + } + }; + + const result = validateFixtureInput(queryAST, schema, fixtureInput); + + // With __typename selected at inner level, validation works correctly + // - outer uses "outerType" alias + // - inner uses "__typename" (not inherited) + // - Each level properly scoped to its field + expect(result.errors).toHaveLength(0); + }); + + it("handles inline fragment on interface type", () => { + const queryAST = parse(` + query { + data { + products { + __typename + ... on Purchasable { + price + currency + } + ... on GiftCard { + code + balance + } + } + } + } + `); + + const fixtureInput = { + data: { + products: [ + { + __typename: "PhysicalProduct", + price: 1000, + currency: "USD" + }, + { + __typename: "DigitalProduct", + price: 500, + currency: "USD" + }, + { + __typename: "GiftCard", + code: "GIFT123", + balance: 5000 + } + ] + } + }; + + const result = validateFixtureInput(queryAST, schema, fixtureInput); + + expect(result.errors).toHaveLength(0); + }); + + it("handles single inline fragment on union without __typename", () => { + const queryAST = parse(` + query { + data { + searchResults { + ... on Item { + id + count + } + } + } + } + `); + + const fixtureInput = { + data: { + searchResults: [ + { + id: "gid://test/Item/1", + count: 5 + } + ] + } + }; + + const result = validateFixtureInput(queryAST, schema, fixtureInput); + + // With only one inline fragment, no __typename is needed for discrimination + expect(result.errors).toHaveLength(0); + }); + + it("handles empty objects in union when inline fragment doesn't match", () => { + const queryAST = parse(` + query { + data { + searchResults { + ... on Item { + id + count + } + } + } + } + `); + + const fixtureInput = { + data: { + searchResults: [ + { + id: "gid://test/Item/1", + count: 5 + }, + {} // Empty object - represents Metadata that didn't match the Item fragment + ] + } + }; + + const result = validateFixtureInput(queryAST, schema, fixtureInput); + + // Empty object {} is valid - GraphQL returns this for union members that don't match any fragments + expect(result.errors).toHaveLength(0); + }); + + it("handles empty objects when narrowing from union to interface subset", () => { + const queryAST = parse(` + query { + data { + products { + ... on Purchasable { + price + currency + } + } + } + } + `); + + const fixtureInput = { + data: { + products: [ + { + price: 1000, + currency: "USD" + }, + {} // Empty object - GiftCard that doesn't implement Purchasable + ] + } + }; + + const result = validateFixtureInput(queryAST, schema, fixtureInput); + + // Empty object {} is valid + // Grandparent (Product union) has 3 types: {PhysicalProduct, DigitalProduct, GiftCard} + // Parent (Purchasable interface) has 2 types: {PhysicalProduct, DigitalProduct} + // Sets are different → type was narrowed → empty object represents GiftCard + expect(result.errors).toHaveLength(0); + }); + + it("handles deeply nested interface fragments with empty objects", () => { + const queryAST = parse(` + query { + data { + interfaceImplementers { + ...on HasId { + id + ...on HasName { + name + ...on HasDescription { + description + } + } + } + } + } + } + `); + + const fixtureInput = { + data: { + interfaceImplementers: [ + { + id: "1", + name: "Implementer1", + description: "Has all three interfaces" + }, + { + id: "2", + name: "Implementer2", + description: "Also has all three" + }, + {} + // Empty object - NoInterfacesImplemented that doesn't implement any interface + // Valid because InterfaceImplementersUnion {1,2,3,4} was narrowed to HasId {1,2,3} + ] + } + }; + + const result = validateFixtureInput(queryAST, schema, fixtureInput); + + // Empty object is valid - demonstrates narrowing through 3 nested interface fragments + // Progressive narrowing: InterfaceImplementersUnion {1,2,3,4} → HasId {1,2,3} → HasName {1,2} → HasDescription {1} + // parentFieldType = InterfaceImplementersUnion (4 types), parentType = HasId (3 types after first fragment) + // Sets are different → type was narrowed → empty object valid + expect(result.errors).toHaveLength(0); + }); + + it("handles deeply nested fragments with field only at innermost level", () => { + const queryAST = parse(` + query { + data { + interfaceImplementers { + ...on HasId { + ...on HasName { + ...on HasDescription { + description + } + } + } + } + } + } + `); + + const fixtureInput = { + data: { + interfaceImplementers: [ + { + description: "Implementer1 - implements all three" + }, + {}, + {}, + {} + // Three empty objects representing Implementer2, 3, 4 that don't implement HasDescription + ] + } + }; + + const result = validateFixtureInput(queryAST, schema, fixtureInput); + + // All empty objects are valid - they don't match the HasDescription fragment + // parentFieldType = InterfaceImplementersUnion (4 types) + // parentType = HasDescription (1 type) + // Sets are different → narrowing detected → empty objects valid + expect(result.errors).toHaveLength(0); + }); + + it("handles nested interface fragments with __typename and partial field sets", () => { + const queryAST = parse(` + query { + data { + interfaceImplementers { + __typename + ...on HasId { + id + ...on HasName { + name + ...on HasDescription { + description + } + } + } + } + } + } + `); + + const fixtureInput = { + data: { + interfaceImplementers: [ + { + __typename: "InterfaceImplementer1", + id: "1", + name: "Implementer1", + description: "Implements all three" + }, + { + __typename: "InterfaceImplementer2", + id: "2", + name: "Implementer2" + // Implements HasId & HasName, but not HasDescription + }, + { + __typename: "InterfaceImplementer3", + id: "3" + // Implements HasId only + }, + { + __typename: "NoInterfacesImplemented" + // Doesn't implement any interface + } + ] + } + }; + + const result = validateFixtureInput(queryAST, schema, fixtureInput); + + // With __typename, validator correctly discriminates which fields are expected for each type + expect(result.errors).toHaveLength(0); + }); + + it("requires __typename for nested interface fragments with partial field sets", () => { + const queryAST = parse(` + query { + data { + interfaceImplementers { + ...on HasId { + id + ...on HasName { + name + ...on HasDescription { + description + } + } + } + } + } + } + `); + + const fixtureInput = { + data: { + interfaceImplementers: [ + { + id: "1", + name: "Implementer1", + description: "Implements all three" + }, + { + id: "2", + name: "Implementer2" + // InterfaceImplementer2: implements HasId & HasName, but not HasDescription + // This is a valid response - nested fragment doesn't match + }, + { + id: "3" + // InterfaceImplementer3: implements HasId only + // This is a valid response - nested fragments don't match + }, + {} + // NoInterfacesImplemented: doesn't implement any interface + // Empty object is valid - handled by empty object logic + ] + } + }; + + const result = validateFixtureInput(queryAST, schema, fixtureInput); + + // Without __typename, validator cannot determine if missing fields are valid + // (due to type not implementing nested interfaces) or invalid (incomplete data) + // So it conservatively expects all selected fields on non-empty objects + expect(result.errors).toHaveLength(3); + expect(result.errors[0]).toBe("Missing expected fixture data for name"); + expect(result.errors[1]).toBe("Missing expected fixture data for description"); + expect(result.errors[2]).toBe("Missing expected fixture data for description"); + }); + + it("handles objects with only __typename when inline fragment doesn't match", () => { + const queryAST = parse(` + query { + data { + searchResults { + __typename + ... on Item { + id + count + } + } + } + } + `); + + const fixtureInput = { + data: { + searchResults: [ + { + __typename: "Item", + id: "gid://test/Item/1", + count: 5 + }, + { + __typename: "Metadata" // Only typename, no other fields + } + ] + } + }; + + const result = validateFixtureInput(queryAST, schema, fixtureInput); + + // Object with only __typename is valid - Metadata doesn't match the Item fragment + expect(result.errors).toHaveLength(0); + }); + it("handles nested inline fragments", () => { const queryAST = parse(` query { @@ -936,7 +1448,143 @@ describe("validateFixtureInput", () => { // Should detect missing type information for the invalid field expect(result.errors).toHaveLength(1); - expect(result.errors[0]).toBe('Cannot validate nonExistentField: missing type information'); + expect(result.errors[0]).toBe('Cannot validate nonExistentField: missing field definition'); + }); + + it("detects empty objects in non-union context", () => { + const queryAST = parse(` + query { + data { + items { + id + count + } + } + } + `); + + const fixtureInput = { + data: { + items: [ + { + id: "gid://test/Item/1", + count: 5 + }, + {} // Empty object in non-union context - should error + ] + } + }; + + const result = validateFixtureInput(queryAST, schema, fixtureInput); + + // Empty object {} is invalid in non-union context - missing required fields + expect(result.errors).toHaveLength(2); + expect(result.errors[0]).toBe("Missing expected fixture data for id"); + expect(result.errors[1]).toBe("Missing expected fixture data for count"); + }); + + it("detects empty objects when inline fragment is on same type as field", () => { + const queryAST = parse(` + query { + data { + purchasable { + ... on Purchasable { + price + } + } + } + } + `); + + const fixtureInput = { + data: { + purchasable: {} // Empty object when selecting on interface itself - should error + } + }; + + const result = validateFixtureInput(queryAST, schema, fixtureInput); + + // Empty object {} is invalid when inline fragment is on the same type as the field + // We're not discriminating between union members, so all fields are required + expect(result.errors).toHaveLength(1); + expect(result.errors[0]).toBe("Missing expected fixture data for price"); + }); + + it("handles multiple inline fragments on same type without typename", () => { + const queryAST = parse(` + query { + data { + searchResults { + ... on Item { + id + } + ... on Item { + count + } + } + } + } + `); + + const fixtureInput = { + data: { + searchResults: [ + { + id: "gid://test/Item/1", + count: 5 + } + ] + } + }; + + const result = validateFixtureInput(queryAST, schema, fixtureInput); + + // Multiple fragments but all on the same type (Item) + // Still errors on missing __typename because fragmentSpreadCount > 1 + // However, NO cascading field errors because all fragments select on same type + expect(result.errors).toHaveLength(1); + expect(result.errors[0]).toBe("Missing __typename field for abstract type SearchResult"); + }); + + it("detects missing fields when __typename is not selected in union with inline fragments", () => { + const queryAST = parse(` + query { + data { + searchResults { + ... on Item { + id + count + } + ... on Metadata { + email + phone + } + } + } + } + `); + + const fixtureInput = { + data: { + searchResults: [ + { + id: "gid://test/Item/1", + count: 5 + }, + { + email: "test@example.com", + phone: "555-0001" + } + ] + } + }; + + const result = validateFixtureInput(queryAST, schema, fixtureInput); + + // Without __typename, we can't discriminate which fields are expected for each object + // Validator detects missing __typename for abstract type with 2+ fragments and BREAKs early + expect(result.errors).toHaveLength(1); + expect(result.errors[0]).toBe("Missing __typename field for abstract type SearchResult"); }); }); }); diff --git a/tsconfig.json b/tsconfig.json index dec559e..7905eb2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { "target": "ES2020", + "lib": ["ES2020", "ESNext.Collection"], "module": "ESNext", "moduleResolution": "Node", "outDir": "./dist",