diff --git a/COMMANDS.md b/COMMANDS.md index c53e6ad..1df8b84 100644 --- a/COMMANDS.md +++ b/COMMANDS.md @@ -1,4 +1,4 @@ -# Available Commands (111) +# Available Commands (120) All commands follow the pattern: `iterable [options]` @@ -432,7 +432,72 @@ iterable events track-bulk --events | `--events` | json[] | **yes** | | -## experiments (4) +## experiments (13) + +### cancel + +Cancel a running or winner-found campaign experiment without declaring a winner + +``` +iterable experiments cancel +``` + +| Option | Type | Required | Description | +|--------|------|----------|-------------| +| `experimentId` | number | **yes** | Experiment ID | + +### copy-variant + +Copy a template into a new variant on a draft or ready campaign experiment + +``` +iterable experiments copy-variant --copyFromTemplateId --experimentId +``` + +| Option | Type | Required | Description | +|--------|------|----------|-------------| +| `--copyFromTemplateId` | number | **yes** | Template ID to copy into a new variant | +| `--experimentId` | number | **yes** | Experiment ID | +| `--name` | string | no | Display name for the new variant | + +### create + +Create a draft campaign experiment. The campaign template becomes the control. + +``` +iterable experiments create --campaignId --experimentType +``` + +| Option | Type | Required | Description | +|--------|------|----------|-------------| +| `--campaignId` | number | **yes** | Campaign ID | +| `--experimentType` | `SubjectLine` \| `PreheaderText` \| `FromNameAndSender` \| `EmailBody` \| `WholeEmail` | **yes** | What to test. Send-time, send-time optimization, and frequency optimization are not supported. | +| `--name` | string | no | Display name. If omitted, Iterable uses '{campaign name} Experiment'. | + +### declare-winner + +Declare a winning variant and end a running or winner-found campaign experiment + +``` +iterable experiments declare-winner --experimentId --variantId +``` + +| Option | Type | Required | Description | +|--------|------|----------|-------------| +| `--experimentId` | number | **yes** | Experiment ID | +| `--variantId` | number | **yes** | Variant ID to declare as the winner. The holdout group cannot be selected. | + +### delete + +Delete a draft, ready, or errored campaign experiment + +``` +iterable experiments delete +``` + +| Option | Type | Required | Description | +|--------|------|----------|-------------| +| `experimentId` | number | **yes** | Experiment ID | ### get @@ -461,6 +526,32 @@ iterable experiments get-metrics | `--experimentId` | number[] | no | Experiment IDs to export (can specify multiple) | | `--startDateTime` | string | no | Export starting from (ISO 8601 format) | +### get-totals + +Get lifetime send and conversion totals for a campaign experiment + +``` +iterable experiments get-totals +``` + +| Option | Type | Required | Description | +|--------|------|----------|-------------| +| `experimentId` | number | **yes** | Experiment ID | + +### get-trends + +Get the performance time series for a campaign experiment. Provide both start and end times, or omit both to use the run window. + +``` +iterable experiments get-trends +``` + +| Option | Type | Required | Description | +|--------|------|----------|-------------| +| `experimentId` | number | **yes** | Experiment ID | +| `--endDateTime` | string | no | Series end (ISO 8601). Required when startDateTime is set. | +| `--startDateTime` | string | no | Series start (ISO 8601). Required when endDateTime is set. | + ### get-variants Get variant content for an experiment, including subject lines, preheaders, HTML source, and plain text @@ -490,6 +581,35 @@ iterable experiments list | `--startDate` | string | no | Filter experiments starting from this date (ISO 8601 format) | | `--status` | `draft` \| `running` \| `finished` | no | Filter by status (draft, running, finished) | +### start + +Start a draft or ready campaign experiment + +``` +iterable experiments start +``` + +| Option | Type | Required | Description | +|--------|------|----------|-------------| +| `experimentId` | number | **yes** | Experiment ID | + +### update-settings + +Update settings on a draft or ready campaign experiment. Omit a field to leave it unchanged. + +``` +iterable experiments update-settings +``` + +| Option | Type | Required | Description | +|--------|------|----------|-------------| +| `experimentId` | number | **yes** | Experiment ID | +| `--conversionEventSettings` | json | no | Conversion settings. Omit to leave unchanged. (JSON: {systemConversionEventType, customConversionId}) | +| `--evenlySplitVariations` | boolean | no | Set true to split traffic evenly and clear blast and trigger settings. false is rejected. Omit to leave allocation unchanged. | +| `--explorationBlastSettings` | json | no | Blast exploration settings. Null removes them. Omit to leave unchanged. (JSON: {testSizePercentage, testDurationMinutes}) | +| `--explorationTriggerSettings` | json | no | Trigger exploration settings. Null removes them. Omit to leave unchanged. (JSON: {testSendsPerVariation}) | +| `--holdoutSettings` | json | no | Holdout settings. Null removes them. Omit to leave unchanged. (JSON: {holdOutGroupSize, attributionPeriodHours}) | + ## export (4) diff --git a/package.json b/package.json index a980557..fe3a908 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "update-commands": "node scripts/update-commands.js" }, "dependencies": { - "@iterable/api": "0.12.0", + "@iterable/api": "0.13.1", "@primno/dpapi": "2.0.1", "boxen": "8.0.1", "chalk": "5.6.2", @@ -70,7 +70,7 @@ "ora": "9.3.0", "package-manager-detector": "1.6.0", "semver": "7.7.4", - "zod": "4.3.6", + "zod": "4.6.7", "zod-opts": "1.0.0" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b1c3732..5171b80 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@iterable/api': - specifier: 0.12.0 - version: 0.12.0(typescript@6.0.2) + specifier: 0.13.1 + version: 0.13.1(typescript@6.0.2) '@primno/dpapi': specifier: 2.0.1 version: 2.0.1 @@ -39,11 +39,11 @@ importers: specifier: 7.7.4 version: 7.7.4 zod: - specifier: 4.3.6 - version: 4.3.6 + specifier: 4.6.7 + version: 4.6.7 zod-opts: specifier: 1.0.0 - version: 1.0.0(zod@4.3.6) + version: 1.0.0(zod@4.6.7) devDependencies: '@eslint/js': specifier: 10.0.1 @@ -103,32 +103,40 @@ packages: resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.29.0': - resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} - '@babel/core@7.29.0': - resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.13': + resolution: {integrity: sha512-j1nvOQCZYSgp7HLmisnCXFTpVvFODiijQ9QHkORfGKWH6YDtskTEeCJS7eV545GZ9UBzrq0e2xsOd5QQ5PjIqw==} engines: {node: '>=6.9.0'} '@babel/generator@7.29.1': resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.28.6': - resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + '@babel/generator@7.29.13': + resolution: {integrity: sha512-T5FRo+RzVHvGjWOuF1Ljc44b+NXAOazodHkGUHp4I3zCkQ4/sNVJm56h1S278LMR0bY5xBhztGziSbli8zObIw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.13': + resolution: {integrity: sha512-8WxJ4/Vmes3ghacj8VOJy3NW8eyHiFY/4thU/LSBuBE470+igjR/j1ZTNn8fV5VdNrLx6iXUwig526UdOvfP0w==} engines: {node: '>=6.9.0'} - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.28.6': - resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.28.6': - resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + '@babel/helper-module-transforms@7.29.13': + resolution: {integrity: sha512-/U96wOz+SCoZ7Da/8kjDGltcNY280JPQcRNPK0Hk3PTbdpaiBDQImtC6JsbVwb1HGMr3usZQU2WsWVjhYNmRzw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -141,18 +149,31 @@ packages: resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.29.13': + resolution: {integrity: sha512-fWegr+WsEsiMVW4KUNdIIzzMTZSzNiqhy0//6+2MRi2d5NFNsL/AYwBQW8eBnRbTBVRnDVzjaJEUqKaCWjMMdQ==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.29.2': - resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + '@babel/helpers@7.29.13': + resolution: {integrity: sha512-laoRpD/kO/iA4CDlgJbixHekg7oPvWALK0PQyK3Ob2I2njZRvVAD77RfWCIImtqWCq6KijoBoZK7bp/HlTWASQ==} engines: {node: '>=6.9.0'} + '@babel/parser@7.29.13': + resolution: {integrity: sha512-fJM50YYjlTiBhgdkGmC1tBpv+FMAyo4annf2X6Aks+0yRlaiVkYW/3f07xAfVVkYIoZ9fFZp+mEThUFnuybhkQ==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/parser@7.29.2': resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} engines: {node: '>=6.0.0'} @@ -249,18 +270,22 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/template@7.28.6': - resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.29.0': - resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + '@babel/traverse@7.29.13': + resolution: {integrity: sha512-0ve0xMlFA73dgY1oyJRY6RoW+/bncdcOQqg6jjYcVfXBK90OdLXjm9R6lmRetdZVrPjXkKIoMVBqYFPjcAlZ4Q==} engines: {node: '>=6.9.0'} '@babel/types@7.29.0': resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.11': + resolution: {integrity: sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==} + engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -479,12 +504,16 @@ packages: resolution: {integrity: sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@humanfs/core@0.19.1': - resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} engines: {node: '>=18.18.0'} - '@humanfs/node@0.16.7': - resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': @@ -641,8 +670,8 @@ packages: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} - '@iterable/api@0.12.0': - resolution: {integrity: sha512-EW4Uwbr1wWJ7sh5pi39b/U2aQvdQy62psu0ZMVqoO3j5lG1VegbOXD6FHM9FNuLRQAq8TuE5Gy9q/BKCIQHFrg==} + '@iterable/api@0.13.1': + resolution: {integrity: sha512-SeWCHdVXKYRx9sbaTiry2TazcV0NUD5L2BsePEVvz0hjG/eACoTEF5np9zInFoWY4Tt0+OiK5Qqw46YX62U10Q==} engines: {node: '>=18.0.0'} '@jest/console@30.3.0': @@ -1114,6 +1143,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + ajv@6.14.0: resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} @@ -1157,8 +1190,8 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - axios@1.13.6: - resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==} + axios@1.21.0: + resolution: {integrity: sha512-YCzXMB53Q9PZBsh7yN7wtapit4WepwcHS2fP8QhGsBxSz5YtgYobV7GGlWlAJi+RxfU77GQTQi2DSLUma42EYA==} babel-jest@30.3.0: resolution: {integrity: sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==} @@ -1192,8 +1225,8 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} - baseline-browser-mapping@2.10.10: - resolution: {integrity: sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ==} + baseline-browser-mapping@2.11.35: + resolution: {integrity: sha512-GNk8KX52CHqeNKGk8+7tZnQab/2k1t/BgrUHPKNdijzpEhF1saPQZHVKb6m5loSAxB39gyrTYw34Qf9CQ3u1qQ==} engines: {node: '>=6.0.0'} hasBin: true @@ -1201,18 +1234,18 @@ packages: resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} engines: {node: '>=18'} - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + brace-expansion@1.1.21: + resolution: {integrity: sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==} - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + brace-expansion@2.1.7: + resolution: {integrity: sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==} - brace-expansion@5.0.5: - resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} - engines: {node: 18 || 20 || >=22} + brace-expansion@5.0.12: + resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} + engines: {node: 20 || >=22} - browserslist@4.28.1: - resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + browserslist@4.29.5: + resolution: {integrity: sha512-khTmKu5NGYTotqcYo8Bew2k4RAE9PuXRSFRXG2lQBrCEQHyeQ/vX6gKMxfokwywBsLiXZMnHsmWMG1pnO8J7uQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1242,8 +1275,8 @@ packages: resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} engines: {node: '>=16'} - caniuse-lite@1.0.30001781: - resolution: {integrity: sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==} + caniuse-lite@1.0.30001823: + resolution: {integrity: sha512-TQ5s/djDjNk1axreUW+4rULH+BmdCMLiJkhaXnO5wqivG7tITkSUj4ItIdzqdZHKaXcHAV8dhH3JxOM7/7bwqw==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -1335,8 +1368,8 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - csv-parse@6.2.1: - resolution: {integrity: sha512-LRLMV+UCyfMokp8Wb411duBf1gaBKJfOfBWU9eHMJ+b+cJYZsNu3AFmjJf3+yPGd59Exz1TsMjaSFyxnYB9+IQ==} + csv-parse@7.0.3: + resolution: {integrity: sha512-YFd3QM/yo17vH91L1IOZuvl09zM0zEEtdfTcOCKWcMdM++MNaQSyp09slXyFCLaPXvHstQFx/xC8myiFHSMUvw==} debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} @@ -1377,8 +1410,8 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.5.322: - resolution: {integrity: sha512-vFU34OcrvMcH66T+dYC3G4nURmgfDVewMIu6Q2urXpumAPSMmzvcn04KVVV8Opikq8Vs5nUbO/8laNhNRqSzYw==} + electron-to-chromium@1.5.478: + resolution: {integrity: sha512-pftG+ma5Lm8O6/p45bhYT6StBN5ziQymaRzmCZwFl8DwWHKPPD93FdOmP1aqftcepolQYvrcyTLa17kE8Yf+jg==} emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} @@ -1551,8 +1584,8 @@ packages: fn.name@1.1.0: resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} - follow-redirects@1.16.0: - resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + follow-redirects@1.16.2: + resolution: {integrity: sha512-/kVP2U8z596Ia0gwFrwyIeo74ta5aVmSeU1FfAUMAmcEP5EP7zydmag3SLiN64NaRKef3lxsq7i/g7ltJQDGow==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -1649,6 +1682,10 @@ packages: html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -1878,8 +1915,8 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@3.14.2: - resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} + js-yaml@3.15.2: + resolution: {integrity: sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==} hasBin: true jsesc@3.1.0: @@ -2013,8 +2050,9 @@ packages: node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - node-releases@2.0.36: - resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==} + node-releases@2.0.63: + resolution: {integrity: sha512-6li2QanLsAXpIH2R6j77MzRyvOyrvdTqlKtO4CgBdHGNvN99ZZfyxszHc5o5+IZBWD5u6Hzwg83JoGXmqZNG9Q==} + engines: {node: '>=18'} normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} @@ -2103,8 +2141,8 @@ packages: resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} - picomatch@4.0.4: - resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} engines: {node: '>=12'} pirates@4.0.7: @@ -2128,8 +2166,9 @@ packages: resolution: {integrity: sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} @@ -2344,8 +2383,8 @@ packages: unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} - update-browserslist-db@1.2.3: - resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + update-browserslist-db@1.3.4: + resolution: {integrity: sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -2432,8 +2471,8 @@ packages: peerDependencies: zod: ^3.25.0 || ^4.0.0 - zod@4.3.6: - resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zod@4.6.7: + resolution: {integrity: sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==} snapshots: @@ -2443,19 +2482,25 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.29.0': {} + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} - '@babel/core@7.29.0': + '@babel/core@7.29.13': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helpers': 7.29.2 - '@babel/parser': 7.29.2 - '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.13 + '@babel/helper-compilation-targets': 7.29.13 + '@babel/helper-module-transforms': 7.29.13(@babel/core@7.29.13) + '@babel/helpers': 7.29.13 + '@babel/parser': 7.29.13 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.13 + '@babel/types': 7.29.11 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3 @@ -2473,29 +2518,37 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 - '@babel/helper-compilation-targets@7.28.6': + '@babel/generator@7.29.13': + dependencies: + '@babel/parser': 7.29.13 + '@babel/types': 7.29.11 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.29.13': dependencies: - '@babel/compat-data': 7.29.0 - '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.1 + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.29.5 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-globals@7.28.0': {} + '@babel/helper-globals@7.29.7': {} - '@babel/helper-module-imports@7.28.6': + '@babel/helper-module-imports@7.29.7': dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/traverse': 7.29.13 + '@babel/types': 7.29.11 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + '@babel/helper-module-transforms@7.29.13(@babel/core@7.29.13)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.13 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.13 transitivePeerDependencies: - supports-color @@ -2503,118 +2556,126 @@ snapshots: '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@7.29.13': {} + '@babel/helper-validator-identifier@7.28.5': {} - '@babel/helper-validator-option@7.27.1': {} + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} - '@babel/helpers@7.29.2': + '@babel/helpers@7.29.13': dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + '@babel/template': 7.29.7 + '@babel/types': 7.29.11 + + '@babel/parser@7.29.13': + dependencies: + '@babel/types': 7.29.11 '@babel/parser@7.29.2': dependencies: '@babel/types': 7.29.0 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0)': + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.13)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.13)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.0)': + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.13)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.0)': + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.13)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.13)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.0)': + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.13)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.13)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.13)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0)': + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.13)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.13)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.0)': + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.13)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.13)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.13)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.13)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.0)': + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.13)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.0)': + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.13)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.13)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@babel/helper-plugin-utils': 7.28.6 - '@babel/template@7.28.6': + '@babel/template@7.29.7': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.13 + '@babel/types': 7.29.11 - '@babel/traverse@7.29.0': + '@babel/traverse@7.29.13': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.2 - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.13 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.13 + '@babel/template': 7.29.7 + '@babel/types': 7.29.11 debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -2624,6 +2685,11 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@7.29.11': + dependencies: + '@babel/helper-string-parser': 7.29.13 + '@babel/helper-validator-identifier': 7.29.7 + '@bcoe/v8-coverage@0.2.3': {} '@colors/colors@1.5.0': @@ -2765,13 +2831,18 @@ snapshots: '@eslint/core': 1.1.1 levn: 0.4.1 - '@humanfs/core@0.19.1': {} + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 - '@humanfs/node@0.16.7': + '@humanfs/node@0.16.8': dependencies: - '@humanfs/core': 0.19.1 + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 '@humanwhocodes/retry': 0.4.3 + '@humanfs/types@0.15.0': {} + '@humanwhocodes/module-importer@1.0.1': {} '@humanwhocodes/retry@0.4.3': {} @@ -2909,21 +2980,22 @@ snapshots: camelcase: 5.3.1 find-up: 4.1.0 get-package-type: 0.1.0 - js-yaml: 3.14.2 + js-yaml: 3.15.2 resolve-from: 5.0.0 '@istanbuljs/schema@0.1.3': {} - '@iterable/api@0.12.0(typescript@6.0.2)': + '@iterable/api@0.13.1(typescript@6.0.2)': dependencies: - '@t3-oss/env-core': 0.13.11(typescript@6.0.2)(zod@4.3.6) - axios: 1.13.6 - csv-parse: 6.2.1 + '@t3-oss/env-core': 0.13.11(typescript@6.0.2)(zod@4.6.7) + axios: 1.21.0 + csv-parse: 7.0.3 winston: 3.19.0 - zod: 4.3.6 + zod: 4.6.7 transitivePeerDependencies: - arktype - debug + - supports-color - typescript - valibot @@ -3081,7 +3153,7 @@ snapshots: '@jest/transform@30.3.0': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@jest/types': 30.3.0 '@jridgewell/trace-mapping': 0.3.31 babel-plugin-istanbul: 7.0.1 @@ -3225,10 +3297,10 @@ snapshots: dependencies: '@swc/counter': 0.1.3 - '@t3-oss/env-core@0.13.11(typescript@6.0.2)(zod@4.3.6)': + '@t3-oss/env-core@0.13.11(typescript@6.0.2)(zod@4.6.7)': optionalDependencies: typescript: 6.0.2 - zod: 4.3.6 + zod: 4.6.7 '@tybys/wasm-util@0.10.1': dependencies: @@ -3462,6 +3534,12 @@ snapshots: acorn@8.16.0: {} + agent-base@6.0.2: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + ajv@6.14.0: dependencies: fast-deep-equal: 3.1.3 @@ -3502,21 +3580,23 @@ snapshots: asynckit@0.4.0: {} - axios@1.13.6: + axios@1.21.0: dependencies: - follow-redirects: 1.16.0 + follow-redirects: 1.16.2 form-data: 4.0.6 - proxy-from-env: 1.1.0 + https-proxy-agent: 5.0.1 + proxy-from-env: 2.1.0 transitivePeerDependencies: - debug + - supports-color - babel-jest@30.3.0(@babel/core@7.29.0): + babel-jest@30.3.0(@babel/core@7.29.13): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@jest/transform': 30.3.0 '@types/babel__core': 7.20.5 babel-plugin-istanbul: 7.0.1 - babel-preset-jest: 30.3.0(@babel/core@7.29.0) + babel-preset-jest: 30.3.0(@babel/core@7.29.13) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 @@ -3537,36 +3617,36 @@ snapshots: dependencies: '@types/babel__core': 7.20.5 - babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0): - dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.0) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.0) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.0) - '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0) - - babel-preset-jest@30.3.0(@babel/core@7.29.0): - dependencies: - '@babel/core': 7.29.0 + babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.13): + dependencies: + '@babel/core': 7.29.13 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.13) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.13) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.13) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.13) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.13) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.13) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.13) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.13) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.13) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.13) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.13) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.13) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.13) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.13) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.13) + + babel-preset-jest@30.3.0(@babel/core@7.29.13): + dependencies: + '@babel/core': 7.29.13 babel-plugin-jest-hoist: 30.3.0 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.13) balanced-match@1.0.2: {} balanced-match@4.0.4: {} - baseline-browser-mapping@2.10.10: {} + baseline-browser-mapping@2.11.35: {} boxen@8.0.1: dependencies: @@ -3579,26 +3659,26 @@ snapshots: widest-line: 5.0.0 wrap-ansi: 9.0.2 - brace-expansion@1.1.12: + brace-expansion@1.1.21: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.2: + brace-expansion@2.1.7: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.5: + brace-expansion@5.0.12: dependencies: balanced-match: 4.0.4 - browserslist@4.28.1: + browserslist@4.29.5: dependencies: - baseline-browser-mapping: 2.10.10 - caniuse-lite: 1.0.30001781 - electron-to-chromium: 1.5.322 - node-releases: 2.0.36 - update-browserslist-db: 1.2.3(browserslist@4.28.1) + baseline-browser-mapping: 2.11.35 + caniuse-lite: 1.0.30001823 + electron-to-chromium: 1.5.478 + node-releases: 2.0.63 + update-browserslist-db: 1.3.4(browserslist@4.29.5) bser@2.1.1: dependencies: @@ -3619,7 +3699,7 @@ snapshots: camelcase@8.0.0: {} - caniuse-lite@1.0.30001781: {} + caniuse-lite@1.0.30001823: {} chalk@4.1.2: dependencies: @@ -3697,7 +3777,7 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - csv-parse@6.2.1: {} + csv-parse@7.0.3: {} debug@4.4.3: dependencies: @@ -3721,7 +3801,7 @@ snapshots: eastasianwidth@0.2.0: {} - electron-to-chromium@1.5.322: {} + electron-to-chromium@1.5.478: {} emittery@0.13.1: {} @@ -3810,7 +3890,7 @@ snapshots: '@eslint/config-helpers': 0.5.3 '@eslint/core': 1.1.1 '@eslint/plugin-kit': 0.6.1 - '@humanfs/node': 0.16.7 + '@humanfs/node': 0.16.8 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 @@ -3900,9 +3980,9 @@ snapshots: dependencies: bser: 2.1.1 - fdir@6.5.0(picomatch@4.0.4): + fdir@6.5.0(picomatch@4.0.7): optionalDependencies: - picomatch: 4.0.4 + picomatch: 4.0.7 fecha@4.2.3: {} @@ -3929,7 +4009,7 @@ snapshots: fn.name@1.1.0: {} - follow-redirects@1.16.0: {} + follow-redirects@1.16.2: {} foreground-child@3.3.1: dependencies: @@ -4023,6 +4103,13 @@ snapshots: html-escaper@2.0.2: {} + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + human-signals@2.1.0: {} iconv-lite@0.7.2: @@ -4083,7 +4170,7 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@babel/parser': 7.29.2 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 @@ -4169,12 +4256,12 @@ snapshots: jest-config@30.3.0(@types/node@25.5.0): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@jest/get-type': 30.1.0 '@jest/pattern': 30.0.1 '@jest/test-sequencer': 30.3.0 '@jest/types': 30.3.0 - babel-jest: 30.3.0(@babel/core@7.29.0) + babel-jest: 30.3.0(@babel/core@7.29.13) chalk: 4.1.2 ci-info: 4.4.0 deepmerge: 4.3.1 @@ -4237,7 +4324,7 @@ snapshots: jest-regex-util: 30.0.1 jest-util: 30.3.0 jest-worker: 30.3.0 - picomatch: 4.0.4 + picomatch: 4.0.7 walker: 1.0.8 optionalDependencies: fsevents: 2.3.3 @@ -4261,7 +4348,7 @@ snapshots: '@types/stack-utils': 2.0.3 chalk: 4.1.2 graceful-fs: 4.2.11 - picomatch: 4.0.4 + picomatch: 4.0.7 pretty-format: 30.3.0 slash: 3.0.0 stack-utils: 2.0.6 @@ -4352,17 +4439,17 @@ snapshots: jest-snapshot@30.3.0: dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.13 '@babel/generator': 7.29.1 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.13) + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.13) '@babel/types': 7.29.0 '@jest/expect-utils': 30.3.0 '@jest/get-type': 30.1.0 '@jest/snapshot-utils': 30.3.0 '@jest/transform': 30.3.0 '@jest/types': 30.3.0 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.13) chalk: 4.1.2 expect: 30.3.0 graceful-fs: 4.2.11 @@ -4383,7 +4470,7 @@ snapshots: chalk: 4.1.2 ci-info: 4.4.0 graceful-fs: 4.2.11 - picomatch: 4.0.4 + picomatch: 4.0.7 jest-validate@30.3.0: dependencies: @@ -4428,7 +4515,7 @@ snapshots: js-tokens@4.0.0: {} - js-yaml@3.14.2: + js-yaml@3.15.2: dependencies: argparse: 1.0.10 esprima: 4.0.1 @@ -4514,15 +4601,15 @@ snapshots: minimatch@10.2.4: dependencies: - brace-expansion: 5.0.5 + brace-expansion: 5.0.12 minimatch@3.1.5: dependencies: - brace-expansion: 1.1.12 + brace-expansion: 1.1.21 minimatch@9.0.9: dependencies: - brace-expansion: 2.0.2 + brace-expansion: 2.1.7 minipass@7.1.3: {} @@ -4538,7 +4625,7 @@ snapshots: node-int64@0.4.0: {} - node-releases@2.0.36: {} + node-releases@2.0.63: {} normalize-path@3.0.0: {} @@ -4628,7 +4715,7 @@ snapshots: picomatch@2.3.2: {} - picomatch@4.0.4: {} + picomatch@4.0.7: {} pirates@4.0.7: {} @@ -4646,7 +4733,7 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 - proxy-from-env@1.1.0: {} + proxy-from-env@2.1.0: {} punycode@2.3.1: {} @@ -4788,8 +4875,8 @@ snapshots: tinyglobby@0.2.15: dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 tmpl@1.0.5: {} @@ -4846,9 +4933,9 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - update-browserslist-db@1.2.3(browserslist@4.28.1): + update-browserslist-db@1.3.4(browserslist@4.29.5): dependencies: - browserslist: 4.28.1 + browserslist: 4.29.5 escalade: 3.2.0 picocolors: 1.1.1 @@ -4943,8 +5030,8 @@ snapshots: yoctocolors@2.1.2: {} - zod-opts@1.0.0(zod@4.3.6): + zod-opts@1.0.0(zod@4.6.7): dependencies: - zod: 4.3.6 + zod: 4.6.7 - zod@4.3.6: {} + zod@4.6.7: {} diff --git a/src/commands/experiments.ts b/src/commands/experiments.ts index 147687d..fbbcc48 100644 --- a/src/commands/experiments.ts +++ b/src/commands/experiments.ts @@ -1,8 +1,14 @@ import { + CopyExperimentVariantParamsSchema, + CreateExperimentParamsSchema, + DeclareExperimentWinnerParamsSchema, + ExperimentIdParamsSchema, GetExperimentMetricsParamsSchema, GetExperimentParamsSchema, + GetExperimentTrendsParamsSchema, GetExperimentVariantsParamsSchema, ListExperimentsParamsSchema, + UpdateExperimentSettingsParamsSchema, } from "@iterable/api"; import type { CommandDefinition } from "./types.js"; @@ -39,4 +45,75 @@ export const experimentCommands: CommandDefinition[] = [ clientMethod: "getExperimentVariants", schema: GetExperimentVariantsParamsSchema, }), + defineCommand({ + category: "experiments", + name: "get-totals", + description: + "Get lifetime send and conversion totals for a campaign experiment", + clientMethod: "getExperimentTotals", + schema: ExperimentIdParamsSchema, + }), + defineCommand({ + category: "experiments", + name: "get-trends", + description: + "Get the performance time series for a campaign experiment. Provide both start and end times, or omit both to use the run window.", + clientMethod: "getExperimentTrends", + schema: GetExperimentTrendsParamsSchema, + }), + defineCommand({ + category: "experiments", + name: "create", + description: + "Create a draft campaign experiment. The campaign template becomes the control.", + clientMethod: "createExperiment", + schema: CreateExperimentParamsSchema, + }), + defineCommand({ + category: "experiments", + name: "copy-variant", + description: + "Copy a template into a new variant on a draft or ready campaign experiment", + clientMethod: "copyExperimentVariant", + schema: CopyExperimentVariantParamsSchema, + }), + defineCommand({ + category: "experiments", + name: "update-settings", + description: + "Update settings on a draft or ready campaign experiment. Omit a field to leave it unchanged.", + clientMethod: "updateExperimentSettings", + schema: UpdateExperimentSettingsParamsSchema, + }), + defineCommand({ + category: "experiments", + name: "start", + description: "Start a draft or ready campaign experiment", + clientMethod: "startExperiment", + schema: ExperimentIdParamsSchema, + }), + defineCommand({ + category: "experiments", + name: "cancel", + description: + "Cancel a running or winner-found campaign experiment without declaring a winner", + clientMethod: "cancelExperiment", + schema: ExperimentIdParamsSchema, + }), + defineCommand({ + category: "experiments", + name: "declare-winner", + description: + "Declare a winning variant and end a running or winner-found campaign experiment", + clientMethod: "declareExperimentWinner", + schema: DeclareExperimentWinnerParamsSchema, + }), + defineCommand({ + category: "experiments", + name: "delete", + description: "Delete a draft, ready, or errored campaign experiment", + clientMethod: "deleteExperiment", + schema: ExperimentIdParamsSchema, + destructive: true, + }), ]; diff --git a/tests/unit/commands.test.ts b/tests/unit/commands.test.ts index b524f57..b093b03 100644 --- a/tests/unit/commands.test.ts +++ b/tests/unit/commands.test.ts @@ -39,10 +39,19 @@ const EXPECTED_COMMANDS = [ "events:get-by-userid", "events:track", "events:track-bulk", + "experiments:cancel", + "experiments:copy-variant", + "experiments:create", + "experiments:declare-winner", + "experiments:delete", "experiments:get", "experiments:get-metrics", + "experiments:get-totals", + "experiments:get-trends", "experiments:get-variants", "experiments:list", + "experiments:start", + "experiments:update-settings", "export:cancel", "export:get-files", "export:list-jobs", diff --git a/tests/unit/experiments-commands.test.ts b/tests/unit/experiments-commands.test.ts new file mode 100644 index 0000000..44207eb --- /dev/null +++ b/tests/unit/experiments-commands.test.ts @@ -0,0 +1,91 @@ +import type { IterableClient } from "@iterable/api"; +import { describe, expect, it, jest } from "@jest/globals"; + +import { experimentCommands } from "../../src/commands/experiments.js"; + +const cases = [ + { + name: "get-totals", + clientMethod: "getExperimentTotals", + params: { experimentId: 1 }, + }, + { + name: "get-trends", + clientMethod: "getExperimentTrends", + params: { + experimentId: 1, + startDateTime: "2026-01-01T00:00:00Z", + endDateTime: "2026-01-02T00:00:00Z", + }, + }, + { + name: "create", + clientMethod: "createExperiment", + params: { campaignId: 1, experimentType: "SubjectLine" }, + }, + { + name: "copy-variant", + clientMethod: "copyExperimentVariant", + params: { experimentId: 1, copyFromTemplateId: 2 }, + }, + { + name: "update-settings", + clientMethod: "updateExperimentSettings", + params: { experimentId: 1, evenlySplitVariations: true }, + }, + { + name: "start", + clientMethod: "startExperiment", + params: { experimentId: 1 }, + }, + { + name: "cancel", + clientMethod: "cancelExperiment", + params: { experimentId: 1 }, + }, + { + name: "declare-winner", + clientMethod: "declareExperimentWinner", + params: { experimentId: 1, variantId: 2 }, + }, + { + name: "delete", + clientMethod: "deleteExperiment", + params: { experimentId: 1 }, + }, +] as const; + +function getCommand(name: string) { + const command = experimentCommands.find( + (candidate) => candidate.name === name + ); + if (!command) { + throw new Error(`Command not found: ${name}`); + } + return command; +} + +describe("experiment lifecycle commands", () => { + it.each(cases)( + "$name validates input and dispatches to $clientMethod", + async ({ name, clientMethod, params }) => { + const command = getCommand(name); + const parsed = command.schema.parse(params) as Record; + const method = jest + .fn<(params: Record) => Promise>() + .mockResolvedValue({}); + const client = { [clientMethod]: method } as unknown as IterableClient; + + expect(command.schema.safeParse({}).success).toBe(false); + await command.execute(client, parsed); + expect(method).toHaveBeenCalledWith(parsed); + } + ); + + it("marks delete as destructive", () => { + const command = experimentCommands.find( + (candidate) => candidate.name === "delete" + ); + expect(command?.destructive).toBe(true); + }); +});