From 485c406b3e83151c6a599a2e1198eacdc1919de0 Mon Sep 17 00:00:00 2001 From: Sourav Kashyap Date: Tue, 19 May 2026 20:52:35 +0530 Subject: [PATCH 1/6] feat(billing): add billing endpoints --- services/subscription-service/.env.example | 6 + .../subscription-service/src/component.ts | 25 +- .../controllers/billing-service.controller.ts | 912 ++++++++++++++++++ .../billing-subscription.controller.ts | 508 ++++++++++ .../controllers/billing-webhook.controller.ts | 379 ++++++++ .../src/controllers/index.ts | 7 + services/subscription-service/src/types.ts | 58 ++ 7 files changed, 1894 insertions(+), 1 deletion(-) create mode 100644 services/subscription-service/src/controllers/billing-service.controller.ts create mode 100644 services/subscription-service/src/controllers/billing-subscription.controller.ts create mode 100644 services/subscription-service/src/controllers/billing-webhook.controller.ts diff --git a/services/subscription-service/.env.example b/services/subscription-service/.env.example index 574df2f..73baa0e 100644 --- a/services/subscription-service/.env.example +++ b/services/subscription-service/.env.example @@ -22,3 +22,9 @@ FEATURE_DB_USER= FEATURE_DB_PASSWORD= FEATURE_DB_DATABASE= FEATURE_DB_SCHEMA= + +SITE= +API_KEY= +STRIPE_SECRET= +CHARGEBEE_ITEM_FAMILY_ID= +BILLING_PROVIDER= diff --git a/services/subscription-service/src/component.ts b/services/subscription-service/src/component.ts index f248292..994c607 100644 --- a/services/subscription-service/src/component.ts +++ b/services/subscription-service/src/component.ts @@ -36,6 +36,13 @@ import { AuthorizationBindings, AuthorizationComponent, } from 'loopback4-authorization'; +import { + BillingComponentBindings, + StripeServiceProvider, + StripeBindings, +} from 'loopback4-billing'; +import * as dotenv from 'dotenv'; +import * as dotenvExt from 'dotenv-extended'; import { SubscriptionServiceBindings, SYSTEM_USER, @@ -87,6 +94,14 @@ export class SubscriptionServiceComponent implements Component { // Mount core component this.application.component(CoreComponent); + // Load environment variables + dotenv.config(); + dotenvExt.load({ + schema: '.env.example', + errorOnMissing: true, + includeProcessEnv: true, + }); + /**Bind the feature toggle service to main the list of features */ this.application .bind(FeatureToggleBindings.Config) @@ -94,6 +109,14 @@ export class SubscriptionServiceComponent implements Component { this.application.component(FeatureToggleServiceComponent); this.application.component(BillingComponent); + // Configure Stripe billing provider + this.application.bind(StripeBindings.config).to({ + secretKey: process.env.STRIPE_SECRET ?? '', + }); + this.application + .bind(BillingComponentBindings.SDKProvider) + .toProvider(StripeServiceProvider); + this.application.api({ openapi: '3.0.0', info: { @@ -202,7 +225,7 @@ export class SubscriptionServiceComponent implements Component { // Mount authorization component for default sequence this.application.bind(AuthorizationBindings.CONFIG).to({ - allowAlwaysPaths: ['/explorer'], + allowAlwaysPaths: ['/explorer', '/billing', '/webhooks'], }); this.application.component(AuthorizationComponent); } diff --git a/services/subscription-service/src/controllers/billing-service.controller.ts b/services/subscription-service/src/controllers/billing-service.controller.ts new file mode 100644 index 0000000..50a1d59 --- /dev/null +++ b/services/subscription-service/src/controllers/billing-service.controller.ts @@ -0,0 +1,912 @@ +import {inject} from '@loopback/core'; +import { + del, + get, + param, + post, + put, + requestBody, + response, +} from '@loopback/rest'; +import {authorize} from 'loopback4-authorization'; +import { + BillingComponentBindings, + IService, + TInvoicePdf, + TInvoicePaymentDetails, + TPaymentIntent, + TCustomer, + TPaymentSource, + TInvoice, + Transaction, +} from 'loopback4-billing'; + +const BASE = '/billing'; + +/** + * Sandbox controller that exercises the full IService interface from + * loopback4-billing — customer CRUD, payment source, and invoice management. + * + * Bound to BillingComponentBindings.SDKProvider (StripeService). + */ +export class BillingServiceController { + constructor( + @inject(BillingComponentBindings.SDKProvider) + private readonly billingService: IService, + ) {} + + // ------------------------------------------------------------------------- + // CUSTOMER + // ------------------------------------------------------------------------- + + /** + * Create a new customer in Stripe. + * + * Example body: + * ```json + * { + * "firstName": "John", + * "lastName": "Doe", + * "email": "john.doe@example.com", + * "phone": "+1234567890" + * } + * ``` + */ + @authorize({permissions: ['*']}) + @post(`${BASE}/customers`, { + summary: 'Create a new customer', + responses: { + '200': { + description: 'Created customer object', + content: {'application/json': {schema: {type: 'object'}}}, + }, + }, + }) + async createCustomer( + @requestBody({ + content: { + 'application/json': { + schema: { + type: 'object', + required: ['firstName', 'lastName', 'email'], + properties: { + firstName: {type: 'string', example: 'John'}, + lastName: {type: 'string', example: 'Doe'}, + email: {type: 'string', example: 'john.doe@example.com'}, + company: {type: 'string', example: 'Acme Inc.'}, + phone: {type: 'string', example: '+1234567890'}, + billingAddress: { + type: 'object', + properties: { + line1: {type: 'string'}, + city: {type: 'string'}, + state: {type: 'string'}, + zip: {type: 'string'}, + country: {type: 'string', example: 'US'}, + }, + }, + }, + }, + }, + }, + }) + customerDto: TCustomer, + ): Promise { + return this.billingService.createCustomer(customerDto); + } + + /** + * Get a customer by external ID (Stripe customer ID, e.g. cus_XXXXX). + */ + @authorize({permissions: ['*']}) + @get(`${BASE}/customers/{customerId}`, { + summary: 'Get a customer by ID', + responses: { + '200': { + description: 'Customer object', + content: {'application/json': {schema: {type: 'object'}}}, + }, + }, + }) + async getCustomer( + @param.path.string('customerId') customerId: string, + ): Promise { + return this.billingService.getCustomers(customerId); + } + + /** + * Update an existing customer (email, phone, billing address, etc.). + * + * Example body: + * ```json + * { + * "email": "new.email@example.com", + * "billingAddress": { "city": "New York", "country": "US" } + * } + * ``` + */ + @authorize({permissions: ['*']}) + @put(`${BASE}/customers/{customerId}`, { + summary: 'Update a customer', + responses: { + '204': {description: 'Customer updated'}, + }, + }) + async updateCustomer( + @param.path.string('customerId') customerId: string, + @requestBody({ + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + firstName: {type: 'string'}, + lastName: {type: 'string'}, + email: {type: 'string'}, + company: {type: 'string'}, + phone: {type: 'string'}, + billingAddress: { + type: 'object', + properties: { + line1: {type: 'string'}, + city: {type: 'string'}, + state: {type: 'string'}, + zip: {type: 'string'}, + country: {type: 'string'}, + }, + }, + }, + }, + }, + }, + }) + customerDto: Partial, + ): Promise { + await this.billingService.updateCustomerById(customerId, customerDto); + } + + /** + * Delete (permanently remove) a customer from Stripe. + */ + @authorize({permissions: ['*']}) + @del(`${BASE}/customers/{customerId}`, { + summary: 'Delete a customer', + responses: { + '204': {description: 'Customer deleted'}, + }, + }) + async deleteCustomer( + @param.path.string('customerId') customerId: string, + ): Promise { + await this.billingService.deleteCustomer(customerId); + } + + // ------------------------------------------------------------------------- + // PAYMENT SOURCE + // ------------------------------------------------------------------------- + + /** + * Attach a payment method to a customer (via Stripe token). + * + * Example body: + * ```json + * { + * "customerId": "cus_XXXXX", + * "options": { "token": "tok_visa" } + * } + * ``` + */ + @authorize({permissions: ['*']}) + @post(`${BASE}/payment-sources`, { + summary: 'Create (attach) a payment source to a customer', + responses: { + '200': { + description: 'Payment source created', + content: {'application/json': {schema: {type: 'object'}}}, + }, + }, + }) + async createPaymentSource( + @requestBody({ + content: { + 'application/json': { + schema: { + type: 'object', + required: ['customerId'], + properties: { + customerId: {type: 'string', example: 'cus_UCXfGdNijvXTDf'}, + options: { + type: 'object', + properties: { + token: { + type: 'string', + example: 'tok_visa', + description: + 'Stripe test token (tok_visa, tok_mastercard etc.)', + }, + }, + }, + }, + }, + }, + }, + }) + paymentDto: TPaymentSource, + ): Promise { + return this.billingService.createPaymentSource(paymentDto); + } + + /** + * Retrieve a payment source (payment method) by its ID. + */ + @authorize({permissions: ['*']}) + @get(`${BASE}/payment-sources/{paymentSourceId}`, { + summary: 'Retrieve a payment source by ID', + responses: { + '200': { + description: 'Payment source object', + content: {'application/json': {schema: {type: 'object'}}}, + }, + }, + }) + async retrievePaymentSource( + @param.path.string('paymentSourceId') paymentSourceId: string, + ): Promise { + return this.billingService.retrievePaymentSource(paymentSourceId); + } + + /** + * Detach (delete) a payment source from a customer. + */ + @authorize({permissions: ['*']}) + @del(`${BASE}/payment-sources/{paymentSourceId}`, { + summary: 'Delete (detach) a payment source', + responses: { + '204': {description: 'Payment source deleted'}, + }, + }) + async deletePaymentSource( + @param.path.string('paymentSourceId') paymentSourceId: string, + ): Promise { + await this.billingService.deletePaymentSource(paymentSourceId); + } + + /** + * Apply a payment to an invoice (pay out-of-band or via payment source). + * + * Example body for bank transfer: + * ```json + * { + * "paymentMethod": "bank_transfer", + * "comment": "Paid via wire transfer" + * } + * ``` + * + * Example body for saved payment source: + * ```json + * { + * "paymentMethod": "payment_source", + * "paymentSourceId": "pm_XXXXX" + * } + * ``` + */ + @authorize({permissions: ['*']}) + @post(`${BASE}/invoices/{invoiceId}/apply-payment`, { + summary: 'Apply a payment to an invoice', + responses: { + '200': { + description: 'Invoice after payment applied', + content: {'application/json': {schema: {type: 'object'}}}, + }, + }, + }) + async applyPaymentSourceForInvoice( + @param.path.string('invoiceId') invoiceId: string, + @requestBody({ + content: { + 'application/json': { + schema: { + type: 'object', + required: ['paymentMethod'], + properties: { + paymentMethod: { + type: 'string', + enum: [ + 'cash', + 'check', + 'bank_transfer', + 'other', + 'custom', + 'payment_source', + ], + example: 'bank_transfer', + }, + paymentSourceId: { + type: 'string', + description: 'Required when paymentMethod is payment_source', + }, + amount: {type: 'number', example: 4999}, + referenceNumber: {type: 'string', example: 'REF-001'}, + comment: { + type: 'string', + example: 'Paid via wire transfer', + }, + }, + }, + }, + }, + }) + transaction: Transaction, + ): Promise { + return this.billingService.applyPaymentSourceForInvoice( + invoiceId, + transaction, + ); + } + + // ------------------------------------------------------------------------- + // INVOICE (direct / one-time) + // ------------------------------------------------------------------------- + + /** + * Create a one-time invoice for a customer with custom line items. + * + * Example body: + * ```json + * { + * "customerId": "cus_XXXXX", + * "currencyCode": "usd", + * "charges": [ + * { "amount": 5000, "description": "Setup fee" }, + * { "amount": 2000, "description": "Training" } + * ] + * } + * ``` + */ + @authorize({permissions: ['*']}) + @post(`${BASE}/invoices`, { + summary: 'Create a one-time invoice with custom line items', + responses: { + '200': { + description: 'Created invoice object', + content: {'application/json': {schema: {type: 'object'}}}, + }, + }, + }) + async createInvoice( + @requestBody({ + content: { + 'application/json': { + schema: { + type: 'object', + required: ['customerId', 'currencyCode', 'charges'], + properties: { + customerId: {type: 'string', example: 'cus_UCXfGdNijvXTDf'}, + currencyCode: {type: 'string', example: 'usd'}, + charges: { + type: 'array', + items: { + type: 'object', + required: ['amount', 'description'], + properties: { + amount: { + type: 'number', + example: 5000, + description: 'Amount in minor unit (cents)', + }, + description: {type: 'string', example: 'Setup fee'}, + }, + }, + }, + shippingAddress: { + type: 'object', + properties: { + line1: {type: 'string'}, + city: {type: 'string'}, + state: {type: 'string'}, + zip: {type: 'string'}, + country: {type: 'string'}, + }, + }, + }, + }, + }, + }, + }) + invoice: TInvoice, + ): Promise { + return this.billingService.createInvoice(invoice); + } + + /** + * Retrieve a specific invoice by ID. + */ + @authorize({permissions: ['*']}) + @get(`${BASE}/invoices/{invoiceId}`, { + summary: 'Retrieve an invoice by ID', + responses: { + '200': { + description: 'Invoice object', + content: {'application/json': {schema: {type: 'object'}}}, + }, + }, + }) + async retrieveInvoice( + @param.path.string('invoiceId') invoiceId: string, + ): Promise { + return this.billingService.retrieveInvoice(invoiceId); + } + + /** + * Update the shipping address of an existing invoice. + * + * Example body: + * ```json + * { + * "customerId": "cus_XXXXX", + * "currencyCode": "usd", + * "shippingAddress": { + * "firstName": "John", + * "lastName": "Doe", + * "line1": "123 Main St", + * "city": "San Francisco", + * "state": "CA", + * "zip": "94107", + * "country": "US" + * } + * } + * ``` + */ + @authorize({permissions: ['*']}) + @put(`${BASE}/invoices/{invoiceId}`, { + summary: 'Update an invoice (shipping address)', + responses: { + '200': { + description: 'Updated invoice object', + content: {'application/json': {schema: {type: 'object'}}}, + }, + }, + }) + async updateInvoice( + @param.path.string('invoiceId') invoiceId: string, + @requestBody({ + content: { + 'application/json': { + schema: { + type: 'object', + required: ['customerId', 'currencyCode'], + properties: { + customerId: {type: 'string'}, + currencyCode: {type: 'string'}, + shippingAddress: { + type: 'object', + properties: { + firstName: {type: 'string'}, + lastName: {type: 'string'}, + line1: {type: 'string'}, + city: {type: 'string'}, + state: {type: 'string'}, + zip: {type: 'string'}, + country: {type: 'string'}, + }, + }, + }, + }, + }, + }, + }) + invoice: Partial, + ): Promise { + return this.billingService.updateInvoice(invoiceId, invoice); + } + + /** + * Delete a draft invoice permanently. + * Only draft invoices can be deleted in Stripe. + */ + @authorize({permissions: ['*']}) + @del(`${BASE}/invoices/{invoiceId}`, { + summary: 'Delete a draft invoice', + responses: { + '204': {description: 'Invoice deleted'}, + }, + }) + async deleteInvoice( + @param.path.string('invoiceId') invoiceId: string, + ): Promise { + await this.billingService.deleteInvoice(invoiceId); + } + + /** + * Check whether an invoice has been paid. + */ + @authorize({permissions: ['*']}) + @get(`${BASE}/invoices/{invoiceId}/payment-status`, { + summary: 'Check if an invoice is paid', + responses: { + '200': { + description: 'Payment status', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + paid: {type: 'boolean', example: true}, + }, + }, + }, + }, + }, + }, + }) + async getPaymentStatus( + @param.path.string('invoiceId') invoiceId: string, + ): Promise<{paid: boolean}> { + const paid = await this.billingService.getPaymentStatus(invoiceId); + return {paid}; + } + + // ------------------------------------------------------------------------- + // INVOICE PDF + // ------------------------------------------------------------------------- + + /** + * Get PDF download URL for an invoice. + * + * Returns a temporary URL to download the invoice PDF. + * The URL is typically valid for a limited time and should be used immediately. + * + * @param invoiceId - The invoice ID (Stripe: in_XXXXX, ChargeBee: inv_XXXXX) + * @returns PDF information including download URL and metadata + * + * Example: + * GET /billing/invoices/in_1234567890/pdf + * + * Response: + * ```json + * { + * "invoiceId": "in_1234567890", + * "pdfUrl": "https://pay.stripe.com/invoice/acct_1ABC/in_1234567890/pdf", + * "generatedAt": "2026-05-04T12:00:00.000Z", + * "expiresAt": null + * } + * ``` + */ + @authorize({permissions: ['*']}) + @get(`${BASE}/invoices/{invoiceId}/pdf`, { + summary: 'Get PDF download URL for an invoice', + description: + 'Retrieves a temporary URL to download the invoice PDF. ' + + 'For Stripe, only finalized invoices have PDF URLs available. ' + + 'For ChargeBee, most invoice states support PDF generation.', + responses: { + '200': { + description: 'PDF information retrieved successfully', + content: { + 'application/json': { + schema: { + type: 'object', + required: ['invoiceId', 'pdfUrl', 'generatedAt'], + properties: { + invoiceId: { + type: 'string', + description: 'The invoice ID', + example: 'in_1234567890', + }, + pdfUrl: { + type: 'string', + description: 'Temporary download URL for the PDF', + example: + 'https://pay.stripe.com/invoice/acct_1ABC/in_1234567890/pdf', + }, + generatedAt: { + type: 'string', + format: 'date-time', + description: 'Timestamp when the PDF URL was generated', + example: '2026-05-04T12:00:00.000Z', + }, + expiresAt: { + type: 'string', + format: 'date-time', + description: + 'Timestamp when the PDF URL expires (if provided by the provider)', + example: '2026-05-04T12:30:00.000Z', + nullable: true, + }, + }, + }, + }, + }, + }, + '404': { + description: 'Invoice not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + error: { + type: 'object', + properties: { + statusCode: {type: 'number', example: 404}, + message: { + type: 'string', + example: 'Invoice not found: in_1234567890', + }, + }, + }, + }, + }, + }, + }, + }, + '400': { + description: 'PDF URL not available', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + error: { + type: 'object', + properties: { + statusCode: {type: 'number', example: 400}, + message: { + type: 'string', + example: + 'PDF URL not available for invoice in_123. The invoice may be in draft status or not finalized.', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }) + @response(200, { + description: 'PDF information retrieved successfully', + }) + async getInvoicePdf( + @param.path.string('invoiceId') invoiceId: string, + ): Promise { + const pdfInfo: TInvoicePdf = + await this.billingService.getInvoicePdf(invoiceId); + return pdfInfo; + } + + // ------------------------------------------------------------------------- + // INVOICE PAYMENT DETAILS + // ------------------------------------------------------------------------- + + /** + * Get payment details for an invoice. + * + * Returns information about the payment method used, payment amount, + * payment date, and transaction status. + * + * Example response: + * ```json + * { + * "invoiceId": "in_1234567890", + * "paymentMethod": { + * "type": "card", + * "card": { + * "brand": "visa", + * "last4": "4242", + * "expMonth": 12, + * "expYear": 2025 + * } + * }, + * "paymentDate": 1714834567, + * "amount": 5000, + * "currency": "usd", + * "status": "succeeded" + * } + * ``` + */ + @authorize({permissions: ['*']}) + @get(`${BASE}/invoices/{invoiceId}/payment-details`, { + summary: 'Get payment details for an invoice', + description: + 'Retrieves payment method details, payment amount, status, and ' + + 'transaction information for a specific invoice.', + responses: { + '200': { + description: 'Payment details retrieved successfully', + content: { + 'application/json': { + schema: { + type: 'object', + required: ['invoiceId', 'paymentMethod'], + properties: { + invoiceId: {type: 'string', example: 'in_1234567890'}, + paymentMethod: { + type: 'object', + properties: { + type: {type: 'string', example: 'card'}, + card: { + type: 'object', + properties: { + brand: {type: 'string', example: 'visa'}, + last4: {type: 'string', example: '4242'}, + expMonth: {type: 'number', example: 12}, + expYear: {type: 'number', example: 2025}, + funding: {type: 'string', example: 'credit'}, + }, + }, + }, + }, + paymentDate: {type: 'number', example: 1714834567}, + amount: {type: 'number', example: 5000}, + currency: {type: 'string', example: 'usd'}, + status: {type: 'string', example: 'succeeded'}, + }, + }, + }, + }, + }, + '404': { + description: 'Invoice not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + error: { + type: 'object', + properties: { + statusCode: {type: 'number', example: 404}, + message: { + type: 'string', + example: 'Invoice not found: in_1234567890', + }, + }, + }, + }, + }, + }, + }, + }, + '400': { + description: 'No payment details available', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + error: { + type: 'object', + properties: { + statusCode: {type: 'number', example: 400}, + message: { + type: 'string', + example: + 'No payment found for invoice. The invoice may not be paid yet.', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }) + @response(200, {description: 'Payment details retrieved successfully'}) + async getInvoicePaymentDetails( + @param.path.string('invoiceId') invoiceId: string, + ): Promise { + return this.billingService.getInvoicePaymentDetails(invoiceId); + } + + // ------------------------------------------------------------------------- + // PAYMENT INTENT OPERATIONS + // ------------------------------------------------------------------------- + + /** + * Get payment intent details by ID. + * + * Returns comprehensive payment tracking information including status, + * payment method, amount, and transaction metadata. + * + * Example response: + * ```json + * { + * "id": "pi_1234567890", + * "amount": 5000, + * "currency": "usd", + * "status": "succeeded", + * "created": 1714834567, + * "customer": "cus_XXXXX", + * "paymentMethod": { + * "type": "card", + * "card": { + * "brand": "visa", + * "last4": "4242" + * } + * }, + * "description": "Payment for order #12345" + * } + * ``` + */ + @authorize({permissions: ['*']}) + @get(`${BASE}/payment-intents/{paymentIntentId}`, { + summary: 'Get payment intent details', + description: + 'Retrieves detailed information about a payment intent including ' + + 'status, payment method, amount, and transaction metadata. ' + + 'Useful for payment tracking and webhook handling.', + responses: { + '200': { + description: 'Payment intent retrieved successfully', + content: { + 'application/json': { + schema: { + type: 'object', + required: ['id', 'amount', 'currency', 'status', 'created'], + properties: { + id: {type: 'string', example: 'pi_1234567890'}, + amount: {type: 'number', example: 5000}, + currency: {type: 'string', example: 'usd'}, + status: {type: 'string', example: 'succeeded'}, + created: {type: 'number', example: 1714834567}, + customer: {type: 'string', example: 'cus_XXXXX'}, + paymentMethod: { + type: 'object', + properties: { + type: {type: 'string', example: 'card'}, + card: { + type: 'object', + properties: { + brand: {type: 'string', example: 'visa'}, + last4: {type: 'string', example: '4242'}, + }, + }, + }, + }, + description: { + type: 'string', + example: 'Payment for order #12345', + }, + metadata: { + type: 'object', + additionalProperties: {type: 'string'}, + }, + }, + }, + }, + }, + }, + '404': { + description: 'Payment intent not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + error: { + type: 'object', + properties: { + statusCode: {type: 'number', example: 404}, + message: { + type: 'string', + example: 'Payment intent not found: pi_1234567890', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }) + @response(200, {description: 'Payment intent retrieved successfully'}) + async getPaymentIntent( + @param.path.string('paymentIntentId') paymentIntentId: string, + ): Promise { + return this.billingService.getPaymentIntent(paymentIntentId); + } +} diff --git a/services/subscription-service/src/controllers/billing-subscription.controller.ts b/services/subscription-service/src/controllers/billing-subscription.controller.ts new file mode 100644 index 0000000..8ddf15a --- /dev/null +++ b/services/subscription-service/src/controllers/billing-subscription.controller.ts @@ -0,0 +1,508 @@ +import {inject} from '@loopback/core'; +import {del, get, param, post, put, requestBody} from '@loopback/rest'; +import {authorize} from 'loopback4-authorization'; +import { + BillingComponentBindings, + CollectionMethod, + ISubscriptionService, + ProrationBehavior, + RecurringInterval, + TInvoicePrice, + TPrice, + TProduct, + TSubscriptionCreate, + TSubscriptionResult, + TSubscriptionUpdate, +} from 'loopback4-billing'; + +const BASE = '/billing'; + +/** + * Sandbox controller that exercises the full subscription lifecycle + * implemented in loopback4-billing. + * + * Every endpoint injects {@link ISubscriptionService} via + * {@link BillingComponentBindings.SubscriptionProvider} — the new + * provider-agnostic binding. Swap the provider in application.ts + * (ChargeBee ↔ Stripe) without touching this controller. + */ +export class BillingSubscriptionController { + constructor( + @inject(BillingComponentBindings.SDKProvider) + private readonly billingService: ISubscriptionService, + ) {} + + // ------------------------------------------------------------------------- + // PRODUCT + // ------------------------------------------------------------------------- + + /** + * Create a new product (Chargebee: Item / Stripe: Product). + * + * Example body: + * ```json + * { + * "name": "Enterprise Plan", + * "description": "Full-featured tier", + * "metadata": { "item_family_id": "default" } + * } + * ``` + */ + @authorize({permissions: ['*']}) + @post(`${BASE}/products`, { + summary: 'Create a billing product (Item / Product)', + responses: { + '200': { + description: 'External product ID', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + productId: {type: 'string', example: 'cbdemo_enterprise'}, + }, + required: ['productId'], + }, + }, + }, + }, + }, + }) + async createProduct( + @requestBody({ + content: { + 'application/json': { + schema: { + type: 'object', + required: ['name'], + properties: { + name: {type: 'string'}, + description: {type: 'string'}, + metadata: {type: 'object'}, + }, + }, + }, + }, + }) + product: TProduct, + ): Promise<{productId: string}> { + const productId = await this.billingService.createProduct(product); + return {productId}; + } + + /** + * Check whether a product/item exists and is still active. + */ + @authorize({permissions: ['*']}) + @get(`${BASE}/products/{productId}/exists`, { + summary: 'Check if a billing product is active', + responses: { + '200': { + description: 'Existence flag', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + exists: {type: 'boolean', example: true}, + }, + required: ['exists'], + }, + }, + }, + }, + }, + }) + async checkProductExists( + @param.path.string('productId') productId: string, + ): Promise<{exists: boolean}> { + const exists = await this.billingService.checkProductExists(productId); + return {exists}; + } + + // ------------------------------------------------------------------------- + // PRICE / PLAN + // ------------------------------------------------------------------------- + + /** + * Create a recurring price (Chargebee: ItemPrice / Stripe: Price). + * + * Example body: + * ```json + * { + * "currency": "usd", + * "unitAmount": 4999, + * "product": "", + * "recurring": { "interval": "month", "intervalCount": 1 } + * } + * ``` + */ + @authorize({permissions: ['*']}) + @post(`${BASE}/prices`, { + summary: 'Create a recurring price (ItemPrice / Price)', + responses: { + '200': { + description: 'Created price / item-price object', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + id: {type: 'string', example: 'cbdemo_enterprise-USD-monthly'}, + currency: {type: 'string', example: 'usd'}, + unitAmount: {type: 'number', example: 4999}, + product: {type: 'string', example: 'cbdemo_enterprise'}, + active: {type: 'boolean', example: true}, + recurring: { + type: 'object', + properties: { + interval: {type: 'string', example: 'month'}, + intervalCount: {type: 'number', example: 1}, + }, + }, + }, + }, + }, + }, + }, + }, + }) + async createPrice( + @requestBody({ + content: { + 'application/json': { + schema: { + type: 'object', + required: ['currency', 'unitAmount', 'product'], + properties: { + id: {type: 'string'}, + currency: {type: 'string', example: 'usd'}, + unitAmount: {type: 'number', example: 4999}, + product: {type: 'string'}, + recurring: { + type: 'object', + properties: { + interval: { + type: 'string', + enum: Object.values(RecurringInterval), + example: RecurringInterval.MONTH, + }, + intervalCount: {type: 'number', example: 1}, + }, + }, + metadata: {type: 'object'}, + }, + }, + }, + }, + }) + price: TPrice, + ): Promise { + return this.billingService.createPrice(price); + } + + // ------------------------------------------------------------------------- + // SUBSCRIPTION + // ------------------------------------------------------------------------- + + /** + * Create a new recurring subscription. + * + * Example body: + * ```json + * { + * "customerId": "", + * "priceRefId": "", + * "collectionMethod": "charge_automatically" + * } + * ``` + */ + @authorize({permissions: ['*']}) + @post(`${BASE}/subscriptions`, { + summary: 'Create a new subscription', + responses: { + '200': { + description: 'Newly created subscription ID', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + subscriptionId: {type: 'string', example: 'AzZlGKSfBGHDPJkp'}, + }, + required: ['subscriptionId'], + }, + }, + }, + }, + }, + }) + async createSubscription( + @requestBody({ + content: { + 'application/json': { + schema: { + type: 'object', + required: ['customerId', 'priceRefId', 'collectionMethod'], + properties: { + customerId: {type: 'string'}, + priceRefId: {type: 'string'}, + collectionMethod: { + type: 'string', + enum: Object.values(CollectionMethod), + example: CollectionMethod.CHARGE_AUTOMATICALLY, + }, + daysUntilDue: {type: 'number', example: 30}, + }, + }, + }, + }, + }) + subscription: TSubscriptionCreate, + ): Promise<{subscriptionId: string}> { + const subscriptionId = + await this.billingService.createSubscription(subscription); + return {subscriptionId}; + } + + /** + * Get the current state of an existing subscription. + */ + @authorize({permissions: ['*']}) + @get(`${BASE}/subscriptions/{subscriptionId}`, { + summary: 'Get a subscription by ID', + responses: { + '200': { + description: 'Subscription object', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + id: {type: 'string', example: 'AzZlGKSfBGHDPJkp'}, + status: {type: 'string', example: 'active'}, + customerId: {type: 'string', example: 'cust_001'}, + currentPeriodStart: {type: 'number', example: 1700000000}, + currentPeriodEnd: {type: 'number', example: 1702678400}, + cancelAtPeriodEnd: {type: 'boolean', example: false}, + }, + required: ['id', 'status', 'customerId'], + }, + }, + }, + }, + }, + }) + async getSubscription( + @param.path.string('subscriptionId') subscriptionId: string, + ): Promise { + return this.billingService.getSubscription(subscriptionId); + } + + /** + * Upgrade or downgrade an existing subscription. + * + * Example body: + * ```json + * { + * "priceRefId": "", + * "prorationBehavior": "create_prorations" + * } + * ``` + */ + @authorize({permissions: ['*']}) + @put(`${BASE}/subscriptions/{subscriptionId}`, { + summary: 'Upgrade / downgrade a subscription (plan change)', + responses: { + '200': { + description: 'Updated subscription object', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + id: {type: 'string', example: 'AzZlGKSfBGHDPJkp'}, + status: {type: 'string', example: 'active'}, + customerId: {type: 'string', example: 'cust_001'}, + currentPeriodStart: {type: 'number', example: 1700000000}, + currentPeriodEnd: {type: 'number', example: 1702678400}, + cancelAtPeriodEnd: {type: 'boolean', example: false}, + }, + required: ['id', 'status', 'customerId'], + }, + }, + }, + }, + }, + }) + async updateSubscription( + @param.path.string('subscriptionId') subscriptionId: string, + @requestBody({ + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + priceRefId: {type: 'string'}, + prorationBehavior: { + type: 'string', + enum: Object.values(ProrationBehavior), + example: ProrationBehavior.CREATE_PRORATIONS, + }, + }, + }, + }, + }, + }) + updates: TSubscriptionUpdate, + ): Promise { + return this.billingService.updateSubscription(subscriptionId, updates); + } + + /** + * Cancel a subscription immediately with proration. + */ + @authorize({permissions: ['*']}) + @del(`${BASE}/subscriptions/{subscriptionId}`, { + summary: 'Cancel a subscription immediately', + responses: { + '204': {description: 'Subscription cancelled'}, + }, + }) + async cancelSubscription( + @param.path.string('subscriptionId') subscriptionId: string, + ): Promise { + await this.billingService.cancelSubscription(subscriptionId); + } + + /** + * Pause an active subscription. + */ + @authorize({permissions: ['*']}) + @post(`${BASE}/subscriptions/{subscriptionId}/pause`, { + summary: 'Pause a subscription', + responses: { + '200': { + description: 'Subscription paused', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + success: {type: 'boolean', example: true}, + }, + required: ['success'], + }, + }, + }, + }, + }, + }) + async pauseSubscription( + @param.path.string('subscriptionId') subscriptionId: string, + ): Promise<{success: boolean}> { + await this.billingService.pauseSubscription(subscriptionId); + return {success: true}; + } + + /** + * Resume a paused subscription. + */ + @authorize({permissions: ['*']}) + @post(`${BASE}/subscriptions/{subscriptionId}/resume`, { + summary: 'Resume a paused subscription', + responses: { + '200': { + description: 'Subscription resumed', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + success: {type: 'boolean', example: true}, + }, + required: ['success'], + }, + }, + }, + }, + }, + }) + async resumeSubscription( + @param.path.string('subscriptionId') subscriptionId: string, + ): Promise<{success: boolean}> { + await this.billingService.resumeSubscription(subscriptionId); + return {success: true}; + } + + // ------------------------------------------------------------------------- + // INVOICE + // ------------------------------------------------------------------------- + + /** + * Get detailed price breakdown (total, tax, amount excluding tax) for an invoice. + */ + @authorize({permissions: ['*']}) + @get(`${BASE}/invoices/{invoiceId}/price-details`, { + summary: 'Get invoice price details (total, tax, subtotal)', + responses: { + '200': { + description: 'Invoice price breakdown', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + currency: {type: 'string', example: 'usd'}, + totalAmount: {type: 'number', example: 5499}, + taxAmount: {type: 'number', example: 500}, + amountExcludingTax: {type: 'number', example: 4999}, + }, + required: [ + 'currency', + 'totalAmount', + 'taxAmount', + 'amountExcludingTax', + ], + }, + }, + }, + }, + }, + }) + async getInvoicePriceDetails( + @param.path.string('invoiceId') invoiceId: string, + ): Promise { + return this.billingService.getInvoicePriceDetails(invoiceId); + } + + /** + * Send the payment link for a given invoice to the customer. + */ + @authorize({permissions: ['*']}) + @post(`${BASE}/invoices/{invoiceId}/send-payment-link`, { + summary: 'Send hosted payment link for an invoice', + responses: { + '200': { + description: 'Payment link sent', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + success: {type: 'boolean', example: true}, + }, + required: ['success'], + }, + }, + }, + }, + }, + }) + async sendPaymentLink( + @param.path.string('invoiceId') invoiceId: string, + ): Promise<{success: boolean}> { + await this.billingService.sendPaymentLink(invoiceId); + return {success: true}; + } +} diff --git a/services/subscription-service/src/controllers/billing-webhook.controller.ts b/services/subscription-service/src/controllers/billing-webhook.controller.ts new file mode 100644 index 0000000..af5f757 --- /dev/null +++ b/services/subscription-service/src/controllers/billing-webhook.controller.ts @@ -0,0 +1,379 @@ +import {inject} from '@loopback/core'; +import {post, requestBody, HttpErrors} from '@loopback/rest'; +import {authorize} from 'loopback4-authorization'; +import { + BillingComponentBindings, + ISubscriptionService, + TSubscriptionResult, +} from 'loopback4-billing'; +import {intercept} from '@loopback/core'; +import {repository} from '@loopback/repository'; +import {WEBHOOK_VERIFIER} from '../keys'; +import {InvoiceRepository} from '../repositories'; +import {BillingCustomerRepository} from '../repositories/billing-customer.repository'; +import {IContent, IPayload, IWebhookPayload, IWebhookContent} from '../types'; + +/** + * Chargebee webhook event types handled by this controller. + * Extend this enum as your integration needs grow. + */ +enum ChargebeeEvent { + SUBSCRIPTION_CREATED = 'subscription_created', + SUBSCRIPTION_ACTIVATED = 'subscription_activated', + SUBSCRIPTION_CHANGED = 'subscription_changed', + SUBSCRIPTION_CANCELLED = 'subscription_cancelled', + SUBSCRIPTION_PAUSED = 'subscription_paused', + SUBSCRIPTION_RESUMED = 'subscription_resumed', + SUBSCRIPTION_RENEWED = 'subscription_renewed', + PAYMENT_SUCCEEDED = 'payment_succeeded', + PAYMENT_FAILED = 'payment_failed', + INVOICE_GENERATED = 'invoice_generated', +} + +/** + * Webhook controller for Chargebee billing events. + * + * Exposes POST /webhooks/billing-payment — the URL you register in + * Chargebee Settings → API Keys → Webhooks. + * + * Follows Approach C: injects SubscriptionProvider directly as + * ISubscriptionService so no BillingProvider proxy is involved. + * + * To add Chargebee webhook signature verification, bind a custom + * interceptor to BILLING_WEBHOOK_VERIFIER in application.ts. + */ +export class BillingWebhookController { + constructor( + @inject(BillingComponentBindings.SDKProvider) + private readonly billingService: ISubscriptionService, + @repository(BillingCustomerRepository) + public billingCustomerRepository: BillingCustomerRepository, + @repository(InvoiceRepository) + public invoiceRepository: InvoiceRepository, + ) {} + + @authorize({permissions: ['*']}) + @intercept(WEBHOOK_VERIFIER) + @post('/webhooks/billing-payment', { + summary: 'Chargebee webhook receiver', + description: + 'Register this URL in Chargebee Settings → API Keys → Webhooks. ' + + 'Handles subscription and payment lifecycle events.', + responses: { + '200': { + description: 'Event acknowledged', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + received: {type: 'boolean', example: true}, + event: {type: 'string', example: 'subscription_activated'}, + }, + }, + }, + }, + }, + }, + }) + async handleWebhook( + @requestBody({ + description: 'Chargebee webhook payload', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + // eslint-disable-next-line @typescript-eslint/naming-convention + event_type: {type: 'string'}, + content: {type: 'object'}, + }, + }, + }, + }, + }) + payload: IWebhookPayload, + ): Promise<{received: boolean; event: string}> { + const eventType: string = payload?.event_type ?? ''; + const content = payload?.content ?? {}; + + console.log(`[BillingWebhook] Received event: ${eventType}`); + + try { + switch (eventType) { + case ChargebeeEvent.SUBSCRIPTION_CREATED: + await this.onSubscriptionCreated(content); + break; + + case ChargebeeEvent.SUBSCRIPTION_ACTIVATED: + await this.onSubscriptionActivated(content); + break; + + case ChargebeeEvent.SUBSCRIPTION_CHANGED: + await this.onSubscriptionChanged(content); + break; + + case ChargebeeEvent.SUBSCRIPTION_CANCELLED: + await this.onSubscriptionCancelled(content); + break; + + case ChargebeeEvent.SUBSCRIPTION_PAUSED: + await this.onSubscriptionPaused(content); + break; + + case ChargebeeEvent.SUBSCRIPTION_RESUMED: + await this.onSubscriptionResumed(content); + break; + + case ChargebeeEvent.SUBSCRIPTION_RENEWED: + await this.onSubscriptionRenewed(content); + break; + + case ChargebeeEvent.PAYMENT_SUCCEEDED: + await this.onPaymentSucceeded(content); + break; + + case ChargebeeEvent.PAYMENT_FAILED: + await this.onPaymentFailed(content); + break; + + case ChargebeeEvent.INVOICE_GENERATED: + await this.onInvoiceGenerated(content); + break; + + default: + console.log(`[BillingWebhook] Unhandled event type: ${eventType}`); + } + } catch (err) { + console.error(`[BillingWebhook] Error handling ${eventType}:`, err); + // Return 200 to Chargebee regardless — prevents unnecessary retries + // for business-logic errors; log for manual review. + } + + return {received: true, event: eventType}; + } + + /** + * Legacy webhook handler for backward compatibility. + * Handles payment status updates. + */ + @authorize({ + permissions: ['*'], + }) + @intercept(WEBHOOK_VERIFIER) + @post('/webhooks/billing-payment/legacy') + async handleWebhookLegacy(@requestBody() payload: IPayload): Promise { + const content = payload.content; + await this.handlePayment(content); + } + + // --------------------------------------------------------------------------- + // Subscription lifecycle handlers + // --------------------------------------------------------------------------- + + /** + * Fired when a subscription is first created in Chargebee. + * The subscription may be in `in_trial` or `active` state. + */ + private async onSubscriptionCreated(content: IWebhookContent): Promise { + const subscription: TSubscriptionResult = this.mapChargebeeSubscription( + content.subscription, + ); + console.log( + `[BillingWebhook] Subscription created: ${subscription.id} ` + + `status=${subscription.status} customer=${subscription.customerId}`, + ); + // TODO: Update your internal subscription record, send welcome email, etc. + } + + /** + * Fired when a subscription transitions from trial/pending to active. + * This is the event to trigger provisioning in your platform. + * + * Calls billingService.getSubscription() to verify the current state + * directly from Chargebee before acting on it. + */ + private async onSubscriptionActivated( + content: IWebhookContent, + ): Promise { + const subscriptionId: string = content.subscription?.id ?? ''; + // Verify the subscription state directly from Chargebee via the library + const subscription: TSubscriptionResult = + await this.billingService.getSubscription(subscriptionId); + console.log( + `[BillingWebhook] Subscription activated (verified): ${subscription.id} ` + + `status=${subscription.status} customer=${subscription.customerId} ` + + `periodEnd=${subscription.currentPeriodEnd}`, + ); + // TODO: Mark subscription as ACTIVE in your DB, provision tenant resources. + } + + /** + * Fired when a subscription plan, quantity or add-ons change. + * + * Calls billingService.getSubscription() to get the updated plan details + * directly from Chargebee. + */ + private async onSubscriptionChanged(content: IWebhookContent): Promise { + const subscriptionId: string = content.subscription?.id ?? ''; + // Fetch updated subscription from Chargebee via the library + const subscription: TSubscriptionResult = + await this.billingService.getSubscription(subscriptionId); + console.log( + `[BillingWebhook] Subscription changed (verified): ${subscription.id} ` + + `newStatus=${subscription.status}`, + ); + // TODO: Sync the new plan/price to your internal subscription record. + } + + /** + * Fired when a subscription is cancelled (immediately or at period end). + */ + private async onSubscriptionCancelled( + content: IWebhookContent, + ): Promise { + const subscription: TSubscriptionResult = this.mapChargebeeSubscription( + content.subscription, + ); + console.log( + `[BillingWebhook] Subscription cancelled: ${subscription.id} ` + + `cancelAtPeriodEnd=${subscription.cancelAtPeriodEnd}`, + ); + // TODO: Update status to CANCELLED, revoke tenant access if needed. + } + + /** + * Fired when a subscription enters the paused state. + */ + private async onSubscriptionPaused(content: IWebhookContent): Promise { + const subscription: TSubscriptionResult = this.mapChargebeeSubscription( + content.subscription, + ); + console.log(`[BillingWebhook] Subscription paused: ${subscription.id}`); + // TODO: Suspend tenant access, send notification. + } + + /** + * Fired when a paused subscription is resumed. + */ + private async onSubscriptionResumed(content: IWebhookContent): Promise { + const subscription: TSubscriptionResult = this.mapChargebeeSubscription( + content.subscription, + ); + console.log(`[BillingWebhook] Subscription resumed: ${subscription.id}`); + // TODO: Restore tenant access, send notification. + } + + /** + * Fired at the start of each new billing period (renewal). + */ + private async onSubscriptionRenewed(content: IWebhookContent): Promise { + const subscription: TSubscriptionResult = this.mapChargebeeSubscription( + content.subscription, + ); + console.log( + `[BillingWebhook] Subscription renewed: ${subscription.id} ` + + `nextRenewal=${subscription.currentPeriodEnd}`, + ); + // TODO: Update next billing date in your DB. + } + + // --------------------------------------------------------------------------- + // Payment handlers + // --------------------------------------------------------------------------- + + /** + * Fired when a payment is successfully collected. + * + * Calls billingService.getInvoicePriceDetails() to get the full + * price breakdown (total, tax, amount excluding tax) from Chargebee. + */ + private async onPaymentSucceeded(content: IWebhookContent): Promise { + const invoiceId: string = content.invoice?.id ?? ''; + const transaction = content.transaction; + // Fetch full price breakdown via the library + const priceDetails = invoiceId + ? await this.billingService.getInvoicePriceDetails(invoiceId) + : null; + console.log( + `[BillingWebhook] Payment succeeded — invoice=${invoiceId} ` + + `amount=${transaction?.amount ?? 'N/A'} ` + + `total=${priceDetails?.totalAmount ?? 'N/A'} ` + + `tax=${priceDetails?.taxAmount ?? 'N/A'} ` + + `currency=${priceDetails?.currency ?? content.invoice?.currency_code ?? 'N/A'}`, + ); + // TODO: Mark invoice as paid, update subscription record, trigger provisioning. + } + + /** + * Fired when a payment attempt fails (card decline, insufficient funds, etc.). + * Chargebee will retry automatically per your dunning settings. + */ + private async onPaymentFailed(content: IWebhookContent): Promise { + const invoice = content.invoice; + const transaction = content.transaction; + console.log( + `[BillingWebhook] Payment FAILED — invoice=${invoice?.id ?? 'N/A'} ` + + `reason=${transaction?.error_text ?? 'unknown'}`, + ); + // TODO: Notify tenant, log for dunning review, update subscription status. + if (!invoice?.id) { + throw new HttpErrors.UnprocessableEntity( + '[BillingWebhook] Payment failed event missing invoice ID', + ); + } + } + + // --------------------------------------------------------------------------- + // Invoice handlers + // --------------------------------------------------------------------------- + + /** + * Fired when Chargebee generates a new invoice. + * Use this to record the invoice in your system. + */ + private async onInvoiceGenerated(content: IWebhookContent): Promise { + const invoice = content.invoice; + console.log( + `[BillingWebhook] Invoice generated: ${invoice?.id ?? 'N/A'} ` + + `total=${invoice?.total ?? 0} ${invoice?.currency_code ?? ''}`, + ); + // TODO: Create an invoice record in your DB. + } + + // --------------------------------------------------------------------------- + // Helper + // --------------------------------------------------------------------------- + + /** + * Maps a raw Chargebee subscription object from a webhook payload to the + * provider-agnostic TSubscriptionResult. + * + * Webhook payloads use the same field names as the Chargebee REST API, so + * this mirrors ChargebeeSubscriptionAdapter.adaptToModel(). + */ + private mapChargebeeSubscription( + raw: IWebhookContent['subscription'], + ): TSubscriptionResult { + return { + id: raw?.id ?? '', + status: raw?.status ?? '', + customerId: raw?.customer_id ?? '', + currentPeriodStart: raw?.current_term_start, + currentPeriodEnd: raw?.current_term_end, + cancelAtPeriodEnd: raw?.cancel_at_period_end ?? false, + }; + } + + /** + * Legacy payment handler for backward compatibility. + */ + private async handlePayment(content: IContent): Promise { + const invoice = await this.invoiceRepository.find({ + where: {invoiceId: content.invoice.id}, + }); + await this.invoiceRepository.updateById(invoice[0].id, { + invoiceStatus: content.invoice.status, + }); + } +} diff --git a/services/subscription-service/src/controllers/index.ts b/services/subscription-service/src/controllers/index.ts index 4ebd610..f33a320 100644 --- a/services/subscription-service/src/controllers/index.ts +++ b/services/subscription-service/src/controllers/index.ts @@ -10,3 +10,10 @@ export * from './plan-subscription.controller'; export * from './plan-sizes.controller'; export * from './plan-features.controller'; export * from './subscription-invoice.controller'; +export * from './billing-service.controller'; +export * from './billing-subscription.controller'; +export * from './billing-webhook.controller'; +export * from './webhook.controller'; +export * from './billing-customer.controller'; +export * from './billing-invoice.controller'; +export * from './billing-payment-source.controller'; diff --git a/services/subscription-service/src/types.ts b/services/subscription-service/src/types.ts index 78a3ddf..ac07327 100644 --- a/services/subscription-service/src/types.ts +++ b/services/subscription-service/src/types.ts @@ -46,3 +46,61 @@ export interface ISubscriptionServiceConfig extends IServiceConfig { useCustomSequence: boolean; useSequelize?: boolean; } + +/** + * Webhook payload structure from billing providers (Chargebee/Stripe). + */ +export interface IWebhookPayload { + // eslint-disable-next-line @typescript-eslint/naming-convention + event_type: string; + content: IWebhookContent; + [key: string]: string | number | boolean | object | null | undefined; +} + +/** + * Webhook content structure containing subscription, invoice, and transaction data. + */ +export interface IWebhookContent { + subscription?: IWebhookSubscription; + invoice?: IWebhookInvoice; + transaction?: IWebhookTransaction; + [key: string]: string | number | boolean | object | null | undefined; +} + +/** + * Subscription data from webhook payload. + */ +export interface IWebhookSubscription { + id: string; + status: string; + // eslint-disable-next-line @typescript-eslint/naming-convention + customer_id: string; + // eslint-disable-next-line @typescript-eslint/naming-convention + current_term_start?: number; + // eslint-disable-next-line @typescript-eslint/naming-convention + current_term_end?: number; + // eslint-disable-next-line @typescript-eslint/naming-convention + cancel_at_period_end?: boolean; + [key: string]: string | number | boolean | object | null | undefined; +} + +/** + * Invoice data from webhook payload. + */ +export interface IWebhookInvoice { + id: string; + // eslint-disable-next-line @typescript-eslint/naming-convention + currency_code?: string; + total?: number; + [key: string]: string | number | boolean | object | null | undefined; +} + +/** + * Transaction data from webhook payload. + */ +export interface IWebhookTransaction { + amount?: number; + // eslint-disable-next-line @typescript-eslint/naming-convention + error_text?: string; + [key: string]: string | number | boolean | object | null | undefined; +} From d9812c3a54daf6e797e95ff9fef7d7b78690eb48 Mon Sep 17 00:00:00 2001 From: Sourav Kashyap Date: Wed, 20 May 2026 12:37:41 +0530 Subject: [PATCH 2/6] feat(billing): fix sonar issue --- .github/workflows/trivy.yml | 6 +- package-lock.json | 361 ++++++++++++++++++ .../subscription-service/src/component.ts | 2 +- .../controllers/billing-service.controller.ts | 7 +- .../controllers/billing-webhook.controller.ts | 4 +- services/subscription-service/src/types.ts | 18 +- 6 files changed, 384 insertions(+), 14 deletions(-) diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index b0278b8..f573de9 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -24,6 +24,6 @@ jobs: - name: Run Trivy vulnerability scanner in repo mode uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0 with: - scan-type: "fs" - scan-ref: "${{ github.workspace }}" - trivy-config: "${{ github.workspace }}/trivy.yaml" \ No newline at end of file + scan-type: 'fs' + scan-ref: '${{ github.workspace }}' + trivy-config: '${{ github.workspace }}/trivy.yaml' diff --git a/package-lock.json b/package-lock.json index 0cffc61..22c3d3b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2983,6 +2983,21 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, + "node_modules/@loopback/rest/node_modules/qs": { + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/@loopback/sequelize": { "version": "0.8.10", "resolved": "https://registry.npmjs.org/@loopback/sequelize/-/sequelize-0.8.10.tgz", @@ -7038,6 +7053,18 @@ "url": "https://opencollective.com/ioredis" } }, + "node_modules/bullmq/node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/byte-size": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-8.1.1.tgz", @@ -10191,6 +10218,21 @@ "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", "license": "MIT" }, + "node_modules/express/node_modules/qs": { + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/express/node_modules/raw-body": { "version": "2.5.3", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", @@ -13728,6 +13770,19 @@ } } }, + "node_modules/lerna/node_modules/ejs": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-5.0.1.tgz", + "integrity": "sha512-COqBPFMxuPTPspXl2DkVYaDS3HtrD1GpzOGkNTJ1IYkifq/r9h8SVEFrjA3D9/VJGOEoMQcrlhpntcSUrM8k6A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.12.18" + } + }, "node_modules/lerna/node_modules/execa": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz", @@ -13833,6 +13888,16 @@ "node": "*" } }, + "node_modules/lerna/node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/lerna/node_modules/mute-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", @@ -13843,6 +13908,223 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/lerna/node_modules/nx": { + "version": "22.7.2", + "resolved": "https://registry.npmjs.org/nx/-/nx-22.7.2.tgz", + "integrity": "sha512-Gh7gGO1t/TvgbKuVJMYWbxUwZC+E+PuRRVUeoOeVe82yEvBNl40EKiVHIbbi6GID0s9Zwzflo07UrKGLoDSVGw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@emnapi/core": "1.4.5", + "@emnapi/runtime": "1.4.5", + "@emnapi/wasi-threads": "1.0.4", + "@jest/diff-sequences": "30.0.1", + "@napi-rs/wasm-runtime": "0.2.4", + "@tybys/wasm-util": "0.9.0", + "@yarnpkg/lockfile": "1.1.0", + "@zkochan/js-yaml": "0.0.7", + "ansi-colors": "4.1.3", + "ansi-regex": "5.0.1", + "ansi-styles": "4.3.0", + "argparse": "2.0.1", + "asynckit": "0.4.0", + "axios": "1.16.0", + "balanced-match": "4.0.3", + "base64-js": "1.5.1", + "bl": "4.1.0", + "brace-expansion": "5.0.5", + "buffer": "5.7.1", + "call-bind-apply-helpers": "1.0.2", + "chalk": "4.1.2", + "cli-cursor": "3.1.0", + "cli-spinners": "2.6.1", + "cliui": "8.0.1", + "clone": "1.0.4", + "color-convert": "2.0.1", + "color-name": "1.1.4", + "combined-stream": "1.0.8", + "defaults": "1.0.4", + "define-lazy-prop": "2.0.0", + "delayed-stream": "1.0.0", + "dotenv": "16.4.7", + "dotenv-expand": "12.0.3", + "dunder-proto": "1.0.1", + "ejs": "5.0.1", + "emoji-regex": "8.0.0", + "end-of-stream": "1.4.5", + "enquirer": "2.3.6", + "es-define-property": "1.0.1", + "es-errors": "1.3.0", + "es-object-atoms": "1.1.1", + "es-set-tostringtag": "2.1.0", + "escalade": "3.2.0", + "escape-string-regexp": "1.0.5", + "figures": "3.2.0", + "flat": "5.0.2", + "follow-redirects": "1.16.0", + "form-data": "4.0.5", + "fs-constants": "1.0.0", + "function-bind": "1.1.2", + "get-caller-file": "2.0.5", + "get-intrinsic": "1.3.0", + "get-proto": "1.0.1", + "gopd": "1.2.0", + "has-flag": "4.0.0", + "has-symbols": "1.1.0", + "has-tostringtag": "1.0.2", + "hasown": "2.0.2", + "ieee754": "1.2.1", + "ignore": "7.0.5", + "inherits": "2.0.4", + "is-docker": "2.2.1", + "is-fullwidth-code-point": "3.0.0", + "is-interactive": "1.0.0", + "is-unicode-supported": "0.1.0", + "is-wsl": "2.2.0", + "json5": "2.2.3", + "jsonc-parser": "3.2.0", + "lines-and-columns": "2.0.3", + "log-symbols": "4.1.0", + "math-intrinsics": "1.1.0", + "mime-db": "1.52.0", + "mime-types": "2.1.35", + "mimic-fn": "2.1.0", + "minimatch": "10.2.5", + "minimist": "1.2.8", + "npm-run-path": "4.0.1", + "once": "1.4.0", + "onetime": "5.1.2", + "open": "8.4.2", + "ora": "5.3.0", + "path-key": "3.1.1", + "picocolors": "1.1.1", + "proxy-from-env": "2.1.0", + "readable-stream": "3.6.2", + "require-directory": "2.1.1", + "resolve.exports": "2.0.3", + "restore-cursor": "3.1.0", + "safe-buffer": "5.2.1", + "semver": "7.7.4", + "signal-exit": "3.0.7", + "smol-toml": "1.6.1", + "string_decoder": "1.3.0", + "string-width": "4.2.3", + "strip-ansi": "6.0.1", + "strip-bom": "3.0.0", + "supports-color": "7.2.0", + "tar-stream": "2.2.0", + "tmp": "0.2.4", + "tree-kill": "1.2.2", + "tsconfig-paths": "4.2.0", + "tslib": "2.8.1", + "util-deprecate": "1.0.2", + "wcwidth": "1.0.1", + "wrap-ansi": "7.0.0", + "wrappy": "1.0.2", + "y18n": "5.0.8", + "yaml": "2.8.0", + "yargs": "17.7.2", + "yargs-parser": "21.1.1" + }, + "bin": { + "nx": "dist/bin/nx.js", + "nx-cloud": "dist/bin/nx-cloud.js" + }, + "optionalDependencies": { + "@nx/nx-darwin-arm64": "22.7.2", + "@nx/nx-darwin-x64": "22.7.2", + "@nx/nx-freebsd-x64": "22.7.2", + "@nx/nx-linux-arm-gnueabihf": "22.7.2", + "@nx/nx-linux-arm64-gnu": "22.7.2", + "@nx/nx-linux-arm64-musl": "22.7.2", + "@nx/nx-linux-x64-gnu": "22.7.2", + "@nx/nx-linux-x64-musl": "22.7.2", + "@nx/nx-win32-arm64-msvc": "22.7.2", + "@nx/nx-win32-x64-msvc": "22.7.2" + }, + "peerDependencies": { + "@swc-node/register": "^1.11.1", + "@swc/core": "^1.15.8" + }, + "peerDependenciesMeta": { + "@swc-node/register": { + "optional": true + }, + "@swc/core": { + "optional": true + } + } + }, + "node_modules/lerna/node_modules/nx/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/lerna/node_modules/nx/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/lerna/node_modules/nx/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/lerna/node_modules/ora": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", + "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "log-symbols": "^4.0.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lerna/node_modules/run-async": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/run-async/-/run-async-4.0.6.tgz", @@ -13853,6 +14135,39 @@ "node": ">=0.12.0" } }, + "node_modules/lerna/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/lerna/node_modules/tmp": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", + "integrity": "sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/lerna/node_modules/yaml": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -14412,6 +14727,21 @@ "safe-buffer": "^5.1.2" } }, + "node_modules/loopback-datasource-juggler/node_modules/qs": { + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/loopback-datasource-juggler/node_modules/uuid": { "version": "13.0.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.2.tgz", @@ -15133,6 +15463,27 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimatch/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/minimatch/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -16480,6 +16831,16 @@ "node": ">=8.10.0" } }, + "node_modules/nodemon/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/nodemon/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", diff --git a/services/subscription-service/src/component.ts b/services/subscription-service/src/component.ts index 994c607..46cf5e3 100644 --- a/services/subscription-service/src/component.ts +++ b/services/subscription-service/src/component.ts @@ -30,7 +30,6 @@ import { FeatureToggleBindings, FeatureToggleServiceComponent, } from '@sourceloop/feature-toggle-service'; -import {BillingComponent} from 'loopback4-billing'; import {AuthenticationComponent} from 'loopback4-authentication'; import { AuthorizationBindings, @@ -39,6 +38,7 @@ import { import { BillingComponentBindings, StripeServiceProvider, + BillingComponent, StripeBindings, } from 'loopback4-billing'; import * as dotenv from 'dotenv'; diff --git a/services/subscription-service/src/controllers/billing-service.controller.ts b/services/subscription-service/src/controllers/billing-service.controller.ts index 50a1d59..98a1fa7 100644 --- a/services/subscription-service/src/controllers/billing-service.controller.ts +++ b/services/subscription-service/src/controllers/billing-service.controller.ts @@ -22,6 +22,7 @@ import { } from 'loopback4-billing'; const BASE = '/billing'; +const OK = 200; /** * Sandbox controller that exercises the full IService interface from @@ -665,7 +666,7 @@ export class BillingServiceController { }, }, }) - @response(200, { + @response(OK, { description: 'PDF information retrieved successfully', }) async getInvoicePdf( @@ -794,7 +795,7 @@ export class BillingServiceController { }, }, }) - @response(200, {description: 'Payment details retrieved successfully'}) + @response(OK, {description: 'Payment details retrieved successfully'}) async getInvoicePaymentDetails( @param.path.string('invoiceId') invoiceId: string, ): Promise { @@ -903,7 +904,7 @@ export class BillingServiceController { }, }, }) - @response(200, {description: 'Payment intent retrieved successfully'}) + @response(OK, {description: 'Payment intent retrieved successfully'}) async getPaymentIntent( @param.path.string('paymentIntentId') paymentIntentId: string, ): Promise { diff --git a/services/subscription-service/src/controllers/billing-webhook.controller.ts b/services/subscription-service/src/controllers/billing-webhook.controller.ts index af5f757..4c0632d 100644 --- a/services/subscription-service/src/controllers/billing-webhook.controller.ts +++ b/services/subscription-service/src/controllers/billing-webhook.controller.ts @@ -1,4 +1,4 @@ -import {inject} from '@loopback/core'; +import {inject, intercept} from '@loopback/core'; import {post, requestBody, HttpErrors} from '@loopback/rest'; import {authorize} from 'loopback4-authorization'; import { @@ -6,7 +6,7 @@ import { ISubscriptionService, TSubscriptionResult, } from 'loopback4-billing'; -import {intercept} from '@loopback/core'; +import {} from '@loopback/core'; import {repository} from '@loopback/repository'; import {WEBHOOK_VERIFIER} from '../keys'; import {InvoiceRepository} from '../repositories'; diff --git a/services/subscription-service/src/types.ts b/services/subscription-service/src/types.ts index ac07327..9c01912 100644 --- a/services/subscription-service/src/types.ts +++ b/services/subscription-service/src/types.ts @@ -47,6 +47,14 @@ export interface ISubscriptionServiceConfig extends IServiceConfig { useSequelize?: boolean; } +/** + * Type alias for flexible webhook additional data. + * Reduces union type complexity by grouping related types. + */ +type PrimitiveValue = string | number | boolean; +type OptionalValue = object | null | undefined; +type WebhookAdditionalData = PrimitiveValue | OptionalValue; + /** * Webhook payload structure from billing providers (Chargebee/Stripe). */ @@ -54,7 +62,7 @@ export interface IWebhookPayload { // eslint-disable-next-line @typescript-eslint/naming-convention event_type: string; content: IWebhookContent; - [key: string]: string | number | boolean | object | null | undefined; + [key: string]: WebhookAdditionalData; } /** @@ -64,7 +72,7 @@ export interface IWebhookContent { subscription?: IWebhookSubscription; invoice?: IWebhookInvoice; transaction?: IWebhookTransaction; - [key: string]: string | number | boolean | object | null | undefined; + [key: string]: WebhookAdditionalData; } /** @@ -81,7 +89,7 @@ export interface IWebhookSubscription { current_term_end?: number; // eslint-disable-next-line @typescript-eslint/naming-convention cancel_at_period_end?: boolean; - [key: string]: string | number | boolean | object | null | undefined; + [key: string]: WebhookAdditionalData; } /** @@ -92,7 +100,7 @@ export interface IWebhookInvoice { // eslint-disable-next-line @typescript-eslint/naming-convention currency_code?: string; total?: number; - [key: string]: string | number | boolean | object | null | undefined; + [key: string]: WebhookAdditionalData; } /** @@ -102,5 +110,5 @@ export interface IWebhookTransaction { amount?: number; // eslint-disable-next-line @typescript-eslint/naming-convention error_text?: string; - [key: string]: string | number | boolean | object | null | undefined; + [key: string]: WebhookAdditionalData; } From 6c769db08934078167581f23d6954e15c7485cb9 Mon Sep 17 00:00:00 2001 From: Sourav Kashyap Date: Wed, 20 May 2026 14:00:38 +0530 Subject: [PATCH 3/6] feat(billing): fix sonar --- package-lock.json | 1620 +++++++++-------- package.json | 6 +- .../src/__tests__/acceptance/test-helper.ts | 13 + .../controllers/billing-webhook.controller.ts | 199 +- 4 files changed, 914 insertions(+), 924 deletions(-) diff --git a/package-lock.json b/package-lock.json index 22c3d3b..feeb77e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -492,17 +492,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, "node_modules/@babel/helper-compilation-targets": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", @@ -1036,6 +1025,17 @@ "node": ">=12" } }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@dabh/diagnostics": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", @@ -1155,8 +1155,9 @@ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 4" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { @@ -1902,17 +1903,6 @@ "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, "node_modules/@jridgewell/remapping": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", @@ -1924,17 +1914,6 @@ "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@jridgewell/remapping/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -1953,14 +1932,14 @@ "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@jsep-plugin/assignment": { @@ -2010,6 +1989,27 @@ "@loopback/core": "^7.0.0" } }, + "node_modules/@loopback/boot/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@loopback/boot/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/@loopback/boot/node_modules/glob": { "version": "13.0.6", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", @@ -2027,6 +2027,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@loopback/boot/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@loopback/build": { "version": "12.0.13", "resolved": "https://registry.npmjs.org/@loopback/build/-/build-12.0.13.tgz", @@ -2183,7 +2198,10 @@ "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/@loopback/build/node_modules/cliui": { @@ -2306,22 +2324,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@loopback/build/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, "node_modules/@loopback/build/node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -2335,10 +2337,10 @@ "node": ">=8" } }, - "node_modules/@loopback/build/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "node_modules/@loopback/build/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "license": "ISC" }, @@ -2983,21 +2985,6 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, - "node_modules/@loopback/rest/node_modules/qs": { - "version": "6.15.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", - "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/@loopback/sequelize": { "version": "0.8.10", "resolved": "https://registry.npmjs.org/@loopback/sequelize/-/sequelize-0.8.10.tgz", @@ -3287,6 +3274,30 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/@npmcli/agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@npmcli/agent/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/@npmcli/arborist": { "version": "9.1.6", "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-9.1.6.tgz", @@ -3335,6 +3346,45 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/@npmcli/arborist/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@npmcli/arborist/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@npmcli/arborist/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@npmcli/arborist/node_modules/npm-bundled": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-5.0.0.tgz", @@ -3552,6 +3602,29 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/@npmcli/map-workspaces/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/@npmcli/map-workspaces/node_modules/glob": { "version": "13.0.6", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", @@ -3570,6 +3643,22 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@npmcli/map-workspaces/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@npmcli/metavuln-calculator": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-9.0.3.tgz", @@ -4696,6 +4785,16 @@ "node": ">=8" } }, + "node_modules/@sigstore/sign/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/@sigstore/sign/node_modules/proc-log": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", @@ -4978,6 +5077,15 @@ } } }, + "node_modules/@sourceloop/core/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/@sourceloop/core/node_modules/bl": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", @@ -4989,6 +5097,18 @@ "readable-stream": "^3.4.0" } }, + "node_modules/@sourceloop/core/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/@sourceloop/core/node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -5013,12 +5133,32 @@ "ieee754": "^1.2.1" } }, - "node_modules/@sourceloop/core/node_modules/inflection": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/inflection/-/inflection-3.0.2.tgz", - "integrity": "sha512-+Bg3+kg+J6JUWn8J6bzFmOWkTQ6L/NHfDRSYU+EVvuKHDxUDHAXgqixHfVlzuBQaPOTac8hn43aPhMNk6rMe3g==", - "license": "MIT", - "engines": { + "node_modules/@sourceloop/core/node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/@sourceloop/core/node_modules/inflection": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-3.0.2.tgz", + "integrity": "sha512-+Bg3+kg+J6JUWn8J6bzFmOWkTQ6L/NHfDRSYU+EVvuKHDxUDHAXgqixHfVlzuBQaPOTac8hn43aPhMNk6rMe3g==", + "license": "MIT", + "engines": { "node": ">=18.0.0" } }, @@ -5089,6 +5229,21 @@ } } }, + "node_modules/@sourceloop/core/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@sourceloop/core/node_modules/msgpack5": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/msgpack5/-/msgpack5-6.0.2.tgz", @@ -5160,6 +5315,16 @@ "db-migrate-pg": "^1.3.0" } }, + "node_modules/@sourceloop/feature-toggle-service/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "peer": true, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/@sourceloop/feature-toggle-service/node_modules/bl": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", @@ -5172,6 +5337,19 @@ "readable-stream": "^3.4.0" } }, + "node_modules/@sourceloop/feature-toggle-service/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/@sourceloop/feature-toggle-service/node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -5197,6 +5375,27 @@ "ieee754": "^1.2.1" } }, + "node_modules/@sourceloop/feature-toggle-service/node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause", + "peer": true + }, "node_modules/@sourceloop/feature-toggle-service/node_modules/inflection": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/inflection/-/inflection-3.0.2.tgz", @@ -5276,6 +5475,22 @@ } } }, + "node_modules/@sourceloop/feature-toggle-service/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "peer": true, + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@sourceloop/feature-toggle-service/node_modules/msgpack5": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/msgpack5/-/msgpack5-6.0.2.tgz", @@ -5364,6 +5579,45 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/@tufjs/models/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@tufjs/models/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@tufjs/models/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", @@ -5812,16 +6066,6 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/@typescript-eslint/parser": { "version": "7.18.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", @@ -6086,15 +6330,6 @@ "node": ">= 0.6" } }, - "node_modules/accepts/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/acorn": { "version": "8.17.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", @@ -6139,12 +6374,15 @@ "license": "MIT" }, "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "license": "MIT", + "dependencies": { + "debug": "4" + }, "engines": { - "node": ">= 14" + "node": ">= 6.0.0" } }, "node_modules/aggregate-error": { @@ -6249,6 +6487,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -6557,23 +6808,6 @@ "node": ">= 10.0.0" } }, - "node_modules/aws-sdk/node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "node_modules/aws-sdk/node_modules/ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "license": "BSD-3-Clause" - }, "node_modules/aws-sdk/node_modules/uuid": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", @@ -6602,13 +6836,14 @@ "peer": true }, "node_modules/axios": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz", - "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", + "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", "license": "MIT", "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, @@ -6792,15 +7027,43 @@ "peer": true }, "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", "license": "MIT", "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/bl/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/bl/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" } }, "node_modules/bluebird": { @@ -6939,27 +7202,14 @@ } }, "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", "license": "MIT", "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" } }, "node_modules/buffer-equal-constant-time": { @@ -7053,18 +7303,6 @@ "url": "https://opencollective.com/ioredis" } }, - "node_modules/bullmq/node_modules/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/byte-size": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-8.1.1.tgz", @@ -7119,6 +7357,29 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/cacache/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/cacache/node_modules/glob": { "version": "13.0.6", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", @@ -8228,9 +8489,9 @@ } }, "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "license": "MIT" }, "node_modules/cors": { @@ -8438,6 +8699,16 @@ "dev": true, "license": "MIT" }, + "node_modules/cz-conventional-changelog/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/cz-conventional-changelog/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -8565,6 +8836,16 @@ "dev": true, "license": "MIT" }, + "node_modules/cz-customizable/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/cz-customizable/node_modules/figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -9196,9 +9477,9 @@ "license": "MIT" }, "node_modules/diff": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", - "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -9254,9 +9535,9 @@ } }, "node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -9694,13 +9975,16 @@ "license": "MIT" }, "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint": { @@ -9891,34 +10175,9 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/eslint/node_modules/minimatch": { @@ -10218,21 +10477,6 @@ "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", "license": "MIT" }, - "node_modules/express/node_modules/qs": { - "version": "6.15.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", - "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/express/node_modules/raw-body": { "version": "2.5.3", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", @@ -10349,6 +10593,19 @@ "node": ">=8.6.0" } }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -10463,6 +10720,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -11415,16 +11682,52 @@ } }, "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", "dependencies": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 6" + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/global-dirs": { @@ -11510,19 +11813,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globals/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/globalthis": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", @@ -11567,16 +11857,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/globrex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", @@ -11903,6 +12183,16 @@ "node": ">= 14" } }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/http-signature": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz", @@ -11935,17 +12225,16 @@ "license": "MIT" }, "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "license": "MIT", "dependencies": { - "agent-base": "^7.1.2", + "agent-base": "6", "debug": "4" }, "engines": { - "node": ">= 14" + "node": ">= 6" } }, "node_modules/human-signals": { @@ -11985,6 +12274,30 @@ "uuid-parse": "^1.1.0" } }, + "node_modules/hyperid/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/hyperid/node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -12032,31 +12345,17 @@ } }, "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", "license": "BSD-3-Clause" }, "node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } @@ -12081,6 +12380,45 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/ignore-walk/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -13770,19 +14108,6 @@ } } }, - "node_modules/lerna/node_modules/ejs": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-5.0.1.tgz", - "integrity": "sha512-COqBPFMxuPTPspXl2DkVYaDS3HtrD1GpzOGkNTJ1IYkifq/r9h8SVEFrjA3D9/VJGOEoMQcrlhpntcSUrM8k6A==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.12.18" - } - }, "node_modules/lerna/node_modules/execa": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz", @@ -13835,19 +14160,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lerna/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/lerna/node_modules/inquirer": { "version": "12.9.6", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.9.6.tgz", @@ -13888,16 +14200,6 @@ "node": "*" } }, - "node_modules/lerna/node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/lerna/node_modules/mute-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", @@ -13908,223 +14210,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/lerna/node_modules/nx": { - "version": "22.7.2", - "resolved": "https://registry.npmjs.org/nx/-/nx-22.7.2.tgz", - "integrity": "sha512-Gh7gGO1t/TvgbKuVJMYWbxUwZC+E+PuRRVUeoOeVe82yEvBNl40EKiVHIbbi6GID0s9Zwzflo07UrKGLoDSVGw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@emnapi/core": "1.4.5", - "@emnapi/runtime": "1.4.5", - "@emnapi/wasi-threads": "1.0.4", - "@jest/diff-sequences": "30.0.1", - "@napi-rs/wasm-runtime": "0.2.4", - "@tybys/wasm-util": "0.9.0", - "@yarnpkg/lockfile": "1.1.0", - "@zkochan/js-yaml": "0.0.7", - "ansi-colors": "4.1.3", - "ansi-regex": "5.0.1", - "ansi-styles": "4.3.0", - "argparse": "2.0.1", - "asynckit": "0.4.0", - "axios": "1.16.0", - "balanced-match": "4.0.3", - "base64-js": "1.5.1", - "bl": "4.1.0", - "brace-expansion": "5.0.5", - "buffer": "5.7.1", - "call-bind-apply-helpers": "1.0.2", - "chalk": "4.1.2", - "cli-cursor": "3.1.0", - "cli-spinners": "2.6.1", - "cliui": "8.0.1", - "clone": "1.0.4", - "color-convert": "2.0.1", - "color-name": "1.1.4", - "combined-stream": "1.0.8", - "defaults": "1.0.4", - "define-lazy-prop": "2.0.0", - "delayed-stream": "1.0.0", - "dotenv": "16.4.7", - "dotenv-expand": "12.0.3", - "dunder-proto": "1.0.1", - "ejs": "5.0.1", - "emoji-regex": "8.0.0", - "end-of-stream": "1.4.5", - "enquirer": "2.3.6", - "es-define-property": "1.0.1", - "es-errors": "1.3.0", - "es-object-atoms": "1.1.1", - "es-set-tostringtag": "2.1.0", - "escalade": "3.2.0", - "escape-string-regexp": "1.0.5", - "figures": "3.2.0", - "flat": "5.0.2", - "follow-redirects": "1.16.0", - "form-data": "4.0.5", - "fs-constants": "1.0.0", - "function-bind": "1.1.2", - "get-caller-file": "2.0.5", - "get-intrinsic": "1.3.0", - "get-proto": "1.0.1", - "gopd": "1.2.0", - "has-flag": "4.0.0", - "has-symbols": "1.1.0", - "has-tostringtag": "1.0.2", - "hasown": "2.0.2", - "ieee754": "1.2.1", - "ignore": "7.0.5", - "inherits": "2.0.4", - "is-docker": "2.2.1", - "is-fullwidth-code-point": "3.0.0", - "is-interactive": "1.0.0", - "is-unicode-supported": "0.1.0", - "is-wsl": "2.2.0", - "json5": "2.2.3", - "jsonc-parser": "3.2.0", - "lines-and-columns": "2.0.3", - "log-symbols": "4.1.0", - "math-intrinsics": "1.1.0", - "mime-db": "1.52.0", - "mime-types": "2.1.35", - "mimic-fn": "2.1.0", - "minimatch": "10.2.5", - "minimist": "1.2.8", - "npm-run-path": "4.0.1", - "once": "1.4.0", - "onetime": "5.1.2", - "open": "8.4.2", - "ora": "5.3.0", - "path-key": "3.1.1", - "picocolors": "1.1.1", - "proxy-from-env": "2.1.0", - "readable-stream": "3.6.2", - "require-directory": "2.1.1", - "resolve.exports": "2.0.3", - "restore-cursor": "3.1.0", - "safe-buffer": "5.2.1", - "semver": "7.7.4", - "signal-exit": "3.0.7", - "smol-toml": "1.6.1", - "string_decoder": "1.3.0", - "string-width": "4.2.3", - "strip-ansi": "6.0.1", - "strip-bom": "3.0.0", - "supports-color": "7.2.0", - "tar-stream": "2.2.0", - "tmp": "0.2.4", - "tree-kill": "1.2.2", - "tsconfig-paths": "4.2.0", - "tslib": "2.8.1", - "util-deprecate": "1.0.2", - "wcwidth": "1.0.1", - "wrap-ansi": "7.0.0", - "wrappy": "1.0.2", - "y18n": "5.0.8", - "yaml": "2.8.0", - "yargs": "17.7.2", - "yargs-parser": "21.1.1" - }, - "bin": { - "nx": "dist/bin/nx.js", - "nx-cloud": "dist/bin/nx-cloud.js" - }, - "optionalDependencies": { - "@nx/nx-darwin-arm64": "22.7.2", - "@nx/nx-darwin-x64": "22.7.2", - "@nx/nx-freebsd-x64": "22.7.2", - "@nx/nx-linux-arm-gnueabihf": "22.7.2", - "@nx/nx-linux-arm64-gnu": "22.7.2", - "@nx/nx-linux-arm64-musl": "22.7.2", - "@nx/nx-linux-x64-gnu": "22.7.2", - "@nx/nx-linux-x64-musl": "22.7.2", - "@nx/nx-win32-arm64-msvc": "22.7.2", - "@nx/nx-win32-x64-msvc": "22.7.2" - }, - "peerDependencies": { - "@swc-node/register": "^1.11.1", - "@swc/core": "^1.15.8" - }, - "peerDependenciesMeta": { - "@swc-node/register": { - "optional": true - }, - "@swc/core": { - "optional": true - } - } - }, - "node_modules/lerna/node_modules/nx/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/lerna/node_modules/nx/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/lerna/node_modules/nx/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/lerna/node_modules/ora": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", - "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "log-symbols": "^4.0.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/lerna/node_modules/run-async": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/run-async/-/run-async-4.0.6.tgz", @@ -14135,39 +14220,6 @@ "node": ">=0.12.0" } }, - "node_modules/lerna/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/lerna/node_modules/tmp": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", - "integrity": "sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.14" - } - }, - "node_modules/lerna/node_modules/yaml": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", - "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", - "dev": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -14538,6 +14590,26 @@ "ieee754": "^1.2.1" } }, + "node_modules/loopback-connector-postgresql/node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/loopback-connector-postgresql/node_modules/loopback-connector": { "version": "6.2.12", "resolved": "https://registry.npmjs.org/loopback-connector/-/loopback-connector-6.2.12.tgz", @@ -14654,6 +14726,15 @@ "node": ">=20" } }, + "node_modules/loopback-datasource-juggler/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/loopback-datasource-juggler/node_modules/bl": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", @@ -14665,6 +14746,18 @@ "readable-stream": "^3.4.0" } }, + "node_modules/loopback-datasource-juggler/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/loopback-datasource-juggler/node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -14689,6 +14782,26 @@ "ieee754": "^1.2.1" } }, + "node_modules/loopback-datasource-juggler/node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/loopback-datasource-juggler/node_modules/inflection": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/inflection/-/inflection-3.0.2.tgz", @@ -14715,6 +14828,21 @@ "node": ">=20" } }, + "node_modules/loopback-datasource-juggler/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/loopback-datasource-juggler/node_modules/msgpack5": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/msgpack5/-/msgpack5-6.0.2.tgz", @@ -14727,21 +14855,6 @@ "safe-buffer": "^5.1.2" } }, - "node_modules/loopback-datasource-juggler/node_modules/qs": { - "version": "6.15.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", - "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/loopback-datasource-juggler/node_modules/uuid": { "version": "13.0.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.2.tgz", @@ -14779,18 +14892,6 @@ "@loopback/rest": "^15.0.11" } }, - "node_modules/loopback4-authentication/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, "node_modules/loopback4-authentication/node_modules/ajv": { "version": "8.20.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", @@ -14807,19 +14908,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/loopback4-authentication/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/loopback4-authentication/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -15062,6 +15150,16 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/make-fetch-happen/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/make-plural": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-7.5.0.tgz", @@ -15445,43 +15543,23 @@ "dev": true, "license": "MIT", "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimatch/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" + "node": ">=4" } }, - "node_modules/minimatch/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", - "license": "MIT", + "node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", "dependencies": { - "balanced-match": "^4.0.2" + "brace-expansion": "^2.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { @@ -15765,19 +15843,30 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "balanced-match": "^1.0.0" + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/mocha/node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "node_modules/mocha/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "engines": { - "node": ">=0.3.1" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/mocha/node_modules/emoji-regex": { @@ -15794,12 +15883,19 @@ "dev": true, "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/mocha/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, "node_modules/mocha/node_modules/glob": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", @@ -15845,22 +15941,6 @@ "dev": true, "license": "ISC" }, - "node_modules/mocha/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/mocha/node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", @@ -16108,46 +16188,6 @@ "node": ">= 0.6.0" } }, - "node_modules/mongodb/node_modules/bl": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", - "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", - "license": "MIT", - "dependencies": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" - } - }, - "node_modules/mongodb/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/mongodb/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/mongodb/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/moo": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.3.tgz", @@ -16226,16 +16266,6 @@ "safe-buffer": "^5.1.2" } }, - "node_modules/msgpack5/node_modules/bl": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", - "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", - "license": "MIT", - "dependencies": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" - } - }, "node_modules/msgpack5/node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -16347,10 +16377,9 @@ "license": "MIT" }, "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "dev": true, + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -16575,6 +16604,26 @@ "ieee754": "^1.2.1" } }, + "node_modules/node-jose/node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/node-jose/node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", @@ -16795,6 +16844,19 @@ "ms": "^2.1.1" } }, + "node_modules/nodemon/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/nodemon/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -16831,16 +16893,6 @@ "node": ">=8.10.0" } }, - "node_modules/nodemon/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, "node_modules/nodemon/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -17940,6 +17992,43 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ora/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/ora/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", @@ -20822,6 +20911,15 @@ "node": ">= 14" } }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -22110,6 +22208,16 @@ } } }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/tsconfig-paths": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", @@ -22231,9 +22339,9 @@ } }, "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -22776,13 +22884,6 @@ "extsprintf": "^1.2.0" } }, - "node_modules/verror/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "license": "MIT", - "peer": true - }, "node_modules/walk-up-path": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-4.0.0.tgz", @@ -23398,26 +23499,19 @@ } }, "services/orchestrator-service/node_modules/@types/node": { - "version": "20.19.19", + "version": "20.19.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", + "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } }, - "services/orchestrator-service/node_modules/dotenv": { - "version": "16.6.1", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, "services/orchestrator-service/node_modules/typescript": { "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -23430,6 +23524,8 @@ }, "services/orchestrator-service/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -23488,25 +23584,19 @@ } }, "services/subscription-service/node_modules/@types/node": { - "version": "20.19.19", + "version": "20.19.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", + "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } }, - "services/subscription-service/node_modules/dotenv": { - "version": "16.6.1", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, "services/subscription-service/node_modules/typescript": { "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -23519,6 +23609,8 @@ }, "services/subscription-service/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -23587,25 +23679,19 @@ } }, "services/tenant-management-service/node_modules/@types/node": { - "version": "20.19.19", + "version": "20.19.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", + "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } }, - "services/tenant-management-service/node_modules/dotenv": { - "version": "16.6.1", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, "services/tenant-management-service/node_modules/typescript": { "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -23618,6 +23704,8 @@ }, "services/tenant-management-service/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" } diff --git a/package.json b/package.json index ed9ccf0..176e0dc 100644 --- a/package.json +++ b/package.json @@ -29,9 +29,9 @@ "overrides": { "semver": "^7.5.2", "qs": "^6.14.1", - "form-data":"4.0.4", - "underscore":"1.13.8", - "undici":"7.24.0" + "form-data": "4.0.4", + "underscore": "1.13.8", + "undici": "7.24.0" }, "config": { "commitizen": { diff --git a/services/subscription-service/src/__tests__/acceptance/test-helper.ts b/services/subscription-service/src/__tests__/acceptance/test-helper.ts index ab60a71..6021a1e 100644 --- a/services/subscription-service/src/__tests__/acceptance/test-helper.ts +++ b/services/subscription-service/src/__tests__/acceptance/test-helper.ts @@ -72,6 +72,19 @@ function setUpEnv() { process.env.ENABLE_OBF = '0'; process.env.JWT_ISSUER = 'test'; process.env.JWT_SECRET = 'test'; + // Feature toggle database config + process.env.FEATURE_DB_HOST = 'localhost'; + process.env.FEATURE_DB_PORT = '5432'; + process.env.FEATURE_DB_USER = 'test'; + process.env.FEATURE_DB_PASSWORD = 'test'; + process.env.FEATURE_DB_DATABASE = 'test'; + process.env.FEATURE_DB_SCHEMA = 'public'; + // Billing config + process.env.SITE = 'test'; + process.env.API_KEY = 'test'; + process.env.STRIPE_SECRET = 'test'; + process.env.CHARGEBEE_ITEM_FAMILY_ID = 'default'; + process.env.BILLING_PROVIDER = 'stripe'; } export interface AppWithClient { diff --git a/services/subscription-service/src/controllers/billing-webhook.controller.ts b/services/subscription-service/src/controllers/billing-webhook.controller.ts index 4c0632d..886319f 100644 --- a/services/subscription-service/src/controllers/billing-webhook.controller.ts +++ b/services/subscription-service/src/controllers/billing-webhook.controller.ts @@ -6,11 +6,9 @@ import { ISubscriptionService, TSubscriptionResult, } from 'loopback4-billing'; -import {} from '@loopback/core'; import {repository} from '@loopback/repository'; import {WEBHOOK_VERIFIER} from '../keys'; import {InvoiceRepository} from '../repositories'; -import {BillingCustomerRepository} from '../repositories/billing-customer.repository'; import {IContent, IPayload, IWebhookPayload, IWebhookContent} from '../types'; /** @@ -27,7 +25,6 @@ enum ChargebeeEvent { SUBSCRIPTION_RENEWED = 'subscription_renewed', PAYMENT_SUCCEEDED = 'payment_succeeded', PAYMENT_FAILED = 'payment_failed', - INVOICE_GENERATED = 'invoice_generated', } /** @@ -46,8 +43,6 @@ export class BillingWebhookController { constructor( @inject(BillingComponentBindings.SDKProvider) private readonly billingService: ISubscriptionService, - @repository(BillingCustomerRepository) - public billingCustomerRepository: BillingCustomerRepository, @repository(InvoiceRepository) public invoiceRepository: InvoiceRepository, ) {} @@ -97,58 +92,29 @@ export class BillingWebhookController { const eventType: string = payload?.event_type ?? ''; const content = payload?.content ?? {}; - console.log(`[BillingWebhook] Received event: ${eventType}`); - - try { - switch (eventType) { - case ChargebeeEvent.SUBSCRIPTION_CREATED: - await this.onSubscriptionCreated(content); - break; - - case ChargebeeEvent.SUBSCRIPTION_ACTIVATED: - await this.onSubscriptionActivated(content); - break; - - case ChargebeeEvent.SUBSCRIPTION_CHANGED: - await this.onSubscriptionChanged(content); - break; - - case ChargebeeEvent.SUBSCRIPTION_CANCELLED: - await this.onSubscriptionCancelled(content); - break; - - case ChargebeeEvent.SUBSCRIPTION_PAUSED: - await this.onSubscriptionPaused(content); - break; - - case ChargebeeEvent.SUBSCRIPTION_RESUMED: - await this.onSubscriptionResumed(content); - break; - - case ChargebeeEvent.SUBSCRIPTION_RENEWED: - await this.onSubscriptionRenewed(content); - break; - - case ChargebeeEvent.PAYMENT_SUCCEEDED: - await this.onPaymentSucceeded(content); - break; - - case ChargebeeEvent.PAYMENT_FAILED: - await this.onPaymentFailed(content); - break; - - case ChargebeeEvent.INVOICE_GENERATED: - await this.onInvoiceGenerated(content); - break; + const eventHandlers: Record< + string, + (content: IWebhookContent) => Promise + > = { + [ChargebeeEvent.SUBSCRIPTION_CREATED]: + this.onSubscriptionCreated.bind(this), + [ChargebeeEvent.SUBSCRIPTION_ACTIVATED]: + this.onSubscriptionActivated.bind(this), + [ChargebeeEvent.SUBSCRIPTION_CHANGED]: + this.onSubscriptionChanged.bind(this), + [ChargebeeEvent.SUBSCRIPTION_CANCELLED]: + this.onSubscriptionCancelled.bind(this), + [ChargebeeEvent.SUBSCRIPTION_PAUSED]: + this.onSubscriptionPaused.bind(this), + [ChargebeeEvent.SUBSCRIPTION_RESUMED]: + this.onSubscriptionResumed.bind(this), + [ChargebeeEvent.SUBSCRIPTION_RENEWED]: + this.onSubscriptionRenewed.bind(this), + [ChargebeeEvent.PAYMENT_SUCCEEDED]: this.onPaymentSucceeded.bind(this), + [ChargebeeEvent.PAYMENT_FAILED]: this.onPaymentFailed.bind(this), + }; - default: - console.log(`[BillingWebhook] Unhandled event type: ${eventType}`); - } - } catch (err) { - console.error(`[BillingWebhook] Error handling ${eventType}:`, err); - // Return 200 to Chargebee regardless — prevents unnecessary retries - // for business-logic errors; log for manual review. - } + await eventHandlers[eventType]?.(content).catch(() => undefined); return {received: true, event: eventType}; } @@ -167,23 +133,12 @@ export class BillingWebhookController { await this.handlePayment(content); } - // --------------------------------------------------------------------------- - // Subscription lifecycle handlers - // --------------------------------------------------------------------------- - /** * Fired when a subscription is first created in Chargebee. * The subscription may be in `in_trial` or `active` state. */ private async onSubscriptionCreated(content: IWebhookContent): Promise { - const subscription: TSubscriptionResult = this.mapChargebeeSubscription( - content.subscription, - ); - console.log( - `[BillingWebhook] Subscription created: ${subscription.id} ` + - `status=${subscription.status} customer=${subscription.customerId}`, - ); - // TODO: Update your internal subscription record, send welcome email, etc. + this.mapChargebeeSubscription(content.subscription); } /** @@ -196,16 +151,7 @@ export class BillingWebhookController { private async onSubscriptionActivated( content: IWebhookContent, ): Promise { - const subscriptionId: string = content.subscription?.id ?? ''; - // Verify the subscription state directly from Chargebee via the library - const subscription: TSubscriptionResult = - await this.billingService.getSubscription(subscriptionId); - console.log( - `[BillingWebhook] Subscription activated (verified): ${subscription.id} ` + - `status=${subscription.status} customer=${subscription.customerId} ` + - `periodEnd=${subscription.currentPeriodEnd}`, - ); - // TODO: Mark subscription as ACTIVE in your DB, provision tenant resources. + await this.verifySubscriptionState(content); } /** @@ -215,15 +161,19 @@ export class BillingWebhookController { * directly from Chargebee. */ private async onSubscriptionChanged(content: IWebhookContent): Promise { + await this.verifySubscriptionState(content); + } + + /** + * Fetches and verifies the current subscription state from Chargebee. + */ + private async verifySubscriptionState( + content: IWebhookContent, + ): Promise { const subscriptionId: string = content.subscription?.id ?? ''; - // Fetch updated subscription from Chargebee via the library - const subscription: TSubscriptionResult = + if (subscriptionId) { await this.billingService.getSubscription(subscriptionId); - console.log( - `[BillingWebhook] Subscription changed (verified): ${subscription.id} ` + - `newStatus=${subscription.status}`, - ); - // TODO: Sync the new plan/price to your internal subscription record. + } } /** @@ -232,56 +182,30 @@ export class BillingWebhookController { private async onSubscriptionCancelled( content: IWebhookContent, ): Promise { - const subscription: TSubscriptionResult = this.mapChargebeeSubscription( - content.subscription, - ); - console.log( - `[BillingWebhook] Subscription cancelled: ${subscription.id} ` + - `cancelAtPeriodEnd=${subscription.cancelAtPeriodEnd}`, - ); - // TODO: Update status to CANCELLED, revoke tenant access if needed. + this.mapChargebeeSubscription(content.subscription); } /** * Fired when a subscription enters the paused state. */ private async onSubscriptionPaused(content: IWebhookContent): Promise { - const subscription: TSubscriptionResult = this.mapChargebeeSubscription( - content.subscription, - ); - console.log(`[BillingWebhook] Subscription paused: ${subscription.id}`); - // TODO: Suspend tenant access, send notification. + this.mapChargebeeSubscription(content.subscription); } /** * Fired when a paused subscription is resumed. */ private async onSubscriptionResumed(content: IWebhookContent): Promise { - const subscription: TSubscriptionResult = this.mapChargebeeSubscription( - content.subscription, - ); - console.log(`[BillingWebhook] Subscription resumed: ${subscription.id}`); - // TODO: Restore tenant access, send notification. + this.mapChargebeeSubscription(content.subscription); } /** * Fired at the start of each new billing period (renewal). */ private async onSubscriptionRenewed(content: IWebhookContent): Promise { - const subscription: TSubscriptionResult = this.mapChargebeeSubscription( - content.subscription, - ); - console.log( - `[BillingWebhook] Subscription renewed: ${subscription.id} ` + - `nextRenewal=${subscription.currentPeriodEnd}`, - ); - // TODO: Update next billing date in your DB. + this.mapChargebeeSubscription(content.subscription); } - // --------------------------------------------------------------------------- - // Payment handlers - // --------------------------------------------------------------------------- - /** * Fired when a payment is successfully collected. * @@ -290,19 +214,10 @@ export class BillingWebhookController { */ private async onPaymentSucceeded(content: IWebhookContent): Promise { const invoiceId: string = content.invoice?.id ?? ''; - const transaction = content.transaction; - // Fetch full price breakdown via the library - const priceDetails = invoiceId - ? await this.billingService.getInvoicePriceDetails(invoiceId) - : null; - console.log( - `[BillingWebhook] Payment succeeded — invoice=${invoiceId} ` + - `amount=${transaction?.amount ?? 'N/A'} ` + - `total=${priceDetails?.totalAmount ?? 'N/A'} ` + - `tax=${priceDetails?.taxAmount ?? 'N/A'} ` + - `currency=${priceDetails?.currency ?? content.invoice?.currency_code ?? 'N/A'}`, - ); - // TODO: Mark invoice as paid, update subscription record, trigger provisioning. + + if (invoiceId) { + await this.billingService.getInvoicePriceDetails(invoiceId); + } } /** @@ -311,12 +226,7 @@ export class BillingWebhookController { */ private async onPaymentFailed(content: IWebhookContent): Promise { const invoice = content.invoice; - const transaction = content.transaction; - console.log( - `[BillingWebhook] Payment FAILED — invoice=${invoice?.id ?? 'N/A'} ` + - `reason=${transaction?.error_text ?? 'unknown'}`, - ); - // TODO: Notify tenant, log for dunning review, update subscription status. + if (!invoice?.id) { throw new HttpErrors.UnprocessableEntity( '[BillingWebhook] Payment failed event missing invoice ID', @@ -324,27 +234,6 @@ export class BillingWebhookController { } } - // --------------------------------------------------------------------------- - // Invoice handlers - // --------------------------------------------------------------------------- - - /** - * Fired when Chargebee generates a new invoice. - * Use this to record the invoice in your system. - */ - private async onInvoiceGenerated(content: IWebhookContent): Promise { - const invoice = content.invoice; - console.log( - `[BillingWebhook] Invoice generated: ${invoice?.id ?? 'N/A'} ` + - `total=${invoice?.total ?? 0} ${invoice?.currency_code ?? ''}`, - ); - // TODO: Create an invoice record in your DB. - } - - // --------------------------------------------------------------------------- - // Helper - // --------------------------------------------------------------------------- - /** * Maps a raw Chargebee subscription object from a webhook payload to the * provider-agnostic TSubscriptionResult. From 1ab47945def5597e022ddadea92d51397b7ca580 Mon Sep 17 00:00:00 2001 From: Sourav Kashyap Date: Wed, 20 May 2026 18:11:37 +0530 Subject: [PATCH 4/6] feat(billing): fix comments --- services/subscription-service/.env.example | 6 - .../src/__tests__/acceptance/test-helper.ts | 13 - .../subscription-service/src/component.ts | 25 +- .../controllers/billing-service.controller.ts | 274 ++++++------------ .../src/models/billing-request.model.ts | 188 ++++++++++++ .../subscription-service/src/models/index.ts | 1 + services/subscription-service/src/types.ts | 8 + 7 files changed, 283 insertions(+), 232 deletions(-) create mode 100644 services/subscription-service/src/models/billing-request.model.ts diff --git a/services/subscription-service/.env.example b/services/subscription-service/.env.example index 73baa0e..574df2f 100644 --- a/services/subscription-service/.env.example +++ b/services/subscription-service/.env.example @@ -22,9 +22,3 @@ FEATURE_DB_USER= FEATURE_DB_PASSWORD= FEATURE_DB_DATABASE= FEATURE_DB_SCHEMA= - -SITE= -API_KEY= -STRIPE_SECRET= -CHARGEBEE_ITEM_FAMILY_ID= -BILLING_PROVIDER= diff --git a/services/subscription-service/src/__tests__/acceptance/test-helper.ts b/services/subscription-service/src/__tests__/acceptance/test-helper.ts index 6021a1e..ab60a71 100644 --- a/services/subscription-service/src/__tests__/acceptance/test-helper.ts +++ b/services/subscription-service/src/__tests__/acceptance/test-helper.ts @@ -72,19 +72,6 @@ function setUpEnv() { process.env.ENABLE_OBF = '0'; process.env.JWT_ISSUER = 'test'; process.env.JWT_SECRET = 'test'; - // Feature toggle database config - process.env.FEATURE_DB_HOST = 'localhost'; - process.env.FEATURE_DB_PORT = '5432'; - process.env.FEATURE_DB_USER = 'test'; - process.env.FEATURE_DB_PASSWORD = 'test'; - process.env.FEATURE_DB_DATABASE = 'test'; - process.env.FEATURE_DB_SCHEMA = 'public'; - // Billing config - process.env.SITE = 'test'; - process.env.API_KEY = 'test'; - process.env.STRIPE_SECRET = 'test'; - process.env.CHARGEBEE_ITEM_FAMILY_ID = 'default'; - process.env.BILLING_PROVIDER = 'stripe'; } export interface AppWithClient { diff --git a/services/subscription-service/src/component.ts b/services/subscription-service/src/component.ts index 46cf5e3..481de51 100644 --- a/services/subscription-service/src/component.ts +++ b/services/subscription-service/src/component.ts @@ -35,14 +35,7 @@ import { AuthorizationBindings, AuthorizationComponent, } from 'loopback4-authorization'; -import { - BillingComponentBindings, - StripeServiceProvider, - BillingComponent, - StripeBindings, -} from 'loopback4-billing'; -import * as dotenv from 'dotenv'; -import * as dotenvExt from 'dotenv-extended'; +import {BillingComponent} from 'loopback4-billing'; import { SubscriptionServiceBindings, SYSTEM_USER, @@ -94,14 +87,6 @@ export class SubscriptionServiceComponent implements Component { // Mount core component this.application.component(CoreComponent); - // Load environment variables - dotenv.config(); - dotenvExt.load({ - schema: '.env.example', - errorOnMissing: true, - includeProcessEnv: true, - }); - /**Bind the feature toggle service to main the list of features */ this.application .bind(FeatureToggleBindings.Config) @@ -109,14 +94,6 @@ export class SubscriptionServiceComponent implements Component { this.application.component(FeatureToggleServiceComponent); this.application.component(BillingComponent); - // Configure Stripe billing provider - this.application.bind(StripeBindings.config).to({ - secretKey: process.env.STRIPE_SECRET ?? '', - }); - this.application - .bind(BillingComponentBindings.SDKProvider) - .toProvider(StripeServiceProvider); - this.application.api({ openapi: '3.0.0', info: { diff --git a/services/subscription-service/src/controllers/billing-service.controller.ts b/services/subscription-service/src/controllers/billing-service.controller.ts index 98a1fa7..e1dc52c 100644 --- a/services/subscription-service/src/controllers/billing-service.controller.ts +++ b/services/subscription-service/src/controllers/billing-service.controller.ts @@ -20,9 +20,17 @@ import { TInvoice, Transaction, } from 'loopback4-billing'; +import {getModelSchemaRefSF, STATUS_CODE} from '@sourceloop/core'; +import { + BillingCustomerBody, + BillingPaymentSourceBody, + BillingPaymentMethodBody, + BillingInvoiceBody, + BillingUpdateInvoiceBody, + BillingPaymentStatusResponse, +} from '../models'; const BASE = '/billing'; -const OK = 200; /** * Sandbox controller that exercises the full IService interface from @@ -57,7 +65,7 @@ export class BillingServiceController { @post(`${BASE}/customers`, { summary: 'Create a new customer', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Created customer object', content: {'application/json': {schema: {type: 'object'}}}, }, @@ -67,33 +75,13 @@ export class BillingServiceController { @requestBody({ content: { 'application/json': { - schema: { - type: 'object', - required: ['firstName', 'lastName', 'email'], - properties: { - firstName: {type: 'string', example: 'John'}, - lastName: {type: 'string', example: 'Doe'}, - email: {type: 'string', example: 'john.doe@example.com'}, - company: {type: 'string', example: 'Acme Inc.'}, - phone: {type: 'string', example: '+1234567890'}, - billingAddress: { - type: 'object', - properties: { - line1: {type: 'string'}, - city: {type: 'string'}, - state: {type: 'string'}, - zip: {type: 'string'}, - country: {type: 'string', example: 'US'}, - }, - }, - }, - }, + schema: {...getModelSchemaRefSF(BillingCustomerBody)}, }, }, }) - customerDto: TCustomer, + customerDto: BillingCustomerBody, ): Promise { - return this.billingService.createCustomer(customerDto); + return this.billingService.createCustomer(customerDto as TCustomer); } /** @@ -103,7 +91,7 @@ export class BillingServiceController { @get(`${BASE}/customers/{customerId}`, { summary: 'Get a customer by ID', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Customer object', content: {'application/json': {schema: {type: 'object'}}}, }, @@ -130,7 +118,7 @@ export class BillingServiceController { @put(`${BASE}/customers/{customerId}`, { summary: 'Update a customer', responses: { - '204': {description: 'Customer updated'}, + [STATUS_CODE.NO_CONTENT]: {description: 'Customer updated'}, }, }) async updateCustomer( @@ -138,32 +126,16 @@ export class BillingServiceController { @requestBody({ content: { 'application/json': { - schema: { - type: 'object', - properties: { - firstName: {type: 'string'}, - lastName: {type: 'string'}, - email: {type: 'string'}, - company: {type: 'string'}, - phone: {type: 'string'}, - billingAddress: { - type: 'object', - properties: { - line1: {type: 'string'}, - city: {type: 'string'}, - state: {type: 'string'}, - zip: {type: 'string'}, - country: {type: 'string'}, - }, - }, - }, - }, + schema: {...getModelSchemaRefSF(BillingCustomerBody)}, }, }, }) - customerDto: Partial, + customerDto: Partial, ): Promise { - await this.billingService.updateCustomerById(customerId, customerDto); + await this.billingService.updateCustomerById( + customerId, + customerDto as Partial, + ); } /** @@ -173,7 +145,7 @@ export class BillingServiceController { @del(`${BASE}/customers/{customerId}`, { summary: 'Delete a customer', responses: { - '204': {description: 'Customer deleted'}, + [STATUS_CODE.NO_CONTENT]: {description: 'Customer deleted'}, }, }) async deleteCustomer( @@ -201,7 +173,7 @@ export class BillingServiceController { @post(`${BASE}/payment-sources`, { summary: 'Create (attach) a payment source to a customer', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Payment source created', content: {'application/json': {schema: {type: 'object'}}}, }, @@ -211,30 +183,15 @@ export class BillingServiceController { @requestBody({ content: { 'application/json': { - schema: { - type: 'object', - required: ['customerId'], - properties: { - customerId: {type: 'string', example: 'cus_UCXfGdNijvXTDf'}, - options: { - type: 'object', - properties: { - token: { - type: 'string', - example: 'tok_visa', - description: - 'Stripe test token (tok_visa, tok_mastercard etc.)', - }, - }, - }, - }, - }, + schema: {...getModelSchemaRefSF(BillingPaymentSourceBody)}, }, }, }) - paymentDto: TPaymentSource, + paymentDto: BillingPaymentSourceBody, ): Promise { - return this.billingService.createPaymentSource(paymentDto); + return this.billingService.createPaymentSource( + paymentDto as TPaymentSource, + ); } /** @@ -244,7 +201,7 @@ export class BillingServiceController { @get(`${BASE}/payment-sources/{paymentSourceId}`, { summary: 'Retrieve a payment source by ID', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Payment source object', content: {'application/json': {schema: {type: 'object'}}}, }, @@ -263,7 +220,7 @@ export class BillingServiceController { @del(`${BASE}/payment-sources/{paymentSourceId}`, { summary: 'Delete (detach) a payment source', responses: { - '204': {description: 'Payment source deleted'}, + [STATUS_CODE.NO_CONTENT]: {description: 'Payment source deleted'}, }, }) async deletePaymentSource( @@ -295,7 +252,7 @@ export class BillingServiceController { @post(`${BASE}/invoices/{invoiceId}/apply-payment`, { summary: 'Apply a payment to an invoice', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Invoice after payment applied', content: {'application/json': {schema: {type: 'object'}}}, }, @@ -306,42 +263,15 @@ export class BillingServiceController { @requestBody({ content: { 'application/json': { - schema: { - type: 'object', - required: ['paymentMethod'], - properties: { - paymentMethod: { - type: 'string', - enum: [ - 'cash', - 'check', - 'bank_transfer', - 'other', - 'custom', - 'payment_source', - ], - example: 'bank_transfer', - }, - paymentSourceId: { - type: 'string', - description: 'Required when paymentMethod is payment_source', - }, - amount: {type: 'number', example: 4999}, - referenceNumber: {type: 'string', example: 'REF-001'}, - comment: { - type: 'string', - example: 'Paid via wire transfer', - }, - }, - }, + schema: {...getModelSchemaRefSF(BillingPaymentMethodBody)}, }, }, }) - transaction: Transaction, + transaction: BillingPaymentMethodBody, ): Promise { return this.billingService.applyPaymentSourceForInvoice( invoiceId, - transaction, + transaction as Transaction, ); } @@ -368,7 +298,7 @@ export class BillingServiceController { @post(`${BASE}/invoices`, { summary: 'Create a one-time invoice with custom line items', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Created invoice object', content: {'application/json': {schema: {type: 'object'}}}, }, @@ -378,45 +308,13 @@ export class BillingServiceController { @requestBody({ content: { 'application/json': { - schema: { - type: 'object', - required: ['customerId', 'currencyCode', 'charges'], - properties: { - customerId: {type: 'string', example: 'cus_UCXfGdNijvXTDf'}, - currencyCode: {type: 'string', example: 'usd'}, - charges: { - type: 'array', - items: { - type: 'object', - required: ['amount', 'description'], - properties: { - amount: { - type: 'number', - example: 5000, - description: 'Amount in minor unit (cents)', - }, - description: {type: 'string', example: 'Setup fee'}, - }, - }, - }, - shippingAddress: { - type: 'object', - properties: { - line1: {type: 'string'}, - city: {type: 'string'}, - state: {type: 'string'}, - zip: {type: 'string'}, - country: {type: 'string'}, - }, - }, - }, - }, + schema: {...getModelSchemaRefSF(BillingInvoiceBody)}, }, }, }) - invoice: TInvoice, + invoice: BillingInvoiceBody, ): Promise { - return this.billingService.createInvoice(invoice); + return this.billingService.createInvoice(invoice as TInvoice); } /** @@ -426,7 +324,7 @@ export class BillingServiceController { @get(`${BASE}/invoices/{invoiceId}`, { summary: 'Retrieve an invoice by ID', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Invoice object', content: {'application/json': {schema: {type: 'object'}}}, }, @@ -462,7 +360,7 @@ export class BillingServiceController { @put(`${BASE}/invoices/{invoiceId}`, { summary: 'Update an invoice (shipping address)', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Updated invoice object', content: {'application/json': {schema: {type: 'object'}}}, }, @@ -473,32 +371,16 @@ export class BillingServiceController { @requestBody({ content: { 'application/json': { - schema: { - type: 'object', - required: ['customerId', 'currencyCode'], - properties: { - customerId: {type: 'string'}, - currencyCode: {type: 'string'}, - shippingAddress: { - type: 'object', - properties: { - firstName: {type: 'string'}, - lastName: {type: 'string'}, - line1: {type: 'string'}, - city: {type: 'string'}, - state: {type: 'string'}, - zip: {type: 'string'}, - country: {type: 'string'}, - }, - }, - }, - }, + schema: {...getModelSchemaRefSF(BillingUpdateInvoiceBody)}, }, }, }) - invoice: Partial, + invoice: BillingUpdateInvoiceBody, ): Promise { - return this.billingService.updateInvoice(invoiceId, invoice); + return this.billingService.updateInvoice( + invoiceId, + invoice as Partial, + ); } /** @@ -509,7 +391,7 @@ export class BillingServiceController { @del(`${BASE}/invoices/{invoiceId}`, { summary: 'Delete a draft invoice', responses: { - '204': {description: 'Invoice deleted'}, + [STATUS_CODE.NO_CONTENT]: {description: 'Invoice deleted'}, }, }) async deleteInvoice( @@ -525,16 +407,11 @@ export class BillingServiceController { @get(`${BASE}/invoices/{invoiceId}/payment-status`, { summary: 'Check if an invoice is paid', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Payment status', content: { 'application/json': { - schema: { - type: 'object', - properties: { - paid: {type: 'boolean', example: true}, - }, - }, + schema: {...getModelSchemaRefSF(BillingPaymentStatusResponse)}, }, }, }, @@ -542,7 +419,7 @@ export class BillingServiceController { }) async getPaymentStatus( @param.path.string('invoiceId') invoiceId: string, - ): Promise<{paid: boolean}> { + ): Promise { const paid = await this.billingService.getPaymentStatus(invoiceId); return {paid}; } @@ -581,7 +458,7 @@ export class BillingServiceController { 'For Stripe, only finalized invoices have PDF URLs available. ' + 'For ChargeBee, most invoice states support PDF generation.', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'PDF information retrieved successfully', content: { 'application/json': { @@ -619,7 +496,7 @@ export class BillingServiceController { }, }, }, - '404': { + [STATUS_CODE.NOT_FOUND]: { description: 'Invoice not found', content: { 'application/json': { @@ -629,7 +506,10 @@ export class BillingServiceController { error: { type: 'object', properties: { - statusCode: {type: 'number', example: 404}, + statusCode: { + type: 'number', + example: STATUS_CODE.NOT_FOUND, + }, message: { type: 'string', example: 'Invoice not found: in_1234567890', @@ -641,7 +521,7 @@ export class BillingServiceController { }, }, }, - '400': { + [STATUS_CODE.BAD_REQUEST]: { description: 'PDF URL not available', content: { 'application/json': { @@ -651,7 +531,10 @@ export class BillingServiceController { error: { type: 'object', properties: { - statusCode: {type: 'number', example: 400}, + statusCode: { + type: 'number', + example: STATUS_CODE.BAD_REQUEST, + }, message: { type: 'string', example: @@ -666,7 +549,7 @@ export class BillingServiceController { }, }, }) - @response(OK, { + @response(STATUS_CODE.OK, { description: 'PDF information retrieved successfully', }) async getInvoicePdf( @@ -714,7 +597,7 @@ export class BillingServiceController { 'Retrieves payment method details, payment amount, status, and ' + 'transaction information for a specific invoice.', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Payment details retrieved successfully', content: { 'application/json': { @@ -748,7 +631,7 @@ export class BillingServiceController { }, }, }, - '404': { + [STATUS_CODE.NOT_FOUND]: { description: 'Invoice not found', content: { 'application/json': { @@ -758,7 +641,10 @@ export class BillingServiceController { error: { type: 'object', properties: { - statusCode: {type: 'number', example: 404}, + statusCode: { + type: 'number', + example: STATUS_CODE.NOT_FOUND, + }, message: { type: 'string', example: 'Invoice not found: in_1234567890', @@ -770,7 +656,7 @@ export class BillingServiceController { }, }, }, - '400': { + [STATUS_CODE.BAD_REQUEST]: { description: 'No payment details available', content: { 'application/json': { @@ -780,7 +666,10 @@ export class BillingServiceController { error: { type: 'object', properties: { - statusCode: {type: 'number', example: 400}, + statusCode: { + type: 'number', + example: STATUS_CODE.BAD_REQUEST, + }, message: { type: 'string', example: @@ -795,7 +684,9 @@ export class BillingServiceController { }, }, }) - @response(OK, {description: 'Payment details retrieved successfully'}) + @response(STATUS_CODE.OK, { + description: 'Payment details retrieved successfully', + }) async getInvoicePaymentDetails( @param.path.string('invoiceId') invoiceId: string, ): Promise { @@ -840,7 +731,7 @@ export class BillingServiceController { 'status, payment method, amount, and transaction metadata. ' + 'Useful for payment tracking and webhook handling.', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Payment intent retrieved successfully', content: { 'application/json': { @@ -880,7 +771,7 @@ export class BillingServiceController { }, }, }, - '404': { + [STATUS_CODE.NOT_FOUND]: { description: 'Payment intent not found', content: { 'application/json': { @@ -890,7 +781,10 @@ export class BillingServiceController { error: { type: 'object', properties: { - statusCode: {type: 'number', example: 404}, + statusCode: { + type: 'number', + example: STATUS_CODE.NOT_FOUND, + }, message: { type: 'string', example: 'Payment intent not found: pi_1234567890', @@ -904,7 +798,9 @@ export class BillingServiceController { }, }, }) - @response(OK, {description: 'Payment intent retrieved successfully'}) + @response(STATUS_CODE.OK, { + description: 'Payment intent retrieved successfully', + }) async getPaymentIntent( @param.path.string('paymentIntentId') paymentIntentId: string, ): Promise { diff --git a/services/subscription-service/src/models/billing-request.model.ts b/services/subscription-service/src/models/billing-request.model.ts new file mode 100644 index 0000000..3f2eed6 --- /dev/null +++ b/services/subscription-service/src/models/billing-request.model.ts @@ -0,0 +1,188 @@ +import {model, property} from '@loopback/repository'; +import {PaymentMethodType} from '../types'; + +@model({ + name: 'billing_address_fields', +}) +export class BillingAddressFields { + @property({type: 'string'}) + line1?: string; + + @property({type: 'string'}) + city?: string; + + @property({type: 'string'}) + state?: string; + + @property({type: 'string'}) + zip?: string; + + @property({type: 'string'}) + country?: string; +} + +@model({ + name: 'billing_customer_body', +}) +export class BillingCustomerBody { + @property({type: 'string', required: true}) + firstName!: string; + + @property({type: 'string', required: true}) + lastName!: string; + + @property({type: 'string', required: true}) + email!: string; + + @property({type: 'string'}) + company?: string; + + @property({type: 'string'}) + phone?: string; + + @property({type: BillingAddressFields}) + billingAddress?: BillingAddressFields; +} + +@model({ + name: 'billing_payment_source_options', +}) +export class BillingPaymentSourceOptions { + @property({type: 'string'}) + token?: string; +} + +@model({ + name: 'billing_payment_source_body', +}) +export class BillingPaymentSourceBody { + @property({type: 'string', required: true}) + customerId!: string; + + @property({type: BillingPaymentSourceOptions}) + options?: BillingPaymentSourceOptions; +} + +@model({ + name: 'billing_payment_method_body', +}) +export class BillingPaymentMethodBody { + @property({ + type: 'string', + required: true, + jsonSchema: { + enum: [ + 'cash', + 'check', + 'bank_transfer', + 'other', + 'custom', + 'payment_source', + ], + }, + }) + paymentMethod!: PaymentMethodType; + + @property({type: 'string'}) + paymentSourceId?: string; + + @property({type: 'number'}) + amount?: number; + + @property({type: 'string'}) + referenceNumber?: string; + + @property({type: 'string'}) + comment?: string; +} + +@model({ + name: 'billing_invoice_charge', +}) +export class BillingInvoiceCharge { + @property({type: 'number', required: true}) + amount!: number; + + @property({type: 'string', required: true}) + description!: string; +} + +@model({ + name: 'billing_invoice_body', +}) +export class BillingInvoiceBody { + @property({type: 'string', required: true}) + customerId!: string; + + @property({type: 'string', required: true}) + currencyCode!: string; + + @property({ + type: 'array', + itemType: BillingInvoiceCharge, + required: true, + }) + charges!: BillingInvoiceCharge[]; + + @property({type: BillingAddressFields}) + shippingAddress?: BillingAddressFields; +} + +@model({ + name: 'billing_shipping_address', +}) +export class BillingShippingAddress { + @property({type: 'string'}) + firstName?: string; + + @property({type: 'string'}) + lastName?: string; + + @property({type: 'string'}) + line1?: string; + + @property({type: 'string'}) + city?: string; + + @property({type: 'string'}) + state?: string; + + @property({type: 'string'}) + zip?: string; + + @property({type: 'string'}) + country?: string; +} + +@model({ + name: 'billing_update_invoice_body', +}) +export class BillingUpdateInvoiceBody { + @property({type: 'string', required: true}) + customerId!: string; + + @property({type: 'string', required: true}) + currencyCode!: string; + + @property({type: BillingShippingAddress}) + shippingAddress?: BillingShippingAddress; +} + +@model({ + name: 'billing_payment_status_response', +}) +export class BillingPaymentStatusResponse { + @property({type: 'boolean'}) + paid!: boolean; +} + +@model({ + name: 'billing_error_response', +}) +export class BillingErrorResponse { + @property({type: 'object'}) + error: { + statusCode: number; + message: string; + }; +} diff --git a/services/subscription-service/src/models/index.ts b/services/subscription-service/src/models/index.ts index 8753b60..3982218 100644 --- a/services/subscription-service/src/models/index.ts +++ b/services/subscription-service/src/models/index.ts @@ -8,3 +8,4 @@ export * from './plan-sizes.model'; export * from './dto'; export * from './billing-customer.model'; export * from './invoice.model'; +export * from './billing-request.model'; diff --git a/services/subscription-service/src/types.ts b/services/subscription-service/src/types.ts index 9c01912..1e94d4d 100644 --- a/services/subscription-service/src/types.ts +++ b/services/subscription-service/src/types.ts @@ -30,6 +30,14 @@ export type InvoiceStatus = | 'voided' | 'pending'; +export type PaymentMethodType = + | 'cash' + | 'check' + | 'bank_transfer' + | 'other' + | 'custom' + | 'payment_source'; + export interface IPayload { id: string; // eslint-disable-next-line @typescript-eslint/naming-convention From 438279279d0df5d905d6302e824656a76f323adf Mon Sep 17 00:00:00 2001 From: Sourav Kashyap Date: Thu, 21 May 2026 18:38:10 +0530 Subject: [PATCH 5/6] feat(billing): fix comments --- .../controllers/billing-service.controller.ts | 412 +----------------- .../controllers/billing-webhook.controller.ts | 268 ------------ .../src/controllers/index.ts | 1 - services/subscription-service/src/types.ts | 66 --- 4 files changed, 11 insertions(+), 736 deletions(-) delete mode 100644 services/subscription-service/src/controllers/billing-webhook.controller.ts diff --git a/services/subscription-service/src/controllers/billing-service.controller.ts b/services/subscription-service/src/controllers/billing-service.controller.ts index e1dc52c..334349e 100644 --- a/services/subscription-service/src/controllers/billing-service.controller.ts +++ b/services/subscription-service/src/controllers/billing-service.controller.ts @@ -1,13 +1,5 @@ import {inject} from '@loopback/core'; -import { - del, - get, - param, - post, - put, - requestBody, - response, -} from '@loopback/rest'; +import {get, param} from '@loopback/rest'; import {authorize} from 'loopback4-authorization'; import { BillingComponentBindings, @@ -15,28 +7,23 @@ import { TInvoicePdf, TInvoicePaymentDetails, TPaymentIntent, - TCustomer, - TPaymentSource, - TInvoice, - Transaction, } from 'loopback4-billing'; import {getModelSchemaRefSF, STATUS_CODE} from '@sourceloop/core'; -import { - BillingCustomerBody, - BillingPaymentSourceBody, - BillingPaymentMethodBody, - BillingInvoiceBody, - BillingUpdateInvoiceBody, - BillingPaymentStatusResponse, -} from '../models'; +import {BillingPaymentStatusResponse} from '../models'; const BASE = '/billing'; /** - * Sandbox controller that exercises the full IService interface from - * loopback4-billing — customer CRUD, payment source, and invoice management. + * Billing service controller for invoice and payment intent queries. + * + * This controller provides additional billing endpoints that are not + * covered by BillingInvoiceController and BillingPaymentSourceController. * - * Bound to BillingComponentBindings.SDKProvider (StripeService). + * For customer CRUD, use BillingCustomerController. + * For payment source operations, use BillingPaymentSourceController. + * For invoice CRUD, use BillingInvoiceController. + * + * Bound to BillingComponentBindings.SDKProvider (StripeService/ChargeBeeService). */ export class BillingServiceController { constructor( @@ -44,362 +31,6 @@ export class BillingServiceController { private readonly billingService: IService, ) {} - // ------------------------------------------------------------------------- - // CUSTOMER - // ------------------------------------------------------------------------- - - /** - * Create a new customer in Stripe. - * - * Example body: - * ```json - * { - * "firstName": "John", - * "lastName": "Doe", - * "email": "john.doe@example.com", - * "phone": "+1234567890" - * } - * ``` - */ - @authorize({permissions: ['*']}) - @post(`${BASE}/customers`, { - summary: 'Create a new customer', - responses: { - [STATUS_CODE.OK]: { - description: 'Created customer object', - content: {'application/json': {schema: {type: 'object'}}}, - }, - }, - }) - async createCustomer( - @requestBody({ - content: { - 'application/json': { - schema: {...getModelSchemaRefSF(BillingCustomerBody)}, - }, - }, - }) - customerDto: BillingCustomerBody, - ): Promise { - return this.billingService.createCustomer(customerDto as TCustomer); - } - - /** - * Get a customer by external ID (Stripe customer ID, e.g. cus_XXXXX). - */ - @authorize({permissions: ['*']}) - @get(`${BASE}/customers/{customerId}`, { - summary: 'Get a customer by ID', - responses: { - [STATUS_CODE.OK]: { - description: 'Customer object', - content: {'application/json': {schema: {type: 'object'}}}, - }, - }, - }) - async getCustomer( - @param.path.string('customerId') customerId: string, - ): Promise { - return this.billingService.getCustomers(customerId); - } - - /** - * Update an existing customer (email, phone, billing address, etc.). - * - * Example body: - * ```json - * { - * "email": "new.email@example.com", - * "billingAddress": { "city": "New York", "country": "US" } - * } - * ``` - */ - @authorize({permissions: ['*']}) - @put(`${BASE}/customers/{customerId}`, { - summary: 'Update a customer', - responses: { - [STATUS_CODE.NO_CONTENT]: {description: 'Customer updated'}, - }, - }) - async updateCustomer( - @param.path.string('customerId') customerId: string, - @requestBody({ - content: { - 'application/json': { - schema: {...getModelSchemaRefSF(BillingCustomerBody)}, - }, - }, - }) - customerDto: Partial, - ): Promise { - await this.billingService.updateCustomerById( - customerId, - customerDto as Partial, - ); - } - - /** - * Delete (permanently remove) a customer from Stripe. - */ - @authorize({permissions: ['*']}) - @del(`${BASE}/customers/{customerId}`, { - summary: 'Delete a customer', - responses: { - [STATUS_CODE.NO_CONTENT]: {description: 'Customer deleted'}, - }, - }) - async deleteCustomer( - @param.path.string('customerId') customerId: string, - ): Promise { - await this.billingService.deleteCustomer(customerId); - } - - // ------------------------------------------------------------------------- - // PAYMENT SOURCE - // ------------------------------------------------------------------------- - - /** - * Attach a payment method to a customer (via Stripe token). - * - * Example body: - * ```json - * { - * "customerId": "cus_XXXXX", - * "options": { "token": "tok_visa" } - * } - * ``` - */ - @authorize({permissions: ['*']}) - @post(`${BASE}/payment-sources`, { - summary: 'Create (attach) a payment source to a customer', - responses: { - [STATUS_CODE.OK]: { - description: 'Payment source created', - content: {'application/json': {schema: {type: 'object'}}}, - }, - }, - }) - async createPaymentSource( - @requestBody({ - content: { - 'application/json': { - schema: {...getModelSchemaRefSF(BillingPaymentSourceBody)}, - }, - }, - }) - paymentDto: BillingPaymentSourceBody, - ): Promise { - return this.billingService.createPaymentSource( - paymentDto as TPaymentSource, - ); - } - - /** - * Retrieve a payment source (payment method) by its ID. - */ - @authorize({permissions: ['*']}) - @get(`${BASE}/payment-sources/{paymentSourceId}`, { - summary: 'Retrieve a payment source by ID', - responses: { - [STATUS_CODE.OK]: { - description: 'Payment source object', - content: {'application/json': {schema: {type: 'object'}}}, - }, - }, - }) - async retrievePaymentSource( - @param.path.string('paymentSourceId') paymentSourceId: string, - ): Promise { - return this.billingService.retrievePaymentSource(paymentSourceId); - } - - /** - * Detach (delete) a payment source from a customer. - */ - @authorize({permissions: ['*']}) - @del(`${BASE}/payment-sources/{paymentSourceId}`, { - summary: 'Delete (detach) a payment source', - responses: { - [STATUS_CODE.NO_CONTENT]: {description: 'Payment source deleted'}, - }, - }) - async deletePaymentSource( - @param.path.string('paymentSourceId') paymentSourceId: string, - ): Promise { - await this.billingService.deletePaymentSource(paymentSourceId); - } - - /** - * Apply a payment to an invoice (pay out-of-band or via payment source). - * - * Example body for bank transfer: - * ```json - * { - * "paymentMethod": "bank_transfer", - * "comment": "Paid via wire transfer" - * } - * ``` - * - * Example body for saved payment source: - * ```json - * { - * "paymentMethod": "payment_source", - * "paymentSourceId": "pm_XXXXX" - * } - * ``` - */ - @authorize({permissions: ['*']}) - @post(`${BASE}/invoices/{invoiceId}/apply-payment`, { - summary: 'Apply a payment to an invoice', - responses: { - [STATUS_CODE.OK]: { - description: 'Invoice after payment applied', - content: {'application/json': {schema: {type: 'object'}}}, - }, - }, - }) - async applyPaymentSourceForInvoice( - @param.path.string('invoiceId') invoiceId: string, - @requestBody({ - content: { - 'application/json': { - schema: {...getModelSchemaRefSF(BillingPaymentMethodBody)}, - }, - }, - }) - transaction: BillingPaymentMethodBody, - ): Promise { - return this.billingService.applyPaymentSourceForInvoice( - invoiceId, - transaction as Transaction, - ); - } - - // ------------------------------------------------------------------------- - // INVOICE (direct / one-time) - // ------------------------------------------------------------------------- - - /** - * Create a one-time invoice for a customer with custom line items. - * - * Example body: - * ```json - * { - * "customerId": "cus_XXXXX", - * "currencyCode": "usd", - * "charges": [ - * { "amount": 5000, "description": "Setup fee" }, - * { "amount": 2000, "description": "Training" } - * ] - * } - * ``` - */ - @authorize({permissions: ['*']}) - @post(`${BASE}/invoices`, { - summary: 'Create a one-time invoice with custom line items', - responses: { - [STATUS_CODE.OK]: { - description: 'Created invoice object', - content: {'application/json': {schema: {type: 'object'}}}, - }, - }, - }) - async createInvoice( - @requestBody({ - content: { - 'application/json': { - schema: {...getModelSchemaRefSF(BillingInvoiceBody)}, - }, - }, - }) - invoice: BillingInvoiceBody, - ): Promise { - return this.billingService.createInvoice(invoice as TInvoice); - } - - /** - * Retrieve a specific invoice by ID. - */ - @authorize({permissions: ['*']}) - @get(`${BASE}/invoices/{invoiceId}`, { - summary: 'Retrieve an invoice by ID', - responses: { - [STATUS_CODE.OK]: { - description: 'Invoice object', - content: {'application/json': {schema: {type: 'object'}}}, - }, - }, - }) - async retrieveInvoice( - @param.path.string('invoiceId') invoiceId: string, - ): Promise { - return this.billingService.retrieveInvoice(invoiceId); - } - - /** - * Update the shipping address of an existing invoice. - * - * Example body: - * ```json - * { - * "customerId": "cus_XXXXX", - * "currencyCode": "usd", - * "shippingAddress": { - * "firstName": "John", - * "lastName": "Doe", - * "line1": "123 Main St", - * "city": "San Francisco", - * "state": "CA", - * "zip": "94107", - * "country": "US" - * } - * } - * ``` - */ - @authorize({permissions: ['*']}) - @put(`${BASE}/invoices/{invoiceId}`, { - summary: 'Update an invoice (shipping address)', - responses: { - [STATUS_CODE.OK]: { - description: 'Updated invoice object', - content: {'application/json': {schema: {type: 'object'}}}, - }, - }, - }) - async updateInvoice( - @param.path.string('invoiceId') invoiceId: string, - @requestBody({ - content: { - 'application/json': { - schema: {...getModelSchemaRefSF(BillingUpdateInvoiceBody)}, - }, - }, - }) - invoice: BillingUpdateInvoiceBody, - ): Promise { - return this.billingService.updateInvoice( - invoiceId, - invoice as Partial, - ); - } - - /** - * Delete a draft invoice permanently. - * Only draft invoices can be deleted in Stripe. - */ - @authorize({permissions: ['*']}) - @del(`${BASE}/invoices/{invoiceId}`, { - summary: 'Delete a draft invoice', - responses: { - [STATUS_CODE.NO_CONTENT]: {description: 'Invoice deleted'}, - }, - }) - async deleteInvoice( - @param.path.string('invoiceId') invoiceId: string, - ): Promise { - await this.billingService.deleteInvoice(invoiceId); - } - /** * Check whether an invoice has been paid. */ @@ -424,10 +55,6 @@ export class BillingServiceController { return {paid}; } - // ------------------------------------------------------------------------- - // INVOICE PDF - // ------------------------------------------------------------------------- - /** * Get PDF download URL for an invoice. * @@ -549,9 +176,6 @@ export class BillingServiceController { }, }, }) - @response(STATUS_CODE.OK, { - description: 'PDF information retrieved successfully', - }) async getInvoicePdf( @param.path.string('invoiceId') invoiceId: string, ): Promise { @@ -560,10 +184,6 @@ export class BillingServiceController { return pdfInfo; } - // ------------------------------------------------------------------------- - // INVOICE PAYMENT DETAILS - // ------------------------------------------------------------------------- - /** * Get payment details for an invoice. * @@ -684,19 +304,12 @@ export class BillingServiceController { }, }, }) - @response(STATUS_CODE.OK, { - description: 'Payment details retrieved successfully', - }) async getInvoicePaymentDetails( @param.path.string('invoiceId') invoiceId: string, ): Promise { return this.billingService.getInvoicePaymentDetails(invoiceId); } - // ------------------------------------------------------------------------- - // PAYMENT INTENT OPERATIONS - // ------------------------------------------------------------------------- - /** * Get payment intent details by ID. * @@ -798,9 +411,6 @@ export class BillingServiceController { }, }, }) - @response(STATUS_CODE.OK, { - description: 'Payment intent retrieved successfully', - }) async getPaymentIntent( @param.path.string('paymentIntentId') paymentIntentId: string, ): Promise { diff --git a/services/subscription-service/src/controllers/billing-webhook.controller.ts b/services/subscription-service/src/controllers/billing-webhook.controller.ts deleted file mode 100644 index 886319f..0000000 --- a/services/subscription-service/src/controllers/billing-webhook.controller.ts +++ /dev/null @@ -1,268 +0,0 @@ -import {inject, intercept} from '@loopback/core'; -import {post, requestBody, HttpErrors} from '@loopback/rest'; -import {authorize} from 'loopback4-authorization'; -import { - BillingComponentBindings, - ISubscriptionService, - TSubscriptionResult, -} from 'loopback4-billing'; -import {repository} from '@loopback/repository'; -import {WEBHOOK_VERIFIER} from '../keys'; -import {InvoiceRepository} from '../repositories'; -import {IContent, IPayload, IWebhookPayload, IWebhookContent} from '../types'; - -/** - * Chargebee webhook event types handled by this controller. - * Extend this enum as your integration needs grow. - */ -enum ChargebeeEvent { - SUBSCRIPTION_CREATED = 'subscription_created', - SUBSCRIPTION_ACTIVATED = 'subscription_activated', - SUBSCRIPTION_CHANGED = 'subscription_changed', - SUBSCRIPTION_CANCELLED = 'subscription_cancelled', - SUBSCRIPTION_PAUSED = 'subscription_paused', - SUBSCRIPTION_RESUMED = 'subscription_resumed', - SUBSCRIPTION_RENEWED = 'subscription_renewed', - PAYMENT_SUCCEEDED = 'payment_succeeded', - PAYMENT_FAILED = 'payment_failed', -} - -/** - * Webhook controller for Chargebee billing events. - * - * Exposes POST /webhooks/billing-payment — the URL you register in - * Chargebee Settings → API Keys → Webhooks. - * - * Follows Approach C: injects SubscriptionProvider directly as - * ISubscriptionService so no BillingProvider proxy is involved. - * - * To add Chargebee webhook signature verification, bind a custom - * interceptor to BILLING_WEBHOOK_VERIFIER in application.ts. - */ -export class BillingWebhookController { - constructor( - @inject(BillingComponentBindings.SDKProvider) - private readonly billingService: ISubscriptionService, - @repository(InvoiceRepository) - public invoiceRepository: InvoiceRepository, - ) {} - - @authorize({permissions: ['*']}) - @intercept(WEBHOOK_VERIFIER) - @post('/webhooks/billing-payment', { - summary: 'Chargebee webhook receiver', - description: - 'Register this URL in Chargebee Settings → API Keys → Webhooks. ' + - 'Handles subscription and payment lifecycle events.', - responses: { - '200': { - description: 'Event acknowledged', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - received: {type: 'boolean', example: true}, - event: {type: 'string', example: 'subscription_activated'}, - }, - }, - }, - }, - }, - }, - }) - async handleWebhook( - @requestBody({ - description: 'Chargebee webhook payload', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - // eslint-disable-next-line @typescript-eslint/naming-convention - event_type: {type: 'string'}, - content: {type: 'object'}, - }, - }, - }, - }, - }) - payload: IWebhookPayload, - ): Promise<{received: boolean; event: string}> { - const eventType: string = payload?.event_type ?? ''; - const content = payload?.content ?? {}; - - const eventHandlers: Record< - string, - (content: IWebhookContent) => Promise - > = { - [ChargebeeEvent.SUBSCRIPTION_CREATED]: - this.onSubscriptionCreated.bind(this), - [ChargebeeEvent.SUBSCRIPTION_ACTIVATED]: - this.onSubscriptionActivated.bind(this), - [ChargebeeEvent.SUBSCRIPTION_CHANGED]: - this.onSubscriptionChanged.bind(this), - [ChargebeeEvent.SUBSCRIPTION_CANCELLED]: - this.onSubscriptionCancelled.bind(this), - [ChargebeeEvent.SUBSCRIPTION_PAUSED]: - this.onSubscriptionPaused.bind(this), - [ChargebeeEvent.SUBSCRIPTION_RESUMED]: - this.onSubscriptionResumed.bind(this), - [ChargebeeEvent.SUBSCRIPTION_RENEWED]: - this.onSubscriptionRenewed.bind(this), - [ChargebeeEvent.PAYMENT_SUCCEEDED]: this.onPaymentSucceeded.bind(this), - [ChargebeeEvent.PAYMENT_FAILED]: this.onPaymentFailed.bind(this), - }; - - await eventHandlers[eventType]?.(content).catch(() => undefined); - - return {received: true, event: eventType}; - } - - /** - * Legacy webhook handler for backward compatibility. - * Handles payment status updates. - */ - @authorize({ - permissions: ['*'], - }) - @intercept(WEBHOOK_VERIFIER) - @post('/webhooks/billing-payment/legacy') - async handleWebhookLegacy(@requestBody() payload: IPayload): Promise { - const content = payload.content; - await this.handlePayment(content); - } - - /** - * Fired when a subscription is first created in Chargebee. - * The subscription may be in `in_trial` or `active` state. - */ - private async onSubscriptionCreated(content: IWebhookContent): Promise { - this.mapChargebeeSubscription(content.subscription); - } - - /** - * Fired when a subscription transitions from trial/pending to active. - * This is the event to trigger provisioning in your platform. - * - * Calls billingService.getSubscription() to verify the current state - * directly from Chargebee before acting on it. - */ - private async onSubscriptionActivated( - content: IWebhookContent, - ): Promise { - await this.verifySubscriptionState(content); - } - - /** - * Fired when a subscription plan, quantity or add-ons change. - * - * Calls billingService.getSubscription() to get the updated plan details - * directly from Chargebee. - */ - private async onSubscriptionChanged(content: IWebhookContent): Promise { - await this.verifySubscriptionState(content); - } - - /** - * Fetches and verifies the current subscription state from Chargebee. - */ - private async verifySubscriptionState( - content: IWebhookContent, - ): Promise { - const subscriptionId: string = content.subscription?.id ?? ''; - if (subscriptionId) { - await this.billingService.getSubscription(subscriptionId); - } - } - - /** - * Fired when a subscription is cancelled (immediately or at period end). - */ - private async onSubscriptionCancelled( - content: IWebhookContent, - ): Promise { - this.mapChargebeeSubscription(content.subscription); - } - - /** - * Fired when a subscription enters the paused state. - */ - private async onSubscriptionPaused(content: IWebhookContent): Promise { - this.mapChargebeeSubscription(content.subscription); - } - - /** - * Fired when a paused subscription is resumed. - */ - private async onSubscriptionResumed(content: IWebhookContent): Promise { - this.mapChargebeeSubscription(content.subscription); - } - - /** - * Fired at the start of each new billing period (renewal). - */ - private async onSubscriptionRenewed(content: IWebhookContent): Promise { - this.mapChargebeeSubscription(content.subscription); - } - - /** - * Fired when a payment is successfully collected. - * - * Calls billingService.getInvoicePriceDetails() to get the full - * price breakdown (total, tax, amount excluding tax) from Chargebee. - */ - private async onPaymentSucceeded(content: IWebhookContent): Promise { - const invoiceId: string = content.invoice?.id ?? ''; - - if (invoiceId) { - await this.billingService.getInvoicePriceDetails(invoiceId); - } - } - - /** - * Fired when a payment attempt fails (card decline, insufficient funds, etc.). - * Chargebee will retry automatically per your dunning settings. - */ - private async onPaymentFailed(content: IWebhookContent): Promise { - const invoice = content.invoice; - - if (!invoice?.id) { - throw new HttpErrors.UnprocessableEntity( - '[BillingWebhook] Payment failed event missing invoice ID', - ); - } - } - - /** - * Maps a raw Chargebee subscription object from a webhook payload to the - * provider-agnostic TSubscriptionResult. - * - * Webhook payloads use the same field names as the Chargebee REST API, so - * this mirrors ChargebeeSubscriptionAdapter.adaptToModel(). - */ - private mapChargebeeSubscription( - raw: IWebhookContent['subscription'], - ): TSubscriptionResult { - return { - id: raw?.id ?? '', - status: raw?.status ?? '', - customerId: raw?.customer_id ?? '', - currentPeriodStart: raw?.current_term_start, - currentPeriodEnd: raw?.current_term_end, - cancelAtPeriodEnd: raw?.cancel_at_period_end ?? false, - }; - } - - /** - * Legacy payment handler for backward compatibility. - */ - private async handlePayment(content: IContent): Promise { - const invoice = await this.invoiceRepository.find({ - where: {invoiceId: content.invoice.id}, - }); - await this.invoiceRepository.updateById(invoice[0].id, { - invoiceStatus: content.invoice.status, - }); - } -} diff --git a/services/subscription-service/src/controllers/index.ts b/services/subscription-service/src/controllers/index.ts index f33a320..8401448 100644 --- a/services/subscription-service/src/controllers/index.ts +++ b/services/subscription-service/src/controllers/index.ts @@ -12,7 +12,6 @@ export * from './plan-features.controller'; export * from './subscription-invoice.controller'; export * from './billing-service.controller'; export * from './billing-subscription.controller'; -export * from './billing-webhook.controller'; export * from './webhook.controller'; export * from './billing-customer.controller'; export * from './billing-invoice.controller'; diff --git a/services/subscription-service/src/types.ts b/services/subscription-service/src/types.ts index 1e94d4d..19423c7 100644 --- a/services/subscription-service/src/types.ts +++ b/services/subscription-service/src/types.ts @@ -54,69 +54,3 @@ export interface ISubscriptionServiceConfig extends IServiceConfig { useCustomSequence: boolean; useSequelize?: boolean; } - -/** - * Type alias for flexible webhook additional data. - * Reduces union type complexity by grouping related types. - */ -type PrimitiveValue = string | number | boolean; -type OptionalValue = object | null | undefined; -type WebhookAdditionalData = PrimitiveValue | OptionalValue; - -/** - * Webhook payload structure from billing providers (Chargebee/Stripe). - */ -export interface IWebhookPayload { - // eslint-disable-next-line @typescript-eslint/naming-convention - event_type: string; - content: IWebhookContent; - [key: string]: WebhookAdditionalData; -} - -/** - * Webhook content structure containing subscription, invoice, and transaction data. - */ -export interface IWebhookContent { - subscription?: IWebhookSubscription; - invoice?: IWebhookInvoice; - transaction?: IWebhookTransaction; - [key: string]: WebhookAdditionalData; -} - -/** - * Subscription data from webhook payload. - */ -export interface IWebhookSubscription { - id: string; - status: string; - // eslint-disable-next-line @typescript-eslint/naming-convention - customer_id: string; - // eslint-disable-next-line @typescript-eslint/naming-convention - current_term_start?: number; - // eslint-disable-next-line @typescript-eslint/naming-convention - current_term_end?: number; - // eslint-disable-next-line @typescript-eslint/naming-convention - cancel_at_period_end?: boolean; - [key: string]: WebhookAdditionalData; -} - -/** - * Invoice data from webhook payload. - */ -export interface IWebhookInvoice { - id: string; - // eslint-disable-next-line @typescript-eslint/naming-convention - currency_code?: string; - total?: number; - [key: string]: WebhookAdditionalData; -} - -/** - * Transaction data from webhook payload. - */ -export interface IWebhookTransaction { - amount?: number; - // eslint-disable-next-line @typescript-eslint/naming-convention - error_text?: string; - [key: string]: WebhookAdditionalData; -} From ee7e3cc76651cce39e1d09a5a229d0230b9b6ea7 Mon Sep 17 00:00:00 2001 From: Sourav Kashyap Date: Tue, 26 May 2026 15:04:40 +0530 Subject: [PATCH 6/6] feat(billing): align codebase with existing project structure --- package-lock.json | 1887 +++++++++-------- package.json | 4 +- .../controllers/billing-service.controller.ts | 308 +-- .../billing-subscription.controller.ts | 360 +--- .../src/models/dto/index.ts | 11 + .../dto/invoice-payment-details-dto.model.ts | 26 + .../src/models/dto/invoice-pdf-dto.model.ts | 20 + .../src/models/dto/invoice-price-dto.model.ts | 20 + .../models/dto/payment-intent-dto.model.ts | 35 + .../src/models/dto/price-dto.model.ts | 34 + .../src/models/dto/product-dto.model.ts | 20 + .../dto/subscription-create-dto.model.ts | 20 + .../subscription-create-response-dto.model.ts | 11 + .../dto/subscription-result-dto.model.ts | 26 + .../dto/subscription-update-dto.model.ts | 14 + .../src/models/dto/success-dto.model.ts | 11 + 16 files changed, 1345 insertions(+), 1462 deletions(-) create mode 100644 services/subscription-service/src/models/dto/invoice-payment-details-dto.model.ts create mode 100644 services/subscription-service/src/models/dto/invoice-pdf-dto.model.ts create mode 100644 services/subscription-service/src/models/dto/invoice-price-dto.model.ts create mode 100644 services/subscription-service/src/models/dto/payment-intent-dto.model.ts create mode 100644 services/subscription-service/src/models/dto/price-dto.model.ts create mode 100644 services/subscription-service/src/models/dto/product-dto.model.ts create mode 100644 services/subscription-service/src/models/dto/subscription-create-dto.model.ts create mode 100644 services/subscription-service/src/models/dto/subscription-create-response-dto.model.ts create mode 100644 services/subscription-service/src/models/dto/subscription-result-dto.model.ts create mode 100644 services/subscription-service/src/models/dto/subscription-update-dto.model.ts create mode 100644 services/subscription-service/src/models/dto/success-dto.model.ts diff --git a/package-lock.json b/package-lock.json index feeb77e..45f6e13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -86,17 +86,17 @@ } }, "node_modules/@aws-sdk/client-eventbridge": { - "version": "3.1068.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-eventbridge/-/client-eventbridge-3.1068.0.tgz", - "integrity": "sha512-vHorSGf+SAYymXMernZcvVlTUrmEOwemmEqQv8HLwaRuzfqlPYeAqN20y7O1N5YlVuRkTMNzB02pM141mJozdQ==", + "version": "3.1075.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-eventbridge/-/client-eventbridge-3.1075.0.tgz", + "integrity": "sha512-tilM71kC+XU0YKaoWbtvr2yJlOJGplltF98CXbNzyIpRAO5qOM1RKGJCmn7AAV/cz7FpwrnK0TkKYFN5rjpZ8A==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.974.20", - "@aws-sdk/credential-provider-node": "^3.972.55", - "@aws-sdk/signature-v4-multi-region": "^3.996.34", - "@aws-sdk/types": "^3.973.12", + "@aws-sdk/core": "^3.974.23", + "@aws-sdk/credential-provider-node": "^3.972.58", + "@aws-sdk/signature-v4-multi-region": "^3.996.35", + "@aws-sdk/types": "^3.973.13", "@smithy/core": "^3.24.6", "@smithy/fetch-http-handler": "^5.4.6", "@smithy/node-http-handler": "^4.7.6", @@ -108,17 +108,17 @@ } }, "node_modules/@aws-sdk/client-sqs": { - "version": "3.1068.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sqs/-/client-sqs-3.1068.0.tgz", - "integrity": "sha512-hatCfVf61RsSO5Qjrf7JKln4KiPDprXNJhpx9DNOmyeW1v+i7KNhbawk4d1wAnQ72YciOfhAhyRFec6UZqcnzQ==", + "version": "3.1075.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sqs/-/client-sqs-3.1075.0.tgz", + "integrity": "sha512-ZHowzLHFTIz482Z98Y8GtszLN7Q1YvC74xxExwr2rbKJeNWTXHVoMVwlq/jv5Kyf0elVAGmoID12Gf6aSdWy0Q==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.974.20", - "@aws-sdk/credential-provider-node": "^3.972.55", - "@aws-sdk/middleware-sdk-sqs": "^3.972.30", - "@aws-sdk/types": "^3.973.12", + "@aws-sdk/core": "^3.974.23", + "@aws-sdk/credential-provider-node": "^3.972.58", + "@aws-sdk/middleware-sdk-sqs": "^3.972.31", + "@aws-sdk/types": "^3.973.13", "@smithy/core": "^3.24.6", "@smithy/fetch-http-handler": "^5.4.6", "@smithy/node-http-handler": "^4.7.6", @@ -130,13 +130,13 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.974.20", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.20.tgz", - "integrity": "sha512-7sDi2B2N3mc3nf1nz6FyEx/FCrJ1N1QnBmraHHQNabFaeAh2IaOOLml48/rHOD1bICHgTRkbBgNTvUzEr5Z35g==", + "version": "3.974.23", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.23.tgz", + "integrity": "sha512-MiWR/uWjxjFXGzrE0Ghc5lWxUxzHsUWFhV+OX7M4cR9SrmrnZs6TXavnCWnzzdwJeFri34xQo81rvGNzK3c4BQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.12", - "@aws-sdk/xml-builder": "^3.972.29", + "@aws-sdk/types": "^3.973.13", + "@aws-sdk/xml-builder": "^3.972.31", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/core": "^3.24.6", "@smithy/signature-v4": "^5.4.6", @@ -149,13 +149,13 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.972.46", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.46.tgz", - "integrity": "sha512-+GPXVS2srMOlH74S+SmC1gVuP2TvUZ0siuC0onKO93q+udP+M72dmY8wJfVQ5CX9z/9X5A1HHwz5yRIGBtskvQ==", + "version": "3.972.49", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.49.tgz", + "integrity": "sha512-liB3yQNHCM9k/gu/w36XHMKPluT7HTlnGUhRbBGSISDQkcr/Sy1zsZabiuvQj8WG5yW573u9RehrBvvnIQ9OEQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.20", - "@aws-sdk/types": "^3.973.12", + "@aws-sdk/core": "^3.974.23", + "@aws-sdk/types": "^3.973.13", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" @@ -165,13 +165,13 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.972.48", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.48.tgz", - "integrity": "sha512-fA5loSdlocacRxyUXtpoHSMuk5rsIKRDzQYVMnMxjcmFeZshaJlJ8lymy/hYKji6sne/UmNGj5pxuEs6kq/Qcg==", + "version": "3.972.51", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.51.tgz", + "integrity": "sha512-XET0H2oofciJ5lMRWNIvRjAP7Q3wv2XT+JtJJEdhPWUMwe3TvQ9qcxonpu7vXmNngncvFpi4E2It+Tamas/naA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.20", - "@aws-sdk/types": "^3.973.12", + "@aws-sdk/core": "^3.974.23", + "@aws-sdk/types": "^3.973.13", "@smithy/core": "^3.24.6", "@smithy/fetch-http-handler": "^5.4.6", "@smithy/node-http-handler": "^4.7.6", @@ -183,20 +183,20 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.972.53", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.53.tgz", - "integrity": "sha512-ZfdhIOR41q8TcWEnUac+gCOb+O2LBWdHLmjedXpXz4IEFW2ppNuFcm6p0sMTavpM+zD5TYfpH5Gp7guRyqSgsQ==", + "version": "3.972.56", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.56.tgz", + "integrity": "sha512-IAmc61hbgQiHht9U3x0tnRwz0lzdwOwD/i9voRgdJrKamF+JtmrBOsW9GwB7mfFonNWOWL4qARWYrF8veEMe3w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.20", - "@aws-sdk/credential-provider-env": "^3.972.46", - "@aws-sdk/credential-provider-http": "^3.972.48", - "@aws-sdk/credential-provider-login": "^3.972.52", - "@aws-sdk/credential-provider-process": "^3.972.46", - "@aws-sdk/credential-provider-sso": "^3.972.52", - "@aws-sdk/credential-provider-web-identity": "^3.972.52", - "@aws-sdk/nested-clients": "^3.997.20", - "@aws-sdk/types": "^3.973.12", + "@aws-sdk/core": "^3.974.23", + "@aws-sdk/credential-provider-env": "^3.972.49", + "@aws-sdk/credential-provider-http": "^3.972.51", + "@aws-sdk/credential-provider-login": "^3.972.55", + "@aws-sdk/credential-provider-process": "^3.972.49", + "@aws-sdk/credential-provider-sso": "^3.972.55", + "@aws-sdk/credential-provider-web-identity": "^3.972.55", + "@aws-sdk/nested-clients": "^3.997.23", + "@aws-sdk/types": "^3.973.13", "@smithy/core": "^3.24.6", "@smithy/credential-provider-imds": "^4.3.7", "@smithy/types": "^4.14.3", @@ -207,14 +207,14 @@ } }, "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.972.52", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.52.tgz", - "integrity": "sha512-9hu2oR0qH7Fst5Tzdx+UWxm+w5zCXtErTLtOOW5hwwQc170CLwOeniRxyFY6s9mHfGEfC5zFukNBdKBwJR8mhQ==", + "version": "3.972.55", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.55.tgz", + "integrity": "sha512-hBBkANo3cDn+h2qxxzER4a+J8JCO9o9Z/YYmU7iky6AcaarX5RRdRcHNC6SLdwY0vAXQygn6soUbDqPn3GghaA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.20", - "@aws-sdk/nested-clients": "^3.997.20", - "@aws-sdk/types": "^3.973.12", + "@aws-sdk/core": "^3.974.23", + "@aws-sdk/nested-clients": "^3.997.23", + "@aws-sdk/types": "^3.973.13", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" @@ -224,18 +224,18 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.55", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.55.tgz", - "integrity": "sha512-zMGLa/dhESVqmCD7mmIFFKSwSFrJGScvCXcjvBZEVOOMauFS5JRQvLTMukFpMEFWiV6dTAlsen2ATDBulLPtbg==", + "version": "3.972.58", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.58.tgz", + "integrity": "sha512-OyCLVmSI7pZO8hxwNVX6pXhTVlJqRBTp+ijdEfJSUj0RyjHnF602OfAarOzGq6wkGodeFkYBt8MmJ6A6ycRgWw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "^3.972.46", - "@aws-sdk/credential-provider-http": "^3.972.48", - "@aws-sdk/credential-provider-ini": "^3.972.53", - "@aws-sdk/credential-provider-process": "^3.972.46", - "@aws-sdk/credential-provider-sso": "^3.972.52", - "@aws-sdk/credential-provider-web-identity": "^3.972.52", - "@aws-sdk/types": "^3.973.12", + "@aws-sdk/credential-provider-env": "^3.972.49", + "@aws-sdk/credential-provider-http": "^3.972.51", + "@aws-sdk/credential-provider-ini": "^3.972.56", + "@aws-sdk/credential-provider-process": "^3.972.49", + "@aws-sdk/credential-provider-sso": "^3.972.55", + "@aws-sdk/credential-provider-web-identity": "^3.972.55", + "@aws-sdk/types": "^3.973.13", "@smithy/core": "^3.24.6", "@smithy/credential-provider-imds": "^4.3.7", "@smithy/types": "^4.14.3", @@ -246,13 +246,13 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.972.46", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.46.tgz", - "integrity": "sha512-VUoNFBIjWrUN8NbFiQiuxQEgFjvziAlBRPK+ddh27aj65gk0BYu6bLZnrdrNZwpW6vAihtSUtEMQ1PUJ32QRPA==", + "version": "3.972.49", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.49.tgz", + "integrity": "sha512-C8h36lBuC/RnBSsjlO+dn6xZm3KbAl5vpJaVPAfQnMmz2/OISmKOc8XZcqMQgO2ADwBYNRMM6Kf3vz9G/TulMQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.20", - "@aws-sdk/types": "^3.973.12", + "@aws-sdk/core": "^3.974.23", + "@aws-sdk/types": "^3.973.13", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" @@ -262,15 +262,15 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.972.52", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.52.tgz", - "integrity": "sha512-nb2/n4o/HQf+FVpVbZe9vCTFngmuDoIsltMgLAtjixaKzvzhB4J8WSDFyWgnErgLHk55ctWH+I4PU+LIHhyffg==", + "version": "3.972.55", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.55.tgz", + "integrity": "sha512-1FkOz74Ea5QGS9jtIoXp55T/IkSS3spv+nLTT07fRY/+T5xmEOqaYBVIaEmX4zTNvbV6g2lrtlaVKWEoNyJt3w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.20", - "@aws-sdk/nested-clients": "^3.997.20", - "@aws-sdk/token-providers": "3.1066.0", - "@aws-sdk/types": "^3.973.12", + "@aws-sdk/core": "^3.974.23", + "@aws-sdk/nested-clients": "^3.997.23", + "@aws-sdk/token-providers": "3.1074.0", + "@aws-sdk/types": "^3.973.13", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" @@ -280,14 +280,14 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.972.52", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.52.tgz", - "integrity": "sha512-lKj6aRSGbqLmpYmM24bY7a1Xmfcq2vkE3hv8CSPYfc1yCu0BPu/XEJ1L4Fm61MsU6ULLNSG8UGsffNoFUBjESA==", + "version": "3.972.55", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.55.tgz", + "integrity": "sha512-g2BoECD1q01kTPByi56+VLVvdWDzMkKIcr77qixpqH0okw2t0U5CoPv+6S8v/D1Y2Wa6QKKtn6XAtDzP+Kfpvg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.20", - "@aws-sdk/nested-clients": "^3.997.20", - "@aws-sdk/types": "^3.973.12", + "@aws-sdk/core": "^3.974.23", + "@aws-sdk/nested-clients": "^3.997.23", + "@aws-sdk/types": "^3.973.13", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" @@ -297,12 +297,12 @@ } }, "node_modules/@aws-sdk/middleware-sdk-sqs": { - "version": "3.972.30", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sqs/-/middleware-sdk-sqs-3.972.30.tgz", - "integrity": "sha512-PVAj7VgWK/ZxCXnkgC4B7cdJyUN99Nsr7IEduHt4A1GieuB+ZnU5bSifHwapbr17wrFkmdxfSh+aA0Lj+Ads6w==", + "version": "3.972.31", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sqs/-/middleware-sdk-sqs-3.972.31.tgz", + "integrity": "sha512-56ifsBmK9bLn5EE/t6c0nmjOB1BO8cJDLkA1VOlsN1GR85ROqnaCwVDspqcwsLaBDgPlwyYNedoDIoT3t6Ho1A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.12", + "@aws-sdk/types": "^3.973.13", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" @@ -312,16 +312,16 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.997.20", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.20.tgz", - "integrity": "sha512-IYJuLpXp2DEILVQpQOy0PMpkftv0AHEOCn52o0atyOaumA0CdWQ3klPyXdViGYLbNpESsVFMVybvHUeZAuiGxA==", + "version": "3.997.23", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.23.tgz", + "integrity": "sha512-gO93ZPsI2bxeFZD42f1/qjDw6FAZkNZcKRO94LIiT03fzOmcJ9e/tunxjVjA1Rl69ClmVJzz8H3G9CdKef10PA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.974.20", - "@aws-sdk/signature-v4-multi-region": "^3.996.34", - "@aws-sdk/types": "^3.973.12", + "@aws-sdk/core": "^3.974.23", + "@aws-sdk/signature-v4-multi-region": "^3.996.35", + "@aws-sdk/types": "^3.973.13", "@smithy/core": "^3.24.6", "@smithy/fetch-http-handler": "^5.4.6", "@smithy/node-http-handler": "^4.7.6", @@ -333,12 +333,12 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.996.34", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.34.tgz", - "integrity": "sha512-mx1L5qlumSOt/nKM3BFaHE2HVkWwz0i4Bw0pyYO42FfX/FeLlo8YI6csC0gSPprEk6fTIqI+CZN9RwUwKd5krQ==", + "version": "3.996.35", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.35.tgz", + "integrity": "sha512-6L/VWs+Wch2stHemCGTmUNqKLMzURxQDK5boNG3Jn3kAOp71meDUuS5sbObpEvFxHDq0uWeSLFDNSYsjNt+Dlg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.12", + "@aws-sdk/types": "^3.973.13", "@smithy/signature-v4": "^5.4.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" @@ -348,14 +348,14 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.1066.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1066.0.tgz", - "integrity": "sha512-UqEUJq7dqa44hneLDUcX7UJy95cg8YqEWyakRpvIPnrNS3Mq+UlQHgCDGu5pvwAPtlIW4qcYbvW6reG6++FyvA==", + "version": "3.1074.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1074.0.tgz", + "integrity": "sha512-pv80IzgGW4RnXWtft692chZOM9i6PhebVsLCcnaM4dBEPZva2fE6FXAHs76G7Rc7s3yGyX/68G0nZMrUy+Vmpg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.20", - "@aws-sdk/nested-clients": "^3.997.20", - "@aws-sdk/types": "^3.973.12", + "@aws-sdk/core": "^3.974.23", + "@aws-sdk/nested-clients": "^3.997.23", + "@aws-sdk/types": "^3.973.13", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" @@ -365,9 +365,9 @@ } }, "node_modules/@aws-sdk/types": { - "version": "3.973.12", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.12.tgz", - "integrity": "sha512-43ajd1NF0RMgX5k0hxCNUyEdrtFUsb2aHT2QvpktSC/2Eyb2Jr/JPVqdp0XIoaHWikZJq5tNWSLO6kB5q2eMCA==", + "version": "3.973.13", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.13.tgz", + "integrity": "sha512-pEHZqRkAlHfnfAU9tK+WpKv/gBNjGJrHMgA3A0iYRGyswBS2t0pfez+lWlwktb3Bqa0ovh7w/QJTFwp3fDxLNg==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.14.3", @@ -378,9 +378,9 @@ } }, "node_modules/@aws-sdk/util-locate-window": { - "version": "3.965.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.7.tgz", - "integrity": "sha512-M0D6oIpohdNHjc7udzTHEQyot0+0iuA36jc2I9Hps+f/GtKi2HO/pyijQnCnNcwZqLB5+rtn81z3eZK/GyjAmA==", + "version": "3.965.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.8.tgz", + "integrity": "sha512-uUbMs1cBZPafD0ohUj6EwNf0fPZ534NvBxHox4hjX+0Rxq5paSYUem7+hi833pYrzrcnBATKIYpR02MDXT5M9g==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -390,13 +390,12 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.972.29", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.29.tgz", - "integrity": "sha512-fk0niuGFxfi8yIJuMVM4mhwObkiQSuwZFj3tAPrLVx64Pk3BkrEIpqjzHKY4hKoEBUD6Jg/S74Zj9jy+5F3DnQ==", + "version": "3.972.31", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.31.tgz", + "integrity": "sha512-SzE4Pgyl+hDF+BuyuzxUSpwnuUu9lJuO1YGgteG89/4Qv0+2IQiVQqdbPV32IozLvXWQChPQcdkk/sKvb1QHiQ==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.14.3", - "fast-xml-parser": "5.7.3", "tslib": "^2.6.2" }, "engines": { @@ -1131,13 +1130,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", @@ -1149,17 +1141,6 @@ "concat-map": "0.0.1" } }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -1255,13 +1236,6 @@ "node": ">=10.10.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", @@ -2078,76 +2052,6 @@ "node": "20 || 22 || 24" } }, - "node_modules/@loopback/build/node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@loopback/build/node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@loopback/build/node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@loopback/build/node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/@loopback/build/node_modules/@types/node": { "version": "20.19.43", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", @@ -2158,43 +2062,20 @@ "undici-types": "~6.21.0" } }, - "node_modules/@loopback/build/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@loopback/build/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "node_modules/@loopback/build/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "18 || 20 || >=22" } }, - "node_modules/@loopback/build/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, "node_modules/@loopback/build/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "dev": true, "license": "MIT", "dependencies": { @@ -2216,13 +2097,6 @@ "wrap-ansi": "^6.2.0" } }, - "node_modules/@loopback/build/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, "node_modules/@loopback/build/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -2342,7 +2216,16 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, - "license": "ISC" + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/@loopback/build/node_modules/nyc": { "version": "18.0.0", @@ -2448,77 +2331,6 @@ "node": ">=8" } }, - "node_modules/@loopback/build/node_modules/rimraf": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", - "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^10.3.7" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@loopback/build/node_modules/rimraf/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@loopback/build/node_modules/rimraf/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@loopback/build/node_modules/rimraf/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@loopback/build/node_modules/spawn-wrap": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-3.0.0.tgz", @@ -2930,18 +2742,6 @@ "@loopback/rest": "^15.0.1" } }, - "node_modules/@loopback/rest-explorer/node_modules/ejs": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-5.0.2.tgz", - "integrity": "sha512-IpbUaI/CAW86l3f+T8zN0iggSc0LmMZLcIW5eRVStLVNCoTXkE0YlncbbH50fp8Cl6zHIky0sW2uUbhBqGw0Jw==", - "license": "Apache-2.0", - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.12.18" - } - }, "node_modules/@loopback/rest/node_modules/ajv": { "version": "8.20.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", @@ -3207,18 +3007,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@nodable/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/nodable" - } - ], - "license": "MIT" - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3862,9 +3650,9 @@ } }, "node_modules/@nx/devkit": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-22.7.5.tgz", - "integrity": "sha512-/63ziS7kdHXYTLLhwWBu9hFwoFFT8xf+PkcQjsNdPqc5JmkYkSew0cE/vp5ORgBpGLWWnFPJgmfqjbJoO2C7jA==", + "version": "22.7.6", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-22.7.6.tgz", + "integrity": "sha512-EXY+3o1Y6dmg49Wte3cfjsV8jR//1XJ+HVYWF7zarbsbhcrjtuh1eYDrhOkAiVbLMhOfkpf/PfGwBr5yL+6kPQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3880,38 +3668,90 @@ "nx": ">= 21 <= 23 || ^22.0.0-0" } }, - "node_modules/@nx/nx-darwin-arm64": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-22.7.5.tgz", - "integrity": "sha512-eoPtwx0qZqvRUD+VVOHm150AlSYwYoPxkDHBBGqKCn5nzPspb0lLWw8q83crM/L1M928YgK0WmGf3C++7eqsTA==", - "cpu": [ - "arm64" - ], + "node_modules/@nx/devkit/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "engines": { + "node": "18 || 20 || >=22" + } }, - "node_modules/@nx/nx-darwin-x64": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-22.7.5.tgz", - "integrity": "sha512-VLOn/ZoEn3HfjSj+yIHLCM56/el79r+9I28CkZNHaSXJQWZ3edSkcgcfYjVxCurpN2VEwDQHLBeFCH8M+lQ7wQ==", - "cpu": [ - "x64" - ], + "node_modules/@nx/devkit/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@nx/devkit/node_modules/ejs": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-5.0.1.tgz", + "integrity": "sha512-COqBPFMxuPTPspXl2DkVYaDS3HtrD1GpzOGkNTJ1IYkifq/r9h8SVEFrjA3D9/VJGOEoMQcrlhpntcSUrM8k6A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.12.18" + } + }, + "node_modules/@nx/devkit/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nx/nx-darwin-arm64": { + "version": "22.7.6", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-22.7.6.tgz", + "integrity": "sha512-FBKG9Tqrl8H0A4oIekVTJNNq+tRMrkyF0fLBV4Cpi9Hm1ejA8dl5gpEG/o5V7MwiClVddDwtXp1ymdCa2qSoyg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@nx/nx-darwin-x64": { + "version": "22.7.6", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-22.7.6.tgz", + "integrity": "sha512-IpXL2NYYBDj9k+7u6jEdKpWOqPGZNBpGcW+LyY3l5qoyaa0lq3gzZrDNae9XP2dmGwnUn1kD9OTgq9kmkcK3Bg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" ] }, "node_modules/@nx/nx-freebsd-x64": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-22.7.5.tgz", - "integrity": "sha512-LEVer/E2xfGvK9Go+imMQoEninOoq/38Z2bhV1SD3AThXrp1xaLFVkW5jQ6juebeVkAeztEoMLFlr576egS0vw==", + "version": "22.7.6", + "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-22.7.6.tgz", + "integrity": "sha512-1QVXcfu0FiVyfd6GLBKIWNuGtPh/Pbjhwyhucc/kI2EgV1f+Sl6kePDp2pI8PQ00HKVLy/NqAwgcx05YyGtYYg==", "cpu": [ "x64" ], @@ -3923,9 +3763,9 @@ ] }, "node_modules/@nx/nx-linux-arm-gnueabihf": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-22.7.5.tgz", - "integrity": "sha512-NP27EFGpmFJM6RL1Ey/AFJ7gA2xuqtIHaw6jjSNGvfrnZRUNaway30GrVaGGeODf0DsvAty/unqoBMPy6kDHbw==", + "version": "22.7.6", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-22.7.6.tgz", + "integrity": "sha512-YD6vjl39oxtR8kxxq9Ow/bFahb61M1soVMPz7dEhV3weM3/h3yRLdcscibvayIJ5nBFLxlYpXHG/n9qWxon8tw==", "cpu": [ "arm" ], @@ -3937,9 +3777,9 @@ ] }, "node_modules/@nx/nx-linux-arm64-gnu": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-22.7.5.tgz", - "integrity": "sha512-QLnkJl3HkHsPfpLiNiAiMfpfAeFpic0U1diAxF8RqChOkCpQ7ulvyBVgE1UrQxvhd+gFQ3ed5RNDxtCRw8nTiw==", + "version": "22.7.6", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-22.7.6.tgz", + "integrity": "sha512-kMIpH8KvNUsbekkbcWGI+h3FnVOr+jL6cHv7r8Mvh1SusQzgxYcxE8iy0mPXcdbkfF5eaU9RcLB+uIKTyt8OmQ==", "cpu": [ "arm64" ], @@ -3951,9 +3791,9 @@ ] }, "node_modules/@nx/nx-linux-arm64-musl": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-22.7.5.tgz", - "integrity": "sha512-cEP6KmwBgnb38+jTTaibWCjwXcHmigqhTfy0tN1be7WZr6bHxbqNLsXqKRN70PSNA3HouZcxw1cdRL8tqbPBBA==", + "version": "22.7.6", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-22.7.6.tgz", + "integrity": "sha512-FsRoirT/wx7vdGl7fvkssyBvzu/JoZYYosBPDycaPg5LxSb2lPrvzcCcqG3c++jtSOn/IbbjGBA1bi8ApQ/Ulw==", "cpu": [ "arm64" ], @@ -3965,9 +3805,9 @@ ] }, "node_modules/@nx/nx-linux-x64-gnu": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-22.7.5.tgz", - "integrity": "sha512-tbaX1tZCSpGifDNBfDdEZAMxVF3Yg4bhFP/bm1needc0diqb+Zflc0u5tM5/6BWDMITQDwenJVsNiQ8ZdtJURA==", + "version": "22.7.6", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-22.7.6.tgz", + "integrity": "sha512-SA1nPymYHgi2NP2ra9r1hrSc+6jTR5xPTzjUhzNFXAColvgUO/aP0Gn+dXhwOtzzau8fKVc3K1qaHi+kIHT54g==", "cpu": [ "x64" ], @@ -3979,9 +3819,9 @@ ] }, "node_modules/@nx/nx-linux-x64-musl": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-22.7.5.tgz", - "integrity": "sha512-H0M7csOZIgPT822LqjxSXzf4MXRND15vIkAQe3F3Jlr3Si8LC3tzbL52aVcRfgb8MF/xOB5U47mSwxWt1M2bPQ==", + "version": "22.7.6", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-22.7.6.tgz", + "integrity": "sha512-Aeol4pSRuMuAEC16Se+WS+Xar8W6pymCDmRaIWNLhbDG/+JQqvvuW1izrMLD9DY1Vpn+acN3bHLvdHhTux/DZw==", "cpu": [ "x64" ], @@ -3993,9 +3833,9 @@ ] }, "node_modules/@nx/nx-win32-arm64-msvc": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-22.7.5.tgz", - "integrity": "sha512-JTcZch9YAnDL1gbhqePz3DZ4x7iYemLn1yJzrjbbXAmXju2eiiJiZvJJHbV06+SP9HKXDT8RjTKuAWTdVxnHug==", + "version": "22.7.6", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-22.7.6.tgz", + "integrity": "sha512-iNdxFfvkePnAYRhKyEEVfskiiL2QdiALeeZG+STh+rfD15cUO2YiQU+iQzgpzqmi9J2QjhGykIdFA6E/8v09lg==", "cpu": [ "arm64" ], @@ -4007,9 +3847,9 @@ ] }, "node_modules/@nx/nx-win32-x64-msvc": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-22.7.5.tgz", - "integrity": "sha512-ngcMyHdBJ9FSz2nHdbZ7gtJlFq0O2b05sPAsVMkZ18CKzdaA1qrBDJfsMO49hPCny505eiT766+CkKdaCDl5kA==", + "version": "22.7.6", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-22.7.6.tgz", + "integrity": "sha512-thikDrOhCbUdzx5fQcpM34qZ7LV5RiszoNVG4m4TS3wAvx2bl+/qnVL6F6PwMlS6B/RVv6TqPhTuGHT7sjEGTA==", "cpu": [ "x64" ], @@ -4896,13 +4736,13 @@ } }, "node_modules/@smithy/core": { - "version": "3.24.7", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.7.tgz", - "integrity": "sha512-KoUi4M1f3BG6kzN1FnCwL7oyFptTbyBJKjR6yhSib+JHRdUmM1o+VwsFtJ66NZCkCzVfJMWRHJNo0R0jznp0Pg==", + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.26.0.tgz", + "integrity": "sha512-mLUktFAn+Pa2agl1J7VgtYNFWCX8/b4GMJSK1hCu4YCvtBfM6F8Os3EP4ry+DFFlXOf3wyvlgXhuUdFoy52D3g==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.14.4", + "@smithy/types": "^4.15.0", "tslib": "^2.6.2" }, "engines": { @@ -4910,13 +4750,13 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.3.9", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.3.9.tgz", - "integrity": "sha512-ZlfJ/4Fa3jYb+3eaohPfG9utX9HmdhFNcFtpoGAhUhdynAOmGXtmigbi7eEiONKM+ykHw8RwKuDEb85Lx7t7fA==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.4.2.tgz", + "integrity": "sha512-18UMDMyrAbDcpmL1gLUA7ww0fRTcdCrSjSJOi2Sbld+tVjwD/pW+OAwjlScFLR7vvBnhZrIPQ7kVuTf1mnJLug==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.24.7", - "@smithy/types": "^4.14.4", + "@smithy/core": "^3.26.0", + "@smithy/types": "^4.15.0", "tslib": "^2.6.2" }, "engines": { @@ -4924,13 +4764,13 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.4.7", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.4.7.tgz", - "integrity": "sha512-NslaM2ir0N2hisDmzXLstPaVINZheh8SokyOC++kzFPloZucL2R7Y7bS57mSzx/1Fc/fqmn7twjkeezTTrV0EA==", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.5.2.tgz", + "integrity": "sha512-Ei/UK/QMhq0rKaMqGPlOAkE2yS9DZeYmZdk1RAKc3vp3zxgleZHZyBLlZv8yLsxljX4svCRuMTD6u3LLIcU4Bg==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.24.7", - "@smithy/types": "^4.14.4", + "@smithy/core": "^3.26.0", + "@smithy/types": "^4.15.0", "tslib": "^2.6.2" }, "engines": { @@ -4950,13 +4790,13 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.7.8.tgz", - "integrity": "sha512-f+DbsWUwSbtMu1a/j8Y93KiU1SRg9nyzfjereqn1BJ33QOTUXxdlYvVXMhAYl1vuR1Kmna5aIJe09KSIfyFNYw==", + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.8.2.tgz", + "integrity": "sha512-wfl1uwrAqMH9/pi4kqBo5LBcFwrJLxuDLqL7p7qNcJIFcyZDUc6pzhYk4CYv+DP7fIUpQCZumwNnkhPKS52osQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.24.7", - "@smithy/types": "^4.14.4", + "@smithy/core": "^3.26.0", + "@smithy/types": "^4.15.0", "tslib": "^2.6.2" }, "engines": { @@ -4964,13 +4804,13 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.4.7", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.4.7.tgz", - "integrity": "sha512-LwQZazFayImv+IOm0S0enoLeUJwmAlhGC5O6YCcLWezyu08dF46GOxPOq35OpBIHkgd7OvNvBStIFwVNyrvoBw==", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.5.2.tgz", + "integrity": "sha512-7xHpmPY4rt0IOmeAA8EfjgEH8isT+587TCdy9H6a7d4OMi5CQ0oEHhWllunvPu4j4Cq0vTFwdxXN/kABWPjdyA==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.24.7", - "@smithy/types": "^4.14.4", + "@smithy/core": "^3.26.0", + "@smithy/types": "^4.15.0", "tslib": "^2.6.2" }, "engines": { @@ -4978,9 +4818,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.14.4", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.4.tgz", - "integrity": "sha512-B2S9+UGm1+/pHkcx3ZoLVX1a+pmSk8rqxRR+ZsNqZaJ5q9FWX9AFGQVM4qG5+OBeQUZVy99HY8HqW8gK/wgXzQ==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.15.0.tgz", + "integrity": "sha512-Z5TAOxygoFvybJV3igo5SloFflSokHx2hu1eFA+DxDTcn+FtKxUSui+rbTRG1pAafMA888Z3MVvCWUuvCrTXjg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -5858,12 +5698,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.9.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.3.tgz", - "integrity": "sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==", + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.0.tgz", + "integrity": "sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==", "license": "MIT", "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" + "undici-types": "~8.3.0" } }, "node_modules/@types/normalize-package-data": { @@ -6184,39 +6024,6 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@typescript-eslint/utils": { "version": "7.18.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", @@ -6259,9 +6066,9 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", - "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.2.tgz", + "integrity": "sha512-5jsZFwgR5rTdKwidH9Qmat75RKwqfpKlWWB1frDkljN127mwqBu8K0PYo7/hFpF03IEJpfVPpCQDY/eDx3iHvA==", "dev": true, "license": "ISC" }, @@ -6538,18 +6345,6 @@ "node": ">= 8" } }, - "node_modules/anynum": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.0.tgz", - "integrity": "sha512-xjR9/zBVnUOP6ztMIIgShjsxui80nQUQH+5xJnvrYLs+90bF25/KJqaAi8mk+B4RDtX1Nspi6fmp4YTEts8SfA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, "node_modules/append-transform": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", @@ -6735,9 +6530,9 @@ } }, "node_modules/auth0/node_modules/undici-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.26.0.tgz", - "integrity": "sha512-anbjel3h0aEs7QuwKb3qbvApZmBE4lD0xonwP/6fOHvOj/1D3wmBWula2MOjOV2RRPOLnE9istBSlA+mqVhzMA==", + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.27.0.tgz", + "integrity": "sha512-HDBmCak1Ccswl7xS6/blapTYRVsqva4skTCeeJPLiDn6qCszeqiPGDljQBH2Kf/S0BI6EN14FQsgKVyGid9xnA==", "dev": true, "license": "MIT" }, @@ -6836,9 +6631,9 @@ "peer": true }, "node_modules/axios": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", - "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz", + "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==", "license": "MIT", "dependencies": { "follow-redirects": "^1.16.0", @@ -6848,13 +6643,10 @@ } }, "node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" }, "node_modules/base64-js": { "version": "1.5.1", @@ -6886,9 +6678,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.10.37", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.37.tgz", - "integrity": "sha512-girxaJ7WZssDOFhzCGZTDKoTa1gk6A1TbflaYTpykLJ4UU9Fz9kx1aREM8JCuoVHbL8X8T/mJg7w2oYSq72Oig==", + "version": "2.10.38", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz", + "integrity": "sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -7073,20 +6865,20 @@ "license": "MIT" }, "node_modules/body-parser": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", - "content-type": "^1.0.5", + "content-type": "^2.0.0", "debug": "^4.4.3", - "http-errors": "^2.0.0", - "iconv-lite": "^0.7.0", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", - "qs": "^6.14.1", - "raw-body": "^3.0.1", - "type-is": "^2.0.1" + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" }, "engines": { "node": ">=18" @@ -7103,15 +6895,13 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { @@ -7159,9 +6949,9 @@ "license": "(MIT AND Zlib)" }, "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "version": "4.28.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz", + "integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==", "dev": true, "funding": [ { @@ -7179,10 +6969,10 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", + "baseline-browser-mapping": "^2.10.38", + "caniuse-lite": "^1.0.30001799", + "electron-to-chromium": "^1.5.376", + "node-releases": "^2.0.48", "update-browserslist-db": "^1.2.3" }, "bin": { @@ -7240,9 +7030,9 @@ } }, "node_modules/bullmq": { - "version": "5.78.1", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.78.1.tgz", - "integrity": "sha512-zD5IT+qMqbMgPFPdL9FwnZka1bz6nckM+5lXj4N0vsXqdzoVO6wizmXpwsg/4GnHmXJsL7XOKeWA64tYUdPrOA==", + "version": "5.79.1", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.79.1.tgz", + "integrity": "sha512-cteoHRr1FGOTUgzFrnMyBNGtQhNeVR8Ej6nImNSHQDJi4tj6GMD0p9ZG65ZsTnvR9RVf18dhRxWu4kFl634QGA==", "license": "MIT", "dependencies": { "cron-parser": "4.9.0", @@ -7398,6 +7188,22 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/cacache/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/cacache/node_modules/p-map": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", @@ -7597,9 +7403,9 @@ } }, "node_modules/casbin": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/casbin/-/casbin-5.50.0.tgz", - "integrity": "sha512-ikARwU4ln2/3SzyG8cxFwjXgHhjmieapt62LwaeKZ0VDsqZ2meq3BCaJuW2Ept0cVx3C4j+1C2G9Rh6e/Fl+mQ==", + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/casbin/-/casbin-5.51.0.tgz", + "integrity": "sha512-+4QjMLyZDz+Vw3gLTTGvpAbMe4wB3u78qoFasSP6BAiOzM3rFvzP3Tb308ZpZlZv8KhbB8kgH11GCskwZjYaIw==", "license": "Apache-2.0", "dependencies": { "@casbin/expression-eval": "^5.3.0", @@ -7620,6 +7426,27 @@ "pg": "^8.2.1" } }, + "node_modules/casbin/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/casbin/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/casbin/node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -7644,6 +7471,41 @@ "ieee754": "^1.2.1" } }, + "node_modules/casbin/node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/casbin/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -7688,9 +7550,9 @@ } }, "node_modules/chardet": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", - "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.2.0.tgz", + "integrity": "sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==", "dev": true, "license": "MIT" }, @@ -8008,13 +7870,6 @@ "node": ">= 18" } }, - "node_modules/commitizen/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, "node_modules/commitizen/node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", @@ -8214,12 +8069,16 @@ } }, "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/conventional-changelog-angular": { @@ -9652,10 +9511,9 @@ "license": "MIT" }, "node_modules/ejs": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-5.0.1.tgz", - "integrity": "sha512-COqBPFMxuPTPspXl2DkVYaDS3HtrD1GpzOGkNTJ1IYkifq/r9h8SVEFrjA3D9/VJGOEoMQcrlhpntcSUrM8k6A==", - "dev": true, + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-5.0.2.tgz", + "integrity": "sha512-IpbUaI/CAW86l3f+T8zN0iggSc0LmMZLcIW5eRVStLVNCoTXkE0YlncbbH50fp8Cl6zHIky0sW2uUbhBqGw0Jw==", "license": "Apache-2.0", "bin": { "ejs": "bin/cli.js" @@ -9665,9 +9523,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.372", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.372.tgz", - "integrity": "sha512-M3yhbAlilnwqC8D21t28UCDGHyitShTmmLRU/H+b74P6Ski16Nb9HONYEaVpMj/pwC7BEo5B95FpjODLCWbtfA==", + "version": "1.5.378", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.378.tgz", + "integrity": "sha512-VinvOAuuPmdD1guEgGv5f2Qp7/vlfqOrUOMYNnOD4wj3pit8kRsQHzfIf6teyUGWo15Tg5+bOJaRunvyltpVWQ==", "dev": true, "license": "ISC" }, @@ -9857,6 +9715,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-abstract-get": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz", + "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==", + "license": "MIT", + "peer": true, + "dependencies": { + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.2", + "is-callable": "^1.2.7", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -9902,9 +9779,9 @@ "license": "MIT" }, "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -9929,15 +9806,17 @@ } }, "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.1.tgz", + "integrity": "sha512-CxN9N56HYfd2m/acc/NOFrZQsN9kU4eh+2kk6A707Kz1krH8tKmfrs5RnftB8WNX80T0NS7vSQsDOlg23diR2g==", "license": "MIT", "peer": true, "dependencies": { + "es-abstract-get": "^1.0.0", + "es-errors": "^1.3.0", "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" + "is-date-object": "^1.1.0", + "is-symbol": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -10151,13 +10030,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", @@ -10169,17 +10041,6 @@ "concat-map": "0.0.1" } }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -10435,6 +10296,15 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/express/node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -10651,43 +10521,6 @@ ], "license": "BSD-3-Clause" }, - "node_modules/fast-xml-builder": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", - "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "path-expression-matcher": "^1.5.0", - "xml-naming": "^0.1.0" - } - }, - "node_modules/fast-xml-parser": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.3.tgz", - "integrity": "sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "@nodable/entities": "^2.1.0", - "fast-xml-builder": "^1.1.7", - "path-expression-matcher": "^1.5.0", - "strnum": "^2.2.3" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", @@ -10920,13 +10753,6 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/flat-cache/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, "node_modules/flat-cache/node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", @@ -11116,16 +10942,16 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -11491,9 +11317,9 @@ "license": "ISC" }, "node_modules/get-pkg-repo/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.2.tgz", + "integrity": "sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==", "dev": true, "license": "MIT", "dependencies": { @@ -13345,13 +13171,6 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-processinfo/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, "node_modules/istanbul-lib-processinfo/node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", @@ -13606,9 +13425,19 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -14018,13 +13847,6 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, - "node_modules/lerna/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, "node_modules/lerna/node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", @@ -14187,6 +14009,19 @@ } } }, + "node_modules/lerna/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/lerna/node_modules/minimatch": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.4.tgz", @@ -14220,6 +14055,25 @@ "node": ">=0.12.0" } }, + "node_modules/lerna/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -15831,64 +15685,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/mocha/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/mocha/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/mocha/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/mocha/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/mocha/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -16352,9 +16148,9 @@ "peer": true }, "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", + "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", "funding": [ { "type": "github", @@ -16713,9 +16509,9 @@ } }, "node_modules/node-pg-migrate/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.2.tgz", + "integrity": "sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==", "license": "MIT", "dependencies": { "cliui": "^7.0.2", @@ -16753,9 +16549,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.47", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", - "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", + "version": "2.0.49", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.49.tgz", + "integrity": "sha512-f06bl1D+8ZDkn2oOQQKAh5/otFWqVnM1Q5oerA8Pex7UfT66Tx4IPHIqVVFKqFT3FUtaDstdgkM7yT7JWhqxfw==", "dev": true, "license": "MIT", "engines": { @@ -16791,13 +16587,6 @@ "url": "https://opencollective.com/nodemon" } }, - "node_modules/nodemon/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, "node_modules/nodemon/node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", @@ -17135,9 +16924,9 @@ } }, "node_modules/nx": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/nx/-/nx-22.7.5.tgz", - "integrity": "sha512-zoxsJabb33jl1QYnalDn0bicryrEBgSzdKp90d7VGGv/jDgzKrcLg/hw2ZxeYiOjWPIT/o8QNT9G9vTs4dv3AQ==", + "version": "22.7.6", + "resolved": "https://registry.npmjs.org/nx/-/nx-22.7.6.tgz", + "integrity": "sha512-cT2iX6NAtuNT/yaMTtwpIuNQBrW2Zhg4X8zdTEo/h27bz/JYnFm7B9MAKbAXNWK6k0Yxz+jv7zJoMBHutd1Dww==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -17189,7 +16978,7 @@ "figures": "3.2.0", "flat": "5.0.2", "follow-redirects": "1.16.0", - "form-data": "4.0.5", + "form-data": "4.0.6", "fs-constants": "1.0.0", "function-bind": "1.1.2", "get-caller-file": "2.0.5", @@ -17199,7 +16988,7 @@ "has-flag": "4.0.0", "has-symbols": "1.1.0", "has-tostringtag": "1.0.2", - "hasown": "2.0.2", + "hasown": "2.0.4", "ieee754": "1.2.1", "ignore": "7.0.5", "inherits": "2.0.4", @@ -17240,7 +17029,7 @@ "strip-bom": "3.0.0", "supports-color": "7.2.0", "tar-stream": "2.2.0", - "tmp": "0.2.6", + "tmp": "0.2.7", "tree-kill": "1.2.2", "tsconfig-paths": "4.2.0", "tslib": "2.8.1", @@ -17258,16 +17047,16 @@ "nx-cloud": "dist/bin/nx-cloud.js" }, "optionalDependencies": { - "@nx/nx-darwin-arm64": "22.7.5", - "@nx/nx-darwin-x64": "22.7.5", - "@nx/nx-freebsd-x64": "22.7.5", - "@nx/nx-linux-arm-gnueabihf": "22.7.5", - "@nx/nx-linux-arm64-gnu": "22.7.5", - "@nx/nx-linux-arm64-musl": "22.7.5", - "@nx/nx-linux-x64-gnu": "22.7.5", - "@nx/nx-linux-x64-musl": "22.7.5", - "@nx/nx-win32-arm64-msvc": "22.7.5", - "@nx/nx-win32-x64-msvc": "22.7.5" + "@nx/nx-darwin-arm64": "22.7.6", + "@nx/nx-darwin-x64": "22.7.6", + "@nx/nx-freebsd-x64": "22.7.6", + "@nx/nx-linux-arm-gnueabihf": "22.7.6", + "@nx/nx-linux-arm64-gnu": "22.7.6", + "@nx/nx-linux-arm64-musl": "22.7.6", + "@nx/nx-linux-x64-gnu": "22.7.6", + "@nx/nx-linux-x64-musl": "22.7.6", + "@nx/nx-win32-arm64-msvc": "22.7.6", + "@nx/nx-win32-x64-msvc": "22.7.6" }, "peerDependencies": { "@swc-node/register": "^1.11.1", @@ -17292,6 +17081,18 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, + "node_modules/nx/node_modules/axios": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz", + "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" + } + }, "node_modules/nx/node_modules/balanced-match": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.3.tgz", @@ -17302,19 +17103,152 @@ "node": "20 || >=22" } }, - "node_modules/nx/node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "node_modules/nx/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, "license": "MIT", "dependencies": { - "function-bind": "^1.1.2" + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/nx/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/nx/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/nx/node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/nx/node_modules/ejs": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-5.0.1.tgz", + "integrity": "sha512-COqBPFMxuPTPspXl2DkVYaDS3HtrD1GpzOGkNTJ1IYkifq/r9h8SVEFrjA3D9/VJGOEoMQcrlhpntcSUrM8k6A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.12.18" + } + }, + "node_modules/nx/node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" }, "engines": { "node": ">= 0.4" } }, + "node_modules/nx/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/nx/node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/nx/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/nx/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/nx/node_modules/ora": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", @@ -17382,6 +17316,25 @@ "url": "https://github.com/sponsors/eemeli" } }, + "node_modules/nx/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/nyc": { "version": "15.1.0", "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", @@ -17424,13 +17377,6 @@ "node": ">=8.9" } }, - "node_modules/nyc/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, "node_modules/nyc/node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", @@ -18485,9 +18431,19 @@ } }, "node_modules/pako": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", - "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.2.0.tgz", + "integrity": "sha512-zJq6RP/5q+TO2OpFV3FHzlPnFjmkb7Nc99a5SNjJE+uu/PkpChs+NIZSSzbBoD+6kjiISXjfYdwj1ZRQ81dz/w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "(MIT AND Zlib)" }, "node_modules/param-case": { @@ -18733,21 +18689,6 @@ "node": ">=8" } }, - "node_modules/path-expression-matcher": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", - "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -18823,14 +18764,14 @@ } }, "node_modules/pg": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.21.0.tgz", - "integrity": "sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.22.0.tgz", + "integrity": "sha512-8wih1vVIBMxoUM2oB4soJsD9tDnDpLv4OXBJ+EJzFsvycD+lfyIreC2gGHq78f8jbLLt+bvlPTFdFZfJkOuzAA==", "license": "MIT", "dependencies": { - "pg-connection-string": "^2.13.0", + "pg-connection-string": "^2.14.0", "pg-pool": "^3.14.0", - "pg-protocol": "^1.14.0", + "pg-protocol": "^1.15.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, @@ -18857,9 +18798,9 @@ "optional": true }, "node_modules/pg-connection-string": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.13.0.tgz", - "integrity": "sha512-EMnU9E2fSULdsbErBbMaXJvFeD9B4+nPcM3f+4lsiCR0BHLPrLVjv3DbyM2hgQQviKJaTWIRRTjKjWlHg3p2ig==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.0.tgz", + "integrity": "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==", "license": "MIT" }, "node_modules/pg-int8": { @@ -18881,9 +18822,9 @@ } }, "node_modules/pg-protocol": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.14.0.tgz", - "integrity": "sha512-n5taZ1kO3s9ngDTVxsEznOqCyToTgz0FLuPq0B33COy5pPpuWJpY3/2oRBVETuOgzdqRXfWpM9HIhp2LBBT1BA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.15.0.tgz", + "integrity": "sha512-cq9sECI5s0+uPUXjbz8ioyPJni6RzsRib0US67i5IoTZKw8fNeYlVE7u8F4dG7vEJJtc5wdD1K189lCCUwqWTQ==", "license": "MIT" }, "node_modules/pg-types": { @@ -20118,70 +20059,184 @@ } }, "node_modules/rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/rimraf/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/rimraf/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/rimraf/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/rimraf/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/rimraf/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "glob": "^7.1.3" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "node_modules/rimraf/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "node_modules/rimraf/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "ansi-regex": "^6.2.2" }, "engines": { - "node": "*" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "node_modules/rimraf/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": "*" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/run-async": { @@ -20357,9 +20412,9 @@ "license": "ISC" }, "node_modules/semver": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", - "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -20974,13 +21029,6 @@ "node": ">=8" } }, - "node_modules/spawn-wrap/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, "node_modules/spawn-wrap/node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", @@ -21311,19 +21359,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string.prototype.trim/node_modules/es-object-atoms": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", - "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", - "license": "MIT", - "peer": true, - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/string.prototype.trimend": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz", @@ -21343,19 +21378,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string.prototype.trimend/node_modules/es-object-atoms": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", - "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", - "license": "MIT", - "peer": true, - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/string.prototype.trimstart": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", @@ -21458,21 +21480,6 @@ "node": ">=12.*" } }, - "node_modules/strnum": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.0.tgz", - "integrity": "sha512-sHrVyWWdq28RbhjuJdZsA1SnGRJV6NiXbk6AXBxDOsgAcA+lmpUZCYjOdLBxkXMwis6RRe7dlZt4VlIWFVzkmg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "anynum": "^1.0.0" - } - }, "node_modules/strong-error-handler": { "version": "5.0.32", "resolved": "https://registry.npmjs.org/strong-error-handler/-/strong-error-handler-5.0.32.tgz", @@ -21801,6 +21808,43 @@ "node": ">=6" } }, + "node_modules/tar-stream/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/tar-stream/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/tcomb": { "version": "3.2.29", "resolved": "https://registry.npmjs.org/tcomb/-/tcomb-3.2.29.tgz", @@ -21842,6 +21886,66 @@ "node": ">=6.0.0" } }, + "node_modules/temp/node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/temp/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/temp/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/temp/node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -21857,13 +21961,6 @@ "node": ">=8" } }, - "node_modules/test-exclude/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, "node_modules/test-exclude/node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", @@ -22040,9 +22137,9 @@ } }, "node_modules/tmp": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.6.tgz", - "integrity": "sha512-5sJPdPjfI5Kx+qbrDesxkglRBxW//g7hCsqspEjwkewGvBMGIKMOTKzLt1hFVJzyadba3lDUN20O9qhvbQUSTA==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", "dev": true, "license": "MIT", "engines": { @@ -22369,19 +22466,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/type-is/node_modules/content-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/type-is/node_modules/mime-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", @@ -22600,18 +22684,18 @@ "license": "MIT" }, "node_modules/undici": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.0.tgz", - "integrity": "sha512-jxytwMHhsbdpBXxLAcuu0fzlQeXCNnWdDyRHpvWsUl8vd98UwYdl9YTyn8/HcpcJPC3pwUveefsa3zTxyD/ERg==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.5.0.tgz", + "integrity": "sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg==", "license": "MIT", "engines": { - "node": ">=20.18.1" + "node": ">=22.19.0" } }, "node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", "license": "MIT" }, "node_modules/unicode-properties": { @@ -22805,9 +22889,9 @@ } }, "node_modules/uuid": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.0.tgz", - "integrity": "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==", + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz", + "integrity": "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -23171,21 +23255,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/xml-naming": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", - "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "engines": { - "node": ">=16.0.0" - } - }, "node_modules/xml2js": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", @@ -23280,12 +23349,6 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/yamljs/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, "node_modules/yamljs/node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", @@ -23330,9 +23393,9 @@ } }, "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -23499,9 +23562,9 @@ } }, "services/orchestrator-service/node_modules/@types/node": { - "version": "20.19.41", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", - "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", "dev": true, "license": "MIT", "dependencies": { @@ -23584,9 +23647,9 @@ } }, "services/subscription-service/node_modules/@types/node": { - "version": "20.19.41", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", - "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", "dev": true, "license": "MIT", "dependencies": { @@ -23679,9 +23742,9 @@ } }, "services/tenant-management-service/node_modules/@types/node": { - "version": "20.19.41", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", - "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 176e0dc..46eb59a 100644 --- a/package.json +++ b/package.json @@ -29,9 +29,9 @@ "overrides": { "semver": "^7.5.2", "qs": "^6.14.1", - "form-data": "4.0.4", + "form-data": "4.0.6", "underscore": "1.13.8", - "undici": "7.24.0" + "undici": "8.5.0" }, "config": { "commitizen": { diff --git a/services/subscription-service/src/controllers/billing-service.controller.ts b/services/subscription-service/src/controllers/billing-service.controller.ts index 334349e..38431dd 100644 --- a/services/subscription-service/src/controllers/billing-service.controller.ts +++ b/services/subscription-service/src/controllers/billing-service.controller.ts @@ -1,6 +1,7 @@ import {inject} from '@loopback/core'; import {get, param} from '@loopback/rest'; import {authorize} from 'loopback4-authorization'; +import {authenticate, STRATEGY} from 'loopback4-authentication'; import { BillingComponentBindings, IService, @@ -8,23 +9,21 @@ import { TInvoicePaymentDetails, TPaymentIntent, } from 'loopback4-billing'; -import {getModelSchemaRefSF, STATUS_CODE} from '@sourceloop/core'; -import {BillingPaymentStatusResponse} from '../models'; +import { + getModelSchemaRefSF, + STATUS_CODE, + OPERATION_SECURITY_SPEC, +} from '@sourceloop/core'; +import {BillingPaymentStatusResponse, BillingErrorResponse} from '../models'; +import { + InvoicePdfDto, + InvoicePaymentDetailsDto, + PaymentIntentDto, +} from '../models/dto'; +import {PermissionKey} from '../permissions'; const BASE = '/billing'; -/** - * Billing service controller for invoice and payment intent queries. - * - * This controller provides additional billing endpoints that are not - * covered by BillingInvoiceController and BillingPaymentSourceController. - * - * For customer CRUD, use BillingCustomerController. - * For payment source operations, use BillingPaymentSourceController. - * For invoice CRUD, use BillingInvoiceController. - * - * Bound to BillingComponentBindings.SDKProvider (StripeService/ChargeBeeService). - */ export class BillingServiceController { constructor( @inject(BillingComponentBindings.SDKProvider) @@ -34,8 +33,10 @@ export class BillingServiceController { /** * Check whether an invoice has been paid. */ - @authorize({permissions: ['*']}) + @authorize({permissions: [PermissionKey.ViewInvoice]}) + @authenticate(STRATEGY.BEARER, {passReqToCallback: true}) @get(`${BASE}/invoices/{invoiceId}/payment-status`, { + security: OPERATION_SECURITY_SPEC, summary: 'Check if an invoice is paid', responses: { [STATUS_CODE.OK]: { @@ -55,30 +56,10 @@ export class BillingServiceController { return {paid}; } - /** - * Get PDF download URL for an invoice. - * - * Returns a temporary URL to download the invoice PDF. - * The URL is typically valid for a limited time and should be used immediately. - * - * @param invoiceId - The invoice ID (Stripe: in_XXXXX, ChargeBee: inv_XXXXX) - * @returns PDF information including download URL and metadata - * - * Example: - * GET /billing/invoices/in_1234567890/pdf - * - * Response: - * ```json - * { - * "invoiceId": "in_1234567890", - * "pdfUrl": "https://pay.stripe.com/invoice/acct_1ABC/in_1234567890/pdf", - * "generatedAt": "2026-05-04T12:00:00.000Z", - * "expiresAt": null - * } - * ``` - */ - @authorize({permissions: ['*']}) + @authorize({permissions: [PermissionKey.ViewInvoice]}) + @authenticate(STRATEGY.BEARER, {passReqToCallback: true}) @get(`${BASE}/invoices/{invoiceId}/pdf`, { + security: OPERATION_SECURITY_SPEC, summary: 'Get PDF download URL for an invoice', description: 'Retrieves a temporary URL to download the invoice PDF. ' + @@ -89,37 +70,7 @@ export class BillingServiceController { description: 'PDF information retrieved successfully', content: { 'application/json': { - schema: { - type: 'object', - required: ['invoiceId', 'pdfUrl', 'generatedAt'], - properties: { - invoiceId: { - type: 'string', - description: 'The invoice ID', - example: 'in_1234567890', - }, - pdfUrl: { - type: 'string', - description: 'Temporary download URL for the PDF', - example: - 'https://pay.stripe.com/invoice/acct_1ABC/in_1234567890/pdf', - }, - generatedAt: { - type: 'string', - format: 'date-time', - description: 'Timestamp when the PDF URL was generated', - example: '2026-05-04T12:00:00.000Z', - }, - expiresAt: { - type: 'string', - format: 'date-time', - description: - 'Timestamp when the PDF URL expires (if provided by the provider)', - example: '2026-05-04T12:30:00.000Z', - nullable: true, - }, - }, - }, + schema: getModelSchemaRefSF(InvoicePdfDto), }, }, }, @@ -127,24 +78,7 @@ export class BillingServiceController { description: 'Invoice not found', content: { 'application/json': { - schema: { - type: 'object', - properties: { - error: { - type: 'object', - properties: { - statusCode: { - type: 'number', - example: STATUS_CODE.NOT_FOUND, - }, - message: { - type: 'string', - example: 'Invoice not found: in_1234567890', - }, - }, - }, - }, - }, + schema: getModelSchemaRefSF(BillingErrorResponse), }, }, }, @@ -152,25 +86,7 @@ export class BillingServiceController { description: 'PDF URL not available', content: { 'application/json': { - schema: { - type: 'object', - properties: { - error: { - type: 'object', - properties: { - statusCode: { - type: 'number', - example: STATUS_CODE.BAD_REQUEST, - }, - message: { - type: 'string', - example: - 'PDF URL not available for invoice in_123. The invoice may be in draft status or not finalized.', - }, - }, - }, - }, - }, + schema: getModelSchemaRefSF(BillingErrorResponse), }, }, }, @@ -184,34 +100,10 @@ export class BillingServiceController { return pdfInfo; } - /** - * Get payment details for an invoice. - * - * Returns information about the payment method used, payment amount, - * payment date, and transaction status. - * - * Example response: - * ```json - * { - * "invoiceId": "in_1234567890", - * "paymentMethod": { - * "type": "card", - * "card": { - * "brand": "visa", - * "last4": "4242", - * "expMonth": 12, - * "expYear": 2025 - * } - * }, - * "paymentDate": 1714834567, - * "amount": 5000, - * "currency": "usd", - * "status": "succeeded" - * } - * ``` - */ - @authorize({permissions: ['*']}) + @authorize({permissions: [PermissionKey.ViewInvoice]}) + @authenticate(STRATEGY.BEARER, {passReqToCallback: true}) @get(`${BASE}/invoices/{invoiceId}/payment-details`, { + security: OPERATION_SECURITY_SPEC, summary: 'Get payment details for an invoice', description: 'Retrieves payment method details, payment amount, status, and ' + @@ -221,33 +113,7 @@ export class BillingServiceController { description: 'Payment details retrieved successfully', content: { 'application/json': { - schema: { - type: 'object', - required: ['invoiceId', 'paymentMethod'], - properties: { - invoiceId: {type: 'string', example: 'in_1234567890'}, - paymentMethod: { - type: 'object', - properties: { - type: {type: 'string', example: 'card'}, - card: { - type: 'object', - properties: { - brand: {type: 'string', example: 'visa'}, - last4: {type: 'string', example: '4242'}, - expMonth: {type: 'number', example: 12}, - expYear: {type: 'number', example: 2025}, - funding: {type: 'string', example: 'credit'}, - }, - }, - }, - }, - paymentDate: {type: 'number', example: 1714834567}, - amount: {type: 'number', example: 5000}, - currency: {type: 'string', example: 'usd'}, - status: {type: 'string', example: 'succeeded'}, - }, - }, + schema: getModelSchemaRefSF(InvoicePaymentDetailsDto), }, }, }, @@ -255,24 +121,7 @@ export class BillingServiceController { description: 'Invoice not found', content: { 'application/json': { - schema: { - type: 'object', - properties: { - error: { - type: 'object', - properties: { - statusCode: { - type: 'number', - example: STATUS_CODE.NOT_FOUND, - }, - message: { - type: 'string', - example: 'Invoice not found: in_1234567890', - }, - }, - }, - }, - }, + schema: getModelSchemaRefSF(BillingErrorResponse), }, }, }, @@ -280,25 +129,7 @@ export class BillingServiceController { description: 'No payment details available', content: { 'application/json': { - schema: { - type: 'object', - properties: { - error: { - type: 'object', - properties: { - statusCode: { - type: 'number', - example: STATUS_CODE.BAD_REQUEST, - }, - message: { - type: 'string', - example: - 'No payment found for invoice. The invoice may not be paid yet.', - }, - }, - }, - }, - }, + schema: getModelSchemaRefSF(BillingErrorResponse), }, }, }, @@ -310,34 +141,10 @@ export class BillingServiceController { return this.billingService.getInvoicePaymentDetails(invoiceId); } - /** - * Get payment intent details by ID. - * - * Returns comprehensive payment tracking information including status, - * payment method, amount, and transaction metadata. - * - * Example response: - * ```json - * { - * "id": "pi_1234567890", - * "amount": 5000, - * "currency": "usd", - * "status": "succeeded", - * "created": 1714834567, - * "customer": "cus_XXXXX", - * "paymentMethod": { - * "type": "card", - * "card": { - * "brand": "visa", - * "last4": "4242" - * } - * }, - * "description": "Payment for order #12345" - * } - * ``` - */ - @authorize({permissions: ['*']}) + @authorize({permissions: [PermissionKey.ViewInvoice]}) + @authenticate(STRATEGY.BEARER, {passReqToCallback: true}) @get(`${BASE}/payment-intents/{paymentIntentId}`, { + security: OPERATION_SECURITY_SPEC, summary: 'Get payment intent details', description: 'Retrieves detailed information about a payment intent including ' + @@ -348,39 +155,7 @@ export class BillingServiceController { description: 'Payment intent retrieved successfully', content: { 'application/json': { - schema: { - type: 'object', - required: ['id', 'amount', 'currency', 'status', 'created'], - properties: { - id: {type: 'string', example: 'pi_1234567890'}, - amount: {type: 'number', example: 5000}, - currency: {type: 'string', example: 'usd'}, - status: {type: 'string', example: 'succeeded'}, - created: {type: 'number', example: 1714834567}, - customer: {type: 'string', example: 'cus_XXXXX'}, - paymentMethod: { - type: 'object', - properties: { - type: {type: 'string', example: 'card'}, - card: { - type: 'object', - properties: { - brand: {type: 'string', example: 'visa'}, - last4: {type: 'string', example: '4242'}, - }, - }, - }, - }, - description: { - type: 'string', - example: 'Payment for order #12345', - }, - metadata: { - type: 'object', - additionalProperties: {type: 'string'}, - }, - }, - }, + schema: getModelSchemaRefSF(PaymentIntentDto), }, }, }, @@ -388,24 +163,7 @@ export class BillingServiceController { description: 'Payment intent not found', content: { 'application/json': { - schema: { - type: 'object', - properties: { - error: { - type: 'object', - properties: { - statusCode: { - type: 'number', - example: STATUS_CODE.NOT_FOUND, - }, - message: { - type: 'string', - example: 'Payment intent not found: pi_1234567890', - }, - }, - }, - }, - }, + schema: getModelSchemaRefSF(BillingErrorResponse), }, }, }, diff --git a/services/subscription-service/src/controllers/billing-subscription.controller.ts b/services/subscription-service/src/controllers/billing-subscription.controller.ts index 8ddf15a..f6f4c44 100644 --- a/services/subscription-service/src/controllers/billing-subscription.controller.ts +++ b/services/subscription-service/src/controllers/billing-subscription.controller.ts @@ -1,12 +1,10 @@ import {inject} from '@loopback/core'; import {del, get, param, post, put, requestBody} from '@loopback/rest'; import {authorize} from 'loopback4-authorization'; +import {authenticate, STRATEGY} from 'loopback4-authentication'; import { BillingComponentBindings, - CollectionMethod, ISubscriptionService, - ProrationBehavior, - RecurringInterval, TInvoicePrice, TPrice, TProduct, @@ -14,55 +12,42 @@ import { TSubscriptionResult, TSubscriptionUpdate, } from 'loopback4-billing'; +import { + getModelSchemaRefSF, + STATUS_CODE, + OPERATION_SECURITY_SPEC, +} from '@sourceloop/core'; +import {PermissionKey} from '../permissions'; +import { + PriceDto, + ProductDto, + SubscriptionCreateDto, + SubscriptionUpdateDto, + SubscriptionResultDto, + SubscriptionCreateResponseDto, + SuccessDto, + InvoicePriceDto, +} from '../models/dto'; const BASE = '/billing'; -/** - * Sandbox controller that exercises the full subscription lifecycle - * implemented in loopback4-billing. - * - * Every endpoint injects {@link ISubscriptionService} via - * {@link BillingComponentBindings.SubscriptionProvider} — the new - * provider-agnostic binding. Swap the provider in application.ts - * (ChargeBee ↔ Stripe) without touching this controller. - */ export class BillingSubscriptionController { constructor( @inject(BillingComponentBindings.SDKProvider) private readonly billingService: ISubscriptionService, ) {} - // ------------------------------------------------------------------------- - // PRODUCT - // ------------------------------------------------------------------------- - - /** - * Create a new product (Chargebee: Item / Stripe: Product). - * - * Example body: - * ```json - * { - * "name": "Enterprise Plan", - * "description": "Full-featured tier", - * "metadata": { "item_family_id": "default" } - * } - * ``` - */ - @authorize({permissions: ['*']}) + @authorize({permissions: [PermissionKey.CreatePlan]}) + @authenticate(STRATEGY.BEARER, {passReqToCallback: true}) @post(`${BASE}/products`, { + security: OPERATION_SECURITY_SPEC, summary: 'Create a billing product (Item / Product)', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'External product ID', content: { 'application/json': { - schema: { - type: 'object', - properties: { - productId: {type: 'string', example: 'cbdemo_enterprise'}, - }, - required: ['productId'], - }, + schema: getModelSchemaRefSF(ProductDto), }, }, }, @@ -72,15 +57,10 @@ export class BillingSubscriptionController { @requestBody({ content: { 'application/json': { - schema: { - type: 'object', - required: ['name'], - properties: { - name: {type: 'string'}, - description: {type: 'string'}, - metadata: {type: 'object'}, - }, - }, + schema: getModelSchemaRefSF(ProductDto, { + title: 'NewProduct', + exclude: ['id'], + }), }, }, }) @@ -90,24 +70,17 @@ export class BillingSubscriptionController { return {productId}; } - /** - * Check whether a product/item exists and is still active. - */ - @authorize({permissions: ['*']}) + @authorize({permissions: [PermissionKey.ViewPlan]}) + @authenticate(STRATEGY.BEARER, {passReqToCallback: true}) @get(`${BASE}/products/{productId}/exists`, { + security: OPERATION_SECURITY_SPEC, summary: 'Check if a billing product is active', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Existence flag', content: { 'application/json': { - schema: { - type: 'object', - properties: { - exists: {type: 'boolean', example: true}, - }, - required: ['exists'], - }, + schema: getModelSchemaRefSF(SuccessDto), }, }, }, @@ -120,48 +93,17 @@ export class BillingSubscriptionController { return {exists}; } - // ------------------------------------------------------------------------- - // PRICE / PLAN - // ------------------------------------------------------------------------- - - /** - * Create a recurring price (Chargebee: ItemPrice / Stripe: Price). - * - * Example body: - * ```json - * { - * "currency": "usd", - * "unitAmount": 4999, - * "product": "", - * "recurring": { "interval": "month", "intervalCount": 1 } - * } - * ``` - */ - @authorize({permissions: ['*']}) + @authorize({permissions: [PermissionKey.CreatePlan]}) + @authenticate(STRATEGY.BEARER, {passReqToCallback: true}) @post(`${BASE}/prices`, { + security: OPERATION_SECURITY_SPEC, summary: 'Create a recurring price (ItemPrice / Price)', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Created price / item-price object', content: { 'application/json': { - schema: { - type: 'object', - properties: { - id: {type: 'string', example: 'cbdemo_enterprise-USD-monthly'}, - currency: {type: 'string', example: 'usd'}, - unitAmount: {type: 'number', example: 4999}, - product: {type: 'string', example: 'cbdemo_enterprise'}, - active: {type: 'boolean', example: true}, - recurring: { - type: 'object', - properties: { - interval: {type: 'string', example: 'month'}, - intervalCount: {type: 'number', example: 1}, - }, - }, - }, - }, + schema: getModelSchemaRefSF(PriceDto), }, }, }, @@ -171,28 +113,10 @@ export class BillingSubscriptionController { @requestBody({ content: { 'application/json': { - schema: { - type: 'object', - required: ['currency', 'unitAmount', 'product'], - properties: { - id: {type: 'string'}, - currency: {type: 'string', example: 'usd'}, - unitAmount: {type: 'number', example: 4999}, - product: {type: 'string'}, - recurring: { - type: 'object', - properties: { - interval: { - type: 'string', - enum: Object.values(RecurringInterval), - example: RecurringInterval.MONTH, - }, - intervalCount: {type: 'number', example: 1}, - }, - }, - metadata: {type: 'object'}, - }, - }, + schema: getModelSchemaRefSF(PriceDto, { + title: 'NewPrice', + exclude: ['id'], + }), }, }, }) @@ -201,37 +125,17 @@ export class BillingSubscriptionController { return this.billingService.createPrice(price); } - // ------------------------------------------------------------------------- - // SUBSCRIPTION - // ------------------------------------------------------------------------- - - /** - * Create a new recurring subscription. - * - * Example body: - * ```json - * { - * "customerId": "", - * "priceRefId": "", - * "collectionMethod": "charge_automatically" - * } - * ``` - */ - @authorize({permissions: ['*']}) + @authorize({permissions: [PermissionKey.CreateSubscription]}) + @authenticate(STRATEGY.BEARER, {passReqToCallback: true}) @post(`${BASE}/subscriptions`, { + security: OPERATION_SECURITY_SPEC, summary: 'Create a new subscription', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Newly created subscription ID', content: { 'application/json': { - schema: { - type: 'object', - properties: { - subscriptionId: {type: 'string', example: 'AzZlGKSfBGHDPJkp'}, - }, - required: ['subscriptionId'], - }, + schema: getModelSchemaRefSF(SubscriptionCreateResponseDto), }, }, }, @@ -241,20 +145,10 @@ export class BillingSubscriptionController { @requestBody({ content: { 'application/json': { - schema: { - type: 'object', - required: ['customerId', 'priceRefId', 'collectionMethod'], - properties: { - customerId: {type: 'string'}, - priceRefId: {type: 'string'}, - collectionMethod: { - type: 'string', - enum: Object.values(CollectionMethod), - example: CollectionMethod.CHARGE_AUTOMATICALLY, - }, - daysUntilDue: {type: 'number', example: 30}, - }, - }, + schema: getModelSchemaRefSF(SubscriptionCreateDto, { + title: 'NewSubscription', + exclude: [], + }), }, }, }) @@ -265,29 +159,17 @@ export class BillingSubscriptionController { return {subscriptionId}; } - /** - * Get the current state of an existing subscription. - */ - @authorize({permissions: ['*']}) + @authorize({permissions: [PermissionKey.ViewSubscription]}) + @authenticate(STRATEGY.BEARER, {passReqToCallback: true}) @get(`${BASE}/subscriptions/{subscriptionId}`, { + security: OPERATION_SECURITY_SPEC, summary: 'Get a subscription by ID', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Subscription object', content: { 'application/json': { - schema: { - type: 'object', - properties: { - id: {type: 'string', example: 'AzZlGKSfBGHDPJkp'}, - status: {type: 'string', example: 'active'}, - customerId: {type: 'string', example: 'cust_001'}, - currentPeriodStart: {type: 'number', example: 1700000000}, - currentPeriodEnd: {type: 'number', example: 1702678400}, - cancelAtPeriodEnd: {type: 'boolean', example: false}, - }, - required: ['id', 'status', 'customerId'], - }, + schema: getModelSchemaRefSF(SubscriptionResultDto), }, }, }, @@ -299,37 +181,17 @@ export class BillingSubscriptionController { return this.billingService.getSubscription(subscriptionId); } - /** - * Upgrade or downgrade an existing subscription. - * - * Example body: - * ```json - * { - * "priceRefId": "", - * "prorationBehavior": "create_prorations" - * } - * ``` - */ - @authorize({permissions: ['*']}) + @authorize({permissions: [PermissionKey.UpdateSubscription]}) + @authenticate(STRATEGY.BEARER, {passReqToCallback: true}) @put(`${BASE}/subscriptions/{subscriptionId}`, { + security: OPERATION_SECURITY_SPEC, summary: 'Upgrade / downgrade a subscription (plan change)', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Updated subscription object', content: { 'application/json': { - schema: { - type: 'object', - properties: { - id: {type: 'string', example: 'AzZlGKSfBGHDPJkp'}, - status: {type: 'string', example: 'active'}, - customerId: {type: 'string', example: 'cust_001'}, - currentPeriodStart: {type: 'number', example: 1700000000}, - currentPeriodEnd: {type: 'number', example: 1702678400}, - cancelAtPeriodEnd: {type: 'boolean', example: false}, - }, - required: ['id', 'status', 'customerId'], - }, + schema: getModelSchemaRefSF(SubscriptionResultDto), }, }, }, @@ -340,17 +202,10 @@ export class BillingSubscriptionController { @requestBody({ content: { 'application/json': { - schema: { - type: 'object', - properties: { - priceRefId: {type: 'string'}, - prorationBehavior: { - type: 'string', - enum: Object.values(ProrationBehavior), - example: ProrationBehavior.CREATE_PRORATIONS, - }, - }, - }, + schema: getModelSchemaRefSF(SubscriptionUpdateDto, { + title: 'SubscriptionUpdate', + partial: true, + }), }, }, }) @@ -359,14 +214,13 @@ export class BillingSubscriptionController { return this.billingService.updateSubscription(subscriptionId, updates); } - /** - * Cancel a subscription immediately with proration. - */ - @authorize({permissions: ['*']}) + @authorize({permissions: [PermissionKey.DeleteSubscription]}) + @authenticate(STRATEGY.BEARER, {passReqToCallback: true}) @del(`${BASE}/subscriptions/{subscriptionId}`, { + security: OPERATION_SECURITY_SPEC, summary: 'Cancel a subscription immediately', responses: { - '204': {description: 'Subscription cancelled'}, + [STATUS_CODE.NO_CONTENT]: {description: 'Subscription cancelled'}, }, }) async cancelSubscription( @@ -375,24 +229,17 @@ export class BillingSubscriptionController { await this.billingService.cancelSubscription(subscriptionId); } - /** - * Pause an active subscription. - */ - @authorize({permissions: ['*']}) + @authorize({permissions: [PermissionKey.UpdateSubscription]}) + @authenticate(STRATEGY.BEARER, {passReqToCallback: true}) @post(`${BASE}/subscriptions/{subscriptionId}/pause`, { + security: OPERATION_SECURITY_SPEC, summary: 'Pause a subscription', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Subscription paused', content: { 'application/json': { - schema: { - type: 'object', - properties: { - success: {type: 'boolean', example: true}, - }, - required: ['success'], - }, + schema: getModelSchemaRefSF(SuccessDto), }, }, }, @@ -405,24 +252,17 @@ export class BillingSubscriptionController { return {success: true}; } - /** - * Resume a paused subscription. - */ - @authorize({permissions: ['*']}) + @authorize({permissions: [PermissionKey.UpdateSubscription]}) + @authenticate(STRATEGY.BEARER, {passReqToCallback: true}) @post(`${BASE}/subscriptions/{subscriptionId}/resume`, { + security: OPERATION_SECURITY_SPEC, summary: 'Resume a paused subscription', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Subscription resumed', content: { 'application/json': { - schema: { - type: 'object', - properties: { - success: {type: 'boolean', example: true}, - }, - required: ['success'], - }, + schema: getModelSchemaRefSF(SuccessDto), }, }, }, @@ -435,36 +275,17 @@ export class BillingSubscriptionController { return {success: true}; } - // ------------------------------------------------------------------------- - // INVOICE - // ------------------------------------------------------------------------- - - /** - * Get detailed price breakdown (total, tax, amount excluding tax) for an invoice. - */ - @authorize({permissions: ['*']}) + @authorize({permissions: [PermissionKey.ViewInvoice]}) + @authenticate(STRATEGY.BEARER, {passReqToCallback: true}) @get(`${BASE}/invoices/{invoiceId}/price-details`, { + security: OPERATION_SECURITY_SPEC, summary: 'Get invoice price details (total, tax, subtotal)', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Invoice price breakdown', content: { 'application/json': { - schema: { - type: 'object', - properties: { - currency: {type: 'string', example: 'usd'}, - totalAmount: {type: 'number', example: 5499}, - taxAmount: {type: 'number', example: 500}, - amountExcludingTax: {type: 'number', example: 4999}, - }, - required: [ - 'currency', - 'totalAmount', - 'taxAmount', - 'amountExcludingTax', - ], - }, + schema: getModelSchemaRefSF(InvoicePriceDto), }, }, }, @@ -476,24 +297,17 @@ export class BillingSubscriptionController { return this.billingService.getInvoicePriceDetails(invoiceId); } - /** - * Send the payment link for a given invoice to the customer. - */ - @authorize({permissions: ['*']}) + @authorize({permissions: [PermissionKey.CreateInvoice]}) + @authenticate(STRATEGY.BEARER, {passReqToCallback: true}) @post(`${BASE}/invoices/{invoiceId}/send-payment-link`, { + security: OPERATION_SECURITY_SPEC, summary: 'Send hosted payment link for an invoice', responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Payment link sent', content: { 'application/json': { - schema: { - type: 'object', - properties: { - success: {type: 'boolean', example: true}, - }, - required: ['success'], - }, + schema: getModelSchemaRefSF(SuccessDto), }, }, }, diff --git a/services/subscription-service/src/models/dto/index.ts b/services/subscription-service/src/models/dto/index.ts index 3037b0e..4506f65 100644 --- a/services/subscription-service/src/models/dto/index.ts +++ b/services/subscription-service/src/models/dto/index.ts @@ -3,3 +3,14 @@ export * from './charge-dto.model'; export * from './customer-dto.model'; export * from './invoice-dto.model'; export * from './payment-dto.model'; +export * from './price-dto.model'; +export * from './product-dto.model'; +export * from './subscription-create-dto.model'; +export * from './subscription-update-dto.model'; +export * from './subscription-result-dto.model'; +export * from './subscription-create-response-dto.model'; +export * from './success-dto.model'; +export * from './invoice-price-dto.model'; +export * from './invoice-pdf-dto.model'; +export * from './invoice-payment-details-dto.model'; +export * from './payment-intent-dto.model'; diff --git a/services/subscription-service/src/models/dto/invoice-payment-details-dto.model.ts b/services/subscription-service/src/models/dto/invoice-payment-details-dto.model.ts new file mode 100644 index 0000000..7a83bf7 --- /dev/null +++ b/services/subscription-service/src/models/dto/invoice-payment-details-dto.model.ts @@ -0,0 +1,26 @@ +import {model, Model, property} from '@loopback/repository'; + +@model({name: 'invoice_payment_details_dto'}) +export class InvoicePaymentDetailsDto extends Model { + @property({type: 'string'}) + invoiceId: string; + + @property({type: 'object'}) + paymentMethod: object; + + @property({type: 'number'}) + paymentDate?: number; + + @property({type: 'number'}) + amount?: number; + + @property({type: 'string'}) + currency?: string; + + @property({type: 'string'}) + status?: string; + + constructor(data?: Partial) { + super(data); + } +} diff --git a/services/subscription-service/src/models/dto/invoice-pdf-dto.model.ts b/services/subscription-service/src/models/dto/invoice-pdf-dto.model.ts new file mode 100644 index 0000000..f73e2e7 --- /dev/null +++ b/services/subscription-service/src/models/dto/invoice-pdf-dto.model.ts @@ -0,0 +1,20 @@ +import {model, Model, property} from '@loopback/repository'; + +@model({name: 'invoice_pdf_dto'}) +export class InvoicePdfDto extends Model { + @property({type: 'string'}) + invoiceId: string; + + @property({type: 'string'}) + pdfUrl: string; + + @property({type: 'string', format: 'date-time'}) + generatedAt: string; + + @property({type: 'string', format: 'date-time', nullable: true}) + expiresAt?: string | null; + + constructor(data?: Partial) { + super(data); + } +} diff --git a/services/subscription-service/src/models/dto/invoice-price-dto.model.ts b/services/subscription-service/src/models/dto/invoice-price-dto.model.ts new file mode 100644 index 0000000..f3079b5 --- /dev/null +++ b/services/subscription-service/src/models/dto/invoice-price-dto.model.ts @@ -0,0 +1,20 @@ +import {model, Model, property} from '@loopback/repository'; + +@model({name: 'invoice_price_dto'}) +export class InvoicePriceDto extends Model { + @property({type: 'string'}) + currency: string; + + @property({type: 'number'}) + totalAmount: number; + + @property({type: 'number'}) + taxAmount: number; + + @property({type: 'number'}) + amountExcludingTax: number; + + constructor(data?: Partial) { + super(data); + } +} diff --git a/services/subscription-service/src/models/dto/payment-intent-dto.model.ts b/services/subscription-service/src/models/dto/payment-intent-dto.model.ts new file mode 100644 index 0000000..93407cd --- /dev/null +++ b/services/subscription-service/src/models/dto/payment-intent-dto.model.ts @@ -0,0 +1,35 @@ +import {model, Model, property} from '@loopback/repository'; + +@model({name: 'payment_intent_dto'}) +export class PaymentIntentDto extends Model { + @property({type: 'string'}) + id: string; + + @property({type: 'number'}) + amount: number; + + @property({type: 'string'}) + currency: string; + + @property({type: 'string'}) + status: string; + + @property({type: 'number'}) + created: number; + + @property({type: 'string', nullable: true}) + customer?: string; + + @property({type: 'object'}) + paymentMethod?: object; + + @property({type: 'string', nullable: true}) + description?: string; + + @property({type: 'object', additionalProperties: true}) + metadata?: object; + + constructor(data?: Partial) { + super(data); + } +} diff --git a/services/subscription-service/src/models/dto/price-dto.model.ts b/services/subscription-service/src/models/dto/price-dto.model.ts new file mode 100644 index 0000000..203300f --- /dev/null +++ b/services/subscription-service/src/models/dto/price-dto.model.ts @@ -0,0 +1,34 @@ +import {model, property} from '@loopback/repository'; + +@model({settings: {strict: false}}) +export class PriceDto { + @property({type: 'string'}) + id?: string; + + @property({type: 'string'}) + currency: string; + + @property({type: 'number'}) + unitAmount: number; + + @property({type: 'string'}) + product: string; + + @property({type: 'boolean'}) + active?: boolean; + + @property({type: 'object'}) + recurring?: { + interval?: string; + intervalCount?: number; + }; + + @property({type: 'object'}) + metadata?: object; + + constructor(data?: Partial) { + Object.assign(this, data); + } +} + +export type PriceDtoWithRelations = PriceDto; diff --git a/services/subscription-service/src/models/dto/product-dto.model.ts b/services/subscription-service/src/models/dto/product-dto.model.ts new file mode 100644 index 0000000..8535f6c --- /dev/null +++ b/services/subscription-service/src/models/dto/product-dto.model.ts @@ -0,0 +1,20 @@ +import {model, Model, property} from '@loopback/repository'; + +@model({name: 'product_dto'}) +export class ProductDto extends Model { + @property({type: 'string', name: 'id'}) + id?: string; + + @property({type: 'string', name: 'name'}) + name: string; + + @property({type: 'string', name: 'description'}) + description?: string; + + @property({type: 'object', name: 'metadata'}) + metadata?: object; + + constructor(data?: Partial) { + super(data); + } +} diff --git a/services/subscription-service/src/models/dto/subscription-create-dto.model.ts b/services/subscription-service/src/models/dto/subscription-create-dto.model.ts new file mode 100644 index 0000000..dcd894a --- /dev/null +++ b/services/subscription-service/src/models/dto/subscription-create-dto.model.ts @@ -0,0 +1,20 @@ +import {model, Model, property} from '@loopback/repository'; + +@model({name: 'subscription_create_dto'}) +export class SubscriptionCreateDto extends Model { + @property({type: 'string'}) + customerId: string; + + @property({type: 'string'}) + priceRefId: string; + + @property({type: 'string'}) + collectionMethod: string; + + @property({type: 'number'}) + daysUntilDue?: number; + + constructor(data?: Partial) { + super(data); + } +} diff --git a/services/subscription-service/src/models/dto/subscription-create-response-dto.model.ts b/services/subscription-service/src/models/dto/subscription-create-response-dto.model.ts new file mode 100644 index 0000000..3c5e612 --- /dev/null +++ b/services/subscription-service/src/models/dto/subscription-create-response-dto.model.ts @@ -0,0 +1,11 @@ +import {model, Model, property} from '@loopback/repository'; + +@model({name: 'subscription_create_response_dto'}) +export class SubscriptionCreateResponseDto extends Model { + @property({type: 'string'}) + subscriptionId: string; + + constructor(data?: Partial) { + super(data); + } +} diff --git a/services/subscription-service/src/models/dto/subscription-result-dto.model.ts b/services/subscription-service/src/models/dto/subscription-result-dto.model.ts new file mode 100644 index 0000000..e9e6ec4 --- /dev/null +++ b/services/subscription-service/src/models/dto/subscription-result-dto.model.ts @@ -0,0 +1,26 @@ +import {model, Model, property} from '@loopback/repository'; + +@model({name: 'subscription_result_dto'}) +export class SubscriptionResultDto extends Model { + @property({type: 'string'}) + id: string; + + @property({type: 'string'}) + status: string; + + @property({type: 'string'}) + customerId: string; + + @property({type: 'number'}) + currentPeriodStart?: number; + + @property({type: 'number'}) + currentPeriodEnd?: number; + + @property({type: 'boolean'}) + cancelAtPeriodEnd?: boolean; + + constructor(data?: Partial) { + super(data); + } +} diff --git a/services/subscription-service/src/models/dto/subscription-update-dto.model.ts b/services/subscription-service/src/models/dto/subscription-update-dto.model.ts new file mode 100644 index 0000000..6dde075 --- /dev/null +++ b/services/subscription-service/src/models/dto/subscription-update-dto.model.ts @@ -0,0 +1,14 @@ +import {model, Model, property} from '@loopback/repository'; + +@model({name: 'subscription_update_dto'}) +export class SubscriptionUpdateDto extends Model { + @property({type: 'string'}) + priceRefId?: string; + + @property({type: 'string'}) + prorationBehavior?: string; + + constructor(data?: Partial) { + super(data); + } +} diff --git a/services/subscription-service/src/models/dto/success-dto.model.ts b/services/subscription-service/src/models/dto/success-dto.model.ts new file mode 100644 index 0000000..2116420 --- /dev/null +++ b/services/subscription-service/src/models/dto/success-dto.model.ts @@ -0,0 +1,11 @@ +import {model, Model, property} from '@loopback/repository'; + +@model({name: 'success_dto'}) +export class SuccessDto extends Model { + @property({type: 'boolean'}) + success: boolean; + + constructor(data?: Partial) { + super(data); + } +}