exiftool-vendored
Version:
Efficient, cross-platform access to ExifTool
1,039 lines • 73.5 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const source_map_support_1 = __importDefault(require("source-map-support"));
source_map_support_1.default.install();
const batch_cluster_1 = require("batch-cluster");
const globule_1 = __importDefault(require("globule"));
const node_fs_1 = __importDefault(require("node:fs"));
const node_os_1 = __importDefault(require("node:os"));
const node_path_1 = __importDefault(require("node:path"));
const node_process_1 = __importDefault(require("node:process"));
const progress_1 = __importDefault(require("progress"));
const Array_1 = require("../Array");
const ExifTool_1 = require("../ExifTool");
const ExifToolVendoredTags_1 = require("../ExifToolVendoredTags");
const GeolocationTags_1 = require("../GeolocationTags");
const ICCProfileTags_1 = require("../ICCProfileTags");
const ImageDataHashTag_1 = require("../ImageDataHashTag");
const IPTCApplicationRecordTags_1 = require("../IPTCApplicationRecordTags");
const Maybe_1 = require("../Maybe");
const MWGTags_1 = require("../MWGTags");
const Number_1 = require("../Number");
const ReadTask_1 = require("../ReadTask");
const String_1 = require("../String");
const Times_1 = require("../Times");
/**
* Unfortunately, TypeScript has a limit on the complexity of types it can
* represent.
*
* If we build a union type of all the tags ExifTool knows about (which is north
* of 15,000 tags last time we checked), we exceed that limit and get `error
* TS2590: Expression produces a union type that is too complex to represent`.
*
* So, we have to hack around this by only including the N "most popular" and
* "most important" tags.
*
* Note that this limit is much less than that threshold (which was about 3,100
* in 2023), but remember to account for the 8+ static interfaces we're also
* including in the final Tags union.
*/
const MAX_TAGS = 2700;
// Static interfaces that Tags extends - these need special handling to avoid
// name collisions and to ensure their tag names appear in TagMetadata.json
const StaticInterfaceMetadata = [
{
name: "ExifToolVendoredTags",
group: "Library", // Library-added tags
tagNames: ExifToolVendoredTags_1.ExifToolVendoredTagNames,
},
{
name: "GeolocationTags",
group: "ExifTool", // ExifTool's geolocation feature
tagNames: GeolocationTags_1.GeolocationTagNames,
},
{
name: "ImageDataHashTag",
group: "Composite", // Computed by ExifTool
tagNames: ImageDataHashTag_1.ImageDataHashTagNames,
},
{
name: "ICCProfileTags",
group: "ICC_Profile",
tagNames: ICCProfileTags_1.ICCProfileTagNames,
},
{
name: "IPTCApplicationRecordTags",
group: "IPTC",
tagNames: IPTCApplicationRecordTags_1.IPTCApplicationRecordTagNames,
},
{
name: "MWGCollectionsTags",
group: "XMP", // MWG (Metadata Working Group) uses XMP
tagNames: MWGTags_1.MWGCollectionsTagNames,
},
{
name: "MWGKeywordTags",
group: "XMP", // MWG (Metadata Working Group) uses XMP
tagNames: MWGTags_1.MWGKeywordTagNames,
},
];
// These tags are important enough that we want to ensure they're always in the
// final Tags interface.
//
// If you're relying on a tag that gets inadvertently dropped in some release,
// consider opening a PR to restore it by adding it here:
const RequiredTags = {
ExifToolVersion: { t: "string", grp: "ExifTool" }, // < ExifTool stores the version as a float (!!) which causes 12.30 to become 12.3.
AccelerometerX: {
t: "number",
grp: "MakerNotes",
doc: "Accelerometer X-axis reading. Panasonic: positive is acceleration to the left.\nAlso found in DJI and Lytro cameras. Used for motion/orientation detection.",
},
AccelerometerY: {
t: "number",
grp: "MakerNotes",
doc: "Accelerometer Y-axis reading. Panasonic: positive is acceleration backwards.\nAlso found in DJI and Lytro cameras. Used for motion/orientation detection.",
},
AccelerometerZ: {
t: "number",
grp: "MakerNotes",
doc: "Accelerometer Z-axis reading. Panasonic: positive is acceleration upward.\nAlso found in DJI and Lytro cameras. Used for motion/orientation detection.",
},
Album: { t: "string", grp: "XMP", value: "Twilight Dreams" },
Aperture: {
t: "number",
grp: "Composite",
doc: "Calculated aperture value derived from FNumber or ApertureValue.\nRead-only composite tag. To write, modify FNumber or ApertureValue instead.",
see: "https://exiftool.org/TagNames/Composite.html",
},
ApertureValue: {
t: "number",
grp: "EXIF",
doc: "Lens aperture in APEX units. Secondary source for Aperture composite (FNumber takes priority).\nFormula: ApertureValue = 2 × log₂(FNumber). To write aperture, prefer FNumber as it's more intuitive.",
see: "https://exiftool.org/TagNames/EXIF.html",
},
Artist: {
t: "string",
grp: "EXIF",
doc: "Image creator/photographer name. ExifTool trims trailing whitespace.\nWhen MWG module is loaded, this becomes a list-type tag synchronized with XMP-dc:Creator and IPTC:By-line.",
see: "https://exiftool.org/TagNames/EXIF.html",
},
AspectRatio: { t: "string", grp: "MakerNotes", value: "3:2" },
AutoRotate: { t: "number | string", grp: "MakerNotes" },
AvgBitrate: {
t: "number | string",
grp: "Composite",
doc: "Average bitrate for video/audio files, calculated from media data size divided by duration.\nRead-only composite tag for QuickTime-based formats (MOV, MP4, etc.).",
see: "https://exiftool.org/TagNames/Composite.html",
},
BodySerialNumber: { t: "string", grp: "MakerNotes" },
BracketSettings: {
t: "string",
grp: "MakerNotes",
doc: "Panasonic exposure bracketing configuration.\nValues: 'No Bracket', '3 Images, Sequence 0/-/+', '3 Images, Sequence -/0/+',\n'5 Images, Sequence 0/-/+', '5 Images, Sequence -/0/+', '7 Images, Sequence 0/-/+', '7 Images, Sequence -/0/+'.",
},
BurstID: { t: "string", grp: "XMP" },
BurstUUID: { t: "string", grp: "MakerNotes" },
CameraID: { t: "string", grp: "MakerNotes" },
CameraOrientation: { t: "string", grp: "MakerNotes" },
CameraSerialNumber: {
t: "string",
grp: "EXIF",
doc: "Camera serial number. DNG tag 0xc62f, writable string in IFD0.",
},
Comment: {
t: "string",
grp: "File",
doc: "Comment text. JPEG COM segment, GIF comment, or other file-level comment.\nNot an XMP tag despite common confusion.",
},
Compass: {
t: "number",
grp: "MakerNotes",
doc: "Ricoh Theta compass heading direction in degrees.\nCamera orientation sensor data.",
},
ColorSpace: {
t: "number | string",
grp: "EXIF",
doc: "Color space of image data. EXIF mandatory tag.\nStandard values: 1 (sRGB), 0xFFFF (Uncalibrated).\nNon-standard values: 2 (Adobe RGB, some cameras), 0xFFFD (Wide Gamut RGB, Sony), 0xFFFE (ICC Profile, Sony).\nAdobe RGB is typically indicated by 'Uncalibrated' with InteropIndex='R03'.",
see: "https://exiftool.org/TagNames/EXIF.html",
},
ContainerDirectory: {
t: "ContainerDirectoryItem[] | Struct[]",
grp: "XMP",
doc: "XMP container directory structure containing list of container items.\nStruct type with flattened fields when Struct option is disabled.",
see: "https://exiftool.org/TagNames/XMP.html",
},
Copyright: {
t: "string",
grp: "EXIF",
doc: "Copyright notice for the image. MWG composite tag that reconciles EXIF:Copyright, IPTC:CopyrightNotice, and XMP-dc:Rights.\nWriting updates all three locations to maintain MWG synchronization.",
see: "https://exiftool.org/TagNames/MWG.html",
},
Country: { t: "string", grp: "XMP" },
CountryCode: { t: "string", grp: "XMP" },
CreateDate: {
t: "ExifDateTime | ExifDate | string | number",
grp: "EXIF",
doc: "When an image was digitized (captured by camera sensor). MWG composite tag that reconciles EXIF:CreateDate, IPTC digital creation fields, and XMP-xmp:CreateDate.\nDistinct from DateTimeOriginal (when photo was taken) - useful for scanned images.\nFor MOV/MP4 videos, use CreateDate instead of DateTimeOriginal.",
see: "https://exiftool.org/TagNames/MWG.html",
},
CreationTime: { t: "ExifDateTime | string", grp: "XMP" },
CropAngle: {
t: "number",
grp: "XMP",
doc: "Crop rotation angle. Primarily from XMP-crs (Camera Raw Settings/Adobe Camera Raw) namespace.\nAlso found in Canon DPP recipe files.",
see: "https://exiftool.org/TagNames/XMP.html#crs",
},
CropBottom: { t: "number", grp: "XMP" },
CropHeight: { t: "number", grp: "XMP" },
CropLeft: { t: "number", grp: "XMP" },
CropRight: { t: "number", grp: "XMP" },
CropTop: { t: "number", grp: "XMP" },
CropWidth: { t: "number", grp: "XMP" },
DateCreated: { t: "ExifDateTime | string", grp: "XMP" },
DateTime: { t: "ExifDateTime | string", grp: "XMP" },
DateTimeCreated: {
t: "ExifDateTime | string",
grp: "Composite",
doc: "Composite tag combining IPTC:DateCreated (YYYYMMDD) and IPTC:TimeCreated (with timezone offset).\nRead-only composite - to write, set IPTC:DateCreated and IPTC:TimeCreated individually.",
see: "https://exiftool.org/TagNames/Composite.html",
},
DateTimeDigitized: { t: "ExifDateTime | string", grp: "XMP" },
DateTimeGenerated: { t: "ExifDateTime | string", grp: "APP1" },
DateTimeOriginal: {
t: "ExifDateTime | string",
grp: "EXIF",
doc: "When a photo was taken (shutter actuation time). MWG composite tag that reconciles EXIF:DateTimeOriginal, IPTC date/time created, and XMP-photoshop DateTimeOriginal.\nThis is the most commonly used timestamp for photos. Different from CreateDate (digitization) and ModifyDate (file modification).",
see: "https://exiftool.org/TagNames/MWG.html",
},
DateTimeUTC: { t: "ExifDateTime | string", grp: "MakerNotes" },
Description: {
t: "string",
grp: "XMP",
doc: "Image caption or description. MWG composite tag that reconciles EXIF:ImageDescription, IPTC:Caption-Abstract, and XMP-dc:Description.\nWriting updates all three locations for MWG compliance. Supports language variants via RFC 3066 codes (e.g., 'Description-fr').",
see: "https://exiftool.org/TagNames/MWG.html",
},
DOF: {
t: "string",
grp: "Composite",
doc: "Calculated depth of field based on focal length, aperture, and focus distance.\nWARNING: This value may be incorrect if the image has been resized.\nRead-only composite tag.",
see: "https://exiftool.org/TagNames/Composite.html",
},
Duration: {
t: "number | string",
grp: "QuickTime",
doc: "Video/audio duration. QuickTime: stored in time scale units, converted to seconds using TimeScale.\nExifTool formats as 'H:MM:SS' or seconds. Some iPhone live-photo MOV videos may show key frame time instead of total duration.",
see: "https://exiftool.org/TagNames/QuickTime.html",
},
Error: { t: "string", grp: "ExifTool" },
ExifImageHeight: { t: "number", grp: "EXIF" },
ExifImageWidth: { t: "number", grp: "EXIF" },
ExifVersion: {
t: "string",
grp: "EXIF",
doc: "EXIF specification version (e.g., '0232' for EXIF 2.32). EXIF mandatory tag.\nStored as 4-byte ASCII without separators. ExifTool accepts '2.32' format when writing.\nSome files incorrectly include null terminators which ExifTool removes.",
see: "https://exiftool.org/TagNames/EXIF.html",
},
ExposureCompensation: {
t: "number | string",
grp: "EXIF",
doc: "Exposure bias/compensation in EV units (e.g., -0.67, +1.0). Also called ExposureBiasValue in EXIF spec.\nSigned value indicating deviation from metered exposure.",
see: "https://exiftool.org/TagNames/EXIF.html",
},
ExposureProgram: {
t: "number | string",
grp: "EXIF",
doc: "Camera exposure mode.\nValues: 0 (Not Defined), 1 (Manual), 2 (Program AE), 3 (Aperture-priority AE), 4 (Shutter speed priority AE), 5 (Creative/Slow speed), 6 (Action/High speed), 7 (Portrait), 8 (Landscape).\nValue 9 (Bulb) is non-standard but used by some Canon models.",
see: "https://exiftool.org/TagNames/EXIF.html",
},
ExposureTime: {
t: "string",
grp: "EXIF",
doc: "Shutter speed in seconds (e.g., '1/250'). Primary source for ShutterSpeed composite.\nTo write shutter speed, use this tag directly. BulbDuration takes priority in ShutterSpeed composite if present and > 0.",
see: "https://exiftool.org/TagNames/EXIF.html",
},
EyeDetection: {
t: "string",
grp: "MakerNotes",
doc: "Canon eye detection AF setting. Values: 'On' or 'Off'.\nEnables autofocus tracking on detected eyes.",
},
FaceDetection: {
t: "string",
grp: "MakerNotes",
doc: "Sony face detection setting. Values: 'On' (16) or 'Off' (1).\nIndicates whether face detection was enabled during capture.",
},
FacesDetected: {
t: "number",
grp: "MakerNotes",
doc: "Number of faces detected by the camera during capture.\nFound in MakerNotes from Canon, Nikon, Sony, Panasonic, Olympus, Fujifilm, Pentax, Ricoh, and others.",
},
FacesRecognized: {
t: "number",
grp: "MakerNotes",
doc: "Panasonic: number of faces matched to user-registered known faces.\nRelated tags: RecognizedFace*Name, RecognizedFace*Position.",
},
FaceRecognition: {
t: "string",
grp: "MakerNotes",
doc: "Samsung face recognition setting. Values: 'On' or 'Off'.\nWhen enabled, camera attempts to match faces to registered profiles.",
},
FileAccessDate: {
t: "ExifDateTime | string",
grp: "File",
doc: "File system access date/time. Not stored metadata - file system property.\nWritable on some systems. Changes when file is read.",
see: "https://exiftool.org/TagNames/File.html",
},
FileCreateDate: {
t: "ExifDateTime | string",
grp: "File",
doc: "File system creation date/time. Not stored metadata - file system property.\nWritable on some systems. Different from image capture date.",
see: "https://exiftool.org/TagNames/File.html",
},
FileInodeChangeDate: {
t: "ExifDateTime | string",
grp: "File",
doc: "File system inode change date/time (Unix/Linux). Not stored metadata - file system property.\nChanges when file metadata (permissions, ownership) or content changes. Not available on Windows.",
see: "https://exiftool.org/TagNames/File.html",
},
FileModifyDate: {
t: "ExifDateTime | string",
grp: "File",
doc: "File system modification date/time. Not stored metadata - file system property.\nWritable. Different from EXIF ModifyDate which tracks user edits.",
see: "https://exiftool.org/TagNames/File.html",
},
FileName: {
t: "string",
grp: "File",
doc: "Name of the file. Not stored metadata - intrinsic file property.\nWritable: can rename files. May include full path to set Directory simultaneously.",
see: "https://exiftool.org/TagNames/File.html",
},
FileSize: {
t: "string",
grp: "File",
doc: "Size of the file. Not stored metadata - intrinsic file property.\nRead-only. Uses SI prefixes by default (1 kB = 1000 bytes).",
see: "https://exiftool.org/TagNames/File.html",
},
FileType: {
t: "string",
grp: "File",
doc: "File type determined from file content, not extension. Not stored metadata - intrinsic file property.\nRead-only.",
see: "https://exiftool.org/TagNames/File.html",
},
FileTypeExtension: {
t: "string",
grp: "File",
doc: "Recommended file extension for this file type. Not stored metadata - intrinsic file property.\nRead-only.",
see: "https://exiftool.org/TagNames/File.html",
},
Flash: {
t: "number | string",
grp: "EXIF",
doc: "Flash status and mode as bitfield. Common values:\n0x00 (No Flash), 0x01 (Fired), 0x05 (Fired, Return not detected), 0x07 (Fired, Return detected),\n0x10 (Off, Did not fire), 0x18 (Auto, Did not fire), 0x19 (Auto, Fired),\n0x41 (Fired, Red-eye reduction), 0x59 (Auto, Fired, Red-eye reduction).\nBit 0: fired, Bit 1-2: return detection, Bit 3-4: mode, Bit 5: function present, Bit 6: red-eye mode.",
see: "https://exiftool.org/TagNames/EXIF.html",
},
FNumber: {
t: "number",
grp: "EXIF",
doc: "Lens aperture as f-number (e.g., 2.8, 5.6). Primary source for Aperture composite.\nTo write aperture, use this tag - it's more intuitive than ApertureValue (which uses APEX units).",
see: "https://exiftool.org/TagNames/EXIF.html",
},
Fnumber: {
t: "string",
grp: "APP12",
doc: "F-number from APP12 segment (Ducky tags, uncommon). Note lowercase 'n'.\nDistinct from standard EXIF:FNumber. Use FNumber instead for consistency.",
see: "https://exiftool.org/TagNames/APP12.html",
},
FocalLength: {
t: "string",
grp: "EXIF",
doc: "Lens focal length in millimeters. Actual focal length, not 35mm equivalent.\nFor 35mm equivalent, see FocalLengthIn35mmFormat or FocalLength35efl composite.",
see: "https://exiftool.org/TagNames/EXIF.html",
},
GPSAltitude: {
t: "number",
grp: "EXIF",
doc: "GPS altitude in meters. Always stored as positive value; sign determined by GPSAltitudeRef.\nComposite GPSAltitude combines this with Ref to return signed value with 'Above/Below Sea Level' text.",
see: "https://exiftool.org/TagNames/GPS.html",
},
GPSAltitudeRef: {
t: "number",
grp: "EXIF",
doc: "GPS altitude reference.\nValues: 0 (Above Sea Level), 1 (Below Sea Level), 2 (Positive Sea Level, sea-level ref), 3 (Negative Sea Level, sea-level ref).\nValues 2-3 added in EXIF 3.0 for ellipsoidal vs sea-level reference.",
},
GPSDateTime: {
t: "ExifDateTime | string",
grp: "Composite",
doc: "GPS timestamp combining GPSDateStamp and GPSTimeStamp fields.\nRead-only composite from multiple GPS sources including manufacturer-specific implementations.",
see: "https://exiftool.org/TagNames/Composite.html",
},
// We normally ask exiftool to render these as decimals, not DMS (degrees/minutes/seconds):
GPSLatitude: {
t: "number | string",
grp: "EXIF",
value: "37 deg 46' 29.64\" N",
doc: "GPS latitude stored as three rationals (degrees, minutes, seconds). Always positive; hemisphere from GPSLatitudeRef.\nExifTool accepts decimal degrees, DMS, or mixed formats when writing. Composite GPSLatitude returns signed decimal.",
see: "https://exiftool.org/TagNames/GPS.html",
},
GPSLatitudeRef: {
t: "string",
grp: "EXIF",
doc: "GPS latitude hemisphere.\nValid values: 'N' (North), 'S' (South). When writing, ExifTool accepts signed numbers or direction strings.",
},
GPSLongitude: {
t: "number | string",
grp: "EXIF",
value: "122 deg 25' 9.85\" W",
doc: "GPS longitude stored as three rationals (degrees, minutes, seconds). Always positive; hemisphere from GPSLongitudeRef.\nExifTool accepts decimal degrees, DMS, or mixed formats when writing. Composite GPSLongitude returns signed decimal.",
see: "https://exiftool.org/TagNames/GPS.html",
},
GPSLongitudeRef: {
t: "string",
grp: "EXIF",
doc: "GPS longitude hemisphere.\nValid values: 'E' (East), 'W' (West)",
},
GPSPosition: {
t: "string",
grp: "Composite",
doc: "Combined GPS latitude and longitude. Writable composite tag.\nWhen written, updates GPSLatitude, GPSLatitudeRef, GPSLongitude, GPSLongitudeRef.\nAccepts Google Maps coordinates (right-click format).",
see: "https://exiftool.org/TagNames/Composite.html",
},
GPSSpeed: {
t: "string",
grp: "EXIF",
doc: "GPS speed of camera movement during capture. Units determined by GPSSpeedRef (K=km/h, M=mph, N=knots).\nMust be paired with GPSSpeedRef for meaningful interpretation.",
see: "https://exiftool.org/TagNames/GPS.html",
},
GPSSpeedRef: {
t: "string",
grp: "EXIF",
doc: "GPS speed measurement unit.\nValid values: 'K' (km/h), 'M' (mph), 'N' (knots)",
},
GPSTimeStamp: {
t: "ExifTime | string",
grp: "EXIF",
doc: "UTC time of GPS fix. When writing, date is stripped off if present, and time is adjusted to UTC if it includes a timezone",
see: "https://exiftool.org/TagNames/GPS.html",
},
History: {
t: "ResourceEvent[] | ResourceEvent | string",
grp: "XMP",
doc: "Tracks editing history of the document. XMP-xmpMM (Media Management) struct type.\nFlattened fields: HistoryAction, HistoryChanged, HistoryInstanceID, HistoryParameters, HistorySoftwareAgent, HistoryWhen.",
see: "https://exiftool.org/TagNames/XMP.html#xmpMM",
},
ImageDataMD5: {
t: "string",
grp: "File",
doc: "MD5 hash of the actual image data (excluding metadata). Computed by ExifTool, not stored in file.\nUseful for detecting identical images with different metadata.",
see: "https://exiftool.org/TagNames/File.html",
},
ImageDescription: { t: "string", grp: "EXIF" },
ImageHeight: {
t: "number",
grp: "File",
doc: "Image height in pixels. File-level property derived from file analysis.\nRead-only.",
see: "https://exiftool.org/TagNames/File.html",
},
ImageNumber: {
t: "number | string",
grp: "XMP",
doc: "Image number/identifier. XMP-aux namespace.\nMay be numeric or string with leading zeros (e.g., '0001').",
see: "https://exiftool.org/TagNames/XMP.html#aux",
},
ImageSize: {
t: "number | string",
grp: "Composite",
doc: "Image dimensions combining width and height from various metadata fields.\nRead-only composite derived from ImageWidth, ImageHeight, ExifImageWidth, ExifImageHeight, or RawImageCroppedSize.",
see: "https://exiftool.org/TagNames/Composite.html",
},
ImageUniqueID: {
t: "string",
grp: "EXIF",
doc: "Unique identifier for the image, typically a 32-character hex string.\nUseful for image deduplication, tracking identity across edits, and linking related files (e.g., RAW + JPEG pairs).\nMay also appear in MakerNotes and XMP.",
see: "https://exiftool.org/TagNames/EXIF.html",
},
ImageWidth: {
t: "number",
grp: "File",
doc: "Image width in pixels. File-level property derived from file analysis.\nRead-only.",
see: "https://exiftool.org/TagNames/File.html",
},
InternalSerialNumber: { t: "string", grp: "MakerNotes" },
ISO: {
t: "number",
grp: "EXIF",
doc: "Camera ISO sensitivity rating. In EXIF, this is an array (int16u[n]) that can contain multiple values.\nHistorically called ISOSpeedRatings in EXIF 2.2, renamed to PhotographicSensitivity in EXIF 2.3.",
see: "https://exiftool.org/TagNames/EXIF.html",
},
ISOSpeed: { t: "number", grp: "EXIF" },
JpgFromRaw: {
t: "BinaryField",
grp: "EXIF",
doc: "Embedded JPEG preview extracted from RAW files. Binary data type.\nAccess via BinaryField to get raw bytes or base64 encoding.",
see: "https://exiftool.org/TagNames/EXIF.html",
},
Keywords: {
t: "string | string[]",
grp: "IPTC",
doc: "Searchable subject terms for image content. MWG composite tag that reconciles IPTC:Keywords and XMP-dc:Subject.\nMulti-value fields use semicolon-space ('; ') separators when represented as string.\nIPTC constraint: max 64 characters per keyword. Character encoding depends on IPTC:CodedCharacterSet; UTF8 recommended.",
see: "https://exiftool.org/TagNames/MWG.html",
},
Lens: {
t: "string",
grp: "Composite",
doc: "Lens identification from focal length range, primarily for Canon cameras.\nRead-only composite. For more detailed lens identification, see LensID.",
see: "https://exiftool.org/TagNames/Composite.html",
},
LensID: {
t: "string",
grp: "Composite",
doc: "Identifies actual lens model using manufacturer-specific lookup tables from partial type information.\nConfigurable: may be extended with user-defined lenses via ExifTool configuration.\nDifferent derivation logic for Canon (focal lengths), Nikon (LensIDNumber), Ricoh (LensFirmware), and others (XMP-aux:LensID).\nRead-only composite.",
see: "https://exiftool.org/TagNames/Composite.html",
},
LensInfo: { t: "string", grp: "EXIF" },
LensMake: { t: "string", grp: "EXIF" },
LensModel: { t: "string", grp: "EXIF" },
LensSerialNumber: { t: "string", grp: "EXIF" },
LensSpec: { t: "string", grp: "MakerNotes" },
LensType: { t: "string", grp: "MakerNotes" },
LensType2: { t: "string", grp: "MakerNotes" },
LensType3: { t: "string", grp: "MakerNotes" },
LightReading: {
t: "number",
grp: "MakerNotes",
doc: "Pentax light meter reading. Calibration varies by camera model.\nFor Optio WP, add 6 to get approximate LV (Light Value).",
},
Make: {
t: "string",
grp: "EXIF",
doc: "Camera/device manufacturer. ExifTool automatically removes trailing whitespace.\nUsed internally by ExifTool for vendor-specific tag handling.",
see: "https://exiftool.org/TagNames/EXIF.html",
},
MaxDataRate: { t: "string", grp: "RIFF" },
MediaCreateDate: {
t: "ExifDateTime | string",
grp: "QuickTime",
doc: "Creation date/time for QuickTime/MOV/MP4 media track. Stored as seconds since 1904-01-01 UTC.\nWARNING: Many cameras incorrectly store local time instead of UTC. ExifTool does not assume timezone unless QuickTimeUTC option is set.\nFor MOV/MP4 videos, use this tag instead of DateTimeOriginal.\nCannot be truly deleted (set to zero instead) as it's part of binary structure.",
see: "https://exiftool.org/TagNames/QuickTime.html",
},
Megapixels: {
t: "number",
grp: "Composite",
doc: "Total megapixels calculated from ImageSize composite field.\nRead-only composite.",
see: "https://exiftool.org/TagNames/Composite.html",
},
MeteringMode: {
t: "number | string",
grp: "EXIF",
doc: "Light metering mode used during capture.\nValues: 0 (Unknown), 1 (Average), 2 (Center-weighted average), 3 (Spot), 4 (Multi-spot), 5 (Multi-segment/Pattern/Evaluative), 6 (Partial), 255 (Other).",
see: "https://exiftool.org/TagNames/EXIF.html",
},
MetadataDate: {
t: "ExifDateTime | string",
grp: "XMP",
doc: "Date when metadata was last modified. XMP-xmp namespace.",
see: "https://exiftool.org/TagNames/XMP.html#xmp",
},
MIMEType: {
t: "string",
grp: "File",
doc: "MIME type of the file. Not stored metadata - intrinsic file property.\nRead-only, determined from file content.",
see: "https://exiftool.org/TagNames/File.html",
},
Model: {
t: "string",
grp: "EXIF",
doc: "Camera/device model name. ExifTool automatically removes trailing whitespace.\nUsed internally by ExifTool for vendor-specific tag handling.",
see: "https://exiftool.org/TagNames/EXIF.html",
},
ModifyDate: {
t: "ExifDateTime | string",
grp: "EXIF",
doc: "When the file was last modified by a user (not automatic processes). MWG composite tag that reconciles EXIF:ModifyDate and XMP-xmp:ModifyDate.\nShould reflect intentional user edits, not automatic metadata updates. Different from file system modification time.",
see: "https://exiftool.org/TagNames/MWG.html",
},
Notes: { t: "string", grp: "XMP", value: "Album description" },
Orientation: {
t: "number",
grp: "EXIF",
doc: "Image orientation. Standard EXIF tag 0x112 in IFD0.\nValid values: 1 (Horizontal/normal), 2 (Mirror horizontal), 3 (Rotate 180°), 4 (Mirror vertical), 5 (Mirror horizontal + rotate 270° CW), 6 (Rotate 90° CW), 7 (Mirror horizontal + rotate 90° CW), 8 (Rotate 270° CW).\nMost images use values 1, 3, 6, and 8.",
see: "https://exiftool.org/TagNames/EXIF.html",
},
OriginalCreateDateTime: { t: "ExifDateTime | string", grp: "XMP" },
OffsetTime: {
t: "string",
grp: "EXIF",
doc: "Timezone offset for ModifyDate (e.g., '+05:30', '-08:00', 'Z'). EXIF 2.31+ tag.\nUsed by SubSecModifyDate composite to produce timezone-aware timestamps.\nWriting SubSecModifyDate automatically updates this field.",
see: "https://exiftool.org/TagNames/EXIF.html",
},
OffsetTimeDigitized: {
t: "string",
grp: "EXIF",
doc: "Timezone offset for CreateDate (e.g., '+05:30', '-08:00', 'Z'). EXIF 2.31+ tag.\nUsed by SubSecCreateDate composite to produce timezone-aware timestamps.\nWriting SubSecCreateDate automatically updates this field.",
see: "https://exiftool.org/TagNames/EXIF.html",
},
OffsetTimeOriginal: {
t: "string",
grp: "EXIF",
doc: "Timezone offset for DateTimeOriginal (e.g., '+05:30', '-08:00', 'Z'). EXIF 2.31+ tag.\nUsed by SubSecDateTimeOriginal composite to produce timezone-aware timestamps.\nWriting SubSecDateTimeOriginal automatically updates this field.",
see: "https://exiftool.org/TagNames/EXIF.html",
},
PersonInImage: {
t: "string | string[]",
grp: "XMP",
doc: "Name(s) of person(s) shown in the image. XMP-iptcExt namespace.\nSimpler alternative to MWG RegionInfo when face coordinates aren't needed.\nMulti-value field; array when multiple people identified.",
see: "https://exiftool.org/TagNames/XMP.html#iptcExt",
},
PersonInImageWDetails: {
t: "Struct | Struct[]",
grp: "XMP",
doc: "Structured details about person(s) shown in the image. XMP-iptcExt namespace.\nIncludes PersonId, PersonName, PersonCharacteristic, PersonDescription.\nMore detailed than PersonInImage; IPTC Extension 2014+ standard.",
see: "https://exiftool.org/TagNames/XMP.html#iptcExt",
},
PreviewImage: {
t: "BinaryField",
grp: "Composite",
doc: "Embedded preview image data extracted from the file.\nCRITICAL: Writable for updating existing embedded images, but cannot create or delete previews.\nCan only modify previews that already exist in the file.",
see: "https://exiftool.org/TagNames/Composite.html",
},
PreviewImageSize: {
t: "string",
grp: "Composite",
value: "816x459",
doc: "Dimensions of the embedded preview image (e.g., '816x459').\nRead-only composite. Useful for checking preview size without extracting binary data.",
see: "https://exiftool.org/TagNames/Composite.html",
},
Rating: {
t: "number",
grp: "XMP",
doc: "Star rating for the image. MWG composite tag from XMP-xmp:Rating.\nValid values: -1 (rejected), 0 (unrated), 1-5 (star rating)\nNote: Rating may appear in EXIF but that's non-standard per MWG. Only XMP writes are supported.",
see: "https://exiftool.org/TagNames/MWG.html",
},
RatingPercent: {
t: "number",
grp: "XMP",
value: 99,
doc: "Microsoft-specific percentage rating (1-99).",
see: "https://exiftool.org/forum/index.php?topic=3567.msg16210#msg16210",
},
RAWFileType: {
t: "string",
grp: "MakerNotes",
doc: "Sony RAW compression setting: 'Compressed RAW', 'Uncompressed RAW', or 'Lossless Compressed RAW'.\nIntroduced 2015 for uncompressed 14-bit RAW support.",
},
RegistryID: {
t: "Struct[]",
grp: "XMP",
doc: "Registry identifiers for the image. XMP-iptcExt namespace struct type.\nFlattened fields: RegistryEntryRole, RegistryItemID, RegistryOrganisationID.",
see: "https://exiftool.org/TagNames/XMP.html#iptcExt",
},
RegionAppliedToDimensionsH: {
t: "number",
grp: "XMP",
doc: "Height of image when regions were defined. From XMP-mwg-rs namespace.\nFlattened from RegionInfo struct (requires struct=0).",
see: "https://exiftool.org/TagNames/MWG.html",
},
RegionAppliedToDimensionsUnit: {
t: "string",
grp: "XMP",
value: "pixel",
doc: "Unit for AppliedToDimensions (typically 'pixel'). From XMP-mwg-rs namespace.\nFlattened from RegionInfo struct (requires struct=0).",
see: "https://exiftool.org/TagNames/MWG.html",
},
RegionAppliedToDimensionsW: {
t: "number",
grp: "XMP",
doc: "Width of image when regions were defined. From XMP-mwg-rs namespace.\nFlattened from RegionInfo struct (requires struct=0).",
see: "https://exiftool.org/TagNames/MWG.html",
},
RegionAreaH: {
t: "number | number[]",
grp: "XMP",
doc: "Height of region(s) as normalized value (0-1). From XMP-mwg-rs namespace.\nFlattened from RegionInfo struct (requires struct=0). Array when multiple regions.",
see: "https://exiftool.org/TagNames/MWG.html",
},
RegionAreaUnit: {
t: "string",
grp: "XMP",
value: "normalized",
doc: "Unit for RegionArea coordinates (typically 'normalized'). From XMP-mwg-rs namespace.\nFlattened from RegionInfo struct (requires struct=0).",
see: "https://exiftool.org/TagNames/MWG.html",
},
RegionAreaW: {
t: "number | number[]",
grp: "XMP",
doc: "Width of region(s) as normalized value (0-1). From XMP-mwg-rs namespace.\nFlattened from RegionInfo struct (requires struct=0). Array when multiple regions.",
see: "https://exiftool.org/TagNames/MWG.html",
},
RegionAreaX: {
t: "number | number[]",
grp: "XMP",
doc: "Horizontal center of region(s) as normalized value (0-1). From XMP-mwg-rs namespace.\nFlattened from RegionInfo struct (requires struct=0). Array when multiple regions.",
see: "https://exiftool.org/TagNames/MWG.html",
},
RegionAreaY: {
t: "number | number[]",
grp: "XMP",
doc: "Vertical center of region(s) as normalized value (0-1). From XMP-mwg-rs namespace.\nFlattened from RegionInfo struct (requires struct=0). Array when multiple regions.",
see: "https://exiftool.org/TagNames/MWG.html",
},
RegionInfo: {
t: "Struct",
grp: "XMP",
doc: "MWG face/region metadata structure containing AppliedToDimensions and RegionList.\nWith struct=1 (default), contains nested objects. With struct=0, fields are flattened.\nUsed by Lightroom, Picasa, Windows Photo Gallery, digiKam, and other photo organizers.",
see: "https://exiftool.org/TagNames/MWG.html",
},
RegionInfoMP: {
t: "Struct",
grp: "XMP",
doc: "Microsoft Photo region metadata structure (XMP-MP namespace).\nAlternative format to MWG regions, used by Windows Photo Gallery and some Microsoft tools.",
see: "https://exiftool.org/TagNames/XMP.html#MP",
},
RegionName: {
t: "string | string[]",
grp: "XMP",
doc: "Name(s) of identified region(s), typically person names for face regions.\nFrom XMP-mwg-rs namespace. Flattened from RegionInfo struct (requires struct=0).\nFor Lightroom compatibility, also add names to Keywords/Subject.",
see: "https://exiftool.org/TagNames/MWG.html",
},
RegionPersonDisplayName: {
t: "string | string[]",
grp: "XMP",
doc: "Display name(s) for person(s) in face regions.\nFrom XMP-MP namespace (Microsoft Photo), not XMP-mwg-rs.\nFlattened from RegionInfoMP struct (requires struct=0).",
see: "https://exiftool.org/TagNames/XMP.html#MP",
},
RegionRotation: {
t: "number | number[]",
grp: "XMP",
doc: "Rotation angle(s) of region(s) in degrees.\nFrom XMP-mwg-rs namespace. Flattened from RegionInfo struct (requires struct=0).",
see: "https://exiftool.org/TagNames/MWG.html",
},
RegionType: {
t: "string | string[]",
grp: "XMP",
doc: "Type(s) of region(s) identified.\nValid values: 'Face', 'Pet', 'BarCode', 'Focus'.\nFrom XMP-mwg-rs namespace. Flattened from RegionInfo struct (requires struct=0).",
see: "https://exiftool.org/TagNames/MWG.html",
},
Rotation: {
t: "number",
grp: "Composite",
doc: "Degrees of clockwise camera rotation for QuickTime/MP4 video files.\nSpecial writable: Writing this tag updates QuickTime MatrixStructure for all tracks with a non-zero image size simultaneously.\nDifferent from EXIF Orientation tag.",
see: "https://exiftool.org/TagNames/Composite.html",
},
RunTimeValue: { t: "number", grp: "MakerNotes" },
SerialNumber: { t: "string", grp: "MakerNotes" },
ShutterCount: { t: "number", grp: "MakerNotes" },
ShutterCount2: { t: "number", grp: "MakerNotes" },
ShutterCount3: { t: "number", grp: "MakerNotes" },
ShutterSpeed: {
t: "string",
grp: "Composite",
doc: "Shutter speed combining ExposureTime, ShutterSpeedValue, and BulbDuration.\nRead-only composite tag. Format typically fractional (e.g., '1/250').",
see: "https://exiftool.org/TagNames/Composite.html",
},
SonyDateTime2: { t: "ExifDateTime | string", grp: "MakerNotes" },
SonyExposureTime: { t: "string", grp: "MakerNotes" },
SonyFNumber: { t: "number", grp: "MakerNotes" },
SonyISO: { t: "number", grp: "MakerNotes" },
SourceFile: { t: "string", grp: "ExifTool", value: "path/to/file.jpg" },
SubSecCreateDate: {
t: "ExifDateTime | string",
grp: "Composite",
doc: "Creation date with subsecond precision, merging EXIF:CreateDate, SubSecTimeDigitized, and OffsetTimeDigitized.\nWritable composite: writing updates all three fields simultaneously for high-precision timestamps with timezone information.",
see: "https://exiftool.org/TagNames/Composite.html",
},
SubSecDateTimeOriginal: {
t: "ExifDateTime | string",
grp: "Composite",
doc: "Original datetime with subsecond precision, combining EXIF:DateTimeOriginal, SubSecTimeOriginal, and OffsetTimeOriginal.\nWritable composite: writing updates all three fields simultaneously. Represents when the photo was originally taken with high precision.",
see: "https://exiftool.org/TagNames/Composite.html",
},
SubSecModifyDate: {
t: "ExifDateTime | string",
grp: "Composite",
doc: "Modification timestamp with subsecond precision, combining EXIF:ModifyDate, SubSecTime, and OffsetTime.\nWritable composite: writing updates all three fields simultaneously. Represents when the file was last modified with high precision.",
see: "https://exiftool.org/TagNames/Composite.html",
},
SubSecTime: { t: "number", grp: "EXIF" },
SubSecTimeDigitized: { t: "number", grp: "EXIF" },
SubjectDetection: {
t: "string",
grp: "MakerNotes",
doc: "Nikon Z series subject detection mode.\nValues: 'Off', 'Auto', 'People', 'Animals', 'Vehicles', 'Birds', 'Airplanes'.\nIndicates what the camera's AI was configured to detect during capture.",
},
ThumbnailImage: {
t: "BinaryField",
grp: "EXIF",
doc: "Embedded thumbnail image data. Binary data type.\nWritable for updating existing thumbnails, but cannot create or delete thumbnails.",
},
ThumbnailLength: {
t: "number",
grp: "EXIF",
doc: "Size in bytes of the embedded JPEG thumbnail.\nUseful for checking thumbnail availability without extracting binary data.",
},
ThumbnailOffset: {
t: "number",
grp: "EXIF",
doc: "Byte offset of the embedded JPEG thumbnail within the file.\nUsed with ThumbnailLength to locate thumbnail data.",
},
ThumbnailTIFF: {
t: "BinaryField",
grp: "Composite",
doc: "Embedded TIFF thumbnail image data. Composite tag rebuilt from component EXIF tags.\nRead-only - derived from SubfileType, Compression, ImageWidth, ImageHeight, etc.",
},
TimeStamp: { t: "ExifDateTime | string", grp: "MakerNotes" },
TimeZone: { t: "string", grp: "MakerNotes" },
TimeZoneOffset: { t: "number | string", grp: "EXIF" },
Title: {
t: "string",
grp: "XMP",
doc: "Image title. XMP-dc (Dublin Core) namespace - use this standard schema instead of non-standard XMP-xmp:Title.\nSupports language variants via RFC 3066 codes (e.g., 'Title-fr'). Using 'x-default' language code preserves other existing languages when writing.",
see: "https://exiftool.org/TagNames/XMP.html#dc",
},
Versions: {
t: "Version[] | Version | string",
grp: "XMP",
doc: "Version history of the document. XMP-xmpMM (Media Management) struct type.\nFlattened fields include VersionsComments, VersionsEvent, etc.",
see: "https://exiftool.org/TagNames/XMP.html#xmpMM",
},
WhiteBalance: {
t: "number | string",
grp: "EXIF",
doc: "White balance mode. Standard EXIF values: 0 (Auto), 1 (Manual).\nMakerNotes often contain more detailed WhiteBalance with values like Daylight, Cloudy, Tungsten, Fluorescent, Flash, Custom, etc.\nEXIF:WhiteBalance has lower priority than MakerNotes version when both exist.",
see: "https://exiftool.org/TagNames/EXIF.html",
},
Warning: { t: "string", grp: "ExifTool" },
XPComment: { t: "string", grp: "EXIF" },
XPKeywords: { t: "string", grp: "EXIF" },
XPSubject: { t: "string", grp: "EXIF" },
XPTitle: { t: "string", grp: "EXIF" },
};
// ☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠
// ☠☠ AVAST YE! ☠☠
// ☠☠ ☠☠
// ☠☠ BEYOND THIS POINT, THERE BE NAUGHT BUT TANGLED RIGGING ☠☠
// ☠☠ AND CODE THAT'D MAKE A KRAKEN WEEP. TURN BACK, ☠☠
// ☠☠ LEST YE FIND YERSELF IN DAVY JONES' ☠☠
// ☠☠ LOCKER, WITH NAUGHT BUT BUGS FER COMPANY. ☠☠
// ☠☠ ☠☠
// ☠☠ YE'VE BEEN WARNED, YE BRAVE AND FOOLISH SOUL. ☠☠
// ☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠
//
// no srsly, this code is _really_ bad, but I wanted a Tags interface, more
// than I felt shame in writing this, so it's here. I'm sorry. Everything
// _else_ has tests, honest.
//
const js_docs = {
CompositeTags: [
"These are tags are derived from the values of one or more other tags.",
"Only a few are writable directly.",
"@see https://exiftool.org/TagNames/Composite.html",
],
EXIFTags: ["@see https://exiftool.org/TagNames/EXIF.html"],
ExifToolTags: ["These tags are added by `exiftool`."],
FileTags: [
"These tags are not metadata fields, but are intrinsic to the content of a",
"given file. ExifTool can't write to many of these tags.",
],
FlashPixTags: ["@see https://exiftool.org/TagNames/FlashPix.html"],
GeolocationTags: [
"These tags are only available if {@link ExifToolOptions.geolocation} is true",
"and the file has valid GPS location data.",
],
IPTCTags: ["@see https://exiftool.org/TagNames/IPTC.html"],
PhotoshopTags: ["@see https://exiftool.org/TagNames/Photoshop.html"],
XMPTags: ["@see https://exiftool.org/TagNames/XMP.html"],
};
// If we don't do tag pruning, TypeScript fails with
// error TS2590: Expression produces a union type that is too complex to represent.
// This is a set of regexp patterns that match tags that are (probably)
// ignorable:
const ExcludedTagRe = new RegExp([
"_",
"A[ab]{3,}",
"AEC",
"AFR",
"AFS",
"AFStatus_",
"AFTrace",
"AFV",
"ASF\\d",
"AtmosphericTrans",
"AWB",
"CAM\\d",
"CameraTemperature",
"ChroSupC",
"DayltConv",
"DefConv",
"DefCor",
"Face\\d",
"FCS\\d",
"HJR",
"IM[a-z]",
"IncandConv",
"Kelvin_?WB",
"Label\\d",
"Value\\d",
"Mask_",
"MODE",
"MTR",
"O[a-f]+Revision",
"PF\\d\\d",
"PictureWizard",
"PiP",
"Planck",
"PF\\d",
"ProfileLook",
"R2[A-Z]", // noooo artooo
"RecallShoot",
"STB\\d",
"Tag[\\d_]+",
"TL84",
"WB[_\\d]",
"YhiY",
"\\w{6,}\\d{1,2}$",
].join("|"));
function sortBy(arr, f) {
return arr
.filter((ea) => ea != null)
.map((item, idx) => ({
item,
cmp: (0, Maybe_1.map)(f(item, idx), (ea) => [ea, idx]),
}))
.filter((ea) => ea.cmp != null)
.sort((a, b) => cmp(a.cmp, b.cmp))
.map((ea) => ea.item);
}
// Benchmark on 3900X: 53s for 10778 files with defaults
// 23s for 10778 files with these overrides:
const exiftool = new ExifTool_1.ExifTool({
maxProcs: node_os_1.default.cpus().length,
// if we use straight defaults, we're load-testing those defaults.
streamFlushMillis: 2,
minDelayBetweenSpawnMillis: 0,
geolocation: true,
// maxTasksPerProcess: 100, // < uncomment to verify proc wearing works
});
function ellipsize(str, max) {
str = "" + str;
return str.length < max ? str : str.slice(0, max - 8) + "…" + str.slice(-7);
}
// ☠☠ NO SRSLY STOP SCROLLING IT REALLY IS BAD ☠☠
(0, batch_cluster_1.setLogger)(batch_cluster_1.Log.withLevels(batch_cluster_1.Log.withTimestamps(batch_cluster_1.Log.filterLevels({
trace: console.log,
debug: console.log,
info: console.log,
warn: console.warn,
error: console.error,
}, node_process_1.default.env.LOG ?? "info"))));
node_process_1.default.on("uncaughtException", (error) => {
console.error("Caught uncaughtException: " + error);
});
node_process_1.default.on("unhandledRejection", (reason) => {
console.error("Caught unhandledRejection: " + reason);
});
function usage() {
console.log("Usage: `npm run mktags IMG_DIR`");
console.log("\nRebuilds src/Tags.ts from tags found in IMG_DIR.");
node_process_1.default.exit(1);
}
function cmp(a, b) {
return a > b ? 1 : a < b ? -1 : 0;
}
const roots = node_process_1.default.argv.slice(2);
if (roots.length === 0)
throw new Error("USAGE: mktags <path to image directory>");
const pattern = "**/*.+(3fr|avi|jpg|mov|mp4|cr2|cr3|nef|orf|raf|arw|rw2|dng)";
const files = roots
.map((root) => {
(0, batch_cluster_1.logger)().info("Scanning " + root + "/" + pattern + "...");
return globule_1.default.find(pattern, {
srcBase: root,
nocase: true,
nodir: true,
absolute: true,
});
})
.reduce((prev, curr) => prev.concat(curr));
if (files.length === 0) {
console.error(`No files found in ${roots}`);
usage();
}
(0, batch_cluster_1.logger)().info("Found " + files.length + " files...", files.slice(0, 7));
function valueType(value) {
if (value == null)
return;
if (Array.isArray(value)) {
const types = (0, Array_1.uniq)((0, Array_1.compact)(value.map((ea) => valueType(ea))));
return (types.length === 1 ? types[0] : "any") + "[]";
}
if (typeof value === "object") {
const ctor = value.constructor.name;
if (ctor === "Object") {
return "Struct";
}
if (ctor.startsWith("ExifDate") ||
ctor.startsWith("ExifTime") ||
ctor.endsWith("Field")) {
return ctor + " | string";
}
return ctor;
}
else {
return typeof value;
}
}
// except CountingMap. Isn't it cute? Not ashamed of you, little guy!
class CountingMap {
size = 0;
m = new Map();
add(...arr) {
this.size += arr.length;
for (const ea of arr) {
this.m.set(ea, 1 + (this.m.get(ea) ?? 0));
}
}
byCountDesc() {
return Array.from(this.m.keys()).sort((a, b) => cmp(this.m.get(b), this.m.get(a)));
}
topN(n) {
return this.byCountDesc().slice(0, n);
}
/**
* @param p [0,1]
* @return the values found in the top p of values
*/
byP(p) {
const min = p * this.size;
return this.byCountDesc().filter((ea) => (this.m.get(ea) ?? 0) > min);
}
}
function sigFigs(i, digits) {
if (i === 0 || digits === 0)
return 0;
const pow = Math.pow(10, digits - Math.round(Math.ceil(Math.log10(Math.abs(i)))));
return Math.round(i * pow) / pow;
}
function toStr(o) {
if (o == null)
return "";
else if ((0, Number_1.isNumber)(o))
return sigFigs(o, 8);
else if ((0, String_1.isString)(o))
return `"${ellipsize(o, 65)}"`;
else if ((0, String_1.isString)(o.rawValue))
return `"${ellipsize(o.rawValue, 65)}"`;
else
return ellipsize(JSON.stringify(o), 65);
}
function exampleToS(examples) {
return examples.length > 1 ? toStr(examples) : toStr(examples[0]);
}
function getOrSet(m, k, valueThunk) {
const prior = m.get(k);
if (prior != null) {
return prior;
}
else {
const