From 334d13ad948fbcb77f1c6b05ff65cd5bd76feb54 Mon Sep 17 00:00:00 2001 From: amianthus <49116958+SirTenzin@users.noreply.github.com> Date: Mon, 20 Oct 2025 11:12:29 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20=F0=9F=90=9B=20crash?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/api/core/checkProductModels.ts | 218 ++++++++---------- shared/api/core/coreOpModels.ts | 112 ++++----- .../customers/cusProducts/apiCusProduct.ts | 4 - shared/api/customers/customersOpenApi.ts | 8 +- shared/api/entities/entitiesOpenApi.ts | 8 +- shared/api/features/featuresOpenApi.ts | 8 +- shared/api/openapi.ts | 30 ++- .../previousVersions/apiProductItem.ts | 7 +- shared/api/products/productsOpenApi.ts | 8 +- shared/api/referrals/apiReferralCode.ts | 67 +++--- .../planFeatureUtils/itemsToPlanFeatures.ts | 10 +- .../planFeatureUtils/planFeatureIntervals.ts | 4 +- .../planFeatureUtils/planFeaturesToItems.ts | 8 +- shared/utils/planFeatureUtils/planToItems.ts | 4 +- 14 files changed, 226 insertions(+), 270 deletions(-) diff --git a/shared/api/core/checkProductModels.ts b/shared/api/core/checkProductModels.ts index 7fd9c1c0f..7c72a6d87 100644 --- a/shared/api/core/checkProductModels.ts +++ b/shared/api/core/checkProductModels.ts @@ -13,97 +13,82 @@ export const ProductScenarioSchema = z.enum([ ]); // Check Product Schemas -export const CheckProductParamsSchema = z - .object({ - customer_id: z.string().meta({ - description: "The ID of the customer", - example: "cus_123", - }), - product_id: z.string().meta({ - description: "The ID of the product to check", - example: "pro_plan", - }), - entity_id: z.string().optional().meta({ - description: "The ID of the entity (optional)", - example: "entity_123", - }), - customer_data: CustomerDataSchema.optional().meta({ - description: - "Customer data to create or update the customer if they don't exist", - }), - entity_data: EntityDataSchema.optional().meta({ - description: "Entity data to create the entity if it doesn't exist", - }), - with_preview: z.boolean().optional().meta({ - description: "Whether to include preview information in the response", - example: true, - }), - }) - .meta({ - id: "CheckProductParams", - description: "Parameters for checking product availability", - }); +export const CheckProductParamsSchema = z.object({ + customer_id: z.string().meta({ + description: "The ID of the customer", + example: "cus_123", + }), + product_id: z.string().meta({ + description: "The ID of the product to check", + example: "pro_plan", + }), + entity_id: z.string().optional().meta({ + description: "The ID of the entity (optional)", + example: "entity_123", + }), + customer_data: CustomerDataSchema.optional().meta({ + description: + "Customer data to create or update the customer if they don't exist", + }), + entity_data: EntityDataSchema.optional().meta({ + description: "Entity data to create the entity if it doesn't exist", + }), + with_preview: z.boolean().optional().meta({ + description: "Whether to include preview information in the response", + example: true, + }), +}); -export const CheckProductPreviewItemSchema = z - .object({ - price: z.string().meta({ - description: "Formatted price string", - example: "$10.00", - }), - description: z.string().meta({ - description: "Description of the item", - example: "Base subscription", - }), - usage_model: z.enum(["prepaid", "pay_per_use"]).optional().meta({ - description: "The usage model for this item", - example: "prepaid", - }), - }) - .meta({ - id: "CheckProductPreviewItem", - description: "Individual item in product preview", - }); +export const CheckProductPreviewItemSchema = z.object({ + price: z.string().meta({ + description: "Formatted price string", + example: "$10.00", + }), + description: z.string().meta({ + description: "Description of the item", + example: "Base subscription", + }), + usage_model: z.enum(["prepaid", "pay_per_use"]).optional().meta({ + description: "The usage model for this item", + example: "prepaid", + }), +}); -export const CheckProductPreviewOptionSchema = z - .object({ - feature_id: z.string().meta({ - description: "The ID of the feature", - example: "api_calls", - }), - feature_name: z.string().meta({ - description: "The name of the feature", - example: "API Calls", - }), - billing_units: z.number().meta({ - description: "Number of billing units", - example: 1000, - }), - price: z.number().optional().meta({ - description: "Price per billing unit", - example: 0.01, - }), - tiers: z - .array( - z.object({ - to: z.union([z.number(), z.string()]).meta({ - description: "Upper limit of this tier (can be 'inf' for infinite)", - example: 1000, - }), - amount: z.number().meta({ - description: "Price amount for this tier", - example: 10, - }), +export const CheckProductPreviewOptionSchema = z.object({ + feature_id: z.string().meta({ + description: "The ID of the feature", + example: "api_calls", + }), + feature_name: z.string().meta({ + description: "The name of the feature", + example: "API Calls", + }), + billing_units: z.number().meta({ + description: "Number of billing units", + example: 1000, + }), + price: z.number().optional().meta({ + description: "Price per billing unit", + example: 0.01, + }), + tiers: z + .array( + z.object({ + to: z.union([z.number(), z.string()]).meta({ + description: "Upper limit of this tier (can be 'inf' for infinite)", + example: 1000, + }), + amount: z.number().meta({ + description: "Price amount for this tier", + example: 10, }), - ) - .optional() - .meta({ - description: "Tiered pricing structure", }), - }) - .meta({ - id: "CheckProductPreviewOption", - description: "Feature option in product preview", - }); + ) + .optional() + .meta({ + description: "Tiered pricing structure", + }), +}); export const CheckProductPreviewSchema = z .object({ @@ -168,42 +153,33 @@ export const CheckProductPreviewSchema = z .meta({ description: "Payment due in the next cycle", }), - }) - .meta({ - id: "CheckProductPreview", - description: "Preview information for a product check", }); -export const CheckProductResultSchema = z - .object({ - allowed: z.boolean().meta({ - description: "Whether the customer can attach the product", - example: true, - }), - customer_id: z.string().meta({ - description: "The ID of the customer", - example: "cus_123", - }), - product_id: z.string().meta({ - description: "The ID of the product", - example: "pro_plan", - }), - entity_id: z.string().optional().meta({ - description: "The ID of the entity (if provided)", - example: "entity_123", - }), - status: z.string().optional().meta({ - description: "Status code for the check result", - example: "upgrade_available", - }), - preview: CheckProductPreviewSchema.optional().meta({ - description: "Preview information if with_preview was true", - }), - }) - .meta({ - id: "CheckProductResult", - description: "Result of a product check", - }); +export const CheckProductResultSchema = z.object({ + allowed: z.boolean().meta({ + description: "Whether the customer can attach the product", + example: true, + }), + customer_id: z.string().meta({ + description: "The ID of the customer", + example: "cus_123", + }), + product_id: z.string().meta({ + description: "The ID of the product", + example: "pro_plan", + }), + entity_id: z.string().optional().meta({ + description: "The ID of the entity (if provided)", + example: "entity_123", + }), + status: z.string().optional().meta({ + description: "Status code for the check result", + example: "upgrade_available", + }), + preview: CheckProductPreviewSchema.optional().meta({ + description: "Preview information if with_preview was true", + }), +}); export type CheckProductParams = z.infer; export type CheckProductResult = z.infer; diff --git a/shared/api/core/coreOpModels.ts b/shared/api/core/coreOpModels.ts index af0f81419..caf2a66ef 100644 --- a/shared/api/core/coreOpModels.ts +++ b/shared/api/core/coreOpModels.ts @@ -121,76 +121,56 @@ export const TrackResultSchema = z.object({ }); // Query Schemas -export const QueryParamsSchema = z - .object({ - customer_id: z.string().meta({ - description: "The ID of the customer to query analytics for", - example: "cus_123", +export const QueryParamsSchema = z.object({ + customer_id: z.string().meta({ + description: "The ID of the customer to query analytics for", + example: "cus_123", + }), + feature_id: z.union([z.string(), z.array(z.string())]).meta({ + description: "The feature ID(s) to query", + example: "api_calls", + }), + range: z + .enum(["24h", "7d", "30d", "90d", "last_cycle"] as const) + .nullish() + .meta({ + description: + "Time range for the query (defaults to last_cycle if not provided)", + example: "7d", }), - feature_id: z.union([z.string(), z.array(z.string())]).meta({ - description: "The feature ID(s) to query", - example: "api_calls", - }), - range: z - .enum(["24h", "7d", "30d", "90d", "last_cycle"] as const) - .nullish() - .meta({ - description: - "Time range for the query (defaults to last_cycle if not provided)", - example: "7d", - }), - }) - .meta({ - id: "QueryParams", - description: "Parameters for querying analytics data", - }); +}); -export const QueryResultSchema = z - .object({ - list: z.array(z.any()).meta({ - description: "List of usage data points", - example: [{ period: 1717000000000, count: 100 }], - }), - }) - .meta({ - id: "QueryResult", - description: "Result of an analytics query", - }); +export const QueryResultSchema = z.object({ + list: z.array(z.any()).meta({ + description: "List of usage data points", + example: [{ period: 1717000000000, count: 100 }], + }), +}); -export const SetupPaymentParamsSchema = z - .object({ - customer_id: z.string().meta({ - description: "The ID of the customer", - example: "cus_123", - }), - success_url: z.string().optional().meta({ - description: "URL to redirect to after successful payment setup", - example: "https://example.com/success", - }), - checkout_session_params: z.record(z.string(), z.unknown()).optional().meta({ - description: "Additional parameters for the checkout session", - }), - }) - .meta({ - id: "SetupPaymentParams", - description: "Parameters for setting up a payment method", - }); +export const SetupPaymentParamsSchema = z.object({ + customer_id: z.string().meta({ + description: "The ID of the customer", + example: "cus_123", + }), + success_url: z.string().optional().meta({ + description: "URL to redirect to after successful payment setup", + example: "https://example.com/success", + }), + checkout_session_params: z.record(z.string(), z.unknown()).optional().meta({ + description: "Additional parameters for the checkout session", + }), +}); -export const SetupPaymentResultSchema = z - .object({ - customer_id: z.string().meta({ - description: "The ID of the customer", - example: "cus_123", - }), - url: z.string().meta({ - description: "URL to the payment setup page", - example: "https://checkout.stripe.com/...", - }), - }) - .meta({ - id: "SetupPaymentResult", - description: "Result of setting up a payment method", - }); +export const SetupPaymentResultSchema = z.object({ + customer_id: z.string().meta({ + description: "The ID of the customer", + example: "cus_123", + }), + url: z.string().meta({ + description: "URL to the payment setup page", + example: "https://checkout.stripe.com/...", + }), +}); export const BillingPortalParamsSchema = z.object({ customer_id: z.string().meta({ diff --git a/shared/api/customers/cusProducts/apiCusProduct.ts b/shared/api/customers/cusProducts/apiCusProduct.ts index 1524c102e..48d419ae6 100644 --- a/shared/api/customers/cusProducts/apiCusProduct.ts +++ b/shared/api/customers/cusProducts/apiCusProduct.ts @@ -92,10 +92,6 @@ export const ApiCusProductSchema = z "The number of units of this product held by the customer, if applicable", example: 1, }), - }) - .meta({ - id: "CustomerProduct", - description: "Customer product object returned by the API", }); export type ApiCusProduct = z.infer; diff --git a/shared/api/customers/customersOpenApi.ts b/shared/api/customers/customersOpenApi.ts index b9dde80d2..b15821459 100644 --- a/shared/api/customers/customersOpenApi.ts +++ b/shared/api/customers/customersOpenApi.ts @@ -10,11 +10,9 @@ import { UpdateCustomerParamsSchema, } from "./customerOpModels.js"; -// Register schema with .meta() for OpenAPI spec generation -export const ApiCustomerWithMeta = ApiCustomerSchema.meta({ - id: "Customer", - description: "Customer object returned by the API", -}); +// Note: The meta with id is added in openapi.ts to avoid duplicate registration +// This schema is exported through the main index and should not have an id here +export const ApiCustomerWithMeta = ApiCustomerSchema; export const customerOps = { "/customers": { diff --git a/shared/api/entities/entitiesOpenApi.ts b/shared/api/entities/entitiesOpenApi.ts index 902cd8092..387ff19f2 100644 --- a/shared/api/entities/entitiesOpenApi.ts +++ b/shared/api/entities/entitiesOpenApi.ts @@ -5,11 +5,9 @@ import { queryStringArray } from "../common/queryHelpers.js"; import { ApiEntitySchema } from "./apiEntity.js"; import { CreateEntityParamsSchema } from "./entityOpModels.js"; -// Register schema with .meta() for OpenAPI spec generation -export const ApiEntityWithMeta = ApiEntitySchema.meta({ - id: "Entity", - description: "Entity object returned by the API", -}); +// Note: The meta with id is added in openapi.ts to avoid duplicate registration +// This schema is exported through the main index and should not have an id here +export const ApiEntityWithMeta = ApiEntitySchema; export const entityOps = { "/customers/{customer_id}/entities": { diff --git a/shared/api/features/featuresOpenApi.ts b/shared/api/features/featuresOpenApi.ts index b330ef88c..8e5972f4f 100644 --- a/shared/api/features/featuresOpenApi.ts +++ b/shared/api/features/featuresOpenApi.ts @@ -9,11 +9,9 @@ import { UpdateFeatureParamsSchema, } from "./featureOpModels.js"; -// Register the schema with .meta() for OpenAPI spec generation -export const ApiFeatureWithMeta = ApiFeatureSchema.meta({ - id: "Feature", - description: "Feature object returned by the API", -}); +// Note: The meta with id is added in openapi.ts to avoid duplicate registration +// This schema is exported through the main index and should not have an id here +export const ApiFeatureWithMeta = ApiFeatureSchema; export const featureOps = { "/features": { diff --git a/shared/api/openapi.ts b/shared/api/openapi.ts index db640fa6c..25b052b9d 100644 --- a/shared/api/openapi.ts +++ b/shared/api/openapi.ts @@ -61,16 +61,34 @@ const document = createDocument({ id: "EntityData", description: "Entity data for creating an entity", }), - Customer: ApiCustomerWithMeta, - CustomerProduct: ApiCusProductSchema, + Customer: ApiCustomerWithMeta.meta({ + id: "Customer", + description: "Customer object returned by the API", + }), + CustomerProduct: ApiCusProductSchema.meta({ + id: "CustomerProduct", + description: "Customer product object returned by the API", + }), CustomerFeature: ApiCusFeatureSchema.meta({ id: "CustomerFeature", description: "Customer feature object returned by the API", }), - Product: ApiProductWithMeta, - ProductItem: ApiProductItemWithMeta, - Feature: ApiFeatureWithMeta, - Entity: ApiEntityWithMeta, + Product: ApiProductWithMeta.meta({ + id: "Product", + description: "A product", + }), + ProductItem: ApiProductItemWithMeta.meta({ + id: "ProductItem", + description: "Product item defining features and pricing within a product", + }), + Feature: ApiFeatureWithMeta.meta({ + id: "Feature", + description: "Feature object returned by the API", + }), + Entity: ApiEntityWithMeta.meta({ + id: "Entity", + description: "Entity object returned by the API", + }), }, securitySchemes: { secretKey: { diff --git a/shared/api/products/planFeature/previousVersions/apiProductItem.ts b/shared/api/products/planFeature/previousVersions/apiProductItem.ts index fb1854519..cc84797c5 100644 --- a/shared/api/products/planFeature/previousVersions/apiProductItem.ts +++ b/shared/api/products/planFeature/previousVersions/apiProductItem.ts @@ -97,9 +97,8 @@ export const ApiProductItemSchema = z.object({ }), }); -export const ApiProductItemWithMeta = ApiProductItemSchema.meta({ - id: "ProductItem", - description: "Product item defining features and pricing within a product", -}); +// Note: The meta with id is added in openapi.ts to avoid duplicate registration +// This schema is exported through the main index and should not have an id here +export const ApiProductItemWithMeta = ApiProductItemSchema; export type ApiProductItem = z.infer; diff --git a/shared/api/products/productsOpenApi.ts b/shared/api/products/productsOpenApi.ts index 1a997e7f7..5c3899a7b 100644 --- a/shared/api/products/productsOpenApi.ts +++ b/shared/api/products/productsOpenApi.ts @@ -6,11 +6,9 @@ import { import { z } from "zod/v4"; import { ApiProductSchema } from "./previousVersions/apiProduct.js"; -// Register schema with .meta() for OpenAPI spec generation -export const ApiProductWithMeta = ApiProductSchema.meta({ - id: "Product", - description: "A product", -}); +// Note: The meta with id is added in openapi.ts to avoid duplicate registration +// This schema is exported through the main index and should not have an id here +export const ApiProductWithMeta = ApiProductSchema; export const productOps = { "/products": { diff --git a/shared/api/referrals/apiReferralCode.ts b/shared/api/referrals/apiReferralCode.ts index fb8d149c4..d298dadc4 100644 --- a/shared/api/referrals/apiReferralCode.ts +++ b/shared/api/referrals/apiReferralCode.ts @@ -1,44 +1,35 @@ import { z } from "zod/v4"; -export const CreateReferralCodeResponseSchema = z - .object({ - code: z.string().meta({ - description: "The referral code that can be shared with customers", - example: "REF123ABC", - }), - customer_id: z.string().meta({ - description: "Your unique identifier for the customer", - example: "cus_123", - }), - created_at: z.number().meta({ - description: "The timestamp of when the referral code was created", - example: 1717000000, - }), - }) - .meta({ - id: "ReferralCode", - description: "Referral code object returned by the API", - }); +// Note: IDs removed to avoid duplicate registration when exported through main index +export const CreateReferralCodeResponseSchema = z.object({ + code: z.string().meta({ + description: "The referral code that can be shared with customers", + example: "REF123ABC", + }), + customer_id: z.string().meta({ + description: "Your unique identifier for the customer", + example: "cus_123", + }), + created_at: z.number().meta({ + description: "The timestamp of when the referral code was created", + example: 1717000000, + }), +}); -export const RedeemReferralCodeResponseSchema = z - .object({ - id: z.string().meta({ - description: "The ID of the redemption event", - example: "red_123", - }), - customer_id: z.string().meta({ - description: "Your unique identifier for the customer", - example: "cus_456", - }), - reward_id: z.string().meta({ - description: "The ID of the reward that will be granted", - example: "reward_789", - }), - }) - .meta({ - id: "RedeemReferralCodeResponse", - description: "Redemption response object returned by the API", - }); +export const RedeemReferralCodeResponseSchema = z.object({ + id: z.string().meta({ + description: "The ID of the redemption event", + example: "red_123", + }), + customer_id: z.string().meta({ + description: "Your unique identifier for the customer", + example: "cus_456", + }), + reward_id: z.string().meta({ + description: "The ID of the reward that will be granted", + example: "reward_789", + }), +}); export type CreateReferralCodeResponse = z.infer< typeof CreateReferralCodeResponseSchema diff --git a/shared/utils/planFeatureUtils/itemsToPlanFeatures.ts b/shared/utils/planFeatureUtils/itemsToPlanFeatures.ts index 131467560..05c96403d 100644 --- a/shared/utils/planFeatureUtils/itemsToPlanFeatures.ts +++ b/shared/utils/planFeatureUtils/itemsToPlanFeatures.ts @@ -1,13 +1,15 @@ +import { ResetInterval } from "@api/models.js"; import { type ApiPlanFeature, ApiPlanFeatureSchema, - type BillingInterval, - Infinite, +} from "@api/products/planFeature/apiPlanFeature.js"; +import type { BillingInterval } from "@models/productModels/priceModels/priceEnums.js"; +import { Infinite } from "@models/productModels/productEnums.js"; +import { type ProductItem, - ResetInterval, TierInfinite, type UsageModel, -} from "@autumn/shared"; +} from "@models/productV2Models/productItemModels/productItemModels.js"; import { itemIntvToResetIntv, rolloverToResetIntv, diff --git a/shared/utils/planFeatureUtils/planFeatureIntervals.ts b/shared/utils/planFeatureUtils/planFeatureIntervals.ts index e3a2ab0e9..745d913a2 100644 --- a/shared/utils/planFeatureUtils/planFeatureIntervals.ts +++ b/shared/utils/planFeatureUtils/planFeatureIntervals.ts @@ -1,8 +1,8 @@ +import { ResetInterval } from "@api/models.js"; import { ProductItemInterval, - ResetInterval, RolloverDuration, -} from "@autumn/shared"; +} from "@models/productV2Models/productItemModels/productItemModels.js"; export const resetIntvToItemIntv = ( resetIntv: ResetInterval, diff --git a/shared/utils/planFeatureUtils/planFeaturesToItems.ts b/shared/utils/planFeatureUtils/planFeaturesToItems.ts index 1a80b510f..0392dee1c 100644 --- a/shared/utils/planFeatureUtils/planFeaturesToItems.ts +++ b/shared/utils/planFeatureUtils/planFeaturesToItems.ts @@ -1,10 +1,10 @@ +import type { ResetInterval } from "@api/models.js"; +import type { ApiPlanFeature } from "@api/products/planFeature/apiPlanFeature.js"; +import { Infinite } from "@models/productModels/productEnums.js"; import { - type ApiPlanFeature, - Infinite, type ProductItem, ProductItemSchema, - type ResetInterval, -} from "@autumn/shared"; +} from "@models/productV2Models/productItemModels/productItemModels.js"; import { resetIntvToItemIntv, resetIntvToRollover, diff --git a/shared/utils/planFeatureUtils/planToItems.ts b/shared/utils/planFeatureUtils/planToItems.ts index a722dcec7..17e4281d8 100644 --- a/shared/utils/planFeatureUtils/planToItems.ts +++ b/shared/utils/planFeatureUtils/planToItems.ts @@ -1,4 +1,6 @@ -import type { ApiPlan, BillingInterval, ProductItem } from "@autumn/shared"; +import type { ApiPlan } from "@api/products/apiPlan.js"; +import type { BillingInterval } from "@models/productModels/priceModels/priceEnums.js"; +import type { ProductItem } from "@models/productV2Models/productItemModels/productItemModels.js"; import { planFeaturesToItems } from "./planFeaturesToItems.js"; /**