refactor(functions): type callable payloads instead of `any`

Change the 5 https.onCall handlers from `data: any` to
`data: Record<string, unknown>`, so payload fields are `unknown` and must go
through the existing validators rather than being implicitly-typed. No behavior
change (every field was already validated); tsc + 53 function tests green.

Left as-is deliberately: `catch (err: unknown)` narrowing (churn, marginal) and
the untyped Tink handles in wrapReleaseKeyCallable (the crypto lib ships no types).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
null 2026-07-06 22:06:45 -05:00
parent c71817241c
commit 89cbd7cb55
10 changed files with 10 additions and 10 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -34,7 +34,7 @@ const ACCEPT_RATE_LIMIT_WINDOW_MS = 60 * 60 * 1000 // 1 hour
const ACCEPT_RATE_LIMIT_MAX = 10 // 10 attempts per hour per user
const ACCEPT_ATTEMPT_TTL_MS = 25 * 60 * 60 * 1000 // 25h: rate window + 1h buffer; Firestore TTL cleans up after
export const acceptInviteCallable = functions.https.onCall(async (data: any, context) => {
export const acceptInviteCallable = functions.https.onCall(async (data: Record<string, unknown>, context) => {
const callerId = context.auth?.uid
if (!callerId) {
throw new functions.https.HttpsError('unauthenticated', 'Must be signed in.')

View File

@ -47,7 +47,7 @@ function generateCode(): string {
return code
}
export const createInviteCallable = functions.https.onCall(async (data: any, context) => {
export const createInviteCallable = functions.https.onCall(async (data: Record<string, unknown>, context) => {
const callerId = context.auth?.uid
if (!callerId) {
throw new functions.https.HttpsError('unauthenticated', 'Must be signed in.')

View File

@ -54,7 +54,7 @@ function isValidScoreMap(value: unknown): value is Record<ScoreKey, number> {
return true
}
export const submitOutcomeCallable = functions.https.onCall(async (data: any, context) => {
export const submitOutcomeCallable = functions.https.onCall(async (data: Record<string, unknown>, context) => {
const callerId = context.auth?.uid
if (!callerId) {
throw new functions.https.HttpsError('unauthenticated', 'Must be signed in.')

View File

@ -72,7 +72,7 @@ export const assignDailyQuestion = functions.pubsub
*
* Body: { coupleId: string, date?: string }
*/
export const assignDailyQuestionCallable = functions.https.onCall(async (data: any, context) => {
export const assignDailyQuestionCallable = functions.https.onCall(async (data: Record<string, unknown>, context) => {
const callerId = context.auth?.uid
if (!callerId) {
throw new functions.https.HttpsError('unauthenticated', 'Caller must be authenticated.')

View File

@ -47,7 +47,7 @@ export interface WrapReleaseKeyResponse {
const DEFAULT_AAD = 'closer_release_key'
export const wrapReleaseKeyCallable = functions.https.onCall(async (data: any, context) => {
export const wrapReleaseKeyCallable = functions.https.onCall(async (data: Record<string, unknown>, context) => {
const callerId = context.auth?.uid
if (!callerId) {
throw new functions.https.HttpsError('unauthenticated', 'Must be signed in.')