diff --git a/src/utils/api.tsx b/src/utils/api.tsx index d9193f3..9a1834a 100644 --- a/src/utils/api.tsx +++ b/src/utils/api.tsx @@ -16,19 +16,19 @@ import type { Optional, Required } from "./general" // The fields of a model. export type Fields = Record -export type TagId = string | number - export interface Tag { type: Type - id: TagId + id: string } +export type ModelId = string | number + /** * A data model. * Id: The type of Id. * Data: The data fields. */ -export type Model = { +export type Model = { id: Id } & Omit @@ -187,13 +187,23 @@ export function buildUrl( return url } -export function isTagId(value: unknown): boolean { +export function isModelId(value: unknown): boolean { return typeof value === "number" || typeof value === "string" } +export function listTag(type: Type): Tag { + return { type, id: "LIST" } +} + +export type TagDataOptions = Partial<{ + includeListTag: boolean + argKeysAreIds: boolean + id: string +}> + export function tagData>( type: Type, - id: string = "id", + options?: TagDataOptions, ): ( result: | Result @@ -205,7 +215,6 @@ export function tagData>( arg: | Arg | Array> - | [M["id"], Arg] | Record> | ListArg | Array @@ -213,35 +222,58 @@ export function tagData>( | number | undefined, ) => Array> { + const { + includeListTag = false, + argKeysAreIds = false, + id = "id", + } = options || {} + + function tags( + ids: ModelId[], + list: boolean = includeListTag, + ): Array> { + const tags = ids.map(id => ({ type, id: String(id) })) + if (list) tags.push(listTag(type)) + return tags + } + return (result, error, arg) => { if (!error) { if (arg) { - if (isTagId(arg)) return [{ type, id: arg as TagId }] - - if (Array.isArray(arg) && arg.length > 0) { - if (isTagId(arg[0])) { - if (arg.length === 2 && !isTagId(arg[1])) { - return [{ type, id: (arg as [M["id"], Arg])[0] }] - } + // The argument is an ID. + if (isModelId(arg)) return tags([arg as ModelId]) - return (arg as Array).map(id => ({ type, id })) + // The argument is an array of IDs. + if (Array.isArray(arg)) { + if (arg.length && isModelId(arg[0])) { + return tags(arg as Array) } } + // The argument is an object that contains the id field. + else if (typeof arg === "object" && argKeysAreIds) { + return tags(Object.keys(arg as Record)) + } } if (result) { + // The result is a model that contains the id field. if (id in result) { - return [{ type, id: (result as Result)[id] as TagId }] + return tags([(result as Result)[id] as ModelId]) } + // The result is an array of models that contain the id field. if (Array.isArray(result)) { - return result.map(result => ({ type, id: result[id] as TagId })) + return tags(result.map(result => result[id] as ModelId)) } - return (result as ListResult).data.map(result => ({ - type, - id: result[id] as TagId, - })) + // The result is a list that contains an array of models that contain + // the id field. + return tags( + (result as ListResult).data.map( + result => result[id] as ModelId, + ), + true, + ) } }