Replace Logger with getLogger and update next version

This commit replaces the use of Logger with getLogger in various parts of the code to handle logging. The Logger has been replaced with getLogger, which assists in getting logs in an asynchronous manner. In addition to this, it updates the next version in pnpm-lock.yaml from next@14.2.0-canary.61 to next@14.2.0-canary.62 and various other dependencies. Also made minor annotations and comments to the function 'isBrowser' and 'formatCurrency' in the 'utils.ts' file.
This commit is contained in:
giancarlo
2024-04-08 12:23:15 +08:00
parent 2b447167f7
commit 9fca45c2de
33 changed files with 369 additions and 250 deletions

View File

@@ -5,7 +5,7 @@ import { z } from 'zod';
import { getProductPlanPair } from '@kit/billing';
import { getBillingGatewayProvider } from '@kit/billing-gateway';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { Database } from '@kit/supabase/database';
import { requireUser } from '@kit/supabase/require-user';
import { getSupabaseServerActionClient } from '@kit/supabase/server-actions-client';
@@ -51,8 +51,9 @@ export class UserBillingService {
}
const { plan } = getProductPlanPair(billingConfig, planId);
const logger = await getLogger();
Logger.info(
logger.info(
{
name: `billing.personal-account`,
planId,
@@ -73,7 +74,7 @@ export class UserBillingService {
variantQuantities: [],
});
Logger.info(
logger.info(
{
userId: user.id,
},
@@ -86,7 +87,7 @@ export class UserBillingService {
checkoutToken,
};
} catch (error) {
Logger.error(
logger.error(
{
name: `billing.personal-account`,
planId,
@@ -118,7 +119,9 @@ export class UserBillingService {
throw new Error('Customer not found');
}
Logger.info(
const logger = await getLogger();
logger.info(
{
name: `billing.personal-account`,
customerId,
@@ -137,7 +140,7 @@ export class UserBillingService {
url = session.url;
} catch (error) {
Logger.error(
logger.error(
{
error,
customerId,
@@ -151,7 +154,7 @@ export class UserBillingService {
);
}
Logger.info(
logger.info(
{
name: `billing.personal-account`,
customerId,
@@ -167,6 +170,14 @@ export class UserBillingService {
async function getCustomerIdFromAccountId(accountId: string) {
const client = getSupabaseServerActionClient();
const logger = await getLogger();
logger.info(
{
accountId,
},
`Getting customer ID for account ${accountId}...`,
);
const { data, error } = await client
.from('billing_customers')
@@ -175,6 +186,14 @@ async function getCustomerIdFromAccountId(accountId: string) {
.maybeSingle();
if (error) {
logger.error(
{
accountId,
error,
},
`Failed to get customer ID`,
);
throw error;
}

View File

@@ -5,7 +5,7 @@ import { z } from 'zod';
import { LineItemSchema } from '@kit/billing';
import { getBillingGatewayProvider } from '@kit/billing-gateway';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { Database } from '@kit/supabase/database';
import { requireUser } from '@kit/supabase/require-user';
import { getSupabaseServerActionClient } from '@kit/supabase/server-actions-client';
@@ -35,8 +35,9 @@ export class TeamBillingService {
const userId = user.id;
const accountId = params.accountId;
const logger = await getLogger();
Logger.info(
logger.info(
{
userId,
accountId,
@@ -54,7 +55,7 @@ export class TeamBillingService {
// if the user does not have permission to manage billing for the account
// then we should not proceed
if (!hasPermission) {
Logger.warn(
logger.warn(
{
userId,
accountId,
@@ -90,7 +91,7 @@ export class TeamBillingService {
accountId,
);
Logger.info(
logger.info(
{
userId,
accountId,
@@ -117,7 +118,7 @@ export class TeamBillingService {
checkoutToken,
};
} catch (error) {
Logger.error(
logger.error(
{
name: this.namespace,
error,
@@ -145,8 +146,9 @@ export class TeamBillingService {
slug: string;
}) {
const client = getSupabaseServerActionClient();
const logger = await getLogger();
Logger.info(
logger.info(
{
accountId,
name: this.namespace,
@@ -171,7 +173,7 @@ export class TeamBillingService {
// if the user does not have permission to manage billing for the account
// then we should not proceed
if (!hasPermission) {
Logger.warn(
logger.warn(
{
userId,
accountId,
@@ -190,7 +192,7 @@ export class TeamBillingService {
throw new Error('Customer not found');
}
Logger.info(
logger.info(
{
userId,
customerId,
@@ -211,7 +213,7 @@ export class TeamBillingService {
// redirect the user to the billing portal
return url;
} catch (error) {
Logger.error(
logger.error(
{
userId,
customerId,
@@ -260,7 +262,9 @@ export class TeamBillingService {
.eq('account_id', accountId);
if (error) {
Logger.error(
const logger = await getLogger();
logger.error(
{
accountId,
error,

View File

@@ -68,7 +68,7 @@ async function ReturnCheckoutSessionPage({ searchParams }: SessionPageProps) {
export default withI18n(ReturnCheckoutSessionPage);
export async function loadCheckoutSession(sessionId: string) {
async function loadCheckoutSession(sessionId: string) {
const client = getSupabaseServerComponentClient();
const { error } = await requireUser(client);

View File

@@ -0,0 +1,3 @@
export default function AccountPage() {
return <div></div>;
}

View File

@@ -1,5 +1,5 @@
import { getBillingEventHandlerService } from '@kit/billing-gateway';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { getSupabaseRouteHandlerClient } from '@kit/supabase/route-handler-client';
import billingConfig from '~/config/billing.config';
@@ -9,8 +9,9 @@ import billingConfig from '~/config/billing.config';
*/
export async function POST(request: Request) {
const provider = billingConfig.provider;
const logger = await getLogger();
Logger.info(
logger.info(
{
name: 'billing.webhook',
provider,
@@ -30,7 +31,7 @@ export async function POST(request: Request) {
try {
await service.handleWebhookEvent(request);
Logger.info(
logger.info(
{
name: 'billing.webhook',
},
@@ -39,7 +40,7 @@ export async function POST(request: Request) {
return new Response('OK', { status: 200 });
} catch (e) {
Logger.error(
logger.error(
{
name: 'billing',
error: e,

View File

@@ -1,7 +1,7 @@
import { redirect } from 'next/navigation';
import type { NextRequest } from 'next/server';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { getSupabaseRouteHandlerClient } from '@kit/supabase/route-handler-client';
import pathsConfig from '~/config/paths.config';
@@ -38,9 +38,12 @@ export async function GET(request: NextRequest) {
return onError({ error: error.message });
}
} catch (error) {
Logger.error(
const logger = await getLogger();
logger.error(
{
error,
name: `auth.callback`,
},
`An error occurred while exchanging code for session`,
);
@@ -58,12 +61,14 @@ export async function GET(request: NextRequest) {
return redirect(nextUrl);
}
function onError({ error }: { error: string }) {
async function onError({ error }: { error: string }) {
const errorMessage = getAuthErrorMessage(error);
const logger = await getLogger();
Logger.error(
logger.error(
{
error,
name: `auth.callback`,
},
`An error occurred while signing user in`,
);

View File

@@ -63,9 +63,10 @@ async function JoinTeamAccountPage({ searchParams }: Context) {
);
if (isInAccount) {
const { Logger } = await import('@kit/shared/logger');
const { getLogger } = await import('@kit/shared/logger');
const logger = await getLogger();
Logger.warn(
logger.warn(
{
name: 'join-team-account',
accountId: invitation.account.id,

View File

@@ -1,7 +1,12 @@
import { registerInstrumentation } from '@kit/monitoring';
/**
* This file is used to register monitoring instrumentation
* for your Next.js application.
*/
export async function register() {
// only run in nodejs runtime
if (process.env.NEXT_RUNTIME === 'nodejs') {
const { registerInstrumentation } = await import('@kit/monitoring');
export function register() {
if (process.env.NEXT_RUNTIME !== 'nodejs') {
// Register monitoring instrumentation based on the
// MONITORING_INSTRUMENTATION_PROVIDER environment variable.
return registerInstrumentation();

View File

@@ -45,7 +45,7 @@
"i18next": "^23.10.1",
"i18next-resources-to-backend": "^1.2.0",
"lucide-react": "^0.363.0",
"next": "14.2.0-canary.61",
"next": "14.2.0-canary.62",
"next-sitemap": "^4.2.3",
"next-themes": "0.3.0",
"react": "18.2.0",

View File

@@ -1,7 +1,7 @@
import { SupabaseClient } from '@supabase/supabase-js';
import { BillingWebhookHandlerService } from '@kit/billing';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { Database } from '@kit/supabase/database';
export class BillingEventHandlerService {
@@ -22,10 +22,11 @@ export class BillingEventHandlerService {
return this.strategy.handleWebhookEvent(event, {
onSubscriptionDeleted: async (subscriptionId: string) => {
const client = this.clientProvider();
const logger = await getLogger();
// Handle the subscription deleted event
// here we delete the subscription from the database
Logger.info(
logger.info(
{
namespace: this.namespace,
subscriptionId,
@@ -42,7 +43,7 @@ export class BillingEventHandlerService {
throw new Error('Failed to delete subscription');
}
Logger.info(
logger.info(
{
namespace: this.namespace,
subscriptionId,
@@ -52,6 +53,7 @@ export class BillingEventHandlerService {
},
onSubscriptionUpdated: async (subscription) => {
const client = this.clientProvider();
const logger = await getLogger();
const ctx = {
namespace: this.namespace,
@@ -61,14 +63,14 @@ export class BillingEventHandlerService {
customerId: subscription.target_customer_id,
};
Logger.info(ctx, 'Processing subscription updated event');
logger.info(ctx, 'Processing subscription updated event');
// Handle the subscription updated event
// here we update the subscription in the database
const { error } = await client.rpc('upsert_subscription', subscription);
if (error) {
Logger.error(
logger.error(
{
error,
...ctx,
@@ -79,12 +81,13 @@ export class BillingEventHandlerService {
throw new Error('Failed to update subscription');
}
Logger.info(ctx, 'Successfully updated subscription');
logger.info(ctx, 'Successfully updated subscription');
},
onCheckoutSessionCompleted: async (payload) => {
// Handle the checkout session completed event
// here we add the subscription to the database
const client = this.clientProvider();
const logger = await getLogger();
// Check if the payload contains an order_id
// if it does, we add an order, otherwise we add a subscription
@@ -97,17 +100,17 @@ export class BillingEventHandlerService {
customerId: payload.target_customer_id,
};
Logger.info(ctx, 'Processing order completed event...');
logger.info(ctx, 'Processing order completed event...');
const { error } = await client.rpc('upsert_order', payload);
if (error) {
Logger.error({ ...ctx, error }, 'Failed to add order');
logger.error({ ...ctx, error }, 'Failed to add order');
throw new Error('Failed to add order');
}
Logger.info(ctx, 'Successfully added order');
logger.info(ctx, 'Successfully added order');
} else {
const ctx = {
namespace: this.namespace,
@@ -117,25 +120,26 @@ export class BillingEventHandlerService {
customerId: payload.target_customer_id,
};
Logger.info(ctx, 'Processing checkout session completed event...');
logger.info(ctx, 'Processing checkout session completed event...');
const { error } = await client.rpc('upsert_subscription', payload);
if (error) {
Logger.error({ ...ctx, error }, 'Failed to add subscription');
logger.error({ ...ctx, error }, 'Failed to add subscription');
throw new Error('Failed to add subscription');
}
Logger.info(ctx, 'Successfully added subscription');
logger.info(ctx, 'Successfully added subscription');
}
},
onPaymentSucceeded: async (sessionId: string) => {
const client = this.clientProvider();
const logger = await getLogger();
// Handle the payment succeeded event
// here we update the payment status in the database
Logger.info(
logger.info(
{
namespace: this.namespace,
sessionId,
@@ -152,7 +156,7 @@ export class BillingEventHandlerService {
throw new Error('Failed to update payment status');
}
Logger.info(
logger.info(
{
namespace: this.namespace,
sessionId,
@@ -162,10 +166,11 @@ export class BillingEventHandlerService {
},
onPaymentFailed: async (sessionId: string) => {
const client = this.clientProvider();
const logger = await getLogger();
// Handle the payment failed event
// here we update the payment status in the database
Logger.info(
logger.info(
{
namespace: this.namespace,
sessionId,
@@ -182,7 +187,7 @@ export class BillingEventHandlerService {
throw new Error('Failed to update payment status');
}
Logger.info(
logger.info(
{
namespace: this.namespace,
sessionId,

View File

@@ -16,7 +16,7 @@ import {
RetrieveCheckoutSessionSchema,
UpdateSubscriptionParamsSchema,
} from '@kit/billing/schema';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { createLemonSqueezyBillingPortalSession } from './create-lemon-squeezy-billing-portal-session';
import { createLemonSqueezyCheckout } from './create-lemon-squeezy-checkout';
@@ -27,13 +27,12 @@ export class LemonSqueezyBillingStrategyService
async createCheckoutSession(
params: z.infer<typeof CreateBillingCheckoutSchema>,
) {
Logger.info(
const logger = await getLogger();
logger.info(
{
name: 'billing.lemon-squeezy',
customerId: params.customerId,
accountId: params.accountId,
returnUrl: params.returnUrl,
trialDays: params.trialDays,
...params,
},
'Creating checkout session...',
);
@@ -43,7 +42,7 @@ export class LemonSqueezyBillingStrategyService
if (error ?? !response?.data.id) {
console.log(error);
Logger.error(
logger.error(
{
name: 'billing.lemon-squeezy',
customerId: params.customerId,
@@ -56,7 +55,7 @@ export class LemonSqueezyBillingStrategyService
throw new Error('Failed to create checkout session');
}
Logger.info(
logger.info(
{
name: 'billing.lemon-squeezy',
customerId: params.customerId,
@@ -73,7 +72,9 @@ export class LemonSqueezyBillingStrategyService
async createBillingPortalSession(
params: z.infer<typeof CreateBillingPortalSessionSchema>,
) {
Logger.info(
const logger = await getLogger();
logger.info(
{
name: 'billing.lemon-squeezy',
customerId: params.customerId,
@@ -85,7 +86,7 @@ export class LemonSqueezyBillingStrategyService
await createLemonSqueezyBillingPortalSession(params);
if (error ?? !data) {
Logger.error(
logger.error(
{
name: 'billing.lemon-squeezy',
customerId: params.customerId,
@@ -97,7 +98,7 @@ export class LemonSqueezyBillingStrategyService
throw new Error('Failed to create billing portal session');
}
Logger.info(
logger.info(
{
name: 'billing.lemon-squeezy',
customerId: params.customerId,
@@ -111,7 +112,9 @@ export class LemonSqueezyBillingStrategyService
async cancelSubscription(
params: z.infer<typeof CancelSubscriptionParamsSchema>,
) {
Logger.info(
const logger = await getLogger();
logger.info(
{
name: 'billing.lemon-squeezy',
subscriptionId: params.subscriptionId,
@@ -123,7 +126,7 @@ export class LemonSqueezyBillingStrategyService
const { error } = await cancelSubscription(params.subscriptionId);
if (error) {
Logger.error(
logger.error(
{
name: 'billing.lemon-squeezy',
subscriptionId: params.subscriptionId,
@@ -135,7 +138,7 @@ export class LemonSqueezyBillingStrategyService
throw error;
}
Logger.info(
logger.info(
{
name: 'billing.lemon-squeezy',
subscriptionId: params.subscriptionId,
@@ -145,7 +148,7 @@ export class LemonSqueezyBillingStrategyService
return { success: true };
} catch (error) {
Logger.error(
logger.error(
{
name: 'billing.lemon-squeezy',
subscriptionId: params.subscriptionId,
@@ -161,7 +164,9 @@ export class LemonSqueezyBillingStrategyService
async retrieveCheckoutSession(
params: z.infer<typeof RetrieveCheckoutSessionSchema>,
) {
Logger.info(
const logger = await getLogger();
logger.info(
{
name: 'billing.lemon-squeezy',
sessionId: params.sessionId,
@@ -172,7 +177,7 @@ export class LemonSqueezyBillingStrategyService
const { data: session, error } = await getCheckout(params.sessionId);
if (error ?? !session?.data) {
Logger.error(
logger.error(
{
name: 'billing.lemon-squeezy',
sessionId: params.sessionId,
@@ -184,7 +189,7 @@ export class LemonSqueezyBillingStrategyService
throw new Error('Failed to retrieve checkout session');
}
Logger.info(
logger.info(
{
name: 'billing.lemon-squeezy',
sessionId: params.sessionId,
@@ -205,7 +210,9 @@ export class LemonSqueezyBillingStrategyService
}
async reportUsage(params: z.infer<typeof ReportBillingUsageSchema>) {
Logger.info(
const logger = await getLogger();
logger.info(
{
name: 'billing.lemon-squeezy',
subscriptionItemId: params.subscriptionItemId,
@@ -220,11 +227,11 @@ export class LemonSqueezyBillingStrategyService
});
if (error) {
Logger.error(
logger.error(
{
name: 'billing.lemon-squeezy',
subscriptionItemId: params.subscriptionItemId,
error: error.message,
error,
},
'Failed to report usage',
);
@@ -232,7 +239,7 @@ export class LemonSqueezyBillingStrategyService
throw new Error('Failed to report usage');
}
Logger.info(
logger.info(
{
name: 'billing.lemon-squeezy',
subscriptionItemId: params.subscriptionItemId,
@@ -246,19 +253,21 @@ export class LemonSqueezyBillingStrategyService
async updateSubscription(
params: z.infer<typeof UpdateSubscriptionParamsSchema>,
) {
const logger = await getLogger();
const ctx = {
name: 'billing.lemon-squeezy',
...params,
};
Logger.info(ctx, 'Updating subscription...');
logger.info(ctx, 'Updating subscription...');
const { error } = await updateSubscriptionItem(params.subscriptionItemId, {
quantity: params.quantity,
});
if (error) {
Logger.error(
logger.error(
{
...ctx,
error,
@@ -269,7 +278,7 @@ export class LemonSqueezyBillingStrategyService
throw error;
}
Logger.info(ctx, 'Subscription updated successfully');
logger.info(ctx, 'Subscription updated successfully');
return { success: true };
}

View File

@@ -1,6 +1,6 @@
import 'server-only';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { getLemonSqueezyEnv } from '../schema/lemon-squeezy-server-env.schema';
@@ -10,11 +10,12 @@ import { getLemonSqueezyEnv } from '../schema/lemon-squeezy-server-env.schema';
export async function initializeLemonSqueezyClient() {
const { lemonSqueezySetup } = await import('@lemonsqueezy/lemonsqueezy.js');
const env = getLemonSqueezyEnv();
const logger = await getLogger();
lemonSqueezySetup({
apiKey: env.secretKey,
onError(error) {
Logger.error(
logger.error(
{
name: `billing.lemon-squeezy`,
error: error.message,

View File

@@ -6,7 +6,7 @@ import {
BillingWebhookHandlerService,
getLineItemTypeById,
} from '@kit/billing';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { Database } from '@kit/supabase/database';
import { getLemonSqueezyEnv } from '../schema/lemon-squeezy-server-env.schema';
@@ -45,6 +45,9 @@ export class LemonSqueezyWebhookHandlerService
* @description Verifies the webhook signature - should throw an error if the signature is invalid
*/
async verifyWebhookSignature(request: Request) {
const logger = await getLogger();
// get the event name and signature from the headers
const eventName = request.headers.get('x-event-name');
const signature = request.headers.get('x-signature') as string;
@@ -53,8 +56,9 @@ export class LemonSqueezyWebhookHandlerService
const body = (await request.json()) as SubscriptionWebhook | OrderWebhook;
const rawBody = await reqClone.text();
// if no signature is found, throw an error
if (!signature) {
Logger.error(
logger.error(
{
eventName,
},
@@ -64,8 +68,9 @@ export class LemonSqueezyWebhookHandlerService
throw new Error('Signature header not found');
}
// if the signature is invalid, throw an error
if (!isSigningSecretValid(Buffer.from(rawBody), signature)) {
Logger.error(
logger.error(
{
eventName,
},
@@ -124,7 +129,9 @@ export class LemonSqueezyWebhookHandlerService
}
default: {
Logger.info(
const logger = await getLogger();
logger.info(
{
eventType: eventName,
name: this.namespace,
@@ -211,7 +218,9 @@ export class LemonSqueezyWebhookHandlerService
const { data: order, error } = await getOrder(orderId);
if (error ?? !order) {
Logger.error(
const logger = await getLogger();
logger.error(
{
orderId,
subscriptionId,

View File

@@ -11,7 +11,7 @@ import {
RetrieveCheckoutSessionSchema,
UpdateSubscriptionParamsSchema,
} from '@kit/billing/schema';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { createStripeBillingPortalSession } from './create-stripe-billing-portal-session';
import { createStripeCheckout } from './create-stripe-checkout';
@@ -24,8 +24,9 @@ export class StripeBillingStrategyService
params: z.infer<typeof CreateBillingCheckoutSchema>,
) {
const stripe = await this.stripeProvider();
const logger = await getLogger();
Logger.info(
logger.info(
{
name: 'billing.stripe',
customerId: params.customerId,
@@ -37,7 +38,7 @@ export class StripeBillingStrategyService
const { client_secret } = await createStripeCheckout(stripe, params);
if (!client_secret) {
Logger.error(
logger.error(
{
name: 'billing.stripe',
customerId: params.customerId,
@@ -49,7 +50,7 @@ export class StripeBillingStrategyService
throw new Error('Failed to create checkout session');
}
Logger.info(
logger.info(
{
name: 'billing.stripe',
customerId: params.customerId,
@@ -65,8 +66,9 @@ export class StripeBillingStrategyService
params: z.infer<typeof CreateBillingPortalSessionSchema>,
) {
const stripe = await this.stripeProvider();
const logger = await getLogger();
Logger.info(
logger.info(
{
name: 'billing.stripe',
customerId: params.customerId,
@@ -77,7 +79,7 @@ export class StripeBillingStrategyService
const session = await createStripeBillingPortalSession(stripe, params);
if (!session?.url) {
Logger.error(
logger.error(
{
name: 'billing.stripe',
customerId: params.customerId,
@@ -85,7 +87,7 @@ export class StripeBillingStrategyService
'Failed to create billing portal session',
);
} else {
Logger.info(
logger.info(
{
name: 'billing.stripe',
customerId: params.customerId,
@@ -101,8 +103,9 @@ export class StripeBillingStrategyService
params: z.infer<typeof CancelSubscriptionParamsSchema>,
) {
const stripe = await this.stripeProvider();
const logger = await getLogger();
Logger.info(
logger.info(
{
name: 'billing.stripe',
subscriptionId: params.subscriptionId,
@@ -115,7 +118,7 @@ export class StripeBillingStrategyService
invoice_now: params.invoiceNow ?? true,
});
Logger.info(
logger.info(
{
name: 'billing.stripe',
subscriptionId: params.subscriptionId,
@@ -125,7 +128,7 @@ export class StripeBillingStrategyService
return { success: true };
} catch (e) {
Logger.error(
logger.error(
{
name: 'billing.stripe',
subscriptionId: params.subscriptionId,
@@ -142,8 +145,9 @@ export class StripeBillingStrategyService
params: z.infer<typeof RetrieveCheckoutSessionSchema>,
) {
const stripe = await this.stripeProvider();
const logger = await getLogger();
Logger.info(
logger.info(
{
name: 'billing.stripe',
sessionId: params.sessionId,
@@ -155,7 +159,7 @@ export class StripeBillingStrategyService
const session = await stripe.checkout.sessions.retrieve(params.sessionId);
const isSessionOpen = session.status === 'open';
Logger.info(
logger.info(
{
name: 'billing.stripe',
sessionId: params.sessionId,
@@ -172,7 +176,7 @@ export class StripeBillingStrategyService
},
};
} catch (error) {
Logger.error(
logger.error(
{
name: 'billing.stripe',
sessionId: params.sessionId,
@@ -203,8 +207,9 @@ export class StripeBillingStrategyService
params: z.infer<typeof UpdateSubscriptionParamsSchema>,
) {
const stripe = await this.stripeProvider();
const logger = await getLogger();
Logger.info(
logger.info(
{
name: 'billing.stripe',
...params,
@@ -222,7 +227,7 @@ export class StripeBillingStrategyService
],
});
Logger.info(
logger.info(
{
name: 'billing.stripe',
...params,
@@ -232,7 +237,7 @@ export class StripeBillingStrategyService
return { success: true };
} catch (e) {
Logger.error(
logger.error(
{
name: 'billing.stripe',
...params,

View File

@@ -5,7 +5,7 @@ import {
BillingWebhookHandlerService,
getLineItemTypeById,
} from '@kit/billing';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { Database } from '@kit/supabase/database';
import { StripeServerEnvSchema } from '../schema/stripe-server-env.schema';
@@ -113,6 +113,8 @@ export class StripeWebhookHandlerService
}
default: {
const Logger = await getLogger();
Logger.info(
{
eventType: event.type,

View File

@@ -1,6 +1,6 @@
import 'server-only';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { getSupabaseRouteHandlerClient } from '@kit/supabase/route-handler-client';
import { RecordChange, Tables } from '../record-change.type';
@@ -10,10 +10,12 @@ export class DatabaseWebhookHandlerService {
private readonly namespace = 'database-webhook-handler';
async handleWebhook(request: Request, webhooksSecret: string) {
const logger = await getLogger();
const json = await request.clone().json();
const { table, type } = json as RecordChange<keyof Tables>;
Logger.info(
logger.info(
{
name: this.namespace,
table,
@@ -40,7 +42,7 @@ export class DatabaseWebhookHandlerService {
// handle the webhook event based on the table
await service.handleWebhook(json);
Logger.info(
logger.info(
{
name: this.namespace,
table,
@@ -49,7 +51,7 @@ export class DatabaseWebhookHandlerService {
'Webhook processed successfully',
);
} catch (error) {
Logger.error(
logger.error(
{
name: this.namespace,
table,

View File

@@ -1,6 +1,6 @@
import { SupabaseClient } from '@supabase/supabase-js';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { Database } from '@kit/supabase/database';
import { RecordChange, Tables } from '../record-change.type';
@@ -8,7 +8,7 @@ import { RecordChange, Tables } from '../record-change.type';
export class DatabaseWebhookRouterService {
constructor(private readonly adminClient: SupabaseClient<Database>) {}
handleWebhook(body: RecordChange<keyof Tables>) {
async handleWebhook(body: RecordChange<keyof Tables>) {
switch (body.table) {
case 'invitations': {
const payload = body as RecordChange<typeof body.table>;
@@ -23,7 +23,9 @@ export class DatabaseWebhookRouterService {
}
default: {
Logger.warn(
const logger = await getLogger();
logger.warn(
{
table: body.table,
},

View File

@@ -4,7 +4,7 @@ import { RedirectType, redirect } from 'next/navigation';
import { z } from 'zod';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { requireUser } from '@kit/supabase/require-user';
import { getSupabaseServerActionClient } from '@kit/supabase/server-actions-client';
@@ -31,7 +31,8 @@ export async function deletePersonalAccountAction(formData: FormData) {
const auth = await requireUser(client);
if (auth.error) {
Logger.error(`User is not authenticated. Redirecting to login page`);
const logger = await getLogger();
logger.error(`User is not authenticated. Redirecting to login page`);
redirect(auth.redirectTo);
}

View File

@@ -1,7 +1,6 @@
import { SupabaseClient } from '@supabase/supabase-js';
import { Mailer } from '@kit/mailers';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { Database } from '@kit/supabase/database';
/**
@@ -35,8 +34,9 @@ export class DeletePersonalAccountService {
};
}) {
const userId = params.userId;
const logger = await getLogger();
Logger.info(
logger.info(
{ name: this.namespace, userId },
'User requested deletion. Processing...',
);
@@ -45,7 +45,7 @@ export class DeletePersonalAccountService {
try {
await params.adminClient.auth.admin.deleteUser(userId);
} catch (error) {
Logger.error(
logger.error(
{
name: this.namespace,
userId,
@@ -60,7 +60,7 @@ export class DeletePersonalAccountService {
// Send account deletion email
if (params.userEmail) {
try {
Logger.info(
logger.info(
{
name: this.namespace,
userId,
@@ -74,8 +74,16 @@ export class DeletePersonalAccountService {
userDisplayName: params.userEmail,
userEmail: params.userEmail,
});
logger.info(
{
name: this.namespace,
userId,
},
`Account deletion email sent`,
);
} catch (error) {
Logger.error(
logger.error(
{
name: this.namespace,
userId,
@@ -94,13 +102,15 @@ export class DeletePersonalAccountService {
productName: string;
}) {
const { renderAccountDeleteEmail } = await import('@kit/email-templates');
const { getMailer } = await import('@kit/mailers');
const mailer = await getMailer();
const html = renderAccountDeleteEmail({
userDisplayName: params.userDisplayName,
productName: params.productName,
});
await Mailer.sendEmail({
return mailer.sendEmail({
to: params.userEmail,
from: params.fromEmail,
subject: 'Account Deletion Request',

View File

@@ -4,7 +4,7 @@ import { redirect } from 'next/navigation';
import { z } from 'zod';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { requireUser } from '@kit/supabase/require-user';
import { getSupabaseServerActionClient } from '@kit/supabase/server-actions-client';
@@ -31,6 +31,7 @@ export async function createOrganizationAccountAction(
redirect(auth.redirectTo);
}
const logger = await getLogger();
const userId = auth.data.id;
const createAccountResponse = await service.createNewOrganizationAccount({
@@ -39,7 +40,7 @@ export async function createOrganizationAccountAction(
});
if (createAccountResponse.error) {
Logger.error(
logger.error(
{
userId,
error: createAccountResponse.error,
@@ -51,6 +52,15 @@ export async function createOrganizationAccountAction(
throw new Error('Error creating team account');
}
logger.info(
{
userId,
accountName,
name: 'accounts',
},
`Team account created successfully`,
);
const accountHomePath =
TEAM_ACCOUNTS_HOME_PATH + '/' + createAccountResponse.data.slug;

View File

@@ -2,8 +2,7 @@ import { SupabaseClient } from '@supabase/supabase-js';
import { z } from 'zod';
import { Mailer } from '@kit/mailers';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { Database } from '@kit/supabase/database';
type Invitation = Database['public']['Tables']['invitations']['Row'];
@@ -57,8 +56,12 @@ export class AccountInvitationsWebhookService {
throw team.error;
}
const logger = await getLogger();
try {
const { renderInviteEmail } = await import('@kit/email-templates');
const { getMailer } = await import('@kit/mailers');
const mailer = await getMailer();
const html = renderInviteEmail({
link: this.getInvitationLink(invitation.invite_token),
@@ -68,26 +71,34 @@ export class AccountInvitationsWebhookService {
teamName: team.data.name,
});
await Mailer.sendEmail({
from: env.emailSender,
to: invitation.email,
subject: 'You have been invited to join a team',
html,
});
Logger.info('Invitation email sent', {
email: invitation.email,
account: invitation.account_id,
name: this.namespace,
});
await mailer
.sendEmail({
from: env.emailSender,
to: invitation.email,
subject: 'You have been invited to join a team',
html,
})
.then(() => {
logger.info('Invitation email sent', {
email: invitation.email,
account: invitation.account_id,
name: this.namespace,
});
})
.catch((error) => {
logger.warn(
{ error, name: this.namespace },
'Failed to send invitation email',
);
});
return {
success: true,
};
} catch (error) {
Logger.warn(
logger.warn(
{ error, name: this.namespace },
'Failed to send invitation email',
'Failed to invite user to team',
);
return {

View File

@@ -4,7 +4,7 @@ import { addDays, formatISO } from 'date-fns';
import 'server-only';
import { z } from 'zod';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { Database } from '@kit/supabase/database';
import { DeleteInvitationSchema } from '../../schema/delete-invitation.schema';
@@ -17,7 +17,9 @@ export class AccountInvitationsService {
constructor(private readonly client: SupabaseClient<Database>) {}
async deleteInvitation(params: z.infer<typeof DeleteInvitationSchema>) {
Logger.info('Removing invitation', {
const logger = await getLogger();
logger.info('Removing invitation', {
name: this.namespace,
...params,
});
@@ -33,7 +35,7 @@ export class AccountInvitationsService {
throw error;
}
Logger.info('Invitation successfully removed', {
logger.info('Invitation successfully removed', {
...params,
name: this.namespace,
});
@@ -42,7 +44,9 @@ export class AccountInvitationsService {
}
async updateInvitation(params: z.infer<typeof UpdateInvitationSchema>) {
Logger.info('Updating invitation', {
const logger = await getLogger();
logger.info('Updating invitation', {
...params,
name: this.namespace,
});
@@ -60,7 +64,7 @@ export class AccountInvitationsService {
throw error;
}
Logger.info('Invitation successfully updated', {
logger.info('Invitation successfully updated', {
...params,
name: this.namespace,
});
@@ -75,7 +79,9 @@ export class AccountInvitationsService {
invitations: z.infer<typeof InviteMembersSchema>['invitations'];
accountSlug: string;
}) {
Logger.info(
const logger = await getLogger();
logger.info(
{
account: accountSlug,
invitations,
@@ -91,7 +97,7 @@ export class AccountInvitationsService {
.single();
if (!accountResponse.data) {
Logger.error(
logger.error(
{
accountSlug,
name: this.namespace,
@@ -108,7 +114,7 @@ export class AccountInvitationsService {
});
if (response.error) {
Logger.error(
logger.error(
{
accountSlug,
error: response.error,
@@ -124,7 +130,7 @@ export class AccountInvitationsService {
? response.data
: [response.data];
Logger.info(
logger.info(
{
account: accountSlug,
count: responseInvitations.length,
@@ -157,7 +163,9 @@ export class AccountInvitationsService {
}
async renewInvitation(invitationId: number) {
Logger.info('Renewing invitation', {
const logger = await getLogger();
logger.info('Renewing invitation', {
invitationId,
name: this.namespace,
});
@@ -177,7 +185,7 @@ export class AccountInvitationsService {
throw error;
}
Logger.info('Invitation successfully renewed', {
logger.info('Invitation successfully renewed', {
invitationId,
name: this.namespace,
});

View File

@@ -3,7 +3,7 @@ import { SupabaseClient } from '@supabase/supabase-js';
import 'server-only';
import { z } from 'zod';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { Database } from '@kit/supabase/database';
import { RemoveMemberSchema } from '../../schema/remove-member.schema';
@@ -17,12 +17,14 @@ export class AccountMembersService {
constructor(private readonly client: SupabaseClient<Database>) {}
async removeMemberFromAccount(params: z.infer<typeof RemoveMemberSchema>) {
const logger = await getLogger();
const ctx = {
namespace: this.namespace,
...params,
};
Logger.info(ctx, `Removing member from account...`);
logger.info(ctx, `Removing member from account...`);
const { data, error } = await this.client
.from('accounts_memberships')
@@ -33,7 +35,7 @@ export class AccountMembersService {
});
if (error) {
Logger.error(
logger.error(
{
...ctx,
error,
@@ -44,7 +46,7 @@ export class AccountMembersService {
throw error;
}
Logger.info(
logger.info(
ctx,
`Successfully removed member from account. Verifying seat count...`,
);
@@ -57,12 +59,14 @@ export class AccountMembersService {
}
async updateMemberRole(params: z.infer<typeof UpdateMemberRoleSchema>) {
const logger = await getLogger();
const ctx = {
namespace: this.namespace,
...params,
};
Logger.info(ctx, `Updating member role...`);
logger.info(ctx, `Updating member role...`);
const { data, error } = await this.client
.from('accounts_memberships')
@@ -75,7 +79,7 @@ export class AccountMembersService {
});
if (error) {
Logger.error(
logger.error(
{
...ctx,
error,
@@ -86,7 +90,7 @@ export class AccountMembersService {
throw error;
}
Logger.info(ctx, `Successfully updated member role`);
logger.info(ctx, `Successfully updated member role`);
return data;
}
@@ -94,12 +98,14 @@ export class AccountMembersService {
async transferOwnership(
params: z.infer<typeof TransferOwnershipConfirmationSchema>,
) {
const logger = await getLogger();
const ctx = {
namespace: this.namespace,
...params,
};
Logger.info(ctx, `Transferring ownership of account...`);
logger.info(ctx, `Transferring ownership of account...`);
const { data, error } = await this.client.rpc(
'transfer_team_account_ownership',
@@ -110,7 +116,7 @@ export class AccountMembersService {
);
if (error) {
Logger.error(
logger.error(
{ ...ctx, error },
`Failed to transfer ownership of account`,
);
@@ -118,7 +124,7 @@ export class AccountMembersService {
throw error;
}
Logger.info(ctx, `Successfully transferred ownership of account`);
logger.info(ctx, `Successfully transferred ownership of account`);
return data;
}

View File

@@ -1,7 +1,7 @@
import { SupabaseClient } from '@supabase/supabase-js';
import { BillingGatewayService } from '@kit/billing-gateway';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { Database } from '@kit/supabase/database';
export class AccountPerSeatBillingService {
@@ -10,7 +10,9 @@ export class AccountPerSeatBillingService {
constructor(private readonly client: SupabaseClient<Database>) {}
async getPerSeatSubscriptionItem(accountId: string) {
Logger.info(
const logger = await getLogger();
logger.info(
{
name: this.namespace,
accountId,
@@ -36,7 +38,7 @@ export class AccountPerSeatBillingService {
.maybeSingle();
if (error) {
Logger.info(
logger.error(
{
name: this.namespace,
accountId,
@@ -49,7 +51,7 @@ export class AccountPerSeatBillingService {
}
if (!data?.subscription_items) {
Logger.info(
logger.info(
{ name: this.namespace, accountId },
`No per-seat subscription item found for account ${accountId}. Exiting...`,
);
@@ -57,7 +59,7 @@ export class AccountPerSeatBillingService {
return;
}
Logger.info(
logger.info(
{
name: this.namespace,
accountId,
@@ -69,6 +71,7 @@ export class AccountPerSeatBillingService {
}
async increaseSeats(accountId: string) {
const logger = await getLogger();
const subscription = await this.getPerSeatSubscriptionItem(accountId);
if (!subscription) {
@@ -85,7 +88,7 @@ export class AccountPerSeatBillingService {
const billingGateway = new BillingGatewayService(subscription.provider);
Logger.info(
logger.info(
{
name: this.namespace,
accountId,
@@ -96,7 +99,7 @@ export class AccountPerSeatBillingService {
const promises = subscriptionItems.map(async (item) => {
try {
Logger.info(
logger.info(
{
name: this.namespace,
accountId,
@@ -112,7 +115,7 @@ export class AccountPerSeatBillingService {
quantity: item.quantity + 1,
});
Logger.info(
logger.info(
{
name: this.namespace,
accountId,
@@ -122,7 +125,7 @@ export class AccountPerSeatBillingService {
`Subscription item updated successfully`,
);
} catch (error) {
Logger.error(
logger.error(
{
name: this.namespace,
accountId,
@@ -137,6 +140,7 @@ export class AccountPerSeatBillingService {
}
async decreaseSeats(accountId: string) {
const logger = await getLogger();
const subscription = await this.getPerSeatSubscriptionItem(accountId);
if (!subscription) {
@@ -151,7 +155,7 @@ export class AccountPerSeatBillingService {
return;
}
Logger.info(
logger.info(
{
name: this.namespace,
accountId,
@@ -164,7 +168,7 @@ export class AccountPerSeatBillingService {
const promises = subscriptionItems.map(async (item) => {
try {
Logger.info(
logger.info(
{
name: this.namespace,
accountId,
@@ -180,7 +184,7 @@ export class AccountPerSeatBillingService {
quantity: item.quantity - 1,
});
Logger.info(
logger.info(
{
name: this.namespace,
accountId,
@@ -190,7 +194,7 @@ export class AccountPerSeatBillingService {
`Subscription item updated successfully`,
);
} catch (error) {
Logger.error(
logger.error(
{
name: this.namespace,
accountId,

View File

@@ -1,6 +1,6 @@
import { SupabaseClient } from '@supabase/supabase-js';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { Database } from '@kit/supabase/database';
export class CreateTeamAccountService {
@@ -8,13 +8,15 @@ export class CreateTeamAccountService {
constructor(private readonly client: SupabaseClient<Database>) {}
createNewOrganizationAccount(params: { name: string; userId: string }) {
Logger.info(
async createNewOrganizationAccount(params: { name: string; userId: string }) {
const logger = await getLogger();
logger.info(
{ ...params, namespace: this.namespace },
`Creating new team account...`,
);
return this.client.rpc('create_account', {
return await this.client.rpc('create_account', {
account_name: params.name,
});
}

View File

@@ -2,7 +2,7 @@ import { SupabaseClient } from '@supabase/supabase-js';
import 'server-only';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { Database } from '@kit/supabase/database';
export class DeleteTeamAccountService {
@@ -24,7 +24,9 @@ export class DeleteTeamAccountService {
userId: string;
},
) {
Logger.info(
const logger = await getLogger();
logger.info(
{
name: this.namespace,
accountId: params.accountId,
@@ -40,7 +42,7 @@ export class DeleteTeamAccountService {
.eq('id', params.accountId);
if (error) {
Logger.error(
logger.error(
{
name: this.namespace,
accountId: params.accountId,
@@ -53,7 +55,7 @@ export class DeleteTeamAccountService {
throw new Error('Failed to delete team account');
}
Logger.info(
logger.info(
{
name: this.namespace,
accountId: params.accountId,

View File

@@ -3,7 +3,7 @@ import { SupabaseClient } from '@supabase/supabase-js';
import 'server-only';
import { z } from 'zod';
import { Logger } from '@kit/shared/logger';
import { getLogger } from '@kit/shared/logger';
import { Database } from '@kit/supabase/database';
const Schema = z.object({
@@ -17,12 +17,14 @@ export class LeaveTeamAccountService {
constructor(private readonly adminClient: SupabaseClient<Database>) {}
async leaveTeamAccount(params: z.infer<typeof Schema>) {
const logger = await getLogger();
const ctx = {
...params,
name: this.namespace,
};
Logger.info(ctx, 'Leaving team account');
logger.info(ctx, 'Leaving team account...');
const { accountId, userId } = Schema.parse(params);
@@ -35,11 +37,11 @@ export class LeaveTeamAccountService {
});
if (error) {
Logger.error({ ...ctx, error }, 'Failed to leave team account');
logger.error({ ...ctx, error }, 'Failed to leave team account');
throw new Error('Failed to leave team account');
}
Logger.info(ctx, 'Successfully left team account');
logger.info(ctx, 'Successfully left team account');
}
}

View File

@@ -27,13 +27,17 @@ MAILER_PROVIDER=cloudflare
### Send an email
```javascript
import { Mailer } from '@kit/mailers';
```tsx
import { getMailer } from '@kit/mailers';
Mailer.sendEmail({
async function sendEmail() {
const mailer = await getMailer();
return mailer.sendEmail({
to: '',
from: '',
subject: 'Hello',
text: 'Hello, World!'
});
});
}
```

View File

@@ -5,28 +5,10 @@ const MAILER_PROVIDER = z
.default('nodemailer')
.parse(process.env.MAILER_PROVIDER);
/**
* @description A mailer interface that can be implemented by any mailer.
* We export a single mailer implementation using Nodemailer. You can add more mailers or replace the existing one.
* @example
* ```ts
* import { Mailer } from '@kit/mailers';
*
* const mailer = new Mailer();
*
* mailer.sendEmail({
* from: '',
* to: '',
* subject: 'Hello',
* text: 'Hello, World!'
* });
*/
export const Mailer = await getMailer();
/**
* @description Get the mailer based on the environment variable.
*/
async function getMailer() {
export async function getMailer() {
switch (MAILER_PROVIDER) {
case 'nodemailer': {
const { Nodemailer } = await import('./impl/nodemailer');

View File

@@ -17,10 +17,9 @@ const DEFAULT_INSTRUMENTATION_PROVIDER = process.env
* Please set the MONITORING_INSTRUMENTATION_PROVIDER environment variable to register the monitoring instrumentation provider.
*/
export async function registerInstrumentation() {
if (
process.env.NEXT_RUNTIME !== 'nodejs' ||
!DEFAULT_INSTRUMENTATION_PROVIDER
) {
if (!DEFAULT_INSTRUMENTATION_PROVIDER) {
console.info(`No instrumentation provider specified. Skipping...`);
return;
}

View File

@@ -20,6 +20,4 @@ async function getLogger(): Promise<LoggerInstance> {
}
}
const Logger = await getLogger();
export { Logger };
export { getLogger };

View File

@@ -1,7 +1,14 @@
/**
* Check if the code is running in a browser environment.
*/
export function isBrowser() {
return typeof window !== 'undefined';
}
/**
*@name formatCurrency
* @description Format the currency based on the currency code
*/
export function formatCurrency(currencyCode: string, value: string | number) {
return new Intl.NumberFormat('en-US', {
style: 'currency',

86
pnpm-lock.yaml generated
View File

@@ -103,7 +103,7 @@ importers:
version: 5.28.6(react@18.2.0)
'@tanstack/react-query-next-experimental':
specifier: ^5.28.14
version: 5.28.14(@tanstack/react-query@5.28.6)(next@14.2.0-canary.61)(react@18.2.0)
version: 5.28.14(@tanstack/react-query@5.28.6)(next@14.2.0-canary.62)(react@18.2.0)
'@tanstack/react-table':
specifier: ^8.15.3
version: 8.15.3(react-dom@18.2.0)(react@18.2.0)
@@ -112,7 +112,7 @@ importers:
version: 3.6.0
edge-csrf:
specifier: ^1.0.9
version: 1.0.9(next@14.2.0-canary.61)
version: 1.0.9(next@14.2.0-canary.62)
i18next:
specifier: ^23.10.1
version: 23.10.1
@@ -123,11 +123,11 @@ importers:
specifier: ^0.363.0
version: 0.363.0(react@18.2.0)
next:
specifier: 14.2.0-canary.61
version: 14.2.0-canary.61(react-dom@18.2.0)(react@18.2.0)
specifier: 14.2.0-canary.62
version: 14.2.0-canary.62(react-dom@18.2.0)(react@18.2.0)
next-sitemap:
specifier: ^4.2.3
version: 4.2.3(next@14.2.0-canary.61)
version: 4.2.3(next@14.2.0-canary.62)
next-themes:
specifier: 0.3.0
version: 0.3.0(react-dom@18.2.0)(react@18.2.0)
@@ -2302,8 +2302,8 @@ packages:
resolution: {integrity: sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==}
dev: false
/@next/env@14.2.0-canary.61:
resolution: {integrity: sha512-Ueqse8kdwaoebGrpSo60M4/cjFaMJEE7BMsKZufYwZDTlE0qXw7N4GsdVpUZzJG00sXf6CoTnCU1lCTPrUMC4g==}
/@next/env@14.2.0-canary.62:
resolution: {integrity: sha512-K5lmKK/TalagQELw3W0hKDXmNGGXY3Zxw3yH27y9DOT7evhiJvK2Ywq5uN4lEjYHkeW+QbyC/OjUIcK3RUSHbQ==}
dev: false
/@next/eslint-plugin-next@14.1.4:
@@ -2330,8 +2330,8 @@ packages:
dev: false
optional: true
/@next/swc-darwin-arm64@14.2.0-canary.61:
resolution: {integrity: sha512-mMlp2/hvtaBbCY5qYhuvAqX9Z/aFC+Rgme4FjFSxq2C3TC/mL3G4fVG/TVl7bqjikKCxSvJgiWXRwvhIaqGktw==}
/@next/swc-darwin-arm64@14.2.0-canary.62:
resolution: {integrity: sha512-Cm53nqD/u7I1wo4xeULBZ6YjThK7Mk+ThaoYJu69varPPHsahpzbf/rM3ZkW3UB5Cgl5I7N3mNIb+Z9U/JZemw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
@@ -2357,8 +2357,8 @@ packages:
dev: false
optional: true
/@next/swc-darwin-x64@14.2.0-canary.61:
resolution: {integrity: sha512-4bEjO0WK6keRi972eAY1AfvTXOQRHnM59klNqnUh5zfalbi7VkEdluhYAZOop2NycCHjF+m8p+ytYtrF1uCO1Q==}
/@next/swc-darwin-x64@14.2.0-canary.62:
resolution: {integrity: sha512-2cSC0EOiJHe9kQad5TR/tXJuIc85JLBUeRl4x20jdsa0oIuOdsvksPA9TAROWLebuIkysPAsfmrbCwlKZk5yDw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
@@ -2384,8 +2384,8 @@ packages:
dev: false
optional: true
/@next/swc-linux-arm64-gnu@14.2.0-canary.61:
resolution: {integrity: sha512-wIXc3EdxrETlL2XwlGlLQkMU9godhNSMAXxiJotd/mhN3K4iKajPahAStvFxY2Zwc/on2IBa0NpUGpzDONNt9A==}
/@next/swc-linux-arm64-gnu@14.2.0-canary.62:
resolution: {integrity: sha512-Iow3tpn05nK2hW1nBzk1rCEVgbwakvlPViFZMxYg4FxEEoIcxcsxTMnG7sjJGmgrzCW7vfCVc+AM3Gy6bZXxqg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
@@ -2411,8 +2411,8 @@ packages:
dev: false
optional: true
/@next/swc-linux-arm64-musl@14.2.0-canary.61:
resolution: {integrity: sha512-95aMF55sq2N6+5iLEqxCfz7ccYMBURQ8D0KYmcq+rMJTE5z/qvxEToSJWAbV4jGyd2Eq/vXjPdR1rFytv2FnOw==}
/@next/swc-linux-arm64-musl@14.2.0-canary.62:
resolution: {integrity: sha512-dTugxbWEi95dNFn59C7RYuNTJ2jOVbDgD9lJmFtAJO46XPtN8IySJSe/5zB9YECA1apK2xWf5XaSbpuh5vWT8Q==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
@@ -2438,8 +2438,8 @@ packages:
dev: false
optional: true
/@next/swc-linux-x64-gnu@14.2.0-canary.61:
resolution: {integrity: sha512-LWG5OC9hNSCYtDb+7MQcIjE1PO70Sho+ZJkqJnmLxJ08Atp/nJQBo9nAMjORdcO5Nz+hPNVY5vmrY+5Fl5kadw==}
/@next/swc-linux-x64-gnu@14.2.0-canary.62:
resolution: {integrity: sha512-yGKHq4PfPgQO+IVWVOxbdv5pdIib/jwr62NsPKbY+t16gDB6RNTPhOcuhkQTvDBYlh+VF3vmhenlM5Ci0W5QQQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
@@ -2465,8 +2465,8 @@ packages:
dev: false
optional: true
/@next/swc-linux-x64-musl@14.2.0-canary.61:
resolution: {integrity: sha512-EjOXbSmDTPVi8xkOix4/WYJM6OUert+lfBtdUJBRba+oGiRw/mheih8FliFZKFOmQbPYj67A9z/QCus2ZDnfSw==}
/@next/swc-linux-x64-musl@14.2.0-canary.62:
resolution: {integrity: sha512-Nw/l0Z4jzAL5VP1Z2mjNAMF7n+9aOTZgFJCtJ6C0aBf5rvZY3BrVDXblK57v6q1bWqU6FRGYmDava3h4B2i0hQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
@@ -2492,8 +2492,8 @@ packages:
dev: false
optional: true
/@next/swc-win32-arm64-msvc@14.2.0-canary.61:
resolution: {integrity: sha512-TK8oV4ozzUGWAwvZp/0SBsNgAUhrUFLWCMSXsFHz+YMRjHg7nT0KdK8BYh2Ln4Qt0jjDTUHbI1jaQKxmSMEMmA==}
/@next/swc-win32-arm64-msvc@14.2.0-canary.62:
resolution: {integrity: sha512-FgS+pzblgiGe1iA2zvIxH5FTz/NVkXknhVgE3g0A5pIQeIfHn5hbki501fCBKvYujhsP094pSGePI9Hi35KFYA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
@@ -2519,8 +2519,8 @@ packages:
dev: false
optional: true
/@next/swc-win32-ia32-msvc@14.2.0-canary.61:
resolution: {integrity: sha512-i0iWCehuLKDOfVbQ6MEKG9v0lfcJU0DPWkYINFSi6p3fkFobI/+7DVT3KvYH5VVng/+opx+pA6cesV5eyQnBtw==}
/@next/swc-win32-ia32-msvc@14.2.0-canary.62:
resolution: {integrity: sha512-1cns8tyYICpspnhVC6FoRmd2Nal7uSXLPW4l1jX0+ofpifcmRy6Lra127/kdl8l+00W6ERQ/QAhx8uiYrw3VGQ==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
@@ -2546,8 +2546,8 @@ packages:
dev: false
optional: true
/@next/swc-win32-x64-msvc@14.2.0-canary.61:
resolution: {integrity: sha512-WB0UjpWcu+oXQOMFDTHDZWKcL2jiXeQfN+1RRkb0x7ZjVxvA/O66vOJE08fLSu7rdymLiGXxX+AKlGFq1Tpisg==}
/@next/swc-win32-x64-msvc@14.2.0-canary.62:
resolution: {integrity: sha512-67gfnbOVzVrg4lsRhE9qHQegpPl/ljHMNFw4JyIOlLmCG7daHe6wgKmXGAyYBSZ6F6P9HiQWHcqU/qTHtw2lFw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
@@ -5038,7 +5038,7 @@ packages:
/@tanstack/query-core@5.28.6:
resolution: {integrity: sha512-hnhotV+DnQtvtR3jPvbQMPNMW4KEK0J4k7c609zJ8muiNknm+yoDyMHmxTWM5ZnlZpsz0zOxYFr+mzRJNHWJsA==}
/@tanstack/react-query-next-experimental@5.28.14(@tanstack/react-query@5.28.6)(next@14.2.0-canary.61)(react@18.2.0):
/@tanstack/react-query-next-experimental@5.28.14(@tanstack/react-query@5.28.6)(next@14.2.0-canary.62)(react@18.2.0):
resolution: {integrity: sha512-gGHx3uJkZNYYpFNFk8eEo96ssiFE2OmYA49wszHxHrtO5nL7kzRcnJF8SALGpqSEjo5D3fLMH24MrhbBsO0sig==}
peerDependencies:
'@tanstack/react-query': ^5.28.14
@@ -5046,7 +5046,7 @@ packages:
react: ^18.0.0
dependencies:
'@tanstack/react-query': 5.28.6(react@18.2.0)
next: 14.2.0-canary.61(react-dom@18.2.0)(react@18.2.0)
next: 14.2.0-canary.62(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
dev: false
@@ -6858,12 +6858,12 @@ packages:
/eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
/edge-csrf@1.0.9(next@14.2.0-canary.61):
/edge-csrf@1.0.9(next@14.2.0-canary.62):
resolution: {integrity: sha512-3F89YTh42UDdISr3s9AEcgJDLi4ysgjGfnybzF0LuZGaG2W31h1ZwgWwEQBLMj04lAklcP4XHZYi7vk9o8zcbg==}
peerDependencies:
next: ^13.0.0 || ^14.0.0
dependencies:
next: 14.2.0-canary.61(react-dom@18.2.0)(react@18.2.0)
next: 14.2.0-canary.62(react-dom@18.2.0)(react@18.2.0)
dev: false
/editorconfig@1.0.4:
@@ -9648,7 +9648,7 @@ packages:
- supports-color
dev: false
/next-sitemap@4.2.3(next@14.2.0-canary.61):
/next-sitemap@4.2.3(next@14.2.0-canary.62):
resolution: {integrity: sha512-vjdCxeDuWDzldhCnyFCQipw5bfpl4HmZA7uoo3GAaYGjGgfL4Cxb1CiztPuWGmS+auYs7/8OekRS8C2cjdAsjQ==}
engines: {node: '>=14.18'}
hasBin: true
@@ -9659,7 +9659,7 @@ packages:
'@next/env': 13.5.6
fast-glob: 3.3.2
minimist: 1.2.8
next: 14.2.0-canary.61(react-dom@18.2.0)(react@18.2.0)
next: 14.2.0-canary.62(react-dom@18.2.0)(react@18.2.0)
dev: false
/next-themes@0.3.0(react-dom@18.2.0)(react@18.2.0):
@@ -9751,8 +9751,8 @@ packages:
- babel-plugin-macros
dev: false
/next@14.2.0-canary.61(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-UbdoNkGX04TO0Q0N3k7fmiNCliE1yihVBHd/nwg2zMnpjB6dGU3r1UNgCBpfUUlrs1t19FAWazfVQANOOfBT4w==}
/next@14.2.0-canary.62(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-SsS+fpJ/anrtLgeCC76V9WOlreZanUYsuKsRMx+FDwOJ3ZnbZkohu3+RRLIQM1vtWcp707iV11+OlF/qgOldCA==}
engines: {node: '>=18.17.0'}
hasBin: true
peerDependencies:
@@ -9769,7 +9769,7 @@ packages:
sass:
optional: true
dependencies:
'@next/env': 14.2.0-canary.61
'@next/env': 14.2.0-canary.62
'@swc/helpers': 0.5.5
busboy: 1.6.0
caniuse-lite: 1.0.30001600
@@ -9779,15 +9779,15 @@ packages:
react-dom: 18.2.0(react@18.2.0)
styled-jsx: 5.1.1(react@18.2.0)
optionalDependencies:
'@next/swc-darwin-arm64': 14.2.0-canary.61
'@next/swc-darwin-x64': 14.2.0-canary.61
'@next/swc-linux-arm64-gnu': 14.2.0-canary.61
'@next/swc-linux-arm64-musl': 14.2.0-canary.61
'@next/swc-linux-x64-gnu': 14.2.0-canary.61
'@next/swc-linux-x64-musl': 14.2.0-canary.61
'@next/swc-win32-arm64-msvc': 14.2.0-canary.61
'@next/swc-win32-ia32-msvc': 14.2.0-canary.61
'@next/swc-win32-x64-msvc': 14.2.0-canary.61
'@next/swc-darwin-arm64': 14.2.0-canary.62
'@next/swc-darwin-x64': 14.2.0-canary.62
'@next/swc-linux-arm64-gnu': 14.2.0-canary.62
'@next/swc-linux-arm64-musl': 14.2.0-canary.62
'@next/swc-linux-x64-gnu': 14.2.0-canary.62
'@next/swc-linux-x64-musl': 14.2.0-canary.62
'@next/swc-win32-arm64-msvc': 14.2.0-canary.62
'@next/swc-win32-ia32-msvc': 14.2.0-canary.62
'@next/swc-win32-x64-msvc': 14.2.0-canary.62
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros