type ExternalIdArgumentCommandName = "identify" | "page" | "screen" | "track"; type ExternalIdArgumentValueName = "traits" | "properties"; type ExternalIdsCallShape = | "identify(userId, traits, { externalIds })" | "identify(traits, { externalIds })" | "page(name, properties, { externalIds })" | "screen(name, properties, { externalIds })" | "track(eventName, properties, { externalIds })"; const isObjectRecord = (value: unknown): value is Record => value !== null && typeof value === "object" && !Array.isArray(value); const hasSegmentExternalIdsProperty = (value: unknown): boolean => { if (!isObjectRecord(value)) { return false; } const externalIds = Object.entries(value).find( ([key]) => key === "externalIds", )?.[1]; return ( Array.isArray(externalIds) && externalIds.length > 0 && externalIds.every(isSegmentExternalIdLike) ); }; const isSegmentExternalIdLike = ( value: unknown, ): value is { id: string; type: string; collection: "users"; encoding: "none"; } => { if (!isObjectRecord(value)) { return false; } return ( typeof value.id === "string" && typeof value.type === "string" && value.collection === "users" && value.encoding === "none" ); }; export const assertNoSegmentExternalIdsInValueArgument = ({ argument, commandName, valueArgumentName, callShape, }: { argument: unknown; commandName: ExternalIdArgumentCommandName; valueArgumentName: ExternalIdArgumentValueName; callShape: ExternalIdsCallShape; }): void => { if (!hasSegmentExternalIdsProperty(argument)) { return; } throw new Error( `${commandName} received externalIds in the ${valueArgumentName} argument. Use ${callShape} instead.`, ); };