refactor: migrate interfaces to dedicated directory

Reorganizes TypeScript interfaces into a modular structure:
- Create dedicated interfaces directory with specialized files
- Split interfaces by domain (claims, common, limits, records, user)
- Update imports in endorserServer.ts to use new interface locations
- Replace 'any' types with 'unknown' for better type safety
- Add proper type imports and exports

This improves code organization and maintainability by:
- Centralizing interface definitions
- Reducing file size of endorserServer.ts
- Making interface relationships more explicit
- Improving type safety with stricter types
This commit is contained in:
Matthew Raymer
2025-02-24 11:21:08 +00:00
parent fbb9fba347
commit c708716675
69 changed files with 663 additions and 645 deletions

View File

@@ -1,4 +1,4 @@
import { Axios, AxiosRequestConfig, AxiosResponse } from "axios";
import { Axios, AxiosRequestConfig } from "axios";
import { Buffer } from "buffer";
import { sha256 } from "ethereum-cryptography/sha256";
import { LRUCache } from "lru-cache";
@@ -17,10 +17,20 @@ import {
retrieveAccountMetadata,
retrieveFullyDecryptedAccount,
getPasskeyExpirationSeconds,
GiverReceiverInputInfo,
} from "../libs/util";
import { createEndorserJwtForKey, KeyMeta } from "../libs/crypto/vc";
import {
GiveVerifiableCredential,
OfferVerifiableCredential,
RegisterVerifiableCredential,
GenericVerifiableCredential,
GenericCredWrapper,
PlanSummaryRecord,
UserInfo,
CreateAndSubmitClaimResult,
} from "../interfaces";
export const SCHEMA_ORG_CONTEXT = "https://schema.org";
// the object in RegisterAction claims
export const SERVICE_ID = "endorser.ch";
@@ -36,44 +46,6 @@ export const CONTACT_URL_PATH_ENDORSER_CH_OLD = "/contact?jwt=";
//export const CONTACT_URL_PREFIX_ENDORSER_CH_OLD = "https://endorser.ch";
// the prefix for handle IDs, the permanent ID for claims on Endorser
export const ENDORSER_CH_HANDLE_PREFIX = "https://endorser.ch/entity/";
export interface AgreeVerifiableCredential {
"@context": string;
"@type": string;
// "any" because arbitrary objects can be subject of agreement
// eslint-disable-next-line @typescript-eslint/no-explicit-any
object: Record<string, any>;
}
export interface GiverOutputInfo {
action: string;
giver?: GiverReceiverInputInfo;
description?: string;
amount?: number;
unitCode?: string;
}
export interface ClaimResult {
success: { claimId: string; handleId: string };
error: { code: string; message: string };
}
// similar to VerifiableCredentialSubject... maybe rename this
export interface GenericVerifiableCredential {
"@context"?: string; // optional when embedded, eg. in an Agree
"@type": string;
[key: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}
export interface GenericCredWrapper<T extends GenericVerifiableCredential> {
claim: T;
claimType?: string;
handleId: string;
id: string;
issuedAt: string;
issuer: string;
publicUrls?: Record<string, string>; // only for IDs that want to be public
}
export const BLANK_GENERIC_SERVER_RECORD: GenericCredWrapper<GenericVerifiableCredential> =
{
claim: { "@type": "" },
@@ -82,232 +54,6 @@ export const BLANK_GENERIC_SERVER_RECORD: GenericCredWrapper<GenericVerifiableCr
issuedAt: "",
issuer: "",
};
// a summary record; the VC is found the fullClaim field
export interface GiveSummaryRecord {
agentDid: string;
amount: number;
amountConfirmed: number;
description: string;
fullClaim: GiveVerifiableCredential;
fulfillsHandleId: string;
fulfillsPlanHandleId?: string;
fulfillsType?: string;
handleId: string;
issuedAt: string;
issuerDid: string;
jwtId: string;
providerPlanHandleId?: string;
recipientDid: string;
unit: string;
}
// a summary record; the VC is found the fullClaim field
export interface OfferSummaryRecord {
amount: number;
amountGiven: number;
amountGivenConfirmed: number;
fullClaim: OfferVerifiableCredential;
fulfillsPlanHandleId: string;
handleId: string;
issuerDid: string;
jwtId: string;
nonAmountGivenConfirmed: number;
objectDescription: string;
offeredByDid: string;
recipientDid: string;
requirementsMet: boolean;
unit: string;
validThrough: string;
}
export interface OfferToPlanSummaryRecord extends OfferSummaryRecord {
planName: string;
}
// a summary record; the VC is not currently part of this record
export interface PlanSummaryRecord {
agentDid?: string; // optional, if the issuer wants someone else to manage as well
description: string;
endTime?: string;
fulfillsPlanHandleId: string;
handleId: string;
image?: string;
issuerDid: string;
locLat?: number;
locLon?: number;
name?: string;
startTime?: string;
url?: string;
}
// Note that previous VCs may have additional fields.
// https://endorser.ch/doc/html/transactions.html#id4
export interface GiveVerifiableCredential extends GenericVerifiableCredential {
"@context"?: string; // optional when embedded, eg. in an Agree
"@type": "GiveAction";
agent?: { identifier: string };
description?: string;
fulfills?: { "@type": string; identifier?: string; lastClaimId?: string }[];
identifier?: string;
image?: string;
object?: { amountOfThisGood: number; unitCode: string };
provider?: GenericVerifiableCredential; // typically @type & identifier
recipient?: { identifier: string };
}
// Note that previous VCs may have additional fields.
// https://endorser.ch/doc/html/transactions.html#id8
export interface OfferVerifiableCredential extends GenericVerifiableCredential {
"@context"?: string; // optional when embedded... though it doesn't make sense to agree to an offer
"@type": "Offer";
description?: string; // conditions for the offer
includesObject?: { amountOfThisGood: number; unitCode: string };
itemOffered?: {
description?: string; // description of the item
isPartOf?: { identifier?: string; lastClaimId?: string; "@type"?: string };
};
offeredBy?: { identifier: string };
recipient?: { identifier: string };
validThrough?: string;
}
// Note that previous VCs may have additional fields.
// https://endorser.ch/doc/html/transactions.html#id7
export interface PlanVerifiableCredential extends GenericVerifiableCredential {
"@context": "https://schema.org";
"@type": "PlanAction";
name: string;
agent?: { identifier: string };
description?: string;
identifier?: string;
lastClaimId?: string;
location?: {
geo: { "@type": "GeoCoordinates"; latitude: number; longitude: number };
};
}
/**
* Represents data about a project
*
* @deprecated
* (Maybe we should use PlanSummaryRecord instead, either by adding rowId or by iterating with jwtId.)
**/
export interface PlanData {
/**
* Description of the project
**/
description: string;
/**
* URL referencing information about the project
**/
handleId: string;
image?: string;
/**
* The DID of the issuer
*/
issuerDid: string;
/**
* Name of the project
**/
name: string;
/**
* The identifier of the project record -- different from jwtId
* (Maybe we should use the jwtId to iterate through the records instead.)
**/
rowId?: string;
}
export interface EndorserRateLimits {
doneClaimsThisWeek: string;
doneRegistrationsThisMonth: string;
maxClaimsPerWeek: string;
maxRegistrationsPerMonth: string;
nextMonthBeginDateTime: string;
nextWeekBeginDateTime: string;
}
export interface ImageRateLimits {
doneImagesThisWeek: string;
maxImagesPerWeek: string;
nextWeekBeginDateTime: string;
}
export interface VerifiableCredential {
exp?: number;
iat: number;
iss: string;
vc: {
"@context": string[];
type: string[];
credentialSubject: VerifiableCredentialSubject;
};
}
// similar to GenericVerifiableCredential... maybe replace that one
export interface VerifiableCredentialSubject {
"@context": string;
"@type": string;
[key: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}
export interface WorldProperties {
startTime?: string;
endTime?: string;
}
// AKA Registration & RegisterAction
export interface RegisterVerifiableCredential {
"@context": typeof SCHEMA_ORG_CONTEXT;
"@type": "RegisterAction";
agent: { identifier: string };
identifier?: string; // used for invites (when participant ID isn't known)
object: string;
participant?: { identifier: string }; // used when person is known (not an invite)
}
// now for some of the error & other wrapper types
export interface ResultWithType {
type: string;
}
export interface SuccessResult extends ResultWithType {
type: "success";
response: AxiosResponse<ClaimResult>;
}
export interface ErrorResponse {
error?: {
message?: string;
};
}
export interface InternalError {
error: string; // for system logging
userMessage?: string; // for user display
}
export interface ErrorResult extends ResultWithType {
type: "error";
error: InternalError;
}
export type CreateAndSubmitClaimResult = SuccessResult | ErrorResult;
/**
* This is similar to Contact but it grew up in different logic paths.
* We may want to change this to be a Contact.
*/
export interface UserInfo {
did: string;
name: string;
publicEncKey: string;
registered: boolean;
profileImageUrl?: string;
nextPublicEncKeyHash?: string;
}
// This is used to check for hidden info.
// See https://github.com/trentlarson/endorser-ch/blob/0cb626f803028e7d9c67f095858a9fc8542e3dbd/server/api/services/util.js#L6
const HIDDEN_DID = "did:none:HIDDEN";