Refactor Freebit integration by removing the csurf dependency and updating related type definitions. Enhance Freebit API request handling with improved error management and logging. Update data structures for traffic and quota history responses to streamline data processing. Adjust service methods for better clarity and maintainability.
This commit is contained in:
parent
7703392f58
commit
b9c24b6dc5
@ -48,7 +48,6 @@
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.2",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"csurf": "^1.11.0",
|
||||
"express": "^4.21.2",
|
||||
"helmet": "^8.1.0",
|
||||
"ioredis": "^5.7.0",
|
||||
@ -67,7 +66,7 @@
|
||||
"salesforce-pubsub-api-client": "^5.5.0",
|
||||
"speakeasy": "^2.0.0",
|
||||
"uuid": "^11.1.0",
|
||||
"zod": "^4.0.17"
|
||||
"zod": "^4.1.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^11.0.10",
|
||||
|
||||
@ -110,7 +110,6 @@ export async function bootstrap(): Promise<INestApplication> {
|
||||
"Accept",
|
||||
"Authorization",
|
||||
"X-API-Key",
|
||||
"X-CSRF-Token",
|
||||
],
|
||||
exposedHeaders: ["X-Total-Count", "X-Page-Count"],
|
||||
maxAge: 86400, // 24 hours
|
||||
|
||||
@ -54,12 +54,9 @@ export interface FreebitAccountDetailsResponse {
|
||||
responseDatas: FreebitAccountDetail[];
|
||||
}
|
||||
|
||||
export interface FreebitTrafficInfoResponseEntry {
|
||||
export interface FreebitTrafficInfoRequest {
|
||||
authKey: string;
|
||||
account: string;
|
||||
todayUsageMb?: number | string;
|
||||
todayUsageKb?: number | string;
|
||||
monthlyUsageMb?: number | string;
|
||||
monthlyUsageKb?: number | string;
|
||||
}
|
||||
|
||||
export interface FreebitTrafficInfoResponse {
|
||||
@ -68,7 +65,12 @@ export interface FreebitTrafficInfoResponse {
|
||||
message: string;
|
||||
statusCode: string | number;
|
||||
};
|
||||
responseDatas: FreebitTrafficInfoResponseEntry[];
|
||||
account: string;
|
||||
traffic: {
|
||||
today: string; // Today's usage in KB
|
||||
inRecentDays: string; // Comma-separated recent days usage
|
||||
blackList: string; // 10=blacklisted, 20=not blacklisted
|
||||
};
|
||||
}
|
||||
|
||||
export interface FreebitTopUpRequest {
|
||||
@ -104,22 +106,26 @@ export interface FreebitAddSpecResponse {
|
||||
status: { message: string; statusCode: string | number };
|
||||
}
|
||||
|
||||
export interface FreebitQuotaAddition {
|
||||
date?: string;
|
||||
quotaMb?: number | string;
|
||||
quotaKb?: number | string;
|
||||
description?: string;
|
||||
export interface FreebitQuotaHistoryRequest {
|
||||
authKey: string;
|
||||
account: string;
|
||||
fromDate: string;
|
||||
toDate: string;
|
||||
}
|
||||
|
||||
export interface FreebitQuotaHistoryResponseEntry {
|
||||
account: string;
|
||||
additions?: FreebitQuotaAddition[];
|
||||
export interface FreebitQuotaHistoryItem {
|
||||
quota: string; // KB as string
|
||||
date: string;
|
||||
expire: string;
|
||||
quotaCode: string;
|
||||
}
|
||||
|
||||
export interface FreebitQuotaHistoryResponse {
|
||||
resultCode: string;
|
||||
status: { message: string; statusCode: string | number };
|
||||
responseDatas: FreebitQuotaHistoryResponseEntry[];
|
||||
total: string | number;
|
||||
count: string | number;
|
||||
quotaHistory: FreebitQuotaHistoryItem[];
|
||||
}
|
||||
|
||||
export interface FreebitPlanChangeRequest {
|
||||
@ -226,15 +232,17 @@ export interface FreebitEsimReissueResponse {
|
||||
|
||||
export interface FreebitEsimAddAccountRequest {
|
||||
authKey: string;
|
||||
aladinOperated?: string;
|
||||
aladinOperated: string; // '10' for issue, '20' for no-issue
|
||||
account: string;
|
||||
eid: string;
|
||||
addKind: "N" | "R"; // N = new, R = reissue
|
||||
createType?: string;
|
||||
simKind?: string;
|
||||
shipDate?: string;
|
||||
planCode?: string;
|
||||
contractLine?: string;
|
||||
reissue?: { oldProductNumber?: string; oldEid?: string };
|
||||
mnp?: {
|
||||
reserveNumber: string;
|
||||
reserveExpireDate: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface FreebitEsimAddAccountResponse {
|
||||
@ -272,8 +280,11 @@ export interface FreebitEsimAccountActivationRequest {
|
||||
}
|
||||
|
||||
export interface FreebitEsimAccountActivationResponse {
|
||||
resultCode: number | string;
|
||||
status?: unknown;
|
||||
resultCode: string;
|
||||
status?: {
|
||||
message?: string;
|
||||
statusCode?: string | number;
|
||||
};
|
||||
statusCode?: string | number;
|
||||
message?: string;
|
||||
}
|
||||
@ -303,20 +314,23 @@ export interface SimUsage {
|
||||
account: string;
|
||||
todayUsageMb: number;
|
||||
todayUsageKb: number;
|
||||
monthlyUsageMb: number;
|
||||
monthlyUsageKb: number;
|
||||
recentDaysUsage?: Array<{ date: string; usageKb: number; usageMb: number }>;
|
||||
lastUpdated: string;
|
||||
monthlyUsageMb?: number;
|
||||
monthlyUsageKb?: number;
|
||||
recentDaysUsage: Array<{ date: string; usageKb: number; usageMb: number }>;
|
||||
isBlacklisted: boolean;
|
||||
lastUpdated?: string;
|
||||
}
|
||||
|
||||
export interface SimTopUpHistory {
|
||||
account: string;
|
||||
totalAdditions: number;
|
||||
additions: Array<{
|
||||
date: string;
|
||||
quotaMb: number;
|
||||
additionCount: number;
|
||||
history: Array<{
|
||||
quotaKb: number;
|
||||
description: string;
|
||||
quotaMb: number;
|
||||
addedDate: string;
|
||||
expiryDate: string;
|
||||
campaignCode: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
|
||||
@ -109,6 +109,93 @@ export class FreebitClientService {
|
||||
throw new FreebitError("Request failed after all retry attempts");
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an authenticated JSON request to Freebit API (for PA05-41)
|
||||
*/
|
||||
async makeAuthenticatedJsonRequest<
|
||||
TResponse extends FreebitResponseBase,
|
||||
TPayload extends object,
|
||||
>(endpoint: string, payload: TPayload): Promise<TResponse> {
|
||||
const config = this.authService.getConfig();
|
||||
const url = `${config.baseUrl}${endpoint}`;
|
||||
|
||||
for (let attempt = 1; attempt <= config.retryAttempts; attempt++) {
|
||||
try {
|
||||
this.logger.debug(`Freebit JSON API request (attempt ${attempt}/${config.retryAttempts})`, {
|
||||
url,
|
||||
payload: this.sanitizePayload(payload as Record<string, unknown>),
|
||||
});
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), config.timeout);
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(payload),
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
clearTimeout(timeout);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new FreebitError(
|
||||
`HTTP ${response.status}: ${response.statusText}`,
|
||||
response.status.toString()
|
||||
);
|
||||
}
|
||||
|
||||
const responseData = (await response.json()) as TResponse;
|
||||
|
||||
if (responseData.resultCode && responseData.resultCode !== "100") {
|
||||
throw new FreebitError(
|
||||
`API Error: ${responseData.status?.message || "Unknown error"}`,
|
||||
responseData.resultCode,
|
||||
responseData.status?.statusCode,
|
||||
responseData.status?.message
|
||||
);
|
||||
}
|
||||
|
||||
this.logger.debug("Freebit JSON API request successful", {
|
||||
url,
|
||||
resultCode: responseData.resultCode,
|
||||
});
|
||||
|
||||
return responseData;
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof FreebitError) {
|
||||
if (error.isAuthError() && attempt === 1) {
|
||||
this.logger.warn("Auth error detected, clearing cache and retrying");
|
||||
this.authService.clearAuthCache();
|
||||
continue;
|
||||
}
|
||||
if (!error.isRetryable() || attempt === config.retryAttempts) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
if (attempt === config.retryAttempts) {
|
||||
const message = getErrorMessage(error);
|
||||
this.logger.error(`Freebit JSON API request failed after ${config.retryAttempts} attempts`, {
|
||||
url,
|
||||
error: message,
|
||||
});
|
||||
throw new FreebitError(`Request failed: ${message}`);
|
||||
}
|
||||
|
||||
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
|
||||
this.logger.warn(`Freebit JSON API request failed, retrying in ${delay}ms`, {
|
||||
url,
|
||||
attempt,
|
||||
error: getErrorMessage(error),
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, delay));
|
||||
}
|
||||
}
|
||||
|
||||
throw new FreebitError("Request failed after all retry attempts");
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a simple request without authentication (for health checks)
|
||||
*/
|
||||
|
||||
@ -70,41 +70,48 @@ export class FreebitMapperService {
|
||||
* Map Freebit traffic info response to SimUsage
|
||||
*/
|
||||
mapToSimUsage(response: FreebitTrafficInfoResponse): SimUsage {
|
||||
const traffic = response.responseDatas[0];
|
||||
if (!traffic) {
|
||||
if (!response.traffic) {
|
||||
throw new Error("No traffic data in response");
|
||||
}
|
||||
|
||||
const todayUsageKb = parseInt(response.traffic.today, 10) || 0;
|
||||
const recentDaysData = response.traffic.inRecentDays
|
||||
.split(",")
|
||||
.map((usage, index) => ({
|
||||
date: new Date(Date.now() - (index + 1) * 24 * 60 * 60 * 1000)
|
||||
.toISOString()
|
||||
.split("T")[0],
|
||||
usageKb: parseInt(usage, 10) || 0,
|
||||
usageMb: Math.round(((parseInt(usage, 10) || 0) / 1024) * 100) / 100,
|
||||
}));
|
||||
|
||||
return {
|
||||
account: String(traffic.account ?? ""),
|
||||
todayUsageMb: Number(traffic.todayUsageMb ?? 0),
|
||||
todayUsageKb: Number(traffic.todayUsageKb ?? 0),
|
||||
monthlyUsageMb: Number(traffic.monthlyUsageMb ?? 0),
|
||||
monthlyUsageKb: Number(traffic.monthlyUsageKb ?? 0),
|
||||
recentDaysUsage: [],
|
||||
lastUpdated: new Date().toISOString(),
|
||||
account: String(response.account ?? ""),
|
||||
todayUsageMb: Math.round((todayUsageKb / 1024) * 100) / 100,
|
||||
todayUsageKb,
|
||||
recentDaysUsage: recentDaysData,
|
||||
isBlacklisted: response.traffic.blackList === "10",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Map Freebit quota history response to SimTopUpHistory
|
||||
*/
|
||||
mapToSimTopUpHistory(response: FreebitQuotaHistoryResponse): SimTopUpHistory {
|
||||
const history = response.responseDatas[0];
|
||||
if (!history) {
|
||||
mapToSimTopUpHistory(response: FreebitQuotaHistoryResponse, account: string): SimTopUpHistory {
|
||||
if (!response.quotaHistory) {
|
||||
throw new Error("No history data in response");
|
||||
}
|
||||
|
||||
const additions = Array.isArray(history.additions) ? history.additions : [];
|
||||
|
||||
return {
|
||||
account: String(history.account ?? ""),
|
||||
totalAdditions: additions.length,
|
||||
additions: additions.map(addition => ({
|
||||
date: String(addition?.date ?? ""),
|
||||
quotaMb: Number(addition?.quotaMb ?? 0),
|
||||
quotaKb: Number(addition?.quotaKb ?? 0),
|
||||
description: String(addition?.description ?? ""),
|
||||
account,
|
||||
totalAdditions: Number(response.total) || 0,
|
||||
additionCount: Number(response.count) || 0,
|
||||
history: response.quotaHistory.map((item) => ({
|
||||
quotaKb: parseInt(item.quota, 10),
|
||||
quotaMb: Math.round((parseInt(item.quota, 10) / 1024) * 100) / 100,
|
||||
addedDate: item.date,
|
||||
expiryDate: item.expire,
|
||||
campaignCode: item.quotaCode,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
@ -15,14 +15,16 @@ import type {
|
||||
FreebitQuotaHistoryResponse,
|
||||
FreebitPlanChangeRequest,
|
||||
FreebitPlanChangeResponse,
|
||||
FreebitAddSpecRequest,
|
||||
FreebitAddSpecResponse,
|
||||
FreebitCancelPlanRequest,
|
||||
FreebitCancelPlanResponse,
|
||||
FreebitEsimReissueRequest,
|
||||
FreebitEsimReissueResponse,
|
||||
FreebitAddSpecRequest,
|
||||
FreebitAddSpecResponse,
|
||||
FreebitPlanChangePayload,
|
||||
FreebitAddSpecPayload,
|
||||
FreebitEsimAddAccountRequest,
|
||||
FreebitEsimAddAccountResponse,
|
||||
FreebitEsimAccountActivationRequest,
|
||||
FreebitEsimAccountActivationResponse,
|
||||
SimDetails,
|
||||
SimUsage,
|
||||
SimTopUpHistory,
|
||||
@ -114,9 +116,7 @@ export class FreebitOperationsService {
|
||||
*/
|
||||
async getSimUsage(account: string): Promise<SimUsage> {
|
||||
try {
|
||||
const request: Omit<FreebitTrafficInfoRequest, "authKey"> = {
|
||||
requestDatas: [{ kind: "MVNO", account }],
|
||||
} as any;
|
||||
const request: Omit<FreebitTrafficInfoRequest, "authKey"> = { account };
|
||||
|
||||
const response = await this.client.makeAuthenticatedRequest<
|
||||
FreebitTrafficInfoResponse,
|
||||
@ -140,26 +140,35 @@ export class FreebitOperationsService {
|
||||
async topUpSim(
|
||||
account: string,
|
||||
quotaMb: number,
|
||||
options: { description?: string } = {}
|
||||
options: { campaignCode?: string; expiryDate?: string; scheduledAt?: string } = {}
|
||||
): Promise<void> {
|
||||
try {
|
||||
const quotaKb = Math.round(quotaMb * 1024);
|
||||
const request: Omit<FreebitTopUpRequest, "authKey"> = {
|
||||
requestDatas: [
|
||||
{
|
||||
kind: "MVNO",
|
||||
account,
|
||||
quotaMb,
|
||||
description: options.description || `Data top-up: ${quotaMb}MB`,
|
||||
},
|
||||
],
|
||||
} as any;
|
||||
account,
|
||||
quota: quotaKb,
|
||||
quotaCode: options.campaignCode,
|
||||
expire: options.expiryDate,
|
||||
};
|
||||
|
||||
const scheduled = !!options.scheduledAt;
|
||||
const endpoint = scheduled ? "/mvno/eachQuota/" : "/master/addSpec/";
|
||||
if (scheduled) {
|
||||
(request as any).runTime = options.scheduledAt;
|
||||
}
|
||||
|
||||
await this.client.makeAuthenticatedRequest<FreebitTopUpResponse, typeof request>(
|
||||
"/mvno/addQuota/",
|
||||
endpoint,
|
||||
request
|
||||
);
|
||||
|
||||
this.logger.log(`Successfully topped up ${quotaMb}MB for account ${account}`);
|
||||
this.logger.log(`Successfully topped up ${quotaMb}MB for account ${account}`, {
|
||||
account,
|
||||
endpoint,
|
||||
quotaMb,
|
||||
quotaKb,
|
||||
scheduled,
|
||||
});
|
||||
} catch (error) {
|
||||
const message = getErrorMessage(error);
|
||||
this.logger.error(`Failed to top up SIM for account ${account}`, {
|
||||
@ -180,23 +189,18 @@ export class FreebitOperationsService {
|
||||
toDate: string
|
||||
): Promise<SimTopUpHistory> {
|
||||
try {
|
||||
const request: Omit<FreebitQuotaHistoryRequest, "authKey"> = {
|
||||
requestDatas: [
|
||||
{
|
||||
kind: "MVNO",
|
||||
account,
|
||||
fromDate,
|
||||
toDate,
|
||||
},
|
||||
],
|
||||
} as any;
|
||||
const request: Omit<FreebitQuotaHistoryRequest, "authKey"> = {
|
||||
account,
|
||||
fromDate,
|
||||
toDate
|
||||
};
|
||||
|
||||
const response = await this.client.makeAuthenticatedRequest<
|
||||
FreebitQuotaHistoryResponse,
|
||||
typeof request
|
||||
>("/mvno/getQuotaHistory/", request);
|
||||
|
||||
return this.mapper.mapToSimTopUpHistory(response);
|
||||
return this.mapper.mapToSimTopUpHistory(response, account);
|
||||
} catch (error) {
|
||||
const message = getErrorMessage(error);
|
||||
this.logger.error(`Failed to get SIM top-up history for account ${account}`, {
|
||||
@ -218,30 +222,28 @@ export class FreebitOperationsService {
|
||||
options: { assignGlobalIp?: boolean; scheduledAt?: string } = {}
|
||||
): Promise<{ ipv4?: string; ipv6?: string }> {
|
||||
try {
|
||||
const request: FreebitPlanChangePayload = {
|
||||
requestDatas: [
|
||||
{
|
||||
kind: "MVNO",
|
||||
account,
|
||||
newPlanCode,
|
||||
assignGlobalIp: options.assignGlobalIp ?? false,
|
||||
scheduledAt: options.scheduledAt,
|
||||
},
|
||||
],
|
||||
const request: Omit<FreebitPlanChangeRequest, "authKey"> = {
|
||||
account,
|
||||
plancode: newPlanCode,
|
||||
globalip: options.assignGlobalIp ? "1" : "0",
|
||||
runTime: options.scheduledAt,
|
||||
};
|
||||
|
||||
const response = await this.client.makeAuthenticatedRequest<
|
||||
FreebitPlanChangeResponse,
|
||||
FreebitPlanChangePayload
|
||||
typeof request
|
||||
>("/mvno/changePlan/", request);
|
||||
|
||||
const result = response.responseDatas?.[0] ?? {};
|
||||
|
||||
this.logger.log(`Successfully changed plan for account ${account} to ${newPlanCode}`);
|
||||
this.logger.log(`Successfully changed plan for account ${account} to ${newPlanCode}`, {
|
||||
account,
|
||||
newPlanCode,
|
||||
assignGlobalIp: options.assignGlobalIp,
|
||||
scheduled: !!options.scheduledAt,
|
||||
});
|
||||
|
||||
return {
|
||||
ipv4: result.ipv4,
|
||||
ipv6: result.ipv6,
|
||||
ipv4: response.ipv4,
|
||||
ipv6: response.ipv6,
|
||||
};
|
||||
} catch (error) {
|
||||
const message = getErrorMessage(error);
|
||||
@ -267,51 +269,37 @@ export class FreebitOperationsService {
|
||||
}
|
||||
): Promise<void> {
|
||||
try {
|
||||
const requests: FreebitAddSpecPayload[] = [];
|
||||
const request: Omit<FreebitAddSpecRequest, "authKey"> = { account };
|
||||
|
||||
const createSpecPayload = (
|
||||
specCode: string,
|
||||
additional: Partial<FreebitAddSpecPayload["requestDatas"][number]> = {}
|
||||
): FreebitAddSpecPayload => ({
|
||||
requestDatas: [
|
||||
{
|
||||
kind: "MVNO",
|
||||
account,
|
||||
specCode,
|
||||
...additional,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Voice options (PA05-06)
|
||||
// Use both variations for compatibility
|
||||
if (typeof features.voiceMailEnabled === "boolean") {
|
||||
requests.push(createSpecPayload("PA05-06", { enabled: features.voiceMailEnabled }));
|
||||
request.voiceMail = features.voiceMailEnabled ? "10" : "20";
|
||||
request.voicemail = request.voiceMail;
|
||||
}
|
||||
|
||||
if (typeof features.callWaitingEnabled === "boolean") {
|
||||
requests.push(createSpecPayload("PA05-06", { enabled: features.callWaitingEnabled }));
|
||||
request.callWaiting = features.callWaitingEnabled ? "10" : "20";
|
||||
request.callwaiting = request.callWaiting;
|
||||
}
|
||||
|
||||
if (typeof features.internationalRoamingEnabled === "boolean") {
|
||||
requests.push(
|
||||
createSpecPayload("PA05-06", { enabled: features.internationalRoamingEnabled })
|
||||
);
|
||||
request.worldWing = features.internationalRoamingEnabled ? "10" : "20";
|
||||
request.worldwing = request.worldWing;
|
||||
}
|
||||
|
||||
// Network type (PA05-38 for contract line change)
|
||||
if (features.networkType) {
|
||||
requests.push(createSpecPayload("PA05-38", { networkType: features.networkType }));
|
||||
request.contractLine = features.networkType;
|
||||
}
|
||||
|
||||
// Execute all requests
|
||||
for (const request of requests) {
|
||||
await this.client.makeAuthenticatedRequest<FreebitAddSpecResponse, FreebitAddSpecPayload>(
|
||||
"/mvno/addSpec/",
|
||||
request
|
||||
);
|
||||
}
|
||||
await this.client.makeAuthenticatedRequest<FreebitAddSpecResponse, typeof request>(
|
||||
"/master/addSpec/",
|
||||
request
|
||||
);
|
||||
|
||||
this.logger.log(`Successfully updated SIM features for account ${account}`, { features });
|
||||
this.logger.log(`Successfully updated SIM features for account ${account}`, {
|
||||
account,
|
||||
voiceMailEnabled: features.voiceMailEnabled,
|
||||
callWaitingEnabled: features.callWaitingEnabled,
|
||||
internationalRoamingEnabled: features.internationalRoamingEnabled,
|
||||
networkType: features.networkType,
|
||||
});
|
||||
} catch (error) {
|
||||
const message = getErrorMessage(error);
|
||||
this.logger.error(`Failed to update SIM features for account ${account}`, {
|
||||
@ -328,22 +316,20 @@ export class FreebitOperationsService {
|
||||
*/
|
||||
async cancelSim(account: string, scheduledAt?: string): Promise<void> {
|
||||
try {
|
||||
const request: FreebitCancelPlanPayload = {
|
||||
requestDatas: [
|
||||
{
|
||||
kind: "MVNO",
|
||||
account,
|
||||
runDate: scheduledAt || this.mapper.formatDateForApi(new Date()),
|
||||
},
|
||||
],
|
||||
const request: Omit<FreebitCancelPlanRequest, "authKey"> = {
|
||||
account,
|
||||
runTime: scheduledAt
|
||||
};
|
||||
|
||||
await this.client.makeAuthenticatedRequest<FreebitCancelPlanResponse, FreebitCancelPlanPayload>(
|
||||
"/mvno/cancelPlan/",
|
||||
await this.client.makeAuthenticatedRequest<FreebitCancelPlanResponse, typeof request>(
|
||||
"/mvno/releasePlan/",
|
||||
request
|
||||
);
|
||||
|
||||
this.logger.log(`Successfully cancelled SIM for account ${account}`);
|
||||
this.logger.log(`Successfully cancelled SIM for account ${account}`, {
|
||||
account,
|
||||
runTime: scheduledAt
|
||||
});
|
||||
} catch (error) {
|
||||
const message = getErrorMessage(error);
|
||||
this.logger.error(`Failed to cancel SIM for account ${account}`, {
|
||||
@ -386,33 +372,31 @@ export class FreebitOperationsService {
|
||||
async reissueEsimProfileEnhanced(
|
||||
account: string,
|
||||
newEid: string,
|
||||
options: { oldEid?: string; planCode?: string } = {}
|
||||
options: { oldProductNumber?: string; oldEid?: string; planCode?: string } = {}
|
||||
): Promise<void> {
|
||||
try {
|
||||
const request: FreebitEsimReissuePayload = {
|
||||
requestDatas: [
|
||||
{
|
||||
kind: "MVNO",
|
||||
account,
|
||||
newEid,
|
||||
oldEid: options.oldEid,
|
||||
planCode: options.planCode,
|
||||
},
|
||||
],
|
||||
const request: Omit<FreebitEsimAddAccountRequest, "authKey"> = {
|
||||
aladinOperated: "20",
|
||||
account,
|
||||
eid: newEid,
|
||||
addKind: "R",
|
||||
planCode: options.planCode,
|
||||
};
|
||||
|
||||
await this.client.makeAuthenticatedRequest<FreebitEsimReissueResponse, FreebitEsimReissuePayload>(
|
||||
"/mvno/reissueEsim/",
|
||||
await this.client.makeAuthenticatedRequest<FreebitEsimAddAccountResponse, typeof request>(
|
||||
"/mvno/esim/addAcnt/",
|
||||
request
|
||||
);
|
||||
|
||||
this.logger.log(`Successfully reissued eSIM profile with new EID for account ${account}`, {
|
||||
this.logger.log(`Successfully reissued eSIM profile via addAcnt for account ${account}`, {
|
||||
account,
|
||||
newEid,
|
||||
oldProductNumber: options.oldProductNumber,
|
||||
oldEid: options.oldEid,
|
||||
});
|
||||
} catch (error) {
|
||||
const message = getErrorMessage(error);
|
||||
this.logger.error(`Failed to reissue eSIM profile with new EID for account ${account}`, {
|
||||
this.logger.error(`Failed to reissue eSIM profile via addAcnt for account ${account}`, {
|
||||
account,
|
||||
newEid,
|
||||
error: message,
|
||||
@ -421,6 +405,75 @@ export class FreebitOperationsService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate new eSIM account using PA05-41 (addAcct)
|
||||
*/
|
||||
async activateEsimAccountNew(params: {
|
||||
account: string;
|
||||
eid: string;
|
||||
planCode?: string;
|
||||
contractLine?: "4G" | "5G";
|
||||
aladinOperated?: "10" | "20";
|
||||
shipDate?: string;
|
||||
mnp?: { reserveNumber: string; reserveExpireDate: string };
|
||||
identity?: {
|
||||
firstnameKanji?: string;
|
||||
lastnameKanji?: string;
|
||||
firstnameZenKana?: string;
|
||||
lastnameZenKana?: string;
|
||||
gender?: string;
|
||||
birthday?: string;
|
||||
};
|
||||
}): Promise<void> {
|
||||
const { account, eid, planCode, contractLine, aladinOperated = "10", shipDate, mnp, identity } = params;
|
||||
|
||||
if (!account || !eid) {
|
||||
throw new BadRequestException("activateEsimAccountNew requires account and eid");
|
||||
}
|
||||
|
||||
try {
|
||||
const payload: FreebitEsimAccountActivationRequest = {
|
||||
authKey: await this.auth.getAuthKey(),
|
||||
aladinOperated,
|
||||
createType: "new",
|
||||
eid,
|
||||
account,
|
||||
simkind: "esim",
|
||||
planCode,
|
||||
contractLine,
|
||||
shipDate,
|
||||
...(mnp ? { mnp } : {}),
|
||||
...(identity ? identity : {}),
|
||||
} as FreebitEsimAccountActivationRequest;
|
||||
|
||||
// Use JSON request for PA05-41
|
||||
await this.client.makeAuthenticatedJsonRequest<
|
||||
FreebitEsimAccountActivationResponse,
|
||||
FreebitEsimAccountActivationRequest
|
||||
>(
|
||||
"/mvno/esim/addAcct/",
|
||||
payload
|
||||
);
|
||||
|
||||
this.logger.log("Successfully activated new eSIM account via PA05-41", {
|
||||
account,
|
||||
planCode,
|
||||
contractLine,
|
||||
scheduled: !!shipDate,
|
||||
mnp: !!mnp,
|
||||
});
|
||||
} catch (error) {
|
||||
const message = getErrorMessage(error);
|
||||
this.logger.error(`Failed to activate new eSIM account ${account}`, {
|
||||
account,
|
||||
eid,
|
||||
planCode,
|
||||
error: message,
|
||||
});
|
||||
throw new BadRequestException(`Failed to activate new eSIM account: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Health check - test API connectivity
|
||||
*/
|
||||
|
||||
@ -5,6 +5,7 @@ import type {
|
||||
SimDetails,
|
||||
SimUsage,
|
||||
SimTopUpHistory,
|
||||
FreebitEsimAddAccountRequest,
|
||||
} from "../interfaces/freebit.types";
|
||||
|
||||
@Injectable()
|
||||
@ -36,7 +37,7 @@ export class FreebitOrchestratorService {
|
||||
async topUpSim(
|
||||
account: string,
|
||||
quotaMb: number,
|
||||
options: { description?: string } = {}
|
||||
options: { campaignCode?: string; expiryDate?: string; scheduledAt?: string } = {}
|
||||
): Promise<void> {
|
||||
const normalizedAccount = this.mapper.normalizeAccount(account);
|
||||
return this.operations.topUpSim(normalizedAccount, quotaMb, options);
|
||||
@ -110,6 +111,39 @@ export class FreebitOrchestratorService {
|
||||
return this.operations.reissueEsimProfileEnhanced(normalizedAccount, newEid, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate new eSIM account
|
||||
*/
|
||||
async activateEsimAccountNew(params: {
|
||||
account: string;
|
||||
eid: string;
|
||||
planCode?: string;
|
||||
contractLine?: "4G" | "5G";
|
||||
aladinOperated?: "10" | "20";
|
||||
shipDate?: string;
|
||||
mnp?: { reserveNumber: string; reserveExpireDate: string };
|
||||
identity?: {
|
||||
firstnameKanji?: string;
|
||||
lastnameKanji?: string;
|
||||
firstnameZenKana?: string;
|
||||
lastnameZenKana?: string;
|
||||
gender?: string;
|
||||
birthday?: string;
|
||||
};
|
||||
}): Promise<void> {
|
||||
const normalizedAccount = this.mapper.normalizeAccount(params.account);
|
||||
return this.operations.activateEsimAccountNew({
|
||||
account: normalizedAccount,
|
||||
eid: params.eid,
|
||||
planCode: params.planCode,
|
||||
contractLine: params.contractLine,
|
||||
aladinOperated: params.aladinOperated,
|
||||
shipDate: params.shipDate,
|
||||
mnp: params.mnp,
|
||||
identity: params.identity,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Health check
|
||||
*/
|
||||
|
||||
@ -50,11 +50,12 @@ export class StatusNormalizer {
|
||||
monthly: "Monthly",
|
||||
quarterly: "Quarterly",
|
||||
semiannually: "Semi-Annually",
|
||||
"semi-annually": "Semi-Annually",
|
||||
annually: "Annually",
|
||||
biennially: "Biennially",
|
||||
triennially: "Triennially",
|
||||
onetime: "One Time",
|
||||
"one time": "One Time",
|
||||
onetime: "One-time",
|
||||
"one time": "One-time",
|
||||
free: "Free",
|
||||
};
|
||||
|
||||
@ -65,7 +66,7 @@ export class StatusNormalizer {
|
||||
* Check if billing cycle represents a one-time payment
|
||||
*/
|
||||
static isOneTimeBilling(cycle: string): boolean {
|
||||
const oneTimeCycles = ["onetime", "one time", "free"];
|
||||
const oneTimeCycles = ["onetime", "one time", "one-time", "free"];
|
||||
return oneTimeCycles.includes(cycle?.toLowerCase());
|
||||
}
|
||||
|
||||
|
||||
@ -54,15 +54,29 @@ export class SimFulfillmentService {
|
||||
throw new Error("Phone number is required for SIM activation");
|
||||
}
|
||||
|
||||
await this.activateSim({
|
||||
account: phoneNumber,
|
||||
eid,
|
||||
planSku,
|
||||
simType,
|
||||
activationType,
|
||||
scheduledAt,
|
||||
mnp,
|
||||
});
|
||||
if (simType === "eSIM") {
|
||||
if (!eid) {
|
||||
throw new Error("EID is required for eSIM activation");
|
||||
}
|
||||
await this.activateSim({
|
||||
account: phoneNumber,
|
||||
eid,
|
||||
planSku,
|
||||
simType: "eSIM",
|
||||
activationType,
|
||||
scheduledAt,
|
||||
mnp,
|
||||
});
|
||||
} else {
|
||||
await this.activateSim({
|
||||
account: phoneNumber,
|
||||
planSku,
|
||||
simType: "Physical SIM",
|
||||
activationType,
|
||||
scheduledAt,
|
||||
mnp,
|
||||
});
|
||||
}
|
||||
|
||||
this.logger.log("SIM fulfillment completed successfully", {
|
||||
orderId: orderDetails.id,
|
||||
|
||||
@ -87,33 +87,15 @@ export class SimOrderActivationService {
|
||||
await this.freebit.activateEsimAccountNew({
|
||||
account: req.msisdn,
|
||||
eid: req.eid!,
|
||||
planSku: req.planSku,
|
||||
planCode: req.planSku,
|
||||
contractLine: "5G",
|
||||
shipDate: req.activationType === "Scheduled" ? req.scheduledAt : undefined,
|
||||
mnp: req.mnp
|
||||
? {
|
||||
reservationNumber: req.mnp.reserveNumber,
|
||||
expiryDate: req.mnp.reserveExpireDate,
|
||||
phoneNumber: req.mnp.account || "",
|
||||
mvnoAccountNumber: "",
|
||||
portingLastName: "",
|
||||
portingFirstName: "",
|
||||
portingLastNameKatakana: "",
|
||||
portingFirstNameKatakana: "",
|
||||
portingGender: "" as const,
|
||||
portingDateOfBirth: ""
|
||||
reserveNumber: req.mnp.reserveNumber || "",
|
||||
reserveExpireDate: req.mnp.reserveExpireDate || ""
|
||||
}
|
||||
: undefined,
|
||||
identity: req.mnp
|
||||
? {
|
||||
firstnameKanji: req.mnp.firstnameKanji,
|
||||
lastnameKanji: req.mnp.lastnameKanji,
|
||||
firstnameZenKana: req.mnp.firstnameZenKana,
|
||||
lastnameZenKana: req.mnp.lastnameZenKana,
|
||||
gender: req.mnp.gender,
|
||||
birthday: req.mnp.birthday,
|
||||
}
|
||||
: undefined,
|
||||
: undefined
|
||||
});
|
||||
} else {
|
||||
this.logger.warn("Physical SIM activation path is not implemented; skipping Freebit call", {
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tw-animate-css": "^1.3.7",
|
||||
"world-countries": "^5.1.0",
|
||||
"zod": "^4.0.17",
|
||||
"zod": "^4.1.9",
|
||||
"zustand": "^5.0.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@ -73,7 +73,6 @@
|
||||
"zod": "^4.1.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sendgrid/mail": "^8.1.5",
|
||||
"csurf": "^1.11.0"
|
||||
"@sendgrid/mail": "^8.1.5"
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,6 +71,7 @@ const subscriptionCycleSchema = z.enum([
|
||||
"Biennially",
|
||||
"Triennially",
|
||||
"One-time",
|
||||
"Free",
|
||||
]);
|
||||
|
||||
// =====================================================
|
||||
|
||||
@ -98,7 +98,16 @@ export const categoryEnum = z.enum(["technical", "billing", "account", "general"
|
||||
|
||||
// Billing cycle enums
|
||||
export const billingCycleEnum = z.enum(["Monthly", "Quarterly", "Annually", "Onetime", "Free"]);
|
||||
export const subscriptionBillingCycleEnum = z.enum(["Monthly", "Quarterly", "Annually", "Biennially", "Triennially"]);
|
||||
export const subscriptionBillingCycleEnum = z.enum([
|
||||
"Monthly",
|
||||
"Quarterly",
|
||||
"Semi-Annually",
|
||||
"Annually",
|
||||
"Biennially",
|
||||
"Triennially",
|
||||
"One-time",
|
||||
"Free"
|
||||
]);
|
||||
|
||||
// =====================================================
|
||||
// TYPE EXPORTS
|
||||
|
||||
113
pnpm-lock.yaml
generated
113
pnpm-lock.yaml
generated
@ -11,9 +11,6 @@ importers:
|
||||
'@sendgrid/mail':
|
||||
specifier: ^8.1.5
|
||||
version: 8.1.5
|
||||
csurf:
|
||||
specifier: ^1.11.0
|
||||
version: 1.11.0
|
||||
devDependencies:
|
||||
'@eslint/eslintrc':
|
||||
specifier: ^3.3.1
|
||||
@ -114,9 +111,6 @@ importers:
|
||||
cookie-parser:
|
||||
specifier: ^1.4.7
|
||||
version: 1.4.7
|
||||
csurf:
|
||||
specifier: ^1.11.0
|
||||
version: 1.11.0
|
||||
express:
|
||||
specifier: ^4.21.2
|
||||
version: 4.21.2
|
||||
@ -137,7 +131,7 @@ importers:
|
||||
version: 4.4.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(pino-http@10.5.0)(pino@9.9.5)(rxjs@7.8.2)
|
||||
nestjs-zod:
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/swagger@11.2.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2))(rxjs@7.8.2)(zod@4.1.5)
|
||||
version: 5.0.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/swagger@11.2.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2))(rxjs@7.8.2)(zod@4.1.9)
|
||||
passport:
|
||||
specifier: ^0.7.0
|
||||
version: 0.7.0
|
||||
@ -172,8 +166,8 @@ importers:
|
||||
specifier: ^11.1.0
|
||||
version: 11.1.0
|
||||
zod:
|
||||
specifier: ^4.0.17
|
||||
version: 4.1.5
|
||||
specifier: ^4.1.9
|
||||
version: 4.1.9
|
||||
devDependencies:
|
||||
'@nestjs/cli':
|
||||
specifier: ^11.0.10
|
||||
@ -302,8 +296,8 @@ importers:
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0
|
||||
zod:
|
||||
specifier: ^4.0.17
|
||||
version: 4.1.5
|
||||
specifier: ^4.1.9
|
||||
version: 4.1.9
|
||||
zustand:
|
||||
specifier: ^5.0.8
|
||||
version: 5.0.8(@types/react@19.1.12)(react@19.1.1)
|
||||
@ -2540,10 +2534,6 @@ packages:
|
||||
resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==}
|
||||
engines: {node: '>=6.6.0'}
|
||||
|
||||
cookie@0.4.0:
|
||||
resolution: {integrity: sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
cookie@0.7.1:
|
||||
resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@ -2592,18 +2582,9 @@ packages:
|
||||
resolution: {integrity: sha512-D3WAbvvgUVIqSxUfdvLeGjuotsB32bvfVPd+AaaTWMtyUeC9zgCnw5xs94no89yFLVsafvY9dMZEhTwsY/ZecA==}
|
||||
engines: {node: '>=0.6.0'}
|
||||
|
||||
csrf@3.1.0:
|
||||
resolution: {integrity: sha512-uTqEnCvWRk042asU6JtapDTcJeeailFy4ydOQS28bj1hcLnYRiqi8SsD2jS412AY1I/4qdOwWZun774iqywf9w==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
csstype@3.1.3:
|
||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||
|
||||
csurf@1.11.0:
|
||||
resolution: {integrity: sha512-UCtehyEExKTxgiu8UHdGvHj4tnpE/Qctue03Giq5gPgMQ9cg/ciod5blZQ5a4uCEenNQjxyGuzygLdKUmee/bQ==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
deprecated: This package is archived and no longer maintained. For support, visit https://github.com/expressjs/express/discussions
|
||||
|
||||
csv-parse@5.6.0:
|
||||
resolution: {integrity: sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q==}
|
||||
|
||||
@ -2697,10 +2678,6 @@ packages:
|
||||
resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
|
||||
engines: {node: '>=0.10'}
|
||||
|
||||
depd@1.1.2:
|
||||
resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
depd@2.0.0:
|
||||
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@ -3325,10 +3302,6 @@ packages:
|
||||
html-escaper@2.0.2:
|
||||
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
|
||||
|
||||
http-errors@1.7.3:
|
||||
resolution: {integrity: sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
http-errors@2.0.0:
|
||||
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@ -4519,10 +4492,6 @@ packages:
|
||||
quick-format-unescaped@4.0.4:
|
||||
resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==}
|
||||
|
||||
random-bytes@1.0.0:
|
||||
resolution: {integrity: sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
randombytes@2.1.0:
|
||||
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
|
||||
|
||||
@ -4637,9 +4606,6 @@ packages:
|
||||
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
|
||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||
|
||||
rndm@1.2.0:
|
||||
resolution: {integrity: sha512-fJhQQI5tLrQvYIYFpOnFinzv9dwmR7hRnUz1XqP3OJ1jIweTNOd6aTO4jwQSgcBSFUB+/KHJxuGneime+FdzOw==}
|
||||
|
||||
router@2.2.0:
|
||||
resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==}
|
||||
engines: {node: '>= 18'}
|
||||
@ -4743,9 +4709,6 @@ packages:
|
||||
resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
setprototypeof@1.1.1:
|
||||
resolution: {integrity: sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==}
|
||||
|
||||
setprototypeof@1.2.0:
|
||||
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
||||
|
||||
@ -4833,10 +4796,6 @@ packages:
|
||||
standard-as-callback@2.1.0:
|
||||
resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
|
||||
|
||||
statuses@1.5.0:
|
||||
resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
statuses@2.0.1:
|
||||
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@ -5033,10 +4992,6 @@ packages:
|
||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||
engines: {node: '>=8.0'}
|
||||
|
||||
toidentifier@1.0.0:
|
||||
resolution: {integrity: sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==}
|
||||
engines: {node: '>=0.6'}
|
||||
|
||||
toidentifier@1.0.1:
|
||||
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
||||
engines: {node: '>=0.6'}
|
||||
@ -5117,10 +5072,6 @@ packages:
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
tsscmp@1.0.6:
|
||||
resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==}
|
||||
engines: {node: '>=0.6.x'}
|
||||
|
||||
tsx@4.20.5:
|
||||
resolution: {integrity: sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
@ -5197,10 +5148,6 @@ packages:
|
||||
engines: {node: '>=0.8.0'}
|
||||
hasBin: true
|
||||
|
||||
uid-safe@2.1.5:
|
||||
resolution: {integrity: sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
uid@2.0.2:
|
||||
resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==}
|
||||
engines: {node: '>=8'}
|
||||
@ -5408,9 +5355,6 @@ packages:
|
||||
resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
zod@4.1.5:
|
||||
resolution: {integrity: sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg==}
|
||||
|
||||
zod@4.1.9:
|
||||
resolution: {integrity: sha512-HI32jTq0AUAC125z30E8bQNz0RQ+9Uc+4J7V97gLYjZVKRjeydPgGt6dvQzFrav7MYOUGFqqOGiHpA/fdbd0cQ==}
|
||||
|
||||
@ -7717,8 +7661,6 @@ snapshots:
|
||||
|
||||
cookie-signature@1.2.2: {}
|
||||
|
||||
cookie@0.4.0: {}
|
||||
|
||||
cookie@0.7.1: {}
|
||||
|
||||
cookie@0.7.2: {}
|
||||
@ -7761,21 +7703,8 @@ snapshots:
|
||||
dependencies:
|
||||
sequin: 0.1.1
|
||||
|
||||
csrf@3.1.0:
|
||||
dependencies:
|
||||
rndm: 1.2.0
|
||||
tsscmp: 1.0.6
|
||||
uid-safe: 2.1.5
|
||||
|
||||
csstype@3.1.3: {}
|
||||
|
||||
csurf@1.11.0:
|
||||
dependencies:
|
||||
cookie: 0.4.0
|
||||
cookie-signature: 1.0.6
|
||||
csrf: 3.1.0
|
||||
http-errors: 1.7.3
|
||||
|
||||
csv-parse@5.6.0: {}
|
||||
|
||||
csv-stringify@6.6.0: {}
|
||||
@ -7846,8 +7775,6 @@ snapshots:
|
||||
|
||||
denque@2.1.0: {}
|
||||
|
||||
depd@1.1.2: {}
|
||||
|
||||
depd@2.0.0: {}
|
||||
|
||||
destr@2.0.5: {}
|
||||
@ -8711,14 +8638,6 @@ snapshots:
|
||||
|
||||
html-escaper@2.0.2: {}
|
||||
|
||||
http-errors@1.7.3:
|
||||
dependencies:
|
||||
depd: 1.1.2
|
||||
inherits: 2.0.4
|
||||
setprototypeof: 1.1.1
|
||||
statuses: 1.5.0
|
||||
toidentifier: 1.0.0
|
||||
|
||||
http-errors@2.0.0:
|
||||
dependencies:
|
||||
depd: 2.0.0
|
||||
@ -9695,12 +9614,12 @@ snapshots:
|
||||
pino-http: 10.5.0
|
||||
rxjs: 7.8.2
|
||||
|
||||
nestjs-zod@5.0.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/swagger@11.2.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2))(rxjs@7.8.2)(zod@4.1.5):
|
||||
nestjs-zod@5.0.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/swagger@11.2.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2))(rxjs@7.8.2)(zod@4.1.9):
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
deepmerge: 4.3.1
|
||||
rxjs: 7.8.2
|
||||
zod: 4.1.5
|
||||
zod: 4.1.9
|
||||
optionalDependencies:
|
||||
'@nestjs/swagger': 11.2.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)
|
||||
|
||||
@ -10097,8 +10016,6 @@ snapshots:
|
||||
|
||||
quick-format-unescaped@4.0.4: {}
|
||||
|
||||
random-bytes@1.0.0: {}
|
||||
|
||||
randombytes@2.1.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
@ -10212,8 +10129,6 @@ snapshots:
|
||||
|
||||
reusify@1.1.0: {}
|
||||
|
||||
rndm@1.2.0: {}
|
||||
|
||||
router@2.2.0:
|
||||
dependencies:
|
||||
debug: 4.4.1
|
||||
@ -10378,8 +10293,6 @@ snapshots:
|
||||
es-errors: 1.3.0
|
||||
es-object-atoms: 1.1.1
|
||||
|
||||
setprototypeof@1.1.1: {}
|
||||
|
||||
setprototypeof@1.2.0: {}
|
||||
|
||||
sharp@0.34.3:
|
||||
@ -10491,8 +10404,6 @@ snapshots:
|
||||
|
||||
standard-as-callback@2.1.0: {}
|
||||
|
||||
statuses@1.5.0: {}
|
||||
|
||||
statuses@2.0.1: {}
|
||||
|
||||
statuses@2.0.2: {}
|
||||
@ -10705,8 +10616,6 @@ snapshots:
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
|
||||
toidentifier@1.0.0: {}
|
||||
|
||||
toidentifier@1.0.1: {}
|
||||
|
||||
token-types@6.1.1:
|
||||
@ -10787,8 +10696,6 @@ snapshots:
|
||||
|
||||
tslib@2.8.1: {}
|
||||
|
||||
tsscmp@1.0.6: {}
|
||||
|
||||
tsx@4.20.5:
|
||||
dependencies:
|
||||
esbuild: 0.25.10
|
||||
@ -10876,10 +10783,6 @@ snapshots:
|
||||
uglify-js@3.19.3:
|
||||
optional: true
|
||||
|
||||
uid-safe@2.1.5:
|
||||
dependencies:
|
||||
random-bytes: 1.0.0
|
||||
|
||||
uid@2.0.2:
|
||||
dependencies:
|
||||
'@lukeed/csprng': 1.1.0
|
||||
@ -11130,8 +11033,6 @@ snapshots:
|
||||
|
||||
yoctocolors-cjs@2.1.3: {}
|
||||
|
||||
zod@4.1.5: {}
|
||||
|
||||
zod@4.1.9: {}
|
||||
|
||||
zustand@5.0.8(@types/react@19.1.12)(react@19.1.1):
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user