From f6f55c002ce7db076578519d584187dc0abc9de9 Mon Sep 17 00:00:00 2001 From: John Yeo Date: Thu, 7 May 2026 13:23:43 +0800 Subject: [PATCH] initialised trigger.dev for migrations --- .gitignore | 2 + bun.lock | 241 +++++++++++++++-- package.json | 2 + scripts/dev.ts | 8 + server/experiments/explainCustomerFilter.ts | 2 +- server/src/external/autumn/autumnCli.ts | 38 ++- .../infisical/fetchInfisicalSecrets.ts | 115 ++++++++ server/src/external/logtail/logtailUtils.ts | 64 +++-- server/src/init.ts | 2 + .../internal/migrations/v2/actions/index.ts | 7 - .../migrations/v2/actions/runMigration.ts | 0 .../filters/customers/buildCustomerSelect.ts | 62 +++++ .../v2/filters/customers/filterCustomers.ts | 55 ++++ .../migrations/v2/filters/customers/index.ts | 2 + .../migrations/v2/filters/getFilterCount.ts | 24 ++ .../internal/migrations/v2/filters/index.ts | 5 + .../v2/filters/iterateOverFilterResults.ts | 30 +++ .../v2/filters/rawWithParamsToDrizzle.ts | 22 ++ .../migrations/v2/filters/runFilter.ts | 48 ++++ .../handleCreateMigration.ts | 2 +- .../v2/handlers/handleDeleteMigration.ts | 28 ++ .../handleListMigrations.ts | 0 .../handlePatchMigration.ts | 0 .../handlePrepareMigration.ts | 10 +- .../v2/handlers/handleRunMigration.ts | 51 ++++ .../internal/migrations/v2/migrationRouter.ts | 12 +- .../internal/migrations/v2/prepare/index.ts | 1 + .../v2/prepare/inferImplicitPrep.ts | 43 ++- .../ensurePricesAndEntitlements.ts | 19 +- .../migrations/v2/prepare/runPrepare.ts | 62 ++--- .../v2/prepare/runPrepareModules.ts | 40 +++ .../migrations/v2/prepare/types/index.ts | 2 +- .../v2/prepare/types/prepareModule.ts | 14 +- .../migrations/v2/repos/deleteMigration.ts | 24 ++ .../migrations/v2/repos/findMigration.ts | 39 +++ .../src/internal/migrations/v2/repos/index.ts | 4 + .../src/internal/migrations/v2/run/index.ts | 4 + .../migrations/v2/run/orchestrators/index.ts | 2 + .../v2/run/orchestrators/iterateScope.ts | 52 ++++ .../v2/run/orchestrators/runPreparation.ts | 27 ++ .../migrations/v2/run/perItem/applyAddItem.ts | 105 ++++++++ .../migrations/v2/run/perItem/index.ts | 4 + .../v2/run/perItem/matchPlanFilter.ts | 35 +++ .../v2/run/perItem/matchStringMatcher.ts | 32 +++ .../v2/run/perItem/runOpsForCustomer.ts | 70 +++++ .../migrations/v2/run/runMigration.ts | 74 ++++++ .../internal/migrations/v2/run/types/index.ts | 2 + .../v2/run/types/runMigrationResponse.ts | 17 ++ .../migrations/v2/run/types/runScope.ts | 9 + server/src/trigger/configureTrigger.ts | 18 ++ .../trigger/migrations/runMigrationTask.ts | 52 ++++ .../src/trigger/utils/createTriggerContext.ts | 46 ++++ server/src/trigger/utils/index.ts | 1 + server/src/utils/envUtils.ts | 27 +- server/src/utils/logging/addContextToLogs.ts | 11 + server/src/utils/logging/initLogger.ts | 249 ++++++++++-------- server/src/utils/logging/loggerTypes.ts | 7 + .../migrations-add-items.test.ts | 101 ++++--- .../tests/utils/testInitUtils/initScenario.ts | 38 +-- .../migrations/compiler/buildCustomerQuery.ts | 123 --------- shared/api/migrations/compiler/index.ts | 1 - shared/db/schema.ts | 107 ++++---- trigger.config.ts | 78 ++++++ vite/package.json | 1 + vite/src/hooks/queries/useMigrationsQuery.tsx | 68 ++++- vite/src/services/MigrationService.ts | 19 -- vite/src/views/migrations/MigrationsView.tsx | 6 +- .../components/CreateMigrationSheet.tsx | 16 +- .../components/EditMigrationSheet.tsx | 216 +++++++++++++++ .../migration-list/MigrationListTable.tsx | 90 ++++--- 70 files changed, 2207 insertions(+), 581 deletions(-) create mode 100644 server/src/external/infisical/fetchInfisicalSecrets.ts delete mode 100644 server/src/internal/migrations/v2/actions/index.ts delete mode 100644 server/src/internal/migrations/v2/actions/runMigration.ts create mode 100644 server/src/internal/migrations/v2/filters/customers/buildCustomerSelect.ts create mode 100644 server/src/internal/migrations/v2/filters/customers/filterCustomers.ts create mode 100644 server/src/internal/migrations/v2/filters/customers/index.ts create mode 100644 server/src/internal/migrations/v2/filters/getFilterCount.ts create mode 100644 server/src/internal/migrations/v2/filters/index.ts create mode 100644 server/src/internal/migrations/v2/filters/iterateOverFilterResults.ts create mode 100644 server/src/internal/migrations/v2/filters/rawWithParamsToDrizzle.ts create mode 100644 server/src/internal/migrations/v2/filters/runFilter.ts rename server/src/internal/migrations/v2/handlers/{handleCreateMigration => }/handleCreateMigration.ts (96%) create mode 100644 server/src/internal/migrations/v2/handlers/handleDeleteMigration.ts rename server/src/internal/migrations/v2/handlers/{handleListMigrations => }/handleListMigrations.ts (100%) rename server/src/internal/migrations/v2/handlers/{handlePatchMigration => }/handlePatchMigration.ts (100%) rename server/src/internal/migrations/v2/handlers/{handlePrepareMigration => }/handlePrepareMigration.ts (76%) create mode 100644 server/src/internal/migrations/v2/handlers/handleRunMigration.ts create mode 100644 server/src/internal/migrations/v2/prepare/runPrepareModules.ts create mode 100644 server/src/internal/migrations/v2/repos/deleteMigration.ts create mode 100644 server/src/internal/migrations/v2/repos/findMigration.ts create mode 100644 server/src/internal/migrations/v2/run/index.ts create mode 100644 server/src/internal/migrations/v2/run/orchestrators/index.ts create mode 100644 server/src/internal/migrations/v2/run/orchestrators/iterateScope.ts create mode 100644 server/src/internal/migrations/v2/run/orchestrators/runPreparation.ts create mode 100644 server/src/internal/migrations/v2/run/perItem/applyAddItem.ts create mode 100644 server/src/internal/migrations/v2/run/perItem/index.ts create mode 100644 server/src/internal/migrations/v2/run/perItem/matchPlanFilter.ts create mode 100644 server/src/internal/migrations/v2/run/perItem/matchStringMatcher.ts create mode 100644 server/src/internal/migrations/v2/run/perItem/runOpsForCustomer.ts create mode 100644 server/src/internal/migrations/v2/run/runMigration.ts create mode 100644 server/src/internal/migrations/v2/run/types/index.ts create mode 100644 server/src/internal/migrations/v2/run/types/runMigrationResponse.ts create mode 100644 server/src/internal/migrations/v2/run/types/runScope.ts create mode 100644 server/src/trigger/configureTrigger.ts create mode 100644 server/src/trigger/migrations/runMigrationTask.ts create mode 100644 server/src/trigger/utils/createTriggerContext.ts create mode 100644 server/src/trigger/utils/index.ts delete mode 100644 shared/api/migrations/compiler/buildCustomerQuery.ts create mode 100644 trigger.config.ts delete mode 100644 vite/src/services/MigrationService.ts create mode 100644 vite/src/views/migrations/components/EditMigrationSheet.tsx diff --git a/.gitignore b/.gitignore index 8f817caa2..9d678b266 100644 --- a/.gitignore +++ b/.gitignore @@ -145,3 +145,5 @@ TAKEHOME.md AGENTS.md server/.turbo + +.trigger \ No newline at end of file diff --git a/bun.lock b/bun.lock index dccfc9120..257afec40 100644 --- a/bun.lock +++ b/bun.lock @@ -8,6 +8,7 @@ "@aws-sdk/client-sqs": "^3.985.0", "@better-auth/core": "catalog:", "@better-auth/oauth-provider": "catalog:", + "@trigger.dev/sdk": "4.4.5", "@wooorm/starry-night": "^3.8.0", "ag-charts-react": "^12.3.0", "better-auth": "catalog:", @@ -19,6 +20,7 @@ "devDependencies": { "@better-auth/cli": "^1.4.21", "@biomejs/biome": "^2.2.7", + "@trigger.dev/build": "4.4.5", "@types/node": "^24.9.1", "concurrently": "^9.2.1", "dotenv": "^16.6.1", @@ -526,6 +528,7 @@ "date-fns": "^3.6.0", "decimal.js": "^10.5.0", "input-otp": "^1.4.2", + "json5": "^2.2.3", "lodash": "^4.17.21", "lucide-react": "^0.562.0", "motion": "^12.26.1", @@ -952,6 +955,8 @@ "@bufbuild/protobuf": ["@bufbuild/protobuf@2.12.0", "", {}, "sha512-B/XlCaFIP8LOwzo+bz5uFzATYokcwCKQcghqnlfwSmM5eX/qTkvDBnDPs+gXtX/RyjxJ4DRikECcPJbyALA8FA=="], + "@bugsnag/cuid": ["@bugsnag/cuid@3.2.2", "", {}, "sha512-7onuYLTMqMmHE9BBPG0YER4nFsU1rB+me1/YIeMusqcLbVbKKuG9u9+BDVDpje5e0llkkrVNOKYwmzM9DRIo7A=="], + "@canvas/image-data": ["@canvas/image-data@1.1.0", "", {}, "sha512-QdObRRjRbcXGmM1tmJ+MrHcaz1MftF2+W7YI+MsphnsCrmtyfS0d5qJbk0MeSbUeyM/jCb0hmnkXPsy026L7dA=="], "@cfworker/json-schema": ["@cfworker/json-schema@4.1.1", "", {}, "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og=="], @@ -994,6 +999,8 @@ "@ecies/ciphers": ["@ecies/ciphers@0.2.6", "", { "peerDependencies": { "@noble/ciphers": "^1.0.0" } }, "sha512-patgsRPKGkhhoBjETV4XxD0En4ui5fbX0hzayqI3M8tvNMGUoUvmyYAIWwlxBc1KX5cturfqByYdj5bYGRpN9g=="], + "@electric-sql/client": ["@electric-sql/client@1.0.14", "", { "dependencies": { "@microsoft/fetch-event-source": "^2.0.1" }, "optionalDependencies": { "@rollup/rollup-darwin-arm64": "^4.18.1" } }, "sha512-LtPAfeMxXRiYS0hyDQ5hue2PjljUiK9stvzsVyVb4nwxWQxfOWTSF42bHTs/o5i3x1T4kAQ7mwHpxa4A+f8X7Q=="], + "@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="], "@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="], @@ -1088,6 +1095,8 @@ "@fortawesome/react-fontawesome": ["@fortawesome/react-fontawesome@0.2.6", "", { "dependencies": { "prop-types": "^15.8.1" }, "peerDependencies": { "@fortawesome/fontawesome-svg-core": "~1 || ~6 || ~7", "react": "^16.3 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-mtBFIi1UsYQo7rYonYFkjgYKGoL8T+fEH6NGUpvuqtY3ytMsAoDaPo5rk25KuMtKDipY4bGYM/CkmCHA1N3FUg=="], + "@google-cloud/precise-date": ["@google-cloud/precise-date@4.0.0", "", {}, "sha512-1TUx3KdaU3cN7nfCdNf+UVqA/PSX29Cjcox3fZZBtINlRrXVTmUkQnCKv2MbBUbCopbK4olAT1IHl76uZyCiVA=="], + "@google/genai": ["@google/genai@1.50.1", "", { "dependencies": { "google-auth-library": "^10.3.0", "p-retry": "^4.6.2", "protobufjs": "^7.5.4", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.25.2" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-YbkX7H9+1Pt8wOt7DDREy8XSoiL6fRDzZQRyaVBarFf8MR3zHGqVdvM4cLbDXqPhxqvegZShgfxb8kw9C7YhAQ=="], "@grpc/grpc-js": ["@grpc/grpc-js@1.14.3", "", { "dependencies": { "@grpc/proto-loader": "^0.8.0", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA=="], @@ -1238,6 +1247,8 @@ "@jsep-plugin/ternary": ["@jsep-plugin/ternary@1.1.4", "", { "peerDependencies": { "jsep": "^0.4.0||^1.0.0" } }, "sha512-ck5wiqIbqdMX6WRQztBL7ASDty9YLgJ3sSAK5ZpBzXeySvFGCzIvM6UiAI4hTZ22fEcYQVV/zhUbNscggW+Ukg=="], + "@jsonhero/path": ["@jsonhero/path@1.0.21", "", {}, "sha512-gVUDj/92acpVoJwsVJ/RuWOaHyG4oFzn898WNGQItLCTQ+hOaVlEaImhwE1WqOTf+l3dGOUkbSiVKlb3q1hd1Q=="], + "@keyv/serialize": ["@keyv/serialize@1.1.1", "", {}, "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA=="], "@kubiks/otel-drizzle": ["@kubiks/otel-drizzle@2.1.0", "", { "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <2.0.0", "drizzle-orm": ">=0.28.0" } }, "sha512-9UHb0od3jwa6zTWMyEYPIZcUq5PDaziCmQLMLakSK2zeqy12SFZ3SAGWXJTgEr8valn/Wa+DKVs+Z3aqKQUpvg=="], @@ -1260,6 +1271,8 @@ "@mermaid-js/parser": ["@mermaid-js/parser@1.1.0", "", { "dependencies": { "langium": "^4.0.0" } }, "sha512-gxK9ZX2+Fex5zu8LhRQoMeMPEHbc73UKZ0FQ54YrQtUxE1VVhMwzeNtKRPAu5aXks4FasbMe4xB4bWrmq6Jlxw=="], + "@microsoft/fetch-event-source": ["@microsoft/fetch-event-source@2.0.1", "", {}, "sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA=="], + "@mintlify/cli": ["@mintlify/cli@4.0.1134", "", { "dependencies": { "@inquirer/prompts": "7.9.0", "@mintlify/common": "1.0.865", "@mintlify/link-rot": "3.0.1043", "@mintlify/prebuild": "1.0.1008", "@mintlify/previewing": "4.0.1069", "@mintlify/validation": "0.1.676", "adm-zip": "0.5.16", "chalk": "5.2.0", "color": "4.2.3", "detect-port": "1.5.1", "front-matter": "4.0.2", "fs-extra": "11.2.0", "ink": "6.3.0", "inquirer": "12.3.0", "js-yaml": "4.1.0", "mdast-util-mdx-jsx": "3.2.0", "open": "^8.4.2", "openid-client": "^6.8.2", "posthog-node": "5.17.2", "react": "19.2.3", "semver": "7.7.2", "unist-util-visit": "5.0.0", "yargs": "17.7.1", "zod": "^4.3.6" }, "optionalDependencies": { "keytar": "^7.9.0" }, "bin": { "mint": "bin/index.js", "mintlify": "bin/index.js" } }, "sha512-BTDUtv15RiqyN9ci/4zPKKpYWdnSvR57WAAlWXxCBCk2nNMYMKBTSVCmmJ91CriYOyUrXrKXrAI1UMiWBrWvzQ=="], "@mintlify/common": ["@mintlify/common@1.0.865", "", { "dependencies": { "@asyncapi/parser": "3.4.0", "@asyncapi/specs": "6.8.1", "@mintlify/mdx": "^3.0.4", "@mintlify/models": "0.0.296", "@mintlify/openapi-parser": "^0.0.8", "@mintlify/validation": "0.1.676", "@sindresorhus/slugify": "2.2.0", "@types/mdast": "4.0.4", "acorn": "8.11.2", "acorn-jsx": "5.3.2", "color-blend": "4.0.0", "estree-util-to-js": "2.0.0", "estree-walker": "3.0.3", "front-matter": "4.0.2", "hast-util-from-html": "2.0.3", "hast-util-to-html": "9.0.4", "hast-util-to-text": "4.0.2", "hex-rgb": "5.0.0", "ignore": "7.0.5", "js-yaml": "4.1.0", "lodash": "4.18.1", "mdast-util-from-markdown": "2.0.2", "mdast-util-gfm": "3.0.0", "mdast-util-mdx": "3.0.0", "mdast-util-mdx-jsx": "3.1.3", "micromark-extension-gfm": "3.0.0", "micromark-extension-mdx-jsx": "3.0.1", "micromark-extension-mdxjs": "3.0.0", "openapi-types": "12.1.3", "postcss": "8.5.6", "rehype-stringify": "10.0.1", "remark": "15.0.1", "remark-frontmatter": "5.0.0", "remark-gfm": "4.0.0", "remark-math": "6.0.0", "remark-mdx": "3.1.0", "remark-parse": "11.0.0", "remark-rehype": "11.1.1", "remark-stringify": "11.0.0", "sucrase": "^3.34.0", "tailwindcss": "^3.4.17", "unified": "11.0.5", "unist-builder": "4.0.0", "unist-util-map": "4.0.0", "unist-util-remove": "4.0.0", "unist-util-remove-position": "5.0.0", "unist-util-visit": "5.0.0", "unist-util-visit-parents": "6.0.1", "vfile": "6.0.3", "xss": "1.0.15" } }, "sha512-p+mDIOwdtSGhgiRvr3mVNBT/PeXB3x2klkmX10SmRdePoyKheDtCqwao67f+4Av2bOeCUX3nti8Ccz6XTW/4BQ=="], @@ -1390,6 +1403,8 @@ "@opentelemetry/exporter-zipkin": ["@opentelemetry/exporter-zipkin@2.0.1", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-a9eeyHIipfdxzCfc2XPrE+/TI3wmrZUDFtG2RRXHSbZZULAny7SyybSvaDvS77a7iib5MPiAvluwVvbGTsHxsw=="], + "@opentelemetry/host-metrics": ["@opentelemetry/host-metrics@0.37.0", "", { "dependencies": { "systeminformation": "5.23.8" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-gf6nRFci0PTni9R1QQKjZ2uZE4Y6olLKhlwdM0qqLbbn3SBVKyP2jyBMiosBTHtRNLjY7s8hzQ44eLdK5wkGNQ=="], + "@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.202.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.202.0", "import-in-the-middle": "^1.8.1", "require-in-the-middle": "^7.1.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Uz3BxZWPgDwgHM2+vCKEQRh0R8WKrd/q6Tus1vThRClhlPO39Dyz7mDrOr6KuqGXAlBQ1e5Tnymzri4RMZNaWA=="], "@opentelemetry/instrumentation-amqplib": ["@opentelemetry/instrumentation-amqplib@0.58.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.211.0", "@opentelemetry/semantic-conventions": "^1.33.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-fjpQtH18J6GxzUZ+cwNhWUpb71u+DzT7rFkg5pLssDGaEber91Y2WNGdpVpwGivfEluMlNMZumzjEqfg8DeKXQ=="], @@ -1606,8 +1621,12 @@ "@prisma/client": ["@prisma/client@5.22.0", "", { "peerDependencies": { "prisma": "*" }, "optionalPeers": ["prisma"] }, "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA=="], + "@prisma/config": ["@prisma/config@6.19.3", "", { "dependencies": { "c12": "3.1.0", "deepmerge-ts": "7.1.5", "effect": "3.21.0", "empathic": "2.0.0" } }, "sha512-CBPT44BjlQxEt8kiMEauji2WHTDoVBOKl7UlewXmUgBPnr/oPRZC3psci5chJnYmH0ivEIog2OU9PGWoki3DLQ=="], + "@prisma/instrumentation": ["@prisma/instrumentation@7.2.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.207.0" }, "peerDependencies": { "@opentelemetry/api": "^1.8" } }, "sha512-Rh9Z4x5kEj1OdARd7U18AtVrnL6rmLSI0qYShaB4W7Wx5BKbgzndWF+QnuzMb7GLfVdlT5aYCXoPQVYuYtVu0g=="], + "@protobuf-ts/runtime": ["@protobuf-ts/runtime@2.11.1", "", {}, "sha512-KuDaT1IfHkugM2pyz+FwiY80ejWrkH1pAtOBOZFuR6SXEFTsnb/jiQWQ1rCIrcKx2BtyxnxW6BWwsVSA/Ie+WQ=="], + "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="], "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="], @@ -1850,6 +1869,8 @@ "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="], + "@s2-dev/streamstore": ["@s2-dev/streamstore@0.22.5", "", { "dependencies": { "@protobuf-ts/runtime": "^2.11.1", "debug": "^4.4.3" } }, "sha512-GqdOKIbIoIxT+40fnKzHbrsHB6gBqKdECmFe7D3Ojk4FoN1Hu0LhFzZv6ZmVMjoHHU+55debS1xSWjZwQmbIyQ=="], + "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="], "@selderee/plugin-htmlparser2": ["@selderee/plugin-htmlparser2@0.11.0", "", { "dependencies": { "domhandler": "^5.0.3", "selderee": "^0.11.0" } }, "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ=="], @@ -2162,6 +2183,12 @@ "@tootallnate/quickjs-emscripten": ["@tootallnate/quickjs-emscripten@0.23.0", "", {}, "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA=="], + "@trigger.dev/build": ["@trigger.dev/build@4.4.5", "", { "dependencies": { "@prisma/config": "^6.10.0", "@trigger.dev/core": "4.4.5", "mlly": "^1.7.1", "pkg-types": "^1.1.3", "resolve": "^1.22.8", "tinyglobby": "^0.2.2", "tsconfck": "3.1.3" } }, "sha512-45l8cv59JERkDrC5tADkKVDJriZjPtCtkAHVWvCZXRJMlm93Ao8deTHdtOf++TqdsqdG9Tk5cxhupY1COq4muA=="], + + "@trigger.dev/core": ["@trigger.dev/core@4.4.5", "", { "dependencies": { "@bugsnag/cuid": "^3.1.1", "@electric-sql/client": "1.0.14", "@google-cloud/precise-date": "^4.0.0", "@jsonhero/path": "^1.0.21", "@opentelemetry/api": "1.9.0", "@opentelemetry/api-logs": "0.203.0", "@opentelemetry/core": "2.0.1", "@opentelemetry/exporter-logs-otlp-http": "0.203.0", "@opentelemetry/exporter-metrics-otlp-http": "0.203.0", "@opentelemetry/exporter-trace-otlp-http": "0.203.0", "@opentelemetry/host-metrics": "^0.37.0", "@opentelemetry/instrumentation": "0.203.0", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-logs": "0.203.0", "@opentelemetry/sdk-metrics": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1", "@opentelemetry/sdk-trace-node": "2.0.1", "@opentelemetry/semantic-conventions": "1.36.0", "@s2-dev/streamstore": "0.22.5", "dequal": "^2.0.3", "eventsource": "^3.0.5", "eventsource-parser": "^3.0.0", "execa": "^8.0.1", "humanize-duration": "^3.27.3", "jose": "^5.4.0", "nanoid": "3.3.8", "prom-client": "^15.1.0", "socket.io": "4.7.4", "socket.io-client": "4.7.5", "std-env": "^3.8.1", "tinyexec": "^0.3.2", "uncrypto": "^0.1.3", "zod": "3.25.76", "zod-error": "1.5.0", "zod-validation-error": "^1.5.0" } }, "sha512-VI4AjX5ivbm+E8RMdP2kjuo/ph8cTCQTNkZSx6UknnAxyu2mihqPbcYWl2tqZCGbbX/HIlzhEtM2NM3a01Juxg=="], + + "@trigger.dev/sdk": ["@trigger.dev/sdk@4.4.5", "", { "dependencies": { "@opentelemetry/api": "1.9.0", "@opentelemetry/semantic-conventions": "1.36.0", "@trigger.dev/core": "4.4.5", "chalk": "^5.2.0", "cronstrue": "^2.21.0", "debug": "^4.3.4", "evt": "^2.4.13", "slug": "^6.0.0", "ulid": "^2.3.0", "uncrypto": "^0.1.3", "ws": "^8.11.0" }, "peerDependencies": { "ai": "^4.2.0 || ^5.0.0 || ^6.0.0", "zod": "^3.0.0 || ^4.0.0" }, "optionalPeers": ["ai"] }, "sha512-iXjEWQQy+Dc7WDdjOtGLTlHIcVdcg00GUYL/hTr5FwMlY/Aswvfe0Snfn+7XECtls2tgZTPJ+rnTP4mo77o5eg=="], + "@ts-morph/common": ["@ts-morph/common@0.27.0", "", { "dependencies": { "fast-glob": "^3.3.3", "minimatch": "^10.0.1", "path-browserify": "^1.0.1" } }, "sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ=="], "@tsconfig/node10": ["@tsconfig/node10@1.0.12", "", {}, "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ=="], @@ -2210,6 +2237,8 @@ "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], + "@types/cookie": ["@types/cookie@0.4.1", "", {}, "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="], + "@types/cookiejar": ["@types/cookiejar@2.1.5", "", {}, "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q=="], "@types/cors": ["@types/cors@2.8.19", "", { "dependencies": { "@types/node": "*" } }, "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg=="], @@ -2874,6 +2903,8 @@ "ci-parallel-vars": ["ci-parallel-vars@1.0.1", "", {}, "sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg=="], + "citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="], + "cjs-module-lexer": ["cjs-module-lexer@1.4.3", "", {}, "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q=="], "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], @@ -2992,6 +3023,8 @@ "cron-parser": ["cron-parser@4.9.0", "", { "dependencies": { "luxon": "^3.2.1" } }, "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q=="], + "cronstrue": ["cronstrue@2.61.0", "", { "bin": { "cronstrue": "bin/cli.js" } }, "sha512-ootN5bvXbIQI9rW94+QsXN5eROtXWwew6NkdGxIRpS/UFWRggL0G5Al7a9GTBFEsuvVhJ2K3CntIIVt7L2ILhA=="], + "cross-env": ["cross-env@7.0.3", "", { "dependencies": { "cross-spawn": "^7.0.1" }, "bin": { "cross-env": "src/bin/cross-env.js", "cross-env-shell": "src/bin/cross-env-shell.js" } }, "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw=="], "cross-fetch": ["cross-fetch@3.2.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q=="], @@ -3146,6 +3179,8 @@ "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], + "deepmerge-ts": ["deepmerge-ts@7.1.5", "", {}, "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw=="], + "default-browser": ["default-browser@5.5.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="], "default-browser-id": ["default-browser-id@5.0.1", "", {}, "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q=="], @@ -3262,6 +3297,8 @@ "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + "effect": ["effect@3.21.0", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-PPN80qRokCd1f015IANNhrwOnLO7GrrMQfk4/lnZRE/8j7UPWrNNjPV0uBrZutI/nHzernbW+J0hdqQysHiSnQ=="], + "ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="], "electron-to-chromium": ["electron-to-chromium@1.5.344", "", {}, "sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg=="], @@ -3270,6 +3307,8 @@ "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + "empathic": ["empathic@2.0.0", "", {}, "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA=="], + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], "encoding-sniffer": ["encoding-sniffer@0.2.1", "", { "dependencies": { "iconv-lite": "^0.6.3", "whatwg-encoding": "^3.1.1" } }, "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw=="], @@ -3278,7 +3317,7 @@ "engine.io": ["engine.io@6.6.7", "", { "dependencies": { "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "@types/ws": "^8.5.12", "accepts": "~1.3.4", "base64id": "2.0.0", "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.4.1", "engine.io-parser": "~5.2.1", "ws": "~8.18.3" } }, "sha512-DgOngfDKM2EviOH3Mr9m7ks1q8roetLy/IMmYthAYzbpInMbYc/GS+fWFA3rl1gvwKVsQrVV61fo5emD1y3OJQ=="], - "engine.io-client": ["engine.io-client@6.6.4", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-parser": "~5.2.1", "ws": "~8.18.3", "xmlhttprequest-ssl": "~2.1.1" } }, "sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw=="], + "engine.io-client": ["engine.io-client@6.5.4", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", "ws": "~8.17.1", "xmlhttprequest-ssl": "~2.0.0" } }, "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ=="], "engine.io-parser": ["engine.io-parser@5.2.3", "", {}, "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="], @@ -3436,6 +3475,8 @@ "eventsource-parser": ["eventsource-parser@3.0.8", "", {}, "sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ=="], + "evt": ["evt@2.5.9", "", { "dependencies": { "minimal-polyfills": "^2.2.3", "run-exclusive": "^2.2.19", "tsafe": "^1.8.5" } }, "sha512-GpjX476FSlttEGWHT8BdVMoI8wGXQGbEOtKcP4E+kggg+yJzXBZN2n4x7TS/zPBJ1DZqWI+rguZZApjjzQ0HpA=="], + "execa": ["execa@9.6.1", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="], "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], @@ -3454,6 +3495,8 @@ "extract-zip": ["extract-zip@2.0.1", "", { "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "optionalDependencies": { "@types/yauzl": "^2.9.1" }, "bin": { "extract-zip": "cli.js" } }, "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg=="], + "fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="], + "fast-copy": ["fast-copy@4.0.3", "", {}, "sha512-58apWr0GUiDFM8+3afrO6eYwJBn9ZAhDOzG3L+/9llab/haCARS2UIfffmOurYLwbgDRs8n0rfr6qAAPEAuAQw=="], "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], @@ -3778,6 +3821,8 @@ "human-signals": ["human-signals@8.0.1", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="], + "humanize-duration": ["humanize-duration@3.33.2", "", {}, "sha512-K7Ny/ULO1hDm2nnhvAY+SJV1skxFb61fd073SG1IWJl+D44ULrruCuTyjHKjBVVcSuTlnY99DKtgEG39CM5QOQ=="], + "humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="], "husky": ["husky@9.1.7", "", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="], @@ -4396,6 +4441,8 @@ "min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="], + "minimal-polyfills": ["minimal-polyfills@2.2.3", "", {}, "sha512-oxdmJ9cL+xV72h0xYxp4tP2d5/fTBpP45H8DIOn9pASuF8a3IYTf+25fMGDYGiWW+MFsuog6KD6nfmhZJQ+uUw=="], + "minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="], "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], @@ -4500,6 +4547,8 @@ "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], + "node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="], + "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="], "node-releases": ["node-releases@2.0.38", "", {}, "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw=="], @@ -4524,6 +4573,8 @@ "nuqs": ["nuqs@2.8.9", "", { "dependencies": { "@standard-schema/spec": "1.0.0" }, "peerDependencies": { "@remix-run/react": ">=2", "@tanstack/react-router": "^1", "next": ">=14.2.0", "react": ">=18.2.0 || ^19.0.0-0", "react-router": "^5 || ^6 || ^7", "react-router-dom": "^5 || ^6 || ^7" }, "optionalPeers": ["@remix-run/react", "@tanstack/react-router", "next", "react-router", "react-router-dom"] }, "sha512-8ou6AEwsxMWSYo2qkfZtYFVzngwbKmg4c00HVxC1fF6CEJv3Fwm6eoZmfVPALB+vw8Udo7KL5uy96PFcYe1BIQ=="], + "nypm": ["nypm@0.6.6", "", { "dependencies": { "citty": "^0.2.2", "pathe": "^2.0.3", "tinyexec": "^1.1.1" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-vRyr0r4cbBapw07Xw8xrj9Teq3o7MUD35rSaTcanDbW+aK2XHDgJFiU6ZTj2GBw7Q12ysdsyFss+Vdz4hQ0Y6Q=="], + "oauth4webapi": ["oauth4webapi@3.8.5", "", {}, "sha512-A8jmyUckVhRJj5lspguklcl90Ydqk61H3dcU0oLhH3Yv13KpAliKTt5hknpGGPZSSfOwGyraNEFmofDYH+1kSg=="], "obj-props": ["obj-props@1.4.0", "", {}, "sha512-p7p/7ltzPDiBs6DqxOrIbtRdwxxVRBj5ROukeNb9RgA+fawhrz5n2hpNz8DDmYR//tviJSj7nUnlppGmONkjiQ=="], @@ -4556,7 +4607,7 @@ "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], - "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + "onetime": ["onetime@6.0.0", "", { "dependencies": { "mimic-fn": "^4.0.0" } }, "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="], "oniguruma-parser": ["oniguruma-parser@0.12.2", "", {}, "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw=="], @@ -4712,7 +4763,7 @@ "pkg-dir": ["pkg-dir@5.0.0", "", { "dependencies": { "find-up": "^5.0.0" } }, "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA=="], - "pkg-types": ["pkg-types@2.3.1", "", { "dependencies": { "confbox": "^0.2.4", "exsolve": "^1.0.8", "pathe": "^2.0.3" } }, "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg=="], + "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], "playwright": ["playwright@1.59.1", "", { "dependencies": { "playwright-core": "1.59.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw=="], @@ -4820,6 +4871,8 @@ "puppeteer-core": ["puppeteer-core@24.42.0", "", { "dependencies": { "@puppeteer/browsers": "2.13.0", "chromium-bidi": "14.0.0", "debug": "^4.4.3", "devtools-protocol": "0.0.1595872", "typed-query-selector": "^2.12.1", "webdriver-bidi-protocol": "0.4.1", "ws": "^8.19.0" } }, "sha512-T4zXokk/izH01fYPhyyev1A4piWiOKrYq7CUFpdoYQxmOnXoV6YjUabmfIjCYkNspSoAXIxRid3Tw+Vg0fthYg=="], + "pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="], + "qs": ["qs@6.15.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg=="], "query-selector-shadow-dom": ["query-selector-shadow-dom@1.0.1", "", {}, "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw=="], @@ -4994,7 +5047,7 @@ "resend": ["resend@4.8.0", "", { "dependencies": { "@react-email/render": "1.1.2" } }, "sha512-R8eBOFQDO6dzRTDmaMEdpqrkmgSjPpVXt4nGfWsZdYOet0kqra0xgbvTES6HmCriZEXbmGk3e0DiGIaLFTFSHA=="], - "resolve": ["resolve@2.0.0-next.6", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "node-exports-info": "^1.6.0", "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA=="], + "resolve": ["resolve@1.22.12", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="], "resolve-alpn": ["resolve-alpn@1.2.1", "", {}, "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="], @@ -5044,6 +5097,8 @@ "run-async": ["run-async@4.0.6", "", {}, "sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ=="], + "run-exclusive": ["run-exclusive@2.2.19", "", { "dependencies": { "minimal-polyfills": "^2.2.3" } }, "sha512-K3mdoAi7tjJ/qT7Flj90L7QyPozwUaAG+CVhkdDje4HLKXUYC3N/Jzkau3flHVDLQVhiHBtcimVodMjN9egYbA=="], + "run-jxa": ["run-jxa@3.0.0", "", { "dependencies": { "execa": "^5.1.1", "macos-version": "^6.0.0", "subsume": "^4.0.0", "type-fest": "^2.0.0" } }, "sha512-4f2CrY7H+sXkKXJn/cE6qRA3z+NMVO7zvlZ/nUV0e62yWftpiLAfw5eV9ZdomzWd2TXWwEIiGjAT57+lWIzzvA=="], "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], @@ -5142,6 +5197,8 @@ "slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="], + "slug": ["slug@6.1.0", "", {}, "sha512-x6vLHCMasg4DR2LPiyFGI0gJJhywY6DTiGhCrOMzb3SOk/0JVLIaL4UhyFSHu04SD3uAavrKY/K3zZ3i6iRcgA=="], + "smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="], "smol-toml": ["smol-toml@1.6.1", "", {}, "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg=="], @@ -5150,7 +5207,7 @@ "socket.io-adapter": ["socket.io-adapter@2.5.6", "", { "dependencies": { "debug": "~4.4.1", "ws": "~8.18.3" } }, "sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ=="], - "socket.io-client": ["socket.io-client@4.8.3", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-client": "~6.6.1", "socket.io-parser": "~4.2.4" } }, "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g=="], + "socket.io-client": ["socket.io-client@4.7.5", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.2", "engine.io-client": "~6.5.2", "socket.io-parser": "~4.2.4" } }, "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ=="], "socket.io-parser": ["socket.io-parser@4.2.6", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1" } }, "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg=="], @@ -5202,6 +5259,8 @@ "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], + "stdin-discarder": ["stdin-discarder@0.2.2", "", {}, "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ=="], "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], @@ -5292,6 +5351,8 @@ "system-architecture": ["system-architecture@0.1.0", "", {}, "sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA=="], + "systeminformation": ["systeminformation@5.23.8", "", { "os": "!aix", "bin": { "systeminformation": "lib/cli.js" } }, "sha512-Osd24mNKe6jr/YoXLLK3k8TMdzaxDffhpCxgkfgBHcapykIkd50HXThM3TCEuHO2pPuCsSx2ms/SunqhU5MmsQ=="], + "tagged-tag": ["tagged-tag@1.0.0", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="], "tailwind-merge": ["tailwind-merge@3.5.0", "", {}, "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A=="], @@ -5436,9 +5497,11 @@ "ts-to-zod": ["ts-to-zod@5.1.0", "", { "dependencies": { "@clack/prompts": "1.0.0-alpha.4", "@oclif/core": "^4.5.4", "@typescript/vfs": "^1.5.0", "chokidar": "^4.0.3", "listr2": "^9.0.4", "slash": "^5.1.0", "text-case": "^1.2.4", "tslib": "^2.3.1", "tsutils": "^3.21.0", "typescript": "^5.2.2", "zod": "^4.1.5" }, "bin": { "ts-to-zod": "bin/run" } }, "sha512-giqqlvRHunlJqG9tBL/KAO3wWIVZGF//mZiWLKm/fdQnKnz4EN2mtiK5cugN9slytBkdMEXQIaLvMzIScbhhFw=="], + "tsafe": ["tsafe@1.8.12", "", {}, "sha512-nFRqW0ttu/2o6XTXsHiVZWJBCOaxhVqZLg7dgs3coZNsCMPXPfwz+zPHAQA+70fNnVJLAPg1EgGIqK9Q84tvAw=="], + "tsc-alias": ["tsc-alias@1.8.16", "", { "dependencies": { "chokidar": "^3.5.3", "commander": "^9.0.0", "get-tsconfig": "^4.10.0", "globby": "^11.0.4", "mylas": "^2.1.9", "normalize-path": "^3.0.0", "plimit-lit": "^1.2.6" }, "bin": { "tsc-alias": "dist/bin/index.js" } }, "sha512-QjCyu55NFyRSBAl6+MTFwplpFcnm2Pq01rR/uxfqJoLMm6X3O14KEGtaSDZpJYaE1bJBGDjD0eSuiIWPe2T58g=="], - "tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="], + "tsconfck": ["tsconfck@3.1.3", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ulNZP1SVpRDesxeMLON/LtWM8HIgAJEIVpVVhBM6gsmvQ8+Rh+ZG7FWGvHh7Ah3pRABwVJWklWCr/BTZSv0xnQ=="], "tsconfig-paths": ["tsconfig-paths@4.2.0", "", { "dependencies": { "json5": "^2.2.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg=="], @@ -5486,6 +5549,8 @@ "uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="], + "ulid": ["ulid@2.4.0", "", { "bin": { "ulid": "bin/cli.js" } }, "sha512-fIRiVTJNcSRmXKPZtGzFQv9WRrZ3M9eoptl/teFJvjOzmpU+/K/JH6HZ8deBfb5vMEpicJcLn7JmvdknlMq7Zg=="], + "unbash": ["unbash@3.0.0", "", {}, "sha512-FeFPZ/WFT0mbRCuydiZzpPFlrYN8ZUpphQKoq4EeElVIYjYyGzPMxQR/simUwCOJIyVhpFk4RbtyO7RuMpMnHA=="], "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], @@ -5684,7 +5749,7 @@ "xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="], - "xmlhttprequest-ssl": ["xmlhttprequest-ssl@2.1.2", "", {}, "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ=="], + "xmlhttprequest-ssl": ["xmlhttprequest-ssl@2.0.0", "", {}, "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A=="], "xo": ["xo@0.53.1", "", { "dependencies": { "@eslint/eslintrc": "^1.3.3", "@typescript-eslint/eslint-plugin": "^5.43.0", "@typescript-eslint/parser": "^5.43.0", "arrify": "^3.0.0", "cosmiconfig": "^7.1.0", "define-lazy-prop": "^3.0.0", "eslint": "^8.27.0", "eslint-config-prettier": "^8.5.0", "eslint-config-xo": "^0.43.1", "eslint-config-xo-typescript": "^0.55.0", "eslint-formatter-pretty": "^4.1.0", "eslint-import-resolver-webpack": "^0.13.2", "eslint-plugin-ava": "^13.2.0", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-n": "^15.5.1", "eslint-plugin-no-use-extend-native": "^0.5.0", "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-unicorn": "^44.0.2", "esm-utils": "^4.1.0", "find-cache-dir": "^4.0.0", "find-up": "^6.3.0", "get-stdin": "^9.0.0", "globby": "^13.1.2", "imurmurhash": "^0.1.4", "json-stable-stringify-without-jsonify": "^1.0.1", "json5": "^2.2.1", "lodash-es": "^4.17.21", "meow": "^11.0.0", "micromatch": "^4.0.5", "open-editor": "^4.0.0", "prettier": "^2.7.1", "semver": "^7.3.8", "slash": "^5.0.0", "to-absolute-glob": "^2.0.2", "typescript": "^4.9.3" }, "bin": { "xo": "cli.js" } }, "sha512-/2R8SPehv1UhiIqJ9uSvrAjslcoygICNsUlEb/Zf2V6rMtr7YCoggc6hlt6b/kbncpR989Roqt6AvEO779dFxw=="], @@ -5724,11 +5789,13 @@ "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "zod-error": ["zod-error@1.5.0", "", { "dependencies": { "zod": "^3.20.2" } }, "sha512-zzopKZ/skI9iXpqCEPj+iLCKl9b88E43ehcU+sbRoHuwGd9F1IDVGQ70TyO6kmfiRL1g4IXkjsXK+g1gLYl4WQ=="], + "zod-openapi": ["zod-openapi@5.4.6", "", { "peerDependencies": { "zod": "^3.25.74 || ^4.0.0" } }, "sha512-P2jsOOBAq/6hCwUsMCjUATZ8szkMsV5VAwZENfyxp2Hc/XPJQpVwAgevWZc65xZauCwWB9LAn7zYeiCJFAEL+A=="], "zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], - "zod-validation-error": ["zod-validation-error@4.0.2", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ=="], + "zod-validation-error": ["zod-validation-error@1.5.0", "", { "peerDependencies": { "zod": "^3.18.0" } }, "sha512-/7eFkAI4qV0tcxMBB/3+d2c1P6jzzZYdYSlBuAklzMuCrJu5bzJfHS0yVAS87dRHVlhftd6RFJDIvv03JgkSbw=="], "zustand": ["zustand@5.0.12", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g=="], @@ -5748,6 +5815,8 @@ "@artilleryio/int-core/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], + "@artilleryio/int-core/socket.io-client": ["socket.io-client@4.8.3", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-client": "~6.6.1", "socket.io-parser": "~4.2.4" } }, "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g=="], + "@artilleryio/int-core/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], "@asyncapi/parser/ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], @@ -6428,6 +6497,8 @@ "@posthog/ai/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "@prisma/config/c12": ["c12@3.1.0", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^16.6.1", "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.4.2", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^1.0.0", "pkg-types": "^2.2.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw=="], + "@prisma/instrumentation/@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.207.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.207.0", "import-in-the-middle": "^2.0.0", "require-in-the-middle": "^8.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-y6eeli9+TLKnznrR8AZlQMSJT7wILpXH+6EYq5Vf/4Ao+huI7EedxQHwRgVUOMLFbe7VFDvHJrX9/f4lcwnJsA=="], "@radix-ui/react-alert-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], @@ -6534,6 +6605,40 @@ "@tailwindcss/vite/tailwindcss": ["tailwindcss@4.2.1", "", {}, "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw=="], + "@trigger.dev/core/@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], + + "@trigger.dev/core/@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.203.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ=="], + + "@trigger.dev/core/@opentelemetry/core": ["@opentelemetry/core@2.0.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw=="], + + "@trigger.dev/core/@opentelemetry/exporter-logs-otlp-http": ["@opentelemetry/exporter-logs-otlp-http@0.203.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.203.0", "@opentelemetry/core": "2.0.1", "@opentelemetry/otlp-exporter-base": "0.203.0", "@opentelemetry/otlp-transformer": "0.203.0", "@opentelemetry/sdk-logs": "0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-s0hys1ljqlMTbXx2XiplmMJg9wG570Z5lH7wMvrZX6lcODI56sG4HL03jklF63tBeyNwK2RV1/ntXGo3HgG4Qw=="], + + "@trigger.dev/core/@opentelemetry/exporter-metrics-otlp-http": ["@opentelemetry/exporter-metrics-otlp-http@0.203.0", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/otlp-exporter-base": "0.203.0", "@opentelemetry/otlp-transformer": "0.203.0", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-metrics": "2.0.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-HFSW10y8lY6BTZecGNpV3GpoSy7eaO0Z6GATwZasnT4bEsILp8UJXNG5OmEsz4SdwCSYvyCbTJdNbZP3/8LGCQ=="], + + "@trigger.dev/core/@opentelemetry/exporter-trace-otlp-http": ["@opentelemetry/exporter-trace-otlp-http@0.203.0", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/otlp-exporter-base": "0.203.0", "@opentelemetry/otlp-transformer": "0.203.0", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-ZDiaswNYo0yq/cy1bBLJFe691izEJ6IgNmkjm4C6kE9ub/OMQqDXORx2D2j8fzTBTxONyzusbaZlqtfmyqURPw=="], + + "@trigger.dev/core/@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.203.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.203.0", "import-in-the-middle": "^1.8.1", "require-in-the-middle": "^7.1.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ=="], + + "@trigger.dev/core/@opentelemetry/resources": ["@opentelemetry/resources@2.0.1", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw=="], + + "@trigger.dev/core/@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.203.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.203.0", "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-vM2+rPq0Vi3nYA5akQD2f3QwossDnTDLvKbea6u/A2NZ3XDkPxMfo/PNrDoXhDUD/0pPo2CdH5ce/thn9K0kLw=="], + + "@trigger.dev/core/@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.0.1", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ=="], + + "@trigger.dev/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.36.0", "", {}, "sha512-TtxJSRD8Ohxp6bKkhrm27JRHAxPczQA7idtcTOMYI+wQRRrfgqxHv1cFbCApcSnNjtXkmzFozn6jQtFrOmbjPQ=="], + + "@trigger.dev/core/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="], + + "@trigger.dev/core/jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="], + + "@trigger.dev/core/nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="], + + "@trigger.dev/core/socket.io": ["socket.io@4.7.4", "", { "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", "debug": "~4.3.2", "engine.io": "~6.5.2", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" } }, "sha512-DcotgfP1Zg9iP/dH9zvAQcWrE0TtbMVwXmlV4T4mqsvY+gw+LqUGPfx2AoVyRk0FLME+GQhufDMyacFmw7ksqw=="], + + "@trigger.dev/sdk/@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], + + "@trigger.dev/sdk/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.36.0", "", {}, "sha512-TtxJSRD8Ohxp6bKkhrm27JRHAxPczQA7idtcTOMYI+wQRRrfgqxHv1cFbCApcSnNjtXkmzFozn6jQtFrOmbjPQ=="], + "@ts-morph/common/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], "@types/body-parser/@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], @@ -6712,6 +6817,8 @@ "c12/dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="], + "c12/pkg-types": ["pkg-types@2.3.1", "", { "dependencies": { "confbox": "^0.2.4", "exsolve": "^1.0.8", "pathe": "^2.0.3" } }, "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg=="], + "cacheable-request/get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], "cacheable-request/mimic-response": ["mimic-response@4.0.0", "", {}, "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg=="], @@ -6792,7 +6899,9 @@ "engine.io/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], - "engine.io-client/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + "engine.io-client/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + + "engine.io-client/ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="], "escodegen/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], @@ -6816,10 +6925,14 @@ "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + "eslint-import-resolver-node/resolve": ["resolve@2.0.0-next.6", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "node-exports-info": "^1.6.0", "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA=="], + "eslint-import-resolver-webpack/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], "eslint-import-resolver-webpack/enhanced-resolve": ["enhanced-resolve@0.9.1", "", { "dependencies": { "graceful-fs": "^4.1.2", "memory-fs": "^0.2.0", "tapable": "^0.1.8" } }, "sha512-kxpoMgrdtkXZ5h0SeraBS1iRntpTpQ3R8ussdb38+UAFnMGX5DDyJXePm+OCHOcoXvHDw7mc2erbJBpDnl7TPw=="], + "eslint-import-resolver-webpack/resolve": ["resolve@2.0.0-next.6", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "node-exports-info": "^1.6.0", "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA=="], + "eslint-import-resolver-webpack/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], "eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], @@ -6844,10 +6957,10 @@ "eslint-plugin-n/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], - "eslint-plugin-n/resolve": ["resolve@1.22.12", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="], - "eslint-plugin-react/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], + "eslint-plugin-react/resolve": ["resolve@2.0.0-next.6", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "node-exports-info": "^1.6.0", "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA=="], + "eslint-plugin-react/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "eslint-plugin-unicorn/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], @@ -6886,8 +6999,6 @@ "filing-cabinet/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], - "filing-cabinet/resolve": ["resolve@1.22.12", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="], - "finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "find-cache-dir/pkg-dir": ["pkg-dir@7.0.0", "", { "dependencies": { "find-up": "^6.3.0" } }, "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA=="], @@ -6988,8 +7099,6 @@ "mixpanel/https-proxy-agent": ["https-proxy-agent@5.0.0", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA=="], - "mlly/pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], - "mocha/escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], "mocha/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], @@ -7034,7 +7143,9 @@ "nuqs/@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], - "onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + "nypm/citty": ["citty@0.2.2", "", {}, "sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w=="], + + "nypm/tinyexec": ["tinyexec@1.1.1", "", {}, "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg=="], "open-editor/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], @@ -7078,12 +7189,12 @@ "pkg-conf/find-up": ["find-up@6.3.0", "", { "dependencies": { "locate-path": "^7.1.0", "path-exists": "^5.0.0" } }, "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw=="], + "pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], + "playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], "postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - "postcss-import/resolve": ["resolve@1.22.12", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="], - "postcss-load-config/lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], "postcss-nested/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], @@ -7156,12 +7267,12 @@ "recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], - "require-in-the-middle/resolve": ["resolve@1.22.12", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="], - "requirejs-config-file/stringify-object": ["stringify-object@3.3.0", "", { "dependencies": { "get-own-enumerable-property-symbols": "^3.0.0", "is-obj": "^1.0.1", "is-regexp": "^1.0.0" } }, "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw=="], "resolve-import/glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="], + "restore-cursor/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + "rimraf/glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="], "router/path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], @@ -7212,6 +7323,8 @@ "socket.io-adapter/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + "socket.io-client/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], "stack-utils/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], @@ -7274,6 +7387,8 @@ "unplugin/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], + "vite-tsconfig-paths/tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="], + "webpack/eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="], "whatwg-encoding/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], @@ -7406,6 +7521,8 @@ "@artilleryio/int-core/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], + "@artilleryio/int-core/socket.io-client/engine.io-client": ["engine.io-client@6.6.4", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-parser": "~5.2.1", "ws": "~8.18.3", "xmlhttprequest-ssl": "~2.1.1" } }, "sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw=="], + "@asyncapi/parser/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], "@asyncapi/parser/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], @@ -7596,6 +7713,8 @@ "@dotenvx/dotenvx/execa/npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], + "@dotenvx/dotenvx/execa/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + "@dotenvx/dotenvx/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], "@dotenvx/dotenvx/which/isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="], @@ -7698,8 +7817,6 @@ "@mintlify/common/tailwindcss/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], - "@mintlify/common/tailwindcss/resolve": ["resolve@1.22.12", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="], - "@mintlify/common/unist-util-visit/unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], "@mintlify/link-rot/unist-util-visit/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], @@ -8060,6 +8177,14 @@ "@opentelemetry/sdk-trace-node/@opentelemetry/sdk-trace-base/@opentelemetry/resources": ["@opentelemetry/resources@2.0.1", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw=="], + "@prisma/config/c12/giget": ["giget@2.0.0", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "defu": "^6.1.4", "node-fetch-native": "^1.6.6", "nypm": "^0.6.0", "pathe": "^2.0.3" }, "bin": { "giget": "dist/cli.mjs" } }, "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA=="], + + "@prisma/config/c12/perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="], + + "@prisma/config/c12/pkg-types": ["pkg-types@2.3.1", "", { "dependencies": { "confbox": "^0.2.4", "exsolve": "^1.0.8", "pathe": "^2.0.3" } }, "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg=="], + + "@prisma/config/c12/rc9": ["rc9@2.1.2", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg=="], + "@prisma/instrumentation/@opentelemetry/instrumentation/@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.207.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-lAb0jQRVyleQQGiuuvCOTDVspc14nx6XJjP4FspJ1sNARo3Regq4ZZbrc3rN4b1TYSuUCvgH+UXUPug4SLOqEQ=="], "@prisma/instrumentation/@opentelemetry/instrumentation/import-in-the-middle": ["import-in-the-middle@2.0.6", "", { "dependencies": { "acorn": "^8.15.0", "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^2.2.0", "module-details-from-path": "^1.0.4" } }, "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw=="], @@ -8112,6 +8237,40 @@ "@tailwindcss/postcss/@tailwindcss/oxide/@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.4", "", { "os": "win32", "cpu": "x64" }, "sha512-ESlKG0EpVJQwRjXDDa9rLvhEAh0mhP1sF7sap9dNZT0yyl9SAG6T7gdP09EH0vIv0UNTlo6jPWyujD6559fZvw=="], + "@trigger.dev/core/@opentelemetry/api-logs/@opentelemetry/api": ["@opentelemetry/api@1.9.1", "", {}, "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q=="], + + "@trigger.dev/core/@opentelemetry/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="], + + "@trigger.dev/core/@opentelemetry/exporter-logs-otlp-http/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.203.0", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/otlp-transformer": "0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Wbxf7k+87KyvxFr5D7uOiSq/vHXWommvdnNE7vECO3tAhsA2GfOlpWINCMWUEPdHZ7tCXxw6Epp3vgx3jU7llQ=="], + + "@trigger.dev/core/@opentelemetry/exporter-logs-otlp-http/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.203.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.203.0", "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-logs": "0.203.0", "@opentelemetry/sdk-metrics": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Y8I6GgoCna0qDQ2W6GCRtaF24SnvqvA8OfeTi7fqigD23u8Jpb4R5KFv/pRvrlGagcCLICMIyh9wiejp4TXu/A=="], + + "@trigger.dev/core/@opentelemetry/exporter-metrics-otlp-http/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.203.0", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/otlp-transformer": "0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Wbxf7k+87KyvxFr5D7uOiSq/vHXWommvdnNE7vECO3tAhsA2GfOlpWINCMWUEPdHZ7tCXxw6Epp3vgx3jU7llQ=="], + + "@trigger.dev/core/@opentelemetry/exporter-metrics-otlp-http/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.203.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.203.0", "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-logs": "0.203.0", "@opentelemetry/sdk-metrics": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Y8I6GgoCna0qDQ2W6GCRtaF24SnvqvA8OfeTi7fqigD23u8Jpb4R5KFv/pRvrlGagcCLICMIyh9wiejp4TXu/A=="], + + "@trigger.dev/core/@opentelemetry/exporter-trace-otlp-http/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.203.0", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/otlp-transformer": "0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Wbxf7k+87KyvxFr5D7uOiSq/vHXWommvdnNE7vECO3tAhsA2GfOlpWINCMWUEPdHZ7tCXxw6Epp3vgx3jU7llQ=="], + + "@trigger.dev/core/@opentelemetry/exporter-trace-otlp-http/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.203.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.203.0", "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-logs": "0.203.0", "@opentelemetry/sdk-metrics": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Y8I6GgoCna0qDQ2W6GCRtaF24SnvqvA8OfeTi7fqigD23u8Jpb4R5KFv/pRvrlGagcCLICMIyh9wiejp4TXu/A=="], + + "@trigger.dev/core/@opentelemetry/resources/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="], + + "@trigger.dev/core/@opentelemetry/sdk-trace-base/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="], + + "@trigger.dev/core/execa/get-stream": ["get-stream@8.0.1", "", {}, "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="], + + "@trigger.dev/core/execa/human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="], + + "@trigger.dev/core/execa/npm-run-path": ["npm-run-path@5.3.0", "", { "dependencies": { "path-key": "^4.0.0" } }, "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ=="], + + "@trigger.dev/core/execa/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "@trigger.dev/core/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="], + + "@trigger.dev/core/socket.io/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + + "@trigger.dev/core/socket.io/engine.io": ["engine.io@6.5.5", "", { "dependencies": { "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", "cookie": "~0.4.1", "cors": "~2.8.5", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", "ws": "~8.17.1" } }, "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA=="], + "@ts-morph/common/minimatch/brace-expansion": ["brace-expansion@5.0.5", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="], "@types/body-parser/@types/node/undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="], @@ -8386,6 +8545,8 @@ "eslint-config-next/eslint-plugin-react-hooks/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "eslint-config-next/eslint-plugin-react-hooks/zod-validation-error": ["zod-validation-error@4.0.2", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ=="], + "eslint-formatter-pretty/ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], "eslint-formatter-pretty/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -8546,8 +8707,6 @@ "mixpanel/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], - "mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], - "mocha/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "msw/@inquirer/confirm/@inquirer/core": ["@inquirer/core@11.1.9", "", { "dependencies": { "@inquirer/ansi": "^2.0.5", "@inquirer/figures": "^2.0.5", "@inquirer/type": "^4.0.5", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-BDE4fG22uYh1bGSifcj7JSx119TVYNViMhMu85usp4Fswrzh6M0DV3yld64jA98uOAa2GSQ4Bg4bZRm2d2cwSg=="], @@ -8590,6 +8749,8 @@ "open-editor/execa/npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], + "open-editor/execa/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + "open-editor/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], "open-editor/open/define-lazy-prop": ["define-lazy-prop@2.0.0", "", {}, "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="], @@ -8696,8 +8857,6 @@ "read-pkg/normalize-package-data/hosted-git-info": ["hosted-git-info@2.8.9", "", {}, "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="], - "read-pkg/normalize-package-data/resolve": ["resolve@1.22.12", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="], - "read-pkg/normalize-package-data/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], "requirejs-config-file/stringify-object/is-obj": ["is-obj@1.0.1", "", {}, "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg=="], @@ -8708,6 +8867,8 @@ "resolve-import/glob/path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="], + "restore-cursor/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + "rimraf/glob/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], "rimraf/glob/path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="], @@ -8720,6 +8881,8 @@ "run-jxa/execa/npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], + "run-jxa/execa/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + "run-jxa/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], "schema-utils/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], @@ -8880,6 +9043,10 @@ "@artilleryio/int-core/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], + "@artilleryio/int-core/socket.io-client/engine.io-client/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + + "@artilleryio/int-core/socket.io-client/engine.io-client/xmlhttprequest-ssl": ["xmlhttprequest-ssl@2.1.2", "", {}, "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ=="], + "@autumn/server/ink/@alcalzone/ansi-tokenize/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], "@autumn/server/ink/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], @@ -8958,6 +9125,8 @@ "@aws-sdk/signature-v4/@smithy/signature-v4/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@1.1.0", "", { "dependencies": { "@smithy/is-array-buffer": "^1.1.0", "tslib": "^2.5.0" } }, "sha512-9m6NXE0ww+ra5HKHCHig20T+FAwxBAm7DIdwc/767uGWbRcY720ybgPacQNB96JMOI7xVr/CDa3oMzKmW4a+kw=="], + "@dotenvx/dotenvx/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + "@infisical/sdk/@aws-sdk/credential-providers/@aws-sdk/client-cognito-identity/@aws-sdk/core": ["@aws-sdk/core@3.598.0", "", { "dependencies": { "@smithy/core": "^2.2.1", "@smithy/protocol-http": "^4.0.1", "@smithy/signature-v4": "^3.1.0", "@smithy/smithy-client": "^3.1.2", "@smithy/types": "^3.1.0", "fast-xml-parser": "4.2.5", "tslib": "^2.6.2" } }, "sha512-HaSjt7puO5Cc7cOlrXFCW0rtA0BM9lvzjl56x0A20Pt+0wxXGeTOZZOkXQIepbrFkV2e/HYukuT9e99vXDm59g=="], "@infisical/sdk/@aws-sdk/credential-providers/@aws-sdk/client-cognito-identity/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.598.0", "", { "dependencies": { "@aws-sdk/types": "3.598.0", "@smithy/protocol-http": "^4.0.1", "@smithy/types": "^3.1.0", "tslib": "^2.6.2" } }, "sha512-WiaG059YBQwQraNejLIi0gMNkX7dfPZ8hDIhvMr5aVPRbaHH8AYF3iNSsXYCHvA2Cfa1O9haYXsuMF9flXnCmA=="], @@ -9184,8 +9353,6 @@ "@mintlify/scraping/@mintlify/common/tailwindcss/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], - "@mintlify/scraping/@mintlify/common/tailwindcss/resolve": ["resolve@1.22.12", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="], - "@mintlify/scraping/yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "@mintlify/scraping/yargs/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], @@ -9290,6 +9457,14 @@ "@tailwindcss/postcss/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "@trigger.dev/core/execa/npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], + + "@trigger.dev/core/socket.io/engine.io/@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], + + "@trigger.dev/core/socket.io/engine.io/cookie": ["cookie@0.4.2", "", {}, "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA=="], + + "@trigger.dev/core/socket.io/engine.io/ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="], + "@ts-morph/common/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], "@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], @@ -9458,6 +9633,10 @@ "nodemon/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + "open-editor/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + + "ora/cli-cursor/restore-cursor/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + "ora/log-symbols/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], "ora/log-symbols/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], @@ -9500,6 +9679,8 @@ "rimraf/glob/minimatch/brace-expansion": ["brace-expansion@5.0.5", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="], + "run-jxa/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + "sdk-test/next/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], "shadcn/cosmiconfig/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], @@ -9684,6 +9865,8 @@ "@mintlify/scraping/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "@trigger.dev/core/socket.io/engine.io/@types/node/undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="], + "artillery-plugin-ensure/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], "artillery-plugin-publish-metrics/@opentelemetry/exporter-metrics-otlp-grpc/@opentelemetry/otlp-transformer/protobufjs/@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], @@ -9726,6 +9909,8 @@ "meow/read-pkg-up/read-pkg/normalize-package-data/hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="], + "ora/cli-cursor/restore-cursor/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + "ora/log-symbols/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], "ora/log-symbols/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], diff --git a/package.json b/package.json index 17f2dc385..58600658b 100644 --- a/package.json +++ b/package.json @@ -128,6 +128,7 @@ "@aws-sdk/client-sqs": "^3.985.0", "@better-auth/core": "catalog:", "@better-auth/oauth-provider": "catalog:", + "@trigger.dev/sdk": "4.4.5", "@wooorm/starry-night": "^3.8.0", "ag-charts-react": "^12.3.0", "better-auth": "catalog:", @@ -139,6 +140,7 @@ "devDependencies": { "@better-auth/cli": "^1.4.21", "@biomejs/biome": "^2.2.7", + "@trigger.dev/build": "4.4.5", "@types/node": "^24.9.1", "concurrently": "^9.2.1", "dotenv": "^16.6.1", diff --git a/scripts/dev.ts b/scripts/dev.ts index 0219e1126..aa46019c0 100644 --- a/scripts/dev.ts +++ b/scripts/dev.ts @@ -174,6 +174,14 @@ async function startDev() { ? `"cd server && bun ${workersScript}"` : `"cd server && bun ${workersScript}"`, ); + + names.push("trigger"); + colors.push("cyan"); + cmds.push( + isWindows + ? `"bunx trigger.dev@latest dev"` + : `"bunx trigger.dev@latest dev"`, + ); } names.push("vite", "checkout"); diff --git a/server/experiments/explainCustomerFilter.ts b/server/experiments/explainCustomerFilter.ts index 9ee1774da..8db2ceb22 100644 --- a/server/experiments/explainCustomerFilter.ts +++ b/server/experiments/explainCustomerFilter.ts @@ -2,7 +2,7 @@ import { AppEnv } from "@autumn/shared"; import { buildCustomerCount, buildCustomerSelect, -} from "@autumn/shared/api/migrations/compiler/buildCustomerQuery.js"; +} from "@/internal/migrations/v2/filters/customers/buildCustomerSelect.js"; import type { CustomerFilter } from "@autumn/shared/api/migrations/filters/customerFilter.js"; import { sql, type SQL } from "drizzle-orm"; import { PgDialect } from "drizzle-orm/pg-core"; diff --git a/server/src/external/autumn/autumnCli.ts b/server/src/external/autumn/autumnCli.ts index 9fd263629..c651dd4a8 100644 --- a/server/src/external/autumn/autumnCli.ts +++ b/server/src/external/autumn/autumnCli.ts @@ -47,9 +47,9 @@ import { type UpdateBalanceParamsV0, type UpdateSubscriptionV0Params, } from "@autumn/shared"; -import type { PrepareResponse } from "@/internal/migrations/v2/prepare/types"; import { defaultApiVersion } from "@tests/constants.js"; import { timeout } from "@tests/utils/genUtils"; +import type { PrepareResponse } from "@/internal/migrations/v2/prepare/types"; export default class AutumnError extends Error { message: string; @@ -956,6 +956,27 @@ export class AutumnInt { const data = await this.post(`/migrations.update`, params); return data as Migration; }, + delete: async (params: { id: string }): Promise => { + const data = await this.post(`/migrations.delete`, params); + return data as Migration; + }, + /** + * Idempotent setup helper for tests / scripts: best-effort deletes + * the migration if it exists, then creates a fresh one with the + * given filter + operations. Avoids the (org_id, env, id) unique + * constraint when reseeding fixtures. + */ + deleteAndCreate: async (params: { + id: string; + filter?: MigrationFilter | null; + operations?: Operations | null; + }): Promise => { + try { + await this.post(`/migrations.delete`, { id: params.id }); + } catch {} + const data = await this.post(`/migrations.create`, params); + return data as Migration; + }, prepare: async (params: { id: string; dry_run: boolean; @@ -963,6 +984,21 @@ export class AutumnInt { const data = await this.post(`/migrations.prepare`, params); return data as PrepareResponse; }, + run: async (params: { + id: string; + dry_run?: boolean; + }): Promise<{ + migration_id: string; + dry_run: boolean; + run_id: string; + }> => { + const data = await this.post(`/migrations.run`, params); + return data as { + migration_id: string; + dry_run: boolean; + run_id: string; + }; + }, }; balances = { diff --git a/server/src/external/infisical/fetchInfisicalSecrets.ts b/server/src/external/infisical/fetchInfisicalSecrets.ts new file mode 100644 index 000000000..1ca76e2d8 --- /dev/null +++ b/server/src/external/infisical/fetchInfisicalSecrets.ts @@ -0,0 +1,115 @@ +/** + * Pure REST fetcher for Infisical secrets. Used at trigger.dev DEPLOY + * time via `syncEnvVars` to push secrets to the cloud env. Kept SDK-free + * so trigger.config.ts can import it without bloating the build. + * + * Runtime code uses `initInfisical` (SDK-based, populates process.env). + */ + +export type InfisicalSyncEnvVar = { name: string; value: string }; + +export type FetchInfisicalSecretsArgs = { + clientId?: string | null; + clientSecret?: string | null; + projectId?: string | null; + /** Defaults to "prod" if not set. */ + environment?: string | null; + secretPath?: string; + recursive?: boolean; + includeImports?: boolean; +}; + +/** + * Authenticate via Universal Auth, fetch secrets at the given path, and + * return them as `{ name, value }[]`. Imported groups are flattened in + * after primary secrets, with first-write-wins de-duplication. + */ +export const fetchInfisicalSecrets = async ({ + clientId, + clientSecret, + projectId, + environment, + secretPath = "/", + recursive = true, + includeImports = true, +}: FetchInfisicalSecretsArgs): Promise => { + const env = environment ?? "prod"; + + if (!clientId || !clientSecret || !projectId) { + throw new Error( + "Missing Infisical credentials. Set INFISICAL_CLIENT_ID, INFISICAL_CLIENT_SECRET, INFISICAL_PROJECT_ID.", + ); + } + + const authRes = await fetch( + "https://app.infisical.com/api/v1/auth/universal-auth/login", + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ clientId, clientSecret }), + }, + ); + if (!authRes.ok) + throw new Error( + `Infisical auth failed: ${authRes.status} ${await authRes.text()}`, + ); + const { accessToken } = (await authRes.json()) as { accessToken: string }; + + const params = new URLSearchParams({ + environment: env, + workspaceId: projectId, + secretPath, + recursive: String(recursive), + includeImports: String(includeImports), + }); + const secretsRes = await fetch( + `https://app.infisical.com/api/v3/secrets/raw?${params}`, + { headers: { Authorization: `Bearer ${accessToken}` } }, + ); + if (!secretsRes.ok) + throw new Error( + `Infisical secrets failed: ${secretsRes.status} ${await secretsRes.text()}`, + ); + const data = (await secretsRes.json()) as { + secrets: Array<{ secretKey: string; secretValue: string }>; + imports?: Array<{ + secrets: Array<{ secretKey: string; secretValue: string }>; + }>; + }; + + const envVars: InfisicalSyncEnvVar[] = []; + const seen = new Set(); + + const push = (key: string, value: string) => { + if (!key || !value || seen.has(key)) return; + envVars.push({ name: key, value }); + seen.add(key); + }; + + for (const secret of data.secrets) push(secret.secretKey, secret.secretValue); + for (const importGroup of data.imports ?? []) + for (const secret of importGroup.secrets) + push(secret.secretKey, secret.secretValue); + + console.log( + `[fetchInfisicalSecrets] Synced ${envVars.length} secrets from Infisical (env=${env}, path=${secretPath})`, + ); + return envVars; +}; + +/** + * Read the four credential vars (`INFISICAL_CLIENT_ID`, `_SECRET`, + * `_PROJECT_ID`, `_ENVIRONMENT`) from the local process env first then + * trigger.dev's deploy-time `ctx.env`. Convenience for `syncEnvVars`. + */ +export const fetchInfisicalSecretsFromEnv = ( + ctxEnv: Record = {}, +): Promise => + fetchInfisicalSecrets({ + clientId: process.env.INFISICAL_CLIENT_ID ?? ctxEnv.INFISICAL_CLIENT_ID, + clientSecret: + process.env.INFISICAL_CLIENT_SECRET ?? ctxEnv.INFISICAL_CLIENT_SECRET, + projectId: process.env.INFISICAL_PROJECT_ID ?? ctxEnv.INFISICAL_PROJECT_ID, + environment: + process.env.INFISICAL_ENVIRONMENT ?? ctxEnv.INFISICAL_ENVIRONMENT, + }); diff --git a/server/src/external/logtail/logtailUtils.ts b/server/src/external/logtail/logtailUtils.ts index 29fa9ca91..d110eb816 100644 --- a/server/src/external/logtail/logtailUtils.ts +++ b/server/src/external/logtail/logtailUtils.ts @@ -1,5 +1,6 @@ import "dotenv/config"; +import type pino from "pino"; import { initLogger } from "@/utils/logging/initLogger"; const pinoLogger = initLogger(); @@ -72,34 +73,45 @@ const createLogMethod = (pinoMethod: any, logtailMethod?: any) => { }; }; -export const createLogger = () => { - // Helper function to create logger structure recursively - const createLoggerStructure = (basePinoLogger: any) => { - return { - debug: createLogMethod(basePinoLogger.debug.bind(basePinoLogger)), - info: createLogMethod(basePinoLogger.info.bind(basePinoLogger)), - warn: createLogMethod(basePinoLogger.warn.bind(basePinoLogger)), - error: createLogMethod(basePinoLogger.error.bind(basePinoLogger)), - child: ({ - context, - onlyProd = false, - }: { - context: any; - onlyProd?: boolean; - }) => { - if (onlyProd && process.env.NODE_ENV !== "production") { - return createLoggerStructure(basePinoLogger); - } +const createLoggerStructure = (basePinoLogger: pino.Logger): Logger => ({ + debug: createLogMethod(basePinoLogger.debug.bind(basePinoLogger)), + info: createLogMethod(basePinoLogger.info.bind(basePinoLogger)), + warn: createLogMethod(basePinoLogger.warn.bind(basePinoLogger)), + error: createLogMethod(basePinoLogger.error.bind(basePinoLogger)), + child: ({ + context, + onlyProd = false, + }: { + context: any; + onlyProd?: boolean; + }) => { + if (onlyProd && process.env.NODE_ENV !== "production") { + return createLoggerStructure(basePinoLogger); + } - const childPinoLogger = basePinoLogger.child(context); - return createLoggerStructure(childPinoLogger); - }, - }; - }; + const childPinoLogger = basePinoLogger.child(context); + return createLoggerStructure(childPinoLogger); + }, +}); - // Create the root logger using the helper function - return createLoggerStructure(pinoLogger); +export const createLogger = () => createLoggerStructure(pinoLogger); + +/** + * Lazy dual-output logger (stdout JSON + axiom). Used only by long-running + * trigger.dev tasks so their lines surface in both the trigger run UI and + * our axiom store. Default `logger` / `createLogger` are unaffected. + */ +let dualPinoLogger: pino.Logger | null = null; +export const createDualLogger = () => { + if (!dualPinoLogger) dualPinoLogger = initLogger({ mode: "dual" }); + return createLoggerStructure(dualPinoLogger); }; export const logger = createLogger(); -export type Logger = ReturnType; +export type Logger = { + debug: (...args: any[]) => void; + info: (...args: any[]) => void; + warn: (...args: any[]) => void; + error: (...args: any[]) => void; + child: (args: { context: any; onlyProd?: boolean }) => Logger; +}; diff --git a/server/src/init.ts b/server/src/init.ts index f9226a2ed..1af43235c 100644 --- a/server/src/init.ts +++ b/server/src/init.ts @@ -26,6 +26,8 @@ import "./internal/misc/edgeConfig/orgLimitsStore.js"; import "./internal/misc/stripeSync/stripeSyncStore.js"; import "./internal/misc/redisV2Cache/redisV2CacheStore.js"; import "./internal/misc/jobQueues/jobQueueStore.js"; +// Side-effect: configures trigger.dev SDK to use TRIGGER_SERVER_SECRET_KEY. +import "./trigger/configureTrigger.js"; import { closeStripeSyncEngine } from "@autumn/stripe-sync"; import { startRedisMonitor, diff --git a/server/src/internal/migrations/v2/actions/index.ts b/server/src/internal/migrations/v2/actions/index.ts deleted file mode 100644 index ed859ef95..000000000 --- a/server/src/internal/migrations/v2/actions/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Higher-level migration business logic (preview, run, idempotency, - * billing-path bridging). Empty in phase 1 — phase 1 is pure CRUD on the - * migration entity, handled directly via `migrationRepo`. Phase 2+ adds - * verbs here as ops grow runtime semantics. - */ -export const migrationActions = {} as const; diff --git a/server/src/internal/migrations/v2/actions/runMigration.ts b/server/src/internal/migrations/v2/actions/runMigration.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/server/src/internal/migrations/v2/filters/customers/buildCustomerSelect.ts b/server/src/internal/migrations/v2/filters/customers/buildCustomerSelect.ts new file mode 100644 index 000000000..920065a26 --- /dev/null +++ b/server/src/internal/migrations/v2/filters/customers/buildCustomerSelect.ts @@ -0,0 +1,62 @@ +import type { CustomerFilter } from "@autumn/shared"; +import { compileFilter } from "@autumn/shared/api/migrations/compiler/compileFilter.js"; +import type { ResolutionContext } from "@autumn/shared/api/migrations/compiler/filterToIr/resolutionContext.js"; +import { type SQL, sql } from "drizzle-orm"; +import { rawWithParamsToDrizzle } from "../rawWithParamsToDrizzle.js"; + +export type CustomerQueryArgs = { + orgId: string; + env: string; + filter: CustomerFilter; + ctx: ResolutionContext; +}; + +const compileWhere = ({ orgId, env, filter, ctx }: CustomerQueryArgs): SQL => + rawWithParamsToDrizzle( + compileFilter({ filter, ctx, ambient: { orgId, env } }), + ); + +/** + * Full SELECT. Returns `{ internal_id, id }` rows newest-first via keyset + * pagination on `c.internal_id DESC`, so successive iterations over an + * unchanged customer set yield rows in the same order. + */ +export const buildCustomerSelect = ({ + orgId, + env, + filter, + ctx, + limit, + afterInternalId, +}: CustomerQueryArgs & { + limit?: number; + afterInternalId?: string; +}): SQL => { + const where = compileWhere({ orgId, env, filter, ctx }); + const cursor = afterInternalId + ? sql`AND c.internal_id < ${afterInternalId}` + : sql``; + const limitClause = limit !== undefined ? sql`LIMIT ${limit}` : sql``; + return sql` + SELECT c.internal_id, c.id + FROM customers c + WHERE (${where}) ${cursor} + ORDER BY c.internal_id DESC + ${limitClause} + `; +}; + +/** COUNT(*) applying the same filter. */ +export const buildCustomerCount = ({ + orgId, + env, + filter, + ctx, +}: CustomerQueryArgs): SQL => { + const where = compileWhere({ orgId, env, filter, ctx }); + return sql` + SELECT COUNT(*)::bigint AS count + FROM customers c + WHERE (${where}) + `; +}; diff --git a/server/src/internal/migrations/v2/filters/customers/filterCustomers.ts b/server/src/internal/migrations/v2/filters/customers/filterCustomers.ts new file mode 100644 index 000000000..d9daed35c --- /dev/null +++ b/server/src/internal/migrations/v2/filters/customers/filterCustomers.ts @@ -0,0 +1,55 @@ +import type { CustomerFilter } from "@autumn/shared"; +import type { AutumnContext } from "@/honoUtils/HonoEnv.js"; +import { iterateOverFilterResults } from "../iterateOverFilterResults.js"; +import { + buildCustomerCount, + buildCustomerSelect, +} from "./buildCustomerSelect.js"; + +export type CustomerRow = { internal_id: string; id: string | null }; + +/** + * Pure inner: takes a CustomerFilter directly. Used by `runFilter` shim + * (Migration-fed) and reusable from scripts that don't have a Migration. + */ +export const filterCustomers = ({ + ctx, + filter, + batchSize, +}: { + ctx: AutumnContext; + filter: CustomerFilter; + batchSize?: number; +}): AsyncGenerator => { + const args = { + orgId: ctx.org.id, + env: ctx.env, + filter, + ctx: { features: ctx.features }, + }; + return iterateOverFilterResults({ + db: ctx.db, + buildSelect: ({ limit, afterInternalId }) => + buildCustomerSelect({ ...args, limit, afterInternalId }), + batchSize, + }); +}; + +/** Count of customers matching `filter`. */ +export const countCustomers = async ({ + ctx, + filter, +}: { + ctx: AutumnContext; + filter: CustomerFilter; +}): Promise => { + const [{ count }] = (await ctx.db.execute( + buildCustomerCount({ + orgId: ctx.org.id, + env: ctx.env, + filter, + ctx: { features: ctx.features }, + }), + )) as Array<{ count: bigint | number }>; + return Number(count); +}; diff --git a/server/src/internal/migrations/v2/filters/customers/index.ts b/server/src/internal/migrations/v2/filters/customers/index.ts new file mode 100644 index 000000000..3c22f081a --- /dev/null +++ b/server/src/internal/migrations/v2/filters/customers/index.ts @@ -0,0 +1,2 @@ +export * from "./buildCustomerSelect.js"; +export * from "./filterCustomers.js"; diff --git a/server/src/internal/migrations/v2/filters/getFilterCount.ts b/server/src/internal/migrations/v2/filters/getFilterCount.ts new file mode 100644 index 000000000..b85ddb000 --- /dev/null +++ b/server/src/internal/migrations/v2/filters/getFilterCount.ts @@ -0,0 +1,24 @@ +import type { Migration } from "@autumn/shared"; +import type { AutumnContext } from "@/honoUtils/HonoEnv.js"; +import type { RunScopeKind } from "../run/types/runScope.js"; +import { countCustomers } from "./customers/filterCustomers.js"; + +/** Migration-fed shim. Delegates to per-kind pure counters. */ +export const getFilterCount = async ({ + ctx, + migration, + kind, +}: { + ctx: AutumnContext; + migration: Migration; + kind: RunScopeKind; +}): Promise => { + if (kind !== "customer") + throw new Error( + `getFilterCount: scope kind "${kind}" not supported yet (phase 2+)`, + ); + return countCustomers({ + ctx, + filter: migration.filter?.customer ?? {}, + }); +}; diff --git a/server/src/internal/migrations/v2/filters/index.ts b/server/src/internal/migrations/v2/filters/index.ts new file mode 100644 index 000000000..09d2486ef --- /dev/null +++ b/server/src/internal/migrations/v2/filters/index.ts @@ -0,0 +1,5 @@ +export * from "./customers/index.js"; +export * from "./getFilterCount.js"; +export * from "./iterateOverFilterResults.js"; +export * from "./rawWithParamsToDrizzle.js"; +export * from "./runFilter.js"; diff --git a/server/src/internal/migrations/v2/filters/iterateOverFilterResults.ts b/server/src/internal/migrations/v2/filters/iterateOverFilterResults.ts new file mode 100644 index 000000000..f6d7b9ea0 --- /dev/null +++ b/server/src/internal/migrations/v2/filters/iterateOverFilterResults.ts @@ -0,0 +1,30 @@ +import type { SQL } from "drizzle-orm"; + +export const DEFAULT_BATCH_SIZE = 10_000; + +/** + * Iterate keyset-paginated rows in batches. Caller supplies a `buildSelect` + * closure that returns the SQL for the next page given a cursor; rows + * MUST include `internal_id` for the cursor to advance. + */ +export async function* iterateOverFilterResults< + TRow extends { internal_id: string }, +>({ + db, + buildSelect, + batchSize = DEFAULT_BATCH_SIZE, +}: { + db: { execute: (query: SQL) => Promise }; + buildSelect: (args: { limit: number; afterInternalId?: string }) => SQL; + batchSize?: number; +}): AsyncGenerator { + let cursor: string | undefined; + while (true) { + const query = buildSelect({ limit: batchSize, afterInternalId: cursor }); + const rows = (await db.execute(query)) as unknown as TRow[]; + if (rows.length === 0) return; + yield rows; + if (rows.length < batchSize) return; + cursor = rows[rows.length - 1].internal_id; + } +} diff --git a/server/src/internal/migrations/v2/filters/rawWithParamsToDrizzle.ts b/server/src/internal/migrations/v2/filters/rawWithParamsToDrizzle.ts new file mode 100644 index 000000000..e7a622c5b --- /dev/null +++ b/server/src/internal/migrations/v2/filters/rawWithParamsToDrizzle.ts @@ -0,0 +1,22 @@ +import { type SQL, sql } from "drizzle-orm"; + +/** Convert the compiler's `{ sql, params }` output to a Drizzle SQL chunk. */ +export const rawWithParamsToDrizzle = ({ + sql: raw, + params, +}: { + sql: string; + params: readonly unknown[]; +}): SQL => { + const parts = raw.split("?"); + if (parts.length - 1 !== params.length) + throw new Error( + `Placeholder/param count mismatch: ${parts.length - 1} placeholders vs ${params.length} params`, + ); + const chunks: SQL[] = []; + for (let i = 0; i < parts.length; i++) { + chunks.push(sql.raw(parts[i])); + if (i < params.length) chunks.push(sql`${params[i]}`); + } + return sql.join(chunks, sql.raw("")); +}; diff --git a/server/src/internal/migrations/v2/filters/runFilter.ts b/server/src/internal/migrations/v2/filters/runFilter.ts new file mode 100644 index 000000000..f887abdeb --- /dev/null +++ b/server/src/internal/migrations/v2/filters/runFilter.ts @@ -0,0 +1,48 @@ +import type { Migration } from "@autumn/shared"; +import type { AutumnContext } from "../../../../honoUtils/HonoEnv.js"; +import type { RunScopeItem, RunScopeKind } from "../run/types/runScope.js"; +import { + countCustomers, + filterCustomers, +} from "./customers/filterCustomers.js"; + +/** + * Migration-fed shim. Dispatches by scope kind, unwraps the relevant + * filter from `migration.filter`, and delegates to the pure inner fns + * (`countCustomers` / `filterCustomers`). Empty filter ⇒ whole org+env. + */ +export const runFilter = async ({ + ctx, + migration, + kind, +}: { + ctx: AutumnContext; + migration: Migration; + kind: RunScopeKind; +}): Promise<{ + kind: RunScopeKind; + count: number; + iterate: () => AsyncGenerator; +}> => { + if (kind !== "customer") + throw new Error( + `runFilter: scope kind "${kind}" not supported yet (phase 2+)`, + ); + + const filter = migration.filter?.customer ?? {}; + const count = await countCustomers({ ctx, filter }); + + const iterate = async function* () { + for await (const batch of filterCustomers({ ctx, filter })) { + yield batch.map( + (row): RunScopeItem => ({ + kind: "customer", + internal_id: row.internal_id, + id: row.id, + }), + ); + } + }; + + return { kind, count, iterate }; +}; diff --git a/server/src/internal/migrations/v2/handlers/handleCreateMigration/handleCreateMigration.ts b/server/src/internal/migrations/v2/handlers/handleCreateMigration.ts similarity index 96% rename from server/src/internal/migrations/v2/handlers/handleCreateMigration/handleCreateMigration.ts rename to server/src/internal/migrations/v2/handlers/handleCreateMigration.ts index bd95e46dc..ac1439841 100644 --- a/server/src/internal/migrations/v2/handlers/handleCreateMigration/handleCreateMigration.ts +++ b/server/src/internal/migrations/v2/handlers/handleCreateMigration.ts @@ -21,6 +21,6 @@ export const handleCreateMigration = createRoute({ const migration = await migrationRepo.insert({ ctx, insert }); - return c.json(migration, 201); + return c.json(migration); }, }); diff --git a/server/src/internal/migrations/v2/handlers/handleDeleteMigration.ts b/server/src/internal/migrations/v2/handlers/handleDeleteMigration.ts new file mode 100644 index 000000000..da3001cbf --- /dev/null +++ b/server/src/internal/migrations/v2/handlers/handleDeleteMigration.ts @@ -0,0 +1,28 @@ +import { ErrCode, RecaseError, Scopes } from "@autumn/shared"; +import { z } from "zod/v4"; +import { createRoute } from "@/honoMiddlewares/routeHandler"; +import { migrationRepo } from "@/internal/migrations/v2/repos/index.js"; + +const DeleteMigrationBody = z.object({ + id: z.string(), +}); + +/** POST /migrations.delete — delete by user `id`. */ +export const handleDeleteMigration = createRoute({ + scopes: [Scopes.Migrations.Write], + body: DeleteMigrationBody, + handler: async (c) => { + const ctx = c.get("ctx"); + const { id } = c.req.valid("json"); + + const deleted = await migrationRepo.delete({ ctx, id }); + if (!deleted) + throw new RecaseError({ + message: `Migration ${id} not found`, + code: ErrCode.MigrationNotFound, + statusCode: 404, + }); + + return c.json(deleted); + }, +}); diff --git a/server/src/internal/migrations/v2/handlers/handleListMigrations/handleListMigrations.ts b/server/src/internal/migrations/v2/handlers/handleListMigrations.ts similarity index 100% rename from server/src/internal/migrations/v2/handlers/handleListMigrations/handleListMigrations.ts rename to server/src/internal/migrations/v2/handlers/handleListMigrations.ts diff --git a/server/src/internal/migrations/v2/handlers/handlePatchMigration/handlePatchMigration.ts b/server/src/internal/migrations/v2/handlers/handlePatchMigration.ts similarity index 100% rename from server/src/internal/migrations/v2/handlers/handlePatchMigration/handlePatchMigration.ts rename to server/src/internal/migrations/v2/handlers/handlePatchMigration.ts diff --git a/server/src/internal/migrations/v2/handlers/handlePrepareMigration/handlePrepareMigration.ts b/server/src/internal/migrations/v2/handlers/handlePrepareMigration.ts similarity index 76% rename from server/src/internal/migrations/v2/handlers/handlePrepareMigration/handlePrepareMigration.ts rename to server/src/internal/migrations/v2/handlers/handlePrepareMigration.ts index c6497d87c..dd4f3a119 100644 --- a/server/src/internal/migrations/v2/handlers/handlePrepareMigration/handlePrepareMigration.ts +++ b/server/src/internal/migrations/v2/handlers/handlePrepareMigration.ts @@ -17,13 +17,7 @@ export const handlePrepareMigration = createRoute({ const ctx = c.get("ctx"); const { id, dry_run } = c.req.valid("json"); - const [migration] = await migrationRepo.get({ ctx, id }); - if (!migration) - throw new RecaseError({ - message: `Migration ${id} not found`, - code: ErrCode.MigrationNotFound, - statusCode: 404, - }); + const migration = await migrationRepo.find({ ctx, id }); if (!migration.operations) throw new RecaseError({ @@ -32,7 +26,7 @@ export const handlePrepareMigration = createRoute({ statusCode: 400, }); - const response = await runPrepare({ ctx, migration, dry_run }); + const { response } = await runPrepare({ ctx, migration, dry_run }); return c.json(response); }, }); diff --git a/server/src/internal/migrations/v2/handlers/handleRunMigration.ts b/server/src/internal/migrations/v2/handlers/handleRunMigration.ts new file mode 100644 index 000000000..bb0baf203 --- /dev/null +++ b/server/src/internal/migrations/v2/handlers/handleRunMigration.ts @@ -0,0 +1,51 @@ +import { ErrCode, RecaseError, Scopes } from "@autumn/shared"; +import { z } from "zod/v4"; +import { createRoute } from "@/honoMiddlewares/routeHandler"; +import { migrationRepo } from "@/internal/migrations/v2/repos/index.js"; +import { runMigrationTask } from "@/trigger/migrations/runMigrationTask.js"; + +const RunMigrationBody = z.object({ + id: z.string(), + dry_run: z.boolean().default(false), +}); + +/** + * POST /migrations.run — kick off a migration on trigger.dev. Returns the + * trigger run handle so the dashboard can poll status. In dev we route + * to EU so dev runs don't touch the production US region. + */ +export const handleRunMigration = createRoute({ + scopes: [Scopes.Migrations.Write], + body: RunMigrationBody, + handler: async (c) => { + const ctx = c.get("ctx"); + const { id, dry_run } = c.req.valid("json"); + + const migration = await migrationRepo.find({ ctx, id }); + + if (!migration.operations) + throw new RecaseError({ + message: `Migration ${id} has no operations to run`, + code: ErrCode.InvalidRequest, + statusCode: 400, + }); + + const isDev = process.env.NODE_ENV === "development"; + + const handle = await runMigrationTask.trigger( + { + orgId: ctx.org.id, + env: ctx.env, + migrationId: id, + dryRun: dry_run, + }, + isDev ? { region: "eu-west-1" } : undefined, + ); + + return c.json({ + migration_id: id, + dry_run, + run_id: handle.id, + }); + }, +}); diff --git a/server/src/internal/migrations/v2/migrationRouter.ts b/server/src/internal/migrations/v2/migrationRouter.ts index 830e09831..5da230cbc 100644 --- a/server/src/internal/migrations/v2/migrationRouter.ts +++ b/server/src/internal/migrations/v2/migrationRouter.ts @@ -1,9 +1,11 @@ import { Hono } from "hono"; import type { HonoEnv } from "@/honoUtils/HonoEnv.js"; -import { handleCreateMigration } from "./handlers/handleCreateMigration/handleCreateMigration.js"; -import { handleListMigrations } from "./handlers/handleListMigrations/handleListMigrations.js"; -import { handlePatchMigration } from "./handlers/handlePatchMigration/handlePatchMigration.js"; -import { handlePrepareMigration } from "./handlers/handlePrepareMigration/handlePrepareMigration.js"; +import { handleCreateMigration } from "./handlers/handleCreateMigration.js"; +import { handleDeleteMigration } from "./handlers/handleDeleteMigration.js"; +import { handleListMigrations } from "./handlers/handleListMigrations.js"; +import { handlePatchMigration } from "./handlers/handlePatchMigration.js"; +import { handlePrepareMigration } from "./handlers/handlePrepareMigration.js"; +import { handleRunMigration } from "./handlers/handleRunMigration.js"; /** * V2 user-facing migrations RPC router. Distinct from the legacy @@ -15,4 +17,6 @@ export const migrationRpcRouter = new Hono(); migrationRpcRouter.post("/migrations.create", ...handleCreateMigration); migrationRpcRouter.post("/migrations.list", ...handleListMigrations); migrationRpcRouter.post("/migrations.update", ...handlePatchMigration); +migrationRpcRouter.post("/migrations.delete", ...handleDeleteMigration); migrationRpcRouter.post("/migrations.prepare", ...handlePrepareMigration); +migrationRpcRouter.post("/migrations.run", ...handleRunMigration); diff --git a/server/src/internal/migrations/v2/prepare/index.ts b/server/src/internal/migrations/v2/prepare/index.ts index fa6be3268..059215c1f 100644 --- a/server/src/internal/migrations/v2/prepare/index.ts +++ b/server/src/internal/migrations/v2/prepare/index.ts @@ -1,4 +1,5 @@ export * from "./inferImplicitPrep.js"; export * from "./modules/ensurePricesAndEntitlements/index.js"; export * from "./runPrepare.js"; +export * from "./runPrepareModules.js"; export * from "./types/index.js"; diff --git a/server/src/internal/migrations/v2/prepare/inferImplicitPrep.ts b/server/src/internal/migrations/v2/prepare/inferImplicitPrep.ts index 1fbc4c96a..bbad60be8 100644 --- a/server/src/internal/migrations/v2/prepare/inferImplicitPrep.ts +++ b/server/src/internal/migrations/v2/prepare/inferImplicitPrep.ts @@ -1,13 +1,10 @@ -import type { Migration } from "@autumn/shared"; +import type { Migration, Operations } from "@autumn/shared"; import { - ensurePricesAndEntitlements, type EnsurePricesAndEntitlementsInput, + ensurePricesAndEntitlements, } from "./modules/ensurePricesAndEntitlements/index.js"; -/** - * One instance of a prep module to run. The orchestrator calls each - * instance's `plan` / `apply` in order. - */ +/** One instance of a prep module to run. */ export type ImplicitPrepInstance = { key: string; module: typeof ensurePricesAndEntitlements; @@ -15,18 +12,17 @@ export type ImplicitPrepInstance = { }; /** - * Walk the migration's operations and derive which prep modules need to - * run. Phase 1 only emits `ensure_prices_and_entitlements` from - * `update_plans[].add_items[]`. - * - * Module key format: `::` so distinct - * (target, feature) pairs get distinct prep entries. + * Pure walker. Takes an `operations` object directly so scripts (and + * any other caller) can derive prep instances without a Migration row. + * Module key format: `::`. */ -export const inferImplicitPrep = ( - migration: Migration, -): ImplicitPrepInstance[] => { +export const inferPrepareModules = ({ + operations, +}: { + operations: Operations | null | undefined; +}): ImplicitPrepInstance[] => { const instances: ImplicitPrepInstance[] = []; - const updatePlans = migration.operations?.customer?.update_plans ?? []; + const updatePlans = operations?.customer?.update_plans ?? []; for (const op of updatePlans) { const planId = @@ -35,20 +31,23 @@ export const inferImplicitPrep = ( for (const item of op.add_items ?? []) { if (!item.feature_id) continue; - // Phase 1 constraint: only entitlement-only items. Priced items - // will be handled by the same module in phase 2+. + // Phase 1 constraint: entitlement-only items. Priced items will be + // handled by the same module in phase 2+. if (item.price) continue; instances.push({ key: `ensure_prices_and_entitlements:${item.feature_id}:${planId}`, module: ensurePricesAndEntitlements, - input: { - target_plan_id: planId, - feature_id: item.feature_id, - }, + input: { target_plan_id: planId, feature_id: item.feature_id }, }); } } return instances; }; + +/** Migration-fed shim. */ +export const inferImplicitPrep = ( + migration: Migration, +): ImplicitPrepInstance[] => + inferPrepareModules({ operations: migration.operations }); diff --git a/server/src/internal/migrations/v2/prepare/modules/ensurePricesAndEntitlements/ensurePricesAndEntitlements.ts b/server/src/internal/migrations/v2/prepare/modules/ensurePricesAndEntitlements/ensurePricesAndEntitlements.ts index da060c5d8..d1bee46bb 100644 --- a/server/src/internal/migrations/v2/prepare/modules/ensurePricesAndEntitlements/ensurePricesAndEntitlements.ts +++ b/server/src/internal/migrations/v2/prepare/modules/ensurePricesAndEntitlements/ensurePricesAndEntitlements.ts @@ -13,17 +13,20 @@ export type EnsurePricesAndEntitlementsInput = { feature_id: string; }; -/** Deterministic ID per (migration, product version, feature). Idempotent across runs. */ +/** + * Deterministic ID per (scope, product version, feature). Migration + * scopes pass `scopeId = mig_` to preserve the original + * `ent_mig__<...>` format; scripts pass their own prefix. + */ export const entitlementIdFor = ({ - migrationInternalId, + scopeId, productInternalId, internalFeatureId, }: { - migrationInternalId: string; + scopeId: string; productInternalId: string; internalFeatureId: string; -}): string => - `ent_mig_${migrationInternalId}_${productInternalId}_${internalFeatureId}`; +}): string => `ent_${scopeId}_${productInternalId}_${internalFeatureId}`; export const ensurePricesAndEntitlements: PrepareModule< EnsurePricesAndEntitlementsInput, @@ -31,7 +34,7 @@ export const ensurePricesAndEntitlements: PrepareModule< > = { kind: "ensure_prices_and_entitlements", - async plan({ ctx, migration, input }) { + async plan({ ctx, scope_id, input }) { const feature = ctx.features.find((f) => f.id === input.feature_id); if (!feature) throw new Error( @@ -50,7 +53,7 @@ export const ensurePricesAndEntitlements: PrepareModule< const desired: EntitlementItemRef[] = matchingProducts.map((product) => ({ entitlement_id: entitlementIdFor({ - migrationInternalId: migration.internal_id, + scopeId: scope_id, productInternalId: product.internal_id, internalFeatureId: feature.internal_id, }), @@ -67,7 +70,7 @@ export const ensurePricesAndEntitlements: PrepareModule< const desired = planned.entitlements; const ids = desired.map((d) => d.entitlement_id); - // Skip rows that already exist in DB. Deterministic IDs make this safe. + // Deterministic IDs let us skip rows already present in DB. const existing = ids.length ? await ctx.db .select({ id: entitlements.id }) diff --git a/server/src/internal/migrations/v2/prepare/runPrepare.ts b/server/src/internal/migrations/v2/prepare/runPrepare.ts index f9664f2fe..4157104d0 100644 --- a/server/src/internal/migrations/v2/prepare/runPrepare.ts +++ b/server/src/internal/migrations/v2/prepare/runPrepare.ts @@ -2,18 +2,18 @@ import type { Migration } from "@autumn/shared"; import type { AutumnContext } from "../../../../honoUtils/HonoEnv.js"; import { migrationRepo } from "../repos/index.js"; import { inferImplicitPrep } from "./inferImplicitPrep.js"; -import type { - PrepareModuleResult, - PrepareResponse, - PreparedState, -} from "./types/index.js"; +import { runPrepareModules } from "./runPrepareModules.js"; +import type { PreparedState, PrepareResponse } from "./types/index.js"; + +/** Stable scope_id for a Migration. Preserves the historical entitlement ID format. */ +const scopeIdFor = (migration: Migration): string => + `mig_${migration.internal_id}`; /** - * Orchestrate a migration's prepare phase. Walks implicit prep modules, - * runs plan → apply per module, persists prepared_state. - * - * On `dry_run: true`, plan runs but apply is skipped and prepared_state - * is not written. + * Migration-fed shim around `runPrepareModules`. Walks implicit prep + * modules from `migration.operations`, runs the pure orchestrator, then + * persists the new `prepared_state` back to the migrations row (skipped + * on dry-run). */ export const runPrepare = async ({ ctx, @@ -23,40 +23,32 @@ export const runPrepare = async ({ ctx: AutumnContext; migration: Migration; dry_run: boolean; -}): Promise => { - const instances = inferImplicitPrep(migration); - const warnings: string[] = []; - const modules: PrepareModuleResult[] = []; - const nextState: PreparedState = { ...(migration.prepared_state ?? {}) }; +}): Promise<{ response: PrepareResponse; prepared_state: PreparedState }> => { + const modules = inferImplicitPrep(migration); - for (const { key, module, input } of instances) { - const planned = await module.plan({ ctx, migration, input }); - - if (planned.entitlements.length === 0) { - warnings.push(`No products matched target for ${key}`); - } - - const result = dry_run - ? planned - : await module.apply({ ctx, migration, input, planned }); - - if (!dry_run) nextState[key] = result; - - modules.push({ key, kind: module.kind, result }); - } + const { results, prepared_state } = await runPrepareModules({ + ctx, + scope_id: scopeIdFor(migration), + modules, + dry_run, + prior_state: migration.prepared_state ?? {}, + }); if (!dry_run) { await migrationRepo.update({ ctx, id: migration.id, - updates: { prepared_state: nextState }, + updates: { prepared_state }, }); } return { - migration_id: migration.id, - dry_run, - modules, - warnings, + response: { + migration_id: migration.id, + dry_run, + modules: results, + warnings: [], + }, + prepared_state, }; }; diff --git a/server/src/internal/migrations/v2/prepare/runPrepareModules.ts b/server/src/internal/migrations/v2/prepare/runPrepareModules.ts new file mode 100644 index 000000000..c081bc20b --- /dev/null +++ b/server/src/internal/migrations/v2/prepare/runPrepareModules.ts @@ -0,0 +1,40 @@ +import type { AutumnContext } from "../../../../honoUtils/HonoEnv.js"; +import type { ImplicitPrepInstance } from "./inferImplicitPrep.js"; +import type { PreparedState, PrepareModuleResult } from "./types/index.js"; + +/** + * Pure orchestrator. Walks a list of prep module instances under a + * given `scope_id`, runs plan → apply per module (apply skipped on + * dry-run), threads `prepared_state` through. No DB reads/writes + * outside what the modules themselves do — script-callable. + */ +export const runPrepareModules = async ({ + ctx, + scope_id, + modules, + dry_run, + prior_state = {}, +}: { + ctx: AutumnContext; + scope_id: string; + modules: ImplicitPrepInstance[]; + dry_run: boolean; + prior_state?: PreparedState; +}): Promise<{ + results: PrepareModuleResult[]; + prepared_state: PreparedState; +}> => { + const results: PrepareModuleResult[] = []; + const next_state: PreparedState = { ...prior_state }; + + for (const { key, module, input } of modules) { + const planned = await module.plan({ ctx, scope_id, input }); + const result = dry_run + ? planned + : await module.apply({ ctx, scope_id, input, planned }); + if (!dry_run) next_state[key] = result; + results.push({ key, kind: module.kind, result }); + } + + return { results, prepared_state: next_state }; +}; diff --git a/server/src/internal/migrations/v2/prepare/types/index.ts b/server/src/internal/migrations/v2/prepare/types/index.ts index 92bb93379..4cfe0015b 100644 --- a/server/src/internal/migrations/v2/prepare/types/index.ts +++ b/server/src/internal/migrations/v2/prepare/types/index.ts @@ -1,4 +1,4 @@ +export * from "./preparedState.js"; export * from "./prepareModule.js"; export * from "./prepareModuleResult.js"; export * from "./prepareResponse.js"; -export * from "./preparedState.js"; diff --git a/server/src/internal/migrations/v2/prepare/types/prepareModule.ts b/server/src/internal/migrations/v2/prepare/types/prepareModule.ts index 5f49a946f..c88cc80b9 100644 --- a/server/src/internal/migrations/v2/prepare/types/prepareModule.ts +++ b/server/src/internal/migrations/v2/prepare/types/prepareModule.ts @@ -1,11 +1,13 @@ -import type { Migration } from "@autumn/shared"; import type { AutumnContext } from "../../../../../honoUtils/HonoEnv"; /** * Common shape every prepare module implements. `Input` and `Result` - * are module-specific (see `modules//types.ts`). The orchestrator - * wraps the returned `Result` in the loose `{ key, kind, result }` - * envelope before persistence / response. + * are module-specific. The orchestrator wraps the returned `Result` in + * the loose `{ key, kind, result }` envelope. + * + * `scope_id` is the namespace under which deterministic catalog rows + * are created — `mig_` for migrations, or any other prefix + * for ad-hoc script invocations. */ export type PrepareModule = { kind: string; @@ -13,14 +15,14 @@ export type PrepareModule = { /** Pure planning. No writes. */ plan: (args: { ctx: AutumnContext; - migration: Migration; + scope_id: string; input: Input; }) => Promise; /** Persist the desired set. Idempotent (deterministic IDs). */ apply: (args: { ctx: AutumnContext; - migration: Migration; + scope_id: string; input: Input; planned: Result; }) => Promise; diff --git a/server/src/internal/migrations/v2/repos/deleteMigration.ts b/server/src/internal/migrations/v2/repos/deleteMigration.ts new file mode 100644 index 000000000..2baf8e4be --- /dev/null +++ b/server/src/internal/migrations/v2/repos/deleteMigration.ts @@ -0,0 +1,24 @@ +import { type Migration, migrations } from "@autumn/shared"; +import { and, eq } from "drizzle-orm"; +import type { RepoContext } from "@/db/repoContext.js"; + +/** Delete by user `id`, scoped to the current org + env. */ +export const deleteMigration = async ({ + ctx, + id, +}: { + ctx: RepoContext; + id: string; +}): Promise => { + const [row] = await ctx.db + .delete(migrations) + .where( + and( + eq(migrations.id, id), + eq(migrations.org_id, ctx.org.id), + eq(migrations.env, ctx.env), + ), + ) + .returning(); + return row ?? null; +}; diff --git a/server/src/internal/migrations/v2/repos/findMigration.ts b/server/src/internal/migrations/v2/repos/findMigration.ts new file mode 100644 index 000000000..96e02d1d5 --- /dev/null +++ b/server/src/internal/migrations/v2/repos/findMigration.ts @@ -0,0 +1,39 @@ +import { ErrCode, type Migration, RecaseError } from "@autumn/shared"; +import { and, eq } from "drizzle-orm"; +import type { RepoContext } from "@/db/repoContext.js"; + +/** + * Single-row lookup by user `id` (or `internal_id`), scoped to the + * current org + env. Throws `MigrationNotFound` if missing — callers + * that need a nullable result use `migrationRepo.get(...)` instead. + */ +export const findMigration = async ({ + ctx, + id, + internalId, +}: { + ctx: RepoContext; + id?: string; + internalId?: string; +}): Promise => { + if (!id && !internalId) + throw new Error("findMigration: pass either `id` or `internalId`"); + + const row = await ctx.db.query.migrations.findFirst({ + where: (m) => + and( + eq(m.org_id, ctx.org.id), + eq(m.env, ctx.env), + id !== undefined ? eq(m.id, id) : eq(m.internal_id, internalId!), + ), + }); + + if (!row) + throw new RecaseError({ + message: `Migration ${id ?? internalId} not found`, + code: ErrCode.MigrationNotFound, + statusCode: 404, + }); + + return row; +}; diff --git a/server/src/internal/migrations/v2/repos/index.ts b/server/src/internal/migrations/v2/repos/index.ts index d26691e6a..b93bf5a95 100644 --- a/server/src/internal/migrations/v2/repos/index.ts +++ b/server/src/internal/migrations/v2/repos/index.ts @@ -1,3 +1,5 @@ +import { deleteMigration } from "./deleteMigration.js"; +import { findMigration } from "./findMigration.js"; import { getMigration } from "./getMigration.js"; import { insertMigration } from "./insertMigration.js"; import { updateMigration } from "./updateMigration.js"; @@ -5,5 +7,7 @@ import { updateMigration } from "./updateMigration.js"; export const migrationRepo = { insert: insertMigration, get: getMigration, + find: findMigration, update: updateMigration, + delete: deleteMigration, }; diff --git a/server/src/internal/migrations/v2/run/index.ts b/server/src/internal/migrations/v2/run/index.ts new file mode 100644 index 000000000..469017554 --- /dev/null +++ b/server/src/internal/migrations/v2/run/index.ts @@ -0,0 +1,4 @@ +export * from "./orchestrators/index.js"; +export * from "./perItem/index.js"; +export * from "./runMigration.js"; +export * from "./types/index.js"; diff --git a/server/src/internal/migrations/v2/run/orchestrators/index.ts b/server/src/internal/migrations/v2/run/orchestrators/index.ts new file mode 100644 index 000000000..d521b6219 --- /dev/null +++ b/server/src/internal/migrations/v2/run/orchestrators/index.ts @@ -0,0 +1,2 @@ +export * from "./iterateScope.js"; +export * from "./runPreparation.js"; diff --git a/server/src/internal/migrations/v2/run/orchestrators/iterateScope.ts b/server/src/internal/migrations/v2/run/orchestrators/iterateScope.ts new file mode 100644 index 000000000..cd8a9e09d --- /dev/null +++ b/server/src/internal/migrations/v2/run/orchestrators/iterateScope.ts @@ -0,0 +1,52 @@ +import type { RunScopeItem } from "../types/runScope.js"; + +export type IterateScopeItemResult = + | { status: "ok"; item: RunScopeItem; value: T } + | { status: "failed"; item: RunScopeItem; error: Error }; + +export type IterateScopeSummary = { + processed: number; + succeeded: number; + failed: number; + results: IterateScopeItemResult[]; +}; + +/** + * Generic iteration over any kind-tagged scope iterator. Calls `perItem` + * for every item, collecting per-item results into a summary. Sequential + * by default; concurrency layer is intentionally out of scope here. + * + * On error: keeps going with `onError: "continue"` (default), or rethrows + * the first error with `onError: "throw"`. Either way every visited + * item shows up in `results` so callers see the full audit trail. + */ +export const iterateScope = async ({ + iterate, + perItem, + onError = "continue", +}: { + iterate: () => AsyncGenerator; + perItem: (item: RunScopeItem) => Promise; + onError?: "throw" | "continue"; +}): Promise> => { + const results: IterateScopeItemResult[] = []; + let succeeded = 0; + let failed = 0; + + for await (const batch of iterate()) { + for (const item of batch) { + try { + const value = await perItem(item); + results.push({ status: "ok", item, value }); + succeeded++; + } catch (raw) { + const error = raw instanceof Error ? raw : new Error(String(raw)); + results.push({ status: "failed", item, error }); + failed++; + if (onError === "throw") throw error; + } + } + } + + return { processed: succeeded + failed, succeeded, failed, results }; +}; diff --git a/server/src/internal/migrations/v2/run/orchestrators/runPreparation.ts b/server/src/internal/migrations/v2/run/orchestrators/runPreparation.ts new file mode 100644 index 000000000..c3380e453 --- /dev/null +++ b/server/src/internal/migrations/v2/run/orchestrators/runPreparation.ts @@ -0,0 +1,27 @@ +import type { Migration } from "@autumn/shared"; +import type { AutumnContext } from "../../../../../honoUtils/HonoEnv.js"; +import { runPrepare } from "../../prepare/runPrepare.js"; +import type { + PreparedState, + PrepareResponse, +} from "../../prepare/types/index.js"; + +/** + * Run-phase wrapper around the prepare orchestrator. Returns the + * freshly written `prepared_state` so per-item handlers can read it + * without re-fetching the migration row. + * + * On `dry_run: true` this still computes and returns the planned + * `prepared_state` shape (so dry-run end-to-end can show what each + * customer would see) — but the migrations row is not updated. + */ +export const runPreparation = async ({ + ctx, + migration, + dry_run, +}: { + ctx: AutumnContext; + migration: Migration; + dry_run: boolean; +}): Promise<{ response: PrepareResponse; prepared_state: PreparedState }> => + runPrepare({ ctx, migration, dry_run }); diff --git a/server/src/internal/migrations/v2/run/perItem/applyAddItem.ts b/server/src/internal/migrations/v2/run/perItem/applyAddItem.ts new file mode 100644 index 000000000..9188ac5ac --- /dev/null +++ b/server/src/internal/migrations/v2/run/perItem/applyAddItem.ts @@ -0,0 +1,105 @@ +import type { CreatePlanItemParamsV1, FullCusProduct } from "@autumn/shared"; +import { customerEntitlements } from "@autumn/shared"; +import { inArray } from "drizzle-orm"; +import type { AutumnContext } from "@/honoUtils/HonoEnv.js"; +import { CusEntService } from "@/internal/customers/cusProducts/cusEnts/CusEntitlementService.js"; +import type { EnsurePricesAndEntitlementsResult } from "@/internal/migrations/v2/prepare/modules/ensurePricesAndEntitlements/types.js"; +import type { PreparedState } from "@/internal/migrations/v2/prepare/types/index.js"; + +export type ApplyAddItemResult = + | { status: "added"; customer_entitlement_id: string } + | { status: "already_present"; customer_entitlement_id: string } + | { + status: "skipped"; + reason: "no_prepared_entitlement" | "priced_item_unsupported"; + }; + +/** Customer-entitlement deterministic ID per (scope, cusproduct, entitlement). */ +const customerEntitlementIdFor = ({ + scopeId, + cusProductInternalId, + entitlementId, +}: { + scopeId: string; + cusProductInternalId: string; + entitlementId: string; +}): string => `cusent_${scopeId}_${cusProductInternalId}_${entitlementId}`; + +/** + * Apply ONE add_item to ONE matched cusproduct. Phase 1 handles + * entitlement-only items; priced items will route through the same + * flow once `ensurePricesAndEntitlements` provisions Stripe prices. + */ +export const applyAddItem = async ({ + ctx, + scope_id, + cusProduct, + addItem, + prepared_state, + dry_run, +}: { + ctx: AutumnContext; + scope_id: string; + cusProduct: FullCusProduct; + addItem: CreatePlanItemParamsV1; + prepared_state: PreparedState; + dry_run: boolean; +}): Promise => { + if (addItem.price) + return { status: "skipped", reason: "priced_item_unsupported" }; + if (!addItem.feature_id) + return { status: "skipped", reason: "no_prepared_entitlement" }; + + // Look up the prepared entitlement for (feature, target plan) keyed + // `ensure_prices_and_entitlements::`. The + // matching `entitlement_id` is whichever ref points at this + // cusproduct's product version. + const stateKey = `ensure_prices_and_entitlements:${addItem.feature_id}:${cusProduct.product_id}`; + const slot = prepared_state[stateKey] as + | EnsurePricesAndEntitlementsResult + | undefined; + const ref = slot?.entitlements.find( + (e) => e.product_internal_id === cusProduct.internal_product_id, + ); + if (!ref) return { status: "skipped", reason: "no_prepared_entitlement" }; + + const customerEntitlementId = customerEntitlementIdFor({ + scopeId: scope_id, + cusProductInternalId: cusProduct.id, + entitlementId: ref.entitlement_id, + }); + + const existing = await ctx.db + .select({ id: customerEntitlements.id }) + .from(customerEntitlements) + .where(inArray(customerEntitlements.id, [customerEntitlementId])); + if (existing.length > 0) + return { + status: "already_present", + customer_entitlement_id: customerEntitlementId, + }; + + if (dry_run) + return { status: "added", customer_entitlement_id: customerEntitlementId }; + + await CusEntService.insert({ + ctx, + data: [ + { + id: customerEntitlementId, + customer_product_id: cusProduct.id, + entitlement_id: ref.entitlement_id, + internal_customer_id: cusProduct.internal_customer_id, + internal_feature_id: ref.internal_feature_id, + feature_id: ref.feature_id, + customer_id: cusProduct.customer_id ?? null, + balance: 0, + created_at: Date.now(), + usage_allowed: false, + unlimited: false, + }, + ], + }); + + return { status: "added", customer_entitlement_id: customerEntitlementId }; +}; diff --git a/server/src/internal/migrations/v2/run/perItem/index.ts b/server/src/internal/migrations/v2/run/perItem/index.ts new file mode 100644 index 000000000..d6d8a4d5c --- /dev/null +++ b/server/src/internal/migrations/v2/run/perItem/index.ts @@ -0,0 +1,4 @@ +export * from "./applyAddItem.js"; +export * from "./matchPlanFilter.js"; +export * from "./matchStringMatcher.js"; +export * from "./runOpsForCustomer.js"; diff --git a/server/src/internal/migrations/v2/run/perItem/matchPlanFilter.ts b/server/src/internal/migrations/v2/run/perItem/matchPlanFilter.ts new file mode 100644 index 000000000..2fba963a6 --- /dev/null +++ b/server/src/internal/migrations/v2/run/perItem/matchPlanFilter.ts @@ -0,0 +1,35 @@ +import type { FullCusProduct, PlanFilter } from "@autumn/shared"; +import { matchStringMatcher } from "./matchStringMatcher.js"; + +/** + * JS-side PlanFilter matcher against a single FullCusProduct. Phase 1 + * supports `plan_id` only — `price`, `paid`, `recurring`, `item`, `$or` + * throw `not_supported_in_matcher` so callers see the gap explicitly. + */ +export const matchPlanFilter = ( + filter: PlanFilter, + cusProduct: FullCusProduct, +): boolean => { + if (filter.plan_id !== undefined) { + if (!matchStringMatcher(filter.plan_id, cusProduct.product_id)) + return false; + } + + const unsupported = ["price", "paid", "recurring", "item", "$or"] as const; + for (const key of unsupported) { + if ((filter as Record)[key] !== undefined) + throw new Error( + `matchPlanFilter: target.${key} not supported in JS matcher yet`, + ); + } + return true; +}; + +/** Returns the cusproducts on a customer that match `target`. */ +export const matchCustomerProductsByTarget = ({ + cusProducts, + target, +}: { + cusProducts: FullCusProduct[]; + target: PlanFilter; +}): FullCusProduct[] => cusProducts.filter((cp) => matchPlanFilter(target, cp)); diff --git a/server/src/internal/migrations/v2/run/perItem/matchStringMatcher.ts b/server/src/internal/migrations/v2/run/perItem/matchStringMatcher.ts new file mode 100644 index 000000000..c18057b9b --- /dev/null +++ b/server/src/internal/migrations/v2/run/perItem/matchStringMatcher.ts @@ -0,0 +1,32 @@ +import type { StringMatcher } from "@autumn/shared"; + +/** Phase 1 ops only: bare value, $eq, $ne, $in, $nin. */ +export const matchStringMatcher = ( + matcher: StringMatcher | undefined, + value: string | null | undefined, +): boolean => { + if (matcher === undefined) return true; + if (matcher === null) return value === null || value === undefined; + if (typeof matcher === "string") return value === matcher; + + if ("$eq" in matcher && matcher.$eq !== undefined) { + if (matcher.$eq === null) return value === null || value === undefined; + if (value !== matcher.$eq) return false; + } + if ("$ne" in matcher && matcher.$ne !== undefined) { + if (matcher.$ne === null) { + if (value === null || value === undefined) return false; + } else if (value === matcher.$ne) return false; + } + if (matcher.$in && !matcher.$in.includes(value ?? "")) return false; + if (matcher.$nin && matcher.$nin.includes(value ?? "")) return false; + + const unsupported = ["$regex", "$startsWith"] as const; + for (const op of unsupported) { + if (op in matcher && (matcher as Record)[op] !== undefined) + throw new Error( + `matchStringMatcher: operator ${op} not supported in JS matcher yet`, + ); + } + return true; +}; diff --git a/server/src/internal/migrations/v2/run/perItem/runOpsForCustomer.ts b/server/src/internal/migrations/v2/run/perItem/runOpsForCustomer.ts new file mode 100644 index 000000000..113ecf4eb --- /dev/null +++ b/server/src/internal/migrations/v2/run/perItem/runOpsForCustomer.ts @@ -0,0 +1,70 @@ +import type { Operations } from "@autumn/shared"; +import type { AutumnContext } from "@/honoUtils/HonoEnv.js"; +import { CusService } from "@/internal/customers/CusService.js"; +import type { PreparedState } from "@/internal/migrations/v2/prepare/types/index.js"; +import { type ApplyAddItemResult, applyAddItem } from "./applyAddItem.js"; +import { matchCustomerProductsByTarget } from "./matchPlanFilter.js"; + +export type RunOpsForCustomerResult = { + internal_customer_id: string; + matched_cusproducts: number; + add_items: ApplyAddItemResult[]; +}; + +/** + * Walk a customer's matching cusproducts × the migration's update_plans + * × add_items. Phase 1 only — delete_items + priced add_items will be + * routed in here as their handlers ship. + */ +export const runOpsForCustomer = async ({ + ctx, + scope_id, + internal_customer_id, + operations, + prepared_state, + dry_run, +}: { + ctx: AutumnContext; + scope_id: string; + internal_customer_id: string; + operations: Operations; + prepared_state: PreparedState; + dry_run: boolean; +}): Promise => { + const fullCustomer = await CusService.getFull({ + ctx, + idOrInternalId: internal_customer_id, + }); + + const updatePlans = operations.customer?.update_plans ?? []; + let matched = 0; + const addItemResults: ApplyAddItemResult[] = []; + + for (const op of updatePlans) { + const cps = matchCustomerProductsByTarget({ + cusProducts: fullCustomer.customer_products, + target: op.target, + }); + matched += cps.length; + + for (const cusProduct of cps) { + for (const addItem of op.add_items ?? []) { + const result = await applyAddItem({ + ctx, + scope_id, + cusProduct, + addItem, + prepared_state, + dry_run, + }); + addItemResults.push(result); + } + } + } + + return { + internal_customer_id, + matched_cusproducts: matched, + add_items: addItemResults, + }; +}; diff --git a/server/src/internal/migrations/v2/run/runMigration.ts b/server/src/internal/migrations/v2/run/runMigration.ts new file mode 100644 index 000000000..5d90923c5 --- /dev/null +++ b/server/src/internal/migrations/v2/run/runMigration.ts @@ -0,0 +1,74 @@ +import type { Migration, Operations } from "@autumn/shared"; +import type { AutumnContext } from "../../../../honoUtils/HonoEnv.js"; +import { runFilter } from "../filters/runFilter.js"; +import { iterateScope, runPreparation } from "./orchestrators/index.js"; +import { runOpsForCustomer } from "./perItem/runOpsForCustomer.js"; +import type { + RunMigrationResponse, + RunMigrationScopeResult, +} from "./types/runMigrationResponse.js"; +import type { RunScopeKind } from "./types/runScope.js"; + +/** Top-level migration run: prepare → per-scope filter+iterate → per-item ops. */ +export const runMigration = async ({ + ctx, + migration, + dry_run, +}: { + ctx: AutumnContext; + migration: Migration; + dry_run: boolean; +}): Promise => { + const { response: prepareResponse, prepared_state } = await runPreparation({ + ctx, + migration, + dry_run, + }); + + const scope_id = `mig_${migration.internal_id}`; + const operations: Operations = migration.operations ?? {}; + + const scopeResults: RunMigrationScopeResult[] = []; + + for (const kind of scopesForRun(migration)) { + const { count, iterate } = await runFilter({ ctx, migration, kind }); + ctx.logger.info(`run-migration: iterating scope`, { + data: { kind, count, dry_run }, + }); + + const summary = await iterateScope({ + iterate, + perItem: async (item) => { + if (item.kind !== "customer") + throw new Error( + `runMigration: per-item handler missing for kind "${item.kind}"`, + ); + return runOpsForCustomer({ + ctx, + scope_id, + internal_customer_id: item.internal_id, + operations, + prepared_state, + dry_run, + }); + }, + }); + + scopeResults.push({ kind, count, summary }); + } + + return { + migration_id: migration.id, + dry_run, + prepare_warnings: prepareResponse.warnings, + scopes: scopeResults, + }; +}; + +/** Active scopes = top-level keys present in `migration.operations`. */ +const scopesForRun = (migration: Migration): RunScopeKind[] => { + const scopes: RunScopeKind[] = []; + if (migration.operations?.customer) scopes.push("customer"); + // future: if (migration.operations?.plan) scopes.push("plan"); + return scopes; +}; diff --git a/server/src/internal/migrations/v2/run/types/index.ts b/server/src/internal/migrations/v2/run/types/index.ts new file mode 100644 index 000000000..3dd586aac --- /dev/null +++ b/server/src/internal/migrations/v2/run/types/index.ts @@ -0,0 +1,2 @@ +export * from "./runMigrationResponse.js"; +export * from "./runScope.js"; diff --git a/server/src/internal/migrations/v2/run/types/runMigrationResponse.ts b/server/src/internal/migrations/v2/run/types/runMigrationResponse.ts new file mode 100644 index 000000000..a44a13be5 --- /dev/null +++ b/server/src/internal/migrations/v2/run/types/runMigrationResponse.ts @@ -0,0 +1,17 @@ +import type { IterateScopeSummary } from "../orchestrators/iterateScope.js"; +import type { RunOpsForCustomerResult } from "../perItem/runOpsForCustomer.js"; +import type { RunScopeKind } from "./runScope.js"; + +/** One scope's portion of a run — kind-tagged for phase-2 multi-scope. */ +export type RunMigrationScopeResult = { + kind: RunScopeKind; + count: number; + summary: IterateScopeSummary; +}; + +export type RunMigrationResponse = { + migration_id: string; + dry_run: boolean; + prepare_warnings: string[]; + scopes: RunMigrationScopeResult[]; +}; diff --git a/server/src/internal/migrations/v2/run/types/runScope.ts b/server/src/internal/migrations/v2/run/types/runScope.ts new file mode 100644 index 000000000..0a7cb90fb --- /dev/null +++ b/server/src/internal/migrations/v2/run/types/runScope.ts @@ -0,0 +1,9 @@ +/** Resource kind being iterated. Phase 2+ adds catalog-rooted kinds. */ +export type RunScopeKind = "customer" | "plan"; + +/** One iterated item, kind-tagged for generic dispatch. */ +export type RunScopeItem = { + kind: RunScopeKind; + internal_id: string; + id: string | null; +}; diff --git a/server/src/trigger/configureTrigger.ts b/server/src/trigger/configureTrigger.ts new file mode 100644 index 000000000..0462edc79 --- /dev/null +++ b/server/src/trigger/configureTrigger.ts @@ -0,0 +1,18 @@ +import { configure } from "@trigger.dev/sdk/v3"; + +/** + * Point the trigger.dev SDK at autumn's project key. + * + * autumn and autumn-cloud each have their own trigger.dev project. We + * keep autumn's secret under `TRIGGER_SERVER_SECRET_KEY` so the two + * never collide in a shared shell — the SDK's default `TRIGGER_SECRET_KEY` + * is left to autumn-cloud. + * + * Module side-effect: this `configure` call runs once on first import. + * Anything that triggers tasks server-side imports from + * `@/trigger/migrations/...`, which re-exports from this file's siblings, + * so the configure happens before any `.trigger()` call. + */ +if (process.env.TRIGGER_SERVER_SECRET_KEY) { + configure({ secretKey: process.env.TRIGGER_SERVER_SECRET_KEY }); +} diff --git a/server/src/trigger/migrations/runMigrationTask.ts b/server/src/trigger/migrations/runMigrationTask.ts new file mode 100644 index 000000000..de08481a1 --- /dev/null +++ b/server/src/trigger/migrations/runMigrationTask.ts @@ -0,0 +1,52 @@ +import { AppEnv } from "@autumn/shared"; +import { task } from "@trigger.dev/sdk/v3"; +import { z } from "zod/v4"; +import { migrationRepo } from "@/internal/migrations/v2/repos/index.js"; +import { runMigration } from "@/internal/migrations/v2/run/runMigration.js"; +import { createTriggerContext } from "@/trigger/utils/createTriggerContext.js"; + +const PayloadSchema = z.object({ + orgId: z.string(), + env: z.enum(AppEnv), + migrationId: z.string(), + dryRun: z.boolean().default(false), +}); + +export type RunMigrationPayload = z.infer; + +/** trigger.dev task for long-running migrations (deploys kill api workers). */ +export const runMigrationTask = task({ + id: "run-migration", + maxDuration: 3600, + run: async (rawPayload: unknown, { ctx: triggerCtx }) => { + const { orgId, env, migrationId, dryRun } = PayloadSchema.parse(rawPayload); + + const { ctx, logger } = await createTriggerContext({ + orgId, + env, + triggerCtx, + }); + + logger.info("run-migration: starting", { + data: { migrationId, dryRun }, + }); + + const migration = await migrationRepo.find({ ctx, id: migrationId }); + + const result = await runMigration({ ctx, migration, dry_run: dryRun }); + + logger.info("run-migration: done", { + data: { + migration_id: result.migration_id, + dry_run: result.dry_run, + scopes: result.scopes.map((s) => ({ + kind: s.kind, + count: s.count, + succeeded: s.summary.succeeded, + failed: s.summary.failed, + })), + }, + }); + return result; + }, +}); diff --git a/server/src/trigger/utils/createTriggerContext.ts b/server/src/trigger/utils/createTriggerContext.ts new file mode 100644 index 000000000..a8394bb13 --- /dev/null +++ b/server/src/trigger/utils/createTriggerContext.ts @@ -0,0 +1,46 @@ +import type { AppEnv } from "@autumn/shared"; +import type { Context as TriggerRunContext } from "@trigger.dev/sdk/v3"; +import { db } from "@/db/initDrizzle.js"; +import { + createDualLogger, + type Logger, +} from "@/external/logtail/logtailUtils.js"; +import type { AutumnContext } from "@/honoUtils/HonoEnv.js"; +import { createWorkerContext } from "@/queue/createWorkerContext.js"; +import { addTriggerToLogs } from "@/utils/logging/addContextToLogs.js"; + +/** + * Build an `AutumnContext` for a trigger.dev task run. Uses the dual + * logger (stdout + axiom) and tags every line with run/task/attempt ids. + */ +export const createTriggerContext = async ({ + orgId, + env, + triggerCtx, +}: { + orgId: string; + env: AppEnv; + triggerCtx: TriggerRunContext; +}): Promise<{ ctx: AutumnContext; logger: Logger }> => { + const logger = addTriggerToLogs({ + logger: createDualLogger(), + triggerContext: { + run_id: triggerCtx.run.id, + task_id: triggerCtx.task.id, + attempt_number: triggerCtx.attempt.number, + }, + }); + + const ctx = await createWorkerContext({ + db, + payload: { orgId, env, requestId: triggerCtx.run.id }, + logger, + }); + + if (!ctx) + throw new Error( + `createTriggerContext: failed to build context for org=${orgId} env=${env}`, + ); + + return { ctx, logger }; +}; diff --git a/server/src/trigger/utils/index.ts b/server/src/trigger/utils/index.ts new file mode 100644 index 000000000..181a14ef9 --- /dev/null +++ b/server/src/trigger/utils/index.ts @@ -0,0 +1 @@ +export * from "./createTriggerContext.js"; diff --git a/server/src/utils/envUtils.ts b/server/src/utils/envUtils.ts index d4a31cd09..251c913e7 100644 --- a/server/src/utils/envUtils.ts +++ b/server/src/utils/envUtils.ts @@ -1,17 +1,36 @@ +import { existsSync } from "node:fs"; import { join } from "node:path"; import { config } from "dotenv"; let hasLoadedLocalEnv = false; const shouldLogLocalEnvLoading = false; +/** + * Resolve the directory holding `.env`, robust to cwd: + * - cwd already a `server/` dir + * - cwd is `autumn/` (typical workspace root) + * - cwd is the monorepo root (one level above `autumn/`) — happens with + * `bun test autumn/...` invocations from VSCode tasks + */ +const resolveServerDir = (): string => { + const cwd = process.cwd(); + const candidates = [ + cwd, + join(cwd, "server"), + join(cwd, "autumn", "server"), + ]; + for (const dir of candidates) { + if (existsSync(join(dir, "package.json"))) return dir; + } + // Fall back to first guess so dotenv silently no-ops if missing. + return cwd.includes("server") ? cwd : join(cwd, "server"); +}; + export const loadLocalEnv = ({ force = false }: { force?: boolean } = {}) => { if (hasLoadedLocalEnv && !force) return; hasLoadedLocalEnv = true; - const processDir = process.cwd(); - const serverDir = processDir.includes("server") - ? processDir - : join(processDir, "server"); + const serverDir = resolveServerDir(); // Determine which env file to load based on ENV_FILE environment variable // Defaults to .env if not specified diff --git a/server/src/utils/logging/addContextToLogs.ts b/server/src/utils/logging/addContextToLogs.ts index adf1c255b..26a3e2d63 100644 --- a/server/src/utils/logging/addContextToLogs.ts +++ b/server/src/utils/logging/addContextToLogs.ts @@ -4,6 +4,7 @@ import type { LogRedisData, LogRequestContext, LogStripeEventContext, + LogTriggerContext, LogWorkflowContext, } from "./loggerTypes.js"; @@ -47,6 +48,16 @@ export const addWorkflowToLogs = ({ return logger.child({ context: { workflow: workflowContext } }); }; +export const addTriggerToLogs = ({ + logger, + triggerContext, +}: { + logger: Logger; + triggerContext: LogTriggerContext; +}): Logger => { + return logger.child({ context: { trigger: triggerContext } }); +}; + export const addRedisToLogs = ({ logger, redisData, diff --git a/server/src/utils/logging/initLogger.ts b/server/src/utils/logging/initLogger.ts index 49198e64d..e59a62c44 100644 --- a/server/src/utils/logging/initLogger.ts +++ b/server/src/utils/logging/initLogger.ts @@ -2,58 +2,84 @@ import { Writable } from "node:stream"; import pino from "pino"; import { getAwsTaskIdentity } from "@/external/aws/ecs/awsTaskIdentity.js"; -// Custom log formatter for Bun compatibility -const createDevLogStream = () => { - const colors = { - reset: "\x1b[0m", - bright: "\x1b[1m", - dim: "\x1b[2m", - red: "\x1b[31m", - green: "\x1b[32m", - yellow: "\x1b[33m", - blue: "\x1b[34m", - magenta: "\x1b[35m", - cyan: "\x1b[36m", - white: "\x1b[37m", - gray: "\x1b[90m", - bgRed: "\x1b[41m", - }; +/** + * Fields that don't render in the formatted dev/local console output + * (they still go to JSON sinks). Trim noise like trigger metadata, + * request envelopes, and high-cardinality structured data. + */ +const FORMATTED_LOG_EXCLUDE_FIELDS = new Set([ + // pino housekeeping + "time", + "level", + "msg", + "pid", + "hostname", + // request / response envelopes + "req", + "res", + "statusCode", + "body", + "query", + "durationMs", + // app-context blocks (added via `addContextToLogs`) + "context", + "workflow", + "trigger", + "stripe_event", + "worker", + "extras", + "type", + // structured payloads + "data", + // AWS task identity (mixin) + "aws", +]); - const levelColors: Record = { - // Numeric levels - 10: colors.gray, // trace - 20: colors.blue, // debug - 30: colors.green, // info - 40: colors.yellow, // warn - 50: colors.red, // error - 60: colors.bgRed, // fatal - // String levels - TRACE: colors.gray, - DEBUG: colors.blue, - INFO: colors.green, - WARN: colors.yellow, - ERROR: colors.red, - FATAL: colors.bgRed, - }; +const colors = { + reset: "\x1b[0m", + bright: "\x1b[1m", + red: "\x1b[31m", + green: "\x1b[32m", + yellow: "\x1b[33m", + blue: "\x1b[34m", + white: "\x1b[37m", + gray: "\x1b[90m", + bgRed: "\x1b[41m", +}; - const levelNames: Record = { - // Numeric levels - 10: "TRACE", - 20: "DEBUG", - 30: "INFO", - 40: "WARN", - 50: "ERROR", - 60: "FATAL", - // String levels (pass through) - TRACE: "TRACE", - DEBUG: "DEBUG", - INFO: "INFO", - WARN: "WARN", - ERROR: "ERROR", - FATAL: "FATAL", - }; +const levelColors: Record = { + 10: colors.gray, + 20: colors.blue, + 30: colors.green, + 40: colors.yellow, + 50: colors.red, + 60: colors.bgRed, + TRACE: colors.gray, + DEBUG: colors.blue, + INFO: colors.green, + WARN: colors.yellow, + ERROR: colors.red, + FATAL: colors.bgRed, +}; - return new Writable({ +const levelNames: Record = { + 10: "TRACE", + 20: "DEBUG", + 30: "INFO", + 40: "WARN", + 50: "ERROR", + 60: "FATAL", + TRACE: "TRACE", + DEBUG: "DEBUG", + INFO: "INFO", + WARN: "WARN", + ERROR: "ERROR", + FATAL: "FATAL", +}; + +/** Bun-friendly pino sink that prints ` `. */ +const createDevLogStream = () => + new Writable({ write(chunk, _encoding, callback) { try { const log = JSON.parse(chunk.toString()); @@ -66,44 +92,22 @@ const createDevLogStream = () => { const levelName = levelNames[level] || (typeof level === "string" ? level : "UNKNOWN"); - // Format the message let message = log.msg || ""; - // Add any additional fields (excluding standard pino fields) - const excludeFields = [ - "time", - "level", - "msg", - "pid", - "hostname", - "res", - "statusCode", - "worker", - "context", - "req", - "data", - "body", - "query", - - "workflow", - "stripe_event", - "extras", - "type", - - "durationMs", - ]; const additionalFields = Object.keys(log) - .filter((key) => !excludeFields.includes(key)) - .reduce((acc, key) => { - acc[key] = log[key]; - return acc; - }, {} as any); + .filter((key) => !FORMATTED_LOG_EXCLUDE_FIELDS.has(key)) + .reduce( + (acc, key) => { + acc[key] = log[key]; + return acc; + }, + {} as Record, + ); if (Object.keys(additionalFields).length > 0) { message += ` ${JSON.stringify(additionalFields, null, 2)}`; } - // Format the final log line const formattedLog = `${colors.gray}${timestamp}${colors.reset} ${levelColor}${colors.bright}${levelName}${colors.reset} ${message}\n`; process.stdout.write(formattedLog); @@ -115,47 +119,79 @@ const createDevLogStream = () => { } }, }); + +/** + * `mode: "dual"` is opt-in only — used by trigger.dev tasks so logs hit + * both stdout (trigger run UI) and axiom (long-term store). + * + * In dev/test the stdout stream uses the formatted dev sink so trigger + * pane lines look like server lines. In prod it stays raw JSON so + * trigger.dev's cloud UI gets structured logs. + * + * `mode: "default"` preserves existing prod behavior verbatim. + */ +export type InitLoggerOptions = { + mode?: "default" | "dual"; }; -export const initLogger = () => { - // Create separate streams for console and HyperDX - const streams: pino.StreamEntry[] = []; +export const initLogger = (options: InitLoggerOptions = {}) => { + const { mode = "default" } = options; + const streams: pino.StreamEntry[] = []; const isDev = process.env.NODE_ENV === "development"; const isTest = process.env.NODE_ENV === "test"; + const isDevOrTest = isDev || isTest; - // Enable dev logging for development OR test environments - if (isDev || isTest) { + if (mode === "dual") { streams.push({ - level: "debug", - stream: createDevLogStream(), + level: isDevOrTest ? "debug" : "info", + stream: isDevOrTest ? createDevLogStream() : process.stdout, }); - } + if (process.env.AXIOM_TOKEN) { + streams.push({ + level: "info", + stream: pino.transport({ + target: "@axiomhq/pino", + options: { + dataset: "express", + token: process.env.AXIOM_TOKEN, + }, + }), + }); + } + } else { + // DEFAULT FLOW — exact prior behavior. DO NOT MODIFY. + if (isDev || isTest) { + streams.push({ + level: "debug", + stream: createDevLogStream(), + }); + } - if (process.env.AXIOM_TOKEN) { - streams.push({ - level: "info", - stream: pino.transport({ - target: "@axiomhq/pino", - options: { - dataset: "express", - token: process.env.AXIOM_TOKEN, - }, - }), - }); - } + if (process.env.AXIOM_TOKEN) { + streams.push({ + level: "info", + stream: pino.transport({ + target: "@axiomhq/pino", + options: { + dataset: "express", + token: process.env.AXIOM_TOKEN, + }, + }), + }); + } - // Fallback: if no streams configured, add dev stream anyway - if (streams.length === 0) { - streams.push({ - level: "info", - stream: createDevLogStream(), - }); + if (streams.length === 0) { + streams.push({ + level: "info", + stream: createDevLogStream(), + }); + } } const logger = pino( { - level: isDev || isTest ? "debug" : "info", + level: isDev || isTest || mode === "dual" ? "debug" : "info", // Tag every log line with this process's AWS task identity so Axiom // can distinguish blue/green task sets. Returns {} until // `resolveAwsTaskIdentity` finishes (~100ms after boot) and on @@ -179,7 +215,6 @@ export const initLogger = () => { }, }, }, - // Use multistream to send logs to multiple destinations pino.multistream(streams), ); diff --git a/server/src/utils/logging/loggerTypes.ts b/server/src/utils/logging/loggerTypes.ts index c6f7e04a6..5f852785a 100644 --- a/server/src/utils/logging/loggerTypes.ts +++ b/server/src/utils/logging/loggerTypes.ts @@ -52,6 +52,13 @@ export type LogWorkflowContext = { name: string; // workflow / job name }; +/** trigger.dev run context — goes under context.trigger */ +export type LogTriggerContext = { + run_id: string; + task_id: string; + attempt_number?: number; +}; + /** Redis slow-command context - goes under context.redis.data (map field) */ export type LogRedisData = { operation: string; diff --git a/server/tests/integration/billing/migrations-v2/migrations-add-items.test.ts b/server/tests/integration/billing/migrations-v2/migrations-add-items.test.ts index ed8de9d31..3ef05d151 100644 --- a/server/tests/integration/billing/migrations-v2/migrations-add-items.test.ts +++ b/server/tests/integration/billing/migrations-v2/migrations-add-items.test.ts @@ -1,12 +1,10 @@ /** - * Migrations V2 — add_items + * Migrations V2 — add_items end-to-end (integration). * - * Phase 1 acceptance: a user can create a migration definition whose - * filter selects customers on a given plan and whose operation adds a - * plan item to their matching cusproducts. - * - * This test only verifies the CREATE path (storing the migration - * definition end-to-end). Execution / preview is phase 2+. + * Seeds 5 customers, parallel-attaches them all to a `free` product, + * creates a migration adding the Dashboard feature, and triggers + * `migrations.run`. Verifies the API call dispatches; downstream + * trigger.dev side effects are out of scope here. */ import { expect, test } from "bun:test"; @@ -15,57 +13,56 @@ import { products } from "@tests/utils/fixtures/products"; import { initScenario, s } from "@tests/utils/testInitUtils/initScenario"; import chalk from "chalk"; -test.concurrent( - `${chalk.yellowBright("migrations-v2 add-items: create migration on free customers adding a feature item")}`, - async () => { - const customerId = "migrations-v2-add-items"; +test.concurrent(`${chalk.yellowBright("migrations-v2 add-items: triggers run for 5 customers on free")}`, async () => { + const customerId = "migration-run-add-items"; + const otherIds = [1, 2, 3, 4].map((i) => `${customerId}-c${i}`); + const free = products.base({ id: "free", items: [], isDefault: true }); - // Default free product so the seeded customer auto-attaches to it. - const free = products.base({ id: "free", items: [], isDefault: true }); - - const { autumnV2_2 } = await initScenario({ + const { autumnV1, autumnV2_2, customer, otherCustomers } = await initScenario( + { customerId, setup: [ s.customer({ paymentMethod: "success" }), s.products({ list: [free] }), + s.otherCustomers( + otherIds.map((id) => ({ id, paymentMethod: "success" as const })), + ), ], - }); + actions: [], + }, + ); - // migrationId reuses the suffixed customerId so it's unique per concurrent - // test run without needing a Date.now() hack. - const created = await autumnV2_2.migrationsV2.create({ - id: customerId, - filter: { - customer: { - plan: { plan_id: free.id }, - }, + expect(customer).toBeTruthy(); + expect(otherCustomers.size).toBe(4); + + // Explicit parallel attach of all 5 customers to free. `free.id` was + // mutated by initProductsV0 to include the prefix. + const allCustomerIds = [customerId, ...otherIds]; + await Promise.all( + allCustomerIds.map((id) => + autumnV1.attach({ customer_id: id, product_id: free.id }), + ), + ); + + await autumnV2_2.migrationsV2.deleteAndCreate({ + id: customerId, + filter: { customer: { plan: { plan_id: free.id } } }, + operations: { + customer: { + update_plans: [ + { + target: { plan_id: free.id }, + add_items: [{ feature_id: TestFeature.Dashboard }], + }, + ], }, - operations: { - customer: { - update_plans: [ - { - target: { plan_id: free.id }, - add_items: [ - { - feature_id: TestFeature.Dashboard, - }, - ], - }, - ], - }, - }, - }); + }, + }); - expect(created.id).toBe(customerId); - expect(created.internal_id).toBeTruthy(); - expect(created.filter?.customer?.plan).toMatchObject({ plan_id: free.id }); - expect(created.operations?.customer?.update_plans?.[0]).toMatchObject({ - target: { plan_id: free.id }, - add_items: [{ feature_id: TestFeature.Dashboard }], - }); - - // Round-trip: list and confirm presence. - const { list } = await autumnV2_2.migrationsV2.list(); - expect(list.some((m) => m.id === customerId)).toBe(true); - }, -); + const runHandle = await autumnV2_2.migrationsV2.run({ + id: customerId, + dry_run: false, + }); + expect(runHandle.run_id).toBeTruthy(); + expect(runHandle.dry_run).toBe(false); +}); diff --git a/server/tests/utils/testInitUtils/initScenario.ts b/server/tests/utils/testInitUtils/initScenario.ts index 36db3e63c..6abcb51bd 100644 --- a/server/tests/utils/testInitUtils/initScenario.ts +++ b/server/tests/utils/testInitUtils/initScenario.ts @@ -1094,25 +1094,27 @@ export async function initScenario({ customer = result.customer; } - // 2.5. Other customers — share the primary's test clock. + // 2.5. Other customers — share the primary's test clock. Created in + // parallel since they're independent (each gets its own Stripe customer); + // the shared `testClockId` is read-only here so concurrency is safe. const otherCustomersMap = new Map(); - for (const otherCusConfig of config.otherCustomers) { - const otherResult = await initCustomerV3({ - ctx, - customerId: otherCusConfig.id, - customerData: otherCusConfig.data, - attachPm: otherCusConfig.paymentMethod, - withTestClock: false, - ...(testClockId ? { existingTestClockId: testClockId } : {}), - withDefault: false, - defaultGroup: productPrefix, - skipWebhooks: config.skipWebhooks, - }); - otherCustomersMap.set(otherCusConfig.id, { - id: otherCusConfig.id, - customer: otherResult.customer, - }); - } + const otherCustomersResults = await Promise.all( + config.otherCustomers.map(async (otherCusConfig) => { + const otherResult = await initCustomerV3({ + ctx, + customerId: otherCusConfig.id, + customerData: otherCusConfig.data, + attachPm: otherCusConfig.paymentMethod, + withTestClock: false, + ...(testClockId ? { existingTestClockId: testClockId } : {}), + withDefault: false, + defaultGroup: productPrefix, + skipWebhooks: config.skipWebhooks, + }); + return { id: otherCusConfig.id, customer: otherResult.customer }; + }), + ); + for (const r of otherCustomersResults) otherCustomersMap.set(r.id, r); // 3. Create autumn clients const autumnV0 = new AutumnInt({ diff --git a/shared/api/migrations/compiler/buildCustomerQuery.ts b/shared/api/migrations/compiler/buildCustomerQuery.ts deleted file mode 100644 index 093dbaf86..000000000 --- a/shared/api/migrations/compiler/buildCustomerQuery.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { type SQL, sql } from "drizzle-orm"; -import type { CustomerFilter } from "../filters/customerFilter.js"; -import { compileFilter } from "./compileFilter.js"; -import type { ResolutionContext } from "./filterToIr/resolutionContext.js"; - -/** - * Top-level query builders for customer-rooted migration filters. - * - * `compileFilter` returns the full WHERE expression (including org/env - * pushdown via the registry's ambient predicates). These helpers wrap it - * into a complete, parameterized Drizzle `SQL` with cursor-based - * pagination for iteration over large customer sets. - */ - -export const DEFAULT_BATCH_SIZE = 10_000; - -/** Convert the compiler's `{ sql, params }` output to a Drizzle SQL chunk. */ -function rawWithParamsToDrizzle({ - sql: raw, - params, -}: { - sql: string; - params: readonly unknown[]; -}): SQL { - const parts = raw.split("?"); - if (parts.length - 1 !== params.length) - throw new Error( - `Placeholder/param count mismatch: ${parts.length - 1} placeholders vs ${params.length} params`, - ); - const chunks: SQL[] = []; - for (let i = 0; i < parts.length; i++) { - chunks.push(sql.raw(parts[i])); - if (i < params.length) chunks.push(sql`${params[i]}`); - } - return sql.join(chunks, sql.raw("")); -} - -type BuildArgs = { - orgId: string; - env: string; - filter: CustomerFilter; - ctx: ResolutionContext; -}; - -const compileWhere = ({ orgId, env, filter, ctx }: BuildArgs): SQL => - rawWithParamsToDrizzle( - compileFilter({ filter, ctx, ambient: { orgId, env } }), - ); - -/** Full SELECT. Returns `{ internal_id, id }` rows. */ -export function buildCustomerSelect({ - orgId, - env, - filter, - ctx, - limit, - afterInternalId, -}: BuildArgs & { limit?: number; afterInternalId?: string }): SQL { - const where = compileWhere({ orgId, env, filter, ctx }); - const cursor = afterInternalId - ? sql`AND c.internal_id > ${afterInternalId}` - : sql``; - const limitClause = limit !== undefined ? sql`LIMIT ${limit}` : sql``; - return sql` - SELECT c.internal_id, c.id - FROM customers c - WHERE (${where}) ${cursor} - ORDER BY c.internal_id - ${limitClause} - `; -} - -/** COUNT(*) applying the same filter. */ -export function buildCustomerCount({ - orgId, - env, - filter, - ctx, -}: BuildArgs): SQL { - const where = compileWhere({ orgId, env, filter, ctx }); - return sql` - SELECT COUNT(*)::bigint AS count - FROM customers c - WHERE (${where}) - `; -} - -/** - * Iterate matching customers in batches (default 10k per step) using - * keyset pagination on `c.internal_id`. Yields one batch at a time so - * callers can stream-process without loading the full result set. - */ -export async function* iterateCustomers({ - db, - orgId, - env, - filter, - ctx, - batchSize = DEFAULT_BATCH_SIZE, -}: BuildArgs & { - db: { execute: (query: SQL) => Promise }; - batchSize?: number; -}): AsyncGenerator> { - let cursor: string | undefined; - while (true) { - const query = buildCustomerSelect({ - orgId, - env, - filter, - ctx, - limit: batchSize, - afterInternalId: cursor, - }); - const rows = (await db.execute(query)) as unknown as Array<{ - internal_id: string; - id: string | null; - }>; - if (rows.length === 0) return; - yield rows; - if (rows.length < batchSize) return; - cursor = rows[rows.length - 1].internal_id; - } -} diff --git a/shared/api/migrations/compiler/index.ts b/shared/api/migrations/compiler/index.ts index bcf840951..aa8055d00 100644 --- a/shared/api/migrations/compiler/index.ts +++ b/shared/api/migrations/compiler/index.ts @@ -1,4 +1,3 @@ -export * from "./buildCustomerQuery.js"; export * from "./compileFilter.js"; export * from "./compilePlanFilter.js"; export * from "./filterToIr/index.js"; diff --git a/shared/db/schema.ts b/shared/db/schema.ts index 9f029b085..c62d18010 100644 --- a/shared/db/schema.ts +++ b/shared/db/schema.ts @@ -38,6 +38,8 @@ import { migrationErrorRelations } from "../models/migrationModels/migrationErro import { migrationErrors } from "../models/migrationModels/migrationErrorTable.js"; // Migration Tables import { migrationJobs } from "../models/migrationModels/migrationJobTable.js"; +// Migrations V2 +import { migrations } from "../models/migrationV2Models/migrationTable.js"; /* RELATIONS */ import { organizationsRelations } from "../models/orgModels/orgRelations.js"; import { organizations } from "../models/orgModels/orgTable.js"; @@ -88,76 +90,77 @@ import { } from "./auth-schema.js"; export { - // Tables - organizations, + account, + actions, + apiKeyRelations, + apiKeys, + autoTopupLimitStates as autoTopupLimits, chatResults, checkouts, - freeTrials, - entitlements, - prices, - features, - products, - customerProducts, - customerPrices, + checkoutsRelations, customerEntitlements, - invoices, - invoiceLineItems, + customerEntitlementsRelations, + customerPrices, + customerPricesRelations, + customerProducts, + customerProductsRelations, customers, - autoTopupLimitStates as autoTopupLimits, + customersRelations, entities, - apiKeys, - metadata, - subscriptions, - rewards, - rewardPrograms, - referralCodes, - rewardRedemptions, - migrationJobs, - migrationErrors, - actions, + entitiesRelations, + entitlements, + entitlementsRelations, events, - replaceables, - rollovers, - schedules, - schedulePhases, - vercelResources, - revenuecatMappings as revcatMappings, - // Auth - user, - session, - account, - verification, - member, + featureRelations, + features, + freeTrialRelations, + freeTrials, invitation, + inviteRelations, + invoiceLineItems, + invoiceRelations, + invoices, // OAuth Provider jwks, - oauthClient, + member, + memberRelations, + metadata, + migrationErrorRelations, + migrationErrors, + migrationJobs, + migrations, oauthAccessToken, - oauthRefreshToken, + oauthClient, oauthConsent, + oauthRefreshToken, + // Tables + organizations, // Relations organizationsRelations, - entitlementsRelations, - featureRelations, priceRelations, + prices, productRelations, - freeTrialRelations, - customerProductsRelations, - customerPricesRelations, - customerEntitlementsRelations, - customersRelations, - entitiesRelations, - apiKeyRelations, - rewardProgramRelations, + products, referralCodeRelations, - rewardRedemptionRelations, - migrationErrorRelations, + referralCodes, replaceableRelations, - invoiceRelations, + replaceables, + revenuecatMappings as revcatMappings, + rewardProgramRelations, + rewardPrograms, + rewardRedemptionRelations, + rewardRedemptions, + rewards, rolloverRelations, - checkoutsRelations, + rollovers, + schedulePhases, + schedules, + session, + subscriptions, + // Auth + user, // Auth Relations userRelations, - memberRelations, - inviteRelations, + vercelResources, + verification, }; diff --git a/trigger.config.ts b/trigger.config.ts new file mode 100644 index 000000000..76acbf817 --- /dev/null +++ b/trigger.config.ts @@ -0,0 +1,78 @@ +import { + additionalPackages, + aptGet, + syncEnvVars, +} from "@trigger.dev/build/extensions/core"; +import { defineConfig } from "@trigger.dev/sdk/v3"; +import { fetchInfisicalSecretsFromEnv } from "./server/src/external/infisical/fetchInfisicalSecrets.js"; + +export default defineConfig({ + project: "proj_cwiutfmpdzfcshxevkok", + runtime: "node", + logLevel: "log", + maxDuration: 3600, + retries: { + enabledInDev: true, + default: { + maxAttempts: 3, + minTimeoutInMs: 1000, + maxTimeoutInMs: 10000, + factor: 2, + randomize: true, + }, + }, + dirs: ["server/src/trigger"], + build: { + // Native / heavy deps stay external — bundling them inflates the deploy + // and breaks platform-specific binaries (pg, ioredis, etc.). + external: [ + "@aws-sdk/client-s3", + "@google-cloud/bigquery", + "ioredis", + "pino", + "@axiomhq/pino", + "drizzle-orm", + "postgres", + "pg", + "zod", + ], + extensions: [ + // Server runtime imports `*.lua` as raw text via Bun's loader. esbuild + // (used by the trigger build) doesn't see bunfig — register a plugin + // that reads .lua files as text so the same imports work post-bundle. + { + name: "lua-text-loader", + onBuildStart: async (context) => { + context.registerPlugin({ + name: "lua-text-loader-plugin", + setup(build) { + build.onLoad({ filter: /\.lua$/ }, async (args) => { + const { readFileSync } = await import("node:fs"); + return { + contents: readFileSync(args.path, "utf-8"), + loader: "text", + }; + }); + }, + }); + }, + }, + aptGet({ packages: ["pkg-config", "liblzma-dev"] }), + additionalPackages({ + packages: [ + "@axiomhq/pino", + "postgres", + "pg", + "drizzle-orm", + "ioredis", + "zod", + ], + }), + // In DEV: `bun d` runs `bunx trigger.dev dev` under + // `infisical run --env=dev --recursive --`, so the trigger CLI + // inherits secrets from process.env directly. `syncEnvVars` only + // runs at DEPLOY time (pushes Infisical secrets to trigger cloud). + syncEnvVars(async (ctx) => fetchInfisicalSecretsFromEnv(ctx.env)), + ], + }, +}); diff --git a/vite/package.json b/vite/package.json index 6f207ad71..125909ff2 100644 --- a/vite/package.json +++ b/vite/package.json @@ -67,6 +67,7 @@ "date-fns": "^3.6.0", "decimal.js": "^10.5.0", "input-otp": "^1.4.2", + "json5": "^2.2.3", "lodash": "^4.17.21", "lucide-react": "^0.562.0", "motion": "^12.26.1", diff --git a/vite/src/hooks/queries/useMigrationsQuery.tsx b/vite/src/hooks/queries/useMigrationsQuery.tsx index f483d460c..4fc32fd1a 100644 --- a/vite/src/hooks/queries/useMigrationsQuery.tsx +++ b/vite/src/hooks/queries/useMigrationsQuery.tsx @@ -1,18 +1,66 @@ import type { Migration } from "@autumn/shared"; -import { useQuery } from "@tanstack/react-query"; +import type { MigrationFilter } from "@autumn/shared/api/migrations/filters/migrationFilter.js"; +import type { Operations } from "@autumn/shared/api/migrations/operations/operations.js"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useQueryKeyFactory } from "@/hooks/common/useQueryKeyFactory"; -import { MigrationService } from "@/services/MigrationService"; import { useAxiosInstance } from "@/services/useAxiosInstance"; export const useMigrationsQuery = () => { const axiosInstance = useAxiosInstance(); const buildKey = useQueryKeyFactory(); + const queryClient = useQueryClient(); + const queryKey = buildKey(["migrations"]); - const { data, isLoading, error, refetch } = useQuery<{ - list: Migration[]; - }>({ - queryKey: buildKey(["migrations"]), - queryFn: () => MigrationService.list(axiosInstance), + const { data, isLoading, error, refetch } = useQuery<{ list: Migration[] }>({ + queryKey, + queryFn: async () => { + const { data } = await axiosInstance.post<{ list: Migration[] }>( + "/migrations.list", + ); + return data; + }, + }); + + const invalidate = () => queryClient.invalidateQueries({ queryKey }); + + const createMutation = useMutation({ + mutationFn: async (body: { id: string }) => { + const { data } = await axiosInstance.post( + "/migrations.create", + body, + ); + return data; + }, + onSuccess: invalidate, + }); + + const updateMutation = useMutation({ + mutationFn: async (body: { + id: string; + updates: { + id?: string; + filter?: MigrationFilter | null; + operations?: Operations | null; + }; + }) => { + const { data } = await axiosInstance.post( + "/migrations.update", + body, + ); + return data; + }, + onSuccess: invalidate, + }); + + const runMutation = useMutation({ + mutationFn: async (body: { id: string; dry_run?: boolean }) => { + const { data } = await axiosInstance.post<{ + migration_id: string; + dry_run: boolean; + run_id: string; + }>("/migrations.run", body); + return data; + }, }); return { @@ -20,5 +68,11 @@ export const useMigrationsQuery = () => { isLoading, error, refetch, + createMigration: createMutation.mutateAsync, + isCreating: createMutation.isPending, + updateMigration: updateMutation.mutateAsync, + isUpdating: updateMutation.isPending, + runMigration: runMutation.mutateAsync, + isRunning: runMutation.isPending, }; }; diff --git a/vite/src/services/MigrationService.ts b/vite/src/services/MigrationService.ts deleted file mode 100644 index a6d454ec1..000000000 --- a/vite/src/services/MigrationService.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { Migration } from "@autumn/shared"; -import type { AxiosInstance } from "axios"; - -export const MigrationService = { - list: async (axiosInstance: AxiosInstance) => { - const { data } = await axiosInstance.post<{ list: Migration[] }>( - "/migrations.list", - ); - return data; - }, - - create: async (axiosInstance: AxiosInstance, body: { id: string }) => { - const { data } = await axiosInstance.post( - "/migrations.create", - body, - ); - return data; - }, -}; diff --git a/vite/src/views/migrations/MigrationsView.tsx b/vite/src/views/migrations/MigrationsView.tsx index 4c951bac0..af54db24e 100644 --- a/vite/src/views/migrations/MigrationsView.tsx +++ b/vite/src/views/migrations/MigrationsView.tsx @@ -2,8 +2,10 @@ import { MigrationListTable } from "./migration-list/MigrationListTable"; export const MigrationsView = () => { return ( -
- +
+
+ +
); }; diff --git a/vite/src/views/migrations/components/CreateMigrationSheet.tsx b/vite/src/views/migrations/components/CreateMigrationSheet.tsx index f3c308f0b..09cda2cff 100644 --- a/vite/src/views/migrations/components/CreateMigrationSheet.tsx +++ b/vite/src/views/migrations/components/CreateMigrationSheet.tsx @@ -10,8 +10,6 @@ import { } from "@/components/v2/sheets/SharedSheetComponents"; import { Sheet, SheetContent } from "@/components/v2/sheets/Sheet"; import { useMigrationsQuery } from "@/hooks/queries/useMigrationsQuery"; -import { MigrationService } from "@/services/MigrationService"; -import { useAxiosInstance } from "@/services/useAxiosInstance"; import { getBackendErr } from "@/utils/genUtils"; function CreateMigrationSheet({ @@ -23,15 +21,13 @@ function CreateMigrationSheet({ onOpenChange?: (open: boolean) => void; onSuccess?: (migrationId: string) => void; } = {}) { - const [loading, setLoading] = useState(false); const [internalOpen, setInternalOpen] = useState(false); const [id, setId] = useState(""); const open = controlledOpen !== undefined ? controlledOpen : internalOpen; const setOpen = controlledOnOpenChange || setInternalOpen; - const axiosInstance = useAxiosInstance(); - const { refetch } = useMigrationsQuery(); + const { createMigration, isCreating } = useMigrationsQuery(); const handleCreateMigration = async () => { if (!id.trim()) { @@ -39,12 +35,8 @@ function CreateMigrationSheet({ return; } - setLoading(true); try { - const created = await MigrationService.create(axiosInstance, { - id: id.trim(), - }); - await refetch(); + const created = await createMigration({ id: id.trim() }); toast.success("Migration created"); setOpen(false); onSuccess?.(created.id); @@ -52,8 +44,6 @@ function CreateMigrationSheet({ toast.error( getBackendErr(error as AxiosError, "Failed to create migration"), ); - } finally { - setLoading(false); } }; @@ -94,7 +84,7 @@ function CreateMigrationSheet({ className="w-full" onClick={handleCreateMigration} metaShortcut="enter" - isLoading={loading} + isLoading={isCreating} > Create migration diff --git a/vite/src/views/migrations/components/EditMigrationSheet.tsx b/vite/src/views/migrations/components/EditMigrationSheet.tsx new file mode 100644 index 000000000..f5535de07 --- /dev/null +++ b/vite/src/views/migrations/components/EditMigrationSheet.tsx @@ -0,0 +1,216 @@ +import type { Migration } from "@autumn/shared"; +import Editor, { type Monaco } from "@monaco-editor/react"; +import { PlayIcon } from "@phosphor-icons/react"; +import type { AxiosError } from "axios"; +import JSON5 from "json5"; +import { useEffect, useMemo, useState } from "react"; +import { toast } from "sonner"; +import { Button } from "@/components/v2/buttons/Button"; +import { ShortcutButton } from "@/components/v2/buttons/ShortcutButton"; +import { + SheetFooter, + SheetHeader, + SheetSection, +} from "@/components/v2/sheets/SharedSheetComponents"; +import { Sheet, SheetContent } from "@/components/v2/sheets/Sheet"; +import { useMigrationsQuery } from "@/hooks/queries/useMigrationsQuery"; +import { getBackendErr } from "@/utils/genUtils"; + +const EMPTY_FILTER = `{ + customer: {}, +} +`; + +const EMPTY_OPERATIONS = `{ + customer: {}, +} +`; + +function stringify(value: unknown, fallback: string) { + if (value === null || value === undefined) return fallback; + // JSON5.stringify with no quotes around safe identifier keys + return JSON5.stringify(value, { space: 2, quote: '"' }); +} + +function tryParse(text: string) { + const trimmed = text.trim(); + if (!trimmed) return { value: null as unknown, error: null as string | null }; + try { + return { value: JSON5.parse(trimmed), error: null }; + } catch (err) { + return { + value: null, + error: err instanceof Error ? err.message : "Invalid syntax", + }; + } +} + +// Disable TS diagnostics so bare object literals like `{ a: 1 }` don't squiggle. +function configureMonaco(monaco: Monaco) { + monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({ + noSemanticValidation: true, + noSyntaxValidation: true, + noSuggestionDiagnostics: true, + }); +} + +const EDITOR_OPTIONS = { + minimap: { enabled: false }, + scrollBeyondLastLine: false, + fontSize: 12, + tabSize: 2, + wordWrap: "on" as const, + formatOnPaste: true, + formatOnType: true, + glyphMargin: false, + folding: false, + lineNumbersMinChars: 2, + lineDecorationsWidth: 4, + padding: { top: 8, bottom: 8 }, +}; + +function EditMigrationSheet({ + migration, + open, + onOpenChange, +}: { + migration: Migration | null; + open: boolean; + onOpenChange: (open: boolean) => void; +}) { + const { updateMigration, isUpdating, runMigration, isRunning } = + useMigrationsQuery(); + + const [filterText, setFilterText] = useState(""); + const [operationsText, setOperationsText] = useState(""); + + useEffect(() => { + if (!migration) return; + setFilterText(stringify(migration.filter, EMPTY_FILTER)); + setOperationsText(stringify(migration.operations, EMPTY_OPERATIONS)); + }, [migration]); + + const filterParsed = useMemo(() => tryParse(filterText), [filterText]); + const operationsParsed = useMemo( + () => tryParse(operationsText), + [operationsText], + ); + + const canSave = !filterParsed.error && !operationsParsed.error; + + const handleSave = async () => { + if (!migration || !canSave) return; + try { + await updateMigration({ + id: migration.id, + updates: { + filter: filterParsed.value as null, + operations: operationsParsed.value as null, + }, + }); + toast.success("Migration saved"); + onOpenChange(false); + } catch (error) { + toast.error( + getBackendErr(error as AxiosError, "Failed to save migration"), + ); + } + }; + + const handleRun = async () => { + if (!migration) return; + try { + const result = await runMigration({ id: migration.id, dry_run: true }); + toast.success(`Migration triggered (run ${result.run_id})`); + } catch (error) { + toast.error( + getBackendErr(error as AxiosError, "Failed to run migration"), + ); + } + }; + + return ( + + + + + Run + + } + /> + +
+ +
+ setFilterText(value ?? "")} + beforeMount={configureMonaco} + options={EDITOR_OPTIONS} + theme="vs-dark" + /> +
+ {filterParsed.error && ( +
+ {filterParsed.error} +
+ )} +
+ + +
+ setOperationsText(value ?? "")} + beforeMount={configureMonaco} + options={EDITOR_OPTIONS} + theme="vs-dark" + /> +
+ {operationsParsed.error && ( +
+ {operationsParsed.error} +
+ )} +
+
+ + + onOpenChange(false)} + singleShortcut="escape" + > + Cancel + + + Save + + +
+
+ ); +} + +export default EditMigrationSheet; diff --git a/vite/src/views/migrations/migration-list/MigrationListTable.tsx b/vite/src/views/migrations/migration-list/MigrationListTable.tsx index 94b8b021a..a010647aa 100644 --- a/vite/src/views/migrations/migration-list/MigrationListTable.tsx +++ b/vite/src/views/migrations/migration-list/MigrationListTable.tsx @@ -1,14 +1,18 @@ +import type { Migration } from "@autumn/shared"; import { ArrowsClockwiseIcon } from "@phosphor-icons/react"; -import { useMemo } from "react"; +import { useMemo, useState } from "react"; import { Table } from "@/components/general/table"; import { EmptyState } from "@/components/v2/empty-states/EmptyState"; import { useMigrationsQuery } from "@/hooks/queries/useMigrationsQuery"; import { useProductTable } from "@/views/products/hooks/useProductTable"; +import EditMigrationSheet from "../components/EditMigrationSheet"; import { createMigrationListColumns } from "./MigrationListColumns"; import { MigrationListCreateButton } from "./MigrationListCreateButton"; export function MigrationListTable() { const { migrations, isLoading } = useMigrationsQuery(); + const [selected, setSelected] = useState(null); + const [sheetOpen, setSheetOpen] = useState(false); const columns = useMemo(() => createMigrationListColumns(), []); @@ -23,6 +27,11 @@ export function MigrationListTable() { const hasRows = table.getRowModel().rows.length > 0; + const handleRowClick = (row: Migration) => { + setSelected(row); + setSheetOpen(true); + }; + if (!isLoading && !hasRows) { return ( - -
- - - Migrations - - -
- -
-
+ <> + + +
+ + + Migrations + + +
+ +
+
+
+
+
+ + + + + +
- -
- - - - - - -
-
+ + { + setSheetOpen(open); + if (!open) setSelected(null); + }} + /> + ); }