Add additional lint rules

This commit is contained in:
Aerilyn Weber 2026-05-19 16:15:15 +09:00
parent 02d782c3da
commit 420b18eb78
67 changed files with 3686 additions and 1415 deletions

View file

@ -13,17 +13,17 @@ import {
RefillListListResponseSchema,
AddToCabinetResponseSchema,
StoreComparisonItemSchema,
type CreateRefillListInput,
type UpdateRefillListInput,
type UpdateRefillListItemInput,
type RefillListQueryInput,
} from '@meshitrack/shared';
import { RefillsRepository } from './refills.repository.js';
import { RefillsService } from './refills.service.js';
import type { RefillListDocument } from '../../schemas/refill-list.schema.js';
function toIso(v: Date | string | { toISOString: () => string }): string {
if (typeof v === 'string') return v;
return v.toISOString();
}
type AnyItem = {
_id: string | { toString: () => string };
interface SerializedRefillListItemResponse {
_id: string;
medicineId: string;
medicineName: string;
quantity: number;
@ -31,57 +31,90 @@ type AnyItem = {
estimatedPrice?: number;
actualPrice?: number;
checked: boolean;
checkedAt?: Date | string;
checkedAt?: string;
addedToCabinet: boolean;
storeId?: string;
notes?: string;
};
}
type AnyRefillList = {
_id: string | { toString: () => string };
interface SerializedRefillListResponse {
_id: string;
householdId: string;
name: string;
items: AnyItem[];
status: string;
items: SerializedRefillListItemResponse[];
status: 'active' | 'completed' | 'cancelled';
preferredStoreId?: string;
totalEstimatedCost?: number;
createdBy: string;
createdAt: Date | string | { toISOString: () => string };
updatedAt: Date | string | { toISOString: () => string };
};
function toItemResponse(rawItem: unknown) {
const item = rawItem as AnyItem;
return {
_id: typeof item._id === 'string' ? item._id : item._id.toString(),
medicineId: item.medicineId,
medicineName: item.medicineName,
quantity: item.quantity,
unit: item.unit,
...(item.estimatedPrice != null ? { estimatedPrice: item.estimatedPrice } : {}),
...(item.actualPrice != null ? { actualPrice: item.actualPrice } : {}),
checked: item.checked,
...(item.checkedAt != null ? { checkedAt: toIso(item.checkedAt) } : {}),
addedToCabinet: item.addedToCabinet,
...(item.storeId != null ? { storeId: item.storeId } : {}),
...(item.notes != null ? { notes: item.notes } : {}),
};
createdAt: string;
updatedAt: string;
}
function toListResponse(rawDoc: unknown) {
const doc = rawDoc as AnyRefillList;
return {
_id: typeof doc._id === 'string' ? doc._id : doc._id.toString(),
function toListResponse(doc: RefillListDocument): SerializedRefillListResponse {
const docAny = doc as unknown as {
preferredStoreId?: string | null;
totalEstimatedCost?: number | null;
createdAt?: { toISOString?: () => string } | string;
updatedAt?: { toISOString?: () => string } | string;
};
const getIsoStr = (d: { toISOString?: () => string } | string | undefined | null): string => {
if (!d) return '';
if (typeof d === 'string') return d;
if (typeof d.toISOString === 'function') return d.toISOString();
return String(d);
};
const response: SerializedRefillListResponse = {
_id: doc._id.toString(),
householdId: doc.householdId,
name: doc.name,
items: doc.items.map(toItemResponse),
status: doc.status as never,
...(doc.preferredStoreId != null ? { preferredStoreId: doc.preferredStoreId } : {}),
...(doc.totalEstimatedCost != null ? { totalEstimatedCost: doc.totalEstimatedCost } : {}),
items: doc.items.map((item) => {
const itemAny = item as unknown as {
_id: { toString: () => string };
estimatedPrice?: number | null;
actualPrice?: number | null;
checkedAt?: { toISOString?: () => string } | string | Date;
storeId?: string | null;
notes?: string | null;
};
const itemResponse: SerializedRefillListItemResponse = {
_id: itemAny._id.toString(),
medicineId: item.medicineId,
medicineName: item.medicineName,
quantity: item.quantity,
unit: item.unit,
checked: item.checked,
addedToCabinet: item.addedToCabinet,
};
if (itemAny.estimatedPrice != null) itemResponse.estimatedPrice = itemAny.estimatedPrice;
if (itemAny.actualPrice != null) itemResponse.actualPrice = itemAny.actualPrice;
if (itemAny.checkedAt) {
if (typeof itemAny.checkedAt === 'string') {
itemResponse.checkedAt = itemAny.checkedAt;
} else if (itemAny.checkedAt instanceof Date) {
itemResponse.checkedAt = itemAny.checkedAt.toISOString();
} else if (typeof itemAny.checkedAt.toISOString === 'function') {
itemResponse.checkedAt = itemAny.checkedAt.toISOString();
}
}
if (itemAny.storeId) itemResponse.storeId = itemAny.storeId;
if (itemAny.notes) itemResponse.notes = itemAny.notes;
return itemResponse;
}),
status: doc.status as 'active' | 'completed' | 'cancelled',
createdBy: doc.createdBy,
createdAt: toIso(doc.createdAt),
updatedAt: toIso(doc.updatedAt),
createdAt: getIsoStr(docAny.createdAt),
updatedAt: getIsoStr(docAny.updatedAt),
};
if (docAny.preferredStoreId) response.preferredStoreId = docAny.preferredStoreId;
if (docAny.totalEstimatedCost != null) response.totalEstimatedCost = docAny.totalEstimatedCost;
return response;
}
declare module '@fastify/awilix' {
@ -111,19 +144,21 @@ export default fp(
},
handler: async (request, reply) => {
const service = fastify.diContainer.resolve('refillsService');
const params = request.params as { householdId: string };
const query = request.query as { userId?: string; thresholdDays?: number };
const alerts = await service.getAlerts(
request.params.householdId,
request.query.userId ?? request.user.keycloakId,
request.query.thresholdDays,
params.householdId,
query.userId ?? request.user.keycloakId,
query.thresholdDays,
);
return reply.send({
data: alerts.map((a) => ({
...a,
lastKnownPrice: a.lastKnownPrice
? { ...a.lastKnownPrice, date: toIso(a.lastKnownPrice.date) }
? { ...a.lastKnownPrice, date: a.lastKnownPrice.date.toISOString() }
: undefined,
cheapestOption: a.cheapestOption
? { ...a.cheapestOption, date: toIso(a.cheapestOption.date) }
? { ...a.cheapestOption, date: a.cheapestOption.date.toISOString() }
: undefined,
})),
});
@ -140,11 +175,9 @@ export default fp(
},
handler: async (request, reply) => {
const service = fastify.diContainer.resolve('refillsService');
const list = await service.createList(
request.body,
request.params.householdId,
request.user.keycloakId,
);
const params = request.params as { householdId: string };
const body = request.body as CreateRefillListInput;
const list = await service.createList(body, params.householdId, request.user.keycloakId);
return reply.status(201).send(toListResponse(list));
},
});
@ -159,7 +192,9 @@ export default fp(
},
handler: async (request, reply) => {
const service = fastify.diContainer.resolve('refillsService');
const result = await service.list(request.params.householdId, request.query);
const params = request.params as { householdId: string };
const query = request.query as RefillListQueryInput;
const result = await service.list(params.householdId, query);
return reply.send({
data: result.data.map(toListResponse),
pagination: result.pagination,
@ -176,7 +211,8 @@ export default fp(
},
handler: async (request, reply) => {
const service = fastify.diContainer.resolve('refillsService');
const list = await service.getById(request.params.id, request.params.householdId);
const params = request.params as { householdId: string; id: string };
const list = await service.getById(params.id, params.householdId);
return reply.send(toListResponse(list));
},
});
@ -191,11 +227,9 @@ export default fp(
},
handler: async (request, reply) => {
const service = fastify.diContainer.resolve('refillsService');
const list = await service.updateList(
request.params.id,
request.params.householdId,
request.body,
);
const params = request.params as { householdId: string; id: string };
const body = request.body as UpdateRefillListInput;
const list = await service.updateList(params.id, params.householdId, body);
return reply.send(toListResponse(list));
},
});
@ -210,12 +244,9 @@ export default fp(
},
handler: async (request, reply) => {
const service = fastify.diContainer.resolve('refillsService');
const list = await service.updateItem(
request.params.id,
request.params.householdId,
request.params.itemId,
request.body,
);
const params = request.params as { householdId: string; id: string; itemId: string };
const body = request.body as UpdateRefillListItemInput;
const list = await service.updateItem(params.id, params.householdId, params.itemId, body);
return reply.send(toListResponse(list));
},
});
@ -229,9 +260,10 @@ export default fp(
},
handler: async (request, reply) => {
const service = fastify.diContainer.resolve('refillsService');
const params = request.params as { householdId: string; id: string };
const result = await service.addToCabinet(
request.params.id,
request.params.householdId,
params.id,
params.householdId,
request.user.keycloakId,
);
return reply.send(result);
@ -256,16 +288,14 @@ export default fp(
},
handler: async (request, reply) => {
const service = fastify.diContainer.resolve('refillsService');
const comparisons = await service.getStoreComparison(
request.params.id,
request.params.householdId,
);
const params = request.params as { householdId: string; id: string };
const comparisons = await service.getStoreComparison(params.id, params.householdId);
return reply.send({
data: comparisons.map((c) => ({
medicineId: c.medicineId,
storeOptions: c.storeOptions.map((opt) => ({
...opt,
date: toIso(opt.date),
date: opt.date.toISOString(),
})),
})),
});